bus/bbc/1mhzbus: Added the Acorn Music 500, Hybrid Music 5000 Synthesiser, Hybrid Music 3000 Expander, and Peartree Music 87 Synthesiser.

This commit is contained in:
Nigel Barnes 2020-09-25 11:01:41 +01:00
parent 5d841ac44f
commit e7b852c7a3
4 changed files with 604 additions and 6 deletions

View File

@ -504,6 +504,8 @@ if (BUSES["BBC_1MHZBUS"]~=null) then
MAME_DIR .. "src/devices/bus/bbc/1mhzbus/ieee488.h",
MAME_DIR .. "src/devices/bus/bbc/1mhzbus/m2000.cpp",
MAME_DIR .. "src/devices/bus/bbc/1mhzbus/m2000.h",
MAME_DIR .. "src/devices/bus/bbc/1mhzbus/m5000.cpp",
MAME_DIR .. "src/devices/bus/bbc/1mhzbus/m5000.h",
MAME_DIR .. "src/devices/bus/bbc/1mhzbus/multiform.cpp",
MAME_DIR .. "src/devices/bus/bbc/1mhzbus/multiform.h",
MAME_DIR .. "src/devices/bus/bbc/1mhzbus/opus3.cpp",

View File

@ -117,7 +117,7 @@ void bbc_1mhzbus_slot_device::jim_w(offs_t offset, uint8_t data)
#include "ide.h"
#include "ieee488.h"
#include "m2000.h"
//#include "m5000.h"
#include "m5000.h"
#include "sasi.h"
#include "scsi.h"
#include "multiform.h"
@ -137,7 +137,7 @@ void bbc_1mhzbus_devices(device_slot_interface &device)
{
//device.option_add("teletext", BBC_TELETEXT); /* Acorn ANE01 Teletext Adapter */
device.option_add("ieee488", BBC_IEEE488); /* Acorn ANK01 IEEE488 Interface */
//device.option_add("m500", BBC_M500); /* Acorn ANV02 Music 500 */
device.option_add("m500", BBC_M500); /* Acorn ANV02 Music 500 */
device.option_add("awhd", BBC_AWHD); /* Acorn Winchester Disc */
device.option_add("autoprom", BBC_AUTOPROM); /* ATPL AutoPrommer */
device.option_add("beebide", BBC_BEEBIDE); /* Sprow BeebIDE 16-bit */
@ -147,6 +147,10 @@ void bbc_1mhzbus_devices(device_slot_interface &device)
//device.option_add("videodig", BBC_VIDEODIG); /* Video Digitiser (RH Electronics) */
device.option_add("emrmidi", BBC_EMRMIDI); /* EMR Midi Interface */
//device.option_add("procyon", BBC_PROCYON); /* CST Procyon IEEE Interface */
device.option_add("m2000", BBC_M2000); /* Hybrid Music 2000 Interface */
device.option_add("m3000", BBC_M3000); /* Hybrid Music 3000 Expander */
device.option_add("m5000", BBC_M5000); /* Hybrid Music 5000 Synthesiser */
device.option_add("m87", BBC_M87); /* Peartree Music 87 Synthesiser */
device.option_add("multiform", BBC_MULTIFORM); /* PEDL Multiform Z80 */
device.option_add("opus3", BBC_OPUS3); /* Opus Challenger 3 */
device.option_add("pdram", BBC_PDRAM); /* Micro User Pull Down RAM */
@ -166,7 +170,6 @@ void bbcm_1mhzbus_devices(device_slot_interface &device)
{
//device.option_add("teletext", BBC_TELETEXT); /* Acorn ANE01 Teletext Adapter */
device.option_add("ieee488", BBC_IEEE488); /* Acorn ANK01 IEEE488 Interface */
//device.option_add("m500", BBC_M500); /* Acorn ANV02 Music 500 */
device.option_add("awhd", BBC_AWHD); /* Acorn Winchester Disc */
device.option_add("beebide", BBC_BEEBIDE); /* Sprow BeebIDE 16-bit */
device.option_add("ide8", BBC_IDE8); /* RetroClinic BBC 8-bit IDE */
@ -175,9 +178,9 @@ void bbcm_1mhzbus_devices(device_slot_interface &device)
device.option_add("emrmidi", BBC_EMRMIDI); /* EMR Midi Interface */
//device.option_add("procyon", BBC_PROCYON); /* CST Procyon IEEE Interface */
device.option_add("m2000", BBC_M2000); /* Hybrid Music 2000 Interface */
//device.option_add("m3000", BBC_M3000); /* Hybrid Music 3000 Expander */
//device.option_add("m5000", BBC_M5000); /* Hybrid Music 5000 Synthesiser */
//device.option_add("m87", BBC_M87); /* Peartree Music 87 Synthesiser */
device.option_add("m3000", BBC_M3000); /* Hybrid Music 3000 Expander */
device.option_add("m5000", BBC_M5000); /* Hybrid Music 5000 Synthesiser */
device.option_add("m87", BBC_M87); /* Peartree Music 87 Synthesiser */
device.option_add("multiform", BBC_MULTIFORM); /* PEDL Multiform Z80 */
device.option_add("opusa", BBC_OPUSA); /* Opus Challenger ADFS */
device.option_add("pdram", BBC_PDRAM); /* Micro User Pull Down RAM */

View File

@ -0,0 +1,435 @@
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
// thanks-to:Darren Izzard
/**********************************************************************
Acorn ANV02 Music 500
http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Acorn_ANV02_Music500.html
Hybrid Music 5000 Synthesiser
https://www.retro-kit.co.uk/page.cfm/content/Hybrid-Music-5000-Synthesiser/
http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Hybrid_Music5000.html
Hybrid Music 3000 Expander
https://www.retro-kit.co.uk/page.cfm/content/Hybrid-Music-3000-Expander/
Peartree Music 87 Synthesiser
http://www.computinghistory.org.uk/det/4535/Peartree-Computers-Music-87-Synthesizer-(M500)/
TODO:
- convert to use DAC76 device
- add suitably patched version of Ample ROM
- add ROMs for Music 87
**********************************************************************/
#include "emu.h"
#include "m5000.h"
#include "speaker.h"
#include "softlist_dev.h"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(HTMUSIC, htmusic_device, "htmusic", "Hybrid Technology Music System")
DEFINE_DEVICE_TYPE(BBC_M500, bbc_m500_device, "bbc_m500", "Acorn Music 500");
DEFINE_DEVICE_TYPE(BBC_M5000, bbc_m5000_device, "bbc_m5000", "Hybrid Music 5000 Synthesiser");
DEFINE_DEVICE_TYPE(BBC_M3000, bbc_m3000_device, "bbc_m3000", "Hybrid Music 3000 Expander");
DEFINE_DEVICE_TYPE(BBC_M87, bbc_m87_device, "bbc_m87", "Peartree Music 87 Synthesiser");
//-------------------------------------------------
// ROM( m5000 )
//-------------------------------------------------
//ROM_START( m5000 )
//ROM_REGION(0x4000, "exp_rom", 0)
// Each AMPLE Nucleus ROM has a unique identification code (ID)
//ROM_LOAD("ample_patch.rom", 0x0000, 0x4000, CRC(15bbd499) SHA1(ac90f403b3bde0cdaae68546799b94962bc00fcb))
//ROM_END
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
void bbc_m500_device::add_common_devices(machine_config &config)
{
SPEAKER(config, "lspeaker").front_left();
SPEAKER(config, "rspeaker").front_right();
HTMUSIC(config, m_hybrid, 12_MHz_XTAL / 2);
m_hybrid->add_route(0, "lspeaker", 1.0);
m_hybrid->add_route(1, "rspeaker", 1.0);
BBC_1MHZBUS_SLOT(config, m_1mhzbus, DERIVED_CLOCK(1, 1), bbc_1mhzbus_devices, nullptr);
m_1mhzbus->irq_handler().set(DEVICE_SELF_OWNER, FUNC(bbc_1mhzbus_slot_device::irq_w));
m_1mhzbus->nmi_handler().set(DEVICE_SELF_OWNER, FUNC(bbc_1mhzbus_slot_device::nmi_w));
}
void bbc_m500_device::device_add_mconfig(machine_config &config)
{
add_common_devices(config);
SOFTWARE_LIST(config, "flop_ls_hybrid").set_original("bbc_flop_hybrid").set_filter("M500");
}
void bbc_m5000_device::device_add_mconfig(machine_config &config)
{
add_common_devices(config);
SOFTWARE_LIST(config, "flop_ls_hybrid").set_original("bbc_flop_hybrid").set_filter("M5000");
}
void bbc_m3000_device::device_add_mconfig(machine_config &config)
{
add_common_devices(config);
}
void bbc_m87_device::device_add_mconfig(machine_config &config)
{
add_common_devices(config);
SOFTWARE_LIST(config, "flop_ls_hybrid").set_original("bbc_flop_hybrid").set_filter("M87");
}
//-------------------------------------------------
// rom_region - device-specific ROM region
//-------------------------------------------------
//const tiny_rom_entry *bbc_m5000_device::device_rom_region() const
//{
//return ROM_NAME( m5000 );
//}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// bbc_hybrid_device - constructor
//-------------------------------------------------
bbc_m500_device::bbc_m500_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
, device_bbc_1mhzbus_interface(mconfig, *this)
, m_1mhzbus(*this, "1mhzbus")
, m_hybrid(*this, "hybrid")
, m_page(0)
{
}
bbc_m500_device::bbc_m500_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: bbc_m500_device(mconfig, BBC_M500, tag, owner, clock)
{
}
bbc_m5000_device::bbc_m5000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: bbc_m500_device(mconfig, BBC_M5000, tag, owner, clock)
{
}
bbc_m3000_device::bbc_m3000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: bbc_m500_device(mconfig, BBC_M3000, tag, owner, clock)
{
}
bbc_m87_device::bbc_m87_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: bbc_m500_device(mconfig, BBC_M87, tag, owner, clock)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void bbc_m500_device::device_start()
{
// register for save states
save_item(NAME(m_page));
}
//**************************************************************************
// IMPLEMENTATION
//**************************************************************************
uint8_t bbc_m500_device::fred_r(offs_t offset)
{
return m_1mhzbus->fred_r(offset);
}
void bbc_m500_device::fred_w(offs_t offset, uint8_t data)
{
if (offset == 0xff)
{
m_page = data;
}
m_1mhzbus->fred_w(offset, data);
}
uint8_t bbc_m500_device::jim_r(offs_t offset)
{
return m_1mhzbus->jim_r(offset);
}
void bbc_m500_device::jim_w(offs_t offset, uint8_t data)
{
if ((m_page & 0xf0) == 0x30)
{
uint8_t page = (m_page >> 1) & 7;
m_hybrid->ram_w((page << 8) | offset, data);
}
m_1mhzbus->jim_w(offset, data);
}
void bbc_m3000_device::jim_w(offs_t offset, uint8_t data)
{
if ((m_page & 0xf0) == 0x50)
{
uint8_t page = (m_page >> 1) & 7;
m_hybrid->ram_w((page << 8) | offset, data);
}
m_1mhzbus->jim_w(offset, data);
}
//**************************************************************************
// HYBRID MUSIC IMPLEMENTATION
//**************************************************************************
//-------------------------------------------------
// htmusic_device - constructor
//-------------------------------------------------
htmusic_device::htmusic_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, HTMUSIC, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, m_counter(0)
, m_c4d(0)
, m_sign(0)
, m_sam(0)
, m_disable(false)
, m_modulate(false)
, m_dsp_timer(nullptr)
, m_stream(nullptr)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void htmusic_device::device_start()
{
// 12-bit antilog as per Am6070 datasheet
for (int i = 0; i < 128; i++)
m_antilog[i] = (uint16_t)(2 * (pow(2.0, i >> 4) * ((i & 15) + 16.5) - 16.5));
// create the stream
m_stream = stream_alloc(0, 2, clock() / 128);
// allocate timer
m_dsp_timer = timer_alloc(0);
// register for save states
save_item(NAME(m_wave_ram));
save_item(NAME(m_phase_ram));
save_item(NAME(m_counter));
save_item(NAME(m_c4d));
save_item(NAME(m_sign));
save_item(NAME(m_disable));
save_item(NAME(m_modulate));
save_item(NAME(m_sam));
save_item(NAME(m_sam_l));
save_item(NAME(m_sam_r));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void htmusic_device::device_reset()
{
// clear ram
memset(m_wave_ram, 0, sizeof(m_wave_ram));
memset(m_phase_ram, 0, sizeof(m_phase_ram));
memset(m_sam_l, 0, sizeof(m_sam_l));
memset(m_sam_r, 0, sizeof(m_sam_r));
// reset counter
m_counter = 0;
m_disable = false;
m_modulate = false;
// start timer
m_dsp_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()));
}
//-------------------------------------------------
// digital signal processor
//-------------------------------------------------
#define I_CHAN(c) ((((c) & 0x1e) >> 1) + (((c) & 0x01) << 7) + 0x700)
#define I_FREQ(c) (I_CHAN(c))
#define I_WAVESEL(c) (I_CHAN(c) + 0x50)
#define I_AMP(c) (I_CHAN(c) + 0x60)
#define I_CTL(c) (I_CHAN(c) + 0x70)
#define FREQ(c) ((m_wave_ram[I_FREQ(c) + 0x20] << 16) | (m_wave_ram[I_FREQ(c) + 0x10] << 8) | (m_wave_ram[I_FREQ(c)] & 0x7e))
#define DISABLE(c) (BIT(m_wave_ram[I_FREQ(c)], 0))
#define AMP(c) (m_wave_ram[I_AMP(c)])
#define WAVESEL(c) (m_wave_ram[I_WAVESEL(c)] >> 4)
#define CTL(c) (m_wave_ram[I_CTL(c)])
#define MODULATE(c) (BIT(CTL(c), 5))
#define INVERT(c) (BIT(CTL(c), 4))
#define PAN(c) (CTL(c) & 0x0f)
void htmusic_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
// 4-bit channel select
uint8_t channel = (m_counter >> 3) & 0x0f;
uint8_t c = (channel << 1) + (m_modulate ? 1 : 0);
// 3-bit program counter
switch (m_counter & 0x07)
{
case 0:
m_stream->update();
m_disable = DISABLE(c);
break;
case 1:
break;
case 2:
// In the real hardware the disable bit works by forcing the
// phase accumulator to zero.
if (m_disable)
{
m_phase_ram[channel] = 0;
m_c4d = 0;
}
else
{
uint32_t sum = m_phase_ram[channel] + FREQ(c);
m_phase_ram[channel] = sum & 0xffffff;
// c4d is used for "Synchronization" e.g. the "Wha" instrument
m_c4d = sum >> 24;
}
break;
case 3:
break;
case 4:
break;
case 5:
m_sam = m_wave_ram[(WAVESEL(c) << 7) + (m_phase_ram[channel] >> 17)];
break;
case 6:
// The amplitude operates in the log domain
// - m_sam holds the wave table output which is 1 bit sign and 7 bit magnitude
// - amp holds the amplitude which is 1 bit sign and 8 bit magnitude (0x00 being quite, 0x7f being loud)
// The real hardware combines these in a single 8 bit adder, as we do here.
//
// Consider a positive wav value (sign bit = 1)
// wav: (0x80 -> 0xFF) + amp: (0x00 -> 0x7F) => (0x80 -> 0x7E)
// values in the range 0x80...0xff are very small and clamped to zero
//
// Consider a negative wav value (sign bit = 0)
// wav: (0x00 -> 0x7F) + amp: (0x00 -> 0x7F) => (0x00 -> 0xFE)
// values in the range 0x00...0x7f are very small and clamped to zero
//
// In both cases:
// - zero clamping happens when the sign bit stays the same
// - the 7-bit result is in bits 0..6
//
m_sign = m_sam & 0x80;
m_sam += AMP(c);
if ((m_sign ^ m_sam) & 0x80)
m_sam &= 0x7f; // sign bits being different is the normal case
else
m_sam = 0x00; // sign bits being the same indicates underflow so clamp to zero
break;
case 7:
{
m_modulate = MODULATE(c) && (!!(m_sign) || !!(m_c4d));
// in the real hardware, inversion does not affect modulation
if (INVERT(c))
m_sign ^= 0x80;
// m_sam is an 8-bit log value
if (m_sign)
m_sam = m_antilog[m_sam]; // sign being 1 is positive
else
m_sam = -m_antilog[m_sam]; // sign being 0 is negative
// m_sam is a 12-bit linear sample
uint8_t pan;
switch (PAN(c))
{
case 8: case 9: case 10: pan = 6; break;
case 11: pan = 5; break;
case 12: pan = 4; break;
case 13: pan = 3; break;
case 14: pan = 2; break;
case 15: pan = 1; break;
default: pan = 0; break;
}
// Apply panning. In the real hardware, a disabled channel is not actually
// forced to zero, but this seems harmless so leave in for now.
m_sam_l[channel] = m_disable ? 0 : ((m_sam * pan) / 6);
m_sam_r[channel] = m_disable ? 0 : ((m_sam * (6 - pan)) / 6);
break;
}
}
m_counter++;
}
//-------------------------------------------------
// sound_stream_update - handle a stream update
//-------------------------------------------------
void htmusic_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
// reset the output streams
outputs[0].fill(0);
outputs[1].fill(0);
// iterate over channels and accumulate sample data
for (int channel = 0; channel < 16; channel++)
{
for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++)
{
outputs[0].add_int(sampindex, m_sam_l[channel], 8031 * 16);
outputs[1].add_int(sampindex, m_sam_r[channel], 8031 * 16);
}
}
}

