Plug & Play improvements (Elan / Air Blaster Joystick) (#5856)

* elan video code improvements (nw)

* fix x scroll on some air blaster bosses (non-split mode scrolling)
This commit is contained in:
David Haywood 2019-11-05 02:34:40 +00:00 committed by R. Belmont
parent b053f1ee78
commit 5dc487c34d
3 changed files with 89 additions and 120 deletions

View File

@ -242,13 +242,8 @@ void elan_eu3a05_state::elan_eu3a05_map(address_map &map)
map(0x502a, 0x502a).rw(m_vid, FUNC(elan_eu3a05vid_device::tile_gfxbase_hi_r), FUNC(elan_eu3a05vid_device::tile_gfxbase_hi_w)); // tilebase map(0x502a, 0x502a).rw(m_vid, FUNC(elan_eu3a05vid_device::tile_gfxbase_hi_r), FUNC(elan_eu3a05vid_device::tile_gfxbase_hi_w)); // tilebase
map(0x502b, 0x502b).rw(m_vid, FUNC(elan_eu3a05vid_device::sprite_gfxbase_lo_r), FUNC(elan_eu3a05vid_device::sprite_gfxbase_lo_w)); // tilebase (spr?) map(0x502b, 0x502b).rw(m_vid, FUNC(elan_eu3a05vid_device::sprite_gfxbase_lo_r), FUNC(elan_eu3a05vid_device::sprite_gfxbase_lo_w)); // tilebase (spr?)
map(0x502c, 0x502c).rw(m_vid, FUNC(elan_eu3a05vid_device::sprite_gfxbase_hi_r), FUNC(elan_eu3a05vid_device::sprite_gfxbase_hi_w)); // tilebase (spr?) map(0x502c, 0x502c).rw(m_vid, FUNC(elan_eu3a05vid_device::sprite_gfxbase_hi_r), FUNC(elan_eu3a05vid_device::sprite_gfxbase_hi_w)); // tilebase (spr?)
map(0x502d, 0x502d).ram(); map(0x502d, 0x502e).rw(m_vid, FUNC(elan_eu3a05vid_device::splitpos_r), FUNC(elan_eu3a05vid_device::splitpos_w)); // split position
map(0x502e, 0x502e).rw(m_vid, FUNC(elan_eu3a05vid_device::splitpos_r), FUNC(elan_eu3a05vid_device::splitpos_w)); // split position map(0x502f, 0x5036).rw(m_vid, FUNC(elan_eu3a05vid_device::tile_scroll_r), FUNC(elan_eu3a05vid_device::tile_scroll_w)); // there are 4 scroll values in here, x scroll, y scroll, xscroll1 for split, xscroll2 for split (eu3a14 can do split too)
map(0x502f, 0x502f).ram();
map(0x5030, 0x5030).ram();
map(0x5031, 0x5032).rw(m_vid, FUNC(elan_eu3a05vid_device::tile_scroll_r), FUNC(elan_eu3a05vid_device::tile_scroll_w));
map(0x5033, 0x5036).rw(m_vid, FUNC(elan_eu3a05vid_device::tile_xscroll_r), FUNC(elan_eu3a05vid_device::tile_xscroll_w)); // there are 2 scroll values in here, air blaster 3d stages uses split scrolling like huntin'3 on eu3a14
// 5037 // 5037
map(0x5038, 0x5038).ram(); map(0x5038, 0x5038).ram();
@ -444,20 +439,26 @@ INTERRUPT_GEN_MEMBER(elan_eu3a05_state::interrupt)
} }
// Tetris (PAL version) has XTAL of 21.281370 /* Tetris (PAL version) has XTAL of 21.281370
// Air Blaster (PAL version) has XTAL of 21.2813 Air Blaster (PAL version) has XTAL of 21.2813
// what are the NTSC clocks? what are the NTSC clocks?
// not confirmed on Space Invaders, actual CPU clock unknown. not confirmed on Space Invaders, actual CPU clock unknown.
// 21281370 is the same value as a PAL SNES 21281370 is the same value as a PAL SNES
// game speed in Air Blaster appears to be limited entirely by CPU speed (and therefore needs to be around 2mhz at most to match hardware)
// low clock speed also helps with the badly programmed controls in Tetris
game logic speed (but not level scroll speed) in Air Blaster appears to be limited entirely by CPU speed (and therefore needs to be around 2-3mhz
at most to match hardware) - a divider of 8 gives something close to original hardware
it is unclear exactly what limits the clock speed (maybe video / sound causes waitstates? - dma in progress could also slow / stop the CPU
and is not going to be 'instant' on hardware)
using a low clock speed also helps with the badly programmed controls in Tetris as that likewise seems to run the game logic 'as fast as possible'
there don't appear to be any kind of blanking bits being checked.
*/
void elan_eu3a05_state::elan_eu3a05(machine_config &config) void elan_eu3a05_state::elan_eu3a05(machine_config &config)
{ {
/* basic machine hardware */ /* basic machine hardware */
M6502(config, m_maincpu, XTAL(21'281'370)/12); // wrong, this is the PAL clock M6502(config, m_maincpu, XTAL(21'281'370)/8); // wrong, this is the PAL clock
m_maincpu->set_addrmap(AS_PROGRAM, &elan_eu3a05_state::elan_eu3a05_map); m_maincpu->set_addrmap(AS_PROGRAM, &elan_eu3a05_state::elan_eu3a05_map);
m_maincpu->set_vblank_int("screen", FUNC(elan_eu3a05_state::interrupt)); m_maincpu->set_vblank_int("screen", FUNC(elan_eu3a05_state::interrupt));

View File

@ -35,19 +35,17 @@ void elan_eu3a05vid_device::device_reset()
m_vidctrl = 0x00; // need to default to an 8x8 mode for Space Invaders test mode at least m_vidctrl = 0x00; // need to default to an 8x8 mode for Space Invaders test mode at least
for (int i=0;i<2;i++) for (int i=0;i<4*2;i++)
m_tile_scroll[i] = 0x00; m_tile_scroll[i] = 0x00;
for (int i=0;i<2;i++)
m_tile_xscroll[i] = 0x00;
m_tile_gfxbase_lo_data = 0x00; m_tile_gfxbase_lo_data = 0x00;
m_tile_gfxbase_hi_data = 0x00; m_tile_gfxbase_hi_data = 0x00;
m_sprite_gfxbase_lo_data = 0x00; m_sprite_gfxbase_lo_data = 0x00;
m_sprite_gfxbase_hi_data = 0x00; m_sprite_gfxbase_hi_data = 0x00;
m_splitpos = 0x00; for (int i=0;i<2;i++)
m_splitpos[i] = 0x00;
} }
uint8_t elan_eu3a05vid_device::read_spriteram(int offset) uint8_t elan_eu3a05vid_device::read_spriteram(int offset)
@ -262,7 +260,7 @@ void elan_eu3a05vid_device::draw_tilemaps(screen_device& screen, bitmap_ind16& b
this doesn't handle 8x8 4bpp (not used by anything yet) this doesn't handle 8x8 4bpp (not used by anything yet)
*/ */
int scroll = (m_tile_scroll[1] << 8) | m_tile_scroll[0]; int scroll = get_scroll(1);
address_space& fullbankspace = m_bank->space(AS_PROGRAM); address_space& fullbankspace = m_bank->space(AS_PROGRAM);
// Phoenix scrolling actually skips a pixel, jumping from 0x001 to 0x1bf, scroll 0x000 isn't used, maybe it has other meanings? // Phoenix scrolling actually skips a pixel, jumping from 0x001 to 0x1bf, scroll 0x000 isn't used, maybe it has other meanings?
@ -331,10 +329,20 @@ void elan_eu3a05vid_device::draw_tilemaps(screen_device& screen, bitmap_ind16& b
int scrollx; int scrollx;
// split can be probably configured in more ways than this // split can be probably configured in more ways than this
if (drawline > m_splitpos) // exact enable conditions unclear
scrollx = get_xscroll(1); int splitpos = (m_splitpos[0] << 8) | m_splitpos[1];
if (splitpos != 0xffff)
{
if (drawline > splitpos)
scrollx = get_scroll(3);
else
scrollx = get_scroll(2);
}
else else
scrollx = get_xscroll(0); {
scrollx = get_scroll(0);
}
int base; int base;
@ -362,92 +370,64 @@ void elan_eu3a05vid_device::draw_tilemaps(screen_device& screen, bitmap_ind16& b
int colour = attr & 0xf0; int colour = attr & 0xf0;
if (m_vidctrl & 0x40) // 16x16 tiles /* 'tiles' are organized / extracted from 'texture' lines that form a 'page' the length of the rom
{ each texture line in 8bpp mode is 256 bytes
if (m_vidctrl & 0x20) // 4bpp mode each texture line in 4bpp mode is 128 bytes
{ in 8x8 mode these pages are 32 tiles wide
tile = (tile & 0xf) + ((tile & ~0xf) * 16); in 16x16 mode these pages are 16 tiles wide
tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5); tiles can start on any line
}
else
{
tile = (tile & 0xf) + ((tile & ~0xf) * 16);
tile <<= 1;
tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5); it is unclear what the behavior is if the tile starts at the far edge of a line (wrap around on line?)
}
} this is eu3a05 specific, eu3a14 uses a more traditional approach
else */
{
if (m_vidctrl & 0x20) // 4bpp const int tilespersrcline = 256 / tilexsize;
{ const int tilespersrcline_mask = tilespersrcline - 1;
// TODO
tile = 0x0000;//machine().rand() & 0x1ff; tile = (tile & tilespersrcline_mask) + ((tile & ~tilespersrcline_mask) * tilexsize);
}
else if (!(m_vidctrl & 0x20)) // 8bpp
{ tile <<= 1;
tile = (tile & 0x1f) + ((tile & ~0x1f) * 8);
tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5); if (!(m_vidctrl & 0x40)) // 8*8 tiles
} tile >>= 1;
}
tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5);
uint16_t* row = &bitmap.pix16(drawline); uint16_t* row = &bitmap.pix16(drawline);
if (m_vidctrl & 0x40) // 16x16 tiles if (m_vidctrl & 0x20) // 4bpp
{ {
if (m_vidctrl & 0x20) // 4bpp for (int xx = 0; xx < tilexsize; xx += 2)
{ {
for (int xx = 0; xx < tilexsize; xx += 2) int realaddr = ((tile + i * 16) << 3) + (xx >> 1);
{ uint8_t pix = fullbankspace.read_byte(realaddr);
int realaddr = ((tile + i * 16) << 3) + (xx >> 1);
uint8_t pix = fullbankspace.read_byte(realaddr);
int drawxpos; int drawxpos;
drawxpos = x * 16 + xx + 0; drawxpos = x * tilexsize + xx + 0 - scrollx;
drawxpos &= 0x1ff; drawxpos &= 0x1ff;
if ((drawxpos >= 0) && (drawxpos < 256)) if ((drawxpos >= 0) && (drawxpos < 256))
row[drawxpos] = ((pix & 0xf0) >> 4) + colour; row[drawxpos] = ((pix & 0xf0) >> 4) + colour;
drawxpos = x * 16 + xx + 1; drawxpos = x * tilexsize + xx + 1 - scrollx;
drawxpos &= 0x1ff; drawxpos &= 0x1ff;
if ((drawxpos >= 0) && (drawxpos < 256)) if ((drawxpos >= 0) && (drawxpos < 256))
row[drawxpos] = ((pix & 0x0f) >> 0) + colour; row[drawxpos] = ((pix & 0x0f) >> 0) + colour;
}
}
else // 8bpp
{
for (int xx = 0; xx < tilexsize; xx++)
{
int realaddr = ((tile + i * 32) << 3) + xx;
uint8_t pix = fullbankspace.read_byte(realaddr);
int drawxpos = x * 16 + xx;
drawxpos &= 0x1ff;
if ((drawxpos >= 0) && (drawxpos < 256))
row[drawxpos] = (pix + ((colour & 0x70) << 1)) & 0xff;
}
} }
} }
else // 8x8 tiles else // 8bpp
{ {
if (m_vidctrl & 0x20) // 4bpp for (int xx = 0; xx < tilexsize; xx++)
{ {
// TODO int realaddr = ((tile + i * 32) << 3) + xx;
} uint8_t pix = fullbankspace.read_byte(realaddr);
else
{
for (int xx = 0; xx < tilexsize; xx++)
{
const int realaddr = ((tile + i * 32) << 3) + xx;
const uint8_t pix = fullbankspace.read_byte(realaddr);
int drawxpos = x * tilexsize + xx - scrollx; int drawxpos = x * tilexsize + xx - scrollx;
drawxpos &= 0x1ff; drawxpos &= 0x1ff;
if ((drawxpos >= 0) && (drawxpos < 256))
if ((drawxpos >= 0) && (drawxpos < 256)) row[drawxpos] = (pix + ((colour & 0x70) << 1)) & 0xff;
row[drawxpos] = (pix + ((colour & 0x70) << 1)) & 0xff;
}
} }
} }
} }
@ -533,32 +513,24 @@ WRITE8_MEMBER(elan_eu3a05vid_device::tile_scroll_w)
m_tile_scroll[offset] = data; m_tile_scroll[offset] = data;
} }
READ8_MEMBER(elan_eu3a05vid_device::tile_xscroll_r)
{
return m_tile_xscroll[offset];
}
WRITE8_MEMBER(elan_eu3a05vid_device::tile_xscroll_w)
{
m_tile_xscroll[offset] = data;
}
READ8_MEMBER(elan_eu3a05vid_device::splitpos_r) READ8_MEMBER(elan_eu3a05vid_device::splitpos_r)
{ {
return m_splitpos; return m_splitpos[offset];
} }
WRITE8_MEMBER(elan_eu3a05vid_device::splitpos_w) WRITE8_MEMBER(elan_eu3a05vid_device::splitpos_w)
{ {
m_splitpos = data; m_splitpos[offset] = data;
} }
uint16_t elan_eu3a05vid_device::get_xscroll(int which) uint16_t elan_eu3a05vid_device::get_scroll(int which)
{ {
switch (which) switch (which)
{ {
case 0x0: return (m_tile_xscroll[1] << 8) | (m_tile_xscroll[0]); case 0x0: return (m_tile_scroll[1] << 8) | (m_tile_scroll[0]); // xscroll
case 0x1: return (m_tile_xscroll[3] << 8) | (m_tile_xscroll[2]); case 0x1: return (m_tile_scroll[3] << 8) | (m_tile_scroll[2]); // yscroll
case 0x2: return (m_tile_scroll[5] << 8) | (m_tile_scroll[4]); // xsplit 1 scroll
case 0x3: return (m_tile_scroll[7] << 8) | (m_tile_scroll[6]); // scplit 2 scroll
} }
return 0x0000; return 0x0000;

View File

@ -34,13 +34,10 @@ public:
DECLARE_READ8_MEMBER(tile_scroll_r); DECLARE_READ8_MEMBER(tile_scroll_r);
DECLARE_WRITE8_MEMBER(tile_scroll_w); DECLARE_WRITE8_MEMBER(tile_scroll_w);
DECLARE_READ8_MEMBER(tile_xscroll_r);
DECLARE_WRITE8_MEMBER(tile_xscroll_w);
DECLARE_READ8_MEMBER(splitpos_r); DECLARE_READ8_MEMBER(splitpos_r);
DECLARE_WRITE8_MEMBER(splitpos_w); DECLARE_WRITE8_MEMBER(splitpos_w);
uint16_t get_xscroll(int which); uint16_t get_scroll(int which);
uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
@ -61,10 +58,9 @@ private:
uint8_t m_sprite_gfxbase_lo_data; uint8_t m_sprite_gfxbase_lo_data;
uint8_t m_sprite_gfxbase_hi_data; uint8_t m_sprite_gfxbase_hi_data;
uint8_t m_tile_scroll[2]; uint8_t m_tile_scroll[4*2];
uint8_t m_tile_xscroll[4];
uint8_t m_splitpos; uint8_t m_splitpos[2];
bool get_tile_data(int base, int drawpri, int& tile, int &attr, int &unk2); bool get_tile_data(int base, int drawpri, int& tile, int &attr, int &unk2);
void draw_tilemaps(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int drawpri); void draw_tilemaps(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int drawpri);