diff options
| author | 2025-09-10 00:50:32 +0900 | |
|---|---|---|
| committer | 2025-09-10 01:50:32 +1000 | |
| commit | 5ee1f1678c8a025b154dd5cced5b9babdc96c6f3 (patch) | |
| tree | b7e9ea53d16f27ee6d8ba147119b00f968916310 | |
| parent | cb3afa1c44f0449b4287459ee06d57458c10ab20 (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.cpp | 448 | ||||
| -rw-r--r-- | src/mame/konami/twin16.h | 154 | ||||
| -rw-r--r-- | src/mame/konami/twin16_v.cpp | 372 | ||||
| -rw-r--r-- | src/mame/konami/twin16_v.h | 101 |
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 |
