From 9873d6afaa7f7d7889b707b66a1b6884b0049cbb Mon Sep 17 00:00:00 2001 From: David Haywood Date: Thu, 28 Mar 2019 02:40:35 +0000 Subject: [PATCH] spg110: tweaks for jak_capb, improves rendering, identifies several things (#4816) * spg110: jak_capb misc guesses (nw) * misc priority stuff (nw) * tweaks (nw) * more tweaks (nw) * small spg2xx refactor (nw) * some spg2xx refactoring (nw) * (nw) * tidy (nw) * these have all been moved out into modules (nw) * sprite work in progress (nw) * (nw) * spg110: shift some stuff around and split into files here too * (nw) * (nw) * (nw) * sprite improvements --- scripts/src/machine.lua | 6 + src/devices/machine/spg110.cpp | 567 ++-------------- src/devices/machine/spg110.h | 149 +---- src/devices/machine/spg110_video.cpp | 587 +++++++++++++++++ src/devices/machine/spg110_video.h | 159 +++++ src/devices/machine/spg2xx.cpp | 916 +------------------------- src/devices/machine/spg2xx.h | 126 +--- src/devices/machine/spg2xx_io.cpp | 2 +- src/devices/machine/spg2xx_sysdma.cpp | 117 ++++ src/devices/machine/spg2xx_sysdma.h | 46 ++ src/devices/machine/spg2xx_video.cpp | 828 +++++++++++++++++++++++ src/devices/machine/spg2xx_video.h | 137 ++++ src/mame/drivers/spg110.cpp | 6 +- 13 files changed, 1994 insertions(+), 1652 deletions(-) create mode 100644 src/devices/machine/spg110_video.cpp create mode 100644 src/devices/machine/spg110_video.h create mode 100644 src/devices/machine/spg2xx_sysdma.cpp create mode 100644 src/devices/machine/spg2xx_sysdma.h create mode 100644 src/devices/machine/spg2xx_video.cpp create mode 100644 src/devices/machine/spg2xx_video.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index fa451ae5dc6..2ec028ceb85 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -2626,8 +2626,14 @@ if (MACHINES["SPG2XX"]~=null) then MAME_DIR .. "src/devices/machine/spg2xx_audio.h", MAME_DIR .. "src/devices/machine/spg2xx_io.cpp", MAME_DIR .. "src/devices/machine/spg2xx_io.h", + MAME_DIR .. "src/devices/machine/spg2xx_sysdma.cpp", + MAME_DIR .. "src/devices/machine/spg2xx_sysdma.h", + MAME_DIR .. "src/devices/machine/spg2xx_video.cpp", + MAME_DIR .. "src/devices/machine/spg2xx_video.h", MAME_DIR .. "src/devices/machine/spg110.cpp", MAME_DIR .. "src/devices/machine/spg110.h", + MAME_DIR .. "src/devices/machine/spg110_video.cpp", + MAME_DIR .. "src/devices/machine/spg110_video.h", } end diff --git a/src/devices/machine/spg110.cpp b/src/devices/machine/spg110.cpp index 9f9256f4b27..ddb672233d9 100644 --- a/src/devices/machine/spg110.cpp +++ b/src/devices/machine/spg110.cpp @@ -18,14 +18,10 @@ DEFINE_DEVICE_TYPE(SPG110, spg110_device, "spg110", "SPG110 System-on-a-Chip") spg110_device::spg110_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock) - , device_memory_interface(mconfig, *this) - , m_space_config("spg110", ENDIANNESS_BIG, 16, 32, 0, address_map_constructor(FUNC(spg110_device::map_video), this)) , m_cpu(*this, finder_base::DUMMY_TAG) , m_screen(*this, finder_base::DUMMY_TAG) - , m_palette(*this, "palette") - , m_gfxdecode(*this, "gfxdecode") - , m_palram(*this, "palram") , m_spg_io(*this, "spg_io") + , m_spg_video(*this, "spg_video") , m_porta_out(*this) , m_portb_out(*this) , m_portc_out(*this) @@ -37,188 +33,13 @@ spg110_device::spg110_device(const machine_config &mconfig, device_type type, co { } -template -void spg110_device::blit(const rectangle &cliprect, uint32_t line, uint32_t xoff, uint32_t yoff, uint32_t attr, uint32_t ctrl, uint32_t bitmap_addr, uint16_t tile) + +spg110_device::spg110_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : spg110_device(mconfig, SPG110, tag, owner, clock) { - address_space &space = m_cpu->space(AS_PROGRAM); - - int32_t h = 8 << ((attr & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); - int32_t w = 8 << ((attr & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); - - uint32_t yflipmask = attr & TILE_Y_FLIP ? h - 1 : 0; - - uint32_t nc = ((attr & 0x0003) + 1) << 1; - - uint32_t palette_offset = (attr & 0x0f00) >> 4; - - palette_offset >>= nc; - palette_offset <<= nc; - - uint32_t bits_per_row = nc * w / 16; - uint32_t words_per_tile = bits_per_row * h; - uint32_t m = bitmap_addr + words_per_tile * tile + bits_per_row * (line ^ yflipmask); - uint32_t bits = 0; - uint32_t nbits = 0; - uint32_t y = line; - - int yy = (yoff + y) & 0x1ff; - if (yy >= 0x01c0) - yy -= 0x0200; - - if (yy > 240 || yy < 0) - return; - - int y_index = yy * 320; - - for (int32_t x = FlipX ? (w - 1) : 0; FlipX ? x >= 0 : x < w; FlipX ? x-- : x++) - { - int xx = xoff + x; - - bits <<= nc; - - if (nbits < nc) - { - uint16_t b = space.read_word(m++ & 0x3fffff); - //b = (b << 8) | (b >> 8); - bits |= b << (nc - nbits); - nbits += 16; - } - nbits -= nc; - - uint32_t pal = palette_offset + (bits >> 16); - bits &= 0xffff; - - xx &= 0x01ff; - if (xx >= 0x01c0) - xx -= 0x0200; - - if (xx >= 0 && xx < 320) - { - // TODO, this is completely wrong for this palette system - int pix_index = xx + y_index; - uint16_t rawpal = m_palram[pal]; - const pen_t *pens = m_palette->pens(); - uint32_t paldata = pens[pal]; - - if (!(rawpal & 0x8000)) - { - m_screenbuf[pix_index] = paldata; - } - } - } -} - -void spg110_device::blit_page(const rectangle &cliprect, uint32_t scanline, int depth, uint32_t bitmap_addr, uint16_t *regs) -{ - uint32_t xscroll = regs[0]; - uint32_t yscroll = regs[1]; - uint32_t attr = regs[2]; - uint32_t ctrl = regs[3]; - uint32_t tilemap = regs[4]; - uint32_t palette_map = regs[5]; - address_space &space2 = this->space(0); - - if (!(ctrl & PAGE_ENABLE_MASK)) - { - return; - } - - if (((attr & PAGE_DEPTH_FLAG_MASK) >> PAGE_DEPTH_FLAG_SHIFT) != depth) - { - return; - } - - uint32_t tile_h = 8 << ((attr & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); - uint32_t tile_w = 8 << ((attr & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); - - uint32_t tile_count_x = 512 / tile_w; - - uint32_t bitmap_y = (scanline + yscroll) & 0xff; - uint32_t y0 = bitmap_y / tile_h; - uint32_t tile_scanline = bitmap_y % tile_h; - uint32_t tile_address = tile_count_x * y0; - - for (uint32_t x0 = 0; x0 < tile_count_x; x0++, tile_address++) - { - uint32_t yy = ((tile_h * y0 - yscroll + 0x10) & 0xff) - 0x10; - uint32_t xx = (tile_w * x0 - xscroll) & 0x1ff; - uint16_t tile = (ctrl & PAGE_WALLPAPER_MASK) ? space2.read_word(tilemap*2) : space2.read_word((tilemap + tile_address)*2); - uint16_t palette = 0; - - if (!tile) - continue; - - palette = space2.read_word(palette_map + tile_address / 2); - if (x0 & 1) - palette = (palette & 0xff00) >> 8; - else - palette = (palette & 0x00ff); - - - bool flip_x = 0;//(tileattr & TILE_X_FLIP); - - if (flip_x) - blit(cliprect, tile_scanline, xx, yy, attr, ctrl, bitmap_addr, tile); - else - blit(cliprect, tile_scanline, xx, yy, attr, ctrl, bitmap_addr, tile); - - } } -/* correct, 4bpp gfxs */ -static const gfx_layout charlayout = -{ - 8,8, - RGN_FRAC(1,1), - 4, - { STEP4(0,1) }, - { 0*4,1*4,2*4,3*4,4*4,5*4,6*4,7*4 }, - { STEP8(0,4*8) }, - 8*8*4 -}; - -static const gfx_layout charlayout6 = -{ - 8,8, - RGN_FRAC(1,1), - 6, - { 0,1,2,3,4,5 }, - { STEP8(0,6) }, - { STEP8(0,6*8) }, - 8*8*6 -}; - -static const gfx_layout char16layout = -{ - 16,16, - RGN_FRAC(1,1), - 4, - { STEP4(0,1) }, - { 0*4,1*4,2*4,3*4,4*4,5*4,6*4,7*4, 8*4,9*4,10*4,11*4,12*4,13*4,14*4,15*4 }, - { STEP16(0,4*16) }, - 16*16*4 -}; - -static const gfx_layout char32layout = -{ - 32,32, - RGN_FRAC(1,1), - 4, - { STEP4(0,1) }, - { STEP32(0,4) }, - { STEP32(0,4*32) }, - 32*32*4 -}; - - - -static GFXDECODE_START( gfx ) - GFXDECODE_ENTRY( ":maincpu", 0, charlayout, 0, 16 ) - GFXDECODE_ENTRY( ":maincpu", 0, char16layout, 0, 16 ) - GFXDECODE_ENTRY( ":maincpu", 0, char32layout, 0, 16 ) - GFXDECODE_ENTRY( ":maincpu", 0, charlayout6, 0, 16 ) // correct for lots of the tiles inc. startup text -GFXDECODE_END void spg110_device::configure_spg_io(spg2xx_io_device* io) { @@ -241,129 +62,12 @@ void spg110_device::configure_spg_io(spg2xx_io_device* io) void spg110_device::device_add_mconfig(machine_config &config) { -// PALETTE(config, m_palette).set_format(palette_device::xRGB_555, 0x100); -// PALETTE(config, m_palette).set_format(palette_device::RGB_565, 0x100); -// PALETTE(config, m_palette).set_format(palette_device::IRGB_4444, 0x100); -// PALETTE(config, m_palette).set_format(palette_device::RGBI_4444, 0x100); -// PALETTE(config, m_palette).set_format(palette_device::xRGB_555, 0x100); - PALETTE(config, m_palette, palette_device::BLACK, 256); - - GFXDECODE(config, m_gfxdecode, m_palette, gfx); - SPG24X_IO(config, m_spg_io, DERIVED_CLOCK(1, 1), m_cpu, m_screen); configure_spg_io(m_spg_io); + SPG110_VIDEO(config, m_spg_video, DERIVED_CLOCK(1, 1), m_cpu, m_screen); } - -device_memory_interface::space_config_vector spg110_device::memory_space_config() const -{ - return space_config_vector { - std::make_pair(0, &m_space_config) - }; -} - -spg110_device::spg110_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : spg110_device(mconfig, SPG110, tag, owner, clock) -{ -} - -// irq source or similar? -READ16_MEMBER(spg110_device::spg110_2063_r) -{ - // checks for bits 0x20 and 0x08 in the IRQ function (all IRQs point to the same place) - return 0x0008; -} - -WRITE16_MEMBER(spg110_device::spg110_2063_w) -{ - // writes 0x28, probably clears the IRQ / IRQ sources? 0x63 is the same offset for this in spg2xx but bits used seem to be different - m_cpu->set_state_unsynced(UNSP_IRQ0_LINE, CLEAR_LINE); -} - - -WRITE16_MEMBER(spg110_device::spg110_201c_w) { } -WRITE16_MEMBER(spg110_device::spg110_2020_w) { } -WRITE16_MEMBER(spg110_device::spg110_2042_w) { } -WRITE16_MEMBER(spg110_device::spg110_2031_w) { } -WRITE16_MEMBER(spg110_device::spg110_2032_w) { } -WRITE16_MEMBER(spg110_device::spg110_2033_w) { } -WRITE16_MEMBER(spg110_device::spg110_2034_w) { } -WRITE16_MEMBER(spg110_device::spg110_2035_w) { } -WRITE16_MEMBER(spg110_device::spg110_2036_w) { COMBINE_DATA(&m_2036_scroll); } -WRITE16_MEMBER(spg110_device::spg110_2039_w) { } -WRITE16_MEMBER(spg110_device::spg110_2037_w) { } -WRITE16_MEMBER(spg110_device::spg110_203c_w) { } -WRITE16_MEMBER(spg110_device::spg110_203d_w) { } -WRITE16_MEMBER(spg110_device::spg110_2045_w) { } - - -WRITE16_MEMBER(spg110_device::spg110_2028_w) { } -WRITE16_MEMBER(spg110_device::spg110_2029_w) { } - -READ16_MEMBER(spg110_device::spg110_2028_r) { return 0x0000; } -READ16_MEMBER(spg110_device::spg110_2029_r) { return 0x0000; } - - -WRITE16_MEMBER(spg110_device::spg110_2050_w) { } -WRITE16_MEMBER(spg110_device::spg110_2051_w) { } -WRITE16_MEMBER(spg110_device::spg110_2052_w) { } -WRITE16_MEMBER(spg110_device::spg110_2053_w) { } -WRITE16_MEMBER(spg110_device::spg110_2054_w) { } -WRITE16_MEMBER(spg110_device::spg110_2055_w) { } -WRITE16_MEMBER(spg110_device::spg110_2056_w) { } -WRITE16_MEMBER(spg110_device::spg110_2057_w) { } -WRITE16_MEMBER(spg110_device::spg110_2058_w) { } -WRITE16_MEMBER(spg110_device::spg110_2059_w) { } -WRITE16_MEMBER(spg110_device::spg110_205a_w) { } -WRITE16_MEMBER(spg110_device::spg110_205b_w) { } -WRITE16_MEMBER(spg110_device::spg110_205c_w) { } -WRITE16_MEMBER(spg110_device::spg110_205d_w) { } -WRITE16_MEMBER(spg110_device::spg110_205e_w) { } -WRITE16_MEMBER(spg110_device::spg110_205f_w) { } - -WRITE16_MEMBER(spg110_device::dma_unk_2061_w) { COMBINE_DATA(&m_dma_unk_2061); } -WRITE16_MEMBER(spg110_device::dma_dst_step_w) { COMBINE_DATA(&m_dma_dst_step); } -WRITE16_MEMBER(spg110_device::dma_unk_2067_w) { COMBINE_DATA(&m_dma_unk_2067); } -WRITE16_MEMBER(spg110_device::dma_src_step_w) { COMBINE_DATA(&m_dma_src_step); } - -WRITE16_MEMBER(spg110_device::dma_dst_w) { COMBINE_DATA(&m_dma_dst); } -WRITE16_MEMBER(spg110_device::dma_src_w) { COMBINE_DATA(&m_dma_src); } - -WRITE16_MEMBER(spg110_device::dma_len_trigger_w) -{ - int length = data & 0x1fff; - - // this is presumably a counter that underflows to 0x1fff, because that's what the wait loop waits for? - logerror("%s: (trigger len) %04x with values (unk) %04x (dststep) %04x (unk) %04x (src step) %04x | (dst) %04x (src) %04x\n", machine().describe_context(), data, m_dma_unk_2061, m_dma_dst_step, m_dma_unk_2067, m_dma_src_step, m_dma_dst, m_dma_src); - - if ((m_dma_unk_2061!=0x0000) || (m_dma_unk_2067 != 0x0000)) - fatalerror("unknown DMA params are not zero!\n"); - - int source = m_dma_src; - int dest = m_dma_dst; - - for (int i = 0; i < length; i++) - { - address_space &mem = m_cpu->space(AS_PROGRAM); - uint16_t val = mem.read_word(source); - - this->space(0).write_word(dest * 2, val, 0xffff); - - source+=m_dma_src_step; - dest+=m_dma_dst_step; - } -} - -READ16_MEMBER(spg110_device::dma_len_status_r) -{ - return 0x1fff; // DMA related? -} - -READ16_MEMBER(spg110_device::spg110_2037_r) { return 0x0000; } -READ16_MEMBER(spg110_device::spg110_2042_r) { return 0x0000; } - - WRITE16_MEMBER(spg110_device::spg110_3100_w) { } WRITE16_MEMBER(spg110_device::spg110_3101_w) { } WRITE16_MEMBER(spg110_device::spg110_3102_w) { } @@ -379,128 +83,58 @@ WRITE16_MEMBER(spg110_device::spg110_310d_w) { } READ16_MEMBER(spg110_device::spg110_310f_r) { return 0x0000; } -READ16_MEMBER(spg110_device::tmap0_regs_r) { return tmap0_regs[offset]; } -READ16_MEMBER(spg110_device::tmap1_regs_r) { return tmap1_regs[offset]; } - -void spg110_device::tilemap_write_regs(int which, uint16_t* regs, int regno, uint16_t data) -{ - switch (regno) - { - case 0x0: // Page X scroll - logerror("video_w: Page %d X Scroll = %04x\n", which, data & 0x01ff); - regs[regno] = data & 0x01ff; - break; - - case 0x1: // Page Y scroll - logerror("video_w: Page %d Y Scroll = %04x\n", which, data & 0x00ff); - regs[regno] = data & 0x00ff; - break; - - case 0x2: // Page Attributes - // 'depth' (aka z value) can't be depth here as it is on spg2xx, or the scores in attract will be behind the table, it really seems to be per attribute bit instead - - logerror("video_w: Page %d Attributes = %04x (Depth:%d, Palette:%d, VSize:%d, HSize:%d, FlipY:%d, FlipX:%d, BPP:%d)\n", which, data - , (data >> 12) & 3, (data >> 8) & 15, 8 << ((data >> 6) & 3), 8 << ((data >> 4) & 3), BIT(data, 3), BIT(data, 2), 2 * ((data & 3) + 1)); - regs[regno] = data; - break; - - case 0x3: // Page Control - logerror("video_w: Page %d Control = %04x (Blend:%d, HiColor:%d, RowScroll:%d, Enable:%d, Wallpaper:%d, RegSet:%d, Bitmap:%d)\n", which, data - , BIT(data, 8), BIT(data, 7), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0)); - regs[regno] = data; - break; - - case 0x4: // Page Tile Address - logerror("video_w: Page %d Tile Address = %04x\n", which, data); - regs[regno] = data; - break; - - case 0x5: // Page Attribute Address - logerror("video_w: Page %d Attribute Address = %04x\n", which, data); - regs[regno] = data; - break; - } -} - - -WRITE16_MEMBER(spg110_device::tmap0_regs_w) -{ - tilemap_write_regs(0, tmap0_regs,offset,data); -} - - -WRITE16_MEMBER(spg110_device::tmap1_regs_w) -{ - tilemap_write_regs(1, tmap1_regs,offset,data); -} - void spg110_device::map(address_map &map) { map(0x000000, 0x000fff).ram(); - // vregs are at 2000? - map(0x002010, 0x002015).rw(FUNC(spg110_device::tmap0_regs_r), FUNC(spg110_device::tmap0_regs_w)); - map(0x002016, 0x00201b).rw(FUNC(spg110_device::tmap1_regs_r), FUNC(spg110_device::tmap1_regs_w)); + map(0x002010, 0x002015).rw(m_spg_video, FUNC(spg110_video_device::tmap0_regs_r), FUNC(spg110_video_device::tmap0_regs_w)); + map(0x002016, 0x00201b).rw(m_spg_video, FUNC(spg110_video_device::tmap1_regs_r), FUNC(spg110_video_device::tmap1_regs_w)); - map(0x00201c, 0x00201c).w(FUNC(spg110_device::spg110_201c_w)); +#if 0 // more vregs? + map(0x00201c, 0x00201c).w(m_spg_video, FUNC(spg110_video_device::spg110_201c_w)); - map(0x002020, 0x002020).w(FUNC(spg110_device::spg110_2020_w)); + map(0x002020, 0x002020).w(m_spg_video, FUNC(spg110_video_device::spg110_2020_w)); - map(0x002028, 0x002028).rw(FUNC(spg110_device::spg110_2028_r), FUNC(spg110_device::spg110_2028_w)); - map(0x002029, 0x002029).rw(FUNC(spg110_device::spg110_2029_r), FUNC(spg110_device::spg110_2029_w)); + map(0x002028, 0x002028).rw(m_spg_video, FUNC(spg110_video_device::spg110_2028_r), FUNC(spg110_video_device::spg110_2028_w)); + map(0x002029, 0x002029).rw(m_spg_video, FUNC(spg110_video_device::spg110_2029_r), FUNC(spg110_video_device::spg110_2029_w)); - map(0x002031, 0x002031).w(FUNC(spg110_device::spg110_2031_w)); // sometimes 14a? - map(0x002032, 0x002032).w(FUNC(spg110_device::spg110_2032_w)); // always 14a? - map(0x002033, 0x002033).w(FUNC(spg110_device::spg110_2033_w)); - map(0x002034, 0x002034).w(FUNC(spg110_device::spg110_2034_w)); - map(0x002035, 0x002035).w(FUNC(spg110_device::spg110_2035_w)); - map(0x002036, 0x002036).w(FUNC(spg110_device::spg110_2036_w)); // possible scroll register? - map(0x002037, 0x002037).rw(FUNC(spg110_device::spg110_2037_r), FUNC(spg110_device::spg110_2037_w)); + map(0x002031, 0x002031).w(m_spg_video, FUNC(spg110_video_device::spg110_2031_w)); // sometimes 14a? + map(0x002032, 0x002032).w(m_spg_video, FUNC(spg110_video_device::spg110_2032_w)); // always 14a? + map(0x002033, 0x002033).w(m_spg_video, FUNC(spg110_video_device::spg110_2033_w)); + map(0x002034, 0x002034).w(m_spg_video, FUNC(spg110_video_device::spg110_2034_w)); + map(0x002035, 0x002035).w(m_spg_video, FUNC(spg110_video_device::spg110_2035_w)); + map(0x002036, 0x002036).w(m_spg_video, FUNC(spg110_video_device::spg110_2036_w)); // possible scroll register? + map(0x002037, 0x002037).rw(m_spg_video, FUNC(spg110_video_device::spg110_2037_r), FUNC(spg110_video_device::spg110_2037_w)); - map(0x002039, 0x002039).w(FUNC(spg110_device::spg110_2039_w)); + map(0x002039, 0x002039).w(m_spg_video, FUNC(spg110_video_device::spg110_2039_w)); - map(0x00203c, 0x00203c).w(FUNC(spg110_device::spg110_203c_w)); + map(0x00203c, 0x00203c).w(m_spg_video, FUNC(spg110_video_device::spg110_203c_w)); - map(0x00203d, 0x00203d).w(FUNC(spg110_device::spg110_203d_w)); // possible scroll register? + map(0x00203d, 0x00203d).w(m_spg_video, FUNC(spg110_video_device::spg110_203d_w)); // possible scroll register? - map(0x002042, 0x002042).rw(FUNC(spg110_device::spg110_2042_r),FUNC(spg110_device::spg110_2042_w)); + map(0x002042, 0x002042).rw(m_spg_video, FUNC(spg110_video_device::spg110_2042_r),FUNC(spg110_video_device::spg110_2042_w)); - map(0x002045, 0x002045).w(FUNC(spg110_device::spg110_2045_w)); + map(0x002045, 0x002045).w(m_spg_video, FUNC(spg110_video_device::spg110_2045_w)); +#endif - // seems to be 16 entries for.. something? - map(0x002050, 0x002050).w(FUNC(spg110_device::spg110_2050_w)); - map(0x002051, 0x002051).w(FUNC(spg110_device::spg110_2051_w)); - map(0x002052, 0x002052).w(FUNC(spg110_device::spg110_2052_w)); - map(0x002053, 0x002053).w(FUNC(spg110_device::spg110_2053_w)); - map(0x002054, 0x002054).w(FUNC(spg110_device::spg110_2054_w)); - map(0x002055, 0x002055).w(FUNC(spg110_device::spg110_2055_w)); - map(0x002056, 0x002056).w(FUNC(spg110_device::spg110_2056_w)); - map(0x002057, 0x002057).w(FUNC(spg110_device::spg110_2057_w)); - map(0x002058, 0x002058).w(FUNC(spg110_device::spg110_2058_w)); - map(0x002059, 0x002059).w(FUNC(spg110_device::spg110_2059_w)); - map(0x00205a, 0x00205a).w(FUNC(spg110_device::spg110_205a_w)); - map(0x00205b, 0x00205b).w(FUNC(spg110_device::spg110_205b_w)); - map(0x00205c, 0x00205c).w(FUNC(spg110_device::spg110_205c_w)); - map(0x00205d, 0x00205d).w(FUNC(spg110_device::spg110_205d_w)); - map(0x00205e, 0x00205e).w(FUNC(spg110_device::spg110_205e_w)); - map(0x00205f, 0x00205f).w(FUNC(spg110_device::spg110_205f_w)); - - //map(0x002010, 0x00205f).ram(); + // seems to be 16 entries for.. something? on jak_capb these seem connected to the palette DMA operations, 0x2050 for 0x8000, 0x2051 for 0x8020, 0x2052 for 0x8040 etc. maybe 1 bit per pen? + map(0x002050, 0x00205f).ram().w(m_spg_video, FUNC(spg110_video_device::spg110_205x_w)).share("spg_video:palctrlram"); // everything (dma? and interrupt flag?!) - map(0x002060, 0x002060).w(FUNC(spg110_device::dma_dst_w)); - map(0x002061, 0x002061).w(FUNC(spg110_device::dma_unk_2061_w)); - map(0x002062, 0x002062).rw(FUNC(spg110_device::dma_len_status_r),FUNC(spg110_device::dma_len_trigger_w)); - map(0x002063, 0x002063).rw(FUNC(spg110_device::spg110_2063_r),FUNC(spg110_device::spg110_2063_w)); // this looks like interrupt stuff and is checked in the irq like an irq source, but why in the middle of what otherwise look like some kind of DMA? - map(0x002064, 0x002064).w(FUNC(spg110_device::dma_dst_step_w)); - map(0x002066, 0x002066).w(FUNC(spg110_device::dma_src_w)); - map(0x002067, 0x002067).w(FUNC(spg110_device::dma_unk_2067_w)); - map(0x002068, 0x002068).w(FUNC(spg110_device::dma_src_step_w)); + map(0x002060, 0x002060).w(m_spg_video, FUNC(spg110_video_device::dma_dst_w)); + map(0x002061, 0x002061).w(m_spg_video, FUNC(spg110_video_device::dma_unk_2061_w)); + map(0x002062, 0x002062).rw(m_spg_video, FUNC(spg110_video_device::dma_len_status_r),FUNC(spg110_video_device::dma_len_trigger_w)); + map(0x002063, 0x002063).rw(m_spg_video, FUNC(spg110_video_device::spg110_2063_r),FUNC(spg110_video_device::spg110_2063_w)); // Video IRQ source / ack (3 different things checked here instead of 2 on spg2xx?) + map(0x002064, 0x002064).w(m_spg_video, FUNC(spg110_video_device::dma_dst_step_w)); + map(0x002066, 0x002066).w(m_spg_video, FUNC(spg110_video_device::dma_src_w)); + map(0x002067, 0x002067).w(m_spg_video, FUNC(spg110_video_device::dma_unk_2067_w)); + map(0x002068, 0x002068).w(m_spg_video, FUNC(spg110_video_device::dma_src_step_w)); - map(0x002200, 0x0022ff).ram(); // looks like per-pen brightness or similar? strange because palette isn't memory mapped here + map(0x002200, 0x0022ff).ram(); // looks like per-pen brightness or similar? strange because palette isn't memory mapped here (maybe rowscroll?) - map(0x003000, 0x00307f).ram(); // sound registers? seems to be 8 long entries, only uses up to 0x7f? +#if 1 // sound registers? seems to be 8 long entries, only uses up to 0x7f? (register mapping seems similar to spg2xx, maybe with less channels?) + map(0x003000, 0x00307f).ram(); map(0x003080, 0x0030ff).ram(); map(0x003100, 0x003100).w(FUNC(spg110_device::spg110_3100_w)); @@ -519,46 +153,15 @@ void spg110_device::map(address_map &map) map(0x00310d, 0x00310d).w(FUNC(spg110_device::spg110_310d_w)); map(0x00310f, 0x00310f).r(FUNC(spg110_device::spg110_310f_r)); +#endif // 0032xx looks like it could be the same as 003d00 on spg2xx map(0x003200, 0x00322f).rw(m_spg_io, FUNC(spg2xx_io_device::io_r), FUNC(spg2xx_io_device::io_w)); } -// this seems to be a different, non-cpu mapped space only accessible via the DMA? -void spg110_device::map_video(address_map &map) -{ - // are these addresses hardcoded, or can they move (in which case tilemap system isn't really suitable) - map(0x00000, 0x03fff).ram(); // 2fff? - - map(0x04000, 0x04fff).ram(); // seems to be 3 blocks, almost certainly spritelist - -// map(0x08000, 0x081ff).ram().w(m_palette, FUNC(palette_device::write16)).share("palette"); // probably? format unknown tho - map(0x08000, 0x081ff).ram().share("palram"); -} - - -/* -TIMER_CALLBACK_MEMBER(spg110_device::test_timer) -{ - // -} -*/ - - void spg110_device::device_start() { -// m_test_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(spg110_device::test_timer), this)); - save_item(NAME(m_dma_src_step)); - save_item(NAME(m_dma_dst_step)); - save_item(NAME(m_dma_unk_2061)); - save_item(NAME(m_dma_unk_2067)); - save_item(NAME(m_dma_dst)); - save_item(NAME(m_dma_src)); - save_item(NAME(m_bg_scrollx)); - save_item(NAME(m_bg_scrolly)); - save_item(NAME(m_2036_scroll)); - m_porta_out.resolve_safe(); m_portb_out.resolve_safe(); m_portc_out.resolve_safe(); @@ -573,97 +176,5 @@ void spg110_device::device_start() void spg110_device::device_reset() { - m_dma_src_step = 0; - m_dma_dst_step = 0; - m_dma_unk_2061 = 0; - m_dma_unk_2067 = 0; - m_dma_dst = 0; - m_dma_src = 0; - m_bg_scrollx = 0; - m_bg_scrolly = 0; - m_2036_scroll = 0; } -double spg110_device::hue2rgb(double p, double q, double t) -{ - if (t < 0) t += 1; - if (t > 1) t -= 1; - if (t < 1 / 6.0f) return p + (q - p) * 6 * t; - if (t < 1 / 2.0f) return q; - if (t < 2 / 3.0f) return p + (q - p) * (2 / 3.0f - t) * 6; - return p; -} - -uint32_t spg110_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) -{ - // Palette, this is still wrong! - int offs = 0; - for (int index = 0;index < 256; index++) - { - uint16_t dat = m_palram[offs++]; - - // llll lsss sshh hhhh - int l_raw = (dat & 0xf800) >> 11; - int sl_raw = (dat & 0x07c0) >> 6; - int h_raw = (dat & 0x003f) >> 0; - - double l = (double)l_raw / 31.0f; - double s = (double)sl_raw / 31.0f; - double h = (double)h_raw / 47.0f; - - double r, g, b; - - if (s == 0) { - r = g = b = l; // greyscale - } else { - double q = l < 0.5f ? l * (1 + s) : l + s - l * s; - double p = 2 * l - q; - r = hue2rgb(p, q, h + 1/3.0f); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1/3.0f); - } - - int r_real = r * 255.0f; - int g_real = g * 255.0f; - int b_real = b * 255.0f; - - m_palette->set_pen_color(index, r_real, g_real, b_real); - } - - memset(&m_screenbuf[320 * cliprect.min_y], 0, 4 * 320 * ((cliprect.max_y - cliprect.min_y) + 1)); - - const uint32_t page1_addr = 0;//0x40 * m_video_regs[0x20]; - const uint32_t page2_addr = 0;//0x40 * m_video_regs[0x21]; - uint16_t *page1_regs = tmap0_regs; - uint16_t *page2_regs = tmap1_regs; - - for (uint32_t scanline = (uint32_t)cliprect.min_y; scanline <= (uint32_t)cliprect.max_y; scanline++) - { - for (int i = 0; i < 4; i++) - { - blit_page(cliprect, scanline, i, page2_addr, page2_regs); - blit_page(cliprect, scanline, i, page1_addr, page1_regs); - //blit_sprites(cliprect, scanline, i); - } - } - - for (int y = cliprect.min_y; y <= cliprect.max_y; y++) - { - uint32_t *dest = &bitmap.pix32(y, cliprect.min_x); - uint32_t *src = &m_screenbuf[cliprect.min_x + 320 * y]; - memcpy(dest, src, sizeof(uint32_t) * ((cliprect.max_x - cliprect.min_x) + 1)); - } - - return 0; -} - -WRITE_LINE_MEMBER(spg110_device::vblank) -{ - if (!state) - { - m_cpu->set_state_unsynced(UNSP_IRQ0_LINE, ASSERT_LINE); - // m_test_timer->adjust(attotime::from_usec(100), 0); - } - - return; -} diff --git a/src/devices/machine/spg110.h b/src/devices/machine/spg110.h index 64464e1ba9d..1cdfc2e19e9 100644 --- a/src/devices/machine/spg110.h +++ b/src/devices/machine/spg110.h @@ -10,9 +10,9 @@ #include "cpu/unsp/unsp.h" #include "emupal.h" #include "spg2xx_io.h" +#include "spg110_video.h" - -class spg110_device : public device_t, public device_memory_interface +class spg110_device : public device_t { public: @@ -28,11 +28,6 @@ public: } void map(address_map &map); - void map_video(address_map &map); - - double hue2rgb(double p, double q, double t); - uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - DECLARE_WRITE_LINE_MEMBER(vblank); auto porta_out() { return m_porta_out.bind(); } auto portb_out() { return m_portb_out.bind(); } @@ -45,107 +40,22 @@ public: auto chip_select() { return m_chip_sel.bind(); } + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { return m_spg_video->screen_update(screen,bitmap,cliprect); } + DECLARE_WRITE_LINE_MEMBER(vblank) { m_spg_video->vblank(state); } + protected: virtual void device_start() override; virtual void device_reset() override; virtual void device_add_mconfig(machine_config &config) override; - virtual space_config_vector memory_space_config() const override; - - address_space_config m_space_config; - private: - enum - { - PAGE_ENABLE_MASK = 0x0008, - PAGE_WALLPAPER_MASK = 0x0004, - - PAGE_DEPTH_FLAG_MASK = 0x3000, - PAGE_DEPTH_FLAG_SHIFT = 12, - PAGE_TILE_HEIGHT_MASK = 0x00c0, - PAGE_TILE_HEIGHT_SHIFT = 6, - PAGE_TILE_WIDTH_MASK = 0x0030, - PAGE_TILE_WIDTH_SHIFT = 4, - - TILE_X_FLIP = 0x0004, - TILE_Y_FLIP = 0x0008 - }; - - enum flipx_t : bool - { - FlipXOff = false, - FlipXOn = true - }; required_device m_cpu; required_device m_screen; - required_device m_palette; - required_device m_gfxdecode; - required_shared_ptr m_palram; + required_device m_spg_io; - - //TIMER_CALLBACK_MEMBER(test_timer); - //emu_timer *m_test_timer; - - - DECLARE_WRITE16_MEMBER(spg110_201c_w); - DECLARE_WRITE16_MEMBER(spg110_2020_w); - - DECLARE_WRITE16_MEMBER(spg110_2028_w); - DECLARE_WRITE16_MEMBER(spg110_2029_w); - - DECLARE_READ16_MEMBER(spg110_2028_r); - DECLARE_READ16_MEMBER(spg110_2029_r); - - DECLARE_WRITE16_MEMBER(spg110_2031_w); - DECLARE_WRITE16_MEMBER(spg110_2032_w); - DECLARE_WRITE16_MEMBER(spg110_2033_w); - DECLARE_WRITE16_MEMBER(spg110_2034_w); - DECLARE_WRITE16_MEMBER(spg110_2035_w); - DECLARE_WRITE16_MEMBER(spg110_2036_w); - DECLARE_WRITE16_MEMBER(spg110_2037_w); - DECLARE_WRITE16_MEMBER(spg110_2039_w); - - DECLARE_WRITE16_MEMBER(spg110_203c_w); - DECLARE_WRITE16_MEMBER(spg110_203d_w); - - DECLARE_WRITE16_MEMBER(spg110_2042_w); - - DECLARE_WRITE16_MEMBER(spg110_2045_w); - - DECLARE_WRITE16_MEMBER(spg110_2050_w); - DECLARE_WRITE16_MEMBER(spg110_2051_w); - DECLARE_WRITE16_MEMBER(spg110_2052_w); - DECLARE_WRITE16_MEMBER(spg110_2053_w); - DECLARE_WRITE16_MEMBER(spg110_2054_w); - DECLARE_WRITE16_MEMBER(spg110_2055_w); - DECLARE_WRITE16_MEMBER(spg110_2056_w); - DECLARE_WRITE16_MEMBER(spg110_2057_w); - DECLARE_WRITE16_MEMBER(spg110_2058_w); - DECLARE_WRITE16_MEMBER(spg110_2059_w); - DECLARE_WRITE16_MEMBER(spg110_205a_w); - DECLARE_WRITE16_MEMBER(spg110_205b_w); - DECLARE_WRITE16_MEMBER(spg110_205c_w); - DECLARE_WRITE16_MEMBER(spg110_205d_w); - DECLARE_WRITE16_MEMBER(spg110_205e_w); - DECLARE_WRITE16_MEMBER(spg110_205f_w); - - - DECLARE_READ16_MEMBER(spg110_2037_r); - DECLARE_READ16_MEMBER(spg110_2042_r); - - DECLARE_WRITE16_MEMBER(dma_dst_w); - DECLARE_WRITE16_MEMBER(dma_unk_2061_w); - DECLARE_WRITE16_MEMBER(dma_len_trigger_w); - DECLARE_WRITE16_MEMBER(spg110_2063_w); - DECLARE_WRITE16_MEMBER(dma_dst_step_w); - DECLARE_WRITE16_MEMBER(dma_src_w); - DECLARE_WRITE16_MEMBER(dma_unk_2067_w); - DECLARE_WRITE16_MEMBER(dma_src_step_w); - - DECLARE_READ16_MEMBER(dma_len_status_r); - DECLARE_READ16_MEMBER(spg110_2063_r); + required_device m_spg_video; DECLARE_WRITE16_MEMBER(spg110_3100_w); @@ -164,33 +74,6 @@ private: DECLARE_READ16_MEMBER(spg110_310f_r); - DECLARE_READ16_MEMBER(tmap0_regs_r); - DECLARE_READ16_MEMBER(tmap1_regs_r); - DECLARE_WRITE16_MEMBER(tmap0_regs_w); - DECLARE_WRITE16_MEMBER(tmap1_regs_w); - - uint16_t tmap0_regs[0x6]; - uint16_t tmap1_regs[0x6]; - - uint16_t m_dma_src_step; - uint16_t m_dma_dst_step; - uint16_t m_dma_unk_2061; - uint16_t m_dma_unk_2067; - - uint16_t m_dma_dst; - uint16_t m_dma_src; - - uint16_t m_bg_scrollx; - uint16_t m_bg_scrolly; - uint16_t m_2036_scroll; - - void tilemap_write_regs(int which, uint16_t* regs, int regno, uint16_t data); - - template - void blit(const rectangle &cliprect, uint32_t line, uint32_t xoff, uint32_t yoff, uint32_t attr, uint32_t ctrl, uint32_t bitmap_addr, uint16_t tile); - void blit_page(const rectangle &cliprect, uint32_t scanline, int depth, uint32_t bitmap_addr, uint16_t *regs); - uint32_t m_screenbuf[320 * 240]; - devcb_write16 m_porta_out; devcb_write16 m_portb_out; devcb_write16 m_portc_out; @@ -202,15 +85,15 @@ private: devcb_write8 m_chip_sel; - DECLARE_READ16_MEMBER(porta_r) { return m_porta_in(); }; - DECLARE_READ16_MEMBER(portb_r) { return m_portb_in(); }; - DECLARE_READ16_MEMBER(portc_r) { return m_portc_in(); }; - DECLARE_WRITE16_MEMBER(porta_w) { m_porta_out(offset, data, mem_mask); }; - DECLARE_WRITE16_MEMBER(portb_w) { m_portb_out(offset, data, mem_mask); }; - DECLARE_WRITE16_MEMBER(portc_w) { m_portc_out(offset, data, mem_mask); }; - template DECLARE_READ16_MEMBER(adc_r) { return m_adc_in[Line](); }; - DECLARE_WRITE8_MEMBER(cs_w) { m_chip_sel(offset, data, mem_mask); }; - DECLARE_READ16_MEMBER(get_pal_r) { return 0; /*m_pal_flag;*/ }; + DECLARE_READ16_MEMBER(porta_r) { return m_porta_in(); } + DECLARE_READ16_MEMBER(portb_r) { return m_portb_in(); } + DECLARE_READ16_MEMBER(portc_r) { return m_portc_in(); } + DECLARE_WRITE16_MEMBER(porta_w) { m_porta_out(offset, data, mem_mask); } + DECLARE_WRITE16_MEMBER(portb_w) { m_portb_out(offset, data, mem_mask); } + DECLARE_WRITE16_MEMBER(portc_w) { m_portc_out(offset, data, mem_mask); } + template DECLARE_READ16_MEMBER(adc_r) { return m_adc_in[Line](); } + DECLARE_WRITE8_MEMBER(cs_w) { m_chip_sel(offset, data, mem_mask); } + DECLARE_READ16_MEMBER(get_pal_r) { return 0; /*m_pal_flag;*/ } void configure_spg_io(spg2xx_io_device* io); }; diff --git a/src/devices/machine/spg110_video.cpp b/src/devices/machine/spg110_video.cpp new file mode 100644 index 00000000000..ea5b2dfcb11 --- /dev/null +++ b/src/devices/machine/spg110_video.cpp @@ -0,0 +1,587 @@ +// license:BSD-3-Clause +// copyright-holders:David Haywood + +#include "emu.h" +#include "spg110_video.h" + +DEFINE_DEVICE_TYPE(SPG110_VIDEO, spg110_video_device, "spg110_video", "SPG110 System-on-a-Chip (Video)") + +spg110_video_device::spg110_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) + , device_memory_interface(mconfig, *this) + , m_space_config("spg110_video", ENDIANNESS_BIG, 16, 32, 0, address_map_constructor(FUNC(spg110_video_device::map_video), this)) + , m_cpu(*this, finder_base::DUMMY_TAG) + , m_screen(*this, finder_base::DUMMY_TAG) + , m_palette(*this, "palette") + , m_gfxdecode(*this, "gfxdecode") + , m_palram(*this, "palram") + , m_palctrlram(*this, "palctrlram") + , m_sprtileno(*this, "sprtileno") + , m_sprattr1(*this, "sprattr1") + , m_sprattr2(*this, "sprattr2") +{ +} + +spg110_video_device::spg110_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : spg110_video_device(mconfig, SPG110_VIDEO, tag, owner, clock) +{ +} + +template +void spg110_video_device::draw(const rectangle &cliprect, uint32_t line, uint32_t xoff, uint32_t yoff, uint32_t ctrl, uint32_t bitmap_addr, uint16_t tile, uint8_t pal, int32_t h, int32_t w, uint8_t bpp) +{ + address_space &space = m_cpu->space(AS_PROGRAM); + + uint32_t yflipmask = 0; //attr & TILE_Y_FLIP ? h - 1 : 0; + + uint32_t nc = (bpp + 1) << 1; + + uint32_t palette_offset = pal; + + palette_offset <<= nc; + + uint32_t bits_per_row = nc * w / 16; + uint32_t words_per_tile = bits_per_row * h; + uint32_t m = bitmap_addr + words_per_tile * tile + bits_per_row * (line ^ yflipmask); + uint32_t bits = 0; + uint32_t nbits = 0; + uint32_t y = line; + + int yy = (yoff + y) & 0x1ff; + if (yy >= 0x01c0) + yy -= 0x0200; + + if (yy > 240 || yy < 0) + return; + + int y_index = yy * 320; + + for (int32_t x = FlipX ? (w - 1) : 0; FlipX ? x >= 0 : x < w; FlipX ? x-- : x++) + { + int xx = xoff + x; + + bits <<= nc; + + if (nbits < nc) + { + uint16_t b = space.read_word(m++ & 0x3fffff); + //b = (b << 8) | (b >> 8); + bits |= b << (nc - nbits); + nbits += 16; + } + nbits -= nc; + + uint32_t pal = palette_offset + (bits >> 16); + bits &= 0xffff; + + xx &= 0x01ff; + if (xx >= 0x01c0) + xx -= 0x0200; + + if (xx >= 0 && xx < 320) + { + // TODO, this is completely wrong for this palette system + int pix_index = xx + y_index; + //uint16_t rawpal = m_palram[pal]; + const pen_t *pens = m_palette->pens(); + uint32_t paldata = pens[pal]; + + int penword = (pal >> 4) & 0xf; + int penbit = (pal & 0xf); + int trans = m_palctrlswapped[penword] & (1 << penbit); + + if (!trans) + { + m_screenbuf[pix_index] = paldata; + } + } + } +} + +void spg110_video_device::draw_page(const rectangle &cliprect, uint32_t scanline, int priority, uint32_t bitmap_addr, uint16_t *regs) +{ + uint32_t xscroll = regs[0]; + uint32_t yscroll = regs[1]; + uint32_t attr = regs[2]; + uint32_t ctrl = regs[3]; + uint32_t tilemap = regs[4]; + uint32_t palette_map = regs[5]; + address_space &space2 = this->space(0); + + if (!(ctrl & PAGE_ENABLE_MASK)) + { + return; + } + +// if (((attr & PAGE_PRIORITY_FLAG_MASK) >> PAGE_PRIORITY_FLAG_SHIFT) != priority) +// { +// return; +// } + + uint8_t bpp = attr & 0x03; + + uint32_t tile_h = 8 << ((attr & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); + uint32_t tile_w = 8 << ((attr & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); + + uint32_t tile_count_x = 512 / tile_w; + + uint32_t bitmap_y = (scanline + yscroll) & 0xff; + uint32_t y0 = bitmap_y / tile_h; + uint32_t tile_scanline = bitmap_y % tile_h; + uint32_t tile_address = tile_count_x * y0; + + for (uint32_t x0 = 0; x0 < tile_count_x; x0++, tile_address++) + { + uint32_t yy = ((tile_h * y0 - yscroll + 0x10) & 0xff) - 0x10; + uint32_t xx = (tile_w * x0 - xscroll) & 0x1ff; + uint16_t tile = (ctrl & PAGE_WALLPAPER_MASK) ? space2.read_word(tilemap*2) : space2.read_word((tilemap + tile_address)*2); + uint16_t extra_attribute = 0; + + if (!tile) + continue; + + extra_attribute = space2.read_word((palette_map*2) + tile_address); + if (x0 & 1) + extra_attribute = (extra_attribute & 0xff00) >> 8; + else + extra_attribute = (extra_attribute & 0x00ff); + + uint8_t pal = extra_attribute & 0x0f; + uint8_t pri = (extra_attribute & 0x30)>>4; + + if (pri == priority) + { + bool flip_x = 0;//(tileattr & TILE_X_FLIP); + + if (flip_x) + draw(cliprect, tile_scanline, xx, yy, ctrl, bitmap_addr, tile, pal, tile_h, tile_w, bpp); + else + draw(cliprect, tile_scanline, xx, yy, ctrl, bitmap_addr, tile, pal, tile_h, tile_w, bpp); + } + + } +} + + +void spg110_video_device::draw_sprite(const rectangle &cliprect, uint32_t scanline, int priority, uint32_t base_addr) +{ + uint32_t bitmap_addr = 0;//0x40 * m_video_regs[0x22]; + uint16_t tile = m_sprtileno[base_addr]; + uint16_t attr1 = m_sprattr1[base_addr]; + uint16_t attr2 = m_sprattr2[base_addr]; + + int x = (attr1 >> 8) & 0xff; + int y = (attr1) & 0xff; + uint8_t pri = (attr2 & 0x3000)>>12; + + // m_sprtileno tttt tttt tttt tttt t = tile number (all bits?) + // m_sprattr1 xxxx xxxx yyyy yyyy x = low x bits, y = low y bits + // m_sprattr2 -Xzz pppp hhww --bb X = high x bit z = priority, p = palette h = height w = width b = bpp + + + if (!(attr2 & 0x4000)) + x+= 0x100; + + if (!tile) + { + return; + } + + // if ((priority==0) && (scanline==128)) printf("%02x, drawing sprite %04x %04x %04x\n", base_addr, tile, attr1, attr2); + + // if (((attr & PAGE_PRIORITY_FLAG_MASK) >> PAGE_PRIORITY_FLAG_SHIFT) != priority) + // { + // return; + // } + + const uint32_t h = 8 << ((attr2 & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); + const uint32_t w = 8 << ((attr2 & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); + +// if (!(m_video_regs[0x42] & SPRITE_COORD_TL_MASK)) +// { +// x = (160 + x) - w / 2; +// y = (120 - y) - (h / 2) + 8; +// } + + y = 0xff - y; + x = x - 128; + + x &= 0x01ff; + y &= 0x01ff; + + uint32_t tile_line = ((scanline - y)) % h; + int16_t test_y = (y + tile_line) & 0x1ff; + if (test_y >= 0x01c0) + test_y -= 0x0200; + + if (test_y != scanline) + { + return; + } + + //bool blend = (attr & 0x4000); + bool flip_x = 0;//(attr & TILE_X_FLIP); + const uint8_t bpp = attr2 & 0x0003; + //const uint32_t yflipmask = attr & TILE_Y_FLIP ? h - 1 : 0; + const uint32_t palette_offset = (attr2 & 0x0f00) >> 8; + + if (pri == priority) + { + if (flip_x) + draw(cliprect, tile_line, x, y, 0, bitmap_addr, tile, palette_offset, h, w, bpp); + else + draw(cliprect, tile_line, x, y, 0, bitmap_addr, tile, palette_offset, h, w, bpp); + } +} + + + +void spg110_video_device::draw_sprites(const rectangle &cliprect, uint32_t scanline, int priority) +{ + //if (!(m_video_regs[0x42] & SPRITE_ENABLE_MASK)) + //{ + // return; + //} + + for (uint32_t n = 0; n < 256; n++) + { + draw_sprite(cliprect, scanline, priority, n); + } +} + + +/* correct, 4bpp gfxs */ +static const gfx_layout charlayout = +{ + 8,8, + RGN_FRAC(1,1), + 4, + { STEP4(0,1) }, + { 0*4,1*4,2*4,3*4,4*4,5*4,6*4,7*4 }, + { STEP8(0,4*8) }, + 8*8*4 +}; + +static const gfx_layout charlayout6 = +{ + 8,8, + RGN_FRAC(1,1), + 6, + { 0,1,2,3,4,5 }, + { STEP8(0,6) }, + { STEP8(0,6*8) }, + 8*8*6 +}; + +static const gfx_layout char16layout = +{ + 16,16, + RGN_FRAC(1,1), + 4, + { STEP4(0,1) }, + { 0*4,1*4,2*4,3*4,4*4,5*4,6*4,7*4, 8*4,9*4,10*4,11*4,12*4,13*4,14*4,15*4 }, + { STEP16(0,4*16) }, + 16*16*4 +}; + +static const gfx_layout char32layout = +{ + 32,32, + RGN_FRAC(1,1), + 4, + { STEP4(0,1) }, + { STEP32(0,4) }, + { STEP32(0,4*32) }, + 32*32*4 +}; + + + +static GFXDECODE_START( gfx ) + GFXDECODE_ENTRY( ":maincpu", 0, charlayout, 0, 16 ) + GFXDECODE_ENTRY( ":maincpu", 0, char16layout, 0, 16 ) + GFXDECODE_ENTRY( ":maincpu", 0, char32layout, 0, 16 ) + GFXDECODE_ENTRY( ":maincpu", 0, charlayout6, 0, 16 ) // correct for lots of the tiles inc. startup text +GFXDECODE_END + + +void spg110_video_device::device_add_mconfig(machine_config &config) +{ + PALETTE(config, m_palette, palette_device::BLACK, 256); + + GFXDECODE(config, m_gfxdecode, m_palette, gfx); +} + + +device_memory_interface::space_config_vector spg110_video_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(0, &m_space_config) + }; +} + + +// irq source or similar? +READ16_MEMBER(spg110_video_device::spg110_2063_r) +{ + // checks for bits 0x20 and 0x08 in the IRQ function (all IRQs point to the same place) + return 0x0008; +} + +WRITE16_MEMBER(spg110_video_device::spg110_2063_w) +{ + // writes 0x28, probably clears the IRQ / IRQ sources? 0x63 is the same offset for this in spg2xx but bits used seem to be different + m_cpu->set_state_unsynced(UNSP_IRQ0_LINE, CLEAR_LINE); +} + + +WRITE16_MEMBER(spg110_video_device::spg110_201c_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2020_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2042_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2031_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2032_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2033_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2034_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2035_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2036_w) { COMBINE_DATA(&m_2036_scroll); } +WRITE16_MEMBER(spg110_video_device::spg110_2039_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2037_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_203c_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_203d_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2045_w) { } + + +WRITE16_MEMBER(spg110_video_device::spg110_2028_w) { } +WRITE16_MEMBER(spg110_video_device::spg110_2029_w) { } + +READ16_MEMBER(spg110_video_device::spg110_2028_r) { return 0x0000; } +READ16_MEMBER(spg110_video_device::spg110_2029_r) { return 0x0000; } + + +WRITE16_MEMBER(spg110_video_device::spg110_205x_w) +{ + COMBINE_DATA(&m_palctrlram[offset]); + + uint16_t temp = m_palctrlram[offset]; + + // 0x0010 to 0x0001 - complete guess that this could be pen transparency + m_palctrlswapped[offset] = bitswap<16>(temp, 11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4); +} + + +WRITE16_MEMBER(spg110_video_device::dma_unk_2061_w) { COMBINE_DATA(&m_dma_unk_2061); } +WRITE16_MEMBER(spg110_video_device::dma_dst_step_w) { COMBINE_DATA(&m_dma_dst_step); } +WRITE16_MEMBER(spg110_video_device::dma_unk_2067_w) { COMBINE_DATA(&m_dma_unk_2067); } +WRITE16_MEMBER(spg110_video_device::dma_src_step_w) { COMBINE_DATA(&m_dma_src_step); } + +WRITE16_MEMBER(spg110_video_device::dma_dst_w) { COMBINE_DATA(&m_dma_dst); } +WRITE16_MEMBER(spg110_video_device::dma_src_w) { COMBINE_DATA(&m_dma_src); } + +WRITE16_MEMBER(spg110_video_device::dma_len_trigger_w) +{ + int length = data & 0x1fff; + + // this is presumably a counter that underflows to 0x1fff, because that's what the wait loop waits for? + logerror("%s: (trigger len) %04x with values (unk) %04x (dststep) %04x (unk) %04x (src step) %04x | (dst) %04x (src) %04x\n", machine().describe_context(), data, m_dma_unk_2061, m_dma_dst_step, m_dma_unk_2067, m_dma_src_step, m_dma_dst, m_dma_src); + + if ((m_dma_unk_2061!=0x0000) || (m_dma_unk_2067 != 0x0000)) + fatalerror("unknown DMA params are not zero!\n"); + + int source = m_dma_src; + int dest = m_dma_dst; + + for (int i = 0; i < length; i++) + { + address_space &mem = m_cpu->space(AS_PROGRAM); + uint16_t val = mem.read_word(source); + + this->space(0).write_word(dest * 2, val, 0xffff); + + source+=m_dma_src_step; + dest+=m_dma_dst_step; + } +} + +READ16_MEMBER(spg110_video_device::dma_len_status_r) +{ + return 0x1fff; // DMA related? +} + +READ16_MEMBER(spg110_video_device::spg110_2037_r) { return 0x0000; } +READ16_MEMBER(spg110_video_device::spg110_2042_r) { return 0x0000; } + +READ16_MEMBER(spg110_video_device::tmap0_regs_r) { return tmap0_regs[offset]; } +READ16_MEMBER(spg110_video_device::tmap1_regs_r) { return tmap1_regs[offset]; } + +void spg110_video_device::tilemap_write_regs(int which, uint16_t* regs, int regno, uint16_t data) +{ + switch (regno) + { + case 0x0: // Page X scroll + logerror("video_w: Page %d X Scroll = %04x\n", which, data & 0x01ff); + regs[regno] = data & 0x01ff; + break; + + case 0x1: // Page Y scroll + logerror("video_w: Page %d Y Scroll = %04x\n", which, data & 0x00ff); + regs[regno] = data & 0x00ff; + break; + + case 0x2: // Page Attributes + // 'priority' can't be priority here as it is on spg2xx, or the scores in attract will be behind the table, it really seems to be per attribute bit instead + + logerror("video_w: Page %d Attributes = %04x (Priority:%d, Palette:%d, VSize:%d, HSize:%d, FlipY:%d, FlipX:%d, BPP:%d)\n", which, data + , (data >> 12) & 3, (data >> 8) & 15, 8 << ((data >> 6) & 3), 8 << ((data >> 4) & 3), BIT(data, 3), BIT(data, 2), 2 * ((data & 3) + 1)); + regs[regno] = data; + break; + + case 0x3: // Page Control + logerror("video_w: Page %d Control = %04x (Blend:%d, HiColor:%d, RowScroll:%d, Enable:%d, Wallpaper:%d, RegSet:%d, Bitmap:%d)\n", which, data + , BIT(data, 8), BIT(data, 7), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0)); + regs[regno] = data; + break; + + case 0x4: // Page Tile Address + logerror("video_w: Page %d Tile Address = %04x\n", which, data); + regs[regno] = data; + break; + + case 0x5: // Page Attribute Address + logerror("video_w: Page %d Attribute Address = %04x\n", which, data); + regs[regno] = data; + break; + } +} + + +WRITE16_MEMBER(spg110_video_device::tmap0_regs_w) +{ + tilemap_write_regs(0, tmap0_regs,offset,data); +} + + +WRITE16_MEMBER(spg110_video_device::tmap1_regs_w) +{ + tilemap_write_regs(1, tmap1_regs,offset,data); +} + +// this seems to be a different, non-cpu mapped space only accessible via the DMA? +void spg110_video_device::map_video(address_map &map) +{ + // are these addresses hardcoded, or can they move (in which case tilemap system isn't really suitable) + map(0x00000, 0x03fff).ram(); // 2fff? + + map(0x04000, 0x041ff).ram().share("sprtileno"); // seems to be 3 blocks, almost certainly spritelist + map(0x04200, 0x043ff).ram().share("sprattr1"); + map(0x04400, 0x045ff).ram().share("sprattr2"); + + map(0x08000, 0x081ff).ram().share("palram"); // palette format unknown +} + +void spg110_video_device::device_start() +{ + save_item(NAME(m_dma_src_step)); + save_item(NAME(m_dma_dst_step)); + save_item(NAME(m_dma_unk_2061)); + save_item(NAME(m_dma_unk_2067)); + save_item(NAME(m_dma_dst)); + save_item(NAME(m_dma_src)); + save_item(NAME(m_bg_scrollx)); + save_item(NAME(m_bg_scrolly)); + save_item(NAME(m_2036_scroll)); +} + +void spg110_video_device::device_reset() +{ + m_dma_src_step = 0; + m_dma_dst_step = 0; + m_dma_unk_2061 = 0; + m_dma_unk_2067 = 0; + m_dma_dst = 0; + m_dma_src = 0; + m_bg_scrollx = 0; + m_bg_scrolly = 0; + m_2036_scroll = 0; +} + +double spg110_video_device::hue2rgb(double p, double q, double t) +{ + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6.0f) return p + (q - p) * 6 * t; + if (t < 1 / 2.0f) return q; + if (t < 2 / 3.0f) return p + (q - p) * (2 / 3.0f - t) * 6; + return p; +} + +uint32_t spg110_video_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + // Palette, this is still wrong! + int offs = 0; + for (int index = 0;index < 256; index++) + { + uint16_t dat = m_palram[offs++]; + + // llll lsss sshh hhhh + int l_raw = (dat & 0xf800) >> 11; + int sl_raw = (dat & 0x07c0) >> 6; + int h_raw = (dat & 0x003f) >> 0; + + double l = (double)l_raw / 31.0f; + double s = (double)sl_raw / 31.0f; + double h = (double)h_raw / 47.0f; + + double r, g, b; + + if (s == 0) { + r = g = b = l; // greyscale + } else { + double q = l < 0.5f ? l * (1 + s) : l + s - l * s; + double p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3.0f); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3.0f); + } + + int r_real = r * 255.0f; + int g_real = g * 255.0f; + int b_real = b * 255.0f; + + m_palette->set_pen_color(index, r_real, g_real, b_real); + } + + memset(&m_screenbuf[320 * cliprect.min_y], 0, 4 * 320 * ((cliprect.max_y - cliprect.min_y) + 1)); + + const uint32_t page1_addr = 0;//0x40 * m_video_regs[0x20]; + const uint32_t page2_addr = 0;//0x40 * m_video_regs[0x21]; + uint16_t *page1_regs = tmap0_regs; + uint16_t *page2_regs = tmap1_regs; + + for (uint32_t scanline = (uint32_t)cliprect.min_y; scanline <= (uint32_t)cliprect.max_y; scanline++) + { + for (int i = 0; i < 4; i++) + { + draw_page(cliprect, scanline, i, page2_addr, page2_regs); + draw_page(cliprect, scanline, i, page1_addr, page1_regs); + draw_sprites(cliprect, scanline, i); + } + } + + for (int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + uint32_t *dest = &bitmap.pix32(y, cliprect.min_x); + uint32_t *src = &m_screenbuf[cliprect.min_x + 320 * y]; + memcpy(dest, src, sizeof(uint32_t) * ((cliprect.max_x - cliprect.min_x) + 1)); + } + + return 0; +} + +WRITE_LINE_MEMBER(spg110_video_device::vblank) +{ + if (!state) + { + m_cpu->set_state_unsynced(UNSP_IRQ0_LINE, ASSERT_LINE); + } + return; +} diff --git a/src/devices/machine/spg110_video.h b/src/devices/machine/spg110_video.h new file mode 100644 index 00000000000..e8117ed6e1b --- /dev/null +++ b/src/devices/machine/spg110_video.h @@ -0,0 +1,159 @@ +// license:BSD-3-Clause +// copyright-holders:David Haywood + +#ifndef MAME_MACHINE_SPG110_VIDEO_H +#define MAME_MACHINE_SPG110_VIDEO_H + +#pragma once + +#include "cpu/unsp/unsp.h" +#include "emupal.h" +#include "screen.h" + + +class spg110_video_device : public device_t, public device_memory_interface + +{ +public: + spg110_video_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + spg110_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template + spg110_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&cpu_tag, U &&screen_tag) + : spg110_video_device(mconfig, tag, owner, clock) + { + m_cpu.set_tag(std::forward(cpu_tag)); + m_screen.set_tag(std::forward(screen_tag)); + } + + void map_video(address_map &map); + + double hue2rgb(double p, double q, double t); + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + DECLARE_WRITE_LINE_MEMBER(vblank); + + DECLARE_WRITE16_MEMBER(spg110_201c_w); + DECLARE_WRITE16_MEMBER(spg110_2020_w); + + DECLARE_WRITE16_MEMBER(spg110_2028_w); + DECLARE_WRITE16_MEMBER(spg110_2029_w); + + DECLARE_READ16_MEMBER(spg110_2028_r); + DECLARE_READ16_MEMBER(spg110_2029_r); + + DECLARE_WRITE16_MEMBER(spg110_2031_w); + DECLARE_WRITE16_MEMBER(spg110_2032_w); + DECLARE_WRITE16_MEMBER(spg110_2033_w); + DECLARE_WRITE16_MEMBER(spg110_2034_w); + DECLARE_WRITE16_MEMBER(spg110_2035_w); + DECLARE_WRITE16_MEMBER(spg110_2036_w); + DECLARE_WRITE16_MEMBER(spg110_2037_w); + DECLARE_WRITE16_MEMBER(spg110_2039_w); + + DECLARE_WRITE16_MEMBER(spg110_203c_w); + DECLARE_WRITE16_MEMBER(spg110_203d_w); + + DECLARE_WRITE16_MEMBER(spg110_2042_w); + + DECLARE_WRITE16_MEMBER(spg110_2045_w); + + DECLARE_WRITE16_MEMBER(spg110_205x_w); + + + DECLARE_READ16_MEMBER(spg110_2037_r); + DECLARE_READ16_MEMBER(spg110_2042_r); + + DECLARE_WRITE16_MEMBER(dma_dst_w); + DECLARE_WRITE16_MEMBER(dma_unk_2061_w); + DECLARE_WRITE16_MEMBER(dma_len_trigger_w); + DECLARE_WRITE16_MEMBER(spg110_2063_w); + DECLARE_WRITE16_MEMBER(dma_dst_step_w); + DECLARE_WRITE16_MEMBER(dma_src_w); + DECLARE_WRITE16_MEMBER(dma_unk_2067_w); + DECLARE_WRITE16_MEMBER(dma_src_step_w); + + DECLARE_READ16_MEMBER(dma_len_status_r); + DECLARE_READ16_MEMBER(spg110_2063_r); + + DECLARE_READ16_MEMBER(tmap0_regs_r); + DECLARE_READ16_MEMBER(tmap1_regs_r); + DECLARE_WRITE16_MEMBER(tmap0_regs_w); + DECLARE_WRITE16_MEMBER(tmap1_regs_w); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + + virtual void device_add_mconfig(machine_config &config) override; + + virtual space_config_vector memory_space_config() const override; + + address_space_config m_space_config; + +private: + enum + { + PAGE_ENABLE_MASK = 0x0008, + PAGE_WALLPAPER_MASK = 0x0004, + + PAGE_PRIORITY_FLAG_MASK = 0x3000, + PAGE_PRIORITY_FLAG_SHIFT = 12, + PAGE_TILE_HEIGHT_MASK = 0x00c0, + PAGE_TILE_HEIGHT_SHIFT = 6, + PAGE_TILE_WIDTH_MASK = 0x0030, + PAGE_TILE_WIDTH_SHIFT = 4, + + TILE_X_FLIP = 0x0004, + TILE_Y_FLIP = 0x0008 + }; + + enum flipx_t : bool + { + FlipXOff = false, + FlipXOn = true + }; + + required_device m_cpu; + required_device m_screen; + required_device m_palette; + required_device m_gfxdecode; + required_shared_ptr m_palram; + required_shared_ptr m_palctrlram; + required_shared_ptr m_sprtileno; + required_shared_ptr m_sprattr1; + required_shared_ptr m_sprattr2; + + + uint16_t m_palctrlswapped[0x10]; + + + + uint16_t tmap0_regs[0x6]; + uint16_t tmap1_regs[0x6]; + + uint16_t m_dma_src_step; + uint16_t m_dma_dst_step; + uint16_t m_dma_unk_2061; + uint16_t m_dma_unk_2067; + + uint16_t m_dma_dst; + uint16_t m_dma_src; + + uint16_t m_bg_scrollx; + uint16_t m_bg_scrolly; + uint16_t m_2036_scroll; + + void tilemap_write_regs(int which, uint16_t* regs, int regno, uint16_t data); + + template + void draw(const rectangle &cliprect, uint32_t line, uint32_t xoff, uint32_t yoff, uint32_t ctrl, uint32_t bitmap_addr, uint16_t tile, uint8_t pal, int32_t h, int32_t w, uint8_t bpp); + void draw_page(const rectangle &cliprect, uint32_t scanline, int priority, uint32_t bitmap_addr, uint16_t *regs); + void draw_sprite(const rectangle &cliprect, uint32_t scanline, int priority, uint32_t base_addr); + void draw_sprites(const rectangle &cliprect, uint32_t scanline, int priority); + + uint32_t m_screenbuf[320 * 240]; +}; + +DECLARE_DEVICE_TYPE(SPG110_VIDEO, spg110_video_device) + +#endif // MAME_MACHINE_SPG110_VIDEO_H diff --git a/src/devices/machine/spg2xx.cpp b/src/devices/machine/spg2xx.cpp index 3a3a22abac3..6d9d18fb3a2 100644 --- a/src/devices/machine/spg2xx.cpp +++ b/src/devices/machine/spg2xx.cpp @@ -17,30 +17,14 @@ DEFINE_DEVICE_TYPE(SPG24X, spg24x_device, "spg24x", "SPG240-series System-on-a-Chip") DEFINE_DEVICE_TYPE(SPG28X, spg28x_device, "spg28x", "SPG280-series System-on-a-Chip") -#define LOG_UNKNOWN_IO (1U << 3) -#define LOG_IRQS (1U << 4) -#define LOG_VLINES (1U << 5) -#define LOG_DMA (1U << 9) -#define LOG_PPU_READS (1U << 22) -#define LOG_PPU_WRITES (1U << 23) -#define LOG_UNKNOWN_PPU (1U << 24) -#define LOG_IO (LOG_IRQS | LOG_DMA | LOG_UNKNOWN_IO) -#define LOG_PPU (LOG_PPU_READS | LOG_PPU_WRITES | LOG_UNKNOWN_PPU) -#define LOG_ALL (LOG_IO | LOG_PPU | LOG_VLINES ) - -#define VERBOSE (0) -#include "logmacro.h" - -#define SPG_DEBUG_VIDEO (0) - -#define VIDEO_IRQ_ENABLE m_video_regs[0x62] -#define VIDEO_IRQ_STATUS m_video_regs[0x63] spg2xx_device::spg2xx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock) , device_mixer_interface(mconfig, *this, 2) , m_spg_audio(*this, "spgaudio") , m_spg_io(*this, "spgio") + , m_spg_sysdma(*this, "spgsysdma") + , m_spg_video(*this, "spgvideo") , m_rowscrolloffset(15) , m_porta_out(*this) , m_portb_out(*this) @@ -55,9 +39,6 @@ spg2xx_device::spg2xx_device(const machine_config &mconfig, device_type type, co , m_chip_sel(*this) , m_cpu(*this, finder_base::DUMMY_TAG) , m_screen(*this, finder_base::DUMMY_TAG) - , m_scrollram(*this, "scrollram") - , m_paletteram(*this, "paletteram") - , m_spriteram(*this, "spriteram") { } @@ -74,30 +55,20 @@ spg28x_device::spg28x_device(const machine_config &mconfig, const char *tag, dev void spg2xx_device::map(address_map &map) { map(0x000000, 0x0027ff).ram(); - map(0x002800, 0x0028ff).rw(FUNC(spg2xx_device::video_r), FUNC(spg2xx_device::video_w)); - map(0x002900, 0x002aff).ram().share("scrollram"); - map(0x002b00, 0x002bff).ram().share("paletteram"); - map(0x002c00, 0x002fff).ram().share("spriteram"); + map(0x002800, 0x0028ff).rw(m_spg_video, FUNC(spg2xx_video_device::video_r), FUNC(spg2xx_video_device::video_w)); + map(0x002900, 0x002aff).ram().share("spgvideo:scrollram"); + map(0x002b00, 0x002bff).ram().share("spgvideo:paletteram"); + map(0x002c00, 0x002fff).ram().share("spgvideo:spriteram"); map(0x003000, 0x0031ff).rw(m_spg_audio, FUNC(spg2xx_audio_device::audio_r), FUNC(spg2xx_audio_device::audio_w)); map(0x003200, 0x0033ff).rw(m_spg_audio, FUNC(spg2xx_audio_device::audio_phase_r), FUNC(spg2xx_audio_device::audio_phase_w)); map(0x003400, 0x0037ff).rw(m_spg_audio, FUNC(spg2xx_audio_device::audio_ctrl_r), FUNC(spg2xx_audio_device::audio_ctrl_w)); map(0x003d00, 0x003d2f).rw(m_spg_io, FUNC(spg2xx_io_device::io_r), FUNC(spg2xx_io_device::io_w)); map(0x003d30, 0x003dff).rw(m_spg_io, FUNC(spg2xx_io_device::io_extended_r), FUNC(spg2xx_io_device::io_extended_w)); - map(0x003e00, 0x003e03).rw(FUNC(spg2xx_device::dma_r), FUNC(spg2xx_device::dma_w)); + map(0x003e00, 0x003e03).rw(m_spg_sysdma, FUNC(spg2xx_sysdma_device::dma_r), FUNC(spg2xx_sysdma_device::dma_w)); } void spg2xx_device::device_start() { - for (uint8_t i = 0; i < 32; i++) - { - m_rgb5_to_rgb8[i] = (i << 3) | (i >> 2); - } - for (uint16_t i = 0; i < 0x8000; i++) - { - m_rgb555_to_rgb888[i] = (m_rgb5_to_rgb8[(i >> 10) & 0x1f] << 16) | - (m_rgb5_to_rgb8[(i >> 5) & 0x1f] << 8) | - (m_rgb5_to_rgb8[(i >> 0) & 0x1f] << 0); - } m_porta_out.resolve_safe(); m_portb_out.resolve_safe(); m_portc_out.resolve_safe(); @@ -111,47 +82,17 @@ void spg2xx_device::device_start() m_uart_tx.resolve_safe(); m_chip_sel.resolve_safe(); - m_screenpos_timer = timer_alloc(TIMER_SCREENPOS); - m_screenpos_timer->adjust(attotime::never); - - save_item(NAME(m_hide_page0)); - save_item(NAME(m_hide_page1)); - save_item(NAME(m_hide_sprites)); - save_item(NAME(m_debug_sprites)); - save_item(NAME(m_debug_blit)); - save_item(NAME(m_debug_palette)); - save_item(NAME(m_sprite_index_to_debug)); - - save_item(NAME(m_dma_regs)); - - save_item(NAME(m_video_regs)); save_item(NAME(m_sprite_limit)); save_item(NAME(m_pal_flag)); } void spg2xx_device::device_reset() { - memset(m_video_regs, 0, 0x100 * sizeof(uint16_t)); - memset(m_dma_regs, 0, 0x4 * sizeof(uint16_t)); - - m_video_regs[0x36] = 0xffff; - m_video_regs[0x37] = 0xffff; - m_video_regs[0x3c] = 0x0020; - m_video_regs[0x42] = 0x0001; - - m_hide_page0 = false; - m_hide_page1 = false; - m_hide_sprites = false; - m_debug_sprites = false; - m_debug_blit = false; - m_debug_palette = false; - m_sprite_index_to_debug = 0; } - -WRITE_LINE_MEMBER(spg2xx_device::audioirq_w) +WRITE_LINE_MEMBER(spg2xx_device::videoirq_w) { - m_cpu->set_state_unsynced(UNSP_IRQ4_LINE, state); + m_cpu->set_state_unsynced(UNSP_IRQ0_LINE, state); } WRITE_LINE_MEMBER(spg2xx_device::timerirq_w) @@ -164,6 +105,11 @@ WRITE_LINE_MEMBER(spg2xx_device::uartirq_w) m_cpu->set_state_unsynced(UNSP_IRQ3_LINE, state); } +WRITE_LINE_MEMBER(spg2xx_device::audioirq_w) +{ + m_cpu->set_state_unsynced(UNSP_IRQ4_LINE, state); +} + WRITE_LINE_MEMBER(spg2xx_device::extirq_w) { m_cpu->set_state_unsynced(UNSP_IRQ5_LINE, state); @@ -187,824 +133,6 @@ READ16_MEMBER(spg2xx_device::space_r) return cpuspace.read_word(offset); } -/************************* -* Video Hardware * -*************************/ - -// Perform a lerp between a and b -inline uint8_t spg2xx_device::mix_channel(uint8_t bottom, uint8_t top) -{ - uint8_t alpha = (m_video_regs[0x2a] & 3) << 6; - return ((256 - alpha) * bottom + alpha * top) >> 8; -} - -template -void spg2xx_device::blit(const rectangle &cliprect, uint32_t line, uint32_t xoff, uint32_t yoff, uint32_t attr, uint32_t ctrl, uint32_t bitmap_addr, uint16_t tile) -{ - address_space &space = m_cpu->space(AS_PROGRAM); - - int32_t h = 8 << ((attr & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); - int32_t w = 8 << ((attr & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); - - uint32_t yflipmask = attr & TILE_Y_FLIP ? h - 1 : 0; - - uint32_t nc = ((attr & 0x0003) + 1) << 1; - - uint32_t palette_offset = (attr & 0x0f00) >> 4; - if (SPG_DEBUG_VIDEO && m_debug_blit) - { - printf("s:%d line:%d xy:%08x,%08x attr:%08x ctrl:%08x bitmap_addr:%08x tile:%04x\n", cliprect.min_x, line, xoff, yoff, attr, ctrl, bitmap_addr, tile); - printf("hw:%d,%d f:%d,%d yfm:%d ncols:%d pobs:%02x ", w, h, (attr & TILE_X_FLIP) ? 1 : 0, (attr & TILE_Y_FLIP) ? 1 : 0, yflipmask, nc, palette_offset); - } - palette_offset >>= nc; - palette_offset <<= nc; - if (SPG_DEBUG_VIDEO && m_debug_blit) - { - printf("poas:%02x\n", palette_offset); - } - - uint32_t bits_per_row = nc * w / 16; - uint32_t words_per_tile = bits_per_row * h; - uint32_t m = bitmap_addr + words_per_tile * tile + bits_per_row * (line ^ yflipmask); - uint32_t bits = 0; - uint32_t nbits = 0; - uint32_t y = line; - - int yy = (yoff + y) & 0x1ff; - if (yy >= 0x01c0) - yy -= 0x0200; - - if (yy > 240 || yy < 0) - return; - - if (SPG_DEBUG_VIDEO && m_debug_blit) - printf("%3d:\n", yy); - - int y_index = yy * 320; - - for (int32_t x = FlipX ? (w - 1) : 0; FlipX ? x >= 0 : x < w; FlipX ? x-- : x++) - { - int xx = xoff + x; - - bits <<= nc; - if (SPG_DEBUG_VIDEO && m_debug_blit) - printf(" %08x:%d ", bits, nbits); - if (nbits < nc) - { - uint16_t b = space.read_word(m++ & 0x3fffff); - b = (b << 8) | (b >> 8); - bits |= b << (nc - nbits); - nbits += 16; - if (SPG_DEBUG_VIDEO && m_debug_blit) - printf("(%04x:%08x:%d) ", b, bits, nbits); - } - nbits -= nc; - - uint32_t pal = palette_offset + (bits >> 16); - if (SPG_DEBUG_VIDEO && m_debug_blit) - printf("%02x:%02x:%04x ", bits >> 16, pal, bits & 0xffff); - bits &= 0xffff; - - if (RowScroll) - xx -= (int16_t)m_scrollram[(yy + m_rowscrolloffset) & 0x1ff]; - - xx &= 0x01ff; - if (xx >= 0x01c0) - xx -= 0x0200; - - if (xx >= 0 && xx < 320) - { - int pix_index = xx + y_index; - - uint16_t rgb = m_paletteram[pal]; - if (SPG_DEBUG_VIDEO && m_debug_blit) - printf("rgb:%04x ", rgb); - - if (!(rgb & 0x8000)) - { - if (Blend) - { - if (SPG_DEBUG_VIDEO && m_debug_blit) - printf("M\n"); - m_screenbuf[pix_index] = (mix_channel((uint8_t)(m_screenbuf[pix_index] >> 16), m_rgb5_to_rgb8[(rgb >> 10) & 0x1f]) << 16) | - (mix_channel((uint8_t)(m_screenbuf[pix_index] >> 8), m_rgb5_to_rgb8[(rgb >> 5) & 0x1f]) << 8) | - (mix_channel((uint8_t)(m_screenbuf[pix_index] >> 0), m_rgb5_to_rgb8[rgb & 0x1f])); - } - else - { - if (SPG_DEBUG_VIDEO && m_debug_blit) - printf("S\n"); - m_screenbuf[pix_index] = m_rgb555_to_rgb888[rgb]; - } - } - else if (SPG_DEBUG_VIDEO && m_debug_blit) - { - printf("X\n"); - } - } - } -} - -void spg2xx_device::blit_page(const rectangle &cliprect, uint32_t scanline, int depth, uint32_t bitmap_addr, uint16_t *regs) -{ - uint32_t xscroll = regs[0]; - uint32_t yscroll = regs[1]; - uint32_t attr = regs[2]; - uint32_t ctrl = regs[3]; - uint32_t tilemap = regs[4]; - uint32_t palette_map = regs[5]; - address_space &space = m_cpu->space(AS_PROGRAM); - - if (!(ctrl & PAGE_ENABLE_MASK)) - { - return; - } - - if (((attr & PAGE_DEPTH_FLAG_MASK) >> PAGE_DEPTH_FLAG_SHIFT) != depth) - { - return; - } - - uint32_t tile_h = 8 << ((attr & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); - uint32_t tile_w = 8 << ((attr & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); - - uint32_t tile_count_x = 512 / tile_w; - - uint32_t bitmap_y = (scanline + yscroll) & 0xff; - uint32_t y0 = bitmap_y / tile_h; - uint32_t tile_scanline = bitmap_y % tile_h; - uint32_t tile_address = tile_count_x * y0; - if (SPG_DEBUG_VIDEO && machine().input().code_pressed(KEYCODE_H)) - printf("s:%3d | baddr:%08x | yscr:%3d | bity:%3d | y0:%2d | ts:%2d\n", scanline, bitmap_addr, yscroll, bitmap_y, y0, tile_scanline); - - if (SPG_DEBUG_VIDEO && machine().input().code_pressed(KEYCODE_EQUALS)) - m_debug_blit = true; - for (uint32_t x0 = 0; x0 < tile_count_x; x0++, tile_address++) - { - uint32_t yy = ((tile_h * y0 - yscroll + 0x10) & 0xff) - 0x10; - uint32_t xx = (tile_w * x0 - xscroll) & 0x1ff; - uint16_t tile = (ctrl & PAGE_WALLPAPER_MASK) ? space.read_word(tilemap) : space.read_word(tilemap + tile_address); - uint16_t palette = 0; - - if (!tile) - continue; - - palette = (ctrl & PAGE_WALLPAPER_MASK) ? space.read_word(palette_map) : space.read_word(palette_map + tile_address / 2); - if (x0 & 1) - palette >>= 8; - - uint32_t tileattr = attr; - uint32_t tilectrl = ctrl; - if ((ctrl & 2) == 0) - { // -(1) bld(1) flip(2) pal(4) - tileattr &= ~0x000c; - tileattr |= (palette >> 2) & 0x000c; // flip - - tileattr &= ~0x0f00; - tileattr |= (palette << 8) & 0x0f00; // palette - - tilectrl &= ~0x0100; - tilectrl |= (palette << 2) & 0x0100; // blend - } - - bool blend = (tileattr & 0x4000 || tilectrl & 0x0100); - bool row_scroll = (tilectrl & 0x0010); - bool flip_x = (tileattr & TILE_X_FLIP); - - if (blend) - { - if (row_scroll) - { - if (flip_x) - blit(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile); - else - blit(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile); - } - else - { - if (flip_x) - blit(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile); - else - blit(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile); - } - } - else - { - if (row_scroll) - { - if (flip_x) - blit(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile); - else - blit(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile); - } - else - { - if (flip_x) - blit(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile); - else - blit(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile); - } - } - } - if (SPG_DEBUG_VIDEO && machine().input().code_pressed(KEYCODE_EQUALS)) - m_debug_blit = false; -} - -void spg2xx_device::blit_sprite(const rectangle &cliprect, uint32_t scanline, int depth, uint32_t base_addr) -{ - uint32_t bitmap_addr = 0x40 * m_video_regs[0x22]; - uint16_t tile = m_spriteram[base_addr + 0]; - int16_t x = m_spriteram[base_addr + 1]; - int16_t y = m_spriteram[base_addr + 2]; - uint16_t attr = m_spriteram[base_addr + 3]; - - if (!tile) - { - return; - } - - if (((attr & PAGE_DEPTH_FLAG_MASK) >> PAGE_DEPTH_FLAG_SHIFT) != depth) - { - return; - } - - const uint32_t h = 8 << ((attr & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); - const uint32_t w = 8 << ((attr & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); - - if (!(m_video_regs[0x42] & SPRITE_COORD_TL_MASK)) - { - x = (160 + x) - w / 2; - y = (120 - y) - (h / 2) + 8; - } - - x &= 0x01ff; - y &= 0x01ff; - - uint32_t tile_line = ((scanline - y) + 0x200) % h; - int16_t test_y = (y + tile_line) & 0x1ff; - if (test_y >= 0x01c0) - test_y -= 0x0200; - - if (test_y != scanline) - { - return; - } - - bool blend = (attr & 0x4000); - bool flip_x = (attr & TILE_X_FLIP); - -#if SPG_DEBUG_VIDEO - if (m_debug_sprites && machine().input().code_pressed(KEYCODE_MINUS)) - m_debug_blit = true; - if (blend) - { - if (flip_x) - blit(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile); - else - blit(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile); - } - else - { - if (flip_x) - blit(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile); - else - blit(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile); - } - m_debug_blit = false; -#else - if (blend) - { - if (flip_x) - blit(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile); - else - blit(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile); - } - else - { - if (flip_x) - blit(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile); - else - blit(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile); - } -#endif -} - -void spg2xx_device::blit_sprites(const rectangle &cliprect, uint32_t scanline, int depth) -{ - if (!(m_video_regs[0x42] & SPRITE_ENABLE_MASK)) - { - return; - } - -#if SPG_DEBUG_VIDEO - if (!m_debug_sprites) - { -#endif - for (uint32_t n = 0; n < m_sprite_limit; n++) - { - blit_sprite(cliprect, scanline, depth, 4 * n); - } -#if SPG_DEBUG_VIDEO - } - else - { - blit_sprite(cliprect, scanline, depth, 4 * m_sprite_index_to_debug); - } -#endif -} - -void spg2xx_device::apply_saturation(const rectangle &cliprect) -{ - static const float s_u8_to_float = 1.0f / 255.0f; - static const float s_gray_r = 0.299f; - static const float s_gray_g = 0.587f; - static const float s_gray_b = 0.114f; - const float sat_adjust = (0xff - (m_video_regs[0x3c] & 0x00ff)) / (float)(0xff - 0x20); - for (int y = cliprect.min_y; y <= cliprect.max_y; y++) - { - uint32_t *src = &m_screenbuf[cliprect.min_x + 320 * y]; - for (int x = cliprect.min_x; x <= cliprect.max_x; x++) - { - const uint32_t src_rgb = *src; - const float src_r = (uint8_t)(src_rgb >> 16) * s_u8_to_float; - const float src_g = (uint8_t)(src_rgb >> 8) * s_u8_to_float; - const float src_b = (uint8_t)(src_rgb >> 0) * s_u8_to_float; - const float luma = src_r * s_gray_r + src_g * s_gray_g + src_b * s_gray_b; - const float adjusted_r = luma + (src_r - luma) * sat_adjust; - const float adjusted_g = luma + (src_g - luma) * sat_adjust; - const float adjusted_b = luma + (src_b - luma) * sat_adjust; - const int integer_r = (int)floor(adjusted_r * 255.0f); - const int integer_g = (int)floor(adjusted_g * 255.0f); - const int integer_b = (int)floor(adjusted_b * 255.0f); - *src++ = (integer_r > 255 ? 0xff0000 : (integer_r < 0 ? 0 : ((uint8_t)integer_r << 16))) | - (integer_g > 255 ? 0x00ff00 : (integer_g < 0 ? 0 : ((uint8_t)integer_g << 8))) | - (integer_b > 255 ? 0x0000ff : (integer_b < 0 ? 0 : (uint8_t)integer_b)); - } - } -} - -void spg2xx_device::apply_fade(const rectangle &cliprect) -{ - const uint16_t fade_offset = m_video_regs[0x30]; - for (int y = cliprect.min_y; y <= cliprect.max_y; y++) - { - uint32_t *src = &m_screenbuf[cliprect.min_x + 320 * y]; - for (int x = cliprect.min_x; x <= cliprect.max_x; x++) - { - const uint32_t src_rgb = *src; - const uint8_t src_r = (src_rgb >> 16) & 0xff; - const uint8_t src_g = (src_rgb >> 8) & 0xff; - const uint8_t src_b = (src_rgb >> 0) & 0xff; - const uint8_t r = src_r - fade_offset; - const uint8_t g = src_g - fade_offset; - const uint8_t b = src_b - fade_offset; - *src++ = (r > src_r ? 0 : (r << 16)) | - (g > src_g ? 0 : (g << 8)) | - (b > src_b ? 0 : (b << 0)); - } - } -} - -uint32_t spg2xx_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) -{ - memset(&m_screenbuf[320 * cliprect.min_y], 0, 4 * 320 * ((cliprect.max_y - cliprect.min_y) + 1)); - - const uint32_t page1_addr = 0x40 * m_video_regs[0x20]; - const uint32_t page2_addr = 0x40 * m_video_regs[0x21]; - uint16_t *page1_regs = m_video_regs + 0x10; - uint16_t *page2_regs = m_video_regs + 0x16; - - for (uint32_t scanline = (uint32_t)cliprect.min_y; scanline <= (uint32_t)cliprect.max_y; scanline++) - { - for (int i = 0; i < 4; i++) - { - if (!SPG_DEBUG_VIDEO || !m_hide_page0) - blit_page(cliprect, scanline, i, page1_addr, page1_regs); - if (!SPG_DEBUG_VIDEO || !m_hide_page1) - blit_page(cliprect, scanline, i, page2_addr, page2_regs); - if (!SPG_DEBUG_VIDEO || !m_hide_sprites) - blit_sprites(cliprect, scanline, i); - } - } - - if ((m_video_regs[0x3c] & 0x00ff) != 0x0020) - { - apply_saturation(cliprect); - } - - if (m_video_regs[0x30] != 0) - { - apply_fade(cliprect); - } - - for (int y = cliprect.min_y; y <= cliprect.max_y; y++) - { - uint32_t *dest = &bitmap.pix32(y, cliprect.min_x); - uint32_t *src = &m_screenbuf[cliprect.min_x + 320 * y]; - memcpy(dest, src, sizeof(uint32_t) * ((cliprect.max_x - cliprect.min_x) + 1)); - } - - if (SPG_DEBUG_VIDEO && m_debug_palette) - { - for (int y = cliprect.min_y; y <= cliprect.max_y && y < 128; y++) - { - const uint16_t high_nybble = (y / 8) << 4; - uint32_t *dest = &bitmap.pix32(y, cliprect.min_x); - for (int x = cliprect.min_x; x <= cliprect.max_x && x < 256; x++) - { - const uint16_t low_nybble = x / 16; - const uint16_t palette_entry = high_nybble | low_nybble; - const uint16_t color = m_paletteram[palette_entry]; - if (!(color & 0x8000)) - { - *dest = m_rgb555_to_rgb888[color & 0x7fff]; - } - dest++; - } - } - } - - return 0; -} - -void spg2xx_device::do_sprite_dma(uint32_t len) -{ - address_space &mem = m_cpu->space(AS_PROGRAM); - - uint32_t src = m_video_regs[0x70] & 0x3fff; - uint32_t dst = m_video_regs[0x71]; - - for (uint32_t j = 0; j < len; j++) - { - m_spriteram[(dst + j) & 0x3ff] = mem.read_word(src + j); - } - - m_video_regs[0x72] = 0; - if (VIDEO_IRQ_ENABLE & 4) - { - const uint16_t old = VIDEO_IRQ_STATUS; - VIDEO_IRQ_STATUS |= 4; - const uint16_t changed = old ^ (VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS); - if (changed) - check_video_irq(); - } -} - -READ16_MEMBER(spg2xx_device::video_r) -{ - switch (offset) - { - case 0x38: // Current Line - LOGMASKED(LOG_VLINES, "video_r: Current Line: %04x\n", m_screen->vpos()); - return m_screen->vpos(); - - case 0x62: // Video IRQ Enable - LOGMASKED(LOG_IRQS, "video_r: Video IRQ Enable: %04x\n", VIDEO_IRQ_ENABLE); - return VIDEO_IRQ_ENABLE; - - case 0x63: // Video IRQ Status - LOGMASKED(LOG_IRQS, "video_r: Video IRQ Status: %04x\n", VIDEO_IRQ_STATUS); - return VIDEO_IRQ_STATUS; - - default: - LOGMASKED(LOG_UNKNOWN_PPU, "video_r: Unknown register %04x = %04x\n", 0x2800 + offset, m_video_regs[offset]); - break; - } - return m_video_regs[offset]; -} - -WRITE16_MEMBER(spg2xx_device::video_w) -{ - switch (offset) - { - case 0x10: // Page 1 X scroll - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 X Scroll = %04x\n", data & 0x01ff); - m_video_regs[offset] = data & 0x01ff; - break; - - case 0x11: // Page 1 Y scroll - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Y Scroll = %04x\n", data & 0x00ff); - m_video_regs[offset] = data & 0x00ff; - break; - - case 0x12: // Page 1 Attributes - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Attributes = %04x (Depth:%d, Palette:%d, VSize:%d, HSize:%d, FlipY:%d, FlipX:%d, BPP:%d)\n", data - , (data >> 12) & 3, (data >> 8) & 15, 8 << ((data >> 6) & 3), 8 << ((data >> 4) & 3), BIT(data, 3), BIT(data, 2), 2 * ((data & 3) + 1)); - m_video_regs[offset] = data; - break; - - case 0x13: // Page 1 Control - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Control = %04x (Blend:%d, HiColor:%d, RowScroll:%d, Enable:%d, Wallpaper:%d, RegSet:%d, Bitmap:%d)\n", data - , BIT(data, 8), BIT(data, 7), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0)); - m_video_regs[offset] = data; - break; - - case 0x14: // Page 1 Tile Address - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Tile Address = %04x\n", data & 0x1fff); - m_video_regs[offset] = data; - break; - - case 0x15: // Page 1 Attribute Address - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Attribute Address = %04x\n", data & 0x1fff); - m_video_regs[offset] = data; - break; - - case 0x16: // Page 2 X scroll - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 X Scroll = %04x\n", data & 0x01ff); - m_video_regs[offset] = data & 0x01ff; - break; - - case 0x17: // Page 2 Y scroll - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Y Scroll: %04x = %04x\n", 0x2800 | offset, data & 0x00ff); - m_video_regs[offset] = data & 0x00ff; - break; - - case 0x18: // Page 2 Attributes - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Attributes = %04x (Depth:%d, Palette:%d, VSize:%d, HSize:%d, FlipY:%d, FlipX:%d, BPP:%d)\n", data - , (data >> 12) & 3, (data >> 8) & 15, 8 << ((data >> 6) & 3), 8 << ((data >> 4) & 3), BIT(data, 3), BIT(data, 2), 2 * ((data & 3) + 1)); - m_video_regs[offset] = data; - break; - - case 0x19: // Page 2 Control - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Control = %04x (Blend:%d, HiColor:%d, RowScroll:%d, Enable:%d, Wallpaper:%d, RegSet:%d, Bitmap:%d)\n", data - , BIT(data, 8), BIT(data, 7), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0)); - m_video_regs[offset] = data; - break; - - case 0x1a: // Page 2 Tile Address - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Tile Address = %04x\n", data & 0x1fff); - m_video_regs[offset] = data; - break; - - case 0x1b: // Page 2 Attribute Address - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Attribute Address = %04x\n", data & 0x1fff); - m_video_regs[offset] = data; - break; - - case 0x20: // Page 1 Segment Address - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Segment Address = %04x\n", data); - m_video_regs[offset] = data; - break; - - case 0x21: // Page 2 Segment Address - LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Segment Address = %04x\n", data); - m_video_regs[offset] = data; - break; - - case 0x22: // Sprite Segment Address - LOGMASKED(LOG_PPU_WRITES, "video_w: Sprite Segment Address = %04x\n", data); - m_video_regs[offset] = data; - break; - - case 0x2a: // Blend Level Control - LOGMASKED(LOG_PPU_WRITES, "video_w: Blend Level Control = %04x\n", data & 0x0003); - m_video_regs[offset] = data & 0x0003; - break; - - case 0x30: // Fade Effect Control - LOGMASKED(LOG_PPU_WRITES, "video_w: Fade Effect Control = %04x\n", data & 0x00ff); - m_video_regs[offset] = data & 0x00ff; - break; - - case 0x36: // IRQ pos V - case 0x37: // IRQ pos H - m_video_regs[offset] = data & 0x01ff; - LOGMASKED(LOG_IRQS, "video_w: Video IRQ Position: %04x,%04x (%04x)\n", m_video_regs[0x37], m_video_regs[0x36], 0x2800 | offset); - if (m_video_regs[0x37] < 160 && m_video_regs[0x36] < 240) - m_screenpos_timer->adjust(m_screen->time_until_pos(m_video_regs[0x36], m_video_regs[0x37] << 1)); - else - m_screenpos_timer->adjust(attotime::never); - break; - - case 0x39: // Latch 1st Line Pen Pulse - LOGMASKED(LOG_PPU_WRITES, "video_w: Latch 1st Line Pen Pulse = %04x\n", data & 0x0001); - m_video_regs[offset] = data & 0x0001; - break; - - case 0x3c: // TV Control 1 - LOGMASKED(LOG_PPU_WRITES, "video_w: TV Control 1 = %04x (Hue:%02x, Saturation:%02x)\n", data, data >> 8, data & 0x00ff); - m_video_regs[offset] = data; - break; - - case 0x3d: // TV Control 2 - { - static const char* const s_lpf_mode[4] = { "LPF1", "LPF2", "All", "Edge" }; - LOGMASKED(LOG_PPU_WRITES, "video_w: TV Control 2 = %04x (LPFMode:%s, Enable:%d, Interlace:%d)\n", data & 0x000f - , s_lpf_mode[(data >> 2) & 3], BIT(data, 1), BIT(data, 0)); - m_video_regs[offset] = data & 0x000f; - break; - } - - case 0x3e: // Light Pen Y Position - LOGMASKED(LOG_PPU_WRITES, "video_w: Light Pen Y (read only) = %04x\n", data & 0x01ff); - break; - - case 0x3f: // Light Pen YXPosition - LOGMASKED(LOG_PPU_WRITES, "video_w: Light Pen X (read only) = %04x\n", data & 0x01ff); - break; - - case 0x42: // Sprite Control - LOGMASKED(LOG_PPU_WRITES, "video_w: Sprite Control = %04x (TopLeft:%d, Enable:%d)\n", data & 0x0003, BIT(data, 1), BIT(data, 0)); - m_video_regs[offset] = data & 0x0003; - break; - - case 0x62: // Video IRQ Enable - { - LOGMASKED(LOG_IRQS, "video_w: Video IRQ Enable = %04x (DMA:%d, Timing:%d, Blanking:%d)\n", data & 0x0007, BIT(data, 2), BIT(data, 1), BIT(data, 0)); - const uint16_t old = VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS; - VIDEO_IRQ_ENABLE = data & 0x0007; - const uint16_t changed = old ^ (VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS); - if (changed) - check_video_irq(); - break; - } - - case 0x63: // Video IRQ Acknowledge - { - LOGMASKED(LOG_IRQS, "video_w: Video IRQ Acknowledge = %04x\n", data); - const uint16_t old = VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS; - VIDEO_IRQ_STATUS &= ~data; - const uint16_t changed = old ^ (VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS); - if (changed) - check_video_irq(); - break; - } - - case 0x70: // Sprite DMA Source - LOGMASKED(LOG_DMA, "video_w: Sprite DMA Source = %04x\n", data & 0x3fff); - m_video_regs[offset] = data & 0x3fff; - break; - - case 0x71: // Sprite DMA Dest - LOGMASKED(LOG_DMA, "video_w: Sprite DMA Dest = %04x\n", data & 0x03ff); - m_video_regs[offset] = data & 0x03ff; - break; - - case 0x72: // Sprite DMA Length - { - LOGMASKED(LOG_DMA, "video_w: Sprite DMA Length = %04x\n", data & 0x03ff); - uint16_t length = data & 0x3ff; - do_sprite_dma(length ? length : 0x400); - break; - } - - default: - LOGMASKED(LOG_UNKNOWN_PPU, "video_w: Unknown register %04x = %04x\n", 0x2800 + offset, data); - m_video_regs[offset] = data; - break; - } -} - -WRITE_LINE_MEMBER(spg2xx_device::vblank) -{ - if (!state) - { - VIDEO_IRQ_STATUS &= ~1; - LOGMASKED(LOG_IRQS, "Setting video IRQ status to %04x\n", VIDEO_IRQ_STATUS); - check_video_irq(); - return; - } - -#if SPG_DEBUG_VIDEO - if (machine().input().code_pressed_once(KEYCODE_5)) - m_hide_page0 = !m_hide_page0; - if (machine().input().code_pressed_once(KEYCODE_6)) - m_hide_page1 = !m_hide_page1; - if (machine().input().code_pressed_once(KEYCODE_7)) - m_hide_sprites = !m_hide_sprites; - if (machine().input().code_pressed_once(KEYCODE_8)) - m_debug_sprites = !m_debug_sprites; - if (machine().input().code_pressed_once(KEYCODE_9)) - m_sprite_index_to_debug--; - if (machine().input().code_pressed_once(KEYCODE_0)) - m_sprite_index_to_debug++; - if (machine().input().code_pressed_once(KEYCODE_L)) - m_debug_palette = !m_debug_palette; -#endif - - if (VIDEO_IRQ_ENABLE & 1) - { - VIDEO_IRQ_STATUS |= 1; - LOGMASKED(LOG_IRQS, "Setting video IRQ status to %04x\n", VIDEO_IRQ_STATUS); - check_video_irq(); - } -} - -void spg2xx_device::check_video_irq() -{ - LOGMASKED(LOG_IRQS, "%ssserting IRQ0 (%04x, %04x)\n", (VIDEO_IRQ_STATUS & VIDEO_IRQ_ENABLE) ? "A" : "Dea", VIDEO_IRQ_STATUS, VIDEO_IRQ_ENABLE); - m_cpu->set_state_unsynced(UNSP_IRQ0_LINE, (VIDEO_IRQ_STATUS & VIDEO_IRQ_ENABLE) ? ASSERT_LINE : CLEAR_LINE); -} - - -/************************* -* Machine Hardware * -*************************/ - -READ16_MEMBER(spg2xx_device::dma_r) -{ - uint16_t val = m_dma_regs[offset]; - switch (offset) - { - - case 0x000: // DMA Source (L) - LOGMASKED(LOG_DMA, "dma_r: DMA Source (lo) = %04x\n", val); - break; - - case 0x001: // DMA Source (H) - LOGMASKED(LOG_DMA, "dma_r: DMA Source (hi) = %04x\n", val); - break; - - case 0x002: // DMA Length - LOGMASKED(LOG_DMA, "dma_r: DMA Length = %04x\n", 0); - val = 0; - break; - - case 0x003: // DMA Destination - LOGMASKED(LOG_DMA, "dma_r: DMA Dest = %04x\n", val); - break; - - default: - LOGMASKED(LOG_UNKNOWN_IO, "dma_r: Unknown register %04x\n", 0x3d00 + offset); - break; - } - - return val; -} - - -WRITE16_MEMBER(spg2xx_device::dma_w) -{ - switch (offset) - { - case 0x000: // DMA Source (lo) - LOGMASKED(LOG_DMA, "dma_w: DMA Source (lo) = %04x\n", data); - m_dma_regs[offset] = data; - break; - - case 0x001: // DMA Source (hi) - LOGMASKED(LOG_DMA, "dma_w: DMA Source (hi) = %04x\n", data); - m_dma_regs[offset] = data; - break; - - case 0x002: // DMA Length - LOGMASKED(LOG_DMA, "dma_w: DMA Length = %04x\n", data); - if (!(data & 0xc000)) // jak_dora writes 0xffff here which ends up trashing registers etc. why? such writes can't be valid - do_cpu_dma(data); - break; - - case 0x003: // DMA Destination - LOGMASKED(LOG_DMA, "dma_w: DMA Dest = %04x\n", data); - m_dma_regs[offset] = data; - break; - - default: - LOGMASKED(LOG_UNKNOWN_IO, "dma_w: Unknown register %04x = %04x\n", 0x3d00 + offset, data); - m_dma_regs[offset] = data; - break; - } -} - -void spg2xx_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - switch (id) - { - case TIMER_SCREENPOS: - { - if (VIDEO_IRQ_ENABLE & 2) - { - VIDEO_IRQ_STATUS |= 2; - check_video_irq(); - } - m_screen->update_partial(m_screen->vpos()); - - // fire again, jak_dbz pinball needs this - m_screenpos_timer->adjust(m_screen->time_until_pos(m_video_regs[0x36], m_video_regs[0x37] << 1)); - break; - } - } -} - - -void spg2xx_device::do_cpu_dma(uint32_t len) -{ - address_space &mem = m_cpu->space(AS_PROGRAM); - - uint32_t src = ((m_dma_regs[0x001] & 0x3f) << 16) | m_dma_regs[0x000]; - uint32_t dst = m_dma_regs[0x003] & 0x3fff; - - for (uint32_t j = 0; j < len; j++) - { - mem.write_word((dst + j) & 0x3fff, mem.read_word(src + j)); - } - - src += len; - m_dma_regs[0x000] = (uint16_t)src; - m_dma_regs[0x001] = (src >> 16) & 0x3f; - m_dma_regs[0x002] = 0; - m_dma_regs[0x003] = (dst + len) & 0x3fff; -} - void spg2xx_device::configure_spg_io(spg2xx_io_device* io) { io->porta_in().set(FUNC(spg2xx_device::porta_r)); @@ -1019,7 +147,7 @@ void spg2xx_device::configure_spg_io(spg2xx_io_device* io) io->eeprom_r().set(FUNC(spg2xx_device::eepromx_r)); io->uart_tx().set(FUNC(spg2xx_device::tx_w)); io->chip_select().set(FUNC(spg2xx_device::cs_w)); - io->pal_read_callback().set(FUNC(spg2xx_device::get_pal_r)); + io->pal_read_callback().set(FUNC(spg2xx_device::get_pal_ntsc)); io->write_timer_irq_callback().set(FUNC(spg2xx_device::timerirq_w)); io->write_uart_adc_irq_callback().set(FUNC(spg2xx_device::uartirq_w)); io->write_external_irq_callback().set(FUNC(spg2xx_device::extirq_w)); @@ -1038,6 +166,13 @@ void spg24x_device::device_add_mconfig(machine_config &config) SPG24X_IO(config, m_spg_io, DERIVED_CLOCK(1, 1), m_cpu, m_screen); configure_spg_io(m_spg_io); + + SPG2XX_SYSDMA(config, m_spg_sysdma, DERIVED_CLOCK(1, 1), m_cpu); + + SPG24X_VIDEO(config, m_spg_video, DERIVED_CLOCK(1, 1), m_cpu, m_screen); + m_spg_video->sprlimit_read_callback().set(FUNC(spg24x_device::get_sprlimit)); + m_spg_video->rowscrolloffset_read_callback().set(FUNC(spg24x_device::get_rowscrolloffset)); + m_spg_video->write_video_irq_callback().set(FUNC(spg24x_device::videoirq_w)); } void spg28x_device::device_add_mconfig(machine_config &config) @@ -1051,4 +186,11 @@ void spg28x_device::device_add_mconfig(machine_config &config) SPG28X_IO(config, m_spg_io, DERIVED_CLOCK(1, 1), m_cpu, m_screen); configure_spg_io(m_spg_io); + + SPG2XX_SYSDMA(config, m_spg_sysdma, DERIVED_CLOCK(1, 1), m_cpu); + + SPG24X_VIDEO(config, m_spg_video, DERIVED_CLOCK(1, 1), m_cpu, m_screen); + m_spg_video->sprlimit_read_callback().set(FUNC(spg28x_device::get_sprlimit)); + m_spg_video->rowscrolloffset_read_callback().set(FUNC(spg28x_device::get_rowscrolloffset)); + m_spg_video->write_video_irq_callback().set(FUNC(spg28x_device::videoirq_w)); } diff --git a/src/devices/machine/spg2xx.h b/src/devices/machine/spg2xx.h index 859620d9f66..806e8395c4d 100644 --- a/src/devices/machine/spg2xx.h +++ b/src/devices/machine/spg2xx.h @@ -37,6 +37,8 @@ #include "cpu/unsp/unsp.h" #include "spg2xx_audio.h" #include "spg2xx_io.h" +#include "spg2xx_sysdma.h" +#include "spg2xx_video.h" #include "screen.h" class spg2xx_device : public device_t, public device_mixer_interface @@ -65,14 +67,16 @@ public: auto chip_select() { return m_chip_sel.bind(); } - uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - DECLARE_WRITE_LINE_MEMBER(vblank); - required_device m_spg_audio; required_device m_spg_io; + required_device m_spg_sysdma; + required_device m_spg_video; - void extint_w(int channel, bool state) { m_spg_io->extint_w(channel, state); }; - void uart_rx(uint8_t data) { m_spg_io->uart_rx(data); }; + void extint_w(int channel, bool state) { m_spg_io->extint_w(channel, state); } + void uart_rx(uint8_t data) { m_spg_io->uart_rx(data); } + + DECLARE_WRITE_LINE_MEMBER(vblank) { m_spg_video->vblank(state); } + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { return m_spg_video->screen_update(screen, bitmap, cliprect); } protected: spg2xx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const uint32_t sprite_limit) @@ -81,30 +85,7 @@ protected: m_sprite_limit = sprite_limit; } - enum - { - PAGE_ENABLE_MASK = 0x0008, - PAGE_WALLPAPER_MASK = 0x0004, - - SPRITE_ENABLE_MASK = 0x0001, - SPRITE_COORD_TL_MASK = 0x0002, - - PAGE_DEPTH_FLAG_MASK = 0x3000, - PAGE_DEPTH_FLAG_SHIFT = 12, - PAGE_TILE_HEIGHT_MASK = 0x00c0, - PAGE_TILE_HEIGHT_SHIFT = 6, - PAGE_TILE_WIDTH_MASK = 0x0030, - PAGE_TILE_WIDTH_SHIFT = 4, - TILE_X_FLIP = 0x0004, - TILE_Y_FLIP = 0x0008 - }; - - DECLARE_READ16_MEMBER(video_r); - DECLARE_WRITE16_MEMBER(video_w); - - DECLARE_READ16_MEMBER(dma_r); - DECLARE_WRITE16_MEMBER(dma_w); - + DECLARE_WRITE_LINE_MEMBER(videoirq_w); DECLARE_WRITE_LINE_MEMBER(audioirq_w); DECLARE_WRITE_LINE_MEMBER(timerirq_w); DECLARE_WRITE_LINE_MEMBER(uartirq_w); @@ -114,68 +95,18 @@ protected: DECLARE_READ16_MEMBER(space_r); - inline void check_video_irq(); - void spg2xx_map(address_map &map); - static const device_timer_id TIMER_SCREENPOS = 2; - - virtual void device_start() override; virtual void device_reset() override; - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - void do_cpu_dma(uint32_t len); - - void do_sprite_dma(uint32_t len); - - enum blend_enable_t : bool - { - BlendOff = false, - BlendOn = true - }; - - enum rowscroll_enable_t : bool - { - RowScrollOff = false, - RowScrollOn = true - }; - - enum flipx_t : bool - { - FlipXOff = false, - FlipXOn = true - }; - - void apply_saturation(const rectangle &cliprect); - void apply_fade(const rectangle &cliprect); - - template - void blit(const rectangle &cliprect, uint32_t line, uint32_t xoff, uint32_t yoff, uint32_t attr, uint32_t ctrl, uint32_t bitmap_addr, uint16_t tile); - void blit_page(const rectangle &cliprect, uint32_t scanline, int depth, uint32_t bitmap_addr, uint16_t *regs); - void blit_sprite(const rectangle &cliprect, uint32_t scanline, int depth, uint32_t base_addr); - void blit_sprites(const rectangle &cliprect, uint32_t scanline, int depth); - - uint8_t mix_channel(uint8_t a, uint8_t b); - - uint32_t m_screenbuf[320 * 240]; - uint8_t m_rgb5_to_rgb8[32]; - uint32_t m_rgb555_to_rgb888[0x8000]; - - bool m_hide_page0; - bool m_hide_page1; - bool m_hide_sprites; - bool m_debug_sprites; - bool m_debug_blit; - bool m_debug_palette; - uint8_t m_sprite_index_to_debug; - - uint16_t m_dma_regs[0x4]; - - uint16_t m_video_regs[0x100]; - uint32_t m_sprite_limit; - int m_rowscrolloffset; // auto racing in 'zone60' minigames needs this to be 15, the JAKKS games (Star Wars Revenge of the sith - Gunship Battle, Wheel of Fortune, Namco Ms. Pac-Man 5-in-1 Pole Position) need it to be 0, where does it come from? + // TODO: these are fixed values, put them in relevant devices? + uint16_t m_sprite_limit; + uint16_t m_rowscrolloffset; // auto racing in 'zone60' minigames needs this to be 15, the JAKKS games (Star Wars Revenge of the sith - Gunship Battle, Wheel of Fortune, Namco Ms. Pac-Man 5-in-1 Pole Position) need it to be 0, where does it come from? uint16_t m_pal_flag; + DECLARE_READ16_MEMBER(get_sprlimit) { return m_sprite_limit; } + DECLARE_READ16_MEMBER(get_rowscrolloffset) { return m_rowscrolloffset; } + DECLARE_READ16_MEMBER(get_pal_ntsc) { return m_pal_flag; } devcb_write16 m_porta_out; devcb_write16 m_portb_out; @@ -197,27 +128,22 @@ protected: required_device m_cpu; required_device m_screen; - required_shared_ptr m_scrollram; - required_shared_ptr m_paletteram; - required_shared_ptr m_spriteram; void configure_spg_io(spg2xx_io_device* io); - DECLARE_READ16_MEMBER(porta_r) { return m_porta_in(); }; - DECLARE_READ16_MEMBER(portb_r) { return m_portb_in(); }; - DECLARE_READ16_MEMBER(portc_r) { return m_portc_in(); }; - DECLARE_WRITE16_MEMBER(porta_w) { m_porta_out(offset, data, mem_mask); }; - DECLARE_WRITE16_MEMBER(portb_w) { m_portb_out(offset, data, mem_mask); }; - DECLARE_WRITE16_MEMBER(portc_w) { m_portc_out(offset, data, mem_mask); }; - template DECLARE_READ16_MEMBER(adc_r) { return m_adc_in[Line](); }; + DECLARE_READ16_MEMBER(porta_r) { return m_porta_in(); } + DECLARE_READ16_MEMBER(portb_r) { return m_portb_in(); } + DECLARE_READ16_MEMBER(portc_r) { return m_portc_in(); } + DECLARE_WRITE16_MEMBER(porta_w) { m_porta_out(offset, data, mem_mask); } + DECLARE_WRITE16_MEMBER(portb_w) { m_portb_out(offset, data, mem_mask); } + DECLARE_WRITE16_MEMBER(portc_w) { m_portc_out(offset, data, mem_mask); } + template DECLARE_READ16_MEMBER(adc_r) { return m_adc_in[Line](); } - DECLARE_WRITE8_MEMBER(eepromx_w) { m_eeprom_w(offset, data, mem_mask); }; + DECLARE_WRITE8_MEMBER(eepromx_w) { m_eeprom_w(offset, data, mem_mask); } DECLARE_READ8_MEMBER(eepromx_r) { return m_eeprom_r(); }; - DECLARE_WRITE8_MEMBER(tx_w) { m_uart_tx(offset, data, mem_mask); }; - DECLARE_WRITE8_MEMBER(cs_w) { m_chip_sel(offset, data, mem_mask); }; - DECLARE_READ16_MEMBER(get_pal_r) { return m_pal_flag; }; - + DECLARE_WRITE8_MEMBER(tx_w) { m_uart_tx(offset, data, mem_mask); } + DECLARE_WRITE8_MEMBER(cs_w) { m_chip_sel(offset, data, mem_mask); } }; class spg24x_device : public spg2xx_device diff --git a/src/devices/machine/spg2xx_io.cpp b/src/devices/machine/spg2xx_io.cpp index 404087f6a7f..f8daaef5bd0 100644 --- a/src/devices/machine/spg2xx_io.cpp +++ b/src/devices/machine/spg2xx_io.cpp @@ -27,7 +27,7 @@ DEFINE_DEVICE_TYPE(SPG28X_IO, spg28x_io_device, "spg28x_io", "SPG280-series Syst #define LOG_EXT_MEM (1U << 27) #define LOG_EXTINT (1U << 28) #define LOG_IO (LOG_IO_READS | LOG_IO_WRITES | LOG_IRQS | LOG_GPIO | LOG_UART | LOG_I2C | LOG_TIMERS | LOG_EXTINT | LOG_UNKNOWN_IO) -#define LOG_ALL (LOG_IO | LOG_VLINES | LOG_SEGMENT | LOG_FIQ) +#define LOG_ALL (LOG_IO | LOG_VLINES | LOG_SEGMENT | LOG_WATCHDOG | LOG_FIQ | LOG_SIO | LOG_EXT_MEM) #define VERBOSE (0) #include "logmacro.h" diff --git a/src/devices/machine/spg2xx_sysdma.cpp b/src/devices/machine/spg2xx_sysdma.cpp new file mode 100644 index 00000000000..fe0d61ea395 --- /dev/null +++ b/src/devices/machine/spg2xx_sysdma.cpp @@ -0,0 +1,117 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/***************************************************************************** + + SunPlus SPG2xx-series SoC peripheral emulation (System DMA) + +**********************************************************************/ + +#include "emu.h" +#include "spg2xx_sysdma.h" + +DEFINE_DEVICE_TYPE(SPG2XX_SYSDMA, spg2xx_sysdma_device, "spg2xx_sysdma", "SPG240-series System-on-a-Chip System DMA") + +#define LOG_DMA (1U << 9) +#define LOG_ALL (LOG_DMA) + +#define VERBOSE (0) +#include "logmacro.h" + + + +spg2xx_sysdma_device::spg2xx_sysdma_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SPG2XX_SYSDMA, tag, owner, clock) + , m_cpu(*this, finder_base::DUMMY_TAG) +{ +} + +void spg2xx_sysdma_device::device_start() +{ + save_item(NAME(m_dma_regs)); +} + +void spg2xx_sysdma_device::device_reset() +{ + memset(m_dma_regs, 0, 0x4 * sizeof(uint16_t)); +} + +/************************* +* Machine Hardware * +*************************/ + +READ16_MEMBER(spg2xx_sysdma_device::dma_r) +{ + offset &= 0x3; + + uint16_t val = m_dma_regs[offset]; + switch (offset) + { + + case 0x000: // DMA Source (L) + LOGMASKED(LOG_DMA, "dma_r: DMA Source (lo) = %04x\n", val); + break; + + case 0x001: // DMA Source (H) + LOGMASKED(LOG_DMA, "dma_r: DMA Source (hi) = %04x\n", val); + break; + + case 0x002: // DMA Length + LOGMASKED(LOG_DMA, "dma_r: DMA Length = %04x\n", 0); + val = 0; + break; + + case 0x003: // DMA Destination + LOGMASKED(LOG_DMA, "dma_r: DMA Dest = %04x\n", val); + break; + } + + return val; +} + +WRITE16_MEMBER(spg2xx_sysdma_device::dma_w) +{ + offset &= 0x3; + + switch (offset) + { + case 0x000: // DMA Source (lo) + LOGMASKED(LOG_DMA, "dma_w: DMA Source (lo) = %04x\n", data); + m_dma_regs[offset] = data; + break; + + case 0x001: // DMA Source (hi) + LOGMASKED(LOG_DMA, "dma_w: DMA Source (hi) = %04x\n", data); + m_dma_regs[offset] = data; + break; + + case 0x002: // DMA Length + LOGMASKED(LOG_DMA, "dma_w: DMA Length = %04x\n", data); + if (!(data & 0xc000)) // jak_dora writes 0xffff here which ends up trashing registers etc. why? such writes can't be valid + do_cpu_dma(data); + break; + + case 0x003: // DMA Destination + LOGMASKED(LOG_DMA, "dma_w: DMA Dest = %04x\n", data); + m_dma_regs[offset] = data; + break; + } +} + +void spg2xx_sysdma_device::do_cpu_dma(uint32_t len) +{ + address_space &mem = m_cpu->space(AS_PROGRAM); + + uint32_t src = ((m_dma_regs[0x001] & 0x3f) << 16) | m_dma_regs[0x000]; + uint32_t dst = m_dma_regs[0x003] & 0x3fff; + + for (uint32_t j = 0; j < len; j++) + { + mem.write_word((dst + j) & 0x3fff, mem.read_word(src + j)); + } + + src += len; + m_dma_regs[0x000] = (uint16_t)src; + m_dma_regs[0x001] = (src >> 16) & 0x3f; + m_dma_regs[0x002] = 0; + m_dma_regs[0x003] = (dst + len) & 0x3fff; +} diff --git a/src/devices/machine/spg2xx_sysdma.h b/src/devices/machine/spg2xx_sysdma.h new file mode 100644 index 00000000000..bea7755bcd9 --- /dev/null +++ b/src/devices/machine/spg2xx_sysdma.h @@ -0,0 +1,46 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/***************************************************************************** + + SunPlus SPG2xx-series SoC peripheral emulation (System DMA) + +**********************************************************************/ + +#ifndef MAME_MACHINE_SPG2XX_SYSDMA_H +#define MAME_MACHINE_SPG2XX_SYSDMA_H + +#pragma once + +#include "cpu/unsp/unsp.h" + +DECLARE_DEVICE_TYPE(SPG2XX_SYSDMA, spg2xx_sysdma_device) + + +class spg2xx_sysdma_device : public device_t +{ +public: + template + spg2xx_sysdma_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&cpu_tag) + : spg2xx_sysdma_device(mconfig, tag, owner, clock) + { + m_cpu.set_tag(std::forward(cpu_tag)); + } + + spg2xx_sysdma_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + DECLARE_READ16_MEMBER(dma_r); + DECLARE_WRITE16_MEMBER(dma_w); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + void do_cpu_dma(uint32_t len); + + uint16_t m_dma_regs[0x4]; + + required_device m_cpu; +}; + + +#endif // MAME_MACHINE_SPG2XX_SYSDMA_H diff --git a/src/devices/machine/spg2xx_video.cpp b/src/devices/machine/spg2xx_video.cpp new file mode 100644 index 00000000000..daef752bc82 --- /dev/null +++ b/src/devices/machine/spg2xx_video.cpp @@ -0,0 +1,828 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/***************************************************************************** + + SunPlus SPG2xx-series SoC peripheral emulation (Video) + +**********************************************************************/ + +#include "emu.h" +#include "spg2xx_video.h" + +DEFINE_DEVICE_TYPE(SPG24X_VIDEO, spg24x_video_device, "spg24x_video", "SPG240-series System-on-a-Chip (Video)") + +#define LOG_IRQS (1U << 4) +#define LOG_VLINES (1U << 5) +#define LOG_DMA (1U << 9) +#define LOG_PPU_READS (1U << 22) +#define LOG_PPU_WRITES (1U << 23) +#define LOG_UNKNOWN_PPU (1U << 24) +#define LOG_PPU (LOG_PPU_READS | LOG_PPU_WRITES | LOG_UNKNOWN_PPU) +#define LOG_ALL (LOG_IRQS | LOG_PPU | LOG_VLINES | LOG_DMA ) + +#define VERBOSE (0) +#include "logmacro.h" + +#define SPG_DEBUG_VIDEO (0) + +#define VIDEO_IRQ_ENABLE m_video_regs[0x62] +#define VIDEO_IRQ_STATUS m_video_regs[0x63] + +spg2xx_video_device::spg2xx_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_sprlimit_read_cb(*this) + , m_rowscrolloffset_read_cb(*this) + , m_cpu(*this, finder_base::DUMMY_TAG) + , m_screen(*this, finder_base::DUMMY_TAG) + , m_scrollram(*this, "scrollram") + , m_paletteram(*this, "paletteram") + , m_spriteram(*this, "spriteram") + , m_video_irq_cb(*this) +{ +} + +spg24x_video_device::spg24x_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : spg2xx_video_device(mconfig, SPG24X_VIDEO, tag, owner, clock) +{ +} + +void spg2xx_video_device::device_start() +{ + for (uint8_t i = 0; i < 32; i++) + { + m_rgb5_to_rgb8[i] = (i << 3) | (i >> 2); + } + for (uint16_t i = 0; i < 0x8000; i++) + { + m_rgb555_to_rgb888[i] = (m_rgb5_to_rgb8[(i >> 10) & 0x1f] << 16) | + (m_rgb5_to_rgb8[(i >> 5) & 0x1f] << 8) | + (m_rgb5_to_rgb8[(i >> 0) & 0x1f] << 0); + } + + m_screenpos_timer = timer_alloc(TIMER_SCREENPOS); + m_screenpos_timer->adjust(attotime::never); + + save_item(NAME(m_hide_page0)); + save_item(NAME(m_hide_page1)); + save_item(NAME(m_hide_sprites)); + save_item(NAME(m_debug_sprites)); + save_item(NAME(m_debug_blit)); + save_item(NAME(m_debug_palette)); + save_item(NAME(m_sprite_index_to_debug)); + + save_item(NAME(m_video_regs)); + + m_sprlimit_read_cb.resolve_safe(0); + m_rowscrolloffset_read_cb.resolve_safe(0); + + m_video_irq_cb.resolve(); + +} + +void spg2xx_video_device::device_reset() +{ + memset(m_video_regs, 0, 0x100 * sizeof(uint16_t)); + + m_video_regs[0x36] = 0xffff; + m_video_regs[0x37] = 0xffff; + m_video_regs[0x3c] = 0x0020; + m_video_regs[0x42] = 0x0001; + + m_hide_page0 = false; + m_hide_page1 = false; + m_hide_sprites = false; + m_debug_sprites = false; + m_debug_blit = false; + m_debug_palette = false; + m_sprite_index_to_debug = 0; +} + +/************************* +* Video Hardware * +*************************/ + +// Perform a lerp between a and b +inline uint8_t spg2xx_video_device::mix_channel(uint8_t bottom, uint8_t top) +{ + uint8_t alpha = (m_video_regs[0x2a] & 3) << 6; + return ((256 - alpha) * bottom + alpha * top) >> 8; +} + +template +void spg2xx_video_device::draw(const rectangle &cliprect, uint32_t line, uint32_t xoff, uint32_t yoff, uint32_t bitmap_addr, uint16_t tile, int32_t h, int32_t w, uint8_t bpp, uint32_t yflipmask, uint32_t palette_offset) +{ + address_space &space = m_cpu->space(AS_PROGRAM); + + uint32_t nc_bpp = ((bpp) + 1) << 1; + + if (SPG_DEBUG_VIDEO && m_debug_blit) + { + printf("s:%d line:%d xy:%08x,%08x bitmap_addr:%08x tile:%04x\n", cliprect.min_x, line, xoff, yoff, bitmap_addr, tile); + printf("hw:%d,%d yfm:%d nc_bppols:%d pobs:%02x ", w, h, yflipmask, nc_bpp, palette_offset); + } + palette_offset >>= nc_bpp; + palette_offset <<= nc_bpp; + if (SPG_DEBUG_VIDEO && m_debug_blit) + { + printf("poas:%02x\n", palette_offset); + } + + uint32_t bits_per_row = nc_bpp * w / 16; + uint32_t words_per_tile = bits_per_row * h; + uint32_t m = bitmap_addr + words_per_tile * tile + bits_per_row * (line ^ yflipmask); + uint32_t bits = 0; + uint32_t nbits = 0; + uint32_t y = line; + + int yy = (yoff + y) & 0x1ff; + if (yy >= 0x01c0) + yy -= 0x0200; + + if (yy > 240 || yy < 0) + return; + + if (SPG_DEBUG_VIDEO && m_debug_blit) + printf("%3d:\n", yy); + + int y_index = yy * 320; + + for (int32_t x = FlipX ? (w - 1) : 0; FlipX ? x >= 0 : x < w; FlipX ? x-- : x++) + { + int xx = xoff + x; + + bits <<= nc_bpp; + if (SPG_DEBUG_VIDEO && m_debug_blit) + printf(" %08x:%d ", bits, nbits); + if (nbits < nc_bpp) + { + uint16_t b = space.read_word(m++ & 0x3fffff); + b = (b << 8) | (b >> 8); + bits |= b << (nc_bpp - nbits); + nbits += 16; + if (SPG_DEBUG_VIDEO && m_debug_blit) + printf("(%04x:%08x:%d) ", b, bits, nbits); + } + nbits -= nc_bpp; + + uint32_t pal = palette_offset + (bits >> 16); + if (SPG_DEBUG_VIDEO && m_debug_blit) + printf("%02x:%02x:%04x ", bits >> 16, pal, bits & 0xffff); + bits &= 0xffff; + + if (RowScroll) + xx -= (int16_t)m_scrollram[(yy + m_rowscrolloffset_read_cb()) & 0x1ff]; + + xx &= 0x01ff; + if (xx >= 0x01c0) + xx -= 0x0200; + + if (xx >= 0 && xx < 320) + { + int pix_index = xx + y_index; + + uint16_t rgb = m_paletteram[pal]; + if (SPG_DEBUG_VIDEO && m_debug_blit) + printf("rgb:%04x ", rgb); + + if (!(rgb & 0x8000)) + { + if (Blend) + { + if (SPG_DEBUG_VIDEO && m_debug_blit) + printf("M\n"); + m_screenbuf[pix_index] = (mix_channel((uint8_t)(m_screenbuf[pix_index] >> 16), m_rgb5_to_rgb8[(rgb >> 10) & 0x1f]) << 16) | + (mix_channel((uint8_t)(m_screenbuf[pix_index] >> 8), m_rgb5_to_rgb8[(rgb >> 5) & 0x1f]) << 8) | + (mix_channel((uint8_t)(m_screenbuf[pix_index] >> 0), m_rgb5_to_rgb8[rgb & 0x1f])); + } + else + { + if (SPG_DEBUG_VIDEO && m_debug_blit) + printf("S\n"); + m_screenbuf[pix_index] = m_rgb555_to_rgb888[rgb]; + } + } + else if (SPG_DEBUG_VIDEO && m_debug_blit) + { + printf("X\n"); + } + } + } +} + +void spg2xx_video_device::draw_page(const rectangle &cliprect, uint32_t scanline, int priority, uint32_t bitmap_addr, uint16_t *regs) +{ + uint32_t xscroll = regs[0]; + uint32_t yscroll = regs[1]; + uint32_t attr = regs[2]; + uint32_t ctrl = regs[3]; + uint32_t tilemap = regs[4]; + uint32_t palette_map = regs[5]; + address_space &space = m_cpu->space(AS_PROGRAM); + + if (!(ctrl & PAGE_ENABLE_MASK)) + { + return; + } + + if (((attr & PAGE_PRIORITY_FLAG_MASK) >> PAGE_PRIORITY_FLAG_SHIFT) != priority) + { + return; + } + + uint32_t tile_h = 8 << ((attr & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); + uint32_t tile_w = 8 << ((attr & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); + + uint32_t tile_count_x = 512 / tile_w; + + uint32_t bitmap_y = (scanline + yscroll) & 0xff; + uint32_t y0 = bitmap_y / tile_h; + uint32_t tile_scanline = bitmap_y % tile_h; + uint32_t tile_address = tile_count_x * y0; + if (SPG_DEBUG_VIDEO && machine().input().code_pressed(KEYCODE_H)) + printf("s:%3d | baddr:%08x | yscr:%3d | bity:%3d | y0:%2d | ts:%2d\n", scanline, bitmap_addr, yscroll, bitmap_y, y0, tile_scanline); + + if (SPG_DEBUG_VIDEO && machine().input().code_pressed(KEYCODE_EQUALS)) + m_debug_blit = true; + for (uint32_t x0 = 0; x0 < tile_count_x; x0++, tile_address++) + { + uint32_t yy = ((tile_h * y0 - yscroll + 0x10) & 0xff) - 0x10; + uint32_t xx = (tile_w * x0 - xscroll) & 0x1ff; + uint16_t tile = (ctrl & PAGE_WALLPAPER_MASK) ? space.read_word(tilemap) : space.read_word(tilemap + tile_address); + uint16_t palette = 0; + + if (!tile) + continue; + + palette = (ctrl & PAGE_WALLPAPER_MASK) ? space.read_word(palette_map) : space.read_word(palette_map + tile_address / 2); + if (x0 & 1) + palette >>= 8; + + uint32_t tileattr = attr; + uint32_t tilectrl = ctrl; + if ((ctrl & 2) == 0) + { // -(1) bld(1) flip(2) pal(4) + tileattr &= ~0x000c; + tileattr |= (palette >> 2) & 0x000c; // flip + + tileattr &= ~0x0f00; + tileattr |= (palette << 8) & 0x0f00; // palette + + tilectrl &= ~0x0100; + tilectrl |= (palette << 2) & 0x0100; // blend + } + + const bool blend = (tileattr & 0x4000 || tilectrl & 0x0100); + const bool row_scroll = (tilectrl & 0x0010); + const bool flip_x = (tileattr & TILE_X_FLIP); + const uint32_t yflipmask = tileattr & TILE_Y_FLIP ? tile_h - 1 : 0; + const uint32_t palette_offset = (tileattr & 0x0f00) >> 4; + + const uint8_t bpp = tileattr & 0x0003; + + if (blend) + { + if (row_scroll) + { + if (flip_x) + draw(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset); + else + draw(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset); + } + else + { + if (flip_x) + draw(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset); + else + draw(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset); + } + } + else + { + if (row_scroll) + { + if (flip_x) + draw(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset); + else + draw(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset); + } + else + { + if (flip_x) + draw(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset); + else + draw(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset); + } + } + } + if (SPG_DEBUG_VIDEO && machine().input().code_pressed(KEYCODE_EQUALS)) + m_debug_blit = false; +} + +void spg2xx_video_device::draw_sprite(const rectangle &cliprect, uint32_t scanline, int priority, uint32_t base_addr) +{ + uint32_t bitmap_addr = 0x40 * m_video_regs[0x22]; + uint16_t tile = m_spriteram[base_addr + 0]; + int16_t x = m_spriteram[base_addr + 1]; + int16_t y = m_spriteram[base_addr + 2]; + uint16_t attr = m_spriteram[base_addr + 3]; + + if (!tile) + { + return; + } + + if (((attr & PAGE_PRIORITY_FLAG_MASK) >> PAGE_PRIORITY_FLAG_SHIFT) != priority) + { + return; + } + + const uint32_t h = 8 << ((attr & PAGE_TILE_HEIGHT_MASK) >> PAGE_TILE_HEIGHT_SHIFT); + const uint32_t w = 8 << ((attr & PAGE_TILE_WIDTH_MASK) >> PAGE_TILE_WIDTH_SHIFT); + + if (!(m_video_regs[0x42] & SPRITE_COORD_TL_MASK)) + { + x = (160 + x) - w / 2; + y = (120 - y) - (h / 2) + 8; + } + + x &= 0x01ff; + y &= 0x01ff; + + uint32_t tile_line = ((scanline - y) + 0x200) % h; + int16_t test_y = (y + tile_line) & 0x1ff; + if (test_y >= 0x01c0) + test_y -= 0x0200; + + if (test_y != scanline) + { + return; + } + + bool blend = (attr & 0x4000); + bool flip_x = (attr & TILE_X_FLIP); + const uint8_t bpp = attr & 0x0003; + const uint32_t yflipmask = attr & TILE_Y_FLIP ? h - 1 : 0; + const uint32_t palette_offset = (attr & 0x0f00) >> 4; + +#if SPG_DEBUG_VIDEO + if (m_debug_sprites && machine().input().code_pressed(KEYCODE_MINUS)) + m_debug_blit = true; + if (blend) + { + if (flip_x) + draw(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset); + else + draw(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset); + } + else + { + if (flip_x) + draw(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset); + else + draw(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset); + } + m_debug_blit = false; +#else + if (blend) + { + if (flip_x) + draw(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset); + else + draw(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset); + } + else + { + if (flip_x) + draw(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset); + else + draw(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset); + } +#endif +} + +void spg2xx_video_device::draw_sprites(const rectangle &cliprect, uint32_t scanline, int priority) +{ + if (!(m_video_regs[0x42] & SPRITE_ENABLE_MASK)) + { + return; + } + +#if SPG_DEBUG_VIDEO + if (!m_debug_sprites) + { +#endif + for (uint32_t n = 0; n < m_sprlimit_read_cb(); n++) + { + draw_sprite(cliprect, scanline, priority, 4 * n); + } +#if SPG_DEBUG_VIDEO + } + else + { + draw_sprite(cliprect, scanline, priority, 4 * m_sprite_index_to_debug); + } +#endif +} + +void spg2xx_video_device::apply_saturation(const rectangle &cliprect) +{ + static const float s_u8_to_float = 1.0f / 255.0f; + static const float s_gray_r = 0.299f; + static const float s_gray_g = 0.587f; + static const float s_gray_b = 0.114f; + const float sat_adjust = (0xff - (m_video_regs[0x3c] & 0x00ff)) / (float)(0xff - 0x20); + for (int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + uint32_t *src = &m_screenbuf[cliprect.min_x + 320 * y]; + for (int x = cliprect.min_x; x <= cliprect.max_x; x++) + { + const uint32_t src_rgb = *src; + const float src_r = (uint8_t)(src_rgb >> 16) * s_u8_to_float; + const float src_g = (uint8_t)(src_rgb >> 8) * s_u8_to_float; + const float src_b = (uint8_t)(src_rgb >> 0) * s_u8_to_float; + const float luma = src_r * s_gray_r + src_g * s_gray_g + src_b * s_gray_b; + const float adjusted_r = luma + (src_r - luma) * sat_adjust; + const float adjusted_g = luma + (src_g - luma) * sat_adjust; + const float adjusted_b = luma + (src_b - luma) * sat_adjust; + const int integer_r = (int)floor(adjusted_r * 255.0f); + const int integer_g = (int)floor(adjusted_g * 255.0f); + const int integer_b = (int)floor(adjusted_b * 255.0f); + *src++ = (integer_r > 255 ? 0xff0000 : (integer_r < 0 ? 0 : ((uint8_t)integer_r << 16))) | + (integer_g > 255 ? 0x00ff00 : (integer_g < 0 ? 0 : ((uint8_t)integer_g << 8))) | + (integer_b > 255 ? 0x0000ff : (integer_b < 0 ? 0 : (uint8_t)integer_b)); + } + } +} + +void spg2xx_video_device::apply_fade(const rectangle &cliprect) +{ + const uint16_t fade_offset = m_video_regs[0x30]; + for (int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + uint32_t *src = &m_screenbuf[cliprect.min_x + 320 * y]; + for (int x = cliprect.min_x; x <= cliprect.max_x; x++) + { + const uint32_t src_rgb = *src; + const uint8_t src_r = (src_rgb >> 16) & 0xff; + const uint8_t src_g = (src_rgb >> 8) & 0xff; + const uint8_t src_b = (src_rgb >> 0) & 0xff; + const uint8_t r = src_r - fade_offset; + const uint8_t g = src_g - fade_offset; + const uint8_t b = src_b - fade_offset; + *src++ = (r > src_r ? 0 : (r << 16)) | + (g > src_g ? 0 : (g << 8)) | + (b > src_b ? 0 : (b << 0)); + } + } +} + +uint32_t spg2xx_video_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + memset(&m_screenbuf[320 * cliprect.min_y], 0, 4 * 320 * ((cliprect.max_y - cliprect.min_y) + 1)); + + const uint32_t page1_addr = 0x40 * m_video_regs[0x20]; + const uint32_t page2_addr = 0x40 * m_video_regs[0x21]; + uint16_t *page1_regs = m_video_regs + 0x10; + uint16_t *page2_regs = m_video_regs + 0x16; + + for (uint32_t scanline = (uint32_t)cliprect.min_y; scanline <= (uint32_t)cliprect.max_y; scanline++) + { + for (int i = 0; i < 4; i++) + { + if (!SPG_DEBUG_VIDEO || !m_hide_page0) + draw_page(cliprect, scanline, i, page1_addr, page1_regs); + if (!SPG_DEBUG_VIDEO || !m_hide_page1) + draw_page(cliprect, scanline, i, page2_addr, page2_regs); + if (!SPG_DEBUG_VIDEO || !m_hide_sprites) + draw_sprites(cliprect, scanline, i); + } + } + + if ((m_video_regs[0x3c] & 0x00ff) != 0x0020) + { + apply_saturation(cliprect); + } + + if (m_video_regs[0x30] != 0) + { + apply_fade(cliprect); + } + + for (int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + uint32_t *dest = &bitmap.pix32(y, cliprect.min_x); + uint32_t *src = &m_screenbuf[cliprect.min_x + 320 * y]; + memcpy(dest, src, sizeof(uint32_t) * ((cliprect.max_x - cliprect.min_x) + 1)); + } + + if (SPG_DEBUG_VIDEO && m_debug_palette) + { + for (int y = cliprect.min_y; y <= cliprect.max_y && y < 128; y++) + { + const uint16_t high_nybble = (y / 8) << 4; + uint32_t *dest = &bitmap.pix32(y, cliprect.min_x); + for (int x = cliprect.min_x; x <= cliprect.max_x && x < 256; x++) + { + const uint16_t low_nybble = x / 16; + const uint16_t palette_entry = high_nybble | low_nybble; + const uint16_t color = m_paletteram[palette_entry]; + if (!(color & 0x8000)) + { + *dest = m_rgb555_to_rgb888[color & 0x7fff]; + } + dest++; + } + } + } + + return 0; +} + +void spg2xx_video_device::do_sprite_dma(uint32_t len) +{ + address_space &mem = m_cpu->space(AS_PROGRAM); + + uint32_t src = m_video_regs[0x70] & 0x3fff; + uint32_t dst = m_video_regs[0x71]; + + for (uint32_t j = 0; j < len; j++) + { + m_spriteram[(dst + j) & 0x3ff] = mem.read_word(src + j); + } + + m_video_regs[0x72] = 0; + if (VIDEO_IRQ_ENABLE & 4) + { + const uint16_t old = VIDEO_IRQ_STATUS; + VIDEO_IRQ_STATUS |= 4; + const uint16_t changed = old ^ (VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS); + if (changed) + check_video_irq(); + } +} + +READ16_MEMBER(spg2xx_video_device::video_r) +{ + switch (offset) + { + case 0x38: // Current Line + LOGMASKED(LOG_VLINES, "video_r: Current Line: %04x\n", m_screen->vpos()); + return m_screen->vpos(); + + case 0x62: // Video IRQ Enable + LOGMASKED(LOG_IRQS, "video_r: Video IRQ Enable: %04x\n", VIDEO_IRQ_ENABLE); + return VIDEO_IRQ_ENABLE; + + case 0x63: // Video IRQ Status + LOGMASKED(LOG_IRQS, "video_r: Video IRQ Status: %04x\n", VIDEO_IRQ_STATUS); + return VIDEO_IRQ_STATUS; + + default: + LOGMASKED(LOG_UNKNOWN_PPU, "video_r: Unknown register %04x = %04x\n", 0x2800 + offset, m_video_regs[offset]); + break; + } + return m_video_regs[offset]; +} + +WRITE16_MEMBER(spg2xx_video_device::video_w) +{ + switch (offset) + { + case 0x10: // Page 1 X scroll + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 X Scroll = %04x\n", data & 0x01ff); + m_video_regs[offset] = data & 0x01ff; + break; + + case 0x11: // Page 1 Y scroll + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Y Scroll = %04x\n", data & 0x00ff); + m_video_regs[offset] = data & 0x00ff; + break; + + case 0x12: // Page 1 Attributes + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Attributes = %04x (Depth:%d, Palette:%d, VSize:%d, HSize:%d, FlipY:%d, FlipX:%d, BPP:%d)\n", data + , (data >> 12) & 3, (data >> 8) & 15, 8 << ((data >> 6) & 3), 8 << ((data >> 4) & 3), BIT(data, 3), BIT(data, 2), 2 * ((data & 3) + 1)); + m_video_regs[offset] = data; + break; + + case 0x13: // Page 1 Control + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Control = %04x (Blend:%d, HiColor:%d, RowScroll:%d, Enable:%d, Wallpaper:%d, RegSet:%d, Bitmap:%d)\n", data + , BIT(data, 8), BIT(data, 7), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0)); + m_video_regs[offset] = data; + break; + + case 0x14: // Page 1 Tile Address + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Tile Address = %04x\n", data & 0x1fff); + m_video_regs[offset] = data; + break; + + case 0x15: // Page 1 Attribute Address + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Attribute Address = %04x\n", data & 0x1fff); + m_video_regs[offset] = data; + break; + + case 0x16: // Page 2 X scroll + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 X Scroll = %04x\n", data & 0x01ff); + m_video_regs[offset] = data & 0x01ff; + break; + + case 0x17: // Page 2 Y scroll + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Y Scroll: %04x = %04x\n", 0x2800 | offset, data & 0x00ff); + m_video_regs[offset] = data & 0x00ff; + break; + + case 0x18: // Page 2 Attributes + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Attributes = %04x (Depth:%d, Palette:%d, VSize:%d, HSize:%d, FlipY:%d, FlipX:%d, BPP:%d)\n", data + , (data >> 12) & 3, (data >> 8) & 15, 8 << ((data >> 6) & 3), 8 << ((data >> 4) & 3), BIT(data, 3), BIT(data, 2), 2 * ((data & 3) + 1)); + m_video_regs[offset] = data; + break; + + case 0x19: // Page 2 Control + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Control = %04x (Blend:%d, HiColor:%d, RowScroll:%d, Enable:%d, Wallpaper:%d, RegSet:%d, Bitmap:%d)\n", data + , BIT(data, 8), BIT(data, 7), BIT(data, 4), BIT(data, 3), BIT(data, 2), BIT(data, 1), BIT(data, 0)); + m_video_regs[offset] = data; + break; + + case 0x1a: // Page 2 Tile Address + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Tile Address = %04x\n", data & 0x1fff); + m_video_regs[offset] = data; + break; + + case 0x1b: // Page 2 Attribute Address + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Attribute Address = %04x\n", data & 0x1fff); + m_video_regs[offset] = data; + break; + + case 0x20: // Page 1 Segment Address + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 1 Segment Address = %04x\n", data); + m_video_regs[offset] = data; + break; + + case 0x21: // Page 2 Segment Address + LOGMASKED(LOG_PPU_WRITES, "video_w: Page 2 Segment Address = %04x\n", data); + m_video_regs[offset] = data; + break; + + case 0x22: // Sprite Segment Address + LOGMASKED(LOG_PPU_WRITES, "video_w: Sprite Segment Address = %04x\n", data); + m_video_regs[offset] = data; + break; + + case 0x2a: // Blend Level Control + LOGMASKED(LOG_PPU_WRITES, "video_w: Blend Level Control = %04x\n", data & 0x0003); + m_video_regs[offset] = data & 0x0003; + break; + + case 0x30: // Fade Effect Control + LOGMASKED(LOG_PPU_WRITES, "video_w: Fade Effect Control = %04x\n", data & 0x00ff); + m_video_regs[offset] = data & 0x00ff; + break; + + case 0x36: // IRQ pos V + case 0x37: // IRQ pos H + m_video_regs[offset] = data & 0x01ff; + LOGMASKED(LOG_IRQS, "video_w: Video IRQ Position: %04x,%04x (%04x)\n", m_video_regs[0x37], m_video_regs[0x36], 0x2800 | offset); + if (m_video_regs[0x37] < 160 && m_video_regs[0x36] < 240) + m_screenpos_timer->adjust(m_screen->time_until_pos(m_video_regs[0x36], m_video_regs[0x37] << 1)); + else + m_screenpos_timer->adjust(attotime::never); + break; + + case 0x39: // Latch 1st Line Pen Pulse + LOGMASKED(LOG_PPU_WRITES, "video_w: Latch 1st Line Pen Pulse = %04x\n", data & 0x0001); + m_video_regs[offset] = data & 0x0001; + break; + + case 0x3c: // TV Control 1 + LOGMASKED(LOG_PPU_WRITES, "video_w: TV Control 1 = %04x (Hue:%02x, Saturation:%02x)\n", data, data >> 8, data & 0x00ff); + m_video_regs[offset] = data; + break; + + case 0x3d: // TV Control 2 + { + static const char* const s_lpf_mode[4] = { "LPF1", "LPF2", "All", "Edge" }; + LOGMASKED(LOG_PPU_WRITES, "video_w: TV Control 2 = %04x (LPFMode:%s, Enable:%d, Interlace:%d)\n", data & 0x000f + , s_lpf_mode[(data >> 2) & 3], BIT(data, 1), BIT(data, 0)); + m_video_regs[offset] = data & 0x000f; + break; + } + + case 0x3e: // Light Pen Y Position + LOGMASKED(LOG_PPU_WRITES, "video_w: Light Pen Y (read only) = %04x\n", data & 0x01ff); + break; + + case 0x3f: // Light Pen YXPosition + LOGMASKED(LOG_PPU_WRITES, "video_w: Light Pen X (read only) = %04x\n", data & 0x01ff); + break; + + case 0x42: // Sprite Control + LOGMASKED(LOG_PPU_WRITES, "video_w: Sprite Control = %04x (TopLeft:%d, Enable:%d)\n", data & 0x0003, BIT(data, 1), BIT(data, 0)); + m_video_regs[offset] = data & 0x0003; + break; + + case 0x62: // Video IRQ Enable + { + LOGMASKED(LOG_IRQS, "video_w: Video IRQ Enable = %04x (DMA:%d, Timing:%d, Blanking:%d)\n", data & 0x0007, BIT(data, 2), BIT(data, 1), BIT(data, 0)); + const uint16_t old = VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS; + VIDEO_IRQ_ENABLE = data & 0x0007; + const uint16_t changed = old ^ (VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS); + if (changed) + check_video_irq(); + break; + } + + case 0x63: // Video IRQ Acknowledge + { + LOGMASKED(LOG_IRQS, "video_w: Video IRQ Acknowledge = %04x\n", data); + const uint16_t old = VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS; + VIDEO_IRQ_STATUS &= ~data; + const uint16_t changed = old ^ (VIDEO_IRQ_ENABLE & VIDEO_IRQ_STATUS); + if (changed) + check_video_irq(); + break; + } + + case 0x70: // Sprite DMA Source + LOGMASKED(LOG_DMA, "video_w: Sprite DMA Source = %04x\n", data & 0x3fff); + m_video_regs[offset] = data & 0x3fff; + break; + + case 0x71: // Sprite DMA Dest + LOGMASKED(LOG_DMA, "video_w: Sprite DMA Dest = %04x\n", data & 0x03ff); + m_video_regs[offset] = data & 0x03ff; + break; + + case 0x72: // Sprite DMA Length + { + LOGMASKED(LOG_DMA, "video_w: Sprite DMA Length = %04x\n", data & 0x03ff); + uint16_t length = data & 0x3ff; + do_sprite_dma(length ? length : 0x400); + break; + } + + default: + LOGMASKED(LOG_UNKNOWN_PPU, "video_w: Unknown register %04x = %04x\n", 0x2800 + offset, data); + m_video_regs[offset] = data; + break; + } +} + +WRITE_LINE_MEMBER(spg2xx_video_device::vblank) +{ + if (!state) + { + VIDEO_IRQ_STATUS &= ~1; + LOGMASKED(LOG_IRQS, "Setting video IRQ status to %04x\n", VIDEO_IRQ_STATUS); + check_video_irq(); + return; + } + +#if SPG_DEBUG_VIDEO + if (machine().input().code_pressed_once(KEYCODE_5)) + m_hide_page0 = !m_hide_page0; + if (machine().input().code_pressed_once(KEYCODE_6)) + m_hide_page1 = !m_hide_page1; + if (machine().input().code_pressed_once(KEYCODE_7)) + m_hide_sprites = !m_hide_sprites; + if (machine().input().code_pressed_once(KEYCODE_8)) + m_debug_sprites = !m_debug_sprites; + if (machine().input().code_pressed_once(KEYCODE_9)) + m_sprite_index_to_debug--; + if (machine().input().code_pressed_once(KEYCODE_0)) + m_sprite_index_to_debug++; + if (machine().input().code_pressed_once(KEYCODE_L)) + m_debug_palette = !m_debug_palette; +#endif + + if (VIDEO_IRQ_ENABLE & 1) + { + VIDEO_IRQ_STATUS |= 1; + LOGMASKED(LOG_IRQS, "Setting video IRQ status to %04x\n", VIDEO_IRQ_STATUS); + check_video_irq(); + } +} + +void spg2xx_video_device::check_video_irq() +{ + LOGMASKED(LOG_IRQS, "%ssserting IRQ0 (%04x, %04x)\n", (VIDEO_IRQ_STATUS & VIDEO_IRQ_ENABLE) ? "A" : "Dea", VIDEO_IRQ_STATUS, VIDEO_IRQ_ENABLE); + m_video_irq_cb((VIDEO_IRQ_STATUS & VIDEO_IRQ_ENABLE) ? ASSERT_LINE : CLEAR_LINE); +} + +void spg2xx_video_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_SCREENPOS: + { + if (VIDEO_IRQ_ENABLE & 2) + { + VIDEO_IRQ_STATUS |= 2; + check_video_irq(); + } + m_screen->update_partial(m_screen->vpos()); + + // fire again, jak_dbz pinball needs this + m_screenpos_timer->adjust(m_screen->time_until_pos(m_video_regs[0x36], m_video_regs[0x37] << 1)); + break; + } + } +} diff --git a/src/devices/machine/spg2xx_video.h b/src/devices/machine/spg2xx_video.h new file mode 100644 index 00000000000..064e3cb9757 --- /dev/null +++ b/src/devices/machine/spg2xx_video.h @@ -0,0 +1,137 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/***************************************************************************** + + SunPlus SPG2xx-series SoC peripheral emulation (Video) + +**********************************************************************/ + +#ifndef MAME_MACHINE_SPG2XX_VIDEO_H +#define MAME_MACHINE_SPG2XX_VIDEO_H + +#pragma once + +#include "cpu/unsp/unsp.h" +#include "screen.h" + +class spg2xx_video_device : public device_t +{ +public: + spg2xx_video_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + DECLARE_WRITE_LINE_MEMBER(vblank); + + DECLARE_READ16_MEMBER(video_r); + DECLARE_WRITE16_MEMBER(video_w); + + auto sprlimit_read_callback() { return m_sprlimit_read_cb.bind(); }; + auto rowscrolloffset_read_callback() { return m_rowscrolloffset_read_cb.bind(); }; + + auto write_video_irq_callback() { return m_video_irq_cb.bind(); }; + +protected: + + enum + { + PAGE_ENABLE_MASK = 0x0008, + PAGE_WALLPAPER_MASK = 0x0004, + + SPRITE_ENABLE_MASK = 0x0001, + SPRITE_COORD_TL_MASK = 0x0002, + + PAGE_PRIORITY_FLAG_MASK = 0x3000, + PAGE_PRIORITY_FLAG_SHIFT = 12, + PAGE_TILE_HEIGHT_MASK = 0x00c0, + PAGE_TILE_HEIGHT_SHIFT = 6, + PAGE_TILE_WIDTH_MASK = 0x0030, + PAGE_TILE_WIDTH_SHIFT = 4, + TILE_X_FLIP = 0x0004, + TILE_Y_FLIP = 0x0008 + }; + + + inline void check_video_irq(); + + static const device_timer_id TIMER_SCREENPOS = 2; + + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + void do_sprite_dma(uint32_t len); + + enum blend_enable_t : bool + { + BlendOff = false, + BlendOn = true + }; + + enum rowscroll_enable_t : bool + { + RowScrollOff = false, + RowScrollOn = true + }; + + enum flipx_t : bool + { + FlipXOff = false, + FlipXOn = true + }; + + void apply_saturation(const rectangle &cliprect); + void apply_fade(const rectangle &cliprect); + + template + void draw(const rectangle &cliprect, uint32_t line, uint32_t xoff, uint32_t yoff, uint32_t bitmap_addr, uint16_t tile, int32_t h, int32_t w, uint8_t bpp, uint32_t yflipmask, uint32_t palette_offset); + void draw_page(const rectangle &cliprect, uint32_t scanline, int priority, uint32_t bitmap_addr, uint16_t *regs); + void draw_sprite(const rectangle &cliprect, uint32_t scanline, int priority, uint32_t base_addr); + void draw_sprites(const rectangle &cliprect, uint32_t scanline, int priority); + + uint8_t mix_channel(uint8_t a, uint8_t b); + + uint32_t m_screenbuf[320 * 240]; + uint8_t m_rgb5_to_rgb8[32]; + uint32_t m_rgb555_to_rgb888[0x8000]; + + bool m_hide_page0; + bool m_hide_page1; + bool m_hide_sprites; + bool m_debug_sprites; + bool m_debug_blit; + bool m_debug_palette; + uint8_t m_sprite_index_to_debug; + + uint16_t m_video_regs[0x100]; + + devcb_read16 m_sprlimit_read_cb; + devcb_read16 m_rowscrolloffset_read_cb; + + emu_timer *m_screenpos_timer; + + required_device m_cpu; + required_device m_screen; + required_shared_ptr m_scrollram; + required_shared_ptr m_paletteram; + required_shared_ptr m_spriteram; + + devcb_write_line m_video_irq_cb; +}; + +class spg24x_video_device : public spg2xx_video_device +{ +public: + template + spg24x_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&cpu_tag, U &&screen_tag) + : spg24x_video_device(mconfig, tag, owner, clock) + { + m_cpu.set_tag(std::forward(cpu_tag)); + m_screen.set_tag(std::forward(screen_tag)); + } + + spg24x_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +DECLARE_DEVICE_TYPE(SPG24X_VIDEO, spg24x_video_device) + +#endif // MAME_MACHINE_SPG2XX_VIDEO_H diff --git a/src/mame/drivers/spg110.cpp b/src/mame/drivers/spg110.cpp index a10d01132fe..c05be2940c9 100644 --- a/src/mame/drivers/spg110.cpp +++ b/src/mame/drivers/spg110.cpp @@ -47,7 +47,7 @@ protected: void spg110_game_state::mem_map(address_map &map) { - map(0x004000, 0x0fffff).rom().region("maincpu", 0x8000); + map(0x004000, 0x3fffff).rom().region("maincpu", 0x8000); map(0x000000, 0x003fff).m(m_spg, FUNC(spg110_device::map)); } @@ -198,7 +198,7 @@ static INPUT_PORTS_START( jak_capb ) PORT_BIT( 0xffff, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("JOYY") - PORT_BIT(0x0fff, 0x0000, IPT_AD_STICK_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff) PORT_NAME("Plunger") + PORT_BIT(0x0fff, 0x0000, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_MINMAX(0x00,0x0fff) PORT_NAME("Plunger") INPUT_PORTS_END static INPUT_PORTS_START( jak_spdmo ) @@ -384,7 +384,7 @@ void spg110_game_state::spg110_base(machine_config &config) } ROM_START( jak_capb ) - ROM_REGION( 0x200000, "maincpu", ROMREGION_ERASE00 ) + ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 ) ROM_LOAD16_WORD_SWAP( "classicarcadepinball.bin", 0x000000, 0x200000, CRC(b643dab0) SHA1(f57d546758ba442e28b5f0f48b3819b2fc2eb7f7) ) ROM_END