summaryrefslogtreecommitdiffstats
path: root/docs/release/src/mame/video/midtunit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docs/release/src/mame/video/midtunit.cpp')
-rw-r--r--docs/release/src/mame/video/midtunit.cpp1092
1 files changed, 1092 insertions, 0 deletions
diff --git a/docs/release/src/mame/video/midtunit.cpp b/docs/release/src/mame/video/midtunit.cpp
new file mode 100644
index 00000000000..10e0d6a7ca5
--- /dev/null
+++ b/docs/release/src/mame/video/midtunit.cpp
@@ -0,0 +1,1092 @@
+// license:BSD-3-Clause
+// copyright-holders:Alex Pasadyn, Zsolt Vasvari, Ernesto Corvi, Aaron Giles
+// thanks-to:Kurt Mahan
+/*************************************************************************
+
+ Video Emulation for Midway T-unit, W-unit, and X-unit games.
+
+**************************************************************************/
+
+#include "emu.h"
+#include "midtunit.h"
+#include "midtunit.ipp"
+#include "screen.h"
+#include "midtview.ipp"
+
+#include "debug/debugcon.h"
+#include "debug/debugcmd.h"
+#include "debugger.h"
+
+#include "emuopts.h" // Used by PNG logging
+#include "png.h" // Used by PNG logging
+
+//#include <rapidjson/prettywriter.h> // Used by JSON logging
+//#include <rapidjson/stringbuffer.h> // Used by JSON logging
+
+DEFINE_DEVICE_TYPE(MIDTUNIT_VIDEO, midtunit_video_device, "tunitvid", "Midway T-Unit Video")
+DEFINE_DEVICE_TYPE(MIDWUNIT_VIDEO, midwunit_video_device, "wunitvid", "Midway W-Unit Video")
+DEFINE_DEVICE_TYPE(MIDXUNIT_VIDEO, midxunit_video_device, "xunitvid", "Midway X-Unit Video")
+
+/* compile-time options */
+#define LOG_DMA 0 /* DMAs are logged if the 'L' key is pressed */
+
+midtunit_video_device::midtunit_video_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , m_maincpu(*this, finder_base::DUMMY_TAG)
+ , m_palette(*this, finder_base::DUMMY_TAG)
+ , m_gfxrom(*this, finder_base::DUMMY_TAG)
+#if DEBUG_MIDTUNIT_BLITTER
+ , m_debug_palette(*this, "debugpalette")
+#endif
+{
+}
+
+midtunit_video_device::midtunit_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : midtunit_video_device(mconfig, MIDTUNIT_VIDEO, tag, owner, clock)
+{
+}
+
+midwunit_video_device::midwunit_video_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : midtunit_video_device(mconfig, type, tag, owner, clock)
+{
+}
+
+midwunit_video_device::midwunit_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : midtunit_video_device(mconfig, MIDWUNIT_VIDEO, tag, owner, clock)
+{
+}
+
+midxunit_video_device::midxunit_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : midwunit_video_device(mconfig, MIDXUNIT_VIDEO, tag, owner, clock)
+{
+}
+
+/*************************************
+ *
+ * Debugger commands
+ *
+ *************************************/
+
+void midtunit_video_device::debug_init()
+{
+ if (machine().debug_flags & DEBUG_FLAG_ENABLED)
+ {
+ using namespace std::placeholders;
+ machine().debugger().console().register_command("midblit", CMDFLAG_CUSTOM_HELP, 0, 1, 4, std::bind(&midtunit_video_device::debug_commands, this, _1, _2));
+ }
+}
+
+void midtunit_video_device::debug_commands(int ref, const std::vector<std::string> &params)
+{
+ if (params.size() < 1)
+ return;
+
+ if (params[0] == "pngdma")
+ debug_png_dma_command(ref, params);
+ else
+ debug_help_command(ref, params);
+}
+
+void midtunit_video_device::debug_help_command(int ref, const std::vector<std::string> &params)
+{
+ debugger_console &con = machine().debugger().console();
+
+ con.printf("Available Midway Blitter commands:\n");
+ con.printf(" midblit pngdma,<enable>[,<path>][,<logjson>] -- Enable or disable dumping of DMA-drawn sprite PNGs to <path>, with or without JSON metadata\n");
+ con.printf(" midblit help -- this list\n");
+}
+
+void midtunit_video_device::debug_png_dma_command(int ref, const std::vector<std::string> &params)
+{
+ debugger_console &con = machine().debugger().console();
+
+ if (params.size() < 2)
+ {
+ con.printf("Error: not enough parameters for midblit pngdma command\n");
+ return;
+ }
+
+ if (params.size() > 4)
+ {
+ con.printf("Error: too many parameters for midblit pngdma command\n");
+ return;
+ }
+
+ bool old_state = m_log_png;
+ bool new_state = false;
+ if (!machine().debugger().commands().validate_boolean_parameter(params[1], new_state))
+ return;
+
+ if (!new_state)
+ {
+ if (params.size() > 2)
+ {
+ con.printf("Error: too many parameters for midblit pngdma command\n");
+ return;
+ }
+ m_log_png = false;
+ return;
+ }
+
+ if (params.size() < 3)
+ {
+ con.printf("Error: not enough parameters for midblit pngdma command\n");
+ return;
+ }
+
+ if (params[2].empty() || params[2].length() > 2047)
+ {
+ con.printf("Error: invalid path parameter for midblit pngdma command\n");
+ return;
+ }
+
+ strncpy(m_log_path, params[2].c_str(), 2047);
+
+ if (params.size() == 4)
+ {
+ if (!machine().debugger().commands().validate_boolean_parameter(params[3], m_log_json))
+ return;
+ }
+
+ m_log_png = new_state;
+
+ if (!old_state && new_state)
+ {
+ m_logged_rom = make_unique_clear<uint64_t[]>(0x4000000);
+ }
+ else if (old_state && !new_state)
+ {
+ m_logged_rom.reset();
+ }
+}
+
+
+/*************************************
+ *
+ * Video startup
+ *
+ *************************************/
+
+void midtunit_video_device::device_start()
+{
+ debug_init();
+
+ /* allocate memory */
+ m_local_videoram = std::make_unique<uint16_t[]>(0x100000/2);
+
+#if DEBUG_MIDTUNIT_BLITTER
+ m_debug_videoram = std::make_unique<uint16_t[]>(0x100000/2);
+#endif
+
+ m_logged_rom.reset();
+ m_log_png = false;
+
+ m_dma_timer = timer_alloc(TIMER_DMA);
+
+ /* reset all the globals */
+ m_gfxbank_offset[0] = 0x000000;
+ m_gfxbank_offset[1] = 0x400000;
+
+ memset(m_dma_register, 0, sizeof(m_dma_register));
+ memset(&m_dma_state, 0, sizeof(dma_state));
+ m_dma_state.gfxrom = m_gfxrom->base();
+
+ /* register for state saving */
+ save_item(NAME(m_midtunit_control));
+ save_item(NAME(m_gfxbank_offset));
+ save_pointer(NAME(m_local_videoram), 0x100000/sizeof(m_local_videoram[0]));
+ save_item(NAME(m_videobank_select));
+ save_item(NAME(m_dma_register));
+
+ INIT_TEMPLATED_DMA_DRAW_GROUP(m_dma_draw_skip_scale, true, true);
+ INIT_TEMPLATED_DMA_DRAW_GROUP(m_dma_draw_noskip_scale, false, true);
+ INIT_TEMPLATED_DMA_DRAW_GROUP(m_dma_draw_skip_noscale, true, false);
+ INIT_TEMPLATED_DMA_DRAW_GROUP(m_dma_draw_noskip_noscale, false, false);
+
+ m_gfx_rom_large = false;
+}
+
+void midwunit_video_device::device_start()
+{
+ midtunit_video_device::device_start();
+ m_gfx_rom_large = true;
+}
+
+void midxunit_video_device::device_start()
+{
+ midtunit_video_device::device_start();
+ m_gfx_rom_large = true;
+ m_videobank_select = 1;
+}
+
+
+
+/*************************************
+ *
+ * Banked graphics ROM access
+ *
+ *************************************/
+
+READ16_MEMBER(midtunit_video_device::midtunit_gfxrom_r)
+{
+ uint8_t *base = m_gfxrom->base() + m_gfxbank_offset[(offset >> 21) & 1];
+ offset = (offset & 0x01fffff) * 2;
+ return base[offset] | (base[offset + 1] << 8);
+}
+
+
+READ16_MEMBER(midwunit_video_device::midwunit_gfxrom_r)
+{
+ uint8_t *base = m_gfxrom->base() + m_gfxbank_offset[0];
+ offset *= 2;
+ return base[offset] | (base[offset + 1] << 8);
+}
+
+
+
+/*************************************
+ *
+ * Video RAM read/write
+ *
+ *************************************/
+
+WRITE16_MEMBER(midtunit_video_device::midtunit_vram_w)
+{
+ offset *= 2;
+ if (m_videobank_select)
+ {
+ if (ACCESSING_BITS_0_7)
+ m_local_videoram[offset] = (data & 0xff) | ((m_dma_register[DMA_PALETTE] & 0xff) << 8);
+ if (ACCESSING_BITS_8_15)
+ m_local_videoram[offset + 1] = ((data >> 8) & 0xff) | (m_dma_register[DMA_PALETTE] & 0xff00);
+ }
+ else
+ {
+ if (ACCESSING_BITS_0_7)
+ m_local_videoram[offset] = (m_local_videoram[offset] & 0xff) | ((data & 0xff) << 8);
+ if (ACCESSING_BITS_8_15)
+ m_local_videoram[offset + 1] = (m_local_videoram[offset + 1] & 0xff) | (data & 0xff00);
+ }
+}
+
+
+WRITE16_MEMBER(midtunit_video_device::midtunit_vram_data_w)
+{
+ offset *= 2;
+ if (ACCESSING_BITS_0_7)
+ m_local_videoram[offset] = (data & 0xff) | ((m_dma_register[DMA_PALETTE] & 0xff) << 8);
+ if (ACCESSING_BITS_8_15)
+ m_local_videoram[offset + 1] = ((data >> 8) & 0xff) | (m_dma_register[DMA_PALETTE] & 0xff00);
+}
+
+
+WRITE16_MEMBER(midtunit_video_device::midtunit_vram_color_w)
+{
+ offset *= 2;
+ if (ACCESSING_BITS_0_7)
+ m_local_videoram[offset] = (m_local_videoram[offset] & 0xff) | ((data & 0xff) << 8);
+ if (ACCESSING_BITS_8_15)
+ m_local_videoram[offset + 1] = (m_local_videoram[offset + 1] & 0xff) | (data & 0xff00);
+}
+
+
+READ16_MEMBER(midtunit_video_device::midtunit_vram_r)
+{
+ offset *= 2;
+ if (m_videobank_select)
+ return (m_local_videoram[offset] & 0x00ff) | (m_local_videoram[offset + 1] << 8);
+ else
+ return (m_local_videoram[offset] >> 8) | (m_local_videoram[offset + 1] & 0xff00);
+}
+
+
+READ16_MEMBER(midtunit_video_device::midtunit_vram_data_r)
+{
+ offset *= 2;
+ return (m_local_videoram[offset] & 0x00ff) | (m_local_videoram[offset + 1] << 8);
+}
+
+
+READ16_MEMBER(midtunit_video_device::midtunit_vram_color_r)
+{
+ offset *= 2;
+ return (m_local_videoram[offset] >> 8) | (m_local_videoram[offset + 1] & 0xff00);
+}
+
+
+
+/*************************************
+ *
+ * Shift register read/write
+ *
+ *************************************/
+
+TMS340X0_TO_SHIFTREG_CB_MEMBER(midtunit_video_device::to_shiftreg)
+{
+ memcpy(shiftreg, &m_local_videoram[address >> 3], 2 * 512 * sizeof(uint16_t));
+}
+
+
+TMS340X0_FROM_SHIFTREG_CB_MEMBER(midtunit_video_device::from_shiftreg)
+{
+ memcpy(&m_local_videoram[address >> 3], shiftreg, 2 * 512 * sizeof(uint16_t));
+}
+
+
+
+/*************************************
+ *
+ * Control register
+ *
+ *************************************/
+
+WRITE16_MEMBER(midtunit_video_device::midtunit_control_w)
+{
+ /*
+ other important bits:
+ bit 2 (0x0004) is toggled periodically
+ */
+ logerror("T-unit control = %04X\n", data);
+
+ COMBINE_DATA(&m_midtunit_control);
+
+ /* gfx bank select is bit 7 */
+ if (!(m_midtunit_control & 0x0080) || !m_gfx_rom_large)
+ m_gfxbank_offset[0] = 0x000000;
+ else
+ m_gfxbank_offset[0] = 0x800000;
+
+ /* video bank select is bit 5 */
+ m_videobank_select = (m_midtunit_control >> 5) & 1;
+}
+
+
+WRITE16_MEMBER(midwunit_video_device::midwunit_control_w)
+{
+ /*
+ other important bits:
+ bit 2 (0x0004) is toggled periodically
+ */
+ logerror("Wolf-unit control = %04X\n", data);
+
+ COMBINE_DATA(&m_midtunit_control);
+
+ /* gfx bank select is bits 8-9 */
+ m_gfxbank_offset[0] = 0x800000 * ((m_midtunit_control >> 8) & 3);
+
+ /* video bank select is unknown */
+ m_videobank_select = (m_midtunit_control >> 11) & 1;
+}
+
+
+READ16_MEMBER(midwunit_video_device::midwunit_control_r)
+{
+ return m_midtunit_control;
+}
+
+
+
+/*************************************
+ *
+ * Palette handlers
+ *
+ *************************************/
+
+WRITE16_MEMBER(midxunit_video_device::midxunit_paletteram_w)
+{
+ if (!(offset & 1))
+ m_palette->write16(offset / 2, data, mem_mask);
+}
+
+
+READ16_MEMBER(midxunit_video_device::midxunit_paletteram_r)
+{
+ return m_palette->read16(offset / 2);
+}
+
+
+
+/*************************************
+ *
+ * DMA drawing routines
+ *
+ *************************************/
+
+/*** fast pixel extractors ***/
+#if !defined(ALIGN_SHORTS) && defined(LSB_FIRST)
+#define EXTRACTGEN(m) ((*(uint16_t *)&base[o >> 3] >> (o & 7)) & (m))
+#elif defined(powerc)
+#define EXTRACTGEN(m) ((__lhbrx(base, o >> 3) >> (o & 7)) & (m))
+#else
+#define EXTRACTGEN(m) (((base[o >> 3] | (base[(o >> 3) + 1] << 8)) >> (o & 7)) & (m))
+#endif
+
+/*** core blitter routine macro ***/
+template <int BitsPerPixel, bool XFlip, bool Skip, bool Scale, midtunit_video_device::op_type_t Zero, midtunit_video_device::op_type_t NonZero>
+void midtunit_video_device::dma_draw()
+{
+ int height = m_dma_state.height << 8;
+ uint8_t *base = m_dma_state.gfxrom;
+ uint32_t offset = m_dma_state.offset;
+ uint16_t pal = m_dma_state.palette;
+ uint16_t color = pal | m_dma_state.color;
+ int sy = m_dma_state.ypos;
+ int iy = 0;
+ int ty;
+ int mask = (1 << BitsPerPixel) - 1;
+ int xstep = Scale ? m_dma_state.xstep : 0x100;
+
+ /* loop over the height */
+ while (iy < height)
+ {
+ int startskip = m_dma_state.startskip << 8;
+ int endskip = m_dma_state.endskip << 8;
+ int width = m_dma_state.width << 8;
+ int sx = m_dma_state.xpos;
+ int ix = 0;
+ int tx;
+ uint32_t o = offset;
+ int pre, post;
+ uint16_t *d;
+
+ /* handle skipping */
+ if (Skip)
+ {
+ uint8_t value = EXTRACTGEN(0xff);
+ o += 8;
+
+ /* adjust for preskip */
+ pre = (value & 0x0f) << (m_dma_state.preskip + 8);
+ tx = pre / xstep;
+ if (XFlip)
+ sx = (sx - tx) & XPOSMASK;
+ else
+ sx = (sx + tx) & XPOSMASK;
+ ix += tx * xstep;
+
+ /* adjust for postskip */
+ post = ((value >> 4) & 0x0f) << (m_dma_state.postskip + 8);
+ width -= post;
+ endskip -= post;
+ }
+
+ /* handle Y clipping */
+ if (sy < m_dma_state.topclip || sy > m_dma_state.botclip)
+ goto clipy;
+
+ /* handle start skip */
+ if (ix < startskip)
+ {
+ tx = ((startskip - ix) / xstep) * xstep;
+ ix += tx;
+ o += (tx >> 8) * BitsPerPixel;
+ }
+
+ /* handle end skip */
+ if ((width >> 8) > m_dma_state.width - m_dma_state.endskip)
+ width = (m_dma_state.width - m_dma_state.endskip) << 8;
+
+ /* determine destination pointer */
+#if DEBUG_MIDTUNIT_BLITTER
+ d = m_doing_debug_dma ? &m_debug_videoram[sy * 512] : &m_local_videoram[sy * 512];
+#else
+ d = &m_local_videoram[sy * 512];
+#endif
+
+ /* loop until we draw the entire width */
+ while (ix < width)
+ {
+ /* only process if not clipped */
+ if (sx >= m_dma_state.leftclip && sx <= m_dma_state.rightclip)
+ {
+ /* special case similar handling of zero/non-zero */
+ if (Zero == NonZero)
+ {
+ if (Zero == PIXEL_COLOR)
+ d[sx] = color;
+ else if (Zero == PIXEL_COPY)
+ d[sx] = (EXTRACTGEN(mask)) | pal;
+ }
+
+ /* otherwise, read the pixel and look */
+ else
+ {
+ int pixel = (EXTRACTGEN(mask));
+
+ /* non-zero pixel case */
+ if (pixel)
+ {
+ if (NonZero == PIXEL_COLOR)
+ d[sx] = color;
+ else if (NonZero == PIXEL_COPY)
+ d[sx] = pixel | pal;
+ }
+
+ /* zero pixel case */
+ else
+ {
+ if (Zero == PIXEL_COLOR)
+ d[sx] = color;
+ else if (Zero == PIXEL_COPY)
+ d[sx] = pal;
+ }
+ }
+ }
+
+ /* update pointers */
+ if (XFlip)
+ sx = (sx - 1) & XPOSMASK;
+ else
+ sx = (sx + 1) & XPOSMASK;
+
+ /* advance to the next pixel */
+ if (!Scale)
+ {
+ ix += 0x100;
+ o += BitsPerPixel;
+ }
+ else
+ {
+ tx = ix >> 8;
+ ix += xstep;
+ tx = (ix >> 8) - tx;
+ o += BitsPerPixel * tx;
+ }
+ }
+
+ clipy:
+ /* advance to the next row */
+ if (m_dma_state.yflip)
+ sy = (sy - 1) & YPOSMASK;
+ else
+ sy = (sy + 1) & YPOSMASK;
+ if (!Scale)
+ {
+ iy += 0x100;
+ width = m_dma_state.width;
+ if (Skip)
+ {
+ offset += 8;
+ width -= (pre + post) >> 8;
+ if (width > 0) offset += width * BitsPerPixel;
+ }
+ else
+ {
+ offset += width * BitsPerPixel;
+ }
+ }
+ else
+ {
+ ty = iy >> 8;
+ iy += m_dma_state.ystep;
+ ty = (iy >> 8) - ty;
+ if (!Skip)
+ {
+ offset += ty * m_dma_state.width * BitsPerPixel;
+ }
+ else if (ty--)
+ {
+ o = offset + 8;
+ width = m_dma_state.width - ((pre + post) >> 8);
+ if (width > 0) o += width * BitsPerPixel;
+ while (ty--)
+ {
+ uint8_t value = EXTRACTGEN(0xff);
+ o += 8;
+ pre = (value & 0x0f) << m_dma_state.preskip;
+ post = ((value >> 4) & 0x0f) << m_dma_state.postskip;
+ width = m_dma_state.width - pre - post;
+ if (width > 0) o += width * BitsPerPixel;
+ }
+ offset = o;
+ }
+ }
+ }
+}
+
+DEFINE_TEMPLATED_DMA_DRAW_GROUP(true, true);
+DEFINE_TEMPLATED_DMA_DRAW_GROUP(false, true);
+DEFINE_TEMPLATED_DMA_DRAW_GROUP(true, false);
+DEFINE_TEMPLATED_DMA_DRAW_GROUP(false, false);
+
+
+/*************************************
+ *
+ * DMA finished callback
+ *
+ *************************************/
+
+void midtunit_video_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_DMA:
+ m_dma_register[DMA_COMMAND] &= ~0x8000; /* tell the cpu we're done */
+ m_maincpu->set_input_line(0, ASSERT_LINE);
+ break;
+ default:
+ assert_always(false, "Unknown id in midtunit_video_device::device_timer");
+ }
+}
+
+
+
+/*************************************
+ *
+ * DMA reader
+ *
+ *************************************/
+
+READ16_MEMBER(midtunit_video_device::midtunit_dma_r)
+{
+ /* rmpgwt sometimes reads register 0, expecting it to return the */
+ /* current DMA status; thus we map register 0 to register 1 */
+ /* openice does it as well */
+ if (offset == 0)
+ offset = 1;
+ return m_dma_register[offset];
+}
+
+
+
+/*************************************
+ *
+ * DMA write handler
+ *
+ *************************************/
+
+/*
+ * DMA registers
+ * ------------------
+ *
+ * Register | Bit | Use
+ * ----------+-FEDCBA9876543210-+------------
+ * 0 | xxxxxxxx-------- | pixels to drop at the start of each row
+ * | --------xxxxxxxx | pixels to drop at the end of each row
+ * 1 | x--------------- | trigger write (or clear if zero)
+ * | -421------------ | image bpp (0=8)
+ * | ----84---------- | post skip size = (1<<x)
+ * | ------21-------- | pre skip size = (1<<x)
+ * | --------8------- | pre/post skip enable
+ * | ---------4------ | clipping enable
+ * | ----------2----- | flip y
+ * | -----------1---- | flip x
+ * | ------------8--- | blit nonzero pixels as color
+ * | -------------4-- | blit zero pixels as color
+ * | --------------2- | blit nonzero pixels
+ * | ---------------1 | blit zero pixels
+ * 2 | xxxxxxxxxxxxxxxx | source address low word
+ * 3 | xxxxxxxxxxxxxxxx | source address high word
+ * 4 | -------xxxxxxxxx | detination x
+ * 5 | -------xxxxxxxxx | destination y
+ * 6 | ------xxxxxxxxxx | image columns
+ * 7 | ------xxxxxxxxxx | image rows
+ * 8 | xxxxxxxxxxxxxxxx | palette
+ * 9 | xxxxxxxxxxxxxxxx | color
+ * 10 | ---xxxxxxxxxxxxx | scale x
+ * 11 | ---xxxxxxxxxxxxx | scale y
+ * 12 | -------xxxxxxxxx | top/left clip
+ * 13 | -------xxxxxxxxx | bottom/right clip
+ * 14 | ---------------- | test
+ * 15 | xxxxxxxx-------- | zero detect byte
+ * | --------8------- | extra page
+ * | ---------4------ | destination size
+ * | ----------2----- | select top/bottom or left/right for reg 12/13
+ */
+
+WRITE16_MEMBER(midtunit_video_device::midtunit_dma_w)
+{
+ static const uint8_t register_map[2][16] =
+ {
+ { 0,1,2,3,4,5,6,7,8,9,10,11,16,17,14,15 },
+ { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }
+ };
+ int regbank = (m_dma_register[DMA_CONFIG] >> 5) & 1;
+ int pixels = 0;
+
+ /* blend with the current register contents */
+ int regnum = register_map[regbank][offset];
+ COMBINE_DATA(&m_dma_register[regnum]);
+
+ /* only writes to DMA_COMMAND actually cause actions */
+ if (regnum != DMA_COMMAND)
+ return;
+
+ /* high bit triggers action */
+ int command = m_dma_register[DMA_COMMAND];
+ m_maincpu->set_input_line(0, CLEAR_LINE);
+ if (!(command & 0x8000))
+ return;
+
+ g_profiler.start(PROFILER_USER1);
+
+ /* determine bpp */
+ int bpp = (command >> 12) & 7;
+
+ /* fill in the basic data */
+ m_dma_state.xpos = m_dma_register[DMA_XSTART] & XPOSMASK;
+ m_dma_state.ypos = m_dma_register[DMA_YSTART] & YPOSMASK;
+ m_dma_state.width = m_dma_register[DMA_WIDTH] & 0x3ff;
+ m_dma_state.height = m_dma_register[DMA_HEIGHT] & 0x3ff;
+ m_dma_state.palette = m_dma_register[DMA_PALETTE] & 0x7f00;
+ m_dma_state.color = m_dma_register[DMA_COLOR] & 0xff;
+
+ /* fill in the rev 2 data */
+ m_dma_state.yflip = (command & 0x20) >> 5;
+ m_dma_state.preskip = (command >> 8) & 3;
+ m_dma_state.postskip = (command >> 10) & 3;
+ m_dma_state.xstep = m_dma_register[DMA_SCALE_X] ? m_dma_register[DMA_SCALE_X] : 0x100;
+ m_dma_state.ystep = m_dma_register[DMA_SCALE_Y] ? m_dma_register[DMA_SCALE_Y] : 0x100;
+
+ /* clip the clippers */
+ m_dma_state.topclip = m_dma_register[DMA_TOPCLIP] & 0x1ff;
+ m_dma_state.botclip = m_dma_register[DMA_BOTCLIP] & 0x1ff;
+ m_dma_state.leftclip = m_dma_register[DMA_LEFTCLIP] & 0x3ff;
+ m_dma_state.rightclip = m_dma_register[DMA_RIGHTCLIP] & 0x3ff;
+
+ /* determine the offset */
+ uint32_t gfxoffset = m_dma_register[DMA_OFFSETLO] | (m_dma_register[DMA_OFFSETHI] << 16);
+
+ /* special case: drawing mode C doesn't need to know about any pixel data */
+ if ((command & 0x0f) == 0x0c)
+ gfxoffset = 0;
+
+ /* determine the location */
+ if (!m_gfx_rom_large && gfxoffset >= 0x2000000)
+ gfxoffset -= 0x2000000;
+ if (gfxoffset >= 0xf8000000)
+ gfxoffset -= 0xf8000000;
+ if (gfxoffset < 0x10000000)
+ m_dma_state.offset = gfxoffset;
+ else
+ {
+ logerror("DMA source out of range: %08X\n", gfxoffset);
+ goto skipdma;
+ }
+
+ if (LOG_DMA || DEBUG_MIDTUNIT_BLITTER)
+ {
+ if (machine().input().code_pressed(KEYCODE_COLON))
+ {
+ logerror("DMA command %04X: (bpp=%d skip=%d xflip=%d yflip=%d preskip=%d postskip=%d)\n",
+ command, (command >> 12) & 7, (command >> 7) & 1, (command >> 4) & 1, (command >> 5) & 1, (command >> 8) & 3, (command >> 10) & 3);
+ logerror(" offset=%08X pos=(%d,%d) w=%d h=%d clip=(%d,%d)-(%d,%d)\n", gfxoffset, m_dma_state.xpos, m_dma_state.ypos,
+ m_dma_state.width, m_dma_state.height, m_dma_state.leftclip, m_dma_state.topclip, m_dma_state.rightclip, m_dma_state.botclip);
+ logerror(" palette=%04X color=%04X lskip=%02X rskip=%02X xstep=%04X ystep=%04X test=%04X config=%04X\n",
+ m_dma_register[DMA_PALETTE], m_dma_register[DMA_COLOR],
+ m_dma_register[DMA_LRSKIP] >> 8, m_dma_register[DMA_LRSKIP] & 0xff,
+ m_dma_register[DMA_SCALE_X], m_dma_register[DMA_SCALE_Y], m_dma_register[DMA_UNKNOWN_E],
+ m_dma_register[DMA_CONFIG]);
+ logerror("----\n");
+ }
+ }
+
+ /* there seems to be two types of behavior for the DMA chip */
+ /* for MK1 and MK2, the upper byte of the LRSKIP is the */
+ /* starting skip value, and the lower byte is the ending */
+ /* skip value; for the NBA Jam, Hangtime, and Open Ice, the */
+ /* full word seems to be the starting skip value. */
+ if (command & 0x40)
+ {
+ m_dma_state.startskip = m_dma_register[DMA_LRSKIP] & 0xff;
+ m_dma_state.endskip = m_dma_register[DMA_LRSKIP] >> 8;
+ }
+ else
+ {
+ m_dma_state.startskip = 0;
+ m_dma_state.endskip = m_dma_register[DMA_LRSKIP];
+ }
+
+ if (m_log_png)
+ {
+ if (command & 0x80)
+ {
+ log_bitmap(command, bpp ? bpp : 8, true);
+ }
+ else
+ {
+ log_bitmap(command, bpp ? bpp : 8, false);
+ }
+ }
+
+ /* then draw */
+ if (m_dma_state.xstep == 0x100 && m_dma_state.ystep == 0x100)
+ {
+ if (command & 0x80)
+ ((this)->*(m_dma_draw_skip_noscale[(command & 0x1f)*8 + bpp]))();
+ else
+ ((this)->*(m_dma_draw_noskip_noscale[(command & 0x1f)*8 + bpp]))();
+
+ pixels = m_dma_state.width * m_dma_state.height;
+ }
+ else
+ {
+ if (command & 0x80)
+ ((this)->*(m_dma_draw_skip_scale[(command & 0x1f)*8 + bpp]))();
+ else
+ ((this)->*(m_dma_draw_noskip_scale[(command & 0x1f)*8 + bpp]))();
+
+ if (m_dma_state.xstep && m_dma_state.ystep)
+ pixels = ((m_dma_state.width << 8) / m_dma_state.xstep) * ((m_dma_state.height << 8) / m_dma_state.ystep);
+ else
+ pixels = 0;
+ }
+
+ /* signal we're done */
+skipdma:
+ m_dma_timer->adjust(attotime::from_nsec(41 * pixels));
+
+ g_profiler.stop();
+}
+
+
+
+/*************************************
+ *
+ * Core refresh routine
+ *
+ *************************************/
+
+TMS340X0_SCANLINE_IND16_CB_MEMBER(midtunit_video_device::scanline_update)
+{
+ uint16_t *src = &m_local_videoram[(params->rowaddr << 9) & 0x3fe00];
+ uint16_t *dest = &bitmap.pix16(scanline);
+ int coladdr = params->coladdr << 1;
+
+ /* copy the non-blanked portions of this scanline */
+ for (int x = params->heblnk; x < params->hsblnk; x++)
+ dest[x] = src[coladdr++ & 0x1ff] & 0x7fff;
+}
+
+TMS340X0_SCANLINE_IND16_CB_MEMBER(midxunit_video_device::scanline_update)
+{
+ uint32_t fulladdr = ((params->rowaddr << 16) | params->coladdr) >> 3;
+ uint16_t *src = &m_local_videoram[fulladdr & 0x3fe00];
+ uint16_t *dest = &bitmap.pix16(scanline);
+
+ /* copy the non-blanked portions of this scanline */
+ for (int x = params->heblnk; x < params->hsblnk; x++)
+ dest[x] = src[fulladdr++ & 0x1ff] & 0x7fff;
+}
+
+void midtunit_video_device::log_bitmap(int command, int bpp, bool Skip)
+{
+ const uint32_t raw_offset = m_dma_register[DMA_OFFSETLO] | (m_dma_register[DMA_OFFSETHI] << 16);
+ if (m_logged_rom[raw_offset >> 6] & (1ULL << (raw_offset & 0x3f)))
+ return;
+
+ int Zero = PIXEL_SKIP;
+ int NonZero = PIXEL_SKIP;
+ switch (command & 0xf)
+ {
+ case 1: Zero = PIXEL_COPY; NonZero = PIXEL_SKIP; break;
+ case 2: Zero = PIXEL_SKIP; NonZero = PIXEL_COPY; break;
+ case 3: Zero = PIXEL_COPY; NonZero = PIXEL_COPY; break;
+ case 4: Zero = PIXEL_COLOR; NonZero = PIXEL_SKIP; break;
+ case 5: Zero = PIXEL_COLOR; NonZero = PIXEL_SKIP; break;
+ case 6: Zero = PIXEL_COLOR; NonZero = PIXEL_COPY; break;
+ case 7: Zero = PIXEL_COLOR; NonZero = PIXEL_COPY; break;
+ case 8: Zero = PIXEL_SKIP; NonZero = PIXEL_COLOR; break;
+ case 9: Zero = PIXEL_COPY; NonZero = PIXEL_COLOR; break;
+ case 10: Zero = PIXEL_SKIP; NonZero = PIXEL_COLOR; break;
+ case 11: Zero = PIXEL_COPY; NonZero = PIXEL_COLOR; break;
+ case 12: Zero = PIXEL_COLOR; NonZero = PIXEL_COLOR; break;
+ case 13: Zero = PIXEL_COLOR; NonZero = PIXEL_COLOR; break;
+ case 14: Zero = PIXEL_COLOR; NonZero = PIXEL_COLOR; break;
+ case 15: Zero = PIXEL_COLOR; NonZero = PIXEL_COLOR; break;
+ default: return;
+ }
+
+ emu_file file(m_log_path, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+
+ char name_buf[256];
+ snprintf(name_buf, 255, "0x%08x.png", raw_offset);
+ auto const filerr = file.open(name_buf);
+ if (filerr != osd_file::error::NONE)
+ {
+ return;
+ }
+
+ m_logged_rom[raw_offset >> 6] |= 1ULL << (raw_offset & 0x3f);
+
+ m_log_bitmap.allocate(m_dma_state.width, m_dma_state.height);
+ m_log_bitmap.fill(0);
+
+ uint8_t *base = m_dma_state.gfxrom;
+ uint32_t offset = m_dma_state.offset;
+ uint16_t pal = m_dma_state.palette;
+ uint16_t color = pal | m_dma_state.color;
+ int mask = (1 << bpp) - 1;
+
+ /* loop over the height */
+ for (int y = 0; y < m_dma_state.height; y++)
+ {
+ int startskip = m_dma_state.startskip;
+ int endskip = m_dma_state.endskip;
+ int width = m_dma_state.width;
+ int ix = 0;
+ int tx;
+ uint32_t o = offset;
+ int pre, post;
+
+ /* handle skipping */
+ if (Skip)
+ {
+ uint8_t value = EXTRACTGEN(0xff);
+ o += 8;
+
+ /* adjust for preskip */
+ pre = (value & 0x0f) << m_dma_state.preskip;
+ tx = pre;
+ ix += tx;
+
+ /* adjust for postskip */
+ post = ((value >> 4) & 0x0f) << m_dma_state.postskip;
+ width -= post;
+ endskip -= post;
+ }
+
+ /* handle start skip */
+ if (ix < startskip)
+ {
+ tx = (startskip - ix);
+ ix += tx;
+ o += tx * bpp;
+ }
+
+ /* handle end skip */
+ if (width > m_dma_state.width - m_dma_state.endskip)
+ width = m_dma_state.width - m_dma_state.endskip;
+
+ bitmap_rgb32::pixel_t *d = &m_log_bitmap.pix(y, ix);
+
+ /* determine destination pointer */
+
+ /* loop until we draw the entire width */
+ while (ix < width)
+ {
+ /* special case similar handling of zero/non-zero */
+ if (Zero == NonZero)
+ {
+ if (Zero == PIXEL_COLOR)
+ *d = m_palette->palette()->entry_list_raw()[color];
+ else if (Zero == PIXEL_COPY)
+ *d = m_palette->palette()->entry_list_raw()[(EXTRACTGEN(mask)) | pal];
+ }
+
+ /* otherwise, read the pixel and look */
+ else
+ {
+ int pixel = (EXTRACTGEN(mask));
+
+ /* non-zero pixel case */
+ if (pixel)
+ {
+ if (NonZero == PIXEL_COLOR)
+ *d = m_palette->palette()->entry_list_raw()[color];
+ else if (NonZero == PIXEL_COPY)
+ *d = m_palette->palette()->entry_list_raw()[pixel | pal];
+ }
+
+ /* zero pixel case */
+ else
+ {
+ if (Zero == PIXEL_COLOR)
+ *d = m_palette->palette()->entry_list_raw()[color];
+ else if (Zero == PIXEL_COPY)
+ *d = m_palette->palette()->entry_list_raw()[pal];
+ }
+ }
+
+ /* advance to the next pixel */
+ ix++;
+ d++;
+ o += bpp;
+ }
+
+ /* advance to the next row */
+ width = m_dma_state.width;
+ if (Skip)
+ {
+ offset += 8;
+ width -= pre + post;
+ if (width > 0) offset += width * bpp;
+ }
+ else
+ {
+ offset += width * bpp;
+ }
+ }
+
+ png_write_bitmap(file, nullptr, m_log_bitmap, 0, nullptr);
+#if 0
+ if (m_log_json)
+ {
+ char hex_buf[11];
+ rapidjson::StringBuffer s;
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(s);
+ emu_file json(m_log_path, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+
+ snprintf(name_buf, 255, "0x%08x.json", raw_offset);
+ auto const jsonerr = json.open(name_buf);
+ if (jsonerr != osd_file::error::NONE)
+ {
+ return;
+ }
+
+ writer.StartObject();
+ writer.Key("DMAState");
+ writer.StartObject();
+
+ sprintf(hex_buf, "0x%08x", raw_offset);
+ writer.Key("MemoryAddress");
+ writer.String(hex_buf);
+
+ sprintf(hex_buf, "0x%08x", m_dma_state.offset >> 3);
+ writer.Key("ROMSourceOffsetByte");
+ writer.String(hex_buf);
+
+ writer.Key("ROMSourceOffsetBit");
+ writer.Int(m_dma_state.offset & 7);
+
+ writer.Key("Size");
+ writer.StartArray();
+ writer.Int(m_dma_state.width);
+ writer.Int(m_dma_state.height);
+ writer.EndArray();
+
+ writer.Key("BitsPerPixel");
+ writer.Uint(bpp);
+
+ writer.Key("PaletteBank");
+ writer.Uint(m_dma_state.palette >> 8);
+
+ writer.Key("FGColor");
+ writer.Uint(m_dma_state.color);
+
+ writer.Key("YFlip");
+ writer.Bool(m_dma_state.yflip ? true : false);
+
+ writer.Key("PreSkipScale");
+ writer.Uint(m_dma_state.preskip);
+
+ writer.Key("PostSkipScale");
+ writer.Uint(m_dma_state.postskip);
+
+ writer.Key("RowSkipBits");
+ writer.Int(m_dma_state.rowbits);
+
+ writer.Key("StartPixelsToSkip");
+ writer.Int(m_dma_state.startskip);
+
+ writer.Key("EndPixelsToSkip");
+ writer.Int(m_dma_state.endskip);
+
+ writer.EndObject();
+ writer.EndObject();
+
+ json.puts(s.GetString());
+ json.close();
+ }
+#endif
+}