mirror of
https://github.com/holub/mame
synced 2025-06-06 04:43:45 +03:00
add Roland PCM sound emulation for CM-32P
This commit is contained in:
parent
0626189f0f
commit
539598fab4
@ -1542,6 +1542,18 @@ if (SOUNDS["SWP30"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
-- Roland sample players
|
||||
--@src/devices/sound/rolandpcm.h,SOUNDS["ROLANDPCM"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (SOUNDS["ROLANDPCM"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/sound/rolandpcm.cpp",
|
||||
MAME_DIR .. "src/devices/sound/rolandpcm.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/sound/vgm_visualizer.h,SOUNDS["VGMVIZ"] = true
|
||||
|
@ -303,6 +303,7 @@ SOUNDS["IOPSPU"] = true
|
||||
SOUNDS["SWP00"] = true
|
||||
SOUNDS["SWP20"] = true
|
||||
SOUNDS["SWP30"] = true
|
||||
SOUNDS["ROLANDPCM"] = true
|
||||
|
||||
--------------------------------------------------
|
||||
-- specify available video cores
|
||||
|
376
src/devices/sound/rolandpcm.cpp
Normal file
376
src/devices/sound/rolandpcm.cpp
Normal file
@ -0,0 +1,376 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Valley Bell
|
||||
|
||||
// register write:
|
||||
// 00/01 - ??
|
||||
// 02/03 - ROM bank (bits 10-13) / loop mode (bits 14-15)
|
||||
// 04/05 - frequency (2.14 fixed point, 0x4000 = 32000 Hz)
|
||||
// 06/07 - volume
|
||||
// 08/09 - sample start address, fraction (2.14 fixed point, i.e. 1 byte = 0x4000)
|
||||
// 0A/0B - sample start address (high word, i.e. address bits 2..17)
|
||||
// 0C/0D - sample end address (high word)
|
||||
// 0E/0F - sample loop address (high word)
|
||||
// 10/12 - ?? (sometimes value 1F is written)
|
||||
// 11/13/15/17 - voice enable mask (11 = least significant 8 bits, 17 = most significant 8 bits)
|
||||
// 19/1B/1D - ?? (written at init time)
|
||||
// 1A - ??
|
||||
// 1F - voice select
|
||||
//
|
||||
// register read:
|
||||
// 01 - last sample data (used by main CPU to read sample table from PCM ROM)
|
||||
// 02/03 - ?? (read after writing to 10/12)
|
||||
|
||||
#include "emu.h"
|
||||
#include "rolandpcm.h"
|
||||
|
||||
|
||||
constexpr int clamp16(int16_t val, int16_t min, int16_t max) { return std::min(max, std::max(min, val)); }
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(ROLANDPCM, rolandpcm_device, "rolandpcm", "Roland PCM")
|
||||
|
||||
rolandpcm_device::rolandpcm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, ROLANDPCM, tag, owner, clock)
|
||||
, device_sound_interface(mconfig, *this)
|
||||
, device_rom_interface(mconfig, *this, 22)
|
||||
, m_clock(0)
|
||||
, m_rate(0)
|
||||
, m_stream(nullptr)
|
||||
, m_sel_chn(0)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void rolandpcm_device::device_start()
|
||||
{
|
||||
m_clock = clock();
|
||||
m_rate = m_clock / 512; // usually 32 KHz
|
||||
|
||||
m_stream = machine().sound().stream_alloc(*this, 0, 2, m_rate);
|
||||
|
||||
logerror("Roland PCM: Clock %u, Rate %u\n", m_clock, m_rate);
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// rom_bank_updated - the rom bank has changed
|
||||
//-------------------------------------------------
|
||||
|
||||
void rolandpcm_device::rom_bank_updated()
|
||||
{
|
||||
// unused right now
|
||||
m_stream->update();
|
||||
}
|
||||
|
||||
|
||||
u8 rolandpcm_device::read(offs_t offset)
|
||||
{
|
||||
// Note: only offset 0x01 is verified, the rest is probably all wrong
|
||||
if (offset != 0x01)
|
||||
logerror("Reading Reg %02X\n", offset);
|
||||
if (offset < 0x10)
|
||||
{
|
||||
pcm_channel& chn = m_chns[m_sel_chn];
|
||||
switch(offset)
|
||||
{
|
||||
case 0x00:
|
||||
return (chn.mode >> 0) & 0xFF;
|
||||
case 0x01:
|
||||
m_stream->update();
|
||||
{
|
||||
offs_t addr = (chn.addr >> 14) | ((chn.bank & 0x3C00) << 8);
|
||||
return read_byte(addr); // return sample data
|
||||
}
|
||||
case 0x02: // ROM bank LSB
|
||||
return (chn.bank >> 0) & 0xFF;
|
||||
case 0x03: // ROM bank MSB
|
||||
return (chn.bank >> 8) & 0xFF;
|
||||
case 0x04: // sample step LSB
|
||||
return (chn.step >> 0) & 0xFF;
|
||||
case 0x05: // sample step LSB
|
||||
return (chn.step >> 8) & 0xFF;
|
||||
case 0x06: // volume LSB
|
||||
return (chn.volume >> 0) & 0xFF;
|
||||
case 0x07: // volume MSB
|
||||
return (chn.volume >> 8) & 0xFF;
|
||||
case 0x08: // current address, fraction LSB
|
||||
return (chn.start >> 0) & 0xFF;
|
||||
case 0x09: // current address, fraction MSB
|
||||
return (chn.start >> 8) & 0xFF;
|
||||
case 0x0A: // current address LSB
|
||||
return (chn.start >> 16) & 0xFF;
|
||||
case 0x0B: // current address MSB
|
||||
return (chn.start >> 24) & 0xFF;
|
||||
case 0x0C: // sample end address LSB
|
||||
return (chn.end >> 0) & 0xFF;
|
||||
case 0x0D: // sample end address MSB
|
||||
return (chn.end >> 8) & 0xFF;
|
||||
case 0x0E: // sample loop address LSB
|
||||
return (chn.loop >> 0) & 0xFF;
|
||||
case 0x0F: // sample loop address MSB
|
||||
return (chn.loop >> 8) & 0xFF;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(offset)
|
||||
{
|
||||
case 0x11:
|
||||
case 0x13:
|
||||
case 0x15:
|
||||
case 0x17:
|
||||
m_stream->update();
|
||||
{
|
||||
uint8_t basechn = ((offset >> 1) & 0x03) * 8;
|
||||
uint8_t result = 0x00;
|
||||
for (uint8_t ch_id = 0; ch_id < 8; ch_id ++)
|
||||
result |= (m_chns[basechn + ch_id].enable << ch_id);
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
case 0x1F:
|
||||
return m_sel_chn;
|
||||
}
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void rolandpcm_device::write(offs_t offset, u8 data)
|
||||
{
|
||||
logerror("Reg %02X = %02X\n", offset, data);
|
||||
if (offset < 0x10)
|
||||
{
|
||||
pcm_channel& chn = m_chns[m_sel_chn];
|
||||
switch(offset)
|
||||
{
|
||||
case 0x00:
|
||||
chn.mode = (chn.mode & 0xFF00) | (data << 0);
|
||||
break;
|
||||
case 0x01:
|
||||
chn.mode = (chn.mode & 0x00FF) | (data << 8);
|
||||
break;
|
||||
case 0x02: // ROM bank LSB
|
||||
chn.bank = (chn.bank & 0xFF00) | (data << 0);
|
||||
break;
|
||||
case 0x03: // ROM bank MSB / loop mode
|
||||
chn.bank = (chn.bank & 0x00FF) | (data << 8);
|
||||
break;
|
||||
case 0x04: // sample step LSB
|
||||
chn.step = (chn.step & 0xFF00) | (data << 0);
|
||||
break;
|
||||
case 0x05: // sample step LSB
|
||||
chn.step = (chn.step & 0x00FF) | (data << 8);
|
||||
break;
|
||||
case 0x06: // volume LSB
|
||||
chn.volume = (chn.volume & 0xFF00) | (data << 0);
|
||||
break;
|
||||
case 0x07: // volume MSB
|
||||
chn.volume = (chn.volume & 0x00FF) | (data << 8);
|
||||
break;
|
||||
case 0x08: // current address, fraction LSB
|
||||
chn.start = (chn.start & 0xFFFFFF00) | (data << 0);
|
||||
if (chn.enable)
|
||||
logerror("Roland PCM, channel %u: changing start address while playing!\n", m_sel_chn);
|
||||
break;
|
||||
case 0x09: // current address, fraction MSB
|
||||
chn.start = (chn.start & 0xFFFF00FF) | (data << 8);
|
||||
if (chn.enable)
|
||||
logerror("Roland PCM, channel %u: changing start address while playing!\n", m_sel_chn);
|
||||
break;
|
||||
case 0x0A: // current address LSB
|
||||
chn.start = (chn.start & 0xFF00FFFF) | (data << 16);
|
||||
if (chn.enable)
|
||||
logerror("Roland PCM, channel %u: changing start address while playing!\n", m_sel_chn);
|
||||
break;
|
||||
case 0x0B: // current address MSB
|
||||
chn.start = (chn.start & 0x00FFFFFF) | (data << 24);
|
||||
if (chn.enable)
|
||||
logerror("Roland PCM, channel %u: changing start address while playing!\n", m_sel_chn);
|
||||
break;
|
||||
case 0x0C: // sample end address, LSB
|
||||
chn.end = (chn.end & 0xFF00) | (data << 0);
|
||||
break;
|
||||
case 0x0D: // sample end address MSB
|
||||
chn.end = (chn.end & 0x00FF) | (data << 8);
|
||||
break;
|
||||
case 0x0E: // sample loop address LSB
|
||||
chn.loop = (chn.loop & 0xFF00) | (data << 0);
|
||||
break;
|
||||
case 0x0F: // sample loop address MSB
|
||||
chn.loop = (chn.loop & 0x00FF) | (data << 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(offset)
|
||||
{
|
||||
case 0x11:
|
||||
case 0x13:
|
||||
case 0x15:
|
||||
case 0x17:
|
||||
{
|
||||
uint8_t basechn = ((offset >> 1) & 0x03) * 8;
|
||||
for (uint8_t ch_id = 0; ch_id < 8; ch_id ++)
|
||||
{
|
||||
pcm_channel& chn = m_chns[basechn + ch_id];
|
||||
bool play = static_cast<bool>((data >> ch_id) & 1);
|
||||
|
||||
if (play && ! chn.enable)
|
||||
{
|
||||
chn.addr = chn.start;
|
||||
offs_t addr = (chn.addr >> 14) | ((chn.bank & 0x3C00) << 8);
|
||||
chn.smpl_cur = 0;
|
||||
chn.smpl_nxt = decode_sample((int8_t)read_byte(addr));
|
||||
chn.play_dir = +1;
|
||||
logerror("Starting channel %u, bank 0x%04X, addr 0x%05X.%03X\n",
|
||||
basechn + ch_id, chn.bank, chn.start >> 14, (chn.start & 0x3FFF) << 2);
|
||||
logerror("Smpl End Ofs: 0x%04X, Loop Ofs 0x%04X, Step 0x%04X, Volume %04X\n",
|
||||
chn.end << 2, chn.loop << 2, chn.step, chn.volume);
|
||||
}
|
||||
chn.enable = play;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x1F:
|
||||
m_sel_chn = data;
|
||||
break;
|
||||
default:
|
||||
logerror("Writing unknown reg %02X = %02X\n", offset, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// sound_stream_update - handle a stream update
|
||||
//-------------------------------------------------
|
||||
|
||||
void rolandpcm_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
|
||||
{
|
||||
memset(outputs[0], 0, samples * sizeof(stream_sample_t));
|
||||
memset(outputs[1], 0, samples * sizeof(stream_sample_t));
|
||||
|
||||
for (auto& chn : m_chns)
|
||||
{
|
||||
if (! chn.enable || chn.play_dir == 0)
|
||||
continue;
|
||||
|
||||
for (int smpl = 0; smpl < samples; smpl ++)
|
||||
{
|
||||
stream_sample_t smp_data;
|
||||
if (chn.play_dir > 0)
|
||||
smp_data = sample_interpolate(chn.smpl_cur, chn.smpl_nxt, chn.addr & 0x3FFF);
|
||||
else
|
||||
smp_data = sample_interpolate(chn.smpl_nxt, chn.smpl_cur, chn.addr & 0x3FFF);
|
||||
smp_data = (smp_data * chn.volume) >> 14; // >>14 results in a good overall volume
|
||||
outputs[0][smpl] += smp_data;
|
||||
outputs[1][smpl] += smp_data;
|
||||
|
||||
uint32_t old_addr = chn.addr;
|
||||
if (chn.play_dir > 0)
|
||||
chn.addr += chn.step;
|
||||
else if (chn.play_dir < 0)
|
||||
chn.addr -= chn.step;
|
||||
|
||||
// Note: The sample data is read after incrementing the address, but before handling loop points.
|
||||
// Reading the sample data after loop point handling breaks the test mode square wave.
|
||||
// (Sample 0xA7 on the CM-32P.)
|
||||
if ((chn.addr >> 14) != (old_addr >> 14))
|
||||
{
|
||||
offs_t addr = (chn.addr >> 14) | ((chn.bank & 0x3C00) << 8);
|
||||
//logerror("Chn %u: sample addr 0x%05X.%03X\n", &chn - &m_chns[0], chn.addr >> 14, (chn.addr & 0x3FFF) << 2);
|
||||
chn.smpl_cur = chn.smpl_nxt;
|
||||
chn.smpl_nxt += decode_sample((int8_t)read_byte(addr)); // This was verified to be independent from play_dir.
|
||||
|
||||
// until the decoding is fixed, we prevent overflow bugs (due to DC offsets when looping) this way
|
||||
chn.smpl_nxt = clamp16(chn.smpl_nxt, -0x7FF, +0x7FF);
|
||||
}
|
||||
|
||||
bool reachedEnd = false;
|
||||
if (chn.play_dir > 0)
|
||||
{
|
||||
// This works well with the test mode sine/square wave samples.
|
||||
if ((chn.addr >> 16) >= chn.end)
|
||||
reachedEnd = true;
|
||||
}
|
||||
else if (chn.play_dir < 0)
|
||||
{
|
||||
if ((chn.addr >> 16) < chn.loop)
|
||||
reachedEnd = true;
|
||||
}
|
||||
if (reachedEnd)
|
||||
{
|
||||
offs_t oldAddr = chn.addr;
|
||||
logerror("Chn %u: Sample End at addr 0x%05X.%03X. (Dir %d, Loop Mode %u)\n",
|
||||
&chn - &m_chns[0], chn.addr >> 14, (chn.addr & 0x3FFF) << 2,
|
||||
chn.play_dir, chn.bank >> 14);
|
||||
switch(chn.bank >> 14)
|
||||
{
|
||||
case 0: // normal loop
|
||||
chn.addr = chn.addr + ((chn.loop - chn.end) << 16);
|
||||
logerror("addr 0x%05X.%03X -> addr 0x%05X.%03X\n",
|
||||
oldAddr >> 14, (oldAddr & 0x3FFF) << 2,
|
||||
chn.addr >> 14, (chn.addr & 0x3FFF) << 2);
|
||||
break;
|
||||
case 1: // no loop
|
||||
case 3: // invalid, assume "no loop"
|
||||
chn.play_dir = 0;
|
||||
break;
|
||||
case 2: // ping-pong
|
||||
// CM-32P samples with ping-pong loop mode:
|
||||
// 0x00 (low piano note), 0x58..0x67, 0x69..0x6C (choir/strings), 0x9F..0xA4 (brass)
|
||||
// Note: These formulae are probably incorrect, as they cause some DC offset in most samples.
|
||||
// Reference sample rate for C5 @ Sample 9F: 34290 Hz.
|
||||
if (chn.play_dir > 0)
|
||||
chn.addr = (chn.end << 16) - chn.addr + (chn.end << 16);
|
||||
else if (chn.play_dir < 0)
|
||||
chn.addr = (chn.loop << 16) - chn.addr + (chn.loop << 16);
|
||||
chn.play_dir = -chn.play_dir;
|
||||
break;
|
||||
}
|
||||
if (chn.play_dir == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t rolandpcm_device::decode_sample(int8_t data)
|
||||
{
|
||||
int16_t val;
|
||||
int16_t sign;
|
||||
uint8_t shift;
|
||||
int16_t result;
|
||||
|
||||
if (data < 0)
|
||||
{
|
||||
sign = -1;
|
||||
val = -data;
|
||||
}
|
||||
else
|
||||
{
|
||||
sign = +1;
|
||||
val = data;
|
||||
}
|
||||
|
||||
// thanks to Sarayan for figuring out the decoding formula
|
||||
shift = val >> 4;
|
||||
val &= 0x0F;
|
||||
if (! shift)
|
||||
result = val;
|
||||
else
|
||||
result = (0x10 + val) << (shift - 1);
|
||||
return result * sign;
|
||||
}
|
||||
|
||||
int16_t rolandpcm_device::sample_interpolate(int16_t smp1, int16_t smp2, uint16_t frac)
|
||||
{
|
||||
int32_t smpfrac0 = (int32_t)smp1 * (0x4000 - frac);
|
||||
int32_t smpfrac1 = (int32_t)smp2 * frac;
|
||||
return (int16_t)((smpfrac0 + smpfrac1) >> 14);
|
||||
}
|
61
src/devices/sound/rolandpcm.h
Normal file
61
src/devices/sound/rolandpcm.h
Normal file
@ -0,0 +1,61 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Valley Bell
|
||||
#ifndef MAME_SOUND_ROLANDPCM_H
|
||||
#define MAME_SOUND_ROLANDPCM_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class rolandpcm_device : public device_t, public device_sound_interface, public device_rom_interface
|
||||
{
|
||||
public:
|
||||
rolandpcm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
u8 read(offs_t offset);
|
||||
void write(offs_t offset, u8 data);
|
||||
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
|
||||
// sound stream update overrides
|
||||
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
|
||||
|
||||
// device_rom_interface overrides
|
||||
virtual void rom_bank_updated() override;
|
||||
|
||||
static int16_t decode_sample(int8_t data);
|
||||
static int16_t sample_interpolate(int16_t smp1, int16_t smp2, uint16_t frac);
|
||||
|
||||
private:
|
||||
static constexpr unsigned NUM_CHANNELS = 32;
|
||||
|
||||
struct pcm_channel
|
||||
{
|
||||
pcm_channel() { }
|
||||
|
||||
// registers
|
||||
uint16_t mode = 0;
|
||||
uint16_t bank = 0;
|
||||
uint16_t step = 0; // 2.14 fixed point (0x4000 equals 32000 Hz)
|
||||
uint16_t volume = 0;
|
||||
uint32_t start = 0; // start address (18.14 fixed point)
|
||||
uint16_t end = 0; // end offset (high word)
|
||||
uint16_t loop = 0; // loop offset (high word)
|
||||
// work variables
|
||||
bool enable = false;
|
||||
int8_t play_dir = 0; // playing direction, -1 [backwards] / 0 [stopped] / +1 [forwards]
|
||||
uint32_t addr = 0; // current address
|
||||
int16_t smpl_cur = 0; // current sample
|
||||
int16_t smpl_nxt = 0; // next sample
|
||||
};
|
||||
|
||||
uint32_t m_clock; // clock
|
||||
uint32_t m_rate; // sample rate (usually 32000 Hz)
|
||||
sound_stream* m_stream; // stream handle
|
||||
pcm_channel m_chns[NUM_CHANNELS]; // channel memory
|
||||
uint8_t m_sel_chn; // selected channel
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(ROLANDPCM, rolandpcm_device)
|
||||
|
||||
#endif // MAME_SOUND_ROLANDPCM_H
|
@ -33,10 +33,12 @@ Notes:
|
||||
The additional PCM ROMs are mapped to 0x100000 .. 0x17FFFF (IC19) and 0x200000 .. 0x27FFFF according to the sample table in IC18.
|
||||
- The sound chip has 32 voices. Voice 0 is reserved by the firmware for reading data from the PCM ROM.
|
||||
The firmware allocates voices from back to front, i.e. voice 31 first.
|
||||
- The "PCM sound chip" itself doesn't seem to support panning.
|
||||
Maybe it just has 6 outputs (one for each part, like the U-110) and an additional DSP does the rest.
|
||||
|
||||
|
||||
TODO:
|
||||
- actual sound emulation
|
||||
- figure out how "freeing a voice" works - right now the firmware gets stuck when playing the 32th note.
|
||||
- add PCM card support
|
||||
|
||||
|
||||
@ -227,9 +229,11 @@ Some routine locations
|
||||
#include "cpu/mcs96/i8x9x.h"
|
||||
#include "machine/ram.h"
|
||||
#include "machine/timer.h"
|
||||
#include "sound/rolandpcm.h"
|
||||
#include "video/msm6222b.h"
|
||||
#include "emupal.h"
|
||||
#include "screen.h"
|
||||
#include "speaker.h"
|
||||
|
||||
|
||||
// unscramble address: ROM dump offset -> proper (descrambled) offset
|
||||
@ -250,6 +254,7 @@ static INPUT_PORTS_START( cm32p )
|
||||
PORT_START("SERVICE") // connected to Port 0 of the P8098 CPU.
|
||||
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test Switch") PORT_TOGGLE PORT_CODE(KEYCODE_F2) // SW A (checked during boot)
|
||||
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: Check/Tune") PORT_CODE(KEYCODE_B) // SW B
|
||||
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("PCM card inserted") PORT_TOGGLE PORT_CODE(KEYCODE_C)
|
||||
|
||||
PORT_START("SW") // test switches, accessed by reading from address 0x1300
|
||||
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Test: MSB Adj.") PORT_CODE(KEYCODE_1) // SW 1
|
||||
@ -269,12 +274,15 @@ public:
|
||||
|
||||
void cm32p(machine_config &config);
|
||||
|
||||
void init_cm32p();
|
||||
|
||||
protected:
|
||||
virtual void machine_start() override;
|
||||
virtual void machine_reset() override;
|
||||
|
||||
private:
|
||||
required_device<i8x9x_device> cpu;
|
||||
required_device<rolandpcm_device> pcm;
|
||||
required_device<msm6222b_device> lcd;
|
||||
required_device<timer_device> midi_timer;
|
||||
required_device<ram_device> some_ram;
|
||||
@ -305,7 +313,6 @@ private:
|
||||
|
||||
u8 midi;
|
||||
int midi_pos;
|
||||
u8 port0;
|
||||
u8 sound_io_buffer[0x100];
|
||||
u8 dsp_io_buffer[0x80];
|
||||
};
|
||||
@ -313,6 +320,7 @@ private:
|
||||
cm32p_state::cm32p_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: driver_device(mconfig, type, tag)
|
||||
, cpu(*this, "maincpu")
|
||||
, pcm(*this, "pcm")
|
||||
, lcd(*this, "lcd")
|
||||
, midi_timer(*this, "midi_timer")
|
||||
, some_ram(*this, "some_ram")
|
||||
@ -370,7 +378,6 @@ void cm32p_state::machine_reset()
|
||||
{
|
||||
midi_timer->adjust(attotime::from_hz(1));
|
||||
midi_pos = 0;
|
||||
port0 = 0;
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(cm32p_state::lcd_ctrl_w)
|
||||
@ -408,14 +415,13 @@ TIMER_DEVICE_CALLBACK_MEMBER(cm32p_state::midi_timer_cb)
|
||||
|
||||
READ16_MEMBER(cm32p_state::port0_r)
|
||||
{
|
||||
return port0 | service_port->read();
|
||||
return service_port->read();
|
||||
}
|
||||
|
||||
READ8_MEMBER(cm32p_state::pcmrom_r)
|
||||
{
|
||||
offs_t romOfs = SCRAMBLE_ADDRESS(offset);
|
||||
const u8* pcm_rom = memregion("pcm32")->base();
|
||||
return UNSCRAMBLE_DATA(pcm_rom[romOfs]);
|
||||
const u8* pcm_rom = memregion("pcm")->base();
|
||||
return pcm_rom[offset];
|
||||
}
|
||||
|
||||
READ8_MEMBER(cm32p_state::dsp_io_r)
|
||||
@ -455,6 +461,19 @@ WRITE8_MEMBER(cm32p_state::dsp_io_w)
|
||||
|
||||
READ8_MEMBER(cm32p_state::snd_io_r)
|
||||
{
|
||||
// lots of offset modification magic to achieve the following:
|
||||
// - offsets 00..1F are "sound chip read"
|
||||
// - offsets 20..3F are a readback of what was written to registers 00..1F
|
||||
// - This behaviour is reversed for offset 01/21, which is used for reading the PCM sample tables.
|
||||
// All this is just for making debugging easier, as it allows one to check the
|
||||
// register state using the Memory Viewer.
|
||||
if (offset == 0x01 || offset == 0x21)
|
||||
offset ^= 0x20; // remove when PCM data readback via sound chip is confirmed to work
|
||||
if (offset < 0x20)
|
||||
return pcm->read(offset);
|
||||
if (offset < 0x40)
|
||||
offset -= 0x20;
|
||||
|
||||
if (offset == 0x01)
|
||||
{
|
||||
// code for reading from the PCM sample table is at 0xB027
|
||||
@ -490,6 +509,8 @@ WRITE8_MEMBER(cm32p_state::snd_io_w)
|
||||
// 11/13/15/17 - voice enable mask (11 = least significant 8 bits, 17 = most significant 8 bits)
|
||||
// 1A - ??
|
||||
// 1F - voice select
|
||||
if (offset < 0x20)
|
||||
pcm->write(offset, data);
|
||||
sound_io_buffer[offset] = data;
|
||||
}
|
||||
|
||||
@ -500,7 +521,7 @@ READ8_MEMBER(cm32p_state::test_sw_r)
|
||||
|
||||
TIMER_DEVICE_CALLBACK_MEMBER(cm32p_state::samples_timer_cb)
|
||||
{
|
||||
port0 ^= 0x10;
|
||||
// TODO: does this trigger something?
|
||||
}
|
||||
|
||||
void cm32p_state::mt32_palette(palette_device &palette) const
|
||||
@ -518,8 +539,7 @@ void cm32p_state::cm32p_map(address_map &map)
|
||||
map(0x1400, 0x14ff).rw(FUNC(cm32p_state::snd_io_r), FUNC(cm32p_state::snd_io_w)); // sound chip area
|
||||
map(0x2000, 0x20ff).rom().region("maincpu", 0x2000); // init vector @ 2080
|
||||
map(0x2100, 0x3fff).ram(); // main RAM
|
||||
map(0x4000, 0xbfff).rom().region("maincpu", 0x4000);
|
||||
map(0xc000, 0xffff).r(FUNC(cm32p_state::pcmrom_r)); // show descrambled PCM ROM (for debugging)
|
||||
map(0x4000, 0xffff).rom().region("maincpu", 0x4000);
|
||||
}
|
||||
|
||||
void cm32p_state::cm32p(machine_config &config)
|
||||
@ -530,6 +550,13 @@ void cm32p_state::cm32p(machine_config &config)
|
||||
maincpu.serial_tx_cb().set(FUNC(cm32p_state::midi_w));
|
||||
maincpu.in_p0_cb().set(FUNC(cm32p_state::port0_r));
|
||||
|
||||
SPEAKER(config, "lspeaker").front_left();
|
||||
SPEAKER(config, "rspeaker").front_right();
|
||||
|
||||
ROLANDPCM(config, pcm, 16.384_MHz_XTAL);
|
||||
pcm->add_route(0, "lspeaker", 1.0);
|
||||
pcm->add_route(1, "rspeaker", 1.0);
|
||||
|
||||
RAM(config, some_ram).set_default_size("8K");
|
||||
|
||||
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
|
||||
@ -548,15 +575,33 @@ void cm32p_state::cm32p(machine_config &config)
|
||||
TIMER(config, "samples_timer").configure_periodic(FUNC(cm32p_state::samples_timer_cb), attotime::from_hz(32000*2));
|
||||
}
|
||||
|
||||
void cm32p_state::init_cm32p()
|
||||
{
|
||||
// Roland did a fair amount of scrambling on the address and data lines.
|
||||
// Only the first 0x20 bytes of the ROMs are readable text in a raw dump.
|
||||
uint8_t* src = static_cast<uint8_t*>(memregion("pcmorg")->base());
|
||||
uint8_t* dst = static_cast<uint8_t*>(memregion("pcm")->base());
|
||||
for (offs_t bank_ofs = 0x00; bank_ofs < 0x400000; bank_ofs += 0x080000)
|
||||
{
|
||||
offs_t dstpos;
|
||||
for (offs_t srcpos = 0x00; srcpos < 0x80000; srcpos ++)
|
||||
{
|
||||
dstpos = UNSCRAMBLE_ADDRESS(srcpos);
|
||||
dst[bank_ofs + dstpos] = UNSCRAMBLE_DATA(src[bank_ofs + srcpos]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ROM_START( cm32p )
|
||||
ROM_REGION( 0x10000, "maincpu", 0 )
|
||||
ROM_LOAD( "cm-32_p__1.0.0.am27c512.7d.ic9", 0x000000, 0x10000, CRC(6f2f6dfd) SHA1(689f77c1d56f923ef1dab7d993a124c47736bc56) ) // "CM-32 P // 1 0 0 <filled bubbles in red marker>" sticker on an AM27C512-150DC eprom @ IC9
|
||||
|
||||
ROM_REGION( 0x400000, "pcm32", 0 )
|
||||
ROM_REGION( 0x400000, "pcmorg", 0 ) // ROMs before descrambling
|
||||
ROM_LOAD( "roland__r15179970__mb834000a-20__3b1_aa__8917_r00.45f.ic18", 0x000000, 0x80000, CRC(8e53b2a3) SHA1(4872530870d5079776e80e477febe425dc0ec1df) ) // markings under chip footprint are "MB834000A-20P-G-3B1"
|
||||
// 0x080000 .. 0x0FFFFF is reserved for the PCM card
|
||||
ROM_LOAD( "roland__r15179971__mb834000a-20__3b2_aa__8919_r02.34f.ic19", 0x100000, 0x80000, CRC(c8220761) SHA1(49e55fa672020f95fd9c858ceaae94d6db93df7d) ) // markings under chip footprint are "MB834000A20P-G-3B2" (including the missing dash, which is a typo on the board silkscreen)
|
||||
ROM_LOAD( "roland__r15179972__hn62304bpe98__9d1_japan.3f.ic20", 0x200000, 0x80000, CRC(733c4054) SHA1(9b6b59ab74e5bf838702abb087c408aaa85b7b1f) ) // markings under chip footprint are "HN62304BPE98"
|
||||
ROM_REGION( 0x400000, "pcm", ROMREGION_ERASEFF ) // ROMs after descrambling
|
||||
ROM_END
|
||||
|
||||
CONS( 1989, cm32p, 0, 0, cm32p, cm32p, cm32p_state, empty_init, "Roland", "CM-32P", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
CONS( 1989, cm32p, 0, 0, cm32p, cm32p, cm32p_state, init_cm32p, "Roland", "CM-32P", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
|
Loading…
Reference in New Issue
Block a user