summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
committer Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
commitf88cefad27a1737c76e09d99c9fb43e173506081 (patch)
tree2d8167d03579c46e226471747eb4407bd00ed6fa /src/devices/video
parente92ac9e0fa8e99869894bea00589bbb526be30aa (diff)
Move all devices into separate part of src tree (nw)
Diffstat (limited to 'src/devices/video')
-rw-r--r--src/devices/video/315_5124.c1709
-rw-r--r--src/devices/video/315_5124.h255
-rw-r--r--src/devices/video/315_5313.c2776
-rw-r--r--src/devices/video/315_5313.h358
-rw-r--r--src/devices/video/bufsprite.c23
-rw-r--r--src/devices/video/bufsprite.h143
-rw-r--r--src/devices/video/cdp1861.c253
-rw-r--r--src/devices/video/cdp1861.h145
-rw-r--r--src/devices/video/cdp1862.c198
-rw-r--r--src/devices/video/cdp1862.h123
-rw-r--r--src/devices/video/cgapal.c433
-rw-r--r--src/devices/video/cgapal.h5
-rw-r--r--src/devices/video/clgd542x.c1512
-rw-r--r--src/devices/video/clgd542x.h114
-rw-r--r--src/devices/video/crt9007.c935
-rw-r--r--src/devices/video/crt9007.h222
-rw-r--r--src/devices/video/crt9021.c187
-rw-r--r--src/devices/video/crt9021.h139
-rw-r--r--src/devices/video/crt9212.c195
-rw-r--r--src/devices/video/crt9212.h126
-rw-r--r--src/devices/video/crtc_ega.c728
-rw-r--r--src/devices/video/crtc_ega.h236
-rw-r--r--src/devices/video/dl1416.c297
-rw-r--r--src/devices/video/dl1416.h78
-rw-r--r--src/devices/video/dm9368.c96
-rw-r--r--src/devices/video/dm9368.h75
-rw-r--r--src/devices/video/ef9340_1.c383
-rw-r--r--src/devices/video/ef9340_1.h81
-rw-r--r--src/devices/video/ef9341_chargen.h269
-rw-r--r--src/devices/video/ef9345.c1021
-rw-r--r--src/devices/video/ef9345.h118
-rw-r--r--src/devices/video/epic12.c949
-rw-r--r--src/devices/video/epic12.h840
-rw-r--r--src/devices/video/epic12_blit0.c558
-rw-r--r--src/devices/video/epic12_blit1.c558
-rw-r--r--src/devices/video/epic12_blit2.c558
-rw-r--r--src/devices/video/epic12_blit3.c558
-rw-r--r--src/devices/video/epic12_blit4.c558
-rw-r--r--src/devices/video/epic12_blit5.c558
-rw-r--r--src/devices/video/epic12_blit6.c558
-rw-r--r--src/devices/video/epic12_blit7.c558
-rw-r--r--src/devices/video/epic12_blit8.c42
-rw-r--r--src/devices/video/epic12in.inc166
-rw-r--r--src/devices/video/epic12pixel.inc193
-rw-r--r--src/devices/video/fixfreq.c304
-rw-r--r--src/devices/video/fixfreq.h163
-rw-r--r--src/devices/video/generic.c94
-rw-r--r--src/devices/video/generic.h31
-rw-r--r--src/devices/video/gf4500.c184
-rw-r--r--src/devices/video/gf4500.h56
-rw-r--r--src/devices/video/gf7600gs.c33
-rw-r--r--src/devices/video/gf7600gs.h27
-rw-r--r--src/devices/video/h63484.c2190
-rw-r--r--src/devices/video/h63484.h189
-rw-r--r--src/devices/video/hd44102.c292
-rw-r--r--src/devices/video/hd44102.h86
-rw-r--r--src/devices/video/hd44352.c439
-rw-r--r--src/devices/video/hd44352.h81
-rw-r--r--src/devices/video/hd44780.c569
-rw-r--r--src/devices/video/hd44780.h146
-rw-r--r--src/devices/video/hd61830.c535
-rw-r--r--src/devices/video/hd61830.h125
-rw-r--r--src/devices/video/hd63484.c1578
-rw-r--r--src/devices/video/hd63484.h81
-rw-r--r--src/devices/video/hd66421.c287
-rw-r--r--src/devices/video/hd66421.h84
-rw-r--r--src/devices/video/huc6202.c367
-rw-r--r--src/devices/video/huc6202.h130
-rw-r--r--src/devices/video/huc6260.c311
-rw-r--r--src/devices/video/huc6260.h98
-rw-r--r--src/devices/video/huc6261.c434
-rw-r--r--src/devices/video/huc6261.h81
-rw-r--r--src/devices/video/huc6270.c895
-rw-r--r--src/devices/video/huc6270.h141
-rw-r--r--src/devices/video/huc6272.c249
-rw-r--r--src/devices/video/huc6272.h80
-rw-r--r--src/devices/video/i8244.c799
-rw-r--r--src/devices/video/i8244.h169
-rw-r--r--src/devices/video/i82730.c527
-rw-r--r--src/devices/video/i82730.h157
-rw-r--r--src/devices/video/i8275.c691
-rw-r--r--src/devices/video/i8275.h236
-rw-r--r--src/devices/video/m50458.c449
-rw-r--r--src/devices/video/m50458.h105
-rw-r--r--src/devices/video/mb90082.c248
-rw-r--r--src/devices/video/mb90082.h85
-rw-r--r--src/devices/video/mb_vcu.c579
-rw-r--r--src/devices/video/mb_vcu.h92
-rw-r--r--src/devices/video/mc6845.c1601
-rw-r--r--src/devices/video/mc6845.h503
-rw-r--r--src/devices/video/mc6847.c1890
-rw-r--r--src/devices/video/mc6847.h706
-rw-r--r--src/devices/video/mos6566.c2839
-rw-r--r--src/devices/video/mos6566.h455
-rw-r--r--src/devices/video/msm6222b.c246
-rw-r--r--src/devices/video/msm6222b.h63
-rw-r--r--src/devices/video/msm6255.c437
-rw-r--r--src/devices/video/msm6255.h95
-rw-r--r--src/devices/video/pc_vga.c5559
-rw-r--r--src/devices/video/pc_vga.h705
-rw-r--r--src/devices/video/poly.h1175
-rw-r--r--src/devices/video/polylgcy.c1359
-rw-r--r--src/devices/video/polylgcy.h155
-rw-r--r--src/devices/video/psx.c3822
-rw-r--r--src/devices/video/psx.h361
-rw-r--r--src/devices/video/ramdac.c204
-rw-r--r--src/devices/video/ramdac.h87
-rw-r--r--src/devices/video/resnet.c719
-rw-r--r--src/devices/video/resnet.h199
-rw-r--r--src/devices/video/rgbgen.c136
-rw-r--r--src/devices/video/rgbgen.h507
-rw-r--r--src/devices/video/rgbsse.c194
-rw-r--r--src/devices/video/rgbsse.h534
-rw-r--r--src/devices/video/rgbutil.h24
-rw-r--r--src/devices/video/rgbvmx.c220
-rw-r--r--src/devices/video/rgbvmx.h509
-rw-r--r--src/devices/video/saa5050.c525
-rw-r--r--src/devices/video/saa5050.h257
-rw-r--r--src/devices/video/scn2674.c766
-rw-r--r--src/devices/video/scn2674.h133
-rw-r--r--src/devices/video/sed1200.c192
-rw-r--r--src/devices/video/sed1200.h92
-rw-r--r--src/devices/video/sed1330.c688
-rw-r--r--src/devices/video/sed1330.h126
-rw-r--r--src/devices/video/sed1520.c164
-rw-r--r--src/devices/video/sed1520.h72
-rw-r--r--src/devices/video/snes_ppu.c2949
-rw-r--r--src/devices/video/snes_ppu.h295
-rw-r--r--src/devices/video/stvvdp1.c2160
-rw-r--r--src/devices/video/stvvdp2.c6968
-rw-r--r--src/devices/video/t6a04.c242
-rw-r--r--src/devices/video/t6a04.h72
-rw-r--r--src/devices/video/tea1002.c85
-rw-r--r--src/devices/video/tea1002.h66
-rw-r--r--src/devices/video/tlc34076.c263
-rw-r--r--src/devices/video/tlc34076.h76
-rw-r--r--src/devices/video/tms34061.c557
-rw-r--r--src/devices/video/tms34061.h122
-rw-r--r--src/devices/video/tms3556.c658
-rw-r--r--src/devices/video/tms3556.h139
-rw-r--r--src/devices/video/tms9927.c326
-rw-r--r--src/devices/video/tms9927.h110
-rw-r--r--src/devices/video/tms9928a.c731
-rw-r--r--src/devices/video/tms9928a.h207
-rw-r--r--src/devices/video/upd3301.c635
-rw-r--r--src/devices/video/upd3301.h194
-rw-r--r--src/devices/video/upd7220.c1683
-rw-r--r--src/devices/video/upd7220.h234
-rw-r--r--src/devices/video/upd7227.c162
-rw-r--r--src/devices/video/upd7227.h95
-rw-r--r--src/devices/video/v9938.c3012
-rw-r--r--src/devices/video/v9938.h250
-rw-r--r--src/devices/video/vector.c307
-rw-r--r--src/devices/video/vector.h73
-rw-r--r--src/devices/video/vic4567.c2055
-rw-r--r--src/devices/video/vic4567.h273
-rw-r--r--src/devices/video/vooddefs.h5099
-rw-r--r--src/devices/video/voodoo.c6537
-rw-r--r--src/devices/video/voodoo.h168
-rw-r--r--src/devices/video/voodoo_pci.c180
-rw-r--r--src/devices/video/voodoo_pci.h59
161 files changed, 99745 insertions, 0 deletions
diff --git a/src/devices/video/315_5124.c b/src/devices/video/315_5124.c
new file mode 100644
index 00000000000..dac2027d126
--- /dev/null
+++ b/src/devices/video/315_5124.c
@@ -0,0 +1,1709 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol, Enik Land
+/*********************************************************************
+
+ sega315_5124.c
+
+ Implementation of video hardware chips used by Sega System E,
+ Master System, and Game Gear.
+
+**********************************************************************/
+
+/*
+
+To do:
+
+ - Display mode 1 (text)
+ - Display mode 3 (multicolor)
+ - Sprite doubling bug of the 315-5124 chip
+ - Verify timing on the Game Gear (315-5378 chip)
+
+
+SMS Display Timing
+------------------
+ For more information, please see:
+ - http://cgfm2.emuviews.com/txt/msvdp.txt
+ - http://www.smspower.org/forums/viewtopic.php?p=44198
+
+A scanline contains the following sections:
+ - horizontal sync 9 E9-ED => HSYNC high
+ - left blanking 2 ED-EE
+ - color burst 14 EE-F5 => increment line counter/generate interrupts/etc
+ - left blanking 8 F5-F9
+ - left border 13 F9-FF
+ - active display 256 00-7F
+ - right border 15 80-87
+ - right blanking 8 87-8B
+ - horizontal sync 17 8B-93 => HSYNC low
+
+
+NTSC frame timing
+ 256x192 256x224 256x240 (doesn't work on real hardware)
+ - vertical blanking 3 D5-D7 3 E5-E7 3 ED-EF
+ - top blanking 13 D8-E4 13 E8-F4 13 F0-FC
+ - top border 27 E5-FF 11 F5-FF 3 FD-FF
+ - active display 192 00-BF 224 00-DF 240 00-EF
+ - bottom border 24 C0-D7 8 E0-E7 0 F0-F0
+ - bottom blanking 3 D8-DA 3 E8-EA 3 F0-F2
+
+
+PAL frame timing
+ 256x192 256x224 256x240
+ - vertical blanking 3 BA-BC 3 CA-CC 3 D2-D4
+ - top blanking 13 BD-C9 13 CD-D9 13 D5-E1
+ - top border 54 CA-FF 38 DA-FF 30 E2-FF
+ - active display 192 00-BF 224 00-DF 240 00-EF
+ - bottom border 48 C0-EF 32 E0-FF 24 F0-07
+ - bottom blanking 3 F0-F2 3 00-02 3 08-0A
+
+*/
+
+#include "emu.h"
+#include "video/315_5124.h"
+
+
+#define STATUS_VINT 0x80 /* Pending vertical interrupt flag */
+#define STATUS_SPROVR 0x40 /* Sprite overflow flag */
+#define STATUS_SPRCOL 0x20 /* Object collision flag */
+#define STATUS_HINT 0x02 /* Pending horizontal interrupt flag */
+
+#define VINT_HPOS 24
+#define VINT_FLAG_HPOS 24
+#define HINT_HPOS 26
+#define NMI_HPOS 28 /* not verified */
+#define VCOUNT_CHANGE_HPOS 23
+#define SPROVR_HPOS 24
+#define SPRCOL_BASEHPOS 59
+#define XSCROLL_HPOS 21
+#define DISPLAY_DISABLED_HPOS 24 /* not verified, works if above 18 (for 'pstrike2') and below 25 (for 'fantdizzy') */
+#define DISPLAY_CB_HPOS 2 /* fixes 'roadrash' (SMS game) title scrolling, due to line counter reload timing */
+
+#define DRAW_TIME_GG 94 /* 9 + 2 + 14 + 8 + 13 + 96/2 */
+#define DRAW_TIME_SMS 46 /* 9 + 2 + 14 + 8 + 13 */
+
+#define PRIORITY_BIT 0x1000
+#define BACKDROP_COLOR ((m_vdp_mode == 4 ? 0x10 : 0x00) + (m_reg[0x07] & 0x0f))
+
+#define VERTICAL_BLANKING 0
+#define TOP_BLANKING 1
+#define TOP_BORDER 2
+#define ACTIVE_DISPLAY_V 3
+#define BOTTOM_BORDER 4
+#define BOTTOM_BLANKING 5
+
+static const UINT8 ntsc_192[6] = { 3, 13, 27, 192, 24, 3 };
+static const UINT8 ntsc_224[6] = { 3, 13, 11, 224, 8, 3 };
+static const UINT8 ntsc_240[6] = { 3, 13, 3, 240, 0, 3 };
+static const UINT8 pal_192[6] = { 3, 13, 54, 192, 48, 3 };
+static const UINT8 pal_224[6] = { 3, 13, 38, 224, 32, 3 };
+static const UINT8 pal_240[6] = { 3, 13, 30, 240, 24, 3 };
+
+
+const device_type SEGA315_5124 = &device_creator<sega315_5124_device>;
+const device_type SEGA315_5246 = &device_creator<sega315_5246_device>;
+const device_type SEGA315_5378 = &device_creator<sega315_5378_device>;
+
+
+PALETTE_INIT_MEMBER(sega315_5124_device, sega315_5124)
+{
+ int i;
+ for (i = 0; i < 64; i++)
+ {
+ int r = i & 0x03;
+ int g = (i & 0x0c) >> 2;
+ int b = (i & 0x30) >> 4;
+ palette.set_pen_color(i, pal2bit(r), pal2bit(g), pal2bit(b));
+ }
+ /* sms and sg1000-mark3 uses a different palette for modes 0 to 3 - see http://www.smspower.org/Development/Palette */
+ /* TMS9918 palette */
+ palette.set_pen_color(64+ 0, 0, 0, 0); // palette.set_pen_color(64+ 0, 0, 0, 0);
+ palette.set_pen_color(64+ 1, 0, 0, 0); // palette.set_pen_color(64+ 1, 0, 0, 0);
+ palette.set_pen_color(64+ 2, 0, 170, 0); // palette.set_pen_color(64+ 2, 33, 200, 66);
+ palette.set_pen_color(64+ 3, 0, 255, 0); // palette.set_pen_color(64+ 3, 94, 220, 120);
+ palette.set_pen_color(64+ 4, 0, 0, 85); // palette.set_pen_color(64+ 4, 84, 85, 237);
+ palette.set_pen_color(64+ 5, 0, 0, 255); // palette.set_pen_color(64+ 5, 125, 118, 252);
+ palette.set_pen_color(64+ 6, 85, 0, 0); // palette.set_pen_color(64+ 6, 212, 82, 77);
+ palette.set_pen_color(64+ 7, 0, 255, 255); // palette.set_pen_color(64+ 7, 66, 235, 245);
+ palette.set_pen_color(64+ 8, 170, 0, 0); // palette.set_pen_color(64+ 8, 252, 85, 84);
+ palette.set_pen_color(64+ 9, 255, 0, 0); // palette.set_pen_color(64+ 9, 255, 121, 120);
+ palette.set_pen_color(64+10, 85, 85, 0); // palette.set_pen_color(64+10, 212, 193, 84);
+ palette.set_pen_color(64+11, 255, 255, 0); // palette.set_pen_color(64+11, 230, 206, 128);
+ palette.set_pen_color(64+12, 0, 85, 0); // palette.set_pen_color(64+12, 33, 176, 59);
+ palette.set_pen_color(64+13, 255, 0, 255); // palette.set_pen_color(64+13, 201, 91, 186);
+ palette.set_pen_color(64+14, 85, 85, 85); // palette.set_pen_color(64+14, 204, 204, 204);
+ palette.set_pen_color(64+15, 255, 255, 255); // palette.set_pen_color(64+15, 255, 255, 255);
+}
+
+
+PALETTE_INIT_MEMBER(sega315_5378_device, sega315_5378)
+{
+ int i;
+ for (i = 0; i < 4096; i++)
+ {
+ int r = i & 0x000f;
+ int g = (i & 0x00f0) >> 4;
+ int b = (i & 0x0f00) >> 8;
+ palette.set_pen_color(i, pal4bit(r), pal4bit(g), pal4bit(b));
+ }
+}
+
+
+// default address map
+static ADDRESS_MAP_START( sega315_5124, AS_0, 8, sega315_5124_device )
+ AM_RANGE(0x0000, VRAM_SIZE-1) AM_RAM
+ADDRESS_MAP_END
+
+
+sega315_5124_device::sega315_5124_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t( mconfig, SEGA315_5124, "Sega 315-5124 VDP", tag, owner, clock, "sega315_5124", __FILE__)
+ , device_memory_interface(mconfig, *this)
+ , device_video_interface(mconfig, *this)
+ , m_cram_size( SEGA315_5124_CRAM_SIZE )
+ , m_palette_offset( 0 )
+ , m_supports_224_240( false )
+ , m_is_pal(false)
+ , m_int_cb(*this)
+ , m_pause_cb(*this)
+ , m_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(sega315_5124))
+ , m_palette(*this, "palette")
+{
+}
+
+
+sega315_5124_device::sega315_5124_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT8 cram_size, UINT8 palette_offset, bool supports_224_240, const char *shortname, const char *source)
+ : device_t( mconfig, type, name, tag, owner, clock, shortname, source)
+ , device_memory_interface(mconfig, *this)
+ , device_video_interface(mconfig, *this)
+ , m_cram_size( cram_size )
+ , m_palette_offset( palette_offset )
+ , m_supports_224_240( supports_224_240 )
+ , m_is_pal(false)
+ , m_int_cb(*this)
+ , m_pause_cb(*this)
+ , m_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(sega315_5124))
+ , m_palette(*this, "palette")
+{
+}
+
+
+sega315_5246_device::sega315_5246_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sega315_5124_device( mconfig, SEGA315_5246, "Sega 315-5246 VDP", tag, owner, clock, SEGA315_5124_CRAM_SIZE, 0, true, "sega315_5246", __FILE__)
+{
+}
+
+
+sega315_5378_device::sega315_5378_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sega315_5124_device( mconfig, SEGA315_5378, "Sega 315-5378 VDP", tag, owner, clock, SEGA315_5378_CRAM_SIZE, 0x10, true, "sega315_5378", __FILE__)
+{
+}
+
+
+void sega315_5124_device::set_display_settings()
+{
+ const bool M1 = m_reg[0x01] & 0x10;
+ const bool M2 = m_reg[0x00] & 0x02;
+ const bool M3 = m_reg[0x01] & 0x08;
+ const bool M4 = m_reg[0x00] & 0x04;
+
+ m_y_pixels = 192;
+
+ if (M4)
+ {
+ /* mode 4 */
+ m_vdp_mode = 4;
+ if ( m_supports_224_240 )
+ {
+ if (M2)
+ {
+ if (M1 && !M3)
+ {
+ m_y_pixels = 224; /* 224-line display */
+ }
+ else if (!M1 && M3)
+ {
+ m_y_pixels = 240; /* 240-line display */
+ }
+ }
+ }
+ }
+ else
+ {
+ /* original TMS9918 mode */
+ if (!M1 && !M2 && !M3)
+ {
+ m_vdp_mode = 0;
+ }
+#if 0
+ /* Mode 1, not implemented */
+ else if (M1 && !M2 && !M3)
+ {
+ m_vdp_mode = 1;
+ }
+#endif
+ else if (!M1 && M2 && !M3)
+ {
+ m_vdp_mode = 2;
+ }
+#if 0
+ /* Mode 3, not implemented */
+ else if (!M1 && !M2 && M3)
+ {
+ m_vdp_mode = 3;
+ }
+#endif
+ else
+ {
+ logerror("Unknown video mode detected (M1 = %c, M2 = %c, M3 = %c, M4 = %c)\n", M1 ? '1' : '0', M2 ? '1' : '0', M3 ? '1' : '0', M4 ? '1' : '0');
+ }
+ }
+
+ set_frame_timing();
+ m_cram_dirty = 1;
+}
+
+
+void sega315_5124_device::set_frame_timing()
+{
+ switch (m_y_pixels)
+ {
+ case 192:
+ m_frame_timing = (m_is_pal) ? pal_192 : ntsc_192;
+ break;
+
+ case 224:
+ m_frame_timing = (m_is_pal) ? pal_224 : ntsc_224;
+ break;
+
+ case 240:
+ m_frame_timing = (m_is_pal) ? pal_240 : ntsc_240;
+ break;
+ }
+}
+
+
+READ8_MEMBER( sega315_5124_device::vcount_read )
+{
+ const int active_scr_start = m_frame_timing[VERTICAL_BLANKING] + m_frame_timing[TOP_BLANKING] + m_frame_timing[TOP_BORDER];
+ int vpos = m_screen->vpos();
+
+ if (m_screen->hpos() < VCOUNT_CHANGE_HPOS)
+ {
+ vpos--;
+ if (vpos < 0)
+ vpos += m_screen->height();
+ }
+
+ return (vpos - active_scr_start) & 0xff;
+}
+
+
+READ8_MEMBER( sega315_5124_device::hcount_read )
+{
+ return m_hcounter;
+}
+
+
+void sega315_5124_device::hcount_latch_at_hpos( int hpos )
+{
+ const int active_scr_start = 46; /* 9 + 2 + 14 + 8 + 13 */
+
+ /* The hcount value returned by the VDP seems to be based on the previous hpos */
+ int hclock = hpos - 1;
+ if (hclock < 0)
+ hclock += SEGA315_5124_WIDTH;
+
+ m_hcounter = ((hclock - active_scr_start) >> 1) & 0xff;
+}
+
+
+void sega315_5378_device::set_sega315_5124_compatibility_mode( bool sega315_5124_compatibility_mode )
+{
+ m_sega315_5124_compatibility_mode = sega315_5124_compatibility_mode;
+ m_cram_mask = (!m_sega315_5124_compatibility_mode) ? (SEGA315_5378_CRAM_SIZE - 1) : (SEGA315_5124_CRAM_SIZE - 1);
+ m_draw_time = m_sega315_5124_compatibility_mode ? DRAW_TIME_SMS : DRAW_TIME_GG;
+}
+
+
+void sega315_5124_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch( id )
+ {
+ case TIMER_LINE:
+ process_line_timer();
+ break;
+
+ case TIMER_FLAGS:
+ /* Activate flags that were pending until the end of the line. */
+ check_pending_flags();
+ break;
+
+ case TIMER_DRAW:
+ update_palette();
+ draw_scanline( SEGA315_5124_LBORDER_START + SEGA315_5124_LBORDER_WIDTH, param, m_screen->vpos() - param );
+ break;
+
+ case TIMER_LBORDER:
+ {
+ rectangle rec;
+ rec.min_y = rec.max_y = param;
+
+ update_palette();
+
+ /* Draw left border */
+ rec.min_x = SEGA315_5124_LBORDER_START;
+ rec.max_x = SEGA315_5124_LBORDER_START + SEGA315_5124_LBORDER_WIDTH - 1;
+ m_tmpbitmap.fill(m_palette->pen(m_current_palette[BACKDROP_COLOR]), rec);
+ m_y1_bitmap.fill(( m_reg[0x07] & 0x0f ) ? 1 : 0, rec);
+ }
+ break;
+
+ case TIMER_RBORDER:
+ {
+ rectangle rec;
+ rec.min_y = rec.max_y = param;
+
+ update_palette();
+
+ /* Draw right border */
+ rec.min_x = SEGA315_5124_LBORDER_START + SEGA315_5124_LBORDER_WIDTH + 256;
+ rec.max_x = rec.min_x + SEGA315_5124_RBORDER_WIDTH - 1;
+ m_tmpbitmap.fill(m_palette->pen(m_current_palette[BACKDROP_COLOR]), rec);
+ m_y1_bitmap.fill(( m_reg[0x07] & 0x0f ) ? 1 : 0, rec);
+ }
+ break;
+
+ case TIMER_HINT:
+ if ((m_pending_status & STATUS_HINT) || (m_status & STATUS_HINT))
+ {
+ if ((m_reg[0x00] & 0x10))
+ {
+ m_irq_state = 1;
+
+ if ( !m_int_cb.isnull() )
+ m_int_cb(ASSERT_LINE);
+ }
+ }
+ break;
+
+ case TIMER_VINT:
+ if ((m_pending_status & STATUS_VINT) || (m_status & STATUS_VINT))
+ {
+ if ((m_reg[0x01] & 0x20))
+ {
+ m_irq_state = 1;
+
+ if ( !m_int_cb.isnull() )
+ m_int_cb(ASSERT_LINE);
+ }
+ }
+ break;
+
+ case TIMER_NMI:
+ if ( !m_pause_cb.isnull() )
+ m_pause_cb(0);
+ break;
+ }
+}
+
+
+void sega315_5124_device::process_line_timer()
+{
+ const int vpos = m_screen->vpos();
+ int vpos_limit = m_frame_timing[VERTICAL_BLANKING] + m_frame_timing[TOP_BLANKING]
+ + m_frame_timing[TOP_BORDER] + m_frame_timing[ACTIVE_DISPLAY_V]
+ + m_frame_timing[BOTTOM_BORDER] + m_frame_timing[BOTTOM_BLANKING];
+
+ /* copy current values in case they are not changed until latch time */
+ m_display_disabled = !(m_reg[0x01] & 0x40);
+ m_reg8copy = m_reg[0x08];
+
+ vpos_limit -= m_frame_timing[BOTTOM_BLANKING];
+
+ /* Check if we're below the bottom border */
+ if (vpos >= vpos_limit)
+ {
+ m_line_counter = m_reg[0x0a];
+ return;
+ }
+
+ vpos_limit -= m_frame_timing[BOTTOM_BORDER];
+
+ /* Check if we're in the bottom border area */
+ if (vpos >= vpos_limit)
+ {
+ if (vpos == vpos_limit)
+ {
+ if (m_line_counter == 0x00)
+ {
+ m_line_counter = m_reg[0x0a];
+ m_hint_timer->adjust( m_screen->time_until_pos( vpos, HINT_HPOS ) );
+ m_pending_status |= STATUS_HINT;
+ }
+ else
+ {
+ m_line_counter--;
+ }
+ }
+ else
+ {
+ m_line_counter = m_reg[0x0a];
+ }
+
+ if (vpos == vpos_limit + 1)
+ {
+ m_vint_timer->adjust( m_screen->time_until_pos( vpos, VINT_HPOS ) );
+ m_pending_status |= STATUS_VINT;
+ }
+
+ /* Draw borders */
+ m_lborder_timer->adjust( m_screen->time_until_pos( vpos, SEGA315_5124_LBORDER_START ), vpos );
+ m_rborder_timer->adjust( m_screen->time_until_pos( vpos, SEGA315_5124_LBORDER_START + SEGA315_5124_LBORDER_WIDTH + 256 ), vpos );
+
+ /* Draw middle of the border */
+ /* We need to do this through the regular drawing function */
+ /* so sprite collisions can occur on the border. */
+ select_sprites( vpos - (vpos_limit - m_frame_timing[ACTIVE_DISPLAY_V]) );
+ m_draw_timer->adjust( m_screen->time_until_pos( vpos, m_draw_time ), vpos_limit - m_frame_timing[ACTIVE_DISPLAY_V] );
+ return;
+ }
+
+ vpos_limit -= m_frame_timing[ACTIVE_DISPLAY_V];
+
+ /* Check if we're in the active display area */
+ if (vpos >= vpos_limit)
+ {
+ if (vpos == vpos_limit)
+ {
+ m_reg9copy = m_reg[0x09];
+ }
+
+ if (m_line_counter == 0x00)
+ {
+ m_line_counter = m_reg[0x0a];
+ m_hint_timer->adjust( m_screen->time_until_pos( vpos, HINT_HPOS ) );
+ m_pending_status |= STATUS_HINT;
+ }
+ else
+ {
+ m_line_counter--;
+ }
+
+ /* Draw borders */
+ m_lborder_timer->adjust( m_screen->time_until_pos( vpos, SEGA315_5124_LBORDER_START ), vpos );
+ m_rborder_timer->adjust( m_screen->time_until_pos( vpos, SEGA315_5124_LBORDER_START + SEGA315_5124_LBORDER_WIDTH + 256 ), vpos );
+
+ /* Draw active display */
+ select_sprites( vpos - vpos_limit );
+ m_draw_timer->adjust( m_screen->time_until_pos( vpos, m_draw_time ), vpos_limit );
+ return;
+ }
+
+ vpos_limit -= m_frame_timing[TOP_BORDER];
+
+ /* Check if we're in the top border area */
+ if (vpos >= vpos_limit)
+ {
+ m_line_counter = m_reg[0x0a];
+
+ /* Check if we're on the last line of the top border */
+ if (vpos == vpos_limit + m_frame_timing[TOP_BORDER] - 1)
+ {
+ m_nmi_timer->adjust( m_screen->time_until_pos( vpos, NMI_HPOS ) );
+ }
+
+ /* Draw borders */
+ m_lborder_timer->adjust( m_screen->time_until_pos( vpos, SEGA315_5124_LBORDER_START ), vpos );
+ m_rborder_timer->adjust( m_screen->time_until_pos( vpos, SEGA315_5124_LBORDER_START + SEGA315_5124_LBORDER_WIDTH + 256 ), vpos );
+
+ /* Draw middle of the border */
+ /* We need to do this through the regular drawing function */
+ /* so sprite collisions can occur on the border. */
+ select_sprites( vpos - (vpos_limit + m_frame_timing[TOP_BORDER]) );
+ m_draw_timer->adjust( m_screen->time_until_pos( vpos, m_draw_time ), vpos_limit + m_frame_timing[TOP_BORDER] );
+ return;
+ }
+
+ /* we're in the vertical or top blanking area */
+ m_line_counter = m_reg[0x0a];
+}
+
+
+READ8_MEMBER( sega315_5124_device::vram_read )
+{
+ UINT8 temp;
+
+ /* SMS 2 & GG behaviour. Seems like the latched data is passed straight through */
+ /* to the address register when in the middle of doing a command. */
+ /* Cosmic Spacehead needs this, among others */
+ /* Clear pending write flag */
+ m_pending_reg_write = 0;
+
+ /* Return read buffer contents */
+ temp = m_buffer;
+
+ if ( !space.debugger_access() )
+ {
+ /* Load read buffer */
+ m_buffer = this->space().read_byte(m_addr & 0x3fff);
+
+ /* Bump internal address register */
+ m_addr += 1;
+ }
+ return temp;
+}
+
+
+void sega315_5124_device::check_pending_flags()
+{
+ int hpos;
+
+ if (!m_pending_status)
+ {
+ return;
+ }
+
+ /* A timer ensures that this function will run at least at end of each line.
+ When this function runs through a CPU instruction executed when the timer
+ was about to fire, the time added in the CPU timeslice may make hpos()
+ return some position in the beginning of next line. To ensure the instruction
+ will get updated status, here a maximum hpos is set if the timer reports no
+ remaining time, what could also occur due to the ahead time of the timeslice. */
+ if (m_pending_flags_timer->remaining() == attotime::zero)
+ {
+ hpos = SEGA315_5124_WIDTH - 1;
+ }
+ else
+ {
+ hpos = m_screen->hpos();
+ }
+
+ if ((m_pending_status & STATUS_HINT) && hpos >= HINT_HPOS)
+ {
+ m_pending_status &= ~STATUS_HINT;
+ m_status |= STATUS_HINT; // fake flag, it is overridden on register read.
+ }
+ if ((m_pending_status & STATUS_VINT) && hpos >= VINT_FLAG_HPOS)
+ {
+ m_pending_status &= ~STATUS_VINT;
+ m_status |= STATUS_VINT;
+ }
+ if ((m_pending_status & STATUS_SPROVR) && hpos >= SPROVR_HPOS)
+ {
+ m_pending_status &= ~STATUS_SPROVR;
+ m_status |= STATUS_SPROVR;
+ }
+ if ((m_pending_status & STATUS_SPRCOL) && hpos >= m_pending_sprcol_x)
+ {
+ m_pending_status &= ~STATUS_SPRCOL;
+ m_status |= STATUS_SPRCOL;
+ m_pending_sprcol_x = 0;
+ }
+}
+
+
+READ8_MEMBER( sega315_5124_device::register_read )
+{
+ UINT8 temp;
+
+ check_pending_flags();
+ temp = m_status;
+
+ if ( !space.debugger_access() )
+ {
+ /* Clear pending write flag */
+ m_pending_reg_write = 0;
+
+ m_status &= ~(STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL | STATUS_HINT);
+
+ if (m_irq_state == 1)
+ {
+ m_irq_state = 0;
+
+ if ( !m_int_cb.isnull() )
+ m_int_cb(CLEAR_LINE);
+ }
+ }
+
+ /* low 5 bits return non-zero data (it fixes PGA Tour Golf course map introduction) */
+ return temp | 0x1f;
+}
+
+
+WRITE8_MEMBER( sega315_5124_device::vram_write )
+{
+ /* SMS 2 & GG behaviour. Seems like the latched data is passed straight through */
+ /* to the address register when in the middle of doing a command. */
+ /* Cosmic Spacehead needs this, among others */
+ /* Clear pending write flag */
+ m_pending_reg_write = 0;
+
+ switch(m_addrmode)
+ {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ this->space().write_byte(m_addr & 0x3fff, data);
+ break;
+
+ case 0x03:
+ cram_write(data);
+ break;
+ }
+
+ m_buffer = data;
+ m_addr += 1;
+}
+
+
+WRITE8_MEMBER( sega315_5124_device::register_write )
+{
+ int reg_num;
+
+ if (m_pending_reg_write == 0)
+ {
+ m_addr = (m_addr & 0xff00) | data;
+ m_pending_reg_write = 1;
+ }
+ else
+ {
+ /* Clear pending write flag */
+ m_pending_reg_write = 0;
+
+ m_addrmode = (data >> 6) & 0x03;
+ m_addr = (data << 8) | (m_addr & 0xff);
+ switch (m_addrmode)
+ {
+ case 0: /* VRAM reading mode */
+ m_buffer = this->space().read_byte(m_addr & 0x3fff);
+ m_addr += 1;
+ break;
+
+ case 1: /* VRAM writing mode */
+ break;
+
+ case 2: /* VDP register write */
+ reg_num = data & 0x0f;
+ m_reg[reg_num] = m_addr & 0xff;
+ //logerror("%s: %s: setting register %x to %02x\n", machine().describe_context(), tag(), reg_num, m_addr & 0xff);
+
+ switch (reg_num)
+ {
+ case 0:
+ set_display_settings();
+ if (m_addr & 0x02)
+ logerror("overscan enabled.\n");
+ break;
+ case 1:
+ set_display_settings();
+ if (m_screen->hpos() <= DISPLAY_DISABLED_HPOS)
+ m_display_disabled = !(m_reg[0x01] & 0x40);
+ break;
+ case 8:
+ if (m_screen->hpos() <= XSCROLL_HPOS)
+ m_reg8copy = m_reg[0x08];
+ }
+
+ check_pending_flags();
+
+ if ( ( reg_num == 0 && (m_status & STATUS_HINT) ) ||
+ ( reg_num == 1 && (m_status & STATUS_VINT) ) )
+ {
+ // For HINT disabling through register 00:
+ // "Line IRQ VCount" test, of Flubba's VDPTest ROM, disables HINT to wait
+ // for next VINT, but HINT occurs when the operation is about to execute.
+ // So here, where the setting is done, the irq_state needs to be cleared.
+ //
+ // For VINT disabling through register 01:
+ // When running eagles5 on the smskr driver the irq_state is 1 because of some
+ // previos HINTs that occurred. eagles5 sets register 01 to 0x02 and expects
+ // the irq state to be cleared after that.
+ // The following bit of code takes care of that.
+ //
+ if ( ( reg_num == 0 && !(m_reg[0x00] & 0x10) ) ||
+ ( reg_num == 1 && !(m_reg[0x01] & 0x20) ) )
+ {
+ if (m_irq_state == 1)
+ {
+ m_irq_state = 0;
+
+ if ( !m_int_cb.isnull() )
+ {
+ m_int_cb(CLEAR_LINE);
+ }
+ }
+ }
+ else
+ {
+ // For register 01 and VINT enabling:
+ // Assert the IRQ line for the scoreboard of robocop3,
+ // on the sms/smspal driver, be displayed correctly.
+ //
+ // Assume the same behavior for reg0+HINT.
+ //
+ m_irq_state = 1;
+
+ if ( !m_int_cb.isnull() )
+ m_int_cb(ASSERT_LINE);
+ }
+ }
+ m_addrmode = 0;
+ break;
+
+ case 3: /* CRAM writing mode */
+ break;
+ }
+ }
+}
+
+
+UINT16 sega315_5124_device::get_name_table_row(int row)
+{
+ return ((row >> 3) << 6) & (((m_reg[0x02] & 0x01) << 10) | 0x3bff);
+}
+
+
+UINT16 sega315_5246_device::get_name_table_row(int row)
+{
+ return (row >> 3) << 6;
+}
+
+
+UINT16 sega315_5378_device::get_name_table_row(int row)
+{
+ return (row >> 3) << 6;
+}
+
+
+void sega315_5124_device::draw_scanline_mode4( int *line_buffer, int *priority_selected, int line )
+{
+ int tile_column;
+ int y_scroll, scroll_mod;
+ int pixel_x, pixel_plot_x;
+ int bit_plane_0, bit_plane_1, bit_plane_2, bit_plane_3;
+ UINT16 name_table_address;
+
+ /* if top 2 rows of screen not affected by horizontal scrolling, then x_scroll = 0 */
+ /* else x_scroll = m_reg8copy */
+ const int x_scroll = (((m_reg[0x00] & 0x40) && (line < 16)) ? 0 : 0x0100 - m_reg8copy);
+
+ const int x_scroll_start_column = (x_scroll >> 3); /* x starting column tile */
+
+ if ( m_y_pixels != 192 )
+ {
+ name_table_address = ((m_reg[0x02] & 0x0c) << 10) | 0x0700;
+ scroll_mod = 256;
+ }
+ else
+ {
+ name_table_address = (m_reg[0x02] << 10) & 0x3800;
+ scroll_mod = 224;
+ }
+
+ /* Draw background layer */
+ for (tile_column = 0; tile_column < 33; tile_column++)
+ {
+ UINT16 tile_data;
+ int tile_selected, palette_selected, horiz_selected, vert_selected, priority_select;
+ int tile_line;
+
+ /* Rightmost 8 columns for SMS (or 2 columns for GG) not affected by */
+ /* vertical scrolling when bit 7 of reg[0x00] is set */
+ y_scroll = ((m_reg[0x00] & 0x80) && (tile_column > 23)) ? 0 : m_reg9copy;
+
+ tile_line = ((tile_column + x_scroll_start_column) & 0x1f) << 1;
+ tile_data = space().read_word(name_table_address + get_name_table_row((line + y_scroll) % scroll_mod) + tile_line);
+
+ tile_selected = (tile_data & 0x01ff);
+ priority_select = tile_data & PRIORITY_BIT;
+ palette_selected = (tile_data >> 11) & 0x01;
+ vert_selected = (tile_data >> 10) & 0x01;
+ horiz_selected = (tile_data >> 9) & 0x01;
+
+ tile_line = line - ((0x07 - (y_scroll & 0x07)) + 1);
+ if (vert_selected)
+ tile_line = 0x07 - tile_line;
+
+ bit_plane_0 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x00);
+ bit_plane_1 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x01);
+ bit_plane_2 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x02);
+ bit_plane_3 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x03);
+
+ for (pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ UINT8 pen_bit_0, pen_bit_1, pen_bit_2, pen_bit_3;
+ UINT8 pen_selected;
+
+ pen_bit_0 = (bit_plane_0 >> (7 - pixel_x)) & 0x01;
+ pen_bit_1 = (bit_plane_1 >> (7 - pixel_x)) & 0x01;
+ pen_bit_2 = (bit_plane_2 >> (7 - pixel_x)) & 0x01;
+ pen_bit_3 = (bit_plane_3 >> (7 - pixel_x)) & 0x01;
+
+ pen_selected = (pen_bit_3 << 3 | pen_bit_2 << 2 | pen_bit_1 << 1 | pen_bit_0);
+ if (palette_selected)
+ pen_selected |= 0x10;
+
+ if (!horiz_selected)
+ {
+ pixel_plot_x = pixel_x;
+ }
+ else
+ {
+ pixel_plot_x = 7 - pixel_x;
+ }
+
+ pixel_plot_x = (0 - (x_scroll & 0x07) + (tile_column << 3) + pixel_plot_x);
+ if (pixel_plot_x >= 0 && pixel_plot_x < 256)
+ {
+ //logerror("%x %x\n", pixel_plot_x, line);
+ if (tile_column == 0 && (x_scroll & 0x07))
+ {
+ /* when the first column hasn't completely entered in the screen, its
+ background is filled only with color #0 of the selected palette */
+ line_buffer[pixel_plot_x] = m_current_palette[palette_selected ? 0x10 : 0x00];
+ priority_selected[pixel_plot_x] = priority_select;
+ }
+ else
+ {
+ line_buffer[pixel_plot_x] = m_current_palette[pen_selected];
+ priority_selected[pixel_plot_x] = priority_select | (pen_selected & 0x0f);
+ }
+ }
+ }
+ }
+}
+
+
+void sega315_5124_device::select_sprites( int line )
+{
+ int max_sprites;
+
+ /* At this point the VDP vcount still doesn't refer the new line,
+ because the logical start point is slightly shifted on the scanline */
+ int parse_line = line - 1;
+
+ /* Check if SI is set */
+ m_sprite_height = (m_reg[0x01] & 0x02) ? 16 : 8;
+ /* Check if MAG is set */
+ m_sprite_zoom = (m_reg[0x01] & 0x01) ? 2 : 1;
+
+ if (m_sprite_zoom > 1)
+ {
+ /* Divide before use the value for comparison, same later with sprite_y, or
+ else an off-by-one bug could occur, as seen with Tarzan, for Game Gear */
+ parse_line >>= 1;
+ }
+
+ m_sprite_count = 0;
+
+ if ( m_vdp_mode == 0 || m_vdp_mode == 2 )
+ {
+ /* TMS9918 compatibility sprites */
+
+ max_sprites = 4;
+
+ m_sprite_base = ((m_reg[0x05] & 0x7f) << 7);
+
+ for (int sprite_index = 0; (sprite_index < 32 * 4) && (m_sprite_count <= max_sprites); sprite_index += 4)
+ {
+ int sprite_y = space().read_byte(m_sprite_base + sprite_index);
+ if (sprite_y == 0xd0)
+ break;
+
+ if (sprite_y >= 240)
+ {
+ sprite_y -= 256;
+ }
+
+ if (m_sprite_zoom > 1)
+ {
+ sprite_y >>= 1;
+ }
+
+ if ((parse_line >= sprite_y) && (parse_line < (sprite_y + m_sprite_height)))
+ {
+ if (m_sprite_count < max_sprites)
+ {
+ int sprite_x = space().read_byte( m_sprite_base + sprite_index + 1 );
+ int sprite_tile_selected = space().read_byte( m_sprite_base + sprite_index + 2 );
+ UINT8 flags = space().read_byte( m_sprite_base + sprite_index + 3 );
+
+ if (flags & 0x80)
+ sprite_x -= 32;
+
+ int sprite_line = parse_line - sprite_y;
+
+ if (m_reg[0x01] & 0x01)
+ sprite_line >>= 1;
+
+ if (m_reg[0x01] & 0x02)
+ {
+ sprite_tile_selected &= 0xfc;
+
+ if (sprite_line > 0x07)
+ {
+ sprite_tile_selected += 1;
+ sprite_line -= 8;
+ }
+ }
+
+ m_sprite_x[m_sprite_count] = sprite_x;
+ m_sprite_tile_selected[m_sprite_count] = sprite_tile_selected;
+ m_sprite_flags[m_sprite_count] = flags;
+ m_sprite_pattern_line[m_sprite_count] = ((m_reg[0x06] & 0x07) << 11) + sprite_line;
+ }
+ m_sprite_count++;
+ }
+ }
+ }
+ else
+ {
+ /* Regular sprites */
+
+ max_sprites = 8;
+
+ m_sprite_base = ((m_reg[0x05] << 7) & 0x3f00);
+
+ for (int sprite_index = 0; (sprite_index < 64) && (m_sprite_count <= max_sprites); sprite_index++)
+ {
+ int sprite_y = space().read_byte(m_sprite_base + sprite_index);
+ if (m_y_pixels == 192 && sprite_y == 0xd0)
+ break;
+
+ if (sprite_y >= 240)
+ {
+ sprite_y -= 256; /* wrap from top if y position is > 240 */
+ }
+
+ if (m_sprite_zoom > 1)
+ {
+ sprite_y >>= 1;
+ }
+
+ if ((parse_line >= sprite_y) && (parse_line < (sprite_y + m_sprite_height)))
+ {
+ if (m_sprite_count < max_sprites)
+ {
+ int sprite_x = space().read_byte( m_sprite_base + 0x80 + (sprite_index << 1) );
+ int sprite_tile_selected = space().read_byte( m_sprite_base + 0x81 + (sprite_index << 1) );
+
+ if (m_reg[0x00] & 0x08)
+ {
+ sprite_x -= 0x08; /* sprite shift */
+ }
+
+ if (m_reg[0x06] & 0x04)
+ {
+ sprite_tile_selected += 256; /* pattern table select */
+ }
+
+ if (m_reg[0x01] & 0x02)
+ {
+ sprite_tile_selected &= 0x01fe; /* force even index */
+ }
+
+ int sprite_line = parse_line - sprite_y;
+
+ if (sprite_line > 0x07)
+ {
+ sprite_tile_selected += 1;
+ }
+
+ m_sprite_x[m_sprite_count] = sprite_x;
+ m_sprite_tile_selected[m_sprite_count] = sprite_tile_selected;
+ m_sprite_pattern_line[m_sprite_count] = ((sprite_line & 0x07) << 2);
+ }
+ m_sprite_count++;
+ }
+ }
+ }
+
+ if ( m_sprite_count > max_sprites )
+ {
+ /* Too many sprites per line */
+
+ m_sprite_count = max_sprites;
+
+ /* Overflow is flagged only on active display and when VINT isn't active */
+ if (!(m_status & STATUS_VINT) && line >= 0 && line < m_frame_timing[ACTIVE_DISPLAY_V])
+ {
+ m_pending_status |= STATUS_SPROVR;
+ }
+ }
+}
+
+
+void sega315_5124_device::draw_sprites_mode4( int *line_buffer, int *priority_selected, int line )
+{
+ bool sprite_col_occurred = false;
+ int sprite_col_x = 255;
+ UINT8 collision_buffer[256];
+ int plot_min_x = 0;
+
+ if (m_display_disabled || m_sprite_count == 0)
+ return;
+
+ /* Sprites aren't drawn and collisions don't occur on column 0 if it is disabled.
+ Note: On Megadrive/Genesis VDP, collisions occur on the disabled column 0. */
+ if (m_reg[0x00] & 0x20)
+ plot_min_x = 8;
+
+ memset(collision_buffer, 0, sizeof(collision_buffer));
+
+ /* Draw sprite layer */
+ for (int sprite_buffer_index = m_sprite_count - 1; sprite_buffer_index >= 0; sprite_buffer_index--)
+ {
+ int sprite_x = m_sprite_x[sprite_buffer_index];
+ int sprite_tile_selected = m_sprite_tile_selected[sprite_buffer_index];
+ UINT16 sprite_pattern_line = m_sprite_pattern_line[sprite_buffer_index];
+
+ UINT8 bit_plane_0 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x00);
+ UINT8 bit_plane_1 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x01);
+ UINT8 bit_plane_2 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x02);
+ UINT8 bit_plane_3 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x03);
+
+ for (int pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ int pixel_plot_x;
+ UINT8 pen_bit_0 = (bit_plane_0 >> (7 - pixel_x)) & 0x01;
+ UINT8 pen_bit_1 = (bit_plane_1 >> (7 - pixel_x)) & 0x01;
+ UINT8 pen_bit_2 = (bit_plane_2 >> (7 - pixel_x)) & 0x01;
+ UINT8 pen_bit_3 = (bit_plane_3 >> (7 - pixel_x)) & 0x01;
+ UINT8 pen_selected = (pen_bit_3 << 3 | pen_bit_2 << 2 | pen_bit_1 << 1 | pen_bit_0) | 0x10;
+
+ if (pen_selected == 0x10)
+ {
+ /* Transparent palette so skip draw */
+ continue;
+ }
+
+ if (m_sprite_zoom > 1)
+ {
+ /* sprite doubling is enabled */
+ pixel_plot_x = sprite_x + (pixel_x << 1);
+ }
+ else
+ {
+ pixel_plot_x = sprite_x + pixel_x;
+ }
+
+ /* Draw at pixel position and, if zoomed, at pixel+1 */
+ for (int zoom = 0; zoom < m_sprite_zoom; zoom++)
+ {
+ pixel_plot_x += zoom;
+
+ /* check to prevent going outside of active display area */
+ if (pixel_plot_x < plot_min_x || pixel_plot_x > 255)
+ {
+ continue;
+ }
+
+ /* Draw sprite pixel */
+ /* Check if the background has lower priority */
+ if (!(priority_selected[pixel_plot_x] & PRIORITY_BIT))
+ {
+ line_buffer[pixel_plot_x] = m_current_palette[pen_selected];
+ priority_selected[pixel_plot_x] = pen_selected;
+ }
+ else
+ {
+ /* Check if the higher priority background has transparent pixel */
+ if (priority_selected[pixel_plot_x] == PRIORITY_BIT)
+ {
+ line_buffer[pixel_plot_x] = m_current_palette[pen_selected];
+ priority_selected[pixel_plot_x] = pen_selected;
+ }
+ }
+ if (collision_buffer[pixel_plot_x] != 1)
+ {
+ collision_buffer[pixel_plot_x] = 1;
+ }
+ else
+ {
+ sprite_col_occurred = true;
+ sprite_col_x = MIN(sprite_col_x, pixel_plot_x);
+ }
+ }
+ }
+ if (sprite_col_occurred)
+ {
+ m_pending_status |= STATUS_SPRCOL;
+ m_pending_sprcol_x = SPRCOL_BASEHPOS + sprite_col_x;
+ }
+ }
+}
+
+
+void sega315_5124_device::draw_sprites_tms9918_mode( int *line_buffer, int line )
+{
+ bool sprite_col_occurred = false;
+ int sprite_col_x = 255;
+ UINT8 collision_buffer[256];
+
+ if (m_display_disabled || m_sprite_count == 0)
+ return;
+
+ memset(collision_buffer, 0, sizeof(collision_buffer));
+
+ /* Draw sprite layer */
+ for (int sprite_buffer_index = m_sprite_count - 1; sprite_buffer_index >= 0; sprite_buffer_index--)
+ {
+ int sprite_x = m_sprite_x[sprite_buffer_index];
+ UINT8 flags = m_sprite_flags[sprite_buffer_index];
+ int pen_selected = m_palette_offset + ( flags & 0x0f );
+
+ int sprite_tile_selected = m_sprite_tile_selected[sprite_buffer_index];
+ UINT16 sprite_pattern_line = m_sprite_pattern_line[sprite_buffer_index];
+
+ for (int height = 8; height <= m_sprite_height; height += 8)
+ {
+ if (height == 16)
+ {
+ sprite_tile_selected += 2;
+ sprite_x += (m_sprite_zoom > 1 ? 16 : 8);
+ }
+
+ UINT8 pattern = space().read_byte( sprite_pattern_line + sprite_tile_selected * 8 );
+
+ for (int pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ if (pen_selected && (pattern & (1 << (7 - pixel_x))))
+ {
+ int pixel_plot_x;
+ if (m_sprite_zoom > 1)
+ {
+ pixel_plot_x = sprite_x + (pixel_x << 1);
+ }
+ else
+ {
+ pixel_plot_x = sprite_x + pixel_x;
+ }
+
+ /* Draw at pixel position and, if zoomed, at pixel+1 */
+ for (int zoom = 0; zoom < m_sprite_zoom; zoom++)
+ {
+ pixel_plot_x += zoom;
+
+ /* check to prevent going outside of active display area */
+ if (pixel_plot_x < 0 || pixel_plot_x > 255)
+ {
+ continue;
+ }
+
+ line_buffer[pixel_plot_x] = m_current_palette[pen_selected];
+
+ if (collision_buffer[pixel_plot_x] != 1)
+ {
+ collision_buffer[pixel_plot_x] = 1;
+ }
+ else
+ {
+ sprite_col_occurred = true;
+ sprite_col_x = MIN(sprite_col_x, pixel_plot_x);
+ }
+ }
+ }
+ }
+ }
+ if (sprite_col_occurred)
+ {
+ m_pending_status |= STATUS_SPRCOL;
+ m_pending_sprcol_x = SPRCOL_BASEHPOS + sprite_col_x;
+ }
+ }
+}
+
+
+void sega315_5124_device::draw_scanline_mode2( int *line_buffer, int line )
+{
+ int tile_column;
+ int pixel_x, pixel_plot_x;
+ UINT16 name_table_base, color_base, pattern_base;
+ int pattern_mask, color_mask, pattern_offset;
+
+ name_table_base = ((m_reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32);
+ color_base = ((m_reg[0x03] & 0x80) << 6);
+ color_mask = ((m_reg[0x03] & 0x7f) << 3) | 0x07;
+ pattern_base = ((m_reg[0x04] & 0x04) << 11);
+ pattern_mask = ((m_reg[0x04] & 0x03) << 8) | 0xff;
+ pattern_offset = (line & 0xc0) << 2;
+
+ /* Draw background layer */
+ for (tile_column = 0; tile_column < 32; tile_column++)
+ {
+ UINT8 name = space().read_byte( name_table_base + tile_column );
+ UINT8 pattern;
+ UINT8 colors;
+
+ pattern = space().read_byte(pattern_base + (((pattern_offset + name) & pattern_mask) * 8) + (line & 0x07) );
+ colors = space().read_byte(color_base + (((pattern_offset + name) & color_mask) * 8) + (line & 0x07) );
+
+ for (pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ UINT8 pen_selected;
+
+ if (pattern & (1 << (7 - pixel_x)))
+ {
+ pen_selected = colors >> 4;
+ }
+ else
+ {
+ pen_selected = colors & 0x0f;
+ }
+
+ if (!pen_selected)
+ pen_selected = BACKDROP_COLOR;
+
+ pixel_plot_x = (tile_column << 3) + pixel_x;
+
+ pen_selected += m_palette_offset;
+
+ line_buffer[pixel_plot_x] = m_current_palette[pen_selected];
+ }
+ }
+}
+
+
+void sega315_5124_device::draw_scanline_mode0( int *line_buffer, int line )
+{
+ int tile_column;
+ int pixel_x, pixel_plot_x;
+ UINT16 name_base, color_base, pattern_base;
+
+ name_base = ((m_reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32);
+ color_base = ((m_reg[0x03] << 6) & (VRAM_SIZE - 1));
+ pattern_base = ((m_reg[0x04] << 11) & (VRAM_SIZE - 1));
+
+ /* Draw background layer */
+ for (tile_column = 0; tile_column < 32; tile_column++)
+ {
+ UINT8 name = space().read_byte( name_base + tile_column );
+ UINT8 pattern;
+ UINT8 colors;
+
+ pattern = space().read_byte( pattern_base + (name * 8) + (line & 0x07) );
+ colors = space().read_byte( color_base + ( name >> 3 ) );
+
+ for (pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ int pen_selected;
+
+ if (pattern & (1 << (7 - pixel_x)))
+ pen_selected = colors >> 4;
+ else
+ pen_selected = colors & 0x0f;
+
+ pen_selected += m_palette_offset;
+
+ pixel_plot_x = (tile_column << 3) + pixel_x;
+ line_buffer[pixel_plot_x] = m_current_palette[pen_selected];
+ }
+ }
+}
+
+
+void sega315_5124_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, int line )
+{
+ int blitline_buffer[256];
+ int priority_selected[256];
+
+ /* Sprite processing is restricted because collisions on top border of extended
+ resolution break the scoreboard of Fantasy Dizzy (SMS) on smspal driver */
+
+ if ( line < m_frame_timing[ACTIVE_DISPLAY_V] )
+ {
+ memset(priority_selected, 1, sizeof(priority_selected));
+
+ switch( m_vdp_mode )
+ {
+ case 0:
+ if ( line >= 0 )
+ {
+ draw_scanline_mode0( blitline_buffer, line );
+ }
+ if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
+ {
+ draw_sprites_tms9918_mode( blitline_buffer, line );
+ }
+ break;
+
+ case 2:
+ if ( line >= 0 )
+ {
+ draw_scanline_mode2( blitline_buffer, line );
+ }
+ if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
+ {
+ draw_sprites_tms9918_mode( blitline_buffer, line );
+ }
+ break;
+
+ case 4:
+ default:
+ if ( line >= 0 )
+ {
+ draw_scanline_mode4( blitline_buffer, priority_selected, line );
+ }
+ if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
+ {
+ draw_sprites_mode4( blitline_buffer, priority_selected, line );
+ }
+ break;
+ }
+ }
+
+ /* Check if display is disabled or we're below/above active area */
+ if (m_display_disabled || line < 0 || line >= m_frame_timing[ACTIVE_DISPLAY_V])
+ {
+ rectangle rec;
+ rec.min_y = rec.max_y = pixel_plot_y + line;
+
+ rec.min_x = pixel_offset_x;
+ rec.max_x = pixel_offset_x + 255;
+ m_tmpbitmap.fill(m_palette->pen(m_current_palette[BACKDROP_COLOR]), rec);
+ m_y1_bitmap.fill(( m_reg[0x07] & 0x0f ) ? 1 : 0, rec);
+ }
+ else
+ {
+ blit_scanline(blitline_buffer, priority_selected, pixel_offset_x, pixel_plot_y, line);
+ }
+}
+
+
+void sega315_5124_device::blit_scanline( int *line_buffer, int *priority_selected, int pixel_offset_x, int pixel_plot_y, int line )
+{
+ UINT32 *p_bitmap = &m_tmpbitmap.pix32(pixel_plot_y + line, pixel_offset_x);
+ UINT8 *p_y1 = &m_y1_bitmap.pix8(pixel_plot_y + line, pixel_offset_x);
+ int x = 0;
+
+ if (m_vdp_mode == 4 && (m_reg[0x00] & 0x20))
+ {
+ /* Fill column 0 with overscan color from m_reg[0x07] */
+ do
+ {
+ p_bitmap[x] = m_palette->pen(m_current_palette[BACKDROP_COLOR]);
+ p_y1[x] = ( m_reg[0x07] & 0x0f ) ? 1 : 0;
+ }
+ while(++x < 8);
+ }
+
+ do
+ {
+ p_bitmap[x] = m_palette->pen(line_buffer[x]);
+ p_y1[x] = ( priority_selected[x] & 0x0f ) ? 1 : 0;
+ }
+ while(++x < 256);
+}
+
+
+void sega315_5378_device::blit_scanline( int *line_buffer, int *priority_selected, int pixel_offset_x, int pixel_plot_y, int line )
+{
+ if (m_sega315_5124_compatibility_mode)
+ {
+ sega315_5124_device::blit_scanline(line_buffer, priority_selected, pixel_offset_x, pixel_plot_y, line);
+ }
+ else
+ {
+ UINT32 *p_bitmap = &m_tmpbitmap.pix32(pixel_plot_y + line, pixel_offset_x);
+ UINT8 *p_y1 = &m_y1_bitmap.pix8(pixel_plot_y + line, pixel_offset_x);
+ int x = 0;
+
+ /* border on left side of the GG active screen */
+ do
+ {
+ p_bitmap[x] = m_palette->pen(m_current_palette[BACKDROP_COLOR]);
+ p_y1[x] = ( m_reg[0x07] & 0x0f ) ? 1 : 0;
+ }
+ while (++x < 48);
+
+ if ( line >= 24 && line < 168 )
+ {
+ do
+ {
+ p_bitmap[x] = m_palette->pen(line_buffer[x]);
+ p_y1[x] = ( priority_selected[x] & 0x0f ) ? 1 : 0;
+ }
+ while (++x < 208);
+ }
+ else
+ {
+ /* top/bottom GG border */
+ do
+ {
+ p_bitmap[x] = m_palette->pen(m_current_palette[BACKDROP_COLOR]);
+ p_y1[x] = ( m_reg[0x07] & 0x0f ) ? 1 : 0;
+ }
+ while (++x < 208);
+ }
+
+ /* border on right side of the GG active screen */
+ do
+ {
+ p_bitmap[x] = m_palette->pen(m_current_palette[BACKDROP_COLOR]);
+ p_y1[x] = ( m_reg[0x07] & 0x0f ) ? 1 : 0;
+ }
+ while (++x < 256);
+ }
+}
+
+
+void sega315_5124_device::update_palette()
+{
+ int i;
+
+ /* Exit if palette has no changes */
+ if (m_cram_dirty == 0)
+ {
+ return;
+ }
+ m_cram_dirty = 0;
+
+ if (m_vdp_mode != 4)
+ {
+ for(i = 0; i < 16; i++)
+ {
+ m_current_palette[i] = 64 + i;
+ }
+ return;
+ }
+
+ for (i = 0; i < 32; i++)
+ {
+ m_current_palette[i] = m_CRAM[i] & 0x3f;
+ }
+}
+
+
+void sega315_5378_device::update_palette()
+{
+ int i;
+
+ /* Exit if palette has no changes */
+ if (m_cram_dirty == 0)
+ {
+ return;
+ }
+ m_cram_dirty = 0;
+
+ if (m_sega315_5124_compatibility_mode)
+ {
+ for (i = 0; i < 32; i++)
+ {
+ m_current_palette[i] = ((m_CRAM[i] & 0x30) << 6) | ((m_CRAM[i] & 0x0c ) << 4) | ((m_CRAM[i] & 0x03) << 2);
+ }
+ }
+ else
+ {
+ for (i = 0; i < 32; i++)
+ {
+ m_current_palette[i] = (m_CRAM[2*i] | (m_CRAM[2*i+1] << 8)) & 0x0fff;
+ }
+ }
+}
+
+
+void sega315_5124_device::cram_write(UINT8 data)
+{
+ UINT16 address = m_addr & m_cram_mask;
+ if (data != m_CRAM[address])
+ {
+ m_CRAM[address] = data;
+ m_cram_dirty = 1;
+ }
+}
+
+
+void sega315_5378_device::cram_write(UINT8 data)
+{
+ if (m_sega315_5124_compatibility_mode)
+ {
+ sega315_5124_device::cram_write(data);
+ }
+ else
+ {
+ if (m_addr & 1)
+ {
+ UINT16 address = (m_addr & m_cram_mask) & ~1;
+ if (m_buffer != m_CRAM[address] || data != m_CRAM[address + 1])
+ {
+ m_CRAM[address] = m_buffer;
+ m_CRAM[address + 1] = data;
+ m_cram_dirty = 1;
+ }
+ }
+ }
+}
+
+
+UINT32 sega315_5124_device::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
+{
+ copybitmap(bitmap, m_tmpbitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+
+// MegaDrive/Genesis VDP (315-5313) is currently coded as superset of the 315-5124
+// To support properly SMS VDP in MegaTech and MegaPlay, we start the 315-5124
+// in all systems using MegaDrive/Genesis VDP, but this affects the performance
+// of the emulator hence we stop it in systems that don't need it
+// Proper way to handle this would be implement the 315-5124 modes in the 315-5313
+// device instead of running the two chips separately...
+void sega315_5124_device::stop_timers()
+{
+ m_display_timer->adjust(attotime::never);
+ m_pending_flags_timer->adjust(attotime::never);
+ m_hint_timer->adjust(attotime::never);
+ m_vint_timer->adjust(attotime::never);
+ m_nmi_timer->adjust(attotime::never);
+ m_draw_timer->adjust(attotime::never);
+ m_lborder_timer->adjust(attotime::never);
+ m_rborder_timer->adjust(attotime::never);
+}
+
+
+/*****************************************************************************
+ DEVICE INTERFACE
+*****************************************************************************/
+
+void sega315_5124_device::vdp_postload()
+{
+ set_frame_timing();
+}
+
+void sega315_5124_device::device_start()
+{
+ /* Resolve callbacks */
+ m_int_cb.resolve();
+ m_pause_cb.resolve();
+
+ /* Allocate video RAM */
+ m_frame_timing = (m_is_pal) ? pal_192 : ntsc_192;
+
+ /* Make temp bitmap for rendering */
+ m_screen->register_screen_bitmap(m_tmpbitmap);
+ m_screen->register_screen_bitmap(m_y1_bitmap);
+
+ m_display_timer = timer_alloc(TIMER_LINE);
+ m_display_timer->adjust(m_screen->time_until_pos(0, DISPLAY_CB_HPOS), 0, m_screen->scan_period());
+ m_pending_flags_timer = timer_alloc(TIMER_FLAGS);
+ m_pending_flags_timer->adjust(m_screen->time_until_pos(0, SEGA315_5124_WIDTH - 1), 0, m_screen->scan_period());
+ m_draw_timer = timer_alloc(TIMER_DRAW);
+ m_lborder_timer = timer_alloc(TIMER_LBORDER);
+ m_rborder_timer = timer_alloc(TIMER_RBORDER);
+ m_hint_timer = timer_alloc(TIMER_HINT);
+ m_vint_timer = timer_alloc(TIMER_VINT);
+ m_nmi_timer = timer_alloc(TIMER_NMI);
+
+ save_item(NAME(m_status));
+ save_item(NAME(m_pending_status));
+ save_item(NAME(m_pending_sprcol_x));
+ save_item(NAME(m_reg8copy));
+ save_item(NAME(m_reg9copy));
+ save_item(NAME(m_addrmode));
+ save_item(NAME(m_addr));
+ save_item(NAME(m_cram_mask));
+ save_item(NAME(m_cram_dirty));
+ save_item(NAME(m_pending_reg_write));
+ save_item(NAME(m_buffer));
+ save_item(NAME(m_sega315_5124_compatibility_mode));
+ save_item(NAME(m_display_disabled));
+ save_item(NAME(m_irq_state));
+ save_item(NAME(m_vdp_mode));
+ save_item(NAME(m_y_pixels));
+ save_item(NAME(m_line_counter));
+ save_item(NAME(m_hcounter));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_current_palette));
+
+ // these were created with register_screen_bitmap which is dynamic, and will reallocate if the screen size changes, saving them is NOT safe with the current core.
+ // The Genesis VDP (315_5313.c) which uses this as a base in order to support the legacy SMS operaiton mode can change resolutions for example.
+ //save_item(NAME(m_tmpbitmap));
+ //save_item(NAME(m_y1_bitmap));
+ save_item(NAME(m_draw_time));
+ save_item(NAME(m_sprite_base));
+ save_item(NAME(m_sprite_pattern_line));
+ save_item(NAME(m_sprite_tile_selected));
+ save_item(NAME(m_sprite_x));
+ save_item(NAME(m_sprite_flags));
+ save_item(NAME(m_sprite_count));
+ save_item(NAME(m_sprite_height));
+ save_item(NAME(m_sprite_zoom));
+ save_item(NAME(m_CRAM));
+
+ machine().save().register_postload(save_prepost_delegate(FUNC(sega315_5124_device::vdp_postload), this));
+}
+
+
+void sega315_5124_device::device_reset()
+{
+ int i;
+
+ /* Most register are 0x00 at power-up */
+ for (i = 0; i < 16; i++)
+ m_reg[i] = 0x00;
+
+ m_reg[0x02] = 0x0e;
+ m_reg[0x0a] = 0xff;
+
+ m_status = 0;
+ m_pending_status = 0;
+ m_pending_sprcol_x = 0;
+ m_pending_reg_write = 0;
+ m_reg8copy = 0;
+ m_reg9copy = 0;
+ m_addrmode = 0;
+ m_addr = 0;
+ m_sega315_5124_compatibility_mode = false;
+ m_display_disabled = false;
+ m_cram_mask = m_cram_size - 1;
+ m_cram_dirty = 1;
+ m_buffer = 0;
+ m_irq_state = 0;
+ m_line_counter = 0;
+ m_hcounter = 0;
+ m_draw_time = DRAW_TIME_SMS;
+
+ for (i = 0; i < 0x20; i++)
+ m_current_palette[i] = 0;
+
+ set_display_settings();
+
+ /* Clear RAM */
+ memset(m_CRAM, 0, sizeof(m_CRAM));
+}
+
+static MACHINE_CONFIG_FRAGMENT( sega315_5124 )
+ MCFG_PALETTE_ADD("palette", SEGA315_5124_PALETTE_SIZE)
+ MCFG_PALETTE_INIT_OWNER(sega315_5124_device, sega315_5124)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor sega315_5124_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( sega315_5124 );
+}
+
+
+void sega315_5378_device::device_reset()
+{
+ sega315_5124_device::device_reset();
+ m_draw_time = DRAW_TIME_GG;
+}
+
+static MACHINE_CONFIG_FRAGMENT( sega315_5378 )
+ MCFG_PALETTE_ADD("palette", SEGA315_5378_PALETTE_SIZE)
+ MCFG_PALETTE_INIT_OWNER(sega315_5378_device, sega315_5378)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor sega315_5378_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( sega315_5378 );
+}
diff --git a/src/devices/video/315_5124.h b/src/devices/video/315_5124.h
new file mode 100644
index 00000000000..409dc2e08a3
--- /dev/null
+++ b/src/devices/video/315_5124.h
@@ -0,0 +1,255 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol, Enik Land
+/*************************************************************************
+
+ sega315_5124.h
+
+ Implementation of Sega VDP chips used in System E, Master System and Game Gear
+
+**************************************************************************/
+
+#ifndef __SEGA315_5124_H__
+#define __SEGA315_5124_H__
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define SEGA315_5124_WIDTH 342 /* 342 pixels */
+#define SEGA315_5124_HEIGHT_NTSC 262 /* 262 lines */
+#define SEGA315_5124_HEIGHT_PAL 313 /* 313 lines */
+#define SEGA315_5124_LBORDER_START (9 + 2 + 14 + 8)
+#define SEGA315_5124_LBORDER_WIDTH 13 /* 13 pixels */
+#define SEGA315_5124_RBORDER_WIDTH 15 /* 15 pixels */
+#define SEGA315_5124_TBORDER_START (3 + 13)
+#define SEGA315_5124_NTSC_192_TBORDER_HEIGHT (0x1b) /* 27 lines */
+//#define SEGA315_5124_NTSC_192_BBORDER_HEIGHT (0x18) /* 24 lines */
+#define SEGA315_5124_NTSC_224_TBORDER_HEIGHT (0x0b) /* 11 lines */
+//#define SEGA315_5124_NTSC_224_BBORDER_HEIGHT (0x08) /* 8 lines */
+//#define SEGA315_5124_PAL_192_TBORDER_HEIGHT (0x36) /* 54 lines */
+//#define SEGA315_5124_PAL_192_BBORDER_HEIGHT (0x30) /* 48 lines */
+//#define SEGA315_5124_PAL_224_TBORDER_HEIGHT (0x26) /* 38 lines */
+//#define SEGA315_5124_PAL_224_BBORDER_HEIGHT (0x20) /* 32 lines */
+#define SEGA315_5124_PAL_240_TBORDER_HEIGHT (0x1e) /* 30 lines */
+//#define SEGA315_5124_PAL_240_BBORDER_HEIGHT (0x18) /* 24 lines */
+
+
+#define SEGA315_5124_PALETTE_SIZE (64+16)
+#define SEGA315_5378_PALETTE_SIZE 4096
+
+
+#define SEGA315_5378_CRAM_SIZE 0x40 /* 32 colors x 2 bytes per color = 64 bytes */
+#define SEGA315_5124_CRAM_SIZE 0x20 /* 32 colors x 1 bytes per color = 32 bytes */
+
+#define VRAM_SIZE 0x4000
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+extern const device_type SEGA315_5124; /* aka SMS1 vdp */
+extern const device_type SEGA315_5246; /* aka SMS2 vdp */
+extern const device_type SEGA315_5378; /* aka Gamegear vdp */
+
+
+class sega315_5124_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ sega315_5124_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ sega315_5124_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT8 cram_size, UINT8 palette_offset, bool supports_224_240, const char *shortname, const char *source);
+
+ static void set_signal_type(device_t &device, bool is_pal) { downcast<sega315_5124_device &>(device).m_is_pal = is_pal; }
+
+
+
+ template<class _Object> static devcb_base &set_int_callback(device_t &device, _Object object) { return downcast<sega315_5124_device &>(device).m_int_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_pause_callback(device_t &device, _Object object) { return downcast<sega315_5124_device &>(device).m_pause_cb.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( vram_read );
+ DECLARE_WRITE8_MEMBER( vram_write );
+ DECLARE_READ8_MEMBER( register_read );
+ DECLARE_WRITE8_MEMBER( register_write );
+ DECLARE_READ8_MEMBER( vcount_read );
+ DECLARE_READ8_MEMBER( hcount_read );
+
+ DECLARE_PALETTE_INIT( sega315_5124 );
+
+ void hcount_latch() { hcount_latch_at_hpos( m_screen->hpos() ); };
+ void hcount_latch_at_hpos( int hpos );
+ void stop_timers();
+
+ bitmap_rgb32 &get_bitmap() { return m_tmpbitmap; };
+ bitmap_ind8 &get_y1_bitmap() { return m_y1_bitmap; };
+
+ /* update the screen */
+ UINT32 screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect );
+
+ virtual void set_sega315_5124_compatibility_mode( bool sega315_5124_compatibility_mode ) { };
+
+protected:
+ void set_display_settings();
+ void set_frame_timing();
+ virtual void update_palette();
+ virtual void cram_write(UINT8 data);
+ virtual void draw_scanline( int pixel_offset_x, int pixel_plot_y, int line );
+ virtual void blit_scanline( int *line_buffer, int *priority_selected, int pixel_offset_x, int pixel_plot_y, int line );
+ virtual UINT16 get_name_table_row(int row);
+ void process_line_timer();
+ void select_sprites( int line );
+ void draw_scanline_mode4( int *line_buffer, int *priority_selected, int line );
+ void draw_sprites_mode4( int *line_buffer, int *priority_selected, int line );
+ void draw_sprites_tms9918_mode( int *line_buffer, int line );
+ void draw_scanline_mode2( int *line_buffer, int line );
+ void draw_scanline_mode0( int *line_buffer, int line );
+ void check_pending_flags();
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const { return (spacenum == AS_0) ? &m_space_config : NULL; }
+
+ void vdp_postload();
+
+ UINT8 m_reg[16]; /* All the registers */
+ UINT8 m_status; /* Status register */
+ UINT8 m_pending_status; /* Pending status flags */
+ UINT8 m_reg8copy; /* Internal copy of register 8 (X-Scroll) */
+ UINT8 m_reg9copy; /* Internal copy of register 9 (Y-Scroll) */
+ UINT8 m_addrmode; /* Type of VDP action */
+ UINT16 m_addr; /* Contents of internal VDP address register */
+ UINT8 m_cram_size; /* CRAM size */
+ UINT8 m_cram_mask; /* Mask to switch between SMS and GG CRAM sizes */
+ int m_cram_dirty; /* Have there been any changes to the CRAM area */
+ int m_pending_reg_write;
+ int m_pending_sprcol_x;
+ UINT8 m_buffer;
+ bool m_sega315_5124_compatibility_mode; /* Shrunk SMS screen on GG lcd mode flag */
+ int m_irq_state; /* The status of the IRQ line of the VDP */
+ int m_vdp_mode; /* Current mode of the VDP: 0,1,2,3,4 */
+ int m_y_pixels; /* 192, 224, 240 */
+ int m_draw_time;
+ UINT8 m_line_counter;
+ UINT8 m_hcounter;
+ UINT8 m_CRAM[SEGA315_5378_CRAM_SIZE]; /* CRAM */
+ const UINT8 *m_frame_timing;
+ bitmap_rgb32 m_tmpbitmap;
+ bitmap_ind8 m_y1_bitmap;
+ UINT8 m_palette_offset;
+ bool m_supports_224_240;
+ bool m_display_disabled;
+ UINT16 m_sprite_base;
+ UINT16 m_sprite_pattern_line[8];
+ int m_sprite_tile_selected[8];
+ int m_sprite_x[8];
+ UINT8 m_sprite_flags[8];
+ int m_sprite_count;
+ int m_sprite_height;
+ int m_sprite_zoom;
+ int m_current_palette[32];
+ bool m_is_pal; /* false = NTSC, true = PAL */
+ devcb_write_line m_int_cb; /* Interrupt callback function */
+ devcb_write_line m_pause_cb; /* Pause callback function */
+ emu_timer *m_display_timer;
+ emu_timer *m_hint_timer;
+ emu_timer *m_vint_timer;
+ emu_timer *m_nmi_timer;
+ emu_timer *m_draw_timer;
+ emu_timer *m_lborder_timer;
+ emu_timer *m_rborder_timer;
+ emu_timer *m_pending_flags_timer;
+
+ const address_space_config m_space_config;
+
+ /* Timers */
+ static const device_timer_id TIMER_LINE = 0;
+ static const device_timer_id TIMER_DRAW = 1;
+ static const device_timer_id TIMER_LBORDER = 2;
+ static const device_timer_id TIMER_RBORDER = 3;
+ static const device_timer_id TIMER_HINT = 4;
+ static const device_timer_id TIMER_VINT = 5;
+ static const device_timer_id TIMER_NMI = 6;
+ static const device_timer_id TIMER_FLAGS = 7;
+
+ required_device<palette_device> m_palette;
+};
+
+
+class sega315_5246_device : public sega315_5124_device
+{
+public:
+ sega315_5246_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual UINT16 get_name_table_row(int row);
+};
+
+
+class sega315_5378_device : public sega315_5124_device
+{
+public:
+ sega315_5378_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_PALETTE_INIT( sega315_5378 );
+
+ virtual void set_sega315_5124_compatibility_mode( bool sega315_5124_compatibility_mode );
+
+protected:
+ virtual void device_reset();
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ virtual void update_palette();
+ virtual void cram_write(UINT8 data);
+ virtual void blit_scanline( int *line_buffer, int *priority_selected, int pixel_offset_x, int pixel_plot_y, int line );
+ virtual UINT16 get_name_table_row(int row);
+};
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_SEGA315_5124_SET_SCREEN MCFG_VIDEO_SET_SCREEN
+
+#define MCFG_SEGA315_5124_IS_PAL(_bool) \
+ sega315_5124_device::set_signal_type(*device, _bool);
+
+#define MCFG_SEGA315_5124_INT_CB(_devcb) \
+ devcb = &sega315_5124_device::set_int_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SEGA315_5124_PAUSE_CB(_devcb) \
+ devcb = &sega315_5124_device::set_pause_callback(*device, DEVCB_##_devcb);
+
+
+#define MCFG_SEGA315_5246_SET_SCREEN MCFG_VIDEO_SET_SCREEN
+
+#define MCFG_SEGA315_5246_IS_PAL(_bool) \
+ sega315_5246_device::set_signal_type(*device, _bool);
+
+#define MCFG_SEGA315_5246_INT_CB(_devcb) \
+ devcb = &sega315_5246_device::set_int_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SEGA315_5246_PAUSE_CB(_devcb) \
+ devcb = &sega315_5246_device::set_pause_callback(*device, DEVCB_##_devcb);
+
+
+#define MCFG_SEGA315_5378_SET_SCREEN MCFG_VIDEO_SET_SCREEN
+
+#define MCFG_SEGA315_5378_IS_PAL(_bool) \
+ sega315_5378_device::set_signal_type(*device, _bool);
+
+#define MCFG_SEGA315_5378_INT_CB(_devcb) \
+ devcb = &sega315_5378_device::set_int_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SEGA315_5378_PAUSE_CB(_devcb) \
+ devcb = &sega315_5378_device::set_pause_callback(*device, DEVCB_##_devcb);
+
+
+#endif /* __SEGA315_5124_H__ */
diff --git a/src/devices/video/315_5313.c b/src/devices/video/315_5313.c
new file mode 100644
index 00000000000..a612c384e99
--- /dev/null
+++ b/src/devices/video/315_5313.c
@@ -0,0 +1,2776 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+/* Sega 315-5313 - Megadrive VDP */
+
+#include "emu.h"
+#include "video/315_5313.h"
+
+/* still have dependencies on the following external gunk */
+
+#include "sound/sn76496.h"
+
+#define MAX_HPOSITION 480
+
+
+const device_type SEGA315_5313 = &device_creator<sega315_5313_device>;
+
+sega315_5313_device::sega315_5313_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sega315_5124_device(mconfig, SEGA315_5313, "Sega 315-5313 Megadrive VDP", tag, owner, clock, SEGA315_5124_CRAM_SIZE, 0, true, "sega315_5313", __FILE__),
+ m_sndirqline_callback(*this),
+ m_lv6irqline_callback(*this),
+ m_lv4irqline_callback(*this),
+ m_dma_delay(0)
+{
+ m_use_alt_timing = 0;
+ m_palwrite_base = -1;
+}
+
+//-------------------------------------------------
+// static_set_palette_tag: Set the tag of the
+// palette device
+//-------------------------------------------------
+
+void sega315_5313_device::static_set_palette_tag(device_t &device, const char *tag)
+{
+ downcast<sega315_5313_device &>(device).m_palette.set_tag(tag);
+}
+
+
+static MACHINE_CONFIG_FRAGMENT( sega_genesis_vdp )
+ MCFG_PALETTE_ADD("palette", 0x200)
+ MCFG_PALETTE_INIT_OWNER(sega315_5124_device, sega315_5124)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor sega315_5313_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( sega_genesis_vdp );
+}
+
+static TIMER_CALLBACK( render_timer_callback )
+{
+ sega315_5313_device* vdp = (sega315_5313_device*)ptr;
+ vdp->render_scanline();
+}
+
+void sega315_5313_device::vdp_handle_irq6_on_timer_callback(int param)
+{
+// m_irq6_pending = 1;
+ if (MEGADRIVE_REG01_IRQ6_ENABLE)
+ m_lv6irqline_callback(true);
+}
+
+static TIMER_CALLBACK( irq6_on_timer_callback )
+{
+ sega315_5313_device* vdp = (sega315_5313_device*)ptr;
+ vdp->vdp_handle_irq6_on_timer_callback(param);
+}
+
+void sega315_5313_device::vdp_handle_irq4_on_timer_callback(int param)
+{
+ m_lv4irqline_callback(true);
+}
+
+static TIMER_CALLBACK( irq4_on_timer_callback )
+{
+ sega315_5313_device* vdp = (sega315_5313_device*)ptr;
+ vdp->vdp_handle_irq4_on_timer_callback(param);
+}
+
+
+
+void sega315_5313_device::set_alt_timing(device_t &device, int use_alt_timing)
+{
+ sega315_5313_device &dev = downcast<sega315_5313_device &>(device);
+ dev.m_use_alt_timing = use_alt_timing;
+}
+
+void sega315_5313_device::set_palwrite_base(device_t &device, int palwrite_base)
+{
+ sega315_5313_device &dev = downcast<sega315_5313_device &>(device);
+ dev.m_palwrite_base = palwrite_base;
+}
+
+
+
+
+void sega315_5313_device::device_start()
+{
+ m_sndirqline_callback.resolve_safe();
+ m_lv6irqline_callback.resolve_safe();
+ m_lv4irqline_callback.resolve_safe();
+
+ m_32x_scanline_func.bind_relative_to(*owner());
+ m_32x_interrupt_func.bind_relative_to(*owner());
+ m_32x_scanline_helper_func.bind_relative_to(*owner());
+
+ m_vram = auto_alloc_array(machine(), UINT16, 0x10000/2);
+ m_cram = auto_alloc_array(machine(), UINT16, 0x80/2);
+ m_vsram = auto_alloc_array(machine(), UINT16, 0x80/2);
+ m_regs = auto_alloc_array(machine(), UINT16, 0x40/2);
+ m_internal_sprite_attribute_table = auto_alloc_array(machine(), UINT16, 0x400/2);
+
+ memset(m_vram, 0x00, 0x10000);
+ memset(m_cram, 0x00, 0x80);
+ memset(m_vsram, 0x00, 0x80);
+ memset(m_regs, 0x00, 0x40);
+ memset(m_internal_sprite_attribute_table, 0x00, 0x400);
+
+
+ save_pointer(NAME(m_vram), 0x10000/2);
+ save_pointer(NAME(m_cram), 0x80/2);
+ save_pointer(NAME(m_vsram), 0x80/2);
+ save_pointer(NAME(m_regs), 0x40/2);
+ save_pointer(NAME(m_internal_sprite_attribute_table), 0x400/2);
+
+ save_item(NAME(m_command_pending));
+ save_item(NAME(m_command_part1));
+ save_item(NAME(m_command_part2));
+ save_item(NAME(m_vdp_code));
+ save_item(NAME(m_vdp_address));
+ save_item(NAME(m_vram_fill_pending));
+ save_item(NAME(m_vram_fill_length));
+ save_item(NAME(m_irq4counter));
+ save_item(NAME(m_imode_odd_frame));
+ save_item(NAME(m_sprite_collision));
+ save_item(NAME(m_imode));
+ save_item(NAME(m_irq6_pending));
+ save_item(NAME(m_irq4_pending));
+ save_item(NAME(m_visible_scanlines));
+ save_item(NAME(m_irq6_scanline));
+ save_item(NAME(m_z80irq_scanline));
+ save_item(NAME(m_scanline_counter));
+ save_item(NAME(m_vblank_flag));
+ save_item(NAME(m_total_scanlines));
+
+ m_sprite_renderline = auto_alloc_array(machine(), UINT8, 1024);
+ m_highpri_renderline = auto_alloc_array(machine(), UINT8, 320);
+ m_video_renderline = auto_alloc_array(machine(), UINT32, 320);
+
+ m_palette_lookup = auto_alloc_array(machine(), UINT16, 0x40);
+ m_palette_lookup_sprite = auto_alloc_array(machine(), UINT16, 0x40);
+
+ m_palette_lookup_shadow = auto_alloc_array(machine(), UINT16, 0x40);
+ m_palette_lookup_highlight = auto_alloc_array(machine(), UINT16, 0x40);
+
+ memset(m_palette_lookup,0x00,0x40*2);
+ memset(m_palette_lookup_sprite,0x00,0x40*2);
+
+ memset(m_palette_lookup_shadow,0x00,0x40*2);
+ memset(m_palette_lookup_highlight,0x00,0x40*2);
+
+
+ if (!m_use_alt_timing)
+ m_render_bitmap = auto_bitmap_ind16_alloc(machine(), 320, 512); // allocate maximum sizes we're going to use, it's safer.
+ else
+ m_render_line = auto_alloc_array(machine(), UINT16, 320);
+
+ m_render_line_raw = auto_alloc_array(machine(), UINT16, 320);
+
+ // FIXME: are these all needed? I'm pretty sure some of these (most?) are just helpers which don't need to be saved,
+ // but better safe than sorry...
+ save_pointer(NAME(m_sprite_renderline), 1024);
+ save_pointer(NAME(m_highpri_renderline), 320);
+ save_pointer(NAME(m_video_renderline), 320/4);
+ save_pointer(NAME(m_palette_lookup), 0x40);
+ save_pointer(NAME(m_palette_lookup_sprite), 0x40);
+ save_pointer(NAME(m_palette_lookup_shadow), 0x40);
+ save_pointer(NAME(m_palette_lookup_highlight), 0x40);
+ save_pointer(NAME(m_render_line_raw), 320/2);
+ if (m_use_alt_timing)
+ save_pointer(NAME(m_render_line), 320/2);
+
+ m_irq6_on_timer = machine().scheduler().timer_alloc(FUNC(irq6_on_timer_callback), (void*)this);
+ m_irq4_on_timer = machine().scheduler().timer_alloc(FUNC(irq4_on_timer_callback), (void*)this);
+ m_render_timer = machine().scheduler().timer_alloc(FUNC(render_timer_callback), (void*)this);
+
+ m_space68k = &machine().device<m68000_base_device>(":maincpu")->space();
+ m_cpu68k = machine().device<m68000_base_device>(":maincpu");
+
+ sega315_5124_device::device_start();
+}
+
+void sega315_5313_device::device_reset()
+{
+ m_command_pending = 0;
+ m_command_part1 = 0;
+ m_command_part2 = 0;
+ m_vdp_code = 0;
+ m_vdp_address = 0;
+ m_vram_fill_pending = 0;
+ m_vram_fill_length = 0;
+ m_irq4counter = -1;
+ m_imode_odd_frame = 0;
+ m_sprite_collision = 0;
+ m_imode = 0;
+ m_irq6_pending = 0;
+ m_irq4_pending = 0;
+ m_scanline_counter = 0;
+ m_vblank_flag = 0;
+ m_total_scanlines = 262;
+
+ sega315_5124_device::device_reset();
+}
+
+void sega315_5313_device::device_reset_old()
+{
+ // other stuff, are we sure we want to set some of these every reset?
+ // it's called from machine_reset
+ m_total_scanlines = 262;
+ m_visible_scanlines = 224;
+ m_irq6_scanline = 224;
+ m_z80irq_scanline = 226;
+}
+
+
+
+void sega315_5313_device::vdp_vram_write(UINT16 data)
+{
+ UINT16 sprite_base_address = MEGADRIVE_REG0C_RS1?((MEGADRIVE_REG05_SPRITE_ADDR&0x7e)<<9):((MEGADRIVE_REG05_SPRITE_ADDR&0x7f)<<9);
+ int spritetable_size = MEGADRIVE_REG0C_RS1?0x400:0x200;
+ int lowlimit = sprite_base_address;
+ int highlimit = sprite_base_address+spritetable_size;
+
+ if (m_vdp_address&1)
+ {
+ data = ((data&0x00ff)<<8)|((data&0xff00)>>8);
+ }
+
+ MEGADRIV_VDP_VRAM(m_vdp_address>>1) = data;
+
+ /* The VDP stores an Internal copy of any data written to the Sprite Attribute Table.
+ This data is _NOT_ invalidated when the Sprite Base Address changes, thus allowing
+ for some funky effects, as used by Castlevania Bloodlines Stage 6-3 */
+ if (m_vdp_address>=lowlimit && m_vdp_address<highlimit)
+ {
+// osd_printf_debug("spritebase is %04x-%04x vram address is %04x, write %04x\n",lowlimit, highlimit-1, m_vdp_address, data);
+ m_internal_sprite_attribute_table[(m_vdp_address&(spritetable_size-1))>>1] = data;
+ }
+
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+ m_vdp_address &= 0xffff;
+}
+
+void sega315_5313_device::vdp_vsram_write(UINT16 data)
+{
+ m_vsram[(m_vdp_address&0x7e)>>1] = data;
+
+ //logerror("Wrote to VSRAM addr %04x data %04x\n",m_vdp_address&0xfffe,m_vsram[m_vdp_address>>1]);
+
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+
+ m_vdp_address &=0xffff;
+}
+
+void sega315_5313_device::write_cram_value(int offset, int data)
+{
+ m_cram[offset] = data;
+
+ //logerror("Wrote to CRAM addr %04x data %04x\n",m_vdp_address&0xfffe,m_cram[m_vdp_address>>1]);
+ if (m_use_cram)
+ {
+ int r,g,b;
+ r = ((data >> 1)&0x07);
+ g = ((data >> 5)&0x07);
+ b = ((data >> 9)&0x07);
+ if (m_palwrite_base != -1)
+ {
+ m_palette->set_pen_color(offset + m_palwrite_base ,pal3bit(r),pal3bit(g),pal3bit(b));
+ m_palette->set_pen_color(offset + m_palwrite_base + 0x40 ,pal3bit(r>>1),pal3bit(g>>1),pal3bit(b>>1));
+ m_palette->set_pen_color(offset + m_palwrite_base + 0x80 ,pal3bit((r>>1)|0x4),pal3bit((g>>1)|0x4),pal3bit((b>>1)|0x4));
+ }
+ m_palette_lookup[offset] = (b<<2) | (g<<7) | (r<<12);
+ m_palette_lookup_sprite[offset] = (b<<2) | (g<<7) | (r<<12);
+ m_palette_lookup_shadow[offset] = (b<<1) | (g<<6) | (r<<11);
+ m_palette_lookup_highlight[offset] = ((b|0x08)<<1) | ((g|0x08)<<6) | ((r|0x08)<<11);
+ }
+}
+
+void sega315_5313_device::vdp_cram_write(UINT16 data)
+{
+ int offset;
+ offset = (m_vdp_address&0x7e)>>1;
+
+ write_cram_value(offset,data);
+
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+
+ m_vdp_address &=0xffff;
+}
+
+
+void sega315_5313_device::data_port_w(int data)
+{
+ m_command_pending = 0;
+
+ /*
+ 0000b : VRAM read
+ 0001b : VRAM write
+ 0011b : CRAM write
+ 0100b : VSRAM read
+ 0101b : VSRAM write
+ 1000b : CRAM read
+ */
+// logerror("write to vdp data port %04x with code %04x, write address %04x\n",data, m_vdp_code, m_vdp_address );
+
+ if (m_vram_fill_pending)
+ {
+ int count;
+
+ m_vdp_address&=0xffff;
+
+ if (m_vdp_address&1)
+ {
+ MEGADRIV_VDP_VRAM((m_vdp_address>>1)) = (MEGADRIV_VDP_VRAM((m_vdp_address>>1))&0xff00) | (data&0x00ff);
+ }
+ else
+ {
+ MEGADRIV_VDP_VRAM((m_vdp_address>>1)) = (MEGADRIV_VDP_VRAM((m_vdp_address>>1))&0x00ff) | ((data&0x00ff)<<8);
+ }
+
+
+ for (count=0;count<=m_vram_fill_length;count++) // <= for james pond 3
+ {
+ if (m_vdp_address&1)
+ {
+ MEGADRIV_VDP_VRAM((m_vdp_address>>1)) = (MEGADRIV_VDP_VRAM((m_vdp_address>>1))&0x00ff) | (data&0xff00);
+ }
+ else
+ {
+ MEGADRIV_VDP_VRAM((m_vdp_address>>1)) = (MEGADRIV_VDP_VRAM((m_vdp_address>>1))&0xff00) | ((data&0xff00)>>8);
+ }
+
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+ m_vdp_address&=0xffff;
+
+ }
+
+ m_regs[0x13] = 0;
+ m_regs[0x14] = 0;
+
+ // m_regs[0x15] = (source>>1) & 0xff;
+ // m_regs[0x16] = (source>>9) & 0xff;
+ // m_regs[0x17] = (source>>17) & 0xff;
+
+
+ }
+ else
+ {
+ switch (m_vdp_code & 0x000f)
+ {
+ case 0x0000:
+ logerror("Attempting to WRITE to DATA PORT in VRAM READ MODE\n");
+ break;
+
+ case 0x0001:
+ vdp_vram_write(data);
+ break;
+
+ case 0x0003:
+ vdp_cram_write(data);
+ break;
+
+ case 0x0004:
+ logerror("Attempting to WRITE to DATA PORT in VSRAM READ MODE\n");
+ break;
+
+ case 0x0005:
+ vdp_vsram_write(data);
+ break;
+
+ case 0x0008:
+ logerror("Attempting to WRITE to DATA PORT in CRAM READ MODE\n");
+ break;
+
+ default:
+ logerror("Attempting to WRITE to DATA PORT in #UNDEFINED# MODE %1x %04x\n",m_vdp_code&0xf, data);
+ break;
+ }
+ }
+
+
+
+}
+
+
+
+void sega315_5313_device::vdp_set_register(int regnum, UINT8 value)
+{
+ m_regs[regnum] = value;
+
+ /* We need special handling for the IRQ enable registers, some games turn
+ off the irqs before they are taken, delaying them until the IRQ is turned
+ back on */
+
+ if (regnum == 0x00)
+ {
+ //osd_printf_debug("setting reg 0, irq enable is now %d\n",MEGADRIVE_REG0_IRQ4_ENABLE);
+
+ if (m_irq4_pending)
+ {
+ if (MEGADRIVE_REG0_IRQ4_ENABLE)
+ m_lv4irqline_callback(true);
+ else
+ m_lv4irqline_callback(false);
+ }
+
+ /* ??? Fatal Rewind needs this but I'm not sure it's accurate behavior
+ it causes flickering in roadrash */
+ // m_irq6_pending = 0;
+ // m_irq4_pending = 0;
+
+ }
+
+ if (regnum == 0x01)
+ {
+ if (m_irq6_pending)
+ {
+ if (MEGADRIVE_REG01_IRQ6_ENABLE )
+ m_lv6irqline_callback(true);
+ else
+ m_lv6irqline_callback(false);
+
+ }
+
+ /* ??? */
+ // m_irq6_pending = 0;
+ // m_irq4_pending = 0;
+
+ }
+
+
+// if (regnum == 0x0a)
+// osd_printf_debug("Set HINT Reload Register to %d on scanline %d\n",value, get_scanline_counter());
+
+// osd_printf_debug("%s: Setting VDP Register #%02x to %02x\n",machine().describe_context(), regnum,value);
+}
+
+void sega315_5313_device::update_code_and_address(void)
+{
+ m_vdp_code = ((m_command_part1 & 0xc000) >> 14) |
+ ((m_command_part2 & 0x00f0) >> 2);
+
+ m_vdp_address = ((m_command_part1 & 0x3fff) >> 0) |
+ ((m_command_part2 & 0x0003) << 14);
+}
+
+// if either SVP CPU or segaCD is present, there is a 'lag' we have to compensate for
+// hence, for segacd and svp we set m_dma_delay to the appropriate value at start
+inline UINT16 sega315_5313_device::vdp_get_word_from_68k_mem(UINT32 source)
+{
+ // should we limit the valid areas here?
+ // how does this behave with the segacd etc?
+ // note, the RV bit on 32x is important for this to work, because it causes a normal cart mapping - see tempo
+
+ //printf("vdp_get_word_from_68k_mem_default %08x\n", source);
+
+ if (source <= 0x3fffff)
+ return m_space68k->read_word(source - m_dma_delay); // compensate DMA lag
+ else if ((source >= 0xe00000) && (source <= 0xffffff))
+ return m_space68k->read_word(source);
+ else
+ {
+ printf("DMA Read unmapped %06x\n",source);
+ return machine().rand();
+ }
+}
+
+/* Table from Charles Macdonald
+
+
+ DMA Mode Width Display Transfer Count
+ -----------------------------------------------------
+ 68K > VDP 32-cell Active 16
+ Blanking 167
+ 40-cell Active 18
+ Blanking 205
+ VRAM Fill 32-cell Active 15
+ Blanking 166
+ 40-cell Active 17
+ Blanking 204
+ VRAM Copy 32-cell Active 8
+ Blanking 83
+ 40-cell Active 9
+ Blanking 102
+
+*/
+
+
+/* Note, In reality this transfer is NOT instant, the 68k isn't paused
+ as the 68k address bus isn't accessed */
+
+/* Wani Wani World, James Pond 3, Pirates Gold! */
+void sega315_5313_device::insta_vram_copy(UINT32 source, UINT16 length)
+{
+ int x;
+
+ for (x=0;x<length;x++)
+ {
+ UINT8 source_byte;
+
+ //osd_printf_debug("vram copy length %04x source %04x dest %04x\n",length, source, m_vdp_address );
+ if (source&1) source_byte = MEGADRIV_VDP_VRAM((source&0xffff)>>1)&0x00ff;
+ else source_byte = (MEGADRIV_VDP_VRAM((source&0xffff)>>1)&0xff00)>>8;
+
+ if (m_vdp_address&1)
+ {
+ MEGADRIV_VDP_VRAM((m_vdp_address&0xffff)>>1) = (MEGADRIV_VDP_VRAM((m_vdp_address&0xffff)>>1)&0xff00) | source_byte;
+ }
+ else
+ {
+ MEGADRIV_VDP_VRAM((m_vdp_address&0xffff)>>1) = (MEGADRIV_VDP_VRAM((m_vdp_address&0xffff)>>1)&0x00ff) | (source_byte<<8);
+ }
+
+ source++;
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+ m_vdp_address&=0xffff;
+ }
+}
+
+/* Instant, but we pause the 68k a bit */
+void sega315_5313_device::insta_68k_to_vram_dma(UINT32 source,int length)
+{
+ int count;
+
+ if (length==0x00) length = 0xffff;
+
+ /* This is a hack until real DMA timings are implemented */
+ m_cpu68k->spin_until_time(attotime::from_nsec(length * 1000 / 3500));
+
+ for (count = 0;count<(length>>1);count++)
+ {
+ vdp_vram_write(vdp_get_word_from_68k_mem(source));
+ source+=2;
+ if (source>0xffffff) source = 0xe00000;
+ }
+
+ m_vdp_address&=0xffff;
+
+ m_regs[0x13] = 0;
+ m_regs[0x14] = 0;
+
+ m_regs[0x15] = (source>>1) & 0xff;
+ m_regs[0x16] = (source>>9) & 0xff;
+ m_regs[0x17] = (source>>17) & 0xff;
+}
+
+
+void sega315_5313_device::insta_68k_to_cram_dma(UINT32 source,UINT16 length)
+{
+ int count;
+
+ if (length==0x00) length = 0xffff;
+
+ for (count = 0;count<(length>>1);count++)
+ {
+ //if (m_vdp_address>=0x80) return; // abandon
+
+ write_cram_value((m_vdp_address&0x7e)>>1, vdp_get_word_from_68k_mem(source));
+ source+=2;
+
+ if (source>0xffffff) source = 0xfe0000;
+
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+ m_vdp_address&=0xffff;
+ }
+
+ m_regs[0x13] = 0;
+ m_regs[0x14] = 0;
+
+ m_regs[0x15] = (source>>1) & 0xff;
+ m_regs[0x16] = (source>>9) & 0xff;
+ m_regs[0x17] = (source>>17) & 0xff;
+
+}
+
+void sega315_5313_device::insta_68k_to_vsram_dma(UINT32 source,UINT16 length)
+{
+ int count;
+
+ if (length==0x00) length = 0xffff;
+
+ for (count = 0;count<(length>>1);count++)
+ {
+ if (m_vdp_address>=0x80) return; // abandon
+
+ m_vsram[(m_vdp_address&0x7e)>>1] = vdp_get_word_from_68k_mem(source);
+ source+=2;
+
+ if (source>0xffffff) source = 0xfe0000;
+
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+ m_vdp_address&=0xffff;
+ }
+
+ m_regs[0x13] = 0;
+ m_regs[0x14] = 0;
+
+ m_regs[0x15] = (source>>1) & 0xff;
+ m_regs[0x16] = (source>>9) & 0xff;
+ m_regs[0x17] = (source>>17) & 0xff;
+}
+
+/* This can be simplified quite a lot.. */
+void sega315_5313_device::handle_dma_bits()
+{
+#if 0
+ if (m_vdp_code&0x20)
+ {
+ UINT32 source;
+ UINT16 length;
+ source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8) | ((MEGADRIVE_REG17_DMASOURCE3&0xff)<<16))<<1;
+ length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8))<<1;
+ osd_printf_debug("%s 68k DMAtran set source %06x length %04x dest %04x enabled %01x code %02x %02x\n", machine().describe_context(), source, length, m_vdp_address,MEGADRIVE_REG01_DMA_ENABLE, m_vdp_code,MEGADRIVE_REG0F_AUTO_INC);
+ }
+#endif
+ if (m_vdp_code==0x20)
+ {
+ osd_printf_debug("DMA bit set 0x20 but invalid??\n");
+ }
+ else if (m_vdp_code==0x21 || m_vdp_code==0x31) /* 0x31 used by tecmo cup */
+ {
+ if (MEGADRIVE_REG17_DMATYPE==0x0 || MEGADRIVE_REG17_DMATYPE==0x1)
+ {
+ UINT32 source;
+ UINT16 length;
+ source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8) | ((MEGADRIVE_REG17_DMASOURCE3&0x7f)<<16))<<1;
+ length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8))<<1;
+
+ /* The 68k is frozen during this transfer, it should be safe to throw a few cycles away and do 'instant' DMA because the 68k can't detect it being in progress (can the z80?) */
+ //osd_printf_debug("68k->VRAM DMA transfer source %06x length %04x dest %04x enabled %01x\n", source, length, m_vdp_address,MEGADRIVE_REG01_DMA_ENABLE);
+ if (MEGADRIVE_REG01_DMA_ENABLE) insta_68k_to_vram_dma(source,length);
+
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x2)
+ {
+ //osd_printf_debug("vram fill length %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+ if (MEGADRIVE_REG01_DMA_ENABLE)
+ {
+ m_vram_fill_pending = 1;
+ m_vram_fill_length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8));
+ }
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x3)
+ {
+ UINT32 source;
+ UINT16 length;
+ source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8)); // source (byte offset)
+ length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8)); // length in bytes
+ //osd_printf_debug("setting vram copy mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+
+ if (MEGADRIVE_REG01_DMA_ENABLE) insta_vram_copy(source, length);
+ }
+ }
+ else if (m_vdp_code==0x23)
+ {
+ if (MEGADRIVE_REG17_DMATYPE==0x0 || MEGADRIVE_REG17_DMATYPE==0x1)
+ {
+ UINT32 source;
+ UINT16 length;
+ source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8) | ((MEGADRIVE_REG17_DMASOURCE3&0x7f)<<16))<<1;
+ length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8))<<1;
+
+ /* The 68k is frozen during this transfer, it should be safe to throw a few cycles away and do 'instant' DMA because the 68k can't detect it being in progress (can the z80?) */
+ //osd_printf_debug("68k->CRAM DMA transfer source %06x length %04x dest %04x enabled %01x\n", source, length, m_vdp_address,MEGADRIVE_REG01_DMA_ENABLE);
+ if (MEGADRIVE_REG01_DMA_ENABLE) insta_68k_to_cram_dma(source,length);
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x2)
+ {
+ //osd_printf_debug("vram fill length %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+ if (MEGADRIVE_REG01_DMA_ENABLE)
+ {
+ m_vram_fill_pending = 1;
+ m_vram_fill_length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8));
+ }
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x3)
+ {
+ osd_printf_debug("setting vram copy (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+ }
+ }
+ else if (m_vdp_code==0x25)
+ {
+ if (MEGADRIVE_REG17_DMATYPE==0x0 || MEGADRIVE_REG17_DMATYPE==0x1)
+ {
+ UINT32 source;
+ UINT16 length;
+ source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8) | ((MEGADRIVE_REG17_DMASOURCE3&0x7f)<<16))<<1;
+ length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8))<<1;
+
+ /* The 68k is frozen during this transfer, it should be safe to throw a few cycles away and do 'instant' DMA because the 68k can't detect it being in progress (can the z80?) */
+ //osd_printf_debug("68k->VSRAM DMA transfer source %06x length %04x dest %04x enabled %01x\n", source, length, m_vdp_address,MEGADRIVE_REG01_DMA_ENABLE);
+ if (MEGADRIVE_REG01_DMA_ENABLE) insta_68k_to_vsram_dma(source,length);
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x2)
+ {
+ //osd_printf_debug("vram fill length %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+ if (MEGADRIVE_REG01_DMA_ENABLE)
+ {
+ m_vram_fill_pending = 1;
+ m_vram_fill_length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8));
+ }
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x3)
+ {
+ osd_printf_debug("setting vram copy (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+ }
+ }
+ else if (m_vdp_code==0x30)
+ {
+ if (MEGADRIVE_REG17_DMATYPE==0x0)
+ {
+ osd_printf_debug("setting vram 68k->vram (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x1)
+ {
+ osd_printf_debug("setting vram 68k->vram (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x2)
+ {
+ osd_printf_debug("setting vram fill (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+ }
+ else if (MEGADRIVE_REG17_DMATYPE==0x3)
+ {
+ UINT32 source;
+ UINT16 length;
+ source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8)); // source (byte offset)
+ length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8)); // length in bytes
+ //osd_printf_debug("setting vram copy mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE);
+
+ if (MEGADRIVE_REG01_DMA_ENABLE) insta_vram_copy(source, length);
+ }
+ }
+}
+
+void sega315_5313_device::ctrl_port_w(int data)
+{
+// logerror("write to vdp control port %04x\n",data);
+ m_vram_fill_pending = 0; // ??
+
+ if (m_command_pending)
+ {
+ /* 2nd part of 32-bit command */
+ m_command_pending = 0;
+ m_command_part2 = data;
+
+ update_code_and_address();
+ handle_dma_bits();
+
+ //logerror("VDP Write Part 2 setting Code %02x Address %04x\n",m_vdp_code, m_vdp_address);
+
+ }
+ else
+ {
+ if ((data & 0xc000) == 0x8000)
+ { /* Register Setting Command */
+ int regnum = (data & 0x3f00) >> 8;
+ int value = (data & 0x00ff);
+
+ if (regnum &0x20) osd_printf_debug("reg error\n");
+
+ vdp_set_register(regnum & 0x1f, value);
+ m_vdp_code = 0;
+ m_vdp_address = 0;
+ }
+ else
+ {
+ m_command_pending = 1;
+ m_command_part1 = data;
+ update_code_and_address();
+ //logerror("VDP Write Part 1 setting Code %02x Address %04x\n",m_vdp_code, m_vdp_address);
+ }
+
+ }
+}
+
+WRITE16_MEMBER( sega315_5313_device::vdp_w )
+{
+ switch (offset<<1)
+ {
+ case 0x00:
+ case 0x02:
+ if (!ACCESSING_BITS_8_15)
+ {
+ data = (data&0x00ff) | data<<8;
+ // osd_printf_debug("8-bit write VDP data port access, offset %04x data %04x mem_mask %04x\n",offset,data,mem_mask);
+ }
+ else if (!ACCESSING_BITS_0_7)
+ {
+ data = (data&0xff00) | data>>8;
+ // osd_printf_debug("8-bit write VDP data port access, offset %04x data %04x mem_mask %04x\n",offset,data,mem_mask);
+ }
+ data_port_w(data);
+ break;
+
+ case 0x04:
+ case 0x06:
+ if ((!ACCESSING_BITS_8_15) || (!ACCESSING_BITS_0_7)) osd_printf_debug("8-bit write VDP control port access, offset %04x data %04x mem_mask %04x\n",offset,data,mem_mask);
+ ctrl_port_w(data);
+ break;
+
+ case 0x08:
+ case 0x0a:
+ case 0x0c:
+ case 0x0e:
+ logerror("Attempt to Write to HV counters!!\n");
+ break;
+
+ case 0x10:
+ case 0x12:
+ case 0x14:
+ case 0x16:
+ {
+ // accessed by either segapsg_device or sn76496_device
+ sn76496_base_device *sn = machine().device<sn76496_base_device>(":snsnd");
+ if (ACCESSING_BITS_0_7) sn->write(space, 0, data & 0xff);
+ //if (ACCESSING_BITS_8_15) sn->write(space, 0, (data>>8) & 0xff);
+ break;
+ }
+
+ default:
+ osd_printf_debug("write to unmapped vdp port\n");
+ }
+}
+
+UINT16 sega315_5313_device::vdp_vram_r(void)
+{
+ return MEGADRIV_VDP_VRAM((m_vdp_address&0xfffe)>>1);
+}
+
+UINT16 sega315_5313_device::vdp_vsram_r(void)
+{
+ return m_vsram[(m_vdp_address&0x7e)>>1];
+}
+
+UINT16 sega315_5313_device::vdp_cram_r(void)
+{
+ return m_cram[(m_vdp_address&0x7e)>>1];
+}
+
+UINT16 sega315_5313_device::data_port_r()
+{
+ UINT16 retdata=0;
+
+ //return machine().rand();
+
+ m_command_pending = 0;
+
+ switch (m_vdp_code & 0x000f)
+ {
+ case 0x0000:
+ retdata = vdp_vram_r();
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+ m_vdp_address&=0xffff;
+ break;
+
+ case 0x0001:
+ logerror("Attempting to READ from DATA PORT in VRAM WRITE MODE\n");
+ retdata = machine().rand();
+ break;
+
+ case 0x0003:
+ logerror("Attempting to READ from DATA PORT in CRAM WRITE MODE\n");
+ retdata = machine().rand();
+ break;
+
+ case 0x0004:
+ retdata = vdp_vsram_r();
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+ m_vdp_address&=0xffff;
+ break;
+
+ case 0x0005:
+ logerror("Attempting to READ from DATA PORT in VSRAM WRITE MODE\n");
+ break;
+
+ case 0x0008:
+ retdata = vdp_cram_r();
+ m_vdp_address+=MEGADRIVE_REG0F_AUTO_INC;
+ m_vdp_address&=0xffff;
+ break;
+
+ default:
+ logerror("Attempting to READ from DATA PORT in #UNDEFINED# MODE\n");
+ retdata = machine().rand();
+ break;
+ }
+
+// osd_printf_debug("vdp_data_port_r %04x %04x %04x\n",m_vdp_code, m_vdp_address, retdata);
+
+// logerror("Read VDP Data Port\n");
+ return retdata;
+}
+
+/*
+
+ NTSC, 256x224
+ -------------
+
+ Lines Description
+
+ 224 Active display
+ 8 Bottom border
+ 3 Bottom blanking
+ 3 Vertical blanking
+ 13 Top blanking
+ 11 Top border
+
+ V counter values
+ 00-EA, E5-FF
+
+PAL, 256x224
+ ------------
+
+ Lines Description
+
+ 224 Active display
+ 32 Bottom border
+ 3 Bottom blanking
+ 3 Vertical blanking
+ 13 Top blanking
+ 38 Top border
+
+ V counter values
+ 00-FF, 00-02, CA-FF
+
+ PAL, 256x240
+ ------------
+
+ Lines Description
+
+ 240 Active display
+ 24 Bottom border
+ 3 Bottom blanking
+ 3 Vertical blanking
+ 13 Top blanking
+ 30 Top border
+
+ V counter values
+ 00-FF, 00-0A, D2-FF
+
+
+
+ Pixels H.Cnt Description
+ 256 : 00-7F : Active display
+ 15 : 80-87 : Right border
+ 8 : 87-8B : Right blanking
+ 26 : 8B-ED : Horizontal sync
+ 2 : ED-EE : Left blanking
+ 14 : EE-F5 : Color burst
+ 8 : F5-F9 : Left blanking
+ 13 : F9-FF : Left border
+
+*/
+
+
+
+UINT16 sega315_5313_device::ctrl_port_r()
+{
+ /* Battletoads is very fussy about the vblank flag
+ it wants it to be 1. in scanline 224 */
+
+ /* Double Dragon 2 is very sensitive to hblank timing */
+ /* xperts is very fussy too */
+
+ /* Game no Kanzume Otokuyou (J) [!] is also fussy
+ - it cares about the bits labeled always 0, always 1.. (!)
+ */
+
+ /* Megalo Mania also fussy - cares about pending flag*/
+
+ int sprite_overflow = 0;
+ int odd_frame = m_imode_odd_frame^1;
+ int hblank_flag = 0;
+ int dma_active = 0;
+ int vblank = m_vblank_flag;
+ int fifo_empty = 1;
+ int fifo_full = 0;
+
+ UINT16 hpos = get_hposition();
+
+ if (hpos>400) hblank_flag = 1;
+ if (hpos>460) hblank_flag = 0;
+
+ /* extra case */
+ if (MEGADRIVE_REG01_DISP_ENABLE==0) vblank = 1;
+
+/*
+
+// these aren't *always* 0/1 some of them are open bus return
+ d15 - Always 0
+ d14 - Always 0
+ d13 - Always 1
+ d12 - Always 1
+
+ d11 - Always 0
+ d10 - Always 1
+ d9 - FIFO Empty
+ d8 - FIFO Full
+
+ d7 - Vertical interrupt pending
+ d6 - Sprite overflow on current scan line
+ d5 - Sprite collision
+ d4 - Odd frame
+
+ d3 - Vertical blanking
+ d2 - Horizontal blanking
+ d1 - DMA in progress
+ d0 - PAL mode flag
+*/
+
+ return (0<<15) | // ALWAYS 0
+ (0<<14) | // ALWAYS 0
+ (1<<13) | // ALWAYS 1
+ (1<<12) | // ALWAYS 1
+ (0<<11) | // ALWAYS 0
+ (1<<10) | // ALWAYS 1
+ (fifo_empty<<9 ) | // FIFO EMPTY
+ (fifo_full<<8 ) | // FIFO FULL
+ (m_irq6_pending << 7) | // exmutants has a tight loop checking this ..
+ (sprite_overflow << 6) |
+ (m_sprite_collision << 5) |
+ (odd_frame << 4) |
+ (vblank << 3) |
+ (hblank_flag << 2) |
+ (dma_active << 1 ) |
+ (m_vdp_pal << 0); // PAL MODE FLAG checked by striker for region prot..
+}
+
+static const UINT8 vc_ntsc_224[] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,/**/0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+ 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+static const UINT8 vc_ntsc_240[] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05
+};
+
+static const UINT8 vc_pal_224[] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0x02,/**/0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+ 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+ 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+static const UINT8 vc_pal_240[] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,/**/0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+ 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+ 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+
+UINT16 sega315_5313_device::get_hposition()
+{
+ UINT16 value4;
+
+ if (!m_use_alt_timing)
+ {
+ attotime time_elapsed_since_megadriv_scanline_timer;
+
+ time_elapsed_since_megadriv_scanline_timer = m_megadriv_scanline_timer->time_elapsed();
+
+ if (time_elapsed_since_megadriv_scanline_timer.attoseconds() < (ATTOSECONDS_PER_SECOND/m_framerate /m_total_scanlines))
+ {
+ value4 = (UINT16)(MAX_HPOSITION*((double)(time_elapsed_since_megadriv_scanline_timer.attoseconds()) / (double)(ATTOSECONDS_PER_SECOND/m_framerate /m_total_scanlines)));
+ }
+ else /* in some cases (probably due to rounding errors) we get some stupid results (the odd huge value where the time elapsed is much higher than the scanline time??!).. hopefully by clamping the result to the maximum we limit errors */
+ {
+ value4 = MAX_HPOSITION;
+ }
+
+ }
+ else
+ {
+ value4 = m_screen->hpos();
+ }
+
+ return value4;
+}
+
+int sega315_5313_device::get_scanline_counter()
+{
+ if (!m_use_alt_timing)
+ return m_scanline_counter;
+ else
+ return m_screen->vpos();
+}
+
+
+UINT16 sega315_5313_device::megadriv_read_hv_counters()
+{
+ /* Bubble and Squeek wants vcount=0xe0 */
+ /* Dracula is very sensitive to this */
+ /* Marvel Land is sensitive to this */
+
+ int vpos = get_scanline_counter();
+ UINT16 hpos = get_hposition();
+
+// if (hpos>424) vpos++; // fixes dracula, breaks road rash
+ if (hpos>460) vpos++; // when does vpos increase.. also on sms, check game gear manual..
+
+ /* shouldn't happen.. */
+ if (vpos<0)
+ {
+ vpos = m_total_scanlines;
+ osd_printf_debug("negative vpos?!\n");
+ }
+
+ if (MEGADRIVE_REG01_240_LINE)
+ {
+ assert(vpos % m_total_scanlines < (m_vdp_pal ? sizeof(vc_pal_240) : sizeof(vc_ntsc_240)));
+ vpos = m_vdp_pal ? vc_pal_240[vpos % m_total_scanlines] : vc_ntsc_240[vpos % m_total_scanlines];
+ }
+ else
+ {
+ assert(vpos % m_total_scanlines < (m_vdp_pal ? sizeof(vc_pal_224) : sizeof(vc_ntsc_224)));
+ vpos = m_vdp_pal ? vc_pal_224[vpos % m_total_scanlines] : vc_ntsc_224[vpos % m_total_scanlines];
+ }
+
+ if (hpos>0xf7) hpos -=0x49;
+
+ return ((vpos&0xff)<<8)|(hpos&0xff);
+
+}
+
+READ16_MEMBER( sega315_5313_device::vdp_r )
+{
+ UINT16 retvalue = 0;
+
+
+
+ switch (offset<<1)
+ {
+ case 0x00:
+ case 0x02:
+ if ((!ACCESSING_BITS_8_15) || (!ACCESSING_BITS_0_7)) osd_printf_debug("8-bit VDP read data port access, offset %04x mem_mask %04x\n",offset,mem_mask);
+ retvalue = data_port_r();
+ break;
+
+ case 0x04:
+ case 0x06:
+ // if ((!ACCESSING_BITS_8_15) || (!ACCESSING_BITS_0_7)) osd_printf_debug("8-bit VDP read control port access, offset %04x mem_mask %04x\n",offset,mem_mask);
+ retvalue = ctrl_port_r();
+ // retvalue = machine().rand();
+ // osd_printf_debug("%06x: Read Control Port at scanline %d hpos %d (return %04x)\n",space.device().safe_pc(),get_scanline_counter(), get_hposition(),retvalue);
+ break;
+
+ case 0x08:
+ case 0x0a:
+ case 0x0c:
+ case 0x0e:
+ // if ((!ACCESSING_BITS_8_15) || (!ACCESSING_BITS_0_7)) osd_printf_debug("8-bit VDP read HV counter port access, offset %04x mem_mask %04x\n",offset,mem_mask);
+ retvalue = megadriv_read_hv_counters();
+ // retvalue = machine().rand();
+ // osd_printf_debug("%06x: Read HV counters at scanline %d hpos %d (return %04x)\n",space.device().safe_pc(),get_scanline_counter(), get_hposition(),retvalue);
+ break;
+
+ case 0x10:
+ case 0x12:
+ case 0x14:
+ case 0x16:
+ logerror("Attempting to read PSG!\n");
+ retvalue = 0;
+ break;
+ }
+ return retvalue;
+}
+
+
+
+// line length = 342
+
+/*
+ The V counter counts up from 00h to EAh, then it jumps back to E5h and
+ continues counting up to FFh. This allows it to cover the entire 262 line
+ display.
+
+ The H counter counts up from 00h to E9h, then it jumps back to 93h and
+ continues counting up to FFh. This allows it to cover an entire 342 pixel
+ line.
+*/
+
+/*
+
+ - The 80th sprite has been drawn in 40-cell mode.
+ - The 64th sprite has been drawn in 32-cell mode.
+ - Twenty sprites on the same scanline have been drawn in 40 cell mode.
+ - Sixteen sprites on the same scanline have been drawn in 32 cell mode.
+ - 320 pixels worth of sprite data has been drawn on the same scanline
+ in 40 cell mode.
+ - 256 pixels worth of sprite data has been drawn on the same scanline
+ in 32 cell mode.
+ - The currently drawn sprite has a link field of zero.
+
+*/
+
+/*
+
+ $05 - Sprite Attribute Table Base Address
+ -----------------------------------------
+
+ Bits 6-0 of this register correspond to bits A15-A09 of the sprite
+ attribute table.
+
+ In 40-cell mode, A09 is always forced to zero.
+
+*/
+
+void sega315_5313_device::render_spriteline_to_spritebuffer(int scanline)
+{
+ int screenwidth;
+ int maxsprites=0;
+ int maxpixels=0;
+ UINT16 base_address=0;
+
+
+
+ screenwidth = MEGADRIVE_REG0C_RS0 | (MEGADRIVE_REG0C_RS1 << 1);
+
+ switch (screenwidth&3)
+ {
+ case 0: maxsprites = 64; maxpixels = 256; base_address = (MEGADRIVE_REG05_SPRITE_ADDR&0x7f)<<9; break;
+ case 1: maxsprites = 64; maxpixels = 256; base_address = (MEGADRIVE_REG05_SPRITE_ADDR&0x7f)<<9; break;
+ case 2: maxsprites = 80; maxpixels = 320; base_address = (MEGADRIVE_REG05_SPRITE_ADDR&0x7e)<<9; break;
+ case 3: maxsprites = 80; maxpixels = 320; base_address = (MEGADRIVE_REG05_SPRITE_ADDR&0x7e)<<9; break;
+ }
+
+
+ /* Clear our Render Buffer */
+ memset(m_sprite_renderline, 0, 1024);
+
+
+ {
+ int spritenum;
+ int ypos,xpos,addr;
+ int drawypos;
+ int /*drawwidth,*/ drawheight;
+ int spritemask = 0;
+ UINT8 height,width=0,link=0,xflip,yflip,colour,pri;
+
+ /* Get Sprite Attribs */
+ spritenum = 0;
+
+ //if (scanline==40) osd_printf_debug("spritelist start base %04x\n",base_address);
+
+ do
+ {
+ //UINT16 value1,value2,value3,value4;
+
+ //value1 = m_vram[((base_address>>1)+spritenum*4)+0x0];
+ //value2 = m_vram[((base_address>>1)+spritenum*4)+0x1];
+ //value3 = m_vram[((base_address>>1)+spritenum*4)+0x2];
+ //value4 = m_vram[((base_address>>1)+spritenum*4)+0x3];
+
+ ypos = (m_internal_sprite_attribute_table[(spritenum*4)+0x0] & 0x01ff)>>0; /* 0x03ff? */ // puyo puyo requires 0x1ff mask, not 0x3ff, see speech bubble corners
+ height= (m_internal_sprite_attribute_table[(spritenum*4)+0x1] & 0x0300)>>8;
+ width = (m_internal_sprite_attribute_table[(spritenum*4)+0x1] & 0x0c00)>>10;
+ link = (m_internal_sprite_attribute_table[(spritenum*4)+0x1] & 0x007f)>>0;
+ xpos = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x3) & 0x01ff)>>0; /* 0x03ff? */ // pirates gold has a sprite with co-ord 0x200...
+
+ if(m_imode == 3)
+ {
+ ypos = (m_internal_sprite_attribute_table[(spritenum*4)+0x0] & 0x03ff)>>0; /* 0x3ff requried in interlace mode (sonic 2 2 player) */
+ drawypos = ypos - 256;
+ drawheight = (height+1)*16;
+ }
+ else
+ {
+ ypos = (m_internal_sprite_attribute_table[(spritenum*4)+0x0] & 0x01ff)>>0; /* 0x03ff? */ // puyo puyo requires 0x1ff mask, not 0x3ff, see speech bubble corners
+ drawypos = ypos - 128;
+ drawheight = (height+1)*8;
+ }
+
+
+
+ //if (scanline==40) osd_printf_debug("xpos %04x ypos %04x\n",xpos,ypos);
+
+ if ((drawypos<=scanline) && ((drawypos+drawheight)>scanline))
+ {
+ addr = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x07ff)>>0;
+ xflip = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x0800)>>11;
+ yflip = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x1000)>>12;
+ colour= (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x6000)>>13;
+ pri = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x8000)>>15;
+
+ if(m_imode == 3)
+ {
+ addr<<=1;
+ addr &=0x7ff;
+ }
+
+ //drawwidth = (width+1)*8;
+ if (pri==1) pri = 0x80;
+ else pri = 0x40;
+
+ /* todo: fix me, I'm sure this isn't right but sprite 0 + other sprite seem to do something..
+ maybe spritemask|=2 should be set for anything < 0x40 ?*/
+ if (xpos==0x00) spritemask|=1;
+
+ //if (xpos==0x01) spritemask|=2;
+ //if (xpos==0x04) spritemask|=2; // sonic 2 title screen
+ //if (xpos==0x08) spritemask|=2; // rocket night adventures
+ //if (xpos==0x10) spritemask|=2; // mercs l1 boss
+ //if (xpos==0x0a) spritemask|=2; // legend of galahad
+ //if (xpos==0x21) spritemask|=2; // shadow of the beast?
+ if ((xpos>0) && (xpos<0x40)) spritemask|=2;
+
+ if (spritemask==0x3)
+ return;
+ /* end todo: */
+
+ {
+ //int xdraw;
+ int xtile;
+ int yline = scanline - drawypos;
+
+ for (xtile=0;xtile<width+1;xtile++)
+ {
+ int dat;
+
+ if (!xflip)
+ {
+ UINT16 base_addr;
+ int xxx;
+ UINT32 gfxdata;
+ int loopcount;
+
+ if(m_imode == 3)
+ {
+ if (!yflip) base_addr = (addr<<4)+(xtile*((height+1)*(2*16)))+(yline*2);
+ else base_addr = (addr<<4)+(xtile*((height+1)*(2*16)))+((((height+1)*16)-yline-1)*2);
+ }
+ else
+ {
+ if (!yflip) base_addr = (addr<<4)+(xtile*((height+1)*(2*8)))+(yline*2);
+ else base_addr = (addr<<4)+(xtile*((height+1)*(2*8)))+((((height+1)*8)-yline-1)*2);
+ }
+
+ xxx = (xpos+xtile*8)&0x1ff;
+
+ gfxdata = MEGADRIV_VDP_VRAM(base_addr+1) | (MEGADRIV_VDP_VRAM(base_addr+0)<<16);
+
+ for(loopcount=0;loopcount<8;loopcount++)
+ {
+ dat = (gfxdata & 0xf0000000)>>28; gfxdata <<=4;
+ if (dat) { if (!m_sprite_renderline[xxx]) { m_sprite_renderline[xxx] = dat | (colour<<4)| pri; } else { m_sprite_collision = 1; } }
+ xxx++;xxx&=0x1ff;
+ if (--maxpixels == 0x00) return;
+ }
+
+ }
+ else
+ {
+ UINT16 base_addr;
+ int xxx;
+ UINT32 gfxdata;
+
+ int loopcount;
+
+ if(m_imode == 3)
+ {
+ if (!yflip) base_addr = (addr<<4)+(((width-xtile))*((height+1)*(2*16)))+(yline*2);
+ else base_addr = (addr<<4)+(((width-xtile))*((height+1)*(2*16)))+((((height+1)*16)-yline-1)*2);
+
+ }
+ else
+ {
+ if (!yflip) base_addr = (addr<<4)+(((width-xtile))*((height+1)*(2*8)))+(yline*2);
+ else base_addr = (addr<<4)+(((width-xtile))*((height+1)*(2*8)))+((((height+1)*8)-yline-1)*2);
+ }
+
+ xxx = (xpos+xtile*8)&0x1ff;
+
+ gfxdata = MEGADRIV_VDP_VRAM((base_addr+1)&0x7fff) | (MEGADRIV_VDP_VRAM((base_addr+0)&0x7fff)<<16);
+
+ for(loopcount=0;loopcount<8;loopcount++)
+ {
+ dat = (gfxdata & 0x0000000f)>>0; gfxdata >>=4;
+ if (dat) { if (!m_sprite_renderline[xxx]) { m_sprite_renderline[xxx] = dat | (colour<<4)| pri; } else { m_sprite_collision = 1; } }
+ xxx++;xxx&=0x1ff;
+ if (--maxpixels == 0x00) return;
+ }
+
+ }
+ }
+ }
+ }
+
+ spritenum = link;
+ maxsprites--;
+ }
+ while ((maxsprites>=0) && (link!=0));
+
+
+ }
+}
+
+/* Clean up this function (!) */
+void sega315_5313_device::render_videoline_to_videobuffer(int scanline)
+{
+ UINT16 base_a;
+ UINT16 base_w=0;
+ UINT16 base_b;
+
+ UINT16 size;
+ UINT16 hsize = 64;
+ UINT16 vsize = 64;
+ UINT16 window_right;
+// UINT16 window_hpos;
+ UINT16 window_down;
+// UINT16 window_vpos;
+ UINT16 hscroll_base;
+// UINT8 vscroll_mode;
+// UINT8 hscroll_mode;
+ int window_firstline;
+ int window_lastline;
+ int window_firstcol;
+ int window_lastcol;
+ int screenwidth;
+ int numcolumns = 0;
+ int hscroll_a = 0;
+ int hscroll_b = 0;
+ int x;
+ int window_hsize=0;
+ int window_vsize=0;
+ int window_is_bugged = 0;
+ int non_window_firstcol;
+ int non_window_lastcol;
+ int screenheight = MEGADRIVE_REG01_240_LINE?240:224;
+
+ /* Clear our Render Buffer */
+ for (x=0;x<320;x++)
+ {
+ m_video_renderline[x]=MEGADRIVE_REG07_BGCOLOUR | 0x20000; // mark as BG
+ }
+
+ memset(m_highpri_renderline, 0, 320);
+
+ /* is this line enabled? */
+ if (!MEGADRIVE_REG01_DISP_ENABLE)
+ {
+ //osd_printf_debug("line disabled %d\n",scanline);
+ return;
+ }
+
+ /* looks different? */
+ if (MEGADRIVE_REG0_DISPLAY_DISABLE)
+ {
+ return;
+ }
+
+
+
+ base_a = MEGADRIVE_REG02_PATTERN_ADDR_A << 13;
+
+ base_b = MEGADRIVE_REG04_PATTERN_ADDR_B << 13;
+ size = MEGADRIVE_REG10_HSCROLL_SIZE | (MEGADRIVE_REG10_VSCROLL_SIZE<<4);
+ window_right = MEGADRIVE_REG11_WINDOW_RIGHT;
+// window_hpos = MEGADRIVE_REG11_WINDOW_HPOS;
+ window_down = MEGADRIVE_REG12_WINDOW_DOWN;
+// window_vpos = MEGADRIVE_REG12_WINDOW_VPOS;
+
+ screenwidth = MEGADRIVE_REG0C_RS0 | (MEGADRIVE_REG0C_RS1 << 1);
+
+ switch (screenwidth)
+ {
+ case 0: numcolumns = 32; window_hsize = 32; window_vsize = 32; base_w = (MEGADRIVE_REG03_PATTERN_ADDR_W&0x1f) << 11; break;
+ case 1: numcolumns = 32; window_hsize = 32; window_vsize = 32; base_w = (MEGADRIVE_REG03_PATTERN_ADDR_W&0x1f) << 11; break;
+ case 2: numcolumns = 40; window_hsize = 64; window_vsize = 32; base_w = (MEGADRIVE_REG03_PATTERN_ADDR_W&0x1e) << 11; break;
+ case 3: numcolumns = 40; window_hsize = 64; window_vsize = 32; base_w = (MEGADRIVE_REG03_PATTERN_ADDR_W&0x1e) << 11; break; // talespin cares about base mask, used for status bar
+ }
+
+ //osd_printf_debug("screenwidth %d\n",screenwidth);
+
+ //base_w = machine().rand()&0xff;
+
+ /* Calculate Exactly where we're going to draw the Window, and if the Window Bug applies */
+ window_is_bugged = 0;
+ if (window_right)
+ {
+ window_firstcol = MEGADRIVE_REG11_WINDOW_HPOS*16;
+ window_lastcol = numcolumns*8;
+ if (window_firstcol>window_lastcol) window_firstcol = window_lastcol;
+
+ non_window_firstcol = 0;
+ non_window_lastcol = window_firstcol;
+ }
+ else
+ {
+ window_firstcol = 0;
+ window_lastcol = MEGADRIVE_REG11_WINDOW_HPOS*16;
+ if (window_lastcol>numcolumns*8) window_lastcol = numcolumns*8;
+
+ non_window_firstcol = window_lastcol;
+ non_window_lastcol = numcolumns*8;
+
+ if (window_lastcol!=0) window_is_bugged=1;
+ }
+
+ if (window_down)
+ {
+ window_firstline = MEGADRIVE_REG12_WINDOW_VPOS*8;
+ window_lastline = screenheight; // 240 in PAL?
+ if (window_firstline>screenheight) window_firstline = screenheight;
+ }
+ else
+ {
+ window_firstline = 0;
+ window_lastline = MEGADRIVE_REG12_WINDOW_VPOS*8;
+ if (window_lastline>screenheight) window_lastline = screenheight;
+ }
+
+ /* if we're on a window scanline between window_firstline and window_lastline the window is the full width of the screen */
+ if (scanline>=window_firstline && scanline < window_lastline)
+ {
+ window_firstcol = 0; window_lastcol = numcolumns*8; // window is full-width of the screen
+ non_window_firstcol = 0; non_window_lastcol=0; // disable non-window
+ }
+
+
+// vscroll_mode = MEGADRIVE_REG0B_VSCROLL_MODE;
+// hscroll_mode = MEGADRIVE_REG0B_HSCROLL_MODE;
+ hscroll_base = MEGADRIVE_REG0D_HSCROLL_ADDR<<10;
+
+ switch (size)
+ {
+ case 0x00: hsize = 32; vsize = 32; break;
+ case 0x01: hsize = 64; vsize = 32; break;
+ case 0x02: hsize = 64; vsize = 1; /* osd_printf_debug("Invalid HSize! %02x\n",size);*/ break;
+ case 0x03: hsize = 128;vsize = 32; break;
+
+ case 0x10: hsize = 32; vsize = 64; break;
+ case 0x11: hsize = 64; vsize = 64; break;
+ case 0x12: hsize = 64; vsize = 1; /*osd_printf_debug("Invalid HSize! %02x\n",size);*/ break;
+ case 0x13: hsize = 128;vsize = 32;/*osd_printf_debug("Invalid Total Size! %02x\n",size);*/break;
+
+ case 0x20: hsize = 32; vsize = 64; osd_printf_debug("Invalid VSize!\n"); break;
+ case 0x21: hsize = 64; vsize = 64; osd_printf_debug("Invalid VSize!\n"); break;
+ case 0x22: hsize = 64; vsize = 1; /*osd_printf_debug("Invalid HSize & Invalid VSize!\n");*/ break;
+ case 0x23: hsize = 128;vsize = 64; osd_printf_debug("Invalid VSize!\n"); break;
+
+ case 0x30: hsize = 32; vsize = 128; break;
+ case 0x31: hsize = 64; vsize = 64; /*osd_printf_debug("Invalid Total Size! %02x\n",size);*/break; // super skidmarks attempts this..
+ case 0x32: hsize = 64; vsize = 1; /*osd_printf_debug("Invalid HSize & Invalid Total Size!\n");*/ break;
+ case 0x33: hsize = 128;vsize = 128; osd_printf_debug("Invalid Total Size! %02x\n",size);break;
+ }
+
+ switch (MEGADRIVE_REG0B_HSCROLL_MODE)
+ {
+ case 0x00: // Full Screen Scroll
+ hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0);
+ hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1);
+ break;
+
+ case 0x01: // 'Broken' Line Scroll
+ if(m_imode == 3)
+ {
+ hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+((scanline>>1)&7)*2);
+ hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+((scanline>>1)&7)*2);
+ }
+ else
+ {
+ hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+(scanline&7)*2);
+ hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+(scanline&7)*2);
+ }
+ break;
+
+ case 0x02: // Cell Scroll
+ if(m_imode == 3)
+ {
+ hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+((scanline>>1)&~7)*2);
+ hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+((scanline>>1)&~7)*2);
+ }
+ else
+ {
+ hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+(scanline&~7)*2);
+ hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+(scanline&~7)*2);
+ }
+ break;
+
+ case 0x03: // Full Line Scroll
+ if(m_imode == 3)
+ {
+ hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+(scanline>>1)*2);
+ hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+(scanline>>1)*2);
+ }
+ else
+ {
+ hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+scanline*2);
+ hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+scanline*2);
+ }
+ break;
+ }
+
+ /* Low Priority B Tiles */
+ {
+ int column;
+ int vscroll;
+
+ for (column=0;column<numcolumns/2;column++)
+ { /* 20x 16x1 blocks */
+ int vcolumn;
+ int dpos;
+
+ /* Get V Scroll Value for this block */
+
+ dpos = column*16;
+
+ {
+ /* hscroll is not divisible by 8, this segment will contain 3 tiles, 1 partial, 1 whole, 1 partial */
+ int hscroll_part = 8-(hscroll_b%8);
+ int hcolumn;
+ int tile_base;
+ int tile_dat;
+ int tile_addr;
+ int tile_xflip;
+ int tile_yflip;
+ int tile_colour;
+ int tile_pri;
+ int dat;
+
+ if (MEGADRIVE_REG0B_VSCROLL_MODE)
+ {
+ if (hscroll_b&0xf) vscroll = m_vsram[((column-1)*2+1)&0x3f];
+ else vscroll = m_vsram[((column)*2+1)&0x3f];
+ }
+ else
+ {
+ vscroll = m_vsram[1];
+ }
+
+ hcolumn = ((column*2-1)-(hscroll_b>>3))&(hsize-1);
+
+ if(m_imode == 3)
+ {
+ vcolumn = (vscroll + scanline)&((vsize*16)-1);
+ tile_base = (base_b>>1)+((vcolumn>>4)*hsize)+hcolumn;
+
+ }
+ else
+ {
+ vcolumn = (vscroll + scanline)&((vsize*8)-1);
+ tile_base = (base_b>>1)+((vcolumn>>3)*hsize)+hcolumn;
+ }
+
+
+ tile_base &=0x7fff;
+ tile_dat = MEGADRIV_VDP_VRAM(tile_base);
+ tile_xflip = (tile_dat&0x0800);
+ tile_yflip = (tile_dat&0x1000);
+ tile_colour =(tile_dat&0x6000)>>13;
+ tile_pri = (tile_dat&0x8000)>>15;
+ tile_addr = ((tile_dat&0x07ff)<<4);
+
+ if(m_imode == 3)
+ {
+ tile_addr <<=1;
+ tile_addr &=0x7fff;
+ if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2;
+ else tile_addr+=((0xf-vcolumn)&0xf)*2;
+ }
+ else
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&7)*2;
+ else tile_addr+=((7-vcolumn)&7)*2;
+ }
+
+ if (!tile_xflip)
+ {
+ /* 8 pixels */
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+
+ for (shift=hscroll_part;shift<8;shift++)
+ {
+ dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4); } else m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ dpos++;
+ }
+ }
+ else
+ {
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+ for (shift=hscroll_part;shift<8;shift++)
+ {
+ dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4); } else m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ dpos++;
+ }
+ }
+
+ if (MEGADRIVE_REG0B_VSCROLL_MODE)
+ {
+ if (hscroll_b&0xf) vscroll = m_vsram[((column-1)*2+1)&0x3f];
+ else vscroll = m_vsram[((column)*2+1)&0x3f];
+ }
+ else
+ {
+ vscroll = m_vsram[1];
+ }
+
+ hcolumn = ((column*2)-(hscroll_b>>3))&(hsize-1);
+
+ if(m_imode == 3)
+ {
+ vcolumn = (vscroll + scanline)&((vsize*16)-1);
+ tile_base = (base_b>>1)+((vcolumn>>4)*hsize)+hcolumn;
+ }
+ else
+ {
+ vcolumn = (vscroll + scanline)&((vsize*8)-1);
+ tile_base = (base_b>>1)+((vcolumn>>3)*hsize)+hcolumn;
+ }
+
+ tile_base &=0x7fff;
+ tile_dat = MEGADRIV_VDP_VRAM(tile_base);
+ tile_xflip = (tile_dat&0x0800);
+ tile_yflip = (tile_dat&0x1000);
+ tile_colour =(tile_dat&0x6000)>>13;
+ tile_pri = (tile_dat&0x8000)>>15;
+ tile_addr = ((tile_dat&0x07ff)<<4);
+
+ if(m_imode == 3)
+ {
+ tile_addr <<=1;
+ tile_addr &=0x7fff;
+
+ if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2;
+ else tile_addr+=((0xf-vcolumn)&0xf)*2;
+ }
+ else
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&7)*2;
+ else tile_addr+=((7-vcolumn)&7)*2;
+ }
+
+ if (!tile_xflip)
+ {
+ /* 8 pixels */
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+
+ for (shift=0;shift<8;shift++)
+ {
+ dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4); } else m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ dpos++;
+ }
+ }
+ else
+ {
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+ for (shift=0;shift<8;shift++)
+ {
+ dat = (gfxdata>>(shift*4))&0x000f; if (!tile_pri) { if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4); } else m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ dpos++;
+ }
+ }
+
+ if (MEGADRIVE_REG0B_VSCROLL_MODE)
+ {
+ vscroll = m_vsram[((column)*2+1)&0x3f];
+ }
+ else
+ {
+ vscroll = m_vsram[1];
+ }
+
+ hcolumn = ((column*2+1)-(hscroll_b>>3))&(hsize-1);
+
+ if(m_imode == 3)
+ {
+ vcolumn = (vscroll + scanline)&((vsize*16)-1);
+ tile_base = (base_b>>1)+((vcolumn>>4)*hsize)+hcolumn;
+ }
+ else
+ {
+ vcolumn = (vscroll + scanline)&((vsize*8)-1);
+ tile_base = (base_b>>1)+((vcolumn>>3)*hsize)+hcolumn;
+ }
+
+ tile_base &=0x7fff;
+ tile_dat = MEGADRIV_VDP_VRAM(tile_base);
+ tile_xflip = (tile_dat&0x0800);
+ tile_yflip = (tile_dat&0x1000);
+ tile_colour =(tile_dat&0x6000)>>13;
+ tile_pri = (tile_dat&0x8000)>>15;
+ tile_addr = ((tile_dat&0x07ff)<<4);
+
+ if(m_imode == 3)
+ {
+ tile_addr <<=1;
+ tile_addr &=0x7fff;
+ if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2;
+ else tile_addr+=((0xf-vcolumn)&0xf)*2;
+ }
+ else
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&7)*2;
+ else tile_addr+=((7-vcolumn)&7)*2;
+ }
+
+
+ if (!tile_xflip)
+ {
+ /* 8 pixels */
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+
+ for (shift=0;shift<(hscroll_part);shift++)
+ {
+ dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4); } else m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ dpos++;
+ }
+ }
+ else
+ {
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+ for (shift=0;shift<(hscroll_part);shift++)
+ {
+ dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4); } else m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ dpos++;
+ }
+ }
+ }
+ }
+ /* END */
+ }
+ /* Low Priority A Tiles + Window(!) */
+
+ {
+ int column;
+ int vscroll;
+
+ for (column=window_firstcol/16;column<window_lastcol/16;column++)
+ {
+ int vcolumn;
+ int dpos;
+
+ int hcolumn;
+ int tile_base;
+ int tile_dat;
+ int tile_addr;
+ int tile_xflip;
+ int tile_yflip;
+ int tile_colour;
+ int tile_pri;
+ int dat;
+
+ vcolumn = scanline&((window_vsize*8)-1);
+ dpos = column*16;
+ hcolumn = (column*2)&(window_hsize-1);
+
+ if(m_imode == 3)
+ {
+ tile_base = (base_w>>1)+((vcolumn>>4)*window_hsize)+hcolumn;
+ }
+ else
+ {
+ tile_base = (base_w>>1)+((vcolumn>>3)*window_hsize)+hcolumn;
+ }
+
+ tile_base &=0x7fff;
+ tile_dat = MEGADRIV_VDP_VRAM(tile_base);
+ tile_xflip = (tile_dat&0x0800);
+ tile_yflip = (tile_dat&0x1000);
+ tile_colour =(tile_dat&0x6000)>>13;
+ tile_pri = (tile_dat&0x8000)>>15;
+ tile_addr = ((tile_dat&0x07ff)<<4);
+
+ if(m_imode == 3)
+ {
+ tile_addr <<=1;
+ tile_addr &=0x7fff;
+ }
+
+ if(m_imode == 3)
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2;
+ else tile_addr+=((0xf-vcolumn)&0xf)*2;
+ }
+ else
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&7)*2;
+ else tile_addr+=((7-vcolumn)&7)*2;
+ }
+
+ if (!tile_xflip)
+ {
+ /* 8 pixels */
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+
+ for (shift=0;shift<8;shift++)
+ {
+ dat = (gfxdata>>(28-(shift*4)))&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+ else
+ {
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+ for (shift=0;shift<8;shift++)
+ {
+ dat = (gfxdata>>(shift*4) )&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+
+ }
+ }
+
+
+ hcolumn = (column*2+1)&(window_hsize-1);
+ if(m_imode == 3)
+ {
+ tile_base = (base_w>>1)+((vcolumn>>4)*window_hsize)+hcolumn;
+ }
+ else
+ {
+ tile_base = (base_w>>1)+((vcolumn>>3)*window_hsize)+hcolumn;
+ }
+ tile_base &=0x7fff;
+ tile_dat = MEGADRIV_VDP_VRAM(tile_base);
+ tile_xflip = (tile_dat&0x0800);
+ tile_yflip = (tile_dat&0x1000);
+ tile_colour =(tile_dat&0x6000)>>13;
+ tile_pri = (tile_dat&0x8000)>>15;
+ tile_addr = ((tile_dat&0x07ff)<<4);
+
+ if(m_imode == 3)
+ {
+ tile_addr <<=1;
+ tile_addr &=0x7fff;
+ }
+
+ if(m_imode == 3)
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2;
+ else tile_addr+=((0xf-vcolumn)&0xf)*2;
+ }
+ else
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&7)*2;
+ else tile_addr+=((7-vcolumn)&7)*2;
+ }
+
+ if (!tile_xflip)
+ {
+ /* 8 pixels */
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+
+ for (shift=0;shift<8;shift++)
+ {
+ dat = (gfxdata>>(28-(shift*4)))&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+ else
+ {
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+ for (shift=0;shift<8;shift++)
+ {
+ dat = (gfxdata>>(shift*4) )&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+ }
+
+ /* Non Window Part */
+
+ for (column=non_window_firstcol/16;column<non_window_lastcol/16;column++)
+ { /* 20x 16x1 blocks */
+ // int xx;
+ int vcolumn;
+ int dpos;
+
+ dpos = column*16;
+
+ { /* hscroll is not divisible by 8, this segment will contain 3 tiles, 1 partial, 1 whole, 1 partial */
+ int hscroll_part = 8-(hscroll_a%8);
+ int hcolumn;
+ int tile_base;
+ int tile_dat;
+ int tile_addr;
+ int tile_xflip;
+ int tile_yflip;
+ int tile_colour;
+ int tile_pri;
+ int dat;
+
+ if (MEGADRIVE_REG0B_VSCROLL_MODE)
+ {
+ if (hscroll_a&0xf) vscroll = m_vsram[((column-1)*2+0)&0x3f];
+ else vscroll = m_vsram[((column)*2+0)&0x3f];
+ }
+ else
+ {
+ vscroll = m_vsram[0];
+ }
+
+
+ if ((!window_is_bugged) || ((hscroll_a&0xf)==0) || (column>non_window_firstcol/16)) hcolumn = ((column*2-1)-(hscroll_a>>3))&(hsize-1);
+ else hcolumn = ((column*2+1)-(hscroll_a>>3))&(hsize-1);
+
+ if(m_imode == 3)
+ {
+ vcolumn = (vscroll + scanline)&((vsize*16)-1);
+ }
+ else
+ {
+ vcolumn = (vscroll + scanline)&((vsize*8)-1);
+ }
+
+ if(m_imode == 3)
+ {
+ tile_base = (base_a>>1)+((vcolumn>>4)*hsize)+hcolumn;
+ }
+ else
+ {
+ tile_base = (base_a>>1)+((vcolumn>>3)*hsize)+hcolumn;
+ }
+
+
+ tile_base &=0x7fff;
+ tile_dat = MEGADRIV_VDP_VRAM(tile_base);
+ tile_xflip = (tile_dat&0x0800);
+ tile_yflip = (tile_dat&0x1000);
+ tile_colour =(tile_dat&0x6000)>>13;
+ tile_pri = (tile_dat&0x8000)>>15;
+ tile_addr = ((tile_dat&0x07ff)<<4);
+
+ if(m_imode == 3)
+ {
+ tile_addr <<=1;
+ tile_addr &=0x7fff;
+ if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2;
+ else tile_addr+=((0xf-vcolumn)&0xf)*2;
+ }
+ else
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&7)*2;
+ else tile_addr+=((7-vcolumn)&7)*2;
+ }
+
+ if (!tile_xflip)
+ {
+ /* 8 pixels */
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+
+ for (shift=hscroll_part;shift<8;shift++)
+ {
+ dat = (gfxdata>>(28-(shift*4)))&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+ else
+ {
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+ for (shift=hscroll_part;shift<8;shift++)
+ {
+ dat = (gfxdata>>(shift*4) )&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+
+ if (MEGADRIVE_REG0B_VSCROLL_MODE)
+ {
+ if (hscroll_a&0xf) vscroll = m_vsram[((column-1)*2+0)&0x3f];
+ else vscroll = m_vsram[((column)*2+0)&0x3f];
+ }
+ else
+ {
+ vscroll = m_vsram[0];
+ }
+
+ if ((!window_is_bugged) || ((hscroll_a&0xf)==0) || (column>non_window_firstcol/16)) hcolumn = ((column*2)-(hscroll_a>>3))&(hsize-1); // not affected by bug?
+ else
+ {
+ if ((hscroll_a&0xf)<8) hcolumn = ((column*2)-(hscroll_a>>3))&(hsize-1);
+ else hcolumn = ((column*2+2)-(hscroll_a>>3))&(hsize-1);
+ }
+
+
+ if(m_imode == 3)
+ {
+ vcolumn = (vscroll + scanline)&((vsize*16)-1);
+ tile_base = (base_a>>1)+((vcolumn>>4)*hsize)+hcolumn;
+ }
+ else
+ {
+ vcolumn = (vscroll + scanline)&((vsize*8)-1);
+ tile_base = (base_a>>1)+((vcolumn>>3)*hsize)+hcolumn;
+ }
+
+ tile_base &=0x7fff;
+ tile_dat = MEGADRIV_VDP_VRAM(tile_base);
+ tile_xflip = (tile_dat&0x0800);
+ tile_yflip = (tile_dat&0x1000);
+ tile_colour =(tile_dat&0x6000)>>13;
+ tile_pri = (tile_dat&0x8000)>>15;
+ tile_addr = ((tile_dat&0x07ff)<<4);
+
+
+ if(m_imode == 3)
+ {
+ tile_addr <<=1;
+ tile_addr &=0x7fff;
+ if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2;
+ else tile_addr+=((0xf-vcolumn)&0xf)*2;
+ }
+ else
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&7)*2;
+ else tile_addr+=((7-vcolumn)&7)*2;
+ }
+
+ if (!tile_xflip)
+ {
+ /* 8 pixels */
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+
+ for (shift=0;shift<8;shift++)
+ {
+ dat = (gfxdata>>(28-(shift*4)))&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+ else
+ {
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+ for (shift=0;shift<8;shift++)
+ {
+ dat = (gfxdata>>(shift*4) )&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+
+ if (MEGADRIVE_REG0B_VSCROLL_MODE)
+ {
+ vscroll = m_vsram[((column)*2+0)&0x3f];
+ }
+ else
+ {
+ vscroll = m_vsram[0];
+ }
+
+ if ((!window_is_bugged) || ((hscroll_a&0xf)==0) || (column>non_window_firstcol/16)) hcolumn = ((column*2+1)-(hscroll_a>>3))&(hsize-1);
+ else hcolumn = ((column*2+1)-(hscroll_a>>3))&(hsize-1);
+
+ if(m_imode == 3)
+ {
+ vcolumn = (vscroll + scanline)&((vsize*16)-1);
+ tile_base = (base_a>>1)+((vcolumn>>4)*hsize)+hcolumn;
+ }
+ else
+ {
+ vcolumn = (vscroll + scanline)&((vsize*8)-1);
+ tile_base = (base_a>>1)+((vcolumn>>3)*hsize)+hcolumn;
+ }
+ tile_base &=0x7fff;
+ tile_dat = MEGADRIV_VDP_VRAM(tile_base);
+ tile_xflip = (tile_dat&0x0800);
+ tile_yflip = (tile_dat&0x1000);
+ tile_colour =(tile_dat&0x6000)>>13;
+ tile_pri = (tile_dat&0x8000)>>15;
+ tile_addr = ((tile_dat&0x07ff)<<4);
+
+ if(m_imode == 3)
+ {
+ tile_addr <<=1;
+ tile_addr &=0x7fff;
+ }
+
+ if(m_imode == 3)
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2;
+ else tile_addr+=((0xf-vcolumn)&0xf)*2;
+ }
+ else
+ {
+ if (!tile_yflip) tile_addr+=(vcolumn&7)*2;
+ else tile_addr+=((7-vcolumn)&7)*2;
+ }
+
+ if (!tile_xflip)
+ {
+ /* 8 pixels */
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+
+ for (shift=0;shift<(hscroll_part);shift++)
+ {
+ dat = (gfxdata>>(28-(shift*4)))&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+ else
+ {
+ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1);
+ int shift;
+ for (shift=0;shift<(hscroll_part);shift++)
+ {
+ dat = (gfxdata>>(shift*4) )&0x000f;
+ if (!tile_pri)
+ {
+ if(dat) m_video_renderline[dpos] = dat | (tile_colour<<4);
+ }
+ else
+ {
+ if (dat) m_highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80;
+ else m_highpri_renderline[dpos] = m_highpri_renderline[dpos]|0x80;
+ }
+ dpos++;
+ }
+ }
+ }
+ }
+ }
+ /* END */
+
+/* MEGADRIVE_REG0C_SHADOW_HIGLIGHT */
+ /* Low Priority Sprites */
+ for (x=0;x<320;x++)
+ {
+ if (!MEGADRIVE_REG0C_SHADOW_HIGLIGHT)
+ {
+ if (m_sprite_renderline[x+128] & 0x40)
+ {
+ m_video_renderline[x] = m_sprite_renderline[x+128]&0x3f;
+ m_video_renderline[x] |= 0x10000; // mark as sprite pixel
+ }
+ }
+ else
+ { /* Special Shadow / Highlight processing */
+
+ if (m_sprite_renderline[x+128] & 0x40)
+ {
+ UINT8 spritedata;
+ spritedata = m_sprite_renderline[x+128]&0x3f;
+
+ if ((spritedata==0x0e) || (spritedata==0x1e) || (spritedata==0x2e))
+ {
+ /* BUG in sprite chip, these colours are always normal intensity */
+ m_video_renderline[x] = spritedata | 0x4000;
+ m_video_renderline[x] |= 0x10000; // mark as sprite pixel
+ }
+ else if (spritedata==0x3e)
+ {
+ /* Everything below this is half colour, mark with 0x8000 to mark highlight' */
+ m_video_renderline[x] = m_video_renderline[x]|0x8000; // spiderwebs..
+ }
+ else if (spritedata==0x3f)
+ {
+ /* This is a Shadow operator, but everything below is already low pri, no effect */
+ m_video_renderline[x] = m_video_renderline[x]|0x2000;
+
+ }
+ else
+ {
+ m_video_renderline[x] = spritedata;
+ m_video_renderline[x] |= 0x10000; // mark as sprite pixel
+ }
+
+ }
+ }
+ }
+ /* High Priority A+B Tiles */
+ for (x=0;x<320;x++)
+ {
+ if (!MEGADRIVE_REG0C_SHADOW_HIGLIGHT)
+ {
+ /* Normal Processing */
+ int dat;
+ dat = m_highpri_renderline[x];
+
+ if (dat&0x80)
+ {
+ if (dat&0x0f) m_video_renderline[x] = m_highpri_renderline[x]&0x3f;
+ }
+ }
+ else
+ {
+ /* Shadow / Highlight Mode */
+ int dat;
+ dat = m_highpri_renderline[x];
+
+ if (dat&0x80)
+ {
+ if (dat&0x0f) m_video_renderline[x] = (m_highpri_renderline[x]&0x3f) | 0x4000;
+ else m_video_renderline[x] = m_video_renderline[x] | 0x4000; // set 'normal'
+ }
+ }
+ }
+
+ /* High Priority Sprites */
+ for (x=0;x<320;x++)
+ {
+ if (!MEGADRIVE_REG0C_SHADOW_HIGLIGHT)
+ {
+ /* Normal */
+ if (m_sprite_renderline[x+128] & 0x80)
+ {
+ m_video_renderline[x] = m_sprite_renderline[x+128]&0x3f;
+ m_video_renderline[x] |= 0x10000; // mark as sprite pixel
+ }
+ }
+ else
+ {
+ if (m_sprite_renderline[x+128] & 0x80)
+ {
+ UINT8 spritedata;
+ spritedata = m_sprite_renderline[x+128]&0x3f;
+
+ if (spritedata==0x3e)
+ {
+ /* set flag 0x8000 to indicate highlight */
+ m_video_renderline[x] = m_video_renderline[x]|0x8000;
+ }
+ else if (spritedata==0x3f)
+ {
+ /* This is a Shadow operator set shadow bit */
+ m_video_renderline[x] = m_video_renderline[x]|0x2000;
+ }
+ else
+ {
+ m_video_renderline[x] = spritedata | 0x4000;
+ m_video_renderline[x] |= 0x10000; // mark as sprite pixel
+ }
+ }
+ }
+ }
+}
+
+
+/* This converts our render buffer to real screen colours */
+void sega315_5313_device::render_videobuffer_to_screenbuffer(int scanline)
+{
+ UINT16 *lineptr;
+
+
+
+ if (!m_use_alt_timing)
+ {
+ if (scanline >= m_render_bitmap->height()) // safety, shouldn't happen now we allocate a fixed amount tho
+ return;
+
+ lineptr = &m_render_bitmap->pix16(scanline);
+
+ }
+ else
+ lineptr = m_render_line;
+
+ for (int x = 0; x < 320; x++)
+ {
+ UINT32 dat = m_video_renderline[x];
+
+ if (!(dat & 0x20000))
+ m_render_line_raw[x] = 0x100;
+ else
+ m_render_line_raw[x] = 0x000;
+
+
+ if (!MEGADRIVE_REG0C_SHADOW_HIGLIGHT)
+ {
+ if (dat & 0x10000)
+ {
+ lineptr[x] = m_palette_lookup_sprite[(dat & 0x3f)];
+ m_render_line_raw[x] |= (dat & 0x3f) | 0x080;
+ }
+ else
+ {
+ lineptr[x] = m_palette_lookup[(dat & 0x3f)];
+ m_render_line_raw[x] |= (dat & 0x3f) | 0x040;
+ }
+
+ }
+ else
+ {
+ /* Verify my handling.. I'm not sure all cases are correct */
+ switch (dat & 0x1e000)
+ {
+ case 0x00000: // low priority, no shadow sprite, no highlight = shadow
+ case 0x02000: // low priority, shadow sprite, no highlight = shadow
+ case 0x06000: // normal pri, shadow sprite, no highlight = shadow?
+ case 0x10000: // (sprite) low priority, no shadow sprite, no highlight = shadow
+ case 0x12000: // (sprite) low priority, shadow sprite, no highlight = shadow
+ case 0x16000: // (sprite) normal pri, shadow sprite, no highlight = shadow?
+ lineptr[x] = m_palette_lookup_shadow[(dat & 0x3f)];
+ m_render_line_raw[x] |= (dat & 0x3f) | 0x000;
+ break;
+
+ case 0x4000: // normal pri, no shadow sprite, no highlight = normal;
+ case 0x8000: // low pri, highlight sprite = normal;
+ lineptr[x] = m_palette_lookup[(dat & 0x3f)];
+ m_render_line_raw[x] |= (dat & 0x3f) | 0x040;
+ break;
+
+ case 0x14000: // (sprite) normal pri, no shadow sprite, no highlight = normal;
+ case 0x18000: // (sprite) low pri, highlight sprite = normal;
+ lineptr[x] = m_palette_lookup_sprite[(dat & 0x3f)];
+ m_render_line_raw[x] |= (dat & 0x3f) | 0x080;
+ break;
+
+
+ case 0x0c000: // normal pri, highlight set = highlight?
+ case 0x1c000: // (sprite) normal pri, highlight set = highlight?
+ lineptr[x] = m_palette_lookup_highlight[(dat & 0x3f)];
+ m_render_line_raw[x] |= (dat & 0x3f) | 0x0c0;
+ break;
+
+ case 0x0a000: // shadow set, highlight set - not possible
+ case 0x0e000: // shadow set, highlight set, normal set, not possible
+ case 0x1a000: // (sprite)shadow set, highlight set - not possible
+ case 0x1e000: // (sprite)shadow set, highlight set, normal set, not possible
+ default:
+ lineptr[x] = m_render_line_raw[x] |= (machine().rand() & 0x3f);
+ break;
+ }
+ }
+ }
+
+ if (!m_32x_scanline_helper_func.isnull())
+ m_32x_scanline_helper_func(scanline);
+ if (!m_32x_scanline_func.isnull())
+ {
+ for (int x = 0; x < 320; x++)
+ m_32x_scanline_func(x, m_video_renderline[x] & 0x20000, lineptr[x]);
+ }
+}
+
+void sega315_5313_device::render_scanline()
+{
+ int scanline = get_scanline_counter();
+
+ if (scanline >= 0 && scanline < m_visible_scanlines)
+ {
+ //if (MEGADRIVE_REG01_DMA_ENABLE==0) osd_printf_debug("off\n");
+ render_spriteline_to_spritebuffer(get_scanline_counter());
+ render_videoline_to_videobuffer(scanline);
+ render_videobuffer_to_screenbuffer(scanline);
+ }
+}
+
+void sega315_5313_device::vdp_handle_scanline_callback(int scanline)
+{
+/* Compensate for some rounding errors
+
+ When the counter reaches 261 we should have reached the end of the frame, however due
+ to rounding errors in the timer calculation we're not quite there. Let's assume we are
+ still in the previous scanline for now.
+ */
+
+ if (get_scanline_counter() != (m_total_scanlines - 1))
+ {
+ if (!m_use_alt_timing) m_scanline_counter++;
+// osd_printf_debug("scanline %d\n",get_scanline_counter());
+ m_render_timer->adjust(attotime::from_usec(1));
+
+ if (get_scanline_counter() == m_irq6_scanline)
+ {
+ // osd_printf_debug("x %d",get_scanline_counter());
+ m_irq6_on_timer->adjust(attotime::from_usec(6));
+ m_irq6_pending = 1;
+ m_vblank_flag = 1;
+
+ }
+
+ // if (get_scanline_counter()==0) m_irq4counter = MEGADRIVE_REG0A_HINT_VALUE;
+ // m_irq4counter = MEGADRIVE_REG0A_HINT_VALUE;
+
+ if (get_scanline_counter()<=224)
+ {
+ m_irq4counter--;
+
+ if (m_irq4counter==-1)
+ {
+ if (m_imode == 3) m_irq4counter = MEGADRIVE_REG0A_HINT_VALUE*2;
+ else m_irq4counter=MEGADRIVE_REG0A_HINT_VALUE;
+
+ m_irq4_pending = 1;
+
+ if (MEGADRIVE_REG0_IRQ4_ENABLE)
+ {
+ m_irq4_on_timer->adjust(attotime::from_usec(1));
+ //osd_printf_debug("irq4 on scanline %d reload %d\n",get_scanline_counter(),MEGADRIVE_REG0A_HINT_VALUE);
+ }
+ }
+ }
+ else
+ {
+ if (m_imode == 3) m_irq4counter = MEGADRIVE_REG0A_HINT_VALUE*2;
+ else m_irq4counter=MEGADRIVE_REG0A_HINT_VALUE;
+ }
+
+ //if (get_scanline_counter()==0) irq4_on_timer->adjust(attotime::from_usec(2));
+
+
+ if (get_scanline_counter() == m_z80irq_scanline)
+ {
+ m_sndirqline_callback(true);
+ }
+ if (get_scanline_counter() == m_z80irq_scanline + 1)
+ {
+ m_sndirqline_callback(false);
+ }
+ }
+ else /* pretend we're still on the same scanline to compensate for rounding errors */
+ {
+ if (!m_use_alt_timing) m_scanline_counter = m_total_scanlines - 1;
+ }
+
+ // 32x interrupts!
+ if (!m_32x_interrupt_func.isnull())
+ m_32x_interrupt_func(get_scanline_counter(), m_irq6_scanline);
+}
+
+
+void sega315_5313_device::vdp_handle_eof()
+{
+ rectangle visarea;
+ int scr_width = 320;
+
+ m_vblank_flag = 0;
+ //m_irq6_pending = 0; /* NO! (breaks warlock) */
+
+ /* Set it to -1 here, so it becomes 0 when the first timer kicks in */
+ if (!m_use_alt_timing) m_scanline_counter = -1;
+ m_sprite_collision=0;//? when to reset this ..
+ m_imode = MEGADRIVE_REG0C_INTERLEAVE; // can't change mid-frame..
+ m_imode_odd_frame^=1;
+// m_genesis_snd_z80->set_input_line(0, CLEAR_LINE); // if the z80 interrupt hasn't happened by now, clear it..
+
+ if (MEGADRIVE_REG01_240_LINE)
+ {
+ /* this is invalid in PAL! */
+ m_total_scanlines = m_base_total_scanlines;
+ m_visible_scanlines = 240;
+ m_irq6_scanline = 240;
+ m_z80irq_scanline = 240;
+ }
+ else
+ {
+ m_total_scanlines = m_base_total_scanlines;
+ m_visible_scanlines = 224;
+ m_irq6_scanline = 224;
+ m_z80irq_scanline = 224;
+ }
+
+ if (m_imode == 3)
+ {
+ m_total_scanlines <<= 1;
+ m_visible_scanlines <<= 1;
+ m_irq6_scanline <<= 1;
+ m_z80irq_scanline <<= 1;
+ }
+
+
+ switch (MEGADRIVE_REG0C_RS0 | (MEGADRIVE_REG0C_RS1 << 1))
+ {
+ /* note, add 240 mode + init new timings! */
+ case 0:scr_width = 256;break;
+ case 1:scr_width = 256;break;
+ case 2:scr_width = 320;break;
+ case 3:scr_width = 320;break;
+ }
+// osd_printf_debug("my mode %02x", m_regs[0x0c]);
+
+ visarea.set(0, scr_width - 1, 0, m_visible_scanlines - 1);
+
+ m_screen->configure(480, m_total_scanlines, visarea, m_screen->frame_period().attoseconds());
+}
+
+
+// called at the start of each scanline
+TIMER_DEVICE_CALLBACK_MEMBER( sega315_5313_device::megadriv_scanline_timer_callback )
+{
+ if (!m_use_alt_timing)
+ {
+ machine().scheduler().synchronize();
+ vdp_handle_scanline_callback(param);
+
+ m_megadriv_scanline_timer->adjust(attotime::from_hz(get_framerate()) / m_total_scanlines);
+ }
+ else
+ {
+ vdp_handle_scanline_callback(param);
+ }
+}
+
+TIMER_DEVICE_CALLBACK_MEMBER( sega315_5313_device::megadriv_scanline_timer_callback_alt_timing )
+{
+ if (m_use_alt_timing)
+ {
+ if (param==0)
+ {
+ //printf("where are we? %d %d\n", m_screen->vpos(), screen().hpos());
+ vdp_handle_eof();
+ //vdp_clear_bitmap();
+ }
+
+
+ vdp_handle_scanline_callback(param);
+
+ int vpos = screen().vpos();
+ if (vpos > 0)
+ screen().update_partial(vpos-1);
+ }
+}
diff --git a/src/devices/video/315_5313.h b/src/devices/video/315_5313.h
new file mode 100644
index 00000000000..8bcf91a6d5f
--- /dev/null
+++ b/src/devices/video/315_5313.h
@@ -0,0 +1,358 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+/* Sega Megadrive / Genesis VDP */
+
+#pragma once
+
+#include "video/315_5124.h"
+#include "cpu/m68000/m68000.h"
+
+
+/* The VDP occupies addresses C00000h to C0001Fh.
+
+ C00000h - Data port (8=r/w, 16=r/w)
+ C00002h - Data port (mirror)
+ C00004h - Control port (8=r/w, 16=r/w)
+ C00006h - Control port (mirror)
+ C00008h - HV counter (8/16=r/o)
+ C0000Ah - HV counter (mirror)
+ C0000Ch - HV counter (mirror)
+ C0000Eh - HV counter (mirror)
+ C00011h - SN76489 PSG (8=w/o)
+ C00013h - SN76489 PSG (mirror)
+ C00015h - SN76489 PSG (mirror)
+ C00017h - SN76489 PSG (mirror)
+*/
+
+#define MEGADRIV_VDP_VRAM(address) m_vram[(address)&0x7fff]
+
+
+
+/*
+
+ $00 - Mode Set Register No. 1
+ -----------------------------
+
+ d7 - No effect
+ d6 - No effect
+ d5 - No effect
+ d4 - IE1 (Horizontal interrupt enable)
+ d3 - 1= Invalid display setting
+ d2 - Palette select
+ d1 - M3 (HV counter latch enable)
+ d0 - Display disable
+
+ */
+
+#define MEGADRIVE_REG0_UNUSED ((m_regs[0x00]&0xc0)>>6)
+#define MEGADRIVE_REG0_BLANK_LEFT ((m_regs[0x00]&0x20)>>5) // like SMS, not used by any commercial games?
+#define MEGADRIVE_REG0_IRQ4_ENABLE ((m_regs[0x00]&0x10)>>4)
+#define MEGADRIVE_REG0_INVALID_MODE ((m_regs[0x00]&0x08)>>3) // invalid display mode, unhandled
+#define MEGADRIVE_REG0_SPECIAL_PAL ((m_regs[0x00]&0x04)>>2) // strange palette mode, unhandled
+#define MEGADRIVE_REG0_HVLATCH_ENABLE ((m_regs[0x00]&0x02)>>1) // HV Latch, used by lightgun games
+#define MEGADRIVE_REG0_DISPLAY_DISABLE ((m_regs[0x00]&0x01)>>0)
+
+/*
+
+ $01 - Mode Set Register No. 2
+ -----------------------------
+
+ d7 - TMS9918 / Genesis display select
+ d6 - DISP (Display Enable)
+ d5 - IE0 (Vertical Interrupt Enable)
+ d4 - M1 (DMA Enable)
+ d3 - M2 (PAL / NTSC)
+ d2 - SMS / Genesis display select
+ d1 - 0 (No effect)
+ d0 - 0 (See notes)
+
+*/
+
+#define MEGADRIVE_REG01_TMS9918_SELECT ((m_regs[0x01]&0x80)>>7)
+#define MEGADRIVE_REG01_DISP_ENABLE ((m_regs[0x01]&0x40)>>6)
+#define MEGADRIVE_REG01_IRQ6_ENABLE ((m_regs[0x01]&0x20)>>5)
+#define MEGADRIVE_REG01_DMA_ENABLE ((m_regs[0x01]&0x10)>>4)
+#define MEGADRIVE_REG01_240_LINE ((m_regs[0x01]&0x08)>>3)
+#define MEGADRIVE_REG01_SMS_SELECT ((m_regs[0x01]&0x04)>>2)
+#define MEGADRIVE_REG01_UNUSED ((m_regs[0x01]&0x02)>>1)
+#define MEGADRIVE_REG01_STRANGE_VIDEO ((m_regs[0x01]&0x01)>>0) // unhandled, does strange things to the display
+
+#define MEGADRIVE_REG02_UNUSED1 ((m_regs[0x02]&0xc0)>>6)
+#define MEGADRIVE_REG02_PATTERN_ADDR_A ((m_regs[0x02]&0x38)>>3)
+#define MEGADRIVE_REG02_UNUSED2 ((m_regs[0x02]&0x07)>>0)
+
+#define MEGADRIVE_REG03_UNUSED1 ((m_regs[0x03]&0xc0)>>6)
+#define MEGADRIVE_REG03_PATTERN_ADDR_W ((m_regs[0x03]&0x3e)>>1)
+#define MEGADRIVE_REG03_UNUSED2 ((m_regs[0x03]&0x01)>>0)
+
+#define MEGADRIVE_REG04_UNUSED ((m_regs[0x04]&0xf8)>>3)
+#define MEGADRIVE_REG04_PATTERN_ADDR_B ((m_regs[0x04]&0x07)>>0)
+
+#define MEGADRIVE_REG05_UNUSED ((m_regs[0x05]&0x80)>>7)
+#define MEGADRIVE_REG05_SPRITE_ADDR ((m_regs[0x05]&0x7f)>>0)
+
+/* 6? */
+
+#define MEGADRIVE_REG07_UNUSED ((m_regs[0x07]&0xc0)>>6)
+#define MEGADRIVE_REG07_BGCOLOUR ((m_regs[0x07]&0x3f)>>0)
+
+/* 8? */
+/* 9? */
+
+#define MEGADRIVE_REG0A_HINT_VALUE ((m_regs[0x0a]&0xff)>>0)
+
+#define MEGADRIVE_REG0B_UNUSED ((m_regs[0x0b]&0xf0)>>4)
+#define MEGADRIVE_REG0B_IRQ2_ENABLE ((m_regs[0x0b]&0x08)>>3)
+#define MEGADRIVE_REG0B_VSCROLL_MODE ((m_regs[0x0b]&0x04)>>2)
+#define MEGADRIVE_REG0B_HSCROLL_MODE ((m_regs[0x0b]&0x03)>>0)
+
+#define MEGADRIVE_REG0C_RS0 ((m_regs[0x0c]&0x80)>>7)
+#define MEGADRIVE_REG0C_UNUSED1 ((m_regs[0x0c]&0x40)>>6)
+#define MEGADRIVE_REG0C_SPECIAL ((m_regs[0x0c]&0x20)>>5)
+#define MEGADRIVE_REG0C_UNUSED2 ((m_regs[0x0c]&0x10)>>4)
+#define MEGADRIVE_REG0C_SHADOW_HIGLIGHT ((m_regs[0x0c]&0x08)>>3)
+#define MEGADRIVE_REG0C_INTERLEAVE ((m_regs[0x0c]&0x06)>>1)
+#define MEGADRIVE_REG0C_RS1 ((m_regs[0x0c]&0x01)>>0)
+
+#define MEGADRIVE_REG0D_UNUSED ((m_regs[0x0d]&0xc0)>>6)
+#define MEGADRIVE_REG0D_HSCROLL_ADDR ((m_regs[0x0d]&0x3f)>>0)
+
+/* e? */
+
+#define MEGADRIVE_REG0F_AUTO_INC ((m_regs[0x0f]&0xff)>>0)
+
+#define MEGADRIVE_REG10_UNUSED1 ((m_regs[0x10]&0xc0)>>6)
+#define MEGADRIVE_REG10_VSCROLL_SIZE ((m_regs[0x10]&0x30)>>4)
+#define MEGADRIVE_REG10_UNUSED2 ((m_regs[0x10]&0x0c)>>2)
+#define MEGADRIVE_REG10_HSCROLL_SIZE ((m_regs[0x10]&0x03)>>0)
+
+#define MEGADRIVE_REG11_WINDOW_RIGHT ((m_regs[0x11]&0x80)>>7)
+#define MEGADRIVE_REG11_UNUSED ((m_regs[0x11]&0x60)>>5)
+#define MEGADRIVE_REG11_WINDOW_HPOS ((m_regs[0x11]&0x1f)>>0)
+
+#define MEGADRIVE_REG12_WINDOW_DOWN ((m_regs[0x12]&0x80)>>7)
+#define MEGADRIVE_REG12_UNUSED ((m_regs[0x12]&0x60)>>5)
+#define MEGADRIVE_REG12_WINDOW_VPOS ((m_regs[0x12]&0x1f)>>0)
+
+#define MEGADRIVE_REG13_DMALENGTH1 ((m_regs[0x13]&0xff)>>0)
+
+#define MEGADRIVE_REG14_DMALENGTH2 ((m_regs[0x14]&0xff)>>0)
+
+#define MEGADRIVE_REG15_DMASOURCE1 ((m_regs[0x15]&0xff)>>0)
+#define MEGADRIVE_REG16_DMASOURCE2 ((m_regs[0x16]&0xff)>>0)
+
+#define MEGADRIVE_REG17_DMASOURCE3 ((m_regs[0x17]&0xff)>>0)
+#define MEGADRIVE_REG17_DMATYPE ((m_regs[0x17]&0xc0)>>6)
+#define MEGADRIVE_REG17_UNUSED ((m_regs[0x17]&0x3f)>>0)
+
+
+#define MCFG_SEGA315_5313_IS_PAL(_bool) \
+ sega315_5313_device::set_signal_type(*device, _bool);
+
+#define MCFG_SEGA315_5313_INT_CB(_devcb) \
+ devcb = &sega315_5313_device::set_int_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SEGA315_5313_PAUSE_CB(_devcb) \
+ devcb = &sega315_5313_device::set_pause_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SEGA315_5313_SND_IRQ_CALLBACK(_write) \
+ devcb = &sega315_5313_device::set_sndirqline_callback(*device, DEVCB_##_write);
+
+#define MCFG_SEGA315_5313_LV6_IRQ_CALLBACK(_write) \
+ devcb = &sega315_5313_device::set_lv6irqline_callback(*device, DEVCB_##_write);
+
+#define MCFG_SEGA315_5313_LV4_IRQ_CALLBACK(_write) \
+ devcb = &sega315_5313_device::set_lv4irqline_callback(*device, DEVCB_##_write);
+
+#define MCFG_SEGA315_5313_ALT_TIMING(_data) \
+ sega315_5313_device::set_alt_timing(*device, _data);
+
+#define MCFG_SEGA315_5313_PAL_WRITE_BASE(_data) \
+ sega315_5313_device::set_palwrite_base(*device, _data);
+
+#define MCFG_SEGA315_5313_PALETTE(_palette_tag) \
+ sega315_5313_device::static_set_palette_tag(*device, "^" _palette_tag);
+
+
+// Temporary solution while 32x VDP mixing and scanline interrupting is moved outside MD VDP
+typedef device_delegate<void (int x, UINT32 priority, UINT16 &lineptr)> md_32x_scanline_delegate;
+typedef device_delegate<void (int scanline, int irq6)> md_32x_interrupt_delegate;
+typedef device_delegate<void (int scanline)> md_32x_scanline_helper_delegate;
+
+#define MCFG_SEGA315_5313_32X_SCANLINE_CB(_class, _method) \
+ sega315_5313_device::set_md_32x_scanline(*device, md_32x_scanline_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_SEGA315_5313_32X_INTERRUPT_CB(_class, _method) \
+ sega315_5313_device::set_md_32x_interrupt(*device, md_32x_interrupt_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_SEGA315_5313_32X_SCANLINE_HELPER_CB(_class, _method) \
+ sega315_5313_device::set_md_32x_scanline_helper(*device, md_32x_scanline_helper_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+
+class sega315_5313_device : public sega315_5124_device
+{
+public:
+ sega315_5313_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_sndirqline_callback(device_t &device, _Object object) { return downcast<sega315_5313_device &>(device).m_sndirqline_callback.set_callback(object); }
+ template<class _Object> static devcb_base &set_lv6irqline_callback(device_t &device, _Object object) { return downcast<sega315_5313_device &>(device).m_lv6irqline_callback.set_callback(object); }
+ template<class _Object> static devcb_base &set_lv4irqline_callback(device_t &device, _Object object) { return downcast<sega315_5313_device &>(device).m_lv4irqline_callback.set_callback(object); }
+ static void set_alt_timing(device_t &device, int use_alt_timing);
+ static void set_palwrite_base(device_t &device, int palwrite_base);
+ static void static_set_palette_tag(device_t &device, const char *tag);
+
+ static void set_md_32x_scanline(device_t &device, md_32x_scanline_delegate callback) { downcast<sega315_5313_device &>(device).m_32x_scanline_func = callback; }
+ static void set_md_32x_interrupt(device_t &device, md_32x_interrupt_delegate callback) { downcast<sega315_5313_device &>(device).m_32x_interrupt_func = callback; }
+ static void set_md_32x_scanline_helper(device_t &device, md_32x_scanline_helper_delegate callback) { downcast<sega315_5313_device &>(device).m_32x_scanline_helper_func = callback; }
+
+ int m_use_alt_timing; // use MAME scanline timer instead, render only one scanline to a single line buffer, to be rendered by a partial update call.. experimental
+
+ int m_palwrite_base; // if we want to write to the actual MAME palette..
+
+ DECLARE_READ16_MEMBER( vdp_r );
+ DECLARE_WRITE16_MEMBER( vdp_w );
+
+ int get_scanline_counter();
+
+ void render_scanline();
+ void vdp_handle_scanline_callback(int scanline);
+ void vdp_handle_irq6_on_timer_callback(int param);
+ void vdp_handle_irq4_on_timer_callback(int param);
+ void vdp_handle_eof();
+ void device_reset_old();
+ void vdp_clear_irq6_pending(void) { m_irq6_pending = 0; };
+ void vdp_clear_irq4_pending(void) { m_irq4_pending = 0; };
+
+ // set some VDP variables at start (shall be moved to a device interface?)
+ void set_scanline_counter(int scanline) { m_scanline_counter = scanline; }
+ void set_total_scanlines(int total) { m_base_total_scanlines = total; }
+ void set_framerate(int rate) { m_framerate = rate; }
+ void set_vdp_pal(bool pal) { m_vdp_pal = pal ? 1 : 0; }
+ void set_use_cram(int cram) { m_use_cram = cram; }
+ void set_dma_delay(int delay) { m_dma_delay = delay; }
+ int get_framerate() { return m_framerate; }
+ int get_imode() { return m_imode; }
+
+
+ void vdp_clear_bitmap(void)
+ {
+ if (m_render_bitmap)
+ m_render_bitmap->fill(0);
+ }
+
+ bitmap_ind16* m_render_bitmap;
+ UINT16* m_render_line;
+ UINT16* m_render_line_raw;
+
+ TIMER_DEVICE_CALLBACK_MEMBER( megadriv_scanline_timer_callback_alt_timing );
+ TIMER_DEVICE_CALLBACK_MEMBER( megadriv_scanline_timer_callback );
+ timer_device* m_megadriv_scanline_timer;
+
+ inline UINT16 vdp_get_word_from_68k_mem(UINT32 source);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ // called when we hit 240 and 241 (used to control the z80 irq line on genesis, or the main irq on c2)
+ devcb_write_line m_sndirqline_callback;
+ devcb_write_line m_lv6irqline_callback;
+ devcb_write_line m_lv4irqline_callback;
+
+ md_32x_scanline_delegate m_32x_scanline_func;
+ md_32x_interrupt_delegate m_32x_interrupt_func;
+ md_32x_scanline_helper_delegate m_32x_scanline_helper_func;
+
+private:
+
+ int m_command_pending; // 2nd half of command pending..
+ UINT16 m_command_part1;
+ UINT16 m_command_part2;
+ UINT8 m_vdp_code;
+ UINT16 m_vdp_address;
+ UINT8 m_vram_fill_pending;
+ UINT16 m_vram_fill_length;
+ int m_irq4counter;
+ int m_imode_odd_frame;
+ int m_sprite_collision;
+ int m_irq6_pending;
+ int m_irq4_pending;
+ int m_scanline_counter;
+ int m_vblank_flag;
+
+ int m_imode;
+
+ int m_visible_scanlines;
+ int m_irq6_scanline;
+ int m_z80irq_scanline;
+ int m_total_scanlines;
+ // this is only set at init: 262 for PAL, 313 for NTSC
+ int m_base_total_scanlines;
+
+ int m_framerate;
+ int m_vdp_pal;
+ int m_use_cram; // c2 uses it's own palette ram, so it sets this to 0
+ int m_dma_delay; // SVP and SegaCD have some 'lag' in DMA transfers
+
+ UINT16* m_regs;
+ UINT16* m_vram;
+ UINT16* m_cram;
+ UINT16* m_vsram;
+ /* The VDP keeps a 0x400 byte on-chip cache of the Sprite Attribute Table
+ to speed up processing, Castlevania Bloodlines abuses this on the upside down level */
+ UINT16* m_internal_sprite_attribute_table;
+
+ // these are used internally by the VDP to schedule when after the start of a scanline
+ // to trigger the various interrupts / rendering to our bitmap, bit of a hack really
+ emu_timer* m_irq6_on_timer;
+ emu_timer* m_irq4_on_timer;
+ emu_timer* m_render_timer;
+
+ UINT16 vdp_vram_r(void);
+ UINT16 vdp_vsram_r(void);
+ UINT16 vdp_cram_r(void);
+
+ void insta_68k_to_cram_dma(UINT32 source,UINT16 length);
+ void insta_68k_to_vsram_dma(UINT32 source,UINT16 length);
+ void insta_68k_to_vram_dma(UINT32 source,int length);
+ void insta_vram_copy(UINT32 source, UINT16 length);
+
+ void vdp_vram_write(UINT16 data);
+ void vdp_cram_write(UINT16 data);
+ void write_cram_value(int offset, int data);
+ void vdp_vsram_write(UINT16 data);
+
+ void vdp_set_register(int regnum, UINT8 value);
+
+ void handle_dma_bits();
+
+ UINT16 get_hposition();
+ UINT16 megadriv_read_hv_counters();
+
+ UINT16 ctrl_port_r();
+ UINT16 data_port_r();
+ void data_port_w(int data);
+ void ctrl_port_w(int data);
+ void update_code_and_address(void);
+
+
+ void render_spriteline_to_spritebuffer(int scanline);
+ void render_videoline_to_videobuffer(int scanline);
+ void render_videobuffer_to_screenbuffer(int scanline);
+
+ /* variables used during emulation - not saved */
+ UINT8* m_sprite_renderline;
+ UINT8* m_highpri_renderline;
+ UINT32* m_video_renderline;
+ UINT16* m_palette_lookup;
+ UINT16* m_palette_lookup_sprite; // for C2
+ UINT16* m_palette_lookup_shadow;
+ UINT16* m_palette_lookup_highlight;
+
+ address_space *m_space68k;
+ m68000_base_device* m_cpu68k;
+};
+
+
+extern const device_type SEGA315_5313;
diff --git a/src/devices/video/bufsprite.c b/src/devices/video/bufsprite.c
new file mode 100644
index 00000000000..bc9717d8ef7
--- /dev/null
+++ b/src/devices/video/bufsprite.c
@@ -0,0 +1,23 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*********************************************************************
+
+ bufsprite.h
+
+ Buffered Sprite RAM device.
+
+*********************************************************************/
+
+#include "emu.h"
+#include "bufsprite.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+extern const device_type BUFFERED_SPRITERAM8 = &device_creator<buffered_spriteram8_device>;
+extern const device_type BUFFERED_SPRITERAM16 = &device_creator<buffered_spriteram16_device>;
+extern const device_type BUFFERED_SPRITERAM32 = &device_creator<buffered_spriteram32_device>;
+extern const device_type BUFFERED_SPRITERAM64 = &device_creator<buffered_spriteram64_device>;
diff --git a/src/devices/video/bufsprite.h b/src/devices/video/bufsprite.h
new file mode 100644
index 00000000000..86fb699085a
--- /dev/null
+++ b/src/devices/video/bufsprite.h
@@ -0,0 +1,143 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*********************************************************************
+
+ bufsprite.h
+
+ Buffered Sprite RAM device.
+
+*********************************************************************/
+
+#pragma once
+
+#ifndef __BUFSPRITE_H__
+#define __BUFSPRITE_H__
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+extern const device_type BUFFERED_SPRITERAM8;
+extern const device_type BUFFERED_SPRITERAM16;
+extern const device_type BUFFERED_SPRITERAM32;
+extern const device_type BUFFERED_SPRITERAM64;
+
+
+
+//**************************************************************************
+// DEVICE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_BUFFERED_SPRITERAM8_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, BUFFERED_SPRITERAM8, 0)
+#define MCFG_BUFFERED_SPRITERAM16_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, BUFFERED_SPRITERAM16, 0)
+#define MCFG_BUFFERED_SPRITERAM32_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, BUFFERED_SPRITERAM32, 0)
+#define MCFG_BUFFERED_SPRITERAM64_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, BUFFERED_SPRITERAM64, 0)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> buffered_spriteram_device
+
+// base class to manage buffered spriteram
+template<typename _Type>
+class buffered_spriteram_device : public device_t
+{
+public:
+ // construction
+ buffered_spriteram_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, type, "Buffered Sprite RAM", tag, owner, clock, "buffered_spriteram", __FILE__),
+ m_spriteram(*owner, tag) { }
+
+ // getters
+ _Type *live() const { return m_spriteram; }
+ _Type *buffer() { return &m_buffered[0]; }
+ UINT32 bytes() const { return m_spriteram.bytes(); }
+
+ // operations
+ _Type *copy(UINT32 srcoffset = 0, UINT32 srclength = 0x7fffffff)
+ {
+ assert(m_spriteram != NULL);
+ if (m_spriteram != NULL)
+ memcpy(&m_buffered[0], m_spriteram + srcoffset, MIN(srclength, m_spriteram.bytes() / sizeof(_Type) - srcoffset) * sizeof(_Type));
+ return &m_buffered[0];
+ }
+
+ // read/write handlers
+ void write(address_space &space, offs_t offset, _Type data, _Type mem_mask = ~_Type(0)) { copy(); }
+
+ // VBLANK handlers
+ void vblank_copy_rising(screen_device &screen, bool state) { if (state) copy(); }
+ void vblank_copy_falling(screen_device &screen, bool state) { if (!state) copy(); }
+
+protected:
+ // first-time setup
+ virtual void device_start()
+ {
+ if (m_spriteram != NULL)
+ {
+ m_buffered.resize(m_spriteram.bytes() / sizeof(_Type));
+ save_item(NAME(m_buffered));
+ }
+ }
+
+private:
+ // internal state
+ required_shared_ptr<_Type> m_spriteram;
+ std::vector<_Type> m_buffered;
+};
+
+
+// ======================> buffered_spriteram8_device
+
+class buffered_spriteram8_device : public buffered_spriteram_device<UINT8>
+{
+public:
+ // construction
+ buffered_spriteram8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : buffered_spriteram_device<UINT8>(mconfig, BUFFERED_SPRITERAM8, tag, owner, clock) { }
+};
+
+
+// ======================> buffered_spriteram16_device
+
+class buffered_spriteram16_device : public buffered_spriteram_device<UINT16>
+{
+public:
+ // construction
+ buffered_spriteram16_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : buffered_spriteram_device<UINT16>(mconfig, BUFFERED_SPRITERAM16, tag, owner, clock) { }
+};
+
+
+// ======================> buffered_spriteram32_device
+
+class buffered_spriteram32_device : public buffered_spriteram_device<UINT32>
+{
+public:
+ // construction
+ buffered_spriteram32_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : buffered_spriteram_device<UINT32>(mconfig, BUFFERED_SPRITERAM32, tag, owner, clock) { }
+};
+
+
+// ======================> buffered_spriteram64_device
+
+class buffered_spriteram64_device : public buffered_spriteram_device<UINT64>
+{
+public:
+ // construction
+ buffered_spriteram64_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : buffered_spriteram_device<UINT64>(mconfig, BUFFERED_SPRITERAM64, tag, owner, clock) { }
+};
+
+
+#endif /* __BUFSPRITE_H__ */
diff --git a/src/devices/video/cdp1861.c b/src/devices/video/cdp1861.c
new file mode 100644
index 00000000000..a02309decca
--- /dev/null
+++ b/src/devices/video/cdp1861.c
@@ -0,0 +1,253 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1861 Video Display Controller emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "cdp1861.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define CDP1861_CYCLES_DMA_START 2*8
+#define CDP1861_CYCLES_DMA_ACTIVE 8*8
+#define CDP1861_CYCLES_DMA_WAIT 6*8
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// device type definition
+const device_type CDP1861 = &device_creator<cdp1861_device>;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// cdp1861_device - constructor
+//-------------------------------------------------
+
+cdp1861_device::cdp1861_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CDP1861, "CDP1861", tag, owner, clock, "cdp1861", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_write_irq(*this),
+ m_write_dma_out(*this),
+ m_write_efx(*this),
+ m_disp(0),
+ m_dispon(0),
+ m_dispoff(0),
+ m_dmaout(CLEAR_LINE)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cdp1861_device::device_start()
+{
+ // resolve callbacks
+ m_write_irq.resolve_safe();
+ m_write_dma_out.resolve_safe();
+ m_write_efx.resolve_safe();
+
+ // allocate timers
+ m_int_timer = timer_alloc(TIMER_INT);
+ m_efx_timer = timer_alloc(TIMER_EFX);
+ m_dma_timer = timer_alloc(TIMER_DMA);
+
+ // find devices
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // register for state saving
+ save_item(NAME(m_disp));
+ save_item(NAME(m_dispon));
+ save_item(NAME(m_dispoff));
+ save_item(NAME(m_dmaout));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void cdp1861_device::device_reset()
+{
+ m_int_timer->adjust(m_screen->time_until_pos(CDP1861_SCANLINE_INT_START, 0));
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1861_SCANLINE_EFX_TOP_START, 0));
+ m_dma_timer->adjust(clocks_to_attotime(CDP1861_CYCLES_DMA_START));
+
+ m_disp = 0;
+ m_dmaout = 0;
+ m_dispon = 0;
+
+ m_write_irq(CLEAR_LINE);
+ m_write_dma_out(CLEAR_LINE);
+ m_write_efx(CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// device_timer - handle timer events
+//-------------------------------------------------
+
+void cdp1861_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int scanline = m_screen->vpos();
+
+ switch (id)
+ {
+ case TIMER_INT:
+ if (scanline == CDP1861_SCANLINE_INT_START)
+ {
+ if (m_disp)
+ {
+ m_write_irq(ASSERT_LINE);
+ }
+
+ m_int_timer->adjust(m_screen->time_until_pos( CDP1861_SCANLINE_INT_END, 0));
+ }
+ else
+ {
+ if (m_disp)
+ {
+ m_write_irq(CLEAR_LINE);
+ }
+
+ m_int_timer->adjust(m_screen->time_until_pos(CDP1861_SCANLINE_INT_START, 0));
+ }
+ break;
+
+ case TIMER_EFX:
+ switch (scanline)
+ {
+ case CDP1861_SCANLINE_EFX_TOP_START:
+ m_write_efx(ASSERT_LINE);
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1861_SCANLINE_EFX_TOP_END, 0));
+ break;
+
+ case CDP1861_SCANLINE_EFX_TOP_END:
+ m_write_efx(CLEAR_LINE);
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1861_SCANLINE_EFX_BOTTOM_START, 0));
+ break;
+
+ case CDP1861_SCANLINE_EFX_BOTTOM_START:
+ m_write_efx(ASSERT_LINE);
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1861_SCANLINE_EFX_BOTTOM_END, 0));
+ break;
+
+ case CDP1861_SCANLINE_EFX_BOTTOM_END:
+ m_write_efx(CLEAR_LINE);
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1861_SCANLINE_EFX_TOP_START, 0));
+ break;
+ }
+ break;
+
+ case TIMER_DMA:
+ if (m_dmaout)
+ {
+ if (m_disp)
+ {
+ if (scanline >= CDP1861_SCANLINE_DISPLAY_START && scanline < CDP1861_SCANLINE_DISPLAY_END)
+ {
+ m_write_dma_out(CLEAR_LINE);
+ }
+ }
+
+ m_dma_timer->adjust(clocks_to_attotime(CDP1861_CYCLES_DMA_WAIT));
+
+ m_dmaout = CLEAR_LINE;
+ }
+ else
+ {
+ if (m_disp)
+ {
+ if (scanline >= CDP1861_SCANLINE_DISPLAY_START && scanline < CDP1861_SCANLINE_DISPLAY_END)
+ {
+ m_write_dma_out(ASSERT_LINE);
+ }
+ }
+
+ m_dma_timer->adjust(clocks_to_attotime(CDP1861_CYCLES_DMA_ACTIVE));
+
+ m_dmaout = ASSERT_LINE;
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// dma_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1861_device::dma_w )
+{
+ int sx = m_screen->hpos() + 4;
+ int y = m_screen->vpos();
+ int x;
+
+ for (x = 0; x < 8; x++)
+ {
+ pen_t color = BIT(data, 7) ? rgb_t::white : rgb_t::black;
+ m_bitmap.pix32(y, sx + x) = color;
+ data <<= 1;
+ }
+}
+
+
+//-------------------------------------------------
+// disp_on_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( cdp1861_device::disp_on_w )
+{
+ if (!m_dispon && state) m_disp = 1;
+
+ m_dispon = state;
+}
+
+
+//-------------------------------------------------
+// disp_off_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( cdp1861_device::disp_off_w )
+{
+ if (!m_dispon && !m_dispoff && state) m_disp = 0;
+
+ m_dispoff = state;
+
+ m_write_irq(CLEAR_LINE);
+ m_write_dma_out(CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// screen_update -
+//-------------------------------------------------
+
+UINT32 cdp1861_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ if (m_disp)
+ {
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+ }
+ else
+ {
+ bitmap.fill(rgb_t::black, cliprect);
+ }
+ return 0;
+}
diff --git a/src/devices/video/cdp1861.h b/src/devices/video/cdp1861.h
new file mode 100644
index 00000000000..ccf6938173b
--- /dev/null
+++ b/src/devices/video/cdp1861.h
@@ -0,0 +1,145 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1861 Video Display Controller emulation
+
+**********************************************************************
+ _____ _____
+ _CLK 1 |* \_/ | 24 Vdd
+ _DMAO 2 | | 23 _CLEAR
+ _INT 3 | | 22 SC1
+ TPA 4 | | 21 SC0
+ TPB 5 | | 20 DI7
+ _COMP SYNC 6 | CDP1861 | 19 DI6
+ VIDEO 7 | | 18 DI5
+ _RESET 8 | | 17 DI4
+ _EFX 9 | | 16 DI3
+ DISP ON 10 | | 15 DI2
+ DISP OFF 11 | | 14 DI1
+ Vss 12 |_____________| 13 DI0
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __CDP1861__
+#define __CDP1861__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define CDP1861_VISIBLE_COLUMNS 64
+#define CDP1861_VISIBLE_LINES 128
+
+#define CDP1861_HBLANK_START 14 * 8
+#define CDP1861_HBLANK_END 12
+#define CDP1861_HSYNC_START 0
+#define CDP1861_HSYNC_END 12
+#define CDP1861_SCREEN_WIDTH 14 * 8
+
+#define CDP1861_TOTAL_SCANLINES 262
+
+#define CDP1861_SCANLINE_DISPLAY_START 80
+#define CDP1861_SCANLINE_DISPLAY_END 208
+#define CDP1861_SCANLINE_VBLANK_START 262
+#define CDP1861_SCANLINE_VBLANK_END 16
+#define CDP1861_SCANLINE_VSYNC_START 16
+#define CDP1861_SCANLINE_VSYNC_END 0
+#define CDP1861_SCANLINE_INT_START CDP1861_SCANLINE_DISPLAY_START - 2
+#define CDP1861_SCANLINE_INT_END CDP1861_SCANLINE_DISPLAY_START
+#define CDP1861_SCANLINE_EFX_TOP_START CDP1861_SCANLINE_DISPLAY_START - 4
+#define CDP1861_SCANLINE_EFX_TOP_END CDP1861_SCANLINE_DISPLAY_START
+#define CDP1861_SCANLINE_EFX_BOTTOM_START CDP1861_SCANLINE_DISPLAY_END - 4
+#define CDP1861_SCANLINE_EFX_BOTTOM_END CDP1861_SCANLINE_DISPLAY_END
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_CDP1861_IRQ_CALLBACK(_write) \
+ devcb = &cdp1861_device::set_irq_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CDP1861_DMA_OUT_CALLBACK(_write) \
+ devcb = &cdp1861_device::set_dma_out_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CDP1861_EFX_CALLBACK(_write) \
+ devcb = &cdp1861_device::set_efx_wr_callback(*device, DEVCB_##_write);
+
+
+#define MCFG_CDP1861_SCREEN_ADD(_cdptag, _tag, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_tag) \
+ MCFG_SCREEN_ADD(_tag, RASTER) \
+ MCFG_SCREEN_UPDATE_DEVICE(_cdptag, cdp1861_device, screen_update) \
+ MCFG_SCREEN_RAW_PARAMS(_clock, CDP1861_SCREEN_WIDTH, CDP1861_HBLANK_END, CDP1861_HBLANK_START, CDP1861_TOTAL_SCANLINES, CDP1861_SCANLINE_VBLANK_END, CDP1861_SCANLINE_VBLANK_START)
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> cdp1861_device
+
+class cdp1861_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ cdp1861_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_irq_wr_callback(device_t &device, _Object object) { return downcast<cdp1861_device &>(device).m_write_irq.set_callback(object); }
+ template<class _Object> static devcb_base &set_dma_out_wr_callback(device_t &device, _Object object) { return downcast<cdp1861_device &>(device).m_write_dma_out.set_callback(object); }
+ template<class _Object> static devcb_base &set_efx_wr_callback(device_t &device, _Object object) { return downcast<cdp1861_device &>(device).m_write_efx.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER( dma_w );
+ DECLARE_WRITE_LINE_MEMBER( disp_on_w );
+ DECLARE_WRITE_LINE_MEMBER( disp_off_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ bitmap_rgb32 m_bitmap;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ enum
+ {
+ TIMER_INT,
+ TIMER_EFX,
+ TIMER_DMA
+ };
+
+ devcb_write_line m_write_irq;
+ devcb_write_line m_write_dma_out;
+ devcb_write_line m_write_efx;
+
+ int m_disp; // display enabled
+ int m_dispon; // display on latch
+ int m_dispoff; // display off latch
+ int m_dmaout; // DMA request active
+
+ // timers
+ emu_timer *m_int_timer; // interrupt timer
+ emu_timer *m_efx_timer; // EFx timer
+ emu_timer *m_dma_timer; // DMA timer
+};
+
+
+// device type definition
+extern const device_type CDP1861;
+
+
+
+#endif
diff --git a/src/devices/video/cdp1862.c b/src/devices/video/cdp1862.c
new file mode 100644
index 00000000000..e75cb66c888
--- /dev/null
+++ b/src/devices/video/cdp1862.c
@@ -0,0 +1,198 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1862 Video Display Controller emulation
+
+**********************************************************************/
+
+#include "cdp1862.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+static const int CDP1862_BACKGROUND_COLOR_SEQUENCE[] = { 2, 0, 1, 4 };
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// device type definition
+const device_type CDP1862 = &device_creator<cdp1862_device>;
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// initialize_palette -
+//-------------------------------------------------
+
+inline void cdp1862_device::initialize_palette()
+{
+ int i;
+
+ double res_total = m_chr_r + m_chr_g + m_chr_b + m_chr_bkg;
+
+ int weight_r = (m_chr_r / res_total) * 100;
+ int weight_g = (m_chr_g / res_total) * 100;
+ int weight_b = (m_chr_b / res_total) * 100;
+ int weight_bkg = (m_chr_bkg / res_total) * 100;
+
+ for (i = 0; i < 16; i++)
+ {
+ int r, g, b, luma = 0;
+
+ luma += (i & 4) ? weight_r : 0;
+ luma += (i & 1) ? weight_g : 0;
+ luma += (i & 2) ? weight_b : 0;
+ luma += (i & 8) ? 0 : weight_bkg;
+
+ luma = (luma * 0xff) / 100;
+
+ r = (i & 4) ? luma : 0;
+ g = (i & 1) ? luma : 0;
+ b = (i & 2) ? luma : 0;
+
+ m_palette[i] = rgb_t(r, g, b);
+ }
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// cdp1862_device - constructor
+//-------------------------------------------------
+
+cdp1862_device::cdp1862_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CDP1862, "CDP1862", tag, owner, clock, "cdp1862", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_read_rd(*this),
+ m_read_bd(*this),
+ m_read_gd(*this)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cdp1862_device::device_start()
+{
+ // resolve callbacks
+ m_read_rd.resolve_safe(0);
+ m_read_bd.resolve_safe(0);
+ m_read_gd.resolve_safe(0);
+
+ // find devices
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // init palette
+ initialize_palette();
+
+ // register for state saving
+ save_item(NAME(m_bgcolor));
+ save_item(NAME(m_con));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void cdp1862_device::device_reset()
+{
+ m_bgcolor = 0;
+ m_con = 1;
+}
+
+
+//-------------------------------------------------
+// dma_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1862_device::dma_w )
+{
+ int rd = 1, bd = 1, gd = 1;
+ int sx = m_screen->hpos() + 4;
+ int y = m_screen->vpos();
+ int x;
+
+ if (!m_con)
+ {
+ rd = m_read_rd();
+ bd = m_read_bd();
+ gd = m_read_gd();
+ }
+
+ for (x = 0; x < 8; x++)
+ {
+ int color = CDP1862_BACKGROUND_COLOR_SEQUENCE[m_bgcolor] + 8;
+
+ if (BIT(data, 7))
+ {
+ color = (gd << 2) | (bd << 1) | rd;
+ }
+
+ m_bitmap.pix32(y, sx + x) = m_palette[color];
+
+ data <<= 1;
+ }
+}
+
+
+//-------------------------------------------------
+// disp_on_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( cdp1862_device::bkg_w )
+{
+ if (state)
+ {
+ m_bgcolor++;
+
+ if (m_bgcolor > 3)
+ {
+ m_bgcolor = 0;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// disp_off_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( cdp1862_device::con_w )
+{
+ if (!state)
+ {
+ m_con = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// screen_update -
+//-------------------------------------------------
+
+UINT32 cdp1862_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+
+ m_bitmap.fill(m_palette[CDP1862_BACKGROUND_COLOR_SEQUENCE[m_bgcolor] + 8], cliprect);
+
+ return 0;
+}
diff --git a/src/devices/video/cdp1862.h b/src/devices/video/cdp1862.h
new file mode 100644
index 00000000000..159c12f8a3c
--- /dev/null
+++ b/src/devices/video/cdp1862.h
@@ -0,0 +1,123 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1862 COS/MOS Color Generator Controller emulation
+
+**********************************************************************
+ _____ _____
+ RD 1 |* \_/ | 24 Vdd
+ _RESET 2 | | 23 R LUM
+ _CON 3 | | 22 G LUM
+ B CHR 4 | | 21 GD
+ B LUM 5 | | 20 BKG LUM
+ BKG 6 | CDP1862 | 19 G CHR
+ _LD CLK 7 | | 18 R CHR
+ STP 8 | | 17 BKG CHR
+ CLK OUT 9 | | 16 BD
+ _SYNC 10 | | 15 BURST
+ LUM IN 11 | | 14 _XTAL
+ Vss 12 |_____________| 13 XTAL
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __CDP1862__
+#define __CDP1862__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define CPD1862_CLOCK XTAL_7_15909MHz
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_CDP1861_RD_CALLBACK(_read) \
+ devcb = &cdp1862_device::set_rd_rd_callback(*device, DEVCB_##_read);
+
+#define MCFG_CDP1861_BD_CALLBACK(_read) \
+ devcb = &cdp1862_device::set_bd_rd_callback(*device, DEVCB_##_read);
+
+#define MCFG_CDP1861_GD_CALLBACK(_read) \
+ devcb = &cdp1862_device::set_gd_rd_callback(*device, DEVCB_##_read);
+
+#define MCFG_CDP1862_LUMINANCE(_r, _b, _g, _bkg) \
+ cdp1862_device::static_set_luminance(*device, _r, _b, _g, _bkg);
+
+#define MCFG_CDP1862_CHROMINANCE(_r, _b, _g, _bkg) \
+ cdp1862_device::static_set_chrominance(*device, _r, _b, _g, _bkg);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> cdp1862_device
+
+class cdp1862_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ cdp1862_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_rd_rd_callback(device_t &device, _Object object) { return downcast<cdp1862_device &>(device).m_read_rd.set_callback(object); }
+ template<class _Object> static devcb_base &set_bd_rd_callback(device_t &device, _Object object) { return downcast<cdp1862_device &>(device).m_read_bd.set_callback(object); }
+ template<class _Object> static devcb_base &set_gd_rd_callback(device_t &device, _Object object) { return downcast<cdp1862_device &>(device).m_read_gd.set_callback(object); }
+
+ static void static_set_luminance(device_t &device, double r, double b, double g, double bkg) { downcast<cdp1862_device &>(device).m_lum_r = r; downcast<cdp1862_device &>(device).m_lum_b = b; downcast<cdp1862_device &>(device).m_lum_g = g; downcast<cdp1862_device &>(device).m_lum_bkg = bkg; }
+ static void static_set_chrominance(device_t &device, double r, double b, double g, double bkg) { downcast<cdp1862_device &>(device).m_chr_r = r; downcast<cdp1862_device &>(device).m_chr_b = b; downcast<cdp1862_device &>(device).m_chr_g = g; downcast<cdp1862_device &>(device).m_chr_bkg = bkg; }
+
+ DECLARE_WRITE8_MEMBER( dma_w );
+ DECLARE_WRITE_LINE_MEMBER( bkg_w );
+ DECLARE_WRITE_LINE_MEMBER( con_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ inline void initialize_palette();
+
+ devcb_read_line m_read_rd;
+ devcb_read_line m_read_bd;
+ devcb_read_line m_read_gd;
+
+ bitmap_rgb32 m_bitmap; // bitmap
+
+ double m_lum_r; // red luminance resistor value
+ double m_lum_b; // blue luminance resistor value
+ double m_lum_g; // green luminance resistor value
+ double m_lum_bkg; // background luminance resistor value
+
+ double m_chr_r; // red chrominance resistor value
+ double m_chr_b; // blue chrominance resistor value
+ double m_chr_g; // green chrominance resistor value
+ double m_chr_bkg; // background chrominance resistor value
+
+ rgb_t m_palette[16];
+ int m_bgcolor; // background color
+ int m_con; // color on
+};
+
+
+// device type definition
+extern const device_type CDP1862;
+
+
+
+#endif
diff --git a/src/devices/video/cgapal.c b/src/devices/video/cgapal.c
new file mode 100644
index 00000000000..396c313c1ba
--- /dev/null
+++ b/src/devices/video/cgapal.c
@@ -0,0 +1,433 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+
+/* CGA palettes
+ *
+ * The first 16 are for RGB monitors
+ * The next 16 are for greyscale modes
+ * The next 16 are for text modes on colour composite
+ * The next 16*16 are Mode 6 (colour composite) }
+ * The next 64*16 are Mode 4 (colour composite) } both indexed by the CGA colour select register 0x3D9
+ *
+ */
+
+#include "video/cgapal.h"
+
+const unsigned char cga_palette[16 * CGA_PALETTE_SETS][3] =
+{
+/* RGB colours */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xaa }, { 0x00, 0xaa, 0x00 }, { 0x00, 0xaa, 0xaa },
+ { 0xaa, 0x00, 0x00 }, { 0xaa, 0x00, 0xaa }, { 0xaa, 0x55, 0x00 }, { 0xaa, 0xaa, 0xaa },
+ { 0x55, 0x55, 0x55 }, { 0x55, 0x55, 0xff }, { 0x55, 0xff, 0x55 }, { 0x55, 0xff, 0xff },
+ { 0xff, 0x55, 0x55 }, { 0xff, 0x55, 0xff }, { 0xff, 0xff, 0x55 }, { 0xff, 0xff, 0xff },
+/* Greyscale */
+ { 0x00, 0x00, 0x00 }, { 0x11, 0x11, 0x11 }, { 0x44, 0x44, 0x44 }, { 0x55, 0x55, 0x55 },
+ { 0x22, 0x22, 0x22 }, { 0x33, 0x33, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x77, 0x77, 0x77 },
+ { 0x88, 0x88, 0x88 }, { 0x99, 0x99, 0x99 }, { 0xCC, 0xCC, 0xCC }, { 0xDD, 0xDD, 0xDD },
+ { 0xAA, 0xAA, 0xAA }, { 0xBB, 0xBB, 0xBB }, { 0xEE, 0xEE, 0xEE }, { 0xFF, 0xFF, 0xFF },
+/* Text mode, composite monitor */
+ { 0x00, 0x00, 0x00 }, { 0x0E, 0x00, 0x7A }, { 0x07, 0x55, 0x00 }, { 0x02, 0x65, 0x39 },
+ { 0x51, 0x00, 0x1A }, { 0x54, 0x00, 0x76 }, { 0x48, 0x63, 0x00 }, { 0x8c, 0x8c, 0x8c },
+ { 0x38, 0x38, 0x38 }, { 0x58, 0x49, 0xD5 }, { 0x5F, 0xAD, 0x26 }, { 0x5B, 0xB9, 0xAC },
+ { 0xAA, 0x4A, 0x5E }, { 0xA7, 0x55, 0xD2 }, { 0xA2, 0xB9, 0x31 }, { 0xE2, 0xE2, 0xE2 },
+/* Composite hi-res, colour reg = 0 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+/* Composite hi-res, colour reg = 1 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x04, 0x04 }, { 0x00, 0x00, 0x61 }, { 0x00, 0x00, 0x6b },
+ { 0x25, 0x00, 0x1E }, { 0x15, 0x00, 0x23 }, { 0x18, 0x00, 0x87 }, { 0x06, 0x00, 0x91 },
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x0b, 0x00 }, { 0x00, 0x00, 0x4C }, { 0x00, 0x02, 0x52 },
+ { 0x24, 0x00, 0x08 }, { 0x0E, 0x00, 0x0D }, { 0x18, 0x00, 0x6f }, { 0x07, 0x00, 0x7C },
+/* Composite hi-res, colour reg = 2 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x28, 0x00 }, { 0x01, 0x00, 0x46 }, { 0x00, 0x21, 0x36 },
+ { 0x22, 0x00, 0x01 }, { 0x00, 0x21, 0x00 }, { 0x1b, 0x00, 0x43 }, { 0x00, 0x22, 0x33 },
+ { 0x07, 0x0D, 0x00 }, { 0x00, 0x4B, 0x00 }, { 0x04, 0x0E, 0x00 }, { 0x00, 0x57, 0x00 },
+ { 0x25, 0x02, 0x00 }, { 0x01, 0x46, 0x00 }, { 0x30, 0x04, 0x00 }, { 0x04, 0x53, 0x00 },
+/* Composite hi-res, colour reg = 3 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x30, 0x00 }, { 0x00, 0x00, 0x8E }, { 0x00, 0x38, 0x87 },
+ { 0x2E, 0x00, 0x01 }, { 0x00, 0x21, 0x00 }, { 0x22, 0x00, 0x8C }, { 0x00, 0x35, 0x95 },
+ { 0x00, 0x0F, 0x00 }, { 0x00, 0x4F, 0x00 }, { 0x00, 0x0B, 0x3F }, { 0x00, 0x62, 0x45 },
+ { 0x29, 0x00, 0x00 }, { 0x00, 0x4E, 0x00 }, { 0x35, 0x04, 0x48 }, { 0x01, 0x62, 0x49 },
+/* Composite hi-res, colour reg = 4 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x06, 0x02 }, { 0x01, 0x00, 0x1f }, { 0x00, 0x00, 0x24 },
+ { 0x54, 0x00, 0x38 }, { 0x25, 0x00, 0x23 }, { 0x3A, 0x00, 0x4f }, { 0x29, 0x00, 0x56 },
+ { 0x10, 0x03, 0x00 }, { 0x06, 0x08, 0x00 }, { 0x15, 0x00, 0x00 }, { 0x02, 0x03, 0x00 },
+ { 0x82, 0x00, 0x00 }, { 0x49, 0x00, 0x00 }, { 0x5B, 0x00, 0x0b }, { 0x52, 0x00, 0x0c },
+/* Composite hi-res, colour reg = 5 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x2c, 0x00 }, { 0x06, 0x01, 0x57 }, { 0x00, 0x22, 0x42 },
+ { 0x33, 0x00, 0x01 }, { 0x00, 0x26, 0x00 }, { 0x3a, 0x00, 0x54 }, { 0x08, 0x1D, 0x54 },
+ { 0x13, 0x17, 0x00 }, { 0x00, 0x64, 0x00 }, { 0x29, 0x15, 0x00 }, { 0x00, 0x64, 0x00 },
+ { 0x59, 0x0A, 0x00 }, { 0x30, 0x61, 0x00 }, { 0x7A, 0x06, 0x00 }, { 0x4A, 0x64, 0x00 },
+/* Composite hi-res, colour reg = 6 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x33, 0x00 }, { 0x06, 0x00, 0x5E }, { 0x00, 0x22, 0x45 },
+ { 0x34, 0x00, 0x04 }, { 0x00, 0x1e, 0x00 }, { 0x3d, 0x00, 0x4c }, { 0x0c, 0x22, 0x58 },
+ { 0x18, 0x19, 0x00 }, { 0x00, 0x62, 0x00 }, { 0x2b, 0x14, 0x00 }, { 0x01, 0x64, 0x00 },
+ { 0x57, 0x0f, 0x00 }, { 0x29, 0x63, 0x00 }, { 0x78, 0x09, 0x00 }, { 0x51, 0x61, 0x00 },
+/* Composite hi-res, colour reg = 7 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x3C, 0x01 }, { 0x04, 0x00, 0xC5 }, { 0x00, 0x4C, 0xC7 },
+ { 0x6A, 0x00, 0x15 }, { 0x28, 0x28, 0x24 }, { 0x8A, 0x00, 0xF8 }, { 0x70, 0x61, 0xFF },
+ { 0x20, 0x33, 0x00 }, { 0x00, 0x85, 0x00 }, { 0x2E, 0x25, 0x28 }, { 0x00, 0x98, 0x3B },
+ { 0xb1, 0x11, 0x00 }, { 0x6A, 0x75, 0x00 }, { 0xcc, 0x16, 0x81 }, { 0x91, 0x8e, 0x91 },
+/* Composite hi-res, colour reg = 8 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x12, 0x0F }, { 0x04, 0x00, 0x5F }, { 0x00, 0x02, 0x67 },
+ { 0x31, 0x00, 0x01 }, { 0x04, 0x01, 0x04 }, { 0x37, 0x00, 0x52 }, { 0x17, 0x00, 0x6d },
+ { 0x00, 0x10, 0x00 }, { 0x00, 0x29, 0x00 }, { 0x04, 0x03, 0x04 }, { 0x00, 0x24, 0x16 },
+ { 0x2f, 0x00, 0x00 }, { 0x07, 0x23, 0x00 }, { 0x43, 0x00, 0x08 }, { 0x25, 0x23, 0x24 },
+/* Composite hi-res, colour reg = 9 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x19, 0x14 }, { 0x00, 0x00, 0xc2 }, { 0x00, 0x1c, 0xed },
+ { 0x5e, 0x00, 0x13 }, { 0x2c, 0x03, 0x3a }, { 0x78, 0x00, 0xfa }, { 0x49, 0x11, 0xff },
+ { 0x00, 0x15, 0x00 }, { 0x00, 0x40, 0x00 }, { 0x0d, 0x11, 0x68 }, { 0x00, 0x4f, 0x9c },
+ { 0x67, 0x00, 0x00 }, { 0x39, 0x36, 0x00 }, { 0x91, 0x05, 0xa6 }, { 0x62, 0x45, 0xdc },
+/* Composite hi-res, colour reg = A */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x4B, 0x08 }, { 0x05, 0x00, 0xAA }, { 0x00, 0x50, 0xc7 },
+ { 0x58, 0x00, 0x06 }, { 0x05, 0x44, 0x06 }, { 0x75, 0x00, 0xb0 }, { 0x2e, 0x4f, 0xdc },
+ { 0x0c, 0x2f, 0x00 }, { 0x00, 0xa7, 0x00 }, { 0x26, 0x2e, 0x03 }, { 0x00, 0xb4, 0x24 },
+ { 0x84, 0x1b, 0x00 }, { 0x2d, 0xa5, 0x00 }, { 0xa5, 0x2a, 0x16 }, { 0x5f, 0xb2, 0x2a },
+/* Composite hi-res, colour reg = B */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x59, 0x07 }, { 0x00, 0x08, 0xf0 }, { 0x00, 0x06, 0xfd },
+ { 0x69, 0x00, 0x09 }, { 0x0d, 0x4c, 0x10 }, { 0x8f, 0x00, 0xf4 }, { 0x38, 0x66, 0xff },
+ { 0x02, 0x27, 0x00 }, { 0x00, 0xac, 0x00 }, { 0x19, 0x2f, 0x6d }, { 0x00, 0xc5, 0x82 },
+ { 0x7b, 0x18, 0x00 }, { 0x30, 0xa7, 0x00 }, { 0xac, 0x2b, 0x81 }, { 0x5b, 0xc0, 0xa4 },
+/* Composite hi-res, colour reg = C */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x1C, 0x0C }, { 0x0a, 0x00, 0x7c }, { 0x00, 0x0d, 0x8f },
+ { 0x6e, 0x00, 0x18 }, { 0x48, 0x02, 0x4a }, { 0x95, 0x00, 0xc3 }, { 0x68, 0x01, 0xef },
+ { 0x12, 0x1d, 0x00 }, { 0x00, 0x53, 0x00 }, { 0x33, 0x21, 0x00 }, { 0x05, 0x52, 0x13 },
+ { 0xb4, 0x09, 0x00 }, { 0x87, 0x41, 0x00 }, { 0xd8, 0x07, 0x3a }, { 0xb0, 0x49, 0x63 },
+/* Composite hi-res, colour reg = D */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x1d, 0x17 }, { 0x00, 0x08, 0xcf }, { 0x00, 0x1b, 0xf2 },
+ { 0x83, 0x00, 0x30 }, { 0x4c, 0x08, 0x53 }, { 0xae, 0x00, 0xfa }, { 0x85, 0x0b, 0xff },
+ { 0x09, 0x19, 0x00 }, { 0x00, 0x57, 0x00 }, { 0x21, 0x15, 0x4f }, { 0x00, 0x5e, 0x89 },
+ { 0xb0, 0x04, 0x00 }, { 0x76, 0x4e, 0x00 }, { 0xe2, 0x0a, 0xa9 }, { 0xae, 0x56, 0xe1 },
+/* Composite hi-res, colour reg = E */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x57, 0x06 }, { 0x17, 0x00, 0xc3 }, { 0x00, 0x55, 0xd9 },
+ { 0x6f, 0x00, 0x06 }, { 0x18, 0x49, 0x0d }, { 0xa4, 0x00, 0xcd }, { 0x4e, 0x4c, 0xf7 },
+ { 0x1c, 0x3f, 0x00 }, { 0x00, 0xbf, 0x00 }, { 0x51, 0x35, 0x00 }, { 0x06, 0xc4, 0x1b },
+ { 0xb6, 0x2d, 0x00 }, { 0x73, 0xb2, 0x00 }, { 0xf5, 0x30, 0x21 }, { 0xaa, 0xbf, 0x2f },
+/* Composite hi-res, colour reg = F */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x68, 0x10 }, { 0x10, 0x00, 0xff }, { 0x00, 0x7c, 0xFF },
+ { 0xb3, 0x00, 0x2A }, { 0x53, 0x55, 0x51 }, { 0xf0, 0x00, 0xff }, { 0x95, 0x72, 0xff },
+ { 0x25, 0x3e, 0x00 }, { 0x00, 0xda, 0x00 }, { 0x58, 0x52, 0x56 }, { 0x00, 0xf8, 0x7f },
+ { 0xf8, 0x2c, 0x00 }, { 0xa8, 0xcf, 0x00 }, { 0xff, 0x41, 0xb8 }, { 0xed, 0xea, 0xed },
+/* Composite lo-res, colour reg = 0 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x26, 0x34 }, { 0x00, 0x00, 0x24 }, { 0x00, 0x25, 0x46 },
+ { 0x29, 0x03, 0x00 }, { 0x04, 0x55, 0x00 }, { 0x1f, 0x0c, 0x00 }, { 0x0e, 0x53, 0x03 },
+ { 0x50, 0x00, 0x00 }, { 0x33, 0x36, 0x2b }, { 0x51, 0x00, 0x0b }, { 0x43, 0x37, 0x44 },
+ { 0x60, 0x07, 0x00 }, { 0x3c, 0x61, 0x00 }, { 0x59, 0x1c, 0x00 }, { 0x4a, 0x64, 0x00 },
+/* Composite lo-res, colour reg = 1 */
+ { 0x07, 0x00, 0x7d }, { 0x00, 0x21, 0x4e }, { 0x15, 0x00, 0x36 }, { 0x04, 0x19, 0x77 },
+ { 0x16, 0x1a, 0x00 }, { 0x04, 0x55, 0x00 }, { 0x23, 0x0b, 0x00 }, { 0x13, 0x51, 0x03 },
+ { 0x41, 0x02, 0x3e }, { 0x2e, 0x33, 0x24 }, { 0x51, 0x00, 0x14 }, { 0x41, 0x33, 0x46 },
+ { 0x51, 0x2b, 0x00 }, { 0x3f, 0x60, 0x00 }, { 0x60, 0x17, 0x00 }, { 0x4d, 0x61, 0x00 },
+/* Composite lo-res, colour reg = 2 */
+ { 0x03, 0x55, 0x00 }, { 0x03, 0x55, 0x00 }, { 0x21, 0x0c, 0x00 }, { 0x11, 0x51, 0x03 },
+ { 0x03, 0x55, 0x00 }, { 0x03, 0x55, 0x00 }, { 0x21, 0x0c, 0x00 }, { 0x11, 0x51, 0x03 },
+ { 0x31, 0x37, 0x29 }, { 0x30, 0x36, 0x2a }, { 0x51, 0x00, 0x11 }, { 0x41, 0x34, 0x46 },
+ { 0x3c, 0x63, 0x00 }, { 0x3d, 0x63, 0x00 }, { 0x5f, 0x17, 0x00 }, { 0x4d, 0x61, 0x00 },
+/* Composite lo-res, colour reg = 3 */
+ { 0x04, 0x61, 0x4e }, { 0x05, 0x49, 0x02 }, { 0x1f, 0x04, 0x00 }, { 0x12, 0x47, 0x13 },
+ { 0x03, 0x68, 0x2f }, { 0x05, 0x54, 0x00 }, { 0x1e, 0x0e, 0x00 }, { 0x0f, 0x51, 0x01 },
+ { 0x26, 0x46, 0x73 }, { 0x2f, 0x34, 0x27 }, { 0x50, 0x00, 0x0b }, { 0x48, 0x31, 0x47 },
+ { 0x3e, 0x70, 0x1e }, { 0x40, 0x5f, 0x00 }, { 0x57, 0x1d, 0x00 }, { 0x4a, 0x62, 0x00 },
+/* Composite lo-res, colour reg = 4 */
+ { 0x52, 0x00, 0x14 }, { 0x2e, 0x32, 0x25 }, { 0x52, 0x00, 0x14 }, { 0x46, 0x2f, 0x47 },
+ { 0x1f, 0x09, 0x00 }, { 0x04, 0x55, 0x00 }, { 0x21, 0x0e, 0x00 }, { 0x11, 0x50, 0x02 },
+ { 0x52, 0x00, 0x14 }, { 0x2d, 0x33, 0x25 }, { 0x52, 0x00, 0x14 }, { 0x40, 0x36, 0x3f },
+ { 0x5c, 0x18, 0x00 }, { 0x40, 0x5f, 0x00 }, { 0x5e, 0x19, 0x00 }, { 0x4b, 0x62, 0x00 },
+/* Composite lo-res, colour reg = 5 */
+ { 0x51, 0x00, 0x81 }, { 0x2a, 0x2a, 0x3f }, { 0x4f, 0x00, 0x1c }, { 0x3b, 0x2b, 0x5c },
+ { 0x22, 0x1b, 0x13 }, { 0x04, 0x55, 0x00 }, { 0x21, 0x0e, 0x00 }, { 0x0e, 0x52, 0x04 },
+ { 0x4c, 0x03, 0x59 }, { 0x2e, 0x32, 0x25 }, { 0x51, 0x00, 0x0b }, { 0x3e, 0x37, 0x3d },
+ { 0x5d, 0x2a, 0x03 }, { 0x3d, 0x60, 0x00 }, { 0x5d, 0x19, 0x00 }, { 0x4a, 0x63, 0x00 },
+/* Composite lo-res, colour reg = 6 */
+ { 0x4b, 0x60, 0x00 }, { 0x41, 0x5f, 0x00 }, { 0x5b, 0x1a, 0x00 }, { 0x4b, 0x60, 0x00 },
+ { 0x0e, 0x51, 0x03 }, { 0x03, 0x55, 0x00 }, { 0x22, 0x0b, 0x00 }, { 0x12, 0x51, 0x03 },
+ { 0x41, 0x34, 0x47 }, { 0x31, 0x37, 0x29 }, { 0x50, 0x00, 0x10 }, { 0x3f, 0x32, 0x43 },
+ { 0x4b, 0x60, 0x00 }, { 0x3d, 0x61, 0x00 }, { 0x62, 0x16, 0x00 }, { 0x4b, 0x60, 0x00 },
+/* Composite lo-res, colour reg = 7 */
+ { 0x8b, 0x8b, 0x8b }, { 0x83, 0x5b, 0x00 }, { 0xa4, 0x1b, 0x00 }, { 0x92, 0x5a, 0x09 },
+ { 0x07, 0x79, 0x6f }, { 0x06, 0x55, 0x00 }, { 0x1f, 0x0d, 0x00 }, { 0x10, 0x52, 0x01 },
+ { 0x23, 0x62, 0xa4 }, { 0x2b, 0x33, 0x29 }, { 0x51, 0x00, 0x11 }, { 0x40, 0x36, 0x42 },
+ { 0x46, 0x86, 0x63 }, { 0x42, 0x5e, 0x00 }, { 0x5e, 0x17, 0x00 }, { 0x4a, 0x62, 0x00 },
+/* Composite lo-res, colour reg = 8 */
+ { 0x26, 0x26, 0x26 }, { 0x0a, 0x49, 0x00 }, { 0x25, 0x07, 0x00 }, { 0x16, 0x4c, 0x0e },
+ { 0x1c, 0x29, 0x12 }, { 0x06, 0x55, 0x00 }, { 0x21, 0x0c, 0x00 }, { 0x11, 0x51, 0x02 },
+ { 0x4d, 0x10, 0x5f }, { 0x2c, 0x33, 0x26 }, { 0x51, 0x00, 0x0f }, { 0x41, 0x35, 0x47 },
+ { 0x5a, 0x35, 0x00 }, { 0x43, 0x5f, 0x00 }, { 0x5f, 0x15, 0x00 }, { 0x4d, 0x62, 0x00 },
+/* Composite lo-res, colour reg = 9 */
+ { 0x92, 0x47, 0xd3 }, { 0x47, 0x47, 0x1b }, { 0x66, 0x00, 0x09 }, { 0x54, 0x44, 0x37 },
+ { 0x15, 0x4b, 0x8a }, { 0x05, 0x55, 0x00 }, { 0x00, 0x10, 0x00 }, { 0x10, 0x52, 0x02 },
+ { 0x40, 0x33, 0xd4 }, { 0x2f, 0x33, 0x26 }, { 0x51, 0x00, 0x0d }, { 0x3e, 0x37, 0x3e },
+ { 0x51, 0x59, 0x75 }, { 0x3b, 0x63, 0x00 }, { 0x5b, 0x1a, 0x00 }, { 0x49, 0x64, 0x00 },
+/* Composite lo-res, colour reg = A */
+ { 0x57, 0xac, 0x33 }, { 0x54, 0x7f, 0x00 }, { 0x7f, 0x2e, 0x00 }, { 0x6a, 0x77, 0x00 },
+ { 0x05, 0x80, 0x70 }, { 0x03, 0x54, 0x00 }, { 0x22, 0x0c, 0x00 }, { 0x13, 0x52, 0x00 },
+ { 0x31, 0x64, 0xbe }, { 0x30, 0x35, 0x2a }, { 0x52, 0x00, 0x12 }, { 0x41, 0x33, 0x46 },
+ { 0x3c, 0x91, 0x50 }, { 0x3c, 0x62, 0x00 }, { 0x60, 0x15, 0x00 }, { 0x4f, 0x61, 0x00 },
+/* Composite lo-res, colour reg = B */
+ { 0x5b, 0xb9, 0xa7 }, { 0x5b, 0x6d, 0x00 }, { 0x7f, 0x29, 0x00 }, { 0x6c, 0x6e, 0x00 },
+ { 0x05, 0x95, 0xcb }, { 0x04, 0x54, 0x00 }, { 0x23, 0x0a, 0x00 }, { 0x12, 0x51, 0x02 },
+ { 0x28, 0x77, 0xfb }, { 0x32, 0x37, 0x2f }, { 0x52, 0x00, 0x12 }, { 0x3e, 0x34, 0x40 },
+ { 0x3a, 0xa3, 0xaf }, { 0x3c, 0x63, 0x00 }, { 0x60, 0x15, 0x00 }, { 0x50, 0x61, 0x00 },
+/* Composite lo-res, colour reg = C */
+ { 0xaa, 0x45, 0x6a }, { 0x8c, 0x59, 0x00 }, { 0xa8, 0x1a, 0x00 }, { 0x96, 0x60, 0x05 },
+ { 0x20, 0x35, 0x41 }, { 0x03, 0x55, 0x00 }, { 0x22, 0x0b, 0x00 }, { 0x10, 0x52, 0x02 },
+ { 0x4f, 0x1e, 0xa2 }, { 0x2e, 0x34, 0x25 }, { 0x50, 0x00, 0x10 }, { 0x42, 0x36, 0x45 },
+ { 0x56, 0x48, 0x2a }, { 0x41, 0x5e, 0x00 }, { 0x5d, 0x19, 0x00 }, { 0x49, 0x64, 0x00 },
+/* Composite lo-res, colour reg = D */
+ { 0xa9, 0x54, 0xd6 }, { 0x85, 0x52, 0x09 }, { 0xa5, 0x17, 0x00 }, { 0x96, 0x52, 0x23 },
+ { 0x1e, 0x48, 0x9f }, { 0x06, 0x55, 0x00 }, { 0x1f, 0x0c, 0x00 }, { 0x0f, 0x52, 0x01 },
+ { 0x46, 0x35, 0xe1 }, { 0x2b, 0x32, 0x26 }, { 0x51, 0x00, 0x0e }, { 0x3e, 0x39, 0x3e },
+ { 0x5d, 0x58, 0x88 }, { 0x41, 0x60, 0x00 }, { 0x57, 0x1c, 0x00 }, { 0x4a, 0x62, 0x00 },
+/* Composite lo-res, colour reg = E */
+ { 0xa4, 0xbb, 0x30 }, { 0x9d, 0x84, 0x00 }, { 0xb6, 0x3f, 0x00 }, { 0xa1, 0x8c, 0x00 },
+ { 0x14, 0x7b, 0x8a }, { 0x06, 0x55, 0x00 }, { 0x21, 0x0b, 0x00 }, { 0x13, 0x51, 0x02 },
+ { 0x3f, 0x67, 0xd5 }, { 0x2d, 0x36, 0x29 }, { 0x52, 0x00, 0x11 }, { 0x41, 0x33, 0x46 },
+ { 0x4c, 0x8e, 0x6e }, { 0x3e, 0x61, 0x00 }, { 0x5f, 0x16, 0x00 }, { 0x4c, 0x61, 0x00 },
+/* Composite lo-res, colour reg = F */
+ { 0xe3, 0xe3, 0xe3 }, { 0xdb, 0x82, 0x00 }, { 0xf5, 0x43, 0x00 }, { 0xee, 0x83, 0x00 },
+ { 0x08, 0xa6, 0xf5 }, { 0x04, 0x53, 0x00 }, { 0x1c, 0x0d, 0x00 }, { 0x13, 0x52, 0x00 },
+ { 0x25, 0x91, 0xfc }, { 0x2c, 0x35, 0x30 }, { 0x51, 0x00, 0x0e }, { 0x3b, 0x36, 0x38 },
+ { 0x43, 0xb5, 0xf7 }, { 0x3b, 0x62, 0x00 }, { 0x56, 0x1c, 0x00 }, { 0x4d, 0x61, 0x00 },
+/* Composite lo-res, colour reg = 10 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x58, 0xb9 }, { 0x00, 0x11, 0x8a }, { 0x00, 0x4a, 0xe1 },
+ { 0x73, 0x22, 0x00 }, { 0x5a, 0xad, 0x2e }, { 0x78, 0x61, 0x0d }, { 0x6a, 0xa9, 0x4c },
+ { 0xac, 0x0a, 0x00 }, { 0x87, 0x8d, 0x80 }, { 0xab, 0x46, 0x6c }, { 0x95, 0x8b, 0x97 },
+ { 0xac, 0x30, 0x00 }, { 0x91, 0xbc, 0x16 }, { 0xb8, 0x6d, 0x07 }, { 0xa7, 0xb9, 0x2d },
+/* Composite lo-res, colour reg = 11 */
+ { 0x60, 0x00, 0x7c }, { 0x01, 0x54, 0xdb }, { 0x09, 0x11, 0xb9 }, { 0x07, 0x47, 0xf8 },
+ { 0x76, 0x46, 0x02 }, { 0x5a, 0xae, 0x2e }, { 0x78, 0x62, 0x0c }, { 0x68, 0xa9, 0x4b },
+ { 0x99, 0x2a, 0x09 }, { 0x87, 0x8d, 0x80 }, { 0xab, 0x46, 0x6b }, { 0x93, 0x89, 0x95 },
+ { 0xa4, 0x54, 0x00 }, { 0x93, 0xbb, 0x16 }, { 0xb9, 0x6b, 0x04 }, { 0xa4, 0xb9, 0x30 },
+/* Composite lo-res, colour reg = 12 */
+ { 0x07, 0x55, 0x00 }, { 0x03, 0x83, 0x70 }, { 0x1c, 0x3a, 0x42 }, { 0x0e, 0x81, 0x82 },
+ { 0x58, 0x7e, 0x00 }, { 0x5b, 0xad, 0x2f }, { 0x77, 0x60, 0x0f }, { 0x67, 0xac, 0x49 },
+ { 0x87, 0x5a, 0x00 }, { 0x89, 0x8c, 0x81 }, { 0xa9, 0x49, 0x5e }, { 0x9b, 0x8a, 0x96 },
+ { 0x9f, 0x83, 0x00 }, { 0x94, 0xb9, 0x19 }, { 0xb0, 0x72, 0x03 }, { 0xa5, 0xbb, 0x30 },
+/* Composite lo-res, colour reg = 13 */
+ { 0x03, 0x63, 0x48 }, { 0x04, 0x76, 0x8c }, { 0x1d, 0x34, 0x5a }, { 0x0d, 0x7a, 0x9c },
+ { 0x5a, 0x8e, 0x03 }, { 0x58, 0xac, 0x33 }, { 0x76, 0x60, 0x0b }, { 0x68, 0xaa, 0x4b },
+ { 0x7e, 0x6e, 0x3b }, { 0x88, 0x8c, 0x80 }, { 0xaa, 0x48, 0x64 }, { 0x94, 0x91, 0x92 },
+ { 0x94, 0x9b, 0x00 }, { 0x96, 0xb9, 0x16 }, { 0xb0, 0x73, 0x01 }, { 0xa7, 0xb8, 0x2e },
+/* Composite lo-res, colour reg = 14 */
+ { 0x52, 0x00, 0x13 }, { 0x29, 0x61, 0xb6 }, { 0x52, 0x1e, 0xa1 }, { 0x41, 0x63, 0xdb },
+ { 0x7b, 0x2f, 0x00 }, { 0x5d, 0xac, 0x2c }, { 0x77, 0x63, 0x0a }, { 0x67, 0xa9, 0x51 },
+ { 0xaf, 0x18, 0x00 }, { 0x83, 0x8a, 0x7d }, { 0xa9, 0x46, 0x66 }, { 0x9a, 0x8c, 0xa0 },
+ { 0xb1, 0x43, 0x00 }, { 0x9a, 0xb7, 0x19 }, { 0xb7, 0x6e, 0x05 }, { 0xa4, 0xb9, 0x2f },
+/* Composite lo-res, colour reg = 15 */
+ { 0x52, 0x00, 0x7a }, { 0x2e, 0x55, 0xdc }, { 0x4e, 0x1b, 0xb1 }, { 0x3c, 0x55, 0xec },
+ { 0x80, 0x3f, 0x00 }, { 0x5b, 0xad, 0x2e }, { 0x73, 0x61, 0x0a }, { 0x66, 0xaa, 0x50 },
+ { 0xa7, 0x29, 0x29 }, { 0x86, 0x8a, 0x7d }, { 0xa8, 0x48, 0x60 }, { 0x98, 0x8e, 0x9b },
+ { 0xc0, 0x4a, 0x00 }, { 0x9a, 0xb5, 0x18 }, { 0xb3, 0x72, 0x06 }, { 0xa2, 0xba, 0x31 },
+/* Composite lo-res, colour reg = 16 */
+ { 0x4d, 0x61, 0x00 }, { 0x3b, 0x91, 0x53 }, { 0x59, 0x46, 0x2c }, { 0x48, 0x95, 0x63 },
+ { 0x6c, 0x77, 0x00 }, { 0x5a, 0xac, 0x31 }, { 0x75, 0x63, 0x09 }, { 0x66, 0xa9, 0x4e },
+ { 0x8e, 0x6a, 0x0f }, { 0x87, 0x8b, 0x7f }, { 0xa9, 0x47, 0x66 }, { 0x9b, 0x8c, 0x9f },
+ { 0xab, 0x86, 0x00 }, { 0x9a, 0xb6, 0x18 }, { 0xae, 0x74, 0x01 }, { 0xa2, 0xba, 0x2f },
+/* Composite lo-res, colour reg = 17 */
+ { 0x8b, 0x8b, 0x8b }, { 0x7f, 0x89, 0x79 }, { 0xa4, 0x4a, 0x5c }, { 0x96, 0x8a, 0x95 },
+ { 0x5c, 0xa1, 0x36 }, { 0x5d, 0xad, 0x2b }, { 0x77, 0x62, 0x0a }, { 0x68, 0xa8, 0x4f },
+ { 0x83, 0x88, 0x6f }, { 0x85, 0x8d, 0x81 }, { 0xa9, 0x46, 0x69 }, { 0x99, 0x8b, 0x9f },
+ { 0x97, 0xb1, 0x22 }, { 0x99, 0xb7, 0x18 }, { 0xb8, 0x6c, 0x04 }, { 0xa2, 0xba, 0x2e },
+/* Composite lo-res, colour reg = 18 */
+ { 0x25, 0x25, 0x25 }, { 0x0b, 0x78, 0x8b }, { 0x25, 0x34, 0x5a }, { 0x14, 0x7d, 0x9d },
+ { 0x76, 0x4f, 0x00 }, { 0x5a, 0xac, 0x2e }, { 0x74, 0x64, 0x07 }, { 0x66, 0xaa, 0x49 },
+ { 0xa7, 0x37, 0x25 }, { 0x87, 0x8b, 0x80 }, { 0xa8, 0x48, 0x64 }, { 0x9a, 0x8f, 0x9a },
+ { 0xb6, 0x5a, 0x00 }, { 0x96, 0xba, 0x17 }, { 0xae, 0x73, 0x01 }, { 0xa2, 0xba, 0x30 },
+/* Composite lo-res, colour reg = 19 */
+ { 0x5d, 0x48, 0xd5 }, { 0x4a, 0x77, 0xb3 }, { 0x65, 0x35, 0x86 }, { 0x4d, 0x77, 0xc2 },
+ { 0x6f, 0x72, 0x53 }, { 0x5a, 0xac, 0x30 }, { 0x75, 0x62, 0x09 }, { 0x68, 0xa9, 0x48 },
+ { 0x9c, 0x57, 0xa1 }, { 0x87, 0x8b, 0x80 }, { 0xa7, 0x49, 0x62 }, { 0x92, 0x90, 0x92 },
+ { 0xab, 0x7d, 0x3a }, { 0x97, 0xb8, 0x17 }, { 0xb0, 0x74, 0x03 }, { 0xa2, 0xba, 0x2e },
+/* Composite lo-res, colour reg = 1A */
+ { 0x59, 0xad, 0x2e }, { 0x59, 0xad, 0x2e }, { 0x75, 0x64, 0x08 }, { 0x69, 0xa7, 0x4d },
+ { 0x59, 0xad, 0x2e }, { 0x59, 0xad, 0x2e }, { 0x75, 0x64, 0x08 }, { 0x69, 0xa7, 0x4d },
+ { 0x87, 0x8d, 0x82 }, { 0x85, 0x8b, 0x7d }, { 0xa9, 0x47, 0x67 }, { 0x99, 0x8c, 0x9d },
+ { 0x94, 0xba, 0x17 }, { 0x94, 0xba, 0x17 }, { 0xb6, 0x6e, 0x06 }, { 0xa2, 0xbb, 0x30 },
+/* Composite lo-res, colour reg = 1B */
+ { 0x5b, 0xb9, 0xa6 }, { 0x5c, 0xa2, 0x4a }, { 0x7a, 0x5c, 0x24 }, { 0x6a, 0x9a, 0x6c },
+ { 0x56, 0xbf, 0x8e }, { 0x59, 0xae, 0x31 }, { 0x78, 0x60, 0x0d }, { 0x68, 0xa9, 0x4f },
+ { 0x7f, 0xa3, 0xcd }, { 0x85, 0x8c, 0x80 }, { 0xaa, 0x47, 0x6a }, { 0x98, 0x8b, 0x9c },
+ { 0x93, 0xcd, 0x72 }, { 0x92, 0xbd, 0x14 }, { 0xb8, 0x6c, 0x06 }, { 0xa4, 0xb9, 0x2f },
+/* Composite lo-res, colour reg = 1C */
+ { 0xa9, 0x44, 0x63 }, { 0x85, 0x8a, 0x7f }, { 0xa9, 0x44, 0x63 }, { 0x99, 0x8e, 0x9d },
+ { 0x74, 0x5f, 0x0d }, { 0x5c, 0xad, 0x2c }, { 0x77, 0x63, 0x0a }, { 0x68, 0xa8, 0x4e },
+ { 0xa9, 0x44, 0x63 }, { 0x84, 0x8b, 0x7e }, { 0xa9, 0x44, 0x63 }, { 0x99, 0x8c, 0x9e },
+ { 0xad, 0x72, 0x01 }, { 0x9b, 0xb6, 0x1a }, { 0xb3, 0x6e, 0x05 }, { 0xa4, 0xb9, 0x2f },
+/* Composite lo-res, colour reg = 1D */
+ { 0xaa, 0x55, 0xd4 }, { 0x83, 0x81, 0x9b }, { 0xa6, 0x43, 0x7b }, { 0x95, 0x80, 0xbd },
+ { 0x76, 0x72, 0x66 }, { 0x5a, 0xad, 0x2c }, { 0x7b, 0x61, 0x0c }, { 0x68, 0xa9, 0x50 },
+ { 0xa5, 0x59, 0xaa }, { 0x87, 0x8e, 0x7f }, { 0xa9, 0x45, 0x6a }, { 0x97, 0x8b, 0x98 },
+ { 0xb2, 0x82, 0x48 }, { 0x93, 0xbb, 0x16 }, { 0xb9, 0x6d, 0x05 }, { 0xa4, 0xb9, 0x2f },
+/* Composite lo-res, colour reg = 1E */
+ { 0xa5, 0xb8, 0x2d }, { 0xa5, 0xb8, 0x2d }, { 0xb4, 0x70, 0x05 }, { 0xa5, 0xb8, 0x2d },
+ { 0x64, 0xaa, 0x4e }, { 0x5b, 0xad, 0x2c }, { 0x77, 0x63, 0x0b }, { 0x68, 0xa8, 0x4f },
+ { 0x94, 0x91, 0x95 }, { 0x83, 0x8a, 0x7b }, { 0xa9, 0x47, 0x67 }, { 0x98, 0x8a, 0x9e },
+ { 0xa5, 0xb8, 0x2d }, { 0x9a, 0xb6, 0x1a }, { 0xb2, 0x70, 0x05 }, { 0xa5, 0xb8, 0x2d },
+/* Composite lo-res, colour reg = 1F */
+ { 0xe3, 0xe3, 0xe3 }, { 0xde, 0xb1, 0x45 }, { 0xf8, 0x71, 0x3e }, { 0xeb, 0xb3, 0x5e },
+ { 0x58, 0xd3, 0xc4 }, { 0x5b, 0xad, 0x2d }, { 0x78, 0x63, 0x0b }, { 0x68, 0xa8, 0x4f },
+ { 0x7f, 0xb7, 0xf4 }, { 0x86, 0x8b, 0x7d }, { 0xa8, 0x46, 0x69 }, { 0x9a, 0x8c, 0x9f },
+ { 0x99, 0xe0, 0xbc }, { 0x99, 0xb6, 0x1a }, { 0xb8, 0x6d, 0x07 }, { 0xa5, 0xb8, 0x30 },
+/* Composite lo-res, colour reg = 20 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x3a, 0x85 }, { 0x00, 0x00, 0x6c }, { 0x00, 0x46, 0xce },
+ { 0x26, 0x01, 0x00 }, { 0x03, 0x61, 0x4a }, { 0x24, 0x16, 0x2a }, { 0x09, 0x72, 0x8d },
+ { 0x4d, 0x00, 0x00 }, { 0x2d, 0x45, 0x9c }, { 0x51, 0x00, 0x7c }, { 0x30, 0x58, 0xe1 },
+ { 0x9e, 0x0f, 0x00 }, { 0x86, 0x7b, 0x45 }, { 0xab, 0x29, 0x2c }, { 0x8b, 0x89, 0x88 },
+/* Composite lo-res, colour reg = 21 */
+ { 0x06, 0x00, 0x7C }, { 0x00, 0x3B, 0xA0 }, { 0x14, 0x00, 0x93 }, { 0x00, 0x49, 0xF7 },
+ { 0x19, 0x12, 0x13 }, { 0x02, 0x63, 0x3f }, { 0x25, 0x16, 0x2b }, { 0x09, 0x71, 0x93 },
+ { 0x46, 0x00, 0x65 }, { 0x28, 0x45, 0x93 }, { 0x50, 0x00, 0x80 }, { 0x32, 0x55, 0xe6 },
+ { 0x9c, 0x2d, 0x0c }, { 0x86, 0x78, 0x44 }, { 0xaa, 0x29, 0x33 }, { 0x92, 0x84, 0x84 },
+/* Composite lo-res, colour reg = 22 */
+ { 0x05, 0x56, 0x00 }, { 0x04, 0x69, 0x32 }, { 0x21, 0x1d, 0x13 }, { 0x07, 0x76, 0x7e },
+ { 0x05, 0x48, 0x03 }, { 0x03, 0x64, 0x43 }, { 0x24, 0x16, 0x28 }, { 0x08, 0x70, 0x92 },
+ { 0x45, 0x41, 0x74 }, { 0x27, 0x44, 0x92 }, { 0x4f, 0x00, 0x7f }, { 0x36, 0x58, 0xe8 },
+ { 0x85, 0x57, 0x02 }, { 0x87, 0x77, 0x45 }, { 0xa6, 0x2c, 0x2c }, { 0x8b, 0x8b, 0x8b },
+/* Composite lo-res, colour reg = 23 */
+ { 0x02, 0x61, 0x49 }, { 0x02, 0x61, 0x49 }, { 0x24, 0x15, 0x27 }, { 0x05, 0x73, 0x84 },
+ { 0x02, 0x61, 0x49 }, { 0x02, 0x61, 0x49 }, { 0x24, 0x15, 0x27 }, { 0x05, 0x73, 0x84 },
+ { 0x2a, 0x43, 0x96 }, { 0x2a, 0x43, 0x96 }, { 0x51, 0x00, 0x7d }, { 0x31, 0x5a, 0xdc },
+ { 0x86, 0x79, 0x3d }, { 0x86, 0x79, 0x3d }, { 0xa8, 0x2a, 0x21 }, { 0x8a, 0x8a, 0x8a },
+/* Composite lo-res, colour reg = 24 */
+ { 0x51, 0x00, 0x0e }, { 0x2b, 0x49, 0x76 }, { 0x4c, 0x04, 0x53 }, { 0x23, 0x5a, 0xf5 },
+ { 0x22, 0x05, 0x00 }, { 0x04, 0x06, 0x4b }, { 0x22, 0x13, 0x22 }, { 0x03, 0x74, 0x82 },
+ { 0x4e, 0x00, 0x25 }, { 0x2d, 0x46, 0x9d }, { 0x52, 0x00, 0x7c }, { 0x34, 0x59, 0xe3 },
+ { 0xaa, 0x17, 0x00 }, { 0x85, 0x79, 0x3d }, { 0xa7, 0x2e, 0x24 }, { 0x8b, 0x8b, 0x8b },
+/* Composite lo-res, colour reg = 25 */
+ { 0x50, 0x00, 0x7e }, { 0x2d, 0x45, 0x9d }, { 0x50, 0x00, 0x7e }, { 0x30, 0x57, 0xde },
+ { 0x23, 0x16, 0x29 }, { 0x05, 0x61, 0x49 }, { 0x23, 0x13, 0x26 }, { 0x04, 0x75, 0x87 },
+ { 0x50, 0x00, 0x7e }, { 0x28, 0x44, 0x96 }, { 0x50, 0x00, 0x7e }, { 0x31, 0x59, 0xdf },
+ { 0xac, 0x28, 0x33 }, { 0x85, 0x79, 0x3c }, { 0xa7, 0x2d, 0x23 }, { 0x8b, 0x8b, 0x8b },
+/* Composite lo-res, colour reg = 26 */
+ { 0x4f, 0x62, 0x00 }, { 0x3d, 0x71, 0x1f }, { 0x60, 0x2a, 0x07 }, { 0x43, 0x88, 0x69 },
+ { 0x13, 0x42, 0x19 }, { 0x05, 0x63, 0x46 }, { 0x24, 0x16, 0x27 }, { 0x07, 0x72, 0x91 },
+ { 0x3c, 0x2b, 0x5f }, { 0x2a, 0x45, 0x92 }, { 0x4f, 0x00, 0x82 }, { 0x36, 0x57, 0xe9 },
+ { 0x92, 0x5a, 0x0b }, { 0x87, 0x78, 0x45 }, { 0xa7, 0x2c, 0x2a }, { 0x8b, 0x8b, 0x8c },
+/* Composite lo-res, colour reg = 27 */
+ { 0x8b, 0x8b, 0x8b }, { 0x89, 0x78, 0x47 }, { 0xa8, 0x2a, 0x2d }, { 0x8b, 0x8b, 0x8b },
+ { 0x08, 0x71, 0x93 }, { 0x02, 0x62, 0x4a }, { 0x26, 0x16, 0x2a }, { 0x06, 0x73, 0x87 },
+ { 0x35, 0x58, 0xe6 }, { 0x2f, 0x45, 0x9e }, { 0x50, 0x00, 0x78 }, { 0x2f, 0x59, 0xe0 },
+ { 0x8b, 0x8b, 0x8b }, { 0x87, 0x7a, 0x46 }, { 0xaa, 0x29, 0x30 }, { 0x8b, 0x8b, 0x8b },
+/* Composite lo-res, colour reg = 28 */
+ { 0x25, 0x25, 0x25 }, { 0x08, 0x5f, 0x4b }, { 0x2b, 0x15, 0x25 }, { 0x09, 0x71, 0x88 },
+ { 0x1e, 0x23, 0x26 }, { 0x04, 0x62, 0x47 }, { 0x21, 0x19, 0x28 }, { 0x06, 0x74, 0x88 },
+ { 0x48, 0x0b, 0x70 }, { 0x26, 0x42, 0x95 }, { 0x52, 0x00, 0x7c }, { 0x34, 0x58, 0xe6 },
+ { 0xa1, 0x37, 0x1c }, { 0x85, 0x78, 0x3e }, { 0xa6, 0x2e, 0x23 }, { 0x8b, 0x8b, 0x8b },
+/* Composite lo-res, colour reg = 29 */
+ { 0x5e, 0x44, 0xd3 }, { 0x45, 0x61, 0x73 }, { 0x6c, 0x18, 0x53 }, { 0x4c, 0x72, 0xbf },
+ { 0x14, 0x45, 0xa3 }, { 0x04, 0x61, 0x49 }, { 0x22, 0x15, 0x25 }, { 0x06, 0x72, 0x8d },
+ { 0x41, 0x2d, 0xf6 }, { 0x27, 0x43, 0x96 }, { 0x51, 0x00, 0x7c }, { 0x34, 0x58, 0xe4 },
+ { 0x9b, 0x5a, 0xa5 }, { 0x85, 0x78, 0x3d }, { 0xa6, 0x2e, 0x23 }, { 0x8c, 0x8c, 0x8c },
+/* Composite lo-res, colour reg = 2A */
+ { 0x5c, 0xae, 0x2a }, { 0x58, 0x91, 0x00 }, { 0x7b, 0x41, 0x00 }, { 0x5e, 0xa0, 0x36 },
+ { 0x06, 0x78, 0x86 }, { 0x03, 0x62, 0x49 }, { 0x25, 0x14, 0x28 }, { 0x03, 0x74, 0x82 },
+ { 0x25, 0x5b, 0xcc }, { 0x2a, 0x43, 0x97 }, { 0x52, 0x00, 0x79 }, { 0x31, 0x5b, 0xe0 },
+ { 0x7e, 0x88, 0x7b }, { 0x86, 0x7b, 0x3e }, { 0xa7, 0x2c, 0x22 }, { 0x89, 0x89, 0x89 },
+/* Composite lo-res, colour reg = 2B */
+ { 0x58, 0xbb, 0x98 }, { 0x5a, 0x8c, 0x0a }, { 0x7f, 0x3b, 0x02 }, { 0x60, 0x9a, 0x4b },
+ { 0x03, 0x96, 0xce }, { 0x04, 0x61, 0x4a }, { 0x23, 0x14, 0x24 }, { 0x04, 0x75, 0x86 },
+ { 0x23, 0x76, 0xfe }, { 0x28, 0x43, 0x95 }, { 0x51, 0x00, 0x7b }, { 0x30, 0x59, 0xdb },
+ { 0x80, 0xab, 0xd2 }, { 0x85, 0x7a, 0x3d }, { 0xa8, 0x2e, 0x26 }, { 0x8b, 0x8b, 0x8b },
+/* Composite lo-res, colour reg = 2C */
+ { 0xa9, 0x46, 0x66 }, { 0x7f, 0x6f, 0x37 }, { 0xa9, 0x27, 0x27 }, { 0x87, 0x82, 0x7f },
+ { 0x1e, 0x31, 0x5c }, { 0x04, 0x63, 0x44 }, { 0x23, 0x16, 0x2a }, { 0x08, 0x71, 0x92 },
+ { 0x4e, 0x1a, 0xb0 }, { 0x27, 0x46, 0x92 }, { 0x50, 0x00, 0x80 }, { 0x33, 0x56, 0xe7 },
+ { 0xa3, 0x4a, 0x58 }, { 0x87, 0x78, 0x46 }, { 0xab, 0x29, 0x34 }, { 0x8b, 0x8b, 0x8b },
+/* Composite lo-res, colour reg = 2D */
+ { 0xa8, 0x53, 0xd8 }, { 0x82, 0x6c, 0x5f }, { 0xac, 0x25, 0x3f }, { 0x8d, 0x7f, 0xa8 },
+ { 0x21, 0x46, 0xb9 }, { 0x04, 0x61, 0x4d }, { 0x24, 0x13, 0x23 }, { 0x04, 0x75, 0x87 },
+ { 0x4c, 0x2e, 0xfe }, { 0x2a, 0x45, 0x99 }, { 0x52, 0x00, 0x78 }, { 0x32, 0x5a, 0xde },
+ { 0xa8, 0x53, 0xd8 }, { 0x84, 0x7c, 0x3d }, { 0xa7, 0x2d, 0x22 }, { 0x8b, 0x8b, 0x8b },
+/* Composite lo-res, colour reg = 2E */
+ { 0xa4, 0xba, 0x2e }, { 0x8e, 0x9f, 0x00 }, { 0xbf, 0x4e, 0x00 }, { 0xa5, 0xae, 0x2d },
+ { 0x13, 0x71, 0xa6 }, { 0x03, 0x62, 0x4a }, { 0x24, 0x14, 0x28 }, { 0x05, 0x74, 0x83 },
+ { 0x32, 0x5d, 0xe0 }, { 0x2e, 0x46, 0x9c }, { 0x51, 0x00, 0x7c }, { 0x2f, 0x59, 0xe0 },
+ { 0x8a, 0x8d, 0x94 }, { 0x86, 0x7b, 0x40 }, { 0xa8, 0x2c, 0x22 }, { 0x8a, 0x8a, 0x8a },
+/* Composite lo-res, colour reg = 2F */
+ { 0xe4, 0xe4, 0xe4 }, { 0xdd, 0xa6, 0x0a }, { 0xf9, 0x53, 0x04 }, { 0xea, 0xae, 0x54 },
+ { 0x08, 0xa2, 0xfc }, { 0x03, 0x62, 0x48 }, { 0x24, 0x14, 0x28 }, { 0x05, 0x74, 0x84 },
+ { 0x27, 0x90, 0xff }, { 0x2a, 0x43, 0x95 }, { 0x52, 0x00, 0x79 }, { 0x34, 0x5a, 0xe3 },
+ { 0x85, 0xbb, 0xff }, { 0x85, 0x7a, 0x3d }, { 0xa7, 0x2c, 0x23 }, { 0x8a, 0x8a, 0x8a },
+/* Composite lo-res, colour reg = 30 */
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x63, 0xfe }, { 0x00, 0x1d, 0xe9 }, { 0x00, 0x81, 0xff },
+ { 0x7e, 0x16, 0x00 }, { 0x5b, 0xb9, 0xa5 }, { 0x79, 0x6a, 0x79 }, { 0x59, 0xce, 0xdc },
+ { 0xa8, 0x05, 0x00 }, { 0x84, 0x9e, 0xf3 }, { 0xaa, 0x54, 0xd3 }, { 0x8c, 0xb1, 0xff },
+ { 0xfb, 0x28, 0x00 }, { 0xde, 0xd2, 0x94 }, { 0xfc, 0x85, 0x7b }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 31 */
+ { 0x07, 0x00, 0x7c }, { 0x00, 0x70, 0xfe }, { 0x0d, 0x20, 0xff }, { 0x04, 0x7f, 0xff },
+ { 0x6f, 0x3b, 0x00 }, { 0x59, 0xbb, 0x9b }, { 0x79, 0x6c, 0x81 }, { 0x5d, 0xcb, 0xe4 },
+ { 0x99, 0x26, 0x29 }, { 0x83, 0x9d, 0xf2 }, { 0xaa, 0x54, 0xd4 }, { 0x88, 0xb0, 0xff },
+ { 0xf4, 0x57, 0x00 }, { 0xdf, 0xd3, 0x9a }, { 0xfe, 0x81, 0x7f }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 32 */
+ { 0x06, 0x55, 0x00 }, { 0x03, 0x99, 0xbe }, { 0x22, 0x46, 0xa4 }, { 0x09, 0xa4, 0xfa },
+ { 0x62, 0x6d, 0x00 }, { 0x59, 0xbb, 0x9b }, { 0x7a, 0x6d, 0x7e }, { 0x5c, 0xc8, 0xe7 },
+ { 0x8a, 0x4f, 0x11 }, { 0x80, 0x9b, 0xea }, { 0xa7, 0x55, 0xda }, { 0x8c, 0xad, 0xff },
+ { 0xdf, 0x7f, 0x00 }, { 0xe2, 0xd1, 0x9c }, { 0xfd, 0x81, 0x87 }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 33 */
+ { 0x03, 0x63, 0x47 }, { 0x06, 0x91, 0xda }, { 0x23, 0x41, 0xbc }, { 0x06, 0xa5, 0xfa },
+ { 0x5d, 0x8a, 0x07 }, { 0x59, 0xbb, 0x9b }, { 0x7c, 0x6e, 0x80 }, { 0x5d, 0xc9, 0xe8 },
+ { 0x86, 0x6b, 0x60 }, { 0x81, 0x9d, 0xea }, { 0xa8, 0x55, 0xd8 }, { 0x8e, 0xae, 0xff },
+ { 0xdf, 0xa4, 0x0c }, { 0xe1, 0xd0, 0x9e }, { 0xfd, 0x81, 0x85 }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 34 */
+ { 0x52, 0x00, 0x14 }, { 0x20, 0x7b, 0xf7 }, { 0x4e, 0x2f, 0xed }, { 0x22, 0x91, 0xff },
+ { 0x7f, 0x28, 0x00 }, { 0x5a, 0xbb, 0x9a }, { 0x79, 0x6d, 0x7f }, { 0x5e, 0xc8, 0xeb },
+ { 0xac, 0x13, 0x02 }, { 0x7e, 0x9c, 0xea }, { 0xa8, 0x54, 0xd6 }, { 0x8e, 0xae, 0xff },
+ { 0xf5, 0x43, 0x00 }, { 0xdf, 0xcf, 0x9d }, { 0xfd, 0x81, 0x88 }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 35 */
+ { 0x50, 0x00, 0x7b }, { 0x22, 0x76, 0xfe }, { 0x4c, 0x2f, 0xff }, { 0x36, 0x85, 0xff },
+ { 0x7b, 0x3d, 0x00 }, { 0x5b, 0xbc, 0xa1 }, { 0x7b, 0x6c, 0x7f }, { 0x5d, 0xca, 0xe8 },
+ { 0xa9, 0x26, 0x3c }, { 0x81, 0x9c, 0xec }, { 0xa7, 0x54, 0xdc }, { 0x8b, 0xad, 0xff },
+ { 0xf8, 0x57, 0x03 }, { 0xe1, 0xd4, 0x9e }, { 0xfd, 0x80, 0x82 }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 36 */
+ { 0x4c, 0x61, 0x00 }, { 0x39, 0xa2, 0xaf }, { 0x5b, 0x5a, 0x8c }, { 0x42, 0xb6, 0xf4 },
+ { 0x66, 0x6b, 0x00 }, { 0x5b, 0xba, 0xa4 }, { 0x7a, 0x6c, 0x7e }, { 0x5e, 0xca, 0xe8 },
+ { 0x91, 0x51, 0x1f }, { 0x85, 0x9d, 0xf4 }, { 0xaa, 0x55, 0xd7 }, { 0x88, 0xaf, 0xff },
+ { 0xea, 0x87, 0x00 }, { 0xde, 0xd4, 0x98 }, { 0xfd, 0x7f, 0x81 }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 37 */
+ { 0x8b, 0x8b, 0x8b }, { 0x86, 0xa7, 0xde }, { 0xa6, 0x5a, 0xbf }, { 0x85, 0xbb, 0xff },
+ { 0x5f, 0x9b, 0x51 }, { 0x5a, 0xb9, 0xa7 }, { 0x78, 0x6a, 0x7b }, { 0x5b, 0xce, 0xdb },
+ { 0x8b, 0x7c, 0xae }, { 0x82, 0x9c, 0xf2 }, { 0xaa, 0x54, 0xd3 }, { 0x87, 0xb2, 0xff },
+ { 0xe7, 0xb0, 0x54 }, { 0xdc, 0xd2, 0x95 }, { 0xfc, 0x84, 0x77 }, { 0xe3, 0xe3, 0xe3 },
+/* Composite lo-res, colour reg = 38 */
+ { 0x24, 0x24, 0x24 }, { 0x06, 0x91, 0xd8 }, { 0x2a, 0x44, 0xb9 }, { 0x0f, 0x9e, 0xfe },
+ { 0x7a, 0x49, 0x00 }, { 0x58, 0xbc, 0x98 }, { 0x7a, 0x6d, 0x7f }, { 0x5e, 0xc8, 0xeb },
+ { 0xa3, 0x36, 0x3a }, { 0x7f, 0x9c, 0xec }, { 0xa8, 0x54, 0xd7 }, { 0x8a, 0xad, 0xff },
+ { 0xf7, 0x64, 0x00 }, { 0xe1, 0xd1, 0x9c }, { 0xfd, 0x7f, 0x8b }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 39 */
+ { 0x62, 0x46, 0xd3 }, { 0x3d, 0x93, 0xf2 }, { 0x6b, 0x46, 0xeb }, { 0x4f, 0xa0, 0xff },
+ { 0x6f, 0x69, 0x6b }, { 0x58, 0xbb, 0x9b }, { 0x7b, 0x6e, 0x80 }, { 0x5e, 0xc8, 0xec },
+ { 0x9d, 0x53, 0xbd }, { 0x81, 0x9d, 0xf0 }, { 0xa8, 0x54, 0xd8 }, { 0x86, 0xb1, 0xff },
+ { 0xf4, 0x85, 0x5e }, { 0xdf, 0xd1, 0x9f }, { 0xfe, 0x7f, 0x88 }, { 0xe3, 0xe3, 0xe3 },
+/* Composite lo-res, colour reg = 3A */
+ { 0x5a, 0xad, 0x2d }, { 0x58, 0xc1, 0x81 }, { 0x77, 0x74, 0x68 }, { 0x58, 0xcf, 0xd1 },
+ { 0x5d, 0xa0, 0x4d }, { 0x59, 0xbb, 0x9b }, { 0x7c, 0x6d, 0x7f }, { 0x5e, 0xc9, 0xeb },
+ { 0x83, 0x7d, 0x9e }, { 0x7f, 0x9c, 0xec }, { 0xa9, 0x54, 0xd6 }, { 0x8c, 0xae, 0xff },
+ { 0xde, 0xae, 0x4b }, { 0xdc, 0xd0, 0x98 }, { 0xfc, 0x81, 0x8a }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 3B */
+ { 0x59, 0xbc, 0x9c }, { 0x59, 0xbc, 0x9c }, { 0x7a, 0x6a, 0x79 }, { 0x59, 0xce, 0xd9 },
+ { 0x59, 0xbc, 0x9c }, { 0x59, 0xbc, 0x9c }, { 0x7b, 0x6b, 0x80 }, { 0x59, 0xcc, 0xd9 },
+ { 0x81, 0x9b, 0xec }, { 0x81, 0x9b, 0xec }, { 0xa9, 0x54, 0xd4 }, { 0x8e, 0xb2, 0xff },
+ { 0xdf, 0xcf, 0x9b }, { 0xdf, 0xcf, 0x9b }, { 0xfd, 0x85, 0x79 }, { 0xe3, 0xe3, 0xe3 },
+/* Composite lo-res, colour reg = 3C */
+ { 0xaa, 0x46, 0x6a }, { 0x7a, 0xa3, 0xc7 }, { 0xa7, 0x58, 0xba }, { 0x84, 0xb5, 0xf8 },
+ { 0x78, 0x5b, 0x23 }, { 0x59, 0xbc, 0x9c }, { 0x7a, 0x6e, 0x81 }, { 0x5f, 0xc9, 0xeb },
+ { 0xa3, 0x44, 0x71 }, { 0x80, 0x9d, 0xec }, { 0xa8, 0x54, 0xd7 }, { 0x8a, 0xae, 0xff },
+ { 0xf8, 0x74, 0x1a }, { 0xdf, 0xd1, 0x9e }, { 0xfe, 0x81, 0x8b }, { 0xe3, 0xe3, 0xe3 },
+/* Composite lo-res, colour reg = 3D */
+ { 0xaa, 0x53, 0xd1 }, { 0x80, 0x9c, 0xec }, { 0xaa, 0x53, 0xd1 }, { 0x88, 0xad, 0xff },
+ { 0x7a, 0x6b, 0x7e }, { 0x58, 0xbb, 0x9d }, { 0x7a, 0x6d, 0x81 }, { 0x5f, 0xc9, 0xe5 },
+ { 0xaa, 0x53, 0xd1 }, { 0x84, 0x9d, 0xf2 }, { 0xaa, 0x53, 0xd1 }, { 0x88, 0xad, 0xff },
+ { 0xfd, 0x85, 0x78 }, { 0xe0, 0xd2, 0x9e }, { 0xfe, 0x80, 0x87 }, { 0xe3, 0xe3, 0xe3 },
+/* Composite lo-res, colour reg = 3E */
+ { 0xa1, 0xba, 0x2f }, { 0x90, 0xce, 0x70 }, { 0xb4, 0x80, 0x4b }, { 0x9d, 0xe0, 0xba },
+ { 0x6a, 0x9f, 0x68 }, { 0x5a, 0xba, 0x9f }, { 0x7a, 0x6b, 0x7a }, { 0x5a, 0xce, 0xdb },
+ { 0x91, 0x83, 0xae }, { 0x80, 0x9b, 0xef }, { 0xaa, 0x54, 0xd2 }, { 0x8c, 0xb1, 0xff },
+ { 0xeb, 0xb3, 0x59 }, { 0xdd, 0xd3, 0x94 }, { 0xfc, 0x85, 0x79 }, { 0xe4, 0xe4, 0xe4 },
+/* Composite lo-res, colour reg = 3F */
+ { 0xe4, 0xe4, 0xe4 }, { 0xdd, 0xd2, 0x93 }, { 0xfc, 0x85, 0x7a }, { 0xe4, 0xe4, 0xe4 },
+ { 0x59, 0xcc, 0xda }, { 0x59, 0xbb, 0x9c }, { 0x7b, 0x6d, 0x7f }, { 0x5c, 0xca, 0xe5 },
+ { 0x87, 0xb3, 0xff }, { 0x7f, 0x9a, 0xea }, { 0xa8, 0x54, 0xd4 }, { 0x8c, 0xb0, 0xff },
+ { 0xe4, 0xe4, 0xe4 }, { 0xdf, 0xd1, 0x98 }, { 0xfd, 0x84, 0x7d }, { 0xe4, 0xe4, 0xe4 },
+};
diff --git a/src/devices/video/cgapal.h b/src/devices/video/cgapal.h
new file mode 100644
index 00000000000..c5dfafd636e
--- /dev/null
+++ b/src/devices/video/cgapal.h
@@ -0,0 +1,5 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+#define CGA_PALETTE_SETS 83 /* one for colour, one for mono,
+ * 81 for colour composite */
+extern const unsigned char cga_palette[16 * CGA_PALETTE_SETS][3];
diff --git a/src/devices/video/clgd542x.c b/src/devices/video/clgd542x.c
new file mode 100644
index 00000000000..854611d1c80
--- /dev/null
+++ b/src/devices/video/clgd542x.c
@@ -0,0 +1,1512 @@
+// license:BSD-3-Clause
+// copyright-holders:Barry Rodewald
+/*
+
+ Cirrus Logic GD542x/3x video chipsets
+
+*/
+
+#include "clgd542x.h"
+
+#define LOG_REG 0
+#define LOG_BLIT 1
+
+#define CRTC_PORT_ADDR ((vga.miscellaneous_output&1)?0x3d0:0x3b0)
+
+//#define TEXT_LINES (LINES_HELPER)
+#define LINES (vga.crtc.vert_disp_end+1)
+#define TEXT_LINES (vga.crtc.vert_disp_end+1)
+
+#define GRAPHIC_MODE (vga.gc.alpha_dis) /* else text mode */
+
+#define EGA_COLUMNS (vga.crtc.horz_disp_end+1)
+#define EGA_START_ADDRESS (vga.crtc.start_addr)
+#define EGA_LINE_LENGTH (vga.crtc.offset<<1)
+
+#define VGA_COLUMNS (vga.crtc.horz_disp_end+1)
+#define VGA_START_ADDRESS (vga.crtc.start_addr)
+#define VGA_LINE_LENGTH (vga.crtc.offset<<3)
+
+#define IBM8514_LINE_LENGTH (m_vga->offset())
+
+#define CHAR_WIDTH ((vga.sequencer.data[1]&1)?8:9)
+
+#define TEXT_COLUMNS (vga.crtc.horz_disp_end+1)
+#define TEXT_START_ADDRESS (vga.crtc.start_addr<<3)
+#define TEXT_LINE_LENGTH (vga.crtc.offset<<1)
+
+#define TEXT_COPY_9COLUMN(ch) (((ch & 0xe0) == 0xc0)&&(vga.attribute.data[0x10]&4))
+
+const device_type CIRRUS_GD5428 = &device_creator<cirrus_gd5428_device>;
+const device_type CIRRUS_GD5430 = &device_creator<cirrus_gd5430_device>;
+
+
+cirrus_gd5428_device::cirrus_gd5428_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : svga_device(mconfig, CIRRUS_GD5428, "Cirrus Logic GD5428", tag, owner, clock, "clgd5428", __FILE__)
+{
+}
+
+cirrus_gd5428_device::cirrus_gd5428_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : svga_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+cirrus_gd5430_device::cirrus_gd5430_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : cirrus_gd5428_device(mconfig, CIRRUS_GD5430, "Cirrus Logic GD5430", tag, owner, clock, "clgd5430", __FILE__)
+{
+}
+
+MACHINE_CONFIG_FRAGMENT( pcvideo_cirrus_gd5428 )
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_25_1748MHz,900,0,640,526,0,480)
+ MCFG_SCREEN_UPDATE_DEVICE("vga", cirrus_gd5428_device, screen_update)
+
+ MCFG_PALETTE_ADD("palette", 0x100)
+ MCFG_DEVICE_ADD("vga", CIRRUS_GD5428, 0)
+MACHINE_CONFIG_END
+
+MACHINE_CONFIG_FRAGMENT( pcvideo_cirrus_gd5430 )
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_25_1748MHz,900,0,640,526,0,480)
+ MCFG_SCREEN_UPDATE_DEVICE("vga", cirrus_gd5430_device, screen_update)
+
+ MCFG_PALETTE_ADD("palette", 0x100)
+ MCFG_DEVICE_ADD("vga", CIRRUS_GD5430, 0)
+MACHINE_CONFIG_END
+
+void cirrus_gd5428_device::device_start()
+{
+ zero();
+
+ int i;
+ for (i = 0; i < 0x100; i++)
+ m_palette->set_pen_color(i, 0, 0, 0);
+
+ // Avoid an infinite loop when displaying. 0 is not possible anyway.
+ vga.crtc.maximum_scan_line = 1;
+
+ // copy over interfaces
+ vga.read_dipswitch = read8_delegate(); //read_dipswitch;
+ vga.svga_intf.seq_regcount = 0x1f;
+ vga.svga_intf.crtc_regcount = 0x2d;
+ vga.svga_intf.vram_size = 0x200000;
+
+ vga.memory.resize(vga.svga_intf.vram_size);
+ memset(&vga.memory[0], 0, vga.svga_intf.vram_size);
+ save_item(NAME(vga.memory));
+ save_pointer(vga.crtc.data,"CRTC Registers",0x100);
+ save_pointer(vga.sequencer.data,"Sequencer Registers",0x100);
+ save_pointer(vga.attribute.data,"Attribute Registers", 0x15);
+ save_item(NAME(m_chip_id));
+
+ m_vblank_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(vga_device::vblank_timer_cb),this));
+
+ m_chip_id = 0x98; // GD5428 - Rev 0
+}
+
+void cirrus_gd5430_device::device_start()
+{
+ cirrus_gd5428_device::device_start();
+ m_chip_id = 0xa0; // GD5430 - Rev 0
+}
+
+void cirrus_gd5428_device::device_reset()
+{
+ vga_device::device_reset();
+ gc_locked = true;
+ gc_mode_ext = 0;
+ gc_bank_0 = gc_bank_1 = 0;
+ m_lock_reg = 0;
+ m_blt_status = 0;
+ m_cursor_attr = 0x00; // disable hardware cursor and extra palette
+ m_cursor_x = m_cursor_y = 0;
+ m_cursor_addr = 0;
+ m_scratchpad1 = m_scratchpad2 = m_scratchpad3 = 0;
+ m_cr19 = m_cr1a = m_cr1b = 0;
+ m_vclk_num[0] = 0x4a;
+ m_vclk_denom[0] = 0x2b;
+ m_vclk_num[1] = 0x5b;
+ m_vclk_denom[1] = 0x2f;
+ m_blt_source = m_blt_dest = m_blt_source_current = m_blt_dest_current = 0;
+ memset(m_ext_palette, 0, sizeof(m_ext_palette));
+ m_ext_palette_enabled = false;
+ m_blt_system_transfer = false;
+}
+
+UINT32 cirrus_gd5428_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int x,y,bit;
+ UINT32 ptr = (vga.svga_intf.vram_size - 0x4000); // cursor patterns are stored in the last 16kB of VRAM
+ svga_device::screen_update(screen, bitmap, cliprect);
+
+ /*UINT8 cur_mode =*/ pc_vga_choosevideomode();
+ if(m_cursor_attr & 0x01) // hardware cursor enabled
+ {
+ // draw hardware graphics cursor
+ if(m_cursor_attr & 0x04) // 64x64
+ {
+ ptr += ((m_cursor_addr & 0x3c) * 256);
+ for(y=0;y<64;y++)
+ {
+ for(x=0;x<64;x+=8)
+ {
+ for(bit=0;bit<8;bit++)
+ {
+ UINT8 pixel1 = vga.memory[ptr % vga.svga_intf.vram_size] >> (7-bit);
+ UINT8 pixel2 = vga.memory[(ptr+512) % vga.svga_intf.vram_size] >> (7-bit);
+ UINT8 output = ((pixel1 & 0x01) << 1) | (pixel2 & 0x01);
+ switch(output)
+ {
+ case 0: // transparent - do nothing
+ break;
+ case 1: // background
+ bitmap.pix32(m_cursor_y+y,m_cursor_x+x+bit) = (m_ext_palette[0].red << 16) | (m_ext_palette[0].green << 8) | (m_ext_palette[0].blue);
+ break;
+ case 2: // XOR
+ bitmap.pix32(m_cursor_y+y,m_cursor_x+x+bit) = ~bitmap.pix32(m_cursor_y+y,m_cursor_x+x+bit);
+ break;
+ case 3: // foreground
+ bitmap.pix32(m_cursor_y+y,m_cursor_x+x+bit) = (m_ext_palette[15].red << 16) | (m_ext_palette[15].green << 8) | (m_ext_palette[15].blue);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ ptr += ((m_cursor_addr & 0x3f) * 256);
+ for(y=0;y<32;y++)
+ {
+ for(x=0;x<32;x+=8)
+ {
+ for(bit=0;bit<8;bit++)
+ {
+ UINT8 pixel1 = vga.memory[ptr % vga.svga_intf.vram_size] >> (7-bit);
+ UINT8 pixel2 = vga.memory[(ptr+128) % vga.svga_intf.vram_size] >> (7-bit);
+ UINT8 output = ((pixel1 & 0x01) << 1) | (pixel2 & 0x01);
+ switch(output)
+ {
+ case 0: // transparent - do nothing
+ break;
+ case 1: // background
+ bitmap.pix32(m_cursor_y+y,m_cursor_x+x+bit) = (m_ext_palette[0].red << 18) | (m_ext_palette[0].green << 10) | (m_ext_palette[0].blue << 2);
+ break;
+ case 2: // XOR
+ bitmap.pix32(m_cursor_y+y,m_cursor_x+x+bit) = ~bitmap.pix32(m_cursor_y+y,m_cursor_x+x+bit);
+ break;
+ case 3: // foreground
+ bitmap.pix32(m_cursor_y+y,m_cursor_x+x+bit) = (m_ext_palette[15].red << 18) | (m_ext_palette[15].green << 10) | (m_ext_palette[15].blue << 2);
+ break;
+ }
+ }
+ ptr++;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void cirrus_gd5428_device::cirrus_define_video_mode()
+{
+ UINT8 divisor = 1;
+ float clock;
+ UINT8 clocksel = (vga.miscellaneous_output & 0xc) >> 2;
+
+ svga.rgb8_en = 0;
+ svga.rgb15_en = 0;
+ svga.rgb16_en = 0;
+ svga.rgb24_en = 0;
+ svga.rgb32_en = 0;
+
+ if(gc_locked || m_vclk_num[clocksel] == 0 || m_vclk_denom[clocksel] == 0)
+ clock = (vga.miscellaneous_output & 0xc) ? XTAL_28_63636MHz : XTAL_25_1748MHz;
+ else
+ {
+ int numerator = m_vclk_num[clocksel] & 0x7f;
+ int denominator = (m_vclk_denom[clocksel] & 0x3e) >> 1;
+ int mul = m_vclk_denom[clocksel] & 0x01 ? 2 : 1;
+ clock = 14.31818f * ((float)numerator / ((float)denominator * mul));
+ clock *= 1000000;
+ }
+
+ if (!gc_locked && (vga.sequencer.data[0x07] & 0x01))
+ {
+ switch(vga.sequencer.data[0x07] & 0x06) // bit 3 is reserved on GD542x
+ {
+ case 0x00: svga.rgb8_en = 1; break;
+ case 0x02: svga.rgb16_en = 1; clock /= 2; break; // Clock / 2 for 16-bit data
+ case 0x04: svga.rgb24_en = 1; clock /= 3; break; // Clock / 3 for 24-bit data
+ case 0x06: svga.rgb16_en = 1; divisor = 2; break; // Clock rate for 16-bit data
+ }
+ }
+ recompute_params_clock(divisor, (int)clock);
+}
+
+UINT16 cirrus_gd5428_device::offset()
+{
+ UINT16 off = vga_device::offset();
+
+ if (svga.rgb8_en == 1) // guess
+ off <<= 2;
+ if (svga.rgb16_en == 1)
+ off <<= 2;
+ if (svga.rgb24_en == 1)
+ off <<= 2;
+ if (svga.rgb32_en == 1)
+ off <<= 2;
+// popmessage("Offset: %04x %s %s ** -- actual: %04x",vga.crtc.offset,vga.crtc.dw?"DW":"--",vga.crtc.word_mode?"BYTE":"WORD",off);
+ return off;
+}
+
+void cirrus_gd5428_device::start_bitblt()
+{
+ UINT32 x,y;
+
+ if(m_blt_mode & 0x01)
+ {
+ start_reverse_bitblt();
+ return;
+ }
+
+ if(LOG_BLIT) logerror("CL: BitBLT started: Src: %06x Dst: %06x Width: %i Height %i ROP: %02x Mode: %02x\n",m_blt_source,m_blt_dest,m_blt_width,m_blt_height,m_blt_rop,m_blt_mode);
+
+ m_blt_source_current = m_blt_source;
+ m_blt_dest_current = m_blt_dest;
+
+ for(y=0;y<=m_blt_height;y++)
+ {
+ for(x=0;x<=m_blt_width;x++)
+ {
+ if(m_blt_mode & 0x80) // colour expand
+ {
+ if(m_blt_mode & 0x10) // 16-bit colour expansion / transparency width
+ {
+ // use GR0/1/10/11 background/foreground regs
+ UINT16 pixel = (vga.memory[m_blt_source_current % vga.svga_intf.vram_size] >> (7-((x/2) % 8)) & 0x01) ? ((m_gr11 << 8) | vga.gc.enable_set_reset) : ((m_gr10 << 8) | vga.gc.set_reset);
+
+ if(m_blt_dest_current & 1)
+ copy_pixel(pixel >> 8, vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ else
+ copy_pixel(pixel & 0xff, vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ if((x % 8) == 7 && !(m_blt_mode & 0x40)) // don't increment if a pattern (it's only 8 bits)
+ m_blt_source_current++;
+ }
+ else
+ {
+ UINT8 pixel = (vga.memory[m_blt_source_current % vga.svga_intf.vram_size] >> (7-(x % 8)) & 0x01) ? vga.gc.enable_set_reset : vga.gc.set_reset; // use GR0/1/10/11 background/foreground regs
+
+ copy_pixel(pixel, vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ if((x % 8) == 7 && !(m_blt_mode & 0x40)) // don't increment if a pattern (it's only 8 bits)
+ m_blt_source_current++;
+ }
+ }
+ else
+ {
+ copy_pixel(vga.memory[m_blt_source_current % vga.svga_intf.vram_size], vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ m_blt_source_current++;
+ }
+
+ m_blt_dest_current++;
+ if(m_blt_mode & 0x40 && (x % 8) == 7) // 8x8 pattern - reset pattern source location
+ {
+ if(m_blt_mode & 0x80) // colour expand
+ m_blt_source_current = m_blt_source + (1*(y % 8)); // patterns are linear data
+ else if(svga.rgb15_en || svga.rgb16_en)
+ {
+ if(m_blt_mode & 0x40 && (x % 16) == 15)
+ m_blt_source_current = m_blt_source + (16*(y % 8));
+ }
+ else
+ m_blt_source_current = m_blt_source + (8*(y % 8));
+ }
+ }
+ if(m_blt_mode & 0x40) // 8x8 pattern
+ {
+ if(m_blt_mode & 0x80) // colour expand
+ m_blt_source_current = m_blt_source + (1*(y % 8)); // patterns are linear data
+ else if(svga.rgb15_en || svga.rgb16_en)
+ {
+ if(m_blt_mode & 0x40 && (x % 16) == 15)
+ m_blt_source_current = m_blt_source + (16*(y % 8));
+ }
+ else
+ m_blt_source_current = m_blt_source + (8*(y % 8));
+ }
+ else
+ m_blt_source_current = m_blt_source + (m_blt_source_pitch*(y+1));
+ m_blt_dest_current = m_blt_dest + (m_blt_dest_pitch*(y+1));
+ }
+ m_blt_status &= ~0x02;
+}
+
+void cirrus_gd5428_device::start_reverse_bitblt()
+{
+ UINT32 x,y;
+
+ if(LOG_BLIT) logerror("CL: Reverse BitBLT started: Src: %06x Dst: %06x Width: %i Height %i ROP: %02x Mode: %02x\n",m_blt_source,m_blt_dest,m_blt_width,m_blt_height,m_blt_rop,m_blt_mode);
+
+ // Start at end of blit
+ m_blt_source_current = m_blt_source;
+ m_blt_dest_current = m_blt_dest;
+
+ for(y=0;y<=m_blt_height;y++)
+ {
+ for(x=0;x<=m_blt_width;x++)
+ {
+ if(m_blt_mode & 0x80) // colour expand
+ {
+ if(m_blt_mode & 0x10) // 16-bit colour expansion / transparency width
+ {
+ // use GR0/1/10/11 background/foreground regs
+ UINT16 pixel = (vga.memory[m_blt_source_current % vga.svga_intf.vram_size] >> (7-((x/2) % 8)) & 0x01) ? ((m_gr11 << 8) | vga.gc.enable_set_reset) : ((m_gr10 << 8) | vga.gc.set_reset);
+
+ if(m_blt_dest_current & 1)
+ copy_pixel(pixel >> 8, vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ else
+ copy_pixel(pixel & 0xff, vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ if((x % 8) == 7 && !(m_blt_mode & 0x40)) // don't increment if a pattern (it's only 8 bits)
+ m_blt_source_current--;
+ }
+ else
+ {
+ UINT8 pixel = (vga.memory[m_blt_source_current % vga.svga_intf.vram_size] >> (7-(x % 8)) & 0x01) ? vga.gc.enable_set_reset : vga.gc.set_reset; // use GR0/1/10/11 background/foreground regs
+
+ copy_pixel(pixel, vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ if((x % 8) == 7 && !(m_blt_mode & 0x40)) // don't decrement if a pattern (it's only 8 bits)
+ m_blt_source_current--;
+ }
+ }
+ else
+ {
+ copy_pixel(vga.memory[m_blt_source_current % vga.svga_intf.vram_size], vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ m_blt_source_current--;
+ }
+ m_blt_dest_current--;
+ if(m_blt_mode & 0x40 && (x % 8) == 7) // 8x8 pattern - reset pattern source location
+ {
+ if(m_blt_mode & 0x80) // colour expand
+ m_blt_source_current = m_blt_source - (1*(y % 8)); // patterns are linear data
+ else if(svga.rgb15_en || svga.rgb16_en)
+ {
+ if(m_blt_mode & 0x40 && (x % 16) == 15)
+ m_blt_source_current = m_blt_source - (16*(y % 8));
+ }
+ else
+ m_blt_source_current = m_blt_source - (8*(y % 8));
+ }
+ }
+ if(m_blt_mode & 0x40) // 8x8 pattern
+ {
+ if(m_blt_mode & 0x80) // colour expand
+ m_blt_source_current = m_blt_source - (1*(y % 8)); // patterns are linear data
+ else if(svga.rgb15_en || svga.rgb16_en)
+ {
+ if(m_blt_mode & 0x40 && (x % 16) == 15)
+ m_blt_source_current = m_blt_source - (16*(y % 8));
+ }
+ else
+ m_blt_source_current = m_blt_source - (8*(y % 8));
+ }
+ else
+ m_blt_source_current = m_blt_source - (m_blt_source_pitch*(y+1));
+ m_blt_dest_current = m_blt_dest - (m_blt_dest_pitch*(y+1));
+ }
+ m_blt_status &= ~0x02;
+}
+
+void cirrus_gd5428_device::start_system_bitblt()
+{
+ if(LOG_BLIT) logerror("CL: BitBLT from system memory started: Src: %06x Dst: %06x Width: %i Height %i ROP: %02x Mode: %02x\n",m_blt_source,m_blt_dest,m_blt_width,m_blt_height,m_blt_rop,m_blt_mode);
+ m_blt_system_transfer = true;
+ m_blt_system_count = 0;
+ m_blt_system_buffer = 0;
+ m_blt_pixel_count = m_blt_scan_count = 0;
+ m_blt_source_current = m_blt_source;
+ m_blt_dest_current = m_blt_dest;
+ m_blt_status |= 0x09;
+}
+
+// non colour-expanded BitBLTs from system memory must be doubleword sized, extra bytes are ignored
+void cirrus_gd5428_device::blit_dword()
+{
+ // TODO: add support for reverse direction
+ UINT8 x,pixel;
+
+ for(x=0;x<32;x+=8)
+ {
+ pixel = ((m_blt_system_buffer & (0x000000ff << x)) >> x);
+ if(m_blt_pixel_count <= m_blt_width)
+ copy_pixel(pixel,vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ m_blt_dest_current++;
+ m_blt_pixel_count++;
+ }
+ if(m_blt_pixel_count > m_blt_width)
+ {
+ m_blt_pixel_count = 0;
+ m_blt_scan_count++;
+ m_blt_dest_current = m_blt_dest + (m_blt_dest_pitch*m_blt_scan_count);
+ }
+ if(m_blt_scan_count > m_blt_height)
+ {
+ m_blt_system_transfer = false; // BitBLT complete
+ m_blt_status &= ~0x0b;
+ }
+}
+
+// colour-expanded BitBLTs from system memory are on a byte boundary, unused bits are ignored
+void cirrus_gd5428_device::blit_byte()
+{
+ // TODO: add support for reverse direction
+ UINT8 x,pixel;
+
+ for(x=0;x<8;x++)
+ {
+ // use GR0/1/10/11 background/foreground regs
+ if(m_blt_dest_current & 1)
+ pixel = ((m_blt_system_buffer & (0x00000001 << (7-x))) >> (7-x)) ? m_gr11 : m_gr10;
+ else
+ pixel = ((m_blt_system_buffer & (0x00000001 << (7-x))) >> (7-x)) ? vga.gc.enable_set_reset : vga.gc.set_reset;
+ if(m_blt_pixel_count <= m_blt_width - 1)
+ copy_pixel(pixel,vga.memory[m_blt_dest_current % vga.svga_intf.vram_size]);
+ m_blt_dest_current++;
+ m_blt_pixel_count++;
+ }
+ if(m_blt_pixel_count > m_blt_width)
+ {
+ m_blt_pixel_count = 0;
+ m_blt_scan_count++;
+ m_blt_dest_current = m_blt_dest + (m_blt_dest_pitch*m_blt_scan_count);
+ }
+ if(m_blt_scan_count > m_blt_height)
+ {
+ m_blt_system_transfer = false; // BitBLT complete
+ m_blt_status &= ~0x0b;
+ }
+}
+
+void cirrus_gd5428_device::copy_pixel(UINT8 src, UINT8 dst)
+{
+ UINT8 res = src;
+
+ switch(m_blt_rop)
+ {
+ case 0x00: // BLACK
+ res = 0x00;
+ break;
+ case 0x0b: // DSTINVERT
+ res = ~dst;
+ break;
+ case 0x0d: // SRC
+ res = src;
+ break;
+ case 0x0e: // WHITE
+ res = 0xff;
+ break;
+ case 0x59: // SRCINVERT
+ res = src ^ dst;
+ break;
+ default:
+ popmessage("CL: Unsupported BitBLT ROP mode %02x",m_blt_rop);
+ }
+
+ // handle transparency compare
+ if(m_blt_mode & 0x08) // TODO: 16-bit compare
+ {
+ // if ROP result matches the transparency colour, don't change the pixel
+ if((res & (~m_blt_trans_colour_mask & 0xff)) == ((m_blt_trans_colour & 0xff) & (~m_blt_trans_colour_mask & 0xff)))
+ return;
+ }
+
+ vga.memory[m_blt_dest_current % vga.svga_intf.vram_size] = res;
+}
+
+UINT8 cirrus_gd5428_device::cirrus_seq_reg_read(UINT8 index)
+{
+ UINT8 res;
+
+ res = 0xff;
+
+ switch(index)
+ {
+ case 0x02:
+ if(gc_mode_ext & 0x08)
+ res = vga.sequencer.map_mask & 0xff;
+ else
+ res = vga.sequencer.map_mask & 0x0f;
+ break;
+ case 0x06:
+ if(gc_locked)
+ return 0x0f;
+ else
+ return m_lock_reg;
+ break;
+ case 0x09:
+ //printf("%02x\n",index);
+ res = vga.sequencer.data[index];
+ break;
+ case 0x0a:
+ res = m_scratchpad1;
+ break;
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ res = m_vclk_num[index-0x0b];
+ break;
+ case 0x0f:
+ res = vga.sequencer.data[index] & 0xe7;
+ res |= 0x18; // 32-bit DRAM data bus width (1MB-2MB)
+ break;
+ case 0x12:
+ res = m_cursor_attr;
+ break;
+ case 0x14:
+ res = m_scratchpad2;
+ break;
+ case 0x15:
+ res = m_scratchpad3;
+ break;
+ case 0x1b:
+ case 0x1c:
+ case 0x1d:
+ case 0x1e:
+ res = m_vclk_denom[index-0x1b];
+ break;
+ default:
+ res = vga.sequencer.data[index];
+ }
+
+ return res;
+}
+
+void cirrus_gd5428_device::cirrus_seq_reg_write(UINT8 index, UINT8 data)
+{
+ if(LOG_REG) logerror("CL: SEQ write %02x to SR%02x\n",data,index);
+ switch(index)
+ {
+ case 0x02:
+ if(gc_mode_ext & 0x08)
+ vga.sequencer.map_mask = data & 0xff;
+ else
+ vga.sequencer.map_mask = data & 0x0f;
+ break;
+ case 0x06:
+ // Note: extensions are always enabled on the GD5429
+ if((data & 0x17) == 0x12) // bits 3,5,6,7 ignored
+ {
+ gc_locked = false;
+ logerror("Cirrus register extensions unlocked\n");
+ }
+ else
+ {
+ gc_locked = true;
+ logerror("Cirrus register extensions locked\n");
+ }
+ m_lock_reg = data & 0x17;
+ break;
+ case 0x07:
+ if((data & 0xf0) != 0)
+ popmessage("1MB framebuffer window enabled at %iMB (%02x)",data >> 4,data);
+ vga.sequencer.data[vga.sequencer.index] = data;
+ break;
+ case 0x09:
+ //printf("%02x %02x\n",index,data);
+ vga.sequencer.data[vga.sequencer.index] = data;
+ break;
+ case 0x0a:
+ m_scratchpad1 = data; // GD5402/GD542x BIOS writes VRAM size here.
+ break;
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ m_vclk_num[index-0x0b] = data;
+ break;
+ case 0x10:
+ case 0x30:
+ case 0x50:
+ case 0x70:
+ case 0x90:
+ case 0xb0:
+ case 0xd0:
+ case 0xf0: // bits 5-7 of the register index are the low bits of the X co-ordinate
+ m_cursor_x = (data << 3) | ((index & 0xe0) >> 5);
+ break;
+ case 0x11:
+ case 0x31:
+ case 0x51:
+ case 0x71:
+ case 0x91:
+ case 0xb1:
+ case 0xd1:
+ case 0xf1: // bits 5-7 of the register index are the low bits of the Y co-ordinate
+ m_cursor_y = (data << 3) | ((index & 0xe0) >> 5);
+ break;
+ case 0x12:
+ // bit 0 - enable cursor
+ // bit 1 - enable extra palette (cursor colours are there)
+ // bit 2 - 64x64 cursor (32x32 if clear, GD5422+)
+ // bit 7 - overscan colour protect - if set, use colour 2 in the extra palette for the border (GD5424+)
+ m_cursor_attr = data;
+ m_ext_palette_enabled = data & 0x02;
+ break;
+ case 0x13:
+ m_cursor_addr = data; // bits 0 and 1 are ignored if using 64x64 cursor
+ break;
+ case 0x14:
+ m_scratchpad2 = data;
+ break;
+ case 0x15:
+ m_scratchpad3 = data; // GD543x BIOS writes VRAM size here
+ break;
+ case 0x1b:
+ case 0x1c:
+ case 0x1d:
+ case 0x1e:
+ m_vclk_denom[index-0x1b] = data;
+ break;
+ default:
+ vga.sequencer.data[vga.sequencer.index] = data;
+ seq_reg_write(vga.sequencer.index,data);
+ }
+}
+
+UINT8 cirrus_gd5428_device::cirrus_gc_reg_read(UINT8 index)
+{
+ UINT8 res = 0xff;
+
+ switch(index)
+ {
+ case 0x00:
+ if(gc_mode_ext & 0x04)
+ res = vga.gc.set_reset & 0xff;
+ else
+ res = vga.gc.set_reset & 0x0f;
+ break;
+ case 0x01:
+ if(gc_mode_ext & 0x04)
+ res = vga.gc.enable_set_reset & 0xff;
+ else
+ res = vga.gc.enable_set_reset & 0x0f;
+ break;
+ case 0x05:
+ res = (vga.gc.shift256 & 1) << 6;
+ res |= (vga.gc.shift_reg & 1) << 5;
+ res |= (vga.gc.host_oe & 1) << 4;
+ res |= (vga.gc.read_mode & 1) << 3;
+ if(gc_mode_ext & 0x04)
+ res |= (vga.gc.write_mode & 7);
+ else
+ res |= (vga.gc.write_mode & 3);
+ break;
+ case 0x09: // Offset register 0
+ res = gc_bank_0;
+ break;
+ case 0x0a: // Offset register 1
+ res = gc_bank_1;
+ break;
+ case 0x0b: // Graphics controller mode extensions
+ res = gc_mode_ext;
+ break;
+ case 0x0c: // Colour Key
+ break;
+ case 0x0d: // Colour Key Mask
+ break;
+ case 0x0e: // Miscellaneous Control
+ break;
+ case 0x10: // Background Colour Byte 1
+ res = m_gr10;
+ break;
+ case 0x11: // Foreground Colour Byte 1
+ res = m_gr11;
+ break;
+ case 0x20: // BLT Width 0
+ res = m_blt_width & 0x00ff;
+ break;
+ case 0x21: // BLT Width 1
+ res = m_blt_width >> 8;
+ break;
+ case 0x22: // BLT Height 0
+ res = m_blt_height & 0x00ff;
+ break;
+ case 0x23: // BLT Height 1
+ res = m_blt_height >> 8;
+ break;
+ case 0x24: // BLT Destination Pitch 0
+ res = m_blt_dest_pitch & 0x00ff;
+ break;
+ case 0x25: // BLT Destination Pitch 1
+ res = m_blt_dest_pitch >> 8;
+ break;
+ case 0x26: // BLT Source Pitch 0
+ res = m_blt_source_pitch & 0x00ff;
+ break;
+ case 0x27: // BLT Source Pitch 1
+ res = m_blt_source_pitch >> 8;
+ break;
+ case 0x28: // BLT Destination start 0
+ res = m_blt_dest & 0x000000ff;
+ break;
+ case 0x29: // BLT Destination start 1
+ res = (m_blt_dest & 0x0000ff00) >> 8;
+ break;
+ case 0x2a: // BLT Destination start 2
+ res = (m_blt_dest & 0x00ff0000) >> 16;
+ break;
+ case 0x2c: // BLT source start 0
+ res = m_blt_source & 0x000000ff;
+ break;
+ case 0x2d: // BLT source start 1
+ res = (m_blt_source & 0x0000ff00) >> 8;
+ break;
+ case 0x2e: // BLT source start 2
+ res = (m_blt_source & 0x00ff0000) >> 16;
+ break;
+ case 0x2f: // BLT destination write mask (GD5430/36/40)
+ // TODO
+ break;
+ case 0x30: // BLT Mode
+ res = m_blt_mode;
+ break;
+ case 0x31: // BitBLT Start / Status
+ res = m_blt_status;
+ break;
+ case 0x32: // BitBLT ROP mode
+ res = m_blt_rop;
+ break;
+ case 0x34: // BitBLT Transparent Colour
+ res = m_blt_trans_colour & 0x00ff;
+ break;
+ case 0x35:
+ res = m_blt_trans_colour >> 8;
+ break;
+ case 0x36: // BitBLT Transparent Colour Mask
+ res = m_blt_trans_colour_mask & 0x00ff;
+ break;
+ case 0x37:
+ res = m_blt_trans_colour_mask >> 8;
+ break;
+ default:
+ res = gc_reg_read(index);
+ }
+
+ return res;
+}
+
+void cirrus_gd5428_device::cirrus_gc_reg_write(UINT8 index, UINT8 data)
+{
+ if(LOG_REG) logerror("CL: GC write %02x to GR%02x\n",data,index);
+ switch(index)
+ {
+ case 0x00: // if extended writes are enabled (bit 2 of index 0bh), then index 0 and 1 are extended to 8 bits, however XFree86 does not appear to do this...
+ vga.gc.set_reset = data & 0xff;
+ break;
+ case 0x01:
+ vga.gc.enable_set_reset = data & 0xff;
+ break;
+ case 0x05:
+ vga.gc.shift256 = (data & 0x40) >> 6;
+ vga.gc.shift_reg = (data & 0x20) >> 5;
+ vga.gc.host_oe = (data & 0x10) >> 4;
+ vga.gc.read_mode = (data & 8) >> 3;
+ if(gc_mode_ext & 0x04)
+ vga.gc.write_mode = data & 7;
+ else
+ vga.gc.write_mode = data & 3;
+ break;
+ case 0x09: // Offset register 0
+ gc_bank_0 = data;
+ logerror("CL: Offset register 0 set to %i\n",data);
+ break;
+ case 0x0a: // Offset register 1
+ gc_bank_1 = data;
+ logerror("CL: Offset register 1 set to %i\n",data);
+ break;
+ case 0x0b: // Graphics controller mode extensions
+ gc_mode_ext = data;
+ if(!(data & 0x04))
+ {
+ vga.gc.set_reset &= 0x0f;
+ vga.gc.enable_set_reset &= 0x0f;
+ }
+ if(!(data & 0x08))
+ vga.sequencer.map_mask &= 0x0f;
+ break;
+ case 0x0c: // Colour Key
+ break;
+ case 0x0d: // Colour Key Mask
+ break;
+ case 0x0e: // Miscellaneous Control
+ break;
+ case 0x10: // Background Colour Byte 1
+ m_gr10 = data;
+ break;
+ case 0x11: // Foreground Colour Byte 1
+ m_gr11 = data;
+ break;
+ case 0x20: // BLT Width 0
+ m_blt_width = (m_blt_width & 0xff00) | data;
+ break;
+ case 0x21: // BLT Width 1
+ m_blt_width = (m_blt_width & 0x00ff) | (data << 8);
+ break;
+ case 0x22: // BLT Height 0
+ m_blt_height = (m_blt_height & 0xff00) | data;
+ break;
+ case 0x23: // BLT Height 1
+ m_blt_height = (m_blt_height & 0x00ff) | (data << 8);
+ break;
+ case 0x24: // BLT Destination Pitch 0
+ m_blt_dest_pitch = (m_blt_dest_pitch & 0xff00) | data;
+ break;
+ case 0x25: // BLT Destination Pitch 1
+ m_blt_dest_pitch = (m_blt_dest_pitch & 0x00ff) | (data << 8);
+ break;
+ case 0x26: // BLT Source Pitch 0
+ m_blt_source_pitch = (m_blt_source_pitch & 0xff00) | data;
+ break;
+ case 0x27: // BLT Source Pitch 1
+ m_blt_source_pitch = (m_blt_source_pitch & 0x00ff) | (data << 8);
+ break;
+ case 0x28: // BLT Destination start 0
+ m_blt_dest = (m_blt_dest & 0xffffff00) | data;
+ break;
+ case 0x29: // BLT Destination start 1
+ m_blt_dest = (m_blt_dest & 0xffff00ff) | (data << 8);
+ break;
+ case 0x2a: // BLT Destination start 2
+ m_blt_dest = (m_blt_dest & 0xff00ffff) | (data << 16);
+ break;
+ case 0x2c: // BLT source start 0
+ m_blt_source = (m_blt_source & 0xffffff00) | data;
+ break;
+ case 0x2d: // BLT source start 1
+ m_blt_source = (m_blt_source & 0xffff00ff) | (data << 8);
+ break;
+ case 0x2e: // BLT source start 2
+ m_blt_source = (m_blt_source & 0xff00ffff) | (data << 16);
+ break;
+ case 0x2f: // BLT destination write mask (GD5430/36/40)
+ // TODO
+ break;
+ case 0x30: // BLT Mode
+ m_blt_mode = data;
+ break;
+ case 0x31: // BitBLT Start / Status
+ m_blt_status = data & ~0xf2;
+ if(data & 0x02)
+ {
+ if(m_blt_mode & 0x04) // blit source is system memory
+ start_system_bitblt();
+ else
+ start_bitblt();
+ }
+ break;
+ case 0x32: // BitBLT ROP mode
+ m_blt_rop = data;
+ break;
+ case 0x34: // BitBLT Transparent Colour
+ m_blt_trans_colour = (m_blt_trans_colour & 0xff00) | data;
+ break;
+ case 0x35:
+ m_blt_trans_colour = (m_blt_trans_colour & 0x00ff) | (data << 8);
+ break;
+ case 0x36: // BitBLT Transparent Colour Mask
+ m_blt_trans_colour_mask = (m_blt_trans_colour_mask & 0xff00) | data;
+ break;
+ case 0x37:
+ m_blt_trans_colour_mask = (m_blt_trans_colour_mask & 0x00ff) | (data << 8);
+ break;
+ default:
+ gc_reg_write(index,data);
+ }
+}
+
+READ8_MEMBER(cirrus_gd5428_device::port_03c0_r)
+{
+ UINT8 res = 0xff;
+
+ switch(offset)
+ {
+ case 0x05:
+ res = cirrus_seq_reg_read(vga.sequencer.index);
+ break;
+ case 0x09:
+ if(!m_ext_palette_enabled)
+ res = vga_device::port_03c0_r(space,offset,mem_mask);
+ else
+ {
+ if (vga.dac.read)
+ {
+ switch (vga.dac.state++)
+ {
+ case 0:
+ res = m_ext_palette[vga.dac.read_index & 0x0f].red;
+ break;
+ case 1:
+ res = m_ext_palette[vga.dac.read_index & 0x0f].green;
+ break;
+ case 2:
+ res = m_ext_palette[vga.dac.read_index & 0x0f].blue;
+ break;
+ }
+
+ if (vga.dac.state==3)
+ {
+ vga.dac.state = 0;
+ vga.dac.read_index++;
+ }
+ }
+ }
+ break;
+ case 0x0f:
+ res = cirrus_gc_reg_read(vga.gc.index);
+ break;
+ default:
+ res = vga_device::port_03c0_r(space,offset,mem_mask);
+ break;
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(cirrus_gd5428_device::port_03c0_w)
+{
+ switch(offset)
+ {
+ case 0x05:
+ cirrus_seq_reg_write(vga.sequencer.index,data);
+ break;
+ case 0x09:
+ if(!m_ext_palette_enabled)
+ vga_device::port_03c0_w(space,offset,data,mem_mask);
+ else
+ {
+ if (!vga.dac.read)
+ {
+ switch (vga.dac.state++) {
+ case 0:
+ m_ext_palette[vga.dac.write_index & 0x0f].red=data;
+ break;
+ case 1:
+ m_ext_palette[vga.dac.write_index & 0x0f].green=data;
+ break;
+ case 2:
+ m_ext_palette[vga.dac.write_index & 0x0f].blue=data;
+ break;
+ }
+ vga.dac.dirty=1;
+ if (vga.dac.state==3)
+ {
+ vga.dac.state=0;
+ vga.dac.write_index++;
+ }
+ }
+ }
+ break;
+ case 0x0f:
+ cirrus_gc_reg_write(vga.gc.index,data);
+ break;
+ default:
+ vga_device::port_03c0_w(space,offset,data,mem_mask);
+ break;
+ }
+ cirrus_define_video_mode();
+}
+
+READ8_MEMBER(cirrus_gd5428_device::port_03b0_r)
+{
+ UINT8 res = 0xff;
+
+ if (CRTC_PORT_ADDR == 0x3b0)
+ {
+ switch(offset)
+ {
+ case 5:
+ res = cirrus_crtc_reg_read(vga.crtc.index);
+ break;
+ default:
+ res = vga_device::port_03b0_r(space,offset,mem_mask);
+ break;
+ }
+ }
+
+ return res;
+}
+
+READ8_MEMBER(cirrus_gd5428_device::port_03d0_r)
+{
+ UINT8 res = 0xff;
+
+ if (CRTC_PORT_ADDR == 0x3d0)
+ {
+ switch(offset)
+ {
+ case 5:
+ res = cirrus_crtc_reg_read(vga.crtc.index);
+ break;
+ default:
+ res = vga_device::port_03d0_r(space,offset,mem_mask);
+ break;
+ }
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(cirrus_gd5428_device::port_03b0_w)
+{
+ if (CRTC_PORT_ADDR == 0x3b0)
+ {
+ switch(offset)
+ {
+ case 5:
+ vga.crtc.data[vga.crtc.index] = data;
+ cirrus_crtc_reg_write(vga.crtc.index,data);
+ break;
+ default:
+ vga_device::port_03b0_w(space,offset,data,mem_mask);
+ break;
+ }
+ }
+ cirrus_define_video_mode();
+}
+
+WRITE8_MEMBER(cirrus_gd5428_device::port_03d0_w)
+{
+ if (CRTC_PORT_ADDR == 0x3d0)
+ {
+ switch(offset)
+ {
+ case 5:
+ vga.crtc.data[vga.crtc.index] = data;
+ cirrus_crtc_reg_write(vga.crtc.index,data);
+ break;
+ default:
+ vga_device::port_03d0_w(space,offset,data,mem_mask);
+ break;
+ }
+ }
+ cirrus_define_video_mode();
+}
+
+UINT8 cirrus_gd5428_device::cirrus_crtc_reg_read(UINT8 index)
+{
+ UINT8 res = 0xff;
+
+ switch(index)
+ {
+ case 0x16: // VGA Vertical Blank end - some SVGA chipsets use all 8 bits, and this is one of them (according to MFGTST CRTC tests)
+ res = vga.crtc.vert_blank_end & 0x00ff;
+ break;
+ case 0x19:
+ res = m_cr19;
+ break;
+ case 0x1a:
+ res = m_cr1a;
+ break;
+ case 0x1b:
+ res = m_cr1b;
+ break;
+ case 0x27:
+ res = m_chip_id;
+ break;
+ default:
+ res = crtc_reg_read(index);
+ break;
+ }
+
+ return res;
+}
+
+void cirrus_gd5428_device::cirrus_crtc_reg_write(UINT8 index, UINT8 data)
+{
+ if(LOG_REG) logerror("CL: CRTC write %02x to CR%02x\n",data,index);
+ switch(index)
+ {
+ case 0x16: // VGA Vertical Blank end - some SVGA chipsets use all 8 bits, and this is one of them (according to MFGTST CRTC tests)
+ vga.crtc.vert_blank_end &= ~0x00ff;
+ vga.crtc.vert_blank_end |= data;
+ break;
+ case 0x19:
+ m_cr19 = data;
+ break;
+ case 0x1a:
+ m_cr1a = data;
+ vga.crtc.horz_blank_end = (vga.crtc.horz_blank_end & 0xff3f) | ((data & 0x30) << 2);
+ vga.crtc.vert_blank_end = (vga.crtc.vert_blank_end & 0xfcff) | ((data & 0xc0) << 2);
+ break;
+ case 0x1b:
+ m_cr1b = data;
+ vga.crtc.start_addr_latch &= ~0x070000;
+ vga.crtc.start_addr_latch |= ((data & 0x01) << 16);
+ vga.crtc.start_addr_latch |= ((data & 0x0c) << 15);
+ vga.crtc.offset = (vga.crtc.offset & 0x00ff) | ((data & 0x10) << 4);
+ cirrus_define_video_mode();
+ break;
+ case 0x1d:
+ //vga.crtc.start_addr_latch = (vga.crtc.start_addr_latch & 0xf7ffff) | ((data & 0x01) << 16); // GD543x
+ break;
+ case 0x27:
+ // Do nothing, read only
+ break;
+ default:
+ crtc_reg_write(index,data);
+ break;
+ }
+
+}
+
+inline UINT8 cirrus_gd5428_device::cirrus_vga_latch_write(int offs, UINT8 data)
+{
+ UINT8 res = 0;
+ UINT8 mode_mask = (gc_mode_ext & 0x04) ? 0x07 : 0x03;
+
+ switch (vga.gc.write_mode & mode_mask) {
+ case 0:
+ data = rotate_right(data);
+ if(vga.gc.enable_set_reset & 1<<offs)
+ res = vga_logical_op((vga.gc.set_reset & 1<<offs) ? vga.gc.bit_mask : 0, offs,vga.gc.bit_mask);
+ else
+ res = vga_logical_op(data, offs, vga.gc.bit_mask);
+ break;
+ case 1:
+ res = vga.gc.latch[offs];
+ break;
+ case 2:
+ res = vga_logical_op((data & 1<<offs) ? 0xff : 0x00,offs,vga.gc.bit_mask);
+ break;
+ case 3:
+ data = rotate_right(data);
+ res = vga_logical_op((vga.gc.set_reset & 1<<offs) ? 0xff : 0x00,offs,data&vga.gc.bit_mask);
+ break;
+ case 4:
+ res = vga.gc.latch[offs];
+ popmessage("CL: Unimplemented VGA write mode 4 enabled");
+ break;
+ case 5:
+ res = vga.gc.latch[offs];
+ popmessage("CL: Unimplemented VGA write mode 5 enabled");
+ break;
+ }
+
+ return res;
+}
+
+READ8_MEMBER(cirrus_gd5428_device::mem_r)
+{
+ UINT32 addr;
+ UINT8 bank;
+ UINT8 cur_mode = pc_vga_choosevideomode();
+
+ if(gc_locked || offset >= 0x10000 || cur_mode == TEXT_MODE || cur_mode == SCREEN_OFF)
+ return vga_device::mem_r(space,offset,mem_mask);
+
+ if(offset >= 0x8000 && offset < 0x10000 && (gc_mode_ext & 0x01)) // if accessing bank 1 (if enabled)
+ bank = gc_bank_1;
+ else
+ bank = gc_bank_0;
+
+ if(gc_mode_ext & 0x20) // 16kB bank granularity
+ addr = bank * 0x4000;
+ else // 4kB bank granularity
+ addr = bank * 0x1000;
+
+ // Is the display address adjusted automatically when not using Chain-4 addressing? The GD542x BIOS doesn't do it, but Virtual Pool expects it.
+ if(!(vga.sequencer.data[4] & 0x8))
+ addr <<= 2;
+
+ if(svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en)
+ {
+ UINT8 data = 0;
+ if(gc_mode_ext & 0x01)
+ {
+ if(offset & 0x10000)
+ return 0;
+ if(offset < 0x8000)
+ offset &= 0x7fff;
+ else
+ {
+ offset -= 0x8000;
+ offset &= 0x7fff;
+ }
+ }
+ else
+ offset &= 0xffff;
+
+ if(vga.sequencer.data[4] & 0x8)
+ data = vga.memory[(offset+addr) % vga.svga_intf.vram_size];
+ else
+ {
+ {
+ int i;
+
+ for(i=0;i<4;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ data |= vga.memory[((offset*4+i)+addr) % vga.svga_intf.vram_size];
+ }
+ }
+ return data;
+ }
+ }
+
+ switch(vga.gc.memory_map_sel & 0x03)
+ {
+ case 0: break;
+ case 1: if(gc_mode_ext & 0x01) offset &= 0x7fff; else offset &= 0x0ffff; break;
+ case 2: offset -= 0x10000; offset &= 0x07fff; break;
+ case 3: offset -= 0x18000; offset &= 0x07fff; break;
+ }
+
+ if(vga.sequencer.data[4] & 4)
+ {
+ int data;
+ if (!space.debugger_access())
+ {
+ vga.gc.latch[0]=vga.memory[(offset+addr) % vga.svga_intf.vram_size];
+ vga.gc.latch[1]=vga.memory[((offset+addr)+0x10000) % vga.svga_intf.vram_size];
+ vga.gc.latch[2]=vga.memory[((offset+addr)+0x20000) % vga.svga_intf.vram_size];
+ vga.gc.latch[3]=vga.memory[((offset+addr)+0x30000) % vga.svga_intf.vram_size];
+ }
+
+ if (vga.gc.read_mode)
+ {
+ UINT8 byte,layer;
+ UINT8 fill_latch;
+ data=0;
+
+ for(byte=0;byte<8;byte++)
+ {
+ fill_latch = 0;
+ for(layer=0;layer<4;layer++)
+ {
+ if(vga.gc.latch[layer] & 1 << byte)
+ fill_latch |= 1 << layer;
+ }
+ fill_latch &= vga.gc.color_dont_care;
+ if(fill_latch == vga.gc.color_compare)
+ data |= 1 << byte;
+ }
+ }
+ else
+ data=vga.gc.latch[vga.gc.read_map_sel];
+
+ return data;
+ }
+ else
+ {
+ // TODO: Lines up in 16-colour mode, likely different for 256-colour modes (docs say video addresses are shifted right 3 places)
+ UINT8 i,data;
+// UINT8 bits = ((gc_mode_ext & 0x08) && (vga.gc.write_mode == 1)) ? 8 : 4;
+
+ data = 0;
+ //printf("%08x\n",offset);
+
+ if(gc_mode_ext & 0x02)
+ {
+ for(i=0;i<8;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ data |= vga.memory[(((offset+addr))+i*0x10000) % vga.svga_intf.vram_size];
+ }
+ }
+ else
+ {
+ for(i=0;i<4;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ data |= vga.memory[(((offset+addr))+i*0x10000) % vga.svga_intf.vram_size];
+ }
+ }
+
+ return data;
+ }
+}
+
+WRITE8_MEMBER(cirrus_gd5428_device::mem_w)
+{
+ UINT32 addr;
+ UINT8 bank;
+ UINT8 cur_mode = pc_vga_choosevideomode();
+
+ if(m_blt_system_transfer)
+ {
+ if(m_blt_mode & 0x80) // colour expand
+ {
+ m_blt_system_buffer &= ~(0x000000ff);
+ m_blt_system_buffer |= data;
+ blit_byte();
+ m_blt_system_count = 0;
+ }
+ else
+ {
+ m_blt_system_buffer &= ~(0x000000ff << (m_blt_system_count * 8));
+ m_blt_system_buffer |= (data << (m_blt_system_count * 8));
+ m_blt_system_count++;
+ if(m_blt_system_count >= 4)
+ {
+ blit_dword();
+ m_blt_system_count = 0;
+ }
+ }
+ return;
+ }
+
+ if(gc_locked || offset >= 0x10000 || cur_mode == TEXT_MODE || cur_mode == SCREEN_OFF)
+ {
+ vga_device::mem_w(space,offset,data,mem_mask);
+ return;
+ }
+
+ if(offset >= 0x8000 && offset < 0x10000 && (gc_mode_ext & 0x01)) // if accessing bank 1 (if enabled)
+ bank = gc_bank_1;
+ else
+ bank = gc_bank_0;
+
+ if(gc_mode_ext & 0x20) // 16kB bank granularity
+ addr = bank * 0x4000;
+ else // 4kB bank granularity
+ addr = bank * 0x1000;
+
+ // Is the display address adjusted automatically when using Chain-4 addressing? The GD542x BIOS doesn't do it, but Virtual Pool expects it.
+ if(!(vga.sequencer.data[4] & 0x8))
+ addr <<= 2;
+
+ if(svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en)
+ {
+ if(offset & 0x10000)
+ return;
+ if(gc_mode_ext & 0x01)
+ {
+ if(offset < 0x8000)
+ offset &= 0x7fff;
+ else
+ {
+ offset -= 0x8000;
+ offset &= 0x7fff;
+ }
+ }
+ else
+ offset &= 0xffff;
+
+ // GR0 (and GR10 in 15/16bpp modes) = background colour in write mode 5
+ // GR1 (and GR11 in 15/16bpp modes) = foreground colour in write modes 4 or 5
+ if(vga.gc.write_mode == 4)
+ {
+ int i;
+
+ for(i=0;i<8;i++)
+ {
+ if(svga.rgb8_en)
+ {
+ if(data & (0x01 << (7-i)))
+ vga.memory[((addr+offset)*8+i) % vga.svga_intf.vram_size] = vga.gc.enable_set_reset;
+ }
+ else if(svga.rgb15_en || svga.rgb16_en)
+ {
+ if(data & (0x01 << (7-i)))
+ {
+ vga.memory[((addr+offset)*16+(i*2)) % vga.svga_intf.vram_size] = vga.gc.enable_set_reset;
+ vga.memory[((addr+offset)*16+(i*2)+1) % vga.svga_intf.vram_size] = m_gr11;
+ }
+ }
+ }
+ return;
+ }
+
+ if(vga.gc.write_mode == 5)
+ {
+ int i;
+
+ for(i=0;i<8;i++)
+ {
+ if(svga.rgb8_en)
+ {
+ if(data & (0x01 << (7-i)))
+ vga.memory[((addr+offset)*8+i) % vga.svga_intf.vram_size] = vga.gc.enable_set_reset;
+ else
+ vga.memory[((addr+offset)*8+i) % vga.svga_intf.vram_size] = vga.gc.set_reset;
+ }
+ else if(svga.rgb15_en || svga.rgb16_en)
+ {
+ if(data & (0x01 << (7-i)))
+ {
+ vga.memory[((addr+offset)*16+(i*2)) % vga.svga_intf.vram_size] = vga.gc.enable_set_reset;
+ vga.memory[((addr+offset)*16+(i*2)+1) % vga.svga_intf.vram_size] = m_gr11;
+ }
+ else
+ {
+ vga.memory[((addr+offset)*16+(i*2)) % vga.svga_intf.vram_size] = vga.gc.set_reset;
+ vga.memory[((addr+offset)*16+(i*2)+1) % vga.svga_intf.vram_size] = m_gr10;
+ }
+ }
+ }
+ return;
+ }
+
+ if(vga.sequencer.data[4] & 0x8)
+ vga.memory[(offset+addr) % vga.svga_intf.vram_size] = data;
+ else
+ {
+ int i;
+ for(i=0;i<4;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ vga.memory[((offset*4+i)+addr) % vga.svga_intf.vram_size] = data;
+ }
+ }
+ }
+ else
+ {
+ //Inside each case must prevent writes to non-mapped VGA memory regions, not only mask the offset.
+ switch(vga.gc.memory_map_sel & 0x03)
+ {
+ case 0: break;
+ case 1:
+ if(offset & 0x10000)
+ return;
+
+ if(gc_mode_ext & 0x01)
+ offset &= 0x7fff;
+ else
+ offset &= 0xffff;
+ break;
+ case 2:
+ if((offset & 0x18000) != 0x10000)
+ return;
+
+ offset &= 0x07fff;
+ break;
+ case 3:
+ if((offset & 0x18000) != 0x18000)
+ return;
+
+ offset &= 0x07fff;
+ break;
+ }
+
+ {
+ // TODO: Lines up in 16-colour mode, likely different for 256-colour modes (docs say video addresses are shifted right 3 places)
+ UINT8 i;
+// UINT8 bits = ((gc_mode_ext & 0x08) && (vga.gc.write_mode == 1)) ? 8 : 4;
+
+ for(i=0;i<4;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ {
+ if(gc_mode_ext & 0x02)
+ {
+ vga.memory[(((offset+addr) << 1)+i*0x10000) % vga.svga_intf.vram_size] = (vga.sequencer.data[4] & 4) ? cirrus_vga_latch_write(i,data) : data;
+ vga.memory[(((offset+addr) << 1)+i*0x10000+1) % vga.svga_intf.vram_size] = (vga.sequencer.data[4] & 4) ? cirrus_vga_latch_write(i,data) : data;
+ }
+ else
+ vga.memory[(((offset+addr))+i*0x10000) % vga.svga_intf.vram_size] = (vga.sequencer.data[4] & 4) ? cirrus_vga_latch_write(i,data) : data;
+ }
+ }
+ return;
+ }
+ }
+}
diff --git a/src/devices/video/clgd542x.h b/src/devices/video/clgd542x.h
new file mode 100644
index 00000000000..20300d160fd
--- /dev/null
+++ b/src/devices/video/clgd542x.h
@@ -0,0 +1,114 @@
+// license:BSD-3-Clause
+// copyright-holders:Barry Rodewald
+/*
+
+ Cirrus Logic GD542x/3x video chipsets
+
+*/
+
+#include "emu.h"
+#include "video/pc_vga.h"
+
+MACHINE_CONFIG_EXTERN( pcvideo_cirrus_gd5428 );
+MACHINE_CONFIG_EXTERN( pcvideo_cirrus_gd5430 );
+
+class cirrus_gd5428_device : public svga_device
+{
+public:
+ // construction/destruction
+ cirrus_gd5428_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ cirrus_gd5428_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ virtual READ8_MEMBER(port_03c0_r);
+ virtual WRITE8_MEMBER(port_03c0_w);
+ virtual READ8_MEMBER(port_03b0_r);
+ virtual WRITE8_MEMBER(port_03b0_w);
+ virtual READ8_MEMBER(port_03d0_r);
+ virtual WRITE8_MEMBER(port_03d0_w);
+ virtual READ8_MEMBER(mem_r);
+ virtual WRITE8_MEMBER(mem_w);
+
+ virtual UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual UINT16 offset();
+
+ UINT8 m_chip_id;
+
+ UINT8 gc_mode_ext;
+ UINT8 gc_bank_0;
+ UINT8 gc_bank_1;
+ bool gc_locked;
+ UINT8 m_lock_reg;
+ UINT8 m_gr10; // high byte of background colour (in 15/16bpp)
+ UINT8 m_gr11; // high byte of foreground colour (in 15/16bpp)
+
+ UINT8 m_cr19;
+ UINT8 m_cr1a;
+ UINT8 m_cr1b;
+
+ // hardware cursor
+ UINT16 m_cursor_x;
+ UINT16 m_cursor_y;
+ UINT16 m_cursor_addr;
+ UINT8 m_cursor_attr;
+ bool m_ext_palette_enabled;
+ struct { UINT8 red, green, blue; } m_ext_palette[16]; // extra palette, colour 0 is cursor background, colour 15 is cursor foreground, colour 2 is overscan border colour
+
+ // BitBLT engine
+ UINT8 m_blt_status;
+ UINT8 m_blt_rop;
+ UINT8 m_blt_mode;
+ UINT32 m_blt_source;
+ UINT32 m_blt_dest;
+ UINT16 m_blt_source_pitch;
+ UINT16 m_blt_dest_pitch;
+ UINT16 m_blt_height;
+ UINT16 m_blt_width;
+ UINT32 m_blt_source_current;
+ UINT32 m_blt_dest_current;
+ UINT16 m_blt_trans_colour;
+ UINT16 m_blt_trans_colour_mask;
+
+ bool m_blt_system_transfer; // blit from system memory
+ UINT8 m_blt_system_count;
+ UINT32 m_blt_system_buffer;
+ UINT16 m_blt_pixel_count;
+ UINT16 m_blt_scan_count;
+
+ UINT8 m_scratchpad1;
+ UINT8 m_scratchpad2;
+ UINT8 m_scratchpad3;
+ UINT8 m_vclk_num[4];
+ UINT8 m_vclk_denom[4];
+
+ inline UINT8 cirrus_vga_latch_write(int offs, UINT8 data);
+private:
+ void cirrus_define_video_mode();
+ UINT8 cirrus_seq_reg_read(UINT8 index);
+ void cirrus_seq_reg_write(UINT8 index, UINT8 data);
+ UINT8 cirrus_gc_reg_read(UINT8 index);
+ void cirrus_gc_reg_write(UINT8 index, UINT8 data);
+ UINT8 cirrus_crtc_reg_read(UINT8 index);
+ void cirrus_crtc_reg_write(UINT8 index, UINT8 data);
+
+ void start_bitblt();
+ void start_reverse_bitblt();
+ void start_system_bitblt();
+ void blit_dword();
+ void blit_byte(); // used for colour expanded system-to-vram bitblts
+ void copy_pixel(UINT8 src, UINT8 dst);
+};
+
+class cirrus_gd5430_device : public cirrus_gd5428_device
+{
+public:
+ cirrus_gd5430_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ virtual void device_start();
+};
+
+// device type definition
+extern const device_type CIRRUS_GD5428;
+extern const device_type CIRRUS_GD5430;
diff --git a/src/devices/video/crt9007.c b/src/devices/video/crt9007.c
new file mode 100644
index 00000000000..a5722fbd3bf
--- /dev/null
+++ b/src/devices/video/crt9007.c
@@ -0,0 +1,935 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ SMC CRT9007 CRT Video Processor and Controller (VPAC) emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - cursor timer
+ - interrupts
+ - light pen
+ - frame timer
+ - non-DMA mode
+ - DMA mode
+ - cursor/blank skew
+ - sequential breaks
+ - interlaced mode
+ - smooth scroll
+ - page blank
+ - double height cursor
+ - row attributes
+ - pin configuration
+ - operation modes 0,4,7
+ - address modes 1,2,3
+ - light pen
+ - state saving
+
+*/
+
+#include "emu.h"
+#include "crt9007.h"
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type CRT9007 = &device_creator<crt9007_t>;
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+#define HAS_VALID_PARAMETERS \
+ (m_reg[0x00] && m_reg[0x01] && m_reg[0x07] && m_reg[0x08] && m_reg[0x09])
+
+#define CHARACTERS_PER_HORIZONTAL_PERIOD \
+ m_reg[0x00]
+
+#define CHARACTERS_PER_DATA_ROW \
+ (m_reg[0x01] + 1)
+
+#define HORIZONTAL_DELAY \
+ m_reg[0x02]
+
+#define HORIZONTAL_SYNC_WIDTH \
+ m_reg[0x03]
+
+#define VERTICAL_SYNC_WIDTH \
+ m_reg[0x04]
+
+#define VERTICAL_DELAY \
+ (m_reg[0x05] - 1)
+
+#define PIN_CONFIGURATION \
+ (m_reg[0x06] >> 6)
+
+#define CURSOR_SKEW \
+ ((m_reg[0x06] >> 3) & 0x07)
+
+#define BLANK_SKEW \
+ (m_reg[0x06] & 0x07)
+
+#define VISIBLE_DATA_ROWS_PER_FRAME \
+ (m_reg[0x07] + 1)
+
+#define SCAN_LINES_PER_DATA_ROW \
+ ((m_reg[0x08] & 0x1f) + 1)
+
+#define SCAN_LINES_PER_FRAME \
+ (((m_reg[0x08] << 3) & 0x0700) | m_reg[0x09])
+
+#define DMA_BURST_COUNT \
+ ((m_reg[0x0a] & 0x0f) + 1)
+
+#define DMA_BURST_DELAY \
+ ((((m_reg[0x0a] >> 4) & 0x07) + 1) % 8)
+
+#define DMA_DISABLE \
+ BIT(m_reg[0x0a], 7)
+
+#define SINGLE_HEIGHT_CURSOR \
+ BIT(m_reg[0x0b], 0)
+
+#define OPERATION_MODE \
+ ((m_reg[0x0b] >> 1) & 0x07)
+
+#define INTERLACE_MODE \
+ ((m_reg[0x0b] >> 4) & 0x03)
+
+#define PAGE_BLANK \
+ BIT(m_reg[0x0b], 6)
+
+#define TABLE_START \
+ (((m_reg[0x0d] << 8) & 0x3f00) | m_reg[0x0c])
+
+#define ADDRESS_MODE \
+ ((m_reg[0x0d] >> 6) & 0x03)
+
+#define AUXILIARY_ADDRESS_1 \
+ (((m_reg[0x0f] << 8) & 0x3f00) | m_reg[0x0e])
+
+#define ROW_ATTRIBUTES_1 \
+ ((m_reg[0x0f] >> 6) & 0x03)
+
+#define SEQUENTIAL_BREAK_1 \
+ m_reg[0x10]
+
+#define SEQUENTIAL_BREAK_2 \
+ m_reg[0x12]
+
+#define DATA_ROW_START \
+ m_reg[0x11]
+
+#define DATA_ROW_END \
+ m_reg[0x12]
+
+#define AUXILIARY_ADDRESS_2 \
+ (((m_reg[0x14] << 8) & 0x3f00) | m_reg[0x13])
+
+#define ROW_ATTRIBUTES_2 \
+ ((m_reg[0x14] >> 6) & 0x03)
+
+#define SMOOTH_SCROLL_OFFSET \
+ ((m_reg[0x17] >> 1) & 0x3f)
+
+#define SMOOTH_SCROLL_OFFSET_OVERFLOW \
+ BIT(m_reg[0x17], 7)
+
+#define VERTICAL_CURSOR \
+ m_reg[0x18]
+
+#define HORIZONTAL_CURSOR \
+ m_reg[0x19]
+
+#define INTERRUPT_ENABLE \
+ m_reg[0x1a]
+
+#define FRAME_TIMER \
+ BIT(m_reg[0x1a], 0)
+
+#define LIGHT_PEN_INTERRUPT \
+ BIT(m_reg[0x1a], 5)
+
+#define VERTICAL_RETRACE_INTERRUPT \
+ BIT(m_reg[0x1a], 6)
+
+#define VERTICAL_LIGHT_PEN \
+ m_reg[0x3b]
+
+#define HORIZONTAL_LIGHT_PEN \
+ m_reg[0x3c]
+
+
+// interlace
+enum
+{
+ NON_INTERLACED = 0,
+ ENHANCED_VIDEO_INTERFACE,
+ NORMAL_VIDEO_INTERFACE
+};
+
+
+// operation modes
+enum
+{
+ OPERATION_MODE_REPETITIVE_MEMORY_ADDRESSING = 0, // not implemented
+ OPERATION_MODE_DOUBLE_ROW_BUFFER = 1,
+ OPERATION_MODE_SINGLE_ROW_BUFFER = 4, // not implemented
+ OPERATION_MODE_ATTRIBUTE_ASSEMBLE = 7 // not implemented
+};
+
+
+// addressing modes
+enum
+{
+ ADDRESS_MODE_SEQUENTIAL_ADDRESSING = 0,
+ ADDRESS_MODE_SEQUENTIAL_ROLL_ADDRESSING, // not implemented
+ ADDRESS_MODE_CONTIGUOUS_ROW_TABLE, // not implemented
+ ADDRESS_MODE_LINKED_LIST_ROW_TABLE // not implemented
+};
+
+
+// interrupt enable register bits
+const int IE_VERTICAL_RETRACE = 0x40;
+//const int IE_LIGHT_PEN = 0x20;
+//const int IE_FRAME_TIMER = 0x01;
+
+// status register bits
+const int STATUS_INTERRUPT_PENDING = 0x80;
+//const int STATUS_VERTICAL_RETRACE = 0x40;
+const int STATUS_LIGHT_PEN_UPDATE = 0x20;
+//const int STATUS_ODD_EVEN = 0x04;
+//const int STATUS_FRAME_TIMER_OCCURRED = 0x01;
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// default address map
+static ADDRESS_MAP_START( crt9007, AS_0, 8, crt9007_t )
+ AM_RANGE(0x0000, 0x3fff) AM_RAM
+ADDRESS_MAP_END
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 crt9007_t::readbyte(offs_t address)
+{
+ return space().read_byte(address);
+}
+
+
+//-------------------------------------------------
+// trigger_interrupt -
+//-------------------------------------------------
+
+inline void crt9007_t::trigger_interrupt(int line)
+{
+ if (INTERRUPT_ENABLE & line)
+ {
+ int status = m_status;
+
+ m_status |= STATUS_INTERRUPT_PENDING | line;
+
+ if (!(status & STATUS_INTERRUPT_PENDING))
+ {
+ if (LOG) logerror("CRT9007 '%s' INT 1\n", tag());
+ m_write_int(ASSERT_LINE);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// update_cblank_line -
+//-------------------------------------------------
+
+inline void crt9007_t::update_cblank_line()
+{
+ int x = m_screen->hpos();
+ int y = m_screen->vpos();
+
+ // composite blank
+ int cblank = !(m_hs & m_vs);
+
+ if (m_cblank != cblank)
+ {
+ m_cblank = cblank;
+
+ if (LOG) logerror("CRT9007 '%s' y %03u x %04u : CBLANK %u\n", tag(), y, x, m_cblank);
+
+ m_write_cblank(m_cblank);
+ }
+}
+
+
+//-------------------------------------------------
+// update_hsync_timer -
+//-------------------------------------------------
+
+inline void crt9007_t::update_hsync_timer(int state)
+{
+ int y = m_screen->vpos();
+
+ int next_x = state ? m_hsync_start : m_hsync_end;
+ int next_y = state ? (y + 1) % SCAN_LINES_PER_FRAME : y;
+
+ attotime duration = m_screen->time_until_pos(next_y, next_x);
+
+ m_hsync_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// update_vsync_timer -
+//-------------------------------------------------
+
+inline void crt9007_t::update_vsync_timer(int state)
+{
+ int next_y = state ? m_vsync_start : m_vsync_end;
+
+ attotime duration = m_screen->time_until_pos(next_y, 0);
+
+ m_vsync_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// update_vlt_timer -
+//-------------------------------------------------
+
+inline void crt9007_t::update_vlt_timer(int state)
+{
+ // this signal is active during all visible scan lines and during the horizontal trace at vertical retrace
+ int y = m_screen->vpos();
+
+ int next_x = state ? m_vlt_end : m_vlt_start;
+ int next_y = state ? y : ((y == m_vlt_bottom) ? 0 : (y + 1));
+
+ attotime duration = m_screen->time_until_pos(next_y, next_x);
+
+ m_vlt_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// update_curs_timer -
+//-------------------------------------------------
+
+inline void crt9007_t::update_curs_timer(int state)
+{
+ // this signal is active for 1 character time for all scanlines within the data row
+ // TODO
+}
+
+
+//-------------------------------------------------
+// update_drb_timer -
+//-------------------------------------------------
+
+inline void crt9007_t::update_drb_timer(int state)
+{
+ // this signal is active for 1 full scan line (VLT edge to edge) at the top scan line of each new row
+ // there is 1 extra DRB signal during the 1st scanline of the vertical retrace interval
+ int y = m_screen->vpos();
+
+ int next_x = m_vlt_end;
+ int next_y = y ? y + 1 : y;
+
+ if (state)
+ {
+ if (y == 0)
+ {
+ next_y = VERTICAL_DELAY - 1;
+ }
+ else if (y == m_drb_bottom)
+ {
+ next_x = 0;
+ next_y = 0;
+ }
+ else
+ {
+ next_y = y + SCAN_LINES_PER_DATA_ROW - 1;
+ }
+ }
+
+ attotime duration = m_screen->time_until_pos(next_y, next_x);
+
+ m_drb_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// update_dma_timer -
+//-------------------------------------------------
+
+inline void crt9007_t::update_dma_timer()
+{
+ // TODO
+}
+
+
+//-------------------------------------------------
+// recompute_parameters -
+//-------------------------------------------------
+
+inline void crt9007_t::recompute_parameters()
+{
+ // check that necessary registers have been loaded
+ if (!HAS_VALID_PARAMETERS) return;
+
+ // screen dimensions
+ //int horiz_pix_total = CHARACTERS_PER_HORIZONTAL_PERIOD * m_hpixels_per_column;
+ //int vert_pix_total = SCAN_LINES_PER_FRAME;
+
+ // refresh rate
+ //attoseconds_t refresh = HZ_TO_ATTOSECONDS(clock()) * horiz_pix_total * vert_pix_total;
+
+ // horizontal sync
+ m_hsync_start = 0;
+ m_hsync_end = HORIZONTAL_SYNC_WIDTH * m_hpixels_per_column;
+
+ // visible line time
+ m_vlt_start = HORIZONTAL_DELAY * m_hpixels_per_column;
+ m_vlt_end = (HORIZONTAL_DELAY + CHARACTERS_PER_DATA_ROW) * m_hpixels_per_column;
+ m_vlt_bottom = VERTICAL_DELAY + (VISIBLE_DATA_ROWS_PER_FRAME * SCAN_LINES_PER_DATA_ROW) - 1;
+
+ // data row boundary
+ m_drb_bottom = VERTICAL_DELAY + (VISIBLE_DATA_ROWS_PER_FRAME * SCAN_LINES_PER_DATA_ROW) - SCAN_LINES_PER_DATA_ROW;
+
+ // vertical sync
+ m_vsync_start = 0;
+ m_vsync_end = VERTICAL_SYNC_WIDTH;
+
+ // visible area
+ //rectangle visarea;
+
+ //visarea.set(m_hsync_end, horiz_pix_total - 1, m_vsync_end, vert_pix_total - 1);
+
+ //if (LOG)
+ //{
+ // logerror("CRT9007 '%s' Screen: %u x %u @ %f Hz\n", tag(), horiz_pix_total, vert_pix_total, 1 / ATTOSECONDS_TO_DOUBLE(refresh));
+ // logerror("CRT9007 '%s' Visible Area: (%u, %u) - (%u, %u)\n", tag(), visarea.min_x, visarea.min_y, visarea.max_x, visarea.max_y);
+ //}
+
+ //m_screen->configure(horiz_pix_total, vert_pix_total, visarea, refresh);
+
+ m_hsync_timer->adjust(m_screen->time_until_pos(0, 0));
+ m_vsync_timer->adjust(m_screen->time_until_pos(0, 0));
+ m_vlt_timer->adjust(m_screen->time_until_pos(0, m_vlt_start), 1);
+ m_drb_timer->adjust(m_screen->time_until_pos(0, 0));
+}
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// crt9007_t - constructor
+//-------------------------------------------------
+
+crt9007_t::crt9007_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, CRT9007, "SMC CRT9007", tag, owner, clock, "crt9007", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(crt9007)),
+ m_write_int(*this),
+ m_write_dmar(*this),
+ m_write_hs(*this),
+ m_write_vs(*this),
+ m_write_vlt(*this),
+ m_write_curs(*this),
+ m_write_drb(*this),
+ m_write_wben(*this),
+ m_write_cblank(*this),
+ m_write_slg(*this),
+ m_write_sld(*this)
+{
+ for (int i = 0; i < 0x3d; i++)
+ m_reg[i] = 0;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void crt9007_t::device_start()
+{
+ // allocate timers
+ m_hsync_timer = timer_alloc(TIMER_HSYNC);
+ m_vsync_timer = timer_alloc(TIMER_VSYNC);
+ m_vlt_timer = timer_alloc(TIMER_VLT);
+ m_curs_timer = timer_alloc(TIMER_CURS);
+ m_drb_timer = timer_alloc(TIMER_DRB);
+ m_dma_timer = timer_alloc(TIMER_DMA);
+
+ // resolve callbacks
+ m_write_int.resolve_safe();
+ m_write_dmar.resolve_safe();
+ m_write_hs.resolve_safe();
+ m_write_vs.resolve_safe();
+ m_write_vlt.resolve_safe();
+ m_write_curs.resolve_safe();
+ m_write_drb.resolve_safe();
+ m_write_wben.resolve_safe();
+ m_write_cblank.resolve_safe();
+ m_write_slg.resolve_safe();
+ m_write_sld.resolve_safe();
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void crt9007_t::device_reset()
+{
+ m_disp = 0;
+ m_vs = 0;
+ m_cblank = 0;
+
+ // HS = 1
+ m_write_hs(1);
+
+ // VS = 1
+ m_write_vs(1);
+
+ // CBLANK = 1
+ m_write_cblank(0);
+
+ // CURS = 0
+ m_write_curs(0);
+
+ // VLT = 0
+ m_write_vlt(0);
+
+ // DRB = 1
+ m_write_drb(1);
+
+ // INT = 0
+ m_write_int(CLEAR_LINE);
+
+ // 28 (DMAR) = 0
+ m_write_dmar(CLEAR_LINE);
+
+ // 29 (WBEN) = 0
+ m_write_wben(1); // HACK
+
+ // 30 (SLG) = 0
+ m_write_slg(0);
+
+ // 31 (SLD) = 0
+ m_write_sld(0);
+
+ // 32 (LPSTB) = 0
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - handle clock change
+//-------------------------------------------------
+
+void crt9007_t::device_clock_changed()
+{
+ recompute_parameters();
+}
+
+
+//-------------------------------------------------
+// device_timer - handle timer events
+//-------------------------------------------------
+
+void crt9007_t::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int x = m_screen->hpos();
+ int y = m_screen->vpos();
+
+ switch (id)
+ {
+ case TIMER_HSYNC:
+ m_hs = param;
+
+ if (LOG) logerror("CRT9007 '%s' y %03u x %04u : HS %u\n", tag(), y, x, m_hs);
+
+ m_write_hs(m_hs);
+
+ update_cblank_line();
+
+ update_hsync_timer(param);
+ break;
+
+ case TIMER_VSYNC:
+ m_vs = param;
+
+ if (LOG) logerror("CRT9007 '%s' y %03u x %04u : VS %u\n", tag(), y, x, m_vs);
+
+ m_write_vs(param);
+
+ if (m_vs)
+ {
+ // reset all other bits except Light Pen Update to logic 0
+ m_status &= STATUS_LIGHT_PEN_UPDATE;
+ }
+ else
+ {
+ trigger_interrupt(IE_VERTICAL_RETRACE);
+
+ update_cblank_line();
+ }
+
+ update_vsync_timer(param);
+ break;
+
+ case TIMER_VLT:
+ m_vlt = param;
+
+ if (LOG) logerror("CRT9007 '%s' y %03u x %04u : VLT %u\n", tag(), y, x, m_vlt);
+
+ m_write_vlt(param);
+
+ update_vlt_timer(param);
+ break;
+
+ case TIMER_CURS:
+ if (LOG) logerror("CRT9007 '%s' y %03u x %04u : CURS %u\n", tag(), y, x, param);
+
+ m_write_curs(param);
+
+ update_curs_timer(param);
+ break;
+
+ case TIMER_DRB:
+ m_drb = param;
+
+ if (LOG) logerror("CRT9007 '%s' y %03u x %04u : DRB %u\n", tag(), y, x, m_drb);
+
+ m_write_drb(param);
+
+ if (!m_drb && !DMA_DISABLE)
+ {
+ // start DMA burst sequence
+ m_dma_count = CHARACTERS_PER_DATA_ROW;
+ m_dma_burst = DMA_BURST_COUNT ? (DMA_BURST_COUNT * 4) : CHARACTERS_PER_DATA_ROW;
+ m_dma_delay = DMA_BURST_DELAY;
+ m_dmar = 1;
+
+ if (LOG) logerror("CRT9007 '%s' DMAR 1\n", tag());
+ m_write_dmar(ASSERT_LINE);
+ }
+
+ update_drb_timer(param);
+ break;
+
+ case TIMER_DMA:
+ readbyte(AUXILIARY_ADDRESS_2);
+
+ update_dma_timer();
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *crt9007_t::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// read - register read
+//-------------------------------------------------
+
+READ8_MEMBER( crt9007_t::read )
+{
+ UINT8 data = 0;
+
+ switch (offset)
+ {
+ case 0x15:
+ if (LOG) logerror("CRT9007 '%s' Start\n", tag());
+ m_disp = 1;
+ break;
+
+ case 0x16:
+ if (LOG) logerror("CRT9007 '%s' Reset\n", tag());
+ device_reset();
+ break;
+
+ case 0x38:
+ data = VERTICAL_CURSOR;
+ break;
+
+ case 0x39:
+ data = HORIZONTAL_CURSOR;
+ break;
+
+ case 0x3a:
+ data = m_status;
+
+ // reset interrupt pending bit
+ m_status &= ~STATUS_INTERRUPT_PENDING;
+ if (LOG) logerror("CRT9007 '%s' INT 0\n", tag());
+ m_write_int(CLEAR_LINE);
+ break;
+
+ case 0x3b:
+ data = VERTICAL_LIGHT_PEN;
+ break;
+
+ case 0x3c:
+ data = HORIZONTAL_LIGHT_PEN;
+
+ // reset light pen update bit
+ m_status &= ~STATUS_LIGHT_PEN_UPDATE;
+ break;
+
+ default:
+ logerror("CRT9007 '%s' Read from Invalid Register: %02x!\n", tag(), offset);
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write - register write
+//-------------------------------------------------
+
+WRITE8_MEMBER( crt9007_t::write )
+{
+ m_reg[offset] = data;
+
+ switch (offset)
+ {
+ case 0x00:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Characters per Horizontal Period: %u\n", tag(), CHARACTERS_PER_HORIZONTAL_PERIOD);
+ break;
+
+ case 0x01:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Characters per Data Row: %u\n", tag(), CHARACTERS_PER_DATA_ROW);
+ break;
+
+ case 0x02:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Horizontal Delay: %u\n", tag(), HORIZONTAL_DELAY);
+ break;
+
+ case 0x03:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Horizontal Sync Width: %u\n", tag(), HORIZONTAL_SYNC_WIDTH);
+ break;
+
+ case 0x04:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Vertical Sync Width: %u\n", tag(), VERTICAL_SYNC_WIDTH);
+ break;
+
+ case 0x05:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Vertical Delay: %u\n", tag(), VERTICAL_DELAY);
+ break;
+
+ case 0x06:
+ recompute_parameters();
+ if (LOG)
+ {
+ logerror("CRT9007 '%s' Pin Configuration: %u\n", tag(), PIN_CONFIGURATION);
+ logerror("CRT9007 '%s' Cursor Skew: %u\n", tag(), CURSOR_SKEW);
+ logerror("CRT9007 '%s' Blank Skew: %u\n", tag(), BLANK_SKEW);
+ }
+ break;
+
+ case 0x07:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Visible Data Rows per Frame: %u\n", tag(), VISIBLE_DATA_ROWS_PER_FRAME);
+ break;
+
+ case 0x08:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Scan Lines per Data Row: %u\n", tag(), SCAN_LINES_PER_DATA_ROW);
+ break;
+
+ case 0x09:
+ recompute_parameters();
+ if (LOG) logerror("CRT9007 '%s' Scan Lines per Frame: %u\n", tag(), SCAN_LINES_PER_FRAME);
+ break;
+
+ case 0x0a:
+ if (LOG)
+ {
+ logerror("CRT9007 '%s' DMA Burst Count: %u\n", tag(), DMA_BURST_COUNT);
+ logerror("CRT9007 '%s' DMA Burst Delay: %u\n", tag(), DMA_BURST_DELAY);
+ logerror("CRT9007 '%s' DMA Disable: %u\n", tag(), DMA_DISABLE);
+ }
+ break;
+
+ case 0x0b:
+ if (LOG)
+ {
+ logerror("CRT9007 '%s' %s Height Cursor\n", tag(), SINGLE_HEIGHT_CURSOR ? "Single" : "Double");
+ logerror("CRT9007 '%s' Operation Mode: %u\n", tag(), OPERATION_MODE);
+ logerror("CRT9007 '%s' Interlace Mode: %u\n", tag(), INTERLACE_MODE);
+ logerror("CRT9007 '%s' %s Mechanism\n", tag(), PAGE_BLANK ? "Page Blank" : "Smooth Scroll");
+ }
+ break;
+
+ case 0x0c:
+ break;
+
+ case 0x0d:
+ if (LOG)
+ {
+ logerror("CRT9007 '%s' Table Start Register: %04x\n", tag(), TABLE_START);
+ logerror("CRT9007 '%s' Address Mode: %u\n", tag(), ADDRESS_MODE);
+ }
+ break;
+
+ case 0x0e:
+ break;
+
+ case 0x0f:
+ if (LOG)
+ {
+ logerror("CRT9007 '%s' Auxialiary Address Register 1: %04x\n", tag(), AUXILIARY_ADDRESS_1);
+ logerror("CRT9007 '%s' Row Attributes: %u\n", tag(), ROW_ATTRIBUTES_1);
+ }
+ break;
+
+ case 0x10:
+ if (LOG) logerror("CRT9007 '%s' Sequential Break Register 1: %u\n", tag(), SEQUENTIAL_BREAK_1);
+ break;
+
+ case 0x11:
+ if (LOG) logerror("CRT9007 '%s' Data Row Start Register: %u\n", tag(), DATA_ROW_START);
+ break;
+
+ case 0x12:
+ if (LOG) logerror("CRT9007 '%s' Data Row End/Sequential Break Register 2: %u\n", tag(), SEQUENTIAL_BREAK_2);
+ break;
+
+ case 0x13:
+ break;
+
+ case 0x14:
+ if (LOG)
+ {
+ logerror("CRT9007 '%s' Auxiliary Address Register 2: %04x\n", tag(), AUXILIARY_ADDRESS_2);
+ logerror("CRT9007 '%s' Row Attributes: %u\n", tag(), ROW_ATTRIBUTES_2);
+ }
+ break;
+
+ case 0x15:
+ if (LOG) logerror("CRT9007 '%s' Start\n", tag());
+ m_disp = 1;
+ break;
+
+ case 0x16:
+ if (LOG) logerror("CRT9007 '%s' Reset\n", tag());
+ device_reset();
+ break;
+
+ case 0x17:
+ if (LOG)
+ {
+ logerror("CRT9007 '%s' Smooth Scroll Offset: %u\n", tag(), SMOOTH_SCROLL_OFFSET);
+ logerror("CRT9007 '%s' Smooth Scroll Offset Overflow: %u\n", tag(), SMOOTH_SCROLL_OFFSET_OVERFLOW);
+ }
+ break;
+
+ case 0x18:
+ if (LOG) logerror("CRT9007 '%s' Vertical Cursor Register: %u\n", tag(), VERTICAL_CURSOR);
+ break;
+
+ case 0x19:
+ if (LOG) logerror("CRT9007 '%s' Horizontal Cursor Register: %u\n", tag(), HORIZONTAL_CURSOR);
+ break;
+
+ case 0x1a:
+ if (LOG)
+ {
+ logerror("CRT9007 '%s' Frame Timer: %u\n", tag(), FRAME_TIMER);
+ logerror("CRT9007 '%s' Light Pen Interrupt: %u\n", tag(), LIGHT_PEN_INTERRUPT);
+ logerror("CRT9007 '%s' Vertical Retrace Interrupt: %u\n", tag(), VERTICAL_RETRACE_INTERRUPT);
+ }
+ break;
+
+ default:
+ logerror("CRT9007 '%s' Write to Invalid Register: %02x!\n", tag(), offset);
+ }
+}
+
+
+//-------------------------------------------------
+// ack_w - DMA acknowledge
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( crt9007_t::ack_w )
+{
+ if (LOG) logerror("CRT9007 '%s' ACK: %u\n", tag(), state);
+
+ if (m_dmar && !m_ack && state)
+ {
+ // start DMA transfer
+ m_dma_timer->adjust(attotime::from_hz(clock()));
+ }
+
+ m_ack = state;
+}
+
+
+//-------------------------------------------------
+// lpstb_w - light pen strobe
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( crt9007_t::lpstb_w )
+{
+ if (LOG) logerror("CRT9007 '%s' LPSTB: %u\n", tag(), state);
+
+ if (!m_lpstb && state)
+ {
+ // TODO latch current row/column position
+ }
+
+ m_lpstb = state;
+}
+
+
+//-------------------------------------------------
+// set_character_width -
+//-------------------------------------------------
+
+void crt9007_t::set_character_width(int value)
+{
+ m_hpixels_per_column = value;
+
+ recompute_parameters();
+}
diff --git a/src/devices/video/crt9007.h b/src/devices/video/crt9007.h
new file mode 100644
index 00000000000..95745ecece6
--- /dev/null
+++ b/src/devices/video/crt9007.h
@@ -0,0 +1,222 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ SMC CRT9007 CRT Video Processor and Controller (VPAC) emulation
+
+**********************************************************************
+ _____ _____
+ VA2 1 |* \_/ | 40 GND
+ VA10 2 | | 39 VA9
+ VA3 3 | | 38 VA1
+ VA11 4 | | 37 VA8
+ VA12 5 | | 36 VA0
+ VA4 6 | | 35 CBLANK
+ VA13 7 | | 34 CURS
+ VA5 8 | | 33 ACK/_TSC
+ VA6 9 | | 32 _CSYNC/LPSTB
+ VA7 10 | CRT9007 | 31 SLD/SL0
+ VLT 11 | | 30 _SLG/SL1
+ _VS 12 | | 29 WBEN/SL2/_CSYNC
+ _HS 13 | | 28 DMAR/SL3/VBLANK
+ _CCLK 14 | | 27 INT
+ _DRB 15 | | 26 _RST
+ VD7 16 | | 25 _CS
+ VD6 17 | | 24 VD0
+ VD5 18 | | 23 VD1
+ VD4 19 | | 22 VD2
+ VD3 20 |_____________| 21 +5V
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __CRT9007__
+#define __CRT9007__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_CRT9007_CHARACTER_WIDTH(_value) \
+ crt9007_t::static_set_character_width(*device, _value);
+
+#define MCFG_CRT9007_INT_CALLBACK(_write) \
+ devcb = &crt9007_t::set_int_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_DMAR_CALLBACK(_write) \
+ devcb = &crt9007_t::set_dmar_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_VS_CALLBACK(_write) \
+ devcb = &crt9007_t::set_vs_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_HS_CALLBACK(_write) \
+ devcb = &crt9007_t::set_hs_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_VLT_CALLBACK(_write) \
+ devcb = &crt9007_t::set_vlt_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_CURS_CALLBACK(_write) \
+ devcb = &crt9007_t::set_curs_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_DRB_CALLBACK(_write) \
+ devcb = &crt9007_t::set_drb_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_WBEN_CALLBACK(_write) \
+ devcb = &crt9007_t::set_wben_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_CBLANK_CALLBACK(_write) \
+ devcb = &crt9007_t::set_cblank_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_SLG_CALLBACK(_write) \
+ devcb = &crt9007_t::set_slg_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9007_SLD_CALLBACK(_write) \
+ devcb = &crt9007_t::set_sld_wr_callback(*device, DEVCB_##_write);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> crt9007_t
+
+class crt9007_t : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ crt9007_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_character_width(device_t &device, int value) { downcast<crt9007_t &>(device).m_hpixels_per_column = value; }
+
+ template<class _Object> static devcb_base &set_int_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_int.set_callback(object); }
+ template<class _Object> static devcb_base &set_dmar_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_dmar.set_callback(object); }
+ template<class _Object> static devcb_base &set_vs_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_vs.set_callback(object); }
+ template<class _Object> static devcb_base &set_hs_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_hs.set_callback(object); }
+ template<class _Object> static devcb_base &set_vlt_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_vlt.set_callback(object); }
+ template<class _Object> static devcb_base &set_curs_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_curs.set_callback(object); }
+ template<class _Object> static devcb_base &set_drb_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_drb.set_callback(object); }
+ template<class _Object> static devcb_base &set_wben_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_wben.set_callback(object); }
+ template<class _Object> static devcb_base &set_cblank_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_cblank.set_callback(object); }
+ template<class _Object> static devcb_base &set_slg_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_slg.set_callback(object); }
+ template<class _Object> static devcb_base &set_sld_wr_callback(device_t &device, _Object object) { return downcast<crt9007_t &>(device).m_write_sld.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_WRITE_LINE_MEMBER( ack_w );
+ DECLARE_WRITE_LINE_MEMBER( lpstb_w );
+
+ void set_character_width(int value);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_clock_changed();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+private:
+ enum
+ {
+ TIMER_HSYNC,
+ TIMER_VSYNC,
+ TIMER_VLT,
+ TIMER_CURS,
+ TIMER_DRB,
+ TIMER_DMA
+ };
+
+ inline UINT8 readbyte(offs_t address);
+
+ inline void trigger_interrupt(int line);
+ inline void update_cblank_line();
+ inline void update_hsync_timer(int state);
+ inline void update_vsync_timer(int state);
+ inline void update_vlt_timer(int state);
+ inline void update_curs_timer(int state);
+ inline void update_drb_timer(int state);
+ inline void update_dma_timer();
+
+ inline void recompute_parameters();
+
+ // address space configurations
+ const address_space_config m_space_config;
+
+ devcb_write_line m_write_int;
+ devcb_write_line m_write_dmar;
+ devcb_write_line m_write_hs;
+ devcb_write_line m_write_vs;
+ devcb_write_line m_write_vlt;
+ devcb_write_line m_write_curs;
+ devcb_write_line m_write_drb;
+ devcb_write_line m_write_wben;
+ devcb_write_line m_write_cblank;
+ devcb_write_line m_write_slg;
+ devcb_write_line m_write_sld;
+
+ // registers
+ UINT8 m_reg[0x3d];
+ UINT8 m_status;
+
+ int m_disp;
+ int m_hpixels_per_column;
+
+ // runtime variables, do not state save
+ int m_vsync_start;
+ int m_vsync_end;
+ int m_hsync_start;
+ int m_hsync_end;
+ int m_vlt_start;
+ int m_vlt_end;
+ int m_vlt_bottom;
+ int m_drb_bottom;
+ int m_hs;
+ int m_vs;
+ int m_cblank;
+ int m_vlt;
+ int m_drb;
+ //int m_wben;
+ //int m_slg;
+ //int m_sld;
+ int m_lpstb;
+
+ // DMA
+ int m_dmar;
+ int m_ack;
+ int m_dma_count;
+ int m_dma_burst;
+ int m_dma_delay;
+
+ // timers
+ emu_timer *m_vsync_timer;
+ emu_timer *m_hsync_timer;
+ emu_timer *m_vlt_timer;
+ emu_timer *m_curs_timer;
+ emu_timer *m_drb_timer;
+ emu_timer *m_dma_timer;
+};
+
+
+// device type definition
+extern const device_type CRT9007;
+
+
+
+#endif
diff --git a/src/devices/video/crt9021.c b/src/devices/video/crt9021.c
new file mode 100644
index 00000000000..d72baff2b2b
--- /dev/null
+++ b/src/devices/video/crt9021.c
@@ -0,0 +1,187 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ SMC CRT9021 Video Attributes Controller (VAC) emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - attributes
+ - character blink
+ - underline
+ - full/half intensity
+ - operation modes
+ - wide graphics
+ - thin graphics
+ - character mode w/o underline
+ - character mode w/underline
+ - double height characters
+ - double width characters
+ - serial scan line
+ - cursor
+ - underline
+ - blinking underline
+ - reverse video
+ - blinking reverse video
+ - programmable character blink rate (75/25 duty)
+ - programmable cursor blink rate (50/50 duty)
+ - attribute latches
+
+*/
+
+#include "crt9021.h"
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type CRT9021 = &device_creator<crt9021_t>;
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// crt9021_t - constructor
+//-------------------------------------------------
+
+crt9021_t::crt9021_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, CRT9021, "SMC CRT9021", tag, owner, clock, "crt9021", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_data(0),
+ m_ms0(0),
+ m_ms1(0),
+ m_revid(0),
+ m_chabl(0),
+ m_blink(0),
+ m_intin(0),
+ m_atten(0),
+ m_cursor(0),
+ m_retbl(0),
+ m_ld_sh(1),
+ m_sld(1),
+ m_slg(0),
+ m_blc(0),
+ m_bkc(0),
+ m_sl0(0),
+ m_sl1(0),
+ m_sl2(0),
+ m_sl3(0),
+ m_vsync(0),
+ m_sr(0),
+ m_intout(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void crt9021_t::device_start()
+{
+ // register bitmap
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // state saving
+ save_item(NAME(m_data));
+ save_item(NAME(m_ms0));
+ save_item(NAME(m_ms1));
+ save_item(NAME(m_revid));
+ save_item(NAME(m_chabl));
+ save_item(NAME(m_blink));
+ save_item(NAME(m_intin));
+ save_item(NAME(m_atten));
+ save_item(NAME(m_cursor));
+ save_item(NAME(m_retbl));
+ save_item(NAME(m_ld_sh));
+ save_item(NAME(m_sld));
+ save_item(NAME(m_slg));
+ save_item(NAME(m_blc));
+ save_item(NAME(m_bkc));
+ save_item(NAME(m_sl0));
+ save_item(NAME(m_sl1));
+ save_item(NAME(m_sl2));
+ save_item(NAME(m_sl3));
+ save_item(NAME(m_vsync));
+ save_item(NAME(m_sr));
+ save_item(NAME(m_intout));
+ save_item(NAME(m_sl));
+}
+
+
+//-------------------------------------------------
+// ld_sh_w - load/shift
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( crt9021_t::ld_sh_w )
+{
+ if (LOG) logerror("CRT9021 '%s' LD/SH: %u\n", tag(), state);
+
+ if (!m_ld_sh && state)
+ {
+ // shift in scanline data
+ if (!m_slg)
+ {
+ m_sl >>= 1;
+ m_sl |= m_sld << 3;
+ }
+
+ // latch data
+ if (m_retbl)
+ {
+ m_sr = 0;
+ }
+ else
+ {
+ m_sr = m_chabl ? 0 : m_data;
+
+ if (m_revid) m_sr ^= 0xff;
+ }
+
+ // latch attributes
+ if (m_atten)
+ {
+ // TODO
+ }
+
+ m_display_cb(m_bitmap, m_screen->vpos(), m_screen->hpos(), m_sr, m_intout);
+ }
+}
+
+
+//-------------------------------------------------
+// vsync_w - vertical sync
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( crt9021_t::vsync_w )
+{
+ if (LOG) logerror("CRT9021 '%s' VSYNC: %u\n", tag(), state);
+}
+
+
+//-------------------------------------------------
+// screen_update - update screen
+//-------------------------------------------------
+
+UINT32 crt9021_t::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ m_bitmap.fill(rgb_t::black, cliprect);
+
+ return 0;
+}
diff --git a/src/devices/video/crt9021.h b/src/devices/video/crt9021.h
new file mode 100644
index 00000000000..f050f7d745c
--- /dev/null
+++ b/src/devices/video/crt9021.h
@@ -0,0 +1,139 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ SMC CRT9021 Video Attributes Controller (VAC) emulation
+
+**********************************************************************
+ _____ _____
+ D0 1 |* \_/ | 28 D1
+ MS0 2 | | 27 D2
+ MS1 3 | | 26 D3
+ REVID 4 | | 25 D4
+ CHABL 5 | | 24 D5
+ BLINK 6 | | 23 D6
+ INTIN 7 | CRT9021 | 22 D7
+ +5V 8 | | 21 _VSYNC
+ ATTEN 9 | | 20 GND
+ INTOUT 10 | | 19 SL0/SLD
+ CURSOR 11 | | 18 SL1/_SLG
+ RETBL 12 | | 17 SL2/BLC
+ _LD/SH 13 | | 16 SL3/BKC
+ VIDEO 14 |_____________| 15 VDC
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __CRT9021__
+#define __CRT9021__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define CRT9021_DRAW_CHARACTER_MEMBER(_name) void _name(bitmap_rgb32 &bitmap, int y, int x, UINT8 video, int intout)
+
+
+#define MCFG_CRT9021_DRAW_CHARACTER_CALLBACK_OWNER(_class, _method) \
+ crt9021_t::static_set_display_callback(*device, crt9021_draw_character_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef device_delegate<void (bitmap_rgb32 &bitmap, int y, int x, UINT8 video, int intout)> crt9021_draw_character_delegate;
+
+
+// ======================> crt9021_t
+
+class crt9021_t : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ crt9021_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_display_callback(device_t &device, crt9021_draw_character_delegate callback) { downcast<crt9021_t &>(device).m_display_cb = callback; }
+
+ void write(UINT8 data) { m_data = data; }
+ DECLARE_WRITE8_MEMBER( write ) { write(data); }
+ DECLARE_WRITE_LINE_MEMBER( ms0_w ) { m_ms0 = state; }
+ DECLARE_WRITE_LINE_MEMBER( ms1_w ) { m_ms1 = state; }
+ DECLARE_WRITE_LINE_MEMBER( revid_w ) { m_revid = state; }
+ DECLARE_WRITE_LINE_MEMBER( chabl_w ) { m_chabl = state; }
+ DECLARE_WRITE_LINE_MEMBER( blink_w ) { m_blink = state; }
+ DECLARE_WRITE_LINE_MEMBER( intin_w ) { m_intin = state; }
+ DECLARE_WRITE_LINE_MEMBER( atten_w ) { m_atten = state; }
+ DECLARE_WRITE_LINE_MEMBER( cursor_w ) { m_cursor = state; }
+ DECLARE_WRITE_LINE_MEMBER( retbl_w ) { m_retbl = state; }
+ DECLARE_WRITE_LINE_MEMBER( ld_sh_w );
+ DECLARE_WRITE_LINE_MEMBER( sld_w ) { m_sld = state; }
+ DECLARE_WRITE_LINE_MEMBER( slg_w ) { m_slg = state; }
+ DECLARE_WRITE_LINE_MEMBER( blc_w ) { m_blc = state; }
+ DECLARE_WRITE_LINE_MEMBER( bkc_w ) { m_bkc = state; }
+ DECLARE_WRITE_LINE_MEMBER( sl0_w ) { m_sl0 = state; }
+ DECLARE_WRITE_LINE_MEMBER( sl1_w ) { m_sl1 = state; }
+ DECLARE_WRITE_LINE_MEMBER( sl2_w ) { m_sl2 = state; }
+ DECLARE_WRITE_LINE_MEMBER( sl3_w ) { m_sl3 = state; }
+ DECLARE_WRITE_LINE_MEMBER( vsync_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+private:
+ enum
+ {
+ MS_WIDE_GRAPHICS,
+ MS_CHARACTER,
+ MS_THIN_GRAPHICS,
+ MS_UNDERLINE
+ };
+
+ crt9021_draw_character_delegate m_display_cb;
+
+ bitmap_rgb32 m_bitmap;
+
+ // inputs
+ UINT8 m_data;
+ int m_ms0;
+ int m_ms1;
+ int m_revid;
+ int m_chabl;
+ int m_blink;
+ int m_intin;
+ int m_atten;
+ int m_cursor;
+ int m_retbl;
+ int m_ld_sh;
+ int m_sld;
+ int m_slg;
+ int m_blc;
+ int m_bkc;
+ int m_sl0;
+ int m_sl1;
+ int m_sl2;
+ int m_sl3;
+ int m_vsync;
+
+ // outputs
+ UINT8 m_sr;
+ int m_intout;
+ int m_sl;
+};
+
+
+// device type definition
+extern const device_type CRT9021;
+
+
+
+#endif
diff --git a/src/devices/video/crt9212.c b/src/devices/video/crt9212.c
new file mode 100644
index 00000000000..0535afa169b
--- /dev/null
+++ b/src/devices/video/crt9212.c
@@ -0,0 +1,195 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ SMC CRT9212 Double Row Buffer (DRB) emulation
+
+**********************************************************************/
+
+#include "crt9212.h"
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type CRT9212 = &device_creator<crt9212_t>;
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// crt9212_t - constructor
+//-------------------------------------------------
+
+crt9212_t::crt9212_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, CRT9212, "SMC CRT9212", tag, owner, clock, "crt9212", __FILE__),
+ m_write_dout(*this),
+ m_write_rof(*this),
+ m_write_wof(*this),
+ m_data(0),
+ m_clrcnt(0),
+ m_tog(0),
+ m_ren(0),
+ m_wen1(1),
+ m_wen2(0),
+ m_oe(0),
+ m_rclk(0),
+ m_wclk(0),
+ m_clrcnt_edge(false),
+ m_data_latch(0),
+ m_ren_int(0),
+ m_wen_int(0),
+ m_buffer(0),
+ m_rac(0),
+ m_wac(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void crt9212_t::device_start()
+{
+ // resolve callbacks
+ m_write_dout.resolve_safe();
+ m_write_rof.resolve_safe();
+ m_write_wof.resolve_safe();
+
+ // state saving
+ save_item(NAME(m_data));
+ save_item(NAME(m_clrcnt));
+ save_item(NAME(m_tog));
+ save_item(NAME(m_ren));
+ save_item(NAME(m_wen1));
+ save_item(NAME(m_wen2));
+ save_item(NAME(m_oe));
+ save_item(NAME(m_rclk));
+ save_item(NAME(m_wclk));
+ save_item(NAME(m_clrcnt_edge));
+ save_item(NAME(m_data_latch));
+ save_item(NAME(m_ren_int));
+ save_item(NAME(m_wen_int));
+ save_item(NAME(m_ram[0]));
+ save_item(NAME(m_ram[1]));
+ save_item(NAME(m_buffer));
+ save_item(NAME(m_rac));
+ save_item(NAME(m_wac));
+}
+
+
+//-------------------------------------------------
+// clrcnt_w - clear counter
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( crt9212_t::clrcnt_w )
+{
+ if (m_clrcnt && !state)
+ {
+ m_clrcnt_edge = true;
+ }
+
+ m_clrcnt = state;
+}
+
+
+//-------------------------------------------------
+// rclk_w - read clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( crt9212_t::rclk_w )
+{
+ if (!m_rclk && state)
+ {
+ if (m_clrcnt_edge)
+ {
+ // reset read address counter
+ m_rac = 0;
+
+ // reset read overflow
+ m_write_rof(0);
+
+ if (!m_tog)
+ {
+ // switch buffers
+ m_buffer = !m_buffer;
+
+ // reset write address counter
+ m_wac = 0;
+
+ // reset write overflow
+ m_write_wof(0);
+ }
+
+ m_clrcnt_edge = false;
+ }
+
+ if (m_ren_int && (m_rac < CRT9212_RAM_SIZE))
+ {
+ // output data
+ m_write_dout(m_ram[m_rac][!m_buffer]);
+
+ // increment read address counter
+ m_rac++;
+
+ if (m_rac == CRT9212_RAM_SIZE - 1)
+ {
+ // set read overflow
+ m_write_rof(1);
+ }
+ }
+
+ m_ren_int = m_ren;
+ }
+
+ m_rclk = state;
+}
+
+
+//-------------------------------------------------
+// wclk_w - write clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( crt9212_t::wclk_w )
+{
+ if (!m_wclk && state)
+ {
+ if (m_wen_int && (m_wac < CRT9212_RAM_SIZE))
+ {
+ // input data
+ m_ram[m_rac][m_buffer] = m_data_latch;
+
+ // increment write address counter
+ m_wac++;
+
+ if (m_wac == CRT9212_RAM_SIZE - 1)
+ {
+ // set write overflow
+ m_write_wof(1);
+ }
+ }
+
+ if (m_wen1 && m_wen2)
+ {
+ m_data_latch = m_data;
+ }
+
+ m_wen_int = m_wen1 && m_wen2;
+ }
+
+ m_wclk = state;
+}
diff --git a/src/devices/video/crt9212.h b/src/devices/video/crt9212.h
new file mode 100644
index 00000000000..71590ee35f8
--- /dev/null
+++ b/src/devices/video/crt9212.h
@@ -0,0 +1,126 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ SMC CRT9212 Double Row Buffer (DRB) emulation
+
+**********************************************************************
+ _____ _____
+ DIN2 1 |* \_/ | 28 DIN3
+ DIN1 2 | | 27 _WCLK
+ DIN0 3 | | 26 _OE
+ DOUT7 4 | | 25 WEN2
+ DOUT6 5 | | 24 WEN1
+ DOUT5 6 | | 23 GND
+ DOUT4 7 | CRT9212 | 22 ROF
+ Vcc 8 | | 21 WOF
+ DOUT3 9 | | 20 REN
+ DOUT2 10 | | 19 _CLRCNT
+ DOUT1 11 | | 18 _TOG
+ DOUT0 12 | | 17 _RCLK
+ DIN7 13 | | 16 DIN4
+ DIN6 14 |_____________| 15 DIN5
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __CRT9212__
+#define __CRT9212__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+const int CRT9212_RAM_SIZE = 135;
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_CRT9212_WEN2_VCC() \
+ crt9212_t::static_set_wen2(*device, 1);
+
+#define MCFG_CRT9212_DOUT_CALLBACK(_write) \
+ devcb = &crt9212_t::set_dout_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9212_ROF_CALLBACK(_write) \
+ devcb = &crt9212_t::set_rof_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CRT9212_WOF_CALLBACK(_write) \
+ devcb = &crt9212_t::set_wof_wr_callback(*device, DEVCB_##_write);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> crt9212_t
+
+class crt9212_t : public device_t
+{
+public:
+ // construction/destruction
+ crt9212_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_wen2(device_t &device, int state) { downcast<crt9212_t &>(device).m_wen2 = state; }
+
+ template<class _Object> static devcb_base &set_dout_wr_callback(device_t &device, _Object object) { return downcast<crt9212_t &>(device).m_write_dout.set_callback(object); }
+ template<class _Object> static devcb_base &set_rof_wr_callback(device_t &device, _Object object) { return downcast<crt9212_t &>(device).m_write_rof.set_callback(object); }
+ template<class _Object> static devcb_base &set_wof_wr_callback(device_t &device, _Object object) { return downcast<crt9212_t &>(device).m_write_wof.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER( write ) { m_data = data; }
+ DECLARE_WRITE_LINE_MEMBER( clrcnt_w );
+ DECLARE_WRITE_LINE_MEMBER( tog_w ) { m_tog = state; }
+ DECLARE_WRITE_LINE_MEMBER( ren_w ) { m_ren = state; }
+ DECLARE_WRITE_LINE_MEMBER( wen1_w ) { m_wen1 = state; }
+ DECLARE_WRITE_LINE_MEMBER( wen2_w ) { m_wen2 = state; }
+ DECLARE_WRITE_LINE_MEMBER( oe_w ) { m_oe = state; }
+ DECLARE_WRITE_LINE_MEMBER( rclk_w );
+ DECLARE_WRITE_LINE_MEMBER( wclk_w );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+private:
+ devcb_write8 m_write_dout;
+ devcb_write_line m_write_rof;
+ devcb_write_line m_write_wof;
+
+ // inputs
+ UINT8 m_data;
+ int m_clrcnt;
+ int m_tog;
+ int m_ren;
+ int m_wen1;
+ int m_wen2;
+ int m_oe;
+ int m_rclk;
+ int m_wclk;
+
+ // internal state
+ bool m_clrcnt_edge;
+ UINT8 m_data_latch;
+ int m_ren_int;
+ int m_wen_int;
+ UINT8 m_ram[CRT9212_RAM_SIZE][2];
+ int m_buffer;
+ int m_rac;
+ int m_wac;
+};
+
+
+// device type definition
+extern const device_type CRT9212;
+
+
+
+#endif
diff --git a/src/devices/video/crtc_ega.c b/src/devices/video/crtc_ega.c
new file mode 100644
index 00000000000..102df087008
--- /dev/null
+++ b/src/devices/video/crtc_ega.c
@@ -0,0 +1,728 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ IBM EGA CRT Controller emulation
+
+ This controller is very loosely based on the mc6845.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "crtc_ega.h"
+
+
+#define LOG (1)
+
+
+const device_type CRTC_EGA = &device_creator<crtc_ega_device>;
+
+
+crtc_ega_device::crtc_ega_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CRTC_EGA, "crtc_EGA", tag, owner, clock, "crtc_ega", __FILE__),
+ device_video_interface(mconfig, *this, false),
+ m_res_out_de_cb(*this),
+ m_res_out_hsync_cb(*this),
+ m_res_out_vsync_cb(*this),
+ m_res_out_vblank_cb(*this),
+ m_hpixels_per_column(0)
+{
+}
+
+
+void crtc_ega_device::device_post_load()
+{
+ recompute_parameters(true);
+}
+
+
+WRITE8_MEMBER( crtc_ega_device::address_w )
+{
+ m_register_address_latch = data & 0x1f;
+}
+
+
+READ8_MEMBER( crtc_ega_device::register_r )
+{
+ UINT8 ret = 0;
+
+ switch (m_register_address_latch)
+ {
+ case 0x0c: ret = (m_disp_start_addr >> 8) & 0xff; break;
+ case 0x0d: ret = (m_disp_start_addr >> 0) & 0xff; break;
+ case 0x0e: ret = (m_cursor_addr >> 8) & 0xff; break;
+ case 0x0f: ret = (m_cursor_addr >> 0) & 0xff; break;
+ case 0x10: ret = (m_light_pen_addr >> 8) & 0xff; m_light_pen_latched = FALSE; break;
+ case 0x11: ret = (m_light_pen_addr >> 0) & 0xff; m_light_pen_latched = FALSE; break;
+
+ /* all other registers are write only and return 0 */
+ default: break;
+ }
+
+ return ret;
+}
+
+
+WRITE8_MEMBER( crtc_ega_device::register_w )
+{
+ if (LOG) logerror("%s CRTC_EGA: reg 0x%02x = 0x%02x\n", machine().describe_context(), m_register_address_latch, data);
+
+ switch (m_register_address_latch)
+ {
+ case 0x00: m_horiz_char_total = data & 0xff; break;
+ case 0x01: m_horiz_disp = data & 0xff; break;
+ case 0x02: m_horiz_blank_start = data & 0xff; break;
+ case 0x03: m_horiz_blank_end = ((data & 0x1f) << 0) | (m_horiz_blank_end & 0x20);
+ m_de_skew = ((data & 0x60) >> 5);
+ m_ena_vert_access = data & 0x80;
+ break;
+ case 0x04: m_horiz_retr_start = data & 0xff; break;
+ case 0x05: m_horiz_retr_end = data & 0x1f;
+ m_horiz_retr_skew = ((data & 0x60) >> 5);
+ m_horiz_blank_end = ((data & 0x80) >> 2) | (m_horiz_blank_end & 0x1f);
+ break;
+ case 0x06: m_vert_total = ((data & 0xff) << 0) | (m_vert_total & 0x0300); break;
+ case 0x07: m_vert_total = ((data & 0x01) << 8) | (m_vert_total & 0x02ff);
+ m_vert_disp_end = ((data & 0x02) << 7) | (m_vert_disp_end & 0x02ff);
+ m_vert_retr_start = ((data & 0x04) << 6) | (m_vert_retr_start & 0x02ff);
+ m_vert_blank_start = ((data & 0x08) << 5) | (m_vert_blank_start & 0x02ff);
+ m_line_compare = ((data & 0x10) << 4) | (m_line_compare & 0x02ff);
+ m_vert_total = ((data & 0x20) << 4) | (m_vert_total & 0x01ff);
+ m_vert_disp_end = ((data & 0x40) << 3) | (m_vert_disp_end & 0x1ff);
+ m_vert_retr_start = ((data & 0x80) << 2) | (m_vert_retr_start & 0x01ff);
+ break;
+ case 0x08: m_preset_row_scan = data & 0x1f;
+ m_byte_panning = ((data & 0x60) >> 5);
+ break;
+ case 0x09: m_max_ras_addr = data & 0x1f;
+ m_vert_blank_start = ((data & 0x20) << 4) | (m_vert_blank_start & 0x01ff);
+ m_line_compare = ((data & 0x40) << 3) | (m_line_compare & 0x01ff);
+ m_scan_doubling = data & 0x80;
+ break;
+ case 0x0a: m_cursor_start_ras = data & 0x1f;
+ m_cursor_disable = data & 0x20;
+ break;
+ case 0x0b: m_cursor_end_ras = data & 0x1f;
+ m_cursor_skew = ((data & 0x60) >> 5);
+ break;
+ case 0x0c: m_disp_start_addr = ((data & 0xff) << 8) | (m_disp_start_addr & 0x00ff); break;
+ case 0x0d: m_disp_start_addr = ((data & 0xff) << 0) | (m_disp_start_addr & 0xff00); break;
+ case 0x0e: m_cursor_addr = ((data & 0xff) << 8) | (m_cursor_addr & 0x00ff); break;
+ case 0x0f: m_cursor_addr = ((data & 0xff) << 0) | (m_cursor_addr & 0xff00); break;
+ case 0x10: m_vert_retr_start = ((data & 0xff) << 0) | (m_vert_retr_start & 0x0300); break;
+ case 0x11: m_vert_retr_end = data & 0x0f;
+ m_bandwidth = data & 0x40;
+ m_protect = data & 0x80;
+ break;
+ case 0x12: m_vert_disp_end = ((data & 0xff) << 0) | (m_vert_disp_end & 0x0300); break;
+ case 0x13: m_offset = data & 0xff; break;
+ case 0x14: m_underline_loc = data & 0x7f; break;
+ case 0x15: m_vert_blank_start = ((data & 0xff) << 0) | (m_vert_blank_start & 0x0300); break;
+ case 0x16: m_vert_blank_end = data & 0x7f; break;
+ case 0x17: m_mode_control = data & 0xff; break;
+ case 0x18: m_line_compare = ((data & 0xff) << 0) | (m_line_compare & 0x0300); break;
+ default: break;
+ }
+
+ recompute_parameters(false);
+}
+
+
+void crtc_ega_device::recompute_parameters(bool postload)
+{
+ UINT16 hsync_on_pos, hsync_off_pos, vsync_on_pos, vsync_off_pos;
+
+ /* compute the screen sizes */
+ UINT16 horiz_pix_total = (m_horiz_char_total + 2) * m_hpixels_per_column;
+ UINT16 vert_pix_total = m_vert_total + 1;
+
+ /* determine the visible area, avoid division by 0 */
+ UINT16 max_visible_x = ( m_horiz_disp + 1 ) * m_hpixels_per_column - 1;
+ UINT16 max_visible_y = m_vert_disp_end;
+
+ /* determine the syncing positions */
+ int horiz_sync_char_width = ( m_horiz_retr_end + 1 ) - ( m_horiz_retr_start & 0x1f );
+ int vert_sync_pix_width = m_vert_retr_end - ( m_vert_retr_start & 0x0f );
+
+ if (horiz_sync_char_width <= 0)
+ horiz_sync_char_width += 0x10;
+
+ if (vert_sync_pix_width <= 0)
+ vert_sync_pix_width += 0x10;
+
+ hsync_on_pos = m_horiz_retr_start * m_hpixels_per_column;
+ hsync_off_pos = hsync_on_pos + (horiz_sync_char_width * m_hpixels_per_column);
+ vsync_on_pos = m_vert_retr_start; /* + 1 ?? */
+ vsync_off_pos = vsync_on_pos + vert_sync_pix_width;
+
+ if (hsync_off_pos > horiz_pix_total)
+ hsync_off_pos = horiz_pix_total;
+
+ if (vsync_off_pos > vert_pix_total)
+ vsync_off_pos = vert_pix_total;
+
+ if ( vsync_on_pos >= vsync_off_pos )
+ {
+ vsync_on_pos = vsync_off_pos - 2;
+ }
+
+ /* update only if screen parameters changed, unless we are coming here after loading the saved state */
+ if (postload ||
+ (horiz_pix_total != m_horiz_pix_total) || (vert_pix_total != m_vert_pix_total) ||
+ (max_visible_x != m_max_visible_x) || (max_visible_y != m_max_visible_y) ||
+ (hsync_on_pos != m_hsync_on_pos) || (vsync_on_pos != m_vsync_on_pos) ||
+ (hsync_off_pos != m_hsync_off_pos) || (vsync_off_pos != m_vsync_off_pos))
+ {
+ /* update the screen if we have valid data */
+ if ((horiz_pix_total > 0) && (max_visible_x < horiz_pix_total) &&
+ (vert_pix_total > 0) && (max_visible_y < vert_pix_total) &&
+ (hsync_on_pos <= horiz_pix_total) && (vsync_on_pos <= vert_pix_total) &&
+ (hsync_on_pos != hsync_off_pos))
+ {
+ attoseconds_t refresh = HZ_TO_ATTOSECONDS(m_clock) * (m_horiz_char_total + 2) * vert_pix_total;
+
+ rectangle visarea(0, max_visible_x, 0, max_visible_y);
+
+ if (LOG) logerror("CRTC_EGA config screen: HTOTAL: 0x%x VTOTAL: 0x%x MAX_X: 0x%x MAX_Y: 0x%x HSYNC: 0x%x-0x%x VSYNC: 0x%x-0x%x Freq: %ffps\n",
+ horiz_pix_total, vert_pix_total, max_visible_x, max_visible_y, hsync_on_pos, hsync_off_pos - 1, vsync_on_pos, vsync_off_pos - 1, 1 / ATTOSECONDS_TO_DOUBLE(refresh));
+
+ if ( m_screen != NULL )
+ m_screen->configure(horiz_pix_total, vert_pix_total, visarea, refresh);
+
+ m_has_valid_parameters = true;
+ }
+ else
+ {
+ m_has_valid_parameters = false;
+ if (LOG) logerror("CRTC_EGA bad config screen: HTOTAL: 0x%x VTOTAL: 0x%x MAX_X: 0x%x MAX_Y: 0x%x HSYNC: 0x%x-0x%x VSYNC: 0x%x-0x%x\n",
+ horiz_pix_total, vert_pix_total, max_visible_x, max_visible_y, hsync_on_pos, hsync_off_pos - 1, vsync_on_pos, vsync_off_pos - 1);
+
+ }
+
+ m_horiz_pix_total = horiz_pix_total;
+ m_vert_pix_total = vert_pix_total;
+ m_max_visible_x = max_visible_x;
+ m_max_visible_y = max_visible_y;
+ m_hsync_on_pos = hsync_on_pos;
+ m_hsync_off_pos = hsync_off_pos;
+ m_vsync_on_pos = vsync_on_pos;
+ m_vsync_off_pos = vsync_off_pos;
+ }
+}
+
+
+void crtc_ega_device::update_counters()
+{
+ m_character_counter = m_line_timer->elapsed().as_ticks( m_clock );
+
+ if ( m_hsync_off_timer->enabled() )
+ {
+ m_hsync_width_counter = m_hsync_off_timer->elapsed().as_ticks( m_clock );
+ }
+}
+
+
+void crtc_ega_device::set_de(int state)
+{
+ if (m_de != state)
+ {
+ m_de = state;
+
+ if (!m_res_out_de_cb.isnull())
+ m_res_out_de_cb(m_de);
+ }
+}
+
+
+void crtc_ega_device::set_hsync(int state)
+{
+ if (m_hsync != state)
+ {
+ m_hsync = state;
+
+ if (!m_res_out_hsync_cb.isnull())
+ m_res_out_hsync_cb(m_hsync);
+ }
+}
+
+
+void crtc_ega_device::set_vsync(int state)
+{
+ if (m_vsync != state)
+ {
+ m_vsync = state;
+
+ if (!m_res_out_vsync_cb.isnull())
+ m_res_out_vsync_cb(m_vsync);
+ }
+}
+
+
+void crtc_ega_device::set_vblank(int state)
+{
+ if (m_vblank != state)
+ {
+ m_vblank = state;
+
+ if (!m_res_out_vblank_cb.isnull())
+ m_res_out_vblank_cb(m_vblank);
+ }
+}
+
+
+void crtc_ega_device::set_cur(int state)
+{
+ if (m_cur != state)
+ {
+ m_cur = state;
+
+// if (!m_res_out_cur_cb.isnull())
+// m_res_out_cur_cb(m_cur);
+ }
+}
+
+
+void crtc_ega_device::handle_line_timer()
+{
+ int new_vsync = m_vsync;
+
+ m_character_counter = 0;
+ m_cursor_x = -1;
+
+ /* Check if VSYNC is active */
+ if ( m_vsync_ff )
+ {
+ m_vsync_width_counter = ( m_vsync_width_counter + 1 ) & 0x0F;
+
+ /* Check if we've reached end of VSYNC */
+ if ( m_vsync_width_counter == m_vert_retr_end )
+ {
+ m_vsync_ff = 0;
+
+ new_vsync = FALSE;
+ }
+ }
+
+ if ( m_raster_counter == m_max_ras_addr )
+ {
+ m_raster_counter = 0;
+ m_line_address = ( m_line_address + m_horiz_disp + 1 ) & 0xffff;
+ }
+ else
+ {
+ m_raster_counter = ( m_raster_counter + 1 ) & 0x1F;
+ }
+
+ m_line_counter = ( m_line_counter + 1 ) & 0x3ff;
+
+ /* Check if we've reached the end of active display */
+ if ( m_line_counter == m_vert_disp_end )
+ {
+ m_line_enable_ff = false;
+ }
+
+ /* Check if VSYNC should be enabled */
+ if ( m_line_counter == m_vert_retr_start )
+ {
+ m_vsync_width_counter = 0;
+ m_vsync_ff = 1;
+
+ new_vsync = TRUE;
+ }
+
+ /* Check if we have reached the end of the vertical area */
+ if ( m_line_counter == m_vert_total )
+ {
+ m_line_counter = 0;
+ m_line_address = m_disp_start_addr;
+ m_line_enable_ff = true;
+ set_vblank( FALSE );
+ /* also update the cursor state now */
+ update_cursor_state();
+
+ if (m_screen != NULL)
+ m_screen->reset_origin();
+ }
+
+ if ( m_line_enable_ff )
+ {
+ /* Schedule DE off signal change */
+ m_de_off_timer->adjust(attotime::from_ticks( m_horiz_disp + 1, m_clock ));
+
+ /* Is cursor visible on this line? */
+ if ( m_cursor_state &&
+ (m_raster_counter >= (m_cursor_start_ras & 0x1f)) &&
+ (m_raster_counter <= m_cursor_end_ras) &&
+ (m_cursor_addr >= m_line_address) &&
+ (m_cursor_addr < (m_line_address + m_horiz_disp + 1)) )
+ {
+ m_cursor_x = m_cursor_addr - m_line_address;
+
+ /* Schedule CURSOR ON signal */
+ m_cur_on_timer->adjust( attotime::from_ticks( m_cursor_x, m_clock ) );
+ }
+ }
+
+ /* Schedule HSYNC on signal */
+ m_hsync_on_timer->adjust( attotime::from_ticks( m_horiz_blank_start, m_clock ) );
+
+ /* Set VBlank signal */
+ if ( m_line_counter == m_vert_disp_end + 1 )
+ {
+ set_vblank( TRUE );
+ }
+
+ /* Schedule our next callback */
+ m_line_timer->adjust( attotime::from_ticks( m_horiz_char_total + 2, m_clock ) );
+
+ /* Set VSYNC and DE signals */
+ set_vsync( new_vsync );
+ set_de( m_line_enable_ff ? TRUE : FALSE );
+}
+
+
+void crtc_ega_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_LINE:
+ handle_line_timer();
+ break;
+
+ case TIMER_DE_OFF:
+ set_de( FALSE );
+ break;
+
+ case TIMER_CUR_ON:
+ set_cur( TRUE );
+
+ /* Schedule CURSOR off signal */
+ m_cur_off_timer->adjust( attotime::from_ticks( 1, m_clock ) );
+ break;
+
+ case TIMER_CUR_OFF:
+ set_cur( FALSE );
+ break;
+
+ case TIMER_HSYNC_ON:
+ {
+ INT8 hsync_width = ( 0x20 | m_horiz_blank_end ) - ( m_horiz_blank_start & 0x1f );
+
+ if ( hsync_width <= 0 )
+ {
+ hsync_width += 0x20;
+ }
+
+ m_hsync_width_counter = 0;
+ set_hsync( TRUE );
+
+ /* Schedule HSYNC off signal */
+ m_hsync_off_timer->adjust( attotime::from_ticks( hsync_width, m_clock ) );
+ }
+ break;
+
+ case TIMER_HSYNC_OFF:
+ set_hsync( FALSE );
+ break;
+
+ case TIMER_LIGHT_PEN_LATCH:
+ m_light_pen_addr = get_ma();
+ m_light_pen_latched = true;
+ break;
+ }
+}
+
+
+UINT16 crtc_ega_device::get_ma()
+{
+ update_counters();
+
+ return m_line_address + m_character_counter;
+}
+
+
+UINT8 crtc_ega_device::get_ra()
+{
+ return m_raster_counter;
+}
+
+
+void crtc_ega_device::assert_light_pen_input()
+{
+ /* compute the pixel coordinate of the NEXT character -- this is when the light pen latches */
+ /* set the timer that will latch the display address into the light pen registers */
+ m_light_pen_latch_timer->adjust(attotime::from_ticks( 1, m_clock ));
+}
+
+
+void crtc_ega_device::set_clock(int clock)
+{
+ /* validate arguments */
+ assert(clock > 0);
+
+ if (clock != m_clock)
+ {
+ m_clock = clock;
+ recompute_parameters(true);
+ }
+}
+
+
+void crtc_ega_device::set_hpixels_per_column(int hpixels_per_column)
+{
+ /* validate arguments */
+ assert(hpixels_per_column > 0);
+
+ if (hpixels_per_column != m_hpixels_per_column)
+ {
+ m_hpixels_per_column = hpixels_per_column;
+ recompute_parameters(true);
+ }
+}
+
+
+void crtc_ega_device::update_cursor_state()
+{
+ /* save and increment cursor counter */
+ UINT8 last_cursor_blink_count = m_cursor_blink_count;
+ m_cursor_blink_count = m_cursor_blink_count + 1;
+
+ /* switch on cursor blinking mode */
+ switch (m_cursor_start_ras & 0x60)
+ {
+ /* always on */
+ case 0x00: m_cursor_state = true; break;
+
+ /* always off */
+ case 0x20: m_cursor_state = false; break;
+
+ /* fast blink */
+ case 0x40:
+ if ((last_cursor_blink_count & 0x10) != (m_cursor_blink_count & 0x10))
+ {
+ m_cursor_state = !m_cursor_state;
+ }
+ break;
+
+ /* slow blink */
+ case 0x60:
+ if ((last_cursor_blink_count & 0x20) != (m_cursor_blink_count & 0x20))
+ {
+ m_cursor_state = !m_cursor_state;
+ }
+ break;
+ }
+}
+
+
+UINT32 crtc_ega_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ assert(bitmap.valid());
+
+ if (m_has_valid_parameters)
+ {
+ UINT16 y;
+
+ assert(!m_row_update_cb.isnull());
+
+ /* call the set up function if any */
+ if (!m_begin_update_cb.isnull())
+ m_begin_update_cb(bitmap, cliprect);
+
+ if (cliprect.min_y == 0)
+ {
+ /* read the start address at the beginning of the frame */
+ m_current_disp_addr = m_disp_start_addr;
+ }
+
+ /* for each row in the visible region */
+ for (y = cliprect.min_y; y <= cliprect.max_y; y++)
+ {
+ /* compute the current raster line */
+ UINT8 ra = y % (m_max_ras_addr + 1);
+
+ /* check if the cursor is visible and is on this scanline */
+ int cursor_visible = m_cursor_state &&
+ (ra >= (m_cursor_start_ras & 0x1f)) &&
+ ( (ra <= (m_cursor_end_ras & 0x1f)) || ((m_cursor_end_ras & 0x1f) == 0x00 )) &&
+ (m_cursor_addr >= m_current_disp_addr) &&
+ (m_cursor_addr < (m_current_disp_addr + ( m_horiz_disp + 1 )));
+
+ /* compute the cursor X position, or -1 if not visible */
+ INT8 cursor_x = cursor_visible ? (m_cursor_addr - m_current_disp_addr) : -1;
+
+ /* call the external system to draw it */
+ m_row_update_cb(bitmap, cliprect, m_current_disp_addr, ra, y, m_horiz_disp + 1, cursor_x);
+
+ /* update MA if the last raster address */
+ if (ra == m_max_ras_addr)
+ m_current_disp_addr = (m_current_disp_addr + m_horiz_disp + 1) & 0xffff;
+ }
+
+ /* call the tear down function if any */
+ if (!m_end_update_cb.isnull())
+ m_end_update_cb(bitmap, cliprect);
+ }
+ else
+ logerror("Invalid crtc_ega screen parameters - display disabled!!!\n");
+
+ return 0;
+}
+
+
+/* device interface */
+void crtc_ega_device::device_start()
+{
+ /* validate arguments */
+ assert(m_clock > 0);
+ assert(m_hpixels_per_column > 0);
+
+ /* resolve callbacks */
+ m_res_out_de_cb.resolve();
+ m_res_out_hsync_cb.resolve();
+ m_res_out_vsync_cb.resolve();
+ m_res_out_vblank_cb.resolve();
+
+ /* bind delegates */
+ m_begin_update_cb.bind_relative_to(*owner());
+ m_row_update_cb.bind_relative_to(*owner());
+ m_end_update_cb.bind_relative_to(*owner());
+
+ /* create the timers */
+ m_line_timer = timer_alloc(TIMER_LINE);
+ m_de_off_timer = timer_alloc(TIMER_DE_OFF);
+ m_cur_on_timer = timer_alloc(TIMER_CUR_ON);
+ m_cur_off_timer = timer_alloc(TIMER_CUR_OFF);
+ m_hsync_on_timer = timer_alloc(TIMER_HSYNC_ON);
+ m_hsync_off_timer = timer_alloc(TIMER_HSYNC_OFF);
+ m_light_pen_latch_timer = timer_alloc(TIMER_LIGHT_PEN_LATCH);
+
+ /* Use some large startup values */
+ m_horiz_char_total = 0xff;
+ m_max_ras_addr = 0x1f;
+ m_vert_total = 0x3ff;
+
+ m_ena_vert_access = 0;
+ m_de_skew = 0;
+ m_horiz_retr_start = 0;
+ m_horiz_retr_end = 0;
+ m_horiz_retr_skew = 0;
+ m_preset_row_scan = 0;
+ m_byte_panning = 0;
+ m_scan_doubling = 0;
+ m_cursor_start_ras = 0x20;
+ m_cursor_disable = 0;
+ m_cursor_end_ras = 0;
+ m_cursor_skew = 0;
+ m_disp_start_addr = 0;
+ m_light_pen_addr = 0;
+ m_vert_retr_end = 0;
+ m_protect = 0;
+ m_bandwidth = 0;
+ m_offset = 0;
+ m_underline_loc = 0;
+ m_vert_blank_end = 0;
+ m_mode_control = 0;
+ m_line_compare = 0;
+ m_register_address_latch = 0;
+ m_cursor_state = false;
+ m_cursor_blink_count = 0;
+ m_cur = 0;
+ m_hsync = 0;
+ m_vsync = 0;
+ m_vblank = 0;
+ m_de = 0;
+ m_character_counter = 0;
+ m_hsync_width_counter = 0;
+ m_vsync_width_counter = 0;
+ m_line_enable_ff = false;
+ m_vsync_ff = 0;
+ m_adjust_active = 0;
+
+ m_current_disp_addr = 0;
+ m_light_pen_latched = FALSE;
+ m_has_valid_parameters = false;
+
+ /* register for state saving */
+ save_item(NAME(m_hpixels_per_column));
+ save_item(NAME(m_register_address_latch));
+ save_item(NAME(m_horiz_char_total));
+ save_item(NAME(m_horiz_disp));
+ save_item(NAME(m_horiz_blank_start));
+ save_item(NAME(m_mode_control));
+ save_item(NAME(m_cursor_start_ras));
+ save_item(NAME(m_cursor_end_ras));
+ save_item(NAME(m_disp_start_addr));
+ save_item(NAME(m_cursor_addr));
+ save_item(NAME(m_light_pen_addr));
+ save_item(NAME(m_light_pen_latched));
+ save_item(NAME(m_cursor_state));
+ save_item(NAME(m_cursor_blink_count));
+ save_item(NAME(m_horiz_blank_end));
+ save_item(NAME(m_ena_vert_access));
+ save_item(NAME(m_de_skew));
+ save_item(NAME(m_horiz_retr_start));
+ save_item(NAME(m_horiz_retr_end));
+ save_item(NAME(m_horiz_retr_skew));
+ save_item(NAME(m_vert_total));
+ save_item(NAME(m_preset_row_scan));
+ save_item(NAME(m_byte_panning));
+ save_item(NAME(m_max_ras_addr));
+ save_item(NAME(m_scan_doubling));
+ save_item(NAME(m_cursor_disable));
+ save_item(NAME(m_cursor_skew));
+ save_item(NAME(m_vert_retr_start));
+ save_item(NAME(m_vert_retr_end));
+ save_item(NAME(m_protect));
+ save_item(NAME(m_bandwidth));
+ save_item(NAME(m_vert_disp_end));
+ save_item(NAME(m_offset));
+ save_item(NAME(m_underline_loc));
+ save_item(NAME(m_vert_blank_start));
+ save_item(NAME(m_vert_blank_end));
+ save_item(NAME(m_line_compare));
+}
+
+
+void crtc_ega_device::device_reset()
+{
+ /* internal registers other than status remain unchanged, all outputs go low */
+ if (!m_res_out_de_cb.isnull())
+ m_res_out_de_cb(false);
+
+ if (!m_res_out_hsync_cb.isnull())
+ m_res_out_hsync_cb(false);
+
+ if (!m_res_out_vsync_cb.isnull())
+ m_res_out_vsync_cb(false);
+
+ if (!m_res_out_vblank_cb.isnull())
+ m_res_out_vblank_cb(false);
+
+ if (!m_line_timer->enabled())
+ {
+ m_line_timer->adjust( attotime::from_ticks( m_horiz_char_total + 2, m_clock ) );
+ }
+
+ m_light_pen_latched = false;
+
+ m_cursor_addr = 0;
+ m_line_address = 0;
+ m_horiz_disp = 0;
+ m_cursor_x = 0;
+ m_horiz_blank_start = 0;
+ m_horiz_blank_end = 0;
+ m_vert_disp_end = 0;
+ m_vert_retr_start = 0;
+ m_vert_blank_start = 0;
+ m_line_counter = 0;
+ m_raster_counter = 0;
+ m_horiz_pix_total = 0;
+ m_vert_pix_total = 0;
+ m_max_visible_x = 0;
+ m_max_visible_y = 0;
+ m_hsync_on_pos = 0;
+ m_vsync_on_pos = 0;
+ m_hsync_off_pos = 0;
+ m_vsync_off_pos = 0;
+}
diff --git a/src/devices/video/crtc_ega.h b/src/devices/video/crtc_ega.h
new file mode 100644
index 00000000000..b06fa31560c
--- /dev/null
+++ b/src/devices/video/crtc_ega.h
@@ -0,0 +1,236 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ IBM EGA CRT Controller emulation
+
+**********************************************************************/
+
+#ifndef __CRTC_EGA__
+#define __CRTC_EGA__
+
+#include "emu.h"
+
+/* callback definitions */
+typedef device_delegate<void (bitmap_ind16 &bitmap, const rectangle &cliprect)> crtc_ega_begin_update_delegate;
+
+typedef device_delegate<void (bitmap_ind16 &bitmap, const rectangle &cliprect, UINT16 ma, UINT8 ra,
+ UINT16 y, UINT8 x_count, INT8 cursor_x)> crtc_ega_row_update_delegate;
+
+typedef device_delegate<void (bitmap_ind16 &bitmap, const rectangle &cliprect)> crtc_ega_end_update_delegate;
+
+
+#define CRTC_EGA_BEGIN_UPDATE(_name) void _name(bitmap_ind16 &bitmap, const rectangle &cliprect)
+#define CRTC_EGA_ROW_UPDATE(_name) void _name(bitmap_ind16 &bitmap, \
+ const rectangle &cliprect, UINT16 ma, UINT8 ra, \
+ UINT16 y, UINT8 x_count, INT8 cursor_x)
+#define CRTC_EGA_END_UPDATE(_name) void _name(bitmap_ind16 &bitmap, const rectangle &cliprect)
+
+
+
+#define MCFG_CRTC_EGA_SET_SCREEN MCFG_VIDEO_SET_SCREEN
+
+#define MCFG_CRTC_EGA_BEGIN_UPDATE_CB(_class, _method) \
+ crtc_ega_device::set_begin_update_callback(*device, crtc_ega_begin_update_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_CRTC_EGA_ROW_UPDATE_CB(_class, _method) \
+ crtc_ega_device::set_row_update_callback(*device, crtc_ega_row_update_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_CRTC_EGA_END_UPDATE_CB(_class, _method) \
+ crtc_ega_device::set_end_update_callback(*device, crtc_ega_end_update_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_CRTC_EGA_HPIXELS_PER_COLUMN(_pix) \
+ crtc_ega_device::set_hpixels_per_column(*device, _pix);
+
+#define MCFG_CRTC_EGA_RES_OUT_DE_CB(_devcb) \
+ devcb = &crtc_ega_device::set_res_out_de_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_CRTC_EGA_RES_OUT_HSYNC_CB(_devcb) \
+ devcb = &crtc_ega_device::set_res_out_hsync_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_CRTC_EGA_RES_OUT_VSYNC_CB(_devcb) \
+ devcb = &crtc_ega_device::set_res_out_vsync_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_CRTC_EGA_RES_OUT_VBLANK_CB(_devcb) \
+ devcb = &crtc_ega_device::set_res_out_vblank_callback(*device, DEVCB_##_devcb);
+
+
+class crtc_ega_device : public device_t,
+ public device_video_interface
+{
+public:
+ crtc_ega_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_res_out_de_callback(device_t &device, _Object object)
+ { return downcast<crtc_ega_device &>(device).m_res_out_de_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_res_out_hsync_callback(device_t &device, _Object object)
+ { return downcast<crtc_ega_device &>(device).m_res_out_hsync_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_res_out_vsync_callback(device_t &device, _Object object)
+ { return downcast<crtc_ega_device &>(device).m_res_out_vsync_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_res_out_vblank_callback(device_t &device, _Object object)
+ { return downcast<crtc_ega_device &>(device).m_res_out_vblank_cb.set_callback(object); }
+
+ static void set_begin_update_callback(device_t &device, crtc_ega_begin_update_delegate callback) { downcast<crtc_ega_device &>(device).m_begin_update_cb = callback; }
+ static void set_row_update_callback(device_t &device, crtc_ega_row_update_delegate callback) { downcast<crtc_ega_device &>(device).m_row_update_cb = callback; }
+ static void set_end_update_callback(device_t &device, crtc_ega_end_update_delegate callback) { downcast<crtc_ega_device &>(device).m_end_update_cb = callback; }
+ static void set_hpixels_per_column(device_t &device, int hpixels_per_column) { downcast<crtc_ega_device &>(device).m_hpixels_per_column = hpixels_per_column; }
+
+ /* select one of the registers for reading or writing */
+ DECLARE_WRITE8_MEMBER( address_w );
+
+ /* read from the currently selected register */
+ DECLARE_READ8_MEMBER( register_r );
+
+ /* write to the currently selected register */
+ DECLARE_WRITE8_MEMBER( register_w );
+
+ /* return the current value on the MA0-MA15 pins */
+ UINT16 get_ma();
+
+ /* return the current value on the RA0-RA4 pins */
+ UINT8 get_ra();
+
+ /* simulates the LO->HI clocking of the light pen pin */
+ void assert_light_pen_input();
+
+ /* set the clock of the chip */
+ void set_clock(int clock);
+
+ /* set number of pixels per video memory address */
+ void set_hpixels_per_column(int hpixels_per_column);
+
+ /* updates the screen -- this will call begin_update(), */
+ /* followed by update_row() reapeatedly and after all row */
+ /* updating is complete, end_update() */
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ devcb_write_line m_res_out_de_cb;
+ devcb_write_line m_res_out_hsync_cb;
+ devcb_write_line m_res_out_vsync_cb;
+ devcb_write_line m_res_out_vblank_cb;
+
+ /* if specified, this gets called before any pixel update,
+ optionally return a pointer that will be passed to the
+ update and tear down callbacks */
+ crtc_ega_begin_update_delegate m_begin_update_cb;
+
+ /* this gets called for every row, the driver must output
+ x_count * hpixels_per_column pixels.
+ cursor_x indicates the character position where the cursor is, or -1
+ if there is no cursor on this row */
+ crtc_ega_row_update_delegate m_row_update_cb;
+
+ /* if specified, this gets called after all row updating is complete */
+ crtc_ega_end_update_delegate m_end_update_cb;
+
+ /* ega/vga register file */
+ UINT8 m_horiz_char_total; /* 0x00 */
+ UINT8 m_horiz_disp; /* 0x01 */
+ UINT8 m_horiz_blank_start; /* 0x02 */
+ UINT8 m_horiz_blank_end; /* 0x03/0x05 */
+ UINT8 m_ena_vert_access; /* 0x03 */
+ UINT8 m_de_skew; /* 0x03 */
+ UINT8 m_horiz_retr_start; /* 0x04 */
+ UINT8 m_horiz_retr_end; /* 0x05 */
+ UINT8 m_horiz_retr_skew; /* 0x05 */
+ UINT16 m_vert_total; /* 0x06/0x07 */
+ UINT8 m_preset_row_scan; /* 0x08 */
+ UINT8 m_byte_panning; /* 0x08 */
+ UINT8 m_max_ras_addr; /* 0x09 */
+ UINT8 m_scan_doubling; /* 0x09 */
+ UINT8 m_cursor_start_ras; /* 0x0a */
+ UINT8 m_cursor_disable; /* 0x0a */
+ UINT8 m_cursor_end_ras; /* 0x0b */
+ UINT8 m_cursor_skew; /* 0x0b */
+ UINT16 m_disp_start_addr; /* 0x0c/0x0d */
+ UINT16 m_cursor_addr; /* 0x0e/0x0f */
+ UINT16 m_light_pen_addr; /* 0x10/0x11 */
+ UINT16 m_vert_retr_start; /* 0x10/0x07 */
+ UINT8 m_vert_retr_end; /* 0x11 */
+ UINT8 m_protect; /* 0x11 */
+ UINT8 m_bandwidth; /* 0x11 */
+ UINT16 m_vert_disp_end; /* 0x12/0x07 */
+ UINT8 m_offset; /* 0x13 */
+ UINT8 m_underline_loc; /* 0x14 */
+ UINT16 m_vert_blank_start; /* 0x15/0x07/0x09 */
+ UINT8 m_vert_blank_end; /* 0x16 */
+ UINT8 m_mode_control; /* 0x17 */
+ UINT16 m_line_compare; /* 0x18/0x07/0x09 */
+
+ /* other internal state */
+ UINT8 m_register_address_latch;
+ bool m_cursor_state; /* 0 = off, 1 = on */
+ UINT8 m_cursor_blink_count;
+ int m_hpixels_per_column; /* number of pixels per video memory address */
+
+ /* output signals */
+ int m_cur;
+ int m_hsync;
+ int m_vsync;
+ int m_vblank;
+ int m_de;
+
+ /* internal counters */
+ UINT8 m_character_counter;
+ UINT8 m_hsync_width_counter;
+ UINT16 m_line_counter;
+ UINT8 m_raster_counter;
+ UINT8 m_vsync_width_counter;
+ bool m_line_enable_ff; /* Internal flip flop which is set when the line_counter is reset and reset when vert_disp is reached */
+ UINT8 m_vsync_ff;
+ UINT8 m_adjust_active;
+ UINT16 m_line_address;
+ INT16 m_cursor_x;
+
+ /* timers */
+ static const device_timer_id TIMER_LINE = 0;
+ static const device_timer_id TIMER_DE_OFF = 1;
+ static const device_timer_id TIMER_CUR_ON = 2;
+ static const device_timer_id TIMER_CUR_OFF = 3;
+ static const device_timer_id TIMER_HSYNC_ON = 4;
+ static const device_timer_id TIMER_HSYNC_OFF = 5;
+ static const device_timer_id TIMER_LIGHT_PEN_LATCH = 6;
+
+ emu_timer *m_line_timer;
+ emu_timer *m_de_off_timer;
+ emu_timer *m_cur_on_timer;
+ emu_timer *m_cur_off_timer;
+ emu_timer *m_hsync_on_timer;
+ emu_timer *m_hsync_off_timer;
+ emu_timer *m_light_pen_latch_timer;
+
+ /* computed values - do NOT state save these! */
+ UINT16 m_horiz_pix_total;
+ UINT16 m_vert_pix_total;
+ UINT16 m_max_visible_x;
+ UINT16 m_max_visible_y;
+ UINT16 m_hsync_on_pos;
+ UINT16 m_hsync_off_pos;
+ UINT16 m_vsync_on_pos;
+ UINT16 m_vsync_off_pos;
+ UINT16 m_current_disp_addr; /* the display address currently drawn */
+ UINT8 m_light_pen_latched;
+ bool m_has_valid_parameters;
+
+ void recompute_parameters(bool postload);
+ void update_counters();
+ void set_de(int state);
+ void set_hsync(int state);
+ void set_vsync(int state);
+ void set_vblank(int state);
+ void set_cur(int state);
+ void handle_line_timer();
+ void update_cursor_state();
+};
+
+extern const device_type CRTC_EGA;
+
+#endif
diff --git a/src/devices/video/dl1416.c b/src/devices/video/dl1416.c
new file mode 100644
index 00000000000..b017e9994e1
--- /dev/null
+++ b/src/devices/video/dl1416.c
@@ -0,0 +1,297 @@
+// license:GPL-2.0+
+// copyright-holders:Dirk Best
+/*****************************************************************************
+ *
+ * DL1416
+ *
+ *
+ * 4-Digit 16-Segment Alphanumeric Intelligent Display
+ * with Memory/Decoder/Driver
+ *
+ * Notes:
+ * - Currently supports the DL1416T and by virtue of it being nearly the same, the DL1414.
+ * - Partial support for DL1416B is available, it just needs the right
+ * character set and MAME core support for its display.
+ * - Cursor support is implemented but not tested, as the AIM65 does not
+ * seem to use it.
+ *
+ * Todo:
+ * - Is the DL1416A identical to the DL1416T? If not, we need to add
+ * support for it.
+ * - Add proper support for DL1414 (pretty much DL1416T without the cursor)
+ *
+ * Changes:
+ * - 2007-07-30: Initial version. [Dirk Best]
+ * - 2008-02-25: Converted to the new device interface. [Dirk Best]
+ * - 2008-12-18: Cleanups. [Dirk Best]
+ * - 2011-10-08: Changed the ram to store character rather than segment data. [Lord Nightmare]
+ *
+ *
+ * We use the following order for the segments:
+ *
+ * 000 111
+ * 7D A E2
+ * 7 D A E 2
+ * 7 DAE 2
+ * 888 999
+ * 6 CBF 3
+ * 6 C B F 3
+ * 6C B F3
+ * 555 444
+ *
+ ****************************************************************************/
+
+#include "emu.h"
+#include "dl1416.h"
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define SEG_UNDEF (0xfffe)
+#define SEG_BLANK (0)
+#define SEG_CURSOR (0xffff)
+#define CURSOR_ON (1)
+#define CURSOR_OFF (0)
+
+/* character set DL1416T */
+static const UINT16 dl1416t_segments[128] = {
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ 0x0000, 0x2421, 0x0480, 0x0f3c, /* ! " # */
+ 0x0fbb, 0x5f99, 0xa579, 0x4000, /* $ % & ' */
+ 0xc000, 0x3000, 0xff00, 0x0f00, /* ( ) * + */
+ 0x1000, 0x0300, 0x0020, 0x5000, /* , - . / */
+ 0x0ce1, 0x0c00, 0x0561, 0x0d21, /* 0 1 2 3 */
+ 0x0d80, 0x09a1, 0x09e1, 0x0c01, /* 4 5 6 7 */
+ 0x0de1, 0x0da1, 0x0021, 0x1001, /* 8 9 : ; */
+ 0x5030, 0x0330, 0xa030, 0x0a07, /* < = > ? */
+ 0x097f, 0x03cf, 0x0e3f, 0x00f3, /* @ A B C */
+ 0x0c3f, 0x01f3, 0x01c3, 0x02fb, /* D E F G */
+ 0x03cc, 0x0c33, 0x0c63, 0xc1c0, /* H I J K */
+ 0x00f0, 0x60cc, 0xa0cc, 0x00ff, /* L M N O */
+ 0x03c7, 0x80ff, 0x83c7, 0x03bb, /* P Q R S */
+ 0x0c03, 0x00fc, 0x50c0, 0x90cc, /* T U V W */
+ 0xf000, 0x6800, 0x5033, 0x00e1, /* X Y Z [ */
+ 0xa000, 0x001e, 0x9000, 0x0030, /* \ ] ^ _ */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
+ SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF /* undefined */
+};
+
+
+/*****************************************************************************
+ DEVICE INTERFACE
+*****************************************************************************/
+
+dl1416_device::dl1416_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_write_enable(FALSE),
+ m_chip_enable(FALSE),
+ m_cursor_enable(FALSE),
+ m_update(*this)
+{
+ for (int i = 0; i < 4; i++)
+ {
+ m_digit_ram[i] = 0;
+ m_cursor_state[i] = 0;
+ }
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void dl1416_device::device_start()
+{
+ /* register for state saving */
+ save_item(NAME(m_chip_enable));
+ save_item(NAME(m_cursor_enable));
+ save_item(NAME(m_write_enable));
+ save_item(NAME(m_digit_ram));
+
+ m_update.resolve();
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void dl1416_device::device_reset()
+{
+ int i;
+ UINT16 pattern;
+ /* disable all lines */
+ m_chip_enable = FALSE;
+ m_write_enable = FALSE;
+ m_cursor_enable = FALSE;
+
+ /* randomize digit and cursor memory */
+ for (i = 0; i < 4; i++)
+ {
+ m_digit_ram[i] = machine().rand()&0x3F;
+ // TODO: only enable the following line if the device actually has a cursor (DL1416T and DL1416B), if DL1414 then cursor is always 0!
+ //m_cursor_state[i] = ((device->machine().rand()&0xFF) >= 0x80) ? CURSOR_ON : CURSOR_OFF;
+ m_cursor_state[i] = CURSOR_OFF;
+ pattern = dl1416t_segments[m_digit_ram[i]];
+
+ /* If cursor for this digit position is enabled and segment is not */
+ /* undefined, replace digit with cursor */
+ if ((m_cursor_state[i] == CURSOR_ON) && (pattern != SEG_UNDEF))
+ pattern = SEG_CURSOR;
+
+ /* Undefined characters are replaced by blanks */
+ if (pattern == SEG_UNDEF)
+ pattern = SEG_BLANK;
+
+ /* Call update function */
+ if (!m_update.isnull())
+ m_update((offs_t)i, pattern);
+ }
+}
+
+
+const device_type DL1416B = &device_creator<dl1416b_device>;
+
+dl1416b_device::dl1416b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : dl1416_device(mconfig, DL1416B, "DL1416B", tag, owner, clock, "dl1416b", __FILE__)
+{
+}
+
+
+const device_type DL1416T = &device_creator<dl1416t_device>;
+
+dl1416t_device::dl1416t_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : dl1416_device(mconfig, DL1416T, "DL1416T", tag, owner, clock, "dl1416t", __FILE__)
+{
+}
+
+
+/*****************************************************************************
+ IMPLEMENTATION
+*****************************************************************************/
+
+/* write enable, active low */
+WRITE_LINE_MEMBER( dl1416_device::wr_w )
+{
+ m_write_enable = !state;
+}
+
+/* chip enable, active low */
+WRITE_LINE_MEMBER( dl1416_device::ce_w )
+{
+ m_chip_enable = !state;
+}
+
+/* cursor enable, active low */
+WRITE_LINE_MEMBER( dl1416_device::cu_w )
+{
+ m_cursor_enable = !state;
+}
+
+/* data */
+WRITE8_MEMBER( dl1416_device::data_w )
+{
+ offset &= 0x03; /* A0-A1 */
+ data &= 0x7f; /* D0-D6 */
+
+ /* Only try to update the data if we are enabled and write is enabled */
+ if (m_chip_enable && m_write_enable)
+ {
+ /* fprintf(stderr,"DL1416 Write: Cursor: %d, Offset: %d, Data: %02X\n (%c)", m_cursor_enable, offset, data, data); */
+ int i, pattern, previous_state;
+
+ if (m_cursor_enable) /* cursor enable is set */
+ {
+ if (type() == DL1416B)
+ {
+ /* DL1416B uses offset to decide cursor pos to change and D0 to hold new state */
+
+ /* The cursor will be set if D0 is high and the original */
+ /* character restored otherwise */
+ previous_state = m_cursor_state[offset];
+ m_cursor_state[offset] = data & 1 ? CURSOR_ON : CURSOR_OFF;
+
+ if (previous_state != m_cursor_state[offset])
+ {
+ pattern = dl1416t_segments[m_digit_ram[offset]];
+
+ /* If cursor for this digit position is enabled and segment is not */
+ /* undefined, replace digit with cursor */
+ if ((m_cursor_state[offset] == CURSOR_ON) && (pattern != SEG_UNDEF))
+ pattern = SEG_CURSOR;
+
+ /* Undefined characters are replaced by blanks */
+ if (pattern == SEG_UNDEF)
+ pattern = SEG_BLANK;
+
+ /* Call update function */
+ if (!m_update.isnull())
+ m_update(offset, pattern, mem_mask);
+ }
+ }
+ else {
+ /* DL1416T uses a bitmap of 4 data bits D0,D1,D2,D3 to decide cursor pos to change and new state */
+
+ for (i = 0; i < 4; i++)
+ {
+ /* The cursor will be set if D0-D3 is high and the original */
+ /* character at the appropriate position restored otherwise */
+ previous_state = m_cursor_state[i];
+ m_cursor_state[i] = data & (1<<i) ? CURSOR_ON : CURSOR_OFF;
+
+ if (previous_state != m_cursor_state[i])
+ {
+ pattern = dl1416t_segments[m_digit_ram[i]];
+
+ /* If cursor for this digit position is enabled and segment is not */
+ /* undefined, replace digit with cursor */
+ if ((m_cursor_state[i] == CURSOR_ON) && (pattern != SEG_UNDEF))
+ pattern = SEG_CURSOR;
+
+ /* Undefined characters are replaced by blanks */
+ if (pattern == SEG_UNDEF)
+ pattern = SEG_BLANK;
+
+ /* Call update function */
+ if (!m_update.isnull())
+ m_update(i, pattern, mem_mask);
+ }
+ }
+ }
+ }
+ else /* cursor enable is not set, so standard write */
+ {
+ /* Save written value */
+ m_digit_ram[offset] = data&0x3f;
+
+ /* Load segment pattern from ROM */
+ pattern = dl1416t_segments[data]; /** TODO: handle DL1416T vs DL1416B vs DL1414 here */
+
+ /* If cursor for this digit position is enabled and segment is not */
+ /* undefined, replace digit with cursor */
+ if ((m_cursor_state[offset] == CURSOR_ON) && (pattern != SEG_UNDEF))
+ pattern = SEG_CURSOR;
+
+ /* Undefined characters are replaced by blanks */
+ if (pattern == SEG_UNDEF)
+ pattern = SEG_BLANK;
+
+ /* Call update function */
+ if (!m_update.isnull())
+ m_update(offset, pattern, mem_mask);
+ }
+ }
+}
diff --git a/src/devices/video/dl1416.h b/src/devices/video/dl1416.h
new file mode 100644
index 00000000000..6b77a75edac
--- /dev/null
+++ b/src/devices/video/dl1416.h
@@ -0,0 +1,78 @@
+// license:GPL-2.0+
+// copyright-holders:Dirk Best
+/*****************************************************************************
+ *
+ * DL1416
+ *
+ * 4-Digit 16-Segment Alphanumeric Intelligent Display
+ * with Memory/Decoder/Driver
+ *
+ * See video/dl1416.c for more info
+ *
+ ****************************************************************************/
+
+#ifndef DL1416_H_
+#define DL1416_H_
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_DL1416_UPDATE_HANDLER(_devcb) \
+ devcb = &dl1416_device::set_update_handler(*device, DEVCB_##_devcb);
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* device get info callback */
+class dl1416_device : public device_t
+{
+public:
+ dl1416_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~dl1416_device() {}
+
+ template<class _Object> static devcb_base &set_update_handler(device_t &device, _Object object) { return downcast<dl1416_device &>(device).m_update.set_callback(object); }
+
+ /* inputs */
+ DECLARE_WRITE_LINE_MEMBER( wr_w ); /* write enable */
+ DECLARE_WRITE_LINE_MEMBER( ce_w ); /* chip enable */
+ DECLARE_WRITE_LINE_MEMBER( cu_w ); /* cursor enable */
+ DECLARE_WRITE8_MEMBER( data_w );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ // internal state
+ int m_write_enable;
+ int m_chip_enable;
+ int m_cursor_enable;
+ devcb_write16 m_update;
+
+ UINT16 m_digit_ram[4]; // holds the digit code for each position
+ UINT8 m_cursor_state[4]; // holds the cursor state for each position, 0=off, 1=on
+};
+
+class dl1416b_device : public dl1416_device
+{
+public:
+ dl1416b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type DL1416B;
+
+class dl1416t_device : public dl1416_device
+{
+public:
+ dl1416t_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type DL1416T;
+
+
+#endif /* DL1416_H_ */
diff --git a/src/devices/video/dm9368.c b/src/devices/video/dm9368.c
new file mode 100644
index 00000000000..16dee4e420f
--- /dev/null
+++ b/src/devices/video/dm9368.c
@@ -0,0 +1,96 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Fairchild DM9368 7-Segment Decoder/Driver/Latch emulation
+
+**********************************************************************/
+
+#include "dm9368.h"
+
+
+
+//**************************************************************************
+// DEVICE DEFINITION
+//**************************************************************************
+
+const device_type DM9368 = &device_creator<dm9368_device>;
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+const UINT8 dm9368_device::m_segment_data[16] =
+{
+ 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x67, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71
+};
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// dm9368_device - constructor
+//-------------------------------------------------
+
+dm9368_device::dm9368_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, DM9368, "DM9368 7-Segment Decoder", tag, owner, clock, "dm9368", __FILE__),
+ device_output_interface(mconfig, *this),
+ m_write_rbo(*this),
+ m_rbi(1),
+ m_rbo(1)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void dm9368_device::device_start()
+{
+ // resolve callbacks
+ m_write_rbo.resolve_safe();
+
+ // state saving
+ save_item(NAME(m_rbi));
+ save_item(NAME(m_rbo));
+}
+
+
+//-------------------------------------------------
+// a_w -
+//-------------------------------------------------
+
+void dm9368_device::a_w(UINT8 data)
+{
+ int a = data & 0x0f;
+ UINT8 value = 0;
+
+ if (!m_rbi && !a)
+ {
+ if (LOG) logerror("DM9368 '%s' Blanked Rippling Zero\n", tag());
+
+ // blank rippling 0
+ m_rbo = 0;
+ }
+ else
+ {
+ if (LOG) logerror("DM9368 '%s' Output Data: %u = %02x\n", tag(), a, m_segment_data[a]);
+
+ value = m_segment_data[a];
+
+ m_rbo = 1;
+ }
+
+ set_digit_value(value);
+
+ m_write_rbo(m_rbo);
+}
diff --git a/src/devices/video/dm9368.h b/src/devices/video/dm9368.h
new file mode 100644
index 00000000000..35a2da3431d
--- /dev/null
+++ b/src/devices/video/dm9368.h
@@ -0,0 +1,75 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Fairchild DM9368 7-Segment Decoder/Driver/Latch emulation
+
+**********************************************************************
+ _____ _____
+ A1 1 |* \_/ | 16 Vcc
+ A2 2 | | 15 F
+ _LE 3 | | 14 G
+ _RBO 4 | DM9368 | 13 A
+ _RBI 5 | | 12 B
+ A3 6 | | 11 C
+ A0 7 | | 10 D
+ GND 8 |_____________| 9 E
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __DM9368__
+#define __DM9368__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_DM9368_RBO_CALLBACK(_write) \
+ devcb = &dm9368_device::set_rbo_wr_callback(*device, DEVCB_##_read);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> dm9368_device
+
+class dm9368_device : public device_t,
+ public device_output_interface
+{
+public:
+ // construction/destruction
+ dm9368_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void a_w(UINT8 data);
+
+ DECLARE_WRITE_LINE_MEMBER( rbi_w ) { m_rbi = state; }
+ DECLARE_READ_LINE_MEMBER( rbo_r ) { return m_rbo; }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+private:
+ devcb_write_line m_write_rbo;
+
+ int m_rbi;
+ int m_rbo;
+
+ static const UINT8 m_segment_data[];
+};
+
+
+// device type definition
+extern const device_type DM9368;
+
+
+
+#endif
diff --git a/src/devices/video/ef9340_1.c b/src/devices/video/ef9340_1.c
new file mode 100644
index 00000000000..d3dd81f5d2e
--- /dev/null
+++ b/src/devices/video/ef9340_1.c
@@ -0,0 +1,383 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/***************************************************************************
+
+ ef9340_1.h
+
+ Thomson EF9340 + EF9341 teletext graphics chips with 1KB external
+ character ram.
+
+***************************************************************************/
+
+#include "ef9340_1.h"
+#include "ef9341_chargen.h"
+
+
+// device type definition
+const device_type EF9340_1 = &device_creator<ef9340_1_device>;
+
+
+static const UINT8 bgr2rgb[8] =
+{
+ 0x00, 0x04, 0x02, 0x06, 0x01, 0x05, 0x03, 0x07
+};
+
+
+ef9340_1_device::ef9340_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, EF9340_1, "EF9340+EF9341", tag, owner, clock, "ef9340_1", __FILE__)
+ , device_video_interface(mconfig, *this)
+ //, m_start_vpos(START_Y)
+ //, m_start_vblank(START_Y + SCREEN_HEIGHT)
+ //, m_screen_lines(LINES)
+{
+}
+
+
+void ef9340_1_device::device_start()
+{
+ // Let the screen create our temporary bitmap with the screen's dimensions
+ m_screen->register_screen_bitmap(m_tmp_bitmap);
+
+ m_line_timer = timer_alloc(TIMER_LINE);
+ m_line_timer->adjust( m_screen->time_until_pos(0, 0), 0, m_screen->scan_period() );
+
+ // register our state
+ save_item(NAME(m_ef9341.TA));
+ save_item(NAME(m_ef9341.TB));
+ save_item(NAME(m_ef9341.busy));
+ save_item(NAME(m_ef9340.X));
+ save_item(NAME(m_ef9340.Y));
+ save_item(NAME(m_ef9340.Y0));
+ save_item(NAME(m_ef9340.R));
+ save_item(NAME(m_ef9340.M));
+ save_pointer(NAME(m_ef934x_ram_a), 1024);
+ save_pointer(NAME(m_ef934x_ram_b), 1024);
+ save_pointer(NAME(m_ef934x_ext_char_ram), 1024);
+}
+
+
+void ef9340_1_device::device_reset()
+{
+ memset(m_ef934x_ram_a, 0, sizeof(m_ef934x_ram_a));
+ memset(m_ef934x_ram_b, 0, sizeof(m_ef934x_ram_b));
+
+ m_ef9340.X = 0;
+ m_ef9340.Y = 0;
+ m_ef9340.Y0 = 0;
+ m_ef9340.R = 0;
+ m_ef9340.M = 0;
+ m_ef9340.max_vpos = 210;
+ m_ef9341.TA = 0;
+ m_ef9341.TB = 0;
+ m_ef9341.busy = 0;
+}
+
+
+void ef9340_1_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch ( id )
+ {
+ case TIMER_LINE:
+ ef9340_scanline(m_screen->vpos());
+ break;
+ }
+}
+
+
+UINT16 ef9340_1_device::ef9340_get_c_addr(UINT8 x, UINT8 y)
+{
+ if ( ( y & 0x18 ) == 0x18 )
+ {
+ return 0x318 | ( ( x & 0x38 ) << 2 ) | ( x & 0x07 );
+ }
+ if ( x & 0x20 )
+ {
+ return 0x300 | ( ( y & 0x07 ) << 5 ) | ( y & 0x18 ) | ( x & 0x07 );
+ }
+ return y << 5 | x;
+}
+
+
+void ef9340_1_device::ef9340_inc_c()
+{
+ m_ef9340.X++;
+ if ( m_ef9340.X == 40 || m_ef9340.X == 48 || m_ef9340.X == 56 || m_ef9340.X == 64 )
+ {
+ m_ef9340.Y = ( m_ef9340.Y + 1 ) & 0x1f;
+ if ( m_ef9340.Y == 24 )
+ {
+ m_ef9340.Y = 0;
+ }
+ m_ef9340.X = 0;
+ }
+}
+
+
+UINT16 ef9340_1_device::external_chargen_address(UINT8 b, UINT8 slice)
+{
+ UINT8 cc = b & 0x7f;
+
+ if ( slice & 8 )
+ {
+ // 0 0 CCE4 CCE3 CCE2 CCE1 CCE0 CCE6 CCE5 ADR0
+ return ( ( cc << 3 ) & 0xf8 ) | ( ( cc >> 4 ) & 0x06) | ( slice & 0x01 );
+ }
+ // CCE6 CCE5 CCE4 CCE3 CCE2 CCE1 CCE0 ADR2 ADR1 ADR0
+ return ( cc << 3 ) | ( slice & 0x07 );
+}
+
+
+void ef9340_1_device::ef9341_write( UINT8 command, UINT8 b, UINT8 data )
+{
+ logerror("ef9341 %s write, t%s, data %02X\n", command ? "command" : "data", b ? "B" : "A", data );
+
+ if ( command )
+ {
+ if ( b )
+ {
+ m_ef9341.TB = data;
+ m_ef9341.busy = 0x80;
+ switch( m_ef9341.TB & 0xE0 )
+ {
+ case 0x00: /* Begin row */
+ m_ef9340.X = 0;
+ m_ef9340.Y = m_ef9341.TA & 0x1F;
+ break;
+ case 0x20: /* Load Y */
+ m_ef9340.Y = m_ef9341.TA & 0x1F;
+ break;
+ case 0x40: /* Load X */
+ m_ef9340.X = m_ef9341.TA & 0x3F;
+ break;
+ case 0x60: /* INC C */
+ ef9340_inc_c();
+ break;
+ case 0x80: /* Load M */
+ m_ef9340.M = m_ef9341.TA;
+ break;
+ case 0xA0: /* Load R */
+ m_ef9340.R = m_ef9341.TA;
+ m_ef9340.max_vpos = ( m_ef9340.R & 0x40 ) ? 250 : 210;
+ break;
+ case 0xC0: /* Load Y0 */
+ m_ef9340.Y0 = m_ef9341.TA & 0x3F;
+ break;
+ }
+ m_ef9341.busy = 0;
+ }
+ else
+ {
+ m_ef9341.TA = data;
+ }
+ }
+ else
+ {
+ if ( b )
+ {
+ UINT16 addr = ef9340_get_c_addr( m_ef9340.X, m_ef9340.Y ) & 0x3ff;
+
+ m_ef9341.TB = data;
+ m_ef9341.busy = 0x80;
+ switch ( m_ef9340.M & 0xE0 )
+ {
+ case 0x00: /* Write */
+ m_ef934x_ram_a[addr] = m_ef9341.TA;
+ m_ef934x_ram_b[addr] = m_ef9341.TB;
+ ef9340_inc_c();
+ break;
+
+ case 0x20: /* Read */
+ m_ef9341.TA = m_ef934x_ram_a[addr];
+ m_ef9341.TB = m_ef934x_ram_b[addr];
+ ef9340_inc_c();
+ break;
+
+ case 0x40: /* Write without increment */
+ m_ef934x_ram_a[addr] = m_ef9341.TA;
+ m_ef934x_ram_b[addr] = m_ef9341.TB;
+ break;
+
+ case 0x60: /* Read without increment */
+ m_ef9341.TA = m_ef934x_ram_a[addr];
+ m_ef9341.TB = m_ef934x_ram_b[addr];
+ break;
+
+ case 0x80: /* Write slice */
+ {
+ UINT8 a = m_ef934x_ram_a[addr];
+ UINT8 b = m_ef934x_ram_b[addr];
+ UINT8 slice = ( m_ef9340.M & 0x0f ) % 10;
+
+ if ( b >= 0xa0 )
+ {
+ m_ef934x_ext_char_ram[ ( ( a & 0x80 ) << 3 ) | external_chargen_address( b, slice ) ] = BITSWAP8(m_ef9341.TA,0,1,2,3,4,5,6,7);
+ }
+
+ // Increment slice number
+ m_ef9340.M = ( m_ef9340.M & 0xf0) | ( ( slice + 1 ) % 10 );
+ }
+ break;
+
+ case 0xA0: /* Read slice */
+ default:
+ fatalerror/*logerror*/("ef9341 unimplemented data action %02X\n", m_ef9340.M & 0xE0 );
+ }
+ m_ef9341.busy = 0;
+ }
+ else
+ {
+ m_ef9341.TA = data;
+ }
+ }
+}
+
+
+UINT8 ef9340_1_device::ef9341_read( UINT8 command, UINT8 b )
+{
+ UINT8 data = 0xFF;
+
+ logerror("ef9341 %s read, t%s\n", command ? "command" : "data", b ? "B" : "A" );
+ if ( command )
+ {
+ if ( b )
+ {
+ data = 0;
+ }
+ else
+ {
+ data = m_ef9341.busy;
+ }
+ }
+ else
+ {
+ if ( b )
+ {
+ data = m_ef9341.TB;
+ }
+ else
+ {
+ data = m_ef9341.TA;
+ }
+ }
+ return data;
+}
+
+
+void ef9340_1_device::ef9340_scanline(int vpos)
+{
+ if ( vpos < m_ef9340.max_vpos )
+ {
+ int y = vpos - 0;
+ int y_row, slice;
+
+ if ( y < 10 )
+ {
+ // Service row
+
+ if ( m_ef9340.R & 0x08 )
+ {
+ // Service row is enabled
+
+ y_row = 31;
+ slice = y;
+ }
+ else
+ {
+ // Service row is disabled
+
+ for ( int i = 0; i < 40 * 8; i++ )
+ {
+ m_tmp_bitmap.pix16(vpos, 0 + i ) = 24;
+ }
+ return;
+ }
+ }
+ else
+ {
+ // Displaying regular row
+ y_row = (y - 10) / 10;
+ slice = (y - 10) % 10;
+ }
+
+ for ( int x = 0; x < 40; x++ )
+ {
+ UINT16 addr = ef9340_get_c_addr( x, y_row );
+ UINT8 a = m_ef934x_ram_a[addr];
+ UINT8 b = m_ef934x_ram_b[addr];
+ UINT8 fg = 0;
+ UINT8 bg = 0;
+ UINT8 char_data = 0x00;
+
+ if ( a & 0x80 )
+ {
+ // Graphics
+ if ( b & 0x80 )
+ {
+ if ( b & 0x60 )
+ {
+ // Extension
+ char_data = m_ef934x_ext_char_ram[ 0x400 | external_chargen_address( b & 0x7f, slice ) ];
+ fg = bgr2rgb[ a & 0x07 ];
+ bg = bgr2rgb[ ( a >> 4 ) & 0x07 ];
+ }
+ }
+ else
+ {
+ // Normal
+ char_data = ef9341_char_set[1][b & 0x7f][slice];
+ fg = bgr2rgb[ a & 0x07 ];
+ bg = bgr2rgb[ ( a >> 4 ) & 0x07 ];
+ }
+ }
+ else
+ {
+ // Alphannumeric
+ if ( b & 0x80 )
+ {
+ if ( b & 0x60 )
+ {
+ // Extension
+ char_data = m_ef934x_ext_char_ram[ external_chargen_address( b & 0x7f, slice ) ];
+
+ if ( a & 0x40 )
+ {
+ fg = bg;
+ bg = bgr2rgb[ a & 0x07 ];
+ }
+ else
+ {
+ fg = bgr2rgb[ a & 0x07 ];
+ }
+ }
+ else
+ {
+ // DEL
+ char_data = 0xff;
+ fg = bgr2rgb[ a & 0x07 ];
+ }
+ }
+ else
+ {
+ // Normal
+ char_data = ef9341_char_set[0][b & 0x7f][slice];
+
+ if ( a & 0x40 )
+ {
+ fg = bg;
+ bg = bgr2rgb[ a & 0x07 ];
+ }
+ else
+ {
+ fg = bgr2rgb[ a & 0x07 ];
+ }
+ }
+ }
+
+ for ( int i = 0; i < 8; i++ )
+ {
+ m_tmp_bitmap.pix16(vpos, 0 + x*8 + i ) = (char_data & 0x80) ? fg : bg;
+ char_data <<= 1;
+ }
+ }
+ }
+}
diff --git a/src/devices/video/ef9340_1.h b/src/devices/video/ef9340_1.h
new file mode 100644
index 00000000000..3aa2d6539d9
--- /dev/null
+++ b/src/devices/video/ef9340_1.h
@@ -0,0 +1,81 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/***************************************************************************
+
+ ef9340_1.h
+
+ Thomson EF9340 + EF9341 teletext graphics chips with 1KB external
+ character ram.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __EF9340_1_H__
+#define __EF9340_1_H__
+
+#include "emu.h"
+
+
+#define MCFG_EF9340_1_ADD(_tag, _clock, _screen_tag) \
+ MCFG_DEVICE_ADD(_tag, EF9340_1, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag)
+
+class ef9340_1_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ ef9340_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ inline bitmap_ind16 *get_bitmap() { return &m_tmp_bitmap; }
+
+ void ef9341_write( UINT8 command, UINT8 b, UINT8 data );
+ UINT8 ef9341_read( UINT8 command, UINT8 b );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ inline UINT16 ef9340_get_c_addr(UINT8 x, UINT8 y);
+ inline void ef9340_inc_c();
+
+ // Calculate the external chargen address for a character and slice
+ inline UINT16 external_chargen_address(UINT8 b, UINT8 slice);
+
+ void ef9340_scanline(int vpos);
+
+ /* timers */
+ static const device_timer_id TIMER_LINE = 0;
+
+ emu_timer *m_line_timer;
+
+ bitmap_ind16 m_tmp_bitmap;
+
+ struct
+ {
+ UINT8 TA;
+ UINT8 TB;
+ UINT8 busy;
+ } m_ef9341;
+ struct
+ {
+ UINT8 X;
+ UINT8 Y;
+ UINT8 Y0;
+ UINT8 R;
+ UINT8 M;
+ int max_vpos;
+ } m_ef9340;
+ UINT8 m_ef934x_ram_a[1024];
+ UINT8 m_ef934x_ram_b[1024];
+ UINT8 m_ef934x_ext_char_ram[2048]; /* The G7400 has 2KB of external ram hooked up. The datasheet only describes how to hookup 1KB. */
+};
+
+
+// device type definition
+extern const device_type EF9340_1;
+
+#endif /* __EF9340_1_H__ */
diff --git a/src/devices/video/ef9341_chargen.h b/src/devices/video/ef9341_chargen.h
new file mode 100644
index 00000000000..d33db19d18e
--- /dev/null
+++ b/src/devices/video/ef9341_chargen.h
@@ -0,0 +1,269 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+static const UINT8 ef9341_char_set[2][128][10] = {
+ // Alphanumeric character set (128 characters)
+ {
+ { 0x00,0x38,0x44,0x40,0x20,0x10,0x00,0x10,0x00,0x00 },
+ { 0x00,0x10,0x28,0x00,0x38,0x44,0x7c,0x44,0x00,0x00 },
+ { 0x00,0x08,0x10,0x3c,0x20,0x30,0x20,0x3c,0x00,0x00 },
+ { 0x00,0x08,0x14,0x10,0x38,0x10,0x24,0x3c,0x00,0x00 },
+ { 0x00,0x10,0x38,0x50,0x38,0x14,0x54,0x38,0x10,0x00 },
+ { 0x00,0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x10,0x20 },
+ { 0x00,0x28,0x28,0x7c,0x28,0x7c,0x28,0x28,0x00,0x00 },
+ { 0x00,0x20,0x18,0x00,0x38,0x44,0x7c,0x44,0x00,0x00 },
+ { 0x00,0x20,0x18,0x00,0x44,0x44,0x44,0x38,0x00,0x00 },
+ { 0x00,0x10,0x08,0x3c,0x20,0x30,0x20,0x3c,0x00,0x00 },
+ { 0x00,0x3c,0x50,0x50,0x58,0x50,0x50,0x3c,0x00,0x00 },
+ { 0x00,0x08,0x14,0x3c,0x20,0x30,0x20,0x3c,0x00,0x00 },
+ { 0x00,0x00,0x10,0x20,0x7f,0x20,0x10,0x00,0x00,0x00 },
+ { 0x00,0x10,0x38,0x54,0x10,0x10,0x10,0x10,0x10,0x10 },
+ { 0x00,0x00,0x08,0x04,0xfe,0x04,0x08,0x00,0x00,0x00 },
+ { 0x10,0x10,0x10,0x10,0x10,0x10,0x54,0x38,0x10,0x00 },
+ { 0x00,0x18,0x24,0x18,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x7c,0x00,0x00 },
+ { 0x00,0x08,0x10,0x38,0x44,0x7c,0x40,0x38,0x00,0x00 },
+ { 0x00,0x28,0x00,0x38,0x44,0x7c,0x40,0x38,0x00,0x00 },
+ { 0x00,0x28,0x00,0x30,0x10,0x10,0x10,0x38,0x00,0x00 },
+ { 0x00,0x00,0x00,0x38,0x40,0x40,0x40,0x38,0x10,0x20 },
+ { 0x00,0x10,0x28,0x00,0x44,0x44,0x4c,0x34,0x00,0x00 },
+ { 0x00,0x20,0x10,0x34,0x4c,0x44,0x4c,0x34,0x00,0x00 },
+ { 0x00,0x00,0x10,0x00,0x7c,0x00,0x10,0x00,0x00,0x00 },
+ { 0x00,0x20,0x10,0x38,0x44,0x7c,0x40,0x38,0x00,0x00 },
+ { 0x00,0x00,0x00,0x3c,0x52,0x5e,0x50,0x3e,0x00,0x00 },
+ { 0x00,0x10,0x28,0x38,0x44,0x7c,0x40,0x38,0x00,0x00 },
+ { 0x00,0x40,0xc0,0x40,0x44,0x4c,0x14,0x3e,0x04,0x00 },
+ { 0x00,0x40,0xc0,0x40,0x4c,0x52,0x04,0x08,0x1e,0x00 },
+ { 0x00,0xe0,0x20,0x40,0x24,0xcc,0x14,0x3e,0x04,0x00 },
+ { 0x00,0x10,0x28,0x00,0x38,0x44,0x44,0x38,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00 },
+ { 0x00,0x28,0x28,0x28,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x28,0x00,0x3c,0x20,0x30,0x20,0x3c,0x00,0x00 },
+ { 0x00,0x10,0x28,0x34,0x4c,0x44,0x4c,0x34,0x00,0x00 },
+ { 0x00,0x60,0x64,0x08,0x10,0x20,0x4c,0x0c,0x00,0x00 },
+ { 0x00,0x20,0x50,0x50,0x20,0x54,0x48,0x34,0x00,0x00 },
+ { 0x00,0x10,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x08,0x10,0x20,0x20,0x20,0x10,0x08,0x00,0x00 },
+ { 0x00,0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00,0x00 },
+ { 0x00,0x10,0x54,0x38,0x10,0x38,0x54,0x10,0x00,0x00 },
+ { 0x00,0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x40,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00 },
+ { 0x01,0x02,0x02,0x04,0x08,0x10,0x20,0x20,0x40,0x80 },
+ { 0x00,0x10,0x28,0x44,0x44,0x44,0x28,0x10,0x00,0x00 },
+ { 0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x00,0x00 },
+ { 0x00,0x38,0x44,0x04,0x18,0x20,0x40,0x7c,0x00,0x00 },
+ { 0x00,0x7c,0x04,0x08,0x18,0x04,0x44,0x38,0x00,0x00 },
+ { 0x00,0x08,0x18,0x28,0x48,0x7c,0x08,0x08,0x00,0x00 },
+ { 0x00,0x7c,0x40,0x78,0x04,0x04,0x44,0x38,0x00,0x00 },
+ { 0x00,0x18,0x20,0x40,0x78,0x44,0x44,0x38,0x00,0x00 },
+ { 0x00,0x7c,0x04,0x08,0x10,0x20,0x20,0x20,0x00,0x00 },
+ { 0x00,0x38,0x44,0x44,0x38,0x44,0x44,0x38,0x00,0x00 },
+ { 0x00,0x38,0x44,0x44,0x3c,0x04,0x04,0x38,0x00,0x00 },
+ { 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00 },
+ { 0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x20,0x40,0x00 },
+ { 0x00,0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x00,0x00 },
+ { 0x00,0x00,0x00,0x7c,0x00,0x7c,0x00,0x00,0x00,0x00 },
+ { 0x00,0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x00 },
+ { 0x00,0x38,0x44,0x04,0x08,0x10,0x00,0x10,0x00,0x00 },
+ { 0x00,0x38,0x44,0x5c,0x54,0x5c,0x40,0x38,0x00,0x00 },
+ { 0x00,0x38,0x44,0x44,0x44,0x7c,0x44,0x44,0x00,0x00 },
+ { 0x00,0x78,0x44,0x44,0x78,0x44,0x44,0x78,0x00,0x00 },
+ { 0x00,0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00,0x00 },
+ { 0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00,0x00 },
+ { 0x00,0x7c,0x40,0x40,0x70,0x40,0x40,0x7c,0x00,0x00 },
+ { 0x00,0x7c,0x40,0x40,0x70,0x40,0x40,0x40,0x00,0x00 },
+ { 0x00,0x38,0x44,0x40,0x40,0x4c,0x44,0x3c,0x00,0x00 },
+ { 0x00,0x44,0x44,0x44,0x7c,0x44,0x44,0x44,0x00,0x00 },
+ { 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00 },
+ { 0x00,0x1c,0x08,0x08,0x08,0x08,0x48,0x30,0x00,0x00 },
+ { 0x00,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00 },
+ { 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x7c,0x00,0x00 },
+ { 0x00,0x44,0x6c,0x54,0x44,0x44,0x44,0x44,0x00,0x00 },
+ { 0x00,0x44,0x44,0x64,0x54,0x4c,0x44,0x44,0x00,0x00 },
+ { 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00 },
+ { 0x00,0x78,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00 },
+ { 0x00,0x38,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00 },
+ { 0x00,0x78,0x44,0x44,0x78,0x50,0x48,0x44,0x00,0x00 },
+ { 0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00,0x00 },
+ { 0x00,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00 },
+ { 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00 },
+ { 0x00,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00 },
+ { 0x00,0x44,0x44,0x44,0x54,0x54,0x54,0x28,0x00,0x00 },
+ { 0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00 },
+ { 0x00,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x00,0x00 },
+ { 0x00,0x7c,0x04,0x08,0x10,0x20,0x40,0x7c,0x00,0x00 },
+ { 0x00,0x1c,0x10,0x10,0x10,0x10,0x10,0x1c,0x00,0x00 },
+ { 0x80,0x40,0x40,0x20,0x10,0x08,0x04,0x04,0x02,0x01 },
+ { 0x00,0x38,0x08,0x08,0x08,0x08,0x08,0x38,0x00,0x00 },
+ { 0x00,0x10,0x28,0x00,0x30,0x10,0x10,0x38,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff },
+ { 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x34,0x4c,0x44,0x4c,0x34,0x00,0x00 },
+ { 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x78,0x00,0x00 },
+ { 0x00,0x00,0x00,0x38,0x40,0x40,0x40,0x38,0x00,0x00 },
+ { 0x00,0x04,0x04,0x3c,0x44,0x44,0x44,0x3c,0x00,0x00 },
+ { 0x00,0x00,0x00,0x38,0x44,0x7c,0x40,0x38,0x00,0x00 },
+ { 0x00,0x18,0x24,0x20,0x70,0x20,0x20,0x20,0x00,0x00 },
+ { 0x00,0x00,0x00,0x3c,0x44,0x44,0x3c,0x04,0x24,0x18 },
+ { 0x00,0x40,0x40,0x58,0x64,0x44,0x44,0x44,0x00,0x00 },
+ { 0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x38,0x00,0x00 },
+ { 0x00,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x48,0x30 },
+ { 0x00,0x20,0x20,0x24,0x28,0x30,0x28,0x24,0x00,0x00 },
+ { 0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00 },
+ { 0x00,0x00,0x00,0x68,0x54,0x54,0x54,0x54,0x00,0x00 },
+ { 0x00,0x00,0x00,0x58,0x64,0x44,0x44,0x44,0x00,0x00 },
+ { 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x38,0x00,0x00 },
+ { 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40 },
+ { 0x00,0x00,0x00,0x3c,0x44,0x44,0x44,0x3c,0x04,0x04 },
+ { 0x00,0x00,0x00,0x58,0x64,0x40,0x40,0x40,0x00,0x00 },
+ { 0x00,0x00,0x00,0x38,0x40,0x38,0x04,0x78,0x00,0x00 },
+ { 0x00,0x20,0x20,0x38,0x20,0x20,0x20,0x18,0x00,0x00 },
+ { 0x00,0x00,0x00,0x44,0x44,0x44,0x4c,0x34,0x00,0x00 },
+ { 0x00,0x00,0x00,0x44,0x44,0x28,0x28,0x10,0x00,0x00 },
+ { 0x00,0x00,0x00,0x44,0x44,0x54,0x54,0x28,0x00,0x00 },
+ { 0x00,0x00,0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00 },
+ { 0x00,0x00,0x00,0x44,0x44,0x4c,0x34,0x04,0x44,0x38 },
+ { 0x00,0x00,0x00,0x7c,0x08,0x10,0x20,0x7c,0x00,0x00 },
+ { 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 },
+ { 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10 },
+ { 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 },
+ { 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }
+ },
+
+ {
+ // Separated semi-graphic character set (64 characters)
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x77,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00 },
+ { 0x70,0x70,0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00 },
+ { 0x07,0x07,0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00 },
+ { 0x77,0x77,0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x07,0x07,0x07,0x00,0x00,0x00,0x00 },
+ { 0x70,0x70,0x00,0x07,0x07,0x07,0x00,0x00,0x00,0x00 },
+ { 0x07,0x07,0x00,0x07,0x07,0x07,0x00,0x00,0x00,0x00 },
+ { 0x77,0x77,0x00,0x07,0x07,0x07,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x77,0x77,0x77,0x00,0x00,0x00,0x00 },
+ { 0x70,0x70,0x00,0x77,0x77,0x77,0x00,0x00,0x00,0x00 },
+ { 0x07,0x07,0x00,0x77,0x77,0x77,0x00,0x00,0x00,0x00 },
+ { 0x77,0x77,0x00,0x77,0x77,0x77,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x70,0x00 },
+ { 0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x70,0x70,0x00 },
+ { 0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x70,0x70,0x00 },
+ { 0x77,0x77,0x00,0x00,0x00,0x00,0x00,0x70,0x70,0x00 },
+ { 0x00,0x00,0x00,0x70,0x70,0x70,0x00,0x70,0x70,0x00 },
+ { 0x70,0x70,0x00,0x70,0x70,0x70,0x00,0x70,0x70,0x00 },
+ { 0x07,0x07,0x00,0x70,0x70,0x70,0x00,0x70,0x70,0x00 },
+ { 0x77,0x77,0x00,0x70,0x70,0x70,0x00,0x70,0x70,0x00 },
+ { 0x00,0x00,0x00,0x07,0x07,0x07,0x00,0x70,0x70,0x00 },
+ { 0x70,0x70,0x00,0x07,0x07,0x07,0x00,0x70,0x70,0x00 },
+ { 0x07,0x07,0x00,0x07,0x07,0x07,0x00,0x70,0x70,0x00 },
+ { 0x77,0x77,0x00,0x07,0x07,0x07,0x00,0x70,0x70,0x00 },
+ { 0x00,0x00,0x00,0x77,0x77,0x77,0x00,0x70,0x70,0x00 },
+ { 0x70,0x70,0x00,0x77,0x77,0x77,0x00,0x70,0x70,0x00 },
+ { 0x07,0x07,0x00,0x77,0x77,0x77,0x00,0x70,0x70,0x00 },
+ { 0x77,0x77,0x00,0x77,0x77,0x77,0x00,0x70,0x70,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x00 },
+ { 0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x00 },
+ { 0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x00 },
+ { 0x77,0x77,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x00 },
+ { 0x00,0x00,0x00,0x70,0x70,0x70,0x00,0x07,0x07,0x00 },
+ { 0x70,0x70,0x00,0x70,0x70,0x70,0x00,0x07,0x07,0x00 },
+ { 0x07,0x07,0x00,0x70,0x70,0x70,0x00,0x07,0x07,0x00 },
+ { 0x77,0x77,0x00,0x70,0x70,0x70,0x00,0x07,0x07,0x00 },
+ { 0x00,0x00,0x00,0x07,0x07,0x07,0x00,0x07,0x07,0x00 },
+ { 0x70,0x70,0x00,0x07,0x07,0x07,0x00,0x07,0x07,0x00 },
+ { 0x07,0x07,0x00,0x07,0x07,0x07,0x00,0x07,0x07,0x00 },
+ { 0x77,0x77,0x00,0x07,0x07,0x07,0x00,0x07,0x07,0x00 },
+ { 0x00,0x00,0x00,0x77,0x77,0x77,0x00,0x07,0x07,0x00 },
+ { 0x70,0x70,0x00,0x77,0x77,0x77,0x00,0x07,0x07,0x00 },
+ { 0x07,0x07,0x00,0x77,0x77,0x77,0x00,0x07,0x07,0x00 },
+ { 0x77,0x77,0x00,0x77,0x77,0x77,0x00,0x07,0x07,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x77,0x77,0x00 },
+ { 0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x77,0x77,0x00 },
+ { 0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x77,0x77,0x00 },
+ { 0x77,0x77,0x00,0x00,0x00,0x00,0x00,0x77,0x77,0x00 },
+ { 0x00,0x00,0x00,0x70,0x70,0x70,0x00,0x77,0x77,0x00 },
+ { 0x70,0x70,0x00,0x70,0x70,0x70,0x00,0x77,0x77,0x00 },
+ { 0x07,0x07,0x00,0x70,0x70,0x70,0x00,0x77,0x77,0x00 },
+ { 0x77,0x77,0x00,0x70,0x70,0x70,0x00,0x77,0x77,0x00 },
+ { 0x00,0x00,0x00,0x07,0x07,0x07,0x00,0x77,0x77,0x00 },
+ { 0x70,0x70,0x00,0x07,0x07,0x07,0x00,0x77,0x77,0x00 },
+ { 0x07,0x07,0x00,0x07,0x07,0x07,0x00,0x77,0x77,0x00 },
+ { 0x77,0x77,0x00,0x07,0x07,0x07,0x00,0x77,0x77,0x00 },
+ { 0x00,0x00,0x00,0x77,0x77,0x77,0x00,0x77,0x77,0x00 },
+ { 0x70,0x70,0x00,0x77,0x77,0x77,0x00,0x77,0x77,0x00 },
+ { 0x07,0x07,0x00,0x77,0x77,0x77,0x00,0x77,0x77,0x00 },
+ { 0x77,0x77,0x00,0x77,0x77,0x77,0x00,0x77,0x77,0x00 },
+
+ // Mosaic semi-graphic character set (64 characters)
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00 },
+ { 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00 },
+ { 0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00 },
+ { 0xff,0xff,0xff,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00 },
+ { 0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00 },
+ { 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00 },
+ { 0xff,0xff,0xff,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00 },
+ { 0xf0,0xf0,0xf0,0xff,0xff,0xff,0xff,0x00,0x00,0x00 },
+ { 0x0f,0x0f,0x0f,0xff,0xff,0xff,0xff,0x00,0x00,0x00 },
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0 },
+ { 0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0 },
+ { 0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0 },
+ { 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0 },
+ { 0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 },
+ { 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 },
+ { 0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 },
+ { 0xff,0xff,0xff,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 },
+ { 0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0 },
+ { 0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0 },
+ { 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0 },
+ { 0xff,0xff,0xff,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0 },
+ { 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0 },
+ { 0xf0,0xf0,0xf0,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0 },
+ { 0x0f,0x0f,0x0f,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0 },
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f },
+ { 0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f },
+ { 0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f },
+ { 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f },
+ { 0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x0f,0x0f,0x0f },
+ { 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0f,0x0f,0x0f },
+ { 0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0x0f,0x0f,0x0f },
+ { 0xff,0xff,0xff,0xf0,0xf0,0xf0,0xf0,0x0f,0x0f,0x0f },
+ { 0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f },
+ { 0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f },
+ { 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f },
+ { 0xff,0xff,0xff,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f },
+ { 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x0f,0x0f },
+ { 0xf0,0xf0,0xf0,0xff,0xff,0xff,0xff,0x0f,0x0f,0x0f },
+ { 0x0f,0x0f,0x0f,0xff,0xff,0xff,0xff,0x0f,0x0f,0x0f },
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0x0f,0x0f },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff },
+ { 0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0xff,0xff,0xff },
+ { 0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0xff,0xff,0xff },
+ { 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff },
+ { 0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0xff,0xff,0xff },
+ { 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xff,0xff,0xff },
+ { 0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0xff,0xff,0xff },
+ { 0xff,0xff,0xff,0xf0,0xf0,0xf0,0xf0,0xff,0xff,0xff },
+ { 0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0xff,0xff,0xff },
+ { 0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,0xff,0xff,0xff },
+ { 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xff,0xff,0xff },
+ { 0xff,0xff,0xff,0x0f,0x0f,0x0f,0x0f,0xff,0xff,0xff },
+ { 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ { 0xf0,0xf0,0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ { 0x0f,0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }
+ }
+};
diff --git a/src/devices/video/ef9345.c b/src/devices/video/ef9345.c
new file mode 100644
index 00000000000..ca8062bbb95
--- /dev/null
+++ b/src/devices/video/ef9345.c
@@ -0,0 +1,1021 @@
+// license:GPL-2.0+
+// copyright-holders:Daniel Coulom,Sandro Ronco
+/*********************************************************************
+
+ ef9345.c
+
+ Thomson EF9345 video controller emulator code
+
+ This code is based on Daniel Coulom's implementation in DCVG5k
+ and DCAlice released by Daniel Coulom under GPL license
+
+*********************************************************************/
+
+#include "emu.h"
+#include "ef9345.h"
+
+#define MODE24x40 0
+#define MODEVAR40 1
+#define MODE8x80 2
+#define MODE12x80 3
+#define MODE16x40 4
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+const device_type EF9345 = &device_creator<ef9345_device>;
+
+// default address map
+static ADDRESS_MAP_START( ef9345, AS_0, 8, ef9345_device )
+ AM_RANGE(0x0000, 0x3fff) AM_RAM
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *ef9345_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+// calculate the internal RAM offset
+inline UINT16 ef9345_device::indexram(UINT8 r)
+{
+ UINT8 x = m_registers[r];
+ UINT8 y = m_registers[r - 1];
+ if (y < 8)
+ y &= 1;
+ return ((x&0x3f) | ((x & 0x40) << 6) | ((x & 0x80) << 4) | ((y & 0x1f) << 6) | ((y & 0x20) << 8));
+}
+
+// calculate the internal ROM offset
+inline UINT16 ef9345_device::indexrom(UINT8 r)
+{
+ UINT8 x = m_registers[r];
+ UINT8 y = m_registers[r - 1];
+ if (y < 8)
+ y &= 1;
+ return((x&0x3f)|((x&0x40)<<6)|((x&0x80)<<4)|((y&0x1f)<<6));
+}
+
+// increment x
+inline void ef9345_device::inc_x(UINT8 r)
+{
+ UINT8 i = (m_registers[r] & 0x3f) + 1;
+ if (i > 39)
+ {
+ i -= 40;
+ m_state |= 0x40;
+ }
+ m_registers[r] = (m_registers[r] & 0xc0) | i;
+}
+
+// increment y
+inline void ef9345_device::inc_y(UINT8 r)
+{
+ UINT8 i = (m_registers[r] & 0x1f) + 1;
+ if (i > 31)
+ i -= 24;
+ m_registers[r] = (m_registers[r] & 0xe0) | i;
+}
+
+
+//**************************************************************************
+// live device
+//**************************************************************************
+
+//-------------------------------------------------
+// ef9345_device - constructor
+//-------------------------------------------------
+
+ef9345_device::ef9345_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, EF9345, "EF9345", tag, owner, clock, "ef9345", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(ef9345)),
+ m_palette(*this)
+{
+}
+
+//-------------------------------------------------
+// static_set_palette_tag: Set the tag of the
+// palette device
+//-------------------------------------------------
+
+void ef9345_device::static_set_palette_tag(device_t &device, const char *tag)
+{
+ downcast<ef9345_device &>(device).m_palette.set_tag(tag);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ef9345_device::device_start()
+{
+ m_busy_timer = timer_alloc(BUSY_TIMER);
+ m_blink_timer = timer_alloc(BLINKING_TIMER);
+
+ m_videoram = &space(0);
+ m_charset = region();
+
+ m_screen_out.allocate(496, m_screen->height());
+
+ m_blink_timer->adjust(attotime::from_msec(500), 0, attotime::from_msec(500));
+
+ init_accented_chars();
+
+ save_item(NAME(m_border));
+ save_item(NAME(m_registers));
+ save_item(NAME(m_last_dial));
+ save_item(NAME(m_ram_base));
+ save_item(NAME(m_bf));
+ save_item(NAME(m_char_mode));
+ save_item(NAME(m_state));
+ save_item(NAME(m_tgs));
+ save_item(NAME(m_mat));
+ save_item(NAME(m_pat));
+ save_item(NAME(m_dor));
+ save_item(NAME(m_ror));
+ save_item(NAME(m_block));
+ save_item(NAME(m_blink));
+ save_item(NAME(m_latchc0));
+ save_item(NAME(m_latchm));
+ save_item(NAME(m_latchi));
+ save_item(NAME(m_latchu));
+
+ save_item(NAME(m_screen_out));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+void ef9345_device::device_reset()
+{
+ m_tgs = m_mat = m_pat = m_dor = m_ror = 0;
+ m_state = 0;
+ m_bf = 0;
+ m_block = 0;
+ m_blink = 0;
+ m_latchc0 = 0;
+ m_latchm = 0;
+ m_latchi = 0;
+ m_latchu = 0;
+ m_char_mode = 0;
+
+ memset(m_last_dial, 0, sizeof(m_last_dial));
+ memset(m_registers, 0, sizeof(m_registers));
+ memset(m_border, 0, sizeof(m_border));
+ memset(m_border, 0, sizeof(m_ram_base));
+
+ m_screen_out.fill(0);
+
+ set_video_mode();
+}
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+void ef9345_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case BUSY_TIMER:
+ m_bf = 0;
+ break;
+
+ case BLINKING_TIMER:
+ m_blink = !m_blink;
+ break;
+ }
+}
+
+
+// set busy flag and timer to clear it
+void ef9345_device::set_busy_flag(int period)
+{
+ m_bf = 1;
+ m_busy_timer->adjust(attotime::from_usec(period));
+}
+
+// draw a char in 40 char line mode
+void ef9345_device::draw_char_40(UINT8 *c, UINT16 x, UINT16 y)
+{
+ //verify size limit
+ if (y * 10 >= m_screen->height() || x * 8 >= m_screen->width())
+ return;
+
+ const rgb_t *palette = m_palette->palette()->entry_list_raw();
+ for(int i = 0; i < 10; i++)
+ for(int j = 0; j < 8; j++)
+ m_screen_out.pix32(y * 10 + i, x * 8 + j) = palette[c[8 * i + j] & 0x07];
+}
+
+// draw a char in 80 char line mode
+void ef9345_device::draw_char_80(UINT8 *c, UINT16 x, UINT16 y)
+{
+ // verify size limit
+ if (y * 10 >= m_screen->height() || x * 6 >= m_screen->width())
+ return;
+
+ const rgb_t *palette = m_palette->palette()->entry_list_raw();
+ for(int i = 0; i < 10; i++)
+ for(int j = 0; j < 6; j++)
+ m_screen_out.pix32(y * 10 + i, x * 6 + j) = palette[c[6 * i + j] & 0x07];
+}
+
+
+// set then ef9345 mode
+void ef9345_device::set_video_mode(void)
+{
+ m_char_mode = ((m_pat & 0x80) >> 5) | ((m_tgs & 0xc0) >> 6);
+ UINT16 new_width = (m_char_mode == MODE12x80 || m_char_mode == MODE8x80) ? 492 : 336;
+
+ if (m_screen->width() != new_width)
+ {
+ rectangle visarea = m_screen->visible_area();
+ visarea.max_x = new_width - 1;
+
+ m_screen->configure(new_width, m_screen->height(), visarea, m_screen->frame_period().attoseconds());
+ }
+
+ //border color
+ memset(m_border, m_mat & 0x07, sizeof(m_border));
+
+ //set the base for the m_videoram charset
+ m_ram_base[0] = ((m_dor & 0x07) << 11);
+ m_ram_base[1] = m_ram_base[0];
+ m_ram_base[2] = ((m_dor & 0x30) << 8);
+ m_ram_base[3] = m_ram_base[2] + 0x0800;
+
+ //address of the current memory block
+ m_block = 0x0800 * ((((m_ror & 0xf0) >> 4) | ((m_ror & 0x40) >> 5) | ((m_ror & 0x20) >> 3)) & 0x0c);
+}
+
+// initialize the ef9345 accented chars
+void ef9345_device::init_accented_chars(void)
+{
+ UINT16 i, j;
+ for(j = 0; j < 0x10; j++)
+ for(i = 0; i < 0x200; i++)
+ m_acc_char[(j << 9) + i] = m_charset->u8(0x0600 + i);
+
+ for(j = 0; j < 0x200; j += 0x40)
+ for(i = 0; i < 4; i++)
+ {
+ m_acc_char[0x0200 + j + i + 4] |= 0x1c; //tilde
+ m_acc_char[0x0400 + j + i + 4] |= 0x10; //acute
+ m_acc_char[0x0400 + j + i + 8] |= 0x08; //acute
+ m_acc_char[0x0600 + j + i + 4] |= 0x04; //grave
+ m_acc_char[0x0600 + j + i + 8] |= 0x08; //grave
+
+ m_acc_char[0x0a00 + j + i + 4] |= 0x1c; //tilde
+ m_acc_char[0x0c00 + j + i + 4] |= 0x10; //acute
+ m_acc_char[0x0c00 + j + i + 8] |= 0x08; //acute
+ m_acc_char[0x0e00 + j + i + 4] |= 0x04; //grave
+ m_acc_char[0x0e00 + j + i + 8] |= 0x08; //grave
+
+ m_acc_char[0x1200 + j + i + 4] |= 0x08; //point
+ m_acc_char[0x1400 + j + i + 4] |= 0x14; //trema
+ m_acc_char[0x1600 + j + i + 32] |= 0x08; //cedilla
+ m_acc_char[0x1600 + j + i + 36] |= 0x04; //cedilla
+
+ m_acc_char[0x1a00 + j + i + 4] |= 0x08; //point
+ m_acc_char[0x1c00 + j + i + 4] |= 0x14; //trema
+ m_acc_char[0x1e00 + j + i + 32] |= 0x08; //cedilla
+ m_acc_char[0x1e00 + j + i + 36] |= 0x04; //cedilla
+ }
+}
+
+// read a char in charset or in m_videoram
+UINT8 ef9345_device::read_char(UINT8 index, UINT16 addr)
+{
+ if (index < 0x04)
+ return m_charset->u8(0x0800*index + addr);
+ else if (index < 0x08)
+ return m_acc_char[0x0800*(index&3) + addr];
+ else if (index < 0x0c)
+ return m_videoram->read_byte(m_ram_base[index-8] + addr);
+ else
+ return m_videoram->read_byte(addr);
+}
+
+// calculate the dial position of the char
+UINT8 ef9345_device::get_dial(UINT8 x, UINT8 attrib)
+{
+ if (x > 0 && m_last_dial[x-1] == 1) //top right
+ m_last_dial[x] = 2;
+ else if (x > 0 && m_last_dial[x-1] == 5) //half right
+ m_last_dial[x] = 10;
+ else if (m_last_dial[x] == 1) //bottom left
+ m_last_dial[x] = 4;
+ else if (m_last_dial[x] == 2) //bottom right
+ m_last_dial[x] = 8;
+ else if (m_last_dial[x] == 3) //lower half
+ m_last_dial[x] = 12;
+ else if (attrib == 1) //Left half
+ m_last_dial[x] = 5;
+ else if (attrib == 2) //half high
+ m_last_dial[x] = 3;
+ else if (attrib == 3) //top left
+ m_last_dial[x] = 1;
+ else //none
+ m_last_dial[x] = 0;
+
+ return m_last_dial[x];
+}
+
+// zoom the char
+void ef9345_device::zoom(UINT8 *pix, UINT16 n)
+{
+ UINT8 i, j;
+ if ((n & 0x0a) == 0)
+ for(i = 0; i < 80; i += 8) // 1, 4, 5
+ for(j = 7; j > 0; j--)
+ pix[i + j] = pix[i + j / 2];
+
+ if ((n & 0x05) == 0)
+ for(i = 0; i < 80; i += 8) // 2, 8, 10
+ for(j =0 ; j < 7; j++)
+ pix[i + j] = pix[i + 4 + j / 2];
+
+ if ((n & 0x0c) == 0)
+ for(i = 0; i < 8; i++) // 1, 2, 3
+ for(j = 9; j > 0; j--)
+ pix[i + 8 * j] = pix[i + 8 * (j / 2)];
+
+ if ((n & 0x03) == 0)
+ for(i = 0; i < 8; i++) // 4, 8, 12
+ for(j = 0; j < 9; j++)
+ pix[i + 8 * j] = pix[i + 40 + 8 * (j / 2)];
+}
+
+
+// calculate the address of the char x,y
+UINT16 ef9345_device::indexblock(UINT16 x, UINT16 y)
+{
+ UINT16 i = x, j;
+ j = (y == 0) ? ((m_tgs & 0x20) >> 5) : ((m_ror & 0x1f) + y - 1);
+
+ //right side of a double width character
+ if ((m_tgs & 0x80) == 0 && x > 0)
+ {
+ if (m_last_dial[x - 1] == 1) i--;
+ if (m_last_dial[x - 1] == 4) i--;
+ if (m_last_dial[x - 1] == 5) i--;
+ }
+
+ return 0x40 * j + i;
+}
+
+// draw bichrome character (40 columns)
+void ef9345_device::bichrome40(UINT8 type, UINT16 address, UINT8 dial, UINT16 iblock, UINT16 x, UINT16 y, UINT8 c0, UINT8 c1, UINT8 insert, UINT8 flash, UINT8 hided, UINT8 negative, UINT8 underline)
+{
+ UINT16 i;
+ UINT8 pix[80];
+
+ if (flash && m_pat & 0x40 && m_blink)
+ c1 = c0; //flash
+ if (hided && m_pat & 0x08)
+ c1 = c0; //hided
+ if (negative) //negative
+ {
+ i = c1;
+ c1 = c0;
+ c0 = i;
+ }
+
+ if ((m_pat & 0x30) == 0x30)
+ insert = 0; //active area mark
+ if (insert == 0)
+ c1 += 8; //foreground color
+ if ((m_pat & 0x30) == 0x00)
+ insert = 1; //insert mode
+ if (insert == 0)
+ c0 += 8; //background color
+
+ //draw the cursor
+ i = (m_registers[6] & 0x1f);
+ if (i < 8)
+ i &= 1;
+
+ if (iblock == 0x40 * i + (m_registers[7] & 0x3f)) //cursor position
+ {
+ switch(m_mat & 0x70)
+ {
+ case 0x40: //00 = fixed complemented
+ c0 = (23 - c0) & 15;
+ c1 = (23 - c1) & 15;
+ break;
+ case 0x50: //01 = fixed underlined
+ underline = 1;
+ break;
+ case 0x60: //10 = flash complemented
+ if (m_blink)
+ {
+ c0 = (23 - c0) & 15;
+ c1 = (23 - c1) & 15;
+ }
+ break;
+ case 0x70: //11 = flash underlined
+ if (m_blink)
+ underline = 1;
+ break;
+ }
+ }
+
+ // generate the pixel table
+ for(i = 0; i < 40; i+=4)
+ {
+ UINT8 ch = read_char(type, address + i);
+
+ for (UINT8 b=0; b<8; b++)
+ pix[i*2 + b] = (ch & (1<<b)) ? c1 : c0;
+ }
+
+ //draw the underline
+ if (underline)
+ memset(&pix[72], c1, 8);
+
+ if (dial > 0)
+ zoom(pix, dial);
+
+ //doubles the height of the char
+ if (m_mat & 0x80)
+ zoom(pix, (y & 0x01) ? 0x0c : 0x03);
+
+ draw_char_40(pix, x + 1 , y + 1);
+}
+
+// draw quadrichrome character (40 columns)
+void ef9345_device::quadrichrome40(UINT8 c, UINT8 b, UINT8 a, UINT16 x, UINT16 y)
+{
+ //C0-6= character code
+ //B0= insert not yet implemented !!!
+ //B1= low resolution
+ //B2= subset index (low resolution only)
+ //B3-5 = set number
+ //A0-6 = 4 color palette
+
+ UINT8 i, j, n, col[8], pix[80];
+ UINT8 lowresolution = (b & 0x02) >> 1, ramx, ramy, ramblock;
+ UINT16 ramindex;
+
+ //quadrichrome don't suppor double size
+ m_last_dial[x] = 0;
+
+ //initialize the color table
+ for(j = 1, n = 0, i = 0; i < 8; i++)
+ {
+ col[n++] = (a & j) ? i : 7;
+ j <<= 1;
+ }
+
+ //find block number in ram
+ ramblock = 0;
+ if (b & 0x20) ramblock |= 4; //B5
+ if (b & 0x08) ramblock |= 2; //B3
+ if (b & 0x10) ramblock |= 1; //B4
+
+ //find character address in ram
+ ramx = c & 0x03;
+ ramy =(c & 0x7f) >> 2;
+ ramindex = 0x0800 * ramblock + 0x40 * ramy + ramx;
+ if (lowresolution) ramindex += 5 * (b & 0x04);
+
+ //fill pixel table
+ for(i = 0, j = 0; i < 10; i++)
+ {
+ UINT8 ch = read_char(0x0c, ramindex + 4 * (i >> lowresolution));
+ pix[j] = pix[j + 1] = col[(ch & 0x03) >> 0]; j += 2;
+ pix[j] = pix[j + 1] = col[(ch & 0x0c) >> 2]; j += 2;
+ pix[j] = pix[j + 1] = col[(ch & 0x30) >> 4]; j += 2;
+ pix[j] = pix[j + 1] = col[(ch & 0xc0) >> 6]; j += 2;
+ }
+
+ draw_char_40(pix, x + 1, y + 1);
+}
+
+// draw bichrome character (80 columns)
+void ef9345_device::bichrome80(UINT8 c, UINT8 a, UINT16 x, UINT16 y)
+{
+ UINT8 c0, c1, pix[60];
+ UINT16 i, j, d;
+
+ c1 = (a & 1) ? (m_dor >> 4) & 7 : m_dor & 7; //foreground color = DOR
+ c0 = m_mat & 7; //background color = MAT
+
+ switch(c & 0x80)
+ {
+ case 0: //alphanumeric G0 set
+ //A0: D = color set
+ //A1: U = underline
+ //A2: F = flash
+ //A3: N = negative
+ //C0-6: character code
+
+ if ((a & 4) && (m_pat & 0x40) && (m_blink))
+ c1 = c0; //flash
+ if (a & 8) //negative
+ {
+ i = c1;
+ c1 = c0;
+ c0 = i;
+ }
+
+ d = ((c & 0x7f) >> 2) * 0x40 + (c & 0x03); //char position
+
+ for(i=0, j=0; i < 10; i++)
+ {
+ UINT8 ch = read_char(0, d + 4 * i);
+ for (UINT8 b=0; b<6; b++)
+ pix[j++] = (ch & (1<<b)) ? c1 : c0;
+ }
+
+ //draw the underline
+ if (a & 2)
+ memset(&pix[54], c1, 6);
+
+ break;
+ default: //dedicated mosaic set
+ //A0: D = color set
+ //A1-3: 3 blocks de 6 pixels
+ //C0-6: 7 blocks de 6 pixels
+ pix[ 0] = (c & 0x01) ? c1 : c0;
+ pix[ 3] = (c & 0x02) ? c1 : c0;
+ pix[12] = (c & 0x04) ? c1 : c0;
+ pix[15] = (c & 0x08) ? c1 : c0;
+ pix[24] = (c & 0x10) ? c1 : c0;
+ pix[27] = (c & 0x20) ? c1 : c0;
+ pix[36] = (c & 0x40) ? c1 : c0;
+ pix[39] = (a & 0x02) ? c1 : c0;
+ pix[48] = (a & 0x04) ? c1 : c0;
+ pix[51] = (a & 0x08) ? c1 : c0;
+
+ for(i = 0; i < 60; i += 12)
+ {
+ pix[i + 6] = pix[i];
+ pix[i + 9] = pix[i + 3];
+ }
+
+ for(i = 0; i < 60; i += 3)
+ pix[i + 2] = pix[i + 1] = pix[i];
+
+ break;
+ }
+
+ draw_char_80(pix, x, y);
+}
+
+// generate 16 bits 40 columns char
+void ef9345_device::makechar_16x40(UINT16 x, UINT16 y)
+{
+ UINT8 a, b, c0, c1, i, f, m, n, u, type, dial;
+ UINT16 address, iblock;
+
+ iblock = (m_mat & 0x80 && y > 1) ? indexblock(x, y / 2) : indexblock(x, y);
+ a = m_videoram->read_byte(m_block + iblock);
+ b = m_videoram->read_byte(m_block + iblock + 0x0800);
+
+ dial = get_dial(x, (a & 0x80) ? 0 : (((a & 0x20) >> 5) | ((a & 0x10) >> 3)));
+
+ //type and address of the char
+ type = ((b & 0x80) >> 4) | ((a & 0x80) >> 6);
+ address = ((b & 0x7f) >> 2) * 0x40 + (b & 0x03);
+
+ //negative space
+ if ((b & 0xe0) == 0x80)
+ {
+ address = 0;
+ type = 3;
+ }
+
+ //reset attributes latch
+ if (x == 0)
+ m_latchm = m_latchi = m_latchu = m_latchc0 = 0;
+
+ if (type == 4)
+ {
+ m_latchm = b & 1;
+ m_latchi = (b & 2) >> 1;
+ m_latchu = (b & 4) >> 2;
+ }
+
+ if (a & 0x80)
+ m_latchc0 = (a & 0x70) >> 4;
+
+ //char attributes
+ c0 = m_latchc0; //background
+ c1 = a & 0x07; //foreground
+ i = m_latchi; //insert mode
+ f = (a & 0x08) >> 3; //flash
+ m = m_latchm; //hided
+ n = (a & 0x80) ? 0: ((a & 0x40) >> 6); //negative
+ u = m_latchu; //underline
+
+ bichrome40(type, address, dial, iblock, x, y, c0, c1, i, f, m, n, u);
+}
+
+// generate 24 bits 40 columns char
+void ef9345_device::makechar_24x40(UINT16 x, UINT16 y)
+{
+ UINT8 a, b, c, c0, c1, i, f, m, n, u, type, dial;
+ UINT16 address, iblock;
+
+ iblock = (m_mat & 0x80 && y > 1) ? indexblock(x, y / 2) : indexblock(x, y);
+ c = m_videoram->read_byte(m_block + iblock);
+ b = m_videoram->read_byte(m_block + iblock + 0x0800);
+ a = m_videoram->read_byte(m_block + iblock + 0x1000);
+
+ if ((b & 0xc0) == 0xc0)
+ {
+ quadrichrome40(c, b, a, x, y);
+ return;
+ }
+
+ dial = get_dial(x, (b & 0x02) + ((b & 0x08) >> 3));
+
+ //type and address of the char
+ address = ((c & 0x7f) >> 2) * 0x40 + (c & 0x03);
+ type = (b & 0xf0) >> 4;
+
+ //char attributes
+ c0 = a & 0x07; //background
+ c1 = (a & 0x70) >> 4; //foreground
+ i = b & 0x01; //insert
+ f = (a & 0x08) >> 3; //flash
+ m = (b & 0x04) >> 2; //hided
+ n = ((a & 0x80) >> 7); //negative
+ u = (((b & 0x60) == 0) || ((b & 0xc0) == 0x40)) ? ((b & 0x10) >> 4) : 0; //underline
+
+ bichrome40(type, address, dial, iblock, x, y, c0, c1, i, f, m, n, u);
+}
+
+// generate 12 bits 80 columns char
+void ef9345_device::makechar_12x80(UINT16 x, UINT16 y)
+{
+ UINT16 iblock = indexblock(x, y);
+ bichrome80(m_videoram->read_byte(m_block + iblock), (m_videoram->read_byte(m_block + iblock + 0x1000) >> 4) & 0x0f, 2 * x + 1, y + 1);
+ bichrome80(m_videoram->read_byte(m_block + iblock + 0x0800), m_videoram->read_byte(m_block + iblock + 0x1000) & 0x0f, 2 * x + 2, y + 1);
+}
+
+void ef9345_device::draw_border(UINT16 line)
+{
+ if (m_char_mode == MODE12x80 || m_char_mode == MODE8x80)
+ for(int i = 0; i < 82; i++)
+ draw_char_80(m_border, i, line);
+ else
+ for(int i = 0; i < 42; i++)
+ draw_char_40(m_border, i, line);
+}
+
+void ef9345_device::makechar(UINT16 x, UINT16 y)
+{
+ switch (m_char_mode)
+ {
+ case MODE24x40:
+ makechar_24x40(x, y);
+ break;
+ case MODEVAR40:
+ case MODE8x80:
+ logerror("Unemulated EF9345 mode: %02x\n", m_char_mode);
+ break;
+ case MODE12x80:
+ makechar_12x80(x, y);
+ break;
+ case MODE16x40:
+ makechar_16x40(x, y);
+ break;
+ default:
+ logerror("Unknown EF9345 mode: %02x\n", m_char_mode);
+ break;
+ }
+}
+
+// Execute EF9345 command
+void ef9345_device::ef9345_exec(UINT8 cmd)
+{
+ m_state = 0;
+ if ((m_registers[5] & 0x3f) == 39) m_state |= 0x10; //S4(LXa) set
+ if ((m_registers[7] & 0x3f) == 39) m_state |= 0x20; //S5(LXm) set
+
+ UINT16 a = indexram(7);
+
+ switch(cmd)
+ {
+ case 0x00: //KRF: R1,R2,R3->ram
+ case 0x01: //KRF: R1,R2,R3->ram + increment
+ set_busy_flag(4);
+ m_videoram->write_byte(a, m_registers[1]);
+ m_videoram->write_byte(a + 0x0800, m_registers[2]);
+ m_videoram->write_byte(a + 0x1000, m_registers[3]);
+ if (cmd&1) inc_x(7);
+ break;
+ case 0x02: //KRG: R1,R2->ram
+ case 0x03: //KRG: R1,R2->ram + increment
+ set_busy_flag(5.5);
+ m_videoram->write_byte(a, m_registers[1]);
+ m_videoram->write_byte(a + 0x0800, m_registers[2]);
+ if (cmd&1) inc_x(7);
+ break;
+ case 0x08: //KRF: ram->R1,R2,R3
+ case 0x09: //KRF: ram->R1,R2,R3 + increment
+ set_busy_flag(7.5);
+ m_registers[1] = m_videoram->read_byte(a);
+ m_registers[2] = m_videoram->read_byte(a + 0x0800);
+ m_registers[3] = m_videoram->read_byte(a + 0x1000);
+ if (cmd&1) inc_x(7);
+ break;
+ case 0x0a: //KRG: ram->R1,R2
+ case 0x0b: //KRG: ram->R1,R2 + increment
+ set_busy_flag(7.5);
+ m_registers[1] = m_videoram->read_byte(a);
+ m_registers[2] = m_videoram->read_byte(a + 0x0800);
+ if (cmd&1) inc_x(7);
+ break;
+ case 0x30: //OCT: R1->RAM, main pointer
+ case 0x31: //OCT: R1->RAM, main pointer + inc
+ set_busy_flag(4);
+ m_videoram->write_byte(indexram(7), m_registers[1]);
+
+ if (cmd&1)
+ {
+ inc_x(7);
+ if ((m_registers[7] & 0x3f) == 0)
+ inc_y(6);
+ }
+ break;
+ case 0x34: //OCT: R1->RAM, aux pointer
+ case 0x35: //OCT: R1->RAM, aux pointer + inc
+ set_busy_flag(4);
+ m_videoram->write_byte(indexram(5), m_registers[1]);
+
+ if (cmd&1)
+ inc_x(5);
+ break;
+ case 0x38: //OCT: RAM->R1, main pointer
+ case 0x39: //OCT: RAM->R1, main pointer + inc
+ set_busy_flag(4.5);
+ m_registers[1] = m_videoram->read_byte(indexram(7));
+
+ if (cmd&1)
+ {
+ inc_x(7);
+
+ if ((m_registers[7] & 0x3f) == 0)
+ inc_y(6);
+ }
+ break;
+ case 0x3c: //OCT: RAM->R1, aux pointer
+ case 0x3d: //OCT: RAM->R1, aux pointer + inc
+ set_busy_flag(4.5);
+ m_registers[1] = m_videoram->read_byte(indexram(5));
+
+ if (cmd&1)
+ inc_x(5);
+ break;
+ case 0x50: //KRL: 80 UINT8 - 12 bits write
+ case 0x51: //KRL: 80 UINT8 - 12 bits write + inc
+ set_busy_flag(12.5);
+ m_videoram->write_byte(a, m_registers[1]);
+ switch((a / 0x0800) & 1)
+ {
+ case 0:
+ {
+ UINT8 tmp_data = m_videoram->read_byte(a + 0x1000);
+ m_videoram->write_byte(a + 0x1000, (tmp_data & 0x0f) | (m_registers[3] & 0xf0));
+ break;
+ }
+ case 1:
+ {
+ UINT8 tmp_data = m_videoram->read_byte(a + 0x0800);
+ m_videoram->write_byte(a + 0x0800, (tmp_data & 0xf0) | (m_registers[3] & 0x0f));
+ break;
+ }
+ }
+ if (cmd&1)
+ {
+ if ((m_registers[7] & 0x80) == 0x00) { m_registers[7] |= 0x80; return; }
+ m_registers[7] &= 0x80;
+ inc_x(7);
+ }
+ break;
+ case 0x58: //KRL: 80 UINT8 - 12 bits read
+ case 0x59: //KRL: 80 UINT8 - 12 bits read + inc
+ set_busy_flag(11.5);
+ m_registers[1] = m_videoram->read_byte(a);
+ switch((a / 0x0800) & 1)
+ {
+ case 0:
+ m_registers[3] = m_videoram->read_byte(a + 0x1000);
+ break;
+ case 1:
+ m_registers[3] = m_videoram->read_byte(a + 0x0800);
+ break;
+ }
+ if (cmd&1)
+ {
+ if ((m_registers[7] & 0x80) == 0x00)
+ {
+ m_registers[7] |= 0x80;
+ break;
+ }
+ m_registers[7] &= 0x80;
+ inc_x(7);
+ }
+ break;
+ case 0x80: //IND: R1->ROM (impossible ?)
+ break;
+ case 0x81: //IND: R1->TGS
+ case 0x82: //IND: R1->MAT
+ case 0x83: //IND: R1->PAT
+ case 0x84: //IND: R1->DOR
+ case 0x87: //IND: R1->ROR
+ set_busy_flag(2);
+ switch(cmd&7)
+ {
+ case 1: m_tgs = m_registers[1]; break;
+ case 2: m_mat = m_registers[1]; break;
+ case 3: m_pat = m_registers[1]; break;
+ case 4: m_dor = m_registers[1]; break;
+ case 7: m_ror = m_registers[1]; break;
+ }
+ set_video_mode();
+ m_state &= 0x8f; //reset S4(LXa), S5(LXm), S6(Al)
+ break;
+ case 0x88: //IND: ROM->R1
+ case 0x89: //IND: TGS->R1
+ case 0x8a: //IND: MAT->R1
+ case 0x8b: //IND: PAT->R1
+ case 0x8c: //IND: DOR->R1
+ case 0x8f: //IND: ROR->R1
+ set_busy_flag(3.5);
+ switch(cmd&7)
+ {
+ case 0: m_registers[1] = m_charset->u8(indexrom(7) & 0x1fff);
+ case 1: m_registers[1] = m_tgs; break;
+ case 2: m_registers[1] = m_mat; break;
+ case 3: m_registers[1] = m_pat; break;
+ case 4: m_registers[1] = m_dor; break;
+ case 7: m_registers[1] = m_ror; break;
+ }
+ m_state &= 0x8f; //reset S4(LXa), S5(LXm), S6(Al)
+ break;
+ case 0x90: //NOP: no operation
+ case 0x91: //NOP: no operation
+ case 0x95: //VRM: vertical sync mask reset
+ case 0x99: //VSM: vertical sync mask set
+ break;
+ case 0xb0: //INY: increment Y
+ set_busy_flag(2);
+ inc_y(6);
+ m_state &= 0x8f; //reset S4(LXa), S5(LXm), S6(Al)
+ break;
+ case 0xd5: //MVB: move buffer MP->AP stop
+ case 0xd6: //MVB: move buffer MP->AP nostop
+ case 0xd9: //MVB: move buffer AP->MP stop
+ case 0xda: //MVB: move buffer AP->MP nostop
+ case 0xe5: //MVD: move double buffer MP->AP stop
+ case 0xe6: //MVD: move double buffer MP->AP nostop
+ case 0xe9: //MVD: move double buffer AP->MP stop
+ case 0xea: //MVD: move double buffer AP->MP nostop
+ case 0xf5: //MVT: move triple buffer MP->AP stop
+ case 0xf6: //MVT: move triple buffer MP->AP nostop
+ case 0xf9: //MVT: move triple buffer AP->MP stop
+ case 0xfa: //MVT: move triple buffer AP->MP nostop
+ {
+ UINT16 i, a1, a2;
+ UINT8 n = (cmd>>4) - 0x0c;
+ UINT8 r1 = (cmd&0x04) ? 7 : 5;
+ UINT8 r2 = (cmd&0x04) ? 5 : 7;
+ int busy = 2;
+
+ for(i = 0; i < 1280; i++)
+ {
+ a1 = indexram(r1); a2 = indexram(r2);
+ m_videoram->write_byte(a2, m_videoram->read_byte(a1));
+
+ if (n > 1) m_videoram->write_byte(a2 + 0x0800, m_videoram->read_byte(a1 + 0x0800));
+ if (n > 2) m_videoram->write_byte(a2 + 0x1000, m_videoram->read_byte(a1 + 0x1000));
+
+ inc_x(r1);
+ inc_x(r2);
+ if ((m_registers[5] & 0x3f) == 0 && (cmd&1))
+ break;
+
+ if ((m_registers[7] & 0x3f) == 0)
+ {
+ if (cmd&1)
+ break;
+ else
+ inc_y(6);
+ }
+
+ busy += 4 * n;
+ }
+ m_state &= 0x8f; //reset S4(LXa), S5(LXm), S6(Al)
+ set_busy_flag(busy);
+ }
+ break;
+ case 0x05: //CLF: Clear page 24 bits
+ case 0x07: //CLG: Clear page 16 bits
+ case 0x40: //KRC: R1 -> ram
+ case 0x41: //KRC: R1 -> ram + inc
+ case 0x48: //KRC: 80 characters - 8 bits
+ case 0x49: //KRC: 80 characters - 8 bits
+ default:
+ logerror("Unemulated EF9345 cmd: %02x\n", cmd);
+ }
+}
+
+
+/**************************************************************
+ EF9345 interface
+**************************************************************/
+
+UINT32 ef9345_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_screen_out, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+void ef9345_device::update_scanline(UINT16 scanline)
+{
+ UINT16 i;
+
+ if (scanline == 250)
+ m_state &= 0xfb;
+
+ set_busy_flag(104);
+
+ if (m_char_mode == MODE12x80 || m_char_mode == MODE8x80)
+ {
+ draw_char_80(m_border, 0, (scanline / 10) + 1);
+ draw_char_80(m_border, 81, (scanline / 10) + 1);
+ }
+ else
+ {
+ draw_char_40(m_border, 0, (scanline / 10) + 1);
+ draw_char_40(m_border, 41, (scanline / 10) + 1);
+ }
+
+ if (scanline == 0)
+ {
+ m_state |= 0x04;
+ draw_border(0);
+ if (m_pat & 1)
+ for(i = 0; i < 40; i++)
+ makechar(i, (scanline / 10));
+ else
+ for(i = 0; i < 42; i++)
+ draw_char_40(m_border, i, 1);
+ }
+ else if (scanline < 120)
+ {
+ if (m_pat & 2)
+ for(i = 0; i < 40; i++)
+ makechar(i, (scanline / 10));
+ else
+ draw_border(scanline / 10);
+ }
+ else if (scanline < 250)
+ {
+ if (m_pat & 4)
+ for(i = 0; i < 40; i++)
+ makechar(i, (scanline / 10));
+ else
+ draw_border(scanline / 10);
+
+ if (scanline == 240)
+ draw_border(26);
+ }
+}
+
+READ8_MEMBER( ef9345_device::data_r )
+{
+ if (offset & 7)
+ return m_registers[offset & 7];
+
+ if (m_bf)
+ m_state |= 0x80;
+ else
+ m_state &= 0x7f;
+
+ return m_state;
+}
+
+WRITE8_MEMBER( ef9345_device::data_w )
+{
+ m_registers[offset & 7] = data;
+
+ if (offset & 8)
+ ef9345_exec(m_registers[0] & 0xff);
+}
diff --git a/src/devices/video/ef9345.h b/src/devices/video/ef9345.h
new file mode 100644
index 00000000000..332b037a223
--- /dev/null
+++ b/src/devices/video/ef9345.h
@@ -0,0 +1,118 @@
+// license:GPL-2.0+
+// copyright-holders:Daniel Coulom,Sandro Ronco
+/*********************************************************************
+
+ ef9345.h
+
+ Thomson EF9345 video controller
+
+*********************************************************************/
+
+
+#pragma once
+
+#ifndef __EF9345_H__
+#define __EF9345_H__
+
+
+#define MCFG_EF9345_PALETTE(_palette_tag) \
+ ef9345_device::static_set_palette_tag(*device, "^" _palette_tag);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> ef9345_device
+
+class ef9345_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ ef9345_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration
+ static void static_set_palette_tag(device_t &device, const char *tag);
+
+ // device interface
+ DECLARE_READ8_MEMBER( data_r );
+ DECLARE_WRITE8_MEMBER( data_w );
+ void update_scanline(UINT16 scanline);
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_config_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ // address space configurations
+ const address_space_config m_space_config;
+
+ // inline helper
+ inline UINT16 indexram(UINT8 r);
+ inline UINT16 indexrom(UINT8 r);
+ inline void inc_x(UINT8 r);
+ inline void inc_y(UINT8 r);
+
+private:
+
+ void set_busy_flag(int period);
+ void draw_char_40(UINT8 *c, UINT16 x, UINT16 y);
+ void draw_char_80(UINT8 *c, UINT16 x, UINT16 y);
+ void set_video_mode(void);
+ void init_accented_chars(void);
+ UINT8 read_char(UINT8 index, UINT16 addr);
+ UINT8 get_dial(UINT8 x, UINT8 attrib);
+ void zoom(UINT8 *pix, UINT16 n);
+ UINT16 indexblock(UINT16 x, UINT16 y);
+ void bichrome40(UINT8 type, UINT16 address, UINT8 dial, UINT16 iblock, UINT16 x, UINT16 y, UINT8 c0, UINT8 c1, UINT8 insert, UINT8 flash, UINT8 hided, UINT8 negative, UINT8 underline);
+ void quadrichrome40(UINT8 c, UINT8 b, UINT8 a, UINT16 x, UINT16 y);
+ void bichrome80(UINT8 c, UINT8 a, UINT16 x, UINT16 y);
+ void makechar(UINT16 x, UINT16 y);
+ void draw_border(UINT16 line);
+ void makechar_16x40(UINT16 x, UINT16 y);
+ void makechar_24x40(UINT16 x, UINT16 y);
+ void makechar_12x80(UINT16 x, UINT16 y);
+ void ef9345_exec(UINT8 cmd);
+
+ // internal state
+ static const device_timer_id BUSY_TIMER = 0;
+ static const device_timer_id BLINKING_TIMER = 1;
+
+ memory_region *m_charset;
+ address_space *m_videoram;
+
+ UINT8 m_bf; //busy flag
+ UINT8 m_char_mode; //40 or 80 chars for line
+ UINT8 m_acc_char[0x2000]; //accented chars
+ UINT8 m_registers[8]; //registers R0-R7
+ UINT8 m_state; //status register
+ UINT8 m_tgs,m_mat,m_pat,m_dor,m_ror; //indirect registers
+ UINT8 m_border[80]; //border color
+ UINT16 m_block; //current memory block
+ UINT16 m_ram_base[4]; //index of ram charset
+ UINT8 m_blink; //cursor status
+ UINT8 m_last_dial[40]; //last chars dial (for determinate the zoom position)
+ UINT8 m_latchc0; //background color latch
+ UINT8 m_latchm; //hided atribute latch
+ UINT8 m_latchi; //insert atribute latch
+ UINT8 m_latchu; //underline atribute latch
+
+ bitmap_rgb32 m_screen_out;
+
+ // timers
+ emu_timer *m_busy_timer;
+ emu_timer *m_blink_timer;
+
+ required_device<palette_device> m_palette;
+};
+
+// device type definition
+extern const device_type EF9345;
+
+#endif
diff --git a/src/devices/video/epic12.c b/src/devices/video/epic12.c
new file mode 100644
index 00000000000..a22100e5543
--- /dev/null
+++ b/src/devices/video/epic12.c
@@ -0,0 +1,949 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood, Luca Elia, MetalliC
+/* emulation of Altera Cyclone EPIC12 FPGA programmed as a blitter */
+
+#include "emu.h"
+#include "epic12.h"
+
+
+
+const device_type EPIC12 = &device_creator<epic12_device>;
+
+epic12_device::epic12_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, EPIC12, "EP1C12 Blitter", tag, owner, clock, "epic12", __FILE__),
+ device_video_interface(mconfig, *this)
+{
+ m_is_unsafe = 0;
+ m_delay_scale = 0;
+ m_blitter_request = 0;
+ m_blitter_delay_timer = 0;
+ m_blitter_busy = 0;
+ m_gfx_addr = 0;
+ m_gfx_scroll_0_x = 0;
+ m_gfx_scroll_0_y = 0;
+ m_gfx_scroll_1_x = 0;
+ m_gfx_scroll_1_y = 0;
+ m_gfx_addr_shadowcopy = 0;
+ m_gfx_scroll_0_x_shadowcopy = 0;
+ m_gfx_scroll_0_y_shadowcopy = 0;
+ m_gfx_scroll_1_x_shadowcopy = 0;
+ m_gfx_scroll_1_y_shadowcopy = 0;
+ epic12_device_blit_delay = 0;
+}
+
+TIMER_CALLBACK_MEMBER( epic12_device::blitter_delay_callback )
+{
+ m_blitter_busy = 0;
+}
+
+
+void epic12_device::device_start()
+{
+ m_gfx_size = 0x2000 * 0x1000;
+ m_bitmaps = auto_bitmap_rgb32_alloc(machine(), 0x2000, 0x1000);
+ m_clip = m_bitmaps->cliprect();
+ m_clip.set(0, 0x2000-1, 0, 0x1000-1);
+
+ m_ram16_copy = auto_alloc_array(machine(), UINT16, m_main_ramsize/2);
+
+ m_blitter_delay_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(epic12_device::blitter_delay_callback),this));
+ m_blitter_delay_timer->adjust(attotime::never);
+}
+
+void epic12_device::device_reset()
+{
+ if (m_is_unsafe)
+ {
+ m_use_ram = m_ram16;
+ m_work_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_HIGH_FREQ|WORK_QUEUE_FLAG_MULTI);
+ }
+ else
+ {
+ m_use_ram = m_ram16_copy; // slow mode
+ m_work_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_HIGH_FREQ);
+ }
+
+ // cache table to avoid divides in blit code, also pre-clamped
+ int x,y;
+ for (y=0;y<0x40;y++)
+ {
+ for (x=0;x<0x20;x++)
+ {
+ epic12_device_colrtable[x][y] = (x*y) / 0x1f;
+ if (epic12_device_colrtable[x][y]>0x1f) epic12_device_colrtable[x][y] = 0x1f;
+
+ epic12_device_colrtable_rev[x^0x1f][y] = (x*y) / 0x1f;
+ if (epic12_device_colrtable_rev[x^0x1f][y]>0x1f) epic12_device_colrtable_rev[x^0x1f][y] = 0x1f;
+ }
+ }
+
+ // preclamped add table
+ for (y=0;y<0x20;y++)
+ {
+ for (x=0;x<0x20;x++)
+ {
+ epic12_device_colrtable_add[x][y] = (x+y);
+ if (epic12_device_colrtable_add[x][y]>0x1f) epic12_device_colrtable_add[x][y] = 0x1f;
+ }
+ }
+
+ m_blitter_busy = 0;
+}
+
+// todo, get these into the device class without ruining performance
+UINT8 epic12_device_colrtable[0x20][0x40];
+UINT8 epic12_device_colrtable_rev[0x20][0x40];
+UINT8 epic12_device_colrtable_add[0x20][0x20];
+UINT64 epic12_device_blit_delay;
+
+inline UINT16 epic12_device::READ_NEXT_WORD(offs_t *addr)
+{
+// UINT16 data = space.read_word(*addr); // going through the memory system is 'more correct' but noticeably slower
+ UINT16 data = m_use_ram[((*addr & m_main_rammask) >> 1) ^ NATIVE_ENDIAN_VALUE_LE_BE(3, 0)];
+
+ *addr += 2;
+
+// printf("data %04x\n", data);
+ return data;
+}
+
+inline UINT16 epic12_device::COPY_NEXT_WORD(address_space &space, offs_t *addr)
+{
+// UINT16 data = space.read_word(*addr); // going through the memory system is 'more correct' but noticeably slower
+ UINT16 data = m_ram16[((*addr & m_main_rammask) >> 1) ^ NATIVE_ENDIAN_VALUE_LE_BE(3, 0)];
+ m_ram16_copy[((*addr & m_main_rammask) >> 1) ^ NATIVE_ENDIAN_VALUE_LE_BE(3, 0)] = data;
+
+ *addr += 2;
+
+// printf("data %04x\n", data);
+ return data;
+}
+
+
+inline void epic12_device::gfx_upload_shadow_copy(address_space &space, offs_t *addr)
+{
+ UINT32 x,y, dimx,dimy;
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+
+ dimx = (COPY_NEXT_WORD(space, addr) & 0x1fff) + 1;
+ dimy = (COPY_NEXT_WORD(space, addr) & 0x0fff) + 1;
+
+ for (y = 0; y < dimy; y++)
+ {
+ for (x = 0; x < dimx; x++)
+ {
+ COPY_NEXT_WORD(space, addr);
+ }
+ }
+}
+
+inline void epic12_device::gfx_upload(offs_t *addr)
+{
+ UINT32 x,y, dst_p,dst_x_start,dst_y_start, dimx,dimy;
+ UINT32 *dst;
+
+ // 0x20000000
+ READ_NEXT_WORD(addr);
+ READ_NEXT_WORD(addr);
+
+ // 0x99999999
+ READ_NEXT_WORD(addr);
+ READ_NEXT_WORD(addr);
+
+ dst_x_start = READ_NEXT_WORD(addr);
+ dst_y_start = READ_NEXT_WORD(addr);
+
+ dst_p = 0;
+ dst_x_start &= 0x1fff;
+ dst_y_start &= 0x0fff;
+
+ dimx = (READ_NEXT_WORD(addr) & 0x1fff) + 1;
+ dimy = (READ_NEXT_WORD(addr) & 0x0fff) + 1;
+
+ logerror("GFX COPY: DST %02X,%02X,%03X DIM %02X,%03X\n", dst_p,dst_x_start,dst_y_start, dimx,dimy);
+
+ for (y = 0; y < dimy; y++)
+ {
+ dst = &m_bitmaps->pix(dst_y_start + y, 0);
+ dst += dst_x_start;
+
+ for (x = 0; x < dimx; x++)
+ {
+ UINT16 pendat = READ_NEXT_WORD(addr);
+ // real hw would upload the gfxword directly, but our VRAM is 32-bit, so convert it.
+ //dst[dst_x_start + x] = pendat;
+ *dst++ = ((pendat&0x8000)<<14) | ((pendat&0x7c00)<<9) | ((pendat&0x03e0)<<6) | ((pendat&0x001f)<<3); // --t- ---- rrrr r--- gggg g--- bbbb b--- format
+ //dst[dst_x_start + x] = ((pendat&0x8000)<<14) | ((pendat&0x7c00)<<6) | ((pendat&0x03e0)<<3) | ((pendat&0x001f)<<0); // --t- ---- ---r rrrr ---g gggg ---b bbbb format
+
+
+ }
+ }
+}
+
+#define draw_params m_bitmaps, &m_clip, &m_bitmaps->pix(0,0),src_x,src_y, x,y, dimx,dimy, flipy, s_alpha, d_alpha, &tint_clr
+
+
+
+epic12_device_blitfunction epic12_device_f0_ti1_tr1_blit_funcs[] =
+{
+ epic12_device::draw_sprite_f0_ti1_tr1_s0_d0, epic12_device::draw_sprite_f0_ti1_tr1_s1_d0, epic12_device::draw_sprite_f0_ti1_tr1_s2_d0, epic12_device::draw_sprite_f0_ti1_tr1_s3_d0, epic12_device::draw_sprite_f0_ti1_tr1_s4_d0, epic12_device::draw_sprite_f0_ti1_tr1_s5_d0, epic12_device::draw_sprite_f0_ti1_tr1_s6_d0, epic12_device::draw_sprite_f0_ti1_tr1_s7_d0,
+ epic12_device::draw_sprite_f0_ti1_tr1_s0_d1, epic12_device::draw_sprite_f0_ti1_tr1_s1_d1, epic12_device::draw_sprite_f0_ti1_tr1_s2_d1, epic12_device::draw_sprite_f0_ti1_tr1_s3_d1, epic12_device::draw_sprite_f0_ti1_tr1_s4_d1, epic12_device::draw_sprite_f0_ti1_tr1_s5_d1, epic12_device::draw_sprite_f0_ti1_tr1_s6_d1, epic12_device::draw_sprite_f0_ti1_tr1_s7_d1,
+ epic12_device::draw_sprite_f0_ti1_tr1_s0_d2, epic12_device::draw_sprite_f0_ti1_tr1_s1_d2, epic12_device::draw_sprite_f0_ti1_tr1_s2_d2, epic12_device::draw_sprite_f0_ti1_tr1_s3_d2, epic12_device::draw_sprite_f0_ti1_tr1_s4_d2, epic12_device::draw_sprite_f0_ti1_tr1_s5_d2, epic12_device::draw_sprite_f0_ti1_tr1_s6_d2, epic12_device::draw_sprite_f0_ti1_tr1_s7_d2,
+ epic12_device::draw_sprite_f0_ti1_tr1_s0_d3, epic12_device::draw_sprite_f0_ti1_tr1_s1_d3, epic12_device::draw_sprite_f0_ti1_tr1_s2_d3, epic12_device::draw_sprite_f0_ti1_tr1_s3_d3, epic12_device::draw_sprite_f0_ti1_tr1_s4_d3, epic12_device::draw_sprite_f0_ti1_tr1_s5_d3, epic12_device::draw_sprite_f0_ti1_tr1_s6_d3, epic12_device::draw_sprite_f0_ti1_tr1_s7_d3,
+ epic12_device::draw_sprite_f0_ti1_tr1_s0_d4, epic12_device::draw_sprite_f0_ti1_tr1_s1_d4, epic12_device::draw_sprite_f0_ti1_tr1_s2_d4, epic12_device::draw_sprite_f0_ti1_tr1_s3_d4, epic12_device::draw_sprite_f0_ti1_tr1_s4_d4, epic12_device::draw_sprite_f0_ti1_tr1_s5_d4, epic12_device::draw_sprite_f0_ti1_tr1_s6_d4, epic12_device::draw_sprite_f0_ti1_tr1_s7_d4,
+ epic12_device::draw_sprite_f0_ti1_tr1_s0_d5, epic12_device::draw_sprite_f0_ti1_tr1_s1_d5, epic12_device::draw_sprite_f0_ti1_tr1_s2_d5, epic12_device::draw_sprite_f0_ti1_tr1_s3_d5, epic12_device::draw_sprite_f0_ti1_tr1_s4_d5, epic12_device::draw_sprite_f0_ti1_tr1_s5_d5, epic12_device::draw_sprite_f0_ti1_tr1_s6_d5, epic12_device::draw_sprite_f0_ti1_tr1_s7_d5,
+ epic12_device::draw_sprite_f0_ti1_tr1_s0_d6, epic12_device::draw_sprite_f0_ti1_tr1_s1_d6, epic12_device::draw_sprite_f0_ti1_tr1_s2_d6, epic12_device::draw_sprite_f0_ti1_tr1_s3_d6, epic12_device::draw_sprite_f0_ti1_tr1_s4_d6, epic12_device::draw_sprite_f0_ti1_tr1_s5_d6, epic12_device::draw_sprite_f0_ti1_tr1_s6_d6, epic12_device::draw_sprite_f0_ti1_tr1_s7_d6,
+ epic12_device::draw_sprite_f0_ti1_tr1_s0_d7, epic12_device::draw_sprite_f0_ti1_tr1_s1_d7, epic12_device::draw_sprite_f0_ti1_tr1_s2_d7, epic12_device::draw_sprite_f0_ti1_tr1_s3_d7, epic12_device::draw_sprite_f0_ti1_tr1_s4_d7, epic12_device::draw_sprite_f0_ti1_tr1_s5_d7, epic12_device::draw_sprite_f0_ti1_tr1_s6_d7, epic12_device::draw_sprite_f0_ti1_tr1_s7_d7,
+};
+
+epic12_device_blitfunction epic12_device_f0_ti1_tr0_blit_funcs[] =
+{
+ epic12_device::draw_sprite_f0_ti1_tr0_s0_d0, epic12_device::draw_sprite_f0_ti1_tr0_s1_d0, epic12_device::draw_sprite_f0_ti1_tr0_s2_d0, epic12_device::draw_sprite_f0_ti1_tr0_s3_d0, epic12_device::draw_sprite_f0_ti1_tr0_s4_d0, epic12_device::draw_sprite_f0_ti1_tr0_s5_d0, epic12_device::draw_sprite_f0_ti1_tr0_s6_d0, epic12_device::draw_sprite_f0_ti1_tr0_s7_d0,
+ epic12_device::draw_sprite_f0_ti1_tr0_s0_d1, epic12_device::draw_sprite_f0_ti1_tr0_s1_d1, epic12_device::draw_sprite_f0_ti1_tr0_s2_d1, epic12_device::draw_sprite_f0_ti1_tr0_s3_d1, epic12_device::draw_sprite_f0_ti1_tr0_s4_d1, epic12_device::draw_sprite_f0_ti1_tr0_s5_d1, epic12_device::draw_sprite_f0_ti1_tr0_s6_d1, epic12_device::draw_sprite_f0_ti1_tr0_s7_d1,
+ epic12_device::draw_sprite_f0_ti1_tr0_s0_d2, epic12_device::draw_sprite_f0_ti1_tr0_s1_d2, epic12_device::draw_sprite_f0_ti1_tr0_s2_d2, epic12_device::draw_sprite_f0_ti1_tr0_s3_d2, epic12_device::draw_sprite_f0_ti1_tr0_s4_d2, epic12_device::draw_sprite_f0_ti1_tr0_s5_d2, epic12_device::draw_sprite_f0_ti1_tr0_s6_d2, epic12_device::draw_sprite_f0_ti1_tr0_s7_d2,
+ epic12_device::draw_sprite_f0_ti1_tr0_s0_d3, epic12_device::draw_sprite_f0_ti1_tr0_s1_d3, epic12_device::draw_sprite_f0_ti1_tr0_s2_d3, epic12_device::draw_sprite_f0_ti1_tr0_s3_d3, epic12_device::draw_sprite_f0_ti1_tr0_s4_d3, epic12_device::draw_sprite_f0_ti1_tr0_s5_d3, epic12_device::draw_sprite_f0_ti1_tr0_s6_d3, epic12_device::draw_sprite_f0_ti1_tr0_s7_d3,
+ epic12_device::draw_sprite_f0_ti1_tr0_s0_d4, epic12_device::draw_sprite_f0_ti1_tr0_s1_d4, epic12_device::draw_sprite_f0_ti1_tr0_s2_d4, epic12_device::draw_sprite_f0_ti1_tr0_s3_d4, epic12_device::draw_sprite_f0_ti1_tr0_s4_d4, epic12_device::draw_sprite_f0_ti1_tr0_s5_d4, epic12_device::draw_sprite_f0_ti1_tr0_s6_d4, epic12_device::draw_sprite_f0_ti1_tr0_s7_d4,
+ epic12_device::draw_sprite_f0_ti1_tr0_s0_d5, epic12_device::draw_sprite_f0_ti1_tr0_s1_d5, epic12_device::draw_sprite_f0_ti1_tr0_s2_d5, epic12_device::draw_sprite_f0_ti1_tr0_s3_d5, epic12_device::draw_sprite_f0_ti1_tr0_s4_d5, epic12_device::draw_sprite_f0_ti1_tr0_s5_d5, epic12_device::draw_sprite_f0_ti1_tr0_s6_d5, epic12_device::draw_sprite_f0_ti1_tr0_s7_d5,
+ epic12_device::draw_sprite_f0_ti1_tr0_s0_d6, epic12_device::draw_sprite_f0_ti1_tr0_s1_d6, epic12_device::draw_sprite_f0_ti1_tr0_s2_d6, epic12_device::draw_sprite_f0_ti1_tr0_s3_d6, epic12_device::draw_sprite_f0_ti1_tr0_s4_d6, epic12_device::draw_sprite_f0_ti1_tr0_s5_d6, epic12_device::draw_sprite_f0_ti1_tr0_s6_d6, epic12_device::draw_sprite_f0_ti1_tr0_s7_d6,
+ epic12_device::draw_sprite_f0_ti1_tr0_s0_d7, epic12_device::draw_sprite_f0_ti1_tr0_s1_d7, epic12_device::draw_sprite_f0_ti1_tr0_s2_d7, epic12_device::draw_sprite_f0_ti1_tr0_s3_d7, epic12_device::draw_sprite_f0_ti1_tr0_s4_d7, epic12_device::draw_sprite_f0_ti1_tr0_s5_d7, epic12_device::draw_sprite_f0_ti1_tr0_s6_d7, epic12_device::draw_sprite_f0_ti1_tr0_s7_d7,
+};
+
+epic12_device_blitfunction epic12_device_f1_ti1_tr1_blit_funcs[] =
+{
+ epic12_device::draw_sprite_f1_ti1_tr1_s0_d0, epic12_device::draw_sprite_f1_ti1_tr1_s1_d0, epic12_device::draw_sprite_f1_ti1_tr1_s2_d0, epic12_device::draw_sprite_f1_ti1_tr1_s3_d0, epic12_device::draw_sprite_f1_ti1_tr1_s4_d0, epic12_device::draw_sprite_f1_ti1_tr1_s5_d0, epic12_device::draw_sprite_f1_ti1_tr1_s6_d0, epic12_device::draw_sprite_f1_ti1_tr1_s7_d0,
+ epic12_device::draw_sprite_f1_ti1_tr1_s0_d1, epic12_device::draw_sprite_f1_ti1_tr1_s1_d1, epic12_device::draw_sprite_f1_ti1_tr1_s2_d1, epic12_device::draw_sprite_f1_ti1_tr1_s3_d1, epic12_device::draw_sprite_f1_ti1_tr1_s4_d1, epic12_device::draw_sprite_f1_ti1_tr1_s5_d1, epic12_device::draw_sprite_f1_ti1_tr1_s6_d1, epic12_device::draw_sprite_f1_ti1_tr1_s7_d1,
+ epic12_device::draw_sprite_f1_ti1_tr1_s0_d2, epic12_device::draw_sprite_f1_ti1_tr1_s1_d2, epic12_device::draw_sprite_f1_ti1_tr1_s2_d2, epic12_device::draw_sprite_f1_ti1_tr1_s3_d2, epic12_device::draw_sprite_f1_ti1_tr1_s4_d2, epic12_device::draw_sprite_f1_ti1_tr1_s5_d2, epic12_device::draw_sprite_f1_ti1_tr1_s6_d2, epic12_device::draw_sprite_f1_ti1_tr1_s7_d2,
+ epic12_device::draw_sprite_f1_ti1_tr1_s0_d3, epic12_device::draw_sprite_f1_ti1_tr1_s1_d3, epic12_device::draw_sprite_f1_ti1_tr1_s2_d3, epic12_device::draw_sprite_f1_ti1_tr1_s3_d3, epic12_device::draw_sprite_f1_ti1_tr1_s4_d3, epic12_device::draw_sprite_f1_ti1_tr1_s5_d3, epic12_device::draw_sprite_f1_ti1_tr1_s6_d3, epic12_device::draw_sprite_f1_ti1_tr1_s7_d3,
+ epic12_device::draw_sprite_f1_ti1_tr1_s0_d4, epic12_device::draw_sprite_f1_ti1_tr1_s1_d4, epic12_device::draw_sprite_f1_ti1_tr1_s2_d4, epic12_device::draw_sprite_f1_ti1_tr1_s3_d4, epic12_device::draw_sprite_f1_ti1_tr1_s4_d4, epic12_device::draw_sprite_f1_ti1_tr1_s5_d4, epic12_device::draw_sprite_f1_ti1_tr1_s6_d4, epic12_device::draw_sprite_f1_ti1_tr1_s7_d4,
+ epic12_device::draw_sprite_f1_ti1_tr1_s0_d5, epic12_device::draw_sprite_f1_ti1_tr1_s1_d5, epic12_device::draw_sprite_f1_ti1_tr1_s2_d5, epic12_device::draw_sprite_f1_ti1_tr1_s3_d5, epic12_device::draw_sprite_f1_ti1_tr1_s4_d5, epic12_device::draw_sprite_f1_ti1_tr1_s5_d5, epic12_device::draw_sprite_f1_ti1_tr1_s6_d5, epic12_device::draw_sprite_f1_ti1_tr1_s7_d5,
+ epic12_device::draw_sprite_f1_ti1_tr1_s0_d6, epic12_device::draw_sprite_f1_ti1_tr1_s1_d6, epic12_device::draw_sprite_f1_ti1_tr1_s2_d6, epic12_device::draw_sprite_f1_ti1_tr1_s3_d6, epic12_device::draw_sprite_f1_ti1_tr1_s4_d6, epic12_device::draw_sprite_f1_ti1_tr1_s5_d6, epic12_device::draw_sprite_f1_ti1_tr1_s6_d6, epic12_device::draw_sprite_f1_ti1_tr1_s7_d6,
+ epic12_device::draw_sprite_f1_ti1_tr1_s0_d7, epic12_device::draw_sprite_f1_ti1_tr1_s1_d7, epic12_device::draw_sprite_f1_ti1_tr1_s2_d7, epic12_device::draw_sprite_f1_ti1_tr1_s3_d7, epic12_device::draw_sprite_f1_ti1_tr1_s4_d7, epic12_device::draw_sprite_f1_ti1_tr1_s5_d7, epic12_device::draw_sprite_f1_ti1_tr1_s6_d7, epic12_device::draw_sprite_f1_ti1_tr1_s7_d7,
+};
+
+epic12_device_blitfunction epic12_device_f1_ti1_tr0_blit_funcs[] =
+{
+ epic12_device::draw_sprite_f1_ti1_tr0_s0_d0, epic12_device::draw_sprite_f1_ti1_tr0_s1_d0, epic12_device::draw_sprite_f1_ti1_tr0_s2_d0, epic12_device::draw_sprite_f1_ti1_tr0_s3_d0, epic12_device::draw_sprite_f1_ti1_tr0_s4_d0, epic12_device::draw_sprite_f1_ti1_tr0_s5_d0, epic12_device::draw_sprite_f1_ti1_tr0_s6_d0, epic12_device::draw_sprite_f1_ti1_tr0_s7_d0,
+ epic12_device::draw_sprite_f1_ti1_tr0_s0_d1, epic12_device::draw_sprite_f1_ti1_tr0_s1_d1, epic12_device::draw_sprite_f1_ti1_tr0_s2_d1, epic12_device::draw_sprite_f1_ti1_tr0_s3_d1, epic12_device::draw_sprite_f1_ti1_tr0_s4_d1, epic12_device::draw_sprite_f1_ti1_tr0_s5_d1, epic12_device::draw_sprite_f1_ti1_tr0_s6_d1, epic12_device::draw_sprite_f1_ti1_tr0_s7_d1,
+ epic12_device::draw_sprite_f1_ti1_tr0_s0_d2, epic12_device::draw_sprite_f1_ti1_tr0_s1_d2, epic12_device::draw_sprite_f1_ti1_tr0_s2_d2, epic12_device::draw_sprite_f1_ti1_tr0_s3_d2, epic12_device::draw_sprite_f1_ti1_tr0_s4_d2, epic12_device::draw_sprite_f1_ti1_tr0_s5_d2, epic12_device::draw_sprite_f1_ti1_tr0_s6_d2, epic12_device::draw_sprite_f1_ti1_tr0_s7_d2,
+ epic12_device::draw_sprite_f1_ti1_tr0_s0_d3, epic12_device::draw_sprite_f1_ti1_tr0_s1_d3, epic12_device::draw_sprite_f1_ti1_tr0_s2_d3, epic12_device::draw_sprite_f1_ti1_tr0_s3_d3, epic12_device::draw_sprite_f1_ti1_tr0_s4_d3, epic12_device::draw_sprite_f1_ti1_tr0_s5_d3, epic12_device::draw_sprite_f1_ti1_tr0_s6_d3, epic12_device::draw_sprite_f1_ti1_tr0_s7_d3,
+ epic12_device::draw_sprite_f1_ti1_tr0_s0_d4, epic12_device::draw_sprite_f1_ti1_tr0_s1_d4, epic12_device::draw_sprite_f1_ti1_tr0_s2_d4, epic12_device::draw_sprite_f1_ti1_tr0_s3_d4, epic12_device::draw_sprite_f1_ti1_tr0_s4_d4, epic12_device::draw_sprite_f1_ti1_tr0_s5_d4, epic12_device::draw_sprite_f1_ti1_tr0_s6_d4, epic12_device::draw_sprite_f1_ti1_tr0_s7_d4,
+ epic12_device::draw_sprite_f1_ti1_tr0_s0_d5, epic12_device::draw_sprite_f1_ti1_tr0_s1_d5, epic12_device::draw_sprite_f1_ti1_tr0_s2_d5, epic12_device::draw_sprite_f1_ti1_tr0_s3_d5, epic12_device::draw_sprite_f1_ti1_tr0_s4_d5, epic12_device::draw_sprite_f1_ti1_tr0_s5_d5, epic12_device::draw_sprite_f1_ti1_tr0_s6_d5, epic12_device::draw_sprite_f1_ti1_tr0_s7_d5,
+ epic12_device::draw_sprite_f1_ti1_tr0_s0_d6, epic12_device::draw_sprite_f1_ti1_tr0_s1_d6, epic12_device::draw_sprite_f1_ti1_tr0_s2_d6, epic12_device::draw_sprite_f1_ti1_tr0_s3_d6, epic12_device::draw_sprite_f1_ti1_tr0_s4_d6, epic12_device::draw_sprite_f1_ti1_tr0_s5_d6, epic12_device::draw_sprite_f1_ti1_tr0_s6_d6, epic12_device::draw_sprite_f1_ti1_tr0_s7_d6,
+ epic12_device::draw_sprite_f1_ti1_tr0_s0_d7, epic12_device::draw_sprite_f1_ti1_tr0_s1_d7, epic12_device::draw_sprite_f1_ti1_tr0_s2_d7, epic12_device::draw_sprite_f1_ti1_tr0_s3_d7, epic12_device::draw_sprite_f1_ti1_tr0_s4_d7, epic12_device::draw_sprite_f1_ti1_tr0_s5_d7, epic12_device::draw_sprite_f1_ti1_tr0_s6_d7, epic12_device::draw_sprite_f1_ti1_tr0_s7_d7,
+};
+
+
+
+epic12_device_blitfunction epic12_device_f0_ti0_tr1_blit_funcs[] =
+{
+ epic12_device::draw_sprite_f0_ti0_tr1_s0_d0, epic12_device::draw_sprite_f0_ti0_tr1_s1_d0, epic12_device::draw_sprite_f0_ti0_tr1_s2_d0, epic12_device::draw_sprite_f0_ti0_tr1_s3_d0, epic12_device::draw_sprite_f0_ti0_tr1_s4_d0, epic12_device::draw_sprite_f0_ti0_tr1_s5_d0, epic12_device::draw_sprite_f0_ti0_tr1_s6_d0, epic12_device::draw_sprite_f0_ti0_tr1_s7_d0,
+ epic12_device::draw_sprite_f0_ti0_tr1_s0_d1, epic12_device::draw_sprite_f0_ti0_tr1_s1_d1, epic12_device::draw_sprite_f0_ti0_tr1_s2_d1, epic12_device::draw_sprite_f0_ti0_tr1_s3_d1, epic12_device::draw_sprite_f0_ti0_tr1_s4_d1, epic12_device::draw_sprite_f0_ti0_tr1_s5_d1, epic12_device::draw_sprite_f0_ti0_tr1_s6_d1, epic12_device::draw_sprite_f0_ti0_tr1_s7_d1,
+ epic12_device::draw_sprite_f0_ti0_tr1_s0_d2, epic12_device::draw_sprite_f0_ti0_tr1_s1_d2, epic12_device::draw_sprite_f0_ti0_tr1_s2_d2, epic12_device::draw_sprite_f0_ti0_tr1_s3_d2, epic12_device::draw_sprite_f0_ti0_tr1_s4_d2, epic12_device::draw_sprite_f0_ti0_tr1_s5_d2, epic12_device::draw_sprite_f0_ti0_tr1_s6_d2, epic12_device::draw_sprite_f0_ti0_tr1_s7_d2,
+ epic12_device::draw_sprite_f0_ti0_tr1_s0_d3, epic12_device::draw_sprite_f0_ti0_tr1_s1_d3, epic12_device::draw_sprite_f0_ti0_tr1_s2_d3, epic12_device::draw_sprite_f0_ti0_tr1_s3_d3, epic12_device::draw_sprite_f0_ti0_tr1_s4_d3, epic12_device::draw_sprite_f0_ti0_tr1_s5_d3, epic12_device::draw_sprite_f0_ti0_tr1_s6_d3, epic12_device::draw_sprite_f0_ti0_tr1_s7_d3,
+ epic12_device::draw_sprite_f0_ti0_tr1_s0_d4, epic12_device::draw_sprite_f0_ti0_tr1_s1_d4, epic12_device::draw_sprite_f0_ti0_tr1_s2_d4, epic12_device::draw_sprite_f0_ti0_tr1_s3_d4, epic12_device::draw_sprite_f0_ti0_tr1_s4_d4, epic12_device::draw_sprite_f0_ti0_tr1_s5_d4, epic12_device::draw_sprite_f0_ti0_tr1_s6_d4, epic12_device::draw_sprite_f0_ti0_tr1_s7_d4,
+ epic12_device::draw_sprite_f0_ti0_tr1_s0_d5, epic12_device::draw_sprite_f0_ti0_tr1_s1_d5, epic12_device::draw_sprite_f0_ti0_tr1_s2_d5, epic12_device::draw_sprite_f0_ti0_tr1_s3_d5, epic12_device::draw_sprite_f0_ti0_tr1_s4_d5, epic12_device::draw_sprite_f0_ti0_tr1_s5_d5, epic12_device::draw_sprite_f0_ti0_tr1_s6_d5, epic12_device::draw_sprite_f0_ti0_tr1_s7_d5,
+ epic12_device::draw_sprite_f0_ti0_tr1_s0_d6, epic12_device::draw_sprite_f0_ti0_tr1_s1_d6, epic12_device::draw_sprite_f0_ti0_tr1_s2_d6, epic12_device::draw_sprite_f0_ti0_tr1_s3_d6, epic12_device::draw_sprite_f0_ti0_tr1_s4_d6, epic12_device::draw_sprite_f0_ti0_tr1_s5_d6, epic12_device::draw_sprite_f0_ti0_tr1_s6_d6, epic12_device::draw_sprite_f0_ti0_tr1_s7_d6,
+ epic12_device::draw_sprite_f0_ti0_tr1_s0_d7, epic12_device::draw_sprite_f0_ti0_tr1_s1_d7, epic12_device::draw_sprite_f0_ti0_tr1_s2_d7, epic12_device::draw_sprite_f0_ti0_tr1_s3_d7, epic12_device::draw_sprite_f0_ti0_tr1_s4_d7, epic12_device::draw_sprite_f0_ti0_tr1_s5_d7, epic12_device::draw_sprite_f0_ti0_tr1_s6_d7, epic12_device::draw_sprite_f0_ti0_tr1_s7_d7,
+};
+
+epic12_device_blitfunction epic12_device_f0_ti0_tr0_blit_funcs[] =
+{
+ epic12_device::draw_sprite_f0_ti0_tr0_s0_d0, epic12_device::draw_sprite_f0_ti0_tr0_s1_d0, epic12_device::draw_sprite_f0_ti0_tr0_s2_d0, epic12_device::draw_sprite_f0_ti0_tr0_s3_d0, epic12_device::draw_sprite_f0_ti0_tr0_s4_d0, epic12_device::draw_sprite_f0_ti0_tr0_s5_d0, epic12_device::draw_sprite_f0_ti0_tr0_s6_d0, epic12_device::draw_sprite_f0_ti0_tr0_s7_d0,
+ epic12_device::draw_sprite_f0_ti0_tr0_s0_d1, epic12_device::draw_sprite_f0_ti0_tr0_s1_d1, epic12_device::draw_sprite_f0_ti0_tr0_s2_d1, epic12_device::draw_sprite_f0_ti0_tr0_s3_d1, epic12_device::draw_sprite_f0_ti0_tr0_s4_d1, epic12_device::draw_sprite_f0_ti0_tr0_s5_d1, epic12_device::draw_sprite_f0_ti0_tr0_s6_d1, epic12_device::draw_sprite_f0_ti0_tr0_s7_d1,
+ epic12_device::draw_sprite_f0_ti0_tr0_s0_d2, epic12_device::draw_sprite_f0_ti0_tr0_s1_d2, epic12_device::draw_sprite_f0_ti0_tr0_s2_d2, epic12_device::draw_sprite_f0_ti0_tr0_s3_d2, epic12_device::draw_sprite_f0_ti0_tr0_s4_d2, epic12_device::draw_sprite_f0_ti0_tr0_s5_d2, epic12_device::draw_sprite_f0_ti0_tr0_s6_d2, epic12_device::draw_sprite_f0_ti0_tr0_s7_d2,
+ epic12_device::draw_sprite_f0_ti0_tr0_s0_d3, epic12_device::draw_sprite_f0_ti0_tr0_s1_d3, epic12_device::draw_sprite_f0_ti0_tr0_s2_d3, epic12_device::draw_sprite_f0_ti0_tr0_s3_d3, epic12_device::draw_sprite_f0_ti0_tr0_s4_d3, epic12_device::draw_sprite_f0_ti0_tr0_s5_d3, epic12_device::draw_sprite_f0_ti0_tr0_s6_d3, epic12_device::draw_sprite_f0_ti0_tr0_s7_d3,
+ epic12_device::draw_sprite_f0_ti0_tr0_s0_d4, epic12_device::draw_sprite_f0_ti0_tr0_s1_d4, epic12_device::draw_sprite_f0_ti0_tr0_s2_d4, epic12_device::draw_sprite_f0_ti0_tr0_s3_d4, epic12_device::draw_sprite_f0_ti0_tr0_s4_d4, epic12_device::draw_sprite_f0_ti0_tr0_s5_d4, epic12_device::draw_sprite_f0_ti0_tr0_s6_d4, epic12_device::draw_sprite_f0_ti0_tr0_s7_d4,
+ epic12_device::draw_sprite_f0_ti0_tr0_s0_d5, epic12_device::draw_sprite_f0_ti0_tr0_s1_d5, epic12_device::draw_sprite_f0_ti0_tr0_s2_d5, epic12_device::draw_sprite_f0_ti0_tr0_s3_d5, epic12_device::draw_sprite_f0_ti0_tr0_s4_d5, epic12_device::draw_sprite_f0_ti0_tr0_s5_d5, epic12_device::draw_sprite_f0_ti0_tr0_s6_d5, epic12_device::draw_sprite_f0_ti0_tr0_s7_d5,
+ epic12_device::draw_sprite_f0_ti0_tr0_s0_d6, epic12_device::draw_sprite_f0_ti0_tr0_s1_d6, epic12_device::draw_sprite_f0_ti0_tr0_s2_d6, epic12_device::draw_sprite_f0_ti0_tr0_s3_d6, epic12_device::draw_sprite_f0_ti0_tr0_s4_d6, epic12_device::draw_sprite_f0_ti0_tr0_s5_d6, epic12_device::draw_sprite_f0_ti0_tr0_s6_d6, epic12_device::draw_sprite_f0_ti0_tr0_s7_d6,
+ epic12_device::draw_sprite_f0_ti0_tr0_s0_d7, epic12_device::draw_sprite_f0_ti0_tr0_s1_d7, epic12_device::draw_sprite_f0_ti0_tr0_s2_d7, epic12_device::draw_sprite_f0_ti0_tr0_s3_d7, epic12_device::draw_sprite_f0_ti0_tr0_s4_d7, epic12_device::draw_sprite_f0_ti0_tr0_s5_d7, epic12_device::draw_sprite_f0_ti0_tr0_s6_d7, epic12_device::draw_sprite_f0_ti0_tr0_s7_d7,
+};
+
+epic12_device_blitfunction epic12_device_f1_ti0_tr1_blit_funcs[] =
+{
+ epic12_device::draw_sprite_f1_ti0_tr1_s0_d0, epic12_device::draw_sprite_f1_ti0_tr1_s1_d0, epic12_device::draw_sprite_f1_ti0_tr1_s2_d0, epic12_device::draw_sprite_f1_ti0_tr1_s3_d0, epic12_device::draw_sprite_f1_ti0_tr1_s4_d0, epic12_device::draw_sprite_f1_ti0_tr1_s5_d0, epic12_device::draw_sprite_f1_ti0_tr1_s6_d0, epic12_device::draw_sprite_f1_ti0_tr1_s7_d0,
+ epic12_device::draw_sprite_f1_ti0_tr1_s0_d1, epic12_device::draw_sprite_f1_ti0_tr1_s1_d1, epic12_device::draw_sprite_f1_ti0_tr1_s2_d1, epic12_device::draw_sprite_f1_ti0_tr1_s3_d1, epic12_device::draw_sprite_f1_ti0_tr1_s4_d1, epic12_device::draw_sprite_f1_ti0_tr1_s5_d1, epic12_device::draw_sprite_f1_ti0_tr1_s6_d1, epic12_device::draw_sprite_f1_ti0_tr1_s7_d1,
+ epic12_device::draw_sprite_f1_ti0_tr1_s0_d2, epic12_device::draw_sprite_f1_ti0_tr1_s1_d2, epic12_device::draw_sprite_f1_ti0_tr1_s2_d2, epic12_device::draw_sprite_f1_ti0_tr1_s3_d2, epic12_device::draw_sprite_f1_ti0_tr1_s4_d2, epic12_device::draw_sprite_f1_ti0_tr1_s5_d2, epic12_device::draw_sprite_f1_ti0_tr1_s6_d2, epic12_device::draw_sprite_f1_ti0_tr1_s7_d2,
+ epic12_device::draw_sprite_f1_ti0_tr1_s0_d3, epic12_device::draw_sprite_f1_ti0_tr1_s1_d3, epic12_device::draw_sprite_f1_ti0_tr1_s2_d3, epic12_device::draw_sprite_f1_ti0_tr1_s3_d3, epic12_device::draw_sprite_f1_ti0_tr1_s4_d3, epic12_device::draw_sprite_f1_ti0_tr1_s5_d3, epic12_device::draw_sprite_f1_ti0_tr1_s6_d3, epic12_device::draw_sprite_f1_ti0_tr1_s7_d3,
+ epic12_device::draw_sprite_f1_ti0_tr1_s0_d4, epic12_device::draw_sprite_f1_ti0_tr1_s1_d4, epic12_device::draw_sprite_f1_ti0_tr1_s2_d4, epic12_device::draw_sprite_f1_ti0_tr1_s3_d4, epic12_device::draw_sprite_f1_ti0_tr1_s4_d4, epic12_device::draw_sprite_f1_ti0_tr1_s5_d4, epic12_device::draw_sprite_f1_ti0_tr1_s6_d4, epic12_device::draw_sprite_f1_ti0_tr1_s7_d4,
+ epic12_device::draw_sprite_f1_ti0_tr1_s0_d5, epic12_device::draw_sprite_f1_ti0_tr1_s1_d5, epic12_device::draw_sprite_f1_ti0_tr1_s2_d5, epic12_device::draw_sprite_f1_ti0_tr1_s3_d5, epic12_device::draw_sprite_f1_ti0_tr1_s4_d5, epic12_device::draw_sprite_f1_ti0_tr1_s5_d5, epic12_device::draw_sprite_f1_ti0_tr1_s6_d5, epic12_device::draw_sprite_f1_ti0_tr1_s7_d5,
+ epic12_device::draw_sprite_f1_ti0_tr1_s0_d6, epic12_device::draw_sprite_f1_ti0_tr1_s1_d6, epic12_device::draw_sprite_f1_ti0_tr1_s2_d6, epic12_device::draw_sprite_f1_ti0_tr1_s3_d6, epic12_device::draw_sprite_f1_ti0_tr1_s4_d6, epic12_device::draw_sprite_f1_ti0_tr1_s5_d6, epic12_device::draw_sprite_f1_ti0_tr1_s6_d6, epic12_device::draw_sprite_f1_ti0_tr1_s7_d6,
+ epic12_device::draw_sprite_f1_ti0_tr1_s0_d7, epic12_device::draw_sprite_f1_ti0_tr1_s1_d7, epic12_device::draw_sprite_f1_ti0_tr1_s2_d7, epic12_device::draw_sprite_f1_ti0_tr1_s3_d7, epic12_device::draw_sprite_f1_ti0_tr1_s4_d7, epic12_device::draw_sprite_f1_ti0_tr1_s5_d7, epic12_device::draw_sprite_f1_ti0_tr1_s6_d7, epic12_device::draw_sprite_f1_ti0_tr1_s7_d7,
+};
+
+epic12_device_blitfunction epic12_device_f1_ti0_tr0_blit_funcs[] =
+{
+ epic12_device::draw_sprite_f1_ti0_tr0_s0_d0, epic12_device::draw_sprite_f1_ti0_tr0_s1_d0, epic12_device::draw_sprite_f1_ti0_tr0_s2_d0, epic12_device::draw_sprite_f1_ti0_tr0_s3_d0, epic12_device::draw_sprite_f1_ti0_tr0_s4_d0, epic12_device::draw_sprite_f1_ti0_tr0_s5_d0, epic12_device::draw_sprite_f1_ti0_tr0_s6_d0, epic12_device::draw_sprite_f1_ti0_tr0_s7_d0,
+ epic12_device::draw_sprite_f1_ti0_tr0_s0_d1, epic12_device::draw_sprite_f1_ti0_tr0_s1_d1, epic12_device::draw_sprite_f1_ti0_tr0_s2_d1, epic12_device::draw_sprite_f1_ti0_tr0_s3_d1, epic12_device::draw_sprite_f1_ti0_tr0_s4_d1, epic12_device::draw_sprite_f1_ti0_tr0_s5_d1, epic12_device::draw_sprite_f1_ti0_tr0_s6_d1, epic12_device::draw_sprite_f1_ti0_tr0_s7_d1,
+ epic12_device::draw_sprite_f1_ti0_tr0_s0_d2, epic12_device::draw_sprite_f1_ti0_tr0_s1_d2, epic12_device::draw_sprite_f1_ti0_tr0_s2_d2, epic12_device::draw_sprite_f1_ti0_tr0_s3_d2, epic12_device::draw_sprite_f1_ti0_tr0_s4_d2, epic12_device::draw_sprite_f1_ti0_tr0_s5_d2, epic12_device::draw_sprite_f1_ti0_tr0_s6_d2, epic12_device::draw_sprite_f1_ti0_tr0_s7_d2,
+ epic12_device::draw_sprite_f1_ti0_tr0_s0_d3, epic12_device::draw_sprite_f1_ti0_tr0_s1_d3, epic12_device::draw_sprite_f1_ti0_tr0_s2_d3, epic12_device::draw_sprite_f1_ti0_tr0_s3_d3, epic12_device::draw_sprite_f1_ti0_tr0_s4_d3, epic12_device::draw_sprite_f1_ti0_tr0_s5_d3, epic12_device::draw_sprite_f1_ti0_tr0_s6_d3, epic12_device::draw_sprite_f1_ti0_tr0_s7_d3,
+ epic12_device::draw_sprite_f1_ti0_tr0_s0_d4, epic12_device::draw_sprite_f1_ti0_tr0_s1_d4, epic12_device::draw_sprite_f1_ti0_tr0_s2_d4, epic12_device::draw_sprite_f1_ti0_tr0_s3_d4, epic12_device::draw_sprite_f1_ti0_tr0_s4_d4, epic12_device::draw_sprite_f1_ti0_tr0_s5_d4, epic12_device::draw_sprite_f1_ti0_tr0_s6_d4, epic12_device::draw_sprite_f1_ti0_tr0_s7_d4,
+ epic12_device::draw_sprite_f1_ti0_tr0_s0_d5, epic12_device::draw_sprite_f1_ti0_tr0_s1_d5, epic12_device::draw_sprite_f1_ti0_tr0_s2_d5, epic12_device::draw_sprite_f1_ti0_tr0_s3_d5, epic12_device::draw_sprite_f1_ti0_tr0_s4_d5, epic12_device::draw_sprite_f1_ti0_tr0_s5_d5, epic12_device::draw_sprite_f1_ti0_tr0_s6_d5, epic12_device::draw_sprite_f1_ti0_tr0_s7_d5,
+ epic12_device::draw_sprite_f1_ti0_tr0_s0_d6, epic12_device::draw_sprite_f1_ti0_tr0_s1_d6, epic12_device::draw_sprite_f1_ti0_tr0_s2_d6, epic12_device::draw_sprite_f1_ti0_tr0_s3_d6, epic12_device::draw_sprite_f1_ti0_tr0_s4_d6, epic12_device::draw_sprite_f1_ti0_tr0_s5_d6, epic12_device::draw_sprite_f1_ti0_tr0_s6_d6, epic12_device::draw_sprite_f1_ti0_tr0_s7_d6,
+ epic12_device::draw_sprite_f1_ti0_tr0_s0_d7, epic12_device::draw_sprite_f1_ti0_tr0_s1_d7, epic12_device::draw_sprite_f1_ti0_tr0_s2_d7, epic12_device::draw_sprite_f1_ti0_tr0_s3_d7, epic12_device::draw_sprite_f1_ti0_tr0_s4_d7, epic12_device::draw_sprite_f1_ti0_tr0_s5_d7, epic12_device::draw_sprite_f1_ti0_tr0_s6_d7, epic12_device::draw_sprite_f1_ti0_tr0_s7_d7,
+};
+
+
+
+inline void epic12_device::gfx_draw_shadow_copy(address_space &space, offs_t *addr)
+{
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr); // UINT16 dst_x_start = COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr); // UINT16 dst_y_start = COPY_NEXT_WORD(space, addr);
+ UINT16 w = COPY_NEXT_WORD(space, addr);
+ UINT16 h = COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+ COPY_NEXT_WORD(space, addr);
+
+
+
+ // todo, calcualte clipping.
+ epic12_device_blit_delay += w*h;
+
+}
+
+
+
+inline void epic12_device::gfx_draw(offs_t *addr)
+{
+ int x,y, dimx,dimy, flipx,flipy;//, src_p;
+ int trans,blend, s_mode, d_mode;
+ clr_t tint_clr;
+ int tinted = 0;
+
+ UINT16 attr = READ_NEXT_WORD(addr);
+ UINT16 alpha = READ_NEXT_WORD(addr);
+ UINT16 src_x = READ_NEXT_WORD(addr);
+ UINT16 src_y = READ_NEXT_WORD(addr);
+ UINT16 dst_x_start = READ_NEXT_WORD(addr);
+ UINT16 dst_y_start = READ_NEXT_WORD(addr);
+ UINT16 w = READ_NEXT_WORD(addr);
+ UINT16 h = READ_NEXT_WORD(addr);
+ UINT16 tint_r = READ_NEXT_WORD(addr);
+ UINT16 tint_gb = READ_NEXT_WORD(addr);
+
+ // 0: +alpha
+ // 1: +source
+ // 2: +dest
+ // 3: *
+ // 4: -alpha
+ // 5: -source
+ // 6: -dest
+ // 7: *
+
+ d_mode = attr & 0x0007;
+ s_mode = (attr & 0x0070) >> 4;
+
+ trans = attr & 0x0100;
+ blend = attr & 0x0200;
+
+ flipy = attr & 0x0400;
+ flipx = attr & 0x0800;
+
+ const UINT8 d_alpha = ((alpha & 0x00ff) )>>3;
+ const UINT8 s_alpha = ((alpha & 0xff00) >> 8 )>>3;
+
+// src_p = 0;
+ src_x = src_x & 0x1fff;
+ src_y = src_y & 0x0fff;
+
+
+ x = (dst_x_start & 0x7fff) - (dst_x_start & 0x8000);
+ y = (dst_y_start & 0x7fff) - (dst_y_start & 0x8000);
+
+ dimx = (w & 0x1fff) + 1;
+ dimy = (h & 0x0fff) + 1;
+
+ // convert parameters to clr
+
+
+ tint_to_clr(tint_r & 0x00ff, (tint_gb >> 8) & 0xff, tint_gb & 0xff, &tint_clr);
+
+ /* interestingly this gets set to 0x20 for 'normal' not 0x1f */
+
+ if (tint_clr.r!=0x20)
+ tinted = 1;
+
+ if (tint_clr.g!=0x20)
+ tinted = 1;
+
+ if (tint_clr.b!=0x20)
+ tinted = 1;
+
+
+ // surprisingly frequent, need to verify if it produces a worthwhile speedup tho.
+ if ((s_mode==0 && s_alpha==0x1f) && (d_mode==4 && d_alpha==0x1f))
+ blend = 0;
+
+ if (tinted)
+ {
+ if (!flipx)
+ {
+ if (trans)
+ {
+ if (!blend)
+ {
+ draw_sprite_f0_ti1_tr1_plain(draw_params);
+ }
+ else
+ {
+ epic12_device_f0_ti1_tr1_blit_funcs[s_mode | (d_mode<<3)](draw_params);
+ }
+ }
+ else
+ {
+ if (!blend)
+ {
+ draw_sprite_f0_ti1_tr0_plain(draw_params);
+ }
+ else
+ {
+ epic12_device_f0_ti1_tr0_blit_funcs[s_mode | (d_mode<<3)](draw_params);
+ }
+ }
+ }
+ else // flipx
+ {
+ if (trans)
+ {
+ if (!blend)
+ {
+ draw_sprite_f1_ti1_tr1_plain(draw_params);
+ }
+ else
+ {
+ epic12_device_f1_ti1_tr1_blit_funcs[s_mode | (d_mode<<3)](draw_params);
+ }
+ }
+ else
+ {
+ if (!blend)
+ {
+ draw_sprite_f1_ti1_tr0_plain(draw_params);
+ }
+ else
+ {
+ epic12_device_f1_ti1_tr0_blit_funcs[s_mode | (d_mode<<3)](draw_params);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (blend==0 && tinted==0)
+ {
+ if (!flipx)
+ {
+ if (trans)
+ {
+ draw_sprite_f0_ti0_tr1_simple(draw_params);
+ }
+ else
+ {
+ draw_sprite_f0_ti0_tr0_simple(draw_params);
+ }
+ }
+ else
+ {
+ if (trans)
+ {
+ draw_sprite_f1_ti0_tr1_simple(draw_params);
+ }
+ else
+ {
+ draw_sprite_f1_ti0_tr0_simple(draw_params);
+ }
+
+ }
+
+ return;
+ }
+
+
+
+ //printf("smode %d dmode %d\n", s_mode, d_mode);
+
+ if (!flipx)
+ {
+ if (trans)
+ {
+ if (!blend)
+ {
+ draw_sprite_f0_ti0_plain(draw_params);
+ }
+ else
+ {
+ epic12_device_f0_ti0_tr1_blit_funcs[s_mode | (d_mode<<3)](draw_params);
+ }
+ }
+ else
+ {
+ if (!blend)
+ {
+ draw_sprite_f0_ti0_tr0_plain(draw_params);
+ }
+ else
+ {
+ epic12_device_f0_ti0_tr0_blit_funcs[s_mode | (d_mode<<3)](draw_params);
+ }
+ }
+ }
+ else // flipx
+ {
+ if (trans)
+ {
+ if (!blend)
+ {
+ draw_sprite_f1_ti0_plain(draw_params);
+ }
+ else
+ {
+ epic12_device_f1_ti0_tr1_blit_funcs[s_mode | (d_mode<<3)](draw_params);
+ }
+ }
+ else
+ {
+ if (!blend)
+ {
+ draw_sprite_f1_ti0_tr0_plain(draw_params);
+ }
+ else
+ {
+ epic12_device_f1_ti0_tr0_blit_funcs[s_mode | (d_mode<<3)](draw_params);
+ }
+ }
+ }
+ }
+
+
+
+}
+
+
+void epic12_device::gfx_create_shadow_copy(address_space &space)
+{
+ offs_t addr = m_gfx_addr & 0x1fffffff;
+ m_clip.set(m_gfx_scroll_1_x_shadowcopy, m_gfx_scroll_1_x_shadowcopy + 320-1, m_gfx_scroll_1_y_shadowcopy, m_gfx_scroll_1_y_shadowcopy + 240-1);
+
+ while (1)
+ {
+ UINT16 data = COPY_NEXT_WORD(space, &addr);
+
+ switch( data & 0xf000 )
+ {
+ case 0x0000:
+ case 0xf000:
+ return;
+
+ case 0xc000:
+ if (COPY_NEXT_WORD(space, &addr)) // cliptype
+ m_clip.set(m_gfx_scroll_1_x_shadowcopy, m_gfx_scroll_1_x_shadowcopy + 320-1, m_gfx_scroll_1_y_shadowcopy, m_gfx_scroll_1_y_shadowcopy + 240-1);
+ else
+ m_clip.set(0, 0x2000-1, 0, 0x1000-1);
+ break;
+
+ case 0x2000:
+ addr -= 2;
+ gfx_upload_shadow_copy(space, &addr);
+ break;
+
+ case 0x1000:
+ addr -= 2;
+ gfx_draw_shadow_copy(space, &addr);
+ break;
+
+ default:
+ popmessage("GFX op = %04X", data);
+ return;
+ }
+ }
+}
+
+
+void epic12_device::gfx_exec(void)
+{
+ offs_t addr = m_gfx_addr_shadowcopy & 0x1fffffff;
+ m_clip.set(m_gfx_scroll_1_x_shadowcopy, m_gfx_scroll_1_x_shadowcopy + 320-1, m_gfx_scroll_1_y_shadowcopy, m_gfx_scroll_1_y_shadowcopy + 240-1);
+
+// logerror("GFX EXEC: %08X\n", addr);
+
+ while (1)
+ {
+ UINT16 data = READ_NEXT_WORD(&addr);
+
+ switch( data & 0xf000 )
+ {
+ case 0x0000:
+ case 0xf000:
+ return;
+
+ case 0xc000:
+ if (READ_NEXT_WORD(&addr)) // cliptype
+ m_clip.set(m_gfx_scroll_1_x_shadowcopy, m_gfx_scroll_1_x_shadowcopy + 320-1, m_gfx_scroll_1_y_shadowcopy, m_gfx_scroll_1_y_shadowcopy + 240-1);
+ else
+ m_clip.set(0, 0x2000-1, 0, 0x1000-1);
+ break;
+
+ case 0x2000:
+ addr -= 2;
+ gfx_upload(&addr);
+ break;
+
+ case 0x1000:
+ addr -= 2;
+ gfx_draw(&addr);
+ break;
+
+ default:
+ popmessage("GFX op = %04X", data);
+ return;
+ }
+ }
+}
+
+
+void epic12_device::gfx_exec_unsafe(void)
+{
+ offs_t addr = m_gfx_addr & 0x1fffffff;
+ m_clip.set(m_gfx_scroll_1_x, m_gfx_scroll_1_x + 320-1, m_gfx_scroll_1_y, m_gfx_scroll_1_y + 240-1);
+
+// logerror("GFX EXEC: %08X\n", addr);
+
+ while (1)
+ {
+ UINT16 data = READ_NEXT_WORD(&addr);
+
+ switch( data & 0xf000 )
+ {
+ case 0x0000:
+ case 0xf000:
+ return;
+
+ case 0xc000:
+ if (READ_NEXT_WORD(&addr)) // cliptype
+ m_clip.set(m_gfx_scroll_1_x, m_gfx_scroll_1_x + 320-1, m_gfx_scroll_1_y, m_gfx_scroll_1_y + 240-1);
+ else
+ m_clip.set(0, 0x2000-1, 0, 0x1000-1);
+ break;
+
+ case 0x2000:
+ addr -= 2;
+ gfx_upload(&addr);
+ break;
+
+ case 0x1000:
+ addr -= 2;
+ gfx_draw(&addr);
+ break;
+
+ default:
+ popmessage("GFX op = %04X", data);
+ return;
+ }
+ }
+}
+
+
+
+void *epic12_device::blit_request_callback(void *param, int threadid)
+{
+ epic12_device *object = reinterpret_cast<epic12_device *>(param);
+
+ object->gfx_exec();
+ return NULL;
+}
+
+
+
+void *epic12_device::blit_request_callback_unsafe(void *param, int threadid)
+{
+ epic12_device *object = reinterpret_cast<epic12_device *>(param);
+
+ epic12_device_blit_delay = 0;
+ object->gfx_exec_unsafe();
+ return NULL;
+}
+
+
+READ32_MEMBER( epic12_device::gfx_ready_r )
+{
+ return 0x00000010;
+}
+
+READ32_MEMBER( epic12_device::gfx_ready_r_unsafe )
+{
+ if (m_blitter_busy)
+ {
+ m_maincpu->spin_until_time(attotime::from_usec(10));
+ return 0x00000000;
+ }
+ else
+ return 0x00000010;
+}
+
+WRITE32_MEMBER( epic12_device::gfx_exec_w )
+{
+ if ( ACCESSING_BITS_0_7 )
+ {
+ if (data & 1)
+ {
+ //g_profiler.start(PROFILER_USER1);
+ // make sure we've not already got a request running
+ if (m_blitter_request)
+ {
+ int result;
+ do
+ {
+ result = osd_work_item_wait(m_blitter_request, 1000);
+ } while (result==0);
+ osd_work_item_release(m_blitter_request);
+ }
+
+ epic12_device_blit_delay = 0;
+ gfx_create_shadow_copy(space); // create a copy of the blit list so we can safely thread it.
+
+ if (epic12_device_blit_delay)
+ {
+ m_blitter_busy = 1;
+ m_blitter_delay_timer->adjust(attotime::from_nsec(epic12_device_blit_delay*8)); // NOT accurate timing (currently ignored anyway)
+ }
+
+ m_gfx_addr_shadowcopy = m_gfx_addr;
+ m_gfx_scroll_0_x_shadowcopy = m_gfx_scroll_0_x;
+ m_gfx_scroll_0_y_shadowcopy = m_gfx_scroll_0_y;
+ m_gfx_scroll_1_x_shadowcopy = m_gfx_scroll_1_x;
+ m_gfx_scroll_1_y_shadowcopy = m_gfx_scroll_1_y;
+ m_blitter_request = osd_work_item_queue(m_work_queue, blit_request_callback, (void*)this, 0);
+ //g_profiler.stop();
+ }
+ }
+}
+
+
+WRITE32_MEMBER( epic12_device::gfx_exec_w_unsafe )
+{
+ if ( ACCESSING_BITS_0_7 )
+ {
+ if (data & 1)
+ {
+ //g_profiler.start(PROFILER_USER1);
+ // make sure we've not already got a request running
+ if (m_blitter_request)
+ {
+ int result;
+ do
+ {
+ result = osd_work_item_wait(m_blitter_request, 1000);
+ } while (result==0);
+ osd_work_item_release(m_blitter_request);
+ }
+
+ if (epic12_device_blit_delay)
+ {
+ m_blitter_busy = 1;
+ int delay = epic12_device_blit_delay*(15 * m_delay_scale / 50);
+ //printf("delay %d\n", delay);
+ m_blitter_delay_timer->adjust(attotime::from_nsec(delay));
+ }
+ else
+ {
+ m_blitter_busy = 0;
+ }
+
+ m_blitter_request = osd_work_item_queue(m_work_queue, blit_request_callback_unsafe, (void*)this, 0);
+ //g_profiler.stop();
+ }
+ }
+}
+
+
+void epic12_device::draw_screen(bitmap_rgb32 &bitmap, const rectangle &cliprect )
+{
+ if (!m_is_unsafe)
+ {
+ if (m_blitter_request)
+ {
+ int result;
+ do
+ {
+ result = osd_work_item_wait(m_blitter_request, 1000);
+ } while (result==0);
+ osd_work_item_release(m_blitter_request);
+ }
+ }
+
+ int scroll_0_x, scroll_0_y;
+// int scroll_1_x, scroll_1_y;
+
+ bitmap.fill(0, cliprect);
+
+ scroll_0_x = -m_gfx_scroll_0_x;
+ scroll_0_y = -m_gfx_scroll_0_y;
+// scroll_1_x = -m_gfx_scroll_1_x;
+// scroll_1_y = -m_gfx_scroll_1_y;
+
+ //printf("SCREEN UPDATE\n %d %d %d %d\n", scroll_0_x, scroll_0_y, scroll_1_x, scroll_1_y);
+
+ copyscrollbitmap(bitmap, *m_bitmaps, 1,&scroll_0_x, 1,&scroll_0_y, cliprect);
+}
+
+
+
+
+
+
+READ32_MEMBER( epic12_device::blitter_r )
+{
+ switch (offset*4)
+ {
+ case 0x10:
+ return gfx_ready_r(space, offset, mem_mask);
+
+ case 0x24:
+ return 0xffffffff;
+
+ case 0x28:
+ return 0xffffffff;
+
+ case 0x50:
+ return space.machine().root_device().ioport(":DSW")->read();
+
+ default:
+ logerror("unknownblitter_r %08x %08x\n", offset*4, mem_mask);
+ break;
+
+ }
+ return 0;
+}
+
+READ32_MEMBER( epic12_device::blitter_r_unsafe )
+{
+ switch (offset*4)
+ {
+ case 0x10:
+ return gfx_ready_r_unsafe(space, offset, mem_mask);
+
+ case 0x24:
+ return 0xffffffff;
+
+ case 0x28:
+ return 0xffffffff;
+
+ case 0x50:
+ return space.machine().root_device().ioport(":DSW")->read();
+
+ default:
+ logerror("unknownblitter_r %08x %08x\n", offset*4, mem_mask);
+ break;
+
+ }
+ return 0;
+}
+
+
+WRITE32_MEMBER( epic12_device::blitter_w )
+{
+ switch (offset*4)
+ {
+ case 0x04:
+ gfx_exec_w(space,offset,data,mem_mask);
+ break;
+
+ case 0x08:
+ COMBINE_DATA(&m_gfx_addr);
+ break;
+
+ case 0x14:
+ COMBINE_DATA(&m_gfx_scroll_0_x);
+ break;
+
+ case 0x18:
+ COMBINE_DATA(&m_gfx_scroll_0_y);
+ break;
+
+ case 0x40:
+ COMBINE_DATA(&m_gfx_scroll_1_x);
+ break;
+
+ case 0x44:
+ COMBINE_DATA(&m_gfx_scroll_1_y);
+ break;
+
+ }
+}
+
+WRITE32_MEMBER( epic12_device::blitter_w_unsafe )
+{
+ switch (offset*4)
+ {
+ case 0x04:
+ gfx_exec_w_unsafe(space,offset,data,mem_mask);
+ break;
+
+ case 0x08:
+ COMBINE_DATA(&m_gfx_addr);
+ break;
+
+ case 0x14:
+ COMBINE_DATA(&m_gfx_scroll_0_x);
+ break;
+
+ case 0x18:
+ COMBINE_DATA(&m_gfx_scroll_0_y);
+ break;
+
+ case 0x40:
+ COMBINE_DATA(&m_gfx_scroll_1_x);
+ break;
+
+ case 0x44:
+ COMBINE_DATA(&m_gfx_scroll_1_y);
+ break;
+
+ }
+}
+
+void epic12_device::install_handlers(int addr1, int addr2)
+{
+ address_space &space = m_maincpu->space(AS_PROGRAM);
+
+ read32_delegate read;
+ write32_delegate write;
+
+ if (m_is_unsafe)
+ {
+ printf("using unsafe blit code!\n");
+ read = read32_delegate(FUNC(epic12_device::blitter_r_unsafe), this);
+ write = write32_delegate(FUNC(epic12_device::blitter_w_unsafe), this);
+ }
+ else
+ {
+ read = read32_delegate(FUNC(epic12_device::blitter_r), this);
+ write = write32_delegate(FUNC(epic12_device::blitter_w), this);
+ }
+
+ space.install_readwrite_handler(addr1, addr2, read , write, U64(0xffffffffffffffff));
+}
+
+READ64_MEMBER( epic12_device::fpga_r )
+{
+ return 0xff;
+}
+
+// todo, store what's written here and checksum it, different microcode probably leads to slightly different blitter timings
+WRITE64_MEMBER( epic12_device::fpga_w )
+{
+ if (ACCESSING_BITS_24_31)
+ {
+ // data & 0x08 = CE
+ // data & 0x10 = CLK
+ // data & 0x20 = DATA
+ }
+}
diff --git a/src/devices/video/epic12.h b/src/devices/video/epic12.h
new file mode 100644
index 00000000000..42d4d3d2172
--- /dev/null
+++ b/src/devices/video/epic12.h
@@ -0,0 +1,840 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood, Luca Elia, MetalliC
+/* emulation of Altera Cyclone EPIC12 FPGA programmed as a blitter */
+
+#define MCFG_EPIC12_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, EPIC12, 0)
+
+#define MCFG_EPIC12_SET_MAINRAMSIZE( _rgn ) \
+ epic12_device::set_mainramsize(*device, _rgn);
+
+
+extern UINT8 epic12_device_colrtable[0x20][0x40];
+extern UINT8 epic12_device_colrtable_rev[0x20][0x40];
+extern UINT8 epic12_device_colrtable_add[0x20][0x20];
+extern UINT64 epic12_device_blit_delay;
+
+struct _clr_t
+{
+ UINT8 b,g,r,t;
+};
+
+typedef struct _clr_t clr_t;
+
+union colour_t
+{
+ clr_t trgb;
+ UINT32 u32;
+};
+
+typedef void (*epic12_device_blitfunction)(bitmap_rgb32 *,
+ const rectangle *,
+ UINT32 *, /* gfx */
+ int , /* src_x */
+ int , /* src_y */
+ const int , /* dst_x_start */
+ const int , /* dst_y_start */
+ int , /* dimx */
+ int , /* dimy */
+ const int , /* flipy */
+ const UINT8 , /* s_alpha */
+ const UINT8 , /* d_alpha */
+ //int , /* tint */
+ const clr_t * );
+
+
+class epic12_device : public device_t,
+ public device_video_interface
+{
+public:
+ epic12_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void set_rambase(UINT16* rambase) { m_ram16 = rambase; }
+ void set_delay_scale(int delay_scale) { m_delay_scale = delay_scale; }
+ void set_is_unsafe(int is_unsafe) { m_is_unsafe = is_unsafe; }
+ void set_cpu_device(cpu_device* maincpu) { m_maincpu = maincpu; }
+
+ inline UINT16 READ_NEXT_WORD(offs_t *addr);
+
+ static void set_mainramsize(device_t &device, int ramsize)
+ {
+ epic12_device &dev = downcast<epic12_device &>(device);
+ dev.m_main_ramsize = ramsize;
+ dev.m_main_rammask = ramsize-1;
+ }
+
+ static void *blit_request_callback(void *param, int threadid);
+
+ DECLARE_READ64_MEMBER( fpga_r );
+ DECLARE_WRITE64_MEMBER( fpga_w );
+
+ void draw_screen(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ UINT16* m_ram16;
+ UINT32 m_gfx_addr;
+ UINT32 m_gfx_scroll_0_x, m_gfx_scroll_0_y;
+ UINT32 m_gfx_scroll_1_x, m_gfx_scroll_1_y;
+
+ int m_gfx_size;
+ bitmap_rgb32 *m_bitmaps;
+ rectangle m_clip;
+
+ UINT16* m_use_ram;
+ int m_main_ramsize; // type D has double the main ram
+ int m_main_rammask;
+
+ int m_is_unsafe;
+ int m_delay_scale;
+ cpu_device* m_maincpu;
+
+ void install_handlers(int addr1, int addr2);
+
+ // thread safe mode, with no delays & shadow ram copy
+ DECLARE_READ32_MEMBER(blitter_r);
+ DECLARE_WRITE32_MEMBER(blitter_w);
+ UINT32 m_gfx_addr_shadowcopy;
+ UINT32 m_gfx_scroll_0_x_shadowcopy, m_gfx_scroll_0_y_shadowcopy;
+ UINT32 m_gfx_scroll_1_x_shadowcopy, m_gfx_scroll_1_y_shadowcopy;
+ UINT16* m_ram16_copy;
+ inline void gfx_upload_shadow_copy(address_space &space, offs_t *addr);
+ inline void gfx_create_shadow_copy(address_space &space);
+ inline UINT16 COPY_NEXT_WORD(address_space &space, offs_t *addr);
+ inline void gfx_draw_shadow_copy(address_space &space, offs_t *addr);
+ inline void gfx_upload(offs_t *addr);
+ inline void gfx_draw(offs_t *addr);
+ void gfx_exec(void);
+ DECLARE_READ32_MEMBER( gfx_ready_r );
+ DECLARE_WRITE32_MEMBER( gfx_exec_w );
+
+ // for thread unsafe mode with blitter delays, no shadow copy of RAM
+ DECLARE_READ32_MEMBER(blitter_r_unsafe);
+ DECLARE_WRITE32_MEMBER(blitter_w_unsafe);
+ READ32_MEMBER( gfx_ready_r_unsafe );
+ WRITE32_MEMBER( gfx_exec_w_unsafe );
+ void gfx_exec_unsafe(void);
+ static void *blit_request_callback_unsafe(void *param, int threadid);
+
+#define BLIT_FUNCTION static void
+#define BLIT_PARAMS bitmap_rgb32 *bitmap, const rectangle *clip, UINT32 *gfx, int src_x, int src_y, const int dst_x_start, const int dst_y_start, int dimx, int dimy, const int flipy, const UINT8 s_alpha, const UINT8 d_alpha, const clr_t *tint_clr
+
+ BLIT_FUNCTION draw_sprite_f0_ti0_plain(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s0_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s1_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s2_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s3_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s4_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s5_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s6_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s7_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s0_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s1_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s2_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s3_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s4_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s5_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s6_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s7_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s0_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s1_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s2_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s3_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s4_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s5_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s6_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s7_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s0_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s1_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s2_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s3_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s4_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s5_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s6_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s7_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s0_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s1_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s2_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s3_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s4_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s5_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s6_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s7_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s0_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s1_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s2_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s3_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s4_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s5_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s6_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s7_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s0_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s1_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s2_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s3_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s4_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s5_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s6_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s7_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s0_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s1_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s2_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s3_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s4_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s5_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s6_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_s7_d7(BLIT_PARAMS);
+
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_plain(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s0_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s1_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s2_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s3_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s4_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s5_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s6_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s7_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s0_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s1_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s2_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s3_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s4_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s5_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s6_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s7_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s0_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s1_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s2_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s3_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s4_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s5_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s6_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s7_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s0_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s1_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s2_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s3_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s4_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s5_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s6_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s7_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s0_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s1_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s2_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s3_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s4_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s5_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s6_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s7_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s0_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s1_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s2_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s3_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s4_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s5_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s6_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s7_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s0_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s1_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s2_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s3_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s4_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s5_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s6_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s7_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s0_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s1_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s2_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s3_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s4_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s5_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s6_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_s7_d7(BLIT_PARAMS);
+
+ BLIT_FUNCTION draw_sprite_f1_ti0_plain(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s0_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s1_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s2_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s3_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s4_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s5_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s6_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s7_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s0_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s1_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s2_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s3_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s4_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s5_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s6_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s7_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s0_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s1_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s2_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s3_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s4_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s5_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s6_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s7_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s0_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s1_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s2_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s3_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s4_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s5_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s6_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s7_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s0_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s1_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s2_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s3_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s4_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s5_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s6_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s7_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s0_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s1_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s2_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s3_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s4_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s5_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s6_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s7_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s0_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s1_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s2_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s3_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s4_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s5_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s6_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s7_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s0_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s1_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s2_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s3_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s4_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s5_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s6_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_s7_d7(BLIT_PARAMS);
+
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_plain(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s0_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s1_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s2_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s3_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s4_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s5_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s6_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s7_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s0_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s1_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s2_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s3_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s4_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s5_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s6_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s7_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s0_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s1_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s2_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s3_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s4_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s5_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s6_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s7_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s0_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s1_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s2_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s3_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s4_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s5_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s6_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s7_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s0_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s1_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s2_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s3_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s4_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s5_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s6_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s7_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s0_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s1_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s2_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s3_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s4_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s5_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s6_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s7_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s0_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s1_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s2_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s3_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s4_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s5_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s6_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s7_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s0_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s1_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s2_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s3_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s4_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s5_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s6_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_s7_d7(BLIT_PARAMS);
+
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_plain(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s0_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s1_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s2_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s3_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s4_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s5_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s6_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s7_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s0_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s1_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s2_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s3_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s4_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s5_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s6_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s7_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s0_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s1_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s2_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s3_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s4_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s5_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s6_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s7_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s0_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s1_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s2_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s3_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s4_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s5_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s6_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s7_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s0_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s1_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s2_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s3_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s4_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s5_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s6_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s7_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s0_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s1_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s2_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s3_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s4_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s5_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s6_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s7_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s0_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s1_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s2_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s3_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s4_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s5_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s6_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s7_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s0_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s1_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s2_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s3_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s4_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s5_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s6_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr1_s7_d7(BLIT_PARAMS);
+
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_plain(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s0_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s1_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s2_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s3_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s4_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s5_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s6_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s7_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s0_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s1_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s2_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s3_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s4_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s5_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s6_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s7_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s0_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s1_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s2_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s3_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s4_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s5_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s6_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s7_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s0_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s1_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s2_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s3_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s4_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s5_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s6_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s7_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s0_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s1_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s2_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s3_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s4_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s5_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s6_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s7_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s0_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s1_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s2_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s3_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s4_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s5_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s6_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s7_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s0_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s1_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s2_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s3_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s4_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s5_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s6_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s7_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s0_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s1_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s2_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s3_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s4_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s5_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s6_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti1_tr0_s7_d7(BLIT_PARAMS);
+
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_plain(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s0_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s1_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s2_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s3_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s4_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s5_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s6_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s7_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s0_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s1_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s2_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s3_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s4_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s5_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s6_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s7_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s0_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s1_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s2_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s3_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s4_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s5_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s6_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s7_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s0_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s1_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s2_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s3_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s4_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s5_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s6_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s7_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s0_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s1_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s2_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s3_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s4_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s5_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s6_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s7_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s0_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s1_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s2_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s3_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s4_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s5_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s6_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s7_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s0_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s1_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s2_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s3_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s4_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s5_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s6_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s7_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s0_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s1_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s2_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s3_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s4_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s5_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s6_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr1_s7_d7(BLIT_PARAMS);
+
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_plain(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s0_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s1_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s2_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s3_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s4_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s5_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s6_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s7_d0(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s0_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s1_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s2_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s3_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s4_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s5_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s6_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s7_d1(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s0_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s1_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s2_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s3_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s4_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s5_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s6_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s7_d2(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s0_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s1_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s2_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s3_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s4_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s5_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s6_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s7_d3(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s0_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s1_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s2_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s3_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s4_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s5_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s6_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s7_d4(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s0_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s1_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s2_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s3_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s4_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s5_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s6_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s7_d5(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s0_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s1_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s2_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s3_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s4_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s5_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s6_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s7_d6(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s0_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s1_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s2_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s3_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s4_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s5_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s6_d7(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti1_tr0_s7_d7(BLIT_PARAMS);
+
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr1_simple(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f0_ti0_tr0_simple(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr1_simple(BLIT_PARAMS);
+ BLIT_FUNCTION draw_sprite_f1_ti0_tr0_simple(BLIT_PARAMS);
+
+
+
+ static inline void pen_to_clr(UINT32 pen, clr_t *clr)
+ {
+ // --t- ---- rrrr r--- gggg g--- bbbb b--- format
+ clr->r = (pen >> (16+3));// & 0x1f;
+ clr->g = (pen >> (8+3));// & 0x1f;
+ clr->b = (pen >> 3);// & 0x1f;
+
+ // --t- ---- ---r rrrr ---g gggg ---b bbbb format
+ // clr->r = (pen >> 16) & 0x1f;
+ // clr->g = (pen >> 8) & 0x1f;
+ // clr->b = (pen >> 0) & 0x1f;
+
+ };
+
+
+ // convert separate r,g,b biases (0..80..ff) to clr_t (-1f..0..1f)
+ static inline void tint_to_clr(UINT8 r, UINT8 g, UINT8 b, clr_t *clr)
+ {
+ clr->r = r>>2;
+ clr->g = g>>2;
+ clr->b = b>>2;
+ };
+
+ // clr_t to r5g5b5
+ static inline UINT32 clr_to_pen(const clr_t *clr)
+ {
+ // --t- ---- rrrr r--- gggg g--- bbbb b--- format
+ return (clr->r << (16+3)) | (clr->g << (8+3)) | (clr->b << 3);
+
+ // --t- ---- ---r rrrr ---g gggg ---b bbbb format
+ // return (clr->r << (16)) | (clr->g << (8)) | (clr->b);
+ };
+
+
+ static inline void clr_add_with_clr_mul_fixed(clr_t *clr, const clr_t *clr0, const UINT8 mulfixed_val, const clr_t *mulfixed_clr0)
+ {
+ clr->r = epic12_device_colrtable_add[clr0->r][epic12_device_colrtable[(mulfixed_clr0->r)][mulfixed_val]];
+ clr->g = epic12_device_colrtable_add[clr0->g][epic12_device_colrtable[(mulfixed_clr0->g)][mulfixed_val]];
+ clr->b = epic12_device_colrtable_add[clr0->b][epic12_device_colrtable[(mulfixed_clr0->b)][mulfixed_val]];
+ }
+
+ static inline void clr_add_with_clr_mul_3param(clr_t *clr, const clr_t *clr0, const clr_t *clr1, const clr_t *clr2)
+ {
+ clr->r = epic12_device_colrtable_add[clr0->r][epic12_device_colrtable[(clr2->r)][(clr1->r)]];
+ clr->g = epic12_device_colrtable_add[clr0->g][epic12_device_colrtable[(clr2->g)][(clr1->g)]];
+ clr->b = epic12_device_colrtable_add[clr0->b][epic12_device_colrtable[(clr2->b)][(clr1->b)]];
+ }
+
+ static inline void clr_add_with_clr_square(clr_t *clr, const clr_t *clr0, const clr_t *clr1)
+ {
+ clr->r = epic12_device_colrtable_add[clr0->r][epic12_device_colrtable[(clr1->r)][(clr1->r)]];
+ clr->g = epic12_device_colrtable_add[clr0->r][epic12_device_colrtable[(clr1->g)][(clr1->g)]];
+ clr->b = epic12_device_colrtable_add[clr0->r][epic12_device_colrtable[(clr1->b)][(clr1->b)]];
+ }
+
+ static inline void clr_add_with_clr_mul_fixed_rev(clr_t *clr, const clr_t *clr0, const UINT8 val, const clr_t *clr1)
+ {
+ clr->r = epic12_device_colrtable_add[clr0->r][epic12_device_colrtable_rev[val][(clr1->r)]];
+ clr->g = epic12_device_colrtable_add[clr0->g][epic12_device_colrtable_rev[val][(clr1->g)]];
+ clr->b = epic12_device_colrtable_add[clr0->b][epic12_device_colrtable_rev[val][(clr1->b)]];
+ }
+
+ static inline void clr_add_with_clr_mul_rev_3param(clr_t *clr, const clr_t *clr0, const clr_t *clr1, const clr_t *clr2)
+ {
+ clr->r = epic12_device_colrtable_add[clr0->r][epic12_device_colrtable_rev[(clr2->r)][(clr1->r)]];
+ clr->g = epic12_device_colrtable_add[clr0->g][epic12_device_colrtable_rev[(clr2->g)][(clr1->g)]];
+ clr->b = epic12_device_colrtable_add[clr0->b][epic12_device_colrtable_rev[(clr2->b)][(clr1->b)]];
+ }
+
+ static inline void clr_add_with_clr_mul_rev_square(clr_t *clr, const clr_t *clr0, const clr_t *clr1)
+ {
+ clr->r = epic12_device_colrtable_add[clr0->r][epic12_device_colrtable_rev[(clr1->r)][(clr1->r)]];
+ clr->g = epic12_device_colrtable_add[clr0->g][epic12_device_colrtable_rev[(clr1->g)][(clr1->g)]];
+ clr->b = epic12_device_colrtable_add[clr0->b][epic12_device_colrtable_rev[(clr1->b)][(clr1->b)]];
+ }
+
+
+ static inline void clr_add(clr_t *clr, const clr_t *clr0, const clr_t *clr1)
+ {
+ /*
+ clr->r = clr0->r + clr1->r;
+ clr->g = clr0->g + clr1->g;
+ clr->b = clr0->b + clr1->b;
+ */
+ // use pre-clamped lookup table
+ clr->r = epic12_device_colrtable_add[clr0->r][clr1->r];
+ clr->g = epic12_device_colrtable_add[clr0->g][clr1->g];
+ clr->b = epic12_device_colrtable_add[clr0->b][clr1->b];
+
+ }
+
+
+ static inline void clr_mul(clr_t *clr0, const clr_t *clr1)
+ {
+ clr0->r = epic12_device_colrtable[(clr0->r)][(clr1->r)];
+ clr0->g = epic12_device_colrtable[(clr0->g)][(clr1->g)];
+ clr0->b = epic12_device_colrtable[(clr0->b)][(clr1->b)];
+ }
+
+ static inline void clr_square(clr_t *clr0, const clr_t *clr1)
+ {
+ clr0->r = epic12_device_colrtable[(clr1->r)][(clr1->r)];
+ clr0->g = epic12_device_colrtable[(clr1->g)][(clr1->g)];
+ clr0->b = epic12_device_colrtable[(clr1->b)][(clr1->b)];
+ }
+
+ static inline void clr_mul_3param(clr_t *clr0, const clr_t *clr1, const clr_t *clr2)
+ {
+ clr0->r = epic12_device_colrtable[(clr2->r)][(clr1->r)];
+ clr0->g = epic12_device_colrtable[(clr2->g)][(clr1->g)];
+ clr0->b = epic12_device_colrtable[(clr2->b)][(clr1->b)];
+ }
+
+ static inline void clr_mul_rev(clr_t *clr0, const clr_t *clr1)
+ {
+ clr0->r = epic12_device_colrtable_rev[(clr0->r)][(clr1->r)];
+ clr0->g = epic12_device_colrtable_rev[(clr0->g)][(clr1->g)];
+ clr0->b = epic12_device_colrtable_rev[(clr0->b)][(clr1->b)];
+ }
+
+ static inline void clr_mul_rev_square(clr_t *clr0, const clr_t *clr1)
+ {
+ clr0->r = epic12_device_colrtable_rev[(clr1->r)][(clr1->r)];
+ clr0->g = epic12_device_colrtable_rev[(clr1->g)][(clr1->g)];
+ clr0->b = epic12_device_colrtable_rev[(clr1->b)][(clr1->b)];
+ }
+
+
+ static inline void clr_mul_rev_3param(clr_t *clr0, const clr_t *clr1, const clr_t *clr2)
+ {
+ clr0->r = epic12_device_colrtable_rev[(clr2->r)][(clr1->r)];
+ clr0->g = epic12_device_colrtable_rev[(clr2->g)][(clr1->g)];
+ clr0->b = epic12_device_colrtable_rev[(clr2->b)][(clr1->b)];
+ }
+
+ static inline void clr_mul_fixed(clr_t *clr, const UINT8 val, const clr_t *clr0)
+ {
+ clr->r = epic12_device_colrtable[val][(clr0->r)];
+ clr->g = epic12_device_colrtable[val][(clr0->g)];
+ clr->b = epic12_device_colrtable[val][(clr0->b)];
+ }
+
+ static inline void clr_mul_fixed_rev(clr_t *clr, const UINT8 val, const clr_t *clr0)
+ {
+ clr->r = epic12_device_colrtable_rev[val][(clr0->r)];
+ clr->g = epic12_device_colrtable_rev[val][(clr0->g)];
+ clr->b = epic12_device_colrtable_rev[val][(clr0->b)];
+ }
+
+ static inline void clr_copy(clr_t *clr, const clr_t *clr0)
+ {
+ clr->r = clr0->r;
+ clr->g = clr0->g;
+ clr->b = clr0->b;
+ }
+
+
+
+ // (1|s|d) * s_factor * s + (1|s|d) * d_factor * d
+ // 0: +alpha
+ // 1: +source
+ // 2: +dest
+ // 3: *
+ // 4: -alpha
+ // 5: -source
+ // 6: -dest
+ // 7: *
+
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+ osd_work_queue *m_work_queue;
+ osd_work_item *m_blitter_request;
+
+ // blit timing
+ emu_timer *m_blitter_delay_timer;
+ int m_blitter_busy;
+
+ TIMER_CALLBACK_MEMBER( blitter_delay_callback );
+};
+
+
+
+extern const device_type EPIC12;
diff --git a/src/devices/video/epic12_blit0.c b/src/devices/video/epic12_blit0.c
new file mode 100644
index 00000000000..3588573bdee
--- /dev/null
+++ b/src/devices/video/epic12_blit0.c
@@ -0,0 +1,558 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+#define REALLY_SIMPLE 0
+/* Non-Flipped, Non-Tinted, Transparent */
+#define FLIPX 0
+#define TINT 0
+#define TRANSPARENT 1
+
+#include "emu.h"
+#include "epic12.h"
+
+/* Special Case */
+#define BLENDED 0
+#define FUNCNAME draw_sprite_f0_ti0_plain
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef BLENDED
+
+/* Regular Cases*/
+#define BLENDED 1
+
+#define _SMODE 0
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s0_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s1_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s2_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s3_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s4_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s5_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s6_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s7_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///////
+
+
+#define _SMODE 0
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s0_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s1_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s2_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s3_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s4_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s5_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s6_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s7_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+////
+
+
+#define _SMODE 0
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s0_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s1_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s2_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s3_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s4_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s5_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s6_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s7_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s0_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s1_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s2_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s3_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s4_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s5_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s6_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s7_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s0_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s1_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s2_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s3_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s4_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s5_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s6_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s7_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s0_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s1_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s2_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s3_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s4_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s5_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s6_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s7_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s0_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s1_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s2_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s3_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s4_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s5_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s6_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s7_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s0_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s1_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s2_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s3_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s4_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s5_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s6_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr1_s7_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#undef BLENDED
+
+#undef FLIPX
+#undef TINT
+#undef TRANSPARENT
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12_blit1.c b/src/devices/video/epic12_blit1.c
new file mode 100644
index 00000000000..2cef4fae933
--- /dev/null
+++ b/src/devices/video/epic12_blit1.c
@@ -0,0 +1,558 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+#define REALLY_SIMPLE 0
+/* Non-Flipped, Non-Tinted, Non-Transparent */
+#define FLIPX 0
+#define TINT 0
+#define TRANSPARENT 0
+
+#include "emu.h"
+#include "epic12.h"
+
+/* Special Case */
+#define BLENDED 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_plain
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef BLENDED
+
+/* Regular Cases*/
+#define BLENDED 1
+
+#define _SMODE 0
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s0_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s1_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s2_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s3_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s4_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s5_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s6_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s7_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///////
+
+
+#define _SMODE 0
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s0_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s1_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s2_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s3_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s4_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s5_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s6_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s7_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+////
+
+
+#define _SMODE 0
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s0_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s1_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s2_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s3_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s4_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s5_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s6_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s7_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s0_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s1_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s2_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s3_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s4_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s5_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s6_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s7_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s0_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s1_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s2_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s3_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s4_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s5_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s6_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s7_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s0_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s1_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s2_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s3_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s4_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s5_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s6_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s7_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s0_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s1_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s2_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s3_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s4_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s5_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s6_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s7_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s0_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s1_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s2_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s3_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s4_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s5_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s6_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti0_tr0_s7_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#undef BLENDED
+
+#undef FLIPX
+#undef TINT
+#undef TRANSPARENT
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12_blit2.c b/src/devices/video/epic12_blit2.c
new file mode 100644
index 00000000000..039ab762635
--- /dev/null
+++ b/src/devices/video/epic12_blit2.c
@@ -0,0 +1,558 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+#define REALLY_SIMPLE 0
+/* X-Flipped, Non-Tinted, Transparent */
+#define FLIPX 1
+#define TINT 0
+#define TRANSPARENT 1
+
+#include "emu.h"
+#include "epic12.h"
+
+/* Special Case */
+#define BLENDED 0
+#define FUNCNAME draw_sprite_f1_ti0_plain
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef BLENDED
+
+/* Regular Cases*/
+#define BLENDED 1
+
+#define _SMODE 0
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s0_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s1_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s2_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s3_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s4_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s5_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s6_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s7_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///////
+
+
+#define _SMODE 0
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s0_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s1_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s2_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s3_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s4_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s5_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s6_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s7_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+////
+
+
+#define _SMODE 0
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s0_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s1_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s2_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s3_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s4_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s5_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s6_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s7_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s0_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s1_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s2_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s3_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s4_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s5_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s6_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s7_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s0_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s1_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s2_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s3_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s4_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s5_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s6_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s7_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s0_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s1_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s2_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s3_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s4_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s5_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s6_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s7_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s0_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s1_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s2_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s3_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s4_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s5_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s6_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s7_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s0_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s1_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s2_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s3_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s4_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s5_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s6_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr1_s7_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#undef BLENDED
+
+#undef FLIPX
+#undef TINT
+#undef TRANSPARENT
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12_blit3.c b/src/devices/video/epic12_blit3.c
new file mode 100644
index 00000000000..868f4c1e25e
--- /dev/null
+++ b/src/devices/video/epic12_blit3.c
@@ -0,0 +1,558 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+#define REALLY_SIMPLE 0
+/* X-Flipped, Non-Tinted, Non-Transparent */
+#define FLIPX 1
+#define TINT 0
+#define TRANSPARENT 0
+
+#include "emu.h"
+#include "epic12.h"
+
+/* Special Case */
+#define BLENDED 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_plain
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef BLENDED
+
+/* Regular Cases*/
+#define BLENDED 1
+
+#define _SMODE 0
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s0_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s1_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s2_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s3_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s4_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s5_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s6_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s7_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///////
+
+
+#define _SMODE 0
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s0_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s1_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s2_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s3_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s4_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s5_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s6_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s7_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+////
+
+
+#define _SMODE 0
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s0_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s1_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s2_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s3_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s4_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s5_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s6_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s7_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s0_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s1_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s2_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s3_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s4_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s5_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s6_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s7_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s0_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s1_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s2_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s3_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s4_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s5_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s6_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s7_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s0_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s1_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s2_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s3_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s4_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s5_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s6_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s7_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s0_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s1_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s2_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s3_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s4_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s5_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s6_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s7_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s0_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s1_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s2_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s3_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s4_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s5_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s6_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti0_tr0_s7_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#undef BLENDED
+
+#undef FLIPX
+#undef TINT
+#undef TRANSPARENT
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12_blit4.c b/src/devices/video/epic12_blit4.c
new file mode 100644
index 00000000000..c5ed93ae70b
--- /dev/null
+++ b/src/devices/video/epic12_blit4.c
@@ -0,0 +1,558 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+#define REALLY_SIMPLE 0
+/* Non-Flipped, Tinted, Transparent */
+#define FLIPX 0
+#define TINT 1
+#define TRANSPARENT 1
+
+#include "emu.h"
+#include "epic12.h"
+
+/* Special Case */
+#define BLENDED 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_plain
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef BLENDED
+
+/* Regular Cases*/
+#define BLENDED 1
+
+#define _SMODE 0
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s0_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s1_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s2_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s3_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s4_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s5_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s6_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s7_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///////
+
+
+#define _SMODE 0
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s0_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s1_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s2_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s3_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s4_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s5_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s6_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s7_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+////
+
+
+#define _SMODE 0
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s0_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s1_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s2_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s3_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s4_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s5_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s6_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s7_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s0_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s1_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s2_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s3_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s4_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s5_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s6_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s7_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s0_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s1_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s2_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s3_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s4_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s5_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s6_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s7_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s0_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s1_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s2_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s3_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s4_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s5_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s6_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s7_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s0_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s1_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s2_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s3_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s4_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s5_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s6_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s7_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s0_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s1_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s2_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s3_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s4_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s5_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s6_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr1_s7_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#undef BLENDED
+
+#undef FLIPX
+#undef TINT
+#undef TRANSPARENT
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12_blit5.c b/src/devices/video/epic12_blit5.c
new file mode 100644
index 00000000000..4c0a3b0bd0d
--- /dev/null
+++ b/src/devices/video/epic12_blit5.c
@@ -0,0 +1,558 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+#define REALLY_SIMPLE 0
+/* Non-Flipped, Tinted, Non-Transparent */
+#define FLIPX 0
+#define TINT 1
+#define TRANSPARENT 0
+
+#include "emu.h"
+#include "epic12.h"
+
+/* Special Case */
+#define BLENDED 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_plain
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef BLENDED
+
+/* Regular Cases*/
+#define BLENDED 1
+
+#define _SMODE 0
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s0_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s1_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s2_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s3_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s4_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s5_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s6_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s7_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///////
+
+
+#define _SMODE 0
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s0_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s1_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s2_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s3_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s4_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s5_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s6_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s7_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+////
+
+
+#define _SMODE 0
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s0_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s1_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s2_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s3_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s4_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s5_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s6_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s7_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s0_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s1_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s2_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s3_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s4_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s5_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s6_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s7_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s0_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s1_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s2_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s3_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s4_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s5_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s6_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s7_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s0_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s1_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s2_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s3_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s4_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s5_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s6_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s7_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s0_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s1_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s2_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s3_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s4_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s5_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s6_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s7_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s0_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s1_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s2_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s3_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s4_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s5_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s6_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f0_ti1_tr0_s7_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#undef BLENDED
+
+#undef FLIPX
+#undef TINT
+#undef TRANSPARENT
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12_blit6.c b/src/devices/video/epic12_blit6.c
new file mode 100644
index 00000000000..9ef7302b139
--- /dev/null
+++ b/src/devices/video/epic12_blit6.c
@@ -0,0 +1,558 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+#define REALLY_SIMPLE 0
+/* X-Flipped, Tinted, Transparent */
+#define FLIPX 1
+#define TINT 1
+#define TRANSPARENT 1
+
+#include "emu.h"
+#include "epic12.h"
+
+/* Special Case */
+#define BLENDED 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_plain
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef BLENDED
+
+/* Regular Cases*/
+#define BLENDED 1
+
+#define _SMODE 0
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s0_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s1_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s2_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s3_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s4_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s5_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s6_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s7_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///////
+
+
+#define _SMODE 0
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s0_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s1_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s2_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s3_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s4_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s5_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s6_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s7_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+////
+
+
+#define _SMODE 0
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s0_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s1_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s2_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s3_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s4_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s5_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s6_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s7_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s0_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s1_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s2_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s3_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s4_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s5_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s6_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s7_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s0_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s1_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s2_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s3_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s4_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s5_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s6_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s7_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s0_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s1_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s2_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s3_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s4_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s5_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s6_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s7_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s0_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s1_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s2_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s3_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s4_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s5_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s6_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s7_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s0_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s1_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s2_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s3_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s4_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s5_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s6_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr1_s7_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#undef BLENDED
+
+#undef FLIPX
+#undef TINT
+#undef TRANSPARENT
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12_blit7.c b/src/devices/video/epic12_blit7.c
new file mode 100644
index 00000000000..6ab5366a721
--- /dev/null
+++ b/src/devices/video/epic12_blit7.c
@@ -0,0 +1,558 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+#define REALLY_SIMPLE 0
+/* X-Flipped, Tinted, Non-Transparent */
+#define FLIPX 1
+#define TINT 1
+#define TRANSPARENT 0
+
+#include "emu.h"
+#include "epic12.h"
+
+/* Special Case */
+#define BLENDED 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_plain
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef BLENDED
+
+/* Regular Cases*/
+#define BLENDED 1
+
+#define _SMODE 0
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s0_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s1_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s2_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s3_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s4_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s5_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s6_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 0
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s7_d0
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///////
+
+
+#define _SMODE 0
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s0_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s1_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s2_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s3_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s4_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s5_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s6_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 1
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s7_d1
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+////
+
+
+#define _SMODE 0
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s0_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s1_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s2_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s3_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s4_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s5_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s6_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 2
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s7_d2
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s0_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s1_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s2_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s3_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s4_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s5_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s6_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 3
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s7_d3
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s0_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s1_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s2_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s3_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s4_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s5_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s6_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 4
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s7_d4
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s0_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s1_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s2_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s3_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s4_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s5_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s6_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 5
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s7_d5
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+#define _SMODE 0
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s0_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s1_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s2_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s3_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s4_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s5_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s6_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 6
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s7_d6
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+///
+
+
+#define _SMODE 0
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s0_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 1
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s1_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 2
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s2_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 3
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s3_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 4
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s4_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 5
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s5_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 6
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s6_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#define _SMODE 7
+#define _DMODE 7
+#define FUNCNAME draw_sprite_f1_ti1_tr0_s7_d7
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef _SMODE
+#undef _DMODE
+
+#undef BLENDED
+
+#undef FLIPX
+#undef TINT
+#undef TRANSPARENT
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12_blit8.c b/src/devices/video/epic12_blit8.c
new file mode 100644
index 00000000000..fb9cc763395
--- /dev/null
+++ b/src/devices/video/epic12_blit8.c
@@ -0,0 +1,42 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+/* Special case 'Really Simple' blitters, no blending, no tinting etc.*/
+
+#include "emu.h"
+#include "epic12.h"
+
+
+#define REALLY_SIMPLE 1
+#define BLENDED 0
+
+#define TRANSPARENT 1
+#define FLIPX 0
+#define FUNCNAME draw_sprite_f0_ti0_tr1_simple
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef FLIPX
+
+#define FLIPX 1
+#define FUNCNAME draw_sprite_f1_ti0_tr1_simple
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef FLIPX
+#undef TRANSPARENT
+
+
+#define TRANSPARENT 0
+#define FLIPX 0
+#define FUNCNAME draw_sprite_f0_ti0_tr0_simple
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef FLIPX
+
+#define FLIPX 1
+#define FUNCNAME draw_sprite_f1_ti0_tr0_simple
+#include "epic12in.inc"
+#undef FUNCNAME
+#undef FLIPX
+#undef TRANSPARENT
+
+#undef BLENDED
+#undef REALLY_SIMPLE
diff --git a/src/devices/video/epic12in.inc b/src/devices/video/epic12in.inc
new file mode 100644
index 00000000000..cf0c81dd80f
--- /dev/null
+++ b/src/devices/video/epic12in.inc
@@ -0,0 +1,166 @@
+/* blitter function */
+
+void epic12_device::FUNCNAME(BLIT_PARAMS)
+{
+ UINT32* gfx2;
+ int y, yf;
+
+#if REALLY_SIMPLE == 0
+ colour_t s_clr;
+#endif
+
+#if BLENDED == 1
+ colour_t d_clr;
+
+#if _SMODE == 2
+#if _DMODE != 0
+ colour_t clr0;
+#endif
+#elif _SMODE == 0
+#if _DMODE != 0
+#if _DMODE != 5
+#if _DMODE != 1
+ colour_t clr0;
+#endif
+#endif
+#endif
+#else
+ colour_t clr0;
+#endif
+
+
+#endif
+
+#if REALLY_SIMPLE == 1
+#if TRANSPARENT == 1
+ UINT32 pen;
+#endif
+#else
+ UINT32 pen;
+#endif
+ UINT32 *bmp;
+
+#if FLIPX == 1
+ src_x += (dimx-1);
+#endif
+
+ if (flipy) { yf = -1; src_y += (dimy-1); }
+ else { yf = +1; }
+
+ int starty = 0;
+ const int dst_y_end = dst_y_start+dimy;
+
+ if (dst_y_start < clip->min_y)
+ starty = clip->min_y - dst_y_start;
+
+ if (dst_y_end > clip->max_y)
+ dimy -= (dst_y_end-1) - clip->max_y;
+
+ // check things are safe to draw (note, if the source would wrap round an edge of the 0x2000*0x1000 vram we don't draw.. not sure what the hw does anyway)
+ // ddpdfk triggers this on boss explosions so it needs fixing
+#if FLIPX == 1
+ if ((src_x &0x1fff) < ((src_x-(dimx-1))&0x1fff))
+ {
+ // popmessage("sprite gets clipped off src_x %04x dimx %04x\n", src_x, dimx);
+ return;
+ }
+#else
+ if ((src_x &0x1fff) > ((src_x+(dimx-1))&0x1fff))
+ {
+ // popmessage("sprite gets clipped off src_x %04x dimx %04x\n", src_x, dimx);
+ return;
+ }
+#endif
+
+ int startx = 0;
+ const int dst_x_end = dst_x_start+dimx;
+
+ if (dst_x_start < clip->min_x)
+ startx = clip->min_x - dst_x_start;
+
+ if (dst_x_end > clip->max_x)
+ dimx -= (dst_x_end-1) - clip->max_x;
+
+// wrong/unsafe slowdown sim
+ if (dimy > starty && dimx > startx)
+ {
+ epic12_device_blit_delay += (dimy - starty)*(dimx - startx);
+
+ //printf("delay is now %d\n", epic12_device_blit_delay);
+ }
+
+#if BLENDED == 1
+#if _SMODE == 0
+#if _DMODE == 0
+ const UINT8* salpha_table = epic12_device_colrtable[s_alpha];
+ const UINT8* dalpha_table = epic12_device_colrtable[d_alpha];
+#endif
+
+#if _DMODE == 5
+ const UINT8* salpha_table = epic12_device_colrtable[s_alpha];
+#endif
+#if _DMODE == 1
+ const UINT8* salpha_table = epic12_device_colrtable[s_alpha];
+#endif
+
+#endif
+
+#if _SMODE == 2
+#if _DMODE == 0
+
+ const UINT8* dalpha_table = epic12_device_colrtable[d_alpha];
+#endif
+#endif
+#endif
+
+
+
+ for (y = starty; y < dimy; y++)
+ {
+ bmp = &bitmap->pix(dst_y_start + y, dst_x_start+startx);
+ const int ysrc_index = ((src_y + yf * y) & 0x0fff) * 0x2000;
+ gfx2 = gfx + ysrc_index;
+
+ #if FLIPX == 1
+ gfx2 += (src_x-startx);
+ #else
+ gfx2 += (src_x+startx);
+ #endif
+
+#if 1
+ const UINT32* end = bmp+(dimx-startx);
+#else
+ // maybe we can do some SSE type optimizations on larger blocks? right now this just results in more code and slower compiling tho.
+
+ const int width = dimx-startx;
+ const UINT32* end = bmp+(width);
+
+ if (width<0) return;
+
+ int bigblocks = width>>3;
+
+ while (bigblocks)
+ {
+ #include "epic12pixel.inc"
+ #include "epic12pixel.inc"
+ #include "epic12pixel.inc"
+ #include "epic12pixel.inc"
+ #include "epic12pixel.inc"
+ #include "epic12pixel.inc"
+ #include "epic12pixel.inc"
+ #include "epic12pixel.inc"
+
+ bigblocks--;
+ }
+#endif
+ while (bmp<end)
+ {
+ #include "epic12pixel.inc"
+ }
+
+ }
+
+// g_profiler.stop();
+}
+
+#undef LOOP_INCREMENTS
diff --git a/src/devices/video/epic12pixel.inc b/src/devices/video/epic12pixel.inc
new file mode 100644
index 00000000000..3cb01cebbf4
--- /dev/null
+++ b/src/devices/video/epic12pixel.inc
@@ -0,0 +1,193 @@
+/* This is the inner-most loop code (per-pixel) and thus the most performance sensitive part */
+
+#if FLIPX == 1
+#define LOOP_INCREMENTS \
+ bmp++; \
+ gfx2--;
+#else
+
+#define LOOP_INCREMENTS \
+ bmp++; \
+ gfx2++;
+#endif
+
+
+/*************** REALLY SIMPLE INNER LOOP, NON-BLENDED, NON-TINTED, SIMPLEST CASE ****************/
+#if REALLY_SIMPLE == 1
+
+#if TRANSPARENT == 1
+ pen = *gfx2;
+ if (pen & 0x20000000)
+ {
+ *bmp = pen;
+#else
+ *bmp = *gfx2;
+#endif
+
+/*************** REGULAR INNER LOOPS ****************/
+#else // NOT REALLY_SIMPLE
+
+ pen = *gfx2;
+
+#if TRANSPARENT == 1
+ if (pen & 0x20000000)
+ {
+#endif
+
+ // convert source to clr
+ pen_to_clr(pen, &s_clr.trgb);
+ //s_clr.u32 = (pen >> 3); // using the union is actually significantly slower than our pen_to_clr to function!
+ // source * intesity and clamp
+
+#if TINT == 1
+ clr_mul(&s_clr.trgb, tint_clr);
+#endif
+
+ #if BLENDED == 1
+
+ // convert destination to clr
+ pen_to_clr(*bmp, &d_clr.trgb);
+ //d_clr.u32 = *bmp >> 3; // using the union is actually significantly slower than our pen_to_clr to function!
+ #if _SMODE == 0
+ //g_profiler.start(PROFILER_USER7);
+
+
+ #if _DMODE == 0
+ //g_profiler.start(PROFILER_USER1);
+ // this is used extensively in the games (ingame, futari title screens etc.)
+
+ s_clr.trgb.r = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.r)]][dalpha_table[(d_clr.trgb.r)]];
+ s_clr.trgb.g = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.g)]][dalpha_table[(d_clr.trgb.g)]];
+ s_clr.trgb.b = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.b)]][dalpha_table[(d_clr.trgb.b)]];
+ #elif _DMODE == 1
+ //g_profiler.start(PROFILER_USER2);
+ // futari ~7%
+ s_clr.trgb.r = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.r)]][epic12_device_colrtable[(s_clr.trgb.r)][(d_clr.trgb.r)]];
+ s_clr.trgb.g = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.g)]][epic12_device_colrtable[(s_clr.trgb.g)][(d_clr.trgb.g)]];
+ s_clr.trgb.b = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.b)]][epic12_device_colrtable[(s_clr.trgb.b)][(d_clr.trgb.b)]];
+ #elif _DMODE == 2
+ //g_profiler.start(PROFILER_USER3);
+ clr_mul_fixed(&clr0.trgb, s_alpha, &s_clr.trgb);
+ clr_add_with_clr_square(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #elif _DMODE == 3
+ //g_profiler.start(PROFILER_USER4);
+ clr_mul_fixed(&clr0.trgb, s_alpha, &s_clr.trgb);
+ clr_add(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+
+ #elif _DMODE == 4
+ //g_profiler.start(PROFILER_USER5);
+ clr_mul_fixed(&clr0.trgb, s_alpha, &s_clr.trgb);
+ clr_add_with_clr_mul_fixed_rev(&s_clr.trgb, &clr0.trgb, d_alpha, &d_clr.trgb);
+ #elif _DMODE == 5
+ // futari black character select ~13%
+ //g_profiler.start(PROFILER_USER6);
+ s_clr.trgb.r = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.r)]][epic12_device_colrtable_rev[(s_clr.trgb.r)][(d_clr.trgb.r)]];
+ s_clr.trgb.g = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.g)]][epic12_device_colrtable_rev[(s_clr.trgb.g)][(d_clr.trgb.g)]];
+ s_clr.trgb.b = epic12_device_colrtable_add[salpha_table[(s_clr.trgb.b)]][epic12_device_colrtable_rev[(s_clr.trgb.b)][(d_clr.trgb.b)]];
+
+ #elif _DMODE == 6
+ //g_profiler.start(PROFILER_USER7);
+ clr_mul_fixed(&clr0.trgb, s_alpha, &s_clr.trgb);
+ clr_add_with_clr_mul_rev_square(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #elif _DMODE == 7
+ //g_profiler.start(PROFILER_USER8);
+ clr_mul_fixed(&clr0.trgb, s_alpha, &s_clr.trgb);
+ clr_add(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #endif
+
+ //g_profiler.stop();
+ #elif _SMODE == 1
+ //g_profiler.start(PROFILER_USER6);
+ clr_square(&clr0.trgb, &s_clr.trgb);
+
+ #elif _SMODE == 2
+ // g_profiler.start(PROFILER_USER4);
+ #if _DMODE == 0
+ // this is used heavily on espgal2 highscore screen (~28%) optimized to avoid use of temp clr0 variable
+ s_clr.trgb.r = epic12_device_colrtable_add[epic12_device_colrtable[(d_clr.trgb.r)][(s_clr.trgb.r)]][dalpha_table[(d_clr.trgb.r)]];
+ s_clr.trgb.g = epic12_device_colrtable_add[epic12_device_colrtable[(d_clr.trgb.g)][(s_clr.trgb.g)]][dalpha_table[(d_clr.trgb.g)]];
+ s_clr.trgb.b = epic12_device_colrtable_add[epic12_device_colrtable[(d_clr.trgb.b)][(s_clr.trgb.b)]][dalpha_table[(d_clr.trgb.b)]];
+ #elif _DMODE == 1
+ clr_mul_3param(&clr0.trgb, &s_clr.trgb, &d_clr.trgb);
+ clr_add_with_clr_mul_3param(&s_clr.trgb, &clr0.trgb, &d_clr.trgb, &s_clr.trgb);
+ #elif _DMODE == 2
+ clr_mul_3param(&clr0.trgb, &s_clr.trgb, &d_clr.trgb);
+ clr_add_with_clr_square(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #elif _DMODE == 3
+ clr_mul_3param(&clr0.trgb, &s_clr.trgb, &d_clr.trgb);
+ clr_add(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+
+ #elif _DMODE == 4
+ clr_mul_3param(&clr0.trgb, &s_clr.trgb, &d_clr.trgb);
+ clr_add_with_clr_mul_fixed_rev(&s_clr.trgb, &clr0.trgb, d_alpha, &d_clr.trgb);
+ #elif _DMODE == 5
+ clr_mul_3param(&clr0.trgb, &s_clr.trgb, &d_clr.trgb);
+ clr_add_with_clr_mul_rev_3param(&s_clr.trgb, &clr0.trgb, &d_clr.trgb, &s_clr.trgb);
+ #elif _DMODE == 6
+ clr_mul_3param(&clr0.trgb, &s_clr.trgb, &d_clr.trgb);
+ clr_add_with_clr_mul_rev_square(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #elif _DMODE == 7
+ clr_mul_3param(&clr0.trgb, &s_clr.trgb, &d_clr.trgb);
+ clr_add(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #endif
+ //g_profiler.stop();
+
+ #elif _SMODE == 3
+ //g_profiler.start(PROFILER_USER1);
+ clr_copy(&clr0.trgb, &s_clr.trgb);
+
+ #elif _SMODE == 4
+ //g_profiler.start(PROFILER_USER2);
+ clr_mul_fixed_rev(&clr0.trgb, s_alpha, &s_clr.trgb);
+ #elif _SMODE == 5
+ //g_profiler.start(PROFILER_USER3);
+ clr_mul_rev_square(&clr0.trgb, &s_clr.trgb);
+ #elif _SMODE == 6
+ //g_profiler.start(PROFILER_USER4);
+ clr_mul_rev_3param(&clr0.trgb, &s_clr.trgb, &d_clr.trgb);
+ #elif _SMODE == 7
+ //g_profiler.start(PROFILER_USER5);
+ clr_copy(&clr0.trgb, &s_clr.trgb);
+ #endif
+
+
+// smode 0/2 cases are already split up and handled above.
+#if _SMODE != 2
+#if _SMODE != 0
+
+ #if _DMODE == 0
+ clr_add_with_clr_mul_fixed(&s_clr.trgb, &clr0.trgb, d_alpha, &d_clr.trgb);
+ #elif _DMODE == 1
+ clr_add_with_clr_mul_3param(&s_clr.trgb, &clr0.trgb, &d_clr.trgb, &s_clr.trgb);
+ #elif _DMODE == 2
+ clr_add_with_clr_square(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #elif _DMODE == 3
+ clr_add(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+
+ #elif _DMODE == 4
+ clr_add_with_clr_mul_fixed_rev(&s_clr.trgb, &clr0.trgb, d_alpha, &d_clr.trgb);
+ #elif _DMODE == 5
+ clr_add_with_clr_mul_rev_3param(&s_clr.trgb, &clr0.trgb, &d_clr.trgb, &s_clr.trgb);
+ #elif _DMODE == 6
+ clr_add_with_clr_mul_rev_square(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #elif _DMODE == 7
+ clr_add(&s_clr.trgb, &clr0.trgb, &d_clr.trgb);
+ #endif
+
+ //g_profiler.stop();
+#endif
+#endif
+
+
+ #endif
+
+ // write result
+ *bmp = clr_to_pen(&s_clr.trgb)|(pen&0x20000000);
+ //*bmp = (s_clr.u32<<3)|(pen&0x20000000); // using the union is actually significantly slower than our clr_to_pen function!
+
+#endif // END NOT REALLY SIMPLE
+
+#if TRANSPARENT == 1
+ }
+#endif
+ LOOP_INCREMENTS
diff --git a/src/devices/video/fixfreq.c b/src/devices/video/fixfreq.c
new file mode 100644
index 00000000000..033bcb5b301
--- /dev/null
+++ b/src/devices/video/fixfreq.c
@@ -0,0 +1,304 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+/***************************************************************************
+
+ fixfreq.h
+
+ 2013 Couriersud
+
+ Fixed frequency monochrome monitor emulation
+
+ The driver is intended for drivers which provide an analog video signal.
+ VSYNC and HSYNC levels are used to create the bitmap.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "fixfreq.h"
+
+/***************************************************************************
+
+ Local variables
+
+***************************************************************************/
+
+//#define VERBOSE_OUT(x) printf x
+#define VERBOSE_OUT(x)
+
+/***************************************************************************
+
+ Fixed frequency monitor
+
+***************************************************************************/
+// device type definition
+const device_type FIXFREQ = &device_creator<fixedfreq_device>;
+
+fixedfreq_device::fixedfreq_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_video_interface(mconfig, *this, false),
+ // default to NTSC "704x480@30i"
+ m_monitor_clock(13500000),
+ m_hvisible(704),
+ m_hfrontporch(728),
+ m_hsync(791),
+ m_hbackporch(858),
+ m_vvisible(480),
+ m_vfrontporch(486),
+ m_vsync(492),
+ m_vbackporch(525),
+ m_fieldcount(2),
+ m_sync_threshold(0.3),
+ m_gain(1.0 / 3.7)
+{
+}
+
+fixedfreq_device::fixedfreq_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, FIXFREQ, "Fixed Frequency Monochrome Monitor", tag, owner, clock, "fixfreq", __FILE__),
+ device_video_interface(mconfig, *this, false),
+ // default to NTSC "704x480@30i"
+ m_monitor_clock(13500000),
+ m_hvisible(704),
+ m_hfrontporch(728),
+ m_hsync(791),
+ m_hbackporch(858),
+ m_vvisible(480),
+ m_vfrontporch(486),
+ m_vsync(492),
+ m_vbackporch(525),
+ m_fieldcount(2),
+ m_sync_threshold(0.3),
+ m_gain(1.0 / 3.7)
+{
+}
+
+void fixedfreq_device::device_start()
+{
+ m_htotal = 0;
+ m_vtotal = 0;
+
+ m_vid = 0.0;
+ m_last_x = 0;
+ m_last_y = 0;
+ m_last_time = attotime::zero;
+ m_line_time = attotime::zero;
+ m_last_hsync_time = attotime::zero;
+ m_last_vsync_time = attotime::zero;
+ m_refresh = attotime::zero;
+ m_clock_period = attotime::zero;
+ //bitmap_rgb32 *m_bitmap[2];
+ m_cur_bm = 0;
+
+ /* sync separator */
+ m_vint = 0.0;
+ m_int_trig = 0.0;
+ m_mult = 0.0;
+
+ m_sig_vsync = 0;
+ m_sig_composite = 0;
+ m_sig_field = 0;
+
+ m_bitmap[0] = NULL;
+ m_bitmap[1] = NULL;
+ //m_vblank_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(vga_device::vblank_timer_cb),this));
+ recompute_parameters(false);
+
+ save_item(NAME(m_vid));
+ save_item(NAME(m_last_x));
+ save_item(NAME(m_last_y));
+ save_item(NAME(m_last_time));
+ save_item(NAME(m_line_time));
+ save_item(NAME(m_last_hsync_time));
+ save_item(NAME(m_last_vsync_time));
+ save_item(NAME(m_refresh));
+ save_item(NAME(m_clock_period));
+ //save_item(NAME(m_bitmap[0]));
+ //save_item(NAME(m_bitmap[1]));
+ save_item(NAME(m_cur_bm));
+
+ /* sync separator */
+ save_item(NAME(m_vint));
+ save_item(NAME(m_int_trig));
+ save_item(NAME(m_mult));
+
+ save_item(NAME(m_sig_vsync));
+ save_item(NAME(m_sig_composite));
+ save_item(NAME(m_sig_field));
+
+
+
+}
+
+void fixedfreq_device::device_reset()
+{
+ m_last_time = attotime::zero;
+ m_line_time = attotime::zero;
+ m_last_hsync_time = attotime::zero;
+ m_last_vsync_time = attotime::zero;
+ m_vint = 0;
+
+}
+
+
+void fixedfreq_device::device_post_load()
+{
+ //recompute_parameters(true);
+}
+
+void fixedfreq_device::recompute_parameters(bool postload)
+{
+ bool needs_realloc = (m_htotal != m_hbackporch) && (m_vtotal != m_vbackporch);
+
+ if (m_bitmap[0] != NULL || needs_realloc)
+ auto_free(machine(), m_bitmap[0]);
+ if (m_bitmap[1] != NULL || needs_realloc)
+ auto_free(machine(), m_bitmap[0]);
+
+ m_htotal = m_hbackporch;
+ m_vtotal = m_vbackporch;
+
+ /* sync separator */
+
+ m_int_trig = (exp(- 3.0/(3.0+3.0))) - exp(-1.0);
+ m_mult = (double) (m_monitor_clock) / (double) m_htotal * 1.0; // / (3.0 + 3.0);
+ VERBOSE_OUT(("trigger %f with len %f\n", m_int_trig, 1e6 / m_mult));
+
+ m_bitmap[0] = auto_bitmap_rgb32_alloc(machine(),m_htotal, m_vtotal);
+ m_bitmap[1] = auto_bitmap_rgb32_alloc(machine(),m_htotal, m_vtotal);
+
+ rectangle visarea(
+ m_hbackporch - m_hfrontporch,
+ m_hbackporch - m_hfrontporch + m_hvisible - 1,
+ m_vbackporch - m_vfrontporch,
+ m_vbackporch - m_vfrontporch + m_vvisible - 1);
+
+ m_clock_period = attotime::from_hz(m_monitor_clock);
+
+ m_refresh = attotime::from_hz(m_monitor_clock) * m_vtotal * m_htotal;
+ screen().configure(m_htotal, m_vtotal, visarea, m_refresh.as_attoseconds());
+}
+
+void fixedfreq_device::update_screen_parameters(const attotime &refresh)
+{
+ rectangle visarea(
+// m_hsync - m_hvisible,
+// m_hsync - 1 ,
+ m_hbackporch - m_hfrontporch,
+ m_hbackporch - m_hfrontporch + m_hvisible - 1,
+ m_vbackporch - m_vfrontporch,
+ m_vbackporch - m_vfrontporch + m_vvisible - 1);
+
+ m_refresh = refresh;
+ screen().configure(m_htotal, m_vtotal, visarea, m_refresh.as_attoseconds());
+}
+
+int fixedfreq_device::sync_separator(const attotime &time, double newval)
+{
+ int last_vsync = m_sig_vsync;
+ int last_comp = m_sig_composite;
+ int ret = 0;
+
+ m_vint += ((double) last_comp - m_vint) * (1.0 - exp(-time.as_double() * m_mult));
+ m_sig_composite = (newval < m_sync_threshold) ? 1 : 0 ;
+
+ m_sig_vsync = (m_vint > m_int_trig) ? 1 : 0;
+
+ if (!last_vsync && m_sig_vsync)
+ {
+ /* TODO - time since last hsync and field detection */
+ ret |= 1;
+ }
+ if (last_vsync && !m_sig_vsync)
+ {
+ m_sig_field = last_comp; /* force false-progressive */
+ m_sig_field = (m_sig_field ^ 1) ^ last_comp; /* if there is no field switch, auto switch */
+ VERBOSE_OUT(("Field: %d\n", m_sig_field));
+ }
+ if (!last_comp && m_sig_composite)
+ {
+ /* TODO - time since last hsync and field detection */
+ ret |= 2;
+ }
+ if (last_comp && !m_sig_composite)
+ {
+ /* falling composite */
+ ret |= 4;
+ }
+ return ret;
+}
+
+UINT32 fixedfreq_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, *m_bitmap[!m_cur_bm], 0, 0, 0, 0, cliprect);
+
+ return 0;
+}
+
+NETDEV_ANALOG_CALLBACK_MEMBER(fixedfreq_device::update_vid)
+{
+ bitmap_rgb32 *bm = m_bitmap[m_cur_bm];
+ const int has_fields = (m_fieldcount > 1) ? 1: 0;
+
+ int pixels = round((time - m_line_time).as_double() / m_clock_period.as_double());
+ attotime delta_time = (time - m_last_time);
+
+ if (data == m_vid)
+ return;
+
+ ATTR_UNUSED int sync = sync_separator(delta_time, data);
+
+ if (m_last_y < bm->height())
+ {
+ rgb_t col;
+
+ if (m_vid < m_sync_threshold)
+ // Mark sync areas
+ col = rgb_t(255, 0, 0);
+ else
+ {
+ int colv = (int) ((m_vid - m_sync_threshold) * m_gain * 255.0);
+ if (colv > 255)
+ colv = 255;
+ col = rgb_t(colv, colv, colv);
+ }
+
+ bm->plot_box(m_last_x, m_last_y + m_sig_field * has_fields, pixels - m_last_x, 1, col);
+ m_last_x = pixels;
+ }
+ if (sync & 1)
+ {
+ VERBOSE_OUT(("VSYNC %d %d\n", pixels, m_last_y + m_sig_field));
+ }
+ if (sync & 2)
+ {
+ VERBOSE_OUT(("HSYNC up %d\n", pixels));
+ //if (m_last_y == 27) printf("HSYNC up %d %d\n", m_last_y, pixels);
+ }
+ if (sync & 4)
+ {
+ VERBOSE_OUT(("HSYNC down %f %d %f\n", time.as_double()* 1e6, pixels, m_vid));
+ }
+
+ if (sync & 1)
+ {
+ m_last_y = m_vbackporch - m_vsync;
+ // toggle bitmap
+ m_cur_bm ^= 1;
+ update_screen_parameters(time - m_last_vsync_time);
+ m_last_vsync_time = time;
+ }
+
+ // FIXME: pixels > 50 filters some spurious hysnc on line 27 in breakout
+ if ((sync & 2) && !m_sig_vsync && (pixels > 100))
+ {
+ m_last_y += m_fieldcount;
+ m_last_x = 0;
+ m_line_time = time;
+ }
+
+ m_last_time = time;
+ m_vid = data;
+
+}
+
+
+/***************************************************************************/
diff --git a/src/devices/video/fixfreq.h b/src/devices/video/fixfreq.h
new file mode 100644
index 00000000000..833829fbc3b
--- /dev/null
+++ b/src/devices/video/fixfreq.h
@@ -0,0 +1,163 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+/***************************************************************************
+
+ fixfreq.h
+
+ Fixed frequency monochrome monitor emulation
+
+ The driver is intended for drivers which provide an analog video signal.
+ VSYNC and HSYNC levels are used to create the bitmap.
+
+***************************************************************************/
+
+#ifndef FIXFREQ_H
+#define FIXFREQ_H
+
+#include "emu.h"
+#include "machine/netlist.h"
+
+#define FIXFREQ_INTERFACE(name) \
+ const fixedfreq_interface (name) =
+
+#define MCFG_FIXFREQ_ADD(_tag, _screen_tag) \
+ MCFG_SCREEN_ADD(_screen_tag, RASTER) \
+ MCFG_SCREEN_RAW_PARAMS(13500000, 858, 0, 858, 525, 0, 525) \
+ MCFG_SCREEN_UPDATE_DEVICE(_tag, fixedfreq_device, screen_update) \
+ MCFG_DEVICE_ADD(_tag, FIXFREQ, 0) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag)
+
+#define MCFG_FIXFREQ_MONITOR_CLOCK(_clock) \
+ fixedfreq_device::set_minitor_clock(*device, _clock);
+
+#define MCFG_FIXFREQ_HORZ_PARAMS(_visible, _frontporch, _sync, _backporch) \
+ fixedfreq_device::set_horz_params(*device, _visible, _frontporch, _sync, _backporch);
+
+#define MCFG_FIXFREQ_VERT_PARAMS(_visible, _frontporch, _sync, _backporch) \
+ fixedfreq_device::set_vert_params(*device, _visible, _frontporch, _sync, _backporch);
+
+#define MCFG_FIXFREQ_FIELDCOUNT(_count) \
+ fixedfreq_device::set_fieldcount(*device, _count);
+
+#define MCFG_FIXFREQ_SYNC_THRESHOLD(_threshold) \
+ fixedfreq_device::set_threshold(*device, _threshold);
+
+#define MCFG_FIXFREQ_GAIN(_gain) \
+ fixedfreq_device::set_gain(*device, _gain);
+
+// pre-defined configurations
+
+//ModeLine "720x480@30i" 13.5 720 736 799 858 480 486 492 525 interlace -hsync -vsync
+#define MCFG_FIXFREQ_MODE_NTSC720 \
+ MCFG_FIXFREQ_MONITOR_CLOCK(13500000) \
+ MCFG_FIXFREQ_HORZ_PARAMS(720, 736, 799, 858) \
+ MCFG_FIXFREQ_VERT_PARAMS(480, 486, 492, 525) \
+ MCFG_FIXFREQ_FIELDCOUNT(2) \
+ MCFG_FIXFREQ_SYNC_THRESHOLD(0.3)
+
+//ModeLine "704x480@30i" 13.5 704 728 791 858 480 486 492 525
+#define MCFG_FIXFREQ_MODE_NTSC704 \
+ MCFG_FIXFREQ_MONITOR_CLOCK(13500000) \
+ MCFG_FIXFREQ_HORZ_PARAMS(704, 728, 791, 858) \
+ MCFG_FIXFREQ_VERT_PARAMS(480, 486, 492, 525) \
+ MCFG_FIXFREQ_FIELDCOUNT(2) \
+ MCFG_FIXFREQ_SYNC_THRESHOLD(0.3)
+
+
+// ======================> vga_device
+
+class fixedfreq_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ fixedfreq_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ fixedfreq_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ // inline configuration helpers
+ static void set_minitor_clock(device_t &device, UINT32 clock) { downcast<fixedfreq_device &>(device).m_monitor_clock = clock; }
+ static void set_fieldcount(device_t &device, int count) { downcast<fixedfreq_device &>(device).m_fieldcount = count; }
+ static void set_threshold(device_t &device, double threshold) { downcast<fixedfreq_device &>(device).m_sync_threshold = threshold; }
+ static void set_gain(device_t &device, double gain) { downcast<fixedfreq_device &>(device).m_gain = gain; }
+ static void set_horz_params(device_t &device, int visible, int frontporch, int sync, int backporch)
+ {
+ fixedfreq_device &dev = downcast<fixedfreq_device &>(device);
+ dev.m_hvisible = visible;
+ dev.m_hfrontporch = frontporch;
+ dev.m_hsync = sync;
+ dev.m_hbackporch = backporch;
+ }
+ static void set_vert_params(device_t &device, int visible, int frontporch, int sync, int backporch)
+ {
+ fixedfreq_device &dev = downcast<fixedfreq_device &>(device);
+ dev.m_vvisible = visible;
+ dev.m_vfrontporch = frontporch;
+ dev.m_vsync = sync;
+ dev.m_vbackporch = backporch;
+ }
+
+ virtual UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ NETDEV_ANALOG_CALLBACK_MEMBER(update_vid);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+ //virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ void recompute_parameters(bool postload);
+ void update_screen_parameters(const attotime &refresh);
+
+private:
+
+ int sync_separator(const attotime &time, double newval);
+
+ int m_htotal;
+ int m_vtotal;
+
+ double m_vid;
+ int m_last_x;
+ int m_last_y;
+ attotime m_last_time;
+ attotime m_line_time;
+ attotime m_last_hsync_time;
+ attotime m_last_vsync_time;
+ attotime m_refresh;
+ attotime m_clock_period;
+ bitmap_rgb32 *m_bitmap[2];
+ int m_cur_bm;
+
+ /* adjustable by drivers */
+ UINT32 m_monitor_clock;
+ int m_hvisible;
+ int m_hfrontporch;
+ int m_hsync;
+ int m_hbackporch;
+ int m_vvisible;
+ int m_vfrontporch;
+ int m_vsync;
+ int m_vbackporch;
+ int m_fieldcount;
+ double m_sync_threshold;
+ double m_gain;
+
+ /* sync separator */
+ double m_vint;
+ double m_int_trig;
+ double m_mult;
+
+ int m_sig_vsync;
+ int m_sig_composite;
+ int m_sig_field;
+
+protected:
+
+};
+
+
+// device type definition
+extern const device_type FIXFREQ;
+
+#endif /* FIXFREQ_H */
diff --git a/src/devices/video/generic.c b/src/devices/video/generic.c
new file mode 100644
index 00000000000..e9356e05037
--- /dev/null
+++ b/src/devices/video/generic.c
@@ -0,0 +1,94 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+/*********************************************************************
+
+ generic.c
+
+ Generic simple video functions.
+
+*********************************************************************/
+
+#include "emu.h"
+
+
+
+/***************************************************************************
+ COMMON GRAPHICS LAYOUTS
+***************************************************************************/
+
+const gfx_layout gfx_8x8x1 =
+{
+ 8,8,
+ RGN_FRAC(1,1),
+ 1,
+ { RGN_FRAC(0,1) },
+ { STEP8(0,1) },
+ { STEP8(0,8) },
+ 8*8
+};
+
+const gfx_layout gfx_8x8x2_planar =
+{
+ 8,8,
+ RGN_FRAC(1,2),
+ 2,
+ { RGN_FRAC(1,2), RGN_FRAC(0,2) },
+ { STEP8(0,1) },
+ { STEP8(0,8) },
+ 8*8
+};
+
+const gfx_layout gfx_8x8x3_planar =
+{
+ 8,8,
+ RGN_FRAC(1,3),
+ 3,
+ { RGN_FRAC(2,3), RGN_FRAC(1,3), RGN_FRAC(0,3) },
+ { STEP8(0,1) },
+ { STEP8(0,8) },
+ 8*8
+};
+
+const gfx_layout gfx_8x8x4_planar =
+{
+ 8,8,
+ RGN_FRAC(1,4),
+ 4,
+ { RGN_FRAC(3,4), RGN_FRAC(2,4), RGN_FRAC(1,4), RGN_FRAC(0,4) },
+ { STEP8(0,1) },
+ { STEP8(0,8) },
+ 8*8
+};
+
+const gfx_layout gfx_8x8x5_planar =
+{
+ 8,8,
+ RGN_FRAC(1,5),
+ 5,
+ { RGN_FRAC(4,5), RGN_FRAC(3,5), RGN_FRAC(2,5), RGN_FRAC(1,5), RGN_FRAC(0,5) },
+ { STEP8(0,1) },
+ { STEP8(0,8) },
+ 8*8
+};
+
+const gfx_layout gfx_8x8x6_planar =
+{
+ 8,8,
+ RGN_FRAC(1,6),
+ 6,
+ { RGN_FRAC(5,6), RGN_FRAC(4,6), RGN_FRAC(3,6), RGN_FRAC(2,6), RGN_FRAC(1,6), RGN_FRAC(0,6) },
+ { STEP8(0,1) },
+ { STEP8(0,8) },
+ 8*8
+};
+
+const gfx_layout gfx_16x16x4_planar =
+{
+ 16,16,
+ RGN_FRAC(1,4),
+ 4,
+ { RGN_FRAC(3,4), RGN_FRAC(2,4), RGN_FRAC(1,4), RGN_FRAC(0,4) },
+ { STEP16(0,1) },
+ { STEP16(0,16) },
+ 16*16
+};
diff --git a/src/devices/video/generic.h b/src/devices/video/generic.h
new file mode 100644
index 00000000000..5f2ef27966c
--- /dev/null
+++ b/src/devices/video/generic.h
@@ -0,0 +1,31 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+/*********************************************************************
+
+ generic.h
+
+ Generic simple video functions.
+
+*********************************************************************/
+
+#pragma once
+
+#ifndef __VIDEO_GENERIC_H__
+#define __VIDEO_GENERIC_H__
+
+
+
+/***************************************************************************
+ COMMON GRAPHICS LAYOUTS
+***************************************************************************/
+
+extern const gfx_layout gfx_8x8x1;
+extern const gfx_layout gfx_8x8x2_planar;
+extern const gfx_layout gfx_8x8x3_planar;
+extern const gfx_layout gfx_8x8x4_planar;
+extern const gfx_layout gfx_8x8x5_planar;
+extern const gfx_layout gfx_8x8x6_planar;
+
+extern const gfx_layout gfx_16x16x4_planar;
+
+#endif /* __VIDEO_GENERIC_H__ */
diff --git a/src/devices/video/gf4500.c b/src/devices/video/gf4500.c
new file mode 100644
index 00000000000..48e2f8495c3
--- /dev/null
+++ b/src/devices/video/gf4500.c
@@ -0,0 +1,184 @@
+// license:BSD-3-Clause
+// copyright-holders:Tim Schuerewegen
+/*
+
+ NVIDIA GoForce 4500
+
+ (c) 2010 Tim Schuerewegen
+
+*/
+
+#include "emu.h"
+#include "video/gf4500.h"
+
+#define VERBOSE_LEVEL ( 0 )
+
+INLINE void ATTR_PRINTF(3,4) verboselog( running_machine &machine, int n_level, const char *s_fmt, ...)
+{
+ if (VERBOSE_LEVEL >= n_level)
+ {
+ va_list v;
+ char buf[32768];
+ va_start(v, s_fmt);
+ vsprintf(buf, s_fmt, v);
+ va_end(v);
+ logerror("%s: %s", machine.describe_context(), buf);
+ }
+}
+
+#define BIT(x,n) (((x)>>(n))&1)
+#define BITS(x,m,n) (((x)>>(n))&(((UINT32)1<<((m)-(n)+1))-1))
+
+#define GF4500_FRAMEBUF_OFFSET 0x20000
+
+
+const device_type GF4500 = &device_creator<gf4500_device>;
+
+
+gf4500_device::gf4500_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, GF4500, "NVIDIA GoForce 4500", tag, owner, clock, "gf4500", __FILE__)
+{
+}
+
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void gf4500_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void gf4500_device::device_start()
+{
+ m_data = auto_alloc_array_clear(machine(), UINT32, 0x140000/4);
+
+ save_pointer(NAME(m_data), 0x140000/4);
+ save_item(NAME(m_screen_x));
+ save_item(NAME(m_screen_y));
+ save_item(NAME(m_screen_x_max));
+ save_item(NAME(m_screen_y_max));
+ save_item(NAME(m_screen_x_min));
+ save_item(NAME(m_screen_y_min));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void gf4500_device::device_reset()
+{
+ m_screen_x = m_screen_y = 0;
+ m_screen_x_max = m_screen_y_max = m_screen_x_min = m_screen_y_min = 0;
+}
+
+
+void gf4500_device::vram_write16( UINT16 data )
+{
+ if ((m_screen_x < m_screen_x_max) && (m_screen_y < m_screen_y_max))
+ {
+ UINT16 *vram = (UINT16 *)((UINT8 *)m_data + GF4500_FRAMEBUF_OFFSET + (((m_screen_y_min + m_screen_y) * (320 + 1)) + (m_screen_x_min + m_screen_x)) * 2);
+ *vram = data;
+ m_screen_x++;
+ }
+}
+
+static rgb_t gf4500_get_color_16( UINT16 data )
+{
+ UINT8 r, g, b;
+ r = BITS(data, 15, 11) << 3;
+ g = BITS(data, 10, 5) << 2;
+ b = BITS(data, 4, 0) << 3;
+ return rgb_t(r, g, b);
+}
+
+UINT32 gf4500_device::screen_update(screen_device &device, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT16 *vram = (UINT16 *)(m_data + GF4500_FRAMEBUF_OFFSET / 4);
+ int x, y;
+ for (y = 0; y < 240; y++)
+ {
+ UINT32 *scanline = &bitmap.pix32(y);
+ for (x = 0; x < 320; x++)
+ {
+ *scanline++ = gf4500_get_color_16(*vram++);
+ }
+ vram += 1;
+ }
+ return 0;
+}
+
+READ32_MEMBER( gf4500_device::read )
+{
+ UINT32 data = m_data[offset];
+ switch (offset)
+ {
+ case 0x4c / 4:
+ data = 0x00145000;
+ break;
+ }
+ if ((offset < (GF4500_FRAMEBUF_OFFSET / 4)) || (offset >= ((GF4500_FRAMEBUF_OFFSET + (321 * 240 * 2)) / 4)))
+ {
+ verboselog(machine(), 9, "(GFO) %08X -> %08X\n", 0x34000000 + (offset << 2), data);
+ }
+ return data;
+}
+
+WRITE32_MEMBER( gf4500_device::write )
+{
+ COMBINE_DATA(&m_data[offset]);
+ if ((offset < (GF4500_FRAMEBUF_OFFSET / 4)) || (offset >= ((GF4500_FRAMEBUF_OFFSET + (321 * 240 * 2)) / 4)))
+ {
+ verboselog(machine(), 9, "(GFO) %08X <- %08X\n", 0x34000000 + (offset << 2), data);
+ }
+ switch (offset)
+ {
+ case 0x300 / 4 :
+ m_screen_x = m_screen_y = 0;
+ break;
+ case 0x304 / 4 :
+ m_screen_x_max = (data >> 0) & 0xFFFF;
+ m_screen_y_max = (data >> 16) & 0xFFFF;
+ if (m_screen_x_max & 1) m_screen_x_min++;
+ //if (screen_y_max & 1) screen_y_min++;
+ break;
+ case 0x308 / 4 :
+ m_screen_x_min = (data >> 0) & 0xFFFF;
+ m_screen_y_min = (data >> 16) & 0xFFFF;
+ if (m_screen_x_min & 1) m_screen_x_min--;
+ //if (screen_y_min & 1) screen_y_min--;
+ break;
+ }
+ if ((offset >= (0x200 / 4)) && (offset < (0x280 / 4)))
+ {
+// 'maincpu' (02996998): (GFO) 34000304 <- 00F00140
+// 'maincpu' (029969A8): (GFO) 34000308 <- 00000000
+// 'maincpu' (029969B4): (GFO) 34000324 <- 00000000
+// 'maincpu' (029969C4): (GFO) 34000328 <- 40000282
+// 'maincpu' (029969D4): (GFO) 34000300 <- 001022CC
+//
+// 'maincpu' (01DCC55C): (GFO) 34000024 -> 00000000
+// 'maincpu' (02996A24): (GFO) 34000200 <- AE9FAE9F
+//
+// 'maincpu' (02996A24): (GFO) 3400027C <- AE9FAE9F
+//
+// 'maincpu' (01DCC55C): (GFO) 34000024 -> 00000000
+// 'maincpu' (02996A24): (GFO) 34000200 <- AE9FAE9F
+// ...
+// 'maincpu' (02996A24): (GFO) 3400027C <- AE9FAE9F
+
+ vram_write16((data >> 0) & 0xFFFF);
+ vram_write16((data >> 16) & 0xFFFF);
+ if (m_screen_x >= m_screen_x_max)
+ {
+ m_screen_x = 0;
+ m_screen_y++;
+ }
+ }
+}
diff --git a/src/devices/video/gf4500.h b/src/devices/video/gf4500.h
new file mode 100644
index 00000000000..eb246ec0ea9
--- /dev/null
+++ b/src/devices/video/gf4500.h
@@ -0,0 +1,56 @@
+// license:BSD-3-Clause
+// copyright-holders:Tim Schuerewegen
+/*
+
+ NVIDIA GoForce 4500
+
+ (c) 2010 Tim Schuerewegen
+
+*/
+
+#ifndef __GF4500_H__
+#define __GF4500_H__
+
+
+class gf4500_device : public device_t
+{
+public:
+ gf4500_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~gf4500_device() {}
+
+
+ DECLARE_READ32_MEMBER( read );
+ DECLARE_WRITE32_MEMBER( write );
+
+ UINT32 screen_update(screen_device &device, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ // internal state
+
+ void vram_write16(UINT16 data);
+
+ UINT32 *m_data;
+ int m_screen_x;
+ int m_screen_y;
+ int m_screen_x_max;
+ int m_screen_y_max;
+ int m_screen_x_min;
+ int m_screen_y_min;
+};
+
+
+
+#define MCFG_GF4500_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, GF4500, 0)
+
+
+extern const device_type GF4500;
+
+
+#endif /* __GF4500_H__ */
diff --git a/src/devices/video/gf7600gs.c b/src/devices/video/gf7600gs.c
new file mode 100644
index 00000000000..85b6b2f06da
--- /dev/null
+++ b/src/devices/video/gf7600gs.c
@@ -0,0 +1,33 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#include "gf7600gs.h"
+
+const device_type GEFORCE_7600GS = &device_creator<geforce_7600gs_device>;
+
+DEVICE_ADDRESS_MAP_START(map1, 32, geforce_7600gs_device)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map2, 32, geforce_7600gs_device)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map3, 32, geforce_7600gs_device)
+ADDRESS_MAP_END
+
+geforce_7600gs_device::geforce_7600gs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : pci_device(mconfig, GEFORCE_7600GS, "NVidia GeForce 7600GS", tag, owner, clock, "geforce_7600gs", __FILE__)
+{
+}
+
+void geforce_7600gs_device::device_start()
+{
+ pci_device::device_start();
+ add_map( 16*1024*1024, M_MEM, FUNC(geforce_7600gs_device::map1));
+ add_map(256*1024*1024, M_MEM, FUNC(geforce_7600gs_device::map2));
+ add_map( 16*1024*1024, M_MEM, FUNC(geforce_7600gs_device::map3));
+ add_rom_from_region();
+}
+
+void geforce_7600gs_device::device_reset()
+{
+ pci_device::device_reset();
+}
diff --git a/src/devices/video/gf7600gs.h b/src/devices/video/gf7600gs.h
new file mode 100644
index 00000000000..1d336ee5454
--- /dev/null
+++ b/src/devices/video/gf7600gs.h
@@ -0,0 +1,27 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#ifndef GF7600GS_H
+#define GF7600GS_H
+
+#include "machine/pci.h"
+
+#define MCFG_GEFORCE_7600GS_ADD(_tag, _subdevice_id) \
+ MCFG_AGP_DEVICE_ADD(_tag, GEFORCE_7600GS, 0x10de02e1, 0xa1, _subdevice_id)
+
+class geforce_7600gs_device : public pci_device {
+public:
+ geforce_7600gs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ DECLARE_ADDRESS_MAP(map1, 32);
+ DECLARE_ADDRESS_MAP(map2, 32);
+ DECLARE_ADDRESS_MAP(map3, 32);
+};
+
+extern const device_type GEFORCE_7600GS;
+
+#endif
diff --git a/src/devices/video/h63484.c b/src/devices/video/h63484.c
new file mode 100644
index 00000000000..906c01573e4
--- /dev/null
+++ b/src/devices/video/h63484.c
@@ -0,0 +1,2190 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese, Sandro Ronco
+/***************************************************************************
+
+ HD63484 ACRTC (rewrite in progress)
+
+ TODO:
+ - 8-bit support for FIFO, parameters and command values
+ - execution cycles;
+
+***************************************************************************/
+
+#include "emu.h"
+#include "h63484.h"
+
+#define LOG 0
+#define FIFO_LOG 0
+#define CMD_LOG 0
+
+// default address map
+static ADDRESS_MAP_START( h63484_vram, AS_0, 16, h63484_device )
+// AM_RANGE(0x00000, 0x7ffff) AM_RAM
+// AM_RANGE(0x80000, 0xfffff) AM_NOP
+ADDRESS_MAP_END
+
+
+//-------------------------------------------------
+// h63484_device - constructor
+//-------------------------------------------------
+
+h63484_device::h63484_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, H63484, "HD63484 CRTC (rewrite)", tag, owner, clock, "h63484", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_ar(0),
+ m_sr(0),
+ m_fifo_ptr(-1),
+ m_fifo_r_ptr(-1),
+ m_cr(0),
+ m_param_ptr(0),
+ m_rwp_dn(0),
+ m_org_dpa(0),
+ m_org_dn(0),
+ m_org_dpd(0),
+ m_cl0(0),
+ m_cl1(0),
+ m_dcr(0),
+ m_space_config("videoram", ENDIANNESS_BIG, 16, 20, -1, NULL, *ADDRESS_MAP_NAME(h63484_vram))
+{
+}
+
+enum
+{
+ FIFO_READ = 0,
+ FIFO_WRITE
+};
+
+enum
+{
+ FIFO_EMPTY = -1,
+ FIFO_PARAMETER,
+ FIFO_COMMAND
+};
+
+#define H63484_SR_CER 0x80 // Command Error
+#define H63484_SR_ARD 0x40 // Area Detect
+#define H63484_SR_CED 0x20 // Command End
+#define H63484_SR_LPD 0x10 // Light Pen Strobe Detect
+#define H63484_SR_RFF 0x08 // Read FIFO Full
+#define H63484_SR_RFR 0x04 // Read FIFO Ready
+#define H63484_SR_WFR 0x02 // Write FIFO Ready
+#define H63484_SR_WFE 0x01 // Write FIFO Empty
+
+
+static const char *const acrtc_regnames[0x100/2] =
+{
+ "FIFO Entry", // 0x00
+ "Command Control (CCR)", // 0x02
+ "Operation Mode (OMR)", // 0x04
+ "Display Control (DCR)", // 0x06
+ "(Undefined)", // 0x08
+ "(Undefined)", // 0x0a
+ "(Undefined)", // 0x0c
+ "(Undefined)", // 0x0e
+ "(Undefined)", // 0x10
+ "(Undefined)", // 0x12
+ "(Undefined)", // 0x14
+ "(Undefined)", // 0x16
+ "(Undefined)", // 0x18
+ "(Undefined)", // 0x1a
+ "(Undefined)", // 0x1c
+ "(Undefined)", // 0x1e
+ "(Undefined)", // 0x20
+ "(Undefined)", // 0x22
+ "(Undefined)", // 0x24
+ "(Undefined)", // 0x26
+ "(Undefined)", // 0x28
+ "(Undefined)", // 0x2a
+ "(Undefined)", // 0x2c
+ "(Undefined)", // 0x2e
+ "(Undefined)", // 0x30
+ "(Undefined)", // 0x32
+ "(Undefined)", // 0x34
+ "(Undefined)", // 0x36
+ "(Undefined)", // 0x38
+ "(Undefined)", // 0x3a
+ "(Undefined)", // 0x3c
+ "(Undefined)", // 0x3e
+ "(Undefined)", // 0x40
+ "(Undefined)", // 0x42
+ "(Undefined)", // 0x44
+ "(Undefined)", // 0x46
+ "(Undefined)", // 0x48
+ "(Undefined)", // 0x4a
+ "(Undefined)", // 0x4c
+ "(Undefined)", // 0x4e
+ "(Undefined)", // 0x50
+ "(Undefined)", // 0x52
+ "(Undefined)", // 0x54
+ "(Undefined)", // 0x56
+ "(Undefined)", // 0x58
+ "(Undefined)", // 0x5a
+ "(Undefined)", // 0x5c
+ "(Undefined)", // 0x5e
+ "(Undefined)", // 0x60
+ "(Undefined)", // 0x62
+ "(Undefined)", // 0x64
+ "(Undefined)", // 0x66
+ "(Undefined)", // 0x68
+ "(Undefined)", // 0x6a
+ "(Undefined)", // 0x6c
+ "(Undefined)", // 0x6e
+ "(Undefined)", // 0x70
+ "(Undefined)", // 0x72
+ "(Undefined)", // 0x74
+ "(Undefined)", // 0x76
+ "(Undefined)", // 0x78
+ "(Undefined)", // 0x7a
+ "(Undefined)", // 0x7c
+ "(Undefined)", // 0x7e
+ "Raster Count (RCR)", // 0x80
+ "Horizontal Sync (HSR)", // 0x82
+ "Horizontal Display (HDR)", // 0x84
+ "Vertical Sync (VSR)", // 0x86
+ "Vertical Display (VDR)", // 0x88
+ "Split Screen Width (SSW) 0x8a", // 0x8a
+ "Split Screen Width (SSW) 0x8c", // 0x8c
+ "Split Screen Width (SSW) 0x8e", // 0x8e
+ "Blink Control (BCR)", // 0x90
+ "Horizontal Window Display (HWR)", // 0x92
+ "Vertical Window Display (VWR) 0x94", // 0x94
+ "Vertical Window Display (VWR) 0x96", // 0x96
+ "Graphic Cursor (GCR0)", // 0x98
+ "Graphic Cursor (GCR1)", // 0x9a
+ "Graphic Cursor (GCR2)", // 0x9c
+ "(Undefined)", // 0x9e
+ "(Undefined)", // 0xa0
+ "(Undefined)", // 0xa2
+ "(Undefined)", // 0xa4
+ "(Undefined)", // 0xa6
+ "(Undefined)", // 0xa8
+ "(Undefined)", // 0xaa
+ "(Undefined)", // 0xac
+ "(Undefined)", // 0xae
+ "(Undefined)", // 0xb0
+ "(Undefined)", // 0xb2
+ "(Undefined)", // 0xb4
+ "(Undefined)", // 0xb6
+ "(Undefined)", // 0xb8
+ "(Undefined)", // 0xba
+ "(Undefined)", // 0xbc
+ "(Undefined)", // 0xbe
+ // upper screen
+ "Raster Address 0 (RAR0)", // 0xc0
+ "Memory Width 0 (MWR0)", // 0xc2
+ "Start Address 0 (SAR0) 0xc4", // 0xc4
+ "Start Address 0 (SAR0) 0xc6", // 0xc6
+ // base screen
+ "Raster Address 1 (RAR1)", // 0xc8
+ "Memory Width 1 (MWR1)", // 0xca
+ "Start Address 1 (SAR1) 0xcc", // 0xcc
+ "Start Address 1 (SAR1) 0xce", // 0xce
+ // lower screen
+ "Raster Address 2 (RAR2)", // 0xd0
+ "Memory Width 2 (MWR2)", // 0xd2
+ "Start Address 2 (SAR2) 0xd4", // 0xd4
+ "Start Address 2 (SAR2) 0xd6", // 0xd6
+ // window screen
+ "Raster Address 3 (RAR3)", // 0xd8
+ "Memory Width 3 (MWR3)", // 0xda
+ "Start Address 3 (SAR3) 0xdc", // 0xdc
+ "Start Address 3 (SAR3) 0xde", // 0xde
+ // block cursor 1
+ "Block Cursor 1 (BCUR1) 0xe0",
+ "Block Cursor 1 (BCUR1) 0xe2",
+ // block cursor 2
+ "Block Cursor 2 (BCUR2) 0xe4",
+ "Block Cursor 2 (BCUR2) 0xe6",
+ "Cursor Definition (CDR)",
+ "Zoom Factor (ZFR)",
+ "Lightpen Address (LPAR) 0xec",
+ "Lightpen Address (LPAR) 0xee",
+ "(Undefined)", // 0xf0
+ "(Undefined)", // 0xf2
+ "(Undefined)", // 0xf4
+ "(Undefined)", // 0xf6
+ "(Undefined)", // 0xf8
+ "(Undefined)", // 0xfa
+ "(Undefined)", // 0xfc
+ "(Undefined)" // 0xfe
+};
+
+static const char *const wpr_regnames[0x20] =
+{
+ "Color 0 (CL0)",
+ "Color 1 (CL1)",
+ "Color Comparison (COMP)",
+ "Edge Color (EDG)",
+ "Mask (MASK)",
+ "Pattern RAM Control (PRC) 0x05",
+ "Pattern RAM Control (PRC) 0x06",
+ "Pattern RAM Control (PRC) 0x07",
+ "Area Definition (ADR) 0x08",
+ "Area Definition (ADR) 0x09",
+ "Area Definition (ADR) 0x0a",
+ "Area Definition (ADR) 0x0b",
+ "Read Write Pointer (RWP) H",
+ "Read Write Pointer (RWP) L",
+ "(Undefined)",
+ "(Undefined)",
+ "Drawing Pointer (DP) H",
+ "Drawing Pointer (DP) L",
+ "Current Pointer (CP) H",
+ "Current Pointer (CP) L",
+ "(Undefined)", // 0x14
+ "(Undefined)",
+ "(Undefined)", // 0x16
+ "(Undefined)",
+ "(Undefined)", // 0x18
+ "(Undefined)",
+ "(Undefined)", // 0x1a
+ "(Undefined)",
+ "(Undefined)", // 0x1c
+ "(Undefined)",
+ "(Undefined)", // 0x1e
+ "(Undefined)"
+};
+
+enum
+{
+ COMMAND_INVALID = -1,
+ COMMAND_ORG,
+ COMMAND_WPR,
+ COMMAND_RPR,
+ COMMAND_WPTN,
+ COMMAND_RPTN,
+ COMMAND_DRD,
+ COMMAND_DWT,
+ COMMAND_DMOD,
+ COMMAND_RD,
+ COMMAND_WT,
+ COMMAND_MOD,
+ COMMAND_CLR,
+ COMMAND_SCLR,
+ COMMAND_CPY,
+ COMMAND_SCPY,
+ COMMAND_AMOVE,
+ COMMAND_RMOVE,
+ COMMAND_ALINE,
+ COMMAND_RLINE,
+ COMMAND_ARCT,
+ COMMAND_RRCT,
+ COMMAND_APLL,
+ COMMAND_RPLL,
+ COMMAND_APLG,
+ COMMAND_RPLG,
+ COMMAND_CRCL,
+ COMMAND_ELPS,
+ COMMAND_AARC,
+ COMMAND_RARC,
+ COMMAND_AEARC,
+ COMMAND_REARC,
+ COMMAND_AFRCT,
+ COMMAND_RFRCT,
+ COMMAND_PAINT,
+ COMMAND_DOT,
+ COMMAND_PTN,
+ COMMAND_AGCPY,
+ COMMAND_RGCPY
+};
+
+#define H63484_COMMAND_ORG 0x0400 // p: 2
+#define H63484_COMMAND_WPR 0x0800 // & ~0x1f p: 1
+#define H63484_COMMAND_RPR 0x0c00 // & ~0x1f p: 0
+#define H63484_COMMAND_WPTN 0x1800 // & ~0xf p: 1 + n
+#define H63484_COMMAND_RPTN 0x1c00 // & ~0xf p: 1
+#define H63484_COMMAND_DRD 0x2400 // p: 2
+#define H63484_COMMAND_DWT 0x2800 // p: 2
+#define H63484_COMMAND_DMOD 0x2c00 // & ~3 p: 2
+#define H63484_COMMAND_RD 0x4400 // p: 0
+#define H63484_COMMAND_WT 0x4800 // p: 1
+#define H63484_COMMAND_MOD 0x4c00 // & ~3 p: 1
+#define H63484_COMMAND_CLR 0x5800 // p: 3
+#define H63484_COMMAND_SCLR 0x5c00 // & ~3 p: 3
+#define H63484_COMMAND_CPY 0x6000 // & ~0x0f03 p: 4
+#define H63484_COMMAND_SCPY 0x7000 // & ~0x0f03 p: 4
+#define H63484_COMMAND_AMOVE 0x8000 // p: 2
+#define H63484_COMMAND_RMOVE 0x8400 // p: 2
+#define H63484_COMMAND_ALINE 0x8800 // & ~0x00ff p: 2
+#define H63484_COMMAND_RLINE 0x8c00 // & ~0x00ff p: 2
+#define H63484_COMMAND_ARCT 0x9000 // & ~0x00ff p: 2
+#define H63484_COMMAND_RRCT 0x9400 // & ~0x00ff p: 2
+#define H63484_COMMAND_APLL 0x9800 // & ~0x00ff p: 1 + n
+#define H63484_COMMAND_RPLL 0x9c00 // & ~0x00ff p: 1 + n
+#define H63484_COMMAND_APLG 0xa000 // & ~0x00ff p: 1 + n
+#define H63484_COMMAND_RPLG 0xa400 // & ~0x00ff p: 1 + n
+#define H63484_COMMAND_CRCL 0xa800 // & ~0x01ff p: 1
+#define H63484_COMMAND_ELPS 0xac00 // & ~0x01ff p: 3
+#define H63484_COMMAND_AARC 0xb000 // & ~0x01ff p: 4
+#define H63484_COMMAND_RARC 0xb400 // & ~0x01ff p: 4
+#define H63484_COMMAND_AEARC 0xb800 // & ~0x01ff p: 6
+#define H63484_COMMAND_REARC 0xbc00 // & ~0x01ff p: 6
+#define H63484_COMMAND_AFRCT 0xc000 // & ~0x00ff p: 2
+#define H63484_COMMAND_RFRCT 0xc400 // & ~0x00ff p: 2
+#define H63484_COMMAND_PAINT 0xc800 // & ~0x01ff p: 0
+#define H63484_COMMAND_DOT 0xcc00 // & ~0x00ff p: 0
+#define H63484_COMMAND_PTN 0xd000 // & ~0x0fff p: 1
+#define H63484_COMMAND_AGCPY 0xe000 // & ~0x0fff p: 4
+#define H63484_COMMAND_RGCPY 0xf000 // & ~0x0fff p: 4
+
+
+/*-------------------------------------------------
+ ROM( h63484 )
+-------------------------------------------------*/
+
+// devices
+const device_type H63484 = &device_creator<h63484_device>;
+
+
+ROM_START( h63484 )
+ ROM_REGION( 0x100, "h63484", 0 )
+ ROM_LOAD( "h63484.bin", 0x000, 0x100, NO_DUMP ) /* internal control ROM */
+ROM_END
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *h63484_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *h63484_device::device_rom_region() const
+{
+ return ROM_NAME( h63484 );
+}
+
+//-------------------------------------------------
+// readword - read a word at the given address
+//-------------------------------------------------
+
+inline UINT16 h63484_device::readword(offs_t address)
+{
+ return space().read_word(address << 1);
+}
+
+
+//-------------------------------------------------
+// writeword - write a word at the given address
+//-------------------------------------------------
+
+inline void h63484_device::writeword(offs_t address, UINT16 data)
+{
+ space().write_word(address << 1, data);
+}
+
+
+inline void h63484_device::inc_ar(int value)
+{
+ if(m_ar & 0x80)
+ {
+ if (m_ar + value > 0xff) // TODO: what happens if it overflows?
+ logerror("HD63484 '%s': Address Register overflows 0x%02x\n", tag(), m_ar + value);
+
+ m_ar = (m_ar + value) & 0xff;
+ }
+}
+
+inline void h63484_device::fifo_w_clear()
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ m_fifo[i] = 0;
+
+ m_fifo_ptr = -1;
+
+ m_sr |= H63484_SR_WFR;
+ m_sr |= H63484_SR_WFE;
+}
+
+inline void h63484_device::queue_w(UINT8 data)
+{
+ if (m_fifo_ptr < 15)
+ {
+ m_fifo_ptr++;
+
+ m_fifo[m_fifo_ptr] = data;
+
+ if (m_fifo_ptr == 16)
+ m_sr &= ~H63484_SR_WFR;
+
+ m_sr &= ~H63484_SR_WFE;
+ }
+ else
+ {
+ // TODO what happen? somebody set us up the bomb
+ printf("FIFO?\n");
+ }
+}
+
+inline void h63484_device::dequeue_w(UINT8 *data)
+{
+ int i;
+
+ *data = m_fifo[0];
+
+ if (m_fifo_ptr > -1)
+ {
+ for (i = 0; i < 15; i++)
+ m_fifo[i] = m_fifo[i + 1];
+
+ m_fifo[15] = 0;
+
+ m_fifo_ptr--;
+
+ m_sr |= H63484_SR_WFR;
+
+ if (m_fifo_ptr == -1)
+ m_sr |= H63484_SR_WFE;
+
+ }
+}
+
+inline void h63484_device::fifo_r_clear()
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ m_fifo_r[i] = 0;
+
+ m_fifo_r_ptr = -1;
+
+ m_sr &= ~H63484_SR_RFR;
+ m_sr &= ~H63484_SR_RFF;
+}
+
+inline void h63484_device::queue_r(UINT8 data)
+{
+ if (m_fifo_r_ptr < 15)
+ {
+ m_fifo_r_ptr++;
+
+ m_fifo_r[m_fifo_r_ptr] = data;
+
+ if (m_fifo_r_ptr == 16)
+ m_sr |= H63484_SR_RFF;
+
+ m_sr |= H63484_SR_RFR;
+ }
+ else
+ {
+ // TODO what happen? somebody set us up the bomb
+ printf("FIFO?\n");
+ }
+}
+
+inline void h63484_device::dequeue_r(UINT8 *data)
+{
+ int i;
+
+ *data = m_fifo_r[0];
+
+ if (m_fifo_r_ptr > -1)
+ {
+ for (i = 0; i < 15; i++)
+ m_fifo_r[i] = m_fifo_r[i + 1];
+
+ m_fifo_r[15] = 0;
+
+ m_fifo_r_ptr--;
+
+ m_sr &= ~H63484_SR_RFF;
+
+ if (m_fifo_r_ptr == -1)
+ m_sr &= ~H63484_SR_RFR;
+ }
+}
+
+//-------------------------------------------------
+// recompute_parameters -
+//-------------------------------------------------
+
+inline void h63484_device::recompute_parameters()
+{
+ if(m_hdw < 3 || m_hc == 0 || m_vc == 0) //bail out if screen params aren't valid
+ return;
+
+ if (LOG)
+ {
+ printf("HC %d HSW %d HDS %d HDW %d HWS %d HWW %d\n",m_hc,m_hsw,m_hds,m_hdw,m_hws,m_hww);
+ printf("VC %d VDS %d VSW %d VWS %d VWW %d\n",m_vc,m_vds,m_vsw,m_vws,m_vww);
+ printf("SP0 %d SP1 %d SP2 %d\n",m_sp[0],m_sp[1],m_sp[2]);
+ }
+
+ int gai = (m_omr>>4) & 0x07;
+ if (gai > 3) printf("unsupported GAI=%d\n", gai);
+ int acm = (m_omr & 0x08) ? 2 : 1;
+ int ppw = 16 / get_bpp();
+ int ppmc = ppw * (1 << gai) / acm; // TODO: GAI > 3
+ int vbstart = m_vds + m_sp[1];
+ if (BIT(m_dcr, 13)) vbstart += m_sp[0];
+ if (BIT(m_dcr, 11)) vbstart += m_sp[2];
+
+ rectangle visarea = m_screen->visible_area();
+ visarea.set((m_hsw + m_hds) * ppmc, (m_hsw + m_hds + m_hdw) * ppmc - 1, m_vds, vbstart - 1);
+ attoseconds_t frame_period = m_screen->frame_period().attoseconds(); // TODO: use clock() to calculate the frame_period
+ m_screen->configure(m_hc * ppmc, m_vc, visarea, frame_period);
+}
+
+
+/*****************************************************************************
+ IMPLEMENTATION
+*****************************************************************************/
+
+int h63484_device::translate_command(UINT16 data)
+{
+ /* annoying switch-case sequence, but it's the only way to get invalid commands ... */
+ switch (data)
+ {
+ case H63484_COMMAND_ORG: return COMMAND_ORG;
+ case H63484_COMMAND_DRD: return COMMAND_DRD;
+ case H63484_COMMAND_DWT: return COMMAND_DWT;
+ case H63484_COMMAND_RD: return COMMAND_RD;
+ case H63484_COMMAND_WT: return COMMAND_WT;
+ case H63484_COMMAND_CLR: return COMMAND_CLR;
+ case H63484_COMMAND_AMOVE: return COMMAND_AMOVE;
+ case H63484_COMMAND_RMOVE: return COMMAND_RMOVE;
+ }
+
+ switch(data & ~0x3)
+ {
+ case H63484_COMMAND_DMOD: return COMMAND_DMOD;
+ case H63484_COMMAND_MOD: return COMMAND_MOD;
+ case H63484_COMMAND_SCLR: return COMMAND_SCLR;
+ }
+
+ switch(data & ~0xf)
+ {
+ case H63484_COMMAND_WPTN: return COMMAND_WPTN;
+ case H63484_COMMAND_RPTN: return COMMAND_RPTN;
+ }
+
+ switch(data & ~0x1f)
+ {
+ case H63484_COMMAND_WPR: return COMMAND_WPR;
+ case H63484_COMMAND_RPR: return COMMAND_RPR;
+ }
+
+ switch(data & ~0x0f03)
+ {
+ case H63484_COMMAND_CPY: return COMMAND_CPY;
+ case H63484_COMMAND_SCPY: return COMMAND_SCPY;
+ }
+
+ switch(data & ~0x00ff)
+ {
+ case H63484_COMMAND_ALINE: return COMMAND_ALINE;
+ case H63484_COMMAND_RLINE: return COMMAND_RLINE;
+ case H63484_COMMAND_ARCT: return COMMAND_ARCT;
+ case H63484_COMMAND_RRCT: return COMMAND_RRCT;
+ case H63484_COMMAND_APLL: return COMMAND_APLL;
+ case H63484_COMMAND_RPLL: return COMMAND_RPLL;
+ case H63484_COMMAND_APLG: return COMMAND_APLG;
+ case H63484_COMMAND_RPLG: return COMMAND_RPLG;
+ case H63484_COMMAND_AFRCT: return COMMAND_AFRCT;
+ case H63484_COMMAND_RFRCT: return COMMAND_RFRCT;
+ case H63484_COMMAND_DOT: return COMMAND_DOT;
+ }
+
+ switch(data & ~0x01ff)
+ {
+ case H63484_COMMAND_CRCL: return COMMAND_CRCL;
+ case H63484_COMMAND_ELPS: return COMMAND_ELPS;
+ case H63484_COMMAND_AARC: return COMMAND_AARC;
+ case H63484_COMMAND_RARC: return COMMAND_RARC;
+ case H63484_COMMAND_AEARC: return COMMAND_AEARC;
+ case H63484_COMMAND_REARC: return COMMAND_REARC;
+ case H63484_COMMAND_PAINT: return COMMAND_PAINT;
+ }
+
+ switch(data & ~0x0fff)
+ {
+ case H63484_COMMAND_PTN: return COMMAND_PTN;
+ case H63484_COMMAND_AGCPY: return COMMAND_AGCPY;
+ case H63484_COMMAND_RGCPY: return COMMAND_RGCPY;
+ }
+
+ return COMMAND_INVALID;
+}
+
+inline void h63484_device::command_end_seq()
+{
+ //h63484->param_ptr = 0;
+ m_sr |= H63484_SR_CED;
+
+ /* TODO: we might need to be more aggressive and clear the params in there */
+}
+
+int h63484_device::get_bpp()
+{
+ int gbm = (m_ccr >> 8) & 0x07;
+
+ if (gbm <= 4)
+ return 1 << gbm;
+
+ //logerror ("Invalid Graphic Bit Mode (%d)\n", gbm);
+ return 1;
+}
+
+void h63484_device::calc_offset(INT16 x, INT16 y, UINT32 &offset, UINT8 &bit_pos)
+{
+ int bpp = get_bpp();
+ int ppw = 16 / bpp;
+ int gbm = (m_ccr >> 8) & 0x07;
+ x += (m_org_dpd >> gbm);
+ if (x >= 0)
+ {
+ offset = x / ppw;
+ bit_pos = x % ppw;
+ }
+ else
+ {
+ offset = x / ppw;
+ bit_pos= (-x) % ppw;
+ if (bit_pos != 0)
+ {
+ offset--;
+ bit_pos = ppw - bit_pos;
+ }
+ }
+
+ offset += m_org_dpa - y * m_mwr[m_org_dn];
+ bit_pos *= bpp;
+}
+
+UINT16 h63484_device::get_dot(INT16 x, INT16 y)
+{
+ UINT8 bpp = get_bpp();
+ UINT32 offset = 0;
+ UINT8 bit_pos = 0;
+
+ calc_offset(x, y, offset, bit_pos);
+
+ return (readword(offset) >> bit_pos) & ((1 << bpp) - 1);
+}
+
+bool h63484_device::set_dot(INT16 x, INT16 y, INT16 px, INT16 py)
+{
+ int xs = m_pex - m_psx + 1;
+ int ys = m_pey - m_psy + 1;
+ int zx = m_pzx + 1;
+ int zy = m_pzy + 1;
+ int xp = m_psx + ((px % (xs * zx)) / zx);
+ int yp = m_psy + ((py % (ys * zy)) / zy);
+
+ if (xp < m_psx)
+ xp = (m_pex + 1) - (m_psx - xp);
+
+ if (yp < m_psy)
+ yp = (m_pey + 1) - (m_psy - yp);
+
+ int pix = (m_pram[yp & 0x0f] >> (xp & 0x0f)) & 0x01;
+
+ UINT8 col = (m_cr >> 3) & 0x03;
+ UINT8 bpp = get_bpp();
+ UINT16 mask = (1 << bpp) - 1;
+ UINT16 xmask = (16 / bpp) - 1;
+ UINT16 cl0 = (m_cl0 >> ((x & xmask) * bpp)) & mask;
+ UINT16 cl1 = (m_cl1 >> ((x & xmask) * bpp)) & mask;
+
+ switch (col)
+ {
+ case 0x00:
+ return set_dot(x, y, pix ? cl1 : cl0);
+ case 0x01:
+ if (pix)
+ return set_dot(x, y, cl1);
+ break;
+ case 0x02:
+ if (!pix)
+ return set_dot(x, y, cl0);
+ break;
+ case 0x03:
+ fatalerror("HD63484 color modes (Pattern RAM indirect)\n");
+ // TODO
+ }
+
+ return false;
+}
+
+bool h63484_device::set_dot(INT16 x, INT16 y, UINT16 color)
+{
+ UINT8 bpp = get_bpp();
+ UINT32 offset = 0;
+ UINT8 bit_pos = 0;
+ UINT8 opm = m_cr & 0x07;
+ UINT8 area = (m_cr >> 5) & 0x07;
+
+ calc_offset(x, y, offset, bit_pos);
+
+ UINT16 mask = ((1 << bpp) - 1) << bit_pos;
+ UINT16 color_shifted = (color << bit_pos) & mask;
+
+ UINT16 data = readword(offset);
+ UINT16 res = data;
+
+ switch (opm)
+ {
+ case 0:
+ res = (data & ~mask) | color_shifted;
+ break;
+ case 1:
+ res = (data & ~mask) | ((data & mask) | color_shifted);
+ break;
+ case 2:
+ res = (data & ~mask) | ((data & mask) & color_shifted);
+ break;
+ case 3:
+ res = (data & ~mask) | ((data & mask) ^ color_shifted);
+ break;
+ case 4:
+ if (get_dot(x, y) == ((m_ccmp & mask) >> bit_pos))
+ res = (data & ~mask) | color_shifted;
+ break;
+ case 5:
+ if (get_dot(x, y) != ((m_ccmp & mask) >> bit_pos))
+ res = (data & ~mask) | color_shifted;
+ break;
+ case 6:
+ if (get_dot(x, y) < ((m_cl0 & mask) >> bit_pos))
+ res = (data & ~mask) | color_shifted;
+ break;
+ case 7:
+ if (get_dot(x, y) > ((m_cl1 & mask) >> bit_pos))
+ res = (data & ~mask) | color_shifted;
+ break;
+ }
+
+ writeword(offset, res);
+
+ if (area)
+ logerror("HD63484 '%s': unsupported area detection %x (%d %d)\n", tag(), area, x, y);
+
+ return false; // TODO: return area detection status
+}
+
+void h63484_device::draw_line(INT16 sx, INT16 sy, INT16 ex, INT16 ey)
+{
+ UINT16 delta_x = abs(ex - sx) * 2;
+ UINT16 delta_y = abs(ey - sy) * 2;
+ int dir_x = (ex < sx) ? -1 : ((ex > sx) ? +1 : 0);
+ int dir_y = (ey < sy) ? -1 : ((ey > sy) ? +1 : 0);
+ int pram_pos = 0;
+
+ if(delta_x > delta_y)
+ {
+ int delta = delta_y - delta_x / 2;
+ while(sx != ex)
+ {
+ set_dot(sx, sy, pram_pos, 0);
+
+ if(delta >= 0)
+ {
+ sy += dir_y;
+ delta -= delta_x;
+ }
+ pram_pos++;
+ sx += dir_x;
+ delta += delta_y;
+ }
+ }
+ else
+ {
+ int delta = delta_x - delta_y / 2;
+ while(sy != ey)
+ {
+ set_dot(sx, sy, pram_pos, 0);
+
+ if(delta >= 0)
+ {
+ sx += dir_x;
+ delta -= delta_y;
+ }
+ pram_pos++;
+ sy += dir_y;
+ delta += delta_x;
+ }
+ }
+}
+
+void h63484_device::draw_ellipse(INT16 cx, INT16 cy, double dx, double dy, double s_angol, double e_angol, bool c)
+{
+ double inc = 1.0 / (MAX(dx, dy) * 100);
+ for (double angol = s_angol; fabs(angol - e_angol) >= inc*2; angol += inc * (c ? -1 : +1))
+ {
+ if (angol > DEGREE_TO_RADIAN(360)) angol -= DEGREE_TO_RADIAN(360);
+ if (angol < DEGREE_TO_RADIAN(0)) angol += DEGREE_TO_RADIAN(360);
+
+ double px = cos(angol) * dx;
+ double py = sin(angol) * dy;
+ set_dot(cx + round(px), cy + round(py), 0, 0);
+ }
+}
+
+void h63484_device::paint(INT16 sx, INT16 sy)
+{
+/*
+ This is not accurate since real hardware can only paint 4 'unpaintable' areas,
+ the other 'unpaintable' points are pushed into the read FIFO to be processed
+ later by the program, but currently is impossible suspend/resume the command
+ in case the read FIFO is full, so all 'unpaintable' areas are painted.
+ Also CP is not in the correct position after this command.
+*/
+ UINT8 e = BIT(m_cr, 8);
+ UINT8 bpp = get_bpp();
+ UINT16 mask = (1 << bpp) - 1;
+ UINT16 xmask = (16 / bpp) - 1;
+
+ for (int ydir=0; ydir<2; ydir++)
+ for(UINT16 y=0;y<0x7fff; y++)
+ {
+ bool limit = true;
+ bool unpaintable_up = false;
+ bool unpaintable_dn = false;
+
+ for (int xdir=0; xdir<2; xdir++)
+ for(UINT16 x=0; x<0x7fff; x++)
+ {
+ INT16 px = sx + (xdir ? -x : x);
+ INT16 py = sy + (ydir ? -y : y);
+
+ UINT16 dot = get_dot(px, py);
+ UINT16 edg = (m_edg >> (px & xmask) * bpp) & mask;
+ UINT16 cl0 = (m_cl0 >> (px & xmask) * bpp) & mask;
+ UINT16 cl1 = (m_cl1 >> (px & xmask) * bpp) & mask;
+
+ if ((e && dot != edg) || (!e && dot == edg) || dot == cl0 || dot == cl1)
+ break;
+
+ if ((!ydir && !xdir && x && y) || (xdir && y) || (ydir && x) || (ydir && xdir))
+ set_dot(px, py, px - m_cpx, py - m_cpy);
+
+ dot = get_dot(px, py + 1);
+ if (unpaintable_up && !((e && dot == edg) && (!e && dot != edg) && dot != cl0 && dot != cl1))
+ paint(px, py + 1);
+ else if (!unpaintable_up && ((e && dot != edg) || (!e && dot == edg) || dot == cl0 || dot == cl1))
+ unpaintable_up = true;
+
+ dot = get_dot(px, py - 1);
+ if (unpaintable_dn && !((e && dot == edg) && (!e && dot != edg) && dot != cl0 && dot != cl1))
+ paint(px, py - 1);
+ else if (!unpaintable_dn && ((e && dot != edg) || (!e && dot == edg) || dot == cl0 || dot == cl1))
+ unpaintable_dn = true;
+
+ limit = false;
+ }
+ if (limit) break;
+ }
+}
+
+UINT16 h63484_device::command_rpr_exec()
+{
+ switch(m_cr & 0x1f)
+ {
+ case 0x00: // color 0
+ return m_cl0;
+ case 0x01: // color 1
+ return m_cl1;
+ case 0x02: // color comparison
+ return m_ccmp;
+ case 0x03: // edge color
+ return m_edg;
+ case 0x04: // mask
+ return m_mask;
+ case 0x05: // Pattern RAM Control 1
+ return (m_ppy << 12) | (m_pzcy << 8) | (m_ppx << 4) | m_pzcx;
+ case 0x06: // Pattern RAM Control 2
+ return (m_psy << 12) | (m_psx << 4);
+ case 0x07: // Pattern RAM Control 3
+ return (m_pey << 12) | (m_pzy << 8) | (m_pex << 4) | m_pzx;
+ case 0x08: // Area Definition XMIN
+ return m_xmin;
+ case 0x09: // Area Definition YMIN
+ return m_ymin;
+ case 0x0a: // Area Definition XMAX
+ return m_xmax;
+ case 0x0b: // Area Definition YMAX
+ return m_ymax;
+ case 0x0c: // Read Write Pointer H
+ return (m_rwp_dn << 14) | ((m_rwp[m_rwp_dn] >> 12) & 0xff);
+ case 0x0d: // Read Write Pointer L
+ return (m_rwp[m_rwp_dn] & 0x0fff) << 4;
+ default:
+ if(LOG) printf("Read %sx\n", wpr_regnames[m_cr & 0x1f]);
+ return 0;
+ }
+}
+
+void h63484_device::command_wpr_exec()
+{
+ switch(m_cr & 0x1f)
+ {
+ case 0x00: // color 0
+ m_cl0 = m_pr[0];
+ break;
+ case 0x01: // color 1
+ m_cl1 = m_pr[0];
+ break;
+ case 0x02: // color comparison
+ m_ccmp = m_pr[0];
+ break;
+ case 0x03: // edge color
+ m_edg = m_pr[0];
+ break;
+ case 0x04: // mask
+ m_mask = m_pr[0];
+ break;
+ case 0x05: // Pattern RAM Control 1
+ m_pzcx = (m_pr[0] >> 0) & 0x0f;
+ m_ppx = (m_pr[0] >> 4) & 0x0f;
+ m_pzcy = (m_pr[0] >> 8) & 0x0f;
+ m_ppy = (m_pr[0] >> 12) & 0x0f;
+ break;
+ case 0x06: // Pattern RAM Control 2
+ m_psx = (m_pr[0] >> 4) & 0x0f;
+ m_psy = (m_pr[0] >> 12) & 0x0f;
+ break;
+ case 0x07: // Pattern RAM Control 3
+ m_pzx = (m_pr[0] >> 0) & 0x0f;
+ m_pex = (m_pr[0] >> 4) & 0x0f;
+ m_pzy = (m_pr[0] >> 8) & 0x0f;
+ m_pey = (m_pr[0] >> 12) & 0x0f;
+ break;
+ case 0x08: // Area Definition XMIN
+ m_xmin = m_pr[0];
+ break;
+ case 0x09: // Area Definition YMIN
+ m_ymin = m_pr[0];
+ break;
+ case 0x0a: // Area Definition XMAX
+ m_xmax = m_pr[0];
+ break;
+ case 0x0b: // Area Definition YMAX
+ m_ymax = m_pr[0];
+ break;
+ case 0x0c: // Read Write Pointer H
+ m_rwp_dn = (m_pr[0] & 0xc000) >> 14;
+ m_rwp[m_rwp_dn] = (m_rwp[m_rwp_dn] & 0x00fff) | ((m_pr[0] & 0x00ff) << 12);
+ break;
+ case 0x0d: // Read Write Pointer L
+ m_rwp[m_rwp_dn] = (m_rwp[m_rwp_dn] & 0xff000) | ((m_pr[0] & 0xfff0) >> 4);
+ break;
+ default:
+ if(LOG) printf("%s -> %02x\n",wpr_regnames[m_cr & 0x1f],m_pr[0]);
+ break;
+ }
+}
+
+void h63484_device::command_clr_exec()
+{
+ UINT8 mm = m_cr & 0x03;
+ UINT16 d = m_pr[0];
+ INT16 ax = (INT16)m_pr[1];
+ INT16 ay = (INT16)m_pr[2];
+
+ int d0_inc = (ax < 0) ? -1 : 1;
+ int d1_inc = (ay < 0) ? -1 : 1;
+
+ for(INT16 d1=0; d1!=ay+d1_inc; d1+=d1_inc)
+ {
+ for(INT16 d0=0; d0!=ax+d0_inc; d0+=d0_inc)
+ {
+ UINT32 offset = m_rwp[m_rwp_dn] - d1 * m_mwr[m_rwp_dn] + d0;
+ UINT16 data = readword(offset);
+ UINT16 res = 0;
+
+ if (BIT(m_cr, 10))
+ {
+ switch(mm)
+ {
+ case 0: // replace
+ res = (data & ~m_mask) | (d & m_mask);
+ break;
+ case 1: // OR
+ res = (data & ~m_mask) | ((data | d) & m_mask);
+ break;
+ case 2: // AND
+ res = (data & ~m_mask) | ((data & d) & m_mask);
+ break;
+ case 3: // EOR
+ res = (data & ~m_mask) | ((data ^ d) & m_mask);
+ break;
+ }
+ }
+ else
+ res = d;
+
+ writeword(offset, res);
+ }
+ }
+
+ m_rwp[m_rwp_dn] -= (ay + d1_inc) * m_mwr[m_rwp_dn];
+ m_rwp[m_rwp_dn] &= 0xfffff;
+}
+
+void h63484_device::command_cpy_exec()
+{
+ UINT8 mm = m_cr & 0x03;
+ UINT8 dsd = (m_cr >> 8) & 0x07;
+ UINT8 s = BIT(m_cr, 11);
+ UINT32 SA = ((m_pr[0] & 0xff) << 12) | ((m_pr[1]&0xfff0) >> 4);
+ INT16 DX = (INT16)m_pr[s ? 3 : 2];
+ INT16 DY = (INT16)m_pr[s ? 2 : 3];
+
+ int sd0_inc = (DX < 0) ? -1 : 1;
+ int sd1_inc = (DY < 0) ? -1 : 1;
+ int dd0_inc, dd1_inc;
+ if (dsd & 0x04)
+ {
+ dd0_inc = dsd & 0x01 ? -1 : +1;
+ dd1_inc = dsd & 0x02 ? -1 : +1;
+ }
+ else
+ {
+ dd0_inc = dsd & 0x02 ? -1 : +1;
+ dd1_inc = dsd & 0x01 ? -1 : +1;
+ }
+
+ for(INT16 sd1=0, dd1=0; sd1!=DY+sd1_inc; sd1+=sd1_inc, dd1+=dd1_inc)
+ {
+ for(INT16 sd0=0, dd0=0; sd0!=DX+sd0_inc; sd0+=sd0_inc, dd0+=dd0_inc)
+ {
+ UINT32 src_offset, dst_offset;
+
+ if (s)
+ src_offset = SA + sd1 - sd0 * m_mwr[m_rwp_dn];
+ else
+ src_offset = SA + sd0 - sd1 * m_mwr[m_rwp_dn];
+
+ if (BIT(dsd, 2))
+ dst_offset = m_rwp[m_rwp_dn] + dd1 - dd0 * m_mwr[m_rwp_dn];
+ else
+ dst_offset = m_rwp[m_rwp_dn] + dd0 - dd1 * m_mwr[m_rwp_dn];
+
+ UINT16 src_data = readword(src_offset);
+ UINT16 dst_data = readword(dst_offset);
+
+ if (BIT(m_cr, 12))
+ {
+ switch(mm)
+ {
+ case 0: // replace
+ dst_data = (dst_data & ~m_mask) | (src_data & m_mask);
+ break;
+ case 1: // OR
+ dst_data = (dst_data & ~m_mask) | ((dst_data | src_data) & m_mask);
+ break;
+ case 2: // AND
+ dst_data = (dst_data & ~m_mask) | ((dst_data & src_data) & m_mask);
+ break;
+ case 3: // EOR
+ dst_data = (dst_data & ~m_mask) | ((dst_data ^ src_data) & m_mask);
+ break;
+ }
+ }
+ else
+ dst_data = src_data;
+
+ writeword(dst_offset, dst_data);
+ }
+ }
+
+ m_rwp[m_rwp_dn] += dsd & 0x04 ? (DY + dd1_inc) : (-(DY + dd1_inc) * m_mwr[m_rwp_dn]);
+ m_rwp[m_rwp_dn] &= 0xfffff;
+}
+
+void h63484_device::command_line_exec()
+{
+ INT16 x = (INT16)m_pr[0];
+ INT16 y = (INT16)m_pr[1];
+
+ if (BIT(m_cr, 10))
+ {
+ x += m_cpx;
+ y += m_cpy;
+ }
+
+ draw_line(m_cpx, m_cpy, x, y);
+
+ m_cpx = x;
+ m_cpy = y;
+}
+
+void h63484_device::command_rct_exec()
+{
+ INT16 dX = m_pr[0];
+ INT16 dY = m_pr[1];
+
+ if (BIT(m_cr, 10)) // relative (RRCT)
+ {
+ dX += m_cpx;
+ dY += m_cpy;
+ }
+
+ /*
+ 3<-2
+ | ^
+ v |
+ 0->1
+ */
+
+ /* 0 -> 1 */
+ draw_line(m_cpx, m_cpy, dX, m_cpy);
+
+ /* 1 -> 2 */
+ draw_line(dX, m_cpy, dX, dY);
+
+ /* 2 -> 3 */
+ draw_line(dX, dY, m_cpx, dY);
+
+ /* 3 -> 4 */
+ draw_line(m_cpx, dY, m_cpx, m_cpy);
+}
+
+void h63484_device::command_gcpy_exec()
+{
+ UINT8 dsd = (m_cr >> 8) & 0x07;
+ UINT8 s = BIT(m_cr, 11);
+ INT16 Xs = (INT16)m_pr[0];
+ INT16 Ys = (INT16)m_pr[1];
+ INT16 DX = (INT16)m_pr[s ? 3 : 2];
+ INT16 DY = (INT16)m_pr[s ? 2 : 3];
+
+ if (BIT(m_cr, 12)) // relative (RGCPY)
+ {
+ Xs += m_cpx;
+ Ys += m_cpy;
+ }
+
+ int sd0_inc = (DX < 0) ? -1 : 1;
+ int sd1_inc = (DY < 0) ? -1 : 1;
+ int dd0_inc, dd1_inc;
+ if (dsd & 0x04)
+ {
+ dd0_inc = dsd & 0x01 ? -1 : +1;
+ dd1_inc = dsd & 0x02 ? -1 : +1;
+ }
+ else
+ {
+ dd0_inc = dsd & 0x02 ? -1 : +1;
+ dd1_inc = dsd & 0x01 ? -1 : +1;
+ }
+
+ for(INT16 sd1=0, dd1=0; sd1!=DY+sd1_inc; sd1+=sd1_inc, dd1+=dd1_inc)
+ {
+ for(INT16 sd0=0, dd0=0; sd0!=DX+sd0_inc; sd0+=sd0_inc, dd0+=dd0_inc)
+ {
+ UINT16 color;
+ if (s)
+ color = get_dot(Xs + sd1, Ys + sd0);
+ else
+ color = get_dot(Xs + sd0, Ys + sd1);
+
+ if (BIT(dsd, 2))
+ set_dot(m_cpx + dd1, m_cpy + dd0, color);
+ else
+ set_dot(m_cpx + dd0, m_cpy + dd1, color);
+ }
+ }
+
+ if (dsd & 0x04)
+ m_cpx += DY + dd1_inc;
+ else
+ m_cpy += DY + dd1_inc;
+
+}
+
+void h63484_device::command_frct_exec()
+{
+ INT16 X = (INT16)m_pr[0];
+ INT16 Y = (INT16)m_pr[1];
+
+ if (!BIT(m_cr, 10))
+ {
+ X -= m_cpx;
+ Y -= m_cpy;
+ }
+
+ int d0_inc = (X < 0) ? -1 : 1;
+ int d1_inc = (Y < 0) ? -1 : 1;
+
+ for(INT16 d1=0; d1!=Y+d1_inc; d1+=d1_inc)
+ {
+ for(INT16 d0=0; d0!=X+d0_inc; d0+=d0_inc)
+ set_dot(m_cpx + d0, m_cpy + d1, d0, d1);
+ }
+
+ m_cpy += (Y + d1_inc);
+}
+
+void h63484_device::command_ptn_exec()
+{
+ INT16 szx = ((m_pr[0] >> 0) & 0xff);
+ INT16 szy = ((m_pr[0] >> 8) & 0xff);
+ UINT8 sl_sd = (m_cr >> 8) & 0x0f;
+ INT16 px = 0;
+ INT16 py = 0;
+
+ for(INT16 d1=0; d1!=szy+1; d1++)
+ {
+ switch (sl_sd)
+ {
+ case 0x00: px = 0; py = d1; break;
+ case 0x01: px = -d1; py = d1; break;
+ case 0x02: py = 0; px = -d1; break;
+ case 0x03: px = -d1; py = -d1; break;
+ case 0x04: px = 0; py = -d1; break;
+ case 0x05: px = d1; py = -d1; break;
+ case 0x06: py = 0; px = d1; break;
+ case 0x07: px = d1; py = d1; break;
+ case 0x08: px = d1; py = d1; break;
+ case 0x09: px = 0; py = d1; break;
+ case 0x0a: px = -d1; py = d1; break;
+ case 0x0b: px = -d1; py = 0; break;
+ case 0x0c: px = -d1; py = -d1; break;
+ case 0x0d: px = 0; py = -d1; break;
+ case 0x0e: px = +d1; py = -d1; break;
+ case 0x0f: px = +d1; py = 0; break;
+ }
+
+ for(INT16 d0=0; d0!=szx+1; d0++)
+ {
+ set_dot(m_cpx + px, m_cpy + py, d0, d1);
+
+ switch (sl_sd)
+ {
+ case 0x00: px++; break;
+ case 0x01: px++; py++; break;
+ case 0x02: py++; break;
+ case 0x03: px--; py++; break;
+ case 0x04: px--; break;
+ case 0x05: px--; py--; break;
+ case 0x06: py--; break;
+ case 0x07: px++; py--; break;
+ case 0x08: px++; break;
+ case 0x09: px++; py++; break;
+ case 0x0a: py++; break;
+ case 0x0b: px--; py++; break;
+ case 0x0c: px--; break;
+ case 0x0d: px--; py--; break;
+ case 0x0e: py--; break;
+ case 0x0f: px++; py--; break;
+ }
+ }
+ }
+
+ switch (sl_sd)
+ {
+ case 0x00: m_cpy += (szy + 1); break;
+ case 0x01: m_cpx -= (szy + 1); m_cpy += (szy + 1); break;
+ case 0x02: m_cpx -= (szy + 1); break;
+ case 0x03: m_cpx -= (szy + 1); m_cpy -= (szy + 1); break;
+ case 0x04: m_cpy -= (szy + 1); break;
+ case 0x05: m_cpx += (szy + 1); m_cpy -= (szy + 1); break;
+ case 0x06: m_cpx += (szy + 1); break;
+ case 0x07: m_cpx += (szy + 1); m_cpy += (szy + 1); break;
+ case 0x08: m_cpx += (szy + 1); m_cpy += (szy + 1); break;
+ case 0x09: m_cpy += (szy + 1); break;
+ case 0x0a: m_cpx -= (szy + 1); m_cpy += (szy + 1); break;
+ case 0x0b: m_cpx -= (szy + 1); break;
+ case 0x0c: m_cpx -= (szy + 1); m_cpy -= (szy + 1); break;
+ case 0x0d: m_cpy -= (szy + 1); break;
+ case 0x0e: m_cpx += (szy + 1); m_cpy -= (szy + 1); break;
+ case 0x0f: m_cpx += (szy + 1); break;
+ }
+}
+
+void h63484_device::command_plg_exec()
+{
+ int sx = m_cpx;
+ int sy = m_cpy;
+ int ex=0;
+ int ey=0;
+
+ for (int i = 0; i < m_dn; i++)
+ {
+ if (BIT(m_cr, 10))
+ {
+ ex = sx + (INT16)m_pr[1 + i * 2];
+ ey = sy + (INT16)m_pr[1 + i * 2 + 1];
+ }
+ else
+ {
+ ex = (INT16)m_pr[1 + i * 2];
+ ey = (INT16)m_pr[1 + i * 2 + 1];
+ }
+
+ draw_line(sx, sy, ex, ey);
+
+ sx = ex;
+ sy = ey;
+ }
+
+ if (m_cr & 0x2000)
+ {
+ // APLG/RPLG
+ draw_line(sx, sy, m_cpx, m_cpy);
+ }
+ else
+ {
+ // APLL/RPLL
+ m_cpx = ex;
+ m_cpy = ey;
+ }
+}
+
+void h63484_device::command_arc_exec()
+{
+ INT16 xc = (INT16)m_pr[0];
+ INT16 yc = (INT16)m_pr[1];
+ INT16 xe = (INT16)m_pr[2];
+ INT16 ye = (INT16)m_pr[3];
+
+ if (BIT(m_cr, 10))
+ {
+ xc += m_cpx;
+ yc += m_cpy;
+ xe += m_cpx;
+ ye += m_cpy;
+ }
+
+ double r = sqrt(pow((double)(xc - m_cpx), 2) + pow((double)(yc - m_cpy), 2));
+ double s_angol = atan2((double)(m_cpy - yc), (double)(m_cpx - xc));
+ double e_angol = atan2((double)(ye - yc), (double)(xe - xc));
+ if (s_angol < 0) s_angol += DEGREE_TO_RADIAN(360);
+ if (e_angol < 0) e_angol += DEGREE_TO_RADIAN(360);
+
+ draw_ellipse(xc, yc, r, r, s_angol, e_angol, BIT(m_cr, 8));
+
+ m_cpx = xe;
+ m_cpy = ye;
+}
+
+void h63484_device::command_earc_exec()
+{
+ UINT16 a = m_pr[0];
+ UINT16 b = m_pr[1];
+ INT16 xc = (INT16)m_pr[2];
+ INT16 yc = (INT16)m_pr[3];
+ INT16 xe = (INT16)m_pr[4];
+ INT16 ye = (INT16)m_pr[5];
+
+ if (BIT(m_cr, 10))
+ {
+ xc += m_cpx;
+ yc += m_cpy;
+ xe += m_cpx;
+ ye += m_cpy;
+ }
+
+ double r = sqrt(pow((double)(xc - m_cpx), 2) / a + pow((double)(yc - m_cpy), 2) / b);
+ double dx = sqrt((double)a);
+ double dy = sqrt((double)b);
+ double s_angol = atan2((double)(m_cpy - yc) / dy, (double)(m_cpx - xc) / dx);
+ double e_angol = atan2((double)(ye - yc) / dy, (double)(xe - xc) / dx);
+ if (s_angol < 0) s_angol += DEGREE_TO_RADIAN(360);
+ if (e_angol < 0) e_angol += DEGREE_TO_RADIAN(360);
+
+ draw_ellipse(xc, yc, r * dx, r * dy, s_angol, e_angol, BIT(m_cr, 8));
+
+ m_cpx = xe;
+ m_cpy = ye;
+}
+
+void h63484_device::process_fifo()
+{
+ UINT8 data;
+
+ dequeue_w(&data);
+
+ if (m_sr & H63484_SR_CED)
+ {
+ m_cr = (data & 0xff) << 8;
+ dequeue_w(&data);
+ m_cr |= data & 0xff;
+ m_param_ptr = 0;
+ m_sr &= ~H63484_SR_CED;
+ }
+ else
+ {
+ m_pr[m_param_ptr] = (data & 0xff) << 8;
+ dequeue_w(&data);
+ m_pr[m_param_ptr] |= (data & 0xff);
+ m_param_ptr++;
+ }
+
+ switch (translate_command(m_cr))
+ {
+ case COMMAND_INVALID:
+ if (CMD_LOG) logerror("HD63484 '%s': <invalid %04x>\n", tag(), m_cr);
+ printf("H63484 '%s' Invalid Command Byte %02x\n", tag(), m_cr);
+ m_sr |= H63484_SR_CER; // command error
+ command_end_seq();
+ break;
+
+ case COMMAND_ORG:
+ if (m_param_ptr == 2)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': ORG 0x%04x, 0x%04x\n", tag(), m_pr[0], m_pr[1]);
+ m_org_dn = (m_pr[0] & 0xc000) >> 14;
+ m_org_dpa = ((m_pr[0] & 0xff) << 12) | ((m_pr[1] & 0xfff0) >> 4);
+ m_org_dpd = (m_pr[1] & 0xf);
+ m_cpx = m_cpy = 0;
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_WPR: // 0x0800 & ~0x1f
+ if (m_param_ptr == 1)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': WPR (%d) 0x%04x\n", tag(), m_cr & 0x1f, m_pr[0]);
+ command_wpr_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_RPR:
+ if (m_param_ptr == 0)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': RPR (%d)\n", tag(), m_cr & 0x1f);
+ UINT16 data = command_rpr_exec();
+ queue_r((data >> 8) & 0xff);
+ queue_r((data >> 0) & 0xff);
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_WPTN:
+ if(m_param_ptr == 1)
+ {
+ m_dn = m_pr[0]; // number of param words
+
+ //if(m_dn > 0x10 || m_dn == 0)
+ // fatalerror("stop!\n");
+ }
+
+ if(m_param_ptr == (1 + m_dn))
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': WPTN (%d) %d", tag(), m_cr & 0x0f, m_pr[0]);
+
+ int pra = m_cr & 0xf;
+ for(int i=0; i<m_dn; i++)
+ {
+ if (CMD_LOG) logerror(", 0x%04x", m_pr[1 + i]);
+ m_pram[(i + pra) & 0xf] = m_pr[1 + i];
+ }
+
+ if (CMD_LOG) logerror("\n");
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_RPTN:
+ if(m_param_ptr == 1)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': RPTN (%d) %d\n", tag(), m_cr & 0x0f, m_pr[0]);
+ command_end_seq();
+ fatalerror("HD63484 COMMAND_RPTN!\n");
+ }
+ break;
+
+ case COMMAND_DRD:
+ if (m_param_ptr == 2)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': DRD %d, %d\n", tag(), m_pr[0], m_pr[1]);
+ command_end_seq();
+ fatalerror("HD63484 COMMAND_DRD!\n");
+ }
+ break;
+
+ case COMMAND_DWT:
+ if (m_param_ptr == 2)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': DWT %d, %d\n", tag(), m_pr[0], m_pr[1]);
+ command_end_seq();
+ fatalerror("HD63484 COMMAND_DWT!\n");
+ }
+ break;
+
+ case COMMAND_DMOD:
+ if (m_param_ptr == 2)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': DMOD (%d) %d, %d\n", tag(), m_cr & 0x03, m_pr[0], m_pr[1]);
+ command_end_seq();
+ fatalerror("HD63484 COMMAND_DMOD!\n");
+ }
+ break;
+
+ case COMMAND_RD:
+ if (m_param_ptr == 0)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': RD\n", tag());
+ UINT16 data = readword(m_rwp[m_rwp_dn]);
+ queue_r((data >> 8) & 0xff);
+ queue_r((data >> 0) & 0xff);
+ m_rwp[m_rwp_dn]+=1;
+ m_rwp[m_rwp_dn]&=0xfffff;
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_WT:
+ if (m_param_ptr == 1)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': WT 0x%04x\n", tag(), m_pr[0]);
+ writeword(m_rwp[m_rwp_dn], m_pr[0]);
+ m_rwp[m_rwp_dn]+=1;
+ m_rwp[m_rwp_dn]&=0xfffff;
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_MOD:
+ if(m_param_ptr == 1)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': MOD (%d) 0x%04x\n", tag(), m_cr & 0x03, m_pr[0]);
+ UINT16 d = m_pr[0];
+ UINT16 data = readword(m_rwp[m_rwp_dn]);
+ UINT16 res = 0;
+
+ switch(m_cr & 0x03)
+ {
+ case 0: // replace
+ res = (data & ~m_mask) | (d & m_mask);
+ break;
+ case 1: // OR
+ res = (data & ~m_mask) | ((data | d) & m_mask);
+ break;
+ case 2: // AND
+ res = (data & ~m_mask) | ((data & d) & m_mask);
+ break;
+ case 3: // EOR
+ res = (data & ~m_mask) | ((data ^ d) & m_mask);
+ break;
+ }
+
+ writeword(m_rwp[m_rwp_dn], res);
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_CLR:
+ case COMMAND_SCLR:
+ if (m_param_ptr == 3)
+ {
+ if (CMD_LOG)
+ {
+ if (BIT(m_cr, 10))
+ logerror("HD63484 '%s': SCLR (%d) 0x%04x, %d, %d\n", tag(), m_cr & 0x03, m_pr[0], (INT16)m_pr[1], (INT16)m_pr[2]);
+ else
+ logerror("HD63484 '%s': CLR 0x%04x, %d, %d\n", tag(), m_pr[0], (INT16)m_pr[1], (INT16)m_pr[2]);
+ }
+
+ command_clr_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_CPY:
+ case COMMAND_SCPY:
+ if (m_param_ptr == 4)
+ {
+ if (CMD_LOG)
+ {
+ if (BIT(m_cr, 12))
+ logerror("HD63484 '%s': SCPY (%d, %d, %d) 0x%x, 0x%x, %d, %d\n", tag(), BIT(m_cr, 11), (m_cr >> 8) & 0x07, m_cr & 0x07, m_pr[0] & 0xff, (m_pr[1]&0xfff0) >> 4, (INT16)m_pr[2], (INT16)m_pr[3]);
+ else
+ logerror("HD63484 '%s': CPY (%d, %d) 0x%x, 0x%x, %d, %d\n", tag(), BIT(m_cr, 11), (m_cr >> 8) & 0x07, m_pr[0] & 0xff, (m_pr[1]&0xfff0) >> 4, (INT16)m_pr[2], (INT16)m_pr[3]);
+ }
+
+ command_cpy_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_AMOVE:
+ case COMMAND_RMOVE:
+ if (m_param_ptr == 2)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': %cMOVE %d, %d\n", tag(), BIT(m_cr, 10) ? 'R' : 'A', (INT16)m_pr[0], (INT16)m_pr[1]);
+ if (BIT(m_cr, 10))
+ {
+ m_cpx += (INT16)m_pr[0];
+ m_cpy += (INT16)m_pr[1];
+ }
+ else
+ {
+ m_cpx = (INT16)m_pr[0];
+ m_cpy = (INT16)m_pr[1];
+ }
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_RRCT:
+ case COMMAND_ARCT:
+ if (m_param_ptr == 2)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': %cRTC (%d, %d, %d) %d, %d\n", tag(), BIT(m_cr, 10) ? 'R' : 'A', (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, (INT16)m_pr[0], (INT16)m_pr[1]);
+ command_rct_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_RLINE:
+ case COMMAND_ALINE:
+ if (m_param_ptr == 2)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': %cLINE (%d, %d, %d) %d, %d\n", tag(), BIT(m_cr, 10) ? 'R' : 'A', (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, (INT16)m_pr[0], (INT16)m_pr[1]);
+ command_line_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_APLG:
+ case COMMAND_RPLG:
+ case COMMAND_APLL:
+ case COMMAND_RPLL:
+ if(m_param_ptr == 1)
+ m_dn = m_pr[0]; // number of param words
+
+ if(m_param_ptr == (1 + m_dn*2))
+ {
+ if (CMD_LOG)
+ {
+ logerror("HD63484 '%s': %cPL%c (%d, %d, %d) %d", tag(), BIT(m_cr, 10) ? 'R' : 'A', m_cr & 0x2000 ? 'G' : 'L', (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, m_pr[0]);
+ for (int i=0; i<m_dn; i++)
+ logerror(", %d, %d", (INT16)m_pr[1 + i * 2], (INT16)m_pr[1 + i * 2 + 1]);
+ logerror("\n");
+ }
+
+ command_plg_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_CRCL:
+ if(m_param_ptr == 1)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': CRCL (%d, %d, %d, %d) %d\n", tag(), BIT(m_cr, 8), (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, m_pr[0]);
+ UINT16 r = m_pr[0] & 0x1fff;
+ draw_ellipse(m_cpx, m_cpy, r, r, DEGREE_TO_RADIAN(0), DEGREE_TO_RADIAN(360), BIT(m_cr, 8));
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_ELPS:
+ if(m_param_ptr == 3)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': ELPS (%d, %d, %d, %d) %d, %d, %d\n", tag(), BIT(m_cr, 8), (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, m_pr[0], m_pr[1], m_pr[2]);
+ double dx = (double)m_pr[3];
+ double dy = sqrt(pow(dx, 2) / ((double)m_pr[0] / m_pr[1]));
+ draw_ellipse(m_cpx, m_cpy, dx, dy, DEGREE_TO_RADIAN(0), DEGREE_TO_RADIAN(360), BIT(m_cr, 8));
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_AARC:
+ case COMMAND_RARC:
+ if(m_param_ptr == 4)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': %cARC (%d, %d, %d, %d) %d, %d, %d, %d\n", tag(), BIT(m_cr, 10) ? 'R' : 'A', BIT(m_cr, 8), (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, (INT16)m_pr[0], (INT16)m_pr[1], (INT16)m_pr[2], (INT16)m_pr[3]);
+ command_arc_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_AEARC:
+ case COMMAND_REARC:
+ if(m_param_ptr == 6)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': %cEARC (%d, %d, %d, %d) %d, %d, %d, %d, %d, %d\n", tag(), BIT(m_cr, 10) ? 'R' : 'A', BIT(m_cr, 8), (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, m_pr[0], m_pr[1], m_pr[2], m_pr[3], m_pr[4], m_pr[5]);
+ command_earc_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_AFRCT:
+ case COMMAND_RFRCT:
+ if (m_param_ptr == 2)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': %cFRCT (%d, %d, %d) %d, %d\n", tag(), BIT(m_cr, 10) ? 'R' : 'A', (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, (INT16)m_pr[0], (INT16)m_pr[1]);
+
+ command_frct_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_PAINT:
+ if (m_param_ptr == 0)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': PAINT (%d, %d, %d, %d)\n", tag(), BIT(m_cr, 8), (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07);
+ paint(m_cpx, m_cpy);
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_DOT:
+ if (m_param_ptr == 0)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': DOT (%d, %d, %d)\n", tag(), (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07);
+ set_dot(m_cpx, m_cpy, 0, 0);
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_PTN:
+ if (m_param_ptr == 1)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': PTN (%d, %d, %d, %d, %d) 0x%04x\n", tag(), (m_cr >> 11) & 0x01, (m_cr >> 8) & 0x07, (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, m_pr[0]);
+ command_ptn_exec();
+ command_end_seq();
+ }
+ break;
+
+ case COMMAND_RGCPY:
+ case COMMAND_AGCPY:
+ if (m_param_ptr == 4)
+ {
+ if (CMD_LOG) logerror("HD63484 '%s': %cGCPY (%d, %d, %d, %d, %d) %d, %d, %d, %d\n", tag(), BIT(m_cr, 12) ? 'R' : 'A', (m_cr >> 11) & 0x01, (m_cr >> 8) & 0x07, (m_cr >> 5) & 0x07, (m_cr >> 3) & 0x03, (m_cr >> 0) & 0x07, (INT16)m_pr[0], (INT16)m_pr[1], (INT16)m_pr[2], (INT16)m_pr[3]);
+
+ command_gcpy_exec();
+ command_end_seq();
+ }
+ break;
+
+ default:
+ printf("%04x\n",m_cr);
+ fatalerror("stop!\n");
+ }
+}
+
+void h63484_device::exec_abort_sequence()
+{
+ fifo_w_clear();
+ fifo_r_clear();
+ m_sr = H63484_SR_WFR | H63484_SR_WFE | H63484_SR_CED; // hard-set to 0x23
+}
+
+UINT16 h63484_device::video_registers_r(int offset)
+{
+ UINT16 res = (m_vreg[offset] << 8) | (m_vreg[offset+1] & 0xff);
+
+ switch(offset)
+ {
+ case 0x06:
+ res = m_dcr;
+ break;
+
+ case 0x80:
+ res = m_screen->vpos() & 0xfff; // Raster Count
+ break;
+
+ default:
+ if(LOG) printf("%s R\n",acrtc_regnames[m_ar/2]);
+ break;
+ }
+
+ return res;
+}
+
+void h63484_device::video_registers_w(int offset)
+{
+ UINT16 vreg_data;
+
+ vreg_data = (m_vreg[offset]<<8)|(m_vreg[offset+1]&0xff);
+
+ switch(offset)
+ {
+ case 0x00: // FIFO entry
+ queue_w((vreg_data & 0xff00) >> 8);
+ queue_w((vreg_data & 0x00ff) >> 0);
+ if(FIFO_LOG) printf("%s -> %04x\n",acrtc_regnames[m_ar/2],vreg_data);
+ process_fifo();
+ break;
+
+ case 0x02: // Command Entry
+
+ if(vreg_data & 0x8000) // abort sequence (ABT)
+ exec_abort_sequence();
+
+ /*
+ x--- ---- ---- ---- ABorT
+ -x-- ---- ---- ---- PauSE
+ ...
+ ---- -xxx ---- ---- Graphic Bit Mode (bpp)
+ ---- ---- xxxx xxxx irq mask, directly correlated to sr
+ */
+ m_ccr = vreg_data;
+ break;
+
+ case 0x04:
+ m_omr = vreg_data;
+ break;
+
+ case 0x06:
+ m_dcr = vreg_data;
+ recompute_parameters();
+ break;
+
+ case 0x82: // Horizontal Sync Register
+ m_hc = ((vreg_data & 0xff00) >> 8) + 1;
+ m_hsw = vreg_data & 0x1f;
+ recompute_parameters();
+ break;
+ case 0x84: // Horizontal Display Register
+ m_hds = ((vreg_data & 0xff00) >> 8) + 1;
+ m_hdw = ((vreg_data & 0x00ff) >> 0) + 1;
+ recompute_parameters();
+ break;
+ case 0x92: // Horizontal Window Register
+ m_hws = ((vreg_data & 0xff00) >> 8) + 1;
+ m_hww = ((vreg_data & 0x00ff) >> 0) + 1;
+ recompute_parameters();
+ break;
+
+ case 0x86: // Vertical Sync Register
+ m_vc = (vreg_data & 0xfff);
+ recompute_parameters();
+ break;
+ case 0x88: // Vertical Display Register
+ m_vds = ((vreg_data & 0xff00) >> 8) + 1;
+ m_vsw = (vreg_data & 0x1f);
+ recompute_parameters();
+ break;
+ case 0x8a: // Split Screen Width 1
+ m_sp[1] = vreg_data & 0x0fff;
+ recompute_parameters();
+ break;
+ case 0x8c: // Split Screen Width 0
+ m_sp[0] = vreg_data & 0x0fff;
+ recompute_parameters();
+ break;
+ case 0x8e: // Split Screen Width 2
+ m_sp[2] = vreg_data & 0x0fff;
+ recompute_parameters();
+ break;
+ case 0x94: // Vertical Window Register A
+ m_vws = (vreg_data & 0xfff) + 1;
+ recompute_parameters();
+ break;
+ case 0x96: // Vertical Window Register B
+ m_vww = (vreg_data & 0xfff);
+ recompute_parameters();
+ break;
+
+ case 0xc2: // Memory Width Register
+ case 0xca:
+ case 0xd2:
+ case 0xda:
+ m_mwr[(offset & 0x18) >> 3] = vreg_data & 0xfff; // pitch
+ m_mwr_chr[(offset & 0x18) >> 3] = (vreg_data & 0x8000) >> 15;
+ break;
+
+ case 0xc4: // Start Address Register
+ case 0xcc:
+ case 0xd4:
+ case 0xdc:
+ m_sar[(offset & 0x18) >> 3] = ((vreg_data & 0xf) << 16) | (m_sar[(offset & 0x18) >> 3] & 0xffff);
+ m_sda[(offset & 0x18) >> 3] = (vreg_data & 0x0f00) >> 8;
+ break;
+
+ case 0xc6: // Start Address Register
+ case 0xce:
+ case 0xd6:
+ case 0xde:
+ m_sar[(offset & 0x18) >> 3] = (vreg_data & 0xffff) | (m_sar[(offset & 0x18) >> 3] & 0xf0000);
+ break;
+
+ default:
+ if(LOG) printf("%s -> %04x\n",acrtc_regnames[m_ar/2],vreg_data);
+ break;
+ }
+}
+
+READ16_MEMBER( h63484_device::status_r )
+{
+ return m_sr;
+}
+
+READ16_MEMBER( h63484_device::data_r )
+{
+ UINT16 res = 0xffff;
+
+ if(m_ar == 0) // FIFO read
+ {
+ UINT8 data;
+
+ dequeue_r(&data);
+ res = (data & 0xff) << 8;
+ dequeue_r(&data);
+ res |= data & 0xff;
+ }
+ else
+ res = video_registers_r(m_ar);
+
+ inc_ar(2);
+
+ return res;
+}
+
+WRITE16_MEMBER( h63484_device::address_w )
+{
+ if(ACCESSING_BITS_0_7)
+ m_ar = data & 0xfe;
+}
+
+WRITE16_MEMBER( h63484_device::data_w )
+{
+ if(ACCESSING_BITS_8_15)
+ m_vreg[m_ar] = (data & 0xff00) >> 8;
+
+ if(ACCESSING_BITS_0_7)
+ m_vreg[m_ar+1] = (data & 0xff);
+
+ video_registers_w(m_ar);
+
+ inc_ar(2);
+}
+
+READ8_MEMBER( h63484_device::status_r )
+{
+ return m_sr;
+}
+
+WRITE8_MEMBER( h63484_device::address_w )
+{
+ m_ar = data;
+}
+
+READ8_MEMBER( h63484_device::data_r )
+{
+ UINT8 res = 0xff;
+
+ if(m_ar < 2) // FIFO read
+ dequeue_r(&res);
+ else
+ res = video_registers_r(m_ar & 0xfe) >> (m_ar & 1 ? 0 : 8);
+
+ inc_ar(1);
+
+ return res;
+}
+
+WRITE8_MEMBER( h63484_device::data_w )
+{
+ m_vreg[m_ar] = data;
+
+ if(m_ar < 2) // FIFO write
+ {
+ queue_w(data);
+ if (m_ar & 1)
+ process_fifo();
+
+ m_ar ^= 1;
+ }
+ else
+ video_registers_w(m_ar & 0xfe);
+
+ inc_ar(1);
+}
+
+void h63484_device::device_start()
+{
+ m_display_cb.bind_relative_to(*owner());
+
+ register_save_state();
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void h63484_device::device_reset()
+{
+ m_sr = H63484_SR_CED | H63484_SR_WFR | H63484_SR_WFE;
+ m_ccr = m_omr = m_edg = m_dcr = m_hsw = 0;
+ m_hc = m_hds = m_hdw = m_hws = m_hww = 0;
+ m_vc = m_vws = m_vww = m_vds = m_vsw = 0;
+ m_sp[0] = m_sp[1] = m_sp[2] = 0;
+ m_ppx = m_ppy = 0;
+ m_cl0 = m_cl1 = 0;
+ m_xmin = m_ymin = m_xmax = m_ymax = 0;
+ m_ppx = m_pzcx = m_psx = m_pzx = m_pex = 0;
+ m_ppy = m_pzcy= m_psy = m_pzy = m_pey = 0;
+ m_ar = m_cr = 0;
+ m_param_ptr = 0;
+ m_rwp_dn = 0;
+ m_org_dpa = 0;
+ m_org_dn = 0;
+ m_org_dpd = 0;
+ m_ccmp = 0;
+ m_mask = -1;
+ m_cpx = m_cpy = 0;
+ m_dn = 0;
+
+ memset(m_vreg, 0, sizeof(m_vreg));
+ memset(m_fifo, 0, sizeof(m_fifo));
+ memset(m_fifo_r, 0, sizeof(m_fifo_r));
+ memset(m_pr, 0, sizeof(m_pr));
+ memset(m_rwp, 0, sizeof(m_rwp));
+ memset(m_mwr, 0, sizeof(m_mwr));
+ memset(m_mwr_chr, 0, sizeof(m_mwr_chr));
+ memset(m_sar, 0, sizeof(m_sar));
+ memset(m_sda, 0, sizeof(m_sda));
+ memset(m_pram, 0, sizeof(m_pram));
+}
+
+//-------------------------------------------------
+// draw_graphics_line -
+//-------------------------------------------------
+
+void h63484_device::draw_graphics_line(bitmap_ind16 &bitmap, const rectangle &cliprect, int vs, int y, int layer_n, bool active, bool ins_window)
+{
+ int bpp = get_bpp();
+ int ppw = 16 / bpp;
+ UINT32 mask = (1 << bpp) - 1;
+ UINT32 base_offs = m_sar[layer_n] + (y - vs) * m_mwr[layer_n];
+ UINT32 wind_offs = m_sar[3] + (y - m_vws) * m_mwr[3];
+ int step = (m_omr & 0x08) ? 2 : 1;
+ int gai = (m_omr>>4) & 0x07;
+ int ppmc = ppw * (1 << gai) / step; // TODO: GAI > 3
+ int ws = m_hsw + m_hws;
+
+ if (m_omr & 0x08)
+ {
+ /*
+ According to the datasheet, in interleaved and superimposed modes:
+ - HDW and HWW must be even
+ - the relation between HDS and HWS must be even/even or odd/odd
+ */
+
+ if (m_hww & 1)
+ ws += step;
+
+ if ((m_hws & 1) ^ (m_hds & 1))
+ wind_offs++;
+ }
+
+ for(int x=cliprect.min_x; x<=cliprect.max_x; x+=ppw)
+ {
+ UINT16 data = 0;
+ if (ins_window && x >= ws * ppmc && x < (ws + m_hww) * ppmc)
+ {
+ data = readword(wind_offs);
+ wind_offs++;
+ }
+ else if (active)
+ data = readword(base_offs);
+
+ for (int b=0; b<ppw; b++)
+ {
+ int px = x + b;
+ if (!m_display_cb.isnull())
+ m_display_cb(bitmap, cliprect, y, px, data & mask);
+ else if (cliprect.contains(px, y))
+ bitmap.pix16(y, px) = data & mask;
+
+ data >>= bpp;
+ }
+
+ base_offs++;
+ }
+}
+
+//-------------------------------------------------
+// update_screen -
+//-------------------------------------------------
+
+UINT32 h63484_device::update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ int l0 = cliprect.min_y + (BIT(m_dcr, 13) ? m_sp[0] : 0);
+ int l1 = l0 + m_sp[1];
+ int l2 = l1 + (BIT(m_dcr, 11) ? m_sp[2] : 0);
+
+ if(m_omr & 0x4000)
+ {
+ for(int y=cliprect.min_y; y<=cliprect.max_y; y++)
+ {
+ bool ins_window = BIT(m_dcr, 9) && y >= m_vws && y < m_vws+m_vww;
+
+ if (BIT(m_dcr, 13) && y >= cliprect.min_y && y < l0)
+ draw_graphics_line(bitmap, cliprect, cliprect.min_y, y, 0, BIT(m_dcr, 12), ins_window);
+ else if (y >= l0 && y < l1)
+ draw_graphics_line(bitmap, cliprect, l0, y, 1, BIT(m_dcr, 14), ins_window);
+ else if (BIT(m_dcr, 11) && y >= l1 && y < l2)
+ draw_graphics_line(bitmap, cliprect, l1, y, 2, BIT(m_dcr, 10), ins_window);
+ }
+ }
+ return 0;
+}
+
+void h63484_device::register_save_state()
+{
+ save_item(NAME(m_ar));
+ save_item(NAME(m_vreg));
+ save_item(NAME(m_sr));
+ save_item(NAME(m_fifo));
+ save_item(NAME(m_fifo_ptr));
+ save_item(NAME(m_fifo_r));
+ save_item(NAME(m_fifo_r_ptr));
+ save_item(NAME(m_cr));
+ save_item(NAME(m_pr));
+ save_item(NAME(m_param_ptr));
+ save_item(NAME(m_rwp));
+ save_item(NAME(m_rwp_dn));
+ save_item(NAME(m_org_dpa));
+ save_item(NAME(m_org_dn));
+ save_item(NAME(m_org_dpd));
+ save_item(NAME(m_cl0));
+ save_item(NAME(m_cl1));
+ save_item(NAME(m_ccmp));
+ save_item(NAME(m_mask));
+ save_item(NAME(m_cpx));
+ save_item(NAME(m_cpy));
+ save_item(NAME(m_mwr));
+ save_item(NAME(m_mwr_chr));
+ save_item(NAME(m_sar));
+ save_item(NAME(m_sda));
+ save_item(NAME(m_pram));
+ save_item(NAME(m_dn));
+ save_item(NAME(m_ccr));
+ save_item(NAME(m_omr));
+ save_item(NAME(m_edg));
+ save_item(NAME(m_dcr));
+ save_item(NAME(m_hc));
+ save_item(NAME(m_hds));
+ save_item(NAME(m_hdw));
+ save_item(NAME(m_hws));
+ save_item(NAME(m_hww));
+ save_item(NAME(m_sp));
+ save_item(NAME(m_hsw));
+ save_item(NAME(m_vc));
+ save_item(NAME(m_vws));
+ save_item(NAME(m_vww));
+ save_item(NAME(m_vds));
+ save_item(NAME(m_vsw));
+ save_item(NAME(m_ppy));
+ save_item(NAME(m_pzcy));
+ save_item(NAME(m_ppx));
+ save_item(NAME(m_pzcx));
+ save_item(NAME(m_psx));
+ save_item(NAME(m_pex));
+ save_item(NAME(m_pzx));
+ save_item(NAME(m_psy));
+ save_item(NAME(m_pzy));
+ save_item(NAME(m_pey));
+ save_item(NAME(m_xmin));
+ save_item(NAME(m_ymin));
+ save_item(NAME(m_xmax));
+ save_item(NAME(m_ymax));
+}
diff --git a/src/devices/video/h63484.h b/src/devices/video/h63484.h
new file mode 100644
index 00000000000..ac0232fae8f
--- /dev/null
+++ b/src/devices/video/h63484.h
@@ -0,0 +1,189 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese, Sandro Ronco
+/*************************************************************************
+
+ HD63484 ACRTC
+ Advanced CRT Controller.
+
+**************************************************************************/
+
+#pragma once
+
+#ifndef __H63484__
+#define __H63484__
+
+
+#include "emu.h"
+
+
+typedef device_delegate<void (bitmap_ind16 &bitmap, const rectangle &cliprect, int y, int x, UINT16 data)> h63484_display_delegate;
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_H63484_ADD(_tag, _clock, _map) \
+ MCFG_DEVICE_ADD(_tag, H63484, _clock) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _map)
+
+#define MCFG_H63484_ADDRESS_MAP(_map) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _map)
+
+#define MCFG_H63484_DISPLAY_CALLBACK_OWNER(_class, _method) \
+ h63484_device::static_set_display_callback(*device, h63484_display_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define H63484_DISPLAY_PIXELS_MEMBER(_name) void _name(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, int x, UINT16 data)
+
+
+// ======================> h63484_device
+
+class h63484_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ h63484_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_display_callback(device_t &device, h63484_display_delegate callback) { downcast<h63484_device &>(device).m_display_cb = callback; }
+
+ DECLARE_WRITE16_MEMBER( address_w );
+ DECLARE_WRITE16_MEMBER( data_w );
+ DECLARE_READ16_MEMBER( status_r );
+ DECLARE_READ16_MEMBER( data_r );
+
+ DECLARE_WRITE8_MEMBER( address_w );
+ DECLARE_WRITE8_MEMBER( data_w );
+ DECLARE_READ8_MEMBER( status_r );
+ DECLARE_READ8_MEMBER( data_r );
+
+ UINT32 update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ virtual const rom_entry *device_rom_region() const;
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ //virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ inline UINT16 readword(offs_t address);
+ inline void writeword(offs_t address, UINT16 data);
+
+private:
+ inline void inc_ar(int value);
+ inline void fifo_w_clear();
+ inline void queue_w(UINT8 data);
+ inline void dequeue_w(UINT8 *data);
+ inline void fifo_r_clear();
+ inline void queue_r(UINT8 data);
+ inline void dequeue_r(UINT8 *data);
+ inline void recompute_parameters();
+ inline void command_end_seq();
+ void calc_offset(INT16 x, INT16 y, UINT32 &offset, UINT8 &bit_pos);
+ int get_bpp();
+ UINT16 get_dot(INT16 x, INT16 y);
+ bool set_dot(INT16 x, INT16 y, INT16 px, INT16 py);
+ bool set_dot(INT16 x, INT16 y, UINT16 color);
+ void draw_line(INT16 sx, INT16 sy, INT16 ex, INT16 ey);
+ void draw_ellipse(INT16 cx, INT16 cy, double dx, double dy, double s_angol, double e_angol, bool c);
+ void paint(INT16 sx, INT16 sy);
+
+ void command_wpr_exec();
+ UINT16 command_rpr_exec();
+ void command_clr_exec();
+ void command_cpy_exec();
+ void command_rct_exec();
+ void command_line_exec();
+ void command_gcpy_exec();
+ void command_ptn_exec();
+ void command_plg_exec();
+ void command_frct_exec();
+ void command_arc_exec();
+ void command_earc_exec();
+
+ void process_fifo();
+ void exec_abort_sequence();
+ UINT16 video_registers_r(int offset);
+ void video_registers_w(int offset);
+ int translate_command(UINT16 data);
+ void draw_graphics_line(bitmap_ind16 &bitmap, const rectangle &cliprect, int vs, int y, int layer_n, bool active, bool ins_window);
+
+ void register_save_state();
+
+ h63484_display_delegate m_display_cb;
+
+ UINT8 m_ar;
+ UINT8 m_vreg[0x100];
+ UINT8 m_sr;
+
+ UINT8 m_fifo[16]; /* FIFO W data queue */
+ int m_fifo_ptr; /* FIFO W pointer */
+
+ UINT8 m_fifo_r[16]; /* FIFO R data queue */
+ int m_fifo_r_ptr; /* FIFO R pointer */
+
+
+ UINT16 m_cr;
+ UINT16 m_pr[0x100]; /* parameter byte register */
+ int m_param_ptr; /* parameter pointer */
+
+ UINT32 m_rwp[4];
+ UINT8 m_rwp_dn;
+
+ UINT32 m_org_dpa;
+ UINT8 m_org_dn;
+ UINT8 m_org_dpd;
+ UINT16 m_cl0;
+ UINT16 m_cl1;
+ UINT16 m_ccmp;
+ UINT16 m_mask;
+
+ INT16 m_cpx;
+ INT16 m_cpy;
+
+ UINT16 m_mwr[4];
+ UINT8 m_mwr_chr[4];
+
+ UINT32 m_sar[4];
+ UINT8 m_sda[4];
+
+ UINT16 m_pram[0x10];
+ UINT8 m_dn;
+
+ UINT16 m_ccr;
+ UINT16 m_omr;
+ UINT16 m_edg;
+ UINT16 m_dcr;
+
+ UINT16 m_hc, m_hds, m_hdw, m_hws, m_hww;
+ UINT16 m_sp[3];
+ UINT8 m_hsw;
+
+ UINT16 m_vc, m_vws, m_vww, m_vds;
+ UINT8 m_vsw;
+
+ UINT16 m_ppy;
+ UINT16 m_pzcy;
+ UINT16 m_ppx;
+ UINT16 m_pzcx;
+ UINT16 m_psx;
+ UINT16 m_pex;
+ UINT16 m_pzx;
+ UINT16 m_psy;
+ UINT16 m_pzy;
+ UINT16 m_pey;
+
+ UINT16 m_xmin;
+ UINT16 m_ymin;
+ UINT16 m_xmax;
+ UINT16 m_ymax;
+
+ const address_space_config m_space_config;
+};
+
+// device type definition
+extern const device_type H63484;
+
+#endif /* __H63484_H__ */
diff --git a/src/devices/video/hd44102.c b/src/devices/video/hd44102.c
new file mode 100644
index 00000000000..e9a1a9d014d
--- /dev/null
+++ b/src/devices/video/hd44102.c
@@ -0,0 +1,292 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ HD44102 Dot Matrix Liquid Crystal Graphic Display Column Driver emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "hd44102.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+#define CONTROL_DISPLAY_OFF 0x38
+#define CONTROL_DISPLAY_ON 0x39
+#define CONTROL_COUNT_DOWN_MODE 0x3a
+#define CONTROL_COUNT_UP_MODE 0x3b
+#define CONTROL_Y_ADDRESS_MASK 0x3f
+#define CONTROL_X_ADDRESS_MASK 0xc0
+#define CONTROL_DISPLAY_START_PAGE 0x3e
+
+
+#define STATUS_BUSY 0x80 /* not supported */
+#define STATUS_COUNT_UP 0x40
+#define STATUS_DISPLAY_OFF 0x20
+#define STATUS_RESET 0x10 /* not supported */
+
+
+// device type definition
+const device_type HD44102 = &device_creator<hd44102_device>;
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// count_up_or_down -
+//-------------------------------------------------
+
+inline void hd44102_device::count_up_or_down()
+{
+ if (m_status & STATUS_COUNT_UP)
+ {
+ if (++m_y > 49) m_y = 0;
+ }
+ else
+ {
+ if (--m_y < 0) m_y = 49;
+ }
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// hd44102_device - constructor
+//-------------------------------------------------
+
+hd44102_device::hd44102_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HD44102, "HD44102", tag, owner, clock, "hd44102", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_cs2(0),
+ m_page(0),
+ m_x(0),
+ m_y(0)
+{
+}
+
+
+//-------------------------------------------------
+// static_set_offsets - configuration helper
+//-------------------------------------------------
+
+void hd44102_device::static_set_offsets(device_t &device, int sx, int sy)
+{
+ hd44102_device &hd44102 = downcast<hd44102_device &>(device);
+
+ hd44102.m_sx = sx;
+ hd44102.m_sy = sy;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void hd44102_device::device_start()
+{
+ // register for state saving
+ save_item(NAME(m_ram[0]));
+ save_item(NAME(m_ram[1]));
+ save_item(NAME(m_ram[2]));
+ save_item(NAME(m_ram[3]));
+ save_item(NAME(m_status));
+ save_item(NAME(m_output));
+ save_item(NAME(m_cs2));
+ save_item(NAME(m_page));
+ save_item(NAME(m_x));
+ save_item(NAME(m_y));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void hd44102_device::device_reset()
+{
+ m_status = STATUS_DISPLAY_OFF | STATUS_COUNT_UP;
+}
+
+
+//-------------------------------------------------
+// read - register read
+//-------------------------------------------------
+
+READ8_MEMBER( hd44102_device::read )
+{
+ UINT8 data = 0;
+
+ if (m_cs2)
+ {
+ data = (offset & 0x01) ? data_r(space, offset) : status_r(space, offset);
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write - register write
+//-------------------------------------------------
+
+WRITE8_MEMBER( hd44102_device::write )
+{
+ if (m_cs2)
+ {
+ (offset & 0x01) ? data_w(space, offset, data) : control_w(space, offset, data);
+ }
+}
+
+
+//-------------------------------------------------
+// status_r - status read
+//-------------------------------------------------
+
+READ8_MEMBER( hd44102_device::status_r )
+{
+ return m_status;
+}
+
+
+//-------------------------------------------------
+// control_w - control write
+//-------------------------------------------------
+
+WRITE8_MEMBER( hd44102_device::control_w )
+{
+ if (m_status & STATUS_BUSY) return;
+
+ switch (data)
+ {
+ case CONTROL_DISPLAY_OFF:
+ if (LOG) logerror("HD44102 '%s' Display Off\n", tag());
+
+ m_status |= STATUS_DISPLAY_OFF;
+ break;
+
+ case CONTROL_DISPLAY_ON:
+ if (LOG) logerror("HD44102 '%s' Display On\n", tag());
+
+ m_status &= ~STATUS_DISPLAY_OFF;
+ break;
+
+ case CONTROL_COUNT_DOWN_MODE:
+ if (LOG) logerror("HD44102 '%s' Count Down Mode\n", tag());
+
+ m_status &= ~STATUS_COUNT_UP;
+ break;
+
+ case CONTROL_COUNT_UP_MODE:
+ if (LOG) logerror("HD44102 '%s' Count Up Mode\n", tag());
+
+ m_status |= STATUS_COUNT_UP;
+ break;
+
+ default:
+ {
+ int x = (data & CONTROL_X_ADDRESS_MASK) >> 6;
+ int y = data & CONTROL_Y_ADDRESS_MASK;
+
+ if ((data & CONTROL_Y_ADDRESS_MASK) == CONTROL_DISPLAY_START_PAGE)
+ {
+ if (LOG) logerror("HD44102 '%s' Display Start Page %u\n", tag(), x);
+
+ m_page = x;
+ }
+ else if (y > 49)
+ {
+ logerror("HD44102 '%s' Invalid Address X %u Y %u (%02x)!\n", tag(), data, x, y);
+ }
+ else
+ {
+ if (LOG) logerror("HD44102 '%s' Address X %u Y %u (%02x)\n", tag(), data, x, y);
+
+ m_x = x;
+ m_y = y;
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// data_r - data read
+//-------------------------------------------------
+
+READ8_MEMBER( hd44102_device::data_r )
+{
+ UINT8 data = m_output;
+
+ m_output = m_ram[m_x][m_y];
+
+ count_up_or_down();
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// data_w - data write
+//-------------------------------------------------
+
+WRITE8_MEMBER( hd44102_device::data_w )
+{
+ m_ram[m_x][m_y] = data;
+
+ count_up_or_down();
+}
+
+
+//-------------------------------------------------
+// cs2_w - chip select 2 write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( hd44102_device::cs2_w )
+{
+ m_cs2 = state;
+}
+
+
+//-------------------------------------------------
+// update_screen - update screen
+//-------------------------------------------------
+
+UINT32 hd44102_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ for (int y = 0; y < 50; y++)
+ {
+ int z = m_page << 3;
+
+ for (int x = 0; x < 32; x++)
+ {
+ UINT8 data = m_ram[z / 8][y];
+
+ int sy = m_sy + z;
+ int sx = m_sx + y;
+
+ if (cliprect.contains(sx, sy))
+ {
+ int color = (m_status & STATUS_DISPLAY_OFF) ? 0 : BIT(data, z % 8);
+
+ bitmap.pix16(sy, sx) = color;
+ }
+
+ z++;
+ z %= 32;
+ }
+ }
+ return 0;
+}
diff --git a/src/devices/video/hd44102.h b/src/devices/video/hd44102.h
new file mode 100644
index 00000000000..2ba39282e9e
--- /dev/null
+++ b/src/devices/video/hd44102.h
@@ -0,0 +1,86 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ HD44102 Dot Matrix Liquid Crystal Graphic Display Column Driver emulation
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __HD44102__
+#define __HD44102__
+
+#include "emu.h"
+
+
+
+///*************************************************************************
+// INTERFACE CONFIGURATION MACROS
+///*************************************************************************
+
+#define MCFG_HD44102_ADD(_tag, _screen_tag, _sx, _sy) \
+ MCFG_DEVICE_ADD(_tag, HD44102, 0) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ hd44102_device::static_set_offsets(*device, _sx, _sy);
+
+
+
+///*************************************************************************
+// TYPE DEFINITIONS
+///*************************************************************************
+
+// ======================> hd44102_device
+
+class hd44102_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ hd44102_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // inline configuration helpers
+ static void static_set_offsets(device_t &device, int sx, int sy);
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_WRITE_LINE_MEMBER( cs2_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ DECLARE_READ8_MEMBER( status_r );
+ DECLARE_WRITE8_MEMBER( control_w );
+
+ DECLARE_READ8_MEMBER( data_r );
+ DECLARE_WRITE8_MEMBER( data_w );
+
+ inline void count_up_or_down();
+
+ UINT8 m_ram[4][50]; // display memory
+
+ UINT8 m_status; // status register
+ UINT8 m_output; // output register
+
+ int m_cs2; // chip select
+ int m_page; // display start page
+ int m_x; // X address
+ int m_y; // Y address
+
+ int m_sx;
+ int m_sy;
+};
+
+
+// device type definition
+extern const device_type HD44102;
+
+
+
+#endif
diff --git a/src/devices/video/hd44352.c b/src/devices/video/hd44352.c
new file mode 100644
index 00000000000..8b30b0afd9b
--- /dev/null
+++ b/src/devices/video/hd44352.c
@@ -0,0 +1,439 @@
+// license:BSD-3-Clause
+// copyright-holders:Sandro Ronco
+/***************************************************************************
+
+ Hitachi HD44352 LCD controller
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/hd44352.h"
+
+#define LCD_BYTE_INPUT 0x01
+#define LCD_BYTE_OUTPUT 0x02
+#define LCD_CHAR_OUTPUT 0x03
+#define LCD_ON_OFF 0x04
+#define LCD_CURSOR_GRAPHIC 0x06
+#define LCD_CURSOR_CHAR 0x07
+#define LCD_SCROLL_CHAR_WIDTH 0x08
+#define LCD_CURSOR_STATUS 0x09
+#define LCD_USER_CHARACTER 0x0b
+#define LCD_CONTRAST 0x0c
+#define LCD_IRQ_FREQUENCY 0x0d
+#define LCD_CURSOR_POSITION 0x0e
+
+
+// devices
+const device_type HD44352 = &device_creator<hd44352_device>;
+
+//**************************************************************************
+// live device
+//**************************************************************************
+
+//-------------------------------------------------
+// hd44352_device - constructor
+//-------------------------------------------------
+
+hd44352_device::hd44352_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock):
+ device_t(mconfig, HD44352, "hd44352", tag, owner, clock, "hd44352", __FILE__),
+ m_on_cb(*this)
+{
+}
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+void hd44352_device::device_validity_check(validity_checker &valid) const
+{
+}
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void hd44352_device::device_start()
+{
+ m_on_cb.resolve_safe();
+
+ m_on_timer = timer_alloc(ON_TIMER);
+ m_on_timer->adjust(attotime::from_hz(m_clock/16384), 0, attotime::from_hz(m_clock/16384));
+
+ save_item( NAME(m_control_lines));
+ save_item( NAME(m_data_bus));
+ save_item( NAME(m_state));
+ save_item( NAME(m_offset));
+ save_item( NAME(m_char_width));
+ save_item( NAME(m_bank));
+ save_item( NAME(m_lcd_on));
+ save_item( NAME(m_scroll));
+ save_item( NAME(m_contrast));
+ save_item( NAME(m_byte_count));
+ save_item( NAME(m_cursor_status));
+ save_item( NAME(m_cursor_x));
+ save_item( NAME(m_cursor_y));
+ save_item( NAME(m_cursor_lcd));
+ save_item( NAME(m_video_ram[0]));
+ save_item( NAME(m_video_ram[1]));
+ save_item( NAME(m_par));
+ save_item( NAME(m_cursor));
+ save_item( NAME(m_custom_char[0]));
+ save_item( NAME(m_custom_char[1]));
+ save_item( NAME(m_custom_char[2]));
+ save_item( NAME(m_custom_char[3]));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void hd44352_device::device_reset()
+{
+ memset(m_video_ram, 0x00, sizeof(m_video_ram));
+ memset(m_par, 0x00, sizeof(m_par));
+ memset(m_custom_char, 0x00, sizeof(m_custom_char));
+ memset(m_cursor, 0x00, sizeof(m_cursor));
+ m_control_lines = 0;
+ m_data_bus = 0xff;
+ m_state = 0;
+ m_bank = 0;
+ m_offset = 0;
+ m_char_width = 6;
+ m_lcd_on = 0;
+ m_scroll = 0;
+ m_byte_count = 0;
+ m_cursor_status = 0;
+ m_cursor_x = 0;
+ m_cursor_y = 0;
+ m_cursor_lcd = 0;
+ m_contrast = 0;
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+void hd44352_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case ON_TIMER:
+ if (m_control_lines & 0x40)
+ {
+ m_on_cb(ASSERT_LINE);
+ m_on_cb(CLEAR_LINE);
+ }
+ break;
+ }
+}
+
+//**************************************************************************
+// device interface
+//**************************************************************************
+
+UINT32 hd44352_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ UINT8 cw = m_char_width;
+
+ bitmap.fill(0, cliprect);
+
+ if (m_control_lines&0x80 && m_lcd_on)
+ {
+ for (int a=0; a<2; a++)
+ for (int py=0; py<4; py++)
+ for (int px=0; px<16; px++)
+ if (BIT(m_cursor_status, 4) && px == m_cursor_x && py == m_cursor_y && a == m_cursor_lcd)
+ {
+ //draw the cursor
+ for (int c=0; c<cw; c++)
+ {
+ UINT8 d = compute_newval((m_cursor_status>>5) & 0x07, m_video_ram[a][py*16*cw + px*cw + c + m_scroll * 48], m_cursor[c]);
+ for (int b=0; b<8; b++)
+ {
+ bitmap.pix16(py*8 + b, a*cw*16 + px*cw + c) = BIT(d, 7-b);
+ }
+ }
+ }
+ else
+ {
+ for (int c=0; c<cw; c++)
+ {
+ UINT8 d = m_video_ram[a][py*16*cw + px*cw + c + m_scroll * 48];
+ for (int b=0; b<8; b++)
+ {
+ bitmap.pix16(py*8 + b, a*cw*16 + px*cw + c) = BIT(d, 7-b);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+void hd44352_device::control_write(UINT8 data)
+{
+ if(m_control_lines == data)
+ m_state = 0;
+
+ m_control_lines = data;
+}
+
+UINT8 hd44352_device::compute_newval(UINT8 type, UINT8 oldval, UINT8 newval)
+{
+ switch(type & 0x07)
+ {
+ case 0x00:
+ return (~oldval) & newval;
+ case 0x01:
+ return oldval ^ newval;
+ case 0x03:
+ return oldval & (~newval);
+ case 0x04:
+ return newval;
+ case 0x05:
+ return oldval | newval;
+ case 0x07:
+ return oldval;
+ case 0x02:
+ case 0x06:
+ default:
+ return 0;
+ }
+}
+
+UINT8 hd44352_device::get_char(UINT16 pos)
+{
+ switch ((UINT8)pos/8)
+ {
+ case 0xcf:
+ return m_custom_char[0][pos%8];
+ case 0xdf:
+ return m_custom_char[1][pos%8];
+ case 0xef:
+ return m_custom_char[2][pos%8];
+ case 0xff:
+ return m_custom_char[3][pos%8];
+ default:
+ return region()->u8(pos);
+ }
+}
+
+void hd44352_device::data_write(UINT8 data)
+{
+ // verify that controller is active
+ if (!(m_control_lines&0x80))
+ return;
+
+ if (m_control_lines & 0x01)
+ {
+ if (!(m_control_lines&0x02) && !(m_control_lines&0x04))
+ return;
+
+ switch (m_state)
+ {
+ case 0: //parameter 0
+ m_par[m_state++] = data;
+ break;
+ case 1: //parameter 1
+ m_par[m_state++] = data;
+ break;
+ case 2: //parameter 2
+ m_par[m_state++] = data;
+ break;
+ }
+
+ switch (m_par[0] & 0x0f)
+ {
+ case LCD_BYTE_INPUT:
+ case LCD_CHAR_OUTPUT:
+ {
+ if (m_state == 1)
+ m_bank = BIT(data, 4);
+ else if (m_state == 2)
+ m_offset = ((data>>1)&0x3f) % 48 + (BIT(data,7) * 48);
+ else if (m_state == 3)
+ m_offset += ((data & 0x03) * 96);
+ }
+ break;
+ case LCD_BYTE_OUTPUT:
+ {
+ if (m_state == 1)
+ m_bank = BIT(data, 4);
+ else if (m_state == 2)
+ m_offset = ((data>>1)&0x3f) % 48 + (BIT(data,7) * 48);
+ else if (m_state == 3)
+ m_offset += ((data & 0x03) * 96);
+ }
+ break;
+ case LCD_ON_OFF:
+ {
+ if (m_state == 1)
+ m_lcd_on = BIT(data, 4);
+ m_data_bus = 0xff;
+ m_state = 0;
+ }
+ break;
+ case LCD_SCROLL_CHAR_WIDTH:
+ {
+ if (m_state == 1)
+ {
+ m_char_width = 8-((data>>4)&3);
+ m_scroll = ((data>>6)&3);
+ }
+
+ m_data_bus = 0xff;
+ m_state = 0;
+ }
+ break;
+ case LCD_CURSOR_STATUS:
+ {
+ if (m_state == 1)
+ m_cursor_status = data;
+ m_data_bus = 0xff;
+ m_state = 0;
+ }
+ break;
+ case LCD_CONTRAST:
+ {
+ if (m_state == 1)
+ m_contrast = (m_contrast & 0x00ffff) | (data<<16);
+ else if (m_state == 2)
+ m_contrast = (m_contrast & 0xff00ff) | (data<<8);
+ else if (m_state == 3)
+ {
+ m_contrast = (m_contrast & 0xffff00) | (data<<0);
+ m_state = 0;
+ }
+
+ m_data_bus = 0xff;
+ }
+ break;
+ case LCD_IRQ_FREQUENCY:
+ {
+ if (m_state == 1)
+ {
+ UINT32 on_timer_rate;
+
+ switch((data>>4) & 0x0f)
+ {
+ case 0x00: on_timer_rate = 16384; break;
+ case 0x01: on_timer_rate = 8; break;
+ case 0x02: on_timer_rate = 16; break;
+ case 0x03: on_timer_rate = 32; break;
+ case 0x04: on_timer_rate = 64; break;
+ case 0x05: on_timer_rate = 128; break;
+ case 0x06: on_timer_rate = 256; break;
+ case 0x07: on_timer_rate = 512; break;
+ case 0x08: on_timer_rate = 1024; break;
+ case 0x09: on_timer_rate = 2048; break;
+ case 0x0a: on_timer_rate = 4096; break;
+ case 0x0b: on_timer_rate = 4096; break;
+ default: on_timer_rate = 8192; break;
+ }
+
+ m_on_timer->adjust(attotime::from_hz(m_clock/on_timer_rate), 0, attotime::from_hz(m_clock/on_timer_rate));
+ }
+ m_data_bus = 0xff;
+ m_state = 0;
+ }
+ break;
+ case LCD_CURSOR_POSITION:
+ {
+ if (m_state == 1)
+ m_cursor_lcd = BIT(data, 4); //0:left lcd 1:right lcd;
+ else if (m_state == 2)
+ m_cursor_x = ((data>>1)&0x3f) % 48 + (BIT(data,7) * 48);
+ else if (m_state == 3)
+ {
+ m_cursor_y = data & 0x03;
+ m_state = 0;
+ }
+
+ m_data_bus = 0xff;
+ }
+ break;
+ }
+
+ m_byte_count = 0;
+ m_data_bus = 0xff;
+ }
+ else
+ {
+ switch (m_par[0] & 0x0f)
+ {
+ case LCD_BYTE_INPUT:
+ {
+ if (((m_par[0]>>5) & 0x07) != 0x03)
+ break;
+
+ m_offset %= 0x180;
+ m_data_bus = ((m_video_ram[m_bank][m_offset]<<4)&0xf0) | ((m_video_ram[m_bank][m_offset]>>4)&0x0f);
+ m_offset++; m_byte_count++;
+ }
+ break;
+ case LCD_BYTE_OUTPUT:
+ {
+ m_offset %= 0x180;
+ m_video_ram[m_bank][m_offset] = compute_newval((m_par[0]>>5) & 0x07, m_video_ram[m_bank][m_offset], data);
+ m_offset++; m_byte_count++;
+
+ m_data_bus = 0xff;
+ }
+ break;
+ case LCD_CHAR_OUTPUT:
+ {
+ int char_pos = data*8;
+
+ for (int i=0; i<m_char_width; i++)
+ {
+ m_offset %= 0x180;
+ m_video_ram[m_bank][m_offset] = compute_newval((m_par[0]>>5) & 0x07, m_video_ram[m_bank][m_offset], get_char(char_pos));
+ m_offset++; char_pos++;
+ }
+
+ m_byte_count++;
+ m_data_bus = 0xff;
+ }
+ break;
+ case LCD_CURSOR_GRAPHIC:
+ if (m_byte_count<8)
+ {
+ m_cursor[m_byte_count] = data;
+ m_byte_count++;
+ m_data_bus = 0xff;
+ }
+ break;
+ case LCD_CURSOR_CHAR:
+ if (m_byte_count<1)
+ {
+ UINT8 char_code = ((data<<4)&0xf0) | ((data>>4)&0x0f);
+
+ for (int i=0; i<8; i++)
+ m_cursor[i] = get_char(char_code*8 + i);
+
+ m_byte_count++;
+ m_data_bus = 0xff;
+ }
+ break;
+ case LCD_USER_CHARACTER:
+ if (m_byte_count<8)
+ {
+ m_custom_char[(m_par[1]&0x03)][m_byte_count] = data;
+ m_byte_count++;
+ m_data_bus = 0xff;
+ }
+ break;
+ default:
+ m_data_bus = 0xff;
+ }
+
+ m_state=0;
+ }
+}
+
+UINT8 hd44352_device::data_read()
+{
+ return m_data_bus;
+}
diff --git a/src/devices/video/hd44352.h b/src/devices/video/hd44352.h
new file mode 100644
index 00000000000..b305e8ccfa9
--- /dev/null
+++ b/src/devices/video/hd44352.h
@@ -0,0 +1,81 @@
+// license:BSD-3-Clause
+// copyright-holders:Sandro Ronco
+/***************************************************************************
+
+ Hitachi HD44352 LCD controller
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __hd44352_H__
+#define __hd44352_H__
+
+
+#define MCFG_HD44352_ON_CB(_devcb) \
+ devcb = &hd44352_device::set_on_callback(*device, DEVCB_##_devcb);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> hd44352_device
+
+class hd44352_device :
+ public device_t
+{
+public:
+ // construction/destruction
+ hd44352_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_on_callback(device_t &device, _Object object) { return downcast<hd44352_device &>(device).m_on_cb.set_callback(object); }
+
+ // device interface
+ UINT8 data_read();
+ void data_write(UINT8 data);
+ void control_write(UINT8 data);
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual void device_validity_check(validity_checker &valid) const;
+
+private:
+ UINT8 compute_newval(UINT8 type, UINT8 oldval, UINT8 newval);
+ UINT8 get_char(UINT16 pos);
+
+ static const device_timer_id ON_TIMER = 1;
+ emu_timer *m_on_timer;
+
+ UINT8 m_video_ram[2][0x180];
+ UINT8 m_control_lines;
+ UINT8 m_data_bus;
+ UINT8 m_par[3];
+ UINT8 m_state;
+ UINT16 m_bank;
+ UINT16 m_offset;
+ UINT8 m_char_width;
+ UINT8 m_lcd_on;
+ UINT8 m_scroll;
+ UINT32 m_contrast;
+
+ UINT8 m_custom_char[4][8]; // 4 chars * 8 bytes
+ UINT8 m_byte_count;
+ UINT8 m_cursor_status;
+ UINT8 m_cursor[8];
+ UINT8 m_cursor_x;
+ UINT8 m_cursor_y;
+ UINT8 m_cursor_lcd;
+
+ devcb_write_line m_on_cb; // ON line callback
+};
+
+// device type definition
+extern const device_type HD44352;
+
+#endif
diff --git a/src/devices/video/hd44780.c b/src/devices/video/hd44780.c
new file mode 100644
index 00000000000..b21dc2e04e1
--- /dev/null
+++ b/src/devices/video/hd44780.c
@@ -0,0 +1,569 @@
+// license:BSD-3-Clause
+// copyright-holders:Sandro Ronco
+/***************************************************************************
+
+ Hitachi HD44780 LCD controller
+
+ TODO:
+ - dump internal CGROM
+ - emulate osc pin, determine video timings and busy flag duration from it
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/hd44780.h"
+
+#define LOG 0
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type HD44780 = &device_creator<hd44780_device>;
+const device_type KS0066_F05 = &device_creator<ks0066_f05_device>;
+
+
+//-------------------------------------------------
+// ROM( hd44780 )
+//-------------------------------------------------
+
+ROM_START( hd44780_a00 )
+ ROM_REGION( 0x1000, "cgrom", 0 )
+ ROM_LOAD( "hd44780_a00.bin", 0x0000, 0x1000, BAD_DUMP CRC(01d108e2) SHA1(bc0cdf0c9ba895f22e183c7bd35a3f655f2ca96f)) // from page 17 of the HD44780 datasheet
+ROM_END
+
+ROM_START( ks0066_f05 )
+ ROM_REGION( 0x1000, "cgrom", 0 )
+ ROM_LOAD( "ks0066_f05.bin", 0x0000, 0x1000, BAD_DUMP CRC(af9e7bd6) SHA1(0196e871584ee5d370856e7307c0f9d1466e3e51)) // from page 51 of the KS0066 datasheet
+ROM_END
+
+//**************************************************************************
+// live device
+//**************************************************************************
+
+//-------------------------------------------------
+// hd44780_device - constructor
+//-------------------------------------------------
+
+hd44780_device::hd44780_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, HD44780, "HD44780 A00", tag, owner, clock, "hd44780_a00", __FILE__),
+ m_pixel_update_func(NULL)
+{
+ set_charset_type(CHARSET_HD44780_A00);
+}
+
+hd44780_device::hd44780_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_pixel_update_func(NULL)
+{
+}
+
+ks0066_f05_device::ks0066_f05_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ hd44780_device(mconfig, KS0066_F05, "KS0066 F05", tag, owner, clock, "ks0066_f05", __FILE__)
+{
+ set_charset_type(CHARSET_KS0066_F05);
+}
+
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *hd44780_device::device_rom_region() const
+{
+ switch (m_charset_type)
+ {
+ case CHARSET_HD44780_A00: return ROM_NAME( hd44780_a00 );
+ case CHARSET_KS0066_F05: return ROM_NAME( ks0066_f05 );
+ }
+
+ return NULL;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void hd44780_device::device_start()
+{
+ if (region())
+ m_cgrom = region()->base();
+ else
+ m_cgrom = memregion("cgrom")->base();
+
+ m_busy_timer = timer_alloc(TIMER_BUSY);
+ m_blink_timer = timer_alloc(TIMER_BLINKING);
+ m_blink_timer->adjust(attotime::from_msec(409), 0, attotime::from_msec(409));
+
+ // state saving
+ save_item(NAME(m_busy_flag));
+ save_item(NAME(m_ac));
+ save_item(NAME(m_dr));
+ save_item(NAME(m_ir));
+ save_item(NAME(m_active_ram));
+ save_item(NAME(m_display_on));
+ save_item(NAME(m_cursor_on));
+ save_item(NAME(m_shift_on));
+ save_item(NAME(m_blink_on));
+ save_item(NAME(m_direction));
+ save_item(NAME(m_data_len));
+ save_item(NAME(m_num_line));
+ save_item(NAME(m_char_size));
+ save_item(NAME(m_disp_shift));
+ save_item(NAME(m_blink));
+ save_item(NAME(m_ddram));
+ save_item(NAME(m_cgram));
+ save_item(NAME(m_nibble));
+ save_item(NAME(m_rs_state));
+ save_item(NAME(m_rw_state));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void hd44780_device::device_reset()
+{
+ memset(m_ddram, 0x20, sizeof(m_ddram)); // filled with SPACE char
+ memset(m_cgram, 0, sizeof(m_cgram));
+
+ m_ac = 0;
+ m_dr = 0;
+ m_ir = 0;
+ m_active_ram = DDRAM;
+ m_display_on = false;
+ m_cursor_on = false;
+ m_blink_on = false;
+ m_shift_on = false;
+ m_direction = 1;
+ m_data_len = 8;
+ m_num_line = 1;
+ m_char_size = 8;
+ m_disp_shift = 0;
+ m_blink = false;
+ m_nibble = false;
+ m_first_cmd = true;
+ m_rs_state = 0;
+ m_rw_state = 0;
+
+ set_busy_flag(1520);
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void hd44780_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_BUSY:
+ m_busy_flag = false;
+ break;
+
+ case TIMER_BLINKING:
+ m_blink = !m_blink;
+ break;
+ }
+}
+
+
+//**************************************************************************
+// HELPERS
+//**************************************************************************
+
+void hd44780_device::set_charset_type(int type)
+{
+ m_charset_type = type;
+}
+
+void hd44780_device::set_busy_flag(UINT16 usec)
+{
+ m_busy_flag = true;
+ m_busy_timer->adjust( attotime::from_usec( usec ) );
+}
+
+void hd44780_device::correct_ac()
+{
+ if (m_active_ram == DDRAM)
+ {
+ int max_ac = (m_num_line == 1) ? 0x4f : 0x67;
+
+ if (m_ac > max_ac)
+ m_ac -= max_ac + 1;
+ else if (m_ac < 0)
+ m_ac = max_ac;
+ else if (m_num_line == 2 && m_ac > 0x27 && m_ac < 0x40)
+ m_ac = 0x40 + (m_ac - 0x28);
+ }
+ else
+ m_ac &= 0x3f;
+}
+
+void hd44780_device::update_ac(int direction)
+{
+ if (m_active_ram == DDRAM && m_num_line == 2 && direction == -1 && m_ac == 0x40)
+ m_ac = 0x27;
+ else
+ m_ac += direction;
+
+ correct_ac();
+}
+
+void hd44780_device::shift_display(int direction)
+{
+ m_disp_shift += direction;
+
+ if (m_disp_shift == 0x50)
+ m_disp_shift = 0;
+ else if (m_disp_shift == -1)
+ m_disp_shift = 0x4f;
+}
+
+void hd44780_device::update_nibble(int rs, int rw)
+{
+ if (m_rs_state != rs || m_rw_state != rw)
+ {
+ m_rs_state = rs;
+ m_rw_state = rw;
+ m_nibble = false;
+ }
+
+ m_nibble = !m_nibble;
+}
+
+inline void hd44780_device::pixel_update(bitmap_ind16 &bitmap, UINT8 line, UINT8 pos, UINT8 y, UINT8 x, int state)
+{
+ if (m_pixel_update_func != NULL)
+ {
+ m_pixel_update_func(*this, bitmap, line, pos, y, x, state);
+ }
+ else
+ {
+ UINT8 line_height = (m_char_size == 8) ? m_char_size : m_char_size + 1;
+
+ if (m_lines <= 2)
+ {
+ if (pos < m_chars)
+ bitmap.pix16(line * (line_height + 1) + y, pos * 6 + x) = state;
+ }
+ else if (m_lines <= 4)
+ {
+ if (pos < m_chars*2)
+ {
+ if (pos >= m_chars)
+ {
+ line += 2;
+ pos -= m_chars;
+ }
+
+ if (line < m_lines)
+ bitmap.pix16(line * (line_height + 1) + y, pos * 6 + x) = state;
+ }
+ }
+ else
+ {
+ fatalerror("%s: use a custom callback for this LCD configuration (%d x %d)\n", tag(), m_lines, m_chars);
+ }
+ }
+}
+
+
+//**************************************************************************
+// device interface
+//**************************************************************************
+
+const UINT8 *hd44780_device::render()
+{
+ memset(m_render_buf, 0, sizeof(m_render_buf));
+
+ if (m_display_on)
+ {
+ UINT8 line_size = 80 / m_num_line;
+
+ for (int line = 0; line < m_num_line; line++)
+ {
+ for (int pos = 0; pos < line_size; pos++)
+ {
+ UINT16 char_pos = line * 0x40 + ((pos + m_disp_shift) % line_size);
+
+ int char_base = 0;
+ if (m_ddram[char_pos] < 0x10)
+ {
+ // draw CGRAM characters
+ if (m_char_size == 8)
+ char_base = (m_ddram[char_pos] & 0x07) * 8;
+ else
+ char_base = ((m_ddram[char_pos] >> 1) & 0x03) * 16;
+ }
+ else
+ {
+ // draw CGROM characters
+ char_base = m_ddram[char_pos] * 0x10;
+ }
+
+ const UINT8 * charset = (m_ddram[char_pos] < 0x10) ? m_cgram : m_cgrom;
+ UINT8 *dest = m_render_buf + 16 * (line * line_size + pos);
+ memcpy (dest, charset + char_base, m_char_size);
+
+ if (char_pos == m_ac)
+ {
+ // draw the cursor
+ if (m_cursor_on)
+ dest[m_char_size - 1] = 0x1f;
+
+ if (!m_blink && m_blink_on)
+ memset(dest, 0x1f, m_char_size);
+ }
+ }
+ }
+ }
+
+ return m_render_buf;
+}
+
+UINT32 hd44780_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ bitmap.fill(0, cliprect);
+ const UINT8 *img = render();
+
+ UINT8 line_size = 80 / m_num_line;
+
+ for (int line = 0; line < m_num_line; line++)
+ {
+ for (int pos = 0; pos < line_size; pos++)
+ {
+ const UINT8 *src = img + 16 * (line * line_size + pos);
+ for (int y = 0; y < m_char_size; y++)
+ for (int x = 0; x < 5; x++)
+ pixel_update(bitmap, line, pos, y, x, BIT(src[y], 4 - x));
+ }
+ }
+
+ return 0;
+}
+
+READ8_MEMBER(hd44780_device::read)
+{
+ switch (offset & 0x01)
+ {
+ case 0: return control_read(space, 0);
+ case 1: return data_read(space, 0);
+ }
+
+ return 0;
+}
+
+WRITE8_MEMBER(hd44780_device::write)
+{
+ switch (offset & 0x01)
+ {
+ case 0: control_write(space, 0, data); break;
+ case 1: data_write(space, 0, data); break;
+ }
+}
+
+WRITE8_MEMBER(hd44780_device::control_write)
+{
+ if (m_data_len == 4)
+ {
+ update_nibble(0, 0);
+
+ if (m_nibble)
+ {
+ m_ir = data & 0xf0;
+ return;
+ }
+ else
+ {
+ m_ir |= ((data >> 4) & 0x0f);
+ }
+ }
+ else
+ {
+ m_ir = data;
+ }
+
+ if (BIT(m_ir, 7))
+ {
+ // set DDRAM address
+ m_active_ram = DDRAM;
+ m_ac = m_ir & 0x7f;
+ correct_ac();
+ set_busy_flag(37);
+
+ if (LOG) logerror("HD44780 '%s': set DDRAM address %x\n", tag(), m_ac);
+ return;
+ }
+ else if (BIT(m_ir, 6))
+ {
+ // set CGRAM address
+ m_active_ram = CGRAM;
+ m_ac = m_ir & 0x3f;
+ set_busy_flag(37);
+
+ if (LOG) logerror("HD44780 '%s': set CGRAM address %x\n", tag(), m_ac);
+ return;
+ }
+ else if (BIT(m_ir, 5))
+ {
+ // function set
+ if (!m_first_cmd && m_data_len == (BIT(m_ir, 4) ? 8 : 4) && (m_char_size != (BIT(m_ir, 2) ? 10 : 8) || m_num_line != (BIT(m_ir, 3) + 1)))
+ {
+ logerror("HD44780 '%s': function set cannot be executed after other instructions unless the interface data length is changed\n", tag());
+ return;
+ }
+
+ m_char_size = BIT(m_ir, 2) ? 10 : 8;
+ m_data_len = BIT(m_ir, 4) ? 8 : 4;
+ m_num_line = BIT(m_ir, 3) + 1;
+ correct_ac();
+ set_busy_flag(37);
+
+ if (LOG) logerror("HD44780 '%s': char size 5x%d, data len %d, lines %d\n", tag(), m_char_size, m_data_len, m_num_line);
+ return;
+ }
+ else if (BIT(m_ir, 4))
+ {
+ // cursor or display shift
+ int direction = (BIT(m_ir, 2)) ? +1 : -1;
+
+ if (LOG) logerror("HD44780 '%s': %s shift %d\n", tag(), BIT(m_ir, 3) ? "display" : "cursor", direction);
+
+ if (BIT(m_ir, 3))
+ shift_display(direction);
+ else
+ update_ac(direction);
+
+ set_busy_flag(37);
+ }
+ else if (BIT(m_ir, 3))
+ {
+ // display on/off control
+ m_display_on = BIT(m_ir, 2);
+ m_cursor_on = BIT(m_ir, 1);
+ m_blink_on = BIT(m_ir, 0);
+ set_busy_flag(37);
+
+ if (LOG) logerror("HD44780 '%s': display %d, cursor %d, blink %d\n", tag(), m_display_on, m_cursor_on, m_blink_on);
+ }
+ else if (BIT(m_ir, 2))
+ {
+ // entry mode set
+ m_direction = (BIT(m_ir, 1)) ? +1 : -1;
+ m_shift_on = BIT(m_ir, 0);
+ set_busy_flag(37);
+
+ if (LOG) logerror("HD44780 '%s': entry mode set: direction %d, shift %d\n", tag(), m_direction, m_shift_on);
+ }
+ else if (BIT(m_ir, 1))
+ {
+ // return home
+ if (LOG) logerror("HD44780 '%s': return home\n", tag());
+
+ m_ac = 0;
+ m_active_ram = DDRAM;
+ m_direction = 1;
+ m_disp_shift = 0;
+ set_busy_flag(1520);
+ }
+ else if (BIT(m_ir, 0))
+ {
+ // clear display
+ if (LOG) logerror("HD44780 '%s': clear display\n", tag());
+
+ m_ac = 0;
+ m_active_ram = DDRAM;
+ m_direction = 1;
+ m_disp_shift = 0;
+ memset(m_ddram, 0x20, sizeof(m_ddram));
+ set_busy_flag(1520);
+ }
+
+ m_first_cmd = false;
+}
+
+READ8_MEMBER(hd44780_device::control_read)
+{
+ if (m_data_len == 4)
+ {
+ if (!space.debugger_access())
+ update_nibble(0, 1);
+
+ if (m_nibble)
+ return (m_busy_flag ? 0x80 : 0) | (m_ac & 0x70);
+ else
+ return (m_ac << 4) & 0xf0;
+ }
+ else
+ {
+ return (m_busy_flag ? 0x80 : 0) | (m_ac & 0x7f);
+ }
+}
+
+WRITE8_MEMBER(hd44780_device::data_write)
+{
+ if (m_busy_flag)
+ {
+ logerror("HD44780 '%s': Ignoring data write %02x due of busy flag\n", tag(), data);
+ return;
+ }
+
+ if (m_data_len == 4)
+ {
+ update_nibble(1, 0);
+
+ if (m_nibble)
+ {
+ m_dr = data & 0xf0;
+ return;
+ }
+ else
+ {
+ m_dr |= ((data >> 4) & 0x0f);
+ }
+ }
+ else
+ {
+ m_dr = data;
+ }
+
+ if (LOG) logerror("HD44780 '%s': %sRAM write %x %x '%c'\n", tag(), m_active_ram == DDRAM ? "DD" : "CG", m_ac, m_dr, isprint(m_dr) ? m_dr : '.');
+
+ if (m_active_ram == DDRAM)
+ m_ddram[m_ac] = m_dr;
+ else
+ m_cgram[m_ac] = m_dr;
+
+ update_ac(m_direction);
+ if (m_shift_on)
+ shift_display(m_direction);
+ set_busy_flag(41);
+}
+
+READ8_MEMBER(hd44780_device::data_read)
+{
+ UINT8 data = (m_active_ram == DDRAM) ? m_ddram[m_ac] : m_cgram[m_ac];
+
+ if (LOG) logerror("HD44780 '%s': %sRAM read %x %c\n", tag(), m_active_ram == DDRAM ? "DD" : "CG", m_ac, data);
+
+ if (m_data_len == 4)
+ {
+ if (!space.debugger_access())
+ update_nibble(1, 1);
+
+ if (m_nibble)
+ return data & 0xf0;
+ else
+ data = (data << 4) & 0xf0;
+ }
+
+ if (!space.debugger_access())
+ {
+ update_ac(m_direction);
+ set_busy_flag(41);
+ }
+
+ return data;
+}
diff --git a/src/devices/video/hd44780.h b/src/devices/video/hd44780.h
new file mode 100644
index 00000000000..c175ccb9e47
--- /dev/null
+++ b/src/devices/video/hd44780.h
@@ -0,0 +1,146 @@
+// license:BSD-3-Clause
+// copyright-holders:Sandro Ronco
+/***************************************************************************
+
+ Hitachi HD44780 LCD controller
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __HD44780_H__
+#define __HD44780_H__
+
+
+#define MCFG_HD44780_ADD( _tag ) \
+ MCFG_DEVICE_ADD( _tag, HD44780, 0 )
+
+#define MCFG_KS0066_F05_ADD( _tag ) \
+ MCFG_DEVICE_ADD( _tag, KS0066_F05, 0 )
+
+#define MCFG_HD44780_LCD_SIZE(_lines, _chars) \
+ hd44780_device::static_set_lcd_size(*device, _lines, _chars);
+
+#define MCFG_HD44780_PIXEL_UPDATE_CB(_cb) \
+ hd44780_device::static_set_pixel_update_cb(*device, _cb);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef void (*hd44780_pixel_update_func)(device_t &device, bitmap_ind16 &bitmap, UINT8 line, UINT8 pos, UINT8 y, UINT8 x, int state);
+#define HD44780_PIXEL_UPDATE(name) void name(device_t &device, bitmap_ind16 &bitmap, UINT8 line, UINT8 pos, UINT8 y, UINT8 x, int state)
+
+
+// ======================> hd44780_device
+
+class hd44780_device : public device_t
+{
+public:
+ // construction/destruction
+ hd44780_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ hd44780_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ // static configuration helpers
+ static void static_set_lcd_size(device_t &device, int _lines, int _chars) { hd44780_device &dev=downcast<hd44780_device &>(device); dev.m_lines = _lines; dev.m_chars = _chars; }
+ static void static_set_pixel_update_cb(device_t &device, hd44780_pixel_update_func _cb) { downcast<hd44780_device &>(device).m_pixel_update_func = _cb; }
+
+ // device interface
+ virtual DECLARE_WRITE8_MEMBER(write);
+ virtual DECLARE_READ8_MEMBER(read);
+ virtual DECLARE_WRITE8_MEMBER(control_write);
+ virtual DECLARE_READ8_MEMBER(control_read);
+ virtual DECLARE_WRITE8_MEMBER(data_write);
+ virtual DECLARE_READ8_MEMBER(data_read);
+
+ const UINT8 *render();
+ virtual UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+
+ // charset
+ enum
+ {
+ CHARSET_HD44780_A00,
+ CHARSET_KS0066_F05 /*,
+ CHARSET_HD44780_A01,
+ CHARSET_HD44780_A02,
+ CHARSET_KS0066_F00,
+ CHARSET_KS0066_F03,
+ CHARSET_KS0066_F04,
+ CHARSET_KS0066_F06,
+ CHARSET_KS0066_F59
+ */
+ };
+
+ void set_charset_type(int type);
+
+private:
+ // internal helper
+ void set_busy_flag(UINT16 usec);
+ void correct_ac();
+ void update_ac(int direction);
+ void update_nibble(int rs, int rw);
+ void shift_display(int direction);
+ void pixel_update(bitmap_ind16 &bitmap, UINT8 line, UINT8 pos, UINT8 y, UINT8 x, int state);
+
+ // internal state
+ static const device_timer_id TIMER_BUSY = 0;
+ static const device_timer_id TIMER_BLINKING = 1;
+
+ emu_timer * m_blink_timer;
+ emu_timer * m_busy_timer;
+
+ UINT8 m_lines; // number of lines
+ UINT8 m_chars; // chars for line
+ hd44780_pixel_update_func m_pixel_update_func; // pixel update callback
+
+ bool m_busy_flag; // busy flag
+ UINT8 m_ddram[0x80]; // internal display data RAM
+ UINT8 m_cgram[0x40]; // internal chargen RAM
+ UINT8 * m_cgrom; // internal chargen ROM
+ int m_ac; // address counter
+ UINT8 m_dr; // data register
+ UINT8 m_ir; // instruction register
+ UINT8 m_active_ram; // DDRAM or CGRAM
+ bool m_display_on; // display on/off
+ bool m_cursor_on; // cursor on/off
+ bool m_blink_on; // blink on/off
+ bool m_shift_on; // shift on/off
+ int m_disp_shift; // display shift
+ int m_direction; // auto increment/decrement (-1 or +1)
+ UINT8 m_data_len; // interface data length 4 or 8 bit
+ UINT8 m_num_line; // number of lines
+ UINT8 m_char_size; // char size 5x8 or 5x10
+ bool m_blink;
+ bool m_first_cmd;
+ int m_rs_state;
+ int m_rw_state;
+ bool m_nibble;
+ int m_charset_type;
+ UINT8 m_render_buf[80 * 16];
+
+ enum { DDRAM, CGRAM };
+};
+
+// ======================> ks0066_f05_device
+
+class ks0066_f05_device : public hd44780_device
+{
+public:
+ // construction/destruction
+ ks0066_f05_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+// device type definition
+extern const device_type HD44780;
+extern const device_type KS0066_F05;
+
+#endif
diff --git a/src/devices/video/hd61830.c b/src/devices/video/hd61830.c
new file mode 100644
index 00000000000..a9a21e7dbc5
--- /dev/null
+++ b/src/devices/video/hd61830.c
@@ -0,0 +1,535 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Hitachi HD61830 LCD Timing Controller emulation
+
+**********************************************************************/
+
+#include "hd61830.h"
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type HD61830 = &device_creator<hd61830_device>;
+const device_type HD61830B = &device_creator<hd61830_device>;
+
+
+// default address map
+static ADDRESS_MAP_START( hd61830, AS_0, 8, hd61830_device )
+ AM_RANGE(0x0000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+
+// internal character generator ROM
+ROM_START( hd61830 )
+ ROM_REGION( 0x5c0, "hd61830", 0 ) // internal 7360-bit chargen ROM
+ ROM_LOAD( "hd61830.bin", 0x000, 0x5c0, BAD_DUMP CRC(06a934da) SHA1(bf3f074db5dc92e6f530cb18d6c013563099a87d) ) // typed in from manual
+ROM_END
+
+
+//-------------------------------------------------
+// device_rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *hd61830_device::device_rom_region() const
+{
+ return ROM_NAME(hd61830);
+}
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+static const int CYCLES[] =
+{
+ 4, 4, 4, 4, 4, -1, -1, -1, 4, 4, 4, 4, 6, 6, 36, 36
+};
+
+const int MODE_EXTERNAL_CG = 0x01;
+const int MODE_GRAPHIC = 0x02;
+const int MODE_CURSOR = 0x04;
+const int MODE_BLINK = 0x08;
+const int MODE_MASTER = 0x10;
+const int MODE_DISPLAY_ON = 0x20;
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 hd61830_device::readbyte(offs_t address)
+{
+ return space().read_byte(address);
+}
+
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void hd61830_device::writebyte(offs_t address, UINT8 data)
+{
+ space().write_byte(address, data);
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// hd61830_device - constructor
+//-------------------------------------------------
+
+hd61830_device::hd61830_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, HD61830, "HD61830 LCDC", tag, owner, clock, "hd61830", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_read_rd(*this),
+ m_bf(false),
+ m_cac(0),
+ m_blink(0),
+ m_cursor(0),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(hd61830)),
+ m_char_rom(*this, "hd61830")
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void hd61830_device::device_start()
+{
+ // allocate timers
+ m_busy_timer = timer_alloc();
+
+ // resolve callbacks
+ m_read_rd.resolve_safe(0);
+
+ // register for state saving
+ save_item(NAME(m_bf));
+ save_item(NAME(m_ir));
+ save_item(NAME(m_mcr));
+ save_item(NAME(m_dor));
+ save_item(NAME(m_cac));
+ save_item(NAME(m_dsa));
+ save_item(NAME(m_vp));
+ save_item(NAME(m_hp));
+ save_item(NAME(m_hn));
+ save_item(NAME(m_nx));
+ save_item(NAME(m_cp));
+ save_item(NAME(m_blink));
+ save_item(NAME(m_cursor));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void hd61830_device::device_reset()
+{
+ // display off, slave mode
+ m_mcr &= ~(MODE_MASTER | MODE_DISPLAY_ON);
+
+ // default horizontal pitch
+ m_hp = 6;
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void hd61830_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ // clear busy flag
+ m_bf = false;
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *hd61830_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+/*-------------------------------------------------
+ set_busy_flag - set busy flag and arm timer
+ to clear it later
+-------------------------------------------------*/
+
+void hd61830_device::set_busy_flag()
+{
+ // set busy flag
+ //m_bf = true; TODO figure out correct timing
+
+ // adjust busy timer
+ m_busy_timer->adjust(clocks_to_attotime(CYCLES[m_ir]));
+}
+
+
+//-------------------------------------------------
+// status_r - status register read
+//-------------------------------------------------
+
+READ8_MEMBER( hd61830_device::status_r )
+{
+ if (LOG) logerror("HD61830 '%s' Status Read: %s\n", tag(), m_bf ? "busy" : "ready");
+
+ return m_bf ? 0x80 : 0;
+}
+
+
+//-------------------------------------------------
+// control_w - instruction register write
+//-------------------------------------------------
+
+WRITE8_MEMBER( hd61830_device::control_w )
+{
+ m_ir = data;
+}
+
+
+//-------------------------------------------------
+// data_r - data register read
+//-------------------------------------------------
+
+READ8_MEMBER( hd61830_device::data_r )
+{
+ UINT8 data = m_dor;
+
+ if (LOG) logerror("HD61830 '%s' Display Data Read %02x\n", tag(), m_dor);
+
+ m_dor = readbyte(m_cac);
+
+ m_cac++;
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// data_w - data register write
+//-------------------------------------------------
+
+WRITE8_MEMBER( hd61830_device::data_w )
+{
+ if (m_bf)
+ {
+ logerror("HD61830 '%s' Ignoring data write %02x due to business\n", tag(), data);
+ return;
+ }
+
+ switch (m_ir)
+ {
+ case INSTRUCTION_MODE_CONTROL:
+ m_mcr = data;
+
+ if (LOG)
+ {
+ logerror("HD61830 '%s' %s CG\n", tag(), (data & MODE_EXTERNAL_CG) ? "External" : "Internal");
+ logerror("HD61830 '%s' %s Display Mode\n", tag(), (data & MODE_GRAPHIC) ? "Graphic" : "Character");
+ logerror("HD61830 '%s' %s Mode\n", tag(), (data & MODE_MASTER) ? "Master" : "Slave");
+ logerror("HD61830 '%s' Cursor %s\n", tag(), (data & MODE_CURSOR) ? "On" : "Off");
+ logerror("HD61830 '%s' Blink %s\n", tag(), (data & MODE_BLINK) ? "On" : "Off");
+ logerror("HD61830 '%s' Display %s\n", tag(), (data & MODE_DISPLAY_ON) ? "On" : "Off");
+ }
+ break;
+
+ case INSTRUCTION_CHARACTER_PITCH:
+ m_hp = (data & 0x07) + 1;
+ m_vp = (data >> 4) + 1;
+
+ if (LOG) logerror("HD61830 '%s' Horizontal Character Pitch: %u\n", tag(), m_hp);
+ if (LOG) logerror("HD61830 '%s' Vertical Character Pitch: %u\n", tag(), m_vp);
+ break;
+
+ case INSTRUCTION_NUMBER_OF_CHARACTERS:
+ m_hn = (data & 0x7f) + 1;
+
+ if (LOG) logerror("HD61830 '%s' Number of Characters: %u\n", tag(), m_hn);
+ break;
+
+ case INSTRUCTION_NUMBER_OF_TIME_DIVISIONS:
+ m_nx = (data & 0x7f) + 1;
+
+ if (LOG) logerror("HD61830 '%s' Number of Time Divisions: %u\n", tag(), m_nx);
+ break;
+
+ case INSTRUCTION_CURSOR_POSITION:
+ m_cp = (data & 0x7f) + 1;
+
+ if (LOG) logerror("HD61830 '%s' Cursor Position: %u\n", tag(), m_cp);
+ break;
+
+ case INSTRUCTION_DISPLAY_START_LOW:
+ m_dsa = (m_dsa & 0xff00) | data;
+
+ if (LOG) logerror("HD61830 '%s' Display Start Address Low %04x\n", tag(), m_dsa);
+ break;
+
+ case INSTRUCTION_DISPLAY_START_HIGH:
+ m_dsa = (data << 8) | (m_dsa & 0xff);
+
+ if (LOG) logerror("HD61830 '%s' Display Start Address High %04x\n", tag(), m_dsa);
+ break;
+
+ case INSTRUCTION_CURSOR_ADDRESS_LOW:
+ if (BIT(m_cac, 7) && !BIT(data, 7))
+ {
+ m_cac = (((m_cac >> 8) + 1) << 8) | data;
+ }
+ else
+ {
+ m_cac = (m_cac & 0xff00) | data;
+ }
+
+ if (LOG) logerror("HD61830 '%s' Cursor Address Low %02x: %04x\n", tag(), data, m_cac);
+ break;
+
+ case INSTRUCTION_CURSOR_ADDRESS_HIGH:
+ m_cac = (data << 8) | (m_cac & 0xff);
+
+ if (LOG) logerror("HD61830 '%s' Cursor Address High %02x: %04x\n", tag(), data, m_cac);
+ break;
+
+ case INSTRUCTION_DISPLAY_DATA_WRITE:
+ writebyte(m_cac, data);
+
+ if (LOG) logerror("HD61830 '%s' Display Data Write %02x -> %04x row %u col %u\n", tag(), data, m_cac, m_cac / 40, m_cac % 40);
+
+ m_cac++;
+ break;
+
+ case INSTRUCTION_CLEAR_BIT:
+ {
+ int bit = data & 0x07;
+ UINT8 md = readbyte(m_cac);
+
+ md &= ~(1 << bit);
+
+ if (LOG) logerror("HD61830 '%s' Clear Bit %u at %04x\n", tag(), bit + 1, m_cac);
+
+ writebyte(m_cac, md);
+
+ m_cac++;
+ }
+ break;
+
+ case INSTRUCTION_SET_BIT:
+ {
+ int bit = data & 0x07;
+ UINT8 md = readbyte(m_cac);
+
+ md |= 1 << bit;
+
+ if (LOG) logerror("HD61830 '%s' Set Bit %u at %04x\n", tag(), bit + 1, m_cac);
+
+ writebyte(m_cac, md);
+
+ m_cac++;
+ }
+ break;
+
+ default:
+ logerror("HD61830 '%s' Illegal Instruction %02x!\n", tag(), m_ir);
+ return;
+ }
+
+ // burn cycles
+ set_busy_flag();
+}
+
+
+//-------------------------------------------------
+// draw_scanline - draw one graphics scanline
+//-------------------------------------------------
+
+UINT16 hd61830_device::draw_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, UINT16 ra)
+{
+ for (int sx = 0; sx < m_hn; sx+=2)
+ {
+ UINT8 data1 = readbyte(ra++);
+ UINT8 data2 = readbyte(ra++);
+
+ for (int x = 0; x < m_hp; x++)
+ {
+ if(y >= 0 && y < bitmap.height())
+ {
+ if(((sx * m_hp) + x) >= 0 && ((sx * m_hp) + x) < bitmap.width())
+ bitmap.pix16(y, (sx * m_hp) + x) = BIT(data1, x);
+ if(((sx * m_hp) + x + m_hp) >= 0 && ((sx * m_hp) + x + m_hp) < bitmap.width())
+ bitmap.pix16(y, (sx * m_hp) + x + m_hp) = BIT(data2, x);
+ }
+ }
+ }
+ return ra;
+}
+
+
+//-------------------------------------------------
+// update_graphics - draw graphics mode screen
+//-------------------------------------------------
+
+void hd61830_device::update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ UINT16 rac1 = m_dsa;
+ UINT16 rac2 = rac1 + (m_nx * m_hn);
+ for (int y = 0; y < m_nx; y++)
+ {
+ /* draw upper half scanline */
+ rac1 = draw_scanline(bitmap, cliprect, y, rac1);
+
+ /* draw lower half scanline */
+ rac2 = draw_scanline(bitmap, cliprect, y + m_nx, rac2);
+ }
+}
+
+
+//-------------------------------------------------
+// draw_char - draw a char
+//-------------------------------------------------
+
+void hd61830_device::draw_char(bitmap_ind16 &bitmap, const rectangle &cliprect, UINT16 ma, int x, int y, UINT8 md)
+{
+ for (int cl = 0; cl < m_vp; cl++)
+ {
+ for (int cr = 0; cr < m_hp; cr++)
+ {
+ int sy = y * m_vp + cl;
+ int sx = x * m_hp + cr;
+ UINT8 data = 0;
+
+ if (m_mcr & MODE_EXTERNAL_CG)
+ {
+ data = m_read_rd((cl << 12) | md);
+ }
+ else
+ {
+ UINT16 addr = 0;
+
+ if (md >= 0x20 && md < 0x80 && cl < 7)
+ {
+ // 5x7 characters 0x20..0x7f
+ addr = (md - 0x20) * 7 + cl;
+ }
+ else if (md >= 0xa0 && md < 0xe0 && cl < 7)
+ {
+ // 5x7 characters 0xa0..0xdf
+ addr = 96*7 + (md - 0xa0) * 7 + cl;
+ }
+ else if (md >= 0xe0 && cl < 11)
+ {
+ // 5x11 characters 0xe0..0xff
+ addr = 160*7 + (md - 0xe0) * 11 + cl;
+ }
+
+ data = m_char_rom[addr];
+ }
+
+ int cursor = m_mcr & MODE_CURSOR;
+ int blink = m_mcr & MODE_BLINK;
+
+ // cursor off
+ int pixel = BIT(data, cr);
+
+ if (blink && (ma == m_cac))
+ {
+ // cursor off, character blink
+ if (!cursor)
+ pixel = m_cursor ? pixel : 0;
+
+ // cursor blink
+ if (cursor && (cl == m_cp))
+ pixel = m_cursor ? 1 : 0;
+ }
+ else
+ {
+ // cursor on
+ if (cursor && (cl == m_cp))
+ pixel = m_cursor ? 1 : 0;
+ }
+
+ if (sy < m_screen->height() && sx < m_screen->width())
+ bitmap.pix16(sy, sx) = pixel;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// update_text - draw text mode screen
+//-------------------------------------------------
+
+void hd61830_device::update_text(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ UINT16 ma = 0;
+ for (int y = 0; y < (m_nx / m_vp); y++)
+ {
+ for (int x = 0; x < m_hn; x+=2)
+ {
+ UINT8 md1 = readbyte(ma);
+ UINT8 md2 = readbyte(ma+1);
+
+ draw_char(bitmap, cliprect, ma, x, y, md1);
+ draw_char(bitmap, cliprect, ma+1, x+1, y, md2);
+
+ ma+=2;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// update_screen - update screen
+//-------------------------------------------------
+
+UINT32 hd61830_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ if (m_mcr & MODE_DISPLAY_ON)
+ {
+ if (m_mcr & MODE_GRAPHIC)
+ {
+ update_graphics(bitmap, cliprect);
+ }
+ else
+ {
+ update_text(bitmap, cliprect);
+ }
+ }
+ else
+ {
+ bitmap.fill(0, cliprect);
+ }
+
+ m_blink++;
+
+ if (m_blink == 0x20)
+ {
+ m_blink = 0;
+ m_cursor = !m_cursor;
+ }
+ return 0;
+}
diff --git a/src/devices/video/hd61830.h b/src/devices/video/hd61830.h
new file mode 100644
index 00000000000..359f2d78b1a
--- /dev/null
+++ b/src/devices/video/hd61830.h
@@ -0,0 +1,125 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Hitachi HD61830 LCD Timing Controller emulation
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __HD61830__
+#define __HD61830__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_HD61830_RD_CALLBACK(_read) \
+ devcb = &hd61830_device::set_rd_rd_callback(*device, DEVCB_##_read);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> hd61830_device
+
+class hd61830_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ hd61830_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_rd_rd_callback(device_t &device, _Object object) { return downcast<hd61830_device &>(device).m_read_rd.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( status_r );
+ DECLARE_WRITE8_MEMBER( control_w );
+
+ DECLARE_READ8_MEMBER( data_r );
+ DECLARE_WRITE8_MEMBER( data_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual const rom_entry *device_rom_region() const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ inline UINT8 readbyte(offs_t address);
+ inline void writebyte(offs_t address, UINT8 data);
+
+private:
+ enum
+ {
+ INSTRUCTION_MODE_CONTROL = 0,
+ INSTRUCTION_CHARACTER_PITCH,
+ INSTRUCTION_NUMBER_OF_CHARACTERS,
+ INSTRUCTION_NUMBER_OF_TIME_DIVISIONS,
+ INSTRUCTION_CURSOR_POSITION,
+ INSTRUCTION_DISPLAY_START_LOW = 8,
+ INSTRUCTION_DISPLAY_START_HIGH,
+ INSTRUCTION_CURSOR_ADDRESS_LOW,
+ INSTRUCTION_CURSOR_ADDRESS_HIGH,
+ INSTRUCTION_DISPLAY_DATA_WRITE,
+ INSTRUCTION_DISPLAY_DATA_READ,
+ INSTRUCTION_CLEAR_BIT,
+ INSTRUCTION_SET_BIT
+ };
+
+ void set_busy_flag();
+
+ UINT16 draw_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, UINT16 ra);
+ void update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void draw_char(bitmap_ind16 &bitmap, const rectangle &cliprect, UINT16 ma, int x, int y, UINT8 md);
+ void update_text(bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+ devcb_read8 m_read_rd;
+
+ emu_timer *m_busy_timer;
+ //address_space *m_data;
+
+ bool m_bf; // busy flag
+
+ UINT8 m_ir; // instruction register
+ UINT8 m_mcr; // mode control register
+ UINT8 m_dor; // data output register
+
+ UINT16 m_dsa; // display start address
+ UINT16 m_cac; // cursor address counter
+
+ int m_vp; // vertical character pitch
+ int m_hp; // horizontal character pitch
+ int m_hn; // horizontal number of characters
+ int m_nx; // number of time divisions
+ int m_cp; // cursor position
+
+ int m_blink; // blink counter
+ int m_cursor; // cursor visible
+
+ // address space configurations
+ const address_space_config m_space_config;
+
+ required_region_ptr<UINT8> m_char_rom;
+};
+
+
+// device type definition
+extern const device_type HD61830;
+extern const device_type HD61830B;
+
+
+
+#endif
diff --git a/src/devices/video/hd63484.c b/src/devices/video/hd63484.c
new file mode 100644
index 00000000000..cdfddbcb23d
--- /dev/null
+++ b/src/devices/video/hd63484.c
@@ -0,0 +1,1578 @@
+// license:BSD-3-Clause
+// copyright-holders:Roberto Zandona'
+/***************************************************************************
+
+ HD63484 ACRTC
+ Advanced CRT Controller.
+
+ This chip is used in:
+ - shanghai.c
+ - adp.c
+ - sigmab52.c
+ - wildpkr.c
+
+ ACRTC memory map:
+
+ 00000-3ffff = RAM
+ 40000-7ffff = ROM handled with a hack in the drivers
+ 80000-bffff = unused
+ c0000-fffff = unused
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/hd63484.h"
+
+#define LOG_COMMANDS 0
+
+const device_type HD63484 = &device_creator<hd63484_device>;
+
+hd63484_device::hd63484_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HD63484, "HD63484 CRTC", tag, owner, clock, "hd63484", __FILE__),
+ m_ram(NULL),
+ m_fifo_counter(0),
+ m_readfifo(0),
+ m_org(0),
+ m_org_dpd(0),
+ m_rwp(0),
+ m_cl0(0),
+ m_cl1(0),
+ m_ccmp(0),
+ m_edg(0),
+ m_mask(0),
+ m_ppy(0),
+ m_pzcy(0),
+ m_ppx(0),
+ m_pzcx(0),
+ m_psy(0),
+ m_psx(0),
+ m_pey(0),
+ m_pzy(0),
+ m_pex(0),
+ m_pzx(0),
+ m_xmin(0),
+ m_ymin(0),
+ m_xmax(0),
+ m_ymax(0),
+ m_rwp_dn(0),
+ m_cpx(0),
+ m_cpy(0),
+ m_regno(0),
+ m_skattva_hack(0)
+{
+ memset(m_reg, 0x00, sizeof(m_reg));
+ //m_pattern[16],
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void hd63484_device::device_start()
+{
+ m_ram = auto_alloc_array_clear(machine(), UINT16, HD63484_RAM_SIZE);
+
+ save_pointer(NAME(m_ram), HD63484_RAM_SIZE);
+ save_item(NAME(m_reg));
+ save_item(NAME(m_fifo_counter));
+ save_item(NAME(m_fifo));
+ save_item(NAME(m_readfifo));
+ save_item(NAME(m_pattern));
+ save_item(NAME(m_org));
+ save_item(NAME(m_org_dpd));
+ save_item(NAME(m_rwp));
+ save_item(NAME(m_cl0));
+ save_item(NAME(m_cl1));
+ save_item(NAME(m_ccmp));
+ save_item(NAME(m_edg));
+ save_item(NAME(m_mask));
+ save_item(NAME(m_ppy));
+ save_item(NAME(m_pzcy));
+ save_item(NAME(m_ppx));
+ save_item(NAME(m_pzcx));
+ save_item(NAME(m_psy));
+ save_item(NAME(m_psx));
+ save_item(NAME(m_pey));
+ save_item(NAME(m_pzy));
+ save_item(NAME(m_pex));
+ save_item(NAME(m_pzx));
+ save_item(NAME(m_xmin));
+ save_item(NAME(m_ymin));
+ save_item(NAME(m_xmax));
+ save_item(NAME(m_ymax));
+ save_item(NAME(m_rwp_dn));
+ save_item(NAME(m_cpx));
+ save_item(NAME(m_cpy));
+ save_item(NAME(m_regno));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void hd63484_device::device_reset()
+{
+ m_fifo_counter = 0;
+}
+
+/*****************************************************************************
+ IMPLEMENTATION
+*****************************************************************************/
+
+static const int instruction_length[64] =
+{
+ 0, 3, 2, 1, /* 0x */
+ 0, 0,-1, 2, /* 1x */
+ 0, 3, 3, 3, /* 2x */
+ 0, 0, 0, 0, /* 3x */
+ 0, 1, 2, 2, /* 4x */
+ 0, 0, 4, 4, /* 5x */
+ 5, 5, 5, 5, /* 6x */
+ 5, 5, 5, 5, /* 7x */
+ 3, 3, 3, 3, /* 8x */
+ 3, 3,-2,-2, /* 9x */
+ -2,-2, 2, 4, /* Ax */
+ 5, 5, 7, 7, /* Bx */
+ 3, 3, 1, 1, /* Cx */
+ 2, 2, 2, 2, /* Dx */
+ 5, 5, 5, 5, /* Ex */
+ 5, 5, 5, 5 /* Fx */
+};
+
+static const char *const instruction_name[64] =
+{
+ "undef","ORG ","WPR ","RPR ", /* 0x */
+ "undef","undef","WPTN ","RPTN ", /* 1x */
+ "undef","DRD ","DWT ","DMOD ", /* 2x */
+ "undef","undef","undef","undef", /* 3x */
+ "undef","RD ","WT ","MOD ", /* 4x */
+ "undef","undef","CLR ","SCLR ", /* 5x */
+ "CPY ","CPY ","CPY ","CPY ", /* 6x */
+ "SCPY ","SCPY ","SCPY ","SCPY ", /* 7x */
+ "AMOVE","RMOVE","ALINE","RLINE", /* 8x */
+ "ARCT ","RRCT ","APLL ","RPLL ", /* 9x */
+ "APLG ","RPLG ","CRCL ","ELPS ", /* Ax */
+ "AARC ","RARC ","AEARC","REARC", /* Bx */
+ "AFRCT","RFRCT","PAINT","DOT ", /* Cx */
+ "PTN ","PTN ","PTN ","PTN ", /* Dx */
+ "AGCPY","AGCPY","AGCPY","AGCPY", /* Ex */
+ "RGCPY","RGCPY","RGCPY","RGCPY" /* Fx */
+};
+
+void hd63484_device::doclr16( int opcode, UINT16 fill, int *dst, INT16 _ax, INT16 _ay )
+{
+ INT16 ax,ay;
+
+ ax = _ax;
+ ay = _ay;
+
+ for (;;)
+ {
+ for (;;)
+ {
+ switch (opcode & 0x0003)
+ {
+ case 0:
+ m_ram[*dst] = fill;
+ break;
+ case 1:
+ m_ram[*dst] |= fill;
+ break;
+ case 2:
+ m_ram[*dst] &= fill;
+ break;
+ case 3:
+ m_ram[*dst] ^= fill;
+ break;
+ }
+ if (ax == 0)
+ break;
+ else if (ax > 0)
+ {
+ *dst = (*dst + 1) & (HD63484_RAM_SIZE - 1);
+ ax--;
+ }
+ else
+ {
+ *dst = (*dst - 1) & (HD63484_RAM_SIZE - 1);
+ ax++;
+ }
+ }
+
+ ax = _ax;
+ if (_ay < 0)
+ {
+ *dst = (*dst + (m_reg[0xca/2] & 0x0fff) - ax) & (HD63484_RAM_SIZE - 1);
+ if (ay == 0)
+ break;
+ ay++;
+ }
+ else
+ {
+ *dst = (*dst - (m_reg[0xca/2] & 0x0fff) - ax) & (HD63484_RAM_SIZE - 1);
+ if (ay == 0)
+ break;
+ ay--;
+ }
+ }
+}
+
+void hd63484_device::docpy16( int opcode, int src, int *dst, INT16 _ax, INT16 _ay )
+{
+ int dstep1,dstep2;
+ int ax = _ax;
+ int ay = _ay;
+
+ switch (opcode & 0x0700)
+ {
+ default:
+ case 0x0000: dstep1 = 1; dstep2 = -1 * (m_reg[0xca/2] & 0x0fff) - ax * dstep1; break;
+ case 0x0100: dstep1 = 1; dstep2 = (m_reg[0xca/2] & 0x0fff) - ax * dstep1; break;
+ case 0x0200: dstep1 = -1; dstep2 = -1 * (m_reg[0xca/2] & 0x0fff) + ax * dstep1; break;
+ case 0x0300: dstep1 = -1; dstep2 = (m_reg[0xca/2] & 0x0fff) + ax * dstep1; break;
+ case 0x0400: dstep1 = -1 * (m_reg[0xca/2] & 0x0fff); dstep2 = 1 - ay * dstep1; break;
+ case 0x0500: dstep1 = (m_reg[0xca/2] & 0x0fff); dstep2 = 1 - ay * dstep1; break;
+ case 0x0600: dstep1 = -1 * (m_reg[0xca/2] & 0x0fff); dstep2 = -1 + ay * dstep1; break;
+ case 0x0700: dstep1 = (m_reg[0xca/2] & 0x0fff); dstep2 = -1 + ay * dstep1; break;
+ }
+
+ for (;;)
+ {
+ for (;;)
+ {
+ switch (opcode & 0x0007)
+ {
+ case 0:
+ m_ram[*dst] = m_ram[src];
+ break;
+ case 1:
+ m_ram[*dst] |= m_ram[src];
+ break;
+ case 2:
+ m_ram[*dst] &= m_ram[src];
+ break;
+ case 3:
+ m_ram[*dst] ^= m_ram[src];
+ break;
+ case 4:
+ if (m_ram[*dst] == (m_ccmp & 0xff))
+ m_ram[*dst] = m_ram[src];
+ break;
+ case 5:
+ if (m_ram[*dst] != (m_ccmp & 0xff))
+ m_ram[*dst] = m_ram[src];
+ break;
+ case 6:
+ if (m_ram[*dst] < m_ram[src])
+ m_ram[*dst] = m_ram[src];
+ break;
+ case 7:
+ if (m_ram[*dst] > m_ram[src])
+ m_ram[*dst] = m_ram[src];
+ break;
+ }
+
+ if (opcode & 0x0800)
+ {
+ if (ay == 0) break;
+ if (_ay > 0)
+ {
+ src = (src - (m_reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
+ *dst = (*dst + dstep1) & (HD63484_RAM_SIZE - 1);
+ ay--;
+ }
+ else
+ {
+ src = (src + (m_reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
+ *dst = (*dst + dstep1) & (HD63484_RAM_SIZE - 1);
+ ay++;
+ }
+ }
+ else
+ {
+ if (ax == 0) break;
+ else if (ax > 0)
+ {
+ src = (src + 1) & (HD63484_RAM_SIZE - 1);
+ *dst = (*dst + dstep1) & (HD63484_RAM_SIZE - 1);
+ ax--;
+ }
+ else
+ {
+ src = (src - 1) & (HD63484_RAM_SIZE - 1);
+ *dst = (*dst + dstep1) & (HD63484_RAM_SIZE - 1);
+ ax++;
+ }
+ }
+ }
+
+ if (opcode & 0x0800)
+ {
+ ay = _ay;
+ if (_ax < 0)
+ {
+ src = (src - 1 + ay * (m_reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
+ *dst = (*dst + dstep2) & (HD63484_RAM_SIZE - 1);
+ if (ax == 0) break;
+ ax++;
+ }
+ else
+ {
+ src = (src + 1 - ay * (m_reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
+ *dst = (*dst + dstep2) & (HD63484_RAM_SIZE - 1);
+ if (ax == 0) break;
+ ax--;
+ }
+ }
+ else
+ {
+ ax = _ax;
+ if (_ay < 0)
+ {
+ src = (src + (m_reg[0xca/2] & 0x0fff) - ax) & (HD63484_RAM_SIZE - 1);
+ *dst = (*dst + dstep2) & (HD63484_RAM_SIZE - 1);
+ if (ay == 0) break;
+ ay++;
+ }
+ else
+ {
+ src = (src - (m_reg[0xca/2] & 0x0fff) - ax) & (HD63484_RAM_SIZE - 1);
+ *dst = (*dst + dstep2) & (HD63484_RAM_SIZE - 1);
+ if (ay == 0) break;
+ ay--;
+ }
+ }
+ }
+}
+
+int hd63484_device::org_first_pixel( int _org_dpd )
+{
+ int gbm = (m_reg[0x02/2] & 0x700) >> 8;
+
+ switch (gbm)
+ {
+ case 0:
+ return (_org_dpd & 0x0f);
+ case 1:
+ return (_org_dpd & 0x0e) >> 1;
+ case 2:
+ return (_org_dpd & 0x0c) >> 2;
+ case 3:
+ return (_org_dpd & 0x08) >> 3;
+ case 4:
+ return 0;
+
+ default:
+ logerror ("Graphic bit mode not supported\n");
+ return 0;
+ }
+}
+
+void hd63484_device::dot( int x, int y, int opm, UINT16 color )
+{
+ int dst, x_int, x_mod, bpp;
+ UINT16 color_shifted, bitmask, bitmask_shifted;
+
+ x += org_first_pixel(m_org_dpd);
+
+ switch ((m_reg[0x02/2] & 0x700) >> 8)
+ {
+ case 0:
+ bpp = 1;
+ bitmask = 0x0001;
+ break;
+ case 1:
+ bpp = 2;
+ bitmask = 0x0003;
+ break;
+ case 2:
+ bpp = 4;
+ bitmask = 0x000f;
+ break;
+ case 3:
+ bpp = 8;
+ bitmask = 0x00ff;
+ break;
+ case 4:
+ bpp = 16;
+ bitmask = 0xffff;
+ break;
+
+ default:
+ bpp = 0;
+ bitmask = 0x0000;
+ logerror ("Graphic bit mode not supported\n");
+ }
+
+ // bpp = 4; // for skattva
+ // bitmask = 0x000f; // for skattva
+
+ if (x >= 0)
+ {
+ x_int = x / (16 / bpp);
+ x_mod = x % (16 / bpp);
+ }
+ else
+ {
+ x_int = x / (16 / bpp);
+ x_mod = -1 * (x % (16 / bpp));
+ if (x_mod) {
+ x_int--;
+ x_mod = (16 / bpp) - x_mod;
+ }
+ }
+
+ color &= bitmask;
+
+ bitmask_shifted = bitmask << (x_mod * bpp);
+ color_shifted = color << (x_mod * bpp);
+
+ dst = (m_org + x_int - y * (m_reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
+
+ switch (opm)
+ {
+ case 0:
+ m_ram[dst] = (m_ram[dst] & ~bitmask_shifted) | color_shifted;
+ break;
+ case 1:
+ m_ram[dst] = m_ram[dst] | color_shifted;
+ break;
+ case 2:
+ m_ram[dst] = m_ram[dst] & ((m_ram[dst] & ~bitmask_shifted) | color_shifted);
+ break;
+ case 3:
+ m_ram[dst] = m_ram[dst] ^ color_shifted;
+ break;
+ case 4:
+ if (get_pixel(x, y) == (m_ccmp & bitmask))
+ m_ram[dst] = (m_ram[dst] & ~bitmask_shifted) | color_shifted;
+ break;
+ case 5:
+ if (get_pixel(x, y) != (m_ccmp & bitmask))
+ m_ram[dst] = (m_ram[dst] & ~bitmask_shifted) | color_shifted;
+ break;
+ case 6:
+ if (get_pixel(x, y) < (m_cl0 & bitmask))
+ m_ram[dst] = (m_ram[dst] & ~bitmask_shifted) | color_shifted;
+ break;
+ case 7:
+ if (get_pixel(x, y) > (m_cl0 & bitmask))
+ m_ram[dst] = (m_ram[dst] & ~bitmask_shifted) | color_shifted;
+ break;
+ }
+}
+
+int hd63484_device::get_pixel( int x, int y )
+{
+ int dst, x_int, x_mod, bpp;
+ UINT16 bitmask, bitmask_shifted;
+
+ switch ((m_reg[0x02/2] & 0x700) >> 8)
+ {
+ case 0:
+ bpp = 1;
+ bitmask = 0x0001;
+ break;
+ case 1:
+ bpp = 2;
+ bitmask = 0x0003;
+ break;
+ case 2:
+ bpp = 4;
+ bitmask = 0x000f;
+ break;
+ case 3:
+ bpp = 8;
+ bitmask = 0x00ff;
+ break;
+ case 4:
+ bpp = 16;
+ bitmask = 0xffff;
+ break;
+
+ default:
+ bpp = 0;
+ bitmask = 0x0000;
+ logerror ("Graphic bit mode not supported\n");
+ }
+ if (x >= 0)
+ {
+ x_int = x / (16 / bpp);
+ x_mod = x % (16 / bpp);
+ }
+ else
+ {
+ x_int = x / (16 / bpp);
+ x_mod = -1 * (x % (16 / bpp));
+ if (x_mod) {
+ x_int--;
+ x_mod = (16 / bpp) - x_mod;
+ }
+ }
+
+ bitmask_shifted = bitmask << (x_mod * bpp);
+
+ dst = (m_org + x_int - y * (m_reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
+
+ return ((m_ram[dst] & bitmask_shifted) >> (x_mod * bpp));
+}
+
+int hd63484_device::get_pixel_ptn( int x, int y )
+{
+ int dst, x_int, x_mod, bpp;
+ UINT16 bitmask, bitmask_shifted;
+
+ bpp = 1;
+ bitmask = 0x0001;
+
+ if (x >= 0)
+ {
+ x_int = x / (16 / bpp);
+ x_mod = x % (16 / bpp);
+ }
+ else
+ {
+ x_int = x / (16 / bpp);
+ x_mod = -1 * (x % (16 / bpp));
+ if (x_mod) {
+ x_int--;
+ x_mod = (16 / bpp) - x_mod;
+ }
+ }
+
+ bitmask_shifted = bitmask << (x_mod * bpp);
+
+ dst = (x_int + y * 1);
+
+ if ((m_pattern[dst] & bitmask_shifted) >> (x_mod * bpp))
+ return 1;
+ else
+ return 0;
+}
+
+void hd63484_device::agcpy( int opcode, int src_x, int src_y, int dst_x, int dst_y, INT16 _ax, INT16 _ay )
+{
+ int dst_step1_x,dst_step1_y,dst_step2_x,dst_step2_y;
+ int src_step1_x,src_step1_y,src_step2_x,src_step2_y;
+ int ax_neg,ay_neg;
+ int ax = _ax;
+ int ay = _ay;
+ int xxs = src_x;
+ int yys = src_y;
+ int xxd = dst_x;
+ int yyd = dst_y;
+
+ if (ax < 0)
+ ax_neg = -1;
+ else
+ ax_neg = 1;
+ if (ay < 0)
+ ay_neg = -1;
+ else
+ ay_neg = 1;
+
+ if (opcode & 0x0800)
+ switch (opcode & 0x0700)
+ {
+ default:
+ case 0x0000: dst_step1_x = 1; dst_step1_y = 0; dst_step2_x = -ay_neg*ay; dst_step2_y = 1; break;
+ case 0x0100: dst_step1_x = 1; dst_step1_y = 0; dst_step2_x = -ay_neg*ay; dst_step2_y = -1; break;
+ case 0x0200: dst_step1_x = -1; dst_step1_y = 0; dst_step2_x = ay_neg*ay; dst_step2_y = 1; break;
+ case 0x0300: dst_step1_x = -1; dst_step1_y = 0; dst_step2_x = ay_neg*ay; dst_step2_y = -1; break;
+ case 0x0400: dst_step1_x = 0; dst_step1_y = 1; dst_step2_x = 1; dst_step2_y = -ay_neg*ay; break;
+ case 0x0500: dst_step1_x = 0; dst_step1_y = -1; dst_step2_x = 1; dst_step2_y = ay_neg*ay; break;
+ case 0x0600: dst_step1_x = 0; dst_step1_y = 1; dst_step2_x = -1; dst_step2_y = -ay_neg*ay; break;
+ case 0x0700: dst_step1_x = 0; dst_step1_y = -1; dst_step2_x = -1; dst_step2_y = ay_neg*ay; break;
+ }
+ else
+ switch (opcode & 0x0700)
+ {
+ default:
+ case 0x0000: dst_step1_x = 1; dst_step1_y = 0; dst_step2_x = -ax_neg*ax; dst_step2_y = 1; break;
+ case 0x0100: dst_step1_x = 1; dst_step1_y = 0; dst_step2_x = -ax_neg*ax; dst_step2_y = -1; break;
+ case 0x0200: dst_step1_x = -1; dst_step1_y = 0; dst_step2_x = ax_neg*ax; dst_step2_y = 1; break;
+ case 0x0300: dst_step1_x = -1; dst_step1_y = 0; dst_step2_x = ax_neg*ax; dst_step2_y = -1; break;
+ case 0x0400: dst_step1_x = 0; dst_step1_y = 1; dst_step2_x = 1; dst_step2_y = ax_neg*ax; break;
+ case 0x0500: dst_step1_x = 0; dst_step1_y = -1; dst_step2_x = 1; dst_step2_y = -ax_neg*ax; break;
+ case 0x0600: dst_step1_x = 0; dst_step1_y = 1; dst_step2_x = -1; dst_step2_y = ax_neg*ax; break;
+ case 0x0700: dst_step1_x = 0; dst_step1_y = -1; dst_step2_x = -1; dst_step2_y = -ax_neg*ax; break;
+ }
+
+ if ((_ax >= 0) && (_ay >= 0) && ((opcode & 0x0800) == 0x0000))
+ { src_step1_x = 1; src_step1_y = 0; src_step2_x = -ax; src_step2_y = 1; }
+ else if ((_ax >= 0) && (_ay < 0) && ((opcode & 0x0800) == 0x0000))
+ { src_step1_x = 1; src_step1_y = 0; src_step2_x = -ax; src_step2_y = -1; }
+ else if ((_ax < 0) && (_ay >= 0) && ((opcode & 0x0800) == 0x0000))
+ { src_step1_x = -1; src_step1_y = 0; src_step2_x = -ax; src_step2_y = 1; }
+ else if ((_ax < 0) && (_ay < 0) && ((opcode & 0x0800) == 0x0000))
+ { src_step1_x = -1; src_step1_y = 0; src_step2_x = -ax; src_step2_y = -1; }
+ else if ((_ax >= 0) && (_ay >= 0) && ((opcode & 0x0800) == 0x0800))
+ { src_step1_x = 0; src_step1_y = 1; src_step2_x = 1; src_step2_y = -ay; }
+ else if ((_ax >= 0) && (_ay < 0) && ((opcode & 0x0800) == 0x0800))
+ { src_step1_x = 0; src_step1_y = -1; src_step2_x = 1; src_step2_y = -ay; }
+ else if ((_ax < 0) && (_ay >= 0) && ((opcode & 0x0800) == 0x0800))
+ { src_step1_x = 0; src_step1_y = 1; src_step2_x = -1; src_step2_y = -ay; }
+ else // ((_ax < 0) && (_ay < 0) && ((opcode & 0x0800) == 0x0800))
+ { src_step1_x = 0; src_step1_y = -1; src_step2_x = -1; src_step2_y = -ay; }
+
+ for (;;)
+ {
+ for (;;)
+ {
+ dot(xxd, yyd, opcode & 0x0007, get_pixel(xxs, yys));
+
+ if (opcode & 0x0800)
+ {
+ if (ay == 0) break;
+ if (_ay > 0)
+ {
+ xxs += src_step1_x;
+ yys += src_step1_y;
+ xxd += dst_step1_x;
+ yyd += dst_step1_y;
+ ay--;
+ }
+ else
+ {
+ xxs += src_step1_x;
+ yys += src_step1_y;
+ xxd += dst_step1_x;
+ yyd += dst_step1_y;
+ ay++;
+ }
+ }
+ else
+ {
+ if (ax == 0) break;
+ else if (ax > 0)
+ {
+ xxs += src_step1_x;
+ yys += src_step1_y;
+ xxd += dst_step1_x;
+ yyd += dst_step1_y;
+ ax--;
+ }
+ else
+ {
+ xxs += src_step1_x;
+ yys += src_step1_y;
+ xxd += dst_step1_x;
+ yyd += dst_step1_y;
+ ax++;
+ }
+ }
+ }
+
+ if (opcode & 0x0800)
+ {
+ ay = _ay;
+ if (_ax < 0)
+ {
+ xxs += src_step2_x;
+ yys += src_step2_y;
+ xxd += dst_step2_x;
+ yyd += dst_step2_y;
+ if (ax == 0) break;
+ ax++;
+ }
+ else
+ {
+ xxs += src_step2_x;
+ yys += src_step2_y;
+ xxd += dst_step2_x;
+ yyd += dst_step2_y;
+ if (ax == 0) break;
+ ax--;
+ }
+ }
+ else
+ {
+ ax = _ax;
+ if (_ay < 0)
+ {
+ xxs += src_step2_x;
+ yys += src_step2_y;
+ xxd += dst_step2_x;
+ yyd += dst_step2_y;
+ if (ay == 0) break;
+ ay++;
+ }
+ else
+ {
+ xxs += src_step2_x;
+ yys += src_step2_y;
+ xxd += dst_step2_x;
+ yyd += dst_step2_y;
+ if (ay == 0) break;
+ ay--;
+ }
+ }
+ }
+}
+
+void hd63484_device::ptn( int opcode, int src_x, int src_y, INT16 _ax, INT16 _ay )
+{
+ int dst_step1_x = 0,dst_step1_y = 0,dst_step2_x = 0,dst_step2_y = 0;
+ int src_step1_x,src_step1_y,src_step2_x,src_step2_y;
+ int ax = _ax;
+ int ay = _ay;
+ int ax_neg; //,ay_neg;
+ int xxs = src_x;
+ int yys = src_y;
+ int xxd = m_cpx;
+ int yyd = m_cpy;
+ int getpixel;
+
+ if (ax < 0)
+ ax_neg = -1;
+ else
+ ax_neg = 1;
+/* if (ay < 0)
+ ay_neg = -1;
+ else
+ ay_neg = 1;*/
+
+ if (opcode & 0x0800)
+ switch (opcode & 0x0700)
+ {
+ default:
+ case 0x0000: logerror("PTN: not supported"); break;
+ case 0x0100: logerror("PTN: not supported"); break;
+ case 0x0200: logerror("PTN: not supported"); break;
+ case 0x0300: logerror("PTN: not supported"); break;
+ case 0x0400: logerror("PTN: not supported"); break;
+ case 0x0500: logerror("PTN: not supported"); break;
+ case 0x0600: logerror("PTN: not supported"); break;
+ case 0x0700: logerror("PTN: not supported"); break;
+ }
+ else
+ switch (opcode & 0x0700)
+ {
+ default:
+ case 0x0000: dst_step1_x = 1; dst_step1_y = 0; dst_step2_x = -ax_neg*ax; dst_step2_y = 1; break;
+ case 0x0100: logerror("PTN: not supported"); break;
+ case 0x0200: dst_step1_x = 0; dst_step1_y = 1; dst_step2_x = -1; dst_step2_y = -ax_neg*ax; break;
+ case 0x0300: logerror("PTN: not supported"); break;
+ case 0x0400: dst_step1_x = -1; dst_step1_y = 0; dst_step2_x = ax_neg*ax; dst_step2_y = -1; break;
+ case 0x0500: logerror("PTN: not supported"); break;
+ case 0x0600: dst_step1_x = 0; dst_step1_y = -1; dst_step2_x = 1; dst_step2_y = ax_neg*ax; break;
+ case 0x0700: logerror("PTN: not supported"); break;
+ }
+
+ src_step1_x = 1; src_step1_y = 0; src_step2_x = -ax; src_step2_y = 1;
+
+ for (;;)
+ {
+ for (;;)
+ {
+ getpixel = get_pixel_ptn(xxs, yys);
+ switch ((opcode & 0x0018) >> 3)
+ {
+ case 0x0000:
+ if (getpixel)
+ dot(xxd, yyd, opcode & 0x0007, m_cl1);
+ else
+ dot(xxd, yyd, opcode & 0x0007, m_cl0);
+ break;
+ case 0x0001:
+ if (getpixel)
+ dot(xxd, yyd, opcode & 0x0007, m_cl1);
+ break;
+ case 0x0002:
+ if (getpixel == 0)
+ dot(xxd, yyd, opcode & 0x0007, m_cl0);
+ break;
+ case 0x0003:
+ logerror("PTN: not supported");
+ break;
+ }
+
+ if (opcode & 0x0800)
+ {
+ if (ay == 0) break;
+ if (_ay > 0)
+ {
+ xxs += src_step1_x;
+ yys += src_step1_y;
+ xxd += dst_step1_x;
+ yyd += dst_step1_y;
+ ay--;
+ }
+ else
+ {
+ xxs += src_step1_x;
+ yys += src_step1_y;
+ xxd += dst_step1_x;
+ yyd += dst_step1_y;
+ ay++;
+ }
+ }
+ else
+ {
+ if (ax == 0) break;
+ else if (ax > 0)
+ {
+ xxs += src_step1_x;
+ yys += src_step1_y;
+ xxd += dst_step1_x;
+ yyd += dst_step1_y;
+ ax--;
+ }
+ else
+ {
+ xxs += src_step1_x;
+ yys += src_step1_y;
+ xxd += dst_step1_x;
+ yyd += dst_step1_y;
+ ax++;
+ }
+ }
+ }
+
+ if (opcode & 0x0800)
+ {
+ ay = _ay;
+ if (_ax < 0)
+ {
+ xxs += src_step2_x;
+ yys += src_step2_y;
+ xxd += dst_step2_x;
+ yyd += dst_step2_y;
+ if (ax == 0) break;
+ ax++;
+ }
+ else
+ {
+ xxs += src_step2_x;
+ yys += src_step2_y;
+ xxd += dst_step2_x;
+ yyd += dst_step2_y;
+ if (ax == 0) break;
+ ax--;
+ }
+ }
+ else
+ {
+ ax = _ax;
+ if (_ay < 0)
+ {
+ xxs += src_step2_x;
+ yys += src_step2_y;
+ xxd += dst_step2_x;
+ yyd += dst_step2_y;
+ if (ay == 0) break;
+ ay++;
+ }
+ else
+ {
+ xxs += src_step2_x;
+ yys += src_step2_y;
+ xxd += dst_step2_x;
+ yyd += dst_step2_y;
+ if (ay == 0) break;
+ ay--;
+ }
+ }
+ }
+}
+
+void hd63484_device::line( INT16 sx, INT16 sy, INT16 ex, INT16 ey, INT16 col )
+{
+ INT16 ax,ay;
+
+ int cpx_t = sx;
+ int cpy_t = sy;
+
+ ax = ex - sx;
+ ay = ey - sy;
+
+ if (abs(ax) >= abs(ay))
+ {
+ while (ax)
+ {
+ dot(cpx_t, cpy_t, col & 7, m_cl0);
+
+ if (ax > 0)
+ {
+ cpx_t++;
+ ax--;
+ }
+ else
+ {
+ cpx_t--;
+ ax++;
+ }
+ cpy_t = sy + ay * (cpx_t - sx) / (ex - sx);
+ }
+ }
+ else
+ {
+ while (ay)
+ {
+ dot(cpx_t, cpy_t, col & 7, m_cl0);
+
+ if (ay > 0)
+ {
+ cpy_t++;
+ ay--;
+ }
+ else
+ {
+ cpy_t--;
+ ay++;
+ }
+ cpx_t = sx + ax * (cpy_t - sy) / (ey - sy);
+ }
+ }
+
+}
+
+void hd63484_device::circle( INT16 sx, INT16 sy, UINT16 r, INT16 col )
+{
+ const float DEG2RAD = 3.14159f/180;
+ int i;
+
+ for (i = 0; i < 360 * (r / 10); i++)
+ {
+ float degInRad = i * DEG2RAD / (r / 10);
+ dot(sx + cos(degInRad) * r,sy + sin(degInRad) * r, col & 7, m_cl0);
+ }
+}
+
+void hd63484_device::paint( int sx, int sy, int col )
+{
+ int getpixel;
+ dot(sx, sy, 0, col);
+
+ getpixel = get_pixel(sx+1,sy);
+ switch ((m_reg[0x02/2] & 0x700) >> 8)
+ {
+ case 0:
+ break;
+ case 1:
+ break;
+ case 2:
+ getpixel = (getpixel << 12) | (getpixel << 8) | (getpixel << 4) | (getpixel << 0);
+ break;
+ case 3:
+ getpixel = (getpixel << 8) | (getpixel << 0);
+ break;
+ case 4:
+ break;
+
+ default:
+ logerror ("Graphic bit mode not supported\n");
+ }
+ if ((getpixel != col) && (getpixel != m_edg))
+ {
+ sx++;
+ paint(sx, sy, col);
+ sx--;
+ }
+
+ getpixel = get_pixel(sx - 1, sy);
+ switch ((m_reg[0x02/2] & 0x700) >> 8)
+ {
+ case 0:
+ break;
+ case 1:
+ break;
+ case 2:
+ getpixel = (getpixel << 12) | (getpixel << 8) | (getpixel << 4) | (getpixel << 0);
+ break;
+ case 3:
+ getpixel = (getpixel << 8) | (getpixel << 0);
+ break;
+ case 4:
+ break;
+
+ default:
+ logerror ("Graphic bit mode not supported\n");
+ }
+ if ((getpixel != col) && (getpixel != m_edg))
+ {
+ sx--;
+ paint(sx, sy, col);
+ sx++;
+ }
+
+ getpixel = get_pixel(sx, sy + 1);
+ switch ((m_reg[0x02/2] & 0x700) >> 8)
+ {
+ case 0:
+ break;
+ case 1:
+ break;
+ case 2:
+ getpixel = (getpixel << 12) | (getpixel << 8) | (getpixel << 4) | (getpixel << 0);
+ break;
+ case 3:
+ getpixel = (getpixel << 8) | (getpixel << 0);
+ break;
+ case 4:
+ break;
+
+ default:
+ logerror ("Graphic bit mode not supported\n");
+ }
+ if ((getpixel != col) && (getpixel != m_edg))
+ {
+ sy++;
+ paint(sx, sy, col);
+ sy--;
+ }
+
+ getpixel = get_pixel(sx, sy - 1);
+ switch ((m_reg[0x02/2] & 0x700) >> 8)
+ {
+ case 0:
+ break;
+ case 1:
+ break;
+ case 2:
+ getpixel = (getpixel << 12) | (getpixel << 8) | (getpixel << 4) | (getpixel << 0);
+ break;
+ case 3:
+ getpixel = (getpixel << 8) | (getpixel << 0);
+ break;
+ case 4:
+ break;
+
+ default:
+ logerror ("Graphic bit mode not supported\n");
+ }
+ if ((getpixel != col) && (getpixel != m_edg))
+ {
+ sy--;
+ paint(sx, sy, col);
+ sy++;
+ }
+}
+
+void hd63484_device::command_w(UINT16 cmd)
+{
+ int len;
+
+ m_fifo[m_fifo_counter++] = cmd;
+
+ len = instruction_length[m_fifo[0] >> 10];
+ if (len == -1)
+ {
+ if (m_fifo_counter < 2) return;
+ else len = m_fifo[1] + 2;
+ }
+ else if (len == -2)
+ {
+ if (m_fifo_counter < 2) return;
+ else len = 2 * m_fifo[1] + 2;
+ }
+
+ if (m_fifo_counter >= len)
+ {
+#if LOG_COMMANDS
+ int i;
+
+ logerror("%s: HD63484 command %s (%04x) ", machine().describe_context(), instruction_name[m_fifo[0] >> 10], m_fifo[0]);
+ for (i = 1; i < m_fifo_counter; i++)
+ logerror("%04x ", m_fifo[i]);
+ logerror("\n");
+#endif
+
+ if (m_fifo[0] == 0x0400) { /* ORG */
+ m_org = ((m_fifo[1] & 0x00ff) << 12) | ((m_fifo[2] & 0xfff0) >> 4);
+ m_org_dpd = m_fifo[2] & 0x000f;
+ }
+ else if ((m_fifo[0] & 0xffe0) == 0x0800) /* WPR */
+ {
+ if (m_fifo[0] == 0x0800)
+ m_cl0 = m_fifo[1];
+ else if (m_fifo[0] == 0x0801)
+ m_cl1 = m_fifo[1];
+ else if (m_fifo[0] == 0x0802)
+ m_ccmp = m_fifo[1];
+ else if (m_fifo[0] == 0x0803)
+ m_edg = m_fifo[1];
+ else if (m_fifo[0] == 0x0804)
+ m_mask = m_fifo[1];
+ else if (m_fifo[0] == 0x0805)
+ {
+ m_ppy = (m_fifo[1] & 0xf000) >> 12;
+ m_pzcy = (m_fifo[1] & 0x0f00) >> 8;
+ m_ppx = (m_fifo[1] & 0x00f0) >> 4;
+ m_pzcx = (m_fifo[1] & 0x000f) >> 0;
+ }
+ else if (m_fifo[0] == 0x0806)
+ {
+ m_psy = (m_fifo[1] & 0xf000) >> 12;
+ m_psx = (m_fifo[1] & 0x00f0) >> 4;
+ }
+ else if (m_fifo[0] == 0x0807)
+ {
+ m_pey = (m_fifo[1] & 0xf000) >> 12;
+ m_pzy = (m_fifo[1] & 0x0f00) >> 8;
+ m_pex = (m_fifo[1] & 0x00f0) >> 4;
+ m_pzx = (m_fifo[1] & 0x000f) >> 0;
+ }
+ else if (m_fifo[0] == 0x0808)
+ m_xmin = m_fifo[1];
+ else if (m_fifo[0] == 0x0809)
+ m_ymin = m_fifo[1];
+ else if (m_fifo[0] == 0x080a)
+ m_xmax = m_fifo[1];
+ else if (m_fifo[0] == 0x080b)
+ m_ymax = m_fifo[1];
+ else if (m_fifo[0] == 0x080c)
+ {
+ m_rwp = (m_rwp & 0x00fff) | ((m_fifo[1] & 0x00ff) << 12);
+ m_rwp_dn = (m_fifo[1] & 0xc000) >> 14;
+ }
+ else if (m_fifo[0] == 0x080d)
+ {
+ m_rwp = (m_rwp & 0xff000) | ((m_fifo[1] & 0xfff0) >> 4);
+ }
+ else
+ logerror("unsupported register\n");
+ }
+ else if ((m_fifo[0] & 0xffe0) == 0x0c00) /* RPR */
+ {
+ if (m_fifo[0] == 0x0c00)
+ m_fifo[1] = m_cl0;
+ else if (m_fifo[0] == 0x0c01)
+ m_fifo[1] = m_cl1;
+ else if (m_fifo[0] == 0x0c02)
+ m_fifo[1] = m_ccmp;
+ else if (m_fifo[0] == 0x0c03)
+ m_fifo[1] = m_edg;
+ else if (m_fifo[0] == 0x0c04)
+ m_fifo[1] = m_mask;
+ else if (m_fifo[0] == 0x0c05)
+ {
+ m_fifo[1] = (m_ppy << 12) | (m_pzcy << 8) | (m_ppx << 4) | m_pzcx;
+ }
+ else if (m_fifo[0] == 0x0c06)
+ {
+ m_fifo[1] = (m_psx << 12) | (m_psx << 4);
+ }
+ else if (m_fifo[0] == 0x0c07)
+ {
+ m_fifo[1] = (m_pey << 12) | (m_pzy << 8) | (m_pex << 4) | m_pzx;
+ }
+ else if (m_fifo[0] == 0x0c08)
+ m_fifo[1] = m_xmin;
+ else if (m_fifo[0] == 0x0c09)
+ m_fifo[1] = m_ymin;
+ else if (m_fifo[0] == 0x0c0a)
+ m_fifo[1] = m_xmax;
+ else if (m_fifo[0] == 0x0c0b)
+ m_fifo[1] = m_ymax;
+ else if (m_fifo[0] == 0x0c0c)
+ {
+ m_fifo[1] = (m_rwp_dn << 14) | ((m_rwp >> 12) & 0xff);
+ }
+ else if (m_fifo[0] == 0x0c0d)
+ {
+ m_fifo[1] = (m_rwp & 0x0fff) << 4;
+ }
+ else if (m_fifo[0] == 0x0c10)
+ {
+ // TODO
+ }
+ else if (m_fifo[0] == 0x0c11)
+ {
+ // TODO
+ }
+ else if (m_fifo[0] == 0x0c12)
+ {
+ m_fifo[1] = m_cpx;
+ }
+ else if (m_fifo[0] == 0x0c13)
+ {
+ m_fifo[1] = m_cpy;
+ }
+ else
+ logerror("unsupported register\n");
+ }
+ else if ((m_fifo[0] & 0xfff0) == 0x1800) /* WPTN */
+ {
+ int i;
+ int start = m_fifo[0] & 0x000f;
+ int n = m_fifo[1];
+ for (i = 0; i < n; i++)
+ m_pattern[start + i] = m_fifo[2 + i];
+ }
+ else if (m_fifo[0] == 0x4400) /* RD */
+ {
+ m_readfifo = m_ram[m_rwp];
+ m_rwp = (m_rwp + 1) & (HD63484_RAM_SIZE - 1);
+ }
+ else if (m_fifo[0] == 0x4800) /* WT */
+ {
+ m_ram[m_rwp] = m_fifo[1];
+ m_rwp = (m_rwp + 1) & (HD63484_RAM_SIZE - 1);
+ }
+ else if (m_fifo[0] == 0x5800) /* CLR */
+ {
+ doclr16(m_fifo[0], m_fifo[1], &m_rwp, m_fifo[2], m_fifo[3]);
+
+ {
+ int fifo2 = (int)m_fifo[2], fifo3 = (int)m_fifo[3];
+ if (fifo2 < 0) fifo2 *= -1;
+ if (fifo3 < 0) fifo3 *= -1;
+ m_rwp += ((fifo2 + 1) * (fifo3 + 1));
+ }
+
+ }
+ else if ((m_fifo[0] & 0xfffc) == 0x5c00) /* SCLR */
+ {
+ doclr16(m_fifo[0], m_fifo[1], &m_rwp, m_fifo[2], m_fifo[3]);
+
+ {
+ int fifo2 = (int)m_fifo[2], fifo3 = (int)m_fifo[3];
+ if (fifo2 < 0) fifo2 *= -1;
+ if (fifo3 < 0) fifo3 *= -1;
+ m_rwp += ((fifo2 + 1) * (fifo3 + 1));
+ }
+
+ }
+ else if ((m_fifo[0] & 0xf0ff) == 0x6000) /* CPY */
+ {
+ docpy16(m_fifo[0], ((m_fifo[1] & 0x00ff) << 12) | ((m_fifo[2] & 0xfff0) >> 4), &m_rwp, m_fifo[3], m_fifo[4]);
+
+ {
+ int fifo2 = (int)m_fifo[2], fifo3 = (int)m_fifo[3];
+ if (fifo2 < 0) fifo2 *= -1;
+ if (fifo3 < 0) fifo3 *= -1;
+ m_rwp += ((fifo2 + 1) * (fifo3 + 1));
+ }
+
+ }
+ else if ((m_fifo[0] & 0xf0fc) == 0x7000) /* SCPY */
+ {
+ docpy16(m_fifo[0], ((m_fifo[1] & 0x00ff) << 12) | ((m_fifo[2] & 0xfff0) >> 4), &m_rwp, m_fifo[3], m_fifo[4]);
+
+ {
+ int fifo2 = (int)m_fifo[2], fifo3 = (int)m_fifo[3];
+ if (fifo2 < 0) fifo2 *= -1;
+ if (fifo3 < 0) fifo3 *= -1;
+ m_rwp += ((fifo2 + 1) * (fifo3 + 1));
+ }
+
+ }
+ else if (m_fifo[0] == 0x8000) /* AMOVE */
+ {
+ m_cpx = m_fifo[1];
+ m_cpy = m_fifo[2];
+ }
+ else if (m_fifo[0] == 0x8400) /* RMOVE */
+ {
+ m_cpx += (INT16)m_fifo[1];
+ m_cpy += (INT16)m_fifo[2];
+ }
+ else if ((m_fifo[0] & 0xff00) == 0x8800) /* ALINE */
+ {
+ line(m_cpx, m_cpy, m_fifo[1], m_fifo[2], m_fifo[0] & 0xff);
+ m_cpx = (INT16)m_fifo[1];
+ m_cpy = (INT16)m_fifo[2];
+ }
+ else if ((m_fifo[0] & 0xff00) == 0x8c00) /* RLINE */
+ {
+ line(m_cpx, m_cpy, m_cpx + (INT16)m_fifo[1], m_cpy + (INT16)m_fifo[2], m_fifo[0] & 0xff);
+ m_cpx += (INT16)m_fifo[1];
+ m_cpy += (INT16)m_fifo[2];
+ }
+ else if ((m_fifo[0] & 0xfff8) == 0x9000) /* ARCT */
+ {
+ line(m_cpx, m_cpy, (INT16)m_fifo[1], m_cpy, m_fifo[0] & 0xff);
+ line((INT16)m_fifo[1], m_cpy, (INT16)m_fifo[1], (INT16)m_fifo[2], m_fifo[0] & 0xff);
+ line((INT16)m_fifo[1], (INT16)m_fifo[2], m_cpx, (INT16)m_fifo[2], m_fifo[0] & 0xff);
+ line(m_cpx, (INT16)m_fifo[2], m_cpx, m_cpy, m_fifo[0] & 0xff);
+ m_cpx = (INT16)m_fifo[1];
+ m_cpy = (INT16)m_fifo[2];
+ }
+ else if ((m_fifo[0] & 0xfff8) == 0x9400) /* RRCT added*/
+ {
+ line(m_cpx, m_cpy, m_cpx + (INT16)m_fifo[1], m_cpy, m_fifo[0] & 0xff);
+ line(m_cpx + (INT16)m_fifo[1], m_cpy, m_cpx + (INT16)m_fifo[1], m_cpy + (INT16)m_fifo[2], m_fifo[0] & 0xff);
+ line(m_cpx + (INT16)m_fifo[1], m_cpy + (INT16)m_fifo[2], m_cpx, m_cpy + (INT16)m_fifo[2], m_fifo[0] & 0xff);
+ line(m_cpx, m_cpy + (INT16)m_fifo[2], m_cpx, m_cpy, m_fifo[0] & 0xff);
+
+ m_cpx += (INT16)m_fifo[1];
+ m_cpy += (INT16)m_fifo[2];
+ }
+ else if ((m_fifo[0] & 0xfff8) == 0xa400) /* RPLG added*/
+ {
+ int nseg, sx, sy, ex, ey;
+ sx = m_cpx;
+ sy = m_cpy;
+ for (nseg = 0; nseg < m_fifo[1]; nseg++)
+ {
+ ex = sx + (INT16)m_fifo[2 + nseg * 2];
+ ey = sy + (INT16)m_fifo[2 + nseg * 2 + 1];
+ line(sx, sy, ex, ey, m_fifo[0] & 7);
+ sx = ex;
+ sy = ey;
+ }
+ line(sx, sy, m_cpx, m_cpy, m_fifo[0] & 7);
+ }
+ else if ((m_fifo[0] & 0xfe00) == 0xa800) /* CRCL added*/
+ {
+ circle(m_cpx, m_cpy, m_fifo[1] & 0x1fff, m_fifo[0] & 7); // only 13 bit are used for the radius
+ }
+ else if ((m_fifo[0] & 0xfff8) == 0xc000) /* AFRCT */
+ {
+ INT16 pcx, pcy;
+ INT16 ax, ay, xx, yy;
+
+
+ pcx = m_fifo[1];
+ pcy = m_fifo[2];
+ ax = pcx - m_cpx;
+ ay = pcy - m_cpy;
+ xx = m_cpx;
+ yy = m_cpy;
+
+ for (;;)
+ {
+ for (;;)
+ {
+ dot(xx, yy, m_fifo[0] & 0x07, m_cl0);
+
+ if (ax == 0) break;
+ else if (ax > 0)
+ {
+ xx++;
+ ax--;
+ }
+ else
+ {
+ xx--;
+ ax++;
+ }
+ }
+
+ ax = pcx - m_cpx;
+ if (pcy < m_cpy)
+ {
+ yy--;
+ xx -= ax;
+ if (ay == 0) break;
+ ay++;
+ }
+ else
+ {
+ yy++;
+ xx -= ax;
+ if (ay == 0) break;
+ ay--;
+ }
+ }
+ }
+ else if ((m_fifo[0] & 0xfff8) == 0xc400) /* RFRCT added TODO*/
+ {
+ line(m_cpx, m_cpy, m_cpx + (INT16)m_fifo[1], m_cpy, m_fifo[0] & 0xff);
+ line(m_cpx + m_fifo[1], m_cpy, m_cpx + m_fifo[1], m_cpy + m_fifo[2], m_fifo[0] & 0xff);
+ line(m_cpx + m_fifo[1], m_cpy + m_fifo[2], m_cpx, m_cpy + m_fifo[2], m_fifo[0] & 0xff);
+ line(m_cpx, m_cpy + m_fifo[2], m_cpx, m_cpy, m_fifo[0] & 0xff);
+
+ m_cpx = m_cpx + (INT16)m_fifo[1];
+ m_cpy = m_cpy + (INT16)m_fifo[2];
+ }
+ else if (m_fifo[0] == 0xc800) /* PAINT */
+ {
+ paint(m_cpx, m_cpy, m_cl0);
+ }
+ else if ((m_fifo[0] & 0xfff8) == 0xcc00) /* DOT */
+ {
+ dot(m_cpx, m_cpy, m_fifo[0] & 0xff, m_cl0);
+ }
+ else if ((m_fifo[0] & 0xf000) == 0xd000) /* PTN (to do) */
+ {
+ ptn(m_fifo[0], m_psx, m_psy, m_pex - m_psx, m_pey - m_psy);
+
+ if ((m_fifo[0] & 0x0800) == 0x0000)
+ switch (m_fifo[0] & 0x0700)
+ {
+ case 0x0000:
+ if ((m_pey - m_psy) > 0)
+ m_cpy += (m_pey - m_psy);
+ else
+ m_cpy -= (m_pey - m_psy);
+ break;
+ case 0x0100:
+ // missing
+ break;
+ case 0x0200:
+ if ((m_pey - m_psy) > 0)
+ m_cpx += (m_pey - m_psy);
+ else
+ m_cpx -= (m_pey - m_psy);
+ break;
+ case 0x0300:
+ // missing
+ break;
+ case 0x0400:
+ if ((m_pey - m_psy) > 0)
+ m_cpy -= (m_pey - m_psy);
+ else
+ m_cpy += (m_pey - m_psy);
+ break;
+ case 0x0500:
+ // missing
+ break;
+ case 0x0600:
+ if ((m_pey - m_psy) > 0)
+ m_cpx -= (m_pey - m_psy);
+ else
+ m_cpx += (m_pey - m_psy);
+ break;
+ case 0x0700:
+ // missing
+ break;
+ }
+ else
+ {
+ // missing
+ }
+ }
+ else if ((m_fifo[0] & 0xf018) == 0xe000) /* agcpy */
+ {
+ agcpy(m_fifo[0], (INT16)m_fifo[1], (INT16)m_fifo[2], m_cpx, m_cpy, m_fifo[3], m_fifo[4]);
+
+ switch (m_fifo[0] & 0x0700)
+ {
+ case 0x0000:
+ if (m_fifo[4] > 0)
+ m_cpy += m_fifo[4];
+ else
+ m_cpy -= m_fifo[4];
+ break;
+ case 0x0100:
+ if (m_fifo[4] > 0)
+ m_cpy -= m_fifo[4];
+ else
+ m_cpy += m_fifo[4];
+ break;
+ case 0x0200:
+ if (m_fifo[4] > 0)
+ m_cpy += m_fifo[4];
+ else
+ m_cpy -= m_fifo[4];
+ break;
+ case 0x0300:
+ if (m_fifo[4] > 0)
+ m_cpy -= m_fifo[4];
+ else
+ m_cpy += m_fifo[4];
+ break;
+ case 0x0400:
+ if (m_fifo[3] > 0)
+ m_cpx += m_fifo[3];
+ else
+ m_cpx -= m_fifo[3];
+ break;
+ case 0x0500:
+ if (m_fifo[3] > 0)
+ m_cpx += m_fifo[3];
+ else
+ m_cpx -= m_fifo[3];
+ break;
+ case 0x0600:
+ if (m_fifo[3] > 0)
+ m_cpx -= m_fifo[3];
+ else
+ m_cpx += m_fifo[3];
+ break;
+ case 0x0700:
+ if (m_fifo[3] > 0)
+ m_cpx -= m_fifo[3];
+ else
+ m_cpx += m_fifo[3];
+ break;
+ }
+ }
+ else
+ {
+ logerror("unsupported command\n");
+ popmessage("unsupported command %s (%04x)", instruction_name[m_fifo[0] >> 10], m_fifo[0]);
+ }
+
+ m_fifo_counter = 0;
+ }
+}
+
+READ16_MEMBER( hd63484_device::status_r )
+{
+// if (space.device().safe_pc() != 0xfced6 && space.device().safe_pc() != 0xfe1d6)
+// logerror("%05x: HD63484 status read\n",space.device().safe_pc());
+
+ return 0xff22 | (machine().rand() & 0x0004); /* write FIFO ready + command end + (read FIFO ready or read FIFO not ready) */
+}
+
+WRITE16_MEMBER( hd63484_device::address_w )
+{
+ /* only low 8 bits are used */
+ if (ACCESSING_BITS_0_7)
+ m_regno = data;
+}
+
+WRITE16_MEMBER( hd63484_device::data_w )
+{
+ COMBINE_DATA(&m_reg[m_regno/2]);
+
+ if (m_skattva_hack)
+ m_reg[2/2] = (m_reg[2/2] & 0xf8ff) | 0x0200; // hack to set proper color depth in skattva
+
+ if (m_regno & 0x80)
+ m_regno += 2; /* autoincrement */
+
+#if LOG_COMMANDS
+// logerror("PC %05x: HD63484 register %02x write %04x\n", space.device().safe_pc(), m_regno, m_reg[m_regno/2]);
+#endif
+
+ if (m_regno == 0) /* FIFO */
+ command_w(m_reg[0]);
+}
+
+READ16_MEMBER( hd63484_device::data_r )
+{
+ int res;
+
+ if (m_regno == 0x80)
+ res = machine().first_screen()->vpos();
+ else if (m_regno == 0)
+ {
+#if LOG_COMMANDS
+// logerror("%05x: HD63484 read FIFO\n", space.device().safe_pc());
+#endif
+ res = m_readfifo;
+ }
+ else
+ {
+#if LOG_COMMANDS
+// logerror("%05x: HD63484 read register %02x\n", space.device().safe_pc(), m_regno);
+#endif
+ res = 0;
+ }
+
+ return res;
+}
+
+READ16_MEMBER( hd63484_device::ram_r )
+{
+ return m_ram[offset];
+}
+
+READ16_MEMBER( hd63484_device::regs_r )
+{
+ return m_reg[offset];
+}
+
+WRITE16_MEMBER( hd63484_device::ram_w )
+{
+ COMBINE_DATA(&m_ram[offset]);
+}
+
+WRITE16_MEMBER( hd63484_device::regs_w )
+{
+ COMBINE_DATA(&m_reg[offset]);
+}
diff --git a/src/devices/video/hd63484.h b/src/devices/video/hd63484.h
new file mode 100644
index 00000000000..08d147a8081
--- /dev/null
+++ b/src/devices/video/hd63484.h
@@ -0,0 +1,81 @@
+// license:BSD-3-Clause
+// copyright-holders:Roberto Zandona'
+/*************************************************************************
+
+ HD63484 ACRTC
+ Advanced CRT Controller.
+
+**************************************************************************/
+
+#ifndef __HD63484_H__
+#define __HD63484_H__
+
+
+/* the on-chip FIFO is 16 bytes long, but we use a larger one to simplify */
+/* decoding of long commands. Commands can be up to 64KB long... but Shanghai */
+/* doesn't reach that length. */
+
+#define FIFO_LENGTH 256
+#define HD63484_RAM_SIZE 0x100000
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class hd63484_device : public device_t
+{
+public:
+ hd63484_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~hd63484_device() {}
+
+ DECLARE_READ16_MEMBER( status_r );
+ DECLARE_WRITE16_MEMBER( address_w );
+ DECLARE_WRITE16_MEMBER( data_w );
+ DECLARE_READ16_MEMBER( data_r );
+
+ DECLARE_READ16_MEMBER( ram_r );
+ DECLARE_READ16_MEMBER( regs_r );
+ DECLARE_WRITE16_MEMBER( ram_w );
+ DECLARE_WRITE16_MEMBER( regs_w );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ // internal state
+ UINT16 * m_ram;
+ UINT16 m_reg[256/2];
+
+ int m_fifo_counter;
+ UINT16 m_fifo[FIFO_LENGTH];
+ UINT16 m_readfifo;
+
+ UINT16 m_pattern[16];
+ int m_org, m_org_dpd, m_rwp;
+ UINT16 m_cl0, m_cl1, m_ccmp, m_edg, m_mask, m_ppy, m_pzcy, m_ppx, m_pzcx, m_psy, m_psx, m_pey, m_pzy, m_pex, m_pzx, m_xmin, m_ymin, m_xmax, m_ymax, m_rwp_dn;
+ INT16 m_cpx, m_cpy;
+
+ int m_regno;
+ int m_skattva_hack;
+
+ void doclr16( int opcode, UINT16 fill, int *dst, INT16 _ax, INT16 _ay );
+ void docpy16( int opcode, int src, int *dst, INT16 _ax, INT16 _ay );
+ int org_first_pixel( int _org_dpd );
+ void dot( int x, int y, int opm, UINT16 color );
+ int get_pixel( int x, int y );
+ int get_pixel_ptn( int x, int y );
+ void agcpy( int opcode, int src_x, int src_y, int dst_x, int dst_y, INT16 _ax, INT16 _ay );
+ void ptn( int opcode, int src_x, int src_y, INT16 _ax, INT16 _ay );
+ void line( INT16 sx, INT16 sy, INT16 ex, INT16 ey, INT16 col );
+ void circle( INT16 sx, INT16 sy, UINT16 r, INT16 col );
+ void paint( int sx, int sy, int col );
+
+ void command_w(UINT16 cmd);
+};
+
+extern ATTR_DEPRECATED const device_type HD63484;
+
+#endif /* __HD63484_H__ */
diff --git a/src/devices/video/hd66421.c b/src/devices/video/hd66421.c
new file mode 100644
index 00000000000..af67be24d78
--- /dev/null
+++ b/src/devices/video/hd66421.c
@@ -0,0 +1,287 @@
+// license:BSD-3-Clause
+// copyright-holders:Tim Schuerewegen
+/*
+
+ Hitachi HD66421 LCD Controller/Driver
+
+ (c) 2001-2007 Tim Schuerewegen
+
+*/
+
+#include "emu.h"
+#include "hd66421.h"
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG_LEVEL 1
+#define _logerror(level,x) do { if (LOG_LEVEL > level) logerror x; } while (0)
+
+#define HD66421_RAM_SIZE (HD66421_WIDTH * HD66421_HEIGHT / 4) // 2-bits per pixel
+
+// R0 - control register 1
+#define LCD_R0_RMW 0x80 // read-modify-write mode
+#define LCD_R0_DISP 0x40 // display on/off
+#define LCD_R0_STBY 0x20 // standby (internal operation and power circuit halt)
+#define LCD_R0_PWR 0x10
+#define LCD_R0_AMP 0x08
+#define LCD_R0_REV 0x04 // reverse
+#define LCD_R0_HOLT 0x02
+#define LCD_R0_ADC 0x01
+
+// R1 - control register 2
+#define LCD_R1_BIS1 0x80 // bias ratio (bit 1)
+#define LCD_R1_BIS0 0x40 // bias ratio (bit 0)
+#define LCD_R1_WLS 0x20
+#define LCD_R1_GRAY 0x10 // grayscale palette 4/32
+#define LCD_R1_DTY1 0x08 // display duty cycle (bit 1)
+#define LCD_R1_DTY0 0x04 // display duty cycle (bit 0)
+#define LCD_R1_INC 0x02
+#define LCD_R1_BLK 0x01 // blink function
+
+// register 0 to 16
+#define LCD_REG_CONTROL_1 0x00 // control register 1
+#define LCD_REG_CONTROL_2 0x01 // control register 2
+#define LCD_REG_ADDR_X 0x02 // x address register
+#define LCD_REG_ADDR_Y 0x03 // y address register
+#define LCD_REG_RAM 0x04 // display ram access register
+#define LCD_REG_START_Y 0x05 // display start line register
+#define LCD_REG_BLINK_START 0x06 // blink start line register
+#define LCD_REG_BLINK_END 0x07 // blink end line register
+#define LCD_REG_BLINK_1 0x08 // blink register 1
+#define LCD_REG_BLINK_2 0x09 // blink register 2
+#define LCD_REG_BLINK_3 0x0A // blink register 3
+#define LCD_REG_PARTIAL 0x0B // partial display block register
+#define LCD_REG_COLOR_1 0x0C // gray scale palette 1 (0,0)
+#define LCD_REG_COLOR_2 0x0D // gray scale palette 2 (0,1)
+#define LCD_REG_COLOR_3 0x0E // gray scale palette 3 (1,0)
+#define LCD_REG_COLOR_4 0x0F // gray scale palette 4 (1,1)
+#define LCD_REG_CONTRAST 0x10 // contrast control register
+#define LCD_REG_PLANE 0x11 // plane selection register
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+const device_type HD66421 = &device_creator<hd66421_device>;
+
+
+// default address map
+static ADDRESS_MAP_START( hd66421, AS_0, 8, hd66421_device )
+ AM_RANGE(0x0000, HD66421_RAM_SIZE) AM_RAM
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *hd66421_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 hd66421_device::readbyte(offs_t address)
+{
+ return space().read_byte(address);
+}
+
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void hd66421_device::writebyte(offs_t address, UINT8 data)
+{
+ space().write_byte(address, data);
+}
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// hd66421_device - constructor
+//-------------------------------------------------
+
+hd66421_device::hd66421_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HD66421, "Hitachi HD66421 LCD Controller", tag, owner, clock, "hd66421", __FILE__),
+ device_memory_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, NULL, *ADDRESS_MAP_NAME(hd66421)),
+ m_cmd(0),
+ m_x(0),
+ m_y(0),
+ m_palette(*this, "palette")
+{
+ for (int i = 0; i < 32; i++)
+ {
+ m_reg[i] = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void hd66421_device::device_start()
+{
+ // register for state saving
+ save_item(NAME(m_cmd));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_x));
+ save_item(NAME(m_y));
+}
+
+READ8_MEMBER( hd66421_device::reg_idx_r )
+{
+ _logerror( 2, ("reg_idx_r\n"));
+ return m_cmd;
+}
+
+WRITE8_MEMBER( hd66421_device::reg_idx_w )
+{
+ _logerror( 2, ("reg_idx_w (%02X)\n", data));
+ m_cmd = data;
+}
+
+READ8_MEMBER( hd66421_device::reg_dat_r )
+{
+ _logerror( 2, ("reg_dat_r\n"));
+ return m_reg[m_cmd];
+}
+
+WRITE8_MEMBER( hd66421_device::reg_dat_w )
+{
+ _logerror( 2, ("reg_dat_w (%02X)\n", data));
+ m_reg[m_cmd] = data;
+
+ switch (m_cmd)
+ {
+ case LCD_REG_ADDR_X :
+ m_x = data;
+ break;
+
+ case LCD_REG_ADDR_Y :
+ m_y = data;
+ break;
+
+ case LCD_REG_RAM :
+ {
+ UINT8 r1;
+ writebyte(m_y * (HD66421_WIDTH / 4) + m_x, data);
+ r1 = m_reg[LCD_REG_CONTROL_2];
+ if (r1 & 0x02)
+ m_x++;
+ else
+ m_y++;
+
+ if (m_x >= (HD66421_WIDTH / 4))
+ {
+ m_x = 0;
+ m_y++;
+ }
+
+ if (m_y >= HD66421_HEIGHT)
+ m_y = 0;
+ }
+ break;
+ }
+}
+
+void hd66421_device::plot_pixel(bitmap_ind16 &bitmap, int x, int y, UINT32 color)
+{
+ bitmap.pix16(y, x) = (UINT16)color;
+}
+
+UINT32 hd66421_device::update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ pen_t pen[4];
+
+ _logerror( 1, ("video_update_hd66421\n"));
+
+ // update palette
+ for (int i = 0; i < 4; i++)
+ {
+ double bright;
+ int temp;
+ temp = 31 - (m_reg[LCD_REG_COLOR_1 + i] - m_reg[LCD_REG_CONTRAST] + 0x03);
+ if (temp < 0) temp = 0;
+ if (temp > 31) temp = 31;
+ bright = 1.0 * temp / 31;
+ pen[i] = i;
+ #ifdef HD66421_BRIGHTNESS_DOES_NOT_WORK
+ m_palette->set_pen_color(pen[i], 255 * bright, 255 * bright, 255 * bright);
+ #else
+ m_palette->set_pen_contrast(pen[i], bright);
+ #endif
+ }
+
+ // draw bitmap (bottom to top)
+ if (m_reg[0] & LCD_R0_DISP)
+ {
+ int x, y;
+ x = 0;
+ y = HD66421_HEIGHT - 1;
+
+ for (int i = 0; i < HD66421_RAM_SIZE; i++)
+ {
+ plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 6) & 3]);
+ plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 4) & 3]);
+ plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 2) & 3]);
+ plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 0) & 3]);
+ if (x >= HD66421_WIDTH)
+ {
+ x = 0;
+ y = y - 1;
+ }
+ }
+ }
+ else
+ {
+ rectangle rect(0, HD66421_WIDTH - 1, 0, HD66421_HEIGHT - 1);
+ bitmap.fill(m_palette->white_pen(), rect);
+ }
+
+ return 0;
+}
+
+PALETTE_INIT_MEMBER(hd66421_device, hd66421)
+{
+ // init palette
+ for (int i = 0; i < 4; i++)
+ {
+ palette.set_pen_color(i, rgb_t::white);
+#ifndef HD66421_BRIGHTNESS_DOES_NOT_WORK
+ palette.set_pen_contrast(i, 1.0 * i / (4 - 1));
+#endif
+ }
+}
+
+
+static MACHINE_CONFIG_FRAGMENT( hd66421 )
+ MCFG_PALETTE_ADD("palette", 4)
+ MCFG_PALETTE_INIT_OWNER(hd66421_device, hd66421)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor hd66421_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( hd66421 );
+}
diff --git a/src/devices/video/hd66421.h b/src/devices/video/hd66421.h
new file mode 100644
index 00000000000..baed72ad30c
--- /dev/null
+++ b/src/devices/video/hd66421.h
@@ -0,0 +1,84 @@
+// license:BSD-3-Clause
+// copyright-holders:Tim Schuerewegen
+/***************************************************************************
+
+ Hitachi HD66421 LCD Controller
+
+ (c) 2001-2007 Tim Schuerewegen
+
+ ***************************************************************************/
+
+#pragma once
+
+#ifndef __HD66421_H__
+#define __HD66421_H__
+
+
+///*************************************************************************
+// MACROS / CONSTANTS
+///*************************************************************************
+
+//#define HD66421_BRIGHTNESS_DOES_NOT_WORK
+
+#define HD66421_WIDTH 160
+#define HD66421_HEIGHT 100
+
+
+/*----------- defined in video/hd66421.c -----------*/
+
+///*************************************************************************
+// INTERFACE CONFIGURATION MACROS
+///*************************************************************************
+
+#define MCFG_HD66421_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, HD66421, 0)
+
+///*************************************************************************
+// TYPE DEFINITIONS
+///*************************************************************************
+
+// ======================> hd66421_device
+
+class hd66421_device : public device_t,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ hd66421_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_READ8_MEMBER( reg_idx_r );
+ DECLARE_WRITE8_MEMBER( reg_idx_w );
+ DECLARE_READ8_MEMBER( reg_dat_r );
+ DECLARE_WRITE8_MEMBER( reg_dat_w );
+ DECLARE_PALETTE_INIT(hd66421);
+
+ UINT32 update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ // device_config_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ // address space configurations
+ const address_space_config m_space_config;
+
+ inline UINT8 readbyte(offs_t address);
+ inline void writebyte(offs_t address, UINT8 data);
+
+ void plot_pixel(bitmap_ind16 &bitmap, int x, int y, UINT32 color);
+
+private:
+ UINT8 m_cmd, m_reg[32];
+ int m_x, m_y;
+ required_device<palette_device> m_palette;
+};
+
+
+// device type definition
+extern const device_type HD66421;
+
+
+#endif
diff --git a/src/devices/video/huc6202.c b/src/devices/video/huc6202.c
new file mode 100644
index 00000000000..3f33a3eaa05
--- /dev/null
+++ b/src/devices/video/huc6202.c
@@ -0,0 +1,367 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Hudson/NEC HuC6202 Video Priority Controller
+
+**********************************************************************/
+
+#include "emu.h"
+#include "huc6270.h"
+#include "huc6202.h"
+
+
+const device_type HUC6202 = &device_creator<huc6202_device>;
+
+
+huc6202_device::huc6202_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HUC6202, "HuC6202 VPC", tag, owner, clock, "huc6202", __FILE__),
+ m_next_pixel_0_cb(*this),
+ m_time_til_next_event_0_cb(*this),
+ m_vsync_changed_0_cb(*this),
+ m_hsync_changed_0_cb(*this),
+ m_read_0_cb(*this),
+ m_write_0_cb(*this),
+ m_next_pixel_1_cb(*this),
+ m_time_til_next_event_1_cb(*this),
+ m_vsync_changed_1_cb(*this),
+ m_hsync_changed_1_cb(*this),
+ m_read_1_cb(*this),
+ m_write_1_cb(*this)
+{
+}
+
+
+READ16_MEMBER( huc6202_device::next_pixel )
+{
+ UINT16 data = huc6270_device::HUC6270_BACKGROUND;
+
+ UINT16 data_0 = m_next_pixel_0_cb( 0, 0xffff );
+ UINT16 data_1 = m_next_pixel_1_cb( 0, 0xffff );
+
+ if ( data_0 == huc6270_device::HUC6270_SPRITE && data_1 == huc6270_device::HUC6270_SPRITE )
+ {
+ m_map_index = 0;
+ if ( m_map_dirty )
+ {
+ int i;
+
+ for ( i = 0; i < 512; i++ )
+ {
+ m_prio_map[ i ] = 0;
+ if ( m_window1 < 0x40 || i > m_window1 )
+ {
+ m_prio_map [ i ] |= 1;
+ }
+ if ( m_window2 < 0x40 || i > m_window2 )
+ {
+ m_prio_map[ i ] |= 2;
+ }
+ }
+ m_map_dirty = 0;
+ }
+ }
+ else
+ {
+ UINT8 prio_index = m_prio_map[ m_map_index ];
+
+ if ( m_prio[ prio_index ].dev0_enabled && data_0 != huc6270_device::HUC6270_SPRITE )
+ {
+ if ( m_prio[ prio_index ].dev1_enabled && data_1 != huc6270_device::HUC6270_SPRITE )
+ {
+ switch ( m_prio[ prio_index ].prio_type )
+ {
+ case 0: /* Back - BG1 SP1 BG0 SP0 - Front */
+ data = ( data_0 & 0x0F ) ? data_0 : data_1;
+ break;
+
+ case 1: /* Back - BG1 BG0 SP1 SP0 - Front */
+ if ( data_0 > huc6270_device::HUC6270_SPRITE )
+ {
+ /* Device 0 sprite */
+ data = data_0;
+ }
+ else if ( data_1 > huc6270_device::HUC6270_SPRITE )
+ {
+ /* Device 1 sprite */
+ data = data_1;
+ }
+ else
+ {
+ /* Device 0 and 1 backgrounds */
+ data = ( data_0 & 0x0F ) ? data_0 : data_1;
+ }
+ break;
+
+ case 2: /* Back - BG0 + SP1 => BG0 - Front
+ BG0 + BG1 => BG0
+ BG1 + SP0 => BG1
+ SP0 + SP1 => SP0
+ */
+ if ( data_1 > huc6270_device::HUC6270_SPRITE )
+ {
+ if ( data_0 > huc6270_device::HUC6270_SPRITE )
+ {
+ /* Device 1 sprite, device 0 sprite */
+ data = data_0;
+ }
+ else
+ {
+ /* Device 1 sprite, device 0 background */
+ data = ( data_0 & 0x0F ) ? data_0 : data_1;
+ }
+ }
+ else
+ {
+ if ( data_0 > huc6270_device::HUC6270_SPRITE )
+ {
+ /* Device 1 background, device 0 sprite */
+ data = data_1;
+ }
+ else
+ {
+ /* Device 1 background, device 0 background */
+ data = ( data_0 & 0x0F ) ? data_0 : data_1;
+ }
+ }
+ break;
+
+ case 3: /* ?? */
+ break;
+ }
+ }
+ else
+ {
+ /* Only device 0 is enabled */
+ data = data_0;
+ }
+ }
+ else
+ {
+ /* Only device 1 is enabled */
+ if ( m_prio[ prio_index ].dev1_enabled && data_1 != huc6270_device::HUC6270_SPRITE )
+ {
+ data = data_1;
+ }
+ }
+ m_map_index += 1;
+ }
+ return data;
+}
+
+
+READ16_MEMBER( huc6202_device::time_until_next_event )
+{
+ UINT16 next_event_clocks_0 = m_time_til_next_event_0_cb( 0, 0xffff );
+ UINT16 next_event_clocks_1 = m_time_til_next_event_1_cb( 0, 0xffff );
+
+ return MIN( next_event_clocks_0, next_event_clocks_1 );
+}
+
+
+WRITE_LINE_MEMBER( huc6202_device::vsync_changed )
+{
+ m_vsync_changed_0_cb( state );
+ m_vsync_changed_1_cb( state );
+}
+
+
+WRITE_LINE_MEMBER( huc6202_device::hsync_changed )
+{
+ m_hsync_changed_0_cb( state );
+ m_hsync_changed_1_cb( state );
+}
+
+
+READ8_MEMBER( huc6202_device::read )
+{
+ UINT8 data = 0xFF;
+
+ switch ( offset & 7 )
+ {
+ case 0x00: /* Priority register #0 */
+ data = ( m_prio[0].prio_type << 2 ) |
+ ( m_prio[0].dev0_enabled ? 0x01 : 0 ) |
+ ( m_prio[0].dev1_enabled ? 0x02 : 0 ) |
+ ( m_prio[1].prio_type << 6 ) |
+ ( m_prio[1].dev0_enabled ? 0x10 : 0 ) |
+ ( m_prio[1].dev1_enabled ? 0x20 : 0 );
+ break;
+
+ case 0x01: /* Priority register #1 */
+ data = ( m_prio[2].prio_type << 2 ) |
+ ( m_prio[2].dev0_enabled ? 0x01 : 0 ) |
+ ( m_prio[2].dev1_enabled ? 0x02 : 0 ) |
+ ( m_prio[3].prio_type << 6 ) |
+ ( m_prio[3].dev0_enabled ? 0x10 : 0 ) |
+ ( m_prio[3].dev1_enabled ? 0x20 : 0 );
+ break;
+
+ case 0x02: /* Window 1 LSB */
+ data = m_window1 & 0xFF;
+ break;
+
+ case 0x03: /* Window 1 MSB */
+ data = ( m_window1 >> 8 ) & 0xFF;
+ break;
+
+ case 0x04: /* Window 2 LSB */
+ data = m_window2 & 0xFF;
+ break;
+
+ case 0x05: /* Window 2 MSB */
+ data = ( m_window2 >> 8 ) & 0xFF;
+ break;
+ }
+
+ return data;
+}
+
+
+WRITE8_MEMBER( huc6202_device::write )
+{
+ switch ( offset & 7 )
+ {
+ case 0x00: /* Priority register #0 */
+ m_prio[0].dev0_enabled = data & 0x01;
+ m_prio[0].dev1_enabled = data & 0x02;
+ m_prio[0].prio_type = ( data >> 2 ) & 0x03;
+ m_prio[1].dev0_enabled = data & 0x10;
+ m_prio[1].dev1_enabled = data & 0x20;
+ m_prio[1].prio_type = ( data >> 6 ) & 0x03;
+ break;
+
+ case 0x01: /* Priority register #1 */
+ m_prio[2].dev0_enabled = data & 0x01;
+ m_prio[2].dev1_enabled = data & 0x02;
+ m_prio[2].prio_type = ( data >> 2 ) & 0x03;
+ m_prio[3].dev0_enabled = data & 0x10;
+ m_prio[3].dev1_enabled = data & 0x20;
+ m_prio[3].prio_type = ( data >> 6 ) & 0x03;
+ break;
+
+ case 0x02: /* Window 1 LSB */
+ m_window1 = ( m_window1 & 0xFF00 ) | data;
+ m_map_dirty = 1;
+ break;
+
+ case 0x03: /* Window 1 MSB */
+ m_window1 = ( ( m_window1 & 0x00FF ) | ( data << 8 ) ) & 0x3FF;
+ m_map_dirty = 1;
+ break;
+
+ case 0x04: /* Window 2 LSB */
+ m_window2 = ( m_window2 & 0xFF00 ) | data;
+ m_map_dirty = 1;
+ break;
+
+ case 0x05: /* Window 2 MSB */
+ m_window2 = ( ( m_window2 & 0x00FF ) | ( data << 8 ) ) & 0x3FF;
+ m_map_dirty = 1;
+ break;
+
+ case 0x06: /* I/O select */
+ m_io_device = data & 0x01;
+ break;
+ }
+}
+
+
+READ8_MEMBER( huc6202_device::io_read )
+{
+ if ( m_io_device )
+ {
+ return m_read_1_cb( offset );
+ }
+ else
+ {
+ return m_read_0_cb( offset );
+ }
+}
+
+
+WRITE8_MEMBER( huc6202_device::io_write )
+{
+ if ( m_io_device )
+ {
+ m_write_1_cb( offset, data );
+ }
+ else
+ {
+ m_write_0_cb( offset, data );
+ }
+}
+
+
+void huc6202_device::device_start()
+{
+ /* Resolve callbacks */
+ m_next_pixel_0_cb.resolve();
+ m_time_til_next_event_0_cb.resolve();
+ m_hsync_changed_0_cb.resolve();
+ m_vsync_changed_0_cb.resolve();
+ m_read_0_cb.resolve();
+ m_write_0_cb.resolve();
+
+ m_next_pixel_1_cb.resolve();
+ m_time_til_next_event_1_cb.resolve();
+ m_hsync_changed_1_cb.resolve();
+ m_vsync_changed_1_cb.resolve();
+ m_read_1_cb.resolve();
+ m_write_1_cb.resolve();
+
+ /* We want all our callbacks to be resolved */
+ assert( ! m_next_pixel_0_cb.isnull() );
+ assert( ! m_time_til_next_event_0_cb.isnull() );
+ assert( ! m_hsync_changed_0_cb.isnull() );
+ assert( ! m_vsync_changed_0_cb.isnull() );
+ assert( ! m_read_0_cb.isnull() );
+ assert( ! m_write_0_cb.isnull() );
+ assert( ! m_next_pixel_1_cb.isnull() );
+ assert( ! m_time_til_next_event_1_cb.isnull() );
+ assert( ! m_hsync_changed_1_cb.isnull() );
+ assert( ! m_vsync_changed_1_cb.isnull() );
+ assert( ! m_read_1_cb.isnull() );
+ assert( ! m_write_1_cb.isnull() );
+
+ /* Register save items */
+ save_item(NAME(m_prio[0].prio_type));
+ save_item(NAME(m_prio[0].dev0_enabled));
+ save_item(NAME(m_prio[0].dev1_enabled));
+ save_item(NAME(m_prio[1].prio_type));
+ save_item(NAME(m_prio[1].dev0_enabled));
+ save_item(NAME(m_prio[1].dev1_enabled));
+ save_item(NAME(m_prio[2].prio_type));
+ save_item(NAME(m_prio[2].dev0_enabled));
+ save_item(NAME(m_prio[2].dev1_enabled));
+ save_item(NAME(m_prio[3].prio_type));
+ save_item(NAME(m_prio[3].dev0_enabled));
+ save_item(NAME(m_prio[3].dev1_enabled));
+ save_item(NAME(m_window1));
+ save_item(NAME(m_window2));
+ save_item(NAME(m_io_device));
+ save_item(NAME(m_map_index));
+ save_item(NAME(m_map_dirty));
+ save_item(NAME(m_prio_map));
+}
+
+
+void huc6202_device::device_reset()
+{
+ m_prio[0].prio_type = 0;
+ m_prio[0].dev0_enabled = 1;
+ m_prio[0].dev1_enabled = 0;
+ m_prio[1].prio_type = 0;
+ m_prio[1].dev0_enabled = 1;
+ m_prio[1].dev1_enabled = 0;
+ m_prio[2].prio_type = 0;
+ m_prio[2].dev0_enabled = 1;
+ m_prio[2].dev1_enabled = 0;
+ m_prio[3].prio_type = 0;
+ m_prio[3].dev0_enabled = 1;
+ m_prio[3].dev1_enabled = 0;
+ m_map_dirty = 1;
+ m_window1 = 0;
+ m_window2 = 0;
+ m_io_device = 0;
+}
diff --git a/src/devices/video/huc6202.h b/src/devices/video/huc6202.h
new file mode 100644
index 00000000000..85ae87c1db7
--- /dev/null
+++ b/src/devices/video/huc6202.h
@@ -0,0 +1,130 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Hudson/NEC HuC6202 interface
+
+**********************************************************************/
+
+#ifndef __HUC6202_H_
+#define __HUC6202_H_
+
+#include "emu.h"
+
+
+#define MCFG_HUC6202_NEXT_PIXEL_0_CB(_devcb) \
+ devcb = &huc6202_device::set_next_pixel_0_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_TIME_TIL_NEXT_EVENT_0_CB(_devcb) \
+ devcb = &huc6202_device::set_time_til_next_event_0_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_VSYNC_CHANGED_0_CB(_devcb) \
+ devcb = &huc6202_device::set_vsync_changed_0_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_HSYNC_CHANGED_0_CB(_devcb) \
+ devcb = &huc6202_device::set_hsync_changed_0_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_READ_0_CB(_devcb) \
+ devcb = &huc6202_device::set_read_0_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_WRITE_0_CB(_devcb) \
+ devcb = &huc6202_device::set_write_0_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_NEXT_PIXEL_1_CB(_devcb) \
+ devcb = &huc6202_device::set_next_pixel_1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_TIME_TIL_NEXT_EVENT_1_CB(_devcb) \
+ devcb = &huc6202_device::set_time_til_next_event_1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_VSYNC_CHANGED_1_CB(_devcb) \
+ devcb = &huc6202_device::set_vsync_changed_1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_HSYNC_CHANGED_1_CB(_devcb) \
+ devcb = &huc6202_device::set_hsync_changed_1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_READ_1_CB(_devcb) \
+ devcb = &huc6202_device::set_read_1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6202_WRITE_1_CB(_devcb) \
+ devcb = &huc6202_device::set_write_1_callback(*device, DEVCB_##_devcb);
+
+
+class huc6202_device : public device_t
+{
+public:
+ // construction/destruction
+ huc6202_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_next_pixel_0_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_next_pixel_0_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_time_til_next_event_0_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_time_til_next_event_0_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_vsync_changed_0_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_vsync_changed_0_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_hsync_changed_0_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_hsync_changed_0_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_read_0_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_read_0_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_write_0_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_write_0_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_next_pixel_1_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_next_pixel_1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_time_til_next_event_1_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_time_til_next_event_1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_vsync_changed_1_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_vsync_changed_1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_hsync_changed_1_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_hsync_changed_1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_read_1_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_read_1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_write_1_callback(device_t &device, _Object object) { return downcast<huc6202_device &>(device).m_write_1_cb.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_READ8_MEMBER( io_read );
+ DECLARE_WRITE8_MEMBER( io_write );
+ DECLARE_READ16_MEMBER( next_pixel );
+ DECLARE_READ16_MEMBER( time_until_next_event );
+ DECLARE_WRITE_LINE_MEMBER( vsync_changed );
+ DECLARE_WRITE_LINE_MEMBER( hsync_changed );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ /* callbacks */
+ /* First gfx input device */
+ devcb_read16 m_next_pixel_0_cb;
+
+ /* TODO: Choose proper types */
+ /* Callback function to get time until next event */
+ devcb_read16 m_time_til_next_event_0_cb;
+
+ devcb_write_line m_vsync_changed_0_cb;
+ devcb_write_line m_hsync_changed_0_cb;
+ devcb_read8 m_read_0_cb;
+ devcb_write8 m_write_0_cb;
+
+
+ /* Second gfx input device */
+ devcb_read16 m_next_pixel_1_cb;
+
+ /* TODO: Choose proper types */
+ /* Callback function to get time until next event */
+ devcb_read16 m_time_til_next_event_1_cb;
+
+ devcb_write_line m_vsync_changed_1_cb;
+ devcb_write_line m_hsync_changed_1_cb;
+ devcb_read8 m_read_1_cb;
+ devcb_write8 m_write_1_cb;
+
+ struct {
+ UINT8 prio_type;
+ UINT8 dev0_enabled;
+ UINT8 dev1_enabled;
+ } m_prio[4];
+ UINT16 m_window1;
+ UINT16 m_window2;
+ int m_io_device;
+ int m_map_index;
+ int m_map_dirty;
+ UINT8 m_prio_map[512];
+
+};
+
+
+extern const device_type HUC6202;
+
+
+#endif
diff --git a/src/devices/video/huc6260.c b/src/devices/video/huc6260.c
new file mode 100644
index 00000000000..7d3ccc75f78
--- /dev/null
+++ b/src/devices/video/huc6260.c
@@ -0,0 +1,311 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Hudson/NEC HuC6260 Video Colour Encoder
+
+ The HuC6260 takes a stream of pixel data, looks up the correct
+ palette data and outputs a video stream.
+
+ The HuC6260 generates the tv control signals. A full line lasts
+ 1365 "master" cycles (typically at 21.47727Mhz).
+
+ HSync is low for 237 and high for 1128 master cycles.
+ VSync is low for 4095 master cycles (3 lines).
+ VSync changes 30 master cycles after HSync would go low.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "huc6260.h"
+
+#define LOG 0
+
+#define HUC6260_HSYNC_LENGTH 237
+#define HUC6260_HSYNC_START ( HUC6260_WPF - HUC6260_HSYNC_LENGTH )
+
+
+PALETTE_INIT_MEMBER(huc6260_device, huc6260)
+{
+ int i;
+
+ for ( i = 0; i < 512; i++ )
+ {
+ int r = pal3bit( ( i >> 3 ) & 7 );
+ int g = pal3bit( ( i >> 6 ) & 7 );
+ int b = pal3bit( ( i ) & 7 );
+ int y = ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16;
+
+ palette.set_pen_color( i, r, g, b );
+ palette.set_pen_color( 512 + i, y, y, y );
+ }
+}
+
+
+const device_type HUC6260 = &device_creator<huc6260_device>;
+
+
+huc6260_device::huc6260_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HUC6260, "HuC6260 VCE", tag, owner, clock, "huc6260", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_next_pixel_data_cb(*this),
+ m_time_til_next_event_cb(*this),
+ m_vsync_changed_cb(*this),
+ m_hsync_changed_cb(*this)
+{
+}
+
+
+void huc6260_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int vpos = m_screen->vpos();
+ int hpos = m_screen->hpos();
+ int h = m_last_h;
+ int v = m_last_v;
+ UINT16 *bitmap_line = &m_bmp->pix16(v);
+
+ while ( h != hpos || v != vpos )
+ {
+ if ( m_pixel_clock == 0 )
+ {
+ g_profiler.start( PROFILER_VIDEO );
+ /* Get next pixel information */
+ m_pixel_data = m_next_pixel_data_cb( 0, 0xffff );
+ g_profiler.stop();
+ if ( m_greyscales )
+ {
+ m_pixel_data += 512;
+ }
+ }
+
+ bitmap_line[ h ] = m_palette[ m_pixel_data ];
+ m_pixel_clock = ( m_pixel_clock + 1 ) % m_pixels_per_clock;
+ h = ( h + 1 ) % HUC6260_WPF;
+
+ switch( h )
+ {
+ case HUC6260_HSYNC_START: /* Start of HSync */
+ m_hsync_changed_cb( 0 );
+// if ( v == 0 )
+// {
+// /* Check if the screen should be resized */
+// m_height = HUC6260_LPF - ( m_blur ? 1 : 0 );
+// if ( m_height != video_screen_get_height( m_screen ) )
+// {
+// rectangle visible_area;
+//
+// /* TODO: Set proper visible area parameters */
+// visible_area.min_x = 64;
+// visible_area.min_y = 18;
+// visible_area.max_x = 64 + 1024 + 64 - 1;
+// visible_area.max_y = 18 + 242 - 1;
+//
+// video_screen_configure( m_screen, HUC6260_WPF, m_height, &visible_area, HZ_TO_ATTOSECONDS( device->clock / ( HUC6260_WPF * m_height ) ) );
+// }
+// }
+ break;
+
+ case 0: /* End of HSync */
+ m_hsync_changed_cb( 1 );
+ m_pixel_clock = 0;
+ v = ( v + 1 ) % m_height;
+ bitmap_line = &m_bmp->pix16(v);
+ break;
+
+ case HUC6260_HSYNC_START + 30: /* End/Start of VSync */
+ if ( v>= m_height - 4 )
+ {
+ m_vsync_changed_cb( ( v >= m_height - 4 && v < m_height - 1 ) ? 0 : 1 );
+ }
+ break;
+ }
+ }
+
+ m_last_h = h;
+ m_last_v = v;
+
+ /* Reschedule timer */
+ if ( m_last_h < HUC6260_HSYNC_START )
+ {
+ /* Next event is start of HSync signal */
+ v = m_last_v;
+ h = HUC6260_HSYNC_START;
+ }
+ else if ( ( m_last_v == m_height - 4 || m_last_v == m_height - 1 ) && m_last_h < HUC6260_HSYNC_START + 30 )
+ {
+ /* Next event is start/end of VSync signal */
+ v = m_last_v;
+ h = HUC6260_HSYNC_START + 30;
+ }
+ else
+ {
+ /* Next event is end of HSync signal */
+ v = ( m_last_v + 1 ) % m_height;
+ h = 0;
+ }
+
+ /* Ask our slave device for time until next possible event */
+ {
+ UINT16 next_event_clocks = m_time_til_next_event_cb( 0, 0xffff );
+ int event_hpos, event_vpos;
+
+ /* Adjust for pixel clocks per pixel */
+ next_event_clocks *= m_pixels_per_clock;
+
+ /* Adjust for clocks left to go for current pixel */
+ next_event_clocks += ( m_pixels_per_clock - ( m_pixel_clock + 1 ) );
+
+ event_hpos = hpos + next_event_clocks;
+ event_vpos = vpos;
+ while ( event_hpos > HUC6260_WPF )
+ {
+ event_vpos += 1;
+ event_hpos -= HUC6260_WPF;
+ }
+
+ if ( event_vpos < v || ( event_vpos == v && event_hpos <= h ) )
+ {
+ if ( event_vpos > vpos || ( event_vpos == vpos && event_hpos > hpos ) )
+ {
+ v = event_vpos;
+ h = event_hpos;
+ }
+ }
+ }
+
+ m_timer->adjust( m_screen->time_until_pos( v, h ) );
+}
+
+
+void huc6260_device::video_update( bitmap_ind16 &bitmap, const rectangle &cliprect )
+{
+ copybitmap( bitmap, *m_bmp, 0, 0, 0, 0, cliprect );
+}
+
+
+// the battlera arcade board reads/writes the palette directly
+READ8_MEMBER(huc6260_device::palette_direct_read)
+{
+ if (!(offset&1)) return m_palette[offset>>1];
+ else return m_palette[offset >> 1] >> 8;
+}
+
+WRITE8_MEMBER(huc6260_device::palette_direct_write)
+{
+ if (!(offset&1)) m_palette[offset>>1] = (m_palette[offset>>1] & 0xff00) | data;
+ else m_palette[offset>>1] = (m_palette[offset>>1] & 0x00ff) | (data<<8);
+}
+
+READ8_MEMBER( huc6260_device::read )
+{
+ UINT8 data = 0xFF;
+
+ switch ( offset & 7 )
+ {
+ case 0x04: /* Color table data LSB */
+ data = m_palette[ m_address ] & 0xFF;
+ break;
+
+ case 0x05: /* Color table data MSB */
+ data = 0xFE | ( m_palette[ m_address ] >> 8 );
+
+ /* Increment internal address */
+ m_address = ( m_address + 1 ) & 0x1FF;
+ break;
+ }
+
+ return data;
+}
+
+
+WRITE8_MEMBER( huc6260_device::write )
+{
+ switch ( offset & 7 )
+ {
+ case 0x00: /* Control register */
+ m_greyscales = data & 0x80;
+ m_blur = data & 0x04;
+ m_pixels_per_clock = ( data & 0x02 ) ? 2 : ( ( data & 0x01 ) ? 3 : 4 );
+ break;
+
+ case 0x02: /* Color table address LSB */
+ m_address = ( ( m_address & 0xFF00 ) | data ) & 0x1FF;
+ break;
+
+ case 0x03: /* Color table address MSB */
+ m_address = ( ( m_address & 0x00FF ) | ( data << 8 ) ) & 0x1FF;
+ break;
+
+ case 0x04: /* Color table data LSB */
+ m_palette[ m_address ] = ( ( m_palette[ m_address ] & 0xFF00 ) | data ) & 0x1FF;
+ break;
+
+ case 0x05: /* Color table data MSB */
+ m_palette[ m_address ] = ( ( m_palette[ m_address ] & 0x00FF ) | ( data << 8 ) ) & 0x1FF;
+
+ /* Increment internal address */
+ m_address = ( m_address + 1 ) & 0x1FF;
+ break;
+ }
+}
+
+
+void huc6260_device::device_start()
+{
+ m_timer = timer_alloc();
+ m_bmp = auto_bitmap_ind16_alloc( machine(), HUC6260_WPF, HUC6260_LPF );
+
+ /* Resolve callbacks */
+ m_hsync_changed_cb.resolve();
+ m_vsync_changed_cb.resolve();
+ m_next_pixel_data_cb.resolve();
+ m_time_til_next_event_cb.resolve();
+
+ /* We want to have a valid screen and valid callbacks */
+ assert( ! m_hsync_changed_cb.isnull() );
+ assert( ! m_vsync_changed_cb.isnull() );
+ assert( ! m_next_pixel_data_cb.isnull() );
+ assert( ! m_time_til_next_event_cb.isnull() );
+
+ save_item(NAME(m_last_h));
+ save_item(NAME(m_last_v));
+ save_item(NAME(m_height));
+ save_item(NAME(m_palette));
+ save_item(NAME(m_address));
+ save_item(NAME(m_greyscales));
+ save_item(NAME(m_blur));
+ save_item(NAME(m_pixels_per_clock));
+ save_item(NAME(m_pixel_data));
+ save_item(NAME(m_pixel_clock));
+}
+
+
+void huc6260_device::device_reset()
+{
+ m_address = 0;
+ m_greyscales = 0;
+ m_blur = 0;
+ m_pixels_per_clock = 4;
+ m_height = 263;
+ m_pixel_clock = 0;
+ memset(m_palette, 0x00, sizeof(m_palette));
+
+ m_last_v = m_screen->vpos();
+ m_last_h = m_screen->hpos();
+ m_timer->adjust( m_screen->time_until_pos( ( m_screen->vpos() + 1 ) % 263, 0 ) );
+}
+
+static MACHINE_CONFIG_FRAGMENT( huc6260 )
+ MCFG_PALETTE_ADD("palette", HUC6260_PALETTE_SIZE )
+ MCFG_PALETTE_INIT_OWNER(huc6260_device, huc6260)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor huc6260_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( huc6260 );
+}
diff --git a/src/devices/video/huc6260.h b/src/devices/video/huc6260.h
new file mode 100644
index 00000000000..2ff38ccf131
--- /dev/null
+++ b/src/devices/video/huc6260.h
@@ -0,0 +1,98 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Hudson/NEC HuC6260 interface and definitions
+
+**********************************************************************/
+
+
+#ifndef __HUC6260_H_
+#define __HUC6260_H_
+
+#include "emu.h"
+
+
+#define HUC6260_PALETTE_SIZE 1024
+
+/* Screen timing stuff */
+#define HUC6260_WPF 1365 /* width of a line in frame including blanking areas */
+#define HUC6260_LPF 263 /* max number of lines in a single frame */
+
+
+#define MCFG_HUC6260_NEXT_PIXEL_DATA_CB(_devcb) \
+ devcb = &huc6260_device::set_next_pixel_data_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6260_TIME_TIL_NEXT_EVENT_CB(_devcb) \
+ devcb = &huc6260_device::set_time_til_next_event_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6260_VSYNC_CHANGED_CB(_devcb) \
+ devcb = &huc6260_device::set_vsync_changed_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_HUC6260_HSYNC_CHANGED_CB(_devcb) \
+ devcb = &huc6260_device::set_hsync_changed_callback(*device, DEVCB_##_devcb);
+
+
+class huc6260_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ huc6260_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_next_pixel_data_callback(device_t &device, _Object object) { return downcast<huc6260_device &>(device).m_next_pixel_data_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_time_til_next_event_callback(device_t &device, _Object object) { return downcast<huc6260_device &>(device).m_time_til_next_event_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_vsync_changed_callback(device_t &device, _Object object) { return downcast<huc6260_device &>(device).m_vsync_changed_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_hsync_changed_callback(device_t &device, _Object object) { return downcast<huc6260_device &>(device).m_hsync_changed_cb.set_callback(object); }
+
+ void video_update(bitmap_ind16 &bitmap, const rectangle &cliprect);
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_PALETTE_INIT(huc6260);
+
+ READ8_MEMBER(palette_direct_read);
+ WRITE8_MEMBER(palette_direct_write);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+private:
+ int m_last_h;
+ int m_last_v;
+ int m_height;
+
+ /* callbacks */
+ /* Callback function to retrieve pixel data */
+ devcb_read16 m_next_pixel_data_cb;
+
+ /* TODO: Choose proper types */
+ /* Callback function to get time until next event */
+ devcb_read16 m_time_til_next_event_cb;
+
+ /* Callback function which gets called when vsync changes */
+ devcb_write_line m_vsync_changed_cb;
+
+ /* Callback function which gets called when hsync changes */
+ devcb_write_line m_hsync_changed_cb;
+
+ UINT16 m_palette[512];
+ UINT16 m_address;
+ UINT8 m_greyscales; /* Should the HuC6260 output grey or color graphics */
+ UINT8 m_blur; /* Should the edges of graphics be blurred/Select screen height 0=262, 1=263 */
+ UINT8 m_pixels_per_clock; /* Number of pixels to output per colour clock */
+ UINT16 m_pixel_data;
+ UINT8 m_pixel_clock;
+
+ emu_timer *m_timer;
+ bitmap_ind16 *m_bmp;
+};
+
+
+extern const device_type HUC6260;
+
+
+#endif
diff --git a/src/devices/video/huc6261.c b/src/devices/video/huc6261.c
new file mode 100644
index 00000000000..f22bdfba582
--- /dev/null
+++ b/src/devices/video/huc6261.c
@@ -0,0 +1,434 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Hudson/NEC HuC6261 Video Colour Encoder
+
+ The HuC6261 generates the tv control signals. A full line lasts
+ 1365 "master" cycles (typically at 21.47727Mhz).
+
+ HSync is low for 237 and high for 1128 master cycles.
+ VSync is low for 4095 master cycles (3 lines).
+ VSync changes 30 master cycles after HSync would go low.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "huc6261.h"
+
+#define LOG 0
+
+#define HUC6261_HSYNC_LENGTH 237
+#define HUC6261_HSYNC_START ( HUC6261_WPF - HUC6261_HSYNC_LENGTH )
+
+
+const device_type HUC6261 = &device_creator<huc6261_device>;
+
+
+huc6261_device::huc6261_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HUC6261, "HuC6261", tag, owner, clock, "huc6261", __FILE__),
+ device_video_interface(mconfig, *this)
+{
+ // Set up UV lookup table
+ for ( int ur = 0; ur < 256; ur++ )
+ {
+ for ( int vr = 0; vr < 256; vr++ )
+ {
+ INT32 r,g,b;
+ INT32 u = ur - 128;
+ INT32 v = vr - 128;
+
+ r = - 1.13983 * v;
+ g = -0.35465 * u - 0.58060 * v;
+ b = 2.03211 * u;
+
+ m_uv_lookup[ ( ur << 8 ) | vr ][0] = r;
+ m_uv_lookup[ ( ur << 8 ) | vr ][1] = g;
+ m_uv_lookup[ ( ur << 8 ) | vr ][2] = b;
+ }
+ }
+}
+
+
+inline UINT32 huc6261_device::yuv2rgb(UINT32 yuv)
+{
+ INT32 r, g, b;
+ UINT8 y = yuv >> 16;
+ UINT16 uv = yuv & 0xffff;
+
+ r = y + m_uv_lookup[uv][0];
+ g = y + m_uv_lookup[uv][1];
+ b = y + m_uv_lookup[uv][2];
+
+ if ( r < 0 ) r = 0;
+ if ( g < 0 ) g = 0;
+ if ( b < 0 ) b = 0;
+ if ( r > 255 ) r = 255;
+ if ( g > 255 ) g = 255;
+ if ( b > 255 ) b = 255;
+
+ return ( r << 16 ) | ( g << 8 ) | b;
+}
+
+void huc6261_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int vpos = m_screen->vpos();
+ int hpos = m_screen->hpos();
+ int h = m_last_h;
+ int v = m_last_v;
+ UINT32 *bitmap_line = &m_bmp->pix32(v);
+
+ while ( h != hpos || v != vpos )
+ {
+ if ( m_pixel_clock == 0 )
+ {
+ g_profiler.start( PROFILER_VIDEO );
+ /* Get next pixel information */
+ m_pixel_data = m_huc6270_b->next_pixel( machine().driver_data()->generic_space(), 0, 0xffff );
+ g_profiler.stop();
+ }
+
+ bitmap_line[ h ] = yuv2rgb( ( ( m_palette[m_pixel_data] & 0xff00 ) << 8 ) | ( ( m_palette[m_pixel_data] & 0xf0 ) << 8 ) | ( ( m_palette[m_pixel_data] & 0x0f ) << 4 ) );
+ m_pixel_clock = ( m_pixel_clock + 1 ) % m_pixels_per_clock;
+ h = ( h + 1 ) % HUC6261_WPF;
+
+ switch( h )
+ {
+ case HUC6261_HSYNC_START: /* Start of HSync */
+ m_huc6270_a->hsync_changed( 0 );
+ m_huc6270_b->hsync_changed( 0 );
+// if ( v == 0 )
+// {
+// /* Check if the screen should be resized */
+// m_height = HUC6261_LPF - ( m_blur ? 1 : 0 );
+// if ( m_height != video_screen_get_height( m_screen ) )
+// {
+// rectangle visible_area;
+//
+// /* TODO: Set proper visible area parameters */
+// visible_area.min_x = 64;
+// visible_area.min_y = 18;
+// visible_area.max_x = 64 + 1024 + 64 - 1;
+// visible_area.max_y = 18 + 242 - 1;
+//
+// video_screen_configure( m_screen, HUC6261_WPF, m_height, &visible_area, HZ_TO_ATTOSECONDS( device->clock / ( HUC6261_WPF * m_height ) ) );
+// }
+// }
+ break;
+
+ case 0: /* End of HSync */
+ m_huc6270_a->hsync_changed( 1 );
+ m_huc6270_b->hsync_changed( 1 );
+ m_pixel_clock = 0;
+ v = ( v + 1 ) % m_height;
+ bitmap_line = &m_bmp->pix32(v);
+ break;
+
+ case HUC6261_HSYNC_START + 30: /* End/Start of VSync */
+ if ( v>= m_height - 4 )
+ {
+ int vsync = ( v >= m_height - 4 && v < m_height - 1 ) ? 0 : 1;
+
+ m_huc6270_a->vsync_changed( vsync );
+ m_huc6270_b->vsync_changed( vsync );
+ }
+ break;
+ }
+ }
+
+ m_last_h = h;
+ m_last_v = v;
+
+ /* Reschedule timer */
+ if ( m_last_h < HUC6261_HSYNC_START )
+ {
+ /* Next event is start of HSync signal */
+ v = m_last_v;
+ h = HUC6261_HSYNC_START;
+ }
+ else if ( ( m_last_v == m_height - 4 || m_last_v == m_height - 1 ) && m_last_h < HUC6261_HSYNC_START + 30 )
+ {
+ /* Next event is start/end of VSync signal */
+ v = m_last_v;
+ h = HUC6261_HSYNC_START + 30;
+ }
+ else
+ {
+ /* Next event is end of HSync signal */
+ v = ( m_last_v + 1 ) % m_height;
+ h = 0;
+ }
+
+ /* Ask our slave device for time until next possible event */
+ {
+ UINT16 next_event_clocks = HUC6261_WPF; //m_get_time_til_next_event( 0, 0xffff );
+ int event_hpos, event_vpos;
+
+ /* Adjust for pixel clocks per pixel */
+ next_event_clocks *= m_pixels_per_clock;
+
+ /* Adjust for clocks left to go for current pixel */
+ next_event_clocks += ( m_pixels_per_clock - ( m_pixel_clock + 1 ) );
+
+ event_hpos = hpos + next_event_clocks;
+ event_vpos = vpos;
+ while ( event_hpos > HUC6261_WPF )
+ {
+ event_vpos += 1;
+ event_hpos -= HUC6261_WPF;
+ }
+
+ if ( event_vpos < v || ( event_vpos == v && event_hpos <= h ) )
+ {
+ if ( event_vpos > vpos || ( event_vpos == vpos && event_hpos > hpos ) )
+ {
+ v = event_vpos;
+ h = event_hpos;
+ }
+ }
+ }
+
+ m_timer->adjust( m_screen->time_until_pos( v, h ) );
+}
+
+
+void huc6261_device::video_update( bitmap_rgb32 &bitmap, const rectangle &cliprect )
+{
+ copybitmap( bitmap, *m_bmp, 0, 0, 0, 0, cliprect );
+}
+
+
+READ16_MEMBER( huc6261_device::read )
+{
+ UINT16 data = 0xFFFF;
+
+ switch ( offset & 1 )
+ {
+ /* Status info */
+ case 0x00:
+ {
+ UINT16 vpos = m_screen->vpos();
+ UINT16 hpos = m_screen->hpos();
+
+ data = ( vpos << 5 ) | ( m_register & 0x1F);
+
+ if ( vpos >= 22 && vpos < 262 && hpos < HUC6261_HSYNC_START )
+ {
+ data |= 0x8000;
+ }
+ }
+ break;
+
+ /* Register contents(?) */
+ case 0x01:
+ switch( m_register )
+ {
+ case 0x00:
+ data = m_control;
+ break;
+
+ case 0x01:
+ data = m_address;
+ break;
+
+ case 0x02:
+ case 0x03:
+ data = m_palette_latch;
+ m_address = ( m_address + 1 ) & 0x1FF;
+ m_palette_latch = m_palette[ m_address ];
+ break;
+
+ case 0x08:
+ data = m_priority[4] | ( m_priority[5] << 4 ) | ( m_priority[6] << 8 );
+ break;
+
+ case 0x09:
+ data = m_priority[0] | ( m_priority[1] << 4 ) | ( m_priority[2] << 8 ) | ( m_priority[3] << 12 );;
+ break;
+ }
+ break;
+ }
+
+ return data;
+}
+
+
+WRITE16_MEMBER( huc6261_device::write )
+{
+ switch ( offset & 1 )
+ {
+ /* Register */
+ case 0x00:
+ m_register = data;
+ break;
+
+ case 0x01:
+ logerror("huc6261: writing 0x%04x to register 0x%02x\n", data, m_register );
+ switch( m_register )
+ {
+ /* Control register */
+ // -x-------------- Enable HuC6271: 0 - disabled, 1 - enabled
+ // --x------------- Enable HuC6272 BG3: 0 - disabled, 1 - enabled
+ // ---x------------ Enable HuC6272 BG2: 0 - disabled, 1 - enabled
+ // ----x----------- Enable Huc6272 BG1: 0 - disabled, 1 - enabled
+ // -----x---------- Enable HuC6272 BG0: 0 - disabled, 1 - enabled
+ // ------x--------- Enable HuC6270 SPR: 0 - disabled, 1 - enabled
+ // -------x-------- Enable HuC6270 BG: 0 - disabled, 1 - enabled
+ // --------x------- Number of SPR colors?: 0 - 16, 1 - 256
+ // ---------x------ Number of BG colors?: 0 - 16, 1 - 256
+ // ------------x--- Dot clock: 0 - 5MHz, 1 - 7MHz
+ // -------------x-- Synchronization: 0 - internal, 1 - external
+ // --------------xx Screen height: 00 - 262 lines, 01 - 263 lines, 10 - interlace, 11 - unknown/undefined
+ case 0x00:
+ m_control = data;
+ m_pixels_per_clock = ( data & 0x04 ) ? 3 : 4;
+ break;
+
+ // Palette address
+ case 0x01:
+ m_address = data & 0x1FF;
+ m_palette_latch = m_palette[ m_address ];
+ break;
+
+ // Palette data
+ case 0x02:
+ m_palette_latch = data;
+ m_palette[ m_address ] = m_palette_latch;
+ m_address = ( m_address + 1 ) & 0x1FF;
+ break;
+
+ // Palette offset 0
+ case 0x04:
+ break;
+
+ // Palette offset 1
+ case 0x05:
+ break;
+
+ // Palette offset 2
+ case 0x06:
+ break;
+
+ // Palette offset 3
+ case 0x07:
+ break;
+
+ // Priority 0
+ // -----xxx-------- HuC6271 Rainbow priority
+ // ---------xxx---- HuC6270 SPR priority
+ // -------------xxx HuC6270 BG priority
+ case 0x08:
+ m_priority[4] = ( data >> 0 ) & 0x07;
+ m_priority[5] = ( data >> 4 ) & 0x07;
+ m_priority[6] = ( data >> 8 ) & 0x07;
+ break;
+
+ // Priority 1
+ // -xxx------------ HuC6272 BG3 priority
+ // -----xxx-------- HuC6272 BG2 priority
+ // ---------xxx---- HuC6272 BG1 priority
+ // -------------xxx HuC6272 BG0 priority
+ case 0x09:
+ m_priority[0] = ( data >> 0 ) & 0x07;
+ m_priority[1] = ( data >> 4 ) & 0x07;
+ m_priority[2] = ( data >> 8 ) & 0x07;
+ m_priority[3] = ( data >> 12 ) & 0x07;
+ break;
+
+ // Chroma key Y
+ case 0x0A:
+ break;
+
+ // Chroma key U
+ case 0x0B:
+ break;
+
+ // Chroma key V
+ case 0x0C:
+ break;
+
+ //
+ case 0x0D:
+ break;
+
+ //
+ case 0x0E:
+ break;
+
+ //
+ case 0x0F:
+ break;
+
+ //
+ case 0x10:
+ break;
+
+ //
+ case 0x11:
+ break;
+
+ //
+ case 0x12:
+ break;
+
+ //
+ case 0x13:
+ break;
+
+ //
+ case 0x14:
+ break;
+
+ //
+ case 0x15:
+ break;
+ }
+ break;
+ }
+}
+
+
+void huc6261_device::device_start()
+{
+ /* Make sure we are supplied all our mandatory tags */
+ assert( m_huc6270_a_tag != NULL );
+ assert( m_huc6270_b_tag != NULL );
+
+ m_timer = timer_alloc();
+ m_huc6270_a = machine().device<huc6270_device>(m_huc6270_a_tag);
+ m_huc6270_b = machine().device<huc6270_device>(m_huc6270_b_tag);
+
+ m_bmp = auto_bitmap_rgb32_alloc( machine(), HUC6261_WPF, HUC6261_LPF );
+
+ /* We want to have valid devices */
+ assert( m_huc6270_a != NULL );
+ assert( m_huc6270_b != NULL );
+
+ save_item(NAME(m_last_h));
+ save_item(NAME(m_last_v));
+ save_item(NAME(m_height));
+ save_item(NAME(m_palette));
+ save_item(NAME(m_palette_latch));
+ save_item(NAME(m_address));
+ save_item(NAME(m_register));
+ save_item(NAME(m_control));
+ save_item(NAME(m_priority));
+ save_item(NAME(m_pixels_per_clock));
+ save_item(NAME(m_pixel_data));
+ save_item(NAME(m_pixel_clock));
+}
+
+
+void huc6261_device::device_reset()
+{
+ m_register = 0;
+ m_pixels_per_clock = 4;
+ m_height = 263;
+ m_pixel_clock = 0;
+
+ memset(m_palette, 0, sizeof(m_palette));
+
+ m_last_v = m_screen->vpos();
+ m_last_h = m_screen->hpos();
+ m_timer->adjust( m_screen->time_until_pos( ( m_screen->vpos() + 1 ) % 263, 0 ) );
+}
diff --git a/src/devices/video/huc6261.h b/src/devices/video/huc6261.h
new file mode 100644
index 00000000000..e5ef43a8429
--- /dev/null
+++ b/src/devices/video/huc6261.h
@@ -0,0 +1,81 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Hudson/NEC HuC6261 interface and definitions
+
+**********************************************************************/
+
+
+#ifndef __HUC6261_H_
+#define __HUC6261_H_
+
+#include "emu.h"
+#include "video/huc6270.h"
+
+
+/* Screen timing stuff */
+#define HUC6261_WPF 1365 /* width of a line in frame including blanking areas */
+#define HUC6261_LPF 263 /* max number of lines in a single frame */
+
+
+#define MCFG_HUC6261_VDC1(_tag) \
+ huc6261_device::set_vdc1_tag(*device, _tag);
+
+#define MCFG_HUC6261_VDC2(_tag) \
+ huc6261_device::set_vdc2_tag(*device, _tag);
+
+
+class huc6261_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ huc6261_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_vdc1_tag(device_t &device, const char *tag) { downcast<huc6261_device &>(device).m_huc6270_a_tag = tag; }
+ static void set_vdc2_tag(device_t &device, const char *tag) { downcast<huc6261_device &>(device).m_huc6270_b_tag = tag; }
+
+ void video_update(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ DECLARE_READ16_MEMBER( read );
+ DECLARE_WRITE16_MEMBER( write );
+
+ inline UINT32 yuv2rgb(UINT32 yuv);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ const char *m_huc6270_a_tag;
+ const char *m_huc6270_b_tag;
+
+ huc6270_device *m_huc6270_a;
+ huc6270_device *m_huc6270_b;
+ int m_last_h;
+ int m_last_v;
+ int m_height;
+
+ UINT16 m_palette[512];
+ UINT16 m_address;
+ UINT16 m_palette_latch;
+ UINT16 m_register;
+ UINT16 m_control;
+ UINT8 m_priority[7];
+
+ UINT8 m_pixels_per_clock; /* Number of pixels to output per colour clock */
+ UINT16 m_pixel_data;
+ UINT8 m_pixel_clock;
+
+ emu_timer *m_timer;
+ bitmap_rgb32 *m_bmp;
+ INT32 m_uv_lookup[65536][3];
+};
+
+
+extern const device_type HUC6261;
+
+
+#endif
diff --git a/src/devices/video/huc6270.c b/src/devices/video/huc6270.c
new file mode 100644
index 00000000000..c0fe5225c8e
--- /dev/null
+++ b/src/devices/video/huc6270.c
@@ -0,0 +1,895 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ NEC HuC6270 Video Display Controller
+
+ The HuC6270 basically outputs a 9-bit stream of pixel data which
+ holds a color index, a palette index, and an indication whether
+ the pixel contains background data or from sprite data.
+
+ This data can be used by a colour encoder to output graphics.
+
+ A regular screen is displayed as follows:
+
+ |<- HDS ->|<-- HDW -->|<- HDE ->|<- HSW ->|
+ |---------|-----------------------|---------|---------|
+ VSW | |
+ |---------|-----------------------|---------|---------|
+ VDS | |
+ | overscan |
+ |---------|-----------------------|---------|---------|
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ VDW | overscan| active display | overscan |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ |---------|-----------------------|---------|---------|
+ VCR | overscan |
+ | |
+ |---------|-----------------------|---------|---------|
+ ^end hsync
+ ^start vsync (30 cycles after hsync)
+
+
+KNOWN ISSUES
+ - Violent Soldier (probably connected):
+ - In the intro some artefacts appear at the top of the
+ screen every now and then.
+ - In ccovell's splitres test not all sections seem to be aligned properly.
+ - Side Arms: Seems to be totally broken.
+
+
+TODO
+ - Fix timing of VRAM-SATB DMA
+ - Implement VRAM-VRAM DMA
+ - DMA speeds differ depending on the dot clock selected in the huc6270
+
+**********************************************************************/
+
+#include "emu.h"
+#include "huc6270.h"
+
+#define LOG 0
+
+enum {
+ MAWR = 0x00,
+ MARR = 0x01,
+ VxR = 0x02,
+ CR = 0x05,
+ RCR = 0x06,
+ BXR = 0x07,
+ BYR = 0x08,
+ MWR = 0x09,
+ HSR = 0x0A,
+ HDR = 0x0B,
+ VPR = 0x0C,
+ VDW = 0x0D,
+ VCR = 0x0E,
+ DCR = 0x0F,
+ SOUR = 0x10,
+ DESR = 0x11,
+ LENR = 0x12,
+ DVSSR = 0x13
+};
+
+ALLOW_SAVE_TYPE(huc6270_device::huc6270_v_state);
+ALLOW_SAVE_TYPE(huc6270_device::huc6270_h_state);
+
+
+/* Bits in the VDC status register */
+#define HUC6270_BSY 0x40 /* Set when the VDC accesses VRAM */
+#define HUC6270_VD 0x20 /* Set when in the vertical blanking period */
+#define HUC6270_DV 0x10 /* Set when a VRAM > VRAM DMA transfer is done */
+#define HUC6270_DS 0x08 /* Set when a VRAM > SATB DMA transfer is done */
+#define HUC6270_RR 0x04 /* Set when the current scanline equals the RCR register */
+#define HUC6270_OR 0x02 /* Set when there are more than 16 sprites on a line */
+#define HUC6270_CR 0x01 /* Set when sprite #0 overlaps with another sprite */
+
+
+const device_type HUC6270 = &device_creator<huc6270_device>;
+
+
+const UINT8 huc6270_device::vram_increments[4] = { 1, 32, 64, 128 };
+
+huc6270_device::huc6270_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HUC6270, "HuC6270 VDC", tag, owner, clock, "huc6270", __FILE__),
+ m_vram_size(0),
+ m_irq_changed_cb(*this)
+{
+}
+
+
+/*
+ Read one row of tile data from video ram
+*/
+inline void huc6270_device::fetch_bat_tile_row()
+{
+ UINT16 bat_data, data1, data2, data3, data4, tile_palette;
+ int i;
+
+ bat_data = m_vram[ m_bat_address & m_vram_mask ];
+ tile_palette = ( bat_data >> 8 ) & 0xF0;
+ data1 = m_vram[ ( ( ( bat_data & 0x0FFF ) << 4 ) + m_bat_row + 0 ) & m_vram_mask ];
+ data2 = ( data1 >> 7 ) & 0x1FE;
+ data3 = m_vram[ ( ( ( bat_data & 0x0FFF ) << 4 ) + m_bat_row + 8 ) & m_vram_mask ];
+ data4 = ( data3 >> 5 ) & 0x7F8;
+ data3 <<= 2;
+
+ for ( i = 7; i >= 0; i-- )
+ {
+ UINT16 c = ( data1 & 0x01 ) | ( data2 & 0x02 ) | ( data3 & 0x04 ) | ( data4 & 0x08 );
+
+ /* Colour 0 for background tiles is always taken from palette 0 */
+ if ( c )
+ c |= tile_palette;
+
+ m_bat_tile_row[i] = c;
+
+ data1 >>= 1;
+ data2 >>= 1;
+ data3 >>= 1;
+ data4 >>= 1;
+ }
+}
+
+
+void huc6270_device::add_sprite( int index, int x, int pattern, int line, int flip_x, int palette, int priority, int sat_lsb )
+{
+ int i = m_sprites_this_line;
+
+ if ( i < 16 )
+ {
+ UINT32 b0, b1, b2, b3;
+ int j;
+
+ if ( flip_x )
+ flip_x = 0x0F;
+
+ pattern += ( ( line >> 4 ) << 1 );
+
+ if ( ( m_mwr & 0x0c ) == 0x04 )
+ {
+ if ( ! sat_lsb )
+ {
+ b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x00 ) & m_vram_mask ];
+ b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x10 ) & m_vram_mask ] << 1;
+ }
+ else
+ {
+ b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x20 ) & m_vram_mask ];
+ b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x30 ) & m_vram_mask ] << 1;
+ }
+ b2 = 0;
+ b3 = 0;
+ }
+ else
+ {
+ b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x00 ) & m_vram_mask ];
+ b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x10 ) & m_vram_mask ] << 1;
+ b2 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x20 ) & m_vram_mask ] << 2;
+ b3 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x30 ) & m_vram_mask ] << 3;
+ }
+
+ for ( j = 15; j >= 0; j-- )
+ {
+ UINT8 data = ( b3 & 0x08 ) | ( b2 & 0x04 ) | ( b1 & 0x02 ) | ( b0 & 0x01 );
+
+ if ( data )
+ {
+ data |= palette << 4;
+
+ if ( x + ( j ^ flip_x ) < 1024 )
+ {
+ if (! m_sprite_row[ x + ( j ^ flip_x ) ] )
+ {
+ m_sprite_row[ x + ( j ^ flip_x ) ] = ( priority ? 0x4000 : 0x0000 ) | ( index << 8 ) | data;
+ }
+ else
+ {
+ if ( ! ( m_sprite_row[ x + ( j ^ flip_x ) ] & 0xFF00 ) )
+ {
+ /* Sprite 0 collission */
+ m_sprite_row[ x + ( j ^ flip_x ) ] |= 0x8000;
+ }
+ }
+ }
+ }
+
+ b0 >>= 1;
+ b1 >>= 1;
+ b2 >>= 1;
+ b3 >>= 1;
+ }
+
+ m_sprites_this_line += 1;
+ }
+}
+
+
+void huc6270_device::select_sprites()
+{
+ int i;
+
+ m_sprites_this_line = 0;
+ memset( m_sprite_row, 0, sizeof( m_sprite_row ) );
+ m_sprite_row_index = 0x20;
+
+ for ( i = 0; i < 4 * 64; i += 4 )
+ {
+ static const int cgy_table[4] = { 16, 32, 64, 64 };
+ int cgy = ( m_sat[i+3] >> 12 ) & 0x03;
+ int height = cgy_table[ cgy ];
+ int sprite_line = m_raster_count - m_sat[i];
+
+ if ( sprite_line >= 0 && sprite_line < height )
+ {
+ int pattern = m_sat[i+2] >> 1;
+ int sat_lsb = m_sat[i+2] & 0x01;
+ int palette = m_sat[i+3] & 0x0F;
+ int priority = m_sat[i+3] & 0x80;
+ int cgx = m_sat[i+3] & 0x0100;
+
+ /* If CGY is set to 1, bit 1 of the sprite pattern index is forced to 0 */
+ if ( cgy & 1 )
+ pattern &= ~0x0002;
+
+ /* If CGY is set to 2 or 3, bits 1 and 2 of the sprite pattern index are forced to 0 */
+ if ( cgy & 2 )
+ pattern &= ~0x0006;
+
+ /* Recalculate line index when sprite is flipped vertically */
+ if ( m_sat[i+3] & 0x8000 )
+ sprite_line = ( height - 1 ) - sprite_line;
+
+ /* Is the sprite 32 pixels wide */
+ if ( cgx )
+ {
+ /* If CGX is set, bit 0 of the sprite pattern index is forced to 0 */
+ pattern &= ~0x0001;
+
+ /* Check for horizontal flip */
+ if ( m_sat[i+3] & 0x0800 )
+ {
+ /* Add to our list of sprites for this line */
+ add_sprite( i/4, m_sat[i+1], pattern + 1, sprite_line, 1, palette, priority, sat_lsb );
+ add_sprite( i/4, m_sat[i+1] + 16, pattern, sprite_line, 1, palette, priority, sat_lsb );
+ }
+ else
+ {
+ /* Add to our list of sprites for this line */
+ add_sprite( i/4, m_sat[i+1], pattern, sprite_line, 0, palette, priority, sat_lsb );
+ add_sprite( i/4, m_sat[i+1] + 16, pattern + 1, sprite_line, 0, palette, priority, sat_lsb );
+ }
+ }
+ else
+ {
+ /* Add to our list of sprites for this line */
+ add_sprite( i/4, m_sat[i+1], pattern, sprite_line, m_sat[i+3] & 0x0800, palette, priority, sat_lsb );
+ }
+ }
+ }
+
+ /* Check for sprite overflow */
+ if ( m_sprites_this_line >= 16 )
+ {
+ /* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
+ if ( m_cr & 0x02 )
+ {
+ m_status |= HUC6270_OR;
+ m_irq_changed_cb( ASSERT_LINE );
+ }
+ }
+}
+
+
+inline void huc6270_device::handle_vblank()
+{
+ if ( ! m_vd_triggered )
+ {
+ if ( m_cr & 0x08 )
+ {
+ m_status |= HUC6270_VD;
+ m_irq_changed_cb( ASSERT_LINE );
+ }
+
+ /* Should we initiate a VRAM->SATB DMA transfer.
+ The timing for this is incorrect.
+ */
+ if ( m_dvssr_written || ( m_dcr & 0x10 ) )
+ {
+ int i;
+
+ if (LOG) logerror("SATB transfer from %05x\n", m_dvssr << 1 );
+ for ( i = 0; i < 4 * 64; i += 4 )
+ {
+ m_sat[i + 0] = m_vram[ ( m_dvssr + i + 0 ) & m_vram_mask ] & 0x03FF;
+ m_sat[i + 1] = m_vram[ ( m_dvssr + i + 1 ) & m_vram_mask ] & 0x03FF;
+ m_sat[i + 2] = m_vram[ ( m_dvssr + i + 2 ) & m_vram_mask ] & 0x07FF;
+ m_sat[i + 3] = m_vram[ ( m_dvssr + i + 3 ) & m_vram_mask ];
+ }
+ m_dvssr_written = 0;
+
+ /* Generate SATB interrupt if requested */
+ if ( m_dcr & 0x01 )
+ {
+ m_satb_countdown = 4;
+// m_status |= HUC6270_DS;
+// m_irq_changed_cb( ASSERT_LINE );
+ }
+ }
+
+ m_vd_triggered = 1;
+ }
+}
+
+
+inline void huc6270_device::next_vert_state()
+{
+ switch ( m_vert_state )
+ {
+ case HUC6270_VSW:
+ m_vert_state = HUC6270_VDS;
+ m_vert_to_go = ( ( m_vpr >> 8 ) & 0xFF ) + 2;
+ break;
+
+ case HUC6270_VDS:
+ m_vert_state = HUC6270_VDW;
+ m_vert_to_go = ( m_vdw & 0x1FF ) + 1;
+ m_byr_latched = m_byr;
+ m_vd_triggered = 0;
+ break;
+
+ case HUC6270_VDW:
+ m_vert_state = HUC6270_VCR;
+ m_vert_to_go = ( m_vcr & 0xFF );
+ handle_vblank();
+ break;
+
+ case HUC6270_VCR:
+ m_vert_state = HUC6270_VSW;
+ m_vert_to_go = ( m_vpr & 0x1F ) + 1;
+ break;
+ }
+}
+
+
+inline void huc6270_device::next_horz_state()
+{
+ switch ( m_horz_state )
+ {
+ case HUC6270_HDS:
+ m_bxr_latched = m_bxr;
+//if (LOG) printf("latched bxr vpos=%d, hpos=%d\n", video_screen_get_vpos(device->machine->first_screen()), video_screen_get_hpos(device->machine->first_screen()));
+ m_horz_state = HUC6270_HDW;
+ m_horz_to_go = ( m_hdr & 0x7F ) + 1;
+ {
+ static const int width_shift[4] = { 5, 6, 7, 7 };
+ UINT16 v;
+
+ v = ( m_byr_latched ) & ( ( m_mwr & 0x40 ) ? 0x1FF : 0xFF );
+ m_bat_row = v & 7;
+ m_bat_address_mask = ( 1 << width_shift[ ( m_mwr >> 4 ) & 0x03 ] ) - 1;
+ m_bat_address = ( ( v >> 3 ) << ( width_shift[ ( m_mwr >> 4 ) & 0x03 ] ) )
+ | ( ( m_bxr_latched >> 3 ) & m_bat_address_mask );
+ m_bat_column = m_bxr & 7;
+ fetch_bat_tile_row();
+ }
+ break;
+
+ case HUC6270_HDW:
+ m_horz_state = HUC6270_HDE;
+ m_horz_to_go = ( ( m_hdr >> 8 ) & 0x7F ) + 1;
+ break;
+
+ case HUC6270_HDE:
+ m_horz_state = HUC6270_HSW;
+ m_horz_to_go = ( m_hsr & 0x1F ) + 1;
+ break;
+
+ case HUC6270_HSW:
+ m_horz_state = HUC6270_HDS;
+ m_horz_to_go = MAX( ( ( m_hsr >> 8 ) & 0x7F ), 2 ) + 1;
+
+ /* If section has ended, advance to next vertical state */
+ while ( m_vert_to_go == 0 )
+ next_vert_state();
+
+ /* Select sprites for the coming line */
+ select_sprites();
+ break;
+ }
+ m_horz_steps = 0;
+}
+
+
+READ16_MEMBER( huc6270_device::next_pixel )
+{
+ UINT16 data = HUC6270_SPRITE;
+
+ /* Check if we're on an active display line */
+ if ( m_vert_state == HUC6270_VDW )
+ {
+ /* Check if we're in active display area */
+ if ( m_horz_state == HUC6270_HDW )
+ {
+ UINT8 sprite_data = m_sprite_row[ m_sprite_row_index ] & 0x00FF;
+ int collission = ( m_sprite_row[ m_sprite_row_index ] & 0x8000 ) ? 1 : 0;
+
+ if ( m_cr & 0x80 )
+ {
+ data = HUC6270_BACKGROUND | m_bat_tile_row[ m_bat_column ];
+ if ( sprite_data && ( m_cr & 0x40 ) )
+ {
+ if ( m_sprite_row[ m_sprite_row_index ] & 0x4000 )
+ {
+ data = HUC6270_SPRITE | sprite_data;
+ }
+ else
+ {
+ if ( data == HUC6270_BACKGROUND )
+ {
+ data = HUC6270_SPRITE | sprite_data;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( m_cr & 0x40 )
+ {
+ data = HUC6270_SPRITE | sprite_data;
+ }
+ }
+
+ m_sprite_row_index = m_sprite_row_index + 1;
+ m_bat_column += 1;
+ if ( m_bat_column >= 8 )
+ {
+ m_bat_address = ( m_bat_address & ~m_bat_address_mask )
+ | ( ( m_bat_address + 1 ) & m_bat_address_mask );
+ m_bat_column = 0;
+ fetch_bat_tile_row();
+ }
+
+ if ( collission && ( m_cr & 0x01 ) )
+ {
+ m_status |= HUC6270_CR;
+ m_irq_changed_cb( ASSERT_LINE );
+ }
+ }
+ }
+
+ m_horz_steps++;
+ if ( m_horz_steps == 8 )
+ {
+ m_horz_to_go -= 1;
+ m_horz_steps = 0;
+ while ( m_horz_to_go == 0 )
+ next_horz_state();
+ }
+ return data;
+}
+
+
+//inline READ16_MEMBER( huc6270_device::time_until_next_event )
+//{
+// return m_horz_to_go * 8 + m_horz_steps;
+//}
+
+
+WRITE_LINE_MEMBER( huc6270_device::vsync_changed )
+{
+ state &= 0x01;
+ if ( m_vsync != state )
+ {
+ /* Check for low->high VSYNC transition */
+ if ( state )
+ {
+ m_vert_state = HUC6270_VCR;
+ m_vert_to_go = 0;
+
+ while ( m_vert_to_go == 0 )
+ next_vert_state();
+ }
+ else
+ /* High->low transition */
+ {
+ handle_vblank();
+
+ /* Should we perform VRAM-VRAM dma.
+ The timing for this is incorrect.
+ */
+ if ( m_dma_enabled )
+ {
+ int desr_inc = ( m_dcr & 0x0008 ) ? -1 : +1;
+ int sour_inc = ( m_dcr & 0x0004 ) ? -1 : +1;
+
+ if (LOG) logerror("doing dma sour = %04x, desr = %04x, lenr = %04x\n", m_sour, m_desr, m_lenr );
+ do {
+ UINT16 data = m_vram[ m_sour & m_vram_mask ];
+ m_vram[ m_desr & m_vram_mask ] = data;
+ m_sour += sour_inc;
+ m_desr += desr_inc;
+ m_lenr -= 1;
+ } while ( m_lenr != 0xFFFF );
+
+ if ( m_dcr & 0x0002 )
+ {
+ m_status |= HUC6270_DV;
+ m_irq_changed_cb( ASSERT_LINE );
+ }
+ m_dma_enabled = 0;
+ }
+ }
+ }
+
+ m_vsync = state;
+}
+
+
+WRITE_LINE_MEMBER( huc6270_device::hsync_changed )
+{
+ state &= 0x01;
+
+ /* Check for high->low HSYNC transition */
+ /* Check for low->high HSYNC transition */
+ if( ! m_hsync && state )
+ {
+ if ( m_satb_countdown )
+ {
+ m_satb_countdown--;
+
+ if ( m_satb_countdown == 0 )
+ {
+ m_status |= HUC6270_DS;
+ m_irq_changed_cb( ASSERT_LINE );
+ }
+ }
+
+ m_horz_state = HUC6270_HSW;
+ m_horz_to_go = 0;
+ m_horz_steps = 0;
+ m_byr_latched += 1;
+ m_raster_count += 1;
+ if ( m_vert_to_go == 1 && m_vert_state == HUC6270_VDS )
+ {
+ m_raster_count = 0x40;
+ }
+
+ m_vert_to_go -= 1;
+
+ while ( m_horz_to_go == 0 )
+ next_horz_state();
+
+ if ( m_raster_count == m_rcr && ( m_cr & 0x04 ) )
+ {
+ m_status |= HUC6270_RR;
+ m_irq_changed_cb( ASSERT_LINE );
+ }
+ }
+
+ m_hsync = state;
+}
+
+
+READ8_MEMBER( huc6270_device::read )
+{
+ UINT8 data = 0x00;
+
+ switch ( offset & 3 )
+ {
+ case 0x00: /* status */
+ data = m_status;
+ m_status &= ~( HUC6270_VD | HUC6270_DV | HUC6270_RR | HUC6270_CR | HUC6270_OR | HUC6270_DS );
+ m_irq_changed_cb( CLEAR_LINE );
+ break;
+
+ case 0x02:
+ data = m_vrr & 0xFF;
+ break;
+
+ case 0x03:
+ data = m_vrr >> 8;
+ if ( m_register_index == VxR )
+ {
+ m_marr += vram_increments[ ( m_cr >> 11 ) & 3 ];
+ m_vrr = m_vram[ m_marr & m_vram_mask ];
+ }
+ break;
+ }
+ return data;
+}
+
+
+WRITE8_MEMBER( huc6270_device::write )
+{
+ if (LOG) logerror("%s: huc6270 write %02x <- %02x ", machine().describe_context(), offset, data);
+
+ switch ( offset & 3 )
+ {
+ case 0x00: /* VDC register select */
+ m_register_index = data & 0x1F;
+ break;
+
+ case 0x02: /* VDC data LSB */
+ switch ( m_register_index )
+ {
+ case MAWR: /* memory address write register LSB */
+ m_mawr = ( m_mawr & 0xFF00 ) | data;
+ break;
+
+ case MARR: /* memory address read register LSB */
+ m_marr = ( m_marr & 0xFF00 ) | data;
+ m_vrr = m_vram[ m_marr & m_vram_mask ];
+ break;
+
+ case VxR: /* vram write data LSB */
+ m_vwr = ( m_vwr & 0xFF00 ) | data;
+ break;
+
+ case CR: /* control register LSB */
+ m_cr = ( m_cr & 0xFF00 ) | data;
+ break;
+
+ case RCR: /* raster compare register LSB */
+ m_rcr = ( m_rcr & 0x0300 ) | data;
+//printf("%s: RCR set to %03x\n", machine().describe_context(), m_rcr);
+// if ( m_raster_count == m_rcr && m_cr & 0x04 )
+// {
+// m_status |= HUC6270_RR;
+// m_irq_changed_cb( ASSERT_LINE );
+// }
+//if (LOG) printf("%04x: RCR (%03x) written at %d,%d\n", activecpu_get_pc(), huc6270->m_rcr, video_screen_get_vpos(device->machine->first_screen()), video_screen_get_hpos(device->machine->first_screen()) );
+ break;
+
+ case BXR: /* background x-scroll register LSB */
+ m_bxr = ( m_bxr & 0x0300 ) | data;
+//if (LOG) printf("*********************** BXR written %d at %d,%d\n", m_bxr, video_screen_get_vpos(device->machine->first_screen()), video_screen_get_hpos(device->machine->first_screen()) );
+ break;
+
+ case BYR: /* background y-scroll register LSB */
+ m_byr = ( m_byr & 0x0100 ) | data;
+ m_byr_latched = m_byr;
+//if (LOG) printf("******************** BYR written %d at %d,%d\n", huc6270->m_byr, video_screen_get_vpos(device->machine->first_screen()), video_screen_get_hpos(device->machine->first_screen()) );
+ break;
+
+ case MWR: /* memory width register LSB */
+ m_mwr = ( m_mwr & 0xFF00 ) | data;
+ break;
+
+ case HSR: /* horizontal sync register LSB */
+ m_hsr = ( m_hsr & 0xFF00 ) | data;
+ break;
+
+ case HDR: /* horizontal display register LSB */
+ m_hdr = ( m_hdr & 0xFF00 ) | data;
+ break;
+
+ case VPR: /* vertical sync register LSB */
+ m_vpr = ( m_vpr & 0xFF00 ) | data;
+ break;
+
+ case VDW: /* vertical display register LSB */
+ m_vdw = ( m_vdw & 0xFF00 ) | data;
+ break;
+
+ case VCR: /* vertical display end position register LSB */
+ m_vcr = ( m_vcr & 0xFF00 ) | data;
+ break;
+
+ case DCR: /* DMA control register LSB */
+ m_dcr = ( m_dcr & 0xFF00 ) | data;
+ break;
+
+ case SOUR: /* DMA source address register LSB */
+ m_sour = ( m_sour & 0xFF00 ) | data;
+ break;
+
+ case DESR: /* DMA destination address register LSB */
+ m_desr = ( m_desr & 0xFF00 ) | data;
+ break;
+
+ case LENR: /* DMA length register LSB */
+ m_lenr = ( m_lenr & 0xFF00 ) | data;
+ break;
+
+ case DVSSR: /* Sprite attribute table LSB */
+ m_dvssr = ( m_dvssr & 0xFF00 ) | data;
+ m_dvssr_written = 1;
+ break;
+ }
+ break;
+
+ case 0x03: /* VDC data MSB */
+ switch ( m_register_index )
+ {
+ case MAWR: /* memory address write register MSB */
+ m_mawr = ( m_mawr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case MARR: /* memory address read register MSB */
+ m_marr = ( m_marr & 0x00FF ) | ( data << 8 );
+ m_vrr = m_vram[ m_marr & m_vram_mask ];
+ break;
+
+ case VxR: /* vram write data MSB */
+ m_vwr = ( m_vwr & 0x00FF ) | ( data << 8 );
+ m_vram[ m_mawr & m_vram_mask ] = m_vwr;
+ m_mawr += vram_increments[ ( m_cr >> 11 ) & 3 ];
+ break;
+
+ case CR: /* control register MSB */
+ m_cr = ( m_cr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case RCR: /* raster compare register MSB */
+ m_rcr = ( m_rcr & 0x00FF ) | ( ( data & 0x03 ) << 8 );
+//printf("%s: RCR set to %03x\n", machine().describe_context(), m_rcr);
+// if ( m_raster_count == m_rcr && m_cr & 0x04 )
+// {
+// m_status |= HUC6270_RR;
+// m_irq_changed_cb( ASSERT_LINE );
+// }
+ break;
+
+ case BXR: /* background x-scroll register MSB */
+ m_bxr = ( m_bxr & 0x00FF ) | ( ( data & 0x03 ) << 8 );
+ break;
+
+ case BYR: /* background y-scroll register MSB */
+ m_byr = ( m_byr & 0x00FF ) | ( ( data & 0x01 ) << 8 );
+ m_byr_latched = m_byr;
+ break;
+
+ case MWR: /* memory width register MSB */
+ m_mwr = ( m_mwr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case HSR: /* horizontal sync register MSB */
+ m_hsr = ( m_hsr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case HDR: /* horizontal display register MSB */
+ m_hdr = ( m_hdr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case VPR: /* vertical sync register MSB */
+ m_vpr = ( m_vpr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case VDW: /* vertical display register MSB */
+ m_vdw = ( m_vdw & 0x00FF ) | ( data << 8 );
+ break;
+
+ case VCR: /* vertical display end position register MSB */
+ m_vcr = ( m_vcr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case DCR: /* DMA control register MSB */
+ m_dcr = ( m_dcr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case SOUR: /* DMA source address register MSB */
+ m_sour = ( m_sour & 0x00FF ) | ( data << 8 );
+ break;
+
+ case DESR: /* DMA destination address register MSB */
+ m_desr = ( m_desr & 0x00FF ) | ( data << 8 );
+ break;
+
+ case LENR: /* DMA length register MSB */
+ m_lenr = ( m_lenr & 0x00FF ) | ( data << 8 );
+ m_dma_enabled = 1;
+//logerror("DMA is not supported yet.\n");
+ break;
+
+ case DVSSR: /* Sprite attribute table MSB */
+ m_dvssr = ( m_dvssr & 0x00FF ) | ( data << 8 );
+ m_dvssr_written = 1;
+ break;
+ }
+ break;
+ }
+ if (LOG) logerror("\n");
+}
+
+
+void huc6270_device::device_start()
+{
+ /* Resolve callbacks */
+ m_irq_changed_cb.resolve_safe();
+
+ m_vram = auto_alloc_array_clear(machine(), UINT16, m_vram_size/sizeof(UINT16));
+ m_vram_mask = (m_vram_size >> 1) - 1;
+
+ save_pointer(NAME(m_vram), m_vram_size/sizeof(UINT16));
+
+ save_item(NAME(m_register_index));
+ save_item(NAME(m_mawr));
+ save_item(NAME(m_marr));
+ save_item(NAME(m_vrr));
+ save_item(NAME(m_vwr));
+ save_item(NAME(m_cr));
+ save_item(NAME(m_rcr));
+ save_item(NAME(m_bxr));
+ save_item(NAME(m_byr));
+ save_item(NAME(m_mwr));
+ save_item(NAME(m_hsr));
+ save_item(NAME(m_hdr));
+ save_item(NAME(m_vpr));
+ save_item(NAME(m_vdw));
+ save_item(NAME(m_vcr));
+ save_item(NAME(m_dcr));
+ save_item(NAME(m_sour));
+ save_item(NAME(m_desr));
+ save_item(NAME(m_lenr));
+ save_item(NAME(m_dvssr));
+ save_item(NAME(m_status));
+ save_item(NAME(m_hsync));
+ save_item(NAME(m_vsync));
+ save_item(NAME(m_vert_state));
+ save_item(NAME(m_horz_state));
+ save_item(NAME(m_vd_triggered));
+ save_item(NAME(m_vert_to_go));
+ save_item(NAME(m_horz_to_go));
+ save_item(NAME(m_horz_steps));
+ save_item(NAME(m_raster_count));
+ save_item(NAME(m_dvssr_written));
+ save_item(NAME(m_satb_countdown));
+ save_item(NAME(m_dma_enabled));
+ save_item(NAME(m_byr_latched));
+ save_item(NAME(m_bxr_latched));
+ save_item(NAME(m_bat_address));
+ save_item(NAME(m_bat_address_mask));
+ save_item(NAME(m_bat_row));
+ save_item(NAME(m_bat_column));
+ save_item(NAME(m_bat_tile_row));
+ save_item(NAME(m_sat));
+ save_item(NAME(m_sprites_this_line));
+ save_item(NAME(m_sprite_row_index));
+ save_item(NAME(m_sprite_row));
+}
+
+
+void huc6270_device::device_reset()
+{
+ m_mawr = 0;
+ m_marr = 0;
+ m_vrr = 0;
+ m_vwr = 0;
+ m_cr = 0;
+ m_rcr = 0;
+ m_bxr = 0;
+ m_byr = 0;
+ m_mwr = 0;
+ m_hsr = 0x0202; /* Take some defaults for horizontal timing */
+ m_hdr = 0x041f;
+ m_vpr = 0x0f02; /* Take some defaults for vertical timing */
+ m_vdw = 0x00ef;
+ m_vcr = 0x0004;
+ m_dcr = 0;
+ m_sour = 0;
+ m_lenr = 0;
+ m_dvssr = 0;
+ m_status = 0;
+ m_vd_triggered = 0;
+ m_dvssr_written = 0;
+ m_satb_countdown = 0;
+ m_raster_count = 0x4000;
+ m_vert_to_go = 0;
+ m_vert_state = HUC6270_VSW;
+ m_horz_steps = 0;
+ m_horz_to_go = 0;
+ m_horz_state = HUC6270_HDS;
+ m_hsync = 0;
+ m_vsync = 0;
+ m_dma_enabled = 0;
+ m_byr_latched = 0;
+
+ memset(m_sat, 0, sizeof(m_sat));
+}
diff --git a/src/devices/video/huc6270.h b/src/devices/video/huc6270.h
new file mode 100644
index 00000000000..017fe5c9e70
--- /dev/null
+++ b/src/devices/video/huc6270.h
@@ -0,0 +1,141 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Hudson/NEC HuC6270 interface
+
+**********************************************************************/
+
+#ifndef __HUC6270_H_
+#define __HUC6270_H_
+
+#include "emu.h"
+
+#define MCFG_HUC6270_VRAM_SIZE(_size) \
+ huc6270_device::set_vram_size(*device, _size);
+
+#define MCFG_HUC6270_IRQ_CHANGED_CB(_devcb) \
+ devcb = &huc6270_device::set_irq_changed_callback(*device, DEVCB_##_devcb);
+
+class huc6270_device : public device_t
+{
+public:
+ // construction/destruction
+ huc6270_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_vram_size(device_t &device, UINT32 vram_size) { downcast<huc6270_device &>(device).m_vram_size = vram_size; }
+ template<class _Object> static devcb_base &set_irq_changed_callback(device_t &device, _Object object) { return downcast<huc6270_device &>(device).m_irq_changed_cb.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_READ16_MEMBER( next_pixel );
+ inline DECLARE_READ16_MEMBER( time_until_next_event )
+ {
+ return m_horz_to_go * 8 + m_horz_steps;
+ }
+
+ DECLARE_WRITE_LINE_MEMBER( vsync_changed );
+ DECLARE_WRITE_LINE_MEMBER( hsync_changed );
+
+ static const UINT16 HUC6270_SPRITE = 0x0100; // sprite colour information
+ static const UINT16 HUC6270_BACKGROUND = 0x0000; // background colour information
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ inline void fetch_bat_tile_row();
+ void add_sprite( int index, int x, int pattern, int line, int flip_x, int palette, int priority, int sat_lsb );
+ void select_sprites();
+ inline void handle_vblank();
+ inline void next_vert_state();
+ inline void next_horz_state();
+
+private:
+
+ enum huc6270_v_state {
+ HUC6270_VSW,
+ HUC6270_VDS,
+ HUC6270_VDW,
+ HUC6270_VCR
+ };
+
+ enum huc6270_h_state {
+ HUC6270_HDS,
+ HUC6270_HDW,
+ HUC6270_HDE,
+ HUC6270_HSW
+ };
+
+
+ /* Size of Video ram (mandatory) */
+ UINT32 m_vram_size;
+
+ /* Callback for when the irq line may have changed (mandatory) */
+ devcb_write_line m_irq_changed_cb;
+
+ UINT8 m_register_index;
+
+ /* HuC6270 registers */
+ UINT16 m_mawr;
+ UINT16 m_marr;
+ UINT16 m_vrr;
+ UINT16 m_vwr;
+ UINT16 m_cr;
+ UINT16 m_rcr;
+ UINT16 m_bxr;
+ UINT16 m_byr;
+ UINT16 m_mwr;
+ UINT16 m_hsr;
+ UINT16 m_hdr;
+ UINT16 m_vpr;
+ UINT16 m_vdw;
+ UINT16 m_vcr;
+ UINT16 m_dcr;
+ UINT16 m_sour;
+ UINT16 m_desr;
+ UINT16 m_lenr;
+ UINT16 m_dvssr;
+ UINT8 m_status;
+
+ /* To keep track of external hsync and vsync signals */
+ int m_hsync;
+ int m_vsync;
+
+ /* internal variables */
+ huc6270_v_state m_vert_state;
+ huc6270_h_state m_horz_state;
+ int m_vd_triggered;
+ int m_vert_to_go;
+ int m_horz_to_go;
+ int m_horz_steps;
+ int m_raster_count;
+ int m_dvssr_written;
+ int m_satb_countdown;
+ int m_dma_enabled;
+ UINT16 m_byr_latched;
+ UINT16 m_bxr_latched;
+ UINT16 m_bat_address;
+ UINT16 m_bat_address_mask;
+ UINT16 m_bat_row;
+ UINT16 m_bat_column;
+ UINT8 m_bat_tile_row[8];
+ /* Internal sprite attribute table. SATB DMA is used to transfer data
+ from VRAM to this internal table.
+ */
+ UINT16 m_sat[4*64];
+ int m_sprites_this_line;
+ int m_sprite_row_index;
+ UINT16 m_sprite_row[1024];
+ UINT16 *m_vram;
+ UINT16 m_vram_mask;
+
+ const static UINT8 vram_increments[4];
+};
+
+
+extern const device_type HUC6270;
+
+
+#endif
diff --git a/src/devices/video/huc6272.c b/src/devices/video/huc6272.c
new file mode 100644
index 00000000000..849ec9f09d6
--- /dev/null
+++ b/src/devices/video/huc6272.c
@@ -0,0 +1,249 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/***************************************************************************
+
+ Hudson/NEC HuC6272 "King" device
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/huc6272.h"
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type huc6272 = &device_creator<huc6272_device>;
+
+static ADDRESS_MAP_START( huc6272_vram, AS_0, 32, huc6272_device )
+ AM_RANGE(0x000000, 0x0fffff) AM_RAM
+ AM_RANGE(0x100000, 0x1fffff) AM_RAM
+ADDRESS_MAP_END
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *huc6272_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// read_dword - read a dword at the given address
+//-------------------------------------------------
+
+inline UINT32 huc6272_device::read_dword(offs_t address)
+{
+ return space().read_dword(address << 2);
+}
+
+
+//-------------------------------------------------
+// write_dword - write a dword at the given address
+//-------------------------------------------------
+
+inline void huc6272_device::write_dword(offs_t address, UINT32 data)
+{
+ space().write_dword(address << 2, data);
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// huc6272_device - constructor
+//-------------------------------------------------
+
+huc6272_device::huc6272_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, huc6272, "huc6272", tag, owner, clock, "huc6272", __FILE__),
+ device_memory_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 32, 32, 0, NULL, *ADDRESS_MAP_NAME(huc6272_vram))
+{
+}
+
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+void huc6272_device::device_validity_check(validity_checker &valid) const
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void huc6272_device::device_start()
+{
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void huc6272_device::device_reset()
+{
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+READ32_MEMBER( huc6272_device::read )
+{
+ UINT32 res = 0;
+
+ if((offset & 1) == 0)
+ {
+ /*
+ xxxx xxxx ---- ---- ---- ---- ---- ---- Sub Channel Buffer
+ ---- ---- x--- ---- ---- ---- ---- ---- SCSI RST flag
+ ---- ---- -x-- ---- ---- ---- ---- ---- SCSI BUSY flag
+ ---- ---- --x- ---- ---- ---- ---- ---- SCSI REQ flag
+ ---- ---- ---x ---- ---- ---- ---- ---- SCSI MSG flag
+ ---- ---- ---- x--- ---- ---- ---- ---- SCSI CD flag
+ ---- ---- ---- -x-- ---- ---- ---- ---- SCSI IO flag
+ ---- ---- ---- --x- ---- ---- ---- ---- SCSI SEL flag
+ ---- ---- ---- ---- -x-- ---- ---- ---- SCSI IRQ pending
+ ---- ---- ---- ---- --x- ---- ---- ---- DMA IRQ pending
+ ---- ---- ---- ---- ---x ---- ---- ---- CD Sub Channel IRQ pending
+ ---- ---- ---- ---- ---- x--- ---- ---- Raster IRQ pending
+ ---- ---- ---- ---- ---- -x-- ---- ---- ADPCM IRQ pending
+ ---- ---- ---- ---- ---- ---- -xxx xxxx register read-back
+ */
+ res = m_register & 0x7f;
+ res |= (0) << 16;
+ }
+ else
+ {
+ switch(m_register)
+ {
+ /*
+ x--- ---- ---- ---- ----
+ */
+ case 0x0c: // KRAM load address
+ res = (m_kram_addr_r & 0x3ffff) | ((m_kram_inc_r & 0x1ff) << 18) | ((m_kram_page_r & 1) << 31);
+ break;
+
+ case 0x0d: // KRAM write address
+ res = (m_kram_addr_w & 0x3ffff) | ((m_kram_inc_w & 0x1ff) << 18) | ((m_kram_page_w & 1) << 31);
+ break;
+
+ case 0x0e:
+ res = read_dword((m_kram_addr_r)|(m_kram_page_r<<18));
+ m_kram_addr_r += (m_kram_inc_r & 0x100) ? ((m_kram_inc_r & 0xff) - 0x100) : (m_kram_inc_r & 0xff);
+ break;
+
+ case 0x0f:
+ res = m_page_setting;
+ break;
+ //default: printf("%04x\n",m_register);
+ }
+ }
+
+ return res;
+}
+
+WRITE32_MEMBER( huc6272_device::write )
+{
+ if((offset & 1) == 0)
+ m_register = data & 0x7f;
+ else
+ {
+ switch(m_register)
+ {
+ case 0x09: // DMA addr
+ //printf("%08x DMA ADDR\n",data);
+ break;
+ case 0x0a: // DMA size
+ //printf("%08x DMA SIZE\n",data);
+ break;
+ case 0x0b: // DMA status
+ //printf("%08x DMA STATUS\n",data);
+ break;
+ /*
+ ---- ---- ---- ---- ----
+ */
+ case 0x0c: // KRAM load address
+ m_kram_addr_r = (data & 0x0003ffff);
+ m_kram_inc_r = (data & 0x07fc0000) >> 18;
+ m_kram_page_r = (data & 0x80000000) >> 31;
+ break;
+
+ case 0x0d: // KRAM write address
+ m_kram_addr_w = (data & 0x0003ffff);
+ m_kram_inc_w = (data & 0x07fc0000) >> 18;
+ m_kram_page_w = (data & 0x80000000) >> 31;
+ break;
+
+ case 0x0e: // KRAM write VRAM
+ write_dword((m_kram_addr_w)|(m_kram_page_w<<18),data); /* TODO: there are some 32-bits accesses during BIOS? */
+ m_kram_addr_w += (m_kram_inc_w & 0x100) ? ((m_kram_inc_w & 0xff) - 0x100) : (m_kram_inc_w & 0xff);
+ break;
+
+ /*
+ ---x ---- ---- ---- ADPCM page setting
+ ---- ---x ---- ---- RAINBOW page setting
+ ---- ---- ---x ---- BG page setting
+ ---- ---- ---- ---x SCSI page setting
+ */
+ case 0x0f:
+ m_page_setting = data;
+ break;
+
+ //
+ // xxxx ---- ---- ---- BG3 mode setting
+ // ---- xxxx ---- ---- BG2 mode setting
+ // ---- ---- xxxx ---- BG1 mode setting
+ // ---- ---- ---- xxxx BG0 mode setting
+ //
+ // 0001 - 4 color palette
+ // 0010 - 16 color palette
+ // 0011 - 256 color palette
+ // 0100 - 64k color
+ // 0101 - 16M color
+ // 1001 - 4 color palette block mode
+ // 1010 - 16 color palette block mode
+ // 1011 - 256 color palette block mode
+ // others - unused/invalid
+ case 0x10:
+ m_bgmode[0] = data & 0x0f;
+ m_bgmode[1] = ( data >> 4 ) & 0x0f;
+ m_bgmode[2] = ( data >> 8 ) & 0x0f;
+ m_bgmode[3] = ( data >> 12 ) & 0x0f;
+ break;
+
+ case 0x13:
+ m_micro_prg.addr = data & 0xf;
+ break;
+
+ case 0x14:
+ m_micro_prg.data[m_micro_prg.addr] = data & 0xffff;
+ m_micro_prg.addr++;
+ m_micro_prg.addr &= 0xf;
+ break;
+
+ case 0x15:
+ m_micro_prg.ctrl = data & 1;
+ break;
+
+ //default: printf("%04x %04x %08x\n",m_register,data,mem_mask);
+ }
+ }
+}
diff --git a/src/devices/video/huc6272.h b/src/devices/video/huc6272.h
new file mode 100644
index 00000000000..140d086efb0
--- /dev/null
+++ b/src/devices/video/huc6272.h
@@ -0,0 +1,80 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/***************************************************************************
+
+Template for skeleton device
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __huc6272DEV_H__
+#define __huc6272DEV_H__
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_HUC6272_ADD(_tag,_freq) \
+ MCFG_DEVICE_ADD(_tag, huc6272, _freq)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> huc6272_device
+
+class huc6272_device : public device_t,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ huc6272_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // I/O operations
+ DECLARE_WRITE32_MEMBER( write );
+ DECLARE_READ32_MEMBER( read );
+
+
+protected:
+ // device-level overrides
+ virtual void device_validity_check(validity_checker &valid) const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+private:
+ inline UINT32 read_dword(offs_t address);
+ inline void write_dword(offs_t address, UINT32 data);
+ UINT8 m_register;
+ UINT32 m_kram_addr_r, m_kram_addr_w;
+ UINT16 m_kram_inc_r,m_kram_inc_w;
+ UINT8 m_kram_page_r,m_kram_page_w;
+ UINT32 m_page_setting;
+ UINT8 m_bgmode[4];
+
+ struct{
+ UINT8 addr;
+ UINT8 ctrl;
+ UINT16 data[16];
+ }m_micro_prg;
+
+ const address_space_config m_space_config;
+};
+
+
+// device type definition
+extern const device_type huc6272;
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+
+
+#endif
diff --git a/src/devices/video/i8244.c b/src/devices/video/i8244.c
new file mode 100644
index 00000000000..a074b676e64
--- /dev/null
+++ b/src/devices/video/i8244.c
@@ -0,0 +1,799 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/***************************************************************************
+
+ i8244.c
+
+ Intel 8244 (NTSC)/8245 (PAL) Graphics and sound chip
+
+
+***************************************************************************/
+
+#include "emu.h"
+#include "i8244.h"
+
+
+// device type definition
+const device_type I8244 = &device_creator<i8244_device>;
+const device_type I8245 = &device_creator<i8245_device>;
+
+
+// Kevtris verified that the data below matches a dump
+// taken from a real chip.
+static const UINT8 c_shape[0x40 * 8] =
+{
+ 0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, // 0
+ 0x18,0x38,0x18,0x18,0x18,0x18,0x3C,0x00,
+ 0x3C,0x66,0x0C,0x18,0x30,0x60,0x7E,0x00,
+ 0x7C,0xC6,0x06,0x3C,0x06,0xC6,0x7C,0x00,
+ 0xCC,0xCC,0xCC,0xFE,0x0C,0x0C,0x0C,0x00,
+ 0xFE,0xC0,0xC0,0x7C,0x06,0xC6,0x7C,0x00,
+ 0x7C,0xC6,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,
+ 0xFE,0x06,0x0C,0x18,0x30,0x60,0xC0,0x00,
+ 0x7C,0xC6,0xC6,0x7C,0xC6,0xC6,0x7C,0x00,
+ 0x7C,0xC6,0xC6,0x7E,0x06,0xC6,0x7C,0x00,
+ 0x00,0x18,0x18,0x00,0x18,0x18,0x00,0x00,
+ 0x18,0x7E,0x58,0x7E,0x1A,0x7E,0x18,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x3C,0x66,0x0C,0x18,0x18,0x00,0x18,0x00,
+ 0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFE,0x00,
+ 0xFC,0xC6,0xC6,0xFC,0xC0,0xC0,0xC0,0x00,
+ 0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,
+ 0xC6,0xC6,0xC6,0xD6,0xFE,0xEE,0xC6,0x00,
+ 0xFE,0xC0,0xC0,0xF8,0xC0,0xC0,0xFE,0x00,
+ 0xFC,0xC6,0xC6,0xFC,0xD8,0xCC,0xC6,0x00,
+ 0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,
+ 0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
+ 0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,
+ 0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
+ 0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x76,0x00,
+ 0x7C,0xC6,0xC0,0x7C,0x06,0xC6,0x7C,0x00,
+ 0xFC,0xC6,0xC6,0xC6,0xC6,0xC6,0xFC,0x00,
+ 0xFE,0xC0,0xC0,0xF8,0xC0,0xC0,0xC0,0x00,
+ 0x7C,0xC6,0xC0,0xC0,0xCE,0xC6,0x7E,0x00,
+ 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,
+ 0x06,0x06,0x06,0x06,0x06,0xC6,0x7C,0x00,
+ 0xC6,0xCC,0xD8,0xF0,0xD8,0xCC,0xC6,0x00,
+ 0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00,
+ 0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00,
+ 0xC6,0xC6,0x6C,0x38,0x6C,0xC6,0xC6,0x00,
+ 0x7C,0xC6,0xC0,0xC0,0xC0,0xC6,0x7C,0x00,
+ 0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x00,
+ 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,
+ 0xC6,0xEE,0xFE,0xD6,0xC6,0xC6,0xC6,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,
+ 0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,
+ 0x00,0x66,0x3C,0x18,0x3C,0x66,0x00,0x00,
+ 0x00,0x18,0x00,0x7E,0x00,0x18,0x00,0x00,
+ 0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00,
+ 0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00,
+ 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,
+ 0x03,0x06,0x0C,0x18,0x30,0x60,0xC0,0x00,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,
+ 0xCE,0xDB,0xDB,0xDB,0xDB,0xDB,0xCE,0x00,
+ 0x00,0x00,0x3C,0x7E,0x7E,0x7E,0x3C,0x00,
+ 0x1C,0x1C,0x18,0x1E,0x18,0x18,0x1C,0x00,
+ 0x1C,0x1C,0x18,0x1E,0x18,0x34,0x26,0x00,
+ 0x38,0x38,0x18,0x78,0x18,0x2C,0x64,0x00,
+ 0x38,0x38,0x18,0x78,0x18,0x18,0x38,0x00,
+ 0x00,0x18,0x0C,0xFE,0x0C,0x18,0x00,0x00,
+ 0x18,0x3C,0x7E,0xFF,0xFF,0x18,0x18,0x00,
+ 0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF,0x00,
+ 0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF,0x00,
+ 0x38,0x38,0x12,0xFE,0xB8,0x28,0x6C,0x00,
+ 0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x00,
+ 0x00,0x00,0x0C,0x08,0x08,0x7F,0x3E,0x00,
+ 0x00,0x03,0x63,0xFF,0xFF,0x18,0x08,0x00,
+ 0x00,0x00,0x00,0x10,0x38,0xFF,0x7E,0x00
+};
+
+
+// Background and grid information is stored in RGB format
+// while the character and sprite colors are stored in BGR
+// format.
+static const UINT8 bgr2rgb[8] =
+{
+ 0x00, 0x04, 0x02, 0x06, 0x01, 0x05, 0x03, 0x07
+};
+
+
+//-------------------------------------------------
+// i8244_device - constructor
+//-------------------------------------------------
+
+i8244_device::i8244_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, I8244, "I8244", tag, owner, clock, "i8244", __FILE__)
+ , device_sound_interface(mconfig, *this)
+ , device_video_interface(mconfig, *this)
+ , m_irq_func(*this)
+ , m_postprocess_func(*this)
+ , m_start_vpos(START_Y)
+ , m_start_vblank(START_Y + SCREEN_HEIGHT)
+ , m_screen_lines(LINES)
+{
+}
+
+
+i8244_device::i8244_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, int lines, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source)
+ , device_sound_interface(mconfig, *this)
+ , device_video_interface(mconfig, *this)
+ , m_irq_func(*this)
+ , m_postprocess_func(*this)
+ , m_start_vpos(START_Y)
+ , m_start_vblank(START_Y + SCREEN_HEIGHT)
+ , m_screen_lines(lines)
+{
+}
+
+
+i8245_device::i8245_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : i8244_device(mconfig, I8245, "I8245", tag, owner, clock, i8245_device::LINES, "i8245", __FILE__)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void i8244_device::device_start()
+{
+ // Let the screen create our temporary bitmap with the screen's dimensions
+ m_screen->register_screen_bitmap(m_tmp_bitmap);
+
+ m_line_timer = timer_alloc(TIMER_LINE);
+ m_line_timer->adjust( m_screen->time_until_pos(1, START_ACTIVE_SCAN ), 0, m_screen->scan_period() );
+
+ m_hblank_timer = timer_alloc(TIMER_HBLANK);
+ m_hblank_timer->adjust( m_screen->time_until_pos(1, END_ACTIVE_SCAN + 18 ), 0, m_screen->scan_period() );
+
+ m_irq_func.resolve_safe();
+ m_postprocess_func.resolve_safe();
+
+ // allocate a stream
+ m_stream = stream_alloc( 0, 1, clock()/(LINE_CLOCKS*4) );
+
+ // register our state
+ save_pointer(NAME(m_vdc.reg), 0x100);
+ save_item(NAME(m_sh_count));
+ save_item(NAME(m_x_beam_pos));
+ save_item(NAME(m_y_beam_pos));
+ save_item(NAME(m_control_status));
+ save_item(NAME(m_collision_status));
+ save_item(NAME(m_iff));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void i8244_device::device_reset()
+{
+ memset(m_vdc.reg, 0, 0x100);
+
+ m_sh_count = 0;
+ m_x_beam_pos = 0;
+ m_y_beam_pos = 0;
+ m_control_status = 0;
+ m_collision_status = 0;
+ m_iff = 0;
+}
+
+
+PALETTE_INIT_MEMBER(i8244_device, i8244)
+{
+ static const UINT8 i8244_colors[3*16] =
+ {
+ 0x00, 0x00, 0x00, // i r g b
+ 0x00, 0x00, 0xAA, // i r g B
+ 0x00, 0xAA, 0x00, // i r G b
+ 0x00, 0xAA, 0xAA, // i r G B
+ 0xAA, 0x00, 0x00, // i R g b
+ 0xAA, 0x00, 0xAA, // i R g B
+ 0xAA, 0xAA, 0x00, // i R G b
+ 0xAA, 0xAA, 0xAA, // i R G B
+ 0x55, 0x55, 0x55, // I r g b
+ 0x55, 0x55, 0xFF, // I r g B
+ 0x55, 0xFF, 0x55, // I r G b
+ 0x55, 0xFF, 0xFF, // I r G B
+ 0xFF, 0x55, 0x55, // I R g b
+ 0xFF, 0x55, 0xFF, // I R g B
+ 0xFF, 0xFF, 0x55, // I R G b
+ 0xFF, 0xFF, 0xFF, // I R G B
+ };
+
+ for ( int i = 0; i < 16; i++ )
+ {
+ palette.set_pen_color( i, i8244_colors[i*3], i8244_colors[i*3+1], i8244_colors[i*3+2] );
+ }
+}
+
+
+void i8244_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int vpos = m_screen->vpos();
+
+ switch ( id )
+ {
+ case TIMER_LINE:
+ // handle i824x line timer
+ render_scanline(vpos);
+ break;
+
+ case TIMER_HBLANK:
+ // handle i824x HBlank timer
+ if ( vpos < m_start_vpos - 1 )
+ {
+ return;
+ }
+
+ if ( vpos < m_start_vblank - 1 )
+ {
+ m_control_status |= 0x01;
+ }
+ break;
+ }
+}
+
+
+int i8244_device::get_y_beam()
+{
+ int y = m_screen->vpos() - m_start_vpos;
+
+ // The Y register becomes 0 only when the VBlank signal is turned off!
+ if ( y < 0 || ( y == 0 && m_screen->hpos() < 366+42 ) )
+ {
+ y += m_screen_lines;
+ }
+
+ return y;
+}
+
+
+int i8244_device::get_x_beam()
+{
+ int x = m_screen->hpos() - START_ACTIVE_SCAN;
+
+ if ( x < 0 )
+ {
+ x += LINE_CLOCKS;
+ }
+
+ return x >> 1;
+}
+
+
+offs_t i8244_device::fix_register_mirrors( offs_t offset )
+{
+ // registers $40,$41 are mirrored at $44,$45, $48,$49, and $4C,$4D
+ if ( ( offset & 0xF2 ) == 0x40 )
+ {
+ offset &= ~0x0C;
+ }
+
+ // registers $A0-$AF are mirrored at $B0-$BF
+ if ( ( offset & 0xF0 ) == 0xB0 )
+ {
+ offset &= ~0x10;
+ }
+
+ return offset;
+}
+
+
+READ8_MEMBER(i8244_device::read)
+{
+ UINT8 data = 0;
+
+ offset = fix_register_mirrors( offset );
+
+ switch (offset)
+ {
+ case 0xa1:
+ data = m_control_status & 0xFE;
+ m_iff = 0;
+ m_irq_func(CLEAR_LINE);
+ m_control_status &= ~0x08;
+ if ( hblank() )
+ {
+ data |= 1;
+ }
+ break;
+
+ case 0xa2:
+ data = m_collision_status;
+ m_collision_status = 0;
+ break;
+
+ case 0xa4:
+ if (m_vdc.s.control & VDC_CONTROL_REG_STROBE_XY)
+ {
+ m_y_beam_pos = get_y_beam();
+ }
+ data = m_y_beam_pos;
+ break;
+
+
+ case 0xa5:
+ if ((m_vdc.s.control & VDC_CONTROL_REG_STROBE_XY))
+ {
+ m_x_beam_pos = get_x_beam();
+ }
+ data = m_x_beam_pos;
+ break;
+
+ default:
+ data = m_vdc.reg[offset];
+ break;
+ }
+
+ return data;
+}
+
+
+WRITE8_MEMBER(i8244_device::write)
+{
+ offset = fix_register_mirrors( offset );
+
+ /* Update the sound */
+ if( offset >= 0xa7 && offset <= 0xaa )
+ {
+ m_stream->update();
+ }
+
+ if (offset == 0xa0)
+ {
+ if ( ( m_vdc.s.control & VDC_CONTROL_REG_STROBE_XY )
+ && !(data & VDC_CONTROL_REG_STROBE_XY))
+ {
+ /* Toggling strobe bit, tuck away values */
+ m_x_beam_pos = get_x_beam();
+ m_y_beam_pos = get_y_beam();
+ }
+ }
+
+ m_vdc.reg[offset] = data;
+}
+
+
+READ_LINE_MEMBER(i8244_device::vblank)
+{
+ if ( m_screen->vpos() > m_start_vpos && m_screen->vpos() < m_start_vblank )
+ {
+ return 0;
+ }
+ return 1;
+}
+
+
+READ_LINE_MEMBER(i8244_device::hblank)
+{
+ int hpos = m_screen->hpos();
+ int vpos = m_screen->vpos();
+
+ if ( hpos >= START_ACTIVE_SCAN && hpos < END_ACTIVE_SCAN )
+ {
+ return 0;
+ }
+
+ // Before active area?
+ if ( vpos < m_start_vpos - 1 )
+ {
+ return 0;
+ }
+
+ // During active area?
+ if ( vpos < m_start_vblank - 1 )
+ {
+ return 1;
+ }
+
+ // After active area
+ return 0;
+}
+
+
+void i8244_device::render_scanline(int vpos)
+{
+ // Some local constants for this method
+ //static const UINT8 COLLISION_SPRITE_0 = 0x01;
+ //static const UINT8 COLLISION_SPRITE_1 = 0x02;
+ //static const UINT8 COLLISION_SPRITE_2 = 0x04;
+ //static const UINT8 COLLISION_SPRITE_3 = 0x08;
+ static const UINT8 COLLISION_VERTICAL_GRID = 0x10;
+ static const UINT8 COLLISION_HORIZ_GRID_DOTS = 0x20;
+ //static const UINT8 COLLISION_EXTERNAL_UNUSED = 0x40;
+ static const UINT8 COLLISION_CHARACTERS = 0x80;
+
+ UINT8 collision_map[160];
+
+ if ( vpos == m_start_vpos )
+ {
+ m_control_status &= ~0x08;
+ }
+
+ if ( m_start_vpos < vpos && vpos < m_start_vblank )
+ {
+ rectangle rect;
+ int scanline = vpos - m_start_vpos;
+
+ m_control_status &= ~ 0x01;
+
+ /* Draw a line */
+ rect.set(START_ACTIVE_SCAN, END_ACTIVE_SCAN - 1, vpos, vpos);
+ m_tmp_bitmap.fill( (m_vdc.s.color >> 3) & 0x7, rect );
+
+ /* Clear collision map */
+ memset( collision_map, 0, sizeof( collision_map ) );
+
+ /* Display grid if enabled */
+ if ( m_vdc.s.control & 0x08 )
+ {
+ UINT16 color = ( m_vdc.s.color & 7 ) | ( ( m_vdc.s.color >> 3 ) & 0x08 );
+ int x_grid_offset = 8;
+ int y_grid_offset = 24;
+ int width = 16;
+ int height = 24;
+ int w = ( m_vdc.s.control & 0x80 ) ? width : 2;
+
+ /* Draw horizontal part of grid */
+ for ( int j = 1, y = 0; y < 9; y++, j <<= 1 )
+ {
+ if ( y_grid_offset + y * height <= scanline && scanline < y_grid_offset + y * height + 3 )
+ {
+ for ( int i = 0; i < 9; i++ )
+ {
+ if ( ( m_vdc.s.hgrid[0][i] & j ) || ( m_vdc.s.hgrid[1][i] & ( j >> 8 ) ) )
+ {
+ for ( int k = 0; k < width + 2; k++ )
+ {
+ int px = x_grid_offset + i * width + k;
+
+ if ( px < 160 )
+ {
+ collision_map[ px ] |= COLLISION_HORIZ_GRID_DOTS;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px ) = color;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px + 1 ) = color;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw vertical part of grid */
+ for( int j = 1, y = 0; y < 8; y++, j <<= 1 )
+ {
+ if ( y_grid_offset + y * height <= scanline && scanline < y_grid_offset + ( y + 1 ) * height )
+ {
+ for ( int i = 0; i < 10; i++ )
+ {
+ if ( m_vdc.s.vgrid[i] & j )
+ {
+ for ( int k = 0; k < w; k++ )
+ {
+ int px = x_grid_offset + i * width + k;
+
+ if ( px < 160 )
+ {
+ /* Check if we collide with an already drawn source object */
+ if ( collision_map[ px ] & m_vdc.s.collision )
+ {
+ m_collision_status |= COLLISION_VERTICAL_GRID;
+ }
+ /* Check if an already drawn object would collide with us */
+ if ( COLLISION_VERTICAL_GRID & m_vdc.s.collision && collision_map[ px ] )
+ {
+ m_collision_status |= collision_map[ px ];
+ }
+ collision_map[ px ] |= COLLISION_VERTICAL_GRID;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px ) = color;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px + 1 ) = color;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Display objects if enabled */
+ if ( m_vdc.s.control & 0x20 )
+ {
+ /* Regular foreground objects */
+ for ( int i = 0; i < ARRAY_LENGTH( m_vdc.s.foreground ); i++ )
+ {
+ int y = m_vdc.s.foreground[i].y & 0xFE;
+ int height = 8 - ( ( ( y >> 1 ) + m_vdc.s.foreground[i].ptr ) & 7 );
+
+ if ( y >= 0x0E && y <= scanline && scanline < y + height * 2 )
+ {
+ UINT16 color = 8 + bgr2rgb[ ( ( m_vdc.s.foreground[i].color >> 1 ) & 0x07 ) ];
+ int offset = ( m_vdc.s.foreground[i].ptr | ( ( m_vdc.s.foreground[i].color & 0x01 ) << 8 ) ) + ( y >> 1 ) + ( ( scanline - y ) >> 1 );
+ UINT8 chr = c_shape[ offset & 0x1FF ];
+ int x = m_vdc.s.foreground[i].x;
+
+ for ( UINT8 m = 0x80; m > 0; m >>= 1, x++ )
+ {
+ if ( chr & m )
+ {
+ if ( x >= 0 && x < 160 )
+ {
+ /* Check if we collide with an already drawn source object */
+ if ( collision_map[ x ] & m_vdc.s.collision )
+ {
+ m_collision_status |= COLLISION_CHARACTERS;
+ }
+ /* Check if an already drawn object would collide with us */
+ if ( COLLISION_CHARACTERS & m_vdc.s.collision && collision_map[ x ] )
+ {
+ m_collision_status |= collision_map[ x ];
+ }
+ collision_map[ x ] |= COLLISION_CHARACTERS;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * x ) = color;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * x + 1 ) = color;
+ }
+ }
+ }
+ }
+ }
+
+ /* Quad objects */
+ for ( int i = 0; i < ARRAY_LENGTH( m_vdc.s.quad ); i++ )
+ {
+ int y = m_vdc.s.quad[i].single[0].y;
+ int height = 8;
+
+ if ( y <= scanline && scanline < y + height * 2 )
+ {
+ int x = m_vdc.s.quad[i].single[0].x;
+
+ // Charaecter height is always determined by the height of the 4th character
+ int char_height = 8 - ( ( ( y >> 1 ) + m_vdc.s.quad[i].single[3].ptr ) & 7 );
+
+ for ( int j = 0; j < ARRAY_LENGTH( m_vdc.s.quad[0].single ); j++, x += 8 )
+ {
+ if ( y <= scanline && scanline < y + char_height * 2 )
+ {
+ UINT16 color = 8 + bgr2rgb[ ( ( m_vdc.s.quad[i].single[j].color >> 1 ) & 0x07 ) ];
+ int offset = ( m_vdc.s.quad[i].single[j].ptr | ( ( m_vdc.s.quad[i].single[j].color & 0x01 ) << 8 ) ) + ( y >> 1 ) + ( ( scanline - y ) >> 1 );
+ UINT8 chr = c_shape[ offset & 0x1FF ];
+
+ for ( UINT8 m = 0x80; m > 0; m >>= 1, x++ )
+ {
+ if ( chr & m )
+ {
+ if ( x >= 0 && x < 160 )
+ {
+ /* Check if we collide with an already drawn source object */
+ if ( collision_map[ x ] & m_vdc.s.collision )
+ {
+ m_collision_status |= COLLISION_CHARACTERS;
+ }
+ /* Check if an already drawn object would collide with us */
+ if ( COLLISION_CHARACTERS & m_vdc.s.collision && collision_map[ x ] )
+ {
+ m_collision_status |= collision_map[ x ];
+ }
+ collision_map[ x ] |= COLLISION_CHARACTERS;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * x ) = color;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * x + 1 ) = color;
+ }
+ }
+ }
+ }
+ else
+ {
+ x += 8;
+ }
+ }
+ }
+ }
+
+ /* Sprites */
+ for ( int i = 0; i < ARRAY_LENGTH( m_vdc.s.sprites ); i++ )
+ {
+ int y = m_vdc.s.sprites[i].y;
+ int height = 8;
+
+ if ( m_vdc.s.sprites[i].color & 4 )
+ {
+ /* Zoomed sprite */
+ if ( y <= scanline && scanline < y + height * 4 )
+ {
+ UINT16 color = 8 + bgr2rgb[ ( ( m_vdc.s.sprites[i].color >> 3 ) & 0x07 ) ];
+ UINT8 chr = m_vdc.s.shape[i][ ( ( scanline - y ) >> 2 ) ];
+ int x = m_vdc.s.sprites[i].x;
+ int x_shift = 0;
+
+ switch ( m_vdc.s.sprites[i].color & 0x03 )
+ {
+ case 1: // Xg attribute set
+ x_shift = 2;
+ break;
+ case 2: // S attribute set
+ x_shift = ( ( ( scanline - y ) >> 1 ) & 0x01 ) ^ 0x01;
+ break;
+ case 3: // Xg and S attributes set
+ x_shift = ( ( scanline - y ) >> 1 ) & 0x01;
+ break;
+ }
+ x_shift <<= 1;
+
+ for ( UINT8 m = 0x01; m > 0; m <<= 1, x += 2 )
+ {
+ if ( chr & m )
+ {
+ if ( x >= 0 && x < 160 )
+ {
+ /* Check if we collide with an already drawn source object */
+ if ( collision_map[ x ] & m_vdc.s.collision )
+ {
+ m_collision_status |= ( 1 << i );
+ }
+ /* Check if an already drawn object would collide with us */
+ if ( ( 1 << i ) & m_vdc.s.collision && collision_map[ x ] )
+ {
+ m_collision_status |= collision_map[ x ];
+ }
+ collision_map[ x ] |= ( 1 << i );
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x ) = color;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x + 1 ) = color;
+ }
+ if ( x >= -1 && x < 159 )
+ {
+ if ( x >= 0 )
+ {
+ /* Check if we collide with an already drawn source object */
+ if ( collision_map[ x ] & m_vdc.s.collision )
+ {
+ m_collision_status |= ( 1 << i );
+ }
+ /* Check if an already drawn object would collide with us */
+ if ( ( 1 << i ) & m_vdc.s.collision && collision_map[ x ] )
+ {
+ m_collision_status |= collision_map[ x ];
+ }
+ collision_map[ x ] |= ( 1 << i );
+ }
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x + 2 ) = color;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x + 3 ) = color;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Regular sprite */
+ if ( y <= scanline && scanline < y + height * 2 )
+ {
+ UINT16 color = 8 + bgr2rgb[ ( ( m_vdc.s.sprites[i].color >> 3 ) & 0x07 ) ];
+ UINT8 chr = m_vdc.s.shape[i][ ( ( scanline - y ) >> 1 ) ];
+ int x = m_vdc.s.sprites[i].x;
+ int x_shift = 0;
+
+ switch ( m_vdc.s.sprites[i].color & 0x03 )
+ {
+ case 1: // Xg attribute set
+ x_shift = 1;
+ break;
+ case 2: // S attribute set
+ x_shift = ( ( ( scanline - y ) >> 1 ) & 0x01 ) ^ 0x01;
+ break;
+ case 3: // Xg and S attributes set
+ x_shift = ( ( scanline - y ) >> 1 ) & 0x01;
+ break;
+ }
+
+ for ( UINT8 m = 0x01; m > 0; m <<= 1, x++ )
+ {
+ if ( chr & m )
+ {
+ if ( x >= 0 && x < 160 )
+ {
+ /* Check if we collide with an already drawn source object */
+ if ( collision_map[ x ] & m_vdc.s.collision )
+ {
+ m_collision_status |= ( 1 << i );
+ }
+ /* Check if an already drawn object would collide with us */
+ if ( ( 1 << i ) & m_vdc.s.collision && collision_map[ x ] )
+ {
+ m_collision_status |= collision_map[ x ];
+ }
+ collision_map[ x ] |= ( 1 << i );
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x ) = color;
+ m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x + 1 ) = color;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Allow driver to do additional processing
+ m_postprocess_func( vpos );
+
+ /* Check for start of VBlank */
+ if ( vpos == m_start_vblank )
+ {
+ m_control_status |= 0x08;
+ if ( ! m_iff )
+ {
+ m_iff = 1;
+ m_irq_func(ASSERT_LINE);
+ }
+ }
+}
+
+
+void i8244_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ UINT32 old_signal, signal;
+ int ii;
+ int period;
+ stream_sample_t *buffer = outputs[0];
+
+ /* Generate the signal */
+ old_signal = signal = m_vdc.s.shift3 | (m_vdc.s.shift2 << 8) | (m_vdc.s.shift1 << 16);
+
+ if( m_vdc.s.sound & 0x80 ) /* Sound is enabled */
+ {
+ for( ii = 0; ii < samples; ii++, buffer++ )
+ {
+ *buffer = 0;
+ *buffer = signal & 0x1;
+ period = (m_vdc.s.sound & 0x20) ? 1 : 4;
+ if( ++m_sh_count >= period )
+ {
+ m_sh_count = 0;
+ signal >>= 1;
+ /* Loop sound */
+ signal |= *buffer << 23;
+ /* Check if noise should be applied */
+ if ( m_vdc.s.sound & 0x10 )
+ {
+ /* Noise tap is on bits 0 and 5 and fed back to bits 15 (and 23!) */
+ UINT32 new_bit = ( ( old_signal ) ^ ( old_signal >> 5 ) ) & 0x01;
+ signal = ( old_signal & 0xFF0000 ) | ( ( old_signal & 0xFFFF ) >> 1 ) | ( new_bit << 15 ) | ( new_bit << 23 );
+ }
+ m_vdc.s.shift3 = signal & 0xFF;
+ m_vdc.s.shift2 = ( signal >> 8 ) & 0xFF;
+ m_vdc.s.shift1 = ( signal >> 16 ) & 0xFF;
+ old_signal = signal;
+ }
+
+ /* Throw an interrupt if enabled */
+ if( m_vdc.s.control & 0x4 )
+ {
+ // This feature does not seem to be finished/enabled in hardware!
+ }
+
+ /* Adjust volume */
+ *buffer *= m_vdc.s.sound & 0xf;
+ /* Pump the volume up */
+ *buffer <<= 10;
+ }
+ }
+ else
+ {
+ /* Sound disabled, so clear the buffer */
+ for( ii = 0; ii < samples; ii++, buffer++ )
+ {
+ *buffer = 0;
+ }
+ }
+}
+
+
+UINT32 i8244_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ copybitmap( bitmap, m_tmp_bitmap, 0, 0, 0, 0, cliprect );
+
+ return 0;
+}
diff --git a/src/devices/video/i8244.h b/src/devices/video/i8244.h
new file mode 100644
index 00000000000..ea518051a9f
--- /dev/null
+++ b/src/devices/video/i8244.h
@@ -0,0 +1,169 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/***************************************************************************
+
+ i8244.h
+
+ Intel 8244 (NTSC)/8245 (PAL) Graphics and sound chip
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __I8244_H__
+#define __I8244_H__
+
+#include "emu.h"
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_I8244_ADD(_tag, _clock, _screen_tag, _irq_cb, _postprocess_cb) \
+ MCFG_DEVICE_ADD(_tag, I8244, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_I8244_IRQ_CB(_irq_cb) \
+ MCFG_I8244_POSTPROCESS_CB(_postprocess_cb)
+#define MCFG_I8244_IRQ_CB(_devcb) \
+ devcb = &i8244_device::set_irq_cb(*device, DEVCB_##_devcb);
+#define MCFG_I8244_POSTPROCESS_CB(_devcb) \
+ devcb = &i8244_device::set_postprocess_cb(*device, DEVCB_##_devcb);
+#define MCFG_I8245_ADD(_tag, _clock, _screen_tag, _irq_cb, _postprocess_cb) \
+ MCFG_DEVICE_ADD(_tag, I8245, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_I8244_IRQ_CB(_irq_cb) \
+ MCFG_I8244_POSTPROCESS_CB(_postprocess_cb )
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+union vdc_t {
+ UINT8 reg[0x100];
+ struct {
+ struct {
+ UINT8 y,x,color,res;
+ } sprites[4];
+ struct {
+ UINT8 y,x,ptr,color;
+ } foreground[12];
+ struct {
+ struct {
+ UINT8 y,x,ptr,color;
+ } single[4];
+ } quad[4];
+ UINT8 shape[4][8];
+ UINT8 control;
+ UINT8 status;
+ UINT8 collision;
+ UINT8 color;
+ UINT8 y;
+ UINT8 x;
+ UINT8 res;
+ UINT8 shift1;
+ UINT8 shift2;
+ UINT8 shift3;
+ UINT8 sound;
+ UINT8 res2[5+0x10];
+ UINT8 hgrid[2][0x10];
+ UINT8 vgrid[0x10];
+ } s;
+};
+
+
+// ======================> i8244_device
+
+class i8244_device : public device_t
+ , public device_sound_interface
+ , public device_video_interface
+{
+public:
+ // construction/destruction
+ i8244_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ i8244_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, int lines, const char *shortname, const char *source);
+
+ // static configuration helpers
+ static void set_screen_tag(device_t &device, const char *screen_tag) { downcast<i8244_device &>(device).m_screen_tag = screen_tag; }
+ template<class _Object> static devcb_base &set_irq_cb(device_t &device, _Object object) { return downcast<i8244_device &>(device).m_irq_func.set_callback(object); }
+ template<class _Object> static devcb_base &set_postprocess_cb(device_t &device, _Object object) { return downcast<i8244_device &>(device).m_postprocess_func.set_callback(object); }
+
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+ DECLARE_READ_LINE_MEMBER(vblank);
+ DECLARE_READ_LINE_MEMBER(hblank);
+ DECLARE_PALETTE_INIT(i8244);
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+ inline bitmap_ind16 *get_bitmap() { return &m_tmp_bitmap; }
+
+ // Global constants
+ static const int START_ACTIVE_SCAN = 42;
+ static const int BORDER_SIZE = 10;
+ static const int END_ACTIVE_SCAN = 42 + 10 + 320 + 10;
+ static const int START_Y = 1;
+ static const int SCREEN_HEIGHT = 243;
+ static const int LINE_CLOCKS = 455;
+ static const int LINES = 262;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ void render_scanline(int vpos);
+ int get_y_beam();
+ int get_x_beam();
+ offs_t fix_register_mirrors( offs_t offset );
+
+ // Local constants
+ static const UINT8 VDC_CONTROL_REG_STROBE_XY = 0x02;
+
+ /* timers */
+ static const device_timer_id TIMER_LINE = 0;
+ static const device_timer_id TIMER_HBLANK = 1;
+
+ // callbacks
+ devcb_write_line m_irq_func;
+ devcb_write16 m_postprocess_func;
+
+ bitmap_ind16 m_tmp_bitmap;
+ emu_timer *m_line_timer;
+ emu_timer *m_hblank_timer;
+ sound_stream *m_stream;
+
+ int m_start_vpos;
+ int m_start_vblank;
+ int m_screen_lines;
+
+ vdc_t m_vdc;
+ UINT16 m_sh_count;
+ UINT8 m_x_beam_pos;
+ UINT8 m_y_beam_pos;
+ UINT8 m_control_status;
+ UINT8 m_collision_status;
+ int m_iff;
+};
+
+
+class i8245_device : public i8244_device
+{
+public:
+ // construction/destruction
+ i8245_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static const int LINES = 312;
+};
+
+
+// device type definition
+extern const device_type I8244;
+extern const device_type I8245;
+
+
+#endif /* __I8244_H__ */
diff --git a/src/devices/video/i82730.c b/src/devices/video/i82730.c
new file mode 100644
index 00000000000..d90a79d04ee
--- /dev/null
+++ b/src/devices/video/i82730.c
@@ -0,0 +1,527 @@
+// license:GPL-2.0+
+// copyright-holders:Dirk Best
+/***************************************************************************
+
+ Intel 82730
+
+ Text Coprocessor
+
+***************************************************************************/
+
+#include "i82730.h"
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+#define VERBOSE 1
+#define VERBOSE_COMMANDS 1
+#define VERBOSE_DATASTREAM 0
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type I82730 = &device_creator<i82730_device>;
+
+const char *i82730_device::m_command_names[] =
+{
+ /* 00 */ "NOP",
+ /* 01 */ "START DISPLAY",
+ /* 02 */ "START VIRTUAL DISPLAY",
+ /* 03 */ "STOP DISPLAY",
+ /* 04 */ "MODE SET",
+ /* 05 */ "LOAD CBP",
+ /* 06 */ "LOAD INTMASK",
+ /* 07 */ "LPEN ENABLE",
+ /* 08 */ "READ STATUS",
+ /* 09 */ "LD CUR POS",
+ /* 0a */ "SELF TEST",
+ /* 0b */ "TEST ROW BUFFER"
+};
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// i82730_device - constructor
+//-------------------------------------------------
+
+i82730_device::i82730_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, I82730, "I82730", tag, owner, clock, "i82730", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_sint_handler(*this),
+ m_cpu_tag(NULL), m_program(NULL),
+ m_row_timer(NULL),
+ m_initialized(false), m_mode_set(false),
+ m_ca(0),
+ m_sysbus(0x00), m_ibp(0x0000), m_cbp(0x0000), m_intmask(0xffff), m_status(0x0000),
+ m_list_switch(0), m_auto_line_feed(0), m_max_dma_count(0),
+ m_lptr(0), m_sptr(0),
+ m_dma_burst_space(0), m_dma_burst_length(0),
+ m_hfldstrt(0), m_margin(0), m_lpr(0), m_field_attribute_mask(0), m_vsyncstp(0), m_vfldstrt(0), m_vfldstp(0),
+ m_frame_int_count(0),
+ m_row_index(0)
+{
+}
+
+//-------------------------------------------------
+// set_cpu_tag - set cpu we are attached to
+//-------------------------------------------------
+
+void i82730_device::set_cpu_tag(device_t &device, device_t *owner, const char *tag)
+{
+ i82730_device &dev = dynamic_cast<i82730_device &>(device);
+ dev.m_cpu_tag = tag;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void i82730_device::device_start()
+{
+ // register bitmap
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // resolve callbacks
+ m_sint_handler.resolve_safe();
+
+ // bind delegates
+ m_update_row_cb.bind_relative_to(*owner());
+
+ // allocate row timer
+ m_row_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(i82730_device::row_update), this));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void i82730_device::device_reset()
+{
+ cpu_device *cpu = m_owner->subdevice<cpu_device>(m_cpu_tag);
+ m_program = &cpu->space(AS_PROGRAM);
+
+ m_initialized = false;
+ m_mode_set = false;
+
+ m_ca = 0;
+ m_status = 0x0000;
+}
+
+
+//**************************************************************************
+// MEMORY ACCESS
+//**************************************************************************
+
+UINT8 i82730_device::read_byte(offs_t address)
+{
+ return m_program->read_byte(address);
+}
+
+UINT16 i82730_device::read_word(offs_t address)
+{
+ UINT16 data = 0xffff;
+
+ if (sysbus_16bit() && !(address & 1))
+ {
+ data = m_program->read_word(address);
+ }
+ else
+ {
+ data = m_program->read_byte(address);
+ data |= m_program->read_byte(address + 1) << 8;
+ }
+
+ return data;
+}
+
+void i82730_device::write_byte(offs_t address, UINT8 data)
+{
+ m_program->write_byte(address, data);
+}
+
+void i82730_device::write_word(offs_t address, UINT16 data)
+{
+ if (sysbus_16bit() && !(address & 1))
+ {
+ m_program->write_word(address, data);
+ }
+ else
+ {
+ m_program->write_byte(address, data & 0xff);
+ m_program->write_byte(address + 1, (data >> 8) & 0xff);
+ }
+}
+
+
+//**************************************************************************
+// IMPLEMENTATION
+//**************************************************************************
+
+void i82730_device::update_interrupts()
+{
+ UINT16 code = m_status & ~m_intmask & ~(VDIP | DIP);
+ write_word(m_cbp + 20, code);
+
+ if (code)
+ m_sint_handler(1);
+}
+
+void i82730_device::mode_set()
+{
+ UINT32 mptr = (read_word(m_cbp + 32) << 16) | read_word(m_cbp + 30);
+ UINT16 tmp;
+
+ tmp = read_word(mptr);
+ m_dma_burst_space = tmp & 0x7f;
+ m_dma_burst_length = (tmp >> 8) & 0x7f;
+
+ tmp = read_word(mptr + 2);
+ UINT8 hsyncstp = tmp & 0xff;
+ UINT8 line_length = (tmp >> 8) & 0xff;
+
+ tmp = read_word(mptr + 4);
+ UINT8 hfldstp = tmp & 0xff;
+ m_hfldstrt = (tmp >> 8) & 0xff;
+
+ tmp = read_word(mptr + 6);
+ UINT8 hbrdstp = tmp & 0xff;
+ UINT8 hbrdstrt = (tmp >> 8) & 0xff;
+
+ tmp = read_word(mptr + 8);
+ m_margin = tmp & 0x1f;
+
+ tmp = read_word(mptr + 10);
+ m_lpr = tmp & 0x1f;
+
+ tmp = read_word(mptr + 24);
+ m_field_attribute_mask = tmp & 0x7fff;
+
+ tmp = read_word(mptr + 26);
+ UINT16 frame_length = tmp & 0x7ff;
+
+ tmp = read_word(mptr + 28);
+ m_vsyncstp = tmp & 0x7ff;
+
+ tmp = read_word(mptr + 30);
+ m_vfldstrt = tmp & 0x7ff;
+
+ tmp = read_word(mptr + 32);
+ m_vfldstp = tmp & 0x7ff;
+
+ tmp = read_word(mptr + 38);
+ m_frame_int_count = tmp & 0x0f;
+
+ // setup screen mode
+ rectangle visarea(hbrdstrt * 16, hbrdstp * 16 - 1, m_vsyncstp, m_vfldstp + m_margin + 1 + m_lpr - 1);
+ attoseconds_t period = HZ_TO_ATTOSECONDS(clock() * 16) * line_length * 16 * frame_length;
+ m_screen->configure(line_length * 16, frame_length, visarea, period);
+
+ // start display is now valid
+ m_mode_set = true;
+
+ // adjust timer for the new mode
+ m_row_timer->adjust(m_screen->time_until_pos(0));
+
+ // output some debug info
+ if (VERBOSE)
+ {
+ logerror("%s('%s'): ---- setting mode ----\n", shortname(), basetag());
+ logerror("%s('%s'): dma burst length %02x, space %02x\n", shortname(), basetag(), m_dma_burst_length, m_dma_burst_space);
+ logerror("%s('%s'): margin %02x, lpr %02x\n", shortname(), basetag(), m_margin, m_lpr);
+ logerror("%s('%s'): hsyncstp: %02x, line_length: %02x, hfldstrt: %02x, hbrdstart: %02x, hfldstop: %02x, hbrdstop: %02x\n",
+ shortname(), basetag(), hsyncstp, line_length, m_hfldstrt, hbrdstrt, hfldstp, hbrdstp);
+ logerror("%s('%s'): frame_length %04x, vsyncstp: %04x, vfldstrt: %04x, vfldstp: %04x\n",
+ shortname(), basetag(), frame_length, m_vsyncstp, m_vfldstrt, m_vfldstp);
+ }
+}
+
+void i82730_device::execute_command()
+{
+ UINT8 command = read_byte(m_cbp + 1);
+ UINT16 tmp;
+
+ if (VERBOSE_COMMANDS && command < ARRAY_LENGTH(m_command_names))
+ logerror("%s('%s'): executing command: %s [cbp = %08x]\n", shortname(), basetag(), m_command_names[command], m_cbp);
+
+ tmp = read_word(m_cbp + 2);
+ m_list_switch = BIT(tmp, 6);
+ m_auto_line_feed = BIT(tmp, 7);
+
+ tmp = read_word(m_cbp + 4);
+ m_max_dma_count = tmp & 0xff;
+
+ switch (command)
+ {
+ // NOP
+ case 0x00:
+ break;
+
+ // START DISPLAY
+ case 0x01:
+ if (m_mode_set)
+ m_status = (m_status & ~VDIP) | DIP;
+ break;
+
+ // START VIRTUAL DISPLAY
+ case 0x02:
+ if (m_mode_set)
+ m_status = VDIP | (m_status & ~DIP);
+ break;
+
+ // STOP DISPLAY
+ case 0x03:
+ m_status &= ~(VDIP | DIP);
+ break;
+
+ // MODE SET
+ case 0x04:
+ mode_set();
+ break;
+
+ // LOAD CBP
+ case 0x05:
+ m_cbp = (read_word(m_cbp + 16) << 16) | read_word(m_cbp + 14);
+ execute_command();
+ break;
+
+ // LOAD INTMASK
+ case 0x06:
+ m_intmask = read_word(m_cbp + 22);
+ if (VERBOSE_COMMANDS)
+ logerror("%s('%s'): intmask now %04x\n", shortname(), basetag(), m_intmask);
+ break;
+
+ // LPEN ENABLE
+ case 0x07:
+ fatalerror("%s('%s'): Unimplemented command %s\n", shortname(), basetag(), m_command_names[command]);
+ break;
+
+ // READ STATUS
+ case 0x08:
+ write_word(m_cbp + 18, m_status);
+ m_status &= (VDIP | DIP);
+ break;
+
+ // LD CUR POS
+ case 0x09:
+ fatalerror("%s('%s'): Unimplemented command %s\n", shortname(), basetag(), m_command_names[command]);
+ break;
+
+ // SELF TEST
+ case 0x0a:
+ fatalerror("%s('%s'): Unimplemented command %s\n", shortname(), basetag(), m_command_names[command]);
+ break;
+
+ // TEST ROW BUFFER
+ case 0x0b:
+ fatalerror("%s('%s'): Unimplemented command %s\n", shortname(), basetag(), m_command_names[command]);
+ break;
+
+ default:
+ if (VERBOSE_COMMANDS)
+ logerror("%s('%s'): executing command: (reserved) [cbp = %08x]\n", shortname(), basetag(), m_cbp);
+ m_status |= RCC;
+ update_interrupts();
+ break;
+ }
+
+ // clear busy
+ write_word(m_cbp, read_word(m_cbp) & 0xff00);
+}
+
+void i82730_device::load_row()
+{
+ bool finished = false;
+
+ m_row[m_row_index].count = 0;
+
+ while (!finished)
+ {
+ UINT16 data = read_word(m_sptr);
+ m_sptr += 2;
+
+ if (BIT(data, 15))
+ {
+ switch (data >> 8)
+ {
+ case 0x8e:
+ m_field_attribute_mask = read_word(m_sptr) & 0x7fff;
+ m_sptr += 2;
+
+ if (VERBOSE_DATASTREAM)
+ logerror("%s('%s'): SET FIELD ATTRIB to %04x\n", shortname(), basetag(), m_field_attribute_mask);
+
+ break;
+
+ default:
+ fatalerror("%s('%s'): Unimplemented datastream command %02x\n", shortname(), basetag(), data >> 8);
+ }
+ }
+ else
+ {
+ // maximum row size is 200
+ if (m_row[m_row_index].count < m_max_dma_count && m_row[m_row_index].count < 200)
+ {
+ m_row[m_row_index].data[m_row[m_row_index].count++] = data;
+ }
+ else
+ {
+#if 0
+ // move to next string?
+ if (m_auto_line_feed == 0)
+ {
+ m_sptr = (read_word(m_lptr + 2) << 16) | read_word(m_lptr);
+ m_lptr += 4;
+ }
+#endif
+ finished = true;
+ }
+ }
+ }
+
+ m_sptr -= 2;
+}
+
+TIMER_CALLBACK_MEMBER( i82730_device::row_update )
+{
+ int y = m_screen->vpos();
+
+ if (y == 0)
+ {
+ // clear interrupt status flags
+ m_status &= (VDIP | DIP);
+
+ // clear field attribute mask
+ m_field_attribute_mask = 0;
+
+ // get listbase
+ if (m_list_switch)
+ m_lptr = (read_word(m_cbp + 8) << 16) | read_word(m_cbp + 6);
+ else
+ m_lptr = (read_word(m_cbp + 12) << 16) | read_word(m_cbp + 10);
+
+ m_sptr = (read_word(m_lptr + 2) << 16) | read_word(m_lptr);
+ m_lptr += 4;
+
+ // fetch initial row
+ m_row_index = 0;
+ load_row();
+ }
+ else if (y >= m_vsyncstp && y < m_vfldstrt)
+ {
+ // blank (top border)
+ }
+ else if (y >= m_vfldstrt && y < m_vfldstp)
+ {
+ UINT8 lc = (y - m_vfldstrt) % (m_lpr + 1);
+
+ // call driver
+ m_update_row_cb(m_bitmap, m_row[m_row_index].data, lc, y - m_vsyncstp, m_row[m_row_index].count);
+
+ // swap buffers at end of row
+ if (lc == m_lpr)
+ {
+ m_row_index ^= 1;
+ load_row();
+ }
+ }
+ else if (y >= m_vfldstp && y < m_vfldstp + m_margin + 1)
+ {
+ // margin
+ }
+ else if (y >= m_vfldstp + m_margin + 1 && y < m_vfldstp + m_margin + 1 + m_lpr + 1)
+ {
+ UINT8 lc = (y - (m_vfldstp + m_margin + 1)) % (m_lpr + 1);
+
+ m_sptr = (read_word(m_cbp + 36) << 16) | read_word(m_cbp + 34);
+ load_row();
+
+ // call driver
+ m_update_row_cb(m_bitmap, m_row[m_row_index].data, lc, y - m_vsyncstp, m_row[m_row_index].count);
+ }
+ else if (y == m_vfldstp + m_margin + 1 + m_lpr + 1)
+ {
+ // todo: check ca
+
+ // frame interrupt?
+ if ((m_screen->frame_number() % m_frame_int_count) == 0)
+ m_status |= EONF;
+
+ // check interrupts
+ update_interrupts();
+ }
+ else
+ {
+ // vblank
+ }
+
+ m_row_timer->adjust(m_screen->time_until_pos((y + 1) % m_screen->height()));
+}
+
+WRITE_LINE_MEMBER( i82730_device::ca_w )
+{
+ if (VERBOSE)
+ logerror("%s('%s'): ca_w %d\n", shortname(), basetag(), state);
+
+ // falling edge
+ if (m_ca == 1 && state == 0)
+ {
+ if (!m_initialized)
+ {
+ // get system bus width
+ m_sysbus = m_program->read_byte(0xfffffff6);
+
+ // get intermediate block pointer
+ m_ibp = (read_word(0xfffffffe) << 16) | read_word(0xfffffffc);
+
+ // get system configuration byte
+ UINT8 scb = read_byte(m_ibp + 6);
+
+ // clear busy
+ write_word(m_ibp, read_word(m_ibp) & 0xff00);
+
+ // done
+ m_initialized = true;
+
+ // output some debug info
+ if (VERBOSE)
+ {
+ logerror("%s('%s'): ---- initializing ----\n", shortname(), basetag());
+ logerror("%s('%s'): %s system bus\n", shortname(), basetag(), sysbus_16bit() ? "16-bit" : "8-bit");
+ logerror("%s('%s'): intermediate block pointer: %08x\n", shortname(), basetag(), m_ibp);
+ logerror("%s('%s'): addrbus: %s, clno: %d, clpos: %d, mode: %s, dtw16: %s, srdy: %s\n", shortname(), basetag(),
+ BIT(scb, 0) ? "32-bit" : "16-bit", (scb >> 1) & 0x03, (scb >> 3) & 0x03,
+ BIT(scb, 5) ? "master" : "slave", BIT(scb, 6) ? "16-bit" : "8-bit", BIT(scb, 7) ? "synchronous" : "asynchronous");
+ }
+ }
+
+ // fetch command block pointer
+ m_cbp = (read_word(m_ibp + 4) << 16) | read_word(m_ibp + 2);
+
+ // and execute command
+ execute_command();
+ }
+
+ m_ca = state;
+}
+
+WRITE_LINE_MEMBER( i82730_device::irst_w )
+{
+ if (VERBOSE)
+ logerror("%s('%s'): irst_w %d\n", shortname(), basetag(), state);
+
+ m_sint_handler(0);
+}
+
+UINT32 i82730_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, m_hfldstrt * 16, 0, cliprect);
+ return 0;
+}
diff --git a/src/devices/video/i82730.h b/src/devices/video/i82730.h
new file mode 100644
index 00000000000..ff2602d2307
--- /dev/null
+++ b/src/devices/video/i82730.h
@@ -0,0 +1,157 @@
+// license:GPL-2.0+
+// copyright-holders:Dirk Best
+/***************************************************************************
+
+ Intel 82730
+
+ Text Coprocessor
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __I82730_H__
+#define __I82730_H__
+
+#include "emu.h"
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_I82730_ADD(_tag, _cpu_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, I82730, _clock) \
+ i82730_device::set_cpu_tag(*device, owner, _cpu_tag);
+
+#define MCFG_I82730_SINT_HANDLER(_devcb) \
+ devcb = &i82730_device::set_sint_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_I82730_UPDATE_ROW_CB(_class, _method) \
+ i82730_device::set_update_row_callback(*device, i82730_update_row_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef device_delegate<void (bitmap_rgb32 &bitmap, UINT16 *data, UINT8 lc, UINT16 y, int x_count)> i82730_update_row_delegate;
+
+#define I82730_UPDATE_ROW(name) \
+ void name(bitmap_rgb32 &bitmap, UINT16 *data, UINT8 lc, UINT16 y, int x_count)
+
+
+// ======================> i82730_device
+
+class i82730_device : public device_t, public device_video_interface
+{
+public:
+ // construction/destruction
+ i82730_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // callbacks
+ template<class _Object> static devcb_base &set_sint_handler(device_t &device, _Object object)
+ { return downcast<i82730_device &>(device).m_sint_handler.set_callback(object); }
+
+ // inline configuration
+ static void set_cpu_tag(device_t &device, device_t *owner, const char *tag);
+ static void set_update_row_callback(device_t &device, i82730_update_row_delegate callback) { downcast<i82730_device &>(device).m_update_row_cb = callback; }
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ DECLARE_WRITE_LINE_MEMBER(ca_w);
+ DECLARE_WRITE_LINE_MEMBER(irst_w);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ // status
+ enum
+ {
+ DUR = 0x001, // data underrun
+ LPU = 0x002, // light pen update
+ DBOR = 0x004, // data buffer overrun
+ EONF = 0x008, // end of n frames
+ FDE = 0x010, // frame data error
+ RCC = 0x020, // reserved channel command executed
+ RDC = 0x040, // reserved data stream command executed
+ DIP = 0x080, // display in progress
+ VDIP = 0x100 // virtual display in progress
+ };
+
+ static const char* m_command_names[];
+
+ bool sysbus_16bit() { return BIT(m_sysbus, 0); }
+
+ UINT8 read_byte(offs_t address);
+ UINT16 read_word(offs_t address);
+ void write_byte(offs_t address, UINT8 data);
+ void write_word(offs_t address, UINT16 data);
+
+ void update_interrupts();
+ void mode_set();
+ void execute_command();
+ void load_row();
+
+ TIMER_CALLBACK_MEMBER(row_update);
+
+ devcb_write_line m_sint_handler;
+ i82730_update_row_delegate m_update_row_cb;
+
+ const char *m_cpu_tag;
+ address_space *m_program;
+
+ emu_timer *m_row_timer;
+
+ bitmap_rgb32 m_bitmap;
+
+ bool m_initialized;
+ bool m_mode_set;
+
+ int m_ca;
+
+ // internal registers
+ UINT8 m_sysbus;
+ UINT32 m_ibp;
+ UINT32 m_cbp;
+ UINT16 m_intmask;
+ UINT16 m_status;
+
+ int m_list_switch;
+ int m_auto_line_feed;
+ UINT8 m_max_dma_count;
+
+ UINT32 m_lptr;
+ UINT32 m_sptr;
+
+ int m_dma_burst_space;
+ int m_dma_burst_length;
+
+ // display parameters
+ int m_hfldstrt;
+ int m_margin;
+ int m_lpr;
+ UINT16 m_field_attribute_mask;
+ int m_vsyncstp;
+ int m_vfldstrt;
+ int m_vfldstp;
+
+ int m_frame_int_count;
+
+ // row buffers
+ struct row_buffer
+ {
+ UINT16 data[200];
+ int count;
+ };
+
+ row_buffer m_row[2];
+ int m_row_index;
+};
+
+// device type definition
+extern const device_type I82730;
+
+#endif // __I82730_H__
diff --git a/src/devices/video/i8275.c b/src/devices/video/i8275.c
new file mode 100644
index 00000000000..4f75d7cbc1a
--- /dev/null
+++ b/src/devices/video/i8275.c
@@ -0,0 +1,691 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Intel 8275 Programmable CRT Controller emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - double spaced rows
+
+*/
+
+#include "i8275.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+const int DMA_BURST_SPACING[] = { 0, 7, 15, 23, 31, 39, 47, 55 };
+
+
+#define DOUBLE_SPACED_ROWS \
+ BIT(m_param[REG_SCN1], 7)
+
+#define CHARACTERS_PER_ROW \
+ ((m_param[REG_SCN1] & 0x7f) + 1)
+
+#define VRTC_ROW_COUNT \
+ ((m_param[REG_SCN2] >> 5) + 1)
+
+#define CHARACTER_ROWS_PER_FRAME \
+ ((m_param[REG_SCN2] & 0x3f) + 1)
+
+#define UNDERLINE \
+ (m_param[REG_SCN3] >> 4)
+
+#define SCANLINES_PER_ROW \
+ ((m_param[REG_SCN3] & 0x0f) + 1)
+
+#define OFFSET_LINE_COUNTER \
+ BIT(m_param[REG_SCN4], 7)
+
+#define VISIBLE_FIELD_ATTRIBUTE \
+ BIT(m_param[REG_SCN4], 6)
+
+#define CURSOR_FORMAT \
+ ((m_param[REG_SCN4] >> 4) & 0x03)
+
+#define HRTC_COUNT \
+ (((m_param[REG_SCN4] & 0x0f) + 1) * 2)
+
+#define DMA_BURST_COUNT \
+ (1 << (m_param[REG_DMA] & 0x03))
+
+#define DMA_BURST_SPACE \
+ DMA_BURST_SPACING[(m_param[REG_DMA] >> 2) & 0x07]
+
+
+const int i8275_device::character_attribute[3][16] =
+{
+ { 2, 2, 4, 4, 2, 4, 4, 4, 2, 4, 4, 0, 2, 0, 0, 0 },
+ { 8, 0xc, 8, 0xc, 1, 0xc, 8, 1, 1, 4, 1, 0, 2, 0, 0, 0 },
+ { 4, 4, 2, 2, 4, 4, 4, 2, 2, 4, 4, 0, 2, 0, 0, 0 }
+};
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// device type definition
+const device_type I8275 = &device_creator<i8275_device>;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// i8275_device - constructor
+//-------------------------------------------------
+
+i8275_device::i8275_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, I8275, "I8275 CRTC", tag, owner, clock, "i8275x", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_write_irq(*this),
+ m_write_drq(*this),
+ m_write_hrtc(*this),
+ m_write_vrtc(*this),
+ m_status(0),
+ m_param_idx(0),
+ m_param_end(0),
+ m_buffer_idx(0),
+ m_fifo_next(false),
+ m_buffer_dma(0),
+ m_lpen(0),
+ m_hlgt(0),
+ m_vsp(0),
+ m_gpa(0),
+ m_rvv(0),
+ m_lten(0),
+ m_scanline(0),
+ m_du(false),
+ m_dma_stop(false),
+ m_end_of_screen(false),
+ m_cursor_blink(0),
+ m_char_blink(0),
+ m_stored_attr(0)
+{
+ memset(m_param, 0x00, sizeof(m_param));
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void i8275_device::device_start()
+{
+ // get the screen device
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // resolve callbacks
+ m_display_cb.bind_relative_to(*owner());
+ m_write_drq.resolve_safe();
+ m_write_irq.resolve_safe();
+ m_write_hrtc.resolve_safe();
+ m_write_vrtc.resolve_safe();
+
+ // allocate timers
+ m_hrtc_on_timer = timer_alloc(TIMER_HRTC_ON);
+ m_drq_on_timer = timer_alloc(TIMER_DRQ_ON);
+ m_scanline_timer = timer_alloc(TIMER_SCANLINE);
+
+ // state saving
+ save_item(NAME(m_status));
+ save_item(NAME(m_param));
+ save_item(NAME(m_param_idx));
+ save_item(NAME(m_param_end));
+ save_item(NAME(m_buffer[0]));
+ save_item(NAME(m_buffer[1]));
+ save_item(NAME(m_buffer_idx));
+ save_item(NAME(m_fifo_idx));
+ save_item(NAME(m_fifo_next));
+ save_item(NAME(m_buffer_dma));
+ save_item(NAME(m_lpen));
+ save_item(NAME(m_hlgt));
+ save_item(NAME(m_vsp));
+ save_item(NAME(m_gpa));
+ save_item(NAME(m_rvv));
+ save_item(NAME(m_lten));
+ save_item(NAME(m_scanline));
+ save_item(NAME(m_irq_scanline));
+ save_item(NAME(m_vrtc_scanline));
+ save_item(NAME(m_vrtc_drq_scanline));
+ save_item(NAME(m_du));
+ save_item(NAME(m_dma_stop));
+ save_item(NAME(m_end_of_screen));
+ save_item(NAME(m_cursor_blink));
+ save_item(NAME(m_char_blink));
+ save_item(NAME(m_stored_attr));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void i8275_device::device_reset()
+{
+ memset(m_buffer, 0, sizeof(m_buffer));
+
+ m_status &= ~ST_IE;
+
+ m_write_irq(CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// device_timer - handle timer events
+//-------------------------------------------------
+
+void i8275_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ //int y = m_screen->vpos();
+ //int x = m_screen->hpos();
+ int rc = m_scanline / SCANLINES_PER_ROW;
+ int lc = m_scanline % SCANLINES_PER_ROW;
+
+ switch (id)
+ {
+ case TIMER_HRTC_ON:
+ //if (LOG) logerror("I8275 '%s' y %u x %u HRTC 1\n", tag(), y, x);
+ m_write_hrtc(1);
+ break;
+
+ case TIMER_DRQ_ON:
+ //if (LOG) logerror("I8275 '%s' y %u x %u DRQ 1\n", tag(), y, x);
+ m_write_drq(1);
+ break;
+
+ case TIMER_SCANLINE:
+ if (!(m_status & ST_VE)) break;
+
+ //if (LOG) logerror("I8275 '%s' y %u x %u HRTC 0\n", tag(), y, x);
+ m_write_hrtc(0);
+
+ if (m_scanline == 0)
+ {
+ //if (LOG) logerror("I8275 '%s' y %u x %u VRTC 0\n", tag(), y, x);
+ m_write_vrtc(0);
+ }
+
+ if (m_scanline <= (m_vrtc_scanline - SCANLINES_PER_ROW))
+ {
+ if (lc == 0)
+ {
+ if (m_buffer_idx < CHARACTERS_PER_ROW)
+ {
+ m_status |= ST_DU;
+ m_du = true;
+
+ //if (LOG) logerror("I8275 '%s' y %u x %u DMA Underrun\n", tag(), y, x);
+
+ m_write_drq(0);
+ }
+
+ if (!m_du && !m_dma_stop)
+ {
+ // swap line buffers
+ m_buffer_dma = !m_buffer_dma;
+ m_buffer_idx = 0;
+ m_fifo_idx = 0;
+
+ if ((m_scanline < (m_vrtc_scanline - SCANLINES_PER_ROW)))
+ {
+ // start DMA burst
+ m_drq_on_timer->adjust(clocks_to_attotime(DMA_BURST_SPACE));
+ }
+ }
+ }
+ }
+
+ if (m_scanline == m_irq_scanline)
+ {
+ if (m_status & ST_IE)
+ {
+ //if (LOG) logerror("I8275 '%s' y %u x %u IRQ 1\n", tag(), y, x);
+ m_status |= ST_IR;
+ m_write_irq(ASSERT_LINE);
+ }
+ }
+
+ if (m_scanline == m_vrtc_scanline)
+ {
+ //if (LOG) logerror("I8275 '%s' y %u x %u VRTC 1\n", tag(), y, x);
+ m_write_vrtc(1);
+
+ // reset field attributes
+ m_hlgt = 0;
+ m_vsp = 0;
+ m_gpa = 0;
+ m_rvv = 0,
+ m_lten = 0;
+
+ m_du = false;
+ m_dma_stop = false;
+ m_end_of_screen = false;
+
+ m_cursor_blink++;
+ m_cursor_blink &= 0x1f;
+
+ m_char_blink++;
+ m_char_blink &= 0x3f;
+ m_stored_attr = 0;
+ }
+
+ if (m_scanline == m_vrtc_drq_scanline)
+ {
+ // swap line buffers
+ m_buffer_dma = !m_buffer_dma;
+ m_buffer_idx = 0;
+ m_fifo_idx = 0;
+
+ // start DMA burst
+ m_drq_on_timer->adjust(clocks_to_attotime(DMA_BURST_SPACE));
+ }
+
+ if (m_scanline < m_vrtc_scanline)
+ {
+ int line_counter = OFFSET_LINE_COUNTER ? ((lc - 1) % SCANLINES_PER_ROW) : lc;
+ bool end_of_row = false;
+ int fifo_idx = 0;
+ m_hlgt = (m_stored_attr & FAC_H) ? 1 : 0;
+ m_vsp = (m_stored_attr & FAC_B) ? 1 : 0;
+ m_gpa = (m_stored_attr & FAC_GG) >> 2;
+ m_rvv = (m_stored_attr & FAC_R) ? 1 : 0;
+ m_lten = ((m_stored_attr & FAC_U) != 0) && (lc == UNDERLINE) ? 1 : 0;
+
+ for (int sx = 0; sx < CHARACTERS_PER_ROW; sx++)
+ {
+ int m_lineattr = 0;
+ int lten = 0;
+ int vsp = 0;
+ int rvv = 0;
+
+ UINT8 data = (end_of_row || m_end_of_screen) ? 0 : m_buffer[!m_buffer_dma][sx];
+
+ if (data & 0x80)
+ {
+ if ((data & 0xc0) == 0x80)
+ {
+ // field attribute code
+ m_hlgt = (data & FAC_H) ? 1 : 0;
+ m_vsp = (data & FAC_B) ? 1 : 0;
+ m_gpa = (data & FAC_GG) >> 2;
+ m_rvv = (data & FAC_R) ? 1 : 0;
+ m_lten = ((data & FAC_U) != 0) && (lc == UNDERLINE) ? 1 : 0;
+ if ((SCANLINES_PER_ROW - lc)==1)
+ m_stored_attr = data;
+
+ if (!VISIBLE_FIELD_ATTRIBUTE)
+ {
+ data = m_fifo[!m_buffer_dma][fifo_idx];
+
+ fifo_idx++;
+ fifo_idx &= 0xf;
+ }
+ else
+ {
+ vsp = 1;
+ }
+ }
+ else
+ {
+ if ((data & 0xf0) == 0xf0)
+ {
+ // special control character
+ switch (data)
+ {
+ case SCC_END_OF_ROW:
+ case SCC_END_OF_ROW_DMA:
+ end_of_row = true;
+ break;
+
+ case SCC_END_OF_SCREEN:
+ case SCC_END_OF_SCREEN_DMA:
+ m_end_of_screen = true;
+ break;
+ }
+ //vsp = 1;
+ }
+ else
+ {
+ // character attribute code
+ m_hlgt = (data & CA_H) ? 1 : 0;
+ m_vsp = (data & CA_B) ? 1 : 0;
+
+ UINT8 ca = 0;
+ int cccc = (data >> 2) & 0x0f;
+
+ if (line_counter < UNDERLINE)
+ {
+ ca = character_attribute[0][cccc];
+ }
+ else if (line_counter == UNDERLINE)
+ {
+ ca = character_attribute[1][cccc];
+ }
+ else
+ {
+ ca = character_attribute[2][cccc];
+ }
+
+ m_lten = (ca & CA_LTEN) ? 1 : 0;
+ m_vsp = (ca & CA_VSP) ? 1 : 0;
+ m_lineattr = ca >> 2;
+ }
+ }
+ }
+
+ if (!vsp && m_vsp)
+ {
+ vsp = (m_char_blink < 32) ? 1 : 0;
+ }
+
+ if ((rc == m_param[REG_CUR_ROW]) && (sx == m_param[REG_CUR_COL]))
+ {
+ int vis = 1;
+
+ if (!(CURSOR_FORMAT & 0x02))
+ {
+ vis = (m_cursor_blink < 16) ? 1 : 0;
+ }
+
+ if (CURSOR_FORMAT & 0x01)
+ {
+ lten = (lc == UNDERLINE) ? vis : 0;
+ }
+ else
+ {
+ rvv = vis;
+ }
+ }
+
+ if (end_of_row || m_end_of_screen)
+ {
+ vsp = 1;
+ }
+
+ if (!m_display_cb.isnull())
+ m_display_cb(m_bitmap,
+ sx * m_hpixels_per_column, // x position on screen of starting point
+ m_scanline, // y position on screen
+ line_counter, // current line of char
+ (data & 0x7f), // char code to be displayed
+ m_lineattr, // line attribute code
+ lten | m_lten, // light enable signal
+ rvv ^ m_rvv, // reverse video signal
+ vsp, // video suppression
+ m_gpa, // general purpose attribute code
+ m_hlgt // highlight
+ );
+ }
+ }
+
+ m_scanline++;
+ m_scanline %= ((CHARACTER_ROWS_PER_FRAME + VRTC_ROW_COUNT) * SCANLINES_PER_ROW);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// read -
+//-------------------------------------------------
+
+READ8_MEMBER( i8275_device::read )
+{
+ UINT8 data = 0;
+
+ if (offset & 0x01)
+ {
+ data = m_status;
+
+ if (m_status & ST_IR)
+ {
+ //if (LOG) logerror("I8275 '%s' IRQ 0\n", tag());
+ m_write_irq(CLEAR_LINE);
+ }
+
+ m_status &= ~(ST_IR | ST_LP | ST_IC | ST_DU | ST_FO);
+ }
+ else
+ {
+ data = m_param[m_param_idx];
+ m_param_idx++;
+
+ if (m_param_idx > m_param_end)
+ {
+ m_status |= ST_IC;
+ }
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write -
+//-------------------------------------------------
+
+WRITE8_MEMBER( i8275_device::write )
+{
+ if (offset & 0x01)
+ {
+ if (LOG) logerror("I8275 '%s' Command %02x\n", tag(), data);
+
+ switch (data >> 5)
+ {
+ case CMD_RESET:
+ if (LOG) logerror("I8275 '%s' Reset\n", tag());
+
+ m_status &= ~ST_IE;
+ if (LOG) logerror("I8275 '%s' IRQ 0\n", tag());
+ m_write_irq(CLEAR_LINE);
+ m_write_drq(0);
+
+ m_param_idx = REG_SCN1;
+ m_param_end = REG_SCN4;
+ break;
+
+ case CMD_START_DISPLAY:
+ {
+ m_param[REG_DMA] = data;
+ if (LOG) logerror("I8275 '%s' Start Display %u %u\n", tag(), DMA_BURST_COUNT, DMA_BURST_SPACE);
+ m_status |= (ST_IE | ST_VE);
+ }
+ break;
+
+ case CMD_STOP_DISPLAY:
+ if (LOG) logerror("I8275 '%s' Stop Display\n", tag());
+ m_status &= ~ST_VE;
+ break;
+
+ case CMD_READ_LIGHT_PEN:
+ if (LOG) logerror("I8275 '%s' Read Light Pen\n", tag());
+ m_param_idx = REG_LPEN_COL;
+ m_param_end = REG_LPEN_ROW;
+ break;
+
+ case CMD_LOAD_CURSOR:
+ if (LOG) logerror("I8275 '%s' Load Cursor\n", tag());
+ m_param_idx = REG_CUR_COL;
+ m_param_end = REG_CUR_ROW;
+ break;
+
+ case CMD_ENABLE_INTERRUPT:
+ if (LOG) logerror("I8275 '%s' Enable Interrupt\n", tag());
+ m_status |= ST_IE;
+ break;
+
+ case CMD_DISABLE_INTERRUPT:
+ if (LOG) logerror("I8275 '%s' Disable Interrupt\n", tag());
+ m_status &= ~ST_IE;
+ break;
+
+ case CMD_PRESET_COUNTERS:
+ if (LOG) logerror("I8275 '%s' Preset Counters\n", tag());
+ m_scanline = 0;
+ break;
+ }
+ }
+ else
+ {
+ if (LOG) logerror("I8275 '%s' Parameter %02x\n", tag(), data);
+
+ m_param[m_param_idx] = data;
+
+ if (m_param_idx == REG_SCN4)
+ {
+ recompute_parameters();
+ }
+
+ m_param_idx++;
+ }
+}
+
+
+//-------------------------------------------------
+// dack_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( i8275_device::dack_w )
+{
+ //if (LOG) logerror("I8275 '%s' y %u x %u DACK %04x:%02x %u\n", tag(), m_screen->vpos(), m_screen->hpos(), offset, data, m_buffer_idx);
+
+ m_write_drq(0);
+
+ if (m_fifo_next)
+ {
+ if (m_fifo_idx == 16)
+ {
+ m_fifo_idx = 0;
+ m_status |= ST_FO;
+ }
+
+ m_fifo[m_buffer_dma][m_fifo_idx++] = data;
+
+ m_fifo_next = false;
+ }
+ else
+ {
+ assert(m_buffer_idx >= 0 && m_buffer_idx < ARRAY_LENGTH(m_buffer[m_buffer_dma]));
+ m_buffer[m_buffer_dma][m_buffer_idx++] = data;
+
+ if (!VISIBLE_FIELD_ATTRIBUTE && ((data & 0xc0) == 0x80))
+ {
+ m_fifo_next = true;
+ }
+
+ switch (data)
+ {
+ case SCC_END_OF_ROW_DMA:
+ // stop DMA
+ // TODO should read one more character if DMA burst not completed
+ break;
+
+ case SCC_END_OF_SCREEN_DMA:
+ m_dma_stop = true;
+ // TODO should read one more character if DMA burst not completed
+ break;
+
+ default:
+ if (m_buffer_idx == CHARACTERS_PER_ROW)
+ {
+ // stop DMA
+ }
+ else if (!(m_buffer_idx % DMA_BURST_COUNT))
+ {
+ m_drq_on_timer->adjust(clocks_to_attotime(DMA_BURST_SPACE));
+ }
+ else
+ {
+ m_drq_on_timer->adjust(attotime::zero);
+ }
+ }
+
+ }
+}
+
+
+//-------------------------------------------------
+// lpen_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( i8275_device::lpen_w )
+{
+ if (!m_lpen && state)
+ {
+ m_param[REG_LPEN_COL] = m_screen->hpos() / m_hpixels_per_column;
+ m_param[REG_LPEN_ROW] = m_screen->vpos() / SCANLINES_PER_ROW;
+
+ m_status |= ST_LP;
+ }
+
+ m_lpen = state;
+}
+
+
+//-------------------------------------------------
+// screen_update -
+//-------------------------------------------------
+
+UINT32 i8275_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ if (!(m_status & ST_VE))
+ {
+ m_bitmap.fill(rgb_t::black);
+ }
+
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// recompute_parameters -
+//-------------------------------------------------
+
+void i8275_device::recompute_parameters()
+{
+ int y = m_screen->vpos();
+
+ int horiz_pix_total = (CHARACTERS_PER_ROW + HRTC_COUNT) * m_hpixels_per_column;
+ int vert_pix_total = (CHARACTER_ROWS_PER_FRAME + VRTC_ROW_COUNT) * SCANLINES_PER_ROW;
+ attoseconds_t refresh = m_screen->frame_period().attoseconds();
+ int max_visible_x = (CHARACTERS_PER_ROW * m_hpixels_per_column) - 1;
+ int max_visible_y = (CHARACTER_ROWS_PER_FRAME * SCANLINES_PER_ROW) - 1;
+
+ if (LOG) logerror("width %u height %u max_x %u max_y %u refresh %f\n", horiz_pix_total, vert_pix_total, max_visible_x, max_visible_y, 1 / ATTOSECONDS_TO_DOUBLE(refresh));
+
+ rectangle visarea;
+ visarea.set(0, max_visible_x, 0, max_visible_y);
+ m_screen->configure(horiz_pix_total, vert_pix_total, visarea, refresh);
+
+ int hrtc_on_pos = CHARACTERS_PER_ROW * m_hpixels_per_column;
+ m_hrtc_on_timer->adjust(m_screen->time_until_pos(y, hrtc_on_pos), 0, m_screen->scan_period());
+
+ m_irq_scanline = (CHARACTER_ROWS_PER_FRAME - 1) * SCANLINES_PER_ROW;
+ m_vrtc_scanline = CHARACTER_ROWS_PER_FRAME * SCANLINES_PER_ROW;
+ m_vrtc_drq_scanline = vert_pix_total - SCANLINES_PER_ROW;
+
+ if (LOG) logerror("irq_y %u vrtc_y %u drq_y %u\n", m_irq_scanline, m_vrtc_scanline, m_vrtc_drq_scanline);
+
+ m_scanline_timer->adjust(m_screen->time_until_pos(0, 0), 0, m_screen->scan_period());
+
+ if (DOUBLE_SPACED_ROWS) fatalerror("Double spaced rows not supported!");
+}
diff --git a/src/devices/video/i8275.h b/src/devices/video/i8275.h
new file mode 100644
index 00000000000..15451d4c7e8
--- /dev/null
+++ b/src/devices/video/i8275.h
@@ -0,0 +1,236 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Intel 8275 Programmable CRT Controller emulation
+
+**********************************************************************
+ _____ _____
+ LC3 1 |* \_/ | 40 Vcc
+ LC2 2 | | 39 LA0
+ LC1 3 | | 38 LA1
+ LC0 4 | | 37 LTEN
+ DRQ 5 | | 36 RVV
+ _DACK 6 | | 35 VSP
+ HRTC 7 | | 34 GPA1
+ VRTC 8 | | 33 GPA0
+ _RD 9 | | 32 HLGT
+ _WR 10 | 8275 | 31 IRQ
+ LPEN 11 | | 30 CCLK
+ DB0 12 | | 29 CC6
+ DB1 13 | | 28 CC5
+ DB2 14 | | 27 CC4
+ DB3 15 | | 26 CC3
+ DB4 16 | | 25 CC2
+ DB5 17 | | 24 CC1
+ DB6 18 | | 23 CC0
+ DB7 19 | | 22 _CS
+ GND 20 |_____________| 21 A0
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __I8275x__
+#define __I8275x__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define I8275_DRAW_CHARACTER_MEMBER(_name) void _name(bitmap_rgb32 &bitmap, int x, int y, UINT8 linecount, UINT8 charcode, UINT8 lineattr, UINT8 lten, UINT8 rvv, UINT8 vsp, UINT8 gpa, UINT8 hlgt)
+
+
+#define MCFG_I8275_CHARACTER_WIDTH(_value) \
+ i8275_device::static_set_character_width(*device, _value);
+
+#define MCFG_I8275_DRAW_CHARACTER_CALLBACK_OWNER(_class, _method) \
+ i8275_device::static_set_display_callback(*device, i8275_draw_character_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_I8275_DRQ_CALLBACK(_write) \
+ devcb = &i8275_device::set_drq_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_I8275_IRQ_CALLBACK(_write) \
+ devcb = &i8275_device::set_irq_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_I8275_HRTC_CALLBACK(_write) \
+ devcb = &i8275_device::set_hrtc_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_I8275_VRTC_CALLBACK(_write) \
+ devcb = &i8275_device::set_vrtc_wr_callback(*device, DEVCB_##_write);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef device_delegate<void (bitmap_rgb32 &bitmap, int x, int y, UINT8 linecount, UINT8 charcode, UINT8 lineattr, UINT8 lten, UINT8 rvv, UINT8 vsp, UINT8 gpa, UINT8 hlgt)> i8275_draw_character_delegate;
+
+
+// ======================> i8275_device
+
+class i8275_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ i8275_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_character_width(device_t &device, int value) { downcast<i8275_device &>(device).m_hpixels_per_column = value; }
+ static void static_set_display_callback(device_t &device, i8275_draw_character_delegate callback) { downcast<i8275_device &>(device).m_display_cb = callback; }
+
+ template<class _Object> static devcb_base &set_drq_wr_callback(device_t &device, _Object object) { return downcast<i8275_device &>(device).m_write_drq.set_callback(object); }
+ template<class _Object> static devcb_base &set_irq_wr_callback(device_t &device, _Object object) { return downcast<i8275_device &>(device).m_write_irq.set_callback(object); }
+ template<class _Object> static devcb_base &set_hrtc_wr_callback(device_t &device, _Object object) { return downcast<i8275_device &>(device).m_write_hrtc.set_callback(object); }
+ template<class _Object> static devcb_base &set_vrtc_wr_callback(device_t &device, _Object object) { return downcast<i8275_device &>(device).m_write_vrtc.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_WRITE8_MEMBER( dack_w );
+
+ DECLARE_WRITE_LINE_MEMBER( lpen_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ void recompute_parameters();
+
+ enum
+ {
+ TIMER_HRTC_ON,
+ TIMER_DRQ_ON,
+ TIMER_SCANLINE
+ };
+
+ enum
+ {
+ ST_IE = 0x40,
+ ST_IR = 0x20,
+ ST_LP = 0x10,
+ ST_IC = 0x08,
+ ST_VE = 0x04,
+ ST_DU = 0x02,
+ ST_FO = 0x01
+ };
+
+ enum
+ {
+ CMD_RESET = 0,
+ CMD_START_DISPLAY,
+ CMD_STOP_DISPLAY,
+ CMD_READ_LIGHT_PEN,
+ CMD_LOAD_CURSOR,
+ CMD_ENABLE_INTERRUPT,
+ CMD_DISABLE_INTERRUPT,
+ CMD_PRESET_COUNTERS
+ };
+
+ enum
+ {
+ REG_SCN1 = 0,
+ REG_SCN2,
+ REG_SCN3,
+ REG_SCN4,
+ REG_CUR_COL,
+ REG_CUR_ROW,
+ REG_LPEN_COL,
+ REG_LPEN_ROW,
+ REG_DMA
+ };
+
+ enum
+ {
+ CA_H = 0x01,
+ CA_B = 0x02,
+ CA_CCCC = 0x3c,
+ CA_LTEN = 0x01,
+ CA_VSP = 0x02,
+ CA_LA0 = 0x04,
+ CA_LA1 = 0x08
+ };
+
+ enum
+ {
+ SCC_END_OF_ROW = 0xf0,
+ SCC_END_OF_ROW_DMA = 0xf1,
+ SCC_END_OF_SCREEN = 0xf2,
+ SCC_END_OF_SCREEN_DMA = 0xf3
+ };
+
+ enum
+ {
+ FAC_H = 0x01,
+ FAC_B = 0x02,
+ FAC_GG = 0x0c,
+ FAC_R = 0x10,
+ FAC_U = 0x20
+ };
+
+ static const int character_attribute[3][16];
+
+ devcb_write_line m_write_irq;
+ devcb_write_line m_write_drq;
+ devcb_write_line m_write_hrtc;
+ devcb_write_line m_write_vrtc;
+
+ i8275_draw_character_delegate m_display_cb;
+ int m_hpixels_per_column;
+
+ bitmap_rgb32 m_bitmap;
+
+ UINT8 m_status;
+ UINT8 m_param[REG_DMA + 1];
+ int m_param_idx;
+ int m_param_end;
+
+ UINT8 m_buffer[2][80];
+ UINT8 m_fifo[2][16];
+ int m_buffer_idx;
+ int m_fifo_idx;
+ bool m_fifo_next;
+ int m_buffer_dma;
+
+ int m_lpen;
+
+ int m_hlgt;
+ int m_vsp;
+ int m_gpa;
+ int m_rvv;
+ int m_lten;
+
+ int m_scanline;
+ int m_irq_scanline;
+ int m_vrtc_scanline;
+ int m_vrtc_drq_scanline;
+ bool m_du;
+ bool m_dma_stop;
+ bool m_end_of_screen;
+
+ int m_cursor_blink;
+ int m_char_blink;
+ UINT8 m_stored_attr;
+
+ // timers
+ emu_timer *m_hrtc_on_timer;
+ emu_timer *m_drq_on_timer;
+ emu_timer *m_scanline_timer;
+};
+
+
+// device type definition
+extern const device_type I8275;
+
+
+
+#endif
diff --git a/src/devices/video/m50458.c b/src/devices/video/m50458.c
new file mode 100644
index 00000000000..1db8b57aaf0
--- /dev/null
+++ b/src/devices/video/m50458.c
@@ -0,0 +1,449 @@
+// license:LGPL-2.1+
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+ Mitsubishi M50458 OSD chip
+
+ device by Angelo Salese
+
+ TODO:
+ - vertical scrolling needs references (might work differently and/or in
+ "worse" ways, the one currently implemented guesses that the screen is
+ masked at the top and the end when in scrolling mode).
+ - Understand what the "vertical start position" really does (vblank?)
+ - Check if the ROM source is actually 2bpp once that a redump is made
+ (the shadow ROM copy doesn't convince me 100%);
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/m50458.h"
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type M50458 = &device_creator<m50458_device>;
+
+static ADDRESS_MAP_START( m50458_vram, AS_0, 16, m50458_device )
+ AM_RANGE(0x0000, 0x023f) AM_RAM // vram
+ AM_RANGE(0x0240, 0x0241) AM_WRITE(vreg_120_w)
+ AM_RANGE(0x0242, 0x0243) AM_WRITE(vreg_121_w)
+ AM_RANGE(0x0244, 0x0245) AM_WRITE(vreg_122_w)
+ AM_RANGE(0x0246, 0x0247) AM_WRITE(vreg_123_w)
+ AM_RANGE(0x0248, 0x0249) AM_WRITE(vreg_124_w)
+ AM_RANGE(0x024a, 0x024b) AM_WRITE(vreg_125_w)
+ AM_RANGE(0x024c, 0x024d) AM_WRITE(vreg_126_w)
+ AM_RANGE(0x024e, 0x024f) AM_WRITE(vreg_127_w)
+ADDRESS_MAP_END
+
+// internal GFX ROM (TODO: GFXs in here should be 12x18, not 16x18)
+// (also note: ROM length CAN'T be 0x1200)
+ROM_START( m50458 )
+ ROM_REGION( 0x1200, "m50458", 0 )
+ ROM_LOAD("m50458-001sp", 0x0000, 0x1200, BAD_DUMP CRC(444f597d) SHA1(96beda6aba3d9f7bb781a3cd0352ed6ae45e2ebe) )
+ ROM_LOAD("m50458_char.bin", 0x0000, 0x1200, BAD_DUMP CRC(011cc342) SHA1(d5b9f32d6e251b4b25945267d7c68c099bd83e96) )
+ROM_END
+
+WRITE16_MEMBER( m50458_device::vreg_120_w)
+{
+// printf("%04x\n",data);
+}
+
+WRITE16_MEMBER( m50458_device::vreg_121_w)
+{
+ /* Horizontal char size for line 0 */
+ m_hsz1 = (data & 0xc0) >> 6;
+
+ /* Horizontal char size for line 1 - 10 */
+ m_hsz2 = (data & 0x300) >> 8;
+
+ /* Horizontal char size for line 11 */
+ m_hsz3 = (data & 0xc00) >> 10;
+}
+
+
+WRITE16_MEMBER( m50458_device::vreg_122_w)
+{
+ /* Vertical char size for line 0 */
+ m_vsz1 = (data & 0xc0) >> 6;
+
+ /* Vertical char size for line 1 - 10 */
+ m_vsz2 = (data & 0x300) >> 8;
+
+ /* Vertical char size for line 11 */
+ m_vsz3 = (data & 0xc00) >> 10;
+
+}
+
+WRITE16_MEMBER( m50458_device::vreg_123_w)
+{
+ /* fractional part of vertical scrolling */
+ m_scrf = data & 0x1f;
+
+ m_space = (data & 0x60) >> 5;
+
+ /* char part of vertical scrolling */
+ m_scrr = (data & 0x0f00) >> 8;
+
+// printf("%02x %02x %02x\n",m_scrr,m_scrf,m_space);
+}
+
+WRITE16_MEMBER( m50458_device::vreg_124_w)
+{
+}
+
+WRITE16_MEMBER( m50458_device::vreg_125_w)
+{
+ /* blinking cycle */
+ m_blink = data & 4 ? 0x20 : 0x40;
+}
+
+WRITE16_MEMBER( m50458_device::vreg_126_w)
+{
+ /* Raster Color Setting */
+ m_phase = data & 7;
+
+ //printf("%04x\n",data);
+}
+
+
+WRITE16_MEMBER( m50458_device::vreg_127_w)
+{
+ if(data & 0x20) // RAMERS, display RAM is erased
+ {
+ int i;
+
+ for(i=0;i<0x120;i++)
+ write_word(i,0x007f);
+ }
+}
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *m50458_device::device_rom_region() const
+{
+ return ROM_NAME( m50458 );
+}
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *m50458_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// read_word - read a word at the given address
+//-------------------------------------------------
+
+inline UINT16 m50458_device::read_word(offs_t address)
+{
+ return space().read_word(address << 1);
+}
+
+//-------------------------------------------------
+// write_word - write a word at the given address
+//-------------------------------------------------
+
+inline void m50458_device::write_word(offs_t address, UINT16 data)
+{
+ space().write_word(address << 1, data);
+}
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// m50458_device - constructor
+//-------------------------------------------------
+
+m50458_device::m50458_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, M50458, "M50458 OSD", tag, owner, clock, "m50458", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 16, 16, 0, NULL, *ADDRESS_MAP_NAME(m50458_vram))
+{
+}
+
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+void m50458_device::device_validity_check(validity_checker &valid) const
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void m50458_device::device_start()
+{
+ UINT16 tmp;
+ UINT8 *pcg = memregion("m50458")->base();
+ int tile;
+ int yi;
+ UINT16 src,dst;
+
+ /* Create an array for shadow gfx */
+ /* this will spread the source ROM into four directions (up-left, up-right, down-left, down-right) thus creating a working shadow copy */
+ m_shadow_gfx = auto_alloc_array_clear(machine(), UINT8, 0x1200);
+
+ for(tile=0;tile<0x80;tile++)
+ {
+ for(yi=1;yi<17;yi++)
+ {
+ src = (tile & 0x7f)*36+yi*2; /* source offset */
+
+ dst = (tile & 0x7f)*36+(yi-1)*2; /* destination offset */
+
+ tmp = (((pcg[src]<<8)|(pcg[src+1]&0xff)) & 0xfffe) >> 1;
+
+ m_shadow_gfx[dst+1] |= tmp & 0xff;
+ m_shadow_gfx[dst] |= (tmp >> 8);
+
+ tmp = (((pcg[src]<<8)|(pcg[src+1]&0xff)) & 0x7fff) << 1;
+
+ m_shadow_gfx[dst+1] |= tmp & 0xff;
+ m_shadow_gfx[dst] |= (tmp >> 8);
+
+ dst = (tile & 0x7f)*36+(yi+1)*2; /* destination offset */
+
+ tmp = (((pcg[src]<<8)|(pcg[src+1]&0xff)) & 0xfffe) >> 1;
+
+ m_shadow_gfx[dst+1] |= tmp & 0xff;
+ m_shadow_gfx[dst] |= (tmp >> 8);
+
+ tmp = (((pcg[src]<<8)|(pcg[src+1]&0xff)) & 0x7fff) << 1;
+
+ m_shadow_gfx[dst+1] |= tmp & 0xff;
+ m_shadow_gfx[dst] |= (tmp >> 8);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void m50458_device::device_reset()
+{
+ int i;
+
+ /* clear VRAM at boot */
+ for(i=0;i<0x120;i++)
+ write_word(i,0x007f);
+
+ m_blink = 0x40;
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+WRITE_LINE_MEMBER( m50458_device::write_bit )
+{
+ m_latch = state;
+}
+
+WRITE_LINE_MEMBER( m50458_device::set_cs_line )
+{
+ m_reset_line = state;
+
+ if(m_reset_line != CLEAR_LINE)
+ {
+ //printf("Reset asserted\n");
+ m_cmd_stream_pos = 0;
+ m_current_cmd = 0;
+ m_osd_state = OSD_SET_ADDRESS;
+ }
+}
+
+
+WRITE_LINE_MEMBER( m50458_device::set_clock_line )
+{
+ if (m_reset_line == CLEAR_LINE)
+ {
+ if(state == 1)
+ {
+ //printf("%d\n",m_latch);
+
+ m_current_cmd = (m_current_cmd >> 1) | ((m_latch<<15)&0x8000);
+ m_cmd_stream_pos++;
+
+ if(m_cmd_stream_pos == 16)
+ {
+ switch(m_osd_state)
+ {
+ case OSD_SET_ADDRESS:
+ m_osd_addr = m_current_cmd;
+ m_osd_state = OSD_SET_DATA;
+ break;
+ case OSD_SET_DATA:
+ //if(m_osd_addr >= 0x120)
+ //printf("%04x %04x\n",m_osd_addr,m_current_cmd);
+ write_word(m_osd_addr,m_current_cmd);
+ m_osd_addr++;
+ /* Presumably wraps at 0x127? */
+ if(m_osd_addr > 0x127) { m_osd_addr = 0; }
+ break;
+ }
+
+ m_cmd_stream_pos = 0;
+ m_current_cmd = 0;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------
+// update_screen -
+//-------------------------------------------------
+
+UINT32 m50458_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int x,y;
+ UINT8 *pcg = memregion("m50458")->base();
+ UINT8 bg_r,bg_g,bg_b;
+
+ /* TODO: there's probably a way to control the brightness in this */
+ bg_r = m_phase & 1 ? 0xdf : 0;
+ bg_g = m_phase & 2 ? 0xdf : 0;
+ bg_b = m_phase & 4 ? 0xdf : 0;
+ bitmap.fill(rgb_t(0xff,bg_r,bg_g,bg_b),cliprect);
+
+ for(y=0;y<12;y++)
+ {
+ for(x=0;x<24;x++)
+ {
+ int xi,yi;
+ UINT16 tile;
+ int y_base = y;
+
+ if(y != 0 && m_scrr > 1) { y_base+=(m_scrr - 1); }
+ if(y_base > 11) { y_base -= 11; }
+ if(m_scrr && y == 11) { y_base = 0; } /* Guess: repeat line 0 if scrolling is active */
+
+ tile = read_word(x+y_base*24);
+
+ for(yi=0;yi<18;yi++)
+ {
+ for(xi=4;xi<16;xi++) /* TODO: remove 4 / 16 / -4 offset once that the ROM is fixed */
+ {
+ UINT8 pix;
+ UINT8 color = (tile & 0x700) >> 8;
+ UINT16 offset = ((tile & 0x7f)*36+yi*2);
+ int res_y,res_x;
+ UINT8 xh,yh;
+
+ if(xi>=8)
+ pix = ((pcg[offset+1] >> (7-(xi & 0x7))) & 1) << 1;
+ else
+ pix = ((pcg[offset+0] >> (7-(xi & 0x7))) & 1) << 1;
+
+ if(xi>=8)
+ pix |= ((m_shadow_gfx[offset+1] >> (7-(xi & 0x7))) & 1);
+ else
+ pix |= ((m_shadow_gfx[offset+0] >> (7-(xi & 0x7))) & 1);
+
+ /* blinking, VERY preliminary */
+ if(tile & 0x800 && m_screen->frame_number() & m_blink)
+ pix = 0;
+
+ if(yi == 17 && tile & 0x1000) /* underline? */
+ pix |= 1;
+
+ res_y = y*18+yi;
+ res_x = x*12+(xi-4);
+
+ if(y != 0 && y != 11)
+ {
+ res_y -= m_scrf;
+ if(res_y < 18) /* wrap-around */
+ res_y += 216;
+ }
+
+ if(pix != 0)
+ {
+ UINT8 r,g,b;
+
+ if(pix & 2)
+ {
+ r = (color & 0x1) ? 0xff : 0x00;
+ g = (color & 0x2) ? 0xff : 0x00;
+ b = (color & 0x4) ? 0xff : 0x00;
+ }
+ else //if(pix & 1)
+ {
+ /* TODO: is there a parameter for the border parameter? */
+ r = 0x00;
+ g = 0x00;
+ b = 0x00;
+ }
+
+ /* TODO: clean this up (also needs better testing) */
+ if(y_base == 0)
+ {
+ res_x *= (m_hsz1 + 1);
+ res_y *= (m_vsz1 + 1);
+
+ if(res_y > 215 || res_x > 288)
+ continue;
+
+ for(yh=0;yh<m_vsz1+1;yh++)
+ for(xh=0;xh<m_hsz1+1;xh++)
+ bitmap.pix32(res_y+yh,res_x+xh) = r << 16 | g << 8 | b;
+ }
+ else if(y_base == 11)
+ {
+ res_x *= (m_hsz3 + 1);
+ res_y += ((m_vsz2 * (y-1)) * 18) + 9 * m_vsz2;
+ res_y *= (m_vsz3 + 1);
+
+ if(res_y > 215 || res_x > 288)
+ continue;
+
+ for(yh=0;yh<m_vsz3+1;yh++)
+ for(xh=0;xh<m_hsz3+1;xh++)
+ bitmap.pix32(res_y+yh,res_x+xh) = r << 16 | g << 8 | b;
+ }
+ else
+ {
+ res_x *= (m_hsz2 + 1);
+ res_y *= (m_vsz2 + 1);
+
+ if(res_y > 215 || res_x > 288)
+ continue;
+
+ for(yh=0;yh<m_vsz2+1;yh++)
+ for(xh=0;xh<m_hsz2+1;xh++)
+ bitmap.pix32(res_y+yh,res_x+xh) = r << 16 | g << 8 | b;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/devices/video/m50458.h b/src/devices/video/m50458.h
new file mode 100644
index 00000000000..79246a68fdd
--- /dev/null
+++ b/src/devices/video/m50458.h
@@ -0,0 +1,105 @@
+// license:LGPL-2.1+
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+Mitsubishi M50458 OSD chip
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __M50458DEV_H__
+#define __M50458DEV_H__
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_M50458_ADD(_tag,_freq,_screen) \
+ MCFG_DEVICE_ADD(_tag, M50458,_freq) \
+ MCFG_VIDEO_SET_SCREEN(_screen)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+enum m50458_state_t
+{
+ OSD_SET_ADDRESS = 0,
+ OSD_SET_DATA
+};
+
+// ======================> m50458_device
+
+class m50458_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ m50458_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // I/O operations
+ DECLARE_WRITE_LINE_MEMBER( write_bit );
+ DECLARE_WRITE_LINE_MEMBER( set_cs_line );
+ DECLARE_WRITE_LINE_MEMBER( set_clock_line );
+ DECLARE_WRITE16_MEMBER(vreg_120_w);
+ DECLARE_WRITE16_MEMBER(vreg_121_w);
+ DECLARE_WRITE16_MEMBER(vreg_122_w);
+ DECLARE_WRITE16_MEMBER(vreg_123_w);
+ DECLARE_WRITE16_MEMBER(vreg_124_w);
+ DECLARE_WRITE16_MEMBER(vreg_125_w);
+ DECLARE_WRITE16_MEMBER(vreg_126_w);
+ DECLARE_WRITE16_MEMBER(vreg_127_w);
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ virtual const rom_entry *device_rom_region() const;
+
+protected:
+ // device-level overrides
+ virtual void device_validity_check(validity_checker &valid) const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ int m_latch;
+ int m_reset_line;
+ int m_clock_line;
+ UINT16 m_current_cmd;
+ int m_cmd_stream_pos;
+ UINT16 m_osd_addr;
+ UINT8 *m_shadow_gfx;
+
+ UINT8 m_bg_pen;
+ UINT8 m_phase;
+ UINT8 m_scrf,m_scrr;
+ UINT8 m_space;
+ UINT8 m_hsz1,m_hsz2,m_hsz3;
+ UINT8 m_vsz1,m_vsz2,m_vsz3;
+ UINT8 m_blink;
+
+ m50458_state_t m_osd_state;
+
+private:
+ inline UINT16 read_word(offs_t address);
+ inline void write_word(offs_t address, UINT16 data);
+
+ const address_space_config m_space_config;
+};
+
+
+// device type definition
+extern const device_type M50458;
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+
+
+#endif
diff --git a/src/devices/video/mb90082.c b/src/devices/video/mb90082.c
new file mode 100644
index 00000000000..cefa4e60415
--- /dev/null
+++ b/src/devices/video/mb90082.c
@@ -0,0 +1,248 @@
+// license:LGPL-2.1+
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+ Fujitsu MB90082 OSD
+
+ preliminary device by Angelo Salese
+
+ TODO:
+ - get a real charset ROM;
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/mb90082.h"
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type MB90082 = &device_creator<mb90082_device>;
+
+static ADDRESS_MAP_START( mb90082_vram, AS_0, 16, mb90082_device )
+ AM_RANGE(0x0000, 0x023f) AM_RAM // main screen vram
+ AM_RANGE(0x0400, 0x063f) AM_RAM // main screen attr
+// AM_RANGE(0x0800, 0x0a3f) AM_RAM // sub screen vram
+// AM_RANGE(0x0c00, 0x0e3f) AM_RAM // sub screen attr
+ADDRESS_MAP_END
+
+/* charset is undumped, but apparently a normal ASCII one is enough for the time being (for example "fnt0808.x1" in Sharp X1) */
+ROM_START( mb90082 )
+ ROM_REGION( 0x2000, "mb90082", ROMREGION_ERASEFF )
+ ROM_LOAD("mb90082_char.bin", 0x0000, 0x0800, NO_DUMP )
+ROM_END
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *mb90082_device::device_rom_region() const
+{
+ return ROM_NAME( mb90082 );
+}
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *mb90082_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT16 mb90082_device::read_word(offs_t address)
+{
+ return space().read_word(address << 1);
+}
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void mb90082_device::write_word(offs_t address, UINT16 data)
+{
+ space().write_word(address << 1, data);
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// mb90082_device - constructor
+//-------------------------------------------------
+
+mb90082_device::mb90082_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MB90082, "MB90082 OSD", tag, owner, clock, "mb90082", __FILE__),
+ device_memory_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 16, 16, 0, NULL, *ADDRESS_MAP_NAME(mb90082_vram))
+{
+}
+
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+void mb90082_device::device_validity_check(validity_checker &valid) const
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mb90082_device::device_start()
+{
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mb90082_device::device_reset()
+{
+ m_cmd_ff = 0;
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+WRITE_LINE_MEMBER( mb90082_device::set_cs_line )
+{
+ m_reset_line = state;
+
+ if(m_reset_line != CLEAR_LINE)
+ {
+ // ...
+ }
+}
+
+
+WRITE8_MEMBER( mb90082_device::write )
+{
+ UINT16 dat;
+
+ switch(m_cmd_ff)
+ {
+ case OSD_COMMAND:
+ m_cmd = data & 0xf8;
+ m_cmd_param = data & 7;
+ //printf("cmd %02x\n",data);
+ break;
+ case OSD_DATA:
+ dat = ((m_cmd_param & 7)<<7) | (data & 0x7f);
+ switch(m_cmd)
+ {
+ case 0x80: // Preset VRAM address
+ m_osd_addr = dat & 0x1ff;
+ m_fil = (dat & 0x200) >> 9;
+ break;
+ case 0x88: // Attribute select
+ m_attr = dat;
+ break;
+ case 0x90: // Write Character
+ int x,y;
+ x = (m_osd_addr & 0x01f);
+ y = (m_osd_addr & 0x1e0) >> 5;
+
+ if(m_fil)
+ {
+ int i;
+ if(x != 0)
+ printf("FIL with %d %d\n",x,y);
+
+ for(i=0;i<24;i++)
+ {
+ write_word((i+y*24)|0x000,dat);
+ write_word((i+y*24)|0x200,m_attr);
+ }
+ }
+ else
+ {
+ write_word((x+y*24)|0x000,dat);
+ write_word((x+y*24)|0x200,m_attr);
+
+ /* handle address increments */
+ x = ((x + 1) % 24);
+ if(x == 0)
+ y = ((y + 1) % 12);
+ m_osd_addr = x + (y << 5);
+ }
+ break;
+ case 0xd0: // Set Under Color
+ m_uc = dat & 7;
+ break;
+
+ }
+ break;
+ }
+
+ m_cmd_ff ^= 1;
+}
+
+UINT32 mb90082_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int x,y;
+ UINT8 *pcg = memregion("mb90082")->base();
+ UINT16 tile,attr;
+ UINT8 bg_r,bg_g,bg_b;
+
+ /* TODO: there's probably a way to control the brightness in this */
+ bg_b = m_uc & 1 ? 0xdf : 0;
+ bg_g = m_uc & 2 ? 0xdf : 0;
+ bg_r = m_uc & 4 ? 0xdf : 0;
+ bitmap.fill(rgb_t(0xff,bg_r,bg_g,bg_b),cliprect);
+
+ for(y=0;y<12;y++)
+ {
+ for(x=0;x<24;x++)
+ {
+ int xi,yi;
+
+ tile = read_word(x+y*24);
+ attr = read_word((x+y*24)|0x200);
+
+ /* TODO: charset hook-up is obviously WRONG so following mustn't be trusted at all */
+ for(yi=0;yi<16;yi++)
+ {
+ for(xi=0;xi<16;xi++)
+ {
+ UINT8 pix;
+ UINT8 color = (attr & 0x70) >> 4;
+ UINT8 r,g,b;
+
+ pix = (pcg[(tile*8)+(yi >> 1)] >> (7-(xi >> 1))) & 1;
+
+ /* TODO: check this */
+ b = (color & 1) ? 0xff : 0;
+ g = (color & 2) ? 0xff : 0;
+ r = (color & 4) ? 0xff : 0;
+
+ if(tile != 0xff && pix != 0)
+ bitmap.pix32(y*16+yi,x*16+xi) = r << 16 | g << 8 | b;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/devices/video/mb90082.h b/src/devices/video/mb90082.h
new file mode 100644
index 00000000000..863a3aec797
--- /dev/null
+++ b/src/devices/video/mb90082.h
@@ -0,0 +1,85 @@
+// license:LGPL-2.1+
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+ Fujitsu MB90082 OSD
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __MB90082DEV_H__
+#define __MB90082DEV_H__
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_MB90082_ADD(_tag,_freq) \
+ MCFG_DEVICE_ADD(_tag, MB90082, _freq)
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+enum
+{
+ OSD_COMMAND = 0,
+ OSD_DATA
+};
+
+
+// ======================> mb90082_device
+
+class mb90082_device : public device_t,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ mb90082_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // I/O operations
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_WRITE_LINE_MEMBER( set_cs_line );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ virtual const rom_entry *device_rom_region() const;
+
+protected:
+ // device-level overrides
+ virtual void device_validity_check(validity_checker &valid) const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+private:
+ UINT8 m_cmd_ff;
+ UINT8 m_cmd,m_cmd_param;
+ UINT8 m_reset_line;
+
+ UINT16 m_osd_addr;
+ UINT8 m_fil;
+ UINT8 m_uc;
+ UINT8 m_attr;
+
+ inline UINT16 read_word(offs_t address);
+ inline void write_word(offs_t address, UINT16 data);
+
+ const address_space_config m_space_config;
+};
+
+
+// device type definition
+extern const device_type MB90082;
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+
+
+#endif
diff --git a/src/devices/video/mb_vcu.c b/src/devices/video/mb_vcu.c
new file mode 100644
index 00000000000..0ad568976f8
--- /dev/null
+++ b/src/devices/video/mb_vcu.c
@@ -0,0 +1,579 @@
+// license:LGPL-2.1+
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+Device for Mazer Blazer/Great Guns custom Video Controller Unit
+
+Written by Angelo Salese, based off old implementation by Jarek Burczynski
+
+TODO:
+- understand what exactly modes 0x03 and 0x13 really reads in set_clr() and
+ where it puts results (yeah, shared VCU RAM, but exactly where?). Almost
+ surely Mazer Blazer tries to read the pixel data for collision detection and
+ Great Guns read backs VRAM for VCU test (patched for now, btw).
+- Understand look-up tables in i/o space.
+- Understand how to handle layer clearance.
+- Understand how planes are really handled.
+- Understand how transparent pens are handled (is 0x0f always transparent or
+ there's some clut gimmick? Great Guns title screen makes me think of the
+ latter option)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/mb_vcu.h"
+#include "video/resnet.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type MB_VCU = &device_creator<mb_vcu_device>;
+
+//-------------------------------------------------
+// static_set_palette_tag: Set the tag of the
+// palette device
+//-------------------------------------------------
+
+void mb_vcu_device::static_set_palette_tag(device_t &device, const char *tag)
+{
+ downcast<mb_vcu_device &>(device).m_palette.set_tag(tag);
+}
+
+
+static ADDRESS_MAP_START( mb_vcu_vram, AS_0, 8, mb_vcu_device )
+ AM_RANGE(0x00000,0x7ffff) AM_RAM // enough for a 256x256x4 x 2 pages of framebuffer with 4 layers (TODO: doubled for simplicity)
+ADDRESS_MAP_END
+
+
+static ADDRESS_MAP_START( mb_vcu_pal_ram, AS_1, 8, mb_vcu_device )
+ AM_RANGE(0x0000, 0x00ff) AM_RAM
+ AM_RANGE(0x0200, 0x02ff) AM_RAM
+ AM_RANGE(0x0400, 0x04ff) AM_RAM
+ AM_RANGE(0x0600, 0x06ff) AM_READWRITE(mb_vcu_paletteram_r,mb_vcu_paletteram_w)
+ADDRESS_MAP_END
+
+READ8_MEMBER( mb_vcu_device::mb_vcu_paletteram_r )
+{
+ return m_palram[offset];
+}
+
+WRITE8_MEMBER( mb_vcu_device::mb_vcu_paletteram_w )
+{
+ int r,g,b, bit0, bit1, bit2;
+
+ m_palram[offset] = data;
+
+ /* red component */
+ bit1 = (m_palram[offset] >> 7) & 0x01;
+ bit0 = (m_palram[offset] >> 6) & 0x01;
+ r = combine_2_weights(m_weights_r, bit0, bit1);
+
+ /* green component */
+ bit2 = (m_palram[offset] >> 5) & 0x01;
+ bit1 = (m_palram[offset] >> 4) & 0x01;
+ bit0 = (m_palram[offset] >> 3) & 0x01;
+ g = combine_3_weights(m_weights_g, bit0, bit1, bit2);
+
+ /* blue component */
+ bit2 = (m_palram[offset] >> 2) & 0x01;
+ bit1 = (m_palram[offset] >> 1) & 0x01;
+ bit0 = (m_palram[offset] >> 0) & 0x01;
+ b = combine_3_weights(m_weights_b, bit0, bit1, bit2);
+
+ m_palette->set_pen_color(offset, rgb_t(r, g, b));
+}
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *mb_vcu_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_0: return &m_videoram_space_config;
+ case AS_1: return &m_paletteram_space_config;
+ default: return NULL;
+ }
+}
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// read_byte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 mb_vcu_device::read_byte(offs_t address)
+{
+ return space(AS_0).read_byte(address);
+}
+
+//-------------------------------------------------
+// write_byte - write a byte at the given address
+//-------------------------------------------------
+
+inline void mb_vcu_device::write_byte(offs_t address, UINT8 data)
+{
+ space(AS_0).write_byte(address, data);
+}
+
+//-------------------------------------------------
+// read_byte - read a byte at the given i/o
+//-------------------------------------------------
+
+inline UINT8 mb_vcu_device::read_io(offs_t address)
+{
+ return space(AS_1).read_byte(address);
+}
+
+//-------------------------------------------------
+// write_byte - write a byte at the given i/o
+//-------------------------------------------------
+
+inline void mb_vcu_device::write_io(offs_t address, UINT8 data)
+{
+ space(AS_1).write_byte(address, data);
+}
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// mb_vcu_device - constructor
+//-------------------------------------------------
+
+mb_vcu_device::mb_vcu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MB_VCU, "Mazer Blazer custom VCU", tag, owner, clock, "mb_vcu", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 19, 0, NULL, *ADDRESS_MAP_NAME(mb_vcu_vram)),
+ m_paletteram_space_config("palram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(mb_vcu_pal_ram)),
+ m_cpu(*this),
+ m_palette(*this)
+{
+}
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+void mb_vcu_device::device_validity_check(validity_checker &valid) const
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mb_vcu_device::device_start()
+{
+ // TODO: m_screen_tag
+ m_ram = auto_alloc_array_clear(machine(), UINT8, 0x800);
+ m_palram = auto_alloc_array_clear(machine(), UINT8, 0x100);
+
+ {
+ static const int resistances_r[2] = { 4700, 2200 };
+ static const int resistances_gb[3] = { 10000, 4700, 2200 };
+
+ /* just to calculate coefficients for later use */
+ compute_resistor_weights(0, 255, -1.0,
+ 3, resistances_gb, m_weights_g, 3600, 0,
+ 3, resistances_gb, m_weights_b, 3600, 0,
+ 2, resistances_r, m_weights_r, 3600, 0);
+ }
+
+ save_item(NAME(m_status));
+ save_pointer(NAME(m_ram), 0x800);
+ save_pointer(NAME(m_palram), 0x100);
+ save_item(NAME(m_param_offset_latch));
+ save_item(NAME(m_xpos));
+ save_item(NAME(m_ypos));
+ save_item(NAME(m_color1));
+ save_item(NAME(m_color2));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_pix_xsize));
+ save_item(NAME(m_pix_ysize));
+ save_item(NAME(m_vregs));
+ save_item(NAME(m_bk_color));
+ save_item(NAME(m_vbank));
+ save_item(NAME(m_weights_r));
+ save_item(NAME(m_weights_g));
+ save_item(NAME(m_weights_b));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mb_vcu_device::device_reset()
+{
+ m_status = 1;
+
+ for(int i=0;i<0x80000;i++)
+ {
+ write_byte(i,0x0f);
+ }
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+// UINT8 *pcg = memregion("sub2")->base();
+
+READ8_MEMBER( mb_vcu_device::read_ram )
+{
+ return m_ram[offset];
+}
+
+WRITE8_MEMBER( mb_vcu_device::write_ram )
+{
+ m_ram[offset] = data;
+}
+
+WRITE8_MEMBER( mb_vcu_device::write_vregs )
+{
+ m_vregs[offset] = data;
+}
+
+/* latches RAM offset to send to params */
+READ8_MEMBER( mb_vcu_device::load_params )
+{
+ m_param_offset_latch = offset;
+
+ m_xpos = m_ram[m_param_offset_latch + 1] | (m_ram[m_param_offset_latch + 2]<<8);
+ m_ypos = m_ram[m_param_offset_latch + 3] | (m_ram[m_param_offset_latch + 4]<<8);
+ m_color1 = m_ram[m_param_offset_latch + 5];
+ m_color2 = m_ram[m_param_offset_latch + 6];
+ m_mode = m_ram[m_param_offset_latch + 7];
+ m_pix_xsize = m_ram[m_param_offset_latch + 8] + 1;
+ m_pix_ysize = m_ram[m_param_offset_latch + 9] + 1;
+
+ if(0)
+ {
+ printf("[0] %02x ",m_ram[m_param_offset_latch]);
+ printf("X: %04x ",m_xpos);
+ printf("Y: %04x ",m_ypos);
+ printf("C1:%02x ",m_color1);
+ printf("C2:%02x ",m_color2);
+ printf("M :%02x ",m_mode);
+ printf("XS:%02x ",m_pix_xsize);
+ printf("YS:%02x ",m_pix_ysize);
+ printf("\n");
+ }
+
+ return 0; // open bus?
+}
+
+READ8_MEMBER( mb_vcu_device::load_gfx )
+{
+ int xi,yi;
+ int dstx,dsty;
+ UINT8 dot;
+ int bits = 0;
+ UINT8 pen = 0;
+ UINT8 cur_layer;
+
+// cur_layer = (m_mode & 0x3);
+ cur_layer = 0;
+
+ switch(m_mode >> 2)
+ {
+ case 0x00: // 4bpp
+ for(yi=0;yi<m_pix_ysize;yi++)
+ {
+ for(xi=0;xi<m_pix_xsize;xi++)
+ {
+ dstx = (m_xpos + xi);
+ dsty = (m_ypos + yi);
+
+ if(dstx >= 0 && dsty >= 0 && dstx < 256 && dsty < 256)
+ {
+ dot = m_cpu->space(AS_PROGRAM).read_byte(((offset + (bits >> 3)) & 0x1fff) + 0x4000) >> (4-(bits & 7));
+ dot&= 0xf;
+
+ //if(dot != 0xf || m_mode & 2)
+ write_byte(dstx|dsty<<8|cur_layer<<16|m_vbank<<18, dot);
+ }
+ bits += 4;
+ }
+ }
+ break;
+
+ case 0x02: // 1bpp
+ for(yi=0;yi<m_pix_ysize;yi++)
+ {
+ for(xi=0;xi<m_pix_xsize;xi++)
+ {
+ dstx = (m_xpos + xi);
+ dsty = (m_ypos + yi);
+
+ if(dstx >= 0 && dsty >= 0 && dstx < 256 && dsty < 256)
+ {
+ dot = m_cpu->space(AS_PROGRAM).read_byte(((offset + (bits >> 3)) & 0x1fff) + 0x4000) >> (7-(bits & 7));
+ dot&= 1;
+
+ pen = dot ? (m_color1 >> 4) : (m_color1 & 0xf);
+ //if(pen != 0xf || m_mode & 2)
+ write_byte(dstx|dsty<<8|cur_layer<<16|m_vbank<<18, pen);
+ }
+ bits++;
+ }
+ }
+ break;
+ case 0x03: //2bpp
+ for (yi = 0; yi < m_pix_ysize; yi++)
+ {
+ for (xi = 0; xi < m_pix_xsize; xi++)
+ {
+ dstx = (m_xpos + xi);
+ dsty = (m_ypos + yi);
+
+ if(dstx >= 0 && dsty >= 0 && dstx < 256 && dsty < 256)
+ {
+ dot = m_cpu->space(AS_PROGRAM).read_byte(((offset + (bits >> 3)) & 0x1fff) + 0x4000) >> (6-(bits & 7));
+
+ switch(dot & 3)
+ {
+ case 0:
+ pen = m_color1 & 0xf;
+ break;
+ case 1:
+ pen = m_color1 >> 4;
+ break;
+ case 2:
+ pen = m_color2 & 0xf;
+ break;
+ case 3:
+ pen = m_color2 >> 4;
+ break;
+ }
+
+ //if(pen != 0xf)
+ write_byte(dstx|dsty<<8|cur_layer<<16|m_vbank<<18, pen);
+ }
+
+ bits+=2;
+ }
+ }
+ break;
+
+ default:
+ popmessage("Unsupported draw mode");
+ break;
+ }
+
+ return 0; // open bus?
+}
+
+/*
+---0 -111 (0x07) write to i/o?
+---0 -011 (0x03) read to i/o?
+---1 -011 (0x13) read to vram?
+*/
+READ8_MEMBER( mb_vcu_device::load_set_clr )
+{
+ int xi,yi;
+ int dstx,dsty;
+// UINT8 dot;
+ int bits = 0;
+ if(m_mode == 0x13 || m_mode == 0x03)
+ {
+ printf("[0] %02x ",m_ram[m_param_offset_latch]);
+ printf("X: %04x ",m_xpos);
+ printf("Y: %04x ",m_ypos);
+ printf("C1:%02x ",m_color1);
+ printf("C2:%02x ",m_color2);
+ printf("M :%02x ",m_mode);
+ printf("XS:%02x ",m_pix_xsize);
+ printf("YS:%02x ",m_pix_ysize);
+ printf("VB:%02x ",m_vbank);
+ printf("\n");
+ }
+
+ switch(m_mode)
+ {
+ case 0x13:
+ case 0x03:
+ for (yi = 0; yi < m_pix_ysize; yi++)
+ {
+ for (xi = 0; xi < m_pix_xsize; xi++)
+ {
+ dstx = (m_xpos + xi);
+ dsty = (m_ypos + yi);
+
+ if(dstx < 256 && dsty < 256)
+ {
+ #if 0
+ dot = m_cpu->space(AS_PROGRAM).read_byte(((offset + (bits >> 3)) & 0x1fff) + 0x4000) >> (6-(bits & 7));
+ dot&= 3;
+
+ switch(dot)
+ {
+ case 0:
+ write_byte(dstx|dsty<<8, m_color1 & 0xf);
+ break;
+ case 1:
+ write_byte(dstx|dsty<<8, m_color1 >> 4);
+ break;
+ case 2:
+ write_byte(dstx|dsty<<8, m_color2 & 0xf);
+ break;
+ case 3:
+ write_byte(dstx|dsty<<8, m_color2 >> 4);
+ break;
+ }
+ #endif
+
+ //write_byte(dstx|dsty<<8, m_mode >> 4);
+ }
+
+ bits+=2;
+ }
+ }
+ break;
+
+ case 0x07:
+ for(int i=0;i<m_pix_xsize;i++)
+ write_io(i+(m_ypos<<8),m_ram[offset + i]);
+
+ break;
+ }
+
+ return 0; // open bus?
+}
+
+WRITE8_MEMBER( mb_vcu_device::background_color_w )
+{
+ int bit0,bit1,bit2;
+ int r,g,b;
+ m_bk_color = data;
+
+ /* red component */
+ bit1 = (m_bk_color >> 7) & 0x01;
+ bit0 = (m_bk_color >> 6) & 0x01;
+ r = combine_2_weights(m_weights_r, bit0, bit1);
+
+ /* green component */
+ bit2 = (m_bk_color >> 5) & 0x01;
+ bit1 = (m_bk_color >> 4) & 0x01;
+ bit0 = (m_bk_color >> 3) & 0x01;
+ g = combine_3_weights(m_weights_g, bit0, bit1, bit2);
+
+ /* blue component */
+ bit2 = (m_bk_color >> 2) & 0x01;
+ bit1 = (m_bk_color >> 1) & 0x01;
+ bit0 = (m_bk_color >> 0) & 0x01;
+ b = combine_3_weights(m_weights_b, bit0, bit1, bit2);
+
+ m_palette->set_pen_color(0x100, rgb_t(r, g, b));
+}
+
+READ8_MEMBER( mb_vcu_device::status_r )
+{
+ /*
+ ---- ---x busy or vblank flag
+ */
+ return m_status;
+}
+
+WRITE8_MEMBER( mb_vcu_device::vbank_w )
+{
+ m_vbank = (data & 0x40) >> 6;
+}
+
+//-------------------------------------------------
+// update_screen -
+//-------------------------------------------------
+
+UINT32 mb_vcu_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int x,y;
+ UINT8 dot;
+
+ bitmap.fill(0x100,cliprect);
+
+ for(y=0;y<256;y++)
+ {
+ for(x=0;x<256;x++)
+ {
+ dot = read_byte((x >> 0)|(y<<8)|0<<16|(m_vbank ^ 1)<<18);
+ //if(dot != 0xf)
+ {
+ dot|= m_vregs[1] << 4;
+
+ bitmap.pix32(y,x) = m_palette->pen(dot);
+ }
+ }
+ }
+
+ #if 0
+ for(y=0;y<256;y++)
+ {
+ for(x=0;x<256;x++)
+ {
+ dot = read_byte((x >> 0)|(y<<8)|3<<16);
+
+ if(dot != 0xf)
+ {
+ dot|= m_vregs[1] << 4;
+
+ bitmap.pix32(y,x) = machine().pens[dot];
+ }
+ }
+ }
+
+ for(y=0;y<256;y++)
+ {
+ for(x=0;x<256;x++)
+ {
+ dot = read_byte((x >> 0)|(y<<8)|0<<16);
+
+ if(dot != 0xf)
+ {
+ dot|= m_vregs[1] << 4;
+
+ bitmap.pix32(y,x) = machine().pens[dot];
+ }
+ }
+ }
+
+ for(y=0;y<256;y++)
+ {
+ for(x=0;x<256;x++)
+ {
+ dot = read_byte((x >> 0)|(y<<8)|1<<16);
+
+ if(dot != 0xf)
+ {
+ dot|= m_vregs[1] << 4;
+
+ bitmap.pix32(y,x) = machine().pens[dot];
+ }
+ }
+ }
+ #endif
+
+ return 0;
+}
+
+void mb_vcu_device::screen_eof(void)
+{
+ //for(int i=0;i<0x10000;i++)
+ {
+ //write_byte(i|0x00000|m_vbank<<18,0x0f);
+ //write_byte(i|0x10000|m_vbank<<18,0x0f);
+ //write_byte(i|0x30000|m_vbank<<18,0x0f);
+ }
+}
diff --git a/src/devices/video/mb_vcu.h b/src/devices/video/mb_vcu.h
new file mode 100644
index 00000000000..ed6e45663c9
--- /dev/null
+++ b/src/devices/video/mb_vcu.h
@@ -0,0 +1,92 @@
+// license:LGPL-2.1+
+// copyright-holders:Angelo Salese
+#pragma once
+
+#ifndef __MB_VCUDEV_H__
+#define __MB_VCUDEV_H__
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> mb_vcu_device
+
+class mb_vcu_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ mb_vcu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration
+ static void static_set_palette_tag(device_t &device, const char *tag);
+ static void set_cpu_tag(device_t &device, const char *tag) { downcast<mb_vcu_device &>(device).m_cpu.set_tag(tag); }
+
+ // I/O operations
+ DECLARE_WRITE8_MEMBER( write_vregs );
+ DECLARE_READ8_MEMBER( read_ram );
+ DECLARE_WRITE8_MEMBER( write_ram );
+ DECLARE_READ8_MEMBER( load_params );
+ DECLARE_READ8_MEMBER( load_gfx );
+ DECLARE_READ8_MEMBER( load_set_clr );
+ DECLARE_WRITE8_MEMBER( background_color_w );
+ DECLARE_READ8_MEMBER( status_r );
+ DECLARE_WRITE8_MEMBER( vbank_w );
+ DECLARE_READ8_MEMBER( mb_vcu_paletteram_r );
+ DECLARE_WRITE8_MEMBER( mb_vcu_paletteram_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void screen_eof(void);
+
+protected:
+ // device-level overrides
+ virtual void device_validity_check(validity_checker &valid) const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+private:
+ inline UINT8 read_byte(offs_t address);
+ inline void write_byte(offs_t address, UINT8 data);
+ inline UINT8 read_io(offs_t address);
+ inline void write_io(offs_t address, UINT8 data);
+
+ const address_space_config m_videoram_space_config;
+ const address_space_config m_paletteram_space_config;
+ UINT8 m_status;
+ UINT8 *m_ram;
+ UINT8 *m_palram;
+ UINT16 m_param_offset_latch;
+
+ INT16 m_xpos, m_ypos;
+ UINT8 m_color1, m_color2;
+ UINT8 m_mode;
+ UINT16 m_pix_xsize, m_pix_ysize;
+ UINT8 m_vregs[4];
+ UINT8 m_bk_color;
+ UINT8 m_vbank;
+
+ double m_weights_r[2];
+ double m_weights_g[3];
+ double m_weights_b[3];
+ required_device<cpu_device> m_cpu;
+ required_device<palette_device> m_palette;
+};
+
+
+// device type definition
+extern const device_type MB_VCU;
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_MB_VCU_CPU(_tag) \
+ mb_vcu_device::set_cpu_tag(*device, "^" _tag);
+
+#define MCFG_MB_VCU_PALETTE(_palette_tag) \
+ mb_vcu_device::static_set_palette_tag(*device, "^" _palette_tag);
+
+#endif
diff --git a/src/devices/video/mc6845.c b/src/devices/video/mc6845.c
new file mode 100644
index 00000000000..4c1d811369c
--- /dev/null
+++ b/src/devices/video/mc6845.c
@@ -0,0 +1,1601 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Motorola MC6845 and compatible CRT controller emulation
+
+ The following variations exist that are different in
+ functionality and not just in speed rating(1):
+ * Motorola 6845, 6845-1
+ * Hitachi 46505
+ * Rockwell 6545, 6545-1 (= Synertek SY6545-1)
+ * MOS Technology 6545-1
+
+ (1) as per the document at
+ http://www.6502.org/users/andre/hwinfo/crtc/diffs.html
+
+ The various speed rated devices are identified by a letter,
+ for example M68A45, M68B45, etc.
+
+ The chip is originally designed by Hitachi, not by Motorola.
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - Change device video emulation x/y offsets when "show border color"
+ is true
+ - Support 'interlace and video' mode
+
+ - mos8563
+
+ - horizontal scroll
+ - vertical scroll
+ - bitmap modes
+ - display enable begin/end
+
+*/
+
+#include "emu.h"
+#include "mc6845.h"
+
+
+#define LOG (0)
+
+
+const device_type MC6845 = &device_creator<mc6845_device>;
+const device_type MC6845_1 = &device_creator<mc6845_1_device>;
+const device_type R6545_1 = &device_creator<r6545_1_device>;
+const device_type C6545_1 = &device_creator<c6545_1_device>;
+const device_type H46505 = &device_creator<h46505_device>;
+const device_type HD6845 = &device_creator<hd6845_device>;
+const device_type SY6545_1 = &device_creator<sy6545_1_device>;
+const device_type SY6845E = &device_creator<sy6845e_device>;
+const device_type HD6345 = &device_creator<hd6345_device>;
+const device_type AMS40041 = &device_creator<ams40041_device>;
+const device_type MOS8563 = &device_creator<mos8563_device>;
+const device_type MOS8568 = &device_creator<mos8568_device>;
+
+
+/* mode macros */
+#define MODE_TRANSPARENT ((m_mode_control & 0x08) != 0)
+#define MODE_TRANSPARENT_PHI2 ((m_mode_control & 0x88) == 0x88)
+/* FIXME: not supported yet */
+#define MODE_TRANSPARENT_BLANK ((m_mode_control & 0x88) == 0x08)
+#define MODE_UPDATE_STROBE ((m_mode_control & 0x40) != 0)
+#define MODE_CURSOR_SKEW ((m_mode_control & 0x20) != 0)
+#define MODE_DISPLAY_ENABLE_SKEW ((m_mode_control & 0x10) != 0)
+#define MODE_ROW_COLUMN_ADDRESSING ((m_mode_control & 0x04) != 0)
+#define MODE_INTERLACE_AND_VIDEO ((m_mode_control & 0x03) == 3)
+
+#define VSS_CBRATE BIT(m_vert_scroll, 5)
+#define VSS_RVS BIT(m_vert_scroll, 6)
+#define VSS_COPY BIT(m_vert_scroll, 7)
+
+#define HSS_DBL BIT(m_horiz_scroll, 4)
+#define HSS_SEMI BIT(m_horiz_scroll, 5)
+#define HSS_ATTR BIT(m_horiz_scroll, 6)
+#define HSS_TEXT BIT(m_horiz_scroll, 7)
+
+#define ATTR_COLOR (attr & 0x0f)
+#define ATTR_BACKGROUND (attr & 0x0f)
+#define ATTR_FOREGROUND (attr >> 4)
+#define ATTR_BLINK BIT(attr, 4)
+#define ATTR_UNDERLINE BIT(attr, 5)
+#define ATTR_REVERSE BIT(attr, 6)
+#define ATTR_ALTERNATE_CHARSET BIT(attr, 7)
+
+
+mc6845_device::mc6845_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_video_interface(mconfig, *this, false),
+ m_show_border_area(true),
+ m_interlace_adjust(0),
+ m_visarea_adjust_min_x(0),
+ m_visarea_adjust_max_x(0),
+ m_visarea_adjust_min_y(0),
+ m_visarea_adjust_max_y(0),
+ m_hpixels_per_column(0),
+ m_out_de_cb(*this),
+ m_out_cur_cb(*this),
+ m_out_hsync_cb(*this),
+ m_out_vsync_cb(*this)
+{
+}
+
+mc6845_device::mc6845_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MC6845, "MC6845 CRTC", tag, owner, clock, "mc6845", __FILE__),
+ device_video_interface(mconfig, *this, false),
+ m_show_border_area(true),
+ m_interlace_adjust(0),
+ m_visarea_adjust_min_x(0),
+ m_visarea_adjust_max_x(0),
+ m_visarea_adjust_min_y(0),
+ m_visarea_adjust_max_y(0),
+ m_hpixels_per_column(0),
+ m_out_de_cb(*this),
+ m_out_cur_cb(*this),
+ m_out_hsync_cb(*this),
+ m_out_vsync_cb(*this)
+{
+}
+
+
+void mc6845_device::device_post_load()
+{
+ recompute_parameters(true);
+}
+
+
+void mc6845_device::call_on_update_address(int strobe)
+{
+ if (!m_on_update_addr_changed_cb.isnull())
+ m_upd_trans_timer->adjust(attotime::zero, (m_update_addr << 8) | strobe);
+ else
+ fatalerror("M6845: transparent memory mode without handler\n");
+}
+
+
+WRITE8_MEMBER( mc6845_device::address_w )
+{
+ m_register_address_latch = data & 0x1f;
+}
+
+
+READ8_MEMBER( mc6845_device::status_r )
+{
+ UINT8 ret = 0;
+
+ /* VBLANK bit */
+ if (m_supports_status_reg_d5 && !m_line_enable_ff)
+ ret = ret | 0x20;
+
+ /* light pen latched */
+ if (m_supports_status_reg_d6 && m_light_pen_latched)
+ ret = ret | 0x40;
+
+ /* UPDATE ready */
+ if (m_supports_status_reg_d7 && m_update_ready_bit)
+ ret = ret | 0x80;
+
+ return ret;
+}
+
+
+READ8_MEMBER( mc6845_device::register_r )
+{
+ UINT8 ret = 0;
+
+ switch (m_register_address_latch)
+ {
+ case 0x0c: ret = m_supports_disp_start_addr_r ? (m_disp_start_addr >> 8) & 0xff : 0; break;
+ case 0x0d: ret = m_supports_disp_start_addr_r ? (m_disp_start_addr >> 0) & 0xff : 0; break;
+ case 0x0e: ret = (m_cursor_addr >> 8) & 0xff; break;
+ case 0x0f: ret = (m_cursor_addr >> 0) & 0xff; break;
+ case 0x10: ret = (m_light_pen_addr >> 8) & 0xff; m_light_pen_latched = false; break;
+ case 0x11: ret = (m_light_pen_addr >> 0) & 0xff; m_light_pen_latched = false; break;
+ case 0x1f:
+ if (m_supports_transparent && MODE_TRANSPARENT)
+ {
+ if(MODE_TRANSPARENT_PHI2)
+ {
+ m_update_addr++;
+ m_update_addr &= 0x3fff;
+ call_on_update_address(0);
+ }
+ else
+ {
+ /* MODE_TRANSPARENT_BLANK */
+ if(m_update_ready_bit)
+ {
+ m_update_ready_bit = false;
+ update_upd_adr_timer();
+ }
+ }
+ }
+ break;
+
+ /* all other registers are write only and return 0 */
+ default: break;
+ }
+
+ return ret;
+}
+
+
+WRITE8_MEMBER( mc6845_device::register_w )
+{
+ if (LOG) logerror("%s:M6845 reg 0x%02x = 0x%02x\n", machine().describe_context(), m_register_address_latch, data);
+
+ switch (m_register_address_latch)
+ {
+ case 0x00: m_horiz_char_total = data & 0xff; break;
+ case 0x01: m_horiz_disp = data & 0xff; break;
+ case 0x02: m_horiz_sync_pos = data & 0xff; break;
+ case 0x03: m_sync_width = data & 0xff; break;
+ case 0x04: m_vert_char_total = data & 0x7f; break;
+ case 0x05: m_vert_total_adj = data & 0x1f; break;
+ case 0x06: m_vert_disp = data & 0x7f; break;
+ case 0x07: m_vert_sync_pos = data & 0x7f; break;
+ case 0x08: m_mode_control = data & 0xff; break;
+ case 0x09: m_max_ras_addr = data & 0x1f; if (MODE_INTERLACE_AND_VIDEO) m_max_ras_addr += m_interlace_adjust; break;
+ case 0x0a: m_cursor_start_ras = data & 0x7f; break;
+ case 0x0b: m_cursor_end_ras = data & 0x1f; break;
+ case 0x0c: m_disp_start_addr = ((data & 0x3f) << 8) | (m_disp_start_addr & 0x00ff); break;
+ case 0x0d: m_disp_start_addr = ((data & 0xff) << 0) | (m_disp_start_addr & 0xff00); break;
+ case 0x0e: m_cursor_addr = ((data & 0x3f) << 8) | (m_cursor_addr & 0x00ff); break;
+ case 0x0f: m_cursor_addr = ((data & 0xff) << 0) | (m_cursor_addr & 0xff00); break;
+ case 0x10: /* read-only */ break;
+ case 0x11: /* read-only */ break;
+ case 0x12:
+ if (m_supports_transparent)
+ {
+ m_update_addr = ((data & 0x3f) << 8) | (m_update_addr & 0x00ff);
+ if(MODE_TRANSPARENT_PHI2)
+ call_on_update_address(0);
+ }
+ break;
+ case 0x13:
+ if (m_supports_transparent)
+ {
+ m_update_addr = ((data & 0xff) << 0) | (m_update_addr & 0xff00);
+ if(MODE_TRANSPARENT_PHI2)
+ call_on_update_address(0);
+ }
+ break;
+ case 0x1f:
+ if (m_supports_transparent && MODE_TRANSPARENT)
+ {
+ if(MODE_TRANSPARENT_PHI2)
+ {
+ m_update_addr++;
+ m_update_addr &= 0x3fff;
+ call_on_update_address(0);
+ }
+ else
+ {
+ /* MODE_TRANSPARENT_BLANK */
+ if(m_update_ready_bit)
+ {
+ m_update_ready_bit = false;
+ update_upd_adr_timer();
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+
+ /* display message if the Mode Control register is not zero */
+ if ((m_register_address_latch == 0x08) && (m_mode_control != 0))
+ if (!m_supports_transparent)
+ logerror("M6845: Mode Control %02X is not supported!!!\n", m_mode_control);
+
+ recompute_parameters(false);
+}
+
+
+WRITE8_MEMBER( mos8563_device::address_w )
+{
+ m_register_address_latch = data & 0x3f;
+}
+
+
+READ8_MEMBER( mos8563_device::status_r )
+{
+ UINT8 ret = m_revision;
+
+ /* VBLANK bit */
+ if (!m_line_enable_ff)
+ ret = ret | 0x20;
+
+ /* light pen latched */
+ if (m_light_pen_latched)
+ ret = ret | 0x40;
+
+ /* UPDATE ready */
+ if (m_update_ready_bit)
+ ret = ret | 0x80;
+
+ return ret;
+}
+
+
+READ8_MEMBER( mos8563_device::register_r )
+{
+ UINT8 ret = 0xff;
+
+ switch (m_register_address_latch)
+ {
+ case 0x00: ret = m_horiz_char_total; break;
+ case 0x01: ret = m_horiz_disp; break;
+ case 0x02: ret = m_horiz_sync_pos; break;
+ case 0x03: ret = m_sync_width; break;
+ case 0x04: ret = m_vert_char_total; break;
+ case 0x05: ret = m_vert_total_adj | 0xc0; break;
+ case 0x06: ret = m_vert_disp; break;
+ case 0x07: ret = m_vert_sync_pos; break;
+ case 0x08: ret = m_mode_control | 0xfc; break;
+ case 0x09: ret = m_max_ras_addr | 0xe0; break;
+ case 0x0a: ret = m_cursor_start_ras | 0x80; break;
+ case 0x0b: ret = m_cursor_end_ras | 0xe0; break;
+ case 0x0c: ret = (m_disp_start_addr >> 8) & 0xff; break;
+ case 0x0d: ret = (m_disp_start_addr >> 0) & 0xff; break;
+ case 0x0e: ret = (m_cursor_addr >> 8) & 0xff; break;
+ case 0x0f: ret = (m_cursor_addr >> 0) & 0xff; break;
+ case 0x10: ret = (m_light_pen_addr >> 8) & 0xff; m_light_pen_latched = false; break;
+ case 0x11: ret = (m_light_pen_addr >> 0) & 0xff; m_light_pen_latched = false; break;
+ case 0x12: ret = (m_update_addr >> 8) & 0xff; break;
+ case 0x13: ret = (m_update_addr >> 0) & 0xff; break;
+ case 0x14: ret = (m_attribute_addr >> 8) & 0xff; break;
+ case 0x15: ret = (m_attribute_addr >> 0) & 0xff; break;
+ case 0x16: ret = m_horiz_char; break;
+ case 0x17: ret = m_vert_char_disp | 0xe0; break;
+ case 0x18: ret = m_vert_scroll; break;
+ case 0x19: ret = m_horiz_scroll; break;
+ case 0x1a: ret = m_color; break;
+ case 0x1b: ret = m_row_addr_incr; break;
+ case 0x1c: ret = m_char_base_addr | 0x1f; break;
+ case 0x1d: ret = m_underline_ras | 0xe0; break;
+ case 0x1e: ret = m_word_count; break;
+ case 0x1f: ret = read_videoram(m_update_addr++); break;
+ case 0x20: ret = (m_block_addr >> 8) & 0xff; break;
+ case 0x21: ret = (m_block_addr >> 0) & 0xff; break;
+ case 0x22: ret = (m_de_begin >> 8) & 0xff; break;
+ case 0x23: ret = (m_de_begin >> 0) & 0xff; break;
+ case 0x24: ret = m_dram_refresh | 0xf0; break;
+ case 0x25: ret = m_sync_polarity | 0x3f; break;
+ }
+
+ return ret;
+}
+
+
+WRITE8_MEMBER( mos8563_device::register_w )
+{
+ if (LOG) logerror("%s:MOS8563 reg 0x%02x = 0x%02x\n", machine().describe_context(), m_register_address_latch, data);
+
+ switch (m_register_address_latch)
+ {
+ case 0x00: m_horiz_char_total = data & 0xff; break;
+ case 0x01: m_horiz_disp = data & 0xff; break;
+ case 0x02: m_horiz_sync_pos = data & 0xff; break;
+ case 0x03: m_sync_width = data & 0xff; break;
+ case 0x04: m_vert_char_total = data & 0xff; break;
+ case 0x05: m_vert_total_adj = data & 0x1f; break;
+ case 0x06: m_vert_disp = data & 0xff; break;
+ case 0x07: m_vert_sync_pos = data & 0xff; break;
+ case 0x08: m_mode_control = data & 0x03; break;
+ case 0x09: m_max_ras_addr = data & 0x1f; break;
+ case 0x0a: m_cursor_start_ras = data & 0x7f; break;
+ case 0x0b: m_cursor_end_ras = data & 0x1f; break;
+ case 0x0c: m_disp_start_addr = ((data & 0xff) << 8) | (m_disp_start_addr & 0x00ff); break;
+ case 0x0d: m_disp_start_addr = ((data & 0xff) << 0) | (m_disp_start_addr & 0xff00); break;
+ case 0x0e: m_cursor_addr = ((data & 0xff) << 8) | (m_cursor_addr & 0x00ff); break;
+ case 0x0f: m_cursor_addr = ((data & 0xff) << 0) | (m_cursor_addr & 0xff00); break;
+ case 0x10: /* read-only */ break;
+ case 0x11: /* read-only */ break;
+ case 0x12: m_update_addr = ((data & 0xff) << 8) | (m_update_addr & 0x00ff); break;
+ case 0x13: m_update_addr = ((data & 0xff) << 0) | (m_update_addr & 0xff00); break;
+ case 0x14: m_attribute_addr = ((data & 0xff) << 8) | (m_attribute_addr & 0x00ff); break;
+ case 0x15: m_attribute_addr = ((data & 0xff) << 0) | (m_attribute_addr & 0xff00); break;
+ case 0x16: m_horiz_char = data & 0xff; break;
+ case 0x17: m_vert_char_disp = data & 0x1f; break;
+ case 0x18: m_vert_scroll = data & 0xff; break;
+ case 0x19:
+ {
+ int dbl = HSS_DBL;
+ m_horiz_scroll = data & 0xff;
+ if (dbl && !HSS_DBL) set_clock(m_clock << 1);
+ if (!dbl && HSS_DBL) set_clock(m_clock >> 1);
+ break;
+ }
+ case 0x1a: m_color = data & 0xff; break;
+ case 0x1b: m_row_addr_incr = data & 0xff; break;
+ case 0x1c: m_char_base_addr = data & 0xe0; break;
+ case 0x1d: m_underline_ras = data & 0x1f; break;
+ case 0x1e:
+ m_word_count = data & 0xff;
+ m_update_ready_bit = 0;
+ m_block_copy_timer->adjust( attotime::from_ticks( 1, m_clock ) );
+ break;
+ case 0x1f:
+ m_data = data & 0xff;
+ write_videoram(m_update_addr++, m_data);
+ break;
+ case 0x20: m_block_addr = ((data & 0xff) << 8) | (m_block_addr & 0x00ff); break;
+ case 0x21: m_block_addr = ((data & 0xff) << 0) | (m_block_addr & 0xff00); break;
+ case 0x22: m_de_begin = ((data & 0xff) << 8) | (m_de_begin & 0x00ff); break;
+ case 0x23: m_de_begin = ((data & 0xff) << 0) | (m_de_begin & 0xff00); break;
+ case 0x24: m_dram_refresh = data & 0x0f; break;
+ case 0x25: m_sync_polarity = data & 0xc0; break;
+ }
+
+ recompute_parameters(false);
+}
+
+
+inline UINT8 mos8563_device::read_videoram(offs_t offset)
+{
+ return space(AS_0).read_byte(offset);
+}
+
+inline void mos8563_device::write_videoram(offs_t offset, UINT8 data)
+{
+ space(AS_0).write_byte(offset, data);
+}
+
+
+READ_LINE_MEMBER( mc6845_device::de_r )
+{
+ return m_de;
+}
+
+
+READ_LINE_MEMBER( mc6845_device::cursor_r )
+{
+ return m_cur;
+}
+
+
+READ_LINE_MEMBER( mc6845_device::hsync_r )
+{
+ return m_hsync;
+}
+
+
+READ_LINE_MEMBER( mc6845_device::vsync_r )
+{
+ return m_vsync;
+}
+
+
+void mc6845_device::recompute_parameters(bool postload)
+{
+ UINT16 hsync_on_pos, hsync_off_pos, vsync_on_pos, vsync_off_pos;
+
+ UINT16 video_char_height = m_max_ras_addr + 1; // fix garbage at the bottom of the screen (eg victor9k)
+ // Would be useful for 'interlace and video' mode support...
+ // UINT16 frame_char_height = (MODE_INTERLACE_AND_VIDEO ? m_max_ras_addr / 2 : m_max_ras_addr) + 1;
+
+ /* compute the screen sizes */
+ UINT16 horiz_pix_total = (m_horiz_char_total + 1) * m_hpixels_per_column;
+ UINT16 vert_pix_total = (m_vert_char_total + 1) * video_char_height + m_vert_total_adj;
+
+ /* determine the visible area, avoid division by 0 */
+ UINT16 max_visible_x = m_horiz_disp * m_hpixels_per_column - 1;
+ UINT16 max_visible_y = m_vert_disp * video_char_height - 1;
+
+ /* determine the syncing positions */
+ UINT8 horiz_sync_char_width = m_sync_width & 0x0f;
+ UINT8 vert_sync_pix_width = m_supports_vert_sync_width ? (m_sync_width >> 4) & 0x0f : 0x10;
+
+ if (horiz_sync_char_width == 0)
+ horiz_sync_char_width = 0x10;
+
+ if (vert_sync_pix_width == 0)
+ vert_sync_pix_width = 0x10;
+
+ /* determine the transparent update cycle time, 1 update every 4 character clocks */
+ m_upd_time = attotime::from_hz(m_clock) * (4 * m_hpixels_per_column);
+
+ hsync_on_pos = m_horiz_sync_pos * m_hpixels_per_column;
+ hsync_off_pos = hsync_on_pos + (horiz_sync_char_width * m_hpixels_per_column);
+ vsync_on_pos = m_vert_sync_pos * video_char_height;
+ vsync_off_pos = vsync_on_pos + vert_sync_pix_width;
+
+ // the Commodore PET computers have a non-standard 20kHz monitor which
+ // requires a wider HSYNC pulse that extends past the scanline width
+ if (hsync_off_pos > horiz_pix_total)
+ hsync_off_pos = horiz_pix_total;
+
+ if (vsync_on_pos > vert_pix_total)
+ vsync_on_pos = vert_pix_total;
+
+ if (vsync_off_pos > vert_pix_total)
+ vsync_off_pos = vert_pix_total;
+
+ /* update only if screen parameters changed, unless we are coming here after loading the saved state */
+ if (postload ||
+ (horiz_pix_total != m_horiz_pix_total) || (vert_pix_total != m_vert_pix_total) ||
+ (max_visible_x != m_max_visible_x) || (max_visible_y != m_max_visible_y) ||
+ (hsync_on_pos != m_hsync_on_pos) || (vsync_on_pos != m_vsync_on_pos) ||
+ (hsync_off_pos != m_hsync_off_pos) || (vsync_off_pos != m_vsync_off_pos))
+ {
+ /* update the screen if we have valid data */
+ if ((horiz_pix_total > 0) && (max_visible_x < horiz_pix_total) &&
+ (vert_pix_total > 0) && (max_visible_y < vert_pix_total) &&
+ (hsync_on_pos <= horiz_pix_total) && (vsync_on_pos <= vert_pix_total) &&
+ (hsync_on_pos != hsync_off_pos))
+ {
+ rectangle visarea;
+
+ attoseconds_t refresh = HZ_TO_ATTOSECONDS(m_clock) * (m_horiz_char_total + 1) * vert_pix_total;
+
+ // This doubles the vertical resolution, required for 'interlace and video' mode support.
+ // Tested and works for super80v, which was designed with this in mind (choose green or monochrome colour in config switches).
+ // However it breaks some other drivers (apricot,a6809,victor9k,bbc(mode7)).
+ // So, it is commented out for now.
+ // Also, the mode-register change needs to be added to the changed-parameter tests above.
+ if (MODE_INTERLACE_AND_VIDEO)
+ {
+ //max_visible_y *= 2;
+ //vert_pix_total *= 2;
+ }
+
+ if(m_show_border_area)
+ visarea.set(0, horiz_pix_total-1, 0, vert_pix_total-1);
+ else
+ visarea.set(0 + m_visarea_adjust_min_x, max_visible_x + m_visarea_adjust_max_x, 0 + m_visarea_adjust_min_y, max_visible_y + m_visarea_adjust_max_y);
+
+ if (LOG) logerror("M6845 config screen: HTOTAL: 0x%x VTOTAL: 0x%x MAX_X: 0x%x MAX_Y: 0x%x HSYNC: 0x%x-0x%x VSYNC: 0x%x-0x%x Freq: %ffps\n",
+ horiz_pix_total, vert_pix_total, max_visible_x, max_visible_y, hsync_on_pos, hsync_off_pos - 1, vsync_on_pos, vsync_off_pos - 1, 1 / ATTOSECONDS_TO_DOUBLE(refresh));
+
+ if ( m_screen != NULL )
+ m_screen->configure(horiz_pix_total, vert_pix_total, visarea, refresh);
+
+ m_has_valid_parameters = true;
+ }
+ else
+ m_has_valid_parameters = false;
+
+ m_horiz_pix_total = horiz_pix_total;
+ m_vert_pix_total = vert_pix_total;
+ m_max_visible_x = max_visible_x;
+ m_max_visible_y = max_visible_y;
+ m_hsync_on_pos = hsync_on_pos;
+ m_hsync_off_pos = hsync_off_pos;
+ m_vsync_on_pos = vsync_on_pos;
+ m_vsync_off_pos = vsync_off_pos;
+ }
+}
+
+
+void mc6845_device::update_counters()
+{
+ m_character_counter = m_line_timer->elapsed( ).as_ticks( m_clock );
+
+ if ( m_hsync_off_timer ->enabled( ) )
+ {
+ m_hsync_width_counter = m_hsync_off_timer ->elapsed( ).as_ticks( m_clock );
+ }
+}
+
+
+void mc6845_device::set_de(int state)
+{
+ if (m_de != state)
+ {
+ m_de = state;
+
+ if (m_de)
+ {
+ /* If the upd_adr_timer was running, cancel it */
+ m_upd_adr_timer->adjust(attotime::never);
+ }
+ else
+ {
+ /* if transparent update was requested fire the update timer */
+ if(!m_update_ready_bit)
+ update_upd_adr_timer();
+ }
+
+ m_out_de_cb(m_de);
+ }
+}
+
+
+void mc6845_device::set_hsync(int state)
+{
+ if (m_hsync != state)
+ {
+ m_hsync = state;
+ m_out_hsync_cb(m_hsync);
+ }
+}
+
+
+void mc6845_device::set_vsync(int state)
+{
+ if (m_vsync != state)
+ {
+ m_vsync = state;
+ m_out_vsync_cb(m_vsync);
+ }
+}
+
+
+void mc6845_device::set_cur(int state)
+{
+ if (m_cur != state)
+ {
+ m_cur = state;
+ m_out_cur_cb(m_cur);
+ }
+}
+
+
+void mc6845_device::update_upd_adr_timer()
+{
+ if (! m_de && m_supports_transparent)
+ m_upd_adr_timer->adjust(m_upd_time);
+}
+
+
+void mc6845_device::handle_line_timer()
+{
+ int new_vsync = m_vsync;
+
+ m_character_counter = 0;
+ m_cursor_x = -1;
+
+ /* Check if VSYNC is active */
+ if ( m_vsync_ff )
+ {
+ UINT8 vsync_width = m_supports_vert_sync_width ? (m_sync_width >> 4) & 0x0f : 0;
+
+ m_vsync_width_counter = ( m_vsync_width_counter + 1 ) & 0x0F;
+
+ /* Check if we've reached end of VSYNC */
+ if ( m_vsync_width_counter == vsync_width )
+ {
+ m_vsync_ff = 0;
+
+ new_vsync = FALSE;
+ }
+ }
+
+ // For rudimentary 'interlace and video' support, m_raster_counter increments by 1 rather than the correct 2.
+ // The correct test would be:
+ // if ( m_raster_counter == (MODE_INTERLACE_AND_VIDEO ? m_max_ras_addr + 1 : m_max_ras_addr) )
+ if ( m_raster_counter == m_max_ras_addr )
+ {
+ /* Check if we have reached the end of the vertical area */
+ if ( m_line_counter == m_vert_char_total )
+ {
+ m_adjust_counter = 0;
+ m_adjust_active = 1;
+ }
+
+ m_raster_counter = 0;
+ m_line_counter = ( m_line_counter + 1 ) & 0x7F;
+ m_line_address = ( m_line_address + m_horiz_disp ) & 0x3fff;
+
+ /* Check if we've reached the end of active display */
+ if ( m_line_counter == m_vert_disp )
+ {
+ m_line_enable_ff = false;
+ }
+
+ /* Check if VSYNC should be enabled */
+ if ( m_line_counter == m_vert_sync_pos )
+ {
+ m_vsync_width_counter = 0;
+ m_vsync_ff = 1;
+
+ new_vsync = TRUE;
+ }
+ }
+ else
+ {
+ // For rudimentary 'interlace and video' support, m_raster_counter increments by 1 rather than the correct 2.
+ // m_raster_counter = ( m_raster_counter + (MODE_INTERLACE_AND_VIDEO ? 2 : 1) ) & 0x1F;
+ m_raster_counter = ( m_raster_counter + 1 ) & 0x1F;
+ }
+
+ if ( m_adjust_active )
+ {
+ /* Check if we have reached the end of a full cycle */
+ if ( m_adjust_counter == m_vert_total_adj )
+ {
+ m_adjust_active = 0;
+ m_raster_counter = 0;
+ m_line_counter = 0;
+ m_line_address = m_disp_start_addr;
+ m_line_enable_ff = true;
+ /* also update the cursor state now */
+ update_cursor_state();
+
+ if (m_screen != NULL)
+ m_screen->reset_origin();
+ }
+ else
+ {
+ m_adjust_counter = ( m_adjust_counter + 1 ) & 0x1F;
+ }
+ }
+
+ if ( m_line_enable_ff )
+ {
+ /* Schedule DE off signal change */
+ m_de_off_timer->adjust(attotime::from_ticks( m_horiz_disp, m_clock ));
+
+ /* Is cursor visible on this line? */
+ if ( m_cursor_state &&
+ (m_raster_counter >= (m_cursor_start_ras & 0x1f)) &&
+ (m_raster_counter <= m_cursor_end_ras) &&
+ (m_cursor_addr >= m_line_address) &&
+ (m_cursor_addr < (m_line_address + m_horiz_disp)) )
+ {
+ m_cursor_x = m_cursor_addr - m_line_address;
+
+ /* Schedule CURSOR ON signal */
+ m_cur_on_timer->adjust( attotime::from_ticks( m_cursor_x, m_clock ) );
+ }
+ }
+
+ /* Schedule HSYNC on signal */
+ m_hsync_on_timer->adjust( attotime::from_ticks( m_horiz_sync_pos, m_clock ) );
+
+ /* Schedule our next callback */
+ m_line_timer->adjust( attotime::from_ticks( m_horiz_char_total + 1, m_clock ) );
+
+ /* Set VSYNC and DE signals */
+ set_vsync( new_vsync );
+ set_de( m_line_enable_ff ? TRUE : FALSE );
+}
+
+
+void mc6845_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_LINE:
+ handle_line_timer();
+ break;
+
+ case TIMER_DE_OFF:
+ set_de( FALSE );
+ break;
+
+ case TIMER_CUR_ON:
+ set_cur( TRUE );
+
+ /* Schedule CURSOR off signal */
+ m_cur_off_timer->adjust( attotime::from_ticks( 1, m_clock ) );
+ break;
+
+ case TIMER_CUR_OFF:
+ set_cur( FALSE );
+ break;
+
+ case TIMER_HSYNC_ON:
+ {
+ UINT8 hsync_width = ( m_sync_width & 0x0f ) ? ( m_sync_width & 0x0f ) : 0x10;
+
+ m_hsync_width_counter = 0;
+ set_hsync( TRUE );
+
+ /* Schedule HSYNC off signal */
+ m_hsync_off_timer->adjust( attotime::from_ticks( hsync_width, m_clock ) );
+ }
+ break;
+
+ case TIMER_HSYNC_OFF:
+ set_hsync( FALSE );
+ break;
+
+ case TIMER_LIGHT_PEN_LATCH:
+ m_light_pen_addr = get_ma();
+ m_light_pen_latched = true;
+ break;
+
+ case TIMER_UPD_ADR:
+ /* fire a update address strobe */
+ call_on_update_address(0);
+ break;
+
+ case TIMER_UPD_TRANS:
+ {
+ int addr = (param >> 8);
+ int strobe = (param & 0xff);
+
+ /* call the callback function -- we know it exists */
+ m_on_update_addr_changed_cb(addr, strobe);
+
+ if(!m_update_ready_bit && MODE_TRANSPARENT_BLANK)
+ {
+ m_update_addr++;
+ m_update_addr &= 0x3fff;
+ m_update_ready_bit = true;
+ }
+ }
+ break;
+
+ }
+}
+
+
+void mos8563_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_BLOCK_COPY:
+ {
+ UINT8 data = VSS_COPY ? read_videoram(m_block_addr++) : m_data;
+
+ write_videoram(m_update_addr++, data);
+
+ if (--m_word_count)
+ {
+ m_block_copy_timer->adjust( attotime::from_ticks( 1, m_clock ) );
+ }
+ else
+ {
+ m_update_ready_bit = 1;
+ }
+ break;
+ }
+ default:
+ mc6845_device::device_timer(timer, id, param, ptr);
+ break;
+ }
+}
+
+
+UINT16 mc6845_device::get_ma()
+{
+ update_counters();
+
+ return ( m_line_address + m_character_counter ) & 0x3fff;
+}
+
+
+UINT8 mc6845_device::get_ra()
+{
+ return m_raster_counter;
+}
+
+
+void mc6845_device::assert_light_pen_input()
+{
+ /* compute the pixel coordinate of the NEXT character -- this is when the light pen latches */
+ /* set the timer that will latch the display address into the light pen registers */
+ m_light_pen_latch_timer->adjust(attotime::from_ticks( 1, m_clock ));
+}
+
+
+void mc6845_device::set_clock(int clock)
+{
+ /* validate arguments */
+ assert(clock > 0);
+
+ if (clock != m_clock)
+ {
+ m_clock = clock;
+ recompute_parameters(true);
+ }
+}
+
+
+void mc6845_device::set_hpixels_per_column(int hpixels_per_column)
+{
+ /* validate arguments */
+ assert(hpixels_per_column > 0);
+
+ if (hpixels_per_column != m_hpixels_per_column)
+ {
+ m_hpixels_per_column = hpixels_per_column;
+ recompute_parameters(false);
+ }
+}
+
+
+void mc6845_device::update_cursor_state()
+{
+ /* save and increment cursor counter */
+ UINT8 last_cursor_blink_count = m_cursor_blink_count;
+ m_cursor_blink_count = m_cursor_blink_count + 1;
+
+ /* switch on cursor blinking mode */
+ switch (m_cursor_start_ras & 0x60)
+ {
+ /* always on */
+ case 0x00: m_cursor_state = true; break;
+
+ /* always off */
+ default:
+ case 0x20: m_cursor_state = false; break;
+
+ /* fast blink */
+ case 0x40:
+ if ((last_cursor_blink_count & 0x10) != (m_cursor_blink_count & 0x10))
+ m_cursor_state = !m_cursor_state;
+ break;
+
+ /* slow blink */
+ case 0x60:
+ if ((last_cursor_blink_count & 0x20) != (m_cursor_blink_count & 0x20))
+ m_cursor_state = !m_cursor_state;
+ break;
+ }
+}
+
+
+UINT8 mc6845_device::draw_scanline(int y, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ /* compute the current raster line */
+ UINT8 ra = y % (m_max_ras_addr + 1);
+
+ /* check if the cursor is visible and is on this scanline */
+ int cursor_visible = m_cursor_state &&
+ (ra >= (m_cursor_start_ras & 0x1f)) &&
+ (ra <= m_cursor_end_ras) &&
+ (m_cursor_addr >= m_current_disp_addr) &&
+ (m_cursor_addr < (m_current_disp_addr + m_horiz_disp));
+
+ /* compute the cursor X position, or -1 if not visible */
+ INT8 cursor_x = cursor_visible ? (m_cursor_addr - m_current_disp_addr) : -1;
+ int de = (y < m_max_visible_y) ? 1 : 0;
+ int vbp = m_vert_pix_total - m_vsync_off_pos;
+ if (vbp < 0) vbp = 0;
+ int hbp = m_horiz_pix_total - m_hsync_off_pos;
+ if (hbp < 0) hbp = 0;
+
+ /* call the external system to draw it */
+ if (MODE_ROW_COLUMN_ADDRESSING)
+ {
+ UINT8 cc = 0;
+ UINT8 cr = y / (m_max_ras_addr + 1);
+ UINT16 ma = (cr << 8) | cc;
+
+ m_update_row_cb(bitmap, cliprect, ma, ra, y, m_horiz_disp, cursor_x, de, hbp, vbp);
+ }
+ else
+ {
+ m_update_row_cb(bitmap, cliprect, m_current_disp_addr, ra, y, m_horiz_disp, cursor_x, de, hbp, vbp);
+ }
+
+ /* update MA if the last raster address */
+ if (ra == m_max_ras_addr)
+ m_current_disp_addr = (m_current_disp_addr + m_horiz_disp) & 0x3fff;
+
+ return ra;
+}
+
+
+UINT32 mc6845_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ assert(bitmap.valid());
+
+ if (m_has_valid_parameters)
+ {
+ assert(!m_update_row_cb.isnull());
+
+ /* call the set up function if any */
+ if (!m_begin_update_cb.isnull())
+ m_begin_update_cb(bitmap, cliprect);
+
+ if (cliprect.min_y == 0)
+ {
+ /* read the start address at the beginning of the frame */
+ m_current_disp_addr = m_disp_start_addr;
+ }
+
+ /* for each row in the visible region */
+ for (UINT16 y = cliprect.min_y; y <= cliprect.max_y; y++)
+ {
+ this->draw_scanline(y, bitmap, cliprect);
+ }
+
+ /* call the tear down function if any */
+ if (!m_end_update_cb.isnull())
+ m_end_update_cb(bitmap, cliprect);
+ }
+ else
+ {
+ if (LOG) logerror("M6845: Invalid screen parameters - display disabled!!!\n");
+ }
+
+ return 0;
+}
+
+
+void mc6845_device::device_start()
+{
+ assert(m_clock > 0);
+ assert(m_hpixels_per_column > 0);
+
+ /* resolve callbacks */
+ m_out_de_cb.resolve_safe();
+ m_out_cur_cb.resolve_safe();
+ m_out_hsync_cb.resolve_safe();
+ m_out_vsync_cb.resolve_safe();
+
+ /* bind delegates */
+ m_begin_update_cb.bind_relative_to(*owner());
+ m_update_row_cb.bind_relative_to(*owner());
+ m_end_update_cb.bind_relative_to(*owner());
+ m_on_update_addr_changed_cb.bind_relative_to(*owner());
+
+ /* create the timers */
+ m_line_timer = timer_alloc(TIMER_LINE);
+ m_de_off_timer = timer_alloc(TIMER_DE_OFF);
+ m_cur_on_timer = timer_alloc(TIMER_CUR_ON);
+ m_cur_off_timer = timer_alloc(TIMER_CUR_OFF);
+ m_hsync_on_timer = timer_alloc(TIMER_HSYNC_ON);
+ m_hsync_off_timer = timer_alloc(TIMER_HSYNC_OFF);
+ m_light_pen_latch_timer = timer_alloc(TIMER_LIGHT_PEN_LATCH);
+ m_upd_adr_timer = timer_alloc(TIMER_UPD_ADR);
+ m_upd_trans_timer = timer_alloc(TIMER_UPD_TRANS);
+
+ /* Use some large startup values */
+ m_horiz_char_total = 0xff;
+ m_max_ras_addr = 0x1f;
+ m_vert_char_total = 0x7f;
+
+ m_supports_disp_start_addr_r = true;
+ m_supports_vert_sync_width = false;
+ m_supports_status_reg_d5 = false;
+ m_supports_status_reg_d6 = false;
+ m_supports_status_reg_d7 = false;
+ m_supports_transparent = false;
+ m_has_valid_parameters = false;
+ m_line_enable_ff = false;
+ m_vsync_ff = 0;
+ m_raster_counter = 0;
+ m_adjust_active = 0;
+ m_horiz_sync_pos = 1;
+ m_vert_sync_pos = 1;
+ m_de = 0;
+ m_sync_width = 1;
+ m_vert_disp = 0;
+ m_horiz_pix_total = m_vert_pix_total = 0;
+ m_max_visible_x = m_max_visible_y = 0;
+ m_hsync_on_pos = m_vsync_on_pos = 0;
+ m_hsync_off_pos = m_vsync_off_pos = 0;
+ m_vsync = m_hsync = 0;
+ m_cur = 0;
+ m_line_counter = 0;
+ m_horiz_disp = m_vert_disp = 0;
+ m_vert_sync_pos = 0;
+ m_vert_total_adj = 0;
+ m_cursor_start_ras = m_cursor_end_ras = m_cursor_addr = 0;
+ m_cursor_blink_count = 0;
+ m_cursor_state = 0;
+ m_update_ready_bit = 0;
+ m_line_address = 0;
+ m_current_disp_addr = 0;
+ m_disp_start_addr = 0;
+
+ save_item(NAME(m_show_border_area));
+ save_item(NAME(m_visarea_adjust_min_x));
+ save_item(NAME(m_visarea_adjust_max_x));
+ save_item(NAME(m_visarea_adjust_min_y));
+ save_item(NAME(m_visarea_adjust_max_y));
+ save_item(NAME(m_hpixels_per_column));
+ save_item(NAME(m_register_address_latch));
+ save_item(NAME(m_horiz_char_total));
+ save_item(NAME(m_horiz_disp));
+ save_item(NAME(m_horiz_sync_pos));
+ save_item(NAME(m_sync_width));
+ save_item(NAME(m_vert_char_total));
+ save_item(NAME(m_vert_total_adj));
+ save_item(NAME(m_vert_disp));
+ save_item(NAME(m_vert_sync_pos));
+ save_item(NAME(m_mode_control));
+ save_item(NAME(m_max_ras_addr));
+ save_item(NAME(m_cursor_start_ras));
+ save_item(NAME(m_cursor_end_ras));
+ save_item(NAME(m_disp_start_addr));
+ save_item(NAME(m_cursor_addr));
+ save_item(NAME(m_light_pen_addr));
+ save_item(NAME(m_light_pen_latched));
+ save_item(NAME(m_cursor_state));
+ save_item(NAME(m_cursor_blink_count));
+ save_item(NAME(m_update_addr));
+ save_item(NAME(m_update_ready_bit));
+ save_item(NAME(m_cur));
+ save_item(NAME(m_hsync));
+ save_item(NAME(m_vsync));
+ save_item(NAME(m_de));
+ save_item(NAME(m_character_counter));
+ save_item(NAME(m_hsync_width_counter));
+ save_item(NAME(m_line_counter));
+ save_item(NAME(m_raster_counter));
+ save_item(NAME(m_adjust_counter));
+ save_item(NAME(m_vsync_width_counter));
+ save_item(NAME(m_line_enable_ff));
+ save_item(NAME(m_vsync_ff));
+ save_item(NAME(m_adjust_active));
+ save_item(NAME(m_line_address));
+ save_item(NAME(m_cursor_x));
+ save_item(NAME(m_has_valid_parameters));
+}
+
+
+void mc6845_1_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_supports_disp_start_addr_r = true;
+ m_supports_vert_sync_width = true;
+ m_supports_status_reg_d5 = false;
+ m_supports_status_reg_d6 = false;
+ m_supports_status_reg_d7 = false;
+ m_supports_transparent = false;
+}
+
+
+void c6545_1_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_supports_disp_start_addr_r = false;
+ m_supports_vert_sync_width = true;
+ m_supports_status_reg_d5 = true;
+ m_supports_status_reg_d6 = true;
+ m_supports_status_reg_d7 = false;
+ m_supports_transparent = false;
+}
+
+
+void r6545_1_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_supports_disp_start_addr_r = false;
+ m_supports_vert_sync_width = true;
+ m_supports_status_reg_d5 = true;
+ m_supports_status_reg_d6 = true;
+ m_supports_status_reg_d7 = true;
+ m_supports_transparent = true;
+}
+
+
+void h46505_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_supports_disp_start_addr_r = false;
+ m_supports_vert_sync_width = false;
+ m_supports_status_reg_d5 = false;
+ m_supports_status_reg_d6 = false;
+ m_supports_status_reg_d7 = false;
+ m_supports_transparent = false;
+}
+
+
+void hd6845_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_supports_disp_start_addr_r = false;
+ m_supports_vert_sync_width = true;
+ m_supports_status_reg_d5 = false;
+ m_supports_status_reg_d6 = false;
+ m_supports_status_reg_d7 = false;
+ m_supports_transparent = false;
+}
+
+
+void sy6545_1_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_supports_disp_start_addr_r = false;
+ m_supports_vert_sync_width = true;
+ m_supports_status_reg_d5 = true;
+ m_supports_status_reg_d6 = true;
+ m_supports_status_reg_d7 = true;
+ m_supports_transparent = true;
+}
+
+
+void sy6845e_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_supports_disp_start_addr_r = false;
+ m_supports_vert_sync_width = true;
+ m_supports_status_reg_d5 = true;
+ m_supports_status_reg_d6 = true;
+ m_supports_status_reg_d7 = true;
+ m_supports_transparent = true;
+}
+
+
+void hd6345_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_supports_disp_start_addr_r = true;
+ m_supports_vert_sync_width = true;
+ m_supports_status_reg_d5 = true;
+ m_supports_status_reg_d6 = true;
+ m_supports_status_reg_d7 = true;
+ m_supports_transparent = true;
+}
+
+
+void ams40041_device::device_start()
+{
+ mc6845_device::device_start();
+
+ m_horiz_char_total = 113;
+ m_horiz_disp = 80;
+ m_horiz_sync_pos = 90;
+ m_sync_width = 10;
+ m_vert_char_total = 127;
+ m_vert_total_adj = 6;
+ m_vert_disp = 100;
+ m_vert_sync_pos = 112;
+ m_mode_control = 2;
+
+ m_supports_disp_start_addr_r = false;
+ m_supports_vert_sync_width = false;
+ m_supports_status_reg_d5 = false;
+ m_supports_status_reg_d6 = false;
+ m_supports_status_reg_d7 = false;
+ m_supports_transparent = false;
+}
+
+
+void mos8563_device::device_start()
+{
+ mc6845_device::device_start();
+
+ /* create the timers */
+ m_block_copy_timer = timer_alloc(TIMER_BLOCK_COPY);
+
+ m_supports_status_reg_d5 = true;
+ m_supports_status_reg_d6 = true;
+ m_supports_status_reg_d7 = true;
+ m_update_ready_bit = 1;
+
+ // default update_row delegate
+ m_update_row_cb = mc6845_update_row_delegate(FUNC(mos8563_device::vdc_update_row), this);
+
+ m_char_blink_state = false;
+ m_char_blink_count = 0;
+ m_attribute_addr = 0;
+ m_horiz_char = 0;
+ m_vert_char_disp = 0;
+ m_vert_scroll = 0;
+ m_horiz_scroll = 0;
+ m_color = 0;
+ m_row_addr_incr = 0;
+ m_char_base_addr = 0;
+ m_underline_ras = 0;
+ m_word_count = 0;
+ m_data = 0;
+ m_block_addr = 0;
+ m_de_begin = 0;
+ m_dram_refresh = 0;
+ m_sync_polarity = 0;
+
+ m_revision = 1;
+
+ // initialize video RAM
+ UINT8 data = 0xff;
+
+ for (offs_t offset = 0; offset < 0x10000; offset++)
+ {
+ write_videoram(offset, data);
+ data ^= 0xff;
+ }
+
+ save_item(NAME(m_char_buffer));
+ save_item(NAME(m_attr_buffer));
+ save_item(NAME(m_attribute_addr));
+ save_item(NAME(m_horiz_char));
+ save_item(NAME(m_vert_char_disp));
+ save_item(NAME(m_vert_scroll));
+ save_item(NAME(m_horiz_scroll));
+ save_item(NAME(m_color));
+ save_item(NAME(m_row_addr_incr));
+ save_item(NAME(m_char_base_addr));
+ save_item(NAME(m_underline_ras));
+ save_item(NAME(m_word_count));
+ save_item(NAME(m_data));
+ save_item(NAME(m_block_addr));
+ save_item(NAME(m_de_begin));
+ save_item(NAME(m_dram_refresh));
+ save_item(NAME(m_sync_polarity));
+ save_item(NAME(m_revision));
+}
+
+
+void mos8568_device::device_start()
+{
+ mos8563_device::device_start();
+}
+
+
+void mc6845_device::device_reset()
+{
+ /* internal registers other than status remain unchanged, all outputs go low */
+ m_out_de_cb(false);
+
+ m_out_hsync_cb(false);
+
+ m_out_vsync_cb(false);
+
+ if (!m_line_timer->enabled())
+ m_line_timer->adjust(attotime::from_ticks(m_horiz_char_total + 1, m_clock));
+
+ m_light_pen_latched = false;
+
+ m_cursor_addr = 0;
+ m_line_address = 0;
+ m_horiz_disp = 0;
+ m_cursor_x = 0;
+ m_mode_control = 0;
+ m_register_address_latch = 0;
+ m_update_addr = 0;
+ m_light_pen_addr = 0;
+}
+
+
+void r6545_1_device::device_reset() { mc6845_device::device_reset(); }
+void h46505_device::device_reset() { mc6845_device::device_reset(); }
+void mc6845_1_device::device_reset() { mc6845_device::device_reset(); }
+void hd6845_device::device_reset() { mc6845_device::device_reset(); }
+void c6545_1_device::device_reset() { mc6845_device::device_reset(); }
+void sy6545_1_device::device_reset() { mc6845_device::device_reset(); }
+void sy6845e_device::device_reset() { mc6845_device::device_reset(); }
+void hd6345_device::device_reset() { mc6845_device::device_reset(); }
+void ams40041_device::device_reset() { mc6845_device::device_reset(); }
+
+void mos8563_device::device_reset()
+{
+ mc6845_device::device_reset();
+
+ m_sync_polarity = 0xc0;
+}
+
+void mos8568_device::device_reset() { mos8563_device::device_reset(); }
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *mos8563_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_0: return &m_videoram_space_config;
+ default: return NULL;
+ }
+}
+
+// default address maps
+static ADDRESS_MAP_START( mos8563_videoram_map, AS_0, 8, mos8563_device )
+ AM_RANGE(0x0000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+
+r6545_1_device::r6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, R6545_1, "R6545-1 CRTC", tag, owner, clock, "r6545_1", __FILE__)
+{
+}
+
+
+h46505_device::h46505_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, H46505, "H46505 CRTC", tag, owner, clock, "h46505", __FILE__)
+{
+}
+
+
+mc6845_1_device::mc6845_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, MC6845_1, "MC6845-1 CRTC", tag, owner, clock, "mc6845_1", __FILE__)
+{
+}
+
+
+hd6845_device::hd6845_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, HD6845, "HD6845 CRTC", tag, owner, clock, "hd6845", __FILE__)
+{
+}
+
+
+c6545_1_device::c6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, C6545_1, "C6545-1 CRTC", tag, owner, clock, "c6545_1", __FILE__)
+{
+}
+
+
+sy6545_1_device::sy6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, SY6545_1, "SY6545-1 CRTC", tag, owner, clock, "sy6545_1", __FILE__)
+{
+}
+
+
+sy6845e_device::sy6845e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, SY6845E, "SY6845E CRTC", tag, owner, clock, "sy6845e", __FILE__)
+{
+}
+
+
+hd6345_device::hd6345_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, HD6345, "HD6345 CRTC", tag, owner, clock, "hd6345", __FILE__)
+{
+}
+
+
+ams40041_device::ams40041_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, AMS40041, "AMS40041 CRTC", tag, owner, clock, "ams40041", __FILE__)
+{
+}
+
+
+mos8563_device::mos8563_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : mc6845_device(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_memory_interface(mconfig, *this),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(mos8563_videoram_map)),
+ m_palette(*this, "palette")
+{
+ set_clock_scale(1.0/8);
+}
+
+
+mos8563_device::mos8563_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6845_device(mconfig, MOS8563, "MOS8563", tag, owner, clock, "mos8563", __FILE__),
+ device_memory_interface(mconfig, *this),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(mos8563_videoram_map)),
+ m_palette(*this, "palette")
+{
+ set_clock_scale(1.0/8);
+}
+
+
+mos8568_device::mos8568_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mos8563_device(mconfig, MOS8568, "MOS8568", tag, owner, clock, "mos8568", __FILE__)
+{
+}
+
+
+static MACHINE_CONFIG_FRAGMENT(mos8563)
+ MCFG_PALETTE_ADD("palette", 16)
+ MCFG_PALETTE_INIT_OWNER(mos8563_device, mos8563)
+MACHINE_CONFIG_END
+
+machine_config_constructor mos8563_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( mos8563 );
+}
+
+
+// VICE palette
+PALETTE_INIT_MEMBER(mos8563_device, mos8563)
+{
+ palette.set_pen_color(0, rgb_t::black);
+ palette.set_pen_color(1, rgb_t(0x55, 0x55, 0x55));
+ palette.set_pen_color(2, rgb_t(0x00, 0x00, 0xaa));
+ palette.set_pen_color(3, rgb_t(0x55, 0x55, 0xff));
+ palette.set_pen_color(4, rgb_t(0x00, 0xaa, 0x00));
+ palette.set_pen_color(5, rgb_t(0x55, 0xff, 0x55));
+ palette.set_pen_color(6, rgb_t(0x00, 0xaa, 0xaa));
+ palette.set_pen_color(7, rgb_t(0x55, 0xff, 0xff));
+ palette.set_pen_color(8, rgb_t(0xaa, 0x00, 0x00));
+ palette.set_pen_color(9, rgb_t(0xff, 0x55, 0x55));
+ palette.set_pen_color(10, rgb_t(0xaa, 0x00, 0xaa));
+ palette.set_pen_color(11, rgb_t(0xff, 0x55, 0xff));
+ palette.set_pen_color(12, rgb_t(0xaa, 0x55, 0x00));
+ palette.set_pen_color(13, rgb_t(0xff, 0xff, 0x55));
+ palette.set_pen_color(14, rgb_t(0xaa, 0xaa, 0xaa));
+ palette.set_pen_color(15, rgb_t::white);
+}
+
+
+void mos8563_device::update_cursor_state()
+{
+ mc6845_device::update_cursor_state();
+
+ /* save and increment character blink counter */
+ UINT8 last_char_blink_count = m_char_blink_count;
+ m_char_blink_count++;
+
+ /* switch on character blinking mode */
+ if (VSS_CBRATE)
+ {
+ if ((last_char_blink_count & 0x20) != (m_char_blink_count & 0x20))
+ m_char_blink_state = !m_char_blink_state;
+ }
+ else
+ {
+ if ((last_char_blink_count & 0x10) != (m_char_blink_count & 0x10))
+ m_char_blink_state = !m_char_blink_state;
+ }
+}
+
+
+UINT8 mos8563_device::draw_scanline(int y, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT8 ra = mc6845_device::draw_scanline(y, bitmap, cliprect);
+
+ if (ra == m_max_ras_addr)
+ m_current_disp_addr = (m_current_disp_addr + m_row_addr_incr) & 0x3fff;
+
+ return ra;
+}
+
+
+MC6845_UPDATE_ROW( mos8563_device::vdc_update_row )
+{
+ const pen_t *pen = m_palette->pens();
+
+ ra += (m_vert_scroll & 0x0f);
+ ra &= 0x0f;
+
+ UINT8 cth = (m_horiz_char >> 4) + (HSS_DBL ? 0 : 1);
+ UINT8 cdh = (m_horiz_char & 0x0f) + (HSS_DBL ? 0 : 1);
+ UINT8 cdv = m_vert_char_disp;
+
+ for (int column = 0; column < x_count; column++)
+ {
+ UINT8 code = read_videoram(ma + column);
+ UINT8 attr = 0;
+
+ int fg = m_color >> 4;
+ int bg = m_color & 0x0f;
+
+ if (HSS_ATTR)
+ {
+ offs_t attr_addr = m_attribute_addr + ma + column;
+ attr = read_videoram(attr_addr);
+ }
+
+ if (HSS_TEXT)
+ {
+ if (HSS_ATTR)
+ {
+ fg = ATTR_FOREGROUND;
+ bg = ATTR_BACKGROUND;
+ }
+
+ if (VSS_RVS) code ^= 0xff;
+
+ for (int bit = 0; bit < cdh; bit++)
+ {
+ int x = (m_horiz_scroll & 0x0f) - cth + (column * cth) + bit;
+ if (x < 0) x = 0;
+ int color = BIT(code, 7) ? fg : bg;
+
+ bitmap.pix32(vbp + y, hbp + x) = pen[de ? color : 0];
+ }
+ }
+ else
+ {
+ if (HSS_ATTR)
+ {
+ fg = ATTR_COLOR;
+ }
+
+ offs_t font_addr;
+
+ if (m_max_ras_addr < 16)
+ {
+ font_addr = ((m_char_base_addr & 0xe0) << 8) | (ATTR_ALTERNATE_CHARSET << 12) | (code << 4) | (ra & 0x0f);
+ }
+ else
+ {
+ font_addr = ((m_char_base_addr & 0xc0) << 8) | (ATTR_ALTERNATE_CHARSET << 13) | (code << 5) | (ra & 0x1f);
+ }
+
+ UINT8 data = read_videoram(font_addr);
+
+ if (ra >= cdv) data = 0;
+ if (ATTR_UNDERLINE && (ra == m_underline_ras)) data = 0xff;
+ if (ATTR_BLINK && !m_char_blink_state) data = 0;
+ if (ATTR_REVERSE) data ^= 0xff;
+ if (column == cursor_x) data ^= 0xff;
+ if (VSS_RVS) data ^= 0xff;
+
+ for (int bit = 0; bit < cdh; bit++)
+ {
+ int x = (m_horiz_scroll & 0x0f) - cth + (column * cth) + bit;
+ if (x < 0) x = 0;
+ int color = BIT(data, 7) ? fg : bg;
+
+ bitmap.pix32(vbp + y, hbp + x) = pen[de ? color : 0];
+
+ if ((bit < 8) || !HSS_SEMI) data <<= 1;
+ }
+ }
+ }
+}
diff --git a/src/devices/video/mc6845.h b/src/devices/video/mc6845.h
new file mode 100644
index 00000000000..053c2b9491e
--- /dev/null
+++ b/src/devices/video/mc6845.h
@@ -0,0 +1,503 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Motorola MC6845 and compatible CRT controller emulation
+
+**********************************************************************/
+
+#ifndef __MC6845__
+#define __MC6845__
+
+#include "emu.h"
+
+
+#define MCFG_MC6845_ADD(_tag, _variant, _screen_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, _variant, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag)
+
+#define MCFG_MOS8563_ADD(_tag, _screen_tag, _clock, _map) \
+ MCFG_DEVICE_ADD(_tag, MOS8563, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _map)
+
+#define MCFG_MOS8568_ADD(_tag, _screen_tag, _clock, _map) \
+ MCFG_DEVICE_ADD(_tag, MOS8568, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _map)
+
+
+#define MCFG_MC6845_SHOW_BORDER_AREA(_show) \
+ mc6845_device::set_show_border_area(*device, _show);
+
+#define MCFG_MC6845_INTERLACE_ADJUST(_value) \
+ mc6845_device::set_interlace_adjust(*device, _value);
+
+#define MCFG_MC6845_VISAREA_ADJUST(_minx, _maxx, _miny, _maxy) \
+ mc6845_device::set_visarea_adjust(*device, _minx, _maxx, _miny, _maxy);
+
+#define MCFG_MC6845_CHAR_WIDTH(_pixels) \
+ mc6845_device::set_char_width(*device, _pixels);
+
+#define MCFG_MC6845_BEGIN_UPDATE_CB(_class, _method) \
+ mc6845_device::set_begin_update_callback(*device, mc6845_begin_update_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_MC6845_UPDATE_ROW_CB(_class, _method) \
+ mc6845_device::set_update_row_callback(*device, mc6845_update_row_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_MC6845_END_UPDATE_CB(_class, _method) \
+ mc6845_device::set_end_update_callback(*device, mc6845_end_update_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_MC6845_ADDR_CHANGED_CB(_class, _method) \
+ mc6845_device::set_on_update_addr_change_callback(*device, mc6845_on_update_addr_changed_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_MC6845_OUT_DE_CB(_write) \
+ devcb = &mc6845_device::set_out_de_callback(*device, DEVCB_##_write);
+
+#define MCFG_MC6845_OUT_CUR_CB(_write) \
+ devcb = &mc6845_device::set_out_cur_callback(*device, DEVCB_##_write);
+
+#define MCFG_MC6845_OUT_HSYNC_CB(_write) \
+ devcb = &mc6845_device::set_out_hsync_callback(*device, DEVCB_##_write);
+
+#define MCFG_MC6845_OUT_VSYNC_CB(_write) \
+ devcb = &mc6845_device::set_out_vsync_callback(*device, DEVCB_##_write);
+
+
+/* callback definitions */
+typedef device_delegate<void (bitmap_rgb32 &bitmap, const rectangle &cliprect)> mc6845_begin_update_delegate;
+#define MC6845_BEGIN_UPDATE(name) void name(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+
+typedef device_delegate<void (bitmap_rgb32 &bitmap, const rectangle &cliprect, UINT16 ma, UINT8 ra,
+ UINT16 y, UINT8 x_count, INT8 cursor_x, int de, int hbp, int vbp)> mc6845_update_row_delegate;
+#define MC6845_UPDATE_ROW(name) void name(bitmap_rgb32 &bitmap, const rectangle &cliprect, UINT16 ma, UINT8 ra, \
+ UINT16 y, UINT8 x_count, INT8 cursor_x, int de, int hbp, int vbp)
+
+typedef device_delegate<void (bitmap_rgb32 &bitmap, const rectangle &cliprect)> mc6845_end_update_delegate;
+#define MC6845_END_UPDATE(name) void name(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+
+typedef device_delegate<void (int address, int strobe)> mc6845_on_update_addr_changed_delegate;
+#define MC6845_ON_UPDATE_ADDR_CHANGED(name) void name(int address, int strobe)
+
+
+class mc6845_device : public device_t,
+ public device_video_interface
+{
+ friend class mc6845_1_device;
+ friend class r6545_1_device;
+ friend class c6545_1_device;
+ friend class h46505_device;
+ friend class hd6845_device;
+ friend class sy6545_1_device;
+ friend class sy6845e_device;
+ friend class hd6345_device;
+ friend class ams40041_device;
+
+public:
+ // construction/destruction
+ mc6845_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ mc6845_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ static void set_show_border_area(device_t &device, bool show) { downcast<mc6845_device &>(device).m_show_border_area = show; }
+ static void set_interlace_adjust(device_t &device, int value) { downcast<mc6845_device &>(device).m_interlace_adjust = value; }
+ static void set_visarea_adjust(device_t &device, int min_x, int max_x, int min_y, int max_y)
+ {
+ mc6845_device &dev = downcast<mc6845_device &>(device);
+ dev.m_visarea_adjust_min_x = min_x;
+ dev.m_visarea_adjust_max_x = max_x;
+ dev.m_visarea_adjust_min_y = min_y;
+ dev.m_visarea_adjust_max_y = max_y;
+ }
+ static void set_char_width(device_t &device, int pixels) { downcast<mc6845_device &>(device).m_hpixels_per_column = pixels; }
+
+ static void set_begin_update_callback(device_t &device, mc6845_begin_update_delegate callback) { downcast<mc6845_device &>(device).m_begin_update_cb = callback; }
+ static void set_update_row_callback(device_t &device, mc6845_update_row_delegate callback) { downcast<mc6845_device &>(device).m_update_row_cb = callback; }
+ static void set_end_update_callback(device_t &device, mc6845_end_update_delegate callback) { downcast<mc6845_device &>(device).m_end_update_cb = callback; }
+ static void set_on_update_addr_change_callback(device_t &device, mc6845_on_update_addr_changed_delegate callback) { downcast<mc6845_device &>(device).m_on_update_addr_changed_cb = callback; }
+
+ template<class _Object> static devcb_base &set_out_de_callback(device_t &device, _Object object) { return downcast<mc6845_device &>(device).m_out_de_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_cur_callback(device_t &device, _Object object) { return downcast<mc6845_device &>(device).m_out_cur_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_hsync_callback(device_t &device, _Object object) { return downcast<mc6845_device &>(device).m_out_hsync_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_vsync_callback(device_t &device, _Object object) { return downcast<mc6845_device &>(device).m_out_vsync_cb.set_callback(object); }
+
+ /* select one of the registers for reading or writing */
+ DECLARE_WRITE8_MEMBER( address_w );
+
+ /* read from the status register */
+ DECLARE_READ8_MEMBER( status_r );
+
+ /* read from the currently selected register */
+ DECLARE_READ8_MEMBER( register_r );
+
+ /* write to the currently selected register */
+ DECLARE_WRITE8_MEMBER( register_w );
+
+ // read display enable line state
+ DECLARE_READ_LINE_MEMBER( de_r );
+
+ // read cursor line state
+ DECLARE_READ_LINE_MEMBER( cursor_r );
+
+ // read horizontal sync line state
+ DECLARE_READ_LINE_MEMBER( hsync_r );
+
+ // read vertical sync line state
+ DECLARE_READ_LINE_MEMBER( vsync_r );
+
+ /* return the current value on the MA0-MA13 pins */
+ UINT16 get_ma();
+
+ /* return the current value on the RA0-RA4 pins */
+ UINT8 get_ra();
+
+ /* simulates the LO->HI clocking of the light pen pin (pin 3) */
+ void assert_light_pen_input();
+
+ /* set the clock (pin 21) of the chip */
+ void set_clock(int clock);
+
+ /* set number of pixels per video memory address */
+ void set_hpixels_per_column(int hpixels_per_column);
+
+ /* updates the screen -- this will call begin_update(),
+ followed by update_row() reapeatedly and after all row
+ updating is complete, end_update() */
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ bool m_supports_disp_start_addr_r;
+ bool m_supports_vert_sync_width;
+ bool m_supports_status_reg_d5;
+ bool m_supports_status_reg_d6;
+ bool m_supports_status_reg_d7;
+ bool m_supports_transparent;
+
+ /* register file */
+ UINT8 m_horiz_char_total; /* 0x00 */
+ UINT8 m_horiz_disp; /* 0x01 */
+ UINT8 m_horiz_sync_pos; /* 0x02 */
+ UINT8 m_sync_width; /* 0x03 */
+ UINT8 m_vert_char_total; /* 0x04 */
+ UINT8 m_vert_total_adj; /* 0x05 */
+ UINT8 m_vert_disp; /* 0x06 */
+ UINT8 m_vert_sync_pos; /* 0x07 */
+ UINT8 m_mode_control; /* 0x08 */
+ UINT8 m_max_ras_addr; /* 0x09 */
+ UINT8 m_cursor_start_ras; /* 0x0a */
+ UINT8 m_cursor_end_ras; /* 0x0b */
+ UINT16 m_disp_start_addr; /* 0x0c/0x0d */
+ UINT16 m_cursor_addr; /* 0x0e/0x0f */
+ UINT16 m_light_pen_addr; /* 0x10/0x11 */
+ UINT16 m_update_addr; /* 0x12/0x13 */
+
+ /* other internal state */
+ UINT8 m_register_address_latch;
+ bool m_cursor_state;
+ UINT8 m_cursor_blink_count;
+ bool m_update_ready_bit;
+ /* output signals */
+ int m_cur;
+ int m_hsync;
+ int m_vsync;
+ int m_de;
+
+ /* internal counters */
+ UINT8 m_character_counter; /* Not used yet */
+ UINT8 m_hsync_width_counter; /* Not used yet */
+ UINT8 m_line_counter;
+ UINT8 m_raster_counter;
+ UINT8 m_adjust_counter;
+ UINT8 m_vsync_width_counter;
+
+ bool m_line_enable_ff; /* Internal flip flop which is set when the line_counter is reset and reset when vert_disp is reached */
+ UINT8 m_vsync_ff;
+ UINT8 m_adjust_active;
+ UINT16 m_line_address;
+ INT16 m_cursor_x;
+
+ /* timers */
+ static const device_timer_id TIMER_LINE = 0;
+ static const device_timer_id TIMER_DE_OFF = 1;
+ static const device_timer_id TIMER_CUR_ON = 2;
+ static const device_timer_id TIMER_CUR_OFF = 3;
+ static const device_timer_id TIMER_HSYNC_ON = 4;
+ static const device_timer_id TIMER_HSYNC_OFF = 5;
+ static const device_timer_id TIMER_LIGHT_PEN_LATCH = 6;
+ static const device_timer_id TIMER_UPD_ADR = 7;
+ static const device_timer_id TIMER_UPD_TRANS = 8;
+
+ emu_timer *m_line_timer;
+ emu_timer *m_de_off_timer;
+ emu_timer *m_cur_on_timer;
+ emu_timer *m_cur_off_timer;
+ emu_timer *m_hsync_on_timer;
+ emu_timer *m_hsync_off_timer;
+ emu_timer *m_light_pen_latch_timer;
+ emu_timer *m_upd_adr_timer;
+ emu_timer *m_upd_trans_timer;
+
+ /* computed values - do NOT state save these! */
+ /* These computed are used to define the screen parameters for a driver */
+ UINT16 m_horiz_pix_total;
+ UINT16 m_vert_pix_total;
+ UINT16 m_max_visible_x;
+ UINT16 m_max_visible_y;
+ UINT16 m_hsync_on_pos;
+ UINT16 m_hsync_off_pos;
+ UINT16 m_vsync_on_pos;
+ UINT16 m_vsync_off_pos;
+ bool m_has_valid_parameters;
+
+ UINT16 m_current_disp_addr; /* the display address currently drawn (used only in mc6845_update) */
+
+ bool m_light_pen_latched;
+ attotime m_upd_time;
+
+ void update_upd_adr_timer();
+ void call_on_update_address(int strobe);
+ void recompute_parameters(bool postload);
+ void update_counters();
+ void set_de(int state);
+ void set_hsync(int state);
+ void set_vsync(int state);
+ void set_cur(int state);
+ void handle_line_timer();
+ virtual void update_cursor_state();
+ virtual UINT8 draw_scanline(int y, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ /************************
+ interface CRTC - driver
+ ************************/
+
+ bool m_show_border_area; /* visible screen area (false) active display (true) active display + blanking */
+ int m_interlace_adjust; /* adjust max ras in interlace mode */
+
+ /* visible screen area adjustment */
+ int m_visarea_adjust_min_x;
+ int m_visarea_adjust_max_x;
+ int m_visarea_adjust_min_y;
+ int m_visarea_adjust_max_y;
+
+ int m_hpixels_per_column; /* number of pixels per video memory address */
+
+ /* if specified, this gets called before any pixel update,
+ optionally return a pointer that will be passed to the
+ update and tear down callbacks */
+ mc6845_begin_update_delegate m_begin_update_cb;
+
+ /* this gets called for every row, the driver must output
+ x_count * hpixels_per_column pixels.
+ cursor_x indicates the character position where the cursor is, or -1
+ if there is no cursor on this row */
+ mc6845_update_row_delegate m_update_row_cb;
+
+ /* if specified, this gets called after all row updating is complete */
+ mc6845_end_update_delegate m_end_update_cb;
+
+ /* Called whenever the update address changes
+ * For vblank/hblank timing strobe indicates the physical update.
+ * vblank/hblank timing not supported yet! */
+ mc6845_on_update_addr_changed_delegate m_on_update_addr_changed_cb;
+
+ /* if specified, this gets called for every change of the disply enable pin (pin 18) */
+ devcb_write_line m_out_de_cb;
+
+ /* if specified, this gets called for every change of the cursor pin (pin 19) */
+ devcb_write_line m_out_cur_cb;
+
+ /* if specified, this gets called for every change of the HSYNC pin (pin 39) */
+ devcb_write_line m_out_hsync_cb;
+
+ /* if specified, this gets called for every change of the VSYNC pin (pin 40) */
+ devcb_write_line m_out_vsync_cb;
+};
+
+
+class mc6845_1_device : public mc6845_device
+{
+public:
+ mc6845_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class r6545_1_device : public mc6845_device
+{
+public:
+ r6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class c6545_1_device : public mc6845_device
+{
+public:
+ c6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class h46505_device : public mc6845_device
+{
+public:
+ h46505_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class hd6845_device : public mc6845_device
+{
+public:
+ hd6845_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class sy6545_1_device : public mc6845_device
+{
+public:
+ sy6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class sy6845e_device : public mc6845_device
+{
+public:
+ sy6845e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class hd6345_device : public mc6845_device
+{
+public:
+ hd6345_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class ams40041_device : public mc6845_device
+{
+public:
+ ams40041_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+class mos8563_device : public mc6845_device,
+ public device_memory_interface
+{
+public:
+ mos8563_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ mos8563_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ DECLARE_PALETTE_INIT(mos8563);
+
+ DECLARE_WRITE8_MEMBER( address_w );
+ DECLARE_READ8_MEMBER( status_r );
+ DECLARE_READ8_MEMBER( register_r );
+ DECLARE_WRITE8_MEMBER( register_w );
+
+ inline UINT8 read_videoram(offs_t offset);
+ inline void write_videoram(offs_t offset, UINT8 data);
+
+ MC6845_UPDATE_ROW( vdc_update_row );
+
+protected:
+ // device-level overrides
+ virtual machine_config_constructor device_mconfig_additions() const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ const address_space_config m_videoram_space_config;
+ required_device<palette_device> m_palette;
+
+ UINT8 m_char_buffer[80];
+ UINT8 m_attr_buffer[80];
+
+ bool m_char_blink_state;
+ UINT8 m_char_blink_count;
+
+ /* register file */
+ UINT16 m_attribute_addr; /* 0x14/0x15 */
+ UINT8 m_horiz_char; /* 0x16 */
+ UINT8 m_vert_char_disp; /* 0x17 */
+ UINT8 m_vert_scroll; /* 0x18 */
+ UINT8 m_horiz_scroll; /* 0x19 */
+ UINT8 m_color; /* 0x1a */
+ UINT8 m_row_addr_incr; /* 0x1b */
+ UINT8 m_char_base_addr; /* 0x1c */
+ UINT8 m_underline_ras; /* 0x1d */
+ UINT8 m_word_count; /* 0x1e */
+ UINT8 m_data; /* 0x1f */
+ UINT16 m_block_addr; /* 0x20/0x21 */
+ UINT16 m_de_begin; /* 0x22/0x23 */
+ UINT8 m_dram_refresh; /* 0x24 */
+ UINT8 m_sync_polarity; /* 0x25 */
+
+ int m_revision;
+
+ virtual void update_cursor_state();
+ virtual UINT8 draw_scanline(int y, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ static const device_timer_id TIMER_BLOCK_COPY = 9;
+
+ emu_timer *m_block_copy_timer;
+};
+
+class mos8568_device : public mos8563_device
+{
+public:
+ mos8568_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+};
+
+
+extern const device_type MC6845;
+extern const device_type MC6845_1;
+extern const device_type R6545_1;
+extern const device_type C6545_1;
+extern const device_type H46505;
+extern const device_type HD6845;
+extern const device_type SY6545_1;
+extern const device_type SY6845E;
+extern const device_type HD6345;
+extern const device_type AMS40041;
+extern const device_type MOS8563;
+extern const device_type MOS8568;
+
+
+
+#endif
diff --git a/src/devices/video/mc6847.c b/src/devices/video/mc6847.c
new file mode 100644
index 00000000000..76c0dcb4c92
--- /dev/null
+++ b/src/devices/video/mc6847.c
@@ -0,0 +1,1890 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/*********************************************************************
+
+ mc6847.c
+
+ Implementation of Motorola 6847 video hardware chip
+
+ Sources:
+ M6847 data sheet
+ M6847T1 info from Rainbow magazine (10/1986-12/1986)
+
+
+ AG AS INTEXT INV GM2 GM1 GM0
+ -- -- ------ --- --- --- ---
+ 0 0 0 0 X X X Internal Alphanumerics
+ 0 0 0 1 X X X Internal Alphanumerics Inverted
+ 0 0 1 0 X X X External Alphanumerics
+ 0 0 1 1 X X X External Alphanumerics Inverted
+ 0 1 0 X X X X Semigraphics 4
+ 0 1 1 X X X X Semigraphics 6
+ 1 X X X 0 0 0 Graphics CG1 (64x64x4) (16 bpr)
+ 1 X X X 0 0 1 Graphics RG1 (128x64x2) (16 bpr)
+ 1 X X X 0 1 0 Graphics CG2 (128x64x4) (32 bpr)
+ 1 X X X 0 1 1 Graphics RG2 (128x96x2) (16 bpr)
+ 1 X X X 1 0 0 Graphics CG3 (128x96x4) (32 bpr)
+ 1 X X X 1 0 1 Graphics RG3 (128x192x2) (16 bpr)
+ 1 X X X 1 1 0 Graphics CG6 (128x192x4) (32 bpr)
+ 1 X X X 1 1 1 Graphics RG6 (256x192x2) (32 bpr)
+
+ Note: The M6847 relies on an external source (typically a 6883 SAM chip)
+ to feed it bytes; so the BPR (bytes per row) figures are effectively
+ suggestions. Mismatching modes is responsible for the semigraphic modes
+ on the CoCo.
+
+ Timing: (source Motorola M6847 Manual, experimentation, SockMaster)
+
+ Horizontal Sync: Total Period: 228 clock cycles
+ @ CLK(0) + DHS_F - falling edge (high to low)
+ @ CLK(16.5) + DHS_R - rising edge (low to high)
+ @ CLK(42) - left border start
+ @ CLK(71.5) - body start
+ @ CLK(199.5) - right border start
+ @ CLK(228) + DHS_F - falling edge (high to low)
+ ...
+
+ Field Sync: Total Period 262*228 clock cycles
+ @ CLK(0) + DFS_F - falling edge (high to low)
+ @ CLK(32*228) + DFS_R - rising edge (low to high)
+ @ CLK(262*228) + DFS_F - falling edge (high to low) (262.5 for the M6847Y)
+
+ DHS_F: 550ns
+ DHS_R: 740ns
+ DFS_F: 520ns
+ DFS_R: 500ns
+
+ The M6847T1 is a later variant of the M6847 chip that implements lower
+ case support and some other nifty features. This chip is in the CoCo 2B.
+ I have not been able to find a pinout diagram for this chip so I am
+ assuming that the extra text modes on the CoCo 2B are activated by the
+ GM2-0 pins. This needs to be confirmed.
+
+ The MC6847 datasheet states that a scanline is 227.5 clock cycles,
+ but experimentation suggests that it is 228. The game "Dragon Fire"
+ has a fine tuned loop that runs in 57 clock cycles by the CPU's
+ reckoning (228 actual clock cycles) and would not function correctly
+ if skew existed. SockMaster has confirmed that scanlines are in
+ fact 228 clock cycles.
+
+ The PAL emulation is little more than having more scanlines; surely
+ there are details that we are missing here.
+
+**********************************************************************/
+
+
+#include "emu.h"
+#include "video/mc6847.h"
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+#define TOP_BORDER 25
+#define USE_HORIZONTAL_CLIP false
+
+#define TIMER_HSYNC_PERIOD (228)
+#define TIMER_HSYNC_OFF_TIME (10.0)
+#define TIMER_HSYNC_ON_TIME (TIMER_HSYNC_OFF_TIME + 16.5)
+#define TIMER_FSYNC_OFF_TIME (TIMER_HSYNC_PERIOD * TOP_BORDER + TIMER_HSYNC_ON_TIME)
+#define TIMER_FSYNC_ON_TIME (TIMER_HSYNC_PERIOD * (TOP_BORDER + 192) + TIMER_HSYNC_ON_TIME)
+
+#define LOG_SCANLINE 0
+#define LOG_HSYNC 0
+#define LOG_FSYNC 0
+#define LOG_FLUSH 0
+#define LOG_INPUT 0
+
+
+const UINT32 mc6847_base_device::s_palette[mc6847_base_device::PALETTE_LENGTH] =
+{
+ rgb_t(0x07, 0xff, 0x00), /* GREEN */
+ rgb_t(0xff, 0xff, 0x00), /* YELLOW */
+ rgb_t(0x3b, 0x08, 0xff), /* BLUE */
+ rgb_t(0xcc, 0x00, 0x3b), /* RED */
+ rgb_t(0xff, 0xff, 0xff), /* BUFF */
+ rgb_t(0x07, 0xe3, 0x99), /* CYAN */
+ rgb_t(0xff, 0x1c, 0xff), /* MAGENTA */
+ rgb_t(0xff, 0x81, 0x00), /* ORANGE */
+
+ rgb_t(0x00, 0x00, 0x00), /* BLACK */
+ rgb_t(0x07, 0xff, 0x00), /* GREEN */
+ rgb_t(0x00, 0x00, 0x00), /* BLACK */
+ rgb_t(0xff, 0xff, 0xff), /* BUFF */
+
+ rgb_t(0x00, 0x7c, 0x00), /* ALPHANUMERIC DARK GREEN */
+ rgb_t(0x07, 0xff, 0x00), /* ALPHANUMERIC BRIGHT GREEN */
+ rgb_t(0x91, 0x00, 0x00), /* ALPHANUMERIC DARK ORANGE */
+ rgb_t(0xff, 0x81, 0x00) /* ALPHANUMERIC BRIGHT ORANGE */
+};
+
+
+
+//**************************************************************************
+// FRIEND DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// ctor
+//-------------------------------------------------
+
+mc6847_friend_device::mc6847_friend_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock,
+ const UINT8 *fontdata, bool is_mc6847t1, double tpfs, int field_sync_falling_edge_scanline, bool supports_partial_body_scanlines, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_write_hsync(*this),
+ m_write_fsync(*this),
+ m_character_map(fontdata, is_mc6847t1)
+{
+ m_tpfs = tpfs;
+ m_supports_partial_body_scanlines = supports_partial_body_scanlines;
+
+ // The MC6847 and the GIME apply field sync on different scanlines
+ m_field_sync_falling_edge_scanline = field_sync_falling_edge_scanline;
+}
+
+
+
+//-------------------------------------------------
+// setup_timer - sets up a single timer relative
+// to the clock
+//-------------------------------------------------
+
+inline emu_timer *mc6847_friend_device::setup_timer(device_timer_id id, double offset, double period)
+{
+ emu_timer *timer = timer_alloc(id);
+ timer->adjust(
+ attotime::from_ticks(offset * 4, m_clock * 4),
+ 0,
+ attotime::from_ticks(period * 4, m_clock * 4));
+ return timer;
+}
+
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mc6847_friend_device::device_start(void)
+{
+ m_write_hsync.resolve_safe();
+ m_write_fsync.resolve_safe();
+
+ /* create the timers */
+ m_frame_timer = setup_timer( TIMER_FRAME, 0, m_tpfs * TIMER_HSYNC_PERIOD);
+ m_hsync_on_timer = setup_timer( TIMER_HSYNC_ON, TIMER_HSYNC_ON_TIME, TIMER_HSYNC_PERIOD);
+ m_hsync_off_timer = setup_timer(TIMER_HSYNC_OFF, TIMER_HSYNC_OFF_TIME, TIMER_HSYNC_PERIOD);
+ m_fsync_timer = timer_alloc(TIMER_FSYNC);
+
+ m_top_border_scanlines = 0;
+ m_body_scanlines = 0;
+ m_wide = false;
+ m_recording_scanline = false;
+ m_physical_scanline = 0;
+ m_logical_scanline_zone = 0;
+ m_field_sync = false;
+ m_horizontal_sync = false;
+ set_geometry(25, 192, false);
+
+ /* save states */
+ save_item(NAME(m_physical_scanline));
+ save_item(NAME(m_logical_scanline));
+ save_item(NAME(m_logical_scanline_zone));
+ save_item(NAME(m_horizontal_sync));
+ save_item(NAME(m_field_sync));
+
+ /* artifacting */
+ m_artifacter.setup_config(this);
+}
+
+
+
+//-------------------------------------------------
+// device_start - device-specific reset
+//-------------------------------------------------
+
+void mc6847_friend_device::device_reset(void)
+{
+ device_t::device_reset();
+ m_video_changed = true;
+}
+
+
+
+//-------------------------------------------------
+// device_post_load - device-specific post load
+//-------------------------------------------------
+
+void mc6847_friend_device::device_post_load(void)
+{
+ device_t::device_post_load();
+ m_video_changed = true;
+}
+
+
+
+//-------------------------------------------------
+// update_field_sync_timer
+//-------------------------------------------------
+
+void mc6847_friend_device::update_field_sync_timer(void)
+{
+ // are we expecting field sync?
+ bool expected_field_sync = (m_physical_scanline < m_field_sync_falling_edge_scanline)
+ || (m_logical_scanline_zone == SCANLINE_ZONE_VBLANK);
+
+ // do we need to adjust the timer?
+ if (expected_field_sync != m_field_sync)
+ {
+ // if so, determine the duration
+ attotime duration = attotime::from_ticks(160, m_clock);
+
+ // and reset the timer
+ m_fsync_timer->adjust(duration, expected_field_sync ? 1 : 0);
+ }
+}
+
+
+
+//-------------------------------------------------
+// device_timer
+//-------------------------------------------------
+
+void mc6847_friend_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case TIMER_FRAME: new_frame(); break;
+ case TIMER_HSYNC_ON: change_horizontal_sync(true); break;
+ case TIMER_HSYNC_OFF: change_horizontal_sync(false); break;
+ case TIMER_FSYNC: change_field_sync(param != 0); break;
+ }
+}
+
+
+
+//-------------------------------------------------
+// new_frame
+//-------------------------------------------------
+
+inline void mc6847_friend_device::new_frame(void)
+{
+ m_physical_scanline = 0;
+ m_logical_scanline = 0;
+ m_logical_scanline_zone = SCANLINE_ZONE_FRAME_END;
+}
+
+
+
+//-------------------------------------------------
+// scanline_zone_string
+//-------------------------------------------------
+
+const char *mc6847_friend_device::scanline_zone_string(scanline_zone zone)
+{
+ const char *result;
+ switch(zone)
+ {
+ case SCANLINE_ZONE_TOP_BORDER: result = "SCANLINE_ZONE_TOP_BORDER"; break;
+ case SCANLINE_ZONE_BODY: result = "SCANLINE_ZONE_BODY"; break;
+ case SCANLINE_ZONE_BOTTOM_BORDER: result = "SCANLINE_ZONE_BOTTOM_BORDER"; break;
+ case SCANLINE_ZONE_RETRACE: result = "SCANLINE_ZONE_RETRACE"; break;
+ case SCANLINE_ZONE_VBLANK: result = "SCANLINE_ZONE_VBLANK"; break;
+ case SCANLINE_ZONE_FRAME_END: result = "SCANLINE_ZONE_FRAME_END"; break;
+ default:
+ fatalerror("Should not get here\n");
+ }
+ return result;
+}
+
+
+
+//-------------------------------------------------
+// change_horizontal_sync
+//-------------------------------------------------
+
+inline void mc6847_friend_device::change_horizontal_sync(bool line)
+{
+ g_profiler.start(PROFILER_USER1);
+
+ // are we on a rising edge?
+ if (line && !m_horizontal_sync)
+ {
+ if (LOG_SCANLINE)
+ logerror("%s: change_horizontal_sync(): Recording scanline\n", describe_context());
+
+ // first store the scanline
+ g_profiler.start(PROFILER_USER2);
+ switch((scanline_zone) m_logical_scanline_zone)
+ {
+ case SCANLINE_ZONE_TOP_BORDER:
+ case SCANLINE_ZONE_BOTTOM_BORDER:
+ record_border_scanline(m_physical_scanline);
+ break;
+
+ case SCANLINE_ZONE_BODY:
+ m_recording_scanline = true;
+ if (m_partial_scanline_clocks > 0)
+ record_partial_body_scanline(m_physical_scanline, m_logical_scanline, m_partial_scanline_clocks, 228);
+ else
+ record_body_scanline(m_physical_scanline, m_logical_scanline);
+ m_recording_scanline = false;
+ break;
+
+ case SCANLINE_ZONE_RETRACE:
+ case SCANLINE_ZONE_VBLANK:
+ case SCANLINE_ZONE_FRAME_END:
+ // do nothing
+ break;
+ }
+ g_profiler.stop();
+
+ // advance to next scanline
+ next_scanline();
+
+ // and update the field sync timer
+ update_field_sync_timer();
+ }
+
+ // finally output horizontal sync
+ if (line != m_horizontal_sync)
+ {
+ m_horizontal_sync = line;
+
+ // log if apprpriate
+ if (LOG_HSYNC)
+ logerror("%s: change_horizontal_sync(): line=%d\n", describe_context(), line ? 1 : 0);
+
+ // invoke callback
+ m_write_hsync(line);
+
+ // call virtual function
+ horizontal_sync_changed(m_horizontal_sync);
+ }
+
+ g_profiler.stop();
+}
+
+
+
+//-------------------------------------------------
+// change_field_sync
+//-------------------------------------------------
+
+inline void mc6847_friend_device::change_field_sync(bool line)
+{
+ /* output field sync */
+ if (line != m_field_sync)
+ {
+ m_field_sync = line;
+
+ /* log if apprpriate */
+ if (LOG_FSYNC)
+ logerror("%s: change_field_sync(): line=%d\n", describe_context(), line ? 1 : 0);
+
+ /* invoke callback */
+ m_write_fsync(line);
+
+ /* call virtual function */
+ field_sync_changed(m_field_sync);
+ }
+}
+
+
+
+//-------------------------------------------------
+// next_scanline
+//-------------------------------------------------
+
+inline void mc6847_friend_device::next_scanline(void)
+{
+ /* advance to next scanline */
+ m_physical_scanline++;
+ m_logical_scanline++;
+ m_partial_scanline_clocks = 0;
+
+ /* check for movement into the next "zone" */
+ if (m_logical_scanline_zone == SCANLINE_ZONE_FRAME_END)
+ {
+ /* we're now in the top border */
+ m_logical_scanline = 0;
+ m_logical_scanline_zone = SCANLINE_ZONE_TOP_BORDER;
+ }
+ else if ((m_logical_scanline_zone < SCANLINE_ZONE_VBLANK) && (m_physical_scanline >= 25+192+26+6))
+ {
+ /* we're now into vblank */
+ m_logical_scanline = 0;
+ m_logical_scanline_zone = SCANLINE_ZONE_VBLANK;
+ }
+ else if ((m_logical_scanline_zone < SCANLINE_ZONE_RETRACE) && (m_physical_scanline >= 25+192+26))
+ {
+ /* we're now into retrace */
+ m_logical_scanline = 0;
+ m_logical_scanline_zone = SCANLINE_ZONE_RETRACE;
+ }
+ else if ((m_logical_scanline_zone == SCANLINE_ZONE_TOP_BORDER) && (m_logical_scanline >= m_top_border_scanlines))
+ {
+ /* we're now into the body */
+ m_logical_scanline = 0;
+ m_logical_scanline_zone = SCANLINE_ZONE_BODY;
+ }
+ else if ((m_logical_scanline_zone == SCANLINE_ZONE_BODY) && (m_logical_scanline >= m_body_scanlines))
+ {
+ /* we're now into the bottom border */
+ m_logical_scanline = 0;
+ m_logical_scanline_zone = SCANLINE_ZONE_BOTTOM_BORDER;
+ enter_bottom_border();
+ }
+}
+
+
+
+//-------------------------------------------------
+// horizontal_sync_changed
+//-------------------------------------------------
+
+void mc6847_friend_device::horizontal_sync_changed(bool line)
+{
+}
+
+
+
+//-------------------------------------------------
+// field_sync_changed
+//-------------------------------------------------
+
+void mc6847_friend_device::field_sync_changed(bool line)
+{
+}
+
+
+
+//-------------------------------------------------
+// enter_bottom_border
+//-------------------------------------------------
+
+void mc6847_friend_device::enter_bottom_border(void)
+{
+}
+
+
+
+//-------------------------------------------------
+// record_border_scanline
+//-------------------------------------------------
+
+void mc6847_friend_device::record_border_scanline(UINT16 physical_scanline)
+{
+}
+
+
+
+//-------------------------------------------------
+// get_clocks_since_hsync
+//-------------------------------------------------
+
+INT32 mc6847_friend_device::get_clocks_since_hsync()
+{
+ UINT64 hsync_on_clocks = attotime_to_clocks(m_hsync_on_timer->start());
+ UINT64 current_clocks = attotime_to_clocks(machine().time());
+ return (INT32) (current_clocks - hsync_on_clocks);
+}
+
+
+
+//-------------------------------------------------
+// video_flush
+//-------------------------------------------------
+
+void mc6847_friend_device::video_flush()
+{
+ // first, only flush if...
+ // 1. We support partial scanlines
+ // 2. We're not already recording
+ // 3. We're in the body
+ if (m_supports_partial_body_scanlines && !m_recording_scanline && (m_logical_scanline_zone == SCANLINE_ZONE_BODY))
+ {
+ UINT32 new_partial_scanline_clocks = get_clocks_since_hsync();
+ if (m_partial_scanline_clocks < new_partial_scanline_clocks)
+ {
+ if (LOG_FLUSH)
+ logerror("%s: new_partial_scanline_clocks=%u\n", describe_context(), new_partial_scanline_clocks);
+
+ m_recording_scanline = true;
+ record_partial_body_scanline(m_physical_scanline, m_logical_scanline, m_partial_scanline_clocks, new_partial_scanline_clocks);
+ m_recording_scanline = false;
+
+ m_partial_scanline_clocks = new_partial_scanline_clocks;
+ }
+ }
+}
+
+
+
+//-------------------------------------------------
+// describe_context
+//-------------------------------------------------
+
+const char *mc6847_friend_device::describe_context(void)
+{
+ static char buffer[128];
+ snprintf(buffer, ARRAY_LENGTH(buffer), "%s (scanline %s:%d)",
+ machine().describe_context(),
+ scanline_zone_string((scanline_zone) m_logical_scanline_zone),
+ m_logical_scanline);
+ return buffer;
+}
+
+
+
+//**************************************************************************
+// BASE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// ctor
+//-------------------------------------------------
+
+mc6847_base_device::mc6847_base_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const UINT8 *fontdata, double tpfs, const char *shortname, const char *source) :
+ mc6847_friend_device(mconfig, type, name, tag, owner, clock, fontdata, (type == MC6847T1_NTSC) || (type == MC6847T1_PAL), tpfs, 25+191, true, shortname, source),
+ m_input_cb(*this),
+ m_black_and_white(false),
+ m_fixed_mode(0),
+ m_fixed_mode_mask(0)
+{
+ m_palette = s_palette;
+
+ for (int i = 0; i < ARRAY_LENGTH(s_palette); i++)
+ {
+ m_bw_palette[i] = black_and_white(s_palette[i]);
+ }
+}
+
+
+
+//-------------------------------------------------
+// setup_fixed_mode - sets up fixed mode mask
+//-------------------------------------------------
+
+void mc6847_base_device::setup_fixed_mode()
+{
+ for (int i = 0; i < 8; i++)
+ {
+ if (BIT(m_fixed_mode, i))
+ m_fixed_mode_mask |= (1 << i);
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mc6847_base_device::device_start()
+{
+ /* inherited function */
+ mc6847_friend_device::device_start();
+
+ /* setup */
+ memset(m_data, 0, sizeof(m_data));
+
+ /* resolve callbacks */
+ m_input_cb.resolve_safe(0);
+ m_charrom_cb.bind_relative_to(*owner());
+
+ /* set up fixed mode */
+ setup_fixed_mode();
+
+ m_dirty = false;
+ m_mode = 0;
+
+ /* state save */
+ save_item(NAME(m_dirty));
+ save_item(NAME(m_mode));
+
+ /* colors */
+ m_palette = m_black_and_white ? m_bw_palette : s_palette;
+}
+
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mc6847_base_device::device_reset()
+{
+ mc6847_friend_device::device_reset();
+ m_mode = m_fixed_mode;
+}
+
+
+
+//-------------------------------------------------
+// input
+//-------------------------------------------------
+
+UINT8 mc6847_base_device::input(UINT16 address)
+{
+ UINT8 data = m_input_cb(address);
+ if (LOG_INPUT)
+ logerror("%s: input: address=0x%04X data=0x%02X\n", describe_context(), address, data);
+ return data;
+}
+
+
+
+//-------------------------------------------------
+// record_scanline_res
+//-------------------------------------------------
+
+template<int sample_count, int yres>
+void mc6847_base_device::record_scanline_res(int scanline, INT32 start_pos, INT32 end_pos)
+{
+ UINT8 current_sample_count = (start_pos > 0) ? m_data[scanline].m_sample_count : 0;
+
+ // main loop
+ for (INT32 pos = start_pos; pos < end_pos; pos++)
+ {
+ // set address at beginning of line
+ if (pos == 0)
+ m_video_address = scanline / (192 / yres) * sample_count;
+
+ if ((sample_count == 32) || ((pos % 1) == 0))
+ {
+ // input data
+ UINT8 data = input(m_video_address++);
+
+ if (pos < 32)
+ {
+ // update values
+ //assert(current_sample_count >= 0);
+ assert(current_sample_count < ARRAY_LENGTH(m_data[scanline].m_mode));
+ update_value(&m_data[scanline].m_mode[current_sample_count], simplify_mode(data, m_mode));
+ update_value(&m_data[scanline].m_data[current_sample_count], data);
+ current_sample_count++;
+ }
+ }
+ }
+
+ // update sample count
+ update_value(&m_data[scanline].m_sample_count, current_sample_count);
+}
+
+
+
+//-------------------------------------------------
+// record_body_scanline
+//-------------------------------------------------
+
+inline void mc6847_base_device::record_body_scanline(UINT16 physical_scanline, UINT16 scanline, INT32 start_pos, INT32 end_pos)
+{
+ // sanity checks
+ assert(scanline < 192);
+
+ if (m_mode & MODE_AG)
+ {
+ switch(m_mode & (MODE_GM2|MODE_GM1|MODE_GM0))
+ {
+ case 0:
+ case MODE_GM0:
+ record_scanline_res<16, 64>(scanline, start_pos, end_pos);
+ break;
+
+ case MODE_GM1:
+ record_scanline_res<32, 64>(scanline, start_pos, end_pos);
+ break;
+
+ case MODE_GM1|MODE_GM0:
+ record_scanline_res<16, 96>(scanline, start_pos, end_pos);
+ break;
+
+ case MODE_GM2:
+ record_scanline_res<32, 96>(scanline, start_pos, end_pos);
+ break;
+
+ case MODE_GM2|MODE_GM0:
+ record_scanline_res<16, 192>(scanline, start_pos, end_pos);
+ break;
+
+ case MODE_GM2|MODE_GM1:
+ case MODE_GM2|MODE_GM1|MODE_GM0:
+ record_scanline_res<32, 192>(scanline, start_pos, end_pos);
+ break;
+
+ default:
+ /* should not get here */
+ fatalerror("should not get here\n");
+ }
+ }
+ else
+ {
+ record_scanline_res<32, 16>(scanline, start_pos, end_pos);
+ }
+}
+
+
+
+//-------------------------------------------------
+// record_body_scanline
+//-------------------------------------------------
+
+void mc6847_base_device::record_body_scanline(UINT16 physical_scanline, UINT16 scanline)
+{
+ record_body_scanline(physical_scanline, scanline, 0, 32);
+}
+
+
+
+//-------------------------------------------------
+// record_partial_body_scanline
+//-------------------------------------------------
+
+void mc6847_base_device::record_partial_body_scanline(UINT16 physical_scanline, UINT16 scanline, INT32 start_clock, INT32 end_clock)
+{
+ INT32 start_pos = MAX(scanline_position_from_clock(start_clock), 0);
+ INT32 end_pos = MIN(scanline_position_from_clock(end_clock), 42);
+
+ if (start_pos < end_pos)
+ record_body_scanline(physical_scanline, scanline, start_pos, end_pos);
+}
+
+
+
+//-------------------------------------------------
+// scanline_position_from_clock
+//-------------------------------------------------
+
+INT32 mc6847_base_device::scanline_position_from_clock(INT32 clocks_since_hsync)
+{
+ return (clocks_since_hsync - 20) / 4;
+}
+
+
+
+//-------------------------------------------------
+// field_sync_changed
+//-------------------------------------------------
+
+void mc6847_base_device::field_sync_changed(bool line)
+{
+ /* when field sync is on, the DA* enter the Hi-Z state */
+ if (line)
+ m_input_cb(~0);
+}
+
+
+
+//-------------------------------------------------
+// border_value
+//-------------------------------------------------
+
+inline mc6847_base_device::pixel_t mc6847_base_device::border_value(UINT8 mode, const pixel_t *palette, bool is_mc6847t1)
+{
+ pixel_t result;
+ switch(mc6847_friend_device::border_value(mode, is_mc6847t1))
+ {
+ case BORDER_COLOR_BLACK:
+ result = palette[8];
+ break;
+ case BORDER_COLOR_GREEN:
+ result = palette[0];
+ break;
+ case BORDER_COLOR_WHITE:
+ result = palette[4];
+ break;
+ case BORDER_COLOR_ORANGE:
+ result = palette[7];
+ break;
+ default:
+ fatalerror("Should not get here\n");
+ }
+ return result;
+}
+
+
+
+//-------------------------------------------------
+// update
+//-------------------------------------------------
+
+UINT32 mc6847_base_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int base_x = 32;
+ int base_y = 25;
+ int x, x2, y, width;
+ bool is_mc6847t1 = (type() == MC6847T1_NTSC) || (type() == MC6847T1_PAL);
+ int min_x = USE_HORIZONTAL_CLIP ? cliprect.min_x : 0;
+ int max_x = USE_HORIZONTAL_CLIP ? cliprect.max_x : (base_x * 2 + 256 - 1);
+ int min_y = cliprect.min_y;
+ int max_y = cliprect.max_y;
+ const pixel_t *palette = m_palette;
+
+ /* if the video didn't change, indicate as much */
+ if (!has_video_changed())
+ return UPDATE_HAS_NOT_CHANGED;
+
+ /* top border */
+ for (y = min_y; y < base_y; y++)
+ {
+ for (x = min_x; x <= max_x; x++)
+ {
+ *bitmap_addr(bitmap, y, x) = border_value(m_data[0].m_mode[0], palette, is_mc6847t1);
+ }
+ }
+
+ for (y = MAX(0, min_y - base_y); y < MIN(192, max_y - base_y); y++)
+ {
+ /* left border */
+ for (x = min_x; x < base_x; x++)
+ {
+ *bitmap_addr(bitmap, y + base_y, x) = border_value(m_data[y].m_mode[0], palette, is_mc6847t1);
+ }
+
+ /* body */
+ x = 0;
+ width = m_data[y].m_sample_count;
+ pixel_t *RESTRICT pixels = bitmap_addr(bitmap, base_y + y, base_x);
+ while(x < width)
+ {
+ /* determine how many bytes exist for which the mode is identical */
+ for (x2 = x + 1; (x2 < width) && (m_data[y].m_mode[x] == m_data[y].m_mode[x2]); x2++)
+ ;
+
+ /* emit the samples */
+ pixels += emit_mc6847_samples<1>(
+ m_data[y].m_mode[x],
+ &m_data[y].m_data[x],
+ x2 - x,
+ pixels,
+ m_palette,
+ m_charrom_cb,
+ x,
+ y);
+
+ /* update x */
+ x = x2;
+ }
+
+ /* right border */
+ if (width)
+ for (x = base_x + 256; x <= max_x; x++)
+ *bitmap_addr(bitmap, y + base_y, x) = border_value(m_data[y].m_mode[width - 1], palette, is_mc6847t1);
+
+ /* artifacting */
+ m_artifacter.process_artifacts<1>(bitmap_addr(bitmap, y + base_y, base_x), m_data[y].m_mode[0], palette);
+ }
+
+ width = m_data[191].m_sample_count;
+
+ /* bottom border */
+ if (width)
+ for (y = base_y + 192; y <= max_y; y++)
+ for (x = min_x; x <= max_x; x++)
+ *bitmap_addr(bitmap, y, x) = border_value(m_data[191].m_mode[width - 1], palette, is_mc6847t1);
+
+ return 0;
+}
+
+
+
+//**************************************************************************
+// CHARACTER MAP
+//**************************************************************************
+
+mc6847_friend_device::character_map::character_map(const UINT8 *text_fontdata, bool is_mc6847t1)
+{
+ int mode, i;
+
+ // set up font data
+ for (i = 0; i < 64*12; i++)
+ {
+ m_text_fontdata_inverse[i] = text_fontdata[i] ^ 0xFF;
+ m_text_fontdata_lower_case[i] = text_fontdata[i + (i < 32*12 ? 64*12 : 0)] ^ (i < 32*12 ? 0xFF : 0x00);
+ m_text_fontdata_lower_case_inverse[i] = m_text_fontdata_lower_case[i] ^ 0xFF;
+ }
+
+ // loop through all modes
+ for (mode = 0; mode < ARRAY_LENGTH(m_entries); mode++)
+ {
+ const UINT8 *fontdata;
+ UINT8 character_mask;
+ UINT8 color_shift_0 = 0;
+ UINT8 color_shift_1 = 0;
+ UINT8 color_mask_0 = 0x00;
+ UINT8 color_mask_1 = 0x00;
+ UINT16 color_base_0;
+ UINT16 color_base_1;
+
+ if ((mode & MODE_INTEXT) && !is_mc6847t1)
+ {
+ // semigraphics 6
+ fontdata = semigraphics6_fontdata8x12;
+ character_mask = 0x3F;
+ color_base_0 = 8;
+ color_base_1 = mode & MODE_CSS ? 4 : 0;
+ color_shift_1 = 6;
+ color_mask_1 = 0x03;
+ }
+ else if (mode & MODE_AS)
+ {
+ // semigraphics 4
+ fontdata = semigraphics4_fontdata8x12;
+ character_mask = 0x0F;
+ color_base_0 = 8;
+ color_base_1 = 0;
+ color_shift_1 = 4;
+ color_mask_1 = 0x07;
+ }
+ else
+ {
+ // text
+ bool is_lower_case = is_mc6847t1 && ((mode & MODE_INV) == 0) && (mode & MODE_GM0);
+ bool is_inverse1 = (mode & MODE_INV) ? true : false;
+ bool is_inverse2 = is_mc6847t1 && (mode & MODE_GM1);
+ bool is_inverse = (is_inverse1 && !is_inverse2) || (!is_inverse1 && is_inverse2);
+ fontdata = is_inverse
+ ? (is_lower_case ? m_text_fontdata_lower_case_inverse : m_text_fontdata_inverse)
+ : (is_lower_case ? m_text_fontdata_lower_case : text_fontdata);
+ character_mask = 0x3F;
+ color_base_0 = (mode & MODE_CSS ? 14 : 12);
+ color_base_1 = (mode & MODE_CSS ? 15 : 13);
+ }
+
+ // populate the entry
+ memset(&m_entries[mode], 0, sizeof(m_entries[mode]));
+ m_entries[mode].m_fontdata = fontdata;
+ m_entries[mode].m_character_mask = character_mask;
+ m_entries[mode].m_color_shift_0 = color_shift_0;
+ m_entries[mode].m_color_shift_1 = color_shift_1;
+ m_entries[mode].m_color_mask_0 = color_mask_0;
+ m_entries[mode].m_color_mask_1 = color_mask_1;
+ m_entries[mode].m_color_base_0 = color_base_0;
+ m_entries[mode].m_color_base_1 = color_base_1;
+ }
+}
+
+
+
+//-------------------------------------------------
+// pal_round_fontdata8x12
+//-------------------------------------------------
+
+const UINT8 mc6847_friend_device::pal_round_fontdata8x12[] =
+{
+ 0x00, 0x00, 0x38, 0x44, 0x04, 0x34, 0x4C, 0x4C, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x28, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x24, 0x24, 0x38, 0x24, 0x24, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x24, 0x24, 0x24, 0x24, 0x24, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x40, 0x40, 0x70, 0x40, 0x40, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x40, 0x40, 0x70, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x40, 0x40, 0x4C, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x6C, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x54, 0x6C, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x54, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x20, 0x7C, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x28, 0x28, 0x7C, 0x28, 0x7C, 0x28, 0x28, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x3C, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x64, 0x08, 0x10, 0x20, 0x4C, 0x0C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x50, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x10, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x54, 0x38, 0x38, 0x54, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x4C, 0x54, 0x64, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x04, 0x38, 0x40, 0x40, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x04, 0x08, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x18, 0x28, 0x48, 0x7C, 0x08, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x40, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x04, 0x08, 0x10, 0x20, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x04, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x04, 0x08, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+
+ /* Lower case */
+ 0x00, 0x00, 0x18, 0x24, 0x20, 0x70, 0x20, 0x24, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x04, 0x3C, 0x44, 0x3C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x40, 0x58, 0x64, 0x44, 0x64, 0x58, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x04, 0x34, 0x4C, 0x44, 0x4C, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x7C, 0x40, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x14, 0x10, 0x38, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x34, 0x4C, 0x44, 0x4C, 0x34, 0x04, 0x38, 0x00,
+ 0x00, 0x00, 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x40, 0x48, 0x50, 0x60, 0x50, 0x48, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x54, 0x54, 0x54, 0x54, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x78, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0x64, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3C, 0x40, 0x38, 0x04, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x20, 0x70, 0x20, 0x20, 0x24, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x4C, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x54, 0x54, 0x28, 0x28, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7C, 0x08, 0x10, 0x20, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x10, 0x10, 0x20, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x10, 0x10, 0x08, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x54, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00
+};
+
+
+
+//-------------------------------------------------
+// pal_square_fontdata8x12
+//-------------------------------------------------
+
+const UINT8 mc6847_friend_device::pal_square_fontdata8x12[] =
+{
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x1A, 0x2A, 0x2A, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x12, 0x12, 0x1C, 0x12, 0x12, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x26, 0x22, 0x22, 0x1E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x24, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x10, 0x08, 0x04, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x3E, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x14, 0x36, 0x00, 0x36, 0x14, 0x14, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x32, 0x32, 0x04, 0x08, 0x10, 0x26, 0x26, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x28, 0x28, 0x10, 0x2A, 0x24, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3E, 0x1C, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x10, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x0C, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x0C, 0x14, 0x3E, 0x04, 0x04, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x3C, 0x02, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x24, 0x04, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00,
+
+ /* Lower case */
+ 0x00, 0x00, 0x00, 0x0C, 0x12, 0x10, 0x38, 0x10, 0x12, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x1E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x20, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x3E, 0x20, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0C, 0x12, 0x10, 0x38, 0x10, 0x10, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x1C,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x24, 0x18,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x24, 0x28, 0x38, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x12, 0x0C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x26, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x2A, 0x2A, 0x1C, 0x14, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x1C,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x04, 0x08, 0x10, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x20, 0x10, 0x10, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x04, 0x3E, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00,
+};
+
+
+
+//-------------------------------------------------
+// ntsc_round_fontdata8x12
+//-------------------------------------------------
+
+const UINT8 mc6847_friend_device::ntsc_round_fontdata8x12[] =
+{
+ 0x00, 0x00, 0x38, 0x44, 0x04, 0x34, 0x4C, 0x4C, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x28, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x24, 0x24, 0x38, 0x24, 0x24, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x24, 0x24, 0x24, 0x24, 0x24, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x40, 0x40, 0x70, 0x40, 0x40, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x40, 0x40, 0x70, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x40, 0x40, 0x4C, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x6C, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x54, 0x6C, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x54, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x20, 0x7C, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x28, 0x28, 0x7C, 0x28, 0x7C, 0x28, 0x28, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x3C, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x64, 0x08, 0x10, 0x20, 0x4C, 0x0C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x50, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x10, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x54, 0x38, 0x38, 0x54, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x4C, 0x54, 0x64, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x04, 0x38, 0x40, 0x40, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x04, 0x08, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x18, 0x28, 0x48, 0x7C, 0x08, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x40, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x04, 0x08, 0x10, 0x20, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x04, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x44, 0x04, 0x08, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+
+ /* Lower case */
+ 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x04, 0x3C, 0x44, 0x3C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x40, 0x58, 0x64, 0x44, 0x64, 0x58, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x04, 0x34, 0x4C, 0x44, 0x4C, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x7C, 0x40, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x14, 0x10, 0x38, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x34, 0x4C, 0x44, 0x4C, 0x34, 0x04, 0x38, 0x00,
+ 0x00, 0x00, 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x40, 0x48, 0x50, 0x60, 0x50, 0x48, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x54, 0x54, 0x54, 0x54, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x78, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0x64, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3C, 0x40, 0x38, 0x04, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x20, 0x70, 0x20, 0x20, 0x24, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x4C, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x54, 0x54, 0x28, 0x28, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7C, 0x08, 0x10, 0x20, 0x7C, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x10, 0x10, 0x20, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x10, 0x10, 0x08, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x54, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00,
+};
+
+
+
+//-------------------------------------------------
+// ntsc_square_fontdata8x12
+//-------------------------------------------------
+
+const UINT8 mc6847_friend_device::ntsc_square_fontdata8x12[] =
+{
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x1A, 0x2A, 0x2A, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x12, 0x12, 0x1C, 0x12, 0x12, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x38, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x38, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x26, 0x22, 0x22, 0x1E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x24, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x10, 0x08, 0x04, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x3E, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x14, 0x36, 0x00, 0x36, 0x14, 0x14, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x32, 0x32, 0x04, 0x08, 0x10, 0x26, 0x26, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x28, 0x28, 0x10, 0x2A, 0x24, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3E, 0x1C, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x10, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x04, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x0C, 0x14, 0x3E, 0x04, 0x04, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x3C, 0x02, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x24, 0x04, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00,
+
+ /* Lower case */
+ 0x00, 0x00, 0x00, 0x0C, 0x12, 0x10, 0x38, 0x10, 0x12, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x1E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x20, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x3E, 0x20, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0C, 0x12, 0x10, 0x38, 0x10, 0x10, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x1C,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x24, 0x18,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x24, 0x28, 0x38, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x12, 0x0C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x26, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x2A, 0x2A, 0x1C, 0x14, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x1C,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x04, 0x08, 0x10, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x20, 0x10, 0x10, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x04, 0x3E, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00
+};
+
+
+
+//-------------------------------------------------
+// s68047_fontdata8x12
+//-------------------------------------------------
+
+const UINT8 mc6847_friend_device::s68047_fontdata8x12[] =
+{
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x2A, 0x2A, 0x2C, 0x20, 0x1E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x26, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x24, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x1C, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x3E, 0x10, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x14, 0x3E, 0x14, 0x3E, 0x14, 0x14, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x1E, 0x28, 0x1C, 0x0A, 0x3C, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x32, 0x32, 0x04, 0x08, 0x10, 0x26, 0x26, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x28, 0x28, 0x10, 0x2A, 0x24, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x2A, 0x1C, 0x2A, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x0C, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x0C, 0x14, 0x24, 0x3E, 0x04, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x3C, 0x02, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0E, 0x10, 0x20, 0x3C, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x04, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x08, 0x10, 0x20, 0x10, 0x08, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x08, 0x04, 0x02, 0x04, 0x08, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x02, 0x04, 0x08, 0x00, 0x08, 0x00, 0x00,
+
+ // No lower case, copy/paste of first 32 characters
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x2A, 0x2A, 0x2C, 0x20, 0x1E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x26, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x32, 0x32, 0x2A, 0x26, 0x26, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x24, 0x1A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x1C, 0x02, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3E, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x3E, 0x10, 0x08, 0x00, 0x00, 0x00,
+};
+
+
+
+//-------------------------------------------------
+// semigraphics4_fontdata8x12
+//-------------------------------------------------
+
+const UINT8 mc6847_friend_device::semigraphics4_fontdata8x12[] =
+{
+ /* Block Graphics (Semigraphics 4 Graphics ) */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+
+
+//-------------------------------------------------
+// semigraphics6_fontdata8x12
+//-------------------------------------------------
+
+const UINT8 mc6847_friend_device::semigraphics6_fontdata8x12[] =
+{
+ /* Semigraphics 6 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+
+
+//**************************************************************************
+// ARTIFACTING
+//**************************************************************************
+
+INPUT_PORTS_START(mc6847_artifacting)
+ PORT_START(ARTIFACTING_TAG)
+ PORT_CONFNAME( 0x03, 0x01, "Artifacting" )
+ PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
+ PORT_CONFSETTING( 0x01, DEF_STR( Standard ) )
+ PORT_CONFSETTING( 0x02, DEF_STR( Reverse ) )
+INPUT_PORTS_END
+
+ioport_constructor mc6847_base_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(mc6847_artifacting);
+}
+
+
+
+//-------------------------------------------------
+// ctor
+//-------------------------------------------------
+
+mc6847_base_device::artifacter::artifacter()
+{
+ m_config = NULL;
+ m_artifacting = 0;
+ m_saved_artifacting = 0;
+ m_saved_c0 = 0;
+ m_saved_c1 = 0;
+ memset(m_expanded_colors, 0, sizeof(m_expanded_colors));
+}
+
+
+
+//-------------------------------------------------
+// artifacter::setup_config
+//-------------------------------------------------
+
+void mc6847_base_device::artifacter::setup_config(device_t *device)
+{
+ char port_name[32];
+ snprintf(port_name, ARRAY_LENGTH(port_name), "%s:%s", device->tag(), ARTIFACTING_TAG);
+ m_config = device->ioport(port_name);
+}
+
+
+
+//-------------------------------------------------
+// artifacter::update_colors
+//-------------------------------------------------
+
+void mc6847_base_device::artifacter::update_colors(pixel_t c0, pixel_t c1)
+{
+ /* Boy this code sucks; this code was adapted from the old M6847
+ * artifacting implmentation. The only reason that it didn't look as
+ * horrible was because the code around it sucked as well. Now that I
+ * have cleaned everything up, the ugliness is much more prominent.
+ *
+ * Hopefully we will have a generic artifacting algorithm that plugs into
+ * the MESS/MAME core directly so we can chuck this hack */
+ static const double artifact_colors[14*3] =
+ {
+ 0.157, 0.000, 0.157, /* [ 1] - dk purple (reverse 2) */
+ 0.000, 0.157, 0.000, /* [ 2] - dk green (reverse 1) */
+ 1.000, 0.824, 1.000, /* [ 3] - lt purple (reverse 4) */
+ 0.824, 1.000, 0.824, /* [ 4] - lt green (reverse 3) */
+ 0.706, 0.236, 0.118, /* [ 5] - dk blue (reverse 6) */
+ 0.000, 0.197, 0.471, /* [ 6] - dk red (reverse 5) */
+ 1.000, 0.550, 0.393, /* [ 7] - lt blue (reverse 8) */
+ 0.275, 0.785, 1.000, /* [ 8] - lt red (reverse 7) */
+ 0.000, 0.500, 1.000, /* [ 9] - red (reverse 10) */
+ 1.000, 0.500, 0.000, /* [10] - blue (reverse 9) */
+ 1.000, 0.942, 0.785, /* [11] - cyan (reverse 12) */
+ 0.393, 0.942, 1.000, /* [12] - yellow (reverse 11) */
+ 0.236, 0.000, 0.000, /* [13] - black-blue (reverse 14) */
+ 0.000, 0.000, 0.236 /* [14] - black-red (reverse 13) */
+ };
+
+ static const UINT8 artifact_correction[128] =
+ {
+ 0, 0, 0, 0, 0, 6, 0, 2,
+ 5, 7, 5, 7, 1, 3, 1, 11,
+ 8, 6, 8, 14, 8, 9, 8, 9,
+ 4, 4, 4, 15, 12, 12, 12, 15,
+
+ 5, 13, 5, 13, 13, 0, 13, 2,
+ 10, 10, 10, 10, 10, 15, 10, 11,
+ 3, 1, 3, 1, 15, 9, 15, 9,
+ 11, 11, 11, 11, 15, 15, 15, 15,
+
+ 14, 0, 14, 0, 14, 6, 14, 2,
+ 0, 7, 0, 7, 1, 3, 1, 11,
+ 9, 6, 9, 14, 9, 9, 9, 9,
+ 15, 4, 15, 15, 12, 12, 12, 15,
+
+ 2, 13, 2, 13, 2, 0, 2, 2,
+ 10, 10, 10, 10, 10, 15, 10, 11,
+ 12, 1, 12, 1, 12, 9, 12, 9,
+ 15, 11, 15, 11, 15, 15, 15, 15
+ };
+
+ pixel_t colors[16];
+ int i;
+
+ /* do we need to update our artifact colors table? */
+ if ((m_artifacting != m_saved_artifacting) || (c0 != m_saved_c0) || (c1 != m_saved_c1))
+ {
+ m_saved_artifacting = m_artifacting;
+ m_saved_c0 = colors[0] = c0;
+ m_saved_c1 = colors[15] = c1;
+
+ /* mix the other colors */
+ for (i = 1; i <= 14; i++)
+ {
+ const double *factors = &artifact_colors[((i - 1) ^ (m_artifacting & 0x01)) * 3];
+
+ colors[i] = (mix_color(factors[0], c0 >> 16, c1 >> 16) << 16)
+ | (mix_color(factors[1], c0 >> 8, c1 >> 8) << 8)
+ | (mix_color(factors[2], c0 >> 0, c1 >> 0) << 0);
+ }
+ for (i = 0; i < 128; i++)
+ {
+ m_expanded_colors[i] = colors[artifact_correction[i]];
+ }
+ }
+}
+
+
+
+//-------------------------------------------------
+// artifacter::update
+//-------------------------------------------------
+
+mc6847_base_device::pixel_t mc6847_base_device::artifacter::mix_color(double factor, UINT8 c0, UINT8 c1)
+{
+ return (UINT32) (UINT8) ((c0 * (1.0 - factor)) + (c1 * (0.0 + factor)) + 0.5);
+}
+
+
+
+//**************************************************************************
+// VARIATIONS
+//**************************************************************************
+
+const device_type MC6847_NTSC = &device_creator<mc6847_ntsc_device>;
+const device_type MC6847_PAL = &device_creator<mc6847_pal_device>;
+const device_type MC6847Y_NTSC = &device_creator<mc6847y_ntsc_device>;
+const device_type MC6847Y_PAL = &device_creator<mc6847y_pal_device>;
+const device_type MC6847T1_NTSC = &device_creator<mc6847t1_ntsc_device>;
+const device_type MC6847T1_PAL = &device_creator<mc6847t1_pal_device>;
+const device_type S68047 = &device_creator<s68047_device>;
+const device_type M5C6847P1 = &device_creator<m5c6847p1_device>;
+
+
+
+//-------------------------------------------------
+// mc6847_ntsc_device
+//-------------------------------------------------
+
+mc6847_ntsc_device::mc6847_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6847_base_device(mconfig, MC6847_NTSC, "MC6847_NTSC", tag, owner, clock, ntsc_square_fontdata8x12, 262.0, "mc6847_ntsc", __FILE__)
+{
+}
+
+
+
+//-------------------------------------------------
+// mc6847_pal_device
+//-------------------------------------------------
+
+mc6847_pal_device::mc6847_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6847_base_device(mconfig, MC6847_PAL, "MC6847_PAL", tag, owner, clock, pal_square_fontdata8x12, 313.0, "mc6847_pal", __FILE__)
+{
+}
+
+
+
+//-------------------------------------------------
+// mc6847y_ntsc_device
+//-------------------------------------------------
+
+mc6847y_ntsc_device::mc6847y_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6847_base_device(mconfig, MC6847Y_NTSC, "MC6847Y_NTSC", tag, owner, clock, ntsc_square_fontdata8x12, 262.5, "mc6847y", __FILE__)
+{
+}
+
+
+
+//-------------------------------------------------
+// mc6847y_pal_device
+//-------------------------------------------------
+
+mc6847y_pal_device::mc6847y_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6847_base_device(mconfig, MC6847Y_PAL, "MC6847Y_PAL", tag, owner, clock, pal_square_fontdata8x12, 313.0, "mc6847y_pal", __FILE__)
+{
+}
+
+
+
+//-------------------------------------------------
+// mc6847t1_ntsc_device
+//-------------------------------------------------
+
+mc6847t1_ntsc_device::mc6847t1_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6847_base_device(mconfig, MC6847T1_NTSC, "MC6847T1_NTSC", tag, owner, clock, ntsc_round_fontdata8x12, 262.0, "mc6847t1_ntsc", __FILE__)
+{
+}
+
+
+
+//-------------------------------------------------
+// mc6847t1_pal_device
+//-------------------------------------------------
+
+mc6847t1_pal_device::mc6847t1_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6847_base_device(mconfig, MC6847T1_PAL, "MC6847T1_PAL", tag, owner, clock, pal_round_fontdata8x12, 313.0, "mc6847t1_pal", __FILE__)
+{
+}
+
+
+
+//-------------------------------------------------
+// s68047_device
+//-------------------------------------------------
+
+s68047_device::s68047_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6847_base_device(mconfig, S68047, "S68047", tag, owner, clock, s68047_fontdata8x12, 262.0, "s68047", __FILE__)
+{
+}
+
+
+//
+// In the Bandai Super Vision 8000 there is a video setting
+// bit which causes black to be displayed as blue when css=1.
+//
+// This is probably done through circuitry outside the s68047,
+// but lacking schematics we don't know how it is hooked up
+// exactly.
+//
+// See https://www.youtube.com/watch?v=QCo24GLyff4
+//
+void s68047_device::hack_black_becomes_blue(bool flag)
+{
+ set_custom_palette( flag ? s_s68047_hack_palette : NULL );
+}
+
+const UINT32 s68047_device::s_s68047_hack_palette[16] =
+{
+ rgb_t(0x07, 0xff, 0x00), /* GREEN */
+ rgb_t(0xff, 0xff, 0x00), /* YELLOW */
+ rgb_t(0x3b, 0x08, 0xff), /* BLUE */
+ rgb_t(0xcc, 0x00, 0x3b), /* RED */
+ rgb_t(0xff, 0xff, 0xff), /* BUFF */
+ rgb_t(0x07, 0xe3, 0x99), /* CYAN */
+ rgb_t(0xff, 0x1c, 0xff), /* MAGENTA */
+ rgb_t(0xff, 0x81, 0x00), /* ORANGE */
+
+ rgb_t(0x00, 0x00, 0x00), /* BLACK */
+ rgb_t(0x07, 0xff, 0x00), /* GREEN */
+ rgb_t(0x3b, 0x08, 0xff), /* BLUE */
+ rgb_t(0xff, 0xff, 0xff), /* BUFF */
+
+ rgb_t(0x00, 0x7c, 0x00), /* ALPHANUMERIC DARK GREEN */
+ rgb_t(0x07, 0xff, 0x00), /* ALPHANUMERIC BRIGHT GREEN */
+ rgb_t(0x91, 0x00, 0x00), /* ALPHANUMERIC DARK ORANGE */
+ rgb_t(0xff, 0x81, 0x00) /* ALPHANUMERIC BRIGHT ORANGE */
+};
+
+
+
+//-------------------------------------------------
+// m5c6847p1_device
+//-------------------------------------------------
+
+m5c6847p1_device::m5c6847p1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mc6847_base_device(mconfig, M5C6847P1, "M5C6847P-1", tag, owner, clock, ntsc_square_fontdata8x12, 262.5, "m5c6847p1", __FILE__)
+{
+}
diff --git a/src/devices/video/mc6847.h b/src/devices/video/mc6847.h
new file mode 100644
index 00000000000..d3420a7fcba
--- /dev/null
+++ b/src/devices/video/mc6847.h
@@ -0,0 +1,706 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/*********************************************************************
+
+ mc6847.h
+
+ Implementation of Motorola 6847 video hardware chip
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __MC6847__
+#define __MC6847__
+
+
+#define MC6847_MODE_AG 0x80
+#define MC6847_MODE_GM2 0x40
+#define MC6847_MODE_GM1 0x20
+#define MC6847_MODE_GM0 0x10
+#define MC6847_MODE_CSS 0x08
+#define MC6847_MODE_AS 0x04
+#define MC6847_MODE_INTEXT 0x02
+#define MC6847_MODE_INV 0x01
+
+
+//**************************************************************************
+// MC6847 CONFIGURATION / INTERFACE
+//**************************************************************************
+
+#define MCFG_SCREEN_MC6847_NTSC_ADD(_tag, _mctag) \
+ MCFG_SCREEN_ADD(_tag, RASTER) \
+ MCFG_SCREEN_UPDATE_DEVICE(_mctag, mc6847_base_device, screen_update) \
+ MCFG_SCREEN_REFRESH_RATE(60) \
+ MCFG_SCREEN_SIZE(320, 243) \
+ MCFG_SCREEN_VISIBLE_AREA(0, 320-1, 1, 241-1) \
+ MCFG_SCREEN_VBLANK_TIME(0)
+
+#define MCFG_SCREEN_MC6847_PAL_ADD(_tag, _mctag) \
+ MCFG_SCREEN_ADD(_tag, RASTER) \
+ MCFG_SCREEN_UPDATE_DEVICE(_mctag, mc6847_base_device, screen_update) \
+ MCFG_SCREEN_REFRESH_RATE(50) \
+ MCFG_SCREEN_SIZE(320, 243) \
+ MCFG_SCREEN_VISIBLE_AREA(0, 320-1, 1, 241-1) \
+ MCFG_SCREEN_VBLANK_TIME(0)
+
+#define MCFG_MC6847_HSYNC_CALLBACK(_write) \
+ devcb = &mc6847_friend_device::set_hsync_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_MC6847_FSYNC_CALLBACK(_write) \
+ devcb = &mc6847_friend_device::set_fsync_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_MC6847_CHARROM_CALLBACK(_class, _method) \
+ mc6847_friend_device::set_get_char_rom(*device, mc6847_get_char_rom_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_MC6847_INPUT_CALLBACK(_read) \
+ devcb = &mc6847_base_device::set_input_callback(*device, DEVCB_##_read);
+
+#define MCFG_MC6847_FIXED_MODE(_mode) \
+ mc6847_base_device::set_get_fixed_mode(*device, _mode);
+
+#define MCFG_MC6847_BW(_bw) \
+ mc6847_base_device::set_black_and_white(*device, _bw);
+
+
+typedef device_delegate<UINT8 (UINT8 ch, int line)> mc6847_get_char_rom_delegate;
+#define MC6847_GET_CHARROM_MEMBER(_name) UINT8 _name(UINT8 ch, int line)
+
+
+#define ARTIFACTING_TAG "artifacting"
+
+INPUT_PORTS_EXTERN(mc6847_artifacting);
+
+
+//**************************************************************************
+// MC6847 CORE
+//**************************************************************************
+
+// base class so that the GIME emulation can access mc6847 stuff
+class mc6847_friend_device : public device_t
+{
+public:
+ // inlines
+ bool hs_r(void) { return m_horizontal_sync; }
+ bool fs_r(void) { return m_field_sync; }
+
+ template<class _Object> static devcb_base &set_hsync_wr_callback(device_t &device, _Object object) { return downcast<mc6847_friend_device &>(device).m_write_hsync.set_callback(object); }
+ template<class _Object> static devcb_base &set_fsync_wr_callback(device_t &device, _Object object) { return downcast<mc6847_friend_device &>(device).m_write_fsync.set_callback(object); }
+
+ static void set_get_char_rom(device_t &device, mc6847_get_char_rom_delegate callback) { downcast<mc6847_friend_device &>(device).m_charrom_cb = callback; }
+
+protected:
+ mc6847_friend_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock,
+ const UINT8 *fontdata, bool is_mc6847t1, double tpfs, int field_sync_falling_edge_scanline, bool supports_partial_body_scanlines, const char *shortname, const char *source);
+
+ // video mode constants
+ static const UINT8 MODE_AG = 0x80;
+ static const UINT8 MODE_GM2 = 0x40;
+ static const UINT8 MODE_GM1 = 0x20;
+ static const UINT8 MODE_GM0 = 0x10;
+ static const UINT8 MODE_CSS = 0x08;
+ static const UINT8 MODE_AS = 0x04;
+ static const UINT8 MODE_INTEXT = 0x02;
+ static const UINT8 MODE_INV = 0x01;
+
+ // timer constants
+ static const device_timer_id TIMER_FRAME = 0;
+ static const device_timer_id TIMER_HSYNC_OFF = 1;
+ static const device_timer_id TIMER_HSYNC_ON = 2;
+ static const device_timer_id TIMER_FSYNC = 3;
+
+ // fonts
+ static const UINT8 pal_round_fontdata8x12[];
+ static const UINT8 pal_square_fontdata8x12[];
+ static const UINT8 ntsc_round_fontdata8x12[];
+ static const UINT8 ntsc_square_fontdata8x12[];
+ static const UINT8 semigraphics4_fontdata8x12[];
+ static const UINT8 semigraphics6_fontdata8x12[];
+ static const UINT8 s68047_fontdata8x12[];
+
+ // pixel definitions
+ typedef UINT32 pixel_t;
+
+ pixel_t *bitmap_addr(bitmap_rgb32 &bitmap, int y, int x)
+ {
+ return &bitmap.pix32(y, x);
+ }
+
+ static UINT8 simplify_mode(UINT8 data, UINT8 mode)
+ {
+ // simplifies MC6847 modes to drop mode flags that are not significant
+ return mode & ~((mode & MODE_AG) ? (MODE_AS | MODE_INV) : 0);
+ }
+
+ // internal class that represents a MC6847 character map
+ class character_map
+ {
+ public:
+ // constructor that sets up the font data
+ character_map(const UINT8 *fontdata, bool is_mc6847t1);
+
+ // optimized template function that emits a single character
+ template<int xscale>
+ ATTR_FORCE_INLINE void emit_character(UINT8 mode, const UINT8 *data, int length, pixel_t *RESTRICT pixels, int y, const pixel_t *palette)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ // get the character
+ UINT8 character = data[i];
+
+ // based on the mode, determine which entry to use
+ const entry *e = &m_entries[mode % ARRAY_LENGTH(m_entries)];
+
+ // identify the character in the font data
+ const UINT8 *font_character = e->m_fontdata + (character & e->m_character_mask) * 12;
+
+ // get the particular slice out
+ UINT8 font_character_slice = font_character[y % 12];
+
+ // get the two colors
+ UINT16 color_base_0 = e->m_color_base_0 + ((character >> e->m_color_shift_0) & e->m_color_mask_0);
+ UINT16 color_base_1 = e->m_color_base_1 + ((character >> e->m_color_shift_1) & e->m_color_mask_1);
+ pixel_t color_0 = palette[color_base_0];
+ pixel_t color_1 = palette[color_base_1];
+
+ // emit the bits
+ for (int j = 0; j < 8; j++)
+ {
+ for (int k = 0; k < xscale; k++)
+ {
+ pixels[(i * 8 + j) * xscale + k] = bit_test(font_character_slice, j, color_0, color_1);
+ }
+ }
+ }
+ }
+
+ private:
+ struct entry
+ {
+ const UINT8 *m_fontdata;
+ UINT8 m_character_mask;
+ UINT8 m_color_shift_0;
+ UINT8 m_color_shift_1;
+ UINT8 m_color_mask_0;
+ UINT8 m_color_mask_1;
+ UINT16 m_color_base_0;
+ UINT16 m_color_base_1;
+ };
+
+ // lookup table for MC6847 modes to determine font data and color
+ entry m_entries[128];
+
+ // text font data calculated on startup
+ UINT8 m_text_fontdata_inverse[64*12];
+ UINT8 m_text_fontdata_lower_case[64*12];
+ UINT8 m_text_fontdata_lower_case_inverse[64*12];
+
+ // optimized function that tests a single bit
+ ATTR_FORCE_INLINE pixel_t bit_test(UINT8 data, int shift, pixel_t color_0, pixel_t color_1)
+ {
+ return data & (0x80 >> shift) ? color_1 : color_0;
+ }
+ };
+
+ // artficater internal class
+ class artifacter
+ {
+ public:
+ artifacter();
+
+ // artifacting config
+ void setup_config(device_t *device);
+ void poll_config(void) { m_artifacting = (m_config!=NULL) ? m_config->read() : 0; }
+
+ // artifacting application
+ template<int xscale>
+ ATTR_FORCE_INLINE void process_artifacts(pixel_t *pixels, UINT8 mode, const pixel_t *palette)
+ {
+ if (((mode & (MODE_AG|MODE_GM2|MODE_GM1|MODE_GM0)) == (MODE_AG|MODE_GM2|MODE_GM1|MODE_GM0))
+ && (m_artifacting != 0))
+ {
+ // identify the new colors and update
+ pixel_t c0 = palette[(mode & MODE_CSS) ? 10 : 8];
+ pixel_t c1 = palette[(mode & MODE_CSS) ? 11 : 9];
+ update_colors(c0, c1);
+
+ // generate the new line
+ pixel_t new_line[256];
+ int x, i;
+ for (x = 0; x < 256; x += 2)
+ {
+ UINT8 val = ((pixels[(x - 2) * xscale] == c1) ? 0x20 : 0x00)
+ | ((pixels[(x - 1) * xscale] == c1) ? 0x10 : 0x00)
+ | ((pixels[(x + 0) * xscale] == c1) ? 0x08 : 0x00)
+ | ((pixels[(x + 1) * xscale] == c1) ? 0x04 : 0x00)
+ | ((pixels[(x + 2) * xscale] == c1) ? 0x02 : 0x00)
+ | ((pixels[(x + 3) * xscale] == c1) ? 0x01 : 0x00);
+
+ new_line[x + 0] = m_expanded_colors[val * 2 + 0];
+ new_line[x + 1] = m_expanded_colors[val * 2 + 1];
+ }
+
+ // and copy it in
+ for (x = 0; x < 256; x++)
+ {
+ for (i = 0; i < xscale; i++)
+ pixels[x * xscale + i] = new_line[x];
+ }
+ }
+ }
+
+ private:
+ ioport_port *m_config;
+ ioport_value m_artifacting;
+ ioport_value m_saved_artifacting;
+ pixel_t m_saved_c0, m_saved_c1;
+ pixel_t m_expanded_colors[128];
+
+ void update_colors(pixel_t c0, pixel_t c1);
+ static pixel_t mix_color(double factor, UINT8 c0, UINT8 c1);
+ };
+
+ enum border_color_t
+ {
+ BORDER_COLOR_BLACK,
+ BORDER_COLOR_GREEN,
+ BORDER_COLOR_WHITE,
+ BORDER_COLOR_ORANGE
+ };
+
+ // callbacks
+ devcb_write_line m_write_hsync;
+ devcb_write_line m_write_fsync;
+
+ /* if specified, this reads the external char rom off of the driver state */
+ // moved here from mc6847_base_device so to be useable in GIME
+ mc6847_get_char_rom_delegate m_charrom_cb;
+
+ // incidentals
+ character_map m_character_map;
+ artifacter m_artifacter;
+
+ // device-level overrides
+ virtual void device_start(void);
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual void device_reset(void);
+ virtual void device_post_load(void);
+
+ // other overridables
+ virtual void new_frame(void);
+ virtual void horizontal_sync_changed(bool line);
+ virtual void field_sync_changed(bool line);
+ virtual void enter_bottom_border(void);
+ virtual void record_border_scanline(UINT16 physical_scanline);
+ virtual void record_body_scanline(UINT16 physical_scanline, UINT16 logical_scanline) = 0;
+ virtual void record_partial_body_scanline(UINT16 physical_scanline, UINT16 logical_scanline, INT32 start_clock, INT32 end_clock) = 0;
+
+ // miscellaneous
+ void video_flush(void);
+ const char *describe_context(void);
+
+ // setup functions
+ emu_timer *setup_timer(device_timer_id id, double offset, double period);
+
+ // converts to B&W
+ static pixel_t black_and_white(rgb_t color)
+ {
+ UINT8 average_color = (color.r() + color.g() + color.b()) / 3;
+ return rgb_t(average_color, average_color, average_color);
+ }
+
+ // changes the geometry
+ ATTR_FORCE_INLINE void set_geometry(UINT16 top_border_scanlines, UINT16 body_scanlines, bool wide)
+ {
+ if (UNEXPECTED((m_top_border_scanlines != top_border_scanlines)
+ || (m_body_scanlines != body_scanlines)
+ || (m_wide != wide)))
+ {
+ m_top_border_scanlines = top_border_scanlines;
+ m_body_scanlines = body_scanlines;
+ m_wide = wide;
+ update_field_sync_timer();
+ }
+ }
+
+ // calculates the border color
+ static ATTR_FORCE_INLINE border_color_t border_value(UINT8 mode, bool is_mc6847t1)
+ {
+ border_color_t result;
+
+ if (mode & MODE_AG)
+ {
+ // graphics
+ result = mode & MODE_CSS ? BORDER_COLOR_WHITE : BORDER_COLOR_GREEN;
+ }
+ else if (!is_mc6847t1 || ((mode & MODE_GM2) == 0))
+ {
+ // text, black border
+ result = BORDER_COLOR_BLACK;
+ }
+ else
+ {
+ // text, green or orange border
+ result = mode & MODE_CSS ? BORDER_COLOR_ORANGE : BORDER_COLOR_GREEN;
+ }
+ return result;
+ }
+
+ // checks to see if the video has changed
+ ATTR_FORCE_INLINE bool has_video_changed(void)
+ {
+ /* poll the artifacting config */
+ m_artifacter.poll_config();
+
+ /* if the video didn't change, indicate as much */
+ bool video_changed = m_video_changed;
+ m_video_changed = false;
+ return video_changed;
+ }
+
+ // updates a byte in the video state
+ template<class T>
+ ATTR_FORCE_INLINE bool update_value(T *ptr, T byte)
+ {
+ bool result = false;
+ if (*ptr != byte)
+ {
+ *ptr = byte;
+ m_video_changed = true;
+ result = true;
+ }
+ return result;
+ }
+
+ // template function for emitting graphics bytes
+ template<int bits_per_pixel, int xscale>
+ ATTR_FORCE_INLINE void emit_graphics(const UINT8 *data, int length, pixel_t *RESTRICT pixels, UINT16 color_base, const pixel_t *RESTRICT palette)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ for (int j = 0; j < (8 / bits_per_pixel); j++)
+ {
+ for (int k = 0; k < xscale; k++)
+ {
+ UINT16 color = color_base + ((data[i] >> (8 - (j + 1) * bits_per_pixel)) & ((1 << bits_per_pixel) - 1));
+ pixels[(i * (8 / bits_per_pixel) + j) * xscale + k] = palette[color];
+ }
+ }
+ }
+ }
+
+ // template function for external bytes
+ template<int bits_per_pixel, int xscale>
+ ATTR_FORCE_INLINE void emit_extbytes(const UINT8 *data, int length, pixel_t *RESTRICT pixels, UINT16 color_base, const pixel_t *RESTRICT palette)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ for (int j = 0; j < (8 / bits_per_pixel); j++)
+ {
+ for (int k = 0; k < xscale; k++)
+ {
+ UINT16 color = color_base + BIT(data[i], 7-j);
+ pixels[(i * (8 / bits_per_pixel) + j) * xscale + k] = palette[color];
+ }
+ }
+ }
+ }
+
+ // template function for emitting samples
+ template<int xscale>
+ UINT32 emit_mc6847_samples(UINT8 mode, const UINT8 *data, int length, pixel_t *RESTRICT pixels, const pixel_t *RESTRICT palette,
+ mc6847_get_char_rom_delegate get_char_rom, int x, int y)
+ {
+ UINT32 result = 0;
+ if (mode & MODE_AG)
+ {
+ /* graphics */
+ switch(mode & (MODE_GM2|MODE_GM1|MODE_GM0))
+ {
+ case 0:
+ emit_graphics<2, xscale * 4>(data, length, pixels, (mode & MODE_CSS) ? 4 : 0, palette);
+ result = length * 8 * xscale * 2;
+ break;
+
+ case MODE_GM0:
+ case MODE_GM1|MODE_GM0:
+ case MODE_GM2|MODE_GM0:
+ emit_graphics<1, xscale * 2>(data, length, pixels, (mode & MODE_CSS) ? 10 : 8, palette);
+ result = length * 8 * xscale * 2;
+ break;
+
+ case MODE_GM1:
+ case MODE_GM2:
+ case MODE_GM2|MODE_GM1:
+ emit_graphics<2, xscale * 2>(data, length, pixels, (mode & MODE_CSS) ? 4 : 0, palette);
+ result = length * 8 * xscale;
+ break;
+
+ case MODE_GM2|MODE_GM1|MODE_GM0:
+ emit_graphics<1, xscale * 1>(data, length, pixels, (mode & MODE_CSS) ? 10 : 8, palette);
+ result = length * 8 * xscale;
+ break;
+
+ default:
+ /* should not get here */
+ fatalerror("Should not get here\n");
+ break;
+ }
+ }
+ else if (!get_char_rom.isnull() && ((mode & (MODE_AG|MODE_AS|MODE_INTEXT)) == MODE_INTEXT))
+ {
+ /* external ROM */
+ for (int i = 0; i < length; i++)
+ {
+ UINT8 byte = get_char_rom(data[i], y % 12) ^ (mode & MODE_INV ? 0xFF : 0x00);
+ emit_extbytes<1, xscale>(&byte, 1, &pixels[i * 8], (mode & MODE_CSS) ? 14 : 12, palette);
+ }
+ result = length * 8 * xscale;
+ }
+ else
+ {
+ /* text/semigraphics */
+ m_character_map.emit_character<xscale>(mode, data, length, pixels, y, palette);
+ result = length * 8 * xscale;
+ }
+ return result;
+ }
+
+private:
+ enum scanline_zone
+ {
+ SCANLINE_ZONE_TOP_BORDER,
+ SCANLINE_ZONE_BODY,
+ SCANLINE_ZONE_BOTTOM_BORDER,
+ SCANLINE_ZONE_RETRACE,
+ SCANLINE_ZONE_VBLANK,
+ SCANLINE_ZONE_FRAME_END
+ };
+
+ // timers
+ emu_timer *m_frame_timer;
+ emu_timer *m_hsync_on_timer;
+ emu_timer *m_hsync_off_timer;
+ emu_timer *m_fsync_timer;
+
+ // incidentals
+ double m_tpfs;
+ int m_field_sync_falling_edge_scanline;
+ bool m_wide;
+ bool m_video_changed;
+ UINT16 m_top_border_scanlines;
+ UINT16 m_body_scanlines;
+ bool m_recording_scanline;
+ bool m_supports_partial_body_scanlines;
+
+ // video state
+ UINT16 m_physical_scanline;
+ UINT16 m_logical_scanline;
+ UINT16 m_logical_scanline_zone;
+ bool m_horizontal_sync;
+ bool m_field_sync;
+ UINT32 m_partial_scanline_clocks;
+
+ // functions
+ void change_horizontal_sync(bool line);
+ void change_field_sync(bool line);
+ void update_field_sync_timer(void);
+ void next_scanline(void);
+ INT32 get_clocks_since_hsync();
+
+ // debugging
+ const char *scanline_zone_string(scanline_zone zone);
+};
+
+// actual base class for MC6847 family of devices
+class mc6847_base_device : public mc6847_friend_device
+{
+public:
+ template<class _Object> static devcb_base &set_input_callback(device_t &device, _Object object) { return downcast<mc6847_base_device &>(device).m_input_cb.set_callback(object); }
+
+ static void set_get_fixed_mode(device_t &device, UINT8 mode) { downcast<mc6847_base_device &>(device).m_fixed_mode = mode; }
+ static void set_black_and_white(device_t &device, bool bw) { downcast<mc6847_base_device &>(device).m_black_and_white = bw; }
+
+ /* updates the screen -- this will call begin_update(),
+ followed by update_row() reapeatedly and after all row
+ updating is complete, end_update() */
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ // mode changing operations
+ DECLARE_WRITE_LINE_MEMBER( ag_w ) { change_mode(MODE_AG, state); }
+ DECLARE_WRITE_LINE_MEMBER( gm2_w ) { change_mode(MODE_GM2, state); }
+ DECLARE_WRITE_LINE_MEMBER( gm1_w ) { change_mode(MODE_GM1, state); }
+ DECLARE_WRITE_LINE_MEMBER( gm0_w ) { change_mode(MODE_GM0, state); }
+ DECLARE_WRITE_LINE_MEMBER( as_w ) { change_mode(MODE_AS, state); }
+ DECLARE_WRITE_LINE_MEMBER( css_w ) { change_mode(MODE_CSS, state); }
+ DECLARE_WRITE_LINE_MEMBER( intext_w ) { change_mode(MODE_INTEXT, state); }
+ DECLARE_WRITE_LINE_MEMBER( inv_w ) { change_mode(MODE_INV, state); }
+
+protected:
+ mc6847_base_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const UINT8 *fontdata, double tpfs, const char *shortname, const char *source);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual ioport_constructor device_input_ports() const;
+
+ // other overrides
+ virtual void field_sync_changed(bool line);
+ virtual void record_body_scanline(UINT16 physical_scanline, UINT16 scanline);
+ virtual void record_partial_body_scanline(UINT16 physical_scanline, UINT16 logical_scanline, INT32 start_clock, INT32 end_clock);
+
+ void set_custom_palette(const pixel_t *custom_palette)
+ {
+ if ( m_palette != m_bw_palette )
+ {
+ m_palette = custom_palette ? custom_palette : s_palette;
+ }
+ }
+
+private:
+ struct video_scanline
+ {
+ UINT8 m_sample_count;
+ UINT8 m_mode[32];
+ UINT8 m_data[32];
+ };
+
+ // palette
+ static const int PALETTE_LENGTH = 16;
+ static const UINT32 s_palette[PALETTE_LENGTH];
+
+ // callbacks
+
+ /* if specified, this gets called whenever reading a byte (offs_t ~0 specifies DA* entering the tristate mode) */
+ devcb_read8 m_input_cb;
+
+ /* if true, this is black and white */
+ bool m_black_and_white;
+
+ // incidentals
+ UINT8 m_fixed_mode;
+ UINT8 m_fixed_mode_mask;
+ const pixel_t *m_palette;
+ pixel_t m_bw_palette[PALETTE_LENGTH];
+
+ // state
+ UINT8 m_mode;
+ UINT16 m_video_address;
+ bool m_dirty;
+ video_scanline m_data[192];
+
+ void change_mode(UINT8 mode, int state)
+ {
+ // sanity check, to ensure that we're not changing fixed modes
+ assert((mode & m_fixed_mode_mask) == 0);
+
+ // calculate new mode
+ UINT8 new_mode;
+ if (state)
+ new_mode = m_mode | mode;
+ else
+ new_mode = m_mode & ~mode;
+
+ // has the mode changed?
+ if (new_mode != m_mode)
+ {
+ // it has! check dirty flag
+ video_flush();
+ if (!m_dirty)
+ {
+ m_dirty = true;
+ }
+
+ // and set the new mode
+ m_mode = new_mode;
+ }
+ }
+
+ // setup functions
+ void setup_fixed_mode();
+
+ // runtime functions
+ void record_body_scanline(UINT16 physical_scanline, UINT16 scanline, INT32 start_pos, INT32 end_pos);
+ pixel_t border_value(UINT8 mode, const pixel_t *palette, bool is_mc6847t1);
+
+ template<int xscale>
+ void emit_samples(UINT8 mode, const UINT8 *data, int length, pixel_t *pixels, int x, int y);
+
+ // template function for doing video update collection
+ template<int sample_count, int yres>
+ void record_scanline_res(int scanline, INT32 start_pos, INT32 end_pos);
+
+ // miscellaneous
+ UINT8 input(UINT16 address);
+ INT32 scanline_position_from_clock(INT32 clocks_since_hsync);
+};
+
+
+//**************************************************************************
+// VARIATIONS
+//**************************************************************************
+
+class mc6847_ntsc_device : public mc6847_base_device
+{
+public:
+ mc6847_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class mc6847_pal_device : public mc6847_base_device
+{
+public:
+ mc6847_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class mc6847y_ntsc_device : public mc6847_base_device
+{
+public:
+ mc6847y_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class mc6847y_pal_device : public mc6847_base_device
+{
+public:
+ mc6847y_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class mc6847t1_ntsc_device : public mc6847_base_device
+{
+public:
+ mc6847t1_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class mc6847t1_pal_device : public mc6847_base_device
+{
+public:
+ mc6847t1_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class s68047_device : public mc6847_base_device
+{
+public:
+ s68047_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void hack_black_becomes_blue(bool flag);
+
+private:
+ static const UINT32 s_s68047_hack_palette[16];
+};
+
+class m5c6847p1_device : public mc6847_base_device
+{
+public:
+ m5c6847p1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+extern const device_type MC6847_NTSC;
+extern const device_type MC6847_PAL;
+extern const device_type MC6847Y_NTSC;
+extern const device_type MC6847Y_PAL;
+extern const device_type MC6847T1_NTSC;
+extern const device_type MC6847T1_PAL;
+extern const device_type S68047;
+extern const device_type M5C6847P1;
+
+#endif /* __MC6847__ */
diff --git a/src/devices/video/mos6566.c b/src/devices/video/mos6566.c
new file mode 100644
index 00000000000..64b061c3107
--- /dev/null
+++ b/src/devices/video/mos6566.c
@@ -0,0 +1,2839 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/***************************************************************************
+
+ MOS 6566/6567/6569 Video Interface Chip (VIC-II) emulation
+
+ A part of the code (cycle routine and drawing routines) is a modified version of the vic ii emulation used in
+ commodore 64 emulator "frodo" by Christian Bauer
+
+ http://frodo.cebix.net/
+ The rights on the source code remain at the author.
+ It may not - not even in parts - used for commercial purposes without explicit written permission by the author.
+ Permission to use it for non-commercial purposes is hereby granted als long as my copyright notice remains in the program.
+ You are not allowed to use the source to create and distribute a modified version of Frodo.
+
+***************************************************************************/
+
+/*
+
+ TODO:
+
+ - cleanup
+ - http://hitmen.c02.at/temp/palstuff/
+
+*/
+
+#include "mos6566.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+enum
+{
+ REGISTER_M0X = 0,
+ REGISTER_M0Y,
+ REGISTER_M1X,
+ REGISTER_M1Y,
+ REGISTER_M2X,
+ REGISTER_M2Y,
+ REGISTER_M3X,
+ REGISTER_M3Y,
+ REGISTER_M4X,
+ REGISTER_M4Y,
+ REGISTER_M5X,
+ REGISTER_M5Y,
+ REGISTER_M6X,
+ REGISTER_M6Y,
+ REGISTER_M7X,
+ REGISTER_M7Y,
+ REGISTER_MX_MSB,
+ REGISTER_CR1,
+ REGISTER_RASTER,
+ REGISTER_LPX,
+ REGISTER_LPY,
+ REGISTER_ME,
+ REGISTER_CR2,
+ REGISTER_MYE,
+ REGISTER_MP,
+ REGISTER_IRQ,
+ REGISTER_IE,
+ REGISTER_MDP,
+ REGISTER_MMC,
+ REGISTER_MXE,
+ REGISTER_MM,
+ REGISTER_MD,
+ REGISTER_EC,
+ REGISTER_B0C,
+ REGISTER_B1C,
+ REGISTER_B2C,
+ REGISTER_B3C,
+ REGISTER_MM0,
+ REGISTER_MM1,
+ REGISTER_M0C,
+ REGISTER_M1C,
+ REGISTER_M2C,
+ REGISTER_M3C,
+ REGISTER_M4C,
+ REGISTER_M5C,
+ REGISTER_M6C,
+ REGISTER_M7C,
+ REGISTER_KCR,
+ REGISTER_FAST
+};
+
+static int UNUSED_BITS[0x40] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x01, 0x70, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+// VICE palette
+static const rgb_t PALETTE_MOS[] =
+{
+ rgb_t(0x00, 0x00, 0x00),
+ rgb_t(0xfd, 0xfe, 0xfc),
+ rgb_t(0xbe, 0x1a, 0x24),
+ rgb_t(0x30, 0xe6, 0xc6),
+ rgb_t(0xb4, 0x1a, 0xe2),
+ rgb_t(0x1f, 0xd2, 0x1e),
+ rgb_t(0x21, 0x1b, 0xae),
+ rgb_t(0xdf, 0xf6, 0x0a),
+ rgb_t(0xb8, 0x41, 0x04),
+ rgb_t(0x6a, 0x33, 0x04),
+ rgb_t(0xfe, 0x4a, 0x57),
+ rgb_t(0x42, 0x45, 0x40),
+ rgb_t(0x70, 0x74, 0x6f),
+ rgb_t(0x59, 0xfe, 0x59),
+ rgb_t(0x5f, 0x53, 0xfe),
+ rgb_t(0xa4, 0xa7, 0xa2)
+};
+
+
+#define VERBOSE_LEVEL 0
+#define DBG_LOG(N,M,A) \
+ do { \
+ if(VERBOSE_LEVEL >= N) \
+ { \
+ if( M ) \
+ logerror("%11.6f: %-24s", machine().time().as_double(), (char*) M ); \
+ logerror A; \
+ } \
+ } while (0)
+
+#define IS_PAL ((m_variant == TYPE_6569) || (m_variant == TYPE_6572) || (m_variant == TYPE_6573) || (m_variant == TYPE_8565) || (m_variant == TYPE_8565) || (m_variant == TYPE_8569))
+#define IS_VICIIE ((m_variant == TYPE_8564) || (m_variant == TYPE_8566) || (m_variant == TYPE_8569))
+
+#define ROW25_YSTART 0x33
+#define ROW25_YSTOP 0xfb
+#define ROW24_YSTART 0x37
+#define ROW24_YSTOP 0xf7
+
+#define RASTERLINE_2_C64(a) (a)
+#define C64_2_RASTERLINE(a) (a)
+#define XPOS (VIC2_STARTVISIBLECOLUMNS + (VIC2_VISIBLECOLUMNS - VIC2_HSIZE) / 2)
+#define YPOS (VIC2_STARTVISIBLELINES /* + (VIC2_VISIBLELINES - VIC2_VSIZE) / 2 */)
+#define FIRSTCOLUMN 50
+
+/* 2008-05 FP: lightpen code needs to read input port from c64.c and cbmb.c */
+
+#define LIGHTPEN_BUTTON (m_in_lightpen_button_func(0))
+#define LIGHTPEN_X_VALUE (m_in_lightpen_x_func(0))
+#define LIGHTPEN_Y_VALUE (m_in_lightpen_y_func(0))
+
+/* lightpen delivers values from internal counters; they do not start with the visual area or frame area */
+#define VIC2_MAME_XPOS 0
+#define VIC2_MAME_YPOS 0
+#define VIC6567_X_BEGIN 38
+#define VIC6567_Y_BEGIN -6 /* first 6 lines after retrace not for lightpen! */
+#define VIC6569_X_BEGIN 38
+#define VIC6569_Y_BEGIN -6
+#define VIC2_X_BEGIN (IS_PAL ? VIC6569_X_BEGIN : VIC6567_X_BEGIN)
+#define VIC2_Y_BEGIN (IS_PAL ? VIC6569_Y_BEGIN : VIC6567_Y_BEGIN)
+#define VIC2_X_VALUE ((LIGHTPEN_X_VALUE / 1.3) + 12)
+#define VIC2_Y_VALUE ((LIGHTPEN_Y_VALUE ) + 10)
+
+/* sprites 0 .. 7 */
+#define SPRITEON(nr) (m_reg[0x15] & (1 << nr))
+#define SPRITE_Y_EXPAND(nr) (m_reg[0x17] & (1 << nr))
+#define SPRITE_Y_SIZE(nr) (SPRITE_Y_EXPAND(nr) ? 2 * 21 : 21)
+#define SPRITE_X_EXPAND(nr) (m_reg[0x1d] & (1 << nr))
+#define SPRITE_X_SIZE(nr) (SPRITE_X_EXPAND(nr) ? 2 * 24 : 24)
+#define SPRITE_X_POS(nr) (m_reg[(nr) * 2] | (m_reg[0x10] & (1 << (nr)) ? 0x100 : 0))
+#define SPRITE_Y_POS(nr) (m_reg[1 + 2 * (nr)])
+#define SPRITE_MULTICOLOR(nr) (m_reg[0x1c] & (1 << nr))
+#define SPRITE_PRIORITY(nr) (m_reg[0x1b] & (1 << nr))
+#define SPRITE_MULTICOLOR1 (m_reg[0x25] & 0x0f)
+#define SPRITE_MULTICOLOR2 (m_reg[0x26] & 0x0f)
+#define SPRITE_COLOR(nr) (m_reg[0x27+nr] & 0x0f)
+#define SPRITE_ADDR(nr) (m_videoaddr | 0x3f8 | nr)
+#define SPRITE_COLL (m_reg[0x1e])
+#define SPRITE_BG_COLL (m_reg[0x1f])
+
+#define GFXMODE ((m_reg[0x11] & 0x60) | (m_reg[0x16] & 0x10)) >> 4
+#define SCREENON (m_reg[0x11] & 0x10)
+#define YSCROLL (m_reg[0x11] & 0x07)
+#define XSCROLL (m_reg[0x16] & 0x07)
+#define ECMON (m_reg[0x11] & 0x40)
+#define HIRESON (m_reg[0x11] & 0x20)
+#define COLUMNS40 (m_reg[0x16] & 0x08) /* else 38 Columns */
+
+#define VIDEOADDR ((m_reg[0x18] & 0xf0) << (10 - 4))
+#define CHARGENADDR ((m_reg[0x18] & 0x0e) << 10)
+#define BITMAPADDR ((data & 0x08) << 10)
+
+#define RASTERLINE (((m_reg[0x11] & 0x80) << 1) | m_reg[0x12])
+
+#define FRAMECOLOR (m_reg[0x20] & 0x0f)
+#define BACKGROUNDCOLOR (m_reg[0x21] & 0x0f)
+#define MULTICOLOR1 (m_reg[0x22] & 0x0f)
+#define MULTICOLOR2 (m_reg[0x23] & 0x0f)
+#define FOREGROUNDCOLOR (m_reg[0x24] & 0x0f)
+
+#define VIC2_LINES (IS_PAL ? VIC6569_LINES : VIC6567_LINES)
+#define VIC2_FIRST_DMA_LINE (IS_PAL ? VIC6569_FIRST_DMA_LINE : VIC6567_FIRST_DMA_LINE)
+#define VIC2_LAST_DMA_LINE (IS_PAL ? VIC6569_LAST_DMA_LINE : VIC6567_LAST_DMA_LINE)
+#define VIC2_FIRST_DISP_LINE (IS_PAL ? VIC6569_FIRST_DISP_LINE : VIC6567_FIRST_DISP_LINE)
+#define VIC2_LAST_DISP_LINE (IS_PAL ? VIC6569_LAST_DISP_LINE : VIC6567_LAST_DISP_LINE)
+#define VIC2_RASTER_2_EMU(a) (IS_PAL ? VIC6569_RASTER_2_EMU(a) : VIC6567_RASTER_2_EMU(a))
+#define VIC2_FIRSTCOLUMN (IS_PAL ? VIC6569_FIRSTCOLUMN : VIC6567_FIRSTCOLUMN)
+#define VIC2_X_2_EMU(a) (IS_PAL ? VIC6569_X_2_EMU(a) : VIC6567_X_2_EMU(a))
+
+#define IRQ_RST 0x01
+#define IRQ_MBC 0x02
+#define IRQ_MMC 0x04
+#define IRQ_LP 0x08
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type MOS6566 = &device_creator<mos6566_device>;
+const device_type MOS6567 = &device_creator<mos6567_device>;
+const device_type MOS8562 = &device_creator<mos8562_device>;
+const device_type MOS8564 = &device_creator<mos8564_device>;
+const device_type MOS6569 = &device_creator<mos6569_device>;
+const device_type MOS8565 = &device_creator<mos8565_device>;
+const device_type MOS8566 = &device_creator<mos8566_device>;
+
+
+// default address maps
+static ADDRESS_MAP_START( mos6566_videoram_map, AS_0, 8, mos6566_device )
+ AM_RANGE(0x0000, 0x3fff) AM_RAM
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( mos6566_colorram_map, AS_1, 8, mos6566_device )
+ AM_RANGE(0x000, 0x3ff) AM_RAM
+ADDRESS_MAP_END
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *mos6566_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_0: return &m_videoram_space_config;
+ case AS_1: return &m_colorram_space_config;
+ default: return NULL;
+ }
+}
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+inline void mos6566_device::set_interrupt( int mask )
+{
+ if (((m_reg[0x19] ^ mask) & m_reg[0x1a] & 0xf))
+ {
+ if (!(m_reg[0x19] & 0x80))
+ {
+ DBG_LOG(2, "vic2", ("irq start %.2x\n", mask));
+ m_reg[0x19] |= 0x80;
+ m_write_irq(ASSERT_LINE);
+ }
+ }
+ m_reg[0x19] |= mask;
+}
+
+inline void mos6566_device::clear_interrupt( int mask )
+{
+ m_reg[0x19] &= ~mask;
+ if ((m_reg[0x19] & 0x80) && !(m_reg[0x19] & m_reg[0x1a] & 0xf))
+ {
+ DBG_LOG(2, "vic2", ("irq end %.2x\n", mask));
+ m_reg[0x19] &= ~0x80;
+ m_write_irq(CLEAR_LINE);
+ }
+}
+
+inline UINT8 mos6566_device::read_videoram(offs_t offset)
+{
+ //logerror("cycle %u VRAM %04x BA %u AEC %u\n", m_cycle, offset & 0x3fff, m_ba, m_aec);
+ m_last_data = space(AS_0).read_byte(offset & 0x3fff);
+
+ return m_last_data;
+}
+
+inline UINT8 mos6566_device::read_colorram(offs_t offset)
+{
+ return space(AS_1).read_byte(offset & 0x3ff);
+}
+
+// Idle access
+inline void mos6566_device::idle_access()
+{
+ read_videoram(0x3fff);
+}
+
+// Fetch sprite data pointer
+inline void mos6566_device::spr_ptr_access( int num )
+{
+ m_spr_ptr[num] = read_videoram(SPRITE_ADDR(num)) << 6;
+}
+
+inline void mos6566_device::spr_ba(int num)
+{
+ if (BIT(m_spr_dma_on, num))
+ {
+ set_ba(CLEAR_LINE);
+ m_rdy_cycles += 2;
+ }
+ else if (num > 1 && !BIT(m_spr_dma_on, num - 1))
+ {
+ set_ba(ASSERT_LINE);
+ }
+}
+
+// Fetch sprite data, increment data counter
+inline void mos6566_device::spr_data_access( int num, int bytenum )
+{
+ if (m_spr_dma_on & (1 << num))
+ {
+ m_spr_data[num][bytenum] = read_videoram((m_mc[num] & 0x3f) | m_spr_ptr[num]);
+ m_mc[num]++;
+ }
+ else
+ if (bytenum == 1)
+ idle_access();
+}
+
+// Turn on display if Bad Line
+inline void mos6566_device::display_if_bad_line()
+{
+ if (m_is_bad_line)
+ m_display_state = 1;
+}
+
+inline void mos6566_device::set_ba(int state)
+{
+ if (m_ba != state)
+ {
+ m_ba = state;
+
+ if (m_ba)
+ {
+ m_aec_delay = 0xff;
+ }
+ }
+}
+
+inline void mos6566_device::set_aec(int state)
+{
+ if (m_aec != state)
+ {
+ m_aec = state;
+ }
+}
+
+inline void mos6566_device::bad_line_ba()
+{
+ if (m_is_bad_line)
+ {
+ if (m_ba)
+ {
+ set_ba(CLEAR_LINE);
+ m_rdy_cycles += 55 - m_cycle;
+ }
+ }
+ else
+ {
+ set_ba(ASSERT_LINE);
+ }
+}
+
+// Refresh access
+inline void mos6566_device::refresh_access()
+{
+ read_videoram(0x3f00 | m_ref_cnt--);
+}
+
+
+inline void mos6566_device::fetch_if_bad_line()
+{
+ if (m_is_bad_line)
+ m_display_state = 1;
+}
+
+
+// Turn on display and matrix access and reset RC if Bad Line
+inline void mos6566_device::rc_if_bad_line()
+{
+ if (m_is_bad_line)
+ {
+ m_display_state = 1;
+ m_rc = 0;
+ }
+}
+
+// Sample border color and increment m_graphic_x
+inline void mos6566_device::sample_border()
+{
+ if (m_draw_this_line)
+ {
+ if (m_border_on)
+ m_border_color_sample[m_cycle - 13] = FRAMECOLOR;
+ m_graphic_x += 8;
+ }
+}
+
+
+// Turn on sprite DMA if necessary
+inline void mos6566_device::check_sprite_dma()
+{
+ int i;
+ UINT8 mask = 1;
+
+ for (i = 0; i < 8; i++, mask <<= 1)
+ if (SPRITEON(i) && ((m_rasterline & 0xff) == SPRITE_Y_POS(i)))
+ {
+ m_spr_dma_on |= mask;
+ m_mc_base[i] = 0;
+ if (SPRITE_Y_EXPAND(i))
+ m_spr_exp_y &= ~mask;
+ }
+}
+
+// Video matrix access
+inline void mos6566_device::matrix_access()
+{
+ if (!m_is_bad_line) return;
+
+ UINT16 adr = (m_vc & 0x03ff) | VIDEOADDR;
+
+ // we're in the second clock phase
+ m_phi0 = 1;
+ set_aec(BIT(m_aec_delay, 2));
+
+ if (!m_ba && m_aec)
+ {
+ m_matrix_line[m_ml_index] = 0xff;
+ }
+ else
+ {
+ m_matrix_line[m_ml_index] = read_videoram(adr);
+ }
+
+ m_color_line[m_ml_index] = read_colorram(adr & 0x03ff);
+}
+
+// Graphics data access
+inline void mos6566_device::graphics_access()
+{
+ if (m_display_state == 1)
+ {
+ UINT16 adr;
+ if (HIRESON)
+ adr = ((m_vc & 0x03ff) << 3) | m_bitmapaddr | m_rc;
+ else
+ adr = (m_matrix_line[m_ml_index] << 3) | m_chargenaddr | m_rc;
+ if (ECMON)
+ adr &= 0xf9ff;
+ m_gfx_data = read_videoram(adr);
+ m_char_data = m_matrix_line[m_ml_index];
+ m_color_data = m_color_line[m_ml_index];
+ m_ml_index++;
+ m_vc++;
+ }
+ else
+ {
+ m_gfx_data = read_videoram((ECMON ? 0x39ff : 0x3fff));
+ m_char_data = 0;
+ }
+}
+
+inline void mos6566_device::draw_background()
+{
+ if (m_draw_this_line)
+ {
+ UINT8 c;
+
+ switch (GFXMODE)
+ {
+ case 0:
+ case 1:
+ case 3:
+ c = m_colors[0];
+ break;
+ case 2:
+ c = m_last_char_data & 0x0f;
+ break;
+ case 4:
+ if (m_last_char_data & 0x80)
+ if (m_last_char_data & 0x40)
+ c = m_colors[3];
+ else
+ c = m_colors[2];
+ else
+ if (m_last_char_data & 0x40)
+ c = m_colors[1];
+ else
+ c = m_colors[0];
+ break;
+ default:
+ c = 0;
+ break;
+ }
+ m_bitmap.plot_box(m_graphic_x, VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[c]);
+ }
+}
+
+inline void mos6566_device::draw_mono( UINT16 p, UINT8 c0, UINT8 c1 )
+{
+ UINT8 c[2];
+ UINT8 data = m_gfx_data;
+
+ c[0] = c0;
+ c[1] = c1;
+
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 7) = PALETTE_MOS[c[data & 1]];
+ m_fore_coll_buf[p + 7] = data & 1; data >>= 1;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 6) = PALETTE_MOS[c[data & 1]];
+ m_fore_coll_buf[p + 6] = data & 1; data >>= 1;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 5) = PALETTE_MOS[c[data & 1]];
+ m_fore_coll_buf[p + 5] = data & 1; data >>= 1;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 4) = PALETTE_MOS[c[data & 1]];
+ m_fore_coll_buf[p + 4] = data & 1; data >>= 1;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 3) = PALETTE_MOS[c[data & 1]];
+ m_fore_coll_buf[p + 3] = data & 1; data >>= 1;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 2) = PALETTE_MOS[c[data & 1]];
+ m_fore_coll_buf[p + 2] = data & 1; data >>= 1;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 1) = PALETTE_MOS[c[data & 1]];
+ m_fore_coll_buf[p + 1] = data & 1; data >>= 1;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 0) = PALETTE_MOS[c[data]];
+ m_fore_coll_buf[p + 0] = data & 1;
+}
+
+inline void mos6566_device::draw_multi( UINT16 p, UINT8 c0, UINT8 c1, UINT8 c2, UINT8 c3 )
+{
+ UINT8 c[4];
+ UINT8 data = m_gfx_data;
+
+ c[0] = c0;
+ c[1] = c1;
+ c[2] = c2;
+ c[3] = c3;
+
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 7) = PALETTE_MOS[c[data & 3]];
+ m_fore_coll_buf[p + 7] = data & 2;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 6) = PALETTE_MOS[c[data & 3]];
+ m_fore_coll_buf[p + 6] = data & 2; data >>= 2;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 5) = PALETTE_MOS[c[data & 3]];
+ m_fore_coll_buf[p + 5] = data & 2;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 4) = PALETTE_MOS[c[data & 3]];
+ m_fore_coll_buf[p + 4] = data & 2; data >>= 2;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 3) = PALETTE_MOS[c[data & 3]];
+ m_fore_coll_buf[p + 3] = data & 2;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 2) = PALETTE_MOS[c[data & 3]];
+ m_fore_coll_buf[p + 2] = data & 2; data >>= 2;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 1) = PALETTE_MOS[c[data]];
+ m_fore_coll_buf[p + 1] = data & 2;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 0) = PALETTE_MOS[c[data]];
+ m_fore_coll_buf[p + 0] = data & 2;
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// mos6566_device - constructor
+//-------------------------------------------------
+
+mos6566_device::mos6566_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MOS6566, "MOS6566", tag, owner, clock, "mos6566", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ device_execute_interface(mconfig, *this),
+ m_icount(0),
+ m_variant(TYPE_6566),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(mos6566_videoram_map)),
+ m_colorram_space_config("colorram", ENDIANNESS_LITTLE, 8, 10, 0, NULL, *ADDRESS_MAP_NAME(mos6566_colorram_map)),
+ m_write_irq(*this),
+ m_write_ba(*this),
+ m_write_aec(*this),
+ m_write_k(*this),
+ m_cpu(*this)
+{
+}
+
+mos6566_device::mos6566_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ device_execute_interface(mconfig, *this),
+ m_icount(0),
+ m_variant(variant),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(mos6566_videoram_map)),
+ m_colorram_space_config("colorram", ENDIANNESS_LITTLE, 8, 10, 0, NULL, *ADDRESS_MAP_NAME(mos6566_colorram_map)),
+ m_write_irq(*this),
+ m_write_ba(*this),
+ m_write_aec(*this),
+ m_write_k(*this),
+ m_cpu(*this),
+ m_phi0(1),
+ m_ba(ASSERT_LINE),
+ m_aec(ASSERT_LINE)
+{
+}
+
+mos6567_device::mos6567_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6566_device(mconfig, MOS6567, "MOS6567", tag, owner, clock, TYPE_6567, "mos6567", __FILE__) { }
+
+mos6567_device::mos6567_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source)
+ :mos6566_device(mconfig, type, name, tag, owner, clock, variant, shortname, source) { }
+
+mos8562_device::mos8562_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6567_device(mconfig, MOS8562, "MOS8562", tag, owner, clock, TYPE_8562, "mos8562", __FILE__) { }
+
+mos8564_device::mos8564_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6567_device(mconfig, MOS8564, "MOS8564", tag, owner, clock, TYPE_8564, "mos8564", __FILE__) { }
+
+mos6569_device::mos6569_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6566_device(mconfig, MOS6566, "MOS6569", tag, owner, clock, TYPE_6569, "mos6569", __FILE__) { }
+
+mos6569_device::mos6569_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source)
+ :mos6566_device(mconfig, type, name, tag, owner, clock, variant, shortname, source) { }
+
+mos8565_device::mos8565_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6569_device(mconfig, MOS8565, "MOS8565", tag, owner, clock, TYPE_8565, "mos8565", __FILE__) { }
+
+mos8566_device::mos8566_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6569_device(mconfig, MOS8566, "MOS8566", tag, owner, clock, TYPE_8566, "mos8566", __FILE__) { }
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mos6566_device::device_start()
+{
+ // set our instruction counter
+ m_icountptr = &m_icount;
+
+ // resolve callbacks
+ m_write_irq.resolve_safe();
+ m_write_ba.resolve_safe();
+ m_write_aec.resolve_safe();
+ m_write_k.resolve_safe();
+
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ for (int i = 0; i < 256; i++)
+ {
+ m_expandx[i] = 0;
+ if (i & 1)
+ m_expandx[i] |= 3;
+ if (i & 2)
+ m_expandx[i] |= 0xc;
+ if (i & 4)
+ m_expandx[i] |= 0x30;
+ if (i & 8)
+ m_expandx[i] |= 0xc0;
+ if (i & 0x10)
+ m_expandx[i] |= 0x300;
+ if (i & 0x20)
+ m_expandx[i] |= 0xc00;
+ if (i & 0x40)
+ m_expandx[i] |= 0x3000;
+ if (i & 0x80)
+ m_expandx[i] |= 0xc000;
+ }
+
+ for (int i = 0; i < 256; i++)
+ {
+ m_expandx_multi[i] = 0;
+ if (i & 1)
+ m_expandx_multi[i] |= 5;
+ if (i & 2)
+ m_expandx_multi[i] |= 0xa;
+ if (i & 4)
+ m_expandx_multi[i] |= 0x50;
+ if (i & 8)
+ m_expandx_multi[i] |= 0xa0;
+ if (i & 0x10)
+ m_expandx_multi[i] |= 0x500;
+ if (i & 0x20)
+ m_expandx_multi[i] |= 0xa00;
+ if (i & 0x40)
+ m_expandx_multi[i] |= 0x5000;
+ if (i & 0x80)
+ m_expandx_multi[i] |= 0xa000;
+ }
+
+ // state saving
+ save_item(NAME(m_reg));
+
+ save_item(NAME(m_on));
+
+ //save_item(NAME(m_bitmap));
+
+ save_item(NAME(m_chargenaddr));
+ save_item(NAME(m_videoaddr));
+ save_item(NAME(m_bitmapaddr));
+
+ save_item(NAME(m_colors));
+ save_item(NAME(m_spritemulti));
+
+ save_item(NAME(m_rasterline));
+ save_item(NAME(m_cycle));
+ save_item(NAME(m_raster_x));
+ save_item(NAME(m_graphic_x));
+ save_item(NAME(m_last_data));
+
+ save_item(NAME(m_dy_start));
+ save_item(NAME(m_dy_stop));
+
+ save_item(NAME(m_draw_this_line));
+ save_item(NAME(m_is_bad_line));
+ save_item(NAME(m_bad_lines_enabled));
+ save_item(NAME(m_display_state));
+ save_item(NAME(m_char_data));
+ save_item(NAME(m_gfx_data));
+ save_item(NAME(m_color_data));
+ save_item(NAME(m_last_char_data));
+ save_item(NAME(m_matrix_line));
+ save_item(NAME(m_color_line));
+ save_item(NAME(m_vblanking));
+ save_item(NAME(m_ml_index));
+ save_item(NAME(m_rc));
+ save_item(NAME(m_vc));
+ save_item(NAME(m_vc_base));
+ save_item(NAME(m_ref_cnt));
+
+ save_item(NAME(m_spr_coll_buf));
+ save_item(NAME(m_fore_coll_buf));
+ save_item(NAME(m_spr_exp_y));
+ save_item(NAME(m_spr_dma_on));
+ save_item(NAME(m_spr_draw));
+ save_item(NAME(m_spr_disp_on));
+ save_item(NAME(m_spr_ptr));
+ save_item(NAME(m_mc_base));
+ save_item(NAME(m_mc));
+
+ for (int i = 0; i < 8; i++)
+ {
+ save_item(NAME(m_spr_data[i]), i);
+ save_item(NAME(m_spr_draw_data[i]), i);
+ }
+
+ save_item(NAME(m_border_on));
+ save_item(NAME(m_ud_border_on));
+ save_item(NAME(m_border_on_sample));
+ save_item(NAME(m_border_color_sample));
+
+ save_item(NAME(m_first_ba_cycle));
+ save_item(NAME(m_device_suspended));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mos6566_device::device_reset()
+{
+ memset(m_reg, 0, sizeof(m_reg));
+
+ for (int i = 0; i < ARRAY_LENGTH(m_mc); i++)
+ m_mc[i] = 63;
+
+ // from 0 to 311 (0 first, PAL) or from 0 to 261 (? first, NTSC 6567R56A) or from 0 to 262 (? first, NTSC 6567R8)
+ m_rasterline = 0; // VIC2_LINES - 1;
+
+ m_cycle = 14;
+ m_raster_x = 0x004;
+ m_graphic_x = 0;
+ m_last_data = 0;
+
+ m_on = 1;
+
+ m_chargenaddr = m_videoaddr = m_bitmapaddr = 0;
+
+ m_dy_start = ROW24_YSTART;
+ m_dy_stop = ROW24_YSTOP;
+
+ m_draw_this_line = 0;
+ m_is_bad_line = 0;
+ m_bad_lines_enabled = 0;
+ m_display_state = 0;
+ m_char_data = 0;
+ m_gfx_data = 0;
+ m_color_data = 0;
+ m_last_char_data = 0;
+ m_vblanking = 0;
+ m_ml_index = 0;
+ m_rc = 0;
+ m_vc = 0;
+ m_vc_base = 0;
+ m_ref_cnt = 0;
+
+ m_spr_exp_y = 0;
+ m_spr_dma_on = 0;
+ m_spr_draw = 0;
+ m_spr_disp_on = 0;
+
+
+ m_border_on = 0;
+ m_ud_border_on = 0;
+
+ m_first_ba_cycle = 0;
+ m_device_suspended = 0;
+
+ memset(m_matrix_line, 0, sizeof(m_matrix_line));
+ memset(m_color_line, 0, sizeof(m_color_line));
+
+ memset(m_spr_coll_buf, 0, sizeof(m_spr_coll_buf));
+ memset(m_fore_coll_buf, 0, sizeof(m_fore_coll_buf));
+ memset(m_border_on_sample, 0, sizeof(m_border_on_sample));
+ memset(m_border_color_sample, 0, sizeof(m_border_color_sample));
+
+ for (int i = 0; i < 8; i++)
+ {
+ m_spr_ptr[i] = 0;
+ m_mc_base[i] = 0;
+ m_mc[i] = 0;
+
+ for (int j = 0; j < 4; j++)
+ {
+ m_spr_draw_data[i][j] = 0;
+ m_spr_data[i][j] = 0;
+ }
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ m_colors[i] = 0;
+ m_spritemulti[i] = 0;
+ }
+
+ m_phi0 = 1;
+ m_ba = CLEAR_LINE;
+ m_aec = CLEAR_LINE;
+ m_aec_delay = 0xff;
+ m_rdy_cycles = 0;
+
+ set_ba(ASSERT_LINE);
+ set_aec(ASSERT_LINE);
+}
+
+
+//-------------------------------------------------
+// execute_run -
+//-------------------------------------------------
+
+void mos6566_device::execute_run()
+{
+ do
+ {
+ UINT8 cpu_cycles = m_cpu->total_cycles() & 0xff;
+ UINT8 vic_cycles = total_cycles() & 0xff;
+
+ m_phi0 = 0;
+
+ m_aec_delay <<= 1;
+ m_aec_delay |= m_ba;
+
+ set_aec(CLEAR_LINE);
+
+ int i;
+ UINT8 mask;
+
+ if (m_rasterline == VIC2_FIRST_DMA_LINE)
+ m_bad_lines_enabled = SCREENON;
+
+ m_is_bad_line = ((m_rasterline >= VIC2_FIRST_DMA_LINE) && (m_rasterline <= VIC2_LAST_DMA_LINE) &&
+ ((m_rasterline & 0x07) == YSCROLL) && m_bad_lines_enabled);
+
+ switch (m_cycle)
+ {
+ // Sprite 3, raster counter, raster IRQ, bad line
+ case 1:
+ if (m_rasterline == (VIC2_LINES - 1))
+ {
+ m_vblanking = 1;
+ }
+ else
+ {
+ m_rasterline++;
+
+ m_draw_this_line = ((VIC2_RASTER_2_EMU(m_rasterline) >= VIC2_RASTER_2_EMU(VIC2_FIRST_DISP_LINE)) &&
+ (VIC2_RASTER_2_EMU(m_rasterline ) <= VIC2_RASTER_2_EMU(VIC2_LAST_DISP_LINE)));
+ }
+
+ m_border_on_sample[0] = m_border_on;
+ spr_ptr_access(3);
+ spr_data_access(3, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 3
+ case 2:
+ if (m_vblanking)
+ {
+ // Vertical blank, reset counters
+ m_rasterline = m_vc_base = 0;
+ m_ref_cnt = 0xff;
+ m_vblanking = 0;
+
+ // Trigger raster IRQ if IRQ in line 0
+ if (RASTERLINE == 0)
+ {
+ set_interrupt(IRQ_RST);
+ }
+ }
+
+ if (m_rasterline == RASTERLINE)
+ {
+ set_interrupt(IRQ_RST);
+ }
+
+ m_graphic_x = VIC2_X_2_EMU(0);
+
+ spr_data_access(3, 1);
+ spr_data_access(3, 2);
+ display_if_bad_line();
+
+ spr_ba(5);
+
+ m_cycle++;
+ break;
+
+ // Sprite 4
+ case 3:
+ spr_ptr_access(4);
+ spr_data_access(4, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 4
+ case 4:
+ spr_data_access(4, 1);
+ spr_data_access(4, 2);
+ display_if_bad_line();
+
+ spr_ba(6);
+
+ m_cycle++;
+ break;
+
+ // Sprite 5
+ case 5:
+ spr_ptr_access(5);
+ spr_data_access(5, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 5
+ case 6:
+ spr_data_access(5, 1);
+ spr_data_access(5, 2);
+ display_if_bad_line();
+
+ spr_ba(7);
+
+ m_cycle++;
+ break;
+
+ // Sprite 6
+ case 7:
+ spr_ptr_access(6);
+ spr_data_access(6, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 6
+ case 8:
+ spr_data_access(6, 1);
+ spr_data_access(6, 2);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 7
+ case 9:
+ spr_ptr_access(7);
+ spr_data_access(7, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 7
+ case 10:
+ spr_data_access(7, 1);
+ spr_data_access(7, 2);
+ display_if_bad_line();
+
+ set_ba(ASSERT_LINE);
+
+ m_cycle++;
+ break;
+
+ // Refresh
+ case 11:
+ refresh_access();
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Refresh, fetch if bad line
+ case 12:
+ bad_line_ba();
+
+ refresh_access();
+ fetch_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Refresh, fetch if bad line, raster_x
+ case 13:
+ bad_line_ba();
+
+ draw_background();
+ sample_border();
+ refresh_access();
+ fetch_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Refresh, fetch if bad line, RC, VC
+ case 14:
+ bad_line_ba();
+
+ draw_background();
+ sample_border();
+ refresh_access();
+ rc_if_bad_line();
+
+ m_vc = m_vc_base;
+
+ m_cycle++;
+ break;
+
+ // Refresh, fetch if bad line, sprite y expansion
+ case 15:
+ bad_line_ba();
+
+ draw_background();
+ sample_border();
+ refresh_access();
+ fetch_if_bad_line();
+
+ for (i = 0; i < 8; i++)
+ if (m_spr_exp_y & (1 << i))
+ m_mc_base[i] += 2;
+
+ m_ml_index = 0;
+ matrix_access();
+
+ m_cycle++;
+ break;
+
+ // Graphics, sprite y expansion, sprite DMA
+ case 16:
+ bad_line_ba();
+
+ draw_background();
+ sample_border();
+ graphics_access();
+ fetch_if_bad_line();
+
+ mask = 1;
+ for (i = 0; i < 8; i++, mask <<= 1)
+ {
+ if (m_spr_exp_y & mask)
+ m_mc_base[i]++;
+ if ((m_mc_base[i] & 0x3f) == 0x3f)
+ m_spr_dma_on &= ~mask;
+ }
+
+ matrix_access();
+
+ m_cycle++;
+ break;
+
+ // Graphics, check border
+ case 17:
+ bad_line_ba();
+
+ if (COLUMNS40)
+ {
+ if (m_rasterline == m_dy_stop)
+ m_ud_border_on = 1;
+ else
+ {
+ if (SCREENON)
+ {
+ if (m_rasterline == m_dy_start)
+ m_border_on = m_ud_border_on = 0;
+ else
+ if (m_ud_border_on == 0)
+ m_border_on = 0;
+ }
+ else
+ if (m_ud_border_on == 0)
+ m_border_on = 0;
+ }
+ }
+
+ // Second sample of border state
+ m_border_on_sample[1] = m_border_on;
+
+ draw_background();
+ draw_graphics();
+ sample_border();
+ graphics_access();
+ fetch_if_bad_line();
+ matrix_access();
+
+ m_cycle++;
+ break;
+
+ // Check border
+ case 18:
+ bad_line_ba();
+
+ if (!COLUMNS40)
+ {
+ if (m_rasterline == m_dy_stop)
+ m_ud_border_on = 1;
+ else
+ {
+ if (SCREENON)
+ {
+ if (m_rasterline == m_dy_start)
+ m_border_on = m_ud_border_on = 0;
+ else
+ if (m_ud_border_on == 0)
+ m_border_on = 0;
+ }
+ else
+ if (m_ud_border_on == 0)
+ m_border_on = 0;
+ }
+ }
+
+ // Third sample of border state
+ m_border_on_sample[2] = m_border_on;
+
+ // Graphics
+
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ case 49:
+ case 50:
+ case 51:
+ case 52:
+ case 53:
+ case 54:
+ draw_graphics();
+ sample_border();
+ graphics_access();
+ fetch_if_bad_line();
+ matrix_access();
+ m_last_char_data = m_char_data;
+
+ m_cycle++;
+ break;
+
+ // Graphics, sprite y expansion, sprite DMA
+ case 55:
+ if (m_is_bad_line)
+ set_ba(ASSERT_LINE);
+
+ draw_graphics();
+ sample_border();
+ graphics_access();
+ display_if_bad_line();
+
+ // sprite y expansion
+ mask = 1;
+ for (i = 0; i < 8; i++, mask <<= 1)
+ if (SPRITE_Y_EXPAND (i))
+ m_spr_exp_y ^= mask;
+
+ check_sprite_dma();
+
+ m_cycle++;
+ break;
+
+ // Check border, sprite DMA
+ case 56:
+ if (!COLUMNS40)
+ m_border_on = 1;
+
+ // Fourth sample of border state
+ m_border_on_sample[3] = m_border_on;
+
+ draw_graphics();
+ sample_border();
+ idle_access();
+ display_if_bad_line();
+ check_sprite_dma();
+
+ m_cycle++;
+ break;
+
+ // Check border, sprites
+ case 57:
+ if (COLUMNS40)
+ m_border_on = 1;
+
+ // Fifth sample of border state
+ m_border_on_sample[4] = m_border_on;
+
+ // Sample spr_disp_on and spr_data for sprite drawing
+ m_spr_draw = m_spr_disp_on;
+ if (m_spr_draw)
+ memcpy(m_spr_draw_data, m_spr_data, 8 * 4);
+
+ mask = 1;
+ for (i = 0; i < 8; i++, mask <<= 1)
+ if ((m_spr_disp_on & mask) && !(m_spr_dma_on & mask))
+ m_spr_disp_on &= ~mask;
+
+ draw_background();
+ sample_border();
+ idle_access();
+ display_if_bad_line();
+
+ spr_ba(0);
+
+ m_cycle++;
+ break;
+
+ // for NTSC 6567R8
+ case 58:
+ draw_background();
+ sample_border();
+ idle_access();
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // for NTSC 6567R8
+ case 59:
+ draw_background();
+ sample_border();
+ idle_access();
+ display_if_bad_line();
+
+ spr_ba(1);
+
+ m_cycle++;
+ break;
+
+ // Sprite 0, sprite DMA, MC, RC
+ case 60:
+ draw_background();
+ sample_border();
+
+ mask = 1;
+ for (i = 0; i < 8; i++, mask <<= 1)
+ {
+ m_mc[i] = m_mc_base[i];
+ if ((m_spr_dma_on & mask) && ((m_rasterline & 0xff) == SPRITE_Y_POS(i)))
+ m_spr_disp_on |= mask;
+ }
+
+ spr_ptr_access(0);
+ spr_data_access(0, 0);
+
+ if (m_rc == 7)
+ {
+ m_vc_base = m_vc;
+ m_display_state = 0;
+ }
+
+ if (m_is_bad_line || m_display_state)
+ {
+ m_display_state = 1;
+ m_rc = (m_rc + 1) & 7;
+ }
+
+ m_cycle++;
+ break;
+
+ // Sprite 0
+ case 61:
+ draw_background();
+ sample_border();
+ spr_data_access(0, 1);
+ spr_data_access(0, 2);
+ display_if_bad_line();
+
+ spr_ba(2);
+
+ m_cycle++;
+ break;
+
+ // Sprite 1, draw
+ case 62:
+ draw_background();
+ sample_border();
+
+ if (m_draw_this_line)
+ {
+ draw_sprites();
+
+ if (m_border_on_sample[0])
+ for (i = 0; i < 4; i++)
+ m_bitmap.plot_box(VIC2_X_2_EMU(i * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[i]]);
+
+ if (m_border_on_sample[1])
+ m_bitmap.plot_box(VIC2_X_2_EMU(4 * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[4]]);
+
+ if (m_border_on_sample[2])
+ for (i = 5; i < 43; i++)
+ m_bitmap.plot_box(VIC2_X_2_EMU(i * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[i]]);
+
+ if (m_border_on_sample[3])
+ m_bitmap.plot_box(VIC2_X_2_EMU(43 * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[43]]);
+
+ if (m_border_on_sample[4])
+ {
+ for (i = 44; i < 48; i++)
+ m_bitmap.plot_box(VIC2_X_2_EMU(i * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[i]]);
+ for (i = 48; i < 53; i++)
+ m_bitmap.plot_box(VIC2_X_2_EMU(i * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[47]]);
+ }
+ }
+
+ spr_ptr_access(1);
+ spr_data_access(1, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 1
+ case 63:
+ spr_data_access(1, 1);
+ spr_data_access(1, 2);
+ display_if_bad_line();
+
+ spr_ba(3);
+
+ m_cycle++;
+ break;
+
+ // Sprite 2
+ case 64:
+ spr_ptr_access(2);
+ spr_data_access(2, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 2
+ case 65:
+ spr_data_access(2, 1);
+ spr_data_access(2, 2);
+ display_if_bad_line();
+
+ if (m_rasterline == m_dy_stop)
+ m_ud_border_on = 1;
+ else
+ if (SCREENON && (m_rasterline == m_dy_start))
+ m_ud_border_on = 0;
+
+ spr_ba(4);
+
+ // Last cycle
+ m_cycle = 1;
+ }
+
+ m_phi0 = 1;
+ set_aec(BIT(m_aec_delay, 2));
+
+ m_write_ba(m_ba);
+ m_write_aec(m_aec);
+
+ m_raster_x += 8;
+ if (m_raster_x == 0x1fc) m_raster_x = 0x004;
+
+ if ((cpu_cycles == vic_cycles) && (m_rdy_cycles > 0))
+ {
+ m_cpu->spin_until_time(m_cpu->cycles_to_attotime(m_rdy_cycles));
+ m_rdy_cycles = 0;
+ }
+
+ m_icount--;
+ } while (m_icount > 0);
+}
+
+
+//-------------------------------------------------
+// execute_run -
+//-------------------------------------------------
+
+void mos6569_device::execute_run()
+{
+ do
+ {
+ UINT8 cpu_cycles = m_cpu->total_cycles() & 0xff;
+ UINT8 vic_cycles = total_cycles() & 0xff;
+
+ m_phi0 = 0;
+
+ m_aec_delay <<= 1;
+ m_aec_delay |= m_ba;
+
+ set_aec(CLEAR_LINE);
+
+ int i;
+ UINT8 mask;
+
+ if ((m_rasterline == VIC2_FIRST_DMA_LINE) && !m_bad_lines_enabled)
+ m_bad_lines_enabled = SCREENON;
+
+ m_is_bad_line = ((m_rasterline >= VIC2_FIRST_DMA_LINE) && (m_rasterline <= VIC2_LAST_DMA_LINE) &&
+ ((m_rasterline & 0x07) == YSCROLL) && m_bad_lines_enabled);
+
+ switch (m_cycle)
+ {
+ // Sprite 3, raster counter, raster IRQ, bad line
+ case 1:
+ if (m_rasterline == (VIC2_LINES - 1))
+ {
+ m_vblanking = 1;
+ }
+ else
+ {
+ m_rasterline++;
+
+ m_draw_this_line = ((VIC2_RASTER_2_EMU(m_rasterline) >= VIC2_RASTER_2_EMU(VIC2_FIRST_DISP_LINE)) &&
+ (VIC2_RASTER_2_EMU(m_rasterline ) <= VIC2_RASTER_2_EMU(VIC2_LAST_DISP_LINE)));
+ }
+
+ m_border_on_sample[0] = m_border_on;
+ spr_ptr_access(3);
+ spr_data_access(3, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 3
+ case 2:
+ spr_ba(5);
+
+ if (m_vblanking)
+ {
+ // Vertical blank, reset counters
+ m_rasterline = m_vc_base = 0;
+ m_ref_cnt = 0xff;
+ m_vblanking = 0;
+
+ // Trigger raster IRQ if IRQ in line 0
+ if (RASTERLINE == 0)
+ {
+ set_interrupt(IRQ_RST);
+ }
+ }
+
+ if (m_rasterline == RASTERLINE)
+ {
+ set_interrupt(IRQ_RST);
+ }
+
+ m_graphic_x = VIC2_X_2_EMU(0);
+
+ spr_data_access(3, 1);
+ spr_data_access(3, 2);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 4
+ case 3:
+ spr_ptr_access(4);
+ spr_data_access(4, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 4
+ case 4:
+ spr_ba(6);
+
+ spr_data_access(4, 1);
+ spr_data_access(4, 2);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 5
+ case 5:
+ spr_ptr_access(5);
+ spr_data_access(5, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 5
+ case 6:
+ spr_ba(7);
+
+ spr_data_access(5, 1);
+ spr_data_access(5, 2);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 6
+ case 7:
+ spr_ptr_access(6);
+ spr_data_access(6, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 6
+ case 8:
+ spr_data_access(6, 1);
+ spr_data_access(6, 2);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 7
+ case 9:
+ spr_ptr_access(7);
+ spr_data_access(7, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 7
+ case 10:
+ spr_data_access(7, 1);
+ spr_data_access(7, 2);
+ display_if_bad_line();
+
+ set_ba(ASSERT_LINE);
+
+ m_cycle++;
+ break;
+
+ // Refresh
+ case 11:
+ refresh_access();
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Refresh, fetch if bad line
+ case 12:
+ bad_line_ba();
+
+ refresh_access();
+ fetch_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Refresh, fetch if bad line, raster_x
+ case 13:
+ bad_line_ba();
+
+ draw_background();
+ sample_border();
+ refresh_access();
+ fetch_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Refresh, fetch if bad line, RC, VC
+ case 14:
+ bad_line_ba();
+
+ draw_background();
+ sample_border();
+ refresh_access();
+ rc_if_bad_line();
+
+ m_vc = m_vc_base;
+
+ m_cycle++;
+ break;
+
+ // Refresh, fetch if bad line, sprite y expansion
+ case 15:
+ bad_line_ba();
+
+ draw_background();
+ sample_border();
+ refresh_access();
+ fetch_if_bad_line();
+
+ for (i = 0; i < 8; i++)
+ if (m_spr_exp_y & (1 << i))
+ m_mc_base[i] += 2;
+
+ m_ml_index = 0;
+
+ matrix_access();
+
+ m_cycle++;
+ break;
+
+ // Graphics, sprite y expansion, sprite DMA
+ case 16:
+ bad_line_ba();
+
+ draw_background();
+ sample_border();
+ graphics_access();
+ fetch_if_bad_line();
+
+ mask = 1;
+ for (i = 0; i < 8; i++, mask <<= 1)
+ {
+ if (m_spr_exp_y & (1 << i))
+ m_mc_base[i]++;
+ if ((m_mc_base[i] & 0x3f) == 0x3f)
+ m_spr_dma_on &= ~mask;
+ }
+
+ matrix_access();
+
+ m_cycle++;
+ break;
+
+ // Graphics, check border
+ case 17:
+ bad_line_ba();
+
+ if (COLUMNS40)
+ {
+ if (m_rasterline == m_dy_stop)
+ m_ud_border_on = 1;
+ else
+ {
+ if (SCREENON)
+ {
+ if (m_rasterline == m_dy_start)
+ m_border_on = m_ud_border_on = 0;
+ else
+ if (m_ud_border_on == 0)
+ m_border_on = 0;
+ } else
+ if (m_ud_border_on == 0)
+ m_border_on = 0;
+ }
+ }
+
+ // Second sample of border state
+ m_border_on_sample[1] = m_border_on;
+
+ draw_background();
+ draw_graphics();
+ sample_border();
+ graphics_access();
+ fetch_if_bad_line();
+ matrix_access();
+
+ m_cycle++;
+ break;
+
+ // Check border
+ case 18:
+ bad_line_ba();
+
+ if (!COLUMNS40)
+ {
+ if (m_rasterline == m_dy_stop)
+ m_ud_border_on = 1;
+ else
+ {
+ if (SCREENON)
+ {
+ if (m_rasterline == m_dy_start)
+ m_border_on = m_ud_border_on = 0;
+ else
+ if (m_ud_border_on == 0)
+ m_border_on = 0;
+ } else
+ if (m_ud_border_on == 0)
+ m_border_on = 0;
+ }
+ }
+
+ // Third sample of border state
+ m_border_on_sample[2] = m_border_on;
+
+ // Graphics
+
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ case 49:
+ case 50:
+ case 51:
+ case 52:
+ case 53:
+ case 54:
+ bad_line_ba();
+
+ draw_graphics();
+ sample_border();
+ graphics_access();
+ fetch_if_bad_line();
+ matrix_access();
+ m_last_char_data = m_char_data;
+
+ m_cycle++;
+ break;
+
+ // Graphics, sprite y expansion, sprite DMA
+ case 55:
+ if (m_is_bad_line)
+ set_ba(ASSERT_LINE);
+
+ draw_graphics();
+ sample_border();
+ graphics_access();
+ display_if_bad_line();
+
+ // sprite y expansion
+ mask = 1;
+ for (i = 0; i < 8; i++, mask <<= 1)
+ if (SPRITE_Y_EXPAND (i))
+ m_spr_exp_y ^= mask;
+
+ check_sprite_dma();
+
+ spr_ba(0);
+
+ m_cycle++;
+ break;
+
+ // Check border, sprite DMA
+ case 56:
+ if (!COLUMNS40)
+ m_border_on = 1;
+
+ // Fourth sample of border state
+ m_border_on_sample[3] = m_border_on;
+
+ draw_graphics();
+ sample_border();
+ idle_access();
+ display_if_bad_line();
+ check_sprite_dma();
+
+ m_cycle++;
+ break;
+
+ // Check border, sprites
+ case 57:
+ spr_ba(1);
+
+ if (COLUMNS40)
+ m_border_on = 1;
+
+ // Fifth sample of border state
+ m_border_on_sample[4] = m_border_on;
+
+ // Sample spr_disp_on and spr_data for sprite drawing
+ m_spr_draw = m_spr_disp_on;
+ if (m_spr_draw)
+ memcpy(m_spr_draw_data, m_spr_data, 8 * 4);
+
+ mask = 1;
+ for (i = 0; i < 8; i++, mask <<= 1)
+ if ((m_spr_disp_on & mask) && !(m_spr_dma_on & mask))
+ m_spr_disp_on &= ~mask;
+
+ draw_background();
+ sample_border();
+ idle_access();
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 0, sprite DMA, MC, RC
+ case 58:
+ draw_background();
+ sample_border();
+
+ mask = 1;
+ for (i = 0; i < 8; i++, mask <<= 1)
+ {
+ m_mc[i] = m_mc_base[i];
+ if ((m_spr_dma_on & mask) && ((m_rasterline & 0xff) == SPRITE_Y_POS(i)))
+ m_spr_disp_on |= mask;
+ }
+
+ spr_ptr_access(0);
+ spr_data_access(0, 0);
+
+ if (m_rc == 7)
+ {
+ m_vc_base = m_vc;
+ m_display_state = 0;
+ }
+
+ if (m_is_bad_line || m_display_state)
+ {
+ m_display_state = 1;
+ m_rc = (m_rc + 1) & 7;
+ }
+
+ m_cycle++;
+ break;
+
+ // Sprite 0
+ case 59:
+ spr_ba(2);
+
+ draw_background();
+ sample_border();
+ spr_data_access(0, 1);
+ spr_data_access(0, 2);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 1, draw
+ case 60:
+ draw_background();
+ sample_border();
+
+ if (m_draw_this_line)
+ {
+ draw_sprites();
+
+ if (m_border_on_sample[0])
+ for (i = 0; i < 4; i++)
+ m_bitmap.plot_box(VIC2_X_2_EMU(i * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[i]]);
+
+ if (m_border_on_sample[1])
+ m_bitmap.plot_box(VIC2_X_2_EMU(4 * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[4]]);
+
+ if (m_border_on_sample[2])
+ for (i = 5; i < 43; i++)
+ m_bitmap.plot_box(VIC2_X_2_EMU(i * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[i]]);
+
+ if (m_border_on_sample[3])
+ m_bitmap.plot_box(VIC2_X_2_EMU(43 * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[43]]);
+
+ if (m_border_on_sample[4])
+ {
+ for (i = 44; i < 48; i++)
+ m_bitmap.plot_box(VIC2_X_2_EMU(i * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[i]]);
+ for (i = 48; i < 51; i++)
+ m_bitmap.plot_box(VIC2_X_2_EMU(i * 8), VIC2_RASTER_2_EMU(m_rasterline), 8, 1, PALETTE_MOS[m_border_color_sample[47]]);
+ }
+ }
+
+ spr_ptr_access(1);
+ spr_data_access(1, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 1
+ case 61:
+ spr_ba(3);
+
+ spr_data_access(1, 1);
+ spr_data_access(1, 2);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 2
+ case 62:
+ spr_ptr_access(2);
+ spr_data_access(2, 0);
+ display_if_bad_line();
+
+ m_cycle++;
+ break;
+
+ // Sprite 2
+ case 63:
+ spr_ba(4);
+
+ spr_data_access(2, 1);
+ spr_data_access(2, 2);
+ display_if_bad_line();
+
+ if (m_rasterline == m_dy_stop)
+ m_ud_border_on = 1;
+ else
+ if (SCREENON && (m_rasterline == m_dy_start))
+ m_ud_border_on = 0;
+
+ // Last cycle
+ m_cycle = 1;
+ }
+
+ m_phi0 = 1;
+ set_aec(BIT(m_aec_delay, 2));
+
+ m_write_ba(m_ba);
+ m_write_aec(m_aec);
+
+ m_raster_x += 8;
+ if (m_raster_x == 0x1fc) m_raster_x = 0x004;
+
+ if ((cpu_cycles == vic_cycles) && (m_rdy_cycles > 0))
+ {
+ m_cpu->spin_until_time(m_cpu->cycles_to_attotime(m_rdy_cycles));
+ m_rdy_cycles = 0;
+ }
+
+ m_icount--;
+ } while (m_icount > 0);
+}
+
+// Graphics display (8 pixels)
+void mos6566_device::draw_graphics()
+{
+ if (m_draw_this_line == 0)
+ {
+ UINT16 p = m_graphic_x + XSCROLL;
+ m_fore_coll_buf[p + 7] = 0;
+ m_fore_coll_buf[p + 6] = 0;
+ m_fore_coll_buf[p + 5] = 0;
+ m_fore_coll_buf[p + 4] = 0;
+ m_fore_coll_buf[p + 3] = 0;
+ m_fore_coll_buf[p + 2] = 0;
+ m_fore_coll_buf[p + 1] = 0;
+ m_fore_coll_buf[p + 0] = 0;
+ }
+ else if (m_ud_border_on)
+ {
+ UINT16 p = m_graphic_x + XSCROLL;
+ m_fore_coll_buf[p + 7] = 0;
+ m_fore_coll_buf[p + 6] = 0;
+ m_fore_coll_buf[p + 5] = 0;
+ m_fore_coll_buf[p + 4] = 0;
+ m_fore_coll_buf[p + 3] = 0;
+ m_fore_coll_buf[p + 2] = 0;
+ m_fore_coll_buf[p + 1] = 0;
+ m_fore_coll_buf[p + 0] = 0;
+ draw_background();
+ }
+ else
+ {
+ UINT8 tmp_col;
+ UINT16 p = m_graphic_x + XSCROLL;
+ switch (GFXMODE)
+ {
+ case 0:
+ draw_mono(p, m_colors[0], m_color_data & 0x0f);
+ break;
+ case 1:
+ if (m_color_data & 0x08)
+ draw_multi(p, m_colors[0], m_colors[1], m_colors[2], m_color_data & 0x07);
+ else
+ draw_mono(p, m_colors[0], m_color_data & 0x0f);
+ break;
+ case 2:
+ draw_mono(p, m_char_data & 0x0f, m_char_data >> 4);
+ break;
+ case 3:
+ draw_multi(p, m_colors[0], m_char_data >> 4, m_char_data & 0x0f, m_color_data & 0x0f);
+ break;
+ case 4:
+ if (m_char_data & 0x80)
+ if (m_char_data & 0x40)
+ tmp_col = m_colors[3];
+ else
+ tmp_col = m_colors[2];
+ else
+ if (m_char_data & 0x40)
+ tmp_col = m_colors[1];
+ else
+ tmp_col = m_colors[0];
+ draw_mono(p, tmp_col, m_color_data & 0x0f);
+ break;
+ case 5:
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 7) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 7] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 6) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 6] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 5) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 5] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 4) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 4] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 3) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 3] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 2) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 2] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 1) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 1] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 0) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 0] = 0;
+ break;
+ case 6:
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 7) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 7] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 6) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 6] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 5) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 5] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 4) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 4] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 3) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 3] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 2) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 2] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 1) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 1] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 0) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 0] = 0;
+ break;
+ case 7:
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 7) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 7] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 6) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 6] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 5) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 5] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 4) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 4] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 3) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 3] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 2) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 2] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 1) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 1] = 0;
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + 0) = PALETTE_MOS[0];
+ m_fore_coll_buf[p + 0] = 0;
+ break;
+ }
+ }
+}
+
+void mos6566_device::draw_sprites()
+{
+ int i;
+ UINT8 snum, sbit;
+ UINT8 spr_coll = 0, gfx_coll = 0;
+ UINT32 plane0_l, plane0_r, plane1_l, plane1_r;
+ UINT32 sdata_l = 0, sdata_r = 0;
+
+ for (i = 0; i < 0x400; i++)
+ m_spr_coll_buf[i] = 0;
+
+ for (snum = 0, sbit = 1; snum < 8; snum++, sbit <<= 1)
+ {
+ if ((m_spr_draw & sbit) && (SPRITE_X_POS(snum) <= (403 - (VIC2_FIRSTCOLUMN + 1))))
+ {
+ UINT16 p = SPRITE_X_POS(snum) + VIC2_X_2_EMU(0) + 8;
+ UINT8 color = SPRITE_COLOR(snum);
+ UINT32 sdata = (m_spr_draw_data[snum][0] << 24) | (m_spr_draw_data[snum][1] << 16) | (m_spr_draw_data[snum][2] << 8);
+
+ if (SPRITE_X_EXPAND(snum))
+ {
+ if (SPRITE_X_POS(snum) > (403 - 24 - (VIC2_FIRSTCOLUMN + 1)))
+ continue;
+
+ if (SPRITE_MULTICOLOR(snum))
+ {
+ sdata_l = (m_expandx_multi[(sdata >> 24) & 0xff] << 16) | m_expandx_multi[(sdata >> 16) & 0xff];
+ sdata_r = m_expandx_multi[(sdata >> 8) & 0xff] << 16;
+ plane0_l = (sdata_l & 0x55555555) | (sdata_l & 0x55555555) << 1;
+ plane1_l = (sdata_l & 0xaaaaaaaa) | (sdata_l & 0xaaaaaaaa) >> 1;
+ plane0_r = (sdata_r & 0x55555555) | (sdata_r & 0x55555555) << 1;
+ plane1_r = (sdata_r & 0xaaaaaaaa) | (sdata_r & 0xaaaaaaaa) >> 1;
+ for (i = 0; i < 32; i++, plane0_l <<= 1, plane1_l <<= 1)
+ {
+ UINT8 col;
+
+ if (plane1_l & 0x80000000)
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+ if (plane0_l & 0x80000000)
+ col = m_spritemulti[3];
+ else
+ col = color;
+ }
+ else
+ {
+ if (plane0_l & 0x80000000)
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+ col = m_spritemulti[1];
+ }
+ else
+ continue;
+ }
+
+ if (m_spr_coll_buf[p + i])
+ spr_coll |= m_spr_coll_buf[p + i] | sbit;
+ else
+ {
+ if (SPRITE_PRIORITY(snum))
+ {
+ if (m_fore_coll_buf[p + i] == 0)
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[col];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ else
+ {
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[col];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ }
+ }
+
+ for (; i < 48; i++, plane0_r <<= 1, plane1_r <<= 1)
+ {
+ UINT8 col;
+
+ if(plane1_r & 0x80000000)
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+
+ if (plane0_r & 0x80000000)
+ col = m_spritemulti[3];
+ else
+ col = color;
+ }
+ else
+ {
+ if (plane0_r & 0x80000000)
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+ col = m_spritemulti[1];
+ }
+ else
+ continue;
+ }
+
+ if (m_spr_coll_buf[p + i])
+ spr_coll |= m_spr_coll_buf[p + i] | sbit;
+ else
+ {
+ if (SPRITE_PRIORITY(snum))
+ {
+ if (m_fore_coll_buf[p + i] == 0)
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[col];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ else
+ {
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[col];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ }
+ }
+ }
+ else
+ {
+ sdata_l = (m_expandx[(sdata >> 24) & 0xff] << 16) | m_expandx[(sdata >> 16) & 0xff];
+ sdata_r = m_expandx[(sdata >> 8) & 0xff] << 16;
+
+ for (i = 0; i < 32; i++, sdata_l <<= 1)
+ if (sdata_l & 0x80000000)
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+
+ if (m_spr_coll_buf[p + i])
+ spr_coll |= m_spr_coll_buf[p + i] | sbit;
+ else
+ {
+ if (SPRITE_PRIORITY(snum))
+ {
+ if (m_fore_coll_buf[p + i] == 0)
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[color];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ else
+ {
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[color];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ }
+ }
+
+ for (; i < 48; i++, sdata_r <<= 1)
+ if (sdata_r & 0x80000000)
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+
+ if (m_spr_coll_buf[p + i])
+ spr_coll |= m_spr_coll_buf[p + i] | sbit;
+ else
+ {
+ if (SPRITE_PRIORITY(snum))
+ {
+ if (m_fore_coll_buf[p + i] == 0)
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[color];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ else
+ {
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[color];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (SPRITE_MULTICOLOR(snum))
+ {
+ UINT32 plane0 = (sdata & 0x55555555) | (sdata & 0x55555555) << 1;
+ UINT32 plane1 = (sdata & 0xaaaaaaaa) | (sdata & 0xaaaaaaaa) >> 1;
+
+ for (i = 0; i < 24; i++, plane0 <<= 1, plane1 <<= 1)
+ {
+ UINT8 col;
+
+ if (plane1 & 0x80000000)
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+
+ if (plane0 & 0x80000000)
+ col = m_spritemulti[3];
+ else
+ col = color;
+ }
+ else
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+
+ if (plane0 & 0x80000000)
+ col = m_spritemulti[1];
+ else
+ continue;
+ }
+
+ if (m_spr_coll_buf[p + i])
+ spr_coll |= m_spr_coll_buf[p + i] | sbit;
+ else
+ {
+ if (SPRITE_PRIORITY(snum))
+ {
+ if (m_fore_coll_buf[p + i] == 0)
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[col];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ else
+ {
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[col];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < 24; i++, sdata <<= 1)
+ {
+ if (sdata & 0x80000000)
+ {
+ if (m_fore_coll_buf[p + i])
+ {
+ gfx_coll |= sbit;
+ }
+ if (m_spr_coll_buf[p + i])
+ {
+ spr_coll |= m_spr_coll_buf[p + i] | sbit;
+ }
+ else
+ {
+ if (SPRITE_PRIORITY(snum))
+ {
+ if (m_fore_coll_buf[p + i] == 0)
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[color];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ else
+ {
+ m_bitmap.pix32(VIC2_RASTER_2_EMU(m_rasterline), p + i) = PALETTE_MOS[color];
+ m_spr_coll_buf[p + i] = sbit;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (SPRITE_COLL)
+ SPRITE_COLL |= spr_coll;
+ else
+ {
+ SPRITE_COLL = spr_coll;
+ if (SPRITE_COLL)
+ set_interrupt(IRQ_MMC);
+ }
+
+ if (SPRITE_BG_COLL)
+ SPRITE_BG_COLL |= gfx_coll;
+ else
+ {
+ SPRITE_BG_COLL = gfx_coll;
+ if (SPRITE_BG_COLL)
+ set_interrupt(IRQ_MBC);
+ }
+}
+
+
+//-------------------------------------------------
+// screen_update -
+//-------------------------------------------------
+
+UINT32 mos6566_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ bitmap.fill(PALETTE_MOS[0], cliprect);
+
+ if (m_on)
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// read -
+//-------------------------------------------------
+
+READ8_MEMBER( mos6566_device::read )
+{
+ UINT8 val = 0;
+
+ offset &= 0x3f;
+
+ switch (offset)
+ {
+ case 0x11:
+ val = (m_reg[offset] & ~0x80) | ((m_rasterline & 0x100) >> 1);
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x12:
+ val = m_rasterline & 0xff;
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x16:
+ val = m_reg[offset] | 0xc0;
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x18:
+ val = m_reg[offset] | 0x01;
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x19: /* interrupt flag register */
+ /* clear_interrupt(0xf); */
+ val = m_reg[offset] | 0x70;
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x1a:
+ val = m_reg[offset] | 0xf0;
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x1e: /* sprite to sprite collision detect */
+ val = m_reg[offset];
+ m_reg[offset] = 0;
+ clear_interrupt(4);
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x1f: /* sprite to background collision detect */
+ val = m_reg[offset];
+ m_reg[offset] = 0;
+ clear_interrupt(2);
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ val = m_reg[offset];
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f:
+ case 0x10:
+ case 0x17:
+ case 0x1b:
+ case 0x1c:
+ case 0x1d:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2c:
+ case 0x2d:
+ case 0x2e:
+ val = m_reg[offset];
+ val |= UNUSED_BITS[offset];
+ break;
+
+ case REGISTER_KCR:
+ case REGISTER_FAST:
+ if (IS_VICIIE)
+ {
+ val = m_reg[offset];
+ DBG_LOG(2, "vic read", ("%.2x:%.2x\n", offset, val));
+ }
+ else
+ {
+ val |= UNUSED_BITS[offset];
+ }
+ break;
+
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ case 0x3c:
+ case 0x3d:
+ case 0x3e:
+ case 0x3f: /* not used */
+ DBG_LOG(2, "vic read", ("%.2x:%.2x\n", offset, val));
+ val |= UNUSED_BITS[offset];
+ break;
+
+ default:
+ val = m_reg[offset];
+ val |= UNUSED_BITS[offset];
+ }
+
+ if ((offset != 0x11) && (offset != 0x12))
+ DBG_LOG(2, "vic read", ("%.2x:%.2x\n", offset, val));
+
+ return val;
+}
+
+
+//-------------------------------------------------
+// write -
+//-------------------------------------------------
+
+WRITE8_MEMBER( mos6566_device::write )
+{
+ DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
+ offset &= 0x3f;
+
+ switch (offset)
+ {
+ case 0x01:
+ case 0x03:
+ case 0x05:
+ case 0x07:
+ case 0x09:
+ case 0x0b:
+ case 0x0d:
+ case 0x0f:
+ m_reg[offset] = data; /* sprite y positions */
+ break;
+
+ case 0x00:
+ case 0x02:
+ case 0x04:
+ case 0x06:
+ case 0x08:
+ case 0x0a:
+ case 0x0c:
+ case 0x0e:
+ m_reg[offset] = data; /* sprite x positions */
+ break;
+
+ case 0x10:
+ m_reg[offset] = data; /* sprite x positions */
+ break;
+
+ case 0x17: /* sprite y size */
+ m_spr_exp_y |= ~data;
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x1d: /* sprite x size */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x1b: /* sprite background priority */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x1c: /* sprite multicolor mode select */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2c:
+ case 0x2d:
+ case 0x2e:
+ /* sprite colors */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x25: /* sprite multicolor */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_spritemulti[1] = SPRITE_MULTICOLOR1;
+ }
+ break;
+
+ case 0x26: /* sprite multicolor */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_spritemulti[3] = SPRITE_MULTICOLOR2;
+ }
+ break;
+
+ case 0x19:
+ clear_interrupt(data & 0x0f);
+ break;
+
+ case 0x1a: /* irq mask */
+ m_reg[offset] = data;
+ set_interrupt(0); // beamrider needs this
+ break;
+
+ case 0x11:
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ if (data & 8)
+ {
+ m_dy_start = ROW25_YSTART;
+ m_dy_stop = ROW25_YSTOP;
+ }
+ else
+ {
+ m_dy_start = ROW24_YSTART;
+ m_dy_stop = ROW24_YSTOP;
+ }
+ }
+ break;
+
+ case 0x12:
+ if (data != m_reg[offset])
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x16:
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x18:
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_videoaddr = VIDEOADDR;
+ m_chargenaddr = CHARGENADDR;
+ m_bitmapaddr = BITMAPADDR;
+ }
+ break;
+
+ case 0x21: /* background color */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_colors[0] = BACKGROUNDCOLOR;
+ }
+ break;
+
+ case 0x22: /* background color 1 */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_colors[1] = MULTICOLOR1;
+ }
+ break;
+
+ case 0x23: /* background color 2 */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_colors[2] = MULTICOLOR2;
+ }
+ break;
+
+ case 0x24: /* background color 3 */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_colors[3] = FOREGROUNDCOLOR;
+ }
+ break;
+
+ case 0x20: /* framecolor */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case REGISTER_KCR:
+ if (IS_VICIIE)
+ {
+ m_reg[offset] = data | 0xf8;
+
+ m_write_k((offs_t)0, data & 0x07);
+ }
+ break;
+
+ case REGISTER_FAST:
+ if (IS_VICIIE)
+ {
+ if (BIT(m_reg[offset], 0) != BIT(data, 0))
+ {
+ m_cpu->set_unscaled_clock(clock() << BIT(data, 0));
+ }
+
+ m_reg[offset] = data | 0xfc;
+
+ m_on = !BIT(data, 0);
+ }
+ break;
+
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ case 0x3c:
+ case 0x3d:
+ case 0x3e:
+ case 0x3f:
+ m_reg[offset] = data;
+ DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
+ break;
+
+ default:
+ m_reg[offset] = data;
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// lp_w - light pen strobe
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( mos6566_device::lp_w )
+{
+ if (m_lp && !state && !(m_reg[REGISTER_IRQ] & IRQ_LP))
+ {
+ m_reg[REGISTER_LPX] = m_raster_x >> 1;
+ m_reg[REGISTER_LPY] = m_rasterline;
+
+ set_interrupt(IRQ_LP);
+ }
+
+ m_lp = state;
+}
diff --git a/src/devices/video/mos6566.h b/src/devices/video/mos6566.h
new file mode 100644
index 00000000000..e14c00140e9
--- /dev/null
+++ b/src/devices/video/mos6566.h
@@ -0,0 +1,455 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/***************************************************************************
+
+ MOS 6566/6567/6569 Video Interface Chip II (VIC-II) emulation
+
+****************************************************************************
+ _____ _____
+ DB6 1 |* \_/ | 40 Vcc
+ DB5 2 | | 39 DB7
+ DB4 3 | | 38 DB8
+ DB3 4 | | 37 DB9
+ DB2 5 | | 36 DB10
+ DB1 6 | | 35 DB11
+ DB0 7 | | 34 A13
+ _IRQ 8 | | 33 A12
+ LP 9 | | 32 A11
+ _CS 10 | MOS6566 | 31 A10
+ R/W 11 | | 30 A9
+ BA 12 | | 29 A8
+ Vdd 13 | | 28 A7
+ COLOR 14 | | 27 A6
+ S/LUM 15 | | 26 A5
+ AEC 16 | | 25 A4
+ PH0 17 | | 24 A3
+ PHIN 18 | | 23 A2
+ PHCOL 19 | | 22 A1
+ Vss 20 |_____________| 21 A0
+
+ _____ _____
+ DB6 1 |* \_/ | 40 Vcc
+ DB5 2 | | 39 DB7
+ DB4 3 | | 38 DB8
+ DB3 4 | | 37 DB9
+ DB2 5 | | 36 DB10
+ DB1 6 | | 35 DB11
+ DB0 7 | | 34 A10
+ _IRQ 8 | | 33 A9
+ LP 9 | MOS6567 | 32 A8
+ _CS 10 | MOS6569 | 31 A7
+ R/W 11 | MOS8562 | 30 A6
+ BA 12 | MOS8565 | 29 A5/A13
+ Vdd 13 | | 28 A4/A12
+ COLOR 14 | | 27 A3/A11
+ S/LUM 15 | | 26 A2/A10
+ AEC 16 | | 25 A1/A9
+ PH0 17 | | 24 A0/A8
+ _RAS 18 | | 23 A11
+ CAS 19 | | 22 PHIN
+ Vss 20 |_____________| 21 PHCL
+
+ _____ _____
+ D6 1 |* \_/ | 48 Vcc
+ D5 2 | | 47 D7
+ D4 3 | | 46 D8
+ D3 4 | | 45 D9
+ D2 5 | | 44 D10
+ D1 6 | | 43 D11
+ D0 7 | | 42 MA10
+ _IRQ 8 | | 41 MA9
+ _LP 9 | | 40 MA8
+ BA 10 | | 39 A7
+ _DMARQST 11 | | 38 A6
+ AEC 12 | MOS8564 | 37 MA5
+ _CS 13 | MOS8566 | 36 MA4
+ R/W 14 | | 35 MA3
+ _DMAACK 15 | | 34 MA2
+ CHROMA 16 | | 33 MA1
+ SYNC/LUM 17 | | 32 MA0
+ 1 MHZ 18 | | 31 MA11
+ _RAS 19 | | 30 PHI IN
+ _CAS 20 | | 29 PHI COLOR
+ MUX 21 | | 28 K2
+ _IOACC 22 | | 27 K1
+ 2 MHZ 23 | | 26 K0
+ Vss 24 |_____________| 25 Z80 PHI
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __MOS6566__
+#define __MOS6566__
+
+#include "emu.h"
+
+
+
+//***************************************************************************
+// DEVICE CONFIGURATION MACROS
+//***************************************************************************
+
+#define MCFG_MOS6566_CPU(_tag) \
+ mos6566_device::static_set_cpu_tag(*device, "^" _tag);
+
+#define MCFG_MOS6566_IRQ_CALLBACK(_write) \
+ devcb = &mos6566_device::set_irq_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_MOS6566_BA_CALLBACK(_write) \
+ devcb = &mos6566_device::set_ba_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_MOS6566_AEC_CALLBACK(_write) \
+ devcb = &mos6566_device::set_aec_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_MOS8564_K_CALLBACK(_write) \
+ devcb = &mos6566_device::set_k_wr_callback(*device, DEVCB_##_write);
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define VIC6566_CLOCK (XTAL_8MHz / 8) // 1000000
+#define VIC6567R56A_CLOCK (XTAL_8MHz / 8) // 1000000
+#define VIC6567_CLOCK (XTAL_14_31818MHz / 14) // 1022727
+#define VIC6569_CLOCK (XTAL_17_734472MHz / 18) // 985248
+
+#define VIC6566_DOTCLOCK (VIC6566_CLOCK * 8) // 8000000
+#define VIC6567R56A_DOTCLOCK (VIC6567R56A_CLOCK * 8) // 8000000
+#define VIC6567_DOTCLOCK (VIC6567_CLOCK * 8) // 8181818
+#define VIC6569_DOTCLOCK (VIC6569_CLOCK * 8) // 7881988
+
+#define VIC6567_CYCLESPERLINE 65
+#define VIC6569_CYCLESPERLINE 63
+
+#define VIC6567_LINES 263
+#define VIC6569_LINES 312
+
+#define VIC6566_VRETRACERATE ((float)VIC6566_CLOCK / 262 / 64)
+#define VIC6567R56A_VRETRACERATE ((float)VIC6567R56A_CLOCK / 262 / 64)
+#define VIC6567_VRETRACERATE ((float)VIC6567_CLOCK / 263 / 65)
+#define VIC6569_VRETRACERATE ((float)VIC6569_CLOCK / 312 / 63)
+
+#define VIC6566_HRETRACERATE (VIC6566_CLOCK / VIC6566_CYCLESPERLINE)
+#define VIC6567_HRETRACERATE (VIC6567_CLOCK / VIC6567_CYCLESPERLINE)
+#define VIC6569_HRETRACERATE (VIC6569_CLOCK / VIC6569_CYCLESPERLINE)
+
+#define VIC2_HSIZE 320
+#define VIC2_VSIZE 200
+
+#define VIC6567_VISIBLELINES 235
+#define VIC6569_VISIBLELINES 284
+
+#define VIC6567_FIRST_DMA_LINE 0x30
+#define VIC6569_FIRST_DMA_LINE 0x30
+
+#define VIC6567_LAST_DMA_LINE 0xf7
+#define VIC6569_LAST_DMA_LINE 0xf7
+
+#define VIC6567_FIRST_DISP_LINE 0x29
+#define VIC6569_FIRST_DISP_LINE 0x10
+
+#define VIC6567_LAST_DISP_LINE (VIC6567_FIRST_DISP_LINE + VIC6567_VISIBLELINES - 1)
+#define VIC6569_LAST_DISP_LINE (VIC6569_FIRST_DISP_LINE + VIC6569_VISIBLELINES - 1)
+
+#define VIC6567_RASTER_2_EMU(a) ((a >= VIC6567_FIRST_DISP_LINE) ? (a - VIC6567_FIRST_DISP_LINE) : (a + 222))
+#define VIC6569_RASTER_2_EMU(a) (a - VIC6569_FIRST_DISP_LINE)
+
+#define VIC6567_FIRSTCOLUMN 50
+#define VIC6569_FIRSTCOLUMN 50
+
+#define VIC6567_VISIBLECOLUMNS 418
+#define VIC6569_VISIBLECOLUMNS 403
+
+#define VIC6567_X_2_EMU(a) (a)
+#define VIC6569_X_2_EMU(a) (a)
+
+#define VIC6567_STARTVISIBLELINES ((VIC6567_LINES - VIC6567_VISIBLELINES)/2)
+#define VIC6569_STARTVISIBLELINES 16 /* ((VIC6569_LINES - VIC6569_VISIBLELINES)/2) */
+
+#define VIC6567_FIRSTRASTERLINE 34
+#define VIC6569_FIRSTRASTERLINE 0
+
+#define VIC6567_COLUMNS 512
+#define VIC6569_COLUMNS 504
+
+#define VIC6567_STARTVISIBLECOLUMNS ((VIC6567_COLUMNS - VIC6567_VISIBLECOLUMNS)/2)
+#define VIC6569_STARTVISIBLECOLUMNS ((VIC6569_COLUMNS - VIC6569_VISIBLECOLUMNS)/2)
+
+#define VIC6567_FIRSTRASTERCOLUMNS 412
+#define VIC6569_FIRSTRASTERCOLUMNS 404
+
+#define VIC6569_FIRST_X 0x194
+#define VIC6567_FIRST_X 0x19c
+
+#define VIC6569_FIRST_VISIBLE_X 0x1e0
+#define VIC6567_FIRST_VISIBLE_X 0x1e8
+
+#define VIC6569_MAX_X 0x1f7
+#define VIC6567_MAX_X 0x1ff
+
+#define VIC6569_LAST_VISIBLE_X 0x17c
+#define VIC6567_LAST_VISIBLE_X 0x184
+
+#define VIC6569_LAST_X 0x193
+#define VIC6567_LAST_X 0x19b
+
+
+
+//***************************************************************************
+// TYPE DEFINITIONS
+//***************************************************************************
+
+// ======================> mos6566_device
+
+class mos6566_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface,
+ public device_execute_interface
+{
+public:
+ // construction/destruction
+ mos6566_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source);
+ mos6566_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_cpu_tag(device_t &device, const char *tag) { downcast<mos6566_device &>(device).m_cpu.set_tag(tag); }
+ template<class _Object> static devcb_base &set_irq_wr_callback(device_t &device, _Object object) { return downcast<mos6566_device &>(device).m_write_irq.set_callback(object); }
+ template<class _Object> static devcb_base &set_ba_wr_callback(device_t &device, _Object object) { return downcast<mos6566_device &>(device).m_write_ba.set_callback(object); }
+ template<class _Object> static devcb_base &set_aec_wr_callback(device_t &device, _Object object) { return downcast<mos6566_device &>(device).m_write_aec.set_callback(object); }
+ template<class _Object> static devcb_base &set_k_wr_callback(device_t &device, _Object object) { return downcast<mos6566_device &>(device).m_write_k.set_callback(object); }
+
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_WRITE_LINE_MEMBER( lp_w );
+
+ DECLARE_READ_LINE_MEMBER( phi0_r ) { return m_phi0; } // phi 0
+ DECLARE_READ_LINE_MEMBER( ba_r ) { return m_ba; } // bus available
+ DECLARE_READ_LINE_MEMBER( aec_r ) { return m_aec; } // address enable control
+
+ UINT8 bus_r() { return m_last_data; }
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ enum
+ {
+ TYPE_6566, // NTSC-M (SRAM)
+ TYPE_6567, // NTSC-M (NMOS)
+ TYPE_8562, // NTSC-M (HMOS)
+ TYPE_8564, // NTSC-M VIC-IIe (C128)
+
+ TYPE_6569, // PAL-B
+ TYPE_6572, // PAL-N
+ TYPE_6573, // PAL-M
+ TYPE_8565, // PAL-B (HMOS)
+ TYPE_8566, // PAL-B VIC-IIe (C128)
+ TYPE_8569 // PAL-N VIC-IIe (C128)
+ };
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void execute_run();
+
+ int m_icount;
+ int m_variant;
+
+ const address_space_config m_videoram_space_config;
+ const address_space_config m_colorram_space_config;
+
+ inline void set_interrupt( int mask );
+ inline void clear_interrupt( int mask );
+ inline void set_ba(int state);
+ inline void set_aec(int state);
+ inline void bad_line_ba();
+ inline UINT8 read_videoram(offs_t offset);
+ inline UINT8 read_colorram(offs_t offset);
+ inline void idle_access();
+ inline void spr_ba(int num);
+ inline void spr_ptr_access( int num );
+ inline void spr_data_access( int num, int bytenum );
+ inline void display_if_bad_line();
+ inline void refresh_access();
+ inline void fetch_if_bad_line();
+ inline void rc_if_bad_line();
+ inline void sample_border();
+ inline void check_sprite_dma();
+ inline void matrix_access();
+ inline void graphics_access();
+ inline void draw_background();
+ inline void draw_mono( UINT16 p, UINT8 c0, UINT8 c1 );
+ inline void draw_multi( UINT16 p, UINT8 c0, UINT8 c1, UINT8 c2, UINT8 c3 );
+ void draw_graphics();
+ void draw_sprites();
+
+ devcb_write_line m_write_irq;
+ devcb_write_line m_write_ba;
+ devcb_write_line m_write_aec;
+ devcb_write8 m_write_k;
+
+ required_device<cpu_device> m_cpu;
+
+ int m_phi0;
+ int m_ba;
+ int m_aec;
+ UINT8 m_aec_delay;
+ int m_rdy_cycles;
+
+ UINT8 m_reg[0x80];
+
+ int m_on; /* rastering of the screen */
+
+ UINT16 m_chargenaddr, m_videoaddr, m_bitmapaddr;
+
+ bitmap_rgb32 m_bitmap;
+
+ UINT16 m_colors[4], m_spritemulti[4];
+
+ int m_rasterline;
+ UINT8 m_cycle;
+ UINT16 m_raster_x;
+ UINT16 m_graphic_x;
+ UINT8 m_last_data;
+ int m_lp;
+
+ /* convert multicolor byte to background/foreground for sprite collision */
+ UINT16 m_expandx[256];
+ UINT16 m_expandx_multi[256];
+
+ /* Display */
+ UINT16 m_dy_start;
+ UINT16 m_dy_stop;
+
+ /* GFX */
+ UINT8 m_draw_this_line;
+ UINT8 m_is_bad_line;
+ UINT8 m_bad_lines_enabled;
+ UINT8 m_display_state;
+ UINT8 m_char_data;
+ UINT8 m_gfx_data;
+ UINT8 m_color_data;
+ UINT8 m_last_char_data;
+ UINT8 m_matrix_line[40]; // Buffer for video line, read in Bad Lines
+ UINT8 m_color_line[40]; // Buffer for color line, read in Bad Lines
+ UINT8 m_vblanking;
+ UINT16 m_ml_index;
+ UINT8 m_rc;
+ UINT16 m_vc;
+ UINT16 m_vc_base;
+ UINT8 m_ref_cnt;
+
+ /* Sprites */
+ UINT8 m_spr_coll_buf[0x400]; // Buffer for sprite-sprite collisions and priorities
+ UINT8 m_fore_coll_buf[0x400]; // Buffer for foreground-sprite collisions and priorities
+ UINT8 m_spr_draw_data[8][4]; // Sprite data for drawing
+ UINT8 m_spr_exp_y;
+ UINT8 m_spr_dma_on;
+ UINT8 m_spr_draw;
+ UINT8 m_spr_disp_on;
+ UINT16 m_spr_ptr[8];
+ UINT8 m_spr_data[8][4];
+ UINT16 m_mc_base[8]; // Sprite data counter bases
+ UINT16 m_mc[8]; // Sprite data counters
+
+ /* Border */
+ UINT8 m_border_on;
+ UINT8 m_ud_border_on;
+ UINT8 m_border_on_sample[5];
+ UINT8 m_border_color_sample[0x400 / 8]; // Samples of border color at each "displayed" cycle
+
+ /* Cycles */
+ UINT64 m_first_ba_cycle;
+ UINT8 m_device_suspended;
+};
+
+
+// ======================> mos6567_device
+
+class mos6567_device : public mos6566_device
+{
+public:
+ // construction/destruction
+ mos6567_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ mos6567_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source);
+};
+
+
+// ======================> mos8562_device
+
+class mos8562_device : public mos6567_device
+{
+public:
+ // construction/destruction
+ mos8562_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+// ======================> mos8564_device
+
+class mos8564_device : public mos6567_device
+{
+public:
+ // construction/destruction
+ mos8564_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device_execute_interface overrides
+ virtual UINT64 execute_clocks_to_cycles(UINT64 clocks) const { return (clocks / 8); }
+ virtual UINT64 execute_cycles_to_clocks(UINT64 cycles) const { return (cycles * 8); }
+};
+
+
+// ======================> mos6569_device
+
+class mos6569_device : public mos6566_device
+{
+public:
+ // construction/destruction
+ mos6569_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ mos6569_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source);
+
+ // device-level overrides
+ virtual void execute_run();
+};
+
+
+// ======================> mos8565_device
+
+class mos8565_device : public mos6569_device
+{
+public:
+ // construction/destruction
+ mos8565_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+// ======================> mos8566_device
+
+class mos8566_device : public mos6569_device
+{
+public:
+ // construction/destruction
+ mos8566_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device_execute_interface overrides
+ virtual UINT64 execute_clocks_to_cycles(UINT64 clocks) const { return (clocks / 8); }
+ virtual UINT64 execute_cycles_to_clocks(UINT64 cycles) const { return (cycles * 8); }
+};
+
+
+// device type definitions
+extern const device_type MOS6566;
+extern const device_type MOS6567;
+extern const device_type MOS8562;
+extern const device_type MOS8564;
+extern const device_type MOS6569;
+extern const device_type MOS8565;
+extern const device_type MOS8566;
+
+
+
+#endif
diff --git a/src/devices/video/msm6222b.c b/src/devices/video/msm6222b.c
new file mode 100644
index 00000000000..f6c2c9564a3
--- /dev/null
+++ b/src/devices/video/msm6222b.c
@@ -0,0 +1,246 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ MSM6222B
+
+ A somewhat hd44780-compatible LCD controller.
+
+ The -01 variant has a fixed cgrom, the other variants are mask-programmed.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "msm6222b.h"
+
+const device_type MSM6222B = &device_creator<msm6222b_device>;
+const device_type MSM6222B_01 = &device_creator<msm6222b_01_device>;
+
+ROM_START( msm6222b_01 )
+ ROM_REGION( 0x1000, "cgrom", 0 )
+ ROM_LOAD( "msm6222b-01.bin", 0x0000, 0x1000, CRC(8ffa8521) SHA1(e108b520e6d20459a7bbd5958bbfa1d551a690bd) )
+ROM_END
+
+msm6222b_device::msm6222b_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+msm6222b_device::msm6222b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, MSM6222B, "msm6222b-xx", tag, owner, clock, "msm6222b", __FILE__)
+{
+}
+
+msm6222b_01_device::msm6222b_01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ msm6222b_device(mconfig, MSM6222B_01, "msm6222b-01", tag, owner, clock, "msm6222b", __FILE__)
+{
+}
+
+const rom_entry *msm6222b_01_device::device_rom_region() const
+{
+ return ROM_NAME(msm6222b_01);
+}
+
+void msm6222b_device::device_start()
+{
+ if(memregion("cgrom"))
+ cgrom = memregion("cgrom")->base();
+ else if(m_region)
+ cgrom = m_region->base();
+ else
+ cgrom = NULL;
+
+ memset(cgram, 0, sizeof(cgram));
+ memset(ddram, 0x20, sizeof(ddram));
+
+ cursor_direction = true;
+ cursor_blinking = false;
+ display_on = false;
+ two_line = false;
+ cursor_on = false;
+ shift_on_write = false;
+ double_height = false;
+ adc = 0x00;
+ shift = 0;
+}
+
+void msm6222b_device::control_w(UINT8 data)
+{
+ int cmd;
+ for(cmd = 7; cmd >= 0 && !(data & (1<<cmd)); cmd--);
+ switch(cmd) {
+ case 0:
+ memset(ddram, 0x20, sizeof(ddram));
+ adc = 0x00;
+ break;
+
+ case 1:
+ adc = 0x00;
+ shift = 0x00;
+ break;
+ case 2:
+ shift_on_write = data & 1;
+ cursor_direction = data & 2;
+ break;
+
+ case 3:
+ display_on = data & 4;
+ cursor_on = data & 2;
+ cursor_blinking = data & 1;
+ break;
+
+ case 4:
+ if(data & 8)
+ shift_step(data & 4);
+ else
+ cursor_step(data & 4);
+ break;
+
+ case 5:
+ two_line = data & 8;
+ double_height = (data & 0xc) == 4;
+ // Bit 4 is 4bits/8bits data access
+ break;
+
+ case 6:
+ adc = data & 0x3f;
+ break;
+
+ case 7:
+ adc = data; // Bit 7 is set
+ break;
+ }
+}
+
+UINT8 msm6222b_device::control_r()
+{
+ return adc & 0x7f;
+}
+
+void msm6222b_device::data_w(UINT8 data)
+{
+ if(adc & 0x80) {
+ int adr = adc & 0x7f;
+ if(two_line) {
+ if((adr >= 40 && adr < 64) || adr >= 64+40)
+ adr = -1;
+ if(adr >= 64)
+ adr += 40-64;
+ } else {
+ if(adr >= 80)
+ adr = -1;
+ }
+ if(adr != -1) {
+ ddram[adr] = data;
+ if(shift_on_write)
+ shift_step(cursor_direction);
+ else
+ cursor_step(cursor_direction);
+ }
+ } else {
+ if(adc < 8*8) {
+ cgram[adc] = data;
+ cursor_step(cursor_direction);
+ }
+ }
+}
+
+void msm6222b_device::cursor_step(bool direction)
+{
+ if(direction) {
+ if(adc & 0x80) {
+ if(two_line && adc == (0x80|39))
+ adc = 0x80|64;
+ else if(two_line && adc == (0x80|(64+39)))
+ adc = 0x80;
+ else if((!two_line) && adc == (0x80|79))
+ adc = 0x80;
+ else
+ adc++;
+ } else {
+ if(adc == 8*8-1)
+ adc = 0x00;
+ else
+ adc++;
+ }
+ } else {
+ if(adc & 0x80) {
+ if(adc == 0x80)
+ adc = two_line ? 0x80|(64+39) : 0x80|79;
+ else if(two_line && adc == (0x80|64))
+ adc = 0x80|39;
+ else
+ adc--;
+ } else {
+ if(adc == 0x00)
+ adc = 8*8-1;
+ else
+ adc--;
+ }
+ }
+}
+
+void msm6222b_device::shift_step(bool direction)
+{
+ if(direction) {
+ if(shift == 79)
+ shift = 0;
+ else
+ shift++;
+ } else {
+ if(shift == 0)
+ shift = 79;
+ else
+ shift--;
+ }
+}
+
+bool msm6222b_device::blink_on() const
+{
+ if(!cursor_blinking)
+ return false;
+ UINT64 clocks = machine().time().as_ticks(250000);
+ if(double_height)
+ return clocks % 281600 >= 140800;
+ else
+ return clocks % 204800 >= 102400;
+}
+
+const UINT8 *msm6222b_device::render()
+{
+ memset(render_buf, 0, 80*16);
+ if(!display_on)
+ return render_buf;
+
+ int char_height = double_height ? 11 : 8;
+
+ for(int i=0; i<80; i++) {
+ UINT8 c = ddram[(i+shift) % 80];
+ if(c < 16)
+ memcpy(render_buf + 16*i, double_height ? cgram + 8*(c & 6) : cgram + 8*(c & 7), char_height);
+ else if(cgrom)
+ memcpy(render_buf + 16*i, cgrom + 16*c, char_height);
+ }
+
+ if(cursor_on) {
+ int cpos = adc & 0x7f;
+ if(two_line) {
+ if((cpos >= 40 && cpos < 64) || cpos >= 64+40)
+ cpos = -1;
+ else if(cpos >= 64)
+ cpos += 40-64;
+ } else {
+ if(cpos >= 80)
+ cpos = -1;
+ }
+ if(cpos != -1) {
+ cpos = (cpos + shift) % 80;
+ render_buf[cpos*16 + (double_height ? 10 : 7)] |= 0x1f;
+ if(blink_on())
+ for(int i=0; i<char_height; i++)
+ render_buf[cpos*16 + i] ^= 0x1f;
+ }
+ }
+
+ return render_buf;
+}
diff --git a/src/devices/video/msm6222b.h b/src/devices/video/msm6222b.h
new file mode 100644
index 00000000000..70279cae218
--- /dev/null
+++ b/src/devices/video/msm6222b.h
@@ -0,0 +1,63 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ MSM6222B
+
+ A somewhat hd44780-compatible LCD controller.
+
+ The -01 variant has a fixed cgrom, the other variants are mask-programmed.
+
+***************************************************************************/
+
+#ifndef __MSM6222B_H__
+#define __MSM6222B_H__
+
+#define MCFG_MSM6222B_ADD( _tag ) \
+ MCFG_DEVICE_ADD( _tag, MSM6222B, 0 )
+
+#define MCFG_MSM6222B_01_ADD( _tag ) \
+ MCFG_DEVICE_ADD( _tag, MSM6222B_01, 0 )
+
+class msm6222b_device : public device_t {
+public:
+ msm6222b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ msm6222b_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ void control_w(UINT8 data);
+ UINT8 control_r();
+ void data_w(UINT8 data);
+ UINT8 data_r();
+
+ // Character n bits are at bytes n*16..n*16+7 when 8-high, +10 when 11-high. Only the low 5 bits are used.
+ // In one line mode n = 0..79. In two line mode first line is 0..39 and second is 40..79.
+ const UINT8 *render();
+
+protected:
+ virtual void device_start();
+
+private:
+ UINT8 cgram[8*8];
+ UINT8 ddram[80];
+ UINT8 render_buf[80*16];
+ bool cursor_direction, cursor_blinking, two_line, shift_on_write, double_height, cursor_on, display_on;
+ UINT8 adc, shift;
+ const UINT8 *cgrom;
+
+ void cursor_step(bool direction);
+ void shift_step(bool direction);
+ bool blink_on() const;
+};
+
+class msm6222b_01_device : public msm6222b_device {
+public:
+ msm6222b_01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual const rom_entry *device_rom_region() const;
+};
+
+extern const device_type MSM6222B;
+extern const device_type MSM6222B_01;
+
+#endif
diff --git a/src/devices/video/msm6255.c b/src/devices/video/msm6255.c
new file mode 100644
index 00000000000..36fd36325d1
--- /dev/null
+++ b/src/devices/video/msm6255.c
@@ -0,0 +1,437 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ OKI MSM6255 Dot Matrix LCD Controller implementation
+
+**********************************************************************/
+
+#include "msm6255.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+#define MOR_GRAPHICS 0x01
+#define MOR_4_BIT_PARALLEL 0x02
+#define MOR_2_BIT_PARALLEL 0x04
+#define MOR_DISPLAY_ON 0x08
+#define MOR_CURSOR_BLINK 0x10
+#define MOR_CURSOR_ON 0x20
+#define MOR_BLINK_TIME_16 0x40
+
+
+#define PR_HP_4 0x03
+#define PR_HP_5 0x04
+#define PR_HP_6 0x05
+#define PR_HP_7 0x06
+#define PR_HP_8 0x07
+#define PR_HP_MASK 0x07
+#define PR_VP_MASK 0xf0
+
+
+#define HNR_HN_MASK 0x7f
+
+
+#define DVR_DN_MASK 0x7f
+
+
+#define CPR_CPD_MASK 0x0f
+#define CPR_CPU_MASK 0xf0
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// device type definition
+const device_type MSM6255 = &device_creator<msm6255_device>;
+
+// I/O map
+DEVICE_ADDRESS_MAP_START( map, 8, msm6255_device )
+ AM_RANGE(0x00, 0x00) AM_READWRITE(dr_r, dr_w)
+ AM_RANGE(0x01, 0x01) AM_READWRITE(ir_r, ir_w)
+ADDRESS_MAP_END
+
+// default address map
+static ADDRESS_MAP_START( msm6255, AS_0, 8, msm6255_device )
+ AM_RANGE(0x00000, 0xfffff) AM_RAM
+ADDRESS_MAP_END
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// msm6255_device - constructor
+//-------------------------------------------------
+
+msm6255_device::msm6255_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, MSM6255, "MSM6255", tag, owner, clock, "msm6255", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 20, 0, NULL, *ADDRESS_MAP_NAME(msm6255)),
+ m_cursor(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void msm6255_device::device_start()
+{
+ // register for state saving
+ save_item(NAME(m_ir));
+ save_item(NAME(m_mor));
+ save_item(NAME(m_pr));
+ save_item(NAME(m_hnr));
+ save_item(NAME(m_dvr));
+ save_item(NAME(m_cpr));
+ save_item(NAME(m_slr));
+ save_item(NAME(m_sur));
+ save_item(NAME(m_clr));
+ save_item(NAME(m_cur));
+ save_item(NAME(m_cursor));
+ save_item(NAME(m_frame));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void msm6255_device::device_reset()
+{
+ m_frame = 0;
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *msm6255_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// ir_r -
+//-------------------------------------------------
+
+READ8_MEMBER( msm6255_device::ir_r )
+{
+ return m_ir;
+}
+
+
+//-------------------------------------------------
+// ir_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( msm6255_device::ir_w )
+{
+ m_ir = data & 0x0f;
+}
+
+
+//-------------------------------------------------
+// dr_r -
+//-------------------------------------------------
+
+READ8_MEMBER( msm6255_device::dr_r )
+{
+ UINT8 data = 0;
+
+ switch (m_ir)
+ {
+ case REGISTER_MOR:
+ break; // write-only
+
+ case REGISTER_PR:
+ data = m_pr;
+ break;
+
+ case REGISTER_HNR:
+ data = m_hnr;
+ break;
+
+ case REGISTER_DVR:
+ break; // write-only
+
+ case REGISTER_CPR:
+ data = m_cpr;
+ break;
+
+ case REGISTER_SLR:
+ data = m_slr;
+ break;
+
+ case REGISTER_SUR:
+ data = m_sur;
+ break;
+
+ case REGISTER_CLR:
+ data = m_clr;
+ break;
+
+ case REGISTER_CUR:
+ data = m_cur;
+ break;
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// dr_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( msm6255_device::dr_w )
+{
+ switch (m_ir)
+ {
+ case REGISTER_MOR:
+ m_mor = data & 0x7f;
+ break;
+
+ case REGISTER_PR:
+ m_pr = data & 0xf7;
+ break;
+
+ case REGISTER_HNR:
+ m_hnr = data & 0x7f;
+ break;
+
+ case REGISTER_DVR:
+ m_dvr = data;
+ break;
+
+ case REGISTER_CPR:
+ m_cpr = data;
+ break;
+
+ case REGISTER_SLR:
+ m_slr = data;
+ break;
+
+ case REGISTER_SUR:
+ m_sur = data;
+ break;
+
+ case REGISTER_CLR:
+ m_clr = data;
+ break;
+
+ case REGISTER_CUR:
+ m_cur = data;
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// read_byte -
+//-------------------------------------------------
+
+UINT8 msm6255_device::read_byte(UINT16 ma, UINT8 ra)
+{
+ offs_t offset;
+
+ if (m_mor & MOR_GRAPHICS)
+ {
+ offset = ma;
+ }
+ else
+ {
+ offset = ((offs_t)ma << 4) | ra;
+ }
+
+ return space().read_byte(offset);
+}
+
+
+//-------------------------------------------------
+// update_cursor -
+//-------------------------------------------------
+
+void msm6255_device::update_cursor()
+{
+ if (m_mor & MOR_CURSOR_ON)
+ {
+ if (m_mor & MOR_CURSOR_BLINK)
+ {
+ if (m_mor & MOR_BLINK_TIME_16)
+ {
+ if (m_frame == 16)
+ {
+ m_cursor = !m_cursor;
+ m_frame = 0;
+ }
+ else
+ {
+ m_frame++;
+ }
+ }
+ else
+ {
+ if (m_frame == 32)
+ {
+ m_cursor = !m_cursor;
+ m_frame = 0;
+ }
+ else
+ {
+ m_frame++;
+ }
+ }
+ }
+ else
+ {
+ m_cursor = 1;
+ }
+ }
+ else
+ {
+ m_cursor = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// draw_scanline -
+//-------------------------------------------------
+
+void msm6255_device::draw_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, UINT16 ma, UINT8 ra)
+{
+ UINT8 hp = (m_pr & PR_HP_MASK) + 1;
+ UINT8 hn = (m_hnr & HNR_HN_MASK) + 1;
+ UINT8 cpu = m_cpr & CPR_CPU_MASK;
+ UINT8 cpd = m_cpr & CPR_CPD_MASK;
+ UINT16 car = (m_cur << 8) | m_clr;
+
+ int sx, x;
+
+ for (sx = 0; sx < hn; sx++)
+ {
+ UINT8 data = read_byte(ma, ra);
+
+ if (m_cursor)
+ {
+ if (ma == car)
+ {
+ if (ra >= cpu && ra <= cpd)
+ {
+ data ^= 0xff;
+ }
+ }
+ }
+
+ for (x = 0; x < hp; x++)
+ {
+ bitmap.pix16(y, (sx * hp) + x) = BIT(data, 7);
+
+ data <<= 1;
+ }
+
+ ma++;
+ }
+}
+
+
+//-------------------------------------------------
+// update_graphics -
+//-------------------------------------------------
+
+void msm6255_device::update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ UINT8 hn = (m_hnr & HNR_HN_MASK) + 1;
+ UINT8 nx = (m_dvr & DVR_DN_MASK) + 1;
+ UINT16 sar = (m_sur << 8) | m_slr;
+
+ int y;
+
+ m_cursor = 0;
+ m_frame = 0;
+
+ for (y = 0; y < nx; y++)
+ {
+ // draw upper half scanline
+ UINT16 ma = sar + (y * hn);
+ draw_scanline(bitmap, cliprect, y, ma);
+
+ // draw lower half scanline
+ ma = sar + ((y + nx) * hn);
+ draw_scanline(bitmap, cliprect, y + nx, ma);
+ }
+}
+
+
+//-------------------------------------------------
+// update_text -
+//-------------------------------------------------
+
+void msm6255_device::update_text(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ UINT8 hn = (m_hnr & HNR_HN_MASK) + 1;
+ UINT8 vp = (m_pr & PR_VP_MASK) + 1;
+ UINT8 nx = (m_dvr & DVR_DN_MASK) + 1;
+ UINT16 sar = (m_sur << 8) | m_slr;
+
+ int sy, y;
+
+ update_cursor();
+
+ for (sy = 0; sy < nx; sy++)
+ {
+ for (y = 0; y < vp; y++)
+ {
+ // draw upper half scanline
+ UINT16 ma = sar + ((sy * vp) + y) * hn;
+ draw_scanline(bitmap, cliprect, (sy * vp) + y, ma, y);
+
+ // draw lower half scanline
+ ma = sar + (((sy + nx) * vp) + y) * hn;
+ draw_scanline(bitmap, cliprect, (sy * vp) + y, ma, y);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// update_screen - update screen
+//-------------------------------------------------
+
+UINT32 msm6255_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ if (m_mor & MOR_DISPLAY_ON)
+ {
+ if (m_mor & MOR_GRAPHICS)
+ {
+ update_graphics(bitmap, cliprect);
+ }
+ else
+ {
+ update_text(bitmap, cliprect);
+ }
+ }
+ else
+ {
+ bitmap.fill(0, cliprect);
+ }
+
+ return 0;
+}
diff --git a/src/devices/video/msm6255.h b/src/devices/video/msm6255.h
new file mode 100644
index 00000000000..32975132cf6
--- /dev/null
+++ b/src/devices/video/msm6255.h
@@ -0,0 +1,95 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ OKI MSM6255 Dot Matrix LCD Controller implementation
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __MSM6255__
+#define __MSM6255__
+
+#include "emu.h"
+
+
+
+///*************************************************************************
+// TYPE DEFINITIONS
+///*************************************************************************
+
+// ======================> msm6255_device
+
+class msm6255_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ msm6255_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual DECLARE_ADDRESS_MAP(map, 8);
+
+ DECLARE_READ8_MEMBER( ir_r );
+ DECLARE_WRITE8_MEMBER( ir_w );
+
+ DECLARE_READ8_MEMBER( dr_r );
+ DECLARE_WRITE8_MEMBER( dr_w );
+
+ UINT32 screen_update(screen_device &device, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+private:
+ // registers
+ enum
+ {
+ REGISTER_MOR = 0,
+ REGISTER_PR,
+ REGISTER_HNR,
+ REGISTER_DVR,
+ REGISTER_CPR,
+ REGISTER_SLR,
+ REGISTER_SUR,
+ REGISTER_CLR,
+ REGISTER_CUR
+ };
+
+ UINT8 read_byte(UINT16 ma, UINT8 ra);
+
+ void update_cursor();
+ void draw_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, UINT16 ma, UINT8 ra = 0);
+ void update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void update_text(bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+ const address_space_config m_space_config;
+
+ UINT8 m_ir; // instruction register
+ UINT8 m_mor; // mode control register
+ UINT8 m_pr; // character pitch register
+ UINT8 m_hnr; // horizontal character number register
+ UINT8 m_dvr; // duty number register
+ UINT8 m_cpr; // cursor form register
+ UINT8 m_slr; // start address (lower) register
+ UINT8 m_sur; // start address (upper) register
+ UINT8 m_clr; // cursor address (lower) register
+ UINT8 m_cur; // cursor address (upper) register
+
+ int m_cursor; // is cursor displayed
+ int m_frame; // frame counter
+};
+
+
+// device type definition
+extern const device_type MSM6255;
+
+
+
+#endif
diff --git a/src/devices/video/pc_vga.c b/src/devices/video/pc_vga.c
new file mode 100644
index 00000000000..c494e4904ec
--- /dev/null
+++ b/src/devices/video/pc_vga.c
@@ -0,0 +1,5559 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods, Peter Trauner, Angelo Salese
+/***************************************************************************
+
+ Video Graphics Adapter (VGA) section
+
+ Nathan Woods npwoods@mess.org
+ Peter Trauner PeT mess@utanet.at
+
+ This code takes care of installing the various VGA memory and port
+ handlers
+
+ The VGA standard is compatible with MDA, CGA, Hercules, EGA
+ (mda, cga, hercules not real register compatible)
+ several vga cards drive also mda, cga, ega monitors
+ some vga cards have register compatible mda, cga, hercules modes
+
+ ega/vga
+ 64k (early ega 16k) words of 32 bit memory
+
+ TODO:
+ - modernize
+ - fix video update, still need to get that choosevideomode() out of it.
+ - rewrite video drawing functions (they are horrible)
+ - add per-gfx card VESA functions;
+ - (and many more ...)
+
+ per-game issues:
+ - The Incredible Machine: fix partial updates
+ - MAME 0.01: fix 92 Hz refresh rate bug (uses VESA register?).
+ - Virtual Pool: ET4k unrecognized;
+ - California Chase (calchase): various gfx bugs, CPU related?
+ - Jazz Jackrabbit: status bar is very jerky, but main screen scrolling is fine?
+ - Catacombs: weird resolution (untested)
+
+ ROM declarations:
+
+ (oti 037 chip)
+ ROM_LOAD("oakvga.bin", 0xc0000, 0x8000, 0x318c5f43)
+ (tseng labs famous et4000 isa vga card (oem))
+ ROM_LOAD("et4000b.bin", 0xc0000, 0x8000, 0xa903540d)
+ (tseng labs famous et4000 isa vga card)
+ ROM_LOAD("et4000.bin", 0xc0000, 0x8000, 0xf01e4be0)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "pc_vga.h"
+#include "bus/isa/trident.h"
+#include "machine/eepromser.h"
+#include "debugger.h"
+
+/***************************************************************************
+
+ Local variables
+
+***************************************************************************/
+
+enum
+{
+ IBM8514_IDLE = 0,
+ IBM8514_DRAWING_RECT,
+ IBM8514_DRAWING_LINE,
+ IBM8514_DRAWING_BITBLT,
+ IBM8514_DRAWING_PATTERN,
+ IBM8514_DRAWING_SSV_1,
+ IBM8514_DRAWING_SSV_2
+};
+
+#define CRTC_PORT_ADDR ((vga.miscellaneous_output&1)?0x3d0:0x3b0)
+
+//#define TEXT_LINES (LINES_HELPER)
+#define LINES (vga.crtc.vert_disp_end+1)
+#define TEXT_LINES (vga.crtc.vert_disp_end+1)
+
+#define GRAPHIC_MODE (vga.gc.alpha_dis) /* else text mode */
+
+#define EGA_COLUMNS (vga.crtc.horz_disp_end+1)
+#define EGA_START_ADDRESS (vga.crtc.start_addr)
+#define EGA_LINE_LENGTH (vga.crtc.offset<<1)
+
+#define VGA_COLUMNS (vga.crtc.horz_disp_end+1)
+#define VGA_START_ADDRESS (vga.crtc.start_addr)
+#define VGA_LINE_LENGTH (vga.crtc.offset<<3)
+
+#define IBM8514_LINE_LENGTH (m_vga->offset())
+
+#define CHAR_WIDTH ((vga.sequencer.data[1]&1)?8:9)
+
+#define TEXT_COLUMNS (vga.crtc.horz_disp_end+1)
+#define TEXT_START_ADDRESS (vga.crtc.start_addr<<3)
+#define TEXT_LINE_LENGTH (vga.crtc.offset<<1)
+
+#define TEXT_COPY_9COLUMN(ch) (((ch & 0xe0) == 0xc0)&&(vga.attribute.data[0x10]&4))
+
+// Special values for SVGA Trident - Mode Vesa 110h
+#define TLINES (LINES)
+#define TGA_COLUMNS (EGA_COLUMNS)
+#define TGA_START_ADDRESS (vga.crtc.start_addr<<2)
+#define TGA_LINE_LENGTH (vga.crtc.offset<<3)
+
+
+/***************************************************************************
+
+ Static declarations
+
+***************************************************************************/
+
+#define LOG_ACCESSES 0
+#define LOG_REGISTERS 0
+
+#define LOG_8514 1
+
+/***************************************************************************
+
+ Generic VGA
+
+***************************************************************************/
+// device type definition
+const device_type VGA = &device_creator<vga_device>;
+const device_type TSENG_VGA = &device_creator<tseng_vga_device>;
+const device_type S3_VGA = &device_creator<s3_vga_device>;
+const device_type GAMTOR_VGA = &device_creator<gamtor_vga_device>;
+const device_type ATI_VGA = &device_creator<ati_vga_device>;
+const device_type IBM8514A = &device_creator<ibm8514a_device>;
+const device_type MACH8 = &device_creator<mach8_device>;
+
+vga_device::vga_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_palette(*this, "^palette"),
+ m_screen(*this,"^screen")
+{
+}
+
+vga_device::vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, VGA, "VGA", tag, owner, clock, "vga", __FILE__),
+ m_palette(*this, "^palette"),
+ m_screen(*this,"^screen")
+{
+}
+
+svga_device::svga_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : vga_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+tseng_vga_device::tseng_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : svga_device(mconfig, TSENG_VGA, "TSENG LABS VGA", tag, owner, clock, "tseng_vga", __FILE__)
+{
+}
+
+s3_vga_device::s3_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ati_vga_device(mconfig, S3_VGA, "S3 Graphics VGA", tag, owner, clock, "s3_vga", __FILE__)
+{
+}
+
+s3_vga_device::s3_vga_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : ati_vga_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+gamtor_vga_device::gamtor_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : svga_device(mconfig, GAMTOR_VGA, "GAMTOR VGA", tag, owner, clock, "gamtor_vga", __FILE__)
+{
+}
+
+ati_vga_device::ati_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : svga_device(mconfig, ATI_VGA, "ATI VGA", tag, owner, clock, "ati_vga", __FILE__)
+{
+}
+
+ati_vga_device::ati_vga_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : svga_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+ibm8514a_device::ibm8514a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, IBM8514A, "IBM8514A Video", tag, owner, clock, "ibm8514a", __FILE__)
+{
+}
+
+ibm8514a_device::ibm8514a_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+mach8_device::mach8_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : ibm8514a_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+mach8_device::mach8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ibm8514a_device(mconfig, MACH8, "MACH8", tag, owner, clock, "mach8", __FILE__)
+{
+}
+
+// zero everything, keep vtbls
+void vga_device::zero()
+{
+ memset(&vga.svga_intf, 0, sizeof(vga.svga_intf));
+ vga.memory.resize(0);
+ memset(vga.pens, 0, sizeof(vga.pens));
+ vga.miscellaneous_output = 0;
+ vga.feature_control = 0;
+ memset(&vga.sequencer, 0, sizeof(vga.sequencer));
+ memset(&vga.crtc, 0, sizeof(vga.crtc));
+ memset(&vga.gc, 0, sizeof(vga.gc));
+ memset(&vga.attribute, 0, sizeof(vga.attribute));
+ memset(&vga.dac, 0, sizeof(vga.dac));
+ memset(&vga.oak, 0, sizeof(vga.oak));
+}
+
+void svga_device::zero()
+{
+ vga_device::zero();
+ memset(&svga, 0, sizeof(svga));
+}
+
+/* VBLANK callback, start address definitely updates AT vblank, not before. */
+TIMER_CALLBACK_MEMBER(vga_device::vblank_timer_cb)
+{
+ vga.crtc.start_addr = vga.crtc.start_addr_latch;
+ vga.attribute.pel_shift = vga.attribute.pel_shift_latch;
+ m_vblank_timer->adjust( machine().first_screen()->time_until_pos(vga.crtc.vert_blank_start + vga.crtc.vert_blank_end) );
+}
+
+void vga_device::device_start()
+{
+ zero();
+
+ int i;
+ for (i = 0; i < 0x100; i++)
+ m_palette->set_pen_color(i, 0, 0, 0);
+
+ // Avoid an infinite loop when displaying. 0 is not possible anyway.
+ vga.crtc.maximum_scan_line = 1;
+
+
+ // copy over interfaces
+ vga.read_dipswitch = read8_delegate(); //read_dipswitch;
+ vga.svga_intf.seq_regcount = 0x05;
+ vga.svga_intf.crtc_regcount = 0x19;
+ vga.svga_intf.vram_size = 0x100000;
+
+ vga.memory.resize(vga.svga_intf.vram_size);
+ memset(&vga.memory[0], 0, vga.svga_intf.vram_size);
+ save_item(NAME(vga.memory));
+ save_pointer(vga.crtc.data,"CRTC Registers",0x100);
+ save_pointer(vga.sequencer.data,"Sequencer Registers",0x100);
+ save_pointer(vga.attribute.data,"Attribute Registers", 0x15);
+
+ m_vblank_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(vga_device::vblank_timer_cb),this));
+}
+
+void svga_device::device_start()
+{
+ vga_device::device_start();
+ memset(&svga, 0, sizeof(svga));
+}
+
+void ati_vga_device::device_start()
+{
+ svga_device::device_start();
+ memset(&ati, 0, sizeof(ati));
+ save_pointer(ati.ext_reg,"ATi Extended Registers",64);
+ m_8514 = subdevice<mach8_device>("8514a");
+ ati.vga_chip_id = 0x06; // 28800-6
+}
+
+void s3_vga_device::device_start()
+{
+ svga_device::device_start();
+ memset(&s3, 0, sizeof(s3));
+ int x;
+ // Initialise hardware graphics cursor colours, Windows 95 doesn't touch the registers for some reason
+ for(x=0;x<4;x++)
+ {
+ s3.cursor_fg[x] = 0xff;
+ s3.cursor_bg[x] = 0x00;
+ }
+ m_8514 = subdevice<ibm8514a_device>("8514a");
+ // set device ID
+ s3.id_high = 0x88; // CR2D
+ s3.id_low = 0x11; // CR2E
+ s3.revision = 0x00; // CR2F
+ s3.id_cr30 = 0xe1; // CR30
+}
+
+void tseng_vga_device::device_start()
+{
+ svga_device::device_start();
+ memset(&et4k, 0, sizeof(et4k));
+}
+
+void ibm8514a_device::device_start()
+{
+ memset(&ibm8514, 0, sizeof(ibm8514));
+ ibm8514.read_mask = 0x00000000;
+ ibm8514.write_mask = 0xffffffff;
+}
+
+void ibm8514a_device::device_config_complete()
+{
+ if(m_vga_tag.length() != 0)
+ {
+ m_vga = machine().device<vga_device>(m_vga_tag.c_str());
+ }
+}
+
+void mach8_device::device_start()
+{
+ ibm8514a_device::device_start();
+ memset(&mach8, 0, sizeof(mach8));
+}
+
+UINT16 vga_device::offset()
+{
+// popmessage("Offset: %04x %s %s **",vga.crtc.offset,vga.crtc.dw?"DW":"--",vga.crtc.word_mode?"BYTE":"WORD");
+ if(vga.crtc.dw)
+ return vga.crtc.offset << 3;
+ if(vga.crtc.word_mode)
+ return vga.crtc.offset << 1;
+ else
+ return vga.crtc.offset << 2;
+}
+
+void vga_device::vga_vh_text(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT8 ch, attr;
+ UINT8 bits;
+ UINT32 font_base;
+ UINT32 *bitmapline;
+ int width=CHAR_WIDTH, height = (vga.crtc.maximum_scan_line) * (vga.crtc.scan_doubling + 1);
+ int pos, line, column, mask, w, h, addr;
+ UINT8 blink_en,fore_col,back_col;
+ pen_t pen;
+
+ if(vga.crtc.cursor_enable)
+ vga.cursor.visible = machine().first_screen()->frame_number() & 0x10;
+ else
+ vga.cursor.visible = 0;
+
+ for (addr = vga.crtc.start_addr, line = -vga.crtc.preset_row_scan; line < TEXT_LINES;
+ line += height, addr += (offset()>>1))
+ {
+ for (pos = addr, column=0; column<TEXT_COLUMNS; column++, pos++)
+ {
+ ch = vga.memory[(pos<<1) + 0];
+ attr = vga.memory[(pos<<1) + 1];
+ font_base = 0x20000+(ch<<5);
+ font_base += ((attr & 8) ? vga.sequencer.char_sel.A : vga.sequencer.char_sel.B)*0x2000;
+ blink_en = (vga.attribute.data[0x10]&8&&machine().first_screen()->frame_number() & 0x20) ? attr & 0x80 : 0;
+
+ fore_col = attr & 0xf;
+ back_col = (attr & 0x70) >> 4;
+ back_col |= (vga.attribute.data[0x10]&8) ? 0 : ((attr & 0x80) >> 4);
+
+ for (h = MAX(-line, 0); (h < height) && (line+h < MIN(TEXT_LINES, bitmap.height())); h++)
+ {
+ bitmapline = &bitmap.pix32(line+h);
+ bits = vga.memory[font_base+(h>>(vga.crtc.scan_doubling))];
+
+ for (mask=0x80, w=0; (w<width)&&(w<8); w++, mask>>=1)
+ {
+ if (bits&mask)
+ pen = vga.pens[blink_en ? back_col : fore_col];
+ else
+ pen = vga.pens[back_col];
+
+ if(!machine().first_screen()->visible_area().contains(column*width+w, line+h))
+ continue;
+ bitmapline[column*width+w] = pen;
+
+ }
+ if (w<width)
+ {
+ /* 9 column */
+ if (TEXT_COPY_9COLUMN(ch)&&(bits&1))
+ pen = vga.pens[blink_en ? back_col : fore_col];
+ else
+ pen = vga.pens[back_col];
+
+ if(!machine().first_screen()->visible_area().contains(column*width+w, line+h))
+ continue;
+ bitmapline[column*width+w] = pen;
+ }
+ }
+ if (vga.cursor.visible&&(pos==vga.crtc.cursor_addr))
+ {
+ for (h=vga.crtc.cursor_scan_start;
+ (h<=vga.crtc.cursor_scan_end)&&(h<height)&&(line+h<TEXT_LINES);
+ h++)
+ {
+ if(!machine().first_screen()->visible_area().contains(column*width, line+h))
+ continue;
+ bitmap.plot_box(column*width, line+h, width, 1, vga.pens[attr&0xf]);
+ }
+ }
+ }
+ }
+}
+
+void vga_device::vga_vh_ega(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int pos, line, column, c, addr, i, yi;
+ int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
+ UINT32 *bitmapline;
+ pen_t pen;
+ int pel_shift = (vga.attribute.pel_shift & 7);
+
+// popmessage("%08x %02x",EGA_START_ADDRESS,pel_shift);
+
+ /**/
+ for (addr=EGA_START_ADDRESS, pos=0, line=0; line<LINES;
+ line += height, addr += offset())
+ {
+ for(yi=0;yi<height;yi++)
+ {
+ bitmapline = &bitmap.pix32(line + yi);
+
+ for (pos=addr, c=0, column=0; column<EGA_COLUMNS+1; column++, c+=8, pos=(pos+1)&0xffff)
+ {
+ int data[4];
+
+ data[0]=vga.memory[(pos & 0xffff)];
+ data[1]=vga.memory[(pos & 0xffff)+0x10000]<<1;
+ data[2]=vga.memory[(pos & 0xffff)+0x20000]<<2;
+ data[3]=vga.memory[(pos & 0xffff)+0x30000]<<3;
+
+ for (i = 7; i >= 0; i--)
+ {
+ pen = vga.pens[(data[0]&1) | (data[1]&2) | (data[2]&4) | (data[3]&8)];
+
+ data[0]>>=1;
+ data[1]>>=1;
+ data[2]>>=1;
+ data[3]>>=1;
+
+ if(!machine().first_screen()->visible_area().contains(c+i-pel_shift, line + yi))
+ continue;
+ bitmapline[c+i-pel_shift] = pen;
+ }
+ }
+ }
+ }
+}
+
+/* TODO: I'm guessing that in 256 colors mode every pixel actually outputs two pixels. Is it right? */
+void vga_device::vga_vh_vga(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int pos, line, column, c, addr, curr_addr;
+ UINT32 *bitmapline;
+ UINT16 mask_comp;
+ int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
+ int yi;
+ int xi;
+ int pel_shift = (vga.attribute.pel_shift & 6);
+
+ /* line compare is screen sensitive */
+ mask_comp = 0x3ff; //| (LINES & 0x300);
+
+// popmessage("%02x %02x",vga.attribute.pel_shift,vga.sequencer.data[4] & 0x08);
+
+ curr_addr = 0;
+ if(!(vga.sequencer.data[4] & 0x08))
+ {
+ for (addr = VGA_START_ADDRESS, line=0; line<LINES; line+=height, addr+=offset(), curr_addr+=offset())
+ {
+ for(yi = 0;yi < height; yi++)
+ {
+ if((line + yi) < (vga.crtc.line_compare & mask_comp))
+ curr_addr = addr;
+ if((line + yi) == (vga.crtc.line_compare & mask_comp))
+ {
+ curr_addr = 0;
+ pel_shift = 0;
+ }
+ bitmapline = &bitmap.pix32(line + yi);
+ for (pos=curr_addr, c=0, column=0; column<VGA_COLUMNS+1; column++, c+=8, pos++)
+ {
+ if(pos > 0x80000/4)
+ return;
+
+ for(xi=0;xi<8;xi++)
+ {
+ if(!machine().first_screen()->visible_area().contains(c+xi-(pel_shift), line + yi))
+ continue;
+ bitmapline[c+xi-(pel_shift)] = m_palette->pen(vga.memory[(pos & 0xffff)+((xi >> 1)*0x10000)]);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for (addr = VGA_START_ADDRESS, line=0; line<LINES; line+=height, addr+=offset(), curr_addr+=offset())
+ {
+ for(yi = 0;yi < height; yi++)
+ {
+ if((line + yi) < (vga.crtc.line_compare & mask_comp))
+ curr_addr = addr;
+ if((line + yi) == (vga.crtc.line_compare & mask_comp))
+ curr_addr = 0;
+ bitmapline = &bitmap.pix32(line + yi);
+ //addr %= 0x80000;
+ for (pos=curr_addr, c=0, column=0; column<VGA_COLUMNS+1; column++, c+=0x10, pos+=0x8)
+ {
+ if(pos + 0x08 > 0x80000)
+ return;
+
+ for(xi=0;xi<0x10;xi++)
+ {
+ if(!machine().first_screen()->visible_area().contains(c+xi-(pel_shift), line + yi))
+ continue;
+ bitmapline[c+xi-pel_shift] = m_palette->pen(vga.memory[(pos+(xi >> 1)) & 0xffff]);
+ }
+ }
+ }
+ }
+ }
+}
+
+void vga_device::vga_vh_cga(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT32 *bitmapline;
+ int height = (vga.crtc.scan_doubling + 1);
+ int x,xi,y,yi;
+ UINT32 addr;
+ pen_t pen;
+ int width;
+
+ width = (vga.crtc.horz_disp_end + 1) * 8;
+
+ for(y=0;y<LINES;y++)
+ {
+ addr = ((y & 1) * 0x2000) + (((y & ~1) >> 1) * width/4);
+
+ for(x=0;x<width;x+=4)
+ {
+ for(yi=0;yi<height;yi++)
+ {
+ bitmapline = &bitmap.pix32(y * height + yi);
+
+ for(xi=0;xi<4;xi++)
+ {
+ pen = vga.pens[(vga.memory[addr] >> (6-xi*2)) & 3];
+ if(!machine().first_screen()->visible_area().contains(x+xi, y * height + yi))
+ continue;
+ bitmapline[x+xi] = pen;
+ }
+ }
+
+ addr++;
+ }
+ }
+}
+
+void vga_device::vga_vh_mono(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT32 *bitmapline;
+ int height = (vga.crtc.scan_doubling + 1);
+ int x,xi,y,yi;
+ UINT32 addr;
+ pen_t pen;
+ int width;
+
+ width = (vga.crtc.horz_disp_end + 1) * 8;
+
+ for(y=0;y<LINES;y++)
+ {
+ addr = ((y & 1) * 0x2000) + (((y & ~1) >> 1) * width/8);
+
+ for(x=0;x<width;x+=8)
+ {
+ for(yi=0;yi<height;yi++)
+ {
+ bitmapline = &bitmap.pix32(y * height + yi);
+
+ for(xi=0;xi<8;xi++)
+ {
+ pen = vga.pens[(vga.memory[addr] >> (7-xi)) & 1];
+ if(!machine().first_screen()->visible_area().contains(x+xi, y * height + yi))
+ continue;
+ bitmapline[x+xi] = pen;
+ }
+ }
+
+ addr++;
+ }
+ }
+}
+
+void svga_device::svga_vh_rgb8(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int pos, line, column, c, addr, curr_addr;
+ UINT32 *bitmapline;
+ UINT16 mask_comp;
+ int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
+ int yi;
+ int xi;
+ UINT8 start_shift;
+// UINT16 line_length;
+
+ /* line compare is screen sensitive */
+ mask_comp = 0x3ff;
+ curr_addr = 0;
+// if(vga.crtc.dw)
+// line_length = vga.crtc.offset << 3; // doubleword mode
+// else
+// {
+// line_length = vga.crtc.offset << 4;
+// }
+
+ start_shift = (!(vga.sequencer.data[4] & 0x08)) ? 2 : 0;
+ {
+ for (addr = VGA_START_ADDRESS << start_shift, line=0; line<LINES; line+=height, addr+=offset(), curr_addr+=offset())
+ {
+ for(yi = 0;yi < height; yi++)
+ {
+ if((line + yi) < (vga.crtc.line_compare & mask_comp))
+ curr_addr = addr;
+ if((line + yi) == (vga.crtc.line_compare & mask_comp))
+ curr_addr = 0;
+ bitmapline = &bitmap.pix32(line + yi);
+ addr %= vga.svga_intf.vram_size;
+ for (pos=curr_addr, c=0, column=0; column<VGA_COLUMNS; column++, c+=8, pos+=0x8)
+ {
+ if(pos + 0x08 >= vga.svga_intf.vram_size)
+ return;
+
+ for(xi=0;xi<8;xi++)
+ {
+ if(!machine().first_screen()->visible_area().contains(c+xi, line + yi))
+ continue;
+ bitmapline[c+xi] = m_palette->pen(vga.memory[(pos+(xi))]);
+ }
+ }
+ }
+ }
+ }
+}
+
+void svga_device::svga_vh_rgb15(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ #define MV(x) (vga.memory[x]+(vga.memory[x+1]<<8))
+ #define IV 0xff000000
+ int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
+ int xi;
+ int yi;
+ int xm;
+ int pos, line, column, c, addr, curr_addr;
+
+ UINT32 *bitmapline;
+// UINT16 mask_comp;
+
+ /* line compare is screen sensitive */
+// mask_comp = 0xff | (TLINES & 0x300);
+ curr_addr = 0;
+ yi=0;
+ for (addr = TGA_START_ADDRESS, line=0; line<TLINES; line+=height, addr+=offset(), curr_addr+=offset())
+ {
+ bitmapline = &bitmap.pix32(line);
+ addr %= vga.svga_intf.vram_size;
+ for (pos=addr, c=0, column=0; column<TGA_COLUMNS; column++, c+=8, pos+=0x10)
+ {
+ if(pos + 0x10 >= vga.svga_intf.vram_size)
+ return;
+ for(xi=0,xm=0;xi<8;xi++,xm+=2)
+ {
+ int r,g,b;
+
+ if(!machine().first_screen()->visible_area().contains(c+xi, line + yi))
+ continue;
+
+ r = (MV(pos+xm)&0x7c00)>>10;
+ g = (MV(pos+xm)&0x03e0)>>5;
+ b = (MV(pos+xm)&0x001f)>>0;
+ r = (r << 3) | (r & 0x7);
+ g = (g << 3) | (g & 0x7);
+ b = (b << 3) | (b & 0x7);
+ bitmapline[c+xi] = IV|(r<<16)|(g<<8)|(b<<0);
+ }
+ }
+ }
+}
+
+void svga_device::svga_vh_rgb16(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ #define MV(x) (vga.memory[x]+(vga.memory[x+1]<<8))
+ #define IV 0xff000000
+ int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
+ int xi;
+ int yi;
+ int xm;
+ int pos, line, column, c, addr, curr_addr;
+
+ UINT32 *bitmapline;
+// UINT16 mask_comp;
+
+ /* line compare is screen sensitive */
+// mask_comp = 0xff | (TLINES & 0x300);
+ curr_addr = 0;
+ yi=0;
+ for (addr = TGA_START_ADDRESS, line=0; line<TLINES; line+=height, addr+=offset(), curr_addr+=offset())
+ {
+ bitmapline = &bitmap.pix32(line);
+ addr %= vga.svga_intf.vram_size;
+ for (pos=addr, c=0, column=0; column<TGA_COLUMNS; column++, c+=8, pos+=0x10)
+ {
+ if(pos + 0x10 >= vga.svga_intf.vram_size)
+ return;
+ for(xi=0,xm=0;xi<8;xi++,xm+=2)
+ {
+ int r,g,b;
+
+ if(!machine().first_screen()->visible_area().contains(c+xi, line + yi))
+ continue;
+
+ r = (MV(pos+xm)&0xf800)>>11;
+ g = (MV(pos+xm)&0x07e0)>>5;
+ b = (MV(pos+xm)&0x001f)>>0;
+ r = (r << 3) | (r & 0x7);
+ g = (g << 2) | (g & 0x3);
+ b = (b << 3) | (b & 0x7);
+ bitmapline[c+xi] = IV|(r<<16)|(g<<8)|(b<<0);
+ }
+ }
+ }
+}
+
+void svga_device::svga_vh_rgb24(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ #define MD(x) (vga.memory[x]+(vga.memory[x+1]<<8)+(vga.memory[x+2]<<16))
+ #define ID 0xff000000
+ int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
+ int xi;
+ int yi;
+ int xm;
+ int pos, line, column, c, addr, curr_addr;
+ UINT32 *bitmapline;
+
+// UINT16 mask_comp;
+
+ /* line compare is screen sensitive */
+// mask_comp = 0xff | (TLINES & 0x300);
+ curr_addr = 0;
+ yi=0;
+ for (addr = TGA_START_ADDRESS<<1, line=0; line<TLINES; line+=height, addr+=offset(), curr_addr+=offset())
+ {
+ bitmapline = &bitmap.pix32(line);
+ addr %= vga.svga_intf.vram_size;
+ for (pos=addr, c=0, column=0; column<TGA_COLUMNS; column++, c+=8, pos+=24)
+ {
+ if(pos + 24 >= vga.svga_intf.vram_size)
+ return;
+ for(xi=0,xm=0;xi<8;xi++,xm+=3)
+ {
+ int r,g,b;
+
+ if(!machine().first_screen()->visible_area().contains(c+xi, line + yi))
+ continue;
+
+ r = (MD(pos+xm)&0xff0000)>>16;
+ g = (MD(pos+xm)&0x00ff00)>>8;
+ b = (MD(pos+xm)&0x0000ff)>>0;
+ bitmapline[c+xi] = IV|(r<<16)|(g<<8)|(b<<0);
+ }
+ }
+ }
+}
+
+void svga_device::svga_vh_rgb32(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ #define MD(x) (vga.memory[x]+(vga.memory[x+1]<<8)+(vga.memory[x+2]<<16))
+ #define ID 0xff000000
+ int height = vga.crtc.maximum_scan_line * (vga.crtc.scan_doubling + 1);
+ int xi;
+ int yi;
+ int xm;
+ int pos, line, column, c, addr, curr_addr;
+ UINT32 *bitmapline;
+
+// UINT16 mask_comp;
+
+ /* line compare is screen sensitive */
+// mask_comp = 0xff | (TLINES & 0x300);
+ curr_addr = 0;
+ yi=0;
+ for (addr = TGA_START_ADDRESS, line=0; line<TLINES; line+=height, addr+=(offset()), curr_addr+=(offset()))
+ {
+ bitmapline = &bitmap.pix32(line);
+ addr %= vga.svga_intf.vram_size;
+ for (pos=addr, c=0, column=0; column<TGA_COLUMNS; column++, c+=8, pos+=0x20)
+ {
+ if(pos + 0x20 >= vga.svga_intf.vram_size)
+ return;
+ for(xi=0,xm=0;xi<8;xi++,xm+=4)
+ {
+ int r,g,b;
+
+ if(!machine().first_screen()->visible_area().contains(c+xi, line + yi))
+ continue;
+
+ r = (MD(pos+xm)&0xff0000)>>16;
+ g = (MD(pos+xm)&0x00ff00)>>8;
+ b = (MD(pos+xm)&0x0000ff)>>0;
+ bitmapline[c+xi] = IV|(r<<16)|(g<<8)|(b<<0);
+ }
+ }
+ }
+}
+
+UINT8 vga_device::pc_vga_choosevideomode()
+{
+ int i;
+
+ if (vga.crtc.sync_en)
+ {
+ if (vga.dac.dirty)
+ {
+ for (i=0; i<256;i++)
+ {
+ /* TODO: color shifters? */
+ m_palette->set_pen_color(i, (vga.dac.color[i & vga.dac.mask].red & 0x3f) << 2,
+ (vga.dac.color[i & vga.dac.mask].green & 0x3f) << 2,
+ (vga.dac.color[i & vga.dac.mask].blue & 0x3f) << 2);
+ }
+ vga.dac.dirty = 0;
+ }
+
+ if (vga.attribute.data[0x10] & 0x80)
+ {
+ for (i=0; i<16;i++)
+ {
+ vga.pens[i] = m_palette->pen((vga.attribute.data[i]&0x0f)
+ |((vga.attribute.data[0x14]&0xf)<<4));
+ }
+ }
+ else
+ {
+ for (i=0; i<16;i++)
+ {
+ vga.pens[i]=m_palette->pen((vga.attribute.data[i]&0x3f)
+ |((vga.attribute.data[0x14]&0xc)<<4));
+ }
+ }
+
+ if (!GRAPHIC_MODE)
+ {
+ return TEXT_MODE;
+ }
+ else if (vga.gc.shift256)
+ {
+ return VGA_MODE;
+ }
+ else if (vga.gc.shift_reg)
+ {
+ return CGA_MODE;
+ }
+ else if (vga.gc.memory_map_sel == 0x03)
+ {
+ return MONO_MODE;
+ }
+ else
+ {
+ return EGA_MODE;
+ }
+ }
+
+ return SCREEN_OFF;
+}
+
+
+UINT8 svga_device::pc_vga_choosevideomode()
+{
+ int i;
+
+ if (vga.crtc.sync_en)
+ {
+ if (vga.dac.dirty)
+ {
+ for (i=0; i<256;i++)
+ {
+ /* TODO: color shifters? */
+ m_palette->set_pen_color(i, (vga.dac.color[i & vga.dac.mask].red & 0x3f) << 2,
+ (vga.dac.color[i & vga.dac.mask].green & 0x3f) << 2,
+ (vga.dac.color[i & vga.dac.mask].blue & 0x3f) << 2);
+ }
+ vga.dac.dirty = 0;
+ }
+
+ if (vga.attribute.data[0x10] & 0x80)
+ {
+ for (i=0; i<16;i++)
+ {
+ vga.pens[i] = m_palette->pen((vga.attribute.data[i]&0x0f)
+ |((vga.attribute.data[0x14]&0xf)<<4));
+ }
+ }
+ else
+ {
+ for (i=0; i<16;i++)
+ {
+ vga.pens[i]=m_palette->pen((vga.attribute.data[i]&0x3f)
+ |((vga.attribute.data[0x14]&0xc)<<4));
+ }
+ }
+
+ if (svga.rgb32_en)
+ {
+ return RGB32_MODE;
+ }
+ else if (svga.rgb24_en)
+ {
+ return RGB24_MODE;
+ }
+ else if (svga.rgb16_en)
+ {
+ return RGB16_MODE;
+ }
+ else if (svga.rgb15_en)
+ {
+ return RGB15_MODE;
+ }
+ else if (svga.rgb8_en)
+ {
+ return RGB8_MODE;
+ }
+ else if (!GRAPHIC_MODE)
+ {
+ return TEXT_MODE;
+ }
+ else if (vga.gc.shift256)
+ {
+ return VGA_MODE;
+ }
+ else if (vga.gc.shift_reg)
+ {
+ return CGA_MODE;
+ }
+ else if (vga.gc.memory_map_sel == 0x03)
+ {
+ return MONO_MODE;
+ }
+ else
+ {
+ return EGA_MODE;
+ }
+ }
+
+ return SCREEN_OFF;
+}
+
+
+UINT32 vga_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT8 cur_mode = pc_vga_choosevideomode();
+ switch(cur_mode)
+ {
+ case SCREEN_OFF: bitmap.fill (m_palette->black_pen(), cliprect);break;
+ case TEXT_MODE: vga_vh_text (bitmap, cliprect); break;
+ case VGA_MODE: vga_vh_vga (bitmap, cliprect); break;
+ case EGA_MODE: vga_vh_ega (bitmap, cliprect); break;
+ case CGA_MODE: vga_vh_cga (bitmap, cliprect); break;
+ case MONO_MODE: vga_vh_mono (bitmap, cliprect); break;
+ }
+
+ return 0;
+}
+
+UINT32 svga_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT8 cur_mode = pc_vga_choosevideomode();
+
+ switch(cur_mode)
+ {
+ case SCREEN_OFF: bitmap.fill (m_palette->black_pen(), cliprect);break;
+ case TEXT_MODE: vga_vh_text (bitmap, cliprect); break;
+ case VGA_MODE: vga_vh_vga (bitmap, cliprect); break;
+ case EGA_MODE: vga_vh_ega (bitmap, cliprect); break;
+ case CGA_MODE: vga_vh_cga (bitmap, cliprect); break;
+ case MONO_MODE: vga_vh_mono (bitmap, cliprect); break;
+ case RGB8_MODE: svga_vh_rgb8 (bitmap, cliprect); break;
+ case RGB15_MODE: svga_vh_rgb15(bitmap, cliprect); break;
+ case RGB16_MODE: svga_vh_rgb16(bitmap, cliprect); break;
+ case RGB24_MODE: svga_vh_rgb24(bitmap, cliprect); break;
+ case RGB32_MODE: svga_vh_rgb32(bitmap, cliprect); break;
+ }
+
+ return 0;
+}
+
+UINT32 s3_vga_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT8 cur_mode = 0;
+
+ svga_device::screen_update(screen, bitmap, cliprect);
+
+ cur_mode = pc_vga_choosevideomode();
+
+ // draw hardware graphics cursor
+ // TODO: support 16 bit and greater video modes
+ if(s3.cursor_mode & 0x01) // if cursor is enabled
+ {
+ UINT32 src;
+ UINT32* dst;
+ UINT8 val;
+ int x,y;
+ UINT16 cx = s3.cursor_x & 0x07ff;
+ UINT16 cy = s3.cursor_y & 0x07ff;
+ UINT32 bg_col;
+ UINT32 fg_col;
+
+ if(cur_mode == SCREEN_OFF || cur_mode == TEXT_MODE || cur_mode == MONO_MODE || cur_mode == CGA_MODE || cur_mode == EGA_MODE)
+ return 0; // cursor only works in VGA or SVGA modes
+
+ src = s3.cursor_start_addr * 1024; // start address is in units of 1024 bytes
+
+ if(cur_mode == RGB16_MODE)
+ {
+ int r,g,b;
+ UINT16 datax;
+
+ datax = s3.cursor_bg[0]|s3.cursor_bg[1]<<8;
+ r = (datax&0xf800)>>11;
+ g = (datax&0x07e0)>>5;
+ b = (datax&0x001f)>>0;
+ r = (r << 3) | (r & 0x7);
+ g = (g << 2) | (g & 0x3);
+ b = (b << 3) | (b & 0x7);
+ bg_col = (0xff<<24)|(r<<16)|(g<<8)|(b<<0);
+
+ datax = s3.cursor_fg[0]|s3.cursor_fg[1]<<8;
+ r = (datax&0xf800)>>11;
+ g = (datax&0x07e0)>>5;
+ b = (datax&0x001f)>>0;
+ r = (r << 3) | (r & 0x7);
+ g = (g << 2) | (g & 0x3);
+ b = (b << 3) | (b & 0x7);
+ fg_col = (0xff<<24)|(r<<16)|(g<<8)|(b<<0);
+ }
+ else /* TODO: other modes */
+ {
+ bg_col = m_palette->pen(s3.cursor_bg[0]);
+ fg_col = m_palette->pen(s3.cursor_fg[0]);
+ }
+
+ //popmessage("%08x %08x",(s3.cursor_bg[0])|(s3.cursor_bg[1]<<8)|(s3.cursor_bg[2]<<16)|(s3.cursor_bg[3]<<24)
+ // ,(s3.cursor_fg[0])|(s3.cursor_fg[1]<<8)|(s3.cursor_fg[2]<<16)|(s3.cursor_fg[3]<<24));
+// for(x=0;x<64;x++)
+// printf("%08x: %02x %02x %02x %02x\n",src+x*4,vga.memory[src+x*4],vga.memory[src+x*4+1],vga.memory[src+x*4+2],vga.memory[src+x*4+3]);
+ for(y=0;y<64;y++)
+ {
+ dst = &bitmap.pix32(cy + y, cx);
+ for(x=0;x<64;x++)
+ {
+ UINT16 bita = (vga.memory[(src+1) % vga.svga_intf.vram_size] | ((vga.memory[(src+0) % vga.svga_intf.vram_size]) << 8)) >> (15-(x % 16));
+ UINT16 bitb = (vga.memory[(src+3) % vga.svga_intf.vram_size] | ((vga.memory[(src+2) % vga.svga_intf.vram_size]) << 8)) >> (15-(x % 16));
+ val = ((bita & 0x01) << 1) | (bitb & 0x01);
+ if(s3.extended_dac_ctrl & 0x10)
+ { // X11 mode
+ switch(val)
+ {
+ case 0x00:
+ // no change
+ break;
+ case 0x01:
+ // no change
+ break;
+ case 0x02:
+ dst[x] = bg_col;
+ break;
+ case 0x03:
+ dst[x] = fg_col;
+ break;
+ }
+ }
+ else
+ { // Windows mode
+ switch(val)
+ {
+ case 0x00:
+ dst[x] = bg_col;
+ break;
+ case 0x01:
+ dst[x] = fg_col;
+ break;
+ case 0x02: // screen data
+ // no change
+ break;
+ case 0x03: // inverted screen data
+ dst[x] = ~(dst[x]);
+ break;
+ }
+ }
+ if(x % 16 == 15)
+ src+=4;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/***************************************************************************/
+
+inline UINT8 vga_device::vga_latch_write(int offs, UINT8 data)
+{
+ UINT8 res = 0;
+
+ switch (vga.gc.write_mode & 3) {
+ case 0:
+ data = rotate_right(data);
+ if(vga.gc.enable_set_reset & 1<<offs)
+ res = vga_logical_op((vga.gc.set_reset & 1<<offs) ? vga.gc.bit_mask : 0, offs,vga.gc.bit_mask);
+ else
+ res = vga_logical_op(data, offs, vga.gc.bit_mask);
+ break;
+ case 1:
+ res = vga.gc.latch[offs];
+ break;
+ case 2:
+ res = vga_logical_op((data & 1<<offs) ? 0xff : 0x00,offs,vga.gc.bit_mask);
+ break;
+ case 3:
+ data = rotate_right(data);
+ res = vga_logical_op((vga.gc.set_reset & 1<<offs) ? 0xff : 0x00,offs,data&vga.gc.bit_mask);
+ break;
+ }
+
+ return res;
+}
+
+UINT8 vga_device::crtc_reg_read(UINT8 index)
+{
+ UINT8 res;
+
+ res = 0xff;
+
+ switch(index)
+ {
+ case 0x00:
+ res = vga.crtc.horz_total & 0xff;
+ break;
+ case 0x01:
+ res = vga.crtc.horz_disp_end & 0xff;
+ break;
+ case 0x02:
+ res = vga.crtc.horz_blank_start & 0xff;
+ break;
+ case 0x03:
+ res = vga.crtc.horz_blank_end & 0x1f;
+ res |= (vga.crtc.disp_enable_skew & 3) << 5;
+ res |= (vga.crtc.evra & 1) << 7;
+ break;
+ case 0x04:
+ res = vga.crtc.horz_retrace_start & 0xff;
+ break;
+ case 0x05:
+ res = (vga.crtc.horz_blank_end & 0x20) << 2;
+ res |= (vga.crtc.horz_retrace_skew & 3) << 5;
+ res |= (vga.crtc.horz_retrace_end & 0x1f);
+ break;
+ case 0x06:
+ res = vga.crtc.vert_total & 0xff;
+ break;
+ case 0x07: // Overflow Register
+ res = (vga.crtc.line_compare & 0x100) >> 4;
+ res |= (vga.crtc.vert_retrace_start & 0x200) >> 2;
+ res |= (vga.crtc.vert_disp_end & 0x200) >> 3;
+ res |= (vga.crtc.vert_total & 0x200) >> 4;
+ res |= (vga.crtc.vert_blank_start & 0x100) >> 5;
+ res |= (vga.crtc.vert_retrace_start & 0x100) >> 6;
+ res |= (vga.crtc.vert_disp_end & 0x100) >> 7;
+ res |= (vga.crtc.vert_total & 0x100) >> 8;
+ break;
+ case 0x08: // Preset Row Scan Register
+ res = (vga.crtc.byte_panning & 3) << 5;
+ res |= (vga.crtc.preset_row_scan & 0x1f);
+ break;
+ case 0x09: // Maximum Scan Line Register
+ res = (vga.crtc.maximum_scan_line - 1) & 0x1f;
+ res |= (vga.crtc.scan_doubling & 1) << 7;
+ res |= (vga.crtc.line_compare & 0x200) >> 3;
+ res |= (vga.crtc.vert_blank_start & 0x200) >> 4;
+ break;
+ case 0x0a:
+ res = (vga.crtc.cursor_scan_start & 0x1f);
+ res |= ((vga.crtc.cursor_enable & 1) ^ 1) << 5;
+ break;
+ case 0x0b:
+ res = (vga.crtc.cursor_skew & 3) << 5;
+ res |= (vga.crtc.cursor_scan_end & 0x1f);
+ break;
+ case 0x0c:
+ case 0x0d:
+ res = (vga.crtc.start_addr_latch >> ((index & 1) ^ 1)*8) & 0xff;
+ break;
+ case 0x0e:
+ case 0x0f:
+ res = (vga.crtc.cursor_addr >> ((index & 1) ^ 1)*8) & 0xff;
+ break;
+ case 0x10:
+ res = vga.crtc.vert_retrace_start & 0xff;
+ break;
+ case 0x11:
+ res = (vga.crtc.protect_enable & 1) << 7;
+ res |= (vga.crtc.bandwidth & 1) << 6;
+ res |= (vga.crtc.vert_retrace_end & 0xf);
+ res |= (vga.crtc.irq_clear & 1) << 4;
+ res |= (vga.crtc.irq_disable & 1) << 5;
+ break;
+ case 0x12:
+ res = vga.crtc.vert_disp_end & 0xff;
+ break;
+ case 0x13:
+ res = vga.crtc.offset & 0xff;
+ break;
+ case 0x14:
+ res = (vga.crtc.dw & 1) << 6;
+ res |= (vga.crtc.div4 & 1) << 5;
+ res |= (vga.crtc.underline_loc & 0x1f);
+ break;
+ case 0x15:
+ res = vga.crtc.vert_blank_start & 0xff;
+ break;
+ case 0x16:
+ res = vga.crtc.vert_blank_end & 0x7f;
+ break;
+ case 0x17:
+ res = (vga.crtc.sync_en & 1) << 7;
+ res |= (vga.crtc.word_mode & 1) << 6;
+ res |= (vga.crtc.aw & 1) << 5;
+ res |= (vga.crtc.div2 & 1) << 3;
+ res |= (vga.crtc.sldiv & 1) << 2;
+ res |= (vga.crtc.map14 & 1) << 1;
+ res |= (vga.crtc.map13 & 1) << 0;
+ break;
+ case 0x18:
+ res = vga.crtc.line_compare & 0xff;
+ break;
+ default:
+ printf("Unhandled CRTC reg r %02x\n",index);
+ break;
+ }
+ return res;
+}
+
+void vga_device::recompute_params_clock(int divisor, int xtal)
+{
+ int vblank_period,hblank_period;
+ attoseconds_t refresh;
+ UINT8 hclock_m = (!GRAPHIC_MODE) ? CHAR_WIDTH : 8;
+ int pixel_clock;
+
+ /* safety check */
+ if(!vga.crtc.horz_disp_end || !vga.crtc.vert_disp_end || !vga.crtc.horz_total || !vga.crtc.vert_total)
+ return;
+
+ rectangle visarea(0, ((vga.crtc.horz_disp_end + 1) * ((float)(hclock_m)/divisor))-1, 0, vga.crtc.vert_disp_end);
+
+ vblank_period = (vga.crtc.vert_total + 2);
+ hblank_period = ((vga.crtc.horz_total + 5) * ((float)(hclock_m)/divisor));
+
+ /* TODO: 10b and 11b settings aren't known */
+ pixel_clock = xtal / (((vga.sequencer.data[1]&8) >> 3) + 1);
+
+ refresh = HZ_TO_ATTOSECONDS(pixel_clock) * (hblank_period) * vblank_period;
+ machine().first_screen()->configure((hblank_period), (vblank_period), visarea, refresh );
+ //popmessage("%d %d\n",vga.crtc.horz_total * 8,vga.crtc.vert_total);
+ m_vblank_timer->adjust( machine().first_screen()->time_until_pos(vga.crtc.vert_blank_start + vga.crtc.vert_blank_end) );
+}
+
+void vga_device::recompute_params()
+{
+ if(vga.miscellaneous_output & 8)
+ logerror("Warning: VGA external clock latch selected\n");
+ else
+ recompute_params_clock(1, (vga.miscellaneous_output & 0xc) ? XTAL_28_63636MHz : XTAL_25_1748MHz);
+}
+
+void vga_device::crtc_reg_write(UINT8 index, UINT8 data)
+{
+ /* Doom does this */
+// if(vga.crtc.protect_enable && index <= 0x07)
+// printf("write to protected address %02x\n",index);
+ switch(index)
+ {
+ case 0x00:
+ if(vga.crtc.protect_enable)
+ break;
+ vga.crtc.horz_total = (vga.crtc.horz_total & ~0xff) | (data & 0xff);
+ recompute_params();
+ break;
+ case 0x01:
+ if(vga.crtc.protect_enable)
+ break;
+ vga.crtc.horz_disp_end = (data & 0xff);
+ recompute_params();
+ break;
+ case 0x02:
+ if(vga.crtc.protect_enable)
+ break;
+ vga.crtc.horz_blank_start = (data & 0xff);
+ break;
+ case 0x03:
+ if(vga.crtc.protect_enable)
+ break;
+ vga.crtc.horz_blank_end &= ~0x1f;
+ vga.crtc.horz_blank_end |= data & 0x1f;
+ vga.crtc.disp_enable_skew = (data & 0x60) >> 5;
+ vga.crtc.evra = (data & 0x80) >> 7;
+ break;
+ case 0x04:
+ if(vga.crtc.protect_enable)
+ break;
+ vga.crtc.horz_retrace_start = data & 0xff;
+ break;
+ case 0x05:
+ if(vga.crtc.protect_enable)
+ break;
+ vga.crtc.horz_blank_end &= ~0x20;
+ vga.crtc.horz_blank_end |= ((data & 0x80) >> 2);
+ vga.crtc.horz_retrace_skew = ((data & 0x60) >> 5);
+ vga.crtc.horz_retrace_end = data & 0x1f;
+ break;
+ case 0x06:
+ if(vga.crtc.protect_enable)
+ break;
+ vga.crtc.vert_total &= ~0xff;
+ vga.crtc.vert_total |= data & 0xff;
+ recompute_params();
+ break;
+ case 0x07: // Overflow Register
+ vga.crtc.line_compare &= ~0x100;
+ vga.crtc.line_compare |= ((data & 0x10) << (8-4));
+ if(vga.crtc.protect_enable)
+ break;
+ vga.crtc.vert_total &= ~0x300;
+ vga.crtc.vert_retrace_start &= ~0x300;
+ vga.crtc.vert_disp_end &= ~0x300;
+ vga.crtc.vert_blank_start &= ~0x100;
+ vga.crtc.vert_retrace_start |= ((data & 0x80) << (9-7));
+ vga.crtc.vert_disp_end |= ((data & 0x40) << (9-6));
+ vga.crtc.vert_total |= ((data & 0x20) << (9-5));
+ vga.crtc.vert_blank_start |= ((data & 0x08) << (8-3));
+ vga.crtc.vert_retrace_start |= ((data & 0x04) << (8-2));
+ vga.crtc.vert_disp_end |= ((data & 0x02) << (8-1));
+ vga.crtc.vert_total |= ((data & 0x01) << (8-0));
+ recompute_params();
+ break;
+ case 0x08: // Preset Row Scan Register
+ vga.crtc.byte_panning = (data & 0x60) >> 5;
+ vga.crtc.preset_row_scan = (data & 0x1f);
+ break;
+ case 0x09: // Maximum Scan Line Register
+ vga.crtc.line_compare &= ~0x200;
+ vga.crtc.vert_blank_start &= ~0x200;
+ vga.crtc.scan_doubling = ((data & 0x80) >> 7);
+ vga.crtc.line_compare |= ((data & 0x40) << (9-6));
+ vga.crtc.vert_blank_start |= ((data & 0x20) << (9-5));
+ vga.crtc.maximum_scan_line = (data & 0x1f) + 1;
+ break;
+ case 0x0a:
+ vga.crtc.cursor_enable = ((data & 0x20) ^ 0x20) >> 5;
+ vga.crtc.cursor_scan_start = data & 0x1f;
+ break;
+ case 0x0b:
+ vga.crtc.cursor_skew = (data & 0x60) >> 5;
+ vga.crtc.cursor_scan_end = data & 0x1f;
+ break;
+ case 0x0c:
+ case 0x0d:
+ vga.crtc.start_addr_latch &= ~(0xff << (((index & 1)^1) * 8));
+ vga.crtc.start_addr_latch |= (data << (((index & 1)^1) * 8));
+ break;
+ case 0x0e:
+ case 0x0f:
+ vga.crtc.cursor_addr &= ~(0xff << (((index & 1)^1) * 8));
+ vga.crtc.cursor_addr |= (data << (((index & 1)^1) * 8));
+ break;
+ case 0x10:
+ vga.crtc.vert_retrace_start &= ~0xff;
+ vga.crtc.vert_retrace_start |= data & 0xff;
+ break;
+ case 0x11:
+ vga.crtc.protect_enable = (data & 0x80) >> 7;
+ vga.crtc.bandwidth = (data & 0x40) >> 6;
+ vga.crtc.vert_retrace_end = data & 0x0f;
+ vga.crtc.irq_clear = (data & 0x10) >> 4;
+ vga.crtc.irq_disable = (data & 0x20) >> 5;
+ break;
+ case 0x12:
+ vga.crtc.vert_disp_end &= ~0xff;
+ vga.crtc.vert_disp_end |= data & 0xff;
+ recompute_params();
+ break;
+ case 0x13:
+ vga.crtc.offset &= ~0xff;
+ vga.crtc.offset |= data & 0xff;
+ break;
+ case 0x14:
+ vga.crtc.dw = (data & 0x40) >> 6;
+ vga.crtc.div4 = (data & 0x20) >> 5;
+ vga.crtc.underline_loc = (data & 0x1f);
+ break;
+ case 0x15:
+ vga.crtc.vert_blank_start &= ~0xff;
+ vga.crtc.vert_blank_start |= data & 0xff;
+ break;
+ case 0x16:
+ vga.crtc.vert_blank_end = data & 0x7f;
+ break;
+ case 0x17:
+ vga.crtc.sync_en = (data & 0x80) >> 7;
+ vga.crtc.word_mode = (data & 0x40) >> 6;
+ vga.crtc.aw = (data & 0x20) >> 5;
+ vga.crtc.div2 = (data & 0x08) >> 3;
+ vga.crtc.sldiv = (data & 0x04) >> 2;
+ vga.crtc.map14 = (data & 0x02) >> 1;
+ vga.crtc.map13 = (data & 0x01) >> 0;
+ break;
+ case 0x18:
+ vga.crtc.line_compare &= ~0xff;
+ vga.crtc.line_compare |= data & 0xff;
+ break;
+ default:
+ logerror("Unhandled CRTC reg w %02x %02x\n",index,data);
+ break;
+ }
+}
+
+void vga_device::seq_reg_write(UINT8 index, UINT8 data)
+{
+ switch(index)
+ {
+ case 0x02:
+ vga.sequencer.map_mask = data & 0xf;
+ break;
+ case 0x03:
+ /* --2- 84-- character select A
+ ---2 --84 character select B */
+ vga.sequencer.char_sel.A = (((data & 0xc) >> 2)<<1) | ((data & 0x20) >> 5);
+ vga.sequencer.char_sel.B = (((data & 0x3) >> 0)<<1) | ((data & 0x10) >> 4);
+ if(data)
+ popmessage("Char SEL checker, contact MAMEdev (%02x %02x)\n",vga.sequencer.char_sel.A,vga.sequencer.char_sel.B);
+ break;
+ }
+}
+
+UINT8 vga_device::vga_vblank()
+{
+ UINT8 res;
+ UINT16 vblank_start,vblank_end,vpos;
+
+ /* calculate vblank start / end positions */
+ res = 0;
+ vblank_start = vga.crtc.vert_blank_start;
+ vblank_end = vga.crtc.vert_blank_start + vga.crtc.vert_blank_end;
+ vpos = machine().first_screen()->vpos();
+
+ /* check if we are under vblank period */
+ if(vblank_end > vga.crtc.vert_total)
+ {
+ vblank_end -= vga.crtc.vert_total;
+ if(vpos >= vblank_start || vpos <= vblank_end)
+ res = 1;
+ }
+ else
+ {
+ if(vpos >= vblank_start && vpos <= vblank_end)
+ res = 1;
+ }
+
+ //popmessage("%d %d %d - SR1=%02x",vblank_start,vblank_end,vga.crtc.vert_total,vga.sequencer.data[1]);
+
+ return res;
+}
+
+READ8_MEMBER(vga_device::vga_crtc_r)
+{
+ UINT8 data = 0xff;
+
+ switch (offset) {
+ case 4:
+ data = vga.crtc.index;
+ break;
+ case 5:
+ data = crtc_reg_read(vga.crtc.index);
+ break;
+ case 0xa:
+ UINT8 hsync,vsync;
+ vga.attribute.state = 0;
+ data = 0;
+
+ hsync = space.machine().first_screen()->hblank() & 1;
+ vsync = vga_vblank(); //space.machine().first_screen()->vblank() & 1;
+
+ data |= (hsync | vsync) & 1; // DD - display disable register
+ data |= (vsync & 1) << 3; // VRetrace register
+
+ /* ega diagnostic readback enough for oak bios */
+ switch (vga.attribute.data[0x12]&0x30) {
+ case 0:
+ if (vga.attribute.data[0x11]&1) data|=0x10;
+ if (vga.attribute.data[0x11]&4) data|=0x20;
+ break;
+ case 0x10:
+ data|=(vga.attribute.data[0x11]&0x30);
+ break;
+ case 0x20:
+ if (vga.attribute.data[0x11]&2) data|=0x10;
+ if (vga.attribute.data[0x11]&8) data|=0x20;
+ break;
+ case 0x30:
+ data|=(vga.attribute.data[0x11]&0xc0)>>2;
+ break;
+ }
+ break;
+ case 0xf:
+ /* oak test */
+ //data=0;
+ /* pega bios on/off */
+ data=0x80;
+ break;
+ }
+ return data;
+}
+
+WRITE8_MEMBER(vga_device::vga_crtc_w)
+{
+ switch (offset)
+ {
+ case 4:
+ vga.crtc.index = data;
+ break;
+
+ case 5:
+ if (LOG_REGISTERS)
+ {
+ logerror("vga_crtc_w(): CRTC[0x%02X%s] = 0x%02X\n",
+ vga.crtc.index,
+ (vga.crtc.index < vga.svga_intf.crtc_regcount) ? "" : "?",
+ data);
+ }
+
+ crtc_reg_write(vga.crtc.index,data);
+ //space.machine().first_screen()->update_partial(space.machine().first_screen()->vpos());
+ #if 0
+ if((vga.crtc.index & 0xfe) != 0x0e)
+ printf("%02x %02x %d\n",vga.crtc.index,data,space.machine().first_screen()->vpos());
+ #endif
+ break;
+
+ case 0xa:
+ vga.feature_control = data;
+ break;
+ }
+}
+
+
+
+READ8_MEMBER(vga_device::port_03b0_r)
+{
+ UINT8 data = 0xff;
+ if (CRTC_PORT_ADDR==0x3b0)
+ data=vga_crtc_r(space, offset, mem_mask);
+ return data;
+}
+
+UINT8 vga_device::gc_reg_read(UINT8 index)
+{
+ UINT8 res;
+
+ switch(index)
+ {
+ case 0x00:
+ res = vga.gc.set_reset & 0xf;
+ break;
+ case 0x01:
+ res = vga.gc.enable_set_reset & 0xf;
+ break;
+ case 0x02:
+ res = vga.gc.color_compare & 0xf;
+ break;
+ case 0x03:
+ res = (vga.gc.logical_op & 3) << 3;
+ res |= (vga.gc.rotate_count & 7);
+ break;
+ case 0x04:
+ res = vga.gc.read_map_sel & 3;
+ break;
+ case 0x05:
+ res = (vga.gc.shift256 & 1) << 6;
+ res |= (vga.gc.shift_reg & 1) << 5;;
+ res |= (vga.gc.host_oe & 1) << 4;
+ res |= (vga.gc.read_mode & 1) << 3;
+ res |= (vga.gc.write_mode & 3);
+ break;
+ case 0x06:
+ res = (vga.gc.memory_map_sel & 3) << 2;
+ res |= (vga.gc.chain_oe & 1) << 1;
+ res |= (vga.gc.alpha_dis & 1);
+ break;
+ case 0x07:
+ res = vga.gc.color_dont_care & 0xf;
+ break;
+ case 0x08:
+ res = vga.gc.bit_mask & 0xff;
+ break;
+ default:
+ res = 0xff;
+ break;
+ }
+
+ return res;
+}
+
+READ8_MEMBER(vga_device::port_03c0_r)
+{
+ UINT8 data = 0xff;
+
+ switch (offset)
+ {
+ case 0:
+ data = vga.attribute.index;
+ break;
+ case 1:
+ if((vga.attribute.index&0x20)
+ && ((vga.attribute.index&0x1f)<0x10))
+ data = 0; // palette access is disabled in this mode
+ else if ((vga.attribute.index&0x1f)<sizeof(vga.attribute.data))
+ data=vga.attribute.data[vga.attribute.index&0x1f];
+ break;
+
+ case 2:
+ // TODO: in VGA bit 4 is actually always on?
+ data = 0x60; // is VGA
+ switch ((vga.miscellaneous_output>>2)&3)
+ {
+ case 3:
+ if (!vga.read_dipswitch.isnull() && vga.read_dipswitch(space, 0, mem_mask) & 0x01)
+ data |= 0x10;
+ else
+ data |= 0x10;
+ break;
+ case 2:
+ if (!vga.read_dipswitch.isnull() && vga.read_dipswitch(space, 0, mem_mask) & 0x02)
+ data |= 0x10;
+ else
+ data |= 0x10;
+ break;
+ case 1:
+ if (!vga.read_dipswitch.isnull() && vga.read_dipswitch(space, 0, mem_mask) & 0x04)
+ data |= 0x10;
+ else
+ data |= 0x10;
+ break;
+ case 0:
+ if (!vga.read_dipswitch.isnull() && vga.read_dipswitch(space, 0, mem_mask) & 0x08)
+ data |= 0x10;
+ else
+ data |= 0x10;
+ break;
+ }
+ break;
+
+ case 3:
+ data = vga.oak.reg;
+ break;
+
+ case 4:
+ data = vga.sequencer.index;
+ break;
+
+ case 5:
+ if (vga.sequencer.index < vga.svga_intf.seq_regcount)
+ data = vga.sequencer.data[vga.sequencer.index];
+ break;
+
+ case 6:
+ data = vga.dac.mask;
+ break;
+
+ case 7:
+ data = (vga.dac.read) ? 3 : 0;
+ break;
+
+ case 8:
+ data = vga.dac.write_index;
+ break;
+
+ case 9:
+ if (vga.dac.read)
+ {
+ switch (vga.dac.state++)
+ {
+ case 0:
+ data = vga.dac.color[vga.dac.read_index].red;
+ break;
+ case 1:
+ data = vga.dac.color[vga.dac.read_index].green;
+ break;
+ case 2:
+ data = vga.dac.color[vga.dac.read_index].blue;
+ break;
+ }
+
+ if (vga.dac.state==3)
+ {
+ vga.dac.state = 0;
+ vga.dac.read_index++;
+ }
+ }
+ break;
+
+ case 0xa:
+ data = vga.feature_control;
+ break;
+
+ case 0xc:
+ data = vga.miscellaneous_output;
+ break;
+
+ case 0xe:
+ data = vga.gc.index;
+ break;
+
+ case 0xf:
+ data = gc_reg_read(vga.gc.index);
+ break;
+ }
+ return data;
+}
+
+READ8_MEMBER(vga_device::port_03d0_r)
+{
+ UINT8 data = 0xff;
+ if (CRTC_PORT_ADDR == 0x3d0)
+ data = vga_crtc_r(space, offset, mem_mask);
+ if(offset == 8)
+ {
+ logerror("VGA: 0x3d8 read at %08x\n",space.device().safe_pc());
+ data = 0; // TODO: PC-200 reads back CGA register here, everything else returns open bus OR CGA emulation of register 0x3d8
+ }
+
+ return data;
+}
+
+WRITE8_MEMBER(vga_device::port_03b0_w)
+{
+ if (LOG_ACCESSES)
+ logerror("vga_port_03b0_w(): port=0x%04x data=0x%02x\n", offset + 0x3b0, data);
+
+ if (CRTC_PORT_ADDR == 0x3b0)
+ vga_crtc_w(space, offset, data, mem_mask);
+}
+
+void vga_device::attribute_reg_write(UINT8 index, UINT8 data)
+{
+ if((index & 0x30) == 0)
+ {
+ //if(vga.sequencer.data[1]&0x20) // ok?
+ vga.attribute.data[index & 0x1f] = data & 0x3f;
+ }
+ else
+ {
+ switch(index & 0x1f)
+ {
+ /* TODO: intentional dirtiness, variable names to be properly changed */
+ case 0x10: vga.attribute.data[0x10] = data; break;
+ case 0x11: vga.attribute.data[0x11] = data; break;
+ case 0x12: vga.attribute.data[0x12] = data; break;
+ case 0x13: vga.attribute.pel_shift_latch = vga.attribute.data[0x13] = data; break;
+ case 0x14: vga.attribute.data[0x14] = data; break;
+ }
+ }
+}
+
+void vga_device::gc_reg_write(UINT8 index,UINT8 data)
+{
+ switch(index)
+ {
+ case 0x00:
+ vga.gc.set_reset = data & 0xf;
+ break;
+ case 0x01:
+ vga.gc.enable_set_reset = data & 0xf;
+ break;
+ case 0x02:
+ vga.gc.color_compare = data & 0xf;
+ break;
+ case 0x03:
+ vga.gc.logical_op = (data & 0x18) >> 3;
+ vga.gc.rotate_count = data & 7;
+ break;
+ case 0x04:
+ vga.gc.read_map_sel = data & 3;
+ break;
+ case 0x05:
+ vga.gc.shift256 = (data & 0x40) >> 6;
+ vga.gc.shift_reg = (data & 0x20) >> 5;
+ vga.gc.host_oe = (data & 0x10) >> 4;
+ vga.gc.read_mode = (data & 8) >> 3;
+ vga.gc.write_mode = data & 3;
+ //if(data & 0x10 && vga.gc.alpha_dis)
+ // popmessage("Host O/E enabled, contact MAMEdev");
+ break;
+ case 0x06:
+ vga.gc.memory_map_sel = (data & 0xc) >> 2;
+ vga.gc.chain_oe = (data & 2) >> 1;
+ vga.gc.alpha_dis = (data & 1);
+ //if(data & 2 && vga.gc.alpha_dis)
+ // popmessage("Chain O/E enabled, contact MAMEdev");
+ break;
+ case 0x07:
+ vga.gc.color_dont_care = data & 0xf;
+ break;
+ case 0x08:
+ vga.gc.bit_mask = data & 0xff;
+ break;
+ }
+}
+
+WRITE8_MEMBER(vga_device::port_03c0_w)
+{
+ if (LOG_ACCESSES)
+ logerror("vga_port_03c0_w(): port=0x%04x data=0x%02x\n", offset + 0x3c0, data);
+
+ switch (offset) {
+ case 0:
+ if (vga.attribute.state==0)
+ {
+ vga.attribute.index=data;
+ }
+ else
+ {
+ attribute_reg_write(vga.attribute.index,data);
+ }
+ vga.attribute.state=!vga.attribute.state;
+ break;
+ case 2:
+ vga.miscellaneous_output=data;
+ recompute_params();
+ break;
+ case 3:
+ vga.oak.reg = data;
+ break;
+ case 4:
+ vga.sequencer.index = data;
+ break;
+ case 5:
+ if (LOG_REGISTERS)
+ {
+ logerror("vga_port_03c0_w(): SEQ[0x%02X%s] = 0x%02X\n",
+ vga.sequencer.index,
+ (vga.sequencer.index < vga.svga_intf.seq_regcount) ? "" : "?",
+ data);
+ }
+ if (vga.sequencer.index < vga.svga_intf.seq_regcount)
+ {
+ vga.sequencer.data[vga.sequencer.index] = data;
+ }
+
+ seq_reg_write(vga.sequencer.index,data);
+ recompute_params();
+ break;
+ case 6:
+ vga.dac.mask=data;
+ vga.dac.dirty=1;
+ break;
+ case 7:
+ vga.dac.read_index=data;
+ vga.dac.state=0;
+ vga.dac.read=1;
+ break;
+ case 8:
+ vga.dac.write_index=data;
+ vga.dac.state=0;
+ vga.dac.read=0;
+ break;
+ case 9:
+ if (!vga.dac.read)
+ {
+ switch (vga.dac.state++) {
+ case 0:
+ vga.dac.color[vga.dac.write_index].red=data;
+ break;
+ case 1:
+ vga.dac.color[vga.dac.write_index].green=data;
+ break;
+ case 2:
+ vga.dac.color[vga.dac.write_index].blue=data;
+ break;
+ }
+ vga.dac.dirty=1;
+ if (vga.dac.state==3) {
+ vga.dac.state=0; vga.dac.write_index++;
+ }
+ }
+ break;
+ case 0xe:
+ vga.gc.index=data;
+ break;
+ case 0xf:
+ gc_reg_write(vga.gc.index,data);
+ break;
+ }
+}
+
+
+
+WRITE8_MEMBER(vga_device::port_03d0_w)
+{
+ if (LOG_ACCESSES)
+ logerror("vga_port_03d0_w(): port=0x%04x data=0x%02x\n", offset + 0x3d0, data);
+
+ if (CRTC_PORT_ADDR == 0x3d0)
+ vga_crtc_w(space, offset, data, mem_mask);
+}
+
+void vga_device::device_reset()
+{
+ /* clear out the VGA structure */
+ memset(vga.pens, 0, sizeof(vga.pens));
+ vga.miscellaneous_output = 0;
+ vga.feature_control = 0;
+ vga.sequencer.index = 0;
+ memset(vga.sequencer.data, 0, sizeof(vga.sequencer.data));
+ vga.crtc.index = 0;
+ memset(vga.crtc.data, 0, sizeof(vga.crtc.data));
+ vga.gc.index = 0;
+ memset(vga.gc.latch, 0, sizeof(vga.gc.latch));
+ memset(&vga.attribute, 0, sizeof(vga.attribute));
+ memset(&vga.dac, 0, sizeof(vga.dac));
+ memset(&vga.cursor, 0, sizeof(vga.cursor));
+ memset(&vga.oak, 0, sizeof(vga.oak));
+
+ vga.gc.memory_map_sel = 0x3; /* prevent xtbios excepting vga ram as system ram */
+/* amstrad pc1640 bios relies on the position of
+ the video memory area,
+ so I introduced the reset to switch to b8000 area */
+ vga.sequencer.data[4] = 0;
+
+ /* TODO: real defaults */
+ vga.crtc.line_compare = 0x3ff;
+ /* indiana.c boot PROM doesn't set this and assumes it's 0xff */
+ vga.dac.mask = 0xff;
+}
+
+void s3_vga_device::device_reset()
+{
+ vga_device::device_reset();
+ // Power-on strapping bits. Sampled at reset, but can be modified later.
+ // These are just assumed defaults.
+ s3.strapping = 0x000f0b1e;
+ s3.sr10 = 0x42;
+ s3.sr11 = 0x41;
+}
+
+READ8_MEMBER(vga_device::mem_r)
+{
+ /* TODO: check me */
+ switch(vga.gc.memory_map_sel & 0x03)
+ {
+ case 0: break;
+ case 1: offset &= 0x0ffff; break;
+ case 2: offset -= 0x10000; offset &= 0x07fff; break;
+ case 3: offset -= 0x18000; offset &= 0x07fff; break;
+ }
+
+ if(vga.sequencer.data[4] & 4)
+ {
+ int data;
+ if (!space.debugger_access())
+ {
+ vga.gc.latch[0]=vga.memory[(offset)];
+ vga.gc.latch[1]=vga.memory[(offset)+0x10000];
+ vga.gc.latch[2]=vga.memory[(offset)+0x20000];
+ vga.gc.latch[3]=vga.memory[(offset)+0x30000];
+ }
+
+ if (vga.gc.read_mode)
+ {
+ UINT8 byte,layer;
+ UINT8 fill_latch;
+ data=0;
+
+ for(byte=0;byte<8;byte++)
+ {
+ fill_latch = 0;
+ for(layer=0;layer<4;layer++)
+ {
+ if(vga.gc.latch[layer] & 1 << byte)
+ fill_latch |= 1 << layer;
+ }
+ fill_latch &= vga.gc.color_dont_care;
+ if(fill_latch == vga.gc.color_compare)
+ data |= 1 << byte;
+ }
+ }
+ else
+ data=vga.gc.latch[vga.gc.read_map_sel];
+
+ return data;
+ }
+ else
+ {
+ // TODO: Guesswork, probably not right
+ UINT8 i,data;
+
+ data = 0;
+ //printf("%08x\n",offset);
+
+ for(i=0;i<4;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ data |= vga.memory[offset+i*0x10000];
+ }
+
+ return data;
+ }
+
+ // never executed
+ //return 0;
+}
+
+WRITE8_MEMBER(vga_device::mem_w)
+{
+ //Inside each case must prevent writes to non-mapped VGA memory regions, not only mask the offset.
+ switch(vga.gc.memory_map_sel & 0x03)
+ {
+ case 0: break;
+ case 1:
+ if(offset & 0x10000)
+ return;
+
+ offset &= 0x0ffff;
+ break;
+ case 2:
+ if((offset & 0x18000) != 0x10000)
+ return;
+
+ offset &= 0x07fff;
+ break;
+ case 3:
+ if((offset & 0x18000) != 0x18000)
+ return;
+
+ offset &= 0x07fff;
+ break;
+ }
+
+ {
+ UINT8 i;
+
+ for(i=0;i<4;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ vga.memory[offset+i*0x10000] = (vga.sequencer.data[4] & 4) ? vga_latch_write(i,data) : data;
+ }
+ return;
+ }
+}
+
+READ8_MEMBER(vga_device::mem_linear_r)
+{
+ return vga.memory[offset];
+}
+
+WRITE8_MEMBER(vga_device::mem_linear_w)
+{
+ vga.memory[offset] = data;
+}
+
+MACHINE_CONFIG_FRAGMENT( pcvideo_vga )
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_25_1748MHz,900,0,640,526,0,480)
+ MCFG_SCREEN_UPDATE_DEVICE("vga", vga_device, screen_update)
+
+ MCFG_PALETTE_ADD("palette", 0x100)
+ MCFG_DEVICE_ADD("vga", VGA, 0)
+MACHINE_CONFIG_END
+
+MACHINE_CONFIG_FRAGMENT( pcvideo_trident_vga )
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_25_1748MHz,900,0,640,526,0,480)
+ MCFG_SCREEN_UPDATE_DEVICE("vga", trident_vga_device, screen_update)
+
+ MCFG_PALETTE_ADD("palette", 0x100)
+ MCFG_DEVICE_ADD("vga", TRIDENT_VGA, 0)
+MACHINE_CONFIG_END
+
+MACHINE_CONFIG_FRAGMENT( pcvideo_gamtor_vga )
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_25_1748MHz,900,0,640,526,0,480)
+ MCFG_SCREEN_UPDATE_DEVICE("vga", gamtor_vga_device, screen_update)
+
+ MCFG_PALETTE_ADD("palette", 0x100)
+ MCFG_DEVICE_ADD("vga", GAMTOR_VGA, 0)
+MACHINE_CONFIG_END
+
+MACHINE_CONFIG_FRAGMENT( pcvideo_s3_vga )
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_25_1748MHz,900,0,640,526,0,480)
+ MCFG_SCREEN_UPDATE_DEVICE("vga", s3_vga_device, screen_update)
+
+ MCFG_PALETTE_ADD("palette", 0x100)
+ MCFG_DEVICE_ADD("vga", S3_VGA, 0)
+MACHINE_CONFIG_END
+
+
+static MACHINE_CONFIG_FRAGMENT( ati_vga )
+ MCFG_MACH8_ADD_OWNER("8514a")
+ MCFG_EEPROM_SERIAL_93C46_ADD("ati_eeprom")
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_FRAGMENT( s3_764 )
+ MCFG_8514A_ADD_OWNER("8514a")
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - device-specific
+// machine configurations
+//-------------------------------------------------
+
+machine_config_constructor ati_vga_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( ati_vga );
+}
+
+machine_config_constructor s3_vga_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( s3_764 );
+}
+
+/******************************************
+
+Tseng ET4000k implementation
+
+******************************************/
+
+void tseng_vga_device::tseng_define_video_mode()
+{
+ int divisor;
+ int xtal = 0;
+ svga.rgb8_en = 0;
+ svga.rgb15_en = 0;
+ svga.rgb16_en = 0;
+ svga.rgb24_en = 0;
+ switch(((et4k.aux_ctrl << 1) & 4)|(vga.miscellaneous_output & 0xc)>>2)
+ {
+ case 0:
+ xtal = XTAL_25_1748MHz;
+ break;
+ case 1:
+ xtal = XTAL_28_63636MHz;
+ break;
+ case 2:
+ xtal = 16257000*2; //2xEGA clock
+ break;
+ case 3:
+ xtal = XTAL_40MHz;
+ break;
+ case 4:
+ xtal = XTAL_36MHz;
+ break;
+ case 5:
+ xtal = XTAL_45MHz;
+ break;
+ case 6:
+ xtal = 31000000;
+ break;
+ case 7:
+ xtal = 38000000;
+ break;
+ }
+ switch(et4k.dac_ctrl & 0xe0)
+ {
+ case 0xa0:
+ svga.rgb15_en = 1;
+ divisor = 2;
+ break;
+ case 0xe0:
+ svga.rgb16_en = 1;
+ divisor = 2;
+ break;
+ case 0x60:
+ svga.rgb24_en = 1;
+ divisor = 3;
+ xtal *= 2.0f/3.0f;
+ break;
+ default:
+ svga.rgb8_en = (!(vga.sequencer.data[1] & 8) && (vga.sequencer.data[4] & 8) && vga.gc.shift256 && vga.crtc.div2 && GRAPHIC_MODE);
+ divisor = 1;
+ break;
+ }
+ recompute_params_clock(divisor, xtal);
+}
+
+UINT8 tseng_vga_device::tseng_crtc_reg_read(UINT8 index)
+{
+ UINT8 res;
+
+ if(index <= 0x18)
+ res = crtc_reg_read(index);
+ else
+ {
+ switch(index)
+ {
+ case 0x34:
+ res = et4k.aux_ctrl;
+ break;
+ case 0x3f:
+ res = et4k.horz_overflow;
+ break;
+ default:
+ res = vga.crtc.data[index];
+ //printf("%02x\n",index);
+ break;
+ }
+ }
+
+ return res;
+}
+
+void tseng_vga_device::tseng_crtc_reg_write(UINT8 index, UINT8 data)
+{
+ if(index <= 0x18)
+ crtc_reg_write(index,data);
+ else
+ {
+ switch(index)
+ {
+ case 0x34:
+ et4k.aux_ctrl = data;
+ break;
+ case 0x3f:
+ et4k.horz_overflow = data;
+ vga.crtc.horz_total = (vga.crtc.horz_total & 0xff) | ((data & 1) << 8);
+ break;
+ default:
+ //printf("%02x %02x\n",index,data);
+ break;
+ }
+ }
+}
+
+UINT8 tseng_vga_device::tseng_seq_reg_read(UINT8 index)
+{
+ UINT8 res;
+
+ res = 0xff;
+
+ if(index <= 0x04)
+ res = vga.sequencer.data[index];
+ else
+ {
+ switch(index)
+ {
+ case 0x06:
+ case 0x07:
+ //printf("%02x\n",index);
+ break;
+ }
+ }
+
+ return res;
+}
+
+void tseng_vga_device::tseng_seq_reg_write(UINT8 index, UINT8 data)
+{
+ if(index <= 0x04)
+ {
+ vga.sequencer.data[vga.sequencer.index] = data;
+ seq_reg_write(vga.sequencer.index,data);
+ }
+ else
+ {
+ switch(index)
+ {
+ case 0x06:
+ case 0x07:
+ //printf("%02x %02x\n",index,data);
+ break;
+ }
+ }
+}
+
+READ8_MEMBER(tseng_vga_device::port_03b0_r)
+{
+ UINT8 res = 0xff;
+
+ if (CRTC_PORT_ADDR == 0x3b0)
+ {
+ switch(offset)
+ {
+ case 5:
+ res = tseng_crtc_reg_read(vga.crtc.index);
+ break;
+ case 8:
+ res = et4k.reg_3d8;
+ break;
+ default:
+ res = vga_device::port_03b0_r(space,offset,mem_mask);
+ break;
+ }
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(tseng_vga_device::port_03b0_w)
+{
+ if (CRTC_PORT_ADDR == 0x3b0)
+ {
+ switch(offset)
+ {
+ case 5:
+ vga.crtc.data[vga.crtc.index] = data;
+ tseng_crtc_reg_write(vga.crtc.index,data);
+ break;
+ case 8:
+ et4k.reg_3d8 = data;
+ if(data == 0xa0)
+ et4k.ext_reg_ena = true;
+ else if(data == 0x29)
+ et4k.ext_reg_ena = false;
+ break;
+ default:
+ vga_device::port_03b0_w(space,offset,data,mem_mask);
+ break;
+ }
+ }
+ tseng_define_video_mode();
+}
+
+void tseng_vga_device::tseng_attribute_reg_write(UINT8 index, UINT8 data)
+{
+ switch(index)
+ {
+ case 0x16:
+ et4k.misc1 = data;
+ #if 0
+ svga.rgb8_en = 0;
+ svga.rgb15_en = 0;
+ svga.rgb16_en = 0;
+ svga.rgb32_en = 0;
+ /* TODO: et4k and w32 are different here */
+ switch(et4k.misc1 & 0x30)
+ {
+ case 0:
+ // normal power-up mode
+ break;
+ case 0x10:
+ svga.rgb8_en = 1;
+ break;
+ case 0x20:
+ case 0x30:
+ popmessage("Tseng 15/16 bit HiColor mode, contact MAMEdev");
+ break;
+ }
+ #endif
+ break;
+ case 0x17: et4k.misc2 = data; break;
+ default:
+ attribute_reg_write(index,data);
+ }
+
+}
+
+READ8_MEMBER(tseng_vga_device::port_03c0_r)
+{
+ UINT8 res;
+
+ switch(offset)
+ {
+ case 0x01:
+ switch(vga.attribute.index)
+ {
+ case 0x16: res = et4k.misc1; break;
+ case 0x17: res = et4k.misc2; break;
+ default:
+ res = vga_device::port_03c0_r(space,offset,mem_mask);
+ break;
+ }
+
+ break;
+
+ case 0x05:
+ res = tseng_seq_reg_read(vga.sequencer.index);
+ break;
+ case 0x0d:
+ res = svga.bank_w & 0xf;
+ res |= (svga.bank_r & 0xf) << 4;
+ break;
+ case 0x06:
+ if(et4k.dac_state == 4)
+ {
+ if(!et4k.dac_ctrl)
+ et4k.dac_ctrl = 0x80;
+ res = et4k.dac_ctrl;
+ break;
+ }
+ et4k.dac_state++;
+ res = vga_device::port_03c0_r(space,offset,mem_mask);
+ break;
+ case 0x08:
+ et4k.dac_state = 0;
+ default:
+ res = vga_device::port_03c0_r(space,offset,mem_mask);
+ break;
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(tseng_vga_device::port_03c0_w)
+{
+ switch(offset)
+ {
+ case 0:
+ if (vga.attribute.state==0)
+ {
+ vga.attribute.index=data;
+ }
+ else
+ {
+ tseng_attribute_reg_write(vga.attribute.index,data);
+ }
+ vga.attribute.state=!vga.attribute.state;
+ break;
+
+ case 0x05:
+ tseng_seq_reg_write(vga.sequencer.index,data);
+ break;
+ case 0x0d:
+ svga.bank_w = data & 0xf;
+ svga.bank_r = (data & 0xf0) >> 4;
+ break;
+ case 0x06:
+ if(et4k.dac_state == 4)
+ {
+ et4k.dac_ctrl = data;
+ break;
+ }
+ default:
+ vga_device::port_03c0_w(space,offset,data,mem_mask);
+ break;
+ }
+ tseng_define_video_mode();
+}
+
+READ8_MEMBER(tseng_vga_device::port_03d0_r)
+{
+ UINT8 res = 0xff;
+
+ if (CRTC_PORT_ADDR == 0x3d0)
+ {
+ switch(offset)
+ {
+ case 5:
+ res = tseng_crtc_reg_read(vga.crtc.index);
+ break;
+ case 8:
+ res = et4k.reg_3d8;
+ break;
+ default:
+ res = vga_device::port_03d0_r(space,offset,mem_mask);
+ break;
+ }
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(tseng_vga_device::port_03d0_w)
+{
+ if (CRTC_PORT_ADDR == 0x3d0)
+ {
+ switch(offset)
+ {
+ case 5:
+ vga.crtc.data[vga.crtc.index] = data;
+ tseng_crtc_reg_write(vga.crtc.index,data);
+ //if((vga.crtc.index & 0xfe) != 0x0e)
+ // printf("%02x %02x %d\n",vga.crtc.index,data,space.machine().first_screen()->vpos());
+ break;
+ case 8:
+ et4k.reg_3d8 = data;
+ if(data == 0xa0)
+ et4k.ext_reg_ena = true;
+ else if(data == 0x29)
+ et4k.ext_reg_ena = false;
+ break;
+ default:
+ vga_device::port_03d0_w(space,offset,data,mem_mask);
+ break;
+ }
+ }
+ tseng_define_video_mode();
+}
+
+READ8_MEMBER(tseng_vga_device::mem_r)
+{
+ if(svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en)
+ {
+ offset &= 0xffff;
+ return vga.memory[(offset+svga.bank_r*0x10000)];
+ }
+
+ return vga_device::mem_r(space,offset,mem_mask);
+}
+
+WRITE8_MEMBER(tseng_vga_device::mem_w)
+{
+ if(svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en)
+ {
+ offset &= 0xffff;
+ vga.memory[(offset+svga.bank_w*0x10000)] = data;
+ }
+ else
+ vga_device::mem_w(space,offset,data,mem_mask);
+}
+
+/******************************************
+
+S3 implementation
+
+******************************************/
+
+UINT16 s3_vga_device::offset()
+{
+ //popmessage("Offset: %04x %s %s %s",vga.crtc.offset,vga.crtc.dw?"DW":"--",vga.crtc.word_mode?"BYTE":"WORD",(s3.memory_config & 0x08)?"31":"--");
+ if(s3.memory_config & 0x08)
+ return vga.crtc.offset << 3;
+ return vga_device::offset();
+}
+
+UINT8 s3_vga_device::s3_crtc_reg_read(UINT8 index)
+{
+ UINT8 res;
+
+ if(index <= 0x18)
+ res = crtc_reg_read(index);
+ else
+ {
+ switch(index)
+ {
+ case 0x2d:
+ res = s3.id_high;
+ break;
+ case 0x2e:
+ res = s3.id_low;
+ break;
+ case 0x2f:
+ res = s3.revision;
+ break;
+ case 0x30: // CR30 Chip ID/REV register
+ res = s3.id_cr30;
+ break;
+ case 0x31:
+ res = s3.memory_config;
+ break;
+ case 0x35:
+ res = s3.crt_reg_lock;
+ break;
+ case 0x36: // Configuration register 1
+ res = s3.strapping & 0x000000ff; // PCI (not really), Fast Page Mode DRAM
+ break;
+ case 0x37: // Configuration register 2
+ res = (s3.strapping & 0x0000ff00) >> 8; // enable chipset, 64k BIOS size, internal DCLK/MCLK
+ break;
+ case 0x38:
+ res = s3.reg_lock1;
+ break;
+ case 0x39:
+ res = s3.reg_lock2;
+ break;
+ case 0x42: // CR42 Mode Control
+ res = s3.cr42 & 0x0f; // bit 5 set if interlaced, leave it unset for now.
+ break;
+ case 0x43:
+ res = s3.cr43;
+ break;
+ case 0x45:
+ res = s3.cursor_mode;
+ break;
+ case 0x46:
+ res = (s3.cursor_x & 0xff00) >> 8;
+ break;
+ case 0x47:
+ res = s3.cursor_x & 0x00ff;
+ break;
+ case 0x48:
+ res = (s3.cursor_y & 0xff00) >> 8;
+ break;
+ case 0x49:
+ res = s3.cursor_y & 0x00ff;
+ break;
+ case 0x4a:
+ res = s3.cursor_fg[s3.cursor_fg_ptr];
+ s3.cursor_fg_ptr = 0;
+ break;
+ case 0x4b:
+ res = s3.cursor_bg[s3.cursor_bg_ptr];
+ s3.cursor_bg_ptr = 0;
+ break;
+ case 0x4c:
+ res = (s3.cursor_start_addr & 0xff00) >> 8;
+ break;
+ case 0x4d:
+ res = s3.cursor_start_addr & 0x00ff;
+ break;
+ case 0x4e:
+ res = s3.cursor_pattern_x;
+ break;
+ case 0x4f:
+ res = s3.cursor_pattern_y;
+ break;
+ case 0x51:
+ res = (vga.crtc.start_addr_latch & 0x0c0000) >> 18;
+ res |= ((svga.bank_w & 0x30) >> 2);
+ res |= ((vga.crtc.offset & 0x0300) >> 4);
+ break;
+ case 0x55:
+ res = s3.extended_dac_ctrl;
+ break;
+ case 0x5c:
+ // if VGA dot clock is set to 3 (misc reg bits 2-3), then selected dot clock is read, otherwise read VGA clock select
+ if((vga.miscellaneous_output & 0xc) == 0x0c)
+ res = s3.cr42 & 0x0f;
+ else
+ res = (vga.miscellaneous_output & 0xc) >> 2;
+ break;
+ case 0x67:
+ res = s3.ext_misc_ctrl_2;
+ break;
+ case 0x68: // Configuration register 3
+ res = (s3.strapping & 0x00ff0000) >> 16; // no /CAS,/OE stretch time, 32-bit data bus size
+ break;
+ case 0x69:
+ res = vga.crtc.start_addr_latch >> 16;
+ break;
+ case 0x6a:
+ res = svga.bank_r & 0x7f;
+ break;
+ case 0x6f: // Configuration register 4 (Trio64V+)
+ res = (s3.strapping & 0xff000000) >> 24; // LPB(?) mode, Serial port I/O at port 0xe8, Serial port I/O disabled (MMIO only), no WE delay
+ break;
+ default:
+ res = vga.crtc.data[index];
+ //debugger_break(machine);
+ //printf("%02x\n",index);
+ break;
+ }
+ }
+
+ return res;
+}
+
+void s3_vga_device::s3_define_video_mode()
+{
+ int divisor = 1;
+ int xtal = (vga.miscellaneous_output & 0xc) ? XTAL_28_63636MHz : XTAL_25_1748MHz;
+ double freq;
+
+ if((vga.miscellaneous_output & 0xc) == 0x0c)
+ {
+ // DCLK calculation
+ freq = ((double)(s3.clk_pll_m+2) / (double)((s3.clk_pll_n+2)*(pow(2.0,s3.clk_pll_r)))) * 14.318; // clock between XIN and XOUT
+ xtal = freq * 1000000;
+ }
+
+ if((s3.ext_misc_ctrl_2) >> 4)
+ {
+ svga.rgb8_en = 0;
+ svga.rgb15_en = 0;
+ svga.rgb16_en = 0;
+ svga.rgb32_en = 0;
+ switch((s3.ext_misc_ctrl_2) >> 4)
+ {
+ case 0x01: svga.rgb8_en = 1; break;
+ case 0x03: svga.rgb15_en = 1; divisor = 2; break;
+ case 0x05: svga.rgb16_en = 1; divisor = 2; break;
+ case 0x0d: svga.rgb32_en = 1; divisor = 1; break;
+ default: fatalerror("TODO: S3 colour mode not implemented %02x\n",((s3.ext_misc_ctrl_2) >> 4));
+ }
+ }
+ else
+ {
+ svga.rgb8_en = (s3.memory_config & 8) >> 3;
+ svga.rgb15_en = 0;
+ svga.rgb16_en = 0;
+ svga.rgb32_en = 0;
+ }
+ recompute_params_clock(divisor, xtal);
+}
+
+void s3_vga_device::s3_crtc_reg_write(UINT8 index, UINT8 data)
+{
+ if(index <= 0x18)
+ crtc_reg_write(index,data);
+ else
+ {
+ switch(index)
+ {
+ case 0x31: // CR31 Memory Configuration Register
+ s3.memory_config = data;
+ vga.crtc.start_addr_latch &= ~0x30000;
+ vga.crtc.start_addr_latch |= ((data & 0x30) << 12);
+ s3_define_video_mode();
+ break;
+ case 0x35:
+ if((s3.reg_lock1 & 0xc) != 8 || ((s3.reg_lock1 & 0xc0) == 0)) // lock register
+ return;
+ s3.crt_reg_lock = data;
+ svga.bank_w = data & 0xf;
+ svga.bank_r = svga.bank_w;
+ break;
+ case 0x36:
+ if(s3.reg_lock2 == 0xa5)
+ {
+ s3.strapping = (s3.strapping & 0xffffff00) | data;
+ logerror("CR36: Strapping data = %08x\n",s3.strapping);
+ }
+ break;
+ case 0x37:
+ if(s3.reg_lock2 == 0xa5)
+ {
+ s3.strapping = (s3.strapping & 0xffff00ff) | (data << 8);
+ logerror("CR37: Strapping data = %08x\n",s3.strapping);
+ }
+ break;
+ case 0x38:
+ s3.reg_lock1 = data;
+ break;
+ case 0x39:
+ /* TODO: reg lock mechanism */
+ s3.reg_lock2 = data;
+ break;
+ case 0x40:
+ s3.enable_8514 = data & 0x01; // enable 8514/A registers (x2e8, x6e8, xae8, xee8)
+ break;
+ case 0x42:
+ s3.cr42 = data; // bit 5 = interlace, bits 0-3 = dot clock (seems to be undocumented)
+ break;
+ case 0x43:
+ s3.cr43 = data; // bit 2 = bit 8 of offset register, but only if bits 4-5 of CR51 are 00h.
+ vga.crtc.offset = (vga.crtc.offset & 0x00ff) | ((data & 0x04) << 6);
+ s3_define_video_mode();
+ break;
+/*
+3d4h index 45h (R/W): CR45 Hardware Graphics Cursor Mode
+bit 0 HWGC ENB. Hardware Graphics Cursor Enable. Set to enable the
+ HardWare Cursor in VGA and enhanced modes.
+ 1 (911/24) Delay Timing for Pattern Data Fetch
+ 2 (801/5,928) Hardware Cursor Horizontal Stretch 2. If set the cursor
+ pixels are stretched horizontally to two bytes and items 0 and 1 of
+ the fore/background stacks in 3d4h index 4Ah/4Bh are used.
+ 3 (801/5,928) Hardware Cursor Horizontal Stretch 3. If set the cursor
+ pixels are stretched horizontally to three bytes and items 0,1 and
+ 2 of the fore/background stacks in 3d4h index 4Ah/4Bh are used.
+ 2-3 (805i,864/964) HWC-CSEL. Hardware Cursor Color Select.
+ 0: 4/8bit, 1: 15/16bt, 2: 24bit, 3: 32bit
+ Note: So far I've had better luck with: 0: 8/15/16bit, 1: 32bit??
+ 4 (80x +) Hardware Cursor Right Storage. If set the cursor data is
+ stored in the last 256 bytes of 4 1Kyte lines (4bits/pixel) or the
+ last 512 bytes of 2 2Kbyte lines (8bits/pixel). Intended for
+ 1280x1024 modes where there are no free lines at the bottom.
+ 5 (928) Cursor Control Enable for Brooktree Bt485 DAC. If set and 3d4h
+ index 55h bit 5 is set the HC1 output becomes the ODF and the HC0
+ output becomes the CDE
+ (964) BT485 ODF Selection for Bt485A RAMDAC. If set pin 185 (RS3
+ /ODF) is the ODF output to a Bt485A compatible RamDAC (low for even
+ fields and high for odd fields), if clear pin185 is the RS3 output.
+ */
+ case 0x45:
+ s3.cursor_mode = data;
+ break;
+/*
+3d4h index 46h M(R/W): CR46/7 Hardware Graphics Cursor Origin-X
+bit 0-10 The HardWare Cursor X position. For 64k modes this value should be
+ twice the actual X co-ordinate.
+ */
+ case 0x46:
+ s3.cursor_x = (s3.cursor_x & 0x00ff) | (data << 8);
+ break;
+ case 0x47:
+ s3.cursor_x = (s3.cursor_x & 0xff00) | data;
+ break;
+/*
+3d4h index 48h M(R/W): CR48/9 Hardware Graphics Cursor Origin-Y
+bit 0-9 (911/24) The HardWare Cursor Y position.
+ 0-10 (80x +) The HardWare Cursor Y position.
+Note: The position is activated when the high byte of the Y coordinate (index
+ 48h) is written, so this byte should be written last (not 911/924 ?)
+ */
+ case 0x48:
+ s3.cursor_y = (s3.cursor_y & 0x00ff) | (data << 8);
+ break;
+ case 0x49:
+ s3.cursor_y = (s3.cursor_y & 0xff00) | data;
+ break;
+
+/*
+3d4h index 4Ah (R/W): Hardware Graphics Cursor Foreground Stack (80x +)
+bit 0-7 The Foreground Cursor color. Three bytes (4 for the 864/964) are
+ stacked here. When the Cursor Mode register (3d4h index 45h) is read
+ the stackpointer is reset. When a byte is written the byte is
+ written into the current top of stack and the stackpointer is
+ increased. The first byte written (item 0) is allways used, the
+ other two(3) only when Hardware Cursor Horizontal Stretch (3d4h
+ index 45h bit 2-3) is enabled.
+ */
+ case 0x4a:
+ s3.cursor_fg[s3.cursor_fg_ptr++] = data;
+ s3.cursor_fg_ptr %= 4;
+ break;
+/*
+3d4h index 4Bh (R/W): Hardware Graphics Cursor Background Stack (80x +)
+bit 0-7 The Background Cursor color. Three bytes (4 for the 864/964) are
+ stacked here. When the Cursor Mode register (3d4h index 45h) is read
+ the stackpointer is reset. When a byte is written the byte is
+ written into the current top of stack and the stackpointer is
+ increased. The first byte written (item 0) is allways used, the
+ other two(3) only when Hardware Cursor Horizontal Stretch (3d4h
+ index 45h bit 2-3) is enabled.
+ */
+ case 0x4b:
+ s3.cursor_bg[s3.cursor_bg_ptr++] = data;
+ s3.cursor_bg_ptr %= 4;
+ break;
+/*
+3d4h index 4Ch M(R/W): CR4C/D Hardware Graphics Cursor Storage Start Address
+bit 0-9 (911,924) HCS_STADR. Hardware Graphics Cursor Storage Start Address
+ 0-11 (80x,928) HWGC_STA. Hardware Graphics Cursor Storage Start Address
+ 0-12 (864,964) HWGC_STA. Hardware Graphics Cursor Storage Start Address
+ Address of the HardWare Cursor Map in units of 1024 bytes (256 bytes
+ for planar modes). The cursor map is a 64x64 bitmap with 2 bits (A
+ and B) per pixel. The map is stored as one word (16 bits) of bit A,
+ followed by one word with the corresponding 16 B bits.
+ The bits are interpreted as:
+ A B MS-Windows: X-11:
+ 0 0 Background Screen data
+ 0 1 Foreground Screen data
+ 1 0 Screen data Background
+ 1 1 Inverted screen Foreground
+ The Windows/X11 switch is only available for the 80x +.
+ (911/24) For 64k color modes the cursor is stored as one byte (8
+ bits) of A bits, followed by the 8 B-bits, and each bit in the
+ cursor should be doubled to provide a consistent cursor image.
+ (801/5,928) For Hi/True color modes use the Horizontal Stretch bits
+ (3d4h index 45h bits 2 and 3).
+ */
+ case 0x4c:
+ s3.cursor_start_addr = (s3.cursor_start_addr & 0x00ff) | (data << 8);
+ break;
+ case 0x4d:
+ s3.cursor_start_addr = (s3.cursor_start_addr & 0xff00) | data;
+ break;
+/*
+3d4h index 4Eh (R/W): CR4E HGC Pattern Disp Start X-Pixel Position
+bit 0-5 Pattern Display Start X-Pixel Position.
+ */
+ case 0x4e:
+ s3.cursor_pattern_x = data;
+ break;
+/*
+3d4h index 4Fh (R/W): CR4F HGC Pattern Disp Start Y-Pixel Position
+bit 0-5 Pattern Display Start Y-Pixel Position.
+ */
+ case 0x4f:
+ s3.cursor_pattern_y = data;
+ break;
+ case 0x51:
+ vga.crtc.start_addr_latch &= ~0xc0000;
+ vga.crtc.start_addr_latch |= ((data & 0x3) << 18);
+ svga.bank_w = (svga.bank_w & 0xcf) | ((data & 0x0c) << 2);
+ svga.bank_r = svga.bank_w;
+ if((data & 0x30) != 0x00)
+ vga.crtc.offset = (vga.crtc.offset & 0x00ff) | ((data & 0x30) << 4);
+ else
+ vga.crtc.offset = (vga.crtc.offset & 0x00ff) | ((s3.cr43 & 0x04) << 6);
+ s3_define_video_mode();
+ break;
+ case 0x53:
+ s3.cr53 = data;
+ break;
+/*
+3d4h index 55h (R/W): Extended Video DAC Control Register (80x +)
+bit 0-1 DAC Register Select Bits. Passed to the RS2 and RS3 pins on the
+ RAMDAC, allowing access to all 8 or 16 registers on advanced RAMDACs.
+ If this field is 0, 3d4h index 43h bit 1 is active.
+ 2 Enable General Input Port Read. If set DAC reads are disabled and the
+ STRD strobe for reading the General Input Port is enabled for reading
+ while DACRD is active, if clear DAC reads are enabled.
+ 3 (928) Enable External SID Operation if set. If set video data is
+ passed directly from the VRAMs to the DAC rather than through the
+ VGA chip
+ 4 Hardware Cursor MS/X11 Mode. If set the Hardware Cursor is in X11
+ mode, if clear in MS-Windows mode
+ 5 (80x,928) Hardware Cursor External Operation Mode. If set the two
+ bits of cursor data ,is output on the HC[0-1] pins for the video DAC
+ The SENS pin becomes HC1 and the MID2 pin becomes HC0.
+ 6 ??
+ 7 (80x,928) Disable PA Output. If set PA[0-7] and VCLK are tristated.
+ (864/964) TOFF VCLK. Tri-State Off VCLK Output. VCLK output tri
+ -stated if set
+ */
+ case 0x55:
+ s3.extended_dac_ctrl = data;
+ break;
+/*
+3d4h index 5Dh (R/W): Extended Horizontal Overflow Register (80x +)
+bit 0 Horizontal Total bit 8. Bit 8 of the Horizontal Total register (3d4h
+ index 0)
+ 1 Horizontal Display End bit 8. Bit 8 of the Horizontal Display End
+ register (3d4h index 1)
+ 2 Start Horizontal Blank bit 8. Bit 8 of the Horizontal Start Blanking
+ register (3d4h index 2).
+ 3 (864,964) EHB+64. End Horizontal Blank +64. If set the /BLANK pulse
+ is extended by 64 DCLKs. Note: Is this bit 6 of 3d4h index 3 or
+ does it really extend by 64 ?
+ 4 Start Horizontal Sync Position bit 8. Bit 8 of the Horizontal Start
+ Retrace register (3d4h index 4).
+ 5 (864,964) EHS+32. End Horizontal Sync +32. If set the HSYNC pulse
+ is extended by 32 DCLKs. Note: Is this bit 5 of 3d4h index 5 or
+ does it really extend by 32 ?
+ 6 (928,964) Data Transfer Position bit 8. Bit 8 of the Data Transfer
+ Position register (3d4h index 3Bh)
+ 7 (928,964) Bus-Grant Terminate Position bit 8. Bit 8 of the Bus Grant
+ Termination register (3d4h index 5Fh).
+*/
+ case 0x5d:
+ vga.crtc.horz_total = (vga.crtc.horz_total & 0xfeff) | ((data & 0x01) << 8);
+ vga.crtc.horz_disp_end = (vga.crtc.horz_disp_end & 0xfeff) | ((data & 0x02) << 7);
+ vga.crtc.horz_blank_start = (vga.crtc.horz_blank_start & 0xfeff) | ((data & 0x04) << 6);
+ vga.crtc.horz_blank_end = (vga.crtc.horz_blank_end & 0xffbf) | ((data & 0x08) << 3);
+ vga.crtc.horz_retrace_start = (vga.crtc.horz_retrace_start & 0xfeff) | ((data & 0x10) << 4);
+ vga.crtc.horz_retrace_end = (vga.crtc.horz_retrace_end & 0xffdf) | (data & 0x20);
+ s3_define_video_mode();
+ break;
+/*
+3d4h index 5Eh (R/W): Extended Vertical Overflow Register (80x +)
+bit 0 Vertical Total bit 10. Bit 10 of the Vertical Total register (3d4h
+ index 6). Bits 8 and 9 are in 3d4h index 7 bit 0 and 5.
+ 1 Vertical Display End bit 10. Bit 10 of the Vertical Display End
+ register (3d4h index 12h). Bits 8 and 9 are in 3d4h index 7 bit 1
+ and 6
+ 2 Start Vertical Blank bit 10. Bit 10 of the Vertical Start Blanking
+ register (3d4h index 15h). Bit 8 is in 3d4h index 7 bit 3 and bit 9
+ in 3d4h index 9 bit 5
+ 4 Vertical Retrace Start bit 10. Bit 10 of the Vertical Start Retrace
+ register (3d4h index 10h). Bits 8 and 9 are in 3d4h index 7 bit 2
+ and 7.
+ 6 Line Compare Position bit 10. Bit 10 of the Line Compare register
+ (3d4h index 18h). Bit 8 is in 3d4h index 7 bit 4 and bit 9 in 3d4h
+ index 9 bit 6.
+ */
+ case 0x5e:
+ vga.crtc.vert_total = (vga.crtc.vert_total & 0xfbff) | ((data & 0x01) << 10);
+ vga.crtc.vert_disp_end = (vga.crtc.vert_disp_end & 0xfbff) | ((data & 0x02) << 9);
+ vga.crtc.vert_blank_start = (vga.crtc.vert_blank_start & 0xfbff) | ((data & 0x04) << 8);
+ vga.crtc.vert_retrace_start = (vga.crtc.vert_retrace_start & 0xfbff) | ((data & 0x10) << 6);
+ vga.crtc.line_compare = (vga.crtc.line_compare & 0xfbff) | ((data & 0x40) << 4);
+ s3_define_video_mode();
+ break;
+ case 0x67:
+ s3.ext_misc_ctrl_2 = data;
+ s3_define_video_mode();
+ break;
+ case 0x68:
+ if(s3.reg_lock2 == 0xa5)
+ {
+ s3.strapping = (s3.strapping & 0xff00ffff) | (data << 16);
+ logerror("CR68: Strapping data = %08x\n",s3.strapping);
+ }
+ break;
+ case 0x69:
+ vga.crtc.start_addr_latch &= ~0x1f0000;
+ vga.crtc.start_addr_latch |= ((data & 0x1f) << 16);
+ s3_define_video_mode();
+ break;
+ case 0x6a:
+ svga.bank_w = data & 0x3f;
+ svga.bank_r = svga.bank_w;
+ break;
+ case 0x6f:
+ if(s3.reg_lock2 == 0xa5)
+ {
+ s3.strapping = (s3.strapping & 0x00ffffff) | (data << 24);
+ logerror("CR6F: Strapping data = %08x\n",s3.strapping);
+ }
+ break;
+ default:
+ if(LOG_8514) logerror("S3: 3D4 index %02x write %02x\n",index,data);
+ break;
+ }
+ }
+}
+
+UINT8 s3_vga_device::s3_seq_reg_read(UINT8 index)
+{
+ UINT8 res = 0xff;
+
+ if(index <= 0x0c)
+ res = vga.sequencer.data[index];
+ else
+ {
+ switch(index)
+ {
+ case 0x10:
+ res = s3.sr10;
+ break;
+ case 0x11:
+ res = s3.sr11;
+ break;
+ case 0x12:
+ res = s3.sr12;
+ break;
+ case 0x13:
+ res = s3.sr13;
+ break;
+ case 0x15:
+ res = s3.sr15;
+ break;
+ case 0x17:
+ res = s3.sr17; // CLKSYN test register
+ s3.sr17--; // who knows what it should return, docs only say it defaults to 0, and is reserved for testing of the clock synthesiser
+ break;
+ }
+ }
+
+ return res;
+}
+
+void s3_vga_device::s3_seq_reg_write(UINT8 index, UINT8 data)
+{
+ if(index <= 0x0c)
+ {
+ vga.sequencer.data[vga.sequencer.index] = data;
+ seq_reg_write(vga.sequencer.index,data);
+ }
+ else
+ {
+ switch(index)
+ {
+ // Memory CLK PLL
+ case 0x10:
+ s3.sr10 = data;
+ break;
+ case 0x11:
+ s3.sr11 = data;
+ break;
+ // Video CLK PLL
+ case 0x12:
+ s3.sr12 = data;
+ break;
+ case 0x13:
+ s3.sr13 = data;
+ break;
+ case 0x15:
+ if(data & 0x02) // load DCLK frequency (would normally have a small variable delay)
+ {
+ s3.clk_pll_n = s3.sr12 & 0x1f;
+ s3.clk_pll_r = (s3.sr12 & 0x60) >> 5;
+ s3.clk_pll_m = s3.sr13 & 0x7f;
+ s3_define_video_mode();
+ }
+ if(data & 0x20) // immediate DCLK/MCLK load
+ {
+ s3.clk_pll_n = s3.sr12 & 0x1f;
+ s3.clk_pll_r = (s3.sr12 & 0x60) >> 5;
+ s3.clk_pll_m = s3.sr13 & 0x7f;
+ s3_define_video_mode();
+ }
+ s3.sr15 = data;
+ }
+ }
+}
+
+
+
+READ8_MEMBER(s3_vga_device::port_03b0_r)
+{
+ UINT8 res = 0xff;
+
+ if (CRTC_PORT_ADDR == 0x3b0)
+ {
+ switch(offset)
+ {
+ case 5:
+ res = s3_crtc_reg_read(vga.crtc.index);
+ break;
+ default:
+ res = vga_device::port_03b0_r(space,offset,mem_mask);
+ break;
+ }
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(s3_vga_device::port_03b0_w)
+{
+ if (CRTC_PORT_ADDR == 0x3b0)
+ {
+ switch(offset)
+ {
+ case 5:
+ vga.crtc.data[vga.crtc.index] = data;
+ s3_crtc_reg_write(vga.crtc.index,data);
+ break;
+ default:
+ vga_device::port_03b0_w(space,offset,data,mem_mask);
+ break;
+ }
+ }
+}
+
+READ8_MEMBER(s3_vga_device::port_03c0_r)
+{
+ UINT8 res;
+
+ switch(offset)
+ {
+ case 5:
+ res = s3_seq_reg_read(vga.sequencer.index);
+ break;
+ default:
+ res = vga_device::port_03c0_r(space,offset,mem_mask);
+ break;
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(s3_vga_device::port_03c0_w)
+{
+ switch(offset)
+ {
+ case 5:
+ s3_seq_reg_write(vga.sequencer.index,data);
+ break;
+ default:
+ vga_device::port_03c0_w(space,offset,data,mem_mask);
+ break;
+ }
+}
+
+READ8_MEMBER(s3_vga_device::port_03d0_r)
+{
+ UINT8 res = 0xff;
+
+ if (CRTC_PORT_ADDR == 0x3d0)
+ {
+ switch(offset)
+ {
+ case 5:
+ res = s3_crtc_reg_read(vga.crtc.index);
+ break;
+ default:
+ res = vga_device::port_03d0_r(space,offset,mem_mask);
+ break;
+ }
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(s3_vga_device::port_03d0_w)
+{
+ if (CRTC_PORT_ADDR == 0x3d0)
+ {
+ switch(offset)
+ {
+ case 5:
+ vga.crtc.data[vga.crtc.index] = data;
+ s3_crtc_reg_write(vga.crtc.index,data);
+ break;
+ default:
+ vga_device::port_03d0_w(space,offset,data,mem_mask);
+ break;
+ }
+ }
+}
+
+READ8_MEMBER(ati_vga_device::port_03c0_r)
+{
+ UINT8 data = 0xff;
+
+ switch(offset)
+ {
+ case 1:
+ if ((vga.attribute.index&0x1f) < sizeof(vga.attribute.data))
+ data = vga.attribute.data[vga.attribute.index&0x1f];
+ break;
+ default:
+ data = vga_device::port_03c0_r(space,offset,mem_mask);
+ break;
+ }
+ return data;
+}
+
+
+/* accelerated ports, TBD ... */
+
+void ibm8514a_device::ibm8514_write_fg(UINT32 offset)
+{
+ address_space& space = machine().device("maincpu")->memory().space(AS_PROGRAM);
+ offset %= m_vga->vga.svga_intf.vram_size;
+ UINT8 dst = m_vga->mem_linear_r(space,offset,0xff);
+ UINT8 src = 0;
+
+ // check clipping rectangle
+ if((ibm8514.current_cmd & 0xe000) == 0xc000) // BitBLT writes to the destination X/Y, so check that instead
+ {
+ if(ibm8514.dest_x < ibm8514.scissors_left || ibm8514.dest_x > ibm8514.scissors_right || ibm8514.dest_y < ibm8514.scissors_top || ibm8514.dest_y > ibm8514.scissors_bottom)
+ return; // do nothing
+ }
+ else
+ if(ibm8514.curr_x < ibm8514.scissors_left || ibm8514.curr_x > ibm8514.scissors_right || ibm8514.curr_y < ibm8514.scissors_top || ibm8514.curr_y > ibm8514.scissors_bottom)
+ return; // do nothing
+
+ // determine source
+ switch(ibm8514.fgmix & 0x0060)
+ {
+ case 0x0000:
+ src = ibm8514.bgcolour;
+ break;
+ case 0x0020:
+ src = ibm8514.fgcolour;
+ break;
+ case 0x0040:
+ src = ibm8514.pixel_xfer;
+ break;
+ case 0x0060:
+ // video memory - presume the memory is sourced from the current X/Y co-ords
+ src = m_vga->mem_linear_r(space,((ibm8514.curr_y * IBM8514_LINE_LENGTH) + ibm8514.curr_x),0xff);
+ break;
+ }
+
+ // write the data
+ switch(ibm8514.fgmix & 0x000f)
+ {
+ case 0x0000:
+ m_vga->mem_linear_w(space,offset,~dst,0xff);
+ break;
+ case 0x0001:
+ m_vga->mem_linear_w(space,offset,0x00,0xff);
+ break;
+ case 0x0002:
+ m_vga->mem_linear_w(space,offset,0xff,0xff);
+ break;
+ case 0x0003:
+ m_vga->mem_linear_w(space,offset,dst,0xff);
+ break;
+ case 0x0004:
+ m_vga->mem_linear_w(space,offset,~src,0xff);
+ break;
+ case 0x0005:
+ m_vga->mem_linear_w(space,offset,src ^ dst,0xff);
+ break;
+ case 0x0006:
+ m_vga->mem_linear_w(space,offset,~(src ^ dst),0xff);
+ break;
+ case 0x0007:
+ m_vga->mem_linear_w(space,offset,src,0xff);
+ break;
+ case 0x0008:
+ m_vga->mem_linear_w(space,offset,~(src & dst),0xff);
+ break;
+ case 0x0009:
+ m_vga->mem_linear_w(space,offset,(~src) | dst,0xff);
+ break;
+ case 0x000a:
+ m_vga->mem_linear_w(space,offset,src | (~dst),0xff);
+ break;
+ case 0x000b:
+ m_vga->mem_linear_w(space,offset,src | dst,0xff);
+ break;
+ case 0x000c:
+ m_vga->mem_linear_w(space,offset,src & dst,0xff);
+ break;
+ case 0x000d:
+ m_vga->mem_linear_w(space,offset,src & (~dst),0xff);
+ break;
+ case 0x000e:
+ m_vga->mem_linear_w(space,offset,(~src) & dst,0xff);
+ break;
+ case 0x000f:
+ m_vga->mem_linear_w(space,offset,~(src | dst),0xff);
+ break;
+ }
+}
+
+void ibm8514a_device::ibm8514_write_bg(UINT32 offset)
+{
+ address_space& space = machine().device("maincpu")->memory().space(AS_PROGRAM);
+ offset %= m_vga->vga.svga_intf.vram_size;
+ UINT8 dst = m_vga->mem_linear_r(space,offset,0xff);
+ UINT8 src = 0;
+
+ // check clipping rectangle
+ if((ibm8514.current_cmd & 0xe000) == 0xc000) // BitBLT writes to the destination X/Y, so check that instead
+ {
+ if(ibm8514.dest_x < ibm8514.scissors_left || ibm8514.dest_x > ibm8514.scissors_right || ibm8514.dest_y < ibm8514.scissors_top || ibm8514.dest_y > ibm8514.scissors_bottom)
+ return; // do nothing
+ }
+ else
+ if(ibm8514.curr_x < ibm8514.scissors_left || ibm8514.curr_x > ibm8514.scissors_right || ibm8514.curr_y < ibm8514.scissors_top || ibm8514.curr_y > ibm8514.scissors_bottom)
+ return; // do nothing
+
+ // determine source
+ switch(ibm8514.bgmix & 0x0060)
+ {
+ case 0x0000:
+ src = ibm8514.bgcolour;
+ break;
+ case 0x0020:
+ src = ibm8514.fgcolour;
+ break;
+ case 0x0040:
+ src = ibm8514.pixel_xfer;
+ break;
+ case 0x0060:
+ // video memory - presume the memory is sourced from the current X/Y co-ords
+ src = m_vga->mem_linear_r(space,((ibm8514.curr_y * IBM8514_LINE_LENGTH) + ibm8514.curr_x),0xff);
+ break;
+ }
+
+ // write the data
+ switch(ibm8514.bgmix & 0x000f)
+ {
+ case 0x0000:
+ m_vga->mem_linear_w(space,offset,~dst,0xff);
+ break;
+ case 0x0001:
+ m_vga->mem_linear_w(space,offset,0x00,0xff);
+ break;
+ case 0x0002:
+ m_vga->mem_linear_w(space,offset,0xff,0xff);
+ break;
+ case 0x0003:
+ m_vga->mem_linear_w(space,offset,dst,0xff);
+ break;
+ case 0x0004:
+ m_vga->mem_linear_w(space,offset,~src,0xff);
+ break;
+ case 0x0005:
+ m_vga->mem_linear_w(space,offset,src ^ dst,0xff);
+ break;
+ case 0x0006:
+ m_vga->mem_linear_w(space,offset,~(src ^ dst),0xff);
+ break;
+ case 0x0007:
+ m_vga->mem_linear_w(space,offset,src,0xff);
+ break;
+ case 0x0008:
+ m_vga->mem_linear_w(space,offset,~(src & dst),0xff);
+ break;
+ case 0x0009:
+ m_vga->mem_linear_w(space,offset,(~src) | dst,0xff);
+ break;
+ case 0x000a:
+ m_vga->mem_linear_w(space,offset,src | (~dst),0xff);
+ break;
+ case 0x000b:
+ m_vga->mem_linear_w(space,offset,src | dst,0xff);
+ break;
+ case 0x000c:
+ m_vga->mem_linear_w(space,offset,src & dst,0xff);
+ break;
+ case 0x000d:
+ m_vga->mem_linear_w(space,offset,src & (~dst),0xff);
+ break;
+ case 0x000e:
+ m_vga->mem_linear_w(space,offset,(~src) & dst,0xff);
+ break;
+ case 0x000f:
+ m_vga->mem_linear_w(space,offset,~(src | dst),0xff);
+ break;
+ }
+}
+
+void ibm8514a_device::ibm8514_write(UINT32 offset, UINT32 src)
+{
+ int data_size = 8;
+ UINT32 xfer = 0;
+ address_space& space = machine().device("maincpu")->memory().space(AS_PROGRAM);
+
+ switch(ibm8514.pixel_control & 0x00c0)
+ {
+ case 0x0000: // Foreground Mix only
+ ibm8514_write_fg(offset);
+ break;
+ case 0x0040: // fixed pattern (?)
+ // TODO
+ break;
+ case 0x0080: // use pixel transfer register
+ if(ibm8514.bus_size == 0) // 8-bit
+ data_size = 8;
+ if(ibm8514.bus_size == 1) // 16-bit
+ data_size = 16;
+ if(ibm8514.bus_size == 2) // 32-bit
+ data_size = 32;
+ if((ibm8514.current_cmd & 0x1000) && (data_size != 8))
+ {
+ xfer = ((ibm8514.pixel_xfer & 0x000000ff) << 8) | ((ibm8514.pixel_xfer & 0x0000ff00) >> 8)
+ | ((ibm8514.pixel_xfer & 0x00ff0000) << 8) | ((ibm8514.pixel_xfer & 0xff000000) >> 8);
+ }
+ else
+ xfer = ibm8514.pixel_xfer;
+ if(ibm8514.current_cmd & 0x0002)
+ {
+ if((xfer & ((1<<(data_size-1))>>ibm8514.src_x)) != 0)
+ ibm8514_write_fg(offset);
+ else
+ ibm8514_write_bg(offset);
+ }
+ else
+ {
+ ibm8514_write_fg(offset);
+ }
+ ibm8514.src_x++;
+ if(ibm8514.src_x >= data_size)
+ ibm8514.src_x = 0;
+ break;
+ case 0x00c0: // use source plane
+ if(m_vga->mem_linear_r(space,src,0xff) != 0x00)
+ ibm8514_write_fg(offset);
+ else
+ ibm8514_write_bg(offset);
+ break;
+ }
+}
+
+/*
+92E8h W(R/W): Line Error Term Read/Write Register (ERR_TERM).
+bit 0-12 (911/924) LINE PARAMETER/ERROR TERM. For Line Drawing this is the
+ Bresenham Initial Error Term 2*dminor-dmajor (one less if the
+ starting X is less than the ending X) in two's complement format.
+ (dminor is the length of the line projected onto the minor or
+ dependent axis, dmajor is the length of the line projected onto
+ the major or independent axis).
+ 0-13 (80x +) LINE PARAMETER/ERROR TERM. See above.
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_line_error_r)
+{
+ return ibm8514.line_errorterm;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_line_error_w)
+{
+ ibm8514.line_errorterm = data;
+ if(LOG_8514) logerror("8514/A: Line Parameter/Error Term write %04x\n",data);
+}
+
+/*
+ 9AE8h W(R): Graphics Processor Status Register (GP_STAT)
+bit 0-7 Queue State.
+ 00h = 8 words available - queue is empty
+ 01h = 7 words available
+ 03h = 6 words available
+ 07h = 5 words available
+ 0Fh = 4 words available
+ 1Fh = 3 words available
+ 3Fh = 2 words available
+ 7Fh = 1 word available
+ FFh = 0 words available - queue is full
+ 8 (911-928) DTA AVA. Read Data Available. If set data is ready to be
+ read from the PIX_TRANS register (E2E8h).
+ 9 HDW BSY. Hardware Graphics Processor Busy
+ If set the Graphics Processor is busy.
+ 10 (928 +) AE. All FIFO Slots Empty. If set all FIFO slots are empty.
+ 11-15 (864/964) (R) Queue State bits 8-12. 1Fh if 8 words or less
+ available, Fh for 9 words, 7 for 10 words, 3 for 11 words, 1 for
+ 12 words and 0 for 13 words available.
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_gpstatus_r)
+{
+ UINT16 ret = 0x0000;
+
+ //if(LOG_8514) logerror("S3: 9AE8 read\n");
+ if(ibm8514.gpbusy == true)
+ ret |= 0x0200;
+ if(ibm8514.data_avail == true)
+ ret |= 0x0100;
+ return ret;
+}
+
+void ibm8514a_device::ibm8514_draw_vector(UINT8 len, UINT8 dir, bool draw)
+{
+ UINT32 offset;
+ int x = 0;
+
+ while(x <= len)
+ {
+ offset = (ibm8514.curr_y * IBM8514_LINE_LENGTH) + ibm8514.curr_x;
+ if(draw)
+ ibm8514_write(offset,offset);
+ switch(dir)
+ {
+ case 0: // 0 degrees
+ ibm8514.curr_x++;
+ break;
+ case 1: // 45 degrees
+ ibm8514.curr_x++;
+ ibm8514.curr_y--;
+ break;
+ case 2: // 90 degrees
+ ibm8514.curr_y--;
+ break;
+ case 3: // 135 degrees
+ ibm8514.curr_y--;
+ ibm8514.curr_x--;
+ break;
+ case 4: // 180 degrees
+ ibm8514.curr_x--;
+ break;
+ case 5: // 225 degrees
+ ibm8514.curr_x--;
+ ibm8514.curr_y++;
+ break;
+ case 6: // 270 degrees
+ ibm8514.curr_y++;
+ break;
+ case 7: // 315 degrees
+ ibm8514.curr_y++;
+ ibm8514.curr_x++;
+ break;
+ }
+ x++;
+ }
+}
+
+/*
+9AE8h W(W): Drawing Command Register (CMD)
+bit 0 (911-928) ~RD/WT. Read/Write Data. If set VRAM write operations are
+ enabled. If clear operations execute normally but writes are
+ disabled.
+ 1 PX MD. Pixel Mode. Defines the orientation of the display bitmap.
+ 0 = Through plane mode (Single pixel transferred at a time)
+ 1 = Across plane mode (Multiple pixels transferred at a time).
+ 2 LAST PXOF. Last Pixel Off. If set the last pixel of a line command
+ (CMD_LINE, SSV or LINEAF) is not drawn. This is used for mixes such
+ as XOR where drawing the same pixel twice would give the wrong
+ color.
+ 3 DIR TYP. Direction Type.
+ 0: Bresenham line drawing (X-Y Axial)
+ CMD_LINE draws a line using the Bresenham algorithm as
+ specified in the DESTY_AXSTP (8AE8h), DESTX_DIASTP (8EE8h),
+ ERR_TERM (92E8h) and MAJ_AXIS_PCNT (96E8h) registers
+ INC_X, INC_Y and YMAJAXIS determines the direction.
+ 1: Vector line draws (Radial).
+ CMD_NOP allows drawing of Short Stroke Vectors (SSVs) by
+ writing to the Short Stroke register (9EE8h).
+ CMD_LINE draws a vector of length MAJ_AXIS_PCNT (96E8h)
+ in the direction specified by LINEDIR (bits 5-7).
+ DRWG-DIR determines the direction of the line.
+ 4 DRAW YES. If clear the current position is moved, but no pixels
+ are modified. This bit should be set when attempting read or
+ write of bitmap data.
+ 5-7 DRWG-DIR. Drawing Direction. When a line draw command (CMD_LINE)
+ with DIR TYP=1 (Radial) is issued, these bits define the direction
+ of the line counter clockwise relative to the positive X-axis.
+ 0 = 000 degrees
+ 1 = 045 degrees
+ 2 = 090 degrees
+ 3 = 135 degrees
+ 4 = 180 degrees
+ 5 = 225 degrees
+ 6 = 270 degrees
+ 7 = 315 degrees
+ 5 INC_X. This bit together with INC_Y determines which quadrant
+ the slope of a line lies within. They also determine the
+ orientation of rectangle draw commands.
+ If set lines are drawn in the positive X direction (left to right).
+ 6 YMAJAXIS. For Bresenham line drawing commands this bit determines
+ which axis is the independent or major axis. INC_X and INC_Y
+ determines which quadrant the slope falls within. This bit further
+ defines the slope to within an octant.
+ If set Y is the major (independent) axis.
+ 7 INC_Y. This bit together with INC_X determines which quadrant
+ the slope of a line lies within. They also determine the
+ orientation of rectangle draw commands.
+ If set lines are drawn in the positive Y direction (down).
+ 8 WAIT YES. If set the drawing engine waits for read/write of the
+ PIX_TRANS register (E2E8h) for each pixel during a draw operation.
+ 9 (911-928) BUS SIZE. If set the PIX_TRANS register (E2E8h) is
+ processed internally as two bytes in the order specified by BYTE
+ SWAP. If clear all accesses to E2E8h are 8bit.
+ 9-10 (864,964) BUS SIZE. Select System Bus Size. Controls the width of
+ the Pixel Data Transfer registers (E2E8h,E2EAh) and the memory
+ mapped I/O. 0: 8bit, 1: 16bit, 2: 32bit
+ 12 BYTE SWAP. Affects both reads and writes of SHORT_STROKE (9EE8h)
+ and PIX_TRANS (E2E8h) when 16bit=1.
+ If set take low byte first, if clear take high byte first.
+ 13-15 Draw Command:
+ 0 = NOP. Used for Short Stroke Vectors.
+ 1 = Draw Line. If bit 3 is set the line is drawn to the angle in
+ bits 5-7 and the length in the Major Axis Pixel Count register
+ (96E8h), if clear the line is drawn from the Bresenham
+ constants in the Axial Step Constant register(8AE8h), Diagonal
+ Step Constant register (8EE8h), Line Error Term register
+ (92E8h) and bits 5-7 of this register.
+ 2 = Rectangle Fill. The Current X (86E8h) and Y (82E8h)
+ registers holds the coordinates of the rectangle to fill and
+ the Major Axis Pixel Count register (96E8h) holds the
+ horizontal width (in pixels) fill and the Minor Axis Pixel
+ Count register (BEE8h index 0) holds the height of the
+ rectangle.
+ 6 = BitBLT. Copies the source rectangle specified by the Current X
+ (86E8h) and Y (8AE8h) registers to the destination rectangle,
+ specified as for the Rectangle Fills.
+ 7 = (80x +) Pattern Fill. The source rectangle is an 8x8 pattern
+ rectangle, which is copied repeatably to the destination
+ rectangle.
+ */
+WRITE16_MEMBER(ibm8514a_device::ibm8514_cmd_w)
+{
+ int x,y;
+ int pattern_x,pattern_y;
+ UINT32 off,src;
+ UINT8 readmask;
+
+ ibm8514.current_cmd = data;
+ ibm8514.src_x = 0;
+ ibm8514.src_y = 0;
+ ibm8514.bus_size = (data & 0x0600) >> 9;
+ switch(data & 0xe000)
+ {
+ case 0x0000: // NOP (for "Short Stroke Vectors")
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ if(LOG_8514) logerror("8514/A: Command (%04x) - NOP (Short Stroke Vector)\n",ibm8514.current_cmd);
+ break;
+ case 0x2000: // Line
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ if(data & 0x0008)
+ {
+ if(data & 0x0100)
+ {
+ ibm8514.state = IBM8514_DRAWING_LINE;
+ ibm8514.data_avail = true;
+ if(LOG_8514) logerror("8514/A: Command (%04x) - Vector Line (WAIT) %i,%i \n",ibm8514.current_cmd,ibm8514.curr_x,ibm8514.curr_y);
+ }
+ else
+ {
+ ibm8514_draw_vector(ibm8514.rect_width,(data & 0x00e0) >> 5,(data & 0010) ? true : false);
+ if(LOG_8514) logerror("8514/A: Command (%04x) - Vector Line - %i,%i \n",ibm8514.current_cmd,ibm8514.curr_x,ibm8514.curr_y);
+ }
+ }
+ else
+ {
+ // Not perfect, but will do for now.
+ INT16 dx = ibm8514.rect_width;
+ INT16 dy = ibm8514.line_axial_step >> 1;
+ INT16 err = ibm8514.line_errorterm;
+ int sx = (data & 0x0020) ? 1 : -1;
+ int sy = (data & 0x0080) ? 1 : -1;
+ int count = 0;
+ INT16 temp;
+
+ if(LOG_8514) logerror("8514/A: Command (%04x) - Line (Bresenham) - %i,%i Axial %i, Diagonal %i, Error %i, Major Axis %i, Minor Axis %i\n",ibm8514.current_cmd,
+ ibm8514.curr_x,ibm8514.curr_y,ibm8514.line_axial_step,ibm8514.line_diagonal_step,ibm8514.line_errorterm,ibm8514.rect_width,ibm8514.rect_height);
+
+ if((data & 0x0040))
+ {
+ temp = dx; dx = dy; dy = temp;
+ }
+ for(;;)
+ {
+ ibm8514_write(ibm8514.curr_x + (ibm8514.curr_y * IBM8514_LINE_LENGTH),ibm8514.curr_x + (ibm8514.curr_y * IBM8514_LINE_LENGTH));
+ if (count > ibm8514.rect_width) break;
+ count++;
+ if((err*2) > -dy)
+ {
+ err -= dy;
+ ibm8514.curr_x += sx;
+ }
+ if((err*2) < dx)
+ {
+ err += dx;
+ ibm8514.curr_y += sy;
+ }
+ }
+ }
+ break;
+ case 0x4000: // Rectangle Fill
+ if(data & 0x0100) // WAIT (for read/write of PIXEL TRANSFER (E2E8))
+ {
+ ibm8514.state = IBM8514_DRAWING_RECT;
+ //ibm8514.gpbusy = true; // DirectX 5 keeps waiting for the busy bit to be clear...
+ ibm8514.bus_size = (data & 0x0600) >> 9;
+ ibm8514.data_avail = true;
+ if(LOG_8514) logerror("8514/A: Command (%04x) - Rectangle Fill (WAIT) %i,%i Width: %i Height: %i Colour: %08x\n",ibm8514.current_cmd,ibm8514.curr_x,
+ ibm8514.curr_y,ibm8514.rect_width,ibm8514.rect_height,ibm8514.fgcolour);
+ break;
+ }
+ if(LOG_8514) logerror("8514/A: Command (%04x) - Rectangle Fill %i,%i Width: %i Height: %i Colour: %08x\n",ibm8514.current_cmd,ibm8514.curr_x,
+ ibm8514.curr_y,ibm8514.rect_width,ibm8514.rect_height,ibm8514.fgcolour);
+ off = 0;
+ off += (IBM8514_LINE_LENGTH * ibm8514.curr_y);
+ off += ibm8514.curr_x;
+ for(y=0;y<=ibm8514.rect_height;y++)
+ {
+ for(x=0;x<=ibm8514.rect_width;x++)
+ {
+ if(data & 0x0020) // source pattern is always based on current X/Y?
+ ibm8514_write((off+x) % m_vga->vga.svga_intf.vram_size,(off+x) % m_vga->vga.svga_intf.vram_size);
+ else
+ ibm8514_write((off-x) % m_vga->vga.svga_intf.vram_size,(off-x) % m_vga->vga.svga_intf.vram_size);
+ if(ibm8514.current_cmd & 0x0020)
+ {
+ ibm8514.curr_x++;
+ if(ibm8514.curr_x > ibm8514.prev_x + ibm8514.rect_width)
+ {
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.src_x = 0;
+ if(ibm8514.current_cmd & 0x0080)
+ ibm8514.curr_y++;
+ else
+ ibm8514.curr_y--;
+ }
+ }
+ else
+ {
+ ibm8514.curr_x--;
+ if(ibm8514.curr_x < ibm8514.prev_x - ibm8514.rect_width)
+ {
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.src_x = 0;
+ if(ibm8514.current_cmd & 0x0080)
+ ibm8514.curr_y++;
+ else
+ ibm8514.curr_y--;
+ }
+ }
+ }
+ if(data & 0x0080)
+ off += IBM8514_LINE_LENGTH;
+ else
+ off -= IBM8514_LINE_LENGTH;
+ }
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ break;
+ case 0xc000: // BitBLT
+ if(LOG_8514) logerror("8514/A: Command (%04x) - BitBLT from %i,%i to %i,%i Width: %i Height: %i\n",ibm8514.current_cmd,
+ ibm8514.curr_x,ibm8514.curr_y,ibm8514.dest_x,ibm8514.dest_y,ibm8514.rect_width,ibm8514.rect_height);
+ off = 0;
+ off += (IBM8514_LINE_LENGTH * ibm8514.dest_y);
+ off += ibm8514.dest_x;
+ src = 0;
+ src += (IBM8514_LINE_LENGTH * ibm8514.curr_y);
+ src += ibm8514.curr_x;
+ readmask = ((ibm8514.read_mask & 0x01) << 7) | ((ibm8514.read_mask & 0xfe) >> 1);
+ for(y=0;y<=ibm8514.rect_height;y++)
+ {
+ for(x=0;x<=ibm8514.rect_width;x++)
+ {
+ if((ibm8514.pixel_control & 0xc0) == 0xc0)
+ {
+ // only check read mask if Mix Select is set to 11 (VRAM determines mix)
+ if(m_vga->mem_linear_r(space,(src+x),0xff) & ~readmask)
+ {
+ // presumably every program is going to be smart enough to set the FG mix to use VRAM (0x6x)
+ if(data & 0x0020)
+ ibm8514_write(off+x,src+x);
+ else
+ ibm8514_write(off-x,src-x);
+ }
+ }
+ else
+ {
+ // presumably every program is going to be smart enough to set the FG mix to use VRAM (0x6x)
+ if(data & 0x0020)
+ ibm8514_write(off+x,src+x);
+ else
+ ibm8514_write(off-x,src-x);
+ }
+ if(ibm8514.current_cmd & 0x0020)
+ {
+ ibm8514.curr_x++;
+ if(ibm8514.curr_x > ibm8514.prev_x + ibm8514.rect_width)
+ {
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.src_x = 0;
+ if(ibm8514.current_cmd & 0x0080)
+ ibm8514.curr_y++;
+ else
+ ibm8514.curr_y--;
+ }
+ }
+ else
+ {
+ ibm8514.curr_x--;
+ if(ibm8514.curr_x < ibm8514.prev_x - ibm8514.rect_width)
+ {
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.src_x = 0;
+ if(ibm8514.current_cmd & 0x0080)
+ ibm8514.curr_y++;
+ else
+ ibm8514.curr_y--;
+ }
+ }
+ }
+ if(data & 0x0080)
+ {
+ src += IBM8514_LINE_LENGTH;
+ off += IBM8514_LINE_LENGTH;
+ }
+ else
+ {
+ src -= IBM8514_LINE_LENGTH;
+ off -= IBM8514_LINE_LENGTH;
+ }
+ }
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.curr_y = ibm8514.prev_y;
+ break;
+ case 0xe000: // Pattern Fill
+ if(LOG_8514) logerror("8514/A: Command (%04x) - Pattern Fill - source %i,%i dest %i,%i Width: %i Height: %i\n",ibm8514.current_cmd,
+ ibm8514.curr_x,ibm8514.curr_y,ibm8514.dest_x,ibm8514.dest_y,ibm8514.rect_width,ibm8514.rect_height);
+ off = 0;
+ off += (IBM8514_LINE_LENGTH * ibm8514.dest_y);
+ off += ibm8514.dest_x;
+ src = 0;
+ src += (IBM8514_LINE_LENGTH * ibm8514.curr_y);
+ src += ibm8514.curr_x;
+ if(data & 0x0020)
+ pattern_x = 0;
+ else
+ pattern_x = 7;
+ if(data & 0x0080)
+ pattern_y = 0;
+ else
+ pattern_y = 7;
+
+ for(y=0;y<=ibm8514.rect_height;y++)
+ {
+ for(x=0;x<=ibm8514.rect_width;x++)
+ {
+ if(data & 0x0020)
+ {
+ ibm8514_write(off+x,src+pattern_x);
+ pattern_x++;
+ if(pattern_x >= 8)
+ pattern_x = 0;
+ }
+ else
+ {
+ ibm8514_write(off-x,src-pattern_x);
+ pattern_x--;
+ if(pattern_x < 0)
+ pattern_x = 7;
+ }
+ }
+
+ // for now, presume that INC_X and INC_Y affect both src and dest, at is would for a bitblt.
+ if(data & 0x0020)
+ pattern_x = 0;
+ else
+ pattern_x = 7;
+ if(data & 0x0080)
+ {
+ pattern_y++;
+ src += IBM8514_LINE_LENGTH;
+ if(pattern_y >= 8)
+ {
+ pattern_y = 0;
+ src -= (IBM8514_LINE_LENGTH * 8); // move src pointer back to top of pattern
+ }
+ off += IBM8514_LINE_LENGTH;
+ }
+ else
+ {
+ pattern_y--;
+ src -= IBM8514_LINE_LENGTH;
+ if(pattern_y < 0)
+ {
+ pattern_y = 7;
+ src += (IBM8514_LINE_LENGTH * 8); // move src pointer back to bottom of pattern
+ }
+ off -= IBM8514_LINE_LENGTH;
+ }
+ }
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ break;
+ default:
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ if(LOG_8514) logerror("8514/A: Unknown command: %04x\n",data);
+ }
+}
+
+/*
+8AE8h W(R/W): Destination Y Position & Axial Step Constant Register
+ (DESTY_AXSTP)
+bit 0-11 DESTINATION Y-POSITION. During BITBLT operations this is the Y
+ co-ordinate of the destination in pixels.
+ 0-12 (911/924) LINE PARAMETER AXIAL STEP CONSTANT. During Line Drawing,
+ this is the Bresenham constant 2*dminor in two's complement
+ format. (dminor is the length of the line projected onto the minor
+ or dependent axis).
+ 0-13 (80 x+) LINE PARAMETER AXIAL STEP CONSTANT. Se above
+
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_desty_r)
+{
+ return ibm8514.line_axial_step;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_desty_w)
+{
+ ibm8514.line_axial_step = data;
+ ibm8514.dest_y = data;
+ if(LOG_8514) logerror("8514/A: Line Axial Step / Destination Y write %04x\n",data);
+}
+
+/*
+8EE8h W(R/W): Destination X Position & Diagonal Step Constant Register
+ (DESTX_DISTP)
+bit 0-11 DESTINATION X-POSITION. During BITBLT operations this is the X
+ co-ordinate of the destination in pixels.
+ 0-12 (911/924) LINE PARAMETER DIAGONAL STEP CONSTANT. During Line
+ Drawing this is the Bresenham constant 2*dminor-2*dmajor in two's
+ complement format. (dminor is the length of the line projected
+ onto the minor or dependent axis, dmajor is the length of the line
+ projected onto the major or independent axis)
+ 0-13 (80x +) LINE PARAMETER DIAGONAL STEP CONSTANT. Se above
+
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_destx_r)
+{
+ return ibm8514.line_diagonal_step;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_destx_w)
+{
+ ibm8514.line_diagonal_step = data;
+ ibm8514.dest_x = data;
+ if(LOG_8514) logerror("8514/A: Line Diagonal Step / Destination X write %04x\n",data);
+}
+
+/*
+9EE8h W(R/W): Short Stroke Vector Transfer Register (SHORT_STROKE)
+bit 0-3 Length of vector projected onto the major axis.
+ This is also the number of pixels drawn.
+ 4 Must be set for pixels to be written.
+ 5-7 VECDIR. The angle measured counter-clockwise from horizontal
+ right) at which the line is drawn,
+ 0 = 000 degrees
+ 1 = 045 degrees
+ 2 = 090 degrees
+ 3 = 135 degrees
+ 4 = 180 degrees
+ 5 = 225 degrees
+ 6 = 270 degrees
+ 7 = 315 degrees
+ 8-15 The lower 8 bits are duplicated in the upper 8 bits so two
+ short stroke vectors can be drawn with one command.
+Note: The upper byte must be written for the SSV command to be executed.
+ Thus if a byte is written to 9EE8h another byte must be written to
+ 9EE9h before execution starts. A single 16bit write will do.
+ If only one SSV is desired the other byte can be set to 0.
+ */
+void ibm8514a_device::ibm8514_wait_draw_ssv()
+{
+ UINT8 len = ibm8514.wait_vector_len;
+ UINT8 dir = ibm8514.wait_vector_dir;
+ bool draw = ibm8514.wait_vector_draw;
+ UINT8 count = ibm8514.wait_vector_count;
+ UINT32 offset;
+ int x;
+ int data_size;
+
+ switch(ibm8514.bus_size)
+ {
+ case 0:
+ data_size = 8;
+ break;
+ case 1:
+ data_size = 16;
+ break;
+ case 2:
+ data_size = 32;
+ break;
+ default:
+ data_size = 8;
+ break;
+ }
+
+ for(x=0;x<data_size;x++)
+ {
+ if(len > count)
+ {
+ if(ibm8514.state == IBM8514_DRAWING_SSV_1)
+ {
+ ibm8514.state = IBM8514_DRAWING_SSV_2;
+ ibm8514.wait_vector_len = (ibm8514.ssv & 0x0f00) >> 8;
+ ibm8514.wait_vector_dir = (ibm8514.ssv & 0xe000) >> 13;
+ ibm8514.wait_vector_draw = (ibm8514.ssv & 0x1000) ? true : false;
+ ibm8514.wait_vector_count = 0;
+ return;
+ }
+ else if(ibm8514.state == IBM8514_DRAWING_SSV_2)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.data_avail = false;
+ return;
+ }
+ }
+
+ if(ibm8514.state == IBM8514_DRAWING_SSV_1 || ibm8514.state == IBM8514_DRAWING_SSV_2)
+ {
+ offset = (ibm8514.curr_y * IBM8514_LINE_LENGTH) + ibm8514.curr_x;
+ if(draw)
+ ibm8514_write(offset,offset);
+ switch(dir)
+ {
+ case 0: // 0 degrees
+ ibm8514.curr_x++;
+ break;
+ case 1: // 45 degrees
+ ibm8514.curr_x++;
+ ibm8514.curr_y--;
+ break;
+ case 2: // 90 degrees
+ ibm8514.curr_y--;
+ break;
+ case 3: // 135 degrees
+ ibm8514.curr_y--;
+ ibm8514.curr_x--;
+ break;
+ case 4: // 180 degrees
+ ibm8514.curr_x--;
+ break;
+ case 5: // 225 degrees
+ ibm8514.curr_x--;
+ ibm8514.curr_y++;
+ break;
+ case 6: // 270 degrees
+ ibm8514.curr_y++;
+ break;
+ case 7: // 315 degrees
+ ibm8514.curr_y++;
+ ibm8514.curr_x++;
+ break;
+ }
+ }
+ }
+}
+
+void ibm8514a_device::ibm8514_draw_ssv(UINT8 data)
+{
+ UINT8 len = data & 0x0f;
+ UINT8 dir = (data & 0xe0) >> 5;
+ bool draw = (data & 0x10) ? true : false;
+
+ ibm8514_draw_vector(len,dir,draw);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_ssv_r)
+{
+ return ibm8514.ssv;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_ssv_w)
+{
+ ibm8514.ssv = data;
+
+ if(ibm8514.current_cmd & 0x0100)
+ {
+ ibm8514.state = IBM8514_DRAWING_SSV_1;
+ ibm8514.data_avail = true;
+ ibm8514.wait_vector_len = ibm8514.ssv & 0x0f;
+ ibm8514.wait_vector_dir = (ibm8514.ssv & 0xe0) >> 5;
+ ibm8514.wait_vector_draw = (ibm8514.ssv & 0x10) ? true : false;
+ ibm8514.wait_vector_count = 0;
+ return;
+ }
+
+ if(ibm8514.current_cmd & 0x1000) // byte sequence
+ {
+ ibm8514_draw_ssv(data & 0xff);
+ ibm8514_draw_ssv(data >> 8);
+ }
+ else
+ {
+ ibm8514_draw_ssv(data >> 8);
+ ibm8514_draw_ssv(data & 0xff);
+ }
+ if(LOG_8514) logerror("8514/A: Short Stroke Vector write %04x\n",data);
+}
+
+void ibm8514a_device::ibm8514_wait_draw_vector()
+{
+ UINT8 len = ibm8514.wait_vector_len;
+ UINT8 dir = ibm8514.wait_vector_dir;
+ bool draw = ibm8514.wait_vector_draw;
+ UINT8 count = ibm8514.wait_vector_count;
+ UINT32 offset;
+ UINT8 data_size = 0;
+ int x;
+
+ if(ibm8514.bus_size == 0) // 8-bit
+ data_size = 8;
+ if(ibm8514.bus_size == 1) // 16-bit
+ data_size = 16;
+ if(ibm8514.bus_size == 2) // 32-bit
+ data_size = 32;
+
+ for(x=0;x<data_size;x++)
+ {
+ if(len > count)
+ {
+ if(ibm8514.state == IBM8514_DRAWING_LINE)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.data_avail = false;
+ return;
+ }
+ }
+
+ if(ibm8514.state == IBM8514_DRAWING_LINE)
+ {
+ offset = (ibm8514.curr_y * IBM8514_LINE_LENGTH) + ibm8514.curr_x;
+ if(draw)
+ ibm8514_write(offset,offset);
+ switch(dir)
+ {
+ case 0: // 0 degrees
+ ibm8514.curr_x++;
+ break;
+ case 1: // 45 degrees
+ ibm8514.curr_x++;
+ ibm8514.curr_y--;
+ break;
+ case 2: // 90 degrees
+ ibm8514.curr_y--;
+ break;
+ case 3: // 135 degrees
+ ibm8514.curr_y--;
+ ibm8514.curr_x--;
+ break;
+ case 4: // 180 degrees
+ ibm8514.curr_x--;
+ break;
+ case 5: // 225 degrees
+ ibm8514.curr_x--;
+ ibm8514.curr_y++;
+ break;
+ case 6: // 270 degrees
+ ibm8514.curr_y++;
+ break;
+ case 7: // 315 degrees
+ ibm8514.curr_y++;
+ ibm8514.curr_x++;
+ break;
+ }
+ }
+ }
+}
+
+/*
+96E8h W(R/W): Major Axis Pixel Count/Rectangle Width Register (MAJ_AXIS_PCNT)
+bit 0-10 (911/924) RECTANGLE WIDTH/LINE PARAMETER MAX. For BITBLT and
+ rectangle commands this is the width of the area. For Line Drawing
+ this is the Bresenham constant dmajor in two's complement format.
+ (dmajor is the length of the line projected onto the major or
+ independent axis). Must be positive.
+ 0-11 (80x +) RECTANGLE WIDTH/LINE PARAMETER MAX. See above
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_width_r)
+{
+ return ibm8514.rect_width;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_width_w)
+{
+ ibm8514.rect_width = data & 0x1fff;
+ if(LOG_8514) logerror("8514/A: Major Axis Pixel Count / Rectangle Width write %04x\n",data);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_currentx_r)
+{
+ return ibm8514.curr_x;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_currentx_w)
+{
+ ibm8514.curr_x = data;
+ ibm8514.prev_x = data;
+ if(LOG_8514) logerror("8514/A: Current X set to %04x (%i)\n",data,ibm8514.curr_x);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_currenty_r)
+{
+ return ibm8514.curr_y;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_currenty_w)
+{
+ ibm8514.curr_y = data;
+ ibm8514.prev_y = data;
+ if(LOG_8514) logerror("8514/A: Current Y set to %04x (%i)\n",data,ibm8514.curr_y);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_fgcolour_r)
+{
+ return ibm8514.fgcolour;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_fgcolour_w)
+{
+ ibm8514.fgcolour = data;
+ if(LOG_8514) logerror("8514/A: Foreground Colour write %04x\n",data);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_bgcolour_r)
+{
+ return ibm8514.bgcolour;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_bgcolour_w)
+{
+ ibm8514.bgcolour = data;
+ if(LOG_8514) logerror("8514/A: Background Colour write %04x\n",data);
+}
+
+/*
+AEE8h W(R/W): Read Mask Register (RD_MASK)
+bit 0-7 (911/924) Read Mask affects the following commands: CMD_RECT,
+ CMD_BITBLT and reading data in Across Plane Mode.
+ Each bit set prevents the plane from being read.
+ 0-15 (801/5) Readmask. See above.
+ 0-31 (928 +) Readmask. See above. In 32 bits per pixel modes there are
+ two 16bit registers at this address. BEE8h index 0Eh bit 4 selects
+ which 16 bits are accessible and each access toggles to the other
+ 16 bits.
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_read_mask_r)
+{
+ return ibm8514.read_mask & 0xffff;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_read_mask_w)
+{
+ ibm8514.read_mask = (ibm8514.read_mask & 0xffff0000) | data;
+ if(LOG_8514) logerror("8514/A: Read Mask (Low) write = %08x\n",ibm8514.read_mask);
+}
+
+/*
+AAE8h W(R/W): Write Mask Register (WRT_MASK)
+bit 0-7 (911/924) Writemask. A plane can only be modified if the
+ corresponding bit is set.
+ 0-15 (801/5) Writemask. See above.
+ 0-31 (928 +) Writemask. See above. In 32 bits per pixel modes there are
+ two 16bit registers at this address. BEE8h index 0Eh bit 4 selects
+ which 16 bits are accessible and each access toggles to the other
+ 16 bits.
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_write_mask_r)
+{
+ return ibm8514.write_mask & 0xffff;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_write_mask_w)
+{
+ ibm8514.write_mask = (ibm8514.write_mask & 0xffff0000) | data;
+ if(LOG_8514) logerror("8514/A: Write Mask (Low) write = %08x\n",ibm8514.write_mask);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_multifunc_r )
+{
+ switch(ibm8514.multifunc_sel)
+ {
+ case 0:
+ return ibm8514.rect_height;
+ case 1:
+ return ibm8514.scissors_top;
+ case 2:
+ return ibm8514.scissors_left;
+ case 3:
+ return ibm8514.scissors_bottom;
+ case 4:
+ return ibm8514.scissors_right;
+ // TODO: remaining functions
+ default:
+ if(LOG_8514) logerror("8514/A: Unimplemented multifunction register %i selected\n",ibm8514.multifunc_sel);
+ return 0xff;
+ }
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_multifunc_w )
+{
+ switch(data & 0xf000)
+ {
+/*
+BEE8h index 00h W(R/W): Minor Axis Pixel Count Register (MIN_AXIS_PCNT).
+bit 0-10 (911/924) Rectangle Height. Height of BITBLT or rectangle command.
+ Actual height is one larger.
+ 0-11 (80x +) Rectangle Height. See above
+*/
+ case 0x0000:
+ ibm8514.rect_height = data & 0x0fff;
+ if(LOG_8514) logerror("8514/A: Minor Axis Pixel Count / Rectangle Height write %04x\n",data);
+ break;
+/*
+BEE8h index 01h W(R/W): Top Scissors Register (SCISSORS_T).
+bit 0-10 (911/924) Clipping Top Limit. Defines the upper bound of the
+ Clipping Rectangle (Lowest Y coordinate).
+ 0-11 (80x +) Clipping Top Limit. See above
+
+BEE8h index 02h W(R/W): Left Scissors Registers (SCISSORS_L).
+bit 0-10 (911,924) Clipping Left Limit. Defines the left bound of the
+ Clipping Rectangle (Lowest X coordinate).
+ 0-11 (80x +) Clipping Left Limit. See above.
+
+BEE8h index 03h W(R/W): Bottom Scissors Register (SCISSORS_B).
+bit 0-10 (911,924) Clipping Bottom Limit. Defines the bottom bound of the
+ Clipping Rectangle (Highest Y coordinate).
+ 0-11 (80x +) Clipping Bottom Limit. See above.
+
+BEE8h index 04h W(R/W): Right Scissors Register (SCISSORS_R).
+bit 0-10 (911,924) Clipping Right Limit. Defines the right bound of the
+ Clipping Rectangle (Highest X coordinate).
+ 0-11 (80x +) Clipping Bottom Limit. See above.
+ */
+ case 0x1000:
+ ibm8514.scissors_top = data & 0x0fff;
+ if(LOG_8514) logerror("S3: Scissors Top write %04x\n",data);
+ break;
+ case 0x2000:
+ ibm8514.scissors_left = data & 0x0fff;
+ if(LOG_8514) logerror("S3: Scissors Left write %04x\n",data);
+ break;
+ case 0x3000:
+ ibm8514.scissors_bottom = data & 0x0fff;
+ if(LOG_8514) logerror("S3: Scissors Bottom write %04x\n",data);
+ break;
+ case 0x4000:
+ ibm8514.scissors_right = data & 0x0fff;
+ if(LOG_8514) logerror("S3: Scissors Right write %04x\n",data);
+ break;
+/*
+BEE8h index 0Ah W(R/W): Pixel Control Register (PIX_CNTL).
+BIT 2 (911-928) Pack Data. If set image read data is a monochrome bitmap,
+ if clear it is a bitmap of the current pixel depth
+ 6-7 DT-EX-DRC. Select Mix Select.
+ 0 Foreground Mix is always used.
+ 1 use fixed pattern to decide which mix setting to use on a pixel
+ 2 CPU Data (Pixel Transfer register) determines the Mix register used.
+ 3 Video memory determines the Mix register used.
+ */
+ case 0xa000:
+ ibm8514.pixel_control = data;
+ if(LOG_8514) logerror("S3: Pixel control write %04x\n",data);
+ break;
+ case 0xe000:
+ ibm8514.multifunc_misc = data;
+ if(LOG_8514) logerror("S3: Multifunction Miscellaneous write %04x\n",data);
+ break;
+/*
+BEE8h index 0Fh W(W): Read Register Select Register (READ_SEL) (801/5,928)
+bit 0-2 (911-928) READ-REG-SEL. Read Register Select. Selects the register
+ that is actually read when a read of BEE8h happens. Each read of
+ BEE8h increments this register by one.
+ 0: Read will return contents of BEE8h index 0.
+ 1: Read will return contents of BEE8h index 1.
+ 2: Read will return contents of BEE8h index 2.
+ 3: Read will return contents of BEE8h index 3.
+ 4: Read will return contents of BEE8h index 4.
+ 5: Read will return contents of BEE8h index 0Ah.
+ 6: Read will return contents of BEE8h index 0Eh.
+ 7: Read will return contents of 9AE8h (Bits 13-15 will be 0).
+ 0-3 (864,964) READ-REG-SEL. See above plus:
+ 8: Read will return contents of 42E8h (Bits 12-15 will be 0)
+ 9: Read will return contents of 46E8h
+ 10: Read will return contents of BEE8h index 0Dh
+ */
+ case 0xf000:
+ ibm8514.multifunc_sel = data & 0x000f;
+ if(LOG_8514) logerror("S3: Multifunction select write %04x\n",data);
+ default:
+ if(LOG_8514) logerror("S3: Unimplemented multifunction register %i write %03x\n",data >> 12,data & 0x0fff);
+ }
+}
+
+void ibm8514a_device::ibm8514_wait_draw()
+{
+ int x, data_size = 8;
+ UINT32 off;
+
+ // the data in the pixel transfer register or written to VRAM masks the rectangle output
+ if(ibm8514.bus_size == 0) // 8-bit
+ data_size = 8;
+ if(ibm8514.bus_size == 1) // 16-bit
+ data_size = 16;
+ if(ibm8514.bus_size == 2) // 32-bit
+ data_size = 32;
+ off = 0;
+ off += (IBM8514_LINE_LENGTH * ibm8514.curr_y);
+ off += ibm8514.curr_x;
+ if(ibm8514.current_cmd & 0x02) // "across plane mode"
+ {
+ for(x=0;x<data_size;x++)
+ {
+ ibm8514_write(off,off);
+ if(ibm8514.current_cmd & 0x0020)
+ {
+ off++;
+ ibm8514.curr_x++;
+ if(ibm8514.curr_x > ibm8514.prev_x + ibm8514.rect_width)
+ {
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.src_x = 0;
+ if(ibm8514.current_cmd & 0x0080)
+ {
+ ibm8514.curr_y++;
+ if(ibm8514.curr_y > ibm8514.prev_y + ibm8514.rect_height)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.data_avail = false;
+ ibm8514.gpbusy = false;
+ }
+ }
+ else
+ {
+ ibm8514.curr_y--;
+ if(ibm8514.curr_y < ibm8514.prev_y - ibm8514.rect_height)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.data_avail = false;
+ ibm8514.gpbusy = false;
+ }
+ }
+ return;
+ }
+ }
+ else
+ {
+ off--;
+ ibm8514.curr_x--;
+ if(ibm8514.curr_x < ibm8514.prev_x - ibm8514.rect_width)
+ {
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.src_x = 0;
+ if(ibm8514.current_cmd & 0x0080)
+ {
+ ibm8514.curr_y++;
+ if(ibm8514.curr_y > ibm8514.prev_y + ibm8514.rect_height)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.data_avail = false;
+ }
+ }
+ else
+ {
+ ibm8514.curr_y--;
+ if(ibm8514.curr_y < ibm8514.prev_y - ibm8514.rect_height)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.data_avail = false;
+ }
+ }
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ // "through plane" mode (single pixel)
+ for(x=0;x<data_size;x+=8)
+ {
+ ibm8514_write(off,off);
+
+ if(ibm8514.current_cmd & 0x0020)
+ {
+ off++;
+ ibm8514.curr_x++;
+ if(ibm8514.curr_x > ibm8514.prev_x + ibm8514.rect_width)
+ {
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.src_x = 0;
+ if(ibm8514.current_cmd & 0x0080)
+ {
+ ibm8514.curr_y++;
+ if(ibm8514.curr_y > ibm8514.prev_y + ibm8514.rect_height)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.data_avail = false;
+ }
+ }
+ else
+ {
+ ibm8514.curr_y--;
+ if(ibm8514.curr_y < ibm8514.prev_y - ibm8514.rect_height)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.data_avail = false;
+ }
+ }
+ return;
+ }
+ }
+ else
+ {
+ off--;
+ ibm8514.curr_x--;
+ if(ibm8514.curr_x < ibm8514.prev_x - ibm8514.rect_width)
+ {
+ ibm8514.curr_x = ibm8514.prev_x;
+ ibm8514.src_x = 0;
+ if(ibm8514.current_cmd & 0x0080)
+ {
+ ibm8514.curr_y++;
+ if(ibm8514.curr_y > ibm8514.prev_y + ibm8514.rect_height)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.data_avail = false;
+ }
+ }
+ else
+ {
+ ibm8514.curr_y--;
+ if(ibm8514.curr_y < ibm8514.prev_y - ibm8514.rect_height)
+ {
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+ ibm8514.data_avail = false;
+ }
+ }
+ return;
+ }
+ }
+ }
+ }
+}
+
+/*
+B6E8h W(R/W): Background Mix Register (BKGD_MIX)
+bit 0-3 Background MIX (BACKMIX).
+ 00 not DST
+ 01 0 (false)
+ 02 1 (true)
+ 03 2 DST
+ 04 not SRC
+ 05 SRC xor DST
+ 06 not (SRC xor DST)
+ 07 SRC
+ 08 not (SRC and DST)
+ 09 (not SRC) or DST
+ 0A SRC or (not DST)
+ 0B SRC or DST
+ 0C SRC and DST
+ 0D SRC and (not DST)
+ 0E (not SRC) and DST
+ 0F not (SRC or DST)
+ DST is always the destination bitmap, bit SRC has four
+ possible sources selected by the BSS bits.
+ 5-6 Background Source Select (BSS)
+ 0 BSS is Background Color
+ 1 BSS is Foreground Color
+ 2 BSS is Pixel Data from the PIX_TRANS register (E2E8h)
+ 3 BSS is Bitmap Data (Source data from display buffer).
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_backmix_r)
+{
+ return ibm8514.bgmix;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_backmix_w)
+{
+ ibm8514.bgmix = data;
+ if(LOG_8514) logerror("8514/A: BG Mix write %04x\n",data);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_foremix_r)
+{
+ return ibm8514.fgmix;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_foremix_w)
+{
+ ibm8514.fgmix = data;
+ if(LOG_8514) logerror("8514/A: FG Mix write %04x\n",data);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_pixel_xfer_r)
+{
+ if(offset == 1)
+ return (ibm8514.pixel_xfer & 0xffff0000) >> 16;
+ else
+ return ibm8514.pixel_xfer & 0x0000ffff;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_pixel_xfer_w)
+{
+ if(offset == 1)
+ ibm8514.pixel_xfer = (ibm8514.pixel_xfer & 0x0000ffff) | (data << 16);
+ else
+ ibm8514.pixel_xfer = (ibm8514.pixel_xfer & 0xffff0000) | data;
+
+ if(ibm8514.state == IBM8514_DRAWING_RECT)
+ ibm8514_wait_draw();
+
+ if(ibm8514.state == IBM8514_DRAWING_SSV_1 || ibm8514.state == IBM8514_DRAWING_SSV_2)
+ ibm8514_wait_draw_ssv();
+
+ if(ibm8514.state == IBM8514_DRAWING_LINE)
+ ibm8514_wait_draw_vector();
+
+ if(LOG_8514) logerror("S3: Pixel Transfer = %08x\n",ibm8514.pixel_xfer);
+}
+
+READ8_MEMBER(s3_vga_device::mem_r)
+{
+ if (svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb32_en)
+ {
+ int data;
+ if(offset & 0x10000)
+ return 0;
+ data = 0;
+ if(vga.sequencer.data[4] & 0x8)
+ {
+ if((offset + (svga.bank_r*0x10000)) < vga.svga_intf.vram_size)
+ data = vga.memory[(offset + (svga.bank_r*0x10000))];
+ }
+ else
+ {
+ int i;
+
+ for(i=0;i<4;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ {
+ if((offset*4+i+(svga.bank_r*0x10000)) < vga.svga_intf.vram_size)
+ data |= vga.memory[(offset*4+i+(svga.bank_r*0x10000))];
+ }
+ }
+ }
+ return data;
+ }
+ if((offset + (svga.bank_r*0x10000)) < vga.svga_intf.vram_size)
+ return vga_device::mem_r(space,offset,mem_mask);
+ else
+ return 0xff;
+}
+
+WRITE8_MEMBER(s3_vga_device::mem_w)
+{
+ ibm8514a_device* dev = get_8514();
+ // bit 4 of CR53 enables memory-mapped I/O
+ // 0xA0000-0xA7fff maps to port 0xE2E8 (pixel transfer)
+ if(s3.cr53 & 0x10)
+ {
+ if(offset < 0x8000)
+ {
+ // pass through to the pixel transfer register (DirectX 5 wants this)
+ if(dev->ibm8514.bus_size == 0)
+ {
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xffffff00) | data;
+ dev->ibm8514_wait_draw();
+ }
+ if(dev->ibm8514.bus_size == 1)
+ {
+ switch(offset & 0x0001)
+ {
+ case 0:
+ default:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xffffff00) | data;
+ break;
+ case 1:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xffff00ff) | (data << 8);
+ dev->ibm8514_wait_draw();
+ break;
+ }
+ }
+ if(dev->ibm8514.bus_size == 2)
+ {
+ switch(offset & 0x0003)
+ {
+ case 0:
+ default:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xffffff00) | data;
+ break;
+ case 1:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xffff00ff) | (data << 8);
+ break;
+ case 2:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xff00ffff) | (data << 16);
+ break;
+ case 3:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0x00ffffff) | (data << 24);
+ dev->ibm8514_wait_draw();
+ break;
+ }
+ }
+ return;
+ }
+ switch(offset)
+ {
+ case 0x8100:
+ case 0x82e8:
+ dev->ibm8514.curr_y = (dev->ibm8514.curr_y & 0xff00) | data;
+ dev->ibm8514.prev_y = (dev->ibm8514.prev_y & 0xff00) | data;
+ break;
+ case 0x8101:
+ case 0x82e9:
+ dev->ibm8514.curr_y = (dev->ibm8514.curr_y & 0x00ff) | (data << 8);
+ dev->ibm8514.prev_y = (dev->ibm8514.prev_y & 0x00ff) | (data << 8);
+ break;
+ case 0x8102:
+ case 0x86e8:
+ dev->ibm8514.curr_x = (dev->ibm8514.curr_x & 0xff00) | data;
+ dev->ibm8514.prev_x = (dev->ibm8514.prev_x & 0xff00) | data;
+ break;
+ case 0x8103:
+ case 0x86e9:
+ dev->ibm8514.curr_x = (dev->ibm8514.curr_x & 0x00ff) | (data << 8);
+ dev->ibm8514.prev_x = (dev->ibm8514.prev_x & 0x00ff) | (data << 8);
+ break;
+ case 0x8108:
+ case 0x8ae8:
+ dev->ibm8514.line_axial_step = (dev->ibm8514.line_axial_step & 0xff00) | data;
+ dev->ibm8514.dest_y = (dev->ibm8514.dest_y & 0xff00) | data;
+ break;
+ case 0x8109:
+ case 0x8ae9:
+ dev->ibm8514.line_axial_step = (dev->ibm8514.line_axial_step & 0x00ff) | ((data & 0x3f) << 8);
+ dev->ibm8514.dest_y = (dev->ibm8514.dest_y & 0x00ff) | (data << 8);
+ break;
+ case 0x810a:
+ case 0x8ee8:
+ dev->ibm8514.line_diagonal_step = (dev->ibm8514.line_diagonal_step & 0xff00) | data;
+ dev->ibm8514.dest_x = (dev->ibm8514.dest_x & 0xff00) | data;
+ break;
+ case 0x810b:
+ case 0x8ee9:
+ dev->ibm8514.line_diagonal_step = (dev->ibm8514.line_diagonal_step & 0x00ff) | ((data & 0x3f) << 8);
+ dev->ibm8514.dest_x = (dev->ibm8514.dest_x & 0x00ff) | (data << 8);
+ break;
+ case 0x8118:
+ case 0x9ae8:
+ s3.mmio_9ae8 = (s3.mmio_9ae8 & 0xff00) | data;
+ break;
+ case 0x8119:
+ case 0x9ae9:
+ s3.mmio_9ae8 = (s3.mmio_9ae8 & 0x00ff) | (data << 8);
+ dev->ibm8514_cmd_w(space,0,s3.mmio_9ae8,0xffff);
+ break;
+ case 0x8120:
+ case 0xa2e8:
+ dev->ibm8514.bgcolour = (dev->ibm8514.bgcolour & 0xff00) | data;
+ break;
+ case 0x8121:
+ case 0xa2e9:
+ dev->ibm8514.bgcolour = (dev->ibm8514.bgcolour & 0x00ff) | (data << 8);
+ break;
+ case 0x8124:
+ case 0xa6e8:
+ dev->ibm8514.fgcolour = (dev->ibm8514.fgcolour & 0xff00) | data;
+ break;
+ case 0x8125:
+ case 0xa6e9:
+ dev->ibm8514.fgcolour = (dev->ibm8514.fgcolour & 0x00ff) | (data << 8);
+ break;
+ case 0x8128:
+ case 0xaae8:
+ dev->ibm8514.write_mask = (dev->ibm8514.write_mask & 0xff00) | data;
+ break;
+ case 0x8129:
+ case 0xaae9:
+ dev->ibm8514.write_mask = (dev->ibm8514.write_mask & 0x00ff) | (data << 8);
+ break;
+ case 0x812c:
+ case 0xaee8:
+ dev->ibm8514.read_mask = (dev->ibm8514.read_mask & 0xff00) | data;
+ break;
+ case 0x812d:
+ case 0xaee9:
+ dev->ibm8514.read_mask = (dev->ibm8514.read_mask & 0x00ff) | (data << 8);
+ break;
+ case 0xb6e8:
+ case 0x8134:
+ dev->ibm8514.bgmix = (dev->ibm8514.bgmix & 0xff00) | data;
+ break;
+ case 0x8135:
+ case 0xb6e9:
+ dev->ibm8514.bgmix = (dev->ibm8514.bgmix & 0x00ff) | (data << 8);
+ break;
+ case 0x8136:
+ case 0xbae8:
+ dev->ibm8514.fgmix = (dev->ibm8514.fgmix & 0xff00) | data;
+ break;
+ case 0x8137:
+ case 0xbae9:
+ dev->ibm8514.fgmix = (dev->ibm8514.fgmix & 0x00ff) | (data << 8);
+ break;
+ case 0x8138:
+ dev->ibm8514.scissors_top = (dev->ibm8514.scissors_top & 0xff00) | data;
+ break;
+ case 0x8139:
+ dev->ibm8514.scissors_top = (dev->ibm8514.scissors_top & 0x00ff) | (data << 8);
+ break;
+ case 0x813a:
+ dev->ibm8514.scissors_left = (dev->ibm8514.scissors_left & 0xff00) | data;
+ break;
+ case 0x813b:
+ dev->ibm8514.scissors_left = (dev->ibm8514.scissors_left & 0x00ff) | (data << 8);
+ break;
+ case 0x813c:
+ dev->ibm8514.scissors_bottom = (dev->ibm8514.scissors_bottom & 0xff00) | data;
+ break;
+ case 0x813d:
+ dev->ibm8514.scissors_bottom = (dev->ibm8514.scissors_bottom & 0x00ff) | (data << 8);
+ break;
+ case 0x813e:
+ dev->ibm8514.scissors_right = (dev->ibm8514.scissors_right & 0xff00) | data;
+ break;
+ case 0x813f:
+ dev->ibm8514.scissors_right = (dev->ibm8514.scissors_right & 0x00ff) | (data << 8);
+ break;
+ case 0x8140:
+ dev->ibm8514.pixel_control = (dev->ibm8514.pixel_control & 0xff00) | data;
+ break;
+ case 0x8141:
+ dev->ibm8514.pixel_control = (dev->ibm8514.pixel_control & 0x00ff) | (data << 8);
+ break;
+ case 0x8146:
+ dev->ibm8514.multifunc_sel = (dev->ibm8514.multifunc_sel & 0xff00) | data;
+ break;
+ case 0x8148:
+ dev->ibm8514.rect_height = (dev->ibm8514.rect_height & 0xff00) | data;
+ break;
+ case 0x8149:
+ dev->ibm8514.rect_height = (dev->ibm8514.rect_height & 0x00ff) | (data << 8);
+ break;
+ case 0x814a:
+ dev->ibm8514.rect_width = (dev->ibm8514.rect_width & 0xff00) | data;
+ break;
+ case 0x814b:
+ dev->ibm8514.rect_width = (dev->ibm8514.rect_width & 0x00ff) | (data << 8);
+ break;
+ case 0x8150:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xffffff00) | data;
+ if(dev->ibm8514.state == IBM8514_DRAWING_RECT)
+ dev->ibm8514_wait_draw();
+ break;
+ case 0x8151:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xffff00ff) | (data << 8);
+ if(dev->ibm8514.state == IBM8514_DRAWING_RECT)
+ dev->ibm8514_wait_draw();
+ break;
+ case 0x8152:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xff00ffff) | (data << 16);
+ if(dev->ibm8514.state == IBM8514_DRAWING_RECT)
+ dev->ibm8514_wait_draw();
+ break;
+ case 0x8153:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0x00ffffff) | (data << 24);
+ if(dev->ibm8514.state == IBM8514_DRAWING_RECT)
+ dev->ibm8514_wait_draw();
+ break;
+ case 0xbee8:
+ s3.mmio_bee8 = (s3.mmio_bee8 & 0xff00) | data;
+ break;
+ case 0xbee9:
+ s3.mmio_bee8 = (s3.mmio_bee8 & 0x00ff) | (data << 8);
+ dev->ibm8514_multifunc_w(space,0,s3.mmio_bee8,0xffff);
+ break;
+ case 0x96e8:
+ s3.mmio_96e8 = (s3.mmio_96e8 & 0xff00) | data;
+ break;
+ case 0x96e9:
+ s3.mmio_96e8 = (s3.mmio_96e8 & 0x00ff) | (data << 8);
+ dev->ibm8514_width_w(space,0,s3.mmio_96e8,0xffff);
+ break;
+ case 0xe2e8:
+ dev->ibm8514.pixel_xfer = (dev->ibm8514.pixel_xfer & 0xffffff00) | data;
+ dev->ibm8514_wait_draw();
+ break;
+ default:
+ if(LOG_8514) logerror("S3: MMIO offset %05x write %02x\n",offset+0xa0000,data);
+ }
+ return;
+ }
+
+ if (svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb32_en)
+ {
+ //printf("%08x %02x (%02x %02x) %02X\n",offset,data,vga.sequencer.map_mask,svga.bank_w,(vga.sequencer.data[4] & 0x08));
+ if(offset & 0x10000)
+ return;
+ if(vga.sequencer.data[4] & 0x8)
+ {
+ if((offset + (svga.bank_w*0x10000)) < vga.svga_intf.vram_size)
+ vga.memory[(offset + (svga.bank_w*0x10000))] = data;
+ }
+ else
+ {
+ int i;
+ for(i=0;i<4;i++)
+ {
+ if(vga.sequencer.map_mask & 1 << i)
+ {
+ if((offset*4+i+(svga.bank_w*0x10000)) < vga.svga_intf.vram_size)
+ vga.memory[(offset*4+i+(svga.bank_w*0x10000))] = data;
+ }
+ }
+ }
+ return;
+ }
+
+ if((offset + (svga.bank_w*0x10000)) < vga.svga_intf.vram_size)
+ vga_device::mem_w(space,offset,data,mem_mask);
+}
+
+/******************************************
+
+gamtor.c implementation (TODO: identify the video card)
+
+******************************************/
+
+READ8_MEMBER(gamtor_vga_device::mem_r)
+{
+ return vga.memory[offset];
+}
+
+WRITE8_MEMBER(gamtor_vga_device::mem_w)
+{
+ vga.memory[offset] = data;
+}
+
+
+READ8_MEMBER(gamtor_vga_device::port_03b0_r)
+{
+ UINT8 res;
+
+ switch(offset)
+ {
+ default:
+ res = vga_device::port_03b0_r(space,offset ^ 3,mem_mask);
+ break;
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(gamtor_vga_device::port_03b0_w)
+{
+ switch(offset)
+ {
+ default:
+ vga_device::port_03b0_w(space,offset ^ 3,data,mem_mask);
+ break;
+ }
+}
+
+READ8_MEMBER(gamtor_vga_device::port_03c0_r)
+{
+ UINT8 res;
+
+ switch(offset)
+ {
+ default:
+ res = vga_device::port_03c0_r(space,offset ^ 3,mem_mask);
+ break;
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(gamtor_vga_device::port_03c0_w)
+{
+ switch(offset)
+ {
+ default:
+ vga_device::port_03c0_w(space,offset ^ 3,data,mem_mask);
+ break;
+ }
+}
+
+READ8_MEMBER(gamtor_vga_device::port_03d0_r)
+{
+ UINT8 res;
+
+ switch(offset)
+ {
+ default:
+ res = vga_device::port_03d0_r(space,offset ^ 3,mem_mask);
+ break;
+ }
+
+ return res;
+}
+
+WRITE8_MEMBER(gamtor_vga_device::port_03d0_w)
+{
+ switch(offset)
+ {
+ default:
+ vga_device::port_03d0_w(space,offset ^ 3,data,mem_mask);
+ break;
+ }
+}
+
+UINT16 ati_vga_device::offset()
+{
+ //popmessage("Offset: %04x %s %s %s %s",vga.crtc.offset,vga.crtc.dw?"DW":"--",vga.crtc.word_mode?"BYTE":"WORD",(ati.ext_reg[0x33] & 0x40) ? "PEL" : "---",(ati.ext_reg[0x30] & 0x20) ? "256" : "---");
+ if(ati.ext_reg[0x30] & 0x20) // likely wrong, gets 640x400/480 SVGA and tweaked 256 colour modes displaying correctly in Fractint.
+ return vga_device::offset() << 3;
+ if(ati.ext_reg[0x33] & 0x40)
+ return vga_device::offset() << 2;
+ return vga_device::offset();
+}
+
+
+void ati_vga_device::ati_define_video_mode()
+{
+ int clock;
+ UINT8 clock_type;
+ int div = ((ati.ext_reg[0x38] & 0xc0) >> 6) + 1;
+ int divisor = 1;
+
+ svga.rgb8_en = 0;
+ svga.rgb15_en = 0;
+ svga.rgb16_en = 0;
+ svga.rgb32_en = 0;
+
+ if(ati.ext_reg[0x30] & 0x20)
+ svga.rgb8_en = 1;
+
+ clock_type = ((ati.ext_reg[0x3e] & 0x10)>>1) | ((ati.ext_reg[0x39] & 0x02)<<1) | ((vga.miscellaneous_output & 0x0c)>>2);
+ switch(clock_type)
+ {
+ case 0:
+ clock = XTAL_42_9545MHz;
+ break;
+ case 1:
+ clock = 48771000;
+ break;
+ case 2:
+ clock = 16657000;
+ break;
+ case 3:
+ clock = XTAL_36MHz;
+ break;
+ case 4:
+ clock = 50350000;
+ break;
+ case 5:
+ clock = 56640000;
+ break;
+ case 6:
+ clock = 28322000;
+ break;
+ case 7:
+ clock = 44900000;
+ break;
+ case 8:
+ clock = 30240000;
+ break;
+ case 9:
+ clock = XTAL_32MHz;
+ break;
+ case 10:
+ clock = 37500000;
+ break;
+ case 11:
+ clock = 39000000;
+ break;
+ case 12:
+ clock = XTAL_40MHz;
+ break;
+ case 13:
+ clock = 56644000;
+ break;
+ case 14:
+ clock = 75000000;
+ break;
+ case 15:
+ clock = 65000000;
+ break;
+ default:
+ clock = XTAL_42_9545MHz;
+ logerror("Invalid dot clock %i selected.\n",clock_type);
+ }
+// logerror("ATI: Clock select type %i (%iHz / %i)\n",clock_type,clock,div);
+ recompute_params_clock(divisor,clock / div);
+}
+
+READ8_MEMBER(ati_vga_device::mem_r)
+{
+ if(svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en)
+ {
+ if(ati.ext_reg[0x3d] & 0x04)
+ {
+ offset &= 0x1ffff;
+ return vga.memory[(offset+svga.bank_r*0x20000) % vga.svga_intf.vram_size];
+ }
+ else
+ {
+ offset &= 0xffff;
+ return vga.memory[(offset+svga.bank_r*0x10000) % vga.svga_intf.vram_size];
+ }
+ }
+
+ return vga_device::mem_r(space,offset,mem_mask);
+}
+
+WRITE8_MEMBER(ati_vga_device::mem_w)
+{
+ if(svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en)
+ {
+ if(ati.ext_reg[0x3d] & 0x04)
+ {
+ offset &= 0x1ffff;
+ vga.memory[(offset+svga.bank_w*0x20000) % vga.svga_intf.vram_size] = data;
+ }
+ else
+ {
+ offset &= 0xffff;
+ vga.memory[(offset+svga.bank_w*0x10000) % vga.svga_intf.vram_size] = data;
+ }
+ }
+ else
+ vga_device::mem_w(space,offset,data,mem_mask);
+}
+
+
+READ8_MEMBER(ati_vga_device::ati_port_ext_r)
+{
+ UINT8 ret = 0xff;
+
+ switch(offset)
+ {
+ case 0:
+ break;
+ case 1:
+ switch(ati.ext_reg_select)
+ {
+ case 0x20:
+ ret = 0x10; // 512kB memory
+ break;
+ case 0x28: // Vertical line counter (high)
+ ret = (machine().first_screen()->vpos() >> 8) & 0x03;
+ break;
+ case 0x29: // Vertical line counter (low)
+ ret = machine().first_screen()->vpos() & 0xff; // correct?
+ break;
+ case 0x2a:
+ ret = ati.vga_chip_id; // Chip revision (6 for the 28800-6, 5 for the 28800-5)
+ break;
+ case 0x37:
+ {
+ eeprom_serial_93cxx_device* eep = subdevice<eeprom_serial_93cxx_device>("ati_eeprom");
+ ret = 0x00;
+ ret |= eep->do_read() << 3;
+ }
+ break;
+ case 0x3d:
+ ret = ati.ext_reg[ati.ext_reg_select] & 0x0f;
+ ret |= 0x10; // EGA DIP switch emulation
+ break;
+ default:
+ ret = ati.ext_reg[ati.ext_reg_select];
+ logerror("ATI: Extended VGA register 0x01CE index %02x read\n",ati.ext_reg_select);
+ }
+ break;
+ }
+ return ret;
+}
+
+WRITE8_MEMBER(ati_vga_device::ati_port_ext_w)
+{
+ switch(offset)
+ {
+ case 0:
+ ati.ext_reg_select = data & 0x3f;
+ break;
+ case 1:
+ ati.ext_reg[ati.ext_reg_select] = data;
+ switch(ati.ext_reg_select)
+ {
+ case 0x23:
+ vga.crtc.start_addr_latch = (vga.crtc.start_addr_latch & 0xfffdffff) | ((data & 0x10) << 13);
+ vga.crtc.cursor_addr = (vga.crtc.cursor_addr & 0xfffdffff) | ((data & 0x08) << 14);
+ logerror("ATI: ATI23 write %02x\n",data);
+ break;
+ case 0x2d:
+ if(data & 0x08)
+ {
+ vga.crtc.horz_total = (vga.crtc.horz_total & 0x00ff) | (data & 0x01) << 8;
+ // bit 1 = bit 8 of horizontal blank start
+ // bit 2 = bit 8 of horizontal retrace start
+ }
+ logerror("ATI: ATI2D (extensions) write %02x\n",data);
+ break;
+ case 0x30:
+ vga.crtc.start_addr_latch = (vga.crtc.start_addr_latch & 0xfffeffff) | ((data & 0x40) << 10);
+ vga.crtc.cursor_addr = (vga.crtc.cursor_addr & 0xfffeffff) | ((data & 0x04) << 14);
+ logerror("ATI: ATI30 write %02x\n",data);
+ break;
+ case 0x32: // memory page select
+ if(ati.ext_reg[0x3e] & 0x08)
+ {
+ svga.bank_r = ((data & 0x01) << 3) | ((data & 0xe0) >> 5);
+ svga.bank_w = ((data & 0x1e) >> 1);
+ }
+ else
+ {
+ svga.bank_r = ((data & 0x1e) >> 1);
+ svga.bank_w = ((data & 0x1e) >> 1);
+ }
+ //logerror("ATI: Memory Page Select write %02x (read: %i write %i)\n",data,svga.bank_r,svga.bank_w);
+ break;
+ case 0x33: // EEPROM
+ if(data & 0x04)
+ {
+ eeprom_serial_93cxx_device* eep = subdevice<eeprom_serial_93cxx_device>("ati_eeprom");
+ if(eep != NULL)
+ {
+ eep->di_write((data & 0x01) ? ASSERT_LINE : CLEAR_LINE);
+ eep->clk_write((data & 0x02) ? ASSERT_LINE : CLEAR_LINE);
+ eep->cs_write((data & 0x08) ? ASSERT_LINE : CLEAR_LINE);
+ }
+ }
+ else
+ logerror("ATI: ATI33 write %02x\n",data);
+ break;
+ default:
+ logerror("ATI: Extended VGA register 0x01CE index %02x write %02x\n",ati.ext_reg_select,data);
+ }
+ break;
+ }
+ ati_define_video_mode();
+}
+
+/*
+02E8h W(R): Display Status Register
+bit 0 SENSE is the result of a wired-OR of 3 comparators, one
+ for each of the RGB video signal.
+ By programming the RAMDAC for various values
+ and patterns and then reading the SENSE, the monitor type
+ (color, monochrome or none) can be determined.
+ 1 VBLANK. Vertical Blank State
+ If Vertical Blank is active this bit is set.
+ 2 HORTOG. Horizontal Toggle
+ This bit toggles every time a HSYNC pulse starts
+ 3-15 Reserved(0)
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_status_r)
+{
+ return m_vga->vga_vblank() << 1;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_htotal_w)
+{
+ ibm8514.htotal = data & 0x01ff;
+ //vga.crtc.horz_total = data & 0x01ff;
+ if(LOG_8514) logerror("8514/A: Horizontal total write %04x\n",data);
+}
+
+/*
+42E8h W(R): Subsystem Status Register (SUBSYS_STAT)
+bit 0-3 Interrupt requests. These bits show the state of internal interrupt
+ requests. An interrupt will only occur if the corresponding bit(s)
+ in SUBSYS_CNTL is set. Interrupts can only be reset by writing a 1
+ to the corresponding Interrupt Clear bit in SUBSYS_CNTL.
+ Bit 0: VBLNKFLG
+ 1: PICKFLAG
+ 2: INVALIDIO
+ 3: GPIDLE
+ 4-6 MONITORID.
+ 1: IBM 8507 (1024x768) Monochrome
+ 2: IBM 8514 (1024x768) Color
+ 5: IBM 8503 (640x480) Monochrome
+ 6: IBM 8512/13 (640x480) Color
+ 7 8PLANE.
+ (CT82c480) This bit is latched on reset from pin P4D7.
+ 8-11 CHIP_REV. Chip revision number.
+ 12-15 (CT82c480) CHIP_ID. 0=CT 82c480.
+ */
+READ16_MEMBER(ibm8514a_device::ibm8514_substatus_r)
+{
+ // TODO:
+ if(m_vga->vga_vblank() != 0) // not correct, but will do for now
+ ibm8514.substatus |= 0x01;
+ return ibm8514.substatus;
+}
+
+/*
+42E8h W(W): Subsystem Control Register (SUBSYS_CNTL)
+bit 0-3 Interrupt Reset. Write 1 to a bit to reset the interrupt.
+ Bit 0 RVBLNKFLG Write 1 to reset Vertical Blank interrupt.
+ 1 RPICKFLAG Write 1 to reset PICK interrupt.
+ 2 RINVALIDIO Write 1 to reset Queue Overflow/Data
+ Underflow interrupt.
+ 3 RGPIDLE Write 1 to reset GPIDLE interrupt.
+ 4-7 Reserved(0)
+ 8 IBLNKFLG. If set Vertical Blank Interrupts are enabled.
+ 9 IPICKFLAG. If set PICK Interrupts are enabled.
+ 10 IINVALIDIO. If set Queue Overflow/Data Underflow Interrupts are
+ enabled.
+ 11 IGPIDLE. If set Graphics Engine Idle Interrupts are enabled.
+ 12-13 CHPTEST. Used for chip testing.
+ 14-15 Graphics Processor Control (GPCTRL).
+ */
+WRITE16_MEMBER(ibm8514a_device::ibm8514_subcontrol_w)
+{
+ ibm8514.subctrl = data;
+ ibm8514.substatus &= ~(data & 0x0f); // reset interrupts
+// if(LOG_8514) logerror("8514/A: Subsystem control write %04x\n",data);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_subcontrol_r)
+{
+ return ibm8514.subctrl;
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_htotal_r)
+{
+ return ibm8514.htotal;
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_vtotal_r)
+{
+ return ibm8514.vtotal;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_vtotal_w)
+{
+ ibm8514.vtotal = data;
+// vga.crtc.vert_total = data;
+ if(LOG_8514) logerror("8514/A: Vertical total write %04x\n",data);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_vdisp_r)
+{
+ return ibm8514.vdisp;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_vdisp_w)
+{
+ ibm8514.vdisp = data;
+// vga.crtc.vert_disp_end = data >> 3;
+ if(LOG_8514) logerror("8514/A: Vertical Displayed write %04x\n",data);
+}
+
+READ16_MEMBER(ibm8514a_device::ibm8514_vsync_r)
+{
+ return ibm8514.vsync;
+}
+
+WRITE16_MEMBER(ibm8514a_device::ibm8514_vsync_w)
+{
+ ibm8514.vsync = data;
+ if(LOG_8514) logerror("8514/A: Vertical Sync write %04x\n",data);
+}
+
+void ibm8514a_device::enabled()
+{
+ ibm8514.state = IBM8514_IDLE;
+ ibm8514.gpbusy = false;
+}
+
+READ16_MEMBER(mach8_device::mach8_ec0_r)
+{
+ return ibm8514.ec0;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_ec0_w)
+{
+ ibm8514.ec0 = data;
+ if(LOG_8514) logerror("8514/A: Extended configuration 0 write %04x\n",data);
+}
+
+READ16_MEMBER(mach8_device::mach8_ec1_r)
+{
+ return ibm8514.ec1;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_ec1_w)
+{
+ ibm8514.ec1 = data;
+ if(LOG_8514) logerror("8514/A: Extended configuration 1 write %04x\n",data);
+}
+
+READ16_MEMBER(mach8_device::mach8_ec2_r)
+{
+ return ibm8514.ec2;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_ec2_w)
+{
+ ibm8514.ec2 = data;
+ if(LOG_8514) logerror("8514/A: Extended configuration 2 write %04x\n",data);
+}
+
+READ16_MEMBER(mach8_device::mach8_ec3_r)
+{
+ return ibm8514.ec3;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_ec3_w)
+{
+ ibm8514.ec3 = data;
+ if(LOG_8514) logerror("8514/A: Extended configuration 3 write %04x\n",data);
+}
+
+READ16_MEMBER(mach8_device::mach8_ext_fifo_r)
+{
+ return 0x00; // for now, report all FIFO slots at free
+}
+
+WRITE16_MEMBER(mach8_device::mach8_linedraw_index_w)
+{
+ mach8.linedraw = data & 0x07;
+ if(LOG_8514) logerror("Mach8: Line Draw Index write %04x\n",data);
+}
+
+READ16_MEMBER(mach8_device::mach8_bresenham_count_r)
+{
+ return ibm8514.rect_width & 0x1fff;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_bresenham_count_w)
+{
+ ibm8514.rect_width = data & 0x1fff;
+ if(LOG_8514) logerror("Mach8: Bresenham count write %04x\n",data);
+}
+
+READ16_MEMBER(mach8_device::mach8_linedraw_r)
+{
+ return 0xff;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_linedraw_w)
+{
+ // TODO: actually draw the lines
+ switch(mach8.linedraw)
+ {
+ case 0: // Set current X
+ ibm8514.curr_x = data;
+ mach8.linedraw++;
+ break;
+ case 1: // Set current Y
+ ibm8514.curr_y = data;
+ mach8.linedraw++;
+ break;
+ case 2: // Line end X
+ ibm8514.curr_x = data;
+ mach8.linedraw++;
+ break;
+ case 3: // Line end Y
+ ibm8514.curr_y = data;
+ mach8.linedraw = 2;
+ break;
+ case 4: // Set current X
+ ibm8514.curr_x = data;
+ mach8.linedraw++;
+ break;
+ case 5: // Set current Y
+ ibm8514.curr_y = data;
+ mach8.linedraw = 4;
+ break;
+ }
+ logerror("ATI: Linedraw register write %04x, mode %i\n",data,mach8.linedraw);
+}
+
+READ16_MEMBER(mach8_device::mach8_sourcex_r)
+{
+ return ibm8514.dest_x;
+}
+
+READ16_MEMBER(mach8_device::mach8_sourcey_r)
+{
+ return ibm8514.dest_y;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_ext_leftscissor_w)
+{
+ // TODO
+}
+
+WRITE16_MEMBER(mach8_device::mach8_ext_topscissor_w)
+{
+ // TODO
+}
+
+READ16_MEMBER(mach8_device::mach8_scratch0_r)
+{
+ return mach8.scratch0;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_scratch0_w)
+{
+ mach8.scratch0 = data;
+ if(LOG_8514) logerror("Mach8: Scratch Pad 0 write %04x\n",data);
+}
+
+READ16_MEMBER(mach8_device::mach8_scratch1_r)
+{
+ return mach8.scratch1;
+}
+
+WRITE16_MEMBER(mach8_device::mach8_scratch1_w)
+{
+ mach8.scratch1 = data;
+ if(LOG_8514) logerror("Mach8: Scratch Pad 1 write %04x\n",data);
+}
+
+/*
+12EEh W(R): Configuration Status 1 Register (Mach8)
+bit 0 CLK_MODE. Set to use clock chip, clear to use crystals.
+ 1 BUS_16. Set for 16bit bus, clear for 8bit bus
+ 2 MC_BUS. Set for MicroChannel bus, clear for ISA/EISA bus
+ 3 EEPROM_ENA. EEPROM enabled if set
+ 4 DRAM_ENA. Set for DRAM, clear for VRAM.
+ 5-6 MEM_INSTALLED. Video memory. 0: 512K, 1: 1024K
+ 7 ROM_ENA. Set is ROM is enabled
+ 8 ROM_PAGE_ENA. Set if ROM paging enabled
+ 9-15 ROM_LOCATION. If bit 2 and 3 are 0 the ROM will be at this location:
+ 0: C000h, 1: C080h, 2: C100h, .. 127: FF80h (unlikely)
+ */
+READ16_MEMBER(mach8_device::mach8_config1_r)
+{
+ return 0x0082;
+}
+
+/*
+16EEh (R): Configuration Status 2 Register (Mach8)
+bit 0 SHARE_CLOCK. If set the Mach8 shares clock with the VGA
+ 1 HIRES_BOOT. Boot in hi-res mode if set
+ 2 EPROM_16_ENA. Adapter configured for 16bit ROM if set
+ 3 WRITE_PER_BIT. Write masked VRAM operations supported if set
+ 4 FLASH_ENA. Flash page writes supported if set
+ */
+READ16_MEMBER(mach8_device::mach8_config2_r)
+{
+ return 0x0002;
+}
diff --git a/src/devices/video/pc_vga.h b/src/devices/video/pc_vga.h
new file mode 100644
index 00000000000..ee0504a95e1
--- /dev/null
+++ b/src/devices/video/pc_vga.h
@@ -0,0 +1,705 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods, Peter Trauner, Angelo Salese
+/***************************************************************************
+
+ pc_vga.h
+
+ PC standard VGA adaptor
+
+***************************************************************************/
+
+#ifndef PC_VGA_H
+#define PC_VGA_H
+
+MACHINE_CONFIG_EXTERN( pcvideo_vga );
+MACHINE_CONFIG_EXTERN( pcvideo_trident_vga );
+MACHINE_CONFIG_EXTERN( pcvideo_gamtor_vga );
+MACHINE_CONFIG_EXTERN( pcvideo_s3_vga );
+
+enum
+{
+ SCREEN_OFF = 0,
+ TEXT_MODE,
+ VGA_MODE,
+ EGA_MODE,
+ CGA_MODE,
+ MONO_MODE,
+ RGB8_MODE,
+ RGB15_MODE,
+ RGB16_MODE,
+ RGB24_MODE,
+ RGB32_MODE
+};
+
+// ======================> vga_device
+
+class vga_device : public device_t
+{
+ friend class ibm8514a_device;
+
+public:
+ // construction/destruction
+ vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ vga_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ virtual void zero();
+ virtual UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ virtual READ8_MEMBER(port_03b0_r);
+ virtual WRITE8_MEMBER(port_03b0_w);
+ virtual READ8_MEMBER(port_03c0_r);
+ virtual WRITE8_MEMBER(port_03c0_w);
+ virtual READ8_MEMBER(port_03d0_r);
+ virtual WRITE8_MEMBER(port_03d0_w);
+ virtual READ8_MEMBER(mem_r);
+ virtual WRITE8_MEMBER(mem_w);
+ virtual READ8_MEMBER(mem_linear_r);
+ virtual WRITE8_MEMBER(mem_linear_w);
+ virtual TIMER_CALLBACK_MEMBER(vblank_timer_cb);
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ void vga_vh_text(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void vga_vh_ega(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void vga_vh_vga(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void vga_vh_cga(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void vga_vh_mono(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ virtual UINT8 pc_vga_choosevideomode();
+ void recompute_params_clock(int divisor, int xtal);
+ UINT8 crtc_reg_read(UINT8 index);
+ void recompute_params();
+ void crtc_reg_write(UINT8 index, UINT8 data);
+ void seq_reg_write(UINT8 index, UINT8 data);
+ UINT8 vga_vblank();
+ READ8_MEMBER(vga_crtc_r);
+ WRITE8_MEMBER(vga_crtc_w);
+ UINT8 gc_reg_read(UINT8 index);
+ void attribute_reg_write(UINT8 index, UINT8 data);
+ void gc_reg_write(UINT8 index,UINT8 data);
+ virtual UINT16 offset();
+ inline UINT8 vga_latch_write(int offs, UINT8 data);
+ inline UINT8 rotate_right(UINT8 val) { return (val >> vga.gc.rotate_count) | (val << (8 - vga.gc.rotate_count)); }
+ inline UINT8 vga_logical_op(UINT8 data, UINT8 plane, UINT8 mask)
+ {
+ UINT8 res = 0;
+
+ switch(vga.gc.logical_op & 3)
+ {
+ case 0: /* NONE */
+ res = (data & mask) | (vga.gc.latch[plane] & ~mask);
+ break;
+ case 1: /* AND */
+ res = (data | ~mask) & (vga.gc.latch[plane]);
+ break;
+ case 2: /* OR */
+ res = (data & mask) | (vga.gc.latch[plane]);
+ break;
+ case 3: /* XOR */
+ res = (data & mask) ^ (vga.gc.latch[plane]);
+ break;
+ }
+
+ return res;
+ }
+
+
+ struct
+ {
+ read8_delegate read_dipswitch;
+ struct
+ {
+ size_t vram_size;
+ int seq_regcount;
+ int crtc_regcount;
+ } svga_intf;
+
+ dynamic_buffer memory;
+ UINT32 pens[16]; /* the current 16 pens */
+
+ UINT8 miscellaneous_output;
+ UINT8 feature_control;
+
+ struct
+ {
+ UINT8 index;
+ UINT8 data[0x100];
+ UINT8 map_mask;
+ struct
+ {
+ UINT8 A, B;
+ }char_sel;
+ } sequencer;
+
+ /* An empty comment at the start of the line indicates that register is currently unused */
+ struct
+ {
+ UINT8 index;
+ UINT8 data[0x100];
+ UINT16 horz_total;
+ UINT16 horz_disp_end;
+ /**/ UINT8 horz_blank_start;
+ /**/ UINT8 horz_blank_end;
+ /**/ UINT8 horz_retrace_start;
+ /**/ UINT8 horz_retrace_skew;
+ /**/ UINT8 horz_retrace_end;
+ /**/ UINT8 disp_enable_skew;
+ /**/ UINT8 evra;
+ UINT16 vert_total;
+ UINT16 vert_disp_end;
+ /**/ UINT16 vert_retrace_start;
+ /**/ UINT8 vert_retrace_end;
+ /**/ UINT16 vert_blank_start;
+ UINT16 line_compare;
+ /**/ UINT32 cursor_addr;
+ /**/ UINT8 byte_panning;
+ /**/ UINT8 preset_row_scan;
+ UINT8 scan_doubling;
+ /**/ UINT8 maximum_scan_line;
+ /**/ UINT8 cursor_enable;
+ /**/ UINT8 cursor_scan_start;
+ /**/ UINT8 cursor_skew;
+ /**/ UINT8 cursor_scan_end;
+ UINT32 start_addr;
+ UINT32 start_addr_latch;
+ /**/ UINT8 protect_enable;
+ /**/ UINT8 bandwidth;
+ /**/ UINT16 offset;
+ /**/ UINT8 word_mode;
+ /**/ UINT8 dw;
+ /**/ UINT8 div4;
+ /**/ UINT8 underline_loc;
+ /**/ UINT16 vert_blank_end;
+ UINT8 sync_en;
+ /**/ UINT8 aw;
+ /**/ UINT8 div2;
+ /**/ UINT8 sldiv;
+ /**/ UINT8 map14;
+ /**/ UINT8 map13;
+ /**/ UINT8 irq_clear;
+ /**/ UINT8 irq_disable;
+ } crtc;
+
+ struct
+ {
+ UINT8 index;
+ UINT8 latch[4];
+ UINT8 set_reset;
+ UINT8 enable_set_reset;
+ UINT8 color_compare;
+ UINT8 logical_op;
+ UINT8 rotate_count;
+ UINT8 shift256;
+ UINT8 shift_reg;
+ UINT8 read_map_sel;
+ UINT8 read_mode;
+ UINT8 write_mode;
+ UINT8 color_dont_care;
+ UINT8 bit_mask;
+ UINT8 alpha_dis;
+ UINT8 memory_map_sel;
+ UINT8 host_oe;
+ UINT8 chain_oe;
+ } gc;
+
+ struct
+ {
+ UINT8 index, data[0x15]; int state;
+ UINT8 prot_bit;
+ UINT8 pel_shift;
+ UINT8 pel_shift_latch;
+ } attribute;
+
+
+ struct {
+ UINT8 read_index, write_index, mask;
+ int read;
+ int state;
+ struct { UINT8 red, green, blue; } color[0x100];
+ int dirty;
+ } dac;
+
+ struct {
+ UINT8 visible;
+ } cursor;
+
+ /* oak vga */
+ struct { UINT8 reg; } oak;
+ } vga;
+
+ emu_timer *m_vblank_timer;
+ required_device<palette_device> m_palette;
+ required_device<screen_device> m_screen;
+};
+
+
+// device type definition
+extern const device_type VGA;
+
+// ======================> svga_device
+
+class svga_device : public vga_device
+{
+public:
+ // construction/destruction
+ svga_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ virtual void zero();
+ virtual UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+protected:
+ void svga_vh_rgb8(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void svga_vh_rgb15(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void svga_vh_rgb16(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void svga_vh_rgb24(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void svga_vh_rgb32(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ virtual UINT8 pc_vga_choosevideomode();
+ virtual void device_start();
+ struct
+ {
+ UINT8 bank_r,bank_w;
+ UINT8 rgb8_en;
+ UINT8 rgb15_en;
+ UINT8 rgb16_en;
+ UINT8 rgb24_en;
+ UINT8 rgb32_en;
+ UINT8 id;
+ }svga;
+private:
+};
+
+// ======================> ibm8514_device
+
+class ibm8514a_device : public device_t
+{
+public:
+ ibm8514a_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ibm8514a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void set_vga(const char* tag) { m_vga_tag.assign(tag); }
+ void set_vga_owner() { m_vga = dynamic_cast<vga_device*>(owner()); }
+
+ void enabled();
+
+ READ16_MEMBER(ibm8514_gpstatus_r);
+ WRITE16_MEMBER(ibm8514_cmd_w);
+ READ16_MEMBER(ibm8514_line_error_r);
+ WRITE16_MEMBER(ibm8514_line_error_w);
+ READ16_MEMBER(ibm8514_status_r);
+ WRITE16_MEMBER(ibm8514_htotal_w);
+ READ16_MEMBER(ibm8514_substatus_r);
+ WRITE16_MEMBER(ibm8514_subcontrol_w);
+ READ16_MEMBER(ibm8514_subcontrol_r);
+ READ16_MEMBER(ibm8514_htotal_r);
+ READ16_MEMBER(ibm8514_vtotal_r);
+ WRITE16_MEMBER(ibm8514_vtotal_w);
+ READ16_MEMBER(ibm8514_vdisp_r);
+ WRITE16_MEMBER(ibm8514_vdisp_w);
+ READ16_MEMBER(ibm8514_vsync_r);
+ WRITE16_MEMBER(ibm8514_vsync_w);
+ READ16_MEMBER(ibm8514_desty_r);
+ WRITE16_MEMBER(ibm8514_desty_w);
+ READ16_MEMBER(ibm8514_destx_r);
+ WRITE16_MEMBER(ibm8514_destx_w);
+ READ16_MEMBER(ibm8514_ssv_r);
+ WRITE16_MEMBER(ibm8514_ssv_w);
+ READ16_MEMBER(ibm8514_currentx_r);
+ WRITE16_MEMBER(ibm8514_currentx_w);
+ READ16_MEMBER(ibm8514_currenty_r);
+ WRITE16_MEMBER(ibm8514_currenty_w);
+ READ16_MEMBER(ibm8514_width_r);
+ WRITE16_MEMBER(ibm8514_width_w);
+ READ16_MEMBER(ibm8514_fgcolour_r);
+ WRITE16_MEMBER(ibm8514_fgcolour_w);
+ READ16_MEMBER(ibm8514_bgcolour_r);
+ WRITE16_MEMBER(ibm8514_bgcolour_w);
+ READ16_MEMBER(ibm8514_multifunc_r);
+ WRITE16_MEMBER(ibm8514_multifunc_w);
+ READ16_MEMBER(ibm8514_backmix_r);
+ WRITE16_MEMBER(ibm8514_backmix_w);
+ READ16_MEMBER(ibm8514_foremix_r);
+ WRITE16_MEMBER(ibm8514_foremix_w);
+ READ16_MEMBER(ibm8514_pixel_xfer_r);
+ WRITE16_MEMBER(ibm8514_pixel_xfer_w);
+ READ16_MEMBER(ibm8514_read_mask_r);
+ WRITE16_MEMBER(ibm8514_read_mask_w);
+ READ16_MEMBER(ibm8514_write_mask_r);
+ WRITE16_MEMBER(ibm8514_write_mask_w);
+ void ibm8514_wait_draw();
+ struct
+ {
+ UINT16 htotal; // Horizontal total (9 bits)
+ UINT16 vtotal; // Vertical total adjust (3 bits), Vertical total base (9 bit)
+ UINT16 vdisp;
+ UINT16 vsync;
+ UINT16 subctrl;
+ UINT16 substatus;
+ UINT16 ssv;
+ UINT16 ec0;
+ UINT16 ec1;
+ UINT16 ec2;
+ UINT16 ec3;
+ bool gpbusy;
+ bool data_avail;
+ INT16 dest_x;
+ INT16 dest_y;
+ INT16 curr_x;
+ INT16 curr_y;
+ INT16 prev_x;
+ INT16 prev_y;
+ INT16 line_axial_step;
+ INT16 line_diagonal_step;
+ INT16 line_errorterm;
+ UINT16 current_cmd;
+ UINT16 src_x;
+ UINT16 src_y;
+ INT16 scissors_left;
+ INT16 scissors_right;
+ INT16 scissors_top;
+ INT16 scissors_bottom;
+ UINT16 rect_width;
+ UINT16 rect_height;
+ UINT32 fgcolour;
+ UINT32 bgcolour;
+ UINT16 fgmix;
+ UINT16 bgmix;
+ UINT32 pixel_xfer;
+ UINT16 pixel_control;
+ UINT8 bus_size;
+ UINT8 multifunc_sel;
+ UINT16 multifunc_misc;
+ UINT32 read_mask;
+ UINT32 write_mask;
+
+ int state;
+ UINT8 wait_vector_len;
+ UINT8 wait_vector_dir;
+ bool wait_vector_draw;
+ UINT8 wait_vector_count;
+
+ } ibm8514;
+protected:
+ virtual void device_start();
+ virtual void device_config_complete();
+ vga_device* m_vga; // for pass-through
+ std::string m_vga_tag; // pass-through device tag
+private:
+ void ibm8514_draw_vector(UINT8 len, UINT8 dir, bool draw);
+ void ibm8514_wait_draw_ssv();
+ void ibm8514_draw_ssv(UINT8 data);
+ void ibm8514_wait_draw_vector();
+ void ibm8514_write_fg(UINT32 offset);
+ void ibm8514_write_bg(UINT32 offset);
+ void ibm8514_write(UINT32 offset, UINT32 src);
+
+ //UINT8* m_vram; // the original 8514/A has it's own VRAM, but most VGA+8514 combination cards will have
+ // only one set of VRAM, so this will only be needed in standalone 8514/A cards
+ //UINT32 m_vramsize;
+};
+
+// device type definition
+extern const device_type IBM8514A;
+
+#define MCFG_8514A_ADD(_tag, _param) \
+ MCFG_DEVICE_ADD(_tag, IBM8514A, 0) \
+ downcast<ibm8514a_device*>(device)->set_vga(_param);
+
+#define MCFG_8514A_ADD_OWNER(_tag) \
+ MCFG_DEVICE_ADD(_tag, IBM8514A, 0) \
+ downcast<ibm8514a_device*>(device)->set_vga_owner();
+
+
+class mach8_device : public ibm8514a_device
+{
+public:
+ mach8_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ mach8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ READ16_MEMBER(mach8_ec0_r);
+ WRITE16_MEMBER(mach8_ec0_w);
+ READ16_MEMBER(mach8_ec1_r);
+ WRITE16_MEMBER(mach8_ec1_w);
+ READ16_MEMBER(mach8_ec2_r);
+ WRITE16_MEMBER(mach8_ec2_w);
+ READ16_MEMBER(mach8_ec3_r);
+ WRITE16_MEMBER(mach8_ec3_w);
+ READ16_MEMBER(mach8_ext_fifo_r);
+ WRITE16_MEMBER(mach8_linedraw_index_w);
+ READ16_MEMBER(mach8_bresenham_count_r);
+ WRITE16_MEMBER(mach8_bresenham_count_w);
+ WRITE16_MEMBER(mach8_linedraw_w);
+ READ16_MEMBER(mach8_linedraw_r);
+ READ16_MEMBER(mach8_scratch0_r);
+ WRITE16_MEMBER(mach8_scratch0_w);
+ READ16_MEMBER(mach8_scratch1_r);
+ WRITE16_MEMBER(mach8_scratch1_w);
+ READ16_MEMBER(mach8_config1_r);
+ READ16_MEMBER(mach8_config2_r);
+ READ16_MEMBER(mach8_sourcex_r);
+ READ16_MEMBER(mach8_sourcey_r);
+ WRITE16_MEMBER(mach8_ext_leftscissor_w);
+ WRITE16_MEMBER(mach8_ext_topscissor_w);
+ READ16_MEMBER(mach8_clksel_r) { return mach8.clksel; }
+
+protected:
+ virtual void device_start();
+ struct
+ {
+ UINT16 scratch0;
+ UINT16 scratch1;
+ UINT16 linedraw;
+ UINT16 clksel;
+ } mach8;
+};
+
+// device type definition
+extern const device_type MACH8;
+
+#define MCFG_MACH8_ADD(_tag, _param) \
+ MCFG_DEVICE_ADD(_tag, MACH8, 0) \
+ downcast<mach8_device*>(device)->set_vga(_param);
+
+#define MCFG_MACH8_ADD_OWNER(_tag) \
+ MCFG_DEVICE_ADD(_tag, MACH8, 0) \
+ downcast<mach8_device*>(device)->set_vga_owner();
+
+// ======================> tseng_vga_device
+
+class tseng_vga_device : public svga_device
+{
+public:
+ // construction/destruction
+ tseng_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual READ8_MEMBER(port_03b0_r);
+ virtual WRITE8_MEMBER(port_03b0_w);
+ virtual READ8_MEMBER(port_03c0_r);
+ virtual WRITE8_MEMBER(port_03c0_w);
+ virtual READ8_MEMBER(port_03d0_r);
+ virtual WRITE8_MEMBER(port_03d0_w);
+ virtual READ8_MEMBER(mem_r);
+ virtual WRITE8_MEMBER(mem_w);
+
+protected:
+ virtual void device_start();
+
+private:
+ void tseng_define_video_mode();
+ UINT8 tseng_crtc_reg_read(UINT8 index);
+ void tseng_crtc_reg_write(UINT8 index, UINT8 data);
+ UINT8 tseng_seq_reg_read(UINT8 index);
+ void tseng_seq_reg_write(UINT8 index, UINT8 data);
+ void tseng_attribute_reg_write(UINT8 index, UINT8 data);
+
+ struct
+ {
+ UINT8 reg_3d8;
+ UINT8 dac_ctrl;
+ UINT8 dac_state;
+ UINT8 horz_overflow;
+ UINT8 aux_ctrl;
+ bool ext_reg_ena;
+ UINT8 misc1;
+ UINT8 misc2;
+ }et4k;
+
+};
+
+
+// device type definition
+extern const device_type TSENG_VGA;
+
+
+// ======================> ati_vga_device
+
+class ati_vga_device : public svga_device
+{
+public:
+ // construction/destruction
+ ati_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ati_vga_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ virtual READ8_MEMBER(mem_r);
+ virtual WRITE8_MEMBER(mem_w);
+
+ // VGA registers
+ virtual READ8_MEMBER(port_03c0_r);
+ READ8_MEMBER(ati_port_ext_r);
+ WRITE8_MEMBER(ati_port_ext_w);
+
+ virtual machine_config_constructor device_mconfig_additions() const;
+ virtual UINT16 offset();
+
+ mach8_device* get_8514() { return m_8514; }
+protected:
+ virtual void device_start();
+private:
+ void ati_define_video_mode();
+ struct
+ {
+ UINT8 ext_reg[64];
+ UINT8 ext_reg_select;
+ UINT8 vga_chip_id;
+ } ati;
+ mach8_device* m_8514;
+};
+
+// device type definition
+extern const device_type ATI_VGA;
+
+
+// ======================> s3_vga_device
+
+class s3_vga_device : public ati_vga_device
+{
+public:
+ // construction/destruction
+ s3_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ s3_vga_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ virtual READ8_MEMBER(port_03b0_r);
+ virtual WRITE8_MEMBER(port_03b0_w);
+ virtual READ8_MEMBER(port_03c0_r);
+ virtual WRITE8_MEMBER(port_03c0_w);
+ virtual READ8_MEMBER(port_03d0_r);
+ virtual WRITE8_MEMBER(port_03d0_w);
+ virtual READ8_MEMBER(mem_r);
+ virtual WRITE8_MEMBER(mem_w);
+
+ virtual UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ ibm8514a_device* get_8514() { return m_8514; }
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ struct
+ {
+ UINT8 memory_config;
+ UINT8 ext_misc_ctrl_2;
+ UINT8 crt_reg_lock;
+ UINT8 reg_lock1;
+ UINT8 reg_lock2;
+ UINT8 enable_8514;
+ UINT8 enable_s3d;
+ UINT8 cr3a;
+ UINT8 cr42;
+ UINT8 cr43;
+ UINT8 cr53;
+ UINT8 id_high;
+ UINT8 id_low;
+ UINT8 revision;
+ UINT8 id_cr30;
+ UINT32 strapping; // power-on strapping bits
+ UINT8 sr10; // MCLK PLL
+ UINT8 sr11; // MCLK PLL
+ UINT8 sr12; // DCLK PLL
+ UINT8 sr13; // DCLK PLL
+ UINT8 sr15; // CLKSYN control 2
+ UINT8 sr17; // CLKSYN test
+ UINT8 clk_pll_r; // individual DCLK PLL values
+ UINT8 clk_pll_m;
+ UINT8 clk_pll_n;
+
+ // data for memory-mapped I/O
+ UINT16 mmio_9ae8;
+ UINT16 mmio_bee8;
+ UINT16 mmio_96e8;
+
+ // hardware graphics cursor
+ UINT8 cursor_mode;
+ UINT16 cursor_x;
+ UINT16 cursor_y;
+ UINT16 cursor_start_addr;
+ UINT8 cursor_pattern_x; // cursor pattern origin
+ UINT8 cursor_pattern_y;
+ UINT8 cursor_fg[4];
+ UINT8 cursor_bg[4];
+ UINT8 cursor_fg_ptr;
+ UINT8 cursor_bg_ptr;
+ UINT8 extended_dac_ctrl;
+ } s3;
+ virtual UINT16 offset();
+
+private:
+ UINT8 s3_crtc_reg_read(UINT8 index);
+ void s3_define_video_mode(void);
+ void s3_crtc_reg_write(UINT8 index, UINT8 data);
+ UINT8 s3_seq_reg_read(UINT8 index);
+ void s3_seq_reg_write(UINT8 index, UINT8 data);
+ ibm8514a_device* m_8514;
+};
+
+// device type definition
+extern const device_type S3_VGA;
+
+// ======================> gamtor_vga_device
+
+class gamtor_vga_device : public svga_device
+{
+public:
+ // construction/destruction
+ gamtor_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+
+ virtual READ8_MEMBER(port_03b0_r);
+ virtual WRITE8_MEMBER(port_03b0_w);
+ virtual READ8_MEMBER(port_03c0_r);
+ virtual WRITE8_MEMBER(port_03c0_w);
+ virtual READ8_MEMBER(port_03d0_r);
+ virtual WRITE8_MEMBER(port_03d0_w);
+ virtual READ8_MEMBER(mem_r);
+ virtual WRITE8_MEMBER(mem_w);
+
+protected:
+private:
+};
+
+
+// device type definition
+extern const device_type GAMTOR_VGA;
+
+/*
+ pega notes (paradise)
+ build in amstrad pc1640
+
+ ROM_LOAD("40100", 0xc0000, 0x8000, CRC(d2d1f1ae))
+
+ 4 additional dipswitches
+ seems to have emulation modes at register level
+ (mda/hgc lines bit 8 not identical to ega/vga)
+
+ standard ega/vga dipswitches
+ 00000000 320x200
+ 00000001 640x200 hanging
+ 00000010 640x200 hanging
+ 00000011 640x200 hanging
+
+ 00000100 640x350 hanging
+ 00000101 640x350 hanging EGA mono
+ 00000110 320x200
+ 00000111 640x200
+
+ 00001000 640x200
+ 00001001 640x200
+ 00001010 720x350 partial visible
+ 00001011 720x350 partial visible
+
+ 00001100 320x200
+ 00001101 320x200
+ 00001110 320x200
+ 00001111 320x200
+
+*/
+
+/*
+ oak vga (oti 037 chip)
+ (below bios patch needed for running)
+
+ ROM_LOAD("oakvga.bin", 0xc0000, 0x8000, CRC(318c5f43))
+*/
+
+
+#endif /* PC_VGA_H */
diff --git a/src/devices/video/poly.h b/src/devices/video/poly.h
new file mode 100644
index 00000000000..1a564a2092d
--- /dev/null
+++ b/src/devices/video/poly.h
@@ -0,0 +1,1175 @@
+// license:BSD-3-Clause
+// copyright-holders:Ville Linde, Aaron Giles
+/***************************************************************************
+
+ poly.h
+
+ Polygon helper routines.
+
+****************************************************************************
+
+ Pixel model:
+
+ (0.0,0.0) (1.0,0.0) (2.0,0.0) (3.0,0.0)
+ +---------------+---------------+---------------+
+ | | | |
+ | | | |
+ | (0.5,0.5) | (1.5,0.5) | (2.5,0.5) |
+ | * | * | * |
+ | | | |
+ | | | |
+ (0.0,1.0) (1.0,1.0) (2.0,1.0) (3.0,1.0)
+ +---------------+---------------+---------------+
+ | | | |
+ | | | |
+ | (0.5,1.5) | (1.5,1.5) | (2.5,1.5) |
+ | * | * | * |
+ | | | |
+ | | | |
+ | | | |
+ +---------------+---------------+---------------+
+ (0.0,2.0) (1.0,2.0) (2.0,2.0) (3.0,2.0)
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __POLY_H__
+#define __POLY_H__
+
+#include <limits.h>
+
+//**************************************************************************
+// DEBUGGING
+//**************************************************************************
+
+// keep statistics
+#define KEEP_POLY_STATISTICS 0
+
+// turn this on to log the reasons for any long waits
+#define LOG_WAITS 0
+
+// number of profiling ticks before we consider a wait "long"
+#define LOG_WAIT_THRESHOLD 1000
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define POLYFLAG_INCLUDE_BOTTOM_EDGE 0x01
+#define POLYFLAG_INCLUDE_RIGHT_EDGE 0x02
+#define POLYFLAG_NO_WORK_QUEUE 0x04
+
+#define SCANLINES_PER_BUCKET 8
+#define CACHE_LINE_SIZE 64 // this is a general guess
+#define TOTAL_BUCKETS (512 / SCANLINES_PER_BUCKET)
+#define UNITS_PER_POLY (100 / SCANLINES_PER_BUCKET)
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+//-------------------------------------------------
+// global helpers for float base types
+//-------------------------------------------------
+
+inline float poly_floor(float x) { return floorf(x); }
+inline float poly_abs(float x) { return fabsf(x); }
+inline float poly_recip(float x) { return 1.0f / x; }
+
+
+//-------------------------------------------------
+// global helpers for double base types
+//-------------------------------------------------
+
+inline double poly_floor(double x) { return floor(x); }
+inline double poly_abs(double x) { return fabs(x); }
+inline double poly_recip(double x) { return 1.0 / x; }
+
+
+// poly_manager is a template class
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+class poly_manager
+{
+public:
+ // each vertex has an X/Y coordinate and a set of parameters
+ struct vertex_t
+ {
+ vertex_t() { }
+ vertex_t(_BaseType _x, _BaseType _y) { x = _x; y = _y; }
+
+ _BaseType x, y; // X, Y coordinates
+ _BaseType p[_MaxParams]; // interpolated parameters
+ };
+
+ // a single extent describes a span and a list of parameter extents
+ struct extent_t
+ {
+ INT16 startx, stopx; // starting (inclusive)/ending (exclusive) endpoints
+ struct
+ {
+ _BaseType start; // parameter value at start
+ _BaseType dpdx; // dp/dx relative to start
+ } param[_MaxParams];
+ void *userdata; // custom per-span data
+ };
+
+ // delegate type for scanline callbacks
+ typedef delegate<void (INT32, const extent_t &, const _ObjectData &, int)> render_delegate;
+
+ // construction/destruction
+ poly_manager(running_machine &machine, UINT8 flags = 0);
+ poly_manager(screen_device &screen, UINT8 flags = 0);
+ virtual ~poly_manager();
+
+ // getters
+ running_machine &machine() const { return m_machine; }
+ screen_device &screen() const { assert(m_screen != NULL); return *m_screen; }
+
+ // synchronization
+ void wait(const char *debug_reason = "general");
+
+ // object data allocators
+ _ObjectData &object_data_alloc();
+ _ObjectData &object_data_last() const { return m_object.last(); }
+
+ // tiles
+ UINT32 render_tile(const rectangle &cliprect, render_delegate callback, int paramcount, const vertex_t &v1, const vertex_t &v2);
+
+ // triangles
+ UINT32 render_triangle(const rectangle &cliprect, render_delegate callback, int paramcount, const vertex_t &v1, const vertex_t &v2, const vertex_t &v3);
+ UINT32 render_triangle_fan(const rectangle &cliprect, render_delegate callback, int paramcount, int numverts, const vertex_t *v);
+ UINT32 render_triangle_strip(const rectangle &cliprect, render_delegate callback, int paramcount, int numverts, const vertex_t *v);
+ UINT32 render_triangle_custom(const rectangle &cliprect, render_delegate callback, int startscanline, int numscanlines, const extent_t *extents);
+
+ // polygons
+ template<int _NumVerts>
+ UINT32 render_polygon(const rectangle &cliprect, render_delegate callback, int paramcount, const vertex_t *v);
+
+ // public helpers
+ int zclip_if_less(int numverts, const vertex_t *v, vertex_t *outv, int paramcount, _BaseType clipval);
+
+private:
+ // polygon_info describes a single polygon, which includes the poly_params
+ struct polygon_info
+ {
+ poly_manager * m_owner; // pointer back to the poly manager
+ _ObjectData * m_object; // object data pointer
+ render_delegate m_callback; // callback to handle a scanline's worth of work
+ };
+
+ // internal unit of work
+ struct work_unit
+ {
+ volatile UINT32 count_next; // number of scanlines and index of next item to process
+ polygon_info * polygon; // pointer to polygon
+ INT16 scanline; // starting scanline
+ UINT16 previtem; // index of previous item in the same bucket
+ #ifndef PTR64
+ UINT32 dummy; // pad to 16 bytes
+ #endif
+ extent_t extent[SCANLINES_PER_BUCKET]; // array of scanline extents
+ };
+
+ // class for managing an array of items
+ template<class _Type, int _Count>
+ class poly_array
+ {
+ // size of an item, rounded up to the cache line size
+ static const int k_itemsize = ((sizeof(_Type) + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE) * CACHE_LINE_SIZE;
+
+ public:
+ // construction
+ poly_array(running_machine &machine, poly_manager &manager)
+ : m_manager(manager),
+ m_base(auto_alloc_array_clear(machine, UINT8, k_itemsize * _Count)),
+ m_next(0),
+ m_max(0),
+ m_waits(0) { }
+
+ // destruction
+ ~poly_array() { auto_free(m_manager.machine(), m_base); }
+
+ // operators
+ _Type &operator[](int index) const { assert(index >= 0 && index < _Count); return *reinterpret_cast<_Type *>(m_base + index * k_itemsize); }
+
+ // getters
+ int count() const { return m_next; }
+ int max() const { return m_max; }
+ int waits() const { return m_waits; }
+ int itemsize() const { return k_itemsize; }
+ int allocated() const { return _Count; }
+ int indexof(_Type &item) const { int result = (reinterpret_cast<UINT8 *>(&item) - m_base) / k_itemsize; assert(result >= 0 && result < _Count); return result; }
+
+ // operations
+ void reset() { m_next = 0; }
+ _Type &next() { if (m_next > m_max) m_max = m_next; assert(m_next < _Count); return *new(m_base + m_next++ * k_itemsize) _Type; }
+ _Type &last() const { return (*this)[m_next - 1]; }
+ void wait_for_space(int count = 1) { while ((m_next + count) >= _Count) { m_waits++; m_manager.wait(""); } }
+
+ private:
+ // internal state
+ poly_manager & m_manager;
+ UINT8 * m_base;
+ int m_next;
+ int m_max;
+ int m_waits;
+ };
+
+ // internal array types
+ typedef poly_array<polygon_info, _MaxPolys> polygon_array;
+ typedef poly_array<_ObjectData, _MaxPolys + 1> objectdata_array;
+ typedef poly_array<work_unit, MIN(_MaxPolys * UNITS_PER_POLY, 65535)> unit_array;
+
+ // round in a cross-platform consistent manner
+ inline INT32 round_coordinate(_BaseType value)
+ {
+ INT32 result = poly_floor(value);
+
+ if ((value > 0) && (result < 0))
+ return INT_MAX-1;
+ return result + (value - _BaseType(result) > _BaseType(0.5));
+ }
+
+ // internal helpers
+ polygon_info &polygon_alloc(int minx, int maxx, int miny, int maxy, render_delegate callback)
+ {
+ // wait for space in the polygon and unit arrays
+ m_polygon.wait_for_space();
+ m_unit.wait_for_space((maxy - miny) / SCANLINES_PER_BUCKET + 2);
+
+ // return and initialize the next one
+ polygon_info &polygon = m_polygon.next();
+ polygon.m_owner = this;
+ polygon.m_object = &object_data_last();
+ polygon.m_callback = callback;
+ return polygon;
+ }
+
+ static void *work_item_callback(void *param, int threadid);
+ void presave() { wait("pre-save"); }
+
+ // queue management
+ running_machine & m_machine;
+ screen_device * m_screen;
+ osd_work_queue * m_queue; // work queue
+
+ // arrays
+ polygon_array m_polygon; // array of polygons
+ objectdata_array m_object; // array of object data
+ unit_array m_unit; // array of work units
+
+ // misc data
+ UINT8 m_flags; // flags
+
+ // buckets
+ UINT16 m_unit_bucket[TOTAL_BUCKETS]; // buckets for tracking unit usage
+
+ // statistics
+ UINT32 m_tiles; // number of tiles queued
+ UINT32 m_triangles; // number of triangles queued
+ UINT32 m_quads; // number of quads queued
+ UINT64 m_pixels; // number of pixels rendered
+#if KEEP_POLY_STATISTICS
+ UINT32 m_conflicts[WORK_MAX_THREADS]; // number of conflicts found, per thread
+ UINT32 m_resolved[WORK_MAX_THREADS]; // number of conflicts resolved, per thread
+#endif
+};
+
+
+//-------------------------------------------------
+// poly_manager - constructor
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::poly_manager(running_machine &machine, UINT8 flags)
+ : m_machine(machine),
+ m_screen(NULL),
+ m_queue(NULL),
+ m_polygon(machine, *this),
+ m_object(machine, *this),
+ m_unit(machine, *this),
+ m_flags(flags),
+ m_triangles(0),
+ m_quads(0),
+ m_pixels(0)
+{
+#if KEEP_POLY_STATISTICS
+ memset(m_conflicts, 0, sizeof(m_conflicts));
+ memset(m_resolved, 0, sizeof(m_resolved));
+#endif
+
+ // create the work queue
+ if (!(flags & POLYFLAG_NO_WORK_QUEUE))
+ m_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_MULTI | WORK_QUEUE_FLAG_HIGH_FREQ);
+
+ // request a pre-save callback for synchronization
+ machine.save().register_presave(save_prepost_delegate(FUNC(poly_manager::presave), this));
+}
+
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::poly_manager(screen_device &screen, UINT8 flags)
+ : m_machine(screen.machine()),
+ m_screen(&screen),
+ m_queue(NULL),
+ m_polygon(screen.machine(), *this),
+ m_object(screen.machine(), *this),
+ m_unit(screen.machine(), *this),
+ m_flags(flags),
+ m_triangles(0),
+ m_quads(0),
+ m_pixels(0)
+{
+#if KEEP_POLY_STATISTICS
+ memset(m_conflicts, 0, sizeof(m_conflicts));
+ memset(m_resolved, 0, sizeof(m_resolved));
+#endif
+
+ // create the work queue
+ if (!(flags & POLYFLAG_NO_WORK_QUEUE))
+ m_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_MULTI | WORK_QUEUE_FLAG_HIGH_FREQ);
+
+ // request a pre-save callback for synchronization
+ machine().save().register_presave(save_prepost_delegate(FUNC(poly_manager::presave), this));
+}
+
+
+//-------------------------------------------------
+// ~poly_manager - destructor
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::~poly_manager()
+{
+#if KEEP_POLY_STATISTICS
+{
+ // accumulate stats over the entire collection
+ int conflicts = 0, resolved = 0;
+ for (int i = 0; i < ARRAY_LENGTH(m_conflicts); i++)
+ {
+ conflicts += m_conflicts[i];
+ resolved += m_resolved[i];
+ }
+
+ // output global stats
+ printf("Total triangles = %d\n", m_triangles);
+ printf("Total quads = %d\n", m_quads);
+ if (m_pixels > 1000000000)
+ printf("Total pixels = %d%09d\n", (UINT32)(m_pixels / 1000000000), (UINT32)(m_pixels % 1000000000));
+ else
+ printf("Total pixels = %d\n", (UINT32)m_pixels);
+
+ printf("Conflicts: %d resolved, %d total\n", resolved, conflicts);
+ printf("Units: %5d used, %5d allocated, %5d waits, %4d bytes each, %7d total\n", m_unit.max(), m_unit.allocated(), m_unit.waits(), m_unit.itemsize(), m_unit.allocated() * m_unit.itemsize());
+ printf("Polygons: %5d used, %5d allocated, %5d waits, %4d bytes each, %7d total\n", m_polygon.max(), m_polygon.allocated(), m_polygon.waits(), m_polygon.itemsize(), m_polygon.allocated() * m_polygon.itemsize());
+ printf("Object data: %5d used, %5d allocated, %5d waits, %4d bytes each, %7d total\n", m_object.max(), m_object.allocated(), m_object.waits(), m_object.itemsize(), m_object.allocated() * m_object.itemsize());
+}
+#endif
+
+ // free the work queue
+ if (m_queue != NULL)
+ osd_work_queue_free(m_queue);
+}
+
+
+//-------------------------------------------------
+// work_item_callback - process a work item
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+void *poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::work_item_callback(void *param, int threadid)
+{
+ while (1)
+ {
+ work_unit &unit = *(work_unit *)param;
+ polygon_info &polygon = *unit.polygon;
+ int count = unit.count_next & 0xffff;
+ UINT32 orig_count_next;
+
+ // if our previous item isn't done yet, enqueue this item to the end and proceed
+ if (unit.previtem != 0xffff)
+ {
+ work_unit &prevunit = polygon.m_owner->m_unit[unit.previtem];
+ if (prevunit.count_next != 0)
+ {
+ UINT32 unitnum = polygon.m_owner->m_unit.indexof(unit);
+ UINT32 new_count_next;
+
+ // attempt to atomically swap in this new value
+ do
+ {
+ orig_count_next = prevunit.count_next;
+ new_count_next = orig_count_next | (unitnum << 16);
+ } while (compare_exchange32((volatile INT32 *)&prevunit.count_next, orig_count_next, new_count_next) != orig_count_next);
+
+#if KEEP_POLY_STATISTICS
+ // track resolved conflicts
+ polygon.m_owner->m_conflicts[threadid]++;
+ if (orig_count_next != 0)
+ polygon.m_owner->m_resolved[threadid]++;
+#endif
+ // if we succeeded, skip out early so we can do other work
+ if (orig_count_next != 0)
+ break;
+ }
+ }
+
+ // iterate over extents
+ for (int curscan = 0; curscan < count; curscan++)
+ polygon.m_callback(unit.scanline + curscan, unit.extent[curscan], *polygon.m_object, threadid);
+
+ // set our count to 0 and re-fetch the original count value
+ do
+ {
+ orig_count_next = unit.count_next;
+ } while (compare_exchange32((volatile INT32 *)&unit.count_next, orig_count_next, 0) != orig_count_next);
+
+ // if we have no more work to do, do nothing
+ orig_count_next >>= 16;
+ if (orig_count_next == 0)
+ break;
+ param = &polygon.m_owner->m_unit[orig_count_next];
+ }
+ return NULL;
+}
+
+
+//-------------------------------------------------
+// wait - stall until all work is complete
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+void poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::wait(const char *debug_reason)
+{
+ osd_ticks_t time;
+
+ // remember the start time if we're logging
+ if (LOG_WAITS)
+ time = get_profile_ticks();
+
+ // wait for all pending work items to complete
+ if (m_queue != NULL)
+ osd_work_queue_wait(m_queue, osd_ticks_per_second() * 100);
+
+ // if we don't have a queue, just run the whole list now
+ else
+ for (int unitnum = 0; unitnum < m_unit.count(); unitnum++)
+ work_item_callback(&m_unit[unitnum], 0);
+
+ // log any long waits
+ if (LOG_WAITS)
+ {
+ time = get_profile_ticks() - time;
+ if (time > LOG_WAIT_THRESHOLD)
+ logerror("Poly:Waited %d cycles for %s\n", (int)time, debug_reason);
+ }
+
+ // reset the state
+ m_polygon.reset();
+ m_unit.reset();
+ memset(m_unit_bucket, 0xff, sizeof(m_unit_bucket));
+
+ // we need to preserve the last object data that was supplied
+ if (m_object.count() > 0)
+ {
+ _ObjectData temp = object_data_last();
+ m_object.reset();
+ m_object.next() = temp;
+ }
+ else
+ m_object.reset();
+}
+
+
+//-------------------------------------------------
+// object_data_alloc - allocate a new _ObjectData
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+_ObjectData &poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::object_data_alloc()
+{
+ // wait for a work item if we have to, then return the next item
+ m_object.wait_for_space();
+ return m_object.next();
+}
+
+
+//-------------------------------------------------
+// render_tile - render a tile
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+UINT32 poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::render_tile(const rectangle &cliprect, render_delegate callback, int paramcount, const vertex_t &_v1, const vertex_t &_v2)
+{
+ const vertex_t *v1 = &_v1;
+ const vertex_t *v2 = &_v2;
+
+ // first sort by Y
+ if (v2->y < v1->y)
+ {
+ const vertex_t *tv = v1;
+ v1 = v2;
+ v2 = tv;
+ }
+
+ // compute some integral X/Y vertex values
+ INT32 v1y = round_coordinate(v1->y);
+ INT32 v2y = round_coordinate(v2->y);
+
+ // clip coordinates
+ INT32 v1yclip = v1y;
+ INT32 v2yclip = v2y + ((m_flags & POLYFLAG_INCLUDE_BOTTOM_EDGE) ? 1 : 0);
+ v1yclip = MAX(v1yclip, cliprect.min_y);
+ v2yclip = MIN(v2yclip, cliprect.max_y + 1);
+ if (v2yclip - v1yclip <= 0)
+ return 0;
+
+ // determine total X extents
+ _BaseType minx = v1->x;
+ _BaseType maxx = v2->x;
+ if (minx > maxx)
+ return 0;
+
+ // allocate and populate a new polygon
+ polygon_info &polygon = polygon_alloc(round_coordinate(minx), round_coordinate(maxx), v1yclip, v2yclip, callback);
+
+ // compute parameter deltas
+ _BaseType param_dpdx[_MaxParams];
+ _BaseType param_dpdy[_MaxParams];
+ if (paramcount > 0)
+ {
+ _BaseType oox = poly_recip(v2->x - v1->x);
+ _BaseType ooy = poly_recip(v2->y - v1->y);
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ param_dpdx[paramnum] = oox * (v2->p[paramnum] - v1->p[paramnum]);
+ param_dpdy[paramnum] = ooy * (v2->p[paramnum] - v1->p[paramnum]);
+ }
+ }
+
+ // clamp to full pixels
+ INT32 istartx = round_coordinate(v1->x);
+ INT32 istopx = round_coordinate(v2->x);
+
+ // force start < stop
+ if (istartx > istopx)
+ {
+ INT32 temp = istartx;
+ istartx = istopx;
+ istopx = temp;
+ }
+
+ // include the right edge if requested
+ if (m_flags & POLYFLAG_INCLUDE_RIGHT_EDGE)
+ istopx++;
+
+ // apply left/right clipping
+ if (istartx < cliprect.min_x)
+ istartx = cliprect.min_x;
+ if (istopx > cliprect.max_x)
+ istopx = cliprect.max_x + 1;
+ if (istartx >= istopx)
+ return 0;
+
+ // compute the X extents for each scanline
+ INT32 pixels = 0;
+ UINT32 startunit = m_unit.count();
+ INT32 scaninc = 1;
+ for (INT32 curscan = v1yclip; curscan < v2yclip; curscan += scaninc)
+ {
+ UINT32 bucketnum = ((UINT32)curscan / SCANLINES_PER_BUCKET) % TOTAL_BUCKETS;
+ UINT32 unit_index = m_unit.count();
+ work_unit &unit = m_unit.next();
+
+ // determine how much to advance to hit the next bucket
+ scaninc = SCANLINES_PER_BUCKET - (UINT32)curscan % SCANLINES_PER_BUCKET;
+
+ // fill in the work unit basics
+ unit.polygon = &polygon;
+ unit.count_next = MIN(v2yclip - curscan, scaninc);
+ unit.scanline = curscan;
+ unit.previtem = m_unit_bucket[bucketnum];
+ m_unit_bucket[bucketnum] = unit_index;
+
+ // iterate over extents
+ for (int extnum = 0; extnum < unit.count_next; extnum++)
+ {
+ // compute the ending X based on which part of the triangle we're in
+ _BaseType fully = _BaseType(curscan + extnum) + _BaseType(0.5);
+
+ // set the extent and update the total pixel count
+ extent_t &extent = unit.extent[extnum];
+ extent.startx = istartx;
+ extent.stopx = istopx;
+ extent.userdata = NULL;
+ pixels += istopx - istartx;
+
+ // fill in the parameters for the extent
+ _BaseType fullstartx = _BaseType(istartx) + _BaseType(0.5);
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ extent.param[paramnum].start = v1->p[paramnum] + fullstartx * param_dpdx[paramnum] + fully * param_dpdy[paramnum];
+ extent.param[paramnum].dpdx = param_dpdx[paramnum];
+ }
+ }
+ }
+
+ // enqueue the work items
+ if (m_queue != NULL)
+ osd_work_item_queue_multiple(m_queue, work_item_callback, m_unit.count() - startunit, &m_unit[startunit], m_unit.itemsize(), WORK_ITEM_FLAG_AUTO_RELEASE);
+
+ // return the total number of pixels in the triangle
+ m_tiles++;
+ m_pixels += pixels;
+ return pixels;
+}
+
+
+//-------------------------------------------------
+// render_triangle - render a single triangle
+// given 3 vertexes
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+UINT32 poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::render_triangle(const rectangle &cliprect, render_delegate callback, int paramcount, const vertex_t &_v1, const vertex_t &_v2, const vertex_t &_v3)
+{
+ const vertex_t *v1 = &_v1;
+ const vertex_t *v2 = &_v2;
+ const vertex_t *v3 = &_v3;
+
+ // first sort by Y
+ if (v2->y < v1->y)
+ {
+ const vertex_t *tv = v1;
+ v1 = v2;
+ v2 = tv;
+ }
+ if (v3->y < v2->y)
+ {
+ const vertex_t *tv = v2;
+ v2 = v3;
+ v3 = tv;
+ if (v2->y < v1->y)
+ {
+ const vertex_t *tv = v1;
+ v1 = v2;
+ v2 = tv;
+ }
+ }
+
+ // compute some integral X/Y vertex values
+ INT32 v1y = round_coordinate(v1->y);
+ INT32 v3y = round_coordinate(v3->y);
+
+ // clip coordinates
+ INT32 v1yclip = v1y;
+ INT32 v3yclip = v3y + ((m_flags & POLYFLAG_INCLUDE_BOTTOM_EDGE) ? 1 : 0);
+ v1yclip = MAX(v1yclip, cliprect.min_y);
+ v3yclip = MIN(v3yclip, cliprect.max_y + 1);
+ if (v3yclip - v1yclip <= 0)
+ return 0;
+
+ // determine total X extents
+ _BaseType minx = v1->x;
+ _BaseType maxx = v1->x;
+ if (v2->x < minx) minx = v2->x;
+ else if (v2->x > maxx) maxx = v2->x;
+ if (v3->x < minx) minx = v3->x;
+ else if (v3->x > maxx) maxx = v3->x;
+
+ // allocate and populate a new polygon
+ polygon_info &polygon = polygon_alloc(round_coordinate(minx), round_coordinate(maxx), v1yclip, v3yclip, callback);
+
+ // compute the slopes for each portion of the triangle
+ _BaseType dxdy_v1v2 = (v2->y == v1->y) ? _BaseType(0.0) : (v2->x - v1->x) / (v2->y - v1->y);
+ _BaseType dxdy_v1v3 = (v3->y == v1->y) ? _BaseType(0.0) : (v3->x - v1->x) / (v3->y - v1->y);
+ _BaseType dxdy_v2v3 = (v3->y == v2->y) ? _BaseType(0.0) : (v3->x - v2->x) / (v3->y - v2->y);
+
+ // compute parameter starting points and deltas
+ _BaseType param_start[_MaxParams];
+ _BaseType param_dpdx[_MaxParams];
+ _BaseType param_dpdy[_MaxParams];
+ if (paramcount > 0)
+ {
+ _BaseType a00 = v2->y - v3->y;
+ _BaseType a01 = v3->x - v2->x;
+ _BaseType a02 = v2->x*v3->y - v3->x*v2->y;
+ _BaseType a10 = v3->y - v1->y;
+ _BaseType a11 = v1->x - v3->x;
+ _BaseType a12 = v3->x*v1->y - v1->x*v3->y;
+ _BaseType a20 = v1->y - v2->y;
+ _BaseType a21 = v2->x - v1->x;
+ _BaseType a22 = v1->x*v2->y - v2->x*v1->y;
+ _BaseType det = a02 + a12 + a22;
+
+ if (poly_abs(det) < _BaseType(0.00001))
+ {
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ param_dpdx[paramnum] = _BaseType(0.0);
+ param_dpdy[paramnum] = _BaseType(0.0);
+ param_start[paramnum] = v1->p[paramnum];
+ }
+ }
+ else
+ {
+ _BaseType idet = poly_recip(det);
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ param_dpdx[paramnum] = idet * (v1->p[paramnum]*a00 + v2->p[paramnum]*a10 + v3->p[paramnum]*a20);
+ param_dpdy[paramnum] = idet * (v1->p[paramnum]*a01 + v2->p[paramnum]*a11 + v3->p[paramnum]*a21);
+ param_start[paramnum] = idet * (v1->p[paramnum]*a02 + v2->p[paramnum]*a12 + v3->p[paramnum]*a22);
+ }
+ }
+ }
+ else // GCC 4.7.0 incorrectly claims these are uninitialized; humor it by initializing in the (hopefully rare) zero parameter case
+ {
+ param_start[0] = _BaseType(0.0);
+ param_dpdx[0] = _BaseType(0.0);
+ param_dpdy[0] = _BaseType(0.0);
+ }
+
+ // compute the X extents for each scanline
+ INT32 pixels = 0;
+ UINT32 startunit = m_unit.count();
+ INT32 scaninc = 1;
+ for (INT32 curscan = v1yclip; curscan < v3yclip; curscan += scaninc)
+ {
+ UINT32 bucketnum = ((UINT32)curscan / SCANLINES_PER_BUCKET) % TOTAL_BUCKETS;
+ UINT32 unit_index = m_unit.count();
+ work_unit &unit = m_unit.next();
+
+ // determine how much to advance to hit the next bucket
+ scaninc = SCANLINES_PER_BUCKET - (UINT32)curscan % SCANLINES_PER_BUCKET;
+
+ // fill in the work unit basics
+ unit.polygon = &polygon;
+ unit.count_next = MIN(v3yclip - curscan, scaninc);
+ unit.scanline = curscan;
+ unit.previtem = m_unit_bucket[bucketnum];
+ m_unit_bucket[bucketnum] = unit_index;
+
+ // iterate over extents
+ for (int extnum = 0; extnum < unit.count_next; extnum++)
+ {
+ // compute the ending X based on which part of the triangle we're in
+ _BaseType fully = _BaseType(curscan + extnum) + _BaseType(0.5);
+ _BaseType startx = v1->x + (fully - v1->y) * dxdy_v1v3;
+ _BaseType stopx;
+ if (fully < v2->y)
+ stopx = v1->x + (fully - v1->y) * dxdy_v1v2;
+ else
+ stopx = v2->x + (fully - v2->y) * dxdy_v2v3;
+
+ // clamp to full pixels
+ INT32 istartx = round_coordinate(startx);
+ INT32 istopx = round_coordinate(stopx);
+
+ // force start < stop
+ if (istartx > istopx)
+ {
+ INT32 temp = istartx;
+ istartx = istopx;
+ istopx = temp;
+ }
+
+ // include the right edge if requested
+ if (m_flags & POLYFLAG_INCLUDE_RIGHT_EDGE)
+ istopx++;
+
+ // apply left/right clipping
+ if (istartx < cliprect.min_x)
+ istartx = cliprect.min_x;
+ if (istopx > cliprect.max_x)
+ istopx = cliprect.max_x + 1;
+
+ // set the extent and update the total pixel count
+ if (istartx >= istopx)
+ istartx = istopx = 0;
+ extent_t &extent = unit.extent[extnum];
+ extent.startx = istartx;
+ extent.stopx = istopx;
+ extent.userdata = NULL;
+ pixels += istopx - istartx;
+
+ // fill in the parameters for the extent
+ _BaseType fullstartx = _BaseType(istartx) + _BaseType(0.5);
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ extent.param[paramnum].start = param_start[paramnum] + fullstartx * param_dpdx[paramnum] + fully * param_dpdy[paramnum];
+ extent.param[paramnum].dpdx = param_dpdx[paramnum];
+ }
+ }
+ }
+
+ // enqueue the work items
+ if (m_queue != NULL)
+ osd_work_item_queue_multiple(m_queue, work_item_callback, m_unit.count() - startunit, &m_unit[startunit], m_unit.itemsize(), WORK_ITEM_FLAG_AUTO_RELEASE);
+
+ // return the total number of pixels in the triangle
+ m_triangles++;
+ m_pixels += pixels;
+ return pixels;
+}
+
+
+//-------------------------------------------------
+// render_triangle_fan - render a set of
+// triangles in a fan
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+UINT32 poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::render_triangle_fan(const rectangle &cliprect, render_delegate callback, int paramcount, int numverts, const vertex_t *v)
+{
+ // iterate over vertices
+ UINT32 pixels = 0;
+ for (int vertnum = 2; vertnum < numverts; vertnum++)
+ pixels += render_triangle(cliprect, callback, paramcount, v[0], v[vertnum - 1], v[vertnum]);
+ return pixels;
+}
+
+
+//-------------------------------------------------
+// render_triangle_strip - render a set of
+// triangles in a strip
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+UINT32 poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::render_triangle_strip(const rectangle &cliprect, render_delegate callback, int paramcount, int numverts, const vertex_t *v)
+{
+ // iterate over vertices
+ UINT32 pixels = 0;
+ for (int vertnum = 2; vertnum < numverts; vertnum++)
+ pixels += render_triangle(cliprect, callback, paramcount, v[vertnum - 2], v[vertnum - 1], v[vertnum]);
+ return pixels;
+}
+
+
+//-------------------------------------------------
+// render_triangle_custom - perform a custom
+// render of an object, given specific extents
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+UINT32 poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::render_triangle_custom(const rectangle &cliprect, render_delegate callback, int startscanline, int numscanlines, const extent_t *extents)
+{
+ // clip coordinates
+ INT32 v1yclip = MAX(startscanline, cliprect.min_y);
+ INT32 v3yclip = MIN(startscanline + numscanlines, cliprect.max_y + 1);
+ if (v3yclip - v1yclip <= 0)
+ return 0;
+
+ // allocate and populate a new polygon
+ polygon_info &polygon = polygon_alloc(0, 0, v1yclip, v3yclip, callback);
+
+ // compute the X extents for each scanline
+ INT32 pixels = 0;
+ UINT32 startunit = m_unit.count();
+ INT32 scaninc = 1;
+ for (INT32 curscan = v1yclip; curscan < v3yclip; curscan += scaninc)
+ {
+ UINT32 bucketnum = ((UINT32)curscan / SCANLINES_PER_BUCKET) % TOTAL_BUCKETS;
+ UINT32 unit_index = m_unit.count();
+ work_unit &unit = m_unit.next();
+
+ // determine how much to advance to hit the next bucket
+ scaninc = SCANLINES_PER_BUCKET - (UINT32)curscan % SCANLINES_PER_BUCKET;
+
+ // fill in the work unit basics
+ unit.polygon = &polygon;
+ unit.count_next = MIN(v3yclip - curscan, scaninc);
+ unit.scanline = curscan;
+ unit.previtem = m_unit_bucket[bucketnum];
+ m_unit_bucket[bucketnum] = unit_index;
+
+ // iterate over extents
+ for (int extnum = 0; extnum < unit.count_next; extnum++)
+ {
+ const extent_t &srcextent = extents[(curscan + extnum) - startscanline];
+ INT32 istartx = srcextent.startx, istopx = srcextent.stopx;
+
+ // apply left/right clipping
+ if (istartx < cliprect.min_x)
+ istartx = cliprect.min_x;
+ if (istartx > cliprect.max_x)
+ istartx = cliprect.max_x + 1;
+ if (istopx < cliprect.min_x)
+ istopx = cliprect.min_x;
+ if (istopx > cliprect.max_x)
+ istopx = cliprect.max_x + 1;
+
+ // set the extent and update the total pixel count
+ extent_t &extent = unit.extent[extnum];
+ extent.startx = istartx;
+ extent.stopx = istopx;
+
+ // fill in the parameters for the extent
+ for (int paramnum = 0; paramnum < _MaxParams; paramnum++)
+ {
+ extent.param[paramnum].start = srcextent.param[paramnum].start;
+ extent.param[paramnum].dpdx = srcextent.param[paramnum].dpdx;
+ }
+
+ extent.userdata = srcextent.userdata;
+ if (istartx < istopx)
+ pixels += istopx - istartx;
+ else if(istopx < istartx)
+ pixels += istartx - istopx;
+ }
+ }
+
+ // enqueue the work items
+ if (m_queue != NULL)
+ osd_work_item_queue_multiple(m_queue, work_item_callback, m_unit.count() - startunit, &m_unit[startunit], m_unit.itemsize(), WORK_ITEM_FLAG_AUTO_RELEASE);
+
+ // return the total number of pixels in the object
+ m_triangles++;
+ m_pixels += pixels;
+ return pixels;
+}
+
+
+//-------------------------------------------------
+// render_polygon - render a single polygon up
+// to 32 vertices
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+template<int _NumVerts>
+UINT32 poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::render_polygon(const rectangle &cliprect, render_delegate callback, int paramcount, const vertex_t *v)
+{
+ // determine min/max Y vertices
+ _BaseType minx = v[0].x;
+ _BaseType maxx = v[0].x;
+ int minv = 0;
+ int maxv = 0;
+ for (int vertnum = 1; vertnum < _NumVerts; vertnum++)
+ {
+ if (v[vertnum].y < v[minv].y)
+ minv = vertnum;
+ else if (v[vertnum].y > v[maxv].y)
+ maxv = vertnum;
+ if (v[vertnum].x < minx)
+ minx = v[vertnum].x;
+ else if (v[vertnum].x > maxx)
+ maxx = v[vertnum].x;
+ }
+
+ // determine start/end scanlines
+ INT32 miny = round_coordinate(v[minv].y);
+ INT32 maxy = round_coordinate(v[maxv].y);
+
+ // clip coordinates
+ INT32 minyclip = miny;
+ INT32 maxyclip = maxy + ((m_flags & POLYFLAG_INCLUDE_BOTTOM_EDGE) ? 1 : 0);
+ minyclip = MAX(minyclip, cliprect.min_y);
+ maxyclip = MIN(maxyclip, cliprect.max_y + 1);
+ if (maxyclip - minyclip <= 0)
+ return 0;
+
+ // allocate a new polygon
+ polygon_info &polygon = polygon_alloc(round_coordinate(minx), round_coordinate(maxx), minyclip, maxyclip, callback);
+
+ // walk forward to build up the forward edge list
+ struct poly_edge
+ {
+ poly_edge * next; // next edge in sequence
+ int index; // index of this edge
+ const vertex_t * v1; // pointer to first vertex
+ const vertex_t * v2; // pointer to second vertex
+ _BaseType dxdy; // dx/dy along the edge
+ _BaseType dpdy[_MaxParams]; // per-parameter dp/dy values
+ };
+ poly_edge fedgelist[_NumVerts - 1];
+ poly_edge *edgeptr = &fedgelist[0];
+ for (int curv = minv; curv != maxv; curv = (curv == _NumVerts - 1) ? 0 : (curv + 1))
+ {
+ // set the two vertices
+ edgeptr->v1 = &v[curv];
+ edgeptr->v2 = &v[(curv == _NumVerts - 1) ? 0 : (curv + 1)];
+
+ // if horizontal, skip altogether
+ if (edgeptr->v1->y == edgeptr->v2->y)
+ continue;
+
+ // need dx/dy always, and parameter deltas as necessary
+ _BaseType ooy = poly_recip(edgeptr->v2->y - edgeptr->v1->y);
+ edgeptr->dxdy = (edgeptr->v2->x - edgeptr->v1->x) * ooy;
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ edgeptr->dpdy[paramnum] = (edgeptr->v2->p[paramnum] - edgeptr->v1->p[paramnum]) * ooy;
+ edgeptr++;
+ }
+
+ // walk backward to build up the backward edge list
+ poly_edge bedgelist[_NumVerts - 1];
+ edgeptr = &bedgelist[0];
+ for (int curv = minv; curv != maxv; curv = (curv == 0) ? (_NumVerts - 1) : (curv - 1))
+ {
+ // set the two vertices
+ edgeptr->v1 = &v[curv];
+ edgeptr->v2 = &v[(curv == 0) ? (_NumVerts - 1) : (curv - 1)];
+
+ // if horizontal, skip altogether
+ if (edgeptr->v1->y == edgeptr->v2->y)
+ continue;
+
+ // need dx/dy always, and parameter deltas as necessary
+ _BaseType ooy = poly_recip(edgeptr->v2->y - edgeptr->v1->y);
+ edgeptr->dxdy = (edgeptr->v2->x - edgeptr->v1->x) * ooy;
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ edgeptr->dpdy[paramnum] = (edgeptr->v2->p[paramnum] - edgeptr->v1->p[paramnum]) * ooy;
+ edgeptr++;
+ }
+
+ // determine which list is left/right:
+ // if the first vertex is shared, compare the slopes
+ // if the first vertex is not shared, compare the X coordinates
+ const poly_edge *ledge, *redge;
+ if ((fedgelist[0].v1 == bedgelist[0].v1 && fedgelist[0].dxdy < bedgelist[0].dxdy) ||
+ (fedgelist[0].v1 != bedgelist[0].v1 && fedgelist[0].v1->x < bedgelist[0].v1->x))
+ {
+ ledge = fedgelist;
+ redge = bedgelist;
+ }
+ else
+ {
+ ledge = bedgelist;
+ redge = fedgelist;
+ }
+
+ // compute the X extents for each scanline
+ INT32 pixels = 0;
+ UINT32 startunit = m_unit.count();
+ INT32 scaninc = 1;
+ for (INT32 curscan = minyclip; curscan < maxyclip; curscan += scaninc)
+ {
+ UINT32 bucketnum = ((UINT32)curscan / SCANLINES_PER_BUCKET) % TOTAL_BUCKETS;
+ UINT32 unit_index = m_unit.count();
+ work_unit &unit = m_unit.next();
+
+ // determine how much to advance to hit the next bucket
+ scaninc = SCANLINES_PER_BUCKET - (UINT32)curscan % SCANLINES_PER_BUCKET;
+
+ // fill in the work unit basics
+ unit.polygon = &polygon;
+ unit.count_next = MIN(maxyclip - curscan, scaninc);
+ unit.scanline = curscan;
+ unit.previtem = m_unit_bucket[bucketnum];
+ m_unit_bucket[bucketnum] = unit_index;
+
+ // iterate over extents
+ for (int extnum = 0; extnum < unit.count_next; extnum++)
+ {
+ // compute the ending X based on which part of the triangle we're in
+ _BaseType fully = _BaseType(curscan + extnum) + _BaseType(0.5);
+ while (fully > ledge->v2->y && fully < v[maxv].y)
+ ledge++;
+ while (fully > redge->v2->y && fully < v[maxv].y)
+ redge++;
+ _BaseType startx = ledge->v1->x + (fully - ledge->v1->y) * ledge->dxdy;
+ _BaseType stopx = redge->v1->x + (fully - redge->v1->y) * redge->dxdy;
+
+ // clamp to full pixels
+ INT32 istartx = round_coordinate(startx);
+ INT32 istopx = round_coordinate(stopx);
+
+ // compute parameter starting points and deltas
+ extent_t &extent = unit.extent[extnum];
+ if (paramcount > 0)
+ {
+ _BaseType ldy = fully - ledge->v1->y;
+ _BaseType rdy = fully - redge->v1->y;
+ _BaseType oox = poly_recip(stopx - startx);
+
+ // iterate over parameters
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ _BaseType lparam = ledge->v1->p[paramnum] + ldy * ledge->dpdy[paramnum];
+ _BaseType rparam = redge->v1->p[paramnum] + rdy * redge->dpdy[paramnum];
+ _BaseType dpdx = (rparam - lparam) * oox;
+
+ extent.param[paramnum].start = lparam;// - (_BaseType(istartx) + 0.5f) * dpdx;
+ extent.param[paramnum].dpdx = dpdx;
+ }
+ }
+
+ // include the right edge if requested
+ if (m_flags & POLYFLAG_INCLUDE_RIGHT_EDGE)
+ istopx++;
+
+ // apply left/right clipping
+ if (istartx < cliprect.min_x)
+ {
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ extent.param[paramnum].start += (cliprect.min_x - istartx) * extent.param[paramnum].dpdx;
+ istartx = cliprect.min_x;
+ }
+ if (istopx > cliprect.max_x)
+ istopx = cliprect.max_x + 1;
+
+ // set the extent and update the total pixel count
+ if (istartx >= istopx)
+ istartx = istopx = 0;
+ extent.startx = istartx;
+ extent.stopx = istopx;
+ extent.userdata = NULL;
+ pixels += istopx - istartx;
+ }
+ }
+
+ // enqueue the work items
+ if (m_queue != NULL)
+ osd_work_item_queue_multiple(m_queue, work_item_callback, m_unit.count() - startunit, &m_unit[startunit], m_unit.itemsize(), WORK_ITEM_FLAG_AUTO_RELEASE);
+
+ // return the total number of pixels in the triangle
+ m_quads++;
+ m_pixels += pixels;
+ return pixels;
+}
+
+
+//-------------------------------------------------
+// zclip_if_less - clip a polygon using p[0] as
+// a z coordinate
+//-------------------------------------------------
+
+template<typename _BaseType, class _ObjectData, int _MaxParams, int _MaxPolys>
+int poly_manager<_BaseType, _ObjectData, _MaxParams, _MaxPolys>::zclip_if_less(int numverts, const vertex_t *v, vertex_t *outv, int paramcount, _BaseType clipval)
+{
+ bool prevclipped = (v[numverts - 1].p[0] < clipval);
+ vertex_t *nextout = outv;
+
+ // iterate over vertices
+ for (int vertnum = 0; vertnum < numverts; vertnum++)
+ {
+ bool thisclipped = (v[vertnum].p[0] < clipval);
+
+ // if we switched from clipped to non-clipped, interpolate a vertex
+ if (thisclipped != prevclipped)
+ {
+ const vertex_t &v1 = v[(vertnum == 0) ? (numverts - 1) : (vertnum - 1)];
+ const vertex_t &v2 = v[vertnum];
+ _BaseType frac = (clipval - v1.p[0]) / (v2.p[0] - v1.p[0]);
+ nextout->x = v1.x + frac * (v2.x - v1.x);
+ nextout->y = v1.y + frac * (v2.y - v1.y);
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ nextout->p[paramnum] = v1.p[paramnum] + frac * (v2.p[paramnum] - v1.p[paramnum]);
+ nextout++;
+ }
+
+ // if this vertex is not clipped, copy it in
+ if (!thisclipped)
+ *nextout++ = v[vertnum];
+
+ // remember the last state
+ prevclipped = thisclipped;
+ }
+ return nextout - outv;
+}
+
+#endif // __POLY_H__
diff --git a/src/devices/video/polylgcy.c b/src/devices/video/polylgcy.c
new file mode 100644
index 00000000000..720110e5ca3
--- /dev/null
+++ b/src/devices/video/polylgcy.c
@@ -0,0 +1,1359 @@
+// license:BSD-3-Clause
+// copyright-holders:Ville Linde, Aaron Giles
+/***************************************************************************
+
+ polylgcy.c
+
+ Legacy helper routines for polygon rendering.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "polylgcy.h"
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+/* keep statistics */
+#define KEEP_STATISTICS 0
+
+/* turn this on to log the reasons for any long waits */
+#define LOG_WAITS 0
+
+/* number of profiling ticks before we consider a wait "long" */
+#define LOG_WAIT_THRESHOLD 1000
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define SCANLINES_PER_BUCKET 8
+#define CACHE_LINE_SIZE 64 /* this is a general guess */
+#define TOTAL_BUCKETS (512 / SCANLINES_PER_BUCKET)
+#define UNITS_PER_POLY (100 / SCANLINES_PER_BUCKET)
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* forward definitions */
+struct polygon_info;
+
+
+/* tri_extent describes start/end points for a scanline */
+struct tri_extent
+{
+ INT16 startx; /* starting X coordinate (inclusive) */
+ INT16 stopx; /* ending X coordinate (exclusive) */
+};
+
+
+/* single set of polygon per-parameter data */
+struct poly_param
+{
+ float start; /* parameter value at starting X,Y */
+ float dpdx; /* dp/dx relative to starting X */
+ float dpdy; /* dp/dy relative to starting Y */
+};
+
+
+/* poly edge is used internally for quad rendering */
+struct poly_edge
+{
+ poly_edge * next; /* next edge in sequence */
+ int index; /* index of this edge */
+ const poly_vertex * v1; /* pointer to first vertex */
+ const poly_vertex * v2; /* pointer to second vertex */
+ float dxdy; /* dx/dy along the edge */
+ float dpdy[MAX_VERTEX_PARAMS];/* per-parameter dp/dy values */
+};
+
+
+/* poly section is used internally for quad rendering */
+struct poly_section
+{
+ const poly_edge * ledge; /* pointer to left edge */
+ const poly_edge * redge; /* pointer to right edge */
+ float ybottom; /* bottom of this section */
+};
+
+
+/* work_unit_shared is a common set of data shared between tris and quads */
+struct work_unit_shared
+{
+ polygon_info * polygon; /* pointer to polygon */
+ volatile UINT32 count_next; /* number of scanlines and index of next item to process */
+ INT16 scanline; /* starting scanline and count */
+ UINT16 previtem; /* index of previous item in the same bucket */
+#ifndef PTR64
+ UINT32 dummy; /* pad to 16 bytes */
+#endif
+};
+
+
+/* tri_work_unit is a triangle-specific work-unit */
+struct tri_work_unit
+{
+ work_unit_shared shared; /* shared data */
+ tri_extent extent[SCANLINES_PER_BUCKET]; /* array of scanline extents */
+};
+
+
+/* quad_work_unit is a quad-specific work-unit */
+struct quad_work_unit
+{
+ work_unit_shared shared; /* shared data */
+ poly_extent extent[SCANLINES_PER_BUCKET]; /* array of scanline extents */
+};
+
+
+/* work_unit is a union of the two types */
+union work_unit
+{
+ work_unit_shared shared; /* shared data */
+ tri_work_unit tri; /* triangle work unit */
+ quad_work_unit quad; /* quad work unit */
+};
+
+
+/* polygon_info describes a single polygon, which includes the poly_params */
+struct polygon_info
+{
+ legacy_poly_manager * poly; /* pointer back to the poly manager */
+ void * dest; /* pointer to the destination we are rendering to */
+ void * extra; /* extra data pointer */
+ UINT8 numparams; /* number of parameters for this polygon */
+ UINT8 numverts; /* number of vertices in this polygon */
+ poly_draw_scanline_func callback; /* callback to handle a scanline's worth of work */
+ INT32 xorigin; /* X origin for all parameters */
+ INT32 yorigin; /* Y origin for all parameters */
+ poly_param param[MAX_VERTEX_PARAMS];/* array of parameter data */
+};
+
+
+/* full poly manager description */
+struct legacy_poly_manager
+{
+ /* queue management */
+ osd_work_queue * queue; /* work queue */
+
+ /* triangle work units */
+ work_unit ** unit; /* array of work unit pointers */
+ UINT32 unit_next; /* index of next unit to allocate */
+ UINT32 unit_count; /* number of work units available */
+ size_t unit_size; /* size of each work unit, in bytes */
+
+ /* quad work units */
+ UINT32 quadunit_next; /* index of next unit to allocate */
+ UINT32 quadunit_count; /* number of work units available */
+ size_t quadunit_size; /* size of each work unit, in bytes */
+
+ /* poly data */
+ polygon_info ** polygon; /* array of polygon pointers */
+ UINT32 polygon_next; /* index of next polygon to allocate */
+ UINT32 polygon_count; /* number of polygon items available */
+ size_t polygon_size; /* size of each polygon, in bytes */
+
+ /* extra data */
+ void ** extra; /* array of extra data pointers */
+ UINT32 extra_next; /* index of next extra data to allocate */
+ UINT32 extra_count; /* number of extra data items available */
+ size_t extra_size; /* size of each extra data, in bytes */
+
+ /* misc data */
+ UINT8 flags; /* flags */
+
+ /* buckets */
+ UINT16 unit_bucket[TOTAL_BUCKETS]; /* buckets for tracking unit usage */
+
+ /* statistics */
+ UINT32 triangles; /* number of triangles queued */
+ UINT32 quads; /* number of quads queued */
+ UINT64 pixels; /* number of pixels rendered */
+#if KEEP_STATISTICS
+ UINT32 unit_waits; /* number of times we waited for a unit */
+ UINT32 unit_max; /* maximum units used */
+ UINT32 polygon_waits; /* number of times we waited for a polygon */
+ UINT32 polygon_max; /* maximum polygons used */
+ UINT32 extra_waits; /* number of times we waited for an extra data */
+ UINT32 extra_max; /* maximum extra data used */
+ UINT32 conflicts[WORK_MAX_THREADS]; /* number of conflicts found, per thread */
+ UINT32 resolved[WORK_MAX_THREADS]; /* number of conflicts resolved, per thread */
+#endif
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void **allocate_array(running_machine &machine, size_t *itemsize, UINT32 itemcount);
+static void *poly_item_callback(void *param, int threadid);
+static void poly_state_presave(legacy_poly_manager *poly);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ round_coordinate - round a coordinate to
+ an integer, following rules that 0.5 rounds
+ down
+-------------------------------------------------*/
+
+INLINE INT32 round_coordinate(float value)
+{
+ INT32 result = floor(value);
+ return result + (value - (float)result > 0.5f);
+}
+
+
+/*-------------------------------------------------
+ convert_tri_extent_to_poly_extent - convert
+ a simple tri_extent to a full poly_extent
+-------------------------------------------------*/
+
+INLINE void convert_tri_extent_to_poly_extent(poly_extent *dstextent, const tri_extent *srcextent, const polygon_info *polygon, INT32 y)
+{
+ /* copy start/stop always */
+ dstextent->startx = srcextent->startx;
+ dstextent->stopx = srcextent->stopx;
+
+ /* if we have parameters, process them as well */
+ for (int paramnum = 0; paramnum < polygon->numparams; paramnum++)
+ {
+ dstextent->param[paramnum].start = polygon->param[paramnum].start + srcextent->startx * polygon->param[paramnum].dpdx + y * polygon->param[paramnum].dpdy;
+ dstextent->param[paramnum].dpdx = polygon->param[paramnum].dpdx;
+ }
+}
+
+
+/*-------------------------------------------------
+ interpolate_vertex - interpolate values in
+ a vertex based on p[0] crossing the clipval
+-------------------------------------------------*/
+
+INLINE void interpolate_vertex(poly_vertex *outv, const poly_vertex *v1, const poly_vertex *v2, int paramcount, float clipval)
+{
+ float frac = (clipval - v1->p[0]) / (v2->p[0] - v1->p[0]);
+ int paramnum;
+
+ /* create a new one at the intersection point */
+ outv->x = v1->x + frac * (v2->x - v1->x);
+ outv->y = v1->y + frac * (v2->y - v1->y);
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ outv->p[paramnum] = v1->p[paramnum] + frac * (v2->p[paramnum] - v1->p[paramnum]);
+}
+
+
+/*-------------------------------------------------
+ copy_vertex - copy vertex data from one to
+ another
+-------------------------------------------------*/
+
+INLINE void copy_vertex(poly_vertex *outv, const poly_vertex *v, int paramcount)
+{
+ int paramnum;
+
+ outv->x = v->x;
+ outv->y = v->y;
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ outv->p[paramnum] = v->p[paramnum];
+}
+
+
+/*-------------------------------------------------
+ allocate_polygon - allocate a new polygon
+ object, blocking if we run out
+-------------------------------------------------*/
+
+INLINE polygon_info *allocate_polygon(legacy_poly_manager *poly, int miny, int maxy)
+{
+ /* wait for a work item if we have to */
+ if (poly->polygon_next + 1 > poly->polygon_count)
+ {
+ poly_wait(poly, "Out of polygons");
+#if KEEP_STATISTICS
+ poly->polygon_waits++;
+#endif
+ }
+ else if (poly->unit_next + (maxy - miny) / SCANLINES_PER_BUCKET + 2 > poly->unit_count)
+ {
+ poly_wait(poly, "Out of work units");
+#if KEEP_STATISTICS
+ poly->unit_waits++;
+#endif
+ }
+#if KEEP_STATISTICS
+ poly->polygon_max = MAX(poly->polygon_max, poly->polygon_next + 1);
+#endif
+ return poly->polygon[poly->polygon_next++];
+}
+
+
+
+/***************************************************************************
+ INITIALIZATION/TEARDOWN
+***************************************************************************/
+
+/*-------------------------------------------------
+ poly_alloc - initialize a new polygon
+ manager
+-------------------------------------------------*/
+
+legacy_poly_manager *poly_alloc(running_machine &machine, int max_polys, size_t extra_data_size, UINT8 flags)
+{
+ legacy_poly_manager *poly;
+
+ /* allocate the manager itself */
+ poly = auto_alloc_clear(machine, legacy_poly_manager);
+ poly->flags = flags;
+
+ /* allocate polygons */
+ poly->polygon_size = sizeof(polygon_info);
+ poly->polygon_count = MAX(max_polys, 1);
+ poly->polygon_next = 0;
+ poly->polygon = (polygon_info **)allocate_array(machine, &poly->polygon_size, poly->polygon_count);
+
+ /* allocate extra data */
+ poly->extra_size = extra_data_size;
+ poly->extra_count = poly->polygon_count;
+ poly->extra_next = 1;
+ poly->extra = allocate_array(machine, &poly->extra_size, poly->extra_count);
+
+ /* allocate triangle work units */
+ poly->unit_size = (flags & POLYFLAG_ALLOW_QUADS) ? sizeof(quad_work_unit) : sizeof(tri_work_unit);
+ poly->unit_count = MIN(poly->polygon_count * UNITS_PER_POLY, 65535);
+ poly->unit_next = 0;
+ poly->unit = (work_unit **)allocate_array(machine, &poly->unit_size, poly->unit_count);
+
+ /* create the work queue */
+ if (!(flags & POLYFLAG_NO_WORK_QUEUE))
+ poly->queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_MULTI | WORK_QUEUE_FLAG_HIGH_FREQ);
+
+ /* request a pre-save callback for synchronization */
+ machine.save().register_presave(save_prepost_delegate(FUNC(poly_state_presave), poly));
+ return poly;
+}
+
+
+/*-------------------------------------------------
+ poly_free - free a polygon manager
+-------------------------------------------------*/
+
+void poly_free(legacy_poly_manager *poly)
+{
+#if KEEP_STATISTICS
+{
+ int i, conflicts = 0, resolved = 0;
+ for (i = 0; i < ARRAY_LENGTH(poly->conflicts); i++)
+ {
+ conflicts += poly->conflicts[i];
+ resolved += poly->resolved[i];
+ }
+ printf("Total triangles = %d\n", poly->triangles);
+ printf("Total quads = %d\n", poly->quads);
+ if (poly->pixels > 1000000000)
+ printf("Total pixels = %d%09d\n", (UINT32)(poly->pixels / 1000000000), (UINT32)(poly->pixels % 1000000000));
+ else
+ printf("Total pixels = %d\n", (UINT32)poly->pixels);
+ printf("Conflicts: %d resolved, %d total\n", resolved, conflicts);
+ printf("Units: %5d used, %5d allocated, %5d waits, %4d bytes each, %7d total\n", poly->unit_max, poly->unit_count, poly->unit_waits, poly->unit_size, poly->unit_count * poly->unit_size);
+ printf("Polygons: %5d used, %5d allocated, %5d waits, %4d bytes each, %7d total\n", poly->polygon_max, poly->polygon_count, poly->polygon_waits, poly->polygon_size, poly->polygon_count * poly->polygon_size);
+ printf("Extra data: %5d used, %5d allocated, %5d waits, %4d bytes each, %7d total\n", poly->extra_max, poly->extra_count, poly->extra_waits, poly->extra_size, poly->extra_count * poly->extra_size);
+}
+#endif
+
+ /* free the work queue */
+ if (poly->queue != NULL)
+ osd_work_queue_free(poly->queue);
+}
+
+
+
+/***************************************************************************
+ COMMON FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ poly_wait - wait for all pending rendering
+ to complete
+-------------------------------------------------*/
+
+void poly_wait(legacy_poly_manager *poly, const char *debug_reason)
+{
+ osd_ticks_t time;
+
+ /* remember the start time if we're logging */
+ if (LOG_WAITS)
+ time = get_profile_ticks();
+
+ /* wait for all pending work items to complete */
+ if (poly->queue != NULL)
+ osd_work_queue_wait(poly->queue, osd_ticks_per_second() * 100);
+
+ /* if we don't have a queue, just run the whole list now */
+ else
+ {
+ int unitnum;
+ for (unitnum = 0; unitnum < poly->unit_next; unitnum++)
+ poly_item_callback(poly->unit[unitnum], 0);
+ }
+
+ /* log any long waits */
+ if (LOG_WAITS)
+ {
+ time = get_profile_ticks() - time;
+ if (time > LOG_WAIT_THRESHOLD)
+ logerror("Poly:Waited %d cycles for %s\n", (int)time, debug_reason);
+ }
+
+ /* reset the state */
+ poly->polygon_next = poly->unit_next = 0;
+ memset(poly->unit_bucket, 0xff, sizeof(poly->unit_bucket));
+
+ /* we need to preserve the last extra data that was supplied */
+ if (poly->extra_next > 1)
+ memcpy(poly->extra[0], poly->extra[poly->extra_next - 1], poly->extra_size);
+ poly->extra_next = 1;
+}
+
+
+/*-------------------------------------------------
+ poly_get_extra_data - get a pointer to the
+ extra data for the next polygon
+-------------------------------------------------*/
+
+void *poly_get_extra_data(legacy_poly_manager *poly)
+{
+ /* wait for a work item if we have to */
+ if (poly->extra_next + 1 > poly->extra_count)
+ {
+ poly_wait(poly, "Out of extra data");
+#if KEEP_STATISTICS
+ poly->extra_waits++;
+#endif
+ }
+
+ /* return a pointer to the extra data for the next item */
+#if KEEP_STATISTICS
+ poly->extra_max = MAX(poly->extra_max, poly->extra_next + 1);
+#endif
+ return poly->extra[poly->extra_next++];
+}
+
+
+
+/***************************************************************************
+ CORE TRIANGLE RENDERING
+***************************************************************************/
+
+/*-------------------------------------------------
+ poly_render_triangle - render a single
+ triangle given 3 vertexes
+-------------------------------------------------*/
+
+UINT32 poly_render_triangle(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, const poly_vertex *v1, const poly_vertex *v2, const poly_vertex *v3)
+{
+ float dxdy_v1v2, dxdy_v1v3, dxdy_v2v3;
+ const poly_vertex *tv;
+ INT32 curscan, scaninc;
+ polygon_info *polygon;
+ INT32 v1yclip, v3yclip;
+ INT32 v1y, v3y, v1x;
+ INT32 pixels = 0;
+ UINT32 startunit;
+
+ /* first sort by Y */
+ if (v2->y < v1->y)
+ {
+ tv = v1;
+ v1 = v2;
+ v2 = tv;
+ }
+ if (v3->y < v2->y)
+ {
+ tv = v2;
+ v2 = v3;
+ v3 = tv;
+ if (v2->y < v1->y)
+ {
+ tv = v1;
+ v1 = v2;
+ v2 = tv;
+ }
+ }
+
+ /* compute some integral X/Y vertex values */
+ v1x = round_coordinate(v1->x);
+ v1y = round_coordinate(v1->y);
+ v3y = round_coordinate(v3->y);
+
+ /* clip coordinates */
+ v1yclip = v1y;
+ v3yclip = v3y + ((poly->flags & POLYFLAG_INCLUDE_BOTTOM_EDGE) ? 1 : 0);
+ v1yclip = MAX(v1yclip, cliprect.min_y);
+ v3yclip = MIN(v3yclip, cliprect.max_y + 1);
+ if (v3yclip - v1yclip <= 0)
+ return 0;
+
+ /* allocate a new polygon */
+ polygon = allocate_polygon(poly, v1yclip, v3yclip);
+
+ /* fill in the polygon information */
+ polygon->poly = poly;
+ polygon->dest = dest;
+ polygon->callback = callback;
+ polygon->extra = poly->extra[poly->extra_next - 1];
+ polygon->numparams = paramcount;
+ polygon->numverts = 3;
+
+ /* set the start X/Y coordinates */
+ polygon->xorigin = v1x;
+ polygon->yorigin = v1y;
+
+ /* compute the slopes for each portion of the triangle */
+ dxdy_v1v2 = (v2->y == v1->y) ? 0.0f : (v2->x - v1->x) / (v2->y - v1->y);
+ dxdy_v1v3 = (v3->y == v1->y) ? 0.0f : (v3->x - v1->x) / (v3->y - v1->y);
+ dxdy_v2v3 = (v3->y == v2->y) ? 0.0f : (v3->x - v2->x) / (v3->y - v2->y);
+
+ /* compute the X extents for each scanline */
+ startunit = poly->unit_next;
+ for (curscan = v1yclip; curscan < v3yclip; curscan += scaninc)
+ {
+ UINT32 bucketnum = ((UINT32)curscan / SCANLINES_PER_BUCKET) % TOTAL_BUCKETS;
+ UINT32 unit_index = poly->unit_next++;
+ tri_work_unit *unit = &poly->unit[unit_index]->tri;
+ int extnum;
+
+ /* determine how much to advance to hit the next bucket */
+ scaninc = SCANLINES_PER_BUCKET - (UINT32)curscan % SCANLINES_PER_BUCKET;
+
+ /* fill in the work unit basics */
+ unit->shared.polygon = polygon;
+ unit->shared.count_next = MIN(v3yclip - curscan, scaninc);
+ unit->shared.scanline = curscan;
+ unit->shared.previtem = poly->unit_bucket[bucketnum];
+ poly->unit_bucket[bucketnum] = unit_index;
+
+ /* iterate over extents */
+ for (extnum = 0; extnum < unit->shared.count_next; extnum++)
+ {
+ float fully = (float)(curscan + extnum) + 0.5f;
+ float startx = v1->x + (fully - v1->y) * dxdy_v1v3;
+ float stopx;
+ INT32 istartx, istopx;
+
+ /* compute the ending X based on which part of the triangle we're in */
+ if (fully < v2->y)
+ stopx = v1->x + (fully - v1->y) * dxdy_v1v2;
+ else
+ stopx = v2->x + (fully - v2->y) * dxdy_v2v3;
+
+ /* clamp to full pixels */
+ istartx = round_coordinate(startx);
+ istopx = round_coordinate(stopx);
+
+ /* force start < stop */
+ if (istartx > istopx)
+ {
+ INT32 temp = istartx;
+ istartx = istopx;
+ istopx = temp;
+ }
+
+ /* include the right edge if requested */
+ if (poly->flags & POLYFLAG_INCLUDE_RIGHT_EDGE)
+ istopx++;
+
+ /* apply left/right clipping */
+ if (istartx < cliprect.min_x)
+ istartx = cliprect.min_x;
+ if (istopx > cliprect.max_x)
+ istopx = cliprect.max_x + 1;
+
+ /* set the extent and update the total pixel count */
+ if (istartx >= istopx)
+ istartx = istopx = 0;
+ unit->extent[extnum].startx = istartx;
+ unit->extent[extnum].stopx = istopx;
+ pixels += istopx - istartx;
+ }
+ }
+#if KEEP_STATISTICS
+ poly->unit_max = MAX(poly->unit_max, poly->unit_next);
+#endif
+
+ /* compute parameter starting points and deltas */
+ if (paramcount > 0)
+ {
+ float a00 = v2->y - v3->y;
+ float a01 = v3->x - v2->x;
+ float a02 = v2->x*v3->y - v3->x*v2->y;
+ float a10 = v3->y - v1->y;
+ float a11 = v1->x - v3->x;
+ float a12 = v3->x*v1->y - v1->x*v3->y;
+ float a20 = v1->y - v2->y;
+ float a21 = v2->x - v1->x;
+ float a22 = v1->x*v2->y - v2->x*v1->y;
+ float det = a02 + a12 + a22;
+
+ if(fabsf(det) < 0.001f) {
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ poly_param *params = &polygon->param[paramnum];
+ params->dpdx = 0;
+ params->dpdy = 0;
+ params->start = v1->p[paramnum];
+ }
+ }
+ else
+ {
+ float idet = 1/det;
+ for (int paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ poly_param *params = &polygon->param[paramnum];
+ params->dpdx = idet*(v1->p[paramnum]*a00 + v2->p[paramnum]*a10 + v3->p[paramnum]*a20);
+ params->dpdy = idet*(v1->p[paramnum]*a01 + v2->p[paramnum]*a11 + v3->p[paramnum]*a21);
+ params->start = idet*(v1->p[paramnum]*a02 + v2->p[paramnum]*a12 + v3->p[paramnum]*a22);
+ }
+ }
+ }
+
+ /* enqueue the work items */
+ if (poly->queue != NULL)
+ osd_work_item_queue_multiple(poly->queue, poly_item_callback, poly->unit_next - startunit, poly->unit[startunit], poly->unit_size, WORK_ITEM_FLAG_AUTO_RELEASE);
+
+ /* return the total number of pixels in the triangle */
+ poly->triangles++;
+ poly->pixels += pixels;
+ return pixels;
+}
+
+
+/*-------------------------------------------------
+ poly_render_triangle_fan - render a set of
+ triangles in a fan
+-------------------------------------------------*/
+
+UINT32 poly_render_triangle_fan(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, int numverts, const poly_vertex *v)
+{
+ UINT32 pixels = 0;
+ int vertnum;
+
+ /* iterate over vertices */
+ for (vertnum = 2; vertnum < numverts; vertnum++)
+ pixels += poly_render_triangle(poly, dest, cliprect, callback, paramcount, &v[0], &v[vertnum - 1], &v[vertnum]);
+ return pixels;
+}
+
+
+/*-------------------------------------------------
+ poly_render_triangle_custom - perform a custom
+ render of an object, given specific extents
+-------------------------------------------------*/
+
+UINT32 poly_render_triangle_custom(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int startscanline, int numscanlines, const poly_extent *extents)
+{
+ INT32 curscan, scaninc;
+ polygon_info *polygon;
+ INT32 v1yclip, v3yclip;
+ INT32 pixels = 0;
+ UINT32 startunit;
+
+ /* clip coordinates */
+ v1yclip = MAX(startscanline, cliprect.min_y);
+ v3yclip = MIN(startscanline + numscanlines, cliprect.max_y + 1);
+ if (v3yclip - v1yclip <= 0)
+ return 0;
+
+ /* allocate a new polygon */
+ polygon = allocate_polygon(poly, v1yclip, v3yclip);
+
+ /* fill in the polygon information */
+ polygon->poly = poly;
+ polygon->dest = dest;
+ polygon->callback = callback;
+ polygon->extra = poly->extra[poly->extra_next - 1];
+ polygon->numparams = 0;
+ polygon->numverts = 3;
+
+ /* compute the X extents for each scanline */
+ startunit = poly->unit_next;
+ for (curscan = v1yclip; curscan < v3yclip; curscan += scaninc)
+ {
+ UINT32 bucketnum = ((UINT32)curscan / SCANLINES_PER_BUCKET) % TOTAL_BUCKETS;
+ UINT32 unit_index = poly->unit_next++;
+ tri_work_unit *unit = &poly->unit[unit_index]->tri;
+ int extnum;
+
+ /* determine how much to advance to hit the next bucket */
+ scaninc = SCANLINES_PER_BUCKET - (UINT32)curscan % SCANLINES_PER_BUCKET;
+
+ /* fill in the work unit basics */
+ unit->shared.polygon = polygon;
+ unit->shared.count_next = MIN(v3yclip - curscan, scaninc);
+ unit->shared.scanline = curscan;
+ unit->shared.previtem = poly->unit_bucket[bucketnum];
+ poly->unit_bucket[bucketnum] = unit_index;
+
+ /* iterate over extents */
+ for (extnum = 0; extnum < unit->shared.count_next; extnum++)
+ {
+ const poly_extent *extent = &extents[(curscan + extnum) - startscanline];
+ INT32 istartx = extent->startx, istopx = extent->stopx;
+
+ /* force start < stop */
+ if (istartx > istopx)
+ {
+ INT32 temp = istartx;
+ istartx = istopx;
+ istopx = temp;
+ }
+
+ /* apply left/right clipping */
+ if (istartx < cliprect.min_x)
+ istartx = cliprect.min_x;
+ if (istopx > cliprect.max_x)
+ istopx = cliprect.max_x + 1;
+
+ /* set the extent and update the total pixel count */
+ unit->extent[extnum].startx = istartx;
+ unit->extent[extnum].stopx = istopx;
+ if (istartx < istopx)
+ pixels += istopx - istartx;
+ }
+ }
+#if KEEP_STATISTICS
+ poly->unit_max = MAX(poly->unit_max, poly->unit_next);
+#endif
+
+ /* enqueue the work items */
+ if (poly->queue != NULL)
+ osd_work_item_queue_multiple(poly->queue, poly_item_callback, poly->unit_next - startunit, poly->unit[startunit], poly->unit_size, WORK_ITEM_FLAG_AUTO_RELEASE);
+
+ /* return the total number of pixels in the object */
+ poly->triangles++;
+ poly->pixels += pixels;
+ return pixels;
+}
+
+
+
+/***************************************************************************
+ CORE QUAD RENDERING
+***************************************************************************/
+
+/*-------------------------------------------------
+ poly_render_quad - render a single quad
+ given 4 vertexes
+-------------------------------------------------*/
+
+UINT32 poly_render_quad(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, const poly_vertex *v1, const poly_vertex *v2, const poly_vertex *v3, const poly_vertex *v4)
+{
+ poly_edge fedgelist[3], bedgelist[3];
+ const poly_edge *ledge, *redge;
+ const poly_vertex *v[4];
+ poly_edge *edgeptr;
+ int minv, maxv, curv;
+ INT32 minyclip, maxyclip;
+ INT32 miny, maxy;
+ INT32 curscan, scaninc;
+ polygon_info *polygon;
+ INT32 pixels = 0;
+ UINT32 startunit;
+
+ assert(poly->flags & POLYFLAG_ALLOW_QUADS);
+
+ /* arrays make things easier */
+ v[0] = v1;
+ v[1] = v2;
+ v[2] = v3;
+ v[3] = v4;
+
+ /* determine min/max Y vertices */
+ if (v[1]->y < v[0]->y)
+ minv = 1, maxv = 0;
+ else
+ minv = 0, maxv = 1;
+ if (v[2]->y < v[minv]->y)
+ minv = 2;
+ else if (v[2]->y > v[maxv]->y)
+ maxv = 2;
+ if (v[3]->y < v[minv]->y)
+ minv = 3;
+ else if (v[3]->y > v[maxv]->y)
+ maxv = 3;
+
+ /* determine start/end scanlines */
+ miny = round_coordinate(v[minv]->y);
+ maxy = round_coordinate(v[maxv]->y);
+
+ /* clip coordinates */
+ minyclip = miny;
+ maxyclip = maxy + ((poly->flags & POLYFLAG_INCLUDE_BOTTOM_EDGE) ? 1 : 0);
+ minyclip = MAX(minyclip, cliprect.min_y);
+ maxyclip = MIN(maxyclip, cliprect.max_y + 1);
+ if (maxyclip - minyclip <= 0)
+ return 0;
+
+ /* allocate a new polygon */
+ polygon = allocate_polygon(poly, minyclip, maxyclip);
+
+ /* fill in the polygon information */
+ polygon->poly = poly;
+ polygon->dest = dest;
+ polygon->callback = callback;
+ polygon->extra = poly->extra[poly->extra_next - 1];
+ polygon->numparams = paramcount;
+ polygon->numverts = 4;
+
+ /* walk forward to build up the forward edge list */
+ edgeptr = &fedgelist[0];
+ for (curv = minv; curv != maxv; curv = (curv + 1) & 3)
+ {
+ int paramnum;
+ float ooy;
+
+ /* set the two vertices */
+ edgeptr->v1 = v[curv];
+ edgeptr->v2 = v[(curv + 1) & 3];
+
+ /* if horizontal, skip altogether */
+ if (edgeptr->v1->y == edgeptr->v2->y)
+ continue;
+
+ /* need dx/dy always, and parameter deltas as necessary */
+ ooy = 1.0f / (edgeptr->v2->y - edgeptr->v1->y);
+ edgeptr->dxdy = (edgeptr->v2->x - edgeptr->v1->x) * ooy;
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ edgeptr->dpdy[paramnum] = (edgeptr->v2->p[paramnum] - edgeptr->v1->p[paramnum]) * ooy;
+ edgeptr++;
+ }
+
+ /* walk backward to build up the backward edge list */
+ edgeptr = &bedgelist[0];
+ for (curv = minv; curv != maxv; curv = (curv - 1) & 3)
+ {
+ int paramnum;
+ float ooy;
+
+ /* set the two vertices */
+ edgeptr->v1 = v[curv];
+ edgeptr->v2 = v[(curv - 1) & 3];
+
+ /* if horizontal, skip altogether */
+ if (edgeptr->v1->y == edgeptr->v2->y)
+ continue;
+
+ /* need dx/dy always, and parameter deltas as necessary */
+ ooy = 1.0f / (edgeptr->v2->y - edgeptr->v1->y);
+ edgeptr->dxdy = (edgeptr->v2->x - edgeptr->v1->x) * ooy;
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ edgeptr->dpdy[paramnum] = (edgeptr->v2->p[paramnum] - edgeptr->v1->p[paramnum]) * ooy;
+ edgeptr++;
+ }
+
+ /* determine which list is left/right: */
+ /* if the first vertex is shared, compare the slopes */
+ /* if the first vertex is not shared, compare the X coordinates */
+ if ((fedgelist[0].v1 == bedgelist[0].v1 && fedgelist[0].dxdy < bedgelist[0].dxdy) ||
+ (fedgelist[0].v1 != bedgelist[0].v1 && fedgelist[0].v1->x < bedgelist[0].v1->x))
+ {
+ ledge = fedgelist;
+ redge = bedgelist;
+ }
+ else
+ {
+ ledge = bedgelist;
+ redge = fedgelist;
+ }
+
+ /* compute the X extents for each scanline */
+ startunit = poly->unit_next;
+ for (curscan = minyclip; curscan < maxyclip; curscan += scaninc)
+ {
+ UINT32 bucketnum = ((UINT32)curscan / SCANLINES_PER_BUCKET) % TOTAL_BUCKETS;
+ UINT32 unit_index = poly->unit_next++;
+ quad_work_unit *unit = &poly->unit[unit_index]->quad;
+ int extnum;
+
+ /* determine how much to advance to hit the next bucket */
+ scaninc = SCANLINES_PER_BUCKET - (UINT32)curscan % SCANLINES_PER_BUCKET;
+
+ /* fill in the work unit basics */
+ unit->shared.polygon = polygon;
+ unit->shared.count_next = MIN(maxyclip - curscan, scaninc);
+ unit->shared.scanline = curscan;
+ unit->shared.previtem = poly->unit_bucket[bucketnum];
+ poly->unit_bucket[bucketnum] = unit_index;
+
+ /* iterate over extents */
+ for (extnum = 0; extnum < unit->shared.count_next; extnum++)
+ {
+ float fully = (float)(curscan + extnum) + 0.5f;
+ float startx, stopx;
+ INT32 istartx, istopx;
+ int paramnum;
+
+ /* compute the ending X based on which part of the triangle we're in */
+ while (fully > ledge->v2->y && fully < v[maxv]->y)
+ ledge++;
+ while (fully > redge->v2->y && fully < v[maxv]->y)
+ redge++;
+ startx = ledge->v1->x + (fully - ledge->v1->y) * ledge->dxdy;
+ stopx = redge->v1->x + (fully - redge->v1->y) * redge->dxdy;
+
+ /* clamp to full pixels */
+ istartx = round_coordinate(startx);
+ istopx = round_coordinate(stopx);
+
+ /* compute parameter starting points and deltas */
+ if (paramcount > 0)
+ {
+ float ldy = fully - ledge->v1->y;
+ float rdy = fully - redge->v1->y;
+ float oox = 1.0f / (stopx - startx);
+
+ /* iterate over parameters */
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ float lparam = ledge->v1->p[paramnum] + ldy * ledge->dpdy[paramnum];
+ float rparam = redge->v1->p[paramnum] + rdy * redge->dpdy[paramnum];
+ float dpdx = (rparam - lparam) * oox;
+
+ unit->extent[extnum].param[paramnum].start = lparam;// - ((float)istartx + 0.5f) * dpdx;
+ unit->extent[extnum].param[paramnum].dpdx = dpdx;
+ }
+ }
+
+ /* include the right edge if requested */
+ if (poly->flags & POLYFLAG_INCLUDE_RIGHT_EDGE)
+ istopx++;
+
+ /* apply left/right clipping */
+ if (istartx < cliprect.min_x)
+ {
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ unit->extent[extnum].param[paramnum].start += (cliprect.min_x - istartx) * unit->extent[extnum].param[paramnum].dpdx;
+ istartx = cliprect.min_x;
+ }
+ if (istopx > cliprect.max_x)
+ istopx = cliprect.max_x + 1;
+
+ /* set the extent and update the total pixel count */
+ if (istartx >= istopx)
+ istartx = istopx = 0;
+ unit->extent[extnum].startx = istartx;
+ unit->extent[extnum].stopx = istopx;
+ pixels += istopx - istartx;
+ }
+ }
+#if KEEP_STATISTICS
+ poly->unit_max = MAX(poly->unit_max, poly->unit_next);
+#endif
+
+ /* enqueue the work items */
+ if (poly->queue != NULL)
+ osd_work_item_queue_multiple(poly->queue, poly_item_callback, poly->unit_next - startunit, poly->unit[startunit], poly->unit_size, WORK_ITEM_FLAG_AUTO_RELEASE);
+
+ /* return the total number of pixels in the triangle */
+ poly->quads++;
+ poly->pixels += pixels;
+ return pixels;
+}
+
+
+/*-------------------------------------------------
+ poly_render_quad_fan - render a set of
+ quads in a fan
+-------------------------------------------------*/
+
+UINT32 poly_render_quad_fan(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, int numverts, const poly_vertex *v)
+{
+ UINT32 pixels = 0;
+ int vertnum;
+
+ /* iterate over vertices */
+ for (vertnum = 2; vertnum < numverts; vertnum += 2)
+ pixels += poly_render_quad(poly, dest, cliprect, callback, paramcount, &v[0], &v[vertnum - 1], &v[vertnum], &v[MIN(vertnum + 1, numverts - 1)]);
+ return pixels;
+}
+
+
+
+/***************************************************************************
+ CORE POLYGON RENDERING
+***************************************************************************/
+
+/*-------------------------------------------------
+ poly_render_polygon - render a single polygon up
+ to 32 vertices
+-------------------------------------------------*/
+
+UINT32 poly_render_polygon(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, int numverts, const poly_vertex *v)
+{
+ poly_edge fedgelist[MAX_POLYGON_VERTS - 1], bedgelist[MAX_POLYGON_VERTS - 1];
+ const poly_edge *ledge, *redge;
+ poly_edge *edgeptr;
+ int minv, maxv, curv;
+ INT32 minyclip, maxyclip;
+ INT32 miny, maxy;
+ INT32 curscan, scaninc;
+ polygon_info *polygon;
+ INT32 pixels = 0;
+ UINT32 startunit;
+ int vertnum;
+
+ assert(poly->flags & POLYFLAG_ALLOW_QUADS);
+
+ /* determine min/max Y vertices */
+ minv = maxv = 0;
+ for (vertnum = 1; vertnum < numverts; vertnum++)
+ {
+ if (v[vertnum].y < v[minv].y)
+ minv = vertnum;
+ else if (v[vertnum].y > v[maxv].y)
+ maxv = vertnum;
+ }
+
+ /* determine start/end scanlines */
+ miny = round_coordinate(v[minv].y);
+ maxy = round_coordinate(v[maxv].y);
+
+ /* clip coordinates */
+ minyclip = miny;
+ maxyclip = maxy + ((poly->flags & POLYFLAG_INCLUDE_BOTTOM_EDGE) ? 1 : 0);
+ minyclip = MAX(minyclip, cliprect.min_y);
+ maxyclip = MIN(maxyclip, cliprect.max_y + 1);
+ if (maxyclip - minyclip <= 0)
+ return 0;
+
+ /* allocate a new polygon */
+ polygon = allocate_polygon(poly, minyclip, maxyclip);
+
+ /* fill in the polygon information */
+ polygon->poly = poly;
+ polygon->dest = dest;
+ polygon->callback = callback;
+ polygon->extra = poly->extra[poly->extra_next - 1];
+ polygon->numparams = paramcount;
+ polygon->numverts = numverts;
+
+ /* walk forward to build up the forward edge list */
+ edgeptr = &fedgelist[0];
+ for (curv = minv; curv != maxv; curv = (curv == numverts - 1) ? 0 : (curv + 1))
+ {
+ int paramnum;
+ float ooy;
+
+ /* set the two vertices */
+ edgeptr->v1 = &v[curv];
+ edgeptr->v2 = &v[(curv == numverts - 1) ? 0 : (curv + 1)];
+
+ /* if horizontal, skip altogether */
+ if (edgeptr->v1->y == edgeptr->v2->y)
+ continue;
+
+ /* need dx/dy always, and parameter deltas as necessary */
+ ooy = 1.0f / (edgeptr->v2->y - edgeptr->v1->y);
+ edgeptr->dxdy = (edgeptr->v2->x - edgeptr->v1->x) * ooy;
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ edgeptr->dpdy[paramnum] = (edgeptr->v2->p[paramnum] - edgeptr->v1->p[paramnum]) * ooy;
+ edgeptr++;
+ }
+
+ /* walk backward to build up the backward edge list */
+ edgeptr = &bedgelist[0];
+ for (curv = minv; curv != maxv; curv = (curv == 0) ? (numverts - 1) : (curv - 1))
+ {
+ int paramnum;
+ float ooy;
+
+ /* set the two vertices */
+ edgeptr->v1 = &v[curv];
+ edgeptr->v2 = &v[(curv == 0) ? (numverts - 1) : (curv - 1)];
+
+ /* if horizontal, skip altogether */
+ if (edgeptr->v1->y == edgeptr->v2->y)
+ continue;
+
+ /* need dx/dy always, and parameter deltas as necessary */
+ ooy = 1.0f / (edgeptr->v2->y - edgeptr->v1->y);
+ edgeptr->dxdy = (edgeptr->v2->x - edgeptr->v1->x) * ooy;
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ edgeptr->dpdy[paramnum] = (edgeptr->v2->p[paramnum] - edgeptr->v1->p[paramnum]) * ooy;
+ edgeptr++;
+ }
+
+ /* determine which list is left/right: */
+ /* if the first vertex is shared, compare the slopes */
+ /* if the first vertex is not shared, compare the X coordinates */
+ if ((fedgelist[0].v1 == bedgelist[0].v1 && fedgelist[0].dxdy < bedgelist[0].dxdy) ||
+ (fedgelist[0].v1 != bedgelist[0].v1 && fedgelist[0].v1->x < bedgelist[0].v1->x))
+ {
+ ledge = fedgelist;
+ redge = bedgelist;
+ }
+ else
+ {
+ ledge = bedgelist;
+ redge = fedgelist;
+ }
+
+ /* compute the X extents for each scanline */
+ startunit = poly->unit_next;
+ for (curscan = minyclip; curscan < maxyclip; curscan += scaninc)
+ {
+ UINT32 bucketnum = ((UINT32)curscan / SCANLINES_PER_BUCKET) % TOTAL_BUCKETS;
+ UINT32 unit_index = poly->unit_next++;
+ quad_work_unit *unit = &poly->unit[unit_index]->quad;
+ int extnum;
+
+ /* determine how much to advance to hit the next bucket */
+ scaninc = SCANLINES_PER_BUCKET - (UINT32)curscan % SCANLINES_PER_BUCKET;
+
+ /* fill in the work unit basics */
+ unit->shared.polygon = polygon;
+ unit->shared.count_next = MIN(maxyclip - curscan, scaninc);
+ unit->shared.scanline = curscan;
+ unit->shared.previtem = poly->unit_bucket[bucketnum];
+ poly->unit_bucket[bucketnum] = unit_index;
+
+ /* iterate over extents */
+ for (extnum = 0; extnum < unit->shared.count_next; extnum++)
+ {
+ float fully = (float)(curscan + extnum) + 0.5f;
+ float startx, stopx;
+ INT32 istartx, istopx;
+ int paramnum;
+
+ /* compute the ending X based on which part of the triangle we're in */
+ while (fully > ledge->v2->y && fully < v[maxv].y)
+ ledge++;
+ while (fully > redge->v2->y && fully < v[maxv].y)
+ redge++;
+ startx = ledge->v1->x + (fully - ledge->v1->y) * ledge->dxdy;
+ stopx = redge->v1->x + (fully - redge->v1->y) * redge->dxdy;
+
+ /* clamp to full pixels */
+ istartx = round_coordinate(startx);
+ istopx = round_coordinate(stopx);
+
+ /* compute parameter starting points and deltas */
+ if (paramcount > 0)
+ {
+ float ldy = fully - ledge->v1->y;
+ float rdy = fully - redge->v1->y;
+ float oox = 1.0f / (stopx - startx);
+
+ /* iterate over parameters */
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ {
+ float lparam = ledge->v1->p[paramnum] + ldy * ledge->dpdy[paramnum];
+ float rparam = redge->v1->p[paramnum] + rdy * redge->dpdy[paramnum];
+ float dpdx = (rparam - lparam) * oox;
+
+ unit->extent[extnum].param[paramnum].start = lparam;// - ((float)istartx + 0.5f) * dpdx;
+ unit->extent[extnum].param[paramnum].dpdx = dpdx;
+ }
+ }
+
+ /* include the right edge if requested */
+ if (poly->flags & POLYFLAG_INCLUDE_RIGHT_EDGE)
+ istopx++;
+
+ /* apply left/right clipping */
+ if (istartx < cliprect.min_x)
+ {
+ for (paramnum = 0; paramnum < paramcount; paramnum++)
+ unit->extent[extnum].param[paramnum].start += (cliprect.min_x - istartx) * unit->extent[extnum].param[paramnum].dpdx;
+ istartx = cliprect.min_x;
+ }
+ if (istopx > cliprect.max_x)
+ istopx = cliprect.max_x + 1;
+
+ /* set the extent and update the total pixel count */
+ if (istartx >= istopx)
+ istartx = istopx = 0;
+ unit->extent[extnum].startx = istartx;
+ unit->extent[extnum].stopx = istopx;
+ pixels += istopx - istartx;
+ }
+ }
+#if KEEP_STATISTICS
+ poly->unit_max = MAX(poly->unit_max, poly->unit_next);
+#endif
+
+ /* enqueue the work items */
+ if (poly->queue != NULL)
+ osd_work_item_queue_multiple(poly->queue, poly_item_callback, poly->unit_next - startunit, poly->unit[startunit], poly->unit_size, WORK_ITEM_FLAG_AUTO_RELEASE);
+
+ /* return the total number of pixels in the triangle */
+ poly->quads++;
+ poly->pixels += pixels;
+ return pixels;
+}
+
+
+
+/***************************************************************************
+ CLIPPING
+***************************************************************************/
+
+/*-------------------------------------------------
+ poly_zclip_if_less - z clip a polygon against
+ the given value, returning a set of clipped
+ vertices
+-------------------------------------------------*/
+
+int poly_zclip_if_less(int numverts, const poly_vertex *v, poly_vertex *outv, int paramcount, float clipval)
+{
+ int prevclipped = (v[numverts - 1].p[0] < clipval);
+ poly_vertex *nextout = outv;
+ int vertnum;
+
+ /* iterate over vertices */
+ for (vertnum = 0; vertnum < numverts; vertnum++)
+ {
+ int thisclipped = (v[vertnum].p[0] < clipval);
+
+ /* if we switched from clipped to non-clipped, interpolate a vertex */
+ if (thisclipped != prevclipped)
+ interpolate_vertex(nextout++, &v[(vertnum == 0) ? (numverts - 1) : (vertnum - 1)], &v[vertnum], paramcount, clipval);
+
+ /* if this vertex is not clipped, copy it in */
+ if (!thisclipped)
+ copy_vertex(nextout++, &v[vertnum], paramcount);
+
+ /* remember the last state */
+ prevclipped = thisclipped;
+ }
+ return nextout - outv;
+}
+
+
+
+/***************************************************************************
+ INTERNAL FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ allocate_array - allocate an array of pointers
+-------------------------------------------------*/
+
+static void **allocate_array(running_machine &machine, size_t *itemsize, UINT32 itemcount)
+{
+ void **ptrarray;
+ int itemnum;
+
+ /* fail if 0 */
+ if (itemcount == 0)
+ return NULL;
+
+ /* round to a cache line boundary */
+ *itemsize = ((*itemsize + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE) * CACHE_LINE_SIZE;
+
+ /* allocate the array */
+ ptrarray = auto_alloc_array_clear(machine, void *, itemcount);
+
+ /* allocate the actual items */
+ ptrarray[0] = auto_alloc_array_clear(machine, UINT8, *itemsize * itemcount);
+
+ /* initialize the pointer array */
+ for (itemnum = 1; itemnum < itemcount; itemnum++)
+ ptrarray[itemnum] = (UINT8 *)ptrarray[0] + *itemsize * itemnum;
+ return ptrarray;
+}
+
+
+/*-------------------------------------------------
+ poly_item_callback - callback for each poly
+ item
+-------------------------------------------------*/
+
+static void *poly_item_callback(void *param, int threadid)
+{
+ while (1)
+ {
+ work_unit *unit = (work_unit *)param;
+ polygon_info *polygon = unit->shared.polygon;
+ int count = unit->shared.count_next & 0xffff;
+ UINT32 orig_count_next;
+ int curscan;
+
+ /* if our previous item isn't done yet, enqueue this item to the end and proceed */
+ if (unit->shared.previtem != 0xffff)
+ {
+ work_unit *prevunit = polygon->poly->unit[unit->shared.previtem];
+ if (prevunit->shared.count_next != 0)
+ {
+ UINT32 unitnum = ((UINT8 *)unit - (UINT8 *)polygon->poly->unit[0]) / polygon->poly->unit_size;
+ UINT32 new_count_next;
+
+ /* attempt to atomically swap in this new value */
+ do
+ {
+ orig_count_next = prevunit->shared.count_next;
+ new_count_next = orig_count_next | (unitnum << 16);
+ } while (compare_exchange32((volatile INT32 *)&prevunit->shared.count_next, orig_count_next, new_count_next) != orig_count_next);
+
+#if KEEP_STATISTICS
+ /* track resolved conflicts */
+ polygon->poly->conflicts[threadid]++;
+ if (orig_count_next != 0)
+ polygon->poly->resolved[threadid]++;
+#endif
+ /* if we succeeded, skip out early so we can do other work */
+ if (orig_count_next != 0)
+ break;
+ }
+ }
+
+ /* iterate over extents */
+ for (curscan = 0; curscan < count; curscan++)
+ {
+ if (polygon->numverts == 3)
+ {
+ poly_extent tmpextent;
+ convert_tri_extent_to_poly_extent(&tmpextent, &unit->tri.extent[curscan], polygon, unit->shared.scanline + curscan);
+ (*polygon->callback)(polygon->dest, unit->shared.scanline + curscan, &tmpextent, polygon->extra, threadid);
+ }
+ else
+ (*polygon->callback)(polygon->dest, unit->shared.scanline + curscan, &unit->quad.extent[curscan], polygon->extra, threadid);
+ }
+
+ /* set our count to 0 and re-fetch the original count value */
+ do
+ {
+ orig_count_next = unit->shared.count_next;
+ } while (compare_exchange32((volatile INT32 *)&unit->shared.count_next, orig_count_next, 0) != orig_count_next);
+
+ /* if we have no more work to do, do nothing */
+ orig_count_next >>= 16;
+ if (orig_count_next == 0)
+ break;
+ param = polygon->poly->unit[orig_count_next];
+ }
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ poly_state_presave - pre-save callback to
+ ensure everything is synced before saving
+-------------------------------------------------*/
+
+static void poly_state_presave(legacy_poly_manager *poly)
+{
+ poly_wait(poly, "pre-save");
+}
diff --git a/src/devices/video/polylgcy.h b/src/devices/video/polylgcy.h
new file mode 100644
index 00000000000..bc409d77db5
--- /dev/null
+++ b/src/devices/video/polylgcy.h
@@ -0,0 +1,155 @@
+// license:BSD-3-Clause
+// copyright-holders:Ville Linde, Aaron Giles
+/***************************************************************************
+
+ polylgcy.h
+
+ Legacy polygon helper routines.
+
+****************************************************************************
+
+ Pixel model:
+
+ (0.0,0.0) (1.0,0.0) (2.0,0.0) (3.0,0.0)
+ +---------------+---------------+---------------+
+ | | | |
+ | | | |
+ | (0.5,0.5) | (1.5,0.5) | (2.5,0.5) |
+ | * | * | * |
+ | | | |
+ | | | |
+ (0.0,1.0) (1.0,1.0) (2.0,1.0) (3.0,1.0)
+ +---------------+---------------+---------------+
+ | | | |
+ | | | |
+ | (0.5,1.5) | (1.5,1.5) | (2.5,1.5) |
+ | * | * | * |
+ | | | |
+ | | | |
+ | | | |
+ +---------------+---------------+---------------+
+ (0.0,2.0) (1.0,2.0) (2.0,2.0) (3.0,2.0)
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __POLYLGCY_H__
+#define __POLYLGCY_H__
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define MAX_VERTEX_PARAMS 6
+#define MAX_POLYGON_VERTS 32
+
+#define POLYFLAG_INCLUDE_BOTTOM_EDGE 0x01
+#define POLYFLAG_INCLUDE_RIGHT_EDGE 0x02
+#define POLYFLAG_NO_WORK_QUEUE 0x04
+#define POLYFLAG_ALLOW_QUADS 0x08
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* opaque reference to the poly manager */
+struct legacy_poly_manager;
+
+
+/* input vertex data */
+struct poly_vertex
+{
+ float x; /* X coordinate */
+ float y; /* Y coordinate */
+ float p[MAX_VERTEX_PARAMS]; /* interpolated parameter values */
+};
+
+
+/* poly_param_extent describes information for a single parameter in an extent */
+struct poly_param_extent
+{
+ float start; /* parameter value at starting X,Y */
+ float dpdx; /* dp/dx relative to starting X */
+};
+
+
+/* poly_extent describes start/end points for a scanline, along with per-scanline parameters */
+struct poly_extent
+{
+ INT16 startx; /* starting X coordinate (inclusive) */
+ INT16 stopx; /* ending X coordinate (exclusive) */
+ poly_param_extent param[MAX_VERTEX_PARAMS]; /* starting and dx values for each parameter */
+};
+
+
+/* callback routine to process a batch of scanlines in a triangle */
+typedef void (*poly_draw_scanline_func)(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+
+/* ----- initialization/teardown ----- */
+
+/* allocate a new poly manager that can render triangles */
+legacy_poly_manager *poly_alloc(running_machine &machine, int max_polys, size_t extra_data_size, UINT8 flags);
+
+/* free a poly manager */
+void poly_free(legacy_poly_manager *poly);
+
+
+
+/* ----- common functions ----- */
+
+/* wait until all polygons in the queue have been rendered */
+void poly_wait(legacy_poly_manager *poly, const char *debug_reason);
+
+/* get a pointer to the extra data for the next polygon */
+void *poly_get_extra_data(legacy_poly_manager *poly);
+
+
+
+/* ----- core triangle rendering ----- */
+
+/* render a single triangle given 3 vertexes */
+UINT32 poly_render_triangle(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, const poly_vertex *v1, const poly_vertex *v2, const poly_vertex *v3);
+
+/* render a set of triangles in a fan */
+UINT32 poly_render_triangle_fan(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, int numverts, const poly_vertex *v);
+
+/* perform a custom render of an object, given specific extents */
+UINT32 poly_render_triangle_custom(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int startscanline, int numscanlines, const poly_extent *extents);
+
+
+
+/* ----- core quad rendering ----- */
+
+/* render a single quad given 4 vertexes */
+UINT32 poly_render_quad(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, const poly_vertex *v1, const poly_vertex *v2, const poly_vertex *v3, const poly_vertex *v4);
+
+/* render a set of quads in a fan */
+UINT32 poly_render_quad_fan(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, int numverts, const poly_vertex *v);
+
+
+
+/* ----- core polygon rendering ----- */
+
+/* render a single polygon up to 32 vertices */
+UINT32 poly_render_polygon(legacy_poly_manager *poly, void *dest, const rectangle &cliprect, poly_draw_scanline_func callback, int paramcount, int numverts, const poly_vertex *v);
+
+
+
+/* ----- clipping ----- */
+
+/* zclip (assumes p[0] == z) a polygon */
+int poly_zclip_if_less(int numverts, const poly_vertex *v, poly_vertex *outv, int paramcount, float clipval);
+
+
+#endif /* __POLYLGCY_H__ */
diff --git a/src/devices/video/psx.c b/src/devices/video/psx.c
new file mode 100644
index 00000000000..167280e3415
--- /dev/null
+++ b/src/devices/video/psx.c
@@ -0,0 +1,3822 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+/*
+ * PlayStation GPU emulator
+ *
+ * Copyright 2003-2014 smf
+ *
+ */
+
+#define DEBUG_VIEWER ( 0 )
+
+#include "emu.h"
+#include "video/psx.h"
+
+#define VERBOSE_LEVEL ( 0 )
+
+// device type definition
+const device_type CXD8514Q = &device_creator<cxd8514q_device>;
+const device_type CXD8538Q = &device_creator<cxd8538q_device>;
+const device_type CXD8561Q = &device_creator<cxd8561q_device>;
+const device_type CXD8561BQ = &device_creator<cxd8561bq_device>;
+const device_type CXD8561CQ = &device_creator<cxd8561cq_device>;
+const device_type CXD8654Q = &device_creator<cxd8654q_device>;
+
+psxgpu_device::psxgpu_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_vblank_handler(*this)
+#if DEBUG_VIEWER
+,
+ m_screen(*this, "screen")
+#endif
+{
+}
+
+void psxgpu_device::device_start( void )
+{
+ m_vblank_handler.resolve_safe();
+
+ if( m_type == CXD8538Q )
+ {
+ psx_gpu_init( 1 );
+ }
+ else
+ {
+ psx_gpu_init( 2 );
+ }
+}
+
+void psxgpu_device::device_reset( void )
+{
+ gpu_reset();
+}
+
+cxd8514q_device::cxd8514q_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : psxgpu_device(mconfig, CXD8514Q, "CXD8514Q GPU", tag, owner, clock, "cxd8514q", __FILE__)
+{
+}
+
+cxd8538q_device::cxd8538q_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : psxgpu_device(mconfig, CXD8538Q, "CXD8538Q GPU", tag, owner, clock, "cxd8538q", __FILE__)
+{
+}
+
+cxd8561q_device::cxd8561q_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : psxgpu_device(mconfig, CXD8561Q, "CXD8561Q GPU", tag, owner, clock, "cxd8561q", __FILE__)
+{
+}
+
+cxd8561bq_device::cxd8561bq_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : psxgpu_device(mconfig, CXD8561BQ, "CXD8561BQ", tag, owner, clock, "cxd8561bq", __FILE__)
+{
+}
+
+cxd8561cq_device::cxd8561cq_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : psxgpu_device(mconfig, CXD8561CQ, "CXD8561CQ GPU", tag, owner, clock, "cxd8561cq", __FILE__)
+{
+}
+
+cxd8654q_device::cxd8654q_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : psxgpu_device(mconfig, CXD8654Q, "CXD8654Q GPU", tag, owner, clock, "cxd8654q", __FILE__)
+{
+}
+
+static const UINT16 m_p_n_nextpointlist4[] = { 1, 3, 0, 2 };
+static const UINT16 m_p_n_prevpointlist4[] = { 2, 0, 3, 1 };
+static const UINT16 m_p_n_nextpointlist4b[] = { 0, 3, 1, 2 };
+static const UINT16 m_p_n_prevpointlist4b[] = { 0, 2, 3, 1 };
+static const UINT16 m_p_n_nextpointlist3[] = { 1, 2, 0 };
+static const UINT16 m_p_n_prevpointlist3[] = { 2, 0, 1 };
+
+#define SINT11( x ) ( ( (INT32)( x ) << 21 ) >> 21 )
+
+#define COORD_X( a ) ( a.sw.l )
+#define COORD_Y( a ) ( a.sw.h )
+#define SIZE_W( a ) ( a.w.l )
+#define SIZE_H( a ) ( a.w.h )
+#define BGR_C( a ) ( a.b.h3 )
+#define BGR_B( a ) ( a.b.h2 )
+#define BGR_G( a ) ( a.b.h )
+#define BGR_R( a ) ( a.b.l )
+#define TEXTURE_V( a ) ( a.b.h )
+#define TEXTURE_U( a ) ( a.b.l )
+
+INLINE void ATTR_PRINTF(3,4) verboselog( running_machine& machine, int n_level, const char *s_fmt, ... )
+{
+ if( VERBOSE_LEVEL >= n_level )
+ {
+ va_list v;
+ char buf[ 32768 ];
+ va_start( v, s_fmt );
+ vsprintf( buf, s_fmt, v );
+ va_end( v );
+ logerror( "%s: %s", machine.describe_context(), buf );
+ }
+}
+
+#if DEBUG_VIEWER
+
+void psxgpu_device::DebugMeshInit( void )
+{
+ int width = m_screen->width();
+ int height = m_screen->height();
+
+ m_debug.b_mesh = 0;
+ m_debug.b_texture = 0;
+ m_debug.n_interleave = -1;
+ m_debug.b_clear = 1;
+ m_debug.n_coord = 0;
+ m_debug.n_skip = 0;
+ m_debug.mesh = auto_bitmap_ind16_alloc( machine(), width, height );
+}
+
+void psxgpu_device::DebugMesh( int n_coordx, int n_coordy )
+{
+ int n_coord;
+ int n_colour;
+ int width = m_screen->width();
+ int height = m_screen->height();
+
+ n_coordx += m_n_displaystartx;
+ n_coordy += n_displaystarty;
+
+ if( m_debug.b_clear )
+ {
+ m_debug.mesh->fill(0x0000);
+ m_debug.b_clear = 0;
+ }
+
+ n_colour = 0x1f;
+ for( n_coord = 0; n_coord < m_debug.n_coord; n_coord++ )
+ {
+ if( n_coordx != m_debug.n_coordx[ n_coord ] ||
+ n_coordy != m_debug.n_coordy[ n_coord ] )
+ {
+ break;
+ }
+ }
+ if( n_coord == m_debug.n_coord && m_debug.n_coord > 1 )
+ {
+ n_colour = 0xffff;
+ }
+ for( n_coord = 0; n_coord < m_debug.n_coord; n_coord++ )
+ {
+ PAIR n_x;
+ PAIR n_y;
+ INT32 n_xstart;
+ INT32 n_ystart;
+ INT32 n_xend;
+ INT32 n_yend;
+ INT32 n_xlen;
+ INT32 n_ylen;
+ INT32 n_len;
+ INT32 n_dx;
+ INT32 n_dy;
+
+ n_xstart = m_debug.n_coordx[ n_coord ];
+ n_xend = n_coordx;
+ if( n_xend > n_xstart )
+ {
+ n_xlen = n_xend - n_xstart;
+ }
+ else
+ {
+ n_xlen = n_xstart - n_xend;
+ }
+
+ n_ystart = m_debug.n_coordy[ n_coord ];
+ n_yend = n_coordy;
+ if( n_yend > n_ystart )
+ {
+ n_ylen = n_yend - n_ystart;
+ }
+ else
+ {
+ n_ylen = n_ystart - n_yend;
+ }
+
+ if( n_xlen > n_ylen )
+ {
+ n_len = n_xlen;
+ }
+ else
+ {
+ n_len = n_ylen;
+ }
+
+ n_x.sw.h = n_xstart; n_x.sw.l = 0;
+ n_y.sw.h = n_ystart; n_y.sw.l = 0;
+
+ if( n_len == 0 )
+ {
+ n_len = 1;
+ }
+
+ n_dx = (INT32)( ( n_xend << 16 ) - n_x.d ) / n_len;
+ n_dy = (INT32)( ( n_yend << 16 ) - n_y.d ) / n_len;
+ while( n_len > 0 )
+ {
+ if( (INT16)n_x.w.h >= 0 &&
+ (INT16)n_y.w.h >= 0 &&
+ (INT16)n_x.w.h <= width - 1 &&
+ (INT16)n_y.w.h <= height - 1 )
+ {
+ if( m_debug.mesh->pix16(n_y.w.h, n_x.w.h) != 0xffff )
+ m_debug.mesh->pix16(n_y.w.h, n_x.w.h) = n_colour;
+ }
+ n_x.d += n_dx;
+ n_y.d += n_dy;
+ n_len--;
+ }
+ }
+
+ if( m_debug.n_coord < DEBUG_COORDS )
+ {
+ m_debug.n_coordx[ m_debug.n_coord ] = n_coordx;
+ m_debug.n_coordy[ m_debug.n_coord ] = n_coordy;
+ m_debug.n_coord++;
+ }
+}
+
+void psxgpu_device::DebugMeshEnd( void )
+{
+ m_debug.n_coord = 0;
+}
+
+void psxgpu_device::DebugCheckKeys( void )
+{
+ if( machine().input().code_pressed_once( KEYCODE_M ) )
+ {
+ m_debug.b_mesh = !m_debug.b_mesh;
+ updatevisiblearea();
+ }
+
+ if( machine().input().code_pressed_once( KEYCODE_V ) )
+ {
+ m_debug.b_texture = !m_debug.b_texture;
+ updatevisiblearea();
+ }
+
+ if( machine().input().code_pressed_once( KEYCODE_I ) )
+ {
+ if( m_debug.b_texture )
+ {
+ m_debug.n_interleave++;
+
+ if( m_debug.n_interleave == 2 )
+ m_debug.n_interleave = -1;
+
+ if( m_debug.n_interleave == -1 )
+ popmessage( "interleave off" );
+ else if( m_debug.n_interleave == 0 )
+ popmessage( "4 bit interleave" );
+ else if( m_debug.n_interleave == 1 )
+ popmessage( "8 bit interleave" );
+ }
+ else
+ {
+ m_debug.n_skip++;
+
+ if( m_debug.n_skip > 15 )
+ m_debug.n_skip = 0;
+
+ popmessage( "debug skip %d", m_debug.n_skip );
+ }
+ }
+
+#if 0
+ if( machine().input().code_pressed_once( KEYCODE_D ) )
+ {
+ FILE *f;
+ int n_x;
+ f = fopen( "dump.txt", "w" );
+ for( n_y = 256; n_y < 512; n_y++ )
+ for( n_x = 640; n_x < 1024; n_x++ )
+ fprintf( f, "%04u,%04u = %04x\n", n_y, n_x, p_p_vram[ n_y ][ n_x ] );
+ fclose( f );
+ }
+ if( machine().input().code_pressed_once( KEYCODE_S ) )
+ {
+ FILE *f;
+ popmessage( "saving..." );
+ f = fopen( "VRAM.BIN", "wb" );
+ for( n_y = 0; n_y < 1024; n_y++ )
+ fwrite( p_p_vram[ n_y ], 1024 * 2, 1, f );
+ fclose( f );
+ }
+ if( machine().input().code_pressed_once( KEYCODE_L ) )
+ {
+ FILE *f;
+ popmessage( "loading..." );
+ f = fopen( "VRAM.BIN", "rb" );
+ for( n_y = 0; n_y < 1024; n_y++ )
+ fread( p_p_vram[ n_y ], 1024 * 2, 1, f );
+ fclose( f );
+ }
+#endif
+}
+
+int psxgpu_device::DebugMeshDisplay( bitmap_ind16 &bitmap, const rectangle &cliprect )
+{
+ if( m_debug.b_mesh )
+ {
+ copybitmap( bitmap, *m_debug.mesh, 0, 0, 0, 0, cliprect );
+ }
+ m_debug.b_clear = 1;
+ return m_debug.b_mesh;
+}
+
+int psxgpu_device::DebugTextureDisplay( bitmap_ind16 &bitmap )
+{
+ UINT32 n_y;
+
+ if( m_debug.b_texture )
+ {
+ int width = m_screen->width();
+ int height = m_screen->height();
+
+ for( n_y = 0; n_y < height; n_y++ )
+ {
+ int n_x;
+ int n_xi;
+ int n_yi;
+ UINT16 p_n_interleave[ 1024 ];
+
+ for( n_x = 0; n_x < width; n_x++ )
+ {
+ if( m_debug.n_interleave == 0 )
+ {
+ n_xi = ( n_x & ~0x3c ) + ( ( n_y << 2 ) & 0x3c );
+ n_yi = ( n_y & ~0xf ) + ( ( n_x >> 2 ) & 0xf );
+ }
+ else if( m_debug.n_interleave == 1 )
+ {
+ n_xi = ( n_x & ~0x78 ) + ( ( n_x << 3 ) & 0x40 ) + ( ( n_y << 3 ) & 0x38 );
+ n_yi = ( n_y & ~0x7 ) + ( ( n_x >> 4 ) & 0x7 );
+ }
+ else
+ {
+ n_xi = n_x;
+ n_yi = n_y;
+ }
+ p_n_interleave[ n_x ] = p_p_vram[ n_yi ][ n_xi ];
+ }
+ draw_scanline16( bitmap, 0, n_y, width, p_n_interleave, m_screen->palette()->pens() );
+ }
+ }
+ return m_debug.b_texture;
+}
+
+#endif
+
+void psxgpu_device::updatevisiblearea()
+{
+ rectangle visarea;
+ float refresh;
+
+ if( ( n_gpustatus & ( 1 << 0x14 ) ) != 0 )
+ {
+ /* pal */
+ refresh = 50;
+ switch( ( n_gpustatus >> 0x13 ) & 1 )
+ {
+ case 0:
+ n_screenheight = 256;
+ break;
+ case 1:
+ n_screenheight = 512;
+ break;
+ }
+ }
+ else
+ {
+ /* ntsc */
+ refresh = 60;
+ switch( ( n_gpustatus >> 0x13 ) & 1 )
+ {
+ case 0:
+ n_screenheight = 240;
+ break;
+ case 1:
+ n_screenheight = 480;
+ break;
+ }
+ }
+ switch( ( n_gpustatus >> 0x11 ) & 3 )
+ {
+ case 0:
+ switch( ( n_gpustatus >> 0x10 ) & 1 )
+ {
+ case 0:
+ n_screenwidth = 256;
+ break;
+ case 1:
+ n_screenwidth = 368;
+ break;
+ }
+ break;
+ case 1:
+ switch( ( n_gpustatus >> 0x10 ) & 1 )
+ {
+ case 0:
+ n_screenwidth = 320;
+ break;
+ case 1:
+ n_screenwidth = 384;
+ break;
+ }
+ break;
+ case 2:
+ n_screenwidth = 512;
+ break;
+ case 3:
+ n_screenwidth = 640;
+ break;
+ }
+
+#if DEBUG_VIEWER
+ if( m_debug.b_mesh || m_debug.b_texture )
+ {
+ n_screenheight = 1024;
+ n_screenwidth = 1024;
+ }
+#endif
+
+ visarea.set(0, n_screenwidth - 1, 0, n_screenheight - 1);
+ machine().first_screen()->configure(n_screenwidth, n_screenheight, visarea, HZ_TO_ATTOSECONDS(refresh));
+}
+
+void psxgpu_device::psx_gpu_init( int n_gputype )
+{
+ int n_line;
+ int n_level;
+ int n_level2;
+ int n_shade;
+ int n_shaded;
+ int width = 1024;
+ int height = ( vramSize / width ) / sizeof( UINT16 );
+
+ m_n_gputype = n_gputype;
+
+#if DEBUG_VIEWER
+ DebugMeshInit();
+#endif
+
+ n_gpustatus = 0x14802000;
+ n_gpuinfo = 0;
+ n_gpu_buffer_offset = 0;
+ n_lightgun_x = 0;
+ n_lightgun_y = 0;
+ b_reverseflag = 0;
+
+ p_vram = auto_alloc_array_clear( machine(), UINT16, width * height );
+
+ for( n_line = 0; n_line < 1024; n_line++ )
+ {
+ p_p_vram[ n_line ] = &p_vram[ ( n_line % height ) * width ];
+ }
+
+ for( n_level = 0; n_level < MAX_LEVEL; n_level++ )
+ {
+ for( n_shade = 0; n_shade < MAX_SHADE; n_shade++ )
+ {
+ /* shaded */
+ n_shaded = ( n_level * n_shade ) / MID_SHADE;
+ if( n_shaded > MAX_LEVEL - 1 )
+ {
+ n_shaded = MAX_LEVEL - 1;
+ }
+ p_n_redshade[ ( n_level * MAX_SHADE ) | n_shade ] = n_shaded;
+ p_n_greenshade[ ( n_level * MAX_SHADE ) | n_shade ] = n_shaded << 5;
+ p_n_blueshade[ ( n_level * MAX_SHADE ) | n_shade ] = n_shaded << 10;
+
+ /* 1/4 x transparency */
+ n_shaded = ( n_level * n_shade ) / MID_SHADE;
+ n_shaded >>= 2;
+ if( n_shaded > MAX_LEVEL - 1 )
+ {
+ n_shaded = MAX_LEVEL - 1;
+ }
+ p_n_f025[ ( n_level * MAX_SHADE ) | n_shade ] = n_shaded;
+
+ /* 1/2 x transparency */
+ n_shaded = ( n_level * n_shade ) / MID_SHADE;
+ n_shaded >>= 1;
+ if( n_shaded > MAX_LEVEL - 1 )
+ {
+ n_shaded = MAX_LEVEL - 1;
+ }
+ p_n_f05[ ( n_level * MAX_SHADE ) | n_shade ] = n_shaded;
+
+ /* 1 x transparency */
+ n_shaded = ( n_level * n_shade ) / MID_SHADE;
+ if( n_shaded > MAX_LEVEL - 1 )
+ {
+ n_shaded = MAX_LEVEL - 1;
+ }
+ p_n_f1[ ( n_level * MAX_SHADE ) | n_shade ] = n_shaded;
+ }
+ }
+
+ for( n_level = 0; n_level < 0x10000; n_level++ )
+ {
+ p_n_redlevel[ n_level ] = ( n_level & ( MAX_LEVEL - 1 ) ) * MAX_SHADE;
+ p_n_greenlevel[ n_level ] = ( ( n_level >> 5 ) & ( MAX_LEVEL - 1 ) ) * MAX_SHADE;
+ p_n_bluelevel[ n_level ] = ( ( n_level >> 10 ) & ( MAX_LEVEL - 1 ) ) * MAX_SHADE;
+
+ /* 0.5 * background */
+ p_n_redb05[ n_level ] = ( ( n_level & ( MAX_LEVEL - 1 ) ) / 2 ) * MAX_LEVEL;
+ p_n_greenb05[ n_level ] = ( ( ( n_level >> 5 ) & ( MAX_LEVEL - 1 ) ) / 2 ) * MAX_LEVEL;
+ p_n_blueb05[ n_level ] = ( ( ( n_level >> 10 ) & ( MAX_LEVEL - 1 ) ) / 2 ) * MAX_LEVEL;
+
+ /* 1 * background */
+ p_n_redb1[ n_level ] = ( n_level & ( MAX_LEVEL - 1 ) ) * MAX_LEVEL;
+ p_n_greenb1[ n_level ] = ( ( n_level >> 5 ) & ( MAX_LEVEL - 1 ) ) * MAX_LEVEL;
+ p_n_blueb1[ n_level ] = ( ( n_level >> 10 ) & ( MAX_LEVEL - 1 ) ) * MAX_LEVEL;
+
+ /* 24bit to 15 bit conversion */
+ p_n_g0r0[ n_level ] = ( ( ( n_level >> 11 ) & ( MAX_LEVEL - 1 ) ) << 5 ) | ( ( ( n_level >> 3 ) & ( MAX_LEVEL - 1 ) ) << 0 );
+ p_n_b0[ n_level ] = ( ( n_level >> 3 ) & ( MAX_LEVEL - 1 ) ) << 10;
+ p_n_r1[ n_level ] = ( ( n_level >> 11 ) & ( MAX_LEVEL - 1 ) ) << 0;
+ p_n_b1g1[ n_level ] = ( ( ( n_level >> 11 ) & ( MAX_LEVEL - 1 ) ) << 10 ) | ( ( ( n_level >> 3 ) & ( MAX_LEVEL - 1 ) ) << 5 );
+ }
+
+ for( n_level = 0; n_level < MAX_LEVEL; n_level++ )
+ {
+ for( n_level2 = 0; n_level2 < MAX_LEVEL; n_level2++ )
+ {
+ /* add transparency */
+ n_shaded = ( n_level + n_level2 );
+ if( n_shaded > MAX_LEVEL - 1 )
+ {
+ n_shaded = MAX_LEVEL - 1;
+ }
+ p_n_redaddtrans[ ( n_level * MAX_LEVEL ) | n_level2 ] = n_shaded;
+ p_n_greenaddtrans[ ( n_level * MAX_LEVEL ) | n_level2 ] = n_shaded << 5;
+ p_n_blueaddtrans[ ( n_level * MAX_LEVEL ) | n_level2 ] = n_shaded << 10;
+
+ /* sub transparency */
+ n_shaded = ( n_level - n_level2 );
+ if( n_shaded < 0 )
+ {
+ n_shaded = 0;
+ }
+ p_n_redsubtrans[ ( n_level * MAX_LEVEL ) | n_level2 ] = n_shaded;
+ p_n_greensubtrans[ ( n_level * MAX_LEVEL ) | n_level2 ] = n_shaded << 5;
+ p_n_bluesubtrans[ ( n_level * MAX_LEVEL ) | n_level2 ] = n_shaded << 10;
+ }
+ }
+
+ // icky!!!
+ machine().save().save_memory( this, "globals", NULL, 0, "m_packet", (UINT8 *)&m_packet, 1, sizeof( m_packet ) );
+
+ save_pointer(NAME(p_vram), width * height );
+ save_item(NAME(n_gpu_buffer_offset));
+ save_item(NAME(n_vramx));
+ save_item(NAME(n_vramy));
+ save_item(NAME(n_twy));
+ save_item(NAME(n_twx));
+ save_item(NAME(n_tww));
+ save_item(NAME(n_drawarea_x1));
+ save_item(NAME(n_drawarea_y1));
+ save_item(NAME(n_drawarea_x2));
+ save_item(NAME(n_drawarea_y2));
+ save_item(NAME(n_horiz_disstart));
+ save_item(NAME(n_horiz_disend));
+ save_item(NAME(n_vert_disstart));
+ save_item(NAME(n_vert_disend));
+ save_item(NAME(b_reverseflag));
+ save_item(NAME(n_drawoffset_x));
+ save_item(NAME(n_drawoffset_y));
+ save_item(NAME(m_n_displaystartx));
+ save_item(NAME(n_displaystarty));
+ save_item(NAME(n_gpustatus));
+ save_item(NAME(n_gpuinfo));
+ save_item(NAME(n_lightgun_x));
+ save_item(NAME(n_lightgun_y));
+ save_item(NAME(m_n_tx));
+ save_item(NAME(m_n_ty));
+ save_item(NAME(n_abr));
+ save_item(NAME(n_tp));
+ save_item(NAME(n_ix));
+ save_item(NAME(n_iy));
+ save_item(NAME(n_ti));
+
+ machine().save().register_postload( save_prepost_delegate( FUNC( psxgpu_device::updatevisiblearea ), this ) );
+}
+
+UINT32 psxgpu_device::update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ UINT32 n_x;
+ UINT32 n_y;
+ int n_top;
+ int n_line;
+ int n_lines;
+ int n_left;
+ int n_column;
+ int n_columns;
+ int n_displaystartx;
+ int n_overscantop;
+ int n_overscanleft;
+
+#if DEBUG_VIEWER
+ if( DebugMeshDisplay( bitmap, cliprect ) )
+ {
+ return 0;
+ }
+ if( DebugTextureDisplay( bitmap ) )
+ {
+ return 0;
+ }
+#endif
+
+ if( ( n_gpustatus & ( 1 << 0x17 ) ) != 0 )
+ {
+ /* todo: only draw to necessary area */
+ bitmap.fill(0, cliprect);
+ }
+ else
+ {
+ if( b_reverseflag )
+ {
+ n_displaystartx = ( 1023 - m_n_displaystartx );
+ /* todo: make this flip the screen, in the meantime.. */
+ n_displaystartx -= ( n_screenwidth - 1 );
+ }
+ else
+ {
+ n_displaystartx = m_n_displaystartx;
+ }
+
+ if( ( n_gpustatus & ( 1 << 0x14 ) ) != 0 )
+ {
+ /* pal */
+ n_overscantop = 0x23;
+ n_overscanleft = 0x27e;
+ }
+ else
+ {
+ /* ntsc */
+ n_overscantop = 0x10;
+ n_overscanleft = 0x260;
+ }
+
+ n_top = (INT32)n_vert_disstart - n_overscantop;
+ n_lines = (INT32)n_vert_disend - (INT32)n_vert_disstart;
+ if( n_top < 0 )
+ {
+ n_y = -n_top;
+ n_lines += n_top;
+ }
+ else
+ {
+ n_y = 0;
+
+ /* draw top border */
+ rectangle clip(cliprect.left(), cliprect.right(), cliprect.top(), n_top);
+ bitmap.fill(0, clip);
+ }
+ if( ( n_gpustatus & ( 1 << 0x16 ) ) != 0 )
+ {
+ /* interlaced */
+ n_lines *= 2;
+ }
+ if( n_lines > n_screenheight - ( n_y + n_top ) )
+ {
+ n_lines = n_screenheight - ( n_y + n_top );
+ }
+ else
+ {
+ /* draw bottom border */
+ rectangle clip(cliprect.left(), cliprect.right(), n_y + n_top + n_lines, cliprect.bottom());
+ bitmap.fill(0, clip);
+ }
+
+ n_left = ( ( (INT32)n_horiz_disstart - n_overscanleft ) * (INT32)n_screenwidth ) / 2560;
+ n_columns = ( ( ( (INT32)n_horiz_disend - n_horiz_disstart ) * (INT32)n_screenwidth ) / 2560 );
+ if( n_left < 0 )
+ {
+ n_x = -n_left;
+ n_columns += n_left;
+ }
+ else
+ {
+ n_x = 0;
+
+ /* draw left border */
+ rectangle clip(cliprect.left(), n_x + n_left, cliprect.top(), cliprect.bottom());
+ bitmap.fill(0, clip);
+ }
+ if( n_columns > n_screenwidth - ( n_x + n_left ) )
+ {
+ n_columns = n_screenwidth - ( n_x + n_left );
+ }
+ else
+ {
+ /* draw right border */
+ rectangle clip(n_x + n_left + n_columns, cliprect.right(), cliprect.top(), cliprect.bottom());
+ bitmap.fill(0, clip);
+ }
+
+ if( ( n_gpustatus & ( 1 << 0x15 ) ) != 0 )
+ {
+ /* 24bit */
+ n_line = n_lines;
+ while( n_line > 0 )
+ {
+ UINT16 *p_n_src = p_p_vram[ n_y + n_displaystarty ] + ((n_x + n_displaystartx) * 3);
+ UINT16 *p_n_dest = &bitmap.pix16(n_y + n_top, n_x + n_left);
+
+ n_column = n_columns;
+ while( n_column > 0 )
+ {
+ UINT32 n_g0r0 = *( p_n_src++ );
+ UINT32 n_r1b0 = *( p_n_src++ );
+ UINT32 n_b1g1 = *( p_n_src++ );
+
+ *( p_n_dest++ ) = p_n_g0r0[ n_g0r0 ] | p_n_b0[ n_r1b0 ];
+ n_column--;
+ if( n_column > 0 )
+ {
+ *( p_n_dest++ ) = p_n_r1[ n_r1b0 ] | p_n_b1g1[ n_b1g1 ];
+ n_column--;
+ }
+ }
+ n_y++;
+ n_line--;
+ }
+ }
+ else
+ {
+ /* 15bit */
+ n_line = n_lines;
+ while( n_line > 0 )
+ {
+ draw_scanline16( bitmap, n_x + n_left, n_y + n_top, n_columns, p_p_vram[ ( n_y + n_displaystarty ) & 1023 ] + n_x + n_displaystartx, NULL );
+ n_y++;
+ n_line--;
+ }
+ }
+ }
+ return 0;
+}
+
+#define WRITE_PIXEL( p ) *( p_vram ) = p
+
+/*
+type 1
+f e| d| c b| a 9| 8 7| 6 5| 4| 3 2 1 0
+ |ti| | tp| abr| ty| | tx
+*/
+
+/*
+type 2
+f e| d c| b| a 9| 8 7| 6 5| 4| 3 2 1 0
+ |iy|ix|ty| | tp| abr|ty| tx
+*/
+
+void psxgpu_device::decode_tpage( UINT32 tpage )
+{
+ if( m_n_gputype == 2 )
+ {
+ n_gpustatus = ( n_gpustatus & 0xfffff800 ) | ( tpage & 0x7ff );
+
+ m_n_tx = ( tpage & 0x0f ) << 6;
+ m_n_ty = ( ( tpage & 0x10 ) << 4 ) | ( ( tpage & 0x800 ) >> 2 );
+ n_abr = ( tpage & 0x60 ) >> 5;
+ n_tp = ( tpage & 0x180 ) >> 7;
+ n_ix = ( tpage & 0x1000 ) >> 12;
+ n_iy = ( tpage & 0x2000 ) >> 13;
+ n_ti = 0;
+ if( ( tpage & ~0x39ff ) != 0 )
+ {
+ verboselog( machine(), 1, "not handled: draw mode %08x\n", tpage & ~0x39ff );
+ }
+ if( n_tp == 3 )
+ {
+ verboselog( machine(), 0, "not handled: tp == 3\n" );
+ }
+ }
+ else
+ {
+ n_gpustatus = ( n_gpustatus & 0xffffe000 ) | ( tpage & 0x1fff );
+
+ m_n_tx = ( tpage & 0x0f ) << 6;
+ m_n_ty = ( ( tpage & 0x60 ) << 3 );
+ n_abr = ( tpage & 0x180 ) >> 7;
+ n_tp = ( tpage & 0x600 ) >> 9;
+ n_ti = ( tpage & 0x2000 ) >> 13;
+ n_ix = 0;
+ n_iy = 0;
+ if( ( tpage & ~0x27ef ) != 0 )
+ {
+ verboselog( machine(), 1, "not handled: draw mode %08x\n", tpage & ~0x27ef );
+ }
+ if( n_tp == 3 )
+ {
+ verboselog( machine(), 0, "not handled: tp == 3\n" );
+ }
+ else if( n_tp == 2 && n_ti != 0 )
+ {
+ verboselog( machine(), 0, "not handled: interleaved 15 bit texture\n" );
+ }
+ }
+}
+
+#define SPRITESETUP \
+ if( n_iy != 0 ) \
+ { \
+ n_dv = -1; \
+ } \
+ else \
+ { \
+ n_dv = 1; \
+ } \
+ if( n_ix != 0 ) \
+ { \
+ n_du = -1; \
+ } \
+ else \
+ { \
+ n_du = 1; \
+ }
+
+#define TRANSPARENCYSETUP \
+ p_n_f = p_n_f1; \
+ p_n_redb = p_n_redb1; \
+ p_n_greenb = p_n_greenb1; \
+ p_n_blueb = p_n_blueb1; \
+ p_n_redtrans = p_n_redaddtrans; \
+ p_n_greentrans = p_n_greenaddtrans; \
+ p_n_bluetrans = p_n_blueaddtrans; \
+ \
+ switch( n_cmd & 0x02 ) \
+ { \
+ case 0x02: \
+ switch( n_abr ) \
+ { \
+ case 0x00: \
+ p_n_f = p_n_f05; \
+ p_n_redb = p_n_redb05; \
+ p_n_greenb = p_n_greenb05; \
+ p_n_blueb = p_n_blueb05; \
+ p_n_redtrans = p_n_redaddtrans; \
+ p_n_greentrans = p_n_greenaddtrans; \
+ p_n_bluetrans = p_n_blueaddtrans; \
+ verboselog( machine(), 2, "Transparency Mode: 0.5*B + 0.5*F\n" ); \
+ break; \
+ case 0x01: \
+ p_n_f = p_n_f1; \
+ p_n_redb = p_n_redb1; \
+ p_n_greenb = p_n_greenb1; \
+ p_n_blueb = p_n_blueb1; \
+ p_n_redtrans = p_n_redaddtrans; \
+ p_n_greentrans = p_n_greenaddtrans; \
+ p_n_bluetrans = p_n_blueaddtrans; \
+ verboselog( machine(), 2, "Transparency Mode: 1.0*B + 1.0*F\n" ); \
+ break; \
+ case 0x02: \
+ p_n_f = p_n_f1; \
+ p_n_redb = p_n_redb1; \
+ p_n_greenb = p_n_greenb1; \
+ p_n_blueb = p_n_blueb1; \
+ p_n_redtrans = p_n_redsubtrans; \
+ p_n_greentrans = p_n_greensubtrans; \
+ p_n_bluetrans = p_n_bluesubtrans; \
+ verboselog( machine(), 2, "Transparency Mode: 1.0*B - 1.0*F\n" ); \
+ break; \
+ case 0x03: \
+ p_n_f = p_n_f025; \
+ p_n_redb = p_n_redb1; \
+ p_n_greenb = p_n_greenb1; \
+ p_n_blueb = p_n_blueb1; \
+ p_n_redtrans = p_n_redaddtrans; \
+ p_n_greentrans = p_n_greenaddtrans; \
+ p_n_bluetrans = p_n_blueaddtrans; \
+ verboselog( machine(), 2, "Transparency Mode: 1.0*B + 0.25*F\n" ); \
+ break; \
+ } \
+ break; \
+ }
+
+#define SOLIDSETUP \
+ TRANSPARENCYSETUP
+
+#define TEXTURESETUP \
+ n_tx = m_n_tx; \
+ n_ty = m_n_ty; \
+ p_clut = p_p_vram[ n_cluty ] + n_clutx; \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ n_tx += n_twx >> 2; \
+ n_ty += n_twy; \
+ break; \
+ case 1: \
+ n_tx += n_twx >> 1; \
+ n_ty += n_twy; \
+ break; \
+ case 2: \
+ n_tx += n_twx >> 0; \
+ n_ty += n_twy; \
+ break; \
+ } \
+ TRANSPARENCYSETUP
+
+#define FLATPOLYGONUPDATE
+#define FLATRECTANGEUPDATE
+#define GOURAUDPOLYGONUPDATE \
+ n_r.d += n_dr; \
+ n_g.d += n_dg; \
+ n_b.d += n_db;
+
+#define SOLIDFILL( PIXELUPDATE ) \
+ if( n_distance > ( (INT32)n_drawarea_x2 - drawx ) + 1 ) \
+ { \
+ n_distance = ( n_drawarea_x2 - drawx ) + 1; \
+ } \
+ p_vram = p_p_vram[ drawy ] + drawx; \
+ \
+ switch( n_cmd & 0x02 ) \
+ { \
+ case 0x00: \
+ /* transparency off */ \
+ while( n_distance > 0 ) \
+ { \
+ WRITE_PIXEL( \
+ p_n_redshade[ MID_LEVEL | n_r.w.h ] | \
+ p_n_greenshade[ MID_LEVEL | n_g.w.h ] | \
+ p_n_blueshade[ MID_LEVEL | n_b.w.h ] ); \
+ p_vram++; \
+ PIXELUPDATE \
+ n_distance--; \
+ } \
+ break; \
+ case 0x02: \
+ /* transparency on */ \
+ while( n_distance > 0 ) \
+ { \
+ WRITE_PIXEL( \
+ p_n_redtrans[ p_n_f[ MID_LEVEL | n_r.w.h ] | p_n_redb[ *( p_vram ) ] ] | \
+ p_n_greentrans[ p_n_f[ MID_LEVEL | n_g.w.h ] | p_n_greenb[ *( p_vram ) ] ] | \
+ p_n_bluetrans[ p_n_f[ MID_LEVEL | n_b.w.h ] | p_n_blueb[ *( p_vram ) ] ] ); \
+ p_vram++; \
+ PIXELUPDATE \
+ n_distance--; \
+ } \
+ break; \
+ }
+
+#define FLATTEXTUREDPOLYGONUPDATE \
+ n_u.d += n_du; \
+ n_v.d += n_dv;
+
+#define GOURAUDTEXTUREDPOLYGONUPDATE \
+ n_r.d += n_dr; \
+ n_g.d += n_dg; \
+ n_b.d += n_db; \
+ n_u.d += n_du; \
+ n_v.d += n_dv;
+
+#define FLATTEXTUREDRECTANGLEUPDATE \
+ n_u += n_du;
+
+#define TEXTURE_LOOP \
+ while( n_distance > 0 ) \
+ {
+#define TEXTURE_ENDLOOP \
+ }
+
+#define TEXTURE4BIT( TXV, TXU ) \
+ TEXTURE_LOOP \
+ n_bgr = p_clut[ ( *( p_p_vram[ n_ty + TXV ] + n_tx + ( TXU >> 2 ) ) >> ( ( TXU & 0x03 ) << 2 ) ) & 0x0f ];
+
+#define TEXTURE8BIT( TXV, TXU ) \
+ TEXTURE_LOOP \
+ n_bgr = p_clut[ ( *( p_p_vram[ n_ty + TXV ] + n_tx + ( TXU >> 1 ) ) >> ( ( TXU & 0x01 ) << 3 ) ) & 0xff ];
+
+#define TEXTURE15BIT( TXV, TXU ) \
+ TEXTURE_LOOP \
+ n_bgr = *( p_p_vram[ n_ty + TXV ] + n_tx + TXU );
+
+#define TEXTUREWINDOW4BIT( TXV, TXU ) TEXTURE4BIT( ( TXV & n_twh ), ( TXU & n_tww ) )
+#define TEXTUREWINDOW8BIT( TXV, TXU ) TEXTURE8BIT( ( TXV & n_twh ), ( TXU & n_tww ) )
+#define TEXTUREWINDOW15BIT( TXV, TXU ) TEXTURE15BIT( ( TXV & n_twh ), ( TXU & n_tww ) )
+
+#define TEXTUREINTERLEAVED4BIT( TXV, TXU ) \
+ TEXTURE_LOOP \
+ int n_xi = ( ( TXU >> 2 ) & ~0x3c ) + ( ( TXV << 2 ) & 0x3c ); \
+ int n_yi = ( TXV & ~0xf ) + ( ( TXU >> 4 ) & 0xf ); \
+ n_bgr = p_clut[ ( *( p_p_vram[ n_ty + n_yi ] + n_tx + n_xi ) >> ( ( TXU & 0x03 ) << 2 ) ) & 0x0f ];
+
+#define TEXTUREINTERLEAVED8BIT( TXV, TXU ) \
+ TEXTURE_LOOP \
+ int n_xi = ( ( TXU >> 1 ) & ~0x78 ) + ( ( TXU << 2 ) & 0x40 ) + ( ( TXV << 3 ) & 0x38 ); \
+ int n_yi = ( TXV & ~0x7 ) + ( ( TXU >> 5 ) & 0x7 ); \
+ n_bgr = p_clut[ ( *( p_p_vram[ n_ty + n_yi ] + n_tx + n_xi ) >> ( ( TXU & 0x01 ) << 3 ) ) & 0xff ];
+
+#define TEXTUREINTERLEAVED15BIT( TXV, TXU ) \
+ TEXTURE_LOOP \
+ int n_xi = TXU; \
+ int n_yi = TXV; \
+ n_bgr = *( p_p_vram[ n_ty + n_yi ] + n_tx + n_xi );
+
+#define TEXTUREWINDOWINTERLEAVED4BIT( TXV, TXU ) TEXTUREINTERLEAVED4BIT( ( TXV & n_twh ), ( TXU & n_tww ) )
+#define TEXTUREWINDOWINTERLEAVED8BIT( TXV, TXU ) TEXTUREINTERLEAVED8BIT( ( TXV & n_twh ), ( TXU & n_tww ) )
+#define TEXTUREWINDOWINTERLEAVED15BIT( TXV, TXU ) TEXTUREINTERLEAVED15BIT( ( TXV & n_twh ), ( TXU & n_tww ) )
+
+#define SHADEDPIXEL( PIXELUPDATE ) \
+ if( n_bgr != 0 ) \
+ { \
+ WRITE_PIXEL( \
+ p_n_redshade[ p_n_redlevel[ n_bgr ] | n_r.w.h ] | \
+ p_n_greenshade[ p_n_greenlevel[ n_bgr ] | n_g.w.h ] | \
+ p_n_blueshade[ p_n_bluelevel[ n_bgr ] | n_b.w.h ] ); \
+ } \
+ p_vram++; \
+ PIXELUPDATE \
+ n_distance--; \
+ TEXTURE_ENDLOOP
+
+#define TRANSPARENTPIXEL( PIXELUPDATE ) \
+ if( n_bgr != 0 ) \
+ { \
+ if( ( n_bgr & 0x8000 ) != 0 ) \
+ { \
+ WRITE_PIXEL( \
+ p_n_redtrans[ p_n_f[ p_n_redlevel[ n_bgr ] | n_r.w.h ] | p_n_redb[ *( p_vram ) ] ] | \
+ p_n_greentrans[ p_n_f[ p_n_greenlevel[ n_bgr ] | n_g.w.h ] | p_n_greenb[ *( p_vram ) ] ] | \
+ p_n_bluetrans[ p_n_f[ p_n_bluelevel[ n_bgr ] | n_b.w.h ] | p_n_blueb[ *( p_vram ) ] ] ); \
+ } \
+ else \
+ { \
+ WRITE_PIXEL( \
+ p_n_redshade[ p_n_redlevel[ n_bgr ] | n_r.w.h ] | \
+ p_n_greenshade[ p_n_greenlevel[ n_bgr ] | n_g.w.h ] | \
+ p_n_blueshade[ p_n_bluelevel[ n_bgr ] | n_b.w.h ] ); \
+ } \
+ } \
+ p_vram++; \
+ PIXELUPDATE \
+ n_distance--; \
+ TEXTURE_ENDLOOP
+
+#define TEXTUREFILL( PIXELUPDATE, TXU, TXV ) \
+ if( n_distance > ( (INT32)n_drawarea_x2 - drawx ) + 1 ) \
+ { \
+ n_distance = ( n_drawarea_x2 - drawx ) + 1; \
+ } \
+ p_vram = p_p_vram[ drawy ] + drawx; \
+ \
+ if( n_ti != 0 ) \
+ { \
+ /* interleaved texture */ \
+ if( n_twh != 255 || \
+ n_tww != 255 || \
+ n_twx != 0 || \
+ n_twy != 0 ) \
+ { \
+ /* texture window */ \
+ switch( n_cmd & 0x02 ) \
+ { \
+ case 0x00: \
+ /* shading */ \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ /* 4 bit clut */ \
+ TEXTUREWINDOWINTERLEAVED4BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ case 1: \
+ /* 8 bit clut */ \
+ TEXTUREWINDOWINTERLEAVED8BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ case 2: \
+ /* 15 bit */ \
+ TEXTUREWINDOWINTERLEAVED15BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ } \
+ break; \
+ case 0x02: \
+ /* semi transparency */ \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ /* 4 bit clut */ \
+ TEXTUREWINDOWINTERLEAVED4BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ case 1: \
+ /* 8 bit clut */ \
+ TEXTUREWINDOWINTERLEAVED8BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ case 2: \
+ /* 15 bit */ \
+ TEXTUREWINDOWINTERLEAVED15BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ } \
+ break; \
+ } \
+ } \
+ else \
+ { \
+ /* no texture window */ \
+ switch( n_cmd & 0x02 ) \
+ { \
+ case 0x00: \
+ /* shading */ \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ /* 4 bit clut */ \
+ TEXTUREINTERLEAVED4BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ case 1: \
+ /* 8 bit clut */ \
+ TEXTUREINTERLEAVED8BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ case 2: \
+ /* 15 bit */ \
+ TEXTUREINTERLEAVED15BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ } \
+ break; \
+ case 0x02: \
+ /* semi transparency */ \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ /* 4 bit clut */ \
+ TEXTUREINTERLEAVED4BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ case 1: \
+ /* 8 bit clut */ \
+ TEXTUREINTERLEAVED8BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ case 2: \
+ /* 15 bit */ \
+ TEXTUREINTERLEAVED15BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ } \
+ break; \
+ } \
+ } \
+ } \
+ else \
+ { \
+ /* standard texture */ \
+ if( n_twh != 255 || \
+ n_tww != 255 || \
+ n_twx != 0 || \
+ n_twy != 0 ) \
+ { \
+ /* texture window */ \
+ switch( n_cmd & 0x02 ) \
+ { \
+ case 0x00: \
+ /* shading */ \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ /* 4 bit clut */ \
+ TEXTUREWINDOW4BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ case 1: \
+ /* 8 bit clut */ \
+ TEXTUREWINDOW8BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ case 2: \
+ /* 15 bit */ \
+ TEXTUREWINDOW15BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ } \
+ break; \
+ case 0x02: \
+ /* semi transparency */ \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ /* 4 bit clut */ \
+ TEXTUREWINDOW4BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ case 1: \
+ /* 8 bit clut */ \
+ TEXTUREWINDOW8BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ case 2: \
+ /* 15 bit */ \
+ TEXTUREWINDOW15BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ } \
+ break; \
+ } \
+ } \
+ else \
+ { \
+ /* no texture window */ \
+ switch( n_cmd & 0x02 ) \
+ { \
+ case 0x00: \
+ /* shading */ \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ TEXTURE4BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ case 1: \
+ /* 8 bit clut */ \
+ TEXTURE8BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ case 2: \
+ /* 15 bit */ \
+ TEXTURE15BIT( TXV, TXU ) \
+ SHADEDPIXEL( PIXELUPDATE ) \
+ break; \
+ } \
+ break; \
+ case 0x02: \
+ /* semi transparency */ \
+ switch( n_tp ) \
+ { \
+ case 0: \
+ /* 4 bit clut */ \
+ TEXTURE4BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ case 1: \
+ /* 8 bit clut */ \
+ TEXTURE8BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ case 2: \
+ /* 15 bit */ \
+ TEXTURE15BIT( TXV, TXU ) \
+ TRANSPARENTPIXEL( PIXELUPDATE ) \
+ break; \
+ } \
+ break; \
+ } \
+ } \
+ }
+
+#define GET_COORD( a ) \
+ a.sw.l = SINT11( COORD_X( a ) ); \
+ a.sw.h = SINT11( COORD_Y( a ) );
+
+INLINE int CullVertex( int a, int b )
+{
+ int d = a - b;
+ if( d < -1023 || d > 1023 )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+#define CULLPOINT( PacketType, p1, p2 ) \
+( \
+ CullVertex( COORD_Y( m_packet.PacketType.vertex[ p1 ].n_coord ), COORD_Y( m_packet.PacketType.vertex[ p2 ].n_coord ) ) || \
+ CullVertex( COORD_X( m_packet.PacketType.vertex[ p1 ].n_coord ), COORD_X( m_packet.PacketType.vertex[ p2 ].n_coord ) ) \
+)
+
+#define CULLTRIANGLE( PacketType, start ) \
+( \
+ CULLPOINT( PacketType, start, start + 1 ) || CULLPOINT( PacketType, start + 1, start + 2 ) || CULLPOINT( PacketType, start + 2, start ) \
+)
+
+#define FINDTOPLEFT( PacketType ) \
+ for( n_point = 0; n_point < n_points; n_point++ ) \
+ { \
+ GET_COORD( m_packet.PacketType.vertex[ n_point ].n_coord ); \
+ } \
+ \
+ n_leftpoint = 0; \
+ if( n_points == 4 ) \
+ { \
+ if( CULLTRIANGLE( PacketType, 0 ) ) \
+ { \
+ if( CULLTRIANGLE( PacketType, 1 ) ) \
+ { \
+ return; \
+ } \
+ \
+ p_n_rightpointlist = m_p_n_nextpointlist4b; \
+ p_n_leftpointlist = m_p_n_prevpointlist4b; \
+ n_leftpoint++; \
+ } \
+ else if( CULLTRIANGLE( PacketType, 1 ) ) \
+ { \
+ p_n_rightpointlist = m_p_n_nextpointlist3; \
+ p_n_leftpointlist = m_p_n_prevpointlist3; \
+ n_points--; \
+ } \
+ else \
+ { \
+ p_n_rightpointlist = m_p_n_nextpointlist4; \
+ p_n_leftpointlist = m_p_n_prevpointlist4; \
+ } \
+ } \
+ else if( CULLTRIANGLE( PacketType, 0 ) ) \
+ { \
+ return; \
+ } \
+ else \
+ { \
+ p_n_rightpointlist = m_p_n_nextpointlist3; \
+ p_n_leftpointlist = m_p_n_prevpointlist3; \
+ } \
+ \
+ for( n_point = n_leftpoint + 1; n_point < n_points; n_point++ ) \
+ { \
+ if( COORD_Y( m_packet.PacketType.vertex[ n_point ].n_coord ) < COORD_Y( m_packet.PacketType.vertex[ n_leftpoint ].n_coord ) || \
+ ( COORD_Y( m_packet.PacketType.vertex[ n_point ].n_coord ) == COORD_Y( m_packet.PacketType.vertex[ n_leftpoint ].n_coord ) && \
+ COORD_X( m_packet.PacketType.vertex[ n_point ].n_coord ) < COORD_X( m_packet.PacketType.vertex[ n_leftpoint ].n_coord ) ) ) \
+ { \
+ n_leftpoint = n_point; \
+ } \
+ } \
+ n_rightpoint = n_leftpoint;
+
+void psxgpu_device::FlatPolygon( int n_points )
+{
+ INT16 n_y;
+ INT16 n_x;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ PAIR n_cx1;
+ PAIR n_cx2;
+ INT32 n_dx1;
+ INT32 n_dx2;
+
+ UINT8 n_cmd;
+
+ INT32 n_distance;
+
+ UINT16 n_point;
+ UINT16 n_rightpoint;
+ UINT16 n_leftpoint;
+ const UINT16 *p_n_rightpointlist;
+ const UINT16 *p_n_leftpointlist;
+
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 1 )
+ {
+ return;
+ }
+ for( n_point = 0; n_point < n_points; n_point++ )
+ {
+ DebugMesh( SINT11( COORD_X( m_packet.FlatPolygon.vertex[ n_point ].n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatPolygon.vertex[ n_point ].n_coord ) ) + n_drawoffset_y );
+ }
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.FlatPolygon.n_bgr );
+
+ n_cx1.d = 0;
+ n_cx2.d = 0;
+
+ SOLIDSETUP
+
+ n_r.w.h = BGR_R( m_packet.FlatPolygon.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.FlatPolygon.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.FlatPolygon.n_bgr ); n_b.w.l = 0;
+
+ FINDTOPLEFT( FlatPolygon )
+
+ n_dx1 = 0;
+ n_dx2 = 0;
+
+ n_y = COORD_Y( m_packet.FlatPolygon.vertex[ n_rightpoint ].n_coord );
+
+ for( ;; )
+ {
+ if( n_y == COORD_Y( m_packet.FlatPolygon.vertex[ n_leftpoint ].n_coord ) )
+ {
+ while( n_y == COORD_Y( m_packet.FlatPolygon.vertex[ p_n_leftpointlist[ n_leftpoint ] ].n_coord ) )
+ {
+ n_leftpoint = p_n_leftpointlist[ n_leftpoint ];
+ if( n_leftpoint == n_rightpoint )
+ {
+ break;
+ }
+ }
+ n_cx1.sw.h = COORD_X( m_packet.FlatPolygon.vertex[ n_leftpoint ].n_coord ); n_cx1.sw.l = 0;
+ n_leftpoint = p_n_leftpointlist[ n_leftpoint ];
+ n_distance = COORD_Y( m_packet.FlatPolygon.vertex[ n_leftpoint ].n_coord ) - n_y;
+ if( n_distance < 1 )
+ {
+ break;
+ }
+ n_dx1 = (INT32)( ( COORD_X( m_packet.FlatPolygon.vertex[ n_leftpoint ].n_coord ) << 16 ) - n_cx1.d ) / n_distance;
+ }
+ if( n_y == COORD_Y( m_packet.FlatPolygon.vertex[ n_rightpoint ].n_coord ) )
+ {
+ while( n_y == COORD_Y( m_packet.FlatPolygon.vertex[ p_n_rightpointlist[ n_rightpoint ] ].n_coord ) )
+ {
+ n_rightpoint = p_n_rightpointlist[ n_rightpoint ];
+ if( n_rightpoint == n_leftpoint )
+ {
+ break;
+ }
+ }
+ n_cx2.sw.h = COORD_X( m_packet.FlatPolygon.vertex[ n_rightpoint ].n_coord ); n_cx2.sw.l = 0;
+ n_rightpoint = p_n_rightpointlist[ n_rightpoint ];
+ n_distance = COORD_Y( m_packet.FlatPolygon.vertex[ n_rightpoint ].n_coord ) - n_y;
+ if( n_distance < 1 )
+ {
+ break;
+ }
+ n_dx2 = (INT32)( ( COORD_X( m_packet.FlatPolygon.vertex[ n_rightpoint ].n_coord ) << 16 ) - n_cx2.d ) / n_distance;
+ }
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( (INT16)n_cx1.sw.h != (INT16)n_cx2.sw.h && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ if( (INT16)n_cx1.sw.h < (INT16)n_cx2.sw.h )
+ {
+ n_x = n_cx1.sw.h;
+ n_distance = (INT16)n_cx2.sw.h - n_x;
+ }
+ else
+ {
+ n_x = n_cx2.sw.h;
+ n_distance = (INT16)n_cx1.sw.h - n_x;
+ }
+
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ SOLIDFILL( FLATPOLYGONUPDATE )
+ }
+ n_cx1.d += n_dx1;
+ n_cx2.d += n_dx2;
+ n_y++;
+ }
+}
+
+void psxgpu_device::FlatTexturedPolygon( int n_points )
+{
+ INT16 n_y;
+ INT16 n_x;
+ int n_tx;
+ int n_ty;
+
+ UINT8 n_cmd;
+
+ UINT32 n_clutx;
+ UINT32 n_cluty;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ PAIR n_u;
+ PAIR n_v;
+
+ PAIR n_cx1;
+ PAIR n_cx2;
+ PAIR n_cu1;
+ PAIR n_cv1;
+ PAIR n_cu2;
+ PAIR n_cv2;
+ INT32 n_du;
+ INT32 n_dv;
+ INT32 n_dx1;
+ INT32 n_dx2;
+ INT32 n_du1;
+ INT32 n_dv1;
+ INT32 n_du2;
+ INT32 n_dv2;
+
+ INT32 n_distance;
+ UINT16 n_point;
+ UINT16 n_rightpoint;
+ UINT16 n_leftpoint;
+ const UINT16 *p_n_rightpointlist;
+ const UINT16 *p_n_leftpointlist;
+ UINT16 *p_clut;
+ UINT16 *p_vram;
+ UINT32 n_bgr;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 2 )
+ {
+ return;
+ }
+ for( n_point = 0; n_point < n_points; n_point++ )
+ {
+ DebugMesh( SINT11( COORD_X( m_packet.FlatTexturedPolygon.vertex[ n_point ].n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatTexturedPolygon.vertex[ n_point ].n_coord ) ) + n_drawoffset_y );
+ }
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.FlatTexturedPolygon.n_bgr );
+
+ n_clutx = ( m_packet.FlatTexturedPolygon.vertex[ 0 ].n_texture.w.h & 0x3f ) << 4;
+ n_cluty = ( m_packet.FlatTexturedPolygon.vertex[ 0 ].n_texture.w.h >> 6 ) & 0x3ff;
+
+ n_r.d = 0;
+ n_g.d = 0;
+ n_b.d = 0;
+ n_cx1.d = 0;
+ n_cu1.d = 0;
+ n_cv1.d = 0;
+ n_cx2.d = 0;
+ n_cu2.d = 0;
+ n_cv2.d = 0;
+
+ decode_tpage( m_packet.FlatTexturedPolygon.vertex[ 1 ].n_texture.w.h );
+ TEXTURESETUP
+
+ switch( n_cmd & 0x01 )
+ {
+ case 0:
+ n_r.w.h = BGR_R( m_packet.FlatTexturedPolygon.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.FlatTexturedPolygon.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.FlatTexturedPolygon.n_bgr ); n_b.w.l = 0;
+ break;
+ case 1:
+ n_r.w.h = 0x80; n_r.w.l = 0;
+ n_g.w.h = 0x80; n_g.w.l = 0;
+ n_b.w.h = 0x80; n_b.w.l = 0;
+ break;
+ }
+
+ FINDTOPLEFT( FlatTexturedPolygon )
+
+ n_dx1 = 0;
+ n_dx2 = 0;
+ n_du1 = 0;
+ n_du2 = 0;
+ n_dv1 = 0;
+ n_dv2 = 0;
+
+ n_y = COORD_Y( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_coord );
+
+ for( ;; )
+ {
+ if( n_y == COORD_Y( m_packet.FlatTexturedPolygon.vertex[ n_leftpoint ].n_coord ) )
+ {
+ while( n_y == COORD_Y( m_packet.FlatTexturedPolygon.vertex[ p_n_leftpointlist[ n_leftpoint ] ].n_coord ) )
+ {
+ n_leftpoint = p_n_leftpointlist[ n_leftpoint ];
+ if( n_leftpoint == n_rightpoint )
+ {
+ break;
+ }
+ }
+ n_cx1.sw.h = COORD_X( m_packet.FlatTexturedPolygon.vertex[ n_leftpoint ].n_coord ); n_cx1.sw.l = 0;
+ n_cu1.w.h = TEXTURE_U( m_packet.FlatTexturedPolygon.vertex[ n_leftpoint ].n_texture ); n_cu1.w.l = 0;
+ n_cv1.w.h = TEXTURE_V( m_packet.FlatTexturedPolygon.vertex[ n_leftpoint ].n_texture ); n_cv1.w.l = 0;
+ n_leftpoint = p_n_leftpointlist[ n_leftpoint ];
+ n_distance = COORD_Y( m_packet.FlatTexturedPolygon.vertex[ n_leftpoint ].n_coord ) - n_y;
+ if( n_distance < 1 )
+ {
+ break;
+ }
+ n_dx1 = (INT32)( ( COORD_X( m_packet.FlatTexturedPolygon.vertex[ n_leftpoint ].n_coord ) << 16 ) - n_cx1.d ) / n_distance;
+ n_du1 = (INT32)( ( TEXTURE_U( m_packet.FlatTexturedPolygon.vertex[ n_leftpoint ].n_texture ) << 16 ) - n_cu1.d ) / n_distance;
+ n_dv1 = (INT32)( ( TEXTURE_V( m_packet.FlatTexturedPolygon.vertex[ n_leftpoint ].n_texture ) << 16 ) - n_cv1.d ) / n_distance;
+ }
+ if( n_y == COORD_Y( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_coord ) )
+ {
+ while( n_y == COORD_Y( m_packet.FlatTexturedPolygon.vertex[ p_n_rightpointlist[ n_rightpoint ] ].n_coord ) )
+ {
+ n_rightpoint = p_n_rightpointlist[ n_rightpoint ];
+ if( n_rightpoint == n_leftpoint )
+ {
+ break;
+ }
+ }
+ n_cx2.sw.h = COORD_X( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_coord ); n_cx2.sw.l = 0;
+ n_cu2.w.h = TEXTURE_U( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_texture ); n_cu2.w.l = 0;
+ n_cv2.w.h = TEXTURE_V( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_texture ); n_cv2.w.l = 0;
+ n_rightpoint = p_n_rightpointlist[ n_rightpoint ];
+ n_distance = COORD_Y( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_coord ) - n_y;
+ if( n_distance < 1 )
+ {
+ break;
+ }
+ n_dx2 = (INT32)( ( COORD_X( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_coord ) << 16 ) - n_cx2.d ) / n_distance;
+ n_du2 = (INT32)( ( TEXTURE_U( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_texture ) << 16 ) - n_cu2.d ) / n_distance;
+ n_dv2 = (INT32)( ( TEXTURE_V( m_packet.FlatTexturedPolygon.vertex[ n_rightpoint ].n_texture ) << 16 ) - n_cv2.d ) / n_distance;
+ }
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( (INT16)n_cx1.sw.h != (INT16)n_cx2.sw.h && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ if( (INT16)n_cx1.sw.h < (INT16)n_cx2.sw.h )
+ {
+ n_x = n_cx1.sw.h;
+ n_distance = (INT16)n_cx2.sw.h - n_x;
+
+ n_u.d = n_cu1.d;
+ n_v.d = n_cv1.d;
+ n_du = (INT32)( n_cu2.d - n_cu1.d ) / n_distance;
+ n_dv = (INT32)( n_cv2.d - n_cv1.d ) / n_distance;
+ }
+ else
+ {
+ n_x = n_cx2.sw.h;
+ n_distance = (INT16)n_cx1.sw.h - n_x;
+
+ n_u.d = n_cu2.d;
+ n_v.d = n_cv2.d;
+ n_du = (INT32)( n_cu1.d - n_cu2.d ) / n_distance;
+ n_dv = (INT32)( n_cv1.d - n_cv2.d ) / n_distance;
+ }
+
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_u.d += n_du * ( n_drawarea_x1 - drawx );
+ n_v.d += n_dv * ( n_drawarea_x1 - drawx );
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ TEXTUREFILL( FLATTEXTUREDPOLYGONUPDATE, n_u.w.h, n_v.w.h );
+ }
+ n_cx1.d += n_dx1;
+ n_cu1.d += n_du1;
+ n_cv1.d += n_dv1;
+ n_cx2.d += n_dx2;
+ n_cu2.d += n_du2;
+ n_cv2.d += n_dv2;
+ n_y++;
+ }
+}
+
+void psxgpu_device::GouraudPolygon( int n_points )
+{
+ INT16 n_y;
+ INT16 n_x;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ UINT8 n_cmd;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ PAIR n_cx1;
+ PAIR n_cx2;
+ PAIR n_cr1;
+ PAIR n_cg1;
+ PAIR n_cb1;
+ PAIR n_cr2;
+ PAIR n_cg2;
+ PAIR n_cb2;
+ INT32 n_dr;
+ INT32 n_dg;
+ INT32 n_db;
+ INT32 n_dx1;
+ INT32 n_dx2;
+ INT32 n_dr1;
+ INT32 n_dg1;
+ INT32 n_db1;
+ INT32 n_dr2;
+ INT32 n_dg2;
+ INT32 n_db2;
+
+ INT32 n_distance;
+
+ UINT16 n_point;
+ UINT16 n_rightpoint;
+ UINT16 n_leftpoint;
+ const UINT16 *p_n_rightpointlist;
+ const UINT16 *p_n_leftpointlist;
+
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 3 )
+ {
+ return;
+ }
+ for( n_point = 0; n_point < n_points; n_point++ )
+ {
+ DebugMesh( SINT11( COORD_X( m_packet.GouraudPolygon.vertex[ n_point ].n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.GouraudPolygon.vertex[ n_point ].n_coord ) ) + n_drawoffset_y );
+ }
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.GouraudPolygon.vertex[ 0 ].n_bgr );
+
+ n_cx1.d = 0;
+ n_cr1.d = 0;
+ n_cg1.d = 0;
+ n_cb1.d = 0;
+ n_cx2.d = 0;
+ n_cr2.d = 0;
+ n_cg2.d = 0;
+ n_cb2.d = 0;
+
+ SOLIDSETUP
+
+ FINDTOPLEFT( GouraudPolygon )
+
+ n_dx1 = 0;
+ n_dx2 = 0;
+ n_dr1 = 0;
+ n_dr2 = 0;
+ n_dg1 = 0;
+ n_dg2 = 0;
+ n_db1 = 0;
+ n_db2 = 0;
+
+ n_y = COORD_Y( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_coord );
+
+ for( ;; )
+ {
+ if( n_y == COORD_Y( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_coord ) )
+ {
+ while( n_y == COORD_Y( m_packet.GouraudPolygon.vertex[ p_n_leftpointlist[ n_leftpoint ] ].n_coord ) )
+ {
+ n_leftpoint = p_n_leftpointlist[ n_leftpoint ];
+ if( n_leftpoint == n_rightpoint )
+ {
+ break;
+ }
+ }
+ n_cx1.sw.h = COORD_X( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_coord ); n_cx1.sw.l = 0;
+ n_cr1.w.h = BGR_R( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_bgr ); n_cr1.w.l = 0;
+ n_cg1.w.h = BGR_G( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_bgr ); n_cg1.w.l = 0;
+ n_cb1.w.h = BGR_B( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_bgr ); n_cb1.w.l = 0;
+ n_leftpoint = p_n_leftpointlist[ n_leftpoint ];
+ n_distance = COORD_Y( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_coord ) - n_y;
+ if( n_distance < 1 )
+ {
+ break;
+ }
+ n_dx1 = (INT32)( ( COORD_X( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_coord ) << 16 ) - n_cx1.d ) / n_distance;
+ n_dr1 = (INT32)( ( BGR_R( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_bgr ) << 16 ) - n_cr1.d ) / n_distance;
+ n_dg1 = (INT32)( ( BGR_G( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_bgr ) << 16 ) - n_cg1.d ) / n_distance;
+ n_db1 = (INT32)( ( BGR_B( m_packet.GouraudPolygon.vertex[ n_leftpoint ].n_bgr ) << 16 ) - n_cb1.d ) / n_distance;
+ }
+ if( n_y == COORD_Y( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_coord ) )
+ {
+ while( n_y == COORD_Y( m_packet.GouraudPolygon.vertex[ p_n_rightpointlist[ n_rightpoint ] ].n_coord ) )
+ {
+ n_rightpoint = p_n_rightpointlist[ n_rightpoint ];
+ if( n_rightpoint == n_leftpoint )
+ {
+ break;
+ }
+ }
+ n_cx2.sw.h = COORD_X( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_coord ); n_cx2.sw.l = 0;
+ n_cr2.w.h = BGR_R( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_bgr ); n_cr2.w.l = 0;
+ n_cg2.w.h = BGR_G( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_bgr ); n_cg2.w.l = 0;
+ n_cb2.w.h = BGR_B( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_bgr ); n_cb2.w.l = 0;
+ n_rightpoint = p_n_rightpointlist[ n_rightpoint ];
+ n_distance = COORD_Y( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_coord ) - n_y;
+ if( n_distance < 1 )
+ {
+ break;
+ }
+ n_dx2 = (INT32)( ( COORD_X( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_coord ) << 16 ) - n_cx2.d ) / n_distance;
+ n_dr2 = (INT32)( ( BGR_R( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_bgr ) << 16 ) - n_cr2.d ) / n_distance;
+ n_dg2 = (INT32)( ( BGR_G( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_bgr ) << 16 ) - n_cg2.d ) / n_distance;
+ n_db2 = (INT32)( ( BGR_B( m_packet.GouraudPolygon.vertex[ n_rightpoint ].n_bgr ) << 16 ) - n_cb2.d ) / n_distance;
+ }
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( (INT16)n_cx1.sw.h != (INT16)n_cx2.sw.h && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ if( (INT16)n_cx1.sw.h < (INT16)n_cx2.sw.h )
+ {
+ n_x = n_cx1.sw.h;
+ n_distance = (INT16)n_cx2.sw.h - n_x;
+
+ n_r.d = n_cr1.d;
+ n_g.d = n_cg1.d;
+ n_b.d = n_cb1.d;
+ n_dr = (INT32)( n_cr2.d - n_cr1.d ) / n_distance;
+ n_dg = (INT32)( n_cg2.d - n_cg1.d ) / n_distance;
+ n_db = (INT32)( n_cb2.d - n_cb1.d ) / n_distance;
+ }
+ else
+ {
+ n_x = n_cx2.sw.h;
+ n_distance = (INT16)n_cx1.sw.h - n_x;
+
+ n_r.d = n_cr2.d;
+ n_g.d = n_cg2.d;
+ n_b.d = n_cb2.d;
+ n_dr = (INT32)( n_cr1.d - n_cr2.d ) / n_distance;
+ n_dg = (INT32)( n_cg1.d - n_cg2.d ) / n_distance;
+ n_db = (INT32)( n_cb1.d - n_cb2.d ) / n_distance;
+ }
+
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_r.d += n_dr * ( n_drawarea_x1 - drawx );
+ n_g.d += n_dg * ( n_drawarea_x1 - drawx );
+ n_b.d += n_db * ( n_drawarea_x1 - drawx );
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ SOLIDFILL( GOURAUDPOLYGONUPDATE )
+ }
+ n_cx1.d += n_dx1;
+ n_cr1.d += n_dr1;
+ n_cg1.d += n_dg1;
+ n_cb1.d += n_db1;
+ n_cx2.d += n_dx2;
+ n_cr2.d += n_dr2;
+ n_cg2.d += n_dg2;
+ n_cb2.d += n_db2;
+ n_y++;
+ }
+}
+
+void psxgpu_device::GouraudTexturedPolygon( int n_points )
+{
+ INT16 n_y;
+ INT16 n_x;
+ int n_tx;
+ int n_ty;
+
+ UINT8 n_cmd;
+
+ UINT32 n_clutx;
+ UINT32 n_cluty;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ PAIR n_u;
+ PAIR n_v;
+
+ PAIR n_cx1;
+ PAIR n_cx2;
+ PAIR n_cu1;
+ PAIR n_cv1;
+ PAIR n_cu2;
+ PAIR n_cv2;
+ PAIR n_cr1;
+ PAIR n_cg1;
+ PAIR n_cb1;
+ PAIR n_cr2;
+ PAIR n_cg2;
+ PAIR n_cb2;
+ INT32 n_dr;
+ INT32 n_dg;
+ INT32 n_db;
+ INT32 n_du;
+ INT32 n_dv;
+ INT32 n_dx1;
+ INT32 n_dx2;
+ INT32 n_dr1;
+ INT32 n_dg1;
+ INT32 n_db1;
+ INT32 n_dr2;
+ INT32 n_dg2;
+ INT32 n_db2;
+ INT32 n_du1;
+ INT32 n_dv1;
+ INT32 n_du2;
+ INT32 n_dv2;
+
+ INT32 n_distance;
+ UINT16 n_point;
+ UINT16 n_rightpoint;
+ UINT16 n_leftpoint;
+ const UINT16 *p_n_rightpointlist;
+ const UINT16 *p_n_leftpointlist;
+ UINT16 *p_clut;
+ UINT16 *p_vram;
+ UINT32 n_bgr;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 4 )
+ {
+ return;
+ }
+ for( n_point = 0; n_point < n_points; n_point++ )
+ {
+ DebugMesh( SINT11( COORD_X( m_packet.GouraudTexturedPolygon.vertex[ n_point ].n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.GouraudTexturedPolygon.vertex[ n_point ].n_coord ) ) + n_drawoffset_y );
+ }
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.GouraudTexturedPolygon.vertex[ 0 ].n_bgr );
+
+ n_clutx = ( m_packet.GouraudTexturedPolygon.vertex[ 0 ].n_texture.w.h & 0x3f ) << 4;
+ n_cluty = ( m_packet.GouraudTexturedPolygon.vertex[ 0 ].n_texture.w.h >> 6 ) & 0x3ff;
+
+ n_cx1.d = 0;
+ n_cr1.d = 0;
+ n_cg1.d = 0;
+ n_cb1.d = 0;
+ n_cu1.d = 0;
+ n_cv1.d = 0;
+ n_cx2.d = 0;
+ n_cr2.d = 0;
+ n_cg2.d = 0;
+ n_cb2.d = 0;
+ n_cu2.d = 0;
+ n_cv2.d = 0;
+
+ decode_tpage( m_packet.GouraudTexturedPolygon.vertex[ 1 ].n_texture.w.h );
+ TEXTURESETUP
+
+ FINDTOPLEFT( GouraudTexturedPolygon )
+
+ n_dx1 = 0;
+ n_dx2 = 0;
+ n_du1 = 0;
+ n_du2 = 0;
+ n_dr1 = 0;
+ n_dr2 = 0;
+ n_dg1 = 0;
+ n_dg2 = 0;
+ n_db1 = 0;
+ n_db2 = 0;
+ n_dv1 = 0;
+ n_dv2 = 0;
+
+ n_y = COORD_Y( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_coord );
+
+ for( ;; )
+ {
+ if( n_y == COORD_Y( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_coord ) )
+ {
+ while( n_y == COORD_Y( m_packet.GouraudTexturedPolygon.vertex[ p_n_leftpointlist[ n_leftpoint ] ].n_coord ) )
+ {
+ n_leftpoint = p_n_leftpointlist[ n_leftpoint ];
+ if( n_leftpoint == n_rightpoint )
+ {
+ break;
+ }
+ }
+ n_cx1.sw.h = COORD_X( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_coord ); n_cx1.sw.l = 0;
+ switch( n_cmd & 0x01 )
+ {
+ case 0x00:
+ n_cr1.w.h = BGR_R( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_bgr ); n_cr1.w.l = 0;
+ n_cg1.w.h = BGR_G( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_bgr ); n_cg1.w.l = 0;
+ n_cb1.w.h = BGR_B( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_bgr ); n_cb1.w.l = 0;
+ break;
+ case 0x01:
+ n_cr1.w.h = 0x80; n_cr1.w.l = 0;
+ n_cg1.w.h = 0x80; n_cg1.w.l = 0;
+ n_cb1.w.h = 0x80; n_cb1.w.l = 0;
+ break;
+ }
+ n_cu1.w.h = TEXTURE_U( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_texture ); n_cu1.w.l = 0;
+ n_cv1.w.h = TEXTURE_V( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_texture ); n_cv1.w.l = 0;
+ n_leftpoint = p_n_leftpointlist[ n_leftpoint ];
+ n_distance = COORD_Y( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_coord ) - n_y;
+ if( n_distance < 1 )
+ {
+ break;
+ }
+ n_dx1 = (INT32)( ( COORD_X( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_coord ) << 16 ) - n_cx1.d ) / n_distance;
+ switch( n_cmd & 0x01 )
+ {
+ case 0x00:
+ n_dr1 = (INT32)( ( BGR_R( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_bgr ) << 16 ) - n_cr1.d ) / n_distance;
+ n_dg1 = (INT32)( ( BGR_G( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_bgr ) << 16 ) - n_cg1.d ) / n_distance;
+ n_db1 = (INT32)( ( BGR_B( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_bgr ) << 16 ) - n_cb1.d ) / n_distance;
+ break;
+ case 0x01:
+ n_dr1 = 0;
+ n_dg1 = 0;
+ n_db1 = 0;
+ break;
+ }
+ n_du1 = (INT32)( ( TEXTURE_U( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_texture ) << 16 ) - n_cu1.d ) / n_distance;
+ n_dv1 = (INT32)( ( TEXTURE_V( m_packet.GouraudTexturedPolygon.vertex[ n_leftpoint ].n_texture ) << 16 ) - n_cv1.d ) / n_distance;
+ }
+ if( n_y == COORD_Y( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_coord ) )
+ {
+ while( n_y == COORD_Y( m_packet.GouraudTexturedPolygon.vertex[ p_n_rightpointlist[ n_rightpoint ] ].n_coord ) )
+ {
+ n_rightpoint = p_n_rightpointlist[ n_rightpoint ];
+ if( n_rightpoint == n_leftpoint )
+ {
+ break;
+ }
+ }
+ n_cx2.sw.h = COORD_X( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_coord ); n_cx2.sw.l = 0;
+ switch( n_cmd & 0x01 )
+ {
+ case 0x00:
+ n_cr2.w.h = BGR_R( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_bgr ); n_cr2.w.l = 0;
+ n_cg2.w.h = BGR_G( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_bgr ); n_cg2.w.l = 0;
+ n_cb2.w.h = BGR_B( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_bgr ); n_cb2.w.l = 0;
+ break;
+ case 0x01:
+ n_cr2.w.h = 0x80; n_cr2.w.l = 0;
+ n_cg2.w.h = 0x80; n_cg2.w.l = 0;
+ n_cb2.w.h = 0x80; n_cb2.w.l = 0;
+ break;
+ }
+ n_cu2.w.h = TEXTURE_U( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_texture ); n_cu2.w.l = 0;
+ n_cv2.w.h = TEXTURE_V( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_texture ); n_cv2.w.l = 0;
+ n_rightpoint = p_n_rightpointlist[ n_rightpoint ];
+ n_distance = COORD_Y( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_coord ) - n_y;
+ if( n_distance < 1 )
+ {
+ break;
+ }
+ n_dx2 = (INT32)( ( COORD_X( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_coord ) << 16 ) - n_cx2.d ) / n_distance;
+ switch( n_cmd & 0x01 )
+ {
+ case 0x00:
+ n_dr2 = (INT32)( ( BGR_R( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_bgr ) << 16 ) - n_cr2.d ) / n_distance;
+ n_dg2 = (INT32)( ( BGR_G( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_bgr ) << 16 ) - n_cg2.d ) / n_distance;
+ n_db2 = (INT32)( ( BGR_B( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_bgr ) << 16 ) - n_cb2.d ) / n_distance;
+ break;
+ case 0x01:
+ n_dr2 = 0;
+ n_dg2 = 0;
+ n_db2 = 0;
+ break;
+ }
+ n_du2 = (INT32)( ( TEXTURE_U( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_texture ) << 16 ) - n_cu2.d ) / n_distance;
+ n_dv2 = (INT32)( ( TEXTURE_V( m_packet.GouraudTexturedPolygon.vertex[ n_rightpoint ].n_texture ) << 16 ) - n_cv2.d ) / n_distance;
+ }
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( (INT16)n_cx1.sw.h != (INT16)n_cx2.sw.h && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ if( (INT16)n_cx1.sw.h < (INT16)n_cx2.sw.h )
+ {
+ n_x = n_cx1.sw.h;
+ n_distance = (INT16)n_cx2.sw.h - n_x;
+
+ n_r.d = n_cr1.d;
+ n_g.d = n_cg1.d;
+ n_b.d = n_cb1.d;
+ n_u.d = n_cu1.d;
+ n_v.d = n_cv1.d;
+ n_dr = (INT32)( n_cr2.d - n_cr1.d ) / n_distance;
+ n_dg = (INT32)( n_cg2.d - n_cg1.d ) / n_distance;
+ n_db = (INT32)( n_cb2.d - n_cb1.d ) / n_distance;
+ n_du = (INT32)( n_cu2.d - n_cu1.d ) / n_distance;
+ n_dv = (INT32)( n_cv2.d - n_cv1.d ) / n_distance;
+ }
+ else
+ {
+ n_x = n_cx2.sw.h;
+ n_distance = (INT16)n_cx1.sw.h - n_x;
+
+ n_r.d = n_cr2.d;
+ n_g.d = n_cg2.d;
+ n_b.d = n_cb2.d;
+ n_u.d = n_cu2.d;
+ n_v.d = n_cv2.d;
+ n_dr = (INT32)( n_cr1.d - n_cr2.d ) / n_distance;
+ n_dg = (INT32)( n_cg1.d - n_cg2.d ) / n_distance;
+ n_db = (INT32)( n_cb1.d - n_cb2.d ) / n_distance;
+ n_du = (INT32)( n_cu1.d - n_cu2.d ) / n_distance;
+ n_dv = (INT32)( n_cv1.d - n_cv2.d ) / n_distance;
+ }
+
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_r.d += n_dr * ( n_drawarea_x1 - drawx );
+ n_g.d += n_dg * ( n_drawarea_x1 - drawx );
+ n_b.d += n_db * ( n_drawarea_x1 - drawx );
+ n_u.d += n_du * ( n_drawarea_x1 - drawx );
+ n_v.d += n_dv * ( n_drawarea_x1 - drawx );
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ TEXTUREFILL( GOURAUDTEXTUREDPOLYGONUPDATE, n_u.w.h, n_v.w.h );
+ }
+ n_cx1.d += n_dx1;
+ n_cr1.d += n_dr1;
+ n_cg1.d += n_dg1;
+ n_cb1.d += n_db1;
+ n_cu1.d += n_du1;
+ n_cv1.d += n_dv1;
+ n_cx2.d += n_dx2;
+ n_cr2.d += n_dr2;
+ n_cg2.d += n_dg2;
+ n_cb2.d += n_db2;
+ n_cu2.d += n_du2;
+ n_cv2.d += n_dv2;
+ n_y++;
+ }
+}
+
+void psxgpu_device::MonochromeLine( void )
+{
+ PAIR n_x;
+ PAIR n_y;
+ INT32 n_dx;
+ INT32 n_dy;
+ INT32 n_len;
+ INT32 n_xlen;
+ INT32 n_ylen;
+ INT32 n_xstart;
+ INT32 n_ystart;
+ INT32 n_xend;
+ INT32 n_yend;
+ UINT32 n_r;
+ UINT32 n_g;
+ UINT32 n_b;
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 5 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.MonochromeLine.vertex[ 0 ].n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.MonochromeLine.vertex[ 0 ].n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.MonochromeLine.vertex[ 1 ].n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.MonochromeLine.vertex[ 1 ].n_coord ) ) + n_drawoffset_y );
+ DebugMeshEnd();
+#endif
+
+ n_xstart = SINT11( COORD_X( m_packet.MonochromeLine.vertex[ 0 ].n_coord ) );
+ n_xend = SINT11( COORD_X( m_packet.MonochromeLine.vertex[ 1 ].n_coord ) );
+ n_ystart = SINT11( COORD_Y( m_packet.MonochromeLine.vertex[ 0 ].n_coord ) );
+ n_yend = SINT11( COORD_Y( m_packet.MonochromeLine.vertex[ 1 ].n_coord ) );
+
+ n_r = BGR_R( m_packet.MonochromeLine.n_bgr );
+ n_g = BGR_G( m_packet.MonochromeLine.n_bgr );
+ n_b = BGR_B( m_packet.MonochromeLine.n_bgr );
+
+ if( n_xend > n_xstart )
+ {
+ n_xlen = n_xend - n_xstart;
+ }
+ else
+ {
+ n_xlen = n_xstart - n_xend;
+ }
+
+ if( n_yend > n_ystart )
+ {
+ n_ylen = n_yend - n_ystart;
+ }
+ else
+ {
+ n_ylen = n_ystart - n_yend;
+ }
+
+ if( n_xlen > n_ylen )
+ {
+ n_len = n_xlen;
+ }
+ else
+ {
+ n_len = n_ylen;
+ }
+
+ if( n_len == 0 )
+ {
+ n_len = 1;
+ }
+
+ n_x.sw.h = n_xstart; n_x.sw.l = 0;
+ n_y.sw.h = n_ystart; n_y.sw.l = 0;
+
+ n_dx = (INT32)( ( n_xend << 16 ) - n_x.d ) / n_len;
+ n_dy = (INT32)( ( n_yend << 16 ) - n_y.d ) / n_len;
+
+ while( n_len > 0 )
+ {
+ int drawx = n_x.sw.h + n_drawoffset_x;
+ int drawy = n_y.sw.h + n_drawoffset_y;
+
+ if( drawx >= (INT32)n_drawarea_x1 && drawy >= (INT32)n_drawarea_y1 &&
+ drawx <= (INT32)n_drawarea_x2 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ p_vram = p_p_vram[ drawy ] + drawx;
+ WRITE_PIXEL(
+ p_n_redshade[ MID_LEVEL | n_r ] |
+ p_n_greenshade[ MID_LEVEL | n_g ] |
+ p_n_blueshade[ MID_LEVEL | n_b ] );
+ }
+ n_x.d += n_dx;
+ n_y.d += n_dy;
+ n_len--;
+ }
+}
+
+void psxgpu_device::GouraudLine( void )
+{
+ PAIR n_x;
+ PAIR n_y;
+ INT32 n_dx;
+ INT32 n_dy;
+ INT32 n_dr;
+ INT32 n_dg;
+ INT32 n_db;
+ INT32 n_distance;
+ INT32 n_xlen;
+ INT32 n_ylen;
+ INT32 n_xstart;
+ INT32 n_ystart;
+ INT32 n_xend;
+ INT32 n_yend;
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ PAIR n_cr1;
+ PAIR n_cg1;
+ PAIR n_cb1;
+ PAIR n_cr2;
+ PAIR n_cg2;
+ PAIR n_cb2;
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 6 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.GouraudLine.vertex[ 0 ].n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.GouraudLine.vertex[ 0 ].n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.GouraudLine.vertex[ 1 ].n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.GouraudLine.vertex[ 1 ].n_coord ) ) + n_drawoffset_y );
+ DebugMeshEnd();
+#endif
+
+ n_xstart = SINT11( COORD_X( m_packet.GouraudLine.vertex[ 0 ].n_coord ) );
+ n_ystart = SINT11( COORD_Y( m_packet.GouraudLine.vertex[ 0 ].n_coord ) );
+ n_cr1.w.h = BGR_R( m_packet.GouraudLine.vertex[ 0 ].n_bgr ); n_cr1.w.l = 0;
+ n_cg1.w.h = BGR_G( m_packet.GouraudLine.vertex[ 0 ].n_bgr ); n_cg1.w.l = 0;
+ n_cb1.w.h = BGR_B( m_packet.GouraudLine.vertex[ 0 ].n_bgr ); n_cb1.w.l = 0;
+
+ n_xend = SINT11( COORD_X( m_packet.GouraudLine.vertex[ 1 ].n_coord ) );
+ n_yend = SINT11( COORD_Y( m_packet.GouraudLine.vertex[ 1 ].n_coord ) );
+ n_cr2.w.h = BGR_R( m_packet.GouraudLine.vertex[ 1 ].n_bgr ); n_cr1.w.l = 0;
+ n_cg2.w.h = BGR_G( m_packet.GouraudLine.vertex[ 1 ].n_bgr ); n_cg1.w.l = 0;
+ n_cb2.w.h = BGR_B( m_packet.GouraudLine.vertex[ 1 ].n_bgr ); n_cb1.w.l = 0;
+
+ n_x.sw.h = n_xstart; n_x.sw.l = 0;
+ n_y.sw.h = n_ystart; n_y.sw.l = 0;
+ n_r.d = n_cr1.d;
+ n_g.d = n_cg1.d;
+ n_b.d = n_cb1.d;
+
+ if( n_xend > n_xstart )
+ {
+ n_xlen = n_xend - n_xstart;
+ }
+ else
+ {
+ n_xlen = n_xstart - n_xend;
+ }
+
+ if( n_yend > n_ystart )
+ {
+ n_ylen = n_yend - n_ystart;
+ }
+ else
+ {
+ n_ylen = n_ystart - n_yend;
+ }
+
+ if( n_xlen > n_ylen )
+ {
+ n_distance = n_xlen;
+ }
+ else
+ {
+ n_distance = n_ylen;
+ }
+
+ if( n_distance == 0 )
+ {
+ n_distance = 1;
+ }
+
+ n_dx = (INT32)( ( n_xend << 16 ) - n_x.sd ) / n_distance;
+ n_dy = (INT32)( ( n_yend << 16 ) - n_y.sd ) / n_distance;
+ n_dr = (INT32)( n_cr2.d - n_cr1.d ) / n_distance;
+ n_dg = (INT32)( n_cg2.d - n_cg1.d ) / n_distance;
+ n_db = (INT32)( n_cb2.d - n_cb1.d ) / n_distance;
+
+ while( n_distance > 0 )
+ {
+ int drawx = n_x.sw.h + n_drawoffset_x;
+ int drawy = n_y.sw.h + n_drawoffset_y;
+
+ if( drawx >= (INT32)n_drawarea_x1 && drawy >= (INT32)n_drawarea_y1 &&
+ drawx <= (INT32)n_drawarea_x2 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ p_vram = p_p_vram[ drawy ] + drawx;
+ WRITE_PIXEL(
+ p_n_redshade[ MID_LEVEL | n_r.w.h ] |
+ p_n_greenshade[ MID_LEVEL | n_g.w.h ] |
+ p_n_blueshade[ MID_LEVEL | n_b.w.h ] );
+ }
+ n_x.sd += n_dx;
+ n_y.sd += n_dy;
+ n_r.d += n_dr;
+ n_g.d += n_dg;
+ n_b.d += n_db;
+ n_distance--;
+ }
+}
+
+void psxgpu_device::FrameBufferRectangleDraw( void )
+{
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ INT32 n_distance;
+ INT32 n_h;
+ INT16 n_y;
+ INT16 n_x;
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 7 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) ), SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) ) );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) ) + SIZE_W( m_packet.FlatRectangle.n_size ), SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) ) );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) ), SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) ) + SIZE_H( m_packet.FlatRectangle.n_size ) );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) ) + SIZE_W( m_packet.FlatRectangle.n_size ), SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) ) + SIZE_H( m_packet.FlatRectangle.n_size ) );
+ DebugMeshEnd();
+#endif
+
+ n_r.w.h = BGR_R( m_packet.FlatRectangle.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.FlatRectangle.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.FlatRectangle.n_bgr ); n_b.w.l = 0;
+
+ n_y = COORD_Y( m_packet.FlatRectangle.n_coord );
+ n_h = SIZE_H( m_packet.FlatRectangle.n_size );
+
+ while( n_h > 0 )
+ {
+ n_x = COORD_X( m_packet.FlatRectangle.n_coord );
+
+ n_distance = SIZE_W( m_packet.FlatRectangle.n_size );
+ while( n_distance > 0 )
+ {
+ p_vram = p_p_vram[ n_y & 1023 ] + ( n_x & 1023 );
+ WRITE_PIXEL(
+ p_n_redshade[ MID_LEVEL | n_r.w.h ] |
+ p_n_greenshade[ MID_LEVEL | n_g.w.h ] |
+ p_n_blueshade[ MID_LEVEL | n_b.w.h ] );
+ n_x++;
+ n_distance--;
+ }
+ n_y++;
+ n_h--;
+ }
+}
+
+void psxgpu_device::FlatRectangle( void )
+{
+ INT16 n_y;
+ INT16 n_x;
+
+ UINT8 n_cmd;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+
+ INT32 n_distance;
+ INT32 n_h;
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 8 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) ) + n_drawoffset_x + SIZE_W( m_packet.FlatRectangle.n_size ), SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) ) + n_drawoffset_y + SIZE_H( m_packet.FlatRectangle.n_size ) );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) ) + n_drawoffset_x + SIZE_W( m_packet.FlatRectangle.n_size ), SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) ) + n_drawoffset_y + SIZE_H( m_packet.FlatRectangle.n_size ) );
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.FlatRectangle.n_bgr );
+
+ SOLIDSETUP
+
+ n_r.w.h = BGR_R( m_packet.FlatRectangle.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.FlatRectangle.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.FlatRectangle.n_bgr ); n_b.w.l = 0;
+
+ n_x = SINT11( COORD_X( m_packet.FlatRectangle.n_coord ) );
+ n_y = SINT11( COORD_Y( m_packet.FlatRectangle.n_coord ) );
+ n_h = SIZE_H( m_packet.FlatRectangle.n_size );
+
+ while( n_h > 0 )
+ {
+ n_distance = SIZE_W( m_packet.FlatRectangle.n_size );
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( n_distance > 0 && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ SOLIDFILL( FLATRECTANGEUPDATE )
+ }
+ n_y++;
+ n_h--;
+ }
+}
+
+void psxgpu_device::FlatRectangle8x8( void )
+{
+ INT16 n_y;
+ INT16 n_x;
+
+ UINT8 n_cmd;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+
+ INT32 n_distance;
+ INT32 n_h;
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 9 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle8x8.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatRectangle8x8.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle8x8.n_coord ) ) + n_drawoffset_x + 8, SINT11( COORD_Y( m_packet.FlatRectangle8x8.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle8x8.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatRectangle8x8.n_coord ) ) + n_drawoffset_y + 8 );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle8x8.n_coord ) ) + n_drawoffset_x + 8, SINT11( COORD_Y( m_packet.FlatRectangle8x8.n_coord ) ) + n_drawoffset_y + 8 );
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.FlatRectangle8x8.n_bgr );
+
+ SOLIDSETUP
+
+ n_r.w.h = BGR_R( m_packet.FlatRectangle8x8.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.FlatRectangle8x8.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.FlatRectangle8x8.n_bgr ); n_b.w.l = 0;
+
+ n_x = SINT11( COORD_X( m_packet.FlatRectangle8x8.n_coord ) );
+ n_y = SINT11( COORD_Y( m_packet.FlatRectangle8x8.n_coord ) );
+ n_h = 8;
+
+ while( n_h > 0 )
+ {
+ n_distance = 8;
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( n_distance > 0 && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ SOLIDFILL( FLATRECTANGEUPDATE )
+ }
+ n_y++;
+ n_h--;
+ }
+}
+
+void psxgpu_device::FlatRectangle16x16( void )
+{
+ INT16 n_y;
+ INT16 n_x;
+
+ UINT8 n_cmd;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+
+ INT32 n_distance;
+ INT32 n_h;
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 10 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle16x16.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatRectangle16x16.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle16x16.n_coord ) ) + n_drawoffset_x + 16, SINT11( COORD_Y( m_packet.FlatRectangle16x16.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle16x16.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatRectangle16x16.n_coord ) ) + n_drawoffset_y + 16 );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatRectangle16x16.n_coord ) ) + n_drawoffset_x + 16, SINT11( COORD_Y( m_packet.FlatRectangle16x16.n_coord ) ) + n_drawoffset_y + 16 );
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.FlatRectangle16x16.n_bgr );
+
+ SOLIDSETUP
+
+ n_r.w.h = BGR_R( m_packet.FlatRectangle16x16.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.FlatRectangle16x16.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.FlatRectangle16x16.n_bgr ); n_b.w.l = 0;
+
+ n_x = SINT11( COORD_X( m_packet.FlatRectangle16x16.n_coord ) );
+ n_y = SINT11( COORD_Y( m_packet.FlatRectangle16x16.n_coord ) );
+ n_h = 16;
+
+ while( n_h > 0 )
+ {
+ n_distance = 16;
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( n_distance > 0 && n_y >= (INT32)n_drawarea_y1 && n_y <= (INT32)n_drawarea_y2 )
+ {
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ SOLIDFILL( FLATRECTANGEUPDATE )
+ }
+ n_y++;
+ n_h--;
+ }
+}
+
+void psxgpu_device::FlatTexturedRectangle( void )
+{
+ INT16 n_y;
+ INT16 n_x;
+ int n_tx;
+ int n_ty;
+
+ UINT8 n_cmd;
+
+ UINT32 n_clutx;
+ UINT32 n_cluty;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ UINT8 n_u;
+ UINT8 n_v;
+ int n_du;
+ int n_dv;
+
+ INT16 n_distance;
+ UINT32 n_h;
+ UINT16 *p_vram;
+ UINT16 *p_clut;
+ UINT16 n_bgr;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 11 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.FlatTexturedRectangle.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatTexturedRectangle.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatTexturedRectangle.n_coord ) ) + n_drawoffset_x + SIZE_W( m_packet.FlatTexturedRectangle.n_size ), SINT11( COORD_Y( m_packet.FlatTexturedRectangle.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatTexturedRectangle.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.FlatTexturedRectangle.n_coord ) ) + n_drawoffset_y + SIZE_H( m_packet.FlatTexturedRectangle.n_size ) );
+ DebugMesh( SINT11( COORD_X( m_packet.FlatTexturedRectangle.n_coord ) ) + n_drawoffset_x + SIZE_W( m_packet.FlatTexturedRectangle.n_size ), SINT11( COORD_Y( m_packet.FlatTexturedRectangle.n_coord ) ) + n_drawoffset_y + SIZE_H( m_packet.FlatTexturedRectangle.n_size ) );
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.FlatTexturedRectangle.n_bgr );
+
+ n_clutx = ( m_packet.FlatTexturedRectangle.n_texture.w.h & 0x3f ) << 4;
+ n_cluty = ( m_packet.FlatTexturedRectangle.n_texture.w.h >> 6 ) & 0x3ff;
+
+ n_r.d = 0;
+ n_g.d = 0;
+ n_b.d = 0;
+
+ TEXTURESETUP
+ SPRITESETUP
+
+ switch( n_cmd & 0x01 )
+ {
+ case 0:
+ n_r.w.h = BGR_R( m_packet.FlatTexturedRectangle.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.FlatTexturedRectangle.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.FlatTexturedRectangle.n_bgr ); n_b.w.l = 0;
+ break;
+ case 1:
+ n_r.w.h = 0x80; n_r.w.l = 0;
+ n_g.w.h = 0x80; n_g.w.l = 0;
+ n_b.w.h = 0x80; n_b.w.l = 0;
+ break;
+ }
+
+ n_x = SINT11( COORD_X( m_packet.FlatTexturedRectangle.n_coord ) );
+ n_y = SINT11( COORD_Y( m_packet.FlatTexturedRectangle.n_coord ) );
+ n_v = TEXTURE_V( m_packet.FlatTexturedRectangle.n_texture );
+ n_h = SIZE_H( m_packet.FlatTexturedRectangle.n_size );
+
+ while( n_h > 0 )
+ {
+ n_u = TEXTURE_U( m_packet.FlatTexturedRectangle.n_texture );
+ n_distance = SIZE_W( m_packet.FlatTexturedRectangle.n_size );
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( n_distance > 0 && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_u += ( n_drawarea_x1 - drawx ) * n_du;
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ TEXTUREFILL( FLATTEXTUREDRECTANGLEUPDATE, n_u, n_v );
+ }
+ n_v += n_dv;
+ n_y++;
+ n_h--;
+ }
+}
+
+void psxgpu_device::Sprite8x8( void )
+{
+ INT16 n_y;
+ INT16 n_x;
+ int n_tx;
+ int n_ty;
+
+ UINT8 n_cmd;
+
+ UINT32 n_clutx;
+ UINT32 n_cluty;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ UINT8 n_u;
+ UINT8 n_v;
+ int n_du;
+ int n_dv;
+
+ INT16 n_distance;
+ UINT32 n_h;
+ UINT16 *p_vram;
+ UINT16 *p_clut;
+ UINT16 n_bgr;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 12 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.Sprite8x8.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.Sprite8x8.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.Sprite8x8.n_coord ) ) + n_drawoffset_x + 7, SINT11( COORD_Y( m_packet.Sprite8x8.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.Sprite8x8.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.Sprite8x8.n_coord ) ) + n_drawoffset_y + 7 );
+ DebugMesh( SINT11( COORD_X( m_packet.Sprite8x8.n_coord ) ) + n_drawoffset_x + 7, SINT11( COORD_Y( m_packet.Sprite8x8.n_coord ) ) + n_drawoffset_y + 7 );
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.Sprite8x8.n_bgr );
+
+ n_clutx = ( m_packet.Sprite8x8.n_texture.w.h & 0x3f ) << 4;
+ n_cluty = ( m_packet.Sprite8x8.n_texture.w.h >> 6 ) & 0x3ff;
+
+ n_r.d = 0;
+ n_g.d = 0;
+ n_b.d = 0;
+
+ TEXTURESETUP
+ SPRITESETUP
+
+ switch( n_cmd & 0x01 )
+ {
+ case 0:
+ n_r.w.h = BGR_R( m_packet.Sprite8x8.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.Sprite8x8.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.Sprite8x8.n_bgr ); n_b.w.l = 0;
+ break;
+ case 1:
+ n_r.w.h = 0x80; n_r.w.l = 0;
+ n_g.w.h = 0x80; n_g.w.l = 0;
+ n_b.w.h = 0x80; n_b.w.l = 0;
+ break;
+ }
+
+ n_x = SINT11( COORD_X( m_packet.Sprite8x8.n_coord ) );
+ n_y = SINT11( COORD_Y( m_packet.Sprite8x8.n_coord ) );
+ n_v = TEXTURE_V( m_packet.Sprite8x8.n_texture );
+ n_h = 8;
+
+ while( n_h > 0 )
+ {
+ n_u = TEXTURE_U( m_packet.Sprite8x8.n_texture );
+ n_distance = 8;
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( n_distance > 0 && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_u += ( n_drawarea_x1 - drawx ) * n_du;
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ TEXTUREFILL( FLATTEXTUREDRECTANGLEUPDATE, n_u, n_v );
+ }
+ n_v += n_dv;
+ n_y++;
+ n_h--;
+ }
+}
+
+void psxgpu_device::Sprite16x16( void )
+{
+ INT16 n_y;
+ INT16 n_x;
+ int n_tx;
+ int n_ty;
+
+ UINT8 n_cmd;
+
+ UINT32 n_clutx;
+ UINT32 n_cluty;
+
+ UINT16 *p_n_f;
+ UINT16 *p_n_redb;
+ UINT16 *p_n_greenb;
+ UINT16 *p_n_blueb;
+ UINT16 *p_n_redtrans;
+ UINT16 *p_n_greentrans;
+ UINT16 *p_n_bluetrans;
+
+ PAIR n_r;
+ PAIR n_g;
+ PAIR n_b;
+ UINT8 n_u;
+ UINT8 n_v;
+ int n_du;
+ int n_dv;
+
+ INT16 n_distance;
+ UINT32 n_h;
+ UINT16 *p_vram;
+ UINT16 *p_clut;
+ UINT16 n_bgr;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 13 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.Sprite16x16.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.Sprite16x16.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.Sprite16x16.n_coord ) ) + n_drawoffset_x + 7, SINT11( COORD_Y( m_packet.Sprite16x16.n_coord ) ) + n_drawoffset_y );
+ DebugMesh( SINT11( COORD_X( m_packet.Sprite16x16.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.Sprite16x16.n_coord ) ) + n_drawoffset_y + 7 );
+ DebugMesh( SINT11( COORD_X( m_packet.Sprite16x16.n_coord ) ) + n_drawoffset_x + 7, SINT11( COORD_Y( m_packet.Sprite16x16.n_coord ) ) + n_drawoffset_y + 7 );
+ DebugMeshEnd();
+#endif
+
+ n_cmd = BGR_C( m_packet.Sprite16x16.n_bgr );
+
+ n_clutx = ( m_packet.Sprite16x16.n_texture.w.h & 0x3f ) << 4;
+ n_cluty = ( m_packet.Sprite16x16.n_texture.w.h >> 6 ) & 0x3ff;
+
+ n_r.d = 0;
+ n_g.d = 0;
+ n_b.d = 0;
+
+ TEXTURESETUP
+ SPRITESETUP
+
+ switch( n_cmd & 0x01 )
+ {
+ case 0:
+ n_r.w.h = BGR_R( m_packet.Sprite16x16.n_bgr ); n_r.w.l = 0;
+ n_g.w.h = BGR_G( m_packet.Sprite16x16.n_bgr ); n_g.w.l = 0;
+ n_b.w.h = BGR_B( m_packet.Sprite16x16.n_bgr ); n_b.w.l = 0;
+ break;
+ case 1:
+ n_r.w.h = 0x80; n_r.w.l = 0;
+ n_g.w.h = 0x80; n_g.w.l = 0;
+ n_b.w.h = 0x80; n_b.w.l = 0;
+ break;
+ }
+
+ n_x = SINT11( COORD_X( m_packet.Sprite16x16.n_coord ) );
+ n_y = SINT11( COORD_Y( m_packet.Sprite16x16.n_coord ) );
+ n_v = TEXTURE_V( m_packet.Sprite16x16.n_texture );
+ n_h = 16;
+
+ while( n_h > 0 )
+ {
+ n_u = TEXTURE_U( m_packet.Sprite16x16.n_texture );
+ n_distance = 16;
+
+ int drawy = n_y + n_drawoffset_y;
+
+ if( n_distance > 0 && drawy >= (INT32)n_drawarea_y1 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ int drawx = n_x + n_drawoffset_x;
+
+ if( ( (INT32)n_drawarea_x1 - drawx ) > 0 )
+ {
+ n_u += ( n_drawarea_x1 - drawx ) * n_du;
+ n_distance -= ( n_drawarea_x1 - drawx );
+ drawx = n_drawarea_x1;
+ }
+
+ TEXTUREFILL( FLATTEXTUREDRECTANGLEUPDATE, n_u, n_v );
+ }
+ n_v += n_dv;
+ n_y++;
+ n_h--;
+ }
+}
+
+void psxgpu_device::Dot( void )
+{
+ INT32 n_x;
+ INT32 n_y;
+ UINT32 n_r;
+ UINT32 n_g;
+ UINT32 n_b;
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 14 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.Dot.vertex.n_coord ) ) + n_drawoffset_x, SINT11( COORD_Y( m_packet.Dot.vertex.n_coord ) ) + n_drawoffset_y );
+ DebugMeshEnd();
+#endif
+
+ n_r = BGR_R( m_packet.Dot.n_bgr );
+ n_g = BGR_G( m_packet.Dot.n_bgr );
+ n_b = BGR_B( m_packet.Dot.n_bgr );
+ n_x = SINT11( COORD_X( m_packet.Dot.vertex.n_coord ) );
+ n_y = SINT11( COORD_Y( m_packet.Dot.vertex.n_coord ) );
+
+ int drawx = n_x + n_drawoffset_x;
+ int drawy = n_y + n_drawoffset_y;
+
+ if( drawx >= (INT32)n_drawarea_x1 && drawy >= (INT32)n_drawarea_y1 &&
+ drawx <= (INT32)n_drawarea_x2 && drawy <= (INT32)n_drawarea_y2 )
+ {
+ p_vram = p_p_vram[ drawy ] + drawx;
+ WRITE_PIXEL(
+ p_n_redshade[ MID_LEVEL | n_r ] |
+ p_n_greenshade[ MID_LEVEL | n_g ] |
+ p_n_blueshade[ MID_LEVEL | n_b ] );
+ }
+}
+
+void psxgpu_device::MoveImage( void )
+{
+ INT16 n_w;
+ INT16 n_h;
+ INT16 n_srcx;
+ INT16 n_srcy;
+ INT16 n_dsty;
+ INT16 n_dstx;
+ UINT16 *p_vram;
+
+#if DEBUG_VIEWER
+ if( m_debug.n_skip == 15 )
+ {
+ return;
+ }
+ DebugMesh( SINT11( COORD_X( m_packet.MoveImage.vertex[ 1 ].n_coord ) ), SINT11( COORD_Y( m_packet.MoveImage.vertex[ 1 ].n_coord ) ) );
+ DebugMesh( SINT11( COORD_X( m_packet.MoveImage.vertex[ 1 ].n_coord ) ) + SIZE_W( m_packet.MoveImage.n_size ), SINT11( COORD_Y( m_packet.MoveImage.vertex[ 1 ].n_coord ) ) );
+ DebugMesh( SINT11( COORD_X( m_packet.MoveImage.vertex[ 1 ].n_coord ) ), SINT11( COORD_Y( m_packet.MoveImage.vertex[ 1 ].n_coord ) ) + SIZE_H( m_packet.MoveImage.n_size ) );
+ DebugMesh( SINT11( COORD_X( m_packet.MoveImage.vertex[ 1 ].n_coord ) ) + SIZE_W( m_packet.MoveImage.n_size ), SINT11( COORD_Y( m_packet.MoveImage.vertex[ 1 ].n_coord ) ) + SIZE_H( m_packet.MoveImage.n_size ) );
+ DebugMeshEnd();
+#endif
+
+ n_srcy = COORD_Y( m_packet.MoveImage.vertex[ 0 ].n_coord );
+ n_dsty = COORD_Y( m_packet.MoveImage.vertex[ 1 ].n_coord );
+ n_h = SIZE_H( m_packet.MoveImage.n_size );
+
+ while( n_h > 0 )
+ {
+ n_srcx = COORD_X( m_packet.MoveImage.vertex[ 0 ].n_coord );
+ n_dstx = COORD_X( m_packet.MoveImage.vertex[ 1 ].n_coord );
+ n_w = SIZE_W( m_packet.MoveImage.n_size );
+ while( n_w > 0 )
+ {
+ p_vram = p_p_vram[ n_dsty & 1023 ] + ( n_dstx & 1023 );
+ WRITE_PIXEL( *( p_p_vram[ n_srcy & 1023 ] + ( n_srcx & 1023 ) ) );
+ n_srcx++;
+ n_dstx++;
+ n_w--;
+ }
+ n_srcy++;
+ n_dsty++;
+ n_h--;
+ }
+}
+
+void psxgpu_device::dma_write( UINT32 *p_n_psxram, UINT32 n_address, INT32 n_size )
+{
+ gpu_write( &p_n_psxram[ n_address / 4 ], n_size );
+}
+
+void psxgpu_device::gpu_write( UINT32 *p_ram, INT32 n_size )
+{
+ while( n_size > 0 )
+ {
+ UINT32 data = *( p_ram );
+
+ verboselog( machine(), 2, "PSX Packet #%u %08x\n", n_gpu_buffer_offset, data );
+ m_packet.n_entry[ n_gpu_buffer_offset ] = data;
+ switch( m_packet.n_entry[ 0 ] >> 24 )
+ {
+ case 0x00:
+ verboselog( machine(), 1, "not handled: GPU Command 0x00: (%08x)\n", data );
+ break;
+ case 0x01:
+ verboselog( machine(), 1, "not handled: clear cache\n" );
+ break;
+ case 0x02:
+ if( n_gpu_buffer_offset < 2 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: frame buffer rectangle %u,%u %u,%u\n", m_packet.n_entry[ 0 ] >> 24,
+ m_packet.n_entry[ 1 ] & 0xffff, m_packet.n_entry[ 1 ] >> 16, m_packet.n_entry[ 2 ] & 0xffff, m_packet.n_entry[ 2 ] >> 16 );
+ FrameBufferRectangleDraw();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ if( n_gpu_buffer_offset < 3 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: monochrome 3 point polygon\n", m_packet.n_entry[ 0 ] >> 24 );
+ FlatPolygon( 3 );
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ if( n_gpu_buffer_offset < 6 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: textured 3 point polygon\n", m_packet.n_entry[ 0 ] >> 24 );
+ FlatTexturedPolygon( 3 );
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ if( n_gpu_buffer_offset < 4 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: monochrome 4 point polygon\n", m_packet.n_entry[ 0 ] >> 24 );
+ FlatPolygon( 4 );
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x2c:
+ case 0x2d:
+ case 0x2e:
+ case 0x2f:
+ if( n_gpu_buffer_offset < 8 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: textured 4 point polygon\n", m_packet.n_entry[ 0 ] >> 24 );
+ FlatTexturedPolygon( 4 );
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ if( n_gpu_buffer_offset < 5 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: gouraud 3 point polygon\n", m_packet.n_entry[ 0 ] >> 24 );
+ GouraudPolygon( 3 );
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ if( n_gpu_buffer_offset < 8 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: gouraud textured 3 point polygon\n", m_packet.n_entry[ 0 ] >> 24 );
+ GouraudTexturedPolygon( 3 );
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x38:
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ if( n_gpu_buffer_offset < 7 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: gouraud 4 point polygon\n", m_packet.n_entry[ 0 ] >> 24 );
+ GouraudPolygon( 4 );
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x3c:
+ case 0x3d:
+ case 0x3e:
+ case 0x3f:
+ if( n_gpu_buffer_offset < 11 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: gouraud textured 4 point polygon\n", m_packet.n_entry[ 0 ] >> 24 );
+ GouraudTexturedPolygon( 4 );
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ if( n_gpu_buffer_offset < 2 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: monochrome line\n", m_packet.n_entry[ 0 ] >> 24 );
+ MonochromeLine();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x48:
+ case 0x4a:
+ case 0x4c:
+ case 0x4e:
+ if( n_gpu_buffer_offset < 3 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: monochrome polyline\n", m_packet.n_entry[ 0 ] >> 24 );
+ MonochromeLine();
+ if( ( m_packet.n_entry[ 3 ] & 0xf000f000 ) != 0x50005000 )
+ {
+ m_packet.n_entry[ 1 ] = m_packet.n_entry[ 2 ];
+ m_packet.n_entry[ 2 ] = m_packet.n_entry[ 3 ];
+ n_gpu_buffer_offset = 3;
+ }
+ else
+ {
+ n_gpu_buffer_offset = 0;
+ }
+ }
+ break;
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ if( n_gpu_buffer_offset < 3 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: gouraud line\n", m_packet.n_entry[ 0 ] >> 24 );
+ GouraudLine();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x58:
+ case 0x5a:
+ case 0x5c:
+ case 0x5e:
+ if( n_gpu_buffer_offset < 5 &&
+ ( n_gpu_buffer_offset != 4 || ( m_packet.n_entry[ 4 ] & 0xf000f000 ) != 0x50005000 ) )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: gouraud polyline\n", m_packet.n_entry[ 0 ] >> 24 );
+ GouraudLine();
+ if( ( m_packet.n_entry[ 4 ] & 0xf000f000 ) != 0x50005000 )
+ {
+ m_packet.n_entry[ 0 ] = ( m_packet.n_entry[ 0 ] & 0xff000000 ) | ( m_packet.n_entry[ 2 ] & 0x00ffffff );
+ m_packet.n_entry[ 1 ] = m_packet.n_entry[ 3 ];
+ m_packet.n_entry[ 2 ] = m_packet.n_entry[ 4 ];
+ m_packet.n_entry[ 3 ] = m_packet.n_entry[ 5 ];
+ n_gpu_buffer_offset = 4;
+ }
+ else
+ {
+ n_gpu_buffer_offset = 0;
+ }
+ }
+ break;
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ if( n_gpu_buffer_offset < 2 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: rectangle %d,%d %d,%d\n",
+ m_packet.n_entry[ 0 ] >> 24,
+ (INT16)( m_packet.n_entry[ 1 ] & 0xffff ), (INT16)( m_packet.n_entry[ 1 ] >> 16 ),
+ (INT16)( m_packet.n_entry[ 2 ] & 0xffff ), (INT16)( m_packet.n_entry[ 2 ] >> 16 ) );
+ FlatRectangle();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ if( n_gpu_buffer_offset < 3 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: sprite %d,%d %u,%u %08x, %08x\n",
+ m_packet.n_entry[ 0 ] >> 24,
+ (INT16)( m_packet.n_entry[ 1 ] & 0xffff ), (INT16)( m_packet.n_entry[ 1 ] >> 16 ),
+ m_packet.n_entry[ 3 ] & 0xffff, m_packet.n_entry[ 3 ] >> 16,
+ m_packet.n_entry[ 0 ], m_packet.n_entry[ 2 ] );
+ FlatTexturedRectangle();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x68:
+ case 0x6a:
+ if( n_gpu_buffer_offset < 1 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: dot %d,%d %08x\n",
+ m_packet.n_entry[ 0 ] >> 24,
+ (INT16)( m_packet.n_entry[ 1 ] & 0xffff ), (INT16)( m_packet.n_entry[ 1 ] >> 16 ),
+ m_packet.n_entry[ 0 ] & 0xffffff );
+ Dot();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x70:
+ case 0x71:
+ /* 8*8 rectangle */
+ if( n_gpu_buffer_offset < 1 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: 16x16 rectangle %08x %08x\n", m_packet.n_entry[ 0 ] >> 24,
+ m_packet.n_entry[ 0 ], m_packet.n_entry[ 1 ] );
+ FlatRectangle8x8();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ if( n_gpu_buffer_offset < 2 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: 8x8 sprite %08x %08x %08x\n", m_packet.n_entry[ 0 ] >> 24,
+ m_packet.n_entry[ 0 ], m_packet.n_entry[ 1 ], m_packet.n_entry[ 2 ] );
+ Sprite8x8();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x78:
+ case 0x79:
+ /* 16*16 rectangle */
+ if( n_gpu_buffer_offset < 1 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: 16x16 rectangle %08x %08x\n", m_packet.n_entry[ 0 ] >> 24,
+ m_packet.n_entry[ 0 ], m_packet.n_entry[ 1 ] );
+ FlatRectangle16x16();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x7c:
+ case 0x7d:
+ case 0x7e:
+ case 0x7f:
+ if( n_gpu_buffer_offset < 2 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: 16x16 sprite %08x %08x %08x\n", m_packet.n_entry[ 0 ] >> 24,
+ m_packet.n_entry[ 0 ], m_packet.n_entry[ 1 ], m_packet.n_entry[ 2 ] );
+ Sprite16x16();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0x80:
+ if( n_gpu_buffer_offset < 3 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "move image in frame buffer %08x %08x %08x %08x\n", m_packet.n_entry[ 0 ], m_packet.n_entry[ 1 ], m_packet.n_entry[ 2 ], m_packet.n_entry[ 3 ] );
+ MoveImage();
+ n_gpu_buffer_offset = 0;
+ }
+ break;
+ case 0xa0:
+ if( n_gpu_buffer_offset < 3 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ UINT32 n_pixel;
+ for( n_pixel = 0; n_pixel < 2; n_pixel++ )
+ {
+ UINT16 *p_vram;
+
+ verboselog( machine(), 2, "send image to framebuffer ( pixel %u,%u = %u )\n",
+ ( n_vramx + m_packet.n_entry[ 1 ] ) & 1023,
+ ( n_vramy + ( m_packet.n_entry[ 1 ] >> 16 ) ) & 1023,
+ data & 0xffff );
+
+ p_vram = p_p_vram[ ( n_vramy + ( m_packet.n_entry[ 1 ] >> 16 ) ) & 1023 ] + ( ( n_vramx + m_packet.n_entry[ 1 ] ) & 1023 );
+ WRITE_PIXEL( data & 0xffff );
+ n_vramx++;
+ if( n_vramx >= ( m_packet.n_entry[ 2 ] & 0xffff ) )
+ {
+ n_vramx = 0;
+ n_vramy++;
+ if( n_vramy >= ( m_packet.n_entry[ 2 ] >> 16 ) )
+ {
+ verboselog( machine(), 1, "%02x: send image to framebuffer %u,%u %u,%u\n", m_packet.n_entry[ 0 ] >> 24,
+ m_packet.n_entry[ 1 ] & 0xffff, ( m_packet.n_entry[ 1 ] >> 16 ),
+ m_packet.n_entry[ 2 ] & 0xffff, ( m_packet.n_entry[ 2 ] >> 16 ) );
+ n_gpu_buffer_offset = 0;
+ n_vramx = 0;
+ n_vramy = 0;
+ break;
+ }
+ }
+ data >>= 16;
+ }
+ }
+ break;
+ case 0xc0:
+ if( n_gpu_buffer_offset < 2 )
+ {
+ n_gpu_buffer_offset++;
+ }
+ else
+ {
+ verboselog( machine(), 1, "%02x: copy image from frame buffer\n", m_packet.n_entry[ 0 ] >> 24 );
+ n_gpustatus |= ( 1L << 0x1b );
+ }
+ break;
+ case 0xe1:
+ verboselog( machine(), 1, "%02x: draw mode %06x\n", m_packet.n_entry[ 0 ] >> 24,
+ m_packet.n_entry[ 0 ] & 0xffffff );
+ decode_tpage( m_packet.n_entry[ 0 ] & 0xffffff );
+ break;
+ case 0xe2:
+ n_twy = ( ( ( m_packet.n_entry[ 0 ] >> 15 ) & 0x1f ) << 3 );
+ n_twx = ( ( ( m_packet.n_entry[ 0 ] >> 10 ) & 0x1f ) << 3 );
+ n_twh = 255 - ( ( ( m_packet.n_entry[ 0 ] >> 5 ) & 0x1f ) << 3 );
+ n_tww = 255 - ( ( m_packet.n_entry[ 0 ] & 0x1f ) << 3 );
+ verboselog( machine(), 1, "%02x: texture window %u,%u %u,%u\n", m_packet.n_entry[ 0 ] >> 24,
+ n_twx, n_twy, n_tww, n_twh );
+ break;
+ case 0xe3:
+ n_drawarea_x1 = m_packet.n_entry[ 0 ] & 1023;
+ if( m_n_gputype == 2 )
+ {
+ n_drawarea_y1 = ( m_packet.n_entry[ 0 ] >> 10 ) & 1023;
+ }
+ else
+ {
+ n_drawarea_y1 = ( m_packet.n_entry[ 0 ] >> 12 ) & 1023;
+ }
+ verboselog( machine(), 1, "%02x: drawing area top left %d,%d\n", m_packet.n_entry[ 0 ] >> 24,
+ n_drawarea_x1, n_drawarea_y1 );
+ break;
+ case 0xe4:
+ n_drawarea_x2 = m_packet.n_entry[ 0 ] & 1023;
+ if( m_n_gputype == 2 )
+ {
+ n_drawarea_y2 = ( m_packet.n_entry[ 0 ] >> 10 ) & 1023;
+ }
+ else
+ {
+ n_drawarea_y2 = ( m_packet.n_entry[ 0 ] >> 12 ) & 1023;
+ }
+ verboselog( machine(), 1, "%02x: drawing area bottom right %d,%d\n", m_packet.n_entry[ 0 ] >> 24,
+ n_drawarea_x2, n_drawarea_y2 );
+ break;
+ case 0xe5:
+ n_drawoffset_x = SINT11( m_packet.n_entry[ 0 ] & 2047 );
+ if( m_n_gputype == 2 )
+ {
+ n_drawoffset_y = SINT11( ( m_packet.n_entry[ 0 ] >> 11 ) & 2047 );
+ }
+ else
+ {
+ n_drawoffset_y = SINT11( ( m_packet.n_entry[ 0 ] >> 12 ) & 2047 );
+ }
+ verboselog( machine(), 1, "%02x: drawing offset %d,%d\n", m_packet.n_entry[ 0 ] >> 24,
+ n_drawoffset_x, n_drawoffset_y );
+ break;
+ case 0xe6:
+ n_gpustatus &= ~( 3L << 0xb );
+ n_gpustatus |= ( data & 0x03 ) << 0xb;
+ if( ( m_packet.n_entry[ 0 ] & 3 ) != 0 )
+ {
+ verboselog( machine(), 1, "not handled: mask setting %d\n", m_packet.n_entry[ 0 ] & 3 );
+ }
+ else
+ {
+ verboselog( machine(), 1, "mask setting %d\n", m_packet.n_entry[ 0 ] & 3 );
+ }
+ break;
+ default:
+#if defined( MAME_DEBUG )
+ popmessage( "unknown GPU packet %08x", m_packet.n_entry[ 0 ] );
+#endif
+ verboselog( machine(), 0, "unknown GPU packet %08x (%08x)\n", m_packet.n_entry[ 0 ], data );
+#if ( STOP_ON_ERROR )
+ n_gpu_buffer_offset = 1;
+#endif
+ break;
+ }
+ p_ram++;
+ n_size--;
+ }
+}
+
+WRITE32_MEMBER( psxgpu_device::write )
+{
+ switch( offset )
+ {
+ case 0x00:
+ gpu_write( &data, 1 );
+ break;
+ case 0x01:
+ switch( data >> 24 )
+ {
+ case 0x00:
+ gpu_reset();
+ break;
+ case 0x01:
+ verboselog( machine(), 1, "not handled: reset command buffer\n" );
+ n_gpu_buffer_offset = 0;
+ break;
+ case 0x02:
+ verboselog( machine(), 1, "not handled: reset irq\n" );
+ break;
+ case 0x03:
+ n_gpustatus &= ~( 1L << 0x17 );
+ n_gpustatus |= ( data & 0x01 ) << 0x17;
+ break;
+ case 0x04:
+ verboselog( machine(), 1, "dma setup %d\n", data & 3 );
+ n_gpustatus &= ~( 3L << 0x1d );
+ n_gpustatus |= ( data & 0x03 ) << 0x1d;
+ n_gpustatus &= ~( 1L << 0x19 );
+ if( ( data & 3 ) == 1 || ( data & 3 ) == 2 )
+ {
+ n_gpustatus |= ( 1L << 0x19 );
+ }
+ break;
+ case 0x05:
+ m_n_displaystartx = data & 1023;
+ if( m_n_gputype == 2 )
+ {
+ n_displaystarty = ( data >> 10 ) & 1023;
+ }
+ else
+ {
+ n_displaystarty = ( data >> 12 ) & 1023;
+ }
+ verboselog( machine(), 1, "start of display area %d %d\n", m_n_displaystartx, n_displaystarty );
+ break;
+ case 0x06:
+ n_horiz_disstart = data & 4095;
+ n_horiz_disend = ( data >> 12 ) & 4095;
+ verboselog( machine(), 1, "horizontal display range %d %d\n", n_horiz_disstart, n_horiz_disend );
+ break;
+ case 0x07:
+ n_vert_disstart = data & 1023;
+ n_vert_disend = ( data >> 10 ) & 2047;
+ verboselog( machine(), 1, "vertical display range %d %d\n", n_vert_disstart, n_vert_disend );
+ break;
+ case 0x08:
+ verboselog( machine(), 1, "display mode %02x\n", data & 0xff );
+ n_gpustatus &= ~( 127L << 0x10 );
+ n_gpustatus |= ( data & 0x3f ) << 0x11; /* width 0 + height + videmode + isrgb24 + isinter */
+ n_gpustatus |= ( ( data & 0x40 ) >> 0x06 ) << 0x10; /* width 1 */
+ if( m_n_gputype == 1 )
+ {
+ b_reverseflag = ( data >> 7 ) & 1;
+ }
+ updatevisiblearea();
+ break;
+ case 0x09:
+ verboselog( machine(), 1, "not handled: GPU Control 0x09: %08x\n", data );
+ break;
+ case 0x0d:
+ verboselog( machine(), 1, "reset lightgun coordinates %08x\n", data );
+ n_lightgun_x = 0;
+ n_lightgun_y = 0;
+ break;
+ case 0x10:
+ switch( data & 0xff )
+ {
+ case 0x03:
+ if( m_n_gputype == 2 )
+ {
+ n_gpuinfo = n_drawarea_x1 | ( n_drawarea_y1 << 10 );
+ }
+ else
+ {
+ n_gpuinfo = n_drawarea_x1 | ( n_drawarea_y1 << 12 );
+ }
+ verboselog( machine(), 1, "GPU Info - Draw area top left %08x\n", n_gpuinfo );
+ break;
+ case 0x04:
+ if( m_n_gputype == 2 )
+ {
+ n_gpuinfo = n_drawarea_x2 | ( n_drawarea_y2 << 10 );
+ }
+ else
+ {
+ n_gpuinfo = n_drawarea_x2 | ( n_drawarea_y2 << 12 );
+ }
+ verboselog( machine(), 1, "GPU Info - Draw area bottom right %08x\n", n_gpuinfo );
+ break;
+ case 0x05:
+ if( m_n_gputype == 2 )
+ {
+ n_gpuinfo = ( n_drawoffset_x & 2047 ) | ( ( n_drawoffset_y & 2047 ) << 11 );
+ }
+ else
+ {
+ n_gpuinfo = ( n_drawoffset_x & 2047 ) | ( ( n_drawoffset_y & 2047 ) << 12 );
+ }
+ verboselog( machine(), 1, "GPU Info - Draw offset %08x\n", n_gpuinfo );
+ break;
+ case 0x07:
+ n_gpuinfo = m_n_gputype;
+ verboselog( machine(), 1, "GPU Info - GPU Type %08x\n", n_gpuinfo );
+ break;
+ case 0x08:
+ n_gpuinfo = n_lightgun_x | ( n_lightgun_y << 16 );
+ verboselog( machine(), 1, "GPU Info - lightgun coordinates %08x\n", n_gpuinfo );
+ break;
+ default:
+ verboselog( machine(), 0, "GPU Info - unknown request (%08x)\n", data );
+ n_gpuinfo = 0;
+ break;
+ }
+ break;
+ case 0x20:
+ verboselog( machine(), 1, "not handled: GPU Control 0x20: %08x\n", data );
+ break;
+ default:
+#if defined( MAME_DEBUG )
+ popmessage( "unknown GPU command %08x", data );
+#endif
+ verboselog( machine(), 0, "gpu_w( %08x ) unknown GPU command\n", data );
+ break;
+ }
+ break;
+ default:
+ verboselog( machine(), 0, "gpu_w( %08x, %08x, %08x ) unknown register\n", offset, data, mem_mask );
+ break;
+ }
+}
+
+
+void psxgpu_device::dma_read( UINT32 *p_n_psxram, UINT32 n_address, INT32 n_size )
+{
+ gpu_read( &p_n_psxram[ n_address / 4 ], n_size );
+}
+
+void psxgpu_device::gpu_read( UINT32 *p_ram, INT32 n_size )
+{
+ while( n_size > 0 )
+ {
+ if( ( n_gpustatus & ( 1L << 0x1b ) ) != 0 )
+ {
+ UINT32 n_pixel;
+ PAIR data;
+
+ verboselog( machine(), 2, "copy image from frame buffer ( %d, %d )\n", n_vramx, n_vramy );
+ data.d = 0;
+ for( n_pixel = 0; n_pixel < 2; n_pixel++ )
+ {
+ data.w.l = data.w.h;
+ data.w.h = *( p_p_vram[ ( n_vramy + ( m_packet.n_entry[ 1 ] >> 16 ) ) & 0x3ff ] + ( ( n_vramx + ( m_packet.n_entry[ 1 ] & 0xffff ) ) & 0x3ff ) );
+ n_vramx++;
+ if( n_vramx >= ( m_packet.n_entry[ 2 ] & 0xffff ) )
+ {
+ n_vramx = 0;
+ n_vramy++;
+ if( n_vramy >= ( m_packet.n_entry[ 2 ] >> 16 ) )
+ {
+ verboselog( machine(), 1, "copy image from frame buffer end\n" );
+ n_gpustatus &= ~( 1L << 0x1b );
+ n_gpu_buffer_offset = 0;
+ n_vramx = 0;
+ n_vramy = 0;
+ if( n_pixel == 0 )
+ {
+ data.w.l = data.w.h;
+ data.w.h = 0;
+ }
+ break;
+ }
+ }
+ }
+ *( p_ram ) = data.d;
+ }
+ else
+ {
+ verboselog( machine(), 2, "read GPU info (%08x)\n", n_gpuinfo );
+ *( p_ram ) = n_gpuinfo;
+ }
+ p_ram++;
+ n_size--;
+ }
+}
+
+READ32_MEMBER( psxgpu_device::read )
+{
+ UINT32 data;
+
+ switch( offset )
+ {
+ case 0x00:
+ gpu_read( &data, 1 );
+ break;
+ case 0x01:
+ data = n_gpustatus;
+ verboselog( machine(), 1, "read GPU status (%08x)\n", data );
+ break;
+ default:
+ verboselog( machine(), 0, "gpu_r( %08x, %08x ) unknown register\n", offset, mem_mask );
+ data = 0;
+ break;
+ }
+ return data;
+}
+
+void psxgpu_device::vblank(screen_device &screen, bool vblank_state)
+{
+ if( vblank_state )
+ {
+#if DEBUG_VIEWER
+ DebugCheckKeys();
+#endif
+
+ n_gpustatus ^= ( 1L << 31 );
+ m_vblank_handler(1);
+ }
+}
+
+void psxgpu_device::gpu_reset( void )
+{
+ verboselog( machine(), 1, "reset gpu\n" );
+ n_gpu_buffer_offset = 0;
+ n_gpustatus = 0x14802000;
+ n_drawarea_x1 = 0;
+ n_drawarea_y1 = 0;
+ n_drawarea_x2 = 1023;
+ n_drawarea_y2 = 1023;
+ n_drawoffset_x = 0;
+ n_drawoffset_y = 0;
+ m_n_displaystartx = 0;
+ n_displaystarty = 0;
+ n_horiz_disstart = 0x260;
+ n_horiz_disend = 0xc60;
+ n_vert_disstart = 0x010;
+ n_vert_disend = 0x100;
+ n_vramx = 0;
+ n_vramy = 0;
+ n_twx = 0;
+ n_twy = 0;
+ n_twh = 255;
+ n_tww = 255;
+ updatevisiblearea();
+}
+
+void psxgpu_device::lightgun_set( int n_x, int n_y )
+{
+ n_lightgun_x = n_x;
+ n_lightgun_y = n_y;
+}
+
+PALETTE_INIT_MEMBER( psxgpu_device, psx )
+{
+ UINT32 n_colour;
+
+ for( n_colour = 0; n_colour < 0x10000; n_colour++ )
+ {
+ palette.set_pen_color( n_colour, pal555(n_colour,0, 5, 10) );
+ }
+}
+
+MACHINE_CONFIG_FRAGMENT( psxgpu )
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_REFRESH_RATE( 60 )
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500) /* not accurate */)
+ MCFG_SCREEN_SIZE( 1024, 1024 )
+ MCFG_SCREEN_VISIBLE_AREA( 0, 639, 0, 479 )
+ MCFG_SCREEN_UPDATE_DEVICE( DEVICE_SELF, psxgpu_device, update_screen )
+ ((screen_device *)device)->register_vblank_callback(vblank_state_delegate(FUNC(psxgpu_device::vblank), (psxgpu_device *) owner));
+ MCFG_SCREEN_PALETTE("palette")
+
+ MCFG_PALETTE_ADD( "palette", 65536 )
+ MCFG_PALETTE_INIT_OWNER(psxgpu_device, psx)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - device-specific
+// machine configurations
+//-------------------------------------------------
+
+machine_config_constructor psxgpu_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( psxgpu );
+}
diff --git a/src/devices/video/psx.h b/src/devices/video/psx.h
new file mode 100644
index 00000000000..c8cdff69c42
--- /dev/null
+++ b/src/devices/video/psx.h
@@ -0,0 +1,361 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+/*
+ * PlayStation GPU emulator
+ *
+ * Copyright 2003-2014 smf
+ *
+ */
+
+#pragma once
+
+#ifndef __PSXGPU_H__
+#define __PSXGPU_H__
+
+#include "emu.h"
+
+#define MCFG_PSX_GPU_VBLANK_HANDLER(_devcb) \
+ devcb = &psxgpu_device::set_vblank_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_PSXGPU_ADD( cputag, tag, type, _vramSize, clock ) \
+ MCFG_DEVICE_MODIFY( cputag ) \
+ MCFG_PSX_GPU_READ_HANDLER(DEVREAD32(tag, psxgpu_device, read)) \
+ MCFG_PSX_GPU_WRITE_HANDLER(DEVWRITE32(tag, psxgpu_device, write)) \
+ MCFG_DEVICE_ADD( tag, type, clock ) \
+ ((psxgpu_device *) device)->vramSize = _vramSize; \
+ MCFG_PSX_GPU_VBLANK_HANDLER(DEVWRITELINE(cputag ":irq", psxirq_device, intin0)) \
+ MCFG_PSX_DMA_CHANNEL_READ( cputag, 2, psx_dma_write_delegate( FUNC( psxgpu_device::dma_read ), (psxgpu_device *) device ) ) \
+ MCFG_PSX_DMA_CHANNEL_WRITE( cputag, 2, psx_dma_read_delegate( FUNC( psxgpu_device::dma_write ), (psxgpu_device *) device ) )
+
+#define MCFG_PSXGPU_REPLACE( cputag, tag, type, _vramSize, clock ) \
+ MCFG_DEVICE_MODIFY( cputag ) \
+ MCFG_PSX_GPU_READ_HANDLER(DEVREAD32(tag, psxgpu_device, read)) \
+ MCFG_PSX_GPU_WRITE_HANDLER(DEVWRITE32(tag, psxgpu_device, write)) \
+ MCFG_DEVICE_REPLACE( tag, type, clock ) \
+ ((psxgpu_device *) device)->vramSize = _vramSize; \
+ MCFG_PSX_GPU_VBLANK_HANDLER(DEVWRITELINE(cputag ":irq", psxirq_device, intin0)) \
+ MCFG_PSX_DMA_CHANNEL_READ( cputag, 2, psx_dma_write_delegate( FUNC( psxgpu_device::dma_read ), (psxgpu_device *) device ) ) \
+ MCFG_PSX_DMA_CHANNEL_WRITE( cputag, 2, psx_dma_read_delegate( FUNC( psxgpu_device::dma_write ), (psxgpu_device *) device ) )
+
+#define MCFG_PSXGPU_VBLANK_CALLBACK( _delegate ) \
+ ((screen_device *) config.device_find( device, "screen" ))->register_vblank_callback( _delegate );
+
+extern const device_type CXD8514Q;
+extern const device_type CXD8538Q;
+extern const device_type CXD8561Q;
+extern const device_type CXD8561BQ;
+extern const device_type CXD8561CQ;
+extern const device_type CXD8654Q;
+
+#define STOP_ON_ERROR ( 0 )
+
+#define MAX_LEVEL ( 32 )
+#define MID_LEVEL ( ( MAX_LEVEL / 2 ) << 8 )
+#define MAX_SHADE ( 0x100 )
+#define MID_SHADE ( 0x80 )
+
+#define DEBUG_COORDS ( 10 )
+
+struct psx_gpu_debug
+{
+ bitmap_ind16 *mesh;
+ int b_clear;
+ int b_mesh;
+ int n_skip;
+ int b_texture;
+ int n_interleave;
+ int n_coord;
+ int n_coordx[ DEBUG_COORDS ];
+ int n_coordy[ DEBUG_COORDS ];
+};
+
+struct FLATVERTEX
+{
+ PAIR n_coord;
+};
+
+struct GOURAUDVERTEX
+{
+ PAIR n_bgr;
+ PAIR n_coord;
+};
+
+struct FLATTEXTUREDVERTEX
+{
+ PAIR n_coord;
+ PAIR n_texture;
+};
+
+struct GOURAUDTEXTUREDVERTEX
+{
+ PAIR n_bgr;
+ PAIR n_coord;
+ PAIR n_texture;
+};
+
+union PACKET
+{
+ UINT32 n_entry[ 16 ];
+
+ struct
+ {
+ PAIR n_cmd;
+ struct FLATVERTEX vertex[ 2 ];
+ PAIR n_size;
+ } MoveImage;
+
+ struct
+ {
+ PAIR n_bgr;
+ PAIR n_coord;
+ PAIR n_size;
+ } FlatRectangle;
+
+ struct
+ {
+ PAIR n_bgr;
+ PAIR n_coord;
+ } FlatRectangle8x8;
+
+ struct
+ {
+ PAIR n_bgr;
+ PAIR n_coord;
+ } FlatRectangle16x16;
+
+ struct
+ {
+ PAIR n_bgr;
+ PAIR n_coord;
+ PAIR n_texture;
+ } Sprite8x8;
+
+ struct
+ {
+ PAIR n_bgr;
+ PAIR n_coord;
+ PAIR n_texture;
+ } Sprite16x16;
+
+ struct
+ {
+ PAIR n_bgr;
+ PAIR n_coord;
+ PAIR n_texture;
+ PAIR n_size;
+ } FlatTexturedRectangle;
+
+ struct
+ {
+ PAIR n_bgr;
+ struct FLATVERTEX vertex[ 4 ];
+ } FlatPolygon;
+
+ struct
+ {
+ struct GOURAUDVERTEX vertex[ 4 ];
+ } GouraudPolygon;
+
+ struct
+ {
+ PAIR n_bgr;
+ struct FLATVERTEX vertex[ 2 ];
+ } MonochromeLine;
+
+ struct
+ {
+ struct GOURAUDVERTEX vertex[ 2 ];
+ } GouraudLine;
+
+ struct
+ {
+ PAIR n_bgr;
+ struct FLATTEXTUREDVERTEX vertex[ 4 ];
+ } FlatTexturedPolygon;
+
+ struct
+ {
+ struct GOURAUDTEXTUREDVERTEX vertex[ 4 ];
+ } GouraudTexturedPolygon;
+
+ struct
+ {
+ PAIR n_bgr;
+ struct FLATVERTEX vertex;
+ } Dot;
+};
+
+class psxgpu_device : public device_t
+{
+public:
+ // construction/destruction
+ psxgpu_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_vblank_handler(device_t &device, _Object object) { return downcast<psxgpu_device &>(device).m_vblank_handler.set_callback(object); }
+
+ UINT32 update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ DECLARE_WRITE32_MEMBER( write );
+ DECLARE_READ32_MEMBER( read );
+ void dma_read( UINT32 *ram, UINT32 n_address, INT32 n_size );
+ void dma_write( UINT32 *ram, UINT32 n_address, INT32 n_size );
+ void lightgun_set( int, int );
+ int vramSize;
+ void vblank(screen_device &screen, bool vblank_state);
+ DECLARE_PALETTE_INIT( psx );
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ void updatevisiblearea();
+ void decode_tpage( UINT32 tpage );
+ void FlatPolygon( int n_points );
+ void FlatTexturedPolygon( int n_points );
+ void GouraudPolygon( int n_points );
+ void GouraudTexturedPolygon( int n_points );
+ void MonochromeLine( void );
+ void GouraudLine( void );
+ void FrameBufferRectangleDraw( void );
+ void FlatRectangle( void );
+ void FlatRectangle8x8( void );
+ void FlatRectangle16x16( void );
+ void FlatTexturedRectangle( void );
+ void Sprite8x8( void );
+ void Sprite16x16( void );
+ void Dot( void );
+ void MoveImage( void );
+ void psx_gpu_init( int n_gputype );
+ void gpu_reset();
+ void gpu_read( UINT32 *p_ram, INT32 n_size );
+ void gpu_write( UINT32 *p_ram, INT32 n_size );
+
+ INT32 m_n_tx;
+ INT32 m_n_ty;
+ INT32 n_abr;
+ INT32 n_tp;
+ INT32 n_ix;
+ INT32 n_iy;
+ INT32 n_ti;
+
+ UINT16 *p_vram;
+ UINT32 n_vramx;
+ UINT32 n_vramy;
+ UINT32 n_twy;
+ UINT32 n_twx;
+ UINT32 n_twh;
+ UINT32 n_tww;
+ UINT32 n_drawarea_x1;
+ UINT32 n_drawarea_y1;
+ UINT32 n_drawarea_x2;
+ UINT32 n_drawarea_y2;
+ UINT32 n_horiz_disstart;
+ UINT32 n_horiz_disend;
+ UINT32 n_vert_disstart;
+ UINT32 n_vert_disend;
+ UINT32 b_reverseflag;
+ INT32 n_drawoffset_x;
+ INT32 n_drawoffset_y;
+ UINT32 m_n_displaystartx;
+ UINT32 n_displaystarty;
+ int m_n_gputype;
+ UINT32 n_gpustatus;
+ UINT32 n_gpuinfo;
+ UINT32 n_gpu_buffer_offset;
+ UINT32 n_lightgun_x;
+ UINT32 n_lightgun_y;
+ UINT32 n_screenwidth;
+ UINT32 n_screenheight;
+
+ PACKET m_packet;
+
+ UINT16 *p_p_vram[ 1024 ];
+
+ UINT16 p_n_redshade[ MAX_LEVEL * MAX_SHADE ];
+ UINT16 p_n_greenshade[ MAX_LEVEL * MAX_SHADE ];
+ UINT16 p_n_blueshade[ MAX_LEVEL * MAX_SHADE ];
+ UINT16 p_n_redlevel[ 0x10000 ];
+ UINT16 p_n_greenlevel[ 0x10000 ];
+ UINT16 p_n_bluelevel[ 0x10000 ];
+
+ UINT16 p_n_f025[ MAX_LEVEL * MAX_SHADE ];
+ UINT16 p_n_f05[ MAX_LEVEL * MAX_SHADE ];
+ UINT16 p_n_f1[ MAX_LEVEL * MAX_SHADE ];
+ UINT16 p_n_redb05[ 0x10000 ];
+ UINT16 p_n_greenb05[ 0x10000 ];
+ UINT16 p_n_blueb05[ 0x10000 ];
+ UINT16 p_n_redb1[ 0x10000 ];
+ UINT16 p_n_greenb1[ 0x10000 ];
+ UINT16 p_n_blueb1[ 0x10000 ];
+ UINT16 p_n_redaddtrans[ MAX_LEVEL * MAX_LEVEL ];
+ UINT16 p_n_greenaddtrans[ MAX_LEVEL * MAX_LEVEL ];
+ UINT16 p_n_blueaddtrans[ MAX_LEVEL * MAX_LEVEL ];
+ UINT16 p_n_redsubtrans[ MAX_LEVEL * MAX_LEVEL ];
+ UINT16 p_n_greensubtrans[ MAX_LEVEL * MAX_LEVEL ];
+ UINT16 p_n_bluesubtrans[ MAX_LEVEL * MAX_LEVEL ];
+
+ UINT16 p_n_g0r0[ 0x10000 ];
+ UINT16 p_n_b0[ 0x10000 ];
+ UINT16 p_n_r1[ 0x10000 ];
+ UINT16 p_n_b1g1[ 0x10000 ];
+
+ devcb_write_line m_vblank_handler;
+
+#if defined(DEBUG_VIEWER) && DEBUG_VIEWER
+ required_device<screen_device> m_screen;
+ void DebugMeshInit( void );
+ void DebugMesh( int n_coordx, int n_coordy );
+ void DebugMeshEnd( void );
+ void DebugCheckKeys( void );
+ int DebugMeshDisplay( bitmap_ind16 &bitmap, const rectangle &cliprect );
+ int DebugTextureDisplay( bitmap_ind16 &bitmap );
+
+ psx_gpu_debug m_debug;
+#endif
+};
+
+class cxd8514q_device : public psxgpu_device
+{
+public:
+ // construction/destruction
+ cxd8514q_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class cxd8538q_device : public psxgpu_device
+{
+public:
+ // construction/destruction
+ cxd8538q_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class cxd8561q_device : public psxgpu_device
+{
+public:
+ // construction/destruction
+ cxd8561q_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class cxd8561bq_device : public psxgpu_device
+{
+public:
+ // construction/destruction
+ cxd8561bq_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class cxd8561cq_device : public psxgpu_device
+{
+public:
+ // construction/destruction
+ cxd8561cq_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+class cxd8654q_device : public psxgpu_device
+{
+public:
+ // construction/destruction
+ cxd8654q_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+#endif
diff --git a/src/devices/video/ramdac.c b/src/devices/video/ramdac.c
new file mode 100644
index 00000000000..f9794e60081
--- /dev/null
+++ b/src/devices/video/ramdac.c
@@ -0,0 +1,204 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+ Generic Palette RAMDAC device
+
+ Written by Angelo Salese
+
+ TODO:
+ - masking register, almost likely it controls rollback on incrementing
+ r/w palette access;
+ - needs information about different models and what exactly they does
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/ramdac.h"
+
+// default address map
+static ADDRESS_MAP_START( ramdac_palram, AS_0, 8, ramdac_device )
+ AM_RANGE(0x000, 0x0ff) AM_RAM // R bank
+ AM_RANGE(0x100, 0x1ff) AM_RAM // G bank
+ AM_RANGE(0x200, 0x2ff) AM_RAM // B bank
+ AM_RANGE(0x300, 0x3ff) AM_NOP
+ADDRESS_MAP_END
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type RAMDAC = &device_creator<ramdac_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// ramdac_device - constructor
+//-------------------------------------------------
+
+ramdac_device::ramdac_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, RAMDAC, "RAMDAC", tag, owner, clock, "ramdac", __FILE__),
+ device_memory_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 10, 0, NULL, *ADDRESS_MAP_NAME(ramdac_palram)),
+ m_palette(*this),
+ m_split_read_reg(0)
+{
+}
+
+//-------------------------------------------------
+// static_set_palette_tag: Set the tag of the
+// palette device
+//-------------------------------------------------
+
+void ramdac_device::static_set_palette_tag(device_t &device, const char *tag)
+{
+ downcast<ramdac_device &>(device).m_palette.set_tag(tag);
+}
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *ramdac_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 ramdac_device::readbyte(offs_t address)
+{
+ return space().read_byte(address);
+}
+
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void ramdac_device::writebyte(offs_t address, UINT8 data)
+{
+ space().write_byte(address, data);
+}
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+void ramdac_device::device_validity_check(validity_checker &valid) const
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ramdac_device::device_start()
+{
+ m_palram = auto_alloc_array_clear(machine(), UINT8, 1 << 10);
+
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ramdac_device::device_reset()
+{
+ m_pal_index[0] = 0;
+ m_int_index[0] = 0;
+ m_pal_index[1] = 0;
+ m_int_index[1] = 0;
+ m_pal_mask = 0xff;
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+// [0] = W register, [1] = R register
+//**************************************************************************
+
+inline void ramdac_device::reg_increment(UINT8 inc_type)
+{
+ m_int_index[inc_type]++;
+ if(m_int_index[inc_type] == 3)
+ {
+ m_int_index[inc_type] = 0;
+ m_pal_index[inc_type]++;
+ }
+}
+
+READ8_MEMBER( ramdac_device::index_r )
+{
+ return m_pal_index[0];
+}
+
+WRITE8_MEMBER( ramdac_device::index_w )
+{
+ m_pal_index[0] = data;
+ m_int_index[0] = 0;
+}
+
+WRITE8_MEMBER( ramdac_device::index_r_w )
+{
+ m_pal_index[1] = data;
+ m_int_index[1] = 0;
+}
+
+READ8_MEMBER( ramdac_device::pal_r )
+{
+ UINT8 res;
+ res = readbyte(m_pal_index[m_split_read_reg] | (m_int_index[m_split_read_reg] << 8));
+ reg_increment(m_split_read_reg);
+ return res;
+}
+
+WRITE8_MEMBER( ramdac_device::pal_w )
+{
+ writebyte(m_pal_index[0] | (m_int_index[0] << 8),data);
+ reg_increment(0);
+}
+
+WRITE8_MEMBER( ramdac_device::mask_w )
+{
+ m_pal_mask = data;
+}
+
+
+//**************************************************************************
+// Generic bank read/write handlers
+//**************************************************************************
+
+READ8_MEMBER( ramdac_device::ramdac_pal_r )
+{
+ return m_palram[offset];
+}
+
+WRITE8_MEMBER( ramdac_device::ramdac_rgb666_w )
+{
+ UINT16 pal_offs;
+
+ m_palram[offset] = data & 0x3f;
+ pal_offs = (offset & 0xff);
+
+ m_palette->set_pen_color(offset&0xff,pal6bit(m_palram[pal_offs|0x000]),pal6bit(m_palram[pal_offs|0x100]),pal6bit(m_palram[pal_offs|0x200]));
+}
+
+WRITE8_MEMBER( ramdac_device::ramdac_rgb888_w )
+{
+ UINT16 pal_offs;
+
+ m_palram[offset] = data;
+ pal_offs = (offset & 0xff);
+
+ m_palette->set_pen_color(offset&0xff,m_palram[pal_offs|0x000],m_palram[pal_offs|0x100],m_palram[pal_offs|0x200]);
+}
diff --git a/src/devices/video/ramdac.h b/src/devices/video/ramdac.h
new file mode 100644
index 00000000000..0cbf29e9057
--- /dev/null
+++ b/src/devices/video/ramdac.h
@@ -0,0 +1,87 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+ Generic Palette RAMDAC device
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __ramdacDEV_H__
+#define __ramdacDEV_H__
+
+#include "emu.h"
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_RAMDAC_ADD(_tag, _map, _palette_tag) \
+ MCFG_DEVICE_ADD(_tag, RAMDAC, 0) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _map) \
+ ramdac_device::static_set_palette_tag(*device, "^" _palette_tag);
+
+#define MCFG_RAMDAC_SPLIT_READ(_split) \
+ ramdac_device::set_split_read(*device, _split);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> ramdac_device
+
+class ramdac_device : public device_t,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ ramdac_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration
+ static void static_set_palette_tag(device_t &device, const char *tag);
+ static void set_split_read(device_t &device, int split) { downcast<ramdac_device &>(device).m_split_read_reg = split; }
+
+ // I/O operations
+ DECLARE_READ8_MEMBER( index_r );
+ DECLARE_READ8_MEMBER( pal_r );
+ DECLARE_WRITE8_MEMBER( index_w );
+ DECLARE_WRITE8_MEMBER( index_r_w );
+ DECLARE_WRITE8_MEMBER( pal_w );
+ DECLARE_WRITE8_MEMBER( mask_w );
+
+ DECLARE_READ8_MEMBER( ramdac_pal_r );
+ DECLARE_WRITE8_MEMBER( ramdac_rgb666_w );
+ DECLARE_WRITE8_MEMBER( ramdac_rgb888_w );
+
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+protected:
+ // device-level overrides
+ virtual void device_validity_check(validity_checker &valid) const;
+ virtual void device_start();
+ virtual void device_reset();
+ inline UINT8 readbyte(offs_t address);
+ inline void writebyte(offs_t address, UINT8 data);
+ inline void reg_increment(UINT8 inc_type);
+
+private:
+ UINT8 m_pal_index[2];
+ UINT8 m_pal_mask;
+ UINT8 m_int_index[2];
+ UINT8 *m_palram;
+
+ const address_space_config m_space_config;
+ required_device<palette_device> m_palette;
+
+ UINT8 m_split_read_reg; // read register index is separated, seen in rltennis
+};
+
+
+// device type definition
+extern const device_type RAMDAC;
+
+
+#endif
diff --git a/src/devices/video/resnet.c b/src/devices/video/resnet.c
new file mode 100644
index 00000000000..0245aba6161
--- /dev/null
+++ b/src/devices/video/resnet.c
@@ -0,0 +1,719 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+/*****************************************************************************
+
+ resnet.c
+
+ Compute weights for resistors networks.
+
+******************************************************************************
+
+ Function can evaluate from one to three networks at a time.
+
+ The output weights can either be scaled with automatically calculated scaler
+ or scaled with a 'scaler' provided on entry.
+
+ On entry
+ --------
+
+ 'minval','maxval' specify the range of output signals (sum of weights).
+ 'scaler' if negative, function will calculate proper scaler,
+ otherwise it will use the one provided here.
+ 'count_x' is the number of resistors in this network
+ 'resistances_x' is the pointer to a table containing the resistances
+ 'weights_x' is the pointer to a table to be filled with the weights
+ (it can contain negative values if 'minval' is below zero).
+ 'pulldown_x' is the resistance of a pulldown resistor (0 means there's no pulldown resistor)
+ 'pullup_x' is the resistance of a pullup resistor (0 means there's no pullup resistor)
+
+
+ Return value
+ ------------
+
+ The value of the scaler that was used for fitting the output within the expected range.
+ Note that if you provide your own scaler on entry it will be returned here.
+
+
+ All resistances are expected in Ohms.
+
+
+ Hint
+ ----
+
+ If there is no need to calculate all three networks at a time, just specify '0'
+ for the 'count_x' for unused network(s).
+
+*****************************************************************************/
+
+
+#include "emu.h"
+#include "resnet.h"
+
+#define VERBOSE 0
+
+
+double compute_resistor_weights(
+ int minval, int maxval, double scaler,
+ int count_1, const int * resistances_1, double * weights_1, int pulldown_1, int pullup_1,
+ int count_2, const int * resistances_2, double * weights_2, int pulldown_2, int pullup_2,
+ int count_3, const int * resistances_3, double * weights_3, int pulldown_3, int pullup_3 )
+{
+ int networks_no;
+
+ int rescount[MAX_NETS]; /* number of resistors in each of the nets */
+ double r[MAX_NETS][MAX_RES_PER_NET]; /* resistances */
+ double w[MAX_NETS][MAX_RES_PER_NET]; /* calulated weights */
+ double ws[MAX_NETS][MAX_RES_PER_NET]; /* calulated, scaled weights */
+ int r_pd[MAX_NETS]; /* pulldown resistances */
+ int r_pu[MAX_NETS]; /* pullup resistances */
+
+ double max_out[MAX_NETS];
+ double * out[MAX_NETS];
+
+ int i,j,n;
+ double scale;
+ double max;
+
+ /* parse input parameters */
+
+ networks_no = 0;
+ for (n = 0; n < MAX_NETS; n++)
+ {
+ int count, pd, pu;
+ const int * resistances;
+ double * weights;
+
+ switch(n){
+ case 0:
+ count = count_1;
+ resistances = resistances_1;
+ weights = weights_1;
+ pd = pulldown_1;
+ pu = pullup_1;
+ break;
+ case 1:
+ count = count_2;
+ resistances = resistances_2;
+ weights = weights_2;
+ pd = pulldown_2;
+ pu = pullup_2;
+ break;
+ case 2:
+ default:
+ count = count_3;
+ resistances = resistances_3;
+ weights = weights_3;
+ pd = pulldown_3;
+ pu = pullup_3;
+ break;
+ }
+
+ /* parameters validity check */
+ if (count > MAX_RES_PER_NET)
+ fatalerror("compute_resistor_weights(): too many resistors in net #%i. The maximum allowed is %i, the number requested was: %i\n",n, MAX_RES_PER_NET, count);
+
+
+ if (count > 0)
+ {
+ rescount[networks_no] = count;
+ for (i=0; i < count; i++)
+ {
+ r[networks_no][i] = 1.0 * resistances[i];
+ }
+ out[networks_no] = weights;
+ r_pd[networks_no] = pd;
+ r_pu[networks_no] = pu;
+ networks_no++;
+ }
+ }
+ if (networks_no < 1)
+ fatalerror("compute_resistor_weights(): no input data\n");
+
+ /* calculate outputs for all given networks */
+ for( i = 0; i < networks_no; i++ )
+ {
+ double R0, R1, Vout, dst;
+
+ /* of n resistors */
+ for(n = 0; n < rescount[i]; n++)
+ {
+ R0 = ( r_pd[i] == 0 ) ? 1.0/1e12 : 1.0/r_pd[i];
+ R1 = ( r_pu[i] == 0 ) ? 1.0/1e12 : 1.0/r_pu[i];
+
+ for( j = 0; j < rescount[i]; j++ )
+ {
+ if( j==n ) /* only one resistance in the network connected to Vcc */
+ {
+ if (r[i][j] != 0.0)
+ R1 += 1.0/r[i][j];
+ }
+ else
+ if (r[i][j] != 0.0)
+ R0 += 1.0/r[i][j];
+ }
+
+ /* now determine the voltage */
+ R0 = 1.0/R0;
+ R1 = 1.0/R1;
+ Vout = (maxval - minval) * R0 / (R1 + R0) + minval;
+
+ /* and convert it to a destination value */
+ dst = (Vout < minval) ? minval : (Vout > maxval) ? maxval : Vout;
+
+ w[i][n] = dst;
+ }
+ }
+
+ /* calculate maximum outputs for all given networks */
+ j = 0;
+ max = 0.0;
+ for( i = 0; i < networks_no; i++ )
+ {
+ double sum = 0.0;
+
+ /* of n resistors */
+ for( n = 0; n < rescount[i]; n++ )
+ sum += w[i][n]; /* maximum output, ie when each resistance is connected to Vcc */
+
+ max_out[i] = sum;
+ if (max < sum)
+ {
+ max = sum;
+ j = i;
+ }
+ }
+
+
+ if (scaler < 0.0) /* use autoscale ? */
+ /* calculate the output scaler according to the network with the greatest output */
+ scale = ((double)maxval) / max_out[j];
+ else /* use scaler provided on entry */
+ scale = scaler;
+
+ /* calculate scaled output and fill the output table(s)*/
+ for(i = 0; i < networks_no;i++)
+ {
+ for (n = 0; n < rescount[i]; n++)
+ {
+ ws[i][n] = w[i][n]*scale; /* scale the result */
+ (out[i])[n] = ws[i][n]; /* fill the output table */
+ }
+ }
+
+/* debug code */
+if (VERBOSE)
+{
+ logerror("compute_resistor_weights(): scaler = %15.10f\n",scale);
+ logerror("min val :%i max val:%i Total number of networks :%i\n", minval, maxval, networks_no );
+
+ for(i = 0; i < networks_no;i++)
+ {
+ double sum = 0.0;
+
+ logerror(" Network no.%i=> resistances: %i", i, rescount[i] );
+ if (r_pu[i] != 0)
+ logerror(", pullup resistor: %i Ohms",r_pu[i]);
+ if (r_pd[i] != 0)
+ logerror(", pulldown resistor: %i Ohms",r_pd[i]);
+ logerror("\n maximum output of this network:%10.5f (scaled to %15.10f)\n", max_out[i], max_out[i]*scale );
+ for (n = 0; n < rescount[i]; n++)
+ {
+ logerror(" res %2i:%9.1f Ohms weight=%10.5f (scaled = %15.10f)\n", n, r[i][n], w[i][n], ws[i][n] );
+ sum += ws[i][n];
+ }
+ logerror(" sum of scaled weights = %15.10f\n", sum );
+ }
+}
+/* debug end */
+
+ return (scale);
+
+}
+
+
+double compute_resistor_net_outputs(
+ int minval, int maxval, double scaler,
+ int count_1, const int * resistances_1, double * outputs_1, int pulldown_1, int pullup_1,
+ int count_2, const int * resistances_2, double * outputs_2, int pulldown_2, int pullup_2,
+ int count_3, const int * resistances_3, double * outputs_3, int pulldown_3, int pullup_3 )
+{
+ int networks_no;
+
+ int rescount[MAX_NETS]; /* number of resistors in each of the nets */
+ double r[MAX_NETS][MAX_RES_PER_NET]; /* resistances */
+ int r_pd[MAX_NETS]; /* pulldown resistances */
+ int r_pu[MAX_NETS]; /* pullup resistances */
+
+ double max_out[MAX_NETS];
+ double min_out[MAX_NETS];
+ double * out[MAX_NETS];
+
+ int i,j,n;
+ double scale;
+ double min;
+ double max;
+
+ /* parse input parameters */
+
+ std::vector<double> o((1<<MAX_RES_PER_NET) * MAX_NETS);
+ std::vector<double> os((1<<MAX_RES_PER_NET) * MAX_NETS);
+
+ networks_no = 0;
+ for (n = 0; n < MAX_NETS; n++)
+ {
+ int count, pd, pu;
+ const int * resistances;
+ double * weights;
+
+ switch(n){
+ case 0:
+ count = count_1;
+ resistances = resistances_1;
+ weights = outputs_1;
+ pd = pulldown_1;
+ pu = pullup_1;
+ break;
+ case 1:
+ count = count_2;
+ resistances = resistances_2;
+ weights = outputs_2;
+ pd = pulldown_2;
+ pu = pullup_2;
+ break;
+ case 2:
+ default:
+ count = count_3;
+ resistances = resistances_3;
+ weights = outputs_3;
+ pd = pulldown_3;
+ pu = pullup_3;
+ break;
+ }
+
+ /* parameters validity check */
+ if (count > MAX_RES_PER_NET)
+ fatalerror("compute_resistor_net_outputs(): too many resistors in net #%i. The maximum allowed is %i, the number requested was: %i\n",n, MAX_RES_PER_NET, count);
+
+ if (count > 0)
+ {
+ rescount[networks_no] = count;
+ for (i=0; i < count; i++)
+ {
+ r[networks_no][i] = 1.0 * resistances[i];
+ }
+ out[networks_no] = weights;
+ r_pd[networks_no] = pd;
+ r_pu[networks_no] = pu;
+ networks_no++;
+ }
+ }
+
+ if (networks_no<1)
+ fatalerror("compute_resistor_net_outputs(): no input data\n");
+
+ /* calculate outputs for all given networks */
+ for( i = 0; i < networks_no; i++ )
+ {
+ double R0, R1, Vout, dst;
+
+ /* of n resistors, generating 1<<n possible outputs */
+ for(n = 0; n < (1<<rescount[i]); n++)
+ {
+ R0 = ( r_pd[i] == 0 ) ? 1.0/1e12 : 1.0/r_pd[i];
+ R1 = ( r_pu[i] == 0 ) ? 1.0/1e12 : 1.0/r_pu[i];
+
+ for( j = 0; j < rescount[i]; j++ )
+ {
+ if( (n & (1<<j)) == 0 )/* only when this resistance in the network connected to GND */
+ if (r[i][j] != 0.0)
+ R0 += 1.0/r[i][j];
+ }
+
+ /* now determine the voltage */
+ R0 = 1.0/R0;
+ R1 = 1.0/R1;
+ Vout = (maxval - minval) * R0 / (R1 + R0) + minval;
+
+ /* and convert it to a destination value */
+ dst = (Vout < minval) ? minval : (Vout > maxval) ? maxval : Vout;
+
+ o[i*(1<<MAX_RES_PER_NET)+n] = dst;
+ }
+ }
+
+ /* calculate minimum outputs for all given networks */
+ min = maxval;
+ max = minval;
+ for( i = 0; i < networks_no; i++ )
+ {
+ double val = 0.0;
+ double max_tmp = minval;
+ double min_tmp = maxval;
+
+ for (n = 0; n < (1<<rescount[i]); n++)
+ {
+ if (min_tmp > o[i*(1<<MAX_RES_PER_NET)+n])
+ min_tmp = o[i*(1<<MAX_RES_PER_NET)+n];
+ if (max_tmp < o[i*(1<<MAX_RES_PER_NET)+n])
+ max_tmp = o[i*(1<<MAX_RES_PER_NET)+n];
+ }
+
+ max_out[i] = max_tmp; /* maximum output */
+ min_out[i] = min_tmp; /* minimum output */
+
+ val = min_out[i]; /* minimum output of this network */
+ if (min > val)
+ {
+ min = val;
+ }
+ val = max_out[i]; /* maximum output of this network */
+ if (max < val)
+ {
+ max = val;
+ }
+ }
+
+
+ if (scaler < 0.0) /* use autoscale ? */
+ /* calculate the output scaler according to the network with the smallest output */
+ scale = ((double)maxval) / (max-min);
+ else /* use scaler provided on entry */
+ scale = scaler;
+
+ /* calculate scaled output and fill the output table(s) */
+ for(i = 0; i < networks_no; i++)
+ {
+ for (n = 0; n < (1<<rescount[i]); n++)
+ {
+ os[i*(1<<MAX_RES_PER_NET)+n] = (o[i*(1<<MAX_RES_PER_NET)+n] - min) * scale; /* scale the result */
+ (out[i])[n] = os[i*(1<<MAX_RES_PER_NET)+n]; /* fill the output table */
+ }
+ }
+
+/* debug code */
+if (VERBOSE)
+{
+ logerror("compute_resistor_net_outputs(): scaler = %15.10f\n",scale);
+ logerror("min val :%i max val:%i Total number of networks :%i\n", minval, maxval, networks_no );
+
+ for(i = 0; i < networks_no;i++)
+ {
+ logerror(" Network no.%i=> resistances: %i", i, rescount[i] );
+ if (r_pu[i] != 0)
+ logerror(", pullup resistor: %i Ohms",r_pu[i]);
+ if (r_pd[i] != 0)
+ logerror(", pulldown resistor: %i Ohms",r_pd[i]);
+ logerror("\n maximum output of this network:%10.5f", max_out[i] );
+ logerror("\n minimum output of this network:%10.5f\n", min_out[i] );
+ for (n = 0; n < rescount[i]; n++)
+ {
+ logerror(" res %2i:%9.1f Ohms\n", n, r[i][n]);
+ }
+ for (n = 0; n < (1<<rescount[i]); n++)
+ {
+ logerror(" combination %2i out=%10.5f (scaled = %15.10f)\n", n, o[i*(1<<MAX_RES_PER_NET)+n], os[i*(1<<MAX_RES_PER_NET)+n] );
+ }
+ }
+}
+/* debug end */
+
+ return (scale);
+
+}
+
+/*****************************************************************************
+
+ New Interface
+
+*****************************************************************************/
+
+
+/* Datasheets give a maximum of 0.4V to 0.5V
+ * However in the circuit simulated here this will only
+ * occur if (rBias + rOutn) = 50 Ohm, rBias exists.
+ * This is highly unlikely. With the resistor values used
+ * in such circuits VOL is likely to be around 50mV.
+ */
+
+#define TTL_VOL (0.05)
+
+
+/* Likely, datasheets give a typical value of 3.4V to 3.6V
+ * for VOH. Modelling the TTL circuit however backs a value
+ * of 4V for typical currents involved in resistor networks.
+ */
+
+#define TTL_VOH (4.0)
+
+int compute_res_net(int inputs, int channel, const res_net_info &di)
+{
+ double rTotal=0.0;
+ double v = 0;
+ int i;
+
+ double vBias = di.rgb[channel].vBias;
+ double vOH = di.vOH;
+ double vOL = di.vOL;
+ double minout = di.rgb[channel].minout;
+ double cut = di.rgb[channel].cut;
+ double vcc = di.vcc;
+ double ttlHRes = 0;
+ double rGnd = di.rgb[channel].rGnd;
+ UINT8 OpenCol = di.OpenCol;
+
+ /* Global options */
+
+ switch (di.options & RES_NET_AMP_MASK)
+ {
+ case RES_NET_AMP_USE_GLOBAL:
+ /* just ignore */
+ break;
+ case RES_NET_AMP_NONE:
+ minout = 0.0;
+ cut = 0.0;
+ break;
+ case RES_NET_AMP_DARLINGTON:
+ minout = 0.9;
+ cut = 0.0;
+ break;
+ case RES_NET_AMP_EMITTER:
+ minout = 0.0;
+ cut = 0.7;
+ break;
+ case RES_NET_AMP_CUSTOM:
+ /* Fall through */
+ break;
+ default:
+ fatalerror("compute_res_net: Unknown amplifier type\n");
+ }
+
+ switch (di.options & RES_NET_VCC_MASK)
+ {
+ case RES_NET_VCC_5V:
+ vcc = 5.0;
+ break;
+ case RES_NET_VCC_CUSTOM:
+ /* Fall through */
+ break;
+ default:
+ fatalerror("compute_res_net: Unknown vcc type\n");
+ }
+
+ switch (di.options & RES_NET_VBIAS_MASK)
+ {
+ case RES_NET_VBIAS_USE_GLOBAL:
+ /* just ignore */
+ break;
+ case RES_NET_VBIAS_5V:
+ vBias = 5.0;
+ break;
+ case RES_NET_VBIAS_TTL:
+ vBias = TTL_VOH;
+ break;
+ case RES_NET_VBIAS_CUSTOM:
+ /* Fall through */
+ break;
+ default:
+ fatalerror("compute_res_net: Unknown vcc type\n");
+ }
+
+ switch (di.options & RES_NET_VIN_MASK)
+ {
+ case RES_NET_VIN_OPEN_COL:
+ OpenCol = 1;
+ vOL = TTL_VOL;
+ break;
+ case RES_NET_VIN_VCC:
+ vOL = 0.0;
+ vOH = vcc;
+ OpenCol = 0;
+ break;
+ case RES_NET_VIN_TTL_OUT:
+ vOL = TTL_VOL;
+ vOH = TTL_VOH;
+ /* rough estimation from 82s129 (7052) datasheet and from various sources
+ * 1.4k / 30
+ */
+ ttlHRes = 50;
+ OpenCol = 0;
+ break;
+ case RES_NET_VIN_CUSTOM:
+ /* Fall through */
+ break;
+ default:
+ fatalerror("compute_res_net: Unknown vin type\n");
+ }
+
+ /* Per channel options */
+
+ switch (di.rgb[channel].options & RES_NET_AMP_MASK)
+ {
+ case RES_NET_AMP_USE_GLOBAL:
+ /* use global defaults */
+ break;
+ case RES_NET_AMP_NONE:
+ minout = 0.0;
+ cut = 0.0;
+ break;
+ case RES_NET_AMP_DARLINGTON:
+ minout = 0.7;
+ cut = 0.0;
+ break;
+ case RES_NET_AMP_EMITTER:
+ minout = 0.0;
+ cut = 0.7;
+ break;
+ case RES_NET_AMP_CUSTOM:
+ /* Fall through */
+ break;
+ default:
+ fatalerror("compute_res_net: Unknown amplifier type\n");
+ }
+
+ switch (di.rgb[channel].options & RES_NET_VBIAS_MASK)
+ {
+ case RES_NET_VBIAS_USE_GLOBAL:
+ /* use global defaults */
+ break;
+ case RES_NET_VBIAS_5V:
+ vBias = 5.0;
+ break;
+ case RES_NET_VBIAS_TTL:
+ vBias = TTL_VOH;
+ break;
+ case RES_NET_VBIAS_CUSTOM:
+ /* Fall through */
+ break;
+ default:
+ fatalerror("compute_res_net: Unknown vcc type\n");
+ }
+
+ /* Input impedances */
+
+ switch (di.options & RES_NET_MONITOR_MASK)
+ {
+ case RES_NET_MONITOR_INVERT:
+ case RES_NET_MONITOR_SANYO_EZV20:
+ /* Nothing */
+ break;
+ case RES_NET_MONITOR_ELECTROHOME_G07:
+ if (rGnd != 0.0)
+ rGnd = rGnd * 5600 / (rGnd + 5600);
+ else
+ rGnd = 5600;
+ break;
+ }
+
+ /* compute here - pass a / low inputs */
+
+ for (i=0; i<di.rgb[channel].num; i++)
+ {
+ int level = ((inputs >> i) & 1);
+ if (di.rgb[channel].R[i] != 0.0 && !level)
+ {
+ // There is no difference in the calculation of the "low" input
+ // (transistor conducting to ground) between TTL output and
+ // open collector output. This is documented explicitly in the
+ // code below (no difference if / else.
+ if (OpenCol)
+ {
+ rTotal += 1.0 / di.rgb[channel].R[i];
+ v += vOL / di.rgb[channel].R[i];
+ }
+ else
+ {
+ rTotal += 1.0 / di.rgb[channel].R[i];
+ v += vOL / di.rgb[channel].R[i];
+ }
+ }
+ }
+
+ /* Mix in rbias and rgnd */
+ if ( di.rgb[channel].rBias != 0.0 )
+ {
+ rTotal += 1.0 / di.rgb[channel].rBias;
+ v += vBias / di.rgb[channel].rBias;
+ }
+ if (rGnd != 0.0)
+ rTotal += 1.0 / rGnd;
+
+ /* if the resulting voltage after application of all low inputs is
+ * greater than vOH, treat high inputs as open collector/high impedance
+ * There will be now current into/from the TTL gate
+ */
+
+ if ( (di.options & RES_NET_VIN_MASK)==RES_NET_VIN_TTL_OUT)
+ {
+ if (v / rTotal > vOH)
+ OpenCol = 1;
+ }
+
+ /* Second pass - high inputs */
+
+ for (i=0; i<di.rgb[channel].num; i++)
+ {
+ int level = ((inputs >> i) & 1);
+ if (di.rgb[channel].R[i] != 0.0 && level)
+ {
+ if (OpenCol)
+ {
+ rTotal += 0;
+ v += 0;
+ }
+ else
+ {
+ rTotal += 1.0 / (di.rgb[channel].R[i] + ttlHRes);
+ v += vOH / (di.rgb[channel].R[i] + ttlHRes);
+ }
+ }
+ }
+
+ rTotal = 1.0 / rTotal;
+ v *= rTotal;
+ v = MAX(minout, v - cut);
+
+ switch (di.options & RES_NET_MONITOR_MASK)
+ {
+ case RES_NET_MONITOR_INVERT:
+ v = vcc - v;
+ break;
+ case RES_NET_MONITOR_SANYO_EZV20:
+ v = vcc - v;
+ v = MAX(0, v-0.7);
+ v = MIN(v, vcc - 2 * 0.7);
+ v = v / (vcc-1.4);
+ v = v * vcc;
+ break;
+ case RES_NET_MONITOR_ELECTROHOME_G07:
+ /* Nothing */
+ break;
+ }
+
+ return (int) (v * 255 / vcc + 0.4);
+}
+
+void compute_res_net_all(std::vector<rgb_t> &rgb, const UINT8 *prom, const res_net_decode_info &rdi, const res_net_info &di)
+{
+ UINT8 r,g,b;
+ int i,j,k;
+
+ rgb.resize(rdi.end - rdi.start + 1);
+ for (i=rdi.start; i<=rdi.end; i++)
+ {
+ UINT8 t[3] = {0,0,0};
+ int s;
+ for (j=0;j<rdi.numcomp;j++)
+ for (k=0; k<3; k++)
+ {
+ s = rdi.shift[3*j+k];
+ if (s>0)
+ t[k] = t[k] | ( (prom[i+rdi.offset[3*j+k]]>>s) & rdi.mask[3*j+k]);
+ else
+ t[k] = t[k] | ( (prom[i+rdi.offset[3*j+k]]<<(0-s)) & rdi.mask[3*j+k]);
+ }
+ r = compute_res_net(t[0], RES_NET_CHAN_RED, di);
+ g = compute_res_net(t[1], RES_NET_CHAN_GREEN, di);
+ b = compute_res_net(t[2], RES_NET_CHAN_BLUE, di);
+ rgb[i-rdi.start] = rgb_t(r,g,b);
+ }
+}
diff --git a/src/devices/video/resnet.h b/src/devices/video/resnet.h
new file mode 100644
index 00000000000..1b1c933cf8f
--- /dev/null
+++ b/src/devices/video/resnet.h
@@ -0,0 +1,199 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+/*****************************************************************************
+
+ resnet.h
+
+ Compute weights for resistors networks.
+
+*****************************************************************************/
+
+#pragma once
+
+#ifndef _RESNET_H_
+#define _RESNET_H_
+
+/**********************************************************************
+ * Rbias
+ * Vbias >-ZZZ-. .-----------------------------------------> Out0
+ * | | Vcc
+ * R0 | | |
+ * In0 >-ZZZ-+----+ Vcc Z
+ * | | | Z
+ * R1 | | / Z
+ * In1 >-ZZZ-+ +----+----ZZZ--| NPN +------------> Out1
+ * : | > <
+ * : | +----+----| PNP
+ * R8 | | Z | \
+ * In8 >-ZZZ-+ | Z | |
+ * | | Z | Gnd
+ * Z | | '-------------------> Out2
+ * Z Rgnd | Gnd
+ * Z | |-----------------------|
+ * | `---| max(vmin,min(sig-vcut)|--------> Out3
+ * Gnd |-----------------------|
+ *
+ *********************************************************************/
+
+/* Amplifier stage per channel but may be specified globally as default */
+
+#define RES_NET_AMP_USE_GLOBAL 0x0000
+#define RES_NET_AMP_NONE 0x0001 //Out0
+#define RES_NET_AMP_DARLINGTON 0x0002 //Out1
+#define RES_NET_AMP_EMITTER 0x0003 //Out2
+#define RES_NET_AMP_CUSTOM 0x0004 //Out3
+#define RES_NET_AMP_MASK 0x0007
+
+/* VCC prebuilds - Global */
+
+#define RES_NET_VCC_5V 0x0000
+#define RES_NET_VCC_CUSTOM 0x0008
+#define RES_NET_VCC_MASK 0x0008
+
+/* VBias prebuils - per channel but may be specified globally as default */
+
+#define RES_NET_VBIAS_USE_GLOBAL 0x0000
+#define RES_NET_VBIAS_5V 0x0010
+#define RES_NET_VBIAS_TTL 0x0020
+#define RES_NET_VBIAS_CUSTOM 0x0030
+#define RES_NET_VBIAS_MASK 0x0030
+
+/* Input Voltage levels - Global */
+
+#define RES_NET_VIN_OPEN_COL 0x0000
+#define RES_NET_VIN_VCC 0x0100
+#define RES_NET_VIN_TTL_OUT 0x0200
+#define RES_NET_VIN_CUSTOM 0x0300
+#define RES_NET_VIN_MASK 0x0300
+
+/* Monitor options */
+
+// Just invert the signal
+#define RES_NET_MONITOR_INVERT 0x1000
+// SANYO_EZV20 / Nintendo with inverter circuit
+#define RES_NET_MONITOR_SANYO_EZV20 0x2000
+// Electrohome G07 Series
+// 5.6k input impedance
+#define RES_NET_MONITOR_ELECTROHOME_G07 0x3000
+
+#define RES_NET_MONITOR_MASK 0x3000
+
+/* General defines */
+
+#define RES_NET_CHAN_RED 0x00
+#define RES_NET_CHAN_GREEN 0x01
+#define RES_NET_CHAN_BLUE 0x02
+
+/* Some aliases */
+
+#define RES_NET_VIN_MB7051 RES_NET_VIN_TTL_OUT
+#define RES_NET_VIN_MB7052 RES_NET_VIN_TTL_OUT
+#define RES_NET_VIN_MB7053 RES_NET_VIN_TTL_OUT
+#define RES_NET_VIN_28S42 RES_NET_VIN_TTL_OUT
+
+/* Structures */
+
+struct res_net_channel_info {
+ // per channel options
+ UINT32 options;
+ // Pullup resistor value in Ohms
+ double rBias;
+ // Pulldown resistor value in Ohms
+ double rGnd;
+ // Number of inputs connected to resistors
+ int num;
+ // Resistor values
+ // - Least significant bit first
+ double R[8];
+ // Minimum output voltage
+ // - Applicable if output is routed through a complimentary
+ // - darlington circuit
+ // - typical value ~ 0.9V
+ double minout;
+ // Cutoff output voltage
+ // - Applicable if output is routed through 1:1 transistor amplifier
+ // - Typical value ~ 0.7V
+ double cut;
+ // Voltage at the pullup resistor
+ // - Typical voltage ~5V
+ double vBias;
+};
+
+struct res_net_info {
+ // global options
+ UINT32 options;
+ // The three color channels
+ res_net_channel_info rgb[3];
+ // Supply Voltage
+ // - Typical value 5V
+ double vcc;
+ // High Level output voltage
+ // - TTL : 3.40V
+ // - CMOS: 4.95V (@5v vcc)
+ double vOL;
+ // Low Level output voltage
+ // - TTL : 0.35V
+ // - CMOS: 0.05V (@5v vcc)
+ double vOH;
+ // Open Collector flag
+ UINT8 OpenCol;
+};
+
+#define RES_NET_MAX_COMP 3
+
+struct res_net_decode_info {
+ int numcomp;
+ int start;
+ int end;
+ UINT16 offset[3 * RES_NET_MAX_COMP];
+ INT16 shift[3 * RES_NET_MAX_COMP];
+ UINT16 mask[3 * RES_NET_MAX_COMP];
+};
+
+/* return a single value for one channel */
+
+int compute_res_net(int inputs, int channel, const res_net_info &di);
+
+/* compute all values */
+
+void compute_res_net_all(std::vector<rgb_t> &rgb, const UINT8 *prom, const res_net_decode_info &rdi, const res_net_info &di);
+
+
+/* legacy interface */
+
+double compute_resistor_weights(
+ int minval, int maxval, double scaler,
+ int count_1, const int * resistances_1, double * weights_1, int pulldown_1, int pullup_1,
+ int count_2, const int * resistances_2, double * weights_2, int pulldown_2, int pullup_2,
+ int count_3, const int * resistances_3, double * weights_3, int pulldown_3, int pullup_3 );
+
+#define combine_8_weights(tab,w0,w1,w2,w3,w4,w5,w6,w7) ((int)(((tab)[0]*(w0) + (tab)[1]*(w1) + (tab)[2]*(w2) + (tab)[3]*(w3) + (tab)[4]*(w4) + (tab)[5]*(w5) + (tab)[6]*(w6) + (tab)[7]*(w7)) + 0.5))
+#define combine_7_weights(tab,w0,w1,w2,w3,w4,w5,w6) ((int)(((tab)[0]*(w0) + (tab)[1]*(w1) + (tab)[2]*(w2) + (tab)[3]*(w3) + (tab)[4]*(w4) + (tab)[5]*(w5) + (tab)[6]*(w6)) + 0.5))
+#define combine_6_weights(tab,w0,w1,w2,w3,w4,w5) ((int)(((tab)[0]*(w0) + (tab)[1]*(w1) + (tab)[2]*(w2) + (tab)[3]*(w3) + (tab)[4]*(w4) + (tab)[5]*(w5)) + 0.5))
+#define combine_5_weights(tab,w0,w1,w2,w3,w4) ((int)(((tab)[0]*(w0) + (tab)[1]*(w1) + (tab)[2]*(w2) + (tab)[3]*(w3) + (tab)[4]*(w4)) + 0.5))
+#define combine_4_weights(tab,w0,w1,w2,w3) ((int)(((tab)[0]*(w0) + (tab)[1]*(w1) + (tab)[2]*(w2) + (tab)[3]*(w3)) + 0.5))
+#define combine_3_weights(tab,w0,w1,w2) ((int)(((tab)[0]*(w0) + (tab)[1]*(w1) + (tab)[2]*(w2)) + 0.5))
+#define combine_2_weights(tab,w0,w1) ((int)(((tab)[0]*(w0) + (tab)[1]*(w1)) + 0.5))
+#define combine_1_weights(tab,w0) ((int)(((tab)[0]*(w0) + 0.5)))
+
+
+
+/* this should be moved to one of the core files */
+
+#define MAX_NETS 3
+#define MAX_RES_PER_NET 18
+
+
+
+
+/* for the open collector outputs PROMs */
+
+double compute_resistor_net_outputs(
+ int minval, int maxval, double scaler,
+ int count_1, const int * resistances_1, double * outputs_1, int pulldown_1, int pullup_1,
+ int count_2, const int * resistances_2, double * outputs_2, int pulldown_2, int pullup_2,
+ int count_3, const int * resistances_3, double * outputs_3, int pulldown_3, int pullup_3 );
+
+
+
+#endif /*_RESNET_H_*/
diff --git a/src/devices/video/rgbgen.c b/src/devices/video/rgbgen.c
new file mode 100644
index 00000000000..4e7e6042569
--- /dev/null
+++ b/src/devices/video/rgbgen.c
@@ -0,0 +1,136 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb, Ryan Holtz
+/***************************************************************************
+
+ rgbgen.c
+
+ General RGB utilities.
+
+***************************************************************************/
+
+#if !(defined(__ALTIVEC__) || ((!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || defined(_MSC_VER)) && defined(PTR64)))
+
+#include "emu.h"
+#include "rgbgen.h"
+
+/***************************************************************************
+ HIGHER LEVEL OPERATIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ rgbaint_blend - blend two colors by the given
+ scale factor
+-------------------------------------------------*/
+
+void rgbaint_t::blend(const rgbaint_t& color2, UINT8 color1scale)
+{
+ INT32 scale1 = (INT32)color1scale;
+ INT32 scale2 = 256 - scale1;
+
+ m_a = (m_a * scale1 + color2.m_a * scale2) >> 8;
+ m_r = (m_r * scale1 + color2.m_r * scale2) >> 8;
+ m_g = (m_g * scale1 + color2.m_g * scale2) >> 8;
+ m_b = (m_b * scale1 + color2.m_b * scale2) >> 8;
+ m_a |= (m_a & 0x00800000) ? 0xff000000 : 0;
+ m_r |= (m_r & 0x00800000) ? 0xff000000 : 0;
+ m_g |= (m_g & 0x00800000) ? 0xff000000 : 0;
+ m_b |= (m_b & 0x00800000) ? 0xff000000 : 0;
+}
+
+
+/*-------------------------------------------------
+ rgbaint_scale_and_clamp - scale the given
+ color by an 8.8 scale factor, immediate or
+ per channel, and clamp to byte values
+-------------------------------------------------*/
+
+void rgbaint_t::scale_imm_and_clamp(INT32 scale)
+{
+ m_a = (m_a * scale) >> 8;
+ m_r = (m_r * scale) >> 8;
+ m_g = (m_g * scale) >> 8;
+ m_b = (m_b * scale) >> 8;
+ m_a |= (m_a & 0x00800000) ? 0xff000000 : 0;
+ m_r |= (m_r & 0x00800000) ? 0xff000000 : 0;
+ m_g |= (m_g & 0x00800000) ? 0xff000000 : 0;
+ m_b |= (m_b & 0x00800000) ? 0xff000000 : 0;
+ if ((UINT32)m_a > 255) { m_a = (m_a < 0) ? 0 : 255; }
+ if ((UINT32)m_r > 255) { m_r = (m_r < 0) ? 0 : 255; }
+ if ((UINT32)m_g > 255) { m_g = (m_g < 0) ? 0 : 255; }
+ if ((UINT32)m_b > 255) { m_b = (m_b < 0) ? 0 : 255; }
+}
+
+void rgbaint_t::scale_and_clamp(const rgbaint_t& scale)
+{
+ m_a = (m_a * scale.m_a) >> 8;
+ m_r = (m_r * scale.m_r) >> 8;
+ m_g = (m_g * scale.m_g) >> 8;
+ m_b = (m_b * scale.m_b) >> 8;
+ m_a |= (m_a & 0x00800000) ? 0xff000000 : 0;
+ m_r |= (m_r & 0x00800000) ? 0xff000000 : 0;
+ m_g |= (m_g & 0x00800000) ? 0xff000000 : 0;
+ m_b |= (m_b & 0x00800000) ? 0xff000000 : 0;
+ if ((UINT32)m_a > 255) { m_a = (m_a < 0) ? 0 : 255; }
+ if ((UINT32)m_r > 255) { m_r = (m_r < 0) ? 0 : 255; }
+ if ((UINT32)m_g > 255) { m_g = (m_g < 0) ? 0 : 255; }
+ if ((UINT32)m_b > 255) { m_b = (m_b < 0) ? 0 : 255; }
+}
+
+
+void rgbaint_t::scale_imm_add_and_clamp(INT32 scale, const rgbaint_t& other)
+{
+ m_a = (m_a * scale) >> 8;
+ m_r = (m_r * scale) >> 8;
+ m_g = (m_g * scale) >> 8;
+ m_b = (m_b * scale) >> 8;
+ m_a |= (m_a & 0x00800000) ? 0xff000000 : 0;
+ m_r |= (m_r & 0x00800000) ? 0xff000000 : 0;
+ m_g |= (m_g & 0x00800000) ? 0xff000000 : 0;
+ m_b |= (m_b & 0x00800000) ? 0xff000000 : 0;
+ m_a += other.m_a;
+ m_r += other.m_r;
+ m_g += other.m_g;
+ m_b += other.m_b;
+ if ((UINT32)m_a > 255) { m_a = (m_a < 0) ? 0 : 255; }
+ if ((UINT32)m_r > 255) { m_r = (m_r < 0) ? 0 : 255; }
+ if ((UINT32)m_g > 255) { m_g = (m_g < 0) ? 0 : 255; }
+ if ((UINT32)m_b > 255) { m_b = (m_b < 0) ? 0 : 255; }
+}
+
+void rgbaint_t::scale_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other)
+{
+ m_a = (m_a * scale.m_a) >> 8;
+ m_r = (m_r * scale.m_r) >> 8;
+ m_g = (m_g * scale.m_g) >> 8;
+ m_b = (m_b * scale.m_b) >> 8;
+ m_a |= (m_a & 0x00800000) ? 0xff000000 : 0;
+ m_r |= (m_r & 0x00800000) ? 0xff000000 : 0;
+ m_g |= (m_g & 0x00800000) ? 0xff000000 : 0;
+ m_b |= (m_b & 0x00800000) ? 0xff000000 : 0;
+ m_a += other.m_a;
+ m_r += other.m_r;
+ m_g += other.m_g;
+ m_b += other.m_b;
+ if ((UINT32)m_a > 255) { m_a = (m_a < 0) ? 0 : 255; }
+ if ((UINT32)m_r > 255) { m_r = (m_r < 0) ? 0 : 255; }
+ if ((UINT32)m_g > 255) { m_g = (m_g < 0) ? 0 : 255; }
+ if ((UINT32)m_b > 255) { m_b = (m_b < 0) ? 0 : 255; }
+}
+
+void rgbaint_t::scale2_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other, const rgbaint_t& scale2)
+{
+ m_a = (m_a * scale.m_a + other.m_a * scale2.m_a) >> 8;
+ m_r = (m_r * scale.m_r + other.m_r * scale2.m_r) >> 8;
+ m_g = (m_g * scale.m_g + other.m_g * scale2.m_g) >> 8;
+ m_b = (m_b * scale.m_b + other.m_b * scale2.m_b) >> 8;
+ m_a |= (m_a & 0x00800000) ? 0xff000000 : 0;
+ m_r |= (m_r & 0x00800000) ? 0xff000000 : 0;
+ m_g |= (m_g & 0x00800000) ? 0xff000000 : 0;
+ m_b |= (m_b & 0x00800000) ? 0xff000000 : 0;
+ if ((UINT32)m_a > 255) { m_a = (m_a < 0) ? 0 : 255; }
+ if ((UINT32)m_r > 255) { m_r = (m_r < 0) ? 0 : 255; }
+ if ((UINT32)m_g > 255) { m_g = (m_g < 0) ? 0 : 255; }
+ if ((UINT32)m_b > 255) { m_b = (m_b < 0) ? 0 : 255; }
+}
+
+#endif // !defined(__ALTIVEC__)
diff --git a/src/devices/video/rgbgen.h b/src/devices/video/rgbgen.h
new file mode 100644
index 00000000000..dae5a2984d3
--- /dev/null
+++ b/src/devices/video/rgbgen.h
@@ -0,0 +1,507 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb, Ryan Holtz
+/***************************************************************************
+
+ rgbgen.h
+
+ General RGB utilities.
+
+***************************************************************************/
+
+#ifndef __RGBGEN__
+#define __RGBGEN__
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class rgbaint_t
+{
+public:
+ inline rgbaint_t() { }
+ inline rgbaint_t(UINT32 rgba) { set(rgba); }
+ inline rgbaint_t(INT32 a, INT32 r, INT32 g, INT32 b) { set(a, r, g, b); }
+ inline rgbaint_t(rgb_t& rgba) { set(rgba); }
+
+ inline void set(rgbaint_t& other) { set(other.m_a, other.m_r, other.m_g, other.m_b); }
+ inline void set(UINT32 rgba) { set((rgba >> 24) & 0xff, (rgba >> 16) & 0xff, (rgba >> 8) & 0xff, rgba & 0xff); }
+ inline void set(INT32 a, INT32 r, INT32 g, INT32 b)
+ {
+ m_a = a;
+ m_r = r;
+ m_g = g;
+ m_b = b;
+ }
+ inline void set(rgb_t& rgba) { set(rgba.a(), rgba.r(), rgba.g(), rgba.b()); }
+
+ inline rgb_t to_rgba()
+ {
+ return rgb_t(m_a, m_r, m_g, m_b);
+ }
+
+ inline rgb_t to_rgba_clamp()
+ {
+ UINT8 a = (m_a < 0) ? 0 : (m_a > 255) ? 255 : m_a;
+ UINT8 r = (m_r < 0) ? 0 : (m_r > 255) ? 255 : m_r;
+ UINT8 g = (m_g < 0) ? 0 : (m_g > 255) ? 255 : m_g;
+ UINT8 b = (m_b < 0) ? 0 : (m_b > 255) ? 255 : m_b;
+ return rgb_t(a, r, g, b);
+ }
+
+ inline void add(const rgbaint_t& color)
+ {
+ add_imm_rgba(color.m_a, color.m_r, color.m_g, color.m_b);
+ }
+
+ inline void add_imm(const INT32 imm)
+ {
+ add_imm_rgba(imm, imm, imm, imm);
+ }
+
+ inline void add_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_a += a;
+ m_r += r;
+ m_g += g;
+ m_b += b;
+ }
+
+ inline void sub(const rgbaint_t& color)
+ {
+ sub_imm_rgba(color.m_a, color.m_r, color.m_g, color.m_b);
+ }
+
+ inline void sub_imm(const INT32 imm)
+ {
+ sub_imm_rgba(imm, imm, imm, imm);
+ }
+
+ inline void sub_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_a -= a;
+ m_r -= r;
+ m_g -= g;
+ m_b -= b;
+ }
+
+ inline void subr(rgbaint_t& color)
+ {
+ subr_imm_rgba(color.m_a, color.m_r, color.m_g, color.m_b);
+ }
+
+ inline void subr_imm(const INT32 imm)
+ {
+ subr_imm_rgba(imm, imm, imm, imm);
+ }
+
+ inline void subr_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_a = a - m_a;
+ m_r = r - m_r;
+ m_g = g - m_g;
+ m_b = b - m_b;
+ }
+
+ inline void set_a(const INT32 value)
+ {
+ m_a = value;
+ }
+
+ inline void set_r(const INT32 value)
+ {
+ m_r = value;
+ }
+
+ inline void set_g(const INT32 value)
+ {
+ m_g = value;
+ }
+
+ inline void set_b(const INT32 value)
+ {
+ m_b = value;
+ }
+
+ inline UINT8 get_a() const
+ {
+ return m_a;
+ }
+
+ inline UINT8 get_r() const
+ {
+ return m_r;
+ }
+
+ inline UINT8 get_g() const
+ {
+ return m_g;
+ }
+
+ inline UINT8 get_b() const
+ {
+ return m_b;
+ }
+
+ inline INT32 get_a32() const
+ {
+ return m_a;
+ }
+
+ inline INT32 get_r32() const
+ {
+ return m_r;
+ }
+
+ inline INT32 get_g32() const
+ {
+ return m_g;
+ }
+
+ inline INT32 get_b32() const
+ {
+ return m_b;
+ }
+
+ inline void mul(rgbaint_t& color)
+ {
+ mul_imm_rgba(color.m_a, color.m_r, color.m_g, color.m_b);
+ }
+
+ inline void mul_imm(const INT32 imm)
+ {
+ mul_imm_rgba(imm, imm, imm, imm);
+ }
+
+ inline void mul_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_a *= a;
+ m_r *= r;
+ m_g *= g;
+ m_b *= b;
+ }
+
+ inline void shl(const rgbaint_t& shift)
+ {
+ m_a <<= shift.m_a;
+ m_r <<= shift.m_r;
+ m_g <<= shift.m_g;
+ m_b <<= shift.m_b;
+ }
+
+ inline void shl_imm(const UINT8 shift)
+ {
+ if (shift == 0)
+ return;
+
+ m_a <<= shift;
+ m_r <<= shift;
+ m_g <<= shift;
+ m_b <<= shift;
+ }
+
+ inline void shr(const rgbaint_t& shift)
+ {
+ m_a >>= shift.m_a;
+ m_r >>= shift.m_r;
+ m_g >>= shift.m_g;
+ m_b >>= shift.m_b;
+ }
+
+ inline void shr_imm(const UINT8 shift)
+ {
+ if (shift == 0)
+ return;
+
+ m_a >>= shift;
+ m_r >>= shift;
+ m_g >>= shift;
+ m_b >>= shift;
+ }
+
+ inline void sra(const rgbaint_t& shift)
+ {
+ m_a >>= shift.m_a;
+ if (m_a & (1 << (31 - shift.m_a)))
+ m_a |= ~0 << (32 - shift.m_a);
+
+ m_r >>= shift.m_r;
+ if (m_r & (1 << (31 - shift.m_r)))
+ m_r |= ~0 << (32 - shift.m_r);
+
+ m_g >>= shift.m_g;
+ if (m_g & (1 << (31 - shift.m_g)))
+ m_g |= ~0 << (32 - shift.m_g);
+
+ m_b >>= shift.m_b;
+ if (m_b & (1 << (31 - shift.m_b)))
+ m_b |= ~0 << (32 - shift.m_b);
+ }
+
+ inline void sra_imm(const UINT8 shift)
+ {
+ const UINT32 high_bit = 1 << (31 - shift);
+ const UINT32 high_mask = ~0 << (32 - shift);
+
+ m_a >>= shift;
+ if (m_a & high_bit)
+ m_a |= high_mask;
+
+ m_r >>= shift;
+ if (m_r & high_bit)
+ m_r |= high_mask;
+
+ m_g >>= shift;
+ if (m_g & high_bit)
+ m_g |= high_mask;
+
+ m_b >>= shift;
+ if (m_b & high_bit)
+ m_b |= high_mask;
+ }
+
+ inline void or_reg(const rgbaint_t& color)
+ {
+ or_imm_rgba(color.m_a, color.m_r, color.m_g, color.m_b);
+ }
+
+ inline void or_imm(const INT32 imm)
+ {
+ or_imm_rgba(imm, imm, imm, imm);
+ }
+
+ inline void or_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_a |= a;
+ m_r |= r;
+ m_g |= g;
+ m_b |= b;
+ }
+
+ inline void and_reg(const rgbaint_t& color)
+ {
+ and_imm_rgba(color.m_a, color.m_r, color.m_g, color.m_b);
+ }
+
+ inline void andnot_reg(const rgbaint_t& color)
+ {
+ and_imm_rgba(~color.m_a, ~color.m_r, ~color.m_g, ~color.m_b);
+ }
+
+ inline void and_imm(const INT32 imm)
+ {
+ and_imm_rgba(imm, imm, imm, imm);
+ }
+
+ inline void and_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_a &= a;
+ m_r &= r;
+ m_g &= g;
+ m_b &= b;
+ }
+
+ inline void xor_reg(const rgbaint_t& color)
+ {
+ xor_imm_rgba(color.m_a, color.m_r, color.m_g, color.m_b);
+ }
+
+ inline void xor_imm(const INT32 imm)
+ {
+ xor_imm_rgba(imm, imm, imm, imm);
+ }
+
+ inline void xor_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_a ^= a;
+ m_r ^= r;
+ m_g ^= g;
+ m_b ^= b;
+ }
+
+ inline void clamp_and_clear(const UINT32 sign)
+ {
+ if (m_a & sign)
+ m_a = 0;
+
+ if (m_r & sign)
+ m_r = 0;
+
+ if (m_g & sign)
+ m_g = 0;
+
+ if (m_b & sign)
+ m_b = 0;
+
+ m_a = (m_a < 0) ? 0 : (m_a > 255) ? 255 : m_a;
+ m_r = (m_r < 0) ? 0 : (m_r > 255) ? 255 : m_r;
+ m_g = (m_g < 0) ? 0 : (m_g > 255) ? 255 : m_g;
+ m_b = (m_b < 0) ? 0 : (m_b > 255) ? 255 : m_b;
+ }
+
+ inline void clamp_to_uint8()
+ {
+ m_a = (m_a < 0) ? 0 : (m_a > 255) ? 255 : m_a;
+ m_r = (m_r < 0) ? 0 : (m_r > 255) ? 255 : m_r;
+ m_g = (m_g < 0) ? 0 : (m_g > 255) ? 255 : m_g;
+ m_b = (m_b < 0) ? 0 : (m_b > 255) ? 255 : m_b;
+ }
+
+ inline void sign_extend(const UINT32 compare, const UINT32 sign)
+ {
+ if ((m_a & compare) == compare)
+ m_a |= sign;
+
+ if ((m_r & compare) == compare)
+ m_r |= sign;
+
+ if ((m_g & compare) == compare)
+ m_g |= sign;
+
+ if ((m_b & compare) == compare)
+ m_b |= sign;
+ }
+
+ inline void min(const INT32 value)
+ {
+ m_a = (m_a > value) ? value : m_a;
+ m_r = (m_r > value) ? value : m_r;
+ m_g = (m_g > value) ? value : m_g;
+ m_b = (m_b > value) ? value : m_b;
+ }
+
+ void blend(const rgbaint_t& other, UINT8 factor);
+
+ void scale_and_clamp(const rgbaint_t& scale);
+ void scale_imm_and_clamp(const INT32 scale);
+ void scale2_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other, const rgbaint_t& scale2);
+ void scale_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other);
+ void scale_imm_add_and_clamp(const INT32 scale, const rgbaint_t& other);
+
+ inline void cmpeq(const rgbaint_t& value)
+ {
+ m_a = (m_a == value.m_a) ? 0xffffffff : 0;
+ m_r = (m_r == value.m_r) ? 0xffffffff : 0;
+ m_g = (m_g == value.m_g) ? 0xffffffff : 0;
+ m_b = (m_b == value.m_b) ? 0xffffffff : 0;
+ }
+
+ inline void cmpeq_imm(const INT32 value)
+ {
+ m_a = (m_a == value) ? 0xffffffff : 0;
+ m_r = (m_r == value) ? 0xffffffff : 0;
+ m_g = (m_g == value) ? 0xffffffff : 0;
+ m_b = (m_b == value) ? 0xffffffff : 0;
+ }
+
+ inline void cmpgt(const rgbaint_t& value)
+ {
+ m_a = (m_a > value.m_a) ? 0xffffffff : 0;
+ m_r = (m_r > value.m_r) ? 0xffffffff : 0;
+ m_g = (m_g > value.m_g) ? 0xffffffff : 0;
+ m_b = (m_b > value.m_b) ? 0xffffffff : 0;
+ }
+
+ inline void cmpgt_imm(const INT32 value)
+ {
+ m_a = (m_a > value) ? 0xffffffff : 0;
+ m_r = (m_r > value) ? 0xffffffff : 0;
+ m_g = (m_g > value) ? 0xffffffff : 0;
+ m_b = (m_b > value) ? 0xffffffff : 0;
+ }
+
+ inline void cmplt(const rgbaint_t& value)
+ {
+ m_a = (m_a < value.m_a) ? 0xffffffff : 0;
+ m_r = (m_r < value.m_r) ? 0xffffffff : 0;
+ m_g = (m_g < value.m_g) ? 0xffffffff : 0;
+ m_b = (m_b < value.m_b) ? 0xffffffff : 0;
+ }
+
+ inline void cmplt_imm(const INT32 value)
+ {
+ m_a = (m_a < value) ? 0xffffffff : 0;
+ m_r = (m_r < value) ? 0xffffffff : 0;
+ m_g = (m_g < value) ? 0xffffffff : 0;
+ m_b = (m_b < value) ? 0xffffffff : 0;
+ }
+
+ inline void merge_alpha(rgbaint_t& alpha)
+ {
+ m_a = alpha.m_a;
+ }
+
+ inline rgbaint_t operator=(const rgbaint_t& other)
+ {
+ m_a = other.m_a;
+ m_r = other.m_r;
+ m_g = other.m_g;
+ m_b = other.m_b;
+ return *this;
+ }
+
+ inline rgbaint_t& operator+=(const rgbaint_t& other)
+ {
+ m_a += other.m_a;
+ m_r += other.m_r;
+ m_g += other.m_g;
+ m_b += other.m_b;
+ return *this;
+ }
+
+ inline rgbaint_t& operator+=(const INT32 other)
+ {
+ m_a += other;
+ m_r += other;
+ m_g += other;
+ m_b += other;
+ return *this;
+ }
+
+ static UINT32 bilinear_filter(UINT32 rgb00, UINT32 rgb01, UINT32 rgb10, UINT32 rgb11, UINT8 u, UINT8 v)
+ {
+ UINT32 rb0 = (rgb00 & 0x00ff00ff) + ((((rgb01 & 0x00ff00ff) - (rgb00 & 0x00ff00ff)) * u) >> 8);
+ UINT32 rb1 = (rgb10 & 0x00ff00ff) + ((((rgb11 & 0x00ff00ff) - (rgb10 & 0x00ff00ff)) * u) >> 8);
+
+ rgb00 >>= 8;
+ rgb01 >>= 8;
+ rgb10 >>= 8;
+ rgb11 >>= 8;
+
+ UINT32 ag0 = (rgb00 & 0x00ff00ff) + ((((rgb01 & 0x00ff00ff) - (rgb00 & 0x00ff00ff)) * u) >> 8);
+ UINT32 ag1 = (rgb10 & 0x00ff00ff) + ((((rgb11 & 0x00ff00ff) - (rgb10 & 0x00ff00ff)) * u) >> 8);
+
+ rb0 = (rb0 & 0x00ff00ff) + ((((rb1 & 0x00ff00ff) - (rb0 & 0x00ff00ff)) * v) >> 8);
+ ag0 = (ag0 & 0x00ff00ff) + ((((ag1 & 0x00ff00ff) - (ag0 & 0x00ff00ff)) * v) >> 8);
+
+ return ((ag0 << 8) & 0xff00ff00) | (rb0 & 0x00ff00ff);
+ }
+
+ inline void bilinear_filter_rgbaint(UINT32 rgb00, UINT32 rgb01, UINT32 rgb10, UINT32 rgb11, UINT8 u, UINT8 v)
+ {
+ UINT32 rb0 = (rgb00 & 0x00ff00ff) + ((((rgb01 & 0x00ff00ff) - (rgb00 & 0x00ff00ff)) * u) >> 8);
+ UINT32 rb1 = (rgb10 & 0x00ff00ff) + ((((rgb11 & 0x00ff00ff) - (rgb10 & 0x00ff00ff)) * u) >> 8);
+
+ rgb00 >>= 8;
+ rgb01 >>= 8;
+ rgb10 >>= 8;
+ rgb11 >>= 8;
+
+ UINT32 ag0 = (rgb00 & 0x00ff00ff) + ((((rgb01 & 0x00ff00ff) - (rgb00 & 0x00ff00ff)) * u) >> 8);
+ UINT32 ag1 = (rgb10 & 0x00ff00ff) + ((((rgb11 & 0x00ff00ff) - (rgb10 & 0x00ff00ff)) * u) >> 8);
+
+ rb0 = (rb0 & 0x00ff00ff) + ((((rb1 & 0x00ff00ff) - (rb0 & 0x00ff00ff)) * v) >> 8);
+ ag0 = (ag0 & 0x00ff00ff) + ((((ag1 & 0x00ff00ff) - (ag0 & 0x00ff00ff)) * v) >> 8);
+
+ UINT32 result = ((ag0 << 8) & 0xff00ff00) | (rb0 & 0x00ff00ff);
+ this->set(result);
+ }
+
+protected:
+ INT32 m_a;
+ INT32 m_r;
+ INT32 m_g;
+ INT32 m_b;
+};
+
+#endif /* __RGBGEN__ */
diff --git a/src/devices/video/rgbsse.c b/src/devices/video/rgbsse.c
new file mode 100644
index 00000000000..ed20479fd9e
--- /dev/null
+++ b/src/devices/video/rgbsse.c
@@ -0,0 +1,194 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb, Ryan Holtz
+/***************************************************************************
+
+ rgbsse.c
+
+ SSE optimized RGB utilities.
+
+ WARNING: This code assumes SSE2 or greater capability.
+
+***************************************************************************/
+
+#if (!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || defined(_MSC_VER)) && defined(PTR64)
+
+#include "emu.h"
+#include <emmintrin.h>
+#include "rgbsse.h"
+
+/***************************************************************************
+ TABLES
+***************************************************************************/
+
+const struct rgbaint_t::_statics rgbaint_t::statics =
+{
+ { 0 },
+ { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000 },
+ { 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff },
+ { 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff },
+ { 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff },
+ {
+ { 0, 256, 0, 256, 0, 256, 0, 256 }, { 1, 255, 1, 255, 1, 255, 1, 255 },
+ { 2, 254, 2, 254, 2, 254, 2, 254 }, { 3, 253, 3, 253, 3, 253, 3, 253 },
+ { 4, 252, 4, 252, 4, 252, 4, 252 }, { 5, 251, 5, 251, 5, 251, 5, 251 },
+ { 6, 250, 6, 250, 6, 250, 6, 250 }, { 7, 249, 7, 249, 7, 249, 7, 249 },
+ { 8, 248, 8, 248, 8, 248, 8, 248 }, { 9, 247, 9, 247, 9, 247, 9, 247 },
+ { 10, 246, 10, 246, 10, 246, 10, 246 }, { 11, 245, 11, 245, 11, 245, 11, 245 },
+ { 12, 244, 12, 244, 12, 244, 12, 244 }, { 13, 243, 13, 243, 13, 243, 13, 243 },
+ { 14, 242, 14, 242, 14, 242, 14, 242 }, { 15, 241, 15, 241, 15, 241, 15, 241 },
+ { 16, 240, 16, 240, 16, 240, 16, 240 }, { 17, 239, 17, 239, 17, 239, 17, 239 },
+ { 18, 238, 18, 238, 18, 238, 18, 238 }, { 19, 237, 19, 237, 19, 237, 19, 237 },
+ { 20, 236, 20, 236, 20, 236, 20, 236 }, { 21, 235, 21, 235, 21, 235, 21, 235 },
+ { 22, 234, 22, 234, 22, 234, 22, 234 }, { 23, 233, 23, 233, 23, 233, 23, 233 },
+ { 24, 232, 24, 232, 24, 232, 24, 232 }, { 25, 231, 25, 231, 25, 231, 25, 231 },
+ { 26, 230, 26, 230, 26, 230, 26, 230 }, { 27, 229, 27, 229, 27, 229, 27, 229 },
+ { 28, 228, 28, 228, 28, 228, 28, 228 }, { 29, 227, 29, 227, 29, 227, 29, 227 },
+ { 30, 226, 30, 226, 30, 226, 30, 226 }, { 31, 225, 31, 225, 31, 225, 31, 225 },
+ { 32, 224, 32, 224, 32, 224, 32, 224 }, { 33, 223, 33, 223, 33, 223, 33, 223 },
+ { 34, 222, 34, 222, 34, 222, 34, 222 }, { 35, 221, 35, 221, 35, 221, 35, 221 },
+ { 36, 220, 36, 220, 36, 220, 36, 220 }, { 37, 219, 37, 219, 37, 219, 37, 219 },
+ { 38, 218, 38, 218, 38, 218, 38, 218 }, { 39, 217, 39, 217, 39, 217, 39, 217 },
+ { 40, 216, 40, 216, 40, 216, 40, 216 }, { 41, 215, 41, 215, 41, 215, 41, 215 },
+ { 42, 214, 42, 214, 42, 214, 42, 214 }, { 43, 213, 43, 213, 43, 213, 43, 213 },
+ { 44, 212, 44, 212, 44, 212, 44, 212 }, { 45, 211, 45, 211, 45, 211, 45, 211 },
+ { 46, 210, 46, 210, 46, 210, 46, 210 }, { 47, 209, 47, 209, 47, 209, 47, 209 },
+ { 48, 208, 48, 208, 48, 208, 48, 208 }, { 49, 207, 49, 207, 49, 207, 49, 207 },
+ { 50, 206, 50, 206, 50, 206, 50, 206 }, { 51, 205, 51, 205, 51, 205, 51, 205 },
+ { 52, 204, 52, 204, 52, 204, 52, 204 }, { 53, 203, 53, 203, 53, 203, 53, 203 },
+ { 54, 202, 54, 202, 54, 202, 54, 202 }, { 55, 201, 55, 201, 55, 201, 55, 201 },
+ { 56, 200, 56, 200, 56, 200, 56, 200 }, { 57, 199, 57, 199, 57, 199, 57, 199 },
+ { 58, 198, 58, 198, 58, 198, 58, 198 }, { 59, 197, 59, 197, 59, 197, 59, 197 },
+ { 60, 196, 60, 196, 60, 196, 60, 196 }, { 61, 195, 61, 195, 61, 195, 61, 195 },
+ { 62, 194, 62, 194, 62, 194, 62, 194 }, { 63, 193, 63, 193, 63, 193, 63, 193 },
+ { 64, 192, 64, 192, 64, 192, 64, 192 }, { 65, 191, 65, 191, 65, 191, 65, 191 },
+ { 66, 190, 66, 190, 66, 190, 66, 190 }, { 67, 189, 67, 189, 67, 189, 67, 189 },
+ { 68, 188, 68, 188, 68, 188, 68, 188 }, { 69, 187, 69, 187, 69, 187, 69, 187 },
+ { 70, 186, 70, 186, 70, 186, 70, 186 }, { 71, 185, 71, 185, 71, 185, 71, 185 },
+ { 72, 184, 72, 184, 72, 184, 72, 184 }, { 73, 183, 73, 183, 73, 183, 73, 183 },
+ { 74, 182, 74, 182, 74, 182, 74, 182 }, { 75, 181, 75, 181, 75, 181, 75, 181 },
+ { 76, 180, 76, 180, 76, 180, 76, 180 }, { 77, 179, 77, 179, 77, 179, 77, 179 },
+ { 78, 178, 78, 178, 78, 178, 78, 178 }, { 79, 177, 79, 177, 79, 177, 79, 177 },
+ { 80, 176, 80, 176, 80, 176, 80, 176 }, { 81, 175, 81, 175, 81, 175, 81, 175 },
+ { 82, 174, 82, 174, 82, 174, 82, 174 }, { 83, 173, 83, 173, 83, 173, 83, 173 },
+ { 84, 172, 84, 172, 84, 172, 84, 172 }, { 85, 171, 85, 171, 85, 171, 85, 171 },
+ { 86, 170, 86, 170, 86, 170, 86, 170 }, { 87, 169, 87, 169, 87, 169, 87, 169 },
+ { 88, 168, 88, 168, 88, 168, 88, 168 }, { 89, 167, 89, 167, 89, 167, 89, 167 },
+ { 90, 166, 90, 166, 90, 166, 90, 166 }, { 91, 165, 91, 165, 91, 165, 91, 165 },
+ { 92, 164, 92, 164, 92, 164, 92, 164 }, { 93, 163, 93, 163, 93, 163, 93, 163 },
+ { 94, 162, 94, 162, 94, 162, 94, 162 }, { 95, 161, 95, 161, 95, 161, 95, 161 },
+ { 96, 160, 96, 160, 96, 160, 96, 160 }, { 97, 159, 97, 159, 97, 159, 97, 159 },
+ { 98, 158, 98, 158, 98, 158, 98, 158 }, { 99, 157, 99, 157, 99, 157, 99, 157 },
+ { 100, 156, 100, 156, 100, 156, 100, 156 }, { 101, 155, 101, 155, 101, 155, 101, 155 },
+ { 102, 154, 102, 154, 102, 154, 102, 154 }, { 103, 153, 103, 153, 103, 153, 103, 153 },
+ { 104, 152, 104, 152, 104, 152, 104, 152 }, { 105, 151, 105, 151, 105, 151, 105, 151 },
+ { 106, 150, 106, 150, 106, 150, 106, 150 }, { 107, 149, 107, 149, 107, 149, 107, 149 },
+ { 108, 148, 108, 148, 108, 148, 108, 148 }, { 109, 147, 109, 147, 109, 147, 109, 147 },
+ { 110, 146, 110, 146, 110, 146, 110, 146 }, { 111, 145, 111, 145, 111, 145, 111, 145 },
+ { 112, 144, 112, 144, 112, 144, 112, 144 }, { 113, 143, 113, 143, 113, 143, 113, 143 },
+ { 114, 142, 114, 142, 114, 142, 114, 142 }, { 115, 141, 115, 141, 115, 141, 115, 141 },
+ { 116, 140, 116, 140, 116, 140, 116, 140 }, { 117, 139, 117, 139, 117, 139, 117, 139 },
+ { 118, 138, 118, 138, 118, 138, 118, 138 }, { 119, 137, 119, 137, 119, 137, 119, 137 },
+ { 120, 136, 120, 136, 120, 136, 120, 136 }, { 121, 135, 121, 135, 121, 135, 121, 135 },
+ { 122, 134, 122, 134, 122, 134, 122, 134 }, { 123, 133, 123, 133, 123, 133, 123, 133 },
+ { 124, 132, 124, 132, 124, 132, 124, 132 }, { 125, 131, 125, 131, 125, 131, 125, 131 },
+ { 126, 130, 126, 130, 126, 130, 126, 130 }, { 127, 129, 127, 129, 127, 129, 127, 129 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 }, { 129, 127, 129, 127, 129, 127, 129, 127 },
+ { 130, 126, 130, 126, 130, 126, 130, 126 }, { 131, 125, 131, 125, 131, 125, 131, 125 },
+ { 132, 124, 132, 124, 132, 124, 132, 124 }, { 133, 123, 133, 123, 133, 123, 133, 123 },
+ { 134, 122, 134, 122, 134, 122, 134, 122 }, { 135, 121, 135, 121, 135, 121, 135, 121 },
+ { 136, 120, 136, 120, 136, 120, 136, 120 }, { 137, 119, 137, 119, 137, 119, 137, 119 },
+ { 138, 118, 138, 118, 138, 118, 138, 118 }, { 139, 117, 139, 117, 139, 117, 139, 117 },
+ { 140, 116, 140, 116, 140, 116, 140, 116 }, { 141, 115, 141, 115, 141, 115, 141, 115 },
+ { 142, 114, 142, 114, 142, 114, 142, 114 }, { 143, 113, 143, 113, 143, 113, 143, 113 },
+ { 144, 112, 144, 112, 144, 112, 144, 112 }, { 145, 111, 145, 111, 145, 111, 145, 111 },
+ { 146, 110, 146, 110, 146, 110, 146, 110 }, { 147, 109, 147, 109, 147, 109, 147, 109 },
+ { 148, 108, 148, 108, 148, 108, 148, 108 }, { 149, 107, 149, 107, 149, 107, 149, 107 },
+ { 150, 106, 150, 106, 150, 106, 150, 106 }, { 151, 105, 151, 105, 151, 105, 151, 105 },
+ { 152, 104, 152, 104, 152, 104, 152, 104 }, { 153, 103, 153, 103, 153, 103, 153, 103 },
+ { 154, 102, 154, 102, 154, 102, 154, 102 }, { 155, 101, 155, 101, 155, 101, 155, 101 },
+ { 156, 100, 156, 100, 156, 100, 156, 100 }, { 157, 99, 157, 99, 157, 99, 157, 99 },
+ { 158, 98, 158, 98, 158, 98, 158, 98 }, { 159, 97, 159, 97, 159, 97, 159, 97 },
+ { 160, 96, 160, 96, 160, 96, 160, 96 }, { 161, 95, 161, 95, 161, 95, 161, 95 },
+ { 162, 94, 162, 94, 162, 94, 162, 94 }, { 163, 93, 163, 93, 163, 93, 163, 93 },
+ { 164, 92, 164, 92, 164, 92, 164, 92 }, { 165, 91, 165, 91, 165, 91, 165, 91 },
+ { 166, 90, 166, 90, 166, 90, 166, 90 }, { 167, 89, 167, 89, 167, 89, 167, 89 },
+ { 168, 88, 168, 88, 168, 88, 168, 88 }, { 169, 87, 169, 87, 169, 87, 169, 87 },
+ { 170, 86, 170, 86, 170, 86, 170, 86 }, { 171, 85, 171, 85, 171, 85, 171, 85 },
+ { 172, 84, 172, 84, 172, 84, 172, 84 }, { 173, 83, 173, 83, 173, 83, 173, 83 },
+ { 174, 82, 174, 82, 174, 82, 174, 82 }, { 175, 81, 175, 81, 175, 81, 175, 81 },
+ { 176, 80, 176, 80, 176, 80, 176, 80 }, { 177, 79, 177, 79, 177, 79, 177, 79 },
+ { 178, 78, 178, 78, 178, 78, 178, 78 }, { 179, 77, 179, 77, 179, 77, 179, 77 },
+ { 180, 76, 180, 76, 180, 76, 180, 76 }, { 181, 75, 181, 75, 181, 75, 181, 75 },
+ { 182, 74, 182, 74, 182, 74, 182, 74 }, { 183, 73, 183, 73, 183, 73, 183, 73 },
+ { 184, 72, 184, 72, 184, 72, 184, 72 }, { 185, 71, 185, 71, 185, 71, 185, 71 },
+ { 186, 70, 186, 70, 186, 70, 186, 70 }, { 187, 69, 187, 69, 187, 69, 187, 69 },
+ { 188, 68, 188, 68, 188, 68, 188, 68 }, { 189, 67, 189, 67, 189, 67, 189, 67 },
+ { 190, 66, 190, 66, 190, 66, 190, 66 }, { 191, 65, 191, 65, 191, 65, 191, 65 },
+ { 192, 64, 192, 64, 192, 64, 192, 64 }, { 193, 63, 193, 63, 193, 63, 193, 63 },
+ { 194, 62, 194, 62, 194, 62, 194, 62 }, { 195, 61, 195, 61, 195, 61, 195, 61 },
+ { 196, 60, 196, 60, 196, 60, 196, 60 }, { 197, 59, 197, 59, 197, 59, 197, 59 },
+ { 198, 58, 198, 58, 198, 58, 198, 58 }, { 199, 57, 199, 57, 199, 57, 199, 57 },
+ { 200, 56, 200, 56, 200, 56, 200, 56 }, { 201, 55, 201, 55, 201, 55, 201, 55 },
+ { 202, 54, 202, 54, 202, 54, 202, 54 }, { 203, 53, 203, 53, 203, 53, 203, 53 },
+ { 204, 52, 204, 52, 204, 52, 204, 52 }, { 205, 51, 205, 51, 205, 51, 205, 51 },
+ { 206, 50, 206, 50, 206, 50, 206, 50 }, { 207, 49, 207, 49, 207, 49, 207, 49 },
+ { 208, 48, 208, 48, 208, 48, 208, 48 }, { 209, 47, 209, 47, 209, 47, 209, 47 },
+ { 210, 46, 210, 46, 210, 46, 210, 46 }, { 211, 45, 211, 45, 211, 45, 211, 45 },
+ { 212, 44, 212, 44, 212, 44, 212, 44 }, { 213, 43, 213, 43, 213, 43, 213, 43 },
+ { 214, 42, 214, 42, 214, 42, 214, 42 }, { 215, 41, 215, 41, 215, 41, 215, 41 },
+ { 216, 40, 216, 40, 216, 40, 216, 40 }, { 217, 39, 217, 39, 217, 39, 217, 39 },
+ { 218, 38, 218, 38, 218, 38, 218, 38 }, { 219, 37, 219, 37, 219, 37, 219, 37 },
+ { 220, 36, 220, 36, 220, 36, 220, 36 }, { 221, 35, 221, 35, 221, 35, 221, 35 },
+ { 222, 34, 222, 34, 222, 34, 222, 34 }, { 223, 33, 223, 33, 223, 33, 223, 33 },
+ { 224, 32, 224, 32, 224, 32, 224, 32 }, { 225, 31, 225, 31, 225, 31, 225, 31 },
+ { 226, 30, 226, 30, 226, 30, 226, 30 }, { 227, 29, 227, 29, 227, 29, 227, 29 },
+ { 228, 28, 228, 28, 228, 28, 228, 28 }, { 229, 27, 229, 27, 229, 27, 229, 27 },
+ { 230, 26, 230, 26, 230, 26, 230, 26 }, { 231, 25, 231, 25, 231, 25, 231, 25 },
+ { 232, 24, 232, 24, 232, 24, 232, 24 }, { 233, 23, 233, 23, 233, 23, 233, 23 },
+ { 234, 22, 234, 22, 234, 22, 234, 22 }, { 235, 21, 235, 21, 235, 21, 235, 21 },
+ { 236, 20, 236, 20, 236, 20, 236, 20 }, { 237, 19, 237, 19, 237, 19, 237, 19 },
+ { 238, 18, 238, 18, 238, 18, 238, 18 }, { 239, 17, 239, 17, 239, 17, 239, 17 },
+ { 240, 16, 240, 16, 240, 16, 240, 16 }, { 241, 15, 241, 15, 241, 15, 241, 15 },
+ { 242, 14, 242, 14, 242, 14, 242, 14 }, { 243, 13, 243, 13, 243, 13, 243, 13 },
+ { 244, 12, 244, 12, 244, 12, 244, 12 }, { 245, 11, 245, 11, 245, 11, 245, 11 },
+ { 246, 10, 246, 10, 246, 10, 246, 10 }, { 247, 9, 247, 9, 247, 9, 247, 9 },
+ { 248, 8, 248, 8, 248, 8, 248, 8 }, { 249, 7, 249, 7, 249, 7, 249, 7 },
+ { 250, 6, 250, 6, 250, 6, 250, 6 }, { 251, 5, 251, 5, 251, 5, 251, 5 },
+ { 252, 4, 252, 4, 252, 4, 252, 4 }, { 253, 3, 253, 3, 253, 3, 253, 3 },
+ { 254, 2, 254, 2, 254, 2, 254, 2 }, { 255, 1, 255, 1, 255, 1, 255, 1 }
+ }
+};
+
+
+/***************************************************************************
+ HIGHER LEVEL OPERATIONS
+***************************************************************************/
+
+void rgbaint_t::blend(const rgbaint_t& other, UINT8 factor)
+{
+ __m128i scale1 = _mm_set1_epi32(factor);
+ __m128i scale2 = _mm_sub_epi32(_mm_set1_epi32(0x100), scale1);
+
+ rgbaint_t scaled_other(other);
+ scaled_other.mul(scale2);
+
+ mul(scale1);
+ add(scaled_other);
+ sra_imm(8);
+}
+
+void rgbaint_t::scale_and_clamp(const rgbaint_t& scale)
+{
+ mul(scale);
+ sra_imm(8);
+ clamp_to_uint8();
+}
+
+void rgbaint_t::scale_imm_and_clamp(const INT32 scale)
+{
+ mul_imm(scale);
+ sra_imm(8);
+ clamp_to_uint8();
+}
+
+#endif // defined(__SSE2__) || defined(_MSC_VER)
diff --git a/src/devices/video/rgbsse.h b/src/devices/video/rgbsse.h
new file mode 100644
index 00000000000..0b729fabe46
--- /dev/null
+++ b/src/devices/video/rgbsse.h
@@ -0,0 +1,534 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb, Ryan Holtz
+/***************************************************************************
+
+ rgbsse.h
+
+ SSE optimized RGB utilities.
+
+ WARNING: This code assumes SSE2 or greater capability.
+
+***************************************************************************/
+
+#ifndef __RGBSSE__
+#define __RGBSSE__
+
+#include <emmintrin.h>
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class rgbaint_t
+{
+public:
+ inline rgbaint_t() { }
+ inline rgbaint_t(UINT32 rgba) { set(rgba); }
+ inline rgbaint_t(INT32 a, INT32 r, INT32 g, INT32 b) { set(a, r, g, b); }
+ inline rgbaint_t(rgb_t& rgb) { set(rgb); }
+ inline rgbaint_t(__m128i rgba) { m_value = rgba; }
+
+ inline void set(rgbaint_t& other) { m_value = other.m_value; }
+ inline void set(UINT32 rgba) { m_value = _mm_and_si128(_mm_set1_epi32(0xff), _mm_set_epi32(rgba >> 24, rgba >> 16, rgba >> 8, rgba)); }
+ inline void set(INT32 a, INT32 r, INT32 g, INT32 b) { m_value = _mm_set_epi32(a, r, g, b); }
+ inline void set(rgb_t& rgb) { m_value = _mm_unpacklo_epi16(_mm_unpacklo_epi8(_mm_cvtsi32_si128(rgb), _mm_setzero_si128()), _mm_setzero_si128()); }
+
+ inline rgb_t to_rgba()
+ {
+ return _mm_cvtsi128_si32(_mm_packus_epi16(_mm_packs_epi32(m_value, _mm_setzero_si128()), _mm_setzero_si128()));
+ }
+
+ inline rgb_t to_rgba_clamp()
+ {
+ return _mm_cvtsi128_si32(_mm_packus_epi16(_mm_packs_epi32(m_value, _mm_setzero_si128()), _mm_setzero_si128()));
+ }
+
+ inline void add(const rgbaint_t& color2)
+ {
+ m_value = _mm_add_epi32(m_value, color2.m_value);
+ }
+
+ inline void add_imm(const INT32 imm)
+ {
+ m_value = _mm_add_epi32(m_value, _mm_set1_epi32(imm));
+ }
+
+ inline void add_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_add_epi32(m_value, _mm_set_epi32(a, r, g, b));
+ }
+
+ inline void sub(const rgbaint_t& color2)
+ {
+ m_value = _mm_sub_epi32(m_value, color2.m_value);
+ }
+
+ inline void sub_imm(const INT32 imm)
+ {
+ m_value = _mm_sub_epi32(m_value, _mm_set1_epi32(imm));
+ }
+
+ inline void sub_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_sub_epi32(m_value, _mm_set_epi32(a, r, g, b));
+ }
+
+ inline void subr(rgbaint_t& color2)
+ {
+ m_value = _mm_sub_epi32(color2.m_value, m_value);
+ }
+
+ inline void subr_imm(const INT32 imm)
+ {
+ m_value = _mm_sub_epi32(_mm_set1_epi32(imm), m_value);
+ }
+
+ inline void subr_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_sub_epi32(_mm_set_epi32(a, r, g, b), m_value);
+ }
+
+ inline void set_a(const INT32 value)
+ {
+ m_value = _mm_or_si128(_mm_and_si128(m_value, alpha_mask()), _mm_set_epi32(value, 0, 0, 0));
+ }
+
+ inline void set_r(const INT32 value)
+ {
+ m_value = _mm_or_si128(_mm_and_si128(m_value, red_mask()), _mm_set_epi32(0, value, 0, 0));
+ }
+
+ inline void set_g(const INT32 value)
+ {
+ m_value = _mm_or_si128(_mm_and_si128(m_value, green_mask()), _mm_set_epi32(0, 0, value, 0));
+ }
+
+ inline void set_b(const INT32 value)
+ {
+ m_value = _mm_or_si128(_mm_and_si128(m_value, blue_mask()), _mm_set_epi32(0, 0, 0, value));
+ }
+
+ inline UINT8 get_a() const
+ {
+ return _mm_extract_epi16(m_value, 6);
+ }
+
+ inline UINT8 get_r() const
+ {
+ return _mm_extract_epi16(m_value, 4);
+ }
+
+ inline UINT8 get_g() const
+ {
+ return _mm_extract_epi16(m_value, 2);
+ }
+
+ inline UINT8 get_b() const
+ {
+ return _mm_extract_epi16(m_value, 0);
+ }
+
+ inline INT32 get_a32() const
+ {
+ return (_mm_extract_epi16(m_value, 7) << 16) | _mm_extract_epi16(m_value, 6);
+ }
+
+ inline INT32 get_r32() const
+ {
+ return (_mm_extract_epi16(m_value, 5) << 16) | _mm_extract_epi16(m_value, 4);
+ }
+
+ inline INT32 get_g32() const
+ {
+ return (_mm_extract_epi16(m_value, 3) << 16) | _mm_extract_epi16(m_value, 2);
+ }
+
+ inline INT32 get_b32() const
+ {
+ return (_mm_extract_epi16(m_value, 1) << 16) | _mm_extract_epi16(m_value, 0);
+ }
+
+ inline void mul(const rgbaint_t& color)
+ {
+ __m128i tmp1 = _mm_mul_epu32(m_value, color.m_value);
+ __m128i tmp2 = _mm_mul_epu32(_mm_srli_si128(m_value, 4), _mm_srli_si128(color.m_value, 4));
+ m_value = _mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1, _MM_SHUFFLE(0, 0, 2, 0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE(0, 0, 2, 0)));
+ }
+
+ inline void mul_imm(const INT32 imm)
+ {
+ __m128i immv = _mm_set1_epi32(imm);
+ __m128i tmp1 = _mm_mul_epu32(m_value, immv);
+ __m128i tmp2 = _mm_mul_epu32(_mm_srli_si128(m_value, 4), _mm_srli_si128(immv, 4));
+ m_value = _mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1, _MM_SHUFFLE(0, 0, 2, 0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE(0, 0, 2, 0)));
+ }
+
+ inline void mul_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ __m128i immv = _mm_set_epi32(a, r, g, b);
+ __m128i tmp1 = _mm_mul_epu32(m_value, immv);
+ __m128i tmp2 = _mm_mul_epu32(_mm_srli_si128(m_value, 4), _mm_srli_si128(immv, 4));
+ m_value = _mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1, _MM_SHUFFLE(0, 0, 2, 0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE(0, 0, 2, 0)));
+ }
+
+ inline void shl(const rgbaint_t& shift)
+ {
+ rgbaint_t areg(*this);
+ rgbaint_t rreg(*this);
+ rgbaint_t greg(*this);
+ rgbaint_t breg(*this);
+ rgbaint_t ashift(0, 0, 0, shift.get_a32());
+ rgbaint_t rshift(0, 0, 0, shift.get_r32());
+ rgbaint_t gshift(0, 0, 0, shift.get_g32());
+ rgbaint_t bshift(0, 0, 0, shift.get_b32());
+ areg.m_value = _mm_sll_epi32(areg.m_value, ashift.m_value);
+ rreg.m_value = _mm_sll_epi32(rreg.m_value, rshift.m_value);
+ greg.m_value = _mm_sll_epi32(greg.m_value, gshift.m_value);
+ breg.m_value = _mm_sll_epi32(breg.m_value, bshift.m_value);
+ set(areg.get_a32(), rreg.get_r32(), greg.get_g32(), breg.get_b32());
+ }
+
+ inline void shl_imm(const UINT8 shift)
+ {
+ m_value = _mm_slli_epi32(m_value, shift);
+ }
+
+ inline void shr(const rgbaint_t& shift)
+ {
+ rgbaint_t areg(*this);
+ rgbaint_t rreg(*this);
+ rgbaint_t greg(*this);
+ rgbaint_t breg(*this);
+ rgbaint_t ashift(0, 0, 0, shift.get_a32());
+ rgbaint_t rshift(0, 0, 0, shift.get_r32());
+ rgbaint_t gshift(0, 0, 0, shift.get_g32());
+ rgbaint_t bshift(0, 0, 0, shift.get_b32());
+ areg.m_value = _mm_srl_epi32(areg.m_value, ashift.m_value);
+ rreg.m_value = _mm_srl_epi32(rreg.m_value, rshift.m_value);
+ greg.m_value = _mm_srl_epi32(greg.m_value, gshift.m_value);
+ breg.m_value = _mm_srl_epi32(breg.m_value, bshift.m_value);
+ set(areg.get_a32(), rreg.get_r32(), greg.get_g32(), breg.get_b32());
+ }
+
+ inline void shr_imm(const UINT8 shift)
+ {
+ m_value = _mm_srli_epi32(m_value, shift);
+ }
+
+ inline void sra(const rgbaint_t& shift)
+ {
+ rgbaint_t areg(*this);
+ rgbaint_t rreg(*this);
+ rgbaint_t greg(*this);
+ rgbaint_t breg(*this);
+ rgbaint_t ashift(0, 0, 0, shift.get_a32());
+ rgbaint_t rshift(0, 0, 0, shift.get_r32());
+ rgbaint_t gshift(0, 0, 0, shift.get_g32());
+ rgbaint_t bshift(0, 0, 0, shift.get_b32());
+ areg.m_value = _mm_sra_epi32(areg.m_value, ashift.m_value);
+ rreg.m_value = _mm_sra_epi32(rreg.m_value, rshift.m_value);
+ greg.m_value = _mm_sra_epi32(greg.m_value, gshift.m_value);
+ breg.m_value = _mm_sra_epi32(breg.m_value, bshift.m_value);
+ set(areg.get_a32(), rreg.get_r32(), greg.get_g32(), breg.get_b32());
+ }
+
+ inline void sra_imm(const UINT8 shift)
+ {
+ m_value = _mm_srai_epi32(m_value, shift);
+ }
+
+ inline void or_reg(const rgbaint_t& color2)
+ {
+ m_value = _mm_or_si128(m_value, color2.m_value);
+ }
+
+ inline void or_imm(const INT32 value)
+ {
+ m_value = _mm_or_si128(m_value, _mm_set1_epi32(value));
+ }
+
+ inline void or_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_or_si128(m_value, _mm_set_epi32(a, r, g, b));
+ }
+
+ inline void and_reg(const rgbaint_t& color)
+ {
+ m_value = _mm_and_si128(m_value, color.m_value);
+ }
+
+ inline void andnot_reg(const rgbaint_t& color)
+ {
+ m_value = _mm_andnot_si128(color.m_value, m_value);
+ }
+
+ inline void and_imm(const INT32 value)
+ {
+ m_value = _mm_and_si128(m_value, _mm_set1_epi32(value));
+ }
+
+ inline void and_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_and_si128(m_value, _mm_set_epi32(a, r, g, b));
+ }
+
+ inline void xor_reg(const rgbaint_t& color2)
+ {
+ m_value = _mm_xor_si128(m_value, color2.m_value);
+ }
+
+ inline void xor_imm(const INT32 value)
+ {
+ m_value = _mm_xor_si128(m_value, _mm_set1_epi32(value));
+ }
+
+ inline void xor_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_xor_si128(m_value, _mm_set_epi32(a, r, g, b));
+ }
+
+ inline void clamp_and_clear(const UINT32 sign)
+ {
+ __m128i vsign = _mm_set1_epi32(sign);
+ m_value = _mm_and_si128(m_value, _mm_cmpeq_epi32(_mm_and_si128(m_value, vsign), _mm_setzero_si128()));
+ vsign = _mm_srai_epi32(vsign, 1);
+ vsign = _mm_xor_si128(vsign, _mm_set1_epi32(0xffffffff));
+ __m128i mask = _mm_cmpgt_epi32(m_value, vsign);
+ m_value = _mm_or_si128(_mm_and_si128(vsign, mask), _mm_and_si128(m_value, _mm_xor_si128(mask, _mm_set1_epi32(0xffffffff))));
+ }
+
+ inline void clamp_to_uint8()
+ {
+ m_value = _mm_packs_epi32(m_value, _mm_setzero_si128());
+ m_value = _mm_packus_epi16(m_value, _mm_setzero_si128());
+ m_value = _mm_unpacklo_epi8(m_value, _mm_setzero_si128());
+ m_value = _mm_unpacklo_epi16(m_value, _mm_setzero_si128());
+ }
+
+ inline void sign_extend(const UINT32 compare, const UINT32 sign)
+ {
+ __m128i compare_vec = _mm_set1_epi32(compare);
+ __m128i compare_mask = _mm_cmpeq_epi32(_mm_and_si128(m_value, compare_vec), compare_vec);
+ __m128i compared = _mm_and_si128(_mm_set1_epi32(sign), compare_mask);
+ m_value = _mm_or_si128(m_value, compared);
+ }
+
+ inline void min(const INT32 value)
+ {
+ __m128i val = _mm_set1_epi32(value);
+ __m128i is_greater_than = _mm_cmpgt_epi32(m_value, val);
+
+ __m128i val_to_set = _mm_and_si128(val, is_greater_than);
+ __m128i keep_mask = _mm_xor_si128(is_greater_than, _mm_set1_epi32(0xffffffff));
+
+ m_value = _mm_and_si128(m_value, keep_mask);
+ m_value = _mm_or_si128(val_to_set, m_value);
+ }
+
+ inline void max(const INT32 value)
+ {
+ __m128i val = _mm_set1_epi32(value);
+ __m128i is_less_than = _mm_cmplt_epi32(m_value, val);
+
+ __m128i val_to_set = _mm_and_si128(val, is_less_than);
+ __m128i keep_mask = _mm_xor_si128(is_less_than, _mm_set1_epi32(0xffffffff));
+
+ m_value = _mm_and_si128(m_value, keep_mask);
+ m_value = _mm_or_si128(val_to_set, m_value);
+ }
+
+ void blend(const rgbaint_t& other, UINT8 factor);
+
+ void scale_and_clamp(const rgbaint_t& scale);
+ void scale_imm_and_clamp(const INT32 scale);
+
+ inline void scale_imm_add_and_clamp(const INT32 scale, const rgbaint_t& other)
+ {
+ mul_imm(scale);
+ sra_imm(8);
+ add(other);
+ clamp_to_uint8();
+ }
+
+ inline void scale_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other)
+ {
+ mul(scale);
+ sra_imm(8);
+ add(other);
+ clamp_to_uint8();
+ }
+
+ inline void scale2_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other, const rgbaint_t& scale2)
+ {
+ rgbaint_t color2(other);
+ color2.mul(scale2);
+
+ mul(scale);
+ add(color2);
+ sra_imm(8);
+ clamp_to_uint8();
+ }
+
+ inline void cmpeq(const rgbaint_t& value)
+ {
+ m_value = _mm_cmpeq_epi32(m_value, value.m_value);
+ }
+
+ inline void cmpeq_imm(const INT32 value)
+ {
+ m_value = _mm_cmpeq_epi32(m_value, _mm_set1_epi32(value));
+ }
+
+ inline void cmpeq_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_cmpeq_epi32(m_value, _mm_set_epi32(a, r, g, b));
+ }
+
+ inline void cmpgt(const rgbaint_t& value)
+ {
+ m_value = _mm_cmpgt_epi32(m_value, value.m_value);
+ }
+
+ inline void cmpgt_imm(const INT32 value)
+ {
+ m_value = _mm_cmpgt_epi32(m_value, _mm_set1_epi32(value));
+ }
+
+ inline void cmpgt_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_cmpgt_epi32(m_value, _mm_set_epi32(a, r, g, b));
+ }
+
+ inline void cmplt(const rgbaint_t& value)
+ {
+ m_value = _mm_cmplt_epi32(m_value, value.m_value);
+ }
+
+ inline void cmplt_imm(const INT32 value)
+ {
+ m_value = _mm_cmplt_epi32(m_value, _mm_set1_epi32(value));
+ }
+
+ inline void cmplt_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ m_value = _mm_cmplt_epi32(m_value, _mm_set_epi32(a, r, g, b));
+ }
+
+ inline rgbaint_t operator=(const rgbaint_t& other)
+ {
+ m_value = other.m_value;
+ return *this;
+ }
+
+ inline rgbaint_t& operator+=(const rgbaint_t& other)
+ {
+ m_value = _mm_add_epi32(m_value, other.m_value);
+ return *this;
+ }
+
+ inline rgbaint_t& operator+=(const INT32 other)
+ {
+ m_value = _mm_add_epi32(m_value, _mm_set1_epi32(other));
+ return *this;
+ }
+
+ inline rgbaint_t& operator-=(const rgbaint_t& other)
+ {
+ m_value = _mm_sub_epi32(m_value, other.m_value);
+ return *this;
+ }
+
+ inline rgbaint_t& operator*=(const rgbaint_t& other)
+ {
+ m_value = _mm_unpacklo_epi32(_mm_shuffle_epi32(_mm_mul_epu32(m_value, other.m_value), _MM_SHUFFLE(0, 0, 2, 0)), _mm_shuffle_epi32(_mm_mul_epu32(_mm_srli_si128(m_value, 4), _mm_srli_si128(other.m_value, 4)), _MM_SHUFFLE(0, 0, 2, 0)));
+ return *this;
+ }
+
+ inline rgbaint_t& operator*=(const INT32 other)
+ {
+ const __m128i immv = _mm_set1_epi32(other);
+ m_value = _mm_unpacklo_epi32(_mm_shuffle_epi32(_mm_mul_epu32(m_value, immv), _MM_SHUFFLE(0, 0, 2, 0)), _mm_shuffle_epi32(_mm_mul_epu32(_mm_srli_si128(m_value, 4), _mm_srli_si128(immv, 4)), _MM_SHUFFLE(0, 0, 2, 0)));
+ return *this;
+ }
+
+ inline rgbaint_t& operator>>=(const INT32 shift)
+ {
+ m_value = _mm_srai_epi32(m_value, shift);
+ return *this;
+ }
+
+ inline void merge_alpha(const rgbaint_t& alpha)
+ {
+ m_value = _mm_insert_epi16(m_value, _mm_extract_epi16(alpha.m_value, 7), 7);
+ m_value = _mm_insert_epi16(m_value, _mm_extract_epi16(alpha.m_value, 6), 6);
+ }
+
+ static UINT32 bilinear_filter(UINT32 rgb00, UINT32 rgb01, UINT32 rgb10, UINT32 rgb11, UINT8 u, UINT8 v)
+ {
+ __m128i color00 = _mm_cvtsi32_si128(rgb00);
+ __m128i color01 = _mm_cvtsi32_si128(rgb01);
+ __m128i color10 = _mm_cvtsi32_si128(rgb10);
+ __m128i color11 = _mm_cvtsi32_si128(rgb11);
+
+ /* interleave color01 and color00 at the byte level */
+ color01 = _mm_unpacklo_epi8(color01, color00);
+ color11 = _mm_unpacklo_epi8(color11, color10);
+ color01 = _mm_unpacklo_epi8(color01, _mm_setzero_si128());
+ color11 = _mm_unpacklo_epi8(color11, _mm_setzero_si128());
+ color01 = _mm_madd_epi16(color01, scale_factor(u));
+ color11 = _mm_madd_epi16(color11, scale_factor(u));
+ color01 = _mm_slli_epi32(color01, 15);
+ color11 = _mm_srli_epi32(color11, 1);
+ color01 = _mm_max_epi16(color01, color11);
+ color01 = _mm_madd_epi16(color01, scale_factor(v));
+ color01 = _mm_srli_epi32(color01, 15);
+ color01 = _mm_packs_epi32(color01, _mm_setzero_si128());
+ color01 = _mm_packus_epi16(color01, _mm_setzero_si128());
+ return _mm_cvtsi128_si32(color01);
+ }
+
+ inline void bilinear_filter_rgbaint(UINT32 rgb00, UINT32 rgb01, UINT32 rgb10, UINT32 rgb11, UINT8 u, UINT8 v)
+ {
+ __m128i color00 = _mm_cvtsi32_si128(rgb00);
+ __m128i color01 = _mm_cvtsi32_si128(rgb01);
+ __m128i color10 = _mm_cvtsi32_si128(rgb10);
+ __m128i color11 = _mm_cvtsi32_si128(rgb11);
+
+ /* interleave color01 and color00 at the byte level */
+ color01 = _mm_unpacklo_epi8(color01, color00);
+ color11 = _mm_unpacklo_epi8(color11, color10);
+ color01 = _mm_unpacklo_epi8(color01, _mm_setzero_si128());
+ color11 = _mm_unpacklo_epi8(color11, _mm_setzero_si128());
+ color01 = _mm_madd_epi16(color01, scale_factor(u));
+ color11 = _mm_madd_epi16(color11, scale_factor(u));
+ color01 = _mm_slli_epi32(color01, 15);
+ color11 = _mm_srli_epi32(color11, 1);
+ color01 = _mm_max_epi16(color01, color11);
+ color01 = _mm_madd_epi16(color01, scale_factor(v));
+ m_value = _mm_srli_epi32(color01, 15);
+ }
+
+protected:
+ struct _statics
+ {
+ __m128 dummy_for_alignment;
+ UINT16 alpha_mask[8];
+ UINT16 red_mask[8];
+ UINT16 green_mask[8];
+ UINT16 blue_mask[8];
+ INT16 scale_table[256][8];
+ };
+
+ static inline __m128i alpha_mask() { return *(__m128i *)&statics.alpha_mask[0]; }
+ static inline __m128i red_mask() { return *(__m128i *)&statics.red_mask[0]; }
+ static inline __m128i green_mask() { return *(__m128i *)&statics.green_mask[0]; }
+ static inline __m128i blue_mask() { return *(__m128i *)&statics.blue_mask[0]; }
+ static inline __m128i scale_factor(UINT8 index) { return *(__m128i *)&statics.scale_table[index][0]; }
+
+ __m128i m_value;
+
+ static const _statics statics;
+
+};
+
+#endif /* __RGBSSE__ */
diff --git a/src/devices/video/rgbutil.h b/src/devices/video/rgbutil.h
new file mode 100644
index 00000000000..15def4da2c0
--- /dev/null
+++ b/src/devices/video/rgbutil.h
@@ -0,0 +1,24 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ rgbutil.h
+
+ Utility definitions for RGB manipulation. Allows RGB handling to be
+ performed in an abstracted fashion and optimized with SIMD.
+
+***************************************************************************/
+
+#ifndef __RGBUTIL__
+#define __RGBUTIL__
+
+/* use SSE on 64-bit implementations, where it can be assumed */
+#if (!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || defined(_MSC_VER)) && defined(PTR64)
+#include "rgbsse.h"
+#elif defined(__ALTIVEC__)
+#include "rgbvmx.h"
+#else
+#include "rgbgen.h"
+#endif
+
+#endif /* __RGBUTIL__ */
diff --git a/src/devices/video/rgbvmx.c b/src/devices/video/rgbvmx.c
new file mode 100644
index 00000000000..a35572368a7
--- /dev/null
+++ b/src/devices/video/rgbvmx.c
@@ -0,0 +1,220 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb, Ryan Holtz
+/***************************************************************************
+
+ rgbsse.c
+
+ VMX/Altivec optimised RGB utilities.
+
+***************************************************************************/
+
+#if defined(__ALTIVEC__)
+
+#include "emu.h"
+#include "rgbutil.h"
+
+/***************************************************************************
+ TABLES
+***************************************************************************/
+
+const rgbaint_t::VECU8 rgbaint_t::alpha_perm = { 16, 17, 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+const rgbaint_t::VECU8 rgbaint_t::red_perm = { 0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 12, 13, 14, 15 };
+const rgbaint_t::VECU8 rgbaint_t::green_perm = { 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15 };
+const rgbaint_t::VECU8 rgbaint_t::blue_perm = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19 };
+const rgbaint_t::VECS16 rgbaint_t::scale_table[256] = {
+ { 0, 256, 0, 256, 0, 256, 0, 256 }, { 1, 255, 1, 255, 1, 255, 1, 255 },
+ { 2, 254, 2, 254, 2, 254, 2, 254 }, { 3, 253, 3, 253, 3, 253, 3, 253 },
+ { 4, 252, 4, 252, 4, 252, 4, 252 }, { 5, 251, 5, 251, 5, 251, 5, 251 },
+ { 6, 250, 6, 250, 6, 250, 6, 250 }, { 7, 249, 7, 249, 7, 249, 7, 249 },
+ { 8, 248, 8, 248, 8, 248, 8, 248 }, { 9, 247, 9, 247, 9, 247, 9, 247 },
+ { 10, 246, 10, 246, 10, 246, 10, 246 }, { 11, 245, 11, 245, 11, 245, 11, 245 },
+ { 12, 244, 12, 244, 12, 244, 12, 244 }, { 13, 243, 13, 243, 13, 243, 13, 243 },
+ { 14, 242, 14, 242, 14, 242, 14, 242 }, { 15, 241, 15, 241, 15, 241, 15, 241 },
+ { 16, 240, 16, 240, 16, 240, 16, 240 }, { 17, 239, 17, 239, 17, 239, 17, 239 },
+ { 18, 238, 18, 238, 18, 238, 18, 238 }, { 19, 237, 19, 237, 19, 237, 19, 237 },
+ { 20, 236, 20, 236, 20, 236, 20, 236 }, { 21, 235, 21, 235, 21, 235, 21, 235 },
+ { 22, 234, 22, 234, 22, 234, 22, 234 }, { 23, 233, 23, 233, 23, 233, 23, 233 },
+ { 24, 232, 24, 232, 24, 232, 24, 232 }, { 25, 231, 25, 231, 25, 231, 25, 231 },
+ { 26, 230, 26, 230, 26, 230, 26, 230 }, { 27, 229, 27, 229, 27, 229, 27, 229 },
+ { 28, 228, 28, 228, 28, 228, 28, 228 }, { 29, 227, 29, 227, 29, 227, 29, 227 },
+ { 30, 226, 30, 226, 30, 226, 30, 226 }, { 31, 225, 31, 225, 31, 225, 31, 225 },
+ { 32, 224, 32, 224, 32, 224, 32, 224 }, { 33, 223, 33, 223, 33, 223, 33, 223 },
+ { 34, 222, 34, 222, 34, 222, 34, 222 }, { 35, 221, 35, 221, 35, 221, 35, 221 },
+ { 36, 220, 36, 220, 36, 220, 36, 220 }, { 37, 219, 37, 219, 37, 219, 37, 219 },
+ { 38, 218, 38, 218, 38, 218, 38, 218 }, { 39, 217, 39, 217, 39, 217, 39, 217 },
+ { 40, 216, 40, 216, 40, 216, 40, 216 }, { 41, 215, 41, 215, 41, 215, 41, 215 },
+ { 42, 214, 42, 214, 42, 214, 42, 214 }, { 43, 213, 43, 213, 43, 213, 43, 213 },
+ { 44, 212, 44, 212, 44, 212, 44, 212 }, { 45, 211, 45, 211, 45, 211, 45, 211 },
+ { 46, 210, 46, 210, 46, 210, 46, 210 }, { 47, 209, 47, 209, 47, 209, 47, 209 },
+ { 48, 208, 48, 208, 48, 208, 48, 208 }, { 49, 207, 49, 207, 49, 207, 49, 207 },
+ { 50, 206, 50, 206, 50, 206, 50, 206 }, { 51, 205, 51, 205, 51, 205, 51, 205 },
+ { 52, 204, 52, 204, 52, 204, 52, 204 }, { 53, 203, 53, 203, 53, 203, 53, 203 },
+ { 54, 202, 54, 202, 54, 202, 54, 202 }, { 55, 201, 55, 201, 55, 201, 55, 201 },
+ { 56, 200, 56, 200, 56, 200, 56, 200 }, { 57, 199, 57, 199, 57, 199, 57, 199 },
+ { 58, 198, 58, 198, 58, 198, 58, 198 }, { 59, 197, 59, 197, 59, 197, 59, 197 },
+ { 60, 196, 60, 196, 60, 196, 60, 196 }, { 61, 195, 61, 195, 61, 195, 61, 195 },
+ { 62, 194, 62, 194, 62, 194, 62, 194 }, { 63, 193, 63, 193, 63, 193, 63, 193 },
+ { 64, 192, 64, 192, 64, 192, 64, 192 }, { 65, 191, 65, 191, 65, 191, 65, 191 },
+ { 66, 190, 66, 190, 66, 190, 66, 190 }, { 67, 189, 67, 189, 67, 189, 67, 189 },
+ { 68, 188, 68, 188, 68, 188, 68, 188 }, { 69, 187, 69, 187, 69, 187, 69, 187 },
+ { 70, 186, 70, 186, 70, 186, 70, 186 }, { 71, 185, 71, 185, 71, 185, 71, 185 },
+ { 72, 184, 72, 184, 72, 184, 72, 184 }, { 73, 183, 73, 183, 73, 183, 73, 183 },
+ { 74, 182, 74, 182, 74, 182, 74, 182 }, { 75, 181, 75, 181, 75, 181, 75, 181 },
+ { 76, 180, 76, 180, 76, 180, 76, 180 }, { 77, 179, 77, 179, 77, 179, 77, 179 },
+ { 78, 178, 78, 178, 78, 178, 78, 178 }, { 79, 177, 79, 177, 79, 177, 79, 177 },
+ { 80, 176, 80, 176, 80, 176, 80, 176 }, { 81, 175, 81, 175, 81, 175, 81, 175 },
+ { 82, 174, 82, 174, 82, 174, 82, 174 }, { 83, 173, 83, 173, 83, 173, 83, 173 },
+ { 84, 172, 84, 172, 84, 172, 84, 172 }, { 85, 171, 85, 171, 85, 171, 85, 171 },
+ { 86, 170, 86, 170, 86, 170, 86, 170 }, { 87, 169, 87, 169, 87, 169, 87, 169 },
+ { 88, 168, 88, 168, 88, 168, 88, 168 }, { 89, 167, 89, 167, 89, 167, 89, 167 },
+ { 90, 166, 90, 166, 90, 166, 90, 166 }, { 91, 165, 91, 165, 91, 165, 91, 165 },
+ { 92, 164, 92, 164, 92, 164, 92, 164 }, { 93, 163, 93, 163, 93, 163, 93, 163 },
+ { 94, 162, 94, 162, 94, 162, 94, 162 }, { 95, 161, 95, 161, 95, 161, 95, 161 },
+ { 96, 160, 96, 160, 96, 160, 96, 160 }, { 97, 159, 97, 159, 97, 159, 97, 159 },
+ { 98, 158, 98, 158, 98, 158, 98, 158 }, { 99, 157, 99, 157, 99, 157, 99, 157 },
+ { 100, 156, 100, 156, 100, 156, 100, 156 }, { 101, 155, 101, 155, 101, 155, 101, 155 },
+ { 102, 154, 102, 154, 102, 154, 102, 154 }, { 103, 153, 103, 153, 103, 153, 103, 153 },
+ { 104, 152, 104, 152, 104, 152, 104, 152 }, { 105, 151, 105, 151, 105, 151, 105, 151 },
+ { 106, 150, 106, 150, 106, 150, 106, 150 }, { 107, 149, 107, 149, 107, 149, 107, 149 },
+ { 108, 148, 108, 148, 108, 148, 108, 148 }, { 109, 147, 109, 147, 109, 147, 109, 147 },
+ { 110, 146, 110, 146, 110, 146, 110, 146 }, { 111, 145, 111, 145, 111, 145, 111, 145 },
+ { 112, 144, 112, 144, 112, 144, 112, 144 }, { 113, 143, 113, 143, 113, 143, 113, 143 },
+ { 114, 142, 114, 142, 114, 142, 114, 142 }, { 115, 141, 115, 141, 115, 141, 115, 141 },
+ { 116, 140, 116, 140, 116, 140, 116, 140 }, { 117, 139, 117, 139, 117, 139, 117, 139 },
+ { 118, 138, 118, 138, 118, 138, 118, 138 }, { 119, 137, 119, 137, 119, 137, 119, 137 },
+ { 120, 136, 120, 136, 120, 136, 120, 136 }, { 121, 135, 121, 135, 121, 135, 121, 135 },
+ { 122, 134, 122, 134, 122, 134, 122, 134 }, { 123, 133, 123, 133, 123, 133, 123, 133 },
+ { 124, 132, 124, 132, 124, 132, 124, 132 }, { 125, 131, 125, 131, 125, 131, 125, 131 },
+ { 126, 130, 126, 130, 126, 130, 126, 130 }, { 127, 129, 127, 129, 127, 129, 127, 129 },
+ { 128, 128, 128, 128, 128, 128, 128, 128 }, { 129, 127, 129, 127, 129, 127, 129, 127 },
+ { 130, 126, 130, 126, 130, 126, 130, 126 }, { 131, 125, 131, 125, 131, 125, 131, 125 },
+ { 132, 124, 132, 124, 132, 124, 132, 124 }, { 133, 123, 133, 123, 133, 123, 133, 123 },
+ { 134, 122, 134, 122, 134, 122, 134, 122 }, { 135, 121, 135, 121, 135, 121, 135, 121 },
+ { 136, 120, 136, 120, 136, 120, 136, 120 }, { 137, 119, 137, 119, 137, 119, 137, 119 },
+ { 138, 118, 138, 118, 138, 118, 138, 118 }, { 139, 117, 139, 117, 139, 117, 139, 117 },
+ { 140, 116, 140, 116, 140, 116, 140, 116 }, { 141, 115, 141, 115, 141, 115, 141, 115 },
+ { 142, 114, 142, 114, 142, 114, 142, 114 }, { 143, 113, 143, 113, 143, 113, 143, 113 },
+ { 144, 112, 144, 112, 144, 112, 144, 112 }, { 145, 111, 145, 111, 145, 111, 145, 111 },
+ { 146, 110, 146, 110, 146, 110, 146, 110 }, { 147, 109, 147, 109, 147, 109, 147, 109 },
+ { 148, 108, 148, 108, 148, 108, 148, 108 }, { 149, 107, 149, 107, 149, 107, 149, 107 },
+ { 150, 106, 150, 106, 150, 106, 150, 106 }, { 151, 105, 151, 105, 151, 105, 151, 105 },
+ { 152, 104, 152, 104, 152, 104, 152, 104 }, { 153, 103, 153, 103, 153, 103, 153, 103 },
+ { 154, 102, 154, 102, 154, 102, 154, 102 }, { 155, 101, 155, 101, 155, 101, 155, 101 },
+ { 156, 100, 156, 100, 156, 100, 156, 100 }, { 157, 99, 157, 99, 157, 99, 157, 99 },
+ { 158, 98, 158, 98, 158, 98, 158, 98 }, { 159, 97, 159, 97, 159, 97, 159, 97 },
+ { 160, 96, 160, 96, 160, 96, 160, 96 }, { 161, 95, 161, 95, 161, 95, 161, 95 },
+ { 162, 94, 162, 94, 162, 94, 162, 94 }, { 163, 93, 163, 93, 163, 93, 163, 93 },
+ { 164, 92, 164, 92, 164, 92, 164, 92 }, { 165, 91, 165, 91, 165, 91, 165, 91 },
+ { 166, 90, 166, 90, 166, 90, 166, 90 }, { 167, 89, 167, 89, 167, 89, 167, 89 },
+ { 168, 88, 168, 88, 168, 88, 168, 88 }, { 169, 87, 169, 87, 169, 87, 169, 87 },
+ { 170, 86, 170, 86, 170, 86, 170, 86 }, { 171, 85, 171, 85, 171, 85, 171, 85 },
+ { 172, 84, 172, 84, 172, 84, 172, 84 }, { 173, 83, 173, 83, 173, 83, 173, 83 },
+ { 174, 82, 174, 82, 174, 82, 174, 82 }, { 175, 81, 175, 81, 175, 81, 175, 81 },
+ { 176, 80, 176, 80, 176, 80, 176, 80 }, { 177, 79, 177, 79, 177, 79, 177, 79 },
+ { 178, 78, 178, 78, 178, 78, 178, 78 }, { 179, 77, 179, 77, 179, 77, 179, 77 },
+ { 180, 76, 180, 76, 180, 76, 180, 76 }, { 181, 75, 181, 75, 181, 75, 181, 75 },
+ { 182, 74, 182, 74, 182, 74, 182, 74 }, { 183, 73, 183, 73, 183, 73, 183, 73 },
+ { 184, 72, 184, 72, 184, 72, 184, 72 }, { 185, 71, 185, 71, 185, 71, 185, 71 },
+ { 186, 70, 186, 70, 186, 70, 186, 70 }, { 187, 69, 187, 69, 187, 69, 187, 69 },
+ { 188, 68, 188, 68, 188, 68, 188, 68 }, { 189, 67, 189, 67, 189, 67, 189, 67 },
+ { 190, 66, 190, 66, 190, 66, 190, 66 }, { 191, 65, 191, 65, 191, 65, 191, 65 },
+ { 192, 64, 192, 64, 192, 64, 192, 64 }, { 193, 63, 193, 63, 193, 63, 193, 63 },
+ { 194, 62, 194, 62, 194, 62, 194, 62 }, { 195, 61, 195, 61, 195, 61, 195, 61 },
+ { 196, 60, 196, 60, 196, 60, 196, 60 }, { 197, 59, 197, 59, 197, 59, 197, 59 },
+ { 198, 58, 198, 58, 198, 58, 198, 58 }, { 199, 57, 199, 57, 199, 57, 199, 57 },
+ { 200, 56, 200, 56, 200, 56, 200, 56 }, { 201, 55, 201, 55, 201, 55, 201, 55 },
+ { 202, 54, 202, 54, 202, 54, 202, 54 }, { 203, 53, 203, 53, 203, 53, 203, 53 },
+ { 204, 52, 204, 52, 204, 52, 204, 52 }, { 205, 51, 205, 51, 205, 51, 205, 51 },
+ { 206, 50, 206, 50, 206, 50, 206, 50 }, { 207, 49, 207, 49, 207, 49, 207, 49 },
+ { 208, 48, 208, 48, 208, 48, 208, 48 }, { 209, 47, 209, 47, 209, 47, 209, 47 },
+ { 210, 46, 210, 46, 210, 46, 210, 46 }, { 211, 45, 211, 45, 211, 45, 211, 45 },
+ { 212, 44, 212, 44, 212, 44, 212, 44 }, { 213, 43, 213, 43, 213, 43, 213, 43 },
+ { 214, 42, 214, 42, 214, 42, 214, 42 }, { 215, 41, 215, 41, 215, 41, 215, 41 },
+ { 216, 40, 216, 40, 216, 40, 216, 40 }, { 217, 39, 217, 39, 217, 39, 217, 39 },
+ { 218, 38, 218, 38, 218, 38, 218, 38 }, { 219, 37, 219, 37, 219, 37, 219, 37 },
+ { 220, 36, 220, 36, 220, 36, 220, 36 }, { 221, 35, 221, 35, 221, 35, 221, 35 },
+ { 222, 34, 222, 34, 222, 34, 222, 34 }, { 223, 33, 223, 33, 223, 33, 223, 33 },
+ { 224, 32, 224, 32, 224, 32, 224, 32 }, { 225, 31, 225, 31, 225, 31, 225, 31 },
+ { 226, 30, 226, 30, 226, 30, 226, 30 }, { 227, 29, 227, 29, 227, 29, 227, 29 },
+ { 228, 28, 228, 28, 228, 28, 228, 28 }, { 229, 27, 229, 27, 229, 27, 229, 27 },
+ { 230, 26, 230, 26, 230, 26, 230, 26 }, { 231, 25, 231, 25, 231, 25, 231, 25 },
+ { 232, 24, 232, 24, 232, 24, 232, 24 }, { 233, 23, 233, 23, 233, 23, 233, 23 },
+ { 234, 22, 234, 22, 234, 22, 234, 22 }, { 235, 21, 235, 21, 235, 21, 235, 21 },
+ { 236, 20, 236, 20, 236, 20, 236, 20 }, { 237, 19, 237, 19, 237, 19, 237, 19 },
+ { 238, 18, 238, 18, 238, 18, 238, 18 }, { 239, 17, 239, 17, 239, 17, 239, 17 },
+ { 240, 16, 240, 16, 240, 16, 240, 16 }, { 241, 15, 241, 15, 241, 15, 241, 15 },
+ { 242, 14, 242, 14, 242, 14, 242, 14 }, { 243, 13, 243, 13, 243, 13, 243, 13 },
+ { 244, 12, 244, 12, 244, 12, 244, 12 }, { 245, 11, 245, 11, 245, 11, 245, 11 },
+ { 246, 10, 246, 10, 246, 10, 246, 10 }, { 247, 9, 247, 9, 247, 9, 247, 9 },
+ { 248, 8, 248, 8, 248, 8, 248, 8 }, { 249, 7, 249, 7, 249, 7, 249, 7 },
+ { 250, 6, 250, 6, 250, 6, 250, 6 }, { 251, 5, 251, 5, 251, 5, 251, 5 },
+ { 252, 4, 252, 4, 252, 4, 252, 4 }, { 253, 3, 253, 3, 253, 3, 253, 3 },
+ { 254, 2, 254, 2, 254, 2, 254, 2 }, { 255, 1, 255, 1, 255, 1, 255, 1 }
+};
+
+
+/***************************************************************************
+ HIGHER LEVEL OPERATIONS
+***************************************************************************/
+
+void rgbaint_t::blend(const rgbaint_t& other, UINT8 factor)
+{
+ const VECU32 shift = vec_splat_u32(-16);
+ const VECS32 scale1 = { factor, factor, factor, factor };
+ const VECS32 scale2 = { 0x100 - factor, 0x100 - factor, 0x100 - factor, 0x100 - factor, };
+
+ VECU32 temp = vec_msum((VECU16)m_value, (VECU16)vec_rl(scale1, shift), vec_splat_u32(0));
+ temp = vec_msum((VECU16)other.m_value, (VECU16)vec_rl(scale2, shift), temp);
+
+ m_value = vec_msum((VECU16)m_value, (VECU16)scale1, vec_mulo((VECU16)other.m_value, (VECU16)scale2));
+ m_value = vec_add(vec_sl(temp, shift), (VECU32)m_value);
+ sra_imm(8);
+}
+
+void rgbaint_t::scale_and_clamp(const rgbaint_t& scale)
+{
+ mul(scale);
+ sra_imm(8);
+ max(0);
+ min(255);
+}
+
+void rgbaint_t::scale_imm_and_clamp(const INT32 scale)
+{
+ mul_imm(scale);
+ sra_imm(8);
+ max(0);
+ min(255);
+}
+
+void rgbaint_t::scale2_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other, const rgbaint_t& scale2)
+{
+ rgbaint_t color2(other);
+ color2.mul(scale2);
+
+ mul(scale);
+ add(color2);
+ sra_imm(8);
+ max(0);
+ min(255);
+}
+
+void rgbaint_t::scale_imm_add_and_clamp(const INT32 scale, const rgbaint_t& other)
+{
+ mul_imm(scale);
+ sra_imm(8);
+ add(other);
+ max(0);
+ min(255);
+}
+
+void rgbaint_t::scale_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other)
+{
+ mul(scale);
+ sra_imm(8);
+ add(other);
+ max(0);
+ min(255);
+}
+
+#endif // defined(__ALTIVEC__)
diff --git a/src/devices/video/rgbvmx.h b/src/devices/video/rgbvmx.h
new file mode 100644
index 00000000000..10c235eaa5a
--- /dev/null
+++ b/src/devices/video/rgbvmx.h
@@ -0,0 +1,509 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb, Ryan Holtz
+/***************************************************************************
+
+ rgbvmx.h
+
+ VMX/Altivec optimised RGB utilities.
+
+***************************************************************************/
+
+#ifndef __RGBVMX__
+#define __RGBVMX__
+
+#include <altivec.h>
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class rgbaint_t
+{
+public:
+ inline rgbaint_t() { }
+ inline rgbaint_t(UINT32 rgba) { set(rgba); }
+ inline rgbaint_t(INT32 a, INT32 r, INT32 g, INT32 b) { set(a, r, g, b); }
+ inline rgbaint_t(rgb_t& rgb) { set(rgb); }
+
+ inline void set(rgbaint_t& other) { m_value = other.m_value; }
+
+ inline void set(UINT32 rgba)
+ {
+ const VECU32 zero = { 0, 0, 0, 0 };
+ const VECS8 temp = vec_perm(vec_lde(0, &rgba), zero, vec_lvsl(0, &rgba));
+ m_value = vec_mergeh((VECS16)zero, (VECS16)vec_mergeh((VECS8)zero, temp));
+ }
+
+ inline void set(INT32 a, INT32 r, INT32 g, INT32 b)
+ {
+ VECS32 result = { a, r, g, b };
+ m_value = result;
+ }
+
+ inline void set(rgb_t& rgb)
+ {
+ const VECU32 zero = { 0, 0, 0, 0 };
+ const VECS8 temp = vec_perm(vec_lde(0, rgb.ptr()), zero, vec_lvsl(0, rgb.ptr()));
+ m_value = vec_mergeh((VECS16)zero, (VECS16)vec_mergeh((VECS8)zero, temp));
+ }
+
+ inline rgb_t to_rgba()
+ {
+ VECU32 temp = vec_pack(m_value, m_value);
+ temp = vec_pack((VECU16)temp, (VECU16)temp);
+ UINT32 result;
+ vec_ste(temp, 0, &result);
+ return result;
+ }
+
+ inline rgb_t to_rgba_clamp()
+ {
+ VECU32 temp = vec_packs(m_value, m_value);
+ temp = vec_packsu((VECU16)temp, (VECU16)temp);
+ UINT32 result;
+ vec_ste(temp, 0, &result);
+ return result;
+ }
+
+ inline void add(const rgbaint_t& color2)
+ {
+ m_value = vec_add(m_value, color2.m_value);
+ }
+
+ inline void add_imm(const INT32 imm)
+ {
+ const VECS32 temp = { imm, imm, imm, imm };
+ m_value = vec_add(m_value, temp);
+ }
+
+ inline void add_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_add(m_value, temp);
+ }
+
+ inline void sub(const rgbaint_t& color2)
+ {
+ m_value = vec_sub(m_value, color2.m_value);
+ }
+
+ inline void sub_imm(const INT32 imm)
+ {
+ const VECS32 temp = { imm, imm, imm, imm };
+ m_value = vec_sub(m_value, temp);
+ }
+
+ inline void sub_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_sub(m_value, temp);
+ }
+
+ inline void subr(rgbaint_t& color2)
+ {
+ m_value = vec_sub(color2.m_value, m_value);
+ }
+
+ inline void subr_imm(const INT32 imm)
+ {
+ const VECS32 temp = { imm, imm, imm, imm };
+ m_value = vec_sub(temp, m_value);
+ }
+
+ inline void subr_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_sub(temp, m_value);
+ }
+
+ inline void set_a(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_perm(m_value, temp, alpha_perm);
+ }
+
+ inline void set_r(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_perm(m_value, temp, red_perm);
+ }
+
+ inline void set_g(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_perm(m_value, temp, green_perm);
+ }
+
+ inline void set_b(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_perm(m_value, temp, blue_perm);
+ }
+
+ inline UINT8 get_a() const
+ {
+ UINT8 result;
+ vec_ste(vec_splat((VECU8)m_value, 3), 0, &result);
+ return result;
+ }
+
+ inline UINT8 get_r() const
+ {
+ UINT8 result;
+ vec_ste(vec_splat((VECU8)m_value, 7), 0, &result);
+ return result;
+ }
+
+ inline UINT8 get_g() const
+ {
+ UINT8 result;
+ vec_ste(vec_splat((VECU8)m_value, 11), 0, &result);
+ return result;
+ }
+
+ inline UINT8 get_b() const
+ {
+ UINT8 result;
+ vec_ste(vec_splat((VECU8)m_value, 15), 0, &result);
+ return result;
+ }
+
+ inline INT32 get_a32() const
+ {
+ INT32 result;
+ vec_ste(vec_splat(m_value, 0), 0, &result);
+ return result;
+ }
+
+ inline INT32 get_r32() const
+ {
+ INT32 result;
+ vec_ste(vec_splat(m_value, 1), 0, &result);
+ return result;
+ }
+
+ inline INT32 get_g32() const
+ {
+ INT32 result;
+ vec_ste(vec_splat(m_value, 2), 0, &result);
+ return result;
+ }
+
+ inline INT32 get_b32() const
+ {
+ INT32 result;
+ vec_ste(vec_splat(m_value, 3), 0, &result);
+ return result;
+ }
+
+ inline void mul(const rgbaint_t& color)
+ {
+ const VECU32 shift = vec_splat_u32(-16);
+ const VECU32 temp = vec_msum((VECU16)m_value, (VECU16)vec_rl(color.m_value, shift), vec_splat_u32(0));
+ m_value = vec_add(vec_sl(temp, shift), vec_mulo((VECU16)m_value, (VECU16)color.m_value));
+ }
+
+ inline void mul_imm(const INT32 imm)
+ {
+ const VECU32 value = { imm, imm, imm, imm };
+ const VECU32 shift = vec_splat_u32(-16);
+ const VECU32 temp = vec_msum((VECU16)m_value, (VECU16)vec_rl(value, shift), vec_splat_u32(0));
+ m_value = vec_add(vec_sl(temp, shift), vec_mulo((VECU16)m_value, (VECU16)value));
+ }
+
+ inline void mul_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECU32 value = { a, r, g, b };
+ const VECU32 shift = vec_splat_u32(-16);
+ const VECU32 temp = vec_msum((VECU16)m_value, (VECU16)vec_rl(value, shift), vec_splat_u32(0));
+ m_value = vec_add(vec_sl(temp, shift), vec_mulo((VECU16)m_value, (VECU16)value));
+ }
+
+ inline void shl(const rgbaint_t& shift)
+ {
+ const VECU32 limit = { 32, 32, 32, 32 };
+ const VECU32 temp = vec_splat(shift.m_value, 3);
+ m_value = vec_and(vec_sl(m_value, temp), vec_cmpgt(limit, temp));
+ }
+
+ inline void shl_imm(const UINT8 shift)
+ {
+ const VECU32 temp = { shift, shift, shift, shift };
+ m_value = vec_sl(m_value, temp);
+ }
+
+ inline void shr(const rgbaint_t& shift)
+ {
+ const VECU32 limit = { 32, 32, 32, 32 };
+ const VECU32 temp = vec_splat(shift.m_value, 3);
+ m_value = vec_and(vec_sr(m_value, temp), vec_cmpgt(limit, temp));
+ }
+
+ inline void shr_imm(const UINT8 shift)
+ {
+ const VECU32 temp = { shift, shift, shift, shift };
+ m_value = vec_sr(m_value, temp);
+ }
+
+ inline void sra(const rgbaint_t& shift)
+ {
+ const VECU32 limit = { 31, 31, 31, 31 };
+ m_value = vec_sra(m_value, vec_min((VECU32)vec_splat(shift.m_value, 3), limit));
+ }
+
+ inline void sra_imm(const UINT8 shift)
+ {
+ const VECU32 temp = { shift, shift, shift, shift };
+ m_value = vec_sra(m_value, temp);
+ }
+
+ inline void or_reg(const rgbaint_t& color2)
+ {
+ m_value = vec_or(m_value, color2.m_value);
+ }
+
+ inline void or_imm(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_or(m_value, temp);
+ }
+
+ inline void or_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_or(m_value, temp);
+ }
+
+ inline void and_reg(const rgbaint_t& color)
+ {
+ m_value = vec_and(m_value, color.m_value);
+ }
+
+ inline void and_imm(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_and(m_value, temp);
+ }
+
+ inline void and_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_and(m_value, temp);
+ }
+
+ inline void xor_reg(const rgbaint_t& color2)
+ {
+ m_value = vec_xor(m_value, color2.m_value);
+ }
+
+ inline void xor_imm(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_xor(m_value, temp);
+ }
+
+ inline void xor_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_xor(m_value, temp);
+ }
+
+ inline void clamp_and_clear(const UINT32 sign)
+ {
+ const VECS32 vzero = { 0, 0, 0, 0 };
+ VECS32 vsign = { sign, sign, sign, sign };
+ m_value = vec_and(m_value, vec_cmpeq(vec_and(m_value, vsign), vzero));
+ vsign = vec_nor(vec_sra(vsign, vec_splat_u32(1)), vzero);
+ const VECS32 mask = vec_cmpgt(m_value, vsign);
+ m_value = vec_or(vec_and(vsign, mask), vec_and(m_value, vec_nor(mask, vzero)));
+ }
+
+ inline void sign_extend(const UINT32 compare, const UINT32 sign)
+ {
+ const VECS32 compare_vec = { compare, compare, compare, compare };
+ const VECS32 compare_mask = vec_cmpeq(vec_and(m_value, compare_vec), compare_vec);
+ const VECS32 sign_vec = { sign, sign, sign, sign };
+ m_value = vec_or(m_value, vec_and(sign_vec, compare_mask));
+ }
+
+ inline void min(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_min(m_value, temp);
+ }
+
+ inline void max(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_max(m_value, temp);
+ }
+
+ void blend(const rgbaint_t& other, UINT8 factor);
+
+ void scale_and_clamp(const rgbaint_t& scale);
+ void scale_imm_and_clamp(const INT32 scale);
+ void scale2_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other, const rgbaint_t& scale2);
+ void scale_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other);
+ void scale_imm_add_and_clamp(const INT32 scale, const rgbaint_t& other);
+
+ inline void cmpeq(const rgbaint_t& value)
+ {
+ m_value = vec_cmpeq(m_value, value.m_value);
+ }
+
+ inline void cmpeq_imm(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_cmpeq(m_value, temp);
+ }
+
+ inline void cmpeq_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_cmpeq(m_value, temp);
+ }
+
+ inline void cmpgt(const rgbaint_t& value)
+ {
+ m_value = vec_cmpgt(m_value, value.m_value);
+ }
+
+ inline void cmpgt_imm(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_cmpgt(m_value, temp);
+ }
+
+ inline void cmpgt_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_cmpgt(m_value, temp);
+ }
+
+ inline void cmplt(const rgbaint_t& value)
+ {
+ m_value = vec_cmplt(m_value, value.m_value);
+ }
+
+ inline void cmplt_imm(const INT32 value)
+ {
+ const VECS32 temp = { value, value, value, value };
+ m_value = vec_cmplt(m_value, temp);
+ }
+
+ inline void cmplt_imm_rgba(const INT32 a, const INT32 r, const INT32 g, const INT32 b)
+ {
+ const VECS32 temp = { a, r, g, b };
+ m_value = vec_cmplt(m_value, temp);
+ }
+
+ inline rgbaint_t operator=(const rgbaint_t& other)
+ {
+ m_value = other.m_value;
+ return *this;
+ }
+
+ inline rgbaint_t& operator+=(const rgbaint_t& other)
+ {
+ m_value = vec_add(m_value, other.m_value);
+ return *this;
+ }
+
+ inline rgbaint_t& operator+=(const INT32 other)
+ {
+ const VECS32 temp = { other, other, other, other };
+ m_value = vec_add(m_value, temp);
+ return *this;
+ }
+
+ inline rgbaint_t& operator-=(const rgbaint_t& other)
+ {
+ m_value = vec_sub(m_value, other.m_value);
+ return *this;
+ }
+
+ inline rgbaint_t& operator*=(const rgbaint_t& other)
+ {
+ const VECU32 shift = vec_splat_u32(-16);
+ const VECU32 temp = vec_msum((VECU16)m_value, (VECU16)vec_rl(other.m_value, shift), vec_splat_u32(0));
+ m_value = vec_add(vec_sl(temp, shift), vec_mulo((VECU16)m_value, (VECU16)other.m_value));
+ return *this;
+ }
+
+ inline rgbaint_t& operator*=(const INT32 other)
+ {
+ const VECS32 value = { other, other, other, other };
+ const VECU32 shift = vec_splat_u32(-16);
+ const VECU32 temp = vec_msum((VECU16)m_value, (VECU16)vec_rl(value, shift), vec_splat_u32(0));
+ m_value = vec_add(vec_sl(temp, shift), vec_mulo((VECU16)m_value, (VECU16)value));
+ return *this;
+ }
+
+ inline rgbaint_t& operator>>=(const INT32 shift)
+ {
+ const VECU32 temp = { shift, shift, shift, shift };
+ m_value = vec_sra(m_value, temp);
+ return *this;
+ }
+
+ inline void merge_alpha(const rgbaint_t& alpha)
+ {
+ m_value = vec_perm(m_value, alpha.m_value, alpha_perm);
+ }
+
+ static UINT32 bilinear_filter(UINT32 rgb00, UINT32 rgb01, UINT32 rgb10, UINT32 rgb11, UINT8 u, UINT8 v)
+ {
+ const VECS32 zero = vec_splat_s32(0);
+
+ VECS32 color00 = vec_perm((VECS32)vec_lde(0, &rgb00), zero, vec_lvsl(0, &rgb00));
+ VECS32 color01 = vec_perm((VECS32)vec_lde(0, &rgb01), zero, vec_lvsl(0, &rgb01));
+ VECS32 color10 = vec_perm((VECS32)vec_lde(0, &rgb10), zero, vec_lvsl(0, &rgb10));
+ VECS32 color11 = vec_perm((VECS32)vec_lde(0, &rgb11), zero, vec_lvsl(0, &rgb11));
+
+ /* interleave color01 and color00 at the byte level */
+ color01 = vec_mergeh((VECU8)color01, (VECU8)color00);
+ color11 = vec_mergeh((VECU8)color11, (VECU8)color10);
+ color01 = vec_mergeh((VECU8)zero, (VECU8)color01);
+ color11 = vec_mergeh((VECU8)zero, (VECU8)color11);
+ color01 = vec_msum((VECS16)color01, scale_table[u], zero);
+ color11 = vec_msum((VECS16)color11, scale_table[u], zero);
+ color01 = vec_sl(color01, vec_splat_u32(15));
+ color11 = vec_sr(color11, vec_splat_u32(1));
+ color01 = vec_max((VECS16)color01, (VECS16)color11);
+ color01 = vec_msum((VECS16)color01, scale_table[v], zero);
+ color01 = vec_sr(color01, vec_splat_u32(15));
+ color01 = vec_packs(color01, color01);
+ color01 = vec_packsu((VECS16)color01, (VECS16)color01);
+
+ UINT32 result;
+ vec_ste((VECU32)color01, 0, &result);
+ return result;
+ }
+
+protected:
+ typedef __vector signed char VECS8;
+ typedef __vector unsigned char VECU8;
+ typedef __vector signed short VECS16;
+ typedef __vector unsigned short VECU16;
+ typedef __vector signed int VECS32;
+ typedef __vector unsigned int VECU32;
+
+ VECS32 m_value;
+
+ static const VECU8 alpha_perm;
+ static const VECU8 red_perm;
+ static const VECU8 green_perm;
+ static const VECU8 blue_perm;
+ static const VECS16 scale_table[256];
+};
+
+
+
+// altivec.h somehow redefines "bool" in a bad way on PowerPC Mac OS X. really.
+#ifdef OSX_PPC
+#undef vector
+#undef pixel
+#undef bool
+#endif
+
+#endif /* __RGBVMX__ */
diff --git a/src/devices/video/saa5050.c b/src/devices/video/saa5050.c
new file mode 100644
index 00000000000..2440d0bc357
--- /dev/null
+++ b/src/devices/video/saa5050.c
@@ -0,0 +1,525 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Mullard SAA5050 Teletext Character Generator emulation
+
+ http://www.bighole.nl/pub/mirror/homepage.ntlworld.com/kryten_droid/teletext/spec/teletext_spec_1974.htm
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - character rounding
+ - remote controller input
+ - boxing
+
+*/
+
+#include "saa5050.h"
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type SAA5050 = &device_creator<saa5050_device>;
+const device_type SAA5051 = &device_creator<saa5051_device>;
+const device_type SAA5052 = &device_creator<saa5052_device>;
+const device_type SAA5053 = &device_creator<saa5053_device>;
+const device_type SAA5054 = &device_creator<saa5054_device>;
+const device_type SAA5055 = &device_creator<saa5055_device>;
+const device_type SAA5056 = &device_creator<saa5056_device>;
+const device_type SAA5057 = &device_creator<saa5057_device>;
+
+
+//-------------------------------------------------
+// ROM( saa5050 )
+//-------------------------------------------------
+
+ROM_START( saa5050 )
+ ROM_REGION( 0xa00, "chargen", 0 )
+ ROM_LOAD( "saa5050", 0x0140, 0x08c0, BAD_DUMP CRC(78c17e3e) SHA1(4e1c59dc484505de1dc0b1ba7e5f70a54b0d4ccc) )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5051 )
+//-------------------------------------------------
+
+ROM_START( saa5051 )
+ ROM_REGION( 0xa00, "chargen", 0 )
+ ROM_LOAD( "saa5051", 0x0140, 0x08c0, NO_DUMP )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5052 )
+//-------------------------------------------------
+
+ROM_START( saa5052 )
+ ROM_REGION( 0xa00, "chargen", 0 )
+ ROM_LOAD( "saa5052", 0x0140, 0x08c0, BAD_DUMP CRC(cda3bf79) SHA1(cf5ea94459c09001d422dadc212bc970b4b4aa20) )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5053 )
+//-------------------------------------------------
+
+ROM_START( saa5053 )
+ ROM_REGION( 0xa00, "chargen", 0 )
+ ROM_LOAD( "saa5053", 0x0140, 0x08c0, NO_DUMP )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5054 )
+//-------------------------------------------------
+
+ROM_START( saa5054 )
+ ROM_REGION( 0xa00, "chargen", 0 )
+ ROM_LOAD( "saa5054", 0x0140, 0x08c0, NO_DUMP )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5055 )
+//-------------------------------------------------
+
+ROM_START( saa5055 )
+ ROM_REGION( 0xa00, "chargen", 0 )
+ ROM_LOAD( "saa5055", 0x0140, 0x08c0, NO_DUMP )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5056 )
+//-------------------------------------------------
+
+ROM_START( saa5056 )
+ ROM_REGION( 0xa00, "chargen", 0 )
+ ROM_LOAD( "saa5056", 0x0140, 0x08c0, NO_DUMP )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5057 )
+//-------------------------------------------------
+
+ROM_START( saa5057 )
+ ROM_REGION( 0xa00, "chargen", 0 )
+ ROM_LOAD( "saa5057", 0x0140, 0x08c0, NO_DUMP )
+ROM_END
+
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *saa5050_device::device_rom_region() const
+{
+ return ROM_NAME( saa5050 );
+}
+
+const rom_entry *saa5051_device::device_rom_region() const
+{
+ return ROM_NAME( saa5051 );
+}
+
+const rom_entry *saa5052_device::device_rom_region() const
+{
+ return ROM_NAME( saa5052 );
+}
+
+const rom_entry *saa5053_device::device_rom_region() const
+{
+ return ROM_NAME( saa5053 );
+}
+
+const rom_entry *saa5054_device::device_rom_region() const
+{
+ return ROM_NAME( saa5054 );
+}
+
+const rom_entry *saa5055_device::device_rom_region() const
+{
+ return ROM_NAME( saa5055 );
+}
+
+const rom_entry *saa5056_device::device_rom_region() const
+{
+ return ROM_NAME( saa5056 );
+}
+
+const rom_entry *saa5057_device::device_rom_region() const
+{
+ return ROM_NAME( saa5057 );
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// saa5050_device - constructor
+//-------------------------------------------------
+
+saa5050_device::saa5050_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_char_rom(*this, "chargen"),
+ m_read_d(*this),
+ m_frame_count(0),
+ m_cols(0),
+ m_rows(0),
+ m_size(0)
+{
+}
+
+saa5050_device::saa5050_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, SAA5050, "SAA5050 Video", tag, owner, clock, "saa5050", __FILE__),
+ m_char_rom(*this, "chargen"),
+ m_read_d(*this),
+ m_frame_count(0),
+ m_cols(0),
+ m_rows(0),
+ m_size(0)
+{
+}
+
+saa5051_device::saa5051_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5051, "SAA5051", tag, owner, clock, "saa5051", __FILE__) { }
+
+saa5052_device::saa5052_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5052, "SAA5052", tag, owner, clock, "saa5052", __FILE__) { }
+
+saa5053_device::saa5053_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5053, "SAA5053", tag, owner, clock, "saa5053", __FILE__) { }
+
+saa5054_device::saa5054_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5054, "SAA5054", tag, owner, clock, "saa5054", __FILE__) { }
+
+saa5055_device::saa5055_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5055, "SAA5055", tag, owner, clock, "saa5055", __FILE__) { }
+
+saa5056_device::saa5056_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5056, "SAA5056", tag, owner, clock, "saa5056", __FILE__) { }
+
+saa5057_device::saa5057_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5057, "SAA5057", tag, owner, clock, "saa5057", __FILE__) { }
+
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void saa5050_device::device_start()
+{
+ // resolve callbacks
+ m_read_d.resolve_safe(0);
+
+ // register for state saving
+ save_item(NAME(m_code));
+ save_item(NAME(m_last_code));
+ save_item(NAME(m_char_data));
+ save_item(NAME(m_bit));
+ save_item(NAME(m_color));
+ save_item(NAME(m_ra));
+ save_item(NAME(m_bg));
+ save_item(NAME(m_fg));
+ save_item(NAME(m_graphics));
+ save_item(NAME(m_separated));
+ save_item(NAME(m_conceal));
+ save_item(NAME(m_flash));
+ save_item(NAME(m_boxed));
+ save_item(NAME(m_double_height));
+ save_item(NAME(m_double_height_top_row));
+ save_item(NAME(m_double_height_bottom_row));
+ save_item(NAME(m_hold));
+ save_item(NAME(m_frame_count));
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific reset
+//-------------------------------------------------
+
+void saa5050_device::device_reset()
+{
+ m_ra = 0;
+ m_double_height_top_row = false;
+ m_double_height_bottom_row = false;
+}
+
+
+//-------------------------------------------------
+// process_control_character -
+//-------------------------------------------------
+
+void saa5050_device::process_control_character(UINT8 data)
+{
+ switch (data)
+ {
+ case ALPHA_RED:
+ case ALPHA_GREEN:
+ case ALPHA_YELLOW:
+ case ALPHA_BLUE:
+ case ALPHA_MAGENTA:
+ case ALPHA_CYAN:
+ case ALPHA_WHITE:
+ m_graphics = false;
+ m_conceal = false;
+ m_fg = data & 0x07;
+ break;
+
+ case FLASH:
+ m_flash = true;
+ break;
+
+ case STEADY:
+ m_flash = false;
+ break;
+
+ case END_BOX:
+ case START_BOX:
+ // TODO
+ break;
+
+ case NORMAL_HEIGHT:
+ m_double_height = 0;
+ break;
+
+ case DOUBLE_HEIGHT:
+ if (!m_double_height_bottom_row)
+ {
+ m_double_height_top_row = true;
+ }
+
+ m_double_height = 1;
+ break;
+
+ case GRAPHICS_RED:
+ case GRAPHICS_GREEN:
+ case GRAPHICS_YELLOW:
+ case GRAPHICS_BLUE:
+ case GRAPHICS_MAGENTA:
+ case GRAPHICS_CYAN:
+ case GRAPHICS_WHITE:
+ m_graphics = true;
+ m_conceal = false;
+ m_fg = data & 0x07;
+ break;
+
+ case CONCEAL_DISPLAY:
+ m_conceal = true;
+ break;
+
+ case CONTIGUOUS_GFX:
+ m_separated = false;
+ break;
+
+ case SEPARATED_GFX:
+ m_separated = true;
+ break;
+
+ case BLACK_BACKGROUND:
+ m_bg = 0;
+ break;
+
+ case NEW_BACKGROUND:
+ m_bg = m_fg;
+ break;
+
+ case HOLD_GRAPHICS:
+ m_hold = true;
+ break;
+
+ case RELEASE_GRAPHICS:
+ m_hold = false;
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// get_character_data -
+//-------------------------------------------------
+
+void saa5050_device::get_character_data(UINT8 data)
+{
+ if (m_graphics && (data & 0x20))
+ {
+ data += (data & 0x40) ? 64 : 96;
+ if (m_separated) data += 64;
+ }
+
+ if ((data < 0x20) && m_hold) data = m_last_code;
+ if (m_conceal) data = 0x20;
+ if (m_flash && (m_frame_count > 38)) data = 0x20;
+ if (m_double_height_bottom_row && !m_double_height) data = 0x20;
+ m_last_code = data;
+
+ offs_t ra = m_ra >> 1;
+ if (m_double_height) ra >>= 1;
+ if (m_double_height && m_double_height_bottom_row) ra += 5;
+
+ m_char_data = m_char_rom[(data * 10) + ra];
+}
+
+
+//-------------------------------------------------
+// dew_w - data entry window
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::dew_w )
+{
+ if (state)
+ {
+ m_ra = 19;
+ m_double_height_top_row = false;
+
+ m_frame_count++;
+ if (m_frame_count > 50) m_frame_count = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// lose_w - load output shift register enable
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::lose_w )
+{
+ if (state)
+ {
+ m_ra++;
+ m_ra %= 20;
+
+ m_fg = 7;
+ m_bg = 0;
+ m_graphics = false;
+ m_separated = false;
+ m_conceal = false;
+ m_flash = false;
+ m_boxed = false;
+ m_hold = false;
+ m_double_height = 0;
+ m_bit = 5;
+ m_last_code = 0x20;
+
+ if (!m_ra)
+ {
+ m_double_height_bottom_row = m_double_height_top_row;
+ m_double_height_top_row = false;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// write - character data write
+//-------------------------------------------------
+
+void saa5050_device::write(UINT8 data)
+{
+ m_code = data & 0x7f;
+}
+
+
+//-------------------------------------------------
+// f1_w - character clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::f1_w )
+{
+ if (state)
+ {
+ process_control_character(m_code);
+ get_character_data(m_code);
+ }
+}
+
+
+//-------------------------------------------------
+// tr6_w - pixel clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::tr6_w )
+{
+ if (state)
+ {
+ m_color = BIT(m_char_data, m_bit) ? m_fg : m_bg;
+
+ m_bit--;
+ if (m_bit < 0) m_bit = 5;
+ }
+}
+
+
+//-------------------------------------------------
+// get_rgb - get output color
+//-------------------------------------------------
+
+int saa5050_device::get_rgb()
+{
+ return m_color;
+}
+
+
+//-------------------------------------------------
+// screen_update -
+//-------------------------------------------------
+
+UINT32 saa5050_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ dew_w(1);
+ dew_w(0);
+
+ for (int y = 0; y < m_rows * 20; y++)
+ {
+ int sy = y / 20;
+ int x = 0;
+
+ lose_w(1);
+ lose_w(0);
+
+ int ssy = m_double_height_bottom_row ? sy - 1 : sy;
+ offs_t video_ram_addr = ssy * m_size;
+
+ for (int sx = 0; sx < m_cols; sx++)
+ {
+ UINT8 code = m_read_d(video_ram_addr++);
+
+ write(code & 0x7f);
+
+ f1_w(1);
+ f1_w(0);
+
+ for (int bit = 0; bit < 6; bit++)
+ {
+ tr6_w(1);
+ tr6_w(0);
+
+ int color = get_rgb();
+
+ if (BIT(code, 7)) color ^= 0x07;
+
+ int r = BIT(color, 0) * 0xff;
+ int g = BIT(color, 1) * 0xff;
+ int b = BIT(color, 2) * 0xff;
+
+ rgb_t rgb = rgb_t(r, g, b);
+
+ bitmap.pix32(y, x++) = rgb;
+ bitmap.pix32(y, x++) = rgb;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/devices/video/saa5050.h b/src/devices/video/saa5050.h
new file mode 100644
index 00000000000..0155da83e4e
--- /dev/null
+++ b/src/devices/video/saa5050.h
@@ -0,0 +1,257 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Mullard SAA5050 Teletext Character Generator emulation
+
+**********************************************************************
+ _____ _____
+ Vss 1 |* \_/ | 28 DE
+ _SI 2 | | 27 PO
+ _DATA 3 | | 26 LOSE
+ D1 4 | SAA5050 | 25 BLAN
+ D2 5 | SAA5051 | 24 R
+ D3 6 | SAA5052 | 23 G
+ D4 7 | SAA5053 | 22 B
+ D5 8 | SAA5054 | 21 Y
+ D6 9 | SAA5055 | 20 F1
+ D7 10 | SAA5056 | 19 TR6
+ DLIM 11 | SAA5057 | 18 Vdd
+ _GLR 12 | | 17 N/C
+ DEW 13 | | 16 _TLC
+ CRS 14 |_____________| 15 _BCS
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __SAA5050__
+#define __SAA5050__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SAA5050_D_CALLBACK(_read) \
+ devcb = &saa5050_device::set_d_rd_callback(*device, DEVCB_##_read);
+
+
+#define MCFG_SAA5050_SCREEN_SIZE(_cols, _rows, _size) \
+ saa5050_device::static_set_screen_size(*device, _cols, _rows, _size);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> saa5050_device
+
+class saa5050_device : public device_t
+{
+public:
+ // construction/destruction
+ saa5050_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ saa5050_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_screen_size(device_t &device, int cols, int rows, int size) { downcast<saa5050_device &>(device).m_cols = cols; downcast<saa5050_device &>(device).m_rows = rows; downcast<saa5050_device &>(device).m_size = size; }
+
+ template<class _Object> static devcb_base &set_d_rd_callback(device_t &device, _Object object) { return downcast<saa5050_device &>(device).m_read_d.set_callback(object); }
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+
+ DECLARE_WRITE_LINE_MEMBER( dew_w );
+ DECLARE_WRITE_LINE_MEMBER( lose_w );
+ void write(UINT8 data);
+ DECLARE_WRITE_LINE_MEMBER( f1_w );
+ DECLARE_WRITE_LINE_MEMBER( tr6_w );
+ int get_rgb();
+
+ // NOTE: the following are provided for convenience only, SAA5050 is not a display controller
+ // this emulates the common setup where bit 7 of data inverts the display, and the
+ // bottom half of a double height row gets the same character data as the top half
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ enum
+ {
+ NUL = 0,
+ ALPHA_RED,
+ ALPHA_GREEN,
+ ALPHA_YELLOW,
+ ALPHA_BLUE,
+ ALPHA_MAGENTA,
+ ALPHA_CYAN,
+ ALPHA_WHITE,
+ FLASH,
+ STEADY,
+ END_BOX,
+ START_BOX,
+ NORMAL_HEIGHT,
+ DOUBLE_HEIGHT,
+ S0,
+ S1,
+ DLE,
+ GRAPHICS_RED,
+ GRAPHICS_GREEN,
+ GRAPHICS_YELLOW,
+ GRAPHICS_BLUE,
+ GRAPHICS_MAGENTA,
+ GRAPHICS_CYAN,
+ GRAPHICS_WHITE,
+ CONCEAL_DISPLAY,
+ CONTIGUOUS_GFX,
+ SEPARATED_GFX,
+ ESC,
+ BLACK_BACKGROUND,
+ NEW_BACKGROUND,
+ HOLD_GRAPHICS,
+ RELEASE_GRAPHICS
+ };
+
+ void process_control_character(UINT8 data);
+ void get_character_data(UINT8 data);
+
+ required_region_ptr<UINT8> m_char_rom;
+
+ devcb_read8 m_read_d;
+
+ UINT8 m_code;
+ UINT8 m_last_code;
+ UINT8 m_char_data;
+ int m_bit;
+ rgb_t m_color;
+ int m_ra;
+ int m_bg;
+ int m_fg;
+ bool m_graphics;
+ bool m_separated;
+ bool m_conceal;
+ bool m_flash;
+ bool m_boxed;
+ int m_double_height;
+ bool m_double_height_top_row;
+ bool m_double_height_bottom_row;
+ bool m_hold;
+ int m_frame_count;
+
+ int m_cols;
+ int m_rows;
+ int m_size;
+};
+
+
+// ======================> saa5051_device
+
+class saa5051_device : public saa5050_device
+{
+public:
+ // construction/destruction
+ saa5051_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+};
+
+
+// ======================> saa5052_device
+
+class saa5052_device : public saa5050_device
+{
+public:
+ // construction/destruction
+ saa5052_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+};
+
+
+// ======================> saa5053_device
+
+class saa5053_device : public saa5050_device
+{
+public:
+ // construction/destruction
+ saa5053_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+};
+
+
+// ======================> saa5054_device
+
+class saa5054_device : public saa5050_device
+{
+public:
+ // construction/destruction
+ saa5054_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+};
+
+
+// ======================> saa5055_device
+
+class saa5055_device : public saa5050_device
+{
+public:
+ // construction/destruction
+ saa5055_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+};
+
+
+// ======================> saa5056_device
+
+class saa5056_device : public saa5050_device
+{
+public:
+ // construction/destruction
+ saa5056_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+};
+
+
+// ======================> saa5057_device
+
+class saa5057_device : public saa5050_device
+{
+public:
+ // construction/destruction
+ saa5057_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+};
+
+
+// device type definition
+extern const device_type SAA5050; // English
+extern const device_type SAA5051; // German
+extern const device_type SAA5052; // Swedish/Finnish
+extern const device_type SAA5053; // Italian
+extern const device_type SAA5054; // Belgian
+extern const device_type SAA5055; // U.S. ASCII
+extern const device_type SAA5056; // Hebrew
+extern const device_type SAA5057; // Cyrillic
+
+
+
+#endif
diff --git a/src/devices/video/scn2674.c b/src/devices/video/scn2674.c
new file mode 100644
index 00000000000..e1998367762
--- /dev/null
+++ b/src/devices/video/scn2674.c
@@ -0,0 +1,766 @@
+// license:BSD-3-Clause
+// copyright-holders:Carl
+/*
+ SCN2674 - Advanced Video Display Controller (AVDC) (Video Chip)
+*/
+
+#include "scn2674.h"
+
+#define S674VERBOSE 0
+#define LOG2674(x) do { if (S674VERBOSE) logerror x; } while (0)
+
+const device_type SCN2674_VIDEO = &device_creator<scn2674_device>;
+
+
+// default address map
+static ADDRESS_MAP_START( scn2674_vram, AS_0, 8, scn2674_device )
+ AM_RANGE(0x0000, 0xffff) AM_NOP
+ADDRESS_MAP_END
+
+scn2674_device::scn2674_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SCN2674_VIDEO, "Signetics SCN2674 AVDC", tag, owner, clock, "scn2674_device", __FILE__),
+ device_video_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_irq_cb(*this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(scn2674_vram))
+{
+}
+
+void scn2674_device::device_start()
+{
+ // resolve callbacks
+ m_display_cb.bind_relative_to(*owner());
+ m_irq_cb.resolve_safe();
+ m_scanline_timer = timer_alloc(TIMER_SCANLINE);
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ save_item(NAME(m_address));
+ save_item(NAME(m_linecounter));
+ save_item(NAME(m_screen2_l));
+ save_item(NAME(m_screen2_h));
+ save_item(NAME(m_cursor_l));
+ save_item(NAME(m_cursor_h));
+}
+
+void scn2674_device::device_reset()
+{
+ m_IR_pointer= 0;
+ m_screen1_l= 0;
+ m_screen1_h= 0;
+ m_cursor_l= 0;
+ m_cursor_h= 0;
+ m_screen2_l= 0;
+ m_screen2_h= 0;
+ m_irq_register= 0;
+ m_status_register= 0;
+ m_irq_mask= 0;
+ m_gfx_enabled= 0;
+ m_display_enabled= 0;
+ m_display_enabled_field= 0;
+ m_display_enabled_scanline= 0;
+ m_cursor_enabled= 0;
+ m_IR0_double_ht_wd= 0;
+ m_IR0_scanline_per_char_row= 0;
+ m_IR0_sync_select= 0;
+ m_IR0_buffer_mode_select= 0;
+ m_IR1_interlace_enable= 0;
+ m_IR1_equalizing_constant= 0;
+ m_IR2_row_table= 0;
+ m_IR2_horz_sync_width= 0;
+ m_IR2_horz_back_porch= 0;
+ m_IR3_vert_front_porch= 0;
+ m_IR3_vert_back_porch= 0;
+ m_IR4_rows_per_screen= 0;
+ m_IR4_character_blink_rate_divisor= 0;
+ m_IR5_character_per_row= 0;
+ m_IR6_cursor_first_scanline= 0;
+ m_IR6_cursor_last_scanline= 0;
+ m_IR7_cursor_underline_position= 0;
+ m_IR7_cursor_rate_divisor= 0;
+ m_IR7_cursor_blink= 0;
+ m_IR7_vsync_width= 0;
+ m_IR8_display_buffer_first_address_LSB= 0;
+ m_IR9_display_buffer_first_address_MSB= 0;
+ m_IR9_display_buffer_last_address= 0;
+ m_IR10_display_pointer_address_lower= 0;
+ m_IR11_display_pointer_address_upper= 0;
+ m_IR11_reset_scanline_counter_on_scrollup= 0;
+ m_IR11_reset_scanline_counter_on_scrolldown= 0;
+ m_IR12_scroll_start= 0;
+ m_IR12_split_register_1= 0;
+ m_IR13_scroll_end= 0;
+ m_IR13_split_register_2= 0;
+ m_IR14_scroll_lines= 0;
+ m_IR14_double_1= 0;
+ m_IR14_double_2= 0;
+ m_spl1= 0;
+ m_spl2= 0;
+ m_dbl1= 0;
+ m_buffer= 0;
+ m_linecounter= 0;
+ m_irq_state= 0;
+ m_IR_pointer = 0;
+ m_address = 0;
+ m_start1change = 0;
+ m_hpixels_per_column = m_text_hpixels_per_column;
+}
+
+// 15 Initialization Registers (8-bit each)
+void scn2674_device::write_init_regs(UINT8 data)
+{
+ LOG2674(("scn2674_write_init_regs %02x %02x\n",m_IR_pointer,data));
+
+ switch ( m_IR_pointer) /* display some debug info, set mame specific variables */
+ {
+ case 0:
+ m_IR0_double_ht_wd = (data & 0x80)>>7;
+ m_IR0_scanline_per_char_row = ((data & 0x78)>>3) + 1;
+ m_IR0_sync_select = (data&0x04)>>2;
+ m_IR0_buffer_mode_select = (data&0x03);
+
+ LOG2674(("IR0 - Double Ht Wd %02x\n",m_IR0_double_ht_wd));//affects IR14 as well
+ LOG2674(("IR0 - Scanlines per Character Row %02x\n",m_IR0_scanline_per_char_row));//value+1 = scanlines
+ LOG2674(("IR0 - Sync Select %02x\n",m_IR0_sync_select));//1 = csync
+ LOG2674(("IR0 - Buffer Mode Select %02x\n",m_IR0_buffer_mode_select)); //0 independent 1 transparent 2 shared 3 row
+ break;
+
+ case 1:
+ m_IR1_interlace_enable = (data&0x80)>>7;
+ m_IR1_equalizing_constant = (data&0x7f)+1;
+
+ LOG2674(("IR1 - Interlace Enable %02x\n",m_IR1_interlace_enable));
+ LOG2674(("IR1 - Equalizing Constant %02i CCLKs\n",m_IR1_equalizing_constant));
+ break;
+
+ case 2:
+ m_IR2_row_table = (data&0x80)>>7;
+ m_IR2_horz_sync_width = (((data&0x78)>>3)*2) + 2;
+ m_IR2_horz_back_porch = ((data&0x07)*4) - 1;
+
+ LOG2674(("IR2 - Row Table %02x\n",m_IR2_row_table));
+ LOG2674(("IR2 - Horizontal Sync Width %02i CCLKs\n",m_IR2_horz_sync_width));
+ LOG2674(("IR2 - Horizontal Back Porch %02i CCLKs\n",m_IR2_horz_back_porch));
+ break;
+
+ case 3:
+ m_IR3_vert_front_porch = (((data&0xe0)>>5) * 4)+4 ;
+ m_IR3_vert_back_porch = ((data&0x1f) * 2) + 4;
+
+ LOG2674(("IR3 - Vertical Front Porch %02i Lines\n",m_IR3_vert_front_porch));
+ LOG2674(("IR3 - Vertical Back Porch %02i Lines\n",m_IR3_vert_back_porch));
+ break;
+
+ case 4:
+ m_IR4_rows_per_screen = (data&0x7f) + 1;
+ m_IR4_character_blink_rate_divisor = ((data & 0x80)>>7 ? 128:64);
+
+ LOG2674(("IR4 - Rows Per Screen %02i\n",m_IR4_rows_per_screen));
+ LOG2674(("IR4 - Character Blink Rate = 1/%02i\n",m_IR4_character_blink_rate_divisor));
+ break;
+
+ case 5:
+ /* IR5 - Active Characters Per Row
+ cccc cccc
+ c = Characters Per Row */
+ m_IR5_character_per_row = data + 1;
+ LOG2674(("IR5 - Active Characters Per Row %02i\n",m_IR5_character_per_row));
+ break;
+
+ case 6:
+ m_IR6_cursor_last_scanline = (data & 0x0f);
+ m_IR6_cursor_first_scanline = (data & 0xf0)>>4;
+ LOG2674(("IR6 - First Line of Cursor %02x\n",m_IR6_cursor_first_scanline));
+ LOG2674(("IR6 - Last Line of Cursor %02x\n",m_IR6_cursor_last_scanline));
+ break;
+
+ case 7:
+ {
+ const UINT8 vsync_table[4] = {3,1,5,7};
+ m_IR7_cursor_underline_position = (data & 0x0f);
+ m_IR7_cursor_rate_divisor = ((data & 0x10)>>4 ? 64:32);
+ m_IR7_cursor_blink = (data & 0x20)>>5;
+
+ m_IR7_vsync_width = vsync_table[(data & 0xC0)>>6];
+
+ LOG2674(("IR7 - Underline Position %02x\n",m_IR7_cursor_underline_position));
+ LOG2674(("IR7 - Cursor rate 1/%02i\n",m_IR7_cursor_rate_divisor));
+ LOG2674(("IR7 - Cursor blink %02x\n",m_IR7_cursor_blink));
+ LOG2674(("IR7 - Vsync Width %02i Lines\n",m_IR7_vsync_width));
+ break;
+ }
+
+ case 8:
+ m_IR8_display_buffer_first_address_LSB = data;
+ LOG2674(("IR8 - Display Buffer First Address LSB %02x\n",m_IR8_display_buffer_first_address_LSB));
+ break;
+
+ case 9:
+ m_IR9_display_buffer_first_address_MSB = data & 0x0f;
+ m_IR9_display_buffer_last_address = (data & 0xf0)>>4;
+ LOG2674(("IR9 - Display Buffer First Address MSB %02x\n",m_IR9_display_buffer_first_address_MSB));
+ LOG2674(("IR9 - Display Buffer Last Address %02x\n",m_IR9_display_buffer_last_address));
+ break;
+
+ case 10:
+ m_IR10_display_pointer_address_lower = data;
+ LOG2674(("IR10 - Display Pointer Address Lower %02x\n",m_IR10_display_pointer_address_lower));
+ break;
+
+ case 11:
+ m_IR11_display_pointer_address_upper= data&0x3f;
+ m_IR11_reset_scanline_counter_on_scrollup= (data&0x40 >> 6);
+ m_IR11_reset_scanline_counter_on_scrolldown= (data&0x80 >> 7);
+
+ LOG2674(("IR11 - Display Pointer Address Lower %02x\n",m_IR11_display_pointer_address_upper));
+ LOG2674(("IR11 - Reset Scanline Counter on Scroll Up %02x\n",m_IR11_reset_scanline_counter_on_scrollup));
+ LOG2674(("IR11 - Reset Scanline Counter on Scroll Down %02x\n",m_IR11_reset_scanline_counter_on_scrolldown));
+ break;
+
+ case 12:
+ m_IR12_scroll_start = (data & 0x80)>>7;
+ m_IR12_split_register_1 = (data & 0x7f);
+ LOG2674(("IR12 - Scroll Start %02x\n",m_IR12_scroll_start));
+ LOG2674(("IR12 - Split Register 1 %02x\n",m_IR12_split_register_1));
+ break;
+
+ case 13:
+ m_IR13_scroll_end = (data & 0x80)>>7;
+ m_IR13_split_register_2 = (data & 0x7f);
+ LOG2674(("IR13 - Scroll End %02x\n",m_IR13_scroll_end));
+ LOG2674(("IR13 - Split Register 2 %02x\n",m_IR13_split_register_2));
+ break;
+
+ case 14:
+ m_IR14_scroll_lines = (data & 0x0f);
+ if (!m_IR0_double_ht_wd)
+ {
+ m_IR14_double_2 = (data & 0x30)>>4;
+ LOG2674(("IR14 - Double 2 %02x\n",m_IR14_double_2));
+ }
+ //0 normal, 1, double width, 2, double width and double tops 3, double width and double bottoms
+ //1 affects SSR1, 2 affects SSR2
+ //If Double Height enabled in IR0, Screen start 1 upper (bits 7 and 6)replace Double 1, and Double 2 is unused
+ m_IR14_double_1 = (data & 0xc0)>>6;
+ LOG2674(("IR14 - Double 1 %02x\n",m_IR14_double_1));
+
+ LOG2674(("IR14 - Scroll Lines %02i\n",m_IR14_scroll_lines));
+ break;
+
+ case 15: /* not valid! */
+ break;
+
+ }
+ recompute_parameters();
+
+ m_IR_pointer++;
+ if (m_IR_pointer>14)m_IR_pointer=14;
+}
+
+void scn2674_device::write_command(UINT8 data)
+{
+ UINT8 operand;
+ int i;
+
+
+ if (data==0x00)
+ {
+ /* master reset, configures registers */
+ LOG2674(("master reset\n"));
+ m_IR_pointer=0;
+ m_irq_register = 0x00;
+ m_status_register = 0x20;//RDFLG activated
+ m_linecounter =0;
+ m_irq_mask = 0x00;
+ m_gfx_enabled = 0;
+ m_display_enabled = 0;
+ m_cursor_enabled = 0;
+ m_IR2_row_table = 0;
+ }
+
+ if ((data&0xf0)==0x10)
+ {
+ /* set IR pointer */
+ operand = data & 0x0f;
+ LOG2674(("set IR pointer %02x\n",operand));
+
+ m_IR_pointer=operand;
+
+ }
+
+ /* ANY COMBINATION OF THESE ARE POSSIBLE */
+
+ if ((data&0xe3)==0x22)
+ {
+ /* Disable GFX */
+ LOG2674(("disable GFX %02x\n",data));
+ m_gfx_enabled = 0;
+ recompute_parameters();
+ }
+
+ if ((data&0xe3)==0x23)
+ {
+ /* Enable GFX */
+ LOG2674(("enable GFX %02x\n",data));
+ m_gfx_enabled = 1;
+ recompute_parameters();
+ }
+
+ if ((data&0xe9)==0x28)
+ {
+ /* Display off */
+ operand = data & 0x04;
+
+ m_display_enabled = 0;
+
+ if (operand)
+ LOG2674(("display OFF - float DADD bus %02x\n",data));
+ else
+ LOG2674(("display OFF - no float DADD bus %02x\n",data));
+ }
+
+ if ((data&0xe9)==0x29)
+ {
+ /* Display on */
+ operand = data & 0x04;
+
+ if (operand)
+ {
+ m_display_enabled_field = 1;
+ LOG2674(("display ON - next field %02x\n",data));
+ }
+ else
+ {
+ m_display_enabled_scanline = 1;
+ LOG2674(("display ON - next scanline %02x\n",data));
+ }
+ recompute_parameters(); // start the scanline timer
+ }
+
+ if ((data&0xf1)==0x30)
+ {
+ /* Cursor Off */
+ LOG2674(("cursor off %02x\n",data));
+ m_cursor_enabled = 0;
+ }
+
+ if ((data&0xf1)==0x31)
+ {
+ /* Cursor On */
+ LOG2674(("cursor on %02x\n",data));
+ m_cursor_enabled = 1;
+ }
+
+ /* END */
+
+ if ((data&0xe0)==0x40)
+ {
+ /* Reset Interrupt / Status bit */
+ operand = data & 0x1f;
+ LOG2674(("reset interrupt / status bit %02x\n",operand));
+
+ m_irq_register &= ~(data & 0x1f);
+ m_status_register &= ~(data & 0x1f);
+
+ LOG2674(("IRQ Status after reset\n"));
+ LOG2674(("Split 2 IRQ: %d Active\n",(m_irq_register>>0)&1));
+ LOG2674(("Ready IRQ: %d Active\n",(m_irq_register>>1)&1));
+ LOG2674(("Split 1 IRQ: %d Active\n",(m_irq_register>>2)&1));
+ LOG2674(("Line Zero IRQ: %d Active\n",(m_irq_register>>3)&1));
+ LOG2674(("V-Blank IRQ: %d Active\n",(m_irq_register>>4)&1));
+
+ m_irq_state = 0;
+
+ for (i = 0; i < 5; i++)
+ {
+ if ((m_irq_register>>i&1)&(m_irq_mask>>i&1))
+ {
+ m_irq_state = 1;
+ }
+ }
+ m_irq_cb(m_irq_register ? 1 : 0);
+
+ }
+ if ((data&0xe0)==0x80)
+ {
+ /* Disable Interrupt mask*/
+ operand = data & 0x1f;
+ m_irq_mask &= ~(operand);
+ LOG2674(("IRQ Mask after disable %x\n",operand));
+ LOG2674(("Split 2 IRQ: %d Unmasked\n",(m_irq_mask>>0)&1));
+ LOG2674(("Ready IRQ: %d Unmasked\n",(m_irq_mask>>1)&1));
+ LOG2674(("Split 1 IRQ: %d Unmasked\n",(m_irq_mask>>2)&1));
+ LOG2674(("Line Zero IRQ: %d Unmasked\n",(m_irq_mask>>3)&1));
+ LOG2674(("V-Blank IRQ: %d Unmasked\n",(m_irq_mask>>4)&1));
+
+ }
+
+ if ((data&0xe0)==0x60)
+ {
+ /* Enable Interrupt mask*/
+ operand = data & 0x1f;
+ m_irq_mask |= (data & 0x1f);
+
+ LOG2674(("IRQ Mask after enable %x\n",operand));
+ LOG2674(("Split 2 IRQ: %d Unmasked\n",(m_irq_mask>>0)&1));
+ LOG2674(("Ready IRQ: %d Unmasked\n",(m_irq_mask>>1)&1));
+ LOG2674(("Split 1 IRQ: %d Unmasked\n",(m_irq_mask>>2)&1));
+ LOG2674(("Line Zero IRQ: %d Unmasked\n",(m_irq_mask>>3)&1));
+ LOG2674(("V-Blank IRQ: %d Unmasked\n",(m_irq_mask>>4)&1));
+
+ }
+
+ /* Delayed Commands */
+ /* These set 0x20 in status register when done */
+ // These use the pointer address according to the datasheet but the pcx expects the screen start 2 address instead
+ switch(data)
+ {
+ case 0xa4:
+ /* read at pointer address */
+ m_buffer = space().read_byte(m_screen2_l | (m_screen2_h << 8));
+ LOG2674(("DELAYED read at pointer address %02x\n",data));
+ break;
+
+ case 0xa2:
+ /* write at pointer address */
+ space().write_byte(m_screen2_l | (m_screen2_h << 8), m_buffer);
+ LOG2674(("DELAYED write at pointer address %02x\n",data));
+ break;
+
+ case 0xa9:
+ /* increment cursor address */
+ if(!(++m_cursor_l))
+ m_cursor_h++;
+ LOG2674(("DELAYED increase cursor address %02x\n",data));
+ break;
+
+ case 0xac:
+ /* read at cursor address */
+ m_buffer = space().read_byte(m_cursor_l | (m_cursor_h << 8));
+ LOG2674(("DELAYED read at cursor address %02x\n",data));
+ break;
+
+ case 0xaa:
+ /* write at cursor address */
+ space().write_byte(m_cursor_l | (m_cursor_h << 8), m_buffer);
+ LOG2674(("DELAYED write at cursor address %02x\n",data));
+ break;
+
+ case 0xad:
+ /* read at cursor address + increment */
+ m_buffer = space().read_byte(m_cursor_l | (m_cursor_h << 8));
+ if(!(++m_cursor_l))
+ m_cursor_h++;
+ LOG2674(("DELAYED read at cursor address+increment %02x\n",data));
+ break;
+
+ case 0xab:
+ /* write at cursor address + increment */
+ space().write_byte(m_cursor_l | (m_cursor_h << 8), m_buffer);
+ if(!(++m_cursor_l))
+ m_cursor_h++;
+ LOG2674(("DELAYED write at cursor address+increment %02x\n",data));
+ break;
+
+ case 0xbb:
+ /* write from cursor address to pointer address TODO: transfer only during blank*/
+ for(i = m_cursor_l | (m_cursor_h << 8); i != (m_screen2_l | (m_screen2_h << 8)); i = ((i + 1) & 0xffff))
+ space().write_byte(i, m_buffer);
+ space().write_byte(i, m_buffer); // get the last
+ m_cursor_l = m_screen2_l;
+ m_cursor_h = m_screen2_h;
+ LOG2674(("DELAYED write from cursor address to pointer address %02x\n",data));
+ break;
+
+ case 0xbd:
+ /* read from cursor address to pointer address */
+ LOG2674(("DELAYED read from cursor address to pointer address %02x\n",data));
+ break;
+ }
+}
+
+
+READ8_MEMBER( scn2674_device::read )
+{
+ /*
+ Offset: Purpose
+ 0 Interrupt Register
+ 1 Status Register
+ 2 Screen Start 1 Lower Register
+ 3 Screen Start 1 Upper Register
+ 4 Cursor Address Lower Register
+ 5 Cursor Address Upper Register
+ 6 Screen Start 2 Lower Register
+ 7 Screen Start 2 Upper Register
+ */
+
+ switch (offset)
+ {
+ /* Status / Irq Register
+
+ --RV ZSRs
+
+ 6+7 -- = ALWAYS 0
+ 5 R = RDFLG (Status Register Only)
+ 4 V = Vblank
+ 3 Z = Line Zero
+ 2 S = Split 1
+ 1 R = Ready
+ 0 s = Split 2
+ */
+
+ case 0:
+ LOG2674(("Read Irq Register %02x %06x\n",m_irq_register,space.device().safe_pc()));
+ return m_irq_register;
+
+ case 1:
+ LOG2674(("Read Status Register %02X %06x\n",m_status_register,space.device().safe_pc()));
+ return m_status_register;
+
+ case 2: LOG2674(("Read Screen1_l Register %06x\n",space.device().safe_pc()));return m_screen1_l;
+ case 3: LOG2674(("Read Screen1_h Register %06x\n",space.device().safe_pc()));return m_screen1_h & 0x3f;
+ case 4: LOG2674(("Read Cursor_l Register %06x\n",space.device().safe_pc()));return m_cursor_l;
+ case 5: LOG2674(("Read Cursor_h Register %06x\n",space.device().safe_pc()));return m_cursor_h;
+ case 6: LOG2674(("Read Screen2_l Register %06x\n",space.device().safe_pc()));return m_screen2_l;
+ case 7: LOG2674(("Read Screen2_h Register %06x\n",space.device().safe_pc()));return m_screen2_h;
+ }
+
+ return 0xff;
+}
+
+
+WRITE8_MEMBER( scn2674_device::write )
+{
+ /*
+ Offset: Purpose
+ 0 Initialization Registers
+ 1 Command Register
+ 2 Screen Start 1 Lower Register
+ 3 Screen Start 1 Upper Register
+ 4 Cursor Address Lower Register
+ 5 Cursor Address Upper Register
+ 6 Screen Start 2 Lower Register
+ 7 Screen Start 2 Upper Register
+ */
+
+ switch (offset)
+ {
+ case 0:
+ write_init_regs(data);
+ break;
+
+ case 1:
+ write_command(data);
+ break;
+
+ case 2:
+ m_screen1_l = data;
+ if(!m_screen->vblank())
+ m_start1change = (m_linecounter / m_IR0_scanline_per_char_row) + 1;
+ break;
+ case 3:
+ m_screen1_h = data;
+ m_dbl1=(data & 0xc0)>>6;
+ if (m_IR0_double_ht_wd)
+ {
+ m_IR14_double_1 = m_dbl1;
+ m_screen1_h &= 0x3f;
+ LOG2674(("IR14 - Double 1 overridden %02x\n",m_IR14_double_1));
+ }
+ if(!m_screen->vblank())
+ m_start1change = (m_linecounter / m_IR0_scanline_per_char_row) + 1;
+ break;
+
+ case 4: m_cursor_l = data; break;
+ case 5: m_cursor_h = (data & 0x3f); break;
+ case 6: m_screen2_l = data; break;
+ case 7:
+ m_screen2_h = (data&0x3f);
+ m_spl1 = (data & 0x40);
+ m_spl2 = (data & 0x80);
+ break;
+ }
+}
+
+void scn2674_device::recompute_parameters()
+{
+ m_hpixels_per_column = m_gfx_enabled ? m_gfx_hpixels_per_column : m_text_hpixels_per_column;
+ int horiz_pix_total = ((m_IR1_equalizing_constant + (m_IR2_horz_sync_width << 1)) << 1) * m_hpixels_per_column;
+ int vert_pix_total = m_IR4_rows_per_screen * m_IR0_scanline_per_char_row + m_IR3_vert_front_porch + m_IR3_vert_back_porch + m_IR7_vsync_width;
+ attoseconds_t refresh = m_screen->frame_period().attoseconds();
+ int max_visible_x = (m_IR5_character_per_row * m_hpixels_per_column) - 1;
+ int max_visible_y = (m_IR4_rows_per_screen * m_IR0_scanline_per_char_row) - 1;
+
+ if(!horiz_pix_total || !vert_pix_total)
+ {
+ m_scanline_timer->adjust(attotime::never);
+ return;
+ }
+
+ LOG2674(("width %u height %u max_x %u max_y %u refresh %f\n", horiz_pix_total, vert_pix_total, max_visible_x, max_visible_y, 1 / ATTOSECONDS_TO_DOUBLE(refresh)));
+
+ rectangle visarea;
+ visarea.set(0, max_visible_x, 0, max_visible_y);
+ m_screen->configure(horiz_pix_total, vert_pix_total, visarea, refresh);
+
+ m_scanline_timer->adjust(m_screen->time_until_pos(0, 0), 0, m_screen->scan_period());
+}
+
+void scn2674_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case TIMER_SCANLINE:
+ {
+ int dw = m_IR0_double_ht_wd ? m_IR14_double_1 : 0; // double width
+ if(((m_display_enabled_scanline) || (m_display_enabled_field && !m_IR1_interlace_enable)) && (!m_display_enabled))
+ {
+ m_display_enabled = 1;
+ m_display_enabled_scanline = 0;
+ m_display_enabled_field = 0;
+ }
+ else
+ m_linecounter++;
+
+ if(m_linecounter >= m_screen->height())
+ {
+ m_linecounter = 0;
+ m_address = (m_screen1_h << 8) | m_screen1_l;
+ }
+
+ if(m_linecounter == (m_IR4_rows_per_screen * m_IR0_scanline_per_char_row))
+ {
+ m_status_register |= 0x10;
+ if(m_irq_mask & 0x10)
+ {
+ LOG2674(("vblank irq\n"));
+ m_irq_state = 1;
+ m_irq_register |= 0x10;
+ m_irq_cb(1);
+ }
+ }
+
+ if(m_linecounter >= (m_IR4_rows_per_screen * m_IR0_scanline_per_char_row))
+ break;
+
+ int charrow = m_linecounter % m_IR0_scanline_per_char_row;
+ int tilerow = charrow;
+
+ // should be triggered at the start of each ROW (line zero for that row)
+ if(!charrow)
+ {
+ m_status_register |= 0x08;
+ if (m_irq_mask & 0x08)
+ {
+ LOG2674(("SCN2674 Line Zero\n"));
+ m_irq_state = 1;
+ m_irq_register |= 0x08;
+ m_irq_cb(1);
+ }
+ }
+
+ if((m_linecounter == (m_IR12_split_register_1 * m_IR0_scanline_per_char_row)) && m_linecounter) /* Split Screen 1 */
+ {
+ m_status_register |= 0x04;
+ if(m_irq_mask & 0x04)
+ {
+ LOG2674(("SCN2674 Split Screen 1 irq\n"));
+ m_irq_state = 1;
+ m_irq_register |= 0x04;
+ m_irq_cb(1);
+ }
+ if(m_spl1)
+ m_address = (m_screen2_h << 8) | m_screen2_l;
+ if(!m_IR0_double_ht_wd)
+ dw = m_IR14_double_1;
+ }
+
+ if((m_linecounter == (m_IR13_split_register_2 * m_IR0_scanline_per_char_row)) && m_linecounter) /* Split Screen 2 */
+ {
+ m_status_register |= 0x01;
+ if(m_irq_mask & 0x01)
+ {
+ LOG2674(("SCN2674 Split Screen 2 irq\n"));
+ m_irq_state = 1;
+ m_irq_register |= 0x01;
+ m_irq_cb(1);
+ }
+ if(m_spl2)
+ m_address = (m_screen2_h << 8) | m_screen2_l;
+ if(!m_IR0_double_ht_wd)
+ dw = m_IR14_double_2;
+ }
+
+ if(!m_display_enabled)
+ break;
+
+ if(m_IR2_row_table)
+ {
+ if(m_IR0_double_ht_wd)
+ dw = m_screen1_h >> 6;
+ if(!charrow)
+ {
+ UINT16 addr = (m_screen2_h << 8) | m_screen2_l;
+ UINT16 line = space().read_word(addr);
+ m_screen1_h = (line >> 8);
+ m_screen1_l = line & 0xff;
+ if(m_IR0_double_ht_wd)
+ {
+ dw = line >> 14;
+ line &= ~0xc000;
+ }
+ m_address = line;
+ addr += 2;
+ m_screen2_h = (addr >> 8) & 0x3f;
+ m_screen2_l = addr & 0xff;
+ }
+ }
+ else if(m_start1change && (m_start1change == (m_linecounter / m_IR0_scanline_per_char_row)))
+ {
+ m_address = (m_screen1_h << 8) | m_screen1_l;
+ m_start1change = 0;
+ }
+
+ if(dw == 2)
+ tilerow >>= 1;
+ else if(dw == 3)
+ tilerow = (charrow + m_IR0_scanline_per_char_row) >> 1;
+
+ UINT16 address = m_address;
+
+ for(int i = 0; i < m_IR5_character_per_row; i++)
+ {
+ bool cursor_on = ((address & 0x3fff) == ((m_cursor_h << 8) | m_cursor_l));
+
+ if (!m_display_cb.isnull())
+ m_display_cb(m_bitmap,
+ i * m_hpixels_per_column,
+ m_linecounter,
+ tilerow,
+ space().read_byte(address),
+ address,
+ (charrow >= m_IR6_cursor_first_scanline) && (charrow <= m_IR6_cursor_last_scanline) && cursor_on,
+ dw != 0,
+ m_gfx_enabled != 0,
+ charrow == m_IR7_cursor_underline_position,
+ m_IR7_cursor_blink && (m_screen->frame_number() & (m_IR7_cursor_rate_divisor ? 0x40 : 0x20)));
+ address = (address + 1) & 0xffff;
+
+ if(address > ((m_IR9_display_buffer_last_address << 10) | 0x3ff))
+ address = (m_IR9_display_buffer_first_address_MSB << 8) | m_IR8_display_buffer_first_address_LSB;
+ }
+
+ if(m_gfx_enabled || (charrow == (m_IR0_scanline_per_char_row - 1)))
+ m_address = address;
+ }
+ }
+}
+
+UINT32 scn2674_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ if (!m_display_enabled)
+ m_bitmap.fill(rgb_t::black);
+ else
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+
+ return 0;
+}
diff --git a/src/devices/video/scn2674.h b/src/devices/video/scn2674.h
new file mode 100644
index 00000000000..b30c65c4039
--- /dev/null
+++ b/src/devices/video/scn2674.h
@@ -0,0 +1,133 @@
+// license:BSD-3-Clause
+// copyright-holders:Carl
+#ifndef SCN2674_H
+#define SCN2674_H
+
+#include "emu.h"
+
+#define MCFG_SCN2674_VIDEO_ADD(_tag, _clock, _irq) \
+ MCFG_DEVICE_ADD(_tag, SCN2674_VIDEO, _clock) \
+ devcb = &scn2674_device::set_irq_callback(*device, DEVCB_##_irq);
+
+#define MCFG_SCN2674_TEXT_CHARACTER_WIDTH(_value) \
+ scn2674_device::static_set_character_width(*device, _value);
+
+#define MCFG_SCN2674_GFX_CHARACTER_WIDTH(_value) \
+ scn2674_device::static_set_gfx_character_width(*device, _value);
+
+#define MCFG_SCN2674_DRAW_CHARACTER_CALLBACK_OWNER(_class, _method) \
+ scn2674_device::static_set_display_callback(*device, scn2674_device::draw_character_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define SCN2674_DRAW_CHARACTER_MEMBER(_name) void _name(bitmap_rgb32 &bitmap, int x, int y, UINT8 linecount, UINT8 charcode, UINT16 address, UINT8 cursor, UINT8 dw, UINT8 lg, UINT8 ul, UINT8 blink)
+
+class scn2674_device : public device_t,
+ public device_video_interface,
+ public device_memory_interface
+{
+public:
+ scn2674_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ typedef device_delegate<void (bitmap_rgb32 &bitmap, int x, int y, UINT8 linecount, UINT8 charcode, UINT16 address, UINT8 cursor, UINT8 dw, UINT8 lg, UINT8 ul, UINT8 blink)> draw_character_delegate;
+
+ // static configuration
+ template<class _Object> static devcb_base &set_irq_callback(device_t &device, _Object object) { return downcast<scn2674_device &>(device).m_irq_cb.set_callback(object); }
+ static void static_set_character_width(device_t &device, int value) { downcast<scn2674_device &>(device).m_text_hpixels_per_column = value; }
+ static void static_set_gfx_character_width(device_t &device, int value) { downcast<scn2674_device &>(device).m_gfx_hpixels_per_column = value; }
+ static void static_set_display_callback(device_t &device, draw_character_delegate callback) { downcast<scn2674_device &>(device).m_display_cb = callback; }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_READ8_MEMBER( buffer_r ) { return m_buffer; }
+ DECLARE_WRITE8_MEMBER( buffer_w ) { m_buffer = data; }
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const { return (spacenum == AS_0) ? &m_space_config : NULL; }
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ bitmap_rgb32 m_bitmap;
+ devcb_write_line m_irq_cb;
+
+ UINT8 m_IR_pointer;
+ UINT8 m_screen1_l;
+ UINT8 m_screen1_h;
+ UINT8 m_cursor_l;
+ UINT8 m_cursor_h;
+ UINT8 m_screen2_l;
+ UINT8 m_screen2_h;
+ UINT8 m_irq_register;
+ UINT8 m_status_register;
+ UINT8 m_irq_mask;
+ UINT8 m_gfx_enabled;
+ UINT8 m_display_enabled;
+ UINT8 m_display_enabled_field;
+ UINT8 m_display_enabled_scanline;
+ UINT8 m_cursor_enabled;
+ UINT8 m_hpixels_per_column;
+ UINT8 m_text_hpixels_per_column;
+ UINT8 m_gfx_hpixels_per_column;
+ UINT8 m_IR0_double_ht_wd;
+ UINT8 m_IR0_scanline_per_char_row;
+ UINT8 m_IR0_sync_select;
+ UINT8 m_IR0_buffer_mode_select;
+ UINT8 m_IR1_interlace_enable;
+ UINT8 m_IR1_equalizing_constant;
+ UINT8 m_IR2_row_table;
+ UINT8 m_IR2_horz_sync_width;
+ UINT8 m_IR2_horz_back_porch;
+ UINT8 m_IR3_vert_front_porch;
+ UINT8 m_IR3_vert_back_porch;
+ UINT8 m_IR4_rows_per_screen;
+ UINT8 m_IR4_character_blink_rate_divisor;
+ UINT8 m_IR5_character_per_row;
+ UINT8 m_IR6_cursor_first_scanline;
+ UINT8 m_IR6_cursor_last_scanline;
+ UINT8 m_IR7_cursor_underline_position;
+ UINT8 m_IR7_cursor_rate_divisor;
+ UINT8 m_IR7_cursor_blink;
+ UINT8 m_IR7_vsync_width;
+ UINT8 m_IR8_display_buffer_first_address_LSB;
+ UINT8 m_IR9_display_buffer_first_address_MSB;
+ UINT8 m_IR9_display_buffer_last_address;
+ UINT8 m_IR10_display_pointer_address_lower;
+ UINT8 m_IR11_display_pointer_address_upper;
+ UINT8 m_IR11_reset_scanline_counter_on_scrollup;
+ UINT8 m_IR11_reset_scanline_counter_on_scrolldown;
+ UINT8 m_IR12_scroll_start;
+ UINT8 m_IR12_split_register_1;
+ UINT8 m_IR13_scroll_end;
+ UINT8 m_IR13_split_register_2;
+ UINT8 m_IR14_scroll_lines;
+ UINT8 m_IR14_double_1;
+ UINT8 m_IR14_double_2;
+ UINT8 m_spl1;
+ UINT8 m_spl2;
+ UINT8 m_dbl1;
+ UINT8 m_buffer;
+ int m_linecounter;
+ UINT16 m_address;
+ int m_start1change;
+
+ UINT8 m_irq_state;
+
+ void write_init_regs(UINT8 data);
+ void write_command(UINT8 data);
+ void recompute_parameters();
+
+ draw_character_delegate m_display_cb;
+ emu_timer *m_scanline_timer;
+ const address_space_config m_space_config;
+ enum
+ {
+ TIMER_SCANLINE
+ };
+};
+
+
+extern const device_type SCN2674_VIDEO;
+
+#endif
diff --git a/src/devices/video/sed1200.c b/src/devices/video/sed1200.c
new file mode 100644
index 00000000000..029ab01c236
--- /dev/null
+++ b/src/devices/video/sed1200.c
@@ -0,0 +1,192 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ SED1200
+
+ A LCD controller.
+
+ The D/F variants have a packaging difference (QFP80 vs. bare chip).
+
+ The A/B variants have an internal CGROM difference (jis
+ vs. european characters)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "sed1200.h"
+
+const device_type SED1200D0A = &device_creator<sed1200d0a_device>;
+const device_type SED1200F0A = &device_creator<sed1200f0a_device>;
+const device_type SED1200D0B = &device_creator<sed1200d0b_device>;
+const device_type SED1200F0B = &device_creator<sed1200f0b_device>;
+
+ROM_START( sed1200x0a )
+ ROM_REGION( 0x800, "cgrom", 0 )
+ ROM_LOAD( "sed1200-a.bin", 0x000, 0x800, CRC(e8c28054) SHA1(086406eb74e9ed97b309d2a4bdedc567626e9a98))
+ROM_END
+
+ROM_START( sed1200x0b )
+ ROM_REGION( 0x800, "cgrom", 0 )
+ ROM_LOAD( "sed1200-b.bin", 0x000, 0x800, CRC(d0741f51) SHA1(c8c856f1357286a2c8c806af81724a828345357e))
+ROM_END
+
+sed1200_device::sed1200_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+sed1200d0a_device::sed1200d0a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ sed1200_device(mconfig, SED1200D0A, "sed1200d-0a", tag, owner, clock, "sed1200", __FILE__)
+{
+}
+
+sed1200f0a_device::sed1200f0a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ sed1200_device(mconfig, SED1200F0A, "sed1200f-0a", tag, owner, clock, "sed1200", __FILE__)
+{
+}
+
+sed1200d0b_device::sed1200d0b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ sed1200_device(mconfig, SED1200D0B, "sed1200d-0b", tag, owner, clock, "sed1200", __FILE__)
+{
+}
+
+sed1200f0b_device::sed1200f0b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ sed1200_device(mconfig, SED1200F0B, "sed1200f-0b", tag, owner, clock, "sed1200", __FILE__)
+{
+}
+
+const rom_entry *sed1200d0a_device::device_rom_region() const
+{
+ return ROM_NAME(sed1200x0a);
+}
+
+const rom_entry *sed1200f0a_device::device_rom_region() const
+{
+ return ROM_NAME(sed1200x0a);
+}
+
+const rom_entry *sed1200d0b_device::device_rom_region() const
+{
+ return ROM_NAME(sed1200x0b);
+}
+
+const rom_entry *sed1200f0b_device::device_rom_region() const
+{
+ return ROM_NAME(sed1200x0b);
+}
+
+void sed1200_device::device_start()
+{
+ memset(cgram, 0, sizeof(cgram));
+ memset(ddram, 0, sizeof(ddram));
+ if(memregion("cgrom"))
+ cgrom = memregion("cgrom")->base();
+ else
+ cgrom = NULL;
+
+ soft_reset();
+}
+
+void sed1200_device::soft_reset()
+{
+ cursor_direction = false;
+ cursor_blinking = false;
+ cursor_full = false;
+ cursor_on = false;
+ display_on = false;
+ cursor_address = 0x00;
+ cgram_address = 0x00;
+}
+
+void sed1200_device::control_w(UINT8 data)
+{
+ switch(data) {
+ case 0x04: case 0x05:
+ cursor_direction = data & 0x01;
+ break;
+ case 0x06: case 0x07:
+ cursor_step();
+ break;
+ case 0x08: case 0x09:
+ cursor_full = data & 0x01;
+ break;
+ case 0x0a: case 0x0b:
+ cursor_blinking = data & 0x01;
+ break;
+ case 0x0c: case 0x0d:
+ display_on = data & 0x01;
+ break;
+ case 0x0e: case 0x0f:
+ cursor_on = data & 0x01;
+ break;
+ case 0x10:
+ soft_reset();
+ break;
+ case 0x12: case 0x13:
+ break; // Number of lines selection
+ default:
+ if((data & 0xf0) == 0x20)
+ cgram_address = (data & 3)*8;
+ else if((data & 0xe0) == 0x40) {
+ cgram[cgram_address++] = data;
+ if(cgram_address == 4*8)
+ cgram_address = 0;
+ } else if(data & 0x80) {
+ cursor_address = data & 0x40 ? 10 : 0;
+ cursor_address += (data & 0x3f) >= 10 ? 9 : data & 0x3f;
+ }
+ break;
+ }
+}
+
+UINT8 sed1200_device::control_r()
+{
+ return 0x00;
+}
+
+void sed1200_device::data_w(UINT8 data)
+{
+ ddram[cursor_address] = data;
+ cursor_step();
+}
+
+void sed1200_device::cursor_step()
+{
+ if(cursor_direction) {
+ if(cursor_address == 0 || cursor_address == 10)
+ cursor_address += 9;
+ else
+ cursor_address --;
+ } else {
+ if(cursor_address == 9 || cursor_address == 19)
+ cursor_address -= 9;
+ else
+ cursor_address ++;
+ }
+}
+
+const UINT8 *sed1200_device::render()
+{
+ memset(render_buf, 0, 20*8);
+ if(!display_on)
+ return render_buf;
+
+ for(int i=0; i<20; i++) {
+ UINT8 c = ddram[i];
+ if(c < 4)
+ memcpy(render_buf + 8*i, cgram + 8*c, 8);
+ else if(cgrom)
+ memcpy(render_buf + 8*i, cgrom + 8*c, 8);
+ }
+
+ if(cursor_on && (!cursor_blinking || (machine().time().as_ticks(2) & 1))) {
+ if(cursor_full)
+ for(int i=0; i<8; i++)
+ render_buf[cursor_address*8+i] ^= 0x1f;
+ else
+ render_buf[cursor_address*8+7] ^= 0x1f;
+ }
+
+ return render_buf;
+}
diff --git a/src/devices/video/sed1200.h b/src/devices/video/sed1200.h
new file mode 100644
index 00000000000..7429215c245
--- /dev/null
+++ b/src/devices/video/sed1200.h
@@ -0,0 +1,92 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ SED1200
+
+ A LCD controller.
+
+ The D/F variants are a packaging difference (QFP80 vs. bare chip).
+ The A/B variants are an internal CGROM difference (jis
+ vs. european characters)
+
+***************************************************************************/
+
+#ifndef __SED1200_H__
+#define __SED1200_H__
+
+#define MCFG_SED1200D0A_ADD( _tag ) \
+ MCFG_DEVICE_ADD( _tag, SED1200D0A, 0 )
+
+#define MCFG_SED1200F0A_ADD( _tag ) \
+ MCFG_DEVICE_ADD( _tag, SED1200F0A, 0 )
+
+#define MCFG_SED1200D0B_ADD( _tag ) \
+ MCFG_DEVICE_ADD( _tag, SED1200D0B, 0 )
+
+#define MCFG_SED1200F0B_ADD( _tag ) \
+ MCFG_DEVICE_ADD( _tag, SED1200F0B, 0 )
+
+class sed1200_device : public device_t {
+public:
+ sed1200_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ void control_w(UINT8 data);
+ UINT8 control_r();
+ void data_w(UINT8 data);
+
+ const UINT8 *render();
+
+protected:
+ virtual void device_start();
+
+private:
+ UINT8 cgram[4*8];
+ UINT8 ddram[10*2];
+ UINT8 render_buf[20*8];
+ bool cursor_direction, cursor_blinking, cursor_full, cursor_on, display_on;
+ UINT8 cursor_address, cgram_address;
+ const UINT8 *cgrom;
+
+ void soft_reset();
+ void cursor_step();
+};
+
+class sed1200d0a_device : public sed1200_device {
+public:
+ sed1200d0a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual const rom_entry *device_rom_region() const;
+};
+
+class sed1200f0a_device : public sed1200_device {
+public:
+ sed1200f0a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual const rom_entry *device_rom_region() const;
+};
+
+class sed1200d0b_device : public sed1200_device {
+public:
+ sed1200d0b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual const rom_entry *device_rom_region() const;
+};
+
+class sed1200f0b_device : public sed1200_device {
+public:
+ sed1200f0b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual const rom_entry *device_rom_region() const;
+};
+
+extern const device_type SED1200D0A;
+extern const device_type SED1200F0A;
+extern const device_type SED1200D0B;
+extern const device_type SED1200F0B;
+
+#endif
diff --git a/src/devices/video/sed1330.c b/src/devices/video/sed1330.c
new file mode 100644
index 00000000000..2ced64a8170
--- /dev/null
+++ b/src/devices/video/sed1330.c
@@ -0,0 +1,688 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Seiko-Epson SED1330 LCD Controller emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "sed1330.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+#define INSTRUCTION_SYSTEM_SET 0x40
+#define INSTRUCTION_SLEEP_IN 0x53 // unimplemented
+#define INSTRUCTION_DISP_ON 0x59
+#define INSTRUCTION_DISP_OFF 0x58
+#define INSTRUCTION_SCROLL 0x44
+#define INSTRUCTION_CSRFORM 0x5d
+#define INSTRUCTION_CGRAM_ADR 0x5c
+#define INSTRUCTION_CSRDIR_RIGHT 0x4c
+#define INSTRUCTION_CSRDIR_LEFT 0x4d
+#define INSTRUCTION_CSRDIR_UP 0x4e
+#define INSTRUCTION_CSRDIR_DOWN 0x4f
+#define INSTRUCTION_HDOT_SCR 0x5a
+#define INSTRUCTION_OVLAY 0x5b
+#define INSTRUCTION_CSRW 0x46
+#define INSTRUCTION_CSRR 0x47 // unimplemented
+#define INSTRUCTION_MWRITE 0x42
+#define INSTRUCTION_MREAD 0x43 // unimplemented
+
+
+#define CSRDIR_RIGHT 0x00
+#define CSRDIR_LEFT 0x01
+#define CSRDIR_UP 0x02
+#define CSRDIR_DOWN 0x03
+
+
+#define MX_OR 0x00
+#define MX_XOR 0x01 // unimplemented
+#define MX_AND 0x02 // unimplemented
+#define MX_PRIORITY_OR 0x03 // unimplemented
+
+
+#define FC_OFF 0x00
+#define FC_SOLID 0x01 // unimplemented
+#define FC_FLASH_32 0x02 // unimplemented
+#define FC_FLASH_64 0x03 // unimplemented
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type SED1330 = &device_creator<sed1330_device>;
+
+
+// default address map
+static ADDRESS_MAP_START( sed1330, AS_0, 8, sed1330_device )
+ AM_RANGE(0x0000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+
+// internal character generator ROM
+ROM_START( sed1330 )
+ ROM_REGION( 0x5c0, "gfx1", 0 ) // internal chargen ROM
+ ROM_LOAD( "sed1330.bin", 0x000, 0x5c0, NO_DUMP )
+ROM_END
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 sed1330_device::readbyte(offs_t address)
+{
+ return space().read_byte(address);
+}
+
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void sed1330_device::writebyte(offs_t address, UINT8 data)
+{
+ space().write_byte(address, data);
+}
+
+
+//-------------------------------------------------
+// increment_csr - increment cursor address
+//-------------------------------------------------
+
+inline void sed1330_device::increment_csr()
+{
+ switch (m_cd)
+ {
+ case CSRDIR_RIGHT:
+ m_csr++;
+ break;
+
+ case CSRDIR_LEFT:
+ m_csr--;
+ break;
+
+ case CSRDIR_UP:
+ m_csr -= m_ap;
+ break;
+
+ case CSRDIR_DOWN:
+ m_csr += m_ap;
+ break;
+ }
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// sed1330_device - constructor
+//-------------------------------------------------
+
+sed1330_device::sed1330_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SED1330, "SED1330", tag, owner, clock, "sed1330", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_bf(0),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(sed1330))
+{
+}
+
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *sed1330_device::device_rom_region() const
+{
+ return ROM_NAME( sed1330 );
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void sed1330_device::device_start()
+{
+ // register for state saving
+ save_item(NAME(m_bf));
+ save_item(NAME(m_ir));
+ save_item(NAME(m_dor));
+ save_item(NAME(m_pbc));
+ save_item(NAME(m_d));
+ save_item(NAME(m_sleep));
+ save_item(NAME(m_sag));
+ save_item(NAME(m_m0));
+ save_item(NAME(m_m1));
+ save_item(NAME(m_m2));
+ save_item(NAME(m_ws));
+ save_item(NAME(m_iv));
+ save_item(NAME(m_wf));
+ save_item(NAME(m_fx));
+ save_item(NAME(m_fy));
+ save_item(NAME(m_cr));
+ save_item(NAME(m_tcr));
+ save_item(NAME(m_lf));
+ save_item(NAME(m_ap));
+ save_item(NAME(m_sad1));
+ save_item(NAME(m_sad2));
+ save_item(NAME(m_sad3));
+ save_item(NAME(m_sad4));
+ save_item(NAME(m_sl1));
+ save_item(NAME(m_sl2));
+ save_item(NAME(m_hdotscr));
+ save_item(NAME(m_csr));
+ save_item(NAME(m_cd));
+ save_item(NAME(m_crx));
+ save_item(NAME(m_cry));
+ save_item(NAME(m_cm));
+ save_item(NAME(m_fc));
+ save_item(NAME(m_fp));
+ save_item(NAME(m_mx));
+ save_item(NAME(m_dm));
+ save_item(NAME(m_ov));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void sed1330_device::device_reset()
+{
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *sed1330_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// status_r -
+//-------------------------------------------------
+
+READ8_MEMBER( sed1330_device::status_r )
+{
+ if (LOG) logerror("SED1330 '%s' Status Read: %s\n", tag(), m_bf ? "busy" : "ready");
+
+ return m_bf << 6;
+}
+
+
+//-------------------------------------------------
+// command_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( sed1330_device::command_w )
+{
+ m_ir = data;
+ m_pbc = 0;
+
+ switch (m_ir)
+ {
+#if 0
+ case INSTRUCTION_SLEEP_IN:
+ break;
+#endif
+ case INSTRUCTION_CSRDIR_RIGHT:
+ case INSTRUCTION_CSRDIR_LEFT:
+ case INSTRUCTION_CSRDIR_UP:
+ case INSTRUCTION_CSRDIR_DOWN:
+ m_cd = data & 0x03;
+
+ if (LOG)
+ {
+ switch (m_cd)
+ {
+ case CSRDIR_RIGHT: logerror("SED1330 '%s' Cursor Direction: Right\n", tag()); break;
+ case CSRDIR_LEFT: logerror("SED1330 '%s' Cursor Direction: Left\n", tag()); break;
+ case CSRDIR_UP: logerror("SED1330 '%s' Cursor Direction: Up\n", tag()); break;
+ case CSRDIR_DOWN: logerror("SED1330 '%s' Cursor Direction: Down\n", tag()); break;
+ }
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// data_r -
+//-------------------------------------------------
+
+READ8_MEMBER( sed1330_device::data_r )
+{
+ UINT8 data = readbyte(m_csr);
+
+ if (LOG) logerror("SED1330 '%s' Memory Read %02x from %04x\n", tag(), data, m_csr);
+
+ increment_csr();
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// data_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( sed1330_device::data_w )
+{
+ switch (m_ir)
+ {
+ case INSTRUCTION_SYSTEM_SET:
+ switch (m_pbc)
+ {
+ case 0:
+ m_m0 = BIT(data, 0);
+ m_m1 = BIT(data, 1);
+ m_m2 = BIT(data, 2);
+ m_ws = BIT(data, 3);
+ m_iv = BIT(data, 5);
+
+ if (LOG)
+ {
+ logerror("SED1330 '%s' %s CG ROM\n", tag(), BIT(data, 0) ? "External" : "Internal");
+ logerror("SED1330 '%s' D6 Correction: %s\n", tag(), BIT(data, 1) ? "enabled" : "disabled");
+ logerror("SED1330 '%s' Character Height: %u\n", tag(), BIT(data, 2) ? 16 : 8);
+ logerror("SED1330 '%s' %s Panel Drive\n", tag(), BIT(data, 3) ? "Dual" : "Single");
+ logerror("SED1330 '%s' Screen Top-Line Correction: %s\n", tag(), BIT(data, 5) ? "disabled" : "enabled");
+ }
+ break;
+
+ case 1:
+ m_fx = (data & 0x07) + 1;
+ m_wf = BIT(data, 7);
+
+ if (LOG)
+ {
+ logerror("SED1330 '%s' Horizontal Character Size: %u\n", tag(), m_fx);
+ logerror("SED1330 '%s' %s AC Drive\n", tag(), BIT(data, 7) ? "2-frame" : "16-line");
+ }
+ break;
+
+ case 2:
+ m_fy = (data & 0x0f) + 1;
+ if (LOG) logerror("SED1330 '%s' Vertical Character Size: %u\n", tag(), m_fy);
+ break;
+
+ case 3:
+ m_cr = data + 1;
+ if (LOG) logerror("SED1330 '%s' Visible Characters Per Line: %u\n", tag(), m_cr);
+ break;
+
+ case 4:
+ m_tcr = data + 1;
+ if (LOG) logerror("SED1330 '%s' Total Characters Per Line: %u\n", tag(), m_tcr);
+ break;
+
+ case 5:
+ m_lf = data + 1;
+ if (LOG) logerror("SED1330 '%s' Frame Height: %u\n", tag(), m_lf);
+ break;
+
+ case 6:
+ m_ap = (m_ap & 0xff00) | data;
+ break;
+
+ case 7:
+ m_ap = (data << 8) | (m_ap & 0xff);
+ if (LOG) logerror("SED1330 '%s' Virtual Screen Width: %u\n", tag(), m_ap);
+ break;
+
+ default:
+ logerror("SED1330 '%s' Invalid parameter byte %02x\n", tag(), data);
+ }
+ break;
+
+ case INSTRUCTION_DISP_ON:
+ case INSTRUCTION_DISP_OFF:
+ m_d = BIT(data, 0);
+ m_fc = data & 0x03;
+ m_fp = data >> 2;
+ if (LOG)
+ {
+ logerror("SED1330 '%s' Display: %s\n", tag(), BIT(data, 0) ? "enabled" : "disabled");
+
+ switch (m_fc)
+ {
+ case FC_OFF: logerror("SED1330 '%s' Cursor: disabled\n", tag()); break;
+ case FC_SOLID: logerror("SED1330 '%s' Cursor: solid\n", tag()); break;
+ case FC_FLASH_32: logerror("SED1330 '%s' Cursor: fFR/32\n", tag()); break;
+ case FC_FLASH_64: logerror("SED1330 '%s' Cursor: fFR/64\n", tag()); break;
+ }
+
+ switch (m_fp & 0x03)
+ {
+ case FC_OFF: logerror("SED1330 '%s' Display Page 1: disabled\n", tag()); break;
+ case FC_SOLID: logerror("SED1330 '%s' Display Page 1: enabled\n", tag()); break;
+ case FC_FLASH_32: logerror("SED1330 '%s' Display Page 1: flash fFR/32\n", tag()); break;
+ case FC_FLASH_64: logerror("SED1330 '%s' Display Page 1: flash fFR/64\n", tag()); break;
+ }
+
+ switch ((m_fp >> 2) & 0x03)
+ {
+ case FC_OFF: logerror("SED1330 '%s' Display Page 2/4: disabled\n", tag()); break;
+ case FC_SOLID: logerror("SED1330 '%s' Display Page 2/4: enabled\n", tag()); break;
+ case FC_FLASH_32: logerror("SED1330 '%s' Display Page 2/4: flash fFR/32\n", tag()); break;
+ case FC_FLASH_64: logerror("SED1330 '%s' Display Page 2/4: flash fFR/64\n", tag()); break;
+ }
+
+ switch ((m_fp >> 4) & 0x03)
+ {
+ case FC_OFF: logerror("SED1330 '%s' Display Page 3: disabled\n", tag()); break;
+ case FC_SOLID: logerror("SED1330 '%s' Display Page 3: enabled\n", tag()); break;
+ case FC_FLASH_32: logerror("SED1330 '%s' Display Page 3: flash fFR/32\n", tag()); break;
+ case FC_FLASH_64: logerror("SED1330 '%s' Display Page 3: flash fFR/64\n", tag()); break;
+ }
+ }
+ break;
+
+ case INSTRUCTION_SCROLL:
+ switch (m_pbc)
+ {
+ case 0:
+ m_sad1 = (m_sad1 & 0xff00) | data;
+ break;
+
+ case 1:
+ m_sad1 = (data << 8) | (m_sad1 & 0xff);
+ if (LOG) logerror("SED1330 '%s' Display Page 1 Start Address: %04x\n", tag(), m_sad1);
+ break;
+
+ case 2:
+ m_sl1 = data + 1;
+ if (LOG) logerror("SED1330 '%s' Display Block 1 Screen Lines: %u\n", tag(), m_sl1);
+ break;
+
+ case 3:
+ m_sad2 = (m_sad2 & 0xff00) | data;
+ break;
+
+ case 4:
+ m_sad2 = (data << 8) | (m_sad2 & 0xff);
+ if (LOG) logerror("SED1330 '%s' Display Page 2 Start Address: %04x\n", tag(), m_sad2);
+ break;
+
+ case 5:
+ m_sl2 = data + 1;
+ if (LOG) logerror("SED1330 '%s' Display Block 2 Screen Lines: %u\n", tag(), m_sl2);
+ break;
+
+ case 6:
+ m_sad3 = (m_sad3 & 0xff00) | data;
+ break;
+
+ case 7:
+ m_sad3 = (data << 8) | (m_sad3 & 0xff);
+ if (LOG) logerror("SED1330 '%s' Display Page 3 Start Address: %04x\n", tag(), m_sad3);
+ break;
+
+ case 8:
+ m_sad4 = (m_sad4 & 0xff00) | data;
+ break;
+
+ case 9:
+ m_sad4 = (data << 8) | (m_sad4 & 0xff);
+ if (LOG) logerror("SED1330 '%s' Display Page 4 Start Address: %04x\n", tag(), m_sad4);
+ break;
+
+ default:
+ logerror("SED1330 '%s' Invalid parameter byte %02x\n", tag(), data);
+ }
+ break;
+
+ case INSTRUCTION_CSRFORM:
+ switch (m_pbc)
+ {
+ case 0:
+ m_crx = (data & 0x0f) + 1;
+ if (LOG) logerror("SED1330 '%s' Horizontal Cursor Size: %u\n", tag(), m_crx);
+ break;
+
+ case 1:
+ m_cry = (data & 0x0f) + 1;
+ m_cm = BIT(data, 7);
+ if (LOG)
+ {
+ logerror("SED1330 '%s' Vertical Cursor Location: %u\n", tag(), m_cry);
+ logerror("SED1330 '%s' Cursor Shape: %s\n", tag(), BIT(data, 7) ? "Block" : "Underscore");
+ }
+ break;
+
+ default:
+ logerror("SED1330 '%s' Invalid parameter byte %02x\n", tag(), data);
+ }
+ break;
+
+ case INSTRUCTION_CGRAM_ADR:
+ switch (m_pbc)
+ {
+ case 0:
+ m_sag = (m_sag & 0xff00) | data;
+ break;
+
+ case 1:
+ m_sag = (data << 8) | (m_sag & 0xff);
+ if (LOG) logerror("SED1330 '%s' Character Generator RAM Start Address: %04x\n", tag(), m_sag);
+ break;
+
+ default:
+ logerror("SED1330 '%s' Invalid parameter byte %02x\n", tag(), data);
+ }
+ break;
+
+ case INSTRUCTION_HDOT_SCR:
+ m_hdotscr = data & 0x07;
+ if (LOG) logerror("SED1330 '%s' Horizontal Dot Scroll: %u\n", tag(), m_hdotscr);
+ break;
+
+ case INSTRUCTION_OVLAY:
+ m_mx = data & 0x03;
+ m_dm = (data >> 2) & 0x03;
+ m_ov = BIT(data, 4);
+
+ if (LOG)
+ {
+ switch (m_mx)
+ {
+ case MX_OR: logerror("SED1330 '%s' Display Composition Method: OR\n", tag()); break;
+ case MX_XOR: logerror("SED1330 '%s' Display Composition Method: Exclusive-OR\n", tag()); break;
+ case MX_AND: logerror("SED1330 '%s' Display Composition Method: AND\n", tag()); break;
+ case MX_PRIORITY_OR: logerror("SED1330 '%s' Display Composition Method: Priority-OR\n", tag()); break;
+ }
+
+ logerror("SED1330 '%s' Display Page 1 Mode: %s\n", tag(), BIT(data, 2) ? "Graphics" : "Text");
+ logerror("SED1330 '%s' Display Page 3 Mode: %s\n", tag(), BIT(data, 3) ? "Graphics" : "Text");
+ logerror("SED1330 '%s' Display Composition Layers: %u\n", tag(), BIT(data, 4) ? 3 : 2);
+ }
+ break;
+
+ case INSTRUCTION_CSRW:
+ switch (m_pbc)
+ {
+ case 0:
+ m_csr = (m_csr & 0xff00) | data;
+ break;
+
+ case 1:
+ m_csr = (data << 8) | (m_csr & 0xff);
+ if (LOG) logerror("SED1330 '%s' Cursor Address %04x\n", tag(), m_csr);
+ break;
+
+ default:
+ logerror("SED1330 '%s' Invalid parameter byte %02x\n", tag(), data);
+ }
+ break;
+#if 0
+ case INSTRUCTION_CSRR:
+ break;
+#endif
+ case INSTRUCTION_MWRITE:
+ if (LOG) logerror("SED1330 '%s' Memory Write %02x to %04x (row %u col %u line %u)\n", tag(), data, m_csr, m_csr/80/8, m_csr%80, m_csr/80);
+
+ writebyte(m_csr, data);
+
+ increment_csr();
+ break;
+#if 0
+ case INSTRUCTION_MREAD:
+ break;
+#endif
+ default:
+ logerror("SED1330 '%s' Unsupported instruction %02x\n", tag(), m_ir);
+ }
+
+ m_pbc++;
+}
+
+
+//-------------------------------------------------
+// draw_text_scanline -
+//-------------------------------------------------
+
+void sed1330_device::draw_text_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, UINT16 va)
+{
+ int sx, x;
+
+ for (sx = 0; sx < m_cr; sx++)
+ {
+ if ((va + sx) == m_csr)
+ {
+ if (m_fc == FC_OFF) continue;
+
+ if (m_cm)
+ {
+ // block cursor
+ if (y % m_fy < m_cry)
+ {
+ for (x = 0; x < m_crx; x++)
+ {
+ bitmap.pix16(y, (sx * m_fx) + x) = 1;
+ }
+ }
+ }
+ else
+ {
+ // underscore cursor
+ if (y % m_fy == m_cry)
+ {
+ for (x = 0; x < m_crx; x++)
+ {
+ bitmap.pix16(y, (sx * m_fx) + x) = 1;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// draw_graphics_scanline -
+//-------------------------------------------------
+
+void sed1330_device::draw_graphics_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, UINT16 va)
+{
+ int sx, x;
+
+ for (sx = 0; sx < m_cr; sx++)
+ {
+ UINT8 data = readbyte(va++);
+
+ for (x = 0; x < m_fx; x++)
+ {
+ bitmap.pix16(y, (sx * m_fx) + x) = BIT(data, 7);
+ data <<= 1;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// update_graphics -
+//-------------------------------------------------
+
+void sed1330_device::update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+}
+
+
+//-------------------------------------------------
+// update_text -
+//-------------------------------------------------
+
+void sed1330_device::update_text(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ int y;
+
+ if (m_ws)
+ {
+ for (y = 0; y < m_sl1; y++)
+ {
+ UINT16 sad1 = m_sad1 + ((y / m_fy) * m_ap);
+ UINT16 sad2 = m_sad2 + (y * m_ap);
+ UINT16 sad3 = m_sad3 + ((y / m_fy) * m_ap);
+ UINT16 sad4 = m_sad4 + (y * m_ap);
+
+ // draw graphics display page 2 scanline
+ draw_graphics_scanline(bitmap, cliprect, y, sad2);
+
+ // draw text display page 1 scanline
+ draw_text_scanline(bitmap, cliprect, y, sad1);
+
+ // draw graphics display page 4 scanline
+ draw_graphics_scanline(bitmap, cliprect, y + m_sl1, sad4);
+
+ // draw text display page 3 scanline
+ draw_text_scanline(bitmap, cliprect, y + m_sl1, sad3);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// screen_update -
+//-------------------------------------------------
+
+UINT32 sed1330_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ if (m_d)
+ {
+ if (m_dm)
+ {
+ update_graphics(bitmap, cliprect);
+ }
+ else
+ {
+ update_text(bitmap, cliprect);
+ }
+ }
+ return 0;
+}
diff --git a/src/devices/video/sed1330.h b/src/devices/video/sed1330.h
new file mode 100644
index 00000000000..a78c71ef3a3
--- /dev/null
+++ b/src/devices/video/sed1330.h
@@ -0,0 +1,126 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Seiko-Epson SED1330 LCD Controller emulation
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __SED1330__
+#define __SED1330__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SED1330_ADD(_tag, _clock, _screen_tag, _map) \
+ MCFG_DEVICE_ADD(_tag, SED1330, _clock) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _map) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag)
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> sed1330_device
+
+class sed1330_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ sed1330_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+
+ DECLARE_READ8_MEMBER( status_r );
+ DECLARE_WRITE8_MEMBER( command_w );
+
+ DECLARE_READ8_MEMBER( data_r );
+ DECLARE_WRITE8_MEMBER( data_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ inline UINT8 readbyte(offs_t address);
+ inline void writebyte(offs_t address, UINT8 m_data);
+ inline void increment_csr();
+
+ void draw_text_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, UINT16 va);
+ void draw_graphics_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, UINT16 va);
+ void update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void update_text(bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+private:
+ int m_bf; // busy flag
+
+ UINT8 m_ir; // instruction register
+ UINT8 m_dor; // data output register
+ int m_pbc; // parameter byte counter
+
+ int m_d; // display enabled
+ int m_sleep; // sleep mode
+
+ UINT16 m_sag; // character generator RAM start address
+ int m_m0; // character generator ROM (0=internal, 1=external)
+ int m_m1; // character generator RAM D6 correction (0=no, 1=yes)
+ int m_m2; // height of character bitmaps (0=8, 1=16 pixels)
+ int m_ws; // LCD drive method (0=single, 1=dual panel)
+ int m_iv; // screen origin compensation for inverse display (0=yes, 1=no)
+ int m_wf; // AC frame drive waveform period (0=16-line, 1=2-frame)
+
+ int m_fx; // character width in pixels
+ int m_fy; // character height in pixels
+ int m_cr; // visible line width in characters
+ int m_tcr; // total line width in characters (including horizontal blanking)
+ int m_lf; // frame height in lines
+ UINT16 m_ap; // virtual screen line width in characters
+
+ UINT16 m_sad1; // display page 1 start address
+ UINT16 m_sad2; // display page 2 start address
+ UINT16 m_sad3; // display page 3 start address
+ UINT16 m_sad4; // display page 4 start address
+ int m_sl1; // display block 1 height in lines
+ int m_sl2; // display block 2 height in lines
+ int m_hdotscr; // horizontal dot scroll in pixels
+ int m_fp; // display page flash control
+
+ UINT16 m_csr; // cursor address register
+ int m_cd; // cursor increment direction
+ int m_crx; // cursor width
+ int m_cry; // cursor height or location
+ int m_cm; // cursor shape (0=underscore, 1=block)
+ int m_fc; // cursor flash control
+
+ int m_mx; // screen layer composition method
+ int m_dm; // display mode for pages 1, 3
+ int m_ov; // graphics mode layer composition
+
+ // address space configurations
+ const address_space_config m_space_config;
+};
+
+
+// device type definition
+extern const device_type SED1330;
+
+
+
+#endif
diff --git a/src/devices/video/sed1520.c b/src/devices/video/sed1520.c
new file mode 100644
index 00000000000..ba5c68145cd
--- /dev/null
+++ b/src/devices/video/sed1520.c
@@ -0,0 +1,164 @@
+// license:BSD-3-Clause
+// copyright-holders:Sandro Ronco
+/***************************************************************************
+
+ SED1520 LCD controller
+
+ TODO:
+ - busy flag
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/sed1520.h"
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type SED1520 = &device_creator<sed1520_device>;
+
+
+//**************************************************************************
+// live device
+//**************************************************************************
+
+//-------------------------------------------------
+// sed1520_device - constructor
+//-------------------------------------------------
+
+sed1520_device::sed1520_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, SED1520, "SED1520", tag, owner, clock, "sed1520", __FILE__),
+ m_screen_update_func(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void sed1520_device::device_start()
+{
+ // state saving
+ save_item(NAME(m_lcd_on));
+ save_item(NAME(m_busy));
+ save_item(NAME(m_page));
+ save_item(NAME(m_column));
+ save_item(NAME(m_old_column));
+ save_item(NAME(m_start_line));
+ save_item(NAME(m_adc));
+ save_item(NAME(m_static_drive));
+ save_item(NAME(m_modify_write));
+ save_item(NAME(m_vram));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void sed1520_device::device_reset()
+{
+ m_lcd_on = 0;
+ m_busy = 0;
+ m_page = 3;
+ m_column = 0;
+ m_old_column = 0;
+ m_start_line = 0;
+ m_adc = 1;
+ m_static_drive = 0;
+ m_modify_write = false;
+ memset(m_vram, 0x00, sizeof(m_vram));
+}
+
+
+//**************************************************************************
+// device interface
+//**************************************************************************
+
+UINT32 sed1520_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ if (m_lcd_on)
+ {
+ if (m_screen_update_func)
+ m_screen_update_func(*this, bitmap, cliprect, m_vram, m_start_line, m_adc);
+ }
+ else if (m_static_drive)
+ return UPDATE_HAS_NOT_CHANGED;
+ else
+ bitmap.fill(0, cliprect);
+
+ return 0;
+}
+
+READ8_MEMBER(sed1520_device::read)
+{
+ if (offset & 0x01)
+ return data_read(space, 0);
+ else
+ return status_read(space, 0);
+}
+
+WRITE8_MEMBER(sed1520_device::write)
+{
+ if (offset & 0x01)
+ data_write(space, 0, data);
+ else
+ control_write(space, 0, data);
+}
+
+WRITE8_MEMBER(sed1520_device::control_write)
+{
+ if((data & 0xfe) == 0xae) // display on/off
+ m_lcd_on = data & 0x01;
+ else if((data & 0xe0) == 0xc0) // set start line
+ m_start_line = data & 0x1f;
+ else if((data & 0xfc) == 0xb8) // set page address
+ m_page = data & 0x03;
+ else if((data & 0x80) == 0x00) // set column address
+ m_column = data % 80;
+ else if((data & 0xfe) == 0xa0) // select ADC
+ m_adc = data & 0x01;
+ else if((data & 0xfe) == 0xa4) // static drive on/off
+ m_static_drive = data & 0x01;
+ else if((data & 0xfe) == 0xa8) // select duty
+ ;
+ else if(data == 0xe0) // read-modify-write on
+ {
+ m_modify_write = true;
+ m_old_column = m_column;
+ }
+ else if(data == 0xee) // read-modify-write off
+ {
+ m_modify_write = false;
+ m_column = m_old_column;
+ }
+ else if(data == 0xe2) // reset
+ {
+ m_start_line = m_column = 0;
+ m_page = 3;
+ }
+ else
+ logerror("%s: invalid SED1520 command: %x\n", tag(), data);
+}
+
+READ8_MEMBER(sed1520_device::status_read)
+{
+ UINT8 data = (m_busy << 7) | (m_adc << 6) | (m_lcd_on << 5);
+ return data;
+}
+
+WRITE8_MEMBER(sed1520_device::data_write)
+{
+ m_vram[(m_page * 80 + m_column) % sizeof(m_vram)] = data;
+ m_column = (m_column + 1) % 80;
+}
+
+READ8_MEMBER(sed1520_device::data_read)
+{
+ UINT8 data = m_vram[(m_page * 80 + m_column) % sizeof(m_vram)];
+ if (!m_modify_write)
+ m_column = (m_column + 1) % 80;
+ return data;
+}
diff --git a/src/devices/video/sed1520.h b/src/devices/video/sed1520.h
new file mode 100644
index 00000000000..ebcc640314e
--- /dev/null
+++ b/src/devices/video/sed1520.h
@@ -0,0 +1,72 @@
+// license:BSD-3-Clause
+// copyright-holders:Sandro Ronco
+/***************************************************************************
+
+ SED1520 LCD controller
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __SED1520_H__
+#define __SED1520_H__
+
+
+#define MCFG_SED1520_ADD( _tag, _cb ) \
+ MCFG_DEVICE_ADD( _tag, SED1520, 0 ) \
+ sed1520_device::static_set_screen_update_cb(*device, _cb);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef UINT32 (*sed1520_screen_update_func)(device_t &device, bitmap_ind16 &bitmap, const rectangle &cliprect, UINT8 *vram, int start_line, int adc);
+#define SED1520_UPDATE_CB(name) UINT32 name(device_t &device, bitmap_ind16 &bitmap, const rectangle &cliprect, UINT8 *vram, int start_line, int adc)
+
+
+// ======================> sed1520_device
+
+class sed1520_device : public device_t
+{
+public:
+ // construction/destruction
+ sed1520_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ static void static_set_screen_update_cb(device_t &device, sed1520_screen_update_func _cb) { downcast<sed1520_device &>(device).m_screen_update_func = _cb; }
+
+ // device interface
+ virtual DECLARE_WRITE8_MEMBER(write);
+ virtual DECLARE_READ8_MEMBER(read);
+ virtual DECLARE_WRITE8_MEMBER(control_write);
+ virtual DECLARE_READ8_MEMBER(status_read);
+ virtual DECLARE_WRITE8_MEMBER(data_write);
+ virtual DECLARE_READ8_MEMBER(data_read);
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ // internal state
+ UINT8 m_lcd_on;
+ UINT8 m_busy;
+ UINT8 m_page;
+ UINT8 m_column;
+ UINT8 m_old_column;
+ UINT8 m_start_line;
+ UINT8 m_adc;
+ UINT8 m_static_drive;
+ bool m_modify_write;
+ sed1520_screen_update_func m_screen_update_func;
+
+ UINT8 m_vram[0x140];
+};
+
+
+// device type definition
+extern const device_type SED1520;
+
+#endif
diff --git a/src/devices/video/snes_ppu.c b/src/devices/video/snes_ppu.c
new file mode 100644
index 00000000000..93529ad307e
--- /dev/null
+++ b/src/devices/video/snes_ppu.c
@@ -0,0 +1,2949 @@
+// license:BSD-3-Clause
+// copyright-holders:Anthony Kruize, Fabio Priuli
+/***************************************************************************
+
+ snes.c
+
+ Video file to handle emulation of the Nintendo Super NES.
+
+ Anthony Kruize
+ Based on the original code by Lee Hammerton (aka Savoury Snax)
+
+ Some notes on the snes video hardware:
+
+ Object Attribute Memory(OAM) is made up of 128 blocks of 32 bits, followed
+ by 128 blocks of 2 bits. The format for each block is:
+ -First Block----------------------------------------------------------------
+ | x pos | y pos |char no.| v flip | h flip |priority|palette |char no msb|
+ +--------+--------+--------+--------+--------+--------+--------+-----------+
+ | 8 bits | 8 bits | 8 bits | 1 bit | 1 bit | 2 bits | 3 bits | 1 bit |
+ -Second Block---------------------------------------------------------------
+ | size | x pos msb |
+ +-------+-----------+
+ | 1 bit | 1 bit |
+ ---------------------
+
+ Video RAM contains information for character data and screen maps.
+ Screen maps are made up of 32 x 32 blocks of 16 bits each.
+ The format for each block is:
+ ----------------------------------------------
+ | v flip | x flip |priority|palette |char no.|
+ +--------+--------+--------+--------+--------+
+ | 1 bit | 1 bit | 1 bit | 3 bits |10 bits |
+ ----------------------------------------------
+ Mode 7 is stored differently. Character data and screen map are interleaved.
+ There are two formats:
+ -Normal----------------- -EXTBG-----------------------------
+ | char data | char no. | | priority | char data | char no. |
+ +-----------+----------+ +----------+-----------+----------+
+ | 8 bits | 8 bits | | 1 bit | 7 bits | 8 bits |
+ ------------------------ -----------------------------------
+
+ The screen layers are drawn with the following priorities (updated info courtesy of byuu):
+
+ | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 0 | BG4B | BG3B | OAM0 | BG4A | BG3A | OAM1 | BG2B | BG1B | OAM2 | BG2A | BG1A | OAM3 |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 1 (*)| BG3B | OAM0 | OAM1 | BG2B | BG1B | OAM2 | BG2A | BG1A | OAM3 | BG3A | | |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 1 (!)| BG3B | OAM0 | BG3A | OAM1 | BG2B | BG1B | OAM2 | BG2A | BG1A | OAM3 | | |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 2 | BG2B | OAM0 | BG1B | OAM1 | BG2A | OAM2 | BG1A | OAM3 | | | | |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 3 | BG2B | OAM0 | BG1B | OAM1 | BG2A | OAM2 | BG1A | OAM3 | | | | |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 4 | BG2B | OAM0 | BG1B | OAM1 | BG2A | OAM2 | BG1A | OAM3 | | | | |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 5 | BG2B | OAM0 | BG1B | OAM1 | BG2A | OAM2 | BG1A | OAM3 | | | | |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 6 | OAM0 | BG1B | OAM1 | OAM2 | BG1A | OAM3 | | | | | | |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 7 (+)| OAM0 | BG1n | OAM1 | OAM2 | OAM3 | | | | | | | |
+ -------------------------------------------------------------------------------------------------------------
+ | Mode 7 (-)| BG2B | OAM0 | BG1n | OAM1 | BG2A | OAM2 | OAM3 | | | | | |
+ -------------------------------------------------------------------------------------------------------------
+
+ Where:
+ - Mode 1 (*) is Mode 1 with bg3_pty = 1
+ - Mode 1 (!) is Mode 1 with bg3_pty = 0
+ - Mode 7 (+) is base Mode 7
+ - Mode 7 (-) is Mode 7 EXTBG
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/snes_ppu.h"
+
+#define SNES_MAINSCREEN 0
+#define SNES_SUBSCREEN 1
+#define SNES_CLIP_NEVER 0
+#define SNES_CLIP_IN 1
+#define SNES_CLIP_OUT 2
+#define SNES_CLIP_ALWAYS 3
+
+#define SNES_VRAM_SIZE 0x20000 /* 128kb of video ram */
+#define SNES_CGRAM_SIZE 0x202 /* 256 16-bit colours + 1 tacked on 16-bit colour for fixed colour */
+#define SNES_OAM_SIZE 0x440 /* 1088 bytes of Object Attribute Memory */
+#define FIXED_COLOUR 256 /* Position in cgram for fixed colour */
+
+
+/* Definitions for PPU Memory-Mapped registers */
+#define INIDISP 0x2100
+#define OBSEL 0x2101
+#define OAMADDL 0x2102
+#define OAMADDH 0x2103
+#define OAMDATA 0x2104
+#define BGMODE 0x2105 /* abcdefff = abcd: bg4-1 tile size | e: BG3 high priority | f: mode */
+#define MOSAIC 0x2106 /* xxxxabcd = x: pixel size | abcd: affects bg 1-4 */
+#define BG1SC 0x2107
+#define BG2SC 0x2108
+#define BG3SC 0x2109
+#define BG4SC 0x210A
+#define BG12NBA 0x210B
+#define BG34NBA 0x210C
+#define BG1HOFS 0x210D
+#define BG1VOFS 0x210E
+#define BG2HOFS 0x210F
+#define BG2VOFS 0x2110
+#define BG3HOFS 0x2111
+#define BG3VOFS 0x2112
+#define BG4HOFS 0x2113
+#define BG4VOFS 0x2114
+#define VMAIN 0x2115 /* i---ffrr = i: Increment timing | f: Full graphic | r: increment rate */
+#define VMADDL 0x2116 /* aaaaaaaa = a: LSB of vram address */
+#define VMADDH 0x2117 /* aaaaaaaa = a: MSB of vram address */
+#define VMDATAL 0x2118 /* dddddddd = d: data to be written */
+#define VMDATAH 0x2119 /* dddddddd = d: data to be written */
+#define M7SEL 0x211A /* ab----yx = a: screen over | y: vertical flip | x: horizontal flip */
+#define M7A 0x211B /* aaaaaaaa = a: COSINE rotate angle / X expansion */
+#define M7B 0x211C /* aaaaaaaa = a: SINE rotate angle / X expansion */
+#define M7C 0x211D /* aaaaaaaa = a: SINE rotate angle / Y expansion */
+#define M7D 0x211E /* aaaaaaaa = a: COSINE rotate angle / Y expansion */
+#define M7X 0x211F
+#define M7Y 0x2120
+#define CGADD 0x2121
+#define CGDATA 0x2122
+#define W12SEL 0x2123
+#define W34SEL 0x2124
+#define WOBJSEL 0x2125
+#define WH0 0x2126 /* pppppppp = p: Left position of window 1 */
+#define WH1 0x2127 /* pppppppp = p: Right position of window 1 */
+#define WH2 0x2128 /* pppppppp = p: Left position of window 2 */
+#define WH3 0x2129 /* pppppppp = p: Right position of window 2 */
+#define WBGLOG 0x212A /* aabbccdd = a: BG4 params | b: BG3 params | c: BG2 params | d: BG1 params */
+#define WOBJLOG 0x212B /* ----ccoo = c: Colour window params | o: Object window params */
+#define TM 0x212C
+#define TS 0x212D
+#define TMW 0x212E
+#define TSW 0x212F
+#define CGWSEL 0x2130
+#define CGADSUB 0x2131
+#define COLDATA 0x2132
+#define SETINI 0x2133
+#define MPYL 0x2134
+#define MPYM 0x2135
+#define MPYH 0x2136
+#define SLHV 0x2137
+#define ROAMDATA 0x2138
+#define RVMDATAL 0x2139
+#define RVMDATAH 0x213A
+#define RCGDATA 0x213B
+#define OPHCT 0x213C
+#define OPVCT 0x213D
+#define STAT77 0x213E
+#define STAT78 0x213F
+
+
+#if SNES_LAYER_DEBUG
+/* red green blue purple yellow cyan grey white */
+static const UINT16 dbg_mode_colours[8] = { 0x1f, 0x3e0, 0x7c00, 0x7c1f, 0x3ff, 0x7fe0, 0x4210, 0x7fff };
+#endif /* SNES_LAYER_DEBUG */
+
+static const UINT16 table_obj_offset[8][8] =
+{
+ { (0*32), (0*32)+32, (0*32)+64, (0*32)+96, (0*32)+128, (0*32)+160, (0*32)+192, (0*32)+224 },
+ { (16*32), (16*32)+32, (16*32)+64, (16*32)+96, (16*32)+128, (16*32)+160, (16*32)+192, (16*32)+224 },
+ { (32*32), (32*32)+32, (32*32)+64, (32*32)+96, (32*32)+128, (32*32)+160, (32*32)+192, (32*32)+224 },
+ { (48*32), (48*32)+32, (48*32)+64, (48*32)+96, (48*32)+128, (48*32)+160, (48*32)+192, (48*32)+224 },
+ { (64*32), (64*32)+32, (64*32)+64, (64*32)+96, (64*32)+128, (64*32)+160, (64*32)+192, (64*32)+224 },
+ { (80*32), (80*32)+32, (80*32)+64, (80*32)+96, (80*32)+128, (80*32)+160, (80*32)+192, (80*32)+224 },
+ { (96*32), (96*32)+32, (96*32)+64, (96*32)+96, (96*32)+128, (96*32)+160, (96*32)+192, (96*32)+224 },
+ { (112*32), (112*32)+32, (112*32)+64, (112*32)+96, (112*32)+128, (112*32)+160, (112*32)+192, (112*32)+224 }
+};
+
+
+enum
+{
+ SNES_COLOR_DEPTH_2BPP = 0,
+ SNES_COLOR_DEPTH_4BPP,
+ SNES_COLOR_DEPTH_8BPP
+};
+
+
+#define PPU_REG(a) m_regs[a - 0x2100]
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type SNES_PPU = &device_creator<snes_ppu_device>;
+
+
+//**************************************************************************
+// live device
+//**************************************************************************
+
+//-------------------------------------------------
+// snes_ppu_device - constructor
+//-------------------------------------------------
+
+snes_ppu_device::snes_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SNES_PPU, "SNES PPU", tag, owner, clock, "snes_ppu", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_openbus_cb(*this)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void snes_ppu_device::device_start()
+{
+ m_openbus_cb.resolve_safe(0);
+
+ m_vram = auto_alloc_array(machine(), UINT8, SNES_VRAM_SIZE);
+ m_cgram = auto_alloc_array(machine(), UINT16, SNES_CGRAM_SIZE/2);
+ m_oam_ram = auto_alloc_array(machine(), UINT16, SNES_OAM_SIZE/2);
+
+ for (int i = 0; i < 2; i++)
+ {
+ save_item(NAME(m_scanlines[i].enable), i);
+ save_item(NAME(m_scanlines[i].clip), i);
+ save_item(NAME(m_scanlines[i].buffer), i);
+ save_item(NAME(m_scanlines[i].priority), i);
+ save_item(NAME(m_scanlines[i].layer), i);
+ save_item(NAME(m_scanlines[i].blend_exception), i);
+ }
+
+ for (int i = 0; i < 6; i++)
+ {
+ save_item(NAME(m_layer[i].window1_enabled), i);
+ save_item(NAME(m_layer[i].window1_invert), i);
+ save_item(NAME(m_layer[i].window2_enabled), i);
+ save_item(NAME(m_layer[i].window2_invert), i);
+ save_item(NAME(m_layer[i].wlog_mask), i);
+ save_item(NAME(m_layer[i].color_math), i);
+ save_item(NAME(m_layer[i].charmap), i);
+ save_item(NAME(m_layer[i].tilemap), i);
+ save_item(NAME(m_layer[i].tilemap_size), i);
+ save_item(NAME(m_layer[i].tile_size), i);
+ save_item(NAME(m_layer[i].mosaic_enabled), i);
+ save_item(NAME(m_layer[i].main_window_enabled), i);
+ save_item(NAME(m_layer[i].sub_window_enabled), i);
+ save_item(NAME(m_layer[i].main_bg_enabled), i);
+ save_item(NAME(m_layer[i].sub_bg_enabled), i);
+ save_item(NAME(m_layer[i].hoffs), i);
+ save_item(NAME(m_layer[i].voffs), i);
+
+ save_item(NAME(m_clipmasks[i]), i);
+ }
+
+ save_item(NAME(m_oam.address_low));
+ save_item(NAME(m_oam.address_high));
+ save_item(NAME(m_oam.saved_address_low));
+ save_item(NAME(m_oam.saved_address_high));
+ save_item(NAME(m_oam.address));
+ save_item(NAME(m_oam.priority_rotation));
+ save_item(NAME(m_oam.next_charmap));
+ save_item(NAME(m_oam.next_size));
+ save_item(NAME(m_oam.size));
+ save_item(NAME(m_oam.next_name_select));
+ save_item(NAME(m_oam.name_select));
+ save_item(NAME(m_oam.first_sprite));
+ save_item(NAME(m_oam.flip));
+ save_item(NAME(m_oam.write_latch));
+
+ save_item(NAME(m_beam.latch_horz));
+ save_item(NAME(m_beam.latch_vert));
+ save_item(NAME(m_beam.current_vert));
+ save_item(NAME(m_beam.last_visible_line));
+ save_item(NAME(m_beam.interlace_count));
+
+ save_item(NAME(m_mode7.repeat));
+ save_item(NAME(m_mode7.hflip));
+ save_item(NAME(m_mode7.vflip));
+ save_item(NAME(m_mode7.matrix_a));
+ save_item(NAME(m_mode7.matrix_b));
+ save_item(NAME(m_mode7.matrix_c));
+ save_item(NAME(m_mode7.matrix_d));
+ save_item(NAME(m_mode7.origin_x));
+ save_item(NAME(m_mode7.origin_y));
+ save_item(NAME(m_mode7.hor_offset));
+ save_item(NAME(m_mode7.ver_offset));
+ save_item(NAME(m_mode7.extbg));
+
+ for (int i = 0; i < ARRAY_LENGTH(m_oam_spritelist); i++)
+ {
+ save_item(NAME(m_oam_spritelist[i].tile), i);
+ save_item(NAME(m_oam_spritelist[i].x), i);
+ save_item(NAME(m_oam_spritelist[i].y), i);
+ save_item(NAME(m_oam_spritelist[i].size), i);
+ save_item(NAME(m_oam_spritelist[i].vflip), i);
+ save_item(NAME(m_oam_spritelist[i].hflip), i);
+ save_item(NAME(m_oam_spritelist[i].priority_bits), i);
+ save_item(NAME(m_oam_spritelist[i].pal), i);
+ save_item(NAME(m_oam_spritelist[i].height), i);
+ save_item(NAME(m_oam_spritelist[i].width), i);
+ }
+
+ for (int i = 0; i < ARRAY_LENGTH(m_oam_tilelist); i++)
+ {
+ save_item(NAME(m_oam_tilelist[i].x), i);
+ save_item(NAME(m_oam_tilelist[i].priority), i);
+ save_item(NAME(m_oam_tilelist[i].pal), i);
+ save_item(NAME(m_oam_tilelist[i].tileaddr), i);
+ save_item(NAME(m_oam_tilelist[i].hflip), i);
+ }
+
+ save_item(NAME(m_mosaic_size));
+ save_item(NAME(m_clip_to_black));
+ save_item(NAME(m_prevent_color_math));
+ save_item(NAME(m_sub_add_mode));
+ save_item(NAME(m_bg3_priority_bit));
+ save_item(NAME(m_direct_color));
+ save_item(NAME(m_ppu_last_scroll));
+ save_item(NAME(m_mode7_last_scroll));
+
+ save_item(NAME(m_ppu1_open_bus));
+ save_item(NAME(m_ppu2_open_bus));
+ save_item(NAME(m_ppu1_version));
+ save_item(NAME(m_ppu2_version));
+ save_item(NAME(m_window1_left));
+ save_item(NAME(m_window1_right));
+ save_item(NAME(m_window2_left));
+ save_item(NAME(m_window2_right));
+
+ save_item(NAME(m_update_windows));
+ save_item(NAME(m_update_offsets));
+ save_item(NAME(m_update_oam_list));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_interlace));
+ save_item(NAME(m_obj_interlace));
+ save_item(NAME(m_screen_brightness));
+ save_item(NAME(m_screen_disabled));
+ save_item(NAME(m_pseudo_hires));
+ save_item(NAME(m_color_modes));
+ save_item(NAME(m_stat77));
+ save_item(NAME(m_stat78));
+
+ save_item(NAME(m_htmult));
+ save_item(NAME(m_cgram_address));
+ save_item(NAME(m_read_ophct));
+ save_item(NAME(m_read_opvct));
+ save_item(NAME(m_vram_fgr_high));
+ save_item(NAME(m_vram_fgr_increment));
+ save_item(NAME(m_vram_fgr_count));
+ save_item(NAME(m_vram_fgr_mask));
+ save_item(NAME(m_vram_fgr_shift));
+ save_item(NAME(m_vram_read_buffer));
+ save_item(NAME(m_vmadd));
+
+ save_item(NAME(m_regs));
+
+ save_pointer(NAME(m_vram), SNES_VRAM_SIZE);
+ save_pointer(NAME(m_cgram), SNES_CGRAM_SIZE/2);
+ save_pointer(NAME(m_oam_ram), SNES_OAM_SIZE/2);
+}
+
+void snes_ppu_device::device_reset()
+{
+#if SNES_LAYER_DEBUG
+ memset(&m_debug_options, 0, sizeof(m_debug_options));
+#endif
+
+ /* Inititialize registers/variables */
+ m_update_windows = 1;
+ m_beam.latch_vert = 0;
+ m_beam.latch_horz = 0;
+ m_beam.current_vert = 0;
+ m_beam.last_visible_line = 225; /* TODO: PAL setting */
+ m_mode = 0;
+ m_ppu1_version = 1; // 5C77 chip version number, read by STAT77, only '1' is known
+ m_ppu2_version = 3; // 5C78 chip version number, read by STAT78, only '2' & '3' encountered so far.
+
+ m_cgram_address = 0;
+ m_read_ophct = 0;
+ m_read_opvct = 0;
+
+ m_vmadd = 0;
+
+ PPU_REG(VMAIN) = 0x80;
+ // what about other regs?
+
+ /* Inititialize mosaic table */
+ for (int j = 0; j < 16; j++)
+ {
+ for (int i = 0; i < 4096; i++)
+ m_mosaic_table[j][i] = (i / (j + 1)) * (j + 1);
+ }
+
+ /* Init VRAM */
+ memset(m_vram, 0, SNES_VRAM_SIZE);
+
+ /* Init Palette RAM */
+ memset((UINT8 *)m_cgram, 0, SNES_CGRAM_SIZE);
+
+ /* Init oam RAM */
+ memset((UINT8 *)m_oam_ram, 0xff, SNES_OAM_SIZE);
+
+ m_stat78 = 0;
+
+ // other initializations to 0
+ memset(m_regs, 0, sizeof(m_regs));
+ memset(m_oam_itemlist, 0, sizeof(m_oam_itemlist));
+ memset(&m_oam, 0, sizeof(m_oam));
+ memset(&m_mode7, 0, sizeof(m_mode7));
+
+ for (int i = 0; i < 2; i++)
+ {
+ m_scanlines[i].enable = 0;
+ m_scanlines[i].clip = 0;
+ memset(m_scanlines[i].buffer, 0, SNES_SCR_WIDTH);
+ memset(m_scanlines[i].priority, 0, SNES_SCR_WIDTH);
+ memset(m_scanlines[i].layer, 0, SNES_SCR_WIDTH);
+ memset(m_scanlines[i].blend_exception, 0, SNES_SCR_WIDTH);
+ }
+
+ for (int i = 0; i < 6; i++)
+ {
+ m_layer[i].window1_enabled = 0;
+ m_layer[i].window1_invert = 0;
+ m_layer[i].window2_enabled = 0;
+ m_layer[i].window2_invert = 0;
+ m_layer[i].wlog_mask = 0;
+ m_layer[i].color_math = 0;
+ m_layer[i].charmap = 0;
+ m_layer[i].tilemap = 0;
+ m_layer[i].tilemap_size = 0;
+ m_layer[i].tile_size = 0;
+ m_layer[i].mosaic_enabled = 0;
+ m_layer[i].main_window_enabled = 0;
+ m_layer[i].sub_window_enabled = 0;
+ m_layer[i].main_bg_enabled = 0;
+ m_layer[i].sub_bg_enabled = 0;
+ m_layer[i].hoffs = 0;
+ m_layer[i].voffs = 0;
+
+ memset(m_clipmasks[i], 0, SNES_SCR_WIDTH);
+ }
+
+ for (int i = 0; i < ARRAY_LENGTH(m_oam_spritelist); i++)
+ {
+ m_oam_spritelist[i].tile = 0;
+ m_oam_spritelist[i].x = 0;
+ m_oam_spritelist[i].y = 0;
+ m_oam_spritelist[i].size = 0;
+ m_oam_spritelist[i].vflip = 0;
+ m_oam_spritelist[i].hflip = 0;
+ m_oam_spritelist[i].priority_bits = 0;
+ m_oam_spritelist[i].pal = 0;
+ m_oam_spritelist[i].height = 0;
+ m_oam_spritelist[i].width = 0;
+ }
+
+ for (int i = 0; i < ARRAY_LENGTH(m_oam_tilelist); i++)
+ {
+ m_oam_tilelist[i].x = 0;
+ m_oam_tilelist[i].priority = 0;
+ m_oam_tilelist[i].pal = 0;
+ m_oam_tilelist[i].tileaddr = 0;
+ m_oam_tilelist[i].hflip = 0;
+ }
+}
+
+/*****************************************
+ * get_bgcolor()
+ *
+ * Get the proper color (direct or from cgram)
+ *****************************************/
+
+inline UINT16 snes_ppu_device::get_bgcolor( UINT8 direct_colors, UINT16 palette, UINT8 color )
+{
+ UINT16 c = 0;
+
+ if (direct_colors)
+ {
+ /* format is 0 | BBb00 | GGGg0 | RRRr0, HW confirms that the data is zero padded. */
+ c = ((color & 0x07) << 2) | ((color & 0x38) << 4) | ((color & 0xc0) << 7);
+ c |= ((palette & 0x04) >> 1) | ((palette & 0x08) << 3) | ((palette & 0x10) << 8);
+ }
+ else
+ c = m_cgram[(palette + color) % FIXED_COLOUR];
+
+ return c;
+}
+
+/*****************************************
+ * set_scanline_pixel()
+ *
+ * Store pixel color, priority, layer and
+ * color math exception (for OAM) in the
+ * proper scanline
+ *****************************************/
+
+inline void snes_ppu_device::set_scanline_pixel( int screen, INT16 x, UINT16 color, UINT8 priority, UINT8 layer, int blend )
+{
+ m_scanlines[screen].buffer[x] = color;
+ m_scanlines[screen].priority[x] = priority;
+ m_scanlines[screen].layer[x] = layer;
+ m_scanlines[screen].blend_exception[x] = blend;
+}
+
+/*************************************************************************************************
+ * SNES tiles
+ *
+ * The way vram is accessed to draw tiles is basically the same for both BG and OAM tiles. Main
+ * differences are bit planes (variable for BG and fixed for OAM) and a few details of the scanline
+ * output (since OAM has neither mosaic, nor hires, nor direct colors).
+ * Hence, we use a common function to take data from VRAM and then we call specific routines for
+ * OAM vs BG vs Hi-Res BG tiles.
+ *************************************************************************************************/
+
+/*****************************************
+ * draw_bgtile_lores()
+ * draw_bgtile_hires()
+ * draw_oamtile_()
+ *
+ * Check if a pixel is clipped or not, and
+ * copy it to the scanline buffer when
+ * appropriate. The actual way to perform
+ * such operations depends on the source
+ * (BG or OAM) and on the resolution (hires
+ * or lores)
+ *****************************************/
+
+inline void snes_ppu_device::draw_bgtile_lores( UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority )
+{
+ int screen;
+ UINT16 c;
+
+ for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
+ {
+ if (ii >= 0 && ii < SNES_SCR_WIDTH && m_scanlines[screen].enable)
+ {
+ if (m_scanlines[screen].priority[ii] <= priority)
+ {
+ UINT8 clr = colour;
+ UINT8 clipmask = m_clipmasks[layer][ii];
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.windows_disabled)
+ clipmask = 0xff;
+#endif /* SNES_LAYER_DEBUG */
+
+ /* Clip to windows */
+ if (m_scanlines[screen].clip)
+ clr &= clipmask;
+
+ /* Only draw if we have a colour (0 == transparent) */
+ if (clr)
+ {
+ c = get_bgcolor(direct_colors, pal, clr);
+ set_scanline_pixel(screen, ii, c, priority, layer, 0);
+ }
+ }
+ }
+ }
+}
+
+inline void snes_ppu_device::draw_bgtile_hires( UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority )
+{
+ int screen;
+ UINT16 c;
+
+ for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
+ {
+ // odd pixels to main screen, even pixels to sub screen
+ if (ii >= 0 && ii < (SNES_SCR_WIDTH << 1) && ((ii & 1) ^ screen) && m_scanlines[screen].enable)
+ {
+ if (m_scanlines[screen].priority[ii >> 1] <= priority)
+ {
+ UINT8 clr = colour;
+ UINT8 clipmask = m_clipmasks[layer][ii >> 1];
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.windows_disabled)
+ clipmask = 0xff;
+#endif /* SNES_LAYER_DEBUG */
+
+ /* Clip to windows */
+ if (m_scanlines[screen].clip)
+ clr &= clipmask;
+
+ /* Only draw if we have a colour (0 == transparent) */
+ if (clr)
+ {
+ c = get_bgcolor(direct_colors, pal, clr);
+ set_scanline_pixel(screen, ii >> 1, c, priority, layer, 0);
+ }
+ }
+ }
+ }
+}
+
+inline void snes_ppu_device::draw_oamtile( INT16 ii, UINT8 colour, UINT16 pal, UINT8 priority )
+{
+ int screen;
+ int blend;
+ UINT16 c;
+ INT16 pos = ii & 0x1ff;
+
+ for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
+ {
+ if (pos >= 0 && pos < SNES_SCR_WIDTH && m_scanlines[screen].enable)
+ {
+ UINT8 clr = colour;
+ UINT8 clipmask = m_clipmasks[SNES_OAM][pos];
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.windows_disabled)
+ clipmask = 0xff;
+#endif /* SNES_LAYER_DEBUG */
+
+ /* Clip to windows */
+ if (m_scanlines[screen].clip)
+ clr &= clipmask;
+
+ /* Only draw if we have a colour (0 == transparent) */
+ if (clr)
+ {
+ c = m_cgram[(pal + clr) % FIXED_COLOUR];
+ blend = (pal + clr < 192) ? 1 : 0;
+ set_scanline_pixel(screen, pos, c, priority, SNES_OAM, blend);
+ }
+ }
+ }
+}
+
+/*****************************************
+ * draw_tile()
+ *
+ * Draw 8 pixels from the expected tile
+ * by reading the color planes from vram
+ * and by calling the appropriate routine
+ * (depending on layer and resolution)
+ *****************************************/
+
+inline void snes_ppu_device::draw_tile( UINT8 planes, UINT8 layer, UINT32 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires )
+{
+ UINT8 plane[8];
+ INT16 ii, jj;
+ int x_mos;
+
+ for (ii = 0; ii < planes / 2; ii++)
+ {
+ plane[2 * ii + 0] = m_vram[(tileaddr + 16 * ii + 0) % SNES_VRAM_SIZE];
+ plane[2 * ii + 1] = m_vram[(tileaddr + 16 * ii + 1) % SNES_VRAM_SIZE];
+ }
+
+ for (ii = x; ii < (x + 8); ii++)
+ {
+ UINT8 colour = 0;
+ UINT8 mosaic = m_layer[layer].mosaic_enabled;
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mosaic_disabled)
+ mosaic = 0;
+#endif /* SNES_LAYER_DEBUG */
+
+ if (flip)
+ {
+ for (jj = 0; jj < planes; jj++)
+ colour |= BIT(plane[jj], ii - x) ? (1 << jj) : 0;
+ }
+ else
+ {
+ for (jj = 0; jj < planes; jj++)
+ colour |= BIT(plane[jj], 7 - (ii - x)) ? (1 << jj) : 0;
+ }
+
+ if (layer == SNES_OAM)
+ draw_oamtile(ii, colour, pal, priority);
+ else if (!hires)
+ {
+ if (mosaic)
+ {
+ for (x_mos = 0; x_mos < (m_mosaic_size + 1); x_mos++)
+ draw_bgtile_lores(layer, ii + x_mos, colour, pal, direct_colors, priority);
+ ii += x_mos - 1;
+ }
+ else
+ draw_bgtile_lores(layer, ii, colour, pal, direct_colors, priority);
+ }
+ else /* hires */
+ {
+ if (mosaic)
+ {
+ for (x_mos = 0; x_mos < (m_mosaic_size + 1); x_mos++)
+ draw_bgtile_hires(layer, ii + x_mos, colour, pal, direct_colors, priority);
+ ii += x_mos - 1;
+ }
+ else
+ draw_bgtile_hires(layer, ii, colour, pal, direct_colors, priority);
+ }
+ }
+}
+
+/*************************************************************************************************
+ * SNES BG layers
+ *
+ * BG drawing theory of each scanline is quite easy: depending on the graphics Mode (0-7), there
+ * are up to 4 background layers. Pixels for each BG layer can have two different priorities.
+ * Depending on the line and on the BGHOFS and BGVOFS PPU registers, we first determine the tile
+ * address in m_vram (by determining x,y coord and tile size and by calling get_tmap_addr).
+ * Then, we load the correspondent data and we determine the tile properties: which priority to
+ * use, which palette etc. Finally, for each pixel of the tile appearing on screen, we check if
+ * the tile priority is higher than the BG/OAM already stored in that pixel for that line. If so
+ * we store the pixel in the buffer, otherwise we discard it.
+ *
+ * Of course, depending on the graphics Mode, it might be easier or harder to determine the proper
+ * tile address in vram (Mode 7 uses different registers, Mode 2, 4 and 6 uses OPT effect, etc.),
+ * but in general it works as described.
+ *************************************************************************************************/
+
+/*********************************************
+ * get_tmap_addr()
+ *
+ * Find the address in VRAM of the tile (x,y)
+ *********************************************/
+
+inline UINT32 snes_ppu_device::get_tmap_addr( UINT8 layer, UINT8 tile_size, UINT32 base, UINT32 x, UINT32 y )
+{
+ UINT32 res = base;
+ x >>= (3 + tile_size);
+ y >>= (3 + tile_size);
+
+ res += (m_layer[layer].tilemap_size & 2) ? ((y & 0x20) << ((m_layer[layer].tilemap_size & 1) ? 7 : 6)) : 0;
+ /* Scroll vertically */
+ res += (y & 0x1f) << 6;
+ /* Offset horizontally */
+ res += (m_layer[layer].tilemap_size & 1) ? ((x & 0x20) << 6) : 0;
+ /* Scroll horizontally */
+ res += (x & 0x1f) << 1;
+
+ return res;
+}
+
+/*********************************************
+ * update_line()
+ *
+ * Update an entire line of tiles.
+ *********************************************/
+
+inline void snes_ppu_device::update_line( UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a, UINT8 color_depth, UINT8 hires, UINT8 offset_per_tile, UINT8 direct_colors )
+{
+ UINT32 tmap, tile, xoff, yoff, charaddr, addr;
+ UINT16 ii = 0, vflip, hflip, pal, pal_direct, tilemap;
+ UINT8 xscroll, priority;
+ INT8 yscroll;
+ int tile_incr = 0;
+ UINT16 opt_bit = (layer == SNES_BG1) ? 13 : (layer == SNES_BG2) ? 14 : 0;
+ UINT8 tile_size;
+ /* variables depending on color_depth */
+ UINT8 color_planes = 2 << color_depth;
+ /* below we cheat to simplify the code: 8BPP should have 0 pal offset, not 0x100 (but we take care of this by later using pal % FIXED_COLOUR) */
+ UINT8 color_shift = 2 << color_depth;
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.bg_disabled[layer])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ m_scanlines[SNES_MAINSCREEN].enable = m_layer[layer].main_bg_enabled;
+ m_scanlines[SNES_SUBSCREEN].enable = m_layer[layer].sub_bg_enabled;
+ m_scanlines[SNES_MAINSCREEN].clip = m_layer[layer].main_window_enabled;
+ m_scanlines[SNES_SUBSCREEN].clip = m_layer[layer].sub_window_enabled;
+
+ if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
+ return;
+
+ /* Handle Mosaic effects */
+ if (m_layer[layer].mosaic_enabled)
+ curline -= (curline % (m_mosaic_size + 1));
+
+ if ((m_interlace == 2) && !hires && !m_pseudo_hires)
+ curline /= 2;
+
+ /* Find the size of the tiles (8x8 or 16x16) */
+ tile_size = m_layer[layer].tile_size;
+
+ /* Find scroll info */
+ xoff = m_layer[layer].hoffs;
+ yoff = m_layer[layer].voffs;
+
+ xscroll = xoff & ((1 << (3 + tile_size)) - 1);
+
+ /* Jump to base map address */
+ tmap = m_layer[layer].tilemap << 9;
+ charaddr = m_layer[layer].charmap << 13;
+
+ while (ii < 256 + (8 << tile_size))
+ {
+ // determine the horizontal position (Bishojo Janshi Suchi Pai & Desert Figther have tile_size & hires == 1)
+ UINT32 xpos = xoff + (ii << (tile_size * hires));
+ UINT32 ypos = yoff + curline;
+
+ if (offset_per_tile != SNES_OPT_NONE)
+ {
+ int opt_x = ii + (xoff & 7);
+ UINT32 haddr = 0, vaddr = 0;
+ UINT16 hval = 0, vval = 0;
+
+ if (opt_x >= 8)
+ {
+ switch (offset_per_tile)
+ {
+ case SNES_OPT_MODE2:
+ case SNES_OPT_MODE6:
+ haddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff));
+ vaddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff) + 8);
+ hval = m_vram[haddr % SNES_VRAM_SIZE] | (m_vram[(haddr + 1) % SNES_VRAM_SIZE] << 8);
+ vval = m_vram[vaddr % SNES_VRAM_SIZE] | (m_vram[(vaddr + 1) % SNES_VRAM_SIZE] << 8);
+ if (BIT(hval, opt_bit))
+ xpos = opt_x + (hval & ~7);
+ if (BIT(vval, opt_bit))
+ ypos = curline + vval;
+ break;
+ case SNES_OPT_MODE4:
+ haddr = get_tmap_addr(SNES_BG3, m_layer[SNES_BG3].tile_size, m_layer[SNES_BG3].tilemap << 9, (opt_x - 8) + ((m_layer[SNES_BG3].hoffs & 0x3ff) & ~7), (m_layer[SNES_BG3].voffs & 0x3ff));
+ hval = m_vram[haddr % SNES_VRAM_SIZE] | (m_vram[(haddr + 1) % SNES_VRAM_SIZE] << 8);
+ if (BIT(hval, opt_bit))
+ {
+ if (!BIT(hval, 15))
+ xpos = opt_x + (hval & ~7);
+ else
+ ypos = curline + hval;
+ }
+ break;
+ }
+ }
+ }
+
+ addr = get_tmap_addr(layer, tile_size, tmap, xpos, ypos);
+
+ /*
+ Tilemap format
+ vhopppcc cccccccc
+
+ v/h = Vertical/Horizontal flip this tile.
+ o = Tile priority.
+ ppp = Tile palette. The number of entries in the palette depends on the Mode and the BG.
+ cccccccccc = Tile number.
+ */
+ tilemap = m_vram[addr % SNES_VRAM_SIZE] | (m_vram[(addr + 1) % SNES_VRAM_SIZE] << 8);
+ vflip = BIT(tilemap, 15);
+ hflip = BIT(tilemap, 14);
+ priority = BIT(tilemap, 13) ? priority_a : priority_b;
+ pal_direct = ((tilemap & 0x1c00) >> 8);
+ tile = tilemap & 0x03ff;
+
+ pal = ((pal_direct >> 2) << color_shift);
+
+ /* Mode 0 palettes are layer specific */
+ if (m_mode == 0)
+ {
+ pal += (layer << 5);
+ }
+
+#if SNES_LAYER_DEBUG
+ /* if we want to draw only one of the priorities of this layer */
+ if (((m_debug_options.select_pri[layer] & 0x01) && (priority == priority_a)) ||
+ ((m_debug_options.select_pri[layer] & 0x02) && (priority == priority_b)))
+ {
+ if (!hires && tile_size)
+ ii += 16;
+ else
+ ii += 8;
+ continue;
+ }
+#endif /* SNES_LAYER_DEBUG */
+
+ /* figure out which line to draw */
+ yscroll = ypos & ((8 << tile_size) - 1);
+
+ if (tile_size)
+ if (BIT(yscroll, 3) != vflip)
+ tile += 16;
+
+ if (yscroll > 7)
+ yscroll &= 7;
+
+ if (vflip)
+ yscroll = 7 - yscroll;
+
+ yscroll <<= 1;
+
+ /* if we have to draw 16 pixels, set tile_incr and adjust tile for horizontal flip */
+ if (tile_size || hires)
+ {
+ if (hflip)
+ {
+ tile += 1;
+ tile_incr = -1; // next 8 pixels from previous tile (because of hflip)
+ }
+ else
+ tile_incr = 1; // next 8 pixels from next tile
+ }
+
+ if (hires)
+ {
+ /* draw 16 pixels (the routine will automatically send half of them to the mainscreen scanline and half to the subscreen one) */
+ draw_tile(color_planes, layer, charaddr + (((tile + 0) & 0x3ff) * 8 * color_planes) + yscroll, (ii - xscroll) * 2, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
+ draw_tile(color_planes, layer, charaddr + (((tile + tile_incr) & 0x3ff) * 8 * color_planes) + yscroll, (ii - xscroll) * 2 + 8, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
+ ii += 8;
+ }
+ else
+ {
+ draw_tile(color_planes, layer, charaddr + ((tile & 0x3ff) * 8 * color_planes) + yscroll, ii - xscroll, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
+ ii += 8;
+
+ if (tile_size)
+ {
+ draw_tile(color_planes, layer, charaddr + (((tile + tile_incr) & 0x3ff) * 8 * color_planes) + yscroll, ii - xscroll, priority, hflip, direct_colors, direct_colors ? pal_direct : pal, hires);
+ ii += 8;
+ }
+ }
+ }
+}
+
+
+/*********************************************
+ * update_line_mode7()
+ *
+ * Update an entire line of mode7 tiles.
+ *********************************************/
+
+#define MODE7_CLIP(x) (((x) & 0x2000) ? ((x) | ~0x03ff) : ((x) & 0x03ff))
+
+void snes_ppu_device::update_line_mode7( UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a )
+{
+ UINT32 tiled;
+ INT16 ma, mb, mc, md;
+ INT32 xc, yc, tx, ty, sx, sy, hs, vs, xpos, xdir, x0, y0;
+ UINT8 priority = priority_b;
+ UINT8 colour = 0;
+ UINT16 *mosaic_x, *mosaic_y;
+ UINT16 c;
+ int screen;
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.bg_disabled[layer])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ m_scanlines[SNES_MAINSCREEN].enable = m_layer[layer].main_bg_enabled;
+ m_scanlines[SNES_SUBSCREEN].enable = m_layer[layer].sub_bg_enabled;
+ m_scanlines[SNES_MAINSCREEN].clip = m_layer[layer].main_window_enabled;
+ m_scanlines[SNES_SUBSCREEN].clip = m_layer[layer].sub_window_enabled;
+
+ if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
+ return;
+
+ ma = m_mode7.matrix_a;
+ mb = m_mode7.matrix_b;
+ mc = m_mode7.matrix_c;
+ md = m_mode7.matrix_d;
+ xc = m_mode7.origin_x;
+ yc = m_mode7.origin_y;
+ hs = m_mode7.hor_offset;
+ vs = m_mode7.ver_offset;
+
+ /* Sign extend */
+ xc <<= 19;
+ xc >>= 19;
+ yc <<= 19;
+ yc >>= 19;
+ hs <<= 19;
+ hs >>= 19;
+ vs <<= 19;
+ vs >>= 19;
+
+ /* Vertical flip */
+ if (m_mode7.vflip)
+ sy = 255 - curline;
+ else
+ sy = curline;
+
+ /* Horizontal flip */
+ if (m_mode7.hflip)
+ {
+ xpos = 255;
+ xdir = -1;
+ }
+ else
+ {
+ xpos = 0;
+ xdir = 1;
+ }
+
+ /* MOSAIC - to be verified */
+ if (layer == SNES_BG2) // BG2 use two different bits for horizontal and vertical mosaic
+ {
+ mosaic_x = m_mosaic_table[m_layer[SNES_BG2].mosaic_enabled ? m_mosaic_size : 0];
+ mosaic_y = m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
+ }
+ else // BG1 works as usual
+ {
+ mosaic_x = m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
+ mosaic_y = m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
+ }
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mosaic_disabled)
+ {
+ mosaic_x = m_mosaic_table[0];
+ mosaic_y = m_mosaic_table[0];
+ }
+#endif /* SNES_LAYER_DEBUG */
+
+ /* Let's do some mode7 drawing huh? */
+ /* These can be computed only once, since they do not depend on sx */
+ x0 = ((ma * MODE7_CLIP(hs - xc)) & ~0x3f) + ((mb * mosaic_y[sy]) & ~0x3f) + ((mb * MODE7_CLIP(vs - yc)) & ~0x3f) + (xc << 8);
+ y0 = ((mc * MODE7_CLIP(hs - xc)) & ~0x3f) + ((md * mosaic_y[sy]) & ~0x3f) + ((md * MODE7_CLIP(vs - yc)) & ~0x3f) + (yc << 8);
+
+ for (sx = 0; sx < 256; sx++, xpos += xdir)
+ {
+ tx = (x0 + (ma * mosaic_x[sx])) >> 8;
+ ty = (y0 + (mc * mosaic_x[sx])) >> 8;
+
+ switch (m_mode7.repeat)
+ {
+ case 0x00: /* Repeat if outside screen area */
+ case 0x01: /* Repeat if outside screen area */
+ tx &= 0x3ff;
+ ty &= 0x3ff;
+ tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
+ colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
+ break;
+ case 0x02: /* Single colour backdrop screen if outside screen area */
+ if ((tx >= 0) && (tx < 1024) && (ty >= 0) && (ty < 1024))
+ {
+ tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
+ colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
+ }
+ else
+ colour = 0;
+ break;
+ case 0x03: /* Character 0x00 repeat if outside screen area */
+ if ((tx >= 0) && (tx < 1024) && (ty >= 0) && (ty < 1024))
+ tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
+ else
+ tiled = 0;
+
+ colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
+ break;
+ }
+
+ /* The last bit is for priority in EXTBG mode (used only for BG2) */
+ if (layer == SNES_BG2)
+ {
+ priority = ((colour & 0x80) >> 7) ? priority_a : priority_b;
+ colour &= 0x7f;
+
+#if SNES_LAYER_DEBUG
+ /* if we want to draw only one of the priorities of this layer */
+ if (((m_debug_options.select_pri[layer] & 0x01) && (priority == priority_a)) ||
+ ((m_debug_options.select_pri[layer] & 0x02) && (priority == priority_b)))
+ continue;
+#endif /* SNES_LAYER_DEBUG */
+ }
+
+ for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
+ {
+ if (m_scanlines[screen].enable)
+ {
+ UINT8 clr = colour;
+ UINT8 clipmask = m_clipmasks[layer][xpos];
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.windows_disabled)
+ clipmask = 0xff;
+#endif /* SNES_LAYER_DEBUG */
+
+ /* Clip to windows */
+ if (m_scanlines[screen].clip)
+ clr &= clipmask;
+
+ /* Draw pixel if appropriate */
+ if (m_scanlines[screen].priority[xpos] <= priority && clr > 0)
+ {
+ /* Direct select, but only outside EXTBG! */
+ // Direct color format is: 0 | BB000 | GGG00 | RRR00, HW confirms that the data is zero padded.
+ // In other words, like normal direct color, with pal = 0
+ c = get_bgcolor(m_direct_color && layer == SNES_BG1, 0, clr);
+ set_scanline_pixel(screen, xpos, c, priority, layer, 0);
+ }
+ }
+ }
+ }
+}
+
+/*************************************************************************************************
+ * SNES Sprites
+ *
+ * 1. First of all: sprites are drawn one line in advance. We emulate this by caching the
+ * starting vram address, the sprite size and the "name select" at each line, and by using
+ * them the next line to output the proper sprites - see update_obsel.
+ *
+ * 2. Each line can select its sprites among 128 available ones in oam_ram, hence we start
+ * by creating a list of the available objects (each one with its x,y coordinate, its size,
+ * its tile address, etc.) - see oam_list_build.
+ *
+ * 3. Next, we start finding out which sprites will appear in the line: starting from
+ * FirstSprite, we count 32 OBJs which intersect our line and we store their indexes in the
+ * oam_itemlist array (if more than 32 sprites intersect our line, we set the Range Over
+ * flag); then, selecting among these sprites, we count 34 8x8 tiles which are visible
+ * in our line (i.e. whose x coord is between -size and 256) and we store the corresponding
+ * coordinates/priorities/palettes/etc. in the oam_tilelist array (if more than 34 tiles would
+ * appear on screen, we set the Time Over flag).
+ * Notice that when we populate oam_tilelist, we proceed from oam_itemlist[31] (or from the last
+ * item which intersects the scanline), towards oam_itemlist[0], i.e. the higher tiles (say
+ * oam_tilelist[34], or the last tile which appear on screen) will contain FirstSprite object,
+ * or the sprites with closer index to FirstSprite which get displayed. This will play an
+ * important role for sprite priority - see update_objects_rto.
+ *
+ * 4. All the above happens at the beginning of each VIDEO_UPDATE. When we finally draw the
+ * scanline, we pass through the oam_tilelist and we store the displayed pixels in our scanline
+ * buffer. Notice that, for each pixel of a SNES sprite, only the priority of the topmost sprite
+ * is tested against the priority of the BG pixel (because FirstSprite is on top of FirstSprite+1,
+ * which is on top of FirstSprite+2, etc., and therefore other sprites are already covered by the
+ * topmost one). To emulate this, we draw each tile over the previous ones no matter what
+ * priorities are (differently from what we did with BGs): in the end, we will have in each pixel z
+ * its topmost sprite and scanline.priority[z] will be the topmost sprite priority as expected.
+ * Of course, sprite drawing must happen before BG drawing, so that afterwords BG pixels properly
+ * test their priority with the one of the correct sprite - see update_objects.
+ *************************************************************************************************/
+
+
+/*********************************************
+ * update_obsel()
+ *
+ * Update sprite settings for next line.
+ *********************************************/
+
+void snes_ppu_device::update_obsel( void )
+{
+ m_layer[SNES_OAM].charmap = m_oam.next_charmap;
+ m_oam.name_select = m_oam.next_name_select;
+
+ if (m_oam.size != m_oam.next_size)
+ {
+ m_oam.size = m_oam.next_size;
+ m_update_oam_list = 1;
+ }
+}
+
+/*********************************************
+ * oam_list_build()
+ *
+ * Build a list of the available obj in OAM ram.
+ *********************************************/
+
+void snes_ppu_device::oam_list_build( void )
+{
+ UINT8 *oamram = (UINT8 *)m_oam_ram;
+ INT16 oam = 0x1ff;
+ UINT16 oam_extra = oam + 0x20;
+ UINT16 extra = 0;
+ int ii;
+
+ m_update_oam_list = 0; // eventually, we can optimize the code by only calling this function when there is a change in size
+
+ for (ii = 127; ii >= 0; ii--)
+ {
+ if (((ii + 1) % 4) == 0)
+ extra = oamram[oam_extra--];
+
+ m_oam_spritelist[ii].vflip = (oamram[oam] & 0x80) >> 7;
+ m_oam_spritelist[ii].hflip = (oamram[oam] & 0x40) >> 6;
+ m_oam_spritelist[ii].priority_bits = (oamram[oam] & 0x30) >> 4;
+ m_oam_spritelist[ii].pal = 128 + ((oamram[oam] & 0x0e) << 3);
+ m_oam_spritelist[ii].tile = (oamram[oam--] & 0x1) << 8;
+ m_oam_spritelist[ii].tile |= oamram[oam--];
+ m_oam_spritelist[ii].y = oamram[oam--] + 1;
+ m_oam_spritelist[ii].x = oamram[oam--];
+ m_oam_spritelist[ii].size = (extra & 0x80) >> 7;
+ extra <<= 1;
+ m_oam_spritelist[ii].x |= ((extra & 0x80) << 1);
+ extra <<= 1;
+
+ m_oam_spritelist[ii].y *= m_obj_interlace;
+ m_oam_spritelist[ii].y &= 0x1ff;
+
+ m_oam_spritelist[ii].x &= 0x1ff;
+
+ /* Determine object size */
+ switch (m_oam.size)
+ {
+ case 0: /* 8x8 or 16x16 */
+ m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 2 : 1;
+ m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 2 : 1;
+ break;
+ case 1: /* 8x8 or 32x32 */
+ m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 4 : 1;
+ m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 1;
+ break;
+ case 2: /* 8x8 or 64x64 */
+ m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 8 : 1;
+ m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 1;
+ break;
+ case 3: /* 16x16 or 32x32 */
+ m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 4 : 2;
+ m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 2;
+ break;
+ case 4: /* 16x16 or 64x64 */
+ m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 8 : 2;
+ m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 2;
+ break;
+ case 5: /* 32x32 or 64x64 */
+ m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 8 : 4;
+ m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 4;
+ break;
+ case 6: /* undocumented: 16x32 or 32x64 */
+ m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 4 : 2;
+ m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 4;
+ if (m_obj_interlace && !m_oam_spritelist[ii].size)
+ m_oam_spritelist[ii].height = 2;
+ break;
+ case 7: /* undocumented: 16x32 or 32x32 */
+ m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 4 : 2;
+ m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 4;
+ if (m_obj_interlace && !m_oam_spritelist[ii].size)
+ m_oam_spritelist[ii].height = 2;
+ break;
+ default:
+ /* we should never enter here... */
+ logerror("Object size unsupported: %d\n", m_oam.size);
+ break;
+ }
+ }
+}
+
+/*********************************************
+ * is_sprite_on_scanline()
+ *
+ * Check if a given sprites intersect current
+ * scanline
+ *********************************************/
+
+int snes_ppu_device::is_sprite_on_scanline( UINT16 curline, UINT8 sprite )
+{
+ //if sprite is entirely offscreen and doesn't wrap around to the left side of the screen,
+ //then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen.
+ int spr_height = (m_oam_spritelist[sprite].height << 3);
+
+ if (m_oam_spritelist[sprite].x > 256 && (m_oam_spritelist[sprite].x + (m_oam_spritelist[sprite].width << 3) - 1) < 512)
+ return 0;
+
+ if (curline >= m_oam_spritelist[sprite].y && curline < (m_oam_spritelist[sprite].y + spr_height))
+ return 1;
+
+ if ((m_oam_spritelist[sprite].y + spr_height) >= 256 && curline < ((m_oam_spritelist[sprite].y + spr_height) & 255))
+ return 1;
+
+ return 0;
+}
+
+/*********************************************
+ * update_objects_rto()
+ *
+ * Determine which OBJs will be drawn on this
+ * scanline.
+ *********************************************/
+
+void snes_ppu_device::update_objects_rto( UINT16 curline )
+{
+ int ii, jj, active_sprite;
+ UINT8 range_over, time_over;
+ INT8 xs, ys;
+ UINT8 line;
+ UINT8 height, width, vflip, hflip, priority, pal;
+ UINT16 tile;
+ INT16 x, y;
+ UINT32 name_sel = 0;
+
+ oam_list_build();
+
+ /* initialize counters */
+ range_over = 0;
+ time_over = 0;
+
+ /* setup the proper line */
+ curline /= m_interlace;
+ curline *= m_obj_interlace;
+
+ /* reset the list of first 32 objects which intersect current scanline */
+ memset(m_oam_itemlist, 0xff, 32);
+
+ /* populate the list of 32 objects */
+ for (ii = 0; ii < 128; ii++)
+ {
+ active_sprite = (ii + m_oam.first_sprite) & 0x7f;
+
+ if (!is_sprite_on_scanline(curline, active_sprite))
+ continue;
+
+ if (range_over++ >= 32)
+ break;
+
+ m_oam_itemlist[range_over - 1] = active_sprite;
+ }
+
+ /* reset the list of first 34 tiles to be drawn */
+ for (ii = 0; ii < 34; ii++)
+ m_oam_tilelist[ii].tileaddr = 0xffff;
+
+ /* populate the list of 34 tiles */
+ for (ii = 31; ii >= 0; ii--)
+ {
+ if (m_oam_itemlist[ii] == 0xff)
+ continue;
+
+ active_sprite = m_oam_itemlist[ii];
+
+ tile = m_oam_spritelist[active_sprite].tile;
+ x = m_oam_spritelist[active_sprite].x;
+ y = m_oam_spritelist[active_sprite].y;
+ height = m_oam_spritelist[active_sprite].height;
+ width = m_oam_spritelist[active_sprite].width;
+ vflip = m_oam_spritelist[active_sprite].vflip;
+ hflip = m_oam_spritelist[active_sprite].hflip;
+ priority = m_oam_spritelist[active_sprite].priority_bits;
+ pal = m_oam_spritelist[active_sprite].pal;
+
+ /* Adjust y, if past maximum position (for sprites which overlap between top & bottom) */
+ if (y >= (0x100 - 16) * m_interlace)
+ y -= (0x100) * m_interlace;
+
+ if (curline >= y && curline < (y + (height << 3)))
+ {
+ /* Only objects using tiles over 255 use name select */
+ name_sel = (tile < 256) ? 0 : m_oam.name_select;
+
+ ys = (curline - y) >> 3;
+ line = (curline - y) % 8;
+ if (vflip)
+ {
+ ys = height - ys - 1;
+ line = 7 - line;
+ }
+ line <<= 1;
+ tile <<= 5;
+
+ for (jj = 0; jj < width; jj++)
+ {
+ INT16 xx = (x + (jj << 3)) & 0x1ff;
+
+ if (x != 256 && xx >= 256 && (xx + 7) < 512)
+ continue;
+
+ if (time_over++ >= 34)
+ break;
+
+ xs = (hflip) ? (width - 1 - jj) : jj;
+ m_oam_tilelist[time_over - 1].tileaddr = name_sel + tile + table_obj_offset[ys][xs] + line;
+ m_oam_tilelist[time_over - 1].hflip = hflip;
+ m_oam_tilelist[time_over - 1].x = xx;
+ m_oam_tilelist[time_over - 1].pal = pal;
+ m_oam_tilelist[time_over - 1].priority = priority;
+ }
+ }
+ }
+
+ /* set Range Over flag if necessary */
+ if (range_over > 32)
+ m_stat77 |= 0x40;
+
+ /* set Time Over flag if necessary */
+ if (time_over > 34)
+ m_stat77 |= 0x80;
+}
+
+/*********************************************
+ * update_objects()
+ *
+ * Update an entire line of sprites.
+ *********************************************/
+
+void snes_ppu_device::update_objects( UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3 )
+{
+ UINT8 pri, priority[4];
+ UINT32 charaddr;
+ int ii;
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.bg_disabled[SNES_OAM])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ m_scanlines[SNES_MAINSCREEN].enable = m_layer[SNES_OAM].main_bg_enabled;
+ m_scanlines[SNES_SUBSCREEN].enable = m_layer[SNES_OAM].sub_bg_enabled;
+ m_scanlines[SNES_MAINSCREEN].clip = m_layer[SNES_OAM].main_window_enabled;
+ m_scanlines[SNES_SUBSCREEN].clip = m_layer[SNES_OAM].sub_window_enabled;
+
+ if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
+ return;
+
+ charaddr = m_layer[SNES_OAM].charmap << 13;
+
+ priority[0] = priority_oam0;
+ priority[1] = priority_oam1;
+ priority[2] = priority_oam2;
+ priority[3] = priority_oam3;
+
+ /* finally draw the tiles from the tilelist */
+ for (ii = 0; ii < 34; ii++)
+ {
+ int tile = ii;
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.sprite_reversed)
+ tile = 33 - ii;
+#endif /* SNES_LAYER_DEBUG */
+
+ if (m_oam_tilelist[tile].tileaddr == 0xffff)
+ continue;
+
+ pri = priority[m_oam_tilelist[tile].priority];
+
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.select_pri[SNES_OAM])
+ {
+ int oam_draw = m_debug_options.select_pri[SNES_OAM] - 1;
+ if (oam_draw != m_oam_tilelist[tile].priority)
+ continue;
+ }
+#endif /* SNES_LAYER_DEBUG */
+
+ /* OAM tiles have fixed planes (4), no direct color and no hires, but otherwise work the same as BG ones */
+ draw_tile(4, SNES_OAM, charaddr + m_oam_tilelist[tile].tileaddr, m_oam_tilelist[tile].x, pri, m_oam_tilelist[tile].hflip, 0, m_oam_tilelist[tile].pal, 0);
+ }
+}
+
+
+/*********************************************
+ * snes_update_mode_X()
+ *
+ * Update Mode X line.
+ *********************************************/
+
+void snes_ppu_device::update_mode_0( UINT16 curline )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mode_disabled[0])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ update_objects(3, 6, 9, 12);
+ update_line(curline, SNES_BG1, 8, 11, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
+ update_line(curline, SNES_BG2, 7, 10, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
+ update_line(curline, SNES_BG3, 2, 5, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
+ update_line(curline, SNES_BG4, 1, 4, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
+}
+
+void snes_ppu_device::update_mode_1( UINT16 curline )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mode_disabled[1])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ if (!m_bg3_priority_bit)
+ {
+ update_objects(2, 4, 7, 10);
+ update_line(curline, SNES_BG1, 6, 9, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
+ update_line(curline, SNES_BG2, 5, 8, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
+ update_line(curline, SNES_BG3, 1, 3, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
+ }
+ else
+ {
+ update_objects(2, 3, 6, 9);
+ update_line(curline, SNES_BG1, 5, 8, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
+ update_line(curline, SNES_BG2, 4, 7, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
+ update_line(curline, SNES_BG3, 1, 10, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_NONE, 0);
+ }
+}
+
+void snes_ppu_device::update_mode_2( UINT16 curline )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mode_disabled[2])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ update_objects(2, 4, 6, 8);
+ update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_MODE2, 0);
+ update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_MODE2, 0);
+}
+
+void snes_ppu_device::update_mode_3( UINT16 curline )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mode_disabled[3])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ update_objects(2, 4, 6, 8);
+ update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_8BPP, 0, SNES_OPT_NONE, m_direct_color);
+ update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_4BPP, 0, SNES_OPT_NONE, 0);
+}
+
+void snes_ppu_device::update_mode_4( UINT16 curline )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mode_disabled[4])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ update_objects(2, 4, 6, 8);
+ update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_8BPP, 0, SNES_OPT_MODE4, m_direct_color);
+ update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_2BPP, 0, SNES_OPT_MODE4, 0);
+}
+
+void snes_ppu_device::update_mode_5( UINT16 curline )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mode_disabled[5])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ update_objects(2, 4, 6, 8);
+ update_line(curline, SNES_BG1, 3, 7, SNES_COLOR_DEPTH_4BPP, 1, SNES_OPT_NONE, 0);
+ update_line(curline, SNES_BG2, 1, 5, SNES_COLOR_DEPTH_2BPP, 1, SNES_OPT_NONE, 0);
+}
+
+void snes_ppu_device::update_mode_6( UINT16 curline )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mode_disabled[6])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ update_objects(1, 3, 4, 6);
+ update_line(curline, SNES_BG1, 2, 5, SNES_COLOR_DEPTH_4BPP, 1, SNES_OPT_MODE6, 0);
+}
+
+void snes_ppu_device::update_mode_7( UINT16 curline )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.mode_disabled[7])
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ if (!m_mode7.extbg)
+ {
+ update_objects(1, 3, 4, 5);
+ update_line_mode7(curline, SNES_BG1, 2, 2);
+ }
+ else
+ {
+ update_objects(2, 4, 6, 7);
+ update_line_mode7(curline, SNES_BG1, 3, 3);
+ update_line_mode7(curline, SNES_BG2, 1, 5);
+ }
+}
+
+/*********************************************
+ * snes_draw_screens()
+ *
+ * Draw the whole screen (Mode 0 -> 7).
+ *********************************************/
+
+void snes_ppu_device::draw_screens( UINT16 curline )
+{
+ switch (m_mode)
+ {
+ case 0: update_mode_0(curline); break; /* Mode 0 */
+ case 1: update_mode_1(curline); break; /* Mode 1 */
+ case 2: update_mode_2(curline); break; /* Mode 2 - Supports offset per tile */
+ case 3: update_mode_3(curline); break; /* Mode 3 - Supports direct colour */
+ case 4: update_mode_4(curline); break; /* Mode 4 - Supports offset per tile and direct colour */
+ case 5: update_mode_5(curline); break; /* Mode 5 - Supports hires */
+ case 6: update_mode_6(curline); break; /* Mode 6 - Supports offset per tile and hires */
+ case 7: update_mode_7(curline); break; /* Mode 7 - Supports direct colour */
+ }
+}
+
+/*********************************************
+ * update_windowmasks()
+ *
+ * An example of how windows work:
+ * Win1: ...#####......
+ * Win2: ......#####...
+ * IN OUT
+ * OR: ...########... ###........###
+ * AND: ......##...... ######..######
+ * XOR: ...###..###... ###...##...###
+ * XNOR: ###...##...### ...###..###...
+ *********************************************/
+
+void snes_ppu_device::update_windowmasks( void )
+{
+ UINT16 ii, jj;
+ INT8 w1, w2;
+
+ m_update_windows = 0; /* reset the flag */
+
+ for (ii = 0; ii < SNES_SCR_WIDTH; ii++)
+ {
+ /* update bg 1, 2, 3, 4, obj & color windows */
+ /* jj = layer */
+ for (jj = 0; jj < 6; jj++)
+ {
+ m_clipmasks[jj][ii] = 0xff; /* let's start from un-masked */
+ w1 = w2 = -1;
+
+ if (m_layer[jj].window1_enabled)
+ {
+ /* Default to mask area inside */
+ if ((ii < m_window1_left) || (ii > m_window1_right))
+ w1 = 0;
+ else
+ w1 = 1;
+
+ /* If mask area is outside then swap */
+ if (m_layer[jj].window1_invert)
+ w1 = !w1;
+ }
+
+ if (m_layer[jj].window2_enabled)
+ {
+ if ((ii < m_window2_left) || (ii > m_window2_right))
+ w2 = 0;
+ else
+ w2 = 1;
+ if (m_layer[jj].window2_invert)
+ w2 = !w2;
+ }
+
+ /* mask if the appropriate expression is true */
+ if (w1 >= 0 && w2 >= 0)
+ {
+ switch (m_layer[jj].wlog_mask)
+ {
+ case 0x00: /* OR */
+ m_clipmasks[jj][ii] = (w1 | w2) ? 0x00 : 0xff;
+ break;
+ case 0x01: /* AND */
+ m_clipmasks[jj][ii] = (w1 & w2) ? 0x00 : 0xff;
+ break;
+ case 0x02: /* XOR */
+ m_clipmasks[jj][ii] = (w1 ^ w2) ? 0x00 : 0xff;
+ break;
+ case 0x03: /* XNOR */
+ m_clipmasks[jj][ii] = !(w1 ^ w2) ? 0x00 : 0xff;
+ break;
+ }
+ }
+ else if (w1 >= 0)
+ m_clipmasks[jj][ii] = w1 ? 0x00 : 0xff;
+ else if (w2 >= 0)
+ m_clipmasks[jj][ii] = w2 ? 0x00 : 0xff;
+ }
+ }
+}
+
+/*********************************************
+ * update_offsets()
+ *
+ * Update the offsets with the latest changes.
+ * This is currently unused, but it could
+ * possibly be handy for some minor optimization
+ *********************************************/
+
+void snes_ppu_device::update_offsets( void )
+{
+ int ii;
+ for (ii = 0; ii < 4; ii++)
+ {
+ }
+ m_update_offsets = 0;
+}
+
+/*****************************************
+ * draw_blend()
+ *
+ * Routine for additive/subtractive blending
+ * between the main and sub screens, i.e.
+ * color math.
+ *****************************************/
+
+inline void snes_ppu_device::draw_blend( UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens )
+{
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.colormath_disabled)
+ return;
+#endif /* SNES_LAYER_DEBUG */
+
+ /* when color math is applied to subscreen pixels, the blending depends on the blending used by the previous mainscreen
+ pixel, except for subscreen pixel 0 which has no previous mainscreen pixel, see comments in refresh_scanline */
+ if (switch_screens && offset > 0)
+ offset -= 1;
+
+ if ((black_pen_clip == SNES_CLIP_ALWAYS) ||
+ (black_pen_clip == SNES_CLIP_IN && m_clipmasks[SNES_COLOR][offset]) ||
+ (black_pen_clip == SNES_CLIP_OUT && !m_clipmasks[SNES_COLOR][offset]))
+ *colour = 0; //clip to black before color math
+
+ if (prevent_color_math == SNES_CLIP_ALWAYS) // blending mode 3 == always OFF
+ return;
+
+ if ((prevent_color_math == SNES_CLIP_NEVER) ||
+ (prevent_color_math == SNES_CLIP_IN && !m_clipmasks[SNES_COLOR][offset]) ||
+ (prevent_color_math == SNES_CLIP_OUT && m_clipmasks[SNES_COLOR][offset]))
+ {
+ UINT16 r, g, b;
+ struct SNES_SCANLINE *subscreen;
+ int clip_max = 0; // if add then clip to 0x1f, if sub then clip to 0
+
+#if SNES_LAYER_DEBUG
+ /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
+ if (m_debug_options.draw_subscreen)
+ {
+ subscreen = switch_screens ? &m_scanlines[SNES_SUBSCREEN] : &m_scanlines[SNES_MAINSCREEN];
+ }
+ else
+#endif /* SNES_LAYER_DEBUG */
+ {
+ subscreen = switch_screens ? &m_scanlines[SNES_MAINSCREEN] : &m_scanlines[SNES_SUBSCREEN];
+ }
+
+ if (m_sub_add_mode) /* SNES_SUBSCREEN*/
+ {
+ if (!BIT(m_color_modes, 7))
+ {
+ /* 0x00 add */
+ r = (*colour & 0x1f) + (subscreen->buffer[offset] & 0x1f);
+ g = ((*colour & 0x3e0) >> 5) + ((subscreen->buffer[offset] & 0x3e0) >> 5);
+ b = ((*colour & 0x7c00) >> 10) + ((subscreen->buffer[offset] & 0x7c00) >> 10);
+ clip_max = 1;
+ }
+ else
+ {
+ /* 0x80 sub */
+ r = (*colour & 0x1f) - (subscreen->buffer[offset] & 0x1f);
+ g = ((*colour & 0x3e0) >> 5) - ((subscreen->buffer[offset] & 0x3e0) >> 5);
+ b = ((*colour & 0x7c00) >> 10) - ((subscreen->buffer[offset] & 0x7c00) >> 10);
+ if (r > 0x1f) r = 0;
+ if (g > 0x1f) g = 0;
+ if (b > 0x1f) b = 0;
+ }
+ /* only halve if the color is not the back colour */
+ if (BIT(m_color_modes, 6) && (subscreen->buffer[offset] != m_cgram[FIXED_COLOUR]))
+ {
+ r >>= 1;
+ g >>= 1;
+ b >>= 1;
+ }
+ }
+ else /* Fixed colour */
+ {
+ if (!BIT(m_color_modes, 7))
+ {
+ /* 0x00 add */
+ r = (*colour & 0x1f) + (m_cgram[FIXED_COLOUR] & 0x1f);
+ g = ((*colour & 0x3e0) >> 5) + ((m_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
+ b = ((*colour & 0x7c00) >> 10) + ((m_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
+ clip_max = 1;
+ }
+ else
+ {
+ /* 0x80: sub */
+ r = (*colour & 0x1f) - (m_cgram[FIXED_COLOUR] & 0x1f);
+ g = ((*colour & 0x3e0) >> 5) - ((m_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
+ b = ((*colour & 0x7c00) >> 10) - ((m_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
+ if (r > 0x1f) r = 0;
+ if (g > 0x1f) g = 0;
+ if (b > 0x1f) b = 0;
+ }
+ /* halve if necessary */
+ if (BIT(m_color_modes, 6))
+ {
+ r >>= 1;
+ g >>= 1;
+ b >>= 1;
+ }
+ }
+
+ /* according to anomie's docs, after addition has been performed, division by 2 happens *before* clipping to max, hence we clip now */
+ if (clip_max)
+ {
+ if (r > 0x1f) r = 0x1f;
+ if (g > 0x1f) g = 0x1f;
+ if (b > 0x1f) b = 0x1f;
+ }
+
+ *colour = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10));
+ }
+}
+
+/*********************************************
+ * refresh_scanline()
+ *
+ * Redraw the current line.
+ *********************************************/
+/*********************************************
+ * Notice that in hires and pseudo hires modes,
+ * i.e. when 512 different pixels are present
+ * in a scanline, a crt TV monitor would end
+ * up blending adjacent pixels. To mimic this,
+ * we add a small (optional) hack which enters
+ * only in the very last stage of the scanline
+ * drawing and which simulates the TV by
+ * replacing the exact pixel color with an
+ * average of the current and next pixel colors.
+ * Credits (and thanks) to Blargg and Byuu for
+ * the optimized averaging algorithm.
+ *********************************************/
+
+void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, UINT16 curline )
+{
+ UINT16 ii;
+ int x;
+ int fade;
+ struct SNES_SCANLINE *scanline1, *scanline2;
+ UINT16 c;
+ UINT16 prev_colour = 0;
+ int blurring = machine().root_device().ioport("OPTIONS")->read_safe(0) & 0x01;
+
+ g_profiler.start(PROFILER_VIDEO);
+
+ if (m_screen_disabled) /* screen is forced blank */
+ for (x = 0; x < SNES_SCR_WIDTH * 2; x++)
+ bitmap.pix32(curline, x) = rgb_t::black;
+ else
+ {
+ /* Update clip window masks if necessary */
+ if (m_update_windows)
+ update_windowmasks();
+ /* Update the offsets if necessary */
+ if (m_update_offsets)
+ update_offsets();
+
+ /* Clear priority */
+ memset(m_scanlines[SNES_MAINSCREEN].priority, 0, SNES_SCR_WIDTH);
+ memset(m_scanlines[SNES_SUBSCREEN].priority, 0, SNES_SCR_WIDTH);
+
+ /* Clear layers */
+ memset(m_scanlines[SNES_MAINSCREEN].layer, SNES_COLOR, SNES_SCR_WIDTH);
+ memset(m_scanlines[SNES_SUBSCREEN].layer, SNES_COLOR, SNES_SCR_WIDTH);
+
+ /* Clear blend_exception (only used for OAM) */
+ memset(m_scanlines[SNES_MAINSCREEN].blend_exception, 0, SNES_SCR_WIDTH);
+ memset(m_scanlines[SNES_SUBSCREEN].blend_exception, 0, SNES_SCR_WIDTH);
+
+ /* Draw back colour */
+ for (ii = 0; ii < SNES_SCR_WIDTH; ii++)
+ {
+ if (m_mode == 5 || m_mode == 6 || m_pseudo_hires)
+ m_scanlines[SNES_SUBSCREEN].buffer[ii] = m_cgram[0];
+ else
+ m_scanlines[SNES_SUBSCREEN].buffer[ii] = m_cgram[FIXED_COLOUR];
+
+ m_scanlines[SNES_MAINSCREEN].buffer[ii] = m_cgram[0];
+ }
+
+ /* Prepare OAM for this scanline */
+ update_objects_rto(curline);
+
+ /* Draw scanline */
+ draw_screens(curline);
+
+ update_obsel();
+
+#if SNES_LAYER_DEBUG
+ if (dbg_video(curline))
+ {
+ g_profiler.stop();
+ return;
+ }
+
+ /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
+ if (m_debug_options.draw_subscreen)
+ {
+ scanline1 = &m_scanlines[SNES_SUBSCREEN];
+ scanline2 = &m_scanlines[SNES_MAINSCREEN];
+ }
+ else
+#endif /* SNES_LAYER_DEBUG */
+ {
+ scanline1 = &m_scanlines[SNES_MAINSCREEN];
+ scanline2 = &m_scanlines[SNES_SUBSCREEN];
+ }
+
+ /* Draw the scanline to screen */
+
+ fade = m_screen_brightness;
+
+ for (x = 0; x < SNES_SCR_WIDTH; x++)
+ {
+ int r, g, b, hires;
+ UINT16 tmp_col[2];
+ hires = (m_mode != 5 && m_mode != 6 && !m_pseudo_hires) ? 0 : 1;
+
+ /* in hires, the first pixel (of 512) is subscreen pixel, then the first mainscreen pixel follows, and so on... */
+ if (!hires)
+ {
+ c = scanline1->buffer[x];
+
+ /* perform color math if the layer wants it (except if it's an object > 192) */
+ if (!scanline1->blend_exception[x] && m_layer[scanline1->layer[x]].color_math)
+ draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 0);
+
+ r = ((c & 0x1f) * fade) >> 4;
+ g = (((c & 0x3e0) >> 5) * fade) >> 4;
+ b = (((c & 0x7c00) >> 10) * fade) >> 4;
+
+ bitmap.pix32(curline, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
+ bitmap.pix32(curline, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
+ }
+ else
+ {
+ /* prepare the pixel from main screen */
+ c = scanline1->buffer[x];
+
+ /* perform color math if the layer wants it (except if it's an object > 192) */
+ if (!scanline1->blend_exception[x] && m_layer[scanline1->layer[x]].color_math)
+ draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 0);
+
+ tmp_col[1] = c;
+
+ /* prepare the pixel from sub screen */
+ c = scanline2->buffer[x];
+
+ /* in hires/pseudo-hires, subscreen pixels are blended as well: for each subscreen pixel, color math
+ is applied if it had been applied to the previous mainscreen pixel. What happens at subscreen pixel 0
+ (which has no previous mainscreen pixel) is undocumented. Until more info are discovered, we (arbitrarily)
+ apply to it the same color math as the *next* mainscreen pixel (i.e. mainscreen pixel 0), which seems as good as
+ any other choice */
+ if (x == 0 && !scanline1->blend_exception[0] && m_layer[scanline1->layer[0]].color_math)
+ draw_blend(0, &c, m_prevent_color_math, m_clip_to_black, 1);
+ else if (x > 0 && !scanline1->blend_exception[x - 1] && m_layer[scanline1->layer[x - 1]].color_math)
+ draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 1);
+
+ tmp_col[0] = c;
+
+ /* average the first pixel if required, or draw it directly*/
+ if (blurring)
+ c = (prev_colour + tmp_col[0] - ((prev_colour ^ tmp_col[0]) & 0x0421)) >> 1; // Hack code to mimic TV pixel blurring
+ else
+ c = tmp_col[0];
+
+ r = ((c & 0x1f) * fade) >> 4;
+ g = (((c & 0x3e0) >> 5) * fade) >> 4;
+ b = (((c & 0x7c00) >> 10) * fade) >> 4;
+
+ bitmap.pix32(curline, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
+ prev_colour = tmp_col[0];
+
+ /* average the second pixel if required, or draw it directly*/
+ if (blurring)
+ c = (prev_colour + tmp_col[1] - ((prev_colour ^ tmp_col[1]) & 0x0421)) >> 1; // Hack code to mimic TV pixel blurring
+ else
+ c = tmp_col[1];
+
+ r = ((c & 0x1f) * fade) >> 4;
+ g = (((c & 0x3e0) >> 5) * fade) >> 4;
+ b = (((c & 0x7c00) >> 10) * fade) >> 4;
+
+ bitmap.pix32(curline, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
+ prev_colour = tmp_col[1];
+ }
+ }
+ }
+
+ g_profiler.stop();
+}
+
+
+/* CPU <-> PPU comms */
+
+// full graphic variables
+static const UINT16 vram_fgr_inctab[4] = { 1, 32, 128, 128 };
+static const UINT16 vram_fgr_inccnts[4] = { 0, 32, 64, 128 };
+static const UINT16 vram_fgr_shiftab[4] = { 0, 5, 6, 7 };
+
+// utility function - latches the H/V counters. Used by IRQ, writes to WRIO, etc.
+void snes_ppu_device::set_latch_hv(INT16 x, INT16 y)
+{
+ m_beam.latch_vert = y;
+ m_beam.latch_horz = x;
+ m_stat78 |= 0x40; // indicate we latched
+
+// printf("latched @ H %d V %d\n", m_beam.latch_horz, m_beam.latch_vert);
+}
+
+void snes_ppu_device::dynamic_res_change()
+{
+ rectangle visarea = m_screen->visible_area();
+ attoseconds_t refresh;
+
+ visarea.min_x = visarea.min_y = 0;
+ visarea.max_y = m_beam.last_visible_line * m_interlace - 1;
+ visarea.max_x = (SNES_SCR_WIDTH * 2) - 1;
+
+ // fixme: should compensate for SNES_DBG_VIDEO
+ if (m_mode == 5 || m_mode == 6 || m_pseudo_hires)
+ m_htmult = 2;
+ else
+ m_htmult = 1;
+
+ /* FIXME: does the timing changes when the gfx mode is equal to 5 or 6? */
+ if ((m_stat78 & 0x10) == SNES_NTSC)
+ {
+ refresh = HZ_TO_ATTOSECONDS(DOTCLK_NTSC) * SNES_HTOTAL * SNES_VTOTAL_NTSC;
+ m_screen->configure(SNES_HTOTAL * 2, SNES_VTOTAL_NTSC * m_interlace, visarea, refresh);
+ }
+ else
+ {
+ refresh = HZ_TO_ATTOSECONDS(DOTCLK_PAL) * SNES_HTOTAL * SNES_VTOTAL_PAL;
+ m_screen->configure(SNES_HTOTAL * 2, SNES_VTOTAL_PAL * m_interlace, visarea, refresh);
+ }
+}
+
+/*************************************************
+
+ SNES VRAM accesses:
+
+ VRAM accesses during active display are invalid.
+ Unlike OAM and CGRAM, they will not be written
+ anywhere at all. Thanks to byuu's researches,
+ the ranges where writes are invalid have been
+ validated on hardware, as has the edge case where
+ the S-CPU open bus can be written if the write
+ occurs during the very last clock cycle of
+ vblank.
+ Our implementation could be not 100% accurate
+ when interlace is active.
+*************************************************/
+
+inline UINT32 snes_ppu_device::get_vram_address()
+{
+ UINT32 addr = m_vmadd;
+
+ if (m_vram_fgr_count)
+ {
+ UINT32 rem = addr & m_vram_fgr_mask;
+ UINT32 faddr = (addr & ~m_vram_fgr_mask) + (rem >> m_vram_fgr_shift) + ((rem & (m_vram_fgr_count - 1)) << 3);
+ return faddr << 1;
+ }
+
+ return addr << 1;
+}
+
+READ8_MEMBER( snes_ppu_device::vram_read )
+{
+ UINT8 res = 0;
+ offset &= 0xffff; // only 64KB are present on SNES
+
+ if (m_screen_disabled)
+ res = m_vram[offset];
+ else
+ {
+ UINT16 v = m_screen->vpos();
+ UINT16 h = m_screen->hpos();
+ UINT16 ls = (((m_stat78 & 0x10) == SNES_NTSC ? 525 : 625) >> 1) - 1;
+
+ if (m_interlace == 2)
+ ls++;
+
+ if (v == ls && h == 1362)
+ res = 0;
+ else if (v < m_beam.last_visible_line - 1)
+ res = 0;
+ else if (v == m_beam.last_visible_line - 1)
+ {
+ if (h == 1362)
+ res = m_vram[offset];
+ else
+ {
+ //printf("%d %d VRAM read, CHECK!\n",h,v);
+ res = 0;
+ }
+ }
+ else
+ res = m_vram[offset];
+ }
+ return res;
+}
+
+WRITE8_MEMBER( snes_ppu_device::vram_write )
+{
+ offset &= 0xffff; // only 64KB are present on SNES, Robocop 3 relies on this
+
+ if (m_screen_disabled)
+ m_vram[offset] = data;
+ else
+ {
+ UINT16 v = m_screen->vpos();
+ UINT16 h = m_screen->hpos();
+ if (v == 0)
+ {
+ if (h <= 4)
+ m_vram[offset] = data;
+ else if (h == 6)
+ m_vram[offset] = m_openbus_cb(space, 0);
+ else
+ {
+ //printf("%d %d VRAM write, CHECK!\n",h,v);
+ //no write
+ }
+ }
+ else if (v < m_beam.last_visible_line)
+ {
+ //printf("%d %d VRAM write, CHECK!\n",h,v);
+ //no write
+ }
+ else if (v == m_beam.last_visible_line)
+ {
+ if (h <= 4)
+ {
+ //printf("%d %d VRAM write, CHECK!\n",h,v);
+ //no write
+ }
+ else
+ m_vram[offset] = data;
+ }
+ else
+ m_vram[offset] = data;
+ }
+}
+
+/*************************************************
+
+ SNES OAM accesses:
+
+ OAM accesses during active display are allowed.
+ The actual address varies during rendering, as the
+ PPU reads in data itself for processing.
+ Unfortunately, no one has been able (yet) to
+ determine how this works. The only known game to
+ actually access OAM during active display is
+ Uniracers and it expects accesses to map to
+ offset 0x0218. Hence, following byuu's choice
+ we rerouted OAM accesses during active display
+ to 0x0218 (0x010c in our snes_oam).
+ This is a hack, but it is more accurate than
+ writing to the 'expected' address set by
+ $2102,$2103.
+
+ Notice that, since PPU_REG(OAMDATA) is never
+ read/written directly, we use it as an index
+ to choose the high/low byte of the snes_oam word.
+*************************************************/
+
+READ8_MEMBER( snes_ppu_device::oam_read )
+{
+ offset &= 0x1ff;
+
+ if (offset & 0x100)
+ offset &= 0x10f;
+
+ if (!m_screen_disabled)
+ {
+ UINT16 v = m_screen->vpos();
+
+ if (v < m_beam.last_visible_line)
+ offset = 0x010c;
+ }
+
+ return (m_oam_ram[offset] >> (PPU_REG(OAMDATA) << 3)) & 0xff;
+}
+
+WRITE8_MEMBER( snes_ppu_device::oam_write )
+{
+ offset &= 0x1ff;
+
+ if (offset & 0x100)
+ offset &= 0x10f;
+
+ if (!m_screen_disabled)
+ {
+ UINT16 v = m_screen->vpos();
+
+ if (v < m_beam.last_visible_line)
+ offset = 0x010c;
+ }
+
+ if (!(PPU_REG(OAMDATA)))
+ m_oam_ram[offset] = (m_oam_ram[offset] & 0xff00) | (data << 0);
+ else
+ m_oam_ram[offset] = (m_oam_ram[offset] & 0x00ff) | (data << 8);
+}
+
+/*************************************************
+
+ SNES CGRAM accesses:
+
+ CGRAM writes during hblank are valid. During
+ active display, the actual address the data
+ is written to varies, as the PPU itself changes
+ the address. Like OAM, it is not known the exact
+ algorithm used, but no commercial software seems
+ to attempt this. While byuu, in his emu, maps
+ those accesses to 0x01ff, because it is more
+ accurate to invalidate the 'expected' address
+ than not, MESS has issues if we don't write to
+ the expected address (see e.g. Tokimeki Memorial).
+ This is because writes should work during hblank
+ (so that the game can produce color fading), but
+ ends up not working with the conditions below.
+ Hence, for the moment, we only document the
+ solution adopted by BSNES without enabling it.
+*************************************************/
+
+READ8_MEMBER( snes_ppu_device::cgram_read )
+{
+ UINT8 res = 0;
+ offset &= 0x1ff;
+
+#if 0
+ if (!m_screen_disabled)
+ {
+ UINT16 v = m_screen->vpos();
+ UINT16 h = m_screen->hpos();
+
+ if (v < m_beam.last_visible_line && h >= 128 && h < 1096)
+ offset = 0x1ff;
+ }
+#endif
+
+ res = ((UINT8 *)m_cgram)[offset];
+
+ // CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
+ // Highest bit is simply ignored.
+ if (offset & 0x01)
+ res &= 0x7f;
+
+ return res;
+}
+
+WRITE8_MEMBER( snes_ppu_device::cgram_write )
+{
+ offset &= 0x1ff;
+
+#if 0
+ // FIXME: this currently breaks some games (e.g. Tokimeki Memorial),
+ // even if it's expected to be more accurate than allowing for
+ // writes to the cgram address
+ if (!m_screen_disabled)
+ {
+ UINT16 v = m_screen->vpos();
+ UINT16 h = m_screen->hpos();
+
+ if (v < m_beam.last_visible_line && h >= 128 && h < 1096)
+ offset = 0x1ff;
+ }
+#endif
+
+ // CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
+ // Highest bit is simply ignored.
+ if (offset & 0x01)
+ data &= 0x7f;
+
+ ((UINT8 *)m_cgram)[offset] = data;
+}
+
+UINT8 snes_ppu_device::read(address_space &space, UINT32 offset, UINT8 wrio_bit7)
+{
+ UINT8 value;
+
+ switch (offset)
+ {
+ case OAMDATA: /* 21xy for x=0,1,2 and y=4,5,6,8,9,a returns PPU1 open bus*/
+ case BGMODE:
+ case MOSAIC:
+ case BG2SC:
+ case BG3SC:
+ case BG4SC:
+ case BG4VOFS:
+ case VMAIN:
+ case VMADDL:
+ case VMDATAL:
+ case VMDATAH:
+ case M7SEL:
+ case W34SEL:
+ case WOBJSEL:
+ case WH0:
+ case WH2:
+ case WH3:
+ case WBGLOG:
+ return m_ppu1_open_bus;
+
+ case MPYL: /* Multiplication result (low) */
+ {
+ /* Perform 16bit * 8bit multiply */
+ UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
+ m_ppu1_open_bus = c & 0xff;
+ return m_ppu1_open_bus;
+ }
+ case MPYM: /* Multiplication result (mid) */
+ {
+ /* Perform 16bit * 8bit multiply */
+ UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
+ m_ppu1_open_bus = (c >> 8) & 0xff;
+ return m_ppu1_open_bus;
+ }
+ case MPYH: /* Multiplication result (high) */
+ {
+ /* Perform 16bit * 8bit multiply */
+ UINT32 c = (INT16)m_mode7.matrix_a * (INT8)(m_mode7.matrix_b >> 8);
+ m_ppu1_open_bus = (c >> 16) & 0xff;
+ return m_ppu1_open_bus;
+ }
+ case SLHV: /* Software latch for H/V counter */
+ set_latch_hv(m_screen->hpos() / m_htmult, m_screen->vpos());
+ return m_openbus_cb(space, 0); /* Return value is meaningless */
+
+ case ROAMDATA: /* Read data from OAM (DR) */
+ m_ppu1_open_bus = oam_read(space, m_oam.address);
+ PPU_REG(OAMDATA) = (PPU_REG(OAMDATA) + 1) % 2;
+ if (!PPU_REG(OAMDATA))
+ {
+ m_oam.address++;
+ m_oam.address &= 0x1ff;
+ m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
+ }
+ return m_ppu1_open_bus;
+ case RVMDATAL: /* Read data from VRAM (low) */
+ {
+ UINT32 addr = get_vram_address();
+ m_ppu1_open_bus = m_vram_read_buffer & 0xff;
+
+ if (!m_vram_fgr_high)
+ {
+ m_vram_read_buffer = vram_read(space, addr);
+ m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
+
+ m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
+ }
+
+ return m_ppu1_open_bus;
+ }
+ case RVMDATAH: /* Read data from VRAM (high) */
+ {
+ UINT32 addr = get_vram_address();
+ m_ppu1_open_bus = (m_vram_read_buffer >> 8) & 0xff;
+
+ if (m_vram_fgr_high)
+ {
+ m_vram_read_buffer = vram_read(space, addr);
+ m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
+
+ m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
+ }
+
+ return m_ppu1_open_bus;
+ }
+ case RCGDATA: /* Read data from CGRAM */
+ if (!(m_cgram_address & 0x01))
+ m_ppu2_open_bus = cgram_read(space, m_cgram_address);
+ else
+ {
+ m_ppu2_open_bus &= 0x80;
+ m_ppu2_open_bus |= cgram_read(space, m_cgram_address) & 0x7f;
+ }
+
+ m_cgram_address = (m_cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
+ return m_ppu2_open_bus;
+ case OPHCT: /* Horizontal counter data by ext/soft latch */
+ if (m_read_ophct)
+ {
+ m_ppu2_open_bus &= 0xfe;
+ m_ppu2_open_bus |= (m_beam.latch_horz >> 8) & 0x01;
+ }
+ else
+ {
+ m_ppu2_open_bus = m_beam.latch_horz & 0xff;
+ }
+ m_read_ophct ^= 1;
+ return m_ppu2_open_bus;
+ case OPVCT: /* Vertical counter data by ext/soft latch */
+ if (m_read_opvct)
+ {
+ m_ppu2_open_bus &= 0xfe;
+ m_ppu2_open_bus |= (m_beam.latch_vert >> 8) & 0x01;
+ }
+ else
+ {
+ m_ppu2_open_bus = m_beam.latch_vert & 0xff;
+ }
+ m_read_opvct ^= 1;
+ return m_ppu2_open_bus;
+ case STAT77: /* PPU status flag and version number */
+ value = m_stat77 & 0xc0; // 0x80 & 0x40 are Time Over / Range Over Sprite flags, set by the video code
+ // 0x20 - Master/slave mode select. Little is known about this bit. We always seem to read back 0 here.
+ value |= (m_ppu1_open_bus & 0x10);
+ value |= (m_ppu1_version & 0x0f);
+ m_stat77 = value; // not sure if this is needed...
+ m_ppu1_open_bus = value;
+ return m_ppu1_open_bus;
+ case STAT78: /* PPU status flag and version number */
+ m_read_ophct = 0;
+ m_read_opvct = 0;
+ if (wrio_bit7)
+ m_stat78 &= ~0x40; //clear ext latch if bit 7 of WRIO is set
+ m_stat78 = (m_stat78 & ~0x2f) | (m_ppu2_open_bus & 0x20) | (m_ppu2_version & 0x0f);
+ m_ppu2_open_bus = m_stat78;
+ return m_ppu2_open_bus;
+ }
+
+ /* note: remaining registers (Namely TM in Super Kick Boxing) returns MDR open bus, not PPU Open Bus! */
+ return m_openbus_cb(space, 0);
+}
+
+
+void snes_ppu_device::write(address_space &space, UINT32 offset, UINT8 data)
+{
+ switch (offset)
+ {
+ case INIDISP: /* Initial settings for screen */
+ if ((m_screen_disabled & 0x80) && (!(data & 0x80))) //a 1->0 force blank transition causes a reset OAM address
+ {
+ space.write_byte(OAMADDL, m_oam.saved_address_low);
+ space.write_byte(OAMADDH, m_oam.saved_address_high);
+ m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
+ }
+ m_screen_disabled = data & 0x80;
+ m_screen_brightness = (data & 0x0f) + 1;
+ break;
+ case OBSEL: /* Object size and data area designation */
+ m_oam.next_charmap = (data & 0x03) << 1;
+ m_oam.next_name_select = (((data & 0x18) >> 3) * 0x1000) << 1;
+ m_oam.next_size = (data & 0xe0) >> 5;
+ break;
+ case OAMADDL: /* Address for accessing OAM (low) */
+ m_oam.saved_address_low = data;
+ m_oam.address = (m_oam.address & 0xff00) + data;
+ m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
+ PPU_REG(OAMDATA) = 0;
+ break;
+ case OAMADDH: /* Address for accessing OAM (high) */
+ m_oam.saved_address_high = data;
+ m_oam.address = (m_oam.address & 0x00ff) | ((data & 0x01) << 8);
+ m_oam.priority_rotation = BIT(data, 7);
+ m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
+ PPU_REG(OAMDATA) = 0;
+ break;
+ case OAMDATA: /* Data for OAM write (DW) */
+ if (m_oam.address >= 0x100)
+ oam_write(space, m_oam.address, data);
+ else
+ {
+ if (!PPU_REG(OAMDATA))
+ m_oam.write_latch = data;
+ else
+ {
+ // in this case, we not only write data to the upper byte of the word,
+ // but also m_oam.write_latch to the lower byte (recall that
+ // PPU_REG(OAMDATA) is used to select high/low byte)
+ oam_write(space, m_oam.address, data);
+ PPU_REG(OAMDATA) = 0;
+ oam_write(space, m_oam.address, m_oam.write_latch);
+ PPU_REG(OAMDATA) = 1;
+ }
+ }
+ PPU_REG(OAMDATA) = (PPU_REG(OAMDATA) + 1) % 2;
+ if (!PPU_REG(OAMDATA))
+ {
+ m_oam.address++;
+ m_oam.address &= 0x1ff;
+ m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
+ }
+ return;
+ case BGMODE: /* BG mode and character size settings */
+ m_mode = data & 0x07;
+ dynamic_res_change();
+ m_bg3_priority_bit = BIT(data, 3);
+ m_layer[SNES_BG1].tile_size = BIT(data, 4);
+ m_layer[SNES_BG2].tile_size = BIT(data, 5);
+ m_layer[SNES_BG3].tile_size = BIT(data, 6);
+ m_layer[SNES_BG4].tile_size = BIT(data, 7);
+ m_update_offsets = 1;
+ break;
+ case MOSAIC: /* Size and screen designation for mosaic */
+ m_mosaic_size = (data & 0xf0) >> 4;
+ m_layer[SNES_BG1].mosaic_enabled = BIT(data, 0);
+ m_layer[SNES_BG2].mosaic_enabled = BIT(data, 1);
+ m_layer[SNES_BG3].mosaic_enabled = BIT(data, 2);
+ m_layer[SNES_BG4].mosaic_enabled = BIT(data, 3);
+ break;
+ case BG1SC: /* Address for storing SC data BG1 SC size designation */
+ case BG2SC: /* Address for storing SC data BG2 SC size designation */
+ case BG3SC: /* Address for storing SC data BG3 SC size designation */
+ case BG4SC: /* Address for storing SC data BG4 SC size designation */
+ m_layer[offset - BG1SC].tilemap = data & 0xfc;
+ m_layer[offset - BG1SC].tilemap_size = data & 0x3;
+ break;
+ case BG12NBA: /* Address for BG 1 and 2 character data */
+ m_layer[SNES_BG1].charmap = (data & 0x0f);
+ m_layer[SNES_BG2].charmap = (data & 0xf0) >> 4;
+ break;
+ case BG34NBA: /* Address for BG 3 and 4 character data */
+ m_layer[SNES_BG3].charmap = (data & 0x0f);
+ m_layer[SNES_BG4].charmap = (data & 0xf0) >> 4;
+ break;
+
+ // Anomie says "H Current = (Byte<<8) | (Prev&~7) | ((Current>>8)&7); V Current = (Current<<8) | Prev;" and Prev is shared by all scrolls but in Mode 7!
+ case BG1HOFS: /* BG1 - horizontal scroll (DW) */
+ /* In Mode 0->6 we use ppu_last_scroll as Prev */
+ m_layer[SNES_BG1].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG1].hoffs >> 8) & 7);
+ m_ppu_last_scroll = data;
+ /* In Mode 7 we use mode7_last_scroll as Prev */
+ m_mode7.hor_offset = (data << 8) | (m_mode7_last_scroll & ~7) | ((m_mode7.hor_offset >> 8) & 7);
+ m_mode7_last_scroll = data;
+ m_update_offsets = 1;
+ return;
+ case BG1VOFS: /* BG1 - vertical scroll (DW) */
+ /* In Mode 0->6 we use ppu_last_scroll as Prev */
+ m_layer[SNES_BG1].voffs = (data << 8) | m_ppu_last_scroll;
+ m_ppu_last_scroll = data;
+ /* In Mode 7 we use mode7_last_scroll as Prev */
+ m_mode7.ver_offset = (data << 8) | m_mode7_last_scroll;
+ m_mode7_last_scroll = data;
+ m_update_offsets = 1;
+ return;
+ case BG2HOFS: /* BG2 - horizontal scroll (DW) */
+ m_layer[SNES_BG2].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG2].hoffs >> 8) & 7);
+ m_ppu_last_scroll = data;
+ m_update_offsets = 1;
+ return;
+ case BG2VOFS: /* BG2 - vertical scroll (DW) */
+ m_layer[SNES_BG2].voffs = (data << 8) | (m_ppu_last_scroll);
+ m_ppu_last_scroll = data;
+ m_update_offsets = 1;
+ return;
+ case BG3HOFS: /* BG3 - horizontal scroll (DW) */
+ m_layer[SNES_BG3].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG3].hoffs >> 8) & 7);
+ m_ppu_last_scroll = data;
+ m_update_offsets = 1;
+ return;
+ case BG3VOFS: /* BG3 - vertical scroll (DW) */
+ m_layer[SNES_BG3].voffs = (data << 8) | (m_ppu_last_scroll);
+ m_ppu_last_scroll = data;
+ m_update_offsets = 1;
+ return;
+ case BG4HOFS: /* BG4 - horizontal scroll (DW) */
+ m_layer[SNES_BG4].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG4].hoffs >> 8) & 7);
+ m_ppu_last_scroll = data;
+ m_update_offsets = 1;
+ return;
+ case BG4VOFS: /* BG4 - vertical scroll (DW) */
+ m_layer[SNES_BG4].voffs = (data << 8) | (m_ppu_last_scroll);
+ m_ppu_last_scroll = data;
+ m_update_offsets = 1;
+ return;
+ case VMAIN: /* VRAM address increment value designation */
+ m_vram_fgr_high = (data & 0x80);
+ m_vram_fgr_increment = vram_fgr_inctab[data & 3];
+
+ if (data & 0xc)
+ {
+ int md = (data & 0xc) >> 2;
+
+ m_vram_fgr_count = vram_fgr_inccnts[md]; // 0x20, 0x40, 0x80
+ m_vram_fgr_mask = (m_vram_fgr_count * 8) - 1; // 0xff, 0x1ff, 0x2ff
+ m_vram_fgr_shift = vram_fgr_shiftab[md]; // 5, 6, 7
+ }
+ else
+ {
+ m_vram_fgr_count = 0;
+ }
+// printf("VMAIN: high %x inc %x count %x mask %x shift %x\n", m_vram_fgr_high, m_vram_fgr_increment, m_vram_fgr_count, m_vram_fgr_mask, m_vram_fgr_shift);
+ break;
+ case VMADDL: /* Address for VRAM read/write (low) */
+ {
+ UINT32 addr;
+ m_vmadd = (m_vmadd & 0xff00) | (data << 0);
+ addr = get_vram_address();
+ m_vram_read_buffer = vram_read(space, addr);
+ m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
+ }
+ break;
+ case VMADDH: /* Address for VRAM read/write (high) */
+ {
+ UINT32 addr;
+ m_vmadd = (m_vmadd & 0x00ff) | (data << 8);
+ addr = get_vram_address();
+ m_vram_read_buffer = vram_read(space, addr);
+ m_vram_read_buffer |= (vram_read(space, addr + 1) << 8);
+ }
+ break;
+ case VMDATAL: /* 2118: Data for VRAM write (low) */
+ {
+ UINT32 addr = get_vram_address();
+ vram_write(space, addr, data);
+
+ if (!m_vram_fgr_high)
+ m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
+ }
+ return;
+ case VMDATAH: /* 2119: Data for VRAM write (high) */
+ {
+ UINT32 addr = get_vram_address();
+ vram_write(space, addr + 1, data);
+
+ if (m_vram_fgr_high)
+ m_vmadd = (m_vmadd + m_vram_fgr_increment) & 0xffff;
+ }
+ return;
+ case M7SEL: /* Mode 7 initial settings */
+ m_mode7.repeat = (data >> 6) & 3;
+ m_mode7.vflip = BIT(data, 1);
+ m_mode7.hflip = BIT(data, 0);
+ break;
+ /* As per Anomie's doc: Reg = (Current<<8) | Prev; and there is only one Prev, shared by these matrix regs and Mode 7 scroll regs */
+ case M7A: /* Mode 7 COS angle/x expansion (DW) */
+ m_mode7.matrix_a = m_mode7_last_scroll + (data << 8);
+ m_mode7_last_scroll = data;
+ break;
+ case M7B: /* Mode 7 SIN angle/ x expansion (DW) */
+ m_mode7.matrix_b = m_mode7_last_scroll + (data << 8);
+ m_mode7_last_scroll = data;
+ break;
+ case M7C: /* Mode 7 SIN angle/y expansion (DW) */
+ m_mode7.matrix_c = m_mode7_last_scroll + (data << 8);
+ m_mode7_last_scroll = data;
+ break;
+ case M7D: /* Mode 7 COS angle/y expansion (DW) */
+ m_mode7.matrix_d = m_mode7_last_scroll + (data << 8);
+ m_mode7_last_scroll = data;
+ break;
+ case M7X: /* Mode 7 x center position (DW) */
+ m_mode7.origin_x = m_mode7_last_scroll + (data << 8);
+ m_mode7_last_scroll = data;
+ break;
+ case M7Y: /* Mode 7 y center position (DW) */
+ m_mode7.origin_y = m_mode7_last_scroll + (data << 8);
+ m_mode7_last_scroll = data;
+ break;
+ case CGADD: /* Initial address for colour RAM writing */
+ /* CGRAM is 16-bit, but when reading/writing we treat it as 8-bit, so we need to double the address */
+ m_cgram_address = data << 1;
+ break;
+ case CGDATA: /* Data for colour RAM */
+ cgram_write(space, m_cgram_address, data);
+ m_cgram_address = (m_cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
+ break;
+ case W12SEL: /* Window mask settings for BG1-2 */
+ if (data != PPU_REG(W12SEL))
+ {
+ m_layer[SNES_BG1].window1_invert = BIT(data, 0);
+ m_layer[SNES_BG1].window1_enabled = BIT(data, 1);
+ m_layer[SNES_BG1].window2_invert = BIT(data, 2);
+ m_layer[SNES_BG1].window2_enabled = BIT(data, 3);
+ m_layer[SNES_BG2].window1_invert = BIT(data, 4);
+ m_layer[SNES_BG2].window1_enabled = BIT(data, 5);
+ m_layer[SNES_BG2].window2_invert = BIT(data, 6);
+ m_layer[SNES_BG2].window2_enabled = BIT(data, 7);
+ m_update_windows = 1;
+ }
+ break;
+ case W34SEL: /* Window mask settings for BG3-4 */
+ if (data != PPU_REG(W34SEL))
+ {
+ m_layer[SNES_BG3].window1_invert = BIT(data, 0);
+ m_layer[SNES_BG3].window1_enabled = BIT(data, 1);
+ m_layer[SNES_BG3].window2_invert = BIT(data, 2);
+ m_layer[SNES_BG3].window2_enabled = BIT(data, 3);
+ m_layer[SNES_BG4].window1_invert = BIT(data, 4);
+ m_layer[SNES_BG4].window1_enabled = BIT(data, 5);
+ m_layer[SNES_BG4].window2_invert = BIT(data, 6);
+ m_layer[SNES_BG4].window2_enabled = BIT(data, 7);
+ m_update_windows = 1;
+ }
+ break;
+ case WOBJSEL: /* Window mask settings for objects */
+ if (data != PPU_REG(WOBJSEL))
+ {
+ m_layer[SNES_OAM].window1_invert = BIT(data, 0);
+ m_layer[SNES_OAM].window1_enabled = BIT(data, 1);
+ m_layer[SNES_OAM].window2_invert = BIT(data, 2);
+ m_layer[SNES_OAM].window2_enabled = BIT(data, 3);
+ m_layer[SNES_COLOR].window1_invert = BIT(data, 4);
+ m_layer[SNES_COLOR].window1_enabled = BIT(data, 5);
+ m_layer[SNES_COLOR].window2_invert = BIT(data, 6);
+ m_layer[SNES_COLOR].window2_enabled = BIT(data, 7);
+ m_update_windows = 1;
+ }
+ break;
+ case WH0: /* Window 1 left position */
+ if (data != PPU_REG(WH0))
+ {
+ m_window1_left = data;
+ m_update_windows = 1;
+ }
+ break;
+ case WH1: /* Window 1 right position */
+ if (data != PPU_REG(WH1))
+ {
+ m_window1_right = data;
+ m_update_windows = 1;
+ }
+ break;
+ case WH2: /* Window 2 left position */
+ if (data != PPU_REG(WH2))
+ {
+ m_window2_left = data;
+ m_update_windows = 1;
+ }
+ break;
+ case WH3: /* Window 2 right position */
+ if (data != PPU_REG(WH3))
+ {
+ m_window2_right = data;
+ m_update_windows = 1;
+ }
+ break;
+ case WBGLOG: /* Window mask logic for BG's */
+ if (data != PPU_REG(WBGLOG))
+ {
+ m_layer[SNES_BG1].wlog_mask = data & 0x03;
+ m_layer[SNES_BG2].wlog_mask = (data & 0x0c) >> 2;
+ m_layer[SNES_BG3].wlog_mask = (data & 0x30) >> 4;
+ m_layer[SNES_BG4].wlog_mask = (data & 0xc0) >> 6;
+ m_update_windows = 1;
+ }
+ break;
+ case WOBJLOG: /* Window mask logic for objects */
+ if (data != PPU_REG(WOBJLOG))
+ {
+ m_layer[SNES_OAM].wlog_mask = data & 0x03;
+ m_layer[SNES_COLOR].wlog_mask = (data & 0x0c) >> 2;
+ m_update_windows = 1;
+ }
+ break;
+ case TM: /* Main screen designation */
+ m_layer[SNES_BG1].main_bg_enabled = BIT(data, 0);
+ m_layer[SNES_BG2].main_bg_enabled = BIT(data, 1);
+ m_layer[SNES_BG3].main_bg_enabled = BIT(data, 2);
+ m_layer[SNES_BG4].main_bg_enabled = BIT(data, 3);
+ m_layer[SNES_OAM].main_bg_enabled = BIT(data, 4);
+ break;
+ case TS: /* Subscreen designation */
+ m_layer[SNES_BG1].sub_bg_enabled = BIT(data, 0);
+ m_layer[SNES_BG2].sub_bg_enabled = BIT(data, 1);
+ m_layer[SNES_BG3].sub_bg_enabled = BIT(data, 2);
+ m_layer[SNES_BG4].sub_bg_enabled = BIT(data, 3);
+ m_layer[SNES_OAM].sub_bg_enabled = BIT(data, 4);
+ break;
+ case TMW: /* Window mask for main screen designation */
+ m_layer[SNES_BG1].main_window_enabled = BIT(data, 0);
+ m_layer[SNES_BG2].main_window_enabled = BIT(data, 1);
+ m_layer[SNES_BG3].main_window_enabled = BIT(data, 2);
+ m_layer[SNES_BG4].main_window_enabled = BIT(data, 3);
+ m_layer[SNES_OAM].main_window_enabled = BIT(data, 4);
+ break;
+ case TSW: /* Window mask for subscreen designation */
+ m_layer[SNES_BG1].sub_window_enabled = BIT(data, 0);
+ m_layer[SNES_BG2].sub_window_enabled = BIT(data, 1);
+ m_layer[SNES_BG3].sub_window_enabled = BIT(data, 2);
+ m_layer[SNES_BG4].sub_window_enabled = BIT(data, 3);
+ m_layer[SNES_OAM].sub_window_enabled = BIT(data, 4);
+ break;
+ case CGWSEL: /* Initial settings for Fixed colour addition or screen addition */
+ m_clip_to_black = (data >> 6) & 0x03;
+ m_prevent_color_math = (data >> 4) & 0x03;
+ m_sub_add_mode = BIT(data, 1);
+ m_direct_color = BIT(data, 0);
+#ifdef SNES_DBG_REG_W
+ if ((data & 0x2) != (PPU_REG(CGWSEL) & 0x2))
+ osd_printf_debug("Add/Sub Layer: %s\n", ((data & 0x2) >> 1) ? "Subscreen" : "Fixed colour");
+#endif
+ break;
+ case CGADSUB: /* Addition/Subtraction designation for each screen */
+ m_color_modes = data & 0xc0;
+ m_layer[SNES_BG1].color_math = BIT(data, 0);
+ m_layer[SNES_BG2].color_math = BIT(data, 1);
+ m_layer[SNES_BG3].color_math = BIT(data, 2);
+ m_layer[SNES_BG4].color_math = BIT(data, 3);
+ m_layer[SNES_OAM].color_math = BIT(data, 4);
+ m_layer[SNES_COLOR].color_math = BIT(data, 5);
+ break;
+ case COLDATA: /* Fixed colour data for fixed colour addition/subtraction */
+ {
+ /* Store it in the extra space we made in the CGRAM. It doesn't really go there, but it's as good a place as any. */
+ UINT8 r, g, b;
+
+ /* Get existing value. */
+ r = m_cgram[FIXED_COLOUR] & 0x1f;
+ g = (m_cgram[FIXED_COLOUR] & 0x3e0) >> 5;
+ b = (m_cgram[FIXED_COLOUR] & 0x7c00) >> 10;
+ /* Set new value */
+ if (data & 0x20)
+ r = data & 0x1f;
+ if (data & 0x40)
+ g = data & 0x1f;
+ if (data & 0x80)
+ b = data & 0x1f;
+ m_cgram[FIXED_COLOUR] = (r | (g << 5) | (b << 10));
+ } break;
+ case SETINI: /* Screen mode/video select */
+ m_interlace = (data & 0x01) ? 2 : 1;
+ m_obj_interlace = (data & 0x02) ? 2 : 1;
+ m_beam.last_visible_line = (data & 0x04) ? 240 : 225;
+ m_pseudo_hires = BIT(data, 3);
+ m_mode7.extbg = BIT(data, 6);
+ dynamic_res_change();
+#ifdef SNES_DBG_REG_W
+ if ((data & 0x8) != (PPU_REG(SETINI) & 0x8))
+ osd_printf_debug("Pseudo 512 mode: %s\n", (data & 0x8) ? "on" : "off");
+#endif
+ break;
+ }
+
+ PPU_REG(offset) = data;
+}
+
+/***** Debug Functions *****/
+
+#if SNES_LAYER_DEBUG
+
+#define DEBUG_TOGGLE(bit, debug_settings, MSG1, MSG2) \
+ if (BIT(toggles, bit) && !debug_settings) \
+ { \
+ debug_settings = 1; \
+ popmessage MSG1; \
+ } \
+ else if (!BIT(toggles, bit) && debug_settings) \
+ { \
+ debug_settings = 0; \
+ popmessage MSG2; \
+ }
+
+UINT8 snes_ppu_device::dbg_video( UINT16 curline )
+{
+ int i;
+ UINT8 toggles = machine().root_device().ioport("DEBUG1")->read_safe(0);
+ m_debug_options.select_pri[SNES_BG1] = (toggles & 0x03);
+ m_debug_options.select_pri[SNES_BG2] = (toggles & 0x0c) >> 2;
+ m_debug_options.select_pri[SNES_BG3] = (toggles & 0x30) >> 4;
+ m_debug_options.select_pri[SNES_BG4] = (toggles & 0xc0) >> 6;
+
+ toggles = machine().root_device().ioport("DEBUG2")->read_safe(0);
+ for (i = 0; i < 4; i++)
+ DEBUG_TOGGLE(i, m_debug_options.bg_disabled[i], ("Debug: Disabled BG%d.\n", i + 1), ("Debug: Enabled BG%d.\n", i + 1))
+ DEBUG_TOGGLE(4, m_debug_options.bg_disabled[SNES_OAM], ("Debug: Disabled OAM.\n"), ("Debug: Enabled OAM.\n"))
+ DEBUG_TOGGLE(5, m_debug_options.draw_subscreen, ("Debug: Switched screens.\n"), ("Debug: Switched screens.\n"))
+ DEBUG_TOGGLE(6, m_debug_options.colormath_disabled, ("Debug: Disabled Color Math.\n"), ("Debug: Enabled Color Math.\n"))
+ DEBUG_TOGGLE(7, m_debug_options.windows_disabled, ("Debug: Disabled Window Masks.\n"), ("Debug: Enabled Window Masks.\n"))
+
+ toggles = machine().root_device().ioport("DEBUG4")->read_safe(0);
+ for (i = 0; i < 8; i++)
+ DEBUG_TOGGLE(i, m_debug_options.mode_disabled[i], ("Debug: Disabled Mode %d drawing.\n", i), ("Debug: Enabled Mode %d drawing.\n", i))
+
+ toggles = machine().root_device().ioport("DEBUG3")->read_safe(0);
+ DEBUG_TOGGLE(2, m_debug_options.mosaic_disabled, ("Debug: Disabled Mosaic.\n"), ("Debug: Enabled Mosaic.\n"))
+ m_debug_options.sprite_reversed = BIT(toggles, 7);
+ m_debug_options.select_pri[SNES_OAM] = (toggles & 0x70) >> 4;
+
+#ifdef MAME_DEBUG
+ /* Once per frame, log video properties */
+ if (curline == 1)
+ {
+ static const char WINLOGIC[4] = { '|', '&', '^', '!' };
+
+ logerror("%s", m_debug_options.windows_disabled?" ":"W");
+ logerror("%s1 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
+ m_debug_options.bg_disabled[0]?" ":"*",
+ (PPU_REG(TM) & 0x1)?"M":" ",
+ (PPU_REG(TS) & 0x1)?"S":" ",
+ (PPU_REG(CGADSUB) & 0x1)?"B":" ",
+ (PPU_REG(TMW) & 0x1)?"m":" ",
+ (PPU_REG(TSW) & 0x1)?"s":" ",
+ WINLOGIC[(PPU_REG(WBGLOG) & 0x3)],
+ (PPU_REG(W12SEL) & 0x2)?((PPU_REG(W12SEL) & 0x1)?"o":"i"):" ",
+ (PPU_REG(W12SEL) & 0x8)?((PPU_REG(W12SEL) & 0x4)?"o":"i"):" ",
+ m_layer[SNES_BG1].tile_size + 1,
+ (PPU_REG(MOSAIC) & 0x1)?"m":" ",
+ PPU_REG(BG1SC) & 0x3,
+ (PPU_REG(BG1SC) & 0xfc) << 9,
+ m_layer[SNES_BG1].charmap << 13);
+ logerror("%s2 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
+ m_debug_options.bg_disabled[1]?" ":"*",
+ (PPU_REG(TM) & 0x2)?"M":" ",
+ (PPU_REG(TS) & 0x2)?"S":" ",
+ (PPU_REG(CGADSUB) & 0x2)?"B":" ",
+ (PPU_REG(TMW) & 0x2)?"m":" ",
+ (PPU_REG(TSW) & 0x2)?"s":" ",
+ WINLOGIC[(PPU_REG(WBGLOG) & 0xc) >> 2],
+ (PPU_REG(W12SEL) & 0x20)?((PPU_REG(W12SEL) & 0x10)?"o":"i"):" ",
+ (PPU_REG(W12SEL) & 0x80)?((PPU_REG(W12SEL) & 0x40)?"o":"i"):" ",
+ m_layer[SNES_BG2].tile_size + 1,
+ (PPU_REG(MOSAIC) & 0x2)?"m":" ",
+ PPU_REG(BG2SC) & 0x3,
+ (PPU_REG(BG2SC) & 0xfc) << 9,
+ m_layer[SNES_BG2].charmap << 13);
+ logerror("%s3 %s%s%s%s%s%c%s%s%d%s%s%d %4X %4X",
+ m_debug_options.bg_disabled[2]?" ":"*",
+ (PPU_REG(TM) & 0x4)?"M":" ",
+ (PPU_REG(TS) & 0x4)?"S":" ",
+ (PPU_REG(CGADSUB) & 0x4)?"B":" ",
+ (PPU_REG(TMW) & 0x4)?"m":" ",
+ (PPU_REG(TSW) & 0x4)?"s":" ",
+ WINLOGIC[(PPU_REG(WBGLOG) & 0x30)>>4],
+ (PPU_REG(W34SEL) & 0x2)?((PPU_REG(W34SEL) & 0x1)?"o":"i"):" ",
+ (PPU_REG(W34SEL) & 0x8)?((PPU_REG(W34SEL) & 0x4)?"o":"i"):" ",
+ m_layer[SNES_BG3].tile_size + 1,
+ (PPU_REG(MOSAIC) & 0x4)?"m":" ",
+ (PPU_REG(BGMODE) & 0x8)?"P":" ",
+ PPU_REG(BG3SC) & 0x3,
+ (PPU_REG(BG3SC) & 0xfc) << 9,
+ m_layer[SNES_BG3].charmap << 13);
+ logerror("%s4 %s%s%s%s%s%c%s%s%d%s %d %4X %4X",
+ m_debug_options.bg_disabled[3]?" ":"*",
+ (PPU_REG(TM) & 0x8)?"M":" ",
+ (PPU_REG(TS) & 0x8)?"S":" ",
+ (PPU_REG(CGADSUB) & 0x8)?"B":" ",
+ (PPU_REG(TMW) & 0x8)?"m":" ",
+ (PPU_REG(TSW) & 0x8)?"s":" ",
+ WINLOGIC[(PPU_REG(WBGLOG) & 0xc0)>>6],
+ (PPU_REG(W34SEL) & 0x20)?((PPU_REG(W34SEL) & 0x10)?"o":"i"):" ",
+ (PPU_REG(W34SEL) & 0x80)?((PPU_REG(W34SEL) & 0x40)?"o":"i"):" ",
+ m_layer[SNES_BG4].tile_size + 1,
+ (PPU_REG(MOSAIC) & 0x8)?"m":" ",
+ PPU_REG(BG4SC) & 0x3,
+ (PPU_REG(BG4SC) & 0xfc) << 9,
+ m_layer[SNES_BG4].charmap << 13 );
+ logerror("%sO %s%s%s%s%s%c%s%s %4X",
+ m_debug_options.bg_disabled[4]?" ":"*",
+ (PPU_REG(TM) & 0x10)?"M":" ",
+ (PPU_REG(TS) & 0x10)?"S":" ",
+ (PPU_REG(CGADSUB) & 0x10)?"B":" ",
+ (PPU_REG(TMW) & 0x10)?"m":" ",
+ (PPU_REG(TSW) & 0x10)?"s":" ",
+ WINLOGIC[(PPU_REG(WOBJLOG) & 0x3)],
+ (PPU_REG(WOBJSEL) & 0x2)?((PPU_REG(WOBJSEL) & 0x1)?"o":"i"):" ",
+ (PPU_REG(WOBJSEL) & 0x8)?((PPU_REG(WOBJSEL) & 0x4)?"o":"i"):" ",
+ m_layer[SNES_OAM].charmap << 13 );
+ logerror("%sB %s %c%s%s",
+ m_debug_options.colormath_disabled?" ":"*",
+ (PPU_REG(CGADSUB) & 0x20)?"B":" ",
+ WINLOGIC[(PPU_REG(WOBJLOG) & 0xc)>>2],
+ (PPU_REG(WOBJSEL) & 0x20)?((PPU_REG(WOBJSEL) & 0x10)?"o":"i"):" ",
+ (PPU_REG(WOBJSEL) & 0x80)?((PPU_REG(WOBJSEL) & 0x40)?"o":"i"):" " );
+ logerror("Flags: %s%s%s %s %2d", (PPU_REG(CGWSEL) & 0x2)?"S":"F", (PPU_REG(CGADSUB) & 0x80)?"-":"+", (PPU_REG(CGADSUB) & 0x40)?" 50%":"100%",(PPU_REG(CGWSEL) & 0x1)?"D":"P", (PPU_REG(MOSAIC) & 0xf0) >> 4 );
+ logerror("SetINI: %s %s %s %s %s %s", (PPU_REG(SETINI) & 0x1)?" I":"NI", (PPU_REG(SETINI) & 0x2)?"P":"R", (PPU_REG(SETINI) & 0x4)?"240":"225",(PPU_REG(SETINI) & 0x8)?"512":"256",(PPU_REG(SETINI) & 0x40)?"E":"N",(PPU_REG(SETINI) & 0x80)?"ES":"NS" );
+ logerror("Mode7: A %5d B %5d", m_mode7.matrix_a, m_mode7.matrix_b );
+ logerror(" %s%s%s C %5d D %5d", (PPU_REG(M7SEL) & 0xc0)?((PPU_REG(M7SEL) & 0x40)?"0":"C"):"R", (PPU_REG(M7SEL) & 0x1)?"H":" ", (PPU_REG(M7SEL) & 0x2)?"V":" ", m_mode7.matrix_c, m_mode7.matrix_d );
+ logerror(" X %5d Y %5d", m_mode7.origin_x, m_mode7.origin_y );
+ }
+#endif
+
+ return 0;
+}
+#endif /* SNES_LAYER_DEBUG */
diff --git a/src/devices/video/snes_ppu.h b/src/devices/video/snes_ppu.h
new file mode 100644
index 00000000000..806865a0729
--- /dev/null
+++ b/src/devices/video/snes_ppu.h
@@ -0,0 +1,295 @@
+// license:BSD-3-Clause
+// copyright-holders:Anthony Kruize, Fabio Priuli
+/***************************************************************************
+
+ SNES PPU
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __SNES_PPU_H__
+#define __SNES_PPU_H__
+
+
+#define MCLK_NTSC (21477272) /* verified */
+#define MCLK_PAL (21218370) /* verified */
+
+#define DOTCLK_NTSC (MCLK_NTSC/4)
+#define DOTCLK_PAL (MCLK_PAL/4)
+
+#define SNES_SCR_WIDTH 256 /* 32 characters 8 pixels wide */
+#define SNES_SCR_HEIGHT_NTSC 225 /* Can be 224 or 240 height */
+#define SNES_SCR_HEIGHT_PAL 240 /* ??? */
+#define SNES_VTOTAL_NTSC 262 /* Maximum number of lines for NTSC systems */
+#define SNES_VTOTAL_PAL 312 /* Maximum number of lines for PAL systems */
+#define SNES_HTOTAL 341 /* Maximum number pixels per line (incl. hblank) */
+
+#define SNES_NTSC 0x00
+#define SNES_PAL 0x10
+
+
+#define SNES_LAYER_DEBUG 0
+
+
+/* offset-per-tile modes */
+enum
+{
+ SNES_OPT_NONE = 0,
+ SNES_OPT_MODE2,
+ SNES_OPT_MODE4,
+ SNES_OPT_MODE6
+};
+
+/* layers */
+enum
+{
+ SNES_BG1 = 0,
+ SNES_BG2,
+ SNES_BG3,
+ SNES_BG4,
+ SNES_OAM,
+ SNES_COLOR
+};
+
+
+struct SNES_SCANLINE
+{
+ int enable, clip;
+
+ UINT16 buffer[SNES_SCR_WIDTH];
+ UINT8 priority[SNES_SCR_WIDTH];
+ UINT8 layer[SNES_SCR_WIDTH];
+ UINT8 blend_exception[SNES_SCR_WIDTH];
+};
+
+// ======================> snes_ppu_device
+
+class snes_ppu_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ snes_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // inline configuration helpers
+ template<class _Object> static devcb_base &static_set_open_bus_callback(device_t &device, _Object object) { return downcast<snes_ppu_device &>(device).m_openbus_cb.set_callback(object); }
+
+ UINT8 m_regs[0x40];
+
+ SNES_SCANLINE m_scanlines[2];
+
+ struct
+ {
+ /* clipmasks */
+ UINT8 window1_enabled, window1_invert;
+ UINT8 window2_enabled, window2_invert;
+ UINT8 wlog_mask;
+ /* color math enabled */
+ UINT8 color_math;
+
+ UINT8 charmap;
+ UINT8 tilemap;
+ UINT8 tilemap_size;
+
+ UINT8 tile_size;
+ UINT8 mosaic_enabled; // actually used only for layers 0->3!
+
+ UINT8 main_window_enabled;
+ UINT8 sub_window_enabled;
+ UINT8 main_bg_enabled;
+ UINT8 sub_bg_enabled;
+
+ UINT16 hoffs;
+ UINT16 voffs;
+ } m_layer[6]; // this is for the BG1 - BG2 - BG3 - BG4 - OBJ - color layers
+
+ struct
+ {
+ UINT8 address_low;
+ UINT8 address_high;
+ UINT8 saved_address_low;
+ UINT8 saved_address_high;
+ UINT16 address;
+ UINT16 priority_rotation;
+ UINT8 next_charmap;
+ UINT8 next_size;
+ UINT8 size;
+ UINT32 next_name_select;
+ UINT32 name_select;
+ UINT8 first_sprite;
+ UINT8 flip;
+ UINT16 write_latch;
+ } m_oam;
+
+ struct
+ {
+ UINT16 latch_horz;
+ UINT16 latch_vert;
+ UINT16 current_vert;
+ UINT8 last_visible_line;
+ UINT8 interlace_count;
+ } m_beam;
+
+ struct
+ {
+ UINT8 repeat;
+ UINT8 hflip;
+ UINT8 vflip;
+ INT16 matrix_a;
+ INT16 matrix_b;
+ INT16 matrix_c;
+ INT16 matrix_d;
+ INT16 origin_x;
+ INT16 origin_y;
+ UINT16 hor_offset;
+ UINT16 ver_offset;
+ UINT8 extbg;
+ } m_mode7;
+
+ struct OAM
+ {
+ UINT16 tile;
+ INT16 x, y;
+ UINT8 size, vflip, hflip, priority_bits, pal;
+ int height, width;
+ };
+
+ struct OAM m_oam_spritelist[SNES_SCR_WIDTH / 2];
+
+ UINT8 m_oam_itemlist[32];
+
+ struct TILELIST {
+ INT16 x;
+ UINT16 priority, pal, tileaddr;
+ int hflip;
+ };
+
+ struct TILELIST m_oam_tilelist[34];
+
+#if SNES_LAYER_DEBUG
+ struct DEBUGOPTS
+ {
+ UINT8 bg_disabled[5];
+ UINT8 mode_disabled[8];
+ UINT8 draw_subscreen;
+ UINT8 windows_disabled;
+ UINT8 mosaic_disabled;
+ UINT8 colormath_disabled;
+ UINT8 sprite_reversed;
+ UINT8 select_pri[5];
+ };
+ struct DEBUGOPTS m_debug_options;
+#endif
+
+ UINT8 m_mosaic_size;
+ UINT8 m_clip_to_black;
+ UINT8 m_prevent_color_math;
+ UINT8 m_sub_add_mode;
+ UINT8 m_bg3_priority_bit;
+ UINT8 m_direct_color;
+ UINT8 m_ppu_last_scroll; /* as per Anomie's doc and Theme Park, all scroll regs shares (but mode 7 ones) the same
+ 'previous' scroll value */
+ UINT8 m_mode7_last_scroll; /* as per Anomie's doc mode 7 scroll regs use a different value, shared with mode 7 matrix! */
+
+ UINT8 m_ppu1_open_bus, m_ppu2_open_bus;
+ UINT8 m_ppu1_version, m_ppu2_version;
+ UINT8 m_window1_left, m_window1_right, m_window2_left, m_window2_right;
+
+ UINT16 m_mosaic_table[16][4096];
+ UINT8 m_clipmasks[6][SNES_SCR_WIDTH];
+ UINT8 m_update_windows;
+ UINT8 m_update_offsets;
+ UINT8 m_update_oam_list;
+ UINT8 m_mode;
+ UINT8 m_interlace; //doubles the visible resolution
+ UINT8 m_obj_interlace;
+ UINT8 m_screen_brightness;
+ UINT8 m_screen_disabled;
+ UINT8 m_pseudo_hires;
+ UINT8 m_color_modes;
+ UINT8 m_stat77;
+ UINT8 m_stat78;
+
+ UINT16 m_htmult; /* in 512 wide, we run HTOTAL double and halve it on latching */
+ UINT16 m_cgram_address; /* CGRAM address */
+ UINT8 m_read_ophct;
+ UINT8 m_read_opvct;
+ UINT16 m_vram_fgr_high;
+ UINT16 m_vram_fgr_increment;
+ UINT16 m_vram_fgr_count;
+ UINT16 m_vram_fgr_mask;
+ UINT16 m_vram_fgr_shift;
+ UINT16 m_vram_read_buffer;
+ UINT16 m_vmadd;
+
+ inline UINT16 get_bgcolor(UINT8 direct_colors, UINT16 palette, UINT8 color);
+ inline void set_scanline_pixel(int screen, INT16 x, UINT16 color, UINT8 priority, UINT8 layer, int blend);
+ inline void draw_bgtile_lores(UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority);
+ inline void draw_bgtile_hires(UINT8 layer, INT16 ii, UINT8 colour, UINT16 pal, UINT8 direct_colors, UINT8 priority);
+ inline void draw_oamtile(INT16 ii, UINT8 colour, UINT16 pal, UINT8 priority);
+ inline void draw_tile(UINT8 planes, UINT8 layer, UINT32 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires);
+ inline UINT32 get_tmap_addr(UINT8 layer, UINT8 tile_size, UINT32 base, UINT32 x, UINT32 y);
+ inline void update_line(UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a, UINT8 color_depth, UINT8 hires, UINT8 offset_per_tile, UINT8 direct_colors);
+ void update_line_mode7(UINT16 curline, UINT8 layer, UINT8 priority_b, UINT8 priority_a);
+ void update_obsel(void);
+ void oam_list_build(void);
+ int is_sprite_on_scanline(UINT16 curline, UINT8 sprite);
+ void update_objects_rto(UINT16 curline);
+ void update_objects(UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3);
+ void update_mode_0(UINT16 curline);
+ void update_mode_1(UINT16 curline);
+ void update_mode_2(UINT16 curline);
+ void update_mode_3(UINT16 curline);
+ void update_mode_4(UINT16 curline);
+ void update_mode_5(UINT16 curline);
+ void update_mode_6(UINT16 curline);
+ void update_mode_7(UINT16 curline);
+ void draw_screens(UINT16 curline);
+ void update_windowmasks(void);
+ void update_offsets(void);
+ inline void draw_blend(UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens);
+ void refresh_scanline(bitmap_rgb32 &bitmap, UINT16 curline);
+
+ inline INT16 current_x() { return m_screen->hpos() / m_htmult; }
+ inline INT16 current_y() { return m_screen->vpos(); }
+ void set_latch_hv(INT16 x, INT16 y);
+ void dynamic_res_change();
+ inline UINT32 get_vram_address();
+ UINT8 dbg_video(UINT16 curline);
+
+ UINT8 read(address_space &space, UINT32 offset, UINT8 wrio_bit7);
+ void write(address_space &space, UINT32 offset, UINT8 data);
+
+ DECLARE_READ8_MEMBER( oam_read );
+ DECLARE_WRITE8_MEMBER( oam_write );
+ DECLARE_READ8_MEMBER( cgram_read );
+ DECLARE_WRITE8_MEMBER( cgram_write );
+ DECLARE_READ8_MEMBER( vram_read );
+ DECLARE_WRITE8_MEMBER( vram_write );
+ UINT16 *m_oam_ram; /* Object Attribute Memory */
+ UINT16 *m_cgram; /* Palette RAM */
+ UINT8 *m_vram; /* Video RAM (TODO: Should be 16-bit, but it's easier this way) */
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ devcb_read16 m_openbus_cb;
+};
+
+
+// device type definition
+extern const device_type SNES_PPU;
+
+
+/***************************************************************************
+ INTERFACE CONFIGURATION MACROS
+ ***************************************************************************/
+
+#define MCFG_SNES_PPU_OPENBUS_CB(_read) \
+ devcb = &snes_ppu_device::static_set_open_bus_callback(*device, DEVCB_##_read);
+
+#endif
diff --git a/src/devices/video/stvvdp1.c b/src/devices/video/stvvdp1.c
new file mode 100644
index 00000000000..126089d40b5
--- /dev/null
+++ b/src/devices/video/stvvdp1.c
@@ -0,0 +1,2160 @@
+// license:LGPL-2.1+
+// copyright-holders:David Haywood, Angelo Salese, Olivier Galibert, Mariusz Wojcieszek, R. Belmont
+/*
+
+STV - VDP1
+
+the vdp1 draws to the FRAMEBUFFER which is mapped in memory
+
+Framebuffer todo:
+- finish manual erase
+- add proper framebuffer erase
+- 8 bpp support - now we always draw as 16 bpp, but this is not a problem since
+ VDP2 interprets framebuffer as 8 bpp in these cases
+
+*/
+
+
+#include "emu.h"
+#include "includes/stv.h"
+
+#define VDP1_LOG 0
+
+
+enum { FRAC_SHIFT = 16 };
+
+struct spoint {
+ INT32 x, y;
+ INT32 u, v;
+};
+
+struct shaded_point
+{
+ INT32 x,y;
+ INT32 r,g,b;
+};
+
+#define RGB_R(_color) (_color & 0x1f)
+#define RGB_G(_color) ((_color >> 5) & 0x1f)
+#define RGB_B(_color) ((_color >> 10) & 0x1f)
+
+#define SWAP_INT32(_a,_b) \
+ { \
+ INT32 t; \
+ t = _a; \
+ _a = _b; \
+ _b = t; \
+ }
+
+#define SWAP_INT32PTR(_p1, _p2) \
+ { \
+ INT32 *p; \
+ p = _p1; \
+ _p1 = _p2; \
+ _p2 = p; \
+ }
+
+/*TV Mode Selection Register */
+/*
+ xxxx xxxx xxxx ---- | UNUSED
+ ---- ---- ---- x--- | VBlank Erase/Write (VBE)
+ ---- ---- ---- -xxx | TV Mode (TVM)
+ TV-Mode:
+ This sets the Frame Buffer size,the rotation of the Frame Buffer & the bit width.
+ bit 2 HDTV disable(0)/enable(1)
+ bit 1 non-rotation/rotation(1)
+ bit 0 16(0)/8(1) bits per pixel
+ Size of the Frame Buffer:
+ 7 invalid
+ 6 invalid
+ 5 invalid
+ 4 512x256
+ 3 512x512
+ 2 512x256
+ 1 1024x256
+ 0 512x256
+*/
+
+/*Frame Buffer Change Mode Register*/
+/*
+ xxxx xxxx xxx- ---- | UNUSED
+ ---- ---- ---x ---- | Even/Odd Coordinate Select Bit (EOS)
+ ---- ---- ---- x--- | Double Interlace Mode (DIE)
+ ---- ---- ---- -x-- | Double Interlace Draw Line (DIL)
+ ---- ---- ---- --x- | Frame Buffer Change Trigger (FCM)
+ ---- ---- ---- ---x | Frame Buffer Change Mode (FCT)
+*/
+#define STV_VDP1_FBCR ((m_vdp1_regs[0x002/2] >> 0)&0xffff)
+#define STV_VDP1_EOS ((STV_VDP1_FBCR & 0x0010) >> 4)
+#define STV_VDP1_DIE ((STV_VDP1_FBCR & 0x0008) >> 3)
+#define STV_VDP1_DIL ((STV_VDP1_FBCR & 0x0004) >> 2)
+#define STV_VDP1_FCM ((STV_VDP1_FBCR & 0x0002) >> 1)
+#define STV_VDP1_FCT ((STV_VDP1_FBCR & 0x0001) >> 0)
+
+/*Plot Trigger Register*/
+/*
+ xxxx xxxx xxxx xx-- | UNUSED
+ ---- ---- ---- --xx | Plot Trigger Mode (PTM)
+
+ Plot Trigger Mode:
+ 3 Invalid
+ 2 Automatic draw
+ 1 VDP1 draw by request
+ 0 VDP1 Idle (no access)
+*/
+#define STV_VDP1_PTMR ((m_vdp1_regs[0x004/2])&0xffff)
+#define STV_VDP1_PTM ((STV_VDP1_PTMR & 0x0003) >> 0)
+#define PTM_0 m_vdp1_regs[0x004/2]&=~0x0001
+
+/*
+ Erase/Write Data Register
+ 16 bpp = data
+ 8 bpp = erase/write data for even/odd X coordinates
+*/
+#define STV_VDP1_EWDR ((m_vdp1_regs[0x006/2])&0xffff)
+
+/*Erase/Write Upper-Left register*/
+/*
+ x--- ---- ---- ---- | UNUSED
+ -xxx xxx- ---- ---- | X1 register
+ ---- ---x xxxx xxxx | Y1 register
+
+*/
+#define STV_VDP1_EWLR ((m_vdp1_regs[0x008/2])&0xffff)
+#define STV_VDP1_EWLR_X1 ((STV_VDP1_EWLR & 0x7e00) >> 9)
+#define STV_VDP1_EWLR_Y1 ((STV_VDP1_EWLR & 0x01ff) >> 0)
+/*Erase/Write Lower-Right register*/
+/*
+ xxxx xxx- ---- ---- | X3 register
+ ---- ---x xxxx xxxx | Y3 register
+
+*/
+#define STV_VDP1_EWRR ((m_vdp1_regs[0x00a/2])&0xffff)
+#define STV_VDP1_EWRR_X3 ((STV_VDP1_EWRR & 0xfe00) >> 9)
+#define STV_VDP1_EWRR_Y3 ((STV_VDP1_EWRR & 0x01ff) >> 0)
+/*Transfer End Status Register*/
+/*
+ xxxx xxxx xxxx xx-- | UNUSED
+ ---- ---- ---- --x- | CEF
+ ---- ---- ---- ---x | BEF
+
+*/
+#define STV_VDP1_EDSR ((m_vdp1_regs[0x010/2])&0xffff)
+#define STV_VDP1_CEF (STV_VDP1_EDSR & 2)
+#define STV_VDP1_BEF (STV_VDP1_EDSR & 1)
+/**/
+
+
+
+READ16_MEMBER( saturn_state::saturn_vdp1_regs_r )
+{
+ //logerror ("cpu %s (PC=%08X) VDP1: Read from Registers, Offset %04x\n", space.device().tag(), space.device().safe_pc(), offset);
+
+ switch(offset)
+ {
+ case 0x02/2:
+ return 0;
+ case 0x10/2:
+ break;
+ case 0x12/2: return m_vdp1.lopr;
+ case 0x14/2: return m_vdp1.copr;
+ /* MODR register, read register for the other VDP1 regs
+ (Shienryu SS version abuses of this during intro) */
+ case 0x16/2:
+ UINT16 modr;
+
+ modr = 0x1000; //vdp1 VER
+ modr |= (STV_VDP1_PTM >> 1) << 8; // PTM1
+ modr |= STV_VDP1_EOS << 7; // EOS
+ modr |= STV_VDP1_DIE << 6; // DIE
+ modr |= STV_VDP1_DIL << 5; // DIL
+ modr |= STV_VDP1_FCM << 4; //FCM
+ modr |= STV_VDP1_VBE << 3; //VBE
+ modr |= STV_VDP1_TVM & 7; //TVM
+
+ return modr;
+ default:
+ if(!space.debugger_access())
+ printf ("cpu %s (PC=%08X) VDP1: Read from Registers, Offset %04x\n", space.device().tag(), space.device().safe_pc(), offset*2);
+ break;
+ }
+
+ return m_vdp1_regs[offset]; //TODO: write-only regs should return open bus or zero
+}
+
+/* TODO: TVM & 1 is just a kludgy work-around, the VDP1 actually needs to be rewritten from scratch. */
+/* Daisenryaku Strong Style (daisenss) uses it */
+void saturn_state::stv_clear_framebuffer( int which_framebuffer )
+{
+ int start_x, end_x, start_y, end_y;
+
+ start_x = STV_VDP1_EWLR_X1 * ((STV_VDP1_TVM & 1) ? 16 : 8);
+ start_y = STV_VDP1_EWLR_Y1 * (m_vdp1.framebuffer_double_interlace+1);
+ end_x = STV_VDP1_EWRR_X3 * ((STV_VDP1_TVM & 1) ? 16 : 8);
+ end_y = (STV_VDP1_EWRR_Y3+1) * (m_vdp1.framebuffer_double_interlace+1);
+// popmessage("%d %d %d %d %d",STV_VDP1_EWLR_X1,STV_VDP1_EWLR_Y1,STV_VDP1_EWRR_X3,STV_VDP1_EWRR_Y3,m_vdp1.framebuffer_double_interlace);
+
+ if(STV_VDP1_TVM & 1)
+ {
+ for(int y=start_y;y<end_y;y++)
+ for(int x=start_x;x<end_x;x++)
+ m_vdp1.framebuffer[ which_framebuffer ][((x&1023)+(y&511)*1024)] = m_vdp1.ewdr;
+ }
+ else
+ {
+ for(int y=start_y;y<end_y;y++)
+ for(int x=start_x;x<end_x;x++)
+ m_vdp1.framebuffer[ which_framebuffer ][((x&511)+(y&511)*512)] = m_vdp1.ewdr;
+ }
+
+ if ( VDP1_LOG ) logerror( "Clearing %d framebuffer\n", m_vdp1.framebuffer_current_draw );
+// memset( m_vdp1.framebuffer[ which_framebuffer ], m_vdp1.ewdr, 1024 * 256 * sizeof(UINT16) * 2 );
+}
+
+
+void saturn_state::stv_prepare_framebuffers( void )
+{
+ int i,rowsize;
+
+ rowsize = m_vdp1.framebuffer_width;
+ if ( m_vdp1.framebuffer_current_draw == 0 )
+ {
+ for ( i = 0; i < m_vdp1.framebuffer_height; i++ )
+ {
+ m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[0][ i * rowsize ];
+ m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[1][ i * rowsize ];
+ }
+ for ( ; i < 512; i++ )
+ {
+ m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[0][0];
+ m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[1][0];
+ }
+ }
+ else
+ {
+ for ( i = 0; i < m_vdp1.framebuffer_height; i++ )
+ {
+ m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[1][ i * rowsize ];
+ m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[0][ i * rowsize ];
+ }
+ for ( ; i < 512; i++ )
+ {
+ m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[1][0];
+ m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[0][0];
+ }
+
+ }
+
+ for ( ; i < 512; i++ )
+ {
+ m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[0][0];
+ m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[1][0];
+ }
+
+}
+
+void saturn_state::stv_vdp1_change_framebuffers( void )
+{
+ m_vdp1.framebuffer_current_display ^= 1;
+ m_vdp1.framebuffer_current_draw ^= 1;
+ if ( VDP1_LOG ) logerror( "Changing framebuffers: %d - draw, %d - display\n", m_vdp1.framebuffer_current_draw, m_vdp1.framebuffer_current_display );
+ stv_prepare_framebuffers();
+}
+
+void saturn_state::stv_set_framebuffer_config( void )
+{
+ if ( m_vdp1.framebuffer_mode == STV_VDP1_TVM &&
+ m_vdp1.framebuffer_double_interlace == STV_VDP1_DIE ) return;
+
+ if ( VDP1_LOG ) logerror( "Setting framebuffer config\n" );
+ m_vdp1.framebuffer_mode = STV_VDP1_TVM;
+ m_vdp1.framebuffer_double_interlace = STV_VDP1_DIE;
+ switch( m_vdp1.framebuffer_mode )
+ {
+ case 0: m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 256; break;
+ case 1: m_vdp1.framebuffer_width = 1024; m_vdp1.framebuffer_height = 256; break;
+ case 2: m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 256; break;
+ case 3: m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 512; break;
+ case 4: m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 256; break;
+ default: logerror( "Invalid framebuffer config %x\n", STV_VDP1_TVM ); m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 256; break;
+ }
+ if ( STV_VDP1_DIE ) m_vdp1.framebuffer_height *= 2; /* double interlace */
+
+ m_vdp1.framebuffer_current_draw = 0;
+ m_vdp1.framebuffer_current_display = 1;
+ stv_prepare_framebuffers();
+}
+
+WRITE16_MEMBER( saturn_state::saturn_vdp1_regs_w )
+{
+ COMBINE_DATA(&m_vdp1_regs[offset]);
+
+ switch(offset)
+ {
+ case 0x00/2:
+ stv_set_framebuffer_config();
+ if ( VDP1_LOG ) logerror( "VDP1: Access to register TVMR = %1X\n", STV_VDP1_TVMR );
+
+ break;
+ case 0x02/2:
+ stv_set_framebuffer_config();
+ if ( VDP1_LOG ) logerror( "VDP1: Access to register FBCR = %1X\n", STV_VDP1_FBCR );
+ m_vdp1.fbcr_accessed = 1;
+ break;
+ case 0x04/2:
+ if ( VDP1_LOG ) logerror( "VDP1: Access to register PTMR = %1X\n", STV_VDP1_PTM );
+ if ( STV_VDP1_PTMR == 1 )
+ stv_vdp1_process_list();
+
+ break;
+ case 0x06/2:
+ if ( VDP1_LOG ) logerror( "VDP1: Erase data set %08X\n", data );
+
+ m_vdp1.ewdr = STV_VDP1_EWDR;
+ break;
+ case 0x08/2:
+ if ( VDP1_LOG ) logerror( "VDP1: Erase upper-left coord set: %08X\n", data );
+ break;
+ case 0x0a/2:
+ if ( VDP1_LOG ) logerror( "VDP1: Erase lower-right coord set: %08X\n", data );
+ break;
+ case 0x0c/2:
+ case 0x0e/2: // After Burner 2 / Out Run / Fantasy Zone writes here with a dword ...
+ if ( VDP1_LOG ) logerror( "VDP1: Draw forced termination register write: %08X %08X\n", offset*2, data );
+ break;
+ default:
+ printf("Warning: write to unknown VDP1 reg %08x %08x\n",offset*2,data);
+ break;
+ }
+
+}
+
+READ32_MEMBER ( saturn_state::saturn_vdp1_vram_r )
+{
+ return m_vdp1_vram[offset];
+}
+
+
+WRITE32_MEMBER ( saturn_state::saturn_vdp1_vram_w )
+{
+ UINT8 *vdp1 = m_vdp1.gfx_decode;
+
+ COMBINE_DATA (&m_vdp1_vram[offset]);
+
+// if (((offset * 4) > 0xdf) && ((offset * 4) < 0x140))
+// {
+// logerror("cpu %s (PC=%08X): VRAM dword write to %08X = %08X & %08X\n", space.device().tag(), space.device().safe_pc(), offset*4, data, mem_mask);
+// }
+
+ data = m_vdp1_vram[offset];
+ /* put in gfx region for easy decoding */
+ vdp1[offset*4+0] = (data & 0xff000000) >> 24;
+ vdp1[offset*4+1] = (data & 0x00ff0000) >> 16;
+ vdp1[offset*4+2] = (data & 0x0000ff00) >> 8;
+ vdp1[offset*4+3] = (data & 0x000000ff) >> 0;
+}
+
+WRITE32_MEMBER ( saturn_state::saturn_vdp1_framebuffer0_w )
+{
+ //popmessage ("STV VDP1 Framebuffer 0 WRITE offset %08x data %08x",offset, data);
+ if ( STV_VDP1_TVM & 1 )
+ {
+ /* 8-bit mode */
+ //printf("VDP1 8-bit mode %08x %02x\n",offset,data);
+ if ( ACCESSING_BITS_24_31 )
+ {
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] &= 0x00ff;
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] |= data & 0xff00;
+ }
+ if ( ACCESSING_BITS_16_23 )
+ {
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] &= 0xff00;
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] |= data & 0x00ff;
+ }
+ if ( ACCESSING_BITS_8_15 )
+ {
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] &= 0x00ff;
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] |= data & 0xff00;
+ }
+ if ( ACCESSING_BITS_0_7 )
+ {
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] &= 0xff00;
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] |= data & 0x00ff;
+ }
+ }
+ else
+ {
+ /* 16-bit mode */
+ if ( ACCESSING_BITS_16_31 )
+ {
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] = (data >> 16) & 0xffff;
+ }
+ if ( ACCESSING_BITS_0_15 )
+ {
+ m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] = data & 0xffff;
+ }
+ }
+}
+
+READ32_MEMBER ( saturn_state::saturn_vdp1_framebuffer0_r )
+{
+ UINT32 result = 0;
+ //popmessage ("STV VDP1 Framebuffer 0 READ offset %08x",offset);
+ if ( STV_VDP1_TVM & 1 )
+ {
+ /* 8-bit mode */
+ //printf("VDP1 8-bit mode %08x\n",offset);
+ if ( ACCESSING_BITS_24_31 )
+ result |= ((m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] & 0xff00) << 16);
+ if ( ACCESSING_BITS_16_23 )
+ result |= ((m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] & 0x00ff) << 16);
+ if ( ACCESSING_BITS_8_15 )
+ result |= ((m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] & 0xff00));
+ if ( ACCESSING_BITS_0_7 )
+ result |= ((m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] & 0x00ff));
+ }
+ else
+ {
+ /* 16-bit mode */
+ if ( ACCESSING_BITS_16_31 )
+ {
+ result |= (m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] << 16);
+ }
+ if ( ACCESSING_BITS_0_15 )
+ {
+ result |= (m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1]);
+ }
+
+ }
+
+ return result;
+}
+
+#ifdef UNUSED_FUNCTION
+WRITE32_MEMBER ( saturn_state::saturn_vdp1_framebuffer1_w )
+{
+ //popmessage ("STV VDP1 Framebuffer 1 WRITE offset %08x data %08x",offset, data);
+}
+
+READ32_MEMBER ( saturn_state::saturn_vdp1_framebuffer1_r )
+{
+ //popmessage ("STV VDP1 Framebuffer 1 READ offset %08x",offset);
+ return 0xffff;
+}
+#endif
+
+
+/*
+
+there is a command every 0x20 bytes
+the first word is the control word
+the rest are data used by it
+
+---
+00 CMDCTRL
+ e--- ---- ---- ---- | end bit (15)
+ -jjj ---- ---- ---- | jump select bits (12-14)
+ ---- zzzz ---- ---- | zoom point / hotspot (8-11)
+ ---- ---- 00-- ---- | UNUSED
+ ---- ---- --dd ---- | character read direction (4,5)
+ ---- ---- ---- cccc | command bits (0-3)
+
+02 CMDLINK
+ llll llll llll ll-- | link
+ ---- ---- ---- --00 | UNUSED
+
+04 CMDPMOD
+ m--- ---- ---- ---- | MON (looks at MSB and apply shadows etc.)
+ -00- ---- ---- ---- | UNUSED
+ ---h ---- ---- ---- | HSS (High Speed Shrink)
+ ---- p--- ---- ---- | PCLIP (Pre Clipping Disable)
+ ---- -c-- ---- ---- | CLIP (Clipping Mode Bit)
+ ---- --m- ---- ---- | CMOD (User Clipping Enable Bit)
+ ---- ---M ---- ---- | MESH (Mesh Enable Bit)
+ ---- ---- e--- ---- | ECD (End Code Disable)
+ ---- ---- -S-- ---- | SPD (Transparent Pixel Disable)
+ ---- ---- --cc c--- | Colour Mode
+ ---- ---- ---- -CCC | Colour Calculation bits
+
+06 CMDCOLR
+ mmmm mmmm mmmm mmmm | Colour Bank, Colour Lookup /8
+
+08 CMDSRCA (Character Address)
+ aaaa aaaa aaaa aa-- | Character Address
+ ---- ---- ---- --00 | UNUSED
+
+0a CMDSIZE (Character Size)
+ 00-- ---- ---- ---- | UNUSED
+ --xx xxxx ---- ---- | Character Size (X)
+ ---- ---- yyyy yyyy | Character Size (Y)
+
+0c CMDXA (used for normal sprite)
+ eeee ee-- ---- ---- | extension bits
+ ---- --xx xxxx xxxx | x position
+
+0e CMDYA (used for normal sprite)
+ eeee ee-- ---- ---- | extension bits
+ ---- --yy yyyy yyyy | y position
+
+10 CMDXB
+12 CMDYB
+14 CMDXC
+16 CMDYC
+18 CMDXD
+1a CMDYD
+1c CMDGRDA (Gouraud Shading Table)
+1e UNUSED
+---
+
+
+*/
+
+void saturn_state::stv_clear_gouraud_shading(void)
+{
+ memset( &stv_gouraud_shading, 0, sizeof( stv_gouraud_shading ) );
+}
+
+UINT8 saturn_state::stv_read_gouraud_table( void )
+{
+ int gaddr;
+
+ if ( (stv2_current_sprite.CMDPMOD & 0x7) == 4 )
+ {
+ gaddr = stv2_current_sprite.CMDGRDA * 8;
+ stv_gouraud_shading.GA = (m_vdp1_vram[gaddr/4] >> 16) & 0xffff;
+ stv_gouraud_shading.GB = (m_vdp1_vram[gaddr/4] >> 0) & 0xffff;
+ stv_gouraud_shading.GC = (m_vdp1_vram[gaddr/4 + 1] >> 16) & 0xffff;
+ stv_gouraud_shading.GD = (m_vdp1_vram[gaddr/4 + 1] >> 0) & 0xffff;
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+INLINE INT32 _shading( INT32 color, INT32 correction )
+{
+ correction = (correction >> 16) & 0x1f;
+ color += (correction - 16);
+
+ if ( color < 0 ) color = 0;
+ if ( color > 0x1f ) color = 0x1f;
+
+ return color;
+}
+
+UINT16 saturn_state::stv_vdp1_apply_gouraud_shading( int x, int y, UINT16 pix )
+{
+ INT32 r,g,b, msb;
+
+ msb = pix & 0x8000;
+
+#ifdef MAME_DEBUG
+ if ( (stv_vdp1_shading_data->scanline[y].x[0] >> 16) != x )
+ {
+ logerror( "ERROR in computing x coordinates (line %d, x = %x, %d, xc = %x, %d)\n", y, x, x, stv_vdp1_shading_data->scanline[y].x[0], stv_vdp1_shading_data->scanline[y].x[0] >> 16 );
+ };
+#endif
+
+ b = RGB_B(pix);
+ g = RGB_G(pix);
+ r = RGB_R(pix);
+
+ b = _shading( b, stv_vdp1_shading_data->scanline[y].b[0] );
+ g = _shading( g, stv_vdp1_shading_data->scanline[y].g[0] );
+ r = _shading( r, stv_vdp1_shading_data->scanline[y].r[0] );
+
+ stv_vdp1_shading_data->scanline[y].b[0] += stv_vdp1_shading_data->scanline[y].db;
+ stv_vdp1_shading_data->scanline[y].g[0] += stv_vdp1_shading_data->scanline[y].dg;
+ stv_vdp1_shading_data->scanline[y].r[0] += stv_vdp1_shading_data->scanline[y].dr;
+
+ stv_vdp1_shading_data->scanline[y].x[0] += 1 << FRAC_SHIFT;
+
+ return msb | b << 10 | g << 5 | r;
+}
+
+void saturn_state::stv_vdp1_setup_shading_for_line(INT32 y, INT32 x1, INT32 x2,
+ INT32 r1, INT32 g1, INT32 b1,
+ INT32 r2, INT32 g2, INT32 b2)
+{
+ int xx1 = x1>>FRAC_SHIFT;
+ int xx2 = x2>>FRAC_SHIFT;
+
+
+ if ( xx1 > xx2 )
+ {
+ SWAP_INT32(xx1, xx2);
+ SWAP_INT32(r1, r2);
+ SWAP_INT32(g1, g2);
+ SWAP_INT32(b1, b2);
+ }
+
+ if ( (y >= 0) && (y < 512) )
+ {
+ INT32 dx;
+ INT32 gbd, ggd, grd;
+
+ dx = xx2 - xx1;
+
+ if ( dx == 0 )
+ {
+ gbd = ggd = grd = 0;
+ }
+ else
+ {
+ gbd = abs(b2 - b1) / dx;
+ if (b2 < b1) gbd = -gbd;
+ ggd = abs(g2 - g1) / dx;
+ if (g2 < g1) ggd = -ggd;
+ grd = abs(r2 - r1) / dx;
+ if (r2 < r1) grd = -grd;
+ }
+
+ stv_vdp1_shading_data->scanline[y].x[0] = x1;
+ stv_vdp1_shading_data->scanline[y].x[1] = x2;
+
+ stv_vdp1_shading_data->scanline[y].b[0] = b1;
+ stv_vdp1_shading_data->scanline[y].g[0] = g1;
+ stv_vdp1_shading_data->scanline[y].r[0] = r1;
+ stv_vdp1_shading_data->scanline[y].b[1] = b2;
+ stv_vdp1_shading_data->scanline[y].g[1] = g2;
+ stv_vdp1_shading_data->scanline[y].r[1] = r2;
+
+ stv_vdp1_shading_data->scanline[y].db = gbd;
+ stv_vdp1_shading_data->scanline[y].dg = ggd;
+ stv_vdp1_shading_data->scanline[y].dr = grd;
+
+ }
+}
+
+void saturn_state::stv_vdp1_setup_shading_for_slope(
+ INT32 x1, INT32 x2, INT32 sl1, INT32 sl2, INT32 *nx1, INT32 *nx2,
+ INT32 r1, INT32 r2, INT32 slr1, INT32 slr2, INT32 *nr1, INT32 *nr2,
+ INT32 g1, INT32 g2, INT32 slg1, INT32 slg2, INT32 *ng1, INT32 *ng2,
+ INT32 b1, INT32 b2, INT32 slb1, INT32 slb2, INT32 *nb1, INT32 *nb2,
+ INT32 _y1, INT32 y2)
+{
+ if(x1 > x2 || (x1==x2 && sl1 > sl2)) {
+ SWAP_INT32(x1,x2);
+ SWAP_INT32(sl1,sl2);
+ SWAP_INT32PTR(nx1, nx2);
+ SWAP_INT32(r1,r2);
+ SWAP_INT32(slr1, slr2);
+ SWAP_INT32PTR(nr1, nr2);
+ SWAP_INT32(g1, g2);
+ SWAP_INT32(slg1, slg2);
+ SWAP_INT32PTR(ng1, ng2);
+ SWAP_INT32(b1, b2);
+ SWAP_INT32(slb1, slb2);
+ SWAP_INT32PTR(nb1, nb2);
+ }
+
+ while(_y1 < y2)
+ {
+ stv_vdp1_setup_shading_for_line(_y1, x1, x2, r1, g1, b1, r2, g2, b2);
+ x1 += sl1;
+ r1 += slr1;
+ g1 += slg1;
+ b1 += slb1;
+
+ x2 += sl2;
+ r2 += slr2;
+ g2 += slg2;
+ b2 += slb2;
+ _y1++;
+ }
+ *nx1 = x1;
+ *nr1 = r1;
+ *ng1 = g1;
+ *nb1 = b1;
+
+ *nx2 = x2;
+ *nr2 = r2;
+ *nb2 = b2;
+ *ng2 = g2;
+}
+
+void saturn_state::stv_vdp1_setup_shading(const struct spoint* q, const rectangle &cliprect)
+{
+ INT32 x1, x2, delta, cury, limy;
+ INT32 r1, g1, b1, r2, g2, b2;
+ INT32 sl1, slg1, slb1, slr1;
+ INT32 sl2, slg2, slb2, slr2;
+ int pmin, pmax, i, ps1, ps2;
+ struct shaded_point p[8];
+ UINT16 gd[4];
+
+ if ( stv_read_gouraud_table() == 0 ) return;
+
+ gd[0] = stv_gouraud_shading.GA;
+ gd[1] = stv_gouraud_shading.GB;
+ gd[2] = stv_gouraud_shading.GC;
+ gd[3] = stv_gouraud_shading.GD;
+
+ for(i=0; i<4; i++) {
+ p[i].x = p[i+4].x = q[i].x << FRAC_SHIFT;
+ p[i].y = p[i+4].y = q[i].y;
+ p[i].r = p[i+4].r = RGB_R(gd[i]) << FRAC_SHIFT;
+ p[i].g = p[i+4].g = RGB_G(gd[i]) << FRAC_SHIFT;
+ p[i].b = p[i+4].b = RGB_B(gd[i]) << FRAC_SHIFT;
+ }
+
+ pmin = pmax = 0;
+ for(i=1; i<4; i++) {
+ if(p[i].y < p[pmin].y)
+ pmin = i;
+ if(p[i].y > p[pmax].y)
+ pmax = i;
+ }
+
+ cury = p[pmin].y;
+ limy = p[pmax].y;
+
+ stv_vdp1_shading_data->sy = cury;
+ stv_vdp1_shading_data->ey = limy;
+
+ if(cury == limy) {
+ x1 = x2 = p[0].x;
+ ps1 = ps2 = 0;
+ for(i=1; i<4; i++) {
+ if(p[i].x < x1) {
+ x1 = p[i].x;
+ ps1 = i;
+ }
+ if(p[i].x > x2) {
+ x2 = p[i].x;
+ ps2 = i;
+ }
+ }
+ stv_vdp1_setup_shading_for_line(cury, x1, x2, p[ps1].r, p[ps1].g, p[ps1].b, p[ps2].r, p[ps2].g, p[ps2].b);
+ goto finish;
+ }
+
+ ps1 = pmin+4;
+ ps2 = pmin;
+
+ goto startup;
+
+ for(;;) {
+ if(p[ps1-1].y == p[ps2+1].y) {
+ stv_vdp1_setup_shading_for_slope(
+ x1, x2, sl1, sl2, &x1, &x2,
+ r1, r2, slr1, slr2, &r1, &r2,
+ g1, g2, slg1, slg2, &g1, &g2,
+ b1, b2, slb1, slb2, &b1, &b2,
+ cury, p[ps1-1].y);
+ cury = p[ps1-1].y;
+ if(cury >= limy)
+ break;
+ ps1--;
+ ps2++;
+
+ startup:
+ while(p[ps1-1].y == cury)
+ ps1--;
+ while(p[ps2+1].y == cury)
+ ps2++;
+ x1 = p[ps1].x;
+ r1 = p[ps1].r;
+ g1 = p[ps1].g;
+ b1 = p[ps1].b;
+ x2 = p[ps2].x;
+ r2 = p[ps2].r;
+ g2 = p[ps2].g;
+ b2 = p[ps2].b;
+
+ delta = cury-p[ps1-1].y;
+ sl1 = (x1-p[ps1-1].x)/delta;
+ slr1 = (r1-p[ps1-1].r)/delta;
+ slg1 = (g1-p[ps1-1].g)/delta;
+ slb1 = (b1-p[ps1-1].b)/delta;
+
+ delta = cury-p[ps2+1].y;
+ sl2 = (x2-p[ps2+1].x)/delta;
+ slr2 = (r2-p[ps2+1].r)/delta;
+ slg2 = (g2-p[ps2+1].g)/delta;
+ slb2 = (b2-p[ps2+1].b)/delta;
+ } else if(p[ps1-1].y < p[ps2+1].y) {
+ stv_vdp1_setup_shading_for_slope(
+ x1, x2, sl1, sl2, &x1, &x2,
+ r1, r2, slr1, slr2, &r1, &r2,
+ g1, g2, slg1, slg2, &g1, &g2,
+ b1, b2, slb1, slb2, &b1, &b2,
+ cury, p[ps1-1].y);
+ cury = p[ps1-1].y;
+ if(cury >= limy)
+ break;
+ ps1--;
+ while(p[ps1-1].y == cury)
+ ps1--;
+ x1 = p[ps1].x;
+ r1 = p[ps1].r;
+ g1 = p[ps1].g;
+ b1 = p[ps1].b;
+
+ delta = cury-p[ps1-1].y;
+ sl1 = (x1-p[ps1-1].x)/delta;
+ slr1 = (r1-p[ps1-1].r)/delta;
+ slg1 = (g1-p[ps1-1].g)/delta;
+ slb1 = (b1-p[ps1-1].b)/delta;
+ } else {
+ stv_vdp1_setup_shading_for_slope(
+ x1, x2, sl1, sl2, &x1, &x2,
+ r1, r2, slr1, slr2, &r1, &r2,
+ g1, g2, slg1, slg2, &g1, &g2,
+ b1, b2, slb1, slb2, &b1, &b2,
+ cury, p[ps2+1].y);
+ cury = p[ps2+1].y;
+ if(cury >= limy)
+ break;
+ ps2++;
+ while(p[ps2+1].y == cury)
+ ps2++;
+ x2 = p[ps2].x;
+ r2 = p[ps2].r;
+ g2 = p[ps2].g;
+ b2 = p[ps2].b;
+
+ delta = cury-p[ps2+1].y;
+ sl2 = (x2-p[ps2+1].x)/delta;
+ slr2 = (r2-p[ps2+1].r)/delta;
+ slg2 = (g2-p[ps2+1].g)/delta;
+ slb2 = (b2-p[ps2+1].b)/delta;
+ }
+ }
+ if(cury == limy)
+ stv_vdp1_setup_shading_for_line(cury, x1, x2, r1, g1, b1, r2, g2, b2 );
+
+finish:
+
+ if ( stv_vdp1_shading_data->sy < 0 ) stv_vdp1_shading_data->sy = 0;
+ if ( stv_vdp1_shading_data->sy >= 512 ) return;
+ if ( stv_vdp1_shading_data->ey < 0 ) return;
+ if ( stv_vdp1_shading_data->ey >= 512 ) stv_vdp1_shading_data->ey = 511;
+
+ for ( cury = stv_vdp1_shading_data->sy; cury <= stv_vdp1_shading_data->ey; cury++ )
+ {
+ while( (stv_vdp1_shading_data->scanline[cury].x[0] >> 16) < cliprect.min_x )
+ {
+ stv_vdp1_shading_data->scanline[cury].x[0] += (1 << FRAC_SHIFT);
+ stv_vdp1_shading_data->scanline[cury].b[0] += stv_vdp1_shading_data->scanline[cury].db;
+ stv_vdp1_shading_data->scanline[cury].g[0] += stv_vdp1_shading_data->scanline[cury].dg;
+ stv_vdp1_shading_data->scanline[cury].r[0] += stv_vdp1_shading_data->scanline[cury].dr;
+ }
+ }
+
+}
+
+/* note that if we're drawing
+to the framebuffer we CAN'T frameskip the vdp1 drawing as the hardware can READ the framebuffer
+and if we skip the drawing the content could be incorrect when it reads it, although i have no idea
+why they would want to */
+
+
+
+void saturn_state::drawpixel_poly(int x, int y, int patterndata, int offsetcnt)
+{
+ /* Capcom Collection Dai 4 uses a dummy polygon to clear VDP1 framebuffer that goes over our current max size ... */
+ if(x >= 1024 || y >= 512)
+ return;
+
+ m_vdp1.framebuffer_draw_lines[y][x] = stv2_current_sprite.CMDCOLR;
+}
+
+void saturn_state::drawpixel_8bpp_trans(int x, int y, int patterndata, int offsetcnt)
+{
+ UINT16 pix;
+
+ pix = m_vdp1.gfx_decode[patterndata+offsetcnt];
+ if ( pix & 0xff )
+ {
+ m_vdp1.framebuffer_draw_lines[y][x] = pix | m_sprite_colorbank;
+ }
+}
+
+void saturn_state::drawpixel_4bpp_notrans(int x, int y, int patterndata, int offsetcnt)
+{
+ UINT16 pix;
+
+ pix = m_vdp1.gfx_decode[patterndata+offsetcnt/2];
+ pix = offsetcnt&1 ? (pix & 0x0f) : ((pix & 0xf0)>>4);
+ m_vdp1.framebuffer_draw_lines[y][x] = pix | m_sprite_colorbank;
+}
+
+void saturn_state::drawpixel_4bpp_trans(int x, int y, int patterndata, int offsetcnt)
+{
+ UINT16 pix;
+
+ pix = m_vdp1.gfx_decode[patterndata+offsetcnt/2];
+ pix = offsetcnt&1 ? (pix & 0x0f) : ((pix & 0xf0)>>4);
+ if ( pix )
+ m_vdp1.framebuffer_draw_lines[y][x] = pix | m_sprite_colorbank;
+}
+
+void saturn_state::drawpixel_generic(int x, int y, int patterndata, int offsetcnt)
+{
+ int pix,mode,transmask, spd = stv2_current_sprite.CMDPMOD & 0x40;
+ int mesh = stv2_current_sprite.CMDPMOD & 0x100;
+ int pix2;
+
+ if ( mesh && !((x ^ y) & 1) )
+ {
+ return;
+ }
+
+ if ( stv2_current_sprite.ispoly )
+ {
+ pix = stv2_current_sprite.CMDCOLR&0xffff;
+
+ transmask = 0xffff;
+ if ( pix & 0x8000 )
+ {
+ mode = 5;
+ }
+ else
+ {
+ mode = 1;
+ }
+ }
+ else
+ {
+ switch (stv2_current_sprite.CMDPMOD&0x0038)
+ {
+ case 0x0000: // mode 0 16 colour bank mode (4bits) (hanagumi blocks)
+ // most of the shienryu sprites use this mode
+ pix = m_vdp1.gfx_decode[(patterndata+offsetcnt/2) & 0xfffff];
+ pix = offsetcnt&1 ? (pix & 0x0f) : ((pix & 0xf0)>>4);
+ pix = pix+((stv2_current_sprite.CMDCOLR&0xfff0));
+ mode = 0;
+ transmask = 0xf;
+ break;
+ case 0x0008: // mode 1 16 colour lookup table mode (4bits)
+ // shienryu explosisons (and some enemies) use this mode
+ pix2 = m_vdp1.gfx_decode[(patterndata+offsetcnt/2) & 0xfffff];
+ pix2 = offsetcnt&1 ? (pix2 & 0x0f) : ((pix2 & 0xf0)>>4);
+ pix = pix2&1 ?
+ ((((m_vdp1_vram[(((stv2_current_sprite.CMDCOLR&0xffff)*8)>>2)+((pix2&0xfffe)/2)])) & 0x0000ffff) >> 0):
+ ((((m_vdp1_vram[(((stv2_current_sprite.CMDCOLR&0xffff)*8)>>2)+((pix2&0xfffe)/2)])) & 0xffff0000) >> 16);
+
+ mode = 5;
+ transmask = 0xffff;
+
+ if ( !spd )
+ {
+ if ( (pix2 & 0xf) == 0 )
+ {
+ return;
+ }
+ else
+ {
+ spd = 1;
+ }
+ }
+ break;
+ case 0x0010: // mode 2 64 colour bank mode (8bits) (character select portraits on hanagumi)
+ pix = m_vdp1.gfx_decode[(patterndata+offsetcnt) & 0xfffff];
+ mode = 2;
+ pix = pix+(stv2_current_sprite.CMDCOLR&0xffc0);
+ transmask = 0x3f;
+ break;
+ case 0x0018: // mode 3 128 colour bank mode (8bits) (little characters on hanagumi use this mode)
+ pix = m_vdp1.gfx_decode[(patterndata+offsetcnt) & 0xfffff];
+ pix = pix+(stv2_current_sprite.CMDCOLR&0xff80);
+ transmask = 0x7f;
+ mode = 3;
+ break;
+ case 0x0020: // mode 4 256 colour bank mode (8bits) (hanagumi title)
+ pix = m_vdp1.gfx_decode[(patterndata+offsetcnt) & 0xfffff];
+ pix = pix+(stv2_current_sprite.CMDCOLR&0xff00);
+ transmask = 0xff;
+ mode = 4;
+ break;
+ case 0x0028: // mode 5 32,768 colour RGB mode (16bits)
+ pix = m_vdp1.gfx_decode[(patterndata+offsetcnt*2+1) & 0xfffff] | (m_vdp1.gfx_decode[(patterndata+offsetcnt*2) & 0xfffff]<<8) ;
+ mode = 5;
+ transmask = -1; /* TODO: check me */
+ break;
+ default: // other settings illegal
+ pix = machine().rand();
+ mode = 0;
+ transmask = 0xff;
+ popmessage("Illegal Sprite Mode, contact MAMEdev");
+ }
+
+
+ // preliminary end code disable support
+ if ( ((stv2_current_sprite.CMDPMOD & 0x80) == 0) &&
+ ((pix & transmask) == transmask) )
+ {
+ return;
+ }
+ }
+
+ /* MSBON */
+ pix |= stv2_current_sprite.CMDPMOD & 0x8000;
+ if ( mode != 5 )
+ {
+ if ( (pix & transmask) || spd )
+ {
+ m_vdp1.framebuffer_draw_lines[y][x] = pix;
+ }
+ }
+ else
+ {
+ if ( (pix & transmask) || spd )
+ {
+ switch( stv2_current_sprite.CMDPMOD & 0x7 )
+ {
+ case 0: /* replace */
+ m_vdp1.framebuffer_draw_lines[y][x] = pix;
+ break;
+ case 1: /* shadow */
+ if ( m_vdp1.framebuffer_draw_lines[y][x] & 0x8000 )
+ {
+ m_vdp1.framebuffer_draw_lines[y][x] = ((m_vdp1.framebuffer_draw_lines[y][x] & ~0x8421) >> 1) | 0x8000;
+ }
+ break;
+ case 2: /* half luminance */
+ m_vdp1.framebuffer_draw_lines[y][x] = ((pix & ~0x8421) >> 1) | 0x8000;
+ break;
+ case 3: /* half transparent */
+ if ( m_vdp1.framebuffer_draw_lines[y][x] & 0x8000 )
+ {
+ m_vdp1.framebuffer_draw_lines[y][x] = alpha_blend_r16( m_vdp1.framebuffer_draw_lines[y][x], pix, 0x80 ) | 0x8000;
+ }
+ else
+ {
+ m_vdp1.framebuffer_draw_lines[y][x] = pix;
+ }
+ break;
+ case 4: /* Gouraud shading */
+ m_vdp1.framebuffer_draw_lines[y][x] = stv_vdp1_apply_gouraud_shading( x, y, pix );
+ break;
+ default:
+ m_vdp1.framebuffer_draw_lines[y][x] = pix;
+ break;
+ }
+ }
+ }
+}
+
+
+void saturn_state::stv_vdp1_set_drawpixel( void )
+{
+ int sprite_type = stv2_current_sprite.CMDCTRL & 0x000f;
+ int sprite_mode = stv2_current_sprite.CMDPMOD&0x0038;
+ int spd = stv2_current_sprite.CMDPMOD & 0x40;
+ int mesh = stv2_current_sprite.CMDPMOD & 0x100;
+ int ecd = stv2_current_sprite.CMDPMOD & 0x80;
+
+ if ( mesh || !ecd || ((stv2_current_sprite.CMDPMOD & 0x7) != 0) )
+ {
+ drawpixel = &saturn_state::drawpixel_generic;
+ return;
+ }
+
+ if (sprite_type == 4 && ((stv2_current_sprite.CMDPMOD & 0x7) == 0))
+ {
+ drawpixel = &saturn_state::drawpixel_poly;
+ }
+ else if ( (sprite_mode == 0x20) && !spd )
+ {
+ m_sprite_colorbank = (stv2_current_sprite.CMDCOLR&0xff00);
+ drawpixel = &saturn_state::drawpixel_8bpp_trans;
+ }
+ else if ((sprite_mode == 0x00) && spd)
+ {
+ m_sprite_colorbank = (stv2_current_sprite.CMDCOLR&0xfff0);
+ drawpixel = &saturn_state::drawpixel_4bpp_notrans;
+ }
+ else if (sprite_mode == 0x00 && !spd )
+ {
+ m_sprite_colorbank = (stv2_current_sprite.CMDCOLR&0xfff0);
+ drawpixel = &saturn_state::drawpixel_4bpp_trans;
+ }
+ else
+ {
+ drawpixel = &saturn_state::drawpixel_generic;
+ }
+}
+
+
+void saturn_state::vdp1_fill_slope(const rectangle &cliprect, int patterndata, int xsize,
+ INT32 x1, INT32 x2, INT32 sl1, INT32 sl2, INT32 *nx1, INT32 *nx2,
+ INT32 u1, INT32 u2, INT32 slu1, INT32 slu2, INT32 *nu1, INT32 *nu2,
+ INT32 v1, INT32 v2, INT32 slv1, INT32 slv2, INT32 *nv1, INT32 *nv2,
+ INT32 _y1, INT32 y2)
+{
+ if(_y1 > cliprect.max_y)
+ return;
+
+ if(y2 <= cliprect.min_y) {
+ int delta = y2-_y1;
+ *nx1 = x1+delta*sl1;
+ *nu1 = u1+delta*slu1;
+ *nv1 = v1+delta*slv1;
+ *nx2 = x2+delta*sl2;
+ *nu2 = u2+delta*slu2;
+ *nv2 = v2+delta*slv2;
+ return;
+ }
+
+ if(y2 > cliprect.max_y)
+ y2 = cliprect.max_y+1;
+
+ if(_y1 < cliprect.min_y) {
+ int delta = cliprect.min_y - _y1;
+ x1 += delta*sl1;
+ u1 += delta*slu1;
+ v1 += delta*slv1;
+ x2 += delta*sl2;
+ u2 += delta*slu2;
+ v2 += delta*slv2;
+ _y1 = cliprect.min_y;
+ }
+
+ if(x1 > x2 || (x1==x2 && sl1 > sl2)) {
+ INT32 t, *tp;
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ t = sl1;
+ sl1 = sl2;
+ sl2 = t;
+ tp = nx1;
+ nx1 = nx2;
+ nx2 = tp;
+
+ t = u1;
+ u1 = u2;
+ u2 = t;
+ t = slu1;
+ slu1 = slu2;
+ slu2 = t;
+ tp = nu1;
+ nu1 = nu2;
+ nu2 = tp;
+
+ t = v1;
+ v1 = v2;
+ v2 = t;
+ t = slv1;
+ slv1 = slv2;
+ slv2 = t;
+ tp = nv1;
+ nv1 = nv2;
+ nv2 = tp;
+ }
+
+ while(_y1 < y2) {
+ if(_y1 >= cliprect.min_y) {
+ INT32 slux = 0, slvx = 0;
+ int xx1 = x1>>FRAC_SHIFT;
+ int xx2 = x2>>FRAC_SHIFT;
+ INT32 u = u1;
+ INT32 v = v1;
+ if(xx1 != xx2) {
+ int delta = xx2-xx1;
+ slux = (u2-u1)/delta;
+ slvx = (v2-v1)/delta;
+ }
+ if(xx1 <= cliprect.max_x || xx2 >= cliprect.min_x) {
+ if(xx1 < cliprect.min_x) {
+ int delta = cliprect.min_x-xx1;
+ u += slux*delta;
+ v += slvx*delta;
+ xx1 = cliprect.min_x;
+ }
+ if(xx2 > cliprect.max_x)
+ xx2 = cliprect.max_x;
+
+ while(xx1 <= xx2) {
+ (this->*drawpixel)(xx1,_y1, patterndata, (v>>FRAC_SHIFT)*xsize+(u>>FRAC_SHIFT));
+ xx1++;
+ u += slux;
+ v += slvx;
+ }
+ }
+ }
+
+ x1 += sl1;
+ u1 += slu1;
+ v1 += slv1;
+ x2 += sl2;
+ u2 += slu2;
+ v2 += slv2;
+ _y1++;
+ }
+ *nx1 = x1;
+ *nu1 = u1;
+ *nv1 = v1;
+ *nx2 = x2;
+ *nu2 = u2;
+ *nv2 = v2;
+}
+
+void saturn_state::vdp1_fill_line(const rectangle &cliprect, int patterndata, int xsize, INT32 y,
+ INT32 x1, INT32 x2, INT32 u1, INT32 u2, INT32 v1, INT32 v2)
+{
+ int xx1 = x1>>FRAC_SHIFT;
+ int xx2 = x2>>FRAC_SHIFT;
+
+ if(y > cliprect.max_y || y < cliprect.min_y)
+ return;
+
+ if(xx1 <= cliprect.max_x || xx2 >= cliprect.min_x) {
+ INT32 slux = 0, slvx = 0;
+ INT32 u = u1;
+ INT32 v = v1;
+ if(xx1 != xx2) {
+ int delta = xx2-xx1;
+ slux = (u2-u1)/delta;
+ slvx = (v2-v1)/delta;
+ }
+ if(xx1 < cliprect.min_x) {
+ int delta = cliprect.min_x-xx1;
+ u += slux*delta;
+ v += slvx*delta;
+ xx1 = cliprect.min_x;
+ }
+ if(xx2 > cliprect.max_x)
+ xx2 = cliprect.max_x;
+
+ while(xx1 <= xx2) {
+ (this->*drawpixel)(xx1,y,patterndata,(v>>FRAC_SHIFT)*xsize+(u>>FRAC_SHIFT));
+ xx1++;
+ u += slux;
+ v += slvx;
+ }
+ }
+}
+
+void saturn_state::vdp1_fill_quad(const rectangle &cliprect, int patterndata, int xsize, const struct spoint *q)
+{
+ INT32 sl1, sl2, slu1, slu2, slv1, slv2, cury, limy, x1, x2, u1, u2, v1, v2, delta;
+ int pmin, pmax, i, ps1, ps2;
+ struct spoint p[8];
+
+ for(i=0; i<4; i++) {
+ p[i].x = p[i+4].x = q[i].x << FRAC_SHIFT;
+ p[i].y = p[i+4].y = q[i].y;
+ p[i].u = p[i+4].u = q[i].u << FRAC_SHIFT;
+ p[i].v = p[i+4].v = q[i].v << FRAC_SHIFT;
+ }
+
+ pmin = pmax = 0;
+ for(i=1; i<4; i++) {
+ if(p[i].y < p[pmin].y)
+ pmin = i;
+ if(p[i].y > p[pmax].y)
+ pmax = i;
+ }
+
+ cury = p[pmin].y;
+ limy = p[pmax].y;
+
+ if(cury == limy) {
+ x1 = x2 = p[0].x;
+ u1 = u2 = p[0].u;
+ v1 = v2 = p[0].v;
+ for(i=1; i<4; i++) {
+ if(p[i].x < x1) {
+ x1 = p[i].x;
+ u1 = p[i].u;
+ v1 = p[i].v;
+ }
+ if(p[i].x > x2) {
+ x2 = p[i].x;
+ u2 = p[i].u;
+ v2 = p[i].v;
+ }
+ }
+ vdp1_fill_line(cliprect, patterndata, xsize, cury, x1, x2, u1, u2, v1, v2);
+ return;
+ }
+
+ if(cury > cliprect.max_y)
+ return;
+ if(limy <= cliprect.min_y)
+ return;
+
+ if(limy > cliprect.max_y)
+ limy = cliprect.max_y;
+
+ ps1 = pmin+4;
+ ps2 = pmin;
+
+ goto startup;
+
+ for(;;) {
+ if(p[ps1-1].y == p[ps2+1].y) {
+ vdp1_fill_slope(cliprect, patterndata, xsize,
+ x1, x2, sl1, sl2, &x1, &x2,
+ u1, u2, slu1, slu2, &u1, &u2,
+ v1, v2, slv1, slv2, &v1, &v2,
+ cury, p[ps1-1].y);
+ cury = p[ps1-1].y;
+ if(cury >= limy)
+ break;
+ ps1--;
+ ps2++;
+
+ startup:
+ while(p[ps1-1].y == cury)
+ ps1--;
+ while(p[ps2+1].y == cury)
+ ps2++;
+ x1 = p[ps1].x;
+ u1 = p[ps1].u;
+ v1 = p[ps1].v;
+ x2 = p[ps2].x;
+ u2 = p[ps2].u;
+ v2 = p[ps2].v;
+
+ delta = cury-p[ps1-1].y;
+ sl1 = (x1-p[ps1-1].x)/delta;
+ slu1 = (u1-p[ps1-1].u)/delta;
+ slv1 = (v1-p[ps1-1].v)/delta;
+
+ delta = cury-p[ps2+1].y;
+ sl2 = (x2-p[ps2+1].x)/delta;
+ slu2 = (u2-p[ps2+1].u)/delta;
+ slv2 = (v2-p[ps2+1].v)/delta;
+ } else if(p[ps1-1].y < p[ps2+1].y) {
+ vdp1_fill_slope(cliprect, patterndata, xsize,
+ x1, x2, sl1, sl2, &x1, &x2,
+ u1, u2, slu1, slu2, &u1, &u2,
+ v1, v2, slv1, slv2, &v1, &v2,
+ cury, p[ps1-1].y);
+ cury = p[ps1-1].y;
+ if(cury >= limy)
+ break;
+ ps1--;
+ while(p[ps1-1].y == cury)
+ ps1--;
+ x1 = p[ps1].x;
+ u1 = p[ps1].u;
+ v1 = p[ps1].v;
+
+ delta = cury-p[ps1-1].y;
+ sl1 = (x1-p[ps1-1].x)/delta;
+ slu1 = (u1-p[ps1-1].u)/delta;
+ slv1 = (v1-p[ps1-1].v)/delta;
+ } else {
+ vdp1_fill_slope(cliprect, patterndata, xsize,
+ x1, x2, sl1, sl2, &x1, &x2,
+ u1, u2, slu1, slu2, &u1, &u2,
+ v1, v2, slv1, slv2, &v1, &v2,
+ cury, p[ps2+1].y);
+ cury = p[ps2+1].y;
+ if(cury >= limy)
+ break;
+ ps2++;
+ while(p[ps2+1].y == cury)
+ ps2++;
+ x2 = p[ps2].x;
+ u2 = p[ps2].u;
+ v2 = p[ps2].v;
+
+ delta = cury-p[ps2+1].y;
+ sl2 = (x2-p[ps2+1].x)/delta;
+ slu2 = (u2-p[ps2+1].u)/delta;
+ slv2 = (v2-p[ps2+1].v)/delta;
+ }
+ }
+ if(cury == limy)
+ vdp1_fill_line(cliprect, patterndata, xsize, cury, x1, x2, u1, u2, v1, v2);
+}
+
+int saturn_state::x2s(int v)
+{
+ return (INT32)(INT16)v + m_vdp1.local_x;
+}
+
+int saturn_state::y2s(int v)
+{
+ return (INT32)(INT16)v + m_vdp1.local_y;
+}
+
+void saturn_state::stv_vdp1_draw_line(const rectangle &cliprect)
+{
+ struct spoint q[4];
+
+ q[0].x = x2s(stv2_current_sprite.CMDXA);
+ q[0].y = y2s(stv2_current_sprite.CMDYA);
+ q[1].x = x2s(stv2_current_sprite.CMDXB);
+ q[1].y = y2s(stv2_current_sprite.CMDYB);
+ q[2].x = x2s(stv2_current_sprite.CMDXA);
+ q[2].y = y2s(stv2_current_sprite.CMDYA);
+ q[3].x = x2s(stv2_current_sprite.CMDXB);
+ q[3].y = y2s(stv2_current_sprite.CMDYB);
+
+ q[0].u = q[3].u = q[1].u = q[2].u = 0;
+ q[0].v = q[1].v = q[2].v = q[3].v = 0;
+
+ vdp1_fill_quad(cliprect, 0, 1, q);
+}
+
+void saturn_state::stv_vdp1_draw_poly_line(const rectangle &cliprect)
+{
+ struct spoint q[4];
+
+ q[0].x = x2s(stv2_current_sprite.CMDXA);
+ q[0].y = y2s(stv2_current_sprite.CMDYA);
+ q[1].x = x2s(stv2_current_sprite.CMDXB);
+ q[1].y = y2s(stv2_current_sprite.CMDYB);
+ q[2].x = x2s(stv2_current_sprite.CMDXA);
+ q[2].y = y2s(stv2_current_sprite.CMDYA);
+ q[3].x = x2s(stv2_current_sprite.CMDXB);
+ q[3].y = y2s(stv2_current_sprite.CMDYB);
+
+ q[0].u = q[3].u = q[1].u = q[2].u = 0;
+ q[0].v = q[1].v = q[2].v = q[3].v = 0;
+
+ vdp1_fill_quad(cliprect, 0, 1, q);
+
+ q[0].x = x2s(stv2_current_sprite.CMDXB);
+ q[0].y = y2s(stv2_current_sprite.CMDYB);
+ q[1].x = x2s(stv2_current_sprite.CMDXC);
+ q[1].y = y2s(stv2_current_sprite.CMDYC);
+ q[2].x = x2s(stv2_current_sprite.CMDXB);
+ q[2].y = y2s(stv2_current_sprite.CMDYB);
+ q[3].x = x2s(stv2_current_sprite.CMDXC);
+ q[3].y = y2s(stv2_current_sprite.CMDYC);
+
+ q[0].u = q[3].u = q[1].u = q[2].u = 0;
+ q[0].v = q[1].v = q[2].v = q[3].v = 0;
+
+ vdp1_fill_quad(cliprect, 0, 1, q);
+
+ q[0].x = x2s(stv2_current_sprite.CMDXC);
+ q[0].y = y2s(stv2_current_sprite.CMDYC);
+ q[1].x = x2s(stv2_current_sprite.CMDXD);
+ q[1].y = y2s(stv2_current_sprite.CMDYD);
+ q[2].x = x2s(stv2_current_sprite.CMDXC);
+ q[2].y = y2s(stv2_current_sprite.CMDYC);
+ q[3].x = x2s(stv2_current_sprite.CMDXD);
+ q[3].y = y2s(stv2_current_sprite.CMDYD);
+
+ q[0].u = q[3].u = q[1].u = q[2].u = 0;
+ q[0].v = q[1].v = q[2].v = q[3].v = 0;
+
+ vdp1_fill_quad(cliprect, 0, 1, q);
+
+ q[0].x = x2s(stv2_current_sprite.CMDXD);
+ q[0].y = y2s(stv2_current_sprite.CMDYD);
+ q[1].x = x2s(stv2_current_sprite.CMDXA);
+ q[1].y = y2s(stv2_current_sprite.CMDYA);
+ q[2].x = x2s(stv2_current_sprite.CMDXD);
+ q[2].y = y2s(stv2_current_sprite.CMDYD);
+ q[3].x = x2s(stv2_current_sprite.CMDXA);
+ q[3].y = y2s(stv2_current_sprite.CMDYA);
+
+ q[0].u = q[3].u = q[1].u = q[2].u = 0;
+ q[0].v = q[1].v = q[2].v = q[3].v = 0;
+
+ stv_vdp1_setup_shading(q, cliprect);
+ vdp1_fill_quad(cliprect, 0, 1, q);
+
+}
+
+void saturn_state::stv_vdp1_draw_distorted_sprite(const rectangle &cliprect)
+{
+ struct spoint q[4];
+
+ int xsize, ysize;
+ int direction;
+ int patterndata;
+
+ direction = (stv2_current_sprite.CMDCTRL & 0x0030)>>4;
+
+ if ( stv2_current_sprite.ispoly )
+ {
+ xsize = ysize = 1;
+ patterndata = 0;
+ }
+ else
+ {
+ xsize = (stv2_current_sprite.CMDSIZE & 0x3f00) >> 8;
+ xsize = xsize * 8;
+ if (xsize == 0) return; /* setting prohibited */
+
+ ysize = (stv2_current_sprite.CMDSIZE & 0x00ff);
+ if (ysize == 0) return; /* setting prohibited */
+
+ patterndata = (stv2_current_sprite.CMDSRCA) & 0xffff;
+ patterndata = patterndata * 0x8;
+
+ }
+
+
+ q[0].x = x2s(stv2_current_sprite.CMDXA);
+ q[0].y = y2s(stv2_current_sprite.CMDYA);
+ q[1].x = x2s(stv2_current_sprite.CMDXB);
+ q[1].y = y2s(stv2_current_sprite.CMDYB);
+ q[2].x = x2s(stv2_current_sprite.CMDXC);
+ q[2].y = y2s(stv2_current_sprite.CMDYC);
+ q[3].x = x2s(stv2_current_sprite.CMDXD);
+ q[3].y = y2s(stv2_current_sprite.CMDYD);
+
+ if(direction & 1) { // xflip
+ q[0].u = q[3].u = xsize-1;
+ q[1].u = q[2].u = 0;
+ } else {
+ q[0].u = q[3].u = 0;
+ q[1].u = q[2].u = xsize-1;
+ }
+ if(direction & 2) { // yflip
+ q[0].v = q[1].v = ysize-1;
+ q[2].v = q[3].v = 0;
+ } else {
+ q[0].v = q[1].v = 0;
+ q[2].v = q[3].v = ysize-1;
+ }
+
+ stv_vdp1_setup_shading(q, cliprect);
+ vdp1_fill_quad(cliprect, patterndata, xsize, q);
+}
+
+void saturn_state::stv_vdp1_draw_scaled_sprite(const rectangle &cliprect)
+{
+ struct spoint q[4];
+
+ int xsize, ysize;
+ int direction;
+ int patterndata;
+ int zoompoint;
+ int x,y;
+ int x2,y2;
+ int screen_width,screen_height,screen_height_negative = 0;
+
+ direction = (stv2_current_sprite.CMDCTRL & 0x0030)>>4;
+
+ xsize = (stv2_current_sprite.CMDSIZE & 0x3f00) >> 8;
+ xsize = xsize * 8;
+
+ ysize = (stv2_current_sprite.CMDSIZE & 0x00ff);
+
+ patterndata = (stv2_current_sprite.CMDSRCA) & 0xffff;
+ patterndata = patterndata * 0x8;
+
+ zoompoint = (stv2_current_sprite.CMDCTRL & 0x0f00)>>8;
+
+ x = stv2_current_sprite.CMDXA;
+ y = stv2_current_sprite.CMDYA;
+
+ screen_width = (INT16)stv2_current_sprite.CMDXB;
+ if ( (screen_width < 0) && zoompoint)
+ {
+ screen_width = -screen_width;
+ direction |= 1;
+ }
+
+ screen_height = (INT16)stv2_current_sprite.CMDYB;
+ if ( (screen_height < 0) && zoompoint )
+ {
+ screen_height_negative = 1;
+ screen_height = -screen_height;
+ direction |= 2;
+ }
+
+ x2 = stv2_current_sprite.CMDXC; // second co-ordinate set x
+ y2 = stv2_current_sprite.CMDYC; // second co-ordinate set y
+
+ switch (zoompoint)
+ {
+ case 0x0: // specified co-ordinates
+ break;
+ case 0x5: // up left
+ break;
+ case 0x6: // up center
+ x -= screen_width/2 ;
+ break;
+ case 0x7: // up right
+ x -= screen_width;
+ break;
+
+ case 0x9: // center left
+ y -= screen_height/2 ;
+ break;
+ case 0xa: // center center
+ y -= screen_height/2 ;
+ x -= screen_width/2 ;
+
+ break;
+
+ case 0xb: // center right
+ y -= screen_height/2 ;
+ x -= screen_width;
+ break;
+
+ case 0xd: // center left
+ y -= screen_height;
+ break;
+
+ case 0xe: // center center
+ y -= screen_height;
+ x -= screen_width/2 ;
+ break;
+
+ case 0xf: // center right
+ y -= screen_height;
+ x -= screen_width;
+ break;
+
+ default: // illegal
+ break;
+
+ }
+
+ /* 0----1
+ | |
+ | |
+ 3----2 */
+
+ if (zoompoint)
+ {
+ q[0].x = x2s(x);
+ q[0].y = y2s(y);
+ q[1].x = x2s(x)+screen_width;
+ q[1].y = y2s(y);
+ q[2].x = x2s(x)+screen_width;
+ q[2].y = y2s(y)+screen_height;
+ q[3].x = x2s(x);
+ q[3].y = y2s(y)+screen_height;
+
+ if ( screen_height_negative )
+ {
+ q[0].y += screen_height;
+ q[1].y += screen_height;
+ q[2].y += screen_height;
+ q[3].y += screen_height;
+ }
+ }
+ else
+ {
+ q[0].x = x2s(x);
+ q[0].y = y2s(y);
+ q[1].x = x2s(x2);
+ q[1].y = y2s(y);
+ q[2].x = x2s(x2);
+ q[2].y = y2s(y2);
+ q[3].x = x2s(x);
+ q[3].y = y2s(y2);
+ }
+
+
+ if(direction & 1) { // xflip
+ q[0].u = q[3].u = xsize-1;
+ q[1].u = q[2].u = 0;
+ } else {
+ q[0].u = q[3].u = 0;
+ q[1].u = q[2].u = xsize-1;
+ }
+ if(direction & 2) { // yflip
+ q[0].v = q[1].v = ysize-1;
+ q[2].v = q[3].v = 0;
+ } else {
+ q[0].v = q[1].v = 0;
+ q[2].v = q[3].v = ysize-1;
+ }
+
+ stv_vdp1_setup_shading(q, cliprect);
+ vdp1_fill_quad(cliprect, patterndata, xsize, q);
+}
+
+
+
+
+void saturn_state::stv_vdp1_draw_normal_sprite(const rectangle &cliprect, int sprite_type)
+{
+ //UINT16 *destline;
+ //saturn_state *state = machine.driver_data<saturn_state>();
+ int y, ysize, drawypos;
+ int x, xsize, drawxpos;
+ int direction;
+ int patterndata;
+ UINT8 shading;
+ int su, u, dux, duy;
+ int maxdrawypos, maxdrawxpos;
+
+ x = x2s(stv2_current_sprite.CMDXA);
+ y = y2s(stv2_current_sprite.CMDYA);
+
+ direction = (stv2_current_sprite.CMDCTRL & 0x0030)>>4;
+
+ xsize = (stv2_current_sprite.CMDSIZE & 0x3f00) >> 8;
+ xsize = xsize * 8;
+
+ ysize = (stv2_current_sprite.CMDSIZE & 0x00ff);
+
+ patterndata = (stv2_current_sprite.CMDSRCA) & 0xffff;
+ patterndata = patterndata * 0x8;
+
+ if (VDP1_LOG) logerror ("Drawing Normal Sprite x %04x y %04x xsize %04x ysize %04x patterndata %06x\n",x,y,xsize,ysize,patterndata);
+
+ if ( x > cliprect.max_x ) return;
+ if ( y > cliprect.max_y ) return;
+
+ shading = stv_read_gouraud_table();
+ if ( shading )
+ {
+ struct spoint q[4];
+ q[0].x = x; q[0].y = y;
+ q[1].x = x + xsize; q[1].y = y;
+ q[2].x = x + xsize; q[2].y = y + ysize;
+ q[3].x = x; q[3].y = y + ysize;
+
+ stv_vdp1_setup_shading( q, cliprect );
+ }
+
+ u = 0;
+ dux = 1;
+ duy = xsize;
+ if ( direction & 0x1 ) //xflip
+ {
+ dux = -1;
+ u = xsize - 1;
+ }
+ if ( direction & 0x2 ) //yflip
+ {
+ duy = -xsize;
+ u += xsize*(ysize-1);
+ }
+ if ( y < cliprect.min_y ) //clip y
+ {
+ u += xsize*(cliprect.min_y - y);
+ ysize -= (cliprect.min_y - y);
+ y = cliprect.min_y;
+ }
+ if ( x < cliprect.min_x ) //clip x
+ {
+ u += dux*(cliprect.min_x - x);
+ xsize -= (cliprect.min_x - x);
+ x = cliprect.min_x;
+ }
+ maxdrawypos = MIN(y+ysize-1,cliprect.max_y);
+ maxdrawxpos = MIN(x+xsize-1,cliprect.max_x);
+ for (drawypos = y; drawypos <= maxdrawypos; drawypos++ )
+ {
+ //destline = m_vdp1.framebuffer_draw_lines[drawypos];
+ su = u;
+ for (drawxpos = x; drawxpos <= maxdrawxpos; drawxpos++ )
+ {
+ (this->*drawpixel)( drawxpos, drawypos, patterndata, u );
+ u += dux;
+ }
+ u = su + duy;
+ }
+}
+
+TIMER_CALLBACK_MEMBER(saturn_state::vdp1_draw_end )
+{
+ /* set CEF to 1*/
+ CEF_1;
+
+ if(!(m_scu.ism & IRQ_VDP1_END))
+ {
+ m_maincpu->set_input_line_and_vector(0x2, HOLD_LINE, 0x4d);
+ scu_do_transfer(6);
+ }
+ else
+ m_scu.ist |= (IRQ_VDP1_END);
+}
+
+
+void saturn_state::stv_vdp1_process_list( void )
+{
+ int position;
+ int spritecount;
+ int vdp1_nest;
+ rectangle *cliprect;
+
+ spritecount = 0;
+ position = 0;
+
+ if (VDP1_LOG) logerror ("Sprite List Process START\n");
+
+ vdp1_nest = -1;
+
+ stv_clear_gouraud_shading();
+
+ /*Set CEF bit to 0*/
+ CEF_0;
+
+ while (spritecount<10000) // if its drawn this many sprites something is probably wrong or sega were crazy ;-)
+ {
+ int draw_this_sprite;
+
+ draw_this_sprite = 1;
+
+ // if (position >= ((0x80000/0x20)/4)) // safety check
+ // {
+ // if (VDP1_LOG) logerror ("Sprite List Position Too High!\n");
+ // position = 0;
+ // }
+
+ spritecount++;
+
+ stv2_current_sprite.CMDCTRL = (m_vdp1_vram[position * (0x20/4)+0] & 0xffff0000) >> 16;
+
+ if (stv2_current_sprite.CMDCTRL == 0x8000)
+ {
+ if (VDP1_LOG) logerror ("List Terminator (0x8000) Encountered, Sprite List Process END\n");
+ goto end; // end of list
+ }
+
+ stv2_current_sprite.CMDLINK = (m_vdp1_vram[position * (0x20/4)+0] & 0x0000ffff) >> 0;
+ stv2_current_sprite.CMDPMOD = (m_vdp1_vram[position * (0x20/4)+1] & 0xffff0000) >> 16;
+ stv2_current_sprite.CMDCOLR = (m_vdp1_vram[position * (0x20/4)+1] & 0x0000ffff) >> 0;
+ stv2_current_sprite.CMDSRCA = (m_vdp1_vram[position * (0x20/4)+2] & 0xffff0000) >> 16;
+ stv2_current_sprite.CMDSIZE = (m_vdp1_vram[position * (0x20/4)+2] & 0x0000ffff) >> 0;
+ stv2_current_sprite.CMDXA = (m_vdp1_vram[position * (0x20/4)+3] & 0xffff0000) >> 16;
+ stv2_current_sprite.CMDYA = (m_vdp1_vram[position * (0x20/4)+3] & 0x0000ffff) >> 0;
+ stv2_current_sprite.CMDXB = (m_vdp1_vram[position * (0x20/4)+4] & 0xffff0000) >> 16;
+ stv2_current_sprite.CMDYB = (m_vdp1_vram[position * (0x20/4)+4] & 0x0000ffff) >> 0;
+ stv2_current_sprite.CMDXC = (m_vdp1_vram[position * (0x20/4)+5] & 0xffff0000) >> 16;
+ stv2_current_sprite.CMDYC = (m_vdp1_vram[position * (0x20/4)+5] & 0x0000ffff) >> 0;
+ stv2_current_sprite.CMDXD = (m_vdp1_vram[position * (0x20/4)+6] & 0xffff0000) >> 16;
+ stv2_current_sprite.CMDYD = (m_vdp1_vram[position * (0x20/4)+6] & 0x0000ffff) >> 0;
+ stv2_current_sprite.CMDGRDA = (m_vdp1_vram[position * (0x20/4)+7] & 0xffff0000) >> 16;
+// stv2_current_sprite.UNUSED = (m_vdp1_vram[position * (0x20/4)+7] & 0x0000ffff) >> 0;
+
+ /* proecess jump / skip commands, set position for next sprite */
+ switch (stv2_current_sprite.CMDCTRL & 0x7000)
+ {
+ case 0x0000: // jump next
+ if (VDP1_LOG) logerror ("Sprite List Process + Next (Normal)\n");
+ position++;
+ break;
+ case 0x1000: // jump assign
+ if (VDP1_LOG) logerror ("Sprite List Process + Jump Old %06x New %06x\n", position, (stv2_current_sprite.CMDLINK>>2));
+ position= (stv2_current_sprite.CMDLINK>>2);
+ break;
+ case 0x2000: // jump call
+ if (vdp1_nest == -1)
+ {
+ if (VDP1_LOG) logerror ("Sprite List Process + Call Old %06x New %06x\n",position, (stv2_current_sprite.CMDLINK>>2));
+ vdp1_nest = position+1;
+ position = (stv2_current_sprite.CMDLINK>>2);
+ }
+ else
+ {
+ if (VDP1_LOG) logerror ("Sprite List Nested Call, ignoring\n");
+ position++;
+ }
+ break;
+ case 0x3000:
+ if (vdp1_nest != -1)
+ {
+ if (VDP1_LOG) logerror ("Sprite List Process + Return\n");
+ position = vdp1_nest;
+ vdp1_nest = -1;
+ }
+ else
+ {
+ if (VDP1_LOG) logerror ("Attempted return from no subroutine, aborting\n");
+ position++;
+ goto end; // end of list
+ }
+ break;
+ case 0x4000:
+ draw_this_sprite = 0;
+ position++;
+ break;
+ case 0x5000:
+ if (VDP1_LOG) logerror ("Sprite List Skip + Jump Old %06x New %06x\n", position, (stv2_current_sprite.CMDLINK>>2));
+ draw_this_sprite = 0;
+ position= (stv2_current_sprite.CMDLINK>>2);
+
+ break;
+ case 0x6000:
+ draw_this_sprite = 0;
+ if (vdp1_nest == -1)
+ {
+ if (VDP1_LOG) logerror ("Sprite List Skip + Call To Subroutine Old %06x New %06x\n",position, (stv2_current_sprite.CMDLINK>>2));
+
+ vdp1_nest = position+1;
+ position = (stv2_current_sprite.CMDLINK>>2);
+ }
+ else
+ {
+ if (VDP1_LOG) logerror ("Sprite List Nested Call, ignoring\n");
+ position++;
+ }
+ break;
+ case 0x7000:
+ draw_this_sprite = 0;
+ if (vdp1_nest != -1)
+ {
+ if (VDP1_LOG) logerror ("Sprite List Skip + Return from Subroutine\n");
+
+ position = vdp1_nest;
+ vdp1_nest = -1;
+ }
+ else
+ {
+ if (VDP1_LOG) logerror ("Attempted return from no subroutine, aborting\n");
+ position++;
+ goto end; // end of list
+ }
+ break;
+ }
+
+ /* continue to draw this sprite only if the command wasn't to skip it */
+ if (draw_this_sprite ==1)
+ {
+ if ( stv2_current_sprite.CMDPMOD & 0x0400 )
+ {
+ //if(stv2_current_sprite.CMDPMOD & 0x0200) /* TODO: Bio Hazard inventory screen uses outside cliprect */
+ // cliprect = &m_vdp1.system_cliprect;
+ //else
+ cliprect = &m_vdp1.user_cliprect;
+ }
+ else
+ {
+ cliprect = &m_vdp1.system_cliprect;
+ }
+
+ stv_vdp1_set_drawpixel();
+
+ switch (stv2_current_sprite.CMDCTRL & 0x000f)
+ {
+ case 0x0000:
+ if (VDP1_LOG) logerror ("Sprite List Normal Sprite (%d %d)\n",stv2_current_sprite.CMDXA,stv2_current_sprite.CMDYA);
+ stv2_current_sprite.ispoly = 0;
+ stv_vdp1_draw_normal_sprite(*cliprect, 0);
+ break;
+
+ case 0x0001:
+ if (VDP1_LOG) logerror ("Sprite List Scaled Sprite (%d %d)\n",stv2_current_sprite.CMDXA,stv2_current_sprite.CMDYA);
+ stv2_current_sprite.ispoly = 0;
+ stv_vdp1_draw_scaled_sprite(*cliprect);
+ break;
+
+ case 0x0002:
+ case 0x0003: // used by Hardcore 4x4
+ if (VDP1_LOG) logerror ("Sprite List Distorted Sprite\n");
+ if (VDP1_LOG) logerror ("(A: %d %d)\n",stv2_current_sprite.CMDXA,stv2_current_sprite.CMDYA);
+ if (VDP1_LOG) logerror ("(B: %d %d)\n",stv2_current_sprite.CMDXB,stv2_current_sprite.CMDYB);
+ if (VDP1_LOG) logerror ("(C: %d %d)\n",stv2_current_sprite.CMDXC,stv2_current_sprite.CMDYC);
+ if (VDP1_LOG) logerror ("(D: %d %d)\n",stv2_current_sprite.CMDXD,stv2_current_sprite.CMDYD);
+ if (VDP1_LOG) logerror ("CMDPMOD = %04x\n",stv2_current_sprite.CMDPMOD);
+
+ stv2_current_sprite.ispoly = 0;
+ stv_vdp1_draw_distorted_sprite(*cliprect);
+ break;
+
+ case 0x0004:
+ if (VDP1_LOG) logerror ("Sprite List Polygon\n");
+ stv2_current_sprite.ispoly = 1;
+ stv_vdp1_draw_distorted_sprite(*cliprect);
+ break;
+
+ case 0x0005:
+// case 0x0007: // mirror? Baroque uses it, crashes for whatever reason
+ if (VDP1_LOG) logerror ("Sprite List Polyline\n");
+ stv2_current_sprite.ispoly = 1;
+ stv_vdp1_draw_poly_line(*cliprect);
+ break;
+
+ case 0x0006:
+ if (VDP1_LOG) logerror ("Sprite List Line\n");
+ stv2_current_sprite.ispoly = 1;
+ stv_vdp1_draw_line(*cliprect);
+ break;
+
+ case 0x0008:
+// case 0x000b: // mirror? Bug 2
+ if (VDP1_LOG) logerror ("Sprite List Set Command for User Clipping (%d,%d),(%d,%d)\n", stv2_current_sprite.CMDXA, stv2_current_sprite.CMDYA, stv2_current_sprite.CMDXC, stv2_current_sprite.CMDYC);
+ m_vdp1.user_cliprect.set(stv2_current_sprite.CMDXA, stv2_current_sprite.CMDXC, stv2_current_sprite.CMDYA, stv2_current_sprite.CMDYC);
+ break;
+
+ case 0x0009:
+ if (VDP1_LOG) logerror ("Sprite List Set Command for System Clipping (0,0),(%d,%d)\n", stv2_current_sprite.CMDXC, stv2_current_sprite.CMDYC);
+ m_vdp1.system_cliprect.set(0, stv2_current_sprite.CMDXC, 0, stv2_current_sprite.CMDYC);
+ break;
+
+ case 0x000a:
+ if (VDP1_LOG) logerror ("Sprite List Local Co-Ordinate Set (%d %d)\n",(INT16)stv2_current_sprite.CMDXA,(INT16)stv2_current_sprite.CMDYA);
+ m_vdp1.local_x = (INT16)stv2_current_sprite.CMDXA;
+ m_vdp1.local_y = (INT16)stv2_current_sprite.CMDYA;
+ break;
+
+ default:
+ popmessage ("VDP1: Sprite List Illegal %02x, contact MAMEdev",stv2_current_sprite.CMDCTRL & 0xf);
+ m_vdp1.lopr = (position * 0x20) >> 3;
+ m_vdp1.copr = (position * 0x20) >> 3;
+ return;
+ }
+ }
+
+ }
+
+
+ end:
+ m_vdp1.copr = (position * 0x20) >> 3;
+
+ /* TODO: what's the exact formula? Guess it should be a mix between number of pixels written and actual command data fetched. */
+ machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(spritecount*16), timer_expired_delegate(FUNC(saturn_state::vdp1_draw_end),this));
+
+ if (VDP1_LOG) logerror ("End of list processing!\n");
+}
+
+void saturn_state::video_update_vdp1( void )
+{
+ int framebuffer_changed = 0;
+
+// int enable;
+// if (machine.input().code_pressed (KEYCODE_R)) VDP1_LOG = 1;
+// if (machine.input().code_pressed (KEYCODE_T)) VDP1_LOG = 0;
+
+// if (machine.input().code_pressed (KEYCODE_Y)) VDP1_LOG = 0;
+// {
+// FILE *fp;
+//
+// fp=fopen("vdp1_ram.dmp", "w+b");
+// if (fp)
+// {
+// fwrite(stv_vdp1, 0x00100000, 1, fp);
+// fclose(fp);
+// }
+// }
+ if (VDP1_LOG) logerror("video_update_vdp1 called\n");
+ if (VDP1_LOG) logerror( "FBCR = %0x, accessed = %d\n", STV_VDP1_FBCR, m_vdp1.fbcr_accessed );
+
+ if(STV_VDP1_CEF)
+ BEF_1;
+ else
+ BEF_0;
+
+ if ( m_vdp1.framebuffer_clear_on_next_frame )
+ {
+ if ( ((STV_VDP1_FBCR & 0x3) == 3) &&
+ m_vdp1.fbcr_accessed )
+ {
+ stv_clear_framebuffer(m_vdp1.framebuffer_current_display);
+ m_vdp1.framebuffer_clear_on_next_frame = 0;
+ }
+ }
+
+ switch( STV_VDP1_FBCR & 0x3 )
+ {
+ case 0: /* Automatic mode */
+ stv_vdp1_change_framebuffers();
+ stv_clear_framebuffer(m_vdp1.framebuffer_current_draw);
+ framebuffer_changed = 1;
+ break;
+ case 1: /* Setting prohibited */
+ break;
+ case 2: /* Manual mode - erase */
+ if ( m_vdp1.fbcr_accessed )
+ {
+ m_vdp1.framebuffer_clear_on_next_frame = 1;
+ }
+ break;
+ case 3: /* Manual mode - change */
+ if ( m_vdp1.fbcr_accessed )
+ {
+ stv_vdp1_change_framebuffers();
+ if ( STV_VDP1_VBE )
+ {
+ stv_clear_framebuffer(m_vdp1.framebuffer_current_draw);
+ }
+ /* TODO: Slam n Jam 96 & Cross Romance doesn't like this, investigate. */
+ framebuffer_changed = 1;
+ }
+ // framebuffer_changed = 1;
+ break;
+ }
+ m_vdp1.fbcr_accessed = 0;
+
+ if (VDP1_LOG) logerror( "PTM = %0x, TVM = %x\n", STV_VDP1_PTM, STV_VDP1_TVM );
+ /*Set CEF bit to 0*/
+ //CEF_0;
+ switch(STV_VDP1_PTM & 3)
+ {
+ case 0:/*Idle Mode*/
+ /*Set CEF bit to 0*/
+ CEF_0;
+ break;
+ case 1:/*Draw by request*/
+ /*Set CEF bit to 0*/
+ CEF_0;
+ break;
+ case 2:/*Automatic Draw*/
+ if ( framebuffer_changed || VDP1_LOG )
+ {
+ /*set CEF to 1*/
+ stv_vdp1_process_list();
+ }
+ break;
+ case 3: /*<invalid>*/
+ logerror("Warning: Invalid PTM mode set for VDP1!\n");
+ break;
+ }
+ //popmessage("%04x %04x",STV_VDP1_EWRR_X3,STV_VDP1_EWRR_Y3);
+}
+
+void saturn_state::stv_vdp1_state_save_postload( void )
+{
+ UINT8 *vdp1 = m_vdp1.gfx_decode;
+ int offset;
+ UINT32 data;
+
+ m_vdp1.framebuffer_mode = -1;
+ m_vdp1.framebuffer_double_interlace = -1;
+
+ stv_set_framebuffer_config();
+
+ for (offset = 0; offset < 0x80000/4; offset++ )
+ {
+ data = m_vdp1_vram[offset];
+ /* put in gfx region for easy decoding */
+ vdp1[offset*4+0] = (data & 0xff000000) >> 24;
+ vdp1[offset*4+1] = (data & 0x00ff0000) >> 16;
+ vdp1[offset*4+2] = (data & 0x0000ff00) >> 8;
+ vdp1[offset*4+3] = (data & 0x000000ff) >> 0;
+ }
+}
+
+int saturn_state::stv_vdp1_start ( void )
+{
+ m_vdp1_regs = auto_alloc_array_clear(machine(), UINT16, 0x020/2 );
+ m_vdp1_vram = auto_alloc_array_clear(machine(), UINT32, 0x100000/4 );
+ m_vdp1.gfx_decode = auto_alloc_array(machine(), UINT8, 0x100000 );
+
+ stv_vdp1_shading_data = auto_alloc(machine(), struct stv_vdp1_poly_scanline_data);
+
+ m_vdp1.framebuffer[0] = auto_alloc_array(machine(), UINT16, 1024 * 256 * 2 ); /* *2 is for double interlace */
+ m_vdp1.framebuffer[1] = auto_alloc_array(machine(), UINT16, 1024 * 256 * 2 );
+
+ m_vdp1.framebuffer_display_lines = auto_alloc_array(machine(), UINT16 *, 512);
+ m_vdp1.framebuffer_draw_lines = auto_alloc_array(machine(), UINT16 *, 512);
+
+ m_vdp1.framebuffer_width = m_vdp1.framebuffer_height = 0;
+ m_vdp1.framebuffer_mode = -1;
+ m_vdp1.framebuffer_double_interlace = -1;
+ m_vdp1.fbcr_accessed = 0;
+ m_vdp1.framebuffer_current_display = 0;
+ m_vdp1.framebuffer_current_draw = 1;
+ stv_clear_framebuffer(m_vdp1.framebuffer_current_draw);
+ m_vdp1.framebuffer_clear_on_next_frame = 0;
+
+ m_vdp1.system_cliprect.set(0, 0, 0, 0);
+ /* Kidou Senshi Z Gundam - Zenpen Zeta no Kodou loves to use the user cliprect vars in an undefined state ... */
+ m_vdp1.user_cliprect.set(0, 512, 0, 256);
+
+ // save state
+ save_pointer(NAME(m_vdp1_regs), 0x020/2);
+ save_pointer(NAME(m_vdp1_vram), 0x100000/4);
+ save_item(NAME(m_vdp1.fbcr_accessed));
+ save_item(NAME(m_vdp1.framebuffer_current_display));
+ save_item(NAME(m_vdp1.framebuffer_current_draw));
+ save_item(NAME(m_vdp1.framebuffer_clear_on_next_frame));
+ save_item(NAME(m_vdp1.local_x));
+ save_item(NAME(m_vdp1.local_y));
+ machine().save().register_postload(save_prepost_delegate(FUNC(saturn_state::stv_vdp1_state_save_postload), this));
+ return 0;
+}
diff --git a/src/devices/video/stvvdp2.c b/src/devices/video/stvvdp2.c
new file mode 100644
index 00000000000..e2fbcf7f58d
--- /dev/null
+++ b/src/devices/video/stvvdp2.c
@@ -0,0 +1,6968 @@
+// license:LGPL-2.1+
+// copyright-holders:David Haywood, Angelo Salese, Olivier Galibert, Mariusz Wojcieszek, R. Belmont
+/* Sega Saturn VDP2 */
+
+#define DEBUG_MODE 0
+#define TEST_FUNCTIONS 0
+#define POPMESSAGE_DEBUG 0
+
+/*
+
+the dirty marking stuff and tile decoding will probably be removed in the end anyway as we'll need custom
+rendering code since mame's drawgfx / tilesytem don't offer everything st-v needs
+
+this system seems far too complex to use Mame's tilemap system
+
+4 'scroll' planes (scroll screens)
+
+the scroll planes have slightly different capabilities
+
+NBG0
+NBG1
+NBG2
+NBG3
+
+2 'rotate' planes
+
+RBG0
+RBG1
+
+-- other crap
+EXBG (external)
+
+-----------------------------------------------------------------------------------------------------------
+
+Video emulation TODO:
+-all games:
+ \-priorities (check myfairld,thunt)
+ \-complete windows effects
+ \-mosaic effect
+ \-ODD bit/H/V Counter not yet emulated properly
+ \-Reduction enable bits
+ \-Check if there are any remaining video registers that are yet to be macroized & added to the rumble.
+-batmanfr:
+ \-If you reset the game after the character selection screen,when you get again to it there's garbage
+ floating behind Batman.
+-elandore:
+ \-(BTANB) priorities at the VS. screen apparently is wrong,but it's like this on the Saturn version too.
+-hanagumi:
+ \-ending screens have corrupt graphics. (*untested*)
+-kiwames:
+ \-incorrect color emulation for the alpha blended flames on the title screen,it's caused by a schizoid
+ linescroll emulation quirk.
+ \-the VDP1 sprites refresh is too slow,causing the "Draw by request" mode to
+ flicker.Moved back to default ATM.
+-pblbeach:
+ \-Sprites are offset, because it doesn't clear vdp1 local coordinates set by bios,
+ I guess that they are cleared when some vdp1 register is written (kludged for now)
+-prikura:
+ \-Attract mode presentation has corrupted graphics in various places,probably caused by incomplete
+ framebuffer data delete.
+-seabass:
+ \-Player sprite is corrupt/missing during movements,caused by incomplete framebuffer switching.
+
+Notes of Interest & Unclear features:
+
+-the test mode / bios is drawn with layer NBG3;
+-hanagumi puts a 'RED' dragon logo in tileram (base 0x64000, 4bpp, 8x8 tiles) but
+its not displayed because its priority value is 0.Left-over?
+
+-scrolling is screen display wise,meaning that a scrolling value is masked with the
+screen resolution size values;
+
+-H-Blank bit is INDIPENDENT of the V-Blank bit...trying to fix enable/disable it during V-Blank period
+ causes wrong gameplay speed in Golden Axe:The Duel.
+
+-Bitmaps uses transparency pens,examples are:
+\-elandore's energy bars;
+\-mausuke's foreground(the one used on the playfield)
+\-shanhigw's tile-based sprites;
+The transparency pen table is like this:
+
+|------------------|---------------------|
+| Character count | Transparency code |
+|------------------|---------------------|
+| 16 colors |=0x0 (4 bits) |
+| 256 colors |=0x00 (8 bits) |
+| 2048 colors |=0x000 (11 bits) |
+| 32,768 colors |MSB=0 (bit 15) |
+| 16,770,000 colors|MSB=0 (bit 31) |
+|------------------|---------------------|
+In other words,the first three types uses the offset and not the color allocated.
+
+-double density interlace setting (LSMD == 3) apparently does a lot of fancy stuff in the graphics sizes.
+
+-Debug key list(only if you enable the debug mode on top of this file):
+ \-T: NBG3 layer toggle
+ \-Y: NBG2 layer toggle
+ \-U: NBG1 layer toggle
+ \-I: NBG0 layer toggle
+ \-O: SPRITE toggle
+ \-K: RBG0 layer toggle
+ \-W Decodes the graphics for F4 menu.
+ \-M Stores VDP1 ram contents from a file.
+ \-N Stores VDP1 ram contents into a file.
+*/
+
+#include "emu.h"
+#include "includes/stv.h"
+
+enum
+{
+ STV_TRANSPARENCY_NONE,
+ STV_TRANSPARENCY_PEN,
+ STV_TRANSPARENCY_ADD_BLEND,
+ STV_TRANSPARENCY_ALPHA
+};
+
+#if DEBUG_MODE
+#define LOG_VDP2 1
+#define LOG_ROZ 0
+#else
+#define LOG_VDP2 0
+#define LOG_ROZ 0
+#endif
+
+/*
+
+-------------------------------------------------|-----------------------------|------------------------------
+| Function | Normal Scroll Screen | Rotation Scroll Screen |
+| |-----------------------------|-----------------------------|------------------------------
+| | NBG0 | NBG1 | NBG2 | NBG3 | RBG0 | RBG1 |
+-------------------------------------------------|-----------------------------|------------------------------
+| Character Colour | 16 colours | 16 colours | 16 colours | 16 colours | 16 colours | 16 colours |
+| Count | 256 " " | 256 " " | 256 " " | 256 " " | 256 " " | 256 " " |
+| | 2048 " " | 2048 " " | | | 2048 " " | 2048 " " |
+| | 32768 " " | 32768 " " | | | 32768 " " | 32768 " " |
+| | 16770000 " " | | | | 16770000 " " | 16770000 " " |
+-------------------------------------------------|-----------------------------|------------------------------
+| Character Size | 1x1 Cells , 2x2 Cells |
+-------------------------------------------------|-----------------------------|------------------------------
+| Pattern Name | 1 word , 2 words |
+| Data Size | |
+-------------------------------------------------|-----------------------------|------------------------------
+| Plane Size | 1 H x 1 V 1 Pages ; 2 H x 1 V 1 Pages ; 2 H x 2 V Pages |
+-------------------------------------------------|-----------------------------|------------------------------
+| Plane Count | 4 | 16 |
+-------------------------------------------------|-----------------------------|------------------------------
+| Bitmap Possible | Yes | No | Yes | No |
+-------------------------------------------------|-----------------------------|------------------------------
+| Bitmap Size | 512 x 256 | N/A | 512x256 | N/A |
+| | 512 x 512 | | 512x512 | |
+| | 1024 x 256 | | | |
+| | 1024 x 512 | | | |
+-------------------------------------------------|-----------------------------|------------------------------
+| Scale | 0.25 x - 256 x | None | Any ? |
+-------------------------------------------------|-----------------------------|------------------------------
+| Rotation | No | Yes |
+-------------------------------------------------|-----------------------------|-----------------------------|
+| Linescroll | Yes | No |
+-------------------------------------------------|-----------------------------|------------------------------
+| Column Scroll | Yes | No |
+-------------------------------------------------|-----------------------------|------------------------------
+| Mosaic | Yes | Horizontal Only |
+-------------------------------------------------|-----------------------------|------------------------------
+
+*/
+
+/* 180000 - r/w - TVMD - TV Screen Mode
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | DISP | -- | -- | -- | -- | -- | -- | BDCLMD |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | LSMD1 | LSMD0 | VRESO1 | VRESO0 | -- | HRESO2 | HRESO1 | HRESO0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_TVMD (m_vdp2_regs[0x000/2])
+
+ #define STV_VDP2_DISP ((STV_VDP2_TVMD & 0x8000) >> 15)
+ #define STV_VDP2_BDCLMD ((STV_VDP2_TVMD & 0x0100) >> 8)
+ #define STV_VDP2_LSMD ((STV_VDP2_TVMD & 0x00c0) >> 6)
+ #define STV_VDP2_VRES ((STV_VDP2_TVMD & 0x0030) >> 4)
+ #define STV_VDP2_HRES ((STV_VDP2_TVMD & 0x0007) >> 0)
+
+/* 180002 - r/w - EXTEN - External Signal Enable Register
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | EXLTEN | EXSYEN |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | DASEL | EXBGEN |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_EXTEN (m_vdp2_regs[0x002/2])
+
+ #define STV_VDP2_EXLTEN ((STV_VDP2_EXTEN & 0x0200) >> 9)
+
+/* 180004 - r/o - TVSTAT - Screen Status
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | EXLTFG | EXSYFG |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | VBLANK | HBLANK | ODD | EVEN |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+/* 180006 - r/w - VRSIZE - VRAM Size
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VRAMSZ | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | VER3 | VER2 | VER1 | VER0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_VRSIZE (m_vdp2_regs[0x006/2])
+
+ #define STV_VDP2_VRAMSZ ((STV_VDP2_VRSIZE & 0x8000) >> 15)
+
+/* 180008 - r/o - HCNT - H-Counter
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | HCT9 | HCT8 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | HCT7 | HCT6 | HCT5 | HCT4 | HCT3 | HCT2 | HCT1 | HCT0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_HCNT (m_vdp2_regs[0x008/2])
+
+/* 18000A - r/o - VCNT - V-Counter
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | VCT9 | VCT8 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCT7 | VCT6 | VCT5 | VCT4 | VCT3 | VCT2 | VCT1 | VCT0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_VCNT (m_vdp2_regs[0x00a/2])
+
+/* 18000C - RESERVED
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+/* 18000E - r/w - RAMCTL - RAM Control
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | CRKTE | -- | CRMD1 | CRMD0 | -- | -- | VRBMD | VRAMD |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | RDBSB11 | RDBSB10 | RDBSB01 | RDBSB00 | RDBSA11 | RDBSA10 | RDBSA01 | RDBSA00 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_RAMCTL (m_vdp2_regs[0x00e/2])
+
+ #define STV_VDP2_CRKTE ((STV_VDP2_RAMCTL & 0x8000) >> 15)
+ #define STV_VDP2_CRMD ((STV_VDP2_RAMCTL & 0x3000) >> 12)
+ #define STV_VDP2_RDBSB1 ((STV_VDP2_RAMCTL & 0x00c0) >> 6)
+ #define STV_VDP2_RDBSB0 ((STV_VDP2_RAMCTL & 0x0030) >> 4)
+ #define STV_VDP2_RDBSA1 ((STV_VDP2_RAMCTL & 0x000c) >> 2)
+ #define STV_VDP2_RDBSA0 ((STV_VDP2_RAMCTL & 0x0003) >> 0)
+
+
+/* 180010 - r/w - -CYCA0L - VRAM CYCLE PATTERN (BANK A0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VCP0A03 | VCP0A02 | VCP0A01 | VCP0A00 | VCP1A03 | VCP1A02 | VCP1A01 | VCP1A00 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCP2A03 | VCP2A02 | VCP2A01 | VCP2A00 | VCP3A03 | VCP3A02 | VCP3A01 | VCP3A00 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CYCA0L (m_vdp2_regs[0x010/2])
+
+/* 180012 - r/w - -CYCA0U - VRAM CYCLE PATTERN (BANK A0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VCP4A03 | VCP4A02 | VCP4A01 | VCP4A00 | VCP5A03 | VCP5A02 | VCP5A01 | VCP5A00 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCP6A03 | VCP6A02 | VCP6A01 | VCP6A00 | VCP7A03 | VCP7A02 | VCP7A01 | VCP7A00 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CYCA0U (m_vdp2_regs[0x012/2])
+
+/* 180014 - r/w - -CYCA1L - VRAM CYCLE PATTERN (BANK A1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VCP0A13 | VCP0A12 | VCP0A11 | VCP0A10 | VCP1A13 | VCP1A12 | VCP1A11 | VCP1A10 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCP2A13 | VCP2A12 | VCP2A11 | VCP2A10 | VCP3A13 | VCP3A12 | VCP3A11 | VCP3A10 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CYCA1L (m_vdp2_regs[0x014/2])
+
+/* 180016 - r/w - -CYCA1U - VRAM CYCLE PATTERN (BANK A1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VCP4A13 | VCP4A12 | VCP4A11 | VCP4A10 | VCP5A13 | VCP5A12 | VCP5A11 | VCP5A10 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCP6A13 | VCP6A12 | VCP6A11 | VCP6A10 | VCP7A13 | VCP7A12 | VCP7A11 | VCP7A10 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CYCA1U (m_vdp2_regs[0x016/2])
+
+/* 180018 - r/w - -CYCB0L - VRAM CYCLE PATTERN (BANK B0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VCP0B03 | VCP0B02 | VCP0B01 | VCP0B00 | VCP1B03 | VCP1B02 | VCP1B01 | VCP1B00 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCP2B03 | VCP2B02 | VCP2B01 | VCP2B00 | VCP3B03 | VCP3B02 | VCP3B01 | VCP3B00 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CYCA2L (m_vdp2_regs[0x018/2])
+
+/* 18001A - r/w - -CYCB0U - VRAM CYCLE PATTERN (BANK B0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VCP4B03 | VCP4B02 | VCP4B01 | VCP4B00 | VCP5B03 | VCP5B02 | VCP5B01 | VCP5B00 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCP6B03 | VCP6B02 | VCP6B01 | VCP6B00 | VCP7B03 | VCP7B02 | VCP7B01 | VCP7B00 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CYCA2U (m_vdp2_regs[0x01a/2])
+
+/* 18001C - r/w - -CYCB1L - VRAM CYCLE PATTERN (BANK B1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VCP0B13 | VCP0B12 | VCP0B11 | VCP0B10 | VCP1B13 | VCP1B12 | VCP1B11 | VCP1B10 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCP2B13 | VCP2B12 | VCP2B11 | VCP2B10 | VCP3B13 | VCP3B12 | VCP3B11 | VCP3B10 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CYCA3L (m_vdp2_regs[0x01c/2])
+
+/* 18001E - r/w - -CYCB1U - VRAM CYCLE PATTERN (BANK B1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | VCP4B13 | VCP4B12 | VCP4B11 | VCP4B10 | VCP5B13 | VCP5B12 | VCP5B11 | VCP5B10 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | VCP6B13 | VCP6B12 | VCP6B11 | VCP6B10 | VCP7B13 | VCP7B12 | VCP7B11 | VCP7B10 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CYCA3U (m_vdp2_regs[0x01e/2])
+
+/* 180020 - r/w - BGON - SCREEN DISPLAY ENABLE
+
+ this register allows each tilemap to be enabled or disabled and also which layers are solid
+
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | R0TPON | N3TPON | N2TPON | N1TPON | N0TPON |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | R1ON | R0ON | N3ON | N2ON | N1ON | N0ON |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_BGON (m_vdp2_regs[0x020/2])
+
+ // NxOn - Layer Enable Register
+ #define STV_VDP2_xxON ((STV_VDP2_BGON & 0x001f) >> 0) /* to see if anything is enabled */
+
+ #define STV_VDP2_N0ON ((STV_VDP2_BGON & 0x0001) >> 0) /* N0On = NBG0 Enable */
+ #define STV_VDP2_N1ON ((STV_VDP2_BGON & 0x0002) >> 1) /* N1On = NBG1 Enable */
+ #define STV_VDP2_N2ON ((STV_VDP2_BGON & 0x0004) >> 2) /* N2On = NBG2 Enable */
+ #define STV_VDP2_N3ON ((STV_VDP2_BGON & 0x0008) >> 3) /* N3On = NBG3 Enable */
+ #define STV_VDP2_R0ON ((STV_VDP2_BGON & 0x0010) >> 4) /* R0On = RBG0 Enable */
+ #define STV_VDP2_R1ON ((STV_VDP2_BGON & 0x0020) >> 5) /* R1On = RBG1 Enable */
+
+ // NxTPON - Transparency Pen Enable Registers
+ #define STV_VDP2_N0TPON ((STV_VDP2_BGON & 0x0100) >> 8) /* N0TPON = NBG0 Draw Transparent Pen (as solid) /or/ RBG1 Draw Transparent Pen */
+ #define STV_VDP2_N1TPON ((STV_VDP2_BGON & 0x0200) >> 9) /* N1TPON = NBG1 Draw Transparent Pen (as solid) /or/ EXBG Draw Transparent Pen */
+ #define STV_VDP2_N2TPON ((STV_VDP2_BGON & 0x0400) >> 10)/* N2TPON = NBG2 Draw Transparent Pen (as solid) */
+ #define STV_VDP2_N3TPON ((STV_VDP2_BGON & 0x0800) >> 11)/* N3TPON = NBG3 Draw Transparent Pen (as solid) */
+ #define STV_VDP2_R0TPON ((STV_VDP2_BGON & 0x1000) >> 12)/* R0TPON = RBG0 Draw Transparent Pen (as solid) */
+
+/*
+180022 - MZCTL - Mosaic Control
+bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MZCTL (m_vdp2_regs[0x022/2])
+
+ #define STV_VDP2_MZSZV ((STV_VDP2_MZCTL & 0xf000) >> 12)
+ #define STV_VDP2_MZSZH ((STV_VDP2_MZCTL & 0x0f00) >> 8)
+ #define STV_VDP2_R0MZE ((STV_VDP2_MZCTL & 0x0010) >> 4)
+ #define STV_VDP2_N3MZE ((STV_VDP2_MZCTL & 0x0008) >> 3)
+ #define STV_VDP2_N2MZE ((STV_VDP2_MZCTL & 0x0004) >> 2)
+ #define STV_VDP2_N1MZE ((STV_VDP2_MZCTL & 0x0002) >> 1)
+ #define STV_VDP2_N0MZE ((STV_VDP2_MZCTL & 0x0001) >> 0)
+
+/*180024 - Special Function Code Select
+
+*/
+
+ #define STV_VDP2_SFSEL (m_vdp2_regs[0x024/2])
+
+/*180026 - Special Function Code
+
+*/
+
+ #define STV_VDP2_SFCODE (m_vdp2_regs[0x026/2])
+
+
+/*
+180028 - CHCTLA - Character Control (NBG0, NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | N1CHCN1 | N1CHCN0 | N1BMSZ1 | N1BMSZ0 | N1BMEN | N1CHSZ |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | N0CHCN2 | N0CHCN1 | N0CHCN0 | N0BMSZ1 | N0BMSZ0 | N0BMEN | N0CHSZ |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CHCTLA (m_vdp2_regs[0x028/2])
+
+/* -------------------------- NBG0 Character Control Registers -------------------------- */
+
+/* N0CHCNx NBG0 (or RGB1) Colour Depth
+ 000 - 16 Colours
+ 001 - 256 Colours
+ 010 - 2048 Colours
+ 011 - 32768 Colours (RGB5)
+ 100 - 16770000 Colours (RGB8)
+ 101 - invalid
+ 110 - invalid
+ 111 - invalid */
+ #define STV_VDP2_N0CHCN ((STV_VDP2_CHCTLA & 0x0070) >> 4)
+
+/* N0BMSZx - NBG0 Bitmap Size *guessed*
+ 00 - 512 x 256
+ 01 - 512 x 512
+ 10 - 1024 x 256
+ 11 - 1024 x 512 */
+ #define STV_VDP2_N0BMSZ ((STV_VDP2_CHCTLA & 0x000c) >> 2)
+
+/* N0BMEN - NBG0 Bitmap Enable
+ 0 - use cell mode
+ 1 - use bitmap mode */
+ #define STV_VDP2_N0BMEN ((STV_VDP2_CHCTLA & 0x0002) >> 1)
+
+/* N0CHSZ - NBG0 Character (Tile) Size
+ 0 - 1 cell x 1 cell (8x8)
+ 1 - 2 cells x 2 cells (16x16) */
+ #define STV_VDP2_N0CHSZ ((STV_VDP2_CHCTLA & 0x0001) >> 0)
+
+/* -------------------------- NBG1 Character Control Registers -------------------------- */
+
+/* N1CHCNx - NBG1 (or EXB1) Colour Depth
+ 00 - 16 Colours
+ 01 - 256 Colours
+ 10 - 2048 Colours
+ 11 - 32768 Colours (RGB5) */
+ #define STV_VDP2_N1CHCN ((STV_VDP2_CHCTLA & 0x3000) >> 12)
+
+/* N1BMSZx - NBG1 Bitmap Size *guessed*
+ 00 - 512 x 256
+ 01 - 512 x 512
+ 10 - 1024 x 256
+ 11 - 1024 x 512 */
+ #define STV_VDP2_N1BMSZ ((STV_VDP2_CHCTLA & 0x0c00) >> 10)
+
+/* N1BMEN - NBG1 Bitmap Enable
+ 0 - use cell mode
+ 1 - use bitmap mode */
+ #define STV_VDP2_N1BMEN ((STV_VDP2_CHCTLA & 0x0200) >> 9)
+
+/* N1CHSZ - NBG1 Character (Tile) Size
+ 0 - 1 cell x 1 cell (8x8)
+ 1 - 2 cells x 2 cells (16x16) */
+ #define STV_VDP2_N1CHSZ ((STV_VDP2_CHCTLA & 0x0100) >> 8)
+
+/*
+18002A - CHCTLB - Character Control (NBG2, NBG1, RBG0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | R0CHCN2 | R0CHCN1 | R0CHCN0 | -- | R0BMSZ | R0BMEN | R0CHSZ |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | N3CHCN | N3CHSZ | -- | -- | N2CHCN | N2CHSZ |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CHCTLB (m_vdp2_regs[0x02a/2])
+
+/* -------------------------- RBG0 Character Control Registers -------------------------- */
+
+
+/* R0CHCNx RBG0 Colour Depth
+ 000 - 16 Colours
+ 001 - 256 Colours
+ 010 - 2048 Colours
+ 011 - 32768 Colours (RGB5)
+ 100 - 16770000 Colours (RGB8)
+ 101 - invalid
+ 110 - invalid
+ 111 - invalid */
+ #define STV_VDP2_R0CHCN ((STV_VDP2_CHCTLB & 0x7000) >> 12)
+
+/* R0BMSZx - RBG0 Bitmap Size *guessed*
+ 00 - 512 x 256
+ 01 - 512 x 512 */
+ #define STV_VDP2_R0BMSZ ((STV_VDP2_CHCTLB & 0x0400) >> 10)
+
+/* R0BMEN - RBG0 Bitmap Enable
+ 0 - use cell mode
+ 1 - use bitmap mode */
+ #define STV_VDP2_R0BMEN ((STV_VDP2_CHCTLB & 0x0200) >> 9)
+
+/* R0CHSZ - RBG0 Character (Tile) Size
+ 0 - 1 cell x 1 cell (8x8)
+ 1 - 2 cells x 2 cells (16x16) */
+ #define STV_VDP2_R0CHSZ ((STV_VDP2_CHCTLB & 0x0100) >> 8)
+
+ #define STV_VDP2_N3CHCN ((STV_VDP2_CHCTLB & 0x0020) >> 5)
+ #define STV_VDP2_N3CHSZ ((STV_VDP2_CHCTLB & 0x0010) >> 4)
+ #define STV_VDP2_N2CHCN ((STV_VDP2_CHCTLB & 0x0002) >> 1)
+ #define STV_VDP2_N2CHSZ ((STV_VDP2_CHCTLB & 0x0001) >> 0)
+
+
+/*
+18002C - BMPNA - Bitmap Palette Number (NBG0, NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_BMPNA (m_vdp2_regs[0x02c/2])
+
+ #define STV_VDP2_N1BMP ((STV_VDP2_BMPNA & 0x0700) >> 8)
+ #define STV_VDP2_N0BMP ((STV_VDP2_BMPNA & 0x0007) >> 0)
+
+/* 18002E - Bitmap Palette Number (RBG0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_BMPNB (m_vdp2_regs[0x02e/2])
+
+ #define STV_VDP2_R0BMP ((STV_VDP2_BMPNB & 0x0007) >> 0)
+
+/* 180030 - PNCN0 - Pattern Name Control (NBG0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | N0PNB | N0CNSM | -- | -- | -- | -- | N0SPR | N0SCC |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | N0SPLT6 | N0SPLT5 | N0SPLT4 | N0SPCN4 | N0SPCN3 | N0SPCN2 | N0SPCN1 | N0SPCN0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PNCN0 (m_vdp2_regs[0x030/2])
+
+/* Pattern Data Size
+ 0 = 2 bytes
+ 1 = 1 byte */
+ #define STV_VDP2_N0PNB ((STV_VDP2_PNCN0 & 0x8000) >> 15)
+
+/* Character Number Supplement (in 1 byte mode)
+ 0 = Character Number = 10bits + 2bits for flip
+ 1 = Character Number = 12 bits, no flip */
+ #define STV_VDP2_N0CNSM ((STV_VDP2_PNCN0 & 0x4000) >> 14)
+
+/* NBG0 Special Priority Register (in 1 byte mode) */
+ #define STV_VDP2_N0SPR ((STV_VDP2_PNCN0 & 0x0200) >> 9)
+
+/* NBG0 Special Colour Control Register (in 1 byte mode) */
+ #define STV_VDP2_N0SCC ((STV_VDP2_PNCN0 & 0x0100) >> 8)
+
+/* Supplementary Palette Bits (in 1 byte mode) */
+ #define STV_VDP2_N0SPLT ((STV_VDP2_PNCN0 & 0x00e0) >> 5)
+
+/* Supplementary Character Bits (in 1 byte mode) */
+ #define STV_VDP2_N0SPCN ((STV_VDP2_PNCN0 & 0x001f) >> 0)
+
+/* 180032 - Pattern Name Control (NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PNCN1 (m_vdp2_regs[0x032/2])
+
+/* Pattern Data Size
+ 0 = 2 bytes
+ 1 = 1 byte */
+ #define STV_VDP2_N1PNB ((STV_VDP2_PNCN1 & 0x8000) >> 15)
+
+/* Character Number Supplement (in 1 byte mode)
+ 0 = Character Number = 10bits + 2bits for flip
+ 1 = Character Number = 12 bits, no flip */
+ #define STV_VDP2_N1CNSM ((STV_VDP2_PNCN1 & 0x4000) >> 14)
+
+/* NBG0 Special Priority Register (in 1 byte mode) */
+ #define STV_VDP2_N1SPR ((STV_VDP2_PNCN1 & 0x0200) >> 9)
+
+/* NBG0 Special Colour Control Register (in 1 byte mode) */
+ #define STV_VDP2_N1SCC ((STV_VDP2_PNCN1 & 0x0100) >> 8)
+
+/* Supplementary Palette Bits (in 1 byte mode) */
+ #define STV_VDP2_N1SPLT ((STV_VDP2_PNCN1 & 0x00e0) >> 5)
+
+/* Supplementary Character Bits (in 1 byte mode) */
+ #define STV_VDP2_N1SPCN ((STV_VDP2_PNCN1 & 0x001f) >> 0)
+
+
+/* 180034 - Pattern Name Control (NBG2)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PNCN2 (m_vdp2_regs[0x034/2])
+
+/* Pattern Data Size
+ 0 = 2 bytes
+ 1 = 1 byte */
+ #define STV_VDP2_N2PNB ((STV_VDP2_PNCN2 & 0x8000) >> 15)
+
+/* Character Number Supplement (in 1 byte mode)
+ 0 = Character Number = 10bits + 2bits for flip
+ 1 = Character Number = 12 bits, no flip */
+ #define STV_VDP2_N2CNSM ((STV_VDP2_PNCN2 & 0x4000) >> 14)
+
+/* NBG0 Special Priority Register (in 1 byte mode) */
+ #define STV_VDP2_N2SPR ((STV_VDP2_PNCN2 & 0x0200) >> 9)
+
+/* NBG0 Special Colour Control Register (in 1 byte mode) */
+ #define STV_VDP2_N2SCC ((STV_VDP2_PNCN2 & 0x0100) >> 8)
+
+/* Supplementary Palette Bits (in 1 byte mode) */
+ #define STV_VDP2_N2SPLT ((STV_VDP2_PNCN2 & 0x00e0) >> 5)
+
+/* Supplementary Character Bits (in 1 byte mode) */
+ #define STV_VDP2_N2SPCN ((STV_VDP2_PNCN2 & 0x001f) >> 0)
+
+
+/* 180036 - Pattern Name Control (NBG3)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | N3PNB | N3CNSM | -- | -- | -- | -- | N3SPR | N3SCC |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | N3SPLT6 | N3SPLT5 | N3SPLT4 | N3SPCN4 | N3SPCN3 | N3SPCN2 | N3SPCN1 | N3SPCN0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PNCN3 (m_vdp2_regs[0x036/2])
+
+/* Pattern Data Size
+ 0 = 2 bytes
+ 1 = 1 byte */
+ #define STV_VDP2_N3PNB ((STV_VDP2_PNCN3 & 0x8000) >> 15)
+
+/* Character Number Supplement (in 1 byte mode)
+ 0 = Character Number = 10bits + 2bits for flip
+ 1 = Character Number = 12 bits, no flip */
+ #define STV_VDP2_N3CNSM ((STV_VDP2_PNCN3 & 0x4000) >> 14)
+
+/* NBG0 Special Priority Register (in 1 byte mode) */
+ #define STV_VDP2_N3SPR ((STV_VDP2_PNCN3 & 0x0200) >> 9)
+
+/* NBG0 Special Colour Control Register (in 1 byte mode) */
+ #define STV_VDP2_N3SCC ((STV_VDP2_PNCN3 & 0x0100) >> 8)
+
+/* Supplementary Palette Bits (in 1 byte mode) */
+ #define STV_VDP2_N3SPLT ((STV_VDP2_PNCN3 & 0x00e0) >> 5)
+
+/* Supplementary Character Bits (in 1 byte mode) */
+ #define STV_VDP2_N3SPCN ((STV_VDP2_PNCN3 & 0x001f) >> 0)
+
+
+/* 180038 - Pattern Name Control (RBG0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PNCR (m_vdp2_regs[0x038/2])
+
+/* Pattern Data Size
+ 0 = 2 bytes
+ 1 = 1 byte */
+ #define STV_VDP2_R0PNB ((STV_VDP2_PNCR & 0x8000) >> 15)
+
+/* Character Number Supplement (in 1 byte mode)
+ 0 = Character Number = 10bits + 2bits for flip
+ 1 = Character Number = 12 bits, no flip */
+ #define STV_VDP2_R0CNSM ((STV_VDP2_PNCR & 0x4000) >> 14)
+
+/* NBG0 Special Priority Register (in 1 byte mode) */
+ #define STV_VDP2_R0SPR ((STV_VDP2_PNCR & 0x0200) >> 9)
+
+/* NBG0 Special Colour Control Register (in 1 byte mode) */
+ #define STV_VDP2_R0SCC ((STV_VDP2_PNCR & 0x0100) >> 8)
+
+/* Supplementary Palette Bits (in 1 byte mode) */
+ #define STV_VDP2_R0SPLT ((STV_VDP2_PNCR & 0x00e0) >> 5)
+
+/* Supplementary Character Bits (in 1 byte mode) */
+ #define STV_VDP2_R0SPCN ((STV_VDP2_PNCR & 0x001f) >> 0)
+
+/* 18003A - PLSZ - Plane Size
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | N3PLSZ1 | N3PLSZ0 | -- | -- | N1PLSZ1 | N1PLSZ0 | N0PLSZ1 | N0PLSZ0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PLSZ (m_vdp2_regs[0x03a/2])
+
+ /* NBG0 Plane Size
+ 00 1H Page x 1V Page
+ 01 2H Pages x 1V Page
+ 10 invalid
+ 11 2H Pages x 2V Pages */
+ #define STV_VDP2_RBOVR ((STV_VDP2_PLSZ & 0xc000) >> 14)
+ #define STV_VDP2_RBPLSZ ((STV_VDP2_PLSZ & 0x3000) >> 12)
+ #define STV_VDP2_RAOVR ((STV_VDP2_PLSZ & 0x0c00) >> 10)
+ #define STV_VDP2_RAPLSZ ((STV_VDP2_PLSZ & 0x0300) >> 8)
+ #define STV_VDP2_N3PLSZ ((STV_VDP2_PLSZ & 0x00c0) >> 6)
+ #define STV_VDP2_N2PLSZ ((STV_VDP2_PLSZ & 0x0030) >> 4)
+ #define STV_VDP2_N1PLSZ ((STV_VDP2_PLSZ & 0x000c) >> 2)
+ #define STV_VDP2_N0PLSZ ((STV_VDP2_PLSZ & 0x0003) >> 0)
+
+/* 18003C - MPOFN - Map Offset (NBG0, NBG1, NBG2, NBG3)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | N3MP8 | N3MP7 | N3MP6 | -- | N2MP8 | N2MP7 | N2MP6 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | N1MP8 | N1MP7 | N1MP6 | -- | N0MP8 | N0MP7 | N0MP6 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPOFN_ (m_vdp2_regs[0x03c/2])
+
+ /* Higher 3 bits of the map offset for each layer */
+ #define STV_VDP2_N3MP_ ((STV_VDP2_MPOFN_ & 0x3000) >> 12)
+ #define STV_VDP2_N2MP_ ((STV_VDP2_MPOFN_ & 0x0300) >> 8)
+ #define STV_VDP2_N1MP_ ((STV_VDP2_MPOFN_ & 0x0030) >> 4)
+ #define STV_VDP2_N0MP_ ((STV_VDP2_MPOFN_ & 0x0003) >> 0)
+
+
+
+
+/* 18003E - Map Offset (Rotation Parameter A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPOFR_ (m_vdp2_regs[0x03e/2])
+
+ #define STV_VDP2_RBMP_ ((STV_VDP2_MPOFR_ & 0x0030) >> 4)
+ #define STV_VDP2_RAMP_ ((STV_VDP2_MPOFR_ & 0x0003) >> 0)
+
+/* 180040 - MPABN0 - Map (NBG0, Plane A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | N0MPB5 | N0MPB4 | N0MPB3 | N0MPB2 | N0MPB1 | N0MPB0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | N0MPA5 | N0MPA4 | N0MPA3 | N0MPA2 | N0MPA1 | N0MPA0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPABN0 (m_vdp2_regs[0x040/2])
+
+ /* N0MPB5 = lower 6 bits of Map Address of Plane B of Tilemap NBG0 */
+ #define STV_VDP2_N0MPB ((STV_VDP2_MPABN0 & 0x3f00) >> 8)
+
+ /* N0MPA5 = lower 6 bits of Map Address of Plane A of Tilemap NBG0 */
+ #define STV_VDP2_N0MPA ((STV_VDP2_MPABN0 & 0x003f) >> 0)
+
+
+/* 180042 - MPCDN0 - (NBG0, Plane C,D)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | N0MPD5 | N0MPD4 | N0MPD3 | N0MPD2 | N0MPD1 | N0MPD0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | N0MPC5 | N0MPC4 | N0MPC3 | N0MPC2 | N0MPC1 | N0MPC0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPCDN0 (m_vdp2_regs[0x042/2])
+
+ /* N0MPB5 = lower 6 bits of Map Address of Plane D of Tilemap NBG0 */
+ #define STV_VDP2_N0MPD ((STV_VDP2_MPCDN0 & 0x3f00) >> 8)
+
+ /* N0MPA5 = lower 6 bits of Map Address of Plane C of Tilemap NBG0 */
+ #define STV_VDP2_N0MPC ((STV_VDP2_MPCDN0 & 0x003f) >> 0)
+
+
+/* 180044 - Map (NBG1, Plane A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPABN1 (m_vdp2_regs[0x044/2])
+
+ /* N0MPB5 = lower 6 bits of Map Address of Plane B of Tilemap NBG1 */
+ #define STV_VDP2_N1MPB ((STV_VDP2_MPABN1 & 0x3f00) >> 8)
+
+ /* N0MPA5 = lower 6 bits of Map Address of Plane A of Tilemap NBG1 */
+ #define STV_VDP2_N1MPA ((STV_VDP2_MPABN1 & 0x003f) >> 0)
+
+/* 180046 - Map (NBG1, Plane C,D)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPCDN1 (m_vdp2_regs[0x046/2])
+
+ /* N0MPB5 = lower 6 bits of Map Address of Plane D of Tilemap NBG0 */
+ #define STV_VDP2_N1MPD ((STV_VDP2_MPCDN1 & 0x3f00) >> 8)
+
+ /* N0MPA5 = lower 6 bits of Map Address of Plane C of Tilemap NBG0 */
+ #define STV_VDP2_N1MPC ((STV_VDP2_MPCDN1 & 0x003f) >> 0)
+
+
+/* 180048 - Map (NBG2, Plane A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPABN2 (m_vdp2_regs[0x048/2])
+
+ /* N0MPB5 = lower 6 bits of Map Address of Plane B of Tilemap NBG2 */
+ #define STV_VDP2_N2MPB ((STV_VDP2_MPABN2 & 0x3f00) >> 8)
+
+ /* N0MPA5 = lower 6 bits of Map Address of Plane A of Tilemap NBG2 */
+ #define STV_VDP2_N2MPA ((STV_VDP2_MPABN2 & 0x003f) >> 0)
+
+/* 18004a - Map (NBG2, Plane C,D)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPCDN2 (m_vdp2_regs[0x04a/2])
+
+ /* N0MPB5 = lower 6 bits of Map Address of Plane D of Tilemap NBG2 */
+ #define STV_VDP2_N2MPD ((STV_VDP2_MPCDN2 & 0x3f00) >> 8)
+
+ /* N0MPA5 = lower 6 bits of Map Address of Plane C of Tilemap NBG2 */
+ #define STV_VDP2_N2MPC ((STV_VDP2_MPCDN2 & 0x003f) >> 0)
+
+/* 18004c - Map (NBG3, Plane A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPABN3 (m_vdp2_regs[0x04c/2])
+
+ /* N0MPB5 = lower 6 bits of Map Address of Plane B of Tilemap NBG1 */
+ #define STV_VDP2_N3MPB ((STV_VDP2_MPABN3 & 0x3f00) >> 8)
+
+ /* N0MPA5 = lower 6 bits of Map Address of Plane A of Tilemap NBG1 */
+ #define STV_VDP2_N3MPA ((STV_VDP2_MPABN3 & 0x003f) >> 0)
+
+
+/* 18004e - Map (NBG3, Plane C,D)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPCDN3 (m_vdp2_regs[0x04e/2])
+
+ /* N0MPB5 = lower 6 bits of Map Address of Plane B of Tilemap NBG0 */
+ #define STV_VDP2_N3MPD ((STV_VDP2_MPCDN3 & 0x3f00) >> 8)
+
+ /* N0MPA5 = lower 6 bits of Map Address of Plane A of Tilemap NBG0 */
+ #define STV_VDP2_N3MPC ((STV_VDP2_MPCDN3 & 0x003f) >> 0)
+
+/* 180050 - Map (Rotation Parameter A, Plane A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPABRA (m_vdp2_regs[0x050/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane B of Tilemap RBG0 */
+ #define STV_VDP2_RAMPB ((STV_VDP2_MPABRA & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane A of Tilemap RBG0 */
+ #define STV_VDP2_RAMPA ((STV_VDP2_MPABRA & 0x003f) >> 0)
+
+
+
+/* 180052 - Map (Rotation Parameter A, Plane C,D)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_MPCDRA (m_vdp2_regs[0x052/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane D of Tilemap RBG0 */
+ #define STV_VDP2_RAMPD ((STV_VDP2_MPCDRA & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane C of Tilemap RBG0 */
+ #define STV_VDP2_RAMPC ((STV_VDP2_MPCDRA & 0x003f) >> 0)
+
+/* 180054 - Map (Rotation Parameter A, Plane E,F)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_MPEFRA (m_vdp2_regs[0x054/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane F of Tilemap RBG0 */
+ #define STV_VDP2_RAMPF ((STV_VDP2_MPEFRA & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane E of Tilemap RBG0 */
+ #define STV_VDP2_RAMPE ((STV_VDP2_MPEFRA & 0x003f) >> 0)
+
+/* 180056 - Map (Rotation Parameter A, Plane G,H)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_MPGHRA (m_vdp2_regs[0x056/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane H of Tilemap RBG0 */
+ #define STV_VDP2_RAMPH ((STV_VDP2_MPGHRA & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane G of Tilemap RBG0 */
+ #define STV_VDP2_RAMPG ((STV_VDP2_MPGHRA & 0x003f) >> 0)
+
+/* 180058 - Map (Rotation Parameter A, Plane I,J)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_MPIJRA (m_vdp2_regs[0x058/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane J of Tilemap RBG0 */
+ #define STV_VDP2_RAMPJ ((STV_VDP2_MPIJRA & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane I of Tilemap RBG0 */
+ #define STV_VDP2_RAMPI ((STV_VDP2_MPIJRA & 0x003f) >> 0)
+
+/* 18005a - Map (Rotation Parameter A, Plane K,L)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_MPKLRA (m_vdp2_regs[0x05a/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane L of Tilemap RBG0 */
+ #define STV_VDP2_RAMPL ((STV_VDP2_MPKLRA & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane K of Tilemap RBG0 */
+ #define STV_VDP2_RAMPK ((STV_VDP2_MPKLRA & 0x003f) >> 0)
+
+/* 18005c - Map (Rotation Parameter A, Plane M,N)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_MPMNRA (m_vdp2_regs[0x05c/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane N of Tilemap RBG0 */
+ #define STV_VDP2_RAMPN ((STV_VDP2_MPMNRA & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane M of Tilemap RBG0 */
+ #define STV_VDP2_RAMPM ((STV_VDP2_MPMNRA & 0x003f) >> 0)
+
+/* 18005e - Map (Rotation Parameter A, Plane O,P)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_MPOPRA (m_vdp2_regs[0x05e/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane P of Tilemap RBG0 */
+ #define STV_VDP2_RAMPP ((STV_VDP2_MPOPRA & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane O of Tilemap RBG0 */
+ #define STV_VDP2_RAMPO ((STV_VDP2_MPOPRA & 0x003f) >> 0)
+
+/* 180060 - Map (Rotation Parameter B, Plane A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPABRB (m_vdp2_regs[0x060/2])
+
+ /* R0MPB5 = lower 6 bits of Map Address of Plane B of Tilemap RBG0 */
+ #define STV_VDP2_RBMPB ((STV_VDP2_MPABRB & 0x3f00) >> 8)
+
+ /* R0MPA5 = lower 6 bits of Map Address of Plane A of Tilemap RBG0 */
+ #define STV_VDP2_RBMPA ((STV_VDP2_MPABRB & 0x003f) >> 0)
+
+
+/* 180062 - Map (Rotation Parameter B, Plane C,D)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPCDRB (m_vdp2_regs[0x062/2])
+
+ /* R0MPD5 = lower 6 bits of Map Address of Plane D of Tilemap RBG0 */
+ #define STV_VDP2_RBMPD ((STV_VDP2_MPCDRB & 0x3f00) >> 8)
+
+ /* R0MPc5 = lower 6 bits of Map Address of Plane C of Tilemap RBG0 */
+ #define STV_VDP2_RBMPC ((STV_VDP2_MPCDRB & 0x003f) >> 0)
+
+/* 180064 - Map (Rotation Parameter B, Plane E,F)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPEFRB (m_vdp2_regs[0x064/2])
+
+ /* R0MPF5 = lower 6 bits of Map Address of Plane F of Tilemap RBG0 */
+ #define STV_VDP2_RBMPF ((STV_VDP2_MPEFRB & 0x3f00) >> 8)
+
+ /* R0MPE5 = lower 6 bits of Map Address of Plane E of Tilemap RBG0 */
+ #define STV_VDP2_RBMPE ((STV_VDP2_MPEFRB & 0x003f) >> 0)
+
+/* 180066 - Map (Rotation Parameter B, Plane G,H)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPGHRB (m_vdp2_regs[0x066/2])
+
+ /* R0MPH5 = lower 6 bits of Map Address of Plane H of Tilemap RBG0 */
+ #define STV_VDP2_RBMPH ((STV_VDP2_MPGHRB & 0x3f00) >> 8)
+
+ /* R0MPG5 = lower 6 bits of Map Address of Plane G of Tilemap RBG0 */
+ #define STV_VDP2_RBMPG ((STV_VDP2_MPGHRB & 0x003f) >> 0)
+
+/* 180068 - Map (Rotation Parameter B, Plane I,J)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPIJRB (m_vdp2_regs[0x068/2])
+
+ /* R0MPJ5 = lower 6 bits of Map Address of Plane J of Tilemap RBG0 */
+ #define STV_VDP2_RBMPJ ((STV_VDP2_MPIJRB & 0x3f00) >> 8)
+
+ /* R0MPI5 = lower 6 bits of Map Address of Plane E of Tilemap RBG0 */
+ #define STV_VDP2_RBMPI ((STV_VDP2_MPIJRB & 0x003f) >> 0)
+
+/* 18006a - Map (Rotation Parameter B, Plane K,L)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPKLRB (m_vdp2_regs[0x06a/2])
+
+ /* R0MPL5 = lower 6 bits of Map Address of Plane L of Tilemap RBG0 */
+ #define STV_VDP2_RBMPL ((STV_VDP2_MPKLRB & 0x3f00) >> 8)
+
+ /* R0MPK5 = lower 6 bits of Map Address of Plane K of Tilemap RBG0 */
+ #define STV_VDP2_RBMPK ((STV_VDP2_MPKLRB & 0x003f) >> 0)
+
+/* 18006c - Map (Rotation Parameter B, Plane M,N)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPMNRB (m_vdp2_regs[0x06c/2])
+
+ /* R0MPN5 = lower 6 bits of Map Address of Plane N of Tilemap RBG0 */
+ #define STV_VDP2_RBMPN ((STV_VDP2_MPMNRB & 0x3f00) >> 8)
+
+ /* R0MPM5 = lower 6 bits of Map Address of Plane M of Tilemap RBG0 */
+ #define STV_VDP2_RBMPM ((STV_VDP2_MPMNRB & 0x003f) >> 0)
+
+/* 18006e - Map (Rotation Parameter B, Plane O,P)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_MPOPRB (m_vdp2_regs[0x06e/2])
+
+ /* R0MPP5 = lower 6 bits of Map Address of Plane P of Tilemap RBG0 */
+ #define STV_VDP2_RBMPP ((STV_VDP2_MPOPRB & 0x3f00) >> 8)
+
+ /* R0MPO5 = lower 6 bits of Map Address of Plane O of Tilemap RBG0 */
+ #define STV_VDP2_RBMPO ((STV_VDP2_MPOPRB & 0x003f) >> 0)
+
+/* 180070 - SCXIN0 - Screen Scroll (NBG0, Horizontal Integer Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCXIN0 (m_vdp2_regs[0x070/2])
+
+
+/* 180072 - Screen Scroll (NBG0, Horizontal Fractional Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCXDN0 (m_vdp2_regs[0x072/2])
+
+/* 180074 - SCYIN0 - Screen Scroll (NBG0, Vertical Integer Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_SCYIN0 (m_vdp2_regs[0x074/2])
+
+
+/* 180076 - Screen Scroll (NBG0, Vertical Fractional Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCYDN0 (m_vdp2_regs[0x076/2])
+
+/* 180078 - Coordinate Inc (NBG0, Horizontal Integer Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMXIN0 (m_vdp2_regs[0x078/2])
+
+ #define STV_VDP2_N0ZMXI ((STV_VDP2_ZMXIN0 & 0x0007) >> 0)
+
+/* 18007a - Coordinate Inc (NBG0, Horizontal Fractional Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMXDN0 (m_vdp2_regs[0x07a/2])
+
+ #define STV_VDP2_N0ZMXD ((STV_VDP2_ZMXDN0 >> 8)& 0xff)
+ #define STV_VDP2_ZMXN0 (((STV_VDP2_N0ZMXI<<16) | (STV_VDP2_N0ZMXD<<8)) & 0x0007ff00)
+
+
+/* 18007c - Coordinate Inc (NBG0, Vertical Integer Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMYIN0 (m_vdp2_regs[0x07c/2])
+
+ #define STV_VDP2_N0ZMYI ((STV_VDP2_ZMYIN0 & 0x0007) >> 0)
+
+/* 18007e - Coordinate Inc (NBG0, Vertical Fractional Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMYDN0 (m_vdp2_regs[0x07e/2])
+
+ #define STV_VDP2_N0ZMYD ((STV_VDP2_ZMYDN0 >> 8)& 0xff)
+ #define STV_VDP2_ZMYN0 (((STV_VDP2_N0ZMYI<<16) | (STV_VDP2_N0ZMYD<<8)) & 0x0007ff00)
+
+/* 180080 - SCXIN1 - Screen Scroll (NBG1, Horizontal Integer Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCXIN1 (m_vdp2_regs[0x080/2])
+
+/* 180082 - Screen Scroll (NBG1, Horizontal Fractional Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCXDN1 (m_vdp2_regs[0x082/2])
+
+/* 180084 - SCYIN1 - Screen Scroll (NBG1, Vertical Integer Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCYIN1 (m_vdp2_regs[0x084/2])
+
+/* 180086 - Screen Scroll (NBG1, Vertical Fractional Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCYDN1 (m_vdp2_regs[0x086/2])
+
+/* 180088 - Coordinate Inc (NBG1, Horizontal Integer Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMXIN1 (m_vdp2_regs[0x088/2])
+
+ #define STV_VDP2_N1ZMXI ((STV_VDP2_ZMXIN1 & 0x0007) >> 0)
+
+/* 18008a - Coordinate Inc (NBG1, Horizontal Fractional Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMXDN1 (m_vdp2_regs[0x08a/2])
+
+ #define STV_VDP2_N1ZMXD ((STV_VDP2_ZMXDN1 >> 8)& 0xff)
+ #define STV_VDP2_ZMXN1 (((STV_VDP2_N1ZMXI<<16) | (STV_VDP2_N1ZMXD<<8)) & 0x0007ff00)
+
+/* 18008c - Coordinate Inc (NBG1, Vertical Integer Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMYIN1 (m_vdp2_regs[0x08c/2])
+
+ #define STV_VDP2_N1ZMYI ((STV_VDP2_ZMYIN1 & 0x0007) >> 0)
+
+/* 18008e - Coordinate Inc (NBG1, Vertical Fractional Part)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMYDN1 (m_vdp2_regs[0x08e/2])
+
+ #define STV_VDP2_N1ZMYD ((STV_VDP2_ZMYDN1 >> 8)& 0xff)
+ #define STV_VDP2_ZMYN1 (((STV_VDP2_N1ZMYI<<16) | (STV_VDP2_N1ZMYD<<8)) & 0x007ff00)
+
+/* 180090 - SCXN2 - Screen Scroll (NBG2, Horizontal)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCXN2 (m_vdp2_regs[0x090/2])
+
+/* 180092 - SCYN2 - Screen Scroll (NBG2, Vertical)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCYN2 (m_vdp2_regs[0x092/2])
+
+/* 180094 - SCXN3 - Screen Scroll (NBG3, Horizontal)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCXN3 (m_vdp2_regs[0x094/2])
+
+/* 180096 - SCYN3 - Screen Scroll (NBG3, Vertical)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCYN3 (m_vdp2_regs[0x096/2])
+
+/* 180098 - Reduction Enable
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | N1ZMQT | N1ZMHF |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | N0ZMQT | N0ZMHF |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_ZMCTL (m_vdp2_regs[0x098/2])
+
+ #define STV_VDP2_N1ZMQT ((STV_VDP2_ZMCTL & 0x0200) >> 9)
+ #define STV_VDP2_N1ZMHF ((STV_VDP2_ZMCTL & 0x0100) >> 8)
+ #define STV_VDP2_N0ZMQT ((STV_VDP2_ZMCTL & 0x0002) >> 1)
+ #define STV_VDP2_N0ZMHF ((STV_VDP2_ZMCTL & 0x0001) >> 0)
+
+/* 18009a - Line and Vertical Cell Scroll Control (NBG0, NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SCRCTL (m_vdp2_regs[0x09a/2])
+
+ #define STV_VDP2_N1LSS ((STV_VDP2_SCRCTL & 0x3000) >> 12)
+ #define STV_VDP2_N1LZMX ((STV_VDP2_SCRCTL & 0x0800) >> 11)
+ #define STV_VDP2_N1LSCY ((STV_VDP2_SCRCTL & 0x0400) >> 10)
+ #define STV_VDP2_N1LSCX ((STV_VDP2_SCRCTL & 0x0200) >> 9)
+ #define STV_VDP2_N1VCSC ((STV_VDP2_SCRCTL & 0x0100) >> 8)
+ #define STV_VDP2_N0LSS ((STV_VDP2_SCRCTL & 0x0030) >> 4)
+ #define STV_VDP2_N0LZMX ((STV_VDP2_SCRCTL & 0x0008) >> 3)
+ #define STV_VDP2_N0LSCY ((STV_VDP2_SCRCTL & 0x0004) >> 2)
+ #define STV_VDP2_N0LSCX ((STV_VDP2_SCRCTL & 0x0002) >> 1)
+ #define STV_VDP2_N0VCSC ((STV_VDP2_SCRCTL & 0x0001) >> 0)
+
+/* 18009c - Vertical Cell Table Address (NBG0, NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_VCSTAU (m_vdp2_regs[0x09c/2] & 7)
+
+
+/* 18009e - Vertical Cell Table Address (NBG0, NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_VCSTAL (m_vdp2_regs[0x09e/2])
+
+
+/* 1800a0 - LSTA0U - Line Scroll Table Address (NBG0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ /*bit 2 unused when VRAM = 4 Mbits*/
+ #define STV_VDP2_LSTA0U (m_vdp2_regs[0x0a0/2] & 7)
+
+/* 1800a2 - LSTA0L - Line Scroll Table Address (NBG0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_LSTA0L (m_vdp2_regs[0x0a2/2])
+
+/* 1800a4 - LSTA1U - Line Scroll Table Address (NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ /*bit 2 unused when VRAM = 4 Mbits*/
+ #define STV_VDP2_LSTA1U (m_vdp2_regs[0x0a4/2] & 7)
+
+/* 1800a6 - LSTA1L - Line Scroll Table Address (NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_LSTA1L (m_vdp2_regs[0x0a6/2])
+
+/* 1800a8 - LCTAU - Line Colour Screen Table Address
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_LCTAU (m_vdp2_regs[0x0a8/2])
+ #define STV_VDP2_LCCLMD ((STV_VDP2_LCTAU & 0x8000) >> 15)
+
+/* 1800aa - LCTAL - Line Colour Screen Table Address
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_LCTAL (m_vdp2_regs[0x0aa/2])
+
+ #define STV_VDP2_LCTA (((STV_VDP2_LCTAU & 0x0007) << 16) | (STV_VDP2_LCTAL & 0xffff))
+
+/* 1800ac - Back Screen Table Address
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | BKCLMD | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | BKTA18 | BKTA17 | BKTA16 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_BKTAU (m_vdp2_regs[0x0ac/2])
+
+ #define STV_VDP2_BKCLMD ((STV_VDP2_BKTAU & 0x8000) >> 15)
+
+
+/* 1800ae - Back Screen Table Address
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | BKTA15 | BKTA14 | BKTA13 | BKTA12 | BKTA11 | BKTA10 | BKTA9 | BKTA8 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | BKTA7 | BKTA7 | BKTA6 | BKTA5 | BKTA4 | BKTA3 | BKTA2 | BKTA0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_BKTAL (m_vdp2_regs[0x0ae/2])
+
+ #define STV_VDP2_BKTA (((STV_VDP2_BKTAU & 0x0007) << 16) | (STV_VDP2_BKTAL & 0xffff))
+
+/* 1800b0 - RPMD - Rotation Parameter Mode
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_RPMD ((m_vdp2_regs[0x0b0/2]) & 0x0003)
+
+/* 1800b2 - RPRCTL - Rotation Parameter Read Control
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | RBKASTRE | RBYSTRE | RBXSTRE |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | RAKASTRE | RAYSTRE | RBXSTRE |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_RPRCTL (m_vdp2_regs[0x0b2/2])
+ #define STV_VDP2_RBKASTRE ((STV_VDP2_RPRCTL & 0x0400) >> 10)
+ #define STV_VDP2_RBYSTRE ((STV_VDP2_RPRCTL & 0x0200) >> 9)
+ #define STV_VDP2_RBXSTRE ((STV_VDP2_RPRCTL & 0x0100) >> 8)
+ #define STV_VDP2_RAKASTRE ((STV_VDP2_RPRCTL & 0x0004) >> 2)
+ #define STV_VDP2_RAYSTRE ((STV_VDP2_RPRCTL & 0x0002) >> 1)
+ #define STV_VDP2_RAXSTRE ((STV_VDP2_RPRCTL & 0x0001) >> 0)
+
+/* 1800b4 - KTCTL - Coefficient Table Control
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | RBKLCE | RBKMD1 | RBKMD0 | RBKDBS | RBKTE |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | RAKLCE | RAKMD1 | RAKMD0 | RAKDBS | RAKTE |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_KTCTL (m_vdp2_regs[0x0b4/2])
+ #define STV_VDP2_RBKLCE ((STV_VDP2_KTCTL & 0x1000) >> 12)
+ #define STV_VDP2_RBKMD ((STV_VDP2_KTCTL & 0x0c00) >> 10)
+ #define STV_VDP2_RBKDBS ((STV_VDP2_KTCTL & 0x0200) >> 9)
+ #define STV_VDP2_RBKTE ((STV_VDP2_KTCTL & 0x0100) >> 8)
+ #define STV_VDP2_RAKLCE ((STV_VDP2_KTCTL & 0x0010) >> 4)
+ #define STV_VDP2_RAKMD ((STV_VDP2_KTCTL & 0x000c) >> 2)
+ #define STV_VDP2_RAKDBS ((STV_VDP2_KTCTL & 0x0002) >> 1)
+ #define STV_VDP2_RAKTE ((STV_VDP2_KTCTL & 0x0001) >> 0)
+
+/* 1800b6 - KTAOF - Coefficient Table Address Offset (Rotation Parameter A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | RBKTAOS2 | RBKTAOS1 | RBKTAOS0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | RAKTAOS2 | RAKTAOS1 | RAKTAOS0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_KTAOF (m_vdp2_regs[0x0b6/2])
+ #define STV_VDP2_RBKTAOS ((STV_VDP2_KTAOF & 0x0700) >> 8)
+ #define STV_VDP2_RAKTAOS ((STV_VDP2_KTAOF & 0x0007) >> 0)
+
+/* 1800b8 - OVPNRA - Screen Over Pattern Name (Rotation Parameter A)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_OVPNRA (m_vdp2_regs[0x0b8/2])
+
+/* 1800ba - Screen Over Pattern Name (Rotation Parameter B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_OVPNRB (m_vdp2_regs[0x0ba/2])
+
+/* 1800bc - RPTAU - Rotation Parameter Table Address (Rotation Parameter A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | RPTA18 | RPTA17 | RPTA16 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_RPTAU (m_vdp2_regs[0x0bc/2] & 7)
+
+/* 1800be - RPTAL - Rotation Parameter Table Address (Rotation Parameter A,B)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | RPTA15 | RPTA14 | RPTA13 | RPTA12 | RPTA11 | RPTA10 | RPTA9 | RPTA8 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | RPTA7 | RPTA6 | RPTA5 | RPTA4 | RPTA3 | RPTA2 | RPTA1 | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_RPTAL (m_vdp2_regs[0x0be/2] & 0x0000ffff)
+
+/* 1800c0 - Window Position (W0, Horizontal Start Point)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WPSX0 (m_vdp2_regs[0x0c0/2])
+
+ #define STV_VDP2_W0SX ((STV_VDP2_WPSX0 & 0x03ff) >> 0)
+
+/* 1800c2 - Window Position (W0, Vertical Start Point)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WPSY0 (m_vdp2_regs[0x0c2/2])
+
+ #define STV_VDP2_W0SY ((STV_VDP2_WPSY0 & 0x03ff) >> 0)
+
+/* 1800c4 - Window Position (W0, Horizontal End Point)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WPEX0 (m_vdp2_regs[0x0c4/2])
+
+ #define STV_VDP2_W0EX ((STV_VDP2_WPEX0 & 0x03ff) >> 0)
+
+/* 1800c6 - Window Position (W0, Vertical End Point)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WPEY0 (m_vdp2_regs[0x0c6/2])
+
+ #define STV_VDP2_W0EY ((STV_VDP2_WPEY0 & 0x03ff) >> 0)
+
+/* 1800c8 - Window Position (W1, Horizontal Start Point)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WPSX1 (m_vdp2_regs[0x0c8/2])
+
+ #define STV_VDP2_W1SX ((STV_VDP2_WPSX1 & 0x03ff) >> 0)
+
+/* 1800ca - Window Position (W1, Vertical Start Point)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WPSY1 (m_vdp2_regs[0x0ca/2])
+
+ #define STV_VDP2_W1SY ((STV_VDP2_WPSY1 & 0x03ff) >> 0)
+
+/* 1800cc - Window Position (W1, Horizontal End Point)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WPEX1 (m_vdp2_regs[0x0cc/2])
+
+ #define STV_VDP2_W1EX ((STV_VDP2_WPEX1 & 0x03ff) >> 0)
+
+/* 1800ce - Window Position (W1, Vertical End Point)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WPEY1 (m_vdp2_regs[0x0ce/2])
+
+ #define STV_VDP2_W1EY ((STV_VDP2_WPEY1 & 0x03ff) >> 0)
+
+/* 1800d0 - Window Control (NBG0, NBG1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WCTLA (m_vdp2_regs[0x0d0/2])
+ #define STV_VDP2_N1LOG ((STV_VDP2_WCTLA & 0x8000) >> 15)
+ #define STV_VDP2_N1SWE ((STV_VDP2_WCTLA & 0x2000) >> 13)
+ #define STV_VDP2_N1SWA ((STV_VDP2_WCTLA & 0x1000) >> 12)
+ #define STV_VDP2_N1W1E ((STV_VDP2_WCTLA & 0x0800) >> 11)
+ #define STV_VDP2_N1W1A ((STV_VDP2_WCTLA & 0x0400) >> 10)
+ #define STV_VDP2_N1W0E ((STV_VDP2_WCTLA & 0x0200) >> 9)
+ #define STV_VDP2_N1W0A ((STV_VDP2_WCTLA & 0x0100) >> 8)
+ #define STV_VDP2_N0LOG ((STV_VDP2_WCTLA & 0x0080) >> 7)
+ #define STV_VDP2_N0SWE ((STV_VDP2_WCTLA & 0x0020) >> 5)
+ #define STV_VDP2_N0SWA ((STV_VDP2_WCTLA & 0x0010) >> 4)
+ #define STV_VDP2_N0W1E ((STV_VDP2_WCTLA & 0x0008) >> 3)
+ #define STV_VDP2_N0W1A ((STV_VDP2_WCTLA & 0x0004) >> 2)
+ #define STV_VDP2_N0W0E ((STV_VDP2_WCTLA & 0x0002) >> 1)
+ #define STV_VDP2_N0W0A ((STV_VDP2_WCTLA & 0x0001) >> 0)
+
+/* 1800d2 - Window Control (NBG2, NBG3)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WCTLB (m_vdp2_regs[0x0d2/2])
+ #define STV_VDP2_N3LOG ((STV_VDP2_WCTLB & 0x8000) >> 15)
+ #define STV_VDP2_N3SWE ((STV_VDP2_WCTLB & 0x2000) >> 13)
+ #define STV_VDP2_N3SWA ((STV_VDP2_WCTLB & 0x1000) >> 12)
+ #define STV_VDP2_N3W1E ((STV_VDP2_WCTLB & 0x0800) >> 11)
+ #define STV_VDP2_N3W1A ((STV_VDP2_WCTLB & 0x0400) >> 10)
+ #define STV_VDP2_N3W0E ((STV_VDP2_WCTLB & 0x0200) >> 9)
+ #define STV_VDP2_N3W0A ((STV_VDP2_WCTLB & 0x0100) >> 8)
+ #define STV_VDP2_N2LOG ((STV_VDP2_WCTLB & 0x0080) >> 7)
+ #define STV_VDP2_N2SWE ((STV_VDP2_WCTLB & 0x0020) >> 5)
+ #define STV_VDP2_N2SWA ((STV_VDP2_WCTLB & 0x0010) >> 4)
+ #define STV_VDP2_N2W1E ((STV_VDP2_WCTLB & 0x0008) >> 3)
+ #define STV_VDP2_N2W1A ((STV_VDP2_WCTLB & 0x0004) >> 2)
+ #define STV_VDP2_N2W0E ((STV_VDP2_WCTLB & 0x0002) >> 1)
+ #define STV_VDP2_N2W0A ((STV_VDP2_WCTLB & 0x0001) >> 0)
+
+/* 1800d4 - Window Control (RBG0, Sprite)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WCTLC (m_vdp2_regs[0x0d4/2])
+ #define STV_VDP2_SPLOG ((STV_VDP2_WCTLC & 0x8000) >> 15)
+ #define STV_VDP2_SPSWE ((STV_VDP2_WCTLC & 0x2000) >> 13)
+ #define STV_VDP2_SPSWA ((STV_VDP2_WCTLC & 0x1000) >> 12)
+ #define STV_VDP2_SPW1E ((STV_VDP2_WCTLC & 0x0800) >> 11)
+ #define STV_VDP2_SPW1A ((STV_VDP2_WCTLC & 0x0400) >> 10)
+ #define STV_VDP2_SPW0E ((STV_VDP2_WCTLC & 0x0200) >> 9)
+ #define STV_VDP2_SPW0A ((STV_VDP2_WCTLC & 0x0100) >> 8)
+ #define STV_VDP2_R0LOG ((STV_VDP2_WCTLC & 0x0080) >> 7)
+ #define STV_VDP2_R0SWE ((STV_VDP2_WCTLC & 0x0020) >> 5)
+ #define STV_VDP2_R0SWA ((STV_VDP2_WCTLC & 0x0010) >> 4)
+ #define STV_VDP2_R0W1E ((STV_VDP2_WCTLC & 0x0008) >> 3)
+ #define STV_VDP2_R0W1A ((STV_VDP2_WCTLC & 0x0004) >> 2)
+ #define STV_VDP2_R0W0E ((STV_VDP2_WCTLC & 0x0002) >> 1)
+ #define STV_VDP2_R0W0A ((STV_VDP2_WCTLC & 0x0001) >> 0)
+
+/* 1800d6 - Window Control (Parameter Window, Colour Calc. Window)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_WCTLD (m_vdp2_regs[0x0d6/2])
+ #define STV_VDP2_CCLOG ((STV_VDP2_WCTLD & 0x8000) >> 15)
+ #define STV_VDP2_CCSWE ((STV_VDP2_WCTLD & 0x2000) >> 13)
+ #define STV_VDP2_CCSWA ((STV_VDP2_WCTLD & 0x1000) >> 12)
+ #define STV_VDP2_CCW1E ((STV_VDP2_WCTLD & 0x0800) >> 11)
+ #define STV_VDP2_CCW1A ((STV_VDP2_WCTLD & 0x0400) >> 10)
+ #define STV_VDP2_CCW0E ((STV_VDP2_WCTLD & 0x0200) >> 9)
+ #define STV_VDP2_CCW0A ((STV_VDP2_WCTLD & 0x0100) >> 8)
+ #define STV_VDP2_RPLOG ((STV_VDP2_WCTLD & 0x0080) >> 7)
+ #define STV_VDP2_RPW1E ((STV_VDP2_WCTLD & 0x0008) >> 3)
+ #define STV_VDP2_RPW1A ((STV_VDP2_WCTLD & 0x0004) >> 2)
+ #define STV_VDP2_RPW0E ((STV_VDP2_WCTLD & 0x0002) >> 1)
+ #define STV_VDP2_RPW0A ((STV_VDP2_WCTLD & 0x0001) >> 0)
+
+/* 1800d8 - Line Window Table Address (W0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_LWTA0U (m_vdp2_regs[0x0d8/2])
+
+ #define STV_VDP2_W0LWE ((STV_VDP2_LWTA0U & 0x8000) >> 15)
+
+/* 1800da - Line Window Table Address (W0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_LWTA0L (m_vdp2_regs[0x0da/2])
+
+ /* bit 19 isn't used when VRAM = 4 Mbit */
+ #define STV_VDP2_W0LWTA (((STV_VDP2_LWTA0U & 0x0007) << 16) | (STV_VDP2_LWTA0L & 0xfffe))
+
+
+/* 1800dc - Line Window Table Address (W1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_LWTA1U (m_vdp2_regs[0x0dc/2])
+
+ #define STV_VDP2_W1LWE ((STV_VDP2_LWTA1U & 0x8000) >> 15)
+
+
+/* 1800de - Line Window Table Address (W1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_LWTA1L (m_vdp2_regs[0x0de/2])
+
+ /* bit 19 isn't used when VRAM = 4 Mbit */
+ #define STV_VDP2_W1LWTA (((STV_VDP2_LWTA1U & 0x0007) << 16) | (STV_VDP2_LWTA1L & 0xfffe))
+
+
+/* 1800e0 - Sprite Control
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | SPCCCS1 | SPCCCS0 | -- | SPCCN2 | SPCCN1 | SPCCN0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | SPCLMD | SPWINEN | SPTYPE3 | SPTYPE2 | SPTYPE1 | SPTYPE0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SPCTL (m_vdp2_regs[0xe0/2])
+ #define STV_VDP2_SPCCCS ((STV_VDP2_SPCTL & 0x3000) >> 12)
+ #define STV_VDP2_SPCCN ((STV_VDP2_SPCTL & 0x700) >> 8)
+ #define STV_VDP2_SPCLMD ((STV_VDP2_SPCTL & 0x20) >> 5)
+ #define STV_VDP2_SPWINEN ((STV_VDP2_SPCTL & 0x10) >> 4)
+ #define STV_VDP2_SPTYPE (STV_VDP2_SPCTL & 0xf)
+
+/* 1800e2 - Shadow Control
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SDCTL (m_vdp2_regs[0x0e2/2])
+
+/* 1800e4 - CRAOFA - Colour Ram Address Offset (NBG0 - NBG3)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | N0CAOS2 | N3CAOS1 | N3CAOS0 | -- | N2CAOS2 | N2CAOS1 | N2CAOS0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | N1CAOS2 | N1CAOS1 | N1CAOS0 | -- | N0CAOS2 | N0CAOS1 | N0CAOS0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CRAOFA (m_vdp2_regs[0x0e4/2])
+
+ /* NxCAOS = */
+ #define STV_VDP2_N0CAOS ((STV_VDP2_CRAOFA & 0x0007) >> 0)
+ #define STV_VDP2_N1CAOS ((STV_VDP2_CRAOFA & 0x0070) >> 4)
+ #define STV_VDP2_N2CAOS ((STV_VDP2_CRAOFA & 0x0700) >> 8)
+ #define STV_VDP2_N3CAOS ((STV_VDP2_CRAOFA & 0x7000) >> 12)
+
+
+/* 1800e6 - Colour Ram Address Offset (RBG0, SPRITE)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_CRAOFB (m_vdp2_regs[0x0e6/2])
+ #define STV_VDP2_R0CAOS ((STV_VDP2_CRAOFB & 0x0007) >> 0)
+ #define STV_VDP2_SPCAOS ((STV_VDP2_CRAOFB & 0x0070) >> 4)
+
+/* 1800e8 - LNCLEN - Line Colour Screen Enable
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | SPLCEN | R0LCEN | N3LCEN | N2LCEN | N1LCEN | N0LCEN |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_LNCLEN (m_vdp2_regs[0x0e8/2])
+ #define STV_VDP2_SPLCEN ((STV_VDP2_LNCLEN & 0x0020) >> 5)
+ #define STV_VDP2_R0LCEN ((STV_VDP2_LNCLEN & 0x0010) >> 4)
+ #define STV_VDP2_N3LCEN ((STV_VDP2_LNCLEN & 0x0008) >> 3)
+ #define STV_VDP2_N2LCEN ((STV_VDP2_LNCLEN & 0x0004) >> 2)
+ #define STV_VDP2_N1LCEN ((STV_VDP2_LNCLEN & 0x0002) >> 1)
+ #define STV_VDP2_N0LCEN ((STV_VDP2_LNCLEN & 0x0001) >> 0)
+
+/* 1800ea - Special Priority Mode
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SFPRMD (m_vdp2_regs[0x0ea/2])
+
+
+/* 1800ec - Colour Calculation Control
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | BOKEN | BOKN2 | BOKN1 | BOKN0 | -- | EXCCEN | CCRTMD | CCMD |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | SPCCEN | LCCCEN | R0CCEN | N3CCEN | N2CCEN | N1CCEN | N0CCEN |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCCR (m_vdp2_regs[0x0ec/2])
+ #define STV_VDP2_CCMD ((STV_VDP2_CCCR & 0x100) >> 8)
+ #define STV_VDP2_SPCCEN ((STV_VDP2_CCCR & 0x40) >> 6)
+ #define STV_VDP2_LCCCEN ((STV_VDP2_CCCR & 0x20) >> 5)
+ #define STV_VDP2_R0CCEN ((STV_VDP2_CCCR & 0x10) >> 4)
+ #define STV_VDP2_N3CCEN ((STV_VDP2_CCCR & 0x8) >> 3)
+ #define STV_VDP2_N2CCEN ((STV_VDP2_CCCR & 0x4) >> 2)
+ #define STV_VDP2_N1CCEN ((STV_VDP2_CCCR & 0x2) >> 1)
+ #define STV_VDP2_N0CCEN ((STV_VDP2_CCCR & 0x1) >> 0)
+
+
+/* 1800ee - Special Colour Calculation Mode
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_SFCCMD (m_vdp2_regs[0x0ee/2])
+
+/* 1800f0 - Priority Number (Sprite 0,1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | S1PRIN2 | S1PRIN1 | S1PRIN0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | S0PRIN2 | S0PRIN1 | S0PRIN0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PRISA (m_vdp2_regs[0x0f0/2])
+ #define STV_VDP2_S1PRIN ((STV_VDP2_PRISA & 0x0700) >> 8)
+ #define STV_VDP2_S0PRIN ((STV_VDP2_PRISA & 0x0007) >> 0)
+
+/* 1800f2 - Priority Number (Sprite 2,3)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | S3PRIN2 | S3PRIN1 | S3PRIN0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | S2PRIN2 | S2PRIN1 | S2PRIN0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PRISB (m_vdp2_regs[0x0f2/2])
+ #define STV_VDP2_S3PRIN ((STV_VDP2_PRISB & 0x0700) >> 8)
+ #define STV_VDP2_S2PRIN ((STV_VDP2_PRISB & 0x0007) >> 0)
+
+/* 1800f4 - Priority Number (Sprite 4,5)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | S5PRIN2 | S5PRIN1 | S5PRIN0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | S4PRIN2 | S4PRIN1 | S4PRIN0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PRISC (m_vdp2_regs[0x0f4/2])
+ #define STV_VDP2_S5PRIN ((STV_VDP2_PRISC & 0x0700) >> 8)
+ #define STV_VDP2_S4PRIN ((STV_VDP2_PRISC & 0x0007) >> 0)
+
+/* 1800f6 - Priority Number (Sprite 6,7)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | S7PRIN2 | S7PRIN1 | S7PRIN0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | S6PRIN2 | S6PRIN1 | S6PRIN0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PRISD (m_vdp2_regs[0x0f6/2])
+ #define STV_VDP2_S7PRIN ((STV_VDP2_PRISD & 0x0700) >> 8)
+ #define STV_VDP2_S6PRIN ((STV_VDP2_PRISD & 0x0007) >> 0)
+
+
+/* 1800f8 - PRINA - Priority Number (NBG 0,1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PRINA (m_vdp2_regs[0x0f8/2])
+
+ #define STV_VDP2_N1PRIN ((STV_VDP2_PRINA & 0x0700) >> 8)
+ #define STV_VDP2_N0PRIN ((STV_VDP2_PRINA & 0x0007) >> 0)
+
+/* 1800fa - PRINB - Priority Number (NBG 2,3)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_PRINB (m_vdp2_regs[0x0fa/2])
+
+ #define STV_VDP2_N3PRIN ((STV_VDP2_PRINB & 0x0700) >> 8)
+ #define STV_VDP2_N2PRIN ((STV_VDP2_PRINB & 0x0007) >> 0)
+
+/* 1800fc - Priority Number (RBG0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_PRIR (m_vdp2_regs[0x0fc/2])
+
+ #define STV_VDP2_R0PRIN ((STV_VDP2_PRIR & 0x0007) >> 0)
+
+/* 1800fe - Reserved
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+/* 180100 - Colour Calculation Ratio (Sprite 0,1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | S1CCRT4 | S1CCRT3 | S1CCRT2 | S1CCRT1 | S1CCRT0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | S0CCRT4 | S0CCRT3 | S0CCRT2 | S0CCRT1 | S0CCRT0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCRSA (m_vdp2_regs[0x100/2])
+ #define STV_VDP2_S1CCRT ((STV_VDP2_CCRSA & 0x1f00) >> 8)
+ #define STV_VDP2_S0CCRT ((STV_VDP2_CCRSA & 0x001f) >> 0)
+
+/* 180102 - Colour Calculation Ratio (Sprite 2,3)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | S3CCRT4 | S3CCRT3 | S3CCRT2 | S3CCRT1 | S3CCRT0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | S2CCRT4 | S2CCRT3 | S2CCRT2 | S2CCRT1 | S2CCRT0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCRSB (m_vdp2_regs[0x102/2])
+ #define STV_VDP2_S3CCRT ((STV_VDP2_CCRSB & 0x1f00) >> 8)
+ #define STV_VDP2_S2CCRT ((STV_VDP2_CCRSB & 0x001f) >> 0)
+
+/* 180104 - Colour Calculation Ratio (Sprite 4,5)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | S5CCRT4 | S5CCRT3 | S5CCRT2 | S5CCRT1 | S5CCRT0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | S4CCRT4 | S4CCRT3 | S4CCRT2 | S4CCRT1 | S4CCRT0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCRSC (m_vdp2_regs[0x104/2])
+ #define STV_VDP2_S5CCRT ((STV_VDP2_CCRSC & 0x1f00) >> 8)
+ #define STV_VDP2_S4CCRT ((STV_VDP2_CCRSC & 0x001f) >> 0)
+
+/* 180106 - Colour Calculation Ratio (Sprite 6,7)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | S7CCRT4 | S7CCRT3 | S7CCRT2 | S7CCRT1 | S7CCRT0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | S6CCRT4 | S6CCRT3 | S6CCRT2 | S6CCRT1 | S6CCRT0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCRSD (m_vdp2_regs[0x106/2])
+ #define STV_VDP2_S7CCRT ((STV_VDP2_CCRSD & 0x1f00) >> 8)
+ #define STV_VDP2_S6CCRT ((STV_VDP2_CCRSD & 0x001f) >> 0)
+
+/* 180108 - Colour Calculation Ratio (NBG 0,1)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | N1CCRT4 | N1CCRT3 | N1CCRT2 | N1CCRT1 | N1CCRT0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | N0CCRT4 | N0CCRT3 | N0CCRT2 | N0CCRT1 | N0CCRT0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCRNA (m_vdp2_regs[0x108/2])
+ #define STV_VDP2_N1CCRT ((STV_VDP2_CCRNA & 0x1f00) >> 8)
+ #define STV_VDP2_N0CCRT (STV_VDP2_CCRNA & 0x1f)
+
+/* 18010a - Colour Calculation Ratio (NBG 2,3)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | N3CCRT4 | N3CCRT3 | N3CCRT2 | N3CCRT1 | N3CCRT0 |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | N2CCRT4 | N2CCRT3 | N2CCRT2 | N2CCRT1 | N2CCRT0 |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCRNB (m_vdp2_regs[0x10a/2])
+ #define STV_VDP2_N3CCRT ((STV_VDP2_CCRNB & 0x1f00) >> 8)
+ #define STV_VDP2_N2CCRT (STV_VDP2_CCRNB & 0x1f)
+
+/* 18010c - Colour Calculation Ratio (RBG 0)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCRR (m_vdp2_regs[0x10c/2])
+ #define STV_VDP2_R0CCRT (STV_VDP2_CCRR & 0x1f)
+
+/* 18010e - Colour Calculation Ratio (Line Colour Screen, Back Colour Screen)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CCRLB (m_vdp2_regs[0x10e/2])
+
+
+/* 180110 - Colour Offset Enable
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CLOFEN (m_vdp2_regs[0x110/2])
+ #define STV_VDP2_N0COEN ((STV_VDP2_CLOFEN & 0x01) >> 0)
+ #define STV_VDP2_N1COEN ((STV_VDP2_CLOFEN & 0x02) >> 1)
+ #define STV_VDP2_N2COEN ((STV_VDP2_CLOFEN & 0x04) >> 2)
+ #define STV_VDP2_N3COEN ((STV_VDP2_CLOFEN & 0x08) >> 3)
+ #define STV_VDP2_R0COEN ((STV_VDP2_CLOFEN & 0x10) >> 4)
+ #define STV_VDP2_BKCOEN ((STV_VDP2_CLOFEN & 0x20) >> 5)
+ #define STV_VDP2_SPCOEN ((STV_VDP2_CLOFEN & 0x40) >> 6)
+
+/* 180112 - Colour Offset Select
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_CLOFSL (m_vdp2_regs[0x112/2])
+ #define STV_VDP2_N0COSL ((STV_VDP2_CLOFSL & 0x01) >> 0)
+ #define STV_VDP2_N1COSL ((STV_VDP2_CLOFSL & 0x02) >> 1)
+ #define STV_VDP2_N2COSL ((STV_VDP2_CLOFSL & 0x04) >> 2)
+ #define STV_VDP2_N3COSL ((STV_VDP2_CLOFSL & 0x08) >> 3)
+ #define STV_VDP2_R0COSL ((STV_VDP2_CLOFSL & 0x10) >> 4)
+ #define STV_VDP2_BKCOSL ((STV_VDP2_CLOFSL & 0x20) >> 5)
+ #define STV_VDP2_SPCOSL ((STV_VDP2_CLOFSL & 0x40) >> 6)
+
+/* 180114 - Colour Offset A (Red)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_COAR (m_vdp2_regs[0x114/2])
+
+/* 180116 - Colour Offset A (Green)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_COAG (m_vdp2_regs[0x116/2])
+
+/* 180118 - Colour Offset A (Blue)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+
+ #define STV_VDP2_COAB (m_vdp2_regs[0x118/2])
+
+/* 18011a - Colour Offset B (Red)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_COBR (m_vdp2_regs[0x11a/2])
+
+/* 18011c - Colour Offset B (Green)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_COBG (m_vdp2_regs[0x11c/2])
+
+/* 18011e - Colour Offset B (Blue)
+ bit-> /----15----|----14----|----13----|----12----|----11----|----10----|----09----|----08----\
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ |----07----|----06----|----05----|----04----|----03----|----02----|----01----|----00----|
+ | -- | -- | -- | -- | -- | -- | -- | -- |
+ \----------|----------|----------|----------|----------|----------|----------|---------*/
+ #define STV_VDP2_COBB (m_vdp2_regs[0x11e/2])
+
+
+#define STV_VDP2_RBG_ROTATION_PARAMETER_A 1
+#define STV_VDP2_RBG_ROTATION_PARAMETER_B 2
+
+
+#define mul_fixed32( a, b ) mul_32x32_shift( a, b, 16 )
+
+void saturn_state::stv_vdp2_fill_rotation_parameter_table( UINT8 rot_parameter )
+{
+ UINT32 address = 0;
+
+ address = (((STV_VDP2_RPTAU << 16) | STV_VDP2_RPTAL) << 1);
+ if ( rot_parameter == 1 )
+ {
+ address &= ~0x00000080;
+ }
+ else if ( rot_parameter == 2 )
+ {
+ address |= 0x00000080;
+ }
+
+ stv_current_rotation_parameter_table.xst = (m_vdp2_vram[address/4] & 0x1fffffc0) | ((m_vdp2_vram[address/4] & 0x10000000) ? 0xe0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.yst = (m_vdp2_vram[address/4 + 1] & 0x1fffffc0) | ((m_vdp2_vram[address/4 + 1] & 0x10000000) ? 0xe0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.zst = (m_vdp2_vram[address/4 + 2] & 0x1fffffc0) | ((m_vdp2_vram[address/4 + 2] & 0x10000000) ? 0xe0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.dxst = (m_vdp2_vram[address/4 + 3] & 0x0007ffc0) | ((m_vdp2_vram[address/4 + 3] & 0x00040000) ? 0xfff80000 : 0x00000000 );
+ stv_current_rotation_parameter_table.dyst = (m_vdp2_vram[address/4 + 4] & 0x0007ffc0) | ((m_vdp2_vram[address/4 + 4] & 0x00040000) ? 0xfff80000 : 0x00000000 );
+ stv_current_rotation_parameter_table.dx = (m_vdp2_vram[address/4 + 5] & 0x0007ffc0) | ((m_vdp2_vram[address/4 + 5] & 0x00040000) ? 0xfff80000 : 0x00000000 );
+ stv_current_rotation_parameter_table.dy = (m_vdp2_vram[address/4 + 6] & 0x0007ffc0) | ((m_vdp2_vram[address/4 + 6] & 0x00040000) ? 0xfff80000 : 0x00000000 );
+ stv_current_rotation_parameter_table.A = (m_vdp2_vram[address/4 + 7] & 0x000fffc0) | ((m_vdp2_vram[address/4 + 7] & 0x00080000) ? 0xfff00000 : 0x00000000 );
+ stv_current_rotation_parameter_table.B = (m_vdp2_vram[address/4 + 8] & 0x000fffc0) | ((m_vdp2_vram[address/4 + 8] & 0x00080000) ? 0xfff00000 : 0x00000000 );
+ stv_current_rotation_parameter_table.C = (m_vdp2_vram[address/4 + 9] & 0x000fffc0) | ((m_vdp2_vram[address/4 + 9] & 0x00080000) ? 0xfff00000 : 0x00000000 );
+ stv_current_rotation_parameter_table.D = (m_vdp2_vram[address/4 + 10] & 0x000fffc0) | ((m_vdp2_vram[address/4 + 10] & 0x00080000) ? 0xfff00000 : 0x00000000 );
+ stv_current_rotation_parameter_table.E = (m_vdp2_vram[address/4 + 11] & 0x000fffc0) | ((m_vdp2_vram[address/4 + 11] & 0x00080000) ? 0xfff00000 : 0x00000000 );
+ stv_current_rotation_parameter_table.F = (m_vdp2_vram[address/4 + 12] & 0x000fffc0) | ((m_vdp2_vram[address/4 + 12] & 0x00080000) ? 0xfff00000 : 0x00000000 );
+ stv_current_rotation_parameter_table.px = (m_vdp2_vram[address/4 + 13] & 0x3fff0000) | ((m_vdp2_vram[address/4 + 13] & 0x30000000) ? 0xc0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.py = (m_vdp2_vram[address/4 + 13] & 0x00003fff) << 16;
+ if ( stv_current_rotation_parameter_table.py & 0x20000000 ) stv_current_rotation_parameter_table.py |= 0xc0000000;
+ stv_current_rotation_parameter_table.pz = (m_vdp2_vram[address/4 + 14] & 0x3fff0000) | ((m_vdp2_vram[address/4 + 14] & 0x20000000) ? 0xc0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.cx = (m_vdp2_vram[address/4 + 15] & 0x3fff0000) | ((m_vdp2_vram[address/4 + 15] & 0x20000000) ? 0xc0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.cy = (m_vdp2_vram[address/4 + 15] & 0x00003fff) << 16;
+ if ( stv_current_rotation_parameter_table.cy & 0x20000000 ) stv_current_rotation_parameter_table.cy |= 0xc0000000;
+ stv_current_rotation_parameter_table.cz = (m_vdp2_vram[address/4 + 16] & 0x3fff0000) | ((m_vdp2_vram[address/4 + 16] & 0x20000000) ? 0xc0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.mx = (m_vdp2_vram[address/4 + 17] & 0x3fffffc0) | ((m_vdp2_vram[address/4 + 17] & 0x20000000) ? 0xc0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.my = (m_vdp2_vram[address/4 + 18] & 0x3fffffc0) | ((m_vdp2_vram[address/4 + 18] & 0x20000000) ? 0xc0000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.kx = (m_vdp2_vram[address/4 + 19] & 0x00ffffff) | ((m_vdp2_vram[address/4 + 19] & 0x00800000) ? 0xff000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.ky = (m_vdp2_vram[address/4 + 20] & 0x00ffffff) | ((m_vdp2_vram[address/4 + 20] & 0x00800000) ? 0xff000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.kast = (m_vdp2_vram[address/4 + 21] & 0xffffffc0);
+ stv_current_rotation_parameter_table.dkast= (m_vdp2_vram[address/4 + 22] & 0x03ffffc0) | ((m_vdp2_vram[address/4 + 22] & 0x02000000) ? 0xfc000000 : 0x00000000 );
+ stv_current_rotation_parameter_table.dkax = (m_vdp2_vram[address/4 + 23] & 0x03ffffc0) | ((m_vdp2_vram[address/4 + 23] & 0x02000000) ? 0xfc000000 : 0x00000000 );
+
+#define RP stv_current_rotation_parameter_table
+
+ if(LOG_ROZ == 1) logerror( "Rotation parameter table (%d)\n", rot_parameter );
+ if(LOG_ROZ == 1) logerror( "xst = %x, yst = %x, zst = %x\n", RP.xst, RP.yst, RP.zst );
+ if(LOG_ROZ == 1) logerror( "dxst = %x, dyst = %x\n", RP.dxst, RP.dyst );
+ if(LOG_ROZ == 1) logerror( "dx = %x, dy = %x\n", RP.dx, RP.dy );
+ if(LOG_ROZ == 1) logerror( "A = %x, B = %x, C = %x, D = %x, E = %x, F = %x\n", RP.A, RP.B, RP.C, RP.D, RP.E, RP.F );
+ if(LOG_ROZ == 1) logerror( "px = %x, py = %x, pz = %x\n", RP.px, RP.py, RP.pz );
+ if(LOG_ROZ == 1) logerror( "cx = %x, cy = %x, cz = %x\n", RP.cx, RP.cy, RP.cz );
+ if(LOG_ROZ == 1) logerror( "mx = %x, my = %x\n", RP.mx, RP.my );
+ if(LOG_ROZ == 1) logerror( "kx = %x, ky = %x\n", RP.kx, RP.ky );
+ if(LOG_ROZ == 1) logerror( "kast = %x, dkast = %x, dkax = %x\n", RP.kast, RP.dkast, RP.dkax );
+
+ /*Attempt to show on screen the rotation table*/
+ #if 0
+ if(LOG_ROZ == 2)
+ {
+ if(machine().input().code_pressed_once(JOYCODE_Y_UP_SWITCH))
+ m_vdpdebug_roz++;
+
+ if(machine().input().code_pressed_once(JOYCODE_Y_DOWN_SWITCH))
+ m_vdpdebug_roz--;
+
+ if(m_vdpdebug_roz > 10)
+ m_vdpdebug_roz = 10;
+
+ switch(m_vdpdebug_roz)
+ {
+ case 0: popmessage( "Rotation parameter Table (%d)", rot_parameter ); break;
+ case 1: popmessage( "xst = %x, yst = %x, zst = %x", RP.xst, RP.yst, RP.zst ); break;
+ case 2: popmessage( "dxst = %x, dyst = %x", RP.dxst, RP.dyst ); break;
+ case 3: popmessage( "dx = %x, dy = %x", RP.dx, RP.dy ); break;
+ case 4: popmessage( "A = %x, B = %x, C = %x, D = %x, E = %x, F = %x", RP.A, RP.B, RP.C, RP.D, RP.E, RP.F ); break;
+ case 5: popmessage( "px = %x, py = %x, pz = %x", RP.px, RP.py, RP.pz ); break;
+ case 6: popmessage( "cx = %x, cy = %x, cz = %x", RP.cx, RP.cy, RP.cz ); break;
+ case 7: popmessage( "mx = %x, my = %x", RP.mx, RP.my ); break;
+ case 8: popmessage( "kx = %x, ky = %x", RP.kx, RP.ky ); break;
+ case 9: popmessage( "kast = %x, dkast = %x, dkax = %x", RP.kast, RP.dkast, RP.dkax ); break;
+ case 10: break;
+ }
+ }
+ #endif
+}
+
+/* check if RGB layer has rotation applied */
+UINT8 saturn_state::stv_vdp2_is_rotation_applied(void)
+{
+#define _FIXED_1 (0x00010000)
+#define _FIXED_0 (0x00000000)
+
+ if ( RP.A == _FIXED_1 &&
+ RP.B == _FIXED_0 &&
+ RP.C == _FIXED_0 &&
+ RP.D == _FIXED_0 &&
+ RP.E == _FIXED_1 &&
+ RP.F == _FIXED_0 &&
+ RP.dxst == _FIXED_0 &&
+ RP.dyst == _FIXED_1 &&
+ RP.dx == _FIXED_1 &&
+ RP.dy == _FIXED_0 &&
+ RP.kx == _FIXED_1 &&
+ RP.ky == _FIXED_1 )
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+UINT8 saturn_state::stv_vdp2_are_map_registers_equal(void)
+{
+ int i;
+
+ for ( i = 1; i < stv2_current_tilemap.map_count; i++ )
+ {
+ if ( stv2_current_tilemap.map_offset[i] != stv2_current_tilemap.map_offset[0] )
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void saturn_state::stv_vdp2_check_fade_control_for_layer( void )
+{
+ if ( stv2_current_tilemap.fade_control & 1 )
+ {
+ if ( stv2_current_tilemap.fade_control & 2 )
+ {
+ if ((STV_VDP2_COBR & 0x1ff) == 0 &&
+ (STV_VDP2_COBG & 0x1ff) == 0 &&
+ (STV_VDP2_COBB & 0x1ff) == 0 )
+ {
+ stv2_current_tilemap.fade_control = 0;
+ }
+ }
+ else
+ {
+ if ((STV_VDP2_COAR & 0x1ff) == 0 &&
+ (STV_VDP2_COAG & 0x1ff) == 0 &&
+ (STV_VDP2_COAB & 0x1ff) == 0 )
+ {
+ stv2_current_tilemap.fade_control = 0;
+ }
+ }
+ }
+}
+
+#define STV_VDP2_CP_NBG0_PNMDR 0x0
+#define STV_VDP2_CP_NBG1_PNMDR 0x1
+#define STV_VDP2_CP_NBG2_PNMDR 0x2
+#define STV_VDP2_CP_NBG3_PNMDR 0x3
+#define STV_VDP2_CP_NBG0_CPDR 0x4
+#define STV_VDP2_CP_NBG1_CPDR 0x5
+#define STV_VDP2_CP_NBG2_CPDR 0x6
+#define STV_VDP2_CP_NBG3_CPDR 0x7
+
+UINT8 saturn_state::stv_vdp2_check_vram_cycle_pattern_registers( UINT8 access_command_pnmdr, UINT8 access_command_cpdr, UINT8 bitmap_enable )
+{
+ int i;
+ UINT8 access_command_ok = 0;
+ UINT16 cp_regs[8];
+ cp_regs[0] = STV_VDP2_CYCA0L;
+ cp_regs[1] = STV_VDP2_CYCA0U;
+ cp_regs[2] = STV_VDP2_CYCA1L;
+ cp_regs[3] = STV_VDP2_CYCA1U;
+ cp_regs[4] = STV_VDP2_CYCA2L;
+ cp_regs[5] = STV_VDP2_CYCA2U;
+ cp_regs[6] = STV_VDP2_CYCA3L;
+ cp_regs[7] = STV_VDP2_CYCA3U;
+
+ if ( bitmap_enable ) access_command_ok = 1;
+
+ for ( i = 0; i < 8; i++ )
+ {
+ if ( ((cp_regs[i] >> 12) & 0xf) == access_command_pnmdr )
+ {
+ access_command_ok |= 1;
+ }
+ if ( ((cp_regs[i] >> 12) & 0xf) == access_command_cpdr )
+ {
+ access_command_ok |= 2;
+ }
+ if ( ((cp_regs[i] >> 8) & 0xf) == access_command_pnmdr )
+ {
+ access_command_ok |= 1;
+ }
+ if ( ((cp_regs[i] >> 8) & 0xf) == access_command_cpdr )
+ {
+ access_command_ok |= 2;
+ }
+ if ( ((cp_regs[i] >> 4) & 0xf) == access_command_pnmdr )
+ {
+ access_command_ok |= 1;
+ }
+ if ( ((cp_regs[i] >> 4) & 0xf) == access_command_cpdr )
+ {
+ access_command_ok |= 2;
+ }
+ if ( ((cp_regs[i] >> 0) & 0xf) == access_command_pnmdr )
+ {
+ access_command_ok |= 1;
+ }
+ if ( ((cp_regs[i] >> 0) & 0xf) == access_command_cpdr )
+ {
+ access_command_ok |= 2;
+ }
+ }
+ return access_command_ok == 3 ? 1 : 0;
+}
+
+INLINE UINT32 stv_add_blend(UINT32 a, UINT32 b)
+{
+ rgb_t rb = (a & 0xff00ff) + (b & 0xff00ff);
+ rgb_t g = (a & 0x00ff00) + (b & 0x00ff00);
+ return rgb_t((rb & 0x1000000) ? 0xff : rb.r(),
+ (g & 0x0010000) ? 0xff : g.g(),
+ (rb & 0x0000100) ? 0xff : rb.b()
+ );
+}
+
+
+void saturn_state::stv_vdp2_compute_color_offset( int *r, int *g, int *b, int cor )
+{
+ if ( cor == 0 )
+ {
+ *r = (STV_VDP2_COAR & 0x100) ? (*r - (0x100 - (STV_VDP2_COAR & 0xff))) : ((STV_VDP2_COAR & 0xff) + *r);
+ *g = (STV_VDP2_COAG & 0x100) ? (*g - (0x100 - (STV_VDP2_COAG & 0xff))) : ((STV_VDP2_COAG & 0xff) + *g);
+ *b = (STV_VDP2_COAB & 0x100) ? (*b - (0x100 - (STV_VDP2_COAB & 0xff))) : ((STV_VDP2_COAB & 0xff) + *b);
+ }
+ else
+ {
+ *r = (STV_VDP2_COBR & 0x100) ? (*r - (0xff - (STV_VDP2_COBR & 0xff))) : ((STV_VDP2_COBR & 0xff) + *r);
+ *g = (STV_VDP2_COBG & 0x100) ? (*g - (0xff - (STV_VDP2_COBG & 0xff))) : ((STV_VDP2_COBG & 0xff) + *g);
+ *b = (STV_VDP2_COBB & 0x100) ? (*b - (0xff - (STV_VDP2_COBB & 0xff))) : ((STV_VDP2_COBB & 0xff) + *b);
+ }
+ if(*r < 0) { *r = 0; }
+ if(*r > 0xff) { *r = 0xff; }
+ if(*g < 0) { *g = 0; }
+ if(*g > 0xff) { *g = 0xff; }
+ if(*b < 0) { *b = 0; }
+ if(*b > 0xff) { *b = 0xff; }
+}
+
+void saturn_state::stv_vdp2_compute_color_offset_UINT32(rgb_t *rgb, int cor)
+{
+ int _r = rgb->r();
+ int _g = rgb->g();
+ int _b = rgb->b();
+ if ( cor == 0 )
+ {
+ _r = (STV_VDP2_COAR & 0x100) ? (_r - (0x100 - (STV_VDP2_COAR & 0xff))) : ((STV_VDP2_COAR & 0xff) + _r);
+ _g = (STV_VDP2_COAG & 0x100) ? (_g - (0x100 - (STV_VDP2_COAG & 0xff))) : ((STV_VDP2_COAG & 0xff) + _g);
+ _b = (STV_VDP2_COAB & 0x100) ? (_b - (0x100 - (STV_VDP2_COAB & 0xff))) : ((STV_VDP2_COAB & 0xff) + _b);
+ }
+ else
+ {
+ _r = (STV_VDP2_COBR & 0x100) ? (_r - (0xff - (STV_VDP2_COBR & 0xff))) : ((STV_VDP2_COBR & 0xff) + _r);
+ _g = (STV_VDP2_COBG & 0x100) ? (_g - (0xff - (STV_VDP2_COBG & 0xff))) : ((STV_VDP2_COBG & 0xff) + _g);
+ _b = (STV_VDP2_COBB & 0x100) ? (_b - (0xff - (STV_VDP2_COBB & 0xff))) : ((STV_VDP2_COBB & 0xff) + _b);
+ }
+ if(_r < 0) { _r = 0; }
+ if(_r > 0xff) { _r = 0xff; }
+ if(_g < 0) { _g = 0; }
+ if(_g > 0xff) { _g = 0xff; }
+ if(_b < 0) { _b = 0; }
+ if(_b > 0xff) { _b = 0xff; }
+
+ *rgb = rgb_t(_r, _g, _b);
+}
+
+void saturn_state::stv_vdp2_drawgfxzoom(
+ bitmap_rgb32 &dest_bmp,const rectangle &clip,gfx_element *gfx,
+ UINT32 code,UINT32 color,int flipx,int flipy,int sx,int sy,
+ int transparency,int transparent_color,int scalex, int scaley,
+ int sprite_screen_width, int sprite_screen_height, int alpha)
+{
+ rectangle myclip;
+
+ if (!scalex || !scaley) return;
+
+ if (gfx->has_pen_usage() && transparency == STV_TRANSPARENCY_PEN)
+ {
+ int transmask = 0;
+
+ transmask = 1 << (transparent_color & 0xff);
+
+ if ((gfx->pen_usage(code) & ~transmask) == 0)
+ /* character is totally transparent, no need to draw */
+ return;
+ else if ((gfx->pen_usage(code) & transmask) == 0)
+ /* character is totally opaque, can disable transparency */
+ transparency = STV_TRANSPARENCY_NONE;
+ }
+
+ /*
+ scalex and scaley are 16.16 fixed point numbers
+ 1<<15 : shrink to 50%
+ 1<<16 : uniform scale
+ 1<<17 : double to 200%
+ */
+
+
+ /* KW 991012 -- Added code to force clip to bitmap boundary */
+ myclip = clip;
+ myclip &= dest_bmp.cliprect();
+
+ if( gfx )
+ {
+ const pen_t *pal = &m_palette->pen(gfx->colorbase() + gfx->granularity() * (color % gfx->colors()));
+ const UINT8 *source_base = gfx->get_data(code % gfx->elements());
+
+ //int sprite_screen_height = (scaley*gfx->height()+0x8000)>>16;
+ //int sprite_screen_width = (scalex*gfx->width()+0x8000)>>16;
+
+ if (sprite_screen_width && sprite_screen_height)
+ {
+ /* compute sprite increment per screen pixel */
+ //int dx = (gfx->width()<<16)/sprite_screen_width;
+ //int dy = (gfx->height()<<16)/sprite_screen_height;
+ int dx = stv2_current_tilemap.incx;
+ int dy = stv2_current_tilemap.incy;
+
+ int ex = sx+sprite_screen_width;
+ int ey = sy+sprite_screen_height;
+
+ int x_index_base;
+ int y_index;
+
+ if( flipx )
+ {
+ x_index_base = (sprite_screen_width-1)*dx;
+ dx = -dx;
+ }
+ else
+ {
+ x_index_base = 0;
+ }
+
+ if( flipy )
+ {
+ y_index = (sprite_screen_height-1)*dy;
+ dy = -dy;
+ }
+ else
+ {
+ y_index = 0;
+ }
+
+ if( sx < myclip.min_x)
+ { /* clip left */
+ int pixels = myclip.min_x-sx;
+ sx += pixels;
+ x_index_base += pixels*dx;
+ }
+ if( sy < myclip.min_y )
+ { /* clip top */
+ int pixels = myclip.min_y-sy;
+ sy += pixels;
+ y_index += pixels*dy;
+ }
+ /* NS 980211 - fixed incorrect clipping */
+ if( ex > myclip.max_x+1 )
+ { /* clip right */
+ int pixels = ex-myclip.max_x-1;
+ ex -= pixels;
+ }
+ if( ey > myclip.max_y+1 )
+ { /* clip bottom */
+ int pixels = ey-myclip.max_y-1;
+ ey -= pixels;
+ }
+
+ if( ex>sx )
+ { /* skip if inner loop doesn't draw anything */
+ int y;
+
+ /* case 0: STV_TRANSPARENCY_NONE */
+ if (transparency == STV_TRANSPARENCY_NONE)
+ {
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = source_base + (y_index>>16) * gfx->rowbytes();
+ UINT32 *dest = &dest_bmp.pix32(y);
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ if(stv_vdp2_window_process(x,y))
+ dest[x] = pal[source[x_index>>16]];
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+ } /* case 1: STV_TRANSPARENCY_PEN */
+ else if (transparency == STV_TRANSPARENCY_PEN)
+ {
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = source_base + (y_index>>16) * gfx->rowbytes();
+ UINT32 *dest = &dest_bmp.pix32(y);
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ if(stv_vdp2_window_process(x,y))
+ {
+ int c = source[x_index>>16];
+ if( c != transparent_color ) dest[x] = pal[c];
+ }
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+ } /* case 6: STV_TRANSPARENCY_ALPHA */
+ else if (transparency == STV_TRANSPARENCY_ALPHA)
+ {
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = source_base + (y_index>>16) * gfx->rowbytes();
+ UINT32 *dest = &dest_bmp.pix32(y);
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ if(stv_vdp2_window_process(x,y))
+ {
+ int c = source[x_index>>16];
+ if( c != transparent_color ) dest[x] = alpha_blend_r32(dest[x], pal[c], alpha);
+ }
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+ } /* case : STV_TRANSPARENCY_ADD_BLEND */
+ else if (transparency == STV_TRANSPARENCY_ADD_BLEND )
+ {
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = source_base + (y_index>>16) * gfx->rowbytes();
+ UINT32 *dest = &dest_bmp.pix32(y);
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ if(stv_vdp2_window_process(x,y))
+ {
+ int c = source[x_index>>16];
+ if( c != transparent_color ) dest[x] = stv_add_blend(dest[x],pal[c]);
+ }
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+ }
+ }
+ }
+ }
+}
+
+void saturn_state::stv_vdp2_drawgfxzoom_rgb555(
+ bitmap_rgb32 &dest_bmp,const rectangle &clip,
+ UINT32 code,UINT32 color,int flipx,int flipy,int sx,int sy,
+ int transparency,int transparent_color,int scalex, int scaley,
+ int sprite_screen_width, int sprite_screen_height, int alpha)
+{
+ rectangle myclip;
+ UINT8* gfxdata;
+
+ gfxdata = m_vdp2.gfx_decode + code * 0x20;
+
+ if(stv2_current_tilemap.window_control.enabled[0] ||
+ stv2_current_tilemap.window_control.enabled[1])
+ popmessage("Window Enabled for RGB555 Zoom");
+
+ if (!scalex || !scaley) return;
+
+ #if 0
+ if (gfx->has_pen_usage() && transparency == STV_TRANSPARENCY_PEN)
+ {
+ int transmask = 0;
+
+ transmask = 1 << (transparent_color & 0xff);
+
+ if ((gfx->pen_usage(code) & ~transmask) == 0)
+ /* character is totally transparent, no need to draw */
+ return;
+ else if ((gfx->pen_usage(code) & transmask) == 0)
+ /* character is totally opaque, can disable transparency */
+ transparency = STV_TRANSPARENCY_NONE;
+ }
+ #endif
+
+ /*
+ scalex and scaley are 16.16 fixed point numbers
+ 1<<15 : shrink to 50%
+ 1<<16 : uniform scale
+ 1<<17 : double to 200%
+ */
+
+
+ /* KW 991012 -- Added code to force clip to bitmap boundary */
+ myclip = clip;
+ myclip &= dest_bmp.cliprect();
+
+// if( gfx )
+ {
+// const UINT8 *source_base = gfx->get_data(code % gfx->elements());
+
+ //int sprite_screen_height = (scaley*gfx->height()+0x8000)>>16;
+ //int sprite_screen_width = (scalex*gfx->width()+0x8000)>>16;
+
+ if (sprite_screen_width && sprite_screen_height)
+ {
+ /* compute sprite increment per screen pixel */
+ //int dx = (gfx->width()<<16)/sprite_screen_width;
+ //int dy = (gfx->height()<<16)/sprite_screen_height;
+ int dx = stv2_current_tilemap.incx;
+ int dy = stv2_current_tilemap.incy;
+
+ int ex = sx+sprite_screen_width;
+ int ey = sy+sprite_screen_height;
+
+ int x_index_base;
+ int y_index;
+
+ if( flipx )
+ {
+ x_index_base = (sprite_screen_width-1)*dx;
+ dx = -dx;
+ }
+ else
+ {
+ x_index_base = 0;
+ }
+
+ if( flipy )
+ {
+ y_index = (sprite_screen_height-1)*dy;
+ dy = -dy;
+ }
+ else
+ {
+ y_index = 0;
+ }
+
+ if( sx < myclip.min_x)
+ { /* clip left */
+ int pixels = myclip.min_x-sx;
+ sx += pixels;
+ x_index_base += pixels*dx;
+ }
+ if( sy < myclip.min_y )
+ { /* clip top */
+ int pixels = myclip.min_y-sy;
+ sy += pixels;
+ y_index += pixels*dy;
+ }
+ /* NS 980211 - fixed incorrect clipping */
+ if( ex > myclip.max_x+1 )
+ { /* clip right */
+ int pixels = ex-myclip.max_x-1;
+ ex -= pixels;
+ }
+ if( ey > myclip.max_y+1 )
+ { /* clip bottom */
+ int pixels = ey-myclip.max_y-1;
+ ey -= pixels;
+ }
+
+ if( ex>sx )
+ { /* skip if inner loop doesn't draw anything */
+ int y;
+
+ /* case 0: STV_TRANSPARENCY_NONE */
+ if (transparency == STV_TRANSPARENCY_NONE)
+ {
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = gfxdata + (y_index>>16)*16;
+ UINT32 *dest = &dest_bmp.pix32(y);
+ int r,g,b,data;
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ data = (source[(x_index>>16)*2] << 8) | source[(x_index>>16)*2+1];
+ b = pal5bit((data & 0x7c00) >> 10);
+ g = pal5bit((data & 0x03e0) >> 5);
+ r = pal5bit( data & 0x001f);
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset(&r,&g,&b,stv2_current_tilemap.fade_control & 2);
+
+ dest[x] = rgb_t(r, g, b);
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+ }
+
+ /* case 1: STV_TRANSPARENCY_PEN */
+ if (transparency == STV_TRANSPARENCY_PEN)
+ {
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = gfxdata + (y_index>>16)*16;
+ UINT32 *dest = &dest_bmp.pix32(y);
+ int r,g,b,data;
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ data = (source[(x_index>>16)*2] << 8) | source[(x_index>>16)*2+1];
+ b = pal5bit((data & 0x7c00) >> 10);
+ g = pal5bit((data & 0x03e0) >> 5);
+ r = pal5bit( data & 0x001f);
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset(&r,&g,&b,stv2_current_tilemap.fade_control & 2);
+
+ if( data ) dest[x] = rgb_t(r, g, b);
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+ }
+
+ /* case 6: STV_TRANSPARENCY_ALPHA */
+ if (transparency == STV_TRANSPARENCY_ALPHA)
+ {
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = gfxdata + (y_index>>16)*16;
+ UINT32 *dest = &dest_bmp.pix32(y);
+ int r,g,b,data;
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ data = (source[(x_index>>16)*2] << 8) | source[(x_index>>16)*2+1];
+ b = pal5bit((data & 0x7c00) >> 10);
+ g = pal5bit((data & 0x03e0) >> 5);
+ r = pal5bit( data & 0x001f);
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset(&r,&g,&b,stv2_current_tilemap.fade_control & 2);
+
+ if( data ) dest[x] = alpha_blend_r32(dest[x], rgb_t(r, g, b), alpha);
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+ }
+
+ /* case : STV_TRANSPARENCY_ADD_BLEND */
+ if (transparency == STV_TRANSPARENCY_ADD_BLEND )
+ {
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = gfxdata + (y_index>>16)*16;
+ UINT32 *dest = &dest_bmp.pix32(y);
+ int r,g,b,data;
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ data = (source[(x_index*2+0)>>16]<<0)|(source[(x_index*2+1)>>16]<<8);
+ b = pal5bit((data & 0x7c00) >> 10);
+ g = pal5bit((data & 0x03e0) >> 5);
+ r = pal5bit( data & 0x001f);
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset(&r,&g,&b,stv2_current_tilemap.fade_control & 2);
+
+ if( data ) dest[x] = stv_add_blend(dest[x], rgb_t(r, g, b));
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+ }
+
+ }
+ }
+ }
+
+}
+
+
+void saturn_state::stv_vdp2_drawgfx_rgb555( bitmap_rgb32 &dest_bmp, const rectangle &clip, UINT32 code, int flipx, int flipy, int sx, int sy, int transparency, int alpha)
+{
+ rectangle myclip;
+ UINT8* gfxdata;
+ int sprite_screen_width, sprite_screen_height;
+
+ gfxdata = m_vdp2.gfx_decode + code * 0x20;
+ sprite_screen_width = sprite_screen_height = 8;
+
+ if(stv2_current_tilemap.window_control.enabled[0] ||
+ stv2_current_tilemap.window_control.enabled[1])
+ popmessage("Window Enabled for RGB555 tiles");
+
+ /* KW 991012 -- Added code to force clip to bitmap boundary */
+ myclip = clip;
+ myclip &= dest_bmp.cliprect();
+
+ {
+ int dx = stv2_current_tilemap.incx;
+ int dy = stv2_current_tilemap.incy;
+
+ int ex = sx+sprite_screen_width;
+ int ey = sy+sprite_screen_height;
+
+ int x_index_base;
+ int y_index;
+
+ if( flipx )
+ {
+ x_index_base = (sprite_screen_width-1)*dx;
+ dx = -dx;
+ }
+ else
+ {
+ x_index_base = 0;
+ }
+
+ if( flipy )
+ {
+ y_index = (sprite_screen_height-1)*dy;
+ dy = -dy;
+ }
+ else
+ {
+ y_index = 0;
+ }
+
+ if( sx < myclip.min_x)
+ { /* clip left */
+ int pixels = myclip.min_x-sx;
+ sx += pixels;
+ x_index_base += pixels*dx;
+ }
+ if( sy < myclip.min_y )
+ { /* clip top */
+ int pixels = myclip.min_y-sy;
+ sy += pixels;
+ y_index += pixels*dy;
+ }
+ /* NS 980211 - fixed incorrect clipping */
+ if( ex > myclip.max_x+1 )
+ { /* clip right */
+ int pixels = ex-myclip.max_x-1;
+ ex -= pixels;
+ }
+ if( ey > myclip.max_y+1 )
+ { /* clip bottom */
+ int pixels = ey-myclip.max_y-1;
+ ey -= pixels;
+ }
+
+ if( ex>sx )
+ { /* skip if inner loop doesn't draw anything */
+ int y;
+
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = gfxdata + (y_index>>16)*16;
+ UINT32 *dest = &dest_bmp.pix32(y);
+ UINT16 data;
+
+ int x, x_index = x_index_base;
+ for( x=sx; x<ex; x++ )
+ {
+ int r,g,b;
+
+ data = (source[(x_index>>16)*2] << 8) | source[(x_index>>16)*2+1];
+ if ((data & 0x8000) || (transparency == STV_TRANSPARENCY_NONE))
+ {
+ b = pal5bit((data & 0x7c00) >> 10);
+ g = pal5bit((data & 0x03e0) >> 5);
+ r = pal5bit( data & 0x001f);
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset(&r,&g,&b,stv2_current_tilemap.fade_control & 2);
+
+ if ( transparency == STV_TRANSPARENCY_ALPHA )
+ dest[x] = alpha_blend_r32( dest[x], rgb_t(r, g, b), alpha );
+ else
+ dest[x] = rgb_t(r, g, b);
+ }
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+
+ }
+
+ }
+
+}
+
+
+void saturn_state::stv_vdp2_drawgfx_rgb888( bitmap_rgb32 &dest_bmp, const rectangle &clip, UINT32 code, int flipx, int flipy,
+ int sx, int sy, int transparency, int alpha)
+{
+ rectangle myclip;
+ UINT8* gfxdata;
+ int sprite_screen_width, sprite_screen_height;
+
+ gfxdata = m_vdp2.gfx_decode + code * 0x20;
+ sprite_screen_width = sprite_screen_height = 8;
+
+ if(stv2_current_tilemap.window_control.enabled[0] ||
+ stv2_current_tilemap.window_control.enabled[1])
+ popmessage("Window Enabled for RGB888 tiles");
+
+ /* KW 991012 -- Added code to force clip to bitmap boundary */
+ myclip = clip;
+ myclip &= dest_bmp.cliprect();
+ {
+ int dx = stv2_current_tilemap.incx;
+ int dy = stv2_current_tilemap.incy;
+
+ int ex = sx+sprite_screen_width;
+ int ey = sy+sprite_screen_height;
+
+ int x_index_base;
+ int y_index;
+
+ if( flipx )
+ {
+ x_index_base = (sprite_screen_width-1)*dx;
+ dx = -dx;
+ }
+ else
+ {
+ x_index_base = 0;
+ }
+
+ if( flipy )
+ {
+ y_index = (sprite_screen_height-1)*dy;
+ dy = -dy;
+ }
+ else
+ {
+ y_index = 0;
+ }
+
+ if( sx < myclip.min_x)
+ { /* clip left */
+ int pixels = myclip.min_x-sx;
+ sx += pixels;
+ x_index_base += pixels*dx;
+ }
+ if( sy < myclip.min_y )
+ { /* clip top */
+ int pixels = myclip.min_y-sy;
+ sy += pixels;
+ y_index += pixels*dy;
+ }
+ /* NS 980211 - fixed incorrect clipping */
+ if( ex > myclip.max_x+1 )
+ { /* clip right */
+ int pixels = ex-myclip.max_x-1;
+ ex -= pixels;
+ }
+ if( ey > myclip.max_y+1 )
+ { /* clip bottom */
+ int pixels = ey-myclip.max_y-1;
+ ey -= pixels;
+ }
+
+ if( ex>sx )
+ { /* skip if inner loop doesn't draw anything */
+ int y;
+
+ for( y=sy; y<ey; y++ )
+ {
+ const UINT8 *source = gfxdata + (y_index>>16)*32;
+ UINT32 *dest = &dest_bmp.pix32(y);
+ UINT32 data;
+
+ int x, x_index = x_index_base;
+
+ for( x=sx; x<ex; x++ )
+ {
+ int r,g,b;
+
+ data = (source[(x_index>>16)*4+0] << 24) | (source[(x_index>>16)*4+1] << 16) | (source[(x_index>>16)*4+2] << 8) | (source[(x_index>>16)*4+3] << 0);
+ if ((data & 0x80000000) || (transparency == STV_TRANSPARENCY_NONE))
+ {
+ b = (data & 0xff0000) >> 16;
+ g = (data & 0x00ff00) >> 8;
+ r = (data & 0x0000ff);
+
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset(&r,&g,&b,stv2_current_tilemap.fade_control & 2);
+
+ if ( transparency == STV_TRANSPARENCY_ALPHA )
+ dest[x] = alpha_blend_r32( dest[x], rgb_t(r, g, b), alpha );
+ else
+ dest[x] = rgb_t(r, g, b);
+ }
+ x_index += dx;
+ }
+
+ y_index += dy;
+ }
+
+ }
+
+ }
+}
+
+void saturn_state::stv_vdp2_drawgfx_alpha(bitmap_rgb32 &dest_bmp,const rectangle &clip,gfx_element *gfx,
+ UINT32 code,UINT32 color, int flipx,int flipy,int offsx,int offsy,
+ int transparent_color, int alpha)
+{
+ const pen_t *pal = &m_palette->pen(gfx->colorbase() + gfx->granularity() * (color % gfx->colors()));
+ const UINT8 *source_base = gfx->get_data(code % gfx->elements());
+ int x_index_base, y_index, sx, sy, ex, ey;
+ int xinc, yinc;
+
+ xinc = flipx ? -1 : 1;
+ yinc = flipy ? -1 : 1;
+
+ x_index_base = flipx ? gfx->width()-1 : 0;
+ y_index = flipy ? gfx->height()-1 : 0;
+
+ /* start coordinates */
+ sx = offsx;
+ sy = offsy;
+
+ /* end coordinates */
+ ex = sx + gfx->width();
+ ey = sy + gfx->height();
+
+ /* clip left */
+ if (sx < clip.min_x)
+ {
+ int pixels = clip.min_x-sx;
+ sx += pixels;
+ x_index_base += xinc*pixels;
+ }
+
+ /* clip top */
+ if (sy < clip.min_y)
+ { int pixels = clip.min_y-sy;
+ sy += pixels;
+ y_index += yinc*pixels;
+ }
+
+ /* clip right */
+ if (ex > clip.max_x+1)
+ {
+ ex = clip.max_x+1;
+ }
+ /* clip bottom */
+ if (ey > clip.max_y+1)
+ {
+ ey = clip.max_y+1;
+ }
+
+ /* skip if inner loop doesn't draw anything */
+ if (ex > sx)
+ {
+ int x, y;
+
+ {
+ for (y = sy; y < ey; y++)
+ {
+ const UINT8 *source = source_base + y_index*gfx->rowbytes();
+ UINT32 *dest = &dest_bmp.pix32(y);
+ int x_index = x_index_base;
+ for (x = sx; x < ex; x++)
+ {
+ if(stv_vdp2_window_process(x,y))
+ {
+ int c = (source[x_index]);
+ if (c != transparent_color)
+ dest[x] = alpha_blend_r32( dest[x], pal[c], alpha );;
+ }
+
+ x_index += xinc;
+ }
+ y_index += yinc;
+ }
+ }
+ }
+}
+
+void saturn_state::stv_vdp2_drawgfx_transpen(bitmap_rgb32 &dest_bmp,const rectangle &clip,gfx_element *gfx,
+ UINT32 code,UINT32 color, int flipx,int flipy,int offsx,int offsy,
+ int transparent_color)
+{
+ const pen_t *pal = &m_palette->pen(gfx->colorbase() + gfx->granularity() * (color % gfx->colors()));
+ const UINT8 *source_base = gfx->get_data(code % gfx->elements());
+ int x_index_base, y_index, sx, sy, ex, ey;
+ int xinc, yinc;
+
+ xinc = flipx ? -1 : 1;
+ yinc = flipy ? -1 : 1;
+
+ x_index_base = flipx ? gfx->width()-1 : 0;
+ y_index = flipy ? gfx->height()-1 : 0;
+
+ /* start coordinates */
+ sx = offsx;
+ sy = offsy;
+
+ /* end coordinates */
+ ex = sx + gfx->width();
+ ey = sy + gfx->height();
+
+ /* clip left */
+ if (sx < clip.min_x)
+ {
+ int pixels = clip.min_x-sx;
+ sx += pixels;
+ x_index_base += xinc*pixels;
+ }
+
+ /* clip top */
+ if (sy < clip.min_y)
+ { int pixels = clip.min_y-sy;
+ sy += pixels;
+ y_index += yinc*pixels;
+ }
+
+ /* clip right */
+ if (ex > clip.max_x+1)
+ {
+ ex = clip.max_x+1;
+ }
+ /* clip bottom */
+ if (ey > clip.max_y+1)
+ {
+ ey = clip.max_y+1;
+ }
+
+ /* skip if inner loop doesn't draw anything */
+ if (ex > sx)
+ {
+ int x, y;
+
+ {
+ for (y = sy; y < ey; y++)
+ {
+ const UINT8 *source = source_base + y_index*gfx->rowbytes();
+ UINT32 *dest = &dest_bmp.pix32(y);
+ int x_index = x_index_base;
+ for (x = sx; x < ex; x++)
+ {
+ if(stv_vdp2_window_process(x,y))
+ {
+ int c = (source[x_index]);
+ if (c != transparent_color)
+ dest[x] = pal[c];
+ }
+
+ x_index += xinc;
+ }
+ y_index += yinc;
+ }
+ }
+ }
+}
+
+void saturn_state::draw_4bpp_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int xsize, ysize, xsize_mask, ysize_mask;
+ int xsrc,ysrc,xdst,ydst;
+ int src_offs;
+ UINT8* vram = m_vdp2.gfx_decode;
+ UINT32 map_offset = stv2_current_tilemap.bitmap_map * 0x20000;
+ int scrollx = stv2_current_tilemap.scrollx;
+ int scrolly = stv2_current_tilemap.scrolly;
+ UINT16 dot_data;
+ UINT16 pal_bank;
+
+ xsize = (stv2_current_tilemap.bitmap_size & 2) ? 1024 : 512;
+ ysize = (stv2_current_tilemap.bitmap_size & 1) ? 512 : 256;
+
+ xsize_mask = (stv2_current_tilemap.linescroll_enable) ? 1024 : xsize;
+ ysize_mask = (stv2_current_tilemap.vertical_linescroll_enable) ? 512 : ysize;
+
+ pal_bank = stv2_current_tilemap.bitmap_palette_number;
+ pal_bank+= stv2_current_tilemap.colour_ram_address_offset;
+ pal_bank&= 7;
+ pal_bank<<=8;
+ if(stv2_current_tilemap.fade_control & 1)
+ pal_bank += ((stv2_current_tilemap.fade_control & 2) ? (2*2048) : (2048));
+
+ for(ydst=cliprect.min_y;ydst<=cliprect.max_y;ydst++)
+ {
+ for(xdst=cliprect.min_x;xdst<=cliprect.max_x;xdst++)
+ {
+ if(!stv_vdp2_window_process(xdst,ydst))
+ continue;
+
+ xsrc = (xdst + scrollx) & (xsize_mask-1);
+ ysrc = (ydst + scrolly) & (ysize_mask-1);
+ src_offs = (xsrc + (ysrc*xsize));
+ src_offs/= 2;
+ src_offs += map_offset;
+ src_offs &= 0x7ffff;
+
+ dot_data = vram[src_offs] >> ((xsrc & 1) ? 0 : 4);
+ dot_data&= 0xf;
+
+ if ((dot_data != 0) || (stv2_current_tilemap.transparency == STV_TRANSPARENCY_NONE))
+ {
+ dot_data += pal_bank;
+
+ if ( stv2_current_tilemap.colour_calculation_enabled == 0 )
+ bitmap.pix32(ydst, xdst) = m_palette->pen(dot_data);
+ else
+ bitmap.pix32(ydst, xdst) = alpha_blend_r32(bitmap.pix32(ydst, xdst), m_palette->pen(dot_data), stv2_current_tilemap.alpha);
+ }
+ }
+ }
+}
+
+
+void saturn_state::draw_8bpp_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int xsize, ysize, xsize_mask, ysize_mask;
+ int xsrc,ysrc,xdst,ydst;
+ int src_offs;
+ UINT8* vram = m_vdp2.gfx_decode;
+ UINT32 map_offset = stv2_current_tilemap.bitmap_map * 0x20000;
+ int scrollx = stv2_current_tilemap.scrollx;
+ int scrolly = stv2_current_tilemap.scrolly;
+ UINT16 dot_data;
+ UINT16 pal_bank;
+ int xf, yf;
+
+ xsize = (stv2_current_tilemap.bitmap_size & 2) ? 1024 : 512;
+ ysize = (stv2_current_tilemap.bitmap_size & 1) ? 512 : 256;
+
+ xsize_mask = (stv2_current_tilemap.linescroll_enable) ? 1024 : xsize;
+ ysize_mask = (stv2_current_tilemap.vertical_linescroll_enable) ? 512 : ysize;
+
+ pal_bank = stv2_current_tilemap.bitmap_palette_number;
+ pal_bank+= stv2_current_tilemap.colour_ram_address_offset;
+ pal_bank&= 7;
+ pal_bank<<=8;
+ if(stv2_current_tilemap.fade_control & 1)
+ pal_bank += ((stv2_current_tilemap.fade_control & 2) ? (2*2048) : (2048));
+
+ for(ydst=cliprect.min_y;ydst<=cliprect.max_y;ydst++)
+ {
+ for(xdst=cliprect.min_x;xdst<=cliprect.max_x;xdst++)
+ {
+ if(!stv_vdp2_window_process(xdst,ydst))
+ continue;
+
+ xf = stv2_current_tilemap.incx * xdst;
+ xf>>=16;
+ yf = stv2_current_tilemap.incy * ydst;
+ yf>>=16;
+
+ xsrc = (xf + scrollx) & (xsize_mask-1);
+ ysrc = (yf + scrolly) & (ysize_mask-1);
+ src_offs = (xsrc + (ysrc*xsize));
+ src_offs += map_offset;
+ src_offs &= 0x7ffff;
+
+ dot_data = vram[src_offs];
+
+ if ((dot_data != 0) || (stv2_current_tilemap.transparency == STV_TRANSPARENCY_NONE))
+ {
+ dot_data += pal_bank;
+
+ if ( stv2_current_tilemap.colour_calculation_enabled == 0 )
+ bitmap.pix32(ydst, xdst) = m_palette->pen(dot_data);
+ else
+ bitmap.pix32(ydst, xdst) = alpha_blend_r32(bitmap.pix32(ydst, xdst), m_palette->pen(dot_data), stv2_current_tilemap.alpha);
+ }
+ }
+ }
+}
+
+void saturn_state::draw_11bpp_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int xsize, ysize, xsize_mask, ysize_mask;
+ int xsrc,ysrc,xdst,ydst;
+ int src_offs;
+ UINT8* vram = m_vdp2.gfx_decode;
+ UINT32 map_offset = stv2_current_tilemap.bitmap_map * 0x20000;
+ int scrollx = stv2_current_tilemap.scrollx;
+ int scrolly = stv2_current_tilemap.scrolly;
+ UINT16 dot_data;
+ UINT16 pal_bank;
+ int xf, yf;
+
+ xsize = (stv2_current_tilemap.bitmap_size & 2) ? 1024 : 512;
+ ysize = (stv2_current_tilemap.bitmap_size & 1) ? 512 : 256;
+
+ xsize_mask = (stv2_current_tilemap.linescroll_enable) ? 1024 : xsize;
+ ysize_mask = (stv2_current_tilemap.vertical_linescroll_enable) ? 512 : ysize;
+
+ pal_bank = 0;
+ if(stv2_current_tilemap.fade_control & 1)
+ pal_bank = ((stv2_current_tilemap.fade_control & 2) ? (2*2048) : (2048));
+
+ for(ydst=cliprect.min_y;ydst<=cliprect.max_y;ydst++)
+ {
+ for(xdst=cliprect.min_x;xdst<=cliprect.max_x;xdst++)
+ {
+ if(!stv_vdp2_window_process(xdst,ydst))
+ continue;
+
+ xf = stv2_current_tilemap.incx * xdst;
+ xf>>=16;
+ yf = stv2_current_tilemap.incy * ydst;
+ yf>>=16;
+
+ xsrc = (xf + scrollx) & (xsize_mask-1);
+ ysrc = (yf + scrolly) & (ysize_mask-1);
+ src_offs = (xsrc + (ysrc*xsize));
+ src_offs *= 2;
+ src_offs += map_offset;
+ src_offs &= 0x7ffff;
+
+ dot_data = ((vram[src_offs]<<8)|(vram[src_offs+1]<<0)) & 0x7ff;
+
+ if ((dot_data != 0) || (stv2_current_tilemap.transparency == STV_TRANSPARENCY_NONE))
+ {
+ dot_data += pal_bank;
+
+ if ( stv2_current_tilemap.colour_calculation_enabled == 0 )
+ bitmap.pix32(ydst, xdst) = m_palette->pen(dot_data);
+ else
+ bitmap.pix32(ydst, xdst) = alpha_blend_r32(bitmap.pix32(ydst, xdst), m_palette->pen(dot_data), stv2_current_tilemap.alpha);
+ }
+ }
+ }
+}
+
+
+void saturn_state::draw_rgb15_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int xsize, ysize, xsize_mask, ysize_mask;
+ int xsrc,ysrc,xdst,ydst;
+ int src_offs;
+ UINT8* vram = m_vdp2.gfx_decode;
+ UINT32 map_offset = stv2_current_tilemap.bitmap_map * 0x20000;
+ int scrollx = stv2_current_tilemap.scrollx;
+ int scrolly = stv2_current_tilemap.scrolly;
+ int r,g,b;
+ UINT16 dot_data;
+ int xf, yf;
+
+ xsize = (stv2_current_tilemap.bitmap_size & 2) ? 1024 : 512;
+ ysize = (stv2_current_tilemap.bitmap_size & 1) ? 512 : 256;
+
+ xsize_mask = (stv2_current_tilemap.linescroll_enable) ? 1024 : xsize;
+ ysize_mask = (stv2_current_tilemap.vertical_linescroll_enable) ? 512 : ysize;
+
+ for(ydst=cliprect.min_y;ydst<=cliprect.max_y;ydst++)
+ {
+ for(xdst=cliprect.min_x;xdst<=cliprect.max_x;xdst++)
+ {
+ if(!stv_vdp2_window_process(xdst,ydst))
+ continue;
+
+ xf = stv2_current_tilemap.incx * xdst;
+ xf>>=16;
+ yf = stv2_current_tilemap.incy * ydst;
+ yf>>=16;
+
+ xsrc = (xf + scrollx) & (xsize_mask-1);
+ ysrc = (yf + scrolly) & (ysize_mask-1);
+ src_offs = (xsrc + (ysrc*xsize));
+ src_offs *= 2;
+ src_offs += map_offset;
+ src_offs &= 0x7ffff;
+
+ dot_data =(vram[src_offs]<<8)|(vram[src_offs+1]<<0);
+
+ if ((dot_data & 0x8000) || (stv2_current_tilemap.transparency == STV_TRANSPARENCY_NONE))
+ {
+ b = pal5bit((dot_data & 0x7c00) >> 10);
+ g = pal5bit((dot_data & 0x03e0) >> 5);
+ r = pal5bit((dot_data & 0x001f) >> 0);
+
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset(&r,&g,&b,stv2_current_tilemap.fade_control & 2);
+
+ if ( stv2_current_tilemap.colour_calculation_enabled == 0 )
+ bitmap.pix32(ydst, xdst) = rgb_t(r, g, b);
+ else
+ bitmap.pix32(ydst, xdst) = alpha_blend_r32( bitmap.pix32(ydst, xdst), rgb_t(r, g, b), stv2_current_tilemap.alpha );
+ }
+ }
+ }
+}
+
+void saturn_state::draw_rgb32_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int xsize, ysize, xsize_mask, ysize_mask;
+ int xsrc,ysrc,xdst,ydst;
+ int src_offs;
+ UINT8* vram = m_vdp2.gfx_decode;
+ UINT32 map_offset = stv2_current_tilemap.bitmap_map * 0x20000;
+ int scrollx = stv2_current_tilemap.scrollx;
+ int scrolly = stv2_current_tilemap.scrolly;
+ int r,g,b;
+ UINT32 dot_data;
+ int xf, yf;
+
+ xsize = (stv2_current_tilemap.bitmap_size & 2) ? 1024 : 512;
+ ysize = (stv2_current_tilemap.bitmap_size & 1) ? 512 : 256;
+
+ xsize_mask = (stv2_current_tilemap.linescroll_enable) ? 1024 : xsize;
+ ysize_mask = (stv2_current_tilemap.vertical_linescroll_enable) ? 512 : ysize;
+
+ for(ydst=cliprect.min_y;ydst<=cliprect.max_y;ydst++)
+ {
+ for(xdst=cliprect.min_x;xdst<=cliprect.max_x;xdst++)
+ {
+ if(!stv_vdp2_window_process(xdst,ydst))
+ continue;
+
+ xf = stv2_current_tilemap.incx * xdst;
+ xf>>=16;
+ yf = stv2_current_tilemap.incy * ydst;
+ yf>>=16;
+
+ xsrc = (xf + scrollx) & (xsize_mask-1);
+ ysrc = (yf + scrolly) & (ysize_mask-1);
+ src_offs = (xsrc + (ysrc*xsize));
+ src_offs *= 4;
+ src_offs += map_offset;
+ src_offs &= 0x7ffff;
+
+ dot_data = (vram[src_offs+0]<<24)|(vram[src_offs+1]<<16)|(vram[src_offs+2]<<8)|(vram[src_offs+3]<<0);
+
+ if ((dot_data & 0x80000000) || (stv2_current_tilemap.transparency == STV_TRANSPARENCY_NONE))
+ {
+ b = ((dot_data & 0x00ff0000) >> 16);
+ g = ((dot_data & 0x0000ff00) >> 8);
+ r = ((dot_data & 0x000000ff) >> 0);
+
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset(&r,&g,&b,stv2_current_tilemap.fade_control & 2);
+
+ if ( stv2_current_tilemap.colour_calculation_enabled == 0 )
+ bitmap.pix32(ydst, xdst) = rgb_t(r, g, b);
+ else
+ bitmap.pix32(ydst, xdst) = alpha_blend_r32( bitmap.pix32(ydst, xdst), rgb_t(r, g, b), stv2_current_tilemap.alpha );
+ }
+ }
+ }
+}
+
+
+void saturn_state::stv_vdp2_draw_basic_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ if (!stv2_current_tilemap.enabled) return;
+
+ /* new bitmap code, supposed to rewrite the old one. Not supposed to be clean, but EFFICIENT! */
+ if(stv2_current_tilemap.incx == 0x10000 && stv2_current_tilemap.incy == 0x10000)
+ {
+ switch(stv2_current_tilemap.colour_depth)
+ {
+ case 0: draw_4bpp_bitmap(bitmap,cliprect); return;
+ case 1: draw_8bpp_bitmap(bitmap,cliprect); return;
+ case 2: draw_11bpp_bitmap(bitmap, cliprect); return;
+ case 3: draw_rgb15_bitmap(bitmap,cliprect); return;
+ case 4: draw_rgb32_bitmap(bitmap,cliprect); return;
+ }
+
+ /* intentional fall-through*/
+ popmessage("%d %s %s %s",stv2_current_tilemap.colour_depth,
+ stv2_current_tilemap.transparency == STV_TRANSPARENCY_NONE ? "no trans" : "trans",
+ stv2_current_tilemap.colour_calculation_enabled ? "cc" : "no cc",
+ (stv2_current_tilemap.incx == 0x10000 && stv2_current_tilemap.incy == 0x10000) ? "no zoom" : "zoom");
+ }
+ else
+ {
+ switch(stv2_current_tilemap.colour_depth)
+ {
+ // case 0: draw_4bpp_bitmap(bitmap,cliprect); return;
+ case 1: draw_8bpp_bitmap(bitmap,cliprect); return;
+ // case 2: draw_11bpp_bitmap(bitmap, cliprect); return;
+ case 3: draw_rgb15_bitmap(bitmap,cliprect); return;
+ case 4: draw_rgb32_bitmap(bitmap,cliprect); return;
+ }
+
+ /* intentional fall-through*/
+ popmessage("%d %s %s %s",stv2_current_tilemap.colour_depth,
+ stv2_current_tilemap.transparency == STV_TRANSPARENCY_NONE ? "no trans" : "trans",
+ stv2_current_tilemap.colour_calculation_enabled ? "cc" : "no cc",
+ (stv2_current_tilemap.incx == 0x10000 && stv2_current_tilemap.incy == 0x10000) ? "no zoom" : "zoom");
+ }
+}
+
+ /*---------------------------------------------------------------------------
+ | Plane Size | Pattern Name Data Size | Character Size | Map Bits / Address |
+ ----------------------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 6-0 * 0x02000 |
+ | | 1 word |-------------------------------------|
+ | | | 2 H x 2 V | bits 8-0 * 0x00800 |
+ | 1 H x 1 V ---------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 5-0 * 0x04000 |
+ | | 2 words |-------------------------------------|
+ | | | 2 H x 2 V | bits 7-0 * 0x01000 |
+ -----------------------------------------------------------------------------
+ | | | 1 H x 1 V | bits 6-1 * 0x04000 |
+ | | 1 word |-------------------------------------|
+ | | | 2 H x 2 V | bits 8-1 * 0x01000 |
+ | 2 H x 1 V ---------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 5-1 * 0x08000 |
+ | | 2 words |-------------------------------------|
+ | | | 2 H x 2 V | bits 7-1 * 0x02000 |
+ -----------------------------------------------------------------------------
+ | | | 1 H x 1 V | bits 6-2 * 0x08000 |
+ | | 1 word |-------------------------------------|
+ | | | 2 H x 2 V | bits 8-2 * 0x02000 |
+ | 2 H x 2 V ---------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 5-2 * 0x10000 |
+ | | 2 words |-------------------------------------|
+ | | | 2 H x 2 V | bits 7-2 * 0x04000 |
+ --the-highest-bit-is-ignored-if-vram-is-only-4mbits------------------------*/
+
+
+/*
+4.2 Sega's Cell / Character Pattern / Page / Plane / Map system, aka a rather annoying thing that makes optimizations hard
+ (this is only for the normal tilemaps at the moment, i haven't even thought about the ROZ ones)
+
+Tiles:
+
+Cells are 8x8 gfx stored in video ram, they can be of various colour depths
+
+Character Patterns can be 8x8 or 16x16 (1 hcell x 1 vcell or 2 hcell x 2 vcell)
+ (a 16x16 character pattern is 4 8x8 cells put together)
+
+A page is made up of 64x64 cells, thats 64x64 character patterns in 8x8 mode or 32x32 character patterns in 16x16 mode.
+ 64 * 8 = 512 (0x200)
+ 32 * 16 = 512 (0x200)
+A page is _always_ 512 (0x200) pixels in each direction
+
+in 1 word mode a 32*16 x 32*16 page is 0x0800 bytes
+in 1 word mode a 64*8 x 64*8 page is 0x2000 bytes
+in 2 word mode a 32*16 x 32*16 page is 0x1000 bytes
+in 2 word mode a 64*8 x 64*8 page is 0x4000 bytes
+
+either 1, 2 or 4 pages make each plane depending on the plane size register (per tilemap)
+ therefore each plane is either
+ 64 * 8 * 1 x 64 * 8 * 1 (512 x 512)
+ 64 * 8 * 2 x 64 * 8 * 1 (1024 x 512)
+ 64 * 8 * 2 x 64 * 8 * 2 (1024 x 1024)
+
+ 32 * 16 * 1 x 32 * 16 * 1 (512 x 512)
+ 32 * 16 * 2 x 32 * 16 * 1 (1024 x 512)
+ 32 * 16 * 2 x 32 * 16 * 2 (1024 x 1024)
+
+map is always enabled?
+ map is a 2x2 arrangement of planes, all 4 of the planes can be the same.
+
+*/
+
+void saturn_state::stv_vdp2_get_map_page( int x, int y, int *_map, int *_page )
+{
+ int page = 0;
+ int map = 0;
+
+ if ( stv2_current_tilemap.map_count == 4 )
+ {
+ if ( stv2_current_tilemap.tile_size == 0 )
+ {
+ if ( stv2_current_tilemap.plane_size & 1 )
+ {
+ page = ((x >> 6) & 1);
+ map = (x >> 7) & 1;
+ }
+ else
+ {
+ map = (x >> 6) & 1;
+ }
+
+ if ( stv2_current_tilemap.plane_size & 2 )
+ {
+ page |= ((y >> (6-1)) & 2);
+ map |= ((y >> (7-1)) & 2);
+ }
+ else
+ {
+ map |= ((y >> (6-1)) & 2);
+ }
+ }
+ else
+ {
+ if ( stv2_current_tilemap.plane_size & 1 )
+ {
+ page = ((x >> 5) & 1);
+ map = (x >> 6) & 1;
+ }
+ else
+ {
+ map = (x >> 5) & 1;
+ }
+
+ if ( stv2_current_tilemap.plane_size & 2 )
+ {
+ page |= ((y >> (5 - 1)) & 2);
+ map |= ((y >> (6-1)) & 2);
+ }
+ else
+ {
+ map |= ((y >> (5-1)) & 2);
+ }
+ }
+ }
+ else //16
+ {
+ if ( stv2_current_tilemap.tile_size == 0 )
+ {
+ if ( stv2_current_tilemap.plane_size & 1 )
+ {
+ page = ((x >> 6) & 1);
+ map = (x >> 7) & 3;
+ }
+ else
+ {
+ map = (x >> 6) & 3;
+ }
+
+ if ( stv2_current_tilemap.plane_size & 2 )
+ {
+ page |= ((y >> (6-1)) & 2);
+ map |= ((y >> (7-2)) & 12);
+ }
+ else
+ {
+ map |= ((y >> (6-2)) & 12);
+ }
+ }
+ else
+ {
+ if ( stv2_current_tilemap.plane_size & 1 )
+ {
+ page = ((x >> 5) & 1);
+ map = (x >> 6) & 3;
+ }
+ else
+ {
+ map = (x >> 5) & 3;
+ }
+
+ if ( stv2_current_tilemap.plane_size & 2 )
+ {
+ page |= ((y >> (5 - 1)) & 2);
+ map |= ((y >> (6-2)) & 12);
+ }
+ else
+ {
+ map |= ((y >> (5-2)) & 12);
+ }
+ }
+ }
+ *_page = page;
+ *_map = map;
+}
+
+void saturn_state::stv_vdp2_draw_basic_tilemap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ /* hopefully this is easier to follow than it is efficient .. */
+
+ /* I call character patterns tiles .. even if they represent up to 4 tiles */
+
+ /* Page variables */
+ int pgtiles_x, pgpixels_x;
+ int pgtiles_y, pgpixels_y;
+ int pgsize_bytes, pgsize_dwords;
+
+ /* Plane Variables */
+ int pltiles_x, plpixels_x;
+ int pltiles_y, plpixels_y;
+ int plsize_bytes/*, plsize_dwords*/;
+
+ /* Map Variables */
+ int mptiles_x, mppixels_x;
+ int mptiles_y, mppixels_y;
+ int mpsize_bytes, mpsize_dwords;
+
+ /* work Variables */
+ int i, x, y;
+ int base[16];
+
+ int scalex,scaley;
+ int tilesizex, tilesizey;
+ int drawypos, drawxpos;
+
+ int tilecodemin = 0x10000000, tilecodemax = 0;
+
+ if ( stv2_current_tilemap.incx == 0 || stv2_current_tilemap.incy == 0 ) return;
+
+ if ( stv2_current_tilemap.colour_calculation_enabled == 1 )
+ {
+ if ( STV_VDP2_CCMD )
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_ADD_BLEND;
+ }
+ else
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_ALPHA;
+ }
+ }
+
+ scalex = (INT32)((INT64)S64(0x100000000) / (INT64)stv2_current_tilemap.incx);
+ scaley = (INT32)((INT64)S64(0x100000000) / (INT64)stv2_current_tilemap.incy);
+ tilesizex = scalex * 8;
+ tilesizey = scaley * 8;
+ drawypos = drawxpos = 0;
+
+ /* Calculate the Number of tiles for x / y directions of each page (actually these will be the same */
+ /* (2-stv2_current_tilemap.tile_size) << 5) */
+ pgtiles_x = ((2-stv2_current_tilemap.tile_size) << 5); // 64 (8x8 mode) or 32 (16x16 mode)
+ pgtiles_y = ((2-stv2_current_tilemap.tile_size) << 5); // 64 (8x8 mode) or 32 (16x16 mode)
+
+ /* Calculate the Page Size in BYTES */
+ /* 64 * 64 * (1 * 2) = 0x2000 bytes
+ 32 * 32 * (1 * 2) = 0x0800 bytes
+ 64 * 64 * (2 * 2) = 0x4000 bytes
+ 32 * 32 * (2 * 2) = 0x1000 bytes */
+
+ pgsize_bytes = (pgtiles_x * pgtiles_y) * ((2-stv2_current_tilemap.pattern_data_size)*2);
+
+ /*---------------------------------------------------------------------------
+ | Plane Size | Pattern Name Data Size | Character Size | Map Bits / Address |
+ ----------------------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 6-0 * 0x02000 |
+ | | 1 word |-------------------------------------|
+ | | | 2 H x 2 V | bits 8-0 * 0x00800 |
+ | 1 H x 1 V ---------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 5-0 * 0x04000 |
+ | | 2 words |-------------------------------------|
+ | | | 2 H x 2 V | bits 7-0 * 0x01000 |
+ ---------------------------------------------------------------------------*/
+
+
+ /* Page Dimensions are always 0x200 pixes (512x512) */
+ pgpixels_x = 0x200;
+ pgpixels_y = 0x200;
+
+ /* Work out the Plane Size in tiles and Plane Dimensions (pixels) */
+ switch (stv2_current_tilemap.plane_size & 3)
+ {
+ case 0: // 1 page * 1 page
+ pltiles_x = pgtiles_x;
+ plpixels_x = pgpixels_x;
+ pltiles_y = pgtiles_y;
+ plpixels_y = pgpixels_y;
+ break;
+
+ case 1: // 2 pages * 1 page
+ pltiles_x = pgtiles_x * 2;
+ plpixels_x = pgpixels_x * 2;
+ pltiles_y = pgtiles_y;
+ plpixels_y = pgpixels_y;
+ break;
+
+ case 3: // 2 pages * 2 pages
+ pltiles_x = pgtiles_x * 2;
+ plpixels_x = pgpixels_x * 2;
+ pltiles_y = pgtiles_y * 2;
+ plpixels_y = pgpixels_y * 2;
+ break;
+
+ default:
+ // illegal
+ pltiles_x = pgtiles_x;
+ plpixels_x = pgpixels_x;
+ pltiles_y = pgtiles_y * 2;
+ plpixels_y = pgpixels_y * 2;
+ break;
+ }
+
+ /* Plane Size in BYTES */
+ /* still the same as before
+ (64 * 1) * (64 * 1) * (1 * 2) = 0x02000 bytes
+ (32 * 1) * (32 * 1) * (1 * 2) = 0x00800 bytes
+ (64 * 1) * (64 * 1) * (2 * 2) = 0x04000 bytes
+ (32 * 1) * (32 * 1) * (2 * 2) = 0x01000 bytes
+ changed
+ (64 * 2) * (64 * 1) * (1 * 2) = 0x04000 bytes
+ (32 * 2) * (32 * 1) * (1 * 2) = 0x01000 bytes
+ (64 * 2) * (64 * 1) * (2 * 2) = 0x08000 bytes
+ (32 * 2) * (32 * 1) * (2 * 2) = 0x02000 bytes
+ changed
+ (64 * 2) * (64 * 1) * (1 * 2) = 0x08000 bytes
+ (32 * 2) * (32 * 1) * (1 * 2) = 0x02000 bytes
+ (64 * 2) * (64 * 1) * (2 * 2) = 0x10000 bytes
+ (32 * 2) * (32 * 1) * (2 * 2) = 0x04000 bytes
+ */
+
+ plsize_bytes = (pltiles_x * pltiles_y) * ((2-stv2_current_tilemap.pattern_data_size)*2);
+
+ /*---------------------------------------------------------------------------
+ | Plane Size | Pattern Name Data Size | Character Size | Map Bits / Address |
+ -----------------------------------------------------------------------------
+ | 1 H x 1 V see above, nothing has changed |
+ -----------------------------------------------------------------------------
+ | | | 1 H x 1 V | bits 6-1 * 0x04000 |
+ | | 1 word |-------------------------------------|
+ | | | 2 H x 2 V | bits 8-1 * 0x01000 |
+ | 2 H x 1 V ---------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 5-1 * 0x08000 |
+ | | 2 words |-------------------------------------|
+ | | | 2 H x 2 V | bits 7-1 * 0x02000 |
+ -----------------------------------------------------------------------------
+ | | | 1 H x 1 V | bits 6-2 * 0x08000 |
+ | | 1 word |-------------------------------------|
+ | | | 2 H x 2 V | bits 8-2 * 0x02000 |
+ | 2 H x 2 V ---------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 5-2 * 0x10000 |
+ | | 2 words |-------------------------------------|
+ | | | 2 H x 2 V | bits 7-2 * 0x04000 |
+ --the-highest-bit-is-ignored-if-vram-is-only-4mbits------------------------*/
+
+
+ /* Work out the Map Sizes in tiles, Map Dimensions */
+ /* maps are always enabled? */
+ if ( stv2_current_tilemap.map_count == 4 )
+ {
+ mptiles_x = pltiles_x * 2;
+ mptiles_y = pltiles_y * 2;
+ mppixels_x = plpixels_x * 2;
+ mppixels_y = plpixels_y * 2;
+ }
+ else
+ {
+ mptiles_x = pltiles_x * 4;
+ mptiles_y = pltiles_y * 4;
+ mppixels_x = plpixels_x * 4;
+ mppixels_y = plpixels_y * 4;
+ }
+
+ /* Map Size in BYTES */
+ mpsize_bytes = (mptiles_x * mptiles_y) * ((2-stv2_current_tilemap.pattern_data_size)*2);
+
+
+ /*-----------------------------------------------------------------------------------------------------------
+ | | | 1 H x 1 V | bits 6-1 (upper mask 0x07f) (0x1ff >> 2) * 0x04000 |
+ | | 1 word |---------------------------------------------------------------------|
+ | | | 2 H x 2 V | bits 8-1 (upper mask 0x1ff) (0x1ff >> 0) * 0x01000 |
+ | 2 H x 1 V -----------------------------------------------------------------------------------------------|
+ | | | 1 H x 1 V | bits 5-1 (upper mask 0x03f) (0x1ff >> 3) * 0x08000 |
+ | | 2 words |---------------------------------------------------------------------|
+ | | | 2 H x 2 V | bits 7-1 (upper mask 0x0ff) (0x1ff >> 1) * 0x02000 |
+ -------------------------------------------------------------------------------------------------------------
+ lower mask = ~stv2_current_tilemap.plane_size
+ -----------------------------------------------------------------------------------------------------------*/
+
+ /* Precalculate bases from MAP registers */
+ for (i = 0; i < stv2_current_tilemap.map_count; i++)
+ {
+ static const int shifttable[4] = {0,1,2,2};
+
+ int uppermask, uppermaskshift;
+
+ uppermaskshift = (1-stv2_current_tilemap.pattern_data_size) | ((1-stv2_current_tilemap.tile_size)<<1);
+ uppermask = 0x1ff >> uppermaskshift;
+
+ base[i] = ((stv2_current_tilemap.map_offset[i] & uppermask) >> shifttable[stv2_current_tilemap.plane_size]) * plsize_bytes;
+
+ base[i] &= 0x7ffff; /* shienryu needs this for the text layer, is there a problem elsewhere or is it just right without the ram cart */
+
+ base[i] = base[i] / 4; // convert bytes to DWORDS
+ }
+
+ /* other bits */
+ //stv2_current_tilemap.trans_enabled = stv2_current_tilemap.trans_enabled ? STV_TRANSPARENCY_NONE : STV_TRANSPARENCY_PEN;
+ stv2_current_tilemap.scrollx &= mppixels_x-1;
+ stv2_current_tilemap.scrolly &= mppixels_y-1;
+
+ pgsize_dwords = pgsize_bytes /4;
+ //plsize_dwords = plsize_bytes /4;
+ mpsize_dwords = mpsize_bytes /4;
+
+// if (stv2_current_tilemap.layer_name==3) popmessage ("well this is a bit %08x", stv2_current_tilemap.map_offset[0]);
+// if (stv2_current_tilemap.layer_name==3) popmessage ("well this is a bit %08x %08x %08x %08x", stv2_current_tilemap.plane_size, pgtiles_x, pltiles_x, mptiles_x);
+
+ if (!stv2_current_tilemap.enabled) return; // stop right now if its disabled ...
+
+ /* most things we need (or don't need) to work out are now worked out */
+
+ for (y = 0; y<mptiles_y; y++) {
+ int ypageoffs;
+ int page, map, newbase, offs, data;
+ int tilecode, flipyx, pal, gfx = 0;
+
+ map = 0 ; page = 0 ;
+ if ( y == 0 )
+ {
+ int drawyposinc = tilesizey*(stv2_current_tilemap.tile_size ? 2 : 1);
+ drawypos = -(stv2_current_tilemap.scrolly*scaley);
+ while( ((drawypos + drawyposinc) >> 16) < cliprect.min_y )
+ {
+ drawypos += drawyposinc;
+ y++;
+ }
+ mptiles_y += y;
+ }
+ else
+ {
+ drawypos += tilesizey*(stv2_current_tilemap.tile_size ? 2 : 1);
+ }
+ if ((drawypos >> 16) > cliprect.max_y) break;
+
+ ypageoffs = y & (pgtiles_y-1);
+
+ for (x = 0; x<mptiles_x; x++) {
+ int xpageoffs;
+ int tilecodespacing = 1;
+
+ if ( x == 0 )
+ {
+ int drawxposinc = tilesizex*(stv2_current_tilemap.tile_size ? 2 : 1);
+ drawxpos = -(stv2_current_tilemap.scrollx*scalex);
+ while( ((drawxpos + drawxposinc) >> 16) < cliprect.min_x )
+ {
+ drawxpos += drawxposinc;
+ x++;
+ }
+ mptiles_x += x;
+ }
+ else
+ {
+ drawxpos+=tilesizex*(stv2_current_tilemap.tile_size ? 2 : 1);
+ }
+ if ( (drawxpos >> 16) > cliprect.max_x ) break;
+
+ xpageoffs = x & (pgtiles_x-1);
+
+ stv_vdp2_get_map_page(x,y,&map,&page);
+
+ newbase = base[map] + page * pgsize_dwords;
+ offs = (ypageoffs * pgtiles_x) + xpageoffs;
+
+/* GET THE TILE INFO ... */
+ /* 1 word per tile mode with supplement bits */
+ if (stv2_current_tilemap.pattern_data_size ==1)
+ {
+ data = m_vdp2_vram[newbase + offs/2];
+ data = (offs&1) ? (data & 0x0000ffff) : ((data & 0xffff0000) >> 16);
+
+ /* Supplement Mode 12 bits, no flip */
+ if (stv2_current_tilemap.character_number_supplement == 1)
+ {
+/* no flip */ flipyx = 0;
+/* 8x8 */ if (stv2_current_tilemap.tile_size==0) tilecode = (data & 0x0fff) + ( (stv2_current_tilemap.supplementary_character_bits&0x1c) << 10);
+/* 16x16 */ else tilecode = ((data & 0x0fff) << 2) + (stv2_current_tilemap.supplementary_character_bits&0x03) + ((stv2_current_tilemap.supplementary_character_bits&0x10) << 10);
+ }
+ /* Supplement Mode 10 bits, with flip */
+ else
+ {
+/* flip bits */ flipyx = (data & 0x0c00) >> 10;
+/* 8x8 */ if (stv2_current_tilemap.tile_size==0) tilecode = (data & 0x03ff) + ( (stv2_current_tilemap.supplementary_character_bits) << 10);
+/* 16x16 */ else tilecode = ((data & 0x03ff) <<2) + (stv2_current_tilemap.supplementary_character_bits&0x03) + ((stv2_current_tilemap.supplementary_character_bits&0x1c) << 10);
+ }
+
+/*>16cols*/ if (stv2_current_tilemap.colour_depth != 0) pal = ((data & 0x7000)>>8);
+/*16 cols*/ else pal = ((data & 0xf000)>>12) +( (stv2_current_tilemap.supplementary_palette_bits) << 4);
+
+ }
+ /* 2 words per tile, no supplement bits */
+ else
+ {
+ data = m_vdp2_vram[newbase + offs];
+ tilecode = (data & 0x00007fff);
+ pal = (data & 0x007f0000)>>16;
+ // specialc = (data & 0x10000000)>>28;
+ flipyx = (data & 0xc0000000)>>30;
+ }
+/* WE'VE GOT THE TILE INFO ... */
+
+ if ( tilecode < tilecodemin ) tilecodemin = tilecode;
+ if ( tilecode > tilecodemax ) tilecodemax = tilecode;
+
+/* DECODE ANY TILES WE NEED TO DECODE */
+
+ pal += stv2_current_tilemap.colour_ram_address_offset<< 4; // bios uses this ..
+
+ /*Enable fading bit*/
+ if(stv2_current_tilemap.fade_control & 1)
+ {
+ /*Select fading bit*/
+ pal += ((stv2_current_tilemap.fade_control & 2) ? (0x100) : (0x80));
+ }
+
+ if (stv2_current_tilemap.colour_depth == 1)
+ {
+ gfx = 2;
+ pal = pal >>4;
+ tilecode &=0x7fff;
+ if (tilecode == 0x7fff) tilecode--; /* prevents crash but unsure what should happen; wrapping? */
+ tilecodespacing = 2;
+ }
+ else if (stv2_current_tilemap.colour_depth == 0)
+ {
+ gfx = 0;
+ tilecode &=0x7fff;
+ tilecodespacing = 1;
+ }
+/* TILES ARE NOW DECODED */
+
+ if(!STV_VDP2_VRAMSZ)
+ tilecode &= 0x3fff;
+
+/* DRAW! */
+ if(stv2_current_tilemap.incx != 0x10000 ||
+ stv2_current_tilemap.incy != 0x10000 ||
+ stv2_current_tilemap.transparency == STV_TRANSPARENCY_ADD_BLEND )
+ {
+#define SCR_TILESIZE_X (((drawxpos + tilesizex) >> 16) - (drawxpos >> 16))
+#define SCR_TILESIZE_X1(startx) (((drawxpos + (startx) + tilesizex) >> 16) - ((drawxpos + (startx))>>16))
+#define SCR_TILESIZE_Y (((drawypos + tilesizey) >> 16) - (drawypos >> 16))
+#define SCR_TILESIZE_Y1(starty) (((drawypos + (starty) + tilesizey) >> 16) - ((drawypos + (starty))>>16))
+ if (stv2_current_tilemap.tile_size==1)
+ {
+ if ( stv2_current_tilemap.colour_depth == 4 )
+ {
+ popmessage("Unsupported tilemap gfx zoom color depth = 4, tile size = 1, contact MAMEdev");
+ }
+ else if ( stv2_current_tilemap.colour_depth == 3 )
+ {
+ /* RGB555 */
+ stv_vdp2_drawgfxzoom_rgb555(bitmap,cliprect,tilecode+(0+(flipyx&1)+(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos >> 16, drawypos >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X, SCR_TILESIZE_Y,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfxzoom_rgb555(bitmap,cliprect,tilecode+(1-(flipyx&1)+(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,(drawxpos+tilesizex) >> 16,drawypos >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X1(tilesizex), SCR_TILESIZE_Y,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfxzoom_rgb555(bitmap,cliprect,tilecode+(2+(flipyx&1)-(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos >> 16,(drawypos+tilesizey) >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X, SCR_TILESIZE_Y1(tilesizey),stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfxzoom_rgb555(bitmap,cliprect,tilecode+(3-(flipyx&1)-(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,(drawxpos+tilesizex)>> 16,(drawypos+tilesizey) >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X1(tilesizex), SCR_TILESIZE_Y1(tilesizey),stv2_current_tilemap.alpha);
+ }
+ else
+ {
+ /* normal */
+ stv_vdp2_drawgfxzoom(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(0+(flipyx&1)+(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos >> 16, drawypos >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X, SCR_TILESIZE_Y,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfxzoom(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(1-(flipyx&1)+(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,(drawxpos+tilesizex) >> 16,drawypos >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X1(tilesizex), SCR_TILESIZE_Y,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfxzoom(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(2+(flipyx&1)-(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos >> 16,(drawypos+tilesizey) >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X, SCR_TILESIZE_Y1(tilesizey),stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfxzoom(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(3-(flipyx&1)-(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,(drawxpos+tilesizex)>> 16,(drawypos+tilesizey) >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X1(tilesizex), SCR_TILESIZE_Y1(tilesizey),stv2_current_tilemap.alpha);
+ }
+ }
+ else
+ {
+ if ( stv2_current_tilemap.colour_depth == 4 )
+ popmessage("Unsupported tilemap gfx zoom color depth = 4, tile size = 0, contact MAMEdev");
+ else if ( stv2_current_tilemap.colour_depth == 3)
+ {
+ stv_vdp2_drawgfxzoom_rgb555(bitmap,cliprect,tilecode,pal,flipyx&1,flipyx&2, drawxpos >> 16, drawypos >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X,SCR_TILESIZE_Y,stv2_current_tilemap.alpha);
+ }
+ else
+ stv_vdp2_drawgfxzoom(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode,pal,flipyx&1,flipyx&2, drawxpos >> 16, drawypos >> 16,stv2_current_tilemap.transparency,0,scalex,scaley,SCR_TILESIZE_X,SCR_TILESIZE_Y,stv2_current_tilemap.alpha);
+ }
+ }
+ else
+ {
+ int olddrawxpos, olddrawypos;
+ olddrawxpos = drawxpos; drawxpos >>= 16;
+ olddrawypos = drawypos; drawypos >>= 16;
+ if (stv2_current_tilemap.tile_size==1)
+ {
+ if ( stv2_current_tilemap.colour_depth == 4 )
+ {
+ /* normal */
+ stv_vdp2_drawgfx_rgb888(bitmap,cliprect,tilecode+(0+(flipyx&1)+(flipyx&2))*4,flipyx&1,flipyx&2,drawxpos, drawypos,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_rgb888(bitmap,cliprect,tilecode+(1-(flipyx&1)+(flipyx&2))*4,flipyx&1,flipyx&2,drawxpos+8,drawypos,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_rgb888(bitmap,cliprect,tilecode+(2+(flipyx&1)-(flipyx&2))*4,flipyx&1,flipyx&2,drawxpos,drawypos+8,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_rgb888(bitmap,cliprect,tilecode+(3-(flipyx&1)-(flipyx&2))*4,flipyx&1,flipyx&2,drawxpos+8,drawypos+8,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ }
+ else if ( stv2_current_tilemap.colour_depth == 3 )
+ {
+ /* normal */
+ stv_vdp2_drawgfx_rgb555(bitmap,cliprect,tilecode+(0+(flipyx&1)+(flipyx&2))*4,flipyx&1,flipyx&2,drawxpos, drawypos,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_rgb555(bitmap,cliprect,tilecode+(1-(flipyx&1)+(flipyx&2))*4,flipyx&1,flipyx&2,drawxpos+8,drawypos,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_rgb555(bitmap,cliprect,tilecode+(2+(flipyx&1)-(flipyx&2))*4,flipyx&1,flipyx&2,drawxpos,drawypos+8,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_rgb555(bitmap,cliprect,tilecode+(3-(flipyx&1)-(flipyx&2))*4,flipyx&1,flipyx&2,drawxpos+8,drawypos+8,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ }
+ else if (stv2_current_tilemap.transparency == STV_TRANSPARENCY_ALPHA)
+ {
+ /* alpha */
+ stv_vdp2_drawgfx_alpha(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(0+(flipyx&1)+(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos, drawypos,0,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_alpha(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(1-(flipyx&1)+(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos+8,drawypos,0,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_alpha(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(2+(flipyx&1)-(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos,drawypos+8,0,stv2_current_tilemap.alpha);
+ stv_vdp2_drawgfx_alpha(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(3-(flipyx&1)-(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos+8,drawypos+8,0,stv2_current_tilemap.alpha);
+ }
+ else
+ {
+ /* normal */
+ stv_vdp2_drawgfx_transpen(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(0+(flipyx&1)+(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos, drawypos,(stv2_current_tilemap.transparency==STV_TRANSPARENCY_PEN)?0:-1);
+ stv_vdp2_drawgfx_transpen(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(1-(flipyx&1)+(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos+8,drawypos,(stv2_current_tilemap.transparency==STV_TRANSPARENCY_PEN)?0:-1);
+ stv_vdp2_drawgfx_transpen(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(2+(flipyx&1)-(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos,drawypos+8,(stv2_current_tilemap.transparency==STV_TRANSPARENCY_PEN)?0:-1);
+ stv_vdp2_drawgfx_transpen(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode+(3-(flipyx&1)-(flipyx&2))*tilecodespacing,pal,flipyx&1,flipyx&2,drawxpos+8,drawypos+8,(stv2_current_tilemap.transparency==STV_TRANSPARENCY_PEN)?0:-1);
+ }
+ }
+ else
+ {
+ if ( stv2_current_tilemap.colour_depth == 4)
+ {
+ stv_vdp2_drawgfx_rgb888(bitmap,cliprect,tilecode,flipyx&1,flipyx&2,drawxpos,drawypos,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ }
+ else if ( stv2_current_tilemap.colour_depth == 3)
+ {
+ stv_vdp2_drawgfx_rgb555(bitmap,cliprect,tilecode,flipyx&1,flipyx&2,drawxpos,drawypos,stv2_current_tilemap.transparency,stv2_current_tilemap.alpha);
+ }
+ else
+ {
+ if (stv2_current_tilemap.transparency == STV_TRANSPARENCY_ALPHA)
+ stv_vdp2_drawgfx_alpha(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode,pal,flipyx&1,flipyx&2, drawxpos, drawypos,0,stv2_current_tilemap.alpha);
+ else
+ stv_vdp2_drawgfx_transpen(bitmap,cliprect,m_gfxdecode->gfx(gfx),tilecode,pal,flipyx&1,flipyx&2, drawxpos, drawypos,(stv2_current_tilemap.transparency==STV_TRANSPARENCY_PEN)?0:-1);
+ }
+ }
+ drawxpos = olddrawxpos;
+ drawypos = olddrawypos;
+ }
+/* DRAWN?! */
+
+ }
+ }
+ if ( stv2_current_tilemap.layer_name & 0x80 )
+ {
+ static const int shifttable[4] = {0,1,2,2};
+ int uppermask, uppermaskshift;
+ int mapsize;
+ uppermaskshift = (1-stv2_current_tilemap.pattern_data_size) | ((1-stv2_current_tilemap.tile_size)<<1);
+ uppermask = 0x1ff >> uppermaskshift;
+
+ if ( LOG_VDP2 )
+ {
+ logerror( "Layer RBG%d, size %d x %d\n", stv2_current_tilemap.layer_name & 0x7f, cliprect.max_x + 1, cliprect.max_y + 1 );
+ logerror( "Tiles: min %08X, max %08X\n", tilecodemin, tilecodemax );
+ logerror( "MAP size in dwords %08X\n", mpsize_dwords );
+ for (i = 0; i < stv2_current_tilemap.map_count; i++)
+ {
+ logerror( "Map register %d: base %08X\n", stv2_current_tilemap.map_offset[i], base[i] );
+ }
+ }
+
+ // store map information
+ stv_vdp2_layer_data_placement.map_offset_min = 0x7fffffff;
+ stv_vdp2_layer_data_placement.map_offset_max = 0x00000000;
+ for (i = 0; i < stv2_current_tilemap.map_count; i++)
+ {
+ if ( base[i] < stv_vdp2_layer_data_placement.map_offset_min )
+ stv_vdp2_layer_data_placement.map_offset_min = base[i];
+ if ( base[i] > stv_vdp2_layer_data_placement.map_offset_max )
+ stv_vdp2_layer_data_placement.map_offset_max = base[i];
+ }
+
+
+ mapsize = ((1 & uppermask) >> shifttable[stv2_current_tilemap.plane_size]) * plsize_bytes -
+ ((0 & uppermask) >> shifttable[stv2_current_tilemap.plane_size]) * plsize_bytes;
+ mapsize /= 4;
+
+ stv_vdp2_layer_data_placement.map_offset_max += mapsize;
+
+ stv_vdp2_layer_data_placement.tile_offset_min = tilecodemin * 0x20 / 4;
+ stv_vdp2_layer_data_placement.tile_offset_max = (tilecodemax + 1) * 0x20 / 4;
+ }
+
+}
+
+#define STV_VDP2_READ_VERTICAL_LINESCROLL( _val, _address ) \
+ { \
+ _val = m_vdp2_vram[ _address ]; \
+ _val &= 0x07ffff00; \
+ if ( _val & 0x04000000 ) _val |= 0xf8000000; \
+ }
+
+
+void saturn_state::stv_vdp2_check_tilemap_with_linescroll(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ rectangle mycliprect;
+ int cur_line = cliprect.min_y;
+ int address;
+ int active_functions = 0;
+ INT32 scroll_values[3], prev_scroll_values[3];
+ int i;
+ int scroll_values_equal;
+ int lines;
+ INT16 main_scrollx, main_scrolly;
+// INT32 incx;
+ int linescroll_enable, vertical_linescroll_enable, linezoom_enable;
+ int vertical_linescroll_index = -1;
+
+ // read original scroll values
+ main_scrollx = stv2_current_tilemap.scrollx;
+ main_scrolly = stv2_current_tilemap.scrolly;
+// incx = stv2_current_tilemap.incx;
+
+ // prepare linescroll flags
+ linescroll_enable = stv2_current_tilemap.linescroll_enable;
+// stv2_current_tilemap.linescroll_enable = 0;
+ vertical_linescroll_enable = stv2_current_tilemap.vertical_linescroll_enable;
+// stv2_current_tilemap.vertical_linescroll_enable = 0;
+ linezoom_enable = stv2_current_tilemap.linezoom_enable;
+// stv2_current_tilemap.linezoom_enable = 0;
+
+ // prepare working clipping rectangle
+ memcpy( &mycliprect, &cliprect, sizeof(rectangle) );
+
+ // calculate the number of active functions
+ if ( linescroll_enable ) active_functions++;
+ if ( vertical_linescroll_enable )
+ {
+ vertical_linescroll_index = active_functions;
+ active_functions++;
+ }
+ if ( linezoom_enable ) active_functions++;
+
+ // address of data table
+ address = stv2_current_tilemap.linescroll_table_address + active_functions*4*cliprect.min_y;
+
+ // get the first scroll values
+ for ( i = 0; i < active_functions; i++ )
+ {
+ if ( i == vertical_linescroll_index )
+ {
+ STV_VDP2_READ_VERTICAL_LINESCROLL( prev_scroll_values[i], (address / 4) + i );
+ prev_scroll_values[i] -= (cur_line * stv2_current_tilemap.incy);
+ }
+ else
+ {
+ prev_scroll_values[i] = m_vdp2_vram[ (address / 4) + i ];
+ }
+ }
+
+ while( cur_line <= cliprect.max_y )
+ {
+ lines = 0;
+ do
+ {
+ // update address
+ address += active_functions*4;
+
+ // update lines count
+ lines += stv2_current_tilemap.linescroll_interval;
+
+ // get scroll values
+ for ( i = 0; i < active_functions; i++ )
+ {
+ if ( i == vertical_linescroll_index )
+ {
+ STV_VDP2_READ_VERTICAL_LINESCROLL( scroll_values[i], (address/4) + i );
+ scroll_values[i] -= (cur_line + lines) * stv2_current_tilemap.incy;
+ }
+ else
+ {
+ scroll_values[i] = m_vdp2_vram[ (address / 4) + i ];
+ }
+ }
+
+ // compare scroll values
+ scroll_values_equal = 1;
+ for ( i = 0; i < active_functions; i++ )
+ {
+ scroll_values_equal &= (scroll_values[i] == prev_scroll_values[i]);
+ }
+ } while( scroll_values_equal && ((cur_line + lines) <= cliprect.max_y) );
+
+ // determined how many lines can be drawn
+ // prepare clipping rectangle
+ mycliprect.min_y = cur_line;
+ mycliprect.max_y = cur_line + lines - 1;
+
+ // prepare scroll values
+ i = 0;
+ // linescroll
+ if ( linescroll_enable )
+ {
+ prev_scroll_values[i] &= 0x07ffff00;
+ if ( prev_scroll_values[i] & 0x04000000 ) prev_scroll_values[i] |= 0xf8000000;
+ stv2_current_tilemap.scrollx = main_scrollx + (prev_scroll_values[i] >> 16);
+ i++;
+ }
+ // vertical line scroll
+ if ( vertical_linescroll_enable )
+ {
+ stv2_current_tilemap.scrolly = main_scrolly + (prev_scroll_values[i] >> 16);
+ i++;
+ }
+
+ // linezooom
+ if ( linezoom_enable )
+ {
+ prev_scroll_values[i] &= 0x0007ff00;
+ if ( prev_scroll_values[i] & 0x00040000 ) prev_scroll_values[i] |= 0xfff80000;
+ stv2_current_tilemap.incx = prev_scroll_values[i];
+ i++;
+ }
+
+// if ( LOG_VDP2 ) logerror( "Linescroll: y < %d, %d >, scrollx = %d, scrolly = %d, incx = %f\n", mycliprect.min_y, mycliprect.max_y, stv2_current_tilemap.scrollx, stv2_current_tilemap.scrolly, (float)stv2_current_tilemap.incx/65536.0 );
+ // render current tilemap portion
+ if (stv2_current_tilemap.bitmap_enable) // this layer is a bitmap
+ {
+ stv_vdp2_draw_basic_bitmap(bitmap, mycliprect);
+ }
+ else
+ {
+ //stv_vdp2_apply_window_on_layer(mycliprect);
+ stv_vdp2_draw_basic_tilemap(bitmap, mycliprect);
+ }
+
+ // update parameters for next iteration
+ memcpy( prev_scroll_values, scroll_values, sizeof(scroll_values));
+ cur_line += lines;
+ }
+}
+
+void saturn_state::stv_vdp2_draw_line(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int x,y;
+ UINT8* gfxdata = m_vdp2.gfx_decode;
+ UINT32 base_offs,base_mask;
+ UINT32 pix;
+ UINT8 interlace;
+
+ interlace = (STV_VDP2_LSMD == 3)+1;
+
+ {
+ base_mask = STV_VDP2_VRAMSZ ? 0x7ffff : 0x3ffff;
+
+ for(y=cliprect.min_y;y<=cliprect.max_y;y++)
+ {
+ base_offs = (STV_VDP2_LCTA & base_mask) << 1;
+
+ if(STV_VDP2_LCCLMD)
+ base_offs += (y / interlace) << 1;
+
+ for(x=cliprect.min_x;x<=cliprect.max_x;x++)
+ {
+ UINT16 pen;
+
+ pen = (gfxdata[base_offs+0]<<8)|gfxdata[base_offs+1];
+ pix = bitmap.pix32(y, x);
+
+ bitmap.pix32(y, x) = stv_add_blend(m_palette->pen(pen & 0x7ff),pix);
+ }
+ }
+ }
+}
+
+void saturn_state::stv_vdp2_draw_mosaic(bitmap_rgb32 &bitmap, const rectangle &cliprect, UINT8 is_roz)
+{
+ int x,y,xi,yi;
+ UINT8 h_size,v_size;
+ UINT32 pix;
+
+ h_size = STV_VDP2_MZSZH+1;
+ v_size = STV_VDP2_MZSZV+1;
+
+ if(is_roz)
+ v_size = 1;
+
+ if(h_size == 1 && v_size == 1)
+ return; // don't bother
+
+ if(STV_VDP2_LSMD == 3)
+ v_size <<= 1;
+
+ for(y=cliprect.min_y;y<=cliprect.max_y;y+=v_size)
+ {
+ for(x=cliprect.min_x;x<=cliprect.max_x;x+=h_size)
+ {
+ pix = bitmap.pix32(y, x);
+
+ for(yi=0;yi<v_size;yi++)
+ for(xi=0;xi<h_size;xi++)
+ bitmap.pix32(y+yi, x+xi) = pix;
+ }
+ }
+}
+
+void saturn_state::stv_vdp2_check_tilemap(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ /* the idea is here we check the tilemap capabilities / whats enabled and call an appropriate tilemap drawing routine, or
+ at the very list throw up a few errors if the tilemaps want to do something we don't support yet */
+// int window_applied = 0;
+ rectangle mycliprect = cliprect;
+
+ if ( stv2_current_tilemap.linescroll_enable ||
+ stv2_current_tilemap.vertical_linescroll_enable ||
+ stv2_current_tilemap.linezoom_enable )
+ {
+ stv_vdp2_check_tilemap_with_linescroll(bitmap, cliprect);
+ return;
+ }
+
+
+ if (stv2_current_tilemap.bitmap_enable) // this layer is a bitmap
+ {
+ stv_vdp2_draw_basic_bitmap(bitmap, mycliprect);
+ }
+ else
+ {
+ //stv_vdp2_apply_window_on_layer(mycliprect);
+ stv_vdp2_draw_basic_tilemap(bitmap, mycliprect);
+ }
+
+ /* post-processing functions (TODO: needs layer bitmaps to be individual planes to work correctly) */
+ if(stv2_current_tilemap.line_screen_enabled && TEST_FUNCTIONS)
+ stv_vdp2_draw_line(bitmap,cliprect);
+
+ if(stv2_current_tilemap.mosaic_screen_enabled && TEST_FUNCTIONS)
+ stv_vdp2_draw_mosaic(bitmap,cliprect,stv2_current_tilemap.layer_name & 0x80);
+
+
+ {
+ if(stv2_current_tilemap.colour_depth == 2 && !stv2_current_tilemap.bitmap_enable)
+ popmessage("2048 color mode used on a non-bitmap plane");
+
+// if(STV_VDP2_SCXDN0 || STV_VDP2_SCXDN1 || STV_VDP2_SCYDN0 || STV_VDP2_SCYDN1)
+// popmessage("Fractional part scrolling write, contact MAMEdev");
+
+ /* Pukunpa */
+ //if(STV_VDP2_SPWINEN)
+ // popmessage("Sprite Window enabled");
+
+ /* Capcom Collection Dai 2 - Choh Makaimura (Duh!) */
+ if(STV_VDP2_MZCTL & 0x1f && POPMESSAGE_DEBUG)
+ popmessage("Mosaic control enabled = %04x\n",STV_VDP2_MZCTL);
+
+ /* Bio Hazard bit 1 */
+ /* Airs Adventure 0x3e */
+ /* Bakuretsu Hunter */
+ if(STV_VDP2_LNCLEN & ~2 && POPMESSAGE_DEBUG)
+ popmessage("Line Colour screen enabled %04x %08x, contact MAMEdev",STV_VDP2_LNCLEN,STV_VDP2_LCTAU<<16|STV_VDP2_LCTAL);
+
+ /* Bio Hazard 0x400 = extended color calculation enabled */
+ /* Advanced World War 0x200 = color calculation ratio mode */
+ /* Whizz = 0x8100 */
+ /* Dark Saviour = 0x9051 on save select screen (the one with a Saturn in the background) */
+ if(STV_VDP2_CCCR & 0x6000)
+ popmessage("Gradation enabled %04x, contact MAMEdev",STV_VDP2_CCCR);
+
+ /* Advanced VG, Shining Force III */
+ if(STV_VDP2_SFCCMD && POPMESSAGE_DEBUG)
+ popmessage("Special Color Calculation enable %04x, contact MAMEdev",STV_VDP2_SFCCMD);
+
+ /* Cleopatra Fortune Transparent Shadow */
+ /* Pretty Fighter X Back & Transparent Shadow*/
+ //if(STV_VDP2_SDCTL & 0x0120)
+ // popmessage("%s shadow select bit enabled, contact MAMEdev",STV_VDP2_SDCTL & 0x100 ? "Transparent" : "Back");
+
+ /* Langrisser III bit 3 normal, bit 1 during battle field */
+ /* Metal Slug bit 0 during gameplay */
+ /* Bug! Sega Away Logo onward 0x470 */
+ /* Command & Conquer 0x0004 0xc000 */
+ if(STV_VDP2_SFSEL & ~0x47f)
+ popmessage("Special Function Code Select enable %04x %04x, contact MAMEdev",STV_VDP2_SFSEL,STV_VDP2_SFCODE);
+
+ /* Albert Odyssey Gaiden 0x0001 */
+ /* Asuka 120% (doesn't make sense?) 0x0101 */
+ /* Slam n Jam 96 0x0003 */
+ if(STV_VDP2_ZMCTL & 0x0200)
+ popmessage("Reduction enable %04x, contact MAMEdev",STV_VDP2_ZMCTL);
+
+ /* Burning Rangers and friends FMV, J.League Pro Soccer Club Wo Tsukurou!! backgrounds */
+ if(STV_VDP2_SCRCTL & 0x0101 && POPMESSAGE_DEBUG)
+ popmessage("Vertical cell scroll enable %04x, contact MAMEdev",STV_VDP2_SCRCTL);
+
+ /* Magical Drop III 0x200 -> color calculation window */
+ /* Ide Yousuke Meijin No Shin Jissen Mahjong 0x0303 */
+ /* Decathlete 0x088 */
+ /* Sexy Parodius 0x2300 */
+// if(STV_VDP2_WCTLD & 0x2000)
+// popmessage("Special window enabled %04x, contact MAMEdev",STV_VDP2_WCTLD);
+
+ /* Shining Force III, After Burner 2 (doesn't make a proper use tho?) */
+ /* Layer Section */
+ //if(STV_VDP2_W0LWE || STV_VDP2_W1LWE)
+ // popmessage("Line Window %s %08x enabled, contact MAMEdev",STV_VDP2_W0LWE ? "0" : "1",STV_VDP2_W0LWTA);
+
+ /* Akumajou Dracula, bits 2-4 */
+ /* Arcana Strikes bit 5 */
+ /* Choh Makai Mura 0x0055 */
+ /* Sega Rally 0x0155 */
+ /* Find Love 0x4400 */
+ /* Dragon Ball Z 0x3800 - 0x2c00 */
+ /* Assault Suit Leynos 2 0x0200*/
+ /* Bug! 0x8800 */
+ /* Wonder 3 0x0018 */
+ if(STV_VDP2_SFPRMD & ~0xff7f)
+ popmessage("Special Priority Mode enabled %04x, contact MAMEdev",STV_VDP2_SFPRMD);
+ }
+}
+
+
+void saturn_state::stv_vdp2_copy_roz_bitmap(bitmap_rgb32 &bitmap,
+ bitmap_rgb32 &roz_bitmap,
+ const rectangle &cliprect,
+ int iRP,
+ int planesizex,
+ int planesizey,
+ int planerenderedsizex,
+ int planerenderedsizey)
+{
+ INT32 xsp, ysp, xp, yp, dx, dy, x, y, xs, ys, dxs, dys;
+ INT32 vcnt, hcnt;
+ INT32 kx, ky;
+ INT8 use_coeff_table, coeff_table_mode, coeff_table_size, coeff_table_shift;
+ INT8 screen_over_process;
+ UINT8 vcnt_shift, hcnt_shift;
+ UINT8 coeff_msb;
+ UINT32 *coeff_table_base, coeff_table_offset;
+ INT32 coeff_table_val;
+ UINT32 address;
+ UINT32 *line;
+ rgb_t pix;
+ //UINT32 coeff_line_color_screen_data;
+ INT32 clipxmask = 0, clipymask = 0;
+
+
+ vcnt_shift = ((STV_VDP2_LSMD & 3) == 3);
+ hcnt_shift = ((STV_VDP2_HRES & 2) == 2);
+
+ planesizex--;
+ planesizey--;
+ planerenderedsizex--;
+ planerenderedsizey--;
+
+ kx = RP.kx;
+ ky = RP.ky;
+
+ use_coeff_table = coeff_table_mode = coeff_table_size = coeff_table_shift = 0;
+ coeff_table_offset = 0;
+ coeff_table_val = 0;
+ coeff_table_base = NULL;
+
+ if ( LOG_ROZ == 1 ) logerror( "Rendering RBG with parameter %s\n", iRP == 1 ? "A" : "B" );
+ if ( LOG_ROZ == 1 ) logerror( "RPMD (parameter mode) = %x\n", STV_VDP2_RPMD );
+ if ( LOG_ROZ == 1 ) logerror( "RPRCTL (parameter read control) = %04x\n", STV_VDP2_RPRCTL );
+ if ( LOG_ROZ == 1 ) logerror( "KTCTL (coefficient table control) = %04x\n", STV_VDP2_KTCTL );
+ if ( LOG_ROZ == 1 ) logerror( "KTAOF (coefficient table address offset) = %04x\n", STV_VDP2_KTAOF );
+ if ( LOG_ROZ == 1 ) logerror( "RAOVR (screen-over process) = %x\n", STV_VDP2_RAOVR );
+ if ( iRP == 1 )
+ {
+ use_coeff_table = STV_VDP2_RAKTE;
+ if ( use_coeff_table == 1 )
+ {
+ coeff_table_mode = STV_VDP2_RAKMD;
+ coeff_table_size = STV_VDP2_RAKDBS;
+ coeff_table_offset = STV_VDP2_RAKTAOS;
+ }
+ screen_over_process = STV_VDP2_RAOVR;
+ }
+ else
+ {
+ use_coeff_table = STV_VDP2_RBKTE;
+ if ( use_coeff_table == 1 )
+ {
+ coeff_table_mode = STV_VDP2_RBKMD;
+ coeff_table_size = STV_VDP2_RBKDBS;
+ coeff_table_offset = STV_VDP2_RBKTAOS;
+ }
+ screen_over_process = STV_VDP2_RBOVR;
+ }
+ if ( use_coeff_table )
+ {
+ if ( STV_VDP2_CRKTE == 0 )
+ {
+ coeff_table_base = m_vdp2_vram;
+ }
+ else
+ {
+ coeff_table_base = m_vdp2_cram;
+ }
+ if ( coeff_table_size == 0 )
+ {
+ coeff_table_offset = (coeff_table_offset & 0x0003) * 0x40000;
+ coeff_table_shift = 2;
+ }
+ else
+ {
+ coeff_table_offset = (coeff_table_offset & 0x0007) * 0x20000;
+ coeff_table_shift = 1;
+ }
+ }
+
+ if ( stv2_current_tilemap.colour_calculation_enabled == 1 )
+ {
+ if ( STV_VDP2_CCMD )
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_ADD_BLEND;
+ }
+ else
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_ALPHA;
+ }
+ }
+
+ /* clipping */
+ switch( screen_over_process )
+ {
+ case 0:
+ /* repeated */
+ clipxmask = clipymask = 0;
+ break;
+ case 1:
+ /* screen over pattern, not supported */
+ clipxmask = clipymask = 0;
+ break;
+ case 2:
+ /* outside display area, scroll screen is transparent */
+ clipxmask = ~planesizex;
+ clipymask = ~planesizey;
+ break;
+ case 3:
+ /* display area is 512x512, outside is transparent */
+ clipxmask = ~511;
+ clipymask = ~511;
+ break;
+ }
+
+ //dx = (RP.A * RP.dx) + (RP.B * RP.dy);
+ //dy = (RP.D * RP.dx) + (RP.E * RP.dy);
+ dx = mul_fixed32( RP.A, RP.dx ) + mul_fixed32( RP.B, RP.dy );
+ dy = mul_fixed32( RP.D, RP.dx ) + mul_fixed32( RP.E, RP.dy );
+
+ //xp = RP.A * ( RP.px - RP.cx ) + RP.B * ( RP.py - RP.cy ) + RP.C * ( RP.pz - RP.cz ) + RP.cx + RP.mx;
+ //yp = RP.D * ( RP.px - RP.cx ) + RP.E * ( RP.py - RP.cy ) + RP.F * ( RP.pz - RP.cz ) + RP.cy + RP.my;
+ xp = mul_fixed32( RP.A, RP.px - RP.cx ) + mul_fixed32( RP.B, RP.py - RP.cy ) + mul_fixed32( RP.C, RP.pz - RP.cz ) + RP.cx + RP.mx;
+ yp = mul_fixed32( RP.D, RP.px - RP.cx ) + mul_fixed32( RP.E, RP.py - RP.cy ) + mul_fixed32( RP.F, RP.pz - RP.cz ) + RP.cy + RP.my;
+
+ for (vcnt = cliprect.min_y; vcnt <= cliprect.max_y; vcnt++ )
+ {
+ /*xsp = RP.A * ( ( RP.xst + RP.dxst * (vcnt << 16) ) - RP.px ) +
+ RP.B * ( ( RP.yst + RP.dyst * (vcnt << 16) ) - RP.py ) +
+ RP.C * ( RP.zst - RP.pz);
+ ysp = RP.D * ( ( RP.xst + RP.dxst * (vcnt << 16) ) - RP.px ) +
+ RP.E * ( ( RP.yst + RP.dyst * (vcnt << 16) ) - RP.py ) +
+ RP.F * ( RP.zst - RP.pz );*/
+ xsp = mul_fixed32( RP.A, RP.xst + mul_fixed32( RP.dxst, vcnt << (16 - vcnt_shift)) - RP.px ) +
+ mul_fixed32( RP.B, RP.yst + mul_fixed32( RP.dyst, vcnt << (16 - vcnt_shift)) - RP.py ) +
+ mul_fixed32( RP.C, RP.zst - RP.pz );
+ ysp = mul_fixed32( RP.D, RP.xst + mul_fixed32( RP.dxst, vcnt << (16 - vcnt_shift)) - RP.px ) +
+ mul_fixed32( RP.E, RP.yst + mul_fixed32( RP.dyst, vcnt << (16 - vcnt_shift)) - RP.py ) +
+ mul_fixed32( RP.F, RP.zst - RP.pz );
+ //xp = RP.A * ( RP.px - RP.cx ) + RP.B * ( RP.py - RP.cy ) + RP.C * ( RP.pz - RP.cz ) + RP.cx + RP.mx;
+ //yp = RP.D * ( RP.px - RP.cx ) + RP.E * ( RP.py - RP.cy ) + RP.F * ( RP.pz - RP.cz ) + RP.cy + RP.my;
+ //dx = (RP.A * RP.dx) + (RP.B * RP.dy);
+ //dy = (RP.D * RP.dx) + (RP.E * RP.dy);
+
+ line = &bitmap.pix32(vcnt);
+
+ if ( !use_coeff_table || RP.dkax == 0 )
+ {
+ if ( use_coeff_table )
+ {
+ switch( coeff_table_size )
+ {
+ case 0:
+ address = coeff_table_offset + ((RP.kast + RP.dkast*(vcnt>>vcnt_shift)) >> 16) * 4;
+ coeff_table_val = coeff_table_base[ address / 4 ];
+ //coeff_line_color_screen_data = (coeff_table_val & 0x7f000000) >> 24;
+ coeff_msb = (coeff_table_val & 0x80000000) > 0;
+ if ( coeff_table_val & 0x00800000 )
+ {
+ coeff_table_val |= 0xff000000;
+ }
+ else
+ {
+ coeff_table_val &= 0x007fffff;
+ }
+ break;
+ case 1:
+ address = coeff_table_offset + ((RP.kast + RP.dkast*(vcnt>>vcnt_shift)) >> 16) * 2;
+ coeff_table_val = coeff_table_base[ address / 4 ];
+ if ( (address & 2) == 0 )
+ {
+ coeff_table_val >>= 16;
+ }
+ coeff_table_val &= 0xffff;
+ //coeff_line_color_screen_data = 0;
+ coeff_msb = (coeff_table_val & 0x8000) > 0;
+ if ( coeff_table_val & 0x4000 )
+ {
+ coeff_table_val |= 0xffff8000;
+ }
+ else
+ {
+ coeff_table_val &= 0x3fff;
+ }
+ coeff_table_val <<= 6; /* to form 16.16 fixed point val */
+ break;
+ default:
+ coeff_msb = 1;
+ break;
+ }
+ if ( coeff_msb ) continue;
+
+ switch( coeff_table_mode )
+ {
+ case 0:
+ kx = ky = coeff_table_val;
+ break;
+ case 1:
+ kx = coeff_table_val;
+ break;
+ case 2:
+ ky = coeff_table_val;
+ break;
+ case 3:
+ xp = coeff_table_val;
+ break;
+ }
+ }
+
+ //x = RP.kx * ( xsp + dx * (hcnt << 16)) + xp;
+ //y = RP.ky * ( ysp + dy * (hcnt << 16)) + yp;
+ xs = mul_fixed32( kx, xsp ) + xp;
+ ys = mul_fixed32( ky, ysp ) + yp;
+ dxs = mul_fixed32( kx, mul_fixed32( dx, 1 << (16-hcnt_shift)));
+ dys = mul_fixed32( ky, mul_fixed32( dy, 1 << (16-hcnt_shift)));
+
+ for (hcnt = cliprect.min_x; hcnt <= cliprect.max_x; xs+=dxs, ys+=dys, hcnt++ )
+ {
+ x = xs >> 16;
+ y = ys >> 16;
+
+ if ( x & clipxmask || y & clipymask ) continue;
+ pix = roz_bitmap.pix32(y & planerenderedsizey, x & planerenderedsizex);
+ switch( stv2_current_tilemap.transparency )
+ {
+ case STV_TRANSPARENCY_PEN:
+ if (pix & 0xffffff)
+ {
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset_UINT32(&pix,stv2_current_tilemap.fade_control & 2);
+
+ line[hcnt] = pix;
+ }
+ break;
+ case STV_TRANSPARENCY_NONE:
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset_UINT32(&pix,stv2_current_tilemap.fade_control & 2);
+
+ line[hcnt] = pix;
+ break;
+ case STV_TRANSPARENCY_ALPHA:
+ if (pix & 0xffffff)
+ {
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset_UINT32(&pix,stv2_current_tilemap.fade_control & 2);
+
+ line[hcnt] = alpha_blend_r32( line[hcnt], pix, stv2_current_tilemap.alpha );
+ }
+ break;
+ case STV_TRANSPARENCY_ADD_BLEND:
+ if (pix & 0xffffff)
+ {
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset_UINT32(&pix,stv2_current_tilemap.fade_control & 2);
+
+ line[hcnt] = stv_add_blend( line[hcnt], pix );
+ }
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ for (hcnt = cliprect.min_x; hcnt <= cliprect.max_x; hcnt++ )
+ {
+ switch( coeff_table_size )
+ {
+ case 0:
+ address = coeff_table_offset + ((RP.kast + RP.dkast*(vcnt>>vcnt_shift) + RP.dkax*hcnt) >> 16) * 4;
+ coeff_table_val = coeff_table_base[ address / 4 ];
+ //coeff_line_color_screen_data = (coeff_table_val & 0x7f000000) >> 24;
+ coeff_msb = (coeff_table_val & 0x80000000) > 0;
+ if ( coeff_table_val & 0x00800000 )
+ {
+ coeff_table_val |= 0xff000000;
+ }
+ else
+ {
+ coeff_table_val &= 0x007fffff;
+ }
+ break;
+ case 1:
+ address = coeff_table_offset + ((RP.kast + RP.dkast*(vcnt>>vcnt_shift) + RP.dkax*hcnt) >> 16) * 2;
+ coeff_table_val = coeff_table_base[ address / 4 ];
+ if ( (address & 2) == 0 )
+ {
+ coeff_table_val >>= 16;
+ }
+ coeff_table_val &= 0xffff;
+ //coeff_line_color_screen_data = 0;
+ coeff_msb = (coeff_table_val & 0x8000) > 0;
+ if ( coeff_table_val & 0x4000 )
+ {
+ coeff_table_val |= 0xffff8000;
+ }
+ else
+ {
+ coeff_table_val &= 0x3fff;
+ }
+ coeff_table_val <<= 6; /* to form 16.16 fixed point val */
+ break;
+ default:
+ coeff_msb = 1;
+ break;
+ }
+ if ( coeff_msb ) continue;
+ switch( coeff_table_mode )
+ {
+ case 0:
+ kx = ky = coeff_table_val;
+ break;
+ case 1:
+ kx = coeff_table_val;
+ break;
+ case 2:
+ ky = coeff_table_val;
+ break;
+ case 3:
+ xp = coeff_table_val;
+ break;
+ }
+
+ //x = RP.kx * ( xsp + dx * (hcnt << 16)) + xp;
+ //y = RP.ky * ( ysp + dy * (hcnt << 16)) + yp;
+ x = mul_fixed32( kx, xsp + mul_fixed32( dx, (hcnt>>hcnt_shift) << 16 ) ) + xp;
+ y = mul_fixed32( ky, ysp + mul_fixed32( dy, (hcnt>>hcnt_shift) << 16 ) ) + yp;
+
+ x >>= 16;
+ y >>= 16;
+
+ if ( x & clipxmask || y & clipymask ) continue;
+
+ pix = roz_bitmap.pix32(y & planerenderedsizey, x & planerenderedsizex);
+ switch( stv2_current_tilemap.transparency )
+ {
+ case STV_TRANSPARENCY_PEN:
+ if (pix & 0xffffff)
+ {
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset_UINT32(&pix,stv2_current_tilemap.fade_control & 2);
+
+ line[hcnt] = pix;
+ }
+ break;
+ case STV_TRANSPARENCY_NONE:
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset_UINT32(&pix,stv2_current_tilemap.fade_control & 2);
+
+ line[hcnt] = pix;
+ break;
+ case STV_TRANSPARENCY_ALPHA:
+ if (pix & 0xffffff)
+ {
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset_UINT32(&pix,stv2_current_tilemap.fade_control & 2);
+
+ line[hcnt] = alpha_blend_r32( line[hcnt], pix, stv2_current_tilemap.alpha );
+ }
+ break;
+ case STV_TRANSPARENCY_ADD_BLEND:
+ if (pix & 0xffffff)
+ {
+ if(stv2_current_tilemap.fade_control & 1)
+ stv_vdp2_compute_color_offset_UINT32(&pix,stv2_current_tilemap.fade_control & 2);
+
+ line[hcnt] = stv_add_blend( line[hcnt], pix );
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+void saturn_state::stv_vdp2_draw_NBG0(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT32 base_mask;
+
+ base_mask = STV_VDP2_VRAMSZ ? 0x7ffff : 0x3ffff;
+
+ /*
+ Colours : 16, 256, 2048, 32768, 16770000
+ Char Size : 1x1 cells, 2x2 cells
+ Pattern Data Size : 1 word, 2 words
+ Plane Layouts : 1 x 1, 2 x 1, 2 x 2
+ Planes : 4
+ Bitmap : Possible
+ Bitmap Sizes : 512 x 256, 512 x 512, 1024 x 256, 1024 x 512
+ Scale : 0.25 x - 256 x
+ Rotation : No
+ Linescroll : Yes
+ Column Scroll : Yes
+ Mosaic : Yes
+ */
+
+ stv2_current_tilemap.enabled = STV_VDP2_N0ON | STV_VDP2_R1ON;
+
+// if (!stv2_current_tilemap.enabled) return; // stop right now if its disabled ...
+
+ //stv2_current_tilemap.trans_enabled = STV_VDP2_N0TPON;
+ if ( STV_VDP2_N0CCEN )
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 1;
+ stv2_current_tilemap.alpha = ((UINT16)(0x1f-STV_VDP2_N0CCRT)*0xff)/0x1f;
+ }
+ else
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 0;
+ }
+ if ( STV_VDP2_N0TPON == 0 )
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_PEN;
+ }
+ else
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_NONE;
+ }
+ stv2_current_tilemap.colour_depth = STV_VDP2_N0CHCN;
+ stv2_current_tilemap.tile_size = STV_VDP2_N0CHSZ;
+ stv2_current_tilemap.bitmap_enable = STV_VDP2_N0BMEN;
+ stv2_current_tilemap.bitmap_size = STV_VDP2_N0BMSZ;
+ stv2_current_tilemap.bitmap_palette_number = STV_VDP2_N0BMP;
+ stv2_current_tilemap.bitmap_map = STV_VDP2_N0MP_;
+ stv2_current_tilemap.map_offset[0] = STV_VDP2_N0MPA | (STV_VDP2_N0MP_ << 6);
+ stv2_current_tilemap.map_offset[1] = STV_VDP2_N0MPB | (STV_VDP2_N0MP_ << 6);
+ stv2_current_tilemap.map_offset[2] = STV_VDP2_N0MPC | (STV_VDP2_N0MP_ << 6);
+ stv2_current_tilemap.map_offset[3] = STV_VDP2_N0MPD | (STV_VDP2_N0MP_ << 6);
+ stv2_current_tilemap.map_count = 4;
+
+ stv2_current_tilemap.pattern_data_size = STV_VDP2_N0PNB;
+ stv2_current_tilemap.character_number_supplement = STV_VDP2_N0CNSM;
+ stv2_current_tilemap.special_priority_register = STV_VDP2_N0SPR;
+ stv2_current_tilemap.special_colour_control_register = STV_VDP2_PNCN0;
+ stv2_current_tilemap.supplementary_palette_bits = STV_VDP2_N0SPLT;
+ stv2_current_tilemap.supplementary_character_bits = STV_VDP2_N0SPCN;
+
+ stv2_current_tilemap.scrollx = STV_VDP2_SCXIN0;
+ stv2_current_tilemap.scrolly = STV_VDP2_SCYIN0;
+ stv2_current_tilemap.incx = STV_VDP2_ZMXN0;
+ stv2_current_tilemap.incy = STV_VDP2_ZMYN0;
+
+ stv2_current_tilemap.linescroll_enable = STV_VDP2_N0LSCX;
+ stv2_current_tilemap.linescroll_interval = (((STV_VDP2_LSMD & 3) == 2) ? (2) : (1)) << (STV_VDP2_N0LSS);
+ stv2_current_tilemap.linescroll_table_address = (((STV_VDP2_LSTA0U << 16) | STV_VDP2_LSTA0L) & base_mask) * 2;
+ stv2_current_tilemap.vertical_linescroll_enable = STV_VDP2_N0LSCY;
+ stv2_current_tilemap.linezoom_enable = STV_VDP2_N0LZMX;
+
+ stv2_current_tilemap.plane_size = (STV_VDP2_R1ON) ? STV_VDP2_RBPLSZ : STV_VDP2_N0PLSZ;
+ stv2_current_tilemap.colour_ram_address_offset = STV_VDP2_N0CAOS;
+ stv2_current_tilemap.fade_control = (STV_VDP2_N0COEN * 1) | (STV_VDP2_N0COSL * 2);
+ stv_vdp2_check_fade_control_for_layer();
+ stv2_current_tilemap.window_control.logic = STV_VDP2_N0LOG;
+ stv2_current_tilemap.window_control.enabled[0] = STV_VDP2_N0W0E;
+ stv2_current_tilemap.window_control.enabled[1] = STV_VDP2_N0W1E;
+// stv2_current_tilemap.window_control.? = STV_VDP2_N0SWE;
+ stv2_current_tilemap.window_control.area[0] = STV_VDP2_N0W0A;
+ stv2_current_tilemap.window_control.area[1] = STV_VDP2_N0W1A;
+// stv2_current_tilemap.window_control.? = STV_VDP2_N0SWA;
+
+ stv2_current_tilemap.line_screen_enabled = STV_VDP2_N0LCEN;
+ stv2_current_tilemap.mosaic_screen_enabled = STV_VDP2_N0MZE;
+
+ stv2_current_tilemap.layer_name=(STV_VDP2_R1ON) ? 0x81 : 0;
+
+ if ( stv2_current_tilemap.enabled && (!(STV_VDP2_R1ON))) /* TODO: check cycle pattern for RBG1 */
+ {
+ stv2_current_tilemap.enabled = stv_vdp2_check_vram_cycle_pattern_registers( STV_VDP2_CP_NBG0_PNMDR, STV_VDP2_CP_NBG0_CPDR, stv2_current_tilemap.bitmap_enable );
+ }
+
+ if(STV_VDP2_R1ON)
+ stv_vdp2_draw_rotation_screen(bitmap, cliprect, 2 );
+ else
+ stv_vdp2_check_tilemap(bitmap, cliprect);
+}
+
+void saturn_state::stv_vdp2_draw_NBG1(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT32 base_mask;
+
+ base_mask = STV_VDP2_VRAMSZ ? 0x7ffff : 0x3ffff;
+
+ /*
+ Colours : 16, 256, 2048, 32768
+ Char Size : 1x1 cells, 2x2 cells
+ Pattern Data Size : 1 word, 2 words
+ Plane Layouts : 1 x 1, 2 x 1, 2 x 2
+ Planes : 4
+ Bitmap : Possible
+ Bitmap Sizes : 512 x 256, 512 x 512, 1024 x 256, 1024 x 512
+ Scale : 0.25 x - 256 x
+ Rotation : No
+ Linescroll : Yes
+ Column Scroll : Yes
+ Mosaic : Yes
+ */
+ stv2_current_tilemap.enabled = STV_VDP2_N1ON;
+
+// if (!stv2_current_tilemap.enabled) return; // stop right now if its disabled ...
+
+ //stv2_current_tilemap.trans_enabled = STV_VDP2_N1TPON;
+ if ( STV_VDP2_N1CCEN )
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 1;
+ stv2_current_tilemap.alpha = ((UINT16)(0x1f-STV_VDP2_N1CCRT)*0xff)/0x1f;
+ }
+ else
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 0;
+ }
+ if ( STV_VDP2_N1TPON == 0 )
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_PEN;
+ }
+ else
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_NONE;
+ }
+ stv2_current_tilemap.colour_depth = STV_VDP2_N1CHCN;
+ stv2_current_tilemap.tile_size = STV_VDP2_N1CHSZ;
+ stv2_current_tilemap.bitmap_enable = STV_VDP2_N1BMEN;
+ stv2_current_tilemap.bitmap_size = STV_VDP2_N1BMSZ;
+ stv2_current_tilemap.bitmap_palette_number = STV_VDP2_N1BMP;
+ stv2_current_tilemap.bitmap_map = STV_VDP2_N1MP_;
+ stv2_current_tilemap.map_offset[0] = STV_VDP2_N1MPA | (STV_VDP2_N1MP_ << 6);
+ stv2_current_tilemap.map_offset[1] = STV_VDP2_N1MPB | (STV_VDP2_N1MP_ << 6);
+ stv2_current_tilemap.map_offset[2] = STV_VDP2_N1MPC | (STV_VDP2_N1MP_ << 6);
+ stv2_current_tilemap.map_offset[3] = STV_VDP2_N1MPD | (STV_VDP2_N1MP_ << 6);
+ stv2_current_tilemap.map_count = 4;
+
+ stv2_current_tilemap.pattern_data_size = STV_VDP2_N1PNB;
+ stv2_current_tilemap.character_number_supplement = STV_VDP2_N1CNSM;
+ stv2_current_tilemap.special_priority_register = STV_VDP2_N1SPR;
+ stv2_current_tilemap.special_colour_control_register = STV_VDP2_PNCN1;
+ stv2_current_tilemap.supplementary_palette_bits = STV_VDP2_N1SPLT;
+ stv2_current_tilemap.supplementary_character_bits = STV_VDP2_N1SPCN;
+
+ stv2_current_tilemap.scrollx = STV_VDP2_SCXIN1;
+ stv2_current_tilemap.scrolly = STV_VDP2_SCYIN1;
+ stv2_current_tilemap.incx = STV_VDP2_ZMXN1;
+ stv2_current_tilemap.incy = STV_VDP2_ZMYN1;
+
+ stv2_current_tilemap.linescroll_enable = STV_VDP2_N1LSCX;
+ stv2_current_tilemap.linescroll_interval = (((STV_VDP2_LSMD & 3) == 2) ? (2) : (1)) << (STV_VDP2_N1LSS);
+ stv2_current_tilemap.linescroll_table_address = (((STV_VDP2_LSTA1U << 16) | STV_VDP2_LSTA1L) & base_mask) * 2;
+ stv2_current_tilemap.vertical_linescroll_enable = STV_VDP2_N1LSCY;
+ stv2_current_tilemap.linezoom_enable = STV_VDP2_N1LZMX;
+
+ stv2_current_tilemap.plane_size = STV_VDP2_N1PLSZ;
+ stv2_current_tilemap.colour_ram_address_offset = STV_VDP2_N1CAOS;
+ stv2_current_tilemap.fade_control = (STV_VDP2_N1COEN * 1) | (STV_VDP2_N1COSL * 2);
+ stv_vdp2_check_fade_control_for_layer();
+ stv2_current_tilemap.window_control.logic = STV_VDP2_N1LOG;
+ stv2_current_tilemap.window_control.enabled[0] = STV_VDP2_N1W0E;
+ stv2_current_tilemap.window_control.enabled[1] = STV_VDP2_N1W1E;
+// stv2_current_tilemap.window_control.? = STV_VDP2_N1SWE;
+ stv2_current_tilemap.window_control.area[0] = STV_VDP2_N1W0A;
+ stv2_current_tilemap.window_control.area[1] = STV_VDP2_N1W1A;
+// stv2_current_tilemap.window_control.? = STV_VDP2_N1SWA;
+
+ stv2_current_tilemap.line_screen_enabled = STV_VDP2_N1LCEN;
+ stv2_current_tilemap.mosaic_screen_enabled = STV_VDP2_N1MZE;
+
+ stv2_current_tilemap.layer_name=1;
+
+ if ( stv2_current_tilemap.enabled )
+ {
+ stv2_current_tilemap.enabled = stv_vdp2_check_vram_cycle_pattern_registers( STV_VDP2_CP_NBG1_PNMDR, STV_VDP2_CP_NBG1_CPDR, stv2_current_tilemap.bitmap_enable );
+ }
+
+ stv_vdp2_check_tilemap(bitmap, cliprect);
+}
+
+void saturn_state::stv_vdp2_draw_NBG2(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ /*
+ NBG2 is the first of the 2 more basic tilemaps, it has exactly the same capabilities as NBG3
+
+ Colours : 16, 256
+ Char Size : 1x1 cells, 2x2 cells
+ Pattern Data Size : 1 word, 2 words
+ Plane Layouts : 1 x 1, 2 x 1, 2 x 2
+ Planes : 4
+ Bitmap : No
+ Bitmap Sizes : N/A
+ Scale : No
+ Rotation : No
+ Linescroll : No
+ Column Scroll : No
+ Mosaic : Yes
+ */
+
+ stv2_current_tilemap.enabled = STV_VDP2_N2ON;
+
+ /* these modes for N0 disable this layer */
+ if (STV_VDP2_N0CHCN == 0x03) stv2_current_tilemap.enabled = 0;
+ if (STV_VDP2_N0CHCN == 0x04) stv2_current_tilemap.enabled = 0;
+
+// if (!stv2_current_tilemap.enabled) return; // stop right now if its disabled ...
+
+ //stv2_current_tilemap.trans_enabled = STV_VDP2_N2TPON;
+ if ( STV_VDP2_N2CCEN )
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 1;
+ stv2_current_tilemap.alpha = ((UINT16)(0x1f-STV_VDP2_N2CCRT)*0xff)/0x1f;
+ }
+ else
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 0;
+ }
+ if ( STV_VDP2_N2TPON == 0 )
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_PEN;
+ }
+ else
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_NONE;
+ }
+ stv2_current_tilemap.colour_depth = STV_VDP2_N2CHCN;
+ stv2_current_tilemap.tile_size = STV_VDP2_N2CHSZ;
+ /* this layer can't be a bitmap,so ignore these registers*/
+ stv2_current_tilemap.bitmap_enable = 0;
+ stv2_current_tilemap.bitmap_size = 0;
+ stv2_current_tilemap.bitmap_palette_number = 0;
+ stv2_current_tilemap.bitmap_map = 0;
+ stv2_current_tilemap.map_offset[0] = STV_VDP2_N2MPA | (STV_VDP2_N2MP_ << 6);
+ stv2_current_tilemap.map_offset[1] = STV_VDP2_N2MPB | (STV_VDP2_N2MP_ << 6);
+ stv2_current_tilemap.map_offset[2] = STV_VDP2_N2MPC | (STV_VDP2_N2MP_ << 6);
+ stv2_current_tilemap.map_offset[3] = STV_VDP2_N2MPD | (STV_VDP2_N2MP_ << 6);
+ stv2_current_tilemap.map_count = 4;
+
+ stv2_current_tilemap.pattern_data_size = STV_VDP2_N2PNB;
+ stv2_current_tilemap.character_number_supplement = STV_VDP2_N2CNSM;
+ stv2_current_tilemap.special_priority_register = STV_VDP2_N2SPR;
+ stv2_current_tilemap.special_colour_control_register = STV_VDP2_PNCN2;
+ stv2_current_tilemap.supplementary_palette_bits = STV_VDP2_N2SPLT;
+ stv2_current_tilemap.supplementary_character_bits = STV_VDP2_N2SPCN;
+
+ stv2_current_tilemap.scrollx = STV_VDP2_SCXN2;
+ stv2_current_tilemap.scrolly = STV_VDP2_SCYN2;
+ /*This layer can't be scaled*/
+ stv2_current_tilemap.incx = 0x10000;
+ stv2_current_tilemap.incy = 0x10000;
+
+ stv2_current_tilemap.linescroll_enable = 0;
+ stv2_current_tilemap.linescroll_interval = 0;
+ stv2_current_tilemap.linescroll_table_address = 0;
+ stv2_current_tilemap.vertical_linescroll_enable = 0;
+ stv2_current_tilemap.linezoom_enable = 0;
+
+ stv2_current_tilemap.colour_ram_address_offset = STV_VDP2_N2CAOS;
+ stv2_current_tilemap.fade_control = (STV_VDP2_N2COEN * 1) | (STV_VDP2_N2COSL * 2);
+ stv_vdp2_check_fade_control_for_layer();
+ stv2_current_tilemap.window_control.logic = STV_VDP2_N2LOG;
+ stv2_current_tilemap.window_control.enabled[0] = STV_VDP2_N2W0E;
+ stv2_current_tilemap.window_control.enabled[1] = STV_VDP2_N2W1E;
+// stv2_current_tilemap.window_control.? = STV_VDP2_N2SWE;
+ stv2_current_tilemap.window_control.area[0] = STV_VDP2_N2W0A;
+ stv2_current_tilemap.window_control.area[1] = STV_VDP2_N2W1A;
+// stv2_current_tilemap.window_control.? = STV_VDP2_N2SWA;
+
+ stv2_current_tilemap.line_screen_enabled = STV_VDP2_N2LCEN;
+ stv2_current_tilemap.mosaic_screen_enabled = STV_VDP2_N2MZE;
+
+ stv2_current_tilemap.layer_name=2;
+
+ stv2_current_tilemap.plane_size = STV_VDP2_N2PLSZ;
+
+ if ( stv2_current_tilemap.enabled )
+ {
+ stv2_current_tilemap.enabled = stv_vdp2_check_vram_cycle_pattern_registers( STV_VDP2_CP_NBG2_PNMDR, STV_VDP2_CP_NBG2_CPDR, stv2_current_tilemap.bitmap_enable );
+ }
+
+ stv_vdp2_check_tilemap(bitmap, cliprect);
+}
+
+void saturn_state::stv_vdp2_draw_NBG3(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ /*
+ NBG3 is the second of the 2 more basic tilemaps, it has exactly the same capabilities as NBG2
+
+ Colours : 16, 256
+ Char Size : 1x1 cells, 2x2 cells
+ Pattern Data Size : 1 word, 2 words
+ Plane Layouts : 1 x 1, 2 x 1, 2 x 2
+ Planes : 4
+ Bitmap : No
+ Bitmap Sizes : N/A
+ Scale : No
+ Rotation : No
+ Linescroll : No
+ Column Scroll : No
+ Mosaic : Yes
+ */
+
+ stv2_current_tilemap.enabled = STV_VDP2_N3ON;
+
+// if (!stv2_current_tilemap.enabled) return; // stop right now if its disabled ...
+
+ /* these modes for N1 disable this layer */
+ if (STV_VDP2_N1CHCN == 0x03) stv2_current_tilemap.enabled = 0;
+ if (STV_VDP2_N1CHCN == 0x04) stv2_current_tilemap.enabled = 0;
+
+ //stv2_current_tilemap.trans_enabled = STV_VDP2_N3TPON;
+ if ( STV_VDP2_N3CCEN )
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 1;
+ stv2_current_tilemap.alpha = ((UINT16)(0x1f-STV_VDP2_N3CCRT)*0xff)/0x1f;
+ }
+ else
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 0;
+ }
+ if ( STV_VDP2_N3TPON == 0 )
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_PEN;
+ }
+ else
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_NONE;
+ }
+ stv2_current_tilemap.colour_depth = STV_VDP2_N3CHCN;
+ stv2_current_tilemap.tile_size = STV_VDP2_N3CHSZ;
+ /* this layer can't be a bitmap,so ignore these registers*/
+ stv2_current_tilemap.bitmap_enable = 0;
+ stv2_current_tilemap.bitmap_size = 0;
+ stv2_current_tilemap.bitmap_palette_number = 0;
+ stv2_current_tilemap.bitmap_map = 0;
+ stv2_current_tilemap.map_offset[0] = STV_VDP2_N3MPA | (STV_VDP2_N3MP_ << 6);
+ stv2_current_tilemap.map_offset[1] = STV_VDP2_N3MPB | (STV_VDP2_N3MP_ << 6);
+ stv2_current_tilemap.map_offset[2] = STV_VDP2_N3MPC | (STV_VDP2_N3MP_ << 6);
+ stv2_current_tilemap.map_offset[3] = STV_VDP2_N3MPD | (STV_VDP2_N3MP_ << 6);
+ stv2_current_tilemap.map_count = 4;
+
+ stv2_current_tilemap.pattern_data_size = STV_VDP2_N3PNB;
+ stv2_current_tilemap.character_number_supplement = STV_VDP2_N3CNSM;
+ stv2_current_tilemap.special_priority_register = STV_VDP2_N3SPR;
+ stv2_current_tilemap.special_colour_control_register = STV_VDP2_N3SCC;
+ stv2_current_tilemap.supplementary_palette_bits = STV_VDP2_N3SPLT;
+ stv2_current_tilemap.supplementary_character_bits = STV_VDP2_N3SPCN;
+
+ stv2_current_tilemap.scrollx = STV_VDP2_SCXN3;
+ stv2_current_tilemap.scrolly = STV_VDP2_SCYN3;
+ /*This layer can't be scaled*/
+ stv2_current_tilemap.incx = 0x10000;
+ stv2_current_tilemap.incy = 0x10000;
+
+ stv2_current_tilemap.linescroll_enable = 0;
+ stv2_current_tilemap.linescroll_interval = 0;
+ stv2_current_tilemap.linescroll_table_address = 0;
+ stv2_current_tilemap.vertical_linescroll_enable = 0;
+ stv2_current_tilemap.linezoom_enable = 0;
+
+ stv2_current_tilemap.colour_ram_address_offset = STV_VDP2_N3CAOS;
+ stv2_current_tilemap.fade_control = (STV_VDP2_N3COEN * 1) | (STV_VDP2_N3COSL * 2);
+ stv_vdp2_check_fade_control_for_layer();
+ stv2_current_tilemap.window_control.logic = STV_VDP2_N3LOG;
+ stv2_current_tilemap.window_control.enabled[0] = STV_VDP2_N3W0E;
+ stv2_current_tilemap.window_control.enabled[1] = STV_VDP2_N3W1E;
+// stv2_current_tilemap.window_control.? = STV_VDP2_N3SWE;
+ stv2_current_tilemap.window_control.area[0] = STV_VDP2_N3W0A;
+ stv2_current_tilemap.window_control.area[1] = STV_VDP2_N3W1A;
+// stv2_current_tilemap.window_control.? = STV_VDP2_N3SWA;
+
+ stv2_current_tilemap.line_screen_enabled = STV_VDP2_N3LCEN;
+ stv2_current_tilemap.mosaic_screen_enabled = STV_VDP2_N3MZE;
+
+ stv2_current_tilemap.layer_name=3;
+
+ stv2_current_tilemap.plane_size = STV_VDP2_N3PLSZ;
+
+ if ( stv2_current_tilemap.enabled )
+ {
+ stv2_current_tilemap.enabled = stv_vdp2_check_vram_cycle_pattern_registers( STV_VDP2_CP_NBG3_PNMDR, STV_VDP2_CP_NBG3_CPDR, stv2_current_tilemap.bitmap_enable );
+ }
+
+ stv_vdp2_check_tilemap(bitmap, cliprect);
+}
+
+
+void saturn_state::stv_vdp2_draw_rotation_screen(bitmap_rgb32 &bitmap, const rectangle &cliprect, int iRP)
+{
+ rectangle roz_clip_rect, mycliprect;
+ int planesizex = 0, planesizey = 0;
+ int planerenderedsizex, planerenderedsizey;
+ UINT8 colour_calculation_enabled;
+ UINT8 fade_control;
+
+ if ( iRP == 1)
+ {
+ stv2_current_tilemap.bitmap_map = STV_VDP2_RAMP_;
+ stv2_current_tilemap.map_offset[0] = STV_VDP2_RAMPA | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[1] = STV_VDP2_RAMPB | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[2] = STV_VDP2_RAMPC | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[3] = STV_VDP2_RAMPD | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[4] = STV_VDP2_RAMPE | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[5] = STV_VDP2_RAMPF | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[6] = STV_VDP2_RAMPG | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[7] = STV_VDP2_RAMPH | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[8] = STV_VDP2_RAMPI | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[9] = STV_VDP2_RAMPJ | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[10] = STV_VDP2_RAMPK | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[11] = STV_VDP2_RAMPL | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[12] = STV_VDP2_RAMPM | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[13] = STV_VDP2_RAMPN | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[14] = STV_VDP2_RAMPO | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_offset[15] = STV_VDP2_RAMPP | (STV_VDP2_RAMP_ << 6);
+ stv2_current_tilemap.map_count = 16;
+ }
+ else
+ {
+ stv2_current_tilemap.bitmap_map = STV_VDP2_RBMP_;
+ stv2_current_tilemap.map_offset[0] = STV_VDP2_RBMPA | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[1] = STV_VDP2_RBMPB | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[2] = STV_VDP2_RBMPC | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[3] = STV_VDP2_RBMPD | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[4] = STV_VDP2_RBMPE | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[5] = STV_VDP2_RBMPF | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[6] = STV_VDP2_RBMPG | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[7] = STV_VDP2_RBMPH | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[8] = STV_VDP2_RBMPI | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[9] = STV_VDP2_RBMPJ | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[10] = STV_VDP2_RBMPK | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[11] = STV_VDP2_RBMPL | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[12] = STV_VDP2_RBMPM | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[13] = STV_VDP2_RBMPN | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[14] = STV_VDP2_RBMPO | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_offset[15] = STV_VDP2_RBMPP | (STV_VDP2_RBMP_ << 6);
+ stv2_current_tilemap.map_count = 16;
+ }
+
+ stv_vdp2_fill_rotation_parameter_table(iRP);
+
+ if ( iRP == 1 )
+ {
+ stv2_current_tilemap.plane_size = STV_VDP2_RAPLSZ;
+ }
+ else
+ {
+ stv2_current_tilemap.plane_size = STV_VDP2_RBPLSZ;
+ }
+
+ if (stv2_current_tilemap.bitmap_enable)
+ {
+ switch (stv2_current_tilemap.bitmap_size)
+ {
+ case 0: planesizex=512; planesizey=256; break;
+ case 1: planesizex=512; planesizey=512; break;
+ case 2: planesizex=1024; planesizey=256; break;
+ case 3: planesizex=1024; planesizey=512; break;
+ }
+ }
+ else
+ {
+ switch( stv2_current_tilemap.plane_size )
+ {
+ case 0:
+ planesizex = planesizey = 2048;
+ break;
+ case 1:
+ planesizex = 4096;
+ planesizey = 2048;
+ break;
+ case 2:
+ planesizex = 0;
+ planesizey = 0;
+ break;
+ case 3:
+ planesizex = planesizey = 4096;
+ break;
+ }
+ }
+
+ if ( stv_vdp2_is_rotation_applied() == 0 )
+ {
+ stv2_current_tilemap.scrollx = stv_current_rotation_parameter_table.mx >> 16;
+ stv2_current_tilemap.scrolly = stv_current_rotation_parameter_table.my >> 16;
+
+ stv_vdp2_check_tilemap(bitmap,cliprect);
+ }
+ else
+ {
+ if ( !m_vdp2.roz_bitmap[iRP-1].valid() )
+ m_vdp2.roz_bitmap[iRP-1].allocate(4096, 4096);
+
+ roz_clip_rect.min_x = roz_clip_rect.min_y = 0;
+ if ( (iRP == 1 && STV_VDP2_RAOVR == 3) ||
+ (iRP == 2 && STV_VDP2_RBOVR == 3) )
+ {
+ roz_clip_rect.max_x = roz_clip_rect.max_y = 511;
+ planerenderedsizex = planerenderedsizey = 512;
+ }
+ else if (stv_vdp2_are_map_registers_equal() &&
+ !stv2_current_tilemap.bitmap_enable)
+ {
+ roz_clip_rect.max_x = (planesizex / 4) - 1;
+ roz_clip_rect.max_y = (planesizey / 4) - 1;
+ planerenderedsizex = planesizex / 4;
+ planerenderedsizey = planesizey / 4;
+ }
+ else
+ {
+ roz_clip_rect.max_x = planesizex - 1;
+ roz_clip_rect.max_y = planesizey - 1;
+ planerenderedsizex = planesizex;
+ planerenderedsizey = planesizey;
+ }
+
+
+ colour_calculation_enabled = stv2_current_tilemap.colour_calculation_enabled;
+ stv2_current_tilemap.colour_calculation_enabled = 0;
+// window_control = stv2_current_tilemap.window_control;
+// stv2_current_tilemap.window_control = 0;
+ fade_control = stv2_current_tilemap.fade_control;
+ stv2_current_tilemap.fade_control = 0;
+ g_profiler.start(PROFILER_USER1);
+ if ( LOG_VDP2 ) logerror( "Checking for cached RBG bitmap, cache_dirty = %d, memcmp() = %d\n", stv_rbg_cache_data.is_cache_dirty, memcmp(&stv_rbg_cache_data.layer_data[iRP-1],&stv2_current_tilemap,sizeof(stv2_current_tilemap)));
+ if ( (stv_rbg_cache_data.is_cache_dirty & iRP) ||
+ memcmp(&stv_rbg_cache_data.layer_data[iRP-1],&stv2_current_tilemap,sizeof(stv2_current_tilemap)) != 0 )
+ {
+ m_vdp2.roz_bitmap[iRP-1].fill(m_palette->black_pen(), roz_clip_rect );
+ stv_vdp2_check_tilemap(m_vdp2.roz_bitmap[iRP-1], roz_clip_rect);
+ // prepare cache data
+ stv_rbg_cache_data.watch_vdp2_vram_writes |= iRP;
+ stv_rbg_cache_data.is_cache_dirty &= ~iRP;
+ memcpy(&stv_rbg_cache_data.layer_data[iRP-1], &stv2_current_tilemap, sizeof(stv2_current_tilemap));
+ stv_rbg_cache_data.map_offset_min[iRP-1] = stv_vdp2_layer_data_placement.map_offset_min;
+ stv_rbg_cache_data.map_offset_max[iRP-1] = stv_vdp2_layer_data_placement.map_offset_max;
+ stv_rbg_cache_data.tile_offset_min[iRP-1] = stv_vdp2_layer_data_placement.tile_offset_min;
+ stv_rbg_cache_data.tile_offset_max[iRP-1] = stv_vdp2_layer_data_placement.tile_offset_max;
+ if ( LOG_VDP2 ) logerror( "Cache watch: map = %06X - %06X, tile = %06X - %06X\n", stv_rbg_cache_data.map_offset_min[iRP-1],
+ stv_rbg_cache_data.map_offset_max[iRP-1], stv_rbg_cache_data.tile_offset_min[iRP-1], stv_rbg_cache_data.tile_offset_max[iRP-1] );
+ }
+
+ g_profiler.stop();
+
+ stv2_current_tilemap.colour_calculation_enabled = colour_calculation_enabled;
+ if ( colour_calculation_enabled )
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_ALPHA;
+ }
+
+ mycliprect = cliprect;
+
+ /* TODO: remove me. */
+ if ( stv2_current_tilemap.window_control.enabled[0] || stv2_current_tilemap.window_control.enabled[1] )
+ {
+ //popmessage("Window control for RBG");
+ stv_vdp2_apply_window_on_layer(mycliprect);
+ stv2_current_tilemap.window_control.enabled[0] = 0;
+ stv2_current_tilemap.window_control.enabled[1] = 0;
+ }
+
+ stv2_current_tilemap.fade_control = fade_control;
+
+ g_profiler.start(PROFILER_USER2);
+ stv_vdp2_copy_roz_bitmap(bitmap, m_vdp2.roz_bitmap[iRP-1], mycliprect, iRP, planesizex, planesizey, planerenderedsizex, planerenderedsizey );
+ g_profiler.stop();
+ }
+
+}
+
+void saturn_state::stv_vdp2_draw_RBG0(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ /*
+ Colours : 16, 256, 2048, 32768, 16770000
+ Char Size : 1x1 cells, 2x2 cells
+ Pattern Data Size : 1 word, 2 words
+ Plane Layouts : 1 x 1, 2 x 1, 2 x 2
+ Planes : 4
+ Bitmap : Possible
+ Bitmap Sizes : 512 x 256, 512 x 512, 1024 x 256, 1024 x 512
+ Scale : 0.25 x - 256 x
+ Rotation : Yes
+ Linescroll : Yes
+ Column Scroll : Yes
+ Mosaic : Yes
+ */
+
+ stv2_current_tilemap.enabled = STV_VDP2_R0ON;
+
+// if (!stv2_current_tilemap.enabled) return; // stop right now if its disabled ...
+
+ //stv2_current_tilemap.trans_enabled = STV_VDP2_R0TPON;
+ if ( STV_VDP2_R0CCEN )
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 1;
+ stv2_current_tilemap.alpha = ((UINT16)(0x1f-STV_VDP2_R0CCRT)*0xff)/0x1f;
+ }
+ else
+ {
+ stv2_current_tilemap.colour_calculation_enabled = 0;
+ }
+ if ( STV_VDP2_R0TPON == 0 )
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_PEN;
+ }
+ else
+ {
+ stv2_current_tilemap.transparency = STV_TRANSPARENCY_NONE;
+ }
+ stv2_current_tilemap.colour_depth = STV_VDP2_R0CHCN;
+ stv2_current_tilemap.tile_size = STV_VDP2_R0CHSZ;
+ stv2_current_tilemap.bitmap_enable = STV_VDP2_R0BMEN;
+ stv2_current_tilemap.bitmap_size = STV_VDP2_R0BMSZ;
+ stv2_current_tilemap.bitmap_palette_number = STV_VDP2_R0BMP;
+
+ stv2_current_tilemap.pattern_data_size = STV_VDP2_R0PNB;
+ stv2_current_tilemap.character_number_supplement = STV_VDP2_R0CNSM;
+ stv2_current_tilemap.special_priority_register = STV_VDP2_R0SPR;
+ stv2_current_tilemap.special_colour_control_register = STV_VDP2_R0SCC;
+ stv2_current_tilemap.supplementary_palette_bits = STV_VDP2_R0SPLT;
+ stv2_current_tilemap.supplementary_character_bits = STV_VDP2_R0SPCN;
+
+ stv2_current_tilemap.colour_ram_address_offset = STV_VDP2_R0CAOS;
+ stv2_current_tilemap.fade_control = (STV_VDP2_R0COEN * 1) | (STV_VDP2_R0COSL * 2);
+ stv_vdp2_check_fade_control_for_layer();
+ stv2_current_tilemap.window_control.logic = STV_VDP2_R0LOG;
+ stv2_current_tilemap.window_control.enabled[0] = STV_VDP2_R0W0E;
+ stv2_current_tilemap.window_control.enabled[1] = STV_VDP2_R0W1E;
+// stv2_current_tilemap.window_control.? = STV_VDP2_R0SWE;
+ stv2_current_tilemap.window_control.area[0] = STV_VDP2_R0W0A;
+ stv2_current_tilemap.window_control.area[1] = STV_VDP2_R0W1A;
+// stv2_current_tilemap.window_control.? = STV_VDP2_R0SWA;
+
+ stv2_current_tilemap.scrollx = 0;
+ stv2_current_tilemap.scrolly = 0;
+ stv2_current_tilemap.incx = 0x10000;
+ stv2_current_tilemap.incy = 0x10000;
+
+ stv2_current_tilemap.linescroll_enable = 0;
+ stv2_current_tilemap.linescroll_interval = 0;
+ stv2_current_tilemap.linescroll_table_address = 0;
+ stv2_current_tilemap.vertical_linescroll_enable = 0;
+ stv2_current_tilemap.linezoom_enable = 0;
+
+ stv2_current_tilemap.line_screen_enabled = STV_VDP2_R0LCEN;
+ stv2_current_tilemap.mosaic_screen_enabled = STV_VDP2_R0MZE;
+
+ /*Use 0x80 as a normal/rotate switch*/
+ stv2_current_tilemap.layer_name=0x80;
+
+ if ( !stv2_current_tilemap.enabled ) return;
+
+ switch(STV_VDP2_RPMD)
+ {
+ case 0://Rotation Parameter A
+ stv_vdp2_draw_rotation_screen(bitmap, cliprect, 1 );
+ break;
+ case 1://Rotation Parameter B
+ //case 2:
+ stv_vdp2_draw_rotation_screen(bitmap, cliprect, 2 );
+ break;
+ case 2://Rotation Parameter A & B CKTE
+ stv_vdp2_draw_rotation_screen(bitmap, cliprect, 2 );
+ stv_vdp2_draw_rotation_screen(bitmap, cliprect, 1 );
+ break;
+ case 3://Rotation Parameter A & B Window (wrong)
+ stv_vdp2_draw_rotation_screen(bitmap, cliprect, 1 );
+ break;
+ }
+
+}
+
+void saturn_state::stv_vdp2_draw_back(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int x,y;
+ UINT8* gfxdata = m_vdp2.gfx_decode;
+ UINT32 base_offs,base_mask;
+ UINT8 interlace;
+
+ interlace = (STV_VDP2_LSMD == 3)+1;
+
+// popmessage("Back screen %08x %08x %08x",STV_VDP2_BDCLMD,STV_VDP2_BKCLMD,STV_VDP2_BKTA);
+
+ /* draw black if BDCLMD and DISP are cleared */
+ if(!(STV_VDP2_BDCLMD) && !(STV_VDP2_DISP))
+ bitmap.fill(m_palette->black_pen(), cliprect);
+ else
+ {
+ base_mask = STV_VDP2_VRAMSZ ? 0x7ffff : 0x3ffff;
+
+ for(y=cliprect.min_y;y<=cliprect.max_y;y++)
+ {
+ base_offs = ((STV_VDP2_BKTA ) & base_mask) << 1;
+ if(STV_VDP2_BKCLMD)
+ base_offs += ((y / interlace) << 1);
+
+ for(x=cliprect.min_x;x<=cliprect.max_x;x++)
+ {
+ int r,g,b;
+ UINT16 dot;
+
+ dot = (gfxdata[base_offs+0]<<8)|gfxdata[base_offs+1];
+ b = pal5bit((dot & 0x7c00) >> 10);
+ g = pal5bit((dot & 0x03e0) >> 5);
+ r = pal5bit( dot & 0x001f);
+ if(STV_VDP2_BKCOEN)
+ stv_vdp2_compute_color_offset( &r, &g, &b, STV_VDP2_BKCOSL );
+
+ bitmap.pix32(y, x) = rgb_t(r, g, b);
+ }
+ }
+ }
+}
+
+READ32_MEMBER ( saturn_state::saturn_vdp2_vram_r )
+{
+ return m_vdp2_vram[offset];
+}
+
+WRITE32_MEMBER ( saturn_state::saturn_vdp2_vram_w )
+{
+ UINT8* gfxdata = m_vdp2.gfx_decode;
+
+ COMBINE_DATA(&m_vdp2_vram[offset]);
+
+ data = m_vdp2_vram[offset];
+ /* put in gfx region for easy decoding */
+ gfxdata[offset*4+0] = (data & 0xff000000) >> 24;
+ gfxdata[offset*4+1] = (data & 0x00ff0000) >> 16;
+ gfxdata[offset*4+2] = (data & 0x0000ff00) >> 8;
+ gfxdata[offset*4+3] = (data & 0x000000ff) >> 0;
+
+ m_gfxdecode->gfx(0)->mark_dirty(offset/8);
+ m_gfxdecode->gfx(1)->mark_dirty(offset/8);
+ m_gfxdecode->gfx(2)->mark_dirty(offset/8);
+ m_gfxdecode->gfx(3)->mark_dirty(offset/8);
+
+ /* 8-bit tiles overlap, so this affects the previous one as well */
+ if (offset/8 != 0)
+ {
+ m_gfxdecode->gfx(2)->mark_dirty(offset/8 - 1);
+ m_gfxdecode->gfx(3)->mark_dirty(offset/8 - 1);
+ }
+
+ if ( stv_rbg_cache_data.watch_vdp2_vram_writes )
+ {
+ if ( stv_rbg_cache_data.watch_vdp2_vram_writes & STV_VDP2_RBG_ROTATION_PARAMETER_A )
+ {
+ if ( (offset >= stv_rbg_cache_data.map_offset_min[0] &&
+ offset < stv_rbg_cache_data.map_offset_max[0]) ||
+ (offset >= stv_rbg_cache_data.tile_offset_min[0] &&
+ offset < stv_rbg_cache_data.tile_offset_max[0]) )
+ {
+ if ( LOG_VDP2 ) logerror( "RBG Cache: dirtying for RP = 1, write at offset = %06X\n", offset );
+ stv_rbg_cache_data.is_cache_dirty |= STV_VDP2_RBG_ROTATION_PARAMETER_A;
+ stv_rbg_cache_data.watch_vdp2_vram_writes &= ~STV_VDP2_RBG_ROTATION_PARAMETER_A;
+ }
+ }
+ if ( stv_rbg_cache_data.watch_vdp2_vram_writes & STV_VDP2_RBG_ROTATION_PARAMETER_B )
+ {
+ if ( (offset >= stv_rbg_cache_data.map_offset_min[1] &&
+ offset < stv_rbg_cache_data.map_offset_max[1]) ||
+ (offset >= stv_rbg_cache_data.tile_offset_min[1] &&
+ offset < stv_rbg_cache_data.tile_offset_max[1]) )
+ {
+ if ( LOG_VDP2 ) logerror( "RBG Cache: dirtying for RP = 2, write at offset = %06X\n", offset );
+ stv_rbg_cache_data.is_cache_dirty |= STV_VDP2_RBG_ROTATION_PARAMETER_B;
+ stv_rbg_cache_data.watch_vdp2_vram_writes &= ~STV_VDP2_RBG_ROTATION_PARAMETER_B;
+ }
+ }
+ }
+}
+
+READ16_MEMBER ( saturn_state::saturn_vdp2_regs_r )
+{
+ switch(offset)
+ {
+ case 0x002/2:
+ {
+ /* latch h/v signals through HV latch*/
+ if(!STV_VDP2_EXLTEN)
+ {
+ if(!space.debugger_access())
+ {
+ m_vdp2.h_count = get_hcounter();
+ m_vdp2.v_count = get_vcounter();
+ /* latch flag */
+ m_vdp2.exltfg |= 1;
+ }
+ }
+
+ break;
+ }
+ case 0x004/2:
+ {
+ /*Screen Status Register*/
+ /*VBLANK HBLANK ODD PAL */
+ m_vdp2_regs[offset] = (m_vdp2.exltfg<<9) |
+ (m_vdp2.exsyfg<<8) |
+ (get_vblank() << 3) |
+ (get_hblank() << 2) |
+ (get_odd_bit() << 1) |
+ (m_vdp2.pal << 0);
+
+ /* vblank bit is always 1 if DISP bit is disabled */
+ if(!STV_VDP2_DISP)
+ m_vdp2_regs[offset] |= 1 << 3;
+
+ /* HV latches clears if this register is read */
+ if(!space.debugger_access())
+ {
+ m_vdp2.exltfg &= ~1;
+ m_vdp2.exsyfg &= ~1;
+ }
+ break;
+ }
+ case 0x006/2:
+ {
+ m_vdp2_regs[offset] = (STV_VDP2_VRAMSZ << 15) |
+ ((0 << 0) & 0xf); // VDP2 version
+
+ /* Games basically r/w the entire VDP2 register area when this is tripped. (example: Silhouette Mirage)
+ Disable log for the time being. */
+ //if(!space.debugger_access())
+ // printf("Warning: VDP2 version read\n");
+ break;
+ }
+
+ /* HCNT */
+ case 0x008/2:
+ {
+ m_vdp2_regs[offset] = (m_vdp2.h_count);
+ break;
+ }
+
+ /* VCNT */
+ case 0x00a/2:
+ {
+ m_vdp2_regs[offset] = (m_vdp2.v_count);
+ break;
+ }
+
+ default:
+ //if(!space.debugger_access())
+ // printf("VDP2: read from register %08x %08x\n",offset*4,mem_mask);
+ break;
+ }
+
+ return m_vdp2_regs[offset];
+}
+
+READ32_MEMBER ( saturn_state::saturn_vdp2_cram_r )
+{
+ offset &= (0xfff) >> (2);
+ return m_vdp2_cram[offset];
+}
+
+
+
+
+WRITE32_MEMBER ( saturn_state::saturn_vdp2_cram_w )
+{
+ int r,g,b;
+ UINT8 cmode0;
+
+ cmode0 = (STV_VDP2_CRMD & 3) == 0;
+
+ offset &= (0xfff) >> (2);
+ COMBINE_DATA(&m_vdp2_cram[offset]);
+
+ switch( STV_VDP2_CRMD )
+ {
+ /*Mode 2/3*/
+ case 2:
+ case 3:
+ {
+ //offset &= (0xfff) >> 2;
+
+ b = ((m_vdp2_cram[offset] & 0x00ff0000) >> 16);
+ g = ((m_vdp2_cram[offset] & 0x0000ff00) >> 8);
+ r = ((m_vdp2_cram[offset] & 0x000000ff) >> 0);
+ m_palette->set_pen_color(offset,rgb_t(r,g,b));
+ m_palette->set_pen_color(offset^0x400,rgb_t(r,g,b));
+ }
+ break;
+ /*Mode 0*/
+ case 0:
+ case 1:
+ {
+ offset &= (0xfff) >> (cmode0+2);
+
+ b = ((m_vdp2_cram[offset] & 0x00007c00) >> 10);
+ g = ((m_vdp2_cram[offset] & 0x000003e0) >> 5);
+ r = ((m_vdp2_cram[offset] & 0x0000001f) >> 0);
+ m_palette->set_pen_color((offset*2)+1,pal5bit(r),pal5bit(g),pal5bit(b));
+ if(cmode0)
+ m_palette->set_pen_color(((offset*2)+1)^0x400,pal5bit(r),pal5bit(g),pal5bit(b));
+
+ b = ((m_vdp2_cram[offset] & 0x7c000000) >> 26);
+ g = ((m_vdp2_cram[offset] & 0x03e00000) >> 21);
+ r = ((m_vdp2_cram[offset] & 0x001f0000) >> 16);
+ m_palette->set_pen_color(offset*2,pal5bit(r),pal5bit(g),pal5bit(b));
+ if(cmode0)
+ m_palette->set_pen_color((offset*2)^0x400,pal5bit(r),pal5bit(g),pal5bit(b));
+ }
+ break;
+ }
+}
+
+void saturn_state::refresh_palette_data( void )
+{
+ int r,g,b;
+ int c_i;
+ UINT8 bank;
+
+ switch( STV_VDP2_CRMD )
+ {
+ case 2:
+ case 3:
+ {
+ for(c_i=0;c_i<0x400;c_i++)
+ {
+ b = ((m_vdp2_cram[c_i] & 0x00ff0000) >> 16);
+ g = ((m_vdp2_cram[c_i] & 0x0000ff00) >> 8);
+ r = ((m_vdp2_cram[c_i] & 0x000000ff) >> 0);
+ m_palette->set_pen_color(c_i,rgb_t(r,g,b));
+ m_palette->set_pen_color(c_i+0x400,rgb_t(r,g,b));
+ }
+ }
+ break;
+ case 0:
+ {
+ for(bank=0;bank<2;bank++)
+ {
+ for(c_i=0;c_i<0x400;c_i++)
+ {
+ b = ((m_vdp2_cram[c_i] & 0x00007c00) >> 10);
+ g = ((m_vdp2_cram[c_i] & 0x000003e0) >> 5);
+ r = ((m_vdp2_cram[c_i] & 0x0000001f) >> 0);
+ m_palette->set_pen_color((c_i*2)+1+bank*0x400,pal5bit(r),pal5bit(g),pal5bit(b));
+ b = ((m_vdp2_cram[c_i] & 0x7c000000) >> 26);
+ g = ((m_vdp2_cram[c_i] & 0x03e00000) >> 21);
+ r = ((m_vdp2_cram[c_i] & 0x001f0000) >> 16);
+ m_palette->set_pen_color(c_i*2+bank*0x400,pal5bit(r),pal5bit(g),pal5bit(b));
+ }
+ }
+ }
+ break;
+ case 1:
+ {
+ for(c_i=0;c_i<0x800;c_i++)
+ {
+ b = ((m_vdp2_cram[c_i] & 0x00007c00) >> 10);
+ g = ((m_vdp2_cram[c_i] & 0x000003e0) >> 5);
+ r = ((m_vdp2_cram[c_i] & 0x0000001f) >> 0);
+ m_palette->set_pen_color((c_i*2)+1,pal5bit(r),pal5bit(g),pal5bit(b));
+ b = ((m_vdp2_cram[c_i] & 0x7c000000) >> 26);
+ g = ((m_vdp2_cram[c_i] & 0x03e00000) >> 21);
+ r = ((m_vdp2_cram[c_i] & 0x001f0000) >> 16);
+ m_palette->set_pen_color(c_i*2,pal5bit(r),pal5bit(g),pal5bit(b));
+ }
+ }
+ break;
+ }
+}
+
+WRITE16_MEMBER ( saturn_state::saturn_vdp2_regs_w )
+{
+ COMBINE_DATA(&m_vdp2_regs[offset]);
+
+ if(m_vdp2.old_crmd != STV_VDP2_CRMD)
+ {
+ m_vdp2.old_crmd = STV_VDP2_CRMD;
+ refresh_palette_data();
+ }
+ if(m_vdp2.old_tvmd != STV_VDP2_TVMD)
+ {
+ m_vdp2.old_tvmd = STV_VDP2_TVMD;
+ stv_vdp2_dynamic_res_change();
+ }
+
+ if(STV_VDP2_VRAMSZ)
+ printf("VDP2 sets up 8 Mbit VRAM!\n");
+}
+
+int saturn_state::get_hblank_duration( void )
+{
+ int res;
+
+ res = (STV_VDP2_HRES & 1) ? 455 : 427;
+
+ /* double pump horizontal max res */
+ if(STV_VDP2_HRES & 2)
+ res<<=1;
+
+ return res;
+}
+
+/*some vblank lines measurements (according to Charles MacDonald)*/
+/* TODO: interlace mode "eats" one line, should be 262.5 */
+int saturn_state::get_vblank_duration( void )
+{
+ int res;
+
+ res = (m_vdp2.pal) ? 313 : 263;
+
+ /* compensate for interlacing */
+ if((STV_VDP2_LSMD & 3) == 3)
+ res<<=1;
+
+ if(STV_VDP2_HRES & 4)
+ res = (STV_VDP2_HRES & 1) ? 561 : 525; //Hi-Vision / 31kHz Monitor
+
+ return res;
+}
+
+int saturn_state::get_pixel_clock( void )
+{
+ int res,divider;
+
+ res = m_vdp2.dotsel ? MASTER_CLOCK_352 : MASTER_CLOCK_320;
+ /* TODO: divider is ALWAYS 8, this thing is just to over-compensate for MAME framework faults ... */
+ divider = 8;
+
+ if(STV_VDP2_HRES & 2)
+ divider>>=1;
+
+ if((STV_VDP2_LSMD & 3) == 3)
+ divider>>=1;
+
+ if(STV_VDP2_HRES & 4) //TODO
+ divider>>=1;
+
+ return res/divider;
+}
+
+/* TODO: hblank position and hblank firing doesn't really match HW behaviour. */
+UINT8 saturn_state::get_hblank( void )
+{
+ const rectangle &visarea = machine().first_screen()->visible_area();
+ int cur_h = machine().first_screen()->hpos();
+
+ if (cur_h > visarea.max_x) //TODO
+ return 1;
+
+ return 0;
+}
+
+UINT8 saturn_state::get_vblank( void )
+{
+ int cur_v,vblank;
+ cur_v = machine().first_screen()->vpos();
+
+ vblank = get_vblank_start_position() * get_ystep_count();
+
+ if (cur_v >= vblank)
+ return 1;
+
+ return 0;
+}
+
+UINT8 saturn_state::get_odd_bit( void )
+{
+ if(STV_VDP2_HRES & 4) //exclusive monitor mode makes this bit to be always 1
+ return 1;
+
+ if(STV_VDP2_LSMD == 0) // same for non-interlace mode
+ return 1;
+
+ return machine().first_screen()->frame_number() & 1;
+}
+
+int saturn_state::get_vblank_start_position( void )
+{
+ /* TODO: test says that second setting happens at 241, might need further investigation ... */
+ const int d_vres[4] = { 224, 240, 256, 256 };
+ int vres_mask;
+ int vblank_line;
+
+ vres_mask = (m_vdp2.pal << 1)|1; //PAL uses mask 3, NTSC uses mask 1
+ vblank_line = d_vres[STV_VDP2_VRES & vres_mask];
+
+ return vblank_line;
+}
+
+int saturn_state::get_ystep_count( void )
+{
+ int max_y = machine().first_screen()->height();
+ int y_step;
+
+ y_step = 2;
+
+ if((max_y == 263 && m_vdp2.pal == 0) || (max_y == 313 && m_vdp2.pal == 1))
+ y_step = 1;
+
+ return y_step;
+}
+
+/* TODO: these needs to be checked via HW tests! */
+int saturn_state::get_hcounter( void )
+{
+ int hcount;
+
+ hcount = machine().first_screen()->hpos();
+
+ switch(STV_VDP2_HRES & 6)
+ {
+ /* Normal */
+ case 0:
+ hcount &= 0x1ff;
+ hcount <<= 1;
+ break;
+ /* Hi-Res */
+ case 2:
+ hcount &= 0x3ff;
+ break;
+ /* Exclusive Normal*/
+ case 4:
+ hcount &= 0x1ff;
+ break;
+ /* Exclusive Hi-Res */
+ case 6:
+ hcount >>= 1;
+ hcount &= 0x1ff;
+ break;
+ }
+
+ return hcount;
+}
+
+int saturn_state::get_vcounter( void )
+{
+ int vcount;
+
+ vcount = machine().first_screen()->vpos();
+
+ /* Exclusive Monitor */
+ if(STV_VDP2_HRES & 4)
+ return vcount & 0x3ff;
+
+ /* Double Density Interlace */
+ if((STV_VDP2_LSMD & 3) == 3)
+ return (vcount & ~1) | (machine().first_screen()->frame_number() & 1);
+
+ /* docs says << 1, but according to HW tests it's a typo. */
+ assert((vcount & 0x1ff) < ARRAY_LENGTH(true_vcount));
+ return (true_vcount[vcount & 0x1ff][STV_VDP2_VRES]); // Non-interlace
+}
+
+void saturn_state::stv_vdp2_state_save_postload( void )
+{
+ UINT8 *gfxdata = m_vdp2.gfx_decode;
+ int offset;
+ UINT32 data;
+
+ for ( offset = 0; offset < 0x100000/4; offset++ )
+ {
+ data = m_vdp2_vram[offset];
+ /* put in gfx region for easy decoding */
+ gfxdata[offset*4+0] = (data & 0xff000000) >> 24;
+ gfxdata[offset*4+1] = (data & 0x00ff0000) >> 16;
+ gfxdata[offset*4+2] = (data & 0x0000ff00) >> 8;
+ gfxdata[offset*4+3] = (data & 0x000000ff) >> 0;
+
+ m_gfxdecode->gfx(0)->mark_dirty(offset/8);
+ m_gfxdecode->gfx(1)->mark_dirty(offset/8);
+ m_gfxdecode->gfx(2)->mark_dirty(offset/8);
+ m_gfxdecode->gfx(3)->mark_dirty(offset/8);
+
+ /* 8-bit tiles overlap, so this affects the previous one as well */
+ if (offset/8 != 0)
+ {
+ m_gfxdecode->gfx(2)->mark_dirty(offset/8 - 1);
+ m_gfxdecode->gfx(3)->mark_dirty(offset/8 - 1);
+ }
+
+ }
+
+ memset( &stv_rbg_cache_data, 0, sizeof(stv_rbg_cache_data));
+ stv_rbg_cache_data.is_cache_dirty = 3;
+ memset( &stv_vdp2_layer_data_placement, 0, sizeof(stv_vdp2_layer_data_placement));
+
+ refresh_palette_data();
+}
+
+void saturn_state::stv_vdp2_exit ( void )
+{
+ m_vdp2.roz_bitmap[0].reset();
+ m_vdp2.roz_bitmap[1].reset();
+}
+
+int saturn_state::stv_vdp2_start ( void )
+{
+ machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(saturn_state::stv_vdp2_exit), this));
+
+ m_vdp2_regs = auto_alloc_array_clear(machine(), UINT16, 0x040000/2 );
+ m_vdp2_vram = auto_alloc_array_clear(machine(), UINT32, 0x100000/4 );
+ m_vdp2_cram = auto_alloc_array_clear(machine(), UINT32, 0x080000/4 );
+ m_vdp2.gfx_decode = auto_alloc_array(machine(), UINT8, 0x100000 );
+
+// m_gfxdecode->gfx(0)->granularity()=4;
+// m_gfxdecode->gfx(1)->granularity()=4;
+
+ memset( &stv_rbg_cache_data, 0, sizeof(stv_rbg_cache_data));
+ stv_rbg_cache_data.is_cache_dirty = 3;
+ memset( &stv_vdp2_layer_data_placement, 0, sizeof(stv_vdp2_layer_data_placement));
+
+ save_pointer(NAME(m_vdp2_regs), 0x040000/2);
+ save_pointer(NAME(m_vdp2_vram), 0x100000/4);
+ save_pointer(NAME(m_vdp2_cram), 0x080000/4);
+ machine().save().register_postload(save_prepost_delegate(FUNC(saturn_state::stv_vdp2_state_save_postload), this));
+
+ return 0;
+}
+
+/* maybe we should move this to video/stv.c */
+VIDEO_START_MEMBER(saturn_state,stv_vdp2)
+{
+ int i;
+ machine().first_screen()->register_screen_bitmap(m_tmpbitmap);
+ stv_vdp2_start();
+ stv_vdp1_start();
+ m_vdpdebug_roz = 0;
+ m_gfxdecode->gfx(0)->set_source(m_vdp2.gfx_decode);
+ m_gfxdecode->gfx(1)->set_source(m_vdp2.gfx_decode);
+ m_gfxdecode->gfx(2)->set_source(m_vdp2.gfx_decode);
+ m_gfxdecode->gfx(3)->set_source(m_vdp2.gfx_decode);
+
+ /* calc V counter offsets */
+ /* 224 mode */
+ for(i=0;i<263;i++)
+ {
+ true_vcount[i][0] = i;
+ if(i>0xec)
+ true_vcount[i][0]+=0xf9;
+ }
+
+ for(i=0;i<263;i++)
+ {
+ true_vcount[i][1] = i;
+ if(i>0xf5)
+ true_vcount[i][1]+=0xf9;
+ }
+
+ /* 256 mode, todo */
+ for(i=0;i<263;i++)
+ {
+ true_vcount[i][2] = i;
+ true_vcount[i][3] = i;
+ }
+}
+
+void saturn_state::stv_vdp2_dynamic_res_change( void )
+{
+ const int d_vres[4] = { 224, 240, 256, 256 };
+ const int d_hres[4] = { 320, 352, 640, 704 };
+ int horz_res,vert_res;
+ int vres_mask;
+
+ vres_mask = (m_vdp2.pal << 1)|1; //PAL uses mask 3, NTSC uses mask 1
+ vert_res = d_vres[STV_VDP2_VRES & vres_mask];
+
+ if((STV_VDP2_VRES & 3) == 3)
+ popmessage("Illegal VRES MODE, contact MAMEdev");
+
+ /*Double-density interlace mode,doubles the vertical res*/
+ if((STV_VDP2_LSMD & 3) == 3) { vert_res*=2; }
+
+ horz_res = d_hres[STV_VDP2_HRES & 3];
+ /*Exclusive modes,they sets the Vertical Resolution without considering the
+ VRES register.*/
+ if(STV_VDP2_HRES & 4)
+ vert_res = 480;
+
+ {
+ int vblank_period,hblank_period;
+ attoseconds_t refresh;
+ rectangle visarea(0, horz_res-1, 0, vert_res-1);
+
+ vblank_period = get_vblank_duration();
+ hblank_period = get_hblank_duration();
+ refresh = HZ_TO_ATTOSECONDS(get_pixel_clock()) * (hblank_period) * vblank_period;
+ //printf("%d %d %d %d\n",horz_res,vert_res,horz_res+hblank_period,vblank_period);
+
+ machine().first_screen()->configure(hblank_period, vblank_period, visarea, refresh );
+ }
+// machine().first_screen()->set_visible_area(0*8, horz_res-1,0*8, vert_res-1);
+}
+
+/*This is for calculating the rgb brightness*/
+/*TODO: Optimize this...*/
+void saturn_state::stv_vdp2_fade_effects( void )
+{
+ /*
+ Note:We have to use temporary storages because palette_get_color must use
+ variables setted with unsigned int8
+ */
+ INT16 t_r,t_g,t_b;
+ UINT8 r,g,b;
+ rgb_t color;
+ int i;
+ //popmessage("%04x %04x",STV_VDP2_CLOFEN,STV_VDP2_CLOFSL);
+ for(i=0;i<2048;i++)
+ {
+ /*Fade A*/
+ color = m_palette->pen_color(i);
+ t_r = (STV_VDP2_COAR & 0x100) ? (color.r() - (0x100 - (STV_VDP2_COAR & 0xff))) : ((STV_VDP2_COAR & 0xff) + color.r());
+ t_g = (STV_VDP2_COAG & 0x100) ? (color.g() - (0x100 - (STV_VDP2_COAG & 0xff))) : ((STV_VDP2_COAG & 0xff) + color.g());
+ t_b = (STV_VDP2_COAB & 0x100) ? (color.b() - (0x100 - (STV_VDP2_COAB & 0xff))) : ((STV_VDP2_COAB & 0xff) + color.b());
+ if(t_r < 0) { t_r = 0; }
+ if(t_r > 0xff) { t_r = 0xff; }
+ if(t_g < 0) { t_g = 0; }
+ if(t_g > 0xff) { t_g = 0xff; }
+ if(t_b < 0) { t_b = 0; }
+ if(t_b > 0xff) { t_b = 0xff; }
+ r = t_r;
+ g = t_g;
+ b = t_b;
+ m_palette->set_pen_color(i+(2048*1),rgb_t(r,g,b));
+
+ /*Fade B*/
+ color = m_palette->pen_color(i);
+ t_r = (STV_VDP2_COBR & 0x100) ? (color.r() - (0xff - (STV_VDP2_COBR & 0xff))) : ((STV_VDP2_COBR & 0xff) + color.r());
+ t_g = (STV_VDP2_COBG & 0x100) ? (color.g() - (0xff - (STV_VDP2_COBG & 0xff))) : ((STV_VDP2_COBG & 0xff) + color.g());
+ t_b = (STV_VDP2_COBB & 0x100) ? (color.b() - (0xff - (STV_VDP2_COBB & 0xff))) : ((STV_VDP2_COBB & 0xff) + color.b());
+ if(t_r < 0) { t_r = 0; }
+ if(t_r > 0xff) { t_r = 0xff; }
+ if(t_g < 0) { t_g = 0; }
+ if(t_g > 0xff) { t_g = 0xff; }
+ if(t_b < 0) { t_b = 0; }
+ if(t_b > 0xff) { t_b = 0xff; }
+ r = t_r;
+ g = t_g;
+ b = t_b;
+ m_palette->set_pen_color(i+(2048*2),rgb_t(r,g,b));
+ }
+ //popmessage("%04x %04x %04x %04x %04x %04x",STV_VDP2_COAR,STV_VDP2_COAG,STV_VDP2_COAB,STV_VDP2_COBR,STV_VDP2_COBG,STV_VDP2_COBB);
+}
+
+void saturn_state::stv_vdp2_get_window0_coordinates(int *s_x, int *e_x, int *s_y, int *e_y)
+{
+ /*W0*/
+ switch(STV_VDP2_LSMD & 3)
+ {
+ case 0:
+ case 1:
+ case 2:
+ *s_y = ((STV_VDP2_W0SY & 0x3ff) >> 0);
+ *e_y = ((STV_VDP2_W0EY & 0x3ff) >> 0);
+ break;
+ case 3:
+ *s_y = ((STV_VDP2_W0SY & 0x7ff) >> 0);
+ *e_y = ((STV_VDP2_W0EY & 0x7ff) >> 0);
+ break;
+ }
+ switch(STV_VDP2_HRES & 6)
+ {
+ /*Normal*/
+ case 0:
+ *s_x = ((STV_VDP2_W0SX & 0x3fe) >> 1);
+ *e_x = ((STV_VDP2_W0EX & 0x3fe) >> 1);
+ break;
+ /*Hi-Res*/
+ case 2:
+ *s_x = ((STV_VDP2_W0SX & 0x3ff) >> 0);
+ *e_x = ((STV_VDP2_W0EX & 0x3ff) >> 0);
+ break;
+ /*Exclusive Normal*/
+ case 4:
+ *s_x = ((STV_VDP2_W0SX & 0x1ff) >> 0);
+ *e_x = ((STV_VDP2_W0EX & 0x1ff) >> 0);
+ *s_y = ((STV_VDP2_W0SY & 0x3ff) >> 0);
+ *e_y = ((STV_VDP2_W0EY & 0x3ff) >> 0);
+ break;
+ /*Exclusive Hi-Res*/
+ case 6:
+ *s_x = ((STV_VDP2_W0SX & 0x1ff) << 1);
+ *e_x = ((STV_VDP2_W0EX & 0x1ff) << 1);
+ *s_y = ((STV_VDP2_W0SY & 0x3ff) >> 0);
+ *e_y = ((STV_VDP2_W0EY & 0x3ff) >> 0);
+ break;
+ }
+}
+
+void saturn_state::stv_vdp2_get_window1_coordinates(int *s_x, int *e_x, int *s_y, int *e_y)
+{
+ /*W1*/
+ switch(STV_VDP2_LSMD & 3)
+ {
+ case 0:
+ case 1:
+ case 2:
+ *s_y = ((STV_VDP2_W1SY & 0x3ff) >> 0);
+ *e_y = ((STV_VDP2_W1EY & 0x3ff) >> 0);
+ break;
+ case 3:
+ *s_y = ((STV_VDP2_W1SY & 0x7ff) >> 0);
+ *e_y = ((STV_VDP2_W1EY & 0x7ff) >> 0);
+ break;
+ }
+ switch(STV_VDP2_HRES & 6)
+ {
+ /*Normal*/
+ case 0:
+ *s_x = ((STV_VDP2_W1SX & 0x3fe) >> 1);
+ *e_x = ((STV_VDP2_W1EX & 0x3fe) >> 1);
+ break;
+ /*Hi-Res*/
+ case 2:
+ *s_x = ((STV_VDP2_W1SX & 0x3ff) >> 0);
+ *e_x = ((STV_VDP2_W1EX & 0x3ff) >> 0);
+ break;
+ /*Exclusive Normal*/
+ case 4:
+ *s_x = ((STV_VDP2_W1SX & 0x1ff) >> 0);
+ *e_x = ((STV_VDP2_W1EX & 0x1ff) >> 0);
+ *s_y = ((STV_VDP2_W1SY & 0x3ff) >> 0);
+ *e_y = ((STV_VDP2_W1EY & 0x3ff) >> 0);
+ break;
+ /*Exclusive Hi-Res*/
+ case 6:
+ *s_x = ((STV_VDP2_W1SX & 0x1ff) << 1);
+ *e_x = ((STV_VDP2_W1EX & 0x1ff) << 1);
+ *s_y = ((STV_VDP2_W1SY & 0x3ff) >> 0);
+ *e_y = ((STV_VDP2_W1EY & 0x3ff) >> 0);
+ break;
+ }
+
+}
+
+int saturn_state::get_window_pixel(int s_x,int e_x,int s_y,int e_y,int x, int y,UINT8 win_num)
+{
+ int res;
+
+ res = 1;
+ if(stv2_current_tilemap.window_control.enabled[win_num])
+ {
+ if(stv2_current_tilemap.window_control.area[win_num])
+ res = (y >= s_y && y <= e_y && x >= s_x && x <= e_x);
+ else
+ res = (y >= s_y && y <= e_y && x >= s_x && x <= e_x) ^ 1;
+ }
+
+ return res;
+}
+
+inline int saturn_state::stv_vdp2_window_process(int x,int y)
+{
+ int s_x=0,e_x=0,s_y=0,e_y=0;
+ int w0_pix, w1_pix;
+
+ if (stv2_current_tilemap.window_control.enabled[0] == 0 &&
+ stv2_current_tilemap.window_control.enabled[1] == 0)
+ return 1;
+
+ stv_vdp2_get_window0_coordinates(&s_x, &e_x, &s_y, &e_y);
+ w0_pix = get_window_pixel(s_x,e_x,s_y,e_y,x,y,0);
+
+ stv_vdp2_get_window1_coordinates(&s_x, &e_x, &s_y, &e_y);
+ w1_pix = get_window_pixel(s_x,e_x,s_y,e_y,x,y,1);
+
+ return stv2_current_tilemap.window_control.logic & 1 ? (w0_pix | w1_pix) : (w0_pix & w1_pix);
+}
+
+/* TODO: remove this crap. */
+int saturn_state::stv_vdp2_apply_window_on_layer(rectangle &cliprect)
+{
+ int s_x=0,e_x=0,s_y=0,e_y=0;
+
+ if ( stv2_current_tilemap.window_control.enabled[0] && (!stv2_current_tilemap.window_control.area[0]))
+ {
+ /* w0, transparent outside supported */
+ stv_vdp2_get_window0_coordinates(&s_x, &e_x, &s_y, &e_y);
+
+ if ( s_x > cliprect.min_x ) cliprect.min_x = s_x;
+ if ( e_x < cliprect.max_x ) cliprect.max_x = e_x;
+ if ( s_y > cliprect.min_y ) cliprect.min_y = s_y;
+ if ( e_y < cliprect.max_y ) cliprect.max_y = e_y;
+
+ return 1;
+ }
+ else if ( stv2_current_tilemap.window_control.enabled[1] && (!stv2_current_tilemap.window_control.area[1]) )
+ {
+ /* w1, transparent outside supported */
+ stv_vdp2_get_window1_coordinates(&s_x, &e_x, &s_y, &e_y);
+
+ if ( s_x > cliprect.min_x ) cliprect.min_x = s_x;
+ if ( e_x < cliprect.max_x ) cliprect.max_x = e_x;
+ if ( s_y > cliprect.min_y ) cliprect.min_y = s_y;
+ if ( e_y < cliprect.max_y ) cliprect.max_y = e_y;
+
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+void saturn_state::draw_sprites(bitmap_rgb32 &bitmap, const rectangle &cliprect, UINT8 pri)
+{
+ int x,y,r,g,b;
+ int i;
+ UINT16 pix;
+ UINT16 *framebuffer_line;
+ UINT32 *bitmap_line, *bitmap_line2 = NULL;
+ UINT8 interlace_framebuffer;
+ UINT8 double_x;
+ static const UINT16 sprite_colormask_table[] = {
+ 0x07ff, 0x07ff, 0x07ff, 0x07ff, 0x03ff, 0x07ff, 0x03ff, 0x01ff,
+ 0x007f, 0x003f, 0x003f, 0x003f, 0x00ff, 0x00ff, 0x00ff, 0x00ff
+ };
+ static const UINT16 priority_shift_table[] = { 14, 13, 14, 13, 13, 12, 12, 12, 7, 7, 6, 0, 7, 7, 6, 0 };
+ static const UINT16 priority_mask_table[] = { 3, 7, 1, 3, 3, 7, 7, 7, 1, 1, 3, 0, 1, 1, 3, 0 };
+ static const UINT16 ccrr_shift_table[] = { 11, 11, 11, 11, 10, 11, 10, 9, 0, 6, 0, 6, 0, 6, 0, 6 };
+ static const UINT16 ccrr_mask_table[] = { 7, 3, 7, 3, 7, 1, 3, 7, 0, 1, 0, 3, 0, 1, 0, 3 };
+ static const UINT16 shadow_mask_table[] = { 0, 0, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0, 0, 0, 0, 0, 0, 0, 0 };
+ UINT16 alpha_enabled;
+
+ int sprite_type;
+ int sprite_colormask;
+ int color_offset_pal;
+ int sprite_shadow;
+ UINT16 sprite_priority_shift, sprite_priority_mask, sprite_ccrr_shift, sprite_ccrr_mask;
+ UINT8 priority;
+ UINT8 ccr = 0;
+ UINT8 sprite_priorities[8];
+ UINT8 sprite_ccr[8];
+ int sprite_color_mode = STV_VDP2_SPCLMD;
+
+ if ( (stv_sprite_priorities_usage_valid == 1) && (stv_sprite_priorities_used[pri] == 0) )
+ return;
+
+ sprite_priorities[0] = STV_VDP2_S0PRIN;
+ sprite_priorities[1] = STV_VDP2_S1PRIN;
+ sprite_priorities[2] = STV_VDP2_S2PRIN;
+ sprite_priorities[3] = STV_VDP2_S3PRIN;
+ sprite_priorities[4] = STV_VDP2_S4PRIN;
+ sprite_priorities[5] = STV_VDP2_S5PRIN;
+ sprite_priorities[6] = STV_VDP2_S6PRIN;
+ sprite_priorities[7] = STV_VDP2_S7PRIN;
+
+ sprite_ccr[0] = STV_VDP2_S0CCRT;
+ sprite_ccr[1] = STV_VDP2_S1CCRT;
+ sprite_ccr[2] = STV_VDP2_S2CCRT;
+ sprite_ccr[3] = STV_VDP2_S3CCRT;
+ sprite_ccr[4] = STV_VDP2_S4CCRT;
+ sprite_ccr[5] = STV_VDP2_S5CCRT;
+ sprite_ccr[6] = STV_VDP2_S6CCRT;
+ sprite_ccr[7] = STV_VDP2_S7CCRT;
+
+ sprite_type = STV_VDP2_SPTYPE;
+ sprite_colormask = sprite_colormask_table[sprite_type];
+ sprite_priority_shift = priority_shift_table[sprite_type];
+ sprite_priority_mask = priority_mask_table[sprite_type];
+ sprite_ccrr_shift = ccrr_shift_table[sprite_type];
+ sprite_ccrr_mask = ccrr_mask_table[sprite_type];
+ sprite_shadow = shadow_mask_table[sprite_type];
+
+ for ( i = 0; i < (sprite_priority_mask+1); i++ ) if ( sprite_priorities[i] == pri ) break;
+ if ( i == (sprite_priority_mask+1) ) return;
+
+ /* color offset (RGB brightness) */
+ color_offset_pal = 0;
+ if ( STV_VDP2_SPCOEN )
+ {
+ if ( STV_VDP2_SPCOSL == 0 )
+ { color_offset_pal = 2048; }
+ else
+ { color_offset_pal = 2048*2; }
+ }
+
+ /* color calculation (alpha blending)*/
+ if ( STV_VDP2_SPCCEN )
+ {
+ alpha_enabled = 0;
+ switch( STV_VDP2_SPCCCS )
+ {
+ case 0x0: if ( pri <= STV_VDP2_SPCCN ) alpha_enabled = 1; break;
+ case 0x1: if ( pri == STV_VDP2_SPCCN ) alpha_enabled = 1; break;
+ case 0x2: if ( pri >= STV_VDP2_SPCCN ) alpha_enabled = 1; break;
+ case 0x3: alpha_enabled = 2; sprite_shadow = 0; break;
+ }
+ }
+ else
+ {
+ alpha_enabled = 0;
+ }
+
+ /* framebuffer interlace */
+ if ( (STV_VDP2_LSMD == 3) && m_vdp1.framebuffer_double_interlace == 0 )
+ interlace_framebuffer = 1;
+ else
+ interlace_framebuffer = 0;
+
+ /*Guess:Some games needs that the horizontal sprite size to be doubled
+ (TODO: understand the proper settings,it might not work like this)*/
+ if(STV_VDP1_TVM == 0 && STV_VDP2_HRES & 2) // astrass & findlove
+ double_x = 1;
+ else
+ double_x = 0;
+
+ /* window control */
+ stv2_current_tilemap.window_control.logic = STV_VDP2_SPLOG;
+ stv2_current_tilemap.window_control.enabled[0] = STV_VDP2_SPW0E;
+ stv2_current_tilemap.window_control.enabled[1] = STV_VDP2_SPW1E;
+// stv2_current_tilemap.window_control.? = STV_VDP2_SPSWE;
+ stv2_current_tilemap.window_control.area[0] = STV_VDP2_SPW0A;
+ stv2_current_tilemap.window_control.area[1] = STV_VDP2_SPW1A;
+// stv2_current_tilemap.window_control.? = STV_VDP2_SPSWA;
+
+// stv_vdp2_apply_window_on_layer(mycliprect);
+
+ if (interlace_framebuffer == 0 && double_x == 0 )
+ {
+ if ( alpha_enabled == 0 )
+ {
+ for ( y = cliprect.min_y; y <= cliprect.max_y; y++ )
+ {
+ if ( stv_sprite_priorities_usage_valid )
+ if (stv_sprite_priorities_in_fb_line[y][pri] == 0)
+ continue;
+
+ framebuffer_line = m_vdp1.framebuffer_display_lines[y];
+ bitmap_line = &bitmap.pix32(y);
+
+ for ( x = cliprect.min_x; x <= cliprect.max_x; x++ )
+ {
+ if(!stv_vdp2_window_process(x,y))
+ continue;
+
+ pix = framebuffer_line[x];
+ if ( (pix & 0x8000) && sprite_color_mode)
+ {
+ if ( sprite_priorities[0] != pri )
+ {
+ stv_sprite_priorities_used[sprite_priorities[0]] = 1;
+ stv_sprite_priorities_in_fb_line[y][sprite_priorities[0]] = 1;
+ continue;
+ };
+
+ if(STV_VDP2_SPWINEN && pix == 0x8000) /* Pukunpa */
+ continue;
+
+ b = pal5bit((pix & 0x7c00) >> 10);
+ g = pal5bit((pix & 0x03e0) >> 5);
+ r = pal5bit( pix & 0x001f);
+ if ( color_offset_pal )
+ {
+ stv_vdp2_compute_color_offset( &r, &g, &b, STV_VDP2_SPCOSL );
+ }
+
+ bitmap_line[x] = rgb_t(r, g, b);
+ }
+ else
+ {
+ priority = sprite_priorities[(pix >> sprite_priority_shift) & sprite_priority_mask];
+ if ( priority != pri )
+ {
+ stv_sprite_priorities_used[priority] = 1;
+ stv_sprite_priorities_in_fb_line[y][priority] = 1;
+ continue;
+ };
+
+ {
+ pix &= sprite_colormask;
+ if ( pix == (sprite_colormask - 1) )
+ {
+ /*shadow - in reality, we should check from what layer pixel beneath comes...*/
+ if ( STV_VDP2_SDCTL & 0x3f )
+ {
+ rgb_t p = bitmap_line[x];
+ bitmap_line[x] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ }
+ /* note that when shadows are disabled, "shadow" palette entries are not drawn */
+ }
+ else if ( pix )
+ {
+ pix += (STV_VDP2_SPCAOS << 8);
+ pix &= 0x7ff;
+ pix += color_offset_pal;
+ bitmap_line[x] = m_palette->pen( pix );
+ }
+ }
+
+ /* TODO: I don't think this one makes much logic ... (1) */
+ if ( pix & sprite_shadow )
+ {
+ if ( pix & ~sprite_shadow )
+ {
+ rgb_t p = bitmap_line[x];
+ bitmap_line[x] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ else //alpha_enabled == 1
+ {
+ for ( y = cliprect.min_y; y <= cliprect.max_y; y++ )
+ {
+ if ( stv_sprite_priorities_usage_valid )
+ if (stv_sprite_priorities_in_fb_line[y][pri] == 0)
+ continue;
+
+ framebuffer_line = m_vdp1.framebuffer_display_lines[y];
+ bitmap_line = &bitmap.pix32(y);
+
+ for ( x = cliprect.min_x; x <= cliprect.max_x; x++ )
+ {
+ if(!stv_vdp2_window_process(x,y))
+ continue;
+
+ pix = framebuffer_line[x];
+ if ( (pix & 0x8000) && sprite_color_mode)
+ {
+ if ( sprite_priorities[0] != pri )
+ {
+ stv_sprite_priorities_used[sprite_priorities[0]] = 1;
+ stv_sprite_priorities_in_fb_line[y][sprite_priorities[0]] = 1;
+ continue;
+ };
+
+ b = pal5bit((pix & 0x7c00) >> 10);
+ g = pal5bit((pix & 0x03e0) >> 5);
+ r = pal5bit( pix & 0x001f);
+ if ( color_offset_pal )
+ {
+ stv_vdp2_compute_color_offset( &r, &g, &b, STV_VDP2_SPCOSL );
+ }
+ ccr = sprite_ccr[0];
+ if ( STV_VDP2_CCMD )
+ {
+ bitmap_line[x] = stv_add_blend( bitmap_line[x], rgb_t(r, g, b));
+ }
+ else
+ {
+ bitmap_line[x] = alpha_blend_r32( bitmap_line[x], rgb_t(r, g ,b), ((UINT16)(0x1f-ccr)*0xff)/0x1f);
+ }
+ }
+ else
+ {
+ priority = sprite_priorities[(pix >> sprite_priority_shift) & sprite_priority_mask];
+ if ( priority != pri )
+ {
+ stv_sprite_priorities_used[priority] = 1;
+ stv_sprite_priorities_in_fb_line[y][priority] = 1;
+ continue;
+ };
+
+ ccr = sprite_ccr[ (pix >> sprite_ccrr_shift) & sprite_ccrr_mask ];
+ if ( alpha_enabled == 2 )
+ {
+ if ( ( pix & 0x8000 ) == 0 )
+ {
+ ccr = 0;
+ }
+ }
+
+
+ {
+ pix &= sprite_colormask;
+ if ( pix == (sprite_colormask - 1) )
+ {
+ /*shadow - in reality, we should check from what layer pixel beneath comes...*/
+ if ( STV_VDP2_SDCTL & 0x3f )
+ {
+ rgb_t p = bitmap_line[x];
+ bitmap_line[x] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ }
+ /* note that when shadows are disabled, "shadow" palette entries are not drawn */
+ } else if ( pix )
+ {
+ pix += (STV_VDP2_SPCAOS << 8);
+ pix &= 0x7ff;
+ pix += color_offset_pal;
+ if ( ccr > 0 )
+ {
+ if ( STV_VDP2_CCMD )
+ {
+ bitmap_line[x] = stv_add_blend( bitmap_line[x], m_palette->pen(pix) );
+ }
+ else
+ {
+ bitmap_line[x] = alpha_blend_r32( bitmap_line[x], m_palette->pen(pix), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ }
+ }
+ else
+ bitmap_line[x] = m_palette->pen(pix);
+ }
+ }
+
+ /* TODO: (1) */
+ if ( pix & sprite_shadow )
+ {
+ if ( pix & ~sprite_shadow )
+ {
+ rgb_t p = bitmap_line[x];
+ bitmap_line[x] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for ( y = cliprect.min_y; y <= cliprect.max_y / (interlace_framebuffer+1); y++ )
+ {
+ if ( stv_sprite_priorities_usage_valid )
+ if (stv_sprite_priorities_in_fb_line[y][pri] == 0)
+ continue;
+
+ framebuffer_line = m_vdp1.framebuffer_display_lines[y];
+ if ( interlace_framebuffer == 0 )
+ {
+ bitmap_line = &bitmap.pix32(y);
+ }
+ else
+ {
+ bitmap_line = &bitmap.pix32(2*y);
+ bitmap_line2 = &bitmap.pix32(2*y + 1);
+ }
+
+ for ( x = cliprect.min_x; x <= cliprect.max_x /(double_x+1) ; x++ )
+ {
+ if(!stv_vdp2_window_process(x,y))
+ continue;
+
+ pix = framebuffer_line[x];
+ if ( (pix & 0x8000) && sprite_color_mode)
+ {
+ if ( sprite_priorities[0] != pri )
+ {
+ stv_sprite_priorities_used[sprite_priorities[0]] = 1;
+ stv_sprite_priorities_in_fb_line[y][sprite_priorities[0]] = 1;
+ continue;
+ };
+
+ b = pal5bit((pix & 0x7c00) >> 10);
+ g = pal5bit((pix & 0x03e0) >> 5);
+ r = pal5bit( pix & 0x001f);
+ if ( color_offset_pal )
+ {
+ stv_vdp2_compute_color_offset( &r, &g, &b, STV_VDP2_SPCOSL );
+ }
+ if ( alpha_enabled == 0 )
+ {
+ if(double_x)
+ {
+ bitmap_line[x*2] = rgb_t(r, g, b);
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2] = rgb_t(r, g, b);
+ bitmap_line[x*2+1] = rgb_t(r, g, b);
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2+1] = rgb_t(r, g, b);
+ }
+ else
+ {
+ bitmap_line[x] = rgb_t(r, g, b);
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x] = rgb_t(r, g, b);
+ }
+ }
+ else // alpha_blend == 1
+ {
+ ccr = sprite_ccr[0];
+
+ if ( STV_VDP2_CCMD )
+ {
+ if(double_x)
+ {
+ bitmap_line[x*2] = stv_add_blend( bitmap_line[x*2], rgb_t(r, g, b) );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2] = stv_add_blend( bitmap_line2[x*2], rgb_t(r, g, b) );
+ bitmap_line[x*2+1] = stv_add_blend( bitmap_line[x*2+1], rgb_t(r, g, b) );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2+1] = stv_add_blend( bitmap_line2[x*2+1], rgb_t(r, g, b) );
+ }
+ else
+ {
+ bitmap_line[x] = stv_add_blend( bitmap_line[x], rgb_t(r, g, b) );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x] = stv_add_blend( bitmap_line2[x], rgb_t(r, g, b) );
+ }
+ }
+ else
+ {
+ if(double_x)
+ {
+ bitmap_line[x*2] = alpha_blend_r32( bitmap_line[x*2], rgb_t(r, g, b), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2] = alpha_blend_r32( bitmap_line2[x*2], rgb_t(r, g, b), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ bitmap_line[x*2+1] = alpha_blend_r32( bitmap_line[x*2+1], rgb_t(r, g, b), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2+1] = alpha_blend_r32( bitmap_line2[x*2+1], rgb_t(r, g, b), ((UINT16)(0x1f-ccr)*0xff)/0x1f);
+ }
+ else
+ {
+ bitmap_line[x] = alpha_blend_r32( bitmap_line[x], rgb_t(r, g, b), ((UINT16)(0x1f-ccr)*0xff)/0x1f);
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x] = alpha_blend_r32( bitmap_line2[x], rgb_t(r, g, b), ((UINT16)(0x1f-ccr)*0xff)/0x1f);
+ }
+ }
+ }
+ }
+ else
+ {
+ priority = sprite_priorities[(pix >> sprite_priority_shift) & sprite_priority_mask];
+ if ( priority != pri )
+ {
+ stv_sprite_priorities_used[priority] = 1;
+ stv_sprite_priorities_in_fb_line[y][priority] = 1;
+ continue;
+ };
+
+ if ( alpha_enabled )
+ ccr = sprite_ccr[ (pix >> sprite_ccrr_shift) & sprite_ccrr_mask ];
+
+ if ( alpha_enabled == 2 )
+ {
+ if ( ( pix & 0x8000 ) == 0 )
+ {
+ ccr = 0;
+ }
+ }
+
+ {
+ pix &= sprite_colormask;
+ if ( pix == (sprite_colormask - 1) )
+ {
+ /*shadow - in reality, we should check from what layer pixel beneath comes...*/
+ if ( STV_VDP2_SDCTL & 0x3f )
+ {
+ rgb_t p = bitmap_line[x];
+ if(double_x)
+ {
+ p = bitmap_line[x*2];
+ bitmap_line[x*2] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ p = bitmap_line[x*2+1];
+ bitmap_line[x*2+1] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ }
+ else
+ bitmap_line[x] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ }
+ /* note that when shadows are disabled, "shadow" palette entries are not drawn */
+ } else if ( pix )
+ {
+ pix += (STV_VDP2_SPCAOS << 8);
+ pix &= 0x7ff;
+ pix += color_offset_pal;
+ if ( alpha_enabled == 0 )
+ {
+ if(double_x)
+ {
+ bitmap_line[x*2] = m_palette->pen( pix );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2] = m_palette->pen( pix );
+ bitmap_line[x*2+1] = m_palette->pen( pix );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2+1] = m_palette->pen( pix );
+ }
+ else
+ {
+ bitmap_line[x] = m_palette->pen( pix );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x] = m_palette->pen( pix );
+ }
+ }
+ else // alpha_blend == 1
+ {
+ if ( STV_VDP2_CCMD )
+ {
+ if(double_x)
+ {
+ bitmap_line[x*2] = stv_add_blend( bitmap_line[x*2], m_palette->pen(pix) );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2] = stv_add_blend( bitmap_line2[x], m_palette->pen(pix) );
+ bitmap_line[x*2+1] = stv_add_blend( bitmap_line[x*2+1], m_palette->pen(pix) );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2+1] = stv_add_blend( bitmap_line2[x], m_palette->pen(pix) );
+ }
+ else
+ {
+ bitmap_line[x] = stv_add_blend( bitmap_line[x], m_palette->pen(pix) );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x] = stv_add_blend( bitmap_line2[x], m_palette->pen(pix) );
+ }
+ }
+ else
+ {
+ if(double_x)
+ {
+ bitmap_line[x*2] = alpha_blend_r32( bitmap_line[x*2], m_palette->pen(pix), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2] = alpha_blend_r32( bitmap_line2[x], m_palette->pen(pix), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ bitmap_line[x*2+1] = alpha_blend_r32( bitmap_line[x*2+1], m_palette->pen(pix), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x*2+1] = alpha_blend_r32( bitmap_line2[x], m_palette->pen(pix), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ }
+ else
+ {
+ bitmap_line[x] = alpha_blend_r32( bitmap_line[x], m_palette->pen(pix), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ if ( interlace_framebuffer == 1 ) bitmap_line2[x] = alpha_blend_r32( bitmap_line2[x], m_palette->pen(pix), ((UINT16)(0x1f-ccr)*0xff)/0x1f );
+ }
+ }
+ }
+ }
+ }
+
+ /* TODO: (1) */
+ if ( pix & sprite_shadow )
+ {
+ if ( pix & ~sprite_shadow )
+ {
+ rgb_t p = bitmap_line[x];
+ if(double_x)
+ {
+ p = bitmap_line[x*2];
+ bitmap_line[x*2] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ p = bitmap_line[x*2+1];
+ bitmap_line[x*2+1] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ }
+ else
+ bitmap_line[x] = rgb_t(p.r() >> 1, p.g() >> 1, p.b() >> 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ stv_sprite_priorities_usage_valid = 1;
+}
+
+UINT32 saturn_state::screen_update_stv_vdp2(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ stv_vdp2_fade_effects();
+
+ stv_vdp2_draw_back(m_tmpbitmap,cliprect);
+
+ if(STV_VDP2_DISP)
+ {
+ UINT8 pri;
+
+ stv_sprite_priorities_usage_valid = 0;
+ memset(stv_sprite_priorities_used, 0, sizeof(stv_sprite_priorities_used));
+ memset(stv_sprite_priorities_in_fb_line, 0, sizeof(stv_sprite_priorities_in_fb_line));
+
+ /*If a plane has a priority value of zero it isn't shown at all.*/
+ for(pri=1;pri<8;pri++)
+ {
+ if(pri==STV_VDP2_N3PRIN) { stv_vdp2_draw_NBG3(m_tmpbitmap,cliprect); }
+ if(pri==STV_VDP2_N2PRIN) { stv_vdp2_draw_NBG2(m_tmpbitmap,cliprect); }
+ if(pri==STV_VDP2_N1PRIN) { stv_vdp2_draw_NBG1(m_tmpbitmap,cliprect); }
+ if(pri==STV_VDP2_N0PRIN) { stv_vdp2_draw_NBG0(m_tmpbitmap,cliprect); }
+ if(pri==STV_VDP2_R0PRIN) { stv_vdp2_draw_RBG0(m_tmpbitmap,cliprect); }
+ { draw_sprites(m_tmpbitmap,cliprect,pri); }
+ }
+ }
+
+ copybitmap(bitmap, m_tmpbitmap, 0, 0, 0, 0, cliprect);
+
+ #if 0
+ /* Do NOT remove me, used to test video code performance. */
+ if(machine().input().code_pressed(KEYCODE_Q))
+ {
+ popmessage("Halt CPUs");
+ m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
+ m_slave->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
+ m_audiocpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
+ }
+ #endif
+ return 0;
+}
diff --git a/src/devices/video/t6a04.c b/src/devices/video/t6a04.c
new file mode 100644
index 00000000000..89bf9cf716a
--- /dev/null
+++ b/src/devices/video/t6a04.c
@@ -0,0 +1,242 @@
+// license:BSD-3-Clause
+// copyright-holders:Sandro Ronco
+/***************************************************************************
+
+ Toshiba T6A04 LCD controller
+
+ TODO:
+ - busy flag
+ - contrast
+ - slave mode
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/t6a04.h"
+
+// devices
+const device_type T6A04 = &device_creator<t6a04_device>;
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+void t6a04_device::device_validity_check(validity_checker &valid) const
+{
+ if (m_height == 0 || m_width == 0)
+ osd_printf_error("Configured with invalid parameter\n");
+}
+
+//**************************************************************************
+// live device
+//**************************************************************************
+
+//-------------------------------------------------
+// t6a04_device - constructor
+//-------------------------------------------------
+
+t6a04_device::t6a04_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, T6A04, "T6A04", tag, owner, clock, "t6a04", __FILE__),
+ m_height(0),
+ m_width(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void t6a04_device::device_start()
+{
+ save_item(NAME(m_busy_flag));
+ save_item(NAME(m_display_on));
+ save_item(NAME(m_contrast));
+ save_item(NAME(m_xpos));
+ save_item(NAME(m_ypos));
+ save_item(NAME(m_zpos));
+ save_item(NAME(m_active_counter));
+ save_item(NAME(m_direction));
+ save_item(NAME(m_word_len));
+ save_item(NAME(m_opa1));
+ save_item(NAME(m_opa2));
+ save_item(NAME(m_output_reg));
+ save_item(NAME(m_lcd_ram));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void t6a04_device::device_reset()
+{
+ //values taken from the datasheet
+ memset(m_lcd_ram, 0x00, sizeof(m_lcd_ram));
+ m_busy_flag = 0;
+ m_display_on = 0;
+ m_contrast = 0;
+ m_xpos = 0;
+ m_ypos = 0;
+ m_zpos = 0;
+ m_direction = 1;
+ m_active_counter = 1;
+ m_word_len = 1; //8bit mode
+ m_opa1 = 0;
+ m_opa2 = 0;
+ m_output_reg = 0;
+}
+
+
+//**************************************************************************
+// device interface
+//**************************************************************************
+
+UINT32 t6a04_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ UINT8 ypages = m_width>>3;
+ UINT8 last_line = m_zpos + m_height;
+
+ if (m_display_on)
+ {
+ for (int y=0; y<ypages; y++)
+ for (int x=m_zpos; x<last_line; x++)
+ {
+ UINT8 data = m_lcd_ram[(x&0x3f)*15 + y];
+
+ for (int b=7; b>=0; b--)
+ {
+ bitmap.pix16(x&0x3f, y*8+b) = data & 1;
+ data>>=1;
+ }
+ }
+ }
+ else
+ {
+ bitmap.fill(0, cliprect);
+ }
+
+ return 0;
+}
+
+WRITE8_MEMBER(t6a04_device::control_write)
+{
+ if ((data & 0xc0) == 0xc0) // SCE (set contrast)
+ {
+ m_contrast = data&0x3f;
+ }
+ else if ((data & 0xc0) == 0x80) // SXE (set x address)
+ {
+ m_xpos = data&0x3f;
+ }
+ else if ((data & 0xc0) == 0x40) // SZE (set z address)
+ {
+ m_zpos = data&0x3f;
+ }
+ else if ((data & 0xe0) == 0x20) // SYE (set y address)
+ {
+ m_ypos = data&0x1f;
+ }
+ else if ((data & 0xf8) == 0x18) // CHE (test mode)
+ {
+ //???
+ }
+ else if ((data & 0xf8) == 0x10) // OPA1 (op-amp control 1)
+ {
+ m_opa1 = data & 3;
+ }
+ else if ((data & 0xf8) == 0x08) // OPA2 (op-amp control 2)
+ {
+ m_opa2 = data & 3;
+ }
+ else if ((data & 0xfc) == 0x04) // UDE (up/down mode)
+ {
+ m_active_counter = (data & 0x02) >> 1;
+ m_direction = (data & 0x01) ? +1 : -1;
+ }
+ else if ((data & 0xfe) == 0x02) // DPE (display on/off)
+ {
+ m_display_on = data & 1;
+ }
+ else if ((data & 0xfe) == 0x00) // 86E (word length)
+ {
+ m_word_len = data & 1;
+ }
+}
+
+READ8_MEMBER(t6a04_device::control_read)
+{
+ /*
+ status read
+ x--- ---- busy
+ -x-- ---- 8/6 mode
+ --x- ---- display on/off
+ ---x ---- reset state
+ ---- xx-- unused (always 0)
+ ---- --x- x/y counter
+ ---- ---x up/down mode
+ */
+
+ return (m_busy_flag<<7) | (m_word_len<<6) | (m_display_on<<5) | (m_active_counter<<1) | (m_direction == 1 ? 1 : 0);
+}
+
+WRITE8_MEMBER(t6a04_device::data_write)
+{
+ if (m_word_len)
+ {
+ //8bit mode
+ m_lcd_ram[m_xpos*15 + m_ypos] = data;
+ }
+ else
+ {
+ //6bit mode
+ data = data<<0x02;
+ UINT8 start_bit = m_ypos * 6;
+ UINT8 pos_bit = start_bit & 0x07;
+ UINT8 *ti82_video = &m_lcd_ram[(m_xpos*15)+(start_bit>>3)];
+
+ ti82_video[0] = (ti82_video[0] & ~(0xFC>>pos_bit)) | (data>>pos_bit);
+ if(pos_bit>0x02)
+ ti82_video[1] = (ti82_video[1] & ~(0xFC<<(8-pos_bit))) | (data<<(8-pos_bit));
+ }
+
+ if (m_active_counter)
+ m_ypos = (m_ypos + m_direction) & 0x1f;
+ else
+ m_xpos = (m_xpos + m_direction) & 0x3f;
+
+}
+
+READ8_MEMBER(t6a04_device::data_read)
+{
+ UINT8 data = m_output_reg;
+ UINT8 output_reg;
+
+ if (m_word_len)
+ {
+ //8bit mode
+ output_reg = m_lcd_ram[m_xpos*15 + m_ypos];
+ }
+ else
+ {
+ //6bit mode
+ UINT8 start_bit = m_ypos * 6;
+ UINT8 pos_bit = start_bit & 7;
+ UINT8 *ti82_video = &m_lcd_ram[(m_xpos*15)+(start_bit>>3)];
+
+ output_reg = ((((*ti82_video)<<8)+ti82_video[1])>>(10-pos_bit));
+ }
+
+ if (!space.debugger_access())
+ {
+ m_output_reg = output_reg;
+
+ if (m_active_counter)
+ m_ypos = (m_ypos + m_direction) & 0x1f;
+ else
+ m_xpos = (m_xpos + m_direction) & 0x3f;
+ }
+
+ return data;
+}
diff --git a/src/devices/video/t6a04.h b/src/devices/video/t6a04.h
new file mode 100644
index 00000000000..2fe5b68bd33
--- /dev/null
+++ b/src/devices/video/t6a04.h
@@ -0,0 +1,72 @@
+// license:BSD-3-Clause
+// copyright-holders:Sandro Ronco
+/***************************************************************************
+
+ Toshiba T6A04 LCD controller
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __T6A04_H__
+#define __T6A04_H__
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> t6a04_device
+
+class t6a04_device : public device_t
+{
+public:
+ // construction/destruction
+ t6a04_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_size(device_t &device, int w, int h)
+ {
+ t6a04_device &dev = downcast<t6a04_device &>(device);
+ dev.m_width = w;
+ dev.m_height = h;
+ }
+
+ // device interface
+ DECLARE_WRITE8_MEMBER(control_write);
+ DECLARE_READ8_MEMBER(control_read);
+ DECLARE_WRITE8_MEMBER(data_write);
+ DECLARE_READ8_MEMBER(data_read);
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_validity_check(validity_checker &valid) const;
+
+private:
+ UINT8 m_busy_flag;
+ UINT8 m_lcd_ram[960]; //7680 bit (64*120)
+ UINT8 m_display_on;
+ UINT8 m_contrast;
+ UINT8 m_xpos;
+ UINT8 m_ypos;
+ UINT8 m_zpos;
+ INT8 m_direction;
+ UINT8 m_active_counter;
+ UINT8 m_word_len;
+ UINT8 m_opa1;
+ UINT8 m_opa2;
+ UINT8 m_output_reg;
+
+ UINT8 m_height; // number of lines
+ UINT8 m_width; // pixels for line
+};
+
+// device type definition
+extern const device_type T6A04;
+
+#define MCFG_T6A04_SIZE(_width, _height) \
+ t6a04_device::set_size(*device, _width, _height);
+
+#endif
diff --git a/src/devices/video/tea1002.c b/src/devices/video/tea1002.c
new file mode 100644
index 00000000000..ba518e6a6ac
--- /dev/null
+++ b/src/devices/video/tea1002.c
@@ -0,0 +1,85 @@
+// license:GPL-2.0+
+// copyright-holders:Dirk Best
+/***************************************************************************
+
+ TEA1002
+
+ PAL colour encoder and video summer
+
+***************************************************************************/
+
+#include "tea1002.h"
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+const float tea1002_device::m_luminance[] =
+{
+ 0, 22.5, 44, 66.5, 8.5, 31, 52.5, 100, // INV = 0
+ 75, 52.5, 31, 8.5, 66.5, 44, 22.5, 0 // INV = 1
+};
+
+const int tea1002_device::m_phase[] =
+{
+ 0, 103, 241, 167, 347, 61, 283, 0, // INV = 0
+ 0, 283, 61, 347, 167, 241, 103, 0 // INV = 1
+};
+
+const int tea1002_device::m_amplitute[] =
+{
+ 0, 48, 44, 33, 33, 44, 48, 0, // INV = 0
+ 0, 24, 22, 17, 17, 22, 24, 0 // INV = 1
+};
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type TEA1002 = &device_creator<tea1002_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// paula_device - constructor
+//-------------------------------------------------
+
+tea1002_device::tea1002_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, TEA1002, "TEA1002 PAL colour encoder", tag, owner, clock, "tea1002", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tea1002_device::device_start()
+{
+}
+
+
+//**************************************************************************
+// IMPLEMENTATION
+//**************************************************************************
+
+// this could be done in device_start() and cached, but it's only
+// accessed once at PALETTE_INIT anyway
+rgb_t tea1002_device::color(int index)
+{
+ // calculate yuv
+ double y = m_luminance[index] / 100;
+ double u = cos((m_phase[index] + m_tint) * M_PI / 180) * m_amplitute[index] / 100;
+ double v = sin((m_phase[index] + m_tint) * M_PI / 180) * m_amplitute[index] / 100;
+
+ // and convert to rgb
+ double r = y + v * 1.14;
+ double g = y - u * 0.395 - v * 0.581;
+ double b = y + u * 2.032;
+
+ return rgb_t(rgb_t::clamp(r * 255), rgb_t::clamp(g * 255), rgb_t::clamp(b * 255));
+}
diff --git a/src/devices/video/tea1002.h b/src/devices/video/tea1002.h
new file mode 100644
index 00000000000..ee01a2178b2
--- /dev/null
+++ b/src/devices/video/tea1002.h
@@ -0,0 +1,66 @@
+// license:GPL-2.0+
+// copyright-holders:Dirk Best
+/***************************************************************************
+
+ TEA1002
+
+ PAL colour encoder and video summer
+
+ _____ _____
+ INV 1 |* \_/ | 18 CBLNK
+ R 2 | | 17 3,54 MHz
+ G 3 | | 16 GND
+ B 4 | | 15 CBF
+ _CSYNC 5 | TEA1002 | 14 8,86 MHz
+ lum. delay line 6 | | 13 8,86 MHz
+ lum. delay line 7 | | 12 PAL switch
+ comp. video to mod. 8 | | 11 chroma band limiting
+ d.c. adj. / colour bar 9 |_____________| 10 Vp
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __TEA1002_H__
+#define __TEA1002_H__
+
+#include "emu.h"
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_TEA1002_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, TEA1002, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> tea1002_device
+
+class tea1002_device : public device_t
+{
+public:
+ // construction/destruction
+ tea1002_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ rgb_t color(int index);
+
+protected:
+ // device_t overrides
+ virtual void device_start();
+
+private:
+ static const int m_tint = -6; // what is this based on?
+ static const float m_luminance[16];
+ static const int m_phase[16];
+ static const int m_amplitute[16];
+};
+
+// device type definition
+extern const device_type TEA1002;
+
+#endif // __TEA1002_H__
diff --git a/src/devices/video/tlc34076.c b/src/devices/video/tlc34076.c
new file mode 100644
index 00000000000..b695ce08ed7
--- /dev/null
+++ b/src/devices/video/tlc34076.c
@@ -0,0 +1,263 @@
+// license:BSD-3-Clause
+// copyright-holders:Philip Bennett
+/***************************************************************************
+
+ tlc34076.c
+
+ Basic implementation of the TLC34076 palette chip and similar
+ compatible chips.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "tlc34076.h"
+
+
+//**************************************************************************
+// MACROS
+//**************************************************************************
+
+#define PALETTE_WRITE_ADDR 0x00
+#define PALETTE_DATA 0x01
+#define PIXEL_READ_MASK 0x02
+#define PALETTE_READ_ADDR 0x03
+#define GENERAL_CONTROL 0x08
+#define INPUT_CLOCK_SEL 0x09
+#define OUTPUT_CLOCK_SEL 0x0a
+#define MUX_CONTROL 0x0b
+#define PALETTE_PAGE 0x0c
+#define TEST_REGISTER 0x0e
+#define RESET_STATE 0x0f
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+// device type definition
+const device_type TLC34076 = &device_creator<tlc34076_device>;
+
+
+//-------------------------------------------------
+// tlc34076_device - constructor
+//-------------------------------------------------
+tlc34076_device::tlc34076_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TLC34076, "TLC34076 VIP", tag, owner, clock, "tlc34076", __FILE__),
+ m_dacbits(6)
+{
+}
+
+
+//-------------------------------------------------
+// static_set_bits - set DAC resolution
+//-------------------------------------------------
+void tlc34076_device::static_set_bits(device_t &device, tlc34076_bits bits)
+{
+ tlc34076_device &tlc = downcast<tlc34076_device &>(device);
+ tlc.m_dacbits = bits;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void tlc34076_device::device_start()
+{
+ save_item(NAME(m_local_paletteram));
+ save_item(NAME(m_regs));
+ save_item(NAME(m_pens));
+
+ save_item(NAME(m_writeindex));
+ save_item(NAME(m_readindex));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void tlc34076_device::device_reset()
+{
+ m_regs[PIXEL_READ_MASK] = 0xff;
+ m_regs[GENERAL_CONTROL] = 0x03;
+ m_regs[INPUT_CLOCK_SEL] = 0x00;
+ m_regs[OUTPUT_CLOCK_SEL] = 0x3f;
+ m_regs[MUX_CONTROL] = 0x2d;
+ m_regs[PALETTE_PAGE] = 0x00;
+ m_regs[TEST_REGISTER] = 0x00;
+ m_regs[RESET_STATE] = 0x00;
+}
+
+
+//**************************************************************************
+// PUBLIC INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// get_pens - retrieve current palette
+//-------------------------------------------------
+
+const rgb_t *tlc34076_device::get_pens()
+{
+ offs_t i;
+
+ for (i = 0; i < 0x100; i++)
+ {
+ int r, g, b;
+
+ if ((i & m_regs[PIXEL_READ_MASK]) == i)
+ {
+ r = m_local_paletteram[3 * i + 0];
+ g = m_local_paletteram[3 * i + 1];
+ b = m_local_paletteram[3 * i + 2];
+
+ if (m_dacbits == 6)
+ {
+ r = pal6bit(r);
+ g = pal6bit(g);
+ b = pal6bit(b);
+ }
+ }
+ else
+ {
+ r = 0;
+ g = 0;
+ b = 0;
+ }
+
+ m_pens[i] = rgb_t(r, g, b);
+ }
+
+ return m_pens;
+}
+
+
+//-------------------------------------------------
+// read - read access
+//-------------------------------------------------
+
+READ8_MEMBER( tlc34076_device::read )
+{
+ UINT8 result;
+
+ /* keep in range */
+ offset &= 0x0f;
+ result = m_regs[offset];
+
+ /* switch off the offset */
+ switch (offset)
+ {
+ case PALETTE_DATA:
+ if (m_readindex == 0)
+ {
+ m_palettedata[0] = m_local_paletteram[3 * m_regs[PALETTE_READ_ADDR] + 0];
+ m_palettedata[1] = m_local_paletteram[3 * m_regs[PALETTE_READ_ADDR] + 1];
+ m_palettedata[2] = m_local_paletteram[3 * m_regs[PALETTE_READ_ADDR] + 2];
+ }
+ result = m_palettedata[m_readindex++];
+ if (m_readindex == 3)
+ {
+ m_readindex = 0;
+ m_regs[PALETTE_READ_ADDR]++;
+ }
+ break;
+ }
+
+ return result;
+}
+
+
+//-------------------------------------------------
+// write - write access
+//-------------------------------------------------
+
+WRITE8_MEMBER( tlc34076_device::write )
+{
+// UINT8 oldval;
+
+ /* keep in range */
+ offset &= 0x0f;
+// oldval = m_regs[offset];
+ m_regs[offset] = data;
+
+ /* switch off the offset */
+ switch (offset)
+ {
+ case PALETTE_WRITE_ADDR:
+ m_writeindex = 0;
+ break;
+
+ case PALETTE_DATA:
+ m_palettedata[m_writeindex++] = data;
+ if (m_writeindex == 3)
+ {
+ m_local_paletteram[3 * m_regs[PALETTE_WRITE_ADDR] + 0] = m_palettedata[0];
+ m_local_paletteram[3 * m_regs[PALETTE_WRITE_ADDR] + 1] = m_palettedata[1];
+ m_local_paletteram[3 * m_regs[PALETTE_WRITE_ADDR] + 2] = m_palettedata[2];
+ m_writeindex = 0;
+ m_regs[PALETTE_WRITE_ADDR]++;
+ }
+ break;
+
+ case PALETTE_READ_ADDR:
+ m_readindex = 0;
+ break;
+
+ case GENERAL_CONTROL:
+ /*
+ 7 6 5 4 3 2 1 0
+ X X X X X X X 0 HSYNCOUT is active-low
+ X X X X X X X 1 HSYNCOUT is active-high (default)
+ X X X X X X 0 X VSYNCOUT is active-low
+ X X X X X X 1 X VSYNCOUT is active-high (default)
+ X X X X X 0 X X Disable split shift register transfer (default)
+ X X X X 0 1 X X Enable split shift register transfer
+ X X X X 0 X X X Disable special nibble mode (default)
+ X X X X 1 0 X X Enable special nibble mode
+ X X X 0 X X X X 0-IRE pedestal (default)
+ X X X 1 X X X X 7.5-IRE pedestal
+ X X 0 X X X X X Disable sync (default)
+ X X 1 X X X X X Enable sync
+ X 0 X X X X X X Little-endian mode (default)
+ X 1 X X X X X X Big-endian mode
+ 0 X X X X X X X MUXOUT is low (default)
+ 1 X X X X X X X MUXOUT is high
+ */
+ break;
+
+ case INPUT_CLOCK_SEL:
+ /*
+ 3 2 1 0
+ 0 0 0 0 Select CLK0 as clock source?
+ 0 0 0 1 Select CLK1 as clock source
+ 0 0 1 0 Select CLK2 as clock source
+ 0 0 1 1 Select CLK3 as TTL clock source
+ 0 1 0 0 Select CLK3 as TTL clock source
+ 1 0 0 0 Select CLK3 and CLK3 as ECL clock sources
+ */
+ break;
+
+ case OUTPUT_CLOCK_SEL:
+ /*
+ 0 0 0 X X X VCLK frequency = DOTCLK frequency
+ 0 0 1 X X X VCLK frequency = DOTCLK frequency/2
+ 0 1 0 X X X VCLK frequency = DOTCLK frequency/4
+ 0 1 1 X X X VCLK frequency = DOTCLK frequency/8
+ 1 0 0 X X X VCLK frequency = DOTCLK frequency/16
+ 1 0 1 X X X VCLK frequency = DOTCLK frequency/32
+ 1 1 X X X X VCLK output held at logic high level (default condition)
+ X X X 0 0 0 SCLK frequency = DOTCLK frequency
+ X X X 0 0 1 SCLK frequency = DOTCLK frequency/2
+ X X X 0 1 0 SCLK frequency = DOTCLK frequency/4
+ X X X 0 1 1 SCLK frequency = DOTCLK frequency/8
+ X X X 1 0 0 SCLK frequency = DOTCLK frequency/16
+ X X X 1 0 1 SCLK frequency = DOTCLK frequency/32
+ X X X 1 1 X SCLK output held at logic level low (default condition)
+ */
+ break;
+
+ case RESET_STATE:
+ device_reset();
+ break;
+ }
+}
diff --git a/src/devices/video/tlc34076.h b/src/devices/video/tlc34076.h
new file mode 100644
index 00000000000..08c8d666fee
--- /dev/null
+++ b/src/devices/video/tlc34076.h
@@ -0,0 +1,76 @@
+// license:BSD-3-Clause
+// copyright-holders:Philip Bennett
+/***************************************************************************
+
+ tlc34076.h
+
+ Basic implementation of the TLC34076 palette chip and similar
+ compatible chips.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __TLC34076_H__
+#define __TLC34076_H__
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+enum tlc34076_bits
+{
+ TLC34076_6_BIT = 6,
+ TLC34076_8_BIT = 8
+};
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class tlc34076_device : public device_t
+{
+public:
+ // construction/destruction
+ tlc34076_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ static void static_set_bits(device_t &device, tlc34076_bits bits);
+
+ // public interface
+ const rgb_t *get_pens();
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ // internal state
+ UINT8 m_local_paletteram[0x300];
+ UINT8 m_regs[0x10];
+ UINT8 m_palettedata[3];
+ UINT8 m_writeindex;
+ UINT8 m_readindex;
+ UINT8 m_dacbits;
+ rgb_t m_pens[0x100];
+};
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_TLC34076_ADD(_tag, _bits) \
+ MCFG_DEVICE_ADD(_tag, TLC34076, 0) \
+ tlc34076_device::static_set_bits(*device, _bits);
+
+
+extern const device_type TLC34076;
+
+
+#endif /* __TLC34076_H__ */
diff --git a/src/devices/video/tms34061.c b/src/devices/video/tms34061.c
new file mode 100644
index 00000000000..02720cda66a
--- /dev/null
+++ b/src/devices/video/tms34061.c
@@ -0,0 +1,557 @@
+// license:BSD-3-Clause
+// copyright-holders:Zsolt Vasvari, Aaron Giles
+/****************************************************************************
+ * *
+ * Functions to emulate the TMS34061 video controller *
+ * *
+ * Created by Zsolt Vasvari on 5/26/1998. *
+ * Updated by Aaron Giles on 11/21/2000. *
+ * *
+ * This is far from complete. See the TMS34061 User's Guide available on *
+ * www.spies.com/arcade *
+ * *
+ ****************************************************************************/
+
+#include "emu.h"
+#include "tms34061.h"
+
+
+#define VERBOSE (0)
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// tms34061_device - constructor
+//-------------------------------------------------
+
+const device_type TMS34061 = &device_creator<tms34061_device>;
+
+tms34061_device::tms34061_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMS34061, "TMS34061 VSC", tag, owner, clock, "tms34061", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_rowshift(0),
+ m_vramsize(0),
+ m_interrupt_cb(*this),
+ m_xmask(0),
+ m_yshift(0),
+ m_vrammask(0),
+ m_vram(NULL),
+ m_latchram(NULL),
+ m_latchdata(0),
+ m_shiftreg(NULL),
+ m_timer(NULL)
+{
+ memset(m_regs, 0, sizeof(m_regs));
+ memset(&m_display, 0, sizeof(m_display));
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms34061_device::device_start()
+{
+ /* resolve callbak */
+ m_interrupt_cb.resolve();
+
+ /* reset the data */
+ m_vrammask = m_vramsize - 1;
+
+ /* allocate memory for VRAM */
+ m_vram = auto_alloc_array_clear(machine(), UINT8, m_vramsize + 256 * 2);
+
+ /* allocate memory for latch RAM */
+ m_latchram = auto_alloc_array_clear(machine(), UINT8, m_vramsize + 256 * 2);
+
+ /* add some buffer space for VRAM and latch RAM */
+ m_vram += 256;
+ m_latchram += 256;
+
+ /* point the shift register to the base of VRAM for now */
+ m_shiftreg = m_vram;
+
+ /* initialize registers to their default values from the manual */
+ m_regs[TMS34061_HORENDSYNC] = 0x0010;
+ m_regs[TMS34061_HORENDBLNK] = 0x0020;
+ m_regs[TMS34061_HORSTARTBLNK] = 0x01f0;
+ m_regs[TMS34061_HORTOTAL] = 0x0200;
+ m_regs[TMS34061_VERENDSYNC] = 0x0004;
+ m_regs[TMS34061_VERENDBLNK] = 0x0010;
+ m_regs[TMS34061_VERSTARTBLNK] = 0x00f0;
+ m_regs[TMS34061_VERTOTAL] = 0x0100;
+ m_regs[TMS34061_DISPUPDATE] = 0x0000;
+ m_regs[TMS34061_DISPSTART] = 0x0000;
+ m_regs[TMS34061_VERINT] = 0x0000;
+ m_regs[TMS34061_CONTROL1] = 0x7000;
+ m_regs[TMS34061_CONTROL2] = 0x0600;
+ m_regs[TMS34061_STATUS] = 0x0000;
+ m_regs[TMS34061_XYOFFSET] = 0x0010;
+ m_regs[TMS34061_XYADDRESS] = 0x0000;
+ m_regs[TMS34061_DISPADDRESS] = 0x0000;
+ m_regs[TMS34061_VERCOUNTER] = 0x0000;
+
+ /* start vertical interrupt timer */
+ m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(tms34061_device::interrupt), this));
+
+ save_item(NAME(m_regs));
+ save_item(NAME(m_xmask));
+ save_item(NAME(m_yshift));
+ save_pointer(NAME(m_vram), m_vramsize);
+ save_pointer(NAME(m_latchram), m_vramsize);
+ save_item(NAME(m_latchdata));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void tms34061_device::device_reset()
+{
+}
+
+/*************************************
+ *
+ * Global variables
+ *
+ *************************************/
+
+static const char *const regnames[] =
+{
+ "HORENDSYNC", "HORENDBLNK", "HORSTARTBLNK", "HORTOTAL",
+ "VERENDSYNC", "VERENDBLNK", "VERSTARTBLNK", "VERTOTAL",
+ "DISPUPDATE", "DISPSTART", "VERINT", "CONTROL1",
+ "CONTROL2", "STATUS", "XYOFFSET", "XYADDRESS",
+ "DISPADDRESS", "VERCOUNTER"
+};
+
+
+/*************************************
+ *
+ * Interrupt handling
+ *
+ *************************************/
+
+void tms34061_device::update_interrupts()
+{
+ /* if we have a callback, process it */
+ if (!m_interrupt_cb.isnull())
+ {
+ /* if the status bit is set, and ints are enabled, turn it on */
+ if ((m_regs[TMS34061_STATUS] & 0x0001) && (m_regs[TMS34061_CONTROL1] & 0x0400))
+ m_interrupt_cb(ASSERT_LINE);
+ else
+ m_interrupt_cb(CLEAR_LINE);
+ }
+}
+
+
+TIMER_CALLBACK_MEMBER( tms34061_device::interrupt )
+{
+ /* set timer for next frame */
+ m_timer->adjust(m_screen->frame_period());
+
+ /* set the interrupt bit in the status reg */
+ m_regs[TMS34061_STATUS] |= 1;
+
+ /* update the interrupt state */
+ update_interrupts();
+}
+
+
+
+/*************************************
+ *
+ * Register writes
+ *
+ *************************************/
+
+void tms34061_device::register_w(address_space &space, offs_t offset, UINT8 data)
+{
+ int scanline;
+ int regnum = offset >> 2;
+
+ /* certain registers affect the display directly */
+ if ((regnum >= TMS34061_HORENDSYNC && regnum <= TMS34061_DISPSTART) ||
+ (regnum == TMS34061_CONTROL2))
+ m_screen->update_partial(m_screen->vpos());
+
+ /* store the hi/lo half */
+ if (regnum < ARRAY_LENGTH(m_regs))
+ {
+ if (offset & 0x02)
+ m_regs[regnum] = (m_regs[regnum] & 0x00ff) | (data << 8);
+ else
+ m_regs[regnum] = (m_regs[regnum] & 0xff00) | data;
+ }
+
+ /* log it */
+ if (VERBOSE) logerror("%s:tms34061 %s = %04x\n", space.machine().describe_context(), regnames[regnum], m_regs[regnum]);
+
+ /* update the state of things */
+ switch (regnum)
+ {
+ /* vertical interrupt: adjust the timer */
+ case TMS34061_VERINT:
+ scanline = m_regs[TMS34061_VERINT] - m_regs[TMS34061_VERENDBLNK];
+
+ if (scanline < 0)
+ scanline += m_regs[TMS34061_VERTOTAL];
+
+ m_timer->adjust(m_screen->time_until_pos(scanline, m_regs[TMS34061_HORSTARTBLNK]));
+ break;
+
+ /* XY offset: set the X and Y masks */
+ case TMS34061_XYOFFSET:
+ switch (m_regs[TMS34061_XYOFFSET] & 0x00ff)
+ {
+ case 0x01: m_yshift = 2; break;
+ case 0x02: m_yshift = 3; break;
+ case 0x04: m_yshift = 4; break;
+ case 0x08: m_yshift = 5; break;
+ case 0x10: m_yshift = 6; break;
+ case 0x20: m_yshift = 7; break;
+ case 0x40: m_yshift = 8; break;
+ case 0x80: m_yshift = 9; break;
+ default: logerror("Invalid value for XYOFFSET = %04x\n", m_regs[TMS34061_XYOFFSET]); break;
+ }
+ m_xmask = (1 << m_yshift) - 1;
+ break;
+
+ /* CONTROL1: they could have turned interrupts on */
+ case TMS34061_CONTROL1:
+ update_interrupts();
+ break;
+
+ /* other supported registers */
+ case TMS34061_XYADDRESS:
+ break;
+ }
+}
+
+
+
+/*************************************
+ *
+ * Register reads
+ *
+ *************************************/
+
+UINT8 tms34061_device::register_r(address_space &space, offs_t offset)
+{
+ int regnum = offset >> 2;
+ UINT16 result;
+
+ /* extract the correct portion of the register */
+ if (regnum < ARRAY_LENGTH(m_regs))
+ result = m_regs[regnum];
+ else
+ result = 0xffff;
+
+ /* special cases: */
+ switch (regnum)
+ {
+ /* status register: a read here clears it */
+ case TMS34061_STATUS:
+ m_regs[TMS34061_STATUS] = 0;
+ update_interrupts();
+ break;
+
+ /* vertical count register: return the current scanline */
+ case TMS34061_VERCOUNTER:
+ result = (m_screen->vpos()+ m_regs[TMS34061_VERENDBLNK]) % m_regs[TMS34061_VERTOTAL];
+ break;
+ }
+
+ /* log it */
+ if (VERBOSE) logerror("%s:tms34061 %s read = %04X\n", space.machine().describe_context(), regnames[regnum], result);
+ return (offset & 0x02) ? (result >> 8) : result;
+}
+
+
+
+/*************************************
+ *
+ * XY addressing
+ *
+ *************************************/
+
+void tms34061_device::adjust_xyaddress(int offset)
+{
+ /* note that carries are allowed if the Y coordinate isn't being modified */
+ switch (offset & 0x1e)
+ {
+ case 0x00: /* no change */
+ break;
+
+ case 0x02: /* X + 1 */
+ m_regs[TMS34061_XYADDRESS]++;
+ break;
+
+ case 0x04: /* X - 1 */
+ m_regs[TMS34061_XYADDRESS]--;
+ break;
+
+ case 0x06: /* X = 0 */
+ m_regs[TMS34061_XYADDRESS] &= ~m_xmask;
+ break;
+
+ case 0x08: /* Y + 1 */
+ m_regs[TMS34061_XYADDRESS] += 1 << m_yshift;
+ break;
+
+ case 0x0a: /* X + 1, Y + 1 */
+ m_regs[TMS34061_XYADDRESS] = (m_regs[TMS34061_XYADDRESS] & ~m_xmask) |
+ ((m_regs[TMS34061_XYADDRESS] + 1) & m_xmask);
+ m_regs[TMS34061_XYADDRESS] += 1 << m_yshift;
+ break;
+
+ case 0x0c: /* X - 1, Y + 1 */
+ m_regs[TMS34061_XYADDRESS] = (m_regs[TMS34061_XYADDRESS] & ~m_xmask) |
+ ((m_regs[TMS34061_XYADDRESS] - 1) & m_xmask);
+ m_regs[TMS34061_XYADDRESS] += 1 << m_yshift;
+ break;
+
+ case 0x0e: /* X = 0, Y + 1 */
+ m_regs[TMS34061_XYADDRESS] &= ~m_xmask;
+ m_regs[TMS34061_XYADDRESS] += 1 << m_yshift;
+ break;
+
+ case 0x10: /* Y - 1 */
+ m_regs[TMS34061_XYADDRESS] -= 1 << m_yshift;
+ break;
+
+ case 0x12: /* X + 1, Y - 1 */
+ m_regs[TMS34061_XYADDRESS] = (m_regs[TMS34061_XYADDRESS] & ~m_xmask) |
+ ((m_regs[TMS34061_XYADDRESS] + 1) & m_xmask);
+ m_regs[TMS34061_XYADDRESS] -= 1 << m_yshift;
+ break;
+
+ case 0x14: /* X - 1, Y - 1 */
+ m_regs[TMS34061_XYADDRESS] = (m_regs[TMS34061_XYADDRESS] & ~m_xmask) |
+ ((m_regs[TMS34061_XYADDRESS] - 1) & m_xmask);
+ m_regs[TMS34061_XYADDRESS] -= 1 << m_yshift;
+ break;
+
+ case 0x16: /* X = 0, Y - 1 */
+ m_regs[TMS34061_XYADDRESS] &= ~m_xmask;
+ m_regs[TMS34061_XYADDRESS] -= 1 << m_yshift;
+ break;
+
+ case 0x18: /* Y = 0 */
+ m_regs[TMS34061_XYADDRESS] &= m_xmask;
+ break;
+
+ case 0x1a: /* X + 1, Y = 0 */
+ m_regs[TMS34061_XYADDRESS]++;
+ m_regs[TMS34061_XYADDRESS] &= m_xmask;
+ break;
+
+ case 0x1c: /* X - 1, Y = 0 */
+ m_regs[TMS34061_XYADDRESS]--;
+ m_regs[TMS34061_XYADDRESS] &= m_xmask;
+ break;
+
+ case 0x1e: /* X = 0, Y = 0 */
+ m_regs[TMS34061_XYADDRESS] = 0;
+ break;
+ }
+}
+
+
+void tms34061_device::xypixel_w(address_space &space, int offset, UINT8 data)
+{
+ /* determine the offset, then adjust it */
+ offs_t pixeloffs = m_regs[TMS34061_XYADDRESS];
+ if (offset)
+ adjust_xyaddress(offset);
+
+ /* adjust for the upper bits */
+ pixeloffs |= (m_regs[TMS34061_XYOFFSET] & 0x0f00) << 8;
+
+ /* mask to the VRAM size */
+ pixeloffs &= m_vrammask;
+ if (VERBOSE) logerror("%s:tms34061 xy (%04x) = %02x/%02x\n", space.machine().describe_context(), pixeloffs, data, m_latchdata);
+
+ /* set the pixel data */
+ m_vram[pixeloffs] = data;
+ m_latchram[pixeloffs] = m_latchdata;
+}
+
+
+UINT8 tms34061_device::xypixel_r(address_space &space, int offset)
+{
+ /* determine the offset, then adjust it */
+ offs_t pixeloffs = m_regs[TMS34061_XYADDRESS];
+ if (offset)
+ adjust_xyaddress(offset);
+
+ /* adjust for the upper bits */
+ pixeloffs |= (m_regs[TMS34061_XYOFFSET] & 0x0f00) << 8;
+
+ /* mask to the VRAM size */
+ pixeloffs &= m_vrammask;
+
+ /* return the result */
+ return m_vram[pixeloffs];
+}
+
+
+
+/*************************************
+ *
+ * Core writes
+ *
+ *************************************/
+
+void tms34061_device::write(address_space &space, int col, int row, int func, UINT8 data)
+{
+ offs_t offs;
+
+ /* the function code determines what to do */
+ switch (func)
+ {
+ /* both 0 and 2 map to register access */
+ case 0:
+ case 2:
+ register_w(space, col, data);
+ break;
+
+ /* function 1 maps to XY access; col is the address adjustment */
+ case 1:
+ xypixel_w(space, col, data);
+ break;
+
+ /* function 3 maps to direct access */
+ case 3:
+ offs = ((row << m_rowshift) | col) & m_vrammask;
+ if (m_regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (m_regs[TMS34061_CONTROL2] & 3) << 16;
+ if (VERBOSE) logerror("%s:tms34061 direct (%04x) = %02x/%02x\n", space.machine().describe_context(), offs, data, m_latchdata);
+ if (m_vram[offs] != data || m_latchram[offs] != m_latchdata)
+ {
+ m_vram[offs] = data;
+ m_latchram[offs] = m_latchdata;
+ }
+ break;
+
+ /* function 4 performs a shift reg transfer to VRAM */
+ case 4:
+ offs = col << m_rowshift;
+ if (m_regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (m_regs[TMS34061_CONTROL2] & 3) << 16;
+ offs &= m_vrammask;
+ if (VERBOSE) logerror("%s:tms34061 shiftreg write (%04x)\n", space.machine().describe_context(), offs);
+
+ memcpy(&m_vram[offs], m_shiftreg, (size_t)1 << m_rowshift);
+ memset(&m_latchram[offs], m_latchdata, (size_t)1 << m_rowshift);
+ break;
+
+ /* function 5 performs a shift reg transfer from VRAM */
+ case 5:
+ offs = col << m_rowshift;
+ if (m_regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (m_regs[TMS34061_CONTROL2] & 3) << 16;
+ offs &= m_vrammask;
+ if (VERBOSE) logerror("%s:tms34061 shiftreg read (%04x)\n", space.machine().describe_context(), offs);
+
+ m_shiftreg = &m_vram[offs];
+ break;
+
+ /* log anything else */
+ default:
+ logerror("%s:Unsupported TMS34061 function %d\n", space.machine().describe_context(), func);
+ break;
+ }
+}
+
+
+UINT8 tms34061_device::read(address_space &space, int col, int row, int func)
+{
+ int result = 0;
+ offs_t offs;
+
+ /* the function code determines what to do */
+ switch (func)
+ {
+ /* both 0 and 2 map to register access */
+ case 0:
+ case 2:
+ result = register_r(space, col);
+ break;
+
+ /* function 1 maps to XY access; col is the address adjustment */
+ case 1:
+ result = xypixel_r(space, col);
+ break;
+
+ /* funtion 3 maps to direct access */
+ case 3:
+ offs = ((row << m_rowshift) | col) & m_vrammask;
+ result = m_vram[offs];
+ break;
+
+ /* function 4 performs a shift reg transfer to VRAM */
+ case 4:
+ offs = col << m_rowshift;
+ if (m_regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (m_regs[TMS34061_CONTROL2] & 3) << 16;
+ offs &= m_vrammask;
+
+ memcpy(&m_vram[offs], m_shiftreg, (size_t)1 << m_rowshift);
+ memset(&m_latchram[offs], m_latchdata, (size_t)1 << m_rowshift);
+ break;
+
+ /* function 5 performs a shift reg transfer from VRAM */
+ case 5:
+ offs = col << m_rowshift;
+ if (m_regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (m_regs[TMS34061_CONTROL2] & 3) << 16;
+ offs &= m_vrammask;
+
+ m_shiftreg = &m_vram[offs];
+ break;
+
+ /* log anything else */
+ default:
+ logerror("%s:Unsupported TMS34061 function %d\n", space.machine().describe_context(),
+ func);
+ break;
+ }
+
+ return result;
+}
+
+
+
+/*************************************
+ *
+ * Misc functions
+ *
+ *************************************/
+
+READ8_MEMBER( tms34061_device::latch_r )
+{
+ return m_latchdata;
+}
+
+
+WRITE8_MEMBER( tms34061_device::latch_w )
+{
+ if (VERBOSE) logerror("tms34061_latch = %02X\n", data);
+ m_latchdata = data;
+}
+
+
+void tms34061_device::get_display_state()
+{
+ m_display.blanked = (~m_regs[TMS34061_CONTROL2] >> 13) & 1;
+ m_display.vram = m_vram;
+ m_display.latchram = m_latchram;
+ m_display.regs = m_regs;
+
+ /* compute the display start */
+ m_display.dispstart = (m_regs[TMS34061_DISPSTART] << (m_rowshift - 2)) & m_vrammask;
+}
diff --git a/src/devices/video/tms34061.h b/src/devices/video/tms34061.h
new file mode 100644
index 00000000000..fa8d6a98601
--- /dev/null
+++ b/src/devices/video/tms34061.h
@@ -0,0 +1,122 @@
+// license:BSD-3-Clause
+// copyright-holders:Zsolt Vasvari, Aaron Giles
+/****************************************************************************
+ * *
+ * Function prototypes and constants used by the TMS34061 emulator *
+ * *
+ * Created by Zsolt Vasvari on 5/26/1998. *
+ * Updated by Aaron Giles on 11/21/2000. *
+ * *
+ ****************************************************************************/
+
+
+#ifndef __TMS34061_H__
+#define __TMS34061_H__
+
+
+#define MCFG_TMS34061_ROWSHIFT(_shift) \
+ tms34061_device::set_rowshift(*device, _shift);
+
+#define MCFG_TMS34061_VRAM_SIZE(_size) \
+ tms34061_device::set_vram_size(*device, _size);
+
+#define MCFG_TMS34061_INTERRUPT_CB(_devcb) \
+ devcb = &tms34061_device::set_interrupt_callback(*device, DEVCB_##_devcb);
+
+
+/* register constants */
+enum
+{
+ TMS34061_HORENDSYNC = 0,
+ TMS34061_HORENDBLNK,
+ TMS34061_HORSTARTBLNK,
+ TMS34061_HORTOTAL,
+ TMS34061_VERENDSYNC,
+ TMS34061_VERENDBLNK,
+ TMS34061_VERSTARTBLNK,
+ TMS34061_VERTOTAL,
+ TMS34061_DISPUPDATE,
+ TMS34061_DISPSTART,
+ TMS34061_VERINT,
+ TMS34061_CONTROL1,
+ TMS34061_CONTROL2,
+ TMS34061_STATUS,
+ TMS34061_XYOFFSET,
+ TMS34061_XYADDRESS,
+ TMS34061_DISPADDRESS,
+ TMS34061_VERCOUNTER,
+ TMS34061_REGCOUNT
+};
+
+/* display state structure */
+struct tms34061_display
+{
+ UINT8 blanked; /* true if blanked */
+ UINT8 *vram; /* base of VRAM */
+ UINT8 *latchram; /* base of latch RAM */
+ UINT16 *regs; /* pointer to array of registers */
+ offs_t dispstart; /* display start */
+};
+
+
+
+
+// ======================> tms34061_device
+
+class tms34061_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ tms34061_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_rowshift(device_t &device, UINT8 rowshift) { downcast<tms34061_device &>(device).m_rowshift = rowshift; }
+ static void set_vram_size(device_t &device, UINT32 vramsize) { downcast<tms34061_device &>(device).m_vramsize = vramsize; }
+ template<class _Object> static devcb_base &set_interrupt_callback(device_t &device, _Object object) { return downcast<tms34061_device &>(device).m_interrupt_cb.set_callback(object); }
+
+ /* reads/writes to the 34061 */
+ UINT8 read(address_space &space, int col, int row, int func);
+ void write(address_space &space, int col, int row, int func, UINT8 data);
+
+ /* latch settings */
+ DECLARE_READ8_MEMBER( latch_r );
+ DECLARE_WRITE8_MEMBER( latch_w );
+
+ /* video update handling */
+ void get_display_state();
+
+ struct tms34061_display m_display;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ UINT8 m_rowshift; /* VRAM address is (row << rowshift) | col */
+ UINT32 m_vramsize; /* size of video RAM */
+ devcb_write_line m_interrupt_cb; /* interrupt gen callback */
+
+ UINT16 m_regs[TMS34061_REGCOUNT];
+ UINT16 m_xmask;
+ UINT8 m_yshift;
+ UINT32 m_vrammask;
+ UINT8 * m_vram;
+ UINT8 * m_latchram;
+ UINT8 m_latchdata;
+ UINT8 * m_shiftreg;
+ emu_timer * m_timer;
+
+ void update_interrupts(void);
+ TIMER_CALLBACK_MEMBER( interrupt );
+ void register_w(address_space &space, offs_t offset, UINT8 data);
+ UINT8 register_r(address_space &space, offs_t offset);
+ void adjust_xyaddress(int offset);
+ void xypixel_w(address_space &space, int offset, UINT8 data);
+ UINT8 xypixel_r(address_space &space, int offset);
+};
+
+// device type definition
+extern const device_type TMS34061;
+
+#endif
diff --git a/src/devices/video/tms3556.c b/src/devices/video/tms3556.c
new file mode 100644
index 00000000000..9d073bdbe0d
--- /dev/null
+++ b/src/devices/video/tms3556.c
@@ -0,0 +1,658 @@
+// license:BSD-3-Clause
+// copyright-holders:Raphael Nabet
+/*
+ tms3556 emulation
+
+ TODO:
+ * implement remaining flags in control registers
+ * test the whole thing
+ * find the bloody tms3556 manual. I mean the register and VRAM interfaces
+ are mostly guesswork full of hacks, and I'd like to compare it with
+ documentation.
+
+ Raphael Nabet, 2004
+*/
+
+#include "emu.h"
+#include "tms3556.h"
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+const device_type TMS3556 = &device_creator<tms3556_device>;
+
+
+// default address map
+static ADDRESS_MAP_START( tms3556, AS_0, 8, tms3556_device )
+ AM_RANGE(0x0000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *tms3556_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 tms3556_device::readbyte(offs_t address)
+{
+ return space().read_byte(address&0xFFFF);
+}
+
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void tms3556_device::writebyte(offs_t address, UINT8 data)
+{
+ space().write_byte(address&0xFFFF, data);
+}
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// tms3556_device - constructor
+//-------------------------------------------------
+
+tms3556_device::tms3556_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMS3556, "Texas Instruments VDP TMS3556", tag, owner, clock, "tms3556", __FILE__),
+ device_memory_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, NULL, *ADDRESS_MAP_NAME(tms3556)),
+ m_reg_access_phase(0),
+ m_row_col_written(0),
+ m_bamp_written(0),
+ m_colrow(0),
+ m_vdp_acmpxy_mode(dma_write),
+ m_vdp_acmpxy(0),
+ m_vdp_acmp(0),
+ m_init_read(0),
+ m_scanline(0),
+ m_blink(0),
+ m_blink_count(0),
+ m_bg_color(0)
+{
+ for (int i = 0; i < 8; i++)
+ {
+ m_control_regs[i] = 0;
+ m_address_regs[i] = 0xFFFF;
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms3556_device::device_start()
+{
+ // register for state saving
+ save_item(NAME(m_control_regs));
+ save_item(NAME(m_address_regs));
+ save_item(NAME(m_reg_access_phase));
+ save_item(NAME(m_row_col_written));
+ save_item(NAME(m_bamp_written));
+ save_item(NAME(m_colrow));
+// save_item(NAME(m_vdp_acmpxy_mode)); // FIXME : mame cannot save enum
+ save_item(NAME(m_vdp_acmpxy));
+ save_item(NAME(m_vdp_acmp));
+ save_item(NAME(m_scanline));
+ save_item(NAME(m_blink));
+ save_item(NAME(m_blink_count));
+ save_item(NAME(m_bg_color));
+ save_item(NAME(m_name_offset));
+ save_item(NAME(m_cg_flag));
+ save_item(NAME(m_char_line_counter));
+ save_item(NAME(m_dbl_h_phase));
+
+ machine().first_screen()->register_screen_bitmap(m_bitmap);
+}
+
+
+/*static const char *const tms3556_mode_names[] = { "DISPLAY OFF", "TEXT", "GRAPHIC", "MIXED" };*/
+
+
+UINT32 tms3556_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+
+//-------------------------------------------------
+// vram_r - VRAM read
+//-------------------------------------------------
+
+READ8_MEMBER( tms3556_device::vram_r )
+{
+ UINT8 ret;
+ if (m_bamp_written) {
+ m_bamp_written=false;
+ m_vdp_acmpxy_mode=dma_write;
+ if (m_init_read)
+ m_vdp_acmp=VDP_BAMP;
+ else
+ m_vdp_acmp=(VDP_BAMP-1)&0xFFFF;
+ }
+
+ if (m_row_col_written) {
+ m_row_col_written=0;
+ m_vdp_acmpxy_mode=dma_read;
+ if (m_init_read)
+ m_vdp_acmpxy=m_colrow;
+ else
+ m_vdp_acmpxy=(m_colrow-1)&0xFFFF;
+ }
+
+ m_init_read=false;
+ if (m_vdp_acmpxy_mode==dma_read) {
+ ret=readbyte(m_vdp_acmpxy);
+ m_vdp_acmpxy++;
+ if (m_vdp_acmpxy==VDP_BAMTF) m_vdp_acmpxy=VDP_BAMP;
+ } else {
+ ret=readbyte(m_vdp_acmp);
+ m_vdp_acmp++;
+ if (m_vdp_acmp==VDP_BAMTF) m_vdp_acmp=VDP_BAMP;
+ }
+ return ret;
+}
+
+//-------------------------------------------------
+// vram_w - VRAM write
+//-------------------------------------------------
+
+WRITE8_MEMBER( tms3556_device::vram_w )
+{
+ if (m_bamp_written) {
+ m_bamp_written=false;
+ m_vdp_acmpxy_mode=dma_read;
+ m_vdp_acmp=VDP_BAMP;
+ }
+
+ if (m_row_col_written) {
+ m_row_col_written=0;
+ m_vdp_acmpxy_mode=dma_write;
+ m_vdp_acmpxy=m_colrow;
+ }
+
+ if (m_vdp_acmpxy_mode==dma_write) {
+ writebyte(m_vdp_acmpxy,data);
+ m_vdp_acmpxy++;
+ if (m_vdp_acmpxy==VDP_BAMTF) m_vdp_acmpxy=VDP_BAMP;
+ } else {
+ writebyte(m_vdp_acmp,data);
+ m_vdp_acmp++;
+ if (m_vdp_acmp==VDP_BAMTF) m_vdp_acmp=VDP_BAMP;
+ }
+
+}
+
+
+//-------------------------------------------------
+// reg_r - read from register port
+//-------------------------------------------------
+
+READ8_MEMBER( tms3556_device::reg_r )
+{
+ if (LOG) logerror("TMS3556 Reg Read: %06x\n", offset);
+
+ int reply = 0; // FIXME : will send internal status (VBL, HBL...)
+ m_reg_access_phase=0;
+ return reply;
+}
+
+//-------------------------------------------------
+// reg_w - write to register port
+//-------------------------------------------------
+
+WRITE8_MEMBER( tms3556_device::reg_w )
+{
+ static int reg2=0; // FIXME : this static makes that only one TMS3556 will be present in one system...
+ static int reg=0;
+
+ if (LOG) logerror("TMS3556 Reg Write: %06x = %02x\n", offset, data);
+
+ switch (m_reg_access_phase) {
+ case 0:
+ reg=data&0x0F;
+ reg2=(data&0xF0)>>4;
+ if (reg!=0)
+ m_reg_access_phase=1;
+ return;
+
+ case 1:
+ if (reg<8) {
+ m_control_regs[reg]=data;
+ // leve un flag si le dernier registre ecrit est row ou col
+ if ((reg==2) || (reg==1)) {
+ m_colrow=(m_control_regs[2]<<8)|m_control_regs[1];
+ m_row_col_written=true;
+ }
+
+ if (reg2==0) {
+ m_reg_access_phase=0;
+ return;
+ }
+ else {
+ m_reg_access_phase=1;
+ reg=reg2;
+ reg2=0;
+ return;
+ }
+ } else {
+ m_address_regs[reg-8]=(m_control_regs[2]<<8)|m_control_regs[1];
+ // cas speciaux de decalage pour les generateurs
+ if ((reg>=0xB) && (reg<=0xE)) {
+ m_address_regs[reg-8]+=2;
+ m_address_regs[reg-8]&=0xFFFF;
+ } else {
+ m_address_regs[reg-8]+=1;
+ m_address_regs[reg-8]&=0xFFFF;
+ }
+ if (reg==9) {
+ m_row_col_written=false;
+ m_bamp_written=true;
+ m_reg_access_phase=0;
+ return;
+ } else {
+ m_row_col_written=0;
+ m_bamp_written=false;
+ m_reg_access_phase=2;//???
+ return;
+ }
+ logerror("VDP16[%d] = x%x",reg,m_address_regs[reg-8]);
+ if (reg2==0) {
+ m_reg_access_phase=0;
+ return;
+ }
+ else {
+ m_reg_access_phase=1;
+ reg=reg2;
+ reg2=0;
+ return;
+ }
+ }
+ case 2:
+ m_reg_access_phase=0;
+ return;
+ }
+}
+
+//--------------------------------------------------------------------------
+// initptr_r - set VDP in read mode (not exacly on the VDP but on the TAL)
+//--------------------------------------------------------------------------
+
+READ8_MEMBER( tms3556_device::initptr_r )
+{
+ m_init_read=true;
+ return 0xff;
+}
+
+
+//-------------------------------------------------
+// redraw code
+//-------------------------------------------------
+
+
+//-------------------------------------------------
+// draw_line_empty - draw an empty line (used for
+// top and bottom borders, and screen off mode)
+//-------------------------------------------------
+
+void tms3556_device::draw_line_empty(UINT16 *ln)
+{
+ int i;
+
+ for (i = 0; i < TMS3556_TOTAL_WIDTH; i++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+}
+
+
+//-------------------------------------------------
+// draw_line_text_common - draw a line of text
+// (called by draw_line_text and draw_line_mixed)
+//-------------------------------------------------
+
+void tms3556_device::draw_line_text_common(UINT16 *ln)
+{
+ int pattern, x, xx, i, name_offset;
+ UINT16 fg, bg;
+ offs_t nametbl_base;
+ offs_t patterntbl_base[4];
+ int name_hi, name_lo;
+ int pattern_ix;
+ int alphanumeric_mode, dbl_w, dbl_h, dbl_w_phase = 0;
+
+ nametbl_base = m_address_regs[2];
+ for (i = 0; i < 4; i++)
+ patterntbl_base[i] = m_address_regs[i + 3];
+
+ for (xx = 0; xx < TMS3556_LEFT_BORDER; xx++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+
+ name_offset = m_name_offset;
+
+ for (x = 0; x < 40; x++)
+ {
+ name_hi = readbyte(nametbl_base + name_offset);
+ name_lo = readbyte(nametbl_base + name_offset + 1);
+ pattern_ix = ((name_hi >> 2) & 2) | ((name_hi >> 4) & 1);
+ alphanumeric_mode = (pattern_ix < 2) || ((pattern_ix == 3) && !(m_control_regs[7] & 0x08));
+ fg = (name_hi >> 5) & 0x7;
+ if (alphanumeric_mode)
+ {
+ if (name_hi & 4)
+ { /* inverted color */
+ bg = fg;
+ fg = m_bg_color;
+ }
+ else
+ bg = m_bg_color;
+ dbl_w = name_hi & 0x2;
+ dbl_h = name_hi & 0x1;
+ }
+ else
+ {
+ bg = name_hi & 0x7;
+ dbl_w = 0;
+ dbl_h = 0;
+ }
+ if ((name_lo & 0x80) && m_blink)
+ fg = bg; /* blink off time */
+ if (! dbl_h)
+ { /* single height */
+ pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * m_char_line_counter);
+ if (m_char_line_counter == 0)
+ m_dbl_h_phase[x] = 0;
+ }
+ else
+ { /* double height */
+ if (! m_dbl_h_phase[x])
+ /* first phase: pattern from upper half */
+ pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * (5 + (m_char_line_counter >> 1)));
+ else
+ /* second phase: pattern from lower half */
+ pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * (m_char_line_counter >> 1));
+ if (m_char_line_counter == 0)
+ m_dbl_h_phase[x] = !m_dbl_h_phase[x];
+ }
+ if (!dbl_w)
+ { /* single width */
+ for (xx = 0; xx < 8; xx++)
+ {
+ UINT16 color = (pattern & 0x80) ? fg : bg;
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = color;
+#endif
+ *ln++ = color;
+ pattern <<= 1;
+ }
+ dbl_w_phase = 0;
+ }
+ else
+ { /* double width */
+ if (dbl_w_phase)
+ /* second phase: display right half */
+ pattern <<= 4;
+ for (xx = 0; xx < 4; xx++)
+ {
+ UINT16 color = (pattern & 0x80) ? fg : bg;
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = color; *ln++ = color;
+#endif
+ *ln++ = color; *ln++ = color;
+ pattern <<= 1;
+ }
+ dbl_w_phase = !dbl_w_phase;
+ }
+ name_offset += 2;
+ }
+
+ for (xx = 0; xx < TMS3556_RIGHT_BORDER; xx++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+
+ if (m_char_line_counter == 0)
+ m_name_offset = name_offset;
+}
+
+
+//-------------------------------------------------
+// draw_line_bitmap_common - draw a line of bitmap
+// (called by draw_line_bitmap and draw_line_mixed)
+//-------------------------------------------------
+
+void tms3556_device::draw_line_bitmap_common(UINT16 *ln)
+{
+ int x, xx;
+ offs_t nametbl_base;
+ int name_b, name_g, name_r;
+
+ nametbl_base = m_address_regs[2];
+
+ for (xx = 0; xx < TMS3556_LEFT_BORDER; xx++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+
+ for (x = 0; x < 40; x++)
+ {
+ name_b = readbyte(nametbl_base + m_name_offset);
+ name_g = readbyte(nametbl_base + m_name_offset + 1);
+ name_r = readbyte(nametbl_base + m_name_offset + 2);
+ for (xx = 0; xx < 8; xx++)
+ {
+ UINT16 color = ((name_b >> 5) & 0x4) | ((name_g >> 6) & 0x2) | ((name_r >> 7) & 0x1);
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = color;
+#endif
+ *ln++ = color;
+ name_b <<= 1;
+ name_g <<= 1;
+ name_r <<= 1;
+ }
+ m_name_offset += 3;
+ }
+
+ for (xx = 0; xx < TMS3556_RIGHT_BORDER; xx++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+}
+
+
+//-------------------------------------------------
+// draw_line_text - draw a line in text mode
+//-------------------------------------------------
+
+void tms3556_device::draw_line_text(UINT16 *ln)
+{
+ if (m_char_line_counter == 0)
+ m_char_line_counter = 10;
+ m_char_line_counter--;
+ draw_line_text_common(ln);
+}
+
+
+//-------------------------------------------------
+// draw_line_bitmap - draw a line in bitmap mode
+//-------------------------------------------------
+
+void tms3556_device::draw_line_bitmap(UINT16 *ln)
+{
+ draw_line_bitmap_common(ln);
+ m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
+ m_name_offset += 2;
+}
+
+
+//-------------------------------------------------
+// draw_line_mixed - draw a line in mixed mode
+//-------------------------------------------------
+
+void tms3556_device::draw_line_mixed(UINT16 *ln)
+{
+ if (m_cg_flag)
+ { /* bitmap line */
+ draw_line_bitmap_common(ln);
+ m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
+ m_cg_flag = (readbyte(m_address_regs[2] + m_name_offset) >> 4) & 0x1;
+ m_name_offset += 2;
+ }
+ else
+ { /* text line */
+ if (m_char_line_counter == 0)
+ m_char_line_counter = 10;
+ m_char_line_counter--;
+ draw_line_text_common(ln);
+ if (m_char_line_counter == 0)
+ {
+ m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
+ m_cg_flag = (readbyte(m_address_regs[2] + m_name_offset) >> 4) & 0x1;
+ m_name_offset += 2;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// draw_line - draw a line. If non-interlaced mode,
+// duplicate the line.
+//-------------------------------------------------
+
+void tms3556_device::draw_line(bitmap_ind16 &bmp, int line)
+{
+ int double_lines = 0;
+ UINT16 *ln, *ln2 = NULL;
+
+// if (m_control_regs[4] & 0x??)
+// { // interlaced mode
+// ln = &bmp->pix16(line, m_field);
+// }
+// else
+ { /* non-interlaced mode */
+ ln = &bmp.pix16(line);
+ ln2 = &bmp.pix16(line, 1);
+ double_lines = 1;
+ }
+
+ if ((line < TMS3556_TOP_BORDER) || (line >= (TMS3556_TOP_BORDER + 250)))
+ {
+ /* draw top and bottom borders */
+ draw_line_empty(ln);
+ m_cg_flag=0; // FIXME : forme text mode for 1st line in mixed
+ }
+ else
+ {
+ /* draw useful area */
+ switch (m_control_regs[6] >> 6)
+ {
+ case TMS3556_MODE_OFF:
+ draw_line_empty(ln);
+ break;
+ case TMS3556_MODE_TEXT:
+ draw_line_text(ln);
+ break;
+ case TMS3556_MODE_BITMAP:
+ draw_line_bitmap(ln);
+ break;
+ case TMS3556_MODE_MIXED:
+ draw_line_mixed(ln);
+ break;
+ }
+ }
+
+ if (double_lines)
+ {
+ // TODO: this overlaps in exeltel - use memmove for now
+ //memcpy(ln2, ln, TMS3556_TOTAL_WIDTH * (TMS3556_DOUBLE_WIDTH ? 2 : 1));
+ memmove(ln2, ln, TMS3556_TOTAL_WIDTH * (TMS3556_DOUBLE_WIDTH ? 2 : 1));
+ }
+}
+
+
+//-------------------------------------------------
+// interrupt_start_vblank - Do vblank-time tasks
+//-------------------------------------------------
+
+void tms3556_device::interrupt_start_vblank(void)
+{
+ /* at every frame, vdp switches fields */
+ //m_field = !m_field;
+
+ /* color blinking */
+ if (m_blink_count)
+ m_blink_count--;
+ if (!m_blink_count)
+ {
+ m_blink = !m_blink;
+ m_blink_count = 60; /*no idea what the real value is*/
+ }
+ /* reset background color */
+ m_bg_color = (m_control_regs[7] >> 5) & 0x7;
+ /* reset name offset */
+ m_name_offset = 0;
+ /* reset character line counter */
+ m_char_line_counter = 0;
+ /* reset c/g flag */
+ m_cg_flag = 0;
+ /* reset double height phase flags */
+ memset(m_dbl_h_phase, 0, sizeof(m_dbl_h_phase));
+}
+
+
+//-------------------------------------------------
+// interrupt - scanline handler
+//-------------------------------------------------
+
+void tms3556_device::interrupt(running_machine &machine)
+{
+ /* check for start of vblank */
+ if (m_scanline == 310) /*no idea what the real value is*/
+ interrupt_start_vblank();
+
+ /* render the current line */
+ if ((m_scanline >= 0) && (m_scanline < TMS3556_TOTAL_HEIGHT))
+ {
+ //if (!video_skip_this_frame())
+ draw_line(m_bitmap, m_scanline);
+ }
+
+ if (++m_scanline == 313)
+ m_scanline = 0;
+}
diff --git a/src/devices/video/tms3556.h b/src/devices/video/tms3556.h
new file mode 100644
index 00000000000..a331b8333e3
--- /dev/null
+++ b/src/devices/video/tms3556.h
@@ -0,0 +1,139 @@
+// license:BSD-3-Clause
+// copyright-holders:Raphael Nabet
+/***************************************************************************
+
+ Texas Instruments TMS3556 Video Display Processor
+
+ ***************************************************************************/
+
+
+#pragma once
+
+#ifndef __TMS3556_H__
+#define __TMS3556_H__
+
+///*************************************************************************
+// MACROS / CONSTANTS
+///*************************************************************************
+
+#define TMS3556_TOP_BORDER 1
+#define TMS3556_BOTTOM_BORDER 1
+#define TMS3556_LEFT_BORDER 8
+#define TMS3556_RIGHT_BORDER 8
+#define TMS3556_TOTAL_WIDTH (320 + TMS3556_LEFT_BORDER + TMS3556_RIGHT_BORDER)
+#define TMS3556_TOTAL_HEIGHT (250 + TMS3556_TOP_BORDER + TMS3556_BOTTOM_BORDER)
+
+/* if DOUBLE_WIDTH set, the horizontal resolution is doubled */
+#define TMS3556_DOUBLE_WIDTH 0
+
+#define TMS3556_MODE_OFF 0
+#define TMS3556_MODE_TEXT 1
+#define TMS3556_MODE_BITMAP 2
+#define TMS3556_MODE_MIXED 3
+
+#define VDP_POINTER m_control_regs[0]
+#define VDP_COL m_control_regs[1]
+#define VDP_ROW m_control_regs[2]
+#define VDP_STAT m_control_regs[3]
+#define VDP_CM1 m_control_regs[4]
+#define VDP_CM2 m_control_regs[5]
+#define VDP_CM3 m_control_regs[6]
+#define VDP_CM4 m_control_regs[7]
+#define VDP_BAMT m_address_regs[0]
+#define VDP_BAMP m_address_regs[1]
+#define VDP_BAPA m_address_regs[2]
+#define VDP_BAGC0 m_address_regs[3]
+#define VDP_BAGC1 m_address_regs[4]
+#define VDP_BAGC2 m_address_regs[5]
+#define VDP_BAGC3 m_address_regs[6]
+#define VDP_BAMTF m_address_regs[7]
+
+///*************************************************************************
+// INTERFACE CONFIGURATION MACROS
+///*************************************************************************
+
+#define MCFG_TMS3556_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, TMS3556, 0)
+
+///*************************************************************************
+// TYPE DEFINITIONS
+///*************************************************************************
+
+typedef enum { dma_read, dma_write } dma_mode_tt;
+
+
+// ======================> tms3556_device
+
+class tms3556_device : public device_t,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ tms3556_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_READ8_MEMBER( vram_r );
+ DECLARE_WRITE8_MEMBER( vram_w );
+ DECLARE_READ8_MEMBER( reg_r );
+ DECLARE_WRITE8_MEMBER( reg_w );
+ DECLARE_READ8_MEMBER( initptr_r );
+
+ void interrupt(running_machine &machine);
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+ // device_config_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ // address space configurations
+ const address_space_config m_space_config;
+
+ inline UINT8 readbyte(offs_t address);
+ inline void writebyte(offs_t address, UINT8 data);
+
+ void draw_line_empty(UINT16 *ln);
+ void draw_line_text_common(UINT16 *ln);
+ void draw_line_bitmap_common(UINT16 *ln);
+ void draw_line_text(UINT16 *ln);
+ void draw_line_bitmap(UINT16 *ln);
+ void draw_line_mixed(UINT16 *ln);
+ void draw_line(bitmap_ind16 &bmp, int line);
+ void interrupt_start_vblank(void);
+
+private:
+ // registers
+ UINT8 m_control_regs[8];
+ UINT16 m_address_regs[8];
+
+ // register interface
+ int m_reg_access_phase;
+
+ int m_row_col_written;
+ int m_bamp_written;
+ int m_colrow;
+ dma_mode_tt m_vdp_acmpxy_mode;
+ UINT16 m_vdp_acmpxy;
+ UINT16 m_vdp_acmp;
+ int m_init_read;
+
+ int m_scanline; // scanline counter
+ int m_blink, m_blink_count; // blinking
+ int m_bg_color; // background color for current line
+ int m_name_offset; // current offset in name table
+ int m_cg_flag; // c/g flag (mixed mode only)
+ int m_char_line_counter; // character line counter (decrements from 10, 0 when we have reached
+ // last line of character row)
+ int m_dbl_h_phase[40]; // double height phase flags (one per horizontal character position)
+
+ bitmap_ind16 m_bitmap;
+};
+
+
+// device type definition
+extern const device_type TMS3556;
+
+
+#endif
diff --git a/src/devices/video/tms9927.c b/src/devices/video/tms9927.c
new file mode 100644
index 00000000000..85c6a178243
--- /dev/null
+++ b/src/devices/video/tms9927.c
@@ -0,0 +1,326 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/**********************************************************************
+
+ TI TMS9927 and compatible CRT controller emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "video/tms9927.h"
+
+
+static const UINT8 chars_per_row_value[8] = { 20, 32, 40, 64, 72, 80, 96, 132 };
+static const UINT8 skew_bits_value[4] = { 0, 1, 2, 2 };
+
+
+#define HCOUNT (m_reg[0] + 1)
+#define INTERLACED ((m_reg[1] >> 7) & 0x01)
+#define HSYNC_WIDTH ((m_reg[1] >> 4) & 0x0f)
+#define HSYNC_DELAY ((m_reg[1] >> 0) & 0x07)
+#define SCANS_PER_DATA_ROW (((m_reg[2] >> 3) & 0x0f) + 1)
+#define CHARS_PER_DATA_ROW (chars_per_row_value[(m_reg[2] >> 0) & 0x07])
+#define SKEW_BITS (skew_bits_value[(m_reg[3] >> 6) & 0x03])
+#define DATA_ROWS_PER_FRAME (((m_reg[3] >> 0) & 0x3f) + 1)
+#define SCAN_LINES_PER_FRAME ((m_reg[4] * 2) + 256)
+#define VERTICAL_DATA_START (m_reg[5])
+#define LAST_DISP_DATA_ROW (m_reg[6] & 0x3f)
+#define CURSOR_CHAR_ADDRESS (m_reg[7])
+#define CURSOR_ROW_ADDRESS (m_reg[8] & 0x3f)
+
+
+const device_type TMS9927 = &device_creator<tms9927_device>;
+const device_type CRT5027 = &device_creator<crt5027_device>;
+const device_type CRT5037 = &device_creator<crt5037_device>;
+const device_type CRT5057 = &device_creator<crt5057_device>;
+
+tms9927_device::tms9927_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMS9927, "TMS9927 VTC", tag, owner, clock, "tms9927", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_write_vsyn(*this),
+ m_hpixels_per_column(0),
+ m_selfload_region(NULL),
+ m_reset(0)
+{
+ memset(m_reg, 0x00, sizeof(m_reg));
+}
+
+tms9927_device::tms9927_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_video_interface(mconfig, *this),
+ m_write_vsyn(*this),
+ m_hpixels_per_column(0),
+ m_selfload_region(NULL),
+ m_reset(0)
+{
+ memset(m_reg, 0x00, sizeof(m_reg));
+}
+
+crt5027_device::crt5027_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9927_device(mconfig, CRT5027, "CRT5027", tag, owner, clock, "crt5027", __FILE__)
+{
+}
+
+crt5037_device::crt5037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9927_device(mconfig, CRT5037, "CRT5037", tag, owner, clock, "crt5037", __FILE__)
+{
+}
+
+crt5057_device::crt5057_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9927_device(mconfig, CRT5057, "CRT5057", tag, owner, clock, "crt5057", __FILE__)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms9927_device::device_start()
+{
+ assert(clock() > 0);
+ assert(m_hpixels_per_column > 0);
+
+ /* copy the initial parameters */
+ m_clock = clock();
+
+ /* get the self-load PROM */
+ if (m_selfload_region != NULL)
+ {
+ m_selfload = machine().root_device().memregion(m_selfload_region)->base();
+ assert(m_selfload != NULL);
+ }
+
+ // resolve callbacks
+ m_write_vsyn.resolve_safe();
+
+ // allocate timers
+ m_vsync_timer = timer_alloc(TIMER_VSYNC);
+
+ /* register for state saving */
+ machine().save().register_postload(save_prepost_delegate(FUNC(tms9927_device::state_postload), this));
+
+ save_item(NAME(m_reg));
+ save_item(NAME(m_start_datarow));
+ save_item(NAME(m_reset));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void tms9927_device::device_reset()
+{
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void tms9927_device::device_stop()
+{
+ osd_printf_debug("TMS9937: Final params: (%d, %d, %d, %d, %d, %d, %d)\n",
+ m_clock,
+ m_total_hpix,
+ 0, m_visible_hpix,
+ m_total_vpix,
+ 0, m_visible_vpix);
+}
+
+
+
+//-------------------------------------------------
+// device_timer - handle timer events
+//-------------------------------------------------
+
+void tms9927_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_VSYNC:
+ m_vsyn = !m_vsyn;
+
+ m_write_vsyn(m_vsyn);
+
+ if (m_vsyn)
+ {
+ m_vsync_timer->adjust(m_screen->time_until_pos(3));
+ }
+ else
+ {
+ m_vsync_timer->adjust(m_screen->time_until_pos(0));
+ }
+ break;
+ }
+}
+
+void tms9927_device::state_postload()
+{
+ recompute_parameters(TRUE);
+}
+
+
+void tms9927_device::generic_access(address_space &space, offs_t offset)
+{
+ switch (offset)
+ {
+ case 0x07: /* Processor Self Load */
+ case 0x0f: /* Non-processor self-load */
+ if (m_selfload != NULL)
+ {
+ for (int cur = 0; cur < 7; cur++)
+ write(space, cur, m_selfload[cur]);
+ for (int cur = 0; cur < 1; cur++)
+ write(space, cur + 0xc, m_selfload[cur + 7]);
+ }
+ else
+ popmessage("tms9927: self-load initiated with no PROM!");
+
+ /* processor self-load waits with reset enabled;
+ non-processor just goes ahead */
+ m_reset = (offset == 0x07);
+ break;
+
+ case 0x0a: /* Reset */
+ if (!m_reset)
+ {
+ m_screen->update_now();
+ m_reset = TRUE;
+ }
+ break;
+
+ case 0x0b: /* Up scroll */
+osd_printf_debug("Up scroll\n");
+ m_screen->update_now();
+ m_start_datarow = (m_start_datarow + 1) % DATA_ROWS_PER_FRAME;
+ break;
+
+ case 0x0e: /* Start timing chain */
+ if (m_reset)
+ {
+ m_screen->update_now();
+ m_reset = FALSE;
+ recompute_parameters(FALSE);
+ }
+ break;
+ }
+}
+
+
+WRITE8_MEMBER( tms9927_device::write )
+{
+ switch (offset)
+ {
+ case 0x00: /* HORIZONTAL CHARACTER COUNT */
+ case 0x01: /* INTERLACED / HSYNC WIDTH / HSYNC DELAY */
+ case 0x02: /* SCANS PER DATA ROW / CHARACTERS PER DATA ROW */
+ case 0x03: /* SKEW BITS / DATA ROWS PER FRAME */
+ case 0x04: /* SCAN LINES / FRAME */
+ case 0x05: /* VERTICAL DATA START */
+ case 0x06: /* LAST DISPLAYED DATA ROW */
+ m_reg[offset] = data;
+ recompute_parameters(FALSE);
+ break;
+
+ case 0x0c: /* LOAD CURSOR CHARACTER ADDRESS */
+ case 0x0d: /* LOAD CURSOR ROW ADDRESS */
+osd_printf_debug("Cursor address changed\n");
+ m_reg[offset - 0x0c + 7] = data;
+ recompute_parameters(FALSE);
+ break;
+
+ default:
+ generic_access(space, offset);
+ break;
+ }
+}
+
+
+READ8_MEMBER( tms9927_device::read )
+{
+ switch (offset)
+ {
+ case 0x08: /* READ CURSOR CHARACTER ADDRESS */
+ case 0x09: /* READ CURSOR ROW ADDRESS */
+ return m_reg[offset - 0x08 + 7];
+
+ default:
+ generic_access(space, offset);
+ break;
+ }
+ return 0xff;
+}
+
+
+int tms9927_device::screen_reset()
+{
+ return m_reset;
+}
+
+
+int tms9927_device::upscroll_offset()
+{
+ return m_start_datarow;
+}
+
+
+int tms9927_device::cursor_bounds(rectangle &bounds)
+{
+ int cursorx = CURSOR_CHAR_ADDRESS;
+ int cursory = CURSOR_ROW_ADDRESS;
+
+ bounds.min_x = cursorx * m_hpixels_per_column;
+ bounds.max_x = bounds.min_x + m_hpixels_per_column - 1;
+ bounds.min_y = cursory * SCANS_PER_DATA_ROW;
+ bounds.max_y = bounds.min_y + SCANS_PER_DATA_ROW - 1;
+
+ return (cursorx < HCOUNT && cursory <= LAST_DISP_DATA_ROW);
+}
+
+
+void tms9927_device::recompute_parameters(int postload)
+{
+ UINT16 offset_hpix, offset_vpix;
+ attoseconds_t refresh;
+ rectangle visarea;
+
+ if (m_reset)
+ return;
+
+ /* compute the screen sizes */
+ m_total_hpix = HCOUNT * m_hpixels_per_column;
+ m_total_vpix = SCAN_LINES_PER_FRAME;
+
+ /* determine the visible area, avoid division by 0 */
+ m_visible_hpix = CHARS_PER_DATA_ROW * m_hpixels_per_column;
+ m_visible_vpix = (LAST_DISP_DATA_ROW + 1) * SCANS_PER_DATA_ROW;
+
+ /* determine the horizontal/vertical offsets */
+ offset_hpix = HSYNC_DELAY * m_hpixels_per_column;
+ offset_vpix = VERTICAL_DATA_START;
+
+ osd_printf_debug("TMS9937: Total = %dx%d, Visible = %dx%d, Offset=%dx%d, Skew=%d\n", m_total_hpix, m_total_vpix, m_visible_hpix, m_visible_vpix, offset_hpix, offset_vpix, SKEW_BITS);
+
+ /* see if it all makes sense */
+ m_valid_config = TRUE;
+ if (m_visible_hpix > m_total_hpix || m_visible_vpix > m_total_vpix)
+ {
+ m_valid_config = FALSE;
+ logerror("tms9927: invalid visible size (%dx%d) versus total size (%dx%d)\n", m_visible_hpix, m_visible_vpix, m_total_hpix, m_total_vpix);
+ }
+
+ /* update */
+ if (!m_valid_config)
+ return;
+
+ /* create a visible area */
+ /* fix me: how do the offsets fit in here? */
+ visarea.set(0, m_visible_hpix - 1, 0, m_visible_vpix - 1);
+
+ refresh = HZ_TO_ATTOSECONDS(m_clock) * m_total_hpix * m_total_vpix;
+
+ m_screen->configure(m_total_hpix, m_total_vpix, visarea, refresh);
+
+ m_vsyn = 0;
+ m_vsync_timer->adjust(m_screen->time_until_pos(0, 0));
+}
diff --git a/src/devices/video/tms9927.h b/src/devices/video/tms9927.h
new file mode 100644
index 00000000000..adadf8945cb
--- /dev/null
+++ b/src/devices/video/tms9927.h
@@ -0,0 +1,110 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/**********************************************************************
+
+ TI TMS9927 and compatible CRT controller emulation
+
+**********************************************************************/
+
+#ifndef __TMS9927__
+#define __TMS9927__
+
+
+#define MCFG_TMS9927_VSYN_CALLBACK(_write) \
+ devcb = &tms9927_device::set_vsyn_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_TMS9927_CHAR_WIDTH(_pixels) \
+ tms9927_device::set_char_width(*device, _pixels);
+
+#define MCFG_TMS9927_REGION(_tag) \
+ tms9927_device::set_region_tag(*device, _tag);
+
+
+class tms9927_device : public device_t,
+ public device_video_interface
+{
+public:
+ tms9927_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ tms9927_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~tms9927_device() {}
+
+ template<class _Object> static devcb_base &set_vsyn_wr_callback(device_t &device, _Object object) { return downcast<tms9927_device &>(device).m_write_vsyn.set_callback(object); }
+
+ static void set_char_width(device_t &device, int pixels) { downcast<tms9927_device &>(device).m_hpixels_per_column = pixels; }
+ static void set_region_tag(device_t &device, const char *tag) { downcast<tms9927_device &>(device).m_selfload_region = tag; }
+
+ DECLARE_WRITE8_MEMBER(write);
+ DECLARE_READ8_MEMBER(read);
+
+ int screen_reset();
+ int upscroll_offset();
+ int cursor_bounds(rectangle &bounds);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ enum
+ {
+ TIMER_VSYNC
+ };
+
+ void state_postload();
+ void recompute_parameters(int postload);
+ void generic_access(address_space &space, offs_t offset);
+
+ devcb_write_line m_write_vsyn;
+ int m_hpixels_per_column; /* number of pixels per video memory address */
+ const char *m_selfload_region; /* name of the region with self-load data */
+
+ // internal state
+ const UINT8 *m_selfload;
+
+ /* live state */
+ UINT32 m_clock;
+ UINT8 m_reg[9];
+ UINT8 m_start_datarow;
+ UINT8 m_reset;
+
+ /* derived state; no need to save */
+ UINT8 m_valid_config;
+ UINT16 m_total_hpix, m_total_vpix;
+ UINT16 m_visible_hpix, m_visible_vpix;
+
+ int m_vsyn;
+
+ emu_timer *m_vsync_timer;
+};
+
+extern const device_type TMS9927;
+
+class crt5027_device : public tms9927_device
+{
+public:
+ crt5027_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type CRT5027;
+
+class crt5037_device : public tms9927_device
+{
+public:
+ crt5037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type CRT5037;
+
+class crt5057_device : public tms9927_device
+{
+public:
+ crt5057_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type CRT5057;
+
+
+#endif
diff --git a/src/devices/video/tms9928a.c b/src/devices/video/tms9928a.c
new file mode 100644
index 00000000000..8e8b672f8e4
--- /dev/null
+++ b/src/devices/video/tms9928a.c
@@ -0,0 +1,731 @@
+// license:BSD-3-Clause
+// copyright-holders:Sean Young, Nathan Woods, Aaron Giles, Wilbert Pol, hap
+/*
+** File: tms9928a.c -- software implementation of the Texas Instruments
+** TMS9918(A), TMS9928(A) and TMS9929(A), used by the Coleco, MSX and
+** TI99/4(A).
+**
+** All undocumented features as described in the following file
+** should be emulated.
+**
+** http://bifi.msxnet.org/msxnet/tech/tms9918a.txt
+**
+** By Sean Young 1999 (sean@msxnet.org).
+** Based on code by Mike Balfour.
+** Improved over the years by MESS and MAME teams.
+**
+** Todo:
+** - External VDP input and sync (pin 34/35 on 9918A)
+** - Updates during mid-scanline, probably only used in some MSX1 demos
+** - Colours are incorrect. [fixed by R Nabet ?]
+** - Sprites 8-31 are ghosted/cloned in mode 3 when using less than
+** three pattern tables. Exact behaviour is not known.
+** - Address scrambling when setting TMS99xxA to 4K (not on TMS91xx)
+*/
+
+#include "emu.h"
+#include "tms9928a.h"
+
+
+const device_type TMS9928A = &device_creator<tms9928a_device>;
+const device_type TMS9918 = &device_creator<tms9918_device>;
+const device_type TMS9918A = &device_creator<tms9918a_device>;
+const device_type TMS9118 = &device_creator<tms9118_device>;
+const device_type TMS9128 = &device_creator<tms9128_device>;
+const device_type TMS9929 = &device_creator<tms9929_device>;
+const device_type TMS9929A = &device_creator<tms9929a_device>;
+const device_type TMS9129 = &device_creator<tms9129_device>;
+
+// ======= Debugging =========
+
+// Log register accesses
+#define TRACE_REG 0
+
+// Log mode settings
+#define TRACE_MODE 0
+
+// ===========================
+
+/*
+ The TMS9928 has an own address space.
+*/
+static ADDRESS_MAP_START(memmap, AS_DATA, 8, tms9928a_device)
+ ADDRESS_MAP_GLOBAL_MASK(0x3fff)
+ AM_RANGE(0x0000, 0x3fff) AM_RAM
+ADDRESS_MAP_END
+
+tms9928a_device::tms9928a_device( const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, bool is_50hz, bool is_reva, bool is_99, const char *shortname, const char *source)
+ : device_t( mconfig, type, name, tag, owner, clock, shortname, source),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_out_int_line_cb(*this),
+ m_space_config("vram",ENDIANNESS_BIG, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(memmap))
+{
+ m_50hz = is_50hz;
+ m_reva = is_reva;
+ m_99 = is_99;
+// static_set_addrmap(*this, AS_DATA, ADDRESS_MAP_NAME(memmap));
+}
+
+
+tms9928a_device::tms9928a_device( const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock )
+ : device_t( mconfig, TMS9928A, "TMS9928A VDP", tag, owner, clock, "tms9928a", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_vram_size(0),
+ m_out_int_line_cb(*this),
+ m_space_config("vram",ENDIANNESS_BIG, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(memmap))
+{
+ m_50hz = false;
+ m_reva = true;
+ m_99 = true;
+// static_set_addrmap(*this, AS_DATA, ADDRESS_MAP_NAME(memmap));
+}
+
+tms9129_device::tms9129_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9928a_device( mconfig, TMS9129, "TMS9129", tag, owner, clock, true, true, false, "tms9129", __FILE__)
+{ }
+
+tms9918_device::tms9918_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9928a_device( mconfig, TMS9918, "TMS9918", tag, owner, clock, false, false, true, "tms9918", __FILE__)
+{ }
+
+tms9918a_device::tms9918a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9928a_device( mconfig, TMS9918A, "TMS9918A", tag, owner, clock, false, true, true, "tms9918a", __FILE__)
+{ }
+
+tms9118_device::tms9118_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9928a_device( mconfig, TMS9118, "TMS9118 VDP", tag, owner, clock, false, true, false, "tms9118", __FILE__)
+{ }
+
+tms9128_device::tms9128_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9928a_device( mconfig, TMS9128, "TMS9128 VDP", tag, owner, clock, false, true, false, "tms9128", __FILE__)
+{ }
+
+tms9929_device::tms9929_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9928a_device( mconfig, TMS9929, "TMS9929", tag, owner, clock, true, false, true, "tms9929", __FILE__)
+{ }
+
+tms9929a_device::tms9929a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms9928a_device( mconfig, TMS9929A, "TMS9929A", tag, owner, clock, true, true, true, "tms9929a", __FILE__)
+{ }
+
+
+READ8_MEMBER( tms9928a_device::vram_read )
+{
+ // prevent debugger from changing the address base
+ if (space.debugger_access()) return 0;
+
+ UINT8 data = m_ReadAhead;
+
+ m_ReadAhead = m_vram_space->read_byte(m_Addr);
+ m_Addr = (m_Addr + 1) & (m_vram_size - 1);
+ m_latch = 0;
+
+ return data;
+}
+
+
+WRITE8_MEMBER( tms9928a_device::vram_write )
+{
+ // prevent debugger from changing the address base
+ if (space.debugger_access()) return;
+
+ m_vram_space->write_byte(m_Addr, data);
+ m_Addr = (m_Addr + 1) & (m_vram_size - 1);
+ m_ReadAhead = data;
+ m_latch = 0;
+}
+
+
+READ8_MEMBER( tms9928a_device::register_read )
+{
+ // prevent debugger from changing the internal state
+ if (space.debugger_access()) return 0;
+
+ UINT8 data = m_StatusReg;
+
+ m_StatusReg = m_FifthSprite;
+ check_interrupt();
+ m_latch = 0;
+
+ return data;
+}
+
+
+void tms9928a_device::check_interrupt()
+{
+ // trigger if vblank and interrupt-enable bits are set
+ UINT8 b = (m_StatusReg & 0x80 && m_Regs[1] & 0x20) ? 1 : 0;
+
+ if (b != m_INT)
+ {
+ m_INT = b;
+ if ( !m_out_int_line_cb.isnull() )
+ m_out_int_line_cb( m_INT );
+ }
+}
+
+
+void tms9928a_device::update_backdrop()
+{
+ // update backdrop colour to transparent if EXTVID bit is set
+ if ((m_Regs[7] & 15) == 0)
+ m_palette[0] = rgb_t(m_Regs[0] & 1 ? 0 : 255,0,0,0);
+}
+
+
+void tms9928a_device::update_table_masks()
+{
+ m_colourmask = ( (m_Regs[3] & 0x7f) << 3 ) | 7;
+
+ // on 91xx family, the colour table mask doesn't affect the pattern table mask
+ m_patternmask = ( (m_Regs[4] & 3) << 8 ) | ( m_99 ? (m_colourmask & 0xff) : 0xff );
+}
+
+
+void tms9928a_device::change_register(UINT8 reg, UINT8 val)
+{
+ static const UINT8 Mask[8] =
+ { 0x03, 0xfb, 0x0f, 0xff, 0x07, 0x7f, 0x07, 0xff };
+ static const char *const modes[] =
+ {
+ "Mode 0 (GRAPHIC 1)", "Mode 1 (TEXT 1)", "Mode 2 (GRAPHIC 2)",
+ "Mode 1+2 (TEXT 1 variation)", "Mode 3 (MULTICOLOR)",
+ "Mode 1+3 (BOGUS)", "Mode 2+3 (MULTICOLOR variation)",
+ "Mode 1+2+3 (BOGUS)"
+ };
+
+ UINT8 prev = m_Regs[reg];
+ val &= Mask[reg];
+ m_Regs[reg] = val;
+
+ if (TRACE_REG) logerror("TMS9928A('%s'): Reg %d = %02xh\n", tag(), reg, (int)val);
+
+ switch (reg)
+ {
+ case 0:
+ /* re-calculate masks and pattern generator & colour */
+ if (val & 2)
+ {
+ m_colour = ((m_Regs[3] & 0x80) * 64) & (m_vram_size - 1);
+ m_pattern = ((m_Regs[4] & 4) * 2048) & (m_vram_size - 1);
+ update_table_masks();
+ }
+ else
+ {
+ m_colour = (m_Regs[3] * 64) & (m_vram_size - 1);
+ m_pattern = (m_Regs[4] * 2048) & (m_vram_size - 1);
+ }
+ m_mode = ( (m_reva ? (m_Regs[0] & 2) : 0) | ((m_Regs[1] & 0x10)>>4) | ((m_Regs[1] & 8)>>1));
+ if ((val ^ prev) & 1)
+ update_backdrop();
+ if (TRACE_MODE) logerror("TMS9928A('%s'): %s\n", tag(), modes[m_mode]);
+ break;
+ case 1:
+ check_interrupt();
+ m_mode = ( (m_reva ? (m_Regs[0] & 2) : 0) | ((m_Regs[1] & 0x10)>>4) | ((m_Regs[1] & 8)>>1));
+ if (TRACE_MODE) logerror("TMS9928A('%s'): %s\n", tag(), modes[m_mode]);
+ break;
+ case 2:
+ m_nametbl = (val * 1024) & (m_vram_size - 1);
+ break;
+ case 3:
+ if (m_Regs[0] & 2)
+ {
+ m_colour = ((val & 0x80) * 64) & (m_vram_size - 1);
+ update_table_masks();
+ }
+ else
+ {
+ m_colour = (val * 64) & (m_vram_size - 1);
+ }
+ break;
+ case 4:
+ if (m_Regs[0] & 2)
+ {
+ m_pattern = ((val & 4) * 2048) & (m_vram_size - 1);
+ update_table_masks();
+ }
+ else
+ {
+ m_pattern = (val * 2048) & (m_vram_size - 1);
+ }
+ break;
+ case 5:
+ m_spriteattribute = (val * 128) & (m_vram_size - 1);
+ break;
+ case 6:
+ m_spritepattern = (val * 2048) & (m_vram_size - 1);
+ break;
+ case 7:
+ if ((val ^ prev) & 15)
+ update_backdrop();
+ break;
+ }
+}
+
+
+WRITE8_MEMBER( tms9928a_device::register_write )
+{
+ // prevent debugger from changing the internal state
+ if (space.debugger_access()) return;
+
+ if (m_latch)
+ {
+ /* set high part of read/write address */
+ m_Addr = ((data << 8) | (m_Addr & 0xff)) & (m_vram_size - 1);
+
+ if (data & 0x80)
+ {
+ /* register write */
+ change_register (data & 7, m_Addr & 0xff);
+ }
+ else
+ {
+ if ( !(data & 0x40) )
+ {
+ /* read ahead */
+ vram_read(space, 0);
+ }
+ }
+ m_latch = 0;
+ }
+ else
+ {
+ /* set low part of read/write address */
+ m_Addr = ((m_Addr & 0xff00) | data) & (m_vram_size - 1);
+ m_latch = 1;
+ }
+}
+
+
+void tms9928a_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int raw_vpos = m_screen->vpos();
+ int vpos = raw_vpos * m_vertical_size / m_screen->height();
+ UINT16 BackColour = m_Regs[7] & 15;
+ UINT32 *p = &m_tmpbmp.pix32(vpos);
+
+ int y = vpos - m_top_border;
+
+ if ( y < 0 || y >= 192 || ! (m_Regs[1] & 0x40) )
+ {
+ /* Draw backdrop colour */
+ for ( int i = 0; i < TMS9928A_TOTAL_HORZ; i++ )
+ p[i] = m_palette[BackColour];
+
+ /* vblank is set at the last cycle of the first inactive line */
+ if ( y == 193 )
+ {
+ m_StatusReg |= 0x80;
+ check_interrupt();
+ }
+ }
+ else
+ {
+ /* Draw regular line */
+
+ /* Left border */
+ for ( int i = 0; i < TMS9928A_HORZ_DISPLAY_START; i++ )
+ p[i] = m_palette[BackColour];
+
+ /* Active display */
+
+ switch( m_mode )
+ {
+ case 0: /* MODE 0 */
+ // if (vpos==100 ) popmessage("TMS9928A MODE 0");
+ {
+ UINT16 addr = m_nametbl + ( ( y & 0xF8 ) << 2 );
+
+ for ( int x = TMS9928A_HORZ_DISPLAY_START; x < TMS9928A_HORZ_DISPLAY_START + 256; x+= 8, addr++ )
+ {
+ UINT8 charcode = m_vram_space->read_byte( addr );
+ UINT8 pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) );
+ UINT8 colour = m_vram_space->read_byte( m_colour + ( charcode >> 3 ) );
+ rgb_t fg = m_palette[(colour >> 4) ? (colour >> 4) : BackColour];
+ rgb_t bg = m_palette[(colour & 15) ? (colour & 15) : BackColour];
+
+ for ( int i = 0; i < 8; pattern <<= 1, i++ )
+ p[x+i] = ( pattern & 0x80 ) ? fg : bg;
+ }
+ }
+ break;
+
+ case 1: /* MODE 1 */
+ //if (vpos==100 ) popmessage("TMS9928A MODE 1");
+ {
+ UINT16 addr = m_nametbl + ( ( y >> 3 ) * 40 );
+ rgb_t fg = m_palette[(m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour];
+ rgb_t bg = m_palette[BackColour];
+
+ /* Extra 6 pixels left border */
+ for ( int x = TMS9928A_HORZ_DISPLAY_START; x < TMS9928A_HORZ_DISPLAY_START + 6; x++ )
+ p[x] = bg;
+
+ for ( int x = TMS9928A_HORZ_DISPLAY_START + 6; x < TMS9928A_HORZ_DISPLAY_START + 246; x+= 6, addr++ )
+ {
+ UINT16 charcode = m_vram_space->read_byte( addr );
+ UINT8 pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) );
+
+ for ( int i = 0; i < 6; pattern <<= 1, i++ )
+ p[x+i] = ( pattern & 0x80 ) ? fg : bg;
+ }
+
+ /* Extra 10 pixels right border */
+ for ( int x = TMS9928A_HORZ_DISPLAY_START + 246; x < TMS9928A_HORZ_DISPLAY_START + 256; x++ )
+ p[x] = bg;
+ }
+ break;
+
+ case 2: /* MODE 2 */
+ //if (vpos==100 ) popmessage("TMS9928A MODE 2");
+ {
+ UINT16 addr = m_nametbl + ( ( y >> 3 ) * 32 );
+
+ for ( int x = TMS9928A_HORZ_DISPLAY_START; x < TMS9928A_HORZ_DISPLAY_START + 256; x+= 8, addr++ )
+ {
+ UINT16 charcode = m_vram_space->read_byte( addr ) + ( ( y >> 6 ) << 8 );
+ UINT8 pattern = m_vram_space->read_byte( m_pattern + ( ( charcode & m_patternmask ) << 3 ) + ( y & 7 ) );
+ UINT8 colour = m_vram_space->read_byte( m_colour + ( ( charcode & m_colourmask ) << 3 ) + ( y & 7 ) );
+ rgb_t fg = m_palette[(colour >> 4) ? (colour >> 4) : BackColour];
+ rgb_t bg = m_palette[(colour & 15) ? (colour & 15) : BackColour];
+
+ for ( int i = 0; i < 8; pattern <<= 1, i++ )
+ p[x+i] = ( pattern & 0x80 ) ? fg : bg;
+ }
+ }
+ break;
+
+ case 3: /* MODE 1+2 */
+ //if (vpos==100) popmessage("TMS9928A MODE1+2");
+ {
+ UINT16 addr = m_nametbl + ( ( y >> 3 ) * 40 );
+ rgb_t fg = m_palette[(m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour];
+ rgb_t bg = m_palette[BackColour];
+
+ /* Extra 6 pixels left border */
+ for ( int x = TMS9928A_HORZ_DISPLAY_START; x < TMS9928A_HORZ_DISPLAY_START + 6; x++ )
+ p[x] = bg;
+
+ for ( int x = TMS9928A_HORZ_DISPLAY_START + 6; x < TMS9928A_HORZ_DISPLAY_START + 246; x+= 6, addr++ )
+ {
+ UINT16 charcode = ( m_vram_space->read_byte( addr ) + ( ( y >> 6 ) << 8 ) ) & m_patternmask;
+ UINT8 pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) );
+
+ for ( int i = 0; i < 6; pattern <<= 1, i++ )
+ p[x+i] = ( pattern & 0x80 ) ? fg : bg;
+ }
+
+ /* Extra 10 pixels right border */
+ for ( int x = TMS9928A_HORZ_DISPLAY_START + 246; x < TMS9928A_HORZ_DISPLAY_START + 256; x++ )
+ p[x] = bg;
+ }
+ break;
+
+ case 4: /* MODE 3 */
+ //if (vpos==100 ) popmessage("TMS9928A MODE 3");
+ {
+ UINT16 addr = m_nametbl + ( ( y >> 3 ) * 32 );
+
+ for ( int x = TMS9928A_HORZ_DISPLAY_START; x < TMS9928A_HORZ_DISPLAY_START + 256; x+= 8, addr++ )
+ {
+ UINT8 charcode = m_vram_space->read_byte( addr );
+ UINT8 colour = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( ( y >> 2 ) & 7 ) );
+ rgb_t fg = m_palette[(colour >> 4) ? (colour >> 4) : BackColour];
+ rgb_t bg = m_palette[(colour & 15) ? (colour & 15) : BackColour];
+
+ p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg;
+ p[x+4] = p[x+5] = p[x+6] = p[x+7] = bg;
+ }
+ }
+ break;
+
+ case 5: case 7: /* MODE bogus */
+ //if (vpos==100 ) popmessage("TMS9928A MODE bogus");
+ {
+ rgb_t fg = m_palette[(m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour];
+ rgb_t bg = m_palette[BackColour];
+
+ /* Extra 6 pixels left border */
+ for ( int x = TMS9928A_HORZ_DISPLAY_START; x < TMS9928A_HORZ_DISPLAY_START + 6; x++ )
+ p[x] = bg;
+
+ for ( int x = TMS9928A_HORZ_DISPLAY_START + 6; x < TMS9928A_HORZ_DISPLAY_START + 246; x+= 6 )
+ {
+ p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg;
+ p[x+4] = p[x+5] = bg;
+ }
+
+ /* Extra 10 pixels right border */
+ for ( int x = TMS9928A_HORZ_DISPLAY_START + 246; x < TMS9928A_HORZ_DISPLAY_START + 256; x++ )
+ p[x] = bg;
+ }
+ break;
+
+ case 6: /* MODE 2+3 */
+ //if (vpos==100 ) popmessage("TMS9928A MODE 2+3");
+ {
+ UINT16 addr = m_nametbl + ( ( y >> 3 ) * 32 );
+
+ for ( int x = TMS9928A_HORZ_DISPLAY_START; x < TMS9928A_HORZ_DISPLAY_START + 256; x+= 8, addr++ )
+ {
+ UINT8 charcode = m_vram_space->read_byte( addr );
+ UINT8 colour = m_vram_space->read_byte( m_pattern + ( ( ( charcode + ( ( y >> 2 ) & 7 ) + ( ( y >> 6 ) << 8 ) ) & m_patternmask ) << 3 ) );
+ rgb_t fg = m_palette[(colour >> 4) ? (colour >> 4) : BackColour];
+ rgb_t bg = m_palette[(colour & 15) ? (colour & 15) : BackColour];
+
+ p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg;
+ p[x+4] = p[x+5] = p[x+6] = p[x+7] = bg;
+ }
+ }
+ break;
+ }
+
+ /* Draw sprites */
+ if ( ( m_Regs[1] & 0x50 ) != 0x40 )
+ {
+ /* sprites are disabled */
+ m_FifthSprite = 31;
+ }
+ else
+ {
+ UINT8 sprite_size = ( m_Regs[1] & 0x02 ) ? 16 : 8;
+ UINT8 sprite_mag = m_Regs[1] & 0x01;
+ UINT8 sprite_height = sprite_size * ( sprite_mag + 1 );
+ UINT8 spr_drawn[32+256+32] = { 0 };
+ UINT8 num_sprites = 0;
+ bool fifth_encountered = false;
+
+ for ( UINT16 sprattr = 0; sprattr < 128; sprattr += 4 )
+ {
+ int spr_y = m_vram_space->read_byte( m_spriteattribute + sprattr + 0 );
+
+ m_FifthSprite = sprattr / 4;
+
+ /* Stop processing sprites */
+ if ( spr_y == 208 )
+ break;
+
+ if ( spr_y > 0xE0 )
+ spr_y -= 256;
+
+ /* vert pos 255 is displayed on the first line of the screen */
+ spr_y++;
+
+ /* is sprite enabled on this line? */
+ if ( spr_y <= y && y < spr_y + sprite_height )
+ {
+ int spr_x = m_vram_space->read_byte( m_spriteattribute + sprattr + 1 );
+ UINT8 sprcode = m_vram_space->read_byte( m_spriteattribute + sprattr + 2 );
+ UINT8 sprcol = m_vram_space->read_byte( m_spriteattribute + sprattr + 3 );
+ UINT16 pataddr = m_spritepattern + ( ( sprite_size == 16 ) ? sprcode & ~0x03 : sprcode ) * 8;
+
+ num_sprites++;
+
+ /* Fifth sprite encountered? */
+ if ( num_sprites == 5 )
+ {
+ fifth_encountered = true;
+ break;
+ }
+
+ if ( sprite_mag )
+ pataddr += ( ( ( y - spr_y ) & 0x1F ) >> 1 );
+ else
+ pataddr += ( ( y - spr_y ) & 0x0F );
+
+ UINT8 pattern = m_vram_space->read_byte( pataddr );
+
+ if ( sprcol & 0x80 )
+ spr_x -= 32;
+
+ sprcol &= 0x0f;
+
+ for ( int s = 0; s < sprite_size; s += 8 )
+ {
+ for ( int i = 0; i < 8; pattern <<= 1, i++ )
+ {
+ int colission_index = spr_x + ( sprite_mag ? i * 2 : i ) + 32;
+
+ for ( int z = 0; z <= sprite_mag; colission_index++, z++ )
+ {
+ /* Check if pixel should be drawn */
+ if ( pattern & 0x80 )
+ {
+ if ( colission_index >= 32 && colission_index < 32 + 256 )
+ {
+ /* Check for colission */
+ if ( spr_drawn[ colission_index ] )
+ m_StatusReg |= 0x20;
+ spr_drawn[ colission_index ] |= 0x01;
+
+ if ( sprcol )
+ {
+ /* Has another sprite already drawn here? */
+ if ( ! ( spr_drawn[ colission_index ] & 0x02 ) )
+ {
+ spr_drawn[ colission_index ] |= 0x02;
+ p[ TMS9928A_HORZ_DISPLAY_START + colission_index - 32 ] = m_palette[sprcol];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ pattern = m_vram_space->read_byte( pataddr + 16 );
+ spr_x += sprite_mag ? 16 : 8;
+ }
+ }
+ }
+
+ /* Update sprite overflow bits */
+ if (~m_StatusReg & 0x40)
+ {
+ m_StatusReg = (m_StatusReg & 0xe0) | m_FifthSprite;
+ if (fifth_encountered && ~m_StatusReg & 0x80)
+ m_StatusReg |= 0x40;
+ }
+ }
+
+ /* Right border */
+ for ( int i = TMS9928A_HORZ_DISPLAY_START + 256; i < TMS9928A_TOTAL_HORZ; i++ )
+ p[i] = m_palette[BackColour];
+ }
+
+ /* Schedule next callback */
+ m_line_timer->adjust( m_screen->time_until_pos( ( raw_vpos + 1 ) % m_screen->height() , TMS9928A_HORZ_DISPLAY_START ) );
+}
+
+
+UINT32 tms9928a_device::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
+{
+ copybitmap( bitmap, m_tmpbmp, 0, 0, 0, 0, cliprect );
+ return 0;
+}
+
+void tms9928a_device::set_palette()
+{
+ /*
+ New palette (R. Nabet).
+
+ First 3 columns from TI datasheet (in volts).
+ Next 3 columns based on formula :
+ Y = .299*R + .587*G + .114*B (NTSC)
+ (the coefficients are likely to be slightly different with PAL, but who cares ?)
+ I assumed the "zero" for R-Y and B-Y was 0.47V.
+ Last 3 coeffs are the 8-bit values.
+
+ Color Y R-Y B-Y R G B R G B
+ 0 Transparent
+ 1 Black 0.00 0.47 0.47 0.00 0.00 0.00 0 0 0
+ 2 Medium green 0.53 0.07 0.20 0.13 0.79 0.26 33 200 66
+ 3 Light green 0.67 0.17 0.27 0.37 0.86 0.47 94 220 120
+ 4 Dark blue 0.40 0.40 1.00 0.33 0.33 0.93 84 85 237
+ 5 Light blue 0.53 0.43 0.93 0.49 0.46 0.99 125 118 252
+ 6 Dark red 0.47 0.83 0.30 0.83 0.32 0.30 212 82 77
+ 7 Cyan 0.73 0.00 0.70 0.26 0.92 0.96 66 235 245
+ 8 Medium red 0.53 0.93 0.27 0.99 0.33 0.33 252 85 84
+ 9 Light red 0.67 0.93 0.27 1.13(!) 0.47 0.47 255 121 120
+ A Dark yellow 0.73 0.57 0.07 0.83 0.76 0.33 212 193 84
+ B Light yellow 0.80 0.57 0.17 0.90 0.81 0.50 230 206 128
+ C Dark green 0.47 0.13 0.23 0.13 0.69 0.23 33 176 59
+ D Magenta 0.53 0.73 0.67 0.79 0.36 0.73 201 91 186
+ E Gray 0.80 0.47 0.47 0.80 0.80 0.80 204 204 204
+ F White 1.00 0.47 0.47 1.00 1.00 1.00 255 255 255
+ */
+ static const rgb_t tms9928a_palette[TMS9928A_PALETTE_SIZE] =
+ {
+ rgb_t::black,
+ rgb_t::black,
+ rgb_t(33, 200, 66),
+ rgb_t(94, 220, 120),
+ rgb_t(84, 85, 237),
+ rgb_t(125, 118, 252),
+ rgb_t(212, 82, 77),
+ rgb_t(66, 235, 245),
+ rgb_t(252, 85, 84),
+ rgb_t(255, 121, 120),
+ rgb_t(212, 193, 84),
+ rgb_t(230, 206, 128),
+ rgb_t(33, 176, 59),
+ rgb_t(201, 91, 186),
+ rgb_t(204, 204, 204),
+ rgb_t::white
+ };
+
+ /* copy default palette into working palette */
+ for (int i = 0; i < TMS9928A_PALETTE_SIZE; i++)
+ {
+ m_palette[i] = tms9928a_palette[i];
+ }
+}
+
+void tms9928a_device::device_start()
+{
+ m_top_border = m_50hz ? TMS9928A_VERT_DISPLAY_START_PAL : TMS9928A_VERT_DISPLAY_START_NTSC;
+ m_vertical_size = m_50hz ? TMS9928A_TOTAL_VERT_PAL : TMS9928A_TOTAL_VERT_NTSC;
+
+ m_out_int_line_cb.resolve();
+
+ // Video RAM is allocated as an own address space
+ m_vram_space = &space(AS_DATA);
+
+ /* back bitmap */
+ m_tmpbmp.allocate(TMS9928A_TOTAL_HORZ, TMS9928A_TOTAL_VERT_PAL);
+
+ m_line_timer = timer_alloc(TIMER_LINE);
+
+ set_palette();
+
+ save_item(NAME(m_Regs[0]));
+ save_item(NAME(m_Regs[1]));
+ save_item(NAME(m_Regs[2]));
+ save_item(NAME(m_Regs[3]));
+ save_item(NAME(m_Regs[4]));
+ save_item(NAME(m_Regs[5]));
+ save_item(NAME(m_Regs[6]));
+ save_item(NAME(m_Regs[7]));
+ save_item(NAME(m_StatusReg));
+ save_item(NAME(m_FifthSprite));
+ save_item(NAME(m_ReadAhead));
+ save_item(NAME(m_latch));
+ save_item(NAME(m_Addr));
+ save_item(NAME(m_INT));
+// save_pointer(NAME(m_vMem), m_vram_size);
+ save_item(NAME(m_colour));
+ save_item(NAME(m_colourmask));
+ save_item(NAME(m_pattern));
+ save_item(NAME(m_patternmask));
+ save_item(NAME(m_nametbl));
+ save_item(NAME(m_spriteattribute));
+ save_item(NAME(m_spritepattern));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_palette));
+}
+
+
+void tms9928a_device::device_reset()
+{
+ for ( int i = 0; i < 8; i++ )
+ m_Regs[i] = 0;
+
+ m_StatusReg = 0;
+ m_FifthSprite = 31;
+ m_nametbl = 0;
+ m_pattern = 0;
+ m_colour = 0;
+ m_spritepattern = 0;
+ m_spriteattribute = 0;
+ m_colourmask = 0x3fff;
+ m_patternmask = 0x3fff;
+ m_Addr = 0;
+ m_ReadAhead = 0;
+ m_INT = 0;
+ m_latch = 0;
+ m_mode = 0;
+
+ m_line_timer->adjust( m_screen->time_until_pos( 0, TMS9928A_HORZ_DISPLAY_START ) );
+}
diff --git a/src/devices/video/tms9928a.h b/src/devices/video/tms9928a.h
new file mode 100644
index 00000000000..b87807782d1
--- /dev/null
+++ b/src/devices/video/tms9928a.h
@@ -0,0 +1,207 @@
+// license:BSD-3-Clause
+// copyright-holders:Sean Young, Nathan Woods, Aaron Giles, Wilbert Pol, hap
+/*
+** File: tms9928a.h -- software implementation of the TMS9928A VDP.
+**
+** By Sean Young 1999 (sean@msxnet.org).
+*/
+
+/*
+
+ Model Video Hz
+
+ TMS9918 NTSC 60
+ TMS9929? YPbPr? 50 (not sure. 50Hz non-A model, used in Creativision? or was it a 3rd party clone chip?)
+
+ TMS9918A NTSC 60
+ TMS9928A YPbPr 60
+ TMS9929A YPbPr 50
+
+ TMS9118 NTSC 60
+ TMS9128 YPbPr 60
+ TMS9129 YPbPr 50
+
+*/
+
+#ifndef __TMS9928A_H__
+#define __TMS9928A_H__
+
+#include "emu.h"
+
+
+#define TMS9928A_PALETTE_SIZE 16
+
+
+/* Some defines used in defining the screens */
+#define TMS9928A_TOTAL_HORZ 342
+#define TMS9928A_TOTAL_VERT_NTSC 262
+#define TMS9928A_TOTAL_VERT_PAL 313
+
+#define TMS9928A_HORZ_DISPLAY_START (2 + 14 + 8 + 13)
+#define TMS9928A_VERT_DISPLAY_START_PAL (13 + 51)
+#define TMS9928A_VERT_DISPLAY_START_NTSC (13 + 27)
+
+// MCFG_DEVICE_ADD(_tag, _variant, XTAL_10_738635MHz / 2 )
+
+#define MCFG_TMS9928A_VRAM_SIZE(_size) \
+ tms9928a_device::set_vram_size(*device, _size);
+
+#define MCFG_TMS9928A_OUT_INT_LINE_CB(_devcb) \
+ devcb = &tms9928a_device::set_out_int_line_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_TMS9928A_SET_SCREEN MCFG_VIDEO_SET_SCREEN
+
+
+#define MCFG_TMS9928A_SCREEN_ADD_NTSC(_screen_tag) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_SCREEN_ADD( _screen_tag, RASTER ) \
+ MCFG_SCREEN_RAW_PARAMS( XTAL_10_738635MHz / 2, TMS9928A_TOTAL_HORZ, TMS9928A_HORZ_DISPLAY_START-12, TMS9928A_HORZ_DISPLAY_START + 256 + 12, \
+ TMS9928A_TOTAL_VERT_NTSC, TMS9928A_VERT_DISPLAY_START_NTSC - 12, TMS9928A_VERT_DISPLAY_START_NTSC + 192 + 12 )
+
+
+#define MCFG_TMS9928A_SCREEN_ADD_PAL(_screen_tag) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_SCREEN_ADD(_screen_tag, RASTER ) \
+ MCFG_SCREEN_RAW_PARAMS( XTAL_10_738635MHz / 2, TMS9928A_TOTAL_HORZ, TMS9928A_HORZ_DISPLAY_START-12, TMS9928A_HORZ_DISPLAY_START + 256 + 12, \
+ TMS9928A_TOTAL_VERT_PAL, TMS9928A_VERT_DISPLAY_START_PAL - 12, TMS9928A_VERT_DISPLAY_START_PAL + 192 + 12 )
+
+
+extern const device_type TMS9918;
+extern const device_type TMS9918A;
+extern const device_type TMS9118;
+extern const device_type TMS9928A;
+extern const device_type TMS9128;
+extern const device_type TMS9929;
+extern const device_type TMS9929A;
+extern const device_type TMS9129;
+
+
+class tms9928a_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ tms9928a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ tms9928a_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, bool is_50hz, bool is_reva, bool is_99, const char *shortname, const char *source);
+
+ static void set_vram_size(device_t &device, int vram_size) { downcast<tms9928a_device &>(device).m_vram_size = vram_size; }
+ template<class _Object> static devcb_base &set_out_int_line_callback(device_t &device, _Object object) { return downcast<tms9928a_device &>(device).m_out_int_line_cb.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( vram_read );
+ DECLARE_WRITE8_MEMBER( vram_write );
+ DECLARE_READ8_MEMBER( register_read );
+ DECLARE_WRITE8_MEMBER( register_write );
+
+ /* update the screen */
+ UINT32 screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect );
+ bitmap_rgb32 &get_bitmap() { return m_tmpbmp; }
+
+ /* RESET pin */
+ void reset_line(int state) { if (state==ASSERT_LINE) device_reset(); }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_DATA) const { return (spacenum == AS_DATA) ? &m_space_config : NULL; }
+
+private:
+ void change_register(UINT8 reg, UINT8 val);
+ void check_interrupt();
+ void update_backdrop();
+ void update_table_masks();
+ void set_palette();
+
+ static const device_timer_id TIMER_LINE = 0;
+
+ int m_vram_size; /* 4K, 8K, or 16K. This should be replaced by fetching data from an address space? */
+ devcb_write_line m_out_int_line_cb; /* Callback is called whenever the state of the INT output changes */
+
+ /* TMS9928A internal settings */
+ UINT8 m_ReadAhead;
+ UINT8 m_Regs[8];
+ UINT8 m_StatusReg;
+ UINT8 m_FifthSprite;
+ UINT8 m_latch;
+ UINT8 m_INT;
+ UINT16 m_Addr;
+ UINT16 m_colour;
+ UINT16 m_pattern;
+ UINT16 m_nametbl;
+ UINT16 m_spriteattribute;
+ UINT16 m_spritepattern;
+ int m_colourmask;
+ int m_patternmask;
+ bool m_50hz;
+ bool m_reva;
+ bool m_99;
+ rgb_t m_palette[16];
+
+ /* memory */
+ const address_space_config m_space_config;
+ address_space* m_vram_space;
+
+ bitmap_rgb32 m_tmpbmp;
+ emu_timer *m_line_timer;
+ UINT8 m_mode;
+
+ /* emulation settings */
+ int m_top_border;
+ int m_vertical_size;
+};
+
+
+class tms9918_device : public tms9928a_device
+{
+public:
+ tms9918_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+class tms9918a_device : public tms9928a_device
+{
+public:
+ tms9918a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+class tms9118_device : public tms9928a_device
+{
+public:
+ tms9118_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+class tms9128_device : public tms9928a_device
+{
+public:
+ tms9128_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+class tms9929_device : public tms9928a_device
+{
+public:
+ tms9929_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+class tms9929a_device : public tms9928a_device
+{
+public:
+ tms9929a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+class tms9129_device : public tms9928a_device
+{
+public:
+ tms9129_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+#endif
diff --git a/src/devices/video/upd3301.c b/src/devices/video/upd3301.c
new file mode 100644
index 00000000000..8370f52c85c
--- /dev/null
+++ b/src/devices/video/upd3301.c
@@ -0,0 +1,635 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ NEC uPD3301 Programmable CRT Controller emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - attributes
+ - N interrupt
+ - light pen
+ - reset counters
+ - proper DMA timing (now the whole screen is transferred at the end of the frame,
+ accurate timing requires CCLK timer which kills performance)
+
+*/
+
+#include "upd3301.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+#define COMMAND_MASK 0xe0
+#define COMMAND_RESET 0x00
+#define COMMAND_START_DISPLAY 0x20
+#define COMMAND_SET_INTERRUPT_MASK 0x40
+#define COMMAND_READ_LIGHT_PEN 0x60 // not supported
+#define COMMAND_LOAD_CURSOR_POSITION 0x80
+#define COMMAND_RESET_INTERRUPT 0xa0
+#define COMMAND_RESET_COUNTERS 0xc0 // not supported
+
+
+#define STATUS_VE 0x10
+#define STATUS_U 0x08 // not supported
+#define STATUS_N 0x04 // not supported
+#define STATUS_E 0x02
+#define STATUS_LP 0x01 // not supported
+
+
+enum
+{
+ MODE_NONE,
+ MODE_RESET,
+ MODE_READ_LIGHT_PEN,
+ MODE_LOAD_CURSOR_POSITION,
+ MODE_RESET_COUNTERS
+};
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type UPD3301 = &device_creator<upd3301_device>;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// upd3301_device - constructor
+//-------------------------------------------------
+
+upd3301_device::upd3301_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, UPD3301, "UPD3301", tag, owner, clock, "upd3301", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_write_int(*this),
+ m_write_drq(*this),
+ m_write_hrtc(*this),
+ m_write_vrtc(*this),
+ m_width(0),
+ m_status(0),
+ m_param_count(0),
+ m_data_fifo_pos(0),
+ m_attr_fifo_pos(0),
+ m_input_fifo(0),
+ m_me(0),
+ m_h(80),
+ m_l(20),
+ m_r(10),
+ m_v(6),
+ m_z(32),
+ m_attr_blink(0),
+ m_attr_frame(0),
+ m_cm(0),
+ m_cx(0),
+ m_cy(0),
+ m_cursor_blink(0),
+ m_cursor_frame(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void upd3301_device::device_start()
+{
+ // resolve callbacks
+ m_display_cb.bind_relative_to(*owner());
+ m_write_drq.resolve_safe();
+ m_write_int.resolve_safe();
+ m_write_hrtc.resolve_safe();
+ m_write_vrtc.resolve_safe();
+
+ // allocate timers
+ m_hrtc_timer = timer_alloc(TIMER_HRTC);
+ m_vrtc_timer = timer_alloc(TIMER_VRTC);
+ m_drq_timer = timer_alloc(TIMER_DRQ);
+
+ // state saving
+ save_item(NAME(m_y));
+ save_item(NAME(m_hrtc));
+ save_item(NAME(m_vrtc));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_status));
+ save_item(NAME(m_param_count));
+ save_item(NAME(m_data_fifo_pos));
+ save_item(NAME(m_attr_fifo_pos));
+ save_item(NAME(m_input_fifo));
+ save_item(NAME(m_mn));
+ save_item(NAME(m_me));
+ save_item(NAME(m_dma_mode));
+ save_item(NAME(m_h));
+ save_item(NAME(m_b));
+ save_item(NAME(m_l));
+ save_item(NAME(m_s));
+ save_item(NAME(m_c));
+ save_item(NAME(m_r));
+ save_item(NAME(m_v));
+ save_item(NAME(m_z));
+ save_item(NAME(m_at1));
+ save_item(NAME(m_at0));
+ save_item(NAME(m_sc));
+ save_item(NAME(m_attr));
+ save_item(NAME(m_attr_blink));
+ save_item(NAME(m_attr_frame));
+ save_item(NAME(m_cm));
+ save_item(NAME(m_cx));
+ save_item(NAME(m_cy));
+ save_item(NAME(m_cursor_blink));
+ save_item(NAME(m_cursor_frame));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void upd3301_device::device_reset()
+{
+ set_interrupt(0);
+ set_drq(0);
+
+ recompute_parameters();
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - handle clock change
+//-------------------------------------------------
+
+void upd3301_device::device_clock_changed()
+{
+ recompute_parameters();
+}
+
+
+//-------------------------------------------------
+// device_timer - handle timer events
+//-------------------------------------------------
+
+void upd3301_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_HRTC:
+ if (LOG) logerror("UPD3301 '%s' HRTC: %u\n", tag(), param);
+
+ m_write_hrtc(param);
+ m_hrtc = param;
+
+ update_hrtc_timer(param);
+ break;
+
+ case TIMER_VRTC:
+ if (LOG) logerror("UPD3301 '%s' VRTC: %u\n", tag(), param);
+
+ m_write_vrtc(param);
+ m_vrtc = param;
+
+ if (param && !m_me)
+ {
+ m_status |= STATUS_E;
+ set_interrupt(1);
+ }
+
+ update_vrtc_timer(param);
+ break;
+
+ case TIMER_DRQ:
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// read -
+//-------------------------------------------------
+
+READ8_MEMBER( upd3301_device::read )
+{
+ UINT8 data = 0;
+
+ switch (offset & 0x01)
+ {
+ case 0: // data
+ break;
+
+ case 1: // status
+ data = m_status;
+ m_status &= ~(STATUS_LP | STATUS_E |STATUS_N | STATUS_U);
+ break;
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write -
+//-------------------------------------------------
+
+WRITE8_MEMBER( upd3301_device::write )
+{
+ switch (offset & 0x01)
+ {
+ case 0: // data
+ switch (m_mode)
+ {
+ case MODE_RESET:
+ switch (m_param_count)
+ {
+ case 0:
+ m_dma_mode = BIT(data, 7);
+ m_h = (data & 0x7f) + 2;
+ if (LOG) logerror("UPD3301 '%s' DMA Mode: %s\n", tag(), m_dma_mode ? "character" : "burst");
+ if (LOG) logerror("UPD3301 '%s' H: %u\n", tag(), m_h);
+ break;
+
+ case 1:
+ m_b = ((data >> 6) + 1) * 16;
+ m_l = (data & 0x3f) + 1;
+ if (LOG) logerror("UPD3301 '%s' B: %u\n", tag(), m_b);
+ if (LOG) logerror("UPD3301 '%s' L: %u\n", tag(), m_l);
+ break;
+
+ case 2:
+ m_s = BIT(data, 7);
+ m_c = (data >> 4) & 0x03;
+ m_r = (data & 0x1f) + 1;
+ if (LOG) logerror("UPD3301 '%s' S: %u\n", tag(), m_s);
+ if (LOG) logerror("UPD3301 '%s' C: %u\n", tag(), m_c);
+ if (LOG) logerror("UPD3301 '%s' R: %u\n", tag(), m_r);
+ break;
+
+ case 3:
+ m_v = (data >> 5) + 1;
+ m_z = (data & 0x1f) + 2;
+ if (LOG) logerror("UPD3301 '%s' V: %u\n", tag(), m_v);
+ if (LOG) logerror("UPD3301 '%s' Z: %u\n", tag(), m_z);
+ recompute_parameters();
+ break;
+
+ case 4:
+ m_at1 = BIT(data, 7);
+ m_at0 = BIT(data, 6);
+ m_sc = BIT(data, 5);
+ m_attr = (data & 0x1f) + 1;
+ if (LOG) logerror("UPD3301 '%s' AT1: %u\n", tag(), m_at1);
+ if (LOG) logerror("UPD3301 '%s' AT0: %u\n", tag(), m_at0);
+ if (LOG) logerror("UPD3301 '%s' SC: %u\n", tag(), m_sc);
+ if (LOG) logerror("UPD3301 '%s' ATTR: %u\n", tag(), m_attr);
+
+ m_mode = MODE_NONE;
+ break;
+ }
+
+ m_param_count++;
+ break;
+
+ case MODE_LOAD_CURSOR_POSITION:
+ switch (m_param_count)
+ {
+ case 0:
+ m_cx = data & 0x7f;
+ if (LOG) logerror("UPD3301 '%s' CX: %u\n", tag(), m_cx);
+ break;
+
+ case 1:
+ m_cy = data & 0x3f;
+ if (LOG) logerror("UPD3301 '%s' CY: %u\n", tag(), m_cy);
+
+ m_mode = MODE_NONE;
+ break;
+ }
+
+ m_param_count++;
+ break;
+
+ default:
+ if (LOG) logerror("UPD3301 '%s' Invalid Parameter Byte %02x!\n", tag(), data);
+ }
+ break;
+
+ case 1: // command
+ m_mode = MODE_NONE;
+ m_param_count = 0;
+
+ switch (data & 0xe0)
+ {
+ case COMMAND_RESET:
+ if (LOG) logerror("UPD3301 '%s' Reset\n", tag());
+ m_mode = MODE_RESET;
+ set_display(0);
+ set_interrupt(0);
+ break;
+
+ case COMMAND_START_DISPLAY:
+ if (LOG) logerror("UPD3301 '%s' Start Display\n", tag());
+ set_display(1);
+ reset_counters();
+ break;
+
+ case COMMAND_SET_INTERRUPT_MASK:
+ if (LOG) logerror("UPD3301 '%s' Set Interrupt Mask\n", tag());
+ m_me = BIT(data, 0);
+ m_mn = BIT(data, 1);
+ if (LOG) logerror("UPD3301 '%s' ME: %u\n", tag(), m_me);
+ if (LOG) logerror("UPD3301 '%s' MN: %u\n", tag(), m_mn);
+ break;
+
+ case COMMAND_READ_LIGHT_PEN:
+ if (LOG) logerror("UPD3301 '%s' Read Light Pen\n", tag());
+ m_mode = MODE_READ_LIGHT_PEN;
+ break;
+
+ case COMMAND_LOAD_CURSOR_POSITION:
+ if (LOG) logerror("UPD3301 '%s' Load Cursor Position\n", tag());
+ m_mode = MODE_LOAD_CURSOR_POSITION;
+ m_cm = BIT(data, 0);
+ if (LOG) logerror("UPD3301 '%s' CM: %u\n", tag(), m_cm);
+ break;
+
+ case COMMAND_RESET_INTERRUPT:
+ if (LOG) logerror("UPD3301 '%s' Reset Interrupt\n", tag());
+ set_interrupt(0);
+ break;
+
+ case COMMAND_RESET_COUNTERS:
+ if (LOG) logerror("UPD3301 '%s' Reset Counters\n", tag());
+ m_mode = MODE_RESET_COUNTERS;
+ reset_counters();
+ break;
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// dack_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( upd3301_device::dack_w )
+{
+ if (m_y >= (m_l * m_r))
+ {
+ return;
+ }
+
+ if (m_data_fifo_pos < m_h)
+ {
+ m_data_fifo[m_data_fifo_pos][m_input_fifo] = data;
+ m_data_fifo_pos++;
+ }
+ else
+ {
+ m_attr_fifo[m_attr_fifo_pos][m_input_fifo] = data;
+ m_attr_fifo_pos++;
+ }
+
+ if ((m_data_fifo_pos == m_h) && (m_attr_fifo_pos == (m_attr << 1)))
+ {
+ m_input_fifo = !m_input_fifo;
+
+ m_data_fifo_pos = 0;
+ m_attr_fifo_pos = 0;
+
+ draw_scanline();
+
+ if (m_y == (m_l * m_r))
+ {
+ // end DMA transfer
+ set_drq(0);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// lpen_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( upd3301_device::lpen_w )
+{
+}
+
+
+//-------------------------------------------------
+// hrtc_r -
+//-------------------------------------------------
+
+READ_LINE_MEMBER( upd3301_device::hrtc_r )
+{
+ return m_hrtc;
+}
+
+
+//-------------------------------------------------
+// vrtc_r -
+//-------------------------------------------------
+
+READ_LINE_MEMBER( upd3301_device::vrtc_r )
+{
+ return m_vrtc;
+}
+
+
+//-------------------------------------------------
+// draw_scanline -
+//-------------------------------------------------
+
+void upd3301_device::draw_scanline()
+{
+ for (int lc = 0; lc < m_r; lc++)
+ {
+ for (int sx = 0; sx < m_h; sx++)
+ {
+ int y = m_y + lc;
+ UINT8 cc = m_data_fifo[sx][!m_input_fifo];
+ int hlgt = 0; // TODO
+ int rvv = 0; // TODO
+ int vsp = 0; // TODO
+ int sl0 = 0; // TODO
+ int sl12 = 0; // TODO
+ int csr = m_cm && m_cursor_blink && ((y / m_r) == m_cy) && (sx == m_cx);
+ int gpa = 0; // TODO
+
+ m_display_cb(*m_bitmap, y, sx, cc, lc, hlgt, rvv, vsp, sl0, sl12, csr, gpa);
+ }
+ }
+
+ m_y += m_r;
+}
+
+
+//-------------------------------------------------
+// update_screen -
+//-------------------------------------------------
+
+UINT32 upd3301_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ if (m_status & STATUS_VE)
+ {
+ m_y = 0;
+ m_bitmap = &bitmap;
+ m_data_fifo_pos = 0;
+ m_attr_fifo_pos = 0;
+
+ m_cursor_frame++;
+
+ if (m_cursor_frame == m_b)
+ {
+ m_cursor_frame = 0;
+ m_cursor_blink = !m_cursor_blink;
+ }
+
+ m_attr_frame++;
+
+ if (m_attr_frame == (m_b << 1))
+ {
+ m_attr_frame = 0;
+ m_attr_blink = !m_attr_blink;
+ }
+
+ // start DMA transfer
+ set_drq(1);
+ }
+ else
+ {
+ bitmap.fill(rgb_t(0x00,0x00,0x00), cliprect);
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// set_interrupt -
+//-------------------------------------------------
+
+void upd3301_device::set_interrupt(int state)
+{
+ if (LOG) logerror("UPD3301 '%s' Interrupt: %u\n", tag(), state);
+
+ m_write_int(state);
+
+ if (!state)
+ {
+ m_status &= ~(STATUS_N | STATUS_E);
+ }
+}
+
+
+//-------------------------------------------------
+// set_drq -
+//-------------------------------------------------
+
+void upd3301_device::set_drq(int state)
+{
+ if (LOG) logerror("UPD3301 '%s' DRQ: %u\n", tag(), state);
+
+ m_write_drq(state);
+}
+
+
+//-------------------------------------------------
+// set_display -
+//-------------------------------------------------
+
+void upd3301_device::set_display(int state)
+{
+ if (state)
+ {
+ m_status |= STATUS_VE;
+ }
+ else
+ {
+ m_status &= ~STATUS_VE;
+ }
+}
+
+
+//-------------------------------------------------
+// reset_counters -
+//-------------------------------------------------
+
+void upd3301_device::reset_counters()
+{
+ set_interrupt(0);
+ set_drq(0);
+}
+
+
+//-------------------------------------------------
+// update_hrtc_timer -
+//-------------------------------------------------
+
+void upd3301_device::update_hrtc_timer(int state)
+{
+ int y = m_screen->vpos();
+
+ int next_x = state ? m_h : 0;
+ int next_y = state ? y : ((y + 1) % ((m_l + m_v) * m_width));
+
+ attotime duration = m_screen->time_until_pos(next_y, next_x);
+
+ m_hrtc_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// update_vrtc_timer -
+//-------------------------------------------------
+
+void upd3301_device::update_vrtc_timer(int state)
+{
+ int next_y = state ? (m_l * m_r) : 0;
+
+ attotime duration = m_screen->time_until_pos(next_y, 0);
+
+ m_vrtc_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// recompute_parameters -
+//-------------------------------------------------
+
+void upd3301_device::recompute_parameters()
+{
+ int horiz_pix_total = (m_h + m_z) * m_width;
+ int vert_pix_total = (m_l + m_v) * m_r;
+
+ attoseconds_t refresh = HZ_TO_ATTOSECONDS(clock()) * horiz_pix_total * vert_pix_total;
+
+ rectangle visarea;
+
+ visarea.set(0, (m_h * m_width) - 1, 0, (m_l * m_r) - 1);
+
+ if (LOG)
+ {
+ if (LOG) logerror("UPD3301 '%s' Screen: %u x %u @ %f Hz\n", tag(), horiz_pix_total, vert_pix_total, 1 / ATTOSECONDS_TO_DOUBLE(refresh));
+ if (LOG) logerror("UPD3301 '%s' Visible Area: (%u, %u) - (%u, %u)\n", tag(), visarea.min_x, visarea.min_y, visarea.max_x, visarea.max_y);
+ }
+
+ m_screen->configure(horiz_pix_total, vert_pix_total, visarea, refresh);
+
+ update_hrtc_timer(0);
+ update_vrtc_timer(0);
+}
diff --git a/src/devices/video/upd3301.h b/src/devices/video/upd3301.h
new file mode 100644
index 00000000000..cef42e08627
--- /dev/null
+++ b/src/devices/video/upd3301.h
@@ -0,0 +1,194 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ NEC uPD3301 Programmable CRT Controller emulation
+
+**********************************************************************
+ _____ _____
+ VRTC 1 |* \_/ | 40 Vcc
+ RVV 2 | | 39 SL0
+ CSR 3 | | 38 LC0
+ LPEN 4 | | 37 LC1
+ INT 5 | | 36 LC2
+ DRQ 6 | | 35 LC3
+ _DACK 7 | | 34 VSP
+ A0 8 | | 33 SL12
+ _RD 9 | | 32 GPA
+ _WR 10 | uPD3301 | 31 HLGT
+ _CS 11 | | 30 CC7
+ DB0 12 | | 29 CC6
+ DB1 13 | | 28 CC5
+ DB2 14 | | 27 CC4
+ DB3 15 | | 26 CC3
+ DB4 16 | | 25 CC2
+ DB5 17 | | 24 CC1
+ DB6 18 | | 23 CC0
+ DB7 19 | | 22 CCLK
+ GND 20 |_____________| 21 HRTC
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __UPD3301__
+#define __UPD3301__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define UPD3301_DRAW_CHARACTER_MEMBER(_name) void _name(bitmap_rgb32 &bitmap, int y, int sx, UINT8 cc, UINT8 lc, int hlgt, int rvv, int vsp, int sl0, int sl12, int csr, int gpa)
+
+
+#define MCFG_UPD3301_CHARACTER_WIDTH(_value) \
+ upd3301_device::static_set_character_width(*device, _value);
+
+#define MCFG_UPD3301_DRAW_CHARACTER_CALLBACK_OWNER(_class, _method) \
+ upd3301_device::static_set_display_callback(*device, upd3301_draw_character_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_UPD3301_DRQ_CALLBACK(_write) \
+ devcb = &upd3301_device::set_drq_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_UPD3301_INT_CALLBACK(_write) \
+ devcb = &upd3301_device::set_int_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_UPD3301_HRTC_CALLBACK(_write) \
+ devcb = &upd3301_device::set_hrtc_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_UPD3301_VRTC_CALLBACK(_write) \
+ devcb = &upd3301_device::set_vrtc_wr_callback(*device, DEVCB_##_write);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef device_delegate<void (bitmap_rgb32 &bitmap, int y, int sx, UINT8 cc, UINT8 lc, int hlgt, int rvv, int vsp, int sl0, int sl12, int csr, int gpa)> upd3301_draw_character_delegate;
+
+
+// ======================> upd3301_device
+
+class upd3301_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ upd3301_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_character_width(device_t &device, int value) { downcast<upd3301_device &>(device).m_width = value; }
+ static void static_set_display_callback(device_t &device, upd3301_draw_character_delegate callback) { downcast<upd3301_device &>(device).m_display_cb = callback; }
+
+ template<class _Object> static devcb_base &set_drq_wr_callback(device_t &device, _Object object) { return downcast<upd3301_device &>(device).m_write_drq.set_callback(object); }
+ template<class _Object> static devcb_base &set_int_wr_callback(device_t &device, _Object object) { return downcast<upd3301_device &>(device).m_write_int.set_callback(object); }
+ template<class _Object> static devcb_base &set_hrtc_wr_callback(device_t &device, _Object object) { return downcast<upd3301_device &>(device).m_write_hrtc.set_callback(object); }
+ template<class _Object> static devcb_base &set_vrtc_wr_callback(device_t &device, _Object object) { return downcast<upd3301_device &>(device).m_write_vrtc.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_WRITE8_MEMBER( dack_w );
+ DECLARE_WRITE_LINE_MEMBER( lpen_w );
+ DECLARE_READ_LINE_MEMBER( hrtc_r );
+ DECLARE_READ_LINE_MEMBER( vrtc_r );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_clock_changed();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ enum
+ {
+ TIMER_HRTC,
+ TIMER_VRTC,
+ TIMER_DRQ
+ };
+
+ void set_interrupt(int state);
+ void set_drq(int state);
+ void set_display(int state);
+ void reset_counters();
+ void update_hrtc_timer(int state);
+ void update_vrtc_timer(int state);
+ void recompute_parameters();
+
+ void draw_scanline();
+
+ devcb_write_line m_write_int;
+ devcb_write_line m_write_drq;
+ devcb_write_line m_write_hrtc;
+ devcb_write_line m_write_vrtc;
+
+ upd3301_draw_character_delegate m_display_cb;
+ int m_width;
+
+ // screen drawing
+ bitmap_rgb32 *m_bitmap; // bitmap
+ int m_y; // current scanline
+ int m_hrtc; // horizontal retrace
+ int m_vrtc; // vertical retrace
+
+ // live state
+ int m_mode; // command mode
+ UINT8 m_status; // status register
+ int m_param_count; // parameter count
+
+ // FIFOs
+ UINT8 m_data_fifo[80][2]; // row data FIFO
+ UINT8 m_attr_fifo[40][2]; // attribute FIFO
+ int m_data_fifo_pos; // row data FIFO position
+ int m_attr_fifo_pos; // attribute FIFO position
+ int m_input_fifo; // which FIFO is in input mode
+
+ // interrupts
+ int m_mn; // disable special character interrupt
+ int m_me; // disable end of screen interrupt
+ int m_dma_mode; // DMA mode
+
+ // screen geometry
+ int m_h; // characters per line
+ int m_b; // cursor blink time
+ int m_l; // lines per screen
+ int m_s; // display every other line
+ int m_c; // cursor mode
+ int m_r; // lines per character
+ int m_v; // vertical blanking height
+ int m_z; // horizontal blanking width
+
+ // attributes
+ int m_at1; //
+ int m_at0; //
+ int m_sc; //
+ int m_attr; // attributes per row
+ int m_attr_blink; // attribute blink
+ int m_attr_frame; // attribute blink frame counter
+
+ // cursor
+ int m_cm; // cursor visible
+ int m_cx; // cursor column
+ int m_cy; // cursor row
+ int m_cursor_blink; // cursor blink
+ int m_cursor_frame; // cursor blink frame counter
+
+ // timers
+ emu_timer *m_hrtc_timer;
+ emu_timer *m_vrtc_timer;
+ emu_timer *m_drq_timer;
+};
+
+
+// device type definition
+extern const device_type UPD3301;
+
+
+
+#endif
diff --git a/src/devices/video/upd7220.c b/src/devices/video/upd7220.c
new file mode 100644
index 00000000000..05f64cc2878
--- /dev/null
+++ b/src/devices/video/upd7220.c
@@ -0,0 +1,1683 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese, Miodrag Milanovic, Carl
+/**********************************************************************
+
+ Intel 82720 Graphics Display Controller emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - implement FIFO as ring buffer
+ - commands
+ - DMAR
+ - DMAW
+ - incomplete / unimplemented FIGD / GCHRD draw modes
+ - FIGD character
+ - slanted character
+ - read-modify-write cycle
+ - read data
+ - modify data
+ - write data
+ - QX-10 diagnostic test has positioning bugs with the bitmap display test;
+ - QX-10 diagnostic test misses the zooming factor (external pin);
+ - compis2 SAD address for bitmap is 0x20000 for whatever reason (presumably missing banking);
+ - A5105 has a FIFO bug with the RDAT, should be a lot larger when it scrolls up.
+ The problem is that DMA-ing with RDAT/WDAT shouldn't be instant;
+
+ - honor visible area
+ - wide mode (32-bit access)
+ - light pen
+ - dad and mask are the same, in figd dad is shifted every step and when msb or lsb are 1 ead is advanced in x dir
+
+*/
+
+#include "emu.h"
+#include "upd7220.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+// todo typedef
+enum
+{
+ COMMAND_INVALID = -1,
+ COMMAND_RESET,
+ COMMAND_SYNC,
+ COMMAND_VSYNC,
+ COMMAND_CCHAR,
+ COMMAND_START,
+ COMMAND_BCTRL,
+ COMMAND_ZOOM,
+ COMMAND_CURS,
+ COMMAND_PRAM,
+ COMMAND_PITCH,
+ COMMAND_WDAT,
+ COMMAND_MASK,
+ COMMAND_FIGS,
+ COMMAND_FIGD,
+ COMMAND_GCHRD,
+ COMMAND_RDAT,
+ COMMAND_CURD,
+ COMMAND_LPRD,
+ COMMAND_DMAR,
+ COMMAND_DMAW,
+ COMMAND_5A
+};
+
+enum
+{
+ FIFO_READ = 0,
+ FIFO_WRITE
+};
+
+enum
+{
+ FIFO_EMPTY = -1,
+ FIFO_PARAMETER,
+ FIFO_COMMAND
+};
+
+#define UPD7220_COMMAND_RESET 0x00
+#define UPD7220_COMMAND_SYNC 0x0e // & 0xfe
+#define UPD7220_COMMAND_VSYNC 0x6e // & 0xfe
+#define UPD7220_COMMAND_CCHAR 0x4b
+#define UPD7220_COMMAND_START 0x6b
+#define UPD7220_COMMAND_BCTRL 0x0c // & 0xfe
+#define UPD7220_COMMAND_ZOOM 0x46
+#define UPD7220_COMMAND_CURS 0x49
+#define UPD7220_COMMAND_PRAM 0x70 // & 0xf0
+#define UPD7220_COMMAND_PITCH 0x47
+#define UPD7220_COMMAND_WDAT 0x20 // & 0xe4
+#define UPD7220_COMMAND_MASK 0x4a
+#define UPD7220_COMMAND_FIGS 0x4c
+#define UPD7220_COMMAND_FIGD 0x6c
+#define UPD7220_COMMAND_GCHRD 0x68
+#define UPD7220_COMMAND_RDAT 0xa0 // & 0xe4
+#define UPD7220_COMMAND_CURD 0xe0
+#define UPD7220_COMMAND_LPRD 0xc0
+#define UPD7220_COMMAND_DMAR 0xa4 // & 0xe4
+#define UPD7220_COMMAND_DMAW 0x24 // & 0xe4
+#define UPD7220_COMMAND_5A 0x5a
+
+#define UPD7220_SR_DATA_READY 0x01
+#define UPD7220_SR_FIFO_FULL 0x02
+#define UPD7220_SR_FIFO_EMPTY 0x04
+#define UPD7220_SR_DRAWING_IN_PROGRESS 0x08
+#define UPD7220_SR_DMA_EXECUTE 0x10
+#define UPD7220_SR_VSYNC_ACTIVE 0x20
+#define UPD7220_SR_HBLANK_ACTIVE 0x40
+#define UPD7220_SR_LIGHT_PEN_DETECT 0x80
+
+#define UPD7220_MODE_REFRESH_RAM 0x04
+#define UPD7220_MODE_DRAW_ON_RETRACE 0x10
+#define UPD7220_MODE_DISPLAY_MASK 0x22
+#define UPD7220_MODE_DISPLAY_MIXED 0x00
+#define UPD7220_MODE_DISPLAY_GRAPHICS 0x02
+#define UPD7220_MODE_DISPLAY_CHARACTER 0x20
+#define UPD7220_MODE_DISPLAY_INVALID 0x22
+#define UPD7220_MODE_INTERLACE_MASK 0x09
+#define UPD7220_MODE_INTERLACE_NONE 0x00
+#define UPD7220_MODE_INTERLACE_INVALID 0x01
+#define UPD7220_MODE_INTERLACE_REPEAT 0x08
+#define UPD7220_MODE_INTERLACE_ON 0x09
+
+
+static const int x_dir[8] = { 0, 1, 1, 1, 0,-1,-1,-1};
+static const int y_dir[8] = { 1, 1, 0,-1,-1,-1, 0, 1};
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+const device_type UPD7220 = &device_creator<upd7220_device>;
+
+
+// default address map
+static ADDRESS_MAP_START( upd7220_vram, AS_0, 16, upd7220_device )
+ AM_RANGE(0x00000, 0x3ffff) AM_RAM
+ADDRESS_MAP_END
+
+
+// internal 128x14 control ROM
+// hand-dumped as little-endian from a die shot
+ROM_START( upd7220 )
+ ROM_REGION( 0x100, "upd7220", 0 )
+ ROM_LOAD( "upd7220.bin", 0x000, 0x100, CRC(3c92b218) SHA1(e154b3106a80c9c98d9f2ee18efcd7f4b4aa7d49) )
+ROM_END
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *upd7220_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *upd7220_device::device_rom_region() const
+{
+ return ROM_NAME( upd7220 );
+}
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 upd7220_device::readbyte(offs_t address)
+{
+ return space().read_byte(address);
+}
+
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void upd7220_device::writebyte(offs_t address, UINT8 data)
+{
+ space().write_byte(address, data);
+}
+
+inline UINT16 upd7220_device::readword(offs_t address)
+{
+ return space().read_word(address);
+}
+
+
+inline void upd7220_device::writeword(offs_t address, UINT16 data)
+{
+ space().write_word(address, data);
+}
+
+//-------------------------------------------------
+// fifo_clear -
+//-------------------------------------------------
+
+inline void upd7220_device::fifo_clear()
+{
+ for (int i = 0; i < 16; i++)
+ {
+ m_fifo[i] = 0;
+ m_fifo_flag[i] = FIFO_EMPTY;
+ }
+
+ m_fifo_ptr = -1;
+
+ m_sr &= ~UPD7220_SR_DATA_READY;
+ m_sr |= UPD7220_SR_FIFO_EMPTY;
+ m_sr &= ~UPD7220_SR_FIFO_FULL;
+}
+
+
+//-------------------------------------------------
+// fifo_param_count -
+//-------------------------------------------------
+
+inline int upd7220_device::fifo_param_count()
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ if (m_fifo_flag[i] != FIFO_PARAMETER) break;
+ }
+
+ return i;
+}
+
+
+//-------------------------------------------------
+// fifo_set_direction -
+//-------------------------------------------------
+
+inline void upd7220_device::fifo_set_direction(int dir)
+{
+ if (m_fifo_dir != dir)
+ {
+ fifo_clear();
+ }
+
+ m_fifo_dir = dir;
+}
+
+
+//-------------------------------------------------
+// queue -
+//-------------------------------------------------
+
+inline void upd7220_device::queue(UINT8 data, int flag)
+{
+ if (m_fifo_ptr < 15)
+ {
+ m_fifo_ptr++;
+
+ m_fifo[m_fifo_ptr] = data;
+ m_fifo_flag[m_fifo_ptr] = flag;
+
+ if (m_fifo_ptr == 16)
+ {
+ m_sr |= UPD7220_SR_FIFO_FULL;
+ }
+
+ m_sr &= ~UPD7220_SR_FIFO_EMPTY;
+ }
+ else
+ {
+ // TODO what happen? somebody set us up the bomb
+ logerror("FIFO?\n");
+ }
+}
+
+
+//-------------------------------------------------
+// dequeue -
+//-------------------------------------------------
+
+inline void upd7220_device::dequeue(UINT8 *data, int *flag)
+{
+ *data = m_fifo[0];
+ *flag = m_fifo_flag[0];
+
+ if (m_fifo_ptr > -1)
+ {
+ for (int i = 0; i < 15; i++)
+ {
+ m_fifo[i] = m_fifo[i + 1];
+ m_fifo_flag[i] = m_fifo_flag[i + 1];
+ }
+
+ m_fifo[15] = 0;
+ m_fifo_flag[15] = 0;
+
+ m_fifo_ptr--;
+
+ if (m_fifo_ptr == -1)
+ {
+ m_sr &= ~UPD7220_SR_DATA_READY;
+ m_sr |= UPD7220_SR_FIFO_EMPTY;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// update_vsync_timer -
+//-------------------------------------------------
+
+inline void upd7220_device::update_vsync_timer(int state)
+{
+ int next_y = state ? m_vs : 0;
+
+ attotime duration = m_screen->time_until_pos(next_y, 0);
+
+ m_vsync_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// update_hsync_timer -
+//-------------------------------------------------
+
+inline void upd7220_device::update_hsync_timer(int state)
+{
+ int y = m_screen->vpos();
+
+ int next_x = state ? m_hs : 0;
+ int next_y = state ? y : ((y + 1) % m_al);
+
+ attotime duration = m_screen->time_until_pos(next_y, next_x);
+
+ m_hsync_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// update_blank_timer -
+//-------------------------------------------------
+
+inline void upd7220_device::update_blank_timer(int state)
+{
+ int y = m_screen->vpos();
+
+ int next_x = state ? (m_hs + m_hbp) : (m_hs + m_hbp + (m_aw << 3));
+ int next_y = state ? ((y + 1) % (m_vs + m_vbp + m_al + m_vfp - 1)) : y;
+
+ attotime duration = m_screen->time_until_pos(next_y, next_x);
+
+ m_hsync_timer->adjust(duration, !state);
+}
+
+
+//-------------------------------------------------
+// recompute_parameters -
+//-------------------------------------------------
+
+inline void upd7220_device::recompute_parameters()
+{
+ int horiz_mult = 16, vert_mult = 1;
+ /* TODO: assume that the pitch also controls number of horizontal pixels in a single cell */
+ // horiz_mult = 4 if both mixed and interlace?
+ if((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED)
+ horiz_mult = 8;
+ else if((m_mode & UPD7220_MODE_INTERLACE_MASK) == UPD7220_MODE_INTERLACE_ON)
+ {
+ // in interlaced mode every line contains both fields
+ horiz_mult = 8;
+ vert_mult = 2;
+ }
+
+ int horiz_pix_total = (m_hs + m_hbp + m_hfp + m_aw) * horiz_mult;
+ int vert_pix_total = (m_vs + m_vbp + m_al + m_vfp) * vert_mult;
+
+ //printf("%d %d %d %d\n",m_hs,m_hbp,m_aw,m_hfp);
+ //printf("%d %d\n",m_aw * 8,m_pitch * 8);
+
+ if (horiz_pix_total == 0 || vert_pix_total == 0) //bail out if screen params aren't valid
+ return;
+
+ attoseconds_t refresh = HZ_TO_ATTOSECONDS(clock() * 8) * horiz_pix_total * vert_pix_total;
+
+ rectangle visarea;
+
+ visarea.min_x = 0; //(m_hs + m_hbp) * 8;
+ visarea.min_y = m_vbp; //m_vs + m_vbp;
+ visarea.max_x = m_aw * horiz_mult - 1;//horiz_pix_total - (m_hfp * 8) - 1;
+ visarea.max_y = m_al * vert_mult + m_vbp - 1;//vert_pix_total - m_vfp - 1;
+
+ LOG(("uPD7220 '%s' Screen: %u x %u @ %f Hz\n", tag(), horiz_pix_total, vert_pix_total, 1 / ATTOSECONDS_TO_DOUBLE(refresh)));
+ LOG(("Visible Area: (%u, %u) - (%u, %u)\n", visarea.min_x, visarea.min_y, visarea.max_x, visarea.max_y));
+ LOG(("%d %d %d %d %d\n",m_hs,m_hbp,m_aw,m_hfp,m_pitch));
+ LOG(("%d %d %d %d\n",m_vs,m_vbp,m_al,m_vfp));
+
+ if (m_m)
+ {
+ m_screen->configure(horiz_pix_total, vert_pix_total, visarea, refresh);
+
+ update_hsync_timer(0);
+ update_vsync_timer(0);
+ }
+ else
+ {
+ m_hsync_timer->enable(0);
+ m_vsync_timer->enable(0);
+ }
+
+ update_blank_timer(0);
+}
+
+
+//-------------------------------------------------
+// reset_figs_param -
+//-------------------------------------------------
+
+inline void upd7220_device::reset_figs_param()
+{
+ m_figs.m_dc = 0x0000;
+ m_figs.m_d = 0x0008;
+ m_figs.m_d1 = 0x0008;
+ m_figs.m_d2 = 0x0000;
+ m_figs.m_dm = 0x0000;
+ m_figs.m_gd = 0;
+}
+
+
+//-------------------------------------------------
+// read_vram -
+//-------------------------------------------------
+
+inline void upd7220_device::read_vram(UINT8 type, UINT8 mod)
+{
+ if (type == 1)
+ {
+ LOG (("uPD7220 invalid type 1 RDAT parameter\n"));
+ return;
+ }
+
+ if (mod)
+ LOG (("uPD7220 RDAT used with mod = %02x?\n",mod));
+
+ while (m_figs.m_dc && m_fifo_ptr < (type ? 15 : 14))
+ {
+ switch(type)
+ {
+ case 0:
+ queue(readbyte(m_ead*2), 0);
+ queue(readbyte(m_ead*2+1), 0);
+ break;
+ case 2:
+ queue(readbyte(m_ead*2), 0);
+ break;
+ case 3:
+ queue(readbyte(m_ead*2+1), 0);
+ break;
+ }
+
+ m_figs.m_dc--;
+ m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * m_pitch);
+ m_ead &= 0x3ffff;
+ }
+
+ if (m_figs.m_dc == 0)
+ reset_figs_param();
+}
+
+
+//-------------------------------------------------
+// write_vram -
+//-------------------------------------------------
+
+inline void upd7220_device::write_vram(UINT8 type, UINT8 mod)
+{
+ UINT16 result;
+
+ if (type == 1)
+ {
+ logerror("uPD7220 invalid type 1 WDAT parameter\n");
+ return;
+ }
+
+ result = 0;
+
+ result = m_pr[1] | (m_pr[2] << 8);
+
+ switch(type)
+ {
+ case 0:
+ result &= m_mask;
+ break;
+ case 2:
+ result &= (m_mask & 0xff);
+ break;
+ case 3:
+ result <<= 8;
+ result &= (m_mask & 0xff00);
+ break;
+ }
+
+ //if(result)
+ {
+ //printf("%04x %02x %02x %04x %02x %02x\n",readbyte(m_ead),m_pr[1],m_pr[2],m_mask,type,mod);
+ //printf("%04x %02x %02x\n",m_ead,m_figs.m_dir,m_pitch);
+ //printf("%04x %04x %02x %04x\n",m_ead,result,mod,m_figs.m_dc);
+ }
+
+ for(int i = 0; i < m_figs.m_dc + 1; i++)
+ {
+ switch(mod & 3)
+ {
+ case 0x00: //replace
+ if(type == 0)
+ writeword(m_ead*2+0, result);
+ if(type == 2)
+ writebyte(m_ead*2+0, result & 0xff);
+ if(type == 3)
+ writebyte(m_ead*2+1, result >> 8);
+ break;
+ case 0x01: //complement
+ if(type == 0)
+ writeword(m_ead*2+0, readword(m_ead*2+0) ^ result);
+ if(type == 2)
+ writebyte(m_ead*2+0, readbyte(m_ead*2+0) ^ (result & 0xff));
+ if(type == 3)
+ writebyte(m_ead*2+1, readbyte(m_ead*2+1) ^ (result >> 8));
+ break;
+ case 0x02: //reset to zero
+ if(type == 0)
+ writeword(m_ead*2+0, readword(m_ead*2+0) & ~result);
+ if(type == 2)
+ writebyte(m_ead*2+0, readbyte(m_ead*2+0) & ~(result & 0xff));
+ if(type == 3)
+ writebyte(m_ead*2+1, readbyte(m_ead*2+1) & ~(result >> 8));
+ break;
+ case 0x03: //set to one
+ if(type == 0)
+ writeword(m_ead*2+0, readword(m_ead*2+0) | result);
+ if(type == 2)
+ writebyte(m_ead*2+0, readbyte(m_ead*2+0) | (result & 0xff));
+ if(type == 3)
+ writebyte(m_ead*2+1, readbyte(m_ead*2+1) | (result >> 8));
+ break;
+ }
+
+ m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * m_pitch);
+ m_ead &= 0x3ffff;
+ }
+}
+
+
+//-------------------------------------------------
+// get_text_partition -
+//-------------------------------------------------
+
+inline void upd7220_device::get_text_partition(int index, UINT32 *sad, UINT16 *len, int *im, int *wd)
+{
+ *sad = ((m_ra[(index * 4) + 1] & 0x1f) << 8) | m_ra[(index * 4) + 0];
+ *len = ((m_ra[(index * 4) + 3] & 0x3f) << 4) | (m_ra[(index * 4) + 2] >> 4);
+ *im = BIT(m_ra[(index * 4) + 3], 6);
+ *wd = BIT(m_ra[(index * 4) + 3], 7);
+}
+
+
+//-------------------------------------------------
+// get_graphics_partition -
+//-------------------------------------------------
+
+inline void upd7220_device::get_graphics_partition(int index, UINT32 *sad, UINT16 *len, int *im, int *wd)
+{
+ *sad = ((m_ra[(index * 4) + 2] & 0x03) << 16) | (m_ra[(index * 4) + 1] << 8) | m_ra[(index * 4) + 0];
+ *len = ((m_ra[(index * 4) + 3] & 0x3f) << 4) | (m_ra[(index * 4) + 2] >> 4);
+ *im = BIT(m_ra[(index * 4) + 3], 6);
+ *wd = BIT(m_ra[(index * 4) + 3], 7);
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// upd7220_device - constructor
+//-------------------------------------------------
+
+upd7220_device::upd7220_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, UPD7220, "uPD7220", tag, owner, clock, "upd7220", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_write_drq(*this),
+ m_write_hsync(*this),
+ m_write_vsync(*this),
+ m_write_blank(*this),
+ m_mask(0),
+ m_pitch(0),
+ m_ead(0),
+ m_dad(0),
+ m_lad(0),
+ m_ra_addr(0),
+ m_sr(UPD7220_SR_FIFO_EMPTY),
+ m_cr(0),
+ m_param_ptr(0),
+ m_fifo_ptr(-1),
+ m_fifo_dir(0),
+ m_mode(0),
+ m_de(0),
+ m_m(0),
+ m_aw(0),
+ m_al(0),
+ m_vs(0),
+ m_vfp(0),
+ m_vbp(0),
+ m_hs(0),
+ m_hfp(0),
+ m_hbp(0),
+ m_dc(0),
+ m_sc(0),
+ m_br(0),
+ m_ctop(0),
+ m_cbot(0),
+ m_lr(0),
+ m_disp(0),
+ m_gchr(0),
+ m_bitmap_mod(0),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 16, 18, 0, NULL, *ADDRESS_MAP_NAME(upd7220_vram))
+{
+ for (int i = 0; i < 16; i++)
+ {
+ m_fifo[i] = 0;
+ m_fifo_flag[i] = FIFO_EMPTY;
+
+ m_ra[i] = 0;
+ }
+
+ for (int i = 0; i < 17; i++)
+ {
+ m_pr[i] = 0;
+ }
+
+ memset(&m_figs, 0x00, sizeof(m_figs));
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void upd7220_device::device_start()
+{
+ // resolve callbacks
+ m_display_cb.bind_relative_to(*owner());
+ m_draw_text_cb.bind_relative_to(*owner());
+
+ m_write_drq.resolve_safe();
+ m_write_hsync.resolve_safe();
+ m_write_vsync.resolve_safe();
+ m_write_blank.resolve_safe();
+
+ // allocate timers
+ m_vsync_timer = timer_alloc(TIMER_VSYNC);
+ m_hsync_timer = timer_alloc(TIMER_HSYNC);
+ m_blank_timer = timer_alloc(TIMER_BLANK);
+
+ // register for state saving
+ save_item(NAME(m_ra));
+ save_item(NAME(m_sr));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_de));
+ save_item(NAME(m_aw));
+ save_item(NAME(m_al));
+ save_item(NAME(m_vs));
+ save_item(NAME(m_vfp));
+ save_item(NAME(m_vbp));
+ save_item(NAME(m_hs));
+ save_item(NAME(m_hfp));
+ save_item(NAME(m_hbp));
+ save_item(NAME(m_m));
+ save_item(NAME(m_dc));
+ save_item(NAME(m_sc));
+ save_item(NAME(m_br));
+ save_item(NAME(m_lr));
+ save_item(NAME(m_ctop));
+ save_item(NAME(m_cbot));
+ save_item(NAME(m_ead));
+ save_item(NAME(m_dad));
+ save_item(NAME(m_lad));
+ save_item(NAME(m_disp));
+ save_item(NAME(m_gchr));
+ save_item(NAME(m_mask));
+ save_item(NAME(m_pitch));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void upd7220_device::device_reset()
+{
+ m_write_drq(CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void upd7220_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_HSYNC:
+ if (param)
+ {
+ m_sr |= UPD7220_SR_HBLANK_ACTIVE;
+ }
+ else
+ {
+ m_sr &= ~UPD7220_SR_HBLANK_ACTIVE;
+ }
+
+ m_write_hsync(param);
+
+ update_hsync_timer(param);
+ break;
+
+ case TIMER_VSYNC:
+ if (param)
+ {
+ m_sr |= UPD7220_SR_VSYNC_ACTIVE;
+ }
+ else
+ {
+ m_sr &= ~UPD7220_SR_VSYNC_ACTIVE;
+ }
+
+ m_write_vsync(param);
+
+ update_vsync_timer(param);
+ break;
+
+ case TIMER_BLANK:
+ if (param)
+ {
+ m_sr |= UPD7220_SR_HBLANK_ACTIVE;
+ }
+ else
+ {
+ m_sr &= ~UPD7220_SR_HBLANK_ACTIVE;
+ }
+
+ m_write_blank(param);
+
+ update_blank_timer(param);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// draw_pixel -
+//-------------------------------------------------
+
+void upd7220_device::draw_pixel(int x, int y, int xi, UINT16 tile_data)
+{
+ UINT32 addr = ((y * (m_pitch << (m_figs.m_gd ? 0 : 1))) + (x >> 3)) & 0x3ffff;
+ UINT16 data = readword(addr);
+ UINT16 new_pixel = (tile_data & (1 << (xi & 0xf))) ? (1 << (x & 0xf)) : 0;
+
+ switch(m_bitmap_mod)
+ {
+ case 0: //replace
+ writeword(addr, (data & ~(1 << (x & 0xf))) | new_pixel);
+ break;
+ case 1: //complement
+ writeword(addr, data ^ new_pixel);
+ break;
+ case 2: //reset
+ writeword(addr, data & ~new_pixel);
+ break;
+ case 3: //set
+ writeword(addr, data | new_pixel);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// draw_line -
+//-------------------------------------------------
+
+void upd7220_device::draw_line(int x, int y)
+{
+ int xi, yi;
+ int d = (m_figs.m_d & 0x2000) ? (INT16)(m_figs.m_d | 0xe000) : m_figs.m_d;
+ int d2 = (m_figs.m_d2 & 0x2000) ? (INT16)(m_figs.m_d2 | 0xe000) : m_figs.m_d2;
+ UINT16 pattern = (m_ra[8]) | (m_ra[9]<<8);
+ const int dot_dir[4] = {1, -1, -1, 1};
+
+ LOG(("uPD7220 line check: %d %d %02x %08x %d %d %d\n",x,y,m_figs.m_dir,m_ead,m_figs.m_d1,m_figs.m_dc,m_bitmap_mod));
+
+ for(yi = xi = 0; yi <= m_figs.m_dc; yi++)
+ {
+ switch(m_figs.m_dir & 3)
+ {
+ case 1:
+ case 2:
+ draw_pixel(yi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3] + x, xi * dot_dir[m_figs.m_dir >> 1] + y, yi, pattern);
+ break;
+ default:
+ draw_pixel(xi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3] + x, yi * dot_dir[m_figs.m_dir >> 1] + y, yi, pattern);
+ break;
+ }
+ if(d > 0)
+ {
+ xi++;
+ d += d2;
+ }
+ else
+ d += m_figs.m_d1;
+ }
+
+ switch(m_figs.m_dir & 3)
+ {
+ case 1:
+ case 2:
+ x += yi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3];
+ y += xi * dot_dir[m_figs.m_dir >> 1];
+ break;
+ default:
+ x += xi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3];
+ y += yi * dot_dir[m_figs.m_dir >> 1];
+ break;
+ }
+
+ m_ead = (x >> 4) + (y * (m_pitch >> m_figs.m_gd));
+ m_dad = x & 0x0f;
+}
+
+//-------------------------------------------------
+// draw_arc -
+//-------------------------------------------------
+
+void upd7220_device::draw_arc(int x, int y)
+{
+ int xi = m_figs.m_d + 1, yi = 0, err = -m_figs.m_d;
+ int x0, y0;
+ UINT16 pattern = (m_ra[8]) | (m_ra[9]<<8);
+ const int dot_dir[4] = {1, -1, -1, 1};
+
+ switch(m_figs.m_dir & 3)
+ {
+ case 1:
+ case 2:
+ x0 = x;
+ y0 = y + xi * dot_dir[m_figs.m_dir >> 1];
+ break;
+ default:
+ x0 = x + xi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3];
+ y0 = y;
+ break;
+ }
+
+ LOG(("uPD7220 arc check: %d %d %02x %08x %d %d %d\n",x,y,m_figs.m_dir,m_ead,m_figs.m_dm,m_figs.m_dc,m_figs.m_d));
+
+ for(int i = 0; i <= m_figs.m_dc; i++)
+ {
+ if(i >= m_figs.m_dm)
+ {
+ switch(m_figs.m_dir & 3)
+ {
+ case 1:
+ case 2:
+ draw_pixel(yi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3] + x0, xi * dot_dir[m_figs.m_dir >> 1] + y0, i, pattern);
+ break;
+ default:
+ draw_pixel(xi * dot_dir[m_figs.m_dir >> 1] + x0, yi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3] + y0, i, pattern);
+ break;
+ }
+ }
+ yi++;
+ if(err < 0)
+ err += (yi + 1) << 1;
+ else
+ {
+ xi--;
+ err += (yi - xi + 1) << 1;
+ }
+ }
+ switch(m_figs.m_dir & 3)
+ {
+ case 1:
+ case 2:
+ x += (m_figs.m_dc + 1) * dot_dir[((m_figs.m_dir >> 1) + 3) & 3];
+ break;
+ default:
+ y += (m_figs.m_dc + 1) * dot_dir[m_figs.m_dir >> 1];
+ break;
+ }
+
+ m_ead = (x >> 4) + (y * (m_pitch >> m_figs.m_gd));
+ m_dad = x & 0x0f;
+}
+
+//-------------------------------------------------
+// draw_rectangle -
+//-------------------------------------------------
+
+void upd7220_device::draw_rectangle(int x, int y)
+{
+ int i;
+ const int rect_x_dir[8] = { 0, 1, 0,-1, 1, 1,-1,-1 };
+ const int rect_y_dir[8] = { 1, 0,-1, 0, 1,-1,-1, 1 };
+ UINT8 rect_type,rect_dir;
+ UINT16 pattern = (m_ra[8]) | (m_ra[9]<<8);
+
+ LOG(("uPD7220 rectangle check: %d %d %02x %08x\n",x,y,m_figs.m_dir,m_ead));
+
+ rect_type = (m_figs.m_dir & 1) << 2;
+ rect_dir = rect_type | (((m_figs.m_dir >> 1) + 0) & 3);
+
+ for(i = 0;i < m_figs.m_d;i++)
+ {
+ draw_pixel(x,y,i,pattern);
+ x+=rect_x_dir[rect_dir];
+ y+=rect_y_dir[rect_dir];
+ }
+
+ rect_dir = rect_type | (((m_figs.m_dir >> 1) + 1) & 3);
+
+ for(i = 0;i < m_figs.m_d2;i++)
+ {
+ draw_pixel(x,y,i,pattern);
+ x+=rect_x_dir[rect_dir];
+ y+=rect_y_dir[rect_dir];
+ }
+
+ rect_dir = rect_type | (((m_figs.m_dir >> 1) + 2) & 3);
+
+ for(i = 0;i < m_figs.m_d;i++)
+ {
+ draw_pixel(x,y,i,pattern);
+ x+=rect_x_dir[rect_dir];
+ y+=rect_y_dir[rect_dir];
+ }
+
+ rect_dir = rect_type | (((m_figs.m_dir >> 1) + 3) & 3);
+
+ for(i = 0;i < m_figs.m_d2;i++)
+ {
+ draw_pixel(x,y,i,pattern);
+ x+=rect_x_dir[rect_dir];
+ y+=rect_y_dir[rect_dir];
+ }
+
+ m_ead = (x >> 4) + (y * (m_pitch >> m_figs.m_gd));
+ m_dad = x & 0x0f;
+
+}
+
+
+//-------------------------------------------------
+// draw_char -
+//-------------------------------------------------
+
+void upd7220_device::draw_char(int x, int y)
+{
+ int isize,psize;
+ UINT16 tile_data = 0;
+
+ LOG(("uPD7220 char check: %d %d %02x %08x %d %d %02x\n",x,y,m_figs.m_dir,m_ead,m_figs.m_d,m_figs.m_dc,m_figs.m_figure_type));
+
+ isize = m_figs.m_d & 0x3ff;
+ /* Guess: D has presumably upper bits for ysize, QX-10 relies on this (TODO: check this on any real HW) */
+ psize = ((m_figs.m_d & 0x400) + m_figs.m_dc) + 1;
+
+ for(int pi = 0; pi < psize; pi++)
+ {
+ tile_data = (m_ra[((psize-1-pi) & 7) | 8] << 8) | m_ra[((psize-1-pi) & 7) | 8];
+ for(int pz = 0; pz <= m_gchr; pz++)
+ {
+ for(int ii = 0, curpixel = 0; ii < isize; ii++)
+ {
+ for(int iz = 0; iz <= m_gchr; iz++)
+ {
+ draw_pixel(x + (curpixel * x_dir[m_figs.m_dir]), y + (curpixel * y_dir[m_figs.m_dir]), ii, tile_data);
+ curpixel++;
+ }
+ }
+ if(m_figs.m_figure_type == 2)
+ {
+ x += x_dir[(m_figs.m_dir + 2) & 7];
+ y += y_dir[(m_figs.m_dir + 2) & 7];
+ }
+ else
+ {
+ x += x_dir[(m_figs.m_dir + 1) & 7];
+ y += y_dir[(m_figs.m_dir + 1) & 7];
+ }
+ }
+ }
+
+ m_ead = (x >> 4) + (y * (m_pitch >> m_figs.m_gd));
+ m_dad = (x & 0xf);
+}
+
+
+//-------------------------------------------------
+// translate_command -
+//-------------------------------------------------
+
+int upd7220_device::translate_command(UINT8 data)
+{
+ int command = COMMAND_INVALID;
+
+ switch (data)
+ {
+ case UPD7220_COMMAND_RESET: command = COMMAND_RESET; break;
+ case UPD7220_COMMAND_CCHAR: command = COMMAND_CCHAR; break;
+ case UPD7220_COMMAND_START: command = COMMAND_START; break;
+ case UPD7220_COMMAND_ZOOM: command = COMMAND_ZOOM; break;
+ case UPD7220_COMMAND_CURS: command = COMMAND_CURS; break;
+ case UPD7220_COMMAND_PITCH: command = COMMAND_PITCH; break;
+ case UPD7220_COMMAND_MASK: command = COMMAND_MASK; break;
+ case UPD7220_COMMAND_FIGS: command = COMMAND_FIGS; break;
+ case UPD7220_COMMAND_FIGD: command = COMMAND_FIGD; break;
+ case UPD7220_COMMAND_GCHRD: command = COMMAND_GCHRD; break;
+ case UPD7220_COMMAND_CURD: command = COMMAND_CURD; break;
+ case UPD7220_COMMAND_LPRD: command = COMMAND_LPRD; break;
+ case UPD7220_COMMAND_5A: command = COMMAND_5A; break;
+ default:
+ switch (data & 0xfe)
+ {
+ case UPD7220_COMMAND_SYNC: command = COMMAND_SYNC; break;
+ case UPD7220_COMMAND_VSYNC: command = COMMAND_VSYNC; break;
+ case UPD7220_COMMAND_BCTRL: command = COMMAND_BCTRL; break;
+ default:
+ switch (data & 0xf0)
+ {
+ case UPD7220_COMMAND_PRAM: command = COMMAND_PRAM; break;
+ default:
+ switch (data & 0xe4)
+ {
+ case UPD7220_COMMAND_WDAT: command = COMMAND_WDAT; break;
+ case UPD7220_COMMAND_RDAT: command = COMMAND_RDAT; break;
+ case UPD7220_COMMAND_DMAR: command = COMMAND_DMAR; break;
+ case UPD7220_COMMAND_DMAW: command = COMMAND_DMAW; break;
+ }
+ }
+ }
+ }
+
+ return command;
+}
+
+
+//-------------------------------------------------
+// process_fifo -
+//-------------------------------------------------
+
+void upd7220_device::process_fifo()
+{
+ UINT8 data;
+ int flag;
+ UINT16 eff_pitch = m_pitch >> m_figs.m_gd;
+
+ dequeue(&data, &flag);
+
+ if (flag == FIFO_COMMAND)
+ {
+ m_cr = data;
+ m_param_ptr = 1;
+ }
+ else
+ {
+ m_pr[m_param_ptr] = data;
+ m_param_ptr++;
+ }
+
+ switch (translate_command(m_cr))
+ {
+ case COMMAND_INVALID:
+ logerror("uPD7220 '%s' Invalid Command Byte %02x\n", tag(), m_cr);
+ break;
+
+ case COMMAND_5A:
+ if (m_param_ptr == 4)
+ logerror("uPD7220 '%s' Undocumented Command 0x5A Executed %02x %02x %02x\n", tag(),m_pr[1],m_pr[2],m_pr[3] );
+ break;
+
+ case COMMAND_RESET: /* reset */
+ switch (m_param_ptr)
+ {
+ case 0:
+ LOG(("uPD7220 '%s' RESET\n", tag()));
+
+ m_de = 0;
+ m_ra[0] = m_ra[1] = m_ra[2] = 0;
+ m_ra[3] = 0x19;
+ m_ead = 0;
+ m_dad = 0;
+ m_mask = 0;
+ break;
+
+ case 9:
+ m_mode = m_pr[1];
+ m_aw = m_pr[2] + 2;
+ m_hs = (m_pr[3] & 0x1f) + 1;
+ m_vs = ((m_pr[4] & 0x03) << 3) | (m_pr[3] >> 5);
+ m_hfp = (m_pr[4] >> 2) + 1;
+ m_hbp = (m_pr[5] & 0x3f) + 1;
+ m_vfp = m_pr[6] & 0x3f;
+ m_al = ((m_pr[8] & 0x03) << 8) | m_pr[7];
+ m_vbp = m_pr[8] >> 2;
+
+ m_pitch = m_aw;
+
+ LOG(("uPD7220 '%s' Mode: %02x\n", tag(), m_mode));
+ LOG(("uPD7220 '%s' AW: %u\n", tag(), m_aw));
+ LOG(("uPD7220 '%s' HS: %u\n", tag(), m_hs));
+ LOG(("uPD7220 '%s' VS: %u\n", tag(), m_vs));
+ LOG(("uPD7220 '%s' HFP: %u\n", tag(), m_hfp));
+ LOG(("uPD7220 '%s' HBP: %u\n", tag(), m_hbp));
+ LOG(("uPD7220 '%s' VFP: %u\n", tag(), m_vfp));
+ LOG(("uPD7220 '%s' AL: %u\n", tag(), m_al));
+ LOG(("uPD7220 '%s' VBP: %u\n", tag(), m_vbp));
+ LOG(("uPD7220 '%s' PITCH: %u\n", tag(), m_pitch));
+
+ recompute_parameters();
+ break;
+ }
+ break;
+
+ case COMMAND_SYNC: /* sync format specify */
+ if (m_param_ptr == 9)
+ {
+ m_mode = m_pr[1];
+ m_aw = m_pr[2] + 2;
+ m_hs = (m_pr[3] & 0x1f) + 1;
+ m_vs = ((m_pr[4] & 0x03) << 3) | (m_pr[3] >> 5);
+ m_hfp = (m_pr[4] >> 2) + 1;
+ m_hbp = (m_pr[5] & 0x3f) + 1;
+ m_vfp = m_pr[6] & 0x3f;
+ m_al = ((m_pr[8] & 0x03) << 8) | m_pr[7];
+ m_vbp = m_pr[8] >> 2;
+
+ m_pitch = m_aw;
+
+ LOG(("uPD7220 '%s' Mode: %02x\n", tag(), m_mode));
+ LOG(("uPD7220 '%s' AW: %u\n", tag(), m_aw));
+ LOG(("uPD7220 '%s' HS: %u\n", tag(), m_hs));
+ LOG(("uPD7220 '%s' VS: %u\n", tag(), m_vs));
+ LOG(("uPD7220 '%s' HFP: %u\n", tag(), m_hfp));
+ LOG(("uPD7220 '%s' HBP: %u\n", tag(), m_hbp));
+ LOG(("uPD7220 '%s' VFP: %u\n", tag(), m_vfp));
+ LOG(("uPD7220 '%s' AL: %u\n", tag(), m_al));
+ LOG(("uPD7220 '%s' VBP: %u\n", tag(), m_vbp));
+ LOG(("uPD7220 '%s' PITCH: %u\n", tag(), m_pitch));
+
+ recompute_parameters();
+ }
+ break;
+
+ case COMMAND_VSYNC: /* vertical sync mode */
+ m_m = m_cr & 0x01;
+
+ LOG(("uPD7220 '%s' M: %u\n", tag(), m_m));
+
+ recompute_parameters();
+ break;
+
+ case COMMAND_CCHAR: /* cursor & character characteristics */
+ if(m_param_ptr == 2)
+ {
+ m_lr = (m_pr[1] & 0x1f) + 1;
+ m_dc = BIT(m_pr[1], 7);
+
+ LOG(("uPD7220 '%s' LR: %u\n", tag(), m_lr));
+ LOG(("uPD7220 '%s' DC: %u\n", tag(), m_dc));
+ }
+
+ if(m_param_ptr == 3)
+ {
+ m_ctop = m_pr[2] & 0x1f;
+ m_sc = BIT(m_pr[2], 5);
+ m_br = (m_pr[2] >> 6); /* guess, assume that blink rate clears upper bits (if any) */
+
+ LOG(("uPD7220 '%s' CTOP: %u\n", tag(), m_ctop));
+ LOG(("uPD7220 '%s' SC: %u\n", tag(), m_sc));
+ }
+
+ if(m_param_ptr == 4)
+ {
+ m_br = ((m_pr[3] & 0x07) << 2) | (m_pr[2] >> 6);
+ m_cbot = m_pr[3] >> 3;
+
+ LOG(("uPD7220 '%s' BR: %u\n", tag(), m_br));
+ LOG(("uPD7220 '%s' CBOT: %u\n", tag(), m_cbot));
+ }
+ break;
+
+ case COMMAND_START: /* start display & end idle mode */
+ m_de = 1;
+
+ //LOG(("uPD7220 '%s' DE: 1\n", tag()));
+ break;
+
+ case COMMAND_BCTRL: /* display blanking control */
+ m_de = m_cr & 0x01;
+
+ //LOG(("uPD7220 '%s' DE: %u\n", tag(), m_de));
+ break;
+
+ case COMMAND_ZOOM: /* zoom factors specify */
+ if (flag == FIFO_PARAMETER)
+ {
+ m_gchr = m_pr[1] & 0x0f;
+ m_disp = m_pr[1] >> 4;
+
+ LOG(("uPD7220 '%s' GCHR: %01x\n", tag(), m_gchr));
+ LOG(("uPD7220 '%s' DISP: %01x\n", tag(), m_disp));
+ }
+ break;
+
+ case COMMAND_CURS: /* cursor position specify */
+ if (m_param_ptr >= 3)
+ {
+ UINT8 upper_addr = (m_param_ptr == 3) ? 0 : (m_pr[3] & 0x03);
+
+ m_ead = (upper_addr << 16) | (m_pr[2] << 8) | m_pr[1];
+
+ LOG(("uPD7220 '%s' EAD: %06x\n", tag(), m_ead));
+
+ if(m_param_ptr == 4)
+ {
+ m_dad = m_pr[3] >> 4;
+ LOG(("uPD7220 '%s' DAD: %01x\n", tag(), m_dad));
+ }
+ }
+ break;
+
+ case COMMAND_PRAM: /* parameter RAM load */
+ if (flag == FIFO_COMMAND)
+ {
+ m_ra_addr = m_cr & 0x0f;
+ }
+ else
+ {
+ if (m_ra_addr < 16)
+ {
+ LOG(("uPD7220 '%s' RA%u: %02x\n", tag(), m_ra_addr, data));
+
+ m_ra[m_ra_addr] = data;
+ m_ra_addr++;
+ }
+
+ m_param_ptr = 0;
+ }
+ break;
+
+ case COMMAND_PITCH: /* pitch specification */
+ if (flag == FIFO_PARAMETER)
+ {
+ m_pitch = data;
+
+ LOG(("uPD7220 '%s' PITCH: %u\n", tag(), m_pitch));
+ }
+ break;
+
+ case COMMAND_WDAT: /* write data into display memory */
+ m_bitmap_mod = m_cr & 3;
+
+ if (m_param_ptr == 3 || (m_param_ptr == 2 && m_cr & 0x10))
+ {
+ LOG(("%02x = %02x %02x (%c) %06x %04x\n",m_cr,m_pr[2],m_pr[1],m_pr[1]?m_pr[1]:' ',m_ead,m_figs.m_dc));
+ fifo_set_direction(FIFO_WRITE);
+
+ write_vram((m_cr & 0x18) >> 3,m_cr & 3);
+ reset_figs_param();
+ m_param_ptr = 1;
+ }
+ break;
+
+ case COMMAND_MASK: /* mask register load */
+ if (m_param_ptr == 3)
+ {
+ m_mask = (m_pr[2] << 8) | m_pr[1];
+
+ LOG(("uPD7220 '%s' MASK: %04x\n", tag(), m_mask));
+ }
+ break;
+
+ case COMMAND_FIGS: /* figure drawing parameters specify */
+ if (m_param_ptr == 2)
+ {
+ m_figs.m_dir = m_pr[1] & 0x7;
+ m_figs.m_figure_type = (m_pr[1] & 0xf8) >> 3;
+
+ //if(m_figs.m_dir != 2)
+ // printf("DIR %02x\n",m_pr[1]);
+ }
+
+ // the Decision Mate V during start-up test upload only 2 params before execute the
+ // RDAT command, so I assume this is the expected behaviour, but this needs to be verified.
+ if (m_param_ptr == 3)
+ m_figs.m_dc = (m_pr[2]) | (m_figs.m_dc & 0x3f00);
+
+ if (m_param_ptr == 4)
+ {
+ m_figs.m_dc = (m_pr[2]) | ((m_pr[3] & 0x3f) << 8);
+ m_figs.m_gd = (m_pr[3] & 0x40) && ((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED);
+ }
+
+ if (m_param_ptr == 6)
+ m_figs.m_d = (m_pr[4]) | ((m_pr[5] & 0x3f) << 8);
+
+ if (m_param_ptr == 8)
+ m_figs.m_d2 = (m_pr[6]) | ((m_pr[7] & 0x3f) << 8);
+
+ if (m_param_ptr == 10)
+ m_figs.m_d1 = (m_pr[8]) | ((m_pr[9] & 0x3f) << 8);
+
+ if (m_param_ptr == 12)
+ m_figs.m_dm = (m_pr[10]) | ((m_pr[11] & 0x3f) << 8);
+
+ break;
+
+ case COMMAND_FIGD: /* figure draw start */
+ if(m_figs.m_figure_type == 0)
+ draw_pixel(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch),m_dad,(m_ra[8]) | (m_ra[9]<<8));
+ else if(m_figs.m_figure_type == 1)
+ draw_line(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch));
+ else if(m_figs.m_figure_type == 4)
+ draw_arc(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch));
+ else if(m_figs.m_figure_type == 8)
+ draw_rectangle(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch));
+ else
+ logerror("uPD7220 '%s' Unimplemented command FIGD %02x\n", tag(),m_figs.m_figure_type);
+
+ reset_figs_param();
+ m_sr |= UPD7220_SR_DRAWING_IN_PROGRESS;
+ break;
+
+ case COMMAND_GCHRD: /* graphics character draw and area filling start */
+ if((m_figs.m_figure_type & 0xf) == 2)
+ draw_char(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch));
+ else
+ logerror("uPD7220 '%s' Unimplemented command GCHRD %02x\n", tag(),m_figs.m_figure_type);
+
+ reset_figs_param();
+ m_sr |= UPD7220_SR_DRAWING_IN_PROGRESS;
+ break;
+
+ case COMMAND_RDAT: /* read data from display memory */
+ fifo_set_direction(FIFO_READ);
+
+ read_vram((m_cr & 0x18) >> 3,m_cr & 3);
+
+ m_sr |= UPD7220_SR_DATA_READY;
+ break;
+
+ case COMMAND_CURD: /* cursor address read */
+ {
+ UINT16 dad = 1 << m_dad;
+ fifo_set_direction(FIFO_READ);
+
+ queue(m_ead & 0xff, 0);
+ queue((m_ead >> 8) & 0xff, 0);
+ queue(m_ead >> 16, 0);
+ queue(dad & 0xff, 0);
+ queue(dad >> 8, 0);
+
+ m_sr |= UPD7220_SR_DATA_READY;
+ break;
+ }
+
+ case COMMAND_LPRD: /* light pen address read */
+ fifo_set_direction(FIFO_READ);
+
+ queue(m_lad & 0xff, 0);
+ queue((m_lad >> 8) & 0xff, 0);
+ queue(m_lad >> 16, 0);
+
+ m_sr |= UPD7220_SR_DATA_READY;
+ m_sr &= ~UPD7220_SR_LIGHT_PEN_DETECT;
+ break;
+
+ case COMMAND_DMAR: /* DMA read request */
+ logerror("uPD7220 '%s' Unimplemented command DMAR\n", tag());
+ break;
+
+ case COMMAND_DMAW: /* DMA write request */
+ logerror("uPD7220 '%s' Unimplemented command DMAW\n", tag());
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// continue command
+//-------------------------------------------------
+
+void upd7220_device::continue_command()
+{
+ // continue RDAT command when data to read are larger than the FIFO (a5105 and dmv text scrolling)
+ if (m_figs.m_dc && translate_command(m_cr) == COMMAND_RDAT)
+ {
+ read_vram((m_cr & 0x18) >> 3, m_cr & 3);
+ m_sr |= UPD7220_SR_DATA_READY;
+ }
+}
+
+
+//-------------------------------------------------
+// read -
+//-------------------------------------------------
+
+READ8_MEMBER( upd7220_device::read )
+{
+ UINT8 data;
+
+ if (offset & 1)
+ {
+ /* FIFO read */
+ int flag;
+ fifo_set_direction(FIFO_READ);
+ dequeue(&data, &flag);
+
+ continue_command();
+ }
+ else
+ {
+ /* status register */
+ data = m_sr;
+
+ /* TODO: timing of these */
+ m_sr &= ~UPD7220_SR_DRAWING_IN_PROGRESS;
+ m_sr &= ~UPD7220_SR_DMA_EXECUTE;
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write -
+//-------------------------------------------------
+
+WRITE8_MEMBER( upd7220_device::write )
+{
+ if (offset & 1)
+ {
+ /* command into FIFO */
+ fifo_set_direction(FIFO_WRITE);
+ queue(data, 1);
+ }
+ else
+ {
+ /* parameter into FIFO */
+// fifo_set_direction(FIFO_WRITE);
+ queue(data, 0);
+ }
+
+ process_fifo();
+}
+
+
+//-------------------------------------------------
+// dack_r -
+//-------------------------------------------------
+
+READ8_MEMBER( upd7220_device::dack_r )
+{
+ return 0;
+}
+
+
+//-------------------------------------------------
+// dack_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( upd7220_device::dack_w )
+{
+}
+
+
+//-------------------------------------------------
+// ext_sync_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( upd7220_device::ext_sync_w )
+{
+ //LOG(("uPD7220 '%s' External Synchronization: %u\n", tag(), state));
+
+ if (state)
+ {
+ m_sr |= UPD7220_SR_VSYNC_ACTIVE;
+ }
+ else
+ {
+ m_sr &= ~UPD7220_SR_VSYNC_ACTIVE;
+ }
+}
+
+
+//-------------------------------------------------
+// ext_sync_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( upd7220_device::lpen_w )
+{
+ /* only if 2 rising edges on the lpen input occur at the same
+ point during successive video fields are the pulses accepted */
+
+ /*
+
+ 1. compute the address of the location on the CRT
+ 2. compare with LAD
+ 3. if not equal move address to LAD
+ 4. if equal set LPEN DETECT flag to 1
+
+ */
+}
+
+
+//-------------------------------------------------
+// update_text -
+//-------------------------------------------------
+
+void upd7220_device::update_text(bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT32 addr, sad;
+ UINT16 len;
+ int im, wd;
+ int y, sy = 0;
+
+ for (int area = 0; area < 4; area++)
+ {
+ get_text_partition(area, &sad, &len, &im, &wd);
+
+ for (y = sy; y < sy + len; y++)
+ {
+ addr = sad + (y * m_pitch);
+
+ if (!m_draw_text_cb.isnull())
+ m_draw_text_cb(bitmap, addr, (y * m_lr) + m_vbp, wd, m_pitch, m_lr, m_dc, m_ead);
+ }
+
+ sy = y + 1;
+ }
+}
+
+
+//-------------------------------------------------
+// draw_graphics_line -
+//-------------------------------------------------
+
+void upd7220_device::draw_graphics_line(bitmap_rgb32 &bitmap, UINT32 addr, int y, int wd, int pitch)
+{
+ int sx, al = bitmap.cliprect().height();
+
+ for (sx = 0; sx < pitch; sx++)
+ {
+ if((sx << 4) < m_aw * 16 && y < al)
+ m_display_cb(bitmap, y, sx << 4, addr);
+
+ addr+= (wd + 1) * 2;
+ }
+}
+
+
+//-------------------------------------------------
+// update_graphics -
+//-------------------------------------------------
+
+void upd7220_device::update_graphics(bitmap_rgb32 &bitmap, const rectangle &cliprect, int force_bitmap)
+{
+ UINT32 addr, sad;
+ UINT16 len;
+ int im, wd, area;
+ int y = 0, tsy = 0, bsy = 0;
+ bool mixed = ((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED);
+ UINT8 interlace = ((m_mode & UPD7220_MODE_INTERLACE_MASK) == UPD7220_MODE_INTERLACE_ON) ? 0 : 1;
+
+ for (area = 0; area < 4; area++)
+ {
+ get_graphics_partition(area, &sad, &len, &im, &wd);
+
+ if (im || force_bitmap)
+ {
+ //get_graphics_partition(area, &sad, &len, &im, &wd);
+
+ if(area >= 3) // TODO: most likely to be correct, Quarth (PC-98xx) definitely draws with area 2. We might see an area 3 someday ...
+ break;
+
+ if(!interlace)
+ len <<= 1;
+
+ for (y = 0; y < len; y++)
+ {
+ /* TODO: again correct?
+ Quarth (PC-98xx) doesn't seem to use pitch here and it definitely wants bsy to be /2 to make scrolling to work.
+ Xevious (PC-98xx) wants the pitch to be fixed at 80, and wants bsy to be /1
+ Dragon Buster (PC-98xx) contradicts with Xevious with regards of the pitch tho ... */
+ addr = ((sad << 1) & 0x3ffff) + (y * (m_pitch << (im ? 0 : 1)));
+
+ if (!m_display_cb.isnull())
+ draw_graphics_line(bitmap, addr, y + ((bsy + m_vbp) / (mixed ? 1 : m_lr)), wd, (m_pitch << interlace));
+ }
+ }
+ else
+ {
+ get_text_partition(area, &sad, &len, &im, &wd);
+
+ if(m_lr)
+ {
+ for (y = 0; y < len; y+=m_lr)
+ {
+ addr = (sad & 0x3ffff) + ((y / m_lr) * m_pitch);
+
+ if (!m_draw_text_cb.isnull())
+ m_draw_text_cb(bitmap, addr, y + tsy + m_vbp, wd, m_pitch, m_lr, m_dc, m_ead);
+ }
+ }
+ }
+
+ if (m_lr)
+ tsy += y;
+ bsy += y;
+ }
+}
+
+
+//-------------------------------------------------
+// update_screen -
+//-------------------------------------------------
+
+UINT32 upd7220_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ if (m_de)
+ {
+ switch (m_mode & UPD7220_MODE_DISPLAY_MASK)
+ {
+ case UPD7220_MODE_DISPLAY_MIXED:
+ update_graphics(bitmap, cliprect, 0);
+ break;
+
+ case UPD7220_MODE_DISPLAY_GRAPHICS:
+ update_graphics(bitmap, cliprect, 1);
+ break;
+
+ case UPD7220_MODE_DISPLAY_CHARACTER:
+ update_text(bitmap, cliprect);
+ break;
+
+ case UPD7220_MODE_DISPLAY_INVALID:
+ LOG(("uPD7220 '%s' Invalid Display Mode!\n", tag()));
+ }
+ }
+ return 0;
+}
diff --git a/src/devices/video/upd7220.h b/src/devices/video/upd7220.h
new file mode 100644
index 00000000000..4a8be071cac
--- /dev/null
+++ b/src/devices/video/upd7220.h
@@ -0,0 +1,234 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese, Miodrag Milanovic, Carl
+/**********************************************************************
+
+ NEC uPD7220 Graphics Display Controller emulation
+
+**********************************************************************
+ _____ _____
+ 2xWCLK 1 |* \_/ | 40 Vcc
+ _DBIN 2 | | 39 A17
+ HSYNC 3 | | 38 A16
+ V/EXT SYNC 4 | | 37 AD15
+ BLANK 5 | | 36 AD14
+ ALE 6 | | 35 AD13
+ DRQ 7 | | 34 AD12
+ _DACK 8 | | 33 AD11
+ _RD 9 | | 32 AD10
+ _WR 10 | uPD7220 | 31 AD9
+ A0 11 | 82720 | 30 AD8
+ DB0 12 | | 29 AD7
+ DB1 13 | | 28 AD6
+ DB2 14 | | 27 AD5
+ DB3 15 | | 26 AD4
+ DB4 16 | | 25 AD3
+ DB5 17 | | 24 AD2
+ DB6 18 | | 23 AD1
+ DB7 19 | | 22 AD0
+ GND 20 |_____________| 21 LPEN
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __UPD7220__
+#define __UPD7220__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define UPD7220_DISPLAY_PIXELS_MEMBER(_name) void _name(bitmap_rgb32 &bitmap, int y, int x, UINT32 address)
+#define UPD7220_DRAW_TEXT_LINE_MEMBER(_name) void _name(bitmap_rgb32 &bitmap, UINT32 addr, int y, int wd, int pitch, int lr, int cursor_on, int cursor_addr)
+
+
+#define MCFG_UPD7220_DISPLAY_PIXELS_CALLBACK_OWNER(_class, _method) \
+ upd7220_device::static_set_display_pixels_callback(*device, upd7220_display_pixels_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_UPD7220_DRAW_TEXT_CALLBACK_OWNER(_class, _method) \
+ upd7220_device::static_set_draw_text_callback(*device, upd7220_draw_text_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_UPD7220_DRQ_CALLBACK(_write) \
+ devcb = &upd7220_device::set_drq_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_UPD7220_HSYNC_CALLBACK(_write) \
+ devcb = &upd7220_device::set_hsync_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_UPD7220_VSYNC_CALLBACK(_write) \
+ devcb = &upd7220_device::set_vsync_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_UPD7220_BLANK_CALLBACK(_write) \
+ devcb = &upd7220_device::set_blank_wr_callback(*device, DEVCB_##_write);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef device_delegate<void (bitmap_rgb32 &bitmap, int y, int x, UINT32 address)> upd7220_display_pixels_delegate;
+typedef device_delegate<void (bitmap_rgb32 &bitmap, UINT32 addr, int y, int wd, int pitch, int lr, int cursor_on, int cursor_addr)> upd7220_draw_text_delegate;
+
+
+// ======================> upd7220_device
+
+class upd7220_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ upd7220_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_display_pixels_callback(device_t &device, upd7220_display_pixels_delegate callback) { downcast<upd7220_device &>(device).m_display_cb = callback; }
+ static void static_set_draw_text_callback(device_t &device, upd7220_draw_text_delegate callback) { downcast<upd7220_device &>(device).m_draw_text_cb = callback; }
+
+ template<class _Object> static devcb_base &set_drq_wr_callback(device_t &device, _Object object) { return downcast<upd7220_device &>(device).m_write_drq.set_callback(object); }
+ template<class _Object> static devcb_base &set_hsync_wr_callback(device_t &device, _Object object) { return downcast<upd7220_device &>(device).m_write_hsync.set_callback(object); }
+ template<class _Object> static devcb_base &set_vsync_wr_callback(device_t &device, _Object object) { return downcast<upd7220_device &>(device).m_write_vsync.set_callback(object); }
+ template<class _Object> static devcb_base &set_blank_wr_callback(device_t &device, _Object object) { return downcast<upd7220_device &>(device).m_write_blank.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_READ8_MEMBER( dack_r );
+ DECLARE_WRITE8_MEMBER( dack_w );
+
+ DECLARE_WRITE_LINE_MEMBER( ext_sync_w );
+ DECLARE_WRITE_LINE_MEMBER( lpen_w );
+
+ DECLARE_WRITE8_MEMBER( bank_w );
+ DECLARE_READ8_MEMBER( vram_r );
+ DECLARE_WRITE8_MEMBER( vram_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ virtual const rom_entry *device_rom_region() const;
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ enum
+ {
+ TIMER_VSYNC,
+ TIMER_HSYNC,
+ TIMER_BLANK
+ };
+
+ inline UINT8 readbyte(offs_t address);
+ inline void writebyte(offs_t address, UINT8 data);
+ inline UINT16 readword(offs_t address);
+ inline void writeword(offs_t address, UINT16 data);
+ inline void fifo_clear();
+ inline int fifo_param_count();
+ inline void fifo_set_direction(int dir);
+ inline void queue(UINT8 data, int flag);
+ inline void dequeue(UINT8 *data, int *flag);
+ inline void update_vsync_timer(int state);
+ inline void update_hsync_timer(int state);
+ inline void update_blank_timer(int state);
+ inline void recompute_parameters();
+ inline void reset_figs_param();
+ inline void read_vram(UINT8 type, UINT8 mod);
+ inline void write_vram(UINT8 type, UINT8 mod);
+ inline void get_text_partition(int index, UINT32 *sad, UINT16 *len, int *im, int *wd);
+ inline void get_graphics_partition(int index, UINT32 *sad, UINT16 *len, int *im, int *wd);
+
+ void draw_pixel(int x, int y, int xi, UINT16 tile_data);
+ void draw_line(int x, int y);
+ void draw_rectangle(int x, int y);
+ void draw_arc(int x, int y);
+ void draw_char(int x, int y);
+ int translate_command(UINT8 data);
+ void process_fifo();
+ void continue_command();
+ void update_text(bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void draw_graphics_line(bitmap_rgb32 &bitmap, UINT32 addr, int y, int wd, int pitch);
+ void update_graphics(bitmap_rgb32 &bitmap, const rectangle &cliprect, int force_bitmap);
+
+ upd7220_display_pixels_delegate m_display_cb;
+ upd7220_draw_text_delegate m_draw_text_cb;
+
+ devcb_write_line m_write_drq;
+ devcb_write_line m_write_hsync;
+ devcb_write_line m_write_vsync;
+ devcb_write_line m_write_blank;
+
+ UINT16 m_mask; // mask register
+ UINT8 m_pitch; // number of word addresses in display memory in the horizontal direction
+ UINT32 m_ead; // execute word address
+ UINT16 m_dad; // dot address within the word
+ UINT32 m_lad; // light pen address
+
+ UINT8 m_ra[16]; // parameter RAM
+ int m_ra_addr; // parameter RAM address
+
+ UINT8 m_sr; // status register
+ UINT8 m_cr; // command register
+ UINT8 m_pr[17]; // parameter byte register
+ int m_param_ptr; // parameter pointer
+
+ UINT8 m_fifo[16]; // FIFO data queue
+ int m_fifo_flag[16]; // FIFO flag queue
+ int m_fifo_ptr; // FIFO pointer
+ int m_fifo_dir; // FIFO direction
+
+ UINT8 m_mode; // mode of operation
+
+ int m_de; // display enabled
+ int m_m; // 0 = accept external vertical sync (slave mode) / 1 = generate & output vertical sync (master mode)
+ int m_aw; // active display words per line - 2 (must be even number with bit 0 = 0)
+ int m_al; // active display lines per video field
+ int m_vs; // vertical sync width - 1
+ int m_vfp; // vertical front porch width - 1
+ int m_vbp; // vertical back porch width - 1
+ int m_hs; // horizontal sync width - 1
+ int m_hfp; // horizontal front porch width - 1
+ int m_hbp; // horizontal back porch width - 1
+
+ int m_dc; // display cursor
+ int m_sc; // 0 = blinking cursor / 1 = steady cursor
+ int m_br; // blink rate
+ int m_ctop; // cursor top line number in the row
+ int m_cbot; // cursor bottom line number in the row (CBOT < LR)
+ int m_lr; // lines per character row - 1
+
+ int m_disp; // display zoom factor
+ int m_gchr; // zoom factor for graphics character writing and area filling
+
+ UINT8 m_bitmap_mod;
+
+ struct {
+ UINT8 m_dir; // figs param 0: drawing direction
+ UINT8 m_figure_type; // figs param 1: figure type
+ UINT16 m_dc; // figs param 2:
+ UINT8 m_gd; // mixed mode only
+ UINT16 m_d; // figs param 3:
+ UINT16 m_d1; // figs param 4:
+ UINT16 m_d2; // figs param 5:
+ UINT16 m_dm; // figs param 6:
+ } m_figs;
+
+ // timers
+ emu_timer *m_vsync_timer; // vertical sync timer
+ emu_timer *m_hsync_timer; // horizontal sync timer
+ emu_timer *m_blank_timer; // CRT blanking timer
+
+ const address_space_config m_space_config;
+};
+
+
+// device type definition
+extern const device_type UPD7220;
+
+
+
+#endif
diff --git a/src/devices/video/upd7227.c b/src/devices/video/upd7227.c
new file mode 100644
index 00000000000..037dd037610
--- /dev/null
+++ b/src/devices/video/upd7227.c
@@ -0,0 +1,162 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ uPD7227 Intelligent Dot-Matrix LCD Controller/Driver emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "upd7227.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type UPD7227 = &device_creator<upd7227_device>;
+
+
+static ADDRESS_MAP_START( upd7227_map, AS_PROGRAM, 8, upd7227_device )
+ AM_RANGE(0x00, 0x27) AM_RAM
+ AM_RANGE(0x40, 0x67) AM_RAM
+ADDRESS_MAP_END
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// upd7227_device - constructor
+//-------------------------------------------------
+
+upd7227_device::upd7227_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, UPD7227, "uPD7227", tag, owner, clock, "upd7227", __FILE__),
+ device_memory_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_BIG, 8, 7, 0, *ADDRESS_MAP_NAME(upd7227_map)),
+ m_cs(1),
+ m_cd(1),
+ m_sck(1),
+ m_si(1),
+ m_so(1)
+{
+}
+
+
+//-------------------------------------------------
+// static_set_offsets - configuration helper
+//-------------------------------------------------
+
+void upd7227_device::static_set_offsets(device_t &device, int sx, int sy)
+{
+ upd7227_device &upd7227 = downcast<upd7227_device &>(device);
+
+ upd7227.m_sx = sx;
+ upd7227.m_sy = sy;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void upd7227_device::device_start()
+{
+ // state saving
+ save_item(NAME(m_cs));
+ save_item(NAME(m_cd));
+ save_item(NAME(m_sck));
+ save_item(NAME(m_si));
+ save_item(NAME(m_so));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void upd7227_device::device_reset()
+{
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *upd7227_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// update_screen - update screen
+//-------------------------------------------------
+
+UINT32 upd7227_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ return 0;
+}
+
+
+//-------------------------------------------------
+// cs_w - chip select
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( upd7227_device::cs_w )
+{
+ m_cs = state;
+}
+
+
+//-------------------------------------------------
+// cd_w - command/data select
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( upd7227_device::cd_w )
+{
+ m_cd = state;
+}
+
+
+//-------------------------------------------------
+// sck_w - serial clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( upd7227_device::sck_w )
+{
+ m_sck = state;
+}
+
+
+//-------------------------------------------------
+// si_w - serial input
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( upd7227_device::si_w )
+{
+ m_si = state;
+}
+
+
+//-------------------------------------------------
+// so_r - serial output/busy
+//-------------------------------------------------
+
+READ_LINE_MEMBER( upd7227_device::so_r )
+{
+ return m_so;
+}
diff --git a/src/devices/video/upd7227.h b/src/devices/video/upd7227.h
new file mode 100644
index 00000000000..19d04224d1c
--- /dev/null
+++ b/src/devices/video/upd7227.h
@@ -0,0 +1,95 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ uPD7227 Intelligent Dot-Matrix LCD Controller/Driver emulation
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __UPD7227__
+#define __UPD7227__
+
+#include "emu.h"
+
+
+
+///*************************************************************************
+// INTERFACE CONFIGURATION MACROS
+///*************************************************************************
+
+#define MCFG_UPD7227_ADD(_tag, _sx, _sy) \
+ MCFG_DEVICE_ADD(_tag, UPD7227, 0) \
+ upd7227_device::static_set_offsets(*device, _sx, _sy);
+
+
+
+///*************************************************************************
+// TYPE DEFINITIONS
+///*************************************************************************
+
+// ======================> upd7227_device
+
+class upd7227_device : public device_t,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ upd7227_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // inline configuration helpers
+ static void static_set_offsets(device_t &device, int sx, int sy);
+
+ DECLARE_WRITE_LINE_MEMBER( cs_w );
+ DECLARE_WRITE_LINE_MEMBER( cd_w );
+ DECLARE_WRITE_LINE_MEMBER( sck_w );
+ DECLARE_WRITE_LINE_MEMBER( si_w );
+ DECLARE_READ_LINE_MEMBER( so_r );
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ address_space_config m_space_config;
+
+private:
+ enum
+ {
+ CMD_SMM = 0x18,
+ CMD_SFF = 0x10,
+ CMD_LDPI = 0x80,
+ CMD_SWM = 0x64,
+ CMD_SRM = 0x60,
+ CMD_SANDM = 0x6c,
+ CMD_SORM = 0x68,
+ CMD_SCM = 0x72,
+ CMD_BSET = 0x40,
+ CMD_BRESET = 0x20,
+ CMD_DISP_ON = 0x09,
+ CMD_DISP_OFF = 0x08
+ };
+
+ int m_sx;
+ int m_sy;
+
+ int m_cs;
+ int m_cd;
+ int m_sck;
+ int m_si;
+ int m_so;
+};
+
+
+// device type definition
+extern const device_type UPD7227;
+
+
+
+#endif
diff --git a/src/devices/video/v9938.c b/src/devices/video/v9938.c
new file mode 100644
index 00000000000..3192593e76a
--- /dev/null
+++ b/src/devices/video/v9938.c
@@ -0,0 +1,3012 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Nathan Woods
+
+/***************************************************************************
+
+v9938 / v9958 emulation
+
+***************************************************************************/
+
+/*
+todo:
+
+- sprite collision
+- vdp engine -- make run at correct speed
+- vr/hr/fh flags: double-check all of that
+- make vdp engine work in exp. ram
+*/
+
+#include "emu.h"
+#include "v9938.h"
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+enum
+{
+ V9938_MODE_TEXT1 = 0,
+ V9938_MODE_MULTI,
+ V9938_MODE_GRAPHIC1,
+ V9938_MODE_GRAPHIC2,
+ V9938_MODE_GRAPHIC3,
+ V9938_MODE_GRAPHIC4,
+ V9938_MODE_GRAPHIC5,
+ V9938_MODE_GRAPHIC6,
+ V9938_MODE_GRAPHIC7,
+ V9938_MODE_TEXT2,
+ V9938_MODE_UNKNOWN
+};
+
+#define MODEL_V9938 (0)
+#define MODEL_V9958 (1)
+
+#define EXPMEM_OFFSET 0x20000
+
+#define LONG_WIDTH (512 + 32)
+
+static const char *const v9938_modes[] = {
+ "TEXT 1", "MULTICOLOR", "GRAPHIC 1", "GRAPHIC 2", "GRAPHIC 3",
+ "GRAPHIC 4", "GRAPHIC 5", "GRAPHIC 6", "GRAPHIC 7", "TEXT 2",
+ "UNKNOWN"
+};
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+/*
+Similar to the TMS9928, the V9938 has an own address space. It can handle
+at most 192 KiB RAM (128 KiB base, 64 KiB expansion).
+*/
+static ADDRESS_MAP_START(memmap, AS_DATA, 8, v99x8_device)
+ADDRESS_MAP_GLOBAL_MASK(0x3ffff)
+AM_RANGE(0x00000, 0x2ffff) AM_RAM
+ADDRESS_MAP_END
+
+
+// devices
+const device_type V9938 = &device_creator<v9938_device>;
+const device_type V9958 = &device_creator<v9958_device>;
+
+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)
+: device_t(mconfig, type, name, tag, owner, clock, shortname, __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_space_config("vram", ENDIANNESS_BIG, 8, 18),
+ m_model(0),
+ m_offset_x(0),
+ m_offset_y(0),
+ m_visible_y(0),
+ m_mode(0),
+ m_pal_write_first(0),
+ m_cmd_write_first(0),
+ m_pal_write(0),
+ m_cmd_write(0),
+ m_read_ahead(0),
+ m_v9958_sp_mode(0),
+ m_address_latch(0),
+ m_vram_size(0),
+ m_int_state(0),
+ m_int_callback(*this),
+ m_scanline(0),
+ m_blink(0),
+ m_blink_count(0),
+ m_mx_delta(0),
+ m_my_delta(0),
+ m_button_state(0),
+ m_vdp_ops_count(0),
+ m_vdp_engine(NULL),
+ m_palette(*this, "palette")
+{
+ static_set_addrmap(*this, AS_DATA, ADDRESS_MAP_NAME(memmap));
+}
+
+v9938_device::v9938_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+: v99x8_device(mconfig, V9938, "V9938 VDP", "v9938", tag, owner, clock)
+{
+ m_model = MODEL_V9938;
+}
+
+v9958_device::v9958_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+: v99x8_device(mconfig, V9938, "V9958 VDP", "v9958", tag, owner, clock)
+{
+ m_model = MODEL_V9958;
+}
+
+
+int v99x8_device::interrupt ()
+{
+ int scanline, max, pal, scanline_start;
+
+ update_command ();
+
+ pal = m_cont_reg[9] & 2;
+ if (pal) scanline_start = 53; else scanline_start = 22;
+
+ // set flags
+ if (m_scanline == (m_offset_y + scanline_start) )
+ {
+ m_stat_reg[2] &= ~0x40;
+ }
+ else if (m_scanline == (m_offset_y + m_visible_y + scanline_start) )
+ {
+ m_stat_reg[2] |= 0x40;
+ m_stat_reg[0] |= 0x80;
+ }
+
+ max = (pal) ? 255 : (m_cont_reg[9] & 0x80) ? 234 : 244;
+ scanline = (m_scanline - scanline_start - m_offset_y);
+ if ( (scanline >= 0) && (scanline <= max) &&
+ ( ( (scanline + m_cont_reg[23]) & 255) == m_cont_reg[19]) )
+ {
+ m_stat_reg[1] |= 1;
+ LOG(("V9938: scanline interrupt (%d)\n", scanline));
+ }
+ else
+ if ( !(m_cont_reg[0] & 0x10) ) m_stat_reg[1] &= 0xfe;
+
+ check_int ();
+
+ // check for start of vblank
+ if ((pal && (m_scanline == 310)) ||
+ (!pal && (m_scanline == 259)))
+ interrupt_start_vblank ();
+
+ // render the current line
+ if ((m_scanline >= scanline_start) && (m_scanline < (212 + 28 + scanline_start)))
+ {
+ scanline = (m_scanline - scanline_start) & 255;
+
+ refresh_line (scanline);
+ }
+
+ max = (m_cont_reg[9] & 2) ? 313 : 262;
+ if (++m_scanline >= max)
+ m_scanline = 0;
+
+ return m_int_state;
+}
+
+/*
+ Not really right... won't work with sprites in graphics 7
+ and with palette updated mid-screen
+*/
+int v99x8_device::get_transpen()
+{
+ if (m_mode == V9938_MODE_GRAPHIC7)
+ {
+ return m_pal_ind256[0];
+ }
+ else
+ {
+ return m_pal_ind16[0];
+ }
+}
+
+/*
+ Driver-specific function: update the vdp mouse state
+*/
+void v99x8_device::update_mouse_state(int mx_delta, int my_delta, int button_state)
+{
+ // save button state
+ m_button_state = (button_state << 6) & 0xc0;
+
+ if ((m_cont_reg[8] & 0xc0) == 0x80)
+ { // vdp will process mouse deltas only if it is in mouse mode
+ m_mx_delta += mx_delta;
+ m_my_delta += my_delta;
+ }
+}
+
+
+
+/***************************************************************************
+
+Palette functions
+
+***************************************************************************/
+
+/*
+About the colour burst registers:
+
+The color burst registers will only have effect on the composite video output from
+the V9938. but the output is only NTSC (Never The Same Color ,so the
+effects are already present) . this system is not used in europe
+the european machines use a separate PAL (Phase Alternating Line) encoder
+or no encoder at all , only RGB output.
+
+Erik de Boer.
+
+--
+Right now they're not emulated. For completeness sake they should -- with
+a dip-switch to turn them off. I really don't know how they work though. :(
+*/
+
+/*
+In screen 8, the colors are encoded as:
+
+7 6 5 4 3 2 1 0
++--+--+--+--+--+--+--+--+
+|g2|g1|g0|r2|r1|r0|b2|b1|
++--+--+--+--+--+--+--+--+
+
+b0 is set if b2 and b1 are set (remember, color bus is 3 bits)
+
+*/
+
+PALETTE_INIT_MEMBER(v9938_device, v9938)
+{
+ int i;
+
+ // create the full 512 colour palette
+ for (i=0;i<512;i++)
+ palette.set_pen_color(i, pal3bit(i >> 6), pal3bit(i >> 3), pal3bit(i >> 0));
+}
+
+/*
+
+The v9958 can display up to 19286 colours. For this we need a larger palette.
+
+The colours are encoded in 17 bits; however there are just 19268 different colours.
+Here we calculate the palette and a 2^17 reference table to the palette,
+which is: s_pal_indYJK. It's 256K in size, but I can't think of a faster way
+to emulate this. Also it keeps the palette a reasonable size. :)
+
+*/
+
+UINT16 v99x8_device::s_pal_indYJK[0x20000];
+
+PALETTE_INIT_MEMBER(v9958_device, v9958)
+{
+ int r,g,b,y,j,k,i,k0,j0,n;
+ UINT8 pal[19268*3];
+
+ // init v9938 512-color palette
+ for (i=0;i<512;i++)
+ palette.set_pen_color(i, pal3bit(i >> 6), pal3bit(i >> 3), pal3bit(i >> 0));
+
+
+ if(palette.entries() != 19780)
+ fatalerror("V9958: not enough palette, must be 19780");
+
+ // set up YJK table
+ LOG(("Building YJK table for V9958 screens, may take a while ... \n"));
+ i = 0;
+ for (y=0;y<32;y++) for (k=0;k<64;k++) for (j=0;j<64;j++)
+ {
+ // calculate the color
+ if (k >= 32) k0 = (k - 64); else k0 = k;
+ if (j >= 32) j0 = (j - 64); else j0 = j;
+ r = y + j0;
+ b = (y * 5 - 2 * j0 - k0) / 4;
+ g = y + k0;
+ if (r < 0) r = 0; else if (r > 31) r = 31;
+ if (g < 0) g = 0; else if (g > 31) g = 31;
+ if (b < 0) b = 0; else if (b > 31) b = 31;
+
+ //r = (r << 3) | (r >> 2);
+ //b = (b << 3) | (b >> 2);
+ //g = (g << 3) | (g >> 2);
+ // have we seen this one before?
+ n = 0;
+ while (n < i)
+ {
+ if (pal[n*3+0] == r && pal[n*3+1] == g && pal[n*3+2] == b)
+ {
+ v99x8_device::s_pal_indYJK[y | j << 5 | k << (5 + 6)] = n + 512;
+ break;
+ }
+ n++;
+ }
+
+ if (i == n)
+ {
+ // so we haven't; add it
+ pal[i*3+0] = r;
+ pal[i*3+1] = g;
+ pal[i*3+2] = b;
+ palette.set_pen_color(i+512, rgb_t(pal5bit(r), pal5bit(g), pal5bit(b)));
+ v99x8_device::s_pal_indYJK[y | j << 5 | k << (5 + 6)] = i + 512;
+ i++;
+ }
+ }
+
+ if (i != 19268)
+ LOG( ("Table creation failed - %d colours out of 19286 created\n", i));
+}
+
+UINT32 v99x8_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+READ8_MEMBER( v99x8_device::read )
+{
+ switch (offset & 3)
+ {
+ case 0: return vram_r();
+ case 1: return status_r();
+ }
+ return 0xff;
+}
+
+WRITE8_MEMBER( v99x8_device::write )
+{
+ switch (offset & 3)
+ {
+ case 0: vram_w(data); break;
+ case 1: command_w(data); break;
+ case 2: palette_w(data); break;
+ case 3: register_w(data); break;
+ }
+}
+
+UINT8 v99x8_device::vram_r()
+{
+ UINT8 ret;
+ int address;
+
+ address = ((int)m_cont_reg[14] << 14) | m_address_latch;
+
+ m_cmd_write_first = 0;
+
+ ret = m_read_ahead;
+
+ if (m_cont_reg[45] & 0x40) // Expansion memory
+ {
+ if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
+ address >>= 1; // correct?
+ // Expansion memory only offers 64 K
+ if (m_vram_size > 0x20000 && ((address & 0x10000)==0))
+ m_read_ahead = m_vram_space->read_byte(address + EXPMEM_OFFSET);
+ else
+ m_read_ahead = 0xff;
+ }
+ else
+ {
+ m_read_ahead = vram_read(address);
+ }
+
+ m_address_latch = (m_address_latch + 1) & 0x3fff;
+ if ((!m_address_latch) && (m_cont_reg[0] & 0x0c) ) // correct ???
+ {
+ m_cont_reg[14] = (m_cont_reg[14] + 1) & 7;
+ }
+
+ return ret;
+}
+
+UINT8 v99x8_device::status_r()
+{
+ int reg;
+ UINT8 ret;
+
+ m_cmd_write_first = 0;
+
+ reg = m_cont_reg[15] & 0x0f;
+ if (reg > 9)
+ return 0xff;
+
+ switch (reg)
+ {
+ case 0:
+ ret = m_stat_reg[0];
+ m_stat_reg[0] &= 0x1f;
+ break;
+ case 1:
+ ret = m_stat_reg[1];
+ m_stat_reg[1] &= 0xfe;
+ if ((m_cont_reg[8] & 0xc0) == 0x80)
+ // mouse mode: add button state
+ ret |= m_button_state & 0xc0;
+ break;
+ case 2:
+ /*update_command ();*/
+ /*
+ WTF is this? Whatever this was intended to do, it is nonsensical.
+ Might as well pick a random number....
+ This was an attempt to emulate H-Blank flag ;)
+ n = cycles_currently_ran ();
+ if ( (n < 28) || (n > 199) ) vdp.statReg[2] |= 0x20;
+ else vdp.statReg[2] &= ~0x20;
+ */
+ if (machine().rand() & 1) m_stat_reg[2] |= 0x20;
+ else m_stat_reg[2] &= ~0x20;
+ ret = m_stat_reg[2];
+ break;
+ case 3:
+ if ((m_cont_reg[8] & 0xc0) == 0x80)
+ { // mouse mode: return x mouse delta
+ ret = m_mx_delta;
+ m_mx_delta = 0;
+ }
+ else
+ ret = m_stat_reg[3];
+ break;
+ case 5:
+ if ((m_cont_reg[8] & 0xc0) == 0x80)
+ { // mouse mode: return y mouse delta
+ ret = m_my_delta;
+ m_my_delta = 0;
+ }
+ else
+ ret = m_stat_reg[5];
+ break;
+ case 7:
+ ret = m_stat_reg[7];
+ m_stat_reg[7] = m_cont_reg[44] = vdp_to_cpu () ;
+ break;
+ default:
+ ret = m_stat_reg[reg];
+ break;
+ }
+
+ LOG(("V9938: Read %02x from S#%d\n", ret, reg));
+ check_int ();
+
+ return ret;
+}
+
+void v99x8_device::palette_w(UINT8 data)
+{
+ int indexp;
+
+ if (m_pal_write_first)
+ {
+ // store in register
+ indexp = m_cont_reg[0x10] & 15;
+ m_pal_reg[indexp*2] = m_pal_write & 0x77;
+ m_pal_reg[indexp*2+1] = data & 0x07;
+ // update palette
+ m_pal_ind16[indexp] = (((int)m_pal_write << 2) & 0x01c0) |
+ (((int)data << 3) & 0x0038) |
+ ((int)m_pal_write & 0x0007);
+
+ m_cont_reg[0x10] = (m_cont_reg[0x10] + 1) & 15;
+ m_pal_write_first = 0;
+ }
+ else
+ {
+ m_pal_write = data;
+ m_pal_write_first = 1;
+ }
+}
+
+void v99x8_device::vram_w(UINT8 data)
+{
+ int address;
+
+ /*update_command ();*/
+
+ m_cmd_write_first = 0;
+
+ address = ((int)m_cont_reg[14] << 14) | m_address_latch;
+
+ if (m_cont_reg[45] & 0x40)
+ {
+ if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
+ address >>= 1; // correct?
+ if (m_vram_size > 0x20000 && ((address & 0x10000)==0))
+ m_vram_space->write_byte(EXPMEM_OFFSET + address, data);
+ }
+ else
+ {
+ vram_write(address, data);
+ }
+
+ m_address_latch = (m_address_latch + 1) & 0x3fff;
+ if ((!m_address_latch) && (m_cont_reg[0] & 0x0c) ) // correct ???
+ {
+ m_cont_reg[14] = (m_cont_reg[14] + 1) & 7;
+ }
+}
+
+void v99x8_device::command_w(UINT8 data)
+{
+ if (m_cmd_write_first)
+ {
+ if (data & 0x80)
+ {
+ if (!(data & 0x40))
+ register_write (data & 0x3f, m_cmd_write);
+ }
+ else
+ {
+ m_address_latch =
+ (((UINT16)data << 8) | m_cmd_write) & 0x3fff;
+ if ( !(data & 0x40) ) vram_r (); // read ahead!
+ }
+
+ m_cmd_write_first = 0;
+ }
+ else
+ {
+ m_cmd_write = data;
+ m_cmd_write_first = 1;
+ }
+}
+
+void v99x8_device::register_w(UINT8 data)
+{
+ int reg;
+
+ reg = m_cont_reg[17] & 0x3f;
+ if (reg != 17)
+ register_write(reg, data); // true ?
+
+ if (!(m_cont_reg[17] & 0x80))
+ m_cont_reg[17] = (m_cont_reg[17] + 1) & 0x3f;
+}
+
+void v99x8_device::static_set_vram_size(device_t &device, UINT32 vram_size)
+{
+ downcast<v99x8_device &>(device).m_vram_size = vram_size;
+}
+
+/***************************************************************************
+
+ Init/stop/reset/Interrupt functions
+
+***************************************************************************/
+
+void v99x8_device::device_start()
+{
+ m_int_callback.resolve_safe();
+ m_vdp_ops_count = 1;
+ m_vdp_engine = NULL;
+
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // Video RAM is allocated as an own address space
+ m_vram_space = &space(AS_DATA);
+
+ // allocate VRAM
+ assert(m_vram_size > 0);
+
+ if (m_vram_size < 0x20000)
+ {
+ // set unavailable RAM to 0xff
+ for (int addr = m_vram_size; addr < 0x30000; addr++) m_vram_space->write_byte(addr, 0xff);
+ }
+
+ save_item(NAME(m_offset_x));
+ save_item(NAME(m_offset_y));
+ save_item(NAME(m_visible_y));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_pal_write_first));
+ save_item(NAME(m_cmd_write_first));
+ save_item(NAME(m_pal_write));
+ save_item(NAME(m_cmd_write));
+ save_item(NAME(m_pal_reg));
+ save_item(NAME(m_stat_reg));
+ save_item(NAME(m_cont_reg));
+ save_item(NAME(m_read_ahead));
+ // save_item(NAME(m_vram));
+ // if ( m_vram_exp != NULL )
+ // save_pointer(NAME(m_vram_exp), 0x10000);
+ save_item(NAME(m_int_state));
+ save_item(NAME(m_scanline));
+ save_item(NAME(m_blink));
+ save_item(NAME(m_blink_count));
+ save_item(NAME(m_mx_delta));
+ save_item(NAME(m_my_delta));
+ save_item(NAME(m_button_state));
+ save_item(NAME(m_pal_ind16));
+ save_item(NAME(m_pal_ind256));
+ save_item(NAME(m_mmc.SX));
+ save_item(NAME(m_mmc.SY));
+ save_item(NAME(m_mmc.DX));
+ save_item(NAME(m_mmc.DY));
+ save_item(NAME(m_mmc.TX));
+ save_item(NAME(m_mmc.TY));
+ save_item(NAME(m_mmc.NX));
+ save_item(NAME(m_mmc.NY));
+ save_item(NAME(m_mmc.MX));
+ save_item(NAME(m_mmc.ASX));
+ save_item(NAME(m_mmc.ADX));
+ save_item(NAME(m_mmc.ANX));
+ save_item(NAME(m_mmc.CL));
+ save_item(NAME(m_mmc.LO));
+ save_item(NAME(m_mmc.CM));
+ save_item(NAME(m_mmc.MXS));
+ save_item(NAME(m_mmc.MXD));
+ save_item(NAME(m_vdp_ops_count));
+}
+
+void v99x8_device::device_reset()
+{
+ int i;
+
+ // offset reset
+ m_offset_x = 8;
+ m_offset_y = 8 + 16;
+ m_visible_y = 192;
+ // register reset
+ reset_palette (); // palette registers
+ for (i=0;i<10;i++) m_stat_reg[i] = 0;
+ m_stat_reg[2] = 0x0c;
+ if (m_model == MODEL_V9958) m_stat_reg[1] |= 4;
+ for (i=0;i<48;i++) m_cont_reg[i] = 0;
+ m_cmd_write_first = m_pal_write_first = 0;
+ m_int_state = 0;
+ m_read_ahead = 0; m_address_latch = 0; // ???
+ m_scanline = 0;
+ // MZ: The status registers 4 and 6 hold the high bits of the sprite
+ // collision location. The unused bits are set to 1.
+ // SR3: x x x x x x x x
+ // SR4: 1 1 1 1 1 1 1 x
+ // SR5: y y y y y y y y
+ // SR6: 1 1 1 1 1 1 y y
+ // Note that status register 4 is used in detection algorithms to tell
+ // apart the tms9929 from the v99x8.
+
+ // TODO: SR3-S6 do not yet store the information about the sprite collision
+ m_stat_reg[4] = 0xfe;
+ m_stat_reg[6] = 0xfc;
+}
+
+
+void v99x8_device::reset_palette ()
+{
+ // taken from V9938 Technical Data book, page 148. it's in G-R-B format
+ static const UINT8 pal16[16*3] = {
+ 0, 0, 0, // 0: black/transparent
+ 0, 0, 0, // 1: black
+ 6, 1, 1, // 2: medium green
+ 7, 3, 3, // 3: light green
+ 1, 1, 7, // 4: dark blue
+ 3, 2, 7, // 5: light blue
+ 1, 5, 1, // 6: dark red
+ 6, 2, 7, // 7: cyan
+ 1, 7, 1, // 8: medium red
+ 3, 7, 3, // 9: light red
+ 6, 6, 1, // 10: dark yellow
+ 6, 6, 4, // 11: light yellow
+ 4, 1, 1, // 12: dark green
+ 2, 6, 5, // 13: magenta
+ 5, 5, 5, // 14: gray
+ 7, 7, 7 // 15: white
+ };
+ int i, red, ind;
+
+ for (i=0;i<16;i++)
+ {
+ // set the palette registers
+ m_pal_reg[i*2+0] = pal16[i*3+1] << 4 | pal16[i*3+2];
+ m_pal_reg[i*2+1] = pal16[i*3];
+ // set the reference table
+ m_pal_ind16[i] = pal16[i*3+1] << 6 | pal16[i*3] << 3 | pal16[i*3+2];
+ }
+
+ // set internal palette GRAPHIC 7
+ for (i=0;i<256;i++)
+ {
+ ind = (i << 4) & 0x01c0;
+ ind |= (i >> 2) & 0x0038;
+ red = (i << 1) & 6; if (red == 6) red++;
+ ind |= red;
+
+ m_pal_ind256[i] = ind;
+ }
+}
+
+/***************************************************************************
+
+Memory functions
+
+***************************************************************************/
+
+void v99x8_device::vram_write(int offset, int data)
+{
+ int newoffset;
+
+ if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
+ {
+ newoffset = ((offset & 1) << 16) | (offset >> 1);
+ if (newoffset < m_vram_size)
+ m_vram_space->write_byte(newoffset, data);
+ }
+ else
+ {
+ if (offset < m_vram_size)
+ m_vram_space->write_byte(offset, data);
+ }
+}
+
+int v99x8_device::vram_read(int offset)
+{
+ if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
+ return m_vram_space->read_byte(((offset & 1) << 16) | (offset >> 1));
+ else
+ return m_vram_space->read_byte(offset);
+}
+
+void v99x8_device::check_int()
+{
+ UINT8 n;
+
+ n = ( (m_cont_reg[1] & 0x20) && (m_stat_reg[0] & 0x80) /*&& m_vblank_int*/) ||
+ ( (m_stat_reg[1] & 0x01) && (m_cont_reg[0] & 0x10) );
+
+ #if 0
+ if(n && m_vblank_int)
+ {
+ m_vblank_int = 0;
+ }
+ #endif
+
+ if (n != m_int_state)
+ {
+ m_int_state = n;
+ LOG(("V9938: IRQ line %s\n", n ? "up" : "down"));
+ }
+
+ /*
+ ** Somehow the IRQ request is going down without cpu_irq_line () being
+ ** called; because of this Mr. Ghost, Xevious and SD Snatcher don't
+ ** run. As a patch it's called every scanline
+ */
+ m_int_callback(n);
+}
+
+/***************************************************************************
+
+ Register functions
+
+***************************************************************************/
+
+void v99x8_device::register_write (int reg, int data)
+{
+ static UINT8 const reg_mask[] =
+ {
+ 0x7e, 0x7b, 0x7f, 0xff, 0x3f, 0xff, 0x3f, 0xff,
+ 0xfb, 0xbf, 0x07, 0x03, 0xff, 0xff, 0x07, 0x0f,
+ 0x0f, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x7f, 0x3f, 0x07
+ };
+
+ if (reg <= 27)
+ {
+ data &= reg_mask[reg];
+ if (m_cont_reg[reg] == data)
+ return;
+ }
+
+ if (reg > 46)
+ {
+ LOG(("V9938: Attempted to write to non-existant R#%d\n", reg));
+ return;
+ }
+
+ /*update_command ();*/
+
+ switch (reg) {
+ // registers that affect interrupt and display mode
+ case 0:
+ case 1:
+ m_cont_reg[reg] = data;
+ set_mode();
+ check_int();
+ LOG(("v9938: mode = %s\n", v9938_modes[m_mode]));
+ break;
+
+ case 18:
+ case 9:
+ m_cont_reg[reg] = data;
+ // recalc offset
+ m_offset_x = (( (~m_cont_reg[18] - 8) & 0x0f) + 1);
+ m_offset_y = ((~(m_cont_reg[18]>>4) - 8) & 0x0f) + 7;
+ if (m_cont_reg[9] & 0x80)
+ {
+ m_visible_y = 212;
+ }
+ else
+ {
+ m_visible_y = 192;
+ m_offset_y += 10;
+ }
+ break;
+
+ case 15:
+ m_pal_write_first = 0;
+ break;
+
+ // color burst registers aren't emulated
+ case 20:
+ case 21:
+ case 22:
+ LOG(("v9938: Write %02xh to R#%d; color burst not emulated\n", data, reg));
+ break;
+ case 25:
+ case 26:
+ case 27:
+ if (m_model != MODEL_V9958)
+ {
+ LOG(("v9938: Attempting to write %02xh to V9958 R#%d\n", data, reg));
+ data = 0;
+ }
+ else
+ {
+ if(reg == 25)
+ m_v9958_sp_mode = data & 0x18;
+ }
+ break;
+
+ case 44:
+ cpu_to_vdp (data);
+ break;
+
+ case 46:
+ command_unit_w (data);
+ break;
+ }
+
+ if (reg != 15)
+ LOG(("v9938: Write %02x to R#%d\n", data, reg));
+
+ m_cont_reg[reg] = data;
+}
+
+/***************************************************************************
+
+Refresh / render function
+
+***************************************************************************/
+
+inline bool v99x8_device::v9938_second_field()
+{
+ return !(((m_cont_reg[9] & 0x04) && !(m_stat_reg[2] & 2)) || m_blink);
+}
+
+
+void v99x8_device::default_border(const pen_t *pens, UINT16 *ln)
+{
+ UINT16 pen;
+ int i;
+
+ pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
+ i = LONG_WIDTH;
+ while (i--) *ln++ = pen;
+}
+
+void v99x8_device::graphic7_border(const pen_t *pens, UINT16 *ln)
+{
+ UINT16 pen;
+ int i;
+
+ pen = pens[m_pal_ind256[m_cont_reg[7]]];
+ i = LONG_WIDTH;
+ while (i--) *ln++ = pen;
+}
+
+void v99x8_device::graphic5_border(const pen_t *pens, UINT16 *ln)
+{
+ int i;
+ UINT16 pen0;
+ UINT16 pen1;
+
+ pen1 = pens[m_pal_ind16[(m_cont_reg[7]&0x03)]];
+ pen0 = pens[m_pal_ind16[((m_cont_reg[7]>>2)&0x03)]];
+ i = LONG_WIDTH / 2;
+ while (i--) { *ln++ = pen0; *ln++ = pen1; }
+}
+
+void v99x8_device::mode_text1(const pen_t *pens, UINT16 *ln, int line)
+{
+ int pattern, x, xx, name, xxx;
+ UINT16 fg, bg, pen;
+ int nametbl_addr, patterntbl_addr;
+
+ patterntbl_addr = m_cont_reg[4] << 11;
+ nametbl_addr = m_cont_reg[2] << 10;
+
+ fg = pens[m_pal_ind16[m_cont_reg[7] >> 4]];
+ bg = pens[m_pal_ind16[m_cont_reg[7] & 15]];
+
+ name = (line/8)*40;
+
+ pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
+
+ xxx = (m_offset_x + 8) * 2;
+ while (xxx--) *ln++ = pen;
+
+ for (x=0;x<40;x++)
+ {
+ pattern = m_vram_space->read_byte(patterntbl_addr + (m_vram_space->read_byte(nametbl_addr + name) * 8) +
+ ((line + m_cont_reg[23]) & 7));
+ for (xx=0;xx<6;xx++)
+ {
+ *ln++ = (pattern & 0x80) ? fg : bg;
+ *ln++ = (pattern & 0x80) ? fg : bg;
+ pattern <<= 1;
+ }
+ /* width height 212, characters start repeating at the bottom */
+ name = (name + 1) & 0x3ff;
+ }
+
+ xxx = ((16 - m_offset_x) + 8) * 2;
+ while (xxx--) *ln++ = pen;
+}
+
+void v99x8_device::mode_text2(const pen_t *pens, UINT16 *ln, int line)
+{
+ int pattern, x, charcode, name, xxx, patternmask, colourmask;
+ UINT16 fg, bg, fg0, bg0, pen;
+ int nametbl_addr, patterntbl_addr, colourtbl_addr;
+
+ patterntbl_addr = m_cont_reg[4] << 11;
+ colourtbl_addr = ((m_cont_reg[3] & 0xf8) << 6) + (m_cont_reg[10] << 14);
+ #if 0
+ colourmask = ((m_cont_reg[3] & 7) << 5) | 0x1f; /* cause a bug in Forth+ v1.0 on Geneve */
+ #else
+ colourmask = ((m_cont_reg[3] & 7) << 6) | 0x3f; /* verify! */
+ #endif
+ nametbl_addr = ((m_cont_reg[2] & 0xfc) << 10);
+ patternmask = ((m_cont_reg[2] & 3) << 10) | 0x3ff; /* seems correct */
+
+ fg = pens[m_pal_ind16[m_cont_reg[7] >> 4]];
+ bg = pens[m_pal_ind16[m_cont_reg[7] & 15]];
+ fg0 = pens[m_pal_ind16[m_cont_reg[12] >> 4]];
+ bg0 = pens[m_pal_ind16[m_cont_reg[12] & 15]];
+
+ name = (line/8)*80;
+
+ xxx = (m_offset_x + 8) * 2;
+ pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
+ while (xxx--) *ln++ = pen;
+
+ for (x=0;x<80;x++)
+ {
+ charcode = m_vram_space->read_byte(nametbl_addr + (name&patternmask));
+ if (m_blink)
+ {
+ pattern = m_vram_space->read_byte(colourtbl_addr + ((name/8)&colourmask));
+ if (pattern & (0x80 >> (name & 7) ) )
+ {
+ pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode * 8) +
+ ((line + m_cont_reg[23]) & 7)));
+
+ *ln++ = (pattern & 0x80) ? fg0 : bg0;
+ *ln++ = (pattern & 0x40) ? fg0 : bg0;
+ *ln++ = (pattern & 0x20) ? fg0 : bg0;
+ *ln++ = (pattern & 0x10) ? fg0 : bg0;
+ *ln++ = (pattern & 0x08) ? fg0 : bg0;
+ *ln++ = (pattern & 0x04) ? fg0 : bg0;
+
+ name++;
+ continue;
+ }
+ }
+
+ pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode * 8) +
+ ((line + m_cont_reg[23]) & 7)));
+
+ *ln++ = (pattern & 0x80) ? fg : bg;
+ *ln++ = (pattern & 0x40) ? fg : bg;
+ *ln++ = (pattern & 0x20) ? fg : bg;
+ *ln++ = (pattern & 0x10) ? fg : bg;
+ *ln++ = (pattern & 0x08) ? fg : bg;
+ *ln++ = (pattern & 0x04) ? fg : bg;
+
+ name++;
+ }
+
+ xxx = (16 - m_offset_x + 8) * 2;
+ while (xxx--) *ln++ = pen;
+}
+
+void v99x8_device::mode_multi(const pen_t *pens, UINT16 *ln, int line)
+{
+ int nametbl_addr, patterntbl_addr, colour;
+ int name, line2, x, xx;
+ UINT16 pen, pen_bg;
+
+ nametbl_addr = (m_cont_reg[2] << 10);
+ patterntbl_addr = (m_cont_reg[4] << 11);
+
+ line2 = (line - m_cont_reg[23]) & 255;
+ name = (line2/8)*32;
+
+ pen_bg = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
+ xx = m_offset_x * 2;
+ while (xx--) *ln++ = pen_bg;
+
+ for (x=0;x<32;x++)
+ {
+ colour = m_vram_space->read_byte(patterntbl_addr + (m_vram_space->read_byte(nametbl_addr + name) * 8) + ((line2/4)&7));
+ pen = pens[m_pal_ind16[colour>>4]];
+ /* eight pixels */
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ pen = pens[m_pal_ind16[colour&15]];
+ /* eight pixels */
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ *ln++ = pen;
+ name++;
+ }
+
+ xx = (16 - m_offset_x) * 2;
+ while (xx--) *ln++ = pen_bg;
+}
+
+void v99x8_device::mode_graphic1(const pen_t *pens, UINT16 *ln, int line)
+{
+ UINT16 fg, bg, pen;
+ int nametbl_addr, patterntbl_addr, colourtbl_addr;
+ int pattern, x, xx, line2, name, charcode, colour, xxx;
+
+ nametbl_addr = (m_cont_reg[2] << 10);
+ colourtbl_addr = (m_cont_reg[3] << 6) + (m_cont_reg[10] << 14);
+ patterntbl_addr = (m_cont_reg[4] << 11);
+
+ line2 = (line - m_cont_reg[23]) & 255;
+
+ name = (line2/8)*32;
+
+ pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
+ xxx = m_offset_x * 2;
+ while (xxx--) *ln++ = pen;
+
+ for (x=0;x<32;x++)
+ {
+ charcode = m_vram_space->read_byte(nametbl_addr + name);
+ colour = m_vram_space->read_byte(colourtbl_addr + charcode/8);
+ fg = pens[m_pal_ind16[colour>>4]];
+ bg = pens[m_pal_ind16[colour&15]];
+ pattern = m_vram_space->read_byte(patterntbl_addr + (charcode * 8 + (line2 & 7)));
+
+ for (xx=0;xx<8;xx++)
+ {
+ *ln++ = (pattern & 0x80) ? fg : bg;
+ *ln++ = (pattern & 0x80) ? fg : bg;
+ pattern <<= 1;
+ }
+ name++;
+ }
+
+ xx = (16 - m_offset_x) * 2;
+ while (xx--) *ln++ = pen;
+}
+
+void v99x8_device::mode_graphic23(const pen_t *pens, UINT16 *ln, int line)
+{
+ UINT16 fg, bg, pen;
+ int nametbl_addr, patterntbl_addr, colourtbl_addr;
+ int pattern, x, xx, line2, name, charcode,
+ colour, colourmask, patternmask, xxx;
+
+ colourmask = ((m_cont_reg[3] & 0x7f) * 8) | 7;
+ patternmask = ((m_cont_reg[4] & 0x03) * 256) | 0xff;
+
+ nametbl_addr = (m_cont_reg[2] << 10);
+ colourtbl_addr = ((m_cont_reg[3] & 0x80) << 6) + (m_cont_reg[10] << 14);
+ patterntbl_addr = ((m_cont_reg[4] & 0x3c) << 11);
+
+ line2 = (line + m_cont_reg[23]) & 255;
+ name = (line2/8)*32;
+
+ pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
+ xxx = m_offset_x * 2;
+ while (xxx--) *ln++ = pen;
+
+ for (x=0;x<32;x++)
+ {
+ charcode = m_vram_space->read_byte(nametbl_addr + name) + (line2&0xc0)*4;
+ colour = m_vram_space->read_byte(colourtbl_addr + ((charcode&colourmask)*8+(line2&7)));
+ pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode&patternmask)*8+(line2&7)));
+ fg = pens[m_pal_ind16[colour>>4]];
+ bg = pens[m_pal_ind16[colour&15]];
+ for (xx=0;xx<8;xx++)
+ {
+ *ln++ = (pattern & 0x80) ? fg : bg;
+ *ln++ = (pattern & 0x80) ? fg : bg;
+ pattern <<= 1;
+ }
+ name++;
+ }
+
+ xx = (16 - m_offset_x) * 2;
+ while (xx--) *ln++ = pen;
+}
+
+void v99x8_device::mode_graphic4(const pen_t *pens, UINT16 *ln, int line)
+{
+ int nametbl_addr, colour;
+ int line2, linemask, x, xx;
+ UINT16 pen, pen_bg;
+
+ linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
+
+ line2 = ((line + m_cont_reg[23]) & linemask) & 255;
+
+ nametbl_addr = ((m_cont_reg[2] & 0x40) << 10) + line2 * 128;
+ if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
+ nametbl_addr += 0x8000;
+
+ pen_bg = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
+ xx = m_offset_x * 2;
+ while (xx--) *ln++ = pen_bg;
+
+ for (x=0;x<128;x++)
+ {
+ colour = m_vram_space->read_byte(nametbl_addr++);
+ pen = pens[m_pal_ind16[colour>>4]];
+ *ln++ = pen;
+ *ln++ = pen;
+ pen = pens[m_pal_ind16[colour&15]];
+ *ln++ = pen;
+ *ln++ = pen;
+ }
+
+ xx = (16 - m_offset_x) * 2;
+ while (xx--) *ln++ = pen_bg;
+}
+
+void v99x8_device::mode_graphic5(const pen_t *pens, UINT16 *ln, int line)
+{
+ int nametbl_addr, colour;
+ int line2, linemask, x, xx;
+ UINT16 pen_bg0[4];
+ UINT16 pen_bg1[4];
+
+ linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
+
+ line2 = ((line + m_cont_reg[23]) & linemask) & 255;
+
+ nametbl_addr = ((m_cont_reg[2] & 0x40) << 10) + line2 * 128;
+ if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
+ nametbl_addr += 0x8000;
+
+ pen_bg1[0] = pens[m_pal_ind16[(m_cont_reg[7]&0x03)]];
+ pen_bg0[0] = pens[m_pal_ind16[((m_cont_reg[7]>>2)&0x03)]];
+
+ xx = m_offset_x;
+ while (xx--) { *ln++ = pen_bg0[0]; *ln++ = pen_bg1[0]; }
+
+ x = (m_cont_reg[8] & 0x20) ? 0 : 1;
+
+ for (;x<4;x++)
+ {
+ pen_bg0[x] = pens[m_pal_ind16[x]];
+ pen_bg1[x] = pens[m_pal_ind16[x]];
+ }
+
+ for (x=0;x<128;x++)
+ {
+ colour = m_vram_space->read_byte(nametbl_addr++);
+
+ *ln++ = pen_bg0[colour>>6];
+ *ln++ = pen_bg1[(colour>>4)&3];
+ *ln++ = pen_bg0[(colour>>2)&3];
+ *ln++ = pen_bg1[(colour&3)];
+ }
+
+ pen_bg1[0] = pens[m_pal_ind16[(m_cont_reg[7]&0x03)]];
+ pen_bg0[0] = pens[m_pal_ind16[((m_cont_reg[7]>>2)&0x03)]];
+ xx = 16 - m_offset_x;
+ while (xx--) { *ln++ = pen_bg0[0]; *ln++ = pen_bg1[0]; }
+}
+
+void v99x8_device::mode_graphic6(const pen_t *pens, UINT16 *ln, int line)
+{
+ UINT8 colour;
+ int line2, linemask, x, xx, nametbl_addr;
+ UINT16 pen_bg, fg0;
+ UINT16 fg1;
+
+ linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
+
+ line2 = ((line + m_cont_reg[23]) & linemask) & 255;
+
+ nametbl_addr = line2 << 8 ;
+ if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
+ nametbl_addr += 0x10000;
+
+ pen_bg = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
+ xx = m_offset_x * 2;
+ while (xx--) *ln++ = pen_bg;
+
+ if (m_cont_reg[2] & 0x40)
+ {
+ for (x=0;x<32;x++)
+ {
+ nametbl_addr++;
+ colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ fg0 = pens[m_pal_ind16[colour>>4]];
+ fg1 = pens[m_pal_ind16[colour&15]];
+ *ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
+ *ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
+ *ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
+ *ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
+ nametbl_addr += 7;
+ }
+ }
+ else
+ {
+ for (x=0;x<256;x++)
+ {
+ colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ *ln++ = pens[m_pal_ind16[colour>>4]];
+ *ln++ = pens[m_pal_ind16[colour&15]];
+ nametbl_addr++;
+ }
+ }
+
+ xx = (16 - m_offset_x) * 2;
+ while (xx--) *ln++ = pen_bg;
+}
+
+void v99x8_device::mode_graphic7(const pen_t *pens, UINT16 *ln, int line)
+{
+ UINT8 colour;
+ int line2, linemask, x, xx, nametbl_addr;
+ UINT16 pen, pen_bg;
+
+ linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
+
+ line2 = ((line + m_cont_reg[23]) & linemask) & 255;
+
+ nametbl_addr = line2 << 8;
+ if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
+ nametbl_addr += 0x10000;
+
+ pen_bg = pens[m_pal_ind256[m_cont_reg[7]]];
+ xx = m_offset_x * 2;
+ while (xx--) *ln++ = pen_bg;
+
+ if ((m_v9958_sp_mode & 0x18) == 0x08) // v9958 screen 12, puzzle star title screen
+ {
+ for (x=0;x<64;x++)
+ {
+ int colour[4];
+ int ind;
+
+ colour[0] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ nametbl_addr++;
+ colour[1] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ nametbl_addr++;
+ colour[2] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ nametbl_addr++;
+ colour[3] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+
+ ind = (colour[0] & 7) << 11 | (colour[1] & 7) << 14 |
+ (colour[2] & 7) << 5 | (colour[3] & 7) << 8;
+
+ *ln++ = s_pal_indYJK[ind | ((colour[0] >> 3) & 31)];
+ *ln++ = s_pal_indYJK[ind | ((colour[0] >> 3) & 31)];
+
+ *ln++ = s_pal_indYJK[ind | ((colour[1] >> 3) & 31)];
+ *ln++ = s_pal_indYJK[ind | ((colour[1] >> 3) & 31)];
+
+ *ln++ = s_pal_indYJK[ind | ((colour[2] >> 3) & 31)];
+ *ln++ = s_pal_indYJK[ind | ((colour[2] >> 3) & 31)];
+
+ *ln++ = s_pal_indYJK[ind | ((colour[3] >> 3) & 31)];
+ *ln++ = s_pal_indYJK[ind | ((colour[3] >> 3) & 31)];
+
+ nametbl_addr++;
+ }
+ }
+ else if ((m_v9958_sp_mode & 0x18) == 0x18) // v9958 screen 10/11, puzzle star & sexy boom gameplay
+ {
+ for (x=0;x<64;x++)
+ {
+ int colour[4];
+ int ind;
+
+ colour[0] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ nametbl_addr++;
+ colour[1] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ nametbl_addr++;
+ colour[2] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ nametbl_addr++;
+ colour[3] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+
+ ind = (colour[0] & 7) << 11 | (colour[1] & 7) << 14 |
+ (colour[2] & 7) << 5 | (colour[3] & 7) << 8;
+
+ *ln++ = colour[0] & 8 ? m_pal_ind16[colour[0] >> 4] : s_pal_indYJK[ind | ((colour[0] >> 3) & 30)];
+ *ln++ = colour[0] & 8 ? m_pal_ind16[colour[0] >> 4] : s_pal_indYJK[ind | ((colour[0] >> 3) & 30)];
+
+ *ln++ = colour[1] & 8 ? m_pal_ind16[colour[1] >> 4] : s_pal_indYJK[ind | ((colour[1] >> 3) & 30)];
+ *ln++ = colour[1] & 8 ? m_pal_ind16[colour[1] >> 4] : s_pal_indYJK[ind | ((colour[1] >> 3) & 30)];
+
+ *ln++ = colour[2] & 8 ? m_pal_ind16[colour[2] >> 4] : s_pal_indYJK[ind | ((colour[2] >> 3) & 30)];
+ *ln++ = colour[2] & 8 ? m_pal_ind16[colour[2] >> 4] : s_pal_indYJK[ind | ((colour[2] >> 3) & 30)];
+
+ *ln++ = colour[3] & 8 ? m_pal_ind16[colour[3] >> 4] : s_pal_indYJK[ind | ((colour[3] >> 3) & 30)];
+ *ln++ = colour[3] & 8 ? m_pal_ind16[colour[3] >> 4] : s_pal_indYJK[ind | ((colour[3] >> 3) & 30)];
+
+ nametbl_addr++;
+ }
+ }
+ else if (m_cont_reg[2] & 0x40)
+ {
+ for (x=0;x<32;x++)
+ {
+ nametbl_addr++;
+ colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ pen = pens[m_pal_ind256[colour]];
+ *ln++ = pen; *ln++ = pen;
+ *ln++ = pen; *ln++ = pen;
+ *ln++ = pen; *ln++ = pen;
+ *ln++ = pen; *ln++ = pen;
+ *ln++ = pen; *ln++ = pen;
+ *ln++ = pen; *ln++ = pen;
+ *ln++ = pen; *ln++ = pen;
+ *ln++ = pen; *ln++ = pen;
+ nametbl_addr++;
+ }
+ }
+ else
+ {
+ for (x=0;x<256;x++)
+ {
+ colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
+ pen = pens[m_pal_ind256[colour]];
+ *ln++ = pen;
+ *ln++ = pen;
+ nametbl_addr++;
+ }
+ }
+
+ xx = (16 - m_offset_x) * 2;
+ while (xx--) *ln++ = pen_bg;
+}
+
+void v99x8_device::mode_unknown(const pen_t *pens, UINT16 *ln, int line)
+{
+ UINT16 fg, bg;
+ int x;
+
+ fg = pens[m_pal_ind16[m_cont_reg[7] >> 4]];
+ bg = pens[m_pal_ind16[m_cont_reg[7] & 15]];
+
+ x = m_offset_x * 2;
+ while (x--) *ln++ = bg;
+
+ x = 512;
+ while (x--) *ln++ = fg;
+
+ x = (16 - m_offset_x) * 2;
+ while (x--) *ln++ = bg;
+}
+
+void v99x8_device::default_draw_sprite(const pen_t *pens, UINT16 *ln, UINT8 *col)
+{
+ int i;
+ ln += m_offset_x * 2;
+
+ for (i=0;i<256;i++)
+ {
+ if (col[i] & 0x80)
+ {
+ *ln++ = pens[m_pal_ind16[col[i]&0x0f]];
+ *ln++ = pens[m_pal_ind16[col[i]&0x0f]];
+ }
+ else
+ {
+ ln += 2;
+ }
+ }
+}
+
+void v99x8_device::graphic5_draw_sprite(const pen_t *pens, UINT16 *ln, UINT8 *col)
+{
+ int i;
+ ln += m_offset_x * 2;
+
+ for (i=0;i<256;i++)
+ {
+ if (col[i] & 0x80)
+ {
+ *ln++ = pens[m_pal_ind16[(col[i]>>2)&0x03]];
+ *ln++ = pens[m_pal_ind16[col[i]&0x03]];
+ }
+ else
+ {
+ ln += 2;
+ }
+ }
+}
+
+
+void v99x8_device::graphic7_draw_sprite(const pen_t *pens, UINT16 *ln, UINT8 *col)
+{
+ static const UINT16 g7_ind16[16] = {
+ 0, 2, 192, 194, 48, 50, 240, 242,
+ 482, 7, 448, 455, 56, 63, 504, 511 };
+ int i;
+
+ ln += m_offset_x * 2;
+
+ for (i=0;i<256;i++)
+ {
+ if (col[i] & 0x80)
+ {
+ *ln++ = pens[g7_ind16[col[i]&0x0f]];
+ *ln++ = pens[g7_ind16[col[i]&0x0f]];
+ }
+ else
+ {
+ ln += 2;
+ }
+ }
+}
+
+
+void v99x8_device::sprite_mode1 (int line, UINT8 *col)
+{
+ int attrtbl_addr, patterntbl_addr, pattern_addr;
+ int x, y, p, height, c, p2, i, n, pattern;
+
+ memset(col, 0, 256);
+
+ // are sprites disabled?
+ if (m_cont_reg[8] & 0x02) return;
+
+ attrtbl_addr = (m_cont_reg[5] << 7) + (m_cont_reg[11] << 15);
+ patterntbl_addr = (m_cont_reg[6] << 11);
+
+ // 16x16 or 8x8 sprites
+ height = (m_cont_reg[1] & 2) ? 16 : 8;
+ // magnified sprites (zoomed)
+ if (m_cont_reg[1] & 1) height *= 2;
+
+ p2 = p = 0;
+ while (1)
+ {
+ y = m_vram_space->read_byte(attrtbl_addr);
+ if (y == 208) break;
+ y = (y - m_cont_reg[23]) & 255;
+ if (y > 208)
+ y = -(~y&255);
+ else
+ y++;
+
+ // if sprite in range, has to be drawn
+ if ( (line >= y) && (line < (y + height) ) )
+ {
+ if (p2 == 4)
+ {
+ // max maximum sprites per line!
+ if ( !(m_stat_reg[0] & 0x40) )
+ m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | 0x40 | p;
+
+ break;
+ }
+ // get x
+ x = m_vram_space->read_byte(attrtbl_addr + 1);
+ if (m_vram_space->read_byte(attrtbl_addr + 3) & 0x80) x -= 32;
+
+ // get pattern
+ pattern = m_vram_space->read_byte(attrtbl_addr + 2);
+ if (m_cont_reg[1] & 2)
+ pattern &= 0xfc;
+ n = line - y;
+ pattern_addr = patterntbl_addr + pattern * 8 + ((m_cont_reg[1] & 1) ? n/2 : n);
+ pattern = (m_vram_space->read_byte(pattern_addr) << 8) | m_vram_space->read_byte(pattern_addr+16);
+
+ // get colour
+ c = m_vram_space->read_byte(attrtbl_addr + 3) & 0x0f;
+
+ // draw left part
+ n = 0;
+ while (1)
+ {
+ if (n == 0) pattern = m_vram_space->read_byte(pattern_addr);
+ else if ( (n == 1) && (m_cont_reg[1] & 2) ) pattern = m_vram_space->read_byte(pattern_addr + 16);
+ else break;
+
+ n++;
+
+ for (i=0;i<8;i++)
+ {
+ if (pattern & 0x80)
+ {
+ if ( (x >= 0) && (x < 256) )
+ {
+ if (col[x] & 0x40)
+ {
+ // we have a collision!
+ if (p2 < 4)
+ m_stat_reg[0] |= 0x20;
+ }
+ if ( !(col[x] & 0x80) )
+ {
+ if (c || (m_cont_reg[8] & 0x20) )
+ col[x] |= 0xc0 | c;
+ else
+ col[x] |= 0x40;
+ }
+
+ // if zoomed, draw another pixel
+ if (m_cont_reg[1] & 1)
+ {
+ if (col[x+1] & 0x40)
+ {
+ // we have a collision!
+ if (p2 < 4)
+ m_stat_reg[0] |= 0x20;
+ }
+ if ( !(col[x+1] & 0x80) )
+ {
+ if (c || (m_cont_reg[8] & 0x20) )
+ col[x+1] |= 0xc0 | c;
+ else
+ col[x+1] |= 0x80;
+ }
+ }
+ }
+ }
+ if (m_cont_reg[1] & 1) x += 2; else x++;
+ pattern <<= 1;
+ }
+ }
+
+ p2++;
+ }
+
+ if (p >= 31) break;
+ p++;
+ attrtbl_addr += 4;
+ }
+
+ if ( !(m_stat_reg[0] & 0x40) )
+ m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | p;
+}
+
+void v99x8_device::sprite_mode2 (int line, UINT8 *col)
+{
+ int attrtbl_addr, patterntbl_addr, pattern_addr, colourtbl_addr;
+ int x, i, y, p, height, c, p2, n, pattern, colourmask, first_cc_seen;
+
+ memset(col, 0, 256);
+
+ // are sprites disabled?
+ if (m_cont_reg[8] & 0x02) return;
+
+ attrtbl_addr = ( (m_cont_reg[5] & 0xfc) << 7) + (m_cont_reg[11] << 15);
+ colourtbl_addr = ( (m_cont_reg[5] & 0xf8) << 7) + (m_cont_reg[11] << 15);
+ patterntbl_addr = (m_cont_reg[6] << 11);
+ colourmask = ( (m_cont_reg[5] & 3) << 3) | 0x7; // check this!
+
+ // 16x16 or 8x8 sprites
+ height = (m_cont_reg[1] & 2) ? 16 : 8;
+ // magnified sprites (zoomed)
+ if (m_cont_reg[1] & 1) height *= 2;
+
+ p2 = p = first_cc_seen = 0;
+ while (1)
+ {
+ y = vram_read(attrtbl_addr);
+ if (y == 216) break;
+ y = (y - m_cont_reg[23]) & 255;
+ if (y > 216)
+ y = -(~y&255);
+ else
+ y++;
+
+ // if sprite in range, has to be drawn
+ if ( (line >= y) && (line < (y + height) ) )
+ {
+ if (p2 == 8)
+ {
+ // max maximum sprites per line!
+ if ( !(m_stat_reg[0] & 0x40) )
+ m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | 0x40 | p;
+
+ break;
+ }
+
+ n = line - y; if (m_cont_reg[1] & 1) n /= 2;
+ // get colour
+ c = vram_read(colourtbl_addr + (((p&colourmask)*16) + n));
+
+ // don't draw all sprite with CC set before any sprites
+ // with CC = 0 are seen on this line
+ if (c & 0x40)
+ {
+ if (!first_cc_seen)
+ goto skip_first_cc_set;
+ }
+ else
+ first_cc_seen = 1;
+
+ // get pattern
+ pattern = vram_read(attrtbl_addr + 2);
+ if (m_cont_reg[1] & 2)
+ pattern &= 0xfc;
+ pattern_addr = patterntbl_addr + pattern * 8 + n;
+ pattern = (vram_read(pattern_addr) << 8) | vram_read(pattern_addr + 16);
+
+ // get x
+ x = vram_read(attrtbl_addr + 1);
+ if (c & 0x80) x -= 32;
+
+ n = (m_cont_reg[1] & 2) ? 16 : 8;
+ while (n--)
+ {
+ for (i=0;i<=(m_cont_reg[1] & 1);i++)
+ {
+ if ( (x >= 0) && (x < 256) )
+ {
+ if ( (pattern & 0x8000) && !(col[x] & 0x10) )
+ {
+ if ( (c & 15) || (m_cont_reg[8] & 0x20) )
+ {
+ if ( !(c & 0x40) )
+ {
+ if (col[x] & 0x20) col[x] |= 0x10;
+ else
+ col[x] |= 0x20 | (c & 15);
+ }
+ else
+ col[x] |= c & 15;
+
+ col[x] |= 0x80;
+ }
+ }
+ else
+ {
+ if ( !(c & 0x40) && (col[x] & 0x20) )
+ col[x] |= 0x10;
+ }
+
+ if ( !(c & 0x60) && (pattern & 0x8000) )
+ {
+ if (col[x] & 0x40)
+ {
+ // sprite collision!
+ if (p2 < 8)
+ m_stat_reg[0] |= 0x20;
+ }
+ else
+ col[x] |= 0x40;
+ }
+
+ x++;
+ }
+ }
+
+ pattern <<= 1;
+ }
+
+ skip_first_cc_set:
+ p2++;
+ }
+
+ if (p >= 31) break;
+ p++;
+ attrtbl_addr += 4;
+ }
+
+ if ( !(m_stat_reg[0] & 0x40) )
+ m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | p;
+}
+
+
+const v99x8_device::v99x8_mode v99x8_device::s_modes[] = {
+ { 0x02,
+ &v99x8_device::mode_text1,
+ &v99x8_device::default_border,
+ NULL,
+ NULL
+ },
+ { 0x01,
+ &v99x8_device::mode_multi,
+ &v99x8_device::default_border,
+ &v99x8_device::sprite_mode1,
+ &v99x8_device::default_draw_sprite
+ },
+ { 0x00,
+ &v99x8_device::mode_graphic1,
+ &v99x8_device::default_border,
+ &v99x8_device::sprite_mode1,
+ &v99x8_device::default_draw_sprite
+ },
+ { 0x04,
+ &v99x8_device::mode_graphic23,
+ &v99x8_device::default_border,
+ &v99x8_device::sprite_mode1,
+ &v99x8_device::default_draw_sprite
+ },
+ { 0x08,
+ &v99x8_device::mode_graphic23,
+ &v99x8_device::default_border,
+ &v99x8_device::sprite_mode2,
+ &v99x8_device::default_draw_sprite
+ },
+ { 0x0c,
+ &v99x8_device::mode_graphic4,
+ &v99x8_device::default_border,
+ &v99x8_device::sprite_mode2,
+ &v99x8_device::default_draw_sprite
+ },
+ { 0x10,
+ &v99x8_device::mode_graphic5,
+ &v99x8_device::graphic5_border,
+ &v99x8_device::sprite_mode2,
+ &v99x8_device::graphic5_draw_sprite
+ },
+ { 0x14,
+ &v99x8_device::mode_graphic6,
+ &v99x8_device::default_border,
+ &v99x8_device::sprite_mode2,
+ &v99x8_device::default_draw_sprite
+ },
+ { 0x1c,
+ &v99x8_device::mode_graphic7,
+ &v99x8_device::graphic7_border,
+ &v99x8_device::sprite_mode2,
+ &v99x8_device::graphic7_draw_sprite
+ },
+ { 0x0a,
+ &v99x8_device::mode_text2,
+ &v99x8_device::default_border,
+ NULL,
+ NULL
+ },
+ { 0xff,
+ &v99x8_device::mode_unknown,
+ &v99x8_device::default_border,
+ NULL,
+ NULL
+ }
+};
+
+void v99x8_device::set_mode()
+{
+ int n,i;
+
+ n = (((m_cont_reg[0] & 0x0e) << 1) | ((m_cont_reg[1] & 0x18) >> 3));
+ for (i=0;;i++)
+ {
+ if ( (s_modes[i].m == n) || (s_modes[i].m == 0xff) ) break;
+ }
+ m_mode = i;
+}
+
+void v99x8_device::refresh_16(int line)
+{
+ const pen_t *pens = m_palette->pens();
+ bool double_lines = false;
+ UINT8 col[256];
+ UINT16 *ln, *ln2 = NULL;
+
+ if (m_cont_reg[9] & 0x08)
+ {
+ ln = &m_bitmap.pix16(line*2+((m_stat_reg[2]>>1)&1));
+ }
+ else
+ {
+ ln = &m_bitmap.pix16(line*2);
+ ln2 = &m_bitmap.pix16(line*2+1);
+ double_lines = true;
+ }
+
+ if ( !(m_cont_reg[1] & 0x40) || (m_stat_reg[2] & 0x40) )
+ {
+ (this->*s_modes[m_mode].border_16) (pens, ln);
+ }
+ else
+ {
+ int i = (line - m_offset_y) & 255;
+ (this->*s_modes[m_mode].visible_16) (pens, ln, i);
+ if (s_modes[m_mode].sprites)
+ {
+ (this->*s_modes[m_mode].sprites) (i, col);
+ (this->*s_modes[m_mode].draw_sprite_16) (pens, ln, col);
+ }
+ }
+
+ if (double_lines)
+ memcpy (ln2, ln, (512 + 32) * 2);
+}
+
+void v99x8_device::refresh_line(int line)
+{
+ int ind16, ind256;
+
+ ind16 = m_pal_ind16[0];
+ ind256 = m_pal_ind256[0];
+
+ if ( !(m_cont_reg[8] & 0x20) && (m_mode != V9938_MODE_GRAPHIC5) )
+ {
+ m_pal_ind16[0] = m_pal_ind16[(m_cont_reg[7] & 0x0f)];
+ m_pal_ind256[0] = m_pal_ind256[m_cont_reg[7]];
+ }
+
+ refresh_16 (line);
+
+ if ( !(m_cont_reg[8] & 0x20) && (m_mode != V9938_MODE_GRAPHIC5) )
+ {
+ m_pal_ind16[0] = ind16;
+ m_pal_ind256[0] = ind256;
+ }
+}
+
+/*
+
+From: awulms@inter.nl.net (Alex Wulms)
+*** About the HR/VR topic: this is how it works according to me:
+
+*** HR:
+HR is very straightforward:
+-HR=1 during 'display time'
+-HR=0 during 'horizontal border, horizontal retrace'
+I have put 'display time' and 'horizontal border, horizontal retrace' between
+quotes because HR does not only flip between 0 and 1 during the display of
+the 192/212 display lines, but also during the vertical border and during the
+vertical retrace.
+
+*** VR:
+VR is a little bit tricky
+-VR always gets set to 0 when the VDP starts with display line 0
+-VR gets set to 1 when the VDP reaches display line (192 if LN=0) or (212 if
+LN=1)
+-The VDP displays contents of VRAM as long as VR=0
+
+As a consequence of this behaviour, it is possible to program the famous
+overscan trick, where VRAM contents is shown in the borders:
+Generate an interrupt at line 230 (or so) and on this interrupt: set LN=1
+Generate an interrupt at line 200 (or so) and on this interrupt: set LN=0
+Repeat the above two steps
+
+*** The top/bottom border contents during overscan:
+On screen 0:
+1) The VDP keeps increasing the name table address pointer during bottom
+border, vertical retrace and top border
+2) The VDP resets the name table address pointer when the first display line
+is reached
+
+On the other screens:
+1) The VDP keeps increasing the name table address pointer during the bottom
+border
+2) The VDP resets the name table address pointer such that the top border
+contents connects up with the first display line. E.g., when the top border
+is 26 lines high, the VDP will take:
+'logical' vram line
+TOPB000 256-26
+...
+TOPB025 256-01
+DISPL000 000
+...
+DISPL211 211
+BOTB000 212
+...
+BOTB024 236
+
+
+
+*** About the horizontal interrupt
+
+All relevant definitions on a row:
+-FH: Bit 0 of status register 1
+-IE1: Bit 4 of mode register 0
+-IL: Line number in mode register 19
+-DL: The line that the VDP is going to display (corrected for vertical scroll)
+-IRQ: Interrupt request line of VDP to Z80
+
+At the *start* of every new line (display, bottom border, part of vertical
+display), the VDP does:
+-FH = (FH && IE1) || (IL==DL)
+
+After reading of status register 1 by the CPU, the VDP does:
+-FH = 0
+
+Furthermore, the following is true all the time:
+-IRQ = FH && IE1
+
+The resulting behaviour:
+When IE1=0:
+-FH will be set as soon as display of line IL starts
+-FH will be reset as soon as status register 1 is read
+-FH will be reset as soon as the next display line is reached
+
+When IE=1:
+-FH and IRQ will be set as soon as display line IL is reached
+-FH and IRQ will be reset as soon as status register 1 is read
+
+Another subtile result:
+If, while FH and IRQ are set, IE1 gets reset, the next happens:
+-IRQ is reset immediately (since IRQ is always FH && IE1)
+-FH will be reset as soon as display of the next line starts (unless the next
+line is line IL)
+
+
+*** About the vertical interrupt:
+Another relevant definition:
+-FV: Bit 7 of status register 0
+-IE0: Bit 5 of mode register 1
+
+I only know for sure the behaviour when IE0=1:
+-FV and IRQ will be set as soon as VR changes from 0 to 1
+-FV and IRQ will be reset as soon as status register 0 is read
+
+A consequence is that NO vertical interrupts will be generated during the
+overscan trick, described in the VR section above.
+
+I do not know the behaviour of FV when IE0=0. That is the part that I still
+have to test.
+*/
+
+void v99x8_device::interrupt_start_vblank()
+{
+ #if 0
+ if (machine.input().code_pressed (KEYCODE_D) )
+ {
+ for (i=0;i<24;i++) osd_printf_debug ("R#%d = %02x\n", i, m_cont_reg[i]);
+ }
+ #endif
+
+ // at every frame, vdp switches fields
+ m_stat_reg[2] = (m_stat_reg[2] & 0xfd) | (~m_stat_reg[2] & 2);
+
+ // color blinking
+ if (!(m_cont_reg[13] & 0xf0))
+ m_blink = 0;
+ else if (!(m_cont_reg[13] & 0x0f))
+ m_blink = 1;
+ else
+ {
+ // both on and off counter are non-zero: timed blinking
+ if (m_blink_count)
+ m_blink_count--;
+ if (!m_blink_count)
+ {
+ m_blink = !m_blink;
+ if (m_blink)
+ m_blink_count = (m_cont_reg[13] >> 4) * 10;
+ else
+ m_blink_count = (m_cont_reg[13] & 0x0f) * 10;
+ }
+ }
+}
+
+/***************************************************************************
+
+Command unit
+
+***************************************************************************/
+
+/*************************************************************/
+/** Completely rewritten by Alex Wulms: **/
+/** - VDP Command execution 'in parallel' with CPU **/
+/** - Corrected behaviour of VDP commands **/
+/** - Made it easier to implement correct S7/8 mapping **/
+/** by concentrating VRAM access in one single place **/
+/** - Made use of the 'in parallel' VDP command exec **/
+/** and correct timing. You must call the function **/
+/** LoopVDP() from LoopZ80 in MSX.c. You must call it **/
+/** exactly 256 times per screen refresh. **/
+/** Started on : 11-11-1999 **/
+/** Beta release 1 on: 9-12-1999 **/
+/** Beta release 2 on: 20-01-2000 **/
+/** - Corrected behaviour of VRM <-> Z80 transfer **/
+/** - Improved performance of the code **/
+/** Public release 1.0: 20-04-2000 **/
+/*************************************************************/
+
+#define VDP_VRMP5(MX, X, Y) ((!MX) ? (((Y&1023)<<7) + ((X&255)>>1)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&255)>>1)))
+#define VDP_VRMP6(MX, X, Y) ((!MX) ? (((Y&1023)<<7) + ((X&511)>>2)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&511)>>2)))
+//#define VDP_VRMP7(MX, X, Y) ((!MX) ? (((Y&511)<<8) + ((X&511)>>1)) : (EXPMEM_OFFSET + ((Y&255)<<8) + ((X&511)>>1)))
+#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))*/)
+//#define VDP_VRMP8(MX, X, Y) ((!MX) ? (((Y&511)<<8) + (X&255)) : (EXPMEM_OFFSET + ((Y&255)<<8) + (X&255)))
+#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))*/)
+
+#define VDP_VRMP(M, MX, X, Y) VDPVRMP(M, MX, X, Y)
+#define VDP_POINT(M, MX, X, Y) VDPpoint(M, MX, X, Y)
+#define VDP_PSET(M, MX, X, Y, C, O) VDPpset(M, MX, X, Y, C, O)
+
+#define CM_ABRT 0x0
+#define CM_POINT 0x4
+#define CM_PSET 0x5
+#define CM_SRCH 0x6
+#define CM_LINE 0x7
+#define CM_LMMV 0x8
+#define CM_LMMM 0x9
+#define CM_LMCM 0xA
+#define CM_LMMC 0xB
+#define CM_HMMV 0xC
+#define CM_HMMM 0xD
+#define CM_YMMM 0xE
+#define CM_HMMC 0xF
+
+/*************************************************************
+Many VDP commands are executed in some kind of loop but
+essentially, there are only a few basic loop structures
+that are re-used. We define the loop structures that are
+re-used here so that they have to be entered only once
+*************************************************************/
+#define pre_loop \
+while ((cnt-=delta) > 0) {
+ #define post_loop \
+}
+
+// Loop over DX, DY
+#define post__x_y(MX) \
+if (!--ANX || ((ADX+=TX)&MX)) { \
+ if (!(--NY&1023) || (DY+=TY)==-1) \
+ break; \
+ else { \
+ ADX=DX; \
+ ANX=NX; \
+ } \
+} \
+post_loop
+
+// Loop over DX, SY, DY
+#define post__xyy(MX) \
+if ((ADX+=TX)&MX) { \
+ if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \
+ break; \
+ else \
+ ADX=DX; \
+} \
+post_loop
+
+// Loop over SX, DX, SY, DY
+#define post_xxyy(MX) \
+if (!--ANX || ((ASX+=TX)&MX) || ((ADX+=TX)&MX)) { \
+ if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \
+ break; \
+ else { \
+ ASX=SX; \
+ ADX=DX; \
+ ANX=NX; \
+ } \
+} \
+post_loop
+
+/*************************************************************/
+/** Variables visible only in this module **/
+/*************************************************************/
+static const UINT8 Mask[4] = { 0x0F,0x03,0x0F,0xFF };
+static const int PPB[4] = { 2,4,2,1 };
+static const int PPL[4] = { 256,512,512,256 };
+
+// SprOn SprOn SprOf SprOf
+// ScrOf ScrOn ScrOf ScrOn
+static const int srch_timing[8]={
+ 818, 1025, 818, 830, // ntsc
+ 696, 854, 696, 684 // pal
+};
+static const int line_timing[8]={
+ 1063, 1259, 1063, 1161,
+ 904, 1026, 904, 953
+};
+static const int hmmv_timing[8]={
+ 439, 549, 439, 531,
+ 366, 439, 366, 427
+};
+static const int lmmv_timing[8]={
+ 873, 1135, 873, 1056,
+ 732, 909, 732, 854
+};
+static const int ymmm_timing[8]={
+ 586, 952, 586, 610,
+ 488, 720, 488, 500
+};
+static const int hmmm_timing[8]={
+ 818, 1111, 818, 854,
+ 684, 879, 684, 708
+};
+static const int lmmm_timing[8]={
+ 1160, 1599, 1160, 1172,
+ 964, 1257, 964, 977
+};
+
+/** VDPVRMP() **********************************************/
+/** Calculate addr of a pixel in vram **/
+/*************************************************************/
+inline int v99x8_device::VDPVRMP(UINT8 M,int MX,int X,int Y)
+{
+ switch(M)
+ {
+ case 0: return VDP_VRMP5(MX,X,Y);
+ case 1: return VDP_VRMP6(MX,X,Y);
+ case 2: return VDP_VRMP7(MX,X,Y);
+ case 3: return VDP_VRMP8(MX,X,Y);
+ }
+
+ return 0;
+}
+
+/** VDPpoint5() ***********************************************/
+/** Get a pixel on screen 5 **/
+/*************************************************************/
+inline UINT8 v99x8_device::VDPpoint5(int MXS, int SX, int SY)
+{
+ return (m_vram_space->read_byte(VDP_VRMP5(MXS, SX, SY)) >>
+ (((~SX)&1)<<2)
+ )&15;
+}
+
+/** VDPpoint6() ***********************************************/
+/** Get a pixel on screen 6 **/
+/*************************************************************/
+inline UINT8 v99x8_device::VDPpoint6(int MXS, int SX, int SY)
+{
+ return (m_vram_space->read_byte(VDP_VRMP6(MXS, SX, SY)) >>
+ (((~SX)&3)<<1)
+ )&3;
+}
+
+/** VDPpoint7() ***********************************************/
+/** Get a pixel on screen 7 **/
+/*************************************************************/
+inline UINT8 v99x8_device::VDPpoint7(int MXS, int SX, int SY)
+{
+ return (m_vram_space->read_byte(VDP_VRMP7(MXS, SX, SY)) >>
+ (((~SX)&1)<<2)
+ )&15;
+}
+
+/** VDPpoint8() ***********************************************/
+/** Get a pixel on screen 8 **/
+/*************************************************************/
+inline UINT8 v99x8_device::VDPpoint8(int MXS, int SX, int SY)
+{
+ return m_vram_space->read_byte(VDP_VRMP8(MXS, SX, SY));
+}
+
+/** VDPpoint() ************************************************/
+/** Get a pixel on a screen **/
+/*************************************************************/
+inline UINT8 v99x8_device::VDPpoint(UINT8 SM, int MXS, int SX, int SY)
+{
+ switch(SM)
+ {
+ case 0: return VDPpoint5(MXS,SX,SY);
+ case 1: return VDPpoint6(MXS,SX,SY);
+ case 2: return VDPpoint7(MXS,SX,SY);
+ case 3: return VDPpoint8(MXS,SX,SY);
+ }
+
+ return(0);
+}
+
+/** VDPpsetlowlevel() ****************************************/
+/** Low level function to set a pixel on a screen **/
+/** Make it inline to make it fast **/
+/*************************************************************/
+inline void v99x8_device::VDPpsetlowlevel(int addr, UINT8 CL, UINT8 M, UINT8 OP)
+{
+ // If this turns out to be too slow, get a pointer to the address space
+ // and work directly on it.
+ UINT8 val = m_vram_space->read_byte(addr);
+ switch (OP)
+ {
+ case 0: val = (val & M) | CL; break;
+ case 1: val = val & (CL | M); break;
+ case 2: val |= CL; break;
+ case 3: val ^= CL; break;
+ case 4: val = (val & M) | ~(CL | M); break;
+ case 8: if (CL) val = (val & M) | CL; break;
+ case 9: if (CL) val = val & (CL | M); break;
+ case 10: if (CL) val |= CL; break;
+ case 11: if (CL) val ^= CL; break;
+ case 12: if (CL) val = (val & M) | ~(CL|M); break;
+ default:
+ LOG(("v9938: invalid operation %d in pset\n", OP));
+ }
+
+ m_vram_space->write_byte(addr, val);
+}
+
+/** VDPpset5() ***********************************************/
+/** Set a pixel on screen 5 **/
+/*************************************************************/
+inline void v99x8_device::VDPpset5(int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
+{
+ UINT8 SH = ((~DX)&1)<<2;
+ VDPpsetlowlevel(VDP_VRMP5(MXD, DX, DY), CL << SH, ~(15<<SH), OP);
+}
+
+/** VDPpset6() ***********************************************/
+/** Set a pixel on screen 6 **/
+/*************************************************************/
+inline void v99x8_device::VDPpset6(int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
+{
+ UINT8 SH = ((~DX)&3)<<1;
+
+ VDPpsetlowlevel(VDP_VRMP6(MXD, DX, DY), CL << SH, ~(3<<SH), OP);
+}
+
+/** VDPpset7() ***********************************************/
+/** Set a pixel on screen 7 **/
+/*************************************************************/
+inline void v99x8_device::VDPpset7(int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
+{
+ UINT8 SH = ((~DX)&1)<<2;
+
+ VDPpsetlowlevel(VDP_VRMP7(MXD, DX, DY), CL << SH, ~(15<<SH), OP);
+}
+
+/** VDPpset8() ***********************************************/
+/** Set a pixel on screen 8 **/
+/*************************************************************/
+inline void v99x8_device::VDPpset8(int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
+{
+ VDPpsetlowlevel(VDP_VRMP8(MXD, DX, DY), CL, 0, OP);
+}
+
+/** VDPpset() ************************************************/
+/** Set a pixel on a screen **/
+/*************************************************************/
+inline void v99x8_device::VDPpset(UINT8 SM, int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
+{
+ switch (SM) {
+ case 0: VDPpset5(MXD, DX, DY, CL, OP); break;
+ case 1: VDPpset6(MXD, DX, DY, CL, OP); break;
+ case 2: VDPpset7(MXD, DX, DY, CL, OP); break;
+ case 3: VDPpset8(MXD, DX, DY, CL, OP); break;
+ }
+}
+
+/** get_vdp_timing_value() **************************************/
+/** Get timing value for a certain VDP command **/
+/*************************************************************/
+int v99x8_device::get_vdp_timing_value(const int *timing_values)
+{
+ return(timing_values[((m_cont_reg[1]>>6)&1)|(m_cont_reg[8]&2)|((m_cont_reg[9]<<1)&4)]);
+}
+
+/** SrchEgine()** ********************************************/
+/** Search a dot **/
+/*************************************************************/
+void v99x8_device::srch_engine()
+{
+ int SX=m_mmc.SX;
+ int SY=m_mmc.SY;
+ int TX=m_mmc.TX;
+ int ANX=m_mmc.ANX;
+ UINT8 CL=m_mmc.CL;
+ int MXD = m_mmc.MXD;
+ int cnt;
+ int delta;
+
+ delta = get_vdp_timing_value(srch_timing);
+ cnt = m_vdp_ops_count;
+
+ #define post_srch(MX) \
+ { m_stat_reg[2]|=0x10; /* Border detected */ break; } \
+ if ((SX+=TX) & MX) { m_stat_reg[2] &= 0xEF; /* Border not detected */ break; }
+ switch (m_mode) {
+ default:
+ case V9938_MODE_GRAPHIC4: pre_loop if ((VDPpoint5(MXD, SX, SY)==CL) ^ANX) post_srch(256) post_loop
+ break;
+ case V9938_MODE_GRAPHIC5: pre_loop if ((VDPpoint6(MXD, SX, SY)==CL) ^ANX) post_srch(512) post_loop
+ break;
+ case V9938_MODE_GRAPHIC6: pre_loop if ((VDPpoint7(MXD, SX, SY)==CL) ^ANX) post_srch(512) post_loop
+ break;
+ case V9938_MODE_GRAPHIC7: pre_loop if ((VDPpoint8(MXD, SX, SY)==CL) ^ANX) post_srch(256) post_loop
+ break;
+ }
+
+ if ((m_vdp_ops_count=cnt)>0) {
+ // Command execution done
+ m_stat_reg[2] &= 0xFE;
+ m_vdp_engine = 0;
+ // Update SX in VDP registers
+ m_stat_reg[8] = SX & 0xFF;
+ m_stat_reg[9] = (SX>>8) | 0xFE;
+ }
+ else {
+ m_mmc.SX=SX;
+ }
+}
+
+/** LineEgine()** ********************************************/
+/** Draw a line **/
+/*************************************************************/
+void v99x8_device::line_engine()
+{
+ int DX=m_mmc.DX;
+ int DY=m_mmc.DY;
+ int TX=m_mmc.TX;
+ int TY=m_mmc.TY;
+ int NX=m_mmc.NX;
+ int NY=m_mmc.NY;
+ int ASX=m_mmc.ASX;
+ int ADX=m_mmc.ADX;
+ UINT8 CL=m_mmc.CL;
+ UINT8 LO=m_mmc.LO;
+ int MXD = m_mmc.MXD;
+ int cnt;
+ int delta;
+
+ delta = get_vdp_timing_value(line_timing);
+ cnt = m_vdp_ops_count;
+
+ #define post_linexmaj(MX) \
+ DX+=TX; \
+ if ((ASX-=NY)<0) { \
+ ASX+=NX; \
+ DY+=TY; \
+ } \
+ ASX&=1023; /* Mask to 10 bits range */\
+ if (ADX++==NX || (DX&MX)) \
+ break; \
+ post_loop
+
+ #define post_lineymaj(MX) \
+ DY+=TY; \
+ if ((ASX-=NY)<0) { \
+ ASX+=NX; \
+ DX+=TX; \
+ } \
+ ASX&=1023; /* Mask to 10 bits range */\
+ if (ADX++==NX || (DX&MX)) \
+ break; \
+ post_loop
+
+ if ((m_cont_reg[45]&0x01)==0)
+ // X-Axis is major direction
+ switch (m_mode) {
+ default:
+ case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, DX, DY, CL, LO); post_linexmaj(256)
+ break;
+ case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, DX, DY, CL, LO); post_linexmaj(512)
+ break;
+ case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, DX, DY, CL, LO); post_linexmaj(512)
+ break;
+ case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, DX, DY, CL, LO); post_linexmaj(256)
+ break;
+ }
+ else
+ // Y-Axis is major direction
+ switch (m_mode) {
+ default:
+ case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, DX, DY, CL, LO); post_lineymaj(256)
+ break;
+ case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, DX, DY, CL, LO); post_lineymaj(512)
+ break;
+ case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, DX, DY, CL, LO); post_lineymaj(512)
+ break;
+ case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, DX, DY, CL, LO); post_lineymaj(256)
+ break;
+ }
+
+ if ((m_vdp_ops_count=cnt)>0) {
+ // Command execution done
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ m_cont_reg[38]=DY & 0xFF;
+ m_cont_reg[39]=(DY>>8) & 0x03;
+ }
+ else {
+ m_mmc.DX=DX;
+ m_mmc.DY=DY;
+ m_mmc.ASX=ASX;
+ m_mmc.ADX=ADX;
+ }
+}
+
+/** lmmv_engine() *********************************************/
+/** VDP -> Vram **/
+/*************************************************************/
+void v99x8_device::lmmv_engine()
+{
+ int DX=m_mmc.DX;
+ int DY=m_mmc.DY;
+ int TX=m_mmc.TX;
+ int TY=m_mmc.TY;
+ int NX=m_mmc.NX;
+ int NY=m_mmc.NY;
+ int ADX=m_mmc.ADX;
+ int ANX=m_mmc.ANX;
+ UINT8 CL=m_mmc.CL;
+ UINT8 LO=m_mmc.LO;
+ int MXD = m_mmc.MXD;
+ int cnt;
+ int delta;
+
+ delta = get_vdp_timing_value(lmmv_timing);
+ cnt = m_vdp_ops_count;
+
+ switch (m_mode) {
+ default:
+ case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, ADX, DY, CL, LO); post__x_y(256)
+ break;
+ case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, ADX, DY, CL, LO); post__x_y(512)
+ break;
+ case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, ADX, DY, CL, LO); post__x_y(512)
+ break;
+ case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, ADX, DY, CL, LO); post__x_y(256)
+ break;
+ }
+
+ if ((m_vdp_ops_count=cnt)>0) {
+ // Command execution done
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ if (!NY)
+ DY+=TY;
+ m_cont_reg[38]=DY & 0xFF;
+ m_cont_reg[39]=(DY>>8) & 0x03;
+ m_cont_reg[42]=NY & 0xFF;
+ m_cont_reg[43]=(NY>>8) & 0x03;
+ }
+ else {
+ m_mmc.DY=DY;
+ m_mmc.NY=NY;
+ m_mmc.ANX=ANX;
+ m_mmc.ADX=ADX;
+ }
+}
+
+/** lmmm_engine() *********************************************/
+/** Vram -> Vram **/
+/*************************************************************/
+void v99x8_device::lmmm_engine()
+{
+ int SX=m_mmc.SX;
+ int SY=m_mmc.SY;
+ int DX=m_mmc.DX;
+ int DY=m_mmc.DY;
+ int TX=m_mmc.TX;
+ int TY=m_mmc.TY;
+ int NX=m_mmc.NX;
+ int NY=m_mmc.NY;
+ int ASX=m_mmc.ASX;
+ int ADX=m_mmc.ADX;
+ int ANX=m_mmc.ANX;
+ UINT8 LO=m_mmc.LO;
+ int MXS = m_mmc.MXS;
+ int MXD = m_mmc.MXD;
+ int cnt;
+ int delta;
+
+ delta = get_vdp_timing_value(lmmm_timing);
+ cnt = m_vdp_ops_count;
+
+ switch (m_mode) {
+ default:
+ case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, ADX, DY, VDPpoint5(MXS, ASX, SY), LO); post_xxyy(256)
+ break;
+ case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, ADX, DY, VDPpoint6(MXS, ASX, SY), LO); post_xxyy(512)
+ break;
+ case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, ADX, DY, VDPpoint7(MXS, ASX, SY), LO); post_xxyy(512)
+ break;
+ case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, ADX, DY, VDPpoint8(MXS, ASX, SY), LO); post_xxyy(256)
+ break;
+ }
+
+ if ((m_vdp_ops_count=cnt)>0) {
+ // Command execution done
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ if (!NY) {
+ SY+=TY;
+ DY+=TY;
+ }
+ else
+ if (SY==-1)
+ DY+=TY;
+ m_cont_reg[42]=NY & 0xFF;
+ m_cont_reg[43]=(NY>>8) & 0x03;
+ m_cont_reg[34]=SY & 0xFF;
+ m_cont_reg[35]=(SY>>8) & 0x03;
+ m_cont_reg[38]=DY & 0xFF;
+ m_cont_reg[39]=(DY>>8) & 0x03;
+ }
+ else {
+ m_mmc.SY=SY;
+ m_mmc.DY=DY;
+ m_mmc.NY=NY;
+ m_mmc.ANX=ANX;
+ m_mmc.ASX=ASX;
+ m_mmc.ADX=ADX;
+ }
+}
+
+/** lmcm_engine() *********************************************/
+/** Vram -> CPU **/
+/*************************************************************/
+void v99x8_device::lmcm_engine()
+{
+ if ((m_stat_reg[2]&0x80)!=0x80) {
+ 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);
+ m_vdp_ops_count-=get_vdp_timing_value(lmmv_timing);
+ m_stat_reg[2]|=0x80;
+
+ if (!--m_mmc.ANX || ((m_mmc.ASX+=m_mmc.TX)&m_mmc.MX)) {
+ if (!(--m_mmc.NY & 1023) || (m_mmc.SY+=m_mmc.TY)==-1) {
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ if (!m_mmc.NY)
+ m_mmc.DY+=m_mmc.TY;
+ m_cont_reg[42]=m_mmc.NY & 0xFF;
+ m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
+ m_cont_reg[34]=m_mmc.SY & 0xFF;
+ m_cont_reg[35]=(m_mmc.SY>>8) & 0x03;
+ }
+ else {
+ m_mmc.ASX=m_mmc.SX;
+ m_mmc.ANX=m_mmc.NX;
+ }
+ }
+ }
+}
+
+/** lmmc_engine() *********************************************/
+/** CPU -> Vram **/
+/*************************************************************/
+void v99x8_device::lmmc_engine()
+{
+ if ((m_stat_reg[2]&0x80)!=0x80) {
+ UINT8 SM=((m_mode >= 5) && (m_mode <= 8)) ? (m_mode-5) : 0;
+
+ m_stat_reg[7]=m_cont_reg[44]&=Mask[SM];
+ VDP_PSET(SM, m_mmc.MXD, m_mmc.ADX, m_mmc.DY, m_cont_reg[44], m_mmc.LO);
+ m_vdp_ops_count-=get_vdp_timing_value(lmmv_timing);
+ m_stat_reg[2]|=0x80;
+
+ if (!--m_mmc.ANX || ((m_mmc.ADX+=m_mmc.TX)&m_mmc.MX)) {
+ if (!(--m_mmc.NY&1023) || (m_mmc.DY+=m_mmc.TY)==-1) {
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ if (!m_mmc.NY)
+ m_mmc.DY+=m_mmc.TY;
+ m_cont_reg[42]=m_mmc.NY & 0xFF;
+ m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
+ m_cont_reg[38]=m_mmc.DY & 0xFF;
+ m_cont_reg[39]=(m_mmc.DY>>8) & 0x03;
+ }
+ else {
+ m_mmc.ADX=m_mmc.DX;
+ m_mmc.ANX=m_mmc.NX;
+ }
+ }
+ }
+}
+
+/** hmmv_engine() *********************************************/
+/** VDP --> Vram **/
+/*************************************************************/
+void v99x8_device::hmmv_engine()
+{
+ int DX=m_mmc.DX;
+ int DY=m_mmc.DY;
+ int TX=m_mmc.TX;
+ int TY=m_mmc.TY;
+ int NX=m_mmc.NX;
+ int NY=m_mmc.NY;
+ int ADX=m_mmc.ADX;
+ int ANX=m_mmc.ANX;
+ UINT8 CL=m_mmc.CL;
+ int MXD = m_mmc.MXD;
+ int cnt;
+ int delta;
+
+ delta = get_vdp_timing_value(hmmv_timing);
+ cnt = m_vdp_ops_count;
+
+ switch (m_mode) {
+ default:
+ case V9938_MODE_GRAPHIC4: pre_loop m_vram_space->write_byte(VDP_VRMP5(MXD, ADX, DY), CL); post__x_y(256)
+ break;
+ case V9938_MODE_GRAPHIC5: pre_loop m_vram_space->write_byte(VDP_VRMP6(MXD, ADX, DY), CL); post__x_y(512)
+ break;
+ case V9938_MODE_GRAPHIC6: pre_loop m_vram_space->write_byte(VDP_VRMP7(MXD, ADX, DY), CL); post__x_y(512)
+ break;
+ case V9938_MODE_GRAPHIC7: pre_loop m_vram_space->write_byte(VDP_VRMP8(MXD, ADX, DY), CL); post__x_y(256)
+ break;
+ }
+
+ if ((m_vdp_ops_count=cnt)>0) {
+ // Command execution done
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ if (!NY)
+ DY+=TY;
+ m_cont_reg[42]=NY & 0xFF;
+ m_cont_reg[43]=(NY>>8) & 0x03;
+ m_cont_reg[38]=DY & 0xFF;
+ m_cont_reg[39]=(DY>>8) & 0x03;
+ }
+ else {
+ m_mmc.DY=DY;
+ m_mmc.NY=NY;
+ m_mmc.ANX=ANX;
+ m_mmc.ADX=ADX;
+ }
+}
+
+/** hmmm_engine() *********************************************/
+/** Vram -> Vram **/
+/*************************************************************/
+void v99x8_device::hmmm_engine()
+{
+ int SX=m_mmc.SX;
+ int SY=m_mmc.SY;
+ int DX=m_mmc.DX;
+ int DY=m_mmc.DY;
+ int TX=m_mmc.TX;
+ int TY=m_mmc.TY;
+ int NX=m_mmc.NX;
+ int NY=m_mmc.NY;
+ int ASX=m_mmc.ASX;
+ int ADX=m_mmc.ADX;
+ int ANX=m_mmc.ANX;
+ int MXS = m_mmc.MXS;
+ int MXD = m_mmc.MXD;
+ int cnt;
+ int delta;
+
+ delta = get_vdp_timing_value(hmmm_timing);
+ cnt = m_vdp_ops_count;
+
+ switch (m_mode) {
+ default:
+ 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)
+ break;
+ 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)
+ break;
+ 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)
+ break;
+ 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)
+ break;
+ }
+
+ if ((m_vdp_ops_count=cnt)>0) {
+ // Command execution done
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ if (!NY) {
+ SY+=TY;
+ DY+=TY;
+ }
+ else
+ if (SY==-1)
+ DY+=TY;
+ m_cont_reg[42]=NY & 0xFF;
+ m_cont_reg[43]=(NY>>8) & 0x03;
+ m_cont_reg[34]=SY & 0xFF;
+ m_cont_reg[35]=(SY>>8) & 0x03;
+ m_cont_reg[38]=DY & 0xFF;
+ m_cont_reg[39]=(DY>>8) & 0x03;
+ }
+ else {
+ m_mmc.SY=SY;
+ m_mmc.DY=DY;
+ m_mmc.NY=NY;
+ m_mmc.ANX=ANX;
+ m_mmc.ASX=ASX;
+ m_mmc.ADX=ADX;
+ }
+}
+
+/** ymmm_engine() *********************************************/
+/** Vram -> Vram **/
+/*************************************************************/
+
+void v99x8_device::ymmm_engine()
+{
+ int SY=m_mmc.SY;
+ int DX=m_mmc.DX;
+ int DY=m_mmc.DY;
+ int TX=m_mmc.TX;
+ int TY=m_mmc.TY;
+ int NY=m_mmc.NY;
+ int ADX=m_mmc.ADX;
+ int MXD = m_mmc.MXD;
+ int cnt;
+ int delta;
+
+ delta = get_vdp_timing_value(ymmm_timing);
+ cnt = m_vdp_ops_count;
+
+ switch (m_mode) {
+ default:
+ 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)
+ break;
+ 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)
+ break;
+ 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)
+ break;
+ 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)
+ break;
+ }
+
+ if ((m_vdp_ops_count=cnt)>0) {
+ // Command execution done
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ if (!NY) {
+ SY+=TY;
+ DY+=TY;
+ }
+ else
+ if (SY==-1)
+ DY+=TY;
+ m_cont_reg[42]=NY & 0xFF;
+ m_cont_reg[43]=(NY>>8) & 0x03;
+ m_cont_reg[34]=SY & 0xFF;
+ m_cont_reg[35]=(SY>>8) & 0x03;
+ m_cont_reg[38]=DY & 0xFF;
+ m_cont_reg[39]=(DY>>8) & 0x03;
+ }
+ else {
+ m_mmc.SY=SY;
+ m_mmc.DY=DY;
+ m_mmc.NY=NY;
+ m_mmc.ADX=ADX;
+ }
+}
+
+/** hmmc_engine() *********************************************/
+/** CPU -> Vram **/
+/*************************************************************/
+void v99x8_device::hmmc_engine()
+{
+ if ((m_stat_reg[2]&0x80)!=0x80) {
+ 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]);
+ m_vdp_ops_count -= get_vdp_timing_value(hmmv_timing);
+ m_stat_reg[2]|=0x80;
+
+ if (!--m_mmc.ANX || ((m_mmc.ADX+=m_mmc.TX)&m_mmc.MX)) {
+ if (!(--m_mmc.NY&1023) || (m_mmc.DY+=m_mmc.TY)==-1) {
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ if (!m_mmc.NY)
+ m_mmc.DY+=m_mmc.TY;
+ m_cont_reg[42]=m_mmc.NY & 0xFF;
+ m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
+ m_cont_reg[38]=m_mmc.DY & 0xFF;
+ m_cont_reg[39]=(m_mmc.DY>>8) & 0x03;
+ }
+ else {
+ m_mmc.ADX=m_mmc.DX;
+ m_mmc.ANX=m_mmc.NX;
+ }
+ }
+ }
+}
+
+/** VDPWrite() ***********************************************/
+/** Use this function to transfer pixel(s) from CPU to m_ **/
+/*************************************************************/
+void v99x8_device::cpu_to_vdp(UINT8 V)
+{
+ m_stat_reg[2]&=0x7F;
+ m_stat_reg[7]=m_cont_reg[44]=V;
+ if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
+}
+
+/** VDPRead() ************************************************/
+/** Use this function to transfer pixel(s) from VDP to CPU. **/
+/*************************************************************/
+UINT8 v99x8_device::vdp_to_cpu()
+{
+ m_stat_reg[2]&=0x7F;
+ if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
+ return(m_cont_reg[44]);
+}
+
+/** report_vdp_command() ***************************************/
+/** Report VDP Command to be executed **/
+/*************************************************************/
+void v99x8_device::report_vdp_command(UINT8 Op)
+{
+ static const char *const Ops[16] =
+ {
+ "SET ","AND ","OR ","XOR ","NOT ","NOP ","NOP ","NOP ",
+ "TSET","TAND","TOR ","TXOR","TNOT","NOP ","NOP ","NOP "
+ };
+ static const char *const Commands[16] =
+ {
+ " ABRT"," ????"," ????"," ????","POINT"," PSET"," SRCH"," LINE",
+ " LMMV"," LMMM"," LMCM"," LMMC"," HMMV"," HMMM"," YMMM"," HMMC"
+ };
+
+ UINT8 CL, CM, LO;
+ int SX,SY, DX,DY, NX,NY;
+
+ // Fetch arguments
+ CL = m_cont_reg[44];
+ SX = (m_cont_reg[32]+((int)m_cont_reg[33]<<8)) & 511;
+ SY = (m_cont_reg[34]+((int)m_cont_reg[35]<<8)) & 1023;
+ DX = (m_cont_reg[36]+((int)m_cont_reg[37]<<8)) & 511;
+ DY = (m_cont_reg[38]+((int)m_cont_reg[39]<<8)) & 1023;
+ NX = (m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023;
+ NY = (m_cont_reg[42]+((int)m_cont_reg[43]<<8)) & 1023;
+ CM = Op>>4;
+ LO = Op&0x0F;
+
+ LOG(("V9938: Opcode %02Xh %s-%s (%d,%d)->(%d,%d),%d [%d,%d]%s\n",
+ Op, Commands[CM], Ops[LO],
+ SX,SY, DX,DY, CL, m_cont_reg[45]&0x04? -NX:NX,
+ m_cont_reg[45]&0x08? -NY:NY,
+ m_cont_reg[45]&0x70? " on ExtVRAM":""
+ ));
+}
+
+/** VDPDraw() ************************************************/
+/** Perform a given V9938 operation Op. **/
+/*************************************************************/
+UINT8 v99x8_device::command_unit_w(UINT8 Op)
+{
+ int SM;
+
+ // V9938 ops only work in SCREENs 5-8
+ if (m_mode<5)
+ return(0);
+
+ SM = m_mode-5; // Screen mode index 0..3
+
+ m_mmc.CM = Op>>4;
+ if ((m_mmc.CM & 0x0C) != 0x0C && m_mmc.CM != 0)
+ // Dot operation: use only relevant bits of color
+ m_stat_reg[7]=(m_cont_reg[44]&=Mask[SM]);
+
+ // if(Verbose&0x02)
+ report_vdp_command(Op);
+
+ switch(Op>>4) {
+ case CM_ABRT:
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ return 1;
+ case CM_POINT:
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ m_stat_reg[7]=m_cont_reg[44]=
+ VDP_POINT(SM, (m_cont_reg[45] & 0x10) != 0,
+ m_cont_reg[32]+((int)m_cont_reg[33]<<8),
+ m_cont_reg[34]+((int)m_cont_reg[35]<<8));
+ return 1;
+ case CM_PSET:
+ m_stat_reg[2]&=0xFE;
+ m_vdp_engine=0;
+ VDP_PSET(SM, (m_cont_reg[45] & 0x20) != 0,
+ m_cont_reg[36]+((int)m_cont_reg[37]<<8),
+ m_cont_reg[38]+((int)m_cont_reg[39]<<8),
+ m_cont_reg[44],
+ Op&0x0F);
+ return 1;
+ case CM_SRCH:
+ m_vdp_engine=&v99x8_device::srch_engine;
+ break;
+ case CM_LINE:
+ m_vdp_engine=&v99x8_device::line_engine;
+ break;
+ case CM_LMMV:
+ m_vdp_engine=&v99x8_device::lmmv_engine;
+ break;
+ case CM_LMMM:
+ m_vdp_engine=&v99x8_device::lmmm_engine;
+ break;
+ case CM_LMCM:
+ m_vdp_engine=&v99x8_device::lmcm_engine;
+ break;
+ case CM_LMMC:
+ m_vdp_engine=&v99x8_device::lmmc_engine;
+ break;
+ case CM_HMMV:
+ m_vdp_engine=&v99x8_device::hmmv_engine;
+ break;
+ case CM_HMMM:
+ m_vdp_engine=&v99x8_device::hmmm_engine;
+ break;
+ case CM_YMMM:
+ m_vdp_engine=&v99x8_device::ymmm_engine;
+ break;
+ case CM_HMMC:
+ m_vdp_engine=&v99x8_device::hmmc_engine;
+ break;
+ default:
+ LOG(("V9938: Unrecognized opcode %02Xh\n",Op));
+ return(0);
+ }
+
+ // Fetch unconditional arguments
+ m_mmc.SX = (m_cont_reg[32]+((int)m_cont_reg[33]<<8)) & 511;
+ m_mmc.SY = (m_cont_reg[34]+((int)m_cont_reg[35]<<8)) & 1023;
+ m_mmc.DX = (m_cont_reg[36]+((int)m_cont_reg[37]<<8)) & 511;
+ m_mmc.DY = (m_cont_reg[38]+((int)m_cont_reg[39]<<8)) & 1023;
+ m_mmc.NY = (m_cont_reg[42]+((int)m_cont_reg[43]<<8)) & 1023;
+ m_mmc.TY = m_cont_reg[45]&0x08? -1:1;
+ m_mmc.MX = PPL[SM];
+ m_mmc.CL = m_cont_reg[44];
+ m_mmc.LO = Op&0x0F;
+ m_mmc.MXS = (m_cont_reg[45] & 0x10) != 0;
+ m_mmc.MXD = (m_cont_reg[45] & 0x20) != 0;
+
+ // Argument depends on UINT8 or dot operation
+ if ((m_mmc.CM & 0x0C) == 0x0C) {
+ m_mmc.TX = m_cont_reg[45]&0x04? -PPB[SM]:PPB[SM];
+ m_mmc.NX = ((m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023)/PPB[SM];
+ }
+ else {
+ m_mmc.TX = m_cont_reg[45]&0x04? -1:1;
+ m_mmc.NX = (m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023;
+ }
+
+ // X loop variables are treated specially for LINE command
+ if (m_mmc.CM == CM_LINE) {
+ m_mmc.ASX=((m_mmc.NX-1)>>1);
+ m_mmc.ADX=0;
+ }
+ else {
+ m_mmc.ASX = m_mmc.SX;
+ m_mmc.ADX = m_mmc.DX;
+ }
+
+ // NX loop variable is treated specially for SRCH command
+ if (m_mmc.CM == CM_SRCH)
+ m_mmc.ANX=(m_cont_reg[45]&0x02)!=0; // Do we look for "==" or "!="?
+ else
+ m_mmc.ANX = m_mmc.NX;
+
+ // Command execution started
+ m_stat_reg[2]|=0x01;
+
+ // Start execution if we still have time slices
+ if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
+
+ // Operation successfully initiated
+ return(1);
+}
+
+/** LoopVDP() ************************************************
+Run X steps of active VDP command
+*************************************************************/
+void v99x8_device::update_command()
+{
+ if(m_vdp_ops_count<=0)
+ {
+ m_vdp_ops_count+=13662;
+ if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
+ }
+ else
+ {
+ m_vdp_ops_count=13662;
+ if(m_vdp_engine) (this->*m_vdp_engine)();
+ }
+}
+
+static MACHINE_CONFIG_FRAGMENT( v9938 )
+ MCFG_PALETTE_ADD("palette", 512)
+ MCFG_PALETTE_INIT_OWNER(v9938_device, v9938)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor v9938_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( v9938 );
+}
+
+static MACHINE_CONFIG_FRAGMENT( v9958 )
+ MCFG_PALETTE_ADD("palette", 19780)
+ MCFG_PALETTE_INIT_OWNER(v9958_device, v9958)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor v9958_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( v9958 );
+}
diff --git a/src/devices/video/v9938.h b/src/devices/video/v9938.h
new file mode 100644
index 00000000000..4430c2ba4d5
--- /dev/null
+++ b/src/devices/video/v9938.h
@@ -0,0 +1,250 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Nathan Woods
+/***************************************************************************
+
+ v9938 / v9958 emulation
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __V9938_H__
+#define __V9938_H__
+
+
+
+//**************************************************************************
+// DEVICE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_V9938_ADD(_tag, _screen, _vramsize, _clock) \
+ MCFG_DEVICE_ADD(_tag, V9938, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen) \
+ v9938_device::static_set_vram_size(*device, _vramsize);
+#define MCFG_V9958_ADD(_tag, _screen, _vramsize, _clock) \
+ MCFG_DEVICE_ADD(_tag, V9958, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen) \
+ v9938_device::static_set_vram_size(*device, _vramsize);
+
+#define MCFG_V99X8_INTERRUPT_CALLBACK(_irq) \
+ downcast<v99x8_device *>(device)->set_interrupt_callback(DEVCB_##_irq);
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+extern const device_type V9938;
+extern const device_type V9958;
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> v99x8_device
+
+class v99x8_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+protected:
+ // construction/destruction
+ v99x8_device(const machine_config &mconfig, device_type type, const char *name, const char *shortname, const char *tag, device_t *owner, UINT32 clock);
+
+public:
+ template<class _irq> void set_interrupt_callback(_irq irq) {
+ m_int_callback.set_callback(irq);
+ }
+ int interrupt ();
+ int get_transpen();
+ bitmap_ind16 &get_bitmap() { return m_bitmap; }
+ void update_mouse_state(int mx_delta, int my_delta, int button_state);
+
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ UINT8 vram_r();
+ UINT8 status_r();
+ void palette_w(UINT8 data);
+ void vram_w(UINT8 data);
+ void command_w(UINT8 data);
+ void register_w(UINT8 data);
+
+ static void static_set_vram_size(device_t &device, UINT32 vram_size);
+
+ /* RESET pin */
+ void reset_line(int state) { if (state==ASSERT_LINE) device_reset(); }
+
+protected:
+ const address_space_config m_space_config;
+ address_space* m_vram_space;
+
+ int m_model;
+
+ // device overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_DATA) const { return (spacenum == AS_DATA) ? &m_space_config : NULL; }
+
+private:
+ // internal helpers
+ void reset_palette();
+ void vram_write(int offset, int data);
+ int vram_read(int offset);
+ void check_int();
+ void register_write(int reg, int data);
+
+ void default_border(const pen_t *pens, UINT16 *ln);
+ void graphic7_border(const pen_t *pens, UINT16 *ln);
+ void graphic5_border(const pen_t *pens, UINT16 *ln);
+ void mode_text1(const pen_t *pens, UINT16 *ln, int line);
+ void mode_text2(const pen_t *pens, UINT16 *ln, int line);
+ void mode_multi(const pen_t *pens, UINT16 *ln, int line);
+ void mode_graphic1(const pen_t *pens, UINT16 *ln, int line);
+ void mode_graphic23(const pen_t *pens, UINT16 *ln, int line);
+ void mode_graphic4(const pen_t *pens, UINT16 *ln, int line);
+ void mode_graphic5(const pen_t *pens, UINT16 *ln, int line);
+ void mode_graphic6(const pen_t *pens, UINT16 *ln, int line);
+ void mode_graphic7(const pen_t *pens, UINT16 *ln, int line);
+// template<typename _PixelType, int _Width> void mode_yae(const pen_t *pens, _PixelType *ln, int line);
+// template<typename _PixelType, int _Width> void mode_yjk(const pen_t *pens, _PixelType *ln, int line);
+ void mode_unknown(const pen_t *pens, UINT16 *ln, int line);
+ void default_draw_sprite(const pen_t *pens, UINT16 *ln, UINT8 *col);
+ void graphic5_draw_sprite(const pen_t *pens, UINT16 *ln, UINT8 *col);
+ void graphic7_draw_sprite(const pen_t *pens, UINT16 *ln, UINT8 *col);
+
+ void sprite_mode1(int line, UINT8 *col);
+ void sprite_mode2(int line, UINT8 *col);
+ void set_mode();
+ void refresh_16(int line);
+ void refresh_line(int line);
+
+ void interrupt_start_vblank();
+
+ int VDPVRMP(UINT8 M, int MX, int X, int Y);
+
+ UINT8 VDPpoint5(int MXS, int SX, int SY);
+ UINT8 VDPpoint6(int MXS, int SX, int SY);
+ UINT8 VDPpoint7(int MXS, int SX, int SY);
+ UINT8 VDPpoint8(int MXS, int SX, int SY);
+
+ UINT8 VDPpoint(UINT8 SM, int MXS, int SX, int SY);
+
+ void VDPpsetlowlevel(int addr, UINT8 CL, UINT8 M, UINT8 OP);
+
+ void VDPpset5(int MXD, int DX, int DY, UINT8 CL, UINT8 OP);
+ void VDPpset6(int MXD, int DX, int DY, UINT8 CL, UINT8 OP);
+ void VDPpset7(int MXD, int DX, int DY, UINT8 CL, UINT8 OP);
+ void VDPpset8(int MXD, int DX, int DY, UINT8 CL, UINT8 OP);
+
+ void VDPpset(UINT8 SM, int MXD, int DX, int DY, UINT8 CL, UINT8 OP);
+
+ int get_vdp_timing_value(const int *);
+
+ void srch_engine();
+ void line_engine();
+ void lmmv_engine();
+ void lmmm_engine();
+ void lmcm_engine();
+ void lmmc_engine();
+ void hmmv_engine();
+ void hmmm_engine();
+ void ymmm_engine();
+ void hmmc_engine();
+
+ inline bool v9938_second_field();
+
+ void cpu_to_vdp(UINT8 V);
+ UINT8 vdp_to_cpu();
+ void report_vdp_command(UINT8 Op);
+ UINT8 command_unit_w(UINT8 Op);
+ void update_command();
+
+ // general
+ int m_offset_x, m_offset_y, m_visible_y, m_mode;
+ // palette
+ int m_pal_write_first, m_cmd_write_first;
+ UINT8 m_pal_write, m_cmd_write;
+ UINT8 m_pal_reg[32], m_stat_reg[10], m_cont_reg[48], m_read_ahead;
+ UINT8 m_v9958_sp_mode;
+
+ // memory
+ UINT16 m_address_latch;
+ int m_vram_size;
+
+ // interrupt
+ UINT8 m_int_state;
+ devcb_write_line m_int_callback;
+ int m_scanline;
+ // blinking
+ int m_blink, m_blink_count;
+ // mouse
+ UINT8 m_mx_delta, m_my_delta;
+ // mouse & lightpen
+ UINT8 m_button_state;
+ // palette
+ UINT16 m_pal_ind16[16];
+ UINT16 m_pal_ind256[256];
+ // render bitmap
+ bitmap_ind16 m_bitmap;
+ // Command unit
+ struct {
+ int SX,SY;
+ int DX,DY;
+ int TX,TY;
+ int NX,NY;
+ int MX;
+ int ASX,ADX,ANX;
+ UINT8 CL;
+ UINT8 LO;
+ UINT8 CM;
+ UINT8 MXS, MXD;
+ } m_mmc;
+ int m_vdp_ops_count;
+ void (v99x8_device::*m_vdp_engine)();
+
+ struct v99x8_mode
+ {
+ UINT8 m;
+ void (v99x8_device::*visible_16)(const pen_t *, UINT16*, int);
+ void (v99x8_device::*border_16)(const pen_t *, UINT16*);
+ void (v99x8_device::*sprites)(int, UINT8*);
+ void (v99x8_device::*draw_sprite_16)(const pen_t *, UINT16*, UINT8*);
+ } ;
+ static const v99x8_mode s_modes[];
+ required_device<palette_device> m_palette;
+protected:
+ static UINT16 s_pal_indYJK[0x20000];
+};
+
+
+class v9938_device : public v99x8_device
+{
+public:
+ v9938_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_PALETTE_INIT(v9938);
+protected:
+ virtual machine_config_constructor device_mconfig_additions() const;
+};
+
+class v9958_device : public v99x8_device
+{
+public:
+ v9958_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_PALETTE_INIT(v9958);
+
+protected:
+ virtual machine_config_constructor device_mconfig_additions() const;
+};
+
+
+#endif
diff --git a/src/devices/video/vector.c b/src/devices/video/vector.c
new file mode 100644
index 00000000000..5e88ad286d9
--- /dev/null
+++ b/src/devices/video/vector.c
@@ -0,0 +1,307 @@
+// license:BSD-3-Clause
+// copyright-holders:Brad Oliver,Aaron Giles,Bernd Wiebelt,Allard van der Bas
+/******************************************************************************
+ *
+ * vector.c
+ *
+ * anti-alias code by Andrew Caldwell
+ * (still more to add)
+ *
+ * 040227 Fixed miny clip scaling which was breaking in mhavoc. AREK
+ * 010903 added support for direct RGB modes MLR
+ * 980611 use translucent vectors. Thanks to Peter Hirschberg
+ * and Neil Bradley for the inspiration. BW
+ * 980307 added cleverer dirty handling. BW, ASG
+ * fixed antialias table .ac
+ * 980221 rewrote anti-alias line draw routine
+ * added inline assembly multiply fuction for 8086 based machines
+ * beam diameter added to draw routine
+ * beam diameter is accurate in anti-alias line draw (Tcosin)
+ * flicker added .ac
+ * 980203 moved LBO's routines for drawing into a buffer of vertices
+ * from avgdvg.c to this location. Scaling is now initialized
+ * by calling vector_init(...). BW
+ * 980202 moved out of msdos.c ASG
+ * 980124 added anti-alias line draw routine
+ * modified avgdvg.c and sega.c to support new line draw routine
+ * added two new tables Tinten and Tmerge (for 256 color support)
+ * added find_color routine to build above tables .ac
+ *
+ **************************************************************************** */
+
+#include "emu.h"
+#include "emuopts.h"
+#include "rendutil.h"
+#include "vector.h"
+
+
+
+#define VECTOR_WIDTH_DENOM 512
+
+
+#define MAX_POINTS 10000
+
+#define VECTOR_TEAM \
+ "-* Vector Heads *-\n" \
+ "Brad Oliver\n" \
+ "Aaron Giles\n" \
+ "Bernd Wiebelt\n" \
+ "Allard van der Bas\n" \
+ "Al Kossow (VECSIM)\n" \
+ "Hedley Rainnie (VECSIM)\n" \
+ "Eric Smith (VECSIM)\n" \
+ "Neil Bradley (technical advice)\n" \
+ "Andrew Caldwell (anti-aliasing)\n" \
+ "- *** -\n"
+
+#if 0
+
+#define TEXTURE_LENGTH_BUCKETS 32
+#define TEXTURE_INTENSITY_BUCKETS 4
+#define TEXTURE_WIDTH 16
+
+#define MAX_INTENSITY 2
+#define VECTOR_BLEED (0.25f)
+#define VECTOR_INT_SCALE (255.0f * 1.5f)
+
+
+struct vector_texture
+{
+ render_texture * texture;
+ bitmap_argb32 * bitmap;
+};
+
+static vector_texture *vectortex[TEXTURE_INTENSITY_BUCKETS][TEXTURE_LENGTH_BUCKETS];
+
+
+static render_texture *get_vector_texture(float dx, float dy, float intensity)
+{
+ float length = sqrt(dx * dx + dy * dy);
+ int lbucket = length * (float)TEXTURE_LENGTH_BUCKETS;
+ int ibucket = (intensity / (float)MAX_INTENSITY) * (float)TEXTURE_INTENSITY_BUCKETS;
+ vector_texture *tex;
+ int height, x, y;
+ float totalint;
+
+ if (lbucket > TEXTURE_LENGTH_BUCKETS)
+ lbucket = TEXTURE_LENGTH_BUCKETS;
+ if (ibucket > TEXTURE_INTENSITY_BUCKETS)
+ ibucket = TEXTURE_INTENSITY_BUCKETS;
+
+ tex = &vectortex[ibucket][lbucket];
+ if (tex->texture != NULL)
+ return tex->texture;
+
+ height = lbucket * VECTOR_WIDTH_DENOM / TEXTURE_LENGTH_BUCKETS;
+ tex->bitmap = global_alloc(bitmap_argb32(TEXTURE_WIDTH, height));
+ tex->bitmap.fill(rgb_t(0xff,0xff,0xff,0xff));
+
+ totalint = 1.0f;
+ for (x = TEXTURE_WIDTH / 2 - 1; x >= 0; x--)
+ {
+ int intensity = (int)(totalint * (1.0f - VECTOR_BLEED) * VECTOR_INT_SCALE);
+ intensity = MIN(255, intensity);
+ totalint -= (float)intensity * (1.0f / VECTOR_INT_SCALE);
+
+ for (y = 0; y < height; y++)
+ {
+ UINT32 *pix;
+
+ pix = (UINT32 *)bitmap.base + y * bitmap.rowpixels + x;
+ *pix = rgb_t((*pix.a() * intensity) >> 8,0xff,0xff,0xff);
+
+ pix = (UINT32 *)bitmap.base + y * bitmap.rowpixels + (TEXTURE_WIDTH - 1 - x);
+ *pix = rgb_t((*pix.a() * intensity) >> 8,0xff,0xff,0xff);
+ }
+ }
+
+ tex->texture = render_texture_create();
+ return tex->texture;
+}
+
+#endif
+
+#define VCLEAN 0
+#define VDIRTY 1
+#define VCLIP 2
+
+// device type definition
+const device_type VECTOR = &device_creator<vector_device>;
+
+vector_device::vector_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_video_interface(mconfig, *this)
+{
+}
+
+vector_device::vector_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, VECTOR, "VECTOR", tag, owner, clock, "vector_device", __FILE__),
+ device_video_interface(mconfig, *this)
+{
+}
+
+float vector_device::m_flicker_correction = 0.0f;
+float vector_device::m_beam_width = 0.0f;
+int vector_device::m_flicker;
+int vector_device::m_vector_index;
+
+void vector_device::device_start()
+{
+ m_beam_width = machine().options().beam();
+
+ /* Grab the settings for this session */
+ set_flicker(machine().options().flicker());
+
+ m_vector_index = 0;
+
+ /* allocate memory for tables */
+ m_vector_list = auto_alloc_array_clear(machine(), point, MAX_POINTS);
+}
+
+void vector_device::set_flicker(float _flicker)
+{
+ m_flicker_correction = _flicker;
+ m_flicker = (int)(m_flicker_correction * 2.55f);
+}
+
+float vector_device::get_flicker()
+{
+ return m_flicker_correction;
+}
+
+void vector_device::set_beam(float _beam)
+{
+ m_beam_width = _beam;
+}
+
+float vector_device::get_beam()
+{
+ return m_beam_width;
+}
+
+
+/*
+ * Adds a line end point to the vertices list. The vector processor emulation
+ * needs to call this.
+ */
+void vector_device::add_point (int x, int y, rgb_t color, int intensity)
+{
+ point *newpoint;
+
+ if (intensity > 0xff)
+ intensity = 0xff;
+
+ if (m_flicker && (intensity > 0))
+ {
+ intensity += (intensity * (0x80-(machine().rand()&0xff)) * m_flicker)>>16;
+ if (intensity < 0)
+ intensity = 0;
+ if (intensity > 0xff)
+ intensity = 0xff;
+ }
+ newpoint = &m_vector_list[m_vector_index];
+ newpoint->x = x;
+ newpoint->y = y;
+ newpoint->col = color;
+ newpoint->intensity = intensity;
+ newpoint->status = VDIRTY; /* mark identical lines as clean later */
+
+ m_vector_index++;
+ if (m_vector_index >= MAX_POINTS)
+ {
+ m_vector_index--;
+ logerror("*** Warning! Vector list overflow!\n");
+ }
+}
+
+/*
+ * Add new clipping info to the list
+ */
+void vector_device::add_clip (int x1, int yy1, int x2, int y2)
+{
+ point *newpoint;
+
+ newpoint = &m_vector_list[m_vector_index];
+ newpoint->x = x1;
+ newpoint->y = yy1;
+ newpoint->arg1 = x2;
+ newpoint->arg2 = y2;
+ newpoint->status = VCLIP;
+
+ m_vector_index++;
+ if (m_vector_index >= MAX_POINTS)
+ {
+ m_vector_index--;
+ logerror("*** Warning! Vector list overflow!\n");
+ }
+}
+
+
+/*
+ * The vector CPU creates a new display list. We save the old display list,
+ * but only once per refresh.
+ */
+void vector_device::clear_list (void)
+{
+ m_vector_index = 0;
+}
+
+
+UINT32 vector_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ UINT32 flags = PRIMFLAG_ANTIALIAS(screen.machine().options().antialias() ? 1 : 0) | PRIMFLAG_BLENDMODE(BLENDMODE_ADD) | PRIMFLAG_VECTOR(1);
+ const rectangle &visarea = screen.visible_area();
+ float xscale = 1.0f / (65536 * visarea.width());
+ float yscale = 1.0f / (65536 * visarea.height());
+ float xoffs = (float)visarea.min_x;
+ float yoffs = (float)visarea.min_y;
+ point *curpoint;
+ render_bounds clip;
+ int lastx = 0, lasty = 0;
+ int i;
+
+ curpoint = m_vector_list;
+
+ screen.container().empty();
+ screen.container().add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(0xff,0x00,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_VECTORBUF(1));
+
+ clip.x0 = clip.y0 = 0.0f;
+ clip.x1 = clip.y1 = 1.0f;
+
+ for (i = 0; i < m_vector_index; i++)
+ {
+ render_bounds coords;
+
+ if (curpoint->status == VCLIP)
+ {
+ coords.x0 = ((float)curpoint->x - xoffs) * xscale;
+ coords.y0 = ((float)curpoint->y - yoffs) * yscale;
+ coords.x1 = ((float)curpoint->arg1 - xoffs) * xscale;
+ coords.y1 = ((float)curpoint->arg2 - yoffs) * yscale;
+
+ clip.x0 = (coords.x0 > 0.0f) ? coords.x0 : 0.0f;
+ clip.y0 = (coords.y0 > 0.0f) ? coords.y0 : 0.0f;
+ clip.x1 = (coords.x1 < 1.0f) ? coords.x1 : 1.0f;
+ clip.y1 = (coords.y1 < 1.0f) ? coords.y1 : 1.0f;
+ }
+ else
+ {
+ coords.x0 = ((float)lastx - xoffs) * xscale;
+ coords.y0 = ((float)lasty - yoffs) * yscale;
+ coords.x1 = ((float)curpoint->x - xoffs) * xscale;
+ coords.y1 = ((float)curpoint->y - yoffs) * yscale;
+
+ if (curpoint->intensity != 0)
+ if (!render_clip_line(&coords, &clip))
+ screen.container().add_line(coords.x0, coords.y0, coords.x1, coords.y1,
+ m_beam_width * (1.0f / (float)VECTOR_WIDTH_DENOM),
+ (curpoint->intensity << 24) | (curpoint->col & 0xffffff),
+ flags);
+
+ lastx = curpoint->x;
+ lasty = curpoint->y;
+ }
+ curpoint++;
+ }
+ return 0;
+}
diff --git a/src/devices/video/vector.h b/src/devices/video/vector.h
new file mode 100644
index 00000000000..2e922d4a922
--- /dev/null
+++ b/src/devices/video/vector.h
@@ -0,0 +1,73 @@
+// license:BSD-3-Clause
+// copyright-holders:Brad Oliver,Aaron Giles,Bernd Wiebelt,Allard van der Bas
+#ifndef __VECTOR__
+#define __VECTOR__
+
+#define VECTOR_COLOR111(c) \
+ rgb_t(pal1bit((c) >> 2), pal1bit((c) >> 1), pal1bit((c) >> 0))
+
+#define VECTOR_COLOR222(c) \
+ rgb_t(pal2bit((c) >> 4), pal2bit((c) >> 2), pal2bit((c) >> 0))
+
+#define VECTOR_COLOR444(c) \
+ rgb_t(pal4bit((c) >> 8), pal4bit((c) >> 4), pal4bit((c) >> 0))
+
+
+/* The vertices are buffered here */
+struct point
+{
+ point():
+ x(0),
+ y(0),
+ col(0),
+ intensity(0),
+ arg1(0),
+ arg2(0),
+ status(0) {}
+
+ int x; int y;
+ rgb_t col;
+ int intensity;
+ int arg1; int arg2; /* start/end in pixel array or clipping info */
+ int status; /* for dirty and clipping handling */
+};
+
+class vector_device : public device_t,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ vector_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ vector_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void clear_list();
+
+ void add_point(int x, int y, rgb_t color, int intensity);
+ void add_clip(int minx, int miny, int maxx, int maxy);
+
+ void set_flicker(float m_flicker_correction);
+ float get_flicker();
+
+ void set_beam(float _beam);
+ float get_beam();
+
+ // device-level overrides
+ virtual void device_start();
+
+private:
+ static int m_flicker; /* beam flicker value */
+ static float m_flicker_correction;
+ static float m_beam_width;
+ point *m_vector_list;
+ static int m_vector_index;
+};
+
+
+// device type definition
+extern const device_type VECTOR;
+
+#define MCFG_VECTOR_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, VECTOR, 0)
+
+#endif
diff --git a/src/devices/video/vic4567.c b/src/devices/video/vic4567.c
new file mode 100644
index 00000000000..ca98279365b
--- /dev/null
+++ b/src/devices/video/vic4567.c
@@ -0,0 +1,2055 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+/***************************************************************************
+
+ Video Interface Chip (4567)
+
+ original emulation by PeT (mess@utanet.at)
+
+ 2010-02: converted to be a device and split from vic II
+
+ TODO:
+ - plenty of cleanups
+ - emulate variants of the vic chip
+ - update vic III to use the new vic6567.c code for the vic II comaptibility
+
+***************************************************************************/
+
+#include "emu.h"
+#include "video/vic4567.h"
+
+/*****************************************************************************
+ CONSTANTS
+*****************************************************************************/
+
+#define VERBOSE_LEVEL 0
+#define DBG_LOG(N,M,A) \
+ do { \
+ if(VERBOSE_LEVEL >= N) \
+ { \
+ if( M ) \
+ logerror("%11.6f: %-24s", machine().time().as_double(), (char*) M ); \
+ logerror A; \
+ } \
+ } while (0)
+
+#define VREFRESHINLINES 28
+
+#define VIC2_YPOS 50
+#define RASTERLINE_2_C64(a) (a)
+#define C64_2_RASTERLINE(a) (a)
+#define XPOS (VIC2_STARTVISIBLECOLUMNS + (VIC2_VISIBLECOLUMNS - VIC2_HSIZE) / 2)
+#define YPOS (VIC2_STARTVISIBLELINES /* + (VIC2_VISIBLELINES - VIC2_VSIZE) / 2 */)
+#define FIRSTLINE 10 /* 36 ((VIC2_VISIBLELINES - VIC2_VSIZE)/2) */
+#define FIRSTCOLUMN 50
+
+/* 2008-05 FP: lightpen code needs to read input port from c64.c and cbmb.c */
+
+#define LIGHTPEN_BUTTON (m_lightpen_button_cb(0))
+#define LIGHTPEN_X_VALUE (m_lightpen_x_cb(0))
+#define LIGHTPEN_Y_VALUE (m_lightpen_y_cb(0))
+
+/* lightpen delivers values from internal counters; they do not start with the visual area or frame area */
+#define VIC2_MAME_XPOS 0
+#define VIC2_MAME_YPOS 0
+#define VIC6567_X_BEGIN 38
+#define VIC6567_Y_BEGIN -6 /* first 6 lines after retrace not for lightpen! */
+#define VIC6569_X_BEGIN 38
+#define VIC6569_Y_BEGIN -6
+#define VIC2_X_BEGIN ((m_type == VIC4567_PAL) ? VIC6569_X_BEGIN : VIC6567_X_BEGIN)
+#define VIC2_Y_BEGIN ((m_type == VIC4567_PAL) ? VIC6569_Y_BEGIN : VIC6567_Y_BEGIN)
+#define VIC2_X_VALUE ((LIGHTPEN_X_VALUE + VIC2_X_BEGIN + VIC2_MAME_XPOS) / 2)
+#define VIC2_Y_VALUE ((LIGHTPEN_Y_VALUE + VIC2_Y_BEGIN + VIC2_MAME_YPOS))
+
+#define VIC2E_K0_LEVEL (m_reg[0x2f] & 0x01)
+#define VIC2E_K1_LEVEL (m_reg[0x2f] & 0x02)
+#define VIC2E_K2_LEVEL (m_reg[0x2f] & 0x04)
+
+/*#define VIC3_P5_LEVEL (m_reg[0x30] & 0x20) */
+#define VIC3_BITPLANES (m_reg[0x31] & 0x10)
+#define VIC3_80COLUMNS (m_reg[0x31] & 0x80)
+#define VIC3_LINES ((m_reg[0x31] & 0x19) == 0x19 ? 400 : 200)
+#define VIC3_BITPLANES_WIDTH (m_reg[0x31] & 0x80 ? 640 : 320)
+
+/*#define VIC2E_TEST (vic2[0x30] & 2) */
+#define DOUBLE_CLOCK (m_reg[0x30] & 0x01)
+
+/* sprites 0 .. 7 */
+#define SPRITEON(nr) (m_reg[0x15] & (1 << nr))
+#define SPRITE_Y_EXPAND(nr) (m_reg[0x17] & (1 << nr))
+#define SPRITE_Y_SIZE(nr) (SPRITE_Y_EXPAND(nr) ? 2 * 21 : 21)
+#define SPRITE_X_EXPAND(nr) (m_reg[0x1d] & (1 << nr))
+#define SPRITE_X_SIZE(nr) (SPRITE_X_EXPAND(nr) ? 2 * 24 : 24)
+#define SPRITE_X_POS(nr) ((m_reg[(nr) * 2] | (m_reg[0x10] & (1 <<(nr)) ? 0x100 : 0)) - 24 + XPOS)
+#define SPRITE_X_POS2(nr) (m_reg[(nr) * 2] | (m_reg[0x10] & (1 <<(nr)) ? 0x100 : 0))
+#define SPRITE_Y_POS(nr) (m_reg[1+2*(nr)] - 50 + YPOS)
+#define SPRITE_Y_POS2(nr) (m_reg[1 + 2 *(nr)])
+#define SPRITE_MULTICOLOR(nr) (m_reg[0x1c] & (1 << nr))
+#define SPRITE_PRIORITY(nr) (m_reg[0x1b] & (1 << nr))
+#define SPRITE_MULTICOLOR1 (m_reg[0x25] & 0x0f)
+#define SPRITE_MULTICOLOR2 (m_reg[0x26] & 0x0f)
+#define SPRITE_COLOR(nr) (m_reg[0x27+nr] & 0x0f)
+#define SPRITE_ADDR(nr) (m_videoaddr | 0x3f8 | nr)
+#define SPRITE_BG_COLLISION(nr) (m_reg[0x1f] & (1 << nr))
+#define SPRITE_COLLISION(nr) (m_reg[0x1e] & (1 << nr))
+#define SPRITE_SET_BG_COLLISION(nr) (m_reg[0x1f] |= (1 << nr))
+#define SPRITE_SET_COLLISION(nr) (m_reg[0x1e] |= (1 << nr))
+#define SPRITE_COLL (m_reg[0x1e])
+#define SPRITE_BG_COLL (m_reg[0x1f])
+
+#define GFXMODE ((m_reg[0x11] & 0x60) | (m_reg[0x16] & 0x10)) >> 4
+#define SCREENON (m_reg[0x11] & 0x10)
+#define VERTICALPOS (m_reg[0x11] & 0x07)
+#define HORIZONTALPOS (m_reg[0x16] & 0x07)
+#define ECMON (m_reg[0x11] & 0x40)
+#define HIRESON (m_reg[0x11] & 0x20)
+#define MULTICOLORON (m_reg[0x16] & 0x10)
+#define LINES25 (m_reg[0x11] & 0x08) /* else 24 Lines */
+#define LINES (LINES25 ? 25 : 24)
+#define YSIZE (LINES * 8)
+#define COLUMNS40 (m_reg[0x16] & 0x08) /* else 38 Columns */
+#define COLUMNS (COLUMNS40 ? 40 : 38)
+#define XSIZE (COLUMNS * 8)
+
+#define VIDEOADDR ((m_reg[0x18] & 0xf0) << (10 - 4))
+#define CHARGENADDR ((m_reg[0x18] & 0x0e) << 10)
+#define BITMAPADDR ((data & 0x08) << 10)
+
+#define RASTERLINE (((m_reg[0x11] & 0x80) << 1) | m_reg[0x12])
+
+#define FRAMECOLOR (m_reg[0x20] & 0x0f)
+#define BACKGROUNDCOLOR (m_reg[0x21] & 0x0f)
+#define MULTICOLOR1 (m_reg[0x22] & 0x0f)
+#define MULTICOLOR2 (m_reg[0x23] & 0x0f)
+#define FOREGROUNDCOLOR (m_reg[0x24] & 0x0f)
+
+
+#define VIC2_LINES (m_type == VIC4567_PAL ? VIC6569_LINES : VIC6567_LINES)
+#define VIC2_VISIBLELINES (m_type == VIC4567_PAL ? VIC6569_VISIBLELINES : VIC6567_VISIBLELINES)
+#define VIC2_VISIBLECOLUMNS (m_type == VIC4567_PAL ? VIC6569_VISIBLECOLUMNS : VIC6567_VISIBLECOLUMNS)
+#define VIC2_STARTVISIBLELINES ((VIC2_LINES - VIC2_VISIBLELINES)/2)
+#define VIC2_FIRSTRASTERLINE (m_type == VIC4567_PAL ? VIC6569_FIRSTRASTERLINE : VIC6567_FIRSTRASTERLINE)
+#define VIC2_COLUMNS (m_type == VIC4567_PAL ? VIC6569_COLUMNS : VIC6567_COLUMNS)
+#define VIC2_STARTVISIBLECOLUMNS ((VIC2_COLUMNS - VIC2_VISIBLECOLUMNS)/2)
+
+#define VIC3_BITPLANES_MASK (m_reg[0x32])
+/* bit 0, 4 not used !?*/
+/* I think hinibbles contains the banknumbers for interlaced modes */
+/* if hinibble set then x&1==0 should be in bank1 (0x10000), x&1==1 in bank 0 */
+#define VIC3_BITPLANE_ADDR_HELPER(x) ((m_reg[0x33 + x] & 0x0f) << 12)
+#define VIC3_BITPLANE_ADDR(x) (x & 1 ? VIC3_BITPLANE_ADDR_HELPER(x) + 0x10000 : VIC3_BITPLANE_ADDR_HELPER(x) )
+#define VIC3_BITPLANE_IADDR_HELPER(x) ((m_reg[0x33 + x] & 0xf0) << 8)
+#define VIC3_BITPLANE_IADDR(x) (x & 1 ? VIC3_BITPLANE_IADDR_HELPER(x) + 0x10000 : VIC3_BITPLANE_IADDR_HELPER(x))
+
+
+const device_type VIC3 = &device_creator<vic3_device>;
+
+vic3_device::vic3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, VIC3, "4567 VIC III", tag, owner, clock, "vic3", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_type(VIC4567_NTSC),
+ m_cpu(*this),
+ m_dma_read_cb(*this),
+ m_dma_read_color_cb(*this),
+ m_interrupt_cb(*this),
+ m_port_changed_cb(*this),
+ m_lightpen_button_cb(*this),
+ m_lightpen_x_cb(*this),
+ m_lightpen_y_cb(*this),
+ m_c64_mem_r_cb(*this),
+ m_palette(*this, "palette")
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void vic3_device::device_start()
+{
+ int width, height;
+
+ width = m_screen->width();
+ height = m_screen->height();
+
+ m_bitmap = auto_bitmap_ind16_alloc(machine(), width, height);
+
+ m_dma_read_cb.resolve_safe(0);
+ m_dma_read_color_cb.resolve_safe(0);
+ m_interrupt_cb.resolve_safe();
+
+ m_port_changed_cb.resolve();
+
+ m_c64_mem_r_cb.resolve_safe(0);
+
+ m_lightpen_button_cb.resolve_safe(0);
+ m_lightpen_x_cb.resolve_safe(0);
+ m_lightpen_y_cb.resolve_safe(0);
+
+ m_screenptr[0] = auto_alloc_array(machine(), UINT8, 216 * 656 / 8);
+
+ for (int i = 1; i < 216; i++)
+ m_screenptr[i] = m_screenptr[i - 1] + 656 / 8;
+
+ for (int i = 0; i < 256; i++)
+ {
+ m_foreground[i] = 0;
+ if ((i & 3) > 1)
+ m_foreground[i] |= 0x3;
+ if ((i & 0xc) > 0x4)
+ m_foreground[i] |= 0xc;
+ if ((i & 0x30) > 0x10)
+ m_foreground[i] |= 0x30;
+ if ((i & 0xc0) > 0x40)
+ m_foreground[i] |= 0xc0;
+ }
+
+ for (int i = 0; i < 256; i++)
+ {
+ m_expandx[i] = 0;
+ if (i & 1)
+ m_expandx[i] |= 3;
+ if (i & 2)
+ m_expandx[i] |= 0xc;
+ if (i & 4)
+ m_expandx[i] |= 0x30;
+ if (i & 8)
+ m_expandx[i] |= 0xc0;
+ if (i & 0x10)
+ m_expandx[i] |= 0x300;
+ if (i & 0x20)
+ m_expandx[i] |= 0xc00;
+ if (i & 0x40)
+ m_expandx[i] |= 0x3000;
+ if (i & 0x80)
+ m_expandx[i] |= 0xc000;
+ }
+
+ for (int i = 0; i < 256; i++)
+ {
+ m_expandx_multi[i] = 0;
+ if (i & 1)
+ m_expandx_multi[i] |= 5;
+ if (i & 2)
+ m_expandx_multi[i] |= 0xa;
+ if (i & 4)
+ m_expandx_multi[i] |= 0x50;
+ if (i & 8)
+ m_expandx_multi[i] |= 0xa0;
+ if (i & 0x10)
+ m_expandx_multi[i] |= 0x500;
+ if (i & 0x20)
+ m_expandx_multi[i] |= 0xa00;
+ if (i & 0x40)
+ m_expandx_multi[i] |= 0x5000;
+ if (i & 0x80)
+ m_expandx_multi[i] |= 0xa000;
+ }
+
+ save_item(NAME(m_reg));
+
+ save_item(NAME(m_on));
+
+ //save_item(NAME(m_bitmap));
+
+ save_item(NAME(m_lines));
+
+ save_item(NAME(m_chargenaddr));
+ save_item(NAME(m_videoaddr));
+ save_item(NAME(m_bitmapaddr));
+
+ save_item(NAME(m_x_begin));
+ save_item(NAME(m_x_end));
+ save_item(NAME(m_y_begin));
+ save_item(NAME(m_y_end));
+
+ save_item(NAME(m_c64_bitmap));
+ save_item(NAME(m_bitmapmulti));
+ save_item(NAME(m_mono));
+ save_item(NAME(m_multi));
+ save_item(NAME(m_ecmcolor));
+ save_item(NAME(m_colors));
+ save_item(NAME(m_spritemulti));
+
+ save_item(NAME(m_lastline));
+ save_item(NAME(m_rasterline));
+ save_item(NAME(m_interlace));
+
+ save_item(NAME(m_columns));
+ save_item(NAME(m_rows));
+
+ save_item(NAME(m_shift));
+ save_item(NAME(m_foreground));
+ save_item(NAME(m_multi_collision));
+
+ save_item(NAME(m_palette_red));
+ save_item(NAME(m_palette_green));
+ save_item(NAME(m_palette_blue));
+ save_item(NAME(m_palette_dirty));
+
+ for (int i = 0; i < 8; i++)
+ {
+ save_item(NAME(m_sprites[i].x), i);
+ save_item(NAME(m_sprites[i].y), i);
+ save_item(NAME(m_sprites[i].repeat), i);
+ save_item(NAME(m_sprites[i].line), i);
+ save_item(NAME(m_sprites[i].paintedline), i);
+ save_item(NAME(m_sprites[i].bitmap[0]), i);
+ save_item(NAME(m_sprites[i].bitmap[1]), i);
+ save_item(NAME(m_sprites[i].bitmap[2]), i);
+ save_item(NAME(m_sprites[i].bitmap[3]), i);
+ save_item(NAME(m_sprites[i].bitmap[4]), i);
+ save_item(NAME(m_sprites[i].bitmap[5]), i);
+ save_item(NAME(m_sprites[i].bitmap[6]), i);
+ save_item(NAME(m_sprites[i].bitmap[7]), i);
+ }
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void vic3_device::device_reset()
+{
+ memset(m_reg, 0, ARRAY_LENGTH(m_reg));
+
+ m_on = 1;
+
+ m_interlace = 0;
+ m_columns = 640;
+ m_rows = 200;
+ m_lines = VIC2_LINES;
+
+ memset(&m_sprites, 0, sizeof(m_sprites));
+
+ m_chargenaddr = 0;
+ m_videoaddr = 0;
+ m_bitmapaddr = 0;
+
+ m_x_begin = 0;
+ m_x_end = 0;
+ m_y_begin = 0;
+ m_y_end = 0;
+
+ for (int i = 0; i < 2; i++)
+ {
+ m_c64_bitmap[i] = 0;
+ m_mono[i] = 0;
+ m_ecmcolor[i] = 0;
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ m_bitmapmulti[i] = 0;
+ m_multi[i] = 0;
+ m_colors[i] = 0;
+ m_spritemulti[i] = 0;
+ }
+
+ m_lastline = 0;
+ m_rasterline = 0;
+
+ memset(m_shift, 0, ARRAY_LENGTH(m_shift));
+ memset(m_multi_collision, 0, ARRAY_LENGTH(m_multi_collision));
+ memset(m_palette_red, 0, ARRAY_LENGTH(m_palette_red));
+ memset(m_palette_green, 0, ARRAY_LENGTH(m_palette_green));
+ memset(m_palette_blue, 0, ARRAY_LENGTH(m_palette_blue));
+
+ m_palette_dirty = 0;
+}
+
+
+/*****************************************************************************
+ IMPLEMENTATION
+*****************************************************************************/
+
+inline int vic3_device::getforeground( int y, int x )
+{
+ return ((m_screenptr[y][x >> 3] << 8) | (m_screenptr[y][(x >> 3) + 1])) >> (8 - (x & 7));
+}
+
+inline int vic3_device::getforeground16( int y, int x )
+{
+ return ((m_screenptr[y][x >> 3] << 16) | (m_screenptr[y][(x >> 3) + 1] << 8) | (m_screenptr[y][(x >> 3) + 2])) >> (8 - (x & 7));
+}
+
+void vic3_device::set_interrupt( int mask )
+{
+ if (((m_reg[0x19] ^ mask) & m_reg[0x1a] & 0xf))
+ {
+ if (!(m_reg[0x19] & 0x80))
+ {
+ //DBG_LOG(2, "vic2", ("irq start %.2x\n", mask));
+ m_reg[0x19] |= 0x80;
+ m_interrupt_cb(1);
+ }
+ }
+ m_reg[0x19] |= mask;
+}
+
+void vic3_device::clear_interrupt( int mask )
+{
+ m_reg[0x19] &= ~mask;
+ if ((m_reg[0x19] & 0x80) && !(m_reg[0x19] & m_reg[0x1a] & 0xf))
+ {
+ //DBG_LOG(2, "vic2", ("irq end %.2x\n", mask));
+ m_reg[0x19] &= ~0x80;
+ m_interrupt_cb(0);
+ }
+}
+
+TIMER_CALLBACK_MEMBER( vic3_device::timer_timeout )
+{
+ int which = param;
+ //DBG_LOG(3, "vic3 ", ("timer %d timeout\n", which));
+ switch (which)
+ {
+ case 1: /* light pen */
+ /* and diode must recognize light */
+ if (1)
+ {
+ m_reg[0x13] = VIC2_X_VALUE;
+ m_reg[0x14] = VIC2_Y_VALUE;
+ }
+ set_interrupt(8);
+ break;
+ }
+}
+
+void vic3_device::draw_character( int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color, int start_x, int end_x )
+{
+ int code;
+
+ for (int y = ybegin; y <= yend; y++)
+ {
+ code = m_dma_read_cb(m_chargenaddr + ch * 8 + y);
+ m_screenptr[y + yoff][xoff >> 3] = code;
+ if ((xoff + 0 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 0) = color[code >> 7];
+ if ((xoff + 1 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 1) = color[(code >> 6) & 1];
+ if ((xoff + 2 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 2) = color[(code >> 5) & 1];
+ if ((xoff + 3 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 3) = color[(code >> 4) & 1];
+ if ((xoff + 4 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 4) = color[(code >> 3) & 1];
+ if ((xoff + 5 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 5) = color[(code >> 2) & 1];
+ if ((xoff + 6 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 6) = color[(code >> 1) & 1];
+ if ((xoff + 7 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 7) = color[code & 1];
+ }
+}
+
+void vic3_device::draw_character_multi( int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x )
+{
+ int code;
+
+ for (int y = ybegin; y <= yend; y++)
+ {
+ code = m_dma_read_cb(m_chargenaddr + ch * 8 + y);
+ m_screenptr[y + yoff][xoff >> 3] = m_foreground[code];
+ if ((xoff + 0 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 0) = m_multi[code >> 6];
+ if ((xoff + 1 > start_x) && (xoff + 1 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 1) = m_multi[code >> 6];
+ if ((xoff + 2 > start_x) && (xoff + 2 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 2) = m_multi[(code >> 4) & 3];
+ if ((xoff + 3 > start_x) && (xoff + 3 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 3) = m_multi[(code >> 4) & 3];
+ if ((xoff + 4 > start_x) && (xoff + 4 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 4) = m_multi[(code >> 2) & 3];
+ if ((xoff + 5 > start_x) && (xoff + 5 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 5) = m_multi[(code >> 2) & 3];
+ if ((xoff + 6 > start_x) && (xoff + 6 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 6) = m_multi[code & 3];
+ if ((xoff + 7 > start_x) && (xoff + 7 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 7) = m_multi[code & 3];
+ }
+}
+
+void vic3_device::draw_bitmap( int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x )
+{
+ int code;
+
+ for (int y = ybegin; y <= yend; y++)
+ {
+ code = m_dma_read_cb((m_chargenaddr & 0x2000) + ch * 8 + y);
+ m_screenptr[y + yoff][xoff >> 3] = code;
+ if ((xoff + 0 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 0) = m_c64_bitmap[code >> 7];
+ if ((xoff + 1 > start_x) && (xoff + 1 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 1) = m_c64_bitmap[(code >> 6) & 1];
+ if ((xoff + 2 > start_x) && (xoff + 2 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 2) = m_c64_bitmap[(code >> 5) & 1];
+ if ((xoff + 3 > start_x) && (xoff + 3 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 3) = m_c64_bitmap[(code >> 4) & 1];
+ if ((xoff + 4 > start_x) && (xoff + 4 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 4) = m_c64_bitmap[(code >> 3) & 1];
+ if ((xoff + 5 > start_x) && (xoff + 5 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 5) = m_c64_bitmap[(code >> 2) & 1];
+ if ((xoff + 6 > start_x) && (xoff + 6 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 6) = m_c64_bitmap[(code >> 1) & 1];
+ if ((xoff + 7 > start_x) && (xoff + 7 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 7) = m_c64_bitmap[code & 1];
+ }
+}
+
+void vic3_device::draw_bitmap_multi( int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x )
+{
+ int code;
+
+ for (int y = ybegin; y <= yend; y++)
+ {
+ code = m_dma_read_cb((m_chargenaddr & 0x2000) + ch * 8 + y);
+ m_screenptr[y + yoff][xoff >> 3] = m_foreground[code];
+ if ((xoff + 0 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 0) = m_bitmapmulti[code >> 6];
+ if ((xoff + 1 > start_x) && (xoff + 1 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 1) = m_bitmapmulti[code >> 6];
+ if ((xoff + 2 > start_x) && (xoff + 2 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 2) = m_bitmapmulti[(code >> 4) & 3];
+ if ((xoff + 3 > start_x) && (xoff + 3 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 3) = m_bitmapmulti[(code >> 4) & 3];
+ if ((xoff + 4 > start_x) && (xoff + 4 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 4) = m_bitmapmulti[(code >> 2) & 3];
+ if ((xoff + 5 > start_x) && (xoff + 5 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 5) = m_bitmapmulti[(code >> 2) & 3];
+ if ((xoff + 6 > start_x) && (xoff + 6 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 6) = m_bitmapmulti[code & 3];
+ if ((xoff + 7 > start_x) && (xoff + 7 < end_x)) m_bitmap->pix16(y + yoff + FIRSTLINE, xoff + 7) = m_bitmapmulti[code & 3];
+ }
+}
+
+void vic3_device::draw_sprite_code( int y, int xbegin, int code, int color, int start_x, int end_x )
+{
+ register int mask, x;
+
+ if ((y < YPOS) || (y >= (VIC2_STARTVISIBLELINES + VIC2_VISIBLELINES)) || (xbegin <= 1) || (xbegin >= (VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS)))
+ return;
+
+ for (x = 0, mask = 0x80; x < 8; x++, mask >>= 1)
+ {
+ if (code & mask)
+ {
+ if ((xbegin + x > start_x) && (xbegin + x < end_x))
+ m_bitmap->pix16(y + FIRSTLINE, xbegin + x) = color;
+ }
+ }
+}
+
+void vic3_device::draw_sprite_code_multi( int y, int xbegin, int code, int prior, int start_x, int end_x )
+{
+ register int x, mask, shift;
+
+ if ((y < YPOS) || (y >= (VIC2_STARTVISIBLELINES + VIC2_VISIBLELINES)) || (xbegin <= 1) || (xbegin >= (VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS)))
+ return;
+
+ for (x = 0, mask = 0xc0, shift = 6; x < 8; x += 2, mask >>= 2, shift -= 2)
+ {
+ if (code & mask)
+ {
+ switch ((prior & mask) >> shift)
+ {
+ case 1:
+ if ((xbegin + x + 1 > start_x) && (xbegin + x + 1 < end_x))
+ m_bitmap->pix16(y + FIRSTLINE, xbegin + x + 1) = m_spritemulti[(code >> shift) & 3];
+ break;
+ case 2:
+ if ((xbegin + x > start_x) && (xbegin + x < end_x))
+ m_bitmap->pix16(y + FIRSTLINE, xbegin + x) = m_spritemulti[(code >> shift) & 3];
+ break;
+ case 3:
+ if ((xbegin + x > start_x) && (xbegin + x < end_x))
+ m_bitmap->pix16(y + FIRSTLINE, xbegin + x) = m_spritemulti[(code >> shift) & 3];
+ if ((xbegin + x + 1> start_x) && (xbegin + x + 1< end_x))
+ m_bitmap->pix16(y + FIRSTLINE, xbegin + x + 1) = m_spritemulti[(code >> shift) & 3];
+ break;
+ }
+ }
+ }
+}
+
+void vic3_device::sprite_collision( int nr, int y, int x, int mask )
+{
+ int value, xdiff;
+
+ for (int i = 7; i > nr; i--)
+ {
+ if (!SPRITEON(i) || !m_sprites[i].paintedline[y] || (SPRITE_COLLISION(i) && SPRITE_COLLISION(nr)))
+ continue;
+
+ if ((x + 7 < SPRITE_X_POS(i)) || (x >= SPRITE_X_POS(i) + SPRITE_X_SIZE(i)))
+ continue;
+
+ xdiff = x - SPRITE_X_POS(i);
+
+ if ((x & 7) == (SPRITE_X_POS(i) & 7))
+ value = m_sprites[i].bitmap[y][xdiff >> 3];
+ else if (xdiff < 0)
+ value = m_sprites[i].bitmap[y][0] >> (-xdiff);
+ else {
+ UINT8 *vp = m_sprites[i].bitmap[y]+(xdiff >> 3);
+ value = ((vp[1] | (*vp << 8)) >> (8 - (xdiff & 7) )) & 0xff;
+ }
+
+ if (value & mask)
+ {
+ SPRITE_SET_COLLISION(i);
+ SPRITE_SET_COLLISION(nr);
+ set_interrupt(4);
+ }
+ }
+}
+
+void vic3_device::draw_sprite( int nr, int yoff, int ybegin, int yend, int start_x, int end_x )
+{
+ int y, i, addr, xbegin, color, prior, collision;
+ int value, value3 = 0;
+
+ xbegin = SPRITE_X_POS(nr);
+ addr = m_dma_read_cb(SPRITE_ADDR(nr)) << 6;
+ color = SPRITE_COLOR(nr);
+ prior = SPRITE_PRIORITY(nr);
+ collision = SPRITE_BG_COLLISION(nr);
+
+ if (SPRITE_X_EXPAND(nr))
+ {
+ for (y = ybegin; y <= yend; y++)
+ {
+ m_sprites[nr].paintedline[y] = 1;
+ for (i = 0; i < 3; i++)
+ {
+ value = m_expandx[m_dma_read_cb(addr + m_sprites[nr].line * 3 + i)];
+ m_sprites[nr].bitmap[y][i * 2] = value >> 8;
+ m_sprites[nr].bitmap[y][i * 2 + 1] = value & 0xff;
+ sprite_collision(nr, y, xbegin + i * 16, value >> 8);
+ sprite_collision(nr, y, xbegin + i * 16 + 8, value & 0xff);
+ if (prior || !collision)
+ value3 = getforeground16(yoff + y, xbegin + i * 16 - 7);
+ if (!collision && (value & value3))
+ {
+ collision = 1;
+ SPRITE_SET_BG_COLLISION(nr);
+ set_interrupt(2);
+ }
+ if (prior)
+ value &= ~value3;
+ draw_sprite_code(yoff + y, xbegin + i * 16, value >> 8, color, start_x, end_x);
+ draw_sprite_code(yoff + y, xbegin + i * 16 + 8, value & 0xff, color, start_x, end_x);
+ }
+ m_sprites[nr].bitmap[y][i * 2]=0; //easier sprite collision detection
+ if (SPRITE_Y_EXPAND(nr))
+ {
+ if (m_sprites[nr].repeat)
+ {
+ m_sprites[nr].line++;
+ m_sprites[nr].repeat = 0;
+ }
+ else
+ m_sprites[nr].repeat = 1;
+ }
+ else
+ {
+ m_sprites[nr].line++;
+ }
+ }
+ }
+ else
+ {
+ for (y = ybegin; y <= yend; y++)
+ {
+ m_sprites[nr].paintedline[y] = 1;
+ for (i = 0; i < 3; i++)
+ {
+ value = m_dma_read_cb(addr + m_sprites[nr].line * 3 + i);
+ m_sprites[nr].bitmap[y][i] = value;
+ sprite_collision(nr, y, xbegin + i * 8, value);
+ if (prior || !collision)
+ value3 = getforeground(yoff + y, xbegin + i * 8 - 7);
+ if (!collision && (value & value3))
+ {
+ collision = 1;
+ SPRITE_SET_BG_COLLISION(nr);
+ set_interrupt(2);
+ }
+ if (prior)
+ value &= ~value3;
+ draw_sprite_code(yoff + y, xbegin + i * 8, value, color, start_x, end_x);
+ }
+ m_sprites[nr].bitmap[y][i]=0; //easier sprite collision detection
+ if (SPRITE_Y_EXPAND(nr))
+ {
+ if (m_sprites[nr].repeat)
+ {
+ m_sprites[nr].line++;
+ m_sprites[nr].repeat = 0;
+ }
+ else
+ m_sprites[nr].repeat = 1;
+ }
+ else
+ {
+ m_sprites[nr].line++;
+ }
+ }
+ }
+}
+
+void vic3_device::draw_sprite_multi( int nr, int yoff, int ybegin, int yend, int start_x, int end_x )
+{
+ int y, i, prior, addr, xbegin, collision;
+ int value, value2, value3 = 0, bg/*, color[2]*/;
+
+ xbegin = SPRITE_X_POS(nr);
+ addr = m_dma_read_cb(SPRITE_ADDR(nr)) << 6;
+ m_spritemulti[2] = SPRITE_COLOR(nr);
+ prior = SPRITE_PRIORITY(nr);
+ collision = SPRITE_BG_COLLISION(nr);
+ //color[0] = 0;
+ //color[1] = 1;
+
+ if (SPRITE_X_EXPAND(nr))
+ {
+ for (y = ybegin; y <= yend; y++)
+ {
+ m_sprites[nr].paintedline[y] = 1;
+ for (i = 0; i < 3; i++)
+ {
+ value = m_expandx_multi[bg = m_dma_read_cb(addr + m_sprites[nr].line * 3 + i)];
+ value2 = m_expandx[m_multi_collision[bg]];
+ m_sprites[nr].bitmap[y][i * 2] = value2 >> 8;
+ m_sprites[nr].bitmap[y][i * 2 + 1] = value2 & 0xff;
+ sprite_collision(nr, y, xbegin + i * 16, value2 >> 8);
+ sprite_collision(nr, y, xbegin + i * 16 + 8, value2 & 0xff);
+ if (prior || !collision)
+ {
+ value3 = getforeground16(yoff + y, xbegin + i * 16 - 7);
+ }
+ if (!collision && (value2 & value3))
+ {
+ collision = 1;
+ SPRITE_SET_BG_COLLISION(nr);
+ set_interrupt(2);
+ }
+ if (prior)
+ {
+ draw_sprite_code_multi(yoff + y, xbegin + i * 16, value >> 8, (value3 >> 8) ^ 0xff, start_x, end_x);
+ draw_sprite_code_multi(yoff + y, xbegin + i * 16 + 8, value & 0xff, (value3 & 0xff) ^ 0xff, start_x, end_x);
+ }
+ else
+ {
+ draw_sprite_code_multi(yoff + y, xbegin + i * 16, value >> 8, 0xff, start_x, end_x);
+ draw_sprite_code_multi(yoff + y, xbegin + i * 16 + 8, value & 0xff, 0xff, start_x, end_x);
+ }
+ }
+ m_sprites[nr].bitmap[y][i * 2]=0; //easier sprite collision detection
+ if (SPRITE_Y_EXPAND(nr))
+ {
+ if (m_sprites[nr].repeat)
+ {
+ m_sprites[nr].line++;
+ m_sprites[nr].repeat = 0;
+ }
+ else
+ m_sprites[nr].repeat = 1;
+ }
+ else
+ {
+ m_sprites[nr].line++;
+ }
+ }
+ }
+ else
+ {
+ for (y = ybegin; y <= yend; y++)
+ {
+ m_sprites[nr].paintedline[y] = 1;
+ for (i = 0; i < 3; i++)
+ {
+ value = m_dma_read_cb(addr + m_sprites[nr].line * 3 + i);
+ m_sprites[nr].bitmap[y][i] = value2 = m_multi_collision[value];
+ sprite_collision(nr, y, xbegin + i * 8, value2);
+ if (prior || !collision)
+ {
+ value3 = getforeground(yoff + y, xbegin + i * 8 - 7);
+ }
+ if (!collision && (value2 & value3))
+ {
+ collision = 1;
+ SPRITE_SET_BG_COLLISION(nr);
+ set_interrupt(2);
+ }
+ if (prior)
+ {
+ draw_sprite_code_multi(yoff + y, xbegin + i * 8, value, value3 ^ 0xff, start_x, end_x);
+ }
+ else
+ {
+ draw_sprite_code_multi(yoff + y, xbegin + i * 8, value, 0xff, start_x, end_x);
+ }
+ }
+ m_sprites[nr].bitmap[y][i] = 0; //easier sprite collision detection
+ if (SPRITE_Y_EXPAND(nr))
+ {
+ if (m_sprites[nr].repeat)
+ {
+ m_sprites[nr].line++;
+ m_sprites[nr].repeat = 0;
+ }
+ else
+ m_sprites[nr].repeat = 1;
+ }
+ else
+ {
+ m_sprites[nr].line++;
+ }
+ }
+ }
+}
+
+#ifndef memset16
+static void *memset16(void *dest, int value, size_t size)
+{
+ register int i;
+
+ for (i = 0; i < size; i++)
+ ((short *) dest)[i] = value;
+ return dest;
+}
+#endif
+
+void vic3_device::drawlines( int first, int last, int start_x, int end_x )
+{
+ int line, vline, end;
+ int attr, ch, ecm;
+ int syend;
+ int offs, yoff, xoff, ybegin, yend, xbegin, xend;
+ int x_end2;
+ int i, j;
+
+ if (first == last)
+ return;
+ m_lastline = last;
+
+ /* top part of display not rastered */
+ first -= VIC2_YPOS - YPOS;
+ last -= VIC2_YPOS - YPOS;
+ if ((first >= last) || (last <= 0))
+ {
+ for (i = 0; i < 8; i++)
+ m_sprites[i].repeat = m_sprites[i].line = 0;
+ return;
+ }
+ if (first < 0)
+ first = 0;
+
+ if (!SCREENON)
+ {
+ for (line = first; (line < last) && (line < m_bitmap->height()); line++)
+ {
+ memset16(&m_bitmap->pix16(line + FIRSTLINE), 0, m_bitmap->width());
+ }
+ return;
+ }
+ if (COLUMNS40)
+ xbegin = XPOS, xend = xbegin + 640;
+ else
+ xbegin = XPOS + 7, xend = xbegin + 624;
+
+ if (last < m_y_begin)
+ end = last;
+ else
+ end = m_y_begin + YPOS;
+
+ for (line = first; line < end; line++)
+ {
+ memset16(&m_bitmap->pix16(line + FIRSTLINE), FRAMECOLOR, m_bitmap->width());
+ }
+
+ if (LINES25)
+ {
+ vline = line - m_y_begin - YPOS;
+ }
+ else
+ {
+ vline = line - m_y_begin - YPOS + 8 - VERTICALPOS;
+ }
+ if (last < m_y_end + YPOS)
+ end = last;
+ else
+ end = m_y_end + YPOS;
+ x_end2 = m_x_end * 2;
+ for (; line < end; vline = (vline + 8) & ~7, line = line + 1 + yend - ybegin)
+ {
+ offs = (vline >> 3) * 80;
+ ybegin = vline & 7;
+ yoff = line - ybegin;
+ yend = (yoff + 7 < end) ? 7 : (end - yoff - 1);
+ /* rendering 39 characters */
+ /* left and right borders are overwritten later */
+ m_shift[line] = HORIZONTALPOS;
+
+ for (xoff = m_x_begin + XPOS; xoff < x_end2 + XPOS; xoff += 8, offs++)
+ {
+ ch = m_dma_read_cb(m_videoaddr + offs);
+ attr = m_dma_read_color_cb(m_videoaddr + offs);
+ if (HIRESON)
+ {
+ m_bitmapmulti[1] = m_c64_bitmap[1] = ch >> 4;
+ m_bitmapmulti[2] = m_c64_bitmap[0] = ch & 0xf;
+ if (MULTICOLORON)
+ {
+ m_bitmapmulti[3] = attr;
+ draw_bitmap_multi(ybegin, yend, offs, yoff, xoff, start_x, end_x);
+ }
+ else
+ {
+ draw_bitmap(ybegin, yend, offs, yoff, xoff, start_x, end_x);
+ }
+ }
+ else if (ECMON)
+ {
+ ecm = ch >> 6;
+ m_ecmcolor[0] = m_colors[ecm];
+ m_ecmcolor[1] = attr;
+ draw_character(ybegin, yend, ch & ~0xC0, yoff, xoff, m_ecmcolor, start_x, end_x);
+ }
+ else if (MULTICOLORON && (attr & 8))
+ {
+ m_multi[3] = attr & 7;
+ draw_character_multi(ybegin, yend, ch, yoff, xoff, start_x, end_x);
+ }
+ else
+ {
+ m_mono[1] = attr;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_mono, start_x, end_x);
+ }
+ }
+ /* sprite priority, sprite overwrites lowerprior pixels */
+ for (i = 7; i >= 0; i--)
+ {
+ if (m_sprites[i].line || m_sprites[i].repeat)
+ {
+ syend = yend;
+ if (SPRITE_Y_EXPAND(i))
+ {
+ if ((21 - m_sprites[i].line) * 2 - m_sprites[i].repeat < yend - ybegin + 1)
+ syend = ybegin + (21 - m_sprites[i].line) * 2 - m_sprites[i].repeat - 1;
+ }
+ else
+ {
+ if (m_sprites[i].line + yend - ybegin + 1 > 20)
+ syend = ybegin + 20 - m_sprites[i].line;
+ }
+ if (yoff + syend > YPOS + 200)
+ syend = YPOS + 200 - yoff - 1;
+ if (SPRITE_MULTICOLOR(i))
+ draw_sprite_multi(i, yoff, ybegin, syend, start_x, end_x);
+ else
+ draw_sprite(i, yoff, ybegin, syend, start_x, end_x);
+ if ((syend != yend) || (m_sprites[i].line > 20))
+ {
+ m_sprites[i].line = m_sprites[i].repeat = 0;
+ for (j = syend; j <= yend; j++)
+ m_sprites[i].paintedline[j] = 0;
+ }
+ }
+ // sprite wrap y at the top of the screen
+ else if (SPRITEON(i) && (yoff == 1 + yend - ybegin) && (SPRITE_Y_POS(i) < 1 + yend - ybegin))
+ {
+ int wrapped = 1 + yend - ybegin - SPRITE_Y_POS(i);
+ syend = yend;
+
+ if (SPRITE_Y_EXPAND(i))
+ {
+ if (wrapped & 1) m_sprites[i].repeat = 1;
+ wrapped >>= 1;
+ syend = 21 * 2 - 1 - wrapped * 2;
+ if (syend > (yend - ybegin)) syend = yend - ybegin;
+ }
+ else
+ {
+ syend = 21 - 1 - wrapped;
+ if (syend > (yend - ybegin)) syend = yend - ybegin;
+ }
+
+ m_sprites[i].line = wrapped;
+
+ if (SPRITE_MULTICOLOR(i))
+ draw_sprite_multi(i, yoff, 0 , syend, start_x, end_x);
+ else
+ draw_sprite(i, yoff, 0 , syend, start_x, end_x);
+
+ if ((syend != yend) || (m_sprites[i].line > 20))
+ {
+ for (j = syend; j <= yend; j++)
+ m_sprites[i].paintedline[j] = 0;
+ m_sprites[i].line = m_sprites[i].repeat = 0;
+ }
+ }
+ else if (SPRITEON(i) && (yoff + ybegin <= SPRITE_Y_POS(i))
+ && (yoff + yend >= SPRITE_Y_POS(i)))
+ {
+ syend = yend;
+ if (SPRITE_Y_EXPAND(i))
+ {
+ if (21 * 2 < yend - ybegin + 1)
+ syend = ybegin + 21 * 2 - 1;
+ }
+ else
+ {
+ if (yend - ybegin + 1 > 21)
+ syend = ybegin + 21 - 1;
+ }
+ if (yoff + syend >= YPOS + 200)
+ syend = YPOS + 200 - yoff - 1;
+ for (j = 0; j < SPRITE_Y_POS(i) - yoff; j++)
+ m_sprites[i].paintedline[j] = 0;
+ if (SPRITE_MULTICOLOR(i))
+ draw_sprite_multi(i, yoff, SPRITE_Y_POS(i) - yoff, syend, start_x, end_x);
+ else
+ draw_sprite(i, yoff, SPRITE_Y_POS(i) - yoff, syend, start_x, end_x);
+ if ((syend != yend) || (m_sprites[i].line > 20))
+ {
+ for (j = syend; j <= yend; j++)
+ m_sprites[i].paintedline[j] = 0;
+ m_sprites[i].line = m_sprites[i].repeat = 0;
+ }
+ }
+ else
+ {
+ memset (m_sprites[i].paintedline, 0, sizeof (m_sprites[i].paintedline));
+ }
+ }
+
+ for (i = ybegin; i <= yend; i++)
+ {
+ m_bitmap->plot_box(0, yoff + ybegin + FIRSTLINE, xbegin, yend - ybegin + 1, FRAMECOLOR);
+ m_bitmap->plot_box(xend, yoff + ybegin + FIRSTLINE, m_bitmap->width() - xend, yend - ybegin + 1, FRAMECOLOR);
+ }
+ }
+ if (last < m_bitmap->height())
+ end = last;
+ else
+ end = m_bitmap->height();
+
+ for (; line < end; line++)
+ {
+ memset16(&m_bitmap->pix16(line + FIRSTLINE), FRAMECOLOR, m_bitmap->width());
+ }
+}
+
+void vic3_device::vic2_drawlines( int first, int last, int start_x, int end_x )
+{
+ int line, vline, end;
+ int attr, ch, ecm;
+ int syend;
+ int offs, yoff, xoff, ybegin, yend, xbegin, xend;
+ int i;
+
+ if (VIC3_BITPLANES)
+ return ;
+
+ /* temporary allowing vic3 displaying 80 columns */
+ if (m_reg[0x31] & 0x80)
+ {
+ drawlines(first, first + 1, start_x, end_x);
+ return;
+ }
+
+ /* otherwise, draw VIC II output (currently using the old code, not the new one from vic6567.c) */
+
+ /* top part of display not rastered */
+ first -= VIC2_YPOS - YPOS;
+
+ xbegin = VIC2_STARTVISIBLECOLUMNS;
+ xend = xbegin + VIC2_VISIBLECOLUMNS;
+ if (!SCREENON)
+ {
+ xbegin = VIC2_STARTVISIBLECOLUMNS;
+ xend = xbegin + VIC2_VISIBLECOLUMNS;
+ if ((start_x <= xbegin) && (end_x >= xend))
+ m_bitmap->plot_box(xbegin, first + FIRSTLINE, xend - xbegin, 1, FRAMECOLOR);
+ if ((start_x > xbegin) && (end_x >= xend))
+ m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, xend - start_x, 1, FRAMECOLOR);
+ if ((start_x <= xbegin) && (end_x < xend))
+ m_bitmap->plot_box(xbegin, first + FIRSTLINE, end_x - xbegin , 1, FRAMECOLOR);
+ if ((start_x > xbegin) && (end_x < xend))
+ m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
+ return;
+ }
+
+ if (COLUMNS40)
+ {
+ xbegin = XPOS;
+ xend = xbegin + 320;
+ }
+ else
+ {
+ xbegin = XPOS + 7;
+ xend = xbegin + 304;
+ }
+
+ if (first + 1 < m_y_begin)
+ end = first + 1;
+ else
+ end = m_y_begin + YPOS;
+
+ line = first;
+ // top border
+ if (line < end)
+ {
+ if ((start_x <= xbegin) && (end_x >= xend))
+ m_bitmap->plot_box(xbegin, first + FIRSTLINE, xend - xbegin, 1, FRAMECOLOR);
+ if ((start_x > xbegin) && (end_x >= xend))
+ m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, xend - start_x, 1, FRAMECOLOR);
+ if ((start_x <= xbegin) && (end_x < xend))
+ m_bitmap->plot_box(xbegin, first + FIRSTLINE, end_x - xbegin , 1, FRAMECOLOR);
+ if ((start_x > xbegin) && (end_x < xend))
+ m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
+ line = end;
+ }
+
+ vline = line - YPOS + 3 - VERTICALPOS;
+
+ if (first + 1 < m_y_end + YPOS)
+ end = first + 1;
+ else
+ end = m_y_end + YPOS;
+
+ if (line < end)
+ {
+ offs = (vline >> 3) * 40;
+ ybegin = vline & 7;
+ yoff = line - ybegin;
+ yend = (yoff + 7 < end) ? 7 : (end - yoff - 1);
+
+ /* rendering 39 characters */
+ /* left and right borders are overwritten later */
+
+ m_shift[line] = HORIZONTALPOS;
+ for (xoff = m_x_begin + XPOS; xoff < m_x_end + XPOS; xoff += 8, offs++)
+ {
+ ch = m_dma_read_cb(m_videoaddr + offs);
+#if 0
+ attr = m_dma_read_color_cb(m_videoaddr + offs);
+#else
+ /* temporary until vic3 finished */
+ attr = m_dma_read_color_cb((m_videoaddr + offs)&0x3ff)&0x0f;
+#endif
+ if (HIRESON)
+ {
+ m_bitmapmulti[1] = m_c64_bitmap[1] = ch >> 4;
+ m_bitmapmulti[2] = m_c64_bitmap[0] = ch & 0xf;
+ if (MULTICOLORON)
+ {
+ m_bitmapmulti[3] = attr;
+ draw_bitmap_multi(ybegin, yend, offs, yoff, xoff, start_x, end_x);
+ }
+ else
+ {
+ draw_bitmap(ybegin, yend, offs, yoff, xoff, start_x, end_x);
+ }
+ }
+ else if (ECMON)
+ {
+ ecm = ch >> 6;
+ m_ecmcolor[0] = m_colors[ecm];
+ m_ecmcolor[1] = attr;
+ draw_character(ybegin, yend, ch & ~0xC0, yoff, xoff, m_ecmcolor, start_x, end_x);
+ }
+ else if (MULTICOLORON && (attr & 8))
+ {
+ m_multi[3] = attr & 7;
+ draw_character_multi(ybegin, yend, ch, yoff, xoff, start_x, end_x);
+ }
+ else
+ {
+ m_mono[1] = attr;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_mono, start_x, end_x);
+ }
+ }
+
+ /* sprite priority, sprite overwrites lowerprior pixels */
+ for (i = 7; i >= 0; i--)
+ {
+ if (SPRITEON (i) &&
+ (yoff + ybegin >= SPRITE_Y_POS (i)) &&
+ (yoff + ybegin - SPRITE_Y_POS (i) < (SPRITE_Y_EXPAND (i)? 21 * 2 : 21 )) &&
+ (SPRITE_Y_POS (i) < 0))
+ {
+ int wrapped = - SPRITE_Y_POS (i) + 6;
+
+ syend = yend;
+
+ if (SPRITE_Y_EXPAND (i))
+ {
+ if (wrapped & 1) m_sprites[i].repeat = 1;
+ wrapped >>= 1;
+ syend = 21 * 2 - 1 - wrapped * 2;
+ if (syend > (yend - ybegin)) syend = yend - ybegin;
+ }
+ else
+ {
+ syend = 21 - 1 - wrapped;
+ if (syend > (yend - ybegin)) syend = yend - ybegin;
+ }
+
+ m_sprites[i].line = wrapped;
+
+ if (SPRITE_MULTICOLOR (i))
+ draw_sprite_multi(i, 0, 0 , syend, start_x, end_x);
+ else
+ draw_sprite(i, 0, 0 , syend, start_x, end_x);
+ }
+ else if (SPRITEON (i) &&
+ (yoff + ybegin >= SPRITE_Y_POS (i)) &&
+ (yoff + ybegin - SPRITE_Y_POS (i) < (SPRITE_Y_EXPAND (i)? 21 * 2 : 21 )) &&
+ (SPRITE_Y_POS (i) >= 0))
+ {
+ int wrapped = yoff + ybegin - SPRITE_Y_POS (i);
+
+ syend = yend;
+
+ if (SPRITE_Y_EXPAND (i))
+ {
+ if (wrapped & 1) m_sprites[i].repeat = 1;
+ wrapped >>= 1;
+ syend = 21 * 2 - 1 - wrapped * 2;
+ if (syend > (yend - ybegin)) syend = yend - ybegin;
+ }
+ else
+ {
+ syend = 21 - 1 - wrapped;
+ if (syend > (yend - ybegin)) syend = yend - ybegin;
+ }
+
+ m_sprites[i].line = wrapped;
+
+ if (SPRITE_MULTICOLOR (i))
+ draw_sprite_multi(i, yoff + ybegin, 0, 0, start_x, end_x);
+ else
+ draw_sprite(i, yoff + ybegin, 0, 0, start_x, end_x);
+ }
+ else
+ {
+ memset(m_sprites[i].paintedline, 0, sizeof (m_sprites[i].paintedline));
+ }
+ }
+ line += 1 + yend - ybegin;
+ }
+
+ // left border
+ if ((start_x <= VIC2_STARTVISIBLECOLUMNS) && (end_x >= xbegin))
+ m_bitmap->plot_box(VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, xbegin - VIC2_STARTVISIBLECOLUMNS, 1, FRAMECOLOR);
+ else if ((start_x > VIC2_STARTVISIBLECOLUMNS) && (end_x >= xbegin))
+ m_bitmap->plot_box(start_x, first + FIRSTLINE, xbegin - start_x, 1, FRAMECOLOR);
+ else if ((start_x <= VIC2_STARTVISIBLECOLUMNS) && (end_x < xbegin))
+ m_bitmap->plot_box(VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, end_x, 1, FRAMECOLOR);
+ else if ((start_x > VIC2_STARTVISIBLECOLUMNS) && (end_x < xbegin))
+ m_bitmap->plot_box(start_x, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
+
+ // right border
+ if ((start_x <= xend) && (end_x >= VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS))
+ m_bitmap->plot_box(xend, first + FIRSTLINE, VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS - xend, 1, FRAMECOLOR);
+ else if ((start_x > xend) && (end_x >= VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS))
+ m_bitmap->plot_box(start_x, first + FIRSTLINE, VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS - start_x, 1, FRAMECOLOR);
+ else if ((start_x <= xend) && (end_x < VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS))
+ m_bitmap->plot_box(xend, first + FIRSTLINE, end_x - xend, 1, FRAMECOLOR);
+ else if ((start_x > VIC2_STARTVISIBLECOLUMNS) && (end_x < xbegin))
+ m_bitmap->plot_box(start_x, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
+
+ if (first + 1 < m_bitmap->height())
+ end = first + 1;
+ else
+ end = m_bitmap->height();
+
+ // bottom border
+ if (line < end)
+ {
+ if ((start_x <= xbegin) && (end_x >= xend))
+ m_bitmap->plot_box(xbegin, first + FIRSTLINE, xend - xbegin, 1, FRAMECOLOR);
+ if ((start_x > xbegin) && (end_x >= xend))
+ m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, xend - start_x, 1, FRAMECOLOR);
+ if ((start_x <= xbegin) && (end_x < xend))
+ m_bitmap->plot_box(xbegin, first + FIRSTLINE, end_x - xbegin , 1, FRAMECOLOR);
+ if ((start_x > xbegin) && (end_x < xend))
+ m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
+ line = end;
+ }
+}
+
+/*****************************************************************************
+ I/O HANDLERS
+*****************************************************************************/
+
+WRITE8_MEMBER( vic3_device::palette_w )
+{
+ if (offset < 0x100)
+ m_palette_red[offset] = data;
+ else if (offset < 0x200)
+ m_palette_green[offset & 0xff] = data;
+ else
+ m_palette_blue[offset & 0xff] = data;
+
+ m_palette_dirty = 1;
+}
+
+
+WRITE8_MEMBER( vic3_device::port_w )
+{
+ DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
+ offset &= 0x7f;
+
+ /* offsets 0x00 -> 0x2e coincide with VICII */
+ switch (offset)
+ {
+ case 0x01:
+ case 0x03:
+ case 0x05:
+ case 0x07:
+ case 0x09:
+ case 0x0b:
+ case 0x0d:
+ case 0x0f:
+ /* sprite y positions */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_sprites[offset / 2].y = SPRITE_Y_POS(offset / 2);
+ }
+ break;
+
+ case 0x00:
+ case 0x02:
+ case 0x04:
+ case 0x06:
+ case 0x08:
+ case 0x0a:
+ case 0x0c:
+ case 0x0e:
+ /* sprite x positions */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_sprites[offset / 2].x = SPRITE_X_POS(offset / 2);
+ }
+ break;
+
+ case 0x10: /* sprite x positions */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_sprites[0].x = SPRITE_X_POS(0);
+ m_sprites[1].x = SPRITE_X_POS(1);
+ m_sprites[2].x = SPRITE_X_POS(2);
+ m_sprites[3].x = SPRITE_X_POS(3);
+ m_sprites[4].x = SPRITE_X_POS(4);
+ m_sprites[5].x = SPRITE_X_POS(5);
+ m_sprites[6].x = SPRITE_X_POS(6);
+ m_sprites[7].x = SPRITE_X_POS(7);
+ }
+ break;
+
+ case 0x17: /* sprite y size */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x1d: /* sprite x size */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x1b: /* sprite background priority */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x1c: /* sprite multicolor mode select */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2c:
+ case 0x2d:
+ case 0x2e:
+ /* sprite colors */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x25: /* sprite multicolor */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_spritemulti[1] = SPRITE_MULTICOLOR1;
+ }
+ break;
+
+ case 0x26: /* sprite multicolor */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_spritemulti[3] = SPRITE_MULTICOLOR2;
+ }
+ break;
+
+ case 0x19:
+ clear_interrupt(data & 0x0f);
+ break;
+
+ case 0x1a: /* irq mask */
+ m_reg[offset] = data;
+ set_interrupt(0); // beamrider needs this
+ break;
+
+ case 0x11:
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ if (LINES25)
+ {
+ m_y_begin = 0;
+ m_y_end = m_y_begin + 200;
+ }
+ else
+ {
+ m_y_begin = 4;
+ m_y_end = m_y_begin + 192;
+ }
+ }
+ break;
+
+ case 0x12:
+ if (data != m_reg[offset])
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x16:
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_x_begin = HORIZONTALPOS;
+ m_x_end = m_x_begin + 320;
+ }
+ break;
+
+ case 0x18:
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_videoaddr = VIDEOADDR;
+ m_chargenaddr = CHARGENADDR;
+ m_bitmapaddr = BITMAPADDR;
+ }
+ break;
+
+ case 0x21: /* background color */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_mono[0] = m_bitmapmulti[0] = m_multi[0] = m_colors[0] = BACKGROUNDCOLOR;
+ }
+ break;
+
+ case 0x22: /* background color 1 */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_multi[1] = m_colors[1] = MULTICOLOR1;
+ }
+ break;
+
+ case 0x23: /* background color 2 */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_multi[2] = m_colors[2] = MULTICOLOR2;
+ }
+ break;
+
+ case 0x24: /* background color 3 */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ m_colors[3] = FOREGROUNDCOLOR;
+ }
+ break;
+
+ case 0x20: /* framecolor */
+ if (m_reg[offset] != data)
+ {
+ m_reg[offset] = data;
+ }
+ break;
+
+ case 0x2f:
+ DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
+ m_reg[offset] = data;
+ break;
+ case 0x30:
+ m_reg[offset] = data;
+ if (!m_port_changed_cb.isnull())
+ {
+ DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
+ m_reg[offset] = data;
+ m_port_changed_cb((offs_t)0,data);
+ }
+ break;
+ case 0x31:
+ m_reg[offset] = data;
+ if (data & 0x40)
+ m_cpu->set_clock_scale(1.0);
+ else
+ m_cpu->set_clock_scale(1.0/3.5);
+ break;
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ case 0x3c:
+ case 0x3d:
+ case 0x3e:
+ case 0x3f:
+ m_reg[offset] = data;
+ DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
+ break;
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ DBG_LOG(2, "vic plane write", ("%.2x:%.2x\n", offset, data));
+ break;
+ default:
+ m_reg[offset] = data;
+ break;
+ }
+}
+
+READ8_MEMBER( vic3_device::port_r )
+{
+ int val = 0;
+ offset &= 0x7f;
+
+ /* offsets 0x00 -> 0x2e coincide with VICII */
+ switch (offset)
+ {
+ case 0x11:
+ val = (m_reg[offset] & ~0x80) | ((m_rasterline & 0x100) >> 1);
+ break;
+
+ case 0x12:
+ val = m_rasterline & 0xff;
+ break;
+
+ case 0x16:
+ val = m_reg[offset] | 0xc0;
+ break;
+
+ case 0x18:
+ val = m_reg[offset] | 0x01;
+ break;
+
+ case 0x19: /* interrupt flag register */
+ /* vic2_clear_interrupt(0xf); */
+ val = m_reg[offset] | 0x70;
+ break;
+
+ case 0x1a:
+ val = m_reg[offset] | 0xf0;
+ break;
+
+ case 0x1e: /* sprite to sprite collision detect */
+ val = m_reg[offset];
+ m_reg[offset] = 0;
+ clear_interrupt(4);
+ break;
+
+ case 0x1f: /* sprite to background collision detect */
+ val = m_reg[offset];
+ m_reg[offset] = 0;
+ clear_interrupt(2);
+ break;
+
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ val = m_reg[offset];
+ break;
+
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f:
+ case 0x10:
+ case 0x17:
+ case 0x1b:
+ case 0x1c:
+ case 0x1d:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2c:
+ case 0x2d:
+ case 0x2e:
+ val = m_reg[offset];
+ break;
+
+ case 0x2f:
+ case 0x30:
+ val = m_reg[offset];
+ DBG_LOG(2, "vic read", ("%.2x:%.2x\n", offset, val));
+ break;
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ case 0x3c:
+ case 0x3d:
+ case 0x3e:
+ case 0x3f: /* not used */
+ val = m_reg[offset];
+ DBG_LOG(2, "vic read", ("%.2x:%.2x\n", offset, val));
+ break;
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ DBG_LOG(2, "vic3 plane read", ("%.2x:%.2x\n", offset, val));
+ break;
+ default:
+ val = m_reg[offset];
+ }
+ return val;
+}
+
+
+#define VIC3_MASK(M) \
+ if (M) \
+ { \
+ if (M & 0x01) \
+ colors[0] = m_c64_mem_r_cb(VIC3_ADDR(0) + offset); \
+ if (M & 0x02) \
+ colors[1] = m_c64_mem_r_cb(VIC3_ADDR(1) + offset) << 1; \
+ if (M & 0x04) \
+ colors[2] = m_c64_mem_r_cb(VIC3_ADDR(2) + offset) << 2; \
+ if (M & 0x08) \
+ colors[3] = m_c64_mem_r_cb(VIC3_ADDR(3) + offset) << 3; \
+ if (M & 0x10) \
+ colors[4] = m_c64_mem_r_cb(VIC3_ADDR(4) + offset) << 4; \
+ if (M & 0x20) \
+ colors[5] = m_c64_mem_r_cb(VIC3_ADDR(5) + offset) << 5; \
+ if (M & 0x40) \
+ colors[6] = m_c64_mem_r_cb(VIC3_ADDR(6) + offset) << 6; \
+ if (M & 0x80) \
+ colors[7] = m_c64_mem_r_cb(VIC3_ADDR(7) + offset) << 7; \
+ for (i = 7; i >= 0; i--) \
+ { \
+ p = 0; \
+ if (M & 0x01) \
+ { \
+ p = colors[0] & 0x01; \
+ colors[0] >>= 1; \
+ } \
+ if (M & 0x02) \
+ { \
+ p |= colors[1] & 0x02; \
+ colors[1] >>= 1; \
+ } \
+ if (M & 0x04) \
+ { \
+ p |= colors[2] & 0x04; \
+ colors[2] >>= 1; \
+ } \
+ if (M & 0x08) \
+ { \
+ p |= colors[3] & 0x08; \
+ colors[3] >>= 1; \
+ } \
+ if (M & 0x10) \
+ { \
+ p |= colors[4] & 0x10; \
+ colors[4] >>= 1; \
+ } \
+ if (M & 0x20) \
+ { \
+ p |= colors[5] & 0x20; \
+ colors[5] >>= 1; \
+ } \
+ if (M & 0x40) \
+ { \
+ p |= colors[6] & 0x40; \
+ colors[6] >>= 1; \
+ } \
+ if (M & 0x80) \
+ { \
+ p |= colors[7] & 0x80; \
+ colors[7] >>= 1; \
+ } \
+ m_bitmap->pix16(YPOS + y, XPOS + x + i) = p; \
+ } \
+ }
+
+#define VIC3_ADDR(a) VIC3_BITPLANE_IADDR(a)
+void vic3_device::interlace_draw_block( int x, int y, int offset )
+{
+ int colors[8] = {0};
+ int i, p;
+
+ switch (VIC3_BITPLANES_MASK)
+ {
+ case 0x05:
+ VIC3_MASK(0x05)
+ break;
+ case 0x07:
+ VIC3_MASK(0x07)
+ break;
+ case 0x0f:
+ VIC3_MASK(0x0f)
+ break;
+ case 0x1f:
+ VIC3_MASK(0x1f)
+ break;
+ case 0x7f:
+ VIC3_MASK(0x7f)
+ break;
+ case 0xff:
+ VIC3_MASK(0xff)
+ break;
+ default:
+ if (VIC3_BITPLANES_MASK & 0x01)
+ colors[0] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(0) + offset);
+
+ if (VIC3_BITPLANES_MASK & 0x02)
+ colors[1] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(1) + offset) << 1;
+
+ if (VIC3_BITPLANES_MASK & 0x04)
+ colors[2] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(2) + offset) << 2;
+
+ if (VIC3_BITPLANES_MASK & 0x08)
+ colors[3] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(3) + offset) << 3;
+
+ if (VIC3_BITPLANES_MASK & 0x10)
+ colors[4] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(4) + offset) << 4;
+
+ if (VIC3_BITPLANES_MASK & 0x20)
+ colors[5] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(5) + offset) << 5;
+
+ if (VIC3_BITPLANES_MASK & 0x40)
+ colors[6] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(6) + offset) << 6;
+
+ if (VIC3_BITPLANES_MASK & 0x80)
+ colors[7] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(7) + offset) << 7;
+
+ for (i = 7; i >= 0; i--)
+ {
+ m_bitmap->pix16(YPOS + y, XPOS + x + i) =
+ (colors[0] & 0x01) | (colors[1] & 0x02)
+ | (colors[2] & 0x04) | (colors[3] & 0x08)
+ | (colors[4] & 0x10) | (colors[5] & 0x20)
+ | (colors[6] & 0x40) | (colors[7] & 0x80);
+ colors[0] >>= 1;
+ colors[1] >>= 1;
+ colors[2] >>= 1;
+ colors[3] >>= 1;
+ colors[4] >>= 1;
+ colors[5] >>= 1;
+ colors[6] >>= 1;
+ colors[7] >>= 1;
+ }
+ }
+}
+
+#undef VIC3_ADDR
+#define VIC3_ADDR(a) VIC3_BITPLANE_ADDR(a)
+void vic3_device::draw_block( int x, int y, int offset )
+{
+ int colors[8] = {0};
+ int i, p;
+
+ switch (VIC3_BITPLANES_MASK)
+ {
+ case 5:
+ VIC3_MASK(0x05)
+ break;
+ case 7:
+ VIC3_MASK(0x07)
+ break;
+ case 0xf:
+ VIC3_MASK(0x0f)
+ break;
+ case 0x1f:
+ VIC3_MASK(0x1f)
+ break;
+ case 0x7f:
+ VIC3_MASK(0x7f)
+ break;
+ case 0xff:
+ VIC3_MASK(0xff)
+ break;
+ default:
+ if (VIC3_BITPLANES_MASK & 0x01)
+ colors[0] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(0) + offset);
+
+ if (VIC3_BITPLANES_MASK & 0x02)
+ colors[1] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(1) + offset) << 1;
+
+ if (VIC3_BITPLANES_MASK & 0x04)
+ colors[2] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(2) + offset) << 2;
+
+ if (VIC3_BITPLANES_MASK & 0x08)
+ colors[3] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(3) + offset) << 3;
+
+ if (VIC3_BITPLANES_MASK & 0x10)
+ colors[4] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(4) + offset) << 4;
+
+ if (VIC3_BITPLANES_MASK & 0x20)
+ colors[5] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(5) + offset) << 5;
+
+ if (VIC3_BITPLANES_MASK & 0x40)
+ colors[6] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(6) + offset) << 6;
+
+ if (VIC3_BITPLANES_MASK & 0x80)
+ colors[7] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(7) + offset) << 7;
+
+ for (i = 7; i >= 0; i--)
+ {
+ m_bitmap->pix16(YPOS + y, XPOS + x + i) =
+ (colors[0] & 0x01) | (colors[1] & 0x02)
+ | (colors[2] & 0x04) | (colors[3] & 0x08)
+ | (colors[4] & 0x10) | (colors[5] & 0x20)
+ | (colors[6] & 0x40) | (colors[7] & 0x80);
+ colors[0] >>= 1;
+ colors[1] >>= 1;
+ colors[2] >>= 1;
+ colors[3] >>= 1;
+ colors[4] >>= 1;
+ colors[5] >>= 1;
+ colors[6] >>= 1;
+ colors[7] >>= 1;
+ }
+ }
+}
+
+
+void vic3_device::draw_bitplanes()
+{
+ int x, y, y1s, offset;
+ rectangle vis;
+ const rectangle &visarea = m_screen->visible_area();
+
+ if (VIC3_LINES == 400)
+ { /* interlaced! */
+ for (y1s = 0, offset = 0; y1s < 400; y1s += 16)
+ {
+ for (x = 0; x < VIC3_BITPLANES_WIDTH; x += 8)
+ {
+ for (y = y1s; y < y1s + 16; y += 2, offset++)
+ {
+ if (m_interlace)
+ draw_block(x, y, offset);
+ else
+ interlace_draw_block(x, y + 1, offset);
+ }
+ }
+ }
+ m_interlace ^= 1;
+ }
+ else
+ {
+ for (y1s = 0, offset = 0; y1s < 200; y1s += 8)
+ {
+ for (x = 0; x < VIC3_BITPLANES_WIDTH; x += 8)
+ {
+ for (y = y1s; y < y1s + 8; y++, offset++)
+ {
+ draw_block(x, y, offset);
+ }
+ }
+ }
+ }
+
+ if (XPOS > 0)
+ {
+ vis.set(0, XPOS - 1, 0, visarea.max_y);
+ m_bitmap->fill(FRAMECOLOR, vis);
+ }
+
+ if (XPOS + VIC3_BITPLANES_WIDTH < visarea.max_x)
+ {
+ vis.set(XPOS + VIC3_BITPLANES_WIDTH, visarea.max_x, 0, visarea.max_y);
+ m_bitmap->fill(FRAMECOLOR, vis);
+ }
+
+ if (YPOS > 0)
+ {
+ vis.set(0, visarea.max_x, 0, YPOS - 1);
+ m_bitmap->fill(FRAMECOLOR, vis);
+ }
+
+ if (YPOS + VIC3_LINES < visarea.max_y)
+ {
+ vis.set(0, visarea.max_x, YPOS + VIC3_LINES, visarea.max_y);
+ m_bitmap->fill(FRAMECOLOR, vis);
+ }
+}
+
+void vic3_device::raster_interrupt_gen()
+{
+ int new_columns, new_rows;
+ int i;
+
+ m_rasterline++;
+ if (m_rasterline >= m_lines)
+ {
+ m_rasterline = 0;
+ if (m_palette_dirty)
+ for (i = 0; i < 256; i++)
+ m_palette->set_pen_color(i, m_palette_red[i] << 4, m_palette_green[i] << 4, m_palette_blue[i] << 4);
+
+ if (m_palette_dirty)
+ {
+ m_spritemulti[1] = SPRITE_MULTICOLOR1;
+ m_spritemulti[3] = SPRITE_MULTICOLOR2;
+ m_mono[0] = m_bitmapmulti[0] = m_multi[0] = m_colors[0] = BACKGROUNDCOLOR;
+ m_multi[1] = m_colors[1] = MULTICOLOR1;
+ m_multi[2] = m_colors[2] = MULTICOLOR2;
+ m_colors[3] = FOREGROUNDCOLOR;
+ m_palette_dirty = 0;
+ }
+
+ new_rows = 200;
+
+ if (VIC3_BITPLANES)
+ {
+ new_columns = VIC3_BITPLANES_WIDTH;
+ if (new_columns < 320)
+ new_columns = 320; /*sprites resolution about 320x200 */
+ new_rows = VIC3_LINES;
+ }
+ else if (VIC3_80COLUMNS)
+ {
+ new_columns = 640;
+ }
+ else
+ {
+ new_columns = 320;
+ }
+ if ((new_columns != m_columns) || (new_rows != m_rows))
+ {
+ m_rows = new_rows;
+ m_columns = new_columns;
+ if (m_type == VIC4567_PAL)
+ m_screen->set_visible_area(
+ VIC2_STARTVISIBLECOLUMNS + 32,
+ VIC2_STARTVISIBLECOLUMNS + 32 + m_columns + 16 - 1,
+ VIC2_STARTVISIBLELINES + 34,
+ VIC2_STARTVISIBLELINES + 34 + m_rows + 16 - 1);
+ else
+ m_screen->set_visible_area(
+ VIC2_STARTVISIBLECOLUMNS + 34,
+ VIC2_STARTVISIBLECOLUMNS + 34 + m_columns + 16 - 1,
+ VIC2_STARTVISIBLELINES + 10,
+ VIC2_STARTVISIBLELINES + 10 + m_rows + 16 - 1);
+ }
+ if (VIC3_BITPLANES)
+ {
+ draw_bitplanes();
+ }
+ else
+ {
+ if (m_type == VIC4567_PAL)
+ {
+ if (m_on)
+ vic2_drawlines(m_lastline, m_lines, VIC2_STARTVISIBLECOLUMNS + 32, VIC2_STARTVISIBLECOLUMNS + 32 + m_columns + 16 - 1);
+ }
+ else
+ {
+ if (m_on)
+ vic2_drawlines(m_lastline, m_lines, VIC2_STARTVISIBLECOLUMNS + 34, VIC2_STARTVISIBLECOLUMNS + 34 + m_columns + 16 - 1);
+ }
+ }
+
+ for (i = 0; i < 8; i++)
+ m_sprites[i].repeat = m_sprites[i].line = 0;
+
+ m_lastline = 0;
+
+ if (LIGHTPEN_BUTTON)
+ {
+ /* lightpen timer start */
+ machine().scheduler().timer_set(attotime(0, 0), timer_expired_delegate(FUNC(vic3_device::timer_timeout),this), 1);
+ }
+
+ }
+
+ if (m_rasterline == C64_2_RASTERLINE(RASTERLINE))
+ {
+ set_interrupt(1);
+ }
+
+ if (m_on)
+ if ((m_rasterline >= VIC2_FIRSTRASTERLINE) && (m_rasterline < (VIC2_FIRSTRASTERLINE + VIC2_VISIBLELINES)))
+ {
+ if (m_type == VIC4567_PAL)
+ {
+ if (m_on)
+ vic2_drawlines(m_rasterline - 1, m_rasterline, VIC2_STARTVISIBLECOLUMNS + 32, VIC2_STARTVISIBLECOLUMNS + 32 + m_columns + 16 - 1);
+ }
+ else
+ {
+ if (m_on)
+ vic2_drawlines(m_rasterline - 1, m_rasterline, VIC2_STARTVISIBLECOLUMNS + 34, VIC2_STARTVISIBLECOLUMNS + 34 + m_columns + 16 - 1);
+ }
+ }
+}
+
+UINT32 vic3_device::video_update( bitmap_ind16 &bitmap, const rectangle &cliprect )
+{
+ copybitmap(bitmap, *m_bitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+
+static MACHINE_CONFIG_FRAGMENT( vic3 )
+ MCFG_PALETTE_ADD_INIT_BLACK("palette", 0x100)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor vic3_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( vic3 );
+}
diff --git a/src/devices/video/vic4567.h b/src/devices/video/vic4567.h
new file mode 100644
index 00000000000..41d385397fa
--- /dev/null
+++ b/src/devices/video/vic4567.h
@@ -0,0 +1,273 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+/*****************************************************************************
+ *
+ * video/vic4567.h
+ *
+ ****************************************************************************/
+
+#ifndef __VIC4567_H__
+#define __VIC4567_H__
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+enum vic3_type
+{
+ VIC4567_NTSC,
+ VIC4567_PAL
+};
+
+#define SPRITE_BASE_X_SIZE 24
+#define SPRITE_BASE_Y_SIZE 21
+
+struct vic3_sprite
+{
+ int x, y;
+
+ int repeat; /* expand, line once drawn */
+ int line; /* 0 not painting, else painting */
+
+ /* buffer for currently painted line */
+ int paintedline[8];
+ UINT8 bitmap[8][SPRITE_BASE_X_SIZE * 2 / 8 + 1 /*for simplier sprite collision detection*/];
+};
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define VIC6567_CLOCK (1022700 /* = 8181600 / 8) */ )
+#define VIC6569_CLOCK ( 985248 /* = 7881984 / 8) */ )
+
+#define VIC6567_CYCLESPERLINE 65
+#define VIC6569_CYCLESPERLINE 63
+
+#define VIC6567_LINES 263
+#define VIC6569_LINES 312
+
+#define VIC6567_VRETRACERATE (59.8245100906698 /* = 1022700 / (65 * 263) */ )
+#define VIC6569_VRETRACERATE (50.1245421245421 /* = 985248 / (63 * 312) */ )
+
+#define VIC6567_HRETRACERATE (VIC6567_CLOCK / VIC6567_CYCLESPERLINE)
+#define VIC6569_HRETRACERATE (VIC6569_CLOCK / VIC6569_CYCLESPERLINE)
+
+#define VIC2_HSIZE 320
+#define VIC2_VSIZE 200
+
+#define VIC6567_VISIBLELINES 235
+#define VIC6569_VISIBLELINES 284
+
+#define VIC6567_FIRST_DMA_LINE 0x30
+#define VIC6569_FIRST_DMA_LINE 0x30
+
+#define VIC6567_LAST_DMA_LINE 0xf7
+#define VIC6569_LAST_DMA_LINE 0xf7
+
+#define VIC6567_FIRST_DISP_LINE 0x29
+#define VIC6569_FIRST_DISP_LINE 0x10
+
+#define VIC6567_LAST_DISP_LINE (VIC6567_FIRST_DISP_LINE + VIC6567_VISIBLELINES - 1)
+#define VIC6569_LAST_DISP_LINE (VIC6569_FIRST_DISP_LINE + VIC6569_VISIBLELINES - 1)
+
+#define VIC6567_RASTER_2_EMU(a) ((a >= VIC6567_FIRST_DISP_LINE) ? (a - VIC6567_FIRST_DISP_LINE) : (a + 222))
+#define VIC6569_RASTER_2_EMU(a) (a - VIC6569_FIRST_DISP_LINE)
+
+#define VIC6567_FIRSTCOLUMN 50
+#define VIC6569_FIRSTCOLUMN 50
+
+#define VIC6567_VISIBLECOLUMNS 418
+#define VIC6569_VISIBLECOLUMNS 403
+
+#define VIC6567_X_2_EMU(a) (a)
+#define VIC6569_X_2_EMU(a) (a)
+
+#define VIC6567_STARTVISIBLELINES ((VIC6567_LINES - VIC6567_VISIBLELINES)/2)
+#define VIC6569_STARTVISIBLELINES 16 /* ((VIC6569_LINES - VIC6569_VISIBLELINES)/2) */
+
+#define VIC6567_FIRSTRASTERLINE 34
+#define VIC6569_FIRSTRASTERLINE 0
+
+#define VIC6567_COLUMNS 512
+#define VIC6569_COLUMNS 504
+
+
+#define VIC6567_STARTVISIBLECOLUMNS ((VIC6567_COLUMNS - VIC6567_VISIBLECOLUMNS)/2)
+#define VIC6569_STARTVISIBLECOLUMNS ((VIC6569_COLUMNS - VIC6569_VISIBLECOLUMNS)/2)
+
+#define VIC6567_FIRSTRASTERCOLUMNS 412
+#define VIC6569_FIRSTRASTERCOLUMNS 404
+
+#define VIC6569_FIRST_X 0x194
+#define VIC6567_FIRST_X 0x19c
+
+#define VIC6569_FIRST_VISIBLE_X 0x1e0
+#define VIC6567_FIRST_VISIBLE_X 0x1e8
+
+#define VIC6569_MAX_X 0x1f7
+#define VIC6567_MAX_X 0x1ff
+
+#define VIC6569_LAST_VISIBLE_X 0x17c
+#define VIC6567_LAST_VISIBLE_X 0x184
+
+#define VIC6569_LAST_X 0x193
+#define VIC6567_LAST_X 0x19b
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+class vic3_device : public device_t,
+ public device_video_interface
+{
+public:
+ vic3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~vic3_device() {}
+
+ static void static_set_cpu_tag(device_t &device, const char *tag) { downcast<vic3_device &>(device).m_cpu.set_tag(tag); }
+ static void set_vic3_type(device_t &device, vic3_type type) { downcast<vic3_device &>(device).m_type = type; }
+ template<class _Object> static devcb_base &set_dma_read_callback(device_t &device, _Object object) { return downcast<vic3_device &>(device).m_dma_read_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_dma_read_color_callback(device_t &device, _Object object) { return downcast<vic3_device &>(device).m_dma_read_color_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_interrupt_callback(device_t &device, _Object object) { return downcast<vic3_device &>(device).m_interrupt_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_port_changed_callback(device_t &device, _Object object) { return downcast<vic3_device &>(device).m_port_changed_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_lightpen_button_callback(device_t &device, _Object object) { return downcast<vic3_device &>(device).m_lightpen_button_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_lightpen_x_callback(device_t &device, _Object object) { return downcast<vic3_device &>(device).m_lightpen_x_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_lightpen_y_callback(device_t &device, _Object object) { return downcast<vic3_device &>(device).m_lightpen_y_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_c64_mem_r_callback(device_t &device, _Object object) { return downcast<vic3_device &>(device).m_c64_mem_r_cb.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER(port_w);
+ DECLARE_WRITE8_MEMBER(palette_w);
+ DECLARE_READ8_MEMBER(port_r);
+
+ void raster_interrupt_gen();
+ UINT32 video_update(bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual machine_config_constructor device_mconfig_additions() const;
+private:
+ // internal state
+
+ inline int getforeground(int y, int x);
+ inline int getforeground16(int y, int x);
+ void set_interrupt(int mask);
+ void clear_interrupt(int mask);
+ void draw_character(int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color, int start_x, int end_x);
+ void draw_character_multi(int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x);
+ void draw_bitmap(int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x);
+ void draw_bitmap_multi(int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x);
+ void draw_sprite_code(int y, int xbegin, int code, int color, int start_x, int end_x);
+ void draw_sprite_code_multi(int y, int xbegin, int code, int prior, int start_x, int end_x);
+ void sprite_collision(int nr, int y, int x, int mask);
+ void draw_sprite(int nr, int yoff, int ybegin, int yend, int start_x, int end_x);
+ void draw_sprite_multi(int nr, int yoff, int ybegin, int yend, int start_x, int end_x);
+ void drawlines(int first, int last, int start_x, int end_x);
+ void vic2_drawlines(int first, int last, int start_x, int end_x);
+ void interlace_draw_block(int x, int y, int offset);
+ void draw_block(int x, int y, int offset);
+ void draw_bitplanes();
+
+ TIMER_CALLBACK_MEMBER(timer_timeout);
+
+ vic3_type m_type;
+
+ required_device<cpu_device> m_cpu;
+
+ UINT8 m_reg[0x80];
+ int m_on; /* rastering of the screen */
+
+ int m_lines;
+
+ UINT16 m_chargenaddr, m_videoaddr, m_bitmapaddr;
+
+ bitmap_ind16 *m_bitmap;
+ int m_x_begin, m_x_end;
+ int m_y_begin, m_y_end;
+
+ UINT16 m_c64_bitmap[2], m_bitmapmulti[4], m_mono[2], m_multi[4], m_ecmcolor[2], m_colors[4], m_spritemulti[4];
+
+ int m_lastline, m_rasterline;
+
+ int m_interlace;
+ int m_columns, m_rows;
+
+ /* background/foreground for sprite collision */
+ UINT8 *m_screenptr[216], m_shift[216];
+
+ /* convert multicolor byte to background/foreground for sprite collision */
+ UINT8 m_foreground[256];
+ UINT16 m_expandx[256];
+ UINT16 m_expandx_multi[256];
+
+ /* converts sprite multicolor info to info for background collision checking */
+ UINT8 m_multi_collision[256];
+
+ vic3_sprite m_sprites[8];
+
+ /* DMA */
+ devcb_read8 m_dma_read_cb;
+ devcb_read8 m_dma_read_color_cb;
+
+ /* IRQ */
+ devcb_write_line m_interrupt_cb;
+
+ /* Port Changed */
+ devcb_write8 m_port_changed_cb;
+
+ /* lightpen */
+ devcb_read8 m_lightpen_button_cb;
+ devcb_read8 m_lightpen_x_cb;
+ devcb_read8 m_lightpen_y_cb;
+
+ /* C64 memory access */
+ devcb_read8 m_c64_mem_r_cb;
+
+ /* palette - vic3 specific items (the ones above are used for VIC II as well) */
+ UINT8 m_palette_red[0x100];
+ UINT8 m_palette_green[0x100];
+ UINT8 m_palette_blue[0x100];
+ int m_palette_dirty;
+
+ required_device<palette_device> m_palette;
+};
+
+extern const device_type VIC3;
+
+
+#define MCFG_VIC3_CPU(_tag) \
+ vic3_device::static_set_cpu_tag(*device, "^" _tag);
+
+#define MCFG_VIC3_TYPE(_type) \
+ vic3_device::set_vic3_type(*device, _type);
+
+#define MCFG_VIC3_DMA_READ_CB(_devcb) \
+ devcb = &vic3_device::set_dma_read_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_VIC3_DMA_READ_COLOR_CB(_devcb) \
+ devcb = &vic3_device::set_dma_read_color_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_VIC3_INTERRUPT_CB(_devcb) \
+ devcb = &vic3_device::set_interrupt_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_VIC3_PORT_CHANGED_CB(_devcb) \
+ devcb = &vic3_device::set_port_changed_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_VIC3_LIGHTPEN_BUTTON_CB(_devcb) \
+ devcb = &vic3_device::set_lightpen_button_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_VIC3_LIGHTPEN_X_CB(_devcb) \
+ devcb = &vic3_device::set_lightpen_x_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_VIC3_LIGHTPEN_Y_CB(_devcb) \
+ devcb = &vic3_device::set_lightpen_y_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_VIC3_C64_MEM_R_CB(_devcb) \
+ devcb = &vic3_device::set_c64_mem_r_callback(*device, DEVCB_##_devcb);
+
+
+#endif /* __VIC4567_H__ */
diff --git a/src/devices/video/vooddefs.h b/src/devices/video/vooddefs.h
new file mode 100644
index 00000000000..f98aece2110
--- /dev/null
+++ b/src/devices/video/vooddefs.h
@@ -0,0 +1,5099 @@
+ // license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ vooddefs.h
+
+ 3dfx Voodoo Graphics SST-1/2 emulator.
+
+***************************************************************************/
+
+
+/*************************************
+ *
+ * Misc. constants
+ *
+ *************************************/
+
+/* enumeration describing reasons we might be stalled */
+enum
+{
+ NOT_STALLED = 0,
+ STALLED_UNTIL_FIFO_LWM,
+ STALLED_UNTIL_FIFO_EMPTY
+};
+
+// Use old macro style or newer SSE2 optimized functions
+#define USE_OLD_RASTER 0
+
+// Use old table lookup versus straight double divide
+#define USE_FAST_RECIP 0
+
+/* maximum number of TMUs */
+#define MAX_TMU 2
+
+/* accumulate operations less than this number of clocks */
+#define ACCUMULATE_THRESHOLD 0
+
+/* number of clocks to set up a triangle (just a guess) */
+#define TRIANGLE_SETUP_CLOCKS 100
+
+/* maximum number of rasterizers */
+#define MAX_RASTERIZERS 1024
+
+/* size of the rasterizer hash table */
+#define RASTER_HASH_SIZE 97
+
+/* flags for LFB writes */
+#define LFB_RGB_PRESENT 1
+#define LFB_ALPHA_PRESENT 2
+#define LFB_DEPTH_PRESENT 4
+#define LFB_DEPTH_PRESENT_MSW 8
+
+/* flags for the register access array */
+#define REGISTER_READ 0x01 /* reads are allowed */
+#define REGISTER_WRITE 0x02 /* writes are allowed */
+#define REGISTER_PIPELINED 0x04 /* writes are pipelined */
+#define REGISTER_FIFO 0x08 /* writes go to FIFO */
+#define REGISTER_WRITETHRU 0x10 /* writes are valid even for CMDFIFO */
+
+/* shorter combinations to make the table smaller */
+#define REG_R (REGISTER_READ)
+#define REG_W (REGISTER_WRITE)
+#define REG_WT (REGISTER_WRITE | REGISTER_WRITETHRU)
+#define REG_RW (REGISTER_READ | REGISTER_WRITE)
+#define REG_RWT (REGISTER_READ | REGISTER_WRITE | REGISTER_WRITETHRU)
+#define REG_RP (REGISTER_READ | REGISTER_PIPELINED)
+#define REG_WP (REGISTER_WRITE | REGISTER_PIPELINED)
+#define REG_RWP (REGISTER_READ | REGISTER_WRITE | REGISTER_PIPELINED)
+#define REG_RWPT (REGISTER_READ | REGISTER_WRITE | REGISTER_PIPELINED | REGISTER_WRITETHRU)
+#define REG_RF (REGISTER_READ | REGISTER_FIFO)
+#define REG_WF (REGISTER_WRITE | REGISTER_FIFO)
+#define REG_RWF (REGISTER_READ | REGISTER_WRITE | REGISTER_FIFO)
+#define REG_RPF (REGISTER_READ | REGISTER_PIPELINED | REGISTER_FIFO)
+#define REG_WPF (REGISTER_WRITE | REGISTER_PIPELINED | REGISTER_FIFO)
+#define REG_RWPF (REGISTER_READ | REGISTER_WRITE | REGISTER_PIPELINED | REGISTER_FIFO)
+
+/* lookup bits is the log2 of the size of the reciprocal/log table */
+#define RECIPLOG_LOOKUP_BITS 9
+
+/* input precision is how many fraction bits the input value has; this is a 64-bit number */
+#define RECIPLOG_INPUT_PREC 32
+
+/* lookup precision is how many fraction bits each table entry contains */
+#define RECIPLOG_LOOKUP_PREC 22
+
+/* output precision is how many fraction bits the result should have */
+#define RECIP_OUTPUT_PREC 15
+#define LOG_OUTPUT_PREC 8
+
+
+
+/*************************************
+ *
+ * Register constants
+ *
+ *************************************/
+
+/* Codes to the right:
+ R = readable
+ W = writeable
+ P = pipelined
+ F = goes to FIFO
+*/
+
+/* 0x000 */
+#define status (0x000/4) /* R P */
+#define intrCtrl (0x004/4) /* RW P -- Voodoo2/Banshee only */
+#define vertexAx (0x008/4) /* W PF */
+#define vertexAy (0x00c/4) /* W PF */
+#define vertexBx (0x010/4) /* W PF */
+#define vertexBy (0x014/4) /* W PF */
+#define vertexCx (0x018/4) /* W PF */
+#define vertexCy (0x01c/4) /* W PF */
+#define startR (0x020/4) /* W PF */
+#define startG (0x024/4) /* W PF */
+#define startB (0x028/4) /* W PF */
+#define startZ (0x02c/4) /* W PF */
+#define startA (0x030/4) /* W PF */
+#define startS (0x034/4) /* W PF */
+#define startT (0x038/4) /* W PF */
+#define startW (0x03c/4) /* W PF */
+
+/* 0x040 */
+#define dRdX (0x040/4) /* W PF */
+#define dGdX (0x044/4) /* W PF */
+#define dBdX (0x048/4) /* W PF */
+#define dZdX (0x04c/4) /* W PF */
+#define dAdX (0x050/4) /* W PF */
+#define dSdX (0x054/4) /* W PF */
+#define dTdX (0x058/4) /* W PF */
+#define dWdX (0x05c/4) /* W PF */
+#define dRdY (0x060/4) /* W PF */
+#define dGdY (0x064/4) /* W PF */
+#define dBdY (0x068/4) /* W PF */
+#define dZdY (0x06c/4) /* W PF */
+#define dAdY (0x070/4) /* W PF */
+#define dSdY (0x074/4) /* W PF */
+#define dTdY (0x078/4) /* W PF */
+#define dWdY (0x07c/4) /* W PF */
+
+/* 0x080 */
+#define triangleCMD (0x080/4) /* W PF */
+#define fvertexAx (0x088/4) /* W PF */
+#define fvertexAy (0x08c/4) /* W PF */
+#define fvertexBx (0x090/4) /* W PF */
+#define fvertexBy (0x094/4) /* W PF */
+#define fvertexCx (0x098/4) /* W PF */
+#define fvertexCy (0x09c/4) /* W PF */
+#define fstartR (0x0a0/4) /* W PF */
+#define fstartG (0x0a4/4) /* W PF */
+#define fstartB (0x0a8/4) /* W PF */
+#define fstartZ (0x0ac/4) /* W PF */
+#define fstartA (0x0b0/4) /* W PF */
+#define fstartS (0x0b4/4) /* W PF */
+#define fstartT (0x0b8/4) /* W PF */
+#define fstartW (0x0bc/4) /* W PF */
+
+/* 0x0c0 */
+#define fdRdX (0x0c0/4) /* W PF */
+#define fdGdX (0x0c4/4) /* W PF */
+#define fdBdX (0x0c8/4) /* W PF */
+#define fdZdX (0x0cc/4) /* W PF */
+#define fdAdX (0x0d0/4) /* W PF */
+#define fdSdX (0x0d4/4) /* W PF */
+#define fdTdX (0x0d8/4) /* W PF */
+#define fdWdX (0x0dc/4) /* W PF */
+#define fdRdY (0x0e0/4) /* W PF */
+#define fdGdY (0x0e4/4) /* W PF */
+#define fdBdY (0x0e8/4) /* W PF */
+#define fdZdY (0x0ec/4) /* W PF */
+#define fdAdY (0x0f0/4) /* W PF */
+#define fdSdY (0x0f4/4) /* W PF */
+#define fdTdY (0x0f8/4) /* W PF */
+#define fdWdY (0x0fc/4) /* W PF */
+
+/* 0x100 */
+#define ftriangleCMD (0x100/4) /* W PF */
+#define fbzColorPath (0x104/4) /* RW PF */
+#define fogMode (0x108/4) /* RW PF */
+#define alphaMode (0x10c/4) /* RW PF */
+#define fbzMode (0x110/4) /* RW F */
+#define lfbMode (0x114/4) /* RW F */
+#define clipLeftRight (0x118/4) /* RW F */
+#define clipLowYHighY (0x11c/4) /* RW F */
+#define nopCMD (0x120/4) /* W F */
+#define fastfillCMD (0x124/4) /* W F */
+#define swapbufferCMD (0x128/4) /* W F */
+#define fogColor (0x12c/4) /* W F */
+#define zaColor (0x130/4) /* W F */
+#define chromaKey (0x134/4) /* W F */
+#define chromaRange (0x138/4) /* W F -- Voodoo2/Banshee only */
+#define userIntrCMD (0x13c/4) /* W F -- Voodoo2/Banshee only */
+
+/* 0x140 */
+#define stipple (0x140/4) /* RW F */
+#define color0 (0x144/4) /* RW F */
+#define color1 (0x148/4) /* RW F */
+#define fbiPixelsIn (0x14c/4) /* R */
+#define fbiChromaFail (0x150/4) /* R */
+#define fbiZfuncFail (0x154/4) /* R */
+#define fbiAfuncFail (0x158/4) /* R */
+#define fbiPixelsOut (0x15c/4) /* R */
+#define fogTable (0x160/4) /* W F */
+
+/* 0x1c0 */
+#define cmdFifoBaseAddr (0x1e0/4) /* RW -- Voodoo2 only */
+#define cmdFifoBump (0x1e4/4) /* RW -- Voodoo2 only */
+#define cmdFifoRdPtr (0x1e8/4) /* RW -- Voodoo2 only */
+#define cmdFifoAMin (0x1ec/4) /* RW -- Voodoo2 only */
+#define colBufferAddr (0x1ec/4) /* RW -- Banshee only */
+#define cmdFifoAMax (0x1f0/4) /* RW -- Voodoo2 only */
+#define colBufferStride (0x1f0/4) /* RW -- Banshee only */
+#define cmdFifoDepth (0x1f4/4) /* RW -- Voodoo2 only */
+#define auxBufferAddr (0x1f4/4) /* RW -- Banshee only */
+#define cmdFifoHoles (0x1f8/4) /* RW -- Voodoo2 only */
+#define auxBufferStride (0x1f8/4) /* RW -- Banshee only */
+
+/* 0x200 */
+#define fbiInit4 (0x200/4) /* RW -- Voodoo/Voodoo2 only */
+#define clipLeftRight1 (0x200/4) /* RW -- Banshee only */
+#define vRetrace (0x204/4) /* R -- Voodoo/Voodoo2 only */
+#define clipTopBottom1 (0x204/4) /* RW -- Banshee only */
+#define backPorch (0x208/4) /* RW -- Voodoo/Voodoo2 only */
+#define videoDimensions (0x20c/4) /* RW -- Voodoo/Voodoo2 only */
+#define fbiInit0 (0x210/4) /* RW -- Voodoo/Voodoo2 only */
+#define fbiInit1 (0x214/4) /* RW -- Voodoo/Voodoo2 only */
+#define fbiInit2 (0x218/4) /* RW -- Voodoo/Voodoo2 only */
+#define fbiInit3 (0x21c/4) /* RW -- Voodoo/Voodoo2 only */
+#define hSync (0x220/4) /* W -- Voodoo/Voodoo2 only */
+#define vSync (0x224/4) /* W -- Voodoo/Voodoo2 only */
+#define clutData (0x228/4) /* W F -- Voodoo/Voodoo2 only */
+#define dacData (0x22c/4) /* W -- Voodoo/Voodoo2 only */
+#define maxRgbDelta (0x230/4) /* W -- Voodoo/Voodoo2 only */
+#define hBorder (0x234/4) /* W -- Voodoo2 only */
+#define vBorder (0x238/4) /* W -- Voodoo2 only */
+#define borderColor (0x23c/4) /* W -- Voodoo2 only */
+
+/* 0x240 */
+#define hvRetrace (0x240/4) /* R -- Voodoo2 only */
+#define fbiInit5 (0x244/4) /* RW -- Voodoo2 only */
+#define fbiInit6 (0x248/4) /* RW -- Voodoo2 only */
+#define fbiInit7 (0x24c/4) /* RW -- Voodoo2 only */
+#define swapPending (0x24c/4) /* W -- Banshee only */
+#define leftOverlayBuf (0x250/4) /* W -- Banshee only */
+#define rightOverlayBuf (0x254/4) /* W -- Banshee only */
+#define fbiSwapHistory (0x258/4) /* R -- Voodoo2/Banshee only */
+#define fbiTrianglesOut (0x25c/4) /* R -- Voodoo2/Banshee only */
+#define sSetupMode (0x260/4) /* W PF -- Voodoo2/Banshee only */
+#define sVx (0x264/4) /* W PF -- Voodoo2/Banshee only */
+#define sVy (0x268/4) /* W PF -- Voodoo2/Banshee only */
+#define sARGB (0x26c/4) /* W PF -- Voodoo2/Banshee only */
+#define sRed (0x270/4) /* W PF -- Voodoo2/Banshee only */
+#define sGreen (0x274/4) /* W PF -- Voodoo2/Banshee only */
+#define sBlue (0x278/4) /* W PF -- Voodoo2/Banshee only */
+#define sAlpha (0x27c/4) /* W PF -- Voodoo2/Banshee only */
+
+/* 0x280 */
+#define sVz (0x280/4) /* W PF -- Voodoo2/Banshee only */
+#define sWb (0x284/4) /* W PF -- Voodoo2/Banshee only */
+#define sWtmu0 (0x288/4) /* W PF -- Voodoo2/Banshee only */
+#define sS_W0 (0x28c/4) /* W PF -- Voodoo2/Banshee only */
+#define sT_W0 (0x290/4) /* W PF -- Voodoo2/Banshee only */
+#define sWtmu1 (0x294/4) /* W PF -- Voodoo2/Banshee only */
+#define sS_Wtmu1 (0x298/4) /* W PF -- Voodoo2/Banshee only */
+#define sT_Wtmu1 (0x29c/4) /* W PF -- Voodoo2/Banshee only */
+#define sDrawTriCMD (0x2a0/4) /* W PF -- Voodoo2/Banshee only */
+#define sBeginTriCMD (0x2a4/4) /* W PF -- Voodoo2/Banshee only */
+
+/* 0x2c0 */
+#define bltSrcBaseAddr (0x2c0/4) /* RW PF -- Voodoo2 only */
+#define bltDstBaseAddr (0x2c4/4) /* RW PF -- Voodoo2 only */
+#define bltXYStrides (0x2c8/4) /* RW PF -- Voodoo2 only */
+#define bltSrcChromaRange (0x2cc/4) /* RW PF -- Voodoo2 only */
+#define bltDstChromaRange (0x2d0/4) /* RW PF -- Voodoo2 only */
+#define bltClipX (0x2d4/4) /* RW PF -- Voodoo2 only */
+#define bltClipY (0x2d8/4) /* RW PF -- Voodoo2 only */
+#define bltSrcXY (0x2e0/4) /* RW PF -- Voodoo2 only */
+#define bltDstXY (0x2e4/4) /* RW PF -- Voodoo2 only */
+#define bltSize (0x2e8/4) /* RW PF -- Voodoo2 only */
+#define bltRop (0x2ec/4) /* RW PF -- Voodoo2 only */
+#define bltColor (0x2f0/4) /* RW PF -- Voodoo2 only */
+#define bltCommand (0x2f8/4) /* RW PF -- Voodoo2 only */
+#define bltData (0x2fc/4) /* W PF -- Voodoo2 only */
+
+/* 0x300 */
+#define textureMode (0x300/4) /* W PF */
+#define tLOD (0x304/4) /* W PF */
+#define tDetail (0x308/4) /* W PF */
+#define texBaseAddr (0x30c/4) /* W PF */
+#define texBaseAddr_1 (0x310/4) /* W PF */
+#define texBaseAddr_2 (0x314/4) /* W PF */
+#define texBaseAddr_3_8 (0x318/4) /* W PF */
+#define trexInit0 (0x31c/4) /* W F -- Voodoo/Voodoo2 only */
+#define trexInit1 (0x320/4) /* W F */
+#define nccTable (0x324/4) /* W F */
+
+
+
+// 2D registers
+#define banshee2D_clip0Min (0x008/4)
+#define banshee2D_clip0Max (0x00c/4)
+#define banshee2D_dstBaseAddr (0x010/4)
+#define banshee2D_dstFormat (0x014/4)
+#define banshee2D_srcColorkeyMin (0x018/4)
+#define banshee2D_srcColorkeyMax (0x01c/4)
+#define banshee2D_dstColorkeyMin (0x020/4)
+#define banshee2D_dstColorkeyMax (0x024/4)
+#define banshee2D_bresError0 (0x028/4)
+#define banshee2D_bresError1 (0x02c/4)
+#define banshee2D_rop (0x030/4)
+#define banshee2D_srcBaseAddr (0x034/4)
+#define banshee2D_commandExtra (0x038/4)
+#define banshee2D_lineStipple (0x03c/4)
+#define banshee2D_lineStyle (0x040/4)
+#define banshee2D_pattern0Alias (0x044/4)
+#define banshee2D_pattern1Alias (0x048/4)
+#define banshee2D_clip1Min (0x04c/4)
+#define banshee2D_clip1Max (0x050/4)
+#define banshee2D_srcFormat (0x054/4)
+#define banshee2D_srcSize (0x058/4)
+#define banshee2D_srcXY (0x05c/4)
+#define banshee2D_colorBack (0x060/4)
+#define banshee2D_colorFore (0x064/4)
+#define banshee2D_dstSize (0x068/4)
+#define banshee2D_dstXY (0x06c/4)
+#define banshee2D_command (0x070/4)
+
+
+/*************************************
+ *
+ * Alias map of the first 64
+ * registers when remapped
+ *
+ *************************************/
+
+static const UINT8 register_alias_map[0x40] =
+{
+ status, 0x004/4, vertexAx, vertexAy,
+ vertexBx, vertexBy, vertexCx, vertexCy,
+ startR, dRdX, dRdY, startG,
+ dGdX, dGdY, startB, dBdX,
+ dBdY, startZ, dZdX, dZdY,
+ startA, dAdX, dAdY, startS,
+ dSdX, dSdY, startT, dTdX,
+ dTdY, startW, dWdX, dWdY,
+
+ triangleCMD,0x084/4, fvertexAx, fvertexAy,
+ fvertexBx, fvertexBy, fvertexCx, fvertexCy,
+ fstartR, fdRdX, fdRdY, fstartG,
+ fdGdX, fdGdY, fstartB, fdBdX,
+ fdBdY, fstartZ, fdZdX, fdZdY,
+ fstartA, fdAdX, fdAdY, fstartS,
+ fdSdX, fdSdY, fstartT, fdTdX,
+ fdTdY, fstartW, fdWdX, fdWdY
+};
+
+
+
+/*************************************
+ *
+ * Table of per-register access rights
+ *
+ *************************************/
+
+static const UINT8 voodoo_register_access[0x100] =
+{
+ /* 0x000 */
+ REG_RP, 0, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x040 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x080 */
+ REG_WPF, 0, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x0c0 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x100 */
+ REG_WPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWF, REG_RWF, REG_RWF, REG_RWF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, 0, 0,
+
+ /* 0x140 */
+ REG_RWF, REG_RWF, REG_RWF, REG_R,
+ REG_R, REG_R, REG_R, REG_R,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x180 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x1c0 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x200 */
+ REG_RW, REG_R, REG_RW, REG_RW,
+ REG_RW, REG_RW, REG_RW, REG_RW,
+ REG_W, REG_W, REG_W, REG_W,
+ REG_W, 0, 0, 0,
+
+ /* 0x240 */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x280 */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x2c0 */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x300 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x340 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x380 */
+ REG_WF
+};
+
+
+static const UINT8 voodoo2_register_access[0x100] =
+{
+ /* 0x000 */
+ REG_RP, REG_RWPT, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x040 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x080 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x0c0 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x100 */
+ REG_WPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWF, REG_RWF, REG_RWF, REG_RWF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x140 */
+ REG_RWF, REG_RWF, REG_RWF, REG_R,
+ REG_R, REG_R, REG_R, REG_R,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x180 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x1c0 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_RWT, REG_RWT, REG_RWT, REG_RWT,
+ REG_RWT, REG_RWT, REG_RWT, REG_RW,
+
+ /* 0x200 */
+ REG_RWT, REG_R, REG_RWT, REG_RWT,
+ REG_RWT, REG_RWT, REG_RWT, REG_RWT,
+ REG_WT, REG_WT, REG_WF, REG_WT,
+ REG_WT, REG_WT, REG_WT, REG_WT,
+
+ /* 0x240 */
+ REG_R, REG_RWT, REG_RWT, REG_RWT,
+ 0, 0, REG_R, REG_R,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x280 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x2c0 */
+ REG_RWPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWPF, REG_RWPF, REG_RWPF, REG_WPF,
+
+ /* 0x300 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x340 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x380 */
+ REG_WF
+};
+
+
+static const UINT8 banshee_register_access[0x100] =
+{
+ /* 0x000 */
+ REG_RP, REG_RWPT, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x040 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x080 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x0c0 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x100 */
+ REG_WPF, REG_RWPF, REG_RWPF, REG_RWPF,
+ REG_RWF, REG_RWF, REG_RWF, REG_RWF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x140 */
+ REG_RWF, REG_RWF, REG_RWF, REG_R,
+ REG_R, REG_R, REG_R, REG_R,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x180 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x1c0 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ 0, 0, 0, REG_RWF,
+ REG_RWF, REG_RWF, REG_RWF, 0,
+
+ /* 0x200 */
+ REG_RWF, REG_RWF, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x240 */
+ 0, 0, 0, REG_WT,
+ REG_RWF, REG_RWF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_R, REG_R,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+
+ /* 0x280 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x2c0 */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ /* 0x300 */
+ REG_WPF, REG_WPF, REG_WPF, REG_WPF,
+ REG_WPF, REG_WPF, REG_WPF, 0,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x340 */
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+ REG_WF, REG_WF, REG_WF, REG_WF,
+
+ /* 0x380 */
+ REG_WF
+};
+
+
+
+/*************************************
+ *
+ * Register string table for debug
+ *
+ *************************************/
+
+static const char *const voodoo_reg_name[] =
+{
+ /* 0x000 */
+ "status", "{intrCtrl}", "vertexAx", "vertexAy",
+ "vertexBx", "vertexBy", "vertexCx", "vertexCy",
+ "startR", "startG", "startB", "startZ",
+ "startA", "startS", "startT", "startW",
+ /* 0x040 */
+ "dRdX", "dGdX", "dBdX", "dZdX",
+ "dAdX", "dSdX", "dTdX", "dWdX",
+ "dRdY", "dGdY", "dBdY", "dZdY",
+ "dAdY", "dSdY", "dTdY", "dWdY",
+ /* 0x080 */
+ "triangleCMD", "reserved084", "fvertexAx", "fvertexAy",
+ "fvertexBx", "fvertexBy", "fvertexCx", "fvertexCy",
+ "fstartR", "fstartG", "fstartB", "fstartZ",
+ "fstartA", "fstartS", "fstartT", "fstartW",
+ /* 0x0c0 */
+ "fdRdX", "fdGdX", "fdBdX", "fdZdX",
+ "fdAdX", "fdSdX", "fdTdX", "fdWdX",
+ "fdRdY", "fdGdY", "fdBdY", "fdZdY",
+ "fdAdY", "fdSdY", "fdTdY", "fdWdY",
+ /* 0x100 */
+ "ftriangleCMD", "fbzColorPath", "fogMode", "alphaMode",
+ "fbzMode", "lfbMode", "clipLeftRight","clipLowYHighY",
+ "nopCMD", "fastfillCMD", "swapbufferCMD","fogColor",
+ "zaColor", "chromaKey", "{chromaRange}","{userIntrCMD}",
+ /* 0x140 */
+ "stipple", "color0", "color1", "fbiPixelsIn",
+ "fbiChromaFail","fbiZfuncFail", "fbiAfuncFail", "fbiPixelsOut",
+ "fogTable160", "fogTable164", "fogTable168", "fogTable16c",
+ "fogTable170", "fogTable174", "fogTable178", "fogTable17c",
+ /* 0x180 */
+ "fogTable180", "fogTable184", "fogTable188", "fogTable18c",
+ "fogTable190", "fogTable194", "fogTable198", "fogTable19c",
+ "fogTable1a0", "fogTable1a4", "fogTable1a8", "fogTable1ac",
+ "fogTable1b0", "fogTable1b4", "fogTable1b8", "fogTable1bc",
+ /* 0x1c0 */
+ "fogTable1c0", "fogTable1c4", "fogTable1c8", "fogTable1cc",
+ "fogTable1d0", "fogTable1d4", "fogTable1d8", "fogTable1dc",
+ "{cmdFifoBaseAddr}","{cmdFifoBump}","{cmdFifoRdPtr}","{cmdFifoAMin}",
+ "{cmdFifoAMax}","{cmdFifoDepth}","{cmdFifoHoles}","reserved1fc",
+ /* 0x200 */
+ "fbiInit4", "vRetrace", "backPorch", "videoDimensions",
+ "fbiInit0", "fbiInit1", "fbiInit2", "fbiInit3",
+ "hSync", "vSync", "clutData", "dacData",
+ "maxRgbDelta", "{hBorder}", "{vBorder}", "{borderColor}",
+ /* 0x240 */
+ "{hvRetrace}", "{fbiInit5}", "{fbiInit6}", "{fbiInit7}",
+ "reserved250", "reserved254", "{fbiSwapHistory}","{fbiTrianglesOut}",
+ "{sSetupMode}", "{sVx}", "{sVy}", "{sARGB}",
+ "{sRed}", "{sGreen}", "{sBlue}", "{sAlpha}",
+ /* 0x280 */
+ "{sVz}", "{sWb}", "{sWtmu0}", "{sS/Wtmu0}",
+ "{sT/Wtmu0}", "{sWtmu1}", "{sS/Wtmu1}", "{sT/Wtmu1}",
+ "{sDrawTriCMD}","{sBeginTriCMD}","reserved2a8", "reserved2ac",
+ "reserved2b0", "reserved2b4", "reserved2b8", "reserved2bc",
+ /* 0x2c0 */
+ "{bltSrcBaseAddr}","{bltDstBaseAddr}","{bltXYStrides}","{bltSrcChromaRange}",
+ "{bltDstChromaRange}","{bltClipX}","{bltClipY}","reserved2dc",
+ "{bltSrcXY}", "{bltDstXY}", "{bltSize}", "{bltRop}",
+ "{bltColor}", "reserved2f4", "{bltCommand}", "{bltData}",
+ /* 0x300 */
+ "textureMode", "tLOD", "tDetail", "texBaseAddr",
+ "texBaseAddr_1","texBaseAddr_2","texBaseAddr_3_8","trexInit0",
+ "trexInit1", "nccTable0.0", "nccTable0.1", "nccTable0.2",
+ "nccTable0.3", "nccTable0.4", "nccTable0.5", "nccTable0.6",
+ /* 0x340 */
+ "nccTable0.7", "nccTable0.8", "nccTable0.9", "nccTable0.A",
+ "nccTable0.B", "nccTable1.0", "nccTable1.1", "nccTable1.2",
+ "nccTable1.3", "nccTable1.4", "nccTable1.5", "nccTable1.6",
+ "nccTable1.7", "nccTable1.8", "nccTable1.9", "nccTable1.A",
+ /* 0x380 */
+ "nccTable1.B"
+};
+
+
+static const char *const banshee_reg_name[] =
+{
+ /* 0x000 */
+ "status", "intrCtrl", "vertexAx", "vertexAy",
+ "vertexBx", "vertexBy", "vertexCx", "vertexCy",
+ "startR", "startG", "startB", "startZ",
+ "startA", "startS", "startT", "startW",
+ /* 0x040 */
+ "dRdX", "dGdX", "dBdX", "dZdX",
+ "dAdX", "dSdX", "dTdX", "dWdX",
+ "dRdY", "dGdY", "dBdY", "dZdY",
+ "dAdY", "dSdY", "dTdY", "dWdY",
+ /* 0x080 */
+ "triangleCMD", "reserved084", "fvertexAx", "fvertexAy",
+ "fvertexBx", "fvertexBy", "fvertexCx", "fvertexCy",
+ "fstartR", "fstartG", "fstartB", "fstartZ",
+ "fstartA", "fstartS", "fstartT", "fstartW",
+ /* 0x0c0 */
+ "fdRdX", "fdGdX", "fdBdX", "fdZdX",
+ "fdAdX", "fdSdX", "fdTdX", "fdWdX",
+ "fdRdY", "fdGdY", "fdBdY", "fdZdY",
+ "fdAdY", "fdSdY", "fdTdY", "fdWdY",
+ /* 0x100 */
+ "ftriangleCMD", "fbzColorPath", "fogMode", "alphaMode",
+ "fbzMode", "lfbMode", "clipLeftRight","clipLowYHighY",
+ "nopCMD", "fastfillCMD", "swapbufferCMD","fogColor",
+ "zaColor", "chromaKey", "chromaRange", "userIntrCMD",
+ /* 0x140 */
+ "stipple", "color0", "color1", "fbiPixelsIn",
+ "fbiChromaFail","fbiZfuncFail", "fbiAfuncFail", "fbiPixelsOut",
+ "fogTable160", "fogTable164", "fogTable168", "fogTable16c",
+ "fogTable170", "fogTable174", "fogTable178", "fogTable17c",
+ /* 0x180 */
+ "fogTable180", "fogTable184", "fogTable188", "fogTable18c",
+ "fogTable190", "fogTable194", "fogTable198", "fogTable19c",
+ "fogTable1a0", "fogTable1a4", "fogTable1a8", "fogTable1ac",
+ "fogTable1b0", "fogTable1b4", "fogTable1b8", "fogTable1bc",
+ /* 0x1c0 */
+ "fogTable1c0", "fogTable1c4", "fogTable1c8", "fogTable1cc",
+ "fogTable1d0", "fogTable1d4", "fogTable1d8", "fogTable1dc",
+ "reserved1e0", "reserved1e4", "reserved1e8", "colBufferAddr",
+ "colBufferStride","auxBufferAddr","auxBufferStride","reserved1fc",
+ /* 0x200 */
+ "clipLeftRight1","clipTopBottom1","reserved208","reserved20c",
+ "reserved210", "reserved214", "reserved218", "reserved21c",
+ "reserved220", "reserved224", "reserved228", "reserved22c",
+ "reserved230", "reserved234", "reserved238", "reserved23c",
+ /* 0x240 */
+ "reserved240", "reserved244", "reserved248", "swapPending",
+ "leftOverlayBuf","rightOverlayBuf","fbiSwapHistory","fbiTrianglesOut",
+ "sSetupMode", "sVx", "sVy", "sARGB",
+ "sRed", "sGreen", "sBlue", "sAlpha",
+ /* 0x280 */
+ "sVz", "sWb", "sWtmu0", "sS/Wtmu0",
+ "sT/Wtmu0", "sWtmu1", "sS/Wtmu1", "sT/Wtmu1",
+ "sDrawTriCMD", "sBeginTriCMD", "reserved2a8", "reserved2ac",
+ "reserved2b0", "reserved2b4", "reserved2b8", "reserved2bc",
+ /* 0x2c0 */
+ "reserved2c0", "reserved2c4", "reserved2c8", "reserved2cc",
+ "reserved2d0", "reserved2d4", "reserved2d8", "reserved2dc",
+ "reserved2e0", "reserved2e4", "reserved2e8", "reserved2ec",
+ "reserved2f0", "reserved2f4", "reserved2f8", "reserved2fc",
+ /* 0x300 */
+ "textureMode", "tLOD", "tDetail", "texBaseAddr",
+ "texBaseAddr_1","texBaseAddr_2","texBaseAddr_3_8","reserved31c",
+ "trexInit1", "nccTable0.0", "nccTable0.1", "nccTable0.2",
+ "nccTable0.3", "nccTable0.4", "nccTable0.5", "nccTable0.6",
+ /* 0x340 */
+ "nccTable0.7", "nccTable0.8", "nccTable0.9", "nccTable0.A",
+ "nccTable0.B", "nccTable1.0", "nccTable1.1", "nccTable1.2",
+ "nccTable1.3", "nccTable1.4", "nccTable1.5", "nccTable1.6",
+ "nccTable1.7", "nccTable1.8", "nccTable1.9", "nccTable1.A",
+ /* 0x380 */
+ "nccTable1.B"
+};
+
+
+
+/*************************************
+ *
+ * Voodoo Banshee I/O space registers
+ *
+ *************************************/
+
+/* 0x000 */
+#define io_status (0x000/4) /* */
+#define io_pciInit0 (0x004/4) /* */
+#define io_sipMonitor (0x008/4) /* */
+#define io_lfbMemoryConfig (0x00c/4) /* */
+#define io_miscInit0 (0x010/4) /* */
+#define io_miscInit1 (0x014/4) /* */
+#define io_dramInit0 (0x018/4) /* */
+#define io_dramInit1 (0x01c/4) /* */
+#define io_agpInit (0x020/4) /* */
+#define io_tmuGbeInit (0x024/4) /* */
+#define io_vgaInit0 (0x028/4) /* */
+#define io_vgaInit1 (0x02c/4) /* */
+#define io_dramCommand (0x030/4) /* */
+#define io_dramData (0x034/4) /* */
+
+/* 0x040 */
+#define io_pllCtrl0 (0x040/4) /* */
+#define io_pllCtrl1 (0x044/4) /* */
+#define io_pllCtrl2 (0x048/4) /* */
+#define io_dacMode (0x04c/4) /* */
+#define io_dacAddr (0x050/4) /* */
+#define io_dacData (0x054/4) /* */
+#define io_rgbMaxDelta (0x058/4) /* */
+#define io_vidProcCfg (0x05c/4) /* */
+#define io_hwCurPatAddr (0x060/4) /* */
+#define io_hwCurLoc (0x064/4) /* */
+#define io_hwCurC0 (0x068/4) /* */
+#define io_hwCurC1 (0x06c/4) /* */
+#define io_vidInFormat (0x070/4) /* */
+#define io_vidInStatus (0x074/4) /* */
+#define io_vidSerialParallelPort (0x078/4) /* */
+#define io_vidInXDecimDeltas (0x07c/4) /* */
+
+/* 0x080 */
+#define io_vidInDecimInitErrs (0x080/4) /* */
+#define io_vidInYDecimDeltas (0x084/4) /* */
+#define io_vidPixelBufThold (0x088/4) /* */
+#define io_vidChromaMin (0x08c/4) /* */
+#define io_vidChromaMax (0x090/4) /* */
+#define io_vidCurrentLine (0x094/4) /* */
+#define io_vidScreenSize (0x098/4) /* */
+#define io_vidOverlayStartCoords (0x09c/4) /* */
+#define io_vidOverlayEndScreenCoord (0x0a0/4) /* */
+#define io_vidOverlayDudx (0x0a4/4) /* */
+#define io_vidOverlayDudxOffsetSrcWidth (0x0a8/4) /* */
+#define io_vidOverlayDvdy (0x0ac/4) /* */
+#define io_vgab0 (0x0b0/4) /* */
+#define io_vgab4 (0x0b4/4) /* */
+#define io_vgab8 (0x0b8/4) /* */
+#define io_vgabc (0x0bc/4) /* */
+
+/* 0x0c0 */
+#define io_vgac0 (0x0c0/4) /* */
+#define io_vgac4 (0x0c4/4) /* */
+#define io_vgac8 (0x0c8/4) /* */
+#define io_vgacc (0x0cc/4) /* */
+#define io_vgad0 (0x0d0/4) /* */
+#define io_vgad4 (0x0d4/4) /* */
+#define io_vgad8 (0x0d8/4) /* */
+#define io_vgadc (0x0dc/4) /* */
+#define io_vidOverlayDvdyOffset (0x0e0/4) /* */
+#define io_vidDesktopStartAddr (0x0e4/4) /* */
+#define io_vidDesktopOverlayStride (0x0e8/4) /* */
+#define io_vidInAddr0 (0x0ec/4) /* */
+#define io_vidInAddr1 (0x0f0/4) /* */
+#define io_vidInAddr2 (0x0f4/4) /* */
+#define io_vidInStride (0x0f8/4) /* */
+#define io_vidCurrOverlayStartAddr (0x0fc/4) /* */
+
+
+
+/*************************************
+ *
+ * Register string table for debug
+ *
+ *************************************/
+
+static const char *const banshee_io_reg_name[] =
+{
+ /* 0x000 */
+ "status", "pciInit0", "sipMonitor", "lfbMemoryConfig",
+ "miscInit0", "miscInit1", "dramInit0", "dramInit1",
+ "agpInit", "tmuGbeInit", "vgaInit0", "vgaInit1",
+ "dramCommand", "dramData", "reserved38", "reserved3c",
+
+ /* 0x040 */
+ "pllCtrl0", "pllCtrl1", "pllCtrl2", "dacMode",
+ "dacAddr", "dacData", "rgbMaxDelta", "vidProcCfg",
+ "hwCurPatAddr", "hwCurLoc", "hwCurC0", "hwCurC1",
+ "vidInFormat", "vidInStatus", "vidSerialParallelPort","vidInXDecimDeltas",
+
+ /* 0x080 */
+ "vidInDecimInitErrs","vidInYDecimDeltas","vidPixelBufThold","vidChromaMin",
+ "vidChromaMax", "vidCurrentLine","vidScreenSize","vidOverlayStartCoords",
+ "vidOverlayEndScreenCoord","vidOverlayDudx","vidOverlayDudxOffsetSrcWidth","vidOverlayDvdy",
+ "vga[b0]", "vga[b4]", "vga[b8]", "vga[bc]",
+
+ /* 0x0c0 */
+ "vga[c0]", "vga[c4]", "vga[c8]", "vga[cc]",
+ "vga[d0]", "vga[d4]", "vga[d8]", "vga[dc]",
+ "vidOverlayDvdyOffset","vidDesktopStartAddr","vidDesktopOverlayStride","vidInAddr0",
+ "vidInAddr1", "vidInAddr2", "vidInStride", "vidCurrOverlayStartAddr"
+};
+
+
+
+/*************************************
+ *
+ * Voodoo Banshee AGP space registers
+ *
+ *************************************/
+
+/* 0x000 */
+#define agpReqSize (0x000/4) /* */
+#define agpHostAddressLow (0x004/4) /* */
+#define agpHostAddressHigh (0x008/4) /* */
+#define agpGraphicsAddress (0x00c/4) /* */
+#define agpGraphicsStride (0x010/4) /* */
+#define agpMoveCMD (0x014/4) /* */
+#define cmdBaseAddr0 (0x020/4) /* */
+#define cmdBaseSize0 (0x024/4) /* */
+#define cmdBump0 (0x028/4) /* */
+#define cmdRdPtrL0 (0x02c/4) /* */
+#define cmdRdPtrH0 (0x030/4) /* */
+#define cmdAMin0 (0x034/4) /* */
+#define cmdAMax0 (0x03c/4) /* */
+
+/* 0x040 */
+#define cmdFifoDepth0 (0x044/4) /* */
+#define cmdHoleCnt0 (0x048/4) /* */
+#define cmdBaseAddr1 (0x050/4) /* */
+#define cmdBaseSize1 (0x054/4) /* */
+#define cmdBump1 (0x058/4) /* */
+#define cmdRdPtrL1 (0x05c/4) /* */
+#define cmdRdPtrH1 (0x060/4) /* */
+#define cmdAMin1 (0x064/4) /* */
+#define cmdAMax1 (0x06c/4) /* */
+#define cmdFifoDepth1 (0x074/4) /* */
+#define cmdHoleCnt1 (0x078/4) /* */
+
+/* 0x080 */
+#define cmdFifoThresh (0x080/4) /* */
+#define cmdHoleInt (0x084/4) /* */
+
+/* 0x100 */
+#define yuvBaseAddress (0x100/4) /* */
+#define yuvStride (0x104/4) /* */
+#define crc1 (0x120/4) /* */
+#define crc2 (0x130/4) /* */
+
+
+
+/*************************************
+ *
+ * Register string table for debug
+ *
+ *************************************/
+
+static const char *const banshee_agp_reg_name[] =
+{
+ /* 0x000 */
+ "agpReqSize", "agpHostAddressLow","agpHostAddressHigh","agpGraphicsAddress",
+ "agpGraphicsStride","agpMoveCMD","reserved18", "reserved1c",
+ "cmdBaseAddr0", "cmdBaseSize0", "cmdBump0", "cmdRdPtrL0",
+ "cmdRdPtrH0", "cmdAMin0", "reserved38", "cmdAMax0",
+
+ /* 0x040 */
+ "reserved40", "cmdFifoDepth0","cmdHoleCnt0", "reserved4c",
+ "cmdBaseAddr1", "cmdBaseSize1", "cmdBump1", "cmdRdPtrL1",
+ "cmdRdPtrH1", "cmdAMin1", "reserved68", "cmdAMax1",
+ "reserved70", "cmdFifoDepth1","cmdHoleCnt1", "reserved7c",
+
+ /* 0x080 */
+ "cmdFifoThresh","cmdHoleInt", "reserved88", "reserved8c",
+ "reserved90", "reserved94", "reserved98", "reserved9c",
+ "reserveda0", "reserveda4", "reserveda8", "reservedac",
+ "reservedb0", "reservedb4", "reservedb8", "reservedbc",
+
+ /* 0x0c0 */
+ "reservedc0", "reservedc4", "reservedc8", "reservedcc",
+ "reservedd0", "reservedd4", "reservedd8", "reserveddc",
+ "reservede0", "reservede4", "reservede8", "reservedec",
+ "reservedf0", "reservedf4", "reservedf8", "reservedfc",
+
+ /* 0x100 */
+ "yuvBaseAddress","yuvStride", "reserved108", "reserved10c",
+ "reserved110", "reserved114", "reserved118", "reserved11c",
+ "crc1", "reserved124", "reserved128", "reserved12c",
+ "crc2", "reserved134", "reserved138", "reserved13c"
+};
+
+
+
+/*************************************
+ *
+ * Dithering tables
+ *
+ *************************************/
+
+static const UINT8 dither_matrix_4x4[16] =
+{
+ 0, 8, 2, 10,
+ 12, 4, 14, 6,
+ 3, 11, 1, 9,
+ 15, 7, 13, 5
+};
+
+static const UINT8 dither_matrix_2x2[16] =
+{
+ 2, 10, 2, 10,
+ 14, 6, 14, 6,
+ 2, 10, 2, 10,
+ 14, 6, 14, 6
+};
+
+
+
+/*************************************
+ *
+ * Macros for extracting pixels
+ *
+ *************************************/
+
+#define EXTRACT_565_TO_888(val, a, b, c) \
+ (a) = (((val) >> 8) & 0xf8) | (((val) >> 13) & 0x07); \
+ (b) = (((val) >> 3) & 0xfc) | (((val) >> 9) & 0x03); \
+ (c) = (((val) << 3) & 0xf8) | (((val) >> 2) & 0x07);
+#define EXTRACT_x555_TO_888(val, a, b, c) \
+ (a) = (((val) >> 7) & 0xf8) | (((val) >> 12) & 0x07); \
+ (b) = (((val) >> 2) & 0xf8) | (((val) >> 7) & 0x07); \
+ (c) = (((val) << 3) & 0xf8) | (((val) >> 2) & 0x07);
+#define EXTRACT_555x_TO_888(val, a, b, c) \
+ (a) = (((val) >> 8) & 0xf8) | (((val) >> 13) & 0x07); \
+ (b) = (((val) >> 3) & 0xf8) | (((val) >> 8) & 0x07); \
+ (c) = (((val) << 2) & 0xf8) | (((val) >> 3) & 0x07);
+#define EXTRACT_1555_TO_8888(val, a, b, c, d) \
+ (a) = ((INT16)(val) >> 15) & 0xff; \
+ EXTRACT_x555_TO_888(val, b, c, d)
+#define EXTRACT_5551_TO_8888(val, a, b, c, d) \
+ EXTRACT_555x_TO_888(val, a, b, c) \
+ (d) = ((val) & 0x0001) ? 0xff : 0x00;
+#define EXTRACT_x888_TO_888(val, a, b, c) \
+ (a) = ((val) >> 16) & 0xff; \
+ (b) = ((val) >> 8) & 0xff; \
+ (c) = ((val) >> 0) & 0xff;
+#define EXTRACT_888x_TO_888(val, a, b, c) \
+ (a) = ((val) >> 24) & 0xff; \
+ (b) = ((val) >> 16) & 0xff; \
+ (c) = ((val) >> 8) & 0xff;
+#define EXTRACT_8888_TO_8888(val, a, b, c, d) \
+ (a) = ((val) >> 24) & 0xff; \
+ (b) = ((val) >> 16) & 0xff; \
+ (c) = ((val) >> 8) & 0xff; \
+ (d) = ((val) >> 0) & 0xff;
+#define EXTRACT_4444_TO_8888(val, a, b, c, d) \
+ (a) = (((val) >> 8) & 0xf0) | (((val) >> 12) & 0x0f); \
+ (b) = (((val) >> 4) & 0xf0) | (((val) >> 8) & 0x0f); \
+ (c) = (((val) >> 0) & 0xf0) | (((val) >> 4) & 0x0f); \
+ (d) = (((val) << 4) & 0xf0) | (((val) >> 0) & 0x0f);
+#define EXTRACT_332_TO_888(val, a, b, c) \
+ (a) = (((val) >> 0) & 0xe0) | (((val) >> 3) & 0x1c) | (((val) >> 6) & 0x03); \
+ (b) = (((val) << 3) & 0xe0) | (((val) >> 0) & 0x1c) | (((val) >> 3) & 0x03); \
+ (c) = (((val) << 6) & 0xc0) | (((val) << 4) & 0x30) | (((val) << 2) & 0x0c) | (((val) << 0) & 0x03);
+
+
+/*************************************
+ *
+ * Misc. macros
+ *
+ *************************************/
+
+/* macro for clamping a value between minimum and maximum values */
+#define CLAMP(val,min,max) do { if ((val) < (min)) { (val) = (min); } else if ((val) > (max)) { (val) = (max); } } while (0)
+
+/* macro to compute the base 2 log for LOD calculations */
+#define LOGB2(x) (log((double)(x)) / log(2.0))
+
+
+
+/*************************************
+ *
+ * Macros for extracting bitfields
+ *
+ *************************************/
+
+#define INITEN_ENABLE_HW_INIT(val) (((val) >> 0) & 1)
+#define INITEN_ENABLE_PCI_FIFO(val) (((val) >> 1) & 1)
+#define INITEN_REMAP_INIT_TO_DAC(val) (((val) >> 2) & 1)
+#define INITEN_ENABLE_SNOOP0(val) (((val) >> 4) & 1)
+#define INITEN_SNOOP0_MEMORY_MATCH(val) (((val) >> 5) & 1)
+#define INITEN_SNOOP0_READWRITE_MATCH(val) (((val) >> 6) & 1)
+#define INITEN_ENABLE_SNOOP1(val) (((val) >> 7) & 1)
+#define INITEN_SNOOP1_MEMORY_MATCH(val) (((val) >> 8) & 1)
+#define INITEN_SNOOP1_READWRITE_MATCH(val) (((val) >> 9) & 1)
+#define INITEN_SLI_BUS_OWNER(val) (((val) >> 10) & 1)
+#define INITEN_SLI_ODD_EVEN(val) (((val) >> 11) & 1)
+#define INITEN_SECONDARY_REV_ID(val) (((val) >> 12) & 0xf) /* voodoo 2 only */
+#define INITEN_MFCTR_FAB_ID(val) (((val) >> 16) & 0xf) /* voodoo 2 only */
+#define INITEN_ENABLE_PCI_INTERRUPT(val) (((val) >> 20) & 1) /* voodoo 2 only */
+#define INITEN_PCI_INTERRUPT_TIMEOUT(val) (((val) >> 21) & 1) /* voodoo 2 only */
+#define INITEN_ENABLE_NAND_TREE_TEST(val) (((val) >> 22) & 1) /* voodoo 2 only */
+#define INITEN_ENABLE_SLI_ADDRESS_SNOOP(val) (((val) >> 23) & 1) /* voodoo 2 only */
+#define INITEN_SLI_SNOOP_ADDRESS(val) (((val) >> 24) & 0xff) /* voodoo 2 only */
+
+#define FBZCP_CC_RGBSELECT(val) (((val) >> 0) & 3)
+#define FBZCP_CC_ASELECT(val) (((val) >> 2) & 3)
+#define FBZCP_CC_LOCALSELECT(val) (((val) >> 4) & 1)
+#define FBZCP_CCA_LOCALSELECT(val) (((val) >> 5) & 3)
+#define FBZCP_CC_LOCALSELECT_OVERRIDE(val) (((val) >> 7) & 1)
+#define FBZCP_CC_ZERO_OTHER(val) (((val) >> 8) & 1)
+#define FBZCP_CC_SUB_CLOCAL(val) (((val) >> 9) & 1)
+#define FBZCP_CC_MSELECT(val) (((val) >> 10) & 7)
+#define FBZCP_CC_REVERSE_BLEND(val) (((val) >> 13) & 1)
+#define FBZCP_CC_ADD_ACLOCAL(val) (((val) >> 14) & 3)
+#define FBZCP_CC_INVERT_OUTPUT(val) (((val) >> 16) & 1)
+#define FBZCP_CCA_ZERO_OTHER(val) (((val) >> 17) & 1)
+#define FBZCP_CCA_SUB_CLOCAL(val) (((val) >> 18) & 1)
+#define FBZCP_CCA_MSELECT(val) (((val) >> 19) & 7)
+#define FBZCP_CCA_REVERSE_BLEND(val) (((val) >> 22) & 1)
+#define FBZCP_CCA_ADD_ACLOCAL(val) (((val) >> 23) & 3)
+#define FBZCP_CCA_INVERT_OUTPUT(val) (((val) >> 25) & 1)
+#define FBZCP_CCA_SUBPIXEL_ADJUST(val) (((val) >> 26) & 1)
+#define FBZCP_TEXTURE_ENABLE(val) (((val) >> 27) & 1)
+#define FBZCP_RGBZW_CLAMP(val) (((val) >> 28) & 1) /* voodoo 2 only */
+#define FBZCP_ANTI_ALIAS(val) (((val) >> 29) & 1) /* voodoo 2 only */
+
+#define ALPHAMODE_ALPHATEST(val) (((val) >> 0) & 1)
+#define ALPHAMODE_ALPHAFUNCTION(val) (((val) >> 1) & 7)
+#define ALPHAMODE_ALPHABLEND(val) (((val) >> 4) & 1)
+#define ALPHAMODE_ANTIALIAS(val) (((val) >> 5) & 1)
+#define ALPHAMODE_SRCRGBBLEND(val) (((val) >> 8) & 15)
+#define ALPHAMODE_DSTRGBBLEND(val) (((val) >> 12) & 15)
+#define ALPHAMODE_SRCALPHABLEND(val) (((val) >> 16) & 15)
+#define ALPHAMODE_DSTALPHABLEND(val) (((val) >> 20) & 15)
+#define ALPHAMODE_ALPHAREF(val) (((val) >> 24) & 0xff)
+
+#define FOGMODE_ENABLE_FOG(val) (((val) >> 0) & 1)
+#define FOGMODE_FOG_ADD(val) (((val) >> 1) & 1)
+#define FOGMODE_FOG_MULT(val) (((val) >> 2) & 1)
+#define FOGMODE_FOG_ZALPHA(val) (((val) >> 3) & 3)
+#define FOGMODE_FOG_CONSTANT(val) (((val) >> 5) & 1)
+#define FOGMODE_FOG_DITHER(val) (((val) >> 6) & 1) /* voodoo 2 only */
+#define FOGMODE_FOG_ZONES(val) (((val) >> 7) & 1) /* voodoo 2 only */
+
+#define FBZMODE_ENABLE_CLIPPING(val) (((val) >> 0) & 1)
+#define FBZMODE_ENABLE_CHROMAKEY(val) (((val) >> 1) & 1)
+#define FBZMODE_ENABLE_STIPPLE(val) (((val) >> 2) & 1)
+#define FBZMODE_WBUFFER_SELECT(val) (((val) >> 3) & 1)
+#define FBZMODE_ENABLE_DEPTHBUF(val) (((val) >> 4) & 1)
+#define FBZMODE_DEPTH_FUNCTION(val) (((val) >> 5) & 7)
+#define FBZMODE_ENABLE_DITHERING(val) (((val) >> 8) & 1)
+#define FBZMODE_RGB_BUFFER_MASK(val) (((val) >> 9) & 1)
+#define FBZMODE_AUX_BUFFER_MASK(val) (((val) >> 10) & 1)
+#define FBZMODE_DITHER_TYPE(val) (((val) >> 11) & 1)
+#define FBZMODE_STIPPLE_PATTERN(val) (((val) >> 12) & 1)
+#define FBZMODE_ENABLE_ALPHA_MASK(val) (((val) >> 13) & 1)
+#define FBZMODE_DRAW_BUFFER(val) (((val) >> 14) & 3)
+#define FBZMODE_ENABLE_DEPTH_BIAS(val) (((val) >> 16) & 1)
+#define FBZMODE_Y_ORIGIN(val) (((val) >> 17) & 1)
+#define FBZMODE_ENABLE_ALPHA_PLANES(val) (((val) >> 18) & 1)
+#define FBZMODE_ALPHA_DITHER_SUBTRACT(val) (((val) >> 19) & 1)
+#define FBZMODE_DEPTH_SOURCE_COMPARE(val) (((val) >> 20) & 1)
+#define FBZMODE_DEPTH_FLOAT_SELECT(val) (((val) >> 21) & 1) /* voodoo 2 only */
+
+#define LFBMODE_WRITE_FORMAT(val) (((val) >> 0) & 0xf)
+#define LFBMODE_WRITE_BUFFER_SELECT(val) (((val) >> 4) & 3)
+#define LFBMODE_READ_BUFFER_SELECT(val) (((val) >> 6) & 3)
+#define LFBMODE_ENABLE_PIXEL_PIPELINE(val) (((val) >> 8) & 1)
+#define LFBMODE_RGBA_LANES(val) (((val) >> 9) & 3)
+#define LFBMODE_WORD_SWAP_WRITES(val) (((val) >> 11) & 1)
+#define LFBMODE_BYTE_SWIZZLE_WRITES(val) (((val) >> 12) & 1)
+#define LFBMODE_Y_ORIGIN(val) (((val) >> 13) & 1)
+#define LFBMODE_WRITE_W_SELECT(val) (((val) >> 14) & 1)
+#define LFBMODE_WORD_SWAP_READS(val) (((val) >> 15) & 1)
+#define LFBMODE_BYTE_SWIZZLE_READS(val) (((val) >> 16) & 1)
+
+#define CHROMARANGE_BLUE_EXCLUSIVE(val) (((val) >> 24) & 1)
+#define CHROMARANGE_GREEN_EXCLUSIVE(val) (((val) >> 25) & 1)
+#define CHROMARANGE_RED_EXCLUSIVE(val) (((val) >> 26) & 1)
+#define CHROMARANGE_UNION_MODE(val) (((val) >> 27) & 1)
+#define CHROMARANGE_ENABLE(val) (((val) >> 28) & 1)
+
+#define FBIINIT0_VGA_PASSTHRU(val) (((val) >> 0) & 1)
+#define FBIINIT0_GRAPHICS_RESET(val) (((val) >> 1) & 1)
+#define FBIINIT0_FIFO_RESET(val) (((val) >> 2) & 1)
+#define FBIINIT0_SWIZZLE_REG_WRITES(val) (((val) >> 3) & 1)
+#define FBIINIT0_STALL_PCIE_FOR_HWM(val) (((val) >> 4) & 1)
+#define FBIINIT0_PCI_FIFO_LWM(val) (((val) >> 6) & 0x1f)
+#define FBIINIT0_LFB_TO_MEMORY_FIFO(val) (((val) >> 11) & 1)
+#define FBIINIT0_TEXMEM_TO_MEMORY_FIFO(val) (((val) >> 12) & 1)
+#define FBIINIT0_ENABLE_MEMORY_FIFO(val) (((val) >> 13) & 1)
+#define FBIINIT0_MEMORY_FIFO_HWM(val) (((val) >> 14) & 0x7ff)
+#define FBIINIT0_MEMORY_FIFO_BURST(val) (((val) >> 25) & 0x3f)
+
+#define FBIINIT1_PCI_DEV_FUNCTION(val) (((val) >> 0) & 1)
+#define FBIINIT1_PCI_WRITE_WAIT_STATES(val) (((val) >> 1) & 1)
+#define FBIINIT1_MULTI_SST1(val) (((val) >> 2) & 1) /* not on voodoo 2 */
+#define FBIINIT1_ENABLE_LFB(val) (((val) >> 3) & 1)
+#define FBIINIT1_X_VIDEO_TILES(val) (((val) >> 4) & 0xf)
+#define FBIINIT1_VIDEO_TIMING_RESET(val) (((val) >> 8) & 1)
+#define FBIINIT1_SOFTWARE_OVERRIDE(val) (((val) >> 9) & 1)
+#define FBIINIT1_SOFTWARE_HSYNC(val) (((val) >> 10) & 1)
+#define FBIINIT1_SOFTWARE_VSYNC(val) (((val) >> 11) & 1)
+#define FBIINIT1_SOFTWARE_BLANK(val) (((val) >> 12) & 1)
+#define FBIINIT1_DRIVE_VIDEO_TIMING(val) (((val) >> 13) & 1)
+#define FBIINIT1_DRIVE_VIDEO_BLANK(val) (((val) >> 14) & 1)
+#define FBIINIT1_DRIVE_VIDEO_SYNC(val) (((val) >> 15) & 1)
+#define FBIINIT1_DRIVE_VIDEO_DCLK(val) (((val) >> 16) & 1)
+#define FBIINIT1_VIDEO_TIMING_VCLK(val) (((val) >> 17) & 1)
+#define FBIINIT1_VIDEO_CLK_2X_DELAY(val) (((val) >> 18) & 3)
+#define FBIINIT1_VIDEO_TIMING_SOURCE(val) (((val) >> 20) & 3)
+#define FBIINIT1_ENABLE_24BPP_OUTPUT(val) (((val) >> 22) & 1)
+#define FBIINIT1_ENABLE_SLI(val) (((val) >> 23) & 1)
+#define FBIINIT1_X_VIDEO_TILES_BIT5(val) (((val) >> 24) & 1) /* voodoo 2 only */
+#define FBIINIT1_ENABLE_EDGE_FILTER(val) (((val) >> 25) & 1)
+#define FBIINIT1_INVERT_VID_CLK_2X(val) (((val) >> 26) & 1)
+#define FBIINIT1_VID_CLK_2X_SEL_DELAY(val) (((val) >> 27) & 3)
+#define FBIINIT1_VID_CLK_DELAY(val) (((val) >> 29) & 3)
+#define FBIINIT1_DISABLE_FAST_READAHEAD(val) (((val) >> 31) & 1)
+
+#define FBIINIT2_DISABLE_DITHER_SUB(val) (((val) >> 0) & 1)
+#define FBIINIT2_DRAM_BANKING(val) (((val) >> 1) & 1)
+#define FBIINIT2_ENABLE_TRIPLE_BUF(val) (((val) >> 4) & 1)
+#define FBIINIT2_ENABLE_FAST_RAS_READ(val) (((val) >> 5) & 1)
+#define FBIINIT2_ENABLE_GEN_DRAM_OE(val) (((val) >> 6) & 1)
+#define FBIINIT2_ENABLE_FAST_READWRITE(val) (((val) >> 7) & 1)
+#define FBIINIT2_ENABLE_PASSTHRU_DITHER(val) (((val) >> 8) & 1)
+#define FBIINIT2_SWAP_BUFFER_ALGORITHM(val) (((val) >> 9) & 3)
+#define FBIINIT2_VIDEO_BUFFER_OFFSET(val) (((val) >> 11) & 0x1ff)
+#define FBIINIT2_ENABLE_DRAM_BANKING(val) (((val) >> 20) & 1)
+#define FBIINIT2_ENABLE_DRAM_READ_FIFO(val) (((val) >> 21) & 1)
+#define FBIINIT2_ENABLE_DRAM_REFRESH(val) (((val) >> 22) & 1)
+#define FBIINIT2_REFRESH_LOAD_VALUE(val) (((val) >> 23) & 0x1ff)
+
+#define FBIINIT3_TRI_REGISTER_REMAP(val) (((val) >> 0) & 1)
+#define FBIINIT3_VIDEO_FIFO_THRESH(val) (((val) >> 1) & 0x1f)
+#define FBIINIT3_DISABLE_TMUS(val) (((val) >> 6) & 1)
+#define FBIINIT3_FBI_MEMORY_TYPE(val) (((val) >> 8) & 7)
+#define FBIINIT3_VGA_PASS_RESET_VAL(val) (((val) >> 11) & 1)
+#define FBIINIT3_HARDCODE_PCI_BASE(val) (((val) >> 12) & 1)
+#define FBIINIT3_FBI2TREX_DELAY(val) (((val) >> 13) & 0xf)
+#define FBIINIT3_TREX2FBI_DELAY(val) (((val) >> 17) & 0x1f)
+#define FBIINIT3_YORIGIN_SUBTRACT(val) (((val) >> 22) & 0x3ff)
+
+#define FBIINIT4_PCI_READ_WAITS(val) (((val) >> 0) & 1)
+#define FBIINIT4_ENABLE_LFB_READAHEAD(val) (((val) >> 1) & 1)
+#define FBIINIT4_MEMORY_FIFO_LWM(val) (((val) >> 2) & 0x3f)
+#define FBIINIT4_MEMORY_FIFO_START_ROW(val) (((val) >> 8) & 0x3ff)
+#define FBIINIT4_MEMORY_FIFO_STOP_ROW(val) (((val) >> 18) & 0x3ff)
+#define FBIINIT4_VIDEO_CLOCKING_DELAY(val) (((val) >> 29) & 7) /* voodoo 2 only */
+
+#define FBIINIT5_DISABLE_PCI_STOP(val) (((val) >> 0) & 1) /* voodoo 2 only */
+#define FBIINIT5_PCI_SLAVE_SPEED(val) (((val) >> 1) & 1) /* voodoo 2 only */
+#define FBIINIT5_DAC_DATA_OUTPUT_WIDTH(val) (((val) >> 2) & 1) /* voodoo 2 only */
+#define FBIINIT5_DAC_DATA_17_OUTPUT(val) (((val) >> 3) & 1) /* voodoo 2 only */
+#define FBIINIT5_DAC_DATA_18_OUTPUT(val) (((val) >> 4) & 1) /* voodoo 2 only */
+#define FBIINIT5_GENERIC_STRAPPING(val) (((val) >> 5) & 0xf) /* voodoo 2 only */
+#define FBIINIT5_BUFFER_ALLOCATION(val) (((val) >> 9) & 3) /* voodoo 2 only */
+#define FBIINIT5_DRIVE_VID_CLK_SLAVE(val) (((val) >> 11) & 1) /* voodoo 2 only */
+#define FBIINIT5_DRIVE_DAC_DATA_16(val) (((val) >> 12) & 1) /* voodoo 2 only */
+#define FBIINIT5_VCLK_INPUT_SELECT(val) (((val) >> 13) & 1) /* voodoo 2 only */
+#define FBIINIT5_MULTI_CVG_DETECT(val) (((val) >> 14) & 1) /* voodoo 2 only */
+#define FBIINIT5_SYNC_RETRACE_READS(val) (((val) >> 15) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_RHBORDER_COLOR(val) (((val) >> 16) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_LHBORDER_COLOR(val) (((val) >> 17) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_BVBORDER_COLOR(val) (((val) >> 18) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_TVBORDER_COLOR(val) (((val) >> 19) & 1) /* voodoo 2 only */
+#define FBIINIT5_DOUBLE_HORIZ(val) (((val) >> 20) & 1) /* voodoo 2 only */
+#define FBIINIT5_DOUBLE_VERT(val) (((val) >> 21) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_16BIT_GAMMA(val) (((val) >> 22) & 1) /* voodoo 2 only */
+#define FBIINIT5_INVERT_DAC_HSYNC(val) (((val) >> 23) & 1) /* voodoo 2 only */
+#define FBIINIT5_INVERT_DAC_VSYNC(val) (((val) >> 24) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_24BIT_DACDATA(val) (((val) >> 25) & 1) /* voodoo 2 only */
+#define FBIINIT5_ENABLE_INTERLACING(val) (((val) >> 26) & 1) /* voodoo 2 only */
+#define FBIINIT5_DAC_DATA_18_CONTROL(val) (((val) >> 27) & 1) /* voodoo 2 only */
+#define FBIINIT5_RASTERIZER_UNIT_MODE(val) (((val) >> 30) & 3) /* voodoo 2 only */
+
+#define FBIINIT6_WINDOW_ACTIVE_COUNTER(val) (((val) >> 0) & 7) /* voodoo 2 only */
+#define FBIINIT6_WINDOW_DRAG_COUNTER(val) (((val) >> 3) & 0x1f) /* voodoo 2 only */
+#define FBIINIT6_SLI_SYNC_MASTER(val) (((val) >> 8) & 1) /* voodoo 2 only */
+#define FBIINIT6_DAC_DATA_22_OUTPUT(val) (((val) >> 9) & 3) /* voodoo 2 only */
+#define FBIINIT6_DAC_DATA_23_OUTPUT(val) (((val) >> 11) & 3) /* voodoo 2 only */
+#define FBIINIT6_SLI_SYNCIN_OUTPUT(val) (((val) >> 13) & 3) /* voodoo 2 only */
+#define FBIINIT6_SLI_SYNCOUT_OUTPUT(val) (((val) >> 15) & 3) /* voodoo 2 only */
+#define FBIINIT6_DAC_RD_OUTPUT(val) (((val) >> 17) & 3) /* voodoo 2 only */
+#define FBIINIT6_DAC_WR_OUTPUT(val) (((val) >> 19) & 3) /* voodoo 2 only */
+#define FBIINIT6_PCI_FIFO_LWM_RDY(val) (((val) >> 21) & 0x7f) /* voodoo 2 only */
+#define FBIINIT6_VGA_PASS_N_OUTPUT(val) (((val) >> 28) & 3) /* voodoo 2 only */
+#define FBIINIT6_X_VIDEO_TILES_BIT0(val) (((val) >> 30) & 1) /* voodoo 2 only */
+
+#define FBIINIT7_GENERIC_STRAPPING(val) (((val) >> 0) & 0xff) /* voodoo 2 only */
+#define FBIINIT7_CMDFIFO_ENABLE(val) (((val) >> 8) & 1) /* voodoo 2 only */
+#define FBIINIT7_CMDFIFO_MEMORY_STORE(val) (((val) >> 9) & 1) /* voodoo 2 only */
+#define FBIINIT7_DISABLE_CMDFIFO_HOLES(val) (((val) >> 10) & 1) /* voodoo 2 only */
+#define FBIINIT7_CMDFIFO_READ_THRESH(val) (((val) >> 11) & 0x1f) /* voodoo 2 only */
+#define FBIINIT7_SYNC_CMDFIFO_WRITES(val) (((val) >> 16) & 1) /* voodoo 2 only */
+#define FBIINIT7_SYNC_CMDFIFO_READS(val) (((val) >> 17) & 1) /* voodoo 2 only */
+#define FBIINIT7_RESET_PCI_PACKER(val) (((val) >> 18) & 1) /* voodoo 2 only */
+#define FBIINIT7_ENABLE_CHROMA_STUFF(val) (((val) >> 19) & 1) /* voodoo 2 only */
+#define FBIINIT7_CMDFIFO_PCI_TIMEOUT(val) (((val) >> 20) & 0x7f) /* voodoo 2 only */
+#define FBIINIT7_ENABLE_TEXTURE_BURST(val) (((val) >> 27) & 1) /* voodoo 2 only */
+
+#define TEXMODE_ENABLE_PERSPECTIVE(val) (((val) >> 0) & 1)
+#define TEXMODE_MINIFICATION_FILTER(val) (((val) >> 1) & 1)
+#define TEXMODE_MAGNIFICATION_FILTER(val) (((val) >> 2) & 1)
+#define TEXMODE_CLAMP_NEG_W(val) (((val) >> 3) & 1)
+#define TEXMODE_ENABLE_LOD_DITHER(val) (((val) >> 4) & 1)
+#define TEXMODE_NCC_TABLE_SELECT(val) (((val) >> 5) & 1)
+#define TEXMODE_CLAMP_S(val) (((val) >> 6) & 1)
+#define TEXMODE_CLAMP_T(val) (((val) >> 7) & 1)
+#define TEXMODE_FORMAT(val) (((val) >> 8) & 0xf)
+#define TEXMODE_TC_ZERO_OTHER(val) (((val) >> 12) & 1)
+#define TEXMODE_TC_SUB_CLOCAL(val) (((val) >> 13) & 1)
+#define TEXMODE_TC_MSELECT(val) (((val) >> 14) & 7)
+#define TEXMODE_TC_REVERSE_BLEND(val) (((val) >> 17) & 1)
+#define TEXMODE_TC_ADD_ACLOCAL(val) (((val) >> 18) & 3)
+#define TEXMODE_TC_INVERT_OUTPUT(val) (((val) >> 20) & 1)
+#define TEXMODE_TCA_ZERO_OTHER(val) (((val) >> 21) & 1)
+#define TEXMODE_TCA_SUB_CLOCAL(val) (((val) >> 22) & 1)
+#define TEXMODE_TCA_MSELECT(val) (((val) >> 23) & 7)
+#define TEXMODE_TCA_REVERSE_BLEND(val) (((val) >> 26) & 1)
+#define TEXMODE_TCA_ADD_ACLOCAL(val) (((val) >> 27) & 3)
+#define TEXMODE_TCA_INVERT_OUTPUT(val) (((val) >> 29) & 1)
+#define TEXMODE_TRILINEAR(val) (((val) >> 30) & 1)
+#define TEXMODE_SEQ_8_DOWNLD(val) (((val) >> 31) & 1)
+
+#define TEXLOD_LODMIN(val) (((val) >> 0) & 0x3f)
+#define TEXLOD_LODMAX(val) (((val) >> 6) & 0x3f)
+#define TEXLOD_LODBIAS(val) (((val) >> 12) & 0x3f)
+#define TEXLOD_LOD_ODD(val) (((val) >> 18) & 1)
+#define TEXLOD_LOD_TSPLIT(val) (((val) >> 19) & 1)
+#define TEXLOD_LOD_S_IS_WIDER(val) (((val) >> 20) & 1)
+#define TEXLOD_LOD_ASPECT(val) (((val) >> 21) & 3)
+#define TEXLOD_LOD_ZEROFRAC(val) (((val) >> 23) & 1)
+#define TEXLOD_TMULTIBASEADDR(val) (((val) >> 24) & 1)
+#define TEXLOD_TDATA_SWIZZLE(val) (((val) >> 25) & 1)
+#define TEXLOD_TDATA_SWAP(val) (((val) >> 26) & 1)
+#define TEXLOD_TDIRECT_WRITE(val) (((val) >> 27) & 1) /* Voodoo 2 only */
+
+#define TEXDETAIL_DETAIL_MAX(val) (((val) >> 0) & 0xff)
+#define TEXDETAIL_DETAIL_BIAS(val) (((val) >> 8) & 0x3f)
+#define TEXDETAIL_DETAIL_SCALE(val) (((val) >> 14) & 7)
+#define TEXDETAIL_RGB_MIN_FILTER(val) (((val) >> 17) & 1) /* Voodoo 2 only */
+#define TEXDETAIL_RGB_MAG_FILTER(val) (((val) >> 18) & 1) /* Voodoo 2 only */
+#define TEXDETAIL_ALPHA_MIN_FILTER(val) (((val) >> 19) & 1) /* Voodoo 2 only */
+#define TEXDETAIL_ALPHA_MAG_FILTER(val) (((val) >> 20) & 1) /* Voodoo 2 only */
+#define TEXDETAIL_SEPARATE_RGBA_FILTER(val) (((val) >> 21) & 1) /* Voodoo 2 only */
+
+#define TREXINIT_SEND_TMU_CONFIG(val) (((val) >> 18) & 1)
+
+
+/*************************************
+ *
+ * Core types
+ *
+ *************************************/
+
+struct voodoo_state;
+struct poly_extra_data;
+
+
+struct rgba
+{
+#ifdef LSB_FIRST
+ UINT8 b, g, r, a;
+#else
+ UINT8 a, r, g, b;
+#endif
+};
+
+
+union voodoo_reg
+{
+ INT32 i;
+ UINT32 u;
+ float f;
+ rgba rgb;
+};
+
+
+typedef voodoo_reg rgb_union;
+
+
+struct voodoo_stats
+{
+ UINT8 lastkey; /* last key state */
+ UINT8 display; /* display stats? */
+ INT32 swaps; /* total swaps */
+ INT32 stalls; /* total stalls */
+ INT32 total_triangles; /* total triangles */
+ INT32 total_pixels_in; /* total pixels in */
+ INT32 total_pixels_out; /* total pixels out */
+ INT32 total_chroma_fail; /* total chroma fail */
+ INT32 total_zfunc_fail; /* total z func fail */
+ INT32 total_afunc_fail; /* total a func fail */
+ INT32 total_clipped; /* total clipped */
+ INT32 total_stippled; /* total stippled */
+ INT32 lfb_writes; /* LFB writes */
+ INT32 lfb_reads; /* LFB reads */
+ INT32 reg_writes; /* register writes */
+ INT32 reg_reads; /* register reads */
+ INT32 tex_writes; /* texture writes */
+ INT32 texture_mode[16]; /* 16 different texture modes */
+ UINT8 render_override; /* render override */
+ char buffer[1024]; /* string */
+};
+
+
+/* note that this structure is an even 64 bytes long */
+struct stats_block
+{
+ INT32 pixels_in; /* pixels in statistic */
+ INT32 pixels_out; /* pixels out statistic */
+ INT32 chroma_fail; /* chroma test fail statistic */
+ INT32 zfunc_fail; /* z function test fail statistic */
+ INT32 afunc_fail; /* alpha function test fail statistic */
+ INT32 clip_fail; /* clipping fail statistic */
+ INT32 stipple_count; /* stipple statistic */
+ INT32 filler[64/4 - 7]; /* pad this structure to 64 bytes */
+};
+
+
+struct fifo_state
+{
+ UINT32 * base; /* base of the FIFO */
+ INT32 size; /* size of the FIFO */
+ INT32 in; /* input pointer */
+ INT32 out; /* output pointer */
+};
+
+
+struct cmdfifo_info
+{
+ UINT8 enable; /* enabled? */
+ UINT8 count_holes; /* count holes? */
+ UINT32 base; /* base address in framebuffer RAM */
+ UINT32 end; /* end address in framebuffer RAM */
+ UINT32 rdptr; /* current read pointer */
+ UINT32 amin; /* minimum address */
+ UINT32 amax; /* maximum address */
+ UINT32 depth; /* current depth */
+ UINT32 holes; /* number of holes */
+};
+
+
+struct pci_state
+{
+ fifo_state fifo; /* PCI FIFO */
+ UINT32 init_enable; /* initEnable value */
+ UINT8 stall_state; /* state of the system if we're stalled */
+ UINT8 op_pending; /* true if an operation is pending */
+ attotime op_end_time; /* time when the pending operation ends */
+ emu_timer * continue_timer; /* timer to use to continue processing */
+ UINT32 fifo_mem[64*2]; /* memory backing the PCI FIFO */
+};
+
+
+struct ncc_table
+{
+ UINT8 dirty; /* is the texel lookup dirty? */
+ voodoo_reg * reg; /* pointer to our registers */
+ INT32 ir[4], ig[4], ib[4]; /* I values for R,G,B */
+ INT32 qr[4], qg[4], qb[4]; /* Q values for R,G,B */
+ INT32 y[16]; /* Y values */
+ rgb_t * palette; /* pointer to associated RGB palette */
+ rgb_t * palettea; /* pointer to associated ARGB palette */
+ rgb_t texel[256]; /* texel lookup */
+};
+
+
+struct tmu_state
+{
+ UINT8 * ram; /* pointer to our RAM */
+ UINT32 mask; /* mask to apply to pointers */
+ voodoo_reg * reg; /* pointer to our register base */
+ UINT32 regdirty; /* true if the LOD/mode/base registers have changed */
+
+ UINT32 texaddr_mask; /* mask for texture address */
+ UINT8 texaddr_shift; /* shift for texture address */
+
+ INT64 starts, startt; /* starting S,T (14.18) */
+ INT64 startw; /* starting W (2.30) */
+ INT64 dsdx, dtdx; /* delta S,T per X */
+ INT64 dwdx; /* delta W per X */
+ INT64 dsdy, dtdy; /* delta S,T per Y */
+ INT64 dwdy; /* delta W per Y */
+
+ INT32 lodmin, lodmax; /* min, max LOD values */
+ INT32 lodbias; /* LOD bias */
+ UINT32 lodmask; /* mask of available LODs */
+ UINT32 lodoffset[9]; /* offset of texture base for each LOD */
+ INT32 detailmax; /* detail clamp */
+ INT32 detailbias; /* detail bias */
+ UINT8 detailscale; /* detail scale */
+
+ UINT32 wmask; /* mask for the current texture width */
+ UINT32 hmask; /* mask for the current texture height */
+
+ UINT32 bilinear_mask; /* mask for bilinear resolution (0xf0 for V1, 0xff for V2) */
+
+ ncc_table ncc[2]; /* two NCC tables */
+
+ rgb_t * lookup; /* currently selected lookup */
+ rgb_t * texel[16]; /* texel lookups for each format */
+
+ rgb_t palette[256]; /* palette lookup table */
+ rgb_t palettea[256]; /* palette+alpha lookup table */
+};
+
+
+struct tmu_shared_state
+{
+ rgb_t rgb332[256]; /* RGB 3-3-2 lookup table */
+ rgb_t alpha8[256]; /* alpha 8-bit lookup table */
+ rgb_t int8[256]; /* intensity 8-bit lookup table */
+ rgb_t ai44[256]; /* alpha, intensity 4-4 lookup table */
+
+ rgb_t rgb565[65536]; /* RGB 5-6-5 lookup table */
+ rgb_t argb1555[65536]; /* ARGB 1-5-5-5 lookup table */
+ rgb_t argb4444[65536]; /* ARGB 4-4-4-4 lookup table */
+};
+
+
+struct setup_vertex
+{
+ float x, y; /* X, Y coordinates */
+ float a, r, g, b; /* A, R, G, B values */
+ float z, wb; /* Z and broadcast W values */
+ float w0, s0, t0; /* W, S, T for TMU 0 */
+ float w1, s1, t1; /* W, S, T for TMU 1 */
+};
+
+
+struct fbi_state
+{
+ UINT8 * ram; /* pointer to frame buffer RAM */
+ UINT32 mask; /* mask to apply to pointers */
+ UINT32 rgboffs[3]; /* word offset to 3 RGB buffers */
+ UINT32 auxoffs; /* word offset to 1 aux buffer */
+
+ UINT8 frontbuf; /* front buffer index */
+ UINT8 backbuf; /* back buffer index */
+ UINT8 swaps_pending; /* number of pending swaps */
+ UINT8 video_changed; /* did the frontbuffer video change? */
+
+ UINT32 yorigin; /* Y origin subtract value */
+ UINT32 lfb_base; /* base of LFB in memory */
+ UINT8 lfb_stride; /* stride of LFB accesses in bits */
+
+ UINT32 width; /* width of current frame buffer */
+ UINT32 height; /* height of current frame buffer */
+ UINT32 xoffs; /* horizontal offset (back porch) */
+ UINT32 yoffs; /* vertical offset (back porch) */
+ UINT32 vsyncscan; /* vertical sync scanline */
+ UINT32 rowpixels; /* pixels per row */
+ UINT32 tile_width; /* width of video tiles */
+ UINT32 tile_height; /* height of video tiles */
+ UINT32 x_tiles; /* number of tiles in the X direction */
+
+ emu_timer * vblank_timer; /* VBLANK timer */
+ UINT8 vblank; /* VBLANK state */
+ UINT8 vblank_count; /* number of VBLANKs since last swap */
+ UINT8 vblank_swap_pending; /* a swap is pending, waiting for a vblank */
+ UINT8 vblank_swap; /* swap when we hit this count */
+ UINT8 vblank_dont_swap; /* don't actually swap when we hit this point */
+
+ /* triangle setup info */
+ UINT8 cheating_allowed; /* allow cheating? */
+ INT32 sign; /* triangle sign */
+ INT16 ax, ay; /* vertex A x,y (12.4) */
+ INT16 bx, by; /* vertex B x,y (12.4) */
+ INT16 cx, cy; /* vertex C x,y (12.4) */
+ INT32 startr, startg, startb, starta; /* starting R,G,B,A (12.12) */
+ INT32 startz; /* starting Z (20.12) */
+ INT64 startw; /* starting W (16.32) */
+ INT32 drdx, dgdx, dbdx, dadx; /* delta R,G,B,A per X */
+ INT32 dzdx; /* delta Z per X */
+ INT64 dwdx; /* delta W per X */
+ INT32 drdy, dgdy, dbdy, dady; /* delta R,G,B,A per Y */
+ INT32 dzdy; /* delta Z per Y */
+ INT64 dwdy; /* delta W per Y */
+
+ stats_block lfb_stats; /* LFB-access statistics */
+
+ UINT8 sverts; /* number of vertices ready */
+ setup_vertex svert[3]; /* 3 setup vertices */
+
+ fifo_state fifo; /* framebuffer memory fifo */
+ cmdfifo_info cmdfifo[2]; /* command FIFOs */
+
+ UINT8 fogblend[64]; /* 64-entry fog table */
+ UINT8 fogdelta[64]; /* 64-entry fog table */
+ UINT8 fogdelta_mask; /* mask for for delta (0xff for V1, 0xfc for V2) */
+
+ rgb_t pen[65536]; /* mapping from pixels to pens */
+ rgb_t clut[512]; /* clut gamma data */
+ UINT8 clut_dirty; /* do we need to recompute? */
+};
+
+
+struct dac_state
+{
+ UINT8 reg[8]; /* 8 registers */
+ UINT8 read_result; /* pending read result */
+};
+
+
+struct raster_info
+{
+ raster_info * next; /* pointer to next entry with the same hash */
+ poly_draw_scanline_func callback; /* callback pointer */
+ UINT8 is_generic; /* TRUE if this is one of the generic rasterizers */
+ UINT8 display; /* display index */
+ UINT32 hits; /* how many hits (pixels) we've used this for */
+ UINT32 polys; /* how many polys we've used this for */
+ UINT32 eff_color_path; /* effective fbzColorPath value */
+ UINT32 eff_alpha_mode; /* effective alphaMode value */
+ UINT32 eff_fog_mode; /* effective fogMode value */
+ UINT32 eff_fbz_mode; /* effective fbzMode value */
+ UINT32 eff_tex_mode_0; /* effective textureMode value for TMU #0 */
+ UINT32 eff_tex_mode_1; /* effective textureMode value for TMU #1 */
+ UINT32 hash;
+};
+
+
+struct poly_extra_data
+{
+ voodoo_state * state; /* pointer back to the voodoo state */
+ raster_info * info; /* pointer to rasterizer information */
+
+ INT16 ax, ay; /* vertex A x,y (12.4) */
+ INT32 startr, startg, startb, starta; /* starting R,G,B,A (12.12) */
+ INT32 startz; /* starting Z (20.12) */
+ INT64 startw; /* starting W (16.32) */
+ INT32 drdx, dgdx, dbdx, dadx; /* delta R,G,B,A per X */
+ INT32 dzdx; /* delta Z per X */
+ INT64 dwdx; /* delta W per X */
+ INT32 drdy, dgdy, dbdy, dady; /* delta R,G,B,A per Y */
+ INT32 dzdy; /* delta Z per Y */
+ INT64 dwdy; /* delta W per Y */
+
+ INT64 starts0, startt0; /* starting S,T (14.18) */
+ INT64 startw0; /* starting W (2.30) */
+ INT64 ds0dx, dt0dx; /* delta S,T per X */
+ INT64 dw0dx; /* delta W per X */
+ INT64 ds0dy, dt0dy; /* delta S,T per Y */
+ INT64 dw0dy; /* delta W per Y */
+ INT32 lodbase0; /* used during rasterization */
+
+ INT64 starts1, startt1; /* starting S,T (14.18) */
+ INT64 startw1; /* starting W (2.30) */
+ INT64 ds1dx, dt1dx; /* delta S,T per X */
+ INT64 dw1dx; /* delta W per X */
+ INT64 ds1dy, dt1dy; /* delta S,T per Y */
+ INT64 dw1dy; /* delta W per Y */
+ INT32 lodbase1; /* used during rasterization */
+
+ UINT16 dither[16]; /* dither matrix, for fastfill */
+};
+
+
+struct banshee_info
+{
+ UINT32 io[0x40]; /* I/O registers */
+ UINT32 agp[0x80]; /* AGP registers */
+ UINT8 vga[0x20]; /* VGA registers */
+ UINT8 crtc[0x27]; /* VGA CRTC registers */
+ UINT8 seq[0x05]; /* VGA sequencer registers */
+ UINT8 gc[0x05]; /* VGA graphics controller registers */
+ UINT8 att[0x15]; /* VGA attribute registers */
+ UINT8 attff; /* VGA attribute flip-flop */
+
+ UINT32 blt_regs[0x20]; /* 2D Blitter registers */
+ UINT32 blt_dst_base;
+ UINT32 blt_dst_x;
+ UINT32 blt_dst_y;
+ UINT32 blt_dst_width;
+ UINT32 blt_dst_height;
+ UINT32 blt_dst_stride;
+ UINT32 blt_dst_bpp;
+ UINT32 blt_cmd;
+ UINT32 blt_src_base;
+ UINT32 blt_src_x;
+ UINT32 blt_src_y;
+ UINT32 blt_src_width;
+ UINT32 blt_src_height;
+ UINT32 blt_src_stride;
+ UINT32 blt_src_bpp;
+};
+
+
+struct voodoo_state
+{
+ UINT8 index; /* index of board */
+ voodoo_device *device; /* pointer to our containing device */
+ screen_device *screen; /* the screen we are acting on */
+ device_t *cpu; /* the CPU we interact with */
+ UINT8 type; /* type of system */
+ UINT8 chipmask; /* mask for which chips are available */
+ UINT32 freq; /* operating frequency */
+ attoseconds_t attoseconds_per_cycle; /* attoseconds per cycle */
+ UINT32 extra_cycles; /* extra cycles not yet accounted for */
+ int trigger; /* trigger used for stalling */
+
+ voodoo_reg reg[0x400]; /* raw registers */
+ const UINT8 * regaccess; /* register access array */
+ const char *const * regnames; /* register names array */
+ UINT8 alt_regmap; /* enable alternate register map? */
+
+ pci_state pci; /* PCI state */
+ dac_state dac; /* DAC state */
+
+ fbi_state fbi; /* FBI states */
+ tmu_state tmu[MAX_TMU]; /* TMU states */
+ tmu_shared_state tmushare; /* TMU shared state */
+ banshee_info banshee; /* Banshee state */
+
+ legacy_poly_manager * poly; /* polygon manager */
+ stats_block * thread_stats; /* per-thread statistics */
+
+ voodoo_stats stats; /* internal statistics */
+
+ offs_t last_status_pc; /* PC of last status description (for logging) */
+ UINT32 last_status_value; /* value of last status read (for logging) */
+
+ int next_rasterizer; /* next rasterizer index */
+ raster_info rasterizer[MAX_RASTERIZERS]; /* array of rasterizers */
+ raster_info * raster_hash[RASTER_HASH_SIZE]; /* hash table of rasterizers */
+
+ bool send_config;
+ UINT32 tmu_config;
+};
+
+
+
+/*************************************
+ *
+ * Inline FIFO management
+ *
+ *************************************/
+
+INLINE void fifo_reset(fifo_state *f)
+{
+ f->in = f->out = 0;
+}
+
+
+INLINE void fifo_add(fifo_state *f, UINT32 data)
+{
+ INT32 next_in;
+
+ /* compute the value of 'in' after we add this item */
+ next_in = f->in + 1;
+ if (next_in >= f->size)
+ next_in = 0;
+
+ /* as long as it's not equal to the output pointer, we can do it */
+ if (next_in != f->out)
+ {
+ f->base[f->in] = data;
+ f->in = next_in;
+ }
+}
+
+
+INLINE UINT32 fifo_remove(fifo_state *f)
+{
+ UINT32 data = 0xffffffff;
+
+ /* as long as we have data, we can do it */
+ if (f->out != f->in)
+ {
+ INT32 next_out;
+
+ /* fetch the data */
+ data = f->base[f->out];
+
+ /* advance the output pointer */
+ next_out = f->out + 1;
+ if (next_out >= f->size)
+ next_out = 0;
+ f->out = next_out;
+ }
+ return data;
+}
+
+
+INLINE UINT32 fifo_peek(fifo_state *f)
+{
+ return f->base[f->out];
+}
+
+
+INLINE int fifo_empty(fifo_state *f)
+{
+ return (f->in == f->out);
+}
+
+
+INLINE int fifo_full(fifo_state *f)
+{
+ return (f->in + 1 == f->out || (f->in == f->size - 1 && f->out == 0));
+}
+
+
+INLINE INT32 fifo_items(fifo_state *f)
+{
+ INT32 items = f->in - f->out;
+ if (items < 0)
+ items += f->size;
+ return items;
+}
+
+
+INLINE INT32 fifo_space(fifo_state *f)
+{
+ INT32 items = f->in - f->out;
+ if (items < 0)
+ items += f->size;
+ return f->size - 1 - items;
+}
+
+
+
+/*************************************
+ *
+ * Computes a fast 16.16 reciprocal
+ * of a 16.32 value; used for
+ * computing 1/w in the rasterizer.
+ *
+ * Since it is trivial to also
+ * compute log2(1/w) = -log2(w) at
+ * the same time, we do that as well
+ * to 16.8 precision for LOD
+ * calculations.
+ *
+ * On a Pentium M, this routine is
+ * 20% faster than a 64-bit integer
+ * divide and also produces the log
+ * for free.
+ *
+ *************************************/
+
+INLINE INT32 fast_reciplog(INT64 value, INT32 *log2)
+{
+ extern UINT32 voodoo_reciplog[];
+ UINT32 temp, recip, rlog;
+ UINT32 interp;
+ UINT32 *table;
+ int neg = FALSE;
+ int lz, exp = 0;
+
+ /* always work with unsigned numbers */
+ if (value < 0)
+ {
+ value = -value;
+ neg = TRUE;
+ }
+
+ /* if we've spilled out of 32 bits, push it down under 32 */
+ if (value & U64(0xffff00000000))
+ {
+ temp = (UINT32)(value >> 16);
+ exp -= 16;
+ }
+ else
+ temp = (UINT32)value;
+
+ /* if the resulting value is 0, the reciprocal is infinite */
+ if (UNEXPECTED(temp == 0))
+ {
+ *log2 = 1000 << LOG_OUTPUT_PREC;
+ return neg ? 0x80000000 : 0x7fffffff;
+ }
+
+ /* determine how many leading zeros in the value and shift it up high */
+ lz = count_leading_zeros(temp);
+ temp <<= lz;
+ exp += lz;
+
+ /* compute a pointer to the table entries we want */
+ /* math is a bit funny here because we shift one less than we need to in order */
+ /* to account for the fact that there are two UINT32's per table entry */
+ table = &voodoo_reciplog[(temp >> (31 - RECIPLOG_LOOKUP_BITS - 1)) & ((2 << RECIPLOG_LOOKUP_BITS) - 2)];
+
+ /* compute the interpolation value */
+ interp = (temp >> (31 - RECIPLOG_LOOKUP_BITS - 8)) & 0xff;
+
+ /* do a linear interpolatation between the two nearest table values */
+ /* for both the log and the reciprocal */
+ rlog = (table[1] * (0x100 - interp) + table[3] * interp) >> 8;
+ recip = (table[0] * (0x100 - interp) + table[2] * interp) >> 8;
+
+ /* the log result is the fractional part of the log; round it to the output precision */
+ rlog = (rlog + (1 << (RECIPLOG_LOOKUP_PREC - LOG_OUTPUT_PREC - 1))) >> (RECIPLOG_LOOKUP_PREC - LOG_OUTPUT_PREC);
+
+ /* the exponent is the non-fractional part of the log; normally, we would subtract it from rlog */
+ /* but since we want the log(1/value) = -log(value), we subtract rlog from the exponent */
+ *log2 = ((exp - (31 - RECIPLOG_INPUT_PREC)) << LOG_OUTPUT_PREC) - rlog;
+
+ /* adjust the exponent to account for all the reciprocal-related parameters to arrive at a final shift amount */
+ exp += (RECIP_OUTPUT_PREC - RECIPLOG_LOOKUP_PREC) - (31 - RECIPLOG_INPUT_PREC);
+
+ /* shift by the exponent */
+ if (exp < 0)
+ recip >>= -exp;
+ else
+ recip <<= exp;
+
+ /* on the way out, apply the original sign to the reciprocal */
+ return neg ? -recip : recip;
+}
+
+
+
+/*************************************
+ *
+ * Float-to-int conversions
+ *
+ *************************************/
+
+INLINE INT32 float_to_int32(UINT32 data, int fixedbits)
+{
+ int exponent = ((data >> 23) & 0xff) - 127 - 23 + fixedbits;
+ INT32 result = (data & 0x7fffff) | 0x800000;
+ if (exponent < 0)
+ {
+ if (exponent > -32)
+ result >>= -exponent;
+ else
+ result = 0;
+ }
+ else
+ {
+ if (exponent < 32)
+ result <<= exponent;
+ else
+ result = 0x7fffffff;
+ }
+ if (data & 0x80000000)
+ result = -result;
+ return result;
+}
+
+
+INLINE INT64 float_to_int64(UINT32 data, int fixedbits)
+{
+ int exponent = ((data >> 23) & 0xff) - 127 - 23 + fixedbits;
+ INT64 result = (data & 0x7fffff) | 0x800000;
+ if (exponent < 0)
+ {
+ if (exponent > -64)
+ result >>= -exponent;
+ else
+ result = 0;
+ }
+ else
+ {
+ if (exponent < 64)
+ result <<= exponent;
+ else
+ result = U64(0x7fffffffffffffff);
+ }
+ if (data & 0x80000000)
+ result = -result;
+ return result;
+}
+
+
+
+/*************************************
+ *
+ * Rasterizer inlines
+ *
+ *************************************/
+
+INLINE UINT32 normalize_color_path(UINT32 eff_color_path)
+{
+ /* ignore the subpixel adjust and texture enable flags */
+ eff_color_path &= ~((1 << 26) | (1 << 27));
+
+ return eff_color_path;
+}
+
+
+INLINE UINT32 normalize_alpha_mode(UINT32 eff_alpha_mode)
+{
+ /* always ignore alpha ref value */
+ eff_alpha_mode &= ~(0xff << 24);
+
+ /* if not doing alpha testing, ignore the alpha function and ref value */
+ if (!ALPHAMODE_ALPHATEST(eff_alpha_mode))
+ eff_alpha_mode &= ~(7 << 1);
+
+ /* if not doing alpha blending, ignore the source and dest blending factors */
+ if (!ALPHAMODE_ALPHABLEND(eff_alpha_mode))
+ eff_alpha_mode &= ~((15 << 8) | (15 << 12) | (15 << 16) | (15 << 20));
+
+ return eff_alpha_mode;
+}
+
+
+INLINE UINT32 normalize_fog_mode(UINT32 eff_fog_mode)
+{
+ /* if not doing fogging, ignore all the other fog bits */
+ if (!FOGMODE_ENABLE_FOG(eff_fog_mode))
+ eff_fog_mode = 0;
+
+ return eff_fog_mode;
+}
+
+
+INLINE UINT32 normalize_fbz_mode(UINT32 eff_fbz_mode)
+{
+ /* ignore the draw buffer */
+ eff_fbz_mode &= ~(3 << 14);
+
+ return eff_fbz_mode;
+}
+
+
+INLINE UINT32 normalize_tex_mode(UINT32 eff_tex_mode)
+{
+ /* ignore the NCC table and seq_8_downld flags */
+ eff_tex_mode &= ~((1 << 5) | (1 << 31));
+
+ /* classify texture formats into 3 format categories */
+ if (TEXMODE_FORMAT(eff_tex_mode) < 8)
+ eff_tex_mode = (eff_tex_mode & ~(0xf << 8)) | (0 << 8);
+ else if (TEXMODE_FORMAT(eff_tex_mode) >= 10 && TEXMODE_FORMAT(eff_tex_mode) <= 12)
+ eff_tex_mode = (eff_tex_mode & ~(0xf << 8)) | (10 << 8);
+ else
+ eff_tex_mode = (eff_tex_mode & ~(0xf << 8)) | (8 << 8);
+
+ return eff_tex_mode;
+}
+
+
+INLINE UINT32 compute_raster_hash(const raster_info *info)
+{
+ UINT32 hash;
+
+ /* make a hash */
+ hash = info->eff_color_path;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_fbz_mode;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_alpha_mode;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_fog_mode;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_tex_mode_0;
+ hash = (hash << 1) | (hash >> 31);
+ hash ^= info->eff_tex_mode_1;
+
+ return hash % RASTER_HASH_SIZE;
+}
+
+
+
+/*************************************
+ *
+ * Dithering macros
+ *
+ *************************************/
+
+/* note that these equations and the dither matrixes have
+ been confirmed to be exact matches to the real hardware */
+#define DITHER_RB(val,dith) ((((val) << 1) - ((val) >> 4) + ((val) >> 7) + (dith)) >> 1)
+#define DITHER_G(val,dith) ((((val) << 2) - ((val) >> 4) + ((val) >> 6) + (dith)) >> 2)
+
+#define DECLARE_DITHER_POINTERS \
+ const UINT8 *dither_lookup = NULL; \
+ const UINT8 *dither4 = NULL; \
+ const UINT8 *dither = NULL
+#define DECLARE_DITHER_POINTERS_NO_DITHER_VAR \
+ const UINT8 *dither_lookup = NULL;
+#define COMPUTE_DITHER_POINTERS(FBZMODE, YY) \
+do \
+{ \
+ /* compute the dithering pointers */ \
+ if (FBZMODE_ENABLE_DITHERING(FBZMODE)) \
+ { \
+ dither4 = &dither_matrix_4x4[((YY) & 3) * 4]; \
+ if (FBZMODE_DITHER_TYPE(FBZMODE) == 0) \
+ { \
+ dither = dither4; \
+ dither_lookup = &dither4_lookup[(YY & 3) << 11]; \
+ } \
+ else \
+ { \
+ dither = &dither_matrix_2x2[((YY) & 3) * 4]; \
+ dither_lookup = &dither2_lookup[(YY & 3) << 11]; \
+ } \
+ } \
+} \
+while (0)
+
+#define COMPUTE_DITHER_POINTERS_NO_DITHER_VAR(FBZMODE, YY) \
+do \
+{ \
+ /* compute the dithering pointers */ \
+ if (FBZMODE_ENABLE_DITHERING(FBZMODE)) \
+ { \
+ if (FBZMODE_DITHER_TYPE(FBZMODE) == 0) \
+ { \
+ dither_lookup = &dither4_lookup[(YY & 3) << 11]; \
+ } \
+ else \
+ { \
+ dither_lookup = &dither2_lookup[(YY & 3) << 11]; \
+ } \
+ } \
+} \
+while (0)
+
+#define APPLY_DITHER(FBZMODE, XX, DITHER_LOOKUP, RR, GG, BB) \
+do \
+{ \
+ /* apply dithering */ \
+ if (FBZMODE_ENABLE_DITHERING(FBZMODE)) \
+ { \
+ /* look up the dither value from the appropriate matrix */ \
+ const UINT8 *dith = &DITHER_LOOKUP[((XX) & 3) << 1]; \
+ \
+ /* apply dithering to R,G,B */ \
+ (RR) = dith[((RR) << 3) + 0]; \
+ (GG) = dith[((GG) << 3) + 1]; \
+ (BB) = dith[((BB) << 3) + 0]; \
+ } \
+ else \
+ { \
+ (RR) >>= 3; \
+ (GG) >>= 2; \
+ (BB) >>= 3; \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Clamping macros
+ *
+ *************************************/
+
+#define CLAMPED_ARGB(ITERR, ITERG, ITERB, ITERA, FBZCP, RESULT) \
+do \
+{ \
+ r = (INT32)(ITERR) >> 12; \
+ g = (INT32)(ITERG) >> 12; \
+ b = (INT32)(ITERB) >> 12; \
+ a = (INT32)(ITERA) >> 12; \
+ \
+ if (FBZCP_RGBZW_CLAMP(FBZCP) == 0) \
+ { \
+ r &= 0xfff; \
+ RESULT.rgb.r = r; \
+ if (r == 0xfff) \
+ RESULT.rgb.r = 0; \
+ else if (r == 0x100) \
+ RESULT.rgb.r = 0xff; \
+ \
+ g &= 0xfff; \
+ RESULT.rgb.g = g; \
+ if (g == 0xfff) \
+ RESULT.rgb.g = 0; \
+ else if (g == 0x100) \
+ RESULT.rgb.g = 0xff; \
+ \
+ b &= 0xfff; \
+ RESULT.rgb.b = b; \
+ if (b == 0xfff) \
+ RESULT.rgb.b = 0; \
+ else if (b == 0x100) \
+ RESULT.rgb.b = 0xff; \
+ \
+ a &= 0xfff; \
+ RESULT.rgb.a = a; \
+ if (a == 0xfff) \
+ RESULT.rgb.a = 0; \
+ else if (a == 0x100) \
+ RESULT.rgb.a = 0xff; \
+ } \
+ else \
+ { \
+ RESULT.rgb.r = (r < 0) ? 0 : (r > 0xff) ? 0xff : r; \
+ RESULT.rgb.g = (g < 0) ? 0 : (g > 0xff) ? 0xff : g; \
+ RESULT.rgb.b = (b < 0) ? 0 : (b > 0xff) ? 0xff : b; \
+ RESULT.rgb.a = (a < 0) ? 0 : (a > 0xff) ? 0xff : a; \
+ } \
+} \
+while (0)
+
+INLINE rgbaint_t ATTR_FORCE_INLINE clampARGB(const rgbaint_t &iterargb, UINT32 FBZCP)
+{
+ rgbaint_t result(iterargb);
+ //rgbaint_t colorint((INT32) (itera>>12), (INT32) (iterr>>12), (INT32) (iterg>>12), (INT32) (iterb>>12));
+ result.shr_imm(12);
+
+ if (FBZCP_RGBZW_CLAMP(FBZCP) == 0)
+ {
+ //r &= 0xfff;
+ result.and_imm(0xfff);
+ //if (r == 0xfff)
+ rgbaint_t temp(result);
+ temp.cmpeq_imm(0xfff);
+ // result.rgb.r = 0;
+ result.andnot_reg(temp);
+ //else if (r == 0x100)
+ temp.set(result);
+ temp.cmpeq_imm(0x100);
+ // Shift by 1 so that INT32 result is not negative
+ temp.shr_imm(1);
+ // result.rgb.r = 0xff;
+ result.or_reg(temp);
+ }
+ else
+ {
+ //return colorint.to_rgba_clamp();
+ }
+ result.clamp_to_uint8();
+ return result;
+}
+
+#define CLAMPED_Z(ITERZ, FBZCP, RESULT) \
+do \
+{ \
+ (RESULT) = (INT32)(ITERZ) >> 12; \
+ if (FBZCP_RGBZW_CLAMP(FBZCP) == 0) \
+ { \
+ (RESULT) &= 0xfffff; \
+ if ((RESULT) == 0xfffff) \
+ (RESULT) = 0; \
+ else if ((RESULT) == 0x10000) \
+ (RESULT) = 0xffff; \
+ else \
+ (RESULT) &= 0xffff; \
+ } \
+ else \
+ { \
+ CLAMP((RESULT), 0, 0xffff); \
+ } \
+} \
+while (0)
+
+
+#define CLAMPED_W(ITERW, FBZCP, RESULT) \
+do \
+{ \
+ (RESULT) = (INT16)((ITERW) >> 32); \
+ if (FBZCP_RGBZW_CLAMP(FBZCP) == 0) \
+ { \
+ (RESULT) &= 0xffff; \
+ if ((RESULT) == 0xffff) \
+ (RESULT) = 0; \
+ else if ((RESULT) == 0x100) \
+ (RESULT) = 0xff; \
+ (RESULT) &= 0xff; \
+ } \
+ else \
+ { \
+ CLAMP((RESULT), 0, 0xff); \
+ } \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Chroma keying macro
+ *
+ *************************************/
+
+#define APPLY_CHROMAKEY(VV, STATS, FBZMODE, COLOR) \
+do \
+{ \
+ if (FBZMODE_ENABLE_CHROMAKEY(FBZMODE)) \
+ { \
+ /* non-range version */ \
+ if (!CHROMARANGE_ENABLE((VV)->reg[chromaRange].u)) \
+ { \
+ if (((COLOR.u ^ (VV)->reg[chromaKey].u) & 0xffffff) == 0) \
+ { \
+ (STATS)->chroma_fail++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+ \
+ /* tricky range version */ \
+ else \
+ { \
+ INT32 low, high, test; \
+ int results = 0; \
+ \
+ /* check blue */ \
+ low = (VV)->reg[chromaKey].rgb.b; \
+ high = (VV)->reg[chromaRange].rgb.b; \
+ test = COLOR.rgb.b; \
+ results = (test >= low && test <= high); \
+ results ^= CHROMARANGE_BLUE_EXCLUSIVE((VV)->reg[chromaRange].u); \
+ results <<= 1; \
+ \
+ /* check green */ \
+ low = (VV)->reg[chromaKey].rgb.g; \
+ high = (VV)->reg[chromaRange].rgb.g; \
+ test = COLOR.rgb.g; \
+ results |= (test >= low && test <= high); \
+ results ^= CHROMARANGE_GREEN_EXCLUSIVE((VV)->reg[chromaRange].u); \
+ results <<= 1; \
+ \
+ /* check red */ \
+ low = (VV)->reg[chromaKey].rgb.r; \
+ high = (VV)->reg[chromaRange].rgb.r; \
+ test = COLOR.rgb.r; \
+ results |= (test >= low && test <= high); \
+ results ^= CHROMARANGE_RED_EXCLUSIVE((VV)->reg[chromaRange].u); \
+ \
+ /* final result */ \
+ if (CHROMARANGE_UNION_MODE((VV)->reg[chromaRange].u)) \
+ { \
+ if (results != 0) \
+ { \
+ (STATS)->chroma_fail++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+ else \
+ { \
+ if (results == 7) \
+ { \
+ (STATS)->chroma_fail++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+ } \
+ } \
+} \
+while (0)
+
+INLINE bool ATTR_FORCE_INLINE chromaKeyTest(voodoo_state *v, stats_block *stats, UINT32 fbzModeReg, rgbaint_t rgbaIntColor)
+{
+ if (FBZMODE_ENABLE_CHROMAKEY(fbzModeReg))
+ {
+ rgb_union color;
+ color.u = (rgbaIntColor.get_a()<<24) | (rgbaIntColor.get_r()<<16) | (rgbaIntColor.get_g()<<8) | rgbaIntColor.get_b();
+ /* non-range version */
+ if (!CHROMARANGE_ENABLE(v->reg[chromaRange].u))
+ {
+ if (((color.u ^ v->reg[chromaKey].u) & 0xffffff) == 0)
+ {
+ stats->chroma_fail++;
+ return false;
+ }
+ }
+
+ /* tricky range version */
+ else
+ {
+ INT32 low, high, test;
+ int results = 0;
+
+ /* check blue */
+ low = v->reg[chromaKey].rgb.b;
+ high = v->reg[chromaRange].rgb.b;
+ test = color.rgb.b;
+ results = (test >= low && test <= high);
+ results ^= CHROMARANGE_BLUE_EXCLUSIVE(v->reg[chromaRange].u);
+ results <<= 1;
+
+ /* check green */
+ low = v->reg[chromaKey].rgb.g;
+ high = v->reg[chromaRange].rgb.g;
+ test = color.rgb.g;
+ results |= (test >= low && test <= high);
+ results ^= CHROMARANGE_GREEN_EXCLUSIVE(v->reg[chromaRange].u);
+ results <<= 1;
+
+ /* check red */
+ low = v->reg[chromaKey].rgb.r;
+ high = v->reg[chromaRange].rgb.r;
+ test = color.rgb.r;
+ results |= (test >= low && test <= high);
+ results ^= CHROMARANGE_RED_EXCLUSIVE(v->reg[chromaRange].u);
+
+ /* final result */
+ if (CHROMARANGE_UNION_MODE(v->reg[chromaRange].u))
+ {
+ if (results != 0)
+ {
+ stats->chroma_fail++;
+ return false;
+ }
+ }
+ else
+ {
+ if (results == 7)
+ {
+ stats->chroma_fail++;
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+
+/*************************************
+ *
+ * Alpha masking macro
+ *
+ *************************************/
+
+#define APPLY_ALPHAMASK(VV, STATS, FBZMODE, AA) \
+do \
+{ \
+ if (FBZMODE_ENABLE_ALPHA_MASK(FBZMODE)) \
+ { \
+ if (((AA) & 1) == 0) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+} \
+while (0)
+
+INLINE bool alphaMaskTest(stats_block *stats, UINT32 fbzModeReg, UINT8 alpha)
+{
+ if (FBZMODE_ENABLE_ALPHA_MASK(fbzModeReg))
+ {
+ if ((alpha & 1) == 0)
+ {
+ stats->afunc_fail++;
+ return false;
+ }
+ }
+ return true;
+}
+
+/*************************************
+ *
+ * Alpha testing macro
+ *
+ *************************************/
+
+#define APPLY_ALPHATEST(VV, STATS, ALPHAMODE, AA) \
+do \
+{ \
+ if (ALPHAMODE_ALPHATEST(ALPHAMODE)) \
+ { \
+ UINT8 alpharef = (VV)->reg[alphaMode].rgb.a; \
+ switch (ALPHAMODE_ALPHAFUNCTION(ALPHAMODE)) \
+ { \
+ case 0: /* alphaOP = never */ \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ \
+ case 1: /* alphaOP = less than */ \
+ if ((AA) >= alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 2: /* alphaOP = equal */ \
+ if ((AA) != alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 3: /* alphaOP = less than or equal */ \
+ if ((AA) > alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 4: /* alphaOP = greater than */ \
+ if ((AA) <= alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 5: /* alphaOP = not equal */ \
+ if ((AA) == alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 6: /* alphaOP = greater than or equal */ \
+ if ((AA) < alpharef) \
+ { \
+ (STATS)->afunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 7: /* alphaOP = always */ \
+ break; \
+ } \
+ } \
+} \
+while (0)
+
+INLINE bool ATTR_FORCE_INLINE alphaTest(voodoo_state *v, stats_block *stats, UINT32 alphaModeReg, UINT8 alpha)
+{
+ if (ALPHAMODE_ALPHATEST(alphaModeReg))
+ {
+ UINT8 alpharef = v->reg[alphaMode].rgb.a;
+ switch (ALPHAMODE_ALPHAFUNCTION(alphaModeReg))
+ {
+ case 0: /* alphaOP = never */
+ stats->afunc_fail++;
+ return false;
+
+ case 1: /* alphaOP = less than */
+ if (alpha >= alpharef)
+ {
+ stats->afunc_fail++;
+ return false;
+ }
+ break;
+
+ case 2: /* alphaOP = equal */
+ if (alpha != alpharef)
+ {
+ stats->afunc_fail++;
+ return false;
+ }
+ break;
+
+ case 3: /* alphaOP = less than or equal */
+ if (alpha > alpharef)
+ {
+ stats->afunc_fail++;
+ return false;
+ }
+ break;
+
+ case 4: /* alphaOP = greater than */
+ if (alpha <= alpharef)
+ {
+ stats->afunc_fail++;
+ return false;
+ }
+ break;
+
+ case 5: /* alphaOP = not equal */
+ if (alpha == alpharef)
+ {
+ stats->afunc_fail++;
+ return false;
+ }
+ break;
+
+ case 6: /* alphaOP = greater than or equal */
+ if (alpha < alpharef)
+ {
+ stats->afunc_fail++;
+ return false;
+ }
+ break;
+
+ case 7: /* alphaOP = always */
+ break;
+ }
+ }
+ return true;
+}
+
+
+/*************************************
+ *
+ * Alpha blending macro
+ *
+ *************************************/
+
+#define APPLY_ALPHA_BLEND(FBZMODE, ALPHAMODE, XX, DITHER, RR, GG, BB, AA) \
+do \
+{ \
+ if (ALPHAMODE_ALPHABLEND(ALPHAMODE)) \
+ { \
+ int dpix = dest[XX]; \
+ int dr, dg, db; \
+ EXTRACT_565_TO_888(dpix, dr, dg, db); \
+ int da = FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) ? depth[XX] : 0xff; \
+ int sr = (RR); \
+ int sg = (GG); \
+ int sb = (BB); \
+ int sa = (AA); \
+ int ta; \
+ \
+ /* apply dither subtraction */ \
+ if (FBZMODE_ALPHA_DITHER_SUBTRACT(FBZMODE)) \
+ { \
+ /* look up the dither value from the appropriate matrix */ \
+ int dith = DITHER[(XX) & 3]; \
+ \
+ /* subtract the dither value */ \
+ dr = ((dr << 1) + 15 - dith) >> 1; \
+ dg = ((dg << 2) + 15 - dith) >> 2; \
+ db = ((db << 1) + 15 - dith) >> 1; \
+ } \
+ \
+ /* compute source portion */ \
+ switch (ALPHAMODE_SRCRGBBLEND(ALPHAMODE)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* AZERO */ \
+ (RR) = (GG) = (BB) = 0; \
+ break; \
+ \
+ case 1: /* ASRC_ALPHA */ \
+ (RR) = (sr * (sa + 1)) >> 8; \
+ (GG) = (sg * (sa + 1)) >> 8; \
+ (BB) = (sb * (sa + 1)) >> 8; \
+ break; \
+ \
+ case 2: /* A_COLOR */ \
+ (RR) = (sr * (dr + 1)) >> 8; \
+ (GG) = (sg * (dg + 1)) >> 8; \
+ (BB) = (sb * (db + 1)) >> 8; \
+ break; \
+ \
+ case 3: /* ADST_ALPHA */ \
+ (RR) = (sr * (da + 1)) >> 8; \
+ (GG) = (sg * (da + 1)) >> 8; \
+ (BB) = (sb * (da + 1)) >> 8; \
+ break; \
+ \
+ case 4: /* AONE */ \
+ break; \
+ \
+ case 5: /* AOMSRC_ALPHA */ \
+ (RR) = (sr * (0x100 - sa)) >> 8; \
+ (GG) = (sg * (0x100 - sa)) >> 8; \
+ (BB) = (sb * (0x100 - sa)) >> 8; \
+ break; \
+ \
+ case 6: /* AOM_COLOR */ \
+ (RR) = (sr * (0x100 - dr)) >> 8; \
+ (GG) = (sg * (0x100 - dg)) >> 8; \
+ (BB) = (sb * (0x100 - db)) >> 8; \
+ break; \
+ \
+ case 7: /* AOMDST_ALPHA */ \
+ (RR) = (sr * (0x100 - da)) >> 8; \
+ (GG) = (sg * (0x100 - da)) >> 8; \
+ (BB) = (sb * (0x100 - da)) >> 8; \
+ break; \
+ \
+ case 15: /* ASATURATE */ \
+ ta = (sa < (0x100 - da)) ? sa : (0x100 - da); \
+ (RR) = (sr * (ta + 1)) >> 8; \
+ (GG) = (sg * (ta + 1)) >> 8; \
+ (BB) = (sb * (ta + 1)) >> 8; \
+ break; \
+ } \
+ \
+ /* add in dest portion */ \
+ switch (ALPHAMODE_DSTRGBBLEND(ALPHAMODE)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* AZERO */ \
+ break; \
+ \
+ case 1: /* ASRC_ALPHA */ \
+ (RR) += (dr * (sa + 1)) >> 8; \
+ (GG) += (dg * (sa + 1)) >> 8; \
+ (BB) += (db * (sa + 1)) >> 8; \
+ break; \
+ \
+ case 2: /* A_COLOR */ \
+ (RR) += (dr * (sr + 1)) >> 8; \
+ (GG) += (dg * (sg + 1)) >> 8; \
+ (BB) += (db * (sb + 1)) >> 8; \
+ break; \
+ \
+ case 3: /* ADST_ALPHA */ \
+ (RR) += (dr * (da + 1)) >> 8; \
+ (GG) += (dg * (da + 1)) >> 8; \
+ (BB) += (db * (da + 1)) >> 8; \
+ break; \
+ \
+ case 4: /* AONE */ \
+ (RR) += dr; \
+ (GG) += dg; \
+ (BB) += db; \
+ break; \
+ \
+ case 5: /* AOMSRC_ALPHA */ \
+ (RR) += (dr * (0x100 - sa)) >> 8; \
+ (GG) += (dg * (0x100 - sa)) >> 8; \
+ (BB) += (db * (0x100 - sa)) >> 8; \
+ break; \
+ \
+ case 6: /* AOM_COLOR */ \
+ (RR) += (dr * (0x100 - sr)) >> 8; \
+ (GG) += (dg * (0x100 - sg)) >> 8; \
+ (BB) += (db * (0x100 - sb)) >> 8; \
+ break; \
+ \
+ case 7: /* AOMDST_ALPHA */ \
+ (RR) += (dr * (0x100 - da)) >> 8; \
+ (GG) += (dg * (0x100 - da)) >> 8; \
+ (BB) += (db * (0x100 - da)) >> 8; \
+ break; \
+ \
+ case 15: /* A_COLORBEFOREFOG */ \
+ (RR) += (dr * (prefogr + 1)) >> 8; \
+ (GG) += (dg * (prefogg + 1)) >> 8; \
+ (BB) += (db * (prefogb + 1)) >> 8; \
+ break; \
+ } \
+ \
+ /* blend the source alpha */ \
+ (AA) = 0; \
+ if (ALPHAMODE_SRCALPHABLEND(ALPHAMODE) == 4) \
+ (AA) = sa; \
+ \
+ /* blend the dest alpha */ \
+ if (ALPHAMODE_DSTALPHABLEND(ALPHAMODE) == 4) \
+ (AA) += da; \
+ \
+ /* clamp */ \
+ CLAMP((RR), 0x00, 0xff); \
+ CLAMP((GG), 0x00, 0xff); \
+ CLAMP((BB), 0x00, 0xff); \
+ CLAMP((AA), 0x00, 0xff); \
+ } \
+} \
+while (0)
+
+INLINE void ATTR_FORCE_INLINE alphaBlend(UINT32 FBZMODE, UINT32 ALPHAMODE, INT32 x, const UINT8 *dither, int dpix, UINT16 *depth, rgbaint_t &preFog, rgbaint_t &srcColor)
+{
+ if (ALPHAMODE_ALPHABLEND(ALPHAMODE))
+ {
+ //int dpix = dest[XX];
+ int dr, dg, db;
+ EXTRACT_565_TO_888(dpix, dr, dg, db);
+ int da = FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) ? depth[x] : 0xff;
+ //int sr = (RR);
+ //int sg = (GG);
+ //int sb = (BB);
+ //int sa = (AA);
+ int sa = srcColor.get_a();
+ int ta;
+ int srcAlphaScale, destAlphaScale;
+ rgbaint_t srcScale, destScale;
+
+ /* apply dither subtraction */
+ if (FBZMODE_ALPHA_DITHER_SUBTRACT(FBZMODE))
+ {
+ /* look up the dither value from the appropriate matrix */
+ //int dith = DITHER[(XX) & 3];
+
+ /* subtract the dither value */
+ dr += (15 - dither[x&3]) >> 1;
+ dg += (15 - dither[x&3]) >> 2;
+ db += (15 - dither[x&3]) >> 1;
+ }
+
+ /* blend the source alpha */
+ srcAlphaScale = 0;
+ if (ALPHAMODE_SRCALPHABLEND(ALPHAMODE) == 4)
+ srcAlphaScale = 256;
+ //(AA) = sa;
+
+ /* compute source portion */
+ switch (ALPHAMODE_SRCRGBBLEND(ALPHAMODE))
+ {
+ default: /* reserved */
+ case 0: /* AZERO */
+ srcScale.set(srcAlphaScale, 0, 0, 0);
+ //(RR) = (GG) = (BB) = 0;
+ break;
+
+ case 1: /* ASRC_ALPHA */
+ srcScale.set(srcAlphaScale-1, sa, sa, sa);
+ srcScale.add_imm(1);
+ //(RR) = (sr * (sa + 1)) >> 8;
+ //(GG) = (sg * (sa + 1)) >> 8;
+ //(BB) = (sb * (sa + 1)) >> 8;
+ break;
+
+ case 2: /* A_COLOR */
+ srcScale.set(srcAlphaScale-1, dr, dg, db);
+ srcScale.add_imm(1);
+ //(RR) = (sr * (dr + 1)) >> 8;
+ //(GG) = (sg * (dg + 1)) >> 8;
+ //(BB) = (sb * (db + 1)) >> 8;
+ break;
+
+ case 3: /* ADST_ALPHA */
+ ta = da + 1;
+ srcScale.set(srcAlphaScale, ta, ta, ta);
+ //(RR) = (sr * (da + 1)) >> 8;
+ //(GG) = (sg * (da + 1)) >> 8;
+ //(BB) = (sb * (da + 1)) >> 8;
+ break;
+
+ case 4: /* AONE */
+ srcScale.set(srcAlphaScale, 256, 256, 256);
+ break;
+
+ case 5: /* AOMSRC_ALPHA */
+ ta = (0x100 - sa);
+ srcScale.set(srcAlphaScale, ta, ta, ta);
+ //(RR) = (sr * (0x100 - sa)) >> 8;
+ //(GG) = (sg * (0x100 - sa)) >> 8;
+ //(BB) = (sb * (0x100 - sa)) >> 8;
+ break;
+
+ case 6: /* AOM_COLOR */
+ srcScale.set(srcAlphaScale, (0x100 - dr), (0x100 - dg), (0x100 - db));
+ //(RR) = (sr * (0x100 - dr)) >> 8;
+ //(GG) = (sg * (0x100 - dg)) >> 8;
+ //(BB) = (sb * (0x100 - db)) >> 8;
+ break;
+
+ case 7: /* AOMDST_ALPHA */
+ ta = (0x100 - da);
+ srcScale.set(srcAlphaScale, ta, ta, ta);
+ //(RR) = (sr * (0x100 - da)) >> 8;
+ //(GG) = (sg * (0x100 - da)) >> 8;
+ //(BB) = (sb * (0x100 - da)) >> 8;
+ break;
+
+ case 15: /* ASATURATE */
+ ta = (sa < (0x100 - da)) ? sa : (0x100 - da);
+ ta++;
+ srcScale.set(srcAlphaScale, ta, ta, ta);
+ //(RR) = (sr * (ta + 1)) >> 8;
+ //(GG) = (sg * (ta + 1)) >> 8;
+ //(BB) = (sb * (ta + 1)) >> 8;
+ break;
+ }
+
+ /* blend the dest alpha */
+ destAlphaScale = 0;
+ if (ALPHAMODE_DSTALPHABLEND(ALPHAMODE) == 4)
+ destAlphaScale = 256;
+ //(AA) += da;
+
+ /* add in dest portion */
+ switch (ALPHAMODE_DSTRGBBLEND(ALPHAMODE))
+ {
+ default: /* reserved */
+ case 0: /* AZERO */
+ destScale.set(destAlphaScale, 0, 0, 0);
+ break;
+
+ case 1: /* ASRC_ALPHA */
+ destScale.set(destAlphaScale-1, sa, sa, sa);
+ destScale.add_imm(1);
+ //(RR) += (dr * (sa + 1)) >> 8;
+ //(GG) += (dg * (sa + 1)) >> 8;
+ //(BB) += (db * (sa + 1)) >> 8;
+ break;
+
+ case 2: /* A_COLOR */
+ destScale.set(srcColor);
+ destScale.add_imm(1);
+ destScale.set_a(destAlphaScale);
+ //(RR) += (dr * (sr + 1)) >> 8;
+ //(GG) += (dg * (sg + 1)) >> 8;
+ //(BB) += (db * (sb + 1)) >> 8;
+ break;
+
+ case 3: /* ADST_ALPHA */
+ ta = da + 1;
+ destScale.set(destAlphaScale, ta, ta, ta);
+ //(RR) += (dr * (da + 1)) >> 8;
+ //(GG) += (dg * (da + 1)) >> 8;
+ //(BB) += (db * (da + 1)) >> 8;
+ break;
+
+ case 4: /* AONE */
+ destScale.set(destAlphaScale, 256, 256, 256);
+ //(RR) += dr;
+ //(GG) += dg;
+ //(BB) += db;
+ break;
+
+ case 5: /* AOMSRC_ALPHA */
+ ta = (0x100 - sa);
+ destScale.set(destAlphaScale, ta, ta, ta);
+ //(RR) += (dr * (0x100 - sa)) >> 8;
+ //(GG) += (dg * (0x100 - sa)) >> 8;
+ //(BB) += (db * (0x100 - sa)) >> 8;
+ break;
+
+ case 6: /* AOM_COLOR */
+ destScale.set(0x100, 0x100, 0x100, 0x100);
+ destScale.sub(srcColor);
+ destScale.set_a(destAlphaScale);
+ //destScale.set(destAlphaScale, (0x100 - color.rgb.r), (0x100 - color.rgb.g), (0x100 - color.rgb.b));
+ //(RR) += (dr * (0x100 - sr)) >> 8;
+ //(GG) += (dg * (0x100 - sg)) >> 8;
+ //(BB) += (db * (0x100 - sb)) >> 8;
+ break;
+
+ case 7: /* AOMDST_ALPHA */
+ ta = (0x100 - da);
+ destScale.set(destAlphaScale, ta, ta, ta);
+ //(RR) += (dr * (0x100 - da)) >> 8;
+ //(GG) += (dg * (0x100 - da)) >> 8;
+ //(BB) += (db * (0x100 - da)) >> 8;
+ break;
+
+ case 15: /* A_COLORBEFOREFOG */
+ destScale.set(preFog);
+ destScale.add_imm(1);
+ destScale.set_a(destAlphaScale);
+ //destScale.set((rgb_t) (((destAlphaScale-1)<<24) | (preFog.u & 0x00ffffff)));
+ //destScale.add_imm(1);
+ //(RR) += (dr * (prefogr + 1)) >> 8;
+ //(GG) += (dg * (prefogg + 1)) >> 8;
+ //(BB) += (db * (prefogb + 1)) >> 8;
+ break;
+ }
+ // Main blend
+ rgbaint_t destColor(da, dr, dg, db);
+
+ srcColor.scale2_add_and_clamp(srcScale, destColor, destScale);
+ /* clamp */
+ //CLAMP((RR), 0x00, 0xff);
+ //CLAMP((GG), 0x00, 0xff);
+ //CLAMP((BB), 0x00, 0xff);
+ //CLAMP((AA), 0x00, 0xff);
+ }
+}
+
+
+/*************************************
+ *
+ * Fogging macro
+ *
+ *************************************/
+
+#define APPLY_FOGGING(VV, FOGMODE, FBZCP, XX, DITHER4, RR, GG, BB, ITERZ, ITERW, ITERAXXX) \
+do \
+{ \
+ if (FOGMODE_ENABLE_FOG(FOGMODE)) \
+ { \
+ rgb_union fogcolor = (VV)->reg[fogColor]; \
+ INT32 fr, fg, fb; \
+ \
+ /* constant fog bypasses everything else */ \
+ if (FOGMODE_FOG_CONSTANT(FOGMODE)) \
+ { \
+ fr = fogcolor.rgb.r; \
+ fg = fogcolor.rgb.g; \
+ fb = fogcolor.rgb.b; \
+ } \
+ \
+ /* non-constant fog comes from several sources */ \
+ else \
+ { \
+ INT32 fogblend = 0; \
+ \
+ /* if fog_add is zero, we start with the fog color */ \
+ if (FOGMODE_FOG_ADD(FOGMODE) == 0) \
+ { \
+ fr = fogcolor.rgb.r; \
+ fg = fogcolor.rgb.g; \
+ fb = fogcolor.rgb.b; \
+ } \
+ else \
+ fr = fg = fb = 0; \
+ \
+ /* if fog_mult is zero, we subtract the incoming color */ \
+ if (FOGMODE_FOG_MULT(FOGMODE) == 0) \
+ { \
+ fr -= (RR); \
+ fg -= (GG); \
+ fb -= (BB); \
+ } \
+ \
+ /* fog blending mode */ \
+ switch (FOGMODE_FOG_ZALPHA(FOGMODE)) \
+ { \
+ case 0: /* fog table */ \
+ { \
+ INT32 delta = (VV)->fbi.fogdelta[fogdepth >> 10]; \
+ INT32 deltaval; \
+ \
+ /* perform the multiply against lower 8 bits of wfloat */ \
+ deltaval = (delta & (VV)->fbi.fogdelta_mask) * \
+ ((fogdepth >> 2) & 0xff); \
+ \
+ /* fog zones allow for negating this value */ \
+ if (FOGMODE_FOG_ZONES(FOGMODE) && (delta & 2)) \
+ deltaval = -deltaval; \
+ deltaval >>= 6; \
+ \
+ /* apply dither */ \
+ if (FOGMODE_FOG_DITHER(FOGMODE)) \
+ deltaval += DITHER4[(XX) & 3]; \
+ deltaval >>= 4; \
+ \
+ /* add to the blending factor */ \
+ fogblend = (VV)->fbi.fogblend[fogdepth >> 10] + deltaval; \
+ break; \
+ } \
+ \
+ case 1: /* iterated A */ \
+ fogblend = ITERAXXX.rgb.a; \
+ break; \
+ \
+ case 2: /* iterated Z */ \
+ CLAMPED_Z((ITERZ), FBZCP, fogblend); \
+ fogblend >>= 8; \
+ break; \
+ \
+ case 3: /* iterated W - Voodoo 2 only */ \
+ CLAMPED_W((ITERW), FBZCP, fogblend); \
+ break; \
+ } \
+ \
+ /* perform the blend */ \
+ fogblend++; \
+ fr = (fr * fogblend) >> 8; \
+ fg = (fg * fogblend) >> 8; \
+ fb = (fb * fogblend) >> 8; \
+ } \
+ \
+ /* if fog_mult is 0, we add this to the original color */ \
+ if (FOGMODE_FOG_MULT(FOGMODE) == 0) \
+ { \
+ (RR) += fr; \
+ (GG) += fg; \
+ (BB) += fb; \
+ } \
+ \
+ /* otherwise this just becomes the new color */ \
+ else \
+ { \
+ (RR) = fr; \
+ (GG) = fg; \
+ (BB) = fb; \
+ } \
+ \
+ /* clamp */ \
+ CLAMP((RR), 0x00, 0xff); \
+ CLAMP((GG), 0x00, 0xff); \
+ CLAMP((BB), 0x00, 0xff); \
+ } \
+} \
+while (0)
+
+INLINE void ATTR_FORCE_INLINE applyFogging(voodoo_state *v, UINT32 fogModeReg, UINT32 fbzCpReg, INT32 x, const UINT8 *dither4, INT32 fogDepth,
+ rgbaint_t &color, INT32 iterz, INT64 iterw, UINT8 itera)
+{
+ if (FOGMODE_ENABLE_FOG(fogModeReg))
+ {
+ UINT32 color_alpha = color.get_a();
+
+ /* constant fog bypasses everything else */
+ rgbaint_t fogColorLocal(v->reg[fogColor].u);
+
+ if (FOGMODE_FOG_CONSTANT(fogModeReg))
+ {
+ /* if fog_mult is 0, we add this to the original color */
+ if (FOGMODE_FOG_MULT(fogModeReg) == 0)
+ {
+ fogColorLocal.add(color);
+ fogColorLocal.clamp_to_uint8();
+ //color += fog;
+ }
+
+ /* otherwise this just becomes the new color */
+ else
+ {
+ //color = fogColorLocal;
+ //color = fog;
+ }
+ }
+ /* non-constant fog comes from several sources */
+ else
+ {
+ INT32 fogblend = 0;
+
+ /* if fog_add is zero, we start with the fog color */
+ if (FOGMODE_FOG_ADD(fogModeReg))
+ fogColorLocal.set(0, 0, 0, 0);
+ //fr = fg = fb = 0;
+
+ /* if fog_mult is zero, we subtract the incoming color */
+ if (!FOGMODE_FOG_MULT(fogModeReg))
+ {
+ fogColorLocal.sub(color);
+ //fog.rgb -= color.rgb;
+ //fr -= (RR);
+ //fg -= (GG);
+ //fb -= (BB);
+ }
+
+ /* fog blending mode */
+ switch (FOGMODE_FOG_ZALPHA(fogModeReg))
+ {
+ case 0: /* fog table */
+ {
+ INT32 delta = v->fbi.fogdelta[fogDepth >> 10];
+ INT32 deltaval;
+
+ /* perform the multiply against lower 8 bits of wfloat */
+ deltaval = (delta & v->fbi.fogdelta_mask) *
+ ((fogDepth >> 2) & 0xff);
+
+ /* fog zones allow for negating this value */
+ if (FOGMODE_FOG_ZONES(fogModeReg) && (delta & 2))
+ deltaval = -deltaval;
+ deltaval >>= 6;
+
+ /* apply dither */
+ if (FOGMODE_FOG_DITHER(fogModeReg))
+ deltaval += dither4[x&3];
+ deltaval >>= 4;
+
+ /* add to the blending factor */
+ fogblend = v->fbi.fogblend[fogDepth >> 10] + deltaval;
+ break;
+ }
+
+ case 1: /* iterated A */
+ fogblend = itera;
+ break;
+
+ case 2: /* iterated Z */
+ CLAMPED_Z(iterz, fbzCpReg, fogblend);
+ fogblend >>= 8;
+ break;
+
+ case 3: /* iterated W - Voodoo 2 only */
+ CLAMPED_W(iterw, fbzCpReg, fogblend);
+ break;
+ }
+
+ /* perform the blend */
+ fogblend++;
+
+ //fr = (fr * fogblend) >> 8;
+ //fg = (fg * fogblend) >> 8;
+ //fb = (fb * fogblend) >> 8;
+ /* if fog_mult is 0, we add this to the original color */
+ if (FOGMODE_FOG_MULT(fogModeReg) == 0)
+ {
+ fogColorLocal.scale_imm_add_and_clamp(fogblend, color);
+ //color += fog;
+ //(RR) += fr;
+ //(GG) += fg;
+ //(BB) += fb;
+ }
+
+ /* otherwise this just becomes the new color */
+ else
+ {
+ fogColorLocal.scale_imm_and_clamp(fogblend);
+ //color = fog;
+ //(RR) = fr;
+ //(GG) = fg;
+ //(BB) = fb;
+ }
+ }
+
+
+ /* clamp */
+ //CLAMP((RR), 0x00, 0xff);
+ //CLAMP((GG), 0x00, 0xff);
+ //CLAMP((BB), 0x00, 0xff);
+ fogColorLocal.set_a(color_alpha);
+ color.set(fogColorLocal);
+ }
+}
+
+
+/*************************************
+ *
+ * Texture pipeline macro
+ *
+ *************************************/
+
+#define TEXTURE_PIPELINE(TT, XX, DITHER4, TEXMODE, COTHER, LOOKUP, LODBASE, ITERS, ITERT, ITERW, RESULT) \
+do \
+{ \
+ INT32 blendr, blendg, blendb, blenda; \
+ INT32 tr, tg, tb, ta; \
+ INT32 s, t, lod, ilod; \
+ INT32 smax, tmax; \
+ UINT32 texbase; \
+ rgb_union c_local; \
+ \
+ /* determine the S/T/LOD values for this texture */ \
+ if (TEXMODE_ENABLE_PERSPECTIVE(TEXMODE)) \
+ { \
+ if (USE_FAST_RECIP) { \
+ const INT32 oow = fast_reciplog((ITERW), &lod); \
+ s = ((INT64)oow * (ITERS)) >> 29; \
+ t = ((INT64)oow * (ITERT)) >> 29; \
+ } else { \
+ multi_reciplog(ITERS, ITERT, ITERW, lod, s, t); \
+ } \
+ lod += (LODBASE); \
+ } \
+ else \
+ { \
+ s = (ITERS) >> 14; \
+ t = (ITERT) >> 14; \
+ lod = (LODBASE); \
+ } \
+ \
+ /* clamp W */ \
+ if (TEXMODE_CLAMP_NEG_W(TEXMODE) && (ITERW) < 0) \
+ s = t = 0; \
+ \
+ /* clamp the LOD */ \
+ lod += (TT)->lodbias; \
+ if (TEXMODE_ENABLE_LOD_DITHER(TEXMODE)) \
+ lod += DITHER4[(XX) & 3] << 4; \
+ if (lod < (TT)->lodmin) \
+ lod = (TT)->lodmin; \
+ if (lod > (TT)->lodmax) \
+ lod = (TT)->lodmax; \
+ \
+ /* now the LOD is in range; if we don't own this LOD, take the next one */ \
+ ilod = lod >> 8; \
+ if (!(((TT)->lodmask >> ilod) & 1)) \
+ ilod++; \
+ \
+ /* fetch the texture base */ \
+ texbase = (TT)->lodoffset[ilod]; \
+ \
+ /* compute the maximum s and t values at this LOD */ \
+ smax = (TT)->wmask >> ilod; \
+ tmax = (TT)->hmask >> ilod; \
+ \
+ /* determine whether we are point-sampled or bilinear */ \
+ if ((lod == (TT)->lodmin && !TEXMODE_MAGNIFICATION_FILTER(TEXMODE)) || \
+ (lod != (TT)->lodmin && !TEXMODE_MINIFICATION_FILTER(TEXMODE))) \
+ { \
+ /* point sampled */ \
+ \
+ UINT32 texel0; \
+ \
+ /* adjust S/T for the LOD and strip off the fractions */ \
+ s >>= ilod + 18; \
+ t >>= ilod + 18; \
+ \
+ /* clamp/wrap S/T if necessary */ \
+ if (TEXMODE_CLAMP_S(TEXMODE)) \
+ CLAMP(s, 0, smax); \
+ if (TEXMODE_CLAMP_T(TEXMODE)) \
+ CLAMP(t, 0, tmax); \
+ s &= smax; \
+ t &= tmax; \
+ t *= smax + 1; \
+ \
+ /* fetch texel data */ \
+ if (TEXMODE_FORMAT(TEXMODE) < 8) \
+ { \
+ texel0 = *(UINT8 *)&(TT)->ram[(texbase + t + s) & (TT)->mask]; \
+ c_local.u = (LOOKUP)[texel0]; \
+ } \
+ else \
+ { \
+ texel0 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s)) & (TT)->mask]; \
+ if (TEXMODE_FORMAT(TEXMODE) >= 10 && TEXMODE_FORMAT(TEXMODE) <= 12) \
+ c_local.u = (LOOKUP)[texel0]; \
+ else \
+ c_local.u = ((LOOKUP)[texel0 & 0xff] & 0xffffff) | \
+ ((texel0 & 0xff00) << 16); \
+ } \
+ } \
+ else \
+ { \
+ /* bilinear filtered */ \
+ \
+ UINT32 texel0, texel1, texel2, texel3; \
+ UINT32 sfrac, tfrac; \
+ INT32 s1, t1; \
+ \
+ /* adjust S/T for the LOD and strip off all but the low 8 bits of */ \
+ /* the fraction */ \
+ s >>= ilod + 10; \
+ t >>= ilod + 10; \
+ \
+ /* also subtract 1/2 texel so that (0.5,0.5) = a full (0,0) texel */ \
+ s -= 0x80; \
+ t -= 0x80; \
+ \
+ /* extract the fractions */ \
+ sfrac = s & (TT)->bilinear_mask; \
+ tfrac = t & (TT)->bilinear_mask; \
+ \
+ /* now toss the rest */ \
+ s >>= 8; \
+ t >>= 8; \
+ s1 = s + 1; \
+ t1 = t + 1; \
+ \
+ /* clamp/wrap S/T if necessary */ \
+ if (TEXMODE_CLAMP_S(TEXMODE)) \
+ { \
+ CLAMP(s, 0, smax); \
+ CLAMP(s1, 0, smax); \
+ } \
+ if (TEXMODE_CLAMP_T(TEXMODE)) \
+ { \
+ CLAMP(t, 0, tmax); \
+ CLAMP(t1, 0, tmax); \
+ } \
+ s &= smax; \
+ s1 &= smax; \
+ t &= tmax; \
+ t1 &= tmax; \
+ t *= smax + 1; \
+ t1 *= smax + 1; \
+ \
+ /* fetch texel data */ \
+ if (TEXMODE_FORMAT(TEXMODE) < 8) \
+ { \
+ texel0 = *(UINT8 *)&(TT)->ram[(texbase + t + s) & (TT)->mask]; \
+ texel1 = *(UINT8 *)&(TT)->ram[(texbase + t + s1) & (TT)->mask]; \
+ texel2 = *(UINT8 *)&(TT)->ram[(texbase + t1 + s) & (TT)->mask]; \
+ texel3 = *(UINT8 *)&(TT)->ram[(texbase + t1 + s1) & (TT)->mask]; \
+ texel0 = (LOOKUP)[texel0]; \
+ texel1 = (LOOKUP)[texel1]; \
+ texel2 = (LOOKUP)[texel2]; \
+ texel3 = (LOOKUP)[texel3]; \
+ } \
+ else \
+ { \
+ texel0 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s)) & (TT)->mask]; \
+ texel1 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s1)) & (TT)->mask];\
+ texel2 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t1 + s)) & (TT)->mask];\
+ texel3 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t1 + s1)) & (TT)->mask];\
+ if (TEXMODE_FORMAT(TEXMODE) >= 10 && TEXMODE_FORMAT(TEXMODE) <= 12) \
+ { \
+ texel0 = (LOOKUP)[texel0]; \
+ texel1 = (LOOKUP)[texel1]; \
+ texel2 = (LOOKUP)[texel2]; \
+ texel3 = (LOOKUP)[texel3]; \
+ } \
+ else \
+ { \
+ texel0 = ((LOOKUP)[texel0 & 0xff] & 0xffffff) | \
+ ((texel0 & 0xff00) << 16); \
+ texel1 = ((LOOKUP)[texel1 & 0xff] & 0xffffff) | \
+ ((texel1 & 0xff00) << 16); \
+ texel2 = ((LOOKUP)[texel2 & 0xff] & 0xffffff) | \
+ ((texel2 & 0xff00) << 16); \
+ texel3 = ((LOOKUP)[texel3 & 0xff] & 0xffffff) | \
+ ((texel3 & 0xff00) << 16); \
+ } \
+ } \
+ \
+ /* weigh in each texel */ \
+ c_local.u = rgbaint_t::bilinear_filter(texel0, texel1, texel2, texel3, sfrac, tfrac); \
+ } \
+ \
+ /* select zero/other for RGB */ \
+ if (!TEXMODE_TC_ZERO_OTHER(TEXMODE)) \
+ { \
+ tr = COTHER.rgb.r; \
+ tg = COTHER.rgb.g; \
+ tb = COTHER.rgb.b; \
+ } \
+ else \
+ tr = tg = tb = 0; \
+ \
+ /* select zero/other for alpha */ \
+ if (!TEXMODE_TCA_ZERO_OTHER(TEXMODE)) \
+ ta = COTHER.rgb.a; \
+ else \
+ ta = 0; \
+ \
+ /* potentially subtract c_local */ \
+ if (TEXMODE_TC_SUB_CLOCAL(TEXMODE)) \
+ { \
+ tr -= c_local.rgb.r; \
+ tg -= c_local.rgb.g; \
+ tb -= c_local.rgb.b; \
+ } \
+ if (TEXMODE_TCA_SUB_CLOCAL(TEXMODE)) \
+ ta -= c_local.rgb.a; \
+ \
+ /* blend RGB */ \
+ switch (TEXMODE_TC_MSELECT(TEXMODE)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* zero */ \
+ blendr = blendg = blendb = 0; \
+ break; \
+ \
+ case 1: /* c_local */ \
+ blendr = c_local.rgb.r; \
+ blendg = c_local.rgb.g; \
+ blendb = c_local.rgb.b; \
+ break; \
+ \
+ case 2: /* a_other */ \
+ blendr = blendg = blendb = COTHER.rgb.a; \
+ break; \
+ \
+ case 3: /* a_local */ \
+ blendr = blendg = blendb = c_local.rgb.a; \
+ break; \
+ \
+ case 4: /* LOD (detail factor) */ \
+ if ((TT)->detailbias <= lod) \
+ blendr = blendg = blendb = 0; \
+ else \
+ { \
+ blendr = ((((TT)->detailbias - lod) << (TT)->detailscale) >> 8);\
+ if (blendr > (TT)->detailmax) \
+ blendr = (TT)->detailmax; \
+ blendg = blendb = blendr; \
+ } \
+ break; \
+ \
+ case 5: /* LOD fraction */ \
+ blendr = blendg = blendb = lod & 0xff; \
+ break; \
+ } \
+ \
+ /* blend alpha */ \
+ switch (TEXMODE_TCA_MSELECT(TEXMODE)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* zero */ \
+ blenda = 0; \
+ break; \
+ \
+ case 1: /* c_local */ \
+ blenda = c_local.rgb.a; \
+ break; \
+ \
+ case 2: /* a_other */ \
+ blenda = COTHER.rgb.a; \
+ break; \
+ \
+ case 3: /* a_local */ \
+ blenda = c_local.rgb.a; \
+ break; \
+ \
+ case 4: /* LOD (detail factor) */ \
+ if ((TT)->detailbias <= lod) \
+ blenda = 0; \
+ else \
+ { \
+ blenda = ((((TT)->detailbias - lod) << (TT)->detailscale) >> 8);\
+ if (blenda > (TT)->detailmax) \
+ blenda = (TT)->detailmax; \
+ } \
+ break; \
+ \
+ case 5: /* LOD fraction */ \
+ blenda = lod & 0xff; \
+ break; \
+ } \
+ \
+ /* reverse the RGB blend */ \
+ if (!TEXMODE_TC_REVERSE_BLEND(TEXMODE)) \
+ { \
+ blendr ^= 0xff; \
+ blendg ^= 0xff; \
+ blendb ^= 0xff; \
+ } \
+ \
+ /* reverse the alpha blend */ \
+ if (!TEXMODE_TCA_REVERSE_BLEND(TEXMODE)) \
+ blenda ^= 0xff; \
+ \
+ /* do the blend */ \
+ tr = (tr * (blendr + 1)) >> 8; \
+ tg = (tg * (blendg + 1)) >> 8; \
+ tb = (tb * (blendb + 1)) >> 8; \
+ ta = (ta * (blenda + 1)) >> 8; \
+ \
+ /* add clocal or alocal to RGB */ \
+ switch (TEXMODE_TC_ADD_ACLOCAL(TEXMODE)) \
+ { \
+ case 3: /* reserved */ \
+ case 0: /* nothing */ \
+ break; \
+ \
+ case 1: /* add c_local */ \
+ tr += c_local.rgb.r; \
+ tg += c_local.rgb.g; \
+ tb += c_local.rgb.b; \
+ break; \
+ \
+ case 2: /* add_alocal */ \
+ tr += c_local.rgb.a; \
+ tg += c_local.rgb.a; \
+ tb += c_local.rgb.a; \
+ break; \
+ } \
+ \
+ /* add clocal or alocal to alpha */ \
+ if (TEXMODE_TCA_ADD_ACLOCAL(TEXMODE)) \
+ ta += c_local.rgb.a; \
+ \
+ /* clamp */ \
+ RESULT.rgb.r = (tr < 0) ? 0 : (tr > 0xff) ? 0xff : tr; \
+ RESULT.rgb.g = (tg < 0) ? 0 : (tg > 0xff) ? 0xff : tg; \
+ RESULT.rgb.b = (tb < 0) ? 0 : (tb > 0xff) ? 0xff : tb; \
+ RESULT.rgb.a = (ta < 0) ? 0 : (ta > 0xff) ? 0xff : ta; \
+ \
+ /* invert */ \
+ if (TEXMODE_TC_INVERT_OUTPUT(TEXMODE)) \
+ RESULT.u ^= 0x00ffffff; \
+ if (TEXMODE_TCA_INVERT_OUTPUT(TEXMODE)) \
+ RESULT.rgb.a ^= 0xff; \
+} \
+while (0)
+
+
+
+/*************************************
+ *
+ * Pixel pipeline macros
+ *
+ *************************************/
+
+#define PIXEL_PIPELINE_BEGIN(VV, STATS, XX, YY, FBZCOLORPATH, FBZMODE, ITERZ, ITERW) \
+do \
+{ \
+ INT32 depthval, wfloat, fogdepth, biasdepth; \
+ INT32 r, g, b, a; \
+ \
+ (STATS)->pixels_in++; \
+ \
+ /* apply clipping */ \
+ /* note that for perf reasons, we assume the caller has done clipping */ \
+ \
+ /* handle stippling */ \
+ if (FBZMODE_ENABLE_STIPPLE(FBZMODE)) \
+ { \
+ /* rotate mode */ \
+ if (FBZMODE_STIPPLE_PATTERN(FBZMODE) == 0) \
+ { \
+ (VV)->reg[stipple].u = ((VV)->reg[stipple].u << 1) | ((VV)->reg[stipple].u >> 31);\
+ if (((VV)->reg[stipple].u & 0x80000000) == 0) \
+ { \
+ (VV)->stats.total_stippled++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+ \
+ /* pattern mode */ \
+ else \
+ { \
+ int stipple_index = (((YY) & 3) << 3) | (~(XX) & 7); \
+ if ((((VV)->reg[stipple].u >> stipple_index) & 1) == 0) \
+ { \
+ (VV)->stats.total_stippled++; \
+ goto skipdrawdepth; \
+ } \
+ } \
+ } \
+ \
+ /* compute "floating point" W value (used for depth and fog) */ \
+ if ((ITERW) & U64(0xffff00000000)) \
+ wfloat = 0x0000; \
+ else \
+ { \
+ UINT32 temp = (UINT32)(ITERW); \
+ if (!(temp & 0xffff0000)) \
+ wfloat = 0xffff; \
+ else \
+ { \
+ int exp = count_leading_zeros(temp); \
+ wfloat = ((exp << 12) | ((~temp >> (19 - exp)) & 0xfff)) + 1; \
+ } \
+ } \
+ fogdepth = wfloat; \
+ /* add the bias for fog selection*/ \
+ if (FBZMODE_ENABLE_DEPTH_BIAS(FBZMODE)) \
+ { \
+ fogdepth += (INT16)(VV)->reg[zaColor].u; \
+ CLAMP(fogdepth, 0, 0xffff); \
+ } \
+ \
+ /* compute depth value (W or Z) for this pixel */ \
+ if (FBZMODE_WBUFFER_SELECT(FBZMODE) == 0) \
+ { \
+ CLAMPED_Z(ITERZ, FBZCOLORPATH, depthval); \
+ } \
+ else if (FBZMODE_DEPTH_FLOAT_SELECT(FBZMODE) == 0) \
+ depthval = wfloat; \
+ else \
+ { \
+ if ((ITERZ) & 0xf0000000) \
+ depthval = 0x0000; \
+ else \
+ { \
+ UINT32 temp = (ITERZ << 4); \
+ if (!(temp & 0xffff0000)) \
+ depthval = 0xffff; \
+ else \
+ { \
+ int exp = count_leading_zeros(temp); \
+ depthval = ((exp << 12) | ((~temp >> (19 - exp)) & 0xfff)) + 1; \
+ } \
+ } \
+ } \
+ /* add the bias */ \
+ biasdepth = depthval; \
+ if (FBZMODE_ENABLE_DEPTH_BIAS(FBZMODE)) \
+ { \
+ biasdepth += (INT16)(VV)->reg[zaColor].u; \
+ CLAMP(biasdepth, 0, 0xffff); \
+ }
+
+
+#define DEPTH_TEST(VV, STATS, XX, FBZMODE) \
+do \
+{ \
+ /* handle depth buffer testing */ \
+ if (FBZMODE_ENABLE_DEPTHBUF(FBZMODE)) \
+ { \
+ INT32 depthsource; \
+ \
+ /* the source depth is either the iterated W/Z+bias or a */ \
+ /* constant value */ \
+ if (FBZMODE_DEPTH_SOURCE_COMPARE(FBZMODE) == 0) \
+ depthsource = biasdepth; \
+ else \
+ depthsource = (UINT16)(VV)->reg[zaColor].u; \
+ \
+ /* test against the depth buffer */ \
+ switch (FBZMODE_DEPTH_FUNCTION(FBZMODE)) \
+ { \
+ case 0: /* depthOP = never */ \
+ (STATS)->zfunc_fail++; \
+ goto skipdrawdepth; \
+ \
+ case 1: /* depthOP = less than */ \
+ if (depthsource >= depth[XX]) \
+ { \
+ (STATS)->zfunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 2: /* depthOP = equal */ \
+ if (depthsource != depth[XX]) \
+ { \
+ (STATS)->zfunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 3: /* depthOP = less than or equal */ \
+ if (depthsource > depth[XX]) \
+ { \
+ (STATS)->zfunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 4: /* depthOP = greater than */ \
+ if (depthsource <= depth[XX]) \
+ { \
+ (STATS)->zfunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 5: /* depthOP = not equal */ \
+ if (depthsource == depth[XX]) \
+ { \
+ (STATS)->zfunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 6: /* depthOP = greater than or equal */ \
+ if (depthsource < depth[XX]) \
+ { \
+ (STATS)->zfunc_fail++; \
+ goto skipdrawdepth; \
+ } \
+ break; \
+ \
+ case 7: /* depthOP = always */ \
+ break; \
+ } \
+ } \
+} \
+while (0)
+
+INLINE bool ATTR_FORCE_INLINE depthTest(UINT16 zaColorReg, stats_block *stats, INT32 destDepth, UINT32 fbzModeReg, INT32 biasdepth)
+{
+ /* handle depth buffer testing */
+ if (FBZMODE_ENABLE_DEPTHBUF(fbzModeReg))
+ {
+ INT32 depthsource;
+
+ /* the source depth is either the iterated W/Z+bias or a */
+ /* constant value */
+ if (FBZMODE_DEPTH_SOURCE_COMPARE(fbzModeReg) == 0)
+ depthsource = biasdepth;
+ else
+ depthsource = zaColorReg;
+
+ /* test against the depth buffer */
+ switch (FBZMODE_DEPTH_FUNCTION(fbzModeReg))
+ {
+ case 0: /* depthOP = never */
+ stats->zfunc_fail++;
+ return false;
+
+ case 1: /* depthOP = less than */
+ if (depthsource >= destDepth)
+ {
+ stats->zfunc_fail++;
+ return false;
+ }
+ break;
+
+ case 2: /* depthOP = equal */
+ if (depthsource != destDepth)
+ {
+ stats->zfunc_fail++;
+ return false;
+ }
+ break;
+
+ case 3: /* depthOP = less than or equal */
+ if (depthsource > destDepth)
+ {
+ stats->zfunc_fail++;
+ return false;
+ }
+ break;
+
+ case 4: /* depthOP = greater than */
+ if (depthsource <= destDepth)
+ {
+ stats->zfunc_fail++;
+ return false;
+ }
+ break;
+
+ case 5: /* depthOP = not equal */
+ if (depthsource == destDepth)
+ {
+ stats->zfunc_fail++;
+ return false;
+ }
+ break;
+
+ case 6: /* depthOP = greater than or equal */
+ if (depthsource < destDepth)
+ {
+ stats->zfunc_fail++;
+ return false;
+ }
+ break;
+
+ case 7: /* depthOP = always */
+ break;
+ }
+ }
+ return true;
+}
+
+#if USE_OLD_RASTER == 1
+#define PIXEL_PIPELINE_END(VV, STATS, DITHER, DITHER4, DITHER_LOOKUP, XX, dest, depth, FBZMODE, FBZCOLORPATH, ALPHAMODE, FOGMODE, ITERZ, ITERW, ITERAXXX) \
+ \
+ /* perform fogging */ \
+ INT32 prefogr, prefogg, prefogb; \
+ prefogr = r; \
+ prefogg = g; \
+ prefogb = b; \
+ APPLY_FOGGING(VV, FOGMODE, FBZCOLORPATH, XX, DITHER4, r, g, b, \
+ ITERZ, ITERW, ITERAXXX); \
+ \
+ /* perform alpha blending */ \
+ APPLY_ALPHA_BLEND(FBZMODE, ALPHAMODE, XX, DITHER, r, g, b, a); \
+ /* modify the pixel for debugging purposes */ \
+ MODIFY_PIXEL(VV); \
+ \
+ /* write to framebuffer */ \
+ if (FBZMODE_RGB_BUFFER_MASK(FBZMODE)) \
+ { \
+ /* apply dithering */ \
+ APPLY_DITHER(FBZMODE, XX, DITHER_LOOKUP, r, g, b); \
+ dest[XX] = (r << 11) | (g << 5) | b; \
+ } \
+ \
+ /* write to aux buffer */ \
+ if (depth && FBZMODE_AUX_BUFFER_MASK(FBZMODE)) \
+ { \
+ if (FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) == 0) \
+ depth[XX] = biasdepth; \
+ else \
+ depth[XX] = a; \
+ } \
+ \
+ /* track pixel writes to the frame buffer regardless of mask */ \
+ (STATS)->pixels_out++; \
+ \
+skipdrawdepth: \
+ ; \
+} \
+while (0)
+#else
+#define PIXEL_PIPELINE_END(VV, STATS, DITHER, DITHER4, DITHER_LOOKUP, XX, dest, depth, FBZMODE, FBZCOLORPATH, ALPHAMODE, FOGMODE, ITERZ, ITERW, ITERAXXX) \
+ \
+ /* perform fogging */ \
+ preFog.set(color); \
+ applyFogging(VV, FOGMODE, FBZCOLORPATH, XX, DITHER4, fogdepth, color, ITERZ, ITERW, ITERAXXX.get_a()); \
+ /* perform alpha blending */ \
+ alphaBlend(FBZMODE, ALPHAMODE, XX, DITHER, dest[XX], depth, preFog, color); \
+ a = color.get_a(); r = color.get_r(); g = color.get_g(); b = color.get_b(); \
+ /* modify the pixel for debugging purposes */ \
+ MODIFY_PIXEL(VV); \
+ \
+ /* write to framebuffer */ \
+ if (FBZMODE_RGB_BUFFER_MASK(FBZMODE)) \
+ { \
+ /* apply dithering */ \
+ APPLY_DITHER(FBZMODE, XX, DITHER_LOOKUP, r, g, b); \
+ dest[XX] = (r << 11) | (g << 5) | b; \
+ } \
+ \
+ /* write to aux buffer */ \
+ if (depth && FBZMODE_AUX_BUFFER_MASK(FBZMODE)) \
+ { \
+ if (FBZMODE_ENABLE_ALPHA_PLANES(FBZMODE) == 0) \
+ depth[XX] = biasdepth; \
+ else \
+ depth[XX] = a; \
+ } \
+ \
+ /* track pixel writes to the frame buffer regardless of mask */ \
+ (STATS)->pixels_out++; \
+ \
+skipdrawdepth: \
+ ; \
+} \
+while (0)
+#endif
+
+
+/*************************************
+ *
+ * Colorpath pipeline macro
+ *
+ *************************************/
+
+/*
+
+ c_other_is_used:
+
+ if (FBZMODE_ENABLE_CHROMAKEY(FBZMODE) ||
+ FBZCP_CC_ZERO_OTHER(FBZCOLORPATH) == 0)
+
+ c_local_is_used:
+
+ if (FBZCP_CC_SUB_CLOCAL(FBZCOLORPATH) ||
+ FBZCP_CC_MSELECT(FBZCOLORPATH) == 1 ||
+ FBZCP_CC_ADD_ACLOCAL(FBZCOLORPATH) == 1)
+
+ NEEDS_ITER_RGB:
+
+ if ((c_other_is_used && FBZCP_CC_RGBSELECT(FBZCOLORPATH) == 0) ||
+ (c_local_is_used && (FBZCP_CC_LOCALSELECT_OVERRIDE(FBZCOLORPATH) != 0 || FBZCP_CC_LOCALSELECT(FBZCOLORPATH) == 0))
+
+ NEEDS_ITER_A:
+
+ if ((a_other_is_used && FBZCP_CC_ASELECT(FBZCOLORPATH) == 0) ||
+ (a_local_is_used && FBZCP_CCA_LOCALSELECT(FBZCOLORPATH) == 0))
+
+ NEEDS_ITER_Z:
+
+ if (FBZMODE_WBUFFER_SELECT(FBZMODE) == 0 ||
+ FBZMODE_DEPTH_FLOAT_SELECT(FBZMODE) != 0 ||
+ FBZCP_CCA_LOCALSELECT(FBZCOLORPATH) == 2)
+
+
+*/
+
+/*
+ Expects the following declarations to be outside of this scope:
+
+ INT32 r, g, b, a;
+*/
+#define COLORPATH_PIPELINE(VV, STATS, FBZCOLORPATH, FBZMODE, ALPHAMODE, TEXELARGB, ITERZ, ITERW, ITERARGB) \
+do \
+{ \
+ INT32 blendr, blendg, blendb, blenda; \
+ rgb_union c_other; \
+ rgb_union c_local; \
+ \
+ /* compute c_other */ \
+ switch (FBZCP_CC_RGBSELECT(FBZCOLORPATH)) \
+ { \
+ case 0: /* iterated RGB */ \
+ c_other.u = ITERARGB.u; \
+ break; \
+ \
+ case 1: /* texture RGB */ \
+ c_other.u = TEXELARGB.u; \
+ break; \
+ \
+ case 2: /* color1 RGB */ \
+ c_other.u = (VV)->reg[color1].u; \
+ break; \
+ \
+ default: /* reserved - voodoo3 framebufferRGB */ \
+ c_other.u = 0; \
+ break; \
+ } \
+ \
+ /* handle chroma key */ \
+ APPLY_CHROMAKEY(VV, STATS, FBZMODE, c_other); \
+ \
+ /* compute a_other */ \
+ switch (FBZCP_CC_ASELECT(FBZCOLORPATH)) \
+ { \
+ case 0: /* iterated alpha */ \
+ c_other.rgb.a = ITERARGB.rgb.a; \
+ break; \
+ \
+ case 1: /* texture alpha */ \
+ c_other.rgb.a = TEXELARGB.rgb.a; \
+ break; \
+ \
+ case 2: /* color1 alpha */ \
+ c_other.rgb.a = (VV)->reg[color1].rgb.a; \
+ break; \
+ \
+ default: /* reserved */ \
+ c_other.rgb.a = 0; \
+ break; \
+ } \
+ \
+ /* handle alpha mask */ \
+ APPLY_ALPHAMASK(VV, STATS, FBZMODE, c_other.rgb.a); \
+ \
+ /* compute c_local */ \
+ if (FBZCP_CC_LOCALSELECT_OVERRIDE(FBZCOLORPATH) == 0) \
+ { \
+ if (FBZCP_CC_LOCALSELECT(FBZCOLORPATH) == 0) /* iterated RGB */ \
+ c_local.u = ITERARGB.u; \
+ else /* color0 RGB */ \
+ c_local.u = (VV)->reg[color0].u; \
+ } \
+ else \
+ { \
+ if (!(TEXELARGB.rgb.a & 0x80)) /* iterated RGB */ \
+ c_local.u = ITERARGB.u; \
+ else /* color0 RGB */ \
+ c_local.u = (VV)->reg[color0].u; \
+ } \
+ \
+ /* compute a_local */ \
+ switch (FBZCP_CCA_LOCALSELECT(FBZCOLORPATH)) \
+ { \
+ default: \
+ case 0: /* iterated alpha */ \
+ c_local.rgb.a = ITERARGB.rgb.a; \
+ break; \
+ \
+ case 1: /* color0 alpha */ \
+ c_local.rgb.a = (VV)->reg[color0].rgb.a; \
+ break; \
+ \
+ case 2: /* clamped iterated Z[27:20] */ \
+ { \
+ int temp; \
+ CLAMPED_Z(ITERZ, FBZCOLORPATH, temp); \
+ c_local.rgb.a = (UINT8)temp; \
+ break; \
+ } \
+ \
+ case 3: /* clamped iterated W[39:32] */ \
+ { \
+ int temp; \
+ CLAMPED_W(ITERW, FBZCOLORPATH, temp); /* Voodoo 2 only */ \
+ c_local.rgb.a = (UINT8)temp; \
+ break; \
+ } \
+ } \
+ \
+ /* select zero or a_other */ \
+ if (!FBZCP_CCA_ZERO_OTHER(FBZCOLORPATH)) \
+ a = c_other.rgb.a; \
+ else \
+ a = 0; \
+ \
+ /* subtract a_local */ \
+ if (FBZCP_CCA_SUB_CLOCAL(FBZCOLORPATH)) \
+ a -= c_local.rgb.a; \
+ \
+ /* blend alpha */ \
+ switch (FBZCP_CCA_MSELECT(FBZCOLORPATH)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* 0 */ \
+ blenda = 0; \
+ break; \
+ \
+ case 1: /* a_local */ \
+ blenda = c_local.rgb.a; \
+ break; \
+ \
+ case 2: /* a_other */ \
+ blenda = c_other.rgb.a; \
+ break; \
+ \
+ case 3: /* a_local */ \
+ blenda = c_local.rgb.a; \
+ break; \
+ \
+ case 4: /* texture alpha */ \
+ blenda = TEXELARGB.rgb.a; \
+ break; \
+ } \
+ \
+ /* reverse the alpha blend */ \
+ if (!FBZCP_CCA_REVERSE_BLEND(FBZCOLORPATH)) \
+ blenda ^= 0xff; \
+ \
+ /* do the blend */ \
+ a = (a * (blenda + 1)) >> 8; \
+ \
+ /* add clocal or alocal to alpha */ \
+ if (FBZCP_CCA_ADD_ACLOCAL(FBZCOLORPATH)) \
+ a += c_local.rgb.a; \
+ \
+ /* clamp */ \
+ CLAMP(a, 0x00, 0xff); \
+ \
+ /* invert */ \
+ if (FBZCP_CCA_INVERT_OUTPUT(FBZCOLORPATH)) \
+ a ^= 0xff; \
+ \
+ /* handle alpha test */ \
+ APPLY_ALPHATEST(VV, STATS, ALPHAMODE, a); \
+ \
+ \
+ /* select zero or c_other */ \
+ if (FBZCP_CC_ZERO_OTHER(FBZCOLORPATH) == 0) \
+ { \
+ r = c_other.rgb.r; \
+ g = c_other.rgb.g; \
+ b = c_other.rgb.b; \
+ } \
+ else \
+ r = g = b = 0; \
+ \
+ /* subtract c_local */ \
+ if (FBZCP_CC_SUB_CLOCAL(FBZCOLORPATH)) \
+ { \
+ r -= c_local.rgb.r; \
+ g -= c_local.rgb.g; \
+ b -= c_local.rgb.b; \
+ } \
+ \
+ /* blend RGB */ \
+ switch (FBZCP_CC_MSELECT(FBZCOLORPATH)) \
+ { \
+ default: /* reserved */ \
+ case 0: /* 0 */ \
+ blendr = blendg = blendb = 0; \
+ break; \
+ \
+ case 1: /* c_local */ \
+ blendr = c_local.rgb.r; \
+ blendg = c_local.rgb.g; \
+ blendb = c_local.rgb.b; \
+ break; \
+ \
+ case 2: /* a_other */ \
+ blendr = blendg = blendb = c_other.rgb.a; \
+ break; \
+ \
+ case 3: /* a_local */ \
+ blendr = blendg = blendb = c_local.rgb.a; \
+ break; \
+ \
+ case 4: /* texture alpha */ \
+ blendr = blendg = blendb = TEXELARGB.rgb.a; \
+ break; \
+ \
+ case 5: /* texture RGB (Voodoo 2 only) */ \
+ blendr = TEXELARGB.rgb.r; \
+ blendg = TEXELARGB.rgb.g; \
+ blendb = TEXELARGB.rgb.b; \
+ break; \
+ } \
+ \
+ /* reverse the RGB blend */ \
+ if (!FBZCP_CC_REVERSE_BLEND(FBZCOLORPATH)) \
+ { \
+ blendr ^= 0xff; \
+ blendg ^= 0xff; \
+ blendb ^= 0xff; \
+ } \
+ \
+ /* do the blend */ \
+ r = (r * (blendr + 1)) >> 8; \
+ g = (g * (blendg + 1)) >> 8; \
+ b = (b * (blendb + 1)) >> 8; \
+ \
+ /* add clocal or alocal to RGB */ \
+ switch (FBZCP_CC_ADD_ACLOCAL(FBZCOLORPATH)) \
+ { \
+ case 3: /* reserved */ \
+ case 0: /* nothing */ \
+ break; \
+ \
+ case 1: /* add c_local */ \
+ r += c_local.rgb.r; \
+ g += c_local.rgb.g; \
+ b += c_local.rgb.b; \
+ break; \
+ \
+ case 2: /* add_alocal */ \
+ r += c_local.rgb.a; \
+ g += c_local.rgb.a; \
+ b += c_local.rgb.a; \
+ break; \
+ } \
+ \
+ /* clamp */ \
+ CLAMP(r, 0x00, 0xff); \
+ CLAMP(g, 0x00, 0xff); \
+ CLAMP(b, 0x00, 0xff); \
+ \
+ /* invert */ \
+ if (FBZCP_CC_INVERT_OUTPUT(FBZCOLORPATH)) \
+ { \
+ r ^= 0xff; \
+ g ^= 0xff; \
+ b ^= 0xff; \
+ } \
+} \
+while (0)
+
+INLINE bool ATTR_FORCE_INLINE combineColor(voodoo_state *VV, stats_block *STATS, UINT32 FBZCOLORPATH, UINT32 FBZMODE, UINT32 ALPHAMODE,
+ rgbaint_t TEXELARGB, INT32 ITERZ, INT64 ITERW, rgbaint_t &srcColor)
+{
+ rgbaint_t c_other;
+ rgbaint_t c_local;
+
+ /* compute c_other */
+ switch (FBZCP_CC_RGBSELECT(FBZCOLORPATH))
+ {
+ case 0: /* iterated RGB */
+ c_other.set(srcColor);
+ break;
+
+ case 1: /* texture RGB */
+ c_other.set(TEXELARGB);
+ break;
+
+ case 2: /* color1 RGB */
+ c_other.set((VV)->reg[color1].u);
+ break;
+
+ default: /* reserved - voodoo3 framebufferRGB */
+ c_other.set(0);
+ break;
+ }
+
+ /* handle chroma key */
+ if (!chromaKeyTest(VV, STATS, FBZMODE, c_other))
+ return false;
+ //APPLY_CHROMAKEY(VV, STATS, FBZMODE, c_other);
+
+ /* compute a_other */
+ switch (FBZCP_CC_ASELECT(FBZCOLORPATH))
+ {
+ case 0: /* iterated alpha */
+ c_other.merge_alpha(srcColor);
+ break;
+
+ case 1: /* texture alpha */
+ c_other.merge_alpha(TEXELARGB);
+ break;
+
+ case 2: /* color1 alpha */
+ c_other.set_a((VV)->reg[color1].rgb.a);
+ break;
+
+ default: /* reserved */
+ c_other.set_a(0);
+ break;
+ }
+
+ /* handle alpha mask */
+ if (!alphaMaskTest(STATS, FBZMODE, c_other.get_a()))
+ return false;
+ //APPLY_ALPHAMASK(VV, STATS, FBZMODE, c_other.rgb.a);
+
+
+ /* compute c_local */
+ if (FBZCP_CC_LOCALSELECT_OVERRIDE(FBZCOLORPATH) == 0)
+ {
+ if (FBZCP_CC_LOCALSELECT(FBZCOLORPATH) == 0) /* iterated RGB */
+ c_local.set(srcColor);
+ else /* color0 RGB */
+ c_local.set((VV)->reg[color0].u);
+ }
+ else
+ {
+ if (!(TEXELARGB.get_a() & 0x80)) /* iterated RGB */
+ c_local.set(srcColor);
+ else /* color0 RGB */
+ c_local.set((VV)->reg[color0].u);
+ }
+
+ /* compute a_local */
+ switch (FBZCP_CCA_LOCALSELECT(FBZCOLORPATH))
+ {
+ default:
+ case 0: /* iterated alpha */
+ c_local.merge_alpha(srcColor);
+ break;
+
+ case 1: /* color0 alpha */
+ c_local.set_a((VV)->reg[color0].rgb.a);
+ break;
+
+ case 2: /* clamped iterated Z[27:20] */
+ {
+ int temp;
+ CLAMPED_Z(ITERZ, FBZCOLORPATH, temp);
+ c_local.set_a((UINT8) temp);
+ break;
+ }
+
+ case 3: /* clamped iterated W[39:32] */
+ {
+ int temp;
+ CLAMPED_W(ITERW, FBZCOLORPATH, temp); /* Voodoo 2 only */
+ c_local.set_a((UINT8) temp);
+ break;
+ }
+ }
+
+ UINT8 a_other = c_other.get_a();
+ UINT8 a_local = c_local.get_a();
+ UINT8 tmp;
+ rgbaint_t add_val(c_local);
+
+ /* select zero or c_other */
+ if (FBZCP_CC_ZERO_OTHER(FBZCOLORPATH))
+ c_other.and_imm_rgba(-1, 0, 0, 0);
+ //r = g = b = 0;
+
+ /* select zero or a_other */
+ if (FBZCP_CCA_ZERO_OTHER(FBZCOLORPATH))
+ c_other.set_a(0);
+
+ /* subtract a/c_local */
+ if (FBZCP_CC_SUB_CLOCAL(FBZCOLORPATH) || (FBZCP_CCA_SUB_CLOCAL(FBZCOLORPATH)))
+ {
+ rgbaint_t sub_val = c_local;
+
+ if (!FBZCP_CC_SUB_CLOCAL(FBZCOLORPATH))
+ sub_val.set(a_local, 0, 0, 0);
+
+ if (!FBZCP_CCA_SUB_CLOCAL(FBZCOLORPATH))
+ sub_val.set_a(0);
+
+ c_other.sub(sub_val);
+ }
+
+ /* blend RGB */
+ switch (FBZCP_CC_MSELECT(FBZCOLORPATH))
+ {
+ default: /* reserved */
+ case 0: /* 0 */
+ c_local.and_imm_rgba(-1, 0, 0, 0);
+ break;
+
+ case 1: /* c_local */
+ break;
+
+ case 2: /* a_other */
+ c_local.set(a_local, a_other, a_other, a_other);
+ break;
+
+ case 3: /* a_local */
+ c_local.set(a_local, a_local, a_local, a_local);
+ break;
+
+ case 4: /* texture alpha */
+ tmp = TEXELARGB.get_a();
+ c_local.set(a_local, tmp, tmp, tmp);
+ break;
+
+ case 5: /* texture RGB (Voodoo 2 only) */
+ c_local.set(TEXELARGB);
+ break;
+ }
+
+ /* blend alpha */
+ switch (FBZCP_CCA_MSELECT(FBZCOLORPATH))
+ {
+ default: /* reserved */
+ case 0: /* 0 */
+ c_local.set_a(0);
+ break;
+
+ case 1: /* a_local */
+ case 3: /* a_local */
+ c_local.set_a(a_local);
+ break;
+
+ case 2: /* a_other */
+ c_local.set_a(a_other);
+ break;
+
+ case 4: /* texture alpha */
+ c_local.merge_alpha(TEXELARGB);
+ break;
+ }
+
+ /* reverse the RGB blend */
+ if (!FBZCP_CC_REVERSE_BLEND(FBZCOLORPATH))
+ c_local.xor_imm_rgba(0, 0xff, 0xff, 0xff);
+
+ /* reverse the alpha blend */
+ if (!FBZCP_CCA_REVERSE_BLEND(FBZCOLORPATH))
+ c_local.xor_imm_rgba(0xff, 0, 0, 0);
+
+ /* do the blend */
+ //color.rgb.a = (color.rgb.a * (blenda + 1)) >> 8;
+ //color.rgb.r = (color.rgb.r * (blendr + 1)) >> 8;
+ //color.rgb.g = (color.rgb.g * (blendg + 1)) >> 8;
+ //color.rgb.b = (color.rgb.b * (blendb + 1)) >> 8;
+
+ /* add clocal or alocal to RGB */
+ switch (FBZCP_CC_ADD_ACLOCAL(FBZCOLORPATH))
+ {
+ case 3: /* reserved */
+ case 0: /* nothing */
+ add_val.set(a_local, 0, 0, 0);
+ break;
+
+ case 1: /* add c_local */
+ break;
+
+ case 2: /* add_alocal */
+ add_val.set(a_local, a_local, a_local, a_local);
+ break;
+ }
+
+ /* add clocal or alocal to alpha */
+ if (!FBZCP_CCA_ADD_ACLOCAL(FBZCOLORPATH))
+ add_val.set_a(0);
+ //color.rgb.a += c_local.rgb.a;
+
+ /* clamp */
+ //CLAMP(color.rgb.a, 0x00, 0xff);
+ //CLAMP(color.rgb.r, 0x00, 0xff);
+ //CLAMP(color.rgb.g, 0x00, 0xff);
+ //CLAMP(color.rgb.b, 0x00, 0xff);
+ c_local.add_imm(1);
+ c_other.scale_add_and_clamp(c_local, add_val);
+ srcColor.set(c_other);
+
+ /* invert */
+ if (FBZCP_CCA_INVERT_OUTPUT(FBZCOLORPATH))
+ srcColor.xor_imm_rgba(0xff, 0, 0, 0);
+ /* invert */
+ if (FBZCP_CC_INVERT_OUTPUT(FBZCOLORPATH))
+ srcColor.xor_imm_rgba(0, 0xff, 0xff, 0xff);
+
+
+ /* handle alpha test */
+ if (!alphaTest(VV, STATS, ALPHAMODE, srcColor.get_a()))
+ return false;
+ //APPLY_ALPHATEST(VV, STATS, ALPHAMODE, color.rgb.a);
+
+ return true;
+}
+
+
+
+/*************************************
+ *
+ * Rasterizer generator macro
+ *
+ *************************************/
+#if USE_OLD_RASTER == 1
+#define RASTERIZER(name, TMUS, FBZCOLORPATH, FBZMODE, ALPHAMODE, FOGMODE, TEXMODE0, TEXMODE1) \
+ \
+static void raster_##name(void *destbase, INT32 y, const poly_extent *extent, const void *extradata, int threadid) \
+{ \
+ const poly_extra_data *extra = (const poly_extra_data *)extradata; \
+ voodoo_state *v = extra->state; \
+ stats_block *stats = &v->thread_stats[threadid]; \
+ DECLARE_DITHER_POINTERS; \
+ INT32 startx = extent->startx; \
+ INT32 stopx = extent->stopx; \
+ INT32 iterr, iterg, iterb, itera; \
+ INT32 iterz; \
+ INT64 iterw, iterw0 = 0, iterw1 = 0; \
+ INT64 iters0 = 0, iters1 = 0; \
+ INT64 itert0 = 0, itert1 = 0; \
+ UINT16 *depth; \
+ UINT16 *dest; \
+ INT32 dx, dy; \
+ INT32 scry; \
+ INT32 x; \
+ \
+ /* determine the screen Y */ \
+ scry = y; \
+ if (FBZMODE_Y_ORIGIN(FBZMODE)) \
+ scry = (v->fbi.yorigin - y) & 0x3ff; \
+ \
+ /* compute dithering */ \
+ COMPUTE_DITHER_POINTERS(FBZMODE, y); \
+ \
+ /* apply clipping */ \
+ if (FBZMODE_ENABLE_CLIPPING(FBZMODE)) \
+ { \
+ INT32 tempclip; \
+ \
+ /* Y clipping buys us the whole scanline */ \
+ if (scry < ((v->reg[clipLowYHighY].u >> 16) & 0x3ff) || \
+ scry >= (v->reg[clipLowYHighY].u & 0x3ff)) \
+ { \
+ stats->pixels_in += stopx - startx; \
+ stats->clip_fail += stopx - startx; \
+ return; \
+ } \
+ \
+ /* X clipping */ \
+ tempclip = (v->reg[clipLeftRight].u >> 16) & 0x3ff; \
+ if (startx < tempclip) \
+ { \
+ stats->pixels_in += tempclip - startx; \
+ v->stats.total_clipped += tempclip - startx; \
+ startx = tempclip; \
+ } \
+ tempclip = v->reg[clipLeftRight].u & 0x3ff; \
+ if (stopx >= tempclip) \
+ { \
+ stats->pixels_in += stopx - tempclip; \
+ v->stats.total_clipped += stopx - tempclip; \
+ stopx = tempclip - 1; \
+ } \
+ } \
+ \
+ /* get pointers to the target buffer and depth buffer */ \
+ dest = (UINT16 *)destbase + scry * v->fbi.rowpixels; \
+ depth = (v->fbi.auxoffs != ~0) ? ((UINT16 *)(v->fbi.ram + v->fbi.auxoffs) + scry * v->fbi.rowpixels) : NULL; \
+ \
+ /* compute the starting parameters */ \
+ dx = startx - (extra->ax >> 4); \
+ dy = y - (extra->ay >> 4); \
+ iterr = extra->startr + dy * extra->drdy + dx * extra->drdx; \
+ iterg = extra->startg + dy * extra->dgdy + dx * extra->dgdx; \
+ iterb = extra->startb + dy * extra->dbdy + dx * extra->dbdx; \
+ itera = extra->starta + dy * extra->dady + dx * extra->dadx; \
+ iterz = extra->startz + dy * extra->dzdy + dx * extra->dzdx; \
+ iterw = extra->startw + dy * extra->dwdy + dx * extra->dwdx; \
+ if (TMUS >= 1) \
+ { \
+ iterw0 = extra->startw0 + dy * extra->dw0dy + dx * extra->dw0dx; \
+ iters0 = extra->starts0 + dy * extra->ds0dy + dx * extra->ds0dx; \
+ itert0 = extra->startt0 + dy * extra->dt0dy + dx * extra->dt0dx; \
+ } \
+ if (TMUS >= 2) \
+ { \
+ iterw1 = extra->startw1 + dy * extra->dw1dy + dx * extra->dw1dx; \
+ iters1 = extra->starts1 + dy * extra->ds1dy + dx * extra->ds1dx; \
+ itert1 = extra->startt1 + dy * extra->dt1dy + dx * extra->dt1dx; \
+ } \
+ extra->info->hits++; \
+ /* loop in X */ \
+ for (x = startx; x < stopx; x++) \
+ { \
+ rgb_union iterargb; \
+ rgb_union texel = { 0 }; \
+ \
+ /* pixel pipeline part 1 handles depth setup and stippling */ \
+ PIXEL_PIPELINE_BEGIN(v, stats, x, y, FBZCOLORPATH, FBZMODE, iterz, iterw); \
+ DEPTH_TEST(v, stats, x, FBZMODE); \
+ \
+ /* run the texture pipeline on TMU1 to produce a value in texel */ \
+ /* note that they set LOD min to 8 to "disable" a TMU */ \
+ if (TMUS >= 2 && v->tmu[1].lodmin < (8 << 8)) \
+ TEXTURE_PIPELINE(&v->tmu[1], x, dither4, TEXMODE1, texel, \
+ v->tmu[1].lookup, extra->lodbase1, \
+ iters1, itert1, iterw1, texel); \
+ \
+ /* run the texture pipeline on TMU0 to produce a final */ \
+ /* result in texel */ \
+ /* note that they set LOD min to 8 to "disable" a TMU */ \
+ if (TMUS >= 1 && v->tmu[0].lodmin < (8 << 8)) \
+ { \
+ if (!v->send_config) \
+ TEXTURE_PIPELINE(&v->tmu[0], x, dither4, TEXMODE0, texel, \
+ v->tmu[0].lookup, extra->lodbase0, \
+ iters0, itert0, iterw0, texel); \
+ else \
+ texel.u = v->tmu_config; \
+ } \
+ /* colorpath pipeline selects source colors and does blending */ \
+ CLAMPED_ARGB(iterr, iterg, iterb, itera, FBZCOLORPATH, iterargb); \
+ COLORPATH_PIPELINE(v, stats, FBZCOLORPATH, FBZMODE, ALPHAMODE, texel, \
+ iterz, iterw, iterargb); \
+ \
+ /* pixel pipeline part 2 handles fog, alpha, and final output */ \
+ PIXEL_PIPELINE_END(v, stats, dither, dither4, dither_lookup, x, dest, depth, \
+ FBZMODE, FBZCOLORPATH, ALPHAMODE, FOGMODE, \
+ iterz, iterw, iterargb); \
+ \
+ /* update the iterated parameters */ \
+ iterr += extra->drdx; \
+ iterg += extra->dgdx; \
+ iterb += extra->dbdx; \
+ itera += extra->dadx; \
+ iterz += extra->dzdx; \
+ iterw += extra->dwdx; \
+ if (TMUS >= 1) \
+ { \
+ iterw0 += extra->dw0dx; \
+ iters0 += extra->ds0dx; \
+ itert0 += extra->dt0dx; \
+ } \
+ if (TMUS >= 2) \
+ { \
+ iterw1 += extra->dw1dx; \
+ iters1 += extra->ds1dx; \
+ itert1 += extra->dt1dx; \
+ } \
+ } \
+}
+#else
+// New rasterizer implementation
+#define RASTERIZER(name, TMUS, FBZCOLORPATH, FBZMODE, ALPHAMODE, FOGMODE, TEXMODE0, TEXMODE1) \
+ \
+static void raster_##name(void *destbase, INT32 y, const poly_extent *extent, const void *extradata, int threadid) \
+{ \
+ const poly_extra_data *extra = (const poly_extra_data *)extradata; \
+ voodoo_state *v = extra->state; \
+ stats_block *stats = &v->thread_stats[threadid]; \
+ DECLARE_DITHER_POINTERS; \
+ INT32 startx = extent->startx; \
+ INT32 stopx = extent->stopx; \
+ rgbaint_t iterargb, iterargbDelta; \
+ INT32 iterz; \
+ INT64 iterw, iterw0 = 0, iterw1 = 0; \
+ INT64 iters0 = 0, iters1 = 0; \
+ INT64 itert0 = 0, itert1 = 0; \
+ UINT16 *depth; \
+ UINT16 *dest; \
+ INT32 dx, dy; \
+ INT32 scry; \
+ INT32 x; \
+ \
+ /* determine the screen Y */ \
+ scry = y; \
+ if (FBZMODE_Y_ORIGIN(FBZMODE)) \
+ scry = (v->fbi.yorigin - y) & 0x3ff; \
+ \
+ /* compute dithering */ \
+ COMPUTE_DITHER_POINTERS(FBZMODE, y); \
+ \
+ /* apply clipping */ \
+ if (FBZMODE_ENABLE_CLIPPING(FBZMODE)) \
+ { \
+ INT32 tempclip; \
+ \
+ /* Y clipping buys us the whole scanline */ \
+ if (scry < ((v->reg[clipLowYHighY].u >> 16) & 0x3ff) || \
+ scry >= (v->reg[clipLowYHighY].u & 0x3ff)) \
+ { \
+ stats->pixels_in += stopx - startx; \
+ stats->clip_fail += stopx - startx; \
+ return; \
+ } \
+ \
+ /* X clipping */ \
+ tempclip = (v->reg[clipLeftRight].u >> 16) & 0x3ff; \
+ if (startx < tempclip) \
+ { \
+ stats->pixels_in += tempclip - startx; \
+ v->stats.total_clipped += tempclip - startx; \
+ startx = tempclip; \
+ } \
+ tempclip = v->reg[clipLeftRight].u & 0x3ff; \
+ if (stopx >= tempclip) \
+ { \
+ stats->pixels_in += stopx - tempclip; \
+ v->stats.total_clipped += stopx - tempclip; \
+ stopx = tempclip - 1; \
+ } \
+ } \
+ \
+ /* get pointers to the target buffer and depth buffer */ \
+ dest = (UINT16 *)destbase + scry * v->fbi.rowpixels; \
+ depth = (v->fbi.auxoffs != ~0) ? ((UINT16 *)(v->fbi.ram + v->fbi.auxoffs) + scry * v->fbi.rowpixels) : NULL; \
+ \
+ /* compute the starting parameters */ \
+ dx = startx - (extra->ax >> 4); \
+ dy = y - (extra->ay >> 4); \
+ INT32 iterr = extra->startr + dy * extra->drdy + dx * extra->drdx; \
+ INT32 iterg = extra->startg + dy * extra->dgdy + dx * extra->dgdx; \
+ INT32 iterb = extra->startb + dy * extra->dbdy + dx * extra->dbdx; \
+ INT32 itera = extra->starta + dy * extra->dady + dx * extra->dadx; \
+ iterargb.set(itera, iterr, iterg, iterb); \
+ iterargbDelta.set(extra->dadx, extra->drdx, extra->dgdx, extra->dbdx); \
+ iterz = extra->startz + dy * extra->dzdy + dx * extra->dzdx; \
+ iterw = extra->startw + dy * extra->dwdy + dx * extra->dwdx; \
+ if (TMUS >= 1) \
+ { \
+ iterw0 = extra->startw0 + dy * extra->dw0dy + dx * extra->dw0dx; \
+ iters0 = extra->starts0 + dy * extra->ds0dy + dx * extra->ds0dx; \
+ itert0 = extra->startt0 + dy * extra->dt0dy + dx * extra->dt0dx; \
+ } \
+ if (TMUS >= 2) \
+ { \
+ iterw1 = extra->startw1 + dy * extra->dw1dy + dx * extra->dw1dx; \
+ iters1 = extra->starts1 + dy * extra->ds1dy + dx * extra->ds1dx; \
+ itert1 = extra->startt1 + dy * extra->dt1dy + dx * extra->dt1dx; \
+ } \
+ extra->info->hits++; \
+ /* loop in X */ \
+ for (x = startx; x < stopx; x++) \
+ { \
+ rgbaint_t texel(0); \
+ rgbaint_t color, preFog; \
+ \
+ /* pixel pipeline part 1 handles depth setup and stippling */ \
+ PIXEL_PIPELINE_BEGIN(v, stats, x, y, FBZCOLORPATH, FBZMODE, iterz, iterw); \
+ /* depth testing */ \
+ if (!depthTest((UINT16) v->reg[zaColor].u, stats, depth[x], FBZMODE, biasdepth)) \
+ goto skipdrawdepth; \
+ \
+ /* run the texture pipeline on TMU1 to produce a value in texel */ \
+ /* note that they set LOD min to 8 to "disable" a TMU */ \
+ if (TMUS >= 2 && v->tmu[1].lodmin < (8 << 8)) { \
+ INT32 tmp; \
+ const rgbaint_t texelZero(0); \
+ texel = genTexture(&v->tmu[1], x, dither4, TEXMODE1, v->tmu[1].lookup, extra->lodbase1, \
+ iters1, itert1, iterw1, tmp); \
+ texel = combineTexture(&v->tmu[1], TEXMODE1, texel, texelZero, tmp); \
+ } \
+ /* run the texture pipeline on TMU0 to produce a final */ \
+ /* result in texel */ \
+ /* note that they set LOD min to 8 to "disable" a TMU */ \
+ if (TMUS >= 1 && v->tmu[0].lodmin < (8 << 8)) \
+ { \
+ if (!v->send_config) \
+ { \
+ INT32 lod0; \
+ rgbaint_t texelT0; \
+ texelT0 = genTexture(&v->tmu[0], x, dither4, TEXMODE0, v->tmu[0].lookup, extra->lodbase0, \
+ iters0, itert0, iterw0, lod0); \
+ texel = combineTexture(&v->tmu[0], TEXMODE0, texelT0, texel, lod0); \
+ } \
+ else \
+ { \
+ texel.set(v->tmu_config); \
+ } \
+ } \
+ \
+ /* colorpath pipeline selects source colors and does blending */ \
+ color = clampARGB(iterargb, FBZCOLORPATH); \
+ if (!combineColor(v, stats, FBZCOLORPATH, FBZMODE, ALPHAMODE, texel, iterz, iterw, color)) \
+ goto skipdrawdepth; \
+ \
+ /* pixel pipeline part 2 handles fog, alpha, and final output */ \
+ PIXEL_PIPELINE_END(v, stats, dither, dither4, dither_lookup, x, dest, depth, \
+ FBZMODE, FBZCOLORPATH, ALPHAMODE, FOGMODE, \
+ iterz, iterw, iterargb); \
+ \
+ /* update the iterated parameters */ \
+ iterargb += iterargbDelta; \
+ iterz += extra->dzdx; \
+ iterw += extra->dwdx; \
+ if (TMUS >= 1) \
+ { \
+ iterw0 += extra->dw0dx; \
+ iters0 += extra->ds0dx; \
+ itert0 += extra->dt0dx; \
+ } \
+ if (TMUS >= 2) \
+ { \
+ iterw1 += extra->dw1dx; \
+ iters1 += extra->ds1dx; \
+ itert1 += extra->dt1dx; \
+ } \
+ } \
+}
+#endif
+// ******************************************************************************************************************************
+// Computes a log2 of a 16.32 value to 2 fractional bits of precision.
+// The return value is coded as a 24.8 value.
+// The maximum error using a 4 bit lookup from the mantissa is 0.0875, which is less than 1/2 lsb (0.125) for 2 bits of fraction.
+// ******************************************************************************************************************************
+INLINE INT32 ATTR_FORCE_INLINE new_log2(double &value)
+{
+ static const INT32 new_log2_table[16] = {0, 22, 44, 63, 82, 100, 118, 134, 150, 165, 179, 193, 207, 220, 232, 244};
+ UINT64 ival = *((UINT64 *)&value);
+ // We zero the result if negative so don't worry about the sign bit
+ INT32 exp = (ival>>52);
+ exp -= 1023+32;
+ exp <<= 8;
+ UINT32 addr = (UINT64)(ival>>48) & 0xf;
+ exp += new_log2_table[addr];
+ // Return 0 if negative
+ return (ival & ((UINT64)1<<63)) ? 0 : exp;
+}
+
+// Computes A/C and B/C and returns log2 of 1/C
+// A, B and C are 16.32 values. The results are 24.8.
+INLINE void ATTR_FORCE_INLINE multi_reciplog(INT64 valueA, INT64 valueB, INT64 valueC, INT32 &log, INT32 &resA, INT32 &resB)
+{
+ double recip = double(1ULL<<(47-39))/valueC;
+ double resAD = valueA * recip;
+ double resBD = valueB * recip;
+ log = new_log2(recip);
+ log += 56<<8;
+ resA = resAD;
+ resB = resBD;
+}
+
+
+INLINE rgbaint_t ATTR_FORCE_INLINE genTexture(tmu_state *TT, INT32 x, const UINT8 *dither4, const UINT32 TEXMODE, rgb_t *LOOKUP, INT32 LODBASE, INT64 ITERS, INT64 ITERT, INT64 ITERW, INT32 &lod)
+{
+ rgbaint_t result;
+ INT32 s, t, ilod;
+
+ /* determine the S/T/LOD values for this texture */
+ lod = (LODBASE);
+ if (TEXMODE_ENABLE_PERSPECTIVE(TEXMODE))
+ {
+ INT32 wLog;
+ if (USE_FAST_RECIP) {
+ const INT32 oow = fast_reciplog((ITERW), &wLog);
+ s = ((INT64)oow * (ITERS)) >> (29+10);
+ t = ((INT64)oow * (ITERT)) >> (29+10);
+ } else {
+ multi_reciplog(ITERS, ITERT, ITERW, wLog, s, t);
+ }
+ lod += wLog;
+ }
+ else
+ {
+ s = (ITERS) >> (14+10);
+ t = (ITERT) >> (14+10);
+ }
+
+ /* clamp W */
+ if (TEXMODE_CLAMP_NEG_W(TEXMODE) && (ITERW) < 0)
+ {
+ s = t = 0;
+ }
+
+ /* clamp the LOD */
+ lod += (TT)->lodbias;
+ if (TEXMODE_ENABLE_LOD_DITHER(TEXMODE))
+ lod += dither4[x&3] << 4;
+ if (lod < (TT)->lodmin)
+ lod = (TT)->lodmin;
+ else if (lod > (TT)->lodmax)
+ lod = (TT)->lodmax;
+
+ /* now the LOD is in range; if we don't own this LOD, take the next one */
+ ilod = lod >> 8;
+ if (!(((TT)->lodmask >> ilod) & 1))
+ ilod++;
+
+ /* fetch the texture base */
+ UINT32 texbase = (TT)->lodoffset[ilod];
+
+ /* compute the maximum s and t values at this LOD */
+ INT32 smax = (TT)->wmask >> ilod;
+ INT32 tmax = (TT)->hmask >> ilod;
+
+ /* determine whether we are point-sampled or bilinear */
+ if ((lod == (TT)->lodmin && !TEXMODE_MAGNIFICATION_FILTER(TEXMODE)) ||
+ (lod != (TT)->lodmin && !TEXMODE_MINIFICATION_FILTER(TEXMODE)))
+ {
+ /* point sampled */
+
+ UINT32 texel0;
+
+ /* adjust S/T for the LOD and strip off the fractions */
+ s >>= ilod + (18-10);
+ t >>= ilod + (18-10);
+
+ /* clamp/wrap S/T if necessary */
+ if (TEXMODE_CLAMP_S(TEXMODE))
+ CLAMP(s, 0, smax);
+ if (TEXMODE_CLAMP_T(TEXMODE))
+ CLAMP(t, 0, tmax);
+ s &= smax;
+ t &= tmax;
+ t *= smax + 1;
+
+ /* fetch texel data */
+ if (TEXMODE_FORMAT(TEXMODE) < 8)
+ {
+ texel0 = *(UINT8 *)&(TT)->ram[(texbase + t + s) & (TT)->mask];
+ result.set((LOOKUP)[texel0]);
+ }
+ else
+ {
+ texel0 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s)) & (TT)->mask];
+ if (TEXMODE_FORMAT(TEXMODE) >= 10 && TEXMODE_FORMAT(TEXMODE) <= 12)
+ result.set((LOOKUP)[texel0]);
+ else
+ result.set(((LOOKUP)[texel0 & 0xff] & 0xffffff) | ((texel0 & 0xff00) << 16));
+ }
+ }
+ else
+ {
+ /* bilinear filtered */
+
+ UINT32 texel0, texel1, texel2, texel3;
+ UINT32 sfrac, tfrac;
+ INT32 s1, t1;
+
+ /* adjust S/T for the LOD and strip off all but the low 8 bits of */
+ /* the fraction */
+ s >>= ilod; // + (10-10);
+ t >>= ilod; // + (10-10);
+
+ /* also subtract 1/2 texel so that (0.5,0.5) = a full (0,0) texel */
+ s -= 0x80;
+ t -= 0x80;
+
+ /* extract the fractions */
+ sfrac = s & (TT)->bilinear_mask;
+ tfrac = t & (TT)->bilinear_mask;
+
+ /* now toss the rest */
+ s >>= 8;
+ t >>= 8;
+ s1 = s + 1;
+ t1 = t + 1;
+
+ /* clamp/wrap S/T if necessary */
+ if (TEXMODE_CLAMP_S(TEXMODE))
+ {
+ if (s < 0) {
+ s = 0;
+ s1 = 0;
+ } else if (s >= smax) {
+ s = smax;
+ s1 = smax;
+ }
+ //CLAMP(s, 0, smax);
+ //CLAMP(s1, 0, smax);
+ } else {
+ s &= smax;
+ s1 &= smax;
+ }
+
+ if (TEXMODE_CLAMP_T(TEXMODE))
+ {
+ if (t < 0) {
+ t = 0;
+ t1 = 0;
+ } else if (t >= tmax) {
+ t = tmax;
+ t1 = tmax;
+ }
+ //CLAMP(t, 0, tmax);
+ //CLAMP(t1, 0, tmax);
+ } else {
+ t &= tmax;
+ t1 &= tmax;
+ }
+ t *= smax + 1;
+ t1 *= smax + 1;
+
+ /* fetch texel data */
+ if (TEXMODE_FORMAT(TEXMODE) < 8)
+ {
+ texel0 = *(UINT8 *)&(TT)->ram[(texbase + t + s)];
+ texel1 = *(UINT8 *)&(TT)->ram[(texbase + t + s1)];
+ texel2 = *(UINT8 *)&(TT)->ram[(texbase + t1 + s)];
+ texel3 = *(UINT8 *)&(TT)->ram[(texbase + t1 + s1)];
+ texel0 = (LOOKUP)[texel0];
+ texel1 = (LOOKUP)[texel1];
+ texel2 = (LOOKUP)[texel2];
+ texel3 = (LOOKUP)[texel3];
+ }
+ else
+ {
+ texel0 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s))];
+ texel1 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t + s1))];
+ texel2 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t1 + s))];
+ texel3 = *(UINT16 *)&(TT)->ram[(texbase + 2*(t1 + s1))];
+ if (TEXMODE_FORMAT(TEXMODE) >= 10 && TEXMODE_FORMAT(TEXMODE) <= 12)
+ {
+ texel0 = (LOOKUP)[texel0];
+ texel1 = (LOOKUP)[texel1];
+ texel2 = (LOOKUP)[texel2];
+ texel3 = (LOOKUP)[texel3];
+ }
+ else
+ {
+ texel0 = ((LOOKUP)[texel0 & 0xff] & 0xffffff) | ((texel0 & 0xff00) << 16);
+ texel1 = ((LOOKUP)[texel1 & 0xff] & 0xffffff) | ((texel1 & 0xff00) << 16);
+ texel2 = ((LOOKUP)[texel2 & 0xff] & 0xffffff) | ((texel2 & 0xff00) << 16);
+ texel3 = ((LOOKUP)[texel3 & 0xff] & 0xffffff) | ((texel3 & 0xff00) << 16);
+ }
+ }
+
+ /* weigh in each texel */
+
+ result.bilinear_filter_rgbaint(texel0, texel1, texel2, texel3, sfrac, tfrac);
+ }
+ return result;
+}
+
+INLINE rgbaint_t ATTR_FORCE_INLINE combineTexture(tmu_state *TT, const UINT32 TEXMODE, rgbaint_t c_local, rgbaint_t c_other, INT32 lod)
+{
+ INT32 a_other = c_other.get_a();
+ INT32 a_local = c_local.get_a();
+ rgbaint_t add_val = c_local;
+ UINT8 tmp;
+
+ /* select zero/other for RGB */
+ if (TEXMODE_TC_ZERO_OTHER(TEXMODE))
+ c_other.and_imm_rgba(-1, 0, 0, 0);
+
+ /* select zero/other for alpha */
+ if (TEXMODE_TCA_ZERO_OTHER(TEXMODE))
+ c_other.set_a(0);
+
+ if (TEXMODE_TC_SUB_CLOCAL(TEXMODE) || TEXMODE_TCA_SUB_CLOCAL(TEXMODE))
+ {
+ rgbaint_t sub_val = c_local;
+
+ /* potentially subtract c_local */
+ if (!TEXMODE_TC_SUB_CLOCAL(TEXMODE))
+ sub_val.and_imm_rgba(-1, 0, 0, 0);
+
+ if (!TEXMODE_TCA_SUB_CLOCAL(TEXMODE))
+ sub_val.set_a(0);
+
+ c_other.sub(sub_val);
+ }
+
+ /* blend RGB */
+ switch (TEXMODE_TC_MSELECT(TEXMODE))
+ {
+ default: /* reserved */
+ case 0: /* zero */
+ c_local.and_imm_rgba(-1, 0, 0, 0);
+ break;
+
+ case 1: /* c_local */
+ break;
+
+ case 2: /* a_other */
+ c_local.set(a_local, a_other, a_other, a_other);
+ break;
+
+ case 3: /* a_local */
+ c_local.set(a_local, a_local, a_local, a_local);
+ break;
+
+ case 4: /* LOD (detail factor) */
+ if ((TT)->detailbias <= lod)
+ c_local.and_imm_rgba(-1, 0, 0, 0);
+ else
+ {
+ tmp = ((((TT)->detailbias - lod) << (TT)->detailscale) >> 8);
+ if (tmp > (TT)->detailmax)
+ tmp = (TT)->detailmax;
+ c_local.set(a_local, tmp, tmp, tmp);
+ }
+ break;
+
+ case 5: /* LOD fraction */
+ tmp = lod & 0xff;
+ c_local.set(a_local, tmp, tmp, tmp);
+ break;
+ }
+
+ /* blend alpha */
+ switch (TEXMODE_TCA_MSELECT(TEXMODE))
+ {
+ default: /* reserved */
+ case 0: /* zero */
+ c_local.set_a(0);
+ break;
+
+ case 1: /* c_local */
+ break;
+
+ case 2: /* a_other */
+ c_local.set_a(a_other);
+ break;
+
+ case 3: /* a_local */
+ break;
+
+ case 4: /* LOD (detail factor) */
+ if ((TT)->detailbias <= lod)
+ c_local.set_a(0);
+ else
+ {
+ tmp = ((((TT)->detailbias - lod) << (TT)->detailscale) >> 8);
+ if (tmp > (TT)->detailmax)
+ tmp = (TT)->detailmax;
+ c_local.set_a(tmp);
+ }
+ break;
+
+ case 5: /* LOD fraction */
+ c_local.set_a(lod & 0xff);
+ break;
+ }
+
+ /* reverse the RGB blend */
+ if (!TEXMODE_TC_REVERSE_BLEND(TEXMODE))
+ {
+ c_local.xor_imm_rgba(0, 0xff, 0xff, 0xff);
+ }
+
+ /* reverse the alpha blend */
+ if (!TEXMODE_TCA_REVERSE_BLEND(TEXMODE))
+ c_local.xor_imm_rgba(0xff, 0, 0, 0);
+
+ /* do the blend */
+ //tr = (tr * (blendr + 1)) >> 8;
+ //tg = (tg * (blendg + 1)) >> 8;
+ //tb = (tb * (blendb + 1)) >> 8;
+ //ta = (ta * (blenda + 1)) >> 8;
+
+ /* add clocal or alocal to RGB */
+ switch (TEXMODE_TC_ADD_ACLOCAL(TEXMODE))
+ {
+ case 3: /* reserved */
+ case 0: /* nothing */
+ add_val.set(a_local, 0, 0, 0);
+ break;
+
+ case 1: /* add c_local */
+ break;
+
+ case 2: /* add_alocal */
+ add_val.set(a_local, a_local , a_local , a_local);
+ //tr += c_local.rgb.a;
+ //tg += c_local.rgb.a;
+ //tb += c_local.rgb.a;
+ break;
+ }
+
+ /* add clocal or alocal to alpha */
+ if (!TEXMODE_TCA_ADD_ACLOCAL(TEXMODE))
+ add_val.set_a(0);
+ //ta += c_local.rgb.a;
+
+ /* clamp */
+ //result.rgb.r = (tr < 0) ? 0 : (tr > 0xff) ? 0xff : tr;
+ //result.rgb.g = (tg < 0) ? 0 : (tg > 0xff) ? 0xff : tg;
+ //result.rgb.b = (tb < 0) ? 0 : (tb > 0xff) ? 0xff : tb;
+ //result.rgb.a = (ta < 0) ? 0 : (ta > 0xff) ? 0xff : ta;
+ c_local.add_imm(1);
+ c_other.scale_add_and_clamp(c_local, add_val);
+ rgbaint_t result(c_other);
+ /* invert */
+ if (TEXMODE_TC_INVERT_OUTPUT(TEXMODE))
+ result.xor_imm_rgba(0, 0xff, 0xff, 0xff);
+ if (TEXMODE_TCA_INVERT_OUTPUT(TEXMODE))
+ result.xor_imm_rgba(0xff, 0, 0, 0);
+ return result;
+}
diff --git a/src/devices/video/voodoo.c b/src/devices/video/voodoo.c
new file mode 100644
index 00000000000..1874fccad29
--- /dev/null
+++ b/src/devices/video/voodoo.c
@@ -0,0 +1,6537 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ voodoo.c
+
+ 3dfx Voodoo Graphics SST-1/2 emulator.
+
+****************************************************************************
+
+//fix me -- blitz2k dies when starting a game with heavy fog (in DRC)
+
+****************************************************************************
+
+ 3dfx Voodoo Graphics SST-1/2 emulator
+
+ emulator by Aaron Giles
+
+ --------------------------
+
+ Specs:
+
+ Voodoo 1 (SST1):
+ 2,4MB frame buffer RAM
+ 1,2,4MB texture RAM
+ 50MHz clock frequency
+ clears @ 2 pixels/clock (RGB and depth simultaneously)
+ renders @ 1 pixel/clock
+ 64 entry PCI FIFO
+ memory FIFO up to 65536 entries
+
+ Voodoo 2:
+ 2,4MB frame buffer RAM
+ 2,4,8,16MB texture RAM
+ 90MHz clock frquency
+ clears @ 2 pixels/clock (RGB and depth simultaneously)
+ renders @ 1 pixel/clock
+ ultrafast clears @ 16 pixels/clock
+ 128 entry PCI FIFO
+ memory FIFO up to 65536 entries
+
+ Voodoo Banshee (h3):
+ Integrated VGA support
+ 2,4,8MB frame buffer RAM
+ 90MHz clock frquency
+ clears @ 2 pixels/clock (RGB and depth simultaneously)
+ renders @ 1 pixel/clock
+ ultrafast clears @ 32 pixels/clock
+
+ Voodoo 3 ("Avenger"/h4):
+ Integrated VGA support
+ 4,8,16MB frame buffer RAM
+ 143MHz clock frquency
+ clears @ 2 pixels/clock (RGB and depth simultaneously)
+ renders @ 1 pixel/clock
+ ultrafast clears @ 32 pixels/clock
+
+ --------------------------
+
+ still to be implemented:
+ * trilinear textures
+
+ things to verify:
+ * floating Z buffer
+
+
+iterated RGBA = 12.12 [24 bits]
+iterated Z = 20.12 [32 bits]
+iterated W = 18.32 [48 bits]
+
+>mamepm blitz
+Stall PCI for HWM: 1
+PCI FIFO Empty Entries LWM: D
+LFB -> FIFO: 1
+Texture -> FIFO: 1
+Memory FIFO: 1
+Memory FIFO HWM: 2000
+Memory FIFO Write Burst HWM: 36
+Memory FIFO LWM for PCI: 5
+Memory FIFO row start: 120
+Memory FIFO row rollover: 3FF
+Video dither subtract: 0
+DRAM banking: 1
+Triple buffer: 0
+Video buffer offset: 60
+DRAM banking: 1
+
+>mamepm wg3dh
+Stall PCI for HWM: 1
+PCI FIFO Empty Entries LWM: D
+LFB -> FIFO: 1
+Texture -> FIFO: 1
+Memory FIFO: 1
+Memory FIFO HWM: 2000
+Memory FIFO Write Burst HWM: 36
+Memory FIFO LWM for PCI: 5
+Memory FIFO row start: C0
+Memory FIFO row rollover: 3FF
+Video dither subtract: 0
+DRAM banking: 1
+Triple buffer: 0
+Video buffer offset: 40
+DRAM banking: 1
+
+
+As a point of reference, the 3D engine uses the following algorithm to calculate the linear memory address as a
+function of the video buffer offset (fbiInit2 bits(19:11)), the number of 32x32 tiles in the X dimension (fbiInit1
+bits(7:4) and bit(24)), X, and Y:
+
+ tilesInX[4:0] = {fbiInit1[24], fbiInit1[7:4], fbiInit6[30]}
+ rowBase = fbiInit2[19:11]
+ rowStart = ((Y>>5) * tilesInX) >> 1
+
+ if (!(tilesInX & 1))
+ {
+ rowOffset = (X>>6);
+ row[9:0] = rowStart + rowOffset (for color buffer 0)
+ row[9:0] = rowBase + rowStart + rowOffset (for color buffer 1)
+ row[9:0] = (rowBase<<1) + rowStart + rowOffset (for depth/alpha buffer when double color buffering[fbiInit5[10:9]=0])
+ row[9:0] = (rowBase<<1) + rowStart + rowOffset (for color buffer 2 when triple color buffering[fbiInit5[10:9]=1 or 2])
+ row[9:0] = (rowBase<<1) + rowBase + rowStart + rowOffset (for depth/alpha buffer when triple color buffering[fbiInit5[10:9]=2])
+ column[8:0] = ((Y % 32) <<4) + ((X % 32)>>1)
+ ramSelect[1] = ((X&0x20) ? 1 : 0) (for color buffers)
+ ramSelect[1] = ((X&0x20) ? 0 : 1) (for depth/alpha buffers)
+ }
+ else
+ {
+ rowOffset = (!(Y&0x20)) ? (X>>6) : ((X>31) ? (((X-32)>>6)+1) : 0)
+ row[9:0] = rowStart + rowOffset (for color buffer 0)
+ row[9:0] = rowBase + rowStart + rowOffset (for color buffer 1)
+ row[9:0] = (rowBase<<1) + rowStart + rowOffset (for depth/alpha buffer when double color buffering[fbiInit5[10:9]=0])
+ row[9:0] = (rowBase<<1) + rowStart + rowOffset (for color buffer 2 when triple color buffering[fbiInit5[10:9]=1 or 2])
+ row[9:0] = (rowBase<<1) + rowBase + rowStart + rowOffset (for depth/alpha buffer when triple color buffering[fbiInit5[10:9]=2])
+ column[8:0] = ((Y % 32) <<4) + ((X % 32)>>1)
+ ramSelect[1] = (((X&0x20)^(Y&0x20)) ? 1 : 0) (for color buffers)
+ ramSelect[1] = (((X&0x20)^(Y&0x20)) ? 0 : 1) (for depth/alpha buffers)
+ }
+ ramSelect[0] = X % 2
+ pixelMemoryAddress[21:0] = (row[9:0]<<12) + (column[8:0]<<3) + (ramSelect[1:0]<<1)
+ bankSelect = pixelMemoryAddress[21]
+
+**************************************************************************/
+
+#ifndef EXPAND_RASTERIZERS
+#define EXPAND_RASTERIZERS
+
+#include "emu.h"
+#include "video/polylgcy.h"
+#include "video/rgbutil.h"
+#include "voodoo.h"
+#include "vooddefs.h"
+
+
+/*************************************
+ *
+ * Debugging
+ *
+ *************************************/
+
+#define DEBUG_DEPTH (0)
+#define DEBUG_LOD (0)
+
+#define LOG_VBLANK_SWAP (0)
+#define LOG_FIFO (0)
+#define LOG_FIFO_VERBOSE (0)
+#define LOG_REGISTERS (0)
+#define LOG_WAITS (0)
+#define LOG_LFB (0)
+#define LOG_TEXTURE_RAM (0)
+#define LOG_RASTERIZERS (0)
+#define LOG_CMDFIFO (0)
+#define LOG_CMDFIFO_VERBOSE (0)
+#define LOG_BANSHEE_2D (0)
+
+#define MODIFY_PIXEL(VV)
+
+// Need to turn off cycle eating when debugging MIPS drc
+// otherwise timer interrupts won't match nodrc debug mode.
+#define EAT_CYCLES (1)
+
+
+/*************************************
+ *
+ * Statics
+ *
+ *************************************/
+
+static const rectangle global_cliprect(-4096, 4095, -4096, 4095);
+
+/* fast dither lookup */
+static UINT8 dither4_lookup[256*16*2];
+static UINT8 dither2_lookup[256*16*2];
+
+/* fast reciprocal+log2 lookup */
+UINT32 voodoo_reciplog[(2 << RECIPLOG_LOOKUP_BITS) + 2];
+
+
+
+/*************************************
+ *
+ * Prototypes
+ *
+ *************************************/
+
+static void init_fbi(voodoo_state *v, fbi_state *f, void *memory, int fbmem);
+static void init_tmu_shared(tmu_shared_state *s);
+static void init_tmu(voodoo_state *v, tmu_state *t, voodoo_reg *reg, void *memory, int tmem);
+static void soft_reset(voodoo_state *v);
+static void recompute_video_memory(voodoo_state *v);
+static void check_stalled_cpu(voodoo_state *v, attotime current_time);
+static void flush_fifos(voodoo_state *v, attotime current_time);
+static TIMER_CALLBACK( stall_cpu_callback );
+static void stall_cpu(voodoo_state *v, int state, attotime current_time);
+static TIMER_CALLBACK( vblank_callback );
+static INT32 register_w(voodoo_state *v, offs_t offset, UINT32 data);
+static INT32 lfb_direct_w(voodoo_state *v, offs_t offset, UINT32 data, UINT32 mem_mask);
+static INT32 lfb_w(voodoo_state *v, offs_t offset, UINT32 data, UINT32 mem_mask);
+static INT32 texture_w(voodoo_state *v, offs_t offset, UINT32 data);
+static INT32 banshee_2d_w(voodoo_state *v, offs_t offset, UINT32 data);
+
+/* command handlers */
+static INT32 fastfill(voodoo_state *v);
+static INT32 swapbuffer(voodoo_state *v, UINT32 data);
+static INT32 triangle(voodoo_state *v);
+static INT32 begin_triangle(voodoo_state *v);
+static INT32 draw_triangle(voodoo_state *v);
+
+/* triangle helpers */
+static INT32 setup_and_draw_triangle(voodoo_state *v);
+static INT32 triangle_create_work_item(voodoo_state *v, UINT16 *drawbuf, int texcount);
+
+/* rasterizer management */
+static raster_info *add_rasterizer(voodoo_state *v, const raster_info *cinfo);
+static raster_info *find_rasterizer(voodoo_state *v, int texcount);
+static void dump_rasterizer_stats(voodoo_state *v);
+
+/* generic rasterizers */
+static void raster_fastfill(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
+static void raster_generic_0tmu(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
+static void raster_generic_1tmu(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
+static void raster_generic_2tmu(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
+
+
+
+/*************************************
+ *
+ * Specific rasterizers
+ *
+ *************************************/
+
+#define RASTERIZER_ENTRY(fbzcp, alpha, fog, fbz, tex0, tex1) \
+ RASTERIZER(fbzcp##_##alpha##_##fog##_##fbz##_##tex0##_##tex1, (((tex0) == 0xffffffff) ? 0 : ((tex1) == 0xffffffff) ? 1 : 2), fbzcp, fbz, alpha, fog, tex0, tex1)
+
+#include "voodoo.c"
+
+#undef RASTERIZER_ENTRY
+
+
+
+/*************************************
+ *
+ * Rasterizer table
+ *
+ *************************************/
+
+#define RASTERIZER_ENTRY(fbzcp, alpha, fog, fbz, tex0, tex1) \
+ { NULL, raster_##fbzcp##_##alpha##_##fog##_##fbz##_##tex0##_##tex1, FALSE, 0, 0, 0, fbzcp, alpha, fog, fbz, tex0, tex1 },
+
+static const raster_info predef_raster_table[] =
+{
+#include "voodoo.c"
+ { 0 }
+};
+
+#undef RASTERIZER_ENTRY
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ get_safe_token - makes sure that the passed
+ in device is, in fact, a voodoo device
+-------------------------------------------------*/
+
+INLINE voodoo_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert((device->type() == VOODOO_1) || (device->type() == VOODOO_2) || (device->type() == VOODOO_BANSHEE) || (device->type() == VOODOO_3));
+
+ return (voodoo_state *)downcast<voodoo_device *>(device)->token();
+}
+
+
+
+/*************************************
+ *
+ * Video update
+ *
+ *************************************/
+
+int voodoo_update(device_t *device, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ voodoo_state *v = get_safe_token(device);
+ int changed = v->fbi.video_changed;
+ int drawbuf = v->fbi.frontbuf;
+ int statskey;
+ int x, y;
+
+ /* reset the video changed flag */
+ v->fbi.video_changed = FALSE;
+
+ /* if we are blank, just fill with black */
+ if (v->type <= TYPE_VOODOO_2 && FBIINIT1_SOFTWARE_BLANK(v->reg[fbiInit1].u))
+ {
+ bitmap.fill(0, cliprect);
+ return changed;
+ }
+
+ /* if the CLUT is dirty, recompute the pens array */
+ if (v->fbi.clut_dirty)
+ {
+ UINT8 rtable[32], gtable[64], btable[32];
+
+ /* Voodoo/Voodoo-2 have an internal 33-entry CLUT */
+ if (v->type <= TYPE_VOODOO_2)
+ {
+ /* kludge: some of the Midway games write 0 to the last entry when they obviously mean FF */
+ if ((v->fbi.clut[32] & 0xffffff) == 0 && (v->fbi.clut[31] & 0xffffff) != 0)
+ v->fbi.clut[32] = 0x20ffffff;
+
+ /* compute the R/G/B pens first */
+ for (x = 0; x < 32; x++)
+ {
+ /* treat X as a 5-bit value, scale up to 8 bits, and linear interpolate for red/blue */
+ y = (x << 3) | (x >> 2);
+ rtable[x] = (v->fbi.clut[y >> 3].r() * (8 - (y & 7)) + v->fbi.clut[(y >> 3) + 1].r() * (y & 7)) >> 3;
+ btable[x] = (v->fbi.clut[y >> 3].b() * (8 - (y & 7)) + v->fbi.clut[(y >> 3) + 1].b() * (y & 7)) >> 3;
+
+ /* treat X as a 6-bit value with LSB=0, scale up to 8 bits, and linear interpolate */
+ y = (x * 2) + 0;
+ y = (y << 2) | (y >> 4);
+ gtable[x*2+0] = (v->fbi.clut[y >> 3].g() * (8 - (y & 7)) + v->fbi.clut[(y >> 3) + 1].g() * (y & 7)) >> 3;
+
+ /* treat X as a 6-bit value with LSB=1, scale up to 8 bits, and linear interpolate */
+ y = (x * 2) + 1;
+ y = (y << 2) | (y >> 4);
+ gtable[x*2+1] = (v->fbi.clut[y >> 3].g() * (8 - (y & 7)) + v->fbi.clut[(y >> 3) + 1].g() * (y & 7)) >> 3;
+ }
+ }
+
+ /* Banshee and later have a 512-entry CLUT that can be bypassed */
+ else
+ {
+ int which = (v->banshee.io[io_vidProcCfg] >> 13) & 1;
+ int bypass = (v->banshee.io[io_vidProcCfg] >> 11) & 1;
+
+ /* compute R/G/B pens first */
+ for (x = 0; x < 32; x++)
+ {
+ /* treat X as a 5-bit value, scale up to 8 bits */
+ y = (x << 3) | (x >> 2);
+ rtable[x] = bypass ? y : v->fbi.clut[which * 256 + y].r();
+ btable[x] = bypass ? y : v->fbi.clut[which * 256 + y].b();
+
+ /* treat X as a 6-bit value with LSB=0, scale up to 8 bits */
+ y = (x * 2) + 0;
+ y = (y << 2) | (y >> 4);
+ gtable[x*2+0] = bypass ? y : v->fbi.clut[which * 256 + y].g();
+
+ /* treat X as a 6-bit value with LSB=1, scale up to 8 bits, and linear interpolate */
+ y = (x * 2) + 1;
+ y = (y << 2) | (y >> 4);
+ gtable[x*2+1] = bypass ? y : v->fbi.clut[which * 256 + y].g();
+ }
+ }
+
+ /* now compute the actual pens array */
+ for (x = 0; x < 65536; x++)
+ {
+ int r = rtable[(x >> 11) & 0x1f];
+ int g = gtable[(x >> 5) & 0x3f];
+ int b = btable[x & 0x1f];
+ v->fbi.pen[x] = rgb_t(r, g, b);
+ }
+
+ /* no longer dirty */
+ v->fbi.clut_dirty = FALSE;
+ changed = TRUE;
+ }
+
+ /* debugging! */
+ if (device->machine().input().code_pressed(KEYCODE_L))
+ drawbuf = v->fbi.backbuf;
+
+ /* copy from the current front buffer */
+ for (y = cliprect.min_y; y <= cliprect.max_y; y++)
+ if (y >= v->fbi.yoffs)
+ {
+ UINT16 *src = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[drawbuf]) + (y - v->fbi.yoffs) * v->fbi.rowpixels - v->fbi.xoffs;
+ UINT32 *dst = &bitmap.pix32(y);
+ for (x = cliprect.min_x; x <= cliprect.max_x; x++)
+ dst[x] = v->fbi.pen[src[x]];
+ }
+
+ /* update stats display */
+ statskey = (device->machine().input().code_pressed(KEYCODE_BACKSLASH) != 0);
+ if (statskey && statskey != v->stats.lastkey)
+ v->stats.display = !v->stats.display;
+ v->stats.lastkey = statskey;
+
+ /* display stats */
+ if (v->stats.display)
+ popmessage(v->stats.buffer, 0, 0);
+
+ /* update render override */
+ v->stats.render_override = device->machine().input().code_pressed(KEYCODE_ENTER);
+ if (DEBUG_DEPTH && v->stats.render_override)
+ {
+ for (y = cliprect.min_y; y <= cliprect.max_y; y++)
+ {
+ UINT16 *src = (UINT16 *)(v->fbi.ram + v->fbi.auxoffs) + (y - v->fbi.yoffs) * v->fbi.rowpixels - v->fbi.xoffs;
+ UINT32 *dst = &bitmap.pix32(y);
+ for (x = cliprect.min_x; x <= cliprect.max_x; x++)
+ dst[x] = ((src[x] << 8) & 0xff0000) | ((src[x] >> 0) & 0xff00) | ((src[x] >> 8) & 0xff);
+ }
+ }
+ return changed;
+}
+
+
+
+/*************************************
+ *
+ * Chip reset
+ *
+ *************************************/
+
+int voodoo_get_type(device_t *device)
+{
+ voodoo_state *v = get_safe_token(device);
+ return v->type;
+}
+
+
+int voodoo_is_stalled(device_t *device)
+{
+ voodoo_state *v = get_safe_token(device);
+ return (v->pci.stall_state != NOT_STALLED);
+}
+
+
+void voodoo_set_init_enable(device_t *device, UINT32 newval)
+{
+ voodoo_state *v = get_safe_token(device);
+ v->pci.init_enable = newval;
+ if (LOG_REGISTERS)
+ logerror("VOODOO.%d.REG:initEnable write = %08X\n", v->index, newval);
+}
+
+
+
+/*************************************
+ *
+ * Common initialization
+ *
+ *************************************/
+
+static void init_fbi(voodoo_state *v, fbi_state *f, void *memory, int fbmem)
+{
+ int pen;
+
+ /* allocate frame buffer RAM and set pointers */
+ f->ram = (UINT8 *)memory;
+ f->mask = fbmem - 1;
+ f->rgboffs[0] = f->rgboffs[1] = f->rgboffs[2] = 0;
+ f->auxoffs = ~0;
+
+ /* default to 0x0 */
+ f->frontbuf = 0;
+ f->backbuf = 1;
+ f->width = 512;
+ f->height = 384;
+
+ /* init the pens */
+ f->clut_dirty = TRUE;
+ if (v->type <= TYPE_VOODOO_2)
+ {
+ for (pen = 0; pen < 32; pen++)
+ v->fbi.clut[pen] = rgb_t(pen, pal5bit(pen), pal5bit(pen), pal5bit(pen));
+ v->fbi.clut[32] = rgb_t(32,0xff,0xff,0xff);
+ }
+ else
+ {
+ for (pen = 0; pen < 512; pen++)
+ v->fbi.clut[pen] = rgb_t(pen,pen,pen);
+ }
+
+ /* allocate a VBLANK timer */
+ f->vblank_timer = v->device->machine().scheduler().timer_alloc(FUNC(vblank_callback), v);
+ f->vblank = FALSE;
+
+ /* initialize the memory FIFO */
+ f->fifo.base = NULL;
+ f->fifo.size = f->fifo.in = f->fifo.out = 0;
+
+ /* set the fog delta mask */
+ f->fogdelta_mask = (v->type < TYPE_VOODOO_2) ? 0xff : 0xfc;
+}
+
+
+static void init_tmu_shared(tmu_shared_state *s)
+{
+ int val;
+
+ /* build static 8-bit texel tables */
+ for (val = 0; val < 256; val++)
+ {
+ int r, g, b, a;
+
+ /* 8-bit RGB (3-3-2) */
+ EXTRACT_332_TO_888(val, r, g, b);
+ s->rgb332[val] = rgb_t(0xff, r, g, b);
+
+ /* 8-bit alpha */
+ s->alpha8[val] = rgb_t(val, val, val, val);
+
+ /* 8-bit intensity */
+ s->int8[val] = rgb_t(0xff, val, val, val);
+
+ /* 8-bit alpha, intensity */
+ a = ((val >> 0) & 0xf0) | ((val >> 4) & 0x0f);
+ r = ((val << 4) & 0xf0) | ((val << 0) & 0x0f);
+ s->ai44[val] = rgb_t(a, r, r, r);
+ }
+
+ /* build static 16-bit texel tables */
+ for (val = 0; val < 65536; val++)
+ {
+ int r, g, b, a;
+
+ /* table 10 = 16-bit RGB (5-6-5) */
+ EXTRACT_565_TO_888(val, r, g, b);
+ s->rgb565[val] = rgb_t(0xff, r, g, b);
+
+ /* table 11 = 16 ARGB (1-5-5-5) */
+ EXTRACT_1555_TO_8888(val, a, r, g, b);
+ s->argb1555[val] = rgb_t(a, r, g, b);
+
+ /* table 12 = 16-bit ARGB (4-4-4-4) */
+ EXTRACT_4444_TO_8888(val, a, r, g, b);
+ s->argb4444[val] = rgb_t(a, r, g, b);
+ }
+}
+
+
+static void init_tmu(voodoo_state *v, tmu_state *t, voodoo_reg *reg, void *memory, int tmem)
+{
+ /* allocate texture RAM */
+ t->ram = (UINT8 *)memory;
+ t->mask = tmem - 1;
+ t->reg = reg;
+ t->regdirty = TRUE;
+ t->bilinear_mask = (v->type >= TYPE_VOODOO_2) ? 0xff : 0xf0;
+
+ /* mark the NCC tables dirty and configure their registers */
+ t->ncc[0].dirty = t->ncc[1].dirty = TRUE;
+ t->ncc[0].reg = &t->reg[nccTable+0];
+ t->ncc[1].reg = &t->reg[nccTable+12];
+
+ /* create pointers to all the tables */
+ t->texel[0] = v->tmushare.rgb332;
+ t->texel[1] = t->ncc[0].texel;
+ t->texel[2] = v->tmushare.alpha8;
+ t->texel[3] = v->tmushare.int8;
+ t->texel[4] = v->tmushare.ai44;
+ t->texel[5] = t->palette;
+ t->texel[6] = (v->type >= TYPE_VOODOO_2) ? t->palettea : NULL;
+ t->texel[7] = NULL;
+ t->texel[8] = v->tmushare.rgb332;
+ t->texel[9] = t->ncc[0].texel;
+ t->texel[10] = v->tmushare.rgb565;
+ t->texel[11] = v->tmushare.argb1555;
+ t->texel[12] = v->tmushare.argb4444;
+ t->texel[13] = v->tmushare.int8;
+ t->texel[14] = t->palette;
+ t->texel[15] = NULL;
+ t->lookup = t->texel[0];
+
+ /* attach the palette to NCC table 0 */
+ t->ncc[0].palette = t->palette;
+ if (v->type >= TYPE_VOODOO_2)
+ t->ncc[0].palettea = t->palettea;
+
+ /* set up texture address calculations */
+ if (v->type <= TYPE_VOODOO_2)
+ {
+ t->texaddr_mask = 0x0fffff;
+ t->texaddr_shift = 3;
+ }
+ else
+ {
+ t->texaddr_mask = 0xfffff0;
+ t->texaddr_shift = 0;
+ }
+}
+
+
+static void voodoo_postload(voodoo_state *v)
+{
+ int index, subindex;
+
+ v->fbi.clut_dirty = TRUE;
+ for (index = 0; index < ARRAY_LENGTH(v->tmu); index++)
+ {
+ v->tmu[index].regdirty = TRUE;
+ for (subindex = 0; subindex < ARRAY_LENGTH(v->tmu[index].ncc); subindex++)
+ v->tmu[index].ncc[subindex].dirty = TRUE;
+ }
+
+ /* recompute video memory to get the FBI FIFO base recomputed */
+ if (v->type <= TYPE_VOODOO_2)
+ recompute_video_memory(v);
+}
+
+
+static void init_save_state(device_t *device)
+{
+ voodoo_state *v = get_safe_token(device);
+ int index, subindex;
+
+ device->machine().save().register_postload(save_prepost_delegate(FUNC(voodoo_postload), v));
+
+ /* register states: core */
+ device->save_item(NAME(v->extra_cycles));
+ device->save_pointer(NAME(&v->reg[0].u), ARRAY_LENGTH(v->reg));
+ device->save_item(NAME(v->alt_regmap));
+
+ /* register states: pci */
+ device->save_item(NAME(v->pci.fifo.in));
+ device->save_item(NAME(v->pci.fifo.out));
+ device->save_item(NAME(v->pci.init_enable));
+ device->save_item(NAME(v->pci.stall_state));
+ device->save_item(NAME(v->pci.op_pending));
+ device->save_item(NAME(v->pci.op_end_time));
+ device->save_item(NAME(v->pci.fifo_mem));
+
+ /* register states: dac */
+ device->save_item(NAME(v->dac.reg));
+ device->save_item(NAME(v->dac.read_result));
+
+ /* register states: fbi */
+ device->save_pointer(NAME(v->fbi.ram), v->fbi.mask + 1);
+ device->save_item(NAME(v->fbi.rgboffs));
+ device->save_item(NAME(v->fbi.auxoffs));
+ device->save_item(NAME(v->fbi.frontbuf));
+ device->save_item(NAME(v->fbi.backbuf));
+ device->save_item(NAME(v->fbi.swaps_pending));
+ device->save_item(NAME(v->fbi.video_changed));
+ device->save_item(NAME(v->fbi.yorigin));
+ device->save_item(NAME(v->fbi.lfb_base));
+ device->save_item(NAME(v->fbi.lfb_stride));
+ device->save_item(NAME(v->fbi.width));
+ device->save_item(NAME(v->fbi.height));
+ device->save_item(NAME(v->fbi.xoffs));
+ device->save_item(NAME(v->fbi.yoffs));
+ device->save_item(NAME(v->fbi.vsyncscan));
+ device->save_item(NAME(v->fbi.rowpixels));
+ device->save_item(NAME(v->fbi.vblank));
+ device->save_item(NAME(v->fbi.vblank_count));
+ device->save_item(NAME(v->fbi.vblank_swap_pending));
+ device->save_item(NAME(v->fbi.vblank_swap));
+ device->save_item(NAME(v->fbi.vblank_dont_swap));
+ device->save_item(NAME(v->fbi.cheating_allowed));
+ device->save_item(NAME(v->fbi.sign));
+ device->save_item(NAME(v->fbi.ax));
+ device->save_item(NAME(v->fbi.ay));
+ device->save_item(NAME(v->fbi.bx));
+ device->save_item(NAME(v->fbi.by));
+ device->save_item(NAME(v->fbi.cx));
+ device->save_item(NAME(v->fbi.cy));
+ device->save_item(NAME(v->fbi.startr));
+ device->save_item(NAME(v->fbi.startg));
+ device->save_item(NAME(v->fbi.startb));
+ device->save_item(NAME(v->fbi.starta));
+ device->save_item(NAME(v->fbi.startz));
+ device->save_item(NAME(v->fbi.startw));
+ device->save_item(NAME(v->fbi.drdx));
+ device->save_item(NAME(v->fbi.dgdx));
+ device->save_item(NAME(v->fbi.dbdx));
+ device->save_item(NAME(v->fbi.dadx));
+ device->save_item(NAME(v->fbi.dzdx));
+ device->save_item(NAME(v->fbi.dwdx));
+ device->save_item(NAME(v->fbi.drdy));
+ device->save_item(NAME(v->fbi.dgdy));
+ device->save_item(NAME(v->fbi.dbdy));
+ device->save_item(NAME(v->fbi.dady));
+ device->save_item(NAME(v->fbi.dzdy));
+ device->save_item(NAME(v->fbi.dwdy));
+ device->save_item(NAME(v->fbi.lfb_stats.pixels_in));
+ device->save_item(NAME(v->fbi.lfb_stats.pixels_out));
+ device->save_item(NAME(v->fbi.lfb_stats.chroma_fail));
+ device->save_item(NAME(v->fbi.lfb_stats.zfunc_fail));
+ device->save_item(NAME(v->fbi.lfb_stats.afunc_fail));
+ device->save_item(NAME(v->fbi.lfb_stats.clip_fail));
+ device->save_item(NAME(v->fbi.lfb_stats.stipple_count));
+ device->save_item(NAME(v->fbi.sverts));
+ for (index = 0; index < ARRAY_LENGTH(v->fbi.svert); index++)
+ {
+ device->save_item(NAME(v->fbi.svert[index].x), index);
+ device->save_item(NAME(v->fbi.svert[index].y), index);
+ device->save_item(NAME(v->fbi.svert[index].a), index);
+ device->save_item(NAME(v->fbi.svert[index].r), index);
+ device->save_item(NAME(v->fbi.svert[index].g), index);
+ device->save_item(NAME(v->fbi.svert[index].b), index);
+ device->save_item(NAME(v->fbi.svert[index].z), index);
+ device->save_item(NAME(v->fbi.svert[index].wb), index);
+ device->save_item(NAME(v->fbi.svert[index].w0), index);
+ device->save_item(NAME(v->fbi.svert[index].s0), index);
+ device->save_item(NAME(v->fbi.svert[index].t0), index);
+ device->save_item(NAME(v->fbi.svert[index].w1), index);
+ device->save_item(NAME(v->fbi.svert[index].s1), index);
+ device->save_item(NAME(v->fbi.svert[index].t1), index);
+ }
+ device->save_item(NAME(v->fbi.fifo.size));
+ device->save_item(NAME(v->fbi.fifo.in));
+ device->save_item(NAME(v->fbi.fifo.out));
+ for (index = 0; index < ARRAY_LENGTH(v->fbi.cmdfifo); index++)
+ {
+ device->save_item(NAME(v->fbi.cmdfifo[index].enable), index);
+ device->save_item(NAME(v->fbi.cmdfifo[index].count_holes), index);
+ device->save_item(NAME(v->fbi.cmdfifo[index].base), index);
+ device->save_item(NAME(v->fbi.cmdfifo[index].end), index);
+ device->save_item(NAME(v->fbi.cmdfifo[index].rdptr), index);
+ device->save_item(NAME(v->fbi.cmdfifo[index].amin), index);
+ device->save_item(NAME(v->fbi.cmdfifo[index].amax), index);
+ device->save_item(NAME(v->fbi.cmdfifo[index].depth), index);
+ device->save_item(NAME(v->fbi.cmdfifo[index].holes), index);
+ }
+ device->save_item(NAME(v->fbi.fogblend));
+ device->save_item(NAME(v->fbi.fogdelta));
+ device->save_item(NAME(v->fbi.clut));
+
+ /* register states: tmu */
+ for (index = 0; index < ARRAY_LENGTH(v->tmu); index++)
+ {
+ tmu_state *tmu = &v->tmu[index];
+ if (tmu->ram == NULL)
+ continue;
+ if (tmu->ram != v->fbi.ram)
+ device->save_pointer(NAME(tmu->ram), tmu->mask + 1, index);
+ device->save_item(NAME(tmu->starts), index);
+ device->save_item(NAME(tmu->startt), index);
+ device->save_item(NAME(tmu->startw), index);
+ device->save_item(NAME(tmu->dsdx), index);
+ device->save_item(NAME(tmu->dtdx), index);
+ device->save_item(NAME(tmu->dwdx), index);
+ device->save_item(NAME(tmu->dsdy), index);
+ device->save_item(NAME(tmu->dtdy), index);
+ device->save_item(NAME(tmu->dwdy), index);
+ for (subindex = 0; subindex < ARRAY_LENGTH(tmu->ncc); subindex++)
+ {
+ device->save_item(NAME(tmu->ncc[subindex].ir), index * ARRAY_LENGTH(tmu->ncc) + subindex);
+ device->save_item(NAME(tmu->ncc[subindex].ig), index * ARRAY_LENGTH(tmu->ncc) + subindex);
+ device->save_item(NAME(tmu->ncc[subindex].ib), index * ARRAY_LENGTH(tmu->ncc) + subindex);
+ device->save_item(NAME(tmu->ncc[subindex].qr), index * ARRAY_LENGTH(tmu->ncc) + subindex);
+ device->save_item(NAME(tmu->ncc[subindex].qg), index * ARRAY_LENGTH(tmu->ncc) + subindex);
+ device->save_item(NAME(tmu->ncc[subindex].qb), index * ARRAY_LENGTH(tmu->ncc) + subindex);
+ device->save_item(NAME(tmu->ncc[subindex].y), index * ARRAY_LENGTH(tmu->ncc) + subindex);
+ }
+ }
+
+ /* register states: banshee */
+ if (v->type >= TYPE_VOODOO_BANSHEE)
+ {
+ device->save_item(NAME(v->banshee.io));
+ device->save_item(NAME(v->banshee.agp));
+ device->save_item(NAME(v->banshee.vga));
+ device->save_item(NAME(v->banshee.crtc));
+ device->save_item(NAME(v->banshee.seq));
+ device->save_item(NAME(v->banshee.gc));
+ device->save_item(NAME(v->banshee.att));
+ device->save_item(NAME(v->banshee.attff));
+ }
+}
+
+
+
+/*************************************
+ *
+ * Statistics management
+ *
+ *************************************/
+
+static void accumulate_statistics(voodoo_state *v, const stats_block *stats)
+{
+ /* apply internal voodoo statistics */
+ v->reg[fbiPixelsIn].u += stats->pixels_in;
+ v->reg[fbiPixelsOut].u += stats->pixels_out;
+ v->reg[fbiChromaFail].u += stats->chroma_fail;
+ v->reg[fbiZfuncFail].u += stats->zfunc_fail;
+ v->reg[fbiAfuncFail].u += stats->afunc_fail;
+
+ /* apply emulation statistics */
+ v->stats.total_pixels_in += stats->pixels_in;
+ v->stats.total_pixels_out += stats->pixels_out;
+ v->stats.total_chroma_fail += stats->chroma_fail;
+ v->stats.total_zfunc_fail += stats->zfunc_fail;
+ v->stats.total_afunc_fail += stats->afunc_fail;
+ v->stats.total_clipped += stats->clip_fail;
+ v->stats.total_stippled += stats->stipple_count;
+}
+
+
+static void update_statistics(voodoo_state *v, int accumulate)
+{
+ int threadnum;
+
+ /* accumulate/reset statistics from all units */
+ for (threadnum = 0; threadnum < WORK_MAX_THREADS; threadnum++)
+ {
+ if (accumulate)
+ accumulate_statistics(v, &v->thread_stats[threadnum]);
+ memset(&v->thread_stats[threadnum], 0, sizeof(v->thread_stats[threadnum]));
+ }
+
+ /* accumulate/reset statistics from the LFB */
+ if (accumulate)
+ accumulate_statistics(v, &v->fbi.lfb_stats);
+ memset(&v->fbi.lfb_stats, 0, sizeof(v->fbi.lfb_stats));
+}
+
+
+
+/*************************************
+ *
+ * VBLANK management
+ *
+ *************************************/
+
+static void swap_buffers(voodoo_state *v)
+{
+ int count;
+
+ if (LOG_VBLANK_SWAP) logerror("--- swap_buffers @ %d\n", v->screen->vpos());
+
+ /* force a partial update */
+ v->screen->update_partial(v->screen->vpos());
+ v->fbi.video_changed = TRUE;
+
+ /* keep a history of swap intervals */
+ count = v->fbi.vblank_count;
+ if (count > 15)
+ count = 15;
+ v->reg[fbiSwapHistory].u = (v->reg[fbiSwapHistory].u << 4) | count;
+
+ /* rotate the buffers */
+ if (v->type <= TYPE_VOODOO_2)
+ {
+ if (v->type < TYPE_VOODOO_2 || !v->fbi.vblank_dont_swap)
+ {
+ if (v->fbi.rgboffs[2] == ~0)
+ {
+ v->fbi.frontbuf = 1 - v->fbi.frontbuf;
+ v->fbi.backbuf = 1 - v->fbi.frontbuf;
+ }
+ else
+ {
+ v->fbi.frontbuf = (v->fbi.frontbuf + 1) % 3;
+ v->fbi.backbuf = (v->fbi.frontbuf + 1) % 3;
+ }
+ }
+ }
+ else
+ v->fbi.rgboffs[0] = v->reg[leftOverlayBuf].u & v->fbi.mask & ~0x0f;
+
+ /* decrement the pending count and reset our state */
+ if (v->fbi.swaps_pending)
+ v->fbi.swaps_pending--;
+ v->fbi.vblank_count = 0;
+ v->fbi.vblank_swap_pending = FALSE;
+
+ /* reset the last_op_time to now and start processing the next command */
+ if (v->pci.op_pending)
+ {
+ v->pci.op_end_time = v->device->machine().time();
+ flush_fifos(v, v->pci.op_end_time);
+ }
+
+ /* we may be able to unstall now */
+ if (v->pci.stall_state != NOT_STALLED)
+ check_stalled_cpu(v, v->device->machine().time());
+
+ /* periodically log rasterizer info */
+ v->stats.swaps++;
+ if (LOG_RASTERIZERS && v->stats.swaps % 1000 == 0)
+ dump_rasterizer_stats(v);
+
+ /* update the statistics (debug) */
+ if (v->stats.display)
+ {
+ const rectangle &visible_area = v->screen->visible_area();
+ int screen_area = visible_area.width() * visible_area.height();
+ char *statsptr = v->stats.buffer;
+ int pixelcount;
+ int i;
+
+ update_statistics(v, TRUE);
+ pixelcount = v->stats.total_pixels_out;
+
+ statsptr += sprintf(statsptr, "Swap:%6d\n", v->stats.swaps);
+ statsptr += sprintf(statsptr, "Hist:%08X\n", v->reg[fbiSwapHistory].u);
+ statsptr += sprintf(statsptr, "Stal:%6d\n", v->stats.stalls);
+ statsptr += sprintf(statsptr, "Rend:%6d%%\n", pixelcount * 100 / screen_area);
+ statsptr += sprintf(statsptr, "Poly:%6d\n", v->stats.total_triangles);
+ statsptr += sprintf(statsptr, "PxIn:%6d\n", v->stats.total_pixels_in);
+ statsptr += sprintf(statsptr, "POut:%6d\n", v->stats.total_pixels_out);
+ statsptr += sprintf(statsptr, "Clip:%6d\n", v->stats.total_clipped);
+ statsptr += sprintf(statsptr, "Stip:%6d\n", v->stats.total_stippled);
+ statsptr += sprintf(statsptr, "Chro:%6d\n", v->stats.total_chroma_fail);
+ statsptr += sprintf(statsptr, "ZFun:%6d\n", v->stats.total_zfunc_fail);
+ statsptr += sprintf(statsptr, "AFun:%6d\n", v->stats.total_afunc_fail);
+ statsptr += sprintf(statsptr, "RegW:%6d\n", v->stats.reg_writes);
+ statsptr += sprintf(statsptr, "RegR:%6d\n", v->stats.reg_reads);
+ statsptr += sprintf(statsptr, "LFBW:%6d\n", v->stats.lfb_writes);
+ statsptr += sprintf(statsptr, "LFBR:%6d\n", v->stats.lfb_reads);
+ statsptr += sprintf(statsptr, "TexW:%6d\n", v->stats.tex_writes);
+ statsptr += sprintf(statsptr, "TexM:");
+ for (i = 0; i < 16; i++)
+ if (v->stats.texture_mode[i])
+ *statsptr++ = "0123456789ABCDEF"[i];
+ *statsptr = 0;
+ }
+
+ /* update statistics */
+ v->stats.stalls = 0;
+ v->stats.total_triangles = 0;
+ v->stats.total_pixels_in = 0;
+ v->stats.total_pixels_out = 0;
+ v->stats.total_chroma_fail = 0;
+ v->stats.total_zfunc_fail = 0;
+ v->stats.total_afunc_fail = 0;
+ v->stats.total_clipped = 0;
+ v->stats.total_stippled = 0;
+ v->stats.reg_writes = 0;
+ v->stats.reg_reads = 0;
+ v->stats.lfb_writes = 0;
+ v->stats.lfb_reads = 0;
+ v->stats.tex_writes = 0;
+ memset(v->stats.texture_mode, 0, sizeof(v->stats.texture_mode));
+}
+
+
+static void adjust_vblank_timer(voodoo_state *v)
+{
+ attotime vblank_period = v->screen->time_until_pos(v->fbi.vsyncscan);
+
+ /* if zero, adjust to next frame, otherwise we may get stuck in an infinite loop */
+ if (vblank_period == attotime::zero)
+ vblank_period = v->screen->frame_period();
+ v->fbi.vblank_timer->adjust(vblank_period);
+}
+
+
+static TIMER_CALLBACK( vblank_off_callback )
+{
+ voodoo_state *v = (voodoo_state *)ptr;
+
+ if (LOG_VBLANK_SWAP) logerror("--- vblank end\n");
+
+ /* set internal state and call the client */
+ v->fbi.vblank = FALSE;
+
+ // TODO: Vblank IRQ enable is VOODOO3 only?
+ if (v->type >= TYPE_VOODOO_3)
+ {
+ if (v->reg[intrCtrl].u & 0x8) // call IRQ handler if VSYNC interrupt (falling) is enabled
+ {
+ v->reg[intrCtrl].u |= 0x200; // VSYNC int (falling) active
+
+ if (!v->device->m_vblank.isnull())
+ v->device->m_vblank(FALSE);
+
+ }
+ }
+ else
+ {
+ if (!v->device->m_vblank.isnull())
+ v->device->m_vblank(FALSE);
+ }
+
+ /* go to the end of the next frame */
+ adjust_vblank_timer(v);
+}
+
+
+static TIMER_CALLBACK( vblank_callback )
+{
+ voodoo_state *v = (voodoo_state *)ptr;
+
+ if (LOG_VBLANK_SWAP) logerror("--- vblank start\n");
+
+ /* flush the pipes */
+ if (v->pci.op_pending)
+ {
+ if (LOG_VBLANK_SWAP) logerror("---- vblank flush begin\n");
+ flush_fifos(v, machine.time());
+ if (LOG_VBLANK_SWAP) logerror("---- vblank flush end\n");
+ }
+
+ /* increment the count */
+ v->fbi.vblank_count++;
+ if (v->fbi.vblank_count > 250)
+ v->fbi.vblank_count = 250;
+ if (LOG_VBLANK_SWAP) logerror("---- vblank count = %d", v->fbi.vblank_count);
+ if (v->fbi.vblank_swap_pending)
+ if (LOG_VBLANK_SWAP) logerror(" (target=%d)", v->fbi.vblank_swap);
+ if (LOG_VBLANK_SWAP) logerror("\n");
+
+ /* if we're past the swap count, do the swap */
+ if (v->fbi.vblank_swap_pending && v->fbi.vblank_count >= v->fbi.vblank_swap)
+ swap_buffers(v);
+
+ /* set a timer for the next off state */
+ machine.scheduler().timer_set(v->screen->time_until_pos(0), FUNC(vblank_off_callback), 0, v);
+
+ /* set internal state and call the client */
+ v->fbi.vblank = TRUE;
+
+ // TODO: Vblank IRQ enable is VOODOO3 only?
+ if (v->type >= TYPE_VOODOO_3)
+ {
+ if (v->reg[intrCtrl].u & 0x4) // call IRQ handler if VSYNC interrupt (rising) is enabled
+ {
+ v->reg[intrCtrl].u |= 0x100; // VSYNC int (rising) active
+
+ if (!v->device->m_vblank.isnull())
+ v->device->m_vblank(TRUE);
+ }
+ }
+ else
+ {
+ if (!v->device->m_vblank.isnull())
+ v->device->m_vblank(TRUE);
+ }
+}
+
+
+
+/*************************************
+ *
+ * Chip reset
+ *
+ *************************************/
+
+static void reset_counters(voodoo_state *v)
+{
+ update_statistics(v, FALSE);
+ v->reg[fbiPixelsIn].u = 0;
+ v->reg[fbiChromaFail].u = 0;
+ v->reg[fbiZfuncFail].u = 0;
+ v->reg[fbiAfuncFail].u = 0;
+ v->reg[fbiPixelsOut].u = 0;
+}
+
+
+static void soft_reset(voodoo_state *v)
+{
+ reset_counters(v);
+ v->reg[fbiTrianglesOut].u = 0;
+ fifo_reset(&v->fbi.fifo);
+ fifo_reset(&v->pci.fifo);
+}
+
+
+
+/*************************************
+ *
+ * Recompute video memory layout
+ *
+ *************************************/
+
+static void recompute_video_memory(voodoo_state *v)
+{
+ UINT32 buffer_pages = FBIINIT2_VIDEO_BUFFER_OFFSET(v->reg[fbiInit2].u);
+ UINT32 fifo_start_page = FBIINIT4_MEMORY_FIFO_START_ROW(v->reg[fbiInit4].u);
+ UINT32 fifo_last_page = FBIINIT4_MEMORY_FIFO_STOP_ROW(v->reg[fbiInit4].u);
+ UINT32 memory_config;
+ int buf;
+
+ /* memory config is determined differently between V1 and V2 */
+ memory_config = FBIINIT2_ENABLE_TRIPLE_BUF(v->reg[fbiInit2].u);
+ if (v->type == TYPE_VOODOO_2 && memory_config == 0)
+ memory_config = FBIINIT5_BUFFER_ALLOCATION(v->reg[fbiInit5].u);
+
+ /* tiles are 64x16/32; x_tiles specifies how many half-tiles */
+ v->fbi.tile_width = (v->type == TYPE_VOODOO_1) ? 64 : 32;
+ v->fbi.tile_height = (v->type == TYPE_VOODOO_1) ? 16 : 32;
+ v->fbi.x_tiles = FBIINIT1_X_VIDEO_TILES(v->reg[fbiInit1].u);
+ if (v->type == TYPE_VOODOO_2)
+ {
+ v->fbi.x_tiles = (v->fbi.x_tiles << 1) |
+ (FBIINIT1_X_VIDEO_TILES_BIT5(v->reg[fbiInit1].u) << 5) |
+ (FBIINIT6_X_VIDEO_TILES_BIT0(v->reg[fbiInit6].u));
+ }
+ v->fbi.rowpixels = v->fbi.tile_width * v->fbi.x_tiles;
+
+// logerror("VOODOO.%d.VIDMEM: buffer_pages=%X fifo=%X-%X tiles=%X rowpix=%d\n", v->index, buffer_pages, fifo_start_page, fifo_last_page, v->fbi.x_tiles, v->fbi.rowpixels);
+
+ /* first RGB buffer always starts at 0 */
+ v->fbi.rgboffs[0] = 0;
+
+ /* second RGB buffer starts immediately afterwards */
+ v->fbi.rgboffs[1] = buffer_pages * 0x1000;
+
+ /* remaining buffers are based on the config */
+ switch (memory_config)
+ {
+ case 3: /* reserved */
+ logerror("VOODOO.%d.ERROR:Unexpected memory configuration in recompute_video_memory!\n", v->index);
+
+ case 0: /* 2 color buffers, 1 aux buffer */
+ v->fbi.rgboffs[2] = ~0;
+ v->fbi.auxoffs = 2 * buffer_pages * 0x1000;
+ break;
+
+ case 1: /* 3 color buffers, 0 aux buffers */
+ v->fbi.rgboffs[2] = 2 * buffer_pages * 0x1000;
+ v->fbi.auxoffs = ~0;
+ break;
+
+ case 2: /* 3 color buffers, 1 aux buffers */
+ v->fbi.rgboffs[2] = 2 * buffer_pages * 0x1000;
+ v->fbi.auxoffs = 3 * buffer_pages * 0x1000;
+ break;
+ }
+
+ /* clamp the RGB buffers to video memory */
+ for (buf = 0; buf < 3; buf++)
+ if (v->fbi.rgboffs[buf] != ~0 && v->fbi.rgboffs[buf] > v->fbi.mask)
+ v->fbi.rgboffs[buf] = v->fbi.mask;
+
+ /* clamp the aux buffer to video memory */
+ if (v->fbi.auxoffs != ~0 && v->fbi.auxoffs > v->fbi.mask)
+ v->fbi.auxoffs = v->fbi.mask;
+
+/* osd_printf_debug("rgb[0] = %08X rgb[1] = %08X rgb[2] = %08X aux = %08X\n",
+ v->fbi.rgboffs[0], v->fbi.rgboffs[1], v->fbi.rgboffs[2], v->fbi.auxoffs);*/
+
+ /* compute the memory FIFO location and size */
+ if (fifo_last_page > v->fbi.mask / 0x1000)
+ fifo_last_page = v->fbi.mask / 0x1000;
+
+ /* is it valid and enabled? */
+ if (fifo_start_page <= fifo_last_page && FBIINIT0_ENABLE_MEMORY_FIFO(v->reg[fbiInit0].u))
+ {
+ v->fbi.fifo.base = (UINT32 *)(v->fbi.ram + fifo_start_page * 0x1000);
+ v->fbi.fifo.size = (fifo_last_page + 1 - fifo_start_page) * 0x1000 / 4;
+ if (v->fbi.fifo.size > 65536*2)
+ v->fbi.fifo.size = 65536*2;
+ }
+
+ /* if not, disable the FIFO */
+ else
+ {
+ v->fbi.fifo.base = NULL;
+ v->fbi.fifo.size = 0;
+ }
+
+ /* reset the FIFO */
+ fifo_reset(&v->fbi.fifo);
+
+ /* reset our front/back buffers if they are out of range */
+ if (v->fbi.rgboffs[2] == ~0)
+ {
+ if (v->fbi.frontbuf == 2)
+ v->fbi.frontbuf = 0;
+ if (v->fbi.backbuf == 2)
+ v->fbi.backbuf = 0;
+ }
+}
+
+
+
+/*************************************
+ *
+ * NCC table management
+ *
+ *************************************/
+
+static void ncc_table_write(ncc_table *n, offs_t regnum, UINT32 data)
+{
+ /* I/Q entries reference the plaette if the high bit is set */
+ if (regnum >= 4 && (data & 0x80000000) && n->palette)
+ {
+ int index = ((data >> 23) & 0xfe) | (regnum & 1);
+
+ /* set the ARGB for this palette index */
+ n->palette[index] = 0xff000000 | data;
+
+ /* if we have an ARGB palette as well, compute its value */
+ if (n->palettea)
+ {
+ int a = ((data >> 16) & 0xfc) | ((data >> 22) & 0x03);
+ int r = ((data >> 10) & 0xfc) | ((data >> 16) & 0x03);
+ int g = ((data >> 4) & 0xfc) | ((data >> 10) & 0x03);
+ int b = ((data << 2) & 0xfc) | ((data >> 4) & 0x03);
+ n->palettea[index] = rgb_t(a, r, g, b);
+ }
+
+ /* this doesn't dirty the table or go to the registers, so bail */
+ return;
+ }
+
+ /* if the register matches, don't update */
+ if (data == n->reg[regnum].u)
+ return;
+ n->reg[regnum].u = data;
+
+ /* first four entries are packed Y values */
+ if (regnum < 4)
+ {
+ regnum *= 4;
+ n->y[regnum+0] = (data >> 0) & 0xff;
+ n->y[regnum+1] = (data >> 8) & 0xff;
+ n->y[regnum+2] = (data >> 16) & 0xff;
+ n->y[regnum+3] = (data >> 24) & 0xff;
+ }
+
+ /* the second four entries are the I RGB values */
+ else if (regnum < 8)
+ {
+ regnum &= 3;
+ n->ir[regnum] = (INT32)(data << 5) >> 23;
+ n->ig[regnum] = (INT32)(data << 14) >> 23;
+ n->ib[regnum] = (INT32)(data << 23) >> 23;
+ }
+
+ /* the final four entries are the Q RGB values */
+ else
+ {
+ regnum &= 3;
+ n->qr[regnum] = (INT32)(data << 5) >> 23;
+ n->qg[regnum] = (INT32)(data << 14) >> 23;
+ n->qb[regnum] = (INT32)(data << 23) >> 23;
+ }
+
+ /* mark the table dirty */
+ n->dirty = TRUE;
+}
+
+
+static void ncc_table_update(ncc_table *n)
+{
+ int r, g, b, i;
+
+ /* generte all 256 possibilities */
+ for (i = 0; i < 256; i++)
+ {
+ int vi = (i >> 2) & 0x03;
+ int vq = (i >> 0) & 0x03;
+
+ /* start with the intensity */
+ r = g = b = n->y[(i >> 4) & 0x0f];
+
+ /* add the coloring */
+ r += n->ir[vi] + n->qr[vq];
+ g += n->ig[vi] + n->qg[vq];
+ b += n->ib[vi] + n->qb[vq];
+
+ /* clamp */
+ CLAMP(r, 0, 255);
+ CLAMP(g, 0, 255);
+ CLAMP(b, 0, 255);
+
+ /* fill in the table */
+ n->texel[i] = rgb_t(0xff, r, g, b);
+ }
+
+ /* no longer dirty */
+ n->dirty = FALSE;
+}
+
+
+
+/*************************************
+ *
+ * Faux DAC implementation
+ *
+ *************************************/
+
+static void dacdata_w(dac_state *d, UINT8 regnum, UINT8 data)
+{
+ d->reg[regnum] = data;
+}
+
+
+static void dacdata_r(dac_state *d, UINT8 regnum)
+{
+ UINT8 result = 0xff;
+
+ /* switch off the DAC register requested */
+ switch (regnum)
+ {
+ case 5:
+ /* this is just to make startup happy */
+ switch (d->reg[7])
+ {
+ case 0x01: result = 0x55; break;
+ case 0x07: result = 0x71; break;
+ case 0x0b: result = 0x79; break;
+ }
+ break;
+
+ default:
+ result = d->reg[regnum];
+ break;
+ }
+
+ /* remember the read result; it is fetched elsewhere */
+ d->read_result = result;
+}
+
+
+
+/*************************************
+ *
+ * Texuture parameter computation
+ *
+ *************************************/
+
+static void recompute_texture_params(tmu_state *t)
+{
+ int bppscale;
+ UINT32 base;
+ int lod;
+
+ /* extract LOD parameters */
+ t->lodmin = TEXLOD_LODMIN(t->reg[tLOD].u) << 6;
+ t->lodmax = TEXLOD_LODMAX(t->reg[tLOD].u) << 6;
+ t->lodbias = (INT8)(TEXLOD_LODBIAS(t->reg[tLOD].u) << 2) << 4;
+
+ /* determine which LODs are present */
+ t->lodmask = 0x1ff;
+ if (TEXLOD_LOD_TSPLIT(t->reg[tLOD].u))
+ {
+ if (!TEXLOD_LOD_ODD(t->reg[tLOD].u))
+ t->lodmask = 0x155;
+ else
+ t->lodmask = 0x0aa;
+ }
+
+ /* determine base texture width/height */
+ t->wmask = t->hmask = 0xff;
+ if (TEXLOD_LOD_S_IS_WIDER(t->reg[tLOD].u))
+ t->hmask >>= TEXLOD_LOD_ASPECT(t->reg[tLOD].u);
+ else
+ t->wmask >>= TEXLOD_LOD_ASPECT(t->reg[tLOD].u);
+
+ /* determine the bpp of the texture */
+ bppscale = TEXMODE_FORMAT(t->reg[textureMode].u) >> 3;
+
+ /* start with the base of LOD 0 */
+ if (t->texaddr_shift == 0 && (t->reg[texBaseAddr].u & 1))
+ osd_printf_debug("Tiled texture\n");
+ base = (t->reg[texBaseAddr].u & t->texaddr_mask) << t->texaddr_shift;
+ t->lodoffset[0] = base & t->mask;
+
+ /* LODs 1-3 are different depending on whether we are in multitex mode */
+ /* Several Voodoo 2 games leave the upper bits of TLOD == 0xff, meaning we think */
+ /* they want multitex mode when they really don't -- disable for now */
+ // Enable for Voodoo 3 or Viper breaks - VL.
+ if (TEXLOD_TMULTIBASEADDR(t->reg[tLOD].u))
+ {
+ base = (t->reg[texBaseAddr_1].u & t->texaddr_mask) << t->texaddr_shift;
+ t->lodoffset[1] = base & t->mask;
+ base = (t->reg[texBaseAddr_2].u & t->texaddr_mask) << t->texaddr_shift;
+ t->lodoffset[2] = base & t->mask;
+ base = (t->reg[texBaseAddr_3_8].u & t->texaddr_mask) << t->texaddr_shift;
+ t->lodoffset[3] = base & t->mask;
+ }
+ else
+ {
+ if (t->lodmask & (1 << 0))
+ base += (((t->wmask >> 0) + 1) * ((t->hmask >> 0) + 1)) << bppscale;
+ t->lodoffset[1] = base & t->mask;
+ if (t->lodmask & (1 << 1))
+ base += (((t->wmask >> 1) + 1) * ((t->hmask >> 1) + 1)) << bppscale;
+ t->lodoffset[2] = base & t->mask;
+ if (t->lodmask & (1 << 2))
+ base += (((t->wmask >> 2) + 1) * ((t->hmask >> 2) + 1)) << bppscale;
+ t->lodoffset[3] = base & t->mask;
+ }
+
+ /* remaining LODs make sense */
+ for (lod = 4; lod <= 8; lod++)
+ {
+ if (t->lodmask & (1 << (lod - 1)))
+ {
+ UINT32 size = ((t->wmask >> (lod - 1)) + 1) * ((t->hmask >> (lod - 1)) + 1);
+ if (size < 4) size = 4;
+ base += size << bppscale;
+ }
+ t->lodoffset[lod] = base & t->mask;
+ }
+
+ /* set the NCC lookup appropriately */
+ t->texel[1] = t->texel[9] = t->ncc[TEXMODE_NCC_TABLE_SELECT(t->reg[textureMode].u)].texel;
+
+ /* pick the lookup table */
+ t->lookup = t->texel[TEXMODE_FORMAT(t->reg[textureMode].u)];
+
+ /* compute the detail parameters */
+ t->detailmax = TEXDETAIL_DETAIL_MAX(t->reg[tDetail].u);
+ t->detailbias = (INT8)(TEXDETAIL_DETAIL_BIAS(t->reg[tDetail].u) << 2) << 6;
+ t->detailscale = TEXDETAIL_DETAIL_SCALE(t->reg[tDetail].u);
+
+ /* no longer dirty */
+ t->regdirty = FALSE;
+
+ /* check for separate RGBA filtering */
+ if (TEXDETAIL_SEPARATE_RGBA_FILTER(t->reg[tDetail].u))
+ fatalerror("Separate RGBA filters!\n");
+}
+
+
+INLINE INT32 prepare_tmu(tmu_state *t)
+{
+ INT64 texdx, texdy;
+ INT32 lodbase;
+
+ /* if the texture parameters are dirty, update them */
+ if (t->regdirty)
+ {
+ recompute_texture_params(t);
+
+ /* ensure that the NCC tables are up to date */
+ if ((TEXMODE_FORMAT(t->reg[textureMode].u) & 7) == 1)
+ {
+ ncc_table *n = &t->ncc[TEXMODE_NCC_TABLE_SELECT(t->reg[textureMode].u)];
+ t->texel[1] = t->texel[9] = n->texel;
+ if (n->dirty)
+ ncc_table_update(n);
+ }
+ }
+
+ /* compute (ds^2 + dt^2) in both X and Y as 28.36 numbers */
+ texdx = (INT64)(t->dsdx >> 14) * (INT64)(t->dsdx >> 14) + (INT64)(t->dtdx >> 14) * (INT64)(t->dtdx >> 14);
+ texdy = (INT64)(t->dsdy >> 14) * (INT64)(t->dsdy >> 14) + (INT64)(t->dtdy >> 14) * (INT64)(t->dtdy >> 14);
+
+ /* pick whichever is larger and shift off some high bits -> 28.20 */
+ if (texdx < texdy)
+ texdx = texdy;
+ texdx >>= 16;
+
+ /* use our fast reciprocal/log on this value; it expects input as a */
+ /* 16.32 number, and returns the log of the reciprocal, so we have to */
+ /* adjust the result: negative to get the log of the original value */
+ /* plus 12 to account for the extra exponent, and divided by 2 to */
+ /* get the log of the square root of texdx */
+ #if USE_FAST_RECIP == 1
+ (void)fast_reciplog(texdx, &lodbase);
+ return (-lodbase + (12 << 8)) / 2;
+ #else
+ double tmpTex = texdx;
+ lodbase = new_log2(tmpTex);
+ return (lodbase + (12 << 8)) / 2;
+ #endif
+}
+
+
+
+/*************************************
+ *
+ * Command FIFO depth computation
+ *
+ *************************************/
+
+static int cmdfifo_compute_expected_depth(voodoo_state *v, cmdfifo_info *f)
+{
+ UINT32 *fifobase = (UINT32 *)v->fbi.ram;
+ UINT32 readptr = f->rdptr;
+ UINT32 command = fifobase[readptr / 4];
+ int i, count = 0;
+
+ /* low 3 bits specify the packet type */
+ switch (command & 7)
+ {
+ /*
+ Packet type 0: 1 or 2 words
+
+ Word Bits
+ 0 31:29 = reserved
+ 0 28:6 = Address [24:2]
+ 0 5:3 = Function (0 = NOP, 1 = JSR, 2 = RET, 3 = JMP LOCAL, 4 = JMP AGP)
+ 0 2:0 = Packet type (0)
+ 1 31:11 = reserved (JMP AGP only)
+ 1 10:0 = Address [35:25]
+ */
+ case 0:
+ if (((command >> 3) & 7) == 4)
+ return 2;
+ return 1;
+
+ /*
+ Packet type 1: 1 + N words
+
+ Word Bits
+ 0 31:16 = Number of words
+ 0 15 = Increment?
+ 0 14:3 = Register base
+ 0 2:0 = Packet type (1)
+ 1 31:0 = Data word
+ */
+ case 1:
+ return 1 + (command >> 16);
+
+ /*
+ Packet type 2: 1 + N words
+
+ Word Bits
+ 0 31:3 = 2D Register mask
+ 0 2:0 = Packet type (2)
+ 1 31:0 = Data word
+ */
+ case 2:
+ for (i = 3; i <= 31; i++)
+ if (command & (1 << i)) count++;
+ return 1 + count;
+
+ /*
+ Packet type 3: 1 + N words
+
+ Word Bits
+ 0 31:29 = Number of dummy entries following the data
+ 0 28 = Packed color data?
+ 0 25 = Disable ping pong sign correction (0=normal, 1=disable)
+ 0 24 = Culling sign (0=positive, 1=negative)
+ 0 23 = Enable culling (0=disable, 1=enable)
+ 0 22 = Strip mode (0=strip, 1=fan)
+ 0 17 = Setup S1 and T1
+ 0 16 = Setup W1
+ 0 15 = Setup S0 and T0
+ 0 14 = Setup W0
+ 0 13 = Setup Wb
+ 0 12 = Setup Z
+ 0 11 = Setup Alpha
+ 0 10 = Setup RGB
+ 0 9:6 = Number of vertices
+ 0 5:3 = Command (0=Independent tris, 1=Start new strip, 2=Continue strip)
+ 0 2:0 = Packet type (3)
+ 1 31:0 = Data word
+ */
+ case 3:
+ count = 2; /* X/Y */
+ if (command & (1 << 28))
+ {
+ if (command & (3 << 10)) count++; /* ARGB */
+ }
+ else
+ {
+ if (command & (1 << 10)) count += 3; /* RGB */
+ if (command & (1 << 11)) count++; /* A */
+ }
+ if (command & (1 << 12)) count++; /* Z */
+ if (command & (1 << 13)) count++; /* Wb */
+ if (command & (1 << 14)) count++; /* W0 */
+ if (command & (1 << 15)) count += 2; /* S0/T0 */
+ if (command & (1 << 16)) count++; /* W1 */
+ if (command & (1 << 17)) count += 2; /* S1/T1 */
+ count *= (command >> 6) & 15; /* numverts */
+ return 1 + count + (command >> 29);
+
+ /*
+ Packet type 4: 1 + N words
+
+ Word Bits
+ 0 31:29 = Number of dummy entries following the data
+ 0 28:15 = General register mask
+ 0 14:3 = Register base
+ 0 2:0 = Packet type (4)
+ 1 31:0 = Data word
+ */
+ case 4:
+ for (i = 15; i <= 28; i++)
+ if (command & (1 << i)) count++;
+ return 1 + count + (command >> 29);
+
+ /*
+ Packet type 5: 2 + N words
+
+ Word Bits
+ 0 31:30 = Space (0,1=reserved, 2=LFB, 3=texture)
+ 0 29:26 = Byte disable W2
+ 0 25:22 = Byte disable WN
+ 0 21:3 = Num words
+ 0 2:0 = Packet type (5)
+ 1 31:30 = Reserved
+ 1 29:0 = Base address [24:0]
+ 2 31:0 = Data word
+ */
+ case 5:
+ return 2 + ((command >> 3) & 0x7ffff);
+
+ default:
+ osd_printf_debug("UNKNOWN PACKET TYPE %d\n", command & 7);
+ return 1;
+ }
+}
+
+
+
+/*************************************
+ *
+ * Command FIFO execution
+ *
+ *************************************/
+
+static UINT32 cmdfifo_execute(voodoo_state *v, cmdfifo_info *f)
+{
+ UINT32 *fifobase = (UINT32 *)v->fbi.ram;
+ UINT32 readptr = f->rdptr;
+ UINT32 *src = &fifobase[readptr / 4];
+ UINT32 command = *src++;
+ int count, inc, code, i;
+ setup_vertex svert = {0};
+ offs_t target;
+ int cycles = 0;
+
+ switch (command & 7)
+ {
+ /*
+ Packet type 0: 1 or 2 words
+
+ Word Bits
+ 0 31:29 = reserved
+ 0 28:6 = Address [24:2]
+ 0 5:3 = Function (0 = NOP, 1 = JSR, 2 = RET, 3 = JMP LOCAL, 4 = JMP AGP)
+ 0 2:0 = Packet type (0)
+ 1 31:11 = reserved (JMP AGP only)
+ 1 10:0 = Address [35:25]
+ */
+ case 0:
+
+ /* extract parameters */
+ target = (command >> 4) & 0x1fffffc;
+
+ /* switch off of the specific command */
+ switch ((command >> 3) & 7)
+ {
+ case 0: /* NOP */
+ if (LOG_CMDFIFO) logerror(" NOP\n");
+ break;
+
+ case 1: /* JSR */
+ if (LOG_CMDFIFO) logerror(" JSR $%06X\n", target);
+ osd_printf_debug("JSR in CMDFIFO!\n");
+ src = &fifobase[target / 4];
+ break;
+
+ case 2: /* RET */
+ if (LOG_CMDFIFO) logerror(" RET $%06X\n", target);
+ fatalerror("RET in CMDFIFO!\n");
+
+ case 3: /* JMP LOCAL FRAME BUFFER */
+ if (LOG_CMDFIFO) logerror(" JMP LOCAL FRAMEBUF $%06X\n", target);
+ src = &fifobase[target / 4];
+ break;
+
+ case 4: /* JMP AGP */
+ if (LOG_CMDFIFO) logerror(" JMP AGP $%06X\n", target);
+ fatalerror("JMP AGP in CMDFIFO!\n");
+ src = &fifobase[target / 4];
+ break;
+
+ default:
+ osd_printf_debug("INVALID JUMP COMMAND!\n");
+ fatalerror(" INVALID JUMP COMMAND\n");
+ }
+ break;
+
+ /*
+ Packet type 1: 1 + N words
+
+ Word Bits
+ 0 31:16 = Number of words
+ 0 15 = Increment?
+ 0 14:3 = Register base
+ 0 2:0 = Packet type (1)
+ 1 31:0 = Data word
+ */
+ case 1:
+
+ /* extract parameters */
+ count = command >> 16;
+ inc = (command >> 15) & 1;
+ target = (command >> 3) & 0xfff;
+
+ if (LOG_CMDFIFO) logerror(" PACKET TYPE 1: count=%d inc=%d reg=%04X\n", count, inc, target);
+
+ if (v->type >= TYPE_VOODOO_BANSHEE && (target & 0x800))
+ {
+ // Banshee/Voodoo3 2D register writes
+
+ /* loop over all registers and write them one at a time */
+ for (i = 0; i < count; i++, target += inc)
+ {
+ cycles += banshee_2d_w(v, target & 0xff, *src);
+ //logerror(" 2d reg: %03x = %08X\n", target & 0x7ff, *src);
+ src++;
+ }
+ }
+ else
+ {
+ /* loop over all registers and write them one at a time */
+ for (i = 0; i < count; i++, target += inc)
+ cycles += register_w(v, target, *src++);
+ }
+ break;
+
+ /*
+ Packet type 2: 1 + N words
+
+ Word Bits
+ 0 31:3 = 2D Register mask
+ 0 2:0 = Packet type (2)
+ 1 31:0 = Data word
+ */
+ case 2:
+ if (LOG_CMDFIFO) logerror(" PACKET TYPE 2: mask=%X\n", (command >> 3) & 0x1ffffff);
+
+ /* loop over all registers and write them one at a time */
+ for (i = 3; i <= 31; i++)
+ if (command & (1 << i))
+ cycles += register_w(v, banshee2D_clip0Min + (i - 3), *src++);
+ break;
+
+ /*
+ Packet type 3: 1 + N words
+
+ Word Bits
+ 0 31:29 = Number of dummy entries following the data
+ 0 28 = Packed color data?
+ 0 25 = Disable ping pong sign correction (0=normal, 1=disable)
+ 0 24 = Culling sign (0=positive, 1=negative)
+ 0 23 = Enable culling (0=disable, 1=enable)
+ 0 22 = Strip mode (0=strip, 1=fan)
+ 0 17 = Setup S1 and T1
+ 0 16 = Setup W1
+ 0 15 = Setup S0 and T0
+ 0 14 = Setup W0
+ 0 13 = Setup Wb
+ 0 12 = Setup Z
+ 0 11 = Setup Alpha
+ 0 10 = Setup RGB
+ 0 9:6 = Number of vertices
+ 0 5:3 = Command (0=Independent tris, 1=Start new strip, 2=Continue strip)
+ 0 2:0 = Packet type (3)
+ 1 31:0 = Data word
+ */
+ case 3:
+
+ /* extract parameters */
+ count = (command >> 6) & 15;
+ code = (command >> 3) & 7;
+
+ if (LOG_CMDFIFO) logerror(" PACKET TYPE 3: count=%d code=%d mask=%03X smode=%02X pc=%d\n", count, code, (command >> 10) & 0xfff, (command >> 22) & 0x3f, (command >> 28) & 1);
+
+ /* copy relevant bits into the setup mode register */
+ v->reg[sSetupMode].u = ((command >> 10) & 0xff) | ((command >> 6) & 0xf0000);
+
+ /* loop over triangles */
+ for (i = 0; i < count; i++)
+ {
+ /* always extract X/Y */
+ svert.x = *(float *)src++;
+ svert.y = *(float *)src++;
+
+ /* load ARGB values if packed */
+ if (command & (1 << 28))
+ {
+ if (command & (3 << 10))
+ {
+ rgb_t argb = *src++;
+ if (command & (1 << 10))
+ {
+ svert.r = argb.r();
+ svert.g = argb.g();
+ svert.b = argb.b();
+ }
+ if (command & (1 << 11))
+ svert.a = argb.a();
+ }
+ }
+
+ /* load ARGB values if not packed */
+ else
+ {
+ if (command & (1 << 10))
+ {
+ svert.r = *(float *)src++;
+ svert.g = *(float *)src++;
+ svert.b = *(float *)src++;
+ }
+ if (command & (1 << 11))
+ svert.a = *(float *)src++;
+ }
+
+ /* load Z and Wb values */
+ if (command & (1 << 12))
+ svert.z = *(float *)src++;
+ if (command & (1 << 13))
+ svert.wb = *(float *)src++;
+
+ /* load W0, S0, T0 values */
+ if (command & (1 << 14))
+ svert.w0 = *(float *)src++;
+ if (command & (1 << 15))
+ {
+ svert.s0 = *(float *)src++;
+ svert.t0 = *(float *)src++;
+ }
+
+ /* load W1, S1, T1 values */
+ if (command & (1 << 16))
+ svert.w1 = *(float *)src++;
+ if (command & (1 << 17))
+ {
+ svert.s1 = *(float *)src++;
+ svert.t1 = *(float *)src++;
+ }
+
+ /* if we're starting a new strip, or if this is the first of a set of verts */
+ /* for a series of individual triangles, initialize all the verts */
+ if ((code == 1 && i == 0) || (code == 0 && i % 3 == 0))
+ {
+ v->fbi.sverts = 1;
+ v->fbi.svert[0] = v->fbi.svert[1] = v->fbi.svert[2] = svert;
+ }
+
+ /* otherwise, add this to the list */
+ else
+ {
+ /* for strip mode, shuffle vertex 1 down to 0 */
+ if (!(command & (1 << 22)))
+ v->fbi.svert[0] = v->fbi.svert[1];
+
+ /* copy 2 down to 1 and add our new one regardless */
+ v->fbi.svert[1] = v->fbi.svert[2];
+ v->fbi.svert[2] = svert;
+
+ /* if we have enough, draw */
+ if (++v->fbi.sverts >= 3)
+ cycles += setup_and_draw_triangle(v);
+ }
+ }
+
+ /* account for the extra dummy words */
+ src += command >> 29;
+ break;
+
+ /*
+ Packet type 4: 1 + N words
+
+ Word Bits
+ 0 31:29 = Number of dummy entries following the data
+ 0 28:15 = General register mask
+ 0 14:3 = Register base
+ 0 2:0 = Packet type (4)
+ 1 31:0 = Data word
+ */
+ case 4:
+
+ /* extract parameters */
+ target = (command >> 3) & 0xfff;
+
+ if (LOG_CMDFIFO) logerror(" PACKET TYPE 4: mask=%X reg=%04X pad=%d\n", (command >> 15) & 0x3fff, target, command >> 29);
+
+ if (v->type >= TYPE_VOODOO_BANSHEE && (target & 0x800))
+ {
+ // Banshee/Voodoo3 2D register writes
+
+ /* loop over all registers and write them one at a time */
+ target &= 0xff;
+ for (i = 15; i <= 28; i++)
+ {
+ if (command & (1 << i))
+ {
+ cycles += banshee_2d_w(v, target + (i - 15), *src);
+ //logerror(" 2d reg: %03x = %08X\n", target & 0x7ff, *src);
+ src++;
+ }
+ }
+ }
+ else
+ {
+ /* loop over all registers and write them one at a time */
+ for (i = 15; i <= 28; i++)
+ if (command & (1 << i))
+ cycles += register_w(v, target + (i - 15), *src++);
+ }
+
+ /* account for the extra dummy words */
+ src += command >> 29;
+ break;
+
+ /*
+ Packet type 5: 2 + N words
+
+ Word Bits
+ 0 31:30 = Space (0,1=reserved, 2=LFB, 3=texture)
+ 0 29:26 = Byte disable W2
+ 0 25:22 = Byte disable WN
+ 0 21:3 = Num words
+ 0 2:0 = Packet type (5)
+ 1 31:30 = Reserved
+ 1 29:0 = Base address [24:0]
+ 2 31:0 = Data word
+ */
+ case 5:
+
+ /* extract parameters */
+ count = (command >> 3) & 0x7ffff;
+ target = *src++ / 4;
+
+ /* handle LFB writes */
+ switch (command >> 30)
+ {
+ case 0: // Linear FB
+ {
+ if (LOG_CMDFIFO) logerror(" PACKET TYPE 5: FB count=%d dest=%08X bd2=%X bdN=%X\n", count, target, (command >> 26) & 15, (command >> 22) & 15);
+
+ UINT32 addr = target * 4;
+ for (i=0; i < count; i++)
+ {
+ UINT32 data = *src++;
+
+ v->fbi.ram[BYTE_XOR_LE(addr + 0)] = (UINT8)(data);
+ v->fbi.ram[BYTE_XOR_LE(addr + 1)] = (UINT8)(data >> 8);
+ v->fbi.ram[BYTE_XOR_LE(addr + 2)] = (UINT8)(data >> 16);
+ v->fbi.ram[BYTE_XOR_LE(addr + 3)] = (UINT8)(data >> 24);
+
+ addr += 4;
+ }
+ break;
+ }
+ case 2: // 3D LFB
+ {
+ if (LOG_CMDFIFO) logerror(" PACKET TYPE 5: 3D LFB count=%d dest=%08X bd2=%X bdN=%X\n", count, target, (command >> 26) & 15, (command >> 22) & 15);
+
+ /* loop over words */
+ for (i = 0; i < count; i++)
+ cycles += lfb_w(v, target++, *src++, 0xffffffff);
+
+ break;
+ }
+
+ case 1: // Planar YUV
+ {
+ // TODO
+
+ /* just update the pointers for now */
+ for (i = 0; i < count; i++)
+ {
+ target++;
+ src++;
+ }
+
+ break;
+ }
+
+ case 3: // Texture Port
+ {
+ if (LOG_CMDFIFO) logerror(" PACKET TYPE 5: textureRAM count=%d dest=%08X bd2=%X bdN=%X\n", count, target, (command >> 26) & 15, (command >> 22) & 15);
+
+ /* loop over words */
+ for (i = 0; i < count; i++)
+ cycles += texture_w(v, target++, *src++);
+
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ fprintf(stderr, "PACKET TYPE %d\n", command & 7);
+ break;
+ }
+
+ /* by default just update the read pointer past all the data we consumed */
+ f->rdptr = 4 * (src - fifobase);
+ return cycles;
+}
+
+
+
+/*************************************
+ *
+ * Handle execution if we're ready
+ *
+ *************************************/
+
+static INT32 cmdfifo_execute_if_ready(voodoo_state *v, cmdfifo_info *f)
+{
+ int needed_depth;
+ int cycles;
+
+ /* all CMDFIFO commands need at least one word */
+ if (f->depth == 0)
+ return -1;
+
+ /* see if we have enough for the current command */
+ needed_depth = cmdfifo_compute_expected_depth(v, f);
+ if (f->depth < needed_depth)
+ return -1;
+
+ /* execute */
+ cycles = cmdfifo_execute(v, f);
+ f->depth -= needed_depth;
+ return cycles;
+}
+
+
+
+/*************************************
+ *
+ * Handle writes to the CMD FIFO
+ *
+ *************************************/
+
+static void cmdfifo_w(voodoo_state *v, cmdfifo_info *f, offs_t offset, UINT32 data)
+{
+ UINT32 addr = f->base + offset * 4;
+ UINT32 *fifobase = (UINT32 *)v->fbi.ram;
+
+ if (LOG_CMDFIFO_VERBOSE) logerror("CMDFIFO_w(%04X) = %08X\n", offset, data);
+
+ /* write the data */
+ if (addr < f->end)
+ fifobase[addr / 4] = data;
+
+ /* count holes? */
+ if (f->count_holes)
+ {
+ /* in-order, no holes */
+ if (f->holes == 0 && addr == f->amin + 4)
+ {
+ f->amin = f->amax = addr;
+ f->depth++;
+ }
+
+ /* out-of-order, below the minimum */
+ else if (addr < f->amin)
+ {
+ if (f->holes != 0)
+ logerror("Unexpected CMDFIFO: AMin=%08X AMax=%08X Holes=%d WroteTo:%08X\n",
+ f->amin, f->amax, f->holes, addr);
+ //f->amin = f->amax = addr;
+ f->holes += (addr - f->base) / 4;
+ f->amin = f->base;
+ f->amax = addr;
+
+ f->depth++;
+ }
+
+ /* out-of-order, but within the min-max range */
+ else if (addr < f->amax)
+ {
+ f->holes--;
+ if (f->holes == 0)
+ {
+ f->depth += (f->amax - f->amin) / 4;
+ f->amin = f->amax;
+ }
+ }
+
+ /* out-of-order, bumping max */
+ else
+ {
+ f->holes += (addr - f->amax) / 4 - 1;
+ f->amax = addr;
+ }
+ }
+
+ /* execute if we can */
+ if (!v->pci.op_pending)
+ {
+ INT32 cycles = cmdfifo_execute_if_ready(v, f);
+ if (cycles > 0)
+ {
+ v->pci.op_pending = TRUE;
+ v->pci.op_end_time = v->device->machine().time() + attotime(0, (attoseconds_t)cycles * v->attoseconds_per_cycle);
+
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:direct write start at %d.%08X%08X end at %d.%08X%08X\n", v->index,
+ v->device->machine().time().seconds(), (UINT32)(v->device->machine().time().attoseconds() >> 32), (UINT32)v->device->machine().time().attoseconds(),
+ v->pci.op_end_time.seconds(), (UINT32)(v->pci.op_end_time.attoseconds() >> 32), (UINT32)v->pci.op_end_time.attoseconds());
+ }
+ }
+}
+
+
+
+/*************************************
+ *
+ * Stall the active cpu until we are
+ * ready
+ *
+ *************************************/
+
+static TIMER_CALLBACK( stall_cpu_callback )
+{
+ check_stalled_cpu((voodoo_state *)ptr, machine.time());
+}
+
+
+static void check_stalled_cpu(voodoo_state *v, attotime current_time)
+{
+ int resume = FALSE;
+
+ /* flush anything we can */
+ if (v->pci.op_pending)
+ flush_fifos(v, current_time);
+
+ /* if we're just stalled until the LWM is passed, see if we're ok now */
+ if (v->pci.stall_state == STALLED_UNTIL_FIFO_LWM)
+ {
+ /* if there's room in the memory FIFO now, we can proceed */
+ if (FBIINIT0_ENABLE_MEMORY_FIFO(v->reg[fbiInit0].u))
+ {
+ if (fifo_items(&v->fbi.fifo) < 2 * 32 * FBIINIT0_MEMORY_FIFO_HWM(v->reg[fbiInit0].u))
+ resume = TRUE;
+ }
+ else if (fifo_space(&v->pci.fifo) > 2 * FBIINIT0_PCI_FIFO_LWM(v->reg[fbiInit0].u))
+ resume = TRUE;
+ }
+
+ /* if we're stalled until the FIFOs are empty, check now */
+ else if (v->pci.stall_state == STALLED_UNTIL_FIFO_EMPTY)
+ {
+ if (FBIINIT0_ENABLE_MEMORY_FIFO(v->reg[fbiInit0].u))
+ {
+ if (fifo_empty(&v->fbi.fifo) && fifo_empty(&v->pci.fifo))
+ resume = TRUE;
+ }
+ else if (fifo_empty(&v->pci.fifo))
+ resume = TRUE;
+ }
+
+ /* resume if necessary */
+ if (resume || !v->pci.op_pending)
+ {
+ if (LOG_FIFO) logerror("VOODOO.%d.FIFO:Stall condition cleared; resuming\n", v->index);
+ v->pci.stall_state = NOT_STALLED;
+
+ /* either call the callback, or trigger the trigger */
+ if (!v->device->m_stall.isnull())
+ v->device->m_stall(FALSE);
+ else
+ v->device->machine().scheduler().trigger(v->trigger);
+ }
+
+ /* if not, set a timer for the next one */
+ else
+ {
+ v->pci.continue_timer->adjust(v->pci.op_end_time - current_time);
+ }
+}
+
+
+static void stall_cpu(voodoo_state *v, int state, attotime current_time)
+{
+ /* sanity check */
+ if (!v->pci.op_pending) fatalerror("FIFOs not empty, no op pending!\n");
+
+ /* set the state and update statistics */
+ v->pci.stall_state = state;
+ v->stats.stalls++;
+
+ /* either call the callback, or spin the CPU */
+ if (!v->device->m_stall.isnull())
+ v->device->m_stall(TRUE);
+ else
+ v->cpu->execute().spin_until_trigger(v->trigger);
+
+ /* set a timer to clear the stall */
+ v->pci.continue_timer->adjust(v->pci.op_end_time - current_time);
+}
+
+
+
+/*************************************
+ *
+ * Voodoo register writes
+ *
+ *************************************/
+
+static INT32 register_w(voodoo_state *v, offs_t offset, UINT32 data)
+{
+ UINT32 origdata = data;
+ INT32 cycles = 0;
+ INT64 data64;
+ UINT8 regnum;
+ UINT8 chips;
+
+ /* statistics */
+ v->stats.reg_writes++;
+
+ /* determine which chips we are addressing */
+ chips = (offset >> 8) & 0xf;
+ if (chips == 0)
+ chips = 0xf;
+ chips &= v->chipmask;
+
+ /* the first 64 registers can be aliased differently */
+ if ((offset & 0x800c0) == 0x80000 && v->alt_regmap)
+ regnum = register_alias_map[offset & 0x3f];
+ else
+ regnum = offset & 0xff;
+
+ /* first make sure this register is readable */
+ if (!(v->regaccess[regnum] & REGISTER_WRITE))
+ {
+ logerror("VOODOO.%d.ERROR:Invalid attempt to write %s\n", v->index, v->regnames[regnum]);
+ return 0;
+ }
+
+ /* switch off the register */
+ switch (regnum)
+ {
+ /* Vertex data is 12.4 formatted fixed point */
+ case fvertexAx:
+ data = float_to_int32(data, 4);
+ case vertexAx:
+ if (chips & 1) v->fbi.ax = (INT16)data;
+ break;
+
+ case fvertexAy:
+ data = float_to_int32(data, 4);
+ case vertexAy:
+ if (chips & 1) v->fbi.ay = (INT16)data;
+ break;
+
+ case fvertexBx:
+ data = float_to_int32(data, 4);
+ case vertexBx:
+ if (chips & 1) v->fbi.bx = (INT16)data;
+ break;
+
+ case fvertexBy:
+ data = float_to_int32(data, 4);
+ case vertexBy:
+ if (chips & 1) v->fbi.by = (INT16)data;
+ break;
+
+ case fvertexCx:
+ data = float_to_int32(data, 4);
+ case vertexCx:
+ if (chips & 1) v->fbi.cx = (INT16)data;
+ break;
+
+ case fvertexCy:
+ data = float_to_int32(data, 4);
+ case vertexCy:
+ if (chips & 1) v->fbi.cy = (INT16)data;
+ break;
+
+ /* RGB data is 12.12 formatted fixed point */
+ case fstartR:
+ data = float_to_int32(data, 12);
+ case startR:
+ if (chips & 1) v->fbi.startr = (INT32)(data << 8) >> 8;
+ break;
+
+ case fstartG:
+ data = float_to_int32(data, 12);
+ case startG:
+ if (chips & 1) v->fbi.startg = (INT32)(data << 8) >> 8;
+ break;
+
+ case fstartB:
+ data = float_to_int32(data, 12);
+ case startB:
+ if (chips & 1) v->fbi.startb = (INT32)(data << 8) >> 8;
+ break;
+
+ case fstartA:
+ data = float_to_int32(data, 12);
+ case startA:
+ if (chips & 1) v->fbi.starta = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdRdX:
+ data = float_to_int32(data, 12);
+ case dRdX:
+ if (chips & 1) v->fbi.drdx = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdGdX:
+ data = float_to_int32(data, 12);
+ case dGdX:
+ if (chips & 1) v->fbi.dgdx = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdBdX:
+ data = float_to_int32(data, 12);
+ case dBdX:
+ if (chips & 1) v->fbi.dbdx = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdAdX:
+ data = float_to_int32(data, 12);
+ case dAdX:
+ if (chips & 1) v->fbi.dadx = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdRdY:
+ data = float_to_int32(data, 12);
+ case dRdY:
+ if (chips & 1) v->fbi.drdy = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdGdY:
+ data = float_to_int32(data, 12);
+ case dGdY:
+ if (chips & 1) v->fbi.dgdy = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdBdY:
+ data = float_to_int32(data, 12);
+ case dBdY:
+ if (chips & 1) v->fbi.dbdy = (INT32)(data << 8) >> 8;
+ break;
+
+ case fdAdY:
+ data = float_to_int32(data, 12);
+ case dAdY:
+ if (chips & 1) v->fbi.dady = (INT32)(data << 8) >> 8;
+ break;
+
+ /* Z data is 20.12 formatted fixed point */
+ case fstartZ:
+ data = float_to_int32(data, 12);
+ case startZ:
+ if (chips & 1) v->fbi.startz = (INT32)data;
+ break;
+
+ case fdZdX:
+ data = float_to_int32(data, 12);
+ case dZdX:
+ if (chips & 1) v->fbi.dzdx = (INT32)data;
+ break;
+
+ case fdZdY:
+ data = float_to_int32(data, 12);
+ case dZdY:
+ if (chips & 1) v->fbi.dzdy = (INT32)data;
+ break;
+
+ /* S,T data is 14.18 formatted fixed point, converted to 16.32 internally */
+ case fstartS:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].starts = data64;
+ if (chips & 4) v->tmu[1].starts = data64;
+ break;
+ case startS:
+ if (chips & 2) v->tmu[0].starts = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].starts = (INT64)(INT32)data << 14;
+ break;
+
+ case fstartT:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].startt = data64;
+ if (chips & 4) v->tmu[1].startt = data64;
+ break;
+ case startT:
+ if (chips & 2) v->tmu[0].startt = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].startt = (INT64)(INT32)data << 14;
+ break;
+
+ case fdSdX:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].dsdx = data64;
+ if (chips & 4) v->tmu[1].dsdx = data64;
+ break;
+ case dSdX:
+ if (chips & 2) v->tmu[0].dsdx = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].dsdx = (INT64)(INT32)data << 14;
+ break;
+
+ case fdTdX:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].dtdx = data64;
+ if (chips & 4) v->tmu[1].dtdx = data64;
+ break;
+ case dTdX:
+ if (chips & 2) v->tmu[0].dtdx = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].dtdx = (INT64)(INT32)data << 14;
+ break;
+
+ case fdSdY:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].dsdy = data64;
+ if (chips & 4) v->tmu[1].dsdy = data64;
+ break;
+ case dSdY:
+ if (chips & 2) v->tmu[0].dsdy = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].dsdy = (INT64)(INT32)data << 14;
+ break;
+
+ case fdTdY:
+ data64 = float_to_int64(data, 32);
+ if (chips & 2) v->tmu[0].dtdy = data64;
+ if (chips & 4) v->tmu[1].dtdy = data64;
+ break;
+ case dTdY:
+ if (chips & 2) v->tmu[0].dtdy = (INT64)(INT32)data << 14;
+ if (chips & 4) v->tmu[1].dtdy = (INT64)(INT32)data << 14;
+ break;
+
+ /* W data is 2.30 formatted fixed point, converted to 16.32 internally */
+ case fstartW:
+ data64 = float_to_int64(data, 32);
+ if (chips & 1) v->fbi.startw = data64;
+ if (chips & 2) v->tmu[0].startw = data64;
+ if (chips & 4) v->tmu[1].startw = data64;
+ break;
+ case startW:
+ if (chips & 1) v->fbi.startw = (INT64)(INT32)data << 2;
+ if (chips & 2) v->tmu[0].startw = (INT64)(INT32)data << 2;
+ if (chips & 4) v->tmu[1].startw = (INT64)(INT32)data << 2;
+ break;
+
+ case fdWdX:
+ data64 = float_to_int64(data, 32);
+ if (chips & 1) v->fbi.dwdx = data64;
+ if (chips & 2) v->tmu[0].dwdx = data64;
+ if (chips & 4) v->tmu[1].dwdx = data64;
+ break;
+ case dWdX:
+ if (chips & 1) v->fbi.dwdx = (INT64)(INT32)data << 2;
+ if (chips & 2) v->tmu[0].dwdx = (INT64)(INT32)data << 2;
+ if (chips & 4) v->tmu[1].dwdx = (INT64)(INT32)data << 2;
+ break;
+
+ case fdWdY:
+ data64 = float_to_int64(data, 32);
+ if (chips & 1) v->fbi.dwdy = data64;
+ if (chips & 2) v->tmu[0].dwdy = data64;
+ if (chips & 4) v->tmu[1].dwdy = data64;
+ break;
+ case dWdY:
+ if (chips & 1) v->fbi.dwdy = (INT64)(INT32)data << 2;
+ if (chips & 2) v->tmu[0].dwdy = (INT64)(INT32)data << 2;
+ if (chips & 4) v->tmu[1].dwdy = (INT64)(INT32)data << 2;
+ break;
+
+ /* setup bits */
+ case sARGB:
+ if (chips & 1)
+ {
+ rgb_t rgbdata(data);
+ v->reg[sAlpha].f = rgbdata.a();
+ v->reg[sRed].f = rgbdata.r();
+ v->reg[sGreen].f = rgbdata.g();
+ v->reg[sBlue].f = rgbdata.b();
+ }
+ break;
+
+ /* mask off invalid bits for different cards */
+ case fbzColorPath:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (v->type < TYPE_VOODOO_2)
+ data &= 0x0fffffff;
+ if (chips & 1) v->reg[fbzColorPath].u = data;
+ break;
+
+ case fbzMode:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (v->type < TYPE_VOODOO_2)
+ data &= 0x001fffff;
+ if (chips & 1) v->reg[fbzMode].u = data;
+ break;
+
+ case fogMode:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (v->type < TYPE_VOODOO_2)
+ data &= 0x0000003f;
+ if (chips & 1) v->reg[fogMode].u = data;
+ break;
+
+ /* triangle drawing */
+ case triangleCMD:
+ v->fbi.cheating_allowed = (v->fbi.ax != 0 || v->fbi.ay != 0 || v->fbi.bx > 50 || v->fbi.by != 0 || v->fbi.cx != 0 || v->fbi.cy > 50);
+ v->fbi.sign = data;
+ cycles = triangle(v);
+ break;
+
+ case ftriangleCMD:
+ v->fbi.cheating_allowed = TRUE;
+ v->fbi.sign = data;
+ cycles = triangle(v);
+ break;
+
+ case sBeginTriCMD:
+ cycles = begin_triangle(v);
+ break;
+
+ case sDrawTriCMD:
+ cycles = draw_triangle(v);
+ break;
+
+ /* other commands */
+ case nopCMD:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (data & 1)
+ reset_counters(v);
+ if (data & 2)
+ v->reg[fbiTrianglesOut].u = 0;
+ break;
+
+ case fastfillCMD:
+ cycles = fastfill(v);
+ break;
+
+ case swapbufferCMD:
+ poly_wait(v->poly, v->regnames[regnum]);
+ cycles = swapbuffer(v, data);
+ break;
+
+ case userIntrCMD:
+ poly_wait(v->poly, v->regnames[regnum]);
+ //fatalerror("userIntrCMD\n");
+
+ v->reg[intrCtrl].u |= 0x1800;
+ v->reg[intrCtrl].u &= ~0x80000000;
+
+ // TODO: rename vblank_client for less confusion?
+ if (!v->device->m_vblank.isnull())
+ v->device->m_vblank(TRUE);
+ break;
+
+ /* gamma table access -- Voodoo/Voodoo2 only */
+ case clutData:
+ if (v->type <= TYPE_VOODOO_2 && (chips & 1))
+ {
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (!FBIINIT1_VIDEO_TIMING_RESET(v->reg[fbiInit1].u))
+ {
+ int index = data >> 24;
+ if (index <= 32)
+ {
+ v->fbi.clut[index] = data;
+ v->fbi.clut_dirty = TRUE;
+ }
+ }
+ else
+ logerror("clutData ignored because video timing reset = 1\n");
+ }
+ break;
+
+ /* external DAC access -- Voodoo/Voodoo2 only */
+ case dacData:
+ if (v->type <= TYPE_VOODOO_2 && (chips & 1))
+ {
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (!(data & 0x800))
+ dacdata_w(&v->dac, (data >> 8) & 7, data & 0xff);
+ else
+ dacdata_r(&v->dac, (data >> 8) & 7);
+ }
+ break;
+
+ /* vertical sync rate -- Voodoo/Voodoo2 only */
+ case hSync:
+ case vSync:
+ case backPorch:
+ case videoDimensions:
+ if (v->type <= TYPE_VOODOO_2 && (chips & 1))
+ {
+ poly_wait(v->poly, v->regnames[regnum]);
+ v->reg[regnum].u = data;
+ if (v->reg[hSync].u != 0 && v->reg[vSync].u != 0 && v->reg[videoDimensions].u != 0)
+ {
+ int hvis, vvis, htotal, vtotal, hbp, vbp;
+ attoseconds_t refresh = v->screen->frame_period().attoseconds();
+ attoseconds_t stdperiod, medperiod, vgaperiod;
+ attoseconds_t stddiff, meddiff, vgadiff;
+ rectangle visarea;
+
+ if (v->type == TYPE_VOODOO_2)
+ {
+ htotal = ((v->reg[hSync].u >> 16) & 0x7ff) + 1 + (v->reg[hSync].u & 0x1ff) + 1;
+ vtotal = ((v->reg[vSync].u >> 16) & 0x1fff) + (v->reg[vSync].u & 0x1fff);
+ hvis = v->reg[videoDimensions].u & 0x7ff;
+ vvis = (v->reg[videoDimensions].u >> 16) & 0x7ff;
+ hbp = (v->reg[backPorch].u & 0x1ff) + 2;
+ vbp = (v->reg[backPorch].u >> 16) & 0x1ff;
+ }
+ else
+ {
+ htotal = ((v->reg[hSync].u >> 16) & 0x3ff) + 1 + (v->reg[hSync].u & 0xff) + 1;
+ vtotal = ((v->reg[vSync].u >> 16) & 0xfff) + (v->reg[vSync].u & 0xfff);
+ hvis = v->reg[videoDimensions].u & 0x3ff;
+ vvis = (v->reg[videoDimensions].u >> 16) & 0x3ff;
+ hbp = (v->reg[backPorch].u & 0xff) + 2;
+ vbp = (v->reg[backPorch].u >> 16) & 0xff;
+ }
+
+ /* create a new visarea */
+ visarea.set(hbp, hbp + hvis - 1, vbp, vbp + vvis - 1);
+
+ /* keep within bounds */
+ visarea.max_x = MIN(visarea.max_x, htotal - 1);
+ visarea.max_y = MIN(visarea.max_y, vtotal - 1);
+
+ /* compute the new period for standard res, medium res, and VGA res */
+ stdperiod = HZ_TO_ATTOSECONDS(15750) * vtotal;
+ medperiod = HZ_TO_ATTOSECONDS(25000) * vtotal;
+ vgaperiod = HZ_TO_ATTOSECONDS(31500) * vtotal;
+
+ /* compute a diff against the current refresh period */
+ stddiff = stdperiod - refresh;
+ if (stddiff < 0) stddiff = -stddiff;
+ meddiff = medperiod - refresh;
+ if (meddiff < 0) meddiff = -meddiff;
+ vgadiff = vgaperiod - refresh;
+ if (vgadiff < 0) vgadiff = -vgadiff;
+
+ osd_printf_debug("hSync=%08X vSync=%08X backPorch=%08X videoDimensions=%08X\n",
+ v->reg[hSync].u, v->reg[vSync].u, v->reg[backPorch].u, v->reg[videoDimensions].u);
+ osd_printf_debug("Horiz: %d-%d (%d total) Vert: %d-%d (%d total) -- ", visarea.min_x, visarea.max_x, htotal, visarea.min_y, visarea.max_y, vtotal);
+
+ /* configure the screen based on which one matches the closest */
+ if (stddiff < meddiff && stddiff < vgadiff)
+ {
+ v->screen->configure(htotal, vtotal, visarea, stdperiod);
+ osd_printf_debug("Standard resolution, %f Hz\n", ATTOSECONDS_TO_HZ(stdperiod));
+ }
+ else if (meddiff < vgadiff)
+ {
+ v->screen->configure(htotal, vtotal, visarea, medperiod);
+ osd_printf_debug("Medium resolution, %f Hz\n", ATTOSECONDS_TO_HZ(medperiod));
+ }
+ else
+ {
+ v->screen->configure(htotal, vtotal, visarea, vgaperiod);
+ osd_printf_debug("VGA resolution, %f Hz\n", ATTOSECONDS_TO_HZ(vgaperiod));
+ }
+
+ /* configure the new framebuffer info */
+ v->fbi.width = hvis;
+ v->fbi.height = vvis;
+ v->fbi.xoffs = hbp;
+ v->fbi.yoffs = vbp;
+ v->fbi.vsyncscan = (v->reg[vSync].u >> 16) & 0xfff;
+
+ /* recompute the time of VBLANK */
+ adjust_vblank_timer(v);
+
+ /* if changing dimensions, update video memory layout */
+ if (regnum == videoDimensions)
+ recompute_video_memory(v);
+ }
+ }
+ break;
+
+ /* fbiInit0 can only be written if initEnable says we can -- Voodoo/Voodoo2 only */
+ case fbiInit0:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (v->type <= TYPE_VOODOO_2 && (chips & 1) && INITEN_ENABLE_HW_INIT(v->pci.init_enable))
+ {
+ v->reg[fbiInit0].u = data;
+ if (FBIINIT0_GRAPHICS_RESET(data))
+ soft_reset(v);
+ if (FBIINIT0_FIFO_RESET(data))
+ fifo_reset(&v->pci.fifo);
+ recompute_video_memory(v);
+ }
+ break;
+
+ /* fbiInit5-7 are Voodoo 2-only; ignore them on anything else */
+ case fbiInit5:
+ case fbiInit6:
+ if (v->type < TYPE_VOODOO_2)
+ break;
+ /* else fall through... */
+
+ /* fbiInitX can only be written if initEnable says we can -- Voodoo/Voodoo2 only */
+ /* most of these affect memory layout, so always recompute that when done */
+ case fbiInit1:
+ case fbiInit2:
+ case fbiInit4:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (v->type <= TYPE_VOODOO_2 && (chips & 1) && INITEN_ENABLE_HW_INIT(v->pci.init_enable))
+ {
+ v->reg[regnum].u = data;
+ recompute_video_memory(v);
+ v->fbi.video_changed = TRUE;
+ }
+ break;
+
+ case fbiInit3:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (v->type <= TYPE_VOODOO_2 && (chips & 1) && INITEN_ENABLE_HW_INIT(v->pci.init_enable))
+ {
+ v->reg[regnum].u = data;
+ v->alt_regmap = FBIINIT3_TRI_REGISTER_REMAP(data);
+ v->fbi.yorigin = FBIINIT3_YORIGIN_SUBTRACT(v->reg[fbiInit3].u);
+ recompute_video_memory(v);
+ }
+ break;
+
+ case fbiInit7:
+/* case swapPending: -- Banshee */
+ if (v->type == TYPE_VOODOO_2 && (chips & 1) && INITEN_ENABLE_HW_INIT(v->pci.init_enable))
+ {
+ poly_wait(v->poly, v->regnames[regnum]);
+ v->reg[regnum].u = data;
+ v->fbi.cmdfifo[0].enable = FBIINIT7_CMDFIFO_ENABLE(data);
+ v->fbi.cmdfifo[0].count_holes = !FBIINIT7_DISABLE_CMDFIFO_HOLES(data);
+ }
+ else if (v->type >= TYPE_VOODOO_BANSHEE)
+ v->fbi.swaps_pending++;
+ break;
+
+ /* cmdFifo -- Voodoo2 only */
+ case cmdFifoBaseAddr:
+ if (v->type == TYPE_VOODOO_2 && (chips & 1))
+ {
+ poly_wait(v->poly, v->regnames[regnum]);
+ v->reg[regnum].u = data;
+ v->fbi.cmdfifo[0].base = (data & 0x3ff) << 12;
+ v->fbi.cmdfifo[0].end = (((data >> 16) & 0x3ff) + 1) << 12;
+ }
+ break;
+
+ case cmdFifoBump:
+ if (v->type == TYPE_VOODOO_2 && (chips & 1))
+ fatalerror("cmdFifoBump\n");
+ break;
+
+ case cmdFifoRdPtr:
+ if (v->type == TYPE_VOODOO_2 && (chips & 1))
+ v->fbi.cmdfifo[0].rdptr = data;
+ break;
+
+ case cmdFifoAMin:
+/* case colBufferAddr: -- Banshee */
+ if (v->type == TYPE_VOODOO_2 && (chips & 1))
+ v->fbi.cmdfifo[0].amin = data;
+ else if (v->type >= TYPE_VOODOO_BANSHEE && (chips & 1))
+ v->fbi.rgboffs[1] = data & v->fbi.mask & ~0x0f;
+ break;
+
+ case cmdFifoAMax:
+/* case colBufferStride: -- Banshee */
+ if (v->type == TYPE_VOODOO_2 && (chips & 1))
+ v->fbi.cmdfifo[0].amax = data;
+ else if (v->type >= TYPE_VOODOO_BANSHEE && (chips & 1))
+ {
+ if (data & 0x8000)
+ v->fbi.rowpixels = (data & 0x7f) << 6;
+ else
+ v->fbi.rowpixels = (data & 0x3fff) >> 1;
+ }
+ break;
+
+ case cmdFifoDepth:
+/* case auxBufferAddr: -- Banshee */
+ if (v->type == TYPE_VOODOO_2 && (chips & 1))
+ v->fbi.cmdfifo[0].depth = data;
+ else if (v->type >= TYPE_VOODOO_BANSHEE && (chips & 1))
+ v->fbi.auxoffs = data & v->fbi.mask & ~0x0f;
+ break;
+
+ case cmdFifoHoles:
+/* case auxBufferStride: -- Banshee */
+ if (v->type == TYPE_VOODOO_2 && (chips & 1))
+ v->fbi.cmdfifo[0].holes = data;
+ else if (v->type >= TYPE_VOODOO_BANSHEE && (chips & 1))
+ {
+ int rowpixels;
+
+ if (data & 0x8000)
+ rowpixels = (data & 0x7f) << 6;
+ else
+ rowpixels = (data & 0x3fff) >> 1;
+ if (v->fbi.rowpixels != rowpixels)
+ fatalerror("aux buffer stride differs from color buffer stride\n");
+ }
+ break;
+
+ /* nccTable entries are processed and expanded immediately */
+ case nccTable+0:
+ case nccTable+1:
+ case nccTable+2:
+ case nccTable+3:
+ case nccTable+4:
+ case nccTable+5:
+ case nccTable+6:
+ case nccTable+7:
+ case nccTable+8:
+ case nccTable+9:
+ case nccTable+10:
+ case nccTable+11:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (chips & 2) ncc_table_write(&v->tmu[0].ncc[0], regnum - nccTable, data);
+ if (chips & 4) ncc_table_write(&v->tmu[1].ncc[0], regnum - nccTable, data);
+ break;
+
+ case nccTable+12:
+ case nccTable+13:
+ case nccTable+14:
+ case nccTable+15:
+ case nccTable+16:
+ case nccTable+17:
+ case nccTable+18:
+ case nccTable+19:
+ case nccTable+20:
+ case nccTable+21:
+ case nccTable+22:
+ case nccTable+23:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (chips & 2) ncc_table_write(&v->tmu[0].ncc[1], regnum - (nccTable+12), data);
+ if (chips & 4) ncc_table_write(&v->tmu[1].ncc[1], regnum - (nccTable+12), data);
+ break;
+
+ /* fogTable entries are processed and expanded immediately */
+ case fogTable+0:
+ case fogTable+1:
+ case fogTable+2:
+ case fogTable+3:
+ case fogTable+4:
+ case fogTable+5:
+ case fogTable+6:
+ case fogTable+7:
+ case fogTable+8:
+ case fogTable+9:
+ case fogTable+10:
+ case fogTable+11:
+ case fogTable+12:
+ case fogTable+13:
+ case fogTable+14:
+ case fogTable+15:
+ case fogTable+16:
+ case fogTable+17:
+ case fogTable+18:
+ case fogTable+19:
+ case fogTable+20:
+ case fogTable+21:
+ case fogTable+22:
+ case fogTable+23:
+ case fogTable+24:
+ case fogTable+25:
+ case fogTable+26:
+ case fogTable+27:
+ case fogTable+28:
+ case fogTable+29:
+ case fogTable+30:
+ case fogTable+31:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (chips & 1)
+ {
+ int base = 2 * (regnum - fogTable);
+ v->fbi.fogdelta[base + 0] = (data >> 0) & 0xff;
+ v->fbi.fogblend[base + 0] = (data >> 8) & 0xff;
+ v->fbi.fogdelta[base + 1] = (data >> 16) & 0xff;
+ v->fbi.fogblend[base + 1] = (data >> 24) & 0xff;
+ }
+ break;
+
+ /* texture modifications cause us to recompute everything */
+ case textureMode:
+ case tLOD:
+ case tDetail:
+ case texBaseAddr:
+ case texBaseAddr_1:
+ case texBaseAddr_2:
+ case texBaseAddr_3_8:
+ poly_wait(v->poly, v->regnames[regnum]);
+ if (chips & 2)
+ {
+ v->tmu[0].reg[regnum].u = data;
+ v->tmu[0].regdirty = TRUE;
+ }
+ if (chips & 4)
+ {
+ v->tmu[1].reg[regnum].u = data;
+ v->tmu[1].regdirty = TRUE;
+ }
+ break;
+
+ case trexInit1:
+ /* send tmu config data to the frame buffer */
+ v->send_config = (TREXINIT_SEND_TMU_CONFIG(data) > 0);
+ goto default_case;
+
+ /* these registers are referenced in the renderer; we must wait for pending work before changing */
+ case chromaRange:
+ case chromaKey:
+ case alphaMode:
+ case fogColor:
+ case stipple:
+ case zaColor:
+ case color1:
+ case color0:
+ case clipLowYHighY:
+ case clipLeftRight:
+ poly_wait(v->poly, v->regnames[regnum]);
+ /* fall through to default implementation */
+
+ /* by default, just feed the data to the chips */
+ default:
+default_case:
+ if (chips & 1) v->reg[0x000 + regnum].u = data;
+ if (chips & 2) v->reg[0x100 + regnum].u = data;
+ if (chips & 4) v->reg[0x200 + regnum].u = data;
+ if (chips & 8) v->reg[0x300 + regnum].u = data;
+ break;
+ }
+
+ if (LOG_REGISTERS)
+ {
+ if (regnum < fvertexAx || regnum > fdWdY)
+ logerror("VOODOO.%d.REG:%s(%d) write = %08X\n", v->index, (regnum < 0x384/4) ? v->regnames[regnum] : "oob", chips, origdata);
+ else
+ logerror("VOODOO.%d.REG:%s(%d) write = %f\n", v->index, (regnum < 0x384/4) ? v->regnames[regnum] : "oob", chips, (double) u2f(origdata));
+ }
+
+ return cycles;
+}
+
+
+
+/*************************************
+ *
+ * Voodoo LFB writes
+ *
+ *************************************/
+static INT32 lfb_direct_w(voodoo_state *v, offs_t offset, UINT32 data, UINT32 mem_mask)
+{
+ UINT16 *dest;
+ UINT32 destmax;
+ int x, y;
+ UINT32 bufoffs;
+
+ /* statistics */
+ v->stats.lfb_writes++;
+
+ /* byte swizzling */
+ if (LFBMODE_BYTE_SWIZZLE_WRITES(v->reg[lfbMode].u))
+ {
+ data = FLIPENDIAN_INT32(data);
+ mem_mask = FLIPENDIAN_INT32(mem_mask);
+ }
+
+ /* word swapping */
+ if (LFBMODE_WORD_SWAP_WRITES(v->reg[lfbMode].u))
+ {
+ data = (data << 16) | (data >> 16);
+ mem_mask = (mem_mask << 16) | (mem_mask >> 16);
+ }
+
+ // TODO: This direct write is not verified.
+ // For direct lfb access just write the data
+ /* compute X,Y */
+ offset <<= 1;
+ x = offset & ((1 << v->fbi.lfb_stride) - 1);
+ y = (offset >> v->fbi.lfb_stride);
+ dest = (UINT16 *)(v->fbi.ram + v->fbi.lfb_base*4);
+ destmax = (v->fbi.mask + 1 - v->fbi.lfb_base*4) / 2;
+ bufoffs = y * v->fbi.rowpixels + x;
+ if (bufoffs >= destmax) {
+ logerror("lfb_direct_w: Buffer offset out of bounds x=%i y=%i offset=%08X bufoffs=%08X data=%08X\n", x, y, offset, (UINT32) bufoffs, data);
+ return 0;
+ }
+ if (ACCESSING_BITS_0_15)
+ dest[bufoffs + 0] = data&0xffff;
+ if (ACCESSING_BITS_16_31)
+ dest[bufoffs + 1] = data>>16;
+ if (LOG_LFB) logerror("VOODOO.%d.LFB:write direct (%d,%d) = %08X & %08X\n", v->index, x, y, data, mem_mask);
+ return 0;
+}
+
+static INT32 lfb_w(voodoo_state *v, offs_t offset, UINT32 data, UINT32 mem_mask)
+{
+ UINT16 *dest, *depth;
+ UINT32 destmax, depthmax;
+ int sr[2], sg[2], sb[2], sa[2], sw[2];
+ int x, y, scry, mask;
+ int pix, destbuf;
+
+ /* statistics */
+ v->stats.lfb_writes++;
+
+ /* byte swizzling */
+ if (LFBMODE_BYTE_SWIZZLE_WRITES(v->reg[lfbMode].u))
+ {
+ data = FLIPENDIAN_INT32(data);
+ mem_mask = FLIPENDIAN_INT32(mem_mask);
+ }
+
+ /* word swapping */
+ if (LFBMODE_WORD_SWAP_WRITES(v->reg[lfbMode].u))
+ {
+ data = (data << 16) | (data >> 16);
+ mem_mask = (mem_mask << 16) | (mem_mask >> 16);
+ }
+
+ /* extract default depth and alpha values */
+ sw[0] = sw[1] = v->reg[zaColor].u & 0xffff;
+ sa[0] = sa[1] = v->reg[zaColor].u >> 24;
+
+ /* first extract A,R,G,B from the data */
+ switch (LFBMODE_WRITE_FORMAT(v->reg[lfbMode].u) + 16 * LFBMODE_RGBA_LANES(v->reg[lfbMode].u))
+ {
+ case 16*0 + 0: /* ARGB, 16-bit RGB 5-6-5 */
+ case 16*2 + 0: /* RGBA, 16-bit RGB 5-6-5 */
+ EXTRACT_565_TO_888(data, sr[0], sg[0], sb[0]);
+ EXTRACT_565_TO_888(data >> 16, sr[1], sg[1], sb[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+ case 16*1 + 0: /* ABGR, 16-bit RGB 5-6-5 */
+ case 16*3 + 0: /* BGRA, 16-bit RGB 5-6-5 */
+ EXTRACT_565_TO_888(data, sb[0], sg[0], sr[0]);
+ EXTRACT_565_TO_888(data >> 16, sb[1], sg[1], sr[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+
+ case 16*0 + 1: /* ARGB, 16-bit RGB x-5-5-5 */
+ EXTRACT_x555_TO_888(data, sr[0], sg[0], sb[0]);
+ EXTRACT_x555_TO_888(data >> 16, sr[1], sg[1], sb[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+ case 16*1 + 1: /* ABGR, 16-bit RGB x-5-5-5 */
+ EXTRACT_x555_TO_888(data, sb[0], sg[0], sr[0]);
+ EXTRACT_x555_TO_888(data >> 16, sb[1], sg[1], sr[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+ case 16*2 + 1: /* RGBA, 16-bit RGB x-5-5-5 */
+ EXTRACT_555x_TO_888(data, sr[0], sg[0], sb[0]);
+ EXTRACT_555x_TO_888(data >> 16, sr[1], sg[1], sb[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+ case 16*3 + 1: /* BGRA, 16-bit RGB x-5-5-5 */
+ EXTRACT_555x_TO_888(data, sb[0], sg[0], sr[0]);
+ EXTRACT_555x_TO_888(data >> 16, sb[1], sg[1], sr[1]);
+ mask = LFB_RGB_PRESENT | (LFB_RGB_PRESENT << 4);
+ offset <<= 1;
+ break;
+
+ case 16*0 + 2: /* ARGB, 16-bit ARGB 1-5-5-5 */
+ EXTRACT_1555_TO_8888(data, sa[0], sr[0], sg[0], sb[0]);
+ EXTRACT_1555_TO_8888(data >> 16, sa[1], sr[1], sg[1], sb[1]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | ((LFB_RGB_PRESENT | LFB_ALPHA_PRESENT) << 4);
+ offset <<= 1;
+ break;
+ case 16*1 + 2: /* ABGR, 16-bit ARGB 1-5-5-5 */
+ EXTRACT_1555_TO_8888(data, sa[0], sb[0], sg[0], sr[0]);
+ EXTRACT_1555_TO_8888(data >> 16, sa[1], sb[1], sg[1], sr[1]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | ((LFB_RGB_PRESENT | LFB_ALPHA_PRESENT) << 4);
+ offset <<= 1;
+ break;
+ case 16*2 + 2: /* RGBA, 16-bit ARGB 1-5-5-5 */
+ EXTRACT_5551_TO_8888(data, sr[0], sg[0], sb[0], sa[0]);
+ EXTRACT_5551_TO_8888(data >> 16, sr[1], sg[1], sb[1], sa[1]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | ((LFB_RGB_PRESENT | LFB_ALPHA_PRESENT) << 4);
+ offset <<= 1;
+ break;
+ case 16*3 + 2: /* BGRA, 16-bit ARGB 1-5-5-5 */
+ EXTRACT_5551_TO_8888(data, sb[0], sg[0], sr[0], sa[0]);
+ EXTRACT_5551_TO_8888(data >> 16, sb[1], sg[1], sr[1], sa[1]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | ((LFB_RGB_PRESENT | LFB_ALPHA_PRESENT) << 4);
+ offset <<= 1;
+ break;
+
+ case 16*0 + 4: /* ARGB, 32-bit RGB x-8-8-8 */
+ EXTRACT_x888_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT;
+ break;
+ case 16*1 + 4: /* ABGR, 32-bit RGB x-8-8-8 */
+ EXTRACT_x888_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT;
+ break;
+ case 16*2 + 4: /* RGBA, 32-bit RGB x-8-8-8 */
+ EXTRACT_888x_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT;
+ break;
+ case 16*3 + 4: /* BGRA, 32-bit RGB x-8-8-8 */
+ EXTRACT_888x_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT;
+ break;
+
+ case 16*0 + 5: /* ARGB, 32-bit ARGB 8-8-8-8 */
+ EXTRACT_8888_TO_8888(data, sa[0], sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT;
+ break;
+ case 16*1 + 5: /* ABGR, 32-bit ARGB 8-8-8-8 */
+ EXTRACT_8888_TO_8888(data, sa[0], sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT;
+ break;
+ case 16*2 + 5: /* RGBA, 32-bit ARGB 8-8-8-8 */
+ EXTRACT_8888_TO_8888(data, sr[0], sg[0], sb[0], sa[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT;
+ break;
+ case 16*3 + 5: /* BGRA, 32-bit ARGB 8-8-8-8 */
+ EXTRACT_8888_TO_8888(data, sb[0], sg[0], sr[0], sa[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT;
+ break;
+
+ case 16*0 + 12: /* ARGB, 32-bit depth+RGB 5-6-5 */
+ case 16*2 + 12: /* RGBA, 32-bit depth+RGB 5-6-5 */
+ sw[0] = data >> 16;
+ EXTRACT_565_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*1 + 12: /* ABGR, 32-bit depth+RGB 5-6-5 */
+ case 16*3 + 12: /* BGRA, 32-bit depth+RGB 5-6-5 */
+ sw[0] = data >> 16;
+ EXTRACT_565_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+
+ case 16*0 + 13: /* ARGB, 32-bit depth+RGB x-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_x555_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*1 + 13: /* ABGR, 32-bit depth+RGB x-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_x555_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*2 + 13: /* RGBA, 32-bit depth+RGB x-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_555x_TO_888(data, sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*3 + 13: /* BGRA, 32-bit depth+RGB x-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_555x_TO_888(data, sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+
+ case 16*0 + 14: /* ARGB, 32-bit depth+ARGB 1-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_1555_TO_8888(data, sa[0], sr[0], sg[0], sb[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*1 + 14: /* ABGR, 32-bit depth+ARGB 1-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_1555_TO_8888(data, sa[0], sb[0], sg[0], sr[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*2 + 14: /* RGBA, 32-bit depth+ARGB 1-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_5551_TO_8888(data, sr[0], sg[0], sb[0], sa[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+ case 16*3 + 14: /* BGRA, 32-bit depth+ARGB 1-5-5-5 */
+ sw[0] = data >> 16;
+ EXTRACT_5551_TO_8888(data, sb[0], sg[0], sr[0], sa[0]);
+ mask = LFB_RGB_PRESENT | LFB_ALPHA_PRESENT | LFB_DEPTH_PRESENT_MSW;
+ break;
+
+ case 16*0 + 15: /* ARGB, 16-bit depth */
+ case 16*1 + 15: /* ARGB, 16-bit depth */
+ case 16*2 + 15: /* ARGB, 16-bit depth */
+ case 16*3 + 15: /* ARGB, 16-bit depth */
+ sw[0] = data & 0xffff;
+ sw[1] = data >> 16;
+ mask = LFB_DEPTH_PRESENT | (LFB_DEPTH_PRESENT << 4);
+ offset <<= 1;
+ break;
+
+ default: /* reserved */
+ logerror("lfb_w: Unknown format\n");
+ return 0;
+ }
+
+ /* compute X,Y */
+ x = offset & ((1 << v->fbi.lfb_stride) - 1);
+ y = (offset >> v->fbi.lfb_stride) & 0x3ff;
+
+ /* adjust the mask based on which half of the data is written */
+ if (!ACCESSING_BITS_0_15)
+ mask &= ~(0x0f - LFB_DEPTH_PRESENT_MSW);
+ if (!ACCESSING_BITS_16_31)
+ mask &= ~(0xf0 + LFB_DEPTH_PRESENT_MSW);
+
+ /* select the target buffer */
+ destbuf = (v->type >= TYPE_VOODOO_BANSHEE) ? 1 : LFBMODE_WRITE_BUFFER_SELECT(v->reg[lfbMode].u);
+ switch (destbuf)
+ {
+ case 0: /* front buffer */
+ dest = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ destmax = (v->fbi.mask + 1 - v->fbi.rgboffs[v->fbi.frontbuf]) / 2;
+ v->fbi.video_changed = TRUE;
+ break;
+
+ case 1: /* back buffer */
+ dest = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.backbuf]);
+ destmax = (v->fbi.mask + 1 - v->fbi.rgboffs[v->fbi.backbuf]) / 2;
+ break;
+
+ default: /* reserved */
+ return 0;
+ }
+ depth = (UINT16 *)(v->fbi.ram + v->fbi.auxoffs);
+ depthmax = (v->fbi.mask + 1 - v->fbi.auxoffs) / 2;
+
+ /* simple case: no pipeline */
+ if (!LFBMODE_ENABLE_PIXEL_PIPELINE(v->reg[lfbMode].u))
+ {
+ DECLARE_DITHER_POINTERS_NO_DITHER_VAR;
+ UINT32 bufoffs;
+
+ if (LOG_LFB) logerror("VOODOO.%d.LFB:write raw mode %X (%d,%d) = %08X & %08X\n", v->index, LFBMODE_WRITE_FORMAT(v->reg[lfbMode].u), x, y, data, mem_mask);
+
+ /* determine the screen Y */
+ scry = y;
+ if (LFBMODE_Y_ORIGIN(v->reg[lfbMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+
+ /* advance pointers to the proper row */
+ bufoffs = scry * v->fbi.rowpixels + x;
+
+ /* compute dithering */
+ COMPUTE_DITHER_POINTERS_NO_DITHER_VAR(v->reg[fbzMode].u, y);
+
+ /* wait for any outstanding work to finish */
+ poly_wait(v->poly, "LFB Write");
+
+ /* loop over up to two pixels */
+ for (pix = 0; mask; pix++)
+ {
+ /* make sure we care about this pixel */
+ if (mask & 0x0f)
+ {
+ /* write to the RGB buffer */
+ if ((mask & LFB_RGB_PRESENT) && bufoffs < destmax)
+ {
+ /* apply dithering and write to the screen */
+ APPLY_DITHER(v->reg[fbzMode].u, x, dither_lookup, sr[pix], sg[pix], sb[pix]);
+ dest[bufoffs] = (sr[pix] << 11) | (sg[pix] << 5) | sb[pix];
+ }
+
+ /* make sure we have an aux buffer to write to */
+ if (depth && bufoffs < depthmax)
+ {
+ /* write to the alpha buffer */
+ if ((mask & LFB_ALPHA_PRESENT) && FBZMODE_ENABLE_ALPHA_PLANES(v->reg[fbzMode].u))
+ depth[bufoffs] = sa[pix];
+
+ /* write to the depth buffer */
+ if ((mask & (LFB_DEPTH_PRESENT | LFB_DEPTH_PRESENT_MSW)) && !FBZMODE_ENABLE_ALPHA_PLANES(v->reg[fbzMode].u))
+ depth[bufoffs] = sw[pix];
+ }
+
+ /* track pixel writes to the frame buffer regardless of mask */
+ v->reg[fbiPixelsOut].u++;
+ }
+
+ /* advance our pointers */
+ bufoffs++;
+ x++;
+ mask >>= 4;
+ }
+ }
+
+ /* tricky case: run the full pixel pipeline on the pixel */
+ else
+ {
+ DECLARE_DITHER_POINTERS;
+
+ if (LOG_LFB) logerror("VOODOO.%d.LFB:write pipelined mode %X (%d,%d) = %08X & %08X\n", v->index, LFBMODE_WRITE_FORMAT(v->reg[lfbMode].u), x, y, data, mem_mask);
+
+ /* determine the screen Y */
+ scry = y;
+ if (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+
+ /* advance pointers to the proper row */
+ dest += scry * v->fbi.rowpixels;
+ if (depth)
+ depth += scry * v->fbi.rowpixels;
+
+ /* compute dithering */
+ COMPUTE_DITHER_POINTERS(v->reg[fbzMode].u, y);
+
+ /* loop over up to two pixels */
+ for (pix = 0; mask; pix++)
+ {
+ /* make sure we care about this pixel */
+ if (mask & 0x0f)
+ {
+ stats_block *stats = &v->fbi.lfb_stats;
+ INT64 iterw;
+ if (LFBMODE_WRITE_W_SELECT(v->reg[lfbMode].u)) {
+ iterw = (UINT32) v->reg[zaColor].u << 16;
+ } else {
+ // The most significant fractional bits of 16.32 W are set to z
+ iterw = (UINT32) sw[pix] << 16;
+ }
+ INT32 iterz = sw[pix] << 12;
+
+ /* apply clipping */
+ if (FBZMODE_ENABLE_CLIPPING(v->reg[fbzMode].u))
+ {
+ if (x < ((v->reg[clipLeftRight].u >> 16) & 0x3ff) ||
+ x >= (v->reg[clipLeftRight].u & 0x3ff) ||
+ scry < ((v->reg[clipLowYHighY].u >> 16) & 0x3ff) ||
+ scry >= (v->reg[clipLowYHighY].u & 0x3ff))
+ {
+ stats->pixels_in++;
+ stats->clip_fail++;
+ goto nextpixel;
+ }
+ }
+ #if USE_OLD_RASTER == 1
+ rgb_union color;
+ rgb_union iterargb = { 0 };
+ #else
+ rgbaint_t color, preFog;
+ rgbaint_t iterargb(0);
+ #endif
+
+ /* pixel pipeline part 1 handles depth testing and stippling */
+ //PIXEL_PIPELINE_BEGIN(v, stats, x, y, v->reg[fbzColorPath].u, v->reg[fbzMode].u, iterz, iterw);
+// Start PIXEL_PIPE_BEGIN copy
+ //#define PIXEL_PIPELINE_BEGIN(VV, STATS, XX, YY, FBZCOLORPATH, FBZMODE, ITERZ, ITERW)
+ INT32 fogdepth, biasdepth;
+ INT32 r, g, b, a;
+
+ (stats)->pixels_in++;
+
+ /* apply clipping */
+ /* note that for perf reasons, we assume the caller has done clipping */
+
+ /* handle stippling */
+ if (FBZMODE_ENABLE_STIPPLE(v->reg[fbzMode].u))
+ {
+ /* rotate mode */
+ if (FBZMODE_STIPPLE_PATTERN(v->reg[fbzMode].u) == 0)
+ {
+ v->reg[stipple].u = (v->reg[stipple].u << 1) | (v->reg[stipple].u >> 31);
+ if ((v->reg[stipple].u & 0x80000000) == 0)
+ {
+ v->stats.total_stippled++;
+ goto skipdrawdepth;
+ }
+ }
+
+ /* pattern mode */
+ else
+ {
+ int stipple_index = ((y & 3) << 3) | (~x & 7);
+ if (((v->reg[stipple].u >> stipple_index) & 1) == 0)
+ {
+ v->stats.total_stippled++;
+ goto nextpixel;
+ }
+ }
+ }
+// End PIXEL_PIPELINE_BEGIN COPY
+
+ // Depth testing value for lfb pipeline writes is directly from write data, no biasing is used
+ fogdepth = biasdepth = (UINT32) sw[pix];
+
+ #if USE_OLD_RASTER == 1
+ /* Perform depth testing */
+ DEPTH_TEST(v, stats, x, v->reg[fbzMode].u);
+
+ /* use the RGBA we stashed above */
+ color.rgb.r = r = sr[pix];
+ color.rgb.g = g = sg[pix];
+ color.rgb.b = b = sb[pix];
+ color.rgb.a = a = sa[pix];
+
+ /* apply chroma key, alpha mask, and alpha testing */
+ APPLY_CHROMAKEY(v, stats, v->reg[fbzMode].u, color);
+ APPLY_ALPHAMASK(v, stats, v->reg[fbzMode].u, color.rgb.a);
+ APPLY_ALPHATEST(v, stats, v->reg[alphaMode].u, color.rgb.a);
+ #else
+ /* Perform depth testing */
+ if (!depthTest((UINT16) v->reg[zaColor].u, stats, depth[x], v->reg[fbzMode].u, biasdepth))
+ goto nextpixel;
+
+ /* use the RGBA we stashed above */
+ color.set(sa[pix], sr[pix], sg[pix], sb[pix]);
+
+ /* handle chroma key */
+ if (!chromaKeyTest(v, stats, v->reg[fbzMode].u, color))
+ goto nextpixel;
+ /* handle alpha mask */
+ if (!alphaMaskTest(stats, v->reg[fbzMode].u, color.get_a()))
+ goto nextpixel;
+ /* handle alpha test */
+ if (!alphaTest(v, stats, v->reg[alphaMode].u, color.get_a()))
+ goto nextpixel;
+ #endif
+
+ /* wait for any outstanding work to finish */
+ poly_wait(v->poly, "LFB Write");
+
+ /* pixel pipeline part 2 handles color combine, fog, alpha, and final output */
+ PIXEL_PIPELINE_END(v, stats, dither, dither4, dither_lookup, x, dest, depth,
+ v->reg[fbzMode].u, v->reg[fbzColorPath].u, v->reg[alphaMode].u, v->reg[fogMode].u,
+ iterz, iterw, iterargb);
+nextpixel:
+ /* advance our pointers */
+ x++;
+ mask >>= 4;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/*************************************
+ *
+ * Voodoo texture RAM writes
+ *
+ *************************************/
+
+static INT32 texture_w(voodoo_state *v, offs_t offset, UINT32 data)
+{
+ int tmunum = (offset >> 19) & 0x03;
+ tmu_state *t;
+
+ /* statistics */
+ v->stats.tex_writes++;
+
+ /* point to the right TMU */
+ if (!(v->chipmask & (2 << tmunum)))
+ return 0;
+ t = &v->tmu[tmunum];
+
+ if (TEXLOD_TDIRECT_WRITE(t->reg[tLOD].u))
+ fatalerror("Texture direct write!\n");
+
+ /* wait for any outstanding work to finish */
+ poly_wait(v->poly, "Texture write");
+
+ /* update texture info if dirty */
+ if (t->regdirty)
+ recompute_texture_params(t);
+
+ /* swizzle the data */
+ if (TEXLOD_TDATA_SWIZZLE(t->reg[tLOD].u))
+ data = FLIPENDIAN_INT32(data);
+ if (TEXLOD_TDATA_SWAP(t->reg[tLOD].u))
+ data = (data >> 16) | (data << 16);
+
+ /* 8-bit texture case */
+ if (TEXMODE_FORMAT(t->reg[textureMode].u) < 8)
+ {
+ int lod, tt, ts;
+ UINT32 tbaseaddr;
+ UINT8 *dest;
+
+ /* extract info */
+ if (v->type <= TYPE_VOODOO_2)
+ {
+ lod = (offset >> 15) & 0x0f;
+ tt = (offset >> 7) & 0xff;
+
+ /* old code has a bit about how this is broken in gauntleg unless we always look at TMU0 */
+ if (TEXMODE_SEQ_8_DOWNLD(v->tmu[0].reg/*t->reg*/[textureMode].u))
+ ts = (offset << 2) & 0xfc;
+ else
+ ts = (offset << 1) & 0xfc;
+
+ /* validate parameters */
+ if (lod > 8)
+ return 0;
+
+ /* compute the base address */
+ tbaseaddr = t->lodoffset[lod];
+ tbaseaddr += tt * ((t->wmask >> lod) + 1) + ts;
+
+ if (LOG_TEXTURE_RAM) logerror("Texture 8-bit w: lod=%d s=%d t=%d data=%08X\n", lod, ts, tt, data);
+ }
+ else
+ {
+ tbaseaddr = t->lodoffset[0] + offset*4;
+
+ if (LOG_TEXTURE_RAM) logerror("Texture 8-bit w: offset=%X data=%08X\n", offset*4, data);
+ }
+
+ /* write the four bytes in little-endian order */
+ dest = t->ram;
+ tbaseaddr &= t->mask;
+ dest[BYTE4_XOR_LE(tbaseaddr + 0)] = (data >> 0) & 0xff;
+ dest[BYTE4_XOR_LE(tbaseaddr + 1)] = (data >> 8) & 0xff;
+ dest[BYTE4_XOR_LE(tbaseaddr + 2)] = (data >> 16) & 0xff;
+ dest[BYTE4_XOR_LE(tbaseaddr + 3)] = (data >> 24) & 0xff;
+ }
+
+ /* 16-bit texture case */
+ else
+ {
+ int lod, tt, ts;
+ UINT32 tbaseaddr;
+ UINT16 *dest;
+
+ /* extract info */
+ if (v->type <= TYPE_VOODOO_2)
+ {
+ lod = (offset >> 15) & 0x0f;
+ tt = (offset >> 7) & 0xff;
+ ts = (offset << 1) & 0xfe;
+
+ /* validate parameters */
+ if (lod > 8)
+ return 0;
+
+ /* compute the base address */
+ tbaseaddr = t->lodoffset[lod];
+ tbaseaddr += 2 * (tt * ((t->wmask >> lod) + 1) + ts);
+
+ if (LOG_TEXTURE_RAM) logerror("Texture 16-bit w: lod=%d s=%d t=%d data=%08X\n", lod, ts, tt, data);
+ }
+ else
+ {
+ tbaseaddr = t->lodoffset[0] + offset*4;
+
+ if (LOG_TEXTURE_RAM) logerror("Texture 16-bit w: offset=%X data=%08X\n", offset*4, data);
+ }
+
+ /* write the two words in little-endian order */
+ dest = (UINT16 *)t->ram;
+ tbaseaddr &= t->mask;
+ tbaseaddr >>= 1;
+ dest[BYTE_XOR_LE(tbaseaddr + 0)] = (data >> 0) & 0xffff;
+ dest[BYTE_XOR_LE(tbaseaddr + 1)] = (data >> 16) & 0xffff;
+ }
+
+ return 0;
+}
+
+
+
+/*************************************
+ *
+ * Flush data from the FIFOs
+ *
+ *************************************/
+
+static void flush_fifos(voodoo_state *v, attotime current_time)
+{
+ static UINT8 in_flush;
+
+ /* check for recursive calls */
+ if (in_flush)
+ return;
+ in_flush = TRUE;
+
+ if (!v->pci.op_pending) fatalerror("flush_fifos called with no pending operation\n");
+
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:flush_fifos start -- pending=%d.%08X%08X cur=%d.%08X%08X\n", v->index,
+ v->pci.op_end_time.seconds(), (UINT32)(v->pci.op_end_time.attoseconds() >> 32), (UINT32)v->pci.op_end_time.attoseconds(),
+ current_time.seconds(), (UINT32)(current_time.attoseconds() >> 32), (UINT32)current_time.attoseconds());
+
+ /* loop while we still have cycles to burn */
+ while (v->pci.op_end_time <= current_time)
+ {
+ INT32 extra_cycles = 0;
+ INT32 cycles;
+
+ /* loop over 0-cycle stuff; this constitutes the bulk of our writes */
+ do
+ {
+ fifo_state *fifo;
+ UINT32 address;
+ UINT32 data;
+
+ /* we might be in CMDFIFO mode */
+ if (v->fbi.cmdfifo[0].enable)
+ {
+ /* if we don't have anything to execute, we're done for now */
+ cycles = cmdfifo_execute_if_ready(v, &v->fbi.cmdfifo[0]);
+ if (cycles == -1)
+ {
+ v->pci.op_pending = FALSE;
+ in_flush = FALSE;
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:flush_fifos end -- CMDFIFO empty\n", v->index);
+ return;
+ }
+ }
+ else if (v->fbi.cmdfifo[1].enable)
+ {
+ /* if we don't have anything to execute, we're done for now */
+ cycles = cmdfifo_execute_if_ready(v, &v->fbi.cmdfifo[1]);
+ if (cycles == -1)
+ {
+ v->pci.op_pending = FALSE;
+ in_flush = FALSE;
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:flush_fifos end -- CMDFIFO empty\n", v->index);
+ return;
+ }
+ }
+
+ /* else we are in standard PCI/memory FIFO mode */
+ else
+ {
+ /* choose which FIFO to read from */
+ if (!fifo_empty(&v->fbi.fifo))
+ fifo = &v->fbi.fifo;
+ else if (!fifo_empty(&v->pci.fifo))
+ fifo = &v->pci.fifo;
+ else
+ {
+ v->pci.op_pending = FALSE;
+ in_flush = FALSE;
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:flush_fifos end -- FIFOs empty\n", v->index);
+ return;
+ }
+
+ /* extract address and data */
+ address = fifo_remove(fifo);
+ data = fifo_remove(fifo);
+
+ /* target the appropriate location */
+ if ((address & (0xc00000/4)) == 0)
+ cycles = register_w(v, address, data);
+ else if (address & (0x800000/4))
+ cycles = texture_w(v, address, data);
+ else
+ {
+ UINT32 mem_mask = 0xffffffff;
+
+ /* compute mem_mask */
+ if (address & 0x80000000)
+ mem_mask &= 0x0000ffff;
+ if (address & 0x40000000)
+ mem_mask &= 0xffff0000;
+ address &= 0xffffff;
+
+ cycles = lfb_w(v, address, data, mem_mask);
+ }
+ }
+
+ /* accumulate smaller operations */
+ if (cycles < ACCUMULATE_THRESHOLD)
+ {
+ extra_cycles += cycles;
+ cycles = 0;
+ }
+ }
+ while (cycles == 0);
+
+ /* account for extra cycles */
+ cycles += extra_cycles;
+
+ /* account for those cycles */
+ v->pci.op_end_time += attotime(0, (attoseconds_t)cycles * v->attoseconds_per_cycle);
+
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:update -- pending=%d.%08X%08X cur=%d.%08X%08X\n", v->index,
+ v->pci.op_end_time.seconds(), (UINT32)(v->pci.op_end_time.attoseconds() >> 32), (UINT32)v->pci.op_end_time.attoseconds(),
+ current_time.seconds(), (UINT32)(current_time.attoseconds() >> 32), (UINT32)current_time.attoseconds());
+ }
+
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:flush_fifos end -- pending command complete at %d.%08X%08X\n", v->index,
+ v->pci.op_end_time.seconds(), (UINT32)(v->pci.op_end_time.attoseconds() >> 32), (UINT32)v->pci.op_end_time.attoseconds());
+
+ in_flush = FALSE;
+}
+
+
+
+/*************************************
+ *
+ * Handle a write to the Voodoo
+ * memory space
+ *
+ *************************************/
+
+WRITE32_MEMBER( voodoo_device::voodoo_w )
+{
+ voodoo_state *v = get_safe_token(this);
+ int stall = FALSE;
+
+ g_profiler.start(PROFILER_USER1);
+
+ /* should not be getting accesses while stalled */
+ if (v->pci.stall_state != NOT_STALLED)
+ logerror("voodoo_w while stalled!\n");
+
+ /* if we have something pending, flush the FIFOs up to the current time */
+ if (v->pci.op_pending)
+ flush_fifos(v, machine().time());
+
+ /* special handling for registers */
+ if ((offset & 0xc00000/4) == 0)
+ {
+ UINT8 access;
+
+ /* some special stuff for Voodoo 2 */
+ if (v->type >= TYPE_VOODOO_2)
+ {
+ /* we might be in CMDFIFO mode */
+ if (FBIINIT7_CMDFIFO_ENABLE(v->reg[fbiInit7].u))
+ {
+ /* if bit 21 is set, we're writing to the FIFO */
+ if (offset & 0x200000/4)
+ {
+ /* check for byte swizzling (bit 18) */
+ if (offset & 0x40000/4)
+ data = FLIPENDIAN_INT32(data);
+ cmdfifo_w(v, &v->fbi.cmdfifo[0], offset & 0xffff, data);
+ g_profiler.stop();
+ return;
+ }
+
+ /* we're a register access; but only certain ones are allowed */
+ access = v->regaccess[offset & 0xff];
+ if (!(access & REGISTER_WRITETHRU))
+ {
+ /* track swap buffers regardless */
+ if ((offset & 0xff) == swapbufferCMD)
+ v->fbi.swaps_pending++;
+
+ logerror("Ignoring write to %s in CMDFIFO mode\n", v->regnames[offset & 0xff]);
+ g_profiler.stop();
+ return;
+ }
+ }
+
+ /* if not, we might be byte swizzled (bit 20) */
+ else if (offset & 0x100000/4)
+ data = FLIPENDIAN_INT32(data);
+ }
+
+ /* check the access behavior; note that the table works even if the */
+ /* alternate mapping is used */
+ access = v->regaccess[offset & 0xff];
+
+ /* ignore if writes aren't allowed */
+ if (!(access & REGISTER_WRITE))
+ {
+ g_profiler.stop();
+ return;
+ }
+
+ /* if this is a non-FIFO command, let it go to the FIFO, but stall until it completes */
+ if (!(access & REGISTER_FIFO))
+ stall = TRUE;
+
+ /* track swap buffers */
+ if ((offset & 0xff) == swapbufferCMD)
+ v->fbi.swaps_pending++;
+ }
+
+ /* if we don't have anything pending, or if FIFOs are disabled, just execute */
+ if (!v->pci.op_pending || !INITEN_ENABLE_PCI_FIFO(v->pci.init_enable))
+ {
+ int cycles;
+
+ /* target the appropriate location */
+ if ((offset & (0xc00000/4)) == 0)
+ cycles = register_w(v, offset, data);
+ else if (offset & (0x800000/4))
+ cycles = texture_w(v, offset, data);
+ else
+ cycles = lfb_w(v, offset, data, mem_mask);
+
+ /* if we ended up with cycles, mark the operation pending */
+ if (cycles)
+ {
+ v->pci.op_pending = TRUE;
+ v->pci.op_end_time = machine().time() + attotime(0, (attoseconds_t)cycles * v->attoseconds_per_cycle);
+
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:direct write start at %d.%08X%08X end at %d.%08X%08X\n", v->index,
+ machine().time().seconds(), (UINT32)(machine().time().attoseconds() >> 32), (UINT32)machine().time().attoseconds(),
+ v->pci.op_end_time.seconds(), (UINT32)(v->pci.op_end_time.attoseconds() >> 32), (UINT32)v->pci.op_end_time.attoseconds());
+ }
+ g_profiler.stop();
+ return;
+ }
+
+ /* modify the offset based on the mem_mask */
+ if (mem_mask != 0xffffffff)
+ {
+ if (!ACCESSING_BITS_16_31)
+ offset |= 0x80000000;
+ if (!ACCESSING_BITS_0_15)
+ offset |= 0x40000000;
+ }
+
+ /* if there's room in the PCI FIFO, add there */
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:voodoo_w adding to PCI FIFO @ %08X=%08X\n", v->index, offset, data);
+ if (!fifo_full(&v->pci.fifo))
+ {
+ fifo_add(&v->pci.fifo, offset);
+ fifo_add(&v->pci.fifo, data);
+ }
+ else
+ fatalerror("PCI FIFO full\n");
+
+ /* handle flushing to the memory FIFO */
+ if (FBIINIT0_ENABLE_MEMORY_FIFO(v->reg[fbiInit0].u) &&
+ fifo_space(&v->pci.fifo) <= 2 * FBIINIT4_MEMORY_FIFO_LWM(v->reg[fbiInit4].u))
+ {
+ UINT8 valid[4];
+
+ /* determine which types of data can go to the memory FIFO */
+ valid[0] = TRUE;
+ valid[1] = FBIINIT0_LFB_TO_MEMORY_FIFO(v->reg[fbiInit0].u);
+ valid[2] = valid[3] = FBIINIT0_TEXMEM_TO_MEMORY_FIFO(v->reg[fbiInit0].u);
+
+ /* flush everything we can */
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:voodoo_w moving PCI FIFO to memory FIFO\n", v->index);
+ while (!fifo_empty(&v->pci.fifo) && valid[(fifo_peek(&v->pci.fifo) >> 22) & 3])
+ {
+ fifo_add(&v->fbi.fifo, fifo_remove(&v->pci.fifo));
+ fifo_add(&v->fbi.fifo, fifo_remove(&v->pci.fifo));
+ }
+
+ /* if we're above the HWM as a result, stall */
+ if (FBIINIT0_STALL_PCIE_FOR_HWM(v->reg[fbiInit0].u) &&
+ fifo_items(&v->fbi.fifo) >= 2 * 32 * FBIINIT0_MEMORY_FIFO_HWM(v->reg[fbiInit0].u))
+ {
+ if (LOG_FIFO) logerror("VOODOO.%d.FIFO:voodoo_w hit memory FIFO HWM -- stalling\n", v->index);
+ stall_cpu(v, STALLED_UNTIL_FIFO_LWM, machine().time());
+ }
+ }
+
+ /* if we're at the LWM for the PCI FIFO, stall */
+ if (FBIINIT0_STALL_PCIE_FOR_HWM(v->reg[fbiInit0].u) &&
+ fifo_space(&v->pci.fifo) <= 2 * FBIINIT0_PCI_FIFO_LWM(v->reg[fbiInit0].u))
+ {
+ if (LOG_FIFO) logerror("VOODOO.%d.FIFO:voodoo_w hit PCI FIFO free LWM -- stalling\n", v->index);
+ stall_cpu(v, STALLED_UNTIL_FIFO_LWM, machine().time());
+ }
+
+ /* if we weren't ready, and this is a non-FIFO access, stall until the FIFOs are clear */
+ if (stall)
+ {
+ if (LOG_FIFO_VERBOSE) logerror("VOODOO.%d.FIFO:voodoo_w wrote non-FIFO register -- stalling until clear\n", v->index);
+ stall_cpu(v, STALLED_UNTIL_FIFO_EMPTY, machine().time());
+ }
+
+ g_profiler.stop();
+}
+
+
+
+/*************************************
+ *
+ * Handle a register read
+ *
+ *************************************/
+
+static UINT32 register_r(voodoo_state *v, offs_t offset)
+{
+ int regnum = offset & 0xff;
+ UINT32 result;
+
+ /* statistics */
+ v->stats.reg_reads++;
+
+ /* first make sure this register is readable */
+ if (!(v->regaccess[regnum] & REGISTER_READ))
+ {
+ logerror("VOODOO.%d.ERROR:Invalid attempt to read %s\n", v->index, regnum < 225 ? v->regnames[regnum] : "unknown register");
+ return 0xffffffff;
+ }
+
+ /* default result is the FBI register value */
+ result = v->reg[regnum].u;
+
+ /* some registers are dynamic; compute them */
+ switch (regnum)
+ {
+ case status:
+
+ /* start with a blank slate */
+ result = 0;
+
+ /* bits 5:0 are the PCI FIFO free space */
+ if (fifo_empty(&v->pci.fifo))
+ result |= 0x3f << 0;
+ else
+ {
+ int temp = fifo_space(&v->pci.fifo)/2;
+ if (temp > 0x3f)
+ temp = 0x3f;
+ result |= temp << 0;
+ }
+
+ /* bit 6 is the vertical retrace */
+ result |= v->fbi.vblank << 6;
+
+ /* bit 7 is FBI graphics engine busy */
+ if (v->pci.op_pending)
+ result |= 1 << 7;
+
+ /* bit 8 is TREX busy */
+ if (v->pci.op_pending)
+ result |= 1 << 8;
+
+ /* bit 9 is overall busy */
+ if (v->pci.op_pending)
+ result |= 1 << 9;
+
+ /* Banshee is different starting here */
+ if (v->type < TYPE_VOODOO_BANSHEE)
+ {
+ /* bits 11:10 specifies which buffer is visible */
+ result |= v->fbi.frontbuf << 10;
+
+ /* bits 27:12 indicate memory FIFO freespace */
+ if (!FBIINIT0_ENABLE_MEMORY_FIFO(v->reg[fbiInit0].u) || fifo_empty(&v->fbi.fifo))
+ result |= 0xffff << 12;
+ else
+ {
+ int temp = fifo_space(&v->fbi.fifo)/2;
+ if (temp > 0xffff)
+ temp = 0xffff;
+ result |= temp << 12;
+ }
+ }
+ else
+ {
+ /* bit 10 is 2D busy */
+
+ /* bit 11 is cmd FIFO 0 busy */
+ if (v->fbi.cmdfifo[0].enable && v->fbi.cmdfifo[0].depth > 0)
+ result |= 1 << 11;
+
+ /* bit 12 is cmd FIFO 1 busy */
+ if (v->fbi.cmdfifo[1].enable && v->fbi.cmdfifo[1].depth > 0)
+ result |= 1 << 12;
+ }
+
+ /* bits 30:28 are the number of pending swaps */
+ if (v->fbi.swaps_pending > 7)
+ result |= 7 << 28;
+ else
+ result |= v->fbi.swaps_pending << 28;
+
+ /* bit 31 is not used */
+
+ /* eat some cycles since people like polling here */
+ if (EAT_CYCLES) v->cpu->execute().eat_cycles(1000);
+ break;
+
+ /* bit 2 of the initEnable register maps this to dacRead */
+ case fbiInit2:
+ if (INITEN_REMAP_INIT_TO_DAC(v->pci.init_enable))
+ result = v->dac.read_result;
+ break;
+
+ /* return the current scanline for now */
+ case vRetrace:
+
+ /* eat some cycles since people like polling here */
+ if (EAT_CYCLES) v->cpu->execute().eat_cycles(10);
+ result = v->screen->vpos();
+ break;
+
+ /* reserved area in the TMU read by the Vegas startup sequence */
+ case hvRetrace:
+ result = 0x200 << 16; /* should be between 0x7b and 0x267 */
+ result |= 0x80; /* should be between 0x17 and 0x103 */
+ break;
+
+ /* cmdFifo -- Voodoo2 only */
+ case cmdFifoRdPtr:
+ result = v->fbi.cmdfifo[0].rdptr;
+
+ /* eat some cycles since people like polling here */
+ if (EAT_CYCLES) v->cpu->execute().eat_cycles(1000);
+ break;
+
+ case cmdFifoAMin:
+ result = v->fbi.cmdfifo[0].amin;
+ break;
+
+ case cmdFifoAMax:
+ result = v->fbi.cmdfifo[0].amax;
+ break;
+
+ case cmdFifoDepth:
+ result = v->fbi.cmdfifo[0].depth;
+ break;
+
+ case cmdFifoHoles:
+ result = v->fbi.cmdfifo[0].holes;
+ break;
+
+ /* all counters are 24-bit only */
+ case fbiPixelsIn:
+ case fbiChromaFail:
+ case fbiZfuncFail:
+ case fbiAfuncFail:
+ case fbiPixelsOut:
+ update_statistics(v, TRUE);
+ case fbiTrianglesOut:
+ result = v->reg[regnum].u & 0xffffff;
+ break;
+ }
+
+ if (LOG_REGISTERS)
+ {
+ int logit = TRUE;
+
+ /* don't log multiple identical status reads from the same address */
+ if (regnum == status)
+ {
+ offs_t pc = v->cpu->safe_pc();
+ if (pc == v->last_status_pc && result == v->last_status_value)
+ logit = FALSE;
+ v->last_status_pc = pc;
+ v->last_status_value = result;
+ }
+ if (regnum == cmdFifoRdPtr)
+ logit = FALSE;
+
+ if (logit)
+ logerror("VOODOO.%d.REG:%s read = %08X\n", v->index, v->regnames[regnum], result);
+ }
+
+ return result;
+}
+
+
+
+/*************************************
+ *
+ * Handle an LFB read
+ *
+ *************************************/
+
+static UINT32 lfb_r(voodoo_state *v, offs_t offset, bool lfb_3d)
+{
+ UINT16 *buffer;
+ UINT32 bufmax;
+ UINT32 bufoffs;
+ UINT32 data;
+ int x, y, scry, destbuf;
+
+ /* statistics */
+ v->stats.lfb_reads++;
+
+ /* compute X,Y */
+ offset <<= 1;
+ x = offset & ((1 << v->fbi.lfb_stride) - 1);
+ y = (offset >> v->fbi.lfb_stride);
+
+ /* select the target buffer */
+ if (lfb_3d) {
+ y &= 0x3ff;
+ destbuf = (v->type >= TYPE_VOODOO_BANSHEE) ? 1 : LFBMODE_READ_BUFFER_SELECT(v->reg[lfbMode].u);
+ switch (destbuf)
+ {
+ case 0: /* front buffer */
+ buffer = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ bufmax = (v->fbi.mask + 1 - v->fbi.rgboffs[v->fbi.frontbuf]) / 2;
+ break;
+
+ case 1: /* back buffer */
+ buffer = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.backbuf]);
+ bufmax = (v->fbi.mask + 1 - v->fbi.rgboffs[v->fbi.backbuf]) / 2;
+ break;
+
+ case 2: /* aux buffer */
+ if (v->fbi.auxoffs == ~0)
+ return 0xffffffff;
+ buffer = (UINT16 *)(v->fbi.ram + v->fbi.auxoffs);
+ bufmax = (v->fbi.mask + 1 - v->fbi.auxoffs) / 2;
+ break;
+
+ default: /* reserved */
+ return 0xffffffff;
+ }
+
+ /* determine the screen Y */
+ scry = y;
+ if (LFBMODE_Y_ORIGIN(v->reg[lfbMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+ } else {
+ // Direct lfb access
+ buffer = (UINT16 *)(v->fbi.ram + v->fbi.lfb_base*4);
+ bufmax = (v->fbi.mask + 1 - v->fbi.lfb_base*4) / 2;
+ scry = y;
+ }
+
+ /* advance pointers to the proper row */
+ bufoffs = scry * v->fbi.rowpixels + x;
+ if (bufoffs >= bufmax) {
+ logerror("LFB_R: Buffer offset out of bounds x=%i y=%i lfb_3d=%i offset=%08X bufoffs=%08X\n", x, y, lfb_3d, offset, (UINT32) bufoffs);
+ return 0xffffffff;
+ }
+
+ /* wait for any outstanding work to finish */
+ poly_wait(v->poly, "LFB read");
+
+ /* compute the data */
+ data = buffer[bufoffs + 0] | (buffer[bufoffs + 1] << 16);
+
+ /* word swapping */
+ if (LFBMODE_WORD_SWAP_READS(v->reg[lfbMode].u))
+ data = (data << 16) | (data >> 16);
+
+ /* byte swizzling */
+ if (LFBMODE_BYTE_SWIZZLE_READS(v->reg[lfbMode].u))
+ data = FLIPENDIAN_INT32(data);
+
+ if (LOG_LFB) logerror("VOODOO.%d.LFB:read (%d,%d) = %08X\n", v->index, x, y, data);
+ return data;
+}
+
+
+
+/*************************************
+ *
+ * Handle a read from the Voodoo
+ * memory space
+ *
+ *************************************/
+
+READ32_MEMBER( voodoo_device::voodoo_r )
+{
+ voodoo_state *v = get_safe_token(this);
+
+ /* if we have something pending, flush the FIFOs up to the current time */
+ if (v->pci.op_pending)
+ flush_fifos(v, machine().time());
+
+ /* target the appropriate location */
+ if (!(offset & (0xc00000/4)))
+ return register_r(v, offset);
+ else if (!(offset & (0x800000/4)))
+ return lfb_r(v, offset, true);
+
+ return 0xffffffff;
+}
+
+
+
+/*************************************
+ *
+ * Handle a read from the Banshee
+ * I/O space
+ *
+ *************************************/
+
+READ32_MEMBER( voodoo_banshee_device::banshee_agp_r )
+{
+ voodoo_state *v = get_safe_token(this);
+ UINT32 result;
+
+ offset &= 0x1ff/4;
+
+ /* switch off the offset */
+ switch (offset)
+ {
+ case cmdRdPtrL0:
+ result = v->fbi.cmdfifo[0].rdptr;
+ break;
+
+ case cmdAMin0:
+ result = v->fbi.cmdfifo[0].amin;
+ break;
+
+ case cmdAMax0:
+ result = v->fbi.cmdfifo[0].amax;
+ break;
+
+ case cmdFifoDepth0:
+ result = v->fbi.cmdfifo[0].depth;
+ break;
+
+ case cmdHoleCnt0:
+ result = v->fbi.cmdfifo[0].holes;
+ break;
+
+ case cmdRdPtrL1:
+ result = v->fbi.cmdfifo[1].rdptr;
+ break;
+
+ case cmdAMin1:
+ result = v->fbi.cmdfifo[1].amin;
+ break;
+
+ case cmdAMax1:
+ result = v->fbi.cmdfifo[1].amax;
+ break;
+
+ case cmdFifoDepth1:
+ result = v->fbi.cmdfifo[1].depth;
+ break;
+
+ case cmdHoleCnt1:
+ result = v->fbi.cmdfifo[1].holes;
+ break;
+
+ default:
+ result = v->banshee.agp[offset];
+ break;
+ }
+
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_r(AGP:%s)\n", v->device->machine().describe_context(), banshee_agp_reg_name[offset]);
+ return result;
+}
+
+
+READ32_MEMBER( voodoo_banshee_device::banshee_r )
+{
+ voodoo_state *v = get_safe_token(this);
+ UINT32 result = 0xffffffff;
+
+ /* if we have something pending, flush the FIFOs up to the current time */
+ if (v->pci.op_pending)
+ flush_fifos(v, machine().time());
+
+ if (offset < 0x80000/4)
+ result = banshee_io_r(space, offset, mem_mask);
+ else if (offset < 0x100000/4)
+ result = banshee_agp_r(space, offset, mem_mask);
+ else if (offset < 0x200000/4)
+ logerror("%s:banshee_r(2D:%X)\n", machine().describe_context(), (offset*4) & 0xfffff);
+ else if (offset < 0x600000/4)
+ result = register_r(v, offset & 0x1fffff/4);
+ else if (offset < 0x800000/4)
+ logerror("%s:banshee_r(TEX0:%X)\n", machine().describe_context(), (offset*4) & 0x1fffff);
+ else if (offset < 0xa00000/4)
+ logerror("%s:banshee_r(TEX1:%X)\n", machine().describe_context(), (offset*4) & 0x1fffff);
+ else if (offset < 0xc00000/4)
+ logerror("%s:banshee_r(FLASH Bios ROM:%X)\n", machine().describe_context(), (offset*4) & 0x3fffff);
+ else if (offset < 0x1000000/4)
+ logerror("%s:banshee_r(YUV:%X)\n", machine().describe_context(), (offset*4) & 0x3fffff);
+ else if (offset < 0x2000000/4)
+ {
+ result = lfb_r(v, offset & 0xffffff/4, true);
+ } else {
+ logerror("%s:banshee_r(%X) Access out of bounds\n", machine().describe_context(), offset*4);
+ }
+ return result;
+}
+
+
+READ32_MEMBER( voodoo_banshee_device::banshee_fb_r )
+{
+ voodoo_state *v = get_safe_token(this);
+ UINT32 result = 0xffffffff;
+
+ /* if we have something pending, flush the FIFOs up to the current time */
+ if (v->pci.op_pending)
+ flush_fifos(v, machine().time());
+
+ if (offset < v->fbi.lfb_base)
+ {
+#if LOG_LFB
+ logerror("%s:banshee_fb_r(%X)\n", machine().describe_context(), offset*4);
+#endif
+ if (offset*4 <= v->fbi.mask)
+ result = ((UINT32 *)v->fbi.ram)[offset];
+ else
+ logerror("%s:banshee_fb_r(%X) Access out of bounds\n", machine().describe_context(), offset*4);
+ }
+ else {
+ if (LOG_LFB)
+ logerror("%s:banshee_fb_r(%X) to lfb_r: %08X lfb_base=%08X\n", machine().describe_context(), offset*4, offset - v->fbi.lfb_base, v->fbi.lfb_base);
+ result = lfb_r(v, offset - v->fbi.lfb_base, false);
+ }
+ return result;
+}
+
+
+READ8_MEMBER( voodoo_banshee_device::banshee_vga_r )
+{
+ voodoo_state *v = get_safe_token(this);
+ UINT8 result = 0xff;
+
+ offset &= 0x1f;
+
+ /* switch off the offset */
+ switch (offset + 0x3c0)
+ {
+ /* attribute access */
+ case 0x3c0:
+ if (v->banshee.vga[0x3c1 & 0x1f] < ARRAY_LENGTH(v->banshee.att))
+ result = v->banshee.att[v->banshee.vga[0x3c1 & 0x1f]];
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_att_r(%X)\n", machine().describe_context(), v->banshee.vga[0x3c1 & 0x1f]);
+ break;
+
+ /* Input status 0 */
+ case 0x3c2:
+ /*
+ bit 7 = Interrupt Status. When its value is ?1?, denotes that an interrupt is pending.
+ bit 6:5 = Feature Connector. These 2 bits are readable bits from the feature connector.
+ bit 4 = Sense. This bit reflects the state of the DAC monitor sense logic.
+ bit 3:0 = Reserved. Read back as 0.
+ */
+ result = 0x00;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_vga_r(%X)\n", machine().describe_context(), 0x300+offset);
+ break;
+
+ /* Sequencer access */
+ case 0x3c5:
+ if (v->banshee.vga[0x3c4 & 0x1f] < ARRAY_LENGTH(v->banshee.seq))
+ result = v->banshee.seq[v->banshee.vga[0x3c4 & 0x1f]];
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_seq_r(%X)\n", machine().describe_context(), v->banshee.vga[0x3c4 & 0x1f]);
+ break;
+
+ /* Feature control */
+ case 0x3ca:
+ result = v->banshee.vga[0x3da & 0x1f];
+ v->banshee.attff = 0;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_vga_r(%X)\n", machine().describe_context(), 0x300+offset);
+ break;
+
+ /* Miscellaneous output */
+ case 0x3cc:
+ result = v->banshee.vga[0x3c2 & 0x1f];
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_vga_r(%X)\n", machine().describe_context(), 0x300+offset);
+ break;
+
+ /* Graphics controller access */
+ case 0x3cf:
+ if (v->banshee.vga[0x3ce & 0x1f] < ARRAY_LENGTH(v->banshee.gc))
+ result = v->banshee.gc[v->banshee.vga[0x3ce & 0x1f]];
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_gc_r(%X)\n", machine().describe_context(), v->banshee.vga[0x3ce & 0x1f]);
+ break;
+
+ /* CRTC access */
+ case 0x3d5:
+ if (v->banshee.vga[0x3d4 & 0x1f] < ARRAY_LENGTH(v->banshee.crtc))
+ result = v->banshee.crtc[v->banshee.vga[0x3d4 & 0x1f]];
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_crtc_r(%X)\n", machine().describe_context(), v->banshee.vga[0x3d4 & 0x1f]);
+ break;
+
+ /* Input status 1 */
+ case 0x3da:
+ /*
+ bit 7:6 = Reserved. These bits read back 0.
+ bit 5:4 = Display Status. These 2 bits reflect 2 of the 8 pixel data outputs from the Attribute
+ controller, as determined by the Attribute controller index 0x12 bits 4 and 5.
+ bit 3 = Vertical sync Status. A ?1? indicates vertical retrace is in progress.
+ bit 2:1 = Reserved. These bits read back 0x2.
+ bit 0 = Display Disable. When this bit is 1, either horizontal or vertical display end has occurred,
+ otherwise video data is being displayed.
+ */
+ result = 0x04;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_vga_r(%X)\n", machine().describe_context(), 0x300+offset);
+ break;
+
+ default:
+ result = v->banshee.vga[offset];
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_vga_r(%X)\n", machine().describe_context(), 0x300+offset);
+ break;
+ }
+ return result;
+}
+
+
+READ32_MEMBER( voodoo_banshee_device::banshee_io_r )
+{
+ voodoo_state *v = get_safe_token(this);
+ UINT32 result;
+
+ offset &= 0xff/4;
+
+ /* switch off the offset */
+ switch (offset)
+ {
+ case io_status:
+ result = register_r(v, 0);
+ break;
+
+ case io_dacData:
+ result = v->fbi.clut[v->banshee.io[io_dacAddr] & 0x1ff] = v->banshee.io[offset];
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_dac_r(%X)\n", machine().describe_context(), v->banshee.io[io_dacAddr] & 0x1ff);
+ break;
+
+ case io_vgab0: case io_vgab4: case io_vgab8: case io_vgabc:
+ case io_vgac0: case io_vgac4: case io_vgac8: case io_vgacc:
+ case io_vgad0: case io_vgad4: case io_vgad8: case io_vgadc:
+ result = 0;
+ if (ACCESSING_BITS_0_7)
+ result |= banshee_vga_r(space, offset*4+0, mem_mask >> 0) << 0;
+ if (ACCESSING_BITS_8_15)
+ result |= banshee_vga_r(space, offset*4+1, mem_mask >> 8) << 8;
+ if (ACCESSING_BITS_16_23)
+ result |= banshee_vga_r(space, offset*4+2, mem_mask >> 16) << 16;
+ if (ACCESSING_BITS_24_31)
+ result |= banshee_vga_r(space, offset*4+3, mem_mask >> 24) << 24;
+ break;
+
+ default:
+ result = v->banshee.io[offset];
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_io_r(%s)\n", machine().describe_context(), banshee_io_reg_name[offset]);
+ break;
+ }
+
+ return result;
+}
+
+
+READ32_MEMBER( voodoo_banshee_device::banshee_rom_r )
+{
+ logerror("%s:banshee_rom_r(%X)\n", machine().describe_context(), offset*4);
+ return 0xffffffff;
+}
+
+static void blit_2d(voodoo_state *v, UINT32 data)
+{
+ switch (v->banshee.blt_cmd)
+ {
+ case 0: // NOP - wait for idle
+ {
+ break;
+ }
+
+ case 1: // Screen-to-screen blit
+ {
+ // TODO
+#if LOG_BANSHEE_2D
+ logerror(" blit_2d:screen_to_screen: src X %d, src Y %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ break;
+ }
+
+ case 2: // Screen-to-screen stretch blit
+ {
+ fatalerror(" blit_2d:screen_to_screen_stretch: src X %d, src Y %d\n", data & 0xfff, (data >> 16) & 0xfff);
+ }
+
+ case 3: // Host-to-screen blit
+ {
+ UINT32 addr = v->banshee.blt_dst_base;
+
+ addr += (v->banshee.blt_dst_y * v->banshee.blt_dst_stride) + (v->banshee.blt_dst_x * v->banshee.blt_dst_bpp);
+
+#if LOG_BANSHEE_2D
+ logerror(" blit_2d:host_to_screen: %08x -> %08x, %d, %d\n", data, addr, v->banshee.blt_dst_x, v->banshee.blt_dst_y);
+#endif
+
+ switch (v->banshee.blt_dst_bpp)
+ {
+ case 1:
+ v->fbi.ram[addr+0] = data & 0xff;
+ v->fbi.ram[addr+1] = (data >> 8) & 0xff;
+ v->fbi.ram[addr+2] = (data >> 16) & 0xff;
+ v->fbi.ram[addr+3] = (data >> 24) & 0xff;
+ v->banshee.blt_dst_x += 4;
+ break;
+ case 2:
+ v->fbi.ram[addr+1] = data & 0xff;
+ v->fbi.ram[addr+0] = (data >> 8) & 0xff;
+ v->fbi.ram[addr+3] = (data >> 16) & 0xff;
+ v->fbi.ram[addr+2] = (data >> 24) & 0xff;
+ v->banshee.blt_dst_x += 2;
+ break;
+ case 3:
+ v->banshee.blt_dst_x += 1;
+ break;
+ case 4:
+ v->fbi.ram[addr+3] = data & 0xff;
+ v->fbi.ram[addr+2] = (data >> 8) & 0xff;
+ v->fbi.ram[addr+1] = (data >> 16) & 0xff;
+ v->fbi.ram[addr+0] = (data >> 24) & 0xff;
+ v->banshee.blt_dst_x += 1;
+ break;
+ }
+
+ if (v->banshee.blt_dst_x >= v->banshee.blt_dst_width)
+ {
+ v->banshee.blt_dst_x = 0;
+ v->banshee.blt_dst_y++;
+ }
+ break;
+ }
+
+ case 5: // Rectangle fill
+ {
+ fatalerror("blit_2d:rectangle_fill: src X %d, src Y %d\n", data & 0xfff, (data >> 16) & 0xfff);
+ }
+
+ case 6: // Line
+ {
+ fatalerror("blit_2d:line: end X %d, end Y %d\n", data & 0xfff, (data >> 16) & 0xfff);
+ }
+
+ case 7: // Polyline
+ {
+ fatalerror("blit_2d:polyline: end X %d, end Y %d\n", data & 0xfff, (data >> 16) & 0xfff);
+ }
+
+ case 8: // Polygon fill
+ {
+ fatalerror("blit_2d:polygon_fill\n");
+ }
+
+ default:
+ {
+ fatalerror("blit_2d: unknown command %d\n", v->banshee.blt_cmd);
+ }
+ }
+}
+
+static INT32 banshee_2d_w(voodoo_state *v, offs_t offset, UINT32 data)
+{
+ switch (offset)
+ {
+ case banshee2D_command:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:command: cmd %d, ROP0 %02X\n", data & 0xf, data >> 24);
+#endif
+
+ v->banshee.blt_src_x = v->banshee.blt_regs[banshee2D_srcXY] & 0xfff;
+ v->banshee.blt_src_y = (v->banshee.blt_regs[banshee2D_srcXY] >> 16) & 0xfff;
+ v->banshee.blt_src_base = v->banshee.blt_regs[banshee2D_srcBaseAddr] & 0xffffff;
+ v->banshee.blt_src_stride = v->banshee.blt_regs[banshee2D_srcFormat] & 0x3fff;
+ v->banshee.blt_src_width = v->banshee.blt_regs[banshee2D_srcSize] & 0xfff;
+ v->banshee.blt_src_height = (v->banshee.blt_regs[banshee2D_srcSize] >> 16) & 0xfff;
+
+ switch ((v->banshee.blt_regs[banshee2D_srcFormat] >> 16) & 0xf)
+ {
+ case 1: v->banshee.blt_src_bpp = 1; break;
+ case 3: v->banshee.blt_src_bpp = 2; break;
+ case 4: v->banshee.blt_src_bpp = 3; break;
+ case 5: v->banshee.blt_src_bpp = 4; break;
+ case 8: v->banshee.blt_src_bpp = 2; break;
+ case 9: v->banshee.blt_src_bpp = 2; break;
+ default: v->banshee.blt_src_bpp = 1; break;
+ }
+
+ v->banshee.blt_dst_x = v->banshee.blt_regs[banshee2D_dstXY] & 0xfff;
+ v->banshee.blt_dst_y = (v->banshee.blt_regs[banshee2D_dstXY] >> 16) & 0xfff;
+ v->banshee.blt_dst_base = v->banshee.blt_regs[banshee2D_dstBaseAddr] & 0xffffff;
+ v->banshee.blt_dst_stride = v->banshee.blt_regs[banshee2D_dstFormat] & 0x3fff;
+ v->banshee.blt_dst_width = v->banshee.blt_regs[banshee2D_dstSize] & 0xfff;
+ v->banshee.blt_dst_height = (v->banshee.blt_regs[banshee2D_dstSize] >> 16) & 0xfff;
+
+ switch ((v->banshee.blt_regs[banshee2D_dstFormat] >> 16) & 0x7)
+ {
+ case 1: v->banshee.blt_dst_bpp = 1; break;
+ case 3: v->banshee.blt_dst_bpp = 2; break;
+ case 4: v->banshee.blt_dst_bpp = 3; break;
+ case 5: v->banshee.blt_dst_bpp = 4; break;
+ default: v->banshee.blt_dst_bpp = 1; break;
+ }
+
+ v->banshee.blt_cmd = data & 0xf;
+ break;
+
+ case banshee2D_colorBack:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:colorBack: %08X\n", data);
+#endif
+ v->banshee.blt_regs[banshee2D_colorBack] = data;
+ break;
+
+ case banshee2D_colorFore:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:colorFore: %08X\n", data);
+#endif
+ v->banshee.blt_regs[banshee2D_colorFore] = data;
+ break;
+
+ case banshee2D_srcBaseAddr:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:srcBaseAddr: %08X, %s\n", data & 0xffffff, data & 0x80000000 ? "tiled" : "non-tiled");
+#endif
+ v->banshee.blt_regs[banshee2D_srcBaseAddr] = data;
+ break;
+
+ case banshee2D_dstBaseAddr:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:dstBaseAddr: %08X, %s\n", data & 0xffffff, data & 0x80000000 ? "tiled" : "non-tiled");
+#endif
+ v->banshee.blt_regs[banshee2D_dstBaseAddr] = data;
+ break;
+
+ case banshee2D_srcSize:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:srcSize: %d, %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ v->banshee.blt_regs[banshee2D_srcSize] = data;
+ break;
+
+ case banshee2D_dstSize:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:dstSize: %d, %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ v->banshee.blt_regs[banshee2D_dstSize] = data;
+ break;
+
+ case banshee2D_srcXY:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:srcXY: %d, %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ v->banshee.blt_regs[banshee2D_srcXY] = data;
+ break;
+
+ case banshee2D_dstXY:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:dstXY: %d, %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ v->banshee.blt_regs[banshee2D_dstXY] = data;
+ break;
+
+ case banshee2D_srcFormat:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:srcFormat: str %d, fmt %d, packing %d\n", data & 0x3fff, (data >> 16) & 0xf, (data >> 22) & 0x3);
+#endif
+ v->banshee.blt_regs[banshee2D_srcFormat] = data;
+ break;
+
+ case banshee2D_dstFormat:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:dstFormat: str %d, fmt %d\n", data & 0x3fff, (data >> 16) & 0xf);
+#endif
+ v->banshee.blt_regs[banshee2D_dstFormat] = data;
+ break;
+
+ case banshee2D_clip0Min:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:clip0Min: %d, %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ v->banshee.blt_regs[banshee2D_clip0Min] = data;
+ break;
+
+ case banshee2D_clip0Max:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:clip0Max: %d, %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ v->banshee.blt_regs[banshee2D_clip0Max] = data;
+ break;
+
+ case banshee2D_clip1Min:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:clip1Min: %d, %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ v->banshee.blt_regs[banshee2D_clip1Min] = data;
+ break;
+
+ case banshee2D_clip1Max:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:clip1Max: %d, %d\n", data & 0xfff, (data >> 16) & 0xfff);
+#endif
+ v->banshee.blt_regs[banshee2D_clip1Max] = data;
+ break;
+
+ case banshee2D_rop:
+#if LOG_BANSHEE_2D
+ logerror(" 2D:rop: %d, %d, %d\n", data & 0xff, (data >> 8) & 0xff, (data >> 16) & 0xff);
+#endif
+ v->banshee.blt_regs[banshee2D_rop] = data;
+ break;
+
+ default:
+ if (offset >= 0x20 && offset < 0x40)
+ {
+ blit_2d(v, data);
+ }
+ else if (offset >= 0x40 && offset < 0x80)
+ {
+ // TODO: colorPattern
+ }
+ break;
+ }
+
+
+ return 1;
+}
+
+
+
+
+WRITE32_MEMBER( voodoo_banshee_device::banshee_agp_w )
+{
+ voodoo_state *v = get_safe_token(this);
+ offset &= 0x1ff/4;
+
+ /* switch off the offset */
+ switch (offset)
+ {
+ case cmdBaseAddr0:
+ COMBINE_DATA(&v->banshee.agp[offset]);
+ v->fbi.cmdfifo[0].base = (data & 0xffffff) << 12;
+ v->fbi.cmdfifo[0].end = v->fbi.cmdfifo[0].base + (((v->banshee.agp[cmdBaseSize0] & 0xff) + 1) << 12);
+ break;
+
+ case cmdBaseSize0:
+ COMBINE_DATA(&v->banshee.agp[offset]);
+ v->fbi.cmdfifo[0].end = v->fbi.cmdfifo[0].base + (((v->banshee.agp[cmdBaseSize0] & 0xff) + 1) << 12);
+ v->fbi.cmdfifo[0].enable = (data >> 8) & 1;
+ v->fbi.cmdfifo[0].count_holes = (~data >> 10) & 1;
+ break;
+
+ case cmdBump0:
+ fatalerror("cmdBump0\n");
+
+ case cmdRdPtrL0:
+ v->fbi.cmdfifo[0].rdptr = data;
+ break;
+
+ case cmdAMin0:
+ v->fbi.cmdfifo[0].amin = data;
+ break;
+
+ case cmdAMax0:
+ v->fbi.cmdfifo[0].amax = data;
+ break;
+
+ case cmdFifoDepth0:
+ v->fbi.cmdfifo[0].depth = data;
+ break;
+
+ case cmdHoleCnt0:
+ v->fbi.cmdfifo[0].holes = data;
+ break;
+
+ case cmdBaseAddr1:
+ COMBINE_DATA(&v->banshee.agp[offset]);
+ v->fbi.cmdfifo[1].base = (data & 0xffffff) << 12;
+ v->fbi.cmdfifo[1].end = v->fbi.cmdfifo[1].base + (((v->banshee.agp[cmdBaseSize1] & 0xff) + 1) << 12);
+ break;
+
+ case cmdBaseSize1:
+ COMBINE_DATA(&v->banshee.agp[offset]);
+ v->fbi.cmdfifo[1].end = v->fbi.cmdfifo[1].base + (((v->banshee.agp[cmdBaseSize1] & 0xff) + 1) << 12);
+ v->fbi.cmdfifo[1].enable = (data >> 8) & 1;
+ v->fbi.cmdfifo[1].count_holes = (~data >> 10) & 1;
+ break;
+
+ case cmdBump1:
+ fatalerror("cmdBump1\n");
+
+ case cmdRdPtrL1:
+ v->fbi.cmdfifo[1].rdptr = data;
+ break;
+
+ case cmdAMin1:
+ v->fbi.cmdfifo[1].amin = data;
+ break;
+
+ case cmdAMax1:
+ v->fbi.cmdfifo[1].amax = data;
+ break;
+
+ case cmdFifoDepth1:
+ v->fbi.cmdfifo[1].depth = data;
+ break;
+
+ case cmdHoleCnt1:
+ v->fbi.cmdfifo[1].holes = data;
+ break;
+
+ default:
+ COMBINE_DATA(&v->banshee.agp[offset]);
+ break;
+ }
+
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_w(AGP:%s) = %08X & %08X\n", machine().describe_context(), banshee_agp_reg_name[offset], data, mem_mask);
+}
+
+
+WRITE32_MEMBER( voodoo_banshee_device::banshee_w )
+{
+ voodoo_state *v = get_safe_token(this);
+
+ /* if we have something pending, flush the FIFOs up to the current time */
+ if (v->pci.op_pending)
+ flush_fifos(v, machine().time());
+
+ if (offset < 0x80000/4)
+ banshee_io_w(space, offset, data, mem_mask);
+ else if (offset < 0x100000/4)
+ banshee_agp_w(space, offset, data, mem_mask);
+ else if (offset < 0x200000/4)
+ logerror("%s:banshee_w(2D:%X) = %08X & %08X\n", machine().describe_context(), (offset*4) & 0xfffff, data, mem_mask);
+ else if (offset < 0x600000/4)
+ register_w(v, offset & 0x1fffff/4, data);
+ else if (offset < 0x800000/4)
+ logerror("%s:banshee_w(TEX0:%X) = %08X & %08X\n", machine().describe_context(), (offset*4) & 0x1fffff, data, mem_mask);
+ else if (offset < 0xa00000/4)
+ logerror("%s:banshee_w(TEX1:%X) = %08X & %08X\n", machine().describe_context(), (offset*4) & 0x1fffff, data, mem_mask);
+ else if (offset < 0xc00000/4)
+ logerror("%s:banshee_r(FLASH Bios ROM:%X)\n", machine().describe_context(), (offset*4) & 0x3fffff);
+ else if (offset < 0x1000000/4)
+ logerror("%s:banshee_w(YUV:%X) = %08X & %08X\n", machine().describe_context(), (offset*4) & 0x3fffff, data, mem_mask);
+ else if (offset < 0x2000000/4)
+ {
+ lfb_w(v, offset & 0xffffff/4, data, mem_mask);
+ } else {
+ logerror("%s:banshee_w Address out of range %08X = %08X & %08X\n", machine().describe_context(), (offset*4), data, mem_mask);
+ }
+}
+
+
+WRITE32_MEMBER( voodoo_banshee_device::banshee_fb_w )
+{
+ voodoo_state *v = get_safe_token(this);
+ UINT32 addr = offset*4;
+
+ /* if we have something pending, flush the FIFOs up to the current time */
+ if (v->pci.op_pending)
+ flush_fifos(v, machine().time());
+
+ if (offset < v->fbi.lfb_base)
+ {
+ if (v->fbi.cmdfifo[0].enable && addr >= v->fbi.cmdfifo[0].base && addr < v->fbi.cmdfifo[0].end)
+ cmdfifo_w(v, &v->fbi.cmdfifo[0], (addr - v->fbi.cmdfifo[0].base) / 4, data);
+ else if (v->fbi.cmdfifo[1].enable && addr >= v->fbi.cmdfifo[1].base && addr < v->fbi.cmdfifo[1].end)
+ cmdfifo_w(v, &v->fbi.cmdfifo[1], (addr - v->fbi.cmdfifo[1].base) / 4, data);
+ else
+ {
+ if (offset*4 <= v->fbi.mask)
+ COMBINE_DATA(&((UINT32 *)v->fbi.ram)[offset]);
+ else
+ logerror("%s:banshee_fb_w Out of bounds (%X) = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
+#if LOG_LFB
+ logerror("%s:banshee_fb_w(%X) = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
+#endif
+ }
+ }
+ else
+ lfb_direct_w(v, offset - v->fbi.lfb_base, data, mem_mask);
+}
+
+
+WRITE8_MEMBER( voodoo_banshee_device::banshee_vga_w )
+{
+ voodoo_state *v = get_safe_token(this);
+ offset &= 0x1f;
+
+ /* switch off the offset */
+ switch (offset + 0x3c0)
+ {
+ /* attribute access */
+ case 0x3c0:
+ case 0x3c1:
+ if (v->banshee.attff == 0)
+ {
+ v->banshee.vga[0x3c1 & 0x1f] = data;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_vga_w(%X) = %02X\n", machine().describe_context(), 0x3c0+offset, data);
+ }
+ else
+ {
+ if (v->banshee.vga[0x3c1 & 0x1f] < ARRAY_LENGTH(v->banshee.att))
+ v->banshee.att[v->banshee.vga[0x3c1 & 0x1f]] = data;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_att_w(%X) = %02X\n", machine().describe_context(), v->banshee.vga[0x3c1 & 0x1f], data);
+ }
+ v->banshee.attff ^= 1;
+ break;
+
+ /* Sequencer access */
+ case 0x3c5:
+ if (v->banshee.vga[0x3c4 & 0x1f] < ARRAY_LENGTH(v->banshee.seq))
+ v->banshee.seq[v->banshee.vga[0x3c4 & 0x1f]] = data;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_seq_w(%X) = %02X\n", machine().describe_context(), v->banshee.vga[0x3c4 & 0x1f], data);
+ break;
+
+ /* Graphics controller access */
+ case 0x3cf:
+ if (v->banshee.vga[0x3ce & 0x1f] < ARRAY_LENGTH(v->banshee.gc))
+ v->banshee.gc[v->banshee.vga[0x3ce & 0x1f]] = data;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_gc_w(%X) = %02X\n", machine().describe_context(), v->banshee.vga[0x3ce & 0x1f], data);
+ break;
+
+ /* CRTC access */
+ case 0x3d5:
+ if (v->banshee.vga[0x3d4 & 0x1f] < ARRAY_LENGTH(v->banshee.crtc))
+ v->banshee.crtc[v->banshee.vga[0x3d4 & 0x1f]] = data;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_crtc_w(%X) = %02X\n", machine().describe_context(), v->banshee.vga[0x3d4 & 0x1f], data);
+ break;
+
+ default:
+ v->banshee.vga[offset] = data;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_vga_w(%X) = %02X\n", machine().describe_context(), 0x3c0+offset, data);
+ break;
+ }
+}
+
+
+WRITE32_MEMBER( voodoo_banshee_device::banshee_io_w )
+{
+ voodoo_state *v = get_safe_token(this);
+ UINT32 old;
+
+ offset &= 0xff/4;
+ old = v->banshee.io[offset];
+
+ /* switch off the offset */
+ switch (offset)
+ {
+ case io_vidProcCfg:
+ COMBINE_DATA(&v->banshee.io[offset]);
+ if ((v->banshee.io[offset] ^ old) & 0x2800)
+ v->fbi.clut_dirty = TRUE;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_io_w(%s) = %08X & %08X\n", machine().describe_context(), banshee_io_reg_name[offset], data, mem_mask);
+ break;
+
+ case io_dacData:
+ COMBINE_DATA(&v->banshee.io[offset]);
+ if (v->banshee.io[offset] != v->fbi.clut[v->banshee.io[io_dacAddr] & 0x1ff])
+ {
+ v->fbi.clut[v->banshee.io[io_dacAddr] & 0x1ff] = v->banshee.io[offset];
+ v->fbi.clut_dirty = TRUE;
+ }
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_dac_w(%X) = %08X & %08X\n", machine().describe_context(), v->banshee.io[io_dacAddr] & 0x1ff, data, mem_mask);
+ break;
+
+ case io_miscInit0:
+ COMBINE_DATA(&v->banshee.io[offset]);
+ v->fbi.yorigin = (data >> 18) & 0xfff;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_io_w(%s) = %08X & %08X\n", machine().describe_context(), banshee_io_reg_name[offset], data, mem_mask);
+ break;
+
+ case io_vidScreenSize:
+ if (data & 0xfff)
+ v->fbi.width = data & 0xfff;
+ if (data & 0xfff000)
+ v->fbi.height = (data >> 12) & 0xfff;
+ /* fall through */
+ case io_vidOverlayDudx:
+ case io_vidOverlayDvdy:
+ {
+ /* warning: this is a hack for now! We should really compute the screen size */
+ /* from the CRTC registers */
+ COMBINE_DATA(&v->banshee.io[offset]);
+
+ int width = v->fbi.width;
+ int height = v->fbi.height;
+
+ if (v->banshee.io[io_vidOverlayDudx] != 0)
+ width = (v->fbi.width * v->banshee.io[io_vidOverlayDudx]) / 1048576;
+ if (v->banshee.io[io_vidOverlayDvdy] != 0)
+ height = (v->fbi.height * v->banshee.io[io_vidOverlayDvdy]) / 1048576;
+
+ v->screen->set_visible_area(0, width - 1, 0, height - 1);
+
+ adjust_vblank_timer(v);
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_io_w(%s) = %08X & %08X\n", machine().describe_context(), banshee_io_reg_name[offset], data, mem_mask);
+ break;
+ }
+
+ case io_lfbMemoryConfig:
+ v->fbi.lfb_base = (data & 0x1fff) << (12-2);
+ v->fbi.lfb_stride = ((data >> 13) & 7) + 9;
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_io_w(%s) = %08X & %08X\n", machine().describe_context(), banshee_io_reg_name[offset], data, mem_mask);
+ break;
+
+ case io_vgab0: case io_vgab4: case io_vgab8: case io_vgabc:
+ case io_vgac0: case io_vgac4: case io_vgac8: case io_vgacc:
+ case io_vgad0: case io_vgad4: case io_vgad8: case io_vgadc:
+ if (ACCESSING_BITS_0_7)
+ banshee_vga_w(space, offset*4+0, data >> 0, mem_mask >> 0);
+ if (ACCESSING_BITS_8_15)
+ banshee_vga_w(space, offset*4+1, data >> 8, mem_mask >> 8);
+ if (ACCESSING_BITS_16_23)
+ banshee_vga_w(space, offset*4+2, data >> 16, mem_mask >> 16);
+ if (ACCESSING_BITS_24_31)
+ banshee_vga_w(space, offset*4+3, data >> 24, mem_mask >> 24);
+ break;
+
+ default:
+ COMBINE_DATA(&v->banshee.io[offset]);
+ if (LOG_REGISTERS)
+ logerror("%s:banshee_io_w(%s) = %08X & %08X\n", machine().describe_context(), banshee_io_reg_name[offset], data, mem_mask);
+ break;
+ }
+}
+
+
+
+/***************************************************************************
+ DEVICE INTERFACE
+***************************************************************************/
+
+/*-------------------------------------------------
+ device start callback
+-------------------------------------------------*/
+
+void voodoo_device::common_start_voodoo(UINT8 type)
+{
+ voodoo_state *v = get_safe_token(this);
+ const raster_info *info;
+ void *fbmem, *tmumem[2];
+ UINT32 tmumem0, tmumem1;
+ int val;
+
+ /* validate configuration */
+ assert(m_screen != NULL);
+ assert(m_cputag != NULL);
+ assert(m_fbmem > 0);
+
+ /* store a pointer back to the device */
+ v->device = this;
+ v->type = type;
+
+ /* copy config data */
+ v->freq = clock();
+ v->device->m_vblank.resolve();
+ v->device->m_stall.resolve();
+
+ /* create a multiprocessor work queue */
+ v->poly = poly_alloc(machine(), 64, sizeof(poly_extra_data), 0);
+ v->thread_stats = auto_alloc_array(machine(), stats_block, WORK_MAX_THREADS);
+
+ /* create a table of precomputed 1/n and log2(n) values */
+ /* n ranges from 1.0000 to 2.0000 */
+ for (val = 0; val <= (1 << RECIPLOG_LOOKUP_BITS); val++)
+ {
+ UINT32 value = (1 << RECIPLOG_LOOKUP_BITS) + val;
+ voodoo_reciplog[val*2 + 0] = (1 << (RECIPLOG_LOOKUP_PREC + RECIPLOG_LOOKUP_BITS)) / value;
+ voodoo_reciplog[val*2 + 1] = (UINT32)(LOGB2((double)value / (double)(1 << RECIPLOG_LOOKUP_BITS)) * (double)(1 << RECIPLOG_LOOKUP_PREC));
+ }
+
+ /* create dithering tables */
+ for (val = 0; val < 256*16*2; val++)
+ {
+ int g = (val >> 0) & 1;
+ int x = (val >> 1) & 3;
+ int color = (val >> 3) & 0xff;
+ int y = (val >> 11) & 3;
+
+ if (!g)
+ {
+ dither4_lookup[val] = DITHER_RB(color, dither_matrix_4x4[y * 4 + x]) >> 3;
+ dither2_lookup[val] = DITHER_RB(color, dither_matrix_2x2[y * 4 + x]) >> 3;
+ }
+ else
+ {
+ dither4_lookup[val] = DITHER_G(color, dither_matrix_4x4[y * 4 + x]) >> 2;
+ dither2_lookup[val] = DITHER_G(color, dither_matrix_2x2[y * 4 + x]) >> 2;
+ }
+ }
+
+ v->tmu_config = 0x11; // revision 1
+
+ /* configure type-specific values */
+ switch (v->type)
+ {
+ case TYPE_VOODOO_1:
+ v->regaccess = voodoo_register_access;
+ v->regnames = voodoo_reg_name;
+ v->alt_regmap = 0;
+ v->fbi.lfb_stride = 10;
+ break;
+
+ case TYPE_VOODOO_2:
+ v->regaccess = voodoo2_register_access;
+ v->regnames = voodoo_reg_name;
+ v->alt_regmap = 0;
+ v->fbi.lfb_stride = 10;
+ v->tmu_config |= 0x800;
+ break;
+
+ case TYPE_VOODOO_BANSHEE:
+ v->regaccess = banshee_register_access;
+ v->regnames = banshee_reg_name;
+ v->alt_regmap = 1;
+ v->fbi.lfb_stride = 11;
+ break;
+
+ case TYPE_VOODOO_3:
+ v->regaccess = banshee_register_access;
+ v->regnames = banshee_reg_name;
+ v->alt_regmap = 1;
+ v->fbi.lfb_stride = 11;
+ break;
+
+ default:
+ fatalerror("Unsupported voodoo card in voodoo_start!\n");
+ }
+
+ /* set the type, and initialize the chip mask */
+ device_iterator iter(machine().root_device());
+ v->index = 0;
+ for (device_t *scan = iter.first(); scan != NULL; scan = iter.next())
+ if (scan->type() == this->type())
+ {
+ if (scan == this)
+ break;
+ v->index++;
+ }
+ v->screen = downcast<screen_device *>(machine().device(m_screen));
+ assert_always(v->screen != NULL, "Unable to find screen attached to voodoo");
+ v->cpu = machine().device(m_cputag);
+ assert_always(v->cpu != NULL, "Unable to find CPU attached to voodoo");
+
+ if (m_tmumem1 != 0)
+ v->tmu_config |= 0xc0; // two TMUs
+
+ v->chipmask = 0x01;
+ v->attoseconds_per_cycle = ATTOSECONDS_PER_SECOND / v->freq;
+ v->trigger = 51324 + v->index;
+
+ /* build the rasterizer table */
+ for (info = predef_raster_table; info->callback; info++)
+ add_rasterizer(v, info);
+
+ /* set up the PCI FIFO */
+ v->pci.fifo.base = v->pci.fifo_mem;
+ v->pci.fifo.size = 64*2;
+ v->pci.fifo.in = v->pci.fifo.out = 0;
+ v->pci.stall_state = NOT_STALLED;
+ v->pci.continue_timer = machine().scheduler().timer_alloc(FUNC(stall_cpu_callback), v);
+
+ /* allocate memory */
+ tmumem0 = m_tmumem0;
+ tmumem1 = m_tmumem1;
+ if (v->type <= TYPE_VOODOO_2)
+ {
+ /* separate FB/TMU memory */
+ fbmem = auto_alloc_array(machine(), UINT8, m_fbmem << 20);
+ tmumem[0] = auto_alloc_array(machine(), UINT8, m_tmumem0 << 20);
+ tmumem[1] = (m_tmumem1 != 0) ? auto_alloc_array(machine(), UINT8, m_tmumem1 << 20) : NULL;
+ }
+ else
+ {
+ /* shared memory */
+ tmumem[0] = tmumem[1] = fbmem = auto_alloc_array(machine(), UINT8, m_fbmem << 20);
+ tmumem0 = m_fbmem;
+ if (v->type == TYPE_VOODOO_3)
+ tmumem1 = m_fbmem;
+ }
+
+ /* set up frame buffer */
+ init_fbi(v, &v->fbi, fbmem, m_fbmem << 20);
+
+ /* build shared TMU tables */
+ init_tmu_shared(&v->tmushare);
+
+ /* set up the TMUs */
+ init_tmu(v, &v->tmu[0], &v->reg[0x100], tmumem[0], tmumem0 << 20);
+ v->chipmask |= 0x02;
+ if (tmumem1 != 0)
+ {
+ init_tmu(v, &v->tmu[1], &v->reg[0x200], tmumem[1], tmumem1 << 20);
+ v->chipmask |= 0x04;
+ v->tmu_config |= 0x40;
+ }
+
+ /* initialize some registers */
+ memset(v->reg, 0, sizeof(v->reg));
+ v->pci.init_enable = 0;
+ v->reg[fbiInit0].u = (1 << 4) | (0x10 << 6);
+ v->reg[fbiInit1].u = (1 << 1) | (1 << 8) | (1 << 12) | (2 << 20);
+ v->reg[fbiInit2].u = (1 << 6) | (0x100 << 23);
+ v->reg[fbiInit3].u = (2 << 13) | (0xf << 17);
+ v->reg[fbiInit4].u = (1 << 0);
+
+ /* initialize banshee registers */
+ memset(v->banshee.io, 0, sizeof(v->banshee.io));
+ v->banshee.io[io_pciInit0] = 0x01800040;
+ v->banshee.io[io_sipMonitor] = 0x40000000;
+ v->banshee.io[io_lfbMemoryConfig] = 0x000a2200;
+ v->banshee.io[io_dramInit0] = 0x00579d29;
+ v->banshee.io[io_dramInit0] |= 0x08000000; // Konami Viper expects 16MBit SGRAMs
+ v->banshee.io[io_dramInit1] = 0x00f02200;
+ v->banshee.io[io_tmuGbeInit] = 0x00000bfb;
+
+ /* do a soft reset to reset everything else */
+ soft_reset(v);
+
+ /* register for save states */
+ init_save_state(this);
+}
+
+
+
+/***************************************************************************
+ COMMAND HANDLERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ fastfill - execute the 'fastfill'
+ command
+-------------------------------------------------*/
+
+static INT32 fastfill(voodoo_state *v)
+{
+ int sx = (v->reg[clipLeftRight].u >> 16) & 0x3ff;
+ int ex = (v->reg[clipLeftRight].u >> 0) & 0x3ff;
+ int sy = (v->reg[clipLowYHighY].u >> 16) & 0x3ff;
+ int ey = (v->reg[clipLowYHighY].u >> 0) & 0x3ff;
+ poly_extent extents[64];
+ UINT16 dithermatrix[16];
+ UINT16 *drawbuf = NULL;
+ UINT32 pixels = 0;
+ int extnum, x, y;
+
+ /* if we're not clearing either, take no time */
+ if (!FBZMODE_RGB_BUFFER_MASK(v->reg[fbzMode].u) && !FBZMODE_AUX_BUFFER_MASK(v->reg[fbzMode].u))
+ return 0;
+
+ /* are we clearing the RGB buffer? */
+ if (FBZMODE_RGB_BUFFER_MASK(v->reg[fbzMode].u))
+ {
+ /* determine the draw buffer */
+ int destbuf = (v->type >= TYPE_VOODOO_BANSHEE) ? 1 : FBZMODE_DRAW_BUFFER(v->reg[fbzMode].u);
+ switch (destbuf)
+ {
+ case 0: /* front buffer */
+ drawbuf = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ break;
+
+ case 1: /* back buffer */
+ drawbuf = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.backbuf]);
+ break;
+
+ default: /* reserved */
+ break;
+ }
+
+ /* determine the dither pattern */
+ for (y = 0; y < 4; y++)
+ {
+ DECLARE_DITHER_POINTERS_NO_DITHER_VAR;
+ COMPUTE_DITHER_POINTERS_NO_DITHER_VAR(v->reg[fbzMode].u, y);
+ for (x = 0; x < 4; x++)
+ {
+ int r = v->reg[color1].rgb.r;
+ int g = v->reg[color1].rgb.g;
+ int b = v->reg[color1].rgb.b;
+
+ APPLY_DITHER(v->reg[fbzMode].u, x, dither_lookup, r, g, b);
+ dithermatrix[y*4 + x] = (r << 11) | (g << 5) | b;
+ }
+ }
+ }
+
+ /* fill in a block of extents */
+ extents[0].startx = sx;
+ extents[0].stopx = ex;
+ for (extnum = 1; extnum < ARRAY_LENGTH(extents); extnum++)
+ extents[extnum] = extents[0];
+
+ /* iterate over blocks of extents */
+ for (y = sy; y < ey; y += ARRAY_LENGTH(extents))
+ {
+ poly_extra_data *extra = (poly_extra_data *)poly_get_extra_data(v->poly);
+ int count = MIN(ey - y, ARRAY_LENGTH(extents));
+
+ extra->state = v;
+ memcpy(extra->dither, dithermatrix, sizeof(extra->dither));
+
+ pixels += poly_render_triangle_custom(v->poly, drawbuf, global_cliprect, raster_fastfill, y, count, extents);
+ }
+
+ /* 2 pixels per clock */
+ return pixels / 2;
+}
+
+
+/*-------------------------------------------------
+ swapbuffer - execute the 'swapbuffer'
+ command
+-------------------------------------------------*/
+
+static INT32 swapbuffer(voodoo_state *v, UINT32 data)
+{
+ /* set the don't swap value for Voodoo 2 */
+ v->fbi.vblank_swap_pending = TRUE;
+ v->fbi.vblank_swap = (data >> 1) & 0xff;
+ v->fbi.vblank_dont_swap = (data >> 9) & 1;
+
+ /* if we're not syncing to the retrace, process the command immediately */
+ if (!(data & 1))
+ {
+ swap_buffers(v);
+ return 0;
+ }
+
+ /* determine how many cycles to wait; we deliberately overshoot here because */
+ /* the final count gets updated on the VBLANK */
+ return (v->fbi.vblank_swap + 1) * v->freq / 30;
+}
+
+
+/*-------------------------------------------------
+ triangle - execute the 'triangle'
+ command
+-------------------------------------------------*/
+
+static INT32 triangle(voodoo_state *v)
+{
+ int texcount = 0;
+ UINT16 *drawbuf;
+ int destbuf;
+ int pixels;
+
+ g_profiler.start(PROFILER_USER2);
+
+ /* determine the number of TMUs involved */
+ texcount = 0;
+ if (!FBIINIT3_DISABLE_TMUS(v->reg[fbiInit3].u) && FBZCP_TEXTURE_ENABLE(v->reg[fbzColorPath].u))
+ {
+ texcount = 1;
+ if (v->chipmask & 0x04)
+ texcount = 2;
+ }
+
+ /* perform subpixel adjustments */
+ if (FBZCP_CCA_SUBPIXEL_ADJUST(v->reg[fbzColorPath].u))
+ {
+ INT32 dx = 8 - (v->fbi.ax & 15);
+ INT32 dy = 8 - (v->fbi.ay & 15);
+
+ /* adjust iterated R,G,B,A and W/Z */
+ v->fbi.startr += (dy * v->fbi.drdy + dx * v->fbi.drdx) >> 4;
+ v->fbi.startg += (dy * v->fbi.dgdy + dx * v->fbi.dgdx) >> 4;
+ v->fbi.startb += (dy * v->fbi.dbdy + dx * v->fbi.dbdx) >> 4;
+ v->fbi.starta += (dy * v->fbi.dady + dx * v->fbi.dadx) >> 4;
+ v->fbi.startw += (dy * v->fbi.dwdy + dx * v->fbi.dwdx) >> 4;
+ v->fbi.startz += mul_32x32_shift(dy, v->fbi.dzdy, 4) + mul_32x32_shift(dx, v->fbi.dzdx, 4);
+
+ /* adjust iterated W/S/T for TMU 0 */
+ if (texcount >= 1)
+ {
+ v->tmu[0].startw += (dy * v->tmu[0].dwdy + dx * v->tmu[0].dwdx) >> 4;
+ v->tmu[0].starts += (dy * v->tmu[0].dsdy + dx * v->tmu[0].dsdx) >> 4;
+ v->tmu[0].startt += (dy * v->tmu[0].dtdy + dx * v->tmu[0].dtdx) >> 4;
+
+ /* adjust iterated W/S/T for TMU 1 */
+ if (texcount >= 2)
+ {
+ v->tmu[1].startw += (dy * v->tmu[1].dwdy + dx * v->tmu[1].dwdx) >> 4;
+ v->tmu[1].starts += (dy * v->tmu[1].dsdy + dx * v->tmu[1].dsdx) >> 4;
+ v->tmu[1].startt += (dy * v->tmu[1].dtdy + dx * v->tmu[1].dtdx) >> 4;
+ }
+ }
+ }
+
+ /* wait for any outstanding work to finish */
+// poly_wait(v->poly, "triangle");
+
+ /* determine the draw buffer */
+ destbuf = (v->type >= TYPE_VOODOO_BANSHEE) ? 1 : FBZMODE_DRAW_BUFFER(v->reg[fbzMode].u);
+ switch (destbuf)
+ {
+ case 0: /* front buffer */
+ drawbuf = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.frontbuf]);
+ v->fbi.video_changed = TRUE;
+ break;
+
+ case 1: /* back buffer */
+ drawbuf = (UINT16 *)(v->fbi.ram + v->fbi.rgboffs[v->fbi.backbuf]);
+ break;
+
+ default: /* reserved */
+ return TRIANGLE_SETUP_CLOCKS;
+ }
+
+ /* find a rasterizer that matches our current state */
+ pixels = triangle_create_work_item(v, drawbuf, texcount);
+
+ /* update stats */
+ v->reg[fbiTrianglesOut].u++;
+
+ /* update stats */
+ v->stats.total_triangles++;
+
+ g_profiler.stop();
+
+ /* 1 pixel per clock, plus some setup time */
+ if (LOG_REGISTERS) logerror("cycles = %d\n", TRIANGLE_SETUP_CLOCKS + pixels);
+ return TRIANGLE_SETUP_CLOCKS + pixels;
+}
+
+
+/*-------------------------------------------------
+ begin_triangle - execute the 'beginTri'
+ command
+-------------------------------------------------*/
+
+static INT32 begin_triangle(voodoo_state *v)
+{
+ setup_vertex *sv = &v->fbi.svert[2];
+
+ /* extract all the data from registers */
+ sv->x = v->reg[sVx].f;
+ sv->y = v->reg[sVy].f;
+ sv->wb = v->reg[sWb].f;
+ sv->w0 = v->reg[sWtmu0].f;
+ sv->s0 = v->reg[sS_W0].f;
+ sv->t0 = v->reg[sT_W0].f;
+ sv->w1 = v->reg[sWtmu1].f;
+ sv->s1 = v->reg[sS_Wtmu1].f;
+ sv->t1 = v->reg[sT_Wtmu1].f;
+ sv->a = v->reg[sAlpha].f;
+ sv->r = v->reg[sRed].f;
+ sv->g = v->reg[sGreen].f;
+ sv->b = v->reg[sBlue].f;
+
+ /* spread it across all three verts and reset the count */
+ v->fbi.svert[0] = v->fbi.svert[1] = v->fbi.svert[2];
+ v->fbi.sverts = 1;
+
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ draw_triangle - execute the 'DrawTri'
+ command
+-------------------------------------------------*/
+
+static INT32 draw_triangle(voodoo_state *v)
+{
+ setup_vertex *sv = &v->fbi.svert[2];
+ int cycles = 0;
+
+ /* for strip mode, shuffle vertex 1 down to 0 */
+ if (!(v->reg[sSetupMode].u & (1 << 16)))
+ v->fbi.svert[0] = v->fbi.svert[1];
+
+ /* copy 2 down to 1 regardless */
+ v->fbi.svert[1] = v->fbi.svert[2];
+
+ /* extract all the data from registers */
+ sv->x = v->reg[sVx].f;
+ sv->y = v->reg[sVy].f;
+ sv->wb = v->reg[sWb].f;
+ sv->w0 = v->reg[sWtmu0].f;
+ sv->s0 = v->reg[sS_W0].f;
+ sv->t0 = v->reg[sT_W0].f;
+ sv->w1 = v->reg[sWtmu1].f;
+ sv->s1 = v->reg[sS_Wtmu1].f;
+ sv->t1 = v->reg[sT_Wtmu1].f;
+ sv->a = v->reg[sAlpha].f;
+ sv->r = v->reg[sRed].f;
+ sv->g = v->reg[sGreen].f;
+ sv->b = v->reg[sBlue].f;
+
+ /* if we have enough verts, go ahead and draw */
+ if (++v->fbi.sverts >= 3)
+ cycles = setup_and_draw_triangle(v);
+
+ return cycles;
+}
+
+
+
+/***************************************************************************
+ TRIANGLE HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ setup_and_draw_triangle - process the setup
+ parameters and render the triangle
+-------------------------------------------------*/
+
+static INT32 setup_and_draw_triangle(voodoo_state *v)
+{
+ float dx1, dy1, dx2, dy2;
+ float divisor, tdiv;
+
+ /* grab the X/Ys at least */
+ v->fbi.ax = (INT16)(v->fbi.svert[0].x * 16.0f);
+ v->fbi.ay = (INT16)(v->fbi.svert[0].y * 16.0f);
+ v->fbi.bx = (INT16)(v->fbi.svert[1].x * 16.0f);
+ v->fbi.by = (INT16)(v->fbi.svert[1].y * 16.0f);
+ v->fbi.cx = (INT16)(v->fbi.svert[2].x * 16.0f);
+ v->fbi.cy = (INT16)(v->fbi.svert[2].y * 16.0f);
+
+ /* compute the divisor */
+ divisor = 1.0f / ((v->fbi.svert[0].x - v->fbi.svert[1].x) * (v->fbi.svert[0].y - v->fbi.svert[2].y) -
+ (v->fbi.svert[0].x - v->fbi.svert[2].x) * (v->fbi.svert[0].y - v->fbi.svert[1].y));
+
+ /* backface culling */
+ if (v->reg[sSetupMode].u & 0x20000)
+ {
+ int culling_sign = (v->reg[sSetupMode].u >> 18) & 1;
+ int divisor_sign = (divisor < 0);
+
+ /* if doing strips and ping pong is enabled, apply the ping pong */
+ if ((v->reg[sSetupMode].u & 0x90000) == 0x00000)
+ culling_sign ^= (v->fbi.sverts - 3) & 1;
+
+ /* if our sign matches the culling sign, we're done for */
+ if (divisor_sign == culling_sign)
+ return TRIANGLE_SETUP_CLOCKS;
+ }
+
+ /* compute the dx/dy values */
+ dx1 = v->fbi.svert[0].y - v->fbi.svert[2].y;
+ dx2 = v->fbi.svert[0].y - v->fbi.svert[1].y;
+ dy1 = v->fbi.svert[0].x - v->fbi.svert[1].x;
+ dy2 = v->fbi.svert[0].x - v->fbi.svert[2].x;
+
+ /* set up R,G,B */
+ tdiv = divisor * 4096.0f;
+ if (v->reg[sSetupMode].u & (1 << 0))
+ {
+ v->fbi.startr = (INT32)(v->fbi.svert[0].r * 4096.0f);
+ v->fbi.drdx = (INT32)(((v->fbi.svert[0].r - v->fbi.svert[1].r) * dx1 - (v->fbi.svert[0].r - v->fbi.svert[2].r) * dx2) * tdiv);
+ v->fbi.drdy = (INT32)(((v->fbi.svert[0].r - v->fbi.svert[2].r) * dy1 - (v->fbi.svert[0].r - v->fbi.svert[1].r) * dy2) * tdiv);
+ v->fbi.startg = (INT32)(v->fbi.svert[0].g * 4096.0f);
+ v->fbi.dgdx = (INT32)(((v->fbi.svert[0].g - v->fbi.svert[1].g) * dx1 - (v->fbi.svert[0].g - v->fbi.svert[2].g) * dx2) * tdiv);
+ v->fbi.dgdy = (INT32)(((v->fbi.svert[0].g - v->fbi.svert[2].g) * dy1 - (v->fbi.svert[0].g - v->fbi.svert[1].g) * dy2) * tdiv);
+ v->fbi.startb = (INT32)(v->fbi.svert[0].b * 4096.0f);
+ v->fbi.dbdx = (INT32)(((v->fbi.svert[0].b - v->fbi.svert[1].b) * dx1 - (v->fbi.svert[0].b - v->fbi.svert[2].b) * dx2) * tdiv);
+ v->fbi.dbdy = (INT32)(((v->fbi.svert[0].b - v->fbi.svert[2].b) * dy1 - (v->fbi.svert[0].b - v->fbi.svert[1].b) * dy2) * tdiv);
+ }
+
+ /* set up alpha */
+ if (v->reg[sSetupMode].u & (1 << 1))
+ {
+ v->fbi.starta = (INT32)(v->fbi.svert[0].a * 4096.0f);
+ v->fbi.dadx = (INT32)(((v->fbi.svert[0].a - v->fbi.svert[1].a) * dx1 - (v->fbi.svert[0].a - v->fbi.svert[2].a) * dx2) * tdiv);
+ v->fbi.dady = (INT32)(((v->fbi.svert[0].a - v->fbi.svert[2].a) * dy1 - (v->fbi.svert[0].a - v->fbi.svert[1].a) * dy2) * tdiv);
+ }
+
+ /* set up Z */
+ if (v->reg[sSetupMode].u & (1 << 2))
+ {
+ v->fbi.startz = (INT32)(v->fbi.svert[0].z * 4096.0f);
+ v->fbi.dzdx = (INT32)(((v->fbi.svert[0].z - v->fbi.svert[1].z) * dx1 - (v->fbi.svert[0].z - v->fbi.svert[2].z) * dx2) * tdiv);
+ v->fbi.dzdy = (INT32)(((v->fbi.svert[0].z - v->fbi.svert[2].z) * dy1 - (v->fbi.svert[0].z - v->fbi.svert[1].z) * dy2) * tdiv);
+ }
+
+ /* set up Wb */
+ tdiv = divisor * 65536.0f * 65536.0f;
+ if (v->reg[sSetupMode].u & (1 << 3))
+ {
+ v->fbi.startw = v->tmu[0].startw = v->tmu[1].startw = (INT64)(v->fbi.svert[0].wb * 65536.0f * 65536.0f);
+ v->fbi.dwdx = v->tmu[0].dwdx = v->tmu[1].dwdx = ((v->fbi.svert[0].wb - v->fbi.svert[1].wb) * dx1 - (v->fbi.svert[0].wb - v->fbi.svert[2].wb) * dx2) * tdiv;
+ v->fbi.dwdy = v->tmu[0].dwdy = v->tmu[1].dwdy = ((v->fbi.svert[0].wb - v->fbi.svert[2].wb) * dy1 - (v->fbi.svert[0].wb - v->fbi.svert[1].wb) * dy2) * tdiv;
+ }
+
+ /* set up W0 */
+ if (v->reg[sSetupMode].u & (1 << 4))
+ {
+ v->tmu[0].startw = v->tmu[1].startw = (INT64)(v->fbi.svert[0].w0 * 65536.0f * 65536.0f);
+ v->tmu[0].dwdx = v->tmu[1].dwdx = ((v->fbi.svert[0].w0 - v->fbi.svert[1].w0) * dx1 - (v->fbi.svert[0].w0 - v->fbi.svert[2].w0) * dx2) * tdiv;
+ v->tmu[0].dwdy = v->tmu[1].dwdy = ((v->fbi.svert[0].w0 - v->fbi.svert[2].w0) * dy1 - (v->fbi.svert[0].w0 - v->fbi.svert[1].w0) * dy2) * tdiv;
+ }
+
+ /* set up S0,T0 */
+ if (v->reg[sSetupMode].u & (1 << 5))
+ {
+ v->tmu[0].starts = v->tmu[1].starts = (INT64)(v->fbi.svert[0].s0 * 65536.0f * 65536.0f);
+ v->tmu[0].dsdx = v->tmu[1].dsdx = ((v->fbi.svert[0].s0 - v->fbi.svert[1].s0) * dx1 - (v->fbi.svert[0].s0 - v->fbi.svert[2].s0) * dx2) * tdiv;
+ v->tmu[0].dsdy = v->tmu[1].dsdy = ((v->fbi.svert[0].s0 - v->fbi.svert[2].s0) * dy1 - (v->fbi.svert[0].s0 - v->fbi.svert[1].s0) * dy2) * tdiv;
+ v->tmu[0].startt = v->tmu[1].startt = (INT64)(v->fbi.svert[0].t0 * 65536.0f * 65536.0f);
+ v->tmu[0].dtdx = v->tmu[1].dtdx = ((v->fbi.svert[0].t0 - v->fbi.svert[1].t0) * dx1 - (v->fbi.svert[0].t0 - v->fbi.svert[2].t0) * dx2) * tdiv;
+ v->tmu[0].dtdy = v->tmu[1].dtdy = ((v->fbi.svert[0].t0 - v->fbi.svert[2].t0) * dy1 - (v->fbi.svert[0].t0 - v->fbi.svert[1].t0) * dy2) * tdiv;
+ }
+
+ /* set up W1 */
+ if (v->reg[sSetupMode].u & (1 << 6))
+ {
+ v->tmu[1].startw = (INT64)(v->fbi.svert[0].w1 * 65536.0f * 65536.0f);
+ v->tmu[1].dwdx = ((v->fbi.svert[0].w1 - v->fbi.svert[1].w1) * dx1 - (v->fbi.svert[0].w1 - v->fbi.svert[2].w1) * dx2) * tdiv;
+ v->tmu[1].dwdy = ((v->fbi.svert[0].w1 - v->fbi.svert[2].w1) * dy1 - (v->fbi.svert[0].w1 - v->fbi.svert[1].w1) * dy2) * tdiv;
+ }
+
+ /* set up S1,T1 */
+ if (v->reg[sSetupMode].u & (1 << 7))
+ {
+ v->tmu[1].starts = (INT64)(v->fbi.svert[0].s1 * 65536.0f * 65536.0f);
+ v->tmu[1].dsdx = ((v->fbi.svert[0].s1 - v->fbi.svert[1].s1) * dx1 - (v->fbi.svert[0].s1 - v->fbi.svert[2].s1) * dx2) * tdiv;
+ v->tmu[1].dsdy = ((v->fbi.svert[0].s1 - v->fbi.svert[2].s1) * dy1 - (v->fbi.svert[0].s1 - v->fbi.svert[1].s1) * dy2) * tdiv;
+ v->tmu[1].startt = (INT64)(v->fbi.svert[0].t1 * 65536.0f * 65536.0f);
+ v->tmu[1].dtdx = ((v->fbi.svert[0].t1 - v->fbi.svert[1].t1) * dx1 - (v->fbi.svert[0].t1 - v->fbi.svert[2].t1) * dx2) * tdiv;
+ v->tmu[1].dtdy = ((v->fbi.svert[0].t1 - v->fbi.svert[2].t1) * dy1 - (v->fbi.svert[0].t1 - v->fbi.svert[1].t1) * dy2) * tdiv;
+ }
+
+ /* draw the triangle */
+ v->fbi.cheating_allowed = 1;
+ return triangle(v);
+}
+
+
+/*-------------------------------------------------
+ triangle_create_work_item - finish triangle
+ setup and create the work item
+-------------------------------------------------*/
+
+static INT32 triangle_create_work_item(voodoo_state *v, UINT16 *drawbuf, int texcount)
+{
+ poly_extra_data *extra = (poly_extra_data *)poly_get_extra_data(v->poly);
+ raster_info *info = find_rasterizer(v, texcount);
+ poly_vertex vert[3];
+
+ /* fill in the vertex data */
+ vert[0].x = (float)v->fbi.ax * (1.0f / 16.0f);
+ vert[0].y = (float)v->fbi.ay * (1.0f / 16.0f);
+ vert[1].x = (float)v->fbi.bx * (1.0f / 16.0f);
+ vert[1].y = (float)v->fbi.by * (1.0f / 16.0f);
+ vert[2].x = (float)v->fbi.cx * (1.0f / 16.0f);
+ vert[2].y = (float)v->fbi.cy * (1.0f / 16.0f);
+
+ /* fill in the extra data */
+ extra->state = v;
+ extra->info = info;
+
+ /* fill in triangle parameters */
+ extra->ax = v->fbi.ax;
+ extra->ay = v->fbi.ay;
+ extra->startr = v->fbi.startr;
+ extra->startg = v->fbi.startg;
+ extra->startb = v->fbi.startb;
+ extra->starta = v->fbi.starta;
+ extra->startz = v->fbi.startz;
+ extra->startw = v->fbi.startw;
+ extra->drdx = v->fbi.drdx;
+ extra->dgdx = v->fbi.dgdx;
+ extra->dbdx = v->fbi.dbdx;
+ extra->dadx = v->fbi.dadx;
+ extra->dzdx = v->fbi.dzdx;
+ extra->dwdx = v->fbi.dwdx;
+ extra->drdy = v->fbi.drdy;
+ extra->dgdy = v->fbi.dgdy;
+ extra->dbdy = v->fbi.dbdy;
+ extra->dady = v->fbi.dady;
+ extra->dzdy = v->fbi.dzdy;
+ extra->dwdy = v->fbi.dwdy;
+
+ /* fill in texture 0 parameters */
+ if (texcount > 0)
+ {
+ extra->starts0 = v->tmu[0].starts;
+ extra->startt0 = v->tmu[0].startt;
+ extra->startw0 = v->tmu[0].startw;
+ extra->ds0dx = v->tmu[0].dsdx;
+ extra->dt0dx = v->tmu[0].dtdx;
+ extra->dw0dx = v->tmu[0].dwdx;
+ extra->ds0dy = v->tmu[0].dsdy;
+ extra->dt0dy = v->tmu[0].dtdy;
+ extra->dw0dy = v->tmu[0].dwdy;
+ extra->lodbase0 = prepare_tmu(&v->tmu[0]);
+ v->stats.texture_mode[TEXMODE_FORMAT(v->tmu[0].reg[textureMode].u)]++;
+
+ /* fill in texture 1 parameters */
+ if (texcount > 1)
+ {
+ extra->starts1 = v->tmu[1].starts;
+ extra->startt1 = v->tmu[1].startt;
+ extra->startw1 = v->tmu[1].startw;
+ extra->ds1dx = v->tmu[1].dsdx;
+ extra->dt1dx = v->tmu[1].dtdx;
+ extra->dw1dx = v->tmu[1].dwdx;
+ extra->ds1dy = v->tmu[1].dsdy;
+ extra->dt1dy = v->tmu[1].dtdy;
+ extra->dw1dy = v->tmu[1].dwdy;
+ extra->lodbase1 = prepare_tmu(&v->tmu[1]);
+ v->stats.texture_mode[TEXMODE_FORMAT(v->tmu[1].reg[textureMode].u)]++;
+ }
+ }
+
+ /* farm the rasterization out to other threads */
+ info->polys++;
+ return poly_render_triangle(v->poly, drawbuf, global_cliprect, info->callback, 0, &vert[0], &vert[1], &vert[2]);
+}
+
+
+
+/***************************************************************************
+ RASTERIZER MANAGEMENT
+***************************************************************************/
+
+/*-------------------------------------------------
+ add_rasterizer - add a rasterizer to our
+ hash table
+-------------------------------------------------*/
+
+static raster_info *add_rasterizer(voodoo_state *v, const raster_info *cinfo)
+{
+ raster_info *info = &v->rasterizer[v->next_rasterizer++];
+ int hash = compute_raster_hash(cinfo);
+
+ assert_always(v->next_rasterizer <= MAX_RASTERIZERS, "Out of space for new rasterizers!");
+
+ /* make a copy of the info */
+ *info = *cinfo;
+
+ /* fill in the data */
+ info->hits = 0;
+ info->polys = 0;
+ info->hash = hash;
+
+ /* hook us into the hash table */
+ info->next = v->raster_hash[hash];
+ v->raster_hash[hash] = info;
+
+ if (LOG_RASTERIZERS)
+ printf("Adding rasterizer @ %p : cp=%08X am=%08X %08X fbzM=%08X tm0=%08X tm1=%08X (hash=%d)\n",
+ (void *)info->callback,
+ info->eff_color_path, info->eff_alpha_mode, info->eff_fog_mode, info->eff_fbz_mode,
+ info->eff_tex_mode_0, info->eff_tex_mode_1, hash);
+
+ return info;
+}
+
+
+/*-------------------------------------------------
+ find_rasterizer - find a rasterizer that
+ matches our current parameters and return
+ it, creating a new one if necessary
+-------------------------------------------------*/
+
+static raster_info *find_rasterizer(voodoo_state *v, int texcount)
+{
+ raster_info *info, *prev = NULL;
+ raster_info curinfo;
+ int hash;
+
+ /* build an info struct with all the parameters */
+ curinfo.eff_color_path = normalize_color_path(v->reg[fbzColorPath].u);
+ curinfo.eff_alpha_mode = normalize_alpha_mode(v->reg[alphaMode].u);
+ curinfo.eff_fog_mode = normalize_fog_mode(v->reg[fogMode].u);
+ curinfo.eff_fbz_mode = normalize_fbz_mode(v->reg[fbzMode].u);
+ curinfo.eff_tex_mode_0 = (texcount >= 1) ? normalize_tex_mode(v->tmu[0].reg[textureMode].u) : 0xffffffff;
+ curinfo.eff_tex_mode_1 = (texcount >= 2) ? normalize_tex_mode(v->tmu[1].reg[textureMode].u) : 0xffffffff;
+
+ /* compute the hash */
+ hash = compute_raster_hash(&curinfo);
+
+ /* find the appropriate hash entry */
+ for (info = v->raster_hash[hash]; info; prev = info, info = info->next)
+ if (info->eff_color_path == curinfo.eff_color_path &&
+ info->eff_alpha_mode == curinfo.eff_alpha_mode &&
+ info->eff_fog_mode == curinfo.eff_fog_mode &&
+ info->eff_fbz_mode == curinfo.eff_fbz_mode &&
+ info->eff_tex_mode_0 == curinfo.eff_tex_mode_0 &&
+ info->eff_tex_mode_1 == curinfo.eff_tex_mode_1)
+ {
+ /* got it, move us to the head of the list */
+ if (prev)
+ {
+ prev->next = info->next;
+ info->next = v->raster_hash[hash];
+ v->raster_hash[hash] = info;
+ }
+
+ /* return the result */
+ return info;
+ }
+
+ /* generate a new one using the generic entry */
+ curinfo.callback = (texcount == 0) ? raster_generic_0tmu : (texcount == 1) ? raster_generic_1tmu : raster_generic_2tmu;
+ curinfo.is_generic = TRUE;
+ curinfo.display = 0;
+ curinfo.polys = 0;
+ curinfo.hits = 0;
+ curinfo.next = 0;
+ curinfo.hash = hash;
+
+ return add_rasterizer(v, &curinfo);
+}
+
+
+/*-------------------------------------------------
+ dump_rasterizer_stats - dump statistics on
+ the current rasterizer usage patterns
+-------------------------------------------------*/
+
+static void dump_rasterizer_stats(voodoo_state *v)
+{
+ static UINT8 display_index;
+ raster_info *cur, *best;
+ int hash;
+
+ printf("----\n");
+ display_index++;
+
+ /* loop until we've displayed everything */
+ while (1)
+ {
+ best = NULL;
+
+ /* find the highest entry */
+ for (hash = 0; hash < RASTER_HASH_SIZE; hash++)
+ for (cur = v->raster_hash[hash]; cur; cur = cur->next)
+ if (cur->display != display_index && (best == NULL || cur->hits > best->hits))
+ best = cur;
+
+ /* if we're done, we're done */
+ if (best == NULL || best->hits == 0)
+ break;
+
+ /* print it */
+ printf("RASTERIZER_ENTRY( 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X ) /* %c %2d %8d %10d */\n",
+ best->eff_color_path,
+ best->eff_alpha_mode,
+ best->eff_fog_mode,
+ best->eff_fbz_mode,
+ best->eff_tex_mode_0,
+ best->eff_tex_mode_1,
+ best->is_generic ? '*' : ' ',
+ best->hash,
+ best->polys,
+ best->hits);
+
+ /* reset */
+ best->display = display_index;
+ }
+}
+
+voodoo_device::voodoo_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_fbmem(0),
+ m_tmumem0(0),
+ m_tmumem1(0),
+ m_screen(NULL),
+ m_cputag(NULL),
+ m_vblank(*this),
+ m_stall(*this)
+{
+ m_token = global_alloc_clear(voodoo_state);
+}
+
+voodoo_device::~voodoo_device()
+{
+ global_free(m_token);
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void voodoo_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void voodoo_device::device_reset()
+{
+ voodoo_state *v = get_safe_token(this);
+ soft_reset(v);
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void voodoo_device::device_stop()
+{
+ voodoo_state *v = get_safe_token(this);
+
+ /* release the work queue, ensuring all work is finished */
+ if (v->poly != NULL)
+ poly_free(v->poly);
+}
+
+
+const device_type VOODOO_1 = &device_creator<voodoo_1_device>;
+
+voodoo_1_device::voodoo_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : voodoo_device(mconfig, VOODOO_1, "3dfx Voodoo Graphics", tag, owner, clock, "voodoo_1", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void voodoo_1_device::device_start()
+{
+ common_start_voodoo(TYPE_VOODOO_1);
+}
+
+
+const device_type VOODOO_2 = &device_creator<voodoo_2_device>;
+
+voodoo_2_device::voodoo_2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : voodoo_device(mconfig, VOODOO_2, "3dfx Voodoo 2", tag, owner, clock, "voodoo_2", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void voodoo_2_device::device_start()
+{
+ common_start_voodoo(TYPE_VOODOO_2);
+}
+
+
+const device_type VOODOO_BANSHEE = &device_creator<voodoo_banshee_device>;
+
+voodoo_banshee_device::voodoo_banshee_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : voodoo_device(mconfig, VOODOO_BANSHEE, "3dfx Voodoo Banshee", tag, owner, clock, "voodoo_banshee", __FILE__)
+{
+}
+
+voodoo_banshee_device::voodoo_banshee_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : voodoo_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void voodoo_banshee_device::device_start()
+{
+ common_start_voodoo(TYPE_VOODOO_BANSHEE);
+}
+
+
+const device_type VOODOO_3 = &device_creator<voodoo_3_device>;
+
+voodoo_3_device::voodoo_3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : voodoo_banshee_device(mconfig, VOODOO_3, "3dfx Voodoo 3", tag, owner, clock, "voodoo_3", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void voodoo_3_device::device_start()
+{
+ common_start_voodoo(TYPE_VOODOO_3);
+}
+
+
+
+/***************************************************************************
+ GENERIC RASTERIZERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ raster_fastfill - per-scanline
+ implementation of the 'fastfill' command
+-------------------------------------------------*/
+
+static void raster_fastfill(void *destbase, INT32 y, const poly_extent *extent, const void *extradata, int threadid)
+{
+ const poly_extra_data *extra = (const poly_extra_data *)extradata;
+ voodoo_state *v = extra->state;
+ stats_block *stats = &v->thread_stats[threadid];
+ INT32 startx = extent->startx;
+ INT32 stopx = extent->stopx;
+ int scry, x;
+
+ /* determine the screen Y */
+ scry = y;
+ if (FBZMODE_Y_ORIGIN(v->reg[fbzMode].u))
+ scry = (v->fbi.yorigin - y) & 0x3ff;
+
+ /* fill this RGB row */
+ if (FBZMODE_RGB_BUFFER_MASK(v->reg[fbzMode].u))
+ {
+ const UINT16 *ditherow = &extra->dither[(y & 3) * 4];
+ UINT64 expanded = *(UINT64 *)ditherow;
+ UINT16 *dest = (UINT16 *)destbase + scry * v->fbi.rowpixels;
+
+ for (x = startx; x < stopx && (x & 3) != 0; x++)
+ dest[x] = ditherow[x & 3];
+ for ( ; x < (stopx & ~3); x += 4)
+ *(UINT64 *)&dest[x] = expanded;
+ for ( ; x < stopx; x++)
+ dest[x] = ditherow[x & 3];
+ stats->pixels_out += stopx - startx;
+ }
+
+ /* fill this dest buffer row */
+ if (FBZMODE_AUX_BUFFER_MASK(v->reg[fbzMode].u) && v->fbi.auxoffs != ~0)
+ {
+ UINT16 color = v->reg[zaColor].u;
+ UINT64 expanded = ((UINT64)color << 48) | ((UINT64)color << 32) | (color << 16) | color;
+ UINT16 *dest = (UINT16 *)(v->fbi.ram + v->fbi.auxoffs) + scry * v->fbi.rowpixels;
+
+ for (x = startx; x < stopx && (x & 3) != 0; x++)
+ dest[x] = color;
+ for ( ; x < (stopx & ~3); x += 4)
+ *(UINT64 *)&dest[x] = expanded;
+ for ( ; x < stopx; x++)
+ dest[x] = color;
+ }
+}
+
+
+/*-------------------------------------------------
+ generic_0tmu - generic rasterizer for 0 TMUs
+-------------------------------------------------*/
+
+RASTERIZER(generic_0tmu, 0, v->reg[fbzColorPath].u, v->reg[fbzMode].u, v->reg[alphaMode].u,
+ v->reg[fogMode].u, 0, 0)
+
+
+/*-------------------------------------------------
+ generic_1tmu - generic rasterizer for 1 TMU
+-------------------------------------------------*/
+
+RASTERIZER(generic_1tmu, 1, v->reg[fbzColorPath].u, v->reg[fbzMode].u, v->reg[alphaMode].u,
+ v->reg[fogMode].u, v->tmu[0].reg[textureMode].u, 0)
+
+
+/*-------------------------------------------------
+ generic_2tmu - generic rasterizer for 2 TMUs
+-------------------------------------------------*/
+
+RASTERIZER(generic_2tmu, 2, v->reg[fbzColorPath].u, v->reg[fbzMode].u, v->reg[alphaMode].u,
+ v->reg[fogMode].u, v->tmu[0].reg[textureMode].u, v->tmu[1].reg[textureMode].u)
+
+
+#else
+
+
+
+/***************************************************************************
+ GAME-SPECIFIC RASTERIZERS
+***************************************************************************/
+
+/* blitz ------> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00000035, 0x00000000, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* 284269 914846168 */
+RASTERIZER_ENTRY( 0x00002C35, 0x00515110, 0x00000000, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* 485421 440309121 */
+RASTERIZER_ENTRY( 0x00582C35, 0x00515110, 0x00000000, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* 31606 230753709 */
+RASTERIZER_ENTRY( 0x00582C35, 0x00515110, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* 76742 211701679 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000000, 0x000B073B, 0x0C261ACF, 0xFFFFFFFF ) /* 6188 152109056 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000000, 0x000B07F9, 0x0C261ACF, 0xFFFFFFFF ) /* 1100 108134400 */
+RASTERIZER_ENTRY( 0x00002C35, 0x00515119, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* 6229525 106197740 */
+RASTERIZER_ENTRY( 0x00002C35, 0x00515119, 0x00000000, 0x000B0799, 0x0C261A0F, 0xFFFFFFFF ) /* 905641 75886220 */
+RASTERIZER_ENTRY( 0x00002C35, 0x00515119, 0x00000000, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* 205236 53317253 */
+RASTERIZER_ENTRY( 0x01422439, 0x00000000, 0x00000000, 0x000B073B, 0x0C2610C9, 0xFFFFFFFF ) /* 817356 48881349 */
+RASTERIZER_ENTRY( 0x00000035, 0x00000000, 0x00000000, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* 37979 41687251 */
+RASTERIZER_ENTRY( 0x00002C35, 0x00515110, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* 26014 41183295 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000000, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* 2512 37911104 */
+RASTERIZER_ENTRY( 0x00006136, 0x00515119, 0x00000000, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* 28834 15527654 */
+RASTERIZER_ENTRY( 0x00582435, 0x00515110, 0x00000000, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* 9878 4979429 */
+RASTERIZER_ENTRY( 0x00002C35, 0x00515119, 0x00000000, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* 199952 4622064 */
+RASTERIZER_ENTRY( 0x00582C35, 0x00515110, 0x00000000, 0x000B0739, 0x0C261AC9, 0xFFFFFFFF ) /* 8672 3676949 */
+RASTERIZER_ENTRY( 0x00582C35, 0x00515010, 0x00000000, 0x000B0739, 0x0C2610CF, 0xFFFFFFFF ) /* 616 2743972 */
+RASTERIZER_ENTRY( 0x01422C39, 0x00045110, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* 81380 2494832 */
+//RASTERIZER_ENTRY( 0x00582435, 0x00515110, 0x00000000, 0x000B0739, 0x0C261AC9, 0xFFFFFFFF ) /* 7670 2235587 */
+//RASTERIZER_ENTRY( 0x00592136, 0x00515110, 0x00000000, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* 210 1639140 */
+//RASTERIZER_ENTRY( 0x00582C35, 0x00515110, 0x00000000, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* 108 1154736 */
+//RASTERIZER_ENTRY( 0x00002C35, 0x00515110, 0x00000000, 0x000B07F9, 0x0C26180F, 0xFFFFFFFF ) /* 2152 1150842 */
+//RASTERIZER_ENTRY( 0x00592136, 0x00515110, 0x00000000, 0x000B073B, 0x0C261ACF, 0xFFFFFFFF ) /* 152 880560 */
+//RASTERIZER_ENTRY( 0x00008035, 0x00515119, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* 90848 805730 */
+//RASTERIZER_ENTRY( 0x00002C35, 0x00515119, 0x00000000, 0x000B07F9, 0x0C261AC9, 0xFFFFFFFF ) /* 2024 571406 */
+//RASTERIZER_ENTRY( 0x00012136, 0x00515110, 0x00000000, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* 1792 494592 */
+//RASTERIZER_ENTRY( 0x00000002, 0x00000000, 0x00000000, 0x00000300, 0xFFFFFFFF, 0xFFFFFFFF ) /* 256 161280 */
+
+/* blitz99 ----> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00000035, 0x00000009, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 6297478 149465839 */
+RASTERIZER_ENTRY( 0x00000035, 0x00000009, 0x00000000, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* * 210693 6285480 */
+RASTERIZER_ENTRY( 0x01422C39, 0x00045110, 0x00000000, 0x000B073B, 0x0C2610C9, 0xFFFFFFFF ) /* * 20180 2718710 */
+RASTERIZER_ENTRY( 0x00582C35, 0x00515110, 0x00000000, 0x000B073B, 0x0C261ACF, 0xFFFFFFFF ) /* * 360 2425416 */
+RASTERIZER_ENTRY( 0x00002C35, 0x00000009, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 67059 1480978 */
+RASTERIZER_ENTRY( 0x00008035, 0x00000009, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 24811 400666 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000000, 0x000B073B, 0x0C2610C9, 0xFFFFFFFF ) /* * 10304 324468 */
+RASTERIZER_ENTRY( 0x00002C35, 0x00515110, 0x00000000, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* * 1024 112665 */
+
+/* blitz2k ----> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000000, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* * 3880 95344128 */
+RASTERIZER_ENTRY( 0x00582C35, 0x00514110, 0x00000000, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* * 148 1785480 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000000, 0x000B073B, 0x0C2610CF, 0xFFFFFFFF ) /* * 9976 314244 */
+
+/* carnevil ---> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00002435, 0x00045119, 0x00000000, 0x00030279, 0x0C261A0F, 0xFFFFFFFF ) /* * 492 84128082 */
+RASTERIZER_ENTRY( 0x00002425, 0x00045119, 0x00000000, 0x00030679, 0x0C261A0F, 0xFFFFFFFF ) /* * 1988398 36166780 */
+RASTERIZER_ENTRY( 0x00486116, 0x00045119, 0x00000000, 0x00030279, 0x0C26180F, 0xFFFFFFFF ) /* * 34424 28788847 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x00030679, 0x0C261A0F, 0xFFFFFFFF ) /* * 514 26316800 */
+RASTERIZER_ENTRY( 0x00480015, 0x00045119, 0x00000000, 0x000306F9, 0x0C261AC9, 0xFFFFFFFF ) /* * 7346 18805760 */
+RASTERIZER_ENTRY( 0x00002435, 0x00045119, 0x00000000, 0x000302F9, 0x0C26180F, 0xFFFFFFFF ) /* * 130764 18678972 */
+RASTERIZER_ENTRY( 0x00482415, 0x00045119, 0x00000000, 0x000306F9, 0x0C2618C9, 0xFFFFFFFF ) /* * 7244 12179040 */
+RASTERIZER_ENTRY( 0x00482415, 0x00045119, 0x00000000, 0x000306F9, 0x0C26180F, 0xFFFFFFFF ) /* * 84520 12059721 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000306F9, 0x0C261AC9, 0xFFFFFFFF ) /* * 21926 11226112 */
+RASTERIZER_ENTRY( 0x00482415, 0x00045119, 0x00000000, 0x00030679, 0x0C2618C9, 0xFFFFFFFF ) /* * 92115 8926536 */
+RASTERIZER_ENTRY( 0x00482415, 0x00045119, 0x00000000, 0x00030279, 0x0C261A0F, 0xFFFFFFFF ) /* * 1730 7629334 */
+RASTERIZER_ENTRY( 0x00002435, 0x00045119, 0x00000000, 0x000B0779, 0x0C26180F, 0xFFFFFFFF ) /* * 37408 5545956 */
+RASTERIZER_ENTRY( 0x00002435, 0x00045119, 0x00000000, 0x00030679, 0x0C26180F, 0xFFFFFFFF ) /* * 26528 4225026 */
+RASTERIZER_ENTRY( 0x00002435, 0x00045119, 0x00000000, 0x000306F9, 0x0C26180F, 0xFFFFFFFF ) /* * 35764 3230884 */
+RASTERIZER_ENTRY( 0x01422409, 0x00045119, 0x00000000, 0x00030679, 0x0C261A0F, 0xFFFFFFFF ) /* * 96020 1226438 */
+RASTERIZER_ENTRY( 0x00482415, 0x00045119, 0x00000000, 0x00030279, 0x0C2618C9, 0xFFFFFFFF ) /* * 1020 574649 */
+RASTERIZER_ENTRY( 0x00482415, 0x00045119, 0x00000000, 0x00030679, 0x0C261A0F, 0xFFFFFFFF ) /* * 360 370008 */
+RASTERIZER_ENTRY( 0x00480015, 0x00045119, 0x00000000, 0x000306F9, 0x0C261A0F, 0xFFFFFFFF ) /* * 576 334404 */
+
+/* calspeed ---> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000B07F9, 0x0C26100F, 0xFFFFFFFF ) /* * 99120 1731923836 */
+RASTERIZER_ENTRY( 0x01022819, 0x00000009, 0x00000001, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 9955804 1526119944 */
+RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000B0739, 0x0C26180F, 0xFFFFFFFF ) /* * 1898207 1124776864 */
+RASTERIZER_ENTRY( 0x01022819, 0x00000009, 0x00000001, 0x000B073B, 0x0C26100F, 0xFFFFFFFF ) /* * 3487467 1101663125 */
+RASTERIZER_ENTRY( 0x01022C19, 0x00000009, 0x00000001, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 1079277 609256033 */
+RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000A0723, 0x0C261ACF, 0xFFFFFFFF ) /* * 11880 583925760 */
+RASTERIZER_ENTRY( 0x00602819, 0x00045119, 0x00000001, 0x000B07F9, 0x0C26180F, 0xFFFFFFFF ) /* * 63644 582469888 */
+RASTERIZER_ENTRY( 0x01022819, 0x00000009, 0x00000001, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* * 22688 556797972 */
+RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000B07F9, 0x0C26180F, 0xFFFFFFFF ) /* * 1360254 417068457 */
+RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 3427489 405421272 */
+RASTERIZER_ENTRY( 0x00002C15, 0x00045119, 0x00000001, 0x000B0739, 0x0C26180F, 0xFFFFFFFF ) /* * 286809 238944049 */
+RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000A0321, 0x0C26180F, 0xFFFFFFFF ) /* * 28160 231084818 */
+RASTERIZER_ENTRY( 0x01022819, 0x00000009, 0x00000001, 0x000B07FB, 0x0C26100F, 0xFFFFFFFF ) /* * 183564 201014424 */
+RASTERIZER_ENTRY( 0x00480015, 0x00045119, 0x00000001, 0x000B0339, 0x0C26100F, 0xFFFFFFFF ) /* * 15275 168207109 */
+RASTERIZER_ENTRY( 0x01022819, 0x00000009, 0x00000001, 0x000B07F9, 0x0C26100F, 0xFFFFFFFF ) /* * 2856 134400000 */
+RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000B0339, 0x0C26180F, 0xFFFFFFFF ) /* * 98551 110417974 */
+RASTERIZER_ENTRY( 0x01022819, 0x00000009, 0x00000001, 0x000B07F9, 0x0C2610CF, 0xFFFFFFFF ) /* * 47040 107360728 */
+RASTERIZER_ENTRY( 0x00480015, 0x00045119, 0x00000001, 0x000B0339, 0x0C26180F, 0xFFFFFFFF ) /* * 13128 86876789 */
+RASTERIZER_ENTRY( 0x01022C19, 0x00000009, 0x00000001, 0x000B073B, 0x0C26100F, 0xFFFFFFFF ) /* * 257515 76329054 */
+RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* * 3934 64958208 */
+//RASTERIZER_ENTRY( 0x00002C15, 0x00045119, 0x00000001, 0x000B07F9, 0x0C26180F, 0xFFFFFFFF ) /* * 77400 63786236 */
+//RASTERIZER_ENTRY( 0x01022C19, 0x00000009, 0x00000001, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* * 12500 63151200 */
+//RASTERIZER_ENTRY( 0x0102001A, 0x00045119, 0x00000001, 0x000A0321, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 8764 57629312 */
+//RASTERIZER_ENTRY( 0x00002C15, 0x00045119, 0x00000001, 0x000A0321, 0x0C26180F, 0xFFFFFFFF ) /* * 3257 32708448 */
+//RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000A07E3, 0x0C2610CF, 0xFFFFFFFF ) /* * 28364 31195605 */
+//RASTERIZER_ENTRY( 0x00002C15, 0x00045119, 0x00000001, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 409001 30699647 */
+//RASTERIZER_ENTRY( 0x00482C35, 0x00045119, 0x00000001, 0x000A0321, 0x0C26100F, 0xFFFFFFFF ) /* * 17669 11214172 */
+//RASTERIZER_ENTRY( 0x00002C15, 0x00045119, 0x00000001, 0x000B0339, 0x0C26180F, 0xFFFFFFFF ) /* * 5844 6064373 */
+//RASTERIZER_ENTRY( 0x00002C15, 0x00045119, 0x00000001, 0x000B07FB, 0x0C26100F, 0xFFFFFFFF ) /* * 626 4651080 */
+//RASTERIZER_ENTRY( 0x00482C35, 0x00045119, 0x00000001, 0x000A0321, 0x0C26180F, 0xFFFFFFFF ) /* * 5887 2945500 */
+//RASTERIZER_ENTRY( 0x00480015, 0x00045119, 0x00000001, 0x000B0339, 0x0C261A0F, 0xFFFFFFFF ) /* * 1090 2945093 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000001, 0x000B07F9, 0x0C26180F, 0xFFFFFFFF ) /* * 228 1723908 */
+//RASTERIZER_ENTRY( 0x00002C15, 0x00045119, 0x00000001, 0x000A0321, 0x0C261A0F, 0xFFFFFFFF ) /* * 112 1433600 */
+//RASTERIZER_ENTRY( 0x00002815, 0x00045119, 0x00000001, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 3091 1165805 */
+//RASTERIZER_ENTRY( 0x01022C19, 0x00000009, 0x00000001, 0x000B07FB, 0x0C26100F, 0xFFFFFFFF ) /* * 620 791202 */
+
+/* hyprdriv ---> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* * 60860 498565120 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B07F9, 0x0C261A0F, 0xFFFFFFFF ) /* * 28688 235012096 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B07F9, 0x0C261ACF, 0xFFFFFFFF ) /* * 11844 156499968 */
+RASTERIZER_ENTRY( 0x00580035, 0x00045119, 0x00000001, 0x00030279, 0x0C261A0F, 0xFFFFFFFF ) /* * 175990 146518715 */
+RASTERIZER_ENTRY( 0x00582C35, 0x00515110, 0x00000001, 0x000B0739, 0x0C261ACF, 0xFFFFFFFF ) /* * 2336 114819072 */
+RASTERIZER_ENTRY( 0x00580035, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A1F, 0xFFFFFFFF ) /* * 363325 100404294 */
+RASTERIZER_ENTRY( 0x00582C35, 0x00045110, 0x00000001, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* * 40918 96318738 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B0739, 0x0C26101F, 0xFFFFFFFF ) /* * 54815 94990269 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B0739, 0x0C261A1F, 0xFFFFFFFF ) /* * 123032 91652828 */
+RASTERIZER_ENTRY( 0x01422429, 0x00000000, 0x00000001, 0x000B0739, 0x0C261A1F, 0xFFFFFFFF ) /* * 82767 86431997 */
+RASTERIZER_ENTRY( 0x01422429, 0x00000000, 0x00000001, 0x000B0739, 0x0C26101F, 0xFFFFFFFF ) /* * 9874 78101834 */
+RASTERIZER_ENTRY( 0x01422429, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A1F, 0xFFFFFFFF ) /* * 102146 72570879 */
+RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B073B, 0x0C26100F, 0xFFFFFFFF ) /* * 657804 67229658 */
+RASTERIZER_ENTRY( 0x00580035, 0x00045110, 0x00000001, 0x000B03F9, 0x0C261A0F, 0xFFFFFFFF ) /* * 10428 63173865 */
+RASTERIZER_ENTRY( 0x01422429, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* * 230145 57902926 */
+RASTERIZER_ENTRY( 0x01422C19, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* * 769654 53992486 */
+RASTERIZER_ENTRY( 0x01422C19, 0x00000000, 0x00000001, 0x000B0739, 0x0C26101F, 0xFFFFFFFF ) /* * 85365 51865697 */
+RASTERIZER_ENTRY( 0x00582435, 0x00515110, 0x00000001, 0x000B0739, 0x0C261AC9, 0xFFFFFFFF ) /* * 454674 46165536 */
+RASTERIZER_ENTRY( 0x00580035, 0x00000000, 0x00000001, 0x000B073B, 0x0C26101F, 0xFFFFFFFF ) /* * 101889 33337987 */
+RASTERIZER_ENTRY( 0x00580035, 0x00000000, 0x00000001, 0x000B0739, 0x0C26101F, 0xFFFFFFFF ) /* * 255952 29810993 */
+//RASTERIZER_ENTRY( 0x00582425, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A1F, 0xFFFFFFFF ) /* * 106190 25430383 */
+//RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A1F, 0xFFFFFFFF ) /* * 595001 23268601 */
+//RASTERIZER_ENTRY( 0x0142612A, 0x00000000, 0x00000001, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 946410 22589110 */
+//RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* * 330036 21323230 */
+//RASTERIZER_ENTRY( 0x01422C19, 0x00000000, 0x00000001, 0x000B0739, 0x0C261A1F, 0xFFFFFFFF ) /* * 40089 13470498 */
+//RASTERIZER_ENTRY( 0x01422C19, 0x00000000, 0x00000000, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* * 90906 12850855 */
+//RASTERIZER_ENTRY( 0x00582C35, 0x00515110, 0x00000001, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 9492 12115280 */
+//RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B073B, 0x0C26101F, 0xFFFFFFFF ) /* * 453515 12013961 */
+//RASTERIZER_ENTRY( 0x01422C19, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A1F, 0xFFFFFFFF ) /* * 33829 8384312 */
+//RASTERIZER_ENTRY( 0x00580035, 0x00000000, 0x00000001, 0x000B073B, 0x0C26100F, 0xFFFFFFFF ) /* * 83986 7841206 */
+//RASTERIZER_ENTRY( 0x00580035, 0x00045110, 0x00000001, 0x000B0339, 0x0C261A0F, 0xFFFFFFFF ) /* * 42515 7242660 */
+//RASTERIZER_ENTRY( 0x00582425, 0x00000000, 0x00000001, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 706 6158684 */
+//RASTERIZER_ENTRY( 0x00582425, 0x00000000, 0x00000001, 0x000B0739, 0x0C26101F, 0xFFFFFFFF ) /* * 62051 5819485 */
+//RASTERIZER_ENTRY( 0x0142612A, 0x00000000, 0x00000000, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 135139 5063467 */
+//RASTERIZER_ENTRY( 0x01422429, 0x00000000, 0x00000001, 0x000B073B, 0x0C26100F, 0xFFFFFFFF ) /* * 10359 5135837 */
+//RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 170159 4449246 */
+//RASTERIZER_ENTRY( 0x00582425, 0x00000000, 0x00000001, 0x000B073B, 0x0C26101F, 0xFFFFFFFF ) /* * 19037 4371219 */
+//RASTERIZER_ENTRY( 0x01422429, 0x00000000, 0x00000001, 0x000B073B, 0x0C26101F, 0xFFFFFFFF ) /* * 8963 4352501 */
+//RASTERIZER_ENTRY( 0x01422C39, 0x00045110, 0x00000001, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* * 47712 4159994 */
+//RASTERIZER_ENTRY( 0x01422C19, 0x00000000, 0x00000000, 0x000B073B, 0x0C261ACF, 0xFFFFFFFF ) /* * 47525 4151435 */
+//RASTERIZER_ENTRY( 0x01422C19, 0x00000000, 0x00000001, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 34980 3794066 */
+//RASTERIZER_ENTRY( 0x0142613A, 0x00045110, 0x00000000, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 6540 2358068 */
+//RASTERIZER_ENTRY( 0x0142611A, 0x00045110, 0x00000000, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 703308 2096781 */
+//RASTERIZER_ENTRY( 0x00580035, 0x00045110, 0x00000001, 0x000B0339, 0x0C261A1F, 0xFFFFFFFF ) /* * 3963 2079440 */
+//RASTERIZER_ENTRY( 0x01422439, 0x00000000, 0x00000001, 0x000B073B, 0x0C261AC9, 0xFFFFFFFF ) /* * 22866 2008397 */
+//RASTERIZER_ENTRY( 0x01420039, 0x00000000, 0x00000001, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 69705 1673671 */
+//RASTERIZER_ENTRY( 0x01422C19, 0x00000000, 0x00000001, 0x000B073B, 0x0C26100F, 0xFFFFFFFF ) /* * 13366 1575120 */
+//RASTERIZER_ENTRY( 0x0142613A, 0x00000000, 0x00000000, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 50625 1408211 */
+//RASTERIZER_ENTRY( 0x0142613A, 0x00045110, 0x00000001, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 1244348 1244346 */
+//RASTERIZER_ENTRY( 0x00582425, 0x00000000, 0x00000001, 0x000B073B, 0x0C26100F, 0xFFFFFFFF ) /* * 13791 1222735 */
+//RASTERIZER_ENTRY( 0x00580035, 0x00000000, 0x00000001, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* * 33064 943590 */
+//RASTERIZER_ENTRY( 0x0142610A, 0x00045110, 0x00000001, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 2041 926507 */
+//RASTERIZER_ENTRY( 0x00480019, 0x00045110, 0x00000001, 0x000B073B, 0x0C261A0F, 0xFFFFFFFF ) /* * 2722 453924 */
+//RASTERIZER_ENTRY( 0x00580035, 0x00000000, 0x00000001, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 68232 306869 */
+//RASTERIZER_ENTRY( 0x0142611A, 0x00045110, 0x00000001, 0x000B0379, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 7164 269002 */
+
+/* mace -------> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824100F, 0xFFFFFFFF ) /* * 7204150 1340201579 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x08241ADF, 0xFFFFFFFF ) /* * 15332 1181663232 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0xFFFFFFFF ) /* * 104456 652582379 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824180F, 0xFFFFFFFF ) /* * 488613 368880164 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x082418CF, 0xFFFFFFFF ) /* * 352924 312417405 */
+RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0xFFFFFFFF ) /* * 15024 291762384 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x082410CF, 0xFFFFFFFF ) /* * 711824 279246170 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x0824100F, 0xFFFFFFFF ) /* * 735574 171881981 */
+RASTERIZER_ENTRY( 0x00602401, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0xFFFFFFFF ) /* * 943006 154374023 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x082410CF, 0xFFFFFFFF ) /* * 103877 101077498 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824108F, 0xFFFFFFFF ) /* * 710125 87547221 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x08241ACF, 0xFFFFFFFF ) /* * 9834 79774966 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0379, 0x082418DF, 0xFFFFFFFF ) /* * 17644 70187036 */
+RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000000, 0x000B0379, 0x082418DF, 0xFFFFFFFF ) /* * 11324 56633925 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0379, 0x0824180F, 0xFFFFFFFF ) /* * 96743 40820171 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0739, 0x082418CF, 0xFFFFFFFF ) /* * 166053 29100794 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045117, 0x00000000, 0x000B0339, 0x082418CF, 0xFFFFFFFF ) /* * 166053 29100697 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0379, 0x0824188F, 0xFFFFFFFF ) /* * 6723 29076516 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824188F, 0xFFFFFFFF ) /* * 53297 23928976 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x0824180F, 0xFFFFFFFF ) /* * 10309 19001776 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0379, 0x0824180F, 0xFFFFFFFF ) /* * 22105 17473157 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0379, 0x0824188F, 0xFFFFFFFF ) /* * 11304 17236698 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x082410DF, 0xFFFFFFFF ) /* * 1664 17180883 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x08241A0F, 0xFFFFFFFF ) /* * 148606 12274278 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x082418CF, 0xFFFFFFFF ) /* * 80692 9248007 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000001, 0x000B0739, 0x082418CF, 0xFFFFFFFF ) /* * 37819 8080994 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045117, 0x00000001, 0x000B0339, 0x082418CF, 0xFFFFFFFF ) /* * 37819 8080969 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0379, 0x082418DF, 0xFFFFFFFF ) /* * 536 7930305 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045117, 0x00000000, 0x000B0339, 0x082418CF, 0xFFFFFFFF ) /* * 27601 7905364 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0739, 0x082418CF, 0xFFFFFFFF ) /* * 27601 7905364 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0739, 0x082418CF, 0xFFFFFFFF ) /* * 36314 7667917 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045117, 0x00000000, 0x000B0339, 0x082418CF, 0xFFFFFFFF ) /* * 36314 7667917 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0739, 0x082418CF, 0xFFFFFFFF ) /* * 31109 6020110 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045117, 0x00000000, 0x000B0339, 0x082418CF, 0xFFFFFFFF ) /* * 31109 6020110 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045117, 0x00000000, 0x000B0339, 0x082418CF, 0xFFFFFFFF ) /* * 42689 5959231 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0739, 0x082418CF, 0xFFFFFFFF ) /* * 42689 5959231 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x0824188F, 0xFFFFFFFF ) /* * 11965 5118044 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000001, 0x000B0379, 0x0824180F, 0xFFFFFFFF ) /* * 11923 4662909 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0379, 0x082410CF, 0xFFFFFFFF ) /* * 4422 4624260 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0379, 0x0824100F, 0xFFFFFFFF ) /* * 3853 3596375 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000001, 0x000B0379, 0x082418DF, 0xFFFFFFFF ) /* * 400 3555759 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0379, 0x0824180F, 0xFFFFFFFF ) /* * 3755 3453084 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0xFFFFFFFF ) /* * 4170 2425016 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824184F, 0xFFFFFFFF ) /* * 322 2220073 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0379, 0x082418CF, 0xFFFFFFFF ) /* * 4008 1201335 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x0824108F, 0xFFFFFFFF ) /* * 13704 883585 */
+
+/* sfrush -----> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0824101F ) /* * 590139 246714190 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x0824101F, 0x0824101F ) /* * 397774 153418144 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x082410DF ) /* * 22732 146975666 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x00000000, 0x0824101F ) /* * 306398 130393278 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824101F, 0x0824101F ) /* * 437743 117403881 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824181F, 0x0824101F ) /* * 66608 109289500 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x00000000, 0x082410DF ) /* * 19101 92573085 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0824181F ) /* * 258287 78618228 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x0824181F, 0x0824101F ) /* * 61814 68788856 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0779, 0x082410DF, 0x0824181F ) /* * 149792 61464124 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824181F, 0x0824181F ) /* * 109988 55083276 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x08241ADF, 0x00000000 ) /* * 478 46989312 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x08241ADF, 0x0824181F ) /* * 468 46006272 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x00000000, 0x0824181F ) /* * 125204 39023396 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x08241ADF, 0x082410DB ) /* * 394 38731776 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x082410DF, 0x082410DB ) /* * 12890 36333568 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0379, 0x0824101F, 0x0824101F ) /* * 147995 31086325 */
+RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000000, 0x000B077B, 0x00000000, 0x082410DB ) /* * 3576 29294592 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x0824181F, 0x0824181F ) /* * 76059 29282981 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0x0824101F ) /* * 12632 29173808 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x00000000, 0x082418DF ) /* * 14040 24318118 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000001, 0x000B0379, 0x0824101F, 0x0824101F ) /* * 56586 17643207 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0x0824181F ) /* * 9130 17277440 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0x0824101F ) /* * 66302 17049921 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0779, 0x082410DF, 0x0824101F ) /* * 64380 16463672 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x082410DF, 0x0824181F ) /* * 152 14942208 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0x0824101F ) /* * 8748 13810176 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x082708DF, 0x0824101F ) /* * 216634 10628656 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000001, 0x000B077B, 0x00000000, 0x082410DB ) /* * 1282 10502144 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0x0824101F ) /* * 74636 9758030 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0x082410DB ) /* * 58652 9353671 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0x082410DB ) /* * 5242 8038747 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B077B, 0x082410DB, 0x082410DB ) /* * 11048 7538060 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0824101F, 0x0824181F ) /* * 121630 6906591 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x082418DF ) /* * 19553 6864245 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0x082418DF ) /* * 1287 6648834 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x082708DF, 0x0824101F ) /* * 197766 6617876 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x082700DF, 0x0824101F ) /* * 75470 6231739 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0779, 0x08241ADF, 0x0824101F ) /* * 180 5898240 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0779, 0x082410DF, 0x082410DB ) /* * 7692 5743360 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0x0824181F ) /* * 20128 4980591 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0x0824181F ) /* * 1144 4685824 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x082700DF, 0x0824101F ) /* * 72299 4466336 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000000, 0x000B0779, 0x082410DF, 0x082410DB ) /* * 3750 4018176 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x082410DF, 0x082410DF ) /* * 7533 3692141 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B077B, 0x082410DB, 0x0824101F ) /* * 9484 3610674 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000001, 0x000B0779, 0x0824101F, 0x0824181F ) /* * 128660 3216280 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0x082410DB ) /* * 22214 3172813 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B077B, 0x082410DB, 0x0824181F ) /* * 5094 3099098 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000001, 0x000B0779, 0x082418DF, 0x0824101F ) /* * 1954 2850924 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0x0824181F ) /* * 1542 2434304 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0x00000000 ) /* * 478 1957888 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0x0824181F ) /* * 468 1916928 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B077B, 0x082410DB, 0x0824101F ) /* * 11664 1729188 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000001, 0x000B077B, 0x082410DB, 0x082410DB ) /* * 1282 1640960 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000001, 0x000B077B, 0x082410DB, 0x0824101F ) /* * 388 1589248 */
+//RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000001, 0x000B0779, 0x082410DF, 0x082410DB ) /* * 1282 1312768 */
+//RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B077B, 0x082410DB, 0x0824181F ) /* * 3928 1046582 */
+
+/* vaportrx ---> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00482405, 0x00000000, 0x00000000, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 2226138 592165102 */
+RASTERIZER_ENTRY( 0x00482435, 0x00000000, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 53533 281405105 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B07F9, 0x0C261ACF, 0xFFFFFFFF ) /* * 314131 219103141 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x000B0339, 0x0C261A0F, 0xFFFFFFFF ) /* * 216329 95014510 */
+RASTERIZER_ENTRY( 0x00482405, 0x00000009, 0x00000000, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 317128 92010096 */
+RASTERIZER_ENTRY( 0x0142613A, 0x00045119, 0x00000000, 0x000B07F9, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 13728 88595930 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0339, 0x0C261ACF, 0xFFFFFFFF ) /* * 649448 81449105 */
+RASTERIZER_ENTRY( 0x00482435, 0x00000000, 0x00000000, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 444231 60067944 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x000B0339, 0x0C26184F, 0xFFFFFFFF ) /* * 36057 58970468 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0339, 0x0C26100F, 0xFFFFFFFF ) /* * 53147 48856709 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B07F9, 0x0C2610C9, 0xFFFFFFFF ) /* * 447654 47171792 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0339, 0x0C261A0F, 0xFFFFFFFF ) /* * 207392 38933691 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0739, 0x0C2610CF, 0xFFFFFFFF ) /* * 2015632 33364173 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x000B0339, 0x0C26100F, 0xFFFFFFFF ) /* * 196361 30395218 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0339, 0x0C2610CF, 0xFFFFFFFF ) /* * 110898 28973006 */
+RASTERIZER_ENTRY( 0x00482435, 0x00000009, 0x00000000, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 135107 16301589 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0339, 0x0C261A8F, 0xFFFFFFFF ) /* * 22375 15797748 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0339, 0x0C26184F, 0xFFFFFFFF ) /* * 141539 7513140 */
+RASTERIZER_ENTRY( 0x0142613A, 0x00045119, 0x00000000, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 621403 5369705 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045110, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 30443 4070277 */
+//RASTERIZER_ENTRY( 0x00482405, 0x00045110, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 22121 3129894 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 9187 1864599 */
+//RASTERIZER_ENTRY( 0x00482405, 0x00044110, 0x00000000, 0x000B0739, 0x0C2610CF, 0xFFFFFFFF ) /* * 10390 1694950 */
+//RASTERIZER_ENTRY( 0x0142610A, 0x00000009, 0x00000000, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 25366 1624563 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0739, 0x0C261A0F, 0xFFFFFFFF ) /* * 69033 1607970 */
+//RASTERIZER_ENTRY( 0x0142610A, 0x00000000, 0x00000000, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 36316 1084818 */
+//RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x000B0339, 0x0C2610CF, 0xFFFFFFFF ) /* * 1813 816763 */
+//RASTERIZER_ENTRY( 0x0142613A, 0x00045119, 0x00000000, 0x000B0339, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 6602 767221 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045110, 0x00000000, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 2547 646048 */
+//RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x000B0339, 0x0C261A8F, 0xFFFFFFFF ) /* * 2394 501590 */
+//RASTERIZER_ENTRY( 0x0142613A, 0x00000009, 0x00000000, 0x000B0739, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 14078 440086 */
+//RASTERIZER_ENTRY( 0x0142610A, 0x00045119, 0x00000000, 0x000B0339, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 9877 429160 */
+//RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x000B0339, 0x0C261ACF, 0xFFFFFFFF ) /* * 3222 366052 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00000009, 0x00000000, 0x000B0739, 0x0C2610CF, 0xFFFFFFFF ) /* * 5942 285657 */
+//RASTERIZER_ENTRY( 0x00482405, 0x00044119, 0x00000000, 0x000B0339, 0x0C2610CF, 0xFFFFFFFF ) /* * 2328 239688 */
+//RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x000B0739, 0x0C26100F, 0xFFFFFFFF ) /* * 1129 208448 */
+
+/* wg3dh ------> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x0824181F, 0xFFFFFFFF ) /* * 127676 116109477 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x0824189F, 0xFFFFFFFF ) /* * 96310 112016758 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x0824109F, 0xFFFFFFFF ) /* * 1412831 108682642 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x0824101F, 0xFFFFFFFF ) /* * 1612798 45952714 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x08241AD9, 0xFFFFFFFF ) /* * 5960 6103040 */
+RASTERIZER_ENTRY( 0x00002435, 0x00045119, 0x00000000, 0x000B0779, 0x082418DF, 0xFFFFFFFF ) /* * 56512 4856542 */
+RASTERIZER_ENTRY( 0x00480035, 0x00045119, 0x00000000, 0x000B0779, 0x0824109F, 0xFFFFFFFF ) /* * 8480 2045940 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0379, 0x0824181F, 0xFFFFFFFF ) /* * 2779 1994317 */
+RASTERIZER_ENTRY( 0x00000035, 0x00045119, 0x00000000, 0x000B0779, 0x0824105F, 0xFFFFFFFF ) /* * 154691 1922774 */
+RASTERIZER_ENTRY( 0x00002435, 0x00045119, 0x00000000, 0x000B0779, 0x082410DF, 0xFFFFFFFF ) /* * 18114 776139 */
+
+/* gauntleg ---> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C24100F ) /* * 157050 668626339 */
+RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0779, 0x0C22400F, 0x0C241ACF ) /* * 1079126 580272490 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x0C241A4F, 0x0C24100F ) /* * 49686 232178144 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x0C24104F, 0x0C24100F ) /* * 1048560 206304396 */
+RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0779, 0x0C2240CF, 0x0C241ACF ) /* * 59176 182444375 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C241A4F ) /* * 66342 179689728 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x0C24180F, 0x0C24180F ) /* * 72264 109413344 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x0C24100F, 0x0C24100F ) /* * 281243 75399210 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C24104F ) /* * 126384 68412120 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0C241A0F, 0x0C24100F ) /* * 26864 43754988 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C241ACF ) /* * 30510 32759936 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x0C24180F, 0x0C24100F ) /* * 44783 31884168 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C24180F ) /* * 34946 31359362 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C241ACF ) /* * 8006 28367999 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0379, 0x0C24180F, 0x0C24180F ) /* * 15430 27908213 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C241A0F ) /* * 29306 25166802 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x0C24180F, 0x0C241ACF ) /* * 27737 24517949 */
+RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0C241ACF, 0x0C24100F ) /* * 6783 21292092 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0379, 0x00000000, 0x0C24180F ) /* * 9591 17815763 */
+RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x0C24100F, 0x0C24180F ) /* * 343966 13864759 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x0C241ACF, 0x0C24100F ) /* * 11842 12126208 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0C241A8F, 0x0C24100F ) /* * 6648 9788508 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C2418CF ) /* * 8444 8646656 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0379, 0x0C24180F, 0x0C24100F ) /* * 9677 8365606 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0C24100F, 0x0C24100F ) /* * 844920 8289326 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C24184F ) /* * 3108 8010176 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B03F9, 0x00000000, 0x0C24180F ) /* * 1435 6209238 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0379, 0x0C24180F, 0x0C24100F ) /* * 5754 5617499 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0379, 0x0C24180F, 0x0C24180F ) /* * 1608 5557253 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x0C24100F, 0x0C241ACF ) /* * 105127 5133321 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0379, 0x0C24180F, 0x0C241ACF ) /* * 3460 4689138 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x0C24180F, 0x0C24100F ) /* * 7025 4629550 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C24180F ) /* * 7164 4407683 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C24188F ) /* * 1922 3924179 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C24180F ) /* * 4116 3733777 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0779, 0x00000000, 0x0C241A8F ) /* * 2626 3732809 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B03F9, 0x0C24180F, 0x0C24180F ) /* * 778 3202973 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x00000000, 0x000B0779, 0x0C24184F, 0x0C24100F ) /* * 1525 2997446 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B03F9, 0x0C24180F, 0x0C241A0F ) /* * 645 2975266 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00044119, 0x00000000, 0x000B0379, 0x00000000, 0x0C241A0F ) /* * 5212 2491361 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0379, 0x00000000, 0x0C24180F ) /* * 825 1996513 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0379, 0x0C24180F, 0x0C241A0F ) /* * 466 1967163 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x0580000F, 0x0C24180F ) /* * 77400 1883434 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0379, 0x0C24180F, 0x0C24100F ) /* * 472 1698177 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0779, 0x0C24180F, 0x0C24180F ) /* * 2476 1678760 */
+//RASTERIZER_ENTRY( 0x00600C09, 0x00045119, 0x00000000, 0x000B0379, 0x00000000, 0x0C24180F ) /* * 4054 1541748 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00044119, 0x00000000, 0x000B0379, 0x0C241A0F, 0x0C24180F ) /* * 3132 1509438 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x00000000, 0x000B0779, 0x0580080F, 0x0C24180F ) /* * 8582 1324196 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00044119, 0x00000000, 0x000B0379, 0x00000000, 0x0C24100F ) /* * 1436 1239704 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B03F9, 0x0C24180F, 0x0C24100F ) /* * 253 1220316 */
+//RASTERIZER_ENTRY( 0x00600039, 0x00045119, 0x00000000, 0x000B0779, 0x0C22480F, 0x0C241ACF ) /* * 2433 1014668 */
+
+/* gauntdl ----> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C241ACF ) /* * 30860 1128173568 */
+RASTERIZER_ENTRY( 0x0060743A, 0x00045119, 0x000000C1, 0x000B0779, 0x0C22400F, 0x0C241ACF ) /* * 2631692 1117011118 */
+RASTERIZER_ENTRY( 0x0060743A, 0x00045110, 0x000000C1, 0x000B0779, 0x0C22400F, 0x0C241ACF ) /* * 2429239 826969012 */
+RASTERIZER_ENTRY( 0x0060743A, 0x00045119, 0x000000C1, 0x000B0779, 0x0C22480F, 0x0C241ACF ) /* * 454056 468285142 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C2418CF ) /* * 257586 355634672 */
+RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0379, 0x00000009, 0x0C24180F ) /* * 10898 134362122 */
+RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C241A0F ) /* * 32195 126327049 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x000000C1, 0x000B0779, 0x0C2410CF, 0x0C24100F ) /* * 855240 123899880 */
+RASTERIZER_ENTRY( 0x00602439, 0x00045110, 0x000000C1, 0x000B0379, 0x00000009, 0x0C24180F ) /* * 1718 120629204 */
+RASTERIZER_ENTRY( 0x0060743A, 0x00045119, 0x000000C1, 0x000B0779, 0x0C22488F, 0x0C241ACF ) /* * 186839 120281357 */
+RASTERIZER_ENTRY( 0x0060743A, 0x00045119, 0x000000C1, 0x000B0379, 0x0C22480F, 0x0C241ACF ) /* * 14102 115428820 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C2410CF ) /* * 88530 98271949 */
+RASTERIZER_ENTRY( 0x0060743A, 0x00045110, 0x000000C1, 0x000B0379, 0x0C22480F, 0x0C241ACF ) /* * 12994 68053222 */
+RASTERIZER_ENTRY( 0x00602439, 0x00044110, 0x00000000, 0x000B0379, 0x00000009, 0x0C24100F ) /* * 68273 67454880 */
+RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24180F ) /* * 100026 62271618 */
+RASTERIZER_ENTRY( 0x0060743A, 0x00045110, 0x000000C1, 0x000B0779, 0x0C22480F, 0x0C241ACF ) /* * 153285 44411342 */
+RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24100F ) /* * 157545 40702131 */
+RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x000000C1, 0x000B0779, 0x0C241ACF, 0x0C24100F ) /* * 7800 31948800 */
+RASTERIZER_ENTRY( 0x0060743A, 0x00045110, 0x000000C1, 0x000B0779, 0x0C22408F, 0x0C241ACF ) /* * 47623 20321183 */
+RASTERIZER_ENTRY( 0x00602439, 0x00044119, 0x00000000, 0x000B0379, 0x00000009, 0x0C24188F ) /* * 21570 19324892 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045110, 0x000000C1, 0x000B0779, 0x0C241ACF, 0x0C24100F ) /* * 3698 15147008 */
+//RASTERIZER_ENTRY( 0x0060743A, 0x00045119, 0x000000C1, 0x000B0779, 0x0C22408F, 0x0C241ACF ) /* * 19765 12383722 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C241ACF ) /* * 662274 10563855 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045110, 0x000000C1, 0x000B0779, 0x0C24180F, 0x0C241ACF ) /* * 27909 10462997 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045110, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24180F ) /* * 78671 10286957 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045110, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24188F ) /* * 52038 9928244 */
+//RASTERIZER_ENTRY( 0x0060743A, 0x00045119, 0x000000C1, 0x000B0779, 0x0C224A0F, 0x0C241ACF ) /* * 27469 9239782 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24100F ) /* * 757116 8072783 */
+//RASTERIZER_ENTRY( 0x0060743A, 0x00045110, 0x000000C1, 0x000B0779, 0x0C22488F, 0x0C241ACF ) /* * 18018 7035833 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00044119, 0x00000000, 0x000B0379, 0x00000009, 0x0C241A0F ) /* * 50339 5976564 */
+//RASTERIZER_ENTRY( 0x00603430, 0x00040219, 0x00000000, 0x000B0379, 0x00000009, 0x0C2410CE ) /* * 29385 5466384 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C24180F ) /* * 423347 5355017 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24180F, 0x0C241ACF ) /* * 162620 4709092 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24100F ) /* * 463705 4642480 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24180F, 0x0C24180F ) /* * 280337 4425529 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0779, 0x0C24180F, 0x0C24180F ) /* * 212646 3432265 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045119, 0x000000C1, 0x000B0779, 0x0C2418CF, 0x0C24100F ) /* * 5788 2963456 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0779, 0x0C24180F, 0x0C24100F ) /* * 460800 2609198 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C24180F ) /* * 251108 2392362 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C24100F ) /* * 297219 2352862 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x0584180F, 0x0C2410CF ) /* * 9913 2097069 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0779, 0x0C24180F, 0x0C241ACF ) /* * 142722 2091569 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0379, 0x0C24180F, 0x0C241ACF ) /* * 8820 2053325 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24188F ) /* * 10346 2033427 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24188F, 0x0C241ACF ) /* * 2136 2017241 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00044119, 0x00000000, 0x000B0379, 0x00000009, 0x0C24100F ) /* * 1505 1928490 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C241ACF ) /* * 176734 1842440 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C24180F ) /* * 262577 1799080 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24180F ) /* * 83179 1534171 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x00000009, 0x0C24188F ) /* * 3863 1527077 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0379, 0x0C24180F, 0x0C24180F ) /* * 8021 1472661 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C241A0F, 0x0C241ACF ) /* * 85416 1342195 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24180F, 0x0C24100F ) /* * 261360 1335048 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00000009, 0x000000C1, 0x000B0779, 0x0C2418CF, 0x0C24100F ) /* * 74811 1320900 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C24100F ) /* * 239331 1268661 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C241ACF ) /* * 107769 1244175 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0379, 0x0C24180F, 0x0C241ACF ) /* * 3706 1216182 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24100F, 0x0C24188F ) /* * 49608 1206129 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00000009, 0x000000C1, 0x000B0779, 0x0C2418CF, 0x0C241ACF ) /* * 42440 1204109 */
+//RASTERIZER_ENTRY( 0x00482435, 0x00045110, 0x000000C1, 0x000B0779, 0x0C2410CF, 0x0C24100F ) /* * 29584 1168568 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00045119, 0x000000C1, 0x000B0779, 0x0C24180F, 0x0C241ACF ) /* * 17729 1152869 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045110, 0x000000C1, 0x000B0379, 0x0C24180F, 0x0C24100F ) /* * 4052 1108726 */
+//RASTERIZER_ENTRY( 0x00602C19, 0x00045119, 0x000000C1, 0x000B0779, 0x0C2418CF, 0x0C24100F ) /* * 7082 1079348 */
+//RASTERIZER_ENTRY( 0x00602439, 0x00044119, 0x00000000, 0x000B0379, 0x00000009, 0x0C24180F ) /* * 7761 1023855 */
+
+/* gradius4 ----> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+//RASTERIZER_ENTRY( 0x02420002, 0x00000009, 0x00000000, 0x00030F7B, 0x08241AC7, 0xFFFFFFFF ) /* intro */
+//RASTERIZER_ENTRY( 0x01420021, 0x00005119, 0x00000000, 0x00030F7B, 0x14261AC7, 0xFFFFFFFF ) /* intro */
+//RASTERIZER_ENTRY( 0x00000005, 0x00005119, 0x00000000, 0x00030F7B, 0x14261A87, 0xFFFFFFFF ) /* in-game */
+
+/* nbapbp ------> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 */
+//RASTERIZER_ENTRY( 0x00424219, 0x00000000, 0x00000001, 0x00030B7B, 0x08241AC7, 0xFFFFFFFF ) /* intro */
+//RASTERIZER_ENTRY( 0x00002809, 0x00004110, 0x00000001, 0x00030FFB, 0x08241AC7, 0xFFFFFFFF ) /* in-game */
+//RASTERIZER_ENTRY( 0x00424219, 0x00000000, 0x00000001, 0x00030F7B, 0x08241AC7, 0xFFFFFFFF ) /* in-game */
+//RASTERIZER_ENTRY( 0x0200421A, 0x00001510, 0x00000001, 0x00030F7B, 0x08241AC7, 0xFFFFFFFF ) /* in-game */
+/* gtfore06 ----> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 hash */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x000000C1, 0x00010F79, 0x0C261ACD, 0x0C261ACD ) /* 18 1064626 69362127 */
+RASTERIZER_ENTRY( 0x00002425, 0x00045119, 0x000000C1, 0x00010F79, 0x0C224A0D, 0x0C261ACD ) /* 47 3272483 31242799 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x000000C1, 0x00010F79, 0x00000ACD, 0x0C261ACD ) /* 9 221917 12348555 */
+RASTERIZER_ENTRY( 0x00002425, 0x00045110, 0x000000C1, 0x00010FF9, 0x00000ACD, 0x0C261ACD ) /* 26 57291 9357989 */
+RASTERIZER_ENTRY( 0x00002429, 0x00000000, 0x000000C1, 0x00010FF9, 0x00000A09, 0x0C261A0F ) /* 12 97156 8530607 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x000000C1, 0x00010F79, 0x000000C4, 0x0C261ACD ) /* 55 110144 5265532 */
+RASTERIZER_ENTRY( 0x00002425, 0x00045110, 0x000000C1, 0x00010FF9, 0x000000C4, 0x0C261ACD ) /* 61 16644 1079382 */
+RASTERIZER_ENTRY( 0x00002425, 0x00045119, 0x000000C1, 0x00010FF9, 0x000000C4, 0x0C261ACD ) /* 5 8332 1065229 */
+RASTERIZER_ENTRY( 0x00002425, 0x00045119, 0x000000C1, 0x00010F79, 0x0C224A0D, 0x0C261A0D ) /* 45 8148 505013 */
+RASTERIZER_ENTRY( 0x00002425, 0x00045119, 0x00000000, 0x00010F79, 0x0C224A0D, 0x0C261A0D ) /* 84 45233 248267 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x000000C1, 0x00010F79, 0x0C261ACD, 0x0C2610C4 ) /* 90 10235 193036 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x000000C1, 0x00010FF9, 0x0C261ACD, 0x0C261ACD ) /* * 29 3777 83777 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x00010FF9, 0x0C261ACD, 0x042210C0 ) /* 2 24952 66761 */
+RASTERIZER_ENTRY( 0x00002429, 0x00000000, 0x00000000, 0x00010FF9, 0x00000A09, 0x0C261A0F ) /* 24 661 50222 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x00010FF9, 0x0C261ACD, 0x04221AC9 ) /* 92 12504 43720 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x000000C1, 0x00010FF9, 0x0C261ACD, 0x0C2610C4 ) /* 79 2160 43650 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x00010FF9, 0x000000C4, 0x04221AC9 ) /* 19 2796 30377 */
+RASTERIZER_ENTRY( 0x00002425, 0x00045119, 0x000000C1, 0x00010FF9, 0x00000ACD, 0x0C261ACD ) /* 67 1962 14755 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x000000C1, 0x00010FF9, 0x000000C4, 0x0C261ACD ) /* * 66 74 3951 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x00000000, 0x00010FF9, 0x00000ACD, 0x04221AC9 ) /* 70 374 3691 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045119, 0x000000C1, 0x00010FF9, 0x00000ACD, 0x0C261ACD ) /* * 20 350 7928 */
+/* virtpool ----> fbzColorPath alphaMode fogMode, fbzMode, texMode0, texMode1 hash */
+RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B0739, 0x0C261A0F, 0x042210C0 ) /* * 78 2182388 74854175 */
+RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B07F9, 0x0C261A0F, 0x042210C0 ) /* * 46 114830 6776826 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045110, 0x00000000, 0x000B0739, 0x0C261A0F, 0x042210C0 ) /* * 58 1273673 4513463 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045110, 0x00000000, 0x000B0739, 0x0C261A09, 0x042210C0 ) /* * 46 634995 2275612 */
+RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B073B, 0x0C261A0F, 0x042210C0 ) /* * 46 26651 1883507 */
+RASTERIZER_ENTRY( 0x00482405, 0x00045110, 0x00000000, 0x000B073B, 0x0C261A0F, 0x042210C0 ) /* * 26 220644 751241 */
+//RASTERIZER_ENTRY( 0x00002421, 0x00445110, 0x00000000, 0x000B073B, 0x0C261A09, 0x042210C0 ) /* * 79 14846 3499120 */
+//RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B0739, 0x0C261A09, 0x042210C0 ) /* * 66 26665 1583363 */
+//RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B073B, 0x0C26100F, 0x042210C0 ) /* * 78 33096 957935 */
+//RASTERIZER_ENTRY( 0x00002425, 0x00445110, 0x00000000, 0x000B07F9, 0x0C261A0F, 0x042210C0 ) /* * 38 12494 678029 */
+//RASTERIZER_ENTRY( 0x00800000, 0x00000000, 0x00000000, 0x00000200, 0x00000000, 0x00000000 ) /* * 28 25348 316181 */
+//RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B0739, 0x0C26100F, 0x042210C0 ) /* * 13 11344 267903 */
+//RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B073B, 0x0C261A09, 0x042210C0 ) /* * 34 1548 112168 */
+//RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B07FB, 0x0C26100F, 0x042210C0 ) /* * 35 664 25222 */
+//RASTERIZER_ENTRY( 0x00000002, 0x00000000, 0x00000000, 0x00000300, 0xFFFFFFFF, 0xFFFFFFFF ) /* * 33 512 18393 */
+//RASTERIZER_ENTRY( 0x00002421, 0x00000000, 0x00000000, 0x000B07FB, 0x0C261A0F, 0x042210C0 ) /* * 14 216 16842 */
+//RASTERIZER_ENTRY( 0x00000001, 0x00000000, 0x00000000, 0x00000300, 0x00000800, 0x00000800 ) /* * 87 2 72 */
+//RASTERIZER_ENTRY( 0x00000001, 0x00000000, 0x00000000, 0x00000200, 0x08241A00, 0x08241A00 ) /* * 92 2 8 */
+//RASTERIZER_ENTRY( 0x00000001, 0x00000000, 0x00000000, 0x00000200, 0x00000000, 0x08241A00 ) /* * 93 2 8 */
+
+#endif
diff --git a/src/devices/video/voodoo.h b/src/devices/video/voodoo.h
new file mode 100644
index 00000000000..dcd03d38876
--- /dev/null
+++ b/src/devices/video/voodoo.h
@@ -0,0 +1,168 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ voodoo.h
+
+ 3dfx Voodoo Graphics SST-1/2 emulator.
+
+***************************************************************************/
+
+#ifndef __VOODOO_H__
+#define __VOODOO_H__
+
+#pragma once
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+/* enumeration specifying which model of Voodoo we are emulating */
+enum
+{
+ TYPE_VOODOO_1,
+ TYPE_VOODOO_2,
+ TYPE_VOODOO_BANSHEE,
+ TYPE_VOODOO_3
+};
+
+#define STD_VOODOO_1_CLOCK 50000000
+#define STD_VOODOO_2_CLOCK 90000000
+#define STD_VOODOO_BANSHEE_CLOCK 90000000
+#define STD_VOODOO_3_CLOCK 132000000
+
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_VOODOO_FBMEM(_value) \
+ voodoo_device::static_set_fbmem(*device, _value);
+
+#define MCFG_VOODOO_TMUMEM(_value1, _value2) \
+ voodoo_device::static_set_tmumem(*device, _value1, _value2);
+
+#define MCFG_VOODOO_SCREEN_TAG(_tag) \
+ voodoo_device::static_set_screen_tag(*device, _tag);
+
+#define MCFG_VOODOO_CPU_TAG(_tag) \
+ voodoo_device::static_set_cpu_tag(*device, _tag);
+
+#define MCFG_VOODOO_VBLANK_CB(_devcb) \
+ devcb = &voodoo_device::static_set_vblank_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_VOODOO_STALL_CB(_devcb) \
+ devcb = &voodoo_device::static_set_stall_callback(*device, DEVCB_##_devcb);
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+int voodoo_update(device_t *device, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+int voodoo_get_type(device_t *device);
+int voodoo_is_stalled(device_t *device);
+void voodoo_set_init_enable(device_t *device, UINT32 newval);
+
+/* ----- device interface ----- */
+
+class voodoo_device : public device_t
+{
+public:
+ voodoo_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~voodoo_device();
+
+
+ static void static_set_fbmem(device_t &device, int value) { downcast<voodoo_device &>(device).m_fbmem = value; }
+ static void static_set_tmumem(device_t &device, int value1, int value2) { downcast<voodoo_device &>(device).m_tmumem0 = value1; downcast<voodoo_device &>(device).m_tmumem1 = value2; }
+ static void static_set_screen_tag(device_t &device, const char *tag) { downcast<voodoo_device &>(device).m_screen = tag; }
+ static void static_set_cpu_tag(device_t &device, const char *tag) { downcast<voodoo_device &>(device).m_cputag = tag; }
+ template<class _Object> static devcb_base &static_set_vblank_callback(device_t &device, _Object object) { return downcast<voodoo_device &>(device).m_vblank.set_callback(object); }
+ template<class _Object> static devcb_base &static_set_stall_callback(device_t &device, _Object object) { return downcast<voodoo_device &>(device).m_stall.set_callback(object); }
+
+ DECLARE_READ32_MEMBER( voodoo_r );
+ DECLARE_WRITE32_MEMBER( voodoo_w );
+
+ // access to legacy token
+ struct voodoo_state *token() const { assert(m_token != NULL); return m_token; }
+ void common_start_voodoo(UINT8 type);
+
+ UINT8 m_fbmem;
+ UINT8 m_tmumem0;
+ UINT8 m_tmumem1;
+ const char * m_screen;
+ const char * m_cputag;
+ devcb_write_line m_vblank;
+ devcb_write_line m_stall;
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_stop();
+ virtual void device_reset();
+private:
+ // internal state
+ struct voodoo_state *m_token;
+};
+
+class voodoo_1_device : public voodoo_device
+{
+public:
+ voodoo_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type VOODOO_1;
+
+class voodoo_2_device : public voodoo_device
+{
+public:
+ voodoo_2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type VOODOO_2;
+
+class voodoo_banshee_device : public voodoo_device
+{
+public:
+ voodoo_banshee_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ voodoo_banshee_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ DECLARE_READ32_MEMBER( banshee_r );
+ DECLARE_WRITE32_MEMBER( banshee_w );
+ DECLARE_READ32_MEMBER( banshee_fb_r );
+ DECLARE_WRITE32_MEMBER( banshee_fb_w );
+ DECLARE_READ32_MEMBER( banshee_io_r );
+ DECLARE_WRITE32_MEMBER( banshee_io_w );
+ DECLARE_READ32_MEMBER( banshee_rom_r );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ DECLARE_READ32_MEMBER( banshee_agp_r );
+ DECLARE_WRITE32_MEMBER( banshee_agp_w );
+ DECLARE_READ8_MEMBER( banshee_vga_r );
+ DECLARE_WRITE8_MEMBER( banshee_vga_w );
+};
+
+extern const device_type VOODOO_BANSHEE;
+
+class voodoo_3_device : public voodoo_banshee_device
+{
+public:
+ voodoo_3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type VOODOO_3;
+
+#endif
diff --git a/src/devices/video/voodoo_pci.c b/src/devices/video/voodoo_pci.c
new file mode 100644
index 00000000000..7570a007efb
--- /dev/null
+++ b/src/devices/video/voodoo_pci.c
@@ -0,0 +1,180 @@
+// license:BSD-3-Clause
+// copyright-holders:Ted Green
+#include "voodoo_pci.h"
+
+int voodoo_pci_device::m_type = 0;
+
+static MACHINE_CONFIG_FRAGMENT( voodoo_1_pci )
+ MCFG_DEVICE_ADD("voodoo", VOODOO_1, STD_VOODOO_1_CLOCK)
+ MCFG_VOODOO_FBMEM(4)
+ MCFG_VOODOO_TMUMEM(1, 0)
+ MCFG_VOODOO_SCREEN_TAG("screen")
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_FRAGMENT( voodoo_2_pci )
+ MCFG_DEVICE_ADD("voodoo", VOODOO_2, STD_VOODOO_2_CLOCK)
+ MCFG_VOODOO_FBMEM(4)
+ MCFG_VOODOO_TMUMEM(1, 0)
+ MCFG_VOODOO_SCREEN_TAG("screen")
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_FRAGMENT( voodoo_banshee_pci )
+ MCFG_DEVICE_ADD("voodoo", VOODOO_BANSHEE, STD_VOODOO_BANSHEE_CLOCK)
+ MCFG_VOODOO_FBMEM(16)
+ MCFG_VOODOO_SCREEN_TAG("screen")
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_FRAGMENT( voodoo_3_pci )
+ MCFG_DEVICE_ADD("voodoo", VOODOO_3, STD_VOODOO_3_CLOCK)
+ MCFG_VOODOO_FBMEM(16)
+ MCFG_VOODOO_SCREEN_TAG("screen")
+MACHINE_CONFIG_END
+
+machine_config_constructor voodoo_pci_device::device_mconfig_additions() const
+{
+ switch (m_type) {
+ case TYPE_VOODOO_1:
+ return MACHINE_CONFIG_NAME( voodoo_1_pci );
+ break;
+ case TYPE_VOODOO_2:
+ return MACHINE_CONFIG_NAME( voodoo_2_pci );
+ break;
+ case TYPE_VOODOO_BANSHEE:
+ return MACHINE_CONFIG_NAME( voodoo_banshee_pci );
+ break;
+ //case TYPE_VOODOO_3
+ default:
+ return MACHINE_CONFIG_NAME( voodoo_3_pci );
+ break;
+ }
+}
+
+const device_type VOODOO_PCI = &device_creator<voodoo_pci_device>;
+
+DEVICE_ADDRESS_MAP_START(config_map, 32, voodoo_pci_device)
+ AM_RANGE(0x40, 0x4f) AM_READWRITE (pcictrl_r, pcictrl_w)
+ AM_INHERIT_FROM(pci_device::config_map)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(voodoo_reg_map, 32, voodoo_pci_device)
+ AM_RANGE(0x0, 0x00ffffff) AM_DEVREADWRITE("voodoo", voodoo_device, voodoo_r, voodoo_w)
+ADDRESS_MAP_END
+
+
+DEVICE_ADDRESS_MAP_START(banshee_reg_map, 32, voodoo_pci_device)
+ AM_RANGE(0x0, 0x01ffffff) AM_DEVREADWRITE("voodoo", voodoo_banshee_device, banshee_r, banshee_w)
+ADDRESS_MAP_END
+DEVICE_ADDRESS_MAP_START(lfb_map, 32, voodoo_pci_device)
+ AM_RANGE(0x0, 0x01ffffff) AM_DEVREADWRITE("voodoo", voodoo_banshee_device, banshee_fb_r, banshee_fb_w)
+ADDRESS_MAP_END
+DEVICE_ADDRESS_MAP_START(io_map, 32, voodoo_pci_device)
+ AM_RANGE(0x000, 0x0ff) AM_DEVREADWRITE("voodoo", voodoo_banshee_device, banshee_io_r, banshee_io_w)
+ADDRESS_MAP_END
+
+voodoo_pci_device::voodoo_pci_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : pci_device(mconfig, VOODOO_PCI, "Voodoo PCI", tag, owner, clock, "voodoo_pci", __FILE__),
+ m_voodoo(*this, "voodoo"), m_fbmem(2), m_tmumem0(0), m_tmumem1(0)
+{
+}
+
+void voodoo_pci_device::set_cpu_tag(const char *_cpu_tag)
+{
+ m_cpu_tag = _cpu_tag;
+}
+
+void voodoo_pci_device::device_start()
+{
+ voodoo_device::static_set_cpu_tag(m_voodoo, m_cpu_tag);
+ voodoo_device::static_set_fbmem(m_voodoo, m_fbmem);
+ voodoo_device::static_set_tmumem(m_voodoo, m_tmumem0, m_tmumem1);
+ switch (m_type) {
+ //void set_ids(UINT32 main_id, UINT8 revision, UINT32 pclass, UINT32 subsystem_id);
+ case TYPE_VOODOO_1:
+ set_ids(0x121a0001, 0x02, 0x000000, 0x000000);
+ break;
+ case TYPE_VOODOO_2:
+ set_ids(0x121a0002, 0x02, 0x040000, 0x000000);
+ break;
+ case TYPE_VOODOO_BANSHEE:
+ set_ids(0x121a0003, 0x02, 0x000003, 0x000000);
+ break;
+ //case TYPE_VOODOO_3
+ default:
+ set_ids(0x121a0005, 0x02, 0x000003, 0x000000);
+ break;
+ }
+ pci_device::device_start();
+ if (m_type<=TYPE_VOODOO_2) {
+ add_map(16*1024*1024, M_MEM | M_PREF, FUNC(voodoo_pci_device::voodoo_reg_map));
+ } else {
+ add_map(32*1024*1024, M_MEM, FUNC(voodoo_pci_device::banshee_reg_map));
+ add_map(32*1024*1024, M_MEM, FUNC(voodoo_pci_device::lfb_map));
+ add_map(256, M_IO, FUNC(voodoo_pci_device::io_map));
+ }
+}
+
+void voodoo_pci_device::device_reset()
+{
+ memset(m_pcictrl_reg, 0, sizeof(m_pcictrl_reg));
+ pci_device::device_reset();
+}
+
+void voodoo_pci_device::map_extra(UINT64 memory_window_start, UINT64 memory_window_end, UINT64 memory_offset, address_space *memory_space,
+ UINT64 io_window_start, UINT64 io_window_end, UINT64 io_offset, address_space *io_space)
+{
+ logerror("%s: map_extra\n", this->tag());
+ // Really awkward way of getting vga address space mapped
+ // Should really be dependent on voodoo VGAINIT0 bit 8 and IO base + 0xc3 bit 0
+ if (1) {
+ // io map is on bank_infos[2]
+ bank_info &bi = bank_infos[2];
+ if(bi.adr==-1)
+ return;
+ if(UINT32(bi.adr) == UINT32(~(bi.size - 1)))
+ return;
+
+ UINT64 start;
+ address_space *space;
+ if(bi.flags & M_IO) {
+ space = io_space;
+ start = bi.adr + io_offset;
+ } else {
+ space = memory_space;
+ start = bi.adr + memory_offset;
+ }
+ // The mapping needs to only check high address bits
+ start = (start & 0xFFFF0000) + 0x300;
+ UINT64 end = (start & 0xFFFF0000) + 0x3ef;
+ space->install_device_delegate(start, end, *this, bi.map);
+ logerror("%s: map %s at %0*x-%0*x\n", this->tag(), bi.map.name(), bi.flags & M_IO ? 4 : 8, UINT32(start), bi.flags & M_IO ? 4 : 8, UINT32(end));
+ }
+
+}
+
+UINT32 voodoo_pci_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ return voodoo_update(m_voodoo, bitmap, cliprect) ? 0 : UPDATE_HAS_NOT_CHANGED;
+}
+
+// PCI bus control
+READ32_MEMBER (voodoo_pci_device::pcictrl_r)
+{
+ UINT32 result = m_pcictrl_reg[offset];
+ if (1)
+ logerror("%06X:voodoo_pci_device pcictrl_r from offset %02X = %08X & %08X\n", space.device().safe_pc(), offset*4, result, mem_mask);
+ return result;
+}
+WRITE32_MEMBER (voodoo_pci_device::pcictrl_w)
+{
+ COMBINE_DATA(&m_pcictrl_reg[offset]);
+ switch (offset) {
+ case 0x0/4: // The address map starts at 0x40
+ // HW initEnable
+ voodoo_set_init_enable(m_voodoo, data);
+ logerror("%06X:voodoo_pci_device pcictrl_w to offset %02X = %08X & %08X\n", space.device().safe_pc(), offset*4, data, mem_mask);
+ break;
+ default:
+ logerror("%06X:voodoo_pci_device pcictrl_w to offset %02X = %08X & %08X\n", space.device().safe_pc(), offset*4, data, mem_mask);
+ break;
+ }
+}
diff --git a/src/devices/video/voodoo_pci.h b/src/devices/video/voodoo_pci.h
new file mode 100644
index 00000000000..a1fe30bd7db
--- /dev/null
+++ b/src/devices/video/voodoo_pci.h
@@ -0,0 +1,59 @@
+// license:BSD-3-Clause
+// copyright-holders:Ted Green
+// 3dfx Voodoo Graphics SST-1/2 emulator.
+
+#ifndef VOODOO_PCI_H
+#define VOODOO_PCI_H
+
+#include "machine/pci.h"
+#include "voodoo.h"
+
+#define MCFG_VOODOO_PCI_ADD(_tag, _type, _cpu_tag) \
+ voodoo_pci_device::set_type(_type); \
+ MCFG_PCI_DEVICE_ADD(_tag, VOODOO_PCI, 0, 0, 0, 0) \
+ downcast<voodoo_pci_device *>(device)->set_cpu_tag(_cpu_tag);
+
+#define MCFG_VOODOO_PCI_FBMEM(_value) \
+ downcast<voodoo_pci_device *>(device)->set_fbmem(_value);
+
+#define MCFG_VOODOO_PCI_TMUMEM(_value1, _value2) \
+ downcast<voodoo_pci_device *>(device)->set_tmumem(_value1, _value2);
+
+class voodoo_pci_device : public pci_device {
+public:
+ voodoo_pci_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual void map_extra(UINT64 memory_window_start, UINT64 memory_window_end, UINT64 memory_offset, address_space *memory_space,
+ UINT64 io_window_start, UINT64 io_window_end, UINT64 io_offset, address_space *io_space);
+ // optional information overrides
+ virtual machine_config_constructor device_mconfig_additions() const;
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ virtual DECLARE_ADDRESS_MAP(config_map, 32);
+
+ void set_cpu_tag(const char *tag);
+ static void set_type(const int type) {m_type = type;}
+ void set_fbmem(const int fbmem) {m_fbmem = fbmem;}
+ void set_tmumem(const int tmumem0, const int tmumem1) {m_tmumem0 = tmumem0; m_tmumem1 = tmumem1;}
+
+ DECLARE_READ32_MEMBER( pcictrl_r);
+ DECLARE_WRITE32_MEMBER( pcictrl_w);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ required_device<voodoo_device> m_voodoo;
+ static int m_type;
+ int m_fbmem, m_tmumem0, m_tmumem1;
+ const char *m_cpu_tag;
+
+ UINT32 m_pcictrl_reg[0x10];
+ DECLARE_ADDRESS_MAP(voodoo_reg_map, 32);
+ DECLARE_ADDRESS_MAP(banshee_reg_map, 32);
+ DECLARE_ADDRESS_MAP(lfb_map, 32);
+ DECLARE_ADDRESS_MAP(io_map, 32);
+};
+
+extern const device_type VOODOO_PCI;
+
+#endif