diff --git a/src/mame/ensoniq/esq5505.cpp b/src/mame/ensoniq/esq5505.cpp index 3662ea7e68ad6..cbf71239fcf22 100644 --- a/src/mame/ensoniq/esq5505.cpp +++ b/src/mame/ensoniq/esq5505.cpp @@ -255,7 +255,8 @@ class esq5505_state : public driver_device uint16_t lower_r(offs_t offset); void lower_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); - uint16_t analog_r(); + uint16_t adc_r(); // read from the ADC, input selected per m_uart_dio + uint16_t analog_r(offs_t offset, uint16_t mask); // read from analog inoput [offset] void analog_w(offs_t offset, uint16_t data); void duart_output(uint8_t data); @@ -427,11 +428,16 @@ void esq5505_state::analog_w(offs_t offset, uint16_t data) m_analog_values[offset] = data; } -uint16_t esq5505_state::analog_r() +uint16_t esq5505_state::adc_r() { return m_analog_values[m_duart_io & 7]; } +uint16_t esq5505_state::analog_r(offs_t offset, uint16_t mask) +{ + return m_analog_values[offset & 0x7]; +} + void esq5505_state::duart_output(uint8_t data) { floppy_image_device *floppy = m_floppy_connector ? m_floppy_connector->get_device() : nullptr; @@ -553,6 +559,7 @@ void esq5505_state::vfx(machine_config &config) ESQPANEL2X40_VFX(config, m_panel); m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w)); m_panel->write_analog().set(FUNC(esq5505_state::analog_w)); + m_panel->read_analog().set(FUNC(esq5505_state::analog_r)); MC68681(config, m_duart, 4000000); m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_3); @@ -580,7 +587,7 @@ void esq5505_state::vfx(machine_config &config) es5505.set_region1("waverom2"); /* Bank 1 */ es5505.set_channels(4); /* channels */ es5505.irq_cb().set_inputline(m_maincpu, M68K_IRQ_1); - es5505.read_port_cb().set(FUNC(esq5505_state::analog_r)); /* ADC */ + es5505.read_port_cb().set(FUNC(esq5505_state::adc_r)); /* ADC read */ es5505.add_route(0, "pump", 1.0, 0); es5505.add_route(1, "pump", 1.0, 1); es5505.add_route(2, "pump", 1.0, 2); @@ -602,6 +609,7 @@ void esq5505_state::eps(machine_config &config) ESQPANEL1X22(config.replace(), m_panel); m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w)); m_panel->write_analog().set(FUNC(esq5505_state::analog_w)); + m_panel->read_analog().set(FUNC(esq5505_state::analog_r)); WD1772(config, m_fdc, 8_MHz_XTAL); FLOPPY_CONNECTOR(config, m_floppy_connector); @@ -643,6 +651,7 @@ void esq5505_state::vfx32(machine_config &config) ESQPANEL2X40_VFX(config, m_panel); m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w)); m_panel->write_analog().set(FUNC(esq5505_state::analog_w)); + m_panel->read_analog().set(FUNC(esq5505_state::analog_r)); MC68681(config, m_duart, 4000000); m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_3); @@ -670,7 +679,7 @@ void esq5505_state::vfx32(machine_config &config) es5505.set_region1("waverom2"); /* Bank 1 */ es5505.set_channels(4); /* channels */ es5505.irq_cb().set_inputline(m_maincpu, M68K_IRQ_1); - es5505.read_port_cb().set(FUNC(esq5505_state::analog_r)); /* ADC */ + es5505.read_port_cb().set(FUNC(esq5505_state::adc_r)); /* ADC read */ es5505.add_route(0, "pump", 1.0, 0); es5505.add_route(1, "pump", 1.0, 1); es5505.add_route(2, "pump", 1.0, 2); @@ -692,6 +701,7 @@ void esq5505_state::sq1(machine_config &config) ESQPANEL2X16_SQ1(config.replace(), m_panel); m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w)); m_panel->write_analog().set(FUNC(esq5505_state::analog_w)); + m_panel->read_analog().set(FUNC(esq5505_state::analog_r)); } void esq5505_state::ks32(machine_config &config) @@ -702,6 +712,7 @@ void esq5505_state::ks32(machine_config &config) ESQPANEL2X16_SQ1(config.replace(), m_panel); m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w)); m_panel->write_analog().set(FUNC(esq5505_state::analog_w)); + m_panel->read_analog().set(FUNC(esq5505_state::analog_r)); } static INPUT_PORTS_START( vfx ) diff --git a/src/mame/ensoniq/esqpanel.cpp b/src/mame/ensoniq/esqpanel.cpp index 1343ab289d1aa..afc46ea9fdafc 100644 --- a/src/mame/ensoniq/esqpanel.cpp +++ b/src/mame/ensoniq/esqpanel.cpp @@ -4,14 +4,18 @@ Ensoniq panel/display device */ #include "emu.h" +#include "ioport.h" #include "esqpanel.h" +#include "extpanel.h" + +#include "vfx.lh" +#include "vfxsd.lh" +#include "sd1.lh" -#include "http.h" #include "main.h" -//************************************************************************** -// External panel support -//************************************************************************** +#define VERBOSE 0 +#include "logmacro.h" #include #include @@ -20,375 +24,6 @@ #include #include - -namespace esqpanel { - - class external_panel; - - using external_panel_ptr = std::shared_ptr; - typedef std::map> connection_to_panel_map; - - enum message_type { - UNKNOWN = 0, - ANALOG = 1 << 0, - BUTTON = 1 << 1, - CONTROL = 1 << 2, - DISPLAY = 1 << 3, - INFO = 1 << 4 - }; - - class external_panel - { - public: - static int get_message_type(const char c) - { - switch(c) - { - case 'A': - return message_type::ANALOG; - case 'B': - return message_type::BUTTON; - case 'C': - return message_type::CONTROL; - case 'D': - return message_type::DISPLAY; - case 'I': - return message_type::INFO; - default: - return message_type::UNKNOWN; - } - } - - external_panel() : m_send_message_types(0) - { - // printf("session: constructed\n"); - } - - int handle_control_message(const std::string &command) - { - int old = m_send_message_types; - std::istringstream is(command); - if (get_message_type(is.get()) != message_type::CONTROL) - { - return 0; - } - - int c; - while ((c = is.get()) != EOF) - { - int message_type = external_panel::get_message_type(char(uint8_t(unsigned(c)))); - int n; - is >> n; - if (n != 0) - { - m_send_message_types |= message_type; - } - else - { - m_send_message_types &= ~message_type; - } - } - - return m_send_message_types ^ old; - } - - int send_message_types() - { - return m_send_message_types; - } - - bool send_display_data() - { - return m_send_message_types & message_type::DISPLAY; - } - - bool send_analog_values() - { - return m_send_message_types & message_type::ANALOG; - } - - bool send_buttons() - { - return m_send_message_types & message_type::BUTTON; - } - - private: - int m_send_message_types; - }; - - class external_panel_server - { - public: - enum websocket_opcode { - text = 1, - binary = 2 - }; - external_panel_server(http_manager *webserver) : - m_server(webserver), - m_keyboard("unknown"), - m_version("1") - { - using namespace std::placeholders; - if (m_server->is_active()) { - m_server->add_endpoint("/esqpanel/socket", - std::bind(&external_panel_server::on_open, this, _1), - std::bind(&external_panel_server::on_message, this, _1, _2, _3), - std::bind(&external_panel_server::on_close, this, _1, _2, _3), - std::bind(&external_panel_server::on_error, this, _1, _2) - ); - } - } - - virtual ~external_panel_server() - { - if (m_server->is_active()) { - m_server->remove_endpoint("/esqpanel/socket"); - } - } - - void send_to_all(char c) - { - // printf("server: send_to_all(%02x)\n", ((unsigned int) c) & 0xff); - std::lock_guard lock(m_mutex); - // printf("server: sending '%02x' to all\n", ((unsigned int) c) & 0xff); - m_to_send.str(""); - m_to_send.put('D'); - m_to_send.put(c); - const std::string &s = m_to_send.str(); - - for (const auto &iter: m_panels) - { - external_panel_ptr panel = iter.second; - if (panel->send_display_data()) - { - send(iter.first, s); - } - } - } - - void on_open(http_manager::websocket_connection_ptr connection) - { - using namespace std::placeholders; - - std::lock_guard lock(m_mutex); - m_panels[connection] = std::make_shared(); - } - - void on_message(http_manager::websocket_connection_ptr connection, const std::string &payload, int opcode) - { - external_panel_ptr panel = external_panel_for_connection(connection); - const std::string &command = payload; - - int t = external_panel::get_message_type(command.front()); - - if (t == message_type::CONTROL) - { - int changed = panel->handle_control_message(command); - // printf("server: control message, changed = '%x'\n", changed); - if ((changed & message_type::DISPLAY) && panel->send_display_data()) - { - // printf("server: control message, sending contents\n"); - send_contents(connection); - } - - if ((changed & message_type::ANALOG) && panel->send_analog_values()) - { - // printf("server: control message, sending analog values\n"); - send_analog_values(connection); - } - - if ((changed & message_type::BUTTON) && panel->send_buttons()) - { - // printf("server: control message, sending button states\n"); - send_button_states(connection); - } - } - else if (t == message_type::INFO) - { - std::ostringstream o; - o << "I" << get_keyboard() << "," << get_version(); - send(connection, o.str()); - } - else - { - { - std::lock_guard lock(m_mutex); - m_commands.emplace_back(command); - } - - // Echo the non-command message to any other connected panels that want it - for (const auto &iter: m_panels) - { - external_panel_ptr other_panel = iter.second; - if (other_panel != panel && (t & other_panel->send_message_types()) != 0) - { - send(iter.first, command); - } - } - } - } - - void on_close(http_manager::websocket_connection_ptr connection, int status, const std::string& reason) - { - std::lock_guard lock(m_mutex); - m_panels.erase(connection); - } - - void on_error(http_manager::websocket_connection_ptr connection, const std::error_code& error_code) - { - std::lock_guard lock(m_mutex); - m_panels.erase(connection); - } - - void on_document_request(http_manager::http_request_ptr request, http_manager::http_response_ptr response, const std::string &filename) - { - m_server->serve_document(request, response, filename); - } - - void on_template_request(http_manager::http_request_ptr request, http_manager::http_response_ptr response, const std::string &filename) - { - using namespace std::placeholders; - m_server->serve_template(request, response, filename, std::bind(&external_panel_server::get_template_value, this, _1), '$', '$'); - } - - external_panel_ptr external_panel_for_connection(http_manager::websocket_connection_ptr connection) - { - auto it = m_panels.find(connection); - - if (it == m_panels.end()) { - // this connection is not in the list. This really shouldn't happen - // and probably means something else is wrong. - throw std::invalid_argument("No panel avaliable for connection"); - } - - return it->second; - } - - bool has_commands() - { - // printf("server: has_commands()\n"); - std::lock_guard lock(m_mutex); - return !m_commands.empty(); - } - - std::string get_next_command() - { - // printf("server: get_next_command()\n"); - std::lock_guard lock(m_mutex); - std::string command = std::move(m_commands.front()); - m_commands.pop_front(); - return command; - } - - void set_index(const std::string &index) - { - m_index = index; - } - - void add_http_document(const std::string &path, const std::string &filename) - { - m_server->remove_http_handler(path); - if (filename != "") - { - using namespace std::placeholders; - m_server->add_http_handler(path, std::bind(&external_panel_server::on_document_request, this, _1, _2, filename)); - } - } - - void add_http_template(const std::string &path, const std::string &filename) - { - m_server->remove_http_handler(path); - if (filename != "") - { - using namespace std::placeholders; - m_server->add_http_handler(path, std::bind(&external_panel_server::on_template_request, this, _1, _2, filename)); - } - } - - void set_content_provider(std::function provider) - { - m_content_provider = provider; - } - - void set_keyboard(const std::string &keyboard) - { - m_keyboard = keyboard; - } - - const std::string &get_keyboard() const - { - return m_keyboard; - } - - const std::string &get_version() const - { - return m_version; - } - - bool get_template_value(std::string &s) - { - if (s == "keyboard") - { - s = m_keyboard; - return true; - } - else if (s == "version") - { - s = m_version; - return true; - } - else - { - return false; - } - } - - private: - void send(http_manager::websocket_connection_ptr connection, const std::string &s) - { - connection->send_message(s, websocket_opcode::binary); - } - - void send_contents(http_manager::websocket_connection_ptr connection) - { - if (m_content_provider) - { - m_to_send.str(""); - m_to_send.put('D'); - if (m_content_provider(m_to_send)) - { - send(connection, m_to_send.str()); - } - } - } - - void send_analog_values(http_manager::websocket_connection_ptr connection) - { - // TODO(cbrunschen): get the current analog values and send them - } - - void send_button_states(http_manager::websocket_connection_ptr connection) - { - // TODO(cbrunschen): track current button states and send them - } - - http_manager *m_server; - std::recursive_mutex m_mutex; - - connection_to_panel_map m_panels; - std::list m_commands; - std::thread m_working_thread; - std::ostringstream m_to_send; - - std::string m_index; - std::string m_keyboard; - std::string m_version; - std::function m_content_provider; - std::map m_template_values; - }; - -} // namespace esqpanel - //************************************************************************** // MACROS / CONSTANTS //************************************************************************** @@ -413,11 +48,14 @@ DEFINE_DEVICE_TYPE(ESQPANEL2X16_SQ1, esqpanel2x16_sq1_device, "esqpanel216_sq1", esqpanel_device::esqpanel_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock), device_serial_interface(mconfig, *this), - m_light_states(0x3f), // maximum number of lights + m_external_panel(*this, "esq_external_panel"), + m_light_states(), m_write_tx(*this), - m_write_analog(*this) + m_write_analog(*this), + m_read_analog(*this, 0) { std::fill(std::begin(m_xmitring), std::end(m_xmitring), 0); + m_light_states.reserve(0x80); } @@ -427,20 +65,8 @@ esqpanel_device::esqpanel_device(const machine_config &mconfig, device_type type void esqpanel_device::device_start() { - m_external_panel_server = new esqpanel::external_panel_server(machine().manager().http()); - if (machine().manager().http()->is_active()) { - m_external_panel_server->set_keyboard(owner()->shortname()); - m_external_panel_server->set_index("/esqpanel/FrontPanel.html"); - m_external_panel_server->add_http_template("/esqpanel/FrontPanel.html", get_front_panel_html_file()); - m_external_panel_server->add_http_document("/esqpanel/FrontPanel.js", get_front_panel_js_file()); - m_external_panel_server->set_content_provider([this](std::ostream& o) - { - return write_contents(o); - }); - - m_external_timer = timer_alloc(FUNC(esqpanel_device::check_external_panel_server), this); - m_external_timer->enable(false); - } + m_external_panel_timer = timer_alloc(FUNC(esqpanel_device::check_external_panel_server), this); + m_external_panel_timer->enable(false); } @@ -450,6 +76,8 @@ void esqpanel_device::device_start() void esqpanel_device::device_reset() { + device_t::device_reset(); + // panel comms is at 62500 baud (double the MIDI rate), 8N2 set_data_frame(1, 8, PARITY_NONE, STOP_BITS_2); set_rcv_rate(62500); @@ -457,18 +85,28 @@ void esqpanel_device::device_reset() m_tx_busy = false; m_xmit_read = m_xmit_write = 0; - m_bCalibSecondByte = false; - m_bButtonLightSecondByte = false; + m_expect_calibration_second_byte = false; + m_expect_light_second_byte = false; +} + +//------------------------------------------------- +// device_reset_after_children - device-specific reset after children +//------------------------------------------------- + +void esqpanel_device::device_reset_after_children() +{ + device_t::device_reset_after_children(); attotime sample_time(0, ATTOSECONDS_PER_MILLISECOND); attotime initial_delay(0, ATTOSECONDS_PER_MILLISECOND); - if (m_external_timer) { - m_external_timer->adjust(initial_delay, 0, sample_time); - m_external_timer->enable(true); + if (m_external_panel && m_external_panel->is_running()) { + m_external_panel_timer->adjust(initial_delay, 0, sample_time); + m_external_panel_timer->enable(true); } } + //------------------------------------------------- // device_stop - device-specific stop //------------------------------------------------- @@ -476,9 +114,7 @@ void esqpanel_device::device_reset() void esqpanel_device::device_stop() { device_t::device_stop(); - - delete m_external_panel_server; - m_external_panel_server = nullptr; + m_external_panel_timer->enable(false); } void esqpanel_device::rcv_complete() // Rx completed receiving byte @@ -486,22 +122,21 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte receive_register_extract(); uint8_t data = get_received_char(); -// if (data >= 0xe0) printf("Got %02x from motherboard (second %s)\n", data, m_bCalibSecondByte ? "yes" : "no"); +// if (data >= 0xe0) LOG("Got %02x from motherboard (second %s)\n", data, m_expect_calibration_second_byte ? "yes" : "no"); send_to_display(data); - m_external_panel_server->send_to_all(data); - if (m_bCalibSecondByte) + if (m_expect_calibration_second_byte) { -// printf("second byte is %02x\n", data); +// LOG("second byte is %02x\n", data); if (data == 0xfd) // calibration request { -// printf("let's send reply!\n"); +// LOG("let's send reply!\n"); xmit_char(0xff); // this is the correct response for "calibration OK" } - m_bCalibSecondByte = false; + m_expect_calibration_second_byte = false; } - else if (m_bButtonLightSecondByte) + else if (m_expect_light_second_byte) { // Lights on the Buttons, on the VFX-SD: // Number Button @@ -521,25 +156,22 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte // d Sounds // e 0 // f Cart - int lightNumber = data & 0x3f; + int light_number = data & 0x3f; // Light states: // 0 = Off // 2 = On // 3 = Blinking - m_light_states[lightNumber] = (data & 0xc0) >> 6; - - // TODO: do something with the button information! - // printf("Setting light %d to %s\n", lightNumber, lightState == 3 ? "Blink" : lightState == 2 ? "On" : "Off"); - m_bButtonLightSecondByte = false; + m_light_states[light_number] = (data & 0xc0) >> 6; + m_expect_light_second_byte = false; } else if (data == 0xfb) // request calibration { - m_bCalibSecondByte = true; + m_expect_calibration_second_byte = true; } else if (data == 0xff) // button light state command { - m_bButtonLightSecondByte = true; + m_expect_light_second_byte = true; } else { @@ -565,7 +197,7 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte void esqpanel_device::tra_complete() // Tx completed sending byte { -// printf("panel Tx complete\n"); +// LOG("panel Tx complete\n"); // is there more waiting to send? if (m_xmit_read != m_xmit_write) { @@ -588,7 +220,7 @@ void esqpanel_device::tra_callback() // Tx send bit void esqpanel_device::xmit_char(uint8_t data) { -// printf("Panel: xmit %02x\n", data); +// LOG("Panel: xmit %02x\n", data); // if tx is busy it'll pick this up automatically when it completes if (!m_tx_busy) @@ -607,42 +239,62 @@ void esqpanel_device::xmit_char(uint8_t data) } } -TIMER_CALLBACK_MEMBER(esqpanel_device::check_external_panel_server) { - while (m_external_panel_server->has_commands()) +void esqpanel_device::set_analog_value(offs_t offset, uint16_t value) +{ + m_write_analog(offset, value); +} + +uint16_t esqpanel_device::get_analog_value(offs_t offset) +{ + return m_read_analog(offset, 0); +} + +void esqpanel_device::set_button(uint8_t button, bool pressed) +{ + LOG("set_button(%d, %d)\r\n", button, pressed); + bool current = m_pressed_buttons.find(button) != m_pressed_buttons.end(); + if (pressed == current) { - std::string command = m_external_panel_server->get_next_command(); - int l = command.length(); - if (l > 0) { - std::istringstream is(command); - char c; - is >> c; - if (c == 'B') { - // button - char ud; - is >> ud; - int button; - is >> button; - bool down = ud == 'D'; - uint8_t sendme = (down ? 0x80 : 0) | (button & 0xff); - // printf("button %d %s : sending char to mainboard: %02x\n", button, down ? "down" : "up", sendme); - xmit_char(sendme); - xmit_char(0x00); - } else if (c == 'A') { - // analog value from ES5505 OTIS: 10 bits, left-aligned within 16 bits. - int channel, value; - is >> channel; - is >> value; - uint16_t analog_value = (value << 6); - // printf("analog: channel %d, value %d = %04x\n", channel, value, analog_value); - set_analog_value(channel, analog_value); - } - } + LOG("- button %d already %d, skipping\r\n", button, pressed); + return; + } + + uint8_t sendme = (pressed ? 0x80 : 0) | (button & 0xff); + // LOG("button %d %s : sending char to mainboard: %02x\n", button, pressed ? "down" : "up", sendme); + xmit_char(sendme); + xmit_char(0x00); + if (pressed) + { + m_pressed_buttons.insert(button); + } + else + { + m_pressed_buttons.erase(button); } } -void esqpanel_device::set_analog_value(offs_t offset, uint16_t value) -{ - m_write_analog(offset, value); +TIMER_CALLBACK_MEMBER(esqpanel_device::check_external_panel_server) { + esq_external_panel_device::Command c; + while (m_external_panel->get_next_command(c)) + { + switch (c.kind) { + case esq_external_panel_device::Command::ButtonKind: + LOG("Panel Handling button event from external panel\r\n"); + set_button(c.button, c.pressed); + break; + + case esq_external_panel_device::Command::AnalogValueKind: + set_analog_value(c.channel, c.value); + break; + + case esq_external_panel_device::Command::SendKind: + send_display_contents(); + send_button_states(); + send_light_states(); + send_analog_values(); + break; + } + } } /* panel with 1x22 VFD display used in the EPS-16 and EPS-16 Plus */ @@ -667,7 +319,6 @@ void esqpanel2x40_device::device_add_mconfig(machine_config &config) ESQ2X40(config, m_vfd, 60); } - esqpanel2x40_device::esqpanel2x40_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : esqpanel_device(mconfig, ESQPANEL2X40, tag, owner, clock), m_vfd(*this, "vfd") @@ -677,30 +328,394 @@ esqpanel2x40_device::esqpanel2x40_device(const machine_config &mconfig, const ch /* panel with 2x40 VFD display used in the VFX, VFX-SD, SD-1 series */ +esqpanel2x40_vfx_device::esqpanel2x40_vfx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + esqpanel_device(mconfig, ESQPANEL2X40_VFX, tag, owner, clock), + m_vfd(*this, "vfd"), + m_lights(*this, "lights"), + m_variant(*this, "variant"), // an output on the panel + m_buttons_0(*this, "buttons_0"), + m_buttons_32(*this, "buttons_32"), + m_analog_data_entry(*this, "analog_data_entry"), + m_analog_volume(*this, "analog_volume") +{ + m_eps_mode = false; +} + void esqpanel2x40_vfx_device::device_add_mconfig(machine_config &config) { - ESQ2X40(config, m_vfd, 60); + ESQ2X40_VFX(config, m_vfd, 60); + ESQ_EXTERNAL_PANEL(config, m_external_panel, 0); + + const std::string &name = owner()->shortname(); + if (name == "vfx") + config.set_default_layout(layout_vfx); + else if (name == "vfxsd") + config.set_default_layout(layout_vfxsd); + else // "sd1" or "sd132" + config.set_default_layout(layout_sd1); } -esqpanel2x40_vfx_device::esqpanel2x40_vfx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - esqpanel_device(mconfig, ESQPANEL2X40_VFX, tag, owner, clock), - m_vfd(*this, "vfd") +void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte { - m_eps_mode = false; + receive_register_extract(); + uint8_t data = get_received_char(); + +// if (data >= 0xe0) LOG("Got %02x from motherboard (second %s)\n", data, m_expect_calibration_second_byte ? "yes" : "no"); + + if (m_expect_calibration_second_byte) + { +// LOG("second byte is %02x\n", data); + if (data == 0xfd) // calibration request + { +// LOG("let's send reply!\n"); + xmit_char(0xff); // this is the correct response for "calibration OK" + } + m_expect_calibration_second_byte = false; + } + else if (m_expect_light_second_byte) + { + // Lights on the Buttons, on the VFX-SD: + // Number Button + // 0 1-6 + // 1 8 + // 2 6 + // 3 4 + // 4 2 + // 5 Compare + // 6 1 + // 7 Presets + // 8 7-12 + // 9 9 + // a 7 + // b 5 + // c 3 + // d Sounds + // e 0 + // f Cart + int light_number = data & 0x3f; + + // Light states: + // 0 = Off + // 2 = On + // 3 = Blinking + auto light_state = (data & 0xc0) >> 6; + m_light_states[light_number] = light_state; + + // update the internal panel + update_lights(); + // and any external panel + m_external_panel->set_light(light_number, light_state); + + m_expect_light_second_byte = false; + } + else if (data == 0xfb) // request calibration + { + m_expect_calibration_second_byte = true; + } + else if (data == 0xff) // button light state command + { + m_expect_light_second_byte = true; + } + else + { + if ((data >= 0x80) && (data < 0xd0)) + { + m_cursy = ((data & 0x7f) >= 40) ? 1 : 0; + m_cursx = (data & 0x7f) % 40; + m_curattr = m_vfd->get_attr(m_cursy, m_cursx); + } + else if (data >= 0xd0) + { + switch (data) + { + case 0xd0: // blink start + m_curattr |= AT_BLINK; + break; + + case 0xd1: // cancel all attribs on VFX+ + m_curattr = 0; + break; + + case 0xd2: // blinking underline on VFX + m_curattr |= AT_BLINK | AT_UNDERLINE; + break; + + case 0xd3: // start underline + m_curattr |= AT_UNDERLINE; + break; + + case 0xd6: // clear screen + m_vfd->clear(); + m_external_panel->clear_display(); + m_cursx = m_cursy = 0; + m_curattr = 0; + break; + + case 0xf5: // save cursor position + m_savedx = m_cursx; + m_savedy = m_cursy; + break; + + case 0xf6: // restore cursor position + m_cursx = m_savedx; + m_cursy = m_savedy; + m_curattr = m_vfd->get_attr(m_cursy, m_cursx); + break; + + case 0xfd: // also clear screen? + m_vfd->clear(); + m_external_panel->clear_display(); + m_cursx = m_cursy = 0; + m_curattr = 0; + break; + + default: + // LOG("Unknown control code %02x\n", data); + break; + } + } + else + { + if ((data >= 0x20) && (data <= 0x5f)) + { + m_vfd->set_char(m_cursy, m_cursx, data - ' ', m_curattr); + m_external_panel->set_char(m_cursy, m_cursx, data - ' ', m_curattr); + + m_cursx++; + + if (m_cursx >= 39) + { + m_cursx = 39; + } + } + } + } +} + +void esqpanel2x40_vfx_device::update_lights() { + // set the lights according to their status and bllink phase. + int32_t lights = 0; + int32_t bit = 1; + for (int i = 0; i < 16; i++) + { + if (m_light_states[i] == 2 || (m_light_states[i] == 3 && ((m_blink_phase & 1) == 0))) + { + lights |= bit; + } + bit <<= 1; + } + m_lights = lights; } -bool esqpanel2x40_vfx_device::write_contents(std::ostream &o) +TIMER_CALLBACK_MEMBER(esqpanel2x40_vfx_device::update_blink) { + m_blink_phase = (m_blink_phase + 1) & 3; + m_vfd->set_blink_on(m_blink_phase & 2); + m_external_panel->set_blink_phase(m_blink_phase); + update_lights(); +} + +void esqpanel2x40_vfx_device::device_start() { - m_vfd->write_contents(o); - for (int i = 0; i < m_light_states.size(); i++) + esqpanel_device::device_start(); + + m_variant.resolve(); + m_lights.resolve(); + + m_blink_timer = timer_alloc(FUNC(esqpanel2x40_vfx_device::update_blink), this); + m_blink_timer->enable(false); +} + +void esqpanel2x40_vfx_device::device_reset() +{ + esqpanel_device::device_reset(); + + const std::string &shortname = owner()->shortname(); + bool has_seq = shortname.find("sd") != std::string::npos; + bool has_bank_set = shortname.find("1") != std::string::npos; + bool has_32_voices = shortname.find("32") != std::string::npos; + + u32 bit_seq = 1 << 0; + u32 bit_bank_set = 1 << 1; + u32 bit_32_voices = 1 << 2; + + m_variant = + (has_seq ? bit_seq : 0) + | (has_bank_set ? bit_bank_set : 0) + | (has_32_voices ? bit_32_voices : 0); + + m_external_panel->set_keyboard(shortname); + m_external_panel->set_version("1"); + + if (m_blink_timer) { + attotime sample_time(0, 250 * ATTOSECONDS_PER_MILLISECOND); + attotime initial_delay(0, 250 * ATTOSECONDS_PER_MILLISECOND); + + m_blink_timer->adjust(initial_delay, 0, sample_time); + m_blink_timer->enable(true); + } + + +} + +void esqpanel2x40_vfx_device::device_reset_after_children() +{ + esqpanel_device::device_reset_after_children(); +} + +void esqpanel2x40_vfx_device::send_display_contents() +{ + std::vector> contents; + contents.reserve(80); + for (int row = 0; row < 2; row++) + { + for (int column = 0; column < 40; column++) + { + contents.emplace_back(std::make_pair(m_vfd->get_char(row, column), m_vfd->get_attr(row, column))); + } + } + m_external_panel->set_display_contents(contents); +} + +void esqpanel2x40_vfx_device::send_analog_values() +{ + std::vector> values; + values.reserve(8); + for (offs_t offset = 0; offset < 8; offset++) { - o.put(char(0xff)); - o.put((m_light_states[i] << 6) | i); + values.emplace_back(std::make_pair(offset, get_analog_value(offset))); } - return true; + m_external_panel->set_analog_values(values); } +void esqpanel2x40_vfx_device::send_button_states() +{ + std::vector> button_states; + button_states.reserve(64); + for (int i = 0; i < 64; i++) + { + button_states.emplace_back(std::make_pair(i, m_pressed_buttons.find(i) != m_pressed_buttons.end())); + } + m_external_panel->set_button_states(button_states); +} +void esqpanel2x40_vfx_device::send_light_states() +{ + std::vector> light_states; + light_states.reserve(16); + for (int i = 0; i < 16; i++) + { + light_states.emplace_back(std::make_pair(i, m_light_states[i])); + } + m_external_panel->set_light_states(light_states); +} + +static INPUT_PORTS_START(esqpanel2x40_vfx_device) + PORT_START("buttons_0") + for (int i = 0; i < 32; i++) + { + PORT_BIT((1 << i), IP_ACTIVE_HIGH, IPT_KEYBOARD); + PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esqpanel2x40_vfx_device::button_change), i) + } + + PORT_START("buttons_32") + for (int i = 0; i < 32; i++) + { + PORT_BIT((1 << i), IP_ACTIVE_HIGH, IPT_KEYBOARD); + PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esqpanel2x40_vfx_device::button_change), 32 + i) + } + + PORT_START("analog_data_entry") + PORT_ADJUSTER(255, "Data Entry") + PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esqpanel2x40_vfx_device::analog_value_change), 3) + + PORT_START("analog_volume") + PORT_ADJUSTER(255, "Volume") + PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(esqpanel2x40_vfx_device::analog_value_change), 5) + +INPUT_PORTS_END + +ioport_constructor esqpanel2x40_vfx_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(esqpanel2x40_vfx_device); +} + +// A button is pressed on the internal panel +INPUT_CHANGED_MEMBER(esqpanel2x40_vfx_device::button_change) +{ + // Update the internal state + esqpanel_device::set_button(param, newval != 0); + + // Synchronize the change to the external panel + m_external_panel->set_button(param, newval != 0); +} + +// A button is pressed on the external panel +void esqpanel2x40_vfx_device::set_button(uint8_t button, bool pressed) +{ + // Update the internal state + esqpanel_device::set_button(button, pressed); + + // Synchronize the change to the internal panel + auto &port = button < 32 ? m_buttons_0 : m_buttons_32; + auto bit = 1 << (button % 32); + port->field(bit)->set_value(pressed); +} + +// An anlog value was changed on the internal panel +INPUT_CHANGED_MEMBER(esqpanel2x40_vfx_device::analog_value_change) +{ + if (oldval == 255) + { + // This is the initial write from the layout. Skip this. + LOG("skipping initial write from internal panel to channnel %d\r\n", param); + return; + } + + int channel = param; + int clamped = std::max(0, std::min(100, (int)newval)); + int value = (1023 << 6) * clamped / 100; + esqpanel_device::set_analog_value(channel, value); + LOG("sending analog channel %d value %d to external panel\r\n", channel, value); + m_external_panel->set_analog_value(channel, value); +} + +// An anlog value was changed on the external panel +void esqpanel2x40_vfx_device::set_analog_value(offs_t offset, uint16_t value) +{ + // Update the internal state + esqpanel_device::set_analog_value(offset, value); + + int intvalue = 100 * ((int)value) / (1023 << 6); + + // Synchronize the change to the internal panel by writing it to the corresponding io port + if (offset == 3) + { + set_adjuster_value(m_analog_data_entry, intvalue); + LOG("Setting data entry io port to %d\r\n", intvalue); + } + else if (offset == 5) + { + set_adjuster_value(m_analog_volume, intvalue); + LOG("Setting volume io port to %d\r\n", intvalue); + } +} + +ioport_value esqpanel2x40_vfx_device::get_adjuster_value(required_ioport &ioport) +{ + auto field = ioport->fields().first(); + ioport_field::user_settings user_settings; + field->get_user_settings(user_settings); + return user_settings.value; +} + +void esqpanel2x40_vfx_device::set_adjuster_value(required_ioport &ioport, const ioport_value & value) +{ + auto field = ioport->fields().first(); + ioport_field::user_settings user_settings; + field->get_user_settings(user_settings); + user_settings.value = value; + field->set_user_settings(user_settings); +} // --- SQ1 - Parduz -------------------------------------------------------------------------------------------------------------------------- void esqpanel2x16_sq1_device::device_add_mconfig(machine_config &config) diff --git a/src/mame/ensoniq/esqpanel.h b/src/mame/ensoniq/esqpanel.h index 468865739bafb..2f0b085a14107 100644 --- a/src/mame/ensoniq/esqpanel.h +++ b/src/mame/ensoniq/esqpanel.h @@ -7,10 +7,12 @@ #include "esqvfd.h" #include "esqlcd.h" +#include "extpanel.h" #include "diserial.h" #include +#include //************************************************************************** @@ -19,18 +21,19 @@ // ======================> esqpanel_device -namespace esqpanel { - class external_panel_server; -} - class esqpanel_device : public device_t, public device_serial_interface { public: auto write_tx() { return m_write_tx.bind(); } auto write_analog() { return m_write_analog.bind(); } + auto read_analog() { return m_read_analog.bind(); } void xmit_char(uint8_t data); - void set_analog_value(offs_t offset, uint16_t value); + + void set_char(int row, int column, uint8_t c, uint8_t attr); + virtual void set_analog_value(offs_t offset, uint16_t value); + uint16_t get_analog_value(offs_t offset); + virtual void set_button(uint8_t button, bool pressed); protected: // construction/destruction @@ -39,6 +42,7 @@ class esqpanel_device : public device_t, public device_serial_interface // device-level overrides virtual void device_start() override ATTR_COLD; virtual void device_reset() override ATTR_COLD; + virtual void device_reset_after_children() override ATTR_COLD; virtual void device_stop() override ATTR_COLD; // serial overrides @@ -48,31 +52,43 @@ class esqpanel_device : public device_t, public device_serial_interface virtual void send_to_display(uint8_t data) = 0; - TIMER_CALLBACK_MEMBER(check_external_panel_server); + virtual TIMER_CALLBACK_MEMBER(check_external_panel_server); + + virtual void send_display_contents() { } + virtual void send_analog_values() { } + virtual void send_button_states() { } + virtual void send_light_states() { } - virtual const std::string get_front_panel_html_file() const { return ""; } - virtual const std::string get_front_panel_js_file() const { return ""; } - virtual bool write_contents(std::ostream &o) { return false; } + optional_device m_external_panel; + std::set m_pressed_buttons; std::vector m_light_states; bool m_eps_mode = false; - - esqpanel::external_panel_server *m_external_panel_server; + bool m_expect_calibration_second_byte = false; + bool m_expect_light_second_byte = false; + + template + void logerror(Format &&fmt, Params &&... args) const + { + util::stream_format( + std::cerr, + "[%s] %s", + tag(), + util::string_format(std::forward(fmt), std::forward(args)...)); + } private: static const int XMIT_RING_SIZE = 16; - bool m_bCalibSecondByte = false; - bool m_bButtonLightSecondByte = false; - devcb_write_line m_write_tx; devcb_write16 m_write_analog; + devcb_read16 m_read_analog; uint8_t m_xmitring[XMIT_RING_SIZE]; int m_xmit_read, m_xmit_write = 0; bool m_tx_busy = false; - emu_timer *m_external_timer = nullptr; + emu_timer *m_external_panel_timer = nullptr; }; class esqpanel1x22_device : public esqpanel_device { @@ -103,16 +119,56 @@ class esqpanel2x40_vfx_device : public esqpanel_device { public: esqpanel2x40_vfx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + DECLARE_INPUT_CHANGED_MEMBER(button_change); + DECLARE_INPUT_CHANGED_MEMBER(analog_value_change); + protected: virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_reset_after_children() override ATTR_COLD; + virtual ioport_constructor device_input_ports() const override; - virtual void send_to_display(uint8_t data) override { m_vfd->write_char(data); } + virtual void send_to_display(uint8_t data) override { } + virtual void rcv_complete() override; // Rx completed receiving byte - virtual const std::string get_front_panel_html_file() const override { return "/esqpanel/vfx/FrontPanel.html"; } - virtual const std::string get_front_panel_js_file() const override { return "/esqpanel/vfx/FrontPanel.js"; } - virtual bool write_contents(std::ostream &o) override; + virtual void send_display_contents() override; + virtual void send_analog_values() override; + virtual void send_button_states() override; + virtual void send_light_states() override; + + virtual void set_button(uint8_t button, bool pressed) override; + virtual void set_analog_value(offs_t offset, uint16_t value) override; + + required_device m_vfd; + + static constexpr uint8_t AT_NORMAL = 0x00; + static constexpr uint8_t AT_UNDERLINE = 0x01; + static constexpr uint8_t AT_BLINK = 0x02; + + TIMER_CALLBACK_MEMBER(update_blink); + + ioport_value get_adjuster_value(required_ioport &ioport); + void set_adjuster_value(required_ioport &ioport, const ioport_value & value); + +private: + int m_cursx = 0, m_cursy = 0; + int m_savedx = 0, m_savedy = 0; + uint8_t m_curattr = 0; + + emu_timer *m_blink_timer = nullptr; + uint8_t m_blink_phase; + + void update_lights(); + + output_finder<> m_lights; + output_finder<> m_variant; + + required_ioport m_buttons_0; + required_ioport m_buttons_32; + required_ioport m_analog_data_entry; + required_ioport m_analog_volume; - required_device m_vfd; }; class esqpanel2x40_sq1_device : public esqpanel_device { diff --git a/src/mame/ensoniq/esqvfd.cpp b/src/mame/ensoniq/esqvfd.cpp index f8aa3435f56b0..09fdd5cae1d54 100644 --- a/src/mame/ensoniq/esqvfd.cpp +++ b/src/mame/ensoniq/esqvfd.cpp @@ -14,6 +14,7 @@ DEFINE_DEVICE_TYPE(ESQ1X22, esq1x22_device, "esq1x22", "Ensoniq 1x22 VFD") DEFINE_DEVICE_TYPE(ESQ2X40, esq2x40_device, "esq2x40", "Ensoniq 2x40 VFD") DEFINE_DEVICE_TYPE(ESQ2X40_SQ1, esq2x40_sq1_device, "esq2x40_sq1", "Ensoniq 2x40 VFD (SQ-1 variant)") +DEFINE_DEVICE_TYPE(ESQ2X40_VFX, esq2x40_vfx_device, "esq2x40_vfx", "Ensoniq 2x40 VFD (VFX Family variant)") // adapted from bfm_bd1, rearranged to work with ASCII data used by the Ensoniq h/w static const uint16_t font[]= @@ -116,6 +117,110 @@ static const uint16_t font[]= 0x0000, // 0000 0000 0000 0000 (DEL) }; +/** + * Character data as used in the VFX family. Bit 0..13 are ordered to match + * led14seg. Bit 14 activates the dot after a character; bit 15 the underline. + * See layout/esq2by40_vfx.lay . + */ +static const uint16_t font_vfx[] = { + 0x0000, // 0000 0000 0000 0000 SPACE + 0x543f, // 0101 0100 0011 1111 '0.' + 0x0120, // 0000 0001 0010 0000 '"' + 0x4300, // 0100 0011 0000 0000 '1.' + 0x03ed, // 0000 0011 1110 1101 '$' + 0x40db, // 0100 0000 1101 1011 '2.' + 0x0000, // 0000 0000 0000 0000 '&' + 0x0800, // 0000 1000 0000 0000 ''' + 0x40cf, // 0100 0000 1100 1111 '3.' + 0x40e6, // 0100 0000 1110 0110 '4.' + 0x3fc0, // 0011 1111 1100 0000 '*' + 0x03c0, // 0000 0011 1100 0000 '+' + 0x0000, // 0000 0000 0000 0000 ',' + 0x00c0, // 0000 0000 1100 0000 '-' + 0x4000, // 0100 0000 0000 0000 '.' + 0x1400, // 0001 0100 0000 0000 '/' + 0x143f, // 0001 0100 0011 1111 '0' + 0x0300, // 0000 0011 0000 0000 '1' + 0x00db, // 0000 0000 1101 1011 '2' + 0x00cf, // 0000 0000 1100 1111 '3' + 0x00e6, // 0000 0000 1110 0110 '4' + 0x00ed, // 0000 0000 1110 1101 '5' + 0x00fd, // 0000 0000 1111 1101 '6' + 0x0007, // 0000 0000 0000 0111 '7' + 0x00ff, // 0000 0000 1111 1111 '8' + 0x00ef, // 0000 0000 1110 1111 '9' + 0x0000, // 0000 0000 0000 0000 ':' + 0x40fd, // 0100 0000 1111 1101 '6.' + 0x3000, // 0011 0000 0000 0000 '(' + 0x00c8, // 0000 0000 1100 1000 '=' + 0x0c00, // 0000 1100 0000 0000 ')' + 0x0000, // 0000 0000 0000 0000 '?' + 0x025f, // 0000 0010 0101 1111 '@' + 0x00f7, // 0000 0000 1111 0111 'A' + 0x038f, // 0000 0011 1000 1111 'B' + 0x0039, // 0000 0000 0011 1001 'C' + 0x030f, // 0000 0011 0000 1111 'D' + 0x00f9, // 0000 0000 1111 1001 'E' + 0x00f1, // 0000 0000 1111 0001 'F' + 0x00bd, // 0000 0000 1011 1101 'G' + 0x00f6, // 0000 0000 1111 0110 'H' + 0x0309, // 0000 0011 0000 1001 'I' + 0x001e, // 0000 0000 0001 1110 'J' + 0x3070, // 0011 0000 0111 0000 'K' + 0x0038, // 0000 0000 0011 1000 'L' + 0x1836, // 0001 1000 0011 0110 'M' + 0x2836, // 0010 1000 0011 0110 'N' + 0x003f, // 0000 0000 0011 1111 'O' + 0x00f3, // 0000 0000 1111 0011 'P' + 0x203f, // 0010 0000 0011 1111 'Q' + 0x20f3, // 0010 0000 1111 0011 'R' + 0x00ed, // 0000 0000 1110 1101 'S' + 0x0301, // 0000 0011 0000 0001 'T' + 0x003e, // 0000 0000 0011 1110 'U' + 0x1430, // 0001 0100 0011 0000 'V' + 0x2436, // 0010 0100 0011 0110 'W' + 0x3c00, // 0011 1100 0000 0000 'X' + 0x1a00, // 0001 1010 0000 0000 'Y' + 0x1409, // 0001 0100 0000 1001 'Z' + 0x0039, // 0000 0000 0011 1001 '[' + 0x40ff, // 0100 0000 1111 1111 '8.' + 0x000f, // 0000 0000 0000 1111 ']' + 0x2400, // 0010 0100 0000 0000 '^' + 0x0008, // 0000 0000 0000 1000 '_' + 0x0800, // 0000 1000 0000 0000 '`' + 0x00f7, // 0000 0000 1111 0111 'a' + 0x038f, // 0000 0011 1000 1111 'b' + 0x0039, // 0000 0000 0011 1001 'c' + 0x030f, // 0000 0011 0000 1111 'd' + 0x00f9, // 0000 0000 1111 1001 'e' + 0x00f1, // 0000 0000 1111 0001 'f' + 0x00bd, // 0000 0000 1011 1101 'g' + 0x00f6, // 0000 0000 1111 0110 'h' + 0x0309, // 0000 0011 0000 1001 'i' + 0x001e, // 0000 0000 0001 1110 'j' + 0x3070, // 0011 0000 0111 0000 'k' + 0x0038, // 0000 0000 0011 1000 'l' + 0x1836, // 0001 1000 0011 0110 'm' + 0x2836, // 0010 1000 0011 0110 'n' + 0x003f, // 0000 0000 0011 1111 'o' + 0x00f3, // 0000 0000 1111 0011 'p' + 0x203f, // 0010 0000 0011 1111 'q' + 0x20f3, // 0010 0000 1111 0011 'r' + 0x00ed, // 0000 0000 1110 1101 's' + 0x0301, // 0000 0011 0000 0001 't' + 0x003e, // 0000 0000 0011 1110 'u' + 0x1430, // 0001 0100 0011 0000 'v' + 0x2436, // 0010 0100 0011 0110 'w' + 0x3c00, // 0011 1100 0000 0000 'x' + 0x1a00, // 0001 1010 0000 0000 'y' + 0x1409, // 0001 0100 0000 1001 'z' + 0x0039, // 0000 0000 0011 1001 '{' + 0x0300, // 0000 0011 0000 0000 '|' + 0x000f, // 0000 0000 0000 1111 '}' + 0x2400, // 0010 0100 0000 0000 '~' + 0x0000, // 0000 0000 0000 0000 DEL +}; + esqvfd_device::esqvfd_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, dimensions_param &&dimensions) : device_t(mconfig, type, tag, owner, clock), m_vfds(std::move(std::get<0>(dimensions))), @@ -262,44 +367,78 @@ void esq2x40_device::write_char(uint8_t data) update_display(); } -bool esq2x40_device::write_contents(std::ostream &o) +esq2x40_device::esq2x40_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + esqvfd_device(mconfig, ESQ2X40, tag, owner, clock, make_dimensions<2, 40>(*this)) { - o.put((char) 0xd6); // clear screen +} - uint8_t attrs = 0; - for (int row = 0; row < 2; row++) - { - o.put((char) (0x80 + (40 * row))); // move to first column this row +/* + * VFX-family specifics: + * This device leavs the handling of commands to the esqpanel subclass that uses this vfd, + * and instead simply focuses on storing and displaying the characters with their attributes. + * + */ + +esq2x40_vfx_device::esq2x40_vfx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + esqvfd_device(mconfig, ESQ2X40_VFX, tag, owner, clock, make_dimensions<2, 40>(*this)) +{ +} - for (int col = 0; col < 40; col++) +void esq2x40_vfx_device::set_char(uint8_t row, uint8_t column, uint8_t c, uint8_t attr) { + m_chars[row][column] = c; + m_attrs[row][column] = attr; + m_dirty[row][column] = true; + + update_display(); +} + +void esq2x40_vfx_device::clear() { + memset(m_chars, 0, sizeof(m_chars)); + memset(m_attrs, 0, sizeof(m_attrs)); + memset(m_dirty, 1, sizeof(m_dirty)); + + update_display(); +} + +void esq2x40_vfx_device::update_display() +{ + for (int row = 0; row < m_rows; row++) + { + for (int col = 0; col < m_cols; col++) { - if (m_attrs[row][col] != attrs) + if (m_dirty[row][col]) { - attrs = m_attrs[row][col]; + uint32_t segdata = font_vfx[m_chars[row][col]]; + auto attr = m_attrs[row][col]; + auto underline = (attr & AT_UNDERLINE) && (!(attr & AT_BLINK) || m_blink_on); - o.put((char) 0xd1); // all attributes off - - if (attrs & AT_BLINK) - { - o.put((char) 0xd0); // blink on - } + // digits: + m_vfds->set((row * m_cols) + col, segdata | (underline ? 0x8000 : 0)); - if (attrs & AT_UNDERLINE) - { - o.put((char) 0xd3); // underline - } + m_dirty[row][col] = 0; } - - o.put((char) (m_chars[row][col] + ' ')); } } - return true; } - -esq2x40_device::esq2x40_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - esqvfd_device(mconfig, ESQ2X40, tag, owner, clock, make_dimensions<2, 40>(*this)) +void esq2x40_vfx_device::device_add_mconfig(machine_config &config) { + // Do not set a default layout. This display must be used + // within a layout that includes the VFD elements, such as + // vfx.lay, vfxsd.lay or sd1.lay. +} + +void esq2x40_vfx_device::set_blink_on(bool blink_on) { + m_blink_on = blink_on; + + for (int row = 0; row < m_rows; row++) + { + for (int col = 0; col < m_cols; col++) + { + m_dirty[row][col] |= m_attrs[row][col] & AT_BLINK; + } + } + update_display(); } /* 1x22 display from the VFX (not right, but it'll do for now) */ @@ -309,7 +448,6 @@ void esq1x22_device::device_add_mconfig(machine_config &config) config.set_default_layout(layout_esq1by22); } - void esq1x22_device::write_char(uint8_t data) { if (data >= 0x60) diff --git a/src/mame/ensoniq/esqvfd.h b/src/mame/ensoniq/esqvfd.h index bbbe2ea7cc7c7..acb992561b058 100644 --- a/src/mame/ensoniq/esqvfd.h +++ b/src/mame/ensoniq/esqvfd.h @@ -36,15 +36,13 @@ class esqvfd_device : public device_t { typedef std::tuple dimensions_param; - template static dimensions_param make_dimensions(device_t &device) { return dimensions_param(std::make_unique >(device), R, C); } + template static dimensions_param make_dimensions(device_t &device) { return dimensions_param(std::make_unique >(device), R, C); } esqvfd_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, dimensions_param &&dimensions); static constexpr uint8_t AT_NORMAL = 0x00; - static constexpr uint8_t AT_BOLD = 0x01; - static constexpr uint8_t AT_UNDERLINE = 0x02; - static constexpr uint8_t AT_BLINK = 0x04; - static constexpr uint8_t AT_BLINKED = 0x80; // set when character should be blinked off + static constexpr uint8_t AT_UNDERLINE = 0x01; + static constexpr uint8_t AT_BLINK = 0x02; virtual void device_start() override ATTR_COLD; virtual void device_reset() override ATTR_COLD; @@ -77,12 +75,36 @@ class esq2x40_device : public esqvfd_device { esq2x40_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); virtual void write_char(uint8_t data) override; - virtual bool write_contents(std::ostream &o) override; + + template static dimensions_param make_dimensions(device_t &device) { return dimensions_param(std::make_unique >(device), R, C); } + +protected: + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; +}; + +// Include VFX-family-specifics +class esq2x40_vfx_device : public esqvfd_device { +public: + esq2x40_vfx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + virtual void set_char(uint8_t row, uint8_t column, uint8_t c, uint8_t attr); + virtual uint8_t get_attr(uint8_t row, uint8_t column) { return m_attrs[row][column]; } + virtual uint8_t get_char(uint8_t row, uint8_t column) { return m_chars[row][column]; } + virtual void clear(); + virtual void set_blink_on(bool blink_on); + virtual void update_display() override; + + virtual void write_char(uint8_t data) override { /* no-op*/ } protected: + // device-level overrides virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + +private: + bool m_blink_on = false; }; + class esq2x40_sq1_device : public esqvfd_device { public: esq2x40_sq1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); @@ -99,5 +121,6 @@ class esq2x40_sq1_device : public esqvfd_device { DECLARE_DEVICE_TYPE(ESQ1X22, esq1x22_device) DECLARE_DEVICE_TYPE(ESQ2X40, esq2x40_device) DECLARE_DEVICE_TYPE(ESQ2X40_SQ1, esq2x40_sq1_device) +DECLARE_DEVICE_TYPE(ESQ2X40_VFX, esq2x40_vfx_device) #endif // MAME_ENSONIQ_ESQVFD_H diff --git a/src/mame/ensoniq/extpanel.cpp b/src/mame/ensoniq/extpanel.cpp new file mode 100644 index 0000000000000..9006aabeb91fc --- /dev/null +++ b/src/mame/ensoniq/extpanel.cpp @@ -0,0 +1,565 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +// copyright-holders:C. Brunschen +// +// esq_external_panel_device::context is heavily based +// on cps2_comm_device::context by Vas Crabb. + +#include "emu.h" +#include "extpanel.h" +#include "emuopts.h" + +#include "asio.h" + +#include +#include + +#define VERBOSE 0 +#include "logmacro.h" + +class esq_external_panel_device::context { +public: + context( + esq_external_panel_device &device, + std::optional const &endpoint) : + m_device(device), + m_acceptor(m_ioctx), + m_socket(m_ioctx), + m_endpoint(endpoint), + m_stopping(false) + { + } + + std::error_code start() + { + std::error_code err; + + if (m_endpoint) + { + m_acceptor.open(m_endpoint->protocol(), err); + if (!err) + { + asio::ip::tcp::acceptor::reuse_address option(true); + m_acceptor.set_option(option); + } + if (!err) + m_acceptor.bind(*m_endpoint, err); + if (!err) + m_acceptor.listen(1, err); + if (err) + return err; + } + + m_thread = std::thread( + [this] () + { + start_accept(); + m_ioctx.run(); + LOG("Network thread completed\n"); + }); + + return err; + } + + void stop() + { + m_ioctx.post( + [this] () + { + m_stopping = true; + std::error_code err; + if (m_acceptor.is_open()) + m_acceptor.close(err); + if (m_socket.is_open()) + m_socket.close(err); + }); + m_thread.join(); + } + + void send_with_crlf(const std::string &s) + { + m_ioctx.post( + [this, s] () + { + if (m_socket.is_open()) + { + bool const sending = !m_send_buf.empty(); + m_send_buf.append(s.data(), s.length()); + m_send_buf.append("\r\n", 2); + if (!sending) + { + start_send(); + } + } + }); + } + +private: + template + void logerror(Format &&fmt, Params &&... args) const + { + util::stream_format( + std::cerr, + "[%s] %s", + m_device.tag(), + util::string_format(std::forward(fmt), std::forward(args)...)); + } + + void start_accept() + { + LOG("Accepting external panel connection on %s\n", *m_endpoint); + m_acceptor.async_accept( + [this] (std::error_code const &err, asio::ip::tcp::socket sock) + { + if (err) + { + LOG("Error accepting external panel connection: %s\n", err.message()); + if (!m_stopping) + start_accept(); + } + else + { + LOG("Accepted external panel connection from %s\n", sock.remote_endpoint()); + m_socket = std::move(sock); + m_device.client_connected(); + start_receive(); + } + }); + } + + void start_receive() + { + m_socket.async_read_some( + asio::buffer(m_receive_buf), + [this] (std::error_code const &err, std::size_t length) + { + if (err || !length) + { + if (err) + LOG("Error receiving from external connection: %s\n", err.message()); + else + LOG("External panel connection lost\n"); + m_send_buf.clear(); + m_device.client_disconnected(); + if (!m_stopping) + { + std::error_code e; + m_socket.close(e); + start_accept(); + } + } + else + { + m_device.handle_received_data(m_receive_buf, length); + start_receive(); + } + }); + } + + template + class buffer + { + public: + std::size_t append(const char *src, std::size_t len) + { + std::size_t used = 0U; + if (!m_full) + { + if (m_wp >= m_rp) + { + used = std::min(m_buf.size() - m_wp, len); + std::copy_n(&src[0], used, &m_buf[m_wp]); + m_wp = (m_wp + used) % m_buf.size(); + } + std::size_t const block = std::min(len - used, m_rp - m_wp); + if (block) + { + std::copy_n(&src[used], block, &m_buf[m_wp]); + used += block; + m_wp += block; + } + m_full = m_wp == m_rp; + } + return used; + } + + std::size_t consume(std::size_t len) + { + std::size_t const delta = std::min( + m_full ? m_buf.size() : ((m_wp + m_buf.size() - m_rp) % m_buf.size()), + len); + m_rp = (m_rp + delta) % m_buf.size(); + if (delta) + m_full = false; + return delta; + } + + auto content() const + { + return asio::buffer( + &m_buf[m_rp], + ((m_full || (m_wp < m_rp)) ? m_buf.size() : m_wp) - m_rp); + } + + void clear() { m_wp = m_rp = 0U; } + + bool empty() const { return !m_full && (m_wp == m_rp); } + + private: + unsigned m_wp = 0U, m_rp = 0U; + bool m_full = false; + std::array m_buf; + }; + + void start_send() + { + m_socket.async_write_some( + m_send_buf.content(), + [this] (std::error_code const &err, std::size_t length) + { + m_send_buf.consume(length); + if (err) + { + LOG("Error sending to external panel connection: %s\n", err.message().c_str()); + m_device.client_disconnected(); + m_send_buf.clear(); + m_socket.close(); + } + else if (!m_send_buf.empty()) + { + start_send(); + } + }); + } + + esq_external_panel_device &m_device; + std::thread m_thread; + asio::io_context m_ioctx; + asio::ip::tcp::acceptor m_acceptor; + asio::ip::tcp::socket m_socket; + std::optional const m_endpoint; + bool m_stopping; + buffer<4096> m_send_buf; + std::array m_receive_buf; +}; + +#define MT_ANALOG 'A' +#define MT_BUTTON 'B' +#define MT_CONTROL 'C' +#define MT_DISPLAY 'D' +#define MT_INFO 'I' + +DEFINE_DEVICE_TYPE(ESQ_EXTERNAL_PANEL, esq_external_panel_device, "esq_external_panel", "Ensoniq External_Panel") + +esq_external_panel_device::esq_external_panel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, ESQ_EXTERNAL_PANEL, tag, owner, clock) +{ +} + +esq_external_panel_device::~esq_external_panel_device() +{ +} + +void esq_external_panel_device::device_start() +{ +} + +void esq_external_panel_device::device_add_mconfig(machine_config &config) +{ +} + +/** + * This must only be called with the mutex locked, and after checking that m_connected == true. + */ +void esq_external_panel_device::send(const std::string &s) +{ + m_context->send_with_crlf(s); +} + +void esq_external_panel_device::clear_display() +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + send("DX"); +} + +void esq_external_panel_device::set_keyboard(const std::string &keyboard) +{ + std::lock_guard lock(m_mutex); + m_keyboard = keyboard; +} + +void esq_external_panel_device::set_version(const std::string &version) +{ + std::lock_guard lock(m_mutex); + m_version = version; +} + + +void esq_external_panel_device::set_char(uint8_t row, uint8_t column, uint8_t c, uint8_t attr) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + send(util::string_format("DC %d %d %02x %1x", row, column, c, attr)); +} + +void esq_external_panel_device::set_light(uint8_t light, uint8_t state) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + send(util::string_format("L %d %d", light, state)); +} + +void esq_external_panel_device::button_down(uint8_t button) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + send(util::string_format("BD %d", button)); +} + +void esq_external_panel_device::button_up(uint8_t button) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + send(util::string_format("BU %d", button)); +} + +void esq_external_panel_device::set_button(uint8_t button, bool pressed) +{ + if (pressed) + { + button_down(button); + } + else + { + button_up(button); + } +} + +void esq_external_panel_device::set_analog_value(uint8_t channel, uint16_t value) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + send(util::string_format("A %d %d", channel, value >> 6)); +} + +void esq_external_panel_device::set_blink_phase(uint8_t phase) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + send(util::string_format("P %d", phase)); +} + +void esq_external_panel_device::set_display_contents(std::vector> &chars_and_attrs) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + + std::ostringstream os; + os << "DC 0 0"; + for (auto ca : chars_and_attrs) + { + util::stream_format(os, " %02x %1x", ca.first, ca.second); + } + + send(os.str()); +} + +void esq_external_panel_device::set_button_states(std::vector> &button_states) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + + std::ostringstream ups; + ups << "BU"; + std::ostringstream downs; + downs << "BD"; + for (auto bs : button_states) + { + util::stream_format(bs.second ? downs : ups, " %d", bs.first); + } + send(ups.str()); + send(downs.str()); +} + +void esq_external_panel_device::set_light_states(std::vector> &light_states) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + + std::ostringstream os; + os << "L"; + for (auto ls : light_states) + { + util::stream_format(os, " %d %d", ls.first, ls.second); + } + send(os.str()); +} + +void esq_external_panel_device::set_analog_values(std::vector> &analog_values) +{ + std::lock_guard lock(m_mutex); + if (!m_connected) return; + + std::ostringstream os; + os << "A"; + for (auto av : analog_values) + { + util::stream_format(os, " %d %u", av.first, av.second >> 6); + } + send(os.str()); +} + +void esq_external_panel_device::handle_received_data(uint8_t c) { + // LOG("Received %02x, overflow=%d\r\n", c, m_rx_overflow); + if (m_rx_overflow) + { + if (c == '\n') + { + // LOG("Found end of overflowing message, restarting.\r\n"); + m_received = 0; + m_rx_overflow = false; + } + else + { + // LOG("In buffer overflow, ignoring.\r\n"); + } + } + else + { + switch(c) + { + case '\r': // ignore + // LOG("ignoring CR\r\n"); + break; + + case '\n': + // LOG("LF, have a message of %d bytes\r\n", m_received); + // We have a complete message! + handle_received_message(std::string(m_rx_buf, m_rx_buf + m_received)); + // Now wait for the start of the next message. + m_received = 0; + break; + + default: + if (m_received >= RX_BUF_SIZE) + { + // LOG("Already have %d of %d characters - buffer overflow!\r\n", m_received, RX_BUF_SIZE); + m_rx_overflow = true; + } + else + { + m_rx_buf[m_received++] = c; + // LOG("accumulated character %02x, now have %d (of max %d)\r\n", c, m_received, RX_BUF_SIZE); + } + } + } +} + +void esq_external_panel_device::handle_received_message(const std::string &message) +{ + if (message.empty()) return; + + std::istringstream is(message); + char mt; + is >> mt; + + LOG("Handling Message '%s' of type %c\r\n", message.c_str(), mt); + + if (mt == MT_CONTROL) + { + // For now, simply send all of the current state. + enqueue_send(); + } + else if (mt == MT_INFO) + { + send(util::string_format("I %s,%s", m_keyboard, m_version)); + } + else if (mt == MT_BUTTON) + { + char ud; + is >> ud; + int button; + is >> button; + bool pressed = ud == 'D'; + enqueue_button(button, pressed); + } + else if (mt == MT_ANALOG) + { + int channel, value; + is >> channel; + is >> value; + uint16_t analog_value = (value << 6); + enqueue_analog_value(channel, analog_value); + } + else + { + // LOG("Ignoring unknown message '%s'\r\n", message); + } +} + +void esq_external_panel_device::device_reset() +{ + if (!m_context) + { + start_network(); + } +} + +void esq_external_panel_device::device_stop() +{ + if (m_context) + { + m_context->stop(); + m_context.reset(); + } +} + +void esq_external_panel_device::start_network() +{ + auto const &opts = mconfig().options(); + std::error_code err; + std::istringstream parsestr; + parsestr.imbue(std::locale::classic()); + + std::optional endpoint; + if (*opts.comm_localhost()) + { + LOG("trying to start network on '%s'", opts.comm_localhost()); + asio::ip::address const bindaddr = asio::ip::make_address(opts.comm_localhost(), err); + if (err) + { + osd_printf_error("[%s] invalid local IP address %s, disabling communication.\n", tag(), opts.comm_localhost()); + return; + } + + parsestr.str(opts.comm_localport()); + parsestr.seekg(0, std::ios_base::beg); + asio::ip::port_type bindport; + parsestr >> bindport; + if (!parsestr || !bindport) + { + osd_printf_error("[%s] invalid local TCP port %s, disabling communication.\n", tag(), opts.comm_localport()); + return; + } + + endpoint.emplace(bindaddr, bindport); + } + + if (!endpoint) + { + osd_printf_error("[%s] no TCP addresses configured, disabling communication.\n", tag()); + return; + } + + auto ctx = std::make_unique(*this, endpoint); + err = ctx->start(); + if (err) + { + osd_printf_error("[%s] error opening/binding sockets, disabling communication (%s).\n", tag(), err.message()); + m_context.reset(); + return; + } + + m_context = std::move(ctx); +} diff --git a/src/mame/ensoniq/extpanel.h b/src/mame/ensoniq/extpanel.h new file mode 100644 index 0000000000000..91d1cc9eca239 --- /dev/null +++ b/src/mame/ensoniq/extpanel.h @@ -0,0 +1,142 @@ +// license:BSD-3-Clause +// copyright-holders:C. Brunschen +#ifndef MAME_ENSONIQ_EXTPANEL_H +#define MAME_ENSONIQ_EXTPANEL_H + +#pragma once + +#include "emumem.h" + +#include +#include +#include +#include +#include +#include + +class esq_external_panel_device : public device_t +{ +public: + struct Command { + enum Kind { + ButtonKind = 1, + AnalogValueKind = 2, + SendKind = 3 + }; + + enum Kind kind; + + union { + struct { + uint8_t button; + bool pressed; + }; + + struct { + uint8_t channel; + uint16_t value; + }; + }; + + static Command Button(uint8_t b, bool p) { Command c { ButtonKind }; c.button = b; c.pressed = p; return c; } + static Command AnalogValue(uint8_t o, uint16_t v) { Command c { AnalogValueKind }; c.channel = o; c.value = v; return c; } + static Command Send() { Command c { SendKind }; return c; } + }; + + esq_external_panel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + virtual ~esq_external_panel_device(); + + void set_keyboard(const std::string &keyboard); + void set_version(const std::string &version); + + bool is_running() { return m_context.operator bool(); } + + void clear_display(); + void set_char(uint8_t row, uint8_t column, uint8_t c, uint8_t attr); + void set_light(uint8_t light, uint8_t state); + void button_down(uint8_t button); + void button_up(uint8_t button); + void set_button(uint8_t button, bool pressed); + void set_analog_value(uint8_t channel, uint16_t value); + void set_blink_phase(uint8_t phase); + + bool get_next_command(Command &c) { + std::lock_guard lock(m_mutex); + if (m_commands.empty()) + return false; + + c = std::move(m_commands.front()); + m_commands.pop_front(); + return true; + } + + // bulk setters + void set_display_contents(std::vector> &chars_and_attrs); + void set_button_states(std::vector> &button_states); + void set_light_states(std::vector> &light_states); + void set_analog_values(std::vector> &analog_values); + + static constexpr size_t RX_BUF_SIZE = 64; + static constexpr uint8_t RX_AWAIT_STX = 0; + static constexpr uint8_t RX_COLLECT = 1; + static constexpr uint8_t RX_COLLECT_ESCAPED = 2; + + class context; + friend class context; + +protected: + // device_t implementation + virtual void device_start() override ATTR_COLD; + virtual void device_stop() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + void send(const std::string &s); + void enqueue_command(Command &&command) + { + std::lock_guard lock(m_mutex); + m_commands.emplace_back(std::move(command)); + } + void enqueue_button(uint8_t button, bool pressed) { enqueue_command(Command::Button(button, pressed)); } + void enqueue_analog_value(uint8_t channel, uint16_t value) { enqueue_command(Command::AnalogValue(channel, value)); } + void enqueue_send() { enqueue_command(Command::Send()); } + + void client_connected() { std::lock_guard lock(m_mutex); m_connected = true; } + void client_disconnected() { std::lock_guard lock(m_mutex); m_connected = false; } + + template + void handle_received_data(const A& a, size_t length) { for (size_t i = 0; i < length; i++) handle_received_data(a[i]); } + void handle_received_data(uint8_t c); + void handle_received_message(const std::string &message); + +private: + void start_network(); + + template + void logerror(Format &&fmt, Params &&... args) const + { + util::stream_format( + std::cerr, + "[%s] %s", + tag(), + util::string_format(std::forward(fmt), std::forward(args)...)); + } + + std::recursive_mutex m_mutex; + + std::string m_keyboard = "unknown"; + std::string m_version = "0"; + + std::list m_commands; + + uint8_t m_rx_buf[RX_BUF_SIZE]; + size_t m_received = 0; + bool m_rx_overflow = false; + std::unique_ptr m_context; + bool m_connected = false; +}; + +// device type definition +DECLARE_DEVICE_TYPE(ESQ_EXTERNAL_PANEL, esq_external_panel_device) + +#endif // MAME_ENSONIQ_EXTPANEL_H diff --git a/src/mame/layout/sd1.lay b/src/mame/layout/sd1.lay new file mode 100644 index 0000000000000..b3d57588a9782 --- /dev/null +++ b/src/mame/layout/sd1.lay @@ -0,0 +1,1196 @@ + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/layout/vfx.lay b/src/mame/layout/vfx.lay new file mode 100644 index 0000000000000..300042fb9e7b3 --- /dev/null +++ b/src/mame/layout/vfx.lay @@ -0,0 +1,1063 @@ + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/layout/vfxsd.lay b/src/mame/layout/vfxsd.lay new file mode 100644 index 0000000000000..46446169f0baa --- /dev/null +++ b/src/mame/layout/vfxsd.lay @@ -0,0 +1,1196 @@ + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + ]]> + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/esqpanel/vfx/FrontPanel.html b/web/esqpanel/vfx/FrontPanel.html deleted file mode 100644 index bdf99c781676b..0000000000000 --- a/web/esqpanel/vfx/FrontPanel.html +++ /dev/null @@ -1,25 +0,0 @@ - - - Front Panel - - - - - - - - - \ No newline at end of file diff --git a/web/esqpanel/vfx/FrontPanel.js b/web/esqpanel/vfx/FrontPanel.js deleted file mode 100644 index 69e1b67ac8f5e..0000000000000 --- a/web/esqpanel/vfx/FrontPanel.js +++ /dev/null @@ -1,1209 +0,0 @@ -var fp = (function() { - var my = {}; - - if ('indexOf' in Array.prototype) { - my.indexOf = function(array, item) { - return array.indexOf(item); - } - } else { - my.indexOf = function(array, item) { - for (var i = 0; i < array.length; i++) { - if (array[i] == item) { - return i; - } - } - return -1; - } - } - - my.Shade = { - LIGHT: "#bbbbbb", - MEDIUM: "#777777", - DARK: "#333333" - }; - - my.LabelPosition = { - ABOVE: 1, - ABOVE_CENTERED: 2, - BELOW: 3 - }; - - my.Light = { - OFF: 0, - ON: 1, - BLINK: 2 - }; - - my.DisplayBlinkState = { - OFF: 0, - UNDERLINE: 1, - CHAR: 2 - }; - - my.Keyboard = { - VFX: 'VFX', - VFX_SD: 'VFX-SD', - SD1: 'SD-1', - SD1_32: 'SD-1/32' - } - - my.segmentPaths = [ - "M1053 705 c-43 19 -57 47 -43 89 23 70 87 106 189 106 38 0 70 8 106 25 79 39 111 41 183 11 80 -34 119 -33 205 6 68 31 78 33 192 33 116 0 123 -1 195 -35 67 -31 87 -35 182 -40 101 -5 108 -7 137 -34 40 -38 50 -89 25 -118 -11 -11 -37 -29 -59 -39 -37 -17 -79 -19 -660 -18 -505 0 -626 2 -652 14z", - "M2519 963 c-20 13 -46 47 -63 81 -28 53 -31 69 -37 199 -7 155 -20 211 -75 319 -50 99 -68 199 -54 301 23 167 52 217 126 217 37 0 47 -5 77 -40 53 -63 74 -151 97 -410 5 -63 16 -167 24 -230 42 -326 45 -374 21 -419 -24 -47 -63 -54 -116 -18z", - "M2144 1089 c-59 43 -88 78 -135 161 -23 41 -75 112 -115 156 -108 119 -132 188 -136 386 -3 107 -1 118 17 132 11 9 28 16 37 16 25 0 92 -63 154 -145 29 -39 100 -129 158 -200 58 -72 113 -144 121 -162 19 -40 32 -106 41 -214 5 -68 3 -91 -9 -116 -28 -52 -74 -57 -133 -14z", - "M1515 1089 c-70 43 -69 41 -77 285 -3 121 -11 259 -18 306 -6 47 -13 142 -17 211 -6 141 5 183 54 195 78 20 124 -53 135 -216 13 -192 26 -274 61 -385 77 -245 76 -359 -3 -400 -39 -20 -99 -19 -135 4z", - "M1108 1087 c-32 36 -42 71 -50 163 -5 52 -11 122 -14 156 -6 55 -1 75 41 200 53 152 59 165 87 183 16 10 24 9 44 -4 31 -20 43 -51 55 -135 5 -36 17 -97 26 -137 14 -63 15 -81 3 -145 -37 -205 -43 -222 -88 -271 -30 -32 -80 -37 -104 -10z", - "M797 938 c-32 36 -44 102 -67 377 -19 222 -30 337 -42 428 -17 138 12 277 67 313 55 36 123 -6 173 -109 52 -106 54 -167 12 -292 -27 -78 -30 -102 -30 -205 0 -79 7 -147 20 -210 11 -51 20 -111 20 -133 0 -123 -97 -231 -153 -169z", - "M1940 2120 c-14 4 -56 8 -94 9 -80 1 -141 26 -181 73 -32 38 -32 78 1 118 48 56 84 67 249 74 146 7 151 7 195 -17 52 -27 99 -89 100 -130 0 -33 -31 -81 -63 -98 -27 -15 -125 -38 -157 -38 -14 1 -36 5 -50 9z", - "M1099 2129 c-51 10 -110 43 -132 73 -28 37 -16 88 32 138 36 38 41 40 95 40 64 0 115 -22 159 -68 32 -34 46 -97 28 -130 -23 -43 -109 -68 -182 -53z", - "M2279 2467 c-56 50 -69 80 -80 186 -6 51 -16 127 -24 169 -14 83 -10 123 25 213 36 95 44 146 31 203 -14 66 -14 205 -1 254 12 41 70 98 100 98 52 0 75 -100 100 -435 6 -77 22 -241 36 -364 28 -255 27 -268 -37 -325 -54 -49 -94 -49 -150 1z", - "M1701 2579 c-24 24 -40 122 -44 261 -2 95 1 112 27 178 15 41 44 94 63 119 19 25 57 92 84 149 58 121 94 164 137 164 38 0 78 -32 90 -73 19 -60 22 -181 7 -238 -20 -77 -116 -277 -180 -376 -30 -46 -66 -106 -80 -133 -35 -69 -69 -86 -104 -51z", - "M1372 2456 c-40 28 -52 66 -52 166 0 92 -27 323 -55 468 -21 108 -19 246 3 290 21 44 59 65 96 56 47 -12 123 -92 146 -152 28 -77 28 -203 -1 -281 -21 -55 -22 -62 -10 -218 6 -88 14 -178 16 -201 6 -54 -11 -110 -38 -129 -28 -19 -76 -19 -105 1z", - "M1067 2721 c-19 11 -122 161 -156 228 -42 81 -51 129 -51 276 0 113 3 147 18 175 39 80 102 35 199 -141 28 -52 56 -112 62 -134 6 -22 11 -114 11 -205 0 -134 -3 -170 -16 -188 -16 -24 -39 -27 -67 -11z", - "M695 2447 c-45 23 -76 54 -91 90 -8 18 -18 101 -24 190 -18 298 -21 328 -52 516 -26 164 -29 194 -18 235 23 91 68 107 130 44 46 -45 59 -86 71 -217 5 -55 13 -143 18 -195 11 -120 37 -199 101 -302 48 -78 50 -85 50 -153 0 -95 -15 -143 -60 -187 -42 -42 -75 -48 -125 -21z", - "M1550 3539 c-14 5 -57 24 -97 44 -107 54 -134 56 -218 12 -79 -42 -105 -41 -170 3 -35 23 -53 28 -145 33 -131 8 -181 24 -194 62 -14 39 9 78 54 94 49 17 1278 18 1315 1 51 -23 42 -87 -18 -132 -21 -15 -48 -21 -115 -26 -77 -4 -94 -9 -140 -38 -85 -55 -195 -76 -272 -53z", - "M2619 3393 c-19 12 -45 43 -59 67 -36 65 -36 183 0 255 48 93 136 107 207 33 60 -61 76 -152 48 -257 -17 -63 -45 -97 -94 -111 -52 -14 -64 -13 -102 13z", - "M512 4422 c-38 8 -46 15 -63 51 -37 83 -18 153 51 181 36 14 127 16 863 16 642 0 827 -3 847 -13 16 -8 31 -31 44 -64 16 -46 17 -57 5 -94 -8 -24 -26 -51 -42 -63 -28 -21 -34 -21 -845 -23 -501 0 -834 3 -860 9z", - ]; - my.charWidth = 342; - my.charHeight = 572; - my.segmentScale = 0.1; - - my.createElement = function(tag) { - return document.createElementNS("http://www.w3.org/2000/svg", tag); - } - - my.showElement = function(e) { - e.removeAttribute("display"); - } - - my.hideElement = function(e) { - e.setAttribute("display", "none"); - } - - my.svg = function() { - if (my._svg == null) { - my._svg = document.getElementsByTagName('svg')[0]; - } - return my._svg; - } - - my.pt = function() { - if (my._pt == null) { - my._pt = my.svg().createSVGPoint(); - } - return my._pt; - } - - my.pointIn = function(el, x, y) { - var pt = my.pt(); - pt.x = x; pt.y = y; - return pt.matrixTransform(el.getScreenCTM().inverse()); - } - - my.Display = function(parent, rows, cols) { - this.cells = new Array(); - this.width = my.charWidth * cols; - this.height = my.charHeight * rows; - this.blinkPhase = true; - - var templateCell = my.createElement("g"); - templateCell.setAttribute('transform', 'scale(' + my.segmentScale + ',' + my.segmentScale + ')'); - for (var i = 0; i < my.segmentPaths.length; i++) { - var segmentPath = my.createElement("path"); - segmentPath.setAttribute('d', my.segmentPaths[i]); - templateCell.appendChild(segmentPath); - } - - for (var row = 0; row < 2; row++) { - this.cells[row] = new Array(); - for (var col = 0; col < 40; col++) { - this.cells[row][col] = { - char: ' ', - blink: false, - underline: false, - segments: new Array(), - }; - var charCell = templateCell.cloneNode(true); - var ctm = "translate(" + col * my.charWidth + ", " + row * my.charHeight + ") " + charCell.getAttribute("transform"); - charCell.setAttribute("transform", ctm); - parent.appendChild(charCell); - - var segs = charCell.getElementsByTagName("path"); - for (var cc = 0; cc < segs.length; cc++) { - this.cells[row][col].segments[cc] = segs[cc]; - } - } - } - - parent.setAttribute("viewBox", "0 0 " + this.width + " " + this.height); - } - - my.Display.segmentsByCharacter = [ - 0x0000, // 0000 0000 0000 0000 SPACE - 0x7927, // 0011 1001 0010 0111 '0.' - 0x0028, // 0000 0000 0010 1000 '"' - 0x4408, // 0000 0100 0000 1000 '1.' - 0x25e9, // 0010 0101 1110 1001 '$' - 0x70c3, // 0011 0000 1100 0011 '2.' - 0x0000, // 0000 0000 0000 0000 '&' - 0x0010, // 0000 0000 0001 0000 ''' - 0x61c3, // 0010 0001 1100 0011 '3.' - 0x41e2, // 0000 0001 1110 0010 '4.' - 0x0edc, // 0000 1110 1101 1100 '*' - 0x04c8, // 0000 0100 1100 1000 '+' - 0x0000, // 0000 0000 0000 0000 ',' - 0x00c0, // 0000 0000 1100 0000 '-' - 0x4000, // 0100 0000 0000 0000 '.' - 0x0804, // 0000 1000 0000 0100 '/' - 0x3927, // 0011 1001 0010 0111 '0' - 0x0408, // 0000 0100 0000 1000 '1' - 0x30c3, // 0011 0000 1100 0011 '2' - 0x21c3, // 0010 0001 1100 0011 '3' - 0x01e2, // 0000 0001 1110 0010 '4' - 0x21e1, // 0010 0001 1110 0001 '5' - 0x31e1, // 0011 0001 1110 0001 '6' - 0x0103, // 0000 0001 0000 0011 '7' - 0x31e3, // 0011 0001 1110 0011 '8' - 0x21e3, // 0010 0001 1110 0011 '9' - 0x0000, // 0000 0000 0000 0000 ':' - 0x71e1, // 0011 0001 1110 0001 '6.' - 0x0204, // 0000 0010 0000 0100 '(' - 0x20c0, // 0010 0000 1100 0000 '=' - 0x0810, // 0000 1000 0001 0000 ')' - 0x0000, // 0000 0000 0000 0000 '?' - 0x3583, // 0011 0101 1000 0011 '@' - 0x11e3, // 0001 0001 1110 0011 'A' - 0x254b, // 0010 0101 0100 1011 'B' - 0x3021, // 0011 0000 0010 0001 'C' - 0x250b, // 0010 0101 0000 1011 'D' - 0x30e1, // 0011 0000 1110 0001 'E' - 0x10e1, // 0001 0000 1110 0001 'F' - 0x3161, // 0011 0001 0110 0001 'G' - 0x11e2, // 0001 0001 1110 0010 'H' - 0x2409, // 0010 0100 0000 1001 'I' - 0x3102, // 0011 0001 0000 0010 'J' - 0x12a4, // 0001 0010 1010 0100 'K' - 0x3020, // 0011 0000 0010 0000 'L' - 0x1136, // 0001 0001 0011 0110 'M' - 0x1332, // 0001 0011 0011 0010 'N' - 0x3123, // 0011 0001 0010 0011 'O' - 0x10e3, // 0001 0000 1110 0011 'P' - 0x3323, // 0011 0011 0010 0011 'Q' - 0x12e3, // 0001 0010 1110 0011 'R' - 0x21e1, // 0010 0001 1110 0001 'S' - 0x0409, // 0000 0100 0000 1001 'T' - 0x3122, // 0011 0001 0010 0010 'U' - 0x1824, // 0001 1000 0010 0100 'V' - 0x1b22, // 0001 1011 0010 0010 'W' - 0x0a14, // 0000 1010 0001 0100 'X' - 0x0414, // 0000 0100 0001 0100 'Y' - 0x2805, // 0010 1000 0000 0101 'Z' - 0x3021, // 0011 0000 0010 0001 '[' - 0x71e3, // 0011 0001 1110 0011 '8.' - 0x2103, // 0010 0001 0000 0011 ']' - 0x0a00, // 0000 1010 0000 0000 '^' - 0x2000, // 0010 0000 0000 0000 '_' - 0x0010, // 0000 0000 0001 0000 '`' - 0x11e3, // 0001 0001 1110 0011 'a' - 0x254b, // 0010 0101 0100 1011 'b' - 0x3021, // 0011 0000 0010 0001 'c' - 0x250b, // 0010 0101 0000 1011 'd' - 0x30e1, // 0011 0000 1110 0001 'e' - 0x10e1, // 0001 0000 1110 0001 'f' - 0x3161, // 0011 0001 0110 0001 'g' - 0x11e2, // 0001 0001 1110 0010 'h' - 0x2409, // 0010 0100 0000 1001 'i' - 0x3102, // 0011 0001 0000 0010 'j' - 0x12a4, // 0001 0010 1010 0100 'k' - 0x3020, // 0011 0000 0010 0000 'l' - 0x1136, // 0001 0001 0011 0110 'm' - 0x1332, // 0001 0011 0011 0010 'n' - 0x3123, // 0011 0001 0010 0011 'o' - 0x10e3, // 0001 0000 1110 0011 'p' - 0x3323, // 0011 0011 0010 0011 'q' - 0x12e3, // 0001 0010 1110 0011 'r' - 0x21e1, // 0010 0001 1110 0001 's' - 0x0409, // 0000 0100 0000 1001 't' - 0x3122, // 0011 0001 0010 0010 'u' - 0x1824, // 0001 1000 0010 0100 'v' - 0x1b22, // 0001 1011 0010 0010 'w' - 0x0a14, // 0000 1010 0001 0100 'x' - 0x0414, // 0000 0100 0001 0100 'y' - 0x2805, // 0010 1000 0000 0101 'z' - 0x3021, // 0011 0000 0010 0001 '{' - 0x0408, // 0000 0100 0000 1000 '|' - 0x2103, // 0010 0001 0000 0011 '}' - 0x0a00, // 0000 1010 0000 0000 '~' - 0x0000, // 0000 0000 0000 0000 DEL - ]; - - my.Display.colorOn = "#00ffbb"; - my.Display.colorOff = "#002211"; - my.Display.overdraw = 0; - - my.Display.prototype.showSegments = function(segments, lit) { - // debugger; - var mask = 1; - var i; - for (i = 0; i < 16; i++) { - var on = (lit & mask) != 0; - segments[i].setAttribute("fill", on ? my.Display.colorOn : my.Display.colorOff); - if (my.Display.overdraw) { - segments[i].setAttribute("stroke-width", my.Display.overdraw); - if (on) { - segments[i].setAttribute("stroke", my.Display.colorOn); - } else { - segments[i].setAttribute("stroke", "none"); - } - } else { - segments[i].setAttribute("stroke", "none"); - } - mask <<= 1; - } - } - - my.Display.segmentsForCharacter = function(c, underline, blink, blinkPhase) { - var lit = (c < 32 || 127 < c) ? 0 : my.Display.segmentsByCharacter[c - 32]; - if (blink && !blinkPhase) { - if (underline) { - return lit; - } else { - return 0; - } - } else { - if (underline) { - return lit | 0x8000; - } else { - return lit; - } - } - } - - my.Display.prototype.setChar = function(y, x, c, underline, blink) { - var cell = this.cells[y][x]; - cell.char = c; - cell.underline = underline; - cell.blink = blink; - - this.showSegments(cell.segments, my.Display.segmentsForCharacter(c, underline, blink, this.blinkPhase)); - } - - my.Display.prototype.showString = function(y, x, s) { - for (var i = 0; i < s.length; i++) { - this.setChar(y, x, s.charCodeAt(i), false, false); - x++; - if (x >= this.cells[y].length) { - x = 0; - y++; - } - if (y >= this.cells.length) { - y = 0; - } - } - } - - my.Display.prototype.clear = function() { - for (var row = 0; row < this.cells.length; row++) { - var line = this.cells[row]; - for (var col = 0; col < line.length; col++) { - this.setChar(row, col, ' ', false, false); - } - } - } - - my.Display.prototype.blink = function(y, x) { - return this.cells[y][x].blink; - } - - my.Display.prototype.underline = function(y, x) { - return this.cells[y][x].underline; - } - - my.Display.prototype.setBlinkPhase = function(phase) { - this.blinkPhase = phase; - for (var row = 0; row < this.cells.length; row++) { - var line = this.cells[row]; - for (var col = 0; col < line.length; col++) { - var cell = line[col]; - if (cell.blink) { - this.showSegments(cell.segments, - my.Display.segmentsForCharacter(cell.char, cell.underline, cell.blink, this.blinkPhase)); - } - } - } - } - - my.Rect = function(x, y, w, h) { - this.x = x; - this.y = y; - this.w = w; - this.h = h; - } - - my.Rect.prototype.union = function(other) { - if (this.w == 0 || this.h == 0) { - return other; - } else if (other.w == 0 || other.h == 0) { - return this; - } else { - minX = Math.min(this.x, other.x); - maxX = Math.max(this.x+this.w, other.x+other.w); - minY = Math.min(this.y, other.y); - maxY = Math.max(this.y+this.h, other.y+other.h); - return new my.Rect(minX, minY, maxX-minX, maxY-minY); - } - } - - my.Rect.prototype.inset = function(dx, dy) { - return new my.Rect(this.x + dx, this.y + dy, this.w - 2*dx, this.h - 2*dy); - } - - my.Rect.prototype.offset = function(dx, dy) { - return new my.Rect(this.x+dx, this.y+dy, this.w, this.h); - } - - my.Rect.prototype.toPath = function(r) { - var rect = my.createElement("rect"); - rect.setAttribute("x", this.x); - rect.setAttribute("y", this.y); - rect.setAttribute("width", this.w); - rect.setAttribute("height", this.h); - if (r != null) { - rect.setAttribute("rx", r); - rect.setAttribute("ry", r); - } - return rect; - } - - my.Rect.prototype.getX = function(d) { - return this.x + d * this.w; - } - - my.Rect.prototype.getY = function(d) { - return this.y + d * this.h; - } - - my.displayRect = new my.Rect(15, 7, 82, 13); - - my.Button = function(x, y, w, h, label, labelPosition, value, color, multiPage, lightId) { - var that = this; - this.rect = new my.Rect(x, y, w, h); - - var rect = this.rect.inset(0.1, 0.1); - var translation = "translate(" + x + "," + y + ")"; - this.halo = rect.toPath(0.5); - this.halo.setAttribute("stroke", "#666666"); - this.halo.setAttribute("stroke-width", "2"); - this.halo.setAttribute("fill", "none"); - my.hideElement(this.halo); - - rect = rect.offset(-x, -y); - this.outline = rect.toPath(0.5); - this.outline.setAttribute("fill", color); - this.outline.setAttribute("stroke", "none"); - - this.group = my.createElement("g"); - this.group.setAttribute("transform", translation); - this.group.appendChild(this.outline); - - this.label = label; - this.labelPosition = labelPosition; - this.value = value; - this.color = color; - this.multiPage = multiPage; - this.lightId = lightId; - - if (label != undefined) { - var labelLines = label.split("\n"); - var fontSize = 1.4; - var labelText = my.createElement("text"); - labelText.setAttribute('fill', 'white'); - labelText.setAttribute('stroke', 'none'); - labelText.setAttribute('font-size', fontSize); - labelText.setAttribute('font-family', 'Helvetica'); - labelText.setAttribute('font-style', 'italic'); - labelText.setAttribute('width', w); - labelText.setAttribute('dominant-baseline', 'bottom'); - var x0 = 0; - var y0 = (1 - labelLines.length) * fontSize; - switch(labelPosition) { - case my.LabelPosition.ABOVE_CENTERED: - labelText.setAttribute('text-anchor', 'middle'); - x0 = w/2; - // fall through - case my.LabelPosition.ABOVE: - y0 = (1 - labelLines.length) * fontSize - 0.3; - break; - case my.LabelPosition.BELOW: - y0 = h + fontSize - 0.3; - break; - } - for (var i = 0; i < labelLines.length; i++) { - var tspan = my.createElement("tspan"); - tspan.setAttribute('x', x0); - tspan.setAttribute('y', y0 + i * fontSize); - tspan.appendChild(document.createTextNode(labelLines[i])); - labelText.appendChild(tspan); - } - this.group.appendChild(labelText); - } - - if (lightId >= 0) { - this.lightOn = my.createElement("path"); - this.lightOn.setAttribute("d", "M" + (rect.w/3) + "," + (rect.y+rect.h/25) + " h" + (rect.w/3) + " v" + (rect.h/3) + " h" + (-rect.w/3) + " z"); - this.lightOff = this.lightOn.cloneNode(true); - this.lightOn.setAttribute("fill", "#22ff22"); - this.lightOff.setAttribute("fill", "#112211"); - my.hideElement(this.lightOn); - - this.group.appendChild(this.lightOn); - this.group.appendChild(this.lightOff); - } - - this.group.addEventListener("touchstart", function(e) { that.press(e); }, true); - this.group.addEventListener("touchend", function(e) { that.release(e); }, true); - this.group.addEventListener("mousedown", function(e) { that.press(e); }, true); - this.group.addEventListener("mouseout", function(e) { that.release(e); }, true); - this.group.addEventListener("mouseup", function(e) { that.release(e); }, true); - - this.isPressed = false; - this.lightState = my.Light.OFF; - this.lightIsOn = false; - this.blinkPhase = true; - - this.onPress = undefined; - this.onRelease = undefined; - } - - my.Button.prototype.showPressed = function(isPressed) { - if (isPressed) { - my.showElement(this.halo); - } else { - my.hideElement(this.halo); - } - } - - my.Button.prototype.press = function(e) { - e.preventDefault(); - - if (!this.isPressed) { - this.isPressed = true; - this.showPressed(true); - - if (this.onPress != undefined) { - this.onPress(this); - } - } - - return false; - } - - my.Button.prototype.release = function(e) { - e.preventDefault(); - - if (this.isPressed) { - this.isPressed = false; - this.showPressed(false); - - if (this.onRelease != undefined) { - this.onRelease(this); - } - } - - return false; - } - - my.Button.prototype.updateLight = function() { - var on = this.lightState == my.Light.ON || (this.blinkPhase && this.lightState == my.Light.BLINK); - if (on != this.lightIsOn) { - my.hideElement(this.lightIsOn ? this.lightOn : this.lightOff); - this.lightIsOn = on; - my.showElement(this.lightIsOn ? this.lightOn : this.lightOff); - } - } - - my.Button.prototype.setLight = function(state) { - this.lightState = state; - this.updateLight(); - } - - my.Button.prototype.setBlinkPhase = function(phase) { - this.blinkPhase = phase; - this.updateLight(); - } - - my.Touch = function(x, y) { - this.x = x; - this.y = y; - } - - my.makeTouch = function(e) { - return new my.Touch(e.clientX, e.clientY); - } - - my.Slider = function(x, y, w, h, channel, value) { - - function makeRectPath(x, y, w, h, color) { - path = new my.Rect(x, y, w, h).toPath(); - path.setAttribute("fill", color); - return path; - } - - var that = this; - this.channel = channel; - this.value = value; - - this.rect = new my.Rect(x, y, w, h); - var rect = this.rect.offset(-x, -y); - var translation = "translate(" + x + "," + y + ")"; - this.group = my.createElement("g"); - this.group.setAttribute("transform", translation); - - this.frameColor = "#333333"; - this.frameActiveColor = "#666666"; - this.frame = rect.inset(0.25, 0.25).toPath(); - this.frame.setAttribute("stroke", this.frameColor); - this.frame.setAttribute("stroke-width", "0.5"); - this.group.appendChild(this.frame); - - this.handleX = 0.75; - this.handleW = w - 1.5; - this.handleH = 4; - this.handleMinY = 0.75; - this.handleMaxY = h - 0.75 - this.handleH; - - this.handle = my.createElement("g"); - this.handle.appendChild(makeRectPath(0, 0, this.handleW, this.handleH, "#333333")); - this.handle.appendChild(makeRectPath(0, 0, this.handleW, 0.75, "#444444")); - this.handle.appendChild(makeRectPath(0, 1.75, this.handleW, 0.25, "#222222")); - this.handle.appendChild(makeRectPath(0, 2, this.handleW, 0.25, "#444444")); - this.handle.appendChild(makeRectPath(0, 3.25, this.handleW, 0.75, "#222222")); - this.group.appendChild(this.handle); - - this.setValue(value); - - this.handle.addEventListener("touchstart", function(e) { that.touchstart(e); }, true); - this.group.addEventListener("touchmove", function(e) { that.touchmove(e); }, true); - this.group.addEventListener("touchend", function(e) { that.touchend(e); }, true); - this.group.addEventListener("touchcancel", function(e) { that.touchend(e); }, true); - - this.handle.addEventListener("mousedown", function(e) { that.grab(e.clientX, e.clientY); }, true); - this.group.addEventListener("mousemove", function(e) { that.drag(e.clientX, e.clientY); }, true); - this.group.addEventListener("mouseup", function(e) { that.release(); }, true); - - this.onValueChanged = undefined; - this.isGrabbed = false; - this.activeTouches = new Map(); - - } - - my.Slider.prototype.setValue = function(value) { - this.value = Math.max(0.0, Math.min((1.0, value))); - this.handleY = this.handleMinY + (1.0 - value) * (this.handleMaxY - this.handleMinY); - this.handle.setAttribute("transform", "translate(" + this.handleX + "," + this.handleY + ")"); - } - - my.Slider.prototype.setHandleY = function(handleY) { - this.handleY = Math.max(this.handleMinY, Math.min(this.handleMaxY, handleY)); - // console.log("Setting handleY to " + handleY + " => " + this.handleY); - this.value = 1.0 - (this.handleY - this.handleMinY) / (this.handleMaxY - this.handleMinY); - this.handle.setAttribute("transform", "translate(" + this.handleX + "," + this.handleY + ")"); - } - - my.Slider.prototype.grab = function(x, y) { - this.isGrabbed = true; - this.frame.setAttribute("stroke", this.frameActiveColor); - var p = my.pointIn(this.group, x, y); - this.dragOffset = p.y - this.handleY; - // console.log("Grabbing with handleY=" + this.handleY + ", p.y=" + p.y + " => dragOffset=" + this.dragOffset); - } - - my.Slider.prototype.drag = function(x, y) { - if (this.isGrabbed) { - var p = my.pointIn(this.group, x, y); - var newHandleY = p.y - this.dragOffset; - // console.log("Dragged with p.y=" + p.y + ", dragOffset=" + this.dragOffset + " => new handleY=" + newHandleY); - this.setHandleY(newHandleY); - if (this.onValueChanged != null) { - this.onValueChanged(this); - } - } - } - - my.Slider.prototype.release = function(e) { - this.isGrabbed = false; - this.frame.setAttribute("stroke", this.frameColor); - } - - my.Slider.prototype.activeTouchCenter = function() { - var n = this.activeTouches.size; - if (n <= 0) { - return undefined; - } - var x = 0; - var y = 0; - - for (const touch of this.activeTouches.values()) { - x += touch.x; - y += touch.y; - } - - return new my.Touch(x / n, y / n); - } - - my.Slider.prototype.touchstart = function(e) { - e.preventDefault(); - - var wasEmpty = this.activeTouches.size == 0; - for (var i = 0; i < e.targetTouches.length; i++) { - var touch = e.targetTouches.item(i); - this.activeTouches.set(touch.identifier, my.makeTouch(touch)); - } - - center = this.activeTouchCenter(); - if (center != null) { - this.grab(center.x, center.y); - } - - return false; - } - - my.Slider.prototype.touchmove = function(e) { - e.preventDefault(); - - for (var i = 0; i < e.changedTouches.length; i++) { - var touch = e.changedTouches.item(i); - if (this.activeTouches.has(touch.identifier)) { - this.activeTouches.set(touch.identifier, my.makeTouch(touch)); - } - } - center = this.activeTouchCenter(); - if (center != null) { - this.drag(center.x, center.y); - } - - return false; - } - - my.Slider.prototype.touchend = function(e) { - e.preventDefault(); - - for (var i = 0; i < e.changedTouches.length; i++) { - var touch = e.changedTouches.item(i); - this.activeTouches.delete(touch.identifier) - } - if (this.activeTouches.size == 0) { - this.release(); - } else { - center = this.activeTouchCenter(); - if (center != null) { - this.grab(center.x, center.y); - } - } - - return false; - } - - my.Panel = function(serverUrl, keyboard, version) { - this.serverUrl = serverUrl; - this.keyboard = keyboard; - this.version = version; - - this.container = my.createElement("svg"); - this.container.setAttribute("preserveAspectRatio", "xMidYMid meet"); - this.container.setAttribute("width", "2000"); - this.container.setAttribute("height", "375"); - this.container.setAttribute("overflow", "scroll"); - - this.haloContainer = my.createElement("g"); - this.container.appendChild(this.haloContainer); - - this.mainContainer = my.createElement("g"); - this.container.appendChild(this.mainContainer); - - this.displayContainer = my.createElement("svg"); - this.display = new my.Display(this.displayContainer, 2, 40); - this.displayContainer.setAttribute("preserveAspectRatio", "xMidYMid meet"); - this.displayContainer.setAttribute("x", my.displayRect.x); - this.displayContainer.setAttribute("y", my.displayRect.y); - this.displayContainer.setAttribute("width", my.displayRect.w); - this.displayContainer.setAttribute("height", my.displayRect.h); - this.container.appendChild(this.displayContainer); - - this.buttons = new Array(); - this.lightButtons = new Array(); - this.analogControls = new Array(); - this.addControls(keyboard); - - this.cursorX = 0; - this.cursorY = 0; - this.savedCursorX = 0; - this.savedCursorY = 0; - this.blink = false; - this.underline = false; - - this.serverUrl = serverUrl; - try { - this.connect(); - } catch (e) { - console.log("Unable to connect to '" + serverUrl + "': " + e); - } - - var that = this; - this.blinkPhase = 0; - setInterval(function() { that.updateBlink(); }, 250); - } - - my.Panel.prototype.updateBlink = function() { - this.blinkPhase = (this.blinkPhase + 1) % 4; - this.display.setBlinkPhase(this.blinkPhase < 2); - var buttonPhase = (this.blinkPhase % 2) == 0; - for (var i = 0; i < this.lightButtons.length; i++) { - this.lightButtons[i].setBlinkPhase(buttonPhase); - } - } - - my.Panel.prototype.connect = function() { - var that = this; - var panel = this; - var reconnect = function() { - that.connect(); - } - - this.socket = new WebSocket(this.serverUrl); - this.socket.binaryType = "arraybuffer"; - - this.socket.onopen = function(event) { - console.log("opened: " + event); - panel.sendString("I"); // Request server information - }; - - this.socket.onmessage = function(event) { - var data = new Uint8Array(event.data); - var c = String.fromCharCode(data[0]); - - if (c == 'A') { - console.log("handling analog value") - panel.handleAnalogValue(data.slice(1)); - } else if (c == 'B') { - console.log("handling button state") - panel.handleButtonState(data.slice(1)); - } else if (c == 'D') { - console.log("handling display data") - panel.handleDisplayData(data.slice(1)); - } else if (c == 'I') { - console.log("handling server information"); - panel.handleServerInformation(data.slice(1)); - } - }; - - this.socket.onclose = function(event) { - console.log("closed: ", event); - // reconnect after 1 second - setTimeout(reconnect, 1000); - }; - - this.socket.onerror = function(event) { - console.log("error: ", event); - }; - } - - my.Panel.prototype.addButton = function(x, y, w, h, label, labelPosition, value, color, multiPage, lightId) { - var that = this; - var button = new my.Button(x, y, w, h, label, labelPosition, value, color, multiPage, lightId); - this.haloContainer.appendChild(button.halo); - - if (lightId >= 0) { - if (lightId >= this.lightButtons.length) { - this.lightButtons.length = lightId + 1; - } - this.lightButtons[lightId] = button; - } - - this.mainContainer.appendChild(button.group); - this.buttons[value] = button; - - button.onPress = function(b) { - that.onButtonPressed(b); - } - button.onRelease = function(b) { - that.onButtonReleased(b); - } - - return button; - } - - my.Panel.prototype.addSlider = function(x, y, w, h, channel, value) { - var that = this; - var slider = new my.Slider(x, y, w, h, channel, value); - - this.mainContainer.appendChild(slider.group); - this.analogControls[channel] = slider; - - slider.onValueChanged = function(s) { - that.onAnalogValueChanged(s); - } - - return slider; - } - - my.Panel.prototype.addButtonBelowDisplay = function(x, y, label, value, shade) { - return this.addButton(x, y, 6, 4, label, my.LabelPosition.BELOW, value, shade, false, -1); - } - - my.Panel.prototype.addButtonWithLightBelowDisplay = function(x, y, label, value, shade, lightId) { - return this.addButton(x, y, 6, 4, label, my.LabelPosition.BELOW, value, shade, false, lightId); - } - - my.Panel.prototype.addLargeButton = function(x, y, label, value, shade) { - return this.addButton(x, y, 6, 4, label, my.LabelPosition.ABOVE, value, shade, false, -1); - } - - my.Panel.prototype.addLargeButtonWithLight = function(x, y, label, value, shade, lightId) { - return this.addButton(x, y, 6, 4, label, my.LabelPosition.ABOVE, value, shade, false, lightId); - } - - my.Panel.prototype.addSmallButton = function(x, y, label, value, shade, multiPage) { - return this.addButton(x, y, 6, 2, label, my.LabelPosition.ABOVE, value, shade, multiPage, -1); - } - - my.Panel.prototype.addIncDecButton = function(x, y, label, value, shade, multiPage) { - return this.addButton(x, y, 6, 2, label, my.LabelPosition.ABOVE_CENTERED, value, shade, multiPage, -1); - } - - my.Panel.prototype.addControls = function(keyboard) { - console.log("keyboard is '" + keyboard + "'"); - - // Normalize the keyboard string. - var hasSeq = false; - var hasBankSet = false; - keyboard = keyboard.toLowerCase(); - if (keyboard.indexOf('sd') != -1) { - hasSeq = true; - - if (keyboard.indexOf('1') != -1) { - hasBankSet = true; - - if (keyboard.indexOf('32') != -1) { - keyboard = my.Keyboard.SD1_32; - } else { - keyboard = my.Keyboard.SD1; - } - } else { - keyboard = my.Keyboard.VFX_SD; - } - } else { - keyboard = my.Keyboard.VFX; - } - - console.log("normalized keyboard is '" + keyboard + "'"); - - var cartString = hasBankSet ? "BankSet" : "Cart"; - - this.addButtonWithLightBelowDisplay(10, 29, cartString, 52, my.Shade.LIGHT, 0xf); - this.addButtonWithLightBelowDisplay(16, 29, "Sounds", 53, my.Shade.LIGHT, 0xd); - this.addButtonWithLightBelowDisplay(22, 29, "Presets", 54, my.Shade.LIGHT, 0x7); - if (hasSeq) { - this.addButtonBelowDisplay (28, 29, "Seq", 51, my.Shade.LIGHT); - } - - this.addButtonWithLightBelowDisplay(42, 29, "0", 55, my.Shade.MEDIUM, 0xe); - this.addButtonWithLightBelowDisplay(48, 29, "1", 56, my.Shade.MEDIUM, 0x6); - this.addButtonWithLightBelowDisplay(54, 29, "2", 57, my.Shade.MEDIUM, 0x4); - this.addButtonWithLightBelowDisplay(60, 29, "3", 46, my.Shade.MEDIUM, 0xc); - this.addButtonWithLightBelowDisplay(66, 29, "4", 47, my.Shade.MEDIUM, 0x3); - this.addButtonWithLightBelowDisplay(72, 29, "5", 48, my.Shade.MEDIUM, 0xb); - this.addButtonWithLightBelowDisplay(78, 29, "6", 49, my.Shade.MEDIUM, 0x2); - this.addButtonWithLightBelowDisplay(84, 29, "7", 35, my.Shade.MEDIUM, 0xa); - this.addButtonWithLightBelowDisplay(90, 29, "8", 34, my.Shade.MEDIUM, 0x1); - this.addButtonWithLightBelowDisplay(96, 29, "9", 25, my.Shade.MEDIUM, 0x9); - - // Large buttons on the main panel part - this.addLargeButton (108, 29, "Replace\nProgram", 29, my.Shade.MEDIUM); - this.addLargeButtonWithLight(114, 29, "1-6", 30, my.Shade.MEDIUM, 0x0); - this.addLargeButtonWithLight(120, 29, "7-12", 31, my.Shade.MEDIUM, 0x8); - - this.addLargeButton (154, 29, "Select\nVoice", 5, my.Shade.MEDIUM); - this.addLargeButton (160, 29, "Copy", 9, my.Shade.MEDIUM); - this.addLargeButton (166, 29, "Write", 3, my.Shade.MEDIUM); - this.addLargeButtonWithLight(172, 29, "Compare", 8, my.Shade.MEDIUM, 0x5); - - // Small buttons, main panel - // -- Performance: - this.addSmallButton(108, 20, "Patch\nSelect", 26, my.Shade.DARK, true); - this.addSmallButton(114, 20, "MIDI", 27, my.Shade.DARK, true); - this.addSmallButton(120, 20, "Effects", 28, my.Shade.DARK, true); - - this.addSmallButton(108, 13, "Key\nZone", 39, my.Shade.DARK, false); - this.addSmallButton(114, 13, "Trans-\npose", 40, my.Shade.DARK, false); - this.addSmallButton(120, 13, "Release", 41, my.Shade.DARK, false); - - this.addSmallButton(108, 6, "Volume", 36, my.Shade.DARK, false); - this.addSmallButton(114, 6, "Pan", 37, my.Shade.DARK, false); - this.addSmallButton(120, 6, "Timbre", 38, my.Shade.DARK, false); - - // Sequencer / System, both large and small buttons: - if (hasSeq) { - // The 'Master', 'Storage' and 'MIDI Control' buttons are small & at the to, - // the sequencer buttons are big and at the bottom. - this.addLargeButton(131, 29, "Rec", 19, my.Shade.MEDIUM); - this.addLargeButton(137, 29, "Stop\n/Cont", 22, my.Shade.MEDIUM); - this.addLargeButton(143, 29, "Play", 23, my.Shade.MEDIUM); - - this.addSmallButton(131, 20, "Click", 32, my.Shade.DARK, false); - this.addSmallButton(137, 20, "Seq\nControl", 18, my.Shade.DARK, true); - this.addSmallButton(143, 20, "Locate", 33, my.Shade.DARK, true); - - this.addSmallButton(131, 13, "Song", 60, my.Shade.DARK, false); - this.addSmallButton(137, 13, "Seq", 59, my.Shade.DARK, false); - this.addSmallButton(143, 13, "Track", 61, my.Shade.DARK, false); - - this.addSmallButton(131, 6, "Master", 20, my.Shade.LIGHT, true); - this.addSmallButton(137, 6, "Storage", 21, my.Shade.LIGHT, false); - this.addSmallButton(143, 6, "MIDI\nControl", 24, my.Shade.LIGHT, true); - } else { - // The 'Master', 'Storage' and 'MIDI Control' buttons are large & at the bottom, - // and there are no sequencer buttons - this.addLargeButton(131, 29, "Master", 20, my.Shade.LIGHT, true); - this.addLargeButton(137, 29, "Storage", 21, my.Shade.LIGHT, false); - this.addLargeButton(143, 29, "MIDI\nControl", 24, my.Shade.LIGHT, true); - } - - // -- Programming: - this.addSmallButton(154, 20, "Wave", 4, my.Shade.DARK, false); - this.addSmallButton(160, 20, "Mod\nMixer", 6, my.Shade.DARK, false); - this.addSmallButton(166, 20, "Program\nControl", 2, my.Shade.DARK, false); - this.addSmallButton(172, 20, "Effects", 7, my.Shade.DARK, true); - - this.addSmallButton(154, 13, "Pitch", 11, my.Shade.DARK, false); - this.addSmallButton(160, 13, "Pitch\nMod", 13, my.Shade.DARK, false); - this.addSmallButton(166, 13, "Filters", 15, my.Shade.DARK, true); - this.addSmallButton(172, 13, "Output", 17, my.Shade.DARK, true); - - this.addSmallButton(154, 6, "LFO", 10, my.Shade.DARK, true); - this.addSmallButton(160, 6, "Env1", 12, my.Shade.DARK, true); - this.addSmallButton(166, 6, "Env2", 14, my.Shade.DARK, true); - this.addSmallButton(172, 6, "Env3", 16, my.Shade.DARK, true); - - // Display buttons - approximate: - this.addSmallButton(32, 21, "", 50, my.Shade.DARK, false); - this.addSmallButton(57, 21, "", 44, my.Shade.DARK, false); - this.addSmallButton(82, 21, "", 45, my.Shade.DARK, false); - - this.addSmallButton(32, 4, "", 58, my.Shade.DARK, false); - this.addSmallButton(57, 4, "", 42, my.Shade.DARK, false); - this.addSmallButton(82, 4, "", 43, my.Shade.DARK, false); - - // Value slider - var valueSlider = this.addSlider(-2.75, 4, 7, 22, 3, 0.7); - - // Increment and Decrement - this.addIncDecButton(-12.5, 22, "\u25BC", 63, my.Shade.DARK, false); - this.addIncDecButton(-12.5, 12, "\u25B2", 62, my.Shade.DARK, false); - - // Volume slider - var volumeSlider = this.addSlider(-30, 4, 7, 22, 5, 1.0); - - var r = undefined; - for (var i = 1; i < this.buttons.length; i++) { - var button = this.buttons[i]; - if (button != null) { - if (r != null) { - r = r.union(button.rect); - } else { - r = button.rect; - } - } - } - r = r.union(valueSlider.rect); - r = r.union(volumeSlider.rect); - - r.x -= 2; - r.y -= 2; - r.w += 4; - r.h += 4; - - var viewBox = "" + r.x + " " + r.y + " " + r.w + " " + r.h; - this.container.setAttribute("viewBox", viewBox); - } - - my.Panel.prototype.sendString = function(s) { - if (this.socket != undefined && this.socket.readyState == WebSocket.OPEN) { - var b = new Uint8Array(s.length); - for (var i = 0; i < s.length; i++) { - b[i] = s.charCodeAt(i); - } - this.socket.send(b); - } - } - - my.Panel.prototype.onButtonPressed = function(button) { - this.sendString("BD " + button.value); - } - - my.Panel.prototype.onButtonReleased = function(button) { - this.sendString("BU " + button.value); - } - - my.Panel.prototype.onAnalogValueChanged = function(slider) { - // 0.05 == 0; 0.95 == 760 - var value = (slider.value - 0.05) / 0.9; - value = 760 * value; - value = Math.round(Math.max(0, Math.min(1023, value))); - var s = "A" + slider.channel + " " + value; - - // console.log(s); - this.sendString(s); - } - - my.Panel.prototype.handleDisplayData = function(data) { - console.log("Handling display data " + data.length + " : " + data); - for (var i = 0; i < data.length; i++) { - var received = data[i]; - - if (this.ignoreNext > 0) { - console.log("skipping byte: 0x" + received.toString(16)); - this.ignoreNext--; - continue; - } - - console.log("handling byte: 0x" + received.toString(16)); - if (this.light) { - var whichLight = received & 0x3f; - var button = this.lightButtons[whichLight]; - if (button != null) { - var state = (received & 0xc0); - if (state == 0x80) { - button.setLight(my.Light.ON); - } else if (state == 0xc0) { - button.setLight(my.Light.BLINK); - } else { - button.setLight(my.Light.OFF); - } - } - this.light = false; - } else if ((received >= 0x80) && (received < 0xd0)) { - this.cursorY = ((received & 0x7f) >= 40) ? 1 : 0; - this.cursorX = (received & 0x7f) % 40; - this.blink = this.display.blink(this.cursorY, this.cursorX); - this.underline = this.display.underline(this.cursorY, this.cursorX); - console.log("moving to row " + this.cursorY + ", column " + this.cursorX); - } else if (received >= 0xd0) { - switch (received) { - case 0xd0: // blink start - console.log("blink on"); - this.blink = true; - break; - - case 0xd1: // blink stop (cancel all attribs on VFX+) - console.log("attrs off"); - this.blink = false; - this.underline = false; - break; - - case 0xd2: // blinking underline? - console.log("blinking underline on"); - this.blink = true; - this.underline = true; - break; - - case 0xd3: // start underline - console.log("underline on"); - this.underline = true; - break; - - case 0xd6: // clear screen - console.log("clear screen"); - this.cursorX = this.cursorY = 0; - this.blink = this.underline = false; - this.display.clear(); - break; - - case 0xf5: // save cursor position - this.savedCursorX = this.cursorX; - this.savedCursorY = this.cursorY; - console.log("saving cursor position (row " + this.savedCursorY + ", col " + this.savedCursorX + ")"); - break; - - case 0xf6: // restore cursor position - this.cursorX = this.savedCursorX; - this.cursorY = this.savedCursorY; - this.blink = this.display.blink(this.cursorY, this.cursorX); - this.underline = this.display.underline(this.cursorY, this.cursorX); - console.log("restored cursor position (row " + this.savedCursorY + ", col " + this.savedCursorX + ")"); - break; - - case 0xff: // Specify a button light state - this.light = true; - break; - - default: - console.log("Unexpected control code: " + received); - break; - } - } else if ((received >= 0x20) && (received <= 0x5f)) { - // var attrs = this.blink ? this.underline ? " with blink & underline" : " with blink" : this.underline ? " with underline" : ""; - // console.log("at (" + this.cursorY + ", " + this.cursorX + ") char " + received + attrs); - this.display.setChar(this.cursorY, this.cursorX, received, this.underline, this.blink); - this.cursorX = Math.min(this.cursorX + 1, 39); - } else { - console.log("Unexpected byte: " + received.toString(16)); - } - } - } - - my.Panel.prototype.handleAnalogValue = function(data) { - var s = String.fromCharCode.apply(null, data); - console.log("Handling analog value: '" + s + "'"); - var parts = s.split(" "); - if (parts.length == 2) { - var channel = parseInt(parts[0]); - var value = parseInt(parts[1]); - - var analogControl = this.analogControls[channel]; - if (analogControl != null) { - if (analogControl instanceof my.Slider) { - // 0.05 == 0; 0.95 == 760 - value = value / 760.0; - value = 0.05 + 0.9 * value; - analogControl.setValue(value); - } - } - } - } - - my.Panel.prototype.handleButtonState = function(data) { - var s = String.fromCharCode.apply(null, data); - var parts = s.split(" "); - if (parts.length == 2) { - var pressed = parts[0] == 'D'; - var number = parseInt(parts[1]); - var button = this.buttons[number]; - if (button != null && button instanceof my.Button) { - button.showPressed(pressed); - } - } - } - - my.Panel.prototype.handleServerInformation = function(data) { - var s = String.fromCharCode.apply(null, data); - var parts = s.split(","); - var keyboard = "none"; - var version = -1; - if (parts.length == 2) { - keyboard = parts[0]; - version = parseInt(parts[1]); - } - if (keyboard == this.keyboard && version == this.version) { - // same keyboard type version - proceed! - this.sendString("CA1B1D1"); // Send me analog data, buttons, and display data - } else { - // we need to reload, forcing a refresh from the server. - document.location.reload(true); - } - } - - return my; - })(); - \ No newline at end of file