rz1: Improve emulation

- Add MT (magnetic tape) support to load and save rythm and sample data. Supports raw .wav files as well as .cas files (same format as TRS-80 Model 3).
- Add a line-in port to support sampling. Currently uses a cassette drive: You can sample .wav files.
- Add NVRAM for data and samples
- Improve layout and add support for the sampling LED
- Add software list for audio (the official RZ-1 Sound Collection tape) and data tapes (homebrew files)
- Update notes and small cleanups
This commit is contained in:
Dirk Best 2022-09-28 20:17:23 +02:00
parent f9870d6f02
commit 6394b12986
3 changed files with 279 additions and 77 deletions

75
hash/rz1_cass.xml Normal file
View file

@ -0,0 +1,75 @@
<?xml version="1.0"?>
<!DOCTYPE softwarelist SYSTEM "softwarelist.dtd">
<!--
license:CC0
Casio RZ-1 tapes
-->
<softwarelist name="rz1_cass" description="Casio RZ-1 cassettes">
<!-- Audio Tapes (for the line-in port) -->
<!-- Created from an MP3 -->
<software name="rz1sc">
<description>Casio RZ-1 Sound Collection</description>
<year>1983</year>
<publisher>Casio</publisher>
<info name="usage" value="Hold SAMPLING, push sample number(s) then let go to start sampling" />
<part name="sidea" interface="audio_cass">
<dataarea name="cass" size="39426360">
<rom name="rz1_sound_collection_side_a.wav" size="39426360" crc="CB00C86C" sha1="869672d3b429033bd0073e0ccf743f26dc6b6bd2" />
</dataarea>
</part>
<part name="sideb" interface="audio_cass">
<dataarea name="cass" size="34800258">
<rom name="rz1_sound_collection_side_b.wav" size="34800258" crc="9F0B0628" sha1="47c88b973e4be88a62144c57719b86e1b90b34d9" />
</dataarea>
</part>
</software>
<!-- Data Tapes (for the MT port) -->
<!-- Provided by R-Massive from http://zine.r-massive.com/casio-rz-1-mt-jack/ -->
<software name="80sdrums">
<description>80's Drums (Samples)</description>
<year>2016</year>
<publisher>&lt;homebrew&gt;</publisher>
<info name="usage" value="Push MT LOAD, push NO to select samples, push YES to load" />
<part name="cass1" interface="rz1_cass">
<dataarea name="cass" size="21406">
<rom name="80s_drums.cas" size="21406" crc="5428108b" sha1="560afbe28fc2112e9cda4811eba78e833e219169" />
</dataarea>
</part>
</software>
<!-- Provided by R-Massive from http://zine.r-massive.com/casio-rz-1-mt-jack/ -->
<software name="fourcymb">
<description>Four Cymbals (Samples)</description>
<year>2016</year>
<publisher>&lt;homebrew&gt;</publisher>
<info name="usage" value="Push MT LOAD, push NO to select samples, push YES to load" />
<part name="cass1" interface="rz1_cass">
<dataarea name="cass" size="8379044">
<rom name="four_cymbals.wav" size="8379044" crc="5b1bce12" sha1="1a2ea236819aed674de199b7fcb8f829ce1167dd" />
</dataarea>
</part>
</software>
<!-- Provided by R-Massive from http://zine.r-massive.com/casio-rz-1-mt-jack/ -->
<software name="harddrum">
<description>Hard Drums (Samples)</description>
<year>2016</year>
<publisher>&lt;homebrew&gt;</publisher>
<info name="usage" value="Push MT LOAD, push NO to select samples, push YES to load" />
<part name="cass1" interface="rz1_cass">
<dataarea name="cass" size="21406">
<rom name="hard_drums.cas" size="21406" crc="4f4aee44" sha1="f16d9f967689fc1d0929f0f17d5f3e31f57cdda9" />
</dataarea>
</part>
</software>
</softwarelist>

View file

@ -6,38 +6,46 @@
Sampling drum machine
Sound ROM info:
Hardware:
- uPD7811G-120
- HN4872128G25 (program rom)
- uPD4364C (data ram, battery backed)
- 2x uPD934G (percussion generator)
- 2x HN613256P (sample rom)
- 2x uPD4364C-15L (sample ram, battery backed)
- EXK-F19Z2064 (10-bit DAC)
Each ROM chip holds 1.49s of sounds and can be opened as raw
PCM data: signed 8-bit, mono, 20,000 Hz.
* Sound A: Toms 1~3, Kick, Snare, Rimshot, Closed Hi-Hat, Open Hi-Hat,
and Metronome Click (in that order).
* Sound B: Clap, Ride, Cowbell, and Crash (in that order).
Note: Holding EDIT/RECORD, DELETE, INSERT/AUTO-COMPENSATE and
CHAIN/BEAT at startup causes the system to go into a RAM test.
Notes:
- Each sample ROM holds 1.49s of sounds in the following format:
PCM data, signed 8-bit, mono, 20,000 Hz
- Holding EDIT/RECORD, DELETE, INSERT/AUTO-COMPENSATE and
CHAIN/BEAT at startup causes the system to go into a RAM test.
TODO:
- Metronome
- MIDI
- Cassette
- Audio input
- Make audio input generic (core support needed)
***************************************************************************/
#include "emu.h"
#include "cpu/upd7810/upd7811.h"
#include "formats/trs_cas.h"
#include "imagedev/cassette.h"
#include "machine/nvram.h"
#include "sound/upd934g.h"
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "cpu/upd7810/upd7811.h"
#include "video/hd44780.h"
#include "sound/upd934g.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "rz1.lh"
namespace {
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
@ -50,8 +58,11 @@ public:
m_maincpu(*this, "maincpu"),
m_hd44780(*this, "hd44780"),
m_pg{ {*this, "upd934g_c"}, {*this, "upd934g_b"} },
m_cassette(*this, "cassette"),
m_linein(*this, "linein"),
m_samples{ {*this, "samples_a"}, {*this, "samples_b"} },
m_keys(*this, "kc%u", 0),
m_led_sampling(*this, "led_sampling"),
m_led_song(*this, "led_song"),
m_led_pattern(*this, "led_pattern"),
m_led_startstop(*this, "led_startstop"),
@ -61,9 +72,6 @@ public:
void rz1(machine_config &config);
void rz1_palette(palette_device &palette) const;
HD44780_PIXEL_UPDATE(lcd_pixel_update);
protected:
virtual void machine_start() override;
virtual void machine_reset() override;
@ -72,27 +80,35 @@ private:
required_device<upd7811_device> m_maincpu;
required_device<hd44780_device> m_hd44780;
required_device<upd934g_device> m_pg[2];
required_device<cassette_image_device> m_cassette;
required_device<cassette_image_device> m_linein;
required_memory_region m_samples[2];
required_ioport_array<8> m_keys;
output_finder<> m_led_sampling;
output_finder<> m_led_song;
output_finder<> m_led_pattern;
output_finder<> m_led_startstop;
void map(address_map &map);
uint8_t port_a_r();
void port_a_w(uint8_t data);
void port_b_w(uint8_t data);
uint8_t port_c_r();
void port_c_w(uint8_t data);
uint8_t key_r();
void rz1_palette(palette_device &palette) const;
HD44780_PIXEL_UPDATE(lcd_pixel_update);
void leds_w(uint8_t data);
uint8_t upd934g_c_data_r(offs_t offset);
void upd934g_c_w(offs_t offset, uint8_t data);
uint8_t upd934g_b_data_r(offs_t offset);
void upd934g_b_w(offs_t offset, uint8_t data);
uint8_t key_r();
void leds_w(uint8_t data);
uint8_t analog_r();
uint8_t port_a_r();
void port_a_w(uint8_t data);
void port_b_w(uint8_t data);
uint8_t port_c_r();
void port_c_w(uint8_t data);
uint8_t m_port_a;
uint8_t m_port_b;
@ -106,12 +122,12 @@ private:
void rz1_state::map(address_map &map)
{
// map(0x0000, 0x0fff).rom().region("maincpu", 0);
map(0x2000, 0x3fff).ram();
map(0x2000, 0x3fff).ram().share("dataram");
map(0x4000, 0x7fff).rom().region("program", 0);
map(0x8000, 0x8fff).w(FUNC(rz1_state::upd934g_c_w));
map(0x9000, 0x9fff).rw(FUNC(rz1_state::key_r), FUNC(rz1_state::upd934g_b_w));
map(0xa000, 0xbfff).ram(); // sample ram 1
map(0xc000, 0xdfff).ram(); // sample ram 2
map(0xa000, 0xbfff).ram().share("sample1");
map(0xc000, 0xdfff).ram().share("sample2");
map(0xe000, 0xe001).w(FUNC(rz1_state::leds_w));
}
@ -196,9 +212,32 @@ INPUT_PORTS_END
//**************************************************************************
// MACHINE EMULATION
// KEYBOARD
//**************************************************************************
uint8_t rz1_state::key_r()
{
uint8_t data = 0;
for (int i = 0; i < 8; i++)
if (BIT(m_port_a, i) == 0)
data |= m_keys[i]->read();
return data;
}
//**************************************************************************
// VIDEO EMULATION
//**************************************************************************
void rz1_state::rz1_palette(palette_device &palette) const
{
palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
palette.set_pen_color(1, rgb_t(92, 83, 88)); // lcd pixel on
palette.set_pen_color(2, rgb_t(131, 136, 139)); // lcd pixel off
}
HD44780_PIXEL_UPDATE( rz1_state::lcd_pixel_update )
{
// char size is 5x8
@ -209,6 +248,27 @@ HD44780_PIXEL_UPDATE( rz1_state::lcd_pixel_update )
bitmap.pix(1 + y, 1 + line*8*6 + pos*6 + x) = state ? 1 : 2;
}
void rz1_state::leds_w(uint8_t data)
{
// 76------ unknown
// --5----- sampling led
// ---4---- start/stop led
// ----3--- pattern led red
// -----2-- pattern led green
// ------1- song led red
// -------0 song led green
m_led_song = BIT(~data, 0, 2);
m_led_pattern = BIT(~data, 2, 2);
m_led_startstop = BIT(~data, 4);
m_led_sampling = BIT(~data, 5);
}
//**************************************************************************
// AUDIO EMULATION
//**************************************************************************
uint8_t rz1_state::upd934g_c_data_r(offs_t offset)
{
if (offset < 0x8000)
@ -240,6 +300,16 @@ void rz1_state::upd934g_b_w(offs_t offset, uint8_t data)
m_pg[1]->write(offset >> 8, data);
}
uint8_t rz1_state::analog_r()
{
return uint8_t(int8_t(m_linein->input() * 127.0) + 127);
}
//**************************************************************************
// MACHINE EMULATION
//**************************************************************************
uint8_t rz1_state::port_a_r()
{
if ((BIT(m_port_b, 7) == 0) && (BIT(m_port_b, 6) == 1))
@ -260,68 +330,53 @@ void rz1_state::port_a_w(uint8_t data)
m_hd44780->write(BIT(m_port_b, 5), data);
}
// 7------- lcd e
// -6------ lcd rw
// --5----- lcd rs
// ---4---- percussion generator reset
// ----3--- metronome trigger
// -----2-- power-on mute for line-out
// ------1- change-over signal tom3/bd
// -------0 change-over signal tom1/tom2
void rz1_state::port_b_w(uint8_t data)
{
// 7------- lcd e
// -6------ lcd rw
// --5----- lcd rs
// ---4---- percussion generator reset
// ----3--- metronome trigger
// -----2-- power-on mute for line-out
// ------1- change-over signal tom3/bd
// -------0 change-over signal tom1/tom2
if (0)
logerror("port_b_w: %02x\n", data);
m_port_b = data;
}
// 7------- foot-sustain input
// -6------ cassette data out
// --5----- cassette remote control
// ---4---- change-over signal for sampling ram
// ----3--- cassette data in
// -----2-- control signal for percussion generator c
// ------1- midi in
// -------0 midi out
uint8_t rz1_state::port_c_r()
{
return 0;
}
// 7------- foot-sustain input
// -6------ cassette data out
// --5----- cassette remote control
// ---4---- change-over signal for sampling ram
// ----3--- cassette data in
// -----2-- control signal for percussion generator c
// ------1- midi in
// -------0 midi out
void rz1_state::port_c_w(uint8_t data)
{
logerror("port_c_w: %02x\n", data);
}
uint8_t rz1_state::key_r()
{
uint8_t data = 0;
if (BIT(m_port_a, 0) == 0) data |= m_keys[0]->read();
if (BIT(m_port_a, 1) == 0) data |= m_keys[1]->read();
if (BIT(m_port_a, 2) == 0) data |= m_keys[2]->read();
if (BIT(m_port_a, 3) == 0) data |= m_keys[3]->read();
if (BIT(m_port_a, 4) == 0) data |= m_keys[4]->read();
if (BIT(m_port_a, 5) == 0) data |= m_keys[5]->read();
if (BIT(m_port_a, 6) == 0) data |= m_keys[6]->read();
if (BIT(m_port_a, 7) == 0) data |= m_keys[7]->read();
data |= (m_cassette->input() > 0 ? 0 : 1) << 3;
return data;
}
void rz1_state::leds_w(uint8_t data)
void rz1_state::port_c_w(uint8_t data)
{
m_led_song = BIT(data, 0) == 0 ? 1 : BIT(data, 1) == 0 ? 2 : 0;
m_led_pattern = BIT(data, 2) == 0 ? 1 : BIT(data, 3) == 0 ? 2 : 0;
m_led_startstop = BIT(data, 4) == 0 ? 1 : 0;
m_cassette->change_state(BIT(data, 5) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR);
m_cassette->output(BIT(data, 6) ? -1.0 : 1.0);
logerror("port_c_w: %02x\n", data);
}
void rz1_state::machine_start()
{
// resolve output finders
m_led_sampling.resolve();
m_led_song.resolve();
m_led_pattern.resolve();
m_led_startstop.resolve();
@ -335,12 +390,6 @@ void rz1_state::machine_reset()
{
}
void rz1_state::rz1_palette(palette_device &palette) const
{
palette.set_pen_color(0, rgb_t(138, 146, 148)); // background
palette.set_pen_color(1, rgb_t(92, 83, 88)); // lcd pixel on
palette.set_pen_color(2, rgb_t(131, 136, 139)); // lcd pixel off
}
//**************************************************************************
// MACHINE DEFINTIONS
@ -355,6 +404,18 @@ void rz1_state::rz1(machine_config &config)
m_maincpu->pb_out_cb().set(FUNC(rz1_state::port_b_w));
m_maincpu->pc_in_cb().set(FUNC(rz1_state::port_c_r));
m_maincpu->pc_out_cb().set(FUNC(rz1_state::port_c_w));
m_maincpu->an0_func().set(FUNC(rz1_state::analog_r));
m_maincpu->an1_func().set(FUNC(rz1_state::analog_r));
m_maincpu->an2_func().set(FUNC(rz1_state::analog_r));
m_maincpu->an3_func().set(FUNC(rz1_state::analog_r));
m_maincpu->an4_func().set(FUNC(rz1_state::analog_r));
m_maincpu->an5_func().set(FUNC(rz1_state::analog_r));
m_maincpu->an6_func().set(FUNC(rz1_state::analog_r));
m_maincpu->an7_func().set(FUNC(rz1_state::analog_r));
NVRAM(config, "dataram", nvram_device::DEFAULT_NONE);
NVRAM(config, "sample1", nvram_device::DEFAULT_NONE);
NVRAM(config, "sample2", nvram_device::DEFAULT_NONE);
// video hardware
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
@ -373,6 +434,7 @@ void rz1_state::rz1(machine_config &config)
config.set_default_layout(layout_rz1);
// audio hardware
SPEAKER(config, "speaker").front_center();
UPD934G(config, m_pg[0], 1333000);
m_pg[0]->data_callback().set(FUNC(rz1_state::upd934g_c_data_r));
@ -380,6 +442,21 @@ void rz1_state::rz1(machine_config &config)
UPD934G(config, m_pg[1], 1280000);
m_pg[1]->data_callback().set(FUNC(rz1_state::upd934g_b_data_r));
m_pg[1]->add_route(ALL_OUTPUTS, "speaker", 1.0);
// mt (magnetic tape)
CASSETTE(config, m_cassette);
m_cassette->set_formats(trs80l2_cassette_formats);
m_cassette->set_default_state(CASSETTE_PLAY | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED);
m_cassette->set_interface("rz1_cass");
m_cassette->add_route(ALL_OUTPUTS, "speaker", 0.05);
// should be a generic audio input port, using cassette for now
CASSETTE(config, m_linein);
m_linein->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED);
m_linein->set_interface("audio_cass");
m_linein->add_route(ALL_OUTPUTS, "speaker", 0.05);
SOFTWARE_LIST(config, "cass_list").set_original("rz1_cass");
}
@ -394,14 +471,19 @@ ROM_START( rz1 )
ROM_REGION(0x4000, "program", 0)
ROM_LOAD("program.bin", 0x0000, 0x4000, CRC(b44b2652) SHA1(b77f8daece9adb177b6ce1ef518fc3238b8c0a9c))
// Toms 1~3, Kick, Snare, Rimshot, Closed Hi-Hat, Open Hi-Hat and Metronome Click
ROM_REGION(0x8000, "samples_a", 0)
ROM_LOAD("sound_a.cm5", 0x0000, 0x8000, CRC(c643ff24) SHA1(e886314d22a9a5473bfa2cb237ecafcf0daedfc1)) // HN613256P
ROM_LOAD("sound_a.cm5", 0x0000, 0x8000, CRC(c643ff24) SHA1(e886314d22a9a5473bfa2cb237ecafcf0daedfc1))
// Clap, Ride, Cowbell and Crash
ROM_REGION(0x8000, "samples_b", 0)
ROM_LOAD("sound_b.cm6", 0x0000, 0x8000, CRC(ee5b703e) SHA1(cbf2e92c68901f236678d704e9e695a5c84ff49e)) // HN613256P
ROM_LOAD("sound_b.cm6", 0x0000, 0x8000, CRC(ee5b703e) SHA1(cbf2e92c68901f236678d704e9e695a5c84ff49e))
ROM_END
} // anonymous namespace
//**************************************************************************
// SYSTEM DRIVERS
//**************************************************************************

