diff options
Diffstat (limited to 'docs/release/src/mame/video/midtunit.cpp')
-rw-r--r-- | docs/release/src/mame/video/midtunit.cpp | 1092 |
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> ¶ms) +{ + 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> ¶ms) +{ + 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> ¶ms) +{ + 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 +} |