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
This commit is contained in:
David Haywood 2019-03-28 02:40:35 +00:00 committed by ajrhacker
parent f1abceb70c
commit 9873d6afaa
13 changed files with 1994 additions and 1652 deletions

View File

@ -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

View File

@ -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<spg110_device::flipx_t FlipX>
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<FlipXOn>(cliprect, tile_scanline, xx, yy, attr, ctrl, bitmap_addr, tile);
else
blit<FlipXOff>(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;
}

View File

@ -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<unsp_device> m_cpu;
required_device<screen_device> m_screen;
required_device<palette_device> m_palette;
required_device<gfxdecode_device> m_gfxdecode;
required_shared_ptr<uint16_t> m_palram;
required_device<spg2xx_io_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<spg110_video_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<flipx_t FlipX>
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 <size_t Line> 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 <size_t Line> 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);
};

View File

@ -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<spg110_video_device::flipx_t FlipX>
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<FlipXOn>(cliprect, tile_scanline, xx, yy, ctrl, bitmap_addr, tile, pal, tile_h, tile_w, bpp);
else
draw<FlipXOff>(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<FlipXOn>(cliprect, tile_line, x, y, 0, bitmap_addr, tile, palette_offset, h, w, bpp);
else
draw<FlipXOff>(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;
}

View File

@ -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 <typename T, typename U>
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<T>(cpu_tag));
m_screen.set_tag(std::forward<U>(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<unsp_device> m_cpu;
required_device<screen_device> m_screen;
required_device<palette_device> m_palette;
required_device<gfxdecode_device> m_gfxdecode;
required_shared_ptr<uint16_t> m_palram;
required_shared_ptr<uint16_t> m_palctrlram;
required_shared_ptr<uint16_t> m_sprtileno;
required_shared_ptr<uint16_t> m_sprattr1;
required_shared_ptr<uint16_t> 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<flipx_t FlipX>
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

View File

@ -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<spg2xx_device::blend_enable_t Blend, spg2xx_device::rowscroll_enable_t RowScroll, spg2xx_device::flipx_t FlipX>
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<BlendOn, RowScrollOn, FlipXOn>(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile);
else
blit<BlendOn, RowScrollOn, FlipXOff>(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile);
}
else
{
if (flip_x)
blit<BlendOn, RowScrollOff, FlipXOn>(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile);
else
blit<BlendOn, RowScrollOff, FlipXOff>(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile);
}
}
else
{
if (row_scroll)
{
if (flip_x)
blit<BlendOff, RowScrollOn, FlipXOn>(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile);
else
blit<BlendOff, RowScrollOn, FlipXOff>(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile);
}
else
{
if (flip_x)
blit<BlendOff, RowScrollOff, FlipXOn>(cliprect, tile_scanline, xx, yy, tileattr, tilectrl, bitmap_addr, tile);
else
blit<BlendOff, RowScrollOff, FlipXOff>(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<BlendOn, RowScrollOff, FlipXOn>(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile);
else
blit<BlendOn, RowScrollOff, FlipXOff>(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile);
}
else
{
if (flip_x)
blit<BlendOff, RowScrollOff, FlipXOn>(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile);
else
blit<BlendOff, RowScrollOff, FlipXOff>(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile);
}
m_debug_blit = false;
#else
if (blend)
{
if (flip_x)
blit<BlendOn, RowScrollOff, FlipXOn>(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile);
else
blit<BlendOn, RowScrollOff, FlipXOff>(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile);
}
else
{
if (flip_x)
blit<BlendOff, RowScrollOff, FlipXOn>(cliprect, tile_line, x, y, attr, 0, bitmap_addr, tile);
else
blit<BlendOff, RowScrollOff, FlipXOff>(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));
}

View File

@ -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<spg2xx_audio_device> m_spg_audio;
required_device<spg2xx_io_device> m_spg_io;
required_device<spg2xx_sysdma_device> m_spg_sysdma;
required_device<spg2xx_video_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<blend_enable_t Blend, rowscroll_enable_t RowScroll, flipx_t FlipX>
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<unsp_device> m_cpu;
required_device<screen_device> m_screen;
required_shared_ptr<uint16_t> m_scrollram;
required_shared_ptr<uint16_t> m_paletteram;
required_shared_ptr<uint16_t> 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 <size_t Line> 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 <size_t Line> 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

View File

@ -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"

View File

@ -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;
}

View File

@ -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 <typename T>
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<T>(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<unsp_device> m_cpu;
};
#endif // MAME_MACHINE_SPG2XX_SYSDMA_H

View File

@ -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<spg2xx_video_device::blend_enable_t Blend, spg2xx_video_device::rowscroll_enable_t RowScroll, spg2xx_video_device::flipx_t FlipX>
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<BlendOn, RowScrollOn, FlipXOn>(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset);
else
draw<BlendOn, RowScrollOn, FlipXOff>(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset);
}
else
{
if (flip_x)
draw<BlendOn, RowScrollOff, FlipXOn>(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset);
else
draw<BlendOn, RowScrollOff, FlipXOff>(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset);
}
}
else
{
if (row_scroll)
{
if (flip_x)
draw<BlendOff, RowScrollOn, FlipXOn>(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset);
else
draw<BlendOff, RowScrollOn, FlipXOff>(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset);
}
else
{
if (flip_x)
draw<BlendOff, RowScrollOff, FlipXOn>(cliprect, tile_scanline, xx, yy, bitmap_addr, tile, tile_h, tile_w, bpp, yflipmask, palette_offset);
else
draw<BlendOff, RowScrollOff, FlipXOff>(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<BlendOn, RowScrollOff, FlipXOn>(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset);
else
draw<BlendOn, RowScrollOff, FlipXOff>(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset);
}
else
{
if (flip_x)
draw<BlendOff, RowScrollOff, FlipXOn>(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset);
else
draw<BlendOff, RowScrollOff, FlipXOff>(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<BlendOn, RowScrollOff, FlipXOn>(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset);
else
draw<BlendOn, RowScrollOff, FlipXOff>(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset);
}
else
{
if (flip_x)
draw<BlendOff, RowScrollOff, FlipXOn>(cliprect, tile_line, x, y, bitmap_addr, tile, h, w, bpp, yflipmask, palette_offset);
else
draw<BlendOff, RowScrollOff, FlipXOff>(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;
}
}
}

View File

@ -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<blend_enable_t Blend, rowscroll_enable_t RowScroll, flipx_t FlipX>
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<unsp_device> m_cpu;
required_device<screen_device> m_screen;
required_shared_ptr<uint16_t> m_scrollram;
required_shared_ptr<uint16_t> m_paletteram;
required_shared_ptr<uint16_t> m_spriteram;
devcb_write_line m_video_irq_cb;
};
class spg24x_video_device : public spg2xx_video_device
{
public:
template <typename T, typename U>
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<T>(cpu_tag));
m_screen.set_tag(std::forward<U>(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

View File

@ -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