summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author cam900 <dbtlrchl@naver.com>2025-09-10 00:50:32 +0900
committer GitHub <noreply@github.com>2025-09-10 01:50:32 +1000
commit5ee1f1678c8a025b154dd5cced5b9babdc96c6f3 (patch)
treeb7e9ea53d16f27ee6d8ba147119b00f968916310
parentcb3afa1c44f0449b4287459ee06d57458c10ab20 (diff)
-konami/twin16_v.cpp: Encapsulated Konami Twin 16 video subsystem as a device. (#14129)
* Slightly optimised sprite drawing. * Use logmacro.h for configurable logging function. * Use <algorithm> templates to replace memset and memcpy. * Reduced duplication, made more local variables const, use more appropriate integer variable types. -konami/twin16.cpp: Cleaned up code and updated comments.
-rw-r--r--src/mame/konami/twin16.cpp448
-rw-r--r--src/mame/konami/twin16.h154
-rw-r--r--src/mame/konami/twin16_v.cpp372
-rw-r--r--src/mame/konami/twin16_v.h101
4 files changed, 589 insertions, 486 deletions
diff --git a/src/mame/konami/twin16.cpp b/src/mame/konami/twin16.cpp
index a4a37881777..3a401eaa0e1 100644
--- a/src/mame/konami/twin16.cpp
+++ b/src/mame/konami/twin16.cpp
@@ -14,11 +14,11 @@ Sounds are generated by a Z80, a Yamaha 2151 and 3012, a Konami custom IC and a
Dark Adventure / Devil World / Majuu no Ohkoku
Vulcan Venture / Gradius II
- Cuebrick
+ Cuebrick (Japan)
MIA (Japan)
Final Round / Hard Puncher (Japan)
-68000 Memory Map for Konami Twin System
+68000 Memory Map for Konami Twin16 System
CPUA CPUB
0x000000..0x03ffff ROM 0x000000..0x03ffff
@@ -35,39 +35,203 @@ Sounds are generated by a Z80, a Yamaha 2151 and 3012, a Konami custom IC and a
0x100000..0x103fff FIXRAM (text layer)
0x120000..0x123fff VIDRAM (tilemaps) 0x480000..0x483fff (shared)
0x140000..0x143fff OBJRAM (sprites) 0x400000..0x403fff (shared)
- ZIP RAM (tiles) 0x500000..0x53ffff
+ ZIP RAM (tile gfx) 0x500000..0x53ffff
gfx ROM (banked) 0x600000..0x77ffff
sprite gfx RAM 0x780000..0x79ffff
Known Issues:
- repeated uPD7759C samples in fround, disconnecting reset helps but doesn't fix it
-- see video/twin16.c for graphics related issues
+- see konami/twin16_v.cpp for graphics related issues
*/
#include "emu.h"
-#include "twin16.h"
#include "konamipt.h"
+#include "twin16_v.h"
#include "cpu/m68000/m68000.h"
#include "cpu/z80/z80.h"
#include "machine/gen_latch.h"
#include "machine/nvram.h"
#include "machine/watchdog.h"
+#include "sound/k007232.h"
+#include "sound/upd7759.h"
#include "sound/ymopm.h"
+
+#include "emupal.h"
+#include "screen.h"
#include "speaker.h"
+#include "tilemap.h"
+
+namespace {
+
+class twin16_state : public driver_device
+{
+public:
+ twin16_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_subcpu(*this, "sub"),
+ m_audiocpu(*this, "audiocpu"),
+ m_video(*this, "video"),
+ m_k007232(*this, "k007232"),
+ m_upd7759(*this, "upd"),
+ m_screen(*this, "screen"),
+ m_palette(*this, "palette"),
+ m_gfxrombank(*this, "gfxrombank"),
+ m_zipram(*this, "zipram"),
+ m_sprite_gfx_ram(*this, "sprite_gfx_ram"),
+ m_gfxrom(*this, "gfxrom")
+ { }
+
+ void devilw(machine_config &config) ATTR_COLD;
+ void miaj(machine_config &config) ATTR_COLD;
+ void twin16(machine_config &config) ATTR_COLD;
+
+protected:
+ virtual void machine_start() override ATTR_COLD;
+ virtual void machine_reset() override ATTR_COLD;
+ virtual void video_start() override ATTR_COLD;
+
+ required_device<cpu_device> m_maincpu;
+ optional_device<cpu_device> m_subcpu;
+ required_device<cpu_device> m_audiocpu;
+ required_device<konami_twin16_video_device> m_video;
+ required_device<k007232_device> m_k007232;
+ required_device<upd7759_device> m_upd7759;
+ required_device<screen_device> m_screen;
+ required_device<palette_device> m_palette;
+ optional_memory_bank m_gfxrombank;
+ optional_shared_ptr<uint16_t> m_zipram;
+ optional_shared_ptr<uint16_t> m_sprite_gfx_ram;
+ required_region_ptr<uint16_t> m_gfxrom;
+
+ uint16_t m_cpua_register = 0;
+ uint16_t m_cpub_register = 0;
+
+ void cpua_register_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+ void cpub_register_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+
+ void zipram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+
+ uint8_t upd_busy_r();
+ void upd_reset_w(uint8_t data);
+ void upd_start_w(uint8_t data);
+
+ void virq_callback(int state);
+ uint16_t sprite_callback(int addr);
+
+ void volume_callback(uint8_t data);
+
+ void main_map(address_map &map) ATTR_COLD;
+ void sound_map(address_map &map) ATTR_COLD;
+ void sub_map(address_map &map) ATTR_COLD;
+};
+
+class fround_state : public twin16_state
+{
+public:
+ fround_state(const machine_config &mconfig, device_type type, const char *tag) :
+ twin16_state(mconfig, type, tag)
+ { }
+
+ void fround(machine_config &config);
+
+protected:
+ virtual void machine_start() override ATTR_COLD;
+
+private:
+ uint8_t m_gfx_bank[4] = { };
+
+ void fround_cpu_register_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+ void gfx_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+
+ uint16_t fround_sprite_callback(int addr);
+ uint32_t tile_callback(uint16_t data);
+
+ void fround_map(address_map &map) ATTR_COLD;
+};
+
+class cuebrickj_state : public twin16_state
+{
+public:
+ cuebrickj_state(const machine_config &mconfig, device_type type, const char *tag) :
+ twin16_state(mconfig, type, tag),
+ m_nvram_bank(*this, "nvram_bank")
+ { }
+
+ void cuebrickj(machine_config &config);
+
+protected:
+ virtual void machine_start() override ATTR_COLD;
+
+private:
+ required_memory_bank m_nvram_bank;
+
+ uint16_t m_nvram[0x400 * 0x20 / 2]{};
+
+ void nvram_bank_w(uint8_t data);
+ void cuebrickj_main_map(address_map &map) ATTR_COLD;
+};
-int twin16_state::spriteram_process_enable()
+
+void twin16_state::virq_callback(int state)
{
- return (m_CPUA_register & 0x40) == 0;
+ if (state)
+ {
+ if (BIT(m_cpua_register, 5))
+ m_maincpu->set_input_line(5, HOLD_LINE);
+ if (m_subcpu.found() && BIT(m_cpub_register, 1))
+ m_subcpu->set_input_line(5, HOLD_LINE);
+ }
}
+
+uint16_t twin16_state::sprite_callback(int addr)
+{
+ switch ((addr >> 18) & 0x3)
+ {
+ case 0:
+ case 1:
+ return m_gfxrom[addr & 0x7ffff];
+ case 2:
+ return m_gfxrom[0x80000 + (addr & 0x3ffff) + (BIT(addr, 20) ? 0x40000 : 0)];
+ case 3:
+ return m_sprite_gfx_ram[addr & 0xffff];
+ }
+ return 0;
+}
+
+
+uint16_t fround_state::fround_sprite_callback(int addr)
+{
+ return m_gfxrom[addr & 0x7ffff];
+}
+
+
+uint32_t fround_state::tile_callback(uint16_t data)
+{
+ /* fedcba9876543210
+ xxx------------- color; high bit is also priority over sprites
+ ---xx----------- tile bank
+ -----xxxxxxxxxxx tile number
+ */
+ const uint8_t bank = (data >> 11) & 3;
+ return (m_gfx_bank[bank] << 11) | (data & 0x7ff);
+}
+
+void twin16_state::video_start()
+{
+ m_palette->set_shadow_factor(0.4); // screenshots estimate
+}
+
+
/******************************************************************************************/
/* Read/Write Handlers */
-void twin16_state::CPUA_register_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+void twin16_state::cpua_register_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
/*
7 6 5 4 3 2 1 0
@@ -77,30 +241,28 @@ void twin16_state::CPUA_register_w(offs_t offset, uint16_t data, uint16_t mem_ma
X 0->1 trigger IRQ on sound CPU
x x x coin counters
*/
- int old = m_CPUA_register;
- COMBINE_DATA(&m_CPUA_register);
+ const int old = m_cpua_register;
+ COMBINE_DATA(&m_cpua_register);
- if (m_CPUA_register != old)
+ if (m_cpua_register != old)
{
- int rising_edge = ~old & m_CPUA_register;
- int falling_edge = old & ~m_CPUA_register;
+ const int rising_edge = ~old & m_cpua_register;
- if (rising_edge & 0x08)
+ if (BIT(rising_edge, 3))
m_audiocpu->set_input_line_and_vector(0, HOLD_LINE, 0xff); // Z80
- if (falling_edge & 0x40)
- spriteram_process();
+ m_video->sprite_process_enable_w(BIT(~m_cpua_register, 6));
- if (rising_edge & 0x10)
+ if (BIT(rising_edge, 4))
m_subcpu->set_input_line(M68K_IRQ_6, HOLD_LINE);
- machine().bookkeeping().coin_counter_w(0, m_CPUA_register & 0x01);
- machine().bookkeeping().coin_counter_w(1, m_CPUA_register & 0x02);
- machine().bookkeeping().coin_counter_w(2, m_CPUA_register & 0x04);
+ machine().bookkeeping().coin_counter_w(0, BIT(m_cpua_register, 0));
+ machine().bookkeeping().coin_counter_w(1, BIT(m_cpua_register, 1));
+ machine().bookkeeping().coin_counter_w(2, BIT(m_cpua_register, 2));
}
}
-void twin16_state::CPUB_register_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+void twin16_state::cpub_register_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
/*
7 6 5 4 3 2 1 0
@@ -108,20 +270,20 @@ void twin16_state::CPUB_register_w(offs_t offset, uint16_t data, uint16_t mem_ma
X IRQ5 enable
X 0->1 trigger IRQ6 on CPUA
*/
- int old = m_CPUB_register;
- COMBINE_DATA(&m_CPUB_register);
+ const int old = m_cpub_register;
+ COMBINE_DATA(&m_cpub_register);
- if (m_CPUB_register != old)
+ if (m_cpub_register != old)
{
- int rising_edge = ~old & m_CPUB_register;
- if (rising_edge & 0x01)
+ const int rising_edge = ~old & m_cpub_register;
+ if (BIT(rising_edge, 0))
m_maincpu->set_input_line(M68K_IRQ_6, HOLD_LINE);
- m_gfxrombank->set_entry(BIT(m_CPUB_register, 2));
+ m_gfxrombank->set_entry(BIT(m_cpub_register, 2));
}
}
-void fround_state::fround_CPU_register_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+void fround_state::fround_cpu_register_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
/*
7 6 5 4 3 2 1 0
@@ -129,19 +291,58 @@ void fround_state::fround_CPU_register_w(offs_t offset, uint16_t data, uint16_t
X 0->1 trigger IRQ on sound CPU
x x coin counters
*/
- uint16_t old = m_CPUA_register;
- COMBINE_DATA(&m_CPUA_register);
+ const uint16_t old = m_cpua_register;
+ COMBINE_DATA(&m_cpua_register);
- if (m_CPUA_register != old)
+ if (m_cpua_register != old)
{
- if ((old & 0x08) == 0 && (m_CPUA_register & 0x08))
+ if (BIT(~old, 3) && BIT(m_cpua_register, 3))
m_audiocpu->set_input_line_and_vector(0, HOLD_LINE, 0xff); // Z80
- machine().bookkeeping().coin_counter_w(0, m_CPUA_register & 0x01);
- machine().bookkeeping().coin_counter_w(1, m_CPUA_register & 0x02);
+ m_video->sprite_process_enable_w(BIT(~m_cpua_register, 6));
+
+ machine().bookkeeping().coin_counter_w(0, BIT(m_cpua_register, 0));
+ machine().bookkeeping().coin_counter_w(1, BIT(m_cpua_register, 1));
}
}
+void cuebrickj_state::nvram_bank_w(uint8_t data)
+{
+ m_nvram_bank->set_entry(data);
+}
+
+void twin16_state::zipram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ const uint16_t old = m_zipram[offset];
+ COMBINE_DATA(&m_zipram[offset]);
+ if (m_zipram[offset] != old)
+ m_video->gfx(1)->mark_dirty(offset / 16);
+}
+
+void fround_state::gfx_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ int changed = 0;
+
+ if (ACCESSING_BITS_0_7)
+ {
+ const int oldbank0 = m_gfx_bank[0];
+ const int oldbank1 = m_gfx_bank[1];
+ m_gfx_bank[0] = (data >> 0) & 0xf;
+ m_gfx_bank[1] = (data >> 4) & 0xf;
+ changed |= (oldbank0 ^ m_gfx_bank[0]) | (oldbank1 ^ m_gfx_bank[1]);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ const int oldbank2 = m_gfx_bank[2];
+ const int oldbank3 = m_gfx_bank[3];
+ m_gfx_bank[2] = (data >> 8) & 0xf;
+ m_gfx_bank[3] = (data >> 12) & 0xf;
+ changed |= (oldbank2 ^ m_gfx_bank[2]) | (oldbank3 ^ m_gfx_bank[3]);
+ }
+ if (changed)
+ m_video->mark_scroll_dirty();
+}
+
uint8_t twin16_state::upd_busy_r()
{
return m_upd7759->busy_r();
@@ -180,7 +381,7 @@ void twin16_state::main_map(address_map &map)
map(0x060000, 0x063fff).ram();
map(0x080000, 0x080fff).rw(m_palette, FUNC(palette_device::read8), FUNC(palette_device::write8)).umask16(0x00ff).share("palette");
map(0x081000, 0x081fff).nopw();
- map(0x0a0000, 0x0a0001).portr("SYSTEM").w(FUNC(twin16_state::CPUA_register_w));
+ map(0x0a0000, 0x0a0001).portr("SYSTEM").w(FUNC(twin16_state::cpua_register_w));
map(0x0a0002, 0x0a0003).portr("P1");
map(0x0a0004, 0x0a0005).portr("P2");
map(0x0a0006, 0x0a0007).portr("P3");
@@ -188,19 +389,19 @@ void twin16_state::main_map(address_map &map)
map(0x0a0010, 0x0a0011).portr("DSW2").w("watchdog", FUNC(watchdog_timer_device::reset16_w));
map(0x0a0012, 0x0a0013).portr("DSW1");
map(0x0a0018, 0x0a0019).portr("DSW3");
- map(0x0c0000, 0x0c000f).w(FUNC(twin16_state::video_register_w));
- map(0x0c000e, 0x0c000f).r(FUNC(twin16_state::sprite_status_r));
- map(0x100000, 0x103fff).ram().w(FUNC(twin16_state::fixram_w)).share("fixram");
+ map(0x0c0000, 0x0c000f).w(m_video, FUNC(konami_twin16_video_device::video_register_w));
+ map(0x0c000e, 0x0c000f).r(m_video, FUNC(konami_twin16_video_device::sprite_status_r));
+ map(0x100000, 0x103fff).rw(m_video, FUNC(konami_twin16_video_device::fixram_r), FUNC(konami_twin16_video_device::fixram_w));
// map(0x104000, 0x105fff).noprw(); // miaj
- map(0x120000, 0x121fff).ram().w(FUNC(twin16_state::videoram0_w)).share("videoram.0");
- map(0x122000, 0x123fff).ram().w(FUNC(twin16_state::videoram1_w)).share("videoram.1");
- map(0x140000, 0x143fff).ram().share("spriteram");
+ map(0x120000, 0x121fff).rw(m_video, FUNC(konami_twin16_video_device::videoram_r<0>), FUNC(konami_twin16_video_device::videoram_w<0>));
+ map(0x122000, 0x123fff).rw(m_video, FUNC(konami_twin16_video_device::videoram_r<1>), FUNC(konami_twin16_video_device::videoram_w<1>));
+ map(0x140000, 0x143fff).rw(m_video, FUNC(konami_twin16_video_device::spriteram_r), FUNC(konami_twin16_video_device::spriteram_w));
}
void cuebrickj_state::cuebrickj_main_map(address_map &map)
{
main_map(map);
- map(0x0b0000, 0x0b03ff).bankrw("nvrambank");
+ map(0x0b0000, 0x0b03ff).bankrw(m_nvram_bank);
map(0x0b0400, 0x0b0400).w(FUNC(cuebrickj_state::nvram_bank_w));
}
@@ -211,14 +412,14 @@ void twin16_state::sub_map(address_map &map)
// map(0x044000, 0x04ffff).noprw(); // miaj
map(0x060000, 0x063fff).ram();
map(0x080000, 0x09ffff).rom().region("data", 0);
- map(0x0a0000, 0x0a0001).w(FUNC(twin16_state::CPUB_register_w));
- map(0x400000, 0x403fff).ram().share("spriteram");
- map(0x480000, 0x481fff).ram().w(FUNC(twin16_state::videoram0_w)).share("videoram.0");
- map(0x482000, 0x483fff).ram().w(FUNC(twin16_state::videoram1_w)).share("videoram.1");
- map(0x500000, 0x53ffff).ram().w(FUNC(twin16_state::zipram_w)).share("zipram");
+ map(0x0a0000, 0x0a0001).w(FUNC(twin16_state::cpub_register_w));
+ map(0x400000, 0x403fff).rw(m_video, FUNC(konami_twin16_video_device::spriteram_r), FUNC(konami_twin16_video_device::spriteram_w));
+ map(0x480000, 0x481fff).rw(m_video, FUNC(konami_twin16_video_device::videoram_r<0>), FUNC(konami_twin16_video_device::videoram_w<0>));
+ map(0x482000, 0x483fff).rw(m_video, FUNC(konami_twin16_video_device::videoram_r<1>), FUNC(konami_twin16_video_device::videoram_w<1>));
+ map(0x500000, 0x53ffff).ram().w(FUNC(twin16_state::zipram_w)).share(m_zipram);
map(0x600000, 0x6fffff).rom().region("gfxrom", 0);
- map(0x700000, 0x77ffff).bankr("gfxrombank");
- map(0x780000, 0x79ffff).ram().share("sprite_gfx_ram");
+ map(0x700000, 0x77ffff).bankr(m_gfxrombank);
+ map(0x780000, 0x79ffff).ram().share(m_sprite_gfx_ram);
}
void fround_state::fround_map(address_map &map)
@@ -227,20 +428,20 @@ void fround_state::fround_map(address_map &map)
map(0x040000, 0x043fff).ram().share("comram");
map(0x060000, 0x063fff).ram();
map(0x080000, 0x080fff).rw(m_palette, FUNC(palette_device::read8), FUNC(palette_device::write8)).umask16(0x00ff).share("palette");
- map(0x0a0000, 0x0a0001).portr("SYSTEM").w(FUNC(fround_state::fround_CPU_register_w));
+ map(0x0a0000, 0x0a0001).portr("SYSTEM").w(FUNC(fround_state::fround_cpu_register_w));
map(0x0a0002, 0x0a0003).portr("P1");
map(0x0a0004, 0x0a0005).portr("P2");
map(0x0a0009, 0x0a0009).w("soundlatch", FUNC(generic_latch_8_device::write));
map(0x0a0010, 0x0a0011).portr("DSW2").w("watchdog", FUNC(watchdog_timer_device::reset16_w));
map(0x0a0012, 0x0a0013).portr("DSW1");
map(0x0a0018, 0x0a0019).portr("DSW3");
- map(0x0c0000, 0x0c000f).w(FUNC(fround_state::video_register_w));
- map(0x0c000e, 0x0c000f).r(FUNC(fround_state::sprite_status_r));
+ map(0x0c0000, 0x0c000f).w(m_video, FUNC(konami_twin16_video_device::video_register_w));
+ map(0x0c000e, 0x0c000f).r(m_video, FUNC(konami_twin16_video_device::sprite_status_r));
map(0x0e0000, 0x0e0001).w(FUNC(fround_state::gfx_bank_w));
- map(0x100000, 0x103fff).ram().w(FUNC(fround_state::fixram_w)).share("fixram");
- map(0x120000, 0x121fff).ram().w(FUNC(fround_state::videoram0_w)).share("videoram.0");
- map(0x122000, 0x123fff).ram().w(FUNC(fround_state::videoram1_w)).share("videoram.1");
- map(0x140000, 0x143fff).ram().share("spriteram");
+ map(0x100000, 0x103fff).rw(m_video, FUNC(konami_twin16_video_device::fixram_r), FUNC(konami_twin16_video_device::fixram_w));
+ map(0x120000, 0x121fff).rw(m_video, FUNC(konami_twin16_video_device::videoram_r<0>), FUNC(konami_twin16_video_device::videoram_w<0>));
+ map(0x122000, 0x123fff).rw(m_video, FUNC(konami_twin16_video_device::videoram_r<1>), FUNC(konami_twin16_video_device::videoram_w<1>));
+ map(0x140000, 0x143fff).rw(m_video, FUNC(konami_twin16_video_device::spriteram_r), FUNC(konami_twin16_video_device::spriteram_w));
map(0x500000, 0x5fffff).rom().region("tiles", 0);
map(0x600000, 0x6fffff).rom().region("gfxrom", 0);
}
@@ -623,16 +824,16 @@ GFXDECODE_END
void twin16_state::volume_callback(uint8_t data)
{
- m_k007232->set_volume(0,(data >> 4) * 0x11,0);
- m_k007232->set_volume(1,0,(data & 0x0f) * 0x11);
+ m_k007232->set_volume(0, (data >> 4) * 0x11, 0);
+ m_k007232->set_volume(1, 0, (data & 0x0f) * 0x11);
}
/* Machine Drivers */
void twin16_state::machine_reset()
{
- m_CPUA_register = 0;
- m_CPUB_register = 0;
+ m_cpua_register = 0;
+ m_cpub_register = 0;
m_upd7759->reset_w(0);
m_upd7759->start_w(1);
@@ -640,9 +841,32 @@ void twin16_state::machine_reset()
void twin16_state::machine_start()
{
+ if (m_gfxrombank.found())
+ {
+ m_gfxrombank->configure_entries(0, 2, memregion("gfxrom")->base() + 0x100000, 0x80000);
+ m_gfxrombank->set_entry(0);
+ }
+
/* register for savestates */
- save_item(NAME(m_CPUA_register));
- save_item(NAME(m_CPUB_register));
+ save_item(NAME(m_cpua_register));
+ save_item(NAME(m_cpub_register));
+}
+
+void fround_state::machine_start()
+{
+ twin16_state::machine_start();
+ save_item(NAME(m_gfx_bank));
+}
+
+void cuebrickj_state::machine_start()
+{
+ twin16_state::machine_start();
+
+ m_nvram_bank->configure_entries(0, 0x20, m_nvram, 0x400);
+
+ subdevice<nvram_device>("nvram")->set_base(m_nvram, sizeof(m_nvram));
+
+ save_item(NAME(m_nvram));
}
void twin16_state::twin16(machine_config &config)
@@ -662,16 +886,17 @@ void twin16_state::twin16(machine_config &config)
WATCHDOG_TIMER(config, "watchdog");
// video hardware
- BUFFERED_SPRITERAM16(config, m_spriteram);
+ KONAMI_TWIN16_VIDEO(config, m_video, XTAL(18'432'000), m_palette, gfx_twin16);
+ m_video->set_screen(m_screen);
+ m_video->virq_callback().set(FUNC(twin16_state::virq_callback));
+ m_video->set_sprite_callback(FUNC(twin16_state::sprite_callback));
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_raw(XTAL(18'432'000)/3, 384, 0, 40*8, 264, 2*8, 30*8);
- m_screen->set_screen_update(FUNC(twin16_state::screen_update_twin16));
- m_screen->screen_vblank().set(FUNC(twin16_state::screen_vblank_twin16));
+ m_screen->set_screen_update(m_video, FUNC(konami_twin16_video_device::screen_update));
+ m_screen->screen_vblank().set(m_video, FUNC(konami_twin16_video_device::screen_vblank));
m_screen->set_palette(m_palette);
- GFXDECODE(config, m_gfxdecode, m_palette, gfx_twin16);
-
PALETTE(config, m_palette).set_format(palette_device::xBGR_555, 1024);
m_palette->set_membits(8);
m_palette->enable_shadows();
@@ -685,10 +910,8 @@ void twin16_state::twin16(machine_config &config)
K007232(config, m_k007232, XTAL(3'579'545));
m_k007232->port_write().set(FUNC(twin16_state::volume_callback));
- m_k007232->add_route(0, "speaker", 0.12, 0); // estimated with gradius2 OST
- m_k007232->add_route(0, "speaker", 0.12, 1);
- m_k007232->add_route(1, "speaker", 0.12, 0);
- m_k007232->add_route(1, "speaker", 0.12, 1);
+ m_k007232->add_route(ALL_OUTPUTS, "speaker", 0.12, 0); // estimated with gradius2 OST
+ m_k007232->add_route(ALL_OUTPUTS, "speaker", 0.12, 1);
UPD7759(config, m_upd7759);
m_upd7759->add_route(ALL_OUTPUTS, "speaker", 0.20, 0);
@@ -715,16 +938,18 @@ void fround_state::fround(machine_config &config)
WATCHDOG_TIMER(config, "watchdog");
/* video hardware */
- BUFFERED_SPRITERAM16(config, m_spriteram);
+ KONAMI_TWIN16_VIDEO(config, m_video, XTAL(18'432'000), m_palette, gfx_fround);
+ m_video->set_screen(m_screen);
+ m_video->virq_callback().set(FUNC(fround_state::virq_callback));
+ m_video->set_sprite_callback(FUNC(fround_state::fround_sprite_callback));
+ m_video->set_tile_callback(FUNC(fround_state::tile_callback));
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_raw(XTAL(18'432'000)/3, 384, 0, 40*8, 264, 2*8, 30*8);
- m_screen->set_screen_update(FUNC(twin16_state::screen_update_twin16));
- m_screen->screen_vblank().set(FUNC(twin16_state::screen_vblank_twin16));
+ m_screen->set_screen_update(m_video, FUNC(konami_twin16_video_device::screen_update));
+ m_screen->screen_vblank().set(m_video, FUNC(konami_twin16_video_device::screen_vblank));
m_screen->set_palette(m_palette);
- GFXDECODE(config, m_gfxdecode, m_palette, gfx_fround);
-
PALETTE(config, m_palette).set_format(palette_device::xBGR_555, 1024);
m_palette->set_membits(8);
m_palette->enable_shadows();
@@ -737,11 +962,9 @@ void fround_state::fround(machine_config &config)
YM2151(config, "ymsnd", XTAL(3'579'545)).add_route(0, "speaker", 1.0, 0).add_route(1, "speaker", 1.0, 1);
K007232(config, m_k007232, XTAL(3'579'545));
- m_k007232->port_write().set(FUNC(twin16_state::volume_callback));
- m_k007232->add_route(0, "speaker", 0.12, 0);
- m_k007232->add_route(0, "speaker", 0.12, 1);
- m_k007232->add_route(1, "speaker", 0.12, 0);
- m_k007232->add_route(1, "speaker", 0.12, 1);
+ m_k007232->port_write().set(FUNC(fround_state::volume_callback));
+ m_k007232->add_route(ALL_OUTPUTS, "speaker", 0.12, 0);
+ m_k007232->add_route(ALL_OUTPUTS, "speaker", 0.12, 1);
UPD7759(config, m_upd7759);
m_upd7759->add_route(ALL_OUTPUTS, "speaker", 0.20, 0);
@@ -756,9 +979,8 @@ void twin16_state::miaj(machine_config &config)
void cuebrickj_state::cuebrickj(machine_config &config)
{
- twin16(config);
+ miaj(config);
m_maincpu->set_addrmap(AS_PROGRAM, &cuebrickj_state::cuebrickj_main_map);
- m_screen->set_raw(XTAL(18'432'000)/3, 384, 1*8, 39*8, 264, 2*8, 30*8);
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
}
@@ -1233,54 +1455,26 @@ ROM_START( cuebrickj )
// unpopulated
ROM_END
-/* Driver Initialization */
-
-void twin16_state::init_twin16()
-{
- m_is_fround = false;
- m_gfxrombank->configure_entries(0, 2, memregion("gfxrom")->base() + 0x100000, 0x80000);
- m_gfxrombank->set_entry(0);
-}
-
-void fround_state::init_fround()
-{
- m_is_fround = true;
-}
-
-void cuebrickj_state::nvram_bank_w(uint8_t data)
-{
- membank("nvrambank")->set_entry(data);
-}
-
-void cuebrickj_state::init_cuebrickj()
-{
- init_twin16();
-
- membank("nvrambank")->configure_entries(0, 0x20, m_nvram, 0x400);
-
- subdevice<nvram_device>("nvram")->set_base(m_nvram, sizeof(m_nvram));
-
- save_item(NAME(m_nvram));
-}
+} // anonymous namespace
/* Game Drivers */
-// YEAR, NAME, PARENT, MACHINE, INPUT, STATE, INIT, MONITOR,COMPANY, FULLNAME,FLAGS
-GAME( 1987, devilw, 0, devilw, devilw, twin16_state, init_twin16, ROT0, "Konami", "Devil World", MACHINE_SUPPORTS_SAVE )
-GAME( 1987, majuu, devilw, devilw, devilw, twin16_state, init_twin16, ROT0, "Konami", "Majuu no Ohkoku", MACHINE_SUPPORTS_SAVE )
-GAME( 1987, darkadv, devilw, devilw, darkadv, twin16_state, init_twin16, ROT0, "Konami", "Dark Adventure", MACHINE_SUPPORTS_SAVE )
+// YEAR, NAME, PARENT, MACHINE, INPUT, STATE, INIT, MONITOR,COMPANY, FULLNAME, FLAGS
+GAME( 1987, devilw, 0, devilw, devilw, twin16_state, empty_init, ROT0, "Konami", "Devil World", MACHINE_SUPPORTS_SAVE )
+GAME( 1987, majuu, devilw, devilw, devilw, twin16_state, empty_init, ROT0, "Konami", "Majuu no Ohkoku (Japan)", MACHINE_SUPPORTS_SAVE )
+GAME( 1987, darkadv, devilw, devilw, darkadv, twin16_state, empty_init, ROT0, "Konami", "Dark Adventure", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, vulcan, 0, twin16, vulcan, twin16_state, init_twin16, ROT0, "Konami", "Vulcan Venture (new)", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, vulcana, vulcan, twin16, vulcan, twin16_state, init_twin16, ROT0, "Konami", "Vulcan Venture (old)", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, vulcanb, vulcan, twin16, vulcan, twin16_state, init_twin16, ROT0, "Konami", "Vulcan Venture (older)", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, gradius2, vulcan, twin16, gradius2, twin16_state, init_twin16, ROT0, "Konami", "Gradius II: GOFER no Yabou (Japan, new)", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, gradius2a, vulcan, twin16, vulcan, twin16_state, init_twin16, ROT0, "Konami", "Gradius II: GOFER no Yabou (Japan, old)", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, gradius2b, vulcan, twin16, vulcan, twin16_state, init_twin16, ROT0, "Konami", "Gradius II: GOFER no Yabou (Japan, older)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, vulcan, 0, twin16, vulcan, twin16_state, empty_init, ROT0, "Konami", "Vulcan Venture (new)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, vulcana, vulcan, twin16, vulcan, twin16_state, empty_init, ROT0, "Konami", "Vulcan Venture (old)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, vulcanb, vulcan, twin16, vulcan, twin16_state, empty_init, ROT0, "Konami", "Vulcan Venture (older)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, gradius2, vulcan, twin16, gradius2, twin16_state, empty_init, ROT0, "Konami", "Gradius II: GOFER no Yabou (Japan, new)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, gradius2a, vulcan, twin16, vulcan, twin16_state, empty_init, ROT0, "Konami", "Gradius II: GOFER no Yabou (Japan, old)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, gradius2b, vulcan, twin16, vulcan, twin16_state, empty_init, ROT0, "Konami", "Gradius II: GOFER no Yabou (Japan, older)", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, fround, 0, fround, fround, fround_state, init_fround, ROT0, "Konami", "The Final Round (version M)", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, froundl, fround, fround, fround, fround_state, init_fround, ROT0, "Konami", "The Final Round (version L)", MACHINE_SUPPORTS_SAVE )
-GAME( 1988, hpuncher, fround, twin16, fround, twin16_state, init_twin16, ROT0, "Konami", "Hard Puncher (Japan)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, fround, 0, fround, fround, fround_state, empty_init, ROT0, "Konami", "The Final Round (version M)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, froundl, fround, fround, fround, fround_state, empty_init, ROT0, "Konami", "The Final Round (version L)", MACHINE_SUPPORTS_SAVE )
+GAME( 1988, hpuncher, fround, twin16, fround, twin16_state, empty_init, ROT0, "Konami", "Hard Puncher (Japan)", MACHINE_SUPPORTS_SAVE )
-GAME( 1989, miaj, mia, miaj, miaj, twin16_state, init_twin16, ROT0, "Konami", "M.I.A.: Missing in Action (Japan, version R)", MACHINE_SUPPORTS_SAVE )
+GAME( 1989, miaj, mia, miaj, miaj, twin16_state, empty_init, ROT0, "Konami", "M.I.A.: Missing in Action (Japan, version R)", MACHINE_SUPPORTS_SAVE )
-GAME( 1989, cuebrickj, cuebrick, cuebrickj, cuebrickj, cuebrickj_state, init_cuebrickj, ROT0, "Konami", "Cue Brick (Japan)", MACHINE_SUPPORTS_SAVE )
+GAME( 1989, cuebrickj, cuebrick, cuebrickj, cuebrickj, cuebrickj_state, empty_init, ROT0, "Konami", "Cue Brick (Japan)", MACHINE_SUPPORTS_SAVE )
diff --git a/src/mame/konami/twin16.h b/src/mame/konami/twin16.h
deleted file mode 100644
index d5711cf683a..00000000000
--- a/src/mame/konami/twin16.h
+++ /dev/null
@@ -1,154 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Phil Stroffolino
-#ifndef MAME_KONAMI_TWIN16_H
-#define MAME_KONAMI_TWIN16_H
-
-#pragma once
-
-#include "video/bufsprite.h"
-#include "sound/upd7759.h"
-#include "sound/k007232.h"
-#include "emupal.h"
-#include "screen.h"
-#include "tilemap.h"
-
-class twin16_state : public driver_device
-{
-public:
- twin16_state(const machine_config &mconfig, device_type type, const char *tag) :
- driver_device(mconfig, type, tag),
- m_maincpu(*this, "maincpu"),
- m_subcpu(*this, "sub"),
- m_audiocpu(*this, "audiocpu"),
- m_k007232(*this, "k007232"),
- m_upd7759(*this, "upd"),
- m_gfxdecode(*this, "gfxdecode"),
- m_screen(*this, "screen"),
- m_palette(*this, "palette"),
- m_spriteram(*this, "spriteram"),
- m_gfxrombank(*this, "gfxrombank"),
- m_fixram(*this, "fixram"),
- m_videoram(*this, "videoram.%u", 0),
- m_zipram(*this, "zipram"),
- m_sprite_gfx_ram(*this, "sprite_gfx_ram"),
- m_gfxrom(*this, "gfxrom")
- { }
-
- void devilw(machine_config &config);
- void miaj(machine_config &config);
- void twin16(machine_config &config);
-
- void init_twin16();
-
- void volume_callback(uint8_t data);
- void screen_vblank_twin16(int state);
- uint32_t screen_update_twin16(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
-
-protected:
- required_device<cpu_device> m_maincpu;
- optional_device<cpu_device> m_subcpu;
- required_device<cpu_device> m_audiocpu;
- required_device<k007232_device> m_k007232;
- required_device<upd7759_device> m_upd7759;
- required_device<gfxdecode_device> m_gfxdecode;
- required_device<screen_device> m_screen;
- required_device<palette_device> m_palette;
- required_device<buffered_spriteram16_device> m_spriteram;
- optional_memory_bank m_gfxrombank;
- required_shared_ptr<uint16_t> m_fixram;
- required_shared_ptr_array<uint16_t, 2> m_videoram;
- optional_shared_ptr<uint16_t> m_zipram;
- optional_shared_ptr<uint16_t> m_sprite_gfx_ram;
- required_region_ptr<uint16_t> m_gfxrom;
-
- uint16_t m_CPUA_register = 0;
- uint16_t m_CPUB_register = 0;
- bool m_is_fround = false;
- uint16_t m_sprite_buffer[0x800] = { };
- emu_timer *m_sprite_timer = nullptr;
- int m_sprite_busy = 0;
- int m_need_process_spriteram = 0;
- uint16_t m_scrollx[3] = { };
- uint16_t m_scrolly[3] = { };
- uint16_t m_video_register = 0;
- tilemap_t *m_fixed_tmap = nullptr;
- tilemap_t *m_scroll_tmap[2] = { };
-
- void CPUA_register_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
- void CPUB_register_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
-
- uint16_t sprite_status_r();
- void video_register_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
- void fixram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
- void videoram0_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
- void videoram1_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
- void zipram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
-
- uint8_t upd_busy_r();
- void upd_reset_w(uint8_t data);
- void upd_start_w(uint8_t data);
-
- TILE_GET_INFO_MEMBER(fix_tile_info);
- TILE_GET_INFO_MEMBER(layer0_tile_info);
- TILE_GET_INFO_MEMBER(layer1_tile_info);
-
- TIMER_CALLBACK_MEMBER(sprite_tick);
-
- void main_map(address_map &map) ATTR_COLD;
- void sound_map(address_map &map) ATTR_COLD;
- void sub_map(address_map &map) ATTR_COLD;
-
- virtual void machine_start() override ATTR_COLD;
- virtual void machine_reset() override ATTR_COLD;
- virtual void video_start() override ATTR_COLD;
-
- virtual void tile_get_info(tile_data &tileinfo, uint16_t data, int color_base);
-
- int set_sprite_timer();
- void spriteram_process();
- void draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
- int spriteram_process_enable();
-};
-
-class fround_state : public twin16_state
-{
-public:
- fround_state(const machine_config &mconfig, device_type type, const char *tag) :
- twin16_state(mconfig, type, tag)
- { }
-
- void fround(machine_config &config);
-
- void init_fround();
-
-private:
- void fround_CPU_register_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
- void gfx_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
-
- void fround_map(address_map &map) ATTR_COLD;
-
- virtual void video_start() override ATTR_COLD;
- virtual void tile_get_info(tile_data &tileinfo, uint16_t data, int color_base) override;
-
- uint8_t m_gfx_bank[4] = { };
-};
-
-class cuebrickj_state : public twin16_state
-{
-public:
- cuebrickj_state(const machine_config &mconfig, device_type type, const char *tag) :
- twin16_state(mconfig, type, tag)
- { }
-
- void cuebrickj(machine_config &config);
-
- void init_cuebrickj();
-
-private:
- void nvram_bank_w(uint8_t data);
-
- void cuebrickj_main_map(address_map &map) ATTR_COLD;
- uint16_t m_nvram[0x400 * 0x20 / 2]{};
-};
-
-#endif // MAME_KONAMI_TWIN16_H
diff --git a/src/mame/konami/twin16_v.cpp b/src/mame/konami/twin16_v.cpp
index 7133f8148c6..2acb0f5a681 100644
--- a/src/mame/konami/twin16_v.cpp
+++ b/src/mame/konami/twin16_v.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Phil Stroffolino
/*
- Konami Twin16 Hardware - Video
+ Konami Twin16 Video subsystem
TODO:
@@ -14,7 +14,15 @@
*/
#include "emu.h"
-#include "twin16.h"
+#include "twin16_v.h"
+
+#include <algorithm>
+
+#define LOG_UNKNOWN (1 << 1)
+
+#define VERBOSE (LOG_UNKNOWN)
+
+#include "logmacro.h"
enum
@@ -35,69 +43,100 @@ enum
TWIN16_SPRITE_OCCUPIED = 0x04
};
-
-void twin16_state::fixram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+DEFINE_DEVICE_TYPE(KONAMI_TWIN16_VIDEO, konami_twin16_video_device, "konami_twin16_video", "Konami Twin16 Video Subsystem")
+
+konami_twin16_video_device::konami_twin16_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, KONAMI_TWIN16_VIDEO, tag, owner, clock)
+ , device_video_interface(mconfig, *this, true)
+ , device_gfx_interface(mconfig, *this, nullptr)
+ , m_fixram(*this, "fixram", 0x4000U, ENDIANNESS_BIG)
+ , m_videoram(*this, "videoram_%u", 0U, 0x2000U, ENDIANNESS_BIG)
+ , m_spriteram{*this, "spriteram_%u", 0U, 0x4000U, ENDIANNESS_BIG}
+ , m_sprite_buffer(nullptr)
+ , m_sprite_timer(nullptr)
+ , m_sprite_process_enable(1)
+ , m_sprite_busy(0)
+ , m_need_process_spriteram(0)
+ , m_scrollx{0}
+ , m_scrolly{0}
+ , m_video_register(0)
+ , m_fixed_tmap(nullptr)
+ , m_scroll_tmap{nullptr, nullptr}
+ , m_virq_cb(*this)
+ , m_sprite_cb(*this)
+ , m_tile_cb(*this, DEVICE_SELF, FUNC(konami_twin16_video_device::default_tile))
{
- COMBINE_DATA(&m_fixram[offset]);
- m_fixed_tmap->mark_tile_dirty(offset);
}
-void twin16_state::videoram0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+void konami_twin16_video_device::device_start()
{
- COMBINE_DATA(&m_videoram[0][offset]);
- m_scroll_tmap[0]->mark_tile_dirty(offset);
+ if (!palette().device().started())
+ throw device_missing_dependencies();
+
+ if (!palette().shadows_enabled())
+ fatalerror("%s: palette shadows must be enabled!", machine().describe_context());
+
+ m_sprite_cb.resolve_safe(0);
+ m_tile_cb.resolve_safe(0);
+
+ const size_t spritebuffer_size = 0x1000 / 2;
+ m_sprite_buffer = std::make_unique<uint16_t []>(spritebuffer_size);
+
+ m_fixed_tmap = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(konami_twin16_video_device::fix_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
+ m_scroll_tmap[0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(konami_twin16_video_device::scroll_tile_info<0>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
+ m_scroll_tmap[1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(konami_twin16_video_device::scroll_tile_info<1>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
+
+ m_fixed_tmap->set_transparent_pen(0);
+ m_scroll_tmap[0]->set_transparent_pen(0);
+ m_scroll_tmap[1]->set_transparent_pen(0);
+
+ std::fill_n(&m_sprite_buffer[0], 0x800, uint16_t(~0));
+ m_sprite_timer = timer_alloc(FUNC(konami_twin16_video_device::sprite_tick), this);
+ m_sprite_timer->adjust(attotime::never);
+
+ save_pointer(NAME(m_sprite_buffer), spritebuffer_size);
+ save_item(NAME(m_sprite_busy));
+ save_item(NAME(m_need_process_spriteram));
+ save_item(NAME(m_scrollx));
+ save_item(NAME(m_scrolly));
+ save_item(NAME(m_video_register));
}
-void twin16_state::videoram1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+void konami_twin16_video_device::device_reset()
{
- COMBINE_DATA(&m_videoram[1][offset]);
- m_scroll_tmap[1]->mark_tile_dirty(offset);
+ m_sprite_process_enable = 1;
+ m_sprite_timer->adjust(attotime::never);
+ m_sprite_busy = 0;
}
-void twin16_state::zipram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+void konami_twin16_video_device::sprite_process_enable_w(uint8_t data)
{
- uint16_t old = m_zipram[offset];
- COMBINE_DATA(&m_zipram[offset]);
- if (m_zipram[offset] != old)
- m_gfxdecode->gfx(1)->mark_dirty(offset / 16);
+ const uint8_t old = m_sprite_process_enable;
+ m_sprite_process_enable = data;
+ if ((old == 0) && (m_sprite_process_enable != 0))
+ spriteram_process();
}
-void fround_state::gfx_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+void konami_twin16_video_device::spriteram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
- int changed = 0;
+ COMBINE_DATA(&m_spriteram[0][offset]);
+}
- if (ACCESSING_BITS_0_7)
- {
- int oldbank0 = m_gfx_bank[0];
- int oldbank1 = m_gfx_bank[1];
- m_gfx_bank[0] = (data >> 0) & 0xf;
- m_gfx_bank[1] = (data >> 4) & 0xf;
- changed |= (oldbank0 ^ m_gfx_bank[0]) | (oldbank1 ^ m_gfx_bank[1]);
- }
- if (ACCESSING_BITS_8_15)
- {
- int oldbank2 = m_gfx_bank[2];
- int oldbank3 = m_gfx_bank[3];
- m_gfx_bank[2] = (data >> 8) & 0xf;
- m_gfx_bank[3] = (data >> 12) & 0xf;
- changed |= (oldbank2 ^ m_gfx_bank[2]) | (oldbank3 ^ m_gfx_bank[3]);
- }
- if (changed)
- {
- m_scroll_tmap[0]->mark_all_dirty();
- m_scroll_tmap[1]->mark_all_dirty();
- }
+void konami_twin16_video_device::fixram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_fixram[offset]);
+ m_fixed_tmap->mark_tile_dirty(offset);
}
-void twin16_state::video_register_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+void konami_twin16_video_device::video_register_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
switch (offset)
{
case 0:
{
- int old = m_video_register;
+ const int old = m_video_register;
COMBINE_DATA(&m_video_register);
- int changed = old ^ m_video_register;
+ const int changed = old ^ m_video_register;
if (changed & (TWIN16_SCREEN_FLIPX | TWIN16_SCREEN_FLIPY))
{
int flip = (m_video_register & TWIN16_SCREEN_FLIPX) ? TILEMAP_FLIPX : 0;
@@ -132,7 +171,7 @@ void twin16_state::video_register_w(offs_t offset, uint16_t data, uint16_t mem_m
break;
default:
- logerror("unknown video_register write:%d", data );
+ LOGMASKED(LOG_UNKNOWN, "%s: unknown video_register write %02x: %04x & %04x", machine().describe_context(), offset, data, mem_mask);
break;
}
}
@@ -182,49 +221,48 @@ void twin16_state::video_register_w(offs_t offset, uint16_t data, uint16_t mem_m
* 3 | ------------xxxx | color
*/
-uint16_t twin16_state::sprite_status_r()
+uint16_t konami_twin16_video_device::sprite_status_r()
{
// bit 0: busy, other bits: dunno
return m_sprite_busy;
}
-TIMER_CALLBACK_MEMBER(twin16_state::sprite_tick)
+TIMER_CALLBACK_MEMBER(konami_twin16_video_device::sprite_tick)
{
m_sprite_busy = 0;
}
-int twin16_state::set_sprite_timer()
+int konami_twin16_video_device::set_sprite_timer()
{
if (m_sprite_busy) return 1;
// sprite system busy, maybe a dma? time is guessed, assume 4 scanlines
m_sprite_busy = 1;
- m_sprite_timer->adjust(m_screen->frame_period() / m_screen->height() * 4);
+ m_sprite_timer->adjust(screen().frame_period() / screen().height() * 4);
return 0;
}
-void twin16_state::spriteram_process()
+void konami_twin16_video_device::spriteram_process()
{
- uint16_t *spriteram16 = m_spriteram->live();
- uint16_t dx = m_scrollx[0];
- uint16_t dy = m_scrolly[0];
+ const uint16_t dx = m_scrollx[0];
+ const uint16_t dy = m_scrolly[0];
- const uint16_t *source = &spriteram16[0x0000];
- const uint16_t *finish = &spriteram16[0x1800];
+ const uint16_t *source = &m_spriteram[0][0x0000];
+ const uint16_t *finish = &m_spriteram[0][0x1800];
set_sprite_timer();
- memset(&spriteram16[0x1800], 0xff, 0x800 * sizeof(uint16_t));
+ std::fill_n(&m_spriteram[0][0x1800], 0x800, uint16_t(~0));
- while (source<finish)
+ while (source < finish)
{
- uint16_t priority = source[0];
- if (priority & 0x8000)
+ const uint16_t priority = source[0];
+ if (BIT(priority, 15))
{
- uint16_t *dest = &spriteram16[0x1800|(priority & 0xff) << 2];
+ uint16_t *dest = &m_spriteram[0][0x1800 | ((priority & 0xff) << 2)];
- uint32_t xpos = (0x10000 * source[4]) | source[5];
- uint32_t ypos = (0x10000 * source[6]) | source[7];
+ const uint32_t xpos = (uint32_t(source[4]) << 16) | source[5];
+ const uint32_t ypos = (uint32_t(source[6]) << 16) | source[7];
/* notes on sprite attributes:
@@ -241,11 +279,11 @@ void twin16_state::spriteram_process()
fround, hpuncher, miaj, cuebrickj, don't use the preprocessor.
*/
- uint16_t attributes = 0x8000 | (source[2] & 0x03ff); // scale,size,color
+ const uint16_t attributes = 0x8000 | (source[2] & 0x03ff); // scale,size,color
dest[0] = source[3]; /* gfx data */
- dest[1] = ((xpos>>8) - dx) & 0xffff;
- dest[2] = ((ypos>>8) - dy) & 0xffff;
+ dest[1] = ((xpos >> 8) - dx) & 0xffff;
+ dest[2] = ((ypos >> 8) - dy) & 0xffff;
dest[3] = attributes;
}
source += 0x50/2;
@@ -253,64 +291,33 @@ void twin16_state::spriteram_process()
m_need_process_spriteram = 0;
}
-void twin16_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+void konami_twin16_video_device::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
- const uint16_t *source = 0x1800 + m_spriteram->buffer() + 0x800 - 4;
- const uint16_t *finish = 0x1800 + m_spriteram->buffer();
+ const uint16_t *source = &m_spriteram[1][0x2000 - 4];
+ const uint16_t *finish = &m_spriteram[1][0x1800];
for (; source >= finish; source -= 4)
{
- uint16_t attributes = source[3];
+ const uint16_t attributes = source[3];
uint16_t code = source[0];
- if ((code != 0xffff) && (attributes & 0x8000))
+ if ((code != 0xffff) && BIT(attributes, 15))
{
int xpos = source[1];
int ypos = source[2];
- int pal_base = ((attributes & 0xf) + 0x10) * 16;
- int height = 16 << ((attributes >> 6) & 0x3);
- int width = 16 << ((attributes >> 4) & 0x3);
- const uint16_t *pen_data = nullptr;
- int flipy = attributes & 0x0200;
- int flipx = attributes & 0x0100;
-
- if (m_is_fround)
- {
- /* fround board */
- pen_data = m_gfxrom;
- }
- else
- {
- switch ((code >> 12) & 0x3)
- {
- /* bank select */
- case 0:
- pen_data = m_gfxrom;
- break;
-
- case 1:
- pen_data = m_gfxrom + 0x40000;
- break;
-
- case 2:
- pen_data = m_gfxrom + 0x80000;
- if (code & 0x4000) pen_data += 0x40000;
- break;
-
- case 3:
- pen_data = m_sprite_gfx_ram;
- break;
- }
- code &= 0xfff;
- }
+ const int pal_base = 0x100 + ((attributes & 0xf) << 4);
+ const int height = 16 << ((attributes >> 6) & 0x3);
+ const int width = 16 << ((attributes >> 4) & 0x3);
+ bool flipy = BIT(attributes, 9);
+ bool flipx = BIT(attributes, 8);
/* some code masking */
if ((height & width) == 64) code &= ~8; // gradius2 ending sequence 64*64
else if ((height & width) == 32) code &= ~3; // devilw 32*32
else if ((height | width) == 48) code &= ~1; // devilw 32*16 / 16*32
- pen_data += code * 0x40;
+ uint32_t pen_addr = code << 6;
if (m_video_register & TWIN16_SCREEN_FLIPY)
{
@@ -327,35 +334,46 @@ void twin16_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, con
if (xpos >= 320) xpos -= 65536;
if (ypos >= 256) ypos -= 65536;
+ const int sx_start = flipx ? (xpos + width - 1) : xpos;
+ const int sy_start = flipy ? (ypos + height - 1) : ypos;
+ const int xinc = flipx ? -1 : 1;
+ const int yinc = flipy ? -1 : 1;
+ const int pitch = (width >> 2);
+
/* slow slow slow, but it's ok for now */
- for (int y = 0; y < height; y++, pen_data += width/4)
+ int sy = sy_start;
+ for (int y = 0; y < height; y++, sy += yinc, pen_addr += pitch)
{
- int sy = (flipy) ? (ypos + height - 1 - y) : (ypos + y);
if (sy >= cliprect.min_y && sy <= cliprect.max_y)
{
uint16_t *const dest = &bitmap.pix(sy);
uint8_t *const pdest = &screen.priority().pix(sy);
- for (int x = 0; x < width; x++)
+ int sx = sx_start;
+ uint32_t pen_addr_x = pen_addr;
+ for (int x = 0; x < width; x += 4, pen_addr_x++)
{
- int sx = (flipx) ? (xpos + width - 1 - x) : (xpos + x);
- if (sx >= cliprect.min_x && sx <= cliprect.max_x)
+ uint16_t pen = m_sprite_cb(pen_addr_x);
+ for (int ix = 0; ix < 4; ix++, sx += xinc, pen <<= 4)
{
- uint16_t pen = pen_data[x >> 2] >> ((~x & 3) << 2) & 0xf;
-
- if (pen && !(pdest[sx] & TWIN16_SPRITE_OCCUPIED))
+ if (sx >= cliprect.min_x && sx <= cliprect.max_x)
{
- pdest[sx] |= TWIN16_SPRITE_OCCUPIED;
+ const uint16_t pixel = (pen >> 12) & 0xf;
- if (pen == 0xf) // shadow
+ if (pixel && !(pdest[sx] & TWIN16_SPRITE_OCCUPIED))
{
- if (!(pdest[sx] & TWIN16_BG_NO_SHADOW))
- dest[sx] = m_palette->shadow_table()[dest[sx]];
- }
- else // opaque pixel
- {
- if (!(pdest[sx] & TWIN16_BG_OVER_SPRITES))
- dest[sx] = pal_base + pen;
+ pdest[sx] |= TWIN16_SPRITE_OCCUPIED;
+
+ if (pixel == 0xf) // shadow
+ {
+ if (!(pdest[sx] & TWIN16_BG_NO_SHADOW))
+ dest[sx] = palette().shadow_table()[dest[sx]];
+ }
+ else // opaque pixel
+ {
+ if (!(pdest[sx] & TWIN16_BG_OVER_SPRITES))
+ dest[sx] = pal_base + pixel;
+ }
}
}
}
@@ -367,100 +385,44 @@ void twin16_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, con
}
-TILE_GET_INFO_MEMBER(twin16_state::fix_tile_info)
+TILE_GET_INFO_MEMBER(konami_twin16_video_device::fix_tile_info)
{
- int attr = m_fixram[tile_index];
+ const uint16_t attr = m_fixram[tile_index];
/* fedcba9876543210
-x-------------- yflip
--x------------- xflip
---xxxx--------- color
-------xxxxxxxxx tile number
*/
- int code = attr & 0x1ff;
- int color = (attr >> 9) & 0x0f;
- int flags=0;
+ const uint32_t code = attr & 0x1ff;
+ const uint32_t color = (attr >> 9) & 0x0f;
+ uint8_t flags = 0;
- if (attr & 0x2000) flags |= TILE_FLIPX;
- if (attr & 0x4000) flags |= TILE_FLIPY;
+ if (BIT(attr, 13)) flags |= TILE_FLIPX;
+ if (BIT(attr, 14)) flags |= TILE_FLIPY;
tileinfo.set(0, code, color, flags);
}
-void twin16_state::tile_get_info(tile_data &tileinfo, uint16_t data, int color_base)
+template <unsigned Which>
+TILE_GET_INFO_MEMBER(konami_twin16_video_device::scroll_tile_info)
{
/* fedcba9876543210
xxx------------- color; high bit is also priority over sprites
---xxxxxxxxxxxxx tile number
*/
- int code = (data & 0x1fff);
- int color = color_base + (data >> 13);
- int flags = 0;
- if (m_video_register & TWIN16_TILE_FLIPY) flags |= TILE_FLIPY;
- tileinfo.set(1, code, color, flags);
- tileinfo.category = BIT(data, 15);
-}
+ const uint16_t data = m_videoram[Which][tile_index];
+ const uint32_t code = m_tile_cb(data & 0x1fff);
+ const uint32_t color = (Which << 3) + (data >> 13);
+ uint8_t flags = 0;
+ if (m_video_register & TWIN16_TILE_FLIPY)
+ flags |= TILE_FLIPY;
-void fround_state::tile_get_info(tile_data &tileinfo, uint16_t data, int color_base)
-{
- /* fedcba9876543210
- xxx------------- color; high bit is also priority over sprites
- ---xx----------- tile bank
- -----xxxxxxxxxxx tile number
- */
- int bank = (data >> 11) & 3;
- int code = (m_gfx_bank[bank] << 11) | (data & 0x7ff);
- int color = color_base | (data >> 13);
- int flags = 0;
- if (m_video_register & TWIN16_TILE_FLIPY) flags |= TILE_FLIPY;
tileinfo.set(1, code, color, flags);
tileinfo.category = BIT(data, 15);
}
-TILE_GET_INFO_MEMBER(twin16_state::layer0_tile_info)
-{
- tile_get_info(tileinfo, m_videoram[0][tile_index], 0);
-}
-
-TILE_GET_INFO_MEMBER(twin16_state::layer1_tile_info)
-{
- tile_get_info(tileinfo, m_videoram[1][tile_index], 8);
-}
-
-void twin16_state::video_start()
-{
- m_fixed_tmap = &machine().tilemap().create(*m_gfxdecode,tilemap_get_info_delegate(*this, FUNC(twin16_state::fix_tile_info)), TILEMAP_SCAN_ROWS, 8,8, 64,32);
- m_scroll_tmap[0] = &machine().tilemap().create(*m_gfxdecode,tilemap_get_info_delegate(*this, FUNC(twin16_state::layer0_tile_info)), TILEMAP_SCAN_ROWS, 8,8, 64,64);
- m_scroll_tmap[1] = &machine().tilemap().create(*m_gfxdecode,tilemap_get_info_delegate(*this, FUNC(twin16_state::layer1_tile_info)), TILEMAP_SCAN_ROWS, 8,8, 64,64);
-
- m_fixed_tmap->set_transparent_pen(0);
- m_scroll_tmap[0]->set_transparent_pen(0);
- m_scroll_tmap[1]->set_transparent_pen(0);
-
- m_palette->set_shadow_factor(0.4); // screenshots estimate
-
- memset(m_sprite_buffer, 0xff, 0x800 * sizeof(uint16_t));
- m_video_register = 0;
- m_sprite_busy = 0;
- m_sprite_timer = timer_alloc(FUNC(twin16_state::sprite_tick),this);
- m_sprite_timer->adjust(attotime::never);
-
- /* register for savestates */
- save_item(NAME(m_sprite_buffer));
- save_item(NAME(m_scrollx));
- save_item(NAME(m_scrolly));
-
- save_item(NAME(m_need_process_spriteram));
- save_item(NAME(m_video_register));
- save_item(NAME(m_sprite_busy));
-}
-
-void fround_state::video_start()
-{
- twin16_state::video_start();
- save_item(NAME(m_gfx_bank));
-}
-
-uint32_t twin16_state::screen_update_twin16(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+uint32_t konami_twin16_video_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
/*
PAL equations (007789 @ 11J):
@@ -534,31 +496,31 @@ uint32_t twin16_state::screen_update_twin16(screen_device &screen, bitmap_ind16
return 0;
}
-void twin16_state::screen_vblank_twin16(int state)
+void konami_twin16_video_device::screen_vblank(int state)
{
// rising edge
if (state)
{
set_sprite_timer();
- if (spriteram_process_enable())
+ if (m_sprite_process_enable)
{
- if (m_need_process_spriteram) spriteram_process();
+ if (m_need_process_spriteram)
+ spriteram_process();
m_need_process_spriteram = 1;
/* if the sprite preprocessor is used, sprite ram is copied to an external buffer first,
as evidenced by 1-frame sprite lag in gradius2 and devilw otherwise, though there's probably
more to it than that */
- memcpy(&m_spriteram->buffer()[0x1800], m_sprite_buffer, 0x800 * sizeof(uint16_t));
- memcpy(m_sprite_buffer, &m_spriteram->live()[0x1800], 0x800 * sizeof(uint16_t));
+ std::copy_n(&m_sprite_buffer[0], 0x800, &m_spriteram[1][0x1800]);
+ std::copy_n(&m_spriteram[0][0x1800], 0x800, &m_sprite_buffer[0]);
}
else
- m_spriteram->copy();
+ {
+ std::copy_n(&m_spriteram[0][0], 0x2000, &m_spriteram[1][0]);
+ }
// IRQ generation
- if (m_CPUA_register & 0x20)
- m_maincpu->set_input_line(5, HOLD_LINE);
- if (m_subcpu.found() && (m_CPUB_register & 0x02))
- m_subcpu->set_input_line(5, HOLD_LINE);
+ m_virq_cb(ASSERT_LINE);
}
}
diff --git a/src/mame/konami/twin16_v.h b/src/mame/konami/twin16_v.h
new file mode 100644
index 00000000000..a2872bf9400
--- /dev/null
+++ b/src/mame/konami/twin16_v.h
@@ -0,0 +1,101 @@
+// license:BSD-3-Clause
+// copyright-holders:Phil Stroffolino
+/*
+ Konami Twin16 Video subsystem
+*/
+#ifndef MAME_KONAMI_TWIN16_V_H
+#define MAME_KONAMI_TWIN16_V_H
+
+#pragma once
+
+#include "screen.h"
+#include "tilemap.h"
+
+class konami_twin16_video_device : public device_t, public device_video_interface, public device_gfx_interface
+{
+public:
+ // TODO: probably cleaner to implemen these as address spaces where the driver can install ROM/RAM rather than delegates
+ using sprite_cb_delegate = device_delegate<uint16_t (int addr)>;
+ using tile_cb_delegate = device_delegate<uint32_t (uint16_t data)>;
+
+ // constructor/destructor
+ konami_twin16_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ template <typename T> konami_twin16_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&palette_tag, const gfx_decode_entry *gfxinfo)
+ : konami_twin16_video_device(mconfig, tag, owner, clock)
+ {
+ set_info(gfxinfo);
+ set_palette(std::forward<T>(palette_tag));
+ }
+
+ // configurations
+ auto virq_callback() { return m_virq_cb.bind(); }
+ template <typename... T> void set_sprite_callback(T &&... args) { m_sprite_cb.set(std::forward<T>(args)...); }
+ template <typename... T> void set_tile_callback(T &&... args) { m_tile_cb.set(std::forward<T>(args)...); }
+
+ // read/write handlers
+ void sprite_process_enable_w(uint8_t data);
+ uint16_t sprite_status_r();
+ void video_register_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+
+ uint16_t spriteram_r(offs_t offset) { return m_spriteram[0][offset]; }
+ uint16_t fixram_r(offs_t offset) { return m_fixram[offset]; }
+ template <unsigned Which> uint16_t videoram_r(offs_t offset) { return m_videoram[Which][offset]; }
+ void spriteram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+ void fixram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+ template <unsigned Which> void videoram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0)
+ {
+ COMBINE_DATA(&m_videoram[Which][offset]);
+ m_scroll_tmap[Which]->mark_tile_dirty(offset);
+ }
+
+ // accessors
+ void mark_scroll_dirty()
+ {
+ m_scroll_tmap[0]->mark_all_dirty();
+ m_scroll_tmap[1]->mark_all_dirty();
+ }
+
+ void spriteram_process();
+
+ void screen_vblank(int state);
+ uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+private:
+ memory_share_creator<uint16_t> m_fixram;
+ memory_share_array_creator<uint16_t, 2> m_videoram;
+
+ memory_share_array_creator<uint16_t, 2> m_spriteram;
+ std::unique_ptr<uint16_t []> m_sprite_buffer;
+
+ emu_timer *m_sprite_timer;
+ uint8_t m_sprite_process_enable;
+ uint8_t m_sprite_busy;
+ uint8_t m_need_process_spriteram;
+ uint16_t m_scrollx[3];
+ uint16_t m_scrolly[3];
+ uint16_t m_video_register;
+ tilemap_t *m_fixed_tmap;
+ tilemap_t *m_scroll_tmap[2];
+
+ devcb_write_line m_virq_cb;
+ sprite_cb_delegate m_sprite_cb;
+ tile_cb_delegate m_tile_cb;
+
+ TILE_GET_INFO_MEMBER(fix_tile_info);
+ template <unsigned Which> TILE_GET_INFO_MEMBER(scroll_tile_info);
+ uint32_t default_tile(uint16_t data) { return data; }
+
+ TIMER_CALLBACK_MEMBER(sprite_tick);
+
+ int set_sprite_timer();
+ void draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ bool spriteram_process_enable();
+};
+
+DECLARE_DEVICE_TYPE(KONAMI_TWIN16_VIDEO, konami_twin16_video_device)
+
+#endif // MAME_KONAMI_TWIN16_V_H