From 09e5a49fd8bd383da84801cfa9dac1e4a34d91ef Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 25 Nov 2022 02:49:47 +1100 Subject: yamaha/ymdx9.cpp: Added skeleton driver for Yamaha DX9. (#10586) New machines marked as not working ------------------------------- Yamaha DX9 Digital Programmable Algorithm Synthesizer --- src/emu/xtal.cpp | 1 + src/mame/layout/dx9.lay | 590 ++++++++++++++++++++++++++++++++++++++++++++++ src/mame/mame.lst | 3 + src/mame/mess.flt | 1 + src/mame/yamaha/ymdx9.cpp | 407 ++++++++++++++++++++++++++++++++ 5 files changed, 1002 insertions(+) create mode 100644 src/mame/layout/dx9.lay create mode 100644 src/mame/yamaha/ymdx9.cpp diff --git a/src/emu/xtal.cpp b/src/emu/xtal.cpp index fc606c4f7db..30d9a9d9f4c 100644 --- a/src/emu/xtal.cpp +++ b/src/emu/xtal.cpp @@ -163,6 +163,7 @@ const double XTAL::known_xtals[] = { 9'000'000, /* 9_MHz_XTAL Homedata PCBs */ 9'216'000, /* 9.216_MHz_XTAL Univac UTS 20 */ 9'400'000, /* 9.4_MHz_XTAL Yamaha MU-5 and TG-100 */ + 9'426'500, /* 9.4265_MHz_XTAL Yamaha DX7, and DX9 */ 9'600'000, /* 9.6_MHz_XTAL WD37C65 second clock (for 300 KB/sec rate) */ 9'732'000, /* 9.732_MHz_XTAL CTA Invader */ 9'828'000, /* 9.828_MHz_XTAL Universal PCBs */ diff --git a/src/mame/layout/dx9.lay b/src/mame/layout/dx9.lay new file mode 100644 index 00000000000..86ba63b70c4 --- /dev/null +++ b/src/mame/layout/dx9.lay @@ -0,0 +1,590 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 418109aabc4..ac19a319879 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -44589,6 +44589,9 @@ yiear2 // GX407 (c) 1985 @source:yamaha/yman1x.cpp an1x // +@source:yamaha/ymdx9.cpp +dx9 // + @source:yamaha/ymdx100.cpp dx100 // diff --git a/src/mame/mess.flt b/src/mame/mess.flt index 08ed0196d3c..42db8c0a14a 100644 --- a/src/mame/mess.flt +++ b/src/mame/mess.flt @@ -1263,6 +1263,7 @@ yamaha/fb01.cpp yamaha/tg100.cpp yamaha/yman1x.cpp yamaha/ymdx100.cpp +yamaha/ymdx9.cpp yamaha/ymdx11.cpp yamaha/ymmu100.cpp yamaha/ymmu5.cpp diff --git a/src/mame/yamaha/ymdx9.cpp b/src/mame/yamaha/ymdx9.cpp new file mode 100644 index 00000000000..b13dd3b9448 --- /dev/null +++ b/src/mame/yamaha/ymdx9.cpp @@ -0,0 +1,407 @@ +// license:BSD-3-Clause +// copyright-holders:ajxs +/******************************************************************************* + + Skeleton driver for the Yamaha DX9 FM synthesizer. + There is currently no MAME emulation of the OPS/EGS chips, so emulating + the synth's tone generation functionality is not possible. + The cassette interface is currently not emulated. + While there are rumours that an updated firmware ROM exists, this driver + is set up to work with the only one that is widely available. + +*******************************************************************************/ + +#include "emu.h" + +#include "bus/midi/midi.h" +#include "cpu/m6800/m6801.h" +#include "machine/adc0808.h" +#include "machine/clock.h" +#include "machine/nvram.h" +#include "video/hd44780.h" + +#include "emupal.h" +#include "screen.h" + +//#define VERBOSE 1 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + +#include "dx9.lh" + + +namespace { + +class yamaha_dx9_state : public driver_device +{ +public: + yamaha_dx9_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_maincpu(*this, "maincpu"), + m_adc(*this, "adc"), + m_leds(*this, "led_%u", 0U), + m_key_switch_input(*this, "KEY_SWITCH_INPUT.%u", 0) + { + } + + void dx9(machine_config &config); + +protected: + virtual void machine_start() override; + +private: + required_device m_maincpu; + required_device m_adc; + output_finder<2> m_leds; + // This ioport array is used to communicate with the front-panel interface in the layout. + // They emulate the circuits wired to the 'Key/Switch Scan Driver'. + required_ioport_array<16> m_key_switch_input; + + /** + * @brief Which input line on the keyboard/switch scan driver is currently selected. + * This driver is used to read the analog switch values from the synth's keyboard, and + * front-panel switches. + * TODO: I'm currently not sure of the actual implementation of this circuit. This + * implementation is based off the *very* limited description in the service manual, and + * the behaviour that's shown in the firmware. + */ + uint8_t m_key_switch_input_select = 0; + + int m_rx_data; + + /** The polarity of the cassette interface's output line. */ + bool m_cassette_interface_output_polarity = false; + + /** The polarity of the cassette interface's remote line. */ + bool m_cassette_interface_remote_polarity = false; + + /** + * @brief LCD pixel update function. + * The `HD44780_PIXEL_UPDATE` macro expands the definition to include the correct + * parameters for the LCD update function. + * Refer to: `src/devices/video/hd44780.h` for the full definition format. + */ + HD44780_PIXEL_UPDATE(lcd_pixel_update); + + /** + * @brief Handles a write to the synth's OPS chip registers. + * This chip is currently not emulated, however this function is useful for debugging. + * @param offset The offset into the memory mapped region being written. + * @param data The data being written. + */ + void ops_w(offs_t offset, uint8_t data); + + /** + * @brief Handles a write to the synth's EGS chip registers. + * This chip is currently not emulated, however this function is useful for debugging. + * @param offset The offset into the memory mapped region being written. + * @param data The data being written. + */ + void egs_w(offs_t offset, uint8_t data); + + /** + * @brief Handles a read from the keyboard/switch scan driver. + * This multiplexing driver circuit is used to read the states of the synth's front-panel + * switches, and keyboard. The driver's input is wired to the CPU's IO port 1, and the + * output is wired into the address map. + * Input line 0 covers the 'main' front-panel switches. + * Input line 1 covers the numeric front-panel switches 1 through 8. + * Input line 2 covers the numeric front-panel switches 9 though 16. + * Input line 3 covers the numeric front-panel switches 17 though 20, as well as the + * modulation pedal inputs: The Portamento, and Sustain pedals are mapped to + * bits 6, and 7 respectively. + * Note: Input lines 4-15 are used to map the keyboard, which is not implemented here. + * When the keyboard state is read, the default value of 0 will be returned. + * @param offset The offset into the memory mapped region being read. + * @return uint8_t The value read from the bus. + */ + uint8_t key_switch_scan_driver_r(offs_t offset) + { + return m_key_switch_input[m_key_switch_input_select]->read(); + } + + /** + * @brief Handles a write to the 7-segment LED memory mapped region. + * This function is responsible for setting the two 7-segment LEDs set in the + * device's layout file. + * @param offset The offset into the memory mapped region being written. + * @param data The data being written. + */ + void led_w(offs_t offset, uint8_t data); + + void palette_init(palette_device &palette); + + void mem_map(address_map &map); + + WRITE_LINE_MEMBER(midi_r) { m_rx_data = state; } + + WRITE_LINE_MEMBER(midiclock_w) { if (state) m_maincpu->m6801_clock_serial(); } + + /** + * @brief Handle a write to the synth's IO Port 1. + * IO Port 1 is mapped as follows: + * Bit 0: Keyboard/Switch Scan Driver Input. + * Bit 1: "". + * Bit 2: "". + * Bit 3: "". + * Bit 4: ADC EOC Input Line. + * Bit 5: Cassette Interface Remote Port. + * Bit 6: Cassette Interface Tape Output. + * Bit 7: Cassette Interface Tape Input. + * @param offset The offset into the memory mapped region being written. + * @param data The data being written. + */ + void p1_w(offs_t offset, uint8_t data); + + /** + * @brief Handle a read from the synth's IO Port 1. + * @param offset The offset into the memory mapped region being read. + * @return uint8_t The value read from the port. + */ + uint8_t p1_r(offs_t offset); +}; + + +/** + * yamaha_dx9_state::machine_start + */ +void yamaha_dx9_state::machine_start() +{ + m_leds.resolve(); + m_rx_data = ASSERT_LINE; +} + + +/** + * yamaha_dx9_state::lcd_pixel_update + */ +HD44780_PIXEL_UPDATE(yamaha_dx9_state::lcd_pixel_update) +{ + if (x < 5 && y < 8 && line < 2 && pos < 16) + bitmap.pix(line * 10 + y + 1 + ((y == 7) ? 1 : 0), pos * 6 + x + 1) = state ? 1 : 2; +} + + +/** + * yamaha_dx9_state::palette_init + */ +void yamaha_dx9_state::palette_init(palette_device &palette) +{ + palette.set_pen_color(0, rgb_t(0x87, 0xad, 0x34)); // background + palette.set_pen_color(1, rgb_t(0x0, 0x0, 0x0)); // lcd pixel on + palette.set_pen_color(2, rgb_t(0x7d, 0x9f, 0x32)); // lcd pixel off +} + + +/** + * yamaha_dx9_state::mem_map + */ +void yamaha_dx9_state::mem_map(address_map &map) +{ + // Internal CPU registers. + map(0x0000, 0x001f).m(m_maincpu, FUNC(hd6303r_cpu_device::m6801_io)); + + map(0x0020, 0x0020).r(FUNC(yamaha_dx9_state::key_switch_scan_driver_r)); + + map(0x0022, 0x0022).r(m_adc, FUNC(m58990_device::data_r)); + map(0x0024, 0x0024).w(m_adc, FUNC(m58990_device::address_data_start_w)); + + // YM21280 OPS. + map(0x0026, 0x0027).w(FUNC(yamaha_dx9_state::ops_w)); + // HD44780 LCD Controller. + map(0x0028, 0x0029).rw("lcdc", FUNC(hd44780_device::read), FUNC(hd44780_device::write)); + // LED. + map(0x002b, 0x002c).w(FUNC(yamaha_dx9_state::led_w)); + + // Internal RAM. + map(0x0040, 0x00ff).ram(); + + // External RAM. + // 2 * 2kb RAM1 IC19 M5M118P. + map(0x0800, 0x0fff).ram().share("ram1"); + map(0x1000, 0x1800).ram().share("ram2"); + + // YM21290 EGS + map(0x1800, 0x18f3).w(FUNC(yamaha_dx9_state::egs_w)); + + // ROM. + map(0xc000, 0xffff).rom().region("program", 0); +} + + +/** + * yamaha_dx9_state::dx9 + */ +void yamaha_dx9_state::dx9(machine_config &config) +{ + // Initialise the HD63B03RP CPU. + // This oscillator frequency comes from the service manual. + HD6303R(config, m_maincpu, 9.4265_MHz_XTAL / 2); + m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_dx9_state::mem_map); + + // Unlike the DX7 only IO port 1 is used. + // The direction flags of other ports are set, however they are never read, or written. + m_maincpu->in_p1_cb().set(FUNC(yamaha_dx9_state::p1_r)); + m_maincpu->out_p1_cb().set(FUNC(yamaha_dx9_state::p1_w)); + + NVRAM(config, "ram1", nvram_device::DEFAULT_ALL_0); + NVRAM(config, "ram2", nvram_device::DEFAULT_ALL_0); + + // Configure the ADC. The clock speed here is a guess. + M58990(config, m_adc, 500'000); + + // ADC source 4 is the battery voltage. Set this input to always read 0x80. + // If the read value is below 0x6f, the firmware considers this a low battery voltage. + m_adc->in_callback<4>().set_constant(0x80); + + // Configure MIDI. + auto &midiclock(CLOCK(config, "midiclock", 500_kHz_XTAL)); + midiclock.signal_handler().set(FUNC(yamaha_dx9_state::midiclock_w)); + + MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(FUNC(yamaha_dx9_state::midi_r)); + + auto &mdout(MIDI_PORT(config, "mdout", midiout_slot, "midiout")); + m_maincpu->out_ser_tx_cb().set(mdout, FUNC(midi_port_device::write_txd)); + + // Configure the LCD screen. + screen_device &lcd_screen(SCREEN(config, "lcd_screen", SCREEN_TYPE_LCD)); + lcd_screen.set_refresh_hz(60); + lcd_screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */ + lcd_screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update)); + lcd_screen.set_size(6*16+1, 10*2+1); + lcd_screen.set_visarea_full(); + lcd_screen.set_palette("palette"); + + PALETTE(config, "palette", FUNC(yamaha_dx9_state::palette_init), 3); + + hd44780_device &lcdc(HD44780(config, "lcdc", 0)); + lcdc.set_lcd_size(2, 16); + lcdc.set_pixel_update_cb(FUNC(yamaha_dx9_state::lcd_pixel_update)); + + config.set_default_layout(layout_dx9); +} + + +/** + * yamaha_dx9_state::ops_w + */ +void yamaha_dx9_state::ops_w(offs_t offset, uint8_t data) +{ + LOG("OPS: %02X=%02X\n", offset, data); +} + + +/** + * yamaha_dx9_state::egs_w + */ +void yamaha_dx9_state::egs_w(offs_t offset, uint8_t data) +{ + LOG("EGS: %02X=%02X\n", offset, data); +} + + +/** + * yamaha_dx9_state::led_w + */ +void yamaha_dx9_state::led_w(offs_t offset, uint8_t data) +{ + // Since the memory mapped region that calls this function is only two byts in + // size, the led number is the least-significant bit of the offset. + // The DX9's LEDs are wired so that a high input line disables the segment, so + // get the one's complement of the data. + m_leds[offset & 1] = (~data) & 0xff; +} + + +/** + * yamaha_dx9_state::p1_r + */ +uint8_t yamaha_dx9_state::p1_r(offs_t offset) +{ + // The ADC EOC line is wired to bit 4, as well as the Cassette Interface input, which + // is wired to bit 7. This is currently not fully implemented. + return m_adc->eoc_r() << 4; +} + + +/** + * yamaha_dx9_state::p1_w + */ +void yamaha_dx9_state::p1_w(offs_t offset, uint8_t data) +{ + // The low-nibble is written by the firmware to select the key/switch driver input line. + m_key_switch_input_select = data & 0xf; + + // The cassette interface remote port polarity is set by bit 5. + m_cassette_interface_remote_polarity = BIT(data, 5); + + // The cassette interface output polarity is set by bit 6. + m_cassette_interface_output_polarity = BIT(data, 6); +} + + +static INPUT_PORTS_START(dx9) + PORT_START("KEY_SWITCH_INPUT.0") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Yes/Up") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("No/Down") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Store") + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Function") + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Edit") + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Memory") + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN) + + PORT_START("KEY_SWITCH_INPUT.1") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 1") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 2") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 3") + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 4") + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 5") + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 6") + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 7") + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 8") + + PORT_START("KEY_SWITCH_INPUT.2") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 9") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 10") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 11") + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 12") + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 13") + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 14") + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 15") + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 16") + + PORT_START("KEY_SWITCH_INPUT.3") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 17") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 18") + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 19") + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Button 20") + + // These IO ports belong to the keyboard scan circuit. + // Each of these 12 ports represents an individual key within an octave. + // The keyboard is wired so that when each key's line is selected, reading the keyboard + // scan driver output will return a power of two indicating the octave the pressed key + // is in, from 0 to 7. + PORT_START("KEY_SWITCH_INPUT.4") + PORT_START("KEY_SWITCH_INPUT.5") + PORT_START("KEY_SWITCH_INPUT.6") + PORT_START("KEY_SWITCH_INPUT.7") + PORT_START("KEY_SWITCH_INPUT.8") + PORT_START("KEY_SWITCH_INPUT.9") + PORT_START("KEY_SWITCH_INPUT.10") + PORT_START("KEY_SWITCH_INPUT.11") + PORT_START("KEY_SWITCH_INPUT.12") + PORT_START("KEY_SWITCH_INPUT.13") + PORT_START("KEY_SWITCH_INPUT.14") + PORT_START("KEY_SWITCH_INPUT.15") +INPUT_PORTS_END + + +ROM_START(dx9) + ROM_REGION(0x4000, "program", 0) + ROM_LOAD("dx9.bin", 0x0000, 0x4000, CRC(c45e1832) SHA1(a92d7add3b89537ad06c719da0005c450d178d81)) +ROM_END + +} // anonymous namespace + + +SYST(1983, dx9, 0, 0, dx9, dx9, yamaha_dx9_state, empty_init, "Yamaha", "DX9 Digital Programmable Algorithm Synthesizer", MACHINE_IS_SKELETON | MACHINE_CLICKABLE_ARTWORK) -- cgit v1.2.3