mirror of
https://github.com/holub/mame
synced 2025-06-24 13:26:36 +03:00
roland_tr707.cpp, video/hd61602.cpp: LCD emulation. (#13798)
* Emulated HD61602 device. * Incorporated it into the 707 driver and layout. * Corrected tempo potentiometer curve. * Made dinsync testable. * Marked systems as supporting save. * Minor layout cleanup.
This commit is contained in:
parent
9f810075ab
commit
9112f09cb5
@ -367,6 +367,18 @@ if (VIDEOS["HD61202"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
--
|
||||
--@src/devices/video/hd61602.h,VIDEOS["HD61602"] = true
|
||||
--------------------------------------------------
|
||||
|
||||
if (VIDEOS["HD61602"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/video/hd61602.cpp",
|
||||
MAME_DIR .. "src/devices/video/hd61602.h",
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
--
|
||||
--@src/devices/video/hd61603.h,VIDEOS["HD61603"] = true
|
||||
|
206
src/devices/video/hd61602.cpp
Normal file
206
src/devices/video/hd61602.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:m1macrophage
|
||||
|
||||
/*
|
||||
Not emulated:
|
||||
- External oscillator (OSC1) [*]
|
||||
- Effects of resistor between OSC1 and OSC2, when using internal oscillator [*].
|
||||
- SB pin, halts internal clock.
|
||||
- SYNC pin for chip cascading.
|
||||
- READY pin.
|
||||
|
||||
[*] The datasheet specifies the frequency of the internal oscillator as
|
||||
100KHz +/- 30KHz. This requires using the recommended resistor values. Examples
|
||||
are shown with a 330K and a 360K resistor. An external oscillator can be used
|
||||
too, and it is also specified as 100Khz +/- 30KHz.
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "hd61602.h"
|
||||
|
||||
DEFINE_DEVICE_TYPE(HD61602, hd61602_device, "hd61602", "Hitachi HD61602 LCD Driver")
|
||||
|
||||
hd61602_device::hd61602_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
|
||||
: device_t(mconfig, HD61602, tag, owner, clock)
|
||||
, m_refresh_timer(nullptr)
|
||||
, m_write_segs(*this)
|
||||
{
|
||||
}
|
||||
|
||||
void hd61602_device::device_start()
|
||||
{
|
||||
m_refresh_timer = timer_alloc(FUNC(hd61602_device::refresh_timer_tick), this);
|
||||
|
||||
m_count = 0;
|
||||
m_data = 0;
|
||||
m_ram = {0, 0, 0, 0};
|
||||
m_blank = false;
|
||||
m_display_mode = DM_STATIC;
|
||||
m_drive_mode = DM_STATIC;
|
||||
m_com_count = 1;
|
||||
m_active_com = 0;
|
||||
|
||||
save_item(NAME(m_count));
|
||||
save_item(NAME(m_data));
|
||||
save_item(NAME(m_ram));
|
||||
save_item(NAME(m_blank));
|
||||
save_item(NAME(m_display_mode));
|
||||
save_item(NAME(m_drive_mode));
|
||||
save_item(NAME(m_com_count));
|
||||
save_item(NAME(m_active_com));
|
||||
}
|
||||
|
||||
void hd61602_device::set_ram_bit(u8 com, u8 seg, u8 bit)
|
||||
{
|
||||
if (com >= NCOM || seg >= NSEG)
|
||||
return;
|
||||
const u64 new_bit = u64(bit ? 1 : 0) << seg;
|
||||
const u64 mask = ~(u64(1) << seg);
|
||||
m_ram[com] = (m_ram[com] & mask) | new_bit;
|
||||
}
|
||||
|
||||
void hd61602_device::set_drive_mode(u8 drive_mode)
|
||||
{
|
||||
if (drive_mode == m_drive_mode)
|
||||
return;
|
||||
|
||||
const u8 old_com_count = m_com_count;
|
||||
m_drive_mode = drive_mode;
|
||||
m_active_com = 0;
|
||||
|
||||
int frame_rate = 0;
|
||||
switch (m_drive_mode)
|
||||
{
|
||||
// Frame rates are reported on the datasheet for an 100KHz clock.
|
||||
// 100KHz is the nominal frequency of the internal oscillator, and the
|
||||
// recommended frequency for an external oscillator.
|
||||
case DM_STATIC: m_com_count = 1; break;
|
||||
case DM_HALF: m_com_count = 2; frame_rate = 65; break;
|
||||
case DM_THIRD: m_com_count = 3; frame_rate = 208; break;
|
||||
case DM_QUARTER: m_com_count = 4; frame_rate = 223; break;
|
||||
}
|
||||
|
||||
if (m_com_count > 1)
|
||||
{
|
||||
const attotime timer_t = attotime::from_hz(frame_rate * m_com_count);
|
||||
m_refresh_timer->adjust(timer_t, 0, timer_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The frame rate in 'static' mode is actually 33 Hz (assuming a 100KHz
|
||||
// clock). But there is no time-multiplexing in that mode, so there is
|
||||
// no need for the timer.
|
||||
m_refresh_timer->reset();
|
||||
}
|
||||
|
||||
// Clear rows that will no longer be updated in the new mode.
|
||||
for (u8 com = m_com_count; com < old_com_count; ++com)
|
||||
m_write_segs(com, 0);
|
||||
}
|
||||
|
||||
TIMER_CALLBACK_MEMBER(hd61602_device::refresh_timer_tick)
|
||||
{
|
||||
m_write_segs(m_active_com, 0);
|
||||
m_active_com = (m_active_com + 1) % m_com_count;
|
||||
if (m_blank)
|
||||
m_write_segs(m_active_com, 0);
|
||||
else
|
||||
m_write_segs(m_active_com, m_ram[m_active_com] & make_bitmask<u64>(NSEG));
|
||||
}
|
||||
|
||||
void hd61602_device::data_w(u8 data)
|
||||
{
|
||||
// 1-byte nop command
|
||||
if (m_count == 0 && (data & 0xc0) == 0xc0)
|
||||
return;
|
||||
|
||||
m_data = (m_data << 8) | data;
|
||||
m_count = (m_count + 1) & 1;
|
||||
if (m_count != 0) // Waiting for the next byte.
|
||||
return;
|
||||
|
||||
switch (BIT(m_data, 14, 2))
|
||||
{
|
||||
// Update display RAM byte, using a logical segment address.
|
||||
// The address-to-bit mapping depends on the configured display mode.
|
||||
case 0:
|
||||
{
|
||||
u8 seg_count = 8;
|
||||
u8 max_address = 6;
|
||||
bool skip_seg0_com2 = false;
|
||||
switch (m_display_mode)
|
||||
{
|
||||
case DM_HALF:
|
||||
seg_count = 4;
|
||||
max_address = 12;
|
||||
break;
|
||||
case DM_THIRD:
|
||||
seg_count = 3;
|
||||
max_address = 16;
|
||||
skip_seg0_com2 = true;
|
||||
break;
|
||||
case DM_QUARTER:
|
||||
seg_count = 2;
|
||||
max_address = 25;
|
||||
break;
|
||||
default:
|
||||
assert(m_display_mode == DM_STATIC);
|
||||
break;
|
||||
}
|
||||
|
||||
const u8 address = BIT(m_data, 8, 5);
|
||||
if (address <= max_address)
|
||||
{
|
||||
assert(seg_count * m_com_count - (skip_seg0_com2 ? 1 : 0) == 8);
|
||||
const u8 start_seg = address * seg_count;
|
||||
int bit_offset = 7;
|
||||
for (u8 s = 0; s < seg_count; ++s)
|
||||
for (u8 com = 0; com < m_com_count; ++com)
|
||||
if (!(skip_seg0_com2 && s == 0 && com == 2))
|
||||
// When address == max_address, some of the segment
|
||||
// addresses won't be valid. set_ram_bit() will
|
||||
// ignore those.
|
||||
set_ram_bit(com, start_seg + s, BIT(m_data, bit_offset--));
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Update a specific bit in display RAM, addressed by COM and SEG.
|
||||
case 1:
|
||||
{
|
||||
const u8 com = BIT(m_data, 8, 2);
|
||||
const u8 seg = BIT(m_data, 0, 6);
|
||||
set_ram_bit(com, seg, BIT(m_data, 13));
|
||||
break;
|
||||
}
|
||||
// Configure mode.
|
||||
case 2:
|
||||
{
|
||||
if (BIT(m_data, 12) == 0)
|
||||
{
|
||||
// Drive mode and display mode are configured independently, even
|
||||
// though they should generally match. Drive mode controls the
|
||||
// actual display signals, whereas display mode controls how byte
|
||||
// writes ate interpreted.
|
||||
m_display_mode = BIT(m_data, 0, 2);
|
||||
m_blank = BIT(m_data, 2);
|
||||
// bits 3-7 unused.
|
||||
set_drive_mode(BIT(m_data, 8, 2));
|
||||
// bit 10: READY mode. Not implemented.
|
||||
// bit 11: Set external power supply. Not relevant.
|
||||
// bit 12: must be 0 to configure the mode.
|
||||
// bit 13: unused.
|
||||
}
|
||||
break;
|
||||
}
|
||||
// case 3 is a 1-byte nop command, checked at the start of this function.
|
||||
}
|
||||
|
||||
// Updates for modes other than 'static' are handled in refresh_timer_tick().
|
||||
if (m_drive_mode == DM_STATIC)
|
||||
m_write_segs(0, m_blank ? 0 : (m_ram[0] & make_bitmask<u64>(NSEG)));
|
||||
}
|
||||
|
||||
void hd61602_device::reset_counter_strobe()
|
||||
{
|
||||
m_count = 0;
|
||||
}
|
82
src/devices/video/hd61602.h
Normal file
82
src/devices/video/hd61602.h
Normal file
@ -0,0 +1,82 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:m1macrophage
|
||||
|
||||
/*
|
||||
Hitachi HD61602 LCD Driver.
|
||||
51 segment outputs, 4 common outputs, configurable duty cycle.
|
||||
|
||||
Pinout (80-pin QFP)
|
||||
|
||||
pin desc
|
||||
------------------------------
|
||||
1 = VDD
|
||||
2 = READY
|
||||
3 = _CS
|
||||
4 = _WE
|
||||
5 = _RE
|
||||
6 = SB
|
||||
7-10 = D7-D4
|
||||
11 = VSS
|
||||
12-15 = D3-D0
|
||||
16 = VREF1
|
||||
17 = VREF2
|
||||
18,19 = VC2
|
||||
20-22 = V1-V3
|
||||
23-26 = COM0-COM3
|
||||
27-77 = SEG50-SEG0
|
||||
78 = SYNC
|
||||
79,80 = OSC2,OSC1
|
||||
*/
|
||||
|
||||
#ifndef MAME_VIDEO_HD61602_H
|
||||
#define MAME_VIDEO_HD61602_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class hd61602_device : public device_t
|
||||
{
|
||||
public:
|
||||
static constexpr const int NCOM = 4; // COM (common) outputs, aka rows.
|
||||
static constexpr const int NSEG = 51; // SEG (segment) outputs, aka columns.
|
||||
|
||||
hd61602_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0) ATTR_COLD;
|
||||
|
||||
auto write_segs() { return m_write_segs.bind(); }
|
||||
|
||||
void data_w(u8 data);
|
||||
|
||||
// Byte counter resets when /CS and /RE are both driven low.
|
||||
void reset_counter_strobe();
|
||||
|
||||
protected:
|
||||
virtual void device_start() override ATTR_COLD;
|
||||
|
||||
private:
|
||||
void set_ram_bit(u8 com, u8 seg, u8 bit);
|
||||
void set_drive_mode(u8 drive_mode);
|
||||
TIMER_CALLBACK_MEMBER(refresh_timer_tick);
|
||||
|
||||
enum display_mode
|
||||
{
|
||||
DM_STATIC = 0,
|
||||
DM_HALF, // 1/2 duty cycle.
|
||||
DM_THIRD, // 1/3 duty cycle.
|
||||
DM_QUARTER, // 1/4 duty cycle.
|
||||
};
|
||||
|
||||
emu_timer *m_refresh_timer;
|
||||
devcb_write64 m_write_segs;
|
||||
|
||||
u8 m_count;
|
||||
u16 m_data;
|
||||
std::array<u64, NCOM> m_ram;
|
||||
bool m_blank;
|
||||
u8 m_display_mode;
|
||||
u8 m_drive_mode;
|
||||
u8 m_com_count;
|
||||
u8 m_active_com;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(HD61602, hd61602_device)
|
||||
|
||||
#endif // MAME_VIDEO_HD61602_H
|
@ -2,6 +2,9 @@
|
||||
<!--
|
||||
license:CC0-1.0
|
||||
copyright-holders:m1macrophage
|
||||
|
||||
The layouts for the tr707 and tr727 are very similar. The `is727` output controls
|
||||
the visibility and style of elements that differ between the two.
|
||||
-->
|
||||
<mamelayout version="2">
|
||||
<element name="script_warning" defstate="1">
|
||||
@ -127,28 +130,71 @@ copyright-holders:m1macrophage
|
||||
<element name="lcd_section_grid">
|
||||
<image><data><![CDATA[
|
||||
<rect fill="#4a5454" width="268" height="134" rx="4" ry="4"/>
|
||||
<rect fill="#b8c2c4" width="266" height="132" rx="3" ry="3" x="1" y="1" />
|
||||
<rect fill="#b8c2c4" width="266" height="132" rx="3" ry="3" x="1" y="1"/>
|
||||
]]></data></image>
|
||||
</element>
|
||||
<element name="lcd_section_tempo">
|
||||
<image><data><![CDATA[
|
||||
<rect fill="#4a5454" width="104" height="64" rx="4" ry="4"/>
|
||||
<rect fill="#b8c2c4" width="102" height="62" rx="3" ry="3" x="1" y="1" />
|
||||
<rect fill="#b8c2c4" width="102" height="62" rx="3" ry="3" x="1" y="1"/>
|
||||
]]></data></image>
|
||||
</element>
|
||||
<element name="lcd_section_track">
|
||||
<image><data><![CDATA[
|
||||
<rect fill="#4a5454" width="160" height="24" rx="4" ry="4"/>
|
||||
<rect fill="#b8c2c4" width="158" height="22" rx="3" ry="3" x="1" y="1" />
|
||||
<rect fill="#b8c2c4" width="158" height="22" rx="3" ry="3" x="1" y="1"/>
|
||||
]]></data></image>
|
||||
</element>
|
||||
<element name="lcd_section_mode">
|
||||
<image><data><![CDATA[
|
||||
<rect fill="#4a5454" width="160" height="38" rx="4" ry="4"/>
|
||||
<rect fill="#b8c2c4" width="158" height="36" rx="3" ry="3" x="1" y="1" />
|
||||
<rect fill="#b8c2c4" width="158" height="36" rx="3" ry="3" x="1" y="1"/>
|
||||
]]></data></image>
|
||||
</element>
|
||||
|
||||
<element name="seg_triangle">
|
||||
<image state="1"><data><![CDATA[<polygon fill="#12120f" fill-opacity="0.8" points="0,0 5,3 0,6"/>]]></data></image>
|
||||
</element>
|
||||
<element name="seg_dot">
|
||||
<disk state="1"><color red="0.07" green="0.07" blue="0.06" alpha="0.7"/></disk>
|
||||
</element>
|
||||
<element name="seg_digit">
|
||||
<led7seg><color red="0.07" green="0.07" blue="0.06" alpha="0.7"/></led7seg>
|
||||
</element>
|
||||
<element name="seg_tempo">
|
||||
<text state="1" string="TEMPO" align="1"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_measure">
|
||||
<text state="1" string="MEASURE" align="1"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_track_1">
|
||||
<text state="1" string="I"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_track_2">
|
||||
<text state="1" string="II"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_track_3">
|
||||
<text state="1" string="III"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_track_4">
|
||||
<text state="1" string="IV"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_track_play">
|
||||
<text state="1" string="TRACK PLAY" align="1"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_track_write">
|
||||
<text state="1" string="TRACK WRITE" align="1"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_pattern_play">
|
||||
<text state="1" string="PATTERN PLAY" align="1"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_step_write">
|
||||
<text state="1" string="STEP WRITE" align="1"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
<element name="seg_tap_write">
|
||||
<text state="1" string="TAP WRITE" align="1"><color red="0.07" green="0.07" blue="0.06" alpha="0.8"/></text>
|
||||
</element>
|
||||
|
||||
|
||||
<!-- Mixer elements -->
|
||||
|
||||
@ -365,60 +411,15 @@ copyright-holders:m1macrophage
|
||||
|
||||
<!-- Drum pad legend elements -->
|
||||
|
||||
<element name="txt_shiftpad_1">
|
||||
<text string="1">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<element name="txt_shiftpad_2">
|
||||
<text string="2">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<element name="txt_shiftpad_3">
|
||||
<text string="3">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<element name="txt_shiftpad_4">
|
||||
<text string="4">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<element name="txt_shiftpad_5">
|
||||
<text string="5">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<element name="txt_shiftpad_6">
|
||||
<text string="6">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<element name="txt_shiftpad_7">
|
||||
<text string="7">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<element name="txt_shiftpad_8">
|
||||
<text string="8">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<element name="txt_shiftpad_9">
|
||||
<text string="9">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
<repeat count="9">
|
||||
<param name="i" start="1" increment="1"/>
|
||||
<element name="txt_shiftpad_~i~">
|
||||
<text string="~i~">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
<color state="1" red="0.95" green="0.95" blue="0.90"/>
|
||||
</text>
|
||||
</element>
|
||||
</repeat>
|
||||
<element name="txt_shiftpad_10">
|
||||
<text string="0">
|
||||
<color state="0" red="0.17" green="0.19" blue="0.20"/>
|
||||
@ -586,17 +587,45 @@ copyright-holders:m1macrophage
|
||||
<param name="i" start="1" increment="1"/>
|
||||
<param name="txt_y" start="183" increment="12"/>
|
||||
<param name="line_y" start="186" increment="12"/>
|
||||
<param name="dot_y" start="183" increment="12"/>
|
||||
<param name="separator_y" start="180" increment="12"/>
|
||||
|
||||
<element ref="lcd_txt_voice_~i~" name="is727"><bounds x="428" y="~txt_y~" width="58" height="8"/></element>
|
||||
<element ref="lcd_line"><bounds x="488" y="~line_y~" width="186" height="1"/></element>
|
||||
<element ref="lcd_line"><bounds x="424" y="~separator_y~" width="64" height="1"/></element>
|
||||
|
||||
<element ref="seg_triangle" name="seg_triangle_~i~"><bounds x="413" y="~dot_y~" width="6" height="7"/></element>
|
||||
<repeat count="16">
|
||||
<param name="j" start="1" increment="1"/>
|
||||
<param name="x" start="495" increment="11"/>
|
||||
<element ref="seg_dot" name="seg_dot_~j~_~i~"><bounds x="~x~" y="~dot_y~" width="7" height="7"/></element>
|
||||
</repeat>
|
||||
</repeat>
|
||||
|
||||
<element ref="lcd_section_tempo"><bounds x="407" y="307" width="104" height="64"/></element>
|
||||
<element ref="seg_tempo" name="seg_text_1"><bounds x="421" y="313" width="30" height="10"/></element>
|
||||
<element ref="seg_measure" name="seg_text_2"><bounds x="455" y="313" width="40" height="10"/></element>
|
||||
<repeat count="3">
|
||||
<param name="i" start="1" increment="1"/>
|
||||
<param name="x" start="416" increment="30"/>
|
||||
<element ref="seg_digit" name="seg_digit_~i~"><bounds x="~x~" y="330" width="24" height="34"/></element>
|
||||
</repeat>
|
||||
|
||||
<element ref="lcd_section_track"><bounds x="515" y="307" width="160" height="24"/></element>
|
||||
<element ref="lcd_txt_track"><bounds x="519" y="313" width="44" height="11"/></element>
|
||||
<repeat count="4">
|
||||
<param name="i" start="1" increment="1"/>
|
||||
<param name="x" start="584" increment="22"/>
|
||||
<element ref="seg_track_~i~" name="seg_track_~i~"><bounds x="~x~" y="310" width="20" height="20"/></element>
|
||||
</repeat>
|
||||
|
||||
<element ref="lcd_section_mode"><bounds x="515" y="333" width="160" height="38"/></element>
|
||||
<element ref="lcd_txt_mode"><bounds x="519" y="336" width="30" height="10"/></element>
|
||||
<element ref="lcd_txt_mode"><bounds x="519" y="335" width="70" height="10"/></element>
|
||||
<element ref="seg_track_play" name="seg_text_3"><bounds x="519" y="347" width="70" height="10"/></element>
|
||||
<element ref="seg_track_write" name="seg_text_4"><bounds x="519" y="359" width="70" height="10"/></element>
|
||||
<element ref="seg_pattern_play" name="seg_text_5"><bounds x="595" y="335" width="70" height="10"/></element>
|
||||
<element ref="seg_step_write" name="seg_text_6"><bounds x="595" y="347" width="70" height="10"/></element>
|
||||
<element ref="seg_tap_write" name="seg_text_7"><bounds x="595" y="359" width="70" height="10"/></element>
|
||||
|
||||
|
||||
<!-- Mixer -->
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "machine/rescap.h"
|
||||
#include "machine/timer.h"
|
||||
#include "sound/va_eg.h"
|
||||
//#include "video/hd61603.h"
|
||||
#include "video/hd61602.h"
|
||||
#include "video/pwm.h"
|
||||
|
||||
#include "roland_tr707.lh"
|
||||
@ -45,48 +45,7 @@ namespace {
|
||||
class roland_tr707_state : public driver_device
|
||||
{
|
||||
public:
|
||||
roland_tr707_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD
|
||||
: driver_device(mconfig, type, tag)
|
||||
, m_maincpu(*this, "maincpu")
|
||||
, m_cartslot(*this, "cartslot")
|
||||
, m_mac(*this, "mac")
|
||||
, m_led_matrix(*this, "led_matrix")
|
||||
, m_key_switches(*this, "KEY%u", 0U)
|
||||
, m_cart_led(*this, "led_cart")
|
||||
, m_leds(4)
|
||||
, m_tapesync_in(*this, "TAPESYNC")
|
||||
, m_dinsync_in(*this, "DINSYNC")
|
||||
, m_dinsync_config(*this, "DINSYNC_CONFIG")
|
||||
, m_dinsync_out(*this, "DINSYNC_OUT_%u", 0U)
|
||||
, m_tempo_timer(*this, "tempo_clock")
|
||||
, m_tempo_restart_timer(*this, "tempo_restart_timer")
|
||||
, m_tempo_ff(*this, "tempo_flipflop")
|
||||
, m_tempo_trimmer(*this, "TM1")
|
||||
, m_tempo_knob(*this, "TEMPO")
|
||||
, m_accent_adc_rc(*this, "accent_adc_rc_network")
|
||||
, m_accent_adc_timer(*this, "accent_adc_timer")
|
||||
, m_accent_adc_ff(*this, "accent_adc_flipflop")
|
||||
, m_accent_trimmer_series(*this, "TM2")
|
||||
, m_accent_trimmer_parallel(*this, "TM3")
|
||||
, m_accent_level(*this, "SLIDER_1")
|
||||
, m_layout_727(*this, "is727")
|
||||
, m_is_727(false)
|
||||
, m_cart_bank(0)
|
||||
, m_key_led_row(0xff)
|
||||
, m_tempo_source(0xff)
|
||||
, m_midi_rxd_bit(true) // Initial value is high, for serial "idle".
|
||||
{
|
||||
constexpr const char *LED_NAME_SUFFIXES[4][6] =
|
||||
{
|
||||
{"1", "2", "3", "4", "scale_1", "scale_2"},
|
||||
{"5", "6", "7", "8", "scale_3", "scale_4"},
|
||||
{"9", "10", "11", "12", "group_1", "group_2"},
|
||||
{"13", "14", "15", "16", "group_3", "group_4"},
|
||||
};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int j = 0; j < 6; ++j)
|
||||
m_leds[i].push_back(output_finder<>(*this, std::string("led_") + LED_NAME_SUFFIXES[i][j]));
|
||||
}
|
||||
roland_tr707_state(const machine_config &mconfig, device_type type, const char *tag) ATTR_COLD;
|
||||
|
||||
void tr707(machine_config &config);
|
||||
void tr727(machine_config &config);
|
||||
@ -115,6 +74,26 @@ private:
|
||||
ACCENT_FLIPFLOP_CLR_ASSERT,
|
||||
};
|
||||
|
||||
struct seg_output // Reference to an LCD segment output.
|
||||
{
|
||||
enum seg_type
|
||||
{
|
||||
NONE = 0,
|
||||
TRIANGLE,
|
||||
TRACK,
|
||||
TEXT,
|
||||
DOT,
|
||||
DIGIT,
|
||||
};
|
||||
|
||||
seg_output() : seg_output(NONE, 0, 0) {}
|
||||
seg_output(enum seg_type _type, offs_t _x, offs_t _y = 0) : type(_type), x(_x), y(_y) {}
|
||||
|
||||
enum seg_type type;
|
||||
offs_t x;
|
||||
offs_t y;
|
||||
};
|
||||
|
||||
static double discharge_t(double r, double c, double v);
|
||||
|
||||
u8 key_scan_r();
|
||||
@ -126,6 +105,10 @@ private:
|
||||
void ga_trigger_w(offs_t offset, u8 data);
|
||||
void voice_select_w(u8 data);
|
||||
|
||||
u8 lcd_reset_r();
|
||||
void lcd_seg_w(offs_t offset, u64 data);
|
||||
void lcd_seg_outputs_w(offs_t offset, u8 data);
|
||||
|
||||
int cart_connected_r() const;
|
||||
u8 cart_r(offs_t offset);
|
||||
void cart_w(offs_t offset, u8 data);
|
||||
@ -157,6 +140,14 @@ private:
|
||||
output_finder<> m_cart_led; // D325 (GL9NP2) dual LED (red & green).
|
||||
std::vector<std::vector<output_finder<>>> m_leds;
|
||||
|
||||
required_device<hd61602_device> m_lcdc;
|
||||
required_device<pwm_display_device> m_lcd_pwm;
|
||||
output_finder<10> m_seg_triangle;
|
||||
output_finder<4> m_seg_track;
|
||||
output_finder<7> m_seg_text;
|
||||
output_finder<16, 10> m_seg_dot;
|
||||
output_finder<3> m_seg_digit;
|
||||
|
||||
required_ioport m_tapesync_in;
|
||||
required_ioport m_dinsync_in;
|
||||
required_ioport m_dinsync_config;
|
||||
@ -177,6 +168,7 @@ private:
|
||||
|
||||
output_finder<> m_layout_727;
|
||||
bool m_is_727; // Configuration. Not needed in save state.
|
||||
std::vector<std::vector<seg_output>> m_seg_map; // Configuration.
|
||||
|
||||
u8 m_cart_bank; // IC27 (40H174), Q5.
|
||||
u8 m_key_led_row; // P60-P63.
|
||||
@ -190,6 +182,143 @@ private:
|
||||
static constexpr const double POS_THRESH_4584 = 2.7;
|
||||
};
|
||||
|
||||
roland_tr707_state::roland_tr707_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: driver_device(mconfig, type, tag)
|
||||
, m_maincpu(*this, "maincpu")
|
||||
, m_cartslot(*this, "cartslot")
|
||||
, m_mac(*this, "mac")
|
||||
, m_led_matrix(*this, "led_matrix")
|
||||
, m_key_switches(*this, "KEY%u", 0U)
|
||||
, m_cart_led(*this, "led_cart")
|
||||
, m_leds(4)
|
||||
, m_lcdc(*this, "lcdc")
|
||||
, m_lcd_pwm(*this, "lcd_pwm")
|
||||
, m_seg_triangle(*this, "seg_triangle_%u", 1U)
|
||||
, m_seg_track(*this, "seg_track_%u", 1U)
|
||||
, m_seg_text(*this, "seg_text_%u", 1U)
|
||||
, m_seg_dot(*this, "seg_dot_%u_%u", 1U, 1U)
|
||||
, m_seg_digit(*this, "seg_digit_%u", 1U)
|
||||
, m_tapesync_in(*this, "TAPESYNC")
|
||||
, m_dinsync_in(*this, "DINSYNC")
|
||||
, m_dinsync_config(*this, "DINSYNC_CONFIG")
|
||||
, m_dinsync_out(*this, "DINSYNC_OUT_%u", 0U)
|
||||
, m_tempo_timer(*this, "tempo_clock")
|
||||
, m_tempo_restart_timer(*this, "tempo_restart_timer")
|
||||
, m_tempo_ff(*this, "tempo_flipflop")
|
||||
, m_tempo_trimmer(*this, "TM1")
|
||||
, m_tempo_knob(*this, "TEMPO")
|
||||
, m_accent_adc_rc(*this, "accent_adc_rc_network")
|
||||
, m_accent_adc_timer(*this, "accent_adc_timer")
|
||||
, m_accent_adc_ff(*this, "accent_adc_flipflop")
|
||||
, m_accent_trimmer_series(*this, "TM2")
|
||||
, m_accent_trimmer_parallel(*this, "TM3")
|
||||
, m_accent_level(*this, "SLIDER_1")
|
||||
, m_layout_727(*this, "is727")
|
||||
, m_is_727(false)
|
||||
, m_seg_map(hd61602_device::NCOM, std::vector<seg_output>(hd61602_device::NSEG)) // 4x51 LCD segments.
|
||||
, m_cart_bank(0)
|
||||
, m_key_led_row(0xff)
|
||||
, m_tempo_source(0xff)
|
||||
, m_midi_rxd_bit(true) // Initial value is high, for serial "idle".
|
||||
{
|
||||
// Initalize LED outputs.
|
||||
|
||||
constexpr const char *LED_NAME_SUFFIXES[4][6] =
|
||||
{
|
||||
{"1", "2", "3", "4", "scale_1", "scale_2"},
|
||||
{"5", "6", "7", "8", "scale_3", "scale_4"},
|
||||
{"9", "10", "11", "12", "group_1", "group_2"},
|
||||
{"13", "14", "15", "16", "group_3", "group_4"},
|
||||
};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int j = 0; j < 6; ++j)
|
||||
m_leds[i].push_back(output_finder<>(*this, std::string("led_") + LED_NAME_SUFFIXES[i][j]));
|
||||
|
||||
|
||||
// Build a mapping (m_seg_map) from the LCD controller's rows and columns (
|
||||
// "com" and "seg" in hd61602 parlance) to the corresponding outputs.
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_seg_map[3 - i][50] = seg_output(seg_output::TRACK, i);
|
||||
|
||||
constexpr const std::tuple<int, int> SEG_TRIANGLES[10] =
|
||||
{
|
||||
{0, 48}, // cymbal
|
||||
{1, 48}, // hi hat
|
||||
{2, 48}, // hcp / tamb
|
||||
{3, 48}, // rim / cowbell
|
||||
{3, 47}, // hi tom
|
||||
{2, 47}, // mid tom
|
||||
{1, 47}, // low tom
|
||||
{0, 47}, // snare drum
|
||||
{0, 46}, // bass drum
|
||||
{1, 46}, // accent
|
||||
};
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
const std::tuple<int, int> &seg = SEG_TRIANGLES[i];
|
||||
m_seg_map[std::get<0>(seg)][std::get<1>(seg)] = seg_output(seg_output::TRIANGLE, i);
|
||||
}
|
||||
|
||||
constexpr const std::tuple<int, int> SEG_TEXT[7] =
|
||||
{
|
||||
{0, 44}, // tempo
|
||||
{0, 40}, // measure
|
||||
{3, 46}, // track play
|
||||
{2, 46}, // track write
|
||||
{3, 49}, // pattern play
|
||||
{2, 49}, // step write
|
||||
{1, 49}, // tap write
|
||||
};
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
const std::tuple<int, int> &seg = SEG_TEXT[i];
|
||||
m_seg_map[std::get<0>(seg)][std::get<1>(seg)] = seg_output(seg_output::TEXT, i);
|
||||
}
|
||||
|
||||
constexpr const std::tuple<int, int> SEG_DIGIT[3][7] =
|
||||
{
|
||||
{ {0, 45}, {1, 44}, {2, 44}, {3, 44}, {3, 45}, {1, 45}, {2, 45} },
|
||||
{ {0, 43}, {1, 42}, {2, 42}, {3, 42}, {3, 43}, {1, 43}, {2, 43} },
|
||||
{ {0, 41}, {1, 40}, {2, 40}, {3, 40}, {3, 41}, {1, 41}, {2, 41} },
|
||||
};
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
for (int j = 0; j < 7; ++j)
|
||||
{
|
||||
const std::tuple<int, int> &seg = SEG_DIGIT[i][j];
|
||||
m_seg_map[std::get<0>(seg)][std::get<1>(seg)] = seg_output(seg_output::DIGIT, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
// Segment addresses (row.col aka com.seg) for the 10x16 matrix of dots.
|
||||
// "0.0 .. 3.0" means "0.0 1.0 2.0 3.0" and similar for the decreasing
|
||||
// cases susch as "3.1 .. 0.1".
|
||||
|
||||
// cymbal 0.0 .. 3.0 3.1 .. 0.1 0.20 .. 3.20 3.21 .. 0.21
|
||||
// hat: 0.2 .. 3.2 3.3 .. 0.3 0.22 .. 3.22 3.23 .. 0.23
|
||||
// hcp: 0.4 .. 3.4 3.5 .. 0.5 0.24 .. 3.24 3.25 .. 0.25
|
||||
// rim: 0.6 .. 3.6 3.7 .. 0.7 0.26 .. 3.26 3.27 .. 0.27
|
||||
// hi tom: 0.8 .. 3.8 3.9 .. 0.9 0.28 .. 3.28 3.29 .. 0.29
|
||||
// mid tom: 0.10 .. 3.10 3.11 .. 0.11 0.30 .. 3.30 3.31 .. 0.31
|
||||
// low tom: 0.12 .. 3.12 3.13 .. 0.13 0.32 .. 3.32 3.33 .. 0.33
|
||||
// snare: 0.14 .. 3.14 3.15 .. 0.15 0.34 .. 3.34 3.35 .. 0.35
|
||||
// bass: 0.16 .. 3.16 3.17 .. 0.17 0.36 .. 3.36 3.37 .. 0.37
|
||||
// accent: 0.18 .. 3.18 3.19 .. 0.19 0.38 .. 3.38 3.39 .. 0.39
|
||||
|
||||
// Map each row.col to an x.y output position, based on the table above.
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
for (int j = 0; j < 10; ++j)
|
||||
{
|
||||
m_seg_map[i][2 * j] = seg_output(seg_output::DOT, i, j);
|
||||
m_seg_map[3 - i][2 * j + 1] = seg_output(seg_output::DOT, i + 4, j);
|
||||
m_seg_map[i][2 * j + 20] = seg_output(seg_output::DOT, i + 8, j);
|
||||
m_seg_map[3 - i][2 * j + 21] = seg_output(seg_output::DOT, i + 12, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void roland_tr707_state::machine_start()
|
||||
{
|
||||
save_item(NAME(m_cart_bank));
|
||||
@ -203,6 +332,12 @@ void roland_tr707_state::machine_start()
|
||||
for (std::vector<output_finder<>> &led_row : m_leds)
|
||||
for (output_finder<> &led_output : led_row)
|
||||
led_output.resolve();
|
||||
|
||||
m_seg_triangle.resolve();
|
||||
m_seg_track.resolve();
|
||||
m_seg_text.resolve();
|
||||
m_seg_dot.resolve();
|
||||
m_seg_digit.resolve();
|
||||
}
|
||||
|
||||
void roland_tr707_state::machine_reset()
|
||||
@ -286,6 +421,38 @@ void roland_tr707_state::voice_select_w(u8 data)
|
||||
LOGMASKED(LOG_TRIGGER, "Voice selected: %02x\n", data);
|
||||
}
|
||||
|
||||
u8 roland_tr707_state::lcd_reset_r()
|
||||
{
|
||||
if (!machine().side_effects_disabled())
|
||||
m_lcdc->reset_counter_strobe();
|
||||
return 0x00; // Data bus pulled low.
|
||||
}
|
||||
|
||||
void roland_tr707_state::lcd_seg_w(offs_t offset, u64 data)
|
||||
{
|
||||
m_lcd_pwm->matrix(1 << offset, data);
|
||||
}
|
||||
|
||||
void roland_tr707_state::lcd_seg_outputs_w(offs_t offset, u8 data)
|
||||
{
|
||||
const seg_output &seg = m_seg_map[offset & 0x3f][offset >> 6];
|
||||
switch (seg.type)
|
||||
{
|
||||
case seg_output::TRIANGLE: m_seg_triangle[seg.x] = data; break;
|
||||
case seg_output::TRACK: m_seg_track[seg.x] = data; break;
|
||||
case seg_output::TEXT: m_seg_text[seg.x] = data; break;
|
||||
case seg_output::DOT: m_seg_dot[seg.x][seg.y] = data; break;
|
||||
case seg_output::DIGIT:
|
||||
{
|
||||
const u8 bit = BIT(data, 0) << seg.y;
|
||||
const u8 mask = 0x7f ^ (1 << seg.y);
|
||||
m_seg_digit[seg.x] = (m_seg_digit[seg.x] & mask) | bit;
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
int roland_tr707_state::cart_connected_r() const
|
||||
{
|
||||
// P54 is pulled up by R24 and connected to pin 1 ("sens") of the cartridge,
|
||||
@ -405,7 +572,11 @@ void roland_tr707_state::update_internal_tempo_timer(bool cap_reset)
|
||||
constexpr const double KNOB_MAX = RES_M(1); // VR301.
|
||||
|
||||
// Using 100.0 - x, so that larger values result in higher tempo.
|
||||
const double knob_r = KNOB_MAX * (100.0 - m_tempo_knob->read()) / 100.0;
|
||||
// The tempo potentiometer has a "C" (inverse audio) taper, wired such that
|
||||
// turning it clockwise reduces the resistance. Treating the input as a
|
||||
// position (a value increase corresponds to a resistance decrease),
|
||||
// results in an "A" (audio) taper wrt the position.
|
||||
const double knob_r = KNOB_MAX * RES_AUDIO_POT_LAW((100.0 - m_tempo_knob->read()) / 100.0);
|
||||
const double trimmer_r = TRIMMER_MAX * (100.0 - m_tempo_trimmer->read()) / 100.0;
|
||||
const double r = R318 + trimmer_r + knob_r;
|
||||
const double period = discharge_t(r, C10, NEG_THRESH_4584);
|
||||
@ -550,7 +721,7 @@ void roland_tr707_state::mem_map(address_map &map)
|
||||
map.unmap_value_low(); // Data bus pulled low by RA301.
|
||||
map(0x0000, 0x0000).mirror(0x7ff).unmaprw();
|
||||
map(0x0800, 0x0800).mirror(0x7ff).r(FUNC(roland_tr707_state::key_scan_r));
|
||||
//map(0x1000, 0x1000).mirror(0xfff).rw("lcdd", FUNC(hd61602_device::ready_r), FUNC(hd61602_device::write));
|
||||
map(0x1000, 0x1000).mirror(0xfff).r(FUNC(roland_tr707_state::lcd_reset_r)).w(m_lcdc, FUNC(hd61602_device::data_w));
|
||||
map(0x2000, 0x27ff).ram().share("nvram1");
|
||||
map(0x2800, 0x2fff).ram().share("nvram2");
|
||||
map(0x3000, 0x3fff).rw(FUNC(roland_tr707_state::cart_r), FUNC(roland_tr707_state::cart_w));
|
||||
@ -603,7 +774,7 @@ static INPUT_PORTS_START(tr707)
|
||||
PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Group C") PORT_CODE(KEYCODE_C)
|
||||
PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Group D") PORT_CODE(KEYCODE_V)
|
||||
|
||||
PORT_START("TEMPO") // Tempo knob, VR301, 1M (EWH-LNAF20C16).
|
||||
PORT_START("TEMPO") // Tempo knob, VR301, 1M(C) (EWH-LNAF20C16, inverse audio taper).
|
||||
PORT_ADJUSTER(50, "Tempo") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::tempo_pots_adjusted), 0)
|
||||
|
||||
PORT_START("VOLUME") // Master volume slider, VR212, 50K(B) (S3028, dual-gang).
|
||||
@ -645,15 +816,16 @@ static INPUT_PORTS_START(tr707)
|
||||
PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::accent_pots_adjusted), 0)
|
||||
|
||||
PORT_START("DINSYNC_CONFIG")
|
||||
PORT_CONFNAME(0x01, 0x00, "DIN sync input cable connected")
|
||||
PORT_CONFNAME(0x01, 0x01, "DIN sync input cable connected")
|
||||
PORT_CONFSETTING(0x00, DEF_STR(No))
|
||||
PORT_CONFSETTING(0x01, DEF_STR(Yes))
|
||||
|
||||
PORT_START("DINSYNC") // SYNC socket (J4). Bit numbers map to `enum dinsync_index`.
|
||||
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC start/stop") // Pin 1.
|
||||
PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC tempo") // Pin 3. 24 cloks per quarter note.
|
||||
// SYNC start needs to be active for SYNC tempo to have an effect.
|
||||
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC start/stop") PORT_CODE(KEYCODE_B) // Pin 1.
|
||||
PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC tempo") PORT_CODE(KEYCODE_M) // Pin 3. 24 cloks per quarter note.
|
||||
PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(roland_tr707_state::sync_input_changed), 0)
|
||||
PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC Continue") // Pin 5.
|
||||
PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("SYNC Continue") PORT_CODE(KEYCODE_N) // Pin 5.
|
||||
|
||||
PORT_START("TAPESYNC") // TAPE LOAD / SYNC IN socket (J5).
|
||||
// Input connected to a filter (C30, R43, C31, R47), followed by a
|
||||
@ -714,10 +886,13 @@ void roland_tr707_state::tr707(machine_config &config)
|
||||
MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(FUNC(roland_tr707_state::midi_rxd_w));
|
||||
MIDI_PORT(config, "mdout", midiout_slot, "midiout");
|
||||
|
||||
//HD61602(config, "lcdd");
|
||||
|
||||
GENERIC_CARTSLOT(config, m_cartslot, generic_plain_slot, nullptr, "tr707_cart");
|
||||
|
||||
HD61602(config, m_lcdc);
|
||||
m_lcdc->write_segs().set(FUNC(roland_tr707_state::lcd_seg_w));
|
||||
PWM_DISPLAY(config, m_lcd_pwm).set_size(hd61602_device::NCOM, hd61602_device::NSEG);
|
||||
m_lcd_pwm->output_x().set(FUNC(roland_tr707_state::lcd_seg_outputs_w));
|
||||
|
||||
PWM_DISPLAY(config, m_led_matrix).set_size(4, 6);
|
||||
m_led_matrix->output_x().set(FUNC(roland_tr707_state::led_outputs_w));
|
||||
|
||||
@ -766,5 +941,5 @@ ROM_END
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
SYST(1985, tr707, 0, 0, tr707, tr707, roland_tr707_state, empty_init, "Roland", "TR-707 Rhythm Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
SYST(1985, tr727, 0, 0, tr727, tr707, roland_tr707_state, empty_init, "Roland", "TR-727 Rhythm Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
SYST(1985, tr707, 0, 0, tr707, tr707, roland_tr707_state, empty_init, "Roland", "TR-707 Rhythm Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
|
||||
SYST(1985, tr727, 0, 0, tr727, tr707, roland_tr707_state, empty_init, "Roland", "TR-727 Rhythm Composer", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE)
|
||||
|
Loading…
Reference in New Issue
Block a user