summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/fuuki
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/fuuki')
-rw-r--r--src/mame/fuuki/fuukifg2.cpp738
-rw-r--r--src/mame/fuuki/fuukifg3.cpp873
-rw-r--r--src/mame/fuuki/fuukispr.cpp180
-rw-r--r--src/mame/fuuki/fuukispr.h39
-rw-r--r--src/mame/fuuki/fuukitmap.cpp263
-rw-r--r--src/mame/fuuki/fuukitmap.h119
6 files changed, 2212 insertions, 0 deletions
diff --git a/src/mame/fuuki/fuukifg2.cpp b/src/mame/fuuki/fuukifg2.cpp
new file mode 100644
index 00000000000..07107373944
--- /dev/null
+++ b/src/mame/fuuki/fuukifg2.cpp
@@ -0,0 +1,738 @@
+// license:BSD-3-Clause
+// copyright-holders: Luca Elia, Paul Priest
+
+/***************************************************************************
+
+ -= Fuuki 16 Bit Games =-
+
+ driver by Luca Elia (l.elia@tin.it)
+
+
+Main CPU : M68000
+Sound Chips : YM2203 + YM3812 + M6295
+Video Chips : FI-002K (208pin PQFP, GA2)
+ FI-003K (208pin PQFP, GA3)
+Other : Mitsubishi M60067-0901FP 452100 (208pin PQFP, GA1)
+
+
+---------------------------------------------------------------------------
+Year + Game
+---------------------------------------------------------------------------
+95 Susume! Mile Smile / Go Go! Mile Smile
+96 Gyakuten!! Puzzle Bancho
+---------------------------------------------------------------------------
+
+Do NOT trust the Service Mode for dipswitch settings for Go Go! Mile Smile:
+ Service Mode shows Coin A as SW2:3-5 & Coin B as SW2:6-8, but the game ignores the
+ setting of Coin B and only uses the settings for Coin A, except for Coin B "Free Play"
+ The game says Press 1 or 2, and will start the game, but jumps right to the Game Over
+ and "Continue" countdown.
+The Service Mode is WAY off on effects of dipswitches for the earlier set!!! It reports
+ the effects of MAME's SW1:3-8 have been moved to SW1:2-7 and Demo Sound has moved to
+ SW2:7. What MAME shows as settings are according to actual game effect and reflect what
+ the manual states.
+
+
+To Do:
+
+- Raster effects (level 5 interrupt is used for that). In pbancho
+ they involve changing the *vertical* scroll value of the layers
+ each scanline (when you are about to die, in the solo game).
+ In gogomile they weave the water backgrounds and do some
+ parallax scrolling on later levels. *partly done, could do with
+ some tweaking
+
+- The scroll values are generally wrong when flip screen is on and rasters are often incorrect
+
+***************************************************************************/
+
+#include "emu.h"
+
+#include "fuukispr.h"
+#include "fuukitmap.h"
+
+#include "cpu/z80/z80.h"
+#include "cpu/m68000/m68000.h"
+#include "machine/gen_latch.h"
+#include "sound/okim6295.h"
+#include "sound/ymopl.h"
+#include "sound/ymopn.h"
+
+#include "emupal.h"
+#include "screen.h"
+#include "speaker.h"
+#include "tilemap.h"
+
+
+namespace {
+
+class fuuki16_state : public driver_device
+{
+public:
+ fuuki16_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_audiocpu(*this, "audiocpu")
+ , m_oki(*this, "oki")
+ , m_gfxdecode(*this, "gfxdecode")
+ , m_screen(*this, "screen")
+ , m_palette(*this, "palette")
+ , m_fuukispr(*this, "fuukispr")
+ , m_fuukitmap(*this, "fuukitmap")
+ , m_soundlatch(*this, "soundlatch")
+ , m_spriteram(*this, "spriteram")
+ , m_soundbank(*this, "soundbank")
+ { }
+
+ void fuuki16(machine_config &config);
+
+protected:
+ virtual void machine_start() override ATTR_COLD;
+ virtual void video_start() override ATTR_COLD;
+
+private:
+ // devices
+ required_device<cpu_device> m_maincpu;
+ required_device<cpu_device> m_audiocpu;
+ required_device<okim6295_device> m_oki;
+ required_device<gfxdecode_device> m_gfxdecode;
+ required_device<screen_device> m_screen;
+ required_device<palette_device> m_palette;
+ required_device<fuukispr_device> m_fuukispr;
+ required_device<fuukitmap_device> m_fuukitmap;
+ required_device<generic_latch_8_device> m_soundlatch;
+
+ // memory pointers
+ required_shared_ptr<u16> m_spriteram;
+
+ required_memory_bank m_soundbank;
+
+ void main_map(address_map &map) ATTR_COLD;
+ void sound_io_map(address_map &map) ATTR_COLD;
+ void sound_map(address_map &map) ATTR_COLD;
+
+ void sound_command_w(u8 data);
+ void sound_rombank_w(u8 data);
+ void oki_banking_w(u8 data);
+
+ void colpri_cb(u32 &colour, u32 &pri_mask);
+ u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+};
+
+
+/***************************************************************************
+
+ [ 4 Scrolling Layers ]
+
+ [ Layer 0 ] [ Layer 1 ] [ Layer 2 (double-buffered) ]
+
+ Tile Size: 16 x 16 x 4 16 x 16 x 8 8 x 8 x 4
+ Layer Size (tiles): 64 x 32 64 x 32 64 x 32
+
+ [ 1024? Zooming Sprites ]
+
+ Sprites are made of 16 x 16 x 4 tiles. Size can vary from 1 to 16
+ tiles both horizontally and vertically.
+ There is zooming (from full size to half size) and 4 levels of
+ priority (wrt layers)
+
+ * Note: the game does hardware assisted raster effects *
+
+***************************************************************************/
+
+/***************************************************************************
+
+
+ Video Hardware Init
+
+
+***************************************************************************/
+
+void fuuki16_state::video_start()
+{
+ m_fuukitmap->set_transparent_pen(0, 0x0f); // 4 bits
+ m_fuukitmap->set_transparent_pen(1, 0xff); // 8 bits
+ m_fuukitmap->set_transparent_pen(2, 0x0f); // 4 bits
+
+ m_fuukitmap->gfx(1)->set_granularity(16); // 256 colour tiles with palette selectable on 16 colour boundaries
+}
+
+
+void fuuki16_state::colpri_cb(u32 &colour, u32 &pri_mask)
+{
+ const u8 priority = (colour >> 6) & 3;
+ switch (priority)
+ {
+ case 3: pri_mask = 0xf0 | 0xcc | 0xaa; break; // behind all layers
+ case 2: pri_mask = 0xf0 | 0xcc; break; // behind fg + middle layer
+ case 1: pri_mask = 0xf0; break; // behind fg layer
+ case 0:
+ default: pri_mask = 0; // above all
+ }
+ colour &= 0x3f;
+}
+
+
+u32 fuuki16_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ m_fuukitmap->prepare();
+ flip_screen_set(m_fuukitmap->flip_screen());
+
+ /* The backmost tilemap decides the background color(s) but sprites can
+ go below the opaque pixels of that tilemap. We thus need to mark the
+ transparent pixels of this layer with a different priority value */
+// m_fuukitmap->draw_layer(screen, bitmap, cliprect, m_fuukitmap->tmap_back(), TILEMAP_DRAW_OPAQUE, 0);
+
+ /* Actually, bg colour is simply the last pen i.e. 0x1fff -pjp */
+ bitmap.fill((0x800 * 4) - 1, cliprect);
+ screen.priority().fill(0, cliprect);
+
+ m_fuukitmap->draw_layer(screen, bitmap, cliprect, m_fuukitmap->tmap_back(), 0, 1);
+ m_fuukitmap->draw_layer(screen, bitmap, cliprect, m_fuukitmap->tmap_middle(), 0, 2);
+ m_fuukitmap->draw_layer(screen, bitmap, cliprect, m_fuukitmap->tmap_front(), 0, 4);
+
+ m_fuukispr->draw_sprites(screen, bitmap, cliprect, flip_screen(), m_spriteram, m_spriteram.bytes() / 2);
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// memory - main CPU
+//-------------------------------------------------
+
+void fuuki16_state::sound_command_w(u8 data)
+{
+ m_soundlatch->write(data);
+ m_audiocpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
+// m_maincpu->spin_until_time(attotime::from_usec(50)); // Allow the other CPU to reply
+ machine().scheduler().perfect_quantum(attotime::from_usec(50)); // Fixes glitching in rasters
+}
+
+void fuuki16_state::main_map(address_map &map)
+{
+ map(0x000000, 0x0fffff).rom();
+ map(0x400000, 0x40ffff).ram();
+ map(0x500000, 0x507fff).m(m_fuukitmap, FUNC(fuukitmap_device::vram_map));
+ map(0x600000, 0x601fff).mirror(0x008000).ram().share(m_spriteram); // mirrored?
+ map(0x700000, 0x703fff).ram().w(m_palette, FUNC(palette_device::write16)).share("palette");
+ map(0x800000, 0x800001).portr("SYSTEM");
+ map(0x810000, 0x810001).portr("P1_P2");
+ map(0x880000, 0x880001).portr("DSW");
+ map(0x8a0001, 0x8a0001).w(FUNC(fuuki16_state::sound_command_w));
+ map(0x8c0000, 0x8effff).m(m_fuukitmap, FUNC(fuukitmap_device::vregs_map));
+}
+
+
+//-------------------------------------------------
+// memory - sound CPU
+//-------------------------------------------------
+
+void fuuki16_state::sound_rombank_w(u8 data)
+{
+ if (data <= 2)
+ m_soundbank->set_entry(data);
+ else
+ logerror("CPU #1 - PC %04X: unknown bank bits: %02X\n", m_audiocpu->pc(), data);
+}
+
+void fuuki16_state::oki_banking_w(u8 data)
+{
+ /*
+ data & 0x06 is always equals to data & 0x60
+ data & 0x10 is always set
+ */
+
+ m_oki->set_rom_bank((data & 6) >> 1);
+}
+
+void fuuki16_state::sound_map(address_map &map)
+{
+ map(0x0000, 0x5fff).rom(); // ROM
+ map(0x6000, 0x7fff).ram(); // RAM
+ map(0x8000, 0xffff).bankr(m_soundbank); // Banked ROM
+}
+
+void fuuki16_state::sound_io_map(address_map &map)
+{
+ map.global_mask(0xff);
+ map(0x00, 0x00).w(FUNC(fuuki16_state::sound_rombank_w));
+ map(0x11, 0x11).r(m_soundlatch, FUNC(generic_latch_8_device::read)).nopw(); // To Main CPU ?
+ map(0x20, 0x20).w(FUNC(fuuki16_state::oki_banking_w));
+ map(0x30, 0x30).nopw(); // ? In the NMI routine
+ map(0x40, 0x41).w("ym1", FUNC(ym2203_device::write));
+ map(0x50, 0x51).rw("ym2", FUNC(ym3812_device::read), FUNC(ym3812_device::write));
+ map(0x60, 0x60).r(m_oki, FUNC(okim6295_device::read));
+ map(0x61, 0x61).w(m_oki, FUNC(okim6295_device::write));
+}
+
+
+//-------------------------------------------------
+// input ports
+//-------------------------------------------------
+
+static INPUT_PORTS_START( gogomile )
+ PORT_START("SYSTEM") // $800000.w
+ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 )
+ PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_SERVICE1 )
+ PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_START1 )
+ PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_START2 )
+ PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_COIN2 )
+ PORT_BIT( 0xfe00, IP_ACTIVE_LOW, IPT_UNKNOWN )
+
+ PORT_START("P1_P2") // $810000.w
+ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
+ PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
+ PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
+ PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
+ PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
+ PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNKNOWN ) // There's code that uses
+ PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN ) // these unknown bits
+ PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
+
+ PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
+ PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
+ PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
+ PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
+ PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
+ PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNKNOWN )
+
+ PORT_START("DSW") // $880000.w
+ PORT_SERVICE_DIPLOC( 0x0001, IP_ACTIVE_LOW, "SW1:1" )
+ PORT_DIPNAME( 0x0002, 0x0002, "Demo Music" ) PORT_DIPLOCATION("SW1:2") // Game play sounds still play, only effects Music
+ PORT_DIPSETTING( 0x0000, DEF_STR( Off ) )
+ PORT_DIPSETTING( 0x0002, DEF_STR( On ) )
+ PORT_DIPNAME( 0x000c, 0x000c, DEF_STR( Difficulty ) ) PORT_DIPLOCATION("SW1:3,4")
+ PORT_DIPSETTING( 0x0000, DEF_STR( Easy ) )
+ PORT_DIPSETTING( 0x000c, DEF_STR( Normal ) )
+ PORT_DIPSETTING( 0x0008, DEF_STR( Hard ) )
+ PORT_DIPSETTING( 0x0004, DEF_STR( Very_Hard ) )
+ PORT_DIPNAME( 0x0030, 0x0030, DEF_STR( Language ) ) PORT_DIPLOCATION("SW1:5,6")
+ PORT_DIPSETTING( 0x0010, DEF_STR( Chinese ) )
+ PORT_DIPSETTING( 0x0030, DEF_STR( Japanese ) ) // Only setting to give a "For use only in...." Copyright Notice
+ PORT_DIPSETTING( 0x0000, DEF_STR( Korean ) )
+ PORT_DIPSETTING( 0x0020, DEF_STR( English ) )
+ PORT_DIPNAME( 0x00c0, 0x00c0, DEF_STR( Lives ) ) PORT_DIPLOCATION("SW1:7,8")
+ PORT_DIPSETTING( 0x0000, "2" )
+ PORT_DIPSETTING( 0x00c0, "3" )
+ PORT_DIPSETTING( 0x0080, "4" )
+ PORT_DIPSETTING( 0x0040, "5" )
+
+ PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Flip_Screen ) ) PORT_DIPLOCATION("SW2:1")
+ PORT_DIPSETTING( 0x0100, DEF_STR( Off ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( On ) )
+ PORT_DIPUNUSED_DIPLOC( 0x0200, 0x0200, "SW2:2" ) // Manual states this dip is "Unused"
+ PORT_DIPNAME( 0x1c00, 0x1c00, DEF_STR( Coinage ) ) PORT_DIPLOCATION("SW2:3,4,5")
+ PORT_DIPSETTING( 0x0400, DEF_STR( 4C_1C ) )
+ PORT_DIPSETTING( 0x1400, DEF_STR( 3C_1C ) )
+ PORT_DIPSETTING( 0x0c00, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0x1c00, DEF_STR( 1C_1C ) )
+ PORT_DIPSETTING( 0x1800, DEF_STR( 1C_2C ) )
+ PORT_DIPSETTING( 0x0800, DEF_STR( 1C_3C ) )
+ PORT_DIPSETTING( 0x1000, DEF_STR( 1C_4C ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( Free_Play ) )
+ PORT_DIPUNUSED_DIPLOC( 0x2000, 0x2000, "SW2:6" )
+ PORT_DIPUNUSED_DIPLOC( 0x4000, 0x4000, "SW2:7" )
+ PORT_DIPUNUSED_DIPLOC( 0x8000, 0x8000, "SW2:8" )
+/*
+ PORT_DIPNAME( 0xe000, 0xe000, DEF_STR( Coin_B ) ) PORT_DIPLOCATION("SW2:6,7,8")
+ PORT_DIPSETTING( 0x2000, DEF_STR( 4C_1C ) )
+ PORT_DIPSETTING( 0xa000, DEF_STR( 3C_1C ) )
+ PORT_DIPSETTING( 0x6000, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0xe000, DEF_STR( 1C_1C ) )
+ PORT_DIPSETTING( 0xc000, DEF_STR( 1C_2C ) )
+ PORT_DIPSETTING( 0x4000, DEF_STR( 1C_3C ) )
+ PORT_DIPSETTING( 0x8000, DEF_STR( 1C_4C ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( Free_Play ) )
+*/
+INPUT_PORTS_END
+
+
+static INPUT_PORTS_START( gogomileo ) // The earlier version has different coinage settings.
+ PORT_INCLUDE( gogomile )
+
+ PORT_MODIFY("DSW") // $880000.w
+ PORT_DIPNAME( 0x1c00, 0x1c00, DEF_STR( Coin_A ) ) PORT_DIPLOCATION("SW2:3,4,5")
+ PORT_DIPSETTING( 0x1800, DEF_STR( 4C_1C ) )
+ PORT_DIPSETTING( 0x1400, DEF_STR( 3C_1C ) )
+ PORT_DIPSETTING( 0x1000, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0x1c00, DEF_STR( 1C_1C ) )
+ PORT_DIPSETTING( 0x0400, DEF_STR( 1C_2C ) )
+ PORT_DIPSETTING( 0x0800, DEF_STR( 1C_3C ) )
+ PORT_DIPSETTING( 0x0c00, DEF_STR( 1C_4C ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( Free_Play ) )
+/*
+ PORT_DIPNAME( 0xe000, 0xe000, DEF_STR( Coin_B ) ) PORT_DIPLOCATION("SW2:6,7,8")
+ PORT_DIPSETTING( 0xc000, DEF_STR( 4C_1C ) )
+ PORT_DIPSETTING( 0xa000, DEF_STR( 3C_1C ) )
+ PORT_DIPSETTING( 0x8000, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0xe000, DEF_STR( 1C_1C ) )
+ PORT_DIPSETTING( 0x2000, DEF_STR( 1C_2C ) )
+ PORT_DIPSETTING( 0x4000, DEF_STR( 1C_3C ) )
+ PORT_DIPSETTING( 0x6000, DEF_STR( 1C_4C ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( Free_Play ) )
+*/
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( pbancho )
+ PORT_INCLUDE( gogomile )
+
+ PORT_MODIFY("SYSTEM") // $800000.w
+ PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 )
+ PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_SERVICE1 )
+
+ PORT_MODIFY("DSW") // $880000.w
+ PORT_DIPNAME( 0x001c, 0x001c, DEF_STR( Difficulty ) ) PORT_DIPLOCATION("SW1:3,4,5")
+ PORT_DIPSETTING( 0x0008, DEF_STR( Easiest ) ) // 1
+ PORT_DIPSETTING( 0x0010, DEF_STR( Easy ) ) // 2
+ PORT_DIPSETTING( 0x001c, DEF_STR( Normal ) ) // 3
+ PORT_DIPSETTING( 0x0000, "Normal, duplicate" ) // 3
+ PORT_DIPSETTING( 0x000c, "Normal, duplicate" ) // 3
+ PORT_DIPSETTING( 0x0014, "Normal, duplicate" ) // 3
+ PORT_DIPSETTING( 0x0018, DEF_STR( Hard ) ) // 4
+ PORT_DIPSETTING( 0x0004, DEF_STR( Hardest ) ) // 5
+ PORT_DIPNAME( 0x0060, 0x0060, "Lives (Vs Mode)" ) PORT_DIPLOCATION("SW1:6,7")
+ PORT_DIPSETTING( 0x0000, "1" ) // 1 1
+ PORT_DIPSETTING( 0x0060, "2" ) // 2 3
+ PORT_DIPSETTING( 0x0020, "2, duplicate" ) // 2 3
+ PORT_DIPSETTING( 0x0040, "3" ) // 3 5
+ PORT_DIPNAME( 0x0080, 0x0080, "? Senin Mode ?" ) PORT_DIPLOCATION("SW1:8")
+ PORT_DIPSETTING( 0x0080, DEF_STR( Off ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( On ) )
+
+ PORT_DIPNAME( 0x0100, 0x0100, DEF_STR( Flip_Screen ) ) PORT_DIPLOCATION("SW2:1")
+ PORT_DIPSETTING( 0x0100, DEF_STR( Off ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( On ) )
+ PORT_DIPNAME( 0x0200, 0x0200, "Allow Versus Mode" ) PORT_DIPLOCATION("SW2:2") // "unused" in the manual?
+ PORT_DIPSETTING( 0x0000, DEF_STR( No ) )
+ PORT_DIPSETTING( 0x0200, DEF_STR( Yes ) )
+ PORT_DIPNAME( 0x1c00, 0x1c00, DEF_STR( Coin_A ) ) PORT_DIPLOCATION("SW2:3,4,5")
+ PORT_DIPSETTING( 0x0c00, DEF_STR( 4C_1C ) )
+ PORT_DIPSETTING( 0x1400, DEF_STR( 3C_1C ) )
+ PORT_DIPSETTING( 0x0400, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0x1c00, DEF_STR( 1C_1C ) )
+ PORT_DIPSETTING( 0x1000, DEF_STR( 1C_2C ) )
+ PORT_DIPSETTING( 0x0800, DEF_STR( 1C_3C ) )
+ PORT_DIPSETTING( 0x1800, DEF_STR( 1C_4C ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( Free_Play ) )
+ PORT_DIPNAME( 0xe000, 0xe000, DEF_STR( Coin_B ) ) PORT_DIPLOCATION("SW2:6,7,8")
+ PORT_DIPSETTING( 0x6000, DEF_STR( 4C_1C ) )
+ PORT_DIPSETTING( 0xa000, DEF_STR( 3C_1C ) )
+ PORT_DIPSETTING( 0x2000, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0xe000, DEF_STR( 1C_1C ) )
+ PORT_DIPSETTING( 0x8000, DEF_STR( 1C_2C ) )
+ PORT_DIPSETTING( 0x4000, DEF_STR( 1C_3C ) )
+ PORT_DIPSETTING( 0xc000, DEF_STR( 1C_4C ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( Free_Play ) )
+INPUT_PORTS_END
+
+
+
+//-------------------------------------------------
+// graphics layouts
+//-------------------------------------------------
+
+// 16x16x8
+static const gfx_layout layout_16x16x8 =
+{
+ 16,16,
+ RGN_FRAC(1,1),
+ 8,
+ { STEP4(0,1), STEP4(16,1) },
+ { STEP4(0,4), STEP4(16*2,4), STEP4(16*4,4), STEP4(16*6,4) },
+ { STEP16(0,16*8) },
+ 16*16*8
+};
+
+static GFXDECODE_START( gfx_fuuki16 )
+ GFXDECODE_ENTRY( "tiles_l0", 0, gfx_16x16x4_packed_msb, 0x400*0, 0x40 ) // [0] Layer 0
+ GFXDECODE_ENTRY( "tiles_l1", 0, layout_16x16x8, 0x400*1, 0x40 ) // [1] Layer 1
+ GFXDECODE_ENTRY( "tiles_l2", 0, gfx_8x8x4_packed_msb, 0x400*3, 0x40 ) // [2] Layer 2
+GFXDECODE_END
+
+
+//-------------------------------------------------
+// driver functions
+//-------------------------------------------------
+
+/*
+ - Interrupts (pbancho) -
+
+ Lev 1: Sets bit 5 of $400010. Prints "credit .." with sprites.
+ Lev 2: Sets bit 7 of $400010. Clears $8c0012.
+ It seems unused by the game.
+ Lev 3: VBlank.
+ Lev 5: Programmable to happen on a raster line. Used to do raster
+ effects when you die and its clearing the blocks
+ also used for water effects and titlescreen linescroll on gogomile
+*/
+
+void fuuki16_state::machine_start()
+{
+ u8 *rom = memregion("audiocpu")->base();
+
+ m_soundbank->configure_entries(0, 3, &rom[0x8000], 0x8000);
+}
+
+
+void fuuki16_state::fuuki16(machine_config &config)
+{
+ // basic machine hardware
+ M68000(config, m_maincpu, XTAL(32'000'000) / 2); // 16 MHz
+ m_maincpu->set_addrmap(AS_PROGRAM, &fuuki16_state::main_map);
+
+ Z80(config, m_audiocpu, XTAL(12'000'000) / 2); // 6 MHz
+ m_audiocpu->set_addrmap(AS_PROGRAM, &fuuki16_state::sound_map);
+ m_audiocpu->set_addrmap(AS_IO, &fuuki16_state::sound_io_map);
+
+ // video hardware
+ SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
+ m_screen->set_refresh_hz(60);
+ m_screen->set_size(320, 256);
+ m_screen->set_visarea(0, 320-1, 0, 256-16-1);
+ m_screen->set_screen_update(FUNC(fuuki16_state::screen_update));
+ m_screen->set_palette(m_palette);
+
+ GFXDECODE(config, m_gfxdecode, m_palette, gfx_fuuki16);
+ PALETTE(config, m_palette, palette_device::BLACK).set_format(palette_device::xRGB_555, 0x4000 / 2);
+
+ FUUKI_SPRITE(config, m_fuukispr, 0);
+ m_fuukispr->set_palette(m_palette);
+ m_fuukispr->set_color_base(0x400*2);
+ m_fuukispr->set_color_num(0x40);
+ m_fuukispr->set_colpri_callback(FUNC(fuuki16_state::colpri_cb));
+
+ FUUKI_TILEMAP(config, m_fuukitmap, 0, m_palette, gfx_fuuki16);
+ m_fuukitmap->set_screen(m_screen);
+ m_fuukitmap->level_1_irq_callback().set_inputline(m_maincpu, 1, HOLD_LINE);
+ m_fuukitmap->vblank_irq_callback().set_inputline(m_maincpu, 3, HOLD_LINE);
+ m_fuukitmap->raster_irq_callback().set_inputline(m_maincpu, 5, HOLD_LINE);
+ m_fuukitmap->set_xoffs(0x1f3, 0x103);
+ m_fuukitmap->set_yoffs(0x3f6, 0x2a7);
+ m_fuukitmap->set_layer2_xoffs(0x10);
+ //m_fuukitmap->set_layer2_yoffs(0x02);
+
+ // sound hardware
+ SPEAKER(config, "mono").front_center();
+
+ GENERIC_LATCH_8(config, m_soundlatch);
+
+ ym2203_device &ym1(YM2203(config, "ym1", XTAL(28'640'000) / 8)); // 3.58 MHz
+ ym1.add_route(ALL_OUTPUTS, "mono", 0.15);
+
+ ym3812_device &ym2(YM3812(config, "ym2", XTAL(28'640'000) / 8)); // 3.58 MHz
+ ym2.irq_handler().set_inputline(m_audiocpu, 0);
+ ym2.add_route(ALL_OUTPUTS, "mono", 0.30);
+
+ OKIM6295(config, m_oki, XTAL(32'000'000) / 32, okim6295_device::PIN7_HIGH); // 1 MHz
+ m_oki->add_route(ALL_OUTPUTS, "mono", 0.85);
+}
+
+
+//-------------------------------------------------
+// ROM loading
+//-------------------------------------------------
+
+/***************************************************************************
+
+ Go! Go! Mile Smile / Susume! Mile Smile
+
+(c)1995 Fuuki
+FG-1C AI AM-2 (same board as Gyakuten Puzzle Banchou)
+
+CPU : TMP68HC000P-16
+Sound: Z80 YM2203C YM3812 M6295 Y3014Bx2
+OSC : 32.00000MHz(OSC1) 28.64000MHz(OSC2) 12.000MHz(Xtal1)
+
+ROMs:
+fp2.rom2 - Main programs (27c4000)
+fp1.rom1 /
+
+lh538n1d.rom25 - Samples (Sharp mask, read as 27c8001)
+fs1.rom24 - Sound program (27c010)
+
+lh5370h8.rom11 - Sprites? (Sharp Mask, read as 27c160)
+lh5370ha.rom12 |
+lh5370h7.rom15 |
+lh5370h9.rom16 /
+
+lh537k2r.rom20 - Tiles? (Sharp Mask, read as 27c160)
+lh5370hb.rom19 |
+lh5370h6.rom3 /
+
+Custom chips:
+FI-002K (208pin PQFP, GA2)
+FI-003K (208pin PQFP, GA3)
+
+
+Others:
+Mitsubishi M60067-0901FP 452100 (208pin PQFP, GA1)
+5 GALs (GAL16V8B, not dumped)
+
+Measured clocks:
+
+ 68k - 16mhz, OSC1 / 2
+ Z80 - 6mhz, Xtal1 / 2
+ M6295 - 1mhz, OSC1 / 32
+YM2203C - 3.58mhz, OSC2 / 8
+ YM3812 - 3.58mhz, OSC2 / 8
+
+***************************************************************************/
+
+ROM_START( gogomile )
+ ROM_REGION( 0x100000, "maincpu", 0 ) // 68000 code
+ ROM_LOAD16_BYTE( "fp2n.rom2", 0x000000, 0x080000, CRC(e73583a0) SHA1(05c6ee5cb2c151b32c462e8b920f9a57fb6cce5b) )
+ ROM_LOAD16_BYTE( "fp1n.rom1", 0x000001, 0x080000, CRC(7b110824) SHA1(980e326d3b9e113ed522be3076663a249da4e739) )
+
+ ROM_REGION( 0x20000, "audiocpu", 0 ) // Z80 code
+ ROM_LOAD( "fs1.rom24", 0x00000, 0x20000, CRC(4e4bd371) SHA1(429e776135ce8960e147762763d952d16ed3f9d4) )
+
+ ROM_REGION( 0x200000, "fuukispr", 0 ) // 16x16x4 sprites
+ ROM_LOAD16_WORD_SWAP( "lh537k2r.rom20", 0x000000, 0x200000, CRC(525dbf51) SHA1(f21876676cc60ed65bc86884da894b24830826bb) )
+
+ ROM_REGION( 0x200000, "tiles_l0", 0 ) // 16x16x4 tiles
+ ROM_LOAD16_WORD_SWAP( "lh5370h6.rom3", 0x000000, 0x200000, CRC(e2ca7107) SHA1(7174c2e1e2106275ad41b53af22651dca492367a) ) // x11xxxxxxxxxxxxxxxxxx = 0xFF
+
+ ROM_REGION( 0x800000, "tiles_l1", 0 ) // 16x16x8 tiles
+ ROM_LOAD32_WORD_SWAP( "lh5370h7.rom15", 0x000000, 0x200000, CRC(34921680) SHA1(d9862f106caa14ea6ad925174e6bf2d542511593) )
+ ROM_LOAD32_WORD_SWAP( "lh5370h8.rom11", 0x000002, 0x200000, CRC(9961c925) SHA1(c47b4f19f090527b3e0c04dd046aa9cd51ca0e16) )
+ ROM_LOAD32_WORD_SWAP( "lh5370h9.rom16", 0x400000, 0x200000, CRC(e0118483) SHA1(36f9068e6c81c171b4426c3794277742bbc926f5) )
+ ROM_LOAD32_WORD_SWAP( "lh5370ha.rom12", 0x400002, 0x200000, CRC(5f2a87de) SHA1(d7ed8f01b40aaf58126aaeee10ec7d948a144080) )
+
+ ROM_REGION( 0x200000, "tiles_l2", 0 ) // 8x8x4 tiles
+ ROM_LOAD16_WORD_SWAP( "lh5370hb.rom19", 0x000000, 0x200000, CRC(bd1e896f) SHA1(075f7600cbced1d285cf32fc196844720eb12671) ) // FIRST AND SECOND HALF IDENTICAL
+
+ // 0x40000 * 4: sounds+speech (Japanese), sounds+speech (English)
+ ROM_REGION( 0x100000, "oki", 0 ) // samples
+ ROM_LOAD( "lh538n1d.rom25", 0x000000, 0x100000, CRC(01622a95) SHA1(8d414bfc6dcfab1cf9cfe5738eb5c2ff31b77df6) ) // 0x40000 * 4
+ROM_END
+
+ROM_START( gogomileo )
+ ROM_REGION( 0x100000, "maincpu", 0 ) // 68000 code
+ ROM_LOAD16_BYTE( "fp2.rom2", 0x000000, 0x080000, CRC(28fd3e4e) SHA1(3303e5759c0781035c74354587e1916719695754) ) // 1xxxxxxxxxxxxxxxxxx = 0xFF
+ ROM_LOAD16_BYTE( "fp1.rom1", 0x000001, 0x080000, CRC(35a5fc45) SHA1(307207791cee7f40e88feffc5805ac25008a8566) ) // 1xxxxxxxxxxxxxxxxxx = 0xFF
+
+ ROM_REGION( 0x20000, "audiocpu", 0 ) // Z80 code
+ ROM_LOAD( "fs1.rom24", 0x00000, 0x20000, CRC(4e4bd371) SHA1(429e776135ce8960e147762763d952d16ed3f9d4) )
+
+ ROM_REGION( 0x200000, "fuukispr", 0 ) // 16x16x4 sprites
+ ROM_LOAD16_WORD_SWAP( "lh537k2r.rom20", 0x000000, 0x200000, CRC(525dbf51) SHA1(f21876676cc60ed65bc86884da894b24830826bb) )
+
+ ROM_REGION( 0x200000, "tiles_l0", 0 ) // 16x16x4 tiles
+ ROM_LOAD16_WORD_SWAP( "lh5370h6.rom3", 0x000000, 0x200000, CRC(e2ca7107) SHA1(7174c2e1e2106275ad41b53af22651dca492367a) ) // x11xxxxxxxxxxxxxxxxxx = 0xFF
+
+ ROM_REGION( 0x800000, "tiles_l1", 0 ) // 16x16x8 tiles
+ ROM_LOAD32_WORD_SWAP( "lh5370h7.rom15", 0x000000, 0x200000, CRC(34921680) SHA1(d9862f106caa14ea6ad925174e6bf2d542511593) )
+ ROM_LOAD32_WORD_SWAP( "lh5370h8.rom11", 0x000002, 0x200000, CRC(9961c925) SHA1(c47b4f19f090527b3e0c04dd046aa9cd51ca0e16) )
+ ROM_LOAD32_WORD_SWAP( "lh5370h9.rom16", 0x400000, 0x200000, CRC(e0118483) SHA1(36f9068e6c81c171b4426c3794277742bbc926f5) )
+ ROM_LOAD32_WORD_SWAP( "lh5370ha.rom12", 0x400002, 0x200000, CRC(5f2a87de) SHA1(d7ed8f01b40aaf58126aaeee10ec7d948a144080) )
+
+ ROM_REGION( 0x200000, "tiles_l2", 0 ) // 8x8x4 tiles
+ ROM_LOAD16_WORD_SWAP( "lh5370hb.rom19", 0x000000, 0x200000, CRC(bd1e896f) SHA1(075f7600cbced1d285cf32fc196844720eb12671) ) // FIRST AND SECOND HALF IDENTICAL
+
+ // 0x40000 * 4: sounds+speech (Japanese), sounds+speech (English)
+ ROM_REGION( 0x100000, "oki", 0 ) // samples
+ ROM_LOAD( "lh538n1d.rom25", 0x000000, 0x100000, CRC(01622a95) SHA1(8d414bfc6dcfab1cf9cfe5738eb5c2ff31b77df6) ) // 0x40000 * 4
+ROM_END
+
+
+
+/***************************************************************************
+
+ Gyakuten!! Puzzle Bancho
+
+(c)1996 Fuuki
+FG-1C AI AM-2
+
+CPU : TMP68HC000P-16
+Sound: Z80 YM2203 YM3812 M6295
+OSC : 32.00000MHz(OSC1) 28.64000MHz(OSC2) 12.000MHz(Xtal1)
+
+ROMs:
+no1.rom2 - Main program (even)(27c4000)
+no2.rom1 - Main program (odd) (27c4000)
+
+no3.rom25 - Samples (27c2001)
+no4.rom24 - Sound program (27c010)
+
+61.rom11 - Graphics (Mask, read as 27c160)
+59.rom15 |
+58.rom20 |
+60.rom3 /
+
+Custom chips:
+FI-002K (208pin PQFP, GA2)
+FI-003K (208pin PQFP, GA3)
+
+Others:
+Mitsubishi M60067-0901FP 452100 (208pin PQFP, GA1)
+
+***************************************************************************/
+
+ROM_START( pbancho ) // ROMs NO1 & NO2 had an addition block dot on labels
+ ROM_REGION( 0x100000, "maincpu", 0 ) // 68000 code
+ ROM_LOAD16_BYTE( "no1..rom2", 0x000000, 0x080000, CRC(e607eca6) SHA1(be9156d2a336a04fb9ff147b0d0287d8ff2ccfc5) ) // 1xxxxxxxxxxxxxxxxxx = 0xFF
+ ROM_LOAD16_BYTE( "no2..rom1", 0x000001, 0x080000, CRC(ee15b423) SHA1(6da7ba9dd785dfcf919c030e126daf8d6750d072) ) // 1xxxxxxxxxxxxxxxxxx = 0xFF
+
+ ROM_REGION( 0x20000, "audiocpu", 0 ) // Z80 code
+ ROM_LOAD( "no4.rom23", 0x00000, 0x20000, CRC(dfbfdb81) SHA1(84b0cbe843a9bbae43975afdbd029a9b76fd488b) )
+
+ ROM_REGION( 0x200000, "fuukispr", 0 ) // 16x16x4 sprites
+ ROM_LOAD16_WORD_SWAP( "58.rom20", 0x000000, 0x200000, CRC(4dad0a2e) SHA1(a4f70557503110a5457b9096a79a5f249095fa55) )
+
+ ROM_REGION( 0x200000, "tiles_l0", 0 ) // 16x16x4 tiles
+ ROM_LOAD16_WORD_SWAP( "60.rom3", 0x000000, 0x200000, CRC(a50a3c1b) SHA1(a2b30f9f83f5dc2e069d7559aefbda9929fc640c) )
+
+ ROM_REGION( 0x400000, "tiles_l1", 0 ) // 16x16x8 tiles
+ ROM_LOAD32_WORD_SWAP( "59.rom15", 0x000000, 0x200000, CRC(b83dcb70) SHA1(b0b9df451535d85612fa095b4f694cf2e7930bca) )
+ ROM_LOAD32_WORD_SWAP( "61.rom11", 0x000002, 0x200000, CRC(7f1213b9) SHA1(f8d6432b270c4d0954602e430ddd26841eb05656) )
+
+ ROM_REGION( 0x200000, "tiles_l2", 0 ) // 8x8x4 tiles
+ ROM_LOAD16_WORD_SWAP( "60.rom3", 0x000000, 0x200000, CRC(a50a3c1b) SHA1(a2b30f9f83f5dc2e069d7559aefbda9929fc640c) ) // ?maybe?
+
+ ROM_REGION( 0x040000, "oki", 0 ) // samples
+ ROM_LOAD( "n03.rom25", 0x000000, 0x040000, CRC(a7bfb5ea) SHA1(61937eae4f8855bc09c494aff52d76d41dc3b76a) )
+ROM_END
+
+ROM_START( pbanchoa )
+ ROM_REGION( 0x100000, "maincpu", 0 ) // 68000 code
+ ROM_LOAD16_BYTE( "no1.rom2", 0x000000, 0x080000, CRC(1b4fd178) SHA1(02cf3d2554b29cd253470d68ea959738f3b98dbe) ) // 1xxxxxxxxxxxxxxxxxx = 0xFF
+ ROM_LOAD16_BYTE( "no2.rom1", 0x000001, 0x080000, CRC(9cf510a5) SHA1(08e79b5bbd1c011c32f82dd15fba42d7898861be) ) // 1xxxxxxxxxxxxxxxxxx = 0xFF
+
+ ROM_REGION( 0x20000, "audiocpu", 0 ) // Z80 code
+ ROM_LOAD( "no4.rom23", 0x00000, 0x20000, CRC(dfbfdb81) SHA1(84b0cbe843a9bbae43975afdbd029a9b76fd488b) )
+
+ ROM_REGION( 0x200000, "fuukispr", 0 ) // 16x16x4 sprites
+ ROM_LOAD16_WORD_SWAP( "58.rom20", 0x000000, 0x200000, CRC(4dad0a2e) SHA1(a4f70557503110a5457b9096a79a5f249095fa55) )
+
+ ROM_REGION( 0x200000, "tiles_l0", 0 ) // 16x16x4 tiles
+ ROM_LOAD16_WORD_SWAP( "60.rom3", 0x000000, 0x200000, CRC(a50a3c1b) SHA1(a2b30f9f83f5dc2e069d7559aefbda9929fc640c) )
+
+ ROM_REGION( 0x400000, "tiles_l1", 0 ) // 16x16x8 tiles
+ ROM_LOAD32_WORD_SWAP( "59.rom15", 0x000000, 0x200000, CRC(b83dcb70) SHA1(b0b9df451535d85612fa095b4f694cf2e7930bca) )
+ ROM_LOAD32_WORD_SWAP( "61.rom11", 0x000002, 0x200000, CRC(7f1213b9) SHA1(f8d6432b270c4d0954602e430ddd26841eb05656) )
+
+ ROM_REGION( 0x200000, "tiles_l2", 0 ) // 8x8x4 tiles
+ ROM_LOAD16_WORD_SWAP( "60.rom3", 0x000000, 0x200000, CRC(a50a3c1b) SHA1(a2b30f9f83f5dc2e069d7559aefbda9929fc640c) ) // ?maybe?
+
+ ROM_REGION( 0x040000, "oki", 0 ) // samples
+ ROM_LOAD( "n03.rom25", 0x000000, 0x040000, CRC(a7bfb5ea) SHA1(61937eae4f8855bc09c494aff52d76d41dc3b76a) )
+ROM_END
+
+} // anonymous namespace
+
+
+//-------------------------------------------------
+// game drivers
+//-------------------------------------------------
+
+GAME( 1995, gogomile, 0, fuuki16, gogomile, fuuki16_state, empty_init, ROT0, "Fuuki", "Susume! Mile Smile / Go Go! Mile Smile (newer)", MACHINE_SUPPORTS_SAVE )
+GAME( 1995, gogomileo, gogomile, fuuki16, gogomileo, fuuki16_state, empty_init, ROT0, "Fuuki", "Susume! Mile Smile / Go Go! Mile Smile (older)", MACHINE_SUPPORTS_SAVE )
+GAME( 1996, pbancho, 0, fuuki16, pbancho, fuuki16_state, empty_init, ROT0, "Fuuki", "Gyakuten!! Puzzle Bancho (Japan, set 1)", MACHINE_SUPPORTS_SAVE ) // program ROMs had extra black dot on labels
+GAME( 1996, pbanchoa, pbancho, fuuki16, pbancho, fuuki16_state, empty_init, ROT0, "Fuuki", "Gyakuten!! Puzzle Bancho (Japan, set 2)", MACHINE_SUPPORTS_SAVE )
diff --git a/src/mame/fuuki/fuukifg3.cpp b/src/mame/fuuki/fuukifg3.cpp
new file mode 100644
index 00000000000..3a9e68fe3fd
--- /dev/null
+++ b/src/mame/fuuki/fuukifg3.cpp
@@ -0,0 +1,873 @@
+// license:BSD-3-Clause
+// copyright-holders: Paul Priest, David Haywood, Luca Elia
+
+/***************************************************************************
+
+ -= Fuuki 32 Bit Games (FG-3) =-
+
+ driver by Paul Priest and David Haywood
+ based on fuukifg2 by Luca Elia
+
+Hardware is similar to FG-2 used for :
+"Go Go! Mile Smile", "Susume! Mile Smile (Japan)" & "Gyakuten!! Puzzle Bancho (Japan, set 1)"
+See fuukifg2.cpp
+
+Main CPU : M68020
+
+---------------------------------------------------------------------------
+Year + Game
+---------------------------------------------------------------------------
+98 Asura Blade - Sword of Dynasty (Japan)
+00 Asura Buster - Eternal Warriors (Japan)
+01 Asura Buster - Eternal Warriors (USA)
+
+English version of Asura Blade also exist, but it isn't dumped
+
+---------------------------------------------------------------------------
+
+--
+Notes so far:
+
+- Dips are correct for game play. Asura Buster's service mode does show the effects of
+ dipswitches and dipswitch state. For the "Round" listing, the 2 and 3 are shown in
+ reverse of actual game play. Any reference below to "Service Mode" means Asura
+ Buster's service mode.
+
+- Raster Effects are imperfect, bad frames when lots of new sprites.
+
+- The scroll values are generally wrong when flip screen is on and rasters are often incorrect
+
+- PCM channels of music in asurabus is sometimes off-tune, check Chen-Mao's stage for example
+ note: srom.u7 (z80 prg) is a good dump
+
+Asura Blade
+Fuuki Co. Ltd., 1998
+
+PCB Layout
+----------
+
+Top Board
+
+FG-3J MAIN-J Revision:1.1
+|-----------------------------------------------------|
+| YAC516 YMF278 N341256(x4) N341028 (x4) FI-002K |
+| 33.8688MHz N341512(x4) |
+| PAL N341256 |
+| Z80 N341256 |
+| N341256 |
+|J DSW1 |
+|A |
+|M 12MHz FI-003K N341256(x2) |
+|M DSW2 N341256(x3) |
+|A PAL |
+| 40MHz PAL |
+| DSW3 PAL N341256 |
+| 68020 PAL N341256 |
+| N341256 28.432MHz M60067-0901FP |
+| DSW4 N341256 PAL |
+|-----------------------------------------------------|
+
+Notes:
+ 68020 clock: 20.000MHz
+ Z80 clock: 6.000MHz
+ YM278 clock: 33.8688MHz
+ VSync: 60Hz
+ Hsync: 15.81kHz
+
+
+Bottom Board
+
+FG-3J ROM-J 507KA0301P04 Rev:1.3
+|--------------------------------|
+| SROM |
+| |
+| SP01* SP89 PCM |
+| |
+| SP23 SPAB |
+| |
+| SP45 SPCD MAP |
+| |
+| SP67 SPEF* PGM3 |
+| |
+| PGM2 |
+| |
+| BG2123 BG1113 PGM1 |
+| |
+| BG2022 BG1012 PGM0 |
+|--------------------------------|
+
+* = Not populated
+
+****************************************************************************
+
+Asura Buster
+Fuuki Co. Ltd.
+
+PCB Layout
+----------
+
+Top Board
+
+FG-3J MAIN-J Revision:1.1
+|-----------------------------------------------------|
+| YAC516 YMF278 N341256(x4) N341028 (x4) FI-002K |
+| 33.8688MHz N341512(x4) |
+| PAL N341256 |
+| Z80 N341256 |
+| N341256 |
+|J DSW1 |
+|A |
+|M 12MHz FI-003K N341256(x2) |
+|M DSW2 N341256(x3) |
+|A PAL |
+| 40MHz PAL |
+| DSW3 PAL N341256 |
+| 68020 PAL N341256 |
+| N341256 28.432MHz M60067-0901FP |
+| DSW4 N341256 PAL |
+|-----------------------------------------------------|
+
+Notes:
+ 68020 clock: 20.000MHz [40/2]
+ Z80 clock: 6.000MHz [12/2]
+ YM278 clock: 33.8688MHz
+ VSync: 60Hz
+ Hsync: 15.81kHz
+
+
+Bottom Board
+
+FG-3J ROM-J 507KA0301P04 Rev:1.3
+|--------------------------------|
+| SROM |
+| |
+| SP01 SP89 OPM |
+| |
+| SP23 SPAB |
+| |
+| SP45 SPCD MAP |
+| |
+| SP67 SPEF PGM3 |
+| |
+| PGM2 |
+| |
+| BG2123 BG1113 PGM1 |
+| |
+| BG2022 BG1012 PGM0 |
+|--------------------------------|
+
+
+There is an Asura Buster known to exist on a FG3-SUB-EP containing all EPROMs
+ with the following checksum values for the program ROMS:
+
+ PGM0 - BB1D, PGM1 - 6D84, PGM2 - EE6B & PGM3 - 7977 (which match the current Japan, set 1)
+ other game EPROMs dated, 10/30, 11/1 or 11/2
+
+***************************************************************************/
+
+#include "emu.h"
+
+#include "fuukispr.h"
+#include "fuukitmap.h"
+
+#include "cpu/m68000/m68020.h"
+#include "cpu/z80/z80.h"
+#include "sound/ymopl.h"
+
+#include "emupal.h"
+#include "screen.h"
+#include "speaker.h"
+#include "tilemap.h"
+
+
+namespace {
+
+class fuuki32_state : public driver_device
+{
+public:
+ fuuki32_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_gfxdecode(*this, "gfxdecode")
+ , m_screen(*this, "screen")
+ , m_palette(*this, "palette")
+ , m_fuukispr(*this, "fuukispr")
+ , m_fuukitmap(*this, "fuukitmap")
+ , m_spriteram(*this, "spriteram", 0x2000, ENDIANNESS_BIG)
+ , m_tilebank(*this, "tilebank")
+ , m_shared_ram(*this, "shared_ram")
+ , m_soundbank(*this, "soundbank")
+ , m_system(*this, "SYSTEM")
+ , m_inputs(*this, "INPUTS")
+ , m_dsw(*this, "DSW%u", 1U)
+ { }
+
+ void fuuki32(machine_config &config);
+
+protected:
+ virtual void machine_start() override ATTR_COLD;
+ virtual void video_start() override ATTR_COLD;
+
+private:
+ // devices
+ required_device<cpu_device> m_maincpu;
+ required_device<gfxdecode_device> m_gfxdecode;
+ required_device<screen_device> m_screen;
+ required_device<palette_device> m_palette;
+ required_device<fuukispr_device> m_fuukispr;
+ required_device<fuukitmap_device> m_fuukitmap;
+
+ // memory pointers
+ memory_share_creator<u16> m_spriteram;
+ required_shared_ptr<u32> m_tilebank;
+ required_shared_ptr<u8> m_shared_ram;
+ std::unique_ptr<u16[]> m_buf_spriteram[2];
+
+ required_memory_bank m_soundbank;
+
+ required_ioport m_system;
+ required_ioport m_inputs;
+ required_ioport_array<2> m_dsw;
+
+ // video-related
+ u32 m_spr_buffered_tilebank[2]{};
+
+ void main_map(address_map &map) ATTR_COLD;
+ void sound_io_map(address_map &map) ATTR_COLD;
+ void sound_map(address_map &map) ATTR_COLD;
+
+ u8 snd_020_r(offs_t offset);
+ void snd_020_w(offs_t offset, u8 data, u8 mem_mask = ~0);
+ void sprram_w(offs_t offset, u16 data, u16 mem_mask = ~0);
+ u16 sprram_r(offs_t offset);
+ void sound_bw_w(u8 data);
+
+ void spr_tile_cb(u32 &code);
+ void spr_colpri_cb(u32 &colour, u32 &pri_mask);
+ void tmap_colour_cb(u8 layer, u32 &colour);
+ u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void screen_vblank(int state);
+};
+
+
+/***************************************************************************
+
+ [ 4 Scrolling Layers ]
+
+ [ Layer 0 ] [ Layer 1 ] [ Layer 2 (double-buffered) ]
+
+ Tile Size: 16 x 16 x 8 16 x 16 x 8 8 x 8 x 4
+ Layer Size (tiles): 64 x 32 64 x 32 64 x 32
+
+ [ 1024? Zooming Sprites ]
+
+ Sprites are made of 16 x 16 x 4 tiles. Size can vary from 1 to 16
+ tiles both horizontally and vertically.
+ There is zooming (from full size to half size) and 4 levels of
+ priority (wrt layers)
+
+ Per-line raster effects used on many stages
+ Sprites buffered by two frames
+ Tilebank buffered by 3 frames? Only 2 in attract
+ Sprite pens needs to be buffered by 3 frames? Or lazy programming? Probably 2
+
+***************************************************************************/
+
+/***************************************************************************
+
+
+ Video Hardware Init
+
+
+***************************************************************************/
+
+void fuuki32_state::video_start()
+{
+ const u32 spriteram_size = m_spriteram.bytes();
+ m_buf_spriteram[0] = make_unique_clear<u16[]>(spriteram_size / 2);
+ m_buf_spriteram[1] = make_unique_clear<u16[]>(spriteram_size / 2);
+
+ m_fuukitmap->set_transparent_pen(0, 0xff); // 8 bits
+ m_fuukitmap->set_transparent_pen(1, 0xff); // 8 bits
+ m_fuukitmap->set_transparent_pen(2, 0x0f); // 4 bits
+
+ //m_fuukitmap->gfx(0)->set_granularity(16); // 256 colour tiles with palette selectable on 16 colour boundaries
+ //m_fuukitmap->gfx(1)->set_granularity(16);
+
+ save_pointer(NAME(m_buf_spriteram[0]), spriteram_size / 2);
+ save_pointer(NAME(m_buf_spriteram[1]), spriteram_size / 2);
+}
+
+
+void fuuki32_state::sprram_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_spriteram[offset]);
+};
+
+u16 fuuki32_state::sprram_r(offs_t offset)
+{
+ return m_spriteram[offset];
+}
+
+void fuuki32_state::spr_tile_cb(u32 &code)
+{
+ const u32 bank = (code & 0xc000) >> 14;
+
+ const u32 bank_lookedup = ((m_spr_buffered_tilebank[1] & 0xffff0000) >> (16 + bank * 4)) & 0xf;
+ code &= 0x3fff;
+ code += bank_lookedup * 0x4000;
+}
+
+void fuuki32_state::spr_colpri_cb(u32 &colour, u32 &pri_mask)
+{
+ const u8 priority = (colour >> 6) & 3;
+ switch (priority)
+ {
+ case 3: pri_mask = 0xf0 | 0xcc | 0xaa; break; // behind all layers
+ case 2: pri_mask = 0xf0 | 0xcc; break; // behind fg + middle layer
+ case 1: pri_mask = 0xf0; break; // behind fg layer
+ case 0:
+ default: pri_mask = 0; // above all
+ }
+ colour &= 0x3f;
+}
+
+void fuuki32_state::tmap_colour_cb(u8 layer, u32 &colour)
+{
+ if (layer < 2)
+ colour >>= 4;
+}
+
+u32 fuuki32_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ m_fuukitmap->prepare();
+ flip_screen_set(m_fuukitmap->flip_screen());
+
+ // The bg colour is the last pen i.e. 0x1fff
+ bitmap.fill((0x800 * 4) - 1, cliprect);
+ screen.priority().fill(0, cliprect);
+
+ m_fuukitmap->draw_layer(screen, bitmap, cliprect, m_fuukitmap->tmap_back(), 0, 1);
+ m_fuukitmap->draw_layer(screen, bitmap, cliprect, m_fuukitmap->tmap_middle(), 0, 2);
+ m_fuukitmap->draw_layer(screen, bitmap, cliprect, m_fuukitmap->tmap_front(), 0, 4);
+
+ m_fuukispr->draw_sprites(screen, bitmap, cliprect, flip_screen(), m_buf_spriteram[1].get(), m_spriteram.bytes() / 2);
+ return 0;
+}
+
+void fuuki32_state::screen_vblank(int state)
+{
+ // rising edge
+ if (state)
+ {
+ // Buffer sprites and tilebank by 2 frames
+ m_spr_buffered_tilebank[1] = m_spr_buffered_tilebank[0];
+ m_spr_buffered_tilebank[0] = m_tilebank[0];
+ memcpy(m_buf_spriteram[1].get(), m_buf_spriteram[0].get(), m_spriteram.bytes());
+ memcpy(m_buf_spriteram[0].get(), m_spriteram, m_spriteram.bytes());
+ }
+}
+
+
+//-------------------------------------------------
+// memory - main CPU
+//-------------------------------------------------
+
+// Sound comms
+u8 fuuki32_state::snd_020_r(offs_t offset)
+{
+ machine().scheduler().synchronize();
+ return m_shared_ram[offset];
+}
+
+void fuuki32_state::snd_020_w(offs_t offset, u8 data, u8 mem_mask)
+{
+ machine().scheduler().synchronize();
+ COMBINE_DATA(&m_shared_ram[offset]);
+}
+
+void fuuki32_state::main_map(address_map &map)
+{
+ map(0x000000, 0x1fffff).rom();
+ map(0x400000, 0x40ffff).ram(); // Work RAM
+ map(0x410000, 0x41ffff).ram(); // Work RAM (used by asurabus)
+
+ map(0x500000, 0x507fff).m(m_fuukitmap, FUNC(fuukitmap_device::vram_map));
+ map(0x508000, 0x517fff).ram(); // More tilemap, or linescroll? Seems to be empty all of the time
+ map(0x600000, 0x601fff).rw(FUNC(fuuki32_state::sprram_r), FUNC(fuuki32_state::sprram_w));
+ map(0x700000, 0x703fff).ram().w(m_palette, FUNC(palette_device::write32)).share("palette");
+
+ map(0x800000, 0x800003).lr16(NAME([this] () { return u16(m_system->read()); })).nopw(); // Coin
+ map(0x810000, 0x810003).lr16(NAME([this] () { return u16(m_inputs->read()); })).nopw(); // Player inputs
+ map(0x880000, 0x880003).lr16(NAME([this] () { return u16(m_dsw[0]->read()); })); // Service + DIPs
+ map(0x890000, 0x890003).lr16(NAME([this] () { return u16(m_dsw[1]->read()); })); // More DIPs
+
+ map(0x8c0000, 0x8effff).m(m_fuukitmap, FUNC(fuukitmap_device::vregs_map));
+ map(0x903fe0, 0x903fff).rw(FUNC(fuuki32_state::snd_020_r), FUNC(fuuki32_state::snd_020_w)).umask32(0x00ff00ff); // Shared with Z80
+ map(0xa00000, 0xa00003).writeonly().share(m_tilebank);
+}
+
+
+//-------------------------------------------------
+// memory - sound CPU
+//-------------------------------------------------
+
+void fuuki32_state::sound_bw_w(u8 data)
+{
+ m_soundbank->set_entry(data);
+}
+
+void fuuki32_state::sound_map(address_map &map)
+{
+ map(0x0000, 0x5fff).rom();
+ map(0x6000, 0x6fff).ram();
+ map(0x7ff0, 0x7fff).ram().share(m_shared_ram);
+ map(0x8000, 0xffff).bankr(m_soundbank);
+}
+
+void fuuki32_state::sound_io_map(address_map &map)
+{
+ map.global_mask(0xff);
+ map(0x00, 0x00).w(FUNC(fuuki32_state::sound_bw_w));
+ map(0x30, 0x30).nopw(); // leftover/unused nmi handler related
+ map(0x40, 0x45).rw("ymf", FUNC(ymf278b_device::read), FUNC(ymf278b_device::write));
+}
+
+
+//-------------------------------------------------
+// input ports
+//-------------------------------------------------
+
+static INPUT_PORTS_START( asurabld )
+ PORT_START("SYSTEM")
+ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 )
+ PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 )
+ PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_START1 )
+ PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_START2 )
+ PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_SERVICE1 )
+ PORT_BIT( 0xfe00, IP_ACTIVE_LOW, IPT_UNKNOWN )
+
+ PORT_START("INPUTS")
+ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
+ PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
+ PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
+ PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
+ PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
+ PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
+ PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
+ PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
+ PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
+ PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
+ PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
+ PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
+ PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
+ PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
+ PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNKNOWN )
+
+ PORT_START("DSW1")
+ PORT_SERVICE_DIPLOC( 0x0001, IP_ACTIVE_LOW, "SW1:1" )
+ PORT_DIPNAME( 0x0002, 0x0002, "Blood Color" ) PORT_DIPLOCATION("SW1:2") // Any other censorship? (Tested in 3 locations)
+ PORT_DIPSETTING( 0x0002, "Red" )
+ PORT_DIPSETTING( 0x0000, "Green" )
+ PORT_DIPNAME( 0x000c, 0x000c, "Demo Sounds & Music" ) PORT_DIPLOCATION("SW1:3,4") // Tested @ 0917AC
+ PORT_DIPSETTING( 0x000c, "Both On" )
+ PORT_DIPSETTING( 0x0008, "Music Off" )
+ PORT_DIPSETTING( 0x0004, "Both Off" )
+ PORT_DIPSETTING( 0x0000, "Both Off" ) // Duplicate setting
+ PORT_DIPNAME( 0x0030, 0x0030, "Timer" ) PORT_DIPLOCATION("SW1:5,6")
+ PORT_DIPSETTING( 0x0000, "Slow" )
+ PORT_DIPSETTING( 0x0030, DEF_STR( Medium ) )
+ PORT_DIPSETTING( 0x0010, "Fast" )
+ PORT_DIPSETTING( 0x0020, "Very Fast" )
+ PORT_DIPNAME( 0x00c0, 0x0000, "Coinage Mode" ) PORT_DIPLOCATION("SW1:7,8")
+ PORT_DIPSETTING( 0x00c0, "Split" )
+ PORT_DIPSETTING( 0x0000, "Joint" )
+ PORT_DIPUNUSED_DIPLOC( 0x0100, 0x0100, "SW2:1" ) // DSW2 bank, not used for either game
+ PORT_DIPUNUSED_DIPLOC( 0x0200, 0x0200, "SW2:2" )
+ PORT_DIPUNUSED_DIPLOC( 0x0400, 0x0400, "SW2:3" )
+ PORT_DIPUNUSED_DIPLOC( 0x0800, 0x0800, "SW2:4" )
+ PORT_DIPUNUSED_DIPLOC( 0x1000, 0x1000, "SW2:5" )
+ PORT_DIPUNUSED_DIPLOC( 0x2000, 0x2000, "SW2:6" )
+ PORT_DIPUNUSED_DIPLOC( 0x4000, 0x4000, "SW2:7" )
+ PORT_DIPUNUSED_DIPLOC( 0x8000, 0x8000, "SW2:8" )
+
+ PORT_START("DSW2")
+ PORT_DIPNAME( 0x0001, 0x0001, DEF_STR( Flip_Screen ) ) PORT_DIPLOCATION("SW3:1")
+ PORT_DIPSETTING( 0x0001, DEF_STR( Off ) )
+ PORT_DIPSETTING( 0x0000, DEF_STR( On ) )
+ PORT_DIPNAME( 0x000e, 0x000e, DEF_STR( Difficulty ) ) PORT_DIPLOCATION("SW3:2,3,4") // AKA Computer Level, see @ 0917CC
+ PORT_DIPSETTING( 0x0000, DEF_STR( Easiest ) ) // Level 1
+ PORT_DIPSETTING( 0x0008, DEF_STR( Very_Easy ) ) // Level 2
+ PORT_DIPSETTING( 0x0004, DEF_STR( Easier ) ) // Level 3
+ PORT_DIPSETTING( 0x000c, DEF_STR( Easy ) ) // Level 4
+ PORT_DIPSETTING( 0x000e, DEF_STR( Normal ) ) // Level 5
+ PORT_DIPSETTING( 0x0002, DEF_STR( Hard ) ) // Level 6
+ PORT_DIPSETTING( 0x000a, DEF_STR( Very_Hard ) ) // Level 7
+ PORT_DIPSETTING( 0x0006, DEF_STR( Hardest ) ) // Level 8
+ PORT_DIPNAME( 0x0030, 0x0030, "Damage" ) PORT_DIPLOCATION("SW3:5,6")
+ PORT_DIPSETTING( 0x0020, "75%" )
+ PORT_DIPSETTING( 0x0030, "100%" )
+ PORT_DIPSETTING( 0x0010, "125%" )
+ PORT_DIPSETTING( 0x0000, "150%" )
+ PORT_DIPNAME( 0x00c0, 0x00c0, "Max Rounds" ) PORT_DIPLOCATION("SW3:7,8") // Service Mode shows rounds needed to win the match
+ PORT_DIPSETTING( 0x0000, "1" ) // Service Mode Shows 1
+ PORT_DIPSETTING( 0x00c0, "3" ) // Service Mode Shows 3, Service Mode has 2 & 3 reversed compared to game play
+ PORT_DIPSETTING( 0x0080, "5" ) // Service Mode Shows 2, Service Mode has 2 & 3 reversed compared to game play
+ PORT_DIPSETTING( 0x0040, "Error!!" ) // Service Mode Shows "Error"
+ PORT_DIPNAME( 0xf000, 0xf000, DEF_STR( Coin_A ) ) PORT_DIPLOCATION("SW4:1,2,3,4") // Service Mode Shows Player 2
+ PORT_DIPSETTING( 0x8000, DEF_STR( 8C_1C ) )
+ PORT_DIPSETTING( 0x9000, DEF_STR( 7C_1C ) )
+ PORT_DIPSETTING( 0xa000, DEF_STR( 6C_1C ) )
+ PORT_DIPSETTING( 0xb000, DEF_STR( 5C_1C ) )
+ PORT_DIPSETTING( 0xc000, DEF_STR( 4C_1C ) )
+ PORT_DIPSETTING( 0xd000, DEF_STR( 3C_1C ) )
+ PORT_DIPSETTING( 0xe000, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0x1000, DEF_STR( 2C_1C ) ) // Duplicate 2C_1C
+ PORT_DIPSETTING( 0xf000, DEF_STR( 1C_1C ) )
+ PORT_DIPSETTING( 0x6000, DEF_STR( 1C_2C ) )
+ PORT_DIPSETTING( 0x5000, DEF_STR( 1C_3C ) )
+ PORT_DIPSETTING( 0x4000, DEF_STR( 1C_4C ) )
+ PORT_DIPSETTING( 0x3000, DEF_STR( 1C_5C ) )
+ PORT_DIPSETTING( 0x2000, "2C Start / 1C Continue" )
+ PORT_DIPSETTING( 0x7000, "Error!!" ) // Causes graphics issues - Service Mode shows "Error"
+ PORT_DIPSETTING( 0x0000, DEF_STR( 1C_1C ) ) PORT_CONDITION("DSW2", 0x0f00, NOTEQUALS, 0x0000)
+ PORT_DIPSETTING( 0x0000, DEF_STR( Free_Play ) ) PORT_CONDITION("DSW2", 0x0f00, EQUALS, 0x0000) // Set both for Free Play
+ PORT_DIPNAME( 0x0f00, 0x0f00, DEF_STR( Coin_B ) ) PORT_DIPLOCATION("SW4:5,6,7,8") // Service Mode Shows Player 1
+ PORT_DIPSETTING( 0x0800, DEF_STR( 8C_1C ) )
+ PORT_DIPSETTING( 0x0900, DEF_STR( 7C_1C ) )
+ PORT_DIPSETTING( 0x0a00, DEF_STR( 6C_1C ) )
+ PORT_DIPSETTING( 0x0b00, DEF_STR( 5C_1C ) )
+ PORT_DIPSETTING( 0x0c00, DEF_STR( 4C_1C ) )
+ PORT_DIPSETTING( 0x0d00, DEF_STR( 3C_1C ) )
+ PORT_DIPSETTING( 0x0e00, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0x0100, DEF_STR( 2C_1C ) ) // Duplicate 2C_1C
+ PORT_DIPSETTING( 0x0f00, DEF_STR( 1C_1C ) )
+ PORT_DIPSETTING( 0x0600, DEF_STR( 1C_2C ) )
+ PORT_DIPSETTING( 0x0500, DEF_STR( 1C_3C ) )
+ PORT_DIPSETTING( 0x0400, DEF_STR( 1C_4C ) )
+ PORT_DIPSETTING( 0x0300, DEF_STR( 1C_5C ) )
+ PORT_DIPSETTING( 0x0200, "2C Start / 1C Continue" )
+ PORT_DIPSETTING( 0x0700, "Error!!" ) // Causes graphics issues - Service Mode shows "Error"
+ PORT_DIPSETTING( 0x0000, DEF_STR( 1C_1C ) ) PORT_CONDITION("DSW2", 0xf000, NOTEQUALS, 0x0000)
+ PORT_DIPSETTING( 0x0000, DEF_STR( Free_Play ) ) PORT_CONDITION("DSW2", 0xf000, EQUALS, 0x0000) // Set both for Free Play
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( asurabus )
+ PORT_INCLUDE(asurabld)
+
+ PORT_MODIFY("DSW1")
+ PORT_DIPNAME( 0x000c, 0x000c, "Demo Sounds & Music" ) PORT_DIPLOCATION("SW1:3,4")
+ PORT_DIPSETTING( 0x000c, "Both On" )
+ PORT_DIPSETTING( 0x0008, "Sounds Off" )
+ PORT_DIPSETTING( 0x0004, "Music Off" )
+ PORT_DIPSETTING( 0x0000, "Both Off" )
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( asurabusa )
+ PORT_INCLUDE(asurabld)
+
+ PORT_MODIFY("INPUTS")
+ PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1)
+ PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2)
+INPUT_PORTS_END
+
+//-------------------------------------------------
+// graphics layouts
+//-------------------------------------------------
+
+// 16x16x8
+static const gfx_layout layout_16x16x8 =
+{
+ 16,16,
+ RGN_FRAC(1,1),
+ 8,
+ { STEP4(0,1), STEP4(16,1) },
+ { STEP4(0,4), STEP4(16*2,4), STEP4(16*4,4), STEP4(16*6,4) },
+ { STEP16(0,16*8) },
+ 16*16*8
+};
+
+static GFXDECODE_START( gfx_fuuki32 )
+ GFXDECODE_ENTRY( "tiles_l0", 0, layout_16x16x8, 0x400*0, 0x40 ) // [0] Layer 1
+ GFXDECODE_ENTRY( "tiles_l1", 0, layout_16x16x8, 0x400*1, 0x40 ) // [1] Layer 2
+ GFXDECODE_ENTRY( "tiles_bg", 0, gfx_8x8x4_packed_msb, 0x400*3, 0x40 ) // [2] BG Layer
+GFXDECODE_END
+
+
+//-------------------------------------------------
+// driver functions
+//-------------------------------------------------
+
+void fuuki32_state::machine_start()
+{
+ u8 *rom = memregion("soundcpu")->base();
+
+ m_soundbank->configure_entries(0, 0x10, &rom[0], 0x8000);
+
+ save_item(NAME(m_spr_buffered_tilebank));
+}
+
+
+void fuuki32_state::fuuki32(machine_config &config)
+{
+ // basic machine hardware
+ M68EC020(config, m_maincpu, 40_MHz_XTAL / 2); // 20MHz verified
+ m_maincpu->set_addrmap(AS_PROGRAM, &fuuki32_state::main_map);
+
+ z80_device &soundcpu(Z80(config, "soundcpu", 12_MHz_XTAL / 2)); // 6MHz verified
+ soundcpu.set_addrmap(AS_PROGRAM, &fuuki32_state::sound_map);
+ soundcpu.set_addrmap(AS_IO, &fuuki32_state::sound_io_map);
+
+ // video hardware
+ SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
+ m_screen->set_refresh_hz(60);
+ m_screen->set_size(64 * 8, 32 * 8);
+ m_screen->set_visarea(0, 40 * 8 - 1, 0, 30 * 8 - 1);
+ m_screen->set_screen_update(FUNC(fuuki32_state::screen_update));
+ m_screen->screen_vblank().set(FUNC(fuuki32_state::screen_vblank));
+ m_screen->set_palette(m_palette);
+
+ GFXDECODE(config, m_gfxdecode, m_palette, gfx_fuuki32);
+ PALETTE(config, m_palette, palette_device::BLACK).set_format(palette_device::xRGB_555, 0x4000 / 2);
+
+ FUUKI_SPRITE(config, m_fuukispr, 0);
+ m_fuukispr->set_palette(m_palette);
+ m_fuukispr->set_color_base(0x400*2);
+ m_fuukispr->set_color_num(0x40);
+ m_fuukispr->set_tile_callback(FUNC(fuuki32_state::spr_tile_cb));
+ m_fuukispr->set_colpri_callback(FUNC(fuuki32_state::spr_colpri_cb));
+
+ FUUKI_TILEMAP(config, m_fuukitmap, 0, m_palette, gfx_fuuki32);
+ m_fuukitmap->set_screen(m_screen);
+ m_fuukitmap->set_colour_callback(FUNC(fuuki32_state::tmap_colour_cb));
+ m_fuukitmap->level_1_irq_callback().set_inputline(m_maincpu, 1, HOLD_LINE);
+ m_fuukitmap->vblank_irq_callback().set_inputline(m_maincpu, 3, HOLD_LINE);
+ m_fuukitmap->raster_irq_callback().set_inputline(m_maincpu, 5, HOLD_LINE);
+ m_fuukitmap->set_xoffs(0x1f3, 0x103);
+ m_fuukitmap->set_yoffs(0x3f6, 0x2c7);
+
+ // sound hardware
+ SPEAKER(config, "speaker", 2).front();
+
+ ymf278b_device &ymf(YMF278B(config, "ymf", 33.8688_MHz_XTAL));
+ ymf.irq_handler().set_inputline("soundcpu", 0);
+ ymf.add_route(0, "speaker", 0.50, 0);
+ ymf.add_route(1, "speaker", 0.50, 1);
+ ymf.add_route(2, "speaker", 0.40, 0);
+ ymf.add_route(3, "speaker", 0.40, 1);
+ ymf.add_route(4, "speaker", 0.50, 0);
+ ymf.add_route(5, "speaker", 0.50, 1);
+}
+
+//-------------------------------------------------
+// ROM loading
+//-------------------------------------------------
+
+/***************************************************************************
+
+ Asura Blade - Sword of Dynasty (Japan)
+
+Fuuki, 1999 Consists of a FG-3J MAIN-J mainboard & FG-3J ROM-J combo
+
+***************************************************************************/
+
+ROM_START( asurabld )
+ ROM_REGION( 0x200000, "maincpu", 0 ) // M68020
+ ROM_LOAD32_BYTE( "pgm3.u1", 0x000000, 0x80000, CRC(053e9758) SHA1(c2754d3f0c607c81c8fa33b667b576eb0474fd0b) )
+ ROM_LOAD32_BYTE( "pgm2.u2", 0x000001, 0x80000, CRC(16b656ca) SHA1(5ffb551ce7dec462d3896f0fed693454496894bc) )
+ ROM_LOAD32_BYTE( "pgm1.u3", 0x000002, 0x80000, CRC(35104452) SHA1(03cfd81429f8a945d5419c9750925bfa997d0607) )
+ ROM_LOAD32_BYTE( "pgm0.u4", 0x000003, 0x80000, CRC(68615497) SHA1(de93751f151f195a863dc6fe83b6e7ed8f99430a) )
+
+ ROM_REGION( 0x80000, "soundcpu", 0 ) // Z80
+ ROM_LOAD( "srom.u7", 0x00000, 0x80000, CRC(bb1deb89) SHA1(b1c70abddc0b9a88beb69a592376ff69a7e091eb) )
+
+ ROM_REGION( 0x2000000, "fuukispr", 0 )
+ // 0x0000000 - 0x03fffff empty */ /* spXX.uYY - XX is the bank number!
+ ROM_LOAD16_WORD_SWAP( "sp23.u14", 0x0400000, 0x400000, CRC(7df492eb) SHA1(30b88a3cd025ffc8c28fef06e0784755be37ef8e) )
+ ROM_LOAD16_WORD_SWAP( "sp45.u15", 0x0800000, 0x400000, CRC(1890f42a) SHA1(22254fe38fd83f4602a25e1ccba32df16edaf3f9) )
+ ROM_LOAD16_WORD_SWAP( "sp67.u16", 0x0c00000, 0x400000, CRC(a48f1ef0) SHA1(bf8787f293793291a503af662d3738c007654726) )
+ ROM_LOAD16_WORD_SWAP( "sp89.u17", 0x1000000, 0x400000, CRC(6b024362) SHA1(8be5cc3c7306d28b75acd970bb3be6d3c9825367) )
+ ROM_LOAD16_WORD_SWAP( "spab.u18", 0x1400000, 0x400000, CRC(803d2d8c) SHA1(25df30689e576a0620656c721d92bcc3fbd84844) )
+ ROM_LOAD16_WORD_SWAP( "spcd.u19", 0x1800000, 0x400000, CRC(42e5c26e) SHA1(b68875d353bdc5d49113bbac02fd83508bce66a5) )
+
+ ROM_REGION( 0x0800000, "tiles_l0", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg1012.u22", 0x0000002, 0x400000, CRC(d717a0a1) SHA1(007df309dc0650ca07e077b983a2b05730349d0b) )
+ ROM_LOAD32_WORD_SWAP( "bg1113.u23", 0x0000000, 0x400000, CRC(94338267) SHA1(7848bc57cb0eac216100a508763451eb57a0a082) )
+
+ ROM_REGION( 0x0800000, "tiles_l1", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg2022.u25", 0x0000002, 0x400000, CRC(ee312cd3) SHA1(2ef9d51928d80375daf8e6b204bb66a8b9cbaee7) )
+ ROM_LOAD32_WORD_SWAP( "bg2123.u24", 0x0000000, 0x400000, CRC(4acfc469) SHA1(a98d06b967ebb3fa3b4c8aa3d7a05063ec981fb2) )
+
+ ROM_REGION( 0x200000, "tiles_bg", 0 )
+ ROM_LOAD16_WORD_SWAP( "map.u5", 0x00000, 0x200000, CRC(e681155e) SHA1(458845b9c86df72685d92d0d4052aacc2fa7d1bd) )
+
+ ROM_REGION( 0x400000, "ymf", 0 ) // OPL4 samples
+ ROM_LOAD( "pcm.u6", 0x00000, 0x400000, CRC(ac72225a) SHA1(8d16399ed34ac5bd69dbf43b2de2b0db9ac1c610) )
+ROM_END
+
+/***************************************************************************
+
+ Asura Buster - Eternal Warriors (Japan)
+
+Fuuki, 2000 Consists of a FG-3J MAIN-J mainboard & FG-3J ROM-J combo
+
+***************************************************************************/
+
+ROM_START( asurabus )
+ ROM_REGION( 0x200000, "maincpu", 0 ) // M68020
+ ROM_LOAD32_BYTE( "uspgm3.u1", 0x000000, 0x80000, CRC(e152cec9) SHA1(af1d93bdabc6732c0ff53972826d67a3753ef785) ) // hand written labels
+ ROM_LOAD32_BYTE( "uspgm2.u2", 0x000001, 0x80000, CRC(b19787db) SHA1(3d6757f38f297c1ee89003173567319b5dae8000) )
+ ROM_LOAD32_BYTE( "uspgm1.u3", 0x000002, 0x80000, CRC(6588e51a) SHA1(9ab978c80d8ece447697557c8000be95760306f3) )
+ ROM_LOAD32_BYTE( "uspgm0.u4", 0x000003, 0x80000, CRC(981e6ff1) SHA1(088d26a3cbd2361ffc756c3da8a67b94ae7bbd65) )
+
+ ROM_REGION( 0x80000, "soundcpu", 0 ) // Z80
+ ROM_LOAD( "srom.u7", 0x00000, 0x80000, CRC(368da389) SHA1(1423b709da40bf3033c9032c4bd07658f1a969de) )
+
+ ROM_REGION( 0x2000000, "fuukispr", 0 )
+ ROM_LOAD16_WORD_SWAP( "sp01.u13", 0x0000000, 0x400000, CRC(5edea463) SHA1(22a780912f060bae0c9a403a7bfd4d27f25b76e3) )
+ ROM_LOAD16_WORD_SWAP( "sp23.u14", 0x0400000, 0x400000, CRC(91b1b0de) SHA1(341367966559ef2027415b673eb0db704680c81f) )
+ ROM_LOAD16_WORD_SWAP( "sp45.u15", 0x0800000, 0x400000, CRC(96c69aac) SHA1(cf053523026651427f884b9dd7c095af362dd24e) )
+ ROM_LOAD16_WORD_SWAP( "sp67.u16", 0x0c00000, 0x400000, CRC(7c3d83bf) SHA1(7188dd923c6c7eb6aee3323e7ab54aa240c35ea3) )
+ ROM_LOAD16_WORD_SWAP( "sp89.u17", 0x1000000, 0x400000, CRC(cb1e14f8) SHA1(941cea1887d7ceb52222adcf1d6913969e6163aa) )
+ ROM_LOAD16_WORD_SWAP( "spab.u18", 0x1400000, 0x400000, CRC(e5a4608d) SHA1(b8e39f53e0b7ad1e16ae9c3726597776b404be1c) )
+ ROM_LOAD16_WORD_SWAP( "spcd.u19", 0x1800000, 0x400000, CRC(99bfbe32) SHA1(926a8afc4a175874f22f53300e76f59331d3b9ba) )
+ ROM_LOAD16_WORD_SWAP( "spef.u20", 0x1c00000, 0x400000, CRC(c9c799cc) SHA1(01373316700d8688deeea2e9e8f831d5f86c7f17) )
+
+ ROM_REGION( 0x0800000, "tiles_l0", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg1012.u22", 0x0000002, 0x400000, CRC(e3fb9af0) SHA1(11900cc2873337692f66fb4f1eb9c574e5a967de) )
+ ROM_LOAD32_WORD_SWAP( "bg1113.u23", 0x0000000, 0x400000, CRC(5f8657e6) SHA1(7c2854dc5d2d4efe55bda01e329da051350e0031) )
+
+ ROM_REGION( 0x0800000, "tiles_l1", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg2022.u25", 0x0000002, 0x400000, CRC(f46eda52) SHA1(46530016b32a164bd76c4f53e7b53b2beb28db06) )
+ ROM_LOAD32_WORD_SWAP( "bg2123.u24", 0x0000000, 0x400000, CRC(c4ebb86b) SHA1(a7093e6e02b64566d277cbbd5fa90cd430e7c8a0) )
+
+ ROM_REGION( 0x200000, "tiles_bg", 0 )
+ ROM_LOAD16_WORD_SWAP( "map.u5", 0x00000, 0x200000, CRC(bd179dc5) SHA1(ce3fcac573b14fd5365eb5dcec3257e439d2c129) )
+
+ ROM_REGION( 0x400000, "ymf", 0 ) // OPL4 samples
+ ROM_LOAD( "opm.u6", 0x00000, 0x400000, CRC(31b05be4) SHA1(d0f4f387f84a74591224b0f42b7f5c538a3dc498) )
+ROM_END
+
+ROM_START( asurabusj )
+ ROM_REGION( 0x200000, "maincpu", 0 ) // M68020
+ ROM_LOAD32_BYTE( "pgm3.u1", 0x000000, 0x80000, CRC(2c6b5271) SHA1(188371f1f003823ac719e962e048719d76696b2f) )
+ ROM_LOAD32_BYTE( "pgm2.u2", 0x000001, 0x80000, CRC(8f8694ec) SHA1(3334df4aecc5ab2f8914ef6748c027a99b39ce26) )
+ ROM_LOAD32_BYTE( "pgm1.u3", 0x000002, 0x80000, CRC(0a040f0f) SHA1(d5e86d33efcbbde7ee62cfc8dfe867f250a33415) )
+ ROM_LOAD32_BYTE( "pgm0.u4", 0x000003, 0x80000, CRC(9b71e9d8) SHA1(9b705b5b6fff549f5679890422b481b5cf1d7bd7) )
+
+ ROM_REGION( 0x80000, "soundcpu", 0 ) // Z80
+ ROM_LOAD( "srom.u7", 0x00000, 0x80000, CRC(368da389) SHA1(1423b709da40bf3033c9032c4bd07658f1a969de) )
+
+ ROM_REGION( 0x2000000, "fuukispr", 0 )
+ ROM_LOAD16_WORD_SWAP( "sp01.u13", 0x0000000, 0x400000, CRC(5edea463) SHA1(22a780912f060bae0c9a403a7bfd4d27f25b76e3) )
+ ROM_LOAD16_WORD_SWAP( "sp23.u14", 0x0400000, 0x400000, CRC(91b1b0de) SHA1(341367966559ef2027415b673eb0db704680c81f) )
+ ROM_LOAD16_WORD_SWAP( "sp45.u15", 0x0800000, 0x400000, CRC(96c69aac) SHA1(cf053523026651427f884b9dd7c095af362dd24e) )
+ ROM_LOAD16_WORD_SWAP( "sp67.u16", 0x0c00000, 0x400000, CRC(7c3d83bf) SHA1(7188dd923c6c7eb6aee3323e7ab54aa240c35ea3) )
+ ROM_LOAD16_WORD_SWAP( "sp89.u17", 0x1000000, 0x400000, CRC(cb1e14f8) SHA1(941cea1887d7ceb52222adcf1d6913969e6163aa) )
+ ROM_LOAD16_WORD_SWAP( "spab.u18", 0x1400000, 0x400000, CRC(e5a4608d) SHA1(b8e39f53e0b7ad1e16ae9c3726597776b404be1c) )
+ ROM_LOAD16_WORD_SWAP( "spcd.u19", 0x1800000, 0x400000, CRC(99bfbe32) SHA1(926a8afc4a175874f22f53300e76f59331d3b9ba) )
+ ROM_LOAD16_WORD_SWAP( "spef.u20", 0x1c00000, 0x400000, CRC(c9c799cc) SHA1(01373316700d8688deeea2e9e8f831d5f86c7f17) )
+
+ ROM_REGION( 0x0800000, "tiles_l0", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg1012.u22", 0x0000002, 0x400000, CRC(e3fb9af0) SHA1(11900cc2873337692f66fb4f1eb9c574e5a967de) )
+ ROM_LOAD32_WORD_SWAP( "bg1113.u23", 0x0000000, 0x400000, CRC(5f8657e6) SHA1(7c2854dc5d2d4efe55bda01e329da051350e0031) )
+
+ ROM_REGION( 0x0800000, "tiles_l1", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg2022.u25", 0x0000002, 0x400000, CRC(f46eda52) SHA1(46530016b32a164bd76c4f53e7b53b2beb28db06) )
+ ROM_LOAD32_WORD_SWAP( "bg2123.u24", 0x0000000, 0x400000, CRC(c4ebb86b) SHA1(a7093e6e02b64566d277cbbd5fa90cd430e7c8a0) )
+
+ ROM_REGION( 0x200000, "tiles_bg", 0 )
+ ROM_LOAD16_WORD_SWAP( "map.u5", 0x00000, 0x200000, CRC(bd179dc5) SHA1(ce3fcac573b14fd5365eb5dcec3257e439d2c129) )
+
+ ROM_REGION( 0x400000, "ymf", 0 ) // OPL4 samples
+ ROM_LOAD( "opm.u6", 0x00000, 0x400000, CRC(31b05be4) SHA1(d0f4f387f84a74591224b0f42b7f5c538a3dc498) )
+ROM_END
+
+ROM_START( asurabusja )
+ ROM_REGION( 0x200000, "maincpu", 0 ) // M68020
+ ROM_LOAD32_BYTE( "pgm3_583a.u1", 0x000000, 0x80000, CRC(46ab3b0e) SHA1(2d6a57352891a484fe11cda9addbff5b3940c17c) ) // hand written labels with checksums
+ ROM_LOAD32_BYTE( "pgm2_0ff4.u2", 0x000001, 0x80000, CRC(fa7aa289) SHA1(6f82371274c45f889a19a4fdd859015fb6ea249a) )
+ ROM_LOAD32_BYTE( "pgm1_bac7.u3", 0x000002, 0x80000, CRC(67364e19) SHA1(959b896b201f103ef9189b537139c89bfc7144ea) )
+ ROM_LOAD32_BYTE( "pgm0_193a.u4", 0x000003, 0x80000, CRC(94d39c64) SHA1(95ca2aa3e19e64bed7add3170653fa3364530fde) )
+
+ ROM_REGION( 0x80000, "soundcpu", 0 ) // Z80
+ ROM_LOAD( "srom.u7", 0x00000, 0x80000, CRC(368da389) SHA1(1423b709da40bf3033c9032c4bd07658f1a969de) )
+
+ ROM_REGION( 0x2000000, "fuukispr", 0 )
+ ROM_LOAD16_WORD_SWAP( "sp01.u13", 0x0000000, 0x400000, CRC(5edea463) SHA1(22a780912f060bae0c9a403a7bfd4d27f25b76e3) )
+ ROM_LOAD16_WORD_SWAP( "sp23.u14", 0x0400000, 0x400000, CRC(91b1b0de) SHA1(341367966559ef2027415b673eb0db704680c81f) )
+ ROM_LOAD16_WORD_SWAP( "sp45.u15", 0x0800000, 0x400000, CRC(96c69aac) SHA1(cf053523026651427f884b9dd7c095af362dd24e) )
+ ROM_LOAD16_WORD_SWAP( "sp67.u16", 0x0c00000, 0x400000, CRC(7c3d83bf) SHA1(7188dd923c6c7eb6aee3323e7ab54aa240c35ea3) )
+ ROM_LOAD16_WORD_SWAP( "sp89.u17", 0x1000000, 0x400000, CRC(cb1e14f8) SHA1(941cea1887d7ceb52222adcf1d6913969e6163aa) )
+ ROM_LOAD16_WORD_SWAP( "spab.u18", 0x1400000, 0x400000, CRC(e5a4608d) SHA1(b8e39f53e0b7ad1e16ae9c3726597776b404be1c) )
+ ROM_LOAD16_WORD_SWAP( "spcd.u19", 0x1800000, 0x400000, CRC(99bfbe32) SHA1(926a8afc4a175874f22f53300e76f59331d3b9ba) )
+ ROM_LOAD16_WORD_SWAP( "spef.u20", 0x1c00000, 0x400000, CRC(c9c799cc) SHA1(01373316700d8688deeea2e9e8f831d5f86c7f17) )
+
+ ROM_REGION( 0x0800000, "tiles_l0", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg1012.u22", 0x0000002, 0x400000, CRC(e3fb9af0) SHA1(11900cc2873337692f66fb4f1eb9c574e5a967de) )
+ ROM_LOAD32_WORD_SWAP( "bg1113.u23", 0x0000000, 0x400000, CRC(5f8657e6) SHA1(7c2854dc5d2d4efe55bda01e329da051350e0031) )
+
+ ROM_REGION( 0x0800000, "tiles_l1", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg2022.u25", 0x0000002, 0x400000, CRC(f46eda52) SHA1(46530016b32a164bd76c4f53e7b53b2beb28db06) )
+ ROM_LOAD32_WORD_SWAP( "bg2123.u24", 0x0000000, 0x400000, CRC(c4ebb86b) SHA1(a7093e6e02b64566d277cbbd5fa90cd430e7c8a0) )
+
+ ROM_REGION( 0x200000, "tiles_bg", 0 )
+ ROM_LOAD16_WORD_SWAP( "map.u5", 0x00000, 0x200000, CRC(bd179dc5) SHA1(ce3fcac573b14fd5365eb5dcec3257e439d2c129) )
+
+ ROM_REGION( 0x400000, "ymf", 0 ) // OPL4 samples
+ ROM_LOAD( "opm.u6", 0x00000, 0x400000, CRC(31b05be4) SHA1(d0f4f387f84a74591224b0f42b7f5c538a3dc498) )
+ROM_END
+
+ROM_START( asurabusjr ) // ARCADIA review build
+ ROM_REGION( 0x200000, "maincpu", 0 ) // M68020
+ ROM_LOAD32_BYTE( "24-31.pgm3", 0x000000, 0x80000, CRC(cfcb9c75) SHA1(51e325d5e60d5bb058429f04a5170dcc17986b7d) )
+ ROM_LOAD32_BYTE( "16-23.pgm2", 0x000001, 0x80000, CRC(e4d07738) SHA1(c6c949c5b0cbc129917bb8c93707539adabbd336) )
+ ROM_LOAD32_BYTE( "8-15.pgm1", 0x000002, 0x80000, CRC(1dd67fe7) SHA1(3fd340ccd4a306783ba0ccd3343ae505c9de3a73) )
+ ROM_LOAD32_BYTE( "0-7.pgm0", 0x000003, 0x80000, CRC(3af08de3) SHA1(1ecc69804693cab6c2c36120acfc6ced094a16e4) )
+
+ ROM_REGION( 0x80000, "soundcpu", 0 ) // Z80
+ ROM_LOAD( "srom.u7", 0x00000, 0x80000, CRC(368da389) SHA1(1423b709da40bf3033c9032c4bd07658f1a969de) )
+
+ ROM_REGION( 0x2000000, "fuukispr", 0 )
+ ROM_LOAD16_WORD_SWAP( "sp01.u13", 0x0000000, 0x400000, CRC(5edea463) SHA1(22a780912f060bae0c9a403a7bfd4d27f25b76e3) )
+ ROM_LOAD16_WORD_SWAP( "sp23.u14", 0x0400000, 0x400000, CRC(91b1b0de) SHA1(341367966559ef2027415b673eb0db704680c81f) )
+ ROM_LOAD16_WORD_SWAP( "sp45.u15", 0x0800000, 0x400000, CRC(96c69aac) SHA1(cf053523026651427f884b9dd7c095af362dd24e) )
+ ROM_LOAD16_WORD_SWAP( "sp67.u16", 0x0c00000, 0x400000, CRC(7c3d83bf) SHA1(7188dd923c6c7eb6aee3323e7ab54aa240c35ea3) )
+ ROM_LOAD16_WORD_SWAP( "sp89.u17", 0x1000000, 0x400000, CRC(cb1e14f8) SHA1(941cea1887d7ceb52222adcf1d6913969e6163aa) )
+ ROM_LOAD16_WORD_SWAP( "spab.u18", 0x1400000, 0x400000, CRC(e5a4608d) SHA1(b8e39f53e0b7ad1e16ae9c3726597776b404be1c) )
+ ROM_LOAD16_WORD_SWAP( "spcd.u19", 0x1800000, 0x400000, CRC(99bfbe32) SHA1(926a8afc4a175874f22f53300e76f59331d3b9ba) )
+ ROM_LOAD16_WORD_SWAP( "spef.u20", 0x1c00000, 0x400000, CRC(c9c799cc) SHA1(01373316700d8688deeea2e9e8f831d5f86c7f17) )
+
+ ROM_REGION( 0x0800000, "tiles_l0", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg1012.u22", 0x0000002, 0x400000, CRC(e3fb9af0) SHA1(11900cc2873337692f66fb4f1eb9c574e5a967de) )
+ ROM_LOAD32_WORD_SWAP( "bg1113.u23", 0x0000000, 0x400000, CRC(5f8657e6) SHA1(7c2854dc5d2d4efe55bda01e329da051350e0031) )
+
+ ROM_REGION( 0x0800000, "tiles_l1", 0 )
+ ROM_LOAD32_WORD_SWAP( "bg2022.u25", 0x0000002, 0x400000, CRC(f46eda52) SHA1(46530016b32a164bd76c4f53e7b53b2beb28db06) )
+ ROM_LOAD32_WORD_SWAP( "bg2123.u24", 0x0000000, 0x400000, CRC(c4ebb86b) SHA1(a7093e6e02b64566d277cbbd5fa90cd430e7c8a0) )
+
+ ROM_REGION( 0x200000, "tiles_bg", 0 )
+ ROM_LOAD16_WORD_SWAP( "map.u5", 0x00000, 0x200000, CRC(bd179dc5) SHA1(ce3fcac573b14fd5365eb5dcec3257e439d2c129) )
+
+ ROM_REGION( 0x400000, "ymf", 0 ) // OPL4 samples
+ ROM_LOAD( "opm.u6", 0x00000, 0x400000, CRC(31b05be4) SHA1(d0f4f387f84a74591224b0f42b7f5c538a3dc498) )
+ROM_END
+
+} // anonymous namespace
+
+
+//-------------------------------------------------
+// game drivers
+//-------------------------------------------------
+
+GAME( 1998, asurabld, 0, fuuki32, asurabld, fuuki32_state, empty_init, ROT0, "Fuuki", "Asura Blade - Sword of Dynasty (Japan)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
+
+GAME( 2001, asurabus, 0, fuuki32, asurabus, fuuki32_state, empty_init, ROT0, "Fuuki", "Asura Buster - Eternal Warriors (USA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
+GAME( 2000, asurabusj, asurabus, fuuki32, asurabus, fuuki32_state, empty_init, ROT0, "Fuuki", "Asura Buster - Eternal Warriors (Japan, set 1)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
+GAME( 2000, asurabusja, asurabus, fuuki32, asurabus, fuuki32_state, empty_init, ROT0, "Fuuki", "Asura Buster - Eternal Warriors (Japan, set 2)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
+GAME( 2000, asurabusjr, asurabus, fuuki32, asurabusa,fuuki32_state, empty_init, ROT0, "Fuuki", "Asura Buster - Eternal Warriors (Japan) (ARCADIA review build)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) // has pause function on P1 button 4
diff --git a/src/mame/fuuki/fuukispr.cpp b/src/mame/fuuki/fuukispr.cpp
new file mode 100644
index 00000000000..0182a8f9c94
--- /dev/null
+++ b/src/mame/fuuki/fuukispr.cpp
@@ -0,0 +1,180 @@
+// license:BSD-3-Clause
+// copyright-holders: Luca Elia, David Haywood
+
+/*
+ Fuuki Sprite hardware
+
+ Used by:
+ fuukifg2.cpp
+ fuukifg3.cpp (with sprite tile bankswitching, triple buffered)
+*/
+
+
+#include "emu.h"
+#include "fuukispr.h"
+#include "screen.h"
+
+DEFINE_DEVICE_TYPE(FUUKI_SPRITE, fuukispr_device, "fuukispr", "Fuuki Sprite hardware")
+
+fuukispr_device::fuukispr_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, FUUKI_SPRITE, tag, owner, clock)
+ , device_gfx_interface(mconfig, *this)
+ , device_video_interface(mconfig, *this)
+ , m_tile_cb(*this)
+ , m_colpri_cb(*this)
+ , m_colbase(0)
+ , m_colnum(0x100)
+{
+}
+
+GFXDECODE_MEMBER(fuukispr_device::gfxinfo)
+ GFXDECODE_DEVICE(DEVICE_SELF, 0, gfx_16x16x4_packed_msb, 0, 16)
+GFXDECODE_END
+
+void fuukispr_device::device_start()
+{
+ decode_gfx(gfxinfo);
+ gfx(0)->set_colorbase(m_colbase);
+ gfx(0)->set_colors(m_colnum);
+
+ m_tile_cb.resolve();
+ m_colpri_cb.resolve();
+}
+
+
+/***************************************************************************
+
+
+ Sprites Drawing
+
+ Offset: Bits: Value:
+
+ 0.w fedc ---- ---- ---- Number Of Tiles Along X - 1
+ ---- b--- ---- ---- Flip X
+ ---- -a-- ---- ---- 1 = Don't Draw This Sprite
+ ---- --98 7654 3210 X (Signed)
+
+ 2.w fedc ---- ---- ---- Number Of Tiles Along Y - 1
+ ---- b--- ---- ---- Flip Y
+ ---- -a-- ---- ----
+ ---- --98 7654 3210 Y (Signed)
+
+ 4.w fedc ---- ---- ---- Zoom X ($0 = Full Size, $F = Half Size)
+ ---- ba98 ---- ---- Zoom Y ""
+ ---- ---- 76-- ---- Priority
+ ---- ---- --54 3210 Color
+
+ 6.w Tile Code
+
+ for FG3 hardware
+
+ 6.w fe-- ---- ---- ---- Tile Bank
+ --dc ba98 7654 3210 Tile Code
+
+***************************************************************************/
+
+void fuukispr_device::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, bool flip_screen, u16 *spriteram, u32 size)
+{
+ // as we're likely framebuffered (sprites are delayed by 2-3 frames, at least on FG3, and doing rasters on sprites causes glitches) we
+ // only draw the sprites when MAME wants to draw the final screen line. Ideally we should framebuffer them instead.
+ if (cliprect.max_y != screen.visible_area().max_y)
+ return;
+
+ const bool tilebank = !m_tile_cb.isnull();
+ const bool priority = !m_colpri_cb.isnull();
+ const rectangle spriteclip = screen.visible_area();
+
+ const int max_x = spriteclip.max_x + 1;
+ const int max_y = spriteclip.max_y + 1;
+
+ int start, end, inc;
+ if (priority) { start = size - 4; end = -4; inc = -4; }
+ else { start = 0; end = size; inc = +4; }
+
+ // Draw them backwards, for pdrawgfx
+ for (int offs = start; offs != end; offs += inc)
+ {
+ const u16 data0 = spriteram[offs + 0];
+ if (BIT(data0, 10))
+ continue;
+
+ const u16 data1 = spriteram[offs + 1];
+ const u16 attr = spriteram[offs + 2];
+ u32 code = spriteram[offs + 3];
+ if (tilebank)
+ m_tile_cb(code);
+
+ bool flipx = BIT(data0, 11);
+ bool flipy = BIT(data1, 11);
+
+ const int xnum = ((data0 >> 12) & 0xf) + 1;
+ const int ynum = ((data1 >> 12) & 0xf) + 1;
+
+ const int xzoom = 16 * 8 - (8 * ((attr >> 12) & 0xf)) / 2;
+ const int yzoom = 16 * 8 - (8 * ((attr >> 8) & 0xf)) / 2;
+
+ u32 pri_mask = 0;
+ u32 colour = attr & 0xff;
+ if (priority)
+ m_colpri_cb(colour, pri_mask);
+
+ int sx = (data0 & 0x1ff) - (data0 & 0x200);
+ int sy = (data1 & 0x1ff) - (data1 & 0x200);
+
+ if (flip_screen)
+ {
+ flipx = !flipx; sx = max_x - sx - xnum * 16;
+ flipy = !flipy; sy = max_y - sy - ynum * 16;
+ }
+
+ int xstart, ystart, xend, yend, xinc, yinc;
+ if (flipx) { xstart = xnum-1; xend = -1; xinc = -1; }
+ else { xstart = 0; xend = xnum; xinc = +1; }
+
+ if (flipy) { ystart = ynum-1; yend = -1; yinc = -1; }
+ else { ystart = 0; yend = ynum; yinc = +1; }
+
+ for (int y = ystart; y != yend; y += yinc)
+ {
+ for (int x = xstart; x != xend; x += xinc)
+ {
+ if (priority)
+ {
+ if (xzoom == (16*8) && yzoom == (16*8))
+ gfx(0)->prio_transpen(bitmap, spriteclip,
+ code++,
+ colour,
+ flipx, flipy,
+ sx + x * 16, sy + y * 16,
+ screen.priority(), pri_mask, 15);
+ else
+ gfx(0)->prio_zoom_transpen(bitmap, spriteclip,
+ code++,
+ colour,
+ flipx, flipy,
+ sx + (x * xzoom) / 8, sy + (y * yzoom) / 8,
+ (0x10000/0x10/8) * (xzoom + 8), (0x10000/0x10/8) * (yzoom + 8),// nearest greater integer value to avoid holes
+ screen.priority(), pri_mask, 15);
+ }
+ else
+ {
+ if (xzoom == (16*8) && yzoom == (16*8))
+ gfx(0)->transpen(bitmap, spriteclip,
+ code++,
+ colour,
+ flipx, flipy,
+ sx + x * 16, sy + y * 16,
+ 15);
+ else
+ gfx(0)->zoom_transpen(bitmap, spriteclip,
+ code++,
+ colour,
+ flipx, flipy,
+ sx + (x * xzoom) / 8, sy + (y * yzoom) / 8,
+ (0x10000/0x10/8) * (xzoom + 8), (0x10000/0x10/8) * (yzoom + 8),// nearest greater integer value to avoid holes
+ 15);
+ }
+ }
+ }
+ }
+}
diff --git a/src/mame/fuuki/fuukispr.h b/src/mame/fuuki/fuukispr.h
new file mode 100644
index 00000000000..661da0afd9a
--- /dev/null
+++ b/src/mame/fuuki/fuukispr.h
@@ -0,0 +1,39 @@
+// license:BSD-3-Clause
+// copyright-holders: Luca Elia, David Haywood
+
+#ifndef MAME_FUUKI_FUUKISPR_H
+#define MAME_FUUKI_FUUKISPR_H
+
+#pragma once
+
+class fuukispr_device : public device_t, public device_gfx_interface, public device_video_interface
+{
+public:
+ typedef device_delegate<void (u32 &code)> tile_delegate;
+ typedef device_delegate<void (u32 &colour, u32 &pri_mask)> colpri_cb_delegate;
+
+ fuukispr_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ // configuration
+ void set_color_base(u16 base) { m_colbase = base; }
+ void set_color_num(u16 num) { m_colnum = num; }
+ template <typename... T> void set_tile_callback(T &&... args) { m_tile_cb.set(std::forward<T>(args)...); }
+ template <typename... T> void set_colpri_callback(T &&... args) { m_colpri_cb.set(std::forward<T>(args)...); }
+
+ void draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, bool flip_screen, u16 *spriteram, u32 size);
+
+protected:
+ virtual void device_start() override ATTR_COLD;
+
+private:
+ tile_delegate m_tile_cb;
+ colpri_cb_delegate m_colpri_cb;
+ u16 m_colbase;
+ u16 m_colnum;
+
+ DECLARE_GFXDECODE_MEMBER(gfxinfo);
+};
+
+DECLARE_DEVICE_TYPE(FUUKI_SPRITE, fuukispr_device)
+
+#endif // MAME_FUUKI_FUUKISPR_H
diff --git a/src/mame/fuuki/fuukitmap.cpp b/src/mame/fuuki/fuukitmap.cpp
new file mode 100644
index 00000000000..cd09b3daef4
--- /dev/null
+++ b/src/mame/fuuki/fuukitmap.cpp
@@ -0,0 +1,263 @@
+// license:BSD-3-Clause
+// copyright-holders: Luca Elia, David Haywood
+
+/*
+ Fuuki Tilemap hardware
+
+ Used by:
+ fuukifg2.cpp
+ fuukifg3.cpp
+*/
+
+#include "emu.h"
+#include "fuukitmap.h"
+
+#include <algorithm>
+
+
+void fuukitmap_device::vram_map(address_map &map)
+{
+ map(0x0000, 0x1fff).rw(FUNC(fuukitmap_device::vram_r<0>), FUNC(fuukitmap_device::vram_w<0>));
+ map(0x2000, 0x3fff).rw(FUNC(fuukitmap_device::vram_r<1>), FUNC(fuukitmap_device::vram_w<1>));
+ map(0x4000, 0x5fff).rw(FUNC(fuukitmap_device::vram_r<2>), FUNC(fuukitmap_device::vram_buffered_w<2>));
+ map(0x6000, 0x7fff).rw(FUNC(fuukitmap_device::vram_r<3>), FUNC(fuukitmap_device::vram_buffered_w<3>));
+}
+
+void fuukitmap_device::vregs_map(address_map &map)
+{
+ map(0x00000, 0x0001f).rw(FUNC(fuukitmap_device::vregs_r), FUNC(fuukitmap_device::vregs_w));
+ map(0x10000, 0x10003).rw(FUNC(fuukitmap_device::unknown_r), FUNC(fuukitmap_device::unknown_w)); // Flipscreen related
+ map(0x20000, 0x20001).rw(FUNC(fuukitmap_device::priority_r), FUNC(fuukitmap_device::priority_w)); // Controls layer order
+}
+
+
+DEFINE_DEVICE_TYPE(FUUKI_TILEMAP, fuukitmap_device, "fuukitmap", "Fuuki Tilemap hardware")
+
+fuukitmap_device::fuukitmap_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, FUUKI_TILEMAP, tag, owner, clock)
+ , device_gfx_interface(mconfig, *this, nullptr)
+ , device_video_interface(mconfig, *this, true)
+ , m_colour_cb(*this)
+ , m_level_1_irq_cb(*this)
+ , m_vblank_irq_cb(*this)
+ , m_raster_irq_cb(*this)
+ , m_xoffs(0)
+ , m_xoffs_flip(0)
+ , m_yoffs(0)
+ , m_yoffs_flip(0)
+ , m_layer2_xoffs(0)
+ , m_layer2_yoffs(0)
+ , m_tilemap{ nullptr, nullptr, nullptr }
+ , m_vram(*this, "vram%u", 0U, 0x2000U, ENDIANNESS_BIG)
+ , m_unknown{ 0, 0 }
+ , m_priority(0)
+ , m_flip(false)
+ , m_tmap_front(0)
+ , m_tmap_middle(1)
+ , m_tmap_back(2)
+ , m_level_1_interrupt_timer(nullptr)
+ , m_vblank_interrupt_timer(nullptr)
+ , m_raster_interrupt_timer(nullptr)
+{
+}
+
+void fuukitmap_device::device_start()
+{
+ m_colour_cb.resolve();
+
+ std::fill(std::begin(m_vregs), std::end(m_vregs), 0);
+
+ m_tilemap[0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(fuukitmap_device::get_tile_info<0>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32);
+ m_tilemap[1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(fuukitmap_device::get_tile_info<1>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32);
+ m_tilemap[2] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(fuukitmap_device::get_tile_info<2>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
+
+ m_level_1_interrupt_timer = timer_alloc(FUNC(fuukitmap_device::level1_interrupt), this);
+ m_vblank_interrupt_timer = timer_alloc(FUNC(fuukitmap_device::vblank_interrupt), this);
+ m_raster_interrupt_timer = timer_alloc(FUNC(fuukitmap_device::raster_interrupt), this);
+
+ save_item(NAME(m_vregs));
+ save_item(NAME(m_unknown));
+ save_item(NAME(m_priority));
+ save_item(NAME(m_flip));
+ save_item(NAME(m_tmap_front));
+ save_item(NAME(m_tmap_middle));
+ save_item(NAME(m_tmap_back));
+}
+
+void fuukitmap_device::device_reset()
+{
+ const rectangle &visarea = screen().visible_area();
+
+ m_level_1_interrupt_timer->adjust(screen().time_until_pos(248));
+ m_vblank_interrupt_timer->adjust(screen().time_until_vblank_start());
+ m_raster_interrupt_timer->adjust(screen().time_until_pos(0, visarea.max_x + 1));
+}
+
+
+TIMER_CALLBACK_MEMBER(fuukitmap_device::level1_interrupt)
+{
+ m_level_1_irq_cb(ASSERT_LINE);
+ m_level_1_interrupt_timer->adjust(screen().time_until_pos(248));
+}
+
+TIMER_CALLBACK_MEMBER(fuukitmap_device::vblank_interrupt)
+{
+ m_vblank_irq_cb(ASSERT_LINE); // VBlank IRQ
+ m_vblank_interrupt_timer->adjust(screen().time_until_vblank_start());
+}
+
+TIMER_CALLBACK_MEMBER(fuukitmap_device::raster_interrupt)
+{
+ m_raster_irq_cb(ASSERT_LINE); // Raster Line IRQ
+ screen().update_partial(screen().vpos());
+ m_raster_interrupt_timer->adjust(screen().frame_period());
+}
+
+
+template <int Layer>
+void fuukitmap_device::vram_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_vram[Layer][offset]);
+ m_tilemap[Layer]->mark_tile_dirty(offset / 2);
+}
+
+template <int Layer>
+void fuukitmap_device::vram_buffered_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ const int buffer = (m_vregs[0x1e / 2] & 0x40) >> 6;
+ COMBINE_DATA(&m_vram[Layer][offset]);
+ if ((Layer & 1) == buffer)
+ m_tilemap[2]->mark_tile_dirty(offset / 2);
+}
+
+
+void fuukitmap_device::vregs_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ const u16 old = m_vregs[offset];
+ data = COMBINE_DATA(&m_vregs[offset]);
+ if (old != data)
+ {
+ if (offset == 0x1c / 2)
+ {
+ const rectangle &visarea = screen().visible_area();
+ attotime period = screen().frame_period();
+ m_raster_interrupt_timer->adjust(screen().time_until_pos(data, visarea.max_x + 1), 0, period);
+ }
+ if (offset == 0x1e / 2)
+ {
+ if ((old ^ data) & 0x40)
+ m_tilemap[2]->mark_all_dirty();
+
+ m_flip = BIT(m_vregs[0x1e / 2], 0);
+ }
+ }
+}
+
+
+/***************************************************************************
+
+
+ Tilemaps
+
+ Offset: Bits: Value:
+
+ 0.w Code
+
+ 2.w fedc ba98 ---- ----
+ ---- ---- 7--- ---- Flip Y
+ ---- ---- -6-- ---- Flip X
+ ---- ---- --54 3210 Color
+
+
+***************************************************************************/
+
+template <int Layer>
+TILE_GET_INFO_MEMBER(fuukitmap_device::get_tile_info)
+{
+ const int buffer = (Layer < 2) ? 0 : (m_vregs[0x1e / 2] & 0x40) >> 6;
+ const u16 code = m_vram[Layer|buffer][2 * tile_index + 0];
+ const u16 attr = m_vram[Layer|buffer][2 * tile_index + 1];
+ u32 colour = attr & 0x3f;
+ if (!m_colour_cb.isnull())
+ m_colour_cb(Layer, colour);
+ tileinfo.set(Layer, code, colour, TILE_FLIPYX((attr >> 6) & 3));
+}
+
+
+/***************************************************************************
+
+
+ Screen Drawing
+
+ Video Registers (vregs):
+
+ 00.w Layer 0 Scroll Y
+ 02.w Layer 0 Scroll X
+ 04.w Layer 1 Scroll Y
+ 06.w Layer 1 Scroll X
+ 08.w Layer 2 Scroll Y
+ 0a.w Layer 2 Scroll X
+ 0c.w Layers Y Offset
+ 0e.w Layers X Offset
+
+ 10-1a.w ? 0
+ 1c.w Trigger a level 5 irq on this raster line
+ 1e.w ? $3390/$3393 (Flip Screen Off/On), $0040 is buffer for tilemap 2
+
+ Priority Register (priority):
+
+ fedc ba98 7654 3---
+ ---- ---- ---- -210 Layer Order
+
+
+ Unknown Registers (unknown):
+
+ 00.w ? $0200/$0201 (Flip Screen Off/On)
+ 02.w ? $f300/$0330
+
+***************************************************************************/
+
+// Wrapper to handle bg and bg2 together
+void fuukitmap_device::draw_layer(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, u8 i, int flag, u8 pri, u8 primask)
+{
+ m_tilemap[i]->draw(screen, bitmap, cliprect, flag, pri, primask);
+}
+
+void fuukitmap_device::prepare()
+{
+ /*
+ It's not independent bits causing layers to switch, that wouldn't make sense with 3 bits.
+ */
+
+ static const u8 pri_table[6][3] = {
+ { 0, 1, 2 }, // Special moves 0>1, 0>2 (0,1,2 or 0,2,1)
+ { 0, 2, 1 }, // Two Levels - 0>1 (0,1,2 or 0,2,1 or 2,0,1)
+ { 1, 0, 2 }, // Most Levels - 2>1 1>0 2>0 (1,0,2)
+ { 1, 2, 0 }, // Not used?
+ { 2, 0, 1 }, // Title etc. - 0>1 (0,1,2 or 0,2,1 or 2,0,1)
+ { 2, 1, 0 }}; // Char Select, prison stage 1>0 (leaves 1,2,0 or 2,1,0)
+
+ m_tmap_front = pri_table[m_priority & 0x0f][0];
+ m_tmap_middle = pri_table[m_priority & 0x0f][1];
+ m_tmap_back = pri_table[m_priority & 0x0f][2];
+
+ // Layers scrolling
+ const u16 scrolly_offs = m_vregs[0xc / 2] - (m_flip ? m_xoffs_flip : m_xoffs);
+ const u16 scrollx_offs = m_vregs[0xe / 2] - (m_flip ? m_yoffs_flip : m_yoffs);
+
+ const u16 layer0_scrolly = m_vregs[0x0 / 2] + scrolly_offs;
+ const u16 layer0_scrollx = m_vregs[0x2 / 2] + scrollx_offs;
+ const u16 layer1_scrolly = m_vregs[0x4 / 2] + scrolly_offs;
+ const u16 layer1_scrollx = m_vregs[0x6 / 2] + scrollx_offs;
+
+ const u16 layer2_scrolly = m_vregs[0x8 / 2];
+ const u16 layer2_scrollx = m_vregs[0xa / 2];
+
+ m_tilemap[0]->set_scrollx(0, layer0_scrollx);
+ m_tilemap[0]->set_scrolly(0, layer0_scrolly);
+ m_tilemap[1]->set_scrollx(0, layer1_scrollx);
+ m_tilemap[1]->set_scrolly(0, layer1_scrolly);
+
+ m_tilemap[2]->set_scrollx(0, layer2_scrollx + m_layer2_xoffs);
+ m_tilemap[2]->set_scrolly(0, layer2_scrolly + m_layer2_yoffs);
+}
diff --git a/src/mame/fuuki/fuukitmap.h b/src/mame/fuuki/fuukitmap.h
new file mode 100644
index 00000000000..fb4d1289fc6
--- /dev/null
+++ b/src/mame/fuuki/fuukitmap.h
@@ -0,0 +1,119 @@
+// license:BSD-3-Clause
+// copyright-holders: Luca Elia, David Haywood
+
+#ifndef MAME_FUUKI_FUUKITMAP_H
+#define MAME_FUUKI_FUUKITMAP_H
+
+#pragma once
+
+#include "tilemap.h"
+#include "screen.h"
+
+class fuukitmap_device : public device_t, public device_gfx_interface, public device_video_interface
+{
+public:
+ typedef device_delegate<void (u8 layer, u32 &colour)> colour_cb_delegate;
+
+ fuukitmap_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+ template <typename T> fuukitmap_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&palette_tag, const gfx_decode_entry *gfxinfo)
+ : fuukitmap_device(mconfig, tag, owner, clock)
+ {
+ set_info(gfxinfo);
+ set_palette(std::forward<T>(palette_tag));
+ }
+
+ // configuration
+ template <typename... T> void set_colour_callback(T &&... args) { m_colour_cb.set(std::forward<T>(args)...); }
+
+ auto level_1_irq_callback() { return m_level_1_irq_cb.bind(); }
+ auto vblank_irq_callback() { return m_vblank_irq_cb.bind(); }
+ auto raster_irq_callback() { return m_raster_irq_cb.bind(); }
+
+ void set_xoffs(int xoffs, int xoffs_flip)
+ {
+ m_xoffs = xoffs;
+ m_xoffs_flip = xoffs_flip;
+ }
+
+ void set_yoffs(int yoffs, int yoffs_flip)
+ {
+ m_yoffs = yoffs;
+ m_yoffs_flip = yoffs_flip;
+ }
+
+ void set_layer2_xoffs(int xoffs) { m_layer2_xoffs = xoffs; }
+ void set_layer2_yoffs(int yoffs) { m_layer2_yoffs = yoffs; }
+
+ // getters, setters
+ bool flip_screen() { return m_flip; }
+ u8 tmap_front() { return m_tmap_front; }
+ u8 tmap_middle() { return m_tmap_middle; }
+ u8 tmap_back() { return m_tmap_back; }
+
+ void set_transparent_pen(int layer, pen_t pen) { m_tilemap[layer]->set_transparent_pen(pen); }
+
+ void prepare();
+ void draw_layer(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, u8 i, int flag, u8 pri, u8 primask = 0xff);
+
+ void vram_map(address_map &map) ATTR_COLD;
+ void vregs_map(address_map &map) ATTR_COLD;
+
+protected:
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+private:
+ // configurations
+ colour_cb_delegate m_colour_cb;
+
+ devcb_write_line m_level_1_irq_cb;
+ devcb_write_line m_vblank_irq_cb;
+ devcb_write_line m_raster_irq_cb;
+
+ int m_xoffs, m_xoffs_flip;
+ int m_yoffs, m_yoffs_flip;
+ int m_layer2_xoffs;
+ int m_layer2_yoffs;
+
+ // video-related
+ tilemap_t *m_tilemap[3];
+
+ memory_share_array_creator<u16, 4> m_vram;
+ u16 m_vregs[0x20 / 2];
+ u16 m_unknown[2];
+ u16 m_priority;
+
+ bool m_flip;
+ u8 m_tmap_front;
+ u8 m_tmap_middle;
+ u8 m_tmap_back;
+
+ // misc
+ emu_timer *m_level_1_interrupt_timer;
+ emu_timer *m_vblank_interrupt_timer;
+ emu_timer *m_raster_interrupt_timer;
+
+ // handlers
+ template <int Layer> u16 vram_r(offs_t offset) { return m_vram[Layer][offset]; }
+ template <int Layer> void vram_w(offs_t offset, u16 data, u16 mem_mask = ~0);
+ template <int Layer> void vram_buffered_w(offs_t offset, u16 data, u16 mem_mask = ~0);
+
+ u16 vregs_r(offs_t offset) { return m_vregs[offset]; }
+ void vregs_w(offs_t offset, u16 data, u16 mem_mask = ~0);
+
+ u16 unknown_r(offs_t offset) { return m_unknown[offset]; }
+ void unknown_w(offs_t offset, u16 data, u16 mem_mask = ~0) { COMBINE_DATA(&m_unknown[offset]); }
+
+ u16 priority_r() { return m_priority; }
+ void priority_w(offs_t offset, u16 data, u16 mem_mask = ~0) { COMBINE_DATA(&m_priority); }
+
+ TIMER_CALLBACK_MEMBER(level1_interrupt);
+ TIMER_CALLBACK_MEMBER(vblank_interrupt);
+ TIMER_CALLBACK_MEMBER(raster_interrupt);
+
+ template <int Layer> TILE_GET_INFO_MEMBER(get_tile_info);
+};
+
+DECLARE_DEVICE_TYPE(FUUKI_TILEMAP, fuukitmap_device)
+
+#endif // MAME_FUUKI_FUUKITMAP_H