Fix problem with bac06 NW

The previous emulation logic change was correct, however Sly Spy, Hippodrome, Birdy Try suffered from an ordering bug - tilemap.cpp in the core cached the tile indices before the game updated the dynamic colmun/row major bit.  It seems the correct fix is to make sure tilemap::mappings_update() should be called when the column/row bit changes, however this is currently a private function.  I've put in a hack at line 396 so that mappings_update() is called as a side effect of a flip.  The better fix would be just to expose mappings_update for general driver use, but I didn't want to make that larger change without approval.
This commit is contained in:
bmcphail 2019-02-25 10:33:40 -05:00
parent ea78bea4ee
commit 2bf7666664
3 changed files with 645 additions and 16 deletions

View File

@ -0,0 +1,592 @@
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail, David Haywood
/*
Deco BAC06 tilemap generator:
this a direct relative of the later chip implemented in deco16ic.c
we could implement this as either an 8-bit or a 16-bit chip, for now
I'm using the 16-bit implementation from dec0.c
used by:
actfancr.c
dec0.c
dec8.c (oscar, cobracom, ghostb)
madmotor.c
stadhero.c
pcktgal.c
Notes (from dec0.c)
All games contain three BAC06 background generator chips, usual (software)
configuration is 2 chips of 16*16 tiles, 1 of 8*8.
Playfield control registers:
bank 0:
0:
bit 0 (0x1) set = 8*8 tiles, else 16*16 tiles
Bit 1 (0x2) set = row major tile layout, else column major*
bit 2 (0x4) set enables rowscroll
bit 3 (0x8) set enables colscroll
bit 7 (0x80) set in playfield 1 is reverse screen (set via dip-switch)
bit 7 (0x80) in other playfields unknown
2: unknown (00 in bg, 03 in fg+text - maybe controls pf transparency?)
4: unknown (always 00) [Used to access 2nd bank of tiles in Stadium Hero)
6: playfield shape: 00 = 4x1, 01 = 2x2, 02 = 1x4 (low 4 bits only)
bank 1:
0: horizontal scroll
2: vertical scroll
4: colscroll shifter (low 4 bits, top 4 bits do nothing)
6: rowscroll shifter (low 4 bits, top 4 bits do nothing)
Row & column scroll can be applied simultaneously or by themselves.
The shift register controls the granularity of the scroll offsets
(more details given later).
* Bandit is the only game known to use column major tile layout, when in this
mode X scrolling is also inverted, and tile character data is flipped on X.
Playfield priority (Bad Dudes, etc):
In the bottommost playfield, pens 8-15 can have priority over the next playfield.
In that next playfield, pens 8-15 can have priority over sprites.
Bit 0: Playfield inversion
Bit 1: Enable playfield mixing (for palettes 8-15 only)
Bit 2: Enable playfield/sprite mixing (for palettes 8-15 only)
Priority word (Midres):
Bit 0 set = Playfield 3 drawn over Playfield 2
~ = Playfield 2 drawn over Playfield 3
Bit 1 set = Sprites are drawn inbetween playfields
~ = Sprites are on top of playfields
Bit 2
Bit 3 set = ...
Note that priority mixing is handled outside of the BAC-06 chip.
*/
#include "emu.h"
#include "decbac06.h"
DEFINE_DEVICE_TYPE(DECO_BAC06, deco_bac06_device, "deco_back06", "DECO BAC06 Tilemap")
deco_bac06_device::deco_bac06_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, DECO_BAC06, tag, owner, clock)
, m_pf_data(nullptr)
, m_pf_rowscroll(nullptr)
, m_pf_colscroll(nullptr)
, m_tile_region_8(0)
, m_tile_region_16(0)
, m_supports_8x8(true)
, m_supports_16x16(true)
, m_supports_rc_scroll(true)
, m_gfxcolmask(0)
, m_rambank(0)
, m_gfxregion8x8(0)
, m_gfxregion16x16(0)
, m_wide(0)
, m_bppmult(0)
, m_bppmask(0)
, m_gfxdecode(*this, finder_base::DUMMY_TAG)
{
for (int i = 0; i < 8; i++)
{
m_pf_control_0[i] = 0;
m_pf_control_1[i] = 0;
}
}
void deco_bac06_device::device_start()
{
if(!m_gfxdecode->started())
throw device_missing_dependencies();
m_pf_data = make_unique_clear<uint16_t[]>(0x4000 / 2); // 0x2000 is the maximum needed, some games / chip setups map less and mirror - stadium hero banks this to 0x4000?!
m_pf_rowscroll = make_unique_clear<uint16_t[]>(0x2000 / 2);
m_pf_colscroll = make_unique_clear<uint16_t[]>(0x2000 / 2);
create_tilemaps(m_gfxregion8x8, m_gfxregion16x16);
m_gfxcolmask = 0x0f;
m_bppmult = 0x10;
m_bppmask = 0x0f;
m_rambank = 0;
m_flip_screen = false;
save_pointer(NAME(m_pf_data), 0x4000/2);
save_pointer(NAME(m_pf_rowscroll), 0x2000/2);
save_pointer(NAME(m_pf_colscroll), 0x2000/2);
save_item(NAME(m_pf_control_0));
save_item(NAME(m_pf_control_1));
save_item(NAME(m_gfxcolmask));
save_item(NAME(m_rambank));
save_item(NAME(m_flip_screen));
}
void deco_bac06_device::device_reset()
{
}
void deco_bac06_device::set_flip_screen(bool flip)
{
if (m_flip_screen != flip)
{
m_flip_screen = flip;
for (int i = 0; i < 3; i++)
{
m_pf8x8_tilemap[i]->set_flip(flip ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0);
m_pf16x16_tilemap[i]->set_flip(flip ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0);
}
}
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape0_scan)
{
if ((m_pf_control_0[0]&2)==0)
return (row & 0xf) + ((0x3f - (col & 0x3f)) << 4);
return (col & 0xf) + ((row & 0xf) << 4) + ((col & 0x1f0) << 4);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape1_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (row & 0xf) + ((col & 0x1f) << 4) + ((col & 0xf0) << 5);
return (col & 0xf) + ((row & 0x1f) << 4) + ((col & 0xf0) << 5);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape2_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (col & 0xf) + ((row & 0x3f) << 4) + ((row & 0x70) << 6);
return (col & 0xf) + ((row & 0x3f) << 4) + ((col & 0x70) << 6);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape0_8x8_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (col & 0x1f) + ((row & 0x1f) << 5) + ((row & 0x60) << 5);
return (col & 0x1f) + ((row & 0x1f) << 5) + ((col & 0x60) << 5);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape1_8x8_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (row & 0x1f) + ((col & 0x1f) << 5) + ((col & 0x20) << 5) + ((row & 0x20) << 6);
return (col & 0x1f) + ((row & 0x1f) << 5) + ((row & 0x20) << 5) + ((col & 0x20) << 6);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape2_8x8_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (row & 0x1f) + ((col & 0x7f) << 5);
return (col & 0x1f) + ((row & 0x7f) << 5);
}
TILE_GET_INFO_MEMBER(deco_bac06_device::get_pf8x8_tile_info)
{
if (m_rambank&1) tile_index+=0x1000;
int tile=m_pf_data[tile_index];
int colourpri=(tile>>12);
int flags=(m_pf_control_0[0]&2) ? 0 : TILE_FLIPX;
SET_TILE_INFO_MEMBER(m_tile_region_8,tile&0xfff,0,flags);
tileinfo.category = colourpri;
}
TILE_GET_INFO_MEMBER(deco_bac06_device::get_pf16x16_tile_info)
{
if (m_rambank&1) tile_index+=0x1000;
int tile=m_pf_data[tile_index];
int colourpri=(tile>>12);
int flags=(m_pf_control_0[0]&2) ? 0 : TILE_FLIPX;
SET_TILE_INFO_MEMBER(m_tile_region_16,tile&0xfff,0,flags);
tileinfo.category = colourpri;
}
void deco_bac06_device::create_tilemaps(int region8x8, int region16x16)
{
m_tile_region_8 = region8x8;
m_tile_region_16 = region16x16;
m_pf8x8_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf8x8_tile_info),this),tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape0_8x8_scan),this), 8, 8,128, 32);
m_pf8x8_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf8x8_tile_info),this),tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape1_8x8_scan),this), 8, 8, 64, 64);
m_pf8x8_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf8x8_tile_info),this),tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape2_8x8_scan),this), 8, 8, 32,128);
if (m_wide==2)
{
m_pf16x16_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this), tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape0_scan),this), 16, 16, 256, 16);
m_pf16x16_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this), tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape1_scan),this), 16, 16, 128, 32);
m_pf16x16_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this), tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape2_scan),this), 16, 16, 64, 64);
}
else if (m_wide==1)
{
m_pf16x16_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this), tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape0_scan),this), 16, 16, 128, 16);
m_pf16x16_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this), tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape1_scan),this), 16, 16, 64, 32);
m_pf16x16_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this), tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape2_scan),this), 16, 16, 32, 64);
}
else
{
m_pf16x16_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this),tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape0_scan),this), 16,16, 64, 16);
m_pf16x16_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this),tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape1_scan),this), 16,16, 32, 32);
m_pf16x16_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(deco_bac06_device::get_pf16x16_tile_info),this),tilemap_mapper_delegate(FUNC(deco_bac06_device::tile_shape2_scan),this), 16,16, 16, 64);
}
}
void deco_bac06_device::custom_tilemap_draw(bitmap_ind16 &bitmap,
const rectangle &cliprect,
tilemap_t *tilemap_ptr,
const uint16_t *rowscroll_ptr,
const uint16_t *colscroll_ptr,
const uint16_t *control0,
const uint16_t *control1,
int flags,
uint16_t penmask,
uint16_t pencondition,
uint16_t colprimask,
uint16_t colpricondition
)
{
const bitmap_ind16 &src_bitmap = tilemap_ptr->pixmap();
const bitmap_ind8 &flags_bitmap = tilemap_ptr->flagsmap();
int x, y, p, colpri;
int column_offset=0, src_x=0, src_y=0;
uint32_t scrollx = 0;
uint32_t scrolly = 0;
if (control1)
{
if (control0 && (control0[0]&2)==0) // Use of column major mode inverts scroll direction
scrollx = -control1[0] - 0x100;
else
scrollx = control1[0];
scrolly = control1[1];
}
int width_mask;
int height_mask;
int row_scroll_enabled = 0;
int col_scroll_enabled = 0;
if (m_supports_rc_scroll)
{
if (control0)
{
row_scroll_enabled = (rowscroll_ptr && (control0[0] & 0x4));
col_scroll_enabled = (colscroll_ptr && (control0[0] & 0x8));
}
}
width_mask = src_bitmap.width() - 1;
height_mask = src_bitmap.height() - 1;
/* Column scroll & row scroll may per applied per pixel, there are
shift registers for each which control the granularity of the row/col
offset (down to per line level for row, and per 8 lines for column).
Nb: The row & col selectors are _not_ affected by the shape of the
playfield (ie, 256*1024, 512*512 or 1024*256). So even if the tilemap
width is only 256, 'src_x' should not wrap at 256 in the code below (to
do so would mean the top half of row RAM would never be accessed which
is incorrect).
Nb2: Real hardware exhibits a strange bug with column scroll on 'mode 2'
(256*1024) - the first column has a strange additional offset, but
curiously the first 'wrap' (at scroll offset 256) does not have this offset,
it is displayed as expected. The bug is confimed to only affect this mode,
the other two modes work as expected. This bug is not emulated, as it
doesn't affect any games.
*/
if (m_flip_screen)
src_y = (src_bitmap.height() - 256) - scrolly;
else
src_y = scrolly;
for (y=0; y<=cliprect.bottom(); y++) {
if (row_scroll_enabled)
src_x=scrollx + rowscroll_ptr[(src_y >> (control1[3]&0xf))&(0x1ff>>(control1[3]&0xf))];
else
src_x=scrollx;
if (m_flip_screen)
src_x=(src_bitmap.width() - 256) - src_x;
for (x=0; x<=cliprect.right(); x++) {
if (col_scroll_enabled)
column_offset=colscroll_ptr[((src_x >> 3) >> (control1[2]&0xf))&(0x3f>>(control1[2]&0xf))];
p = src_bitmap.pix16((src_y + column_offset)&height_mask, src_x&width_mask);
colpri = flags_bitmap.pix8((src_y + column_offset)&height_mask, src_x&width_mask)&0xf;
src_x++;
if ((flags&TILEMAP_DRAW_OPAQUE) || (p&m_bppmask))
{
if ((p&penmask)==pencondition)
if((colpri&colprimask)==colpricondition)
bitmap.pix16(y, x) = p+(colpri&m_gfxcolmask)*m_bppmult;
}
}
src_y++;
}
}
void deco_bac06_device::deco_bac06_pf_draw(bitmap_ind16 &bitmap,const rectangle &cliprect,int flags,uint16_t penmask, uint16_t pencondition,uint16_t colprimask, uint16_t colpricondition)
{
tilemap_t* tm = nullptr;
int tm_dimensions = m_pf_control_0[3] & 0x3;
if (tm_dimensions == 3) tm_dimensions = 1; // 3 is invalid / the same as 1?
if (m_pf_control_0[0]&0x1) // is 8x8 tiles mode selected?
{
if (m_supports_8x8)
{
tm = m_pf8x8_tilemap[tm_dimensions];
}
else if (m_supports_16x16)
{
tm = m_pf16x16_tilemap[tm_dimensions];
}
}
else // 16x16 tiles mode is selected
{
if (m_supports_16x16)
{
tm = m_pf16x16_tilemap[tm_dimensions];
}
else if (m_supports_8x8)
{
tm = m_pf8x8_tilemap[tm_dimensions];
}
}
if (tm)
custom_tilemap_draw(bitmap,cliprect,tm,m_pf_rowscroll.get(),m_pf_colscroll.get(),m_pf_control_0,m_pf_control_1,flags, penmask, pencondition, colprimask, colpricondition);
}
// used for pocket gal bootleg, which doesn't set registers properly and simply expects a fixed size tilemap.
void deco_bac06_device::deco_bac06_pf_draw_bootleg(bitmap_ind16 &bitmap,const rectangle &cliprect,int flags, int mode, int type)
{
tilemap_t* tm = nullptr;
if (!mode) tm = m_pf8x8_tilemap[type];
else tm = m_pf16x16_tilemap[type];
custom_tilemap_draw(bitmap,cliprect,tm,m_pf_rowscroll.get(),m_pf_colscroll.get(),nullptr,nullptr,flags, 0, 0, 0, 0);
}
WRITE16_MEMBER( deco_bac06_device::pf_control_0_w )
{
int oldRegister0 = m_pf_control_0[0];
offset &= 3;
COMBINE_DATA(&m_pf_control_0[offset]);
bool dirtyAll = false;
if (offset==0)
{
if ((oldRegister0&2)!=(m_pf_control_0[offset]&2))
{
int flip = m_flip_screen;
set_flip_screen(flip^1);
set_flip_screen(flip); // Hack to force tilemap.cpp mappings_update() to be called
dirtyAll = true;
}
}
if (offset==2)
{
int newbank = m_pf_control_0[offset]&1;
if ((newbank&1) != (m_rambank&1))
{
// I don't know WHY Stadium Hero uses this as a bank but the RAM test expects it..
// I'm curious as to if anything else sets it tho
if (strcmp(machine().system().name,"stadhero"))
printf("tilemap ram bank change to %d\n", newbank&1);
dirtyAll = true;
m_rambank = newbank&1;
}
}
if (dirtyAll)
{
m_pf8x8_tilemap[0]->mark_all_dirty();
m_pf8x8_tilemap[1]->mark_all_dirty();
m_pf8x8_tilemap[2]->mark_all_dirty();
m_pf16x16_tilemap[0]->mark_all_dirty();
m_pf16x16_tilemap[1]->mark_all_dirty();
m_pf16x16_tilemap[2]->mark_all_dirty();
}
}
READ16_MEMBER( deco_bac06_device::pf_control_1_r )
{
offset &= 7;
return m_pf_control_1[offset];
}
WRITE16_MEMBER( deco_bac06_device::pf_control_1_w )
{
offset &= 7;
COMBINE_DATA(&m_pf_control_1[offset]);
}
WRITE16_MEMBER( deco_bac06_device::pf_data_w )
{
if (m_rambank&1) offset+=0x1000;
COMBINE_DATA(&m_pf_data[offset]);
m_pf8x8_tilemap[0]->mark_tile_dirty(offset);
m_pf8x8_tilemap[1]->mark_tile_dirty(offset);
m_pf8x8_tilemap[2]->mark_tile_dirty(offset);
m_pf16x16_tilemap[0]->mark_tile_dirty(offset);
m_pf16x16_tilemap[1]->mark_tile_dirty(offset);
m_pf16x16_tilemap[2]->mark_tile_dirty(offset);
}
READ16_MEMBER( deco_bac06_device::pf_data_r )
{
if (m_rambank&1) offset+=0x1000;
return m_pf_data[offset];
}
WRITE8_MEMBER( deco_bac06_device::pf_data_8bit_w )
{
if (offset&1)
pf_data_w(space,offset/2,data,0x00ff);
else
pf_data_w(space,offset/2,data<<8,0xff00);
}
READ8_MEMBER( deco_bac06_device::pf_data_8bit_r )
{
if (offset&1) /* MSB */
return pf_data_r(space,offset/2,0x00ff);
else
return pf_data_r(space,offset/2,0xff00)>>8;
}
WRITE16_MEMBER( deco_bac06_device::pf_rowscroll_w )
{
COMBINE_DATA(&m_pf_rowscroll[offset]);
}
WRITE16_MEMBER( deco_bac06_device::pf_colscroll_w )
{
COMBINE_DATA(&m_pf_colscroll[offset]);
}
READ16_MEMBER( deco_bac06_device::pf_rowscroll_r )
{
return m_pf_rowscroll[offset];
}
READ16_MEMBER( deco_bac06_device::pf_colscroll_r )
{
return m_pf_colscroll[offset];
}
/* used by dec8.c */
WRITE8_MEMBER( deco_bac06_device::pf_control0_8bit_w )
{
if (offset&1)
pf_control_0_w(space,offset/2,data,0x00ff); // oscar (mirrors?)
else
pf_control_0_w(space,offset/2,data,0x00ff);
}
/* used by dec8.c */
READ8_MEMBER( deco_bac06_device::pf_control1_8bit_r )
{
if (offset&1)
return pf_control_1_r(space,offset/2,0x00ff);
else
return pf_control_1_r(space,offset/2,0xff00)>>8;
}
/* used by dec8.c */
WRITE8_MEMBER( deco_bac06_device::pf_control1_8bit_w )
{
if (offset<4) // these registers are 16-bit?
{
if (offset&1)
pf_control_1_w(space,offset/2,data,0x00ff);
else
pf_control_1_w(space,offset/2,data<<8,0xff00);
}
else // these registers are 8-bit and mirror? (triothep vs actfancr)
{
if (offset&1)
pf_control_1_w(space,offset/2,data,0x00ff);
else
pf_control_1_w(space,offset/2,data,0x00ff);
}
}
READ8_MEMBER( deco_bac06_device::pf_rowscroll_8bit_r )
{
if (offset&1)
return pf_rowscroll_r(space,offset/2,0x00ff);
else
return pf_rowscroll_r(space,offset/2,0xff00)>>8;
}
WRITE8_MEMBER( deco_bac06_device::pf_rowscroll_8bit_w )
{
if (offset&1)
pf_rowscroll_w(space,offset/2,data,0x00ff);
else
pf_rowscroll_w(space,offset/2,data<<8,0xff00);
}
READ8_MEMBER( deco_bac06_device::pf_rowscroll_8bit_swap_r )
{
if (offset&1)
return pf_rowscroll_r(space,offset/2,0xff00)>>8;
else
return pf_rowscroll_r(space,offset/2,0x00ff);
}
WRITE8_MEMBER( deco_bac06_device::pf_rowscroll_8bit_swap_w )
{
if (offset&1)
pf_rowscroll_w(space,offset/2,data<<8,0xff00);
else
pf_rowscroll_w(space,offset/2,data,0x00ff);
}
/* used by hippodrm */
WRITE8_MEMBER( deco_bac06_device::pf_control0_8bit_packed_w )
{
if (offset&1)
pf_control_0_w(space,offset/2,data<<8,0xff00);
else
pf_control_0_w(space,offset/2,data,0x00ff);
}
/* used by hippodrm */
WRITE8_MEMBER( deco_bac06_device::pf_control1_8bit_swap_w )
{
pf_control1_8bit_w(space, offset^1, data);
}
/* used by hippodrm */
READ8_MEMBER( deco_bac06_device::pf_data_8bit_swap_r )
{
return pf_data_8bit_r(space, offset^1);
}
/* used by hippodrm */
WRITE8_MEMBER( deco_bac06_device::pf_data_8bit_swap_w )
{
pf_data_8bit_w(space, offset^1, data);
}

