// license:BSD-3-Clause // copyright-holders:R. Belmont /* Yamaha PSR-340 PortaSound keyboard Preliminary driver by R. Belmont CPU and Sound: SWX00B, which is an H8S/2000 series CPU and Yamaha SWP00? (or SWP30?) on one chip. LCD controller: KS0066U (apparently equivalent to Samsung S6A0069) FDC: HD63266F (uPD765 derivative) RAM: 256KiB, 2x uPD431000 or equivalent TODO: - Customized CPU. Interrupt controller is H8S/2655 style, but there are customizations too. - LCD (it's similar to the MU-5's) - Front panel / keyboard scanner (6305 with internal ROM manages this on hardware) - Sound generation */ #include "emu.h" #include "cpu/h8/h8s2655.h" #include "sound/swp00.h" #include "video/hd44780.h" #include "bus/midi/midi.h" #include "emupal.h" #include "screen.h" #include "speaker.h" class psr340_state : public driver_device { public: psr340_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_swp00(*this, "swp00"), m_lcdc(*this, "ks0066"), m_outputs(*this, "%02d.%x.%x", 0U, 0U, 0U), m_key(*this, "S%c", 'A') { } void psr340(machine_config &config); protected: virtual void machine_start() override; virtual void machine_reset() override; private: required_device m_maincpu; required_device m_swp00; required_device m_lcdc; output_finder<80, 8, 5> m_outputs; required_ioport_array<8> m_key; void psr340_map(address_map &map); void psr340_io_map(address_map &map); void lcd_ctrl_w(u8 data); void lcd_data_w(u8 data); DECLARE_WRITE_LINE_MEMBER(render_w); u8 m_matrixsel = 0U; u8 matrix_r(); }; u8 psr340_state::matrix_r() { u8 data = 0; for (int i = 0; i < 8; i++) { if (BIT(m_matrixsel, i)) { data |= m_key[i]->read(); } } return data; } void psr340_state::lcd_ctrl_w(u8 data) { // bit 3 = E, bit 4 = RS, R/W is connected to GND so write-only // all bits are also matrix select for reading the controls m_lcdc->rs_w(BIT(data, 4)); m_lcdc->e_w(BIT(data, 3)); } void psr340_state::lcd_data_w(u8 data) { m_lcdc->db_w(data); m_matrixsel = data; } void psr340_state::psr340_map(address_map &map) { map(0x000000, 0x1fffff).rom().region("maincpu", 0); map(0x400000, 0x43ffff).ram(); // Work RAM? Or SWP / MEG? map(0x600000, 0x600000).lr8(NAME([]() -> uint8_t { return 0x80; })); // FDC status // map(0xffe000, 0xffe7ff).ram(); map(0xffe027, 0xffe027).r(FUNC(psr340_state::matrix_r)); map(0xffe02a, 0xffe02a).w(FUNC(psr340_state::lcd_ctrl_w)); map(0xffe02b, 0xffe02b).w(FUNC(psr340_state::lcd_data_w)); map(0xffe02f, 0xffe02f).lr8(NAME([]() -> uint8_t { return 0xff; })); } void psr340_state::psr340_io_map(address_map &map) { } void psr340_state::machine_start() { m_outputs.resolve(); } void psr340_state::machine_reset() { } WRITE_LINE_MEMBER(psr340_state::render_w) { if(!state) return; const u8 *render = m_lcdc->render(); if(0) { logerror("XX -\n"); for(int i=2; i != 3; i++) { for(int y=0; y != 8; y++) { std::string r = "XX"; for(int x=0; x != 10; x++) { int idx = x+20*i; uint8_t v = render[16*(x+20*i) + y]; r += ' '; for(int b=4; b >= 0; b--) { bool known = (idx == 43 && b <= 3) || (idx == 44) || (idx == 45) || (idx == 46 && b >= 3); known = known || (idx == 46 && b == 2 && y >= 5); known = known || (idx == 46 && b == 1 && y >= 4); known = known || (idx == 46 && b == 0 && y >= 5); known = known || (idx == 47 && b == 4 && y >= 4); known = known || (idx == 47 && b == 3 && y >= 5); known = known || (idx == 47 && b == 2 && y >= 4); known = known || (idx == 43 && b == 4 && y >= 4); known = known || (idx == 42 && b == 0 && y >= 4 && y <= 6); known = known || (idx == 42 && b == 1 && y >= 4); known = known || (idx == 42 && b == 2 && y >= 4 && y <= 6); known = known || (idx == 42 && b == 3 && y >= 4); known = known || (idx == 42 && b == 4 && y >= 4 && y <= 6); known = known || (idx == 41 && b == 1 && y >= 4); known = known || (idx == 41 && b == 2 && y >= 4 && y <= 6); known = known || (idx == 41 && b == 3 && y >= 4); known = known || (idx == 41 && b == 4 && y >= 4 && y <= 6); known = known || (idx == 40 && b == 0 && y >= 4); known = known || (idx == 40 && b == 1 && y >= 4 && y <= 6); known = known || (idx == 40 && b == 4 && y >= 1 && y <= 6); known = known || (idx == 46 && y == 3 && b == 2); known = known || (idx == 46 && y == 2 && b == 1); known = known || (idx == 46 && y == 3 && b == 1); known = known || (idx == 46 && y == 2 && b == 0); known = known || (idx == 46 && y == 3 && b == 0); known = known || (idx == 47 && y == 3 && b == 4); known = known || (idx == 47 && y == 4 && b == 3); known = known || (idx == 40 && y == 7 && b == 2); known = known || (idx == 40 && y == 7 && b == 1); known = known || (idx == 41 && y == 7 && b == 4); known = known || (idx == 41 && y == 7 && b == 2); known = known || (idx == 42 && y == 7 && b == 4); known = known || (idx == 42 && y == 7 && b == 2); known = known || (idx == 42 && y == 7 && b == 0); known = known || (idx == 47 && y == 3 && b == 3); known = known || (idx == 47 && y == 3 && b == 2); known = known || (idx == 47 && y == 1 && b == 1); known = known || (idx == 47 && y == 1 && b == 0); known = known || (idx == 47 && y == 2 && b == 4); known = known || (idx == 41 && y == 4 && b == 0); bool is = v & (0x01 << b); r += known ? '_' : is ? '#' : '.'; } } logerror("%s\n", r); } logerror("XX\n"); } } for(int yy=0; yy != 8; yy++) for(int x=0; x != 80; x++) { uint8_t v = render[16*x + yy]; for(int xx=0; xx != 5; xx++) m_outputs[x][yy][xx] = (v >> xx) & 1; } } static INPUT_PORTS_START(psr340) PORT_START("SA") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Song") PORT_CODE(KEYCODE_Z) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Record") PORT_CODE(KEYCODE_X) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Main A") PORT_CODE(KEYCODE_C) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("-") PORT_CODE(KEYCODE_LEFT) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Execute") PORT_CODE(KEYCODE_ENTER) PORT_START("SB") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Function") PORT_CODE(KEYCODE_A) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Dual") PORT_CODE(KEYCODE_S) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Intro/End") PORT_CODE(KEYCODE_D) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Metronome") PORT_CODE(KEYCODE_F) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Tenkey +") PORT_CODE(KEYCODE_EQUALS) PORT_START("SC") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 6") PORT_CODE(KEYCODE_Y) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Touch") PORT_CODE(KEYCODE_G) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Start/Stop") PORT_CODE(KEYCODE_H) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Porta Grand") PORT_CODE(KEYCODE_J) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Tenkey -") PORT_CODE(KEYCODE_MINUS) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_START("SD") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 5") PORT_CODE(KEYCODE_T) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Harmony") PORT_CODE(KEYCODE_Z) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Sync Start") PORT_CODE(KEYCODE_X) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Utility") PORT_CODE(KEYCODE_C) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_START("SE") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 4") PORT_CODE(KEYCODE_R) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Reverb") PORT_CODE(KEYCODE_V) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Accomp On") PORT_CODE(KEYCODE_N) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Save") PORT_CODE(KEYCODE_M) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_START("SF") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 3") PORT_CODE(KEYCODE_E) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Demo") PORT_CODE(KEYCODE_COMMA) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Load") PORT_CODE(KEYCODE_STOP) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("+") PORT_CODE(KEYCODE_RIGHT) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_START("SG") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 2") PORT_CODE(KEYCODE_W) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Chord Guide") PORT_CODE(KEYCODE_SPACE) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Voice") PORT_CODE(KEYCODE_SLASH) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_START("SH") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("O.T.S. 1") PORT_CODE(KEYCODE_Q) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Main B") PORT_CODE(KEYCODE_LSHIFT) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Style") PORT_CODE(KEYCODE_LCONTROL) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("3") PORT_CODE(KEYCODE_3) INPUT_PORTS_END void psr340_state::psr340(machine_config &config) { /* basic machine hardware */ H8S2655(config, m_maincpu, 16_MHz_XTAL); // gives correct MIDI serial rate and matches MU100, but doesn't line up exactly with schematic value m_maincpu->set_addrmap(AS_PROGRAM, &psr340_state::psr340_map); m_maincpu->set_addrmap(AS_IO, &psr340_state::psr340_io_map); // SCI0 is externally clocked at the 31250 Hz MIDI rate m_maincpu->subdevice("sci0")->set_external_clock_period(attotime::from_hz(31250 * 16)); KS0066_F05(config, m_lcdc, 0); m_lcdc->set_lcd_size(2, 40); /* video hardware */ auto &screen = SCREEN(config, "screen", SCREEN_TYPE_SVG); screen.set_refresh_hz(60); screen.set_size(800, 384); screen.set_visarea_full(); screen.screen_vblank().set(FUNC(psr340_state::render_w)); MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set("maincpu:sci0", FUNC(h8_sci_device::rx_w)); auto &mdout(MIDI_PORT(config, "mdout", midiout_slot, "midiout")); m_maincpu->subdevice("sci0")->tx_handler().set(mdout, FUNC(midi_port_device::write_txd)); SPEAKER(config, "lspeaker").front_left(); SPEAKER(config, "rspeaker").front_right(); SWP00(config, m_swp00); m_swp00->add_route(0, "lspeaker", 1.0); m_swp00->add_route(1, "rspeaker", 1.0); } ROM_START( psr340 ) ROM_REGION(0x200000, "maincpu", 0) ROM_LOAD16_WORD_SWAP("xv89710.bin", 0x000000, 0x200000, CRC(271ccb8a) SHA1(ec6abbdb82a5e851b77338c79ecabfd8040f023d)) // patch out battery check for now (SWX00B customized H8S reads ADC *value* at FFFF90) ROM_FILL(0x20e6, 1, 0x54) ROM_FILL(0x20e7, 1, 0x70) ROM_REGION(0x200000, "swp00", 0) ROM_LOAD("xv89810.bin", 0x000000, 0x200000, CRC(10e68363) SHA1(5edee814bf07c49088da44474fdd5c817e7c5af0)) ROM_REGION(0x5704b, "screen", 0) ROM_LOAD("psr340-lcd.svg", 0, 0x5704b, CRC(d93af0a9) SHA1(76156443025e0b4089259417bb266888c547b2d7)) ROM_END CONS(1994, psr340, 0, 0, psr340, psr340, psr340_state, empty_init, "Yamaha", "PSR-340 PortaSound", MACHINE_NOT_WORKING)