// license:BSD-3-Clause
// copyright-holders:Luca Elia
/***************************************************************************
-= Billiard Academy Real Break =-
driver by Luca Elia (l.elia@tin.it)
This hardware provides for:
- 2 scrolling background layers, 1024 x 512 in size
made of 16 x 16 tiles with 256 colors
- 1 text layer (fixed?), 512 x 256 in size
made of 8 x 8 tiles with 16 colors
- 0x300 sprites made of 16x16 tiles, both 256 or 16 colors
per tile and from 1 to 32x32 (more?) tiles per sprite.
Sprites can zoom / shrink / rotate
***************************************************************************/
#include "emu.h"
#include "includes/realbrk.h"
void realbrk_state::realbrk_flipscreen_w(offs_t offset, u16 data, u16 mem_mask)
{
if (ACCESSING_BITS_0_7)
{
machine().bookkeeping().coin_counter_w(0, data & 0x0001);
machine().bookkeeping().coin_counter_w(1, data & 0x0004);
flip_screen_set( data & 0x0080);
}
if (ACCESSING_BITS_8_15)
{
m_disable_video = data & 0x8000;
}
}
void realbrk_state::dai2kaku_flipscreen_w(u16 data)
{
m_disable_video = 0;
}
/***************************************************************************
Tilemaps
***************************************************************************/
/***************************************************************************
Background Tilemaps
Offset: Bits: Value:
0.w f--- ---- ---- ---- Flip Y
-e-- ---- ---- ---- Flip X
--dc ba98 7--- ----
---- ---- -654 3210 Color
2.w Code
***************************************************************************/
template<int Layer>
TILE_GET_INFO_MEMBER(realbrk_state::get_tile_info)
{
const u16 attr = m_vram[Layer][tile_index * 2 + 0];
const u16 code = m_vram[Layer][tile_index * 2 + 1];
SET_TILE_INFO_MEMBER(0,
code,
attr & 0x7f,
TILE_FLIPYX( attr >> 14 ));
}
/***************************************************************************
Text Tilemap
Offset: Bits: Value:
0.w fedc ---- ---- ---- Color
---- ba98 7654 3210 Code
The full palette of 0x8000 colors can be used by this tilemap since
a video register selects the higher bits of the color code.
***************************************************************************/
TILE_GET_INFO_MEMBER(realbrk_state::get_tile_info_2)
{
const u16 code = m_vram[2][tile_index];
SET_TILE_INFO_MEMBER(1,
code & 0x0fff,
((code & 0xf000) >> 12) | ((m_vregs[0xa/2] & 0x7f) << 4),
0);
}
void realbrk_state::vram_2_w(offs_t offset, u16 data, u16 mem_mask)
{
COMBINE_DATA(&m_vram[2][offset]);
m_tilemap[2]->mark_tile_dirty(offset);
}
/***************************************************************************
Video Hardware Init
***************************************************************************/
void realbrk_state::video_start()
{
/* Backgrounds */
m_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(realbrk_state::get_tile_info<0>)), TILEMAP_SCAN_ROWS, 16, 16, 0x40, 0x20);
m_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(realbrk_state::get_tile_info<1>)), TILEMAP_SCAN_ROWS, 16, 16, 0x40, 0x20);
/* Text */
m_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(realbrk_state::get_tile_info_2)), TILEMAP_SCAN_ROWS, 8, 8, 0x40, 0x20);
m_tilemap[0]->set_transparent_pen(0);
m_tilemap[1]->set_transparent_pen(0);
m_tilemap[2]->set_transparent_pen(0);
m_tmpbitmap0 = std::make_unique<bitmap_ind16>(32,32);
m_tmpbitmap1 = std::make_unique<bitmap_ind16>(32,32);
save_item(NAME(m_disable_video));
}
/***************************************************************************
Sprites Drawing
Sprites RAM is 0x4000 bytes long with each sprite needing 16 bytes.
Not all sprites must be displayed: there is a list of words at offset
0x3000. If the high bit of a word is 0 the low bits form the index
of a sprite to be drawn. 0x300 items of the list seem to be used.
Each sprite is made of several 16x16 tiles (from 1 to 32x32) and
can be zoomed / shrunk in size.
There are two set of tiles: with 256 or 16 colors.
Offset: Bits: Value:
0.w Y
2.w X
4.w fedc ba98 ---- ---- Number of tiles along Y, minus 1 (5 bits or more ?)
---- ---- 7654 3210 Number of tiles along X, minus 1 (5 bits or more ?)
6.w fedc ba98 ---- ---- Zoom factor along Y (0x40 = no zoom)
---- ---- 7654 3210 Zoom factor along X (0x40 = no zoom)
8.w fe-- ---- ---- ----
--d- ---- ---- ---- Flip Y
---c ---- ---- ---- Flip X
---- ba98 76-- ----
---- ---- --54 ---- Rotation
---- ---- ---- 32--
---- ---- ---- --10 Priority
A.w fedc b--- ---- ----
---- -a98 7654 3210 Color
C.w fedc ba9- ---- ----
---- ---8 ---- ----
---- ---- 7654 321-
---- ---- ---- ---0 1 = Use 16 color sprites, 0 = Use 256 color sprites
E.w Code
***************************************************************************/
// DaiDaiKakumei
// layer : 0== bghigh<spr 1== bglow<spr<bghigh 2==spr<bglow 3==boarder
template <bool Rotatable>
void realbrk_state::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect, bitmap_ind8 &priority)
{
constexpr u32 PRI_MAP[4]{
GFX_PMASK_4, // over m_tilemap[1], over m_tilemap[0], under m_tilemap[2]
GFX_PMASK_4 | GFX_PMASK_2, // over m_tilemap[1], under m_tilemap[0], under m_tilemap[2]
GFX_PMASK_4 | GFX_PMASK_2 | GFX_PMASK_1, // under m_tilemap[1], under m_tilemap[0], under m_tilemap[2]
GFX_PMASK_4 | GFX_PMASK_2 | GFX_PMASK_1 }; // unknown
const int max_x(m_screen->width());
const int max_y(m_screen->height());
for (int offs = (0x3600 - 2) / 2; offs >= 0x3000 / 2; offs -= 2 / 2)
{
if (BIT(m_spriteram[offs], 15))
continue;
const u16 *const s(&m_spriteram[(m_spriteram[offs] & 0x3ff) * 16 / 2]);
int sy = s[0];
int sx = s[1];
const int xnum = ((s[2] >> 0) & 0x001f) + 1;
const int ynum = ((s[2] >> 8) & 0x001f) + 1;
const int xdim = ((s[3] >> 0) & 0x00ff) << (16 - 6 + 4);
const int ydim = ((s[3] >> 8) & 0x00ff) << (16 - 6 + 4);
int flipx = BIT(s[4], 8);
int flipy = BIT(s[4], 9);
const u8 rot = (s[4] >> 4) & 0x0003;
const u8 pri = (s[4] >> 0) & 0x0003;
const u32 color = s[5];
const u8 gfx = m_gfxdecode->gfx(3) ? (s[6] & 0x0001) + 2 : 2;
u32 code = s[7];
sx = ((sx & 0x1ff) - (sx & 0x200)) << 16;
sy = ((sy & 0x0ff) - (sy & 0x100)) << 16;
if (flip_screen_x()) { flipx = !flipx; sx = (max_x << 16) - sx - xnum * xdim; }
if (flip_screen_y()) { flipy = !flipy; sy = (max_y << 16) - sy - ynum * ydim; }
int xstart, xend, xinc;
if (flipx) { xstart = xnum-1; xend = -1; xinc = -1; }
else { xstart = 0; xend = xnum; xinc = +1; }
int ystart, yend, yinc;
if (flipy) { ystart = ynum-1; yend = -1; yinc = -1; }
else { ystart = 0; yend = ynum; yinc = +1; }
/*
The positioning of the rotated sprites makes it look as if
the sprite source is scanned in a constant pattern left to right,
top to bottom, and the destination plotting pattern is varied.
copyrozbitmap works the other way.
Rotating a sprite when drawgfxzoom draws a tile at a time means
- rotating each sprite tile
- transforming each tile position
- compensating for the offset introduced by the difference in
scanning patterns between the original mechanism and copyrozbitmap
*/
for (int y = ystart; y != yend; y += yinc)
{
for (int x = xstart; x != xend; x += xinc)
{
int currx = (sx + x * xdim) / 0x10000;
int curry = (sy + y * ydim) / 0x10000;
const int scalex = (sx + (x + 1) * xdim) / 0x10000 - currx;
const int scaley = (sy + (y + 1) * ydim) / 0x10000 - curry;
if (Rotatable && rot)
{
// buffer the tile and rotate it into bitmap
constexpr rectangle spritetile_clip(0, 31, 0, 31);
m_tmpbitmap0->fill(0, spritetile_clip);
m_tmpbitmap1->fill(0, spritetile_clip);
m_gfxdecode->gfx(gfx)->zoom_transpen(*m_tmpbitmap0, spritetile_clip,
code++,
color,
flipx, flipy,
0, 0,
((rot & 1) ? scaley : scalex) << 12, ((rot & 1) ? scalex : scaley) << 12,
0);
// peek at the unrotated sprite
// copybitmap_trans(bitmap, *m_tmpbitmap0, 0,0, 50 + (x * xdim / 0x10000), 50 + (y * ydim/0x10000), cliprect, 0);
switch (rot)
{
case 0x1: // rot 90
copyrozbitmap_trans(*m_tmpbitmap1, m_tmpbitmap1->cliprect(), *m_tmpbitmap0,
u32(0) << 16,
u32(16) << 16,
0 << 16,
0xffff << 16,
1 << 16,
0 << 16,
false, 0);
currx = (sx - (y + 1) * ydim) / 0x10000;
curry = (sy + x * xdim) / 0x10000;
break;
case 0x2: // rot 180
copyrozbitmap_trans(*m_tmpbitmap1, m_tmpbitmap1->cliprect(), *m_tmpbitmap0,
u32(16) << 16,
u32(16) << 16,
0xffff << 16,
0 << 16,
0 << 16,
0xffff << 16,
false, 0);
currx = (sx - (x + 1) * xdim) / 0x10000;
curry = (sy - (y + 1) * ydim) / 0x10000;
break;
case 0x3: // rot 270
copyrozbitmap_trans(*m_tmpbitmap1, m_tmpbitmap1->cliprect(), *m_tmpbitmap0,
u32(16) << 16,
u32(0) << 16,
0 << 16,
1 << 16,
0xffff << 16,
0 << 16,
false, 0);
currx = (sx + y * ydim) / 0x10000;
curry = (sy - (x + 1) * xdim) / 0x10000;
break;
}
prio_copybitmap_trans(bitmap, *m_tmpbitmap1, 0, 0, currx, curry, cliprect, priority, PRI_MAP[pri], 0);
}
else
{
m_gfxdecode->gfx(gfx)->prio_zoom_transpen(bitmap, cliprect,
code++,
color,
flipx, flipy,
currx, curry,
scalex << 12, scaley << 12,
priority, PRI_MAP[pri],
0);
}
}
}
}
}
/***************************************************************************
Screen Drawing
Video Registers:
Offset: Bits: Value:
0.w Background 0 Scroll Y
2.w Background 0 Scroll X
4.w Background 1 Scroll Y
6.w Background 1 Scroll X
8.w fedc ba98 ---- ---- ? bit f = flip
---- ---- 7654 3210
A.w fedc ba98 7--- ----
---- ---- -654 3210 Color codes high bits for the text tilemap
C.w f--- ---- ---- ----
-edc ba98 7654 3210 Index of the background color
***************************************************************************/
void realbrk_state::vregs_w(offs_t offset, u16 data, u16 mem_mask)
{
const u16 old_data = m_vregs[offset];
data = COMBINE_DATA(&m_vregs[offset]);
if (data != old_data)
{
if ((offset == 0xa/2) && ((old_data ^ data) & 0x7f))
m_tilemap[0]->mark_all_dirty();
}
}
u32 realbrk_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
m_tilemap[0]->set_scrolly(0, m_vregs[0x0/2]);
m_tilemap[0]->set_scrollx(0, m_vregs[0x2/2]);
m_tilemap[1]->set_scrolly(0, m_vregs[0x4/2]);
m_tilemap[1]->set_scrollx(0, m_vregs[0x6/2]);
if (m_disable_video)
{
bitmap.fill(m_palette->black_pen(), cliprect);
return 0;
}
bitmap.fill(m_vregs[0xc / 2] & 0x7fff, cliprect);
screen.priority().fill(0, cliprect);
int layers_ctrl(-1);
#ifdef MAME_DEBUG
if (machine().input().code_pressed(KEYCODE_Z))
{
int msk(0);
if (machine().input().code_pressed(KEYCODE_Q)) msk |= 1;
if (machine().input().code_pressed(KEYCODE_W)) msk |= 2;
if (machine().input().code_pressed(KEYCODE_E)) msk |= 4;
if (machine().input().code_pressed(KEYCODE_A)) msk |= 8;
if (msk) layers_ctrl &= msk;
}
#endif
if (layers_ctrl & 2) m_tilemap[1]->draw(screen, bitmap, cliprect, 0, 1);
if (layers_ctrl & 1) m_tilemap[0]->draw(screen, bitmap, cliprect, 0, 2);
if (layers_ctrl & 4) m_tilemap[2]->draw(screen, bitmap, cliprect, 0, 4);
if (layers_ctrl & 8) draw_sprites<true>(bitmap,cliprect, screen.priority());
// popmessage("%04x",m_vregs[0x8/2]);
return 0;
}
/* DaiDaiKakumei */
u32 realbrk_state::screen_update_dai2kaku(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
// bg0
const int bgy0(m_vregs[0x0 / 2]);
const int bgx0(m_vregs[0x2 / 2]);
if (BIT(m_vregs[8 / 2], 8))
{
m_tilemap[0]->set_scroll_rows(512);
for (int offs = 0; offs < 512; offs++)
m_tilemap[0]->set_scrollx(offs, bgx0 - (m_vram_ras[1][offs] & 0x3ff));
}
else
{
m_tilemap[0]->set_scroll_rows(1);
m_tilemap[0]->set_scrollx(0, bgx0);
}
m_tilemap[0]->set_scrolly(0, bgy0);
// bg1
const int bgy1(m_vregs[0x4 / 2]);
const int bgx1(m_vregs[0x6 / 2]);
if (BIT(m_vregs[8 / 2], 0))
{
m_tilemap[1]->set_scroll_rows(512);
for (int offs = 0; offs < 512; offs++)
m_tilemap[1]->set_scrollx(offs, bgx1 - (m_vram_ras[1][offs] & 0x3ff));
}
else
{
m_tilemap[1]->set_scroll_rows(1);
m_tilemap[1]->set_scrollx(0, bgx1);
}
m_tilemap[1]->set_scrolly(0, bgy1);
if (m_disable_video)
{
bitmap.fill(m_palette->black_pen(), cliprect);
return 0;
}
bitmap.fill(m_vregs[0xc/2] & 0x7fff, cliprect);
screen.priority().fill(0, cliprect);
int layers_ctrl(-1);
#ifdef MAME_DEBUG
if (machine().input().code_pressed(KEYCODE_Z))
{
int msk(0);
if (machine().input().code_pressed(KEYCODE_Q)) msk |= 1;
if (machine().input().code_pressed(KEYCODE_W)) msk |= 2;
if (machine().input().code_pressed(KEYCODE_E)) msk |= 4;
if (machine().input().code_pressed(KEYCODE_A)) msk |= 8;
if (msk) layers_ctrl &= msk;
}
#endif
const bool bgpri(BIT(m_vregs[8 / 2], 15));
// bglow
if (layers_ctrl & (bgpri ? 1 : 2)) m_tilemap[bgpri ? 0 : 1]->draw(screen, bitmap, cliprect, 0, 1);
// bghigh
if (layers_ctrl & (bgpri ? 2 : 1)) m_tilemap[bgpri ? 1 : 0]->draw(screen, bitmap, cliprect, 0, 2);
// fix
if (layers_ctrl & 4) m_tilemap[2]->draw(screen, bitmap, cliprect, 0, 4);
// spr
if (layers_ctrl & 8) draw_sprites<false>(bitmap, cliprect, screen.priority());
// usrintf_showmessage("%04x",m_vregs[0x8/2]);
return 0;
}