summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/roland_tb303.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/roland_tb303.cpp')
-rw-r--r--src/mame/drivers/roland_tb303.cpp332
1 files changed, 332 insertions, 0 deletions
diff --git a/src/mame/drivers/roland_tb303.cpp b/src/mame/drivers/roland_tb303.cpp
new file mode 100644
index 00000000000..7596b78a4ce
--- /dev/null
+++ b/src/mame/drivers/roland_tb303.cpp
@@ -0,0 +1,332 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/***************************************************************************
+
+ Roland TB-303 Bass Line, 1982, designed by Tadao Kikumoto
+ * NEC uCOM-43 MCU, labeled D650C 133
+ * 3*uPD444C 1024x4 Static CMOS SRAM
+ * board is packed with discrete components
+
+ TODO:
+ - still too much to list here
+
+***************************************************************************/
+
+#include "emu.h"
+#include "cpu/ucom4/ucom4.h"
+#include "video/pwm.h"
+#include "machine/timer.h"
+
+#include "tb303.lh"
+
+
+class tb303_state : public driver_device
+{
+public:
+ tb303_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_display(*this, "display"),
+ m_inputs(*this, "IN.%u", 0)
+ { }
+
+ void tb303(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+
+private:
+ required_device<ucom4_cpu_device> m_maincpu;
+ required_device<pwm_display_device> m_display;
+ required_ioport_array<5> m_inputs;
+
+ u8 m_ram[0xc00];
+ u8 m_ram_addrset[3];
+ u16 m_ram_address;
+ u8 m_ram_data;
+ bool m_ram_ce;
+ bool m_ram_we;
+ u8 m_led_data;
+ u8 m_inp_mux;
+
+ void refresh_ram();
+ template<int N> DECLARE_WRITE8_MEMBER(ram_address_w);
+ DECLARE_WRITE8_MEMBER(ram_data_w);
+ DECLARE_READ8_MEMBER(ram_data_r);
+ DECLARE_WRITE8_MEMBER(strobe_w);
+
+ void update_leds();
+ DECLARE_WRITE8_MEMBER(led_w);
+ DECLARE_WRITE8_MEMBER(input_w);
+ DECLARE_READ8_MEMBER(input_r);
+
+ TIMER_DEVICE_CALLBACK_MEMBER(tp3_clock) { m_maincpu->set_input_line(0, ASSERT_LINE); }
+ TIMER_DEVICE_CALLBACK_MEMBER(tp3_clear) { m_maincpu->set_input_line(0, CLEAR_LINE); }
+};
+
+void tb303_state::machine_start()
+{
+ // zerofill
+ memset(m_ram, 0, sizeof(m_ram));
+ memset(m_ram_addrset, 0, sizeof(m_ram_addrset));
+ m_ram_address = 0;
+ m_ram_data = 0;
+ m_ram_ce = false;
+ m_ram_we = false;
+ m_led_data = 0;
+ m_inp_mux = 0;
+
+ // register for savestates
+ save_item(NAME(m_ram));
+ save_item(NAME(m_ram_addrset));
+ save_item(NAME(m_ram_address));
+ save_item(NAME(m_ram_data));
+ save_item(NAME(m_ram_ce));
+ save_item(NAME(m_ram_we));
+ save_item(NAME(m_led_data));
+ save_item(NAME(m_inp_mux));
+}
+
+// TP2 to MCU CLK: LC circuit(TI S74230), stable sine wave, 2.2us interval
+#define TP2_HZ 454545
+
+// TP3 to MCU _INT: square wave, 1.8ms interval, short duty cycle
+#define TP3_PERIOD attotime::from_usec(1800)
+#define TP3_LOW (TP3_PERIOD / 8)
+
+
+/***************************************************************************
+
+ I/O
+
+***************************************************************************/
+
+// external ram
+
+void tb303_state::refresh_ram()
+{
+ // MCU E2,E3 goes through a 4556 IC(pin 14,13) to one of uPD444 _CE:
+ // _Q0: N/C, _Q1: IC-5, _Q2: IC-3, _Q3: IC-4
+ m_ram_ce = true;
+ u8 hi = 0;
+ switch (m_ram_addrset[2] >> 2 & 3)
+ {
+ case 0: m_ram_ce = false; break;
+ case 1: hi = 0; break;
+ case 2: hi = 1; break;
+ case 3: hi = 2; break;
+ }
+
+ if (m_ram_ce)
+ {
+ // _WE must be high(read mode) for address transitions
+ if (!m_ram_we)
+ m_ram_address = hi << 10 | (m_ram_addrset[2] << 8 & 0x300) | m_ram_addrset[1] << 4 | m_ram_addrset[0];
+ else
+ m_ram[m_ram_address] = m_ram_data;
+ }
+}
+
+template<int N>
+WRITE8_MEMBER(tb303_state::ram_address_w)
+{
+ // MCU D,F,E: RAM address
+ m_ram_addrset[N] = data;
+ refresh_ram();
+
+ // MCU D,F01: pitch data
+ //..
+}
+
+WRITE8_MEMBER(tb303_state::ram_data_w)
+{
+ // MCU C: RAM data
+ m_ram_data = data;
+ refresh_ram();
+}
+
+READ8_MEMBER(tb303_state::ram_data_r)
+{
+ // MCU C: RAM data
+ if (m_ram_ce && !m_ram_we)
+ return m_ram[m_ram_address];
+ else
+ return 0;
+}
+
+WRITE8_MEMBER(tb303_state::strobe_w)
+{
+ // MCU I0: RAM _WE
+ m_ram_we = (data & 1) ? false : true;
+ refresh_ram();
+
+ // MCU I1: pitch data latch strobe
+ // MCU I2: gate signal
+}
+
+
+// switch board
+
+void tb303_state::update_leds()
+{
+ // 4*4 LED matrix from port G/H:
+ /*
+ 0.0 D204 1.0 D211 2.0 D217 3.0 D205
+ 0.1 D206 1.1 D213 2.1 D218 3.1 D207
+ 0.2 D208 1.2 D215 2.2 D220 3.2 D210
+ 0.3 D209 1.3 D216 2.3 D221 3.3 D212
+ */
+ m_display->matrix(m_inp_mux, m_led_data);
+
+ // todo: battery led
+ // todo: 4 more leds(see top-left part)
+}
+
+WRITE8_MEMBER(tb303_state::led_w)
+{
+ // MCU G: leds state
+ m_led_data = data;
+ update_leds();
+}
+
+WRITE8_MEMBER(tb303_state::input_w)
+{
+ // MCU H: input/led mux
+ m_inp_mux = data ^ 0xf;
+ update_leds();
+}
+
+READ8_MEMBER(tb303_state::input_r)
+{
+ u8 data = 0;
+
+ // MCU A,B: multiplexed inputs
+ // if input mux(port H) is 0, port A status buffer & gate is selected (via Q5 NAND)
+ if (offset == 0 && m_inp_mux == 0)
+ {
+ // todo..
+ data = m_inputs[4]->read();
+ }
+ else
+ {
+ for (int i = 0; i < 4; i++)
+ if (BIT(m_inp_mux, i))
+ data |= m_inputs[i]->read();
+
+ data >>= (offset*4) & 0xf;
+ }
+
+ return data;
+}
+
+
+
+/***************************************************************************
+
+ Inputs
+
+***************************************************************************/
+
+static INPUT_PORTS_START( tb303 )
+ PORT_START("IN.0") // H0 port A/B
+ PORT_CONFNAME( 0x03, 0x03, "Mode" )
+ PORT_CONFSETTING( 0x03, "Track Write" )
+ PORT_CONFSETTING( 0x02, "Track Play" )
+ PORT_CONFSETTING( 0x00, "Pattern Play" )
+ PORT_CONFSETTING( 0x01, "Pattern Write" )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("DEL C#")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("INS D#")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_NAME("1 C")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_NAME("2 D")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_NAME("3 E")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_NAME("4 F")
+
+ PORT_START("IN.1") // H1 port A/B
+ PORT_CONFNAME( 0x07, 0x00, "Track / Patt.Group" )
+ PORT_CONFSETTING( 0x00, "1 / I" )
+ PORT_CONFSETTING( 0x01, "2 / I" )
+ PORT_CONFSETTING( 0x02, "3 / II" )
+ PORT_CONFSETTING( 0x03, "4 / II" )
+ PORT_CONFSETTING( 0x04, "5 / III" )
+ PORT_CONFSETTING( 0x05, "6 / III" )
+ PORT_CONFSETTING( 0x06, "7 / IV" )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_NAME("5 G")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_NAME("6 A")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_NAME("7 B")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_NAME("8 C")
+
+ PORT_START("IN.2") // H2 port A/B
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("Pattern Clear")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("Function")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Pitch Mode")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Time Mode")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_NAME("9 Step")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_NAME("0 3n")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("100 A")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("200 B")
+
+ PORT_START("IN.3") // H3 port B
+ PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("F#")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("G#")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("A#")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Back")
+
+ PORT_START("IN.4") // H=0 port A
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Run/Stop")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Tap")
+ PORT_BIT( 0xfc, IP_ACTIVE_HIGH, IPT_UNUSED )
+INPUT_PORTS_END
+
+
+
+/***************************************************************************
+
+ Machine Config
+
+***************************************************************************/
+
+void tb303_state::tb303(machine_config &config)
+{
+ /* basic machine hardware */
+ NEC_D650(config, m_maincpu, TP2_HZ);
+ m_maincpu->read_a().set(FUNC(tb303_state::input_r));
+ m_maincpu->read_b().set(FUNC(tb303_state::input_r));
+ m_maincpu->read_c().set(FUNC(tb303_state::ram_data_r));
+ m_maincpu->write_c().set(FUNC(tb303_state::ram_data_w));
+ m_maincpu->write_d().set(FUNC(tb303_state::ram_address_w<0>));
+ m_maincpu->write_e().set(FUNC(tb303_state::ram_address_w<2>));
+ m_maincpu->write_f().set(FUNC(tb303_state::ram_address_w<1>));
+ m_maincpu->write_g().set(FUNC(tb303_state::led_w));
+ m_maincpu->write_h().set(FUNC(tb303_state::input_w));
+ m_maincpu->write_i().set(FUNC(tb303_state::strobe_w));
+
+ timer_device &tp3_clock(TIMER(config, "tp3_clock"));
+ tp3_clock.configure_periodic(FUNC(tb303_state::tp3_clock), TP3_PERIOD);
+ tp3_clock.set_start_delay(TP3_PERIOD - TP3_LOW);
+ TIMER(config, "tp3_clear").configure_periodic(FUNC(tb303_state::tp3_clear), TP3_PERIOD);
+
+ /* video hardware */
+ PWM_DISPLAY(config, m_display).set_size(4, 4);
+ config.set_default_layout(layout_tb303);
+
+ /* sound hardware */
+ // discrete...
+}
+
+
+
+/***************************************************************************
+
+ Game driver(s)
+
+***************************************************************************/
+
+ROM_START( tb303 )
+ ROM_REGION( 0x0800, "maincpu", 0 )
+ ROM_LOAD( "d650c-133.ic8", 0x0000, 0x0800, CRC(268a8d8b) SHA1(7a4236b2bc9a5cd4c80c63ca1a193e03becfcb4c) )
+ROM_END
+
+
+CONS( 1982, tb303, 0, 0, tb303, tb303, tb303_state, empty_init, "Roland", "TB-303 Bass Line", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )