summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/src/sound.lua12
-rw-r--r--src/devices/bus/nscsi/hd.cpp22
-rw-r--r--src/devices/machine/mb87030.cpp9
-rw-r--r--src/devices/sound/fz_pcm.cpp427
-rw-r--r--src/devices/sound/fz_pcm.h113
-rw-r--r--src/mame/casio/fz1.cpp65
6 files changed, 636 insertions, 12 deletions
diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua
index a1a23792cd0..7f7d7506fe5 100644
--- a/scripts/src/sound.lua
+++ b/scripts/src/sound.lua
@@ -1809,3 +1809,15 @@ if (SOUNDS["ADC"]~=null) then
MAME_DIR .. "src/devices/sound/adc.h",
}
end
+
+---------------------------------------------------
+-- Casio FZ-series PCM
+--@src/devices/sound/fz_pcm.h,SOUNDS["FZ_PCM"] = true
+---------------------------------------------------
+
+if (SOUNDS["FZ_PCM"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/sound/fz_pcm.cpp",
+ MAME_DIR .. "src/devices/sound/fz_pcm.h",
+ }
+end
diff --git a/src/devices/bus/nscsi/hd.cpp b/src/devices/bus/nscsi/hd.cpp
index fe5fc45127b..75767e7b701 100644
--- a/src/devices/bus/nscsi/hd.cpp
+++ b/src/devices/bus/nscsi/hd.cpp
@@ -136,8 +136,15 @@ void nscsi_harddisk_device::scsi_command()
LOG("command WRITE start=%08x blocks=%04x\n", lba, blocks);
- scsi_data_out(2, blocks*bytes_per_sector);
- scsi_status_complete(SS_GOOD);
+ if(image->write(lba, block)) {
+ scsi_data_out(2, blocks*bytes_per_sector);
+ scsi_status_complete(SS_GOOD);
+ }
+ else
+ {
+ scsi_status_complete(SS_CHECK_CONDITION);
+ sense(false, SK_ILLEGAL_REQUEST, SK_ASC_INVALID_FIELD_IN_CDB);
+ }
break;
case SC_INQUIRY: {
@@ -486,8 +493,15 @@ void nscsi_harddisk_device::scsi_command()
LOG("command WRITE EXTENDED start=%08x blocks=%04x\n", lba, blocks);
- scsi_data_out(2, blocks*bytes_per_sector);
- scsi_status_complete(SS_GOOD);
+ if(image->write(lba, block)) {
+ scsi_data_out(2, blocks*bytes_per_sector);
+ scsi_status_complete(SS_GOOD);
+ }
+ else
+ {
+ scsi_status_complete(SS_CHECK_CONDITION);
+ sense(false, SK_ILLEGAL_REQUEST, SK_ASC_INVALID_FIELD_IN_CDB);
+ }
break;
case SC_FORMAT_UNIT:
diff --git a/src/devices/machine/mb87030.cpp b/src/devices/machine/mb87030.cpp
index ad60af82e42..9266a81821a 100644
--- a/src/devices/machine/mb87030.cpp
+++ b/src/devices/machine/mb87030.cpp
@@ -276,6 +276,7 @@ void mb87030_device::step(bool timeout)
if ((m_sctl & SCTL_RESET_AND_DISABLE) && m_state != State::Idle) {
scsi_set_ctrl(0, S_ALL);
m_ssts &= ~SSTS_SPC_BUSY;
+ m_fifo.clear();
update_state(State::Idle);
return;
}
@@ -383,8 +384,9 @@ void mb87030_device::step(bool timeout)
if (m_scsi_phase != (ctrl & S_PHASE_MASK)) {
LOG("SCSI phase change during transfer\n");
- m_ints |= INTS_SERVICE_REQUIRED;
+ m_ints |= (INTS_SERVICE_REQUIRED | INTS_COMMAND_COMPLETE);
m_ssts &= ~SSTS_SPC_BUSY;
+ scsi_bus->data_w(scsi_refid, 0);
update_ints();
update_state(State::Idle);
break;
@@ -583,6 +585,11 @@ void mb87030_device::scmd_w(uint8_t data)
LOG("%s: %02X\n", __FUNCTION__, data);
m_scmd = data;
+ if (!(m_sctl & SCTL_RESET_AND_DISABLE))
+ {
+ scsi_set_ctrl((m_scmd & SCMD_RST_OUT) ? S_RST : 0, S_RST);
+ }
+
switch (m_scmd & SCMD_CMD_MASK) {
case SCMD_CMD_BUS_RELEASE:
LOG("%s: Bus release\n", __FUNCTION__);
diff --git a/src/devices/sound/fz_pcm.cpp b/src/devices/sound/fz_pcm.cpp
new file mode 100644
index 00000000000..fce3df53634
--- /dev/null
+++ b/src/devices/sound/fz_pcm.cpp
@@ -0,0 +1,427 @@
+// license:BSD-3-Clause
+// copyright-holders:Devin Acker
+
+/*
+ Casio FZ series PCM
+
+ This hardware actually comprises two main gate arrays (GAA and GAB)
+ which each handle about half of the address generation & timing logic
+ for 8 PCM voices. An additional gate array (GAX) demultiplexes the
+ sample RAM output for each voice.
+
+ TODO:
+ - crossfade loop support
+ - sampling (writes 16-bit mic/line input to sample RAM)
+*/
+
+#include "emu.h"
+
+#include "fz_pcm.h"
+
+#include <climits>
+
+DEFINE_DEVICE_TYPE(FZ_PCM, fz_pcm_device, "fz_pcm", "Casio FZ PCM")
+
+fz_pcm_device::fz_pcm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, FZ_PCM, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , device_memory_interface(mconfig, *this)
+ , m_irq_cb(*this)
+ , m_ram_config("ram", ENDIANNESS_LITTLE, 16, 21, -1)
+{
+}
+
+/**************************************************************************/
+void fz_pcm_device::device_start()
+{
+ m_stream = stream_alloc(1, 8, clock() / CLOCKS_PER_SAMPLE);
+
+ m_irq_timer = timer_alloc(FUNC(fz_pcm_device::timer_tick), this);
+
+ space().specific(m_ram);
+
+ save_item(NAME(m_gaa_param));
+ save_item(NAME(m_gab_param));
+ save_item(NAME(m_gaa_cmd));
+ save_item(NAME(m_gab_cmd));
+
+ save_item(NAME(m_irq_stat));
+
+ save_item(STRUCT_MEMBER(m_voices, m_flags));
+
+ save_item(STRUCT_MEMBER(m_voices, m_addr_start));
+ save_item(STRUCT_MEMBER(m_voices, m_addr_end));
+ save_item(STRUCT_MEMBER(m_voices, m_loop_start));
+ save_item(STRUCT_MEMBER(m_voices, m_loop_end));
+ save_item(STRUCT_MEMBER(m_voices, m_loop_start_fine));
+ save_item(STRUCT_MEMBER(m_voices, m_loop_len));
+ save_item(STRUCT_MEMBER(m_voices, m_loop_trace));
+ save_item(STRUCT_MEMBER(m_voices, m_loop_xfade));
+
+ save_item(STRUCT_MEMBER(m_voices, m_pitch));
+ save_item(STRUCT_MEMBER(m_voices, m_addr));
+ save_item(STRUCT_MEMBER(m_voices, m_addr_frac));
+
+ save_item(STRUCT_MEMBER(m_voices, m_sample));
+ save_item(STRUCT_MEMBER(m_voices, m_sample_last));
+}
+
+/**************************************************************************/
+void fz_pcm_device::device_reset()
+{
+ m_gaa_param[0] = m_gaa_param[1] = m_gaa_param[2] = 0;
+ m_gab_param[0] = m_gab_param[1] = 0;
+ m_gaa_cmd = m_gab_cmd = 0;
+ m_voice_mask = 0;
+
+ m_irq_stat = 0;
+ m_irq_timer->adjust(attotime::never);
+ m_irq_cb(0);
+
+ for (voice_t &v : m_voices)
+ v = voice_t();
+}
+
+/**************************************************************************/
+void fz_pcm_device::device_clock_changed()
+{
+ m_stream->set_sample_rate(clock() / CLOCKS_PER_SAMPLE);
+ update_pending_irq();
+}
+
+/**************************************************************************/
+device_memory_interface::space_config_vector fz_pcm_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(0, &m_ram_config)
+ };
+}
+
+/**************************************************************************/
+TIMER_CALLBACK_MEMBER(fz_pcm_device::timer_tick)
+{
+ m_irq_cb(1);
+}
+
+/**************************************************************************/
+void fz_pcm_device::sound_stream_update(sound_stream &stream)
+{
+ for (int s = 0; s < stream.samples(); s++)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ voice_t &v = m_voices[i];
+
+ if (BIT(v.m_flags, FLAG_PLAY))
+ {
+ v.m_addr_frac += v.m_pitch;
+ if (v.m_addr_frac >= (1 << ADDR_FRAC_SHIFT))
+ {
+ v.m_sample_last = v.m_sample;
+ v.m_sample = (s16)m_ram.read_word(v.m_addr);
+
+ if (v.update())
+ m_irq_stat |= (1 << i);
+ }
+ }
+
+ s16 sample = 0;
+ if (BIT(v.m_flags, FLAG_OUTPUT))
+ {
+ const u8 frac = BIT(v.m_addr_frac, ADDR_FRAC_SHIFT - 3, 3);
+ sample = v.m_sample_last + (s32(v.m_sample - v.m_sample_last) * frac / 8);
+ }
+
+ stream.put_int_clamp(i, s, sample, 1 << 15);
+ }
+ }
+}
+
+/**************************************************************************/
+bool fz_pcm_device::voice_t::update()
+{
+ bool looped = false;
+
+ while (m_addr_frac >= (1 << ADDR_FRAC_SHIFT))
+ {
+ m_addr_frac -= (1 << ADDR_FRAC_SHIFT);
+
+ if (!BIT(m_flags, FLAG_REVERSE))
+ {
+ if (m_addr < m_addr_end)
+ {
+ m_addr++;
+
+ if (BIT(m_flags, FLAG_LOOP) && m_addr >= m_loop_end)
+ {
+ looped = BIT(m_flags, FLAG_INT);
+
+ if (m_loop_trace)
+ {
+ // next=trace: continue as normal
+ m_addr = m_loop_start + (m_addr - m_loop_end);
+ m_addr_frac += m_loop_start_fine << (ADDR_FRAC_SHIFT - 8);
+ }
+ else
+ {
+ // next=skip: jump to exact loop start, stop further interrupts
+ m_addr = m_loop_start;
+ m_addr_frac = m_loop_start_fine << (ADDR_FRAC_SHIFT - 8);
+ m_flags &= ~(1 << FLAG_INT);
+ }
+ }
+ }
+ else
+ {
+ m_flags &= ~(1 << FLAG_PLAY);
+ }
+ }
+ else
+ {
+ if (m_addr > m_addr_end)
+ m_addr--;
+ else
+ m_flags &= ~(1 << FLAG_PLAY);
+ }
+ }
+
+ return looped;
+}
+
+/**************************************************************************/
+bool fz_pcm_device::voice_t::calc_timeout(u32 &samples) const
+{
+ // don't update interrupt timer if this voice isn't set to interrupt
+ if (!BIT(m_flags, FLAG_PLAY)
+ || !BIT(m_flags, FLAG_LOOP)
+ || !BIT(m_flags, FLAG_INT)
+ || BIT(m_flags, FLAG_REVERSE)
+ || !m_pitch)
+ {
+ return false;
+ }
+
+ if (m_addr >= m_loop_end)
+ {
+ // loop end is somehow in the past
+ samples = 0;
+ }
+ else
+ {
+ // calculate number of output samples until loop end is reached
+ const u64 to_loop = ((u64(m_loop_end - m_addr) << ADDR_FRAC_SHIFT) - m_addr_frac + m_pitch - 1) / m_pitch;
+ if (to_loop < samples)
+ samples = to_loop;
+ }
+
+ return true;
+}
+
+/**************************************************************************/
+void fz_pcm_device::update_pending_irq()
+{
+ bool pending = false;
+ u32 new_time = UINT_MAX;
+
+ for (voice_t &v : m_voices)
+ pending |= v.calc_timeout(new_time);
+
+ if (pending)
+ m_irq_timer->adjust(clocks_to_attotime((u64)new_time * CLOCKS_PER_SAMPLE));
+ else
+ m_irq_timer->adjust(attotime::never);
+}
+
+/**************************************************************************/
+u16 fz_pcm_device::gaa_r(offs_t offset)
+{
+ offset &= 3;
+
+ switch (offset & 3)
+ {
+ default:
+ return m_gaa_param[offset & 3];
+
+ case 3:
+ return m_gaa_cmd;
+ }
+}
+
+/**************************************************************************/
+u16 fz_pcm_device::gab_r(offs_t offset)
+{
+ switch (offset & 3)
+ {
+ default:
+ return m_gab_param[offset & 3];
+
+ case 2:
+ if (!machine().side_effects_disabled())
+ m_stream->update();
+ return m_irq_stat;
+
+ case 3:
+ return m_gab_cmd;
+ }
+}
+
+/**************************************************************************/
+void fz_pcm_device::gaa_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ m_stream->update();
+
+ switch (offset & 3)
+ {
+ default:
+ COMBINE_DATA(&m_gaa_param[offset & 3]);
+ break;
+
+ case 3:
+ COMBINE_DATA(&m_gaa_cmd);
+ if (ACCESSING_BITS_8_15)
+ {
+ m_voice_mask = m_gaa_cmd & 0xff;
+
+ switch (m_gaa_cmd >> 8)
+ {
+ case 0x40: voice_cmd(CMD_ADDR_START); break;
+ case 0x41: voice_cmd(CMD_LOOP_START); break;
+ case 0x42: voice_cmd(CMD_PITCH); break;
+ case 0x43: voice_cmd(CMD_LOOP_LEN); break;
+ case 0x45: voice_cmd(CMD_LOOP_XFADE); break;
+ case 0x80: voice_cmd(CMD_GET_ADDR); break;
+
+ default:
+ logerror("%s: unknown GAA cmd %04x (param %04x %04x %04x)\n", machine().describe_context(),
+ m_gaa_cmd, m_gaa_param[0], m_gaa_param[1], m_gaa_param[2]);
+ break;
+ }
+
+ update_pending_irq();
+ }
+ break;
+ }
+}
+
+/**************************************************************************/
+void fz_pcm_device::gab_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ m_stream->update();
+
+ switch (offset & 3)
+ {
+ case 0:
+ case 1:
+ COMBINE_DATA(&m_gab_param[offset & 3]);
+ break;
+
+ case 3:
+ COMBINE_DATA(&m_gab_cmd);
+ if (ACCESSING_BITS_8_15)
+ {
+ m_voice_mask = m_gab_cmd & 0xff;
+
+ switch (m_gab_cmd >> 8)
+ {
+ case 0x20: /* TODO: recording rate */ break;
+ case 0x28: /* TODO: recording cue */ break;
+ case 0x30: /* TODO: recording start */ break;
+ case 0x38: /* TODO: recording stop */ break;
+ case 0x40: voice_cmd(CMD_ADDR_END); break;
+ case 0x41: voice_cmd(CMD_LOOP_END); break;
+ case 0x42: voice_cmd(CMD_FLAG_SET); break;
+ case 0x43: voice_cmd(CMD_FLAG_CLR); break;
+ case 0x44: voice_cmd(CMD_LOOP_TRACE); break;
+
+ case 0x46:
+ m_irq_stat &= m_gab_param[0];
+ if (!m_irq_stat)
+ m_irq_cb(0);
+ break;
+
+ case 0x82:
+ m_gab_param[0] = 0;
+ voice_cmd(CMD_GET_STATUS);
+ break;
+
+ default:
+ logerror("%s: unknown GAB cmd %04x (param %04x %04x)\n", machine().describe_context(),
+ m_gab_cmd, m_gab_param[0], m_gab_param[1]);
+ break;
+ }
+
+ update_pending_irq();
+ }
+ break;
+ }
+}
+
+/**************************************************************************/
+void fz_pcm_device::voice_cmd(unsigned cmd)
+{
+ for (int i = 0; i < 8; i++)
+ {
+ if (!BIT(m_voice_mask, i))
+ continue;
+
+ voice_t &v = m_voices[i];
+
+ switch (cmd)
+ {
+ case CMD_ADDR_START:
+ v.m_addr_start = m_gaa_param[1] | (m_gaa_param[2] << 16);
+ v.m_addr = v.m_addr_start;
+ v.m_addr_frac = 0;
+ v.m_sample = v.m_sample_last = 0;
+ break;
+
+ case CMD_ADDR_END:
+ v.m_addr_end = m_gab_param[0] | (m_gab_param[1] << 16);
+ break;
+
+ case CMD_LOOP_START:
+ v.m_loop_start = m_gaa_param[0] | (m_gaa_param[1] << 16);
+ v.m_loop_start_fine = m_gaa_param[2] & 0xff;
+ break;
+
+ case CMD_LOOP_END:
+ v.m_loop_end = m_gab_param[0] | (m_gab_param[1] << 16);
+ break;
+
+ case CMD_LOOP_LEN:
+ // TODO: what is this used for? it only has half the resolution of the actual loop start/end points
+ v.m_loop_len = (m_gaa_param[0] << 1) | (m_gaa_param[1] << 17);
+ break;
+
+ case CMD_LOOP_TRACE:
+ v.m_loop_trace = m_gab_param[0] & 1;
+ break;
+
+ case CMD_LOOP_XFADE:
+ v.m_loop_xfade = m_gaa_param[0];
+ break;
+
+ case CMD_PITCH:
+ v.m_pitch = m_gaa_param[0];
+ break;
+
+ case CMD_FLAG_SET:
+ v.m_flags |= m_gab_param[0];
+ break;
+
+ case CMD_FLAG_CLR:
+ v.m_flags &= ~m_gab_param[0];
+ break;
+
+ case CMD_GET_ADDR:
+ m_gaa_param[1] = v.m_addr;
+ m_gaa_param[2] = v.m_addr >> 16;
+ break;
+
+ case CMD_GET_STATUS:
+ // TODO: verify
+ m_gab_param[0] |= v.m_flags;
+ break;
+ }
+ }
+}
diff --git a/src/devices/sound/fz_pcm.h b/src/devices/sound/fz_pcm.h
new file mode 100644
index 00000000000..0736429da06
--- /dev/null
+++ b/src/devices/sound/fz_pcm.h
@@ -0,0 +1,113 @@
+// license:BSD-3-Clause
+// copyright-holders:Devin Acker
+#ifndef MAME_SOUND_FZ_PCM_H
+#define MAME_SOUND_FZ_PCM_H
+
+#pragma once
+
+#include "dimemory.h"
+
+class fz_pcm_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ static constexpr feature_type imperfect_features() { return feature::SOUND; }
+
+ fz_pcm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ auto irq_cb() { return m_irq_cb.bind(); }
+
+ u16 gaa_r(offs_t offset);
+ u16 gab_r(offs_t offset);
+
+ void gaa_w(offs_t offset, u16 data, u16 mem_mask = 0xffff);
+ void gab_w(offs_t offset, u16 data, u16 mem_mask = 0xffff);
+
+protected:
+ // device_t overrides
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+ virtual void device_clock_changed() override;
+
+ // device_memory_interface overrides
+ virtual space_config_vector memory_space_config() const override;
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream) override;
+
+private:
+ static constexpr unsigned CLOCKS_PER_SAMPLE = 384;
+ static constexpr unsigned ADDR_FRAC_SHIFT = 13;
+
+ struct voice_t
+ {
+ bool update();
+ bool calc_timeout(u32 &samples) const;
+
+ u16 m_flags = 0;
+
+ u32 m_addr_start = 0, m_addr_end = 0;
+ u32 m_loop_start = 0, m_loop_end = 0;
+ u8 m_loop_start_fine = 0;
+ u32 m_loop_len = 0;
+ u8 m_loop_trace = 1;
+ u16 m_loop_xfade = 0;
+
+ u16 m_pitch = 0;
+ u32 m_addr = 0, m_addr_frac = 0;
+ s16 m_sample = 0, m_sample_last = 0;
+ };
+
+ enum
+ {
+ CMD_ADDR_START,
+ CMD_ADDR_END,
+ CMD_LOOP_START,
+ CMD_LOOP_END,
+ CMD_LOOP_LEN,
+ CMD_LOOP_TRACE,
+ CMD_LOOP_XFADE,
+ CMD_PITCH,
+ CMD_FLAG_SET,
+ CMD_FLAG_CLR,
+ CMD_GET_ADDR,
+ CMD_GET_STATUS,
+ };
+
+ enum
+ {
+ FLAG_PLAY = 0,
+ FLAG_LOOP = 1,
+ FLAG_REVERSE = 3,
+ FLAG_OUTPUT = 4,
+ FLAG_INT = 5,
+ FLAG_XFADE = 6,
+ };
+
+ TIMER_CALLBACK_MEMBER(timer_tick);
+
+ void update_pending_irq();
+ void voice_cmd(unsigned cmd);
+
+ sound_stream* m_stream;
+
+ devcb_write_line m_irq_cb;
+ emu_timer *m_irq_timer;
+
+ address_space_config m_ram_config;
+ memory_access<21, 1, -1, ENDIANNESS_LITTLE>::specific m_ram;
+
+ u16 m_gaa_param[3];
+ u16 m_gab_param[2];
+ u16 m_gaa_cmd, m_gab_cmd;
+ u8 m_voice_mask;
+
+ u8 m_irq_stat;
+
+ voice_t m_voices[8];
+};
+
+DECLARE_DEVICE_TYPE(FZ_PCM, fz_pcm_device)
+
+#endif // MAME_SOUND_FZ_PCM_H
diff --git a/src/mame/casio/fz1.cpp b/src/mame/casio/fz1.cpp
index aea73b07f12..ce23f4a8eb5 100644
--- a/src/mame/casio/fz1.cpp
+++ b/src/mame/casio/fz1.cpp
@@ -19,6 +19,11 @@
A good deal of hardware and programming info is available courtesy of Rainer Buchty:
http://www.buchty.net/casio/
+
+ TODO:
+ - add mic & line in/cassette, connect to PCM hardware & filter
+ - audio input peak detection (connected to AN0)
+ - filters
*/
#include "emu.h"
@@ -35,6 +40,8 @@
#include "machine/msm6200.h"
#include "machine/ram.h"
#include "machine/upd765.h"
+#include "sound/flt_biquad.h"
+#include "sound/fz_pcm.h"
#include "video/hd44352.h"
#include "emupal.h"
@@ -54,6 +61,9 @@ public:
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_subcpu(*this, "subcpu")
+ , m_pcm(*this, "pcm")
+ , m_filter(*this, "filter_%u", 1)
+ , m_line_out(*this, "line_out_%u", 1)
, m_ram(*this, "ram")
, m_ram_bank(*this, "ram_bank")
, m_io(*this, "io%u", 0u)
@@ -70,6 +80,9 @@ public:
void fz10m(machine_config &config);
void fz20m(machine_config &config);
+ void mem_w(offs_t offset, u8 data) { m_maincpu->space(AS_PROGRAM).write_byte(offset, data); }
+ u8 mem_r(offs_t offset) { return m_maincpu->space(AS_PROGRAM).read_byte(offset); }
+
u8 fdc_irq_r() { return m_fdc->get_irq() ? 1 : 0; }
void fdc_control_w(u8 data);
@@ -98,6 +111,7 @@ private:
void gal_w(u8 data);
void led_w(u8 data);
+ void dca_w(offs_t offset, u8 data);
// main CPU / key MCU comm methods
u8 subcpu_r();
@@ -106,6 +120,10 @@ private:
required_device<v50_device> m_maincpu;
optional_device<i8049_device> m_subcpu;
+ required_device<fz_pcm_device> m_pcm;
+ required_device_array<filter_biquad_device, 8> m_filter;
+ required_device_array<speaker_device, 8> m_line_out;
+
required_device<ram_device> m_ram;
required_device<address_map_bank_device> m_ram_bank;
@@ -131,6 +149,8 @@ private:
u8 m_lcd_data;
u8 m_lcd_data_phase;
u8 m_lcd_nibble;
+
+ u16 m_dca_level[8];
};
/**************************************************************************/
@@ -194,10 +214,10 @@ static INPUT_PORTS_START(fz10m)
PORT_BIT(0xff, 0x00, IPT_CUSTOM) // audio input peak level
PORT_START("AN5")
- PORT_BIT(0xff, 0xff, IPT_POSITIONAL_V) PORT_NAME("Master Volume") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)
+ PORT_BIT(0xff, 0xff, IPT_POSITIONAL_V) PORT_NAME("Master Volume") PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)
PORT_START("AN6")
- PORT_BIT(0xff, 0x80, IPT_POSITIONAL_V) PORT_NAME("Value") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)
+ PORT_BIT(0xff, 0x80, IPT_POSITIONAL_V) PORT_NAME("Value") PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CENTERDELTA(0) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)
INPUT_PORTS_END
@@ -356,8 +376,8 @@ void fz1_state::maincpu_map(address_map &map)
/**************************************************************************/
void fz1_state::fz10m_io_map(address_map &map)
{
- // 0x00-07: GAA
- // 0x08-0f: GAB
+ map(0x00, 0x07).rw(m_pcm, FUNC(fz_pcm_device::gaa_r), FUNC(fz_pcm_device::gaa_w));
+ map(0x08, 0x0f).rw(m_pcm, FUNC(fz_pcm_device::gab_r), FUNC(fz_pcm_device::gab_w));
map(0x10, 0x13).mirror(0x04).m(m_fdc, FUNC(upd72065_device::map)).umask16(0x00ff);
map(0x18, 0x1f).rw(m_io[0], FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
map(0x20, 0x27).rw(m_io[1], FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
@@ -366,6 +386,8 @@ void fz1_state::fz10m_io_map(address_map &map)
map(0x70, 0x77).w(FUNC(fz1_state::gal_w)).umask16(0x00ff);
map(0x78, 0x7f).w(FUNC(fz1_state::led_w)).umask16(0x00ff);
// 0x80-bf: DCF/DCA
+ map(0x80, 0x9f).w(FUNC(fz1_state::dca_w)).umask16(0x00ff);
+ map(0xa0, 0xbf).ram();
}
/**************************************************************************/
@@ -451,6 +473,18 @@ void fz1_state::fz10m(machine_config &config)
screen.set_palette("palette");
PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);
+
+ FZ_PCM(config, m_pcm, 13.824_MHz_XTAL);
+ m_pcm->irq_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ2);
+ for (int i = 0; i < 8; i++)
+ {
+ SPEAKER(config, m_line_out[i]).front_center();
+ m_pcm->add_route(i, m_filter[i], 1.0);
+
+ FILTER_BIQUAD(config, m_filter[i]);
+ // TODO: these are switched-capacitor lowpass filters
+ m_filter[i]->add_route(0, m_line_out[i], 1.0 / 8);
+ }
}
/**************************************************************************/
@@ -484,6 +518,9 @@ void fz1_state::fz20m(machine_config &config)
{
fz10m(config);
m_maincpu->set_addrmap(AS_IO, &fz1_state::fz20m_io_map);
+ m_maincpu->out_hreq_cb().set(m_maincpu, FUNC(v50_device::hack_w));
+ m_maincpu->in_memr_cb().set(FUNC(fz1_state::mem_r));
+ m_maincpu->out_memw_cb().set(FUNC(fz1_state::mem_w));
m_maincpu->in_ior_cb<1>().set("scsi:7:spc", FUNC(mb89352_device::dma_r));
m_maincpu->out_iow_cb<1>().set("scsi:7:spc", FUNC(mb89352_device::dma_w));
@@ -518,6 +555,7 @@ void fz1_state::machine_start()
m_sub_p2 = 0;
m_ram_bank->space().install_ram(0, m_ram->mask(), m_ram->pointer());
+ m_pcm->space().install_ram(0, m_ram->mask() >> 1, m_ram->pointer());
m_fdc->set_rate(500000);
m_fdc->set_floppy(m_floppy->get_device());
@@ -531,6 +569,8 @@ void fz1_state::machine_start()
save_item(NAME(m_lcd_nibble));
save_item(NAME(m_lcd_data));
save_item(NAME(m_lcd_data_phase));
+
+ save_item(NAME(m_dca_level));
}
/**************************************************************************/
@@ -649,6 +689,17 @@ void fz1_state::led_w(u8 data)
}
/**************************************************************************/
+void fz1_state::dca_w(offs_t offset, u8 data)
+{
+ const offs_t num = offset & 7;
+ if (BIT(offset, 3))
+ m_dca_level[num] = (m_dca_level[num] & 0xff) | ((data & 3) << 8);
+ else
+ m_dca_level[num] = (m_dca_level[num] & 0x300) | data;
+ m_filter[num]->set_output_gain(ALL_OUTPUTS, m_dca_level[num] / 1023.0);
+}
+
+/**************************************************************************/
ROM_START( fz1 )
ROM_REGION(0x10000, "maincpu", 0) // "ROM ver.[B]"
ROM_LOAD16_BYTE( "fz1s.bin", 0x00000, 0x08000, CRC(b0ba313d) SHA1(45a3660d708f0a584f9f61d04e205a96688f0950) )
@@ -681,6 +732,6 @@ ROM_END
} // anonymous namespace
-SYST( 1987, fz1, 0, 0, fz1, fz1, fz1_state, empty_init, "Casio", "FZ-1 Digital Sampling Synthesizer", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
-SYST( 1987, fz10m, fz1, 0, fz10m, fz10m, fz1_state, empty_init, "Casio", "FZ-10M Digital Sampling Synthesizer Module", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
-SYST( 1989, fz20m, fz1, 0, fz20m, fz10m, fz1_state, empty_init, "Casio", "FZ-20M Digital Sampling Synthesizer Module", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
+SYST( 1987, fz1, 0, 0, fz1, fz1, fz1_state, empty_init, "Casio", "FZ-1 Digital Sampling Synthesizer", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
+SYST( 1987, fz10m, fz1, 0, fz10m, fz10m, fz1_state, empty_init, "Casio", "FZ-10M Digital Sampling Synthesizer Module", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
+SYST( 1989, fz20m, fz1, 0, fz20m, fz10m, fz1_state, empty_init, "Casio", "FZ-20M Digital Sampling Synthesizer Module", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )