diff --git a/hash/snes.xml b/hash/snes.xml
index 61385ea032f..ca64b570696 100644
--- a/hash/snes.xml
+++ b/hash/snes.xml
@@ -15397,7 +15397,7 @@ more investigation needed...
-
+
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index 1ef192b9890..1b5484abf58 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -3675,6 +3675,8 @@ if (BUSES["SNES"]~=null) then
MAME_DIR .. "src/devices/bus/snes/sgb.h",
MAME_DIR .. "src/devices/bus/snes/spc7110.cpp",
MAME_DIR .. "src/devices/bus/snes/spc7110.h",
+ MAME_DIR .. "src/devices/bus/snes/st018.cpp",
+ MAME_DIR .. "src/devices/bus/snes/st018.h",
MAME_DIR .. "src/devices/bus/snes/sufami.cpp",
MAME_DIR .. "src/devices/bus/snes/sufami.h",
MAME_DIR .. "src/devices/bus/snes/upd.cpp",
diff --git a/src/devices/bus/snes/snes_carts.cpp b/src/devices/bus/snes/snes_carts.cpp
index 228c340fac4..f29610c20e2 100644
--- a/src/devices/bus/snes/snes_carts.cpp
+++ b/src/devices/bus/snes/snes_carts.cpp
@@ -20,6 +20,7 @@
#include "sfx.h"
#include "sgb.h"
#include "spc7110.h"
+#include "st018.h"
#include "sufami.h"
#include "upd.h"
@@ -40,7 +41,7 @@ void snes_cart(device_slot_interface &device)
device.option_add_internal("lorom_sgb2", SNS_LOROM_SUPERGB2); // SuperGB2 base cart - unsupported
device.option_add_internal("lorom_st010", SNS_LOROM_SETA10);
device.option_add_internal("lorom_st011", SNS_LOROM_SETA11);
- device.option_add_internal("lorom_st018", SNS_LOROM); // Cart + ST018 - unsupported
+ device.option_add_internal("lorom_st018", SNS_LOROM_ST018);
device.option_add_internal("lorom_sufami", SNS_LOROM_SUFAMI); // Sufami Turbo base cart
device.option_add_internal("hirom", SNS_HIROM);
device.option_add_internal("hirom_bsx", SNS_HIROM_BSX); // HiROM + BS-X slot - unsupported
diff --git a/src/devices/bus/snes/st018.cpp b/src/devices/bus/snes/st018.cpp
new file mode 100644
index 00000000000..adc15a921d0
--- /dev/null
+++ b/src/devices/bus/snes/st018.cpp
@@ -0,0 +1,161 @@
+// license:BSD-3-Clause
+// copyright-holders:cam900
+/***********************************************************************************************************
+
+ ARMv3 add-on chip emulation (for SNES/SFC)
+ used in carts with ST-018 add-on chips
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "st018.h"
+
+
+// helpers
+inline uint32_t get_prg(uint8_t *CPU, uint32_t addr)
+{
+ return (CPU[addr * 4] | (CPU[addr * 4 + 1] << 8) | (CPU[addr * 4 + 2] << 16) | (CPU[addr * 4 + 3] << 24));
+}
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+DEFINE_DEVICE_TYPE(SNS_LOROM_ST018, sns_rom_st018_device, "sns_rom_st018", "SNES Cart (LoROM) + Seta ST018")
+
+
+sns_rom_st018_device::sns_rom_st018_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sns_rom_device(mconfig, SNS_LOROM_ST018, tag, owner, clock)
+ , m_copro(*this, "copro")
+ , m_cpu2copro(*this, "cpu2copro")
+ , m_copro2cpu(*this, "copro2cpu")
+ , m_signal(false)
+ , m_copro_reset(false)
+{
+}
+
+void sns_rom_st018_device::device_start()
+{
+ m_copro_prg.resize(0x20000/sizeof(uint32_t));
+ m_copro_data.resize(0x8000/sizeof(uint32_t));
+
+ save_item(NAME(m_signal));
+ save_item(NAME(m_copro_reset));
+}
+
+void sns_rom_st018_device::device_reset()
+{
+ m_signal = false;
+ m_copro_reset = false;
+ m_copro->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
+}
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+//-------------------------------------------------
+// Seta ST018
+//-------------------------------------------------
+
+// ST018 dump contains prg at offset 0 and data at offset 0x20000
+uint32_t sns_rom_st018_device::copro_prg_r(offs_t offset)
+{
+ return get_prg(&m_bios[0], offset);
+}
+
+uint32_t sns_rom_st018_device::copro_data_r(offs_t offset)
+{
+ return get_prg(&m_bios[0], offset + 0x20000/4);
+}
+
+uint8_t sns_rom_st018_device::status_r()
+{
+ return (m_copro2cpu->pending_r() ? 0x01 : 0) |
+ (m_signal ? 0x04 : 0) |
+ (m_cpu2copro->pending_r() ? 0x08 : 0) |
+ (m_copro_reset ? 0 : 0x80);
+}
+
+void sns_rom_st018_device::signal_w(uint8_t data)
+{
+ m_signal = true;
+}
+
+//-------------------------------------------------
+// copro_map
+//-------------------------------------------------
+
+void sns_rom_st018_device::copro_map(address_map &map)
+{
+ map(0x0000'0000, 0x0001'ffff).r(FUNC(sns_rom_st018_device::copro_prg_r));
+ map(0x4000'0000, 0x4000'0000).w(m_copro2cpu, FUNC(generic_latch_8_device::write));
+ map(0x4000'0010, 0x4000'0010).r(m_cpu2copro, FUNC(generic_latch_8_device::read)).w(FUNC(sns_rom_st018_device::signal_w));
+ map(0x4000'0020, 0x4000'0020).r(FUNC(sns_rom_st018_device::status_r));
+ map(0x4000'0020, 0x4000'002f).nopw(); // Unknown write
+ map(0xa000'0000, 0xa000'7fff).r(FUNC(sns_rom_st018_device::copro_data_r));
+ map(0xe000'0000, 0xe000'3fff).ram();
+}
+
+
+//-------------------------------------------------
+// device_add_mconfig - add device configuration
+//-------------------------------------------------
+
+void sns_rom_st018_device::device_add_mconfig(machine_config &config)
+{
+ ARM7(config, m_copro, 21440000); // TODO: ARMv3
+ m_copro->set_addrmap(AS_PROGRAM, &sns_rom_st018_device::copro_map);
+
+ GENERIC_LATCH_8(config, m_cpu2copro);
+ GENERIC_LATCH_8(config, m_copro2cpu);
+}
+
+uint8_t sns_rom_st018_device::chip_read(offs_t offset)
+{
+ uint8_t ret = 0xff;
+ switch (offset & 0x06)
+ {
+ case 0x00:
+ ret = m_copro2cpu->read();
+ break;
+ case 0x02:
+ if (!machine().side_effects_disabled())
+ m_signal = false;
+ break;
+ case 0x04:
+ ret = status_r();
+ break;
+ }
+ return ret;
+}
+
+
+void sns_rom_st018_device::chip_write(offs_t offset, uint8_t data)
+{
+ switch (offset & 0x06)
+ {
+ case 0x02:
+ m_cpu2copro->write(data);
+ break;
+ case 0x04:
+ m_copro_reset = BIT(data, 0);
+ m_copro->set_input_line(INPUT_LINE_RESET, m_copro_reset ? ASSERT_LINE : CLEAR_LINE);
+ break;
+ }
+}
+
+
+// To make faster DSP access to its internal rom, let's install read banks and map m_bios there with correct byte order
+
+void sns_rom_st018_device::speedup_addon_bios_access()
+{
+ m_copro->space(AS_PROGRAM).install_rom(0x0000'0000, 0x0001'ffff, &m_copro_prg[0]);
+ m_copro->space(AS_PROGRAM).install_rom(0xa000'0000, 0xa000'7fff, &m_copro_data[0]);
+ // copy data in the correct format
+ for (int x = 0; x < 0x8000; x++)
+ m_copro_prg[x] = m_bios[x * 4] | (m_bios[x * 4 + 1] << 8) | (m_bios[x * 4 + 2] << 16) | (m_bios[x * 4 + 3] << 24);
+ for (int x = 0; x < 0x2000; x++)
+ m_copro_data[x] = m_bios[0x20000 + x * 4] | (m_bios[0x20000 + x * 4 + 1] << 8) | (m_bios[0x20000 + x * 4 + 2] << 16) | (m_bios[0x20000 + x * 4 + 3] << 24);
+}
diff --git a/src/devices/bus/snes/st018.h b/src/devices/bus/snes/st018.h
new file mode 100644
index 00000000000..1621c7598d7
--- /dev/null
+++ b/src/devices/bus/snes/st018.h
@@ -0,0 +1,56 @@
+// license:BSD-3-Clause
+// copyright-holders:cam900
+#ifndef MAME_BUS_SNES_ST018_H
+#define MAME_BUS_SNES_ST018_H
+
+#pragma once
+
+#include "snes_slot.h"
+#include "rom.h"
+#include "cpu/arm7/arm7.h"
+#include "machine/gen_latch.h"
+
+// ======================> sns_rom_st018_device
+
+class sns_rom_st018_device : public sns_rom_device
+{
+public:
+ // construction/destruction
+ sns_rom_st018_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ // device-level overrides
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+
+ virtual void speedup_addon_bios_access() override;
+
+ // additional reading and writing
+ virtual uint8_t chip_read(offs_t offset) override;
+ virtual void chip_write(offs_t offset, uint8_t data) override;
+
+private:
+ uint32_t copro_prg_r(offs_t offset);
+ uint32_t copro_data_r(offs_t offset);
+
+ uint8_t status_r();
+ void signal_w(uint8_t data);
+
+ void copro_map(address_map &map) ATTR_COLD;
+
+ required_device m_copro;
+ required_device m_cpu2copro;
+ required_device m_copro2cpu;
+
+ std::vector m_copro_prg;
+ std::vector m_copro_data;
+
+ bool m_signal;
+ bool m_copro_reset;
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(SNS_LOROM_ST018, sns_rom_st018_device)
+
+#endif // MAME_BUS_SNES_ST018_H
diff --git a/src/mame/nintendo/snes.cpp b/src/mame/nintendo/snes.cpp
index 93ad4be6e64..27e32acabb2 100644
--- a/src/mame/nintendo/snes.cpp
+++ b/src/mame/nintendo/snes.cpp
@@ -1200,7 +1200,10 @@ void snes_console_state::machine_start()
case SNES_CX4: // this still uses the old simulation instead of emulating the CPU
case SNES_ST010: // this requires two diff kinds of chip access, so we handle it in snes20_lo/hi_r/w
case SNES_ST011: // this requires two diff kinds of chip access, so we handle it in snes20_lo/hi_r/w
- case SNES_ST018: // still unemulated
+ break;
+ case SNES_ST018:
+ m_maincpu->space(AS_PROGRAM).install_read_handler(0x003800, 0x0038ff, 0, 0xbf0000, 0, read8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_read)));
+ m_maincpu->space(AS_PROGRAM).install_write_handler(0x003800, 0x0038ff, 0, 0xbf0000, 0, write8sm_delegate(*m_cartslot, FUNC(base_sns_cart_slot_device::chip_write)));
break;
case SNES_Z80GB: // skeleton support
m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x000000, 0x7dffff, read8m_delegate(*this, FUNC(snes_console_state::snessgb_lo_r)), write8m_delegate(*this, FUNC(snes_console_state::snessgb_lo_w)));