View File

@ -0,0 +1,158 @@
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/**********************************************************************
Acorn ANV02 Music 500
http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Acorn_ANV02_Music500.html
Hybrid Music 5000 Synthesiser
https://www.retro-kit.co.uk/page.cfm/content/Hybrid-Music-5000-Synthesiser/
http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Hybrid_Music5000.html
Hybrid Music 3000 Expander
https://www.retro-kit.co.uk/page.cfm/content/Hybrid-Music-3000-Expander/
Peartree Music 87 Synthesiser
http://www.computinghistory.org.uk/det/4535/Peartree-Computers-Music-87-Synthesizer-(M500)/
**********************************************************************/
#ifndef MAME_BUS_BBC_1MHZBUS_M5000_H
#define MAME_BUS_BBC_1MHZBUS_M5000_H
#include "1mhzbus.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> htmusic_device
class htmusic_device : public device_t, public device_sound_interface
{
public:
htmusic_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
void ram_w(offs_t offset, uint8_t data) { m_wave_ram[offset & 0x7ff] = data; }
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
private:
uint16_t m_antilog[128];
uint8_t m_wave_ram[0x800];
uint32_t m_phase_ram[0x200];
uint8_t m_counter;
uint8_t m_c4d;
uint8_t m_sign;
int16_t m_sam;
bool m_disable;
bool m_modulate;
stream_sample_t m_sam_l[16];
stream_sample_t m_sam_r[16];
emu_timer *m_dsp_timer;
sound_stream *m_stream;
};
// ======================> bbc_m500_device
class bbc_m500_device : public device_t, public device_bbc_1mhzbus_interface
{
public:
// construction/destruction
bbc_m500_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
bbc_m500_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
// device-level overrides
virtual void device_start() override;
// optional information overrides
virtual void device_add_mconfig(machine_config &config) override;
void add_common_devices(machine_config &config);
virtual uint8_t fred_r(offs_t offset) override;
virtual void fred_w(offs_t offset, uint8_t data) override;
virtual uint8_t jim_r(offs_t offset) override;
virtual void jim_w(offs_t offset, uint8_t data) override;
required_device<bbc_1mhzbus_slot_device> m_1mhzbus;
required_device<htmusic_device> m_hybrid;
uint8_t m_page;
};
// ======================> bbc_m5000_device
class bbc_m5000_device : public bbc_m500_device
{
public:
// construction/destruction
bbc_m5000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
// optional information overrides
virtual void device_add_mconfig(machine_config &config) override;
//virtual const tiny_rom_entry *device_rom_region() const override;
};
// ======================> bbc_m3000_device
class bbc_m3000_device : public bbc_m500_device
{
public:
// construction/destruction
bbc_m3000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
// optional information overrides
virtual void device_add_mconfig(machine_config &config) override;
virtual void jim_w(offs_t offset, uint8_t data) override;
};
// ======================> bbc_m87_device
class bbc_m87_device : public bbc_m500_device
{
public:
// construction/destruction
bbc_m87_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
// optional information overrides
virtual void device_add_mconfig(machine_config &config) override;
};
// device type definition
DECLARE_DEVICE_TYPE(BBC_M500, bbc_m500_device);
DECLARE_DEVICE_TYPE(BBC_M5000, bbc_m5000_device);
DECLARE_DEVICE_TYPE(BBC_M3000, bbc_m3000_device);
DECLARE_DEVICE_TYPE(BBC_M87, bbc_m87_device);
#endif /* MAME_BUS_BBC_1MHZBUS_M5000_H */