summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/gamate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/video/gamate.cpp')
-rw-r--r--src/mame/video/gamate.cpp347
1 files changed, 347 insertions, 0 deletions
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;
+}