View file

@ -50,6 +50,15 @@ Casio RZ-1 layout
</rect>
</element>
<element name="led_red" defstate="0">
<rect state="0">
<color red="0.05" green="0.05" blue="0.05" />
</rect>
<rect state="1">
<color red="0.8" green="0.05" blue="0.05" />
</rect>
</element>
<element name="text_0"><text string="0"><color red="0.15" green="0.11" blue="0.11" /></text></element>
<element name="text_1"><text string="1"><color red="0.15" green="0.11" blue="0.11" /></text></element>
<element name="text_2"><text string="2"><color red="0.15" green="0.11" blue="0.11" /></text></element>
@ -64,6 +73,10 @@ Casio RZ-1 layout
<element name="text_casio"><text string="CASIO"><color red="0.98" green="0.95" blue="0.89" /></text></element>
<element name="text_desc"><text string="DIGITAL SAMPLING RHYTHM COMPOSER"><color red="0.29" green="0.74" blue="0.68" /></text></element>
<element name="text_rz1"><text string="RZ-1"><color red="0.29" green="0.74" blue="0.68" /></text></element>
<element name="text_instrument_level"><text string="INSTRUMENT LEVEL"><color red="0.98" green="0.95" blue="0.89" /></text></element>
<element name="text_sampling_white"><text string="SAMPLING"><color red="0.98" green="0.95" blue="0.89" /></text></element>
<element name="text_level"><text string="LEVEL"><color red="0.98" green="0.95" blue="0.89" /></text></element>
<element name="text_volume"><text string="VOLUME"><color red="0.98" green="0.95" blue="0.89" /></text></element>
<element name="text_song"><text string="SONG"><color red="0.98" green="0.95" blue="0.89" /></text></element>
<element name="text_pattern"><text string="PATTERN"><color red="0.98" green="0.95" blue="0.89" /></text></element>
<element name="text_edit"><text string="EDIT"><color red="0.98" green="0.95" blue="0.89" /></text></element>
@ -224,6 +237,13 @@ Casio RZ-1 layout
</screen>
<collection name="Controls">
<!-- sampling led -->
<element name="led_sampling" ref="led_red">
<bounds x="526" y="35" width="9" height="4" />
</element>
<!-- logo and text top right -->
<element ref="text_casio">
@ -236,6 +256,31 @@ Casio RZ-1 layout
<bounds x="665" y="134" width="59" height="24" />
</element>
<!-- dial labels -->
<element ref="text_instrument_level">
<bounds x="53" y="168" width="378" height="7" />
</element>
<element ref="teal">
<bounds x="53" y="177" width="378" height="1" />
</element>
<element ref="text_sampling_white">
<bounds x="502" y="160" width="56" height="7" />
</element>
<element ref="text_level">
<bounds x="502" y="168" width="56" height="7" />
</element>
<element ref="teal">
<bounds x="502" y="177" width="56" height="1" />
</element>
<element ref="text_volume">
<bounds x="560" y="168" width="56" height="7" />
</element>
<element ref="teal">
<bounds x="560" y="177" width="56" height="1" />
</element>
<!-- separator -->
<element name="sep1a" ref="darkgray">