diff --git a/hash/r8_card.xml b/hash/r8_card.xml
new file mode 100644
index 00000000000..9ac5af3f6e9
--- /dev/null
+++ b/hash/r8_card.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+ SN-R8-01 Contemporary Percussion
+ 1989
+ Roland
+
+
+
+
+
+
+
+ SN-R8-02 Jazz Brush
+ 1989
+ Roland
+
+
+
+
+
+
+
diff --git a/hash/u110_card.xml b/hash/u110_card.xml
index babace4334c..2a197a4e4b4 100644
--- a/hash/u110_card.xml
+++ b/hash/u110_card.xml
@@ -12,6 +12,31 @@ Undumped cards:
-> see https://www.playkeyboardnow.com/Roland_D70_Soundbanks/page63/index.html
Release dates were taken from http://www.rolandmuseum.de/z_syn_wc.php
+
+
+address/data line scrambling (same as SN-R8):
+
+ address data
+external internal external internal
+ A0 - - - A0 D0 - - - D2
+ A1 - - - A5 D1 - - - D7
+ A2 - - - A4 D2 - - - D6
+ A3 - - - A6 D3 - - - D4
+ A4 - - - A1 D4 - - - D1
+ A5 - - - A2 D5 - - - D3
+ A6 - - - A3 D6 - - - D0
+ A7 - - - A15 D7 - - - D5
+ A8 - - - A13
+ A9 - - - A10
+ A10 - - - A14
+ A11 - - - A7
+ A12 - - - A12
+ A13 - - - A11
+ A14 - - - A16
+ A15 - - - A9
+ A16 - - - A8
+ A17 - - - A17
+ A18 - - - A18
-->
diff --git a/src/mame/drivers/roland_cm32p.cpp b/src/mame/drivers/roland_cm32p.cpp
index cefe4efe729..70d74112453 100644
--- a/src/mame/drivers/roland_cm32p.cpp
+++ b/src/mame/drivers/roland_cm32p.cpp
@@ -117,6 +117,34 @@ Parts:
|-----------------------------------------------------------------------|
+PCM ROM address/data line scrambling:
+
+ address data
+external internal external internal
+ A0 - - - A0 D0 - - - D2
+ A1 - - - A5 D1 - - - D7
+ A2 - - - A4 D2 - - - D6
+ A3 - - - A6 D3 - - - D4
+ A4 - - - A1 D4 - - - D1
+ A5 - - - A2 D5 - - - D3
+ A6 - - - A3 D6 - - - D0
+ A7 - - - A8 D7 - - - D5
+ A8 - - - A10
+ A9 - - - A13
+ A10 - - - A9
+ A11 - - - A7
+ A12 - - - A11
+ A13 - - - A12
+ A14 - - - A16
+ A15 - - - A14
+ A16 - - - A15
+ A17 - - - A17
+ A18 - - - A18
+
+Line scramling of A0..A6 and D0..D7 matches the SN-U110 cards.
+For A7..A16 they changed the scrambling.
+
+
PCM ROM Tables
--------------
Sample Table (address: 0x00100)
diff --git a/src/mame/drivers/roland_r8.cpp b/src/mame/drivers/roland_r8.cpp
index 9a04f0464bf..eb20bd49efa 100644
--- a/src/mame/drivers/roland_r8.cpp
+++ b/src/mame/drivers/roland_r8.cpp
@@ -1,48 +1,234 @@
// license:BSD-3-Clause
-// copyright-holders:AJR
+// copyright-holders:AJR,Valley Bell
/****************************************************************************
Skeleton driver for Roland R-8 drum machine.
****************************************************************************/
+/*
+PCM Card Sample Table Format
+----------------------------
+Note: 2-byte values are stored in Little Endian.
+
+General Layout
+--------------
+Pos Len Description
+ 80 01 card ID (00 for SN-R8-01, 01 for SN-R8-02, etc.)
+ 81 01 number of tones
+ 82 0E padding (byte 0xFF)
+ 90 70 tone offsets, 2 bytes per offset, relative to 0x100, each value points to the start of a "tone" definition
+ 100 500 tone list
+ 600 200 demo song header?
+ 800 800 ??
+ 1000 2000 demo song data?
+ 3000 7D000 sample data
+
+Tone List (address: 0x100)
+---------
+Pos Len Description
+00 08 tone name
+08 01 ?? (always 0x8F)
+09 01 ??
+0A 01 frequency (0xE0 == 44100 Hz)
+0B 02 ??
+0C 02 sample A bank/flags
+ sample cards:
+ Bit 10 (0x0400) = bank (0 = card offsets 0x00000..0x3FFFF, 1 = card offsets 0x40000..0x7FFFF)
+ R8 internal ROM:
+ Bit 10 (0x0400) = bank bit 0 (0 = offsets 0x00000..0x3FFFF, 1 = offsets 0x40000..0x7FFFF)
+ Bit 12 (0x1000) = bank bit 1 (0 = offsets 0x00000..0x7FFFF, 1 = offsets 0x80000..0xFFFFF)
+ Bits 14-15 (0xC000) - play/loop mode
+ 0 = normal loop
+ 1 = no loop? (not used)
+ 2 = ping-pong 1? (forwards, backwards, forwards, backwards, ...)
+ 3 = ping-pong 2? (backwards, forwards, backwards, forwards, ...)
+ -> when this is used, then the start address is usually set to (end address) or (end address+1)
+0E 02 sample A start address (high word, i.e. address bits 2..17)
+10 02 sample A start address, fraction (2.14 fixed point, i.e. 1 byte = 0x4000)
+12 02 sample A loop address (high word)
+14 02 sample A end address (high word)
+16 02 ??
+18 02 ??
+1A 02 ??
+1C 02 ??
+1E 02 sample B bank and flags?
+20 02 sample B start address (high word)
+22 02 sample B start address, fraction
+24 02 sample B loop address (high word)
+26 02 sample B end address (high word)
+28 02 ??
+2A 02 ??
+2C 02 ??
+2E 02 ??
+-> 30h bytes per entry
+
+"sample A" is what is heard. The use for "sample B" is unknown.
+The 8 unknwon consecutive bytes (0x16..0x1D and 0x28..0x2F) are probably related to the envelope generation.
+
+
+R8/R8M internal tone list offsets: 0x018C10
+R8/R8M internal tone list data: 0x018CA0
+R8 mkII doesn't seem to store the tone list in the program ROM.
+
+*/
+
#include "emu.h"
#include "cpu/upd78k/upd78k2.h"
#include "machine/nvram.h"
#include "sound/rolandpcm.h"
+#include "bus/generic/slot.h"
+#include "bus/generic/carts.h"
#include "speaker.h"
-class roland_r8_state : public driver_device
+// PCM card address line scrambling
+#define UNSCRAMBLE_ADDR_EXT(_offset) \
+ bitswap<19>(_offset,18,17, 8, 9,16,11,12, 7,14,10,13,15, 3, 2, 1, 6, 4, 5, 0)
+
+#define UNSCRAMBLE_DATA(_data) \
+ bitswap<8>(_data,1,2,7,3,5,0,4,6)
+
+// PCM card offsets in PCM chip memory space
+// Right now this is an assumption based on the existing sample tables and wasn't confirmed with the firmware yet.
+static const offs_t PCMCARD_SIZE = 0x080000; // 512 KB
+static const std::array PCMCARD_OFFSETS = {0x080000, 0x180000, 0x280000};
+
+class roland_r8_base_state : public driver_device
{
public:
- roland_r8_state(const machine_config &mconfig, device_type type, const char *tag)
+ roland_r8_base_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_pcm(*this, "pcm")
{
}
- void r8(machine_config &config);
- void r8m(machine_config &config);
- void r8mk2(machine_config &config);
+ void r8_common(machine_config &config);
+ void init_r8();
-private:
+protected:
void mk1_map(address_map &map);
void mk2_map(address_map &map);
+ image_init_result pcmrom_load(generic_slot_device* pcmcard, int card_id, device_image_interface &image);
+ void pcmrom_unload(int card_id);
+ void descramble_rom_external(u8* dst, const u8* src);
+
required_device m_maincpu;
required_device m_pcm;
+ //required_device m_ramcard; // TODO
+};
+
+class roland_r8_state : public roland_r8_base_state
+{
+public:
+ roland_r8_state(const machine_config &mconfig, device_type type, const char *tag)
+ : roland_r8_base_state(mconfig, type, tag)
+ , m_pcmcard(*this, "pcmcard")
+ {
+ }
+
+ void r8(machine_config &config);
+
+private:
+ DEVICE_IMAGE_LOAD_MEMBER(pcmcard_load) { return pcmrom_load(m_pcmcard, 0, image); }
+ DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard_unload) { pcmrom_unload(0); }
+
+ required_device m_pcmcard;
+};
+
+class roland_r8m_state : public roland_r8_base_state
+{
+public:
+ roland_r8m_state(const machine_config &mconfig, device_type type, const char *tag)
+ : roland_r8_base_state(mconfig, type, tag)
+ , m_pcmcards(*this, "pcmcard%u", 0U)
+ {
+ }
+
+ void r8m(machine_config &config);
+
+private:
+ DEVICE_IMAGE_LOAD_MEMBER(pcmcard0_load) { return pcmrom_load(m_pcmcards[0], 0, image); }
+ DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard0_unload) { pcmrom_unload(0); }
+ DEVICE_IMAGE_LOAD_MEMBER(pcmcard1_load) { return pcmrom_load(m_pcmcards[1], 1, image); }
+ DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard1_unload) { pcmrom_unload(1); }
+ DEVICE_IMAGE_LOAD_MEMBER(pcmcard2_load) { return pcmrom_load(m_pcmcards[2], 2, image); }
+ DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard2_unload) { pcmrom_unload(2); }
+
+ required_device_array m_pcmcards;
+};
+
+class roland_r8mk2_state : public roland_r8_base_state
+{
+public:
+ roland_r8mk2_state(const machine_config &mconfig, device_type type, const char *tag)
+ : roland_r8_base_state(mconfig, type, tag)
+ , m_pcmcard(*this, "pcmcard")
+ {
+ }
+
+ void r8mk2(machine_config &config);
+
+private:
+ DEVICE_IMAGE_LOAD_MEMBER(pcmcard_load) { return pcmrom_load(m_pcmcard, 0, image); }
+ DEVICE_IMAGE_UNLOAD_MEMBER(pcmcard_unload) { pcmrom_unload(0); }
+
+ required_device m_pcmcard;
};
-void roland_r8_state::mk1_map(address_map &map)
+image_init_result roland_r8_base_state::pcmrom_load(generic_slot_device* pcmcard, int card_id, device_image_interface &image)
+{
+ uint32_t size = pcmcard->common_get_size("rom");
+ if (size > PCMCARD_SIZE)
+ {
+ image.seterror(image_error::INVALIDIMAGE, "Invalid size: Only up to 512K is supported");
+ return image_init_result::FAIL;
+ }
+
+ pcmcard->rom_alloc(PCMCARD_SIZE, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
+ pcmcard->common_load_rom(pcmcard->get_rom_base(), size, "rom");
+ u8* base = pcmcard->get_rom_base();
+ if (size < PCMCARD_SIZE)
+ {
+ uint32_t mirror = (1 << (31 - count_leading_zeros_32(size)));
+ if (mirror < 0x020000) // due to how address descrambling works, we can currently only do mirroring for 128K pages
+ mirror = 0x020000;
+ for (uint32_t ofs = mirror; ofs < PCMCARD_SIZE; ofs += mirror)
+ memcpy(base + ofs, base, mirror);
+ }
+
+ offs_t pcm_addr = PCMCARD_OFFSETS[card_id];
+ u8* src = static_cast(memregion("pcmorg")->base());
+ u8* dst = static_cast(memregion("pcm")->base());
+ memcpy(&src[pcm_addr], base, PCMCARD_SIZE);
+ // descramble PCM card ROM
+ descramble_rom_external(&dst[pcm_addr], &src[pcm_addr]);
+ //pcmard_loaded[card_id] = true;
+
+ return image_init_result::PASS;
+}
+
+void roland_r8_base_state::pcmrom_unload(int card_id)
+{
+ u8* src = static_cast(memregion("pcmorg")->base());
+ u8* dst = static_cast(memregion("pcm")->base());
+ offs_t pcm_addr = PCMCARD_OFFSETS[card_id];
+ memset(&src[pcm_addr], 0xFF, PCMCARD_SIZE);
+ memset(&dst[pcm_addr], 0xFF, PCMCARD_SIZE);
+ //pcmard_loaded[card_id] = false;
+}
+
+
+void roland_r8_base_state::mk1_map(address_map &map)
{
map(0x00000, 0x1ffff).rom().region("maincpu", 0);
map(0x20000, 0x27fff).ram().share("nvram");
map(0x70000, 0x7001f).rw(m_pcm, FUNC(mb87419_mb87420_device::read), FUNC(mb87419_mb87420_device::write));
}
-void roland_r8_state::mk2_map(address_map &map)
+void roland_r8_base_state::mk2_map(address_map &map)
{
map(0x00000, 0x1ffff).rom().region("maincpu", 0);
map(0x20000, 0x27fff).ram().share("nvram1");
@@ -55,7 +241,7 @@ static INPUT_PORTS_START(r8)
INPUT_PORTS_END
-void roland_r8_state::r8(machine_config &config)
+void roland_r8_base_state::r8_common(machine_config &config)
{
UPD78210(config, m_maincpu, 12_MHz_XTAL);
m_maincpu->set_addrmap(AS_PROGRAM, &roland_r8_state::mk1_map);
@@ -70,23 +256,44 @@ void roland_r8_state::r8(machine_config &config)
MB87419_MB87420(config, m_pcm, 33.8688_MHz_XTAL);
//m_pcm->int_callback().set_inputline(m_maincpu, upd78k2_device::INTP1_LINE);
- m_pcm->set_device_rom_tag("wavedata");
+ m_pcm->set_device_rom_tag("pcm");
m_pcm->add_route(0, "lspeaker", 1.0);
m_pcm->add_route(1, "rspeaker", 1.0);
}
-void roland_r8_state::r8m(machine_config &config)
+void roland_r8_state::r8(machine_config &config)
{
- r8(config);
+ r8_common(config);
- UPD78213(config.replace(), m_maincpu, 12_MHz_XTAL);
- m_maincpu->set_addrmap(AS_PROGRAM, &roland_r8_state::mk1_map);
+ GENERIC_CARTSLOT(config, m_pcmcard, generic_romram_plain_slot, "r8_card", "bin");
+ m_pcmcard->set_device_load(FUNC(roland_r8_state::pcmcard_load));
+ m_pcmcard->set_device_unload(FUNC(roland_r8_state::pcmcard_unload));
+
+ SOFTWARE_LIST(config, "card_list").set_original("r8_card");
}
-void roland_r8_state::r8mk2(machine_config &config)
+void roland_r8m_state::r8m(machine_config &config)
+{
+ r8_common(config);
+
+ UPD78213(config.replace(), m_maincpu, 12_MHz_XTAL);
+ m_maincpu->set_addrmap(AS_PROGRAM, &roland_r8m_state::mk1_map);
+
+ for (auto& pcmcard : m_pcmcards)
+ GENERIC_CARTSLOT(config, pcmcard, generic_romram_plain_slot, "r8_card", "bin");
+ m_pcmcards[0]->set_device_load(FUNC(roland_r8m_state::pcmcard0_load));
+ m_pcmcards[0]->set_device_unload(FUNC(roland_r8m_state::pcmcard0_unload));
+ m_pcmcards[1]->set_device_load(FUNC(roland_r8m_state::pcmcard1_load));
+ m_pcmcards[1]->set_device_unload(FUNC(roland_r8m_state::pcmcard1_unload));
+ m_pcmcards[2]->set_device_load(FUNC(roland_r8m_state::pcmcard2_load));
+ m_pcmcards[2]->set_device_unload(FUNC(roland_r8m_state::pcmcard2_unload));
+ SOFTWARE_LIST(config, "card_list").set_original("r8_card");
+}
+
+void roland_r8mk2_state::r8mk2(machine_config &config)
{
UPD78213(config, m_maincpu, 12_MHz_XTAL);
- m_maincpu->set_addrmap(AS_PROGRAM, &roland_r8_state::mk2_map);
+ m_maincpu->set_addrmap(AS_PROGRAM, &roland_r8mk2_state::mk2_map);
NVRAM(config, "nvram1", nvram_device::DEFAULT_ALL_0); // SRM20256LC-10 + battery
NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0); // SRM20256LC-10 + battery
@@ -99,9 +306,37 @@ void roland_r8_state::r8mk2(machine_config &config)
MB87419_MB87420(config, m_pcm, 33.8688_MHz_XTAL);
//m_pcm->int_callback().set_inputline(m_maincpu, upd78k2_device::INTP1_LINE);
- m_pcm->set_device_rom_tag("wavedata");
+ m_pcm->set_device_rom_tag("pcm");
m_pcm->add_route(0, "lspeaker", 1.0);
m_pcm->add_route(1, "rspeaker", 1.0);
+
+ GENERIC_CARTSLOT(config, m_pcmcard, generic_romram_plain_slot, "r8_card", "bin");
+ m_pcmcard->set_device_load(FUNC(roland_r8mk2_state::pcmcard_load));
+ m_pcmcard->set_device_unload(FUNC(roland_r8mk2_state::pcmcard_unload));
+ SOFTWARE_LIST(config, "card_list").set_original("r8_card");
+}
+
+void roland_r8_base_state::init_r8()
+{
+ u8* src = static_cast(memregion("pcmorg")->base());
+ u8* dst = static_cast(memregion("pcm")->base());
+
+ for (offs_t addr = 0; addr < memregion("pcmorg")->bytes(); addr += 0x100000)
+ {
+ // TODO: descramble internal ROMs
+ memcpy(&dst[addr + 0x00000], &src[addr + 0x00000], 0x80000);
+ // descramble PCM card ROMs
+ descramble_rom_external(&dst[addr + 0x80000], &src[addr + 0x80000]);
+ }
+}
+
+void roland_r8_base_state::descramble_rom_external(u8* dst, const u8* src)
+{
+ for (offs_t srcpos = 0x00; srcpos < PCMCARD_SIZE; srcpos ++)
+ {
+ offs_t dstpos = UNSCRAMBLE_ADDR_EXT(srcpos);
+ dst[dstpos] = UNSCRAMBLE_DATA(src[srcpos]);
+ }
}
@@ -109,31 +344,37 @@ ROM_START(r8)
ROM_REGION(0x20000, "maincpu", 0)
ROM_LOAD("roland r-8_2.02_27c010.bin", 0x00000, 0x20000, CRC(45d0f64f) SHA1(55f0831db74cbdeae20cd7f1ff28af27dafba9b9))
- ROM_REGION(0x100000, "wavedata", ROMREGION_ERASE00)
+ ROM_REGION(0x200000, "pcmorg", ROMREGION_ERASE00) // ROMs before descrambling
ROM_LOAD("r15179929-mn234000rle.ic30", 0x000000, 0x080000, NO_DUMP)
- ROM_LOAD("r15179930-mn234000rlf.ic31", 0x080000, 0x080000, NO_DUMP)
+ // 0x80000..0xFFFFF is reserved for PCM cards, according to the sample list
+ ROM_LOAD("r15179930-mn234000rlf.ic31", 0x100000, 0x080000, NO_DUMP)
+ ROM_REGION(0x200000, "pcm", ROMREGION_ERASE00) // ROMs after descrambling
ROM_END
ROM_START(r8m)
ROM_REGION(0x20000, "maincpu", 0)
ROM_LOAD("rolandr8mv104.bin", 0x00000, 0x20000, CRC(5e95e2f6) SHA1(b4e1a8f15f72a9db9aa8fd41ee3c3ebd10460587))
- ROM_REGION(0x100000, "wavedata", ROMREGION_ERASE00) // same ROMs as R-8 assumed
+ ROM_REGION(0x400000, "pcmorg", ROMREGION_ERASE00) // ROMs before descrambling (must be large enough to allow for 3 PCM cards at offsets 0.5M, 1.5M and 2.5M)
+ // same ROMs as R-8 assumed
ROM_LOAD("r15179929-mn234000rle.bin", 0x000000, 0x080000, NO_DUMP)
- ROM_LOAD("r15179930-mn234000rlf.bin", 0x080000, 0x080000, NO_DUMP)
+ // 0x80000..0xFFFFF is reserved for PCM cards, according to the sample list
+ ROM_LOAD("r15179930-mn234000rlf.bin", 0x100000, 0x080000, NO_DUMP)
+ ROM_REGION(0x400000, "pcm", ROMREGION_ERASE00) // ROMs after descrambling
ROM_END
ROM_START(r8mk2)
ROM_REGION(0x20000, "maincpu", 0)
ROM_LOAD("roland r8 mkii eprom v1.0.3.bin", 0x00000, 0x20000, CRC(128a9a0c) SHA1(94bd8c76efe270754219f2899f31b62fc4f9060d))
- ROM_REGION(0x200000, "wavedata", ROMREGION_ERASE00)
+ ROM_REGION(0x400000, "pcmorg", ROMREGION_ERASE00) // ROMs before descrambling
ROM_LOAD("r15209440-upd27c8001eacz-025.ic30", 0x000000, 0x080000, NO_DUMP)
- ROM_LOAD("r15209441-upd27c8001eacz-026.ic31", 0x080000, 0x080000, NO_DUMP)
- ROM_LOAD("r15209442-upd27c8001eacz-027.ic82", 0x100000, 0x080000, NO_DUMP)
+ ROM_LOAD("r15209441-upd27c8001eacz-026.ic31", 0x100000, 0x080000, NO_DUMP)
+ ROM_LOAD("r15209442-upd27c8001eacz-027.ic82", 0x200000, 0x080000, NO_DUMP)
+ ROM_REGION(0x400000, "pcm", ROMREGION_ERASE00) // ROMs after descrambling
ROM_END
-SYST(1989, r8, 0, 0, r8, r8, roland_r8_state, empty_init, "Roland", "R-8 Human Rhythm Composer (v2.02)", MACHINE_IS_SKELETON)
-SYST(1990, r8m, r8, 0, r8m, r8, roland_r8_state, empty_init, "Roland", "R-8M Total Percussion Sound Module (v1.04)", MACHINE_IS_SKELETON)
-SYST(1992, r8mk2, 0, 0, r8mk2, r8, roland_r8_state, empty_init, "Roland", "R-8 Mk II Human Rhythm Composer (v1.0.3)", MACHINE_IS_SKELETON)
+SYST(1989, r8, 0, 0, r8, r8, roland_r8_state, init_r8, "Roland", "R-8 Human Rhythm Composer (v2.02)", MACHINE_IS_SKELETON)
+SYST(1990, r8m, r8, 0, r8m, r8, roland_r8m_state, init_r8, "Roland", "R-8M Total Percussion Sound Module (v1.04)", MACHINE_IS_SKELETON)
+SYST(1992, r8mk2, 0, 0, r8mk2, r8, roland_r8mk2_state, init_r8, "Roland", "R-8 Mk II Human Rhythm Composer (v1.0.3)", MACHINE_IS_SKELETON)