View File

@ -25,7 +25,7 @@ configuration is 2 chips of 16*16 tiles, 1 of 8*8.
bank 0:
0:
bit 0 (0x1) set = 8*8 tiles, else 16*16 tiles
Bit 1 (0x2) unknown
Bit 1 (0x2) set = row major tile layout, else column major*
bit 2 (0x4) set enables rowscroll
bit 3 (0x8) set enables colscroll
bit 7 (0x80) set in playfield 1 is reverse screen (set via dip-switch)
@ -44,6 +44,9 @@ configuration is 2 chips of 16*16 tiles, 1 of 8*8.
The shift register controls the granularity of the scroll offsets
(more details given later).
* Bandit is the only game known to use column major tile layout, when in this
mode X scrolling is also inverted, and tile character data is flipped on X.
Playfield priority (Bad Dudes, etc):
In the bottommost playfield, pens 8-15 can have priority over the next playfield.
In that next playfield, pens 8-15 can have priority over sprites.
@ -60,6 +63,8 @@ Priority word (Midres):
Bit 2
Bit 3 set = ...
Note that priority mixing is handled outside of the BAC-06 chip.
*/
#include "emu.h"
@ -139,31 +144,43 @@ void deco_bac06_device::set_flip_screen(bool flip)
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape0_scan)
{
if ((m_pf_control_0[0]&2)==0)
return (row & 0xf) + ((0x3f - (col & 0x3f)) << 4);
return (col & 0xf) + ((row & 0xf) << 4) + ((col & 0x1f0) << 4);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape1_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (row & 0xf) + ((col & 0x1f) << 4) + ((col & 0xf0) << 5);
return (col & 0xf) + ((row & 0x1f) << 4) + ((col & 0xf0) << 5);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape2_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (col & 0xf) + ((row & 0x3f) << 4) + ((row & 0x70) << 6);
return (col & 0xf) + ((row & 0x3f) << 4) + ((col & 0x70) << 6);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape0_8x8_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (col & 0x1f) + ((row & 0x1f) << 5) + ((row & 0x60) << 5);
return (col & 0x1f) + ((row & 0x1f) << 5) + ((col & 0x60) << 5);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape1_8x8_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (row & 0x1f) + ((col & 0x1f) << 5) + ((col & 0x20) << 5) + ((row & 0x20) << 6);
return (col & 0x1f) + ((row & 0x1f) << 5) + ((row & 0x20) << 5) + ((col & 0x20) << 6);
}
TILEMAP_MAPPER_MEMBER(deco_bac06_device::tile_shape2_8x8_scan)
{
//if (m_pf_control_0[0]&2) // Needs testing on real hardware, not used by any game
// return (row & 0x1f) + ((col & 0x7f) << 5);
return (col & 0x1f) + ((row & 0x7f) << 5);
}
@ -172,7 +189,8 @@ TILE_GET_INFO_MEMBER(deco_bac06_device::get_pf8x8_tile_info)
if (m_rambank&1) tile_index+=0x1000;
int tile=m_pf_data[tile_index];
int colourpri=(tile>>12);
SET_TILE_INFO_MEMBER(m_tile_region_8,tile&0xfff,0,0);
int flags=(m_pf_control_0[0]&2) ? 0 : TILE_FLIPX;
SET_TILE_INFO_MEMBER(m_tile_region_8,tile&0xfff,0,flags);
tileinfo.category = colourpri;
}
@ -181,7 +199,8 @@ TILE_GET_INFO_MEMBER(deco_bac06_device::get_pf16x16_tile_info)
if (m_rambank&1) tile_index+=0x1000;
int tile=m_pf_data[tile_index];
int colourpri=(tile>>12);
SET_TILE_INFO_MEMBER(m_tile_region_16,tile&0xfff,0,0);
int flags=(m_pf_control_0[0]&2) ? 0 : TILE_FLIPX;
SET_TILE_INFO_MEMBER(m_tile_region_16,tile&0xfff,0,flags);
tileinfo.category = colourpri;
}
@ -238,7 +257,10 @@ void deco_bac06_device::custom_tilemap_draw(bitmap_ind16 &bitmap,
if (control1)
{
scrollx = control1[0];
if (control0 && (control0[0]&2)==0) // Use of column major mode inverts scroll direction
scrollx = -control1[0] - 0x100;
else
scrollx = control1[0];
scrolly = control1[1];
}
@ -359,10 +381,23 @@ void deco_bac06_device::deco_bac06_pf_draw_bootleg(bitmap_ind16 &bitmap,const re
WRITE16_MEMBER( deco_bac06_device::pf_control_0_w )
{
int oldRegister0 = m_pf_control_0[0];
offset &= 3;
COMBINE_DATA(&m_pf_control_0[offset]);
bool dirtyAll = false;
if (offset==0)
{
if ((oldRegister0&2)!=(m_pf_control_0[offset]&2))
{
int flip = m_flip_screen;
set_flip_screen(flip^1);
set_flip_screen(flip); // Hack to force tilemap.cpp mappings_update() to be called
dirtyAll = true;
}
}
if (offset==2)
{
int newbank = m_pf_control_0[offset]&1;
@ -373,15 +408,20 @@ WRITE16_MEMBER( deco_bac06_device::pf_control_0_w )
if (strcmp(machine().system().name,"stadhero"))
printf("tilemap ram bank change to %d\n", newbank&1);
dirtyAll = true;
m_rambank = newbank&1;
m_pf8x8_tilemap[0]->mark_all_dirty();
m_pf8x8_tilemap[1]->mark_all_dirty();
m_pf8x8_tilemap[2]->mark_all_dirty();
m_pf16x16_tilemap[0]->mark_all_dirty();
m_pf16x16_tilemap[1]->mark_all_dirty();
m_pf16x16_tilemap[2]->mark_all_dirty();
}
}
if (dirtyAll)
{
m_pf8x8_tilemap[0]->mark_all_dirty();
m_pf8x8_tilemap[1]->mark_all_dirty();
m_pf8x8_tilemap[2]->mark_all_dirty();
m_pf16x16_tilemap[0]->mark_all_dirty();
m_pf16x16_tilemap[1]->mark_all_dirty();
m_pf16x16_tilemap[2]->mark_all_dirty();
}
}
READ16_MEMBER( deco_bac06_device::pf_control_1_r )

View File

@ -61,7 +61,7 @@ void deco_mxc06_device::draw_sprites( bitmap_ind16 &bitmap, const rectangle &cli
offs = 0;
while (offs < m_ramsize / 2)
{
int sx, sy, code, color, w, h, flipx, flipy, incy, flash, mult, x, y;
int sx, sy, code, color, w, h, flipx, flipy, incy, flash, mult, x, y, parentFlipY;
sy = spriteram[offs];
sx = spriteram[offs + 2];
@ -70,12 +70,9 @@ void deco_mxc06_device::draw_sprites( bitmap_ind16 &bitmap, const rectangle &cli
flash = sx & 0x800;
flipx = sy & 0x2000;
flipy = sy & 0x4000;
parentFlipY = flipy = sy & 0x4000;
h = (1 << ((sy & 0x1800) >> 11)); /* 1x, 2x, 4x, 8x height */
w = (1 << ((sy & 0x0600) >> 9)); /* 1x, 2x, 4x, 8x width */
/* multi width used only on the title screen? */
sx = sx & 0x01ff;
sy = sy & 0x01ff;
@ -112,7 +109,7 @@ void deco_mxc06_device::draw_sprites( bitmap_ind16 &bitmap, const rectangle &cli
code &= ~(h - 1);
// not affected by flipscreen
if (spriteram[offs] & 0x4000)
if (parentFlipY) // in the case of multi-width sprites the y flip bit is set by the parent
incy = -1;
else
{