summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author R. Belmont <rb6502@users.noreply.github.com>2017-10-19 14:27:04 -0400
committer GitHub <noreply@github.com>2017-10-19 14:27:04 -0400
commit98fbbe8275bfc68b4c7a03d3135e6f8e90b9ba0f (patch)
treed775607b0844a30aa63dce20d4d996eea4f6cc9f
parentff6a9334e39ab9e5ff368f98319dcfc6f4544d8d (diff)
parent1721d2ab598df2dd16f129829fd4fb9d7d2dd063 (diff)
Merge pull request #2731 from DavidHaywood/gamate
rewrote the Gamate video implementation from scratch using Kevtris' document and Peter Wilhelmsen's notes
-rw-r--r--scripts/target/mame/mess.lua2
-rw-r--r--src/mame/drivers/gamate.cpp165
-rw-r--r--src/mame/video/gamate.cpp347
-rw-r--r--src/mame/video/gamate.h64
4 files changed, 418 insertions, 160 deletions
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index fc525cd6f30..dc9d42af3aa 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -3467,6 +3467,8 @@ files {
MAME_DIR .. "src/mame/drivers/fk1.cpp",
MAME_DIR .. "src/mame/drivers/ft68m.cpp",
MAME_DIR .. "src/mame/drivers/gamate.cpp",
+ MAME_DIR .. "src/mame/video/gamate.cpp",
+ MAME_DIR .. "src/mame/video/gamate.h",
MAME_DIR .. "src/mame/drivers/gameking.cpp",
MAME_DIR .. "src/mame/drivers/gimix.cpp",
MAME_DIR .. "src/mame/drivers/goupil.cpp",
diff --git a/src/mame/drivers/gamate.cpp b/src/mame/drivers/gamate.cpp
index d73743e4310..02ee6bda185 100644
--- a/src/mame/drivers/gamate.cpp
+++ b/src/mame/drivers/gamate.cpp
@@ -18,7 +18,7 @@
#include "bus/generic/carts.h"
#include "bus/generic/slot.h"
#include "cpu/m6502/m6502.h"
-#include "rendlay.h"
+#include "video/gamate.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"
@@ -32,7 +32,6 @@ public:
, m_ay(*this, "ay8910")
, m_cart(*this, "cartslot")
, m_io_joy(*this, "JOY")
- , m_palette(*this, "palette")
, m_bios(*this, "bios")
, m_bank(*this, "bank")
, m_bankmulti(*this, "bankmulti")
@@ -46,9 +45,7 @@ public:
DECLARE_WRITE8_MEMBER(gamate_cart_protection_w);
DECLARE_WRITE8_MEMBER(cart_bankswitchmulti_w);
DECLARE_WRITE8_MEMBER(cart_bankswitch_w);
- DECLARE_READ8_MEMBER(gamate_video_r);
DECLARE_READ8_MEMBER(gamate_nmi_r);
- DECLARE_WRITE8_MEMBER(gamate_video_w);
DECLARE_WRITE8_MEMBER(sound_w);
DECLARE_READ8_MEMBER(sound_r);
DECLARE_DRIVER_INIT(gamate);
@@ -62,19 +59,6 @@ private:
struct
{
- uint8_t reg[8];
- struct
- {
- bool page2; // else page1
- uint8_t ypos, xpos/*tennis*/;
- uint8_t data[2][0x100][0x20];
- } bitmap;
- uint8_t x, y;
- bool y_increment;
- } video;
-
- struct
- {
bool set;
int bit_shifter;
uint8_t cartridge_byte;
@@ -87,7 +71,6 @@ private:
required_device<ay8910_device> m_ay;
required_device<generic_slot_device> m_cart;
required_ioport m_io_joy;
- required_device<palette_device> m_palette;
required_shared_ptr<uint8_t> m_bios;
required_memory_bank m_bank;
required_memory_bank m_bankmulti;
@@ -163,40 +146,6 @@ READ8_MEMBER( gamate_state::newer_protection_set )
return 0;
}
-WRITE8_MEMBER( gamate_state::gamate_video_w )
-{
- video.reg[offset]=data;
- switch (offset)
- {
- case 1:
- if (data&0xf)
- printf("lcd mode %x\n", data);
- video.y_increment=data&0x40;
- break;
- case 2:
- video.bitmap.xpos=data;
- break;
- case 3:
- if (data>=200)
- printf("lcd ypos: %x\n", data);
- video.bitmap.ypos=data;
- break;
- case 4:
- video.bitmap.page2=data&0x80;
- video.x=data&0x1f;
- break;
- case 5:
- video.y=data;
- break;
- case 7:
- video.bitmap.data[video.bitmap.page2][video.y][video.x&(ARRAY_LENGTH(video.bitmap.data[0][0])-1)]=data;
- if (video.y_increment)
- video.y++;
- else
- video.x++; // overruns
- }
-}
-
WRITE8_MEMBER( gamate_state::cart_bankswitchmulti_w )
{
bank_multi=data;
@@ -208,21 +157,6 @@ WRITE8_MEMBER( gamate_state::cart_bankswitch_w )
m_bank->set_base(m_cart_ptr+0x4000*data);
}
-READ8_MEMBER( gamate_state::gamate_video_r )
-{
- if (offset!=6)
- return 0;
- uint8_t data = video.bitmap.data[video.bitmap.page2][video.y][video.x&(ARRAY_LENGTH(video.bitmap.data[0][0])-1)];
-// if (m_maincpu->pc()<0xf000)
-// popmessage("lcd read x:%x y:%x mode:%x data:%x\n", video.x, video.y, video.reg[1], data);
- if (video.y_increment)
- video.y++;
- else
- video.x++; // overruns?
-
- return data;
-}
-
READ8_MEMBER( gamate_state::gamate_nmi_r )
{
uint8_t data=0;
@@ -247,7 +181,7 @@ static ADDRESS_MAP_START( gamate_mem, AS_PROGRAM, 8, gamate_state )
AM_RANGE(0x4000, 0x400f) AM_READWRITE(sound_r,sound_w)
AM_RANGE(0x4400, 0x4400) AM_READ_PORT("JOY")
AM_RANGE(0x4800, 0x4800) AM_READ(gamate_nmi_r)
- AM_RANGE(0x5000, 0x5007) AM_READWRITE(gamate_video_r, gamate_video_w)
+ AM_RANGE(0x5000, 0x5007) AM_DEVICE("video", gamate_video_device, regs_map)
AM_RANGE(0x5800, 0x5800) AM_READ(newer_protection_set)
AM_RANGE(0x5900, 0x5900) AM_WRITE(protection_reset)
AM_RANGE(0x5a00, 0x5a00) AM_READ(protection_r)
@@ -272,75 +206,12 @@ static INPUT_PORTS_START( gamate )
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("Select")
INPUT_PORTS_END
-/* palette in red, green, blue tribles */
-static const unsigned char gamate_colors[4][3] =
-{
- { 255,255,255 },
- { 0xa0, 0xa0, 0xa0 },
- { 0x60, 0x60, 0x60 },
- { 0, 0, 0 }
-};
-
-PALETTE_INIT_MEMBER(gamate_state, gamate)
-{
- int i;
-
- for (i = 0; i < 4; i++)
- {
- palette.set_pen_color(i, gamate_colors[i][0], gamate_colors[i][1], gamate_colors[i][2]);
- }
-}
-
-static void BlitPlane(uint16_t* line, uint8_t plane1, uint8_t plane2)
-{
- line[3]=(plane1&1)|((plane2<<1)&2);
- line[2]=((plane1>>1)&1)|((plane2<<0)&2);
- line[1]=((plane1>>2)&1)|((plane2>>1)&2);
- line[0]=((plane1>>3)&1)|((plane2>>2)&2);
-}
-
-uint32_t gamate_state::screen_update_gamate(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
-{
- int x, y, j;
- for (y=0;y<152;y++)
- {
- for (x=-(video.bitmap.xpos&7), j=0;x<160;x+=8, j++)
- {
- uint8_t d1, d2;
- if (video.bitmap.ypos<200)
- {
- d1=video.bitmap.data[0][(y+video.bitmap.ypos)%200][(j+video.bitmap.xpos/8)&0x1f];
- d2=video.bitmap.data[1][(y+video.bitmap.ypos)%200][(j+video.bitmap.xpos/8)&0x1f];
- }
- else
- if ((video.bitmap.ypos&0xf)<8)
- { // lcdtest, of course still some registers not known, my gamate doesn't display bottom lines; most likely problematic 200 warp around hardware! no real usage
- int yi=(y+(video.bitmap.ypos&0xf)-8);
- if (yi<0)
- yi=video.bitmap.ypos+y; // in this case only 2nd plane used!?, source of first plane?
- d1=video.bitmap.data[0][yi][(j+video.bitmap.xpos/8)&0x1f]; // value of lines bevor 0 chaos
- d2=video.bitmap.data[1][yi][(j+video.bitmap.xpos/8)&0x1f];
- }
- else
- {
- d1=video.bitmap.data[0][y][(j+video.bitmap.xpos/8)&0x1f];
- d2=video.bitmap.data[1][y][(j+video.bitmap.xpos/8)&0x1f];
- }
- BlitPlane(&bitmap.pix16(y, x+4), d1, d2);
- BlitPlane(&bitmap.pix16(y, x), d1>>4, d2>>4);
- }
- }
- return 0;
-}
-
DRIVER_INIT_MEMBER(gamate_state,gamate)
{
- memset(&video, 0, sizeof(video));/* memset(m_ram, 0, sizeof(m_ram));*/
timer1 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gamate_state::gamate_timer),this));
timer2 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gamate_state::gamate_timer2),this));
}
-
void gamate_state::machine_start()
{
m_cart_ptr = memregion("maincpu")->base() + 0x6000;
@@ -357,17 +228,6 @@ void gamate_state::machine_start()
card_protection.unprotected=false;
timer2->enable(true);
timer2->reset(m_maincpu->cycles_to_attotime(1000));
-#if 0
- save_item(NAME(m_video.data));
- save_item(NAME(m_video.index));
- save_item(NAME(m_video.x));
- save_item(NAME(m_video.y));
- save_item(NAME(m_video.mode));
- save_item(NAME(m_video.delayed));
- save_item(NAME(m_video.pixels));
- save_item(NAME(m_ports));
- save_item(NAME(m_ram));
-#endif
}
TIMER_CALLBACK_MEMBER(gamate_state::gamate_timer)
@@ -385,26 +245,11 @@ TIMER_CALLBACK_MEMBER(gamate_state::gamate_timer2)
timer2->reset(m_maincpu->cycles_to_attotime(32768/2));
}
-
-INTERRUPT_GEN_MEMBER(gamate_state::gamate_interrupt)
-{
-}
-
static MACHINE_CONFIG_START( gamate )
- MCFG_CPU_ADD("maincpu", M6502, 4433000/2)
+ MCFG_CPU_ADD("maincpu", M6502, 4433000/2) // NCR 65CX02
MCFG_CPU_PROGRAM_MAP(gamate_mem)
- MCFG_CPU_VBLANK_INT_DRIVER("screen", gamate_state, gamate_interrupt)
-
- MCFG_SCREEN_ADD("screen", LCD)
- MCFG_SCREEN_REFRESH_RATE(60)
- MCFG_SCREEN_SIZE(160, 152)
- MCFG_SCREEN_VISIBLE_AREA(0, 160-1, 0, 152-1)
- MCFG_SCREEN_UPDATE_DRIVER(gamate_state, screen_update_gamate)
- MCFG_SCREEN_PALETTE("palette")
-
- MCFG_PALETTE_ADD("palette", ARRAY_LENGTH(gamate_colors))
- MCFG_PALETTE_INIT_OWNER(gamate_state, gamate)
- MCFG_DEFAULT_LAYOUT(layout_lcd)
+
+ MCFG_GAMATE_VIDEO_ADD("video")
/* sound hardware */
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") // Stereo headphone output
diff --git a/src/mame/video/gamate.cpp b/src/mame/video/gamate.cpp
new file mode 100644
index 00000000000..90b224f4034
--- /dev/null
+++ b/src/mame/video/gamate.cpp
@@ -0,0 +1,347 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood, Peter Wilhelmsen, Kevtris
+
+/*
+ Notes:
+
+ Some games are glitchy, most of these glitches are verified to happen on hardware
+ for example
+
+ Badly flipped sprites in Tornado and Insect War
+ Heavy flickering sprites in many games
+
+ Most of these issues are difficult to notice on real hardware due to the poor
+ quality display.
+
+ Thanks to Kevtris for the documentation on which this implementation is based
+ (some comments taken directly from this)
+ http://blog.kevtris.org/blogfiles/Gamate%20Inside.txt
+
+ ToDo:
+
+ Emulate vram pull / LCD refresh timings more accurately.
+ Interrupt should maybe be in here, not in drivers/gamate.cpp?
+ Verify both Window modes act the same as hardware.
+*/
+
+#include "emu.h"
+#include "video/gamate.h"
+#include "rendlay.h"
+#include "screen.h"
+
+DEFINE_DEVICE_TYPE(GAMATE_VIDEO, gamate_video_device, "gamate_vid", "Gamate Video Hardware")
+
+DEVICE_ADDRESS_MAP_START( regs_map, 8, gamate_video_device )
+ AM_RANGE(0x01,0x01) AM_WRITE(lcdcon_w)
+ AM_RANGE(0x02,0x02) AM_WRITE(xscroll_w)
+ AM_RANGE(0x03,0x03) AM_WRITE(yscroll_w)
+ AM_RANGE(0x04,0x04) AM_WRITE(xpos_w)
+ AM_RANGE(0x05,0x05) AM_WRITE(ypos_w)
+ AM_RANGE(0x06,0x06) AM_READ(vram_r)
+ AM_RANGE(0x07,0x07) AM_WRITE(vram_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START( vram_map, 8, gamate_video_device )
+ AM_RANGE(0x0000, 0x3fff) AM_RAM AM_SHARE("vram") // 2x 8KB SRAMs
+ADDRESS_MAP_END
+
+gamate_video_device::gamate_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, GAMATE_VIDEO, tag, owner, clock),
+ device_memory_interface(mconfig, *this),
+ m_vram_space_config("vramspace", ENDIANNESS_BIG, 8, 14, 0, address_map_delegate(FUNC(gamate_video_device::vram_map), this)),
+ m_vram(*this, "vram"),
+ m_vramaddress(0),
+ m_bitplaneselect(0),
+ m_scrollx(0),
+ m_scrolly(0),
+ m_window(0),
+ m_swapplanes(0),
+ m_incrementdir(0),
+ m_displayblank(0)
+{
+}
+
+void gamate_video_device::set_vram_addr_lower_5bits(uint8_t data)
+{
+ m_vramaddress = (m_vramaddress & 0x3fe0) | (data & 0x1f);
+}
+
+void gamate_video_device::set_vram_addr_upper_8bits(uint8_t data)
+{
+ m_vramaddress = (m_vramaddress & 0x001f) | (data << 5);
+}
+
+void gamate_video_device::increment_vram_address()
+{
+ if (m_incrementdir)
+ m_vramaddress += 0x20;
+ else
+ m_vramaddress++;
+
+ m_vramaddress &= 0x1fff;
+}
+
+WRITE8_MEMBER(gamate_video_device::lcdcon_w)
+{
+ /*
+ NXWS ???E
+ E: When set, stops the LCD controller from refreshing the LCD. This can
+ damage the LCD material because the invert signal is no longer toggling,
+ and the pixel/frame/row clocks/pulses are not being output.
+ S: Swap plane bits. When set, flip bit planes 0 and 1.
+ W: D0-DF is mapped in at rows 00-0Fh at the top of the screen, with no
+ X scroll for those rows. (see window bit info below)
+ X: When clear the video address increments by 1. When set, it increments
+ by 32.
+ N: When set, clears the LCD by blanking the data. The LCD refresh still occurs.
+ */
+ m_displayblank = (data & 0x80);
+ m_incrementdir = (data & 0x40);
+ m_window = (data & 0x20);
+ m_swapplanes = (data & 0x10);
+ // setting data & 0x01 is bad
+}
+
+WRITE8_MEMBER(gamate_video_device::xscroll_w)
+{
+ /*
+ XXXX XXXX
+ X: 8 bit Xscroll value
+ */
+ m_scrollx = data;
+}
+
+WRITE8_MEMBER(gamate_video_device::yscroll_w)
+{
+ /*
+ YYYY YYYY
+ Y: 8 bit Yscroll value
+ */
+ m_scrolly = data;
+}
+
+WRITE8_MEMBER(gamate_video_device::xpos_w)
+{
+ /*
+ BxxX XXXX
+ B: Bitplane. 0 = lower (bitplane 0), 1 = upper (bitplane 1)
+ X: 5 lower bits of the 13 bit VRAM address.
+ */
+ m_bitplaneselect = (data & 0x80) >> 7;
+ set_vram_addr_lower_5bits(data & 0x1f);
+}
+
+WRITE8_MEMBER(gamate_video_device::ypos_w)
+{
+ /*
+ YYYY YYYY
+ Y: 8 upper bits of 13 bit VRAM address.
+ */
+ set_vram_addr_upper_8bits(data);
+}
+
+READ8_MEMBER(gamate_video_device::vram_r)
+{
+ uint16_t address = m_vramaddress << 1;
+
+ if (m_bitplaneselect)
+ address += 1;
+
+ uint8_t ret = m_vramspace->read_byte(address);
+
+ increment_vram_address();
+
+ return ret;
+}
+
+WRITE8_MEMBER(gamate_video_device::vram_w)
+{
+ uint16_t address = m_vramaddress << 1;
+
+ if (m_bitplaneselect)
+ address += 1;
+
+ m_vramspace->write_byte(address, data);
+
+ increment_vram_address();
+}
+
+void gamate_video_device::get_real_x_and_y(int &ret_x, int &ret_y, int scanline)
+{
+ /* the Gamate video has 2 'Window' modes,
+ Mode 1 is enabled with an actual register
+ Mode 2 is enabled automatically based on the yscroll value
+
+ both modes seem designed to allow for a non-scrolling status bar at
+ the top of the display.
+ */
+
+ if (m_scrolly < 0xc8)
+ {
+ ret_y = scanline + m_scrolly;
+
+ if (ret_y >= 0xc8)
+ ret_y -= 0xc8;
+
+ ret_x = m_scrollx;
+
+ if (m_window) /* Mode 1 Window */
+ {
+ if (scanline < 0x10)
+ {
+ ret_x = 0;
+ ret_y = 0xd0 + scanline;
+ }
+ }
+ }
+ else /* Mode 2, do any games use this ? does above Window logic override this if enabled? */
+ {
+ ret_x = m_scrollx;
+
+ /*
+ Using Yscroll values of C8-CF, D8-DF, E8-EF, and F8-FF will result in the same
+ effect as if a Yscroll value of 00h were used.
+ */
+ if (m_scrolly & 0x08) // values of C8-CF, D8-DF, E8-EF, and F8-FF
+ {
+ ret_y = 0x00;
+ ret_x = m_scrollx;
+ }
+ else
+ {
+ /*
+ Values D0-D7, E0-E7, and F0-F7 all produce a bit more useful effect. The upper
+ 1-8 scanlines will be pulled from rows F8-FFh in VRAM (i.e. 1F00h = row F8h).
+
+ If F0 is selected, then the upper 8 rows will be the last 8 rows in VRAM-
+ 1F00-1FFFh area. If F1 is selected, the upper 8 rows will be the last 7 rows
+ in VRAM and so on. This special window area DOES NOT SCROLL with X making it
+ useful for status bars. I don't think any games actually used it, though.
+ */
+ int fixedscanlines = m_scrolly & 0x7;
+
+ if (scanline <= fixedscanlines)
+ {
+ ret_x = 0;
+ ret_y = 0xf8 + scanline + (7-fixedscanlines);
+ }
+ else
+ {
+ // no yscroll in this mode?
+ ret_x = m_scrollx;
+ ret_y = scanline;// +m_scrolly;
+
+ //if (ret_y >= 0xc8)
+ // ret_y -= 0xc8;
+ }
+
+ }
+ }
+}
+
+int gamate_video_device::get_pixel_from_vram(int x, int y)
+{
+ x &= 0xff;
+ y &= 0xff;
+
+ int x_byte = x >> 3;
+ x &= 0x7; // x pixel;
+
+ int address = ((y * 0x20) + x_byte) << 1;
+
+ int plane0 = (m_vram[address] >> (7-x)) & 0x1;
+ int plane1 = (m_vram[address + 1] >> (7-x)) & 0x1;
+
+ if (!m_swapplanes)
+ return plane0 | (plane1 << 1);
+ else
+ return plane1 | (plane0 << 1); // does any game use this?
+}
+
+uint32_t gamate_video_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
+ {
+ //printf("updating scanline %d\n", y);
+ int real_x, real_y;
+ get_real_x_and_y(real_x, real_y, y);
+
+ for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
+ {
+ int pix = get_pixel_from_vram(x + real_x, real_y);
+
+ if (m_displayblank)
+ pix = 0;
+
+ bitmap.pix16(y, x) = pix;
+ }
+ }
+
+ return 0;
+}
+
+device_memory_interface::space_config_vector gamate_video_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(0, &m_vram_space_config),
+ };
+}
+
+// this palette is taken from megaduck, from videos it looks similar
+static const unsigned char palette_gamate[] = {
+ 0x6B, 0xA6, 0x4A, 0x43, 0x7A, 0x63, 0x25, 0x59, 0x55, 0x12, 0x42, 0x4C
+};
+
+PALETTE_INIT_MEMBER(gamate_video_device, gamate)
+{
+ for (int i = 0; i < 4; i++)
+ palette.set_pen_color(i, palette_gamate[i * 3 + 0], palette_gamate[i * 3 + 1], palette_gamate[i * 3 + 2]);
+}
+
+/*
+ Of the 150 scanlines emitted, all contain pixel data pulled from RAM. There are
+ exactly 72900 clocks per frame, so at the nominal 4.433MHz rate, this means the
+ frame rate is 60.8093Hz.
+*/
+
+MACHINE_CONFIG_MEMBER( gamate_video_device::device_add_mconfig )
+ MCFG_SCREEN_ADD("screen", LCD)
+ MCFG_SCREEN_REFRESH_RATE(60.8093)
+ MCFG_SCREEN_SIZE(160, 150)
+ MCFG_SCREEN_VISIBLE_AREA(0, 160-1, 0, 150-1)
+ MCFG_SCREEN_UPDATE_DRIVER(gamate_video_device, screen_update)
+ MCFG_SCREEN_PALETTE("palette")
+ MCFG_SCREEN_VIDEO_ATTRIBUTES(VIDEO_UPDATE_SCANLINE) // close approximate until we use timers to emulate exact video update
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(0))
+
+ MCFG_DEFAULT_LAYOUT(layout_lcd)
+
+ MCFG_PALETTE_ADD("palette", 4)
+ MCFG_PALETTE_INIT_OWNER(gamate_video_device,gamate)
+MACHINE_CONFIG_END
+
+void gamate_video_device::device_start()
+{
+ m_vramspace = &space(0);
+
+ save_item(NAME(m_vramaddress));
+ save_item(NAME(m_bitplaneselect));
+ save_item(NAME(m_scrollx));
+ save_item(NAME(m_scrolly));
+ save_item(NAME(m_window));
+ save_item(NAME(m_swapplanes));
+ save_item(NAME(m_incrementdir));
+ save_item(NAME(m_displayblank));
+}
+
+void gamate_video_device::device_reset()
+{
+ m_vramaddress = 0;
+ m_bitplaneselect = 0;
+ m_scrollx = 0;
+ m_scrolly = 10;
+ m_window = 0;
+ m_swapplanes = 0;
+ m_incrementdir = 0;
+ m_displayblank = 0;
+}
diff --git a/src/mame/video/gamate.h b/src/mame/video/gamate.h
new file mode 100644
index 00000000000..0e5bf2f767b
--- /dev/null
+++ b/src/mame/video/gamate.h
@@ -0,0 +1,64 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood, Peter Wilhelmsen, Kevtris
+
+#ifndef MAME_VIDEO_GAMATE_H
+#define MAME_VIDEO_GAMATE_H
+
+#pragma once
+
+DECLARE_DEVICE_TYPE(GAMATE_VIDEO, gamate_video_device)
+
+#define MCFG_GAMATE_VIDEO_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, GAMATE_VIDEO, 0)
+
+class gamate_video_device : public device_t,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ gamate_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ DECLARE_WRITE8_MEMBER(lcdcon_w);
+ DECLARE_WRITE8_MEMBER(xscroll_w);
+ DECLARE_WRITE8_MEMBER(yscroll_w);
+ DECLARE_WRITE8_MEMBER(xpos_w);
+ DECLARE_WRITE8_MEMBER(ypos_w);
+ DECLARE_READ8_MEMBER(vram_r);
+ DECLARE_WRITE8_MEMBER(vram_w);
+
+ DECLARE_ADDRESS_MAP(regs_map, 8);
+ DECLARE_ADDRESS_MAP(vram_map, 8);
+
+ DECLARE_PALETTE_INIT(gamate);
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual space_config_vector memory_space_config() const override;
+ address_space *m_vramspace;
+
+private:
+ uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ const address_space_config m_vram_space_config;
+ required_shared_ptr<uint8_t> m_vram;
+
+ void set_vram_addr_lower_5bits(uint8_t data);
+ void set_vram_addr_upper_8bits(uint8_t data);
+ void increment_vram_address();
+
+ void get_real_x_and_y(int &ret_x, int &ret_y, int scanline);
+ int get_pixel_from_vram(int x, int y);
+
+ int m_vramaddress;
+ int m_bitplaneselect;
+ int m_scrollx;
+ int m_scrolly;
+ int m_window;
+ int m_swapplanes;
+ int m_incrementdir;
+ int m_displayblank;
+};
+
+#endif // MAME_VIDEO_GAMATE_H