mirror of
https://github.com/holub/mame
synced 2025-06-07 21:33:45 +03:00
casio/cps200.cpp: Added a skeleton Casio CPS-2000 driver; also improved µPD934 and MSM6200 emulation. (#13591)
* machine/msm6200.cpp: Scan matrix on a timer until a change is detected. * sound/upd934g.cpp: Fixed play sample command. * casio/cz1.cpp: Added MSM6200 clock input frequency. New systems marked not working --------------- Casio CPS-2000 [BCM, Devin Acker]
This commit is contained in:
parent
8ac83f8b02
commit
ec7eb50bf6
@ -21,6 +21,8 @@ msm6200_device::msm6200_device(const machine_config &mconfig, const char *tag, d
|
|||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
void msm6200_device::device_start()
|
void msm6200_device::device_start()
|
||||||
{
|
{
|
||||||
|
m_timer = timer_alloc(FUNC(msm6200_device::scan_timer), this);
|
||||||
|
|
||||||
m_cmd = 0xf;
|
m_cmd = 0xf;
|
||||||
|
|
||||||
save_item(NAME(m_cmd));
|
save_item(NAME(m_cmd));
|
||||||
@ -39,6 +41,18 @@ void msm6200_device::device_reset()
|
|||||||
std::fill(std::begin(m_last_state), std::end(m_last_state), 0);
|
std::fill(std::begin(m_last_state), std::end(m_last_state), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
TIMER_CALLBACK_MEMBER(msm6200_device::scan_timer)
|
||||||
|
{
|
||||||
|
(++m_row) %= m_keys.size();
|
||||||
|
m_key_state = m_keys[m_row].read_safe(0);
|
||||||
|
if (m_key_state != m_last_state[m_row])
|
||||||
|
{
|
||||||
|
m_irq_cb(1);
|
||||||
|
m_timer->adjust(attotime::never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
void msm6200_device::write(offs_t offset, u8 data)
|
void msm6200_device::write(offs_t offset, u8 data)
|
||||||
{
|
{
|
||||||
@ -60,30 +74,32 @@ void msm6200_device::write(offs_t offset, u8 data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_key_state == m_last_state[m_row])
|
if (m_key_state == m_last_state[m_row])
|
||||||
|
{
|
||||||
m_irq_cb(0);
|
m_irq_cb(0);
|
||||||
|
m_timer->adjust(attotime::zero, 0, attotime::from_ticks(TIMER_RATE, clock()));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: // read velocity
|
case 1: // read velocity
|
||||||
m_key_data = m_velocity.read_safe(0x3f);
|
m_key_data = m_velocity.read_safe(0x3f);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // next row?
|
case 2:
|
||||||
(++m_row) %= m_keys.size();
|
|
||||||
// TODO: what should this one actually be?
|
// TODO: what should this one actually be?
|
||||||
// the cz1/ht6000 key MCU code outputs the result to port 1 for debugging
|
// the cz1/ht6000 key MCU code outputs the result to port 1 for debugging
|
||||||
m_key_data = m_row;
|
m_key_data = m_row;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7: // capture current row?
|
case 7: // ?
|
||||||
m_key_state = m_keys[m_row].read_safe(0);
|
// cz1/ht6000 writes this constantly, cps2000 much less frequently
|
||||||
if (m_key_state != m_last_state[m_row])
|
|
||||||
m_irq_cb(1);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 8: // init all rows
|
case 8: // init all rows, start scanning
|
||||||
for (int i = 0; i < m_keys.size(); i++)
|
for (int i = 0; i < m_keys.size(); i++)
|
||||||
m_last_state[i] = m_keys[i].read_safe(0);
|
m_last_state[i] = m_keys[i].read_safe(0);
|
||||||
|
|
||||||
m_irq_cb(0);
|
m_irq_cb(0);
|
||||||
|
m_timer->adjust(attotime::zero, 0, attotime::from_ticks(TIMER_RATE, clock()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -24,11 +24,17 @@ protected:
|
|||||||
virtual void device_reset() override ATTR_COLD;
|
virtual void device_reset() override ATTR_COLD;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr unsigned TIMER_RATE = 256; // arbitrary
|
||||||
|
|
||||||
|
TIMER_CALLBACK_MEMBER(scan_timer);
|
||||||
|
|
||||||
optional_ioport_array<38> m_keys;
|
optional_ioport_array<38> m_keys;
|
||||||
optional_ioport m_velocity;
|
optional_ioport m_velocity;
|
||||||
|
|
||||||
devcb_write_line m_irq_cb;
|
devcb_write_line m_irq_cb;
|
||||||
|
|
||||||
|
emu_timer *m_timer;
|
||||||
|
|
||||||
u8 m_cmd, m_row, m_key_data;
|
u8 m_cmd, m_row, m_key_data;
|
||||||
u8 m_key_state;
|
u8 m_key_state;
|
||||||
u8 m_last_state[38];
|
u8 m_last_state[38];
|
||||||
|
@ -53,7 +53,8 @@ void upd934g_device::device_start()
|
|||||||
m_stream = stream_alloc(0, 4, 20000);
|
m_stream = stream_alloc(0, 4, 20000);
|
||||||
|
|
||||||
// register for save states
|
// register for save states
|
||||||
save_pointer(NAME(m_addr), 16);
|
save_item(NAME(m_addr));
|
||||||
|
save_item(NAME(m_valid));
|
||||||
|
|
||||||
save_item(STRUCT_MEMBER(m_channel, pos));
|
save_item(STRUCT_MEMBER(m_channel, pos));
|
||||||
save_item(STRUCT_MEMBER(m_channel, playing));
|
save_item(STRUCT_MEMBER(m_channel, playing));
|
||||||
@ -71,8 +72,15 @@ void upd934g_device::device_reset()
|
|||||||
{
|
{
|
||||||
m_ready = false;
|
m_ready = false;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 16; i++)
|
||||||
|
m_addr[i] = m_valid[i] = 0;
|
||||||
|
|
||||||
for (unsigned i = 0; i < 4; i++)
|
for (unsigned i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
m_channel[i].pos = 0;
|
||||||
m_channel[i].playing = -1;
|
m_channel[i].playing = -1;
|
||||||
|
m_channel[i].effect = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
@ -86,14 +94,15 @@ void upd934g_device::sound_stream_update(sound_stream &stream, std::vector<read_
|
|||||||
{
|
{
|
||||||
if (m_ready && m_channel[ch].playing != -1)
|
if (m_ready && m_channel[ch].playing != -1)
|
||||||
{
|
{
|
||||||
uint16_t end = m_addr[m_channel[ch].playing + 1] - 1;
|
uint16_t end = m_addr[(m_channel[ch].playing + 1) & 0xf] - 1;
|
||||||
|
|
||||||
for (unsigned i = 0; i < outputs[ch].samples(); i++)
|
for (unsigned i = 0; i < outputs[ch].samples(); i++)
|
||||||
{
|
{
|
||||||
int16_t raw = static_cast<int8_t>(read_byte(m_channel[ch].pos)) * 4;
|
int16_t raw = static_cast<int8_t>(read_byte(m_channel[ch].pos)) * 4;
|
||||||
|
|
||||||
// normal, muted, accented
|
// normal, muted, accented
|
||||||
const double adjust[] = { 0, 0.7, 0.4, 1.0 };
|
// TODO: cps2000 regularly sets effect bits to 0 instead of 2 - are these the same?
|
||||||
|
const double adjust[] = { 0.4, 0.7, 0.4, 1.0 };
|
||||||
raw *= adjust[m_channel[ch].effect];
|
raw *= adjust[m_channel[ch].effect];
|
||||||
|
|
||||||
outputs[ch].put_int(i, raw, 32768 / 64);
|
outputs[ch].put_int(i, raw, 32768 / 64);
|
||||||
@ -118,23 +127,20 @@ void upd934g_device::sound_stream_update(sound_stream &stream, std::vector<read_
|
|||||||
|
|
||||||
void upd934g_device::write(offs_t offset, uint8_t data)
|
void upd934g_device::write(offs_t offset, uint8_t data)
|
||||||
{
|
{
|
||||||
switch (offset)
|
switch (offset & 3)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
// format of data written here is:
|
// format of data written here is:
|
||||||
// 76------ command
|
// 76------ effect
|
||||||
// --5432-- sample number
|
// --5432-- sample number
|
||||||
// ------10 output channel
|
// ------10 output channel
|
||||||
m_sample = (data >> 2) & 0x0f;
|
m_sample = (data >> 2) & 0x0f;
|
||||||
|
|
||||||
switch (data >> 6)
|
// don't play a sample unless we're sure it's actually supposed to play:
|
||||||
{
|
// - all models write this with effect=0 to select which sample to set the address of
|
||||||
case 0:
|
// - cps2000 writes this with effect=0 to play muted(?) samples
|
||||||
LOG("CMD STORE ADDRESS sample %x\n", m_sample);
|
// - cps2000 also writes this with effect=1 and sample=f on boot even though there are only 12 valid samples
|
||||||
break;
|
if (m_valid[m_sample])
|
||||||
case 1: // normal
|
|
||||||
case 2: // muted
|
|
||||||
case 3: // accented
|
|
||||||
{
|
{
|
||||||
const u8 ch = (data & 3) ^ 2; // effective order seems to be "2, 3, 0, 1"
|
const u8 ch = (data & 3) ^ 2; // effective order seems to be "2, 3, 0, 1"
|
||||||
LOG("CMD PLAY sample %x (channel %d, effect %d)\n", m_sample, ch, data >> 6);
|
LOG("CMD PLAY sample %x (channel %d, effect %d)\n", m_sample, ch, data >> 6);
|
||||||
@ -143,13 +149,12 @@ void upd934g_device::write(offs_t offset, uint8_t data)
|
|||||||
m_channel[ch].effect = data >> 6;
|
m_channel[ch].effect = data >> 6;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1:
|
case 1:
|
||||||
m_addr[m_sample] = (m_addr[m_sample] & 0xff00) | (data << 0);
|
m_addr[m_sample] = (m_addr[m_sample] & 0xff00) | (data << 0);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
m_addr[m_sample] = (m_addr[m_sample] & 0x00ff) | (data << 8);
|
m_addr[m_sample] = (m_addr[m_sample] & 0x00ff) | (data << 8);
|
||||||
|
m_valid[m_sample] = 1;
|
||||||
LOG(" sample %x address = %04x\n", m_sample, m_addr[m_sample]);
|
LOG(" sample %x address = %04x\n", m_sample, m_addr[m_sample]);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
@ -27,7 +27,7 @@ public:
|
|||||||
// construction/destruction
|
// construction/destruction
|
||||||
upd934g_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
upd934g_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||||
|
|
||||||
void write(offs_t offset, uint8_t data);
|
void write(offs_t offset, u8 data);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// device-level overrides
|
// device-level overrides
|
||||||
@ -39,18 +39,19 @@ protected:
|
|||||||
private:
|
private:
|
||||||
sound_stream *m_stream;
|
sound_stream *m_stream;
|
||||||
|
|
||||||
uint16_t m_addr[16];
|
u16 m_addr[16];
|
||||||
|
u8 m_valid[16];
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
uint16_t pos;
|
u16 pos;
|
||||||
int playing;
|
s8 playing;
|
||||||
int effect;
|
u8 effect;
|
||||||
}
|
}
|
||||||
m_channel[4];
|
m_channel[4];
|
||||||
|
|
||||||
int m_sample;
|
u8 m_sample;
|
||||||
bool m_ready;
|
u8 m_ready;
|
||||||
};
|
};
|
||||||
|
|
||||||
// device type definition
|
// device type definition
|
||||||
|
@ -83,6 +83,7 @@ const double XTAL::known_xtals[] = {
|
|||||||
2'250'000, // 2.25_MHz_XTAL Resonator - YM2154 on Yamaha PSR-60 & PSR-70
|
2'250'000, // 2.25_MHz_XTAL Resonator - YM2154 on Yamaha PSR-60 & PSR-70
|
||||||
2'376'000, // 2.376_MHz_XTAL CIT-101 keyboard
|
2'376'000, // 2.376_MHz_XTAL CIT-101 keyboard
|
||||||
2'457'600, // 2.4576_MHz_XTAL Atari ST MFP
|
2'457'600, // 2.4576_MHz_XTAL Atari ST MFP
|
||||||
|
2'470'000, // 2.47_MHz_XTAL CSA2.47MG ceramic oscillator - Casio CZ-1
|
||||||
2'500'000, // 2.5_MHz_XTAL Janken Man units
|
2'500'000, // 2.5_MHz_XTAL Janken Man units
|
||||||
2'600'000, // 2.6_MHz_XTAL Sharp PC-1500
|
2'600'000, // 2.6_MHz_XTAL Sharp PC-1500
|
||||||
2'700'000, // 2.7_MHz_XTAL Resonator - YM2154 on Yamaha RX15
|
2'700'000, // 2.7_MHz_XTAL Resonator - YM2154 on Yamaha RX15
|
||||||
@ -119,6 +120,7 @@ const double XTAL::known_xtals[] = {
|
|||||||
4'433'619, // 4.433619_MHz_XTAL PAL color subcarrier (technically 4.43361875mhz)
|
4'433'619, // 4.433619_MHz_XTAL PAL color subcarrier (technically 4.43361875mhz)
|
||||||
4'608'000, // 4.608_MHz_XTAL Luxor ABC-77 keyboard (Keytronic custom part #48-300-107 is equivalent)
|
4'608'000, // 4.608_MHz_XTAL Luxor ABC-77 keyboard (Keytronic custom part #48-300-107 is equivalent)
|
||||||
4'915'200, // 4.9152_MHz_XTAL -
|
4'915'200, // 4.9152_MHz_XTAL -
|
||||||
|
4'946'800, // 4.9468_MHz_XTAL Casio CPS-2000
|
||||||
4'946'864, // 4.946864_MHz_XTAL Casiotone 8000
|
4'946'864, // 4.946864_MHz_XTAL Casiotone 8000
|
||||||
4'952'000, // 4.952_MHz_XTAL IGS M036 based mahjong games, for TT5665 sound chip
|
4'952'000, // 4.952_MHz_XTAL IGS M036 based mahjong games, for TT5665 sound chip
|
||||||
5'000'000, // 5_MHz_XTAL Mutant Night
|
5'000'000, // 5_MHz_XTAL Mutant Night
|
||||||
|
402
src/mame/casio/cps2000.cpp
Normal file
402
src/mame/casio/cps2000.cpp
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
// license:BSD-3-Clause
|
||||||
|
// copyright-holders:Devin Acker
|
||||||
|
/*
|
||||||
|
Skeleton driver for Casio CPS-2000 piano.
|
||||||
|
|
||||||
|
Sound hardware:
|
||||||
|
- 2x uPD932 consonant-vowel synth
|
||||||
|
- uPD934 PCM rhythm
|
||||||
|
- monophonic square wave bass w/ RC envelope and filter
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "emu.h"
|
||||||
|
|
||||||
|
#include "cpu/upd7810/upd7810.h"
|
||||||
|
#include "machine/msm6200.h"
|
||||||
|
#include "sound/dac.h"
|
||||||
|
#include "sound/flt_rc.h"
|
||||||
|
#include "sound/upd934g.h"
|
||||||
|
#include "sound/va_eg.h"
|
||||||
|
#include "sound/va_vca.h"
|
||||||
|
|
||||||
|
#include "speaker.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class cps2000_state : public driver_device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cps2000_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||||
|
: driver_device(mconfig, type, tag)
|
||||||
|
, m_maincpu(*this, "maincpu")
|
||||||
|
, m_kbd(*this, "kbd")
|
||||||
|
, m_bass_env(*this, "bass_env")
|
||||||
|
, m_pcm(*this, "pcm")
|
||||||
|
, m_keys(*this, "KC%u", 0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void cps2000(machine_config &config);
|
||||||
|
|
||||||
|
void keys_w(ioport_value val) { m_key_sel = val; }
|
||||||
|
ioport_value keys_r();
|
||||||
|
|
||||||
|
void bass_w(ioport_value val);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void machine_start() override ATTR_COLD;
|
||||||
|
virtual void machine_reset() override ATTR_COLD;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void maincpu_map(address_map &map) ATTR_COLD;
|
||||||
|
|
||||||
|
void io_w(offs_t offset, u8 data);
|
||||||
|
|
||||||
|
required_device<upd78c10_device> m_maincpu;
|
||||||
|
required_device<msm6200_device> m_kbd;
|
||||||
|
required_device<va_rc_eg_device> m_bass_env;
|
||||||
|
required_device<upd934g_device> m_pcm;
|
||||||
|
|
||||||
|
required_ioport_array<5> m_keys;
|
||||||
|
|
||||||
|
ioport_value m_key_sel;
|
||||||
|
|
||||||
|
u8 m_932a_regs[256];
|
||||||
|
u8 m_932b_regs[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
static INPUT_PORTS_START(cps2000)
|
||||||
|
PORT_START("PA")
|
||||||
|
PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(cps2000_state::keys_w))
|
||||||
|
PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_OUTPUT ) // LED outputs
|
||||||
|
|
||||||
|
PORT_START("PB")
|
||||||
|
PORT_BIT( 0x3f, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(cps2000_state::keys_r))
|
||||||
|
PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_OUTPUT ) // LED outputs
|
||||||
|
|
||||||
|
PORT_START("PC")
|
||||||
|
PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_OUTPUT ) // LED outputs
|
||||||
|
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_CUSTOM ) // 934 busy
|
||||||
|
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(cps2000_state::bass_w))
|
||||||
|
PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )
|
||||||
|
|
||||||
|
PORT_START("kbd:KI1")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G7")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#7")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI2")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F7")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E7")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI3")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#7")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D7")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI4")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#7")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C7")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI5")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B6")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#6")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI6")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A6")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#6")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI7")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G6")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#6")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI8")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F6")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E6")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI9")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#6")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D6")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI10")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#6")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C6")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI11")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B5")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#5")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI12")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A5")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#5")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI13")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G5")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#5")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI14")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F5")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E5")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI15")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#5")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D5")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI16")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#5")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C5")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI17")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B4")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#4")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI18")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A4")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#4")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI19")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G4")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#4")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI20")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F4")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E4")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI21")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#4")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D4")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI22")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#4")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C4")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI23")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B3")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#3")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI24")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A3")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#3")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI25")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G3")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#3")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI26")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F3")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E3")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI27")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#3")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D3")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI28")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#3")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C3")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI29")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B2")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#2")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI30")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A2")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#2")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI31")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G2")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#2")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI32")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F2")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E2")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI33")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D#2")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D2")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI34")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C#2")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C2")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI35")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B1")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A#1")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI36")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A1")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G#1")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI37")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G1")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F#1")
|
||||||
|
|
||||||
|
PORT_START("kbd:KI38")
|
||||||
|
PORT_BIT(0x1, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F1")
|
||||||
|
PORT_BIT(0x2, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E1")
|
||||||
|
|
||||||
|
PORT_START("kbd:VELOCITY")
|
||||||
|
PORT_BIT(0x3f, 0x3f, IPT_POSITIONAL_V) PORT_NAME("Key Velocity") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)
|
||||||
|
|
||||||
|
PORT_START("AN0")
|
||||||
|
PORT_BIT(0xff, 0x80, IPT_POSITIONAL_V) PORT_NAME("Tempo") PORT_SENSITIVITY(10) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)
|
||||||
|
|
||||||
|
PORT_START("AN1")
|
||||||
|
PORT_BIT(0xff, 0x80, IPT_POSITIONAL_H) PORT_NAME("Tuning") PORT_SENSITIVITY(10) PORT_KEYDELTA(5) PORT_CENTERDELTA(0) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_X_LEFT_SWITCH) PORT_CODE_INC(JOYCODE_X_RIGHT_SWITCH)
|
||||||
|
|
||||||
|
PORT_START("KC0")
|
||||||
|
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Octave Unison")
|
||||||
|
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Sustain")
|
||||||
|
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Chorus")
|
||||||
|
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Fingered")
|
||||||
|
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Casio Chord On")
|
||||||
|
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Casio Chord Off")
|
||||||
|
PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
|
||||||
|
|
||||||
|
PORT_START("KC1")
|
||||||
|
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Start / Stop")
|
||||||
|
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Synchro")
|
||||||
|
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Intro / Fill-In")
|
||||||
|
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Super Accomp")
|
||||||
|
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Memory")
|
||||||
|
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Variation")
|
||||||
|
PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
|
||||||
|
|
||||||
|
PORT_START("KC2")
|
||||||
|
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 1")
|
||||||
|
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 2")
|
||||||
|
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 3")
|
||||||
|
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 4")
|
||||||
|
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm Select")
|
||||||
|
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Touch Response")
|
||||||
|
PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
|
||||||
|
|
||||||
|
PORT_START("KC3")
|
||||||
|
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 5")
|
||||||
|
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 6")
|
||||||
|
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone 7")
|
||||||
|
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 1")
|
||||||
|
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 2")
|
||||||
|
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 3")
|
||||||
|
PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
|
||||||
|
|
||||||
|
PORT_START("KC4")
|
||||||
|
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone Select")
|
||||||
|
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Sustain Pedal")
|
||||||
|
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
|
||||||
|
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 4")
|
||||||
|
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 5")
|
||||||
|
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm 6")
|
||||||
|
PORT_BIT( 0xc0, IP_ACTIVE_HIGH, IPT_UNUSED )
|
||||||
|
|
||||||
|
INPUT_PORTS_END
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
void cps2000_state::maincpu_map(address_map &map)
|
||||||
|
{
|
||||||
|
map(0x0000, 0x7fff).rom();
|
||||||
|
map(0x8000, 0xbfff).r(m_kbd, FUNC(msm6200_device::read));
|
||||||
|
map(0x8000, 0xffff).w(FUNC(cps2000_state::io_w));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
void cps2000_state::cps2000(machine_config &config)
|
||||||
|
{
|
||||||
|
UPD78C10(config, m_maincpu, 10'000'000);
|
||||||
|
m_maincpu->set_addrmap(AS_PROGRAM, &cps2000_state::maincpu_map);
|
||||||
|
m_maincpu->pa_out_cb().set_ioport("PA");
|
||||||
|
m_maincpu->pb_in_cb().set_ioport("PB");
|
||||||
|
m_maincpu->pb_out_cb().set_ioport("PB");
|
||||||
|
m_maincpu->pc_in_cb().set_ioport("PC");
|
||||||
|
m_maincpu->pc_out_cb().set_ioport("PC");
|
||||||
|
m_maincpu->an0_func().set_ioport("AN0");
|
||||||
|
m_maincpu->an1_func().set_ioport("AN1");
|
||||||
|
|
||||||
|
MSM6200(config, m_kbd, 4.9468_MHz_XTAL);
|
||||||
|
m_kbd->irq_cb().set_inputline(m_maincpu, UPD7810_INTF1);
|
||||||
|
|
||||||
|
SPEAKER(config, "lspeaker").front_left();
|
||||||
|
SPEAKER(config, "rspeaker").front_right();
|
||||||
|
|
||||||
|
// UPD932(config, "upd932a", 4.9468_MHz_XTAL);
|
||||||
|
// UPD932(config, "upd932b", 4.9468_MHz_XTAL);
|
||||||
|
|
||||||
|
DAC_1BIT(config, "bass").add_route(0, "bass_vca", 1.0);
|
||||||
|
m_maincpu->co0_func().set("bass", FUNC(dac_1bit_device::write));
|
||||||
|
|
||||||
|
VA_RC_EG(config, m_bass_env).set_c(CAP_U(3.3)).add_route(0, "bass_vca", 1.0 / 5);
|
||||||
|
VA_VCA(config, "bass_vca").configure_streaming_cv(true).add_route(0, "bass_rc1", 1.0);
|
||||||
|
|
||||||
|
FILTER_RC(config, "bass_rc1").set_lowpass(RES_K(47), CAP_N(47)).add_route(0, "bass_rc2", 1.0);
|
||||||
|
FILTER_RC(config, "bass_rc2").set_lowpass(RES_K(22), CAP_N(47))
|
||||||
|
.add_route(0, "lspeaker", 0.25).add_route(0, "rspeaker", 0.25);
|
||||||
|
|
||||||
|
UPD934G(config, m_pcm, 4.9468_MHz_XTAL / 4);
|
||||||
|
m_pcm->add_route(0, "lspeaker", 0.25);
|
||||||
|
m_pcm->add_route(0, "rspeaker", 0.125);
|
||||||
|
m_pcm->add_route(1, "lspeaker", 0.25);
|
||||||
|
m_pcm->add_route(1, "rspeaker", 0.125);
|
||||||
|
m_pcm->add_route(2, "lspeaker", 0.125);
|
||||||
|
m_pcm->add_route(2, "rspeaker", 0.25);
|
||||||
|
m_pcm->add_route(3, "lspeaker", 0.125);
|
||||||
|
m_pcm->add_route(3, "rspeaker", 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
void cps2000_state::machine_start()
|
||||||
|
{
|
||||||
|
m_key_sel = 0;
|
||||||
|
|
||||||
|
std::fill(std::begin(m_932a_regs), std::end(m_932a_regs), 0);
|
||||||
|
std::fill(std::begin(m_932b_regs), std::end(m_932b_regs), 0);
|
||||||
|
|
||||||
|
save_item(NAME(m_key_sel));
|
||||||
|
save_item(NAME(m_932a_regs));
|
||||||
|
save_item(NAME(m_932b_regs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
void cps2000_state::machine_reset()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
ioport_value cps2000_state::keys_r()
|
||||||
|
{
|
||||||
|
if (m_key_sel < 5)
|
||||||
|
return m_keys[m_key_sel]->read();
|
||||||
|
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
void cps2000_state::bass_w(ioport_value val)
|
||||||
|
{
|
||||||
|
if (val)
|
||||||
|
m_bass_env->set_r(RES_R(270)).set_target_v(5.0);
|
||||||
|
else
|
||||||
|
m_bass_env->set_r(RES_K(22)).set_target_v(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
void cps2000_state::io_w(offs_t offset, u8 data)
|
||||||
|
{
|
||||||
|
if (!BIT(offset, 14))
|
||||||
|
m_kbd->write(offset, data);
|
||||||
|
if (!BIT(offset, 13))
|
||||||
|
m_pcm->write(offset >> 8, offset);
|
||||||
|
if (!BIT(offset, 12))
|
||||||
|
m_932a_regs[offset & 0xff] = data;
|
||||||
|
if (!BIT(offset, 11))
|
||||||
|
m_932b_regs[offset & 0xff] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
ROM_START( cps2000 )
|
||||||
|
ROM_REGION(0x8000, "maincpu", 0)
|
||||||
|
ROM_LOAD( "hn613256p-db6.bin", 0x0000, 0x8000, CRC(92b68074) SHA1(eda13d59a15077b06d95004b5f2a2a8432109893) )
|
||||||
|
|
||||||
|
ROM_REGION(0x10000, "pcm", 0)
|
||||||
|
ROM_LOAD( "hn613256p-da5.bin", 0x0000, 0x8000, CRC(63d6dd54) SHA1(231ffb6a4cc113133accd19dd9ed1e5165e3837e) )
|
||||||
|
ROM_END
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
SYST( 1986, cps2000, 0, 0, cps2000, cps2000, cps2000_state, empty_init, "Casio", "CPS-2000", MACHINE_NOT_WORKING )
|
@ -910,7 +910,7 @@ void cz1_state::cz1(machine_config &config)
|
|||||||
m_mcu->t0_in_cb().set(FUNC(cz1_state::sync49_r));
|
m_mcu->t0_in_cb().set(FUNC(cz1_state::sync49_r));
|
||||||
m_mcu->t1_in_cb().set([this] () { return BIT(m_main_port[2], 7); });
|
m_mcu->t1_in_cb().set([this] () { return BIT(m_main_port[2], 7); });
|
||||||
|
|
||||||
MSM6200(config, "kbd").irq_cb().set_inputline(m_mcu, MCS48_INPUT_IRQ);
|
MSM6200(config, "kbd", 2.47_MHz_XTAL).irq_cb().set_inputline(m_mcu, MCS48_INPUT_IRQ); // CSA2.47MG ceramic oscillator
|
||||||
|
|
||||||
config.set_default_layout(layout_cz1);
|
config.set_default_layout(layout_cz1);
|
||||||
}
|
}
|
||||||
|
@ -16189,6 +16189,9 @@ casloopy
|
|||||||
cfx9850
|
cfx9850
|
||||||
cfx9850gb
|
cfx9850gb
|
||||||
|
|
||||||
|
@source:casio/cps2000.cpp
|
||||||
|
cps2000
|
||||||
|
|
||||||
@source:casio/ct8000.cpp
|
@source:casio/ct8000.cpp
|
||||||
ct8000
|
ct8000
|
||||||
ctfk1
|
ctfk1
|
||||||
|
Loading…
Reference in New Issue
Block a user