From 9d31030196ac34ae3d91b29bc6e4ebfbad1a30b2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Dec 2022 00:59:21 -0500 Subject: [PATCH] yamaha/ymdx100.cpp: Implemented most of the Yamaha DX100 emulation. (#9278) * Includes controls, cassette interface, and an internal layout. * Currently using YM2151 in place of unemulated YM2164 sound engine. * Thanks to Devin Acker for assistance. Machines promoted to working ---------------------- Yamaha DX100 Digital Programmable Algorithm Synthesizer [Pietro Gagliardi] --- src/mame/layout/dx100.lay | 999 ++++++++++++++++++++++++++++++++++++ src/mame/yamaha/ymdx100.cpp | 543 +++++++++++++++++++- 2 files changed, 1525 insertions(+), 17 deletions(-) create mode 100644 src/mame/layout/dx100.lay diff --git a/src/mame/layout/dx100.lay b/src/mame/layout/dx100.lay new file mode 100644 index 00000000000..ec44e26f206 --- /dev/null +++ b/src/mame/layout/dx100.lay @@ -0,0 +1,999 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/yamaha/ymdx100.cpp b/src/mame/yamaha/ymdx100.cpp index a9b7813b425..b9ea0870627 100644 --- a/src/mame/yamaha/ymdx100.cpp +++ b/src/mame/yamaha/ymdx100.cpp @@ -1,12 +1,239 @@ // license:BSD-3-Clause -// copyright-holders:AJR +// copyright-holders:AJR, Pietro Gagliardi /******************************************************************************* - Skeleton driver for Yamaha DX100 & DX27 FM synthesizers. +Yamaha DX27 and DX100 digital synthesizers - The main differences between DX100 and DX27 are that DX27 has 61 keys - rather than 49, but DX100 is portable and can run on battery power. Their - tone generation capabilities are identical. +The DX27 and DX100 are mid-tier professional synthesizers released by Yamaha +around 1985. The DX27 is a full-size keyboard with 61 full-size keys that can +only run on AC power. The DX100 is a smaller, wearable keyboard with only 49 +small-size keys and can run on either AC power or batteries. Both keybaords have +full MIDI in/out/thru, and can also hook up to a Yamaha foot pedal and breath +controller. + +Apart from the differences listed above and a few less significant differences, +these two keyboards are largely identical. In fact, they use the exact same +mainboard, with both DX27 and DX100 printed on the silkscreen; a marker was used +to cross out the irrelevant model number during assembly. The front panel and +keyboard decoding circuits differ, as do the LCD/LED screen assemblies. + +These two appear to be based on the DX21, a slightly higher-end keyboard released +by Yamaha earlier in 1985 that features a hardware chorus effect (implemented +independently of the FM synthesis chip) and some additional voice parameters (which +appear to be implemented in software). There is also the DX27S, which appears to +be based on the DX27 but adds a few extra features. Neither of these two models +are covered here as of this writing. + +The main CPU is a Hitachi HD6303; its I/O circuitry is used extensively. +Some of the controls and inputs are analog; a M58990 ADC chip is used for these. + +The FM synthesis chip is the YM2164 "OPP", a proprietary variant of the YM2151 +that changes the rate of Timer B, moves the test/LFO reset register, and adds +an additional set of 8 registers used by the breath controller code. This chip +was also used in the DX21 (and possibly also the DX27S), but is perhaps most +famously used by the SFG-05 expansion module for the MSX and the FB-01 standalone +desktop MIDI synthesizer. As these two don't support the breath controller, the +extra registers were totally unused (set to 0 by the firmware), and as a result +sound and behave entirely identically to the YM2151. (Figuring out what these +registers did was part of the impetus for me (Pietro Gagliardi) actually building +this emulation.) + +In addition to the independent instruction manuals for the DX27 and DX100, there is +also a single service manual that covers both keyboards (but not the DX27S, curiously +enough). The DX21 service manual comes with an "Overall Circuit Diagram" for that +keyboard; it is likely that the DX27/DX100 service manual also did, but PDFs available +online are missing it. A scan of the DX27/DX100 Overall Circuit Diagram is available +online separately; it does not appear to have spread as far and wide as the rest of the +service manual, but is still readily available. + +*** Currently imperfectly emulated + + - The cassette interface. + This uses an 8-pin DIN with what appears to be the same pinout as the MSX. + However, the remote lines are completely unused, and the tape player has + to be manually operated. The "dxconvert" project by rarepixel does claim to + support DX100 tape dumps, and does so by using the "castools" project by + joyrex2001, which includes a program to convert wav files storing the raw + tape audio to MSX .cas files. dxconvert uses the command-line parameters + "-p -e 4 -t 10"; I'm not sure how to interpret this, even after reading + the code. + + The constant used below for testing against m_cassette->input() was guessed + through trial and error until I could get a successful load at least somewhat + consistently. It still isn't perfect, but I don't know how to refine this + further. + + "dxconvert": https://github.com/rarepixel/dxconvert + "castools": https://github.com/joyrex2001/castools + The sample voices that I used to test can be found at + http://robertgomez.org/blog/2017/08/02/some-yamaha-dx100-dx27-synth-patches/ + I loaded the wav download here, saved it to another wav, + verified that wav (the DX100 will ask to verify data you just + saved), and then converted both to .cas as described and compared + the two; they are identical. + +*** Currently unemulated + + - Bit 5 of port 6 is tied to the /G2A and /G2B pins of the two + TC40H138P chips (~~ 74138?) that sit between the panel switches and the CPU. + I don't yet understand how these lines are actually used, but I can still use + the full functions of the keyboard if I just have it return 0 on read, so. + +*** CPU ports + + Port 2 is arranged as so: + Bit(s) Connection + 0 Controls the state of the power LED on the DX100. + The firmware will blink this LED when battery power is low. + This is unused on the DX27. + 1 0 (pulled to chassis ground by 220 ohm resistor) + 2 500khz clock + 3 MIDI In data bit + 4 MIDI Out data bit + 5 0 if the foot pedal is connected, 1 otherwise. + The manual says to use either the Yamaha FC-4 or Yamaha FC-5 foot pedals + with the DX100. + 6 Pulled TODO if the foot pedal is pressed. + 7 0 (pulled to chassis ground by 220 ohm resistor) + + Port 5 is connected to the panel switches and keyboard keys. + Which sets of buttons are exposed is determined by the low four bits + of port 6. The exact matrix differs between the DX27 and DX100. + + Port 6 is arranged as so: + Bit(s) Connection + 0-3 Which set of buttons to expose on port 5. + 4 Connected to the EOC line of the ADC. + 5 (see /G2A and /G2B comment in Currently unemulated) + 6 Connected to the REC (TS) line of the cassette interface. + 7 Connected to the PLAY (TL) line of the cassette interface. + +*** M58990 ports + + Port 0 is connected to the pitch wheel. + + Port 1 is connected to the mod wheel. + + Port 2 is connected to the breath controller. + The manual says to use the Yamaha BC-1 breath controller with the DX100. + + Port 3 is connected to the data entry slider. + + (TODO port 4 appears to be connected to chassis ground by 220 ohm resistor?) + + Port 5 is a voltmeter for the RAM battery. + + Port 6 is a voltmeter for system power. + + (TODO port 7 appears to be tied to port 6?) + +*** Default keyboard layout + + For quick testing, the default keyboard button layout will be: + 1 2 3 4 5 6 7 8 9 0 - = -> 1 2 3 4 5 6 7 8 9 10 11 12 + Q W T Y O P -> PBend KeyShift Store Func BankA BankB + E R U I [ ] -> -1 +1 Edit Internal BankC BankD + Octave 3 will be avaible over [Z S X D C V G B H N J M] + Pitch bend will be ' / + Mod wheel will be ; . + Data entry slider will be L , + + This leaves the breath controller, switches 13-24, and the other + keyboard octaves unmapped by default. + + When first starting with a fresh configuration and no NVRAM, + press Y and then O (Func and then BankA) to switch to the + internal voices. Note that some voices may not respond to + the pitch or mod wheels as much as other voices do. + + To disable memory protection, press Y, ], and then E (Func, + 12 to select the Memory Protection setting, and -1 to turn + it off). Then you can press U (Edit) to edit voice + parameters. + + IPT_KEYBOARD is used, so use the UI mode key (default + Scroll Lock) to re-enable the MAME UI. + + No, I don't know why some voices (such as Pianobells) + seem to not let you turn off modulation even if you set + the mod wheel to its lowest point. You can set the pitch + sensitivity value ("PMS", Edit/Compare key 9) to 0 to + force it off. This might have to do with the YM2164 + moving the LFO Reset register; if not, it might have to + do with the default pitch wheel position not being 0 + (see TODO below). + +*** Test mode + + To enter test mode, hold 1 and 2 on the panel while powering the system + on. You'll see the version number, and then a prompt asking if you want + to enter test mode; press +1 to enter test mode. + + If 1 and 3 or 1 and 4 are held instead of 1 and 2, different subsets of the + test mode will run instead. Furthermore, some tests will only be run on + the DX100. + + For more details on the individual tests, refer to the service manual. + + To make developing this driver easier I wrote an init function for the + DX100 1.1 driver which allows me to pick and choose which tests to actually + run. I've disabled it in the final version of the driver, but here it is + in this comment if you want to run it yourself: + +void yamaha_dx100_state::init_dx100() +{ + // Since test mode forcibly halts on the first failed test, and we can only choose to not run certain tests, use these blocks of code to skip specific tests. + // The numbering and naming below is from the service manual, which, surprise, doesn't quite line up with the way the tests are written in the code. + u8 *rom = (u8 *) (memregion("program")->base()); + auto skip = [rom](u16 whichAddr, u16 jumpTo, u8 extraInst = 0, u16 nop2 = 0) { + if (extraInst != 0) { + rom[whichAddr & 0x7fff] = extraInst; + whichAddr++; + } + rom[whichAddr & 0x7fff] = 0x7e; + rom[(whichAddr + 1) & 0x7fff] = (u8) ((jumpTo >> 8) & 0xff); + rom[(whichAddr + 2) & 0x7fff] = (u8) (jumpTo & 0xff); + if (nop2 != 0) { + rom[nop2 & 0x7fff] = 0x01; + rom[(nop2 + 1) & 0x7fff] = 0x01; + } + }; + // 1. Output level, RAM battery voltage + skip(0xc464, 0xc4b3); + // 2. RAM, cassette interface, MIDI in/out, MIDI thru + // This is hard to skip as a unit in the code, so you need to skip them individually: + // Or to only disable a subset of these tests: + // 2a. RAM test + rom[0xc55f & 0x7fff] = 0x39; + // 2b. Cassette interface test (I'm not entirely sure MAME's cassette interface + // supports the direct writing; I *think* this is MSX format?) + rom[0xc66e & 0x7fff] = 0x39; + // 2c. MIDI in/out (I'm not sure why this is failing, but something's not right) + rom[0xc631 & 0x7fff] = 0x39; + // 2d. MIDI thru + skip(0xc6b0, 0xc52a, 0, 0xc4dd); + // 3. LCD test + // This is actually two tests in the code, so you need to skip them individually: + // 2a. LCD solid flashing test + skip(0xc6e1, 0xc52a, 0, 0xc4e6); + // 2b. LCD checkerboard test + skip(0xc6d1, 0xc52a, 0, 0xc4ef); + // 4. Pitch wheel, mod wheel, data entry slider, breath controller, foot switch + // AND (run as part of 4 in the code) + // 5. Foot switch detecting circuit + skip(0xc70f, 0xc7a9); + // Or to only disable a subset of these tests: + // 4a/5. Foot switch tests + skip(0xc7b3, 0xc7a9, 0x38); + // 6. Keyboard + skip(0xc86e, 0xc927); + // 7. LCD contrast + // AND (run as part of 7 in the code) + // 8. Main power, LED flash on low power + skip(0xc927, 0xc98f); + // 9. Panel switches + skip(0xc9d3, 0xca50); +} *******************************************************************************/ @@ -14,6 +241,7 @@ #include "bus/midi/midi.h" #include "cpu/m6800/m6801.h" +#include "imagedev/cassette.h" #include "machine/adc0808.h" #include "machine/clock.h" #include "machine/nvram.h" @@ -24,6 +252,8 @@ #include "screen.h" #include "speaker.h" +#include "dx100.lh" + namespace { class yamaha_dx100_state : public driver_device @@ -32,14 +262,23 @@ public: yamaha_dx100_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_keysbuttons(*this, "P6_%d", 0) , m_midi_in(true) + , m_led(*this, "LED") + , m_cassette(*this, "cassette") { } void dx100(machine_config &config); + DECLARE_WRITE_LINE_MEMBER(led_w) { m_led = state; } + DECLARE_CUSTOM_INPUT_MEMBER(midi_in_r) { return m_midi_in; } + protected: + virtual void driver_start() override; virtual void machine_start() override; + virtual void machine_reset() override; private: HD44780_PIXEL_UPDATE(lcd_pixel_update); @@ -50,26 +289,46 @@ private: void mem_map(address_map &map); required_device m_maincpu; + required_device m_adc; + required_ioport_array<16> m_keysbuttons; bool m_midi_in; + + u8 m_port6_val; + + // for hooking up the LED to layouts + output_finder<> m_led; + + required_device m_cassette; }; +void yamaha_dx100_state::driver_start() +{ + m_led.resolve(); +} + void yamaha_dx100_state::machine_start() { save_item(NAME(m_midi_in)); } +void yamaha_dx100_state::machine_reset() +{ + // TODO: figure out the actual power-on state + m_port6_val = 0; +} + HD44780_PIXEL_UPDATE(yamaha_dx100_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; + if (x < 5 && y < 8 && line < 2 && pos < 8) + bitmap.pix(y + 1 + ((y == 7) ? 1 : 0), (line * 8 + pos) * 6 + x + 1) = state ? 1 : 2; } void yamaha_dx100_state::palette_init(palette_device &palette) { - palette.set_pen_color(0, rgb_t(0x00, 0x00, 0x00)); // background - palette.set_pen_color(1, rgb_t(0xff, 0xff, 0xff)); // lcd pixel on - palette.set_pen_color(2, rgb_t(0x18, 0x18, 0x18)); // lcd pixel off + palette.set_pen_color(0, rgb_t(0xff, 0xff, 0xff)); // background + palette.set_pen_color(1, rgb_t(0x00, 0x00, 0x00)); // lcd pixel on + palette.set_pen_color(2, rgb_t(0xe7, 0xe7, 0xe7)); // lcd pixel off } WRITE_LINE_MEMBER(yamaha_dx100_state::p22_w) @@ -92,20 +351,263 @@ void yamaha_dx100_state::mem_map(address_map &map) } static INPUT_PORTS_START(dx100) + PORT_START("P2") + // TODO: Should 0x02, 0x04, 0x10, and 0x80 be listed here? + // They should be handled by the other interconnections in this file. + // If so, verify the active states of the MIDI ports. + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OUTPUT ) PORT_NAME("LED") PORT_WRITE_LINE_MEMBER(yamaha_dx100_state, led_w) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // tied to ground + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) // 500khz clock + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(yamaha_dx100_state, midi_in_r) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT ) // MIDI out + PORT_CONFNAME( 0x20, 0x00, "Foot Switch" ) + PORT_CONFSETTING( 0x00, "Connected" ) + PORT_CONFSETTING( 0x20, "Disconnected" ) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Foot Switch") + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // tied to ground + + PORT_START("P6_0") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Data Entry +1/Yes/On") PORT_CODE(KEYCODE_R) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Data Entry -1/No/Off") PORT_CODE(KEYCODE_E) + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Store/EG Copy") PORT_CODE(KEYCODE_T) + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Function/Compare") PORT_CODE(KEYCODE_Y) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Edit/Compare") PORT_CODE(KEYCODE_U) + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Internal/Play") PORT_CODE(KEYCODE_I) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Pitch B Mode/Operator Select/Mode Set") PORT_CODE(KEYCODE_Q) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Key Shift/Key Set") PORT_CODE(KEYCODE_W) + + PORT_START("P6_1") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("1/Algorithm/Master Tune Adj") PORT_CODE(KEYCODE_1) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("2/Feedback/MIDI On/Off") PORT_CODE(KEYCODE_2) + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("3/LFO Wave/MIDI Channel") PORT_CODE(KEYCODE_3) + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("4/LFO Speed/MIDI Ch Info") PORT_CODE(KEYCODE_4) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("5/LFO Delay/MIDI Sys Info") PORT_CODE(KEYCODE_5) + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("6/LFO PMD/Recall Edit") PORT_CODE(KEYCODE_6) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("7/LFO AMD/Init Voice") PORT_CODE(KEYCODE_7) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("8/LFO Sync/Edit Bank") PORT_CODE(KEYCODE_8) + + PORT_START("P6_2") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("9/Modulation Sensitivity Pitch/Cassette Save/Verify") PORT_CODE(KEYCODE_9) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("10/Modulation Sensitivity Velocity/Cassette Load") PORT_CODE(KEYCODE_0) + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("11/Modulation Sensitivity EG Bias/Cassette Load Single") PORT_CODE(KEYCODE_MINUS) + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("12/Key Velocity/Memory Protect") PORT_CODE(KEYCODE_EQUALS) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("13/Oscillator Freq Ratio/Poly/Mono") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("14/Oscillator Detune/Pitch Bend Range") + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("15/Envelope Generator AR/Portamento Mode") + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("16/Envelope Generator D1R/Portamento Time") + + PORT_START("P6_3") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("17/Envelope Generator D1L/Foot Sw Assign") + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("18/Envelope Generator D2R/Wheel Range Pitch") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("19/Envelope Generator RR/Wheel Range Amplitude") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("20/Operator Out Level/Breath Range Pitch") + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("21/Keyboard Scaling Rate/Breath Range Amplitude") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("22/Keyboard Scaling Level/Breath Range Pitch Bias") + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("23/Transpose/Breath Range EG Bias") + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("24/Voice Name Cursor >") + + PORT_START("P6_4") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C#1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C#2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C#3") PORT_CODE(KEYCODE_S) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C#4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Bank D/Operator/AMS On/Off 4/Preset Search 401~424") PORT_CODE(KEYCODE_CLOSEBRACE) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Bank C/Operator/AMS On/Off 3/Preset Search 301~324") PORT_CODE(KEYCODE_OPENBRACE) + + PORT_START("P6_5") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D3") PORT_CODE(KEYCODE_X) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Bank B/Operator/AMS On/Off 2/Preset Search 201~224") PORT_CODE(KEYCODE_P) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Bank A/Operator/AMS On/Off 1/Preset Search 101~124") PORT_CODE(KEYCODE_O) + + PORT_START("P6_6") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D#1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D#2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D#3") PORT_CODE(KEYCODE_D) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D#4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_7") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E3") PORT_CODE(KEYCODE_C) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("E4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_8") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_V) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_9") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F#1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F#2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F#3") PORT_CODE(KEYCODE_G) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F#4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_10") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G3") PORT_CODE(KEYCODE_B) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_11") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G#1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G#2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G#3") PORT_CODE(KEYCODE_H) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("G#4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_12") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A3") PORT_CODE(KEYCODE_N) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_13") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A#1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A#2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A#3") PORT_CODE(KEYCODE_J) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("A#4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_14") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B1") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B2") + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B3") PORT_CODE(KEYCODE_M) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("B4") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("P6_15") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C1") + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C2") + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C3") PORT_CODE(KEYCODE_Z) + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C4") + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("C5") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("AN0") + // The pitch wheel returns to center once released. + PORT_BIT( 0xff, 0x7f, IPT_AD_STICK_Y ) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CODE_INC(KEYCODE_QUOTE) PORT_CODE_DEC(KEYCODE_SLASH) + + PORT_START("AN1") + // The mod wheel stays in place to wherever it's set. + // TODO: This should have its initial state be 0, not halfway. + PORT_BIT( 0xff, 0, IPT_PADDLE_V ) PORT_NAME("Modulation Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CENTERDELTA(0) PORT_CODE_INC(KEYCODE_COLON) PORT_CODE_DEC(KEYCODE_STOP) + + PORT_START("AN2") + // TODO: This appears to be inverted; if this is set to 255 it behaves as if + // there was no breath controller? or at least seems to? on instruments like + // 112 Pianobrass if this is set to 0 it acts as if the mod wheel had been turned + // all the way up and enables LFO -- and there's probably a better way we could + // simulate not having a breath controller attached at all??? + PORT_BIT( 0xff, 0, IPT_PEDAL ) PORT_NAME("Breath Controller") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) + + PORT_START("AN3") + // The data entry slider stays in place to wherever it's set. + PORT_BIT( 0xff, 0, IPT_POSITIONAL_V ) PORT_NAME("Data Entry Slider") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0xff) PORT_CENTERDELTA(0) PORT_CODE_INC(KEYCODE_L) PORT_CODE_DEC(KEYCODE_COMMA) + + PORT_START("AN5") + PORT_CONFNAME( 0xff, 0x80, "Internal RAM Battery Level" ) + // "CNG RAM BATTERY!" displayed unless value is between 0x70 and 0xcc + PORT_CONFSETTING( 0x6f, "Too Low" ) + PORT_CONFSETTING( 0x70, "Lowest Allowed" ) + PORT_CONFSETTING( 0x80, "Normal" ) // for some arbitrary definition of "normal" + PORT_CONFSETTING( 0xcb, "Highest Allowed" ) + PORT_CONFSETTING( 0xcc, "Too High" ) + + PORT_START("AN6") + PORT_CONFNAME( 0xff, 0x00, "Battery Power Level" ) + // Yes, higher values mean lower voltages. + // I think this is opposite to how the RAM battery voltmeter works. + // The weird granularity here is due to the buggy implementation of the + // test in the test mode; all 7V values should LED flash, but some don't. + PORT_CONFSETTING( 0x00, "9V (Normal)" ) // for some arbitrary definition of "normal" + PORT_CONFSETTING( 0x4b, "9V (Lowest Allowed)" ) + PORT_CONFSETTING( 0x67, "7V (Highest Allowed Without LED Flash)" ) + PORT_CONFSETTING( 0x6b, "7V (Lowest Allowed Without LED Flash)" ) + PORT_CONFSETTING( 0x6c, "7V (Highest Allowed With LED Flash)" ) + PORT_CONFSETTING( 0x6f, "7V (Lowest Allowed With LED Flash)" ) + PORT_CONFSETTING( 0x70, "Less Than 7V" ) INPUT_PORTS_END void yamaha_dx100_state::dx100(machine_config &config) { HD6303X(config, m_maincpu, 7.15909_MHz_XTAL / 2); // HD6303XP m_maincpu->set_addrmap(AS_PROGRAM, &yamaha_dx100_state::mem_map); - m_maincpu->in_p2_cb().set([this]() -> u8 { return m_midi_in ? 0x04 : 0; }); - m_maincpu->in_p6_cb().set("adc", FUNC(m58990_device::eoc_r)).lshift(4); + m_maincpu->in_p2_cb().set_ioport("P2"); + m_maincpu->out_p2_cb().set_ioport("P2"); + m_maincpu->in_p6_cb().set([this]() -> u8 { + // TODO: The threshold here and the output values below should be double-checked. + u8 casplay = (m_cassette->input() > 0.009) ? 0x80 : 0; + // TODO: Need to verify if this bit is held on output or not + u8 casrec = m_port6_val & 0x40; + u8 adcval = ((u8) (m_adc->eoc_r() << 4)) & 0x10; + u8 port5line = (m_port6_val & 0x2f); + return casplay | casrec | adcval | port5line; + }); + m_maincpu->out_p6_cb().set([this](u8 val) { + m_port6_val = val; + m_cassette->output(((m_port6_val & 0x40) != 0) ? -1.0 : 1.0); + }); + m_maincpu->in_p5_cb().set([this]() -> u8 { + u8 line = m_port6_val & 0x2f; + // TODO: figure out how the /G2A and /G2B pins are connected + if ((line & 0x20) != 0) + return 0x00; + return (u8) (m_keysbuttons[line]->read() & 0xff); + }); m_maincpu->out_ser_tx_cb().set("mdout", FUNC(midi_port_device::write_txd)); NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5518BPL + CR2032T battery - m58990_device &adc(M58990(config, "adc", 7.15909_MHz_XTAL / 8)); // M58990P-1 (clocked by E) - adc.in_callback<5>().set_constant(0x80); // "CNG RAM BATTERY!" displayed unless value is between 0x70 and 0xCC + M58990(config, m_adc, 7.15909_MHz_XTAL / 8); // M58990P-1 (clocked by E) + m_adc->in_callback<0>().set_ioport("AN0"); // pitch wheel + m_adc->in_callback<1>().set_ioport("AN1"); // mod wheel + m_adc->in_callback<2>().set_ioport("AN2"); // breath controller + m_adc->in_callback<3>().set_ioport("AN3"); // data entry slider + m_adc->in_callback<5>().set_ioport("AN5"); // internal RAM battery voltmeter + m_adc->in_callback<6>().set_ioport("AN6"); // battery power voltmeter CLOCK(config, "subclock", 500_kHz_XTAL).signal_handler().set(FUNC(yamaha_dx100_state::p22_w)); @@ -116,14 +618,14 @@ void yamaha_dx100_state::dx100(machine_config &config) screen.set_refresh_hz(60); screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */ screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update)); - screen.set_size(6*16+1, 10*2+1); + screen.set_size(6*16+1, 10*1+1); screen.set_visarea_full(); screen.set_palette("palette"); PALETTE(config, "palette", FUNC(yamaha_dx100_state::palette_init), 3); hd44780_device &lcdc(HD44780(config, "lcdc", 0)); // HD44780RA00 - lcdc.set_lcd_size(2, 16); + lcdc.set_lcd_size(1, 16); lcdc.set_pixel_update_cb(FUNC(yamaha_dx100_state::lcd_pixel_update)); SPEAKER(config, "lspeaker").front_left(); @@ -132,6 +634,12 @@ void yamaha_dx100_state::dx100(machine_config &config) ym2164_device &ymsnd(YM2164(config, "ymsnd", 7.15909_MHz_XTAL / 2)); // with YM3014 DAC ymsnd.add_route(0, "lspeaker", 0.60); ymsnd.add_route(1, "rspeaker", 0.60); + + CASSETTE(config, m_cassette); + m_cassette->set_default_state(CASSETTE_STOPPED); + m_cassette->set_interface("ymdx100_cass"); + + config.set_default_layout(layout_dx100); } ROM_START(dx100) @@ -141,4 +649,5 @@ ROM_END } // anonymous namespace -SYST(1985, dx100, 0, 0, dx100, dx100, yamaha_dx100_state, empty_init, "Yamaha", "DX100 Digital Programmable Algorithm Synthesizer", MACHINE_IS_SKELETON) +SYST(1985, dx100, 0, 0, dx100, dx100, yamaha_dx100_state, empty_init, "Yamaha", "DX100 Digital Programmable Algorithm Synthesizer", MACHINE_IMPERFECT_SOUND) +