From 3b8f09d075623d6e04d4e893d6f206779e725833 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Fri, 5 Sep 2025 09:03:09 +0100
Subject: [PATCH 01/22] Handle the command that are encountered before the
"Disk Drive Not Ready" message is shown, by clearing the screen and ignoring
things otherwise. This seems to behave much like the real display does.
---
web/esqpanel/vfx/FrontPanel.js | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/web/esqpanel/vfx/FrontPanel.js b/web/esqpanel/vfx/FrontPanel.js
index 69e1b67ac8f5e..dedf85dca6438 100644
--- a/web/esqpanel/vfx/FrontPanel.js
+++ b/web/esqpanel/vfx/FrontPanel.js
@@ -1134,6 +1134,19 @@ var fp = (function() {
console.log("restored cursor position (row " + this.savedCursorY + ", col " + this.savedCursorX + ")");
break;
+ case 0xfb: // unknown but encountered in command stream
+ console.log("0xfb: ignoring");
+ break;
+
+ case 0xfd: // unknown but encountered in command streem
+ console.log("0xfd: clearing");
+ this.cursorX = this.cursorY = 0;
+ this.blink = this.underline = false;
+ this.display.clear();
+ // seems to be always followed by 0x7f, let's ignore that one
+ this.ignoreNext = 1;
+ break;
+
case 0xff: // Specify a button light state
this.light = true;
break;
From d99a457ef7a1cc65bd30bc2fbd84026300e83638 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Fri, 5 Sep 2025 22:42:49 +0100
Subject: [PATCH 02/22] Add a layout reproducing the NEC FIP80B5R display used
by some Ensoniq keyboards,
---
src/mame/layout/nec_fip80b5r.lay | 222 +++++++++++++++++++++++++++++++
1 file changed, 222 insertions(+)
create mode 100644 src/mame/layout/nec_fip80b5r.lay
diff --git a/src/mame/layout/nec_fip80b5r.lay b/src/mame/layout/nec_fip80b5r.lay
new file mode 100644
index 0000000000000..ece139a422218
--- /dev/null
+++ b/src/mame/layout/nec_fip80b5r.lay
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From e89dbf8ad20872d018ef7d735c85e9c4da1c3d6f Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Fri, 5 Sep 2025 23:25:01 +0100
Subject: [PATCH 03/22] Some refactoring of the Ensoniq VFX-family panel and
VFD display classes.
Specifically, introduced a new display class that uses the NEC FIP80B5R layout, so that it visually matches the display within the HTTP front panel.
Also, move its command parsing code into the corresponding panel class, which already handles command parsing for the keboard processor, lights attached to buttons, thus collecting that processing in a single place.
This also makes it eaier to handle commands differently, as they commands differ between keyboards, even those that have the same display.
Simiilarly, simplify the FrontPanel.js code so that it, too, no longer parses the command stream from the CPU, but insteadis a much simpler extrnal panel display - effectively making it also match the new VFX-family VFD class.
---
src/mame/ensoniq/esqpanel.cpp | 335 +++++++++++++++++++++++++++++----
src/mame/ensoniq/esqpanel.h | 40 +++-
src/mame/ensoniq/esqvfd.cpp | 206 +++++++++++++++++---
src/mame/ensoniq/esqvfd.h | 29 ++-
web/esqpanel/vfx/FrontPanel.js | 183 ++++++------------
5 files changed, 599 insertions(+), 194 deletions(-)
diff --git a/src/mame/ensoniq/esqpanel.cpp b/src/mame/ensoniq/esqpanel.cpp
index 1343ab289d1aa..108479d6828f3 100644
--- a/src/mame/ensoniq/esqpanel.cpp
+++ b/src/mame/ensoniq/esqpanel.cpp
@@ -146,26 +146,84 @@ namespace esqpanel {
}
}
- void send_to_all(char c)
+ void send(const std::string &s, http_manager::websocket_connection_ptr conn = nullptr)
{
- // 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())
+ if (conn) {
+ // sending this only to a single connection that needs a large update
+ send(conn, s);
+ } else {
+ for (const auto &iter: m_panels)
{
- send(iter.first, s);
+ external_panel_ptr panel = iter.second;
+ if (panel->send_display_data())
+ {
+ send(iter.first, s);
+ }
}
}
}
+ void clear_display(http_manager::websocket_connection_ptr conn = nullptr)
+ {
+ send("DX", conn);
+ }
+
+ void set_char(uint8_t row, uint8_t column, uint8_t c, bool underline, bool blink, http_manager::websocket_connection_ptr conn = nullptr)
+ {
+ std::ostringstream os;
+ os << "DC" << static_cast(row) << " " << static_cast(column) << " " << static_cast(c) << " " << underline << " " << blink;
+ send(os.str(), conn);
+ }
+
+ void set_light(uint8_t light, uint8_t state, http_manager::websocket_connection_ptr conn = nullptr)
+ {
+ std::ostringstream os;
+ os << "L" << static_cast(light) << " " << static_cast(state);
+ send(os.str(), conn);
+ }
+
+ void button_down(uint8_t button, http_manager::websocket_connection_ptr conn = nullptr)
+ {
+ std::ostringstream os;
+ os << "BD " << static_cast(button);
+ send(os.str(), conn);
+ }
+
+ void button_up(uint8_t button, http_manager::websocket_connection_ptr conn = nullptr)
+ {
+ std::ostringstream os;
+ os << "BU " << static_cast(button);
+ send(os.str(), conn);
+ }
+
+ void set_button(uint8_t button, bool pressed, http_manager::websocket_connection_ptr conn = nullptr)
+ {
+ if (pressed)
+ {
+ button_down(button, conn);
+ }
+ else
+ {
+ button_up(button, conn);
+ }
+ }
+
+ void set_analog_value(uint8_t channel, int value, http_manager::websocket_connection_ptr conn = nullptr)
+ {
+ std::ostringstream os;
+ os << "A" << static_cast(channel) << " " << value;
+ send(os.str(), conn);
+ }
+
+ void set_blink_phase(uint8_t phase)
+ {
+ std::ostringstream os;
+ os << "P" << static_cast(phase);
+ send(os.str());
+ }
+
void on_open(http_manager::websocket_connection_ptr connection)
{
using namespace std::placeholders;
@@ -188,7 +246,7 @@ namespace esqpanel {
if ((changed & message_type::DISPLAY) && panel->send_display_data())
{
// printf("server: control message, sending contents\n");
- send_contents(connection);
+ send_display_contents(connection);
}
if ((changed & message_type::ANALOG) && panel->send_analog_values())
@@ -305,9 +363,19 @@ namespace esqpanel {
}
}
- void set_content_provider(std::function provider)
+ void set_send_display_contents(std::function fun)
+ {
+ m_send_display_contents = fun;
+ }
+
+ void set_send_analog_values(std::function fun)
{
- m_content_provider = provider;
+ m_send_analog_values = fun;
+ }
+
+ void set_send_button_states(std::function fun)
+ {
+ m_send_button_states = fun;
}
void set_keyboard(const std::string &keyboard)
@@ -349,27 +417,28 @@ namespace esqpanel {
connection->send_message(s, websocket_opcode::binary);
}
- void send_contents(http_manager::websocket_connection_ptr connection)
+ void send_display_contents(http_manager::websocket_connection_ptr connection)
{
- if (m_content_provider)
+ if (m_send_display_contents)
{
- m_to_send.str("");
- m_to_send.put('D');
- if (m_content_provider(m_to_send))
- {
- send(connection, m_to_send.str());
- }
+ m_send_display_contents(connection);
}
}
void send_analog_values(http_manager::websocket_connection_ptr connection)
{
- // TODO(cbrunschen): get the current analog values and send them
+ if (m_send_analog_values)
+ {
+ m_send_analog_values(connection);
+ }
}
void send_button_states(http_manager::websocket_connection_ptr connection)
{
- // TODO(cbrunschen): track current button states and send them
+ if (m_send_button_states)
+ {
+ m_send_button_states(connection);
+ }
}
http_manager *m_server;
@@ -383,7 +452,12 @@ namespace esqpanel {
std::string m_index;
std::string m_keyboard;
std::string m_version;
- std::function m_content_provider;
+
+ // callbacks to send
+ std::function m_send_display_contents;
+ std::function m_send_button_states; // also includes button light states. really
+ std::function m_send_analog_values;
+
std::map m_template_values;
};
@@ -429,14 +503,16 @@ void esqpanel_device::device_start()
{
m_external_panel_server = new esqpanel::external_panel_server(machine().manager().http());
if (machine().manager().http()->is_active()) {
+ using namespace std::placeholders;
+
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_panel_server->set_send_display_contents(std::bind(&esqpanel_device::send_display_contents, this, _1));
+ m_external_panel_server->set_send_analog_values(std::bind(&esqpanel_device::send_analog_values, this, _1));
+ m_external_panel_server->set_send_button_states(std::bind(&esqpanel_device::send_button_states, this, _1));
m_external_timer = timer_alloc(FUNC(esqpanel_device::check_external_panel_server), this);
m_external_timer->enable(false);
@@ -489,7 +565,6 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte
// if (data >= 0xe0) printf("Got %02x from motherboard (second %s)\n", data, m_bCalibSecondByte ? "yes" : "no");
send_to_display(data);
- m_external_panel_server->send_to_all(data);
if (m_bCalibSecondByte)
{
@@ -627,6 +702,16 @@ TIMER_CALLBACK_MEMBER(esqpanel_device::check_external_panel_server) {
// printf("button %d %s : sending char to mainboard: %02x\n", button, down ? "down" : "up", sendme);
xmit_char(sendme);
xmit_char(0x00);
+
+ // record the button pressed state
+ if (down)
+ {
+ m_pressed_buttons.insert(button);
+ }
+ else
+ {
+ m_pressed_buttons.erase(button);
+ }
} else if (c == 'A') {
// analog value from ES5505 OTIS: 10 bits, left-aligned within 16 bits.
int channel, value;
@@ -679,7 +764,7 @@ esqpanel2x40_device::esqpanel2x40_device(const machine_config &mconfig, const ch
void esqpanel2x40_vfx_device::device_add_mconfig(machine_config &config)
{
- ESQ2X40(config, m_vfd, 60);
+ ESQ2X40_VFX(config, m_vfd, 60);
}
esqpanel2x40_vfx_device::esqpanel2x40_vfx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
@@ -689,17 +774,191 @@ esqpanel2x40_vfx_device::esqpanel2x40_vfx_device(const machine_config &mconfig,
m_eps_mode = false;
}
-bool esqpanel2x40_vfx_device::write_contents(std::ostream &o)
+void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
{
- m_vfd->write_contents(o);
- for (int i = 0; i < m_light_states.size(); i++)
+ 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 (m_bCalibSecondByte)
{
- o.put(char(0xff));
- o.put((m_light_states[i] << 6) | i);
+// printf("second byte is %02x\n", data);
+ if (data == 0xfd) // calibration request
+ {
+// printf("let's send reply!\n");
+ xmit_char(0xff); // this is the correct response for "calibration OK"
+ }
+ m_bCalibSecondByte = false;
+ }
+ else if (m_bButtonLightSecondByte)
+ {
+ // 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 lightNumber = data & 0x3f;
+
+ // Light states:
+ // 0 = Off
+ // 2 = On
+ // 3 = Blinking
+ m_light_states[lightNumber] = (data & 0xc0) >> 6;
+ m_external_panel_server->set_light(lightNumber, m_light_states[lightNumber]);
+
+ // 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;
+ }
+ else if (data == 0xfb) // request calibration
+ {
+ m_bCalibSecondByte = true;
+ }
+ else if (data == 0xff) // button light state command
+ {
+ m_bButtonLightSecondByte = 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_server->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;
+
+ default:
+ // printf("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_server->set_char(m_cursy, m_cursx, data - ' ', (m_curattr * AT_UNDERLINE) != 0, (m_curattr & AT_BLINK) != 0);
+ m_cursx++;
+
+ if (m_cursx >= 39)
+ {
+ m_cursx = 39;
+ }
+ }
+ }
+ }
+}
+
+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_server->set_blink_phase(m_blink_phase);
+}
+
+void esqpanel2x40_vfx_device::device_start()
+{
+ esqpanel_device::device_start();
+
+ 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();
+
+ 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::send_display_contents(http_manager::websocket_connection_ptr conn)
+{
+ for (int row = 0; row < 2; row++)
+ {
+ for (int column = 0; column < 40; column++)
+ {
+ auto attr = m_vfd->get_attr(row, column);
+ m_external_panel_server->set_char(row, column, m_vfd->get_char(row, column), (attr & AT_UNDERLINE) != 0, (attr & AT_BLINK) != 0);
+ }
}
- return true;
}
+void esqpanel2x40_vfx_device::send_analog_values(http_manager::websocket_connection_ptr conn)
+{
+ // TODO(cbrunschen): read analog values from the emulated keyboard and send them to the panel(s)
+}
+
+void esqpanel2x40_vfx_device::send_button_states(http_manager::websocket_connection_ptr conn)
+{
+ // send the button statuses
+ for (int i = 0; i < 64; i++)
+ {
+ bool pressed = m_pressed_buttons.find(i) != m_pressed_buttons.end();
+ m_external_panel_server->set_button(i, pressed);
+ }
+
+ // and the states for the lights
+ for (int i = 0; i < 16; i++)
+ {
+ m_external_panel_server->set_light(i, m_light_states[i]);
+ }
+}
// --- SQ1 - Parduz --------------------------------------------------------------------------------------------------------------------------
diff --git a/src/mame/ensoniq/esqpanel.h b/src/mame/ensoniq/esqpanel.h
index 468865739bafb..a5a557b8cd20d 100644
--- a/src/mame/ensoniq/esqpanel.h
+++ b/src/mame/ensoniq/esqpanel.h
@@ -9,8 +9,10 @@
#include "esqlcd.h"
#include "diserial.h"
+#include "http.h"
#include
+#include
//**************************************************************************
@@ -52,20 +54,22 @@ class esqpanel_device : public device_t, public device_serial_interface
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; }
+ virtual void send_display_contents(http_manager::websocket_connection_ptr conn) { }
+ virtual void send_analog_values(http_manager::websocket_connection_ptr conn) { }
+ virtual void send_button_states(http_manager::websocket_connection_ptr conn) { }
+ std::set m_pressed_buttons;
std::vector m_light_states;
bool m_eps_mode = false;
+ bool m_bCalibSecondByte = false;
+ bool m_bButtonLightSecondByte = false;
esqpanel::external_panel_server *m_external_panel_server;
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;
uint8_t m_xmitring[XMIT_RING_SIZE];
@@ -105,14 +109,36 @@ class esqpanel2x40_vfx_device : public esqpanel_device {
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 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;
- required_device m_vfd;
+ virtual void send_display_contents(http_manager::websocket_connection_ptr conn) override;
+ virtual void send_analog_values(http_manager::websocket_connection_ptr conn) override;
+ virtual void send_button_states(http_manager::websocket_connection_ptr conn) override;
+
+ required_device m_vfd;
+
+ 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;
+
+ TIMER_CALLBACK_MEMBER(update_blink);
+
+private:
+ int m_cursx = 0, m_cursy = 0;
+ int m_savedx = 0, m_savedy = 0;
+ int const m_rows = 0, m_cols = 0;
+ uint8_t m_curattr = 0;
+
+ emu_timer *m_blink_timer = nullptr;
+ uint8_t m_blink_phase;
};
class esqpanel2x40_sq1_device : public esqpanel_device {
diff --git a/src/mame/ensoniq/esqvfd.cpp b/src/mame/ensoniq/esqvfd.cpp
index f8aa3435f56b0..0b95c2fea0a7c 100644
--- a/src/mame/ensoniq/esqvfd.cpp
+++ b/src/mame/ensoniq/esqvfd.cpp
@@ -10,10 +10,12 @@
#include "esq1by22.lh"
#include "esq2by40.lh"
+#include "nec_fip80b5r.lh"
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 +118,127 @@ static const uint16_t font[]=
0x0000, // 0000 0000 0000 0000 (DEL)
};
+/**
+ * The font used by the VFX family on the NEC FIP80B5R display, including
+ * VFX-family-specific characters sich as digits followed by a period / decimal point.
+ * Arranged to match this, fairly arbitrarily chosen, order of segments:
+ *
+ * ---- 0 ----
+ * |\ | /|
+ * | \ | / |
+ * 5 4 3 2 1
+ * | \ | / |
+ * | \|/ |
+ * --7-- --6--
+ * | /|\ |
+ * | / | \ |
+ * 12 11 10 9 8
+ * | / | \ |
+ * |/ | \|
+ * ----13----- *14
+ *
+ * -----15-----
+ *
+ */
+static const uint16_t font_vfx[] = {
+ 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
+};
+
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 +385,76 @@ 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, 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];
-
- o.put((char) 0xd1); // all attributes off
+ 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);
- 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)
{
+ config.set_default_layout(layout_nec_fip80b5r);
+}
+
+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 +464,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..f24aa19d1560c 100644
--- a/src/mame/ensoniq/esqvfd.h
+++ b/src/mame/ensoniq/esqvfd.h
@@ -36,7 +36,7 @@ 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);
@@ -77,12 +77,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 +123,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/web/esqpanel/vfx/FrontPanel.js b/web/esqpanel/vfx/FrontPanel.js
index dedf85dca6438..a24607e3a20f9 100644
--- a/web/esqpanel/vfx/FrontPanel.js
+++ b/web/esqpanel/vfx/FrontPanel.js
@@ -264,7 +264,7 @@ var fp = (function() {
}
my.Display.segmentsForCharacter = function(c, underline, blink, blinkPhase) {
- var lit = (c < 32 || 127 < c) ? 0 : my.Display.segmentsByCharacter[c - 32];
+ var lit = (c < 0 || 95 < c) ? 0 : my.Display.segmentsByCharacter[c];
if (blink && !blinkPhase) {
if (underline) {
return lit;
@@ -736,13 +736,6 @@ var fp = (function() {
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();
@@ -750,15 +743,13 @@ var fp = (function() {
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;
+ my.Panel.prototype.setBlinkPhase = function(blinkPhase) {
+ this.blinkPhase = blinkPhase % 4;
+ this.display.setBlinkPhase(this.blinkPhase & 2);
+ var buttonPhase = (this.blinkPhase & 1) == 0;
for (var i = 0; i < this.lightButtons.length; i++) {
this.lightButtons[i].setBlinkPhase(buttonPhase);
}
@@ -775,7 +766,7 @@ var fp = (function() {
this.socket.binaryType = "arraybuffer";
this.socket.onopen = function(event) {
- console.log("opened: " + event);
+ // console.log("opened: " + event);
panel.sendString("I"); // Request server information
};
@@ -783,17 +774,26 @@ var fp = (function() {
var data = new Uint8Array(event.data);
var c = String.fromCharCode(data[0]);
+ var s = String.fromCharCode.apply(null, data);
+ // console.log("Handling message '" + s + "'");
+
if (c == 'A') {
- console.log("handling analog value")
+ // console.log("handling analog value")
panel.handleAnalogValue(data.slice(1));
} else if (c == 'B') {
- console.log("handling button state")
+ // console.log("handling button state")
panel.handleButtonState(data.slice(1));
} else if (c == 'D') {
- console.log("handling display data")
+ // console.log("handling display data")
panel.handleDisplayData(data.slice(1));
+ } else if (c == 'L') {
+ // console.log("handling Light state")
+ panel.handleLightState(data.slice(1));
+ } else if (c == 'P') {
+ // console.log("handling blink Phase")
+ panel.handleBlinkPhase(data.slice(1));
} else if (c == 'I') {
- console.log("handling server information");
+ // console.log("handling server information");
panel.handleServerInformation(data.slice(1));
}
};
@@ -1058,117 +1058,58 @@ var fp = (function() {
}
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;
+ var c = String.fromCharCode(data[0]);
+ if (c == 'X') {
+ // Clear the screen
+ // console.log("Clearing the screen");
+ this.display.clear();
+ } else if (c == 'C') {
+ // Character data
+ var s = String.fromCharCode.apply(null, data.slice(1));
+ // console.log("Displaying character: '" + s + "'");
+ var parts = s.split(" ");
+ if (parts.length == 5) {
+ let row = parseInt(parts[0]);
+ let column = parseInt(parts[1]);
+ let ch = parseInt(parts[2]);
+ let underline = parseInt(parts[3]) != 0;;
+ let blink = parseInt(parts[4]) != 0;
+ this.display.setChar(row, column, ch, underline, blink);
}
+ } else {
+ var s = String.fromCharCode.apply(null, data);
+ console.log("Unknown display message '" + s + "'");
+ }
+ }
- 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 0xfb: // unknown but encountered in command stream
- console.log("0xfb: ignoring");
- break;
-
- case 0xfd: // unknown but encountered in command streem
- console.log("0xfd: clearing");
- this.cursorX = this.cursorY = 0;
- this.blink = this.underline = false;
- this.display.clear();
- // seems to be always followed by 0x7f, let's ignore that one
- this.ignoreNext = 1;
- break;
-
- case 0xff: // Specify a button light state
- this.light = true;
- break;
-
- default:
- console.log("Unexpected control code: " + received);
- break;
+ my.Panel.prototype.handleLightState = function(data) {
+ var s = String.fromCharCode.apply(null, data);
+ var parts = s.split(" ");
+ if (parts.length == 2) {
+ let whichLight = parseInt(parts[0]);
+ let state = parseInt(parts[1]);
+ var button = this.lightButtons[whichLight];
+ if (button != null && button instanceof my.Button) {
+ if (state == 2) {
+ button.setLight(my.Light.ON);
+ } else if (state == 3) {
+ button.setLight(my.Light.BLINK);
+ } else {
+ button.setLight(my.Light.OFF);
}
- } 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.handleBlinkPhase = function(data) {
+ var s = String.fromCharCode.apply(null, data);
+ let phase = parseInt(s);
+ this.setBlinkPhase(phase);
+ }
+
my.Panel.prototype.handleAnalogValue = function(data) {
var s = String.fromCharCode.apply(null, data);
- console.log("Handling analog value: '" + s + "'");
+ // console.log("Handling analog value: '" + s + "'");
var parts = s.split(" ");
if (parts.length == 2) {
var channel = parseInt(parts[0]);
From e19050add87171299bc3e847f19568e4f18f00b4 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Sat, 6 Sep 2025 08:11:51 +0100
Subject: [PATCH 04/22] In each segment SVG, remove an errant quote mark and
add an explicit viewBox
---
src/mame/layout/nec_fip80b5r.lay | 64 ++++++++++++++++----------------
1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/src/mame/layout/nec_fip80b5r.lay b/src/mame/layout/nec_fip80b5r.lay
index ece139a422218..6cc1fc4638689 100644
--- a/src/mame/layout/nec_fip80b5r.lay
+++ b/src/mame/layout/nec_fip80b5r.lay
@@ -11,192 +11,192 @@ license:CC0-1.0
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
+
]]>
From 0625a74097b678c540ecbee0de8755284640eb15 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Sat, 6 Sep 2025 08:31:13 +0100
Subject: [PATCH 05/22] Move each segment into its own artwork SVG file, and
use those in the nec_fip80b5r layout.
---
artwork/nec_fip80b5r/segment0.svg | 5 +
artwork/nec_fip80b5r/segment1.svg | 5 +
artwork/nec_fip80b5r/segment10.svg | 5 +
artwork/nec_fip80b5r/segment11.svg | 5 +
artwork/nec_fip80b5r/segment12.svg | 5 +
artwork/nec_fip80b5r/segment13.svg | 5 +
artwork/nec_fip80b5r/segment14.svg | 5 +
artwork/nec_fip80b5r/segment15.svg | 5 +
artwork/nec_fip80b5r/segment2.svg | 5 +
artwork/nec_fip80b5r/segment3.svg | 5 +
artwork/nec_fip80b5r/segment4.svg | 5 +
artwork/nec_fip80b5r/segment5.svg | 5 +
artwork/nec_fip80b5r/segment6.svg | 5 +
artwork/nec_fip80b5r/segment7.svg | 5 +
artwork/nec_fip80b5r/segment8.svg | 5 +
artwork/nec_fip80b5r/segment9.svg | 5 +
src/mame/layout/nec_fip80b5r.lay | 318 ++++++++++++-----------------
17 files changed, 207 insertions(+), 191 deletions(-)
create mode 100644 artwork/nec_fip80b5r/segment0.svg
create mode 100644 artwork/nec_fip80b5r/segment1.svg
create mode 100644 artwork/nec_fip80b5r/segment10.svg
create mode 100644 artwork/nec_fip80b5r/segment11.svg
create mode 100644 artwork/nec_fip80b5r/segment12.svg
create mode 100644 artwork/nec_fip80b5r/segment13.svg
create mode 100644 artwork/nec_fip80b5r/segment14.svg
create mode 100644 artwork/nec_fip80b5r/segment15.svg
create mode 100644 artwork/nec_fip80b5r/segment2.svg
create mode 100644 artwork/nec_fip80b5r/segment3.svg
create mode 100644 artwork/nec_fip80b5r/segment4.svg
create mode 100644 artwork/nec_fip80b5r/segment5.svg
create mode 100644 artwork/nec_fip80b5r/segment6.svg
create mode 100644 artwork/nec_fip80b5r/segment7.svg
create mode 100644 artwork/nec_fip80b5r/segment8.svg
create mode 100644 artwork/nec_fip80b5r/segment9.svg
diff --git a/artwork/nec_fip80b5r/segment0.svg b/artwork/nec_fip80b5r/segment0.svg
new file mode 100644
index 0000000000000..a4627505c1d20
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment0.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment1.svg b/artwork/nec_fip80b5r/segment1.svg
new file mode 100644
index 0000000000000..de8cf5235902a
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment1.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment10.svg b/artwork/nec_fip80b5r/segment10.svg
new file mode 100644
index 0000000000000..9be5a368e2c2a
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment10.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment11.svg b/artwork/nec_fip80b5r/segment11.svg
new file mode 100644
index 0000000000000..c0cf7b2c5840b
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment11.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment12.svg b/artwork/nec_fip80b5r/segment12.svg
new file mode 100644
index 0000000000000..8b40cdb43fec9
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment12.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment13.svg b/artwork/nec_fip80b5r/segment13.svg
new file mode 100644
index 0000000000000..bc60b895be731
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment13.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment14.svg b/artwork/nec_fip80b5r/segment14.svg
new file mode 100644
index 0000000000000..e3832da116428
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment14.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment15.svg b/artwork/nec_fip80b5r/segment15.svg
new file mode 100644
index 0000000000000..6d99aa1675ea9
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment15.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment2.svg b/artwork/nec_fip80b5r/segment2.svg
new file mode 100644
index 0000000000000..84b0bb1f6da5e
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment2.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment3.svg b/artwork/nec_fip80b5r/segment3.svg
new file mode 100644
index 0000000000000..43d697b8c51b6
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment3.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment4.svg b/artwork/nec_fip80b5r/segment4.svg
new file mode 100644
index 0000000000000..555cdf0aaaf01
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment4.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment5.svg b/artwork/nec_fip80b5r/segment5.svg
new file mode 100644
index 0000000000000..8f804b469cac4
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment5.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment6.svg b/artwork/nec_fip80b5r/segment6.svg
new file mode 100644
index 0000000000000..6b758af407973
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment6.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment7.svg b/artwork/nec_fip80b5r/segment7.svg
new file mode 100644
index 0000000000000..a73d458059e92
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment7.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment8.svg b/artwork/nec_fip80b5r/segment8.svg
new file mode 100644
index 0000000000000..79502c9dc0293
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment8.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/artwork/nec_fip80b5r/segment9.svg b/artwork/nec_fip80b5r/segment9.svg
new file mode 100644
index 0000000000000..00591729415f1
--- /dev/null
+++ b/artwork/nec_fip80b5r/segment9.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/src/mame/layout/nec_fip80b5r.lay b/src/mame/layout/nec_fip80b5r.lay
index 6cc1fc4638689..96e0ae8bdd814 100644
--- a/src/mame/layout/nec_fip80b5r.lay
+++ b/src/mame/layout/nec_fip80b5r.lay
@@ -9,197 +9,133 @@ license:CC0-1.0
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
-
-
-
-
-
- ]]>
-
-
-
- ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From ed032f04807c1b6286d11532c721cf93d1a85d3a Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Sat, 6 Sep 2025 09:07:48 +0100
Subject: [PATCH 06/22] Document the numbering of the segments in the NEC
FIP80B5R display.
---
src/mame/ensoniq/esqvfd.cpp | 35 ++++++++++++++++++--------------
src/mame/layout/nec_fip80b5r.lay | 22 ++++++++++++++++++++
2 files changed, 42 insertions(+), 15 deletions(-)
diff --git a/src/mame/ensoniq/esqvfd.cpp b/src/mame/ensoniq/esqvfd.cpp
index 0b95c2fea0a7c..7b97ba6f31f91 100644
--- a/src/mame/ensoniq/esqvfd.cpp
+++ b/src/mame/ensoniq/esqvfd.cpp
@@ -121,23 +121,28 @@ static const uint16_t font[]=
/**
* The font used by the VFX family on the NEC FIP80B5R display, including
* VFX-family-specific characters sich as digits followed by a period / decimal point.
- * Arranged to match this, fairly arbitrarily chosen, order of segments:
*
- * ---- 0 ----
- * |\ | /|
- * | \ | / |
- * 5 4 3 2 1
- * | \ | / |
- * | \|/ |
- * --7-- --6--
- * | /|\ |
- * | / | \ |
- * 12 11 10 9 8
- * | / | \ |
- * |/ | \|
- * ----13----- *14
+ * The segments are numbered in order of the pins 58 .. 73 that drive
+ * the top row of character segments, as shown on a scan of the Ensoniq ESQ1
+ * display board at http://buchty.net/ensoniq/files/schematics/esq1-display.jpg
+ * This is the same order as in the [nec_fip80b5r.lay] layout, so they can be
+ * used immediately without need for bit order conversion.
*
- * -----15-----
+ * ==== 0 ====
+ * |\ | /|
+ * | \ | / |
+ * 5 4 3 2 1
+ * | \ | / |
+ * | \|/ |
+ * ==7== ==6==
+ * | /|\ |
+ * | / | \ |
+ * 12 11 10 9 8
+ * | / | \ |
+ * |/ | \|
+ * ==== 13 === #14
+ *
+ * ===== 15 ====
*
*/
static const uint16_t font_vfx[] = {
diff --git a/src/mame/layout/nec_fip80b5r.lay b/src/mame/layout/nec_fip80b5r.lay
index 96e0ae8bdd814..791fd43e5ea18 100644
--- a/src/mame/layout/nec_fip80b5r.lay
+++ b/src/mame/layout/nec_fip80b5r.lay
@@ -5,6 +5,28 @@ license:CC0-1.0
+
+
From c7d28367aef903b38ba2617d3211246ae6907846 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Sat, 6 Sep 2025 10:12:49 +0100
Subject: [PATCH 07/22] Add documentation and license (CC0) to the NEC FIP80B5R
segments.
---
artwork/nec_fip80b5r/README.md | 29 +++++++++++++++++++++++++++++
artwork/nec_fip80b5r/segment0.svg | 1 +
artwork/nec_fip80b5r/segment1.svg | 1 +
artwork/nec_fip80b5r/segment10.svg | 1 +
artwork/nec_fip80b5r/segment11.svg | 1 +
artwork/nec_fip80b5r/segment12.svg | 1 +
artwork/nec_fip80b5r/segment13.svg | 1 +
artwork/nec_fip80b5r/segment14.svg | 1 +
artwork/nec_fip80b5r/segment15.svg | 1 +
artwork/nec_fip80b5r/segment2.svg | 1 +
artwork/nec_fip80b5r/segment3.svg | 1 +
artwork/nec_fip80b5r/segment4.svg | 1 +
artwork/nec_fip80b5r/segment5.svg | 1 +
artwork/nec_fip80b5r/segment6.svg | 1 +
artwork/nec_fip80b5r/segment7.svg | 1 +
artwork/nec_fip80b5r/segment8.svg | 1 +
artwork/nec_fip80b5r/segment9.svg | 1 +
17 files changed, 45 insertions(+)
create mode 100644 artwork/nec_fip80b5r/README.md
diff --git a/artwork/nec_fip80b5r/README.md b/artwork/nec_fip80b5r/README.md
new file mode 100644
index 0000000000000..704c2708df191
--- /dev/null
+++ b/artwork/nec_fip80b5r/README.md
@@ -0,0 +1,29 @@
+# **NEC FIP90B5R Segments** #
+
+These segments were traced from photos of an NEC FIP80B5R display,
+as used in several Ensoniq keyboards including the ESQ1, the VFX and TS families.
+
+ The segments are numbered in order of the pins 58 .. 73 that drive
+ the top row of character segments, as shown on [this scan of the Ensoniq ESQ1
+ display board](http://buchty.net/ensoniq/files/schematics/esq1-display.jpg), as follows:
+
+ ```
+ ==== 0 ====
+ |\ | /|
+ | \ | / |
+ 5 4 3 2 1
+ | \ | / |
+ | \|/ |
+ ==7== ==6==
+ | /|\ |
+ | / | \ |
+ 12 11 10 9 8
+ | / | \ |
+ |/ | \|
+ ==== 13 === #14
+
+ ===== 15 ====
+```
+
+They are licensed as [CC0](https://creativecommons.org/publicdomain/zero/1.0/) by their author,
+Christian Brunschen. Revision 6 September 2025.
\ No newline at end of file
diff --git a/artwork/nec_fip80b5r/segment0.svg b/artwork/nec_fip80b5r/segment0.svg
index a4627505c1d20..0120fe9fa5524 100644
--- a/artwork/nec_fip80b5r/segment0.svg
+++ b/artwork/nec_fip80b5r/segment0.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment1.svg b/artwork/nec_fip80b5r/segment1.svg
index de8cf5235902a..f0f65847961e8 100644
--- a/artwork/nec_fip80b5r/segment1.svg
+++ b/artwork/nec_fip80b5r/segment1.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment10.svg b/artwork/nec_fip80b5r/segment10.svg
index 9be5a368e2c2a..acee462d87f45 100644
--- a/artwork/nec_fip80b5r/segment10.svg
+++ b/artwork/nec_fip80b5r/segment10.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment11.svg b/artwork/nec_fip80b5r/segment11.svg
index c0cf7b2c5840b..077e10a83deca 100644
--- a/artwork/nec_fip80b5r/segment11.svg
+++ b/artwork/nec_fip80b5r/segment11.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment12.svg b/artwork/nec_fip80b5r/segment12.svg
index 8b40cdb43fec9..377472f795096 100644
--- a/artwork/nec_fip80b5r/segment12.svg
+++ b/artwork/nec_fip80b5r/segment12.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment13.svg b/artwork/nec_fip80b5r/segment13.svg
index bc60b895be731..a18dc6be5621b 100644
--- a/artwork/nec_fip80b5r/segment13.svg
+++ b/artwork/nec_fip80b5r/segment13.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment14.svg b/artwork/nec_fip80b5r/segment14.svg
index e3832da116428..48c9259fb0cc5 100644
--- a/artwork/nec_fip80b5r/segment14.svg
+++ b/artwork/nec_fip80b5r/segment14.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment15.svg b/artwork/nec_fip80b5r/segment15.svg
index 6d99aa1675ea9..fb34482b71d8c 100644
--- a/artwork/nec_fip80b5r/segment15.svg
+++ b/artwork/nec_fip80b5r/segment15.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment2.svg b/artwork/nec_fip80b5r/segment2.svg
index 84b0bb1f6da5e..761f773340805 100644
--- a/artwork/nec_fip80b5r/segment2.svg
+++ b/artwork/nec_fip80b5r/segment2.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment3.svg b/artwork/nec_fip80b5r/segment3.svg
index 43d697b8c51b6..6236335a5dddf 100644
--- a/artwork/nec_fip80b5r/segment3.svg
+++ b/artwork/nec_fip80b5r/segment3.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment4.svg b/artwork/nec_fip80b5r/segment4.svg
index 555cdf0aaaf01..a8959fd4b72cd 100644
--- a/artwork/nec_fip80b5r/segment4.svg
+++ b/artwork/nec_fip80b5r/segment4.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment5.svg b/artwork/nec_fip80b5r/segment5.svg
index 8f804b469cac4..a4afa3ec9ca26 100644
--- a/artwork/nec_fip80b5r/segment5.svg
+++ b/artwork/nec_fip80b5r/segment5.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment6.svg b/artwork/nec_fip80b5r/segment6.svg
index 6b758af407973..e71f9d17be4be 100644
--- a/artwork/nec_fip80b5r/segment6.svg
+++ b/artwork/nec_fip80b5r/segment6.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment7.svg b/artwork/nec_fip80b5r/segment7.svg
index a73d458059e92..b42133a0c8d46 100644
--- a/artwork/nec_fip80b5r/segment7.svg
+++ b/artwork/nec_fip80b5r/segment7.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment8.svg b/artwork/nec_fip80b5r/segment8.svg
index 79502c9dc0293..3bdc8f28d0916 100644
--- a/artwork/nec_fip80b5r/segment8.svg
+++ b/artwork/nec_fip80b5r/segment8.svg
@@ -1,5 +1,6 @@
+
diff --git a/artwork/nec_fip80b5r/segment9.svg b/artwork/nec_fip80b5r/segment9.svg
index 00591729415f1..663da9f5a4af2 100644
--- a/artwork/nec_fip80b5r/segment9.svg
+++ b/artwork/nec_fip80b5r/segment9.svg
@@ -1,5 +1,6 @@
+
From ffc67f0b82df49f434061c203c05d3450b587ead Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Sun, 7 Sep 2025 06:43:43 +0100
Subject: [PATCH 08/22] Remove a pair of unused variables.
---
src/mame/ensoniq/esqpanel.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/mame/ensoniq/esqpanel.h b/src/mame/ensoniq/esqpanel.h
index a5a557b8cd20d..9ab819fc98f7c 100644
--- a/src/mame/ensoniq/esqpanel.h
+++ b/src/mame/ensoniq/esqpanel.h
@@ -134,7 +134,6 @@ class esqpanel2x40_vfx_device : public esqpanel_device {
private:
int m_cursx = 0, m_cursy = 0;
int m_savedx = 0, m_savedy = 0;
- int const m_rows = 0, m_cols = 0;
uint8_t m_curattr = 0;
emu_timer *m_blink_timer = nullptr;
From 27ec234b8e7fa86da2310074a4c82758fa580ebe Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Sun, 7 Sep 2025 08:01:03 +0100
Subject: [PATCH 09/22] Pass the correct device type.
---
src/mame/ensoniq/esqvfd.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mame/ensoniq/esqvfd.cpp b/src/mame/ensoniq/esqvfd.cpp
index 7b97ba6f31f91..be1df66f53917 100644
--- a/src/mame/ensoniq/esqvfd.cpp
+++ b/src/mame/ensoniq/esqvfd.cpp
@@ -403,7 +403,7 @@ esq2x40_device::esq2x40_device(const machine_config &mconfig, const char *tag, d
*/
esq2x40_vfx_device::esq2x40_vfx_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))
+ esqvfd_device(mconfig, ESQ2X40_VFX, tag, owner, clock, make_dimensions<2, 40>(*this))
{
}
From 11fc87326362c7698c3ad6b1b4f1d9935c2da94a Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Tue, 9 Sep 2025 10:01:34 +0100
Subject: [PATCH 10/22] Replace the nec_fip80b5r layout with an esq2by40_vfx
layout that uses an led14seg element, a doit element and an underline one.
led14segsc would add a redundant and inaccurate "comma tail".
---
src/mame/ensoniq/esqvfd.cpp | 224 ++++++++++++++-----------------
src/mame/layout/esq2by40_vfx.lay | 61 +++++++++
src/mame/layout/nec_fip80b5r.lay | 180 -------------------------
3 files changed, 162 insertions(+), 303 deletions(-)
create mode 100644 src/mame/layout/esq2by40_vfx.lay
delete mode 100644 src/mame/layout/nec_fip80b5r.lay
diff --git a/src/mame/ensoniq/esqvfd.cpp b/src/mame/ensoniq/esqvfd.cpp
index be1df66f53917..1f4e3d7b14bc6 100644
--- a/src/mame/ensoniq/esqvfd.cpp
+++ b/src/mame/ensoniq/esqvfd.cpp
@@ -10,7 +10,7 @@
#include "esq1by22.lh"
#include "esq2by40.lh"
-#include "nec_fip80b5r.lh"
+#include "esq2by40_vfx.lh"
DEFINE_DEVICE_TYPE(ESQ1X22, esq1x22_device, "esq1x22", "Ensoniq 1x22 VFD")
DEFINE_DEVICE_TYPE(ESQ2X40, esq2x40_device, "esq2x40", "Ensoniq 2x40 VFD")
@@ -119,129 +119,107 @@ static const uint16_t font[]=
};
/**
- * The font used by the VFX family on the NEC FIP80B5R display, including
- * VFX-family-specific characters sich as digits followed by a period / decimal point.
- *
- * The segments are numbered in order of the pins 58 .. 73 that drive
- * the top row of character segments, as shown on a scan of the Ensoniq ESQ1
- * display board at http://buchty.net/ensoniq/files/schematics/esq1-display.jpg
- * This is the same order as in the [nec_fip80b5r.lay] layout, so they can be
- * used immediately without need for bit order conversion.
- *
- * ==== 0 ====
- * |\ | /|
- * | \ | / |
- * 5 4 3 2 1
- * | \ | / |
- * | \|/ |
- * ==7== ==6==
- * | /|\ |
- * | / | \ |
- * 12 11 10 9 8
- * | / | \ |
- * |/ | \|
- * ==== 13 === #14
- *
- * ===== 15 ====
- *
+ * 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
- 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
+ 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) :
@@ -446,7 +424,7 @@ void esq2x40_vfx_device::update_display()
void esq2x40_vfx_device::device_add_mconfig(machine_config &config)
{
- config.set_default_layout(layout_nec_fip80b5r);
+ config.set_default_layout(layout_esq2by40_vfx);
}
void esq2x40_vfx_device::set_blink_on(bool blink_on) {
diff --git a/src/mame/layout/esq2by40_vfx.lay b/src/mame/layout/esq2by40_vfx.lay
new file mode 100644
index 0000000000000..e19f9a47b5c61
--- /dev/null
+++ b/src/mame/layout/esq2by40_vfx.lay
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mame/layout/nec_fip80b5r.lay b/src/mame/layout/nec_fip80b5r.lay
deleted file mode 100644
index 791fd43e5ea18..0000000000000
--- a/src/mame/layout/nec_fip80b5r.lay
+++ /dev/null
@@ -1,180 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 6b11fd9a55b3b3056ad5db5df73025815b44967e Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Tue, 9 Sep 2025 13:02:18 +0100
Subject: [PATCH 11/22] Also remove the no-longer--used NEC FIP80B5R segment
SVG files.
---
artwork/nec_fip80b5r/README.md | 29 -----------------------------
artwork/nec_fip80b5r/segment0.svg | 6 ------
artwork/nec_fip80b5r/segment1.svg | 6 ------
artwork/nec_fip80b5r/segment10.svg | 6 ------
artwork/nec_fip80b5r/segment11.svg | 6 ------
artwork/nec_fip80b5r/segment12.svg | 6 ------
artwork/nec_fip80b5r/segment13.svg | 6 ------
artwork/nec_fip80b5r/segment14.svg | 6 ------
artwork/nec_fip80b5r/segment15.svg | 6 ------
artwork/nec_fip80b5r/segment2.svg | 6 ------
artwork/nec_fip80b5r/segment3.svg | 6 ------
artwork/nec_fip80b5r/segment4.svg | 6 ------
artwork/nec_fip80b5r/segment5.svg | 6 ------
artwork/nec_fip80b5r/segment6.svg | 6 ------
artwork/nec_fip80b5r/segment7.svg | 6 ------
artwork/nec_fip80b5r/segment8.svg | 6 ------
artwork/nec_fip80b5r/segment9.svg | 6 ------
17 files changed, 125 deletions(-)
delete mode 100644 artwork/nec_fip80b5r/README.md
delete mode 100644 artwork/nec_fip80b5r/segment0.svg
delete mode 100644 artwork/nec_fip80b5r/segment1.svg
delete mode 100644 artwork/nec_fip80b5r/segment10.svg
delete mode 100644 artwork/nec_fip80b5r/segment11.svg
delete mode 100644 artwork/nec_fip80b5r/segment12.svg
delete mode 100644 artwork/nec_fip80b5r/segment13.svg
delete mode 100644 artwork/nec_fip80b5r/segment14.svg
delete mode 100644 artwork/nec_fip80b5r/segment15.svg
delete mode 100644 artwork/nec_fip80b5r/segment2.svg
delete mode 100644 artwork/nec_fip80b5r/segment3.svg
delete mode 100644 artwork/nec_fip80b5r/segment4.svg
delete mode 100644 artwork/nec_fip80b5r/segment5.svg
delete mode 100644 artwork/nec_fip80b5r/segment6.svg
delete mode 100644 artwork/nec_fip80b5r/segment7.svg
delete mode 100644 artwork/nec_fip80b5r/segment8.svg
delete mode 100644 artwork/nec_fip80b5r/segment9.svg
diff --git a/artwork/nec_fip80b5r/README.md b/artwork/nec_fip80b5r/README.md
deleted file mode 100644
index 704c2708df191..0000000000000
--- a/artwork/nec_fip80b5r/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# **NEC FIP90B5R Segments** #
-
-These segments were traced from photos of an NEC FIP80B5R display,
-as used in several Ensoniq keyboards including the ESQ1, the VFX and TS families.
-
- The segments are numbered in order of the pins 58 .. 73 that drive
- the top row of character segments, as shown on [this scan of the Ensoniq ESQ1
- display board](http://buchty.net/ensoniq/files/schematics/esq1-display.jpg), as follows:
-
- ```
- ==== 0 ====
- |\ | /|
- | \ | / |
- 5 4 3 2 1
- | \ | / |
- | \|/ |
- ==7== ==6==
- | /|\ |
- | / | \ |
- 12 11 10 9 8
- | / | \ |
- |/ | \|
- ==== 13 === #14
-
- ===== 15 ====
-```
-
-They are licensed as [CC0](https://creativecommons.org/publicdomain/zero/1.0/) by their author,
-Christian Brunschen. Revision 6 September 2025.
\ No newline at end of file
diff --git a/artwork/nec_fip80b5r/segment0.svg b/artwork/nec_fip80b5r/segment0.svg
deleted file mode 100644
index 0120fe9fa5524..0000000000000
--- a/artwork/nec_fip80b5r/segment0.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment1.svg b/artwork/nec_fip80b5r/segment1.svg
deleted file mode 100644
index f0f65847961e8..0000000000000
--- a/artwork/nec_fip80b5r/segment1.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment10.svg b/artwork/nec_fip80b5r/segment10.svg
deleted file mode 100644
index acee462d87f45..0000000000000
--- a/artwork/nec_fip80b5r/segment10.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment11.svg b/artwork/nec_fip80b5r/segment11.svg
deleted file mode 100644
index 077e10a83deca..0000000000000
--- a/artwork/nec_fip80b5r/segment11.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment12.svg b/artwork/nec_fip80b5r/segment12.svg
deleted file mode 100644
index 377472f795096..0000000000000
--- a/artwork/nec_fip80b5r/segment12.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment13.svg b/artwork/nec_fip80b5r/segment13.svg
deleted file mode 100644
index a18dc6be5621b..0000000000000
--- a/artwork/nec_fip80b5r/segment13.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment14.svg b/artwork/nec_fip80b5r/segment14.svg
deleted file mode 100644
index 48c9259fb0cc5..0000000000000
--- a/artwork/nec_fip80b5r/segment14.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment15.svg b/artwork/nec_fip80b5r/segment15.svg
deleted file mode 100644
index fb34482b71d8c..0000000000000
--- a/artwork/nec_fip80b5r/segment15.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment2.svg b/artwork/nec_fip80b5r/segment2.svg
deleted file mode 100644
index 761f773340805..0000000000000
--- a/artwork/nec_fip80b5r/segment2.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment3.svg b/artwork/nec_fip80b5r/segment3.svg
deleted file mode 100644
index 6236335a5dddf..0000000000000
--- a/artwork/nec_fip80b5r/segment3.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment4.svg b/artwork/nec_fip80b5r/segment4.svg
deleted file mode 100644
index a8959fd4b72cd..0000000000000
--- a/artwork/nec_fip80b5r/segment4.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment5.svg b/artwork/nec_fip80b5r/segment5.svg
deleted file mode 100644
index a4afa3ec9ca26..0000000000000
--- a/artwork/nec_fip80b5r/segment5.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment6.svg b/artwork/nec_fip80b5r/segment6.svg
deleted file mode 100644
index e71f9d17be4be..0000000000000
--- a/artwork/nec_fip80b5r/segment6.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment7.svg b/artwork/nec_fip80b5r/segment7.svg
deleted file mode 100644
index b42133a0c8d46..0000000000000
--- a/artwork/nec_fip80b5r/segment7.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment8.svg b/artwork/nec_fip80b5r/segment8.svg
deleted file mode 100644
index 3bdc8f28d0916..0000000000000
--- a/artwork/nec_fip80b5r/segment8.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/artwork/nec_fip80b5r/segment9.svg b/artwork/nec_fip80b5r/segment9.svg
deleted file mode 100644
index 663da9f5a4af2..0000000000000
--- a/artwork/nec_fip80b5r/segment9.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
From e28d78ea83499ae6a9810c626e39acab0644f854 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Thu, 11 Sep 2025 15:40:02 +0100
Subject: [PATCH 12/22] Remove the web interface, and any dependency on the
http server, from the Ensoniq keyboards.
Instead, add a new external panel class, that runs a simple asio-based TCP socket server, heavily based on cps2comm, that will serve one client at a time. This uses the existing comm_local{host,port} flags to specify where to listen for connections.
---
src/mame/ensoniq/esqpanel.cpp | 666 +++--------------
src/mame/ensoniq/esqpanel.h | 46 +-
src/mame/ensoniq/esqvfd.h | 6 +-
src/mame/ensoniq/extpanel.cpp | 563 +++++++++++++++
src/mame/ensoniq/extpanel.h | 144 ++++
web/esqpanel/vfx/FrontPanel.html | 25 -
web/esqpanel/vfx/FrontPanel.js | 1163 ------------------------------
7 files changed, 850 insertions(+), 1763 deletions(-)
create mode 100644 src/mame/ensoniq/extpanel.cpp
create mode 100644 src/mame/ensoniq/extpanel.h
delete mode 100644 web/esqpanel/vfx/FrontPanel.html
delete mode 100644 web/esqpanel/vfx/FrontPanel.js
diff --git a/src/mame/ensoniq/esqpanel.cpp b/src/mame/ensoniq/esqpanel.cpp
index 108479d6828f3..1b0d7f13c36f2 100644
--- a/src/mame/ensoniq/esqpanel.cpp
+++ b/src/mame/ensoniq/esqpanel.cpp
@@ -5,8 +5,8 @@
*/
#include "emu.h"
#include "esqpanel.h"
+#include "extpanel.h"
-#include "http.h"
#include "main.h"
//**************************************************************************
@@ -20,449 +20,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(const std::string &s, http_manager::websocket_connection_ptr conn = nullptr)
- {
- std::lock_guard lock(m_mutex);
-
- if (conn) {
- // sending this only to a single connection that needs a large update
- send(conn, s);
- } else {
- for (const auto &iter: m_panels)
- {
- external_panel_ptr panel = iter.second;
- if (panel->send_display_data())
- {
- send(iter.first, s);
- }
- }
- }
- }
-
- void clear_display(http_manager::websocket_connection_ptr conn = nullptr)
- {
- send("DX", conn);
- }
-
- void set_char(uint8_t row, uint8_t column, uint8_t c, bool underline, bool blink, http_manager::websocket_connection_ptr conn = nullptr)
- {
- std::ostringstream os;
- os << "DC" << static_cast(row) << " " << static_cast(column) << " " << static_cast(c) << " " << underline << " " << blink;
- send(os.str(), conn);
- }
-
- void set_light(uint8_t light, uint8_t state, http_manager::websocket_connection_ptr conn = nullptr)
- {
- std::ostringstream os;
- os << "L" << static_cast(light) << " " << static_cast(state);
- send(os.str(), conn);
- }
-
- void button_down(uint8_t button, http_manager::websocket_connection_ptr conn = nullptr)
- {
- std::ostringstream os;
- os << "BD " << static_cast(button);
- send(os.str(), conn);
- }
-
- void button_up(uint8_t button, http_manager::websocket_connection_ptr conn = nullptr)
- {
- std::ostringstream os;
- os << "BU " << static_cast(button);
- send(os.str(), conn);
- }
-
- void set_button(uint8_t button, bool pressed, http_manager::websocket_connection_ptr conn = nullptr)
- {
- if (pressed)
- {
- button_down(button, conn);
- }
- else
- {
- button_up(button, conn);
- }
- }
-
- void set_analog_value(uint8_t channel, int value, http_manager::websocket_connection_ptr conn = nullptr)
- {
- std::ostringstream os;
- os << "A" << static_cast(channel) << " " << value;
- send(os.str(), conn);
- }
-
- void set_blink_phase(uint8_t phase)
- {
- std::ostringstream os;
- os << "P" << static_cast(phase);
- send(os.str());
- }
-
- 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_display_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_send_display_contents(std::function fun)
- {
- m_send_display_contents = fun;
- }
-
- void set_send_analog_values(std::function fun)
- {
- m_send_analog_values = fun;
- }
-
- void set_send_button_states(std::function fun)
- {
- m_send_button_states = fun;
- }
-
- 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_display_contents(http_manager::websocket_connection_ptr connection)
- {
- if (m_send_display_contents)
- {
- m_send_display_contents(connection);
- }
- }
-
- void send_analog_values(http_manager::websocket_connection_ptr connection)
- {
- if (m_send_analog_values)
- {
- m_send_analog_values(connection);
- }
- }
-
- void send_button_states(http_manager::websocket_connection_ptr connection)
- {
- if (m_send_button_states)
- {
- m_send_button_states(connection);
- }
- }
-
- 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;
-
- // callbacks to send
- std::function m_send_display_contents;
- std::function m_send_button_states; // also includes button light states. really
- std::function m_send_analog_values;
-
- std::map m_template_values;
- };
-
-} // namespace esqpanel
-
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
@@ -487,6 +44,7 @@ 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_external_panel(*this, "esq_external_panel"),
m_light_states(0x3f), // maximum number of lights
m_write_tx(*this),
m_write_analog(*this)
@@ -501,22 +59,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()) {
- using namespace std::placeholders;
-
- 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_send_display_contents(std::bind(&esqpanel_device::send_display_contents, this, _1));
- m_external_panel_server->set_send_analog_values(std::bind(&esqpanel_device::send_analog_values, this, _1));
- m_external_panel_server->set_send_button_states(std::bind(&esqpanel_device::send_button_states, this, _1));
-
- 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);
}
@@ -526,6 +70,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);
@@ -533,18 +79,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
//-------------------------------------------------
@@ -552,9 +108,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
@@ -562,11 +116,11 @@ 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) printf("Got %02x from motherboard (second %s)\n", data, m_expect_calibration_second_byte ? "yes" : "no");
send_to_display(data);
- if (m_bCalibSecondByte)
+ if (m_expect_calibration_second_byte)
{
// printf("second byte is %02x\n", data);
if (data == 0xfd) // calibration request
@@ -574,9 +128,9 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte
// printf("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
@@ -596,25 +150,25 @@ 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;
+ m_light_states[light_number] = (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;
+ // printf("Setting light %d to %s\n", light_number, lightState == 3 ? "Blink" : lightState == 2 ? "On" : "Off");
+ 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
{
@@ -682,52 +236,48 @@ 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);
+}
+
+void esqpanel_device::set_button(uint8_t button, bool pressed)
+{
+ uint8_t sendme = (pressed ? 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);
+ if (pressed)
{
- 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);
-
- // record the button pressed state
- if (down)
- {
- m_pressed_buttons.insert(button);
- }
- else
- {
- m_pressed_buttons.erase(button);
- }
- } 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);
- }
- }
+ 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:
+ 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 */
@@ -752,7 +302,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")
@@ -765,6 +314,7 @@ esqpanel2x40_device::esqpanel2x40_device(const machine_config &mconfig, const ch
void esqpanel2x40_vfx_device::device_add_mconfig(machine_config &config)
{
ESQ2X40_VFX(config, m_vfd, 60);
+ ESQ_EXTERNAL_PANEL_DEVICE(config, m_external_panel, 0);
}
esqpanel2x40_vfx_device::esqpanel2x40_vfx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
@@ -779,9 +329,9 @@ void esqpanel2x40_vfx_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) printf("Got %02x from motherboard (second %s)\n", data, m_expect_calibration_second_byte ? "yes" : "no");
- if (m_bCalibSecondByte)
+ if (m_expect_calibration_second_byte)
{
// printf("second byte is %02x\n", data);
if (data == 0xfd) // calibration request
@@ -789,9 +339,9 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
// printf("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
@@ -811,26 +361,24 @@ void esqpanel2x40_vfx_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;
- m_external_panel_server->set_light(lightNumber, m_light_states[lightNumber]);
-
- // 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;
+ auto light_state = (data & 0xc0) >> 6;
+ m_light_states[light_number] = light_state;
+ m_external_panel->set_light(light_number, light_state);
+ 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
{
@@ -862,7 +410,7 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
case 0xd6: // clear screen
m_vfd->clear();
- m_external_panel_server->clear_display();
+ m_external_panel->clear_display();
m_cursx = m_cursy = 0;
m_curattr = 0;
break;
@@ -878,6 +426,13 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
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:
// printf("Unknown control code %02x\n", data);
break;
@@ -888,7 +443,8 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
if ((data >= 0x20) && (data <= 0x5f))
{
m_vfd->set_char(m_cursy, m_cursx, data - ' ', m_curattr);
- m_external_panel_server->set_char(m_cursy, m_cursx, data - ' ', (m_curattr * AT_UNDERLINE) != 0, (m_curattr & AT_BLINK) != 0);
+ m_external_panel->set_char(m_cursy, m_cursx, data - ' ', m_curattr);
+
m_cursx++;
if (m_cursx >= 39)
@@ -903,13 +459,16 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
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_server->set_blink_phase(m_blink_phase);
+ m_external_panel->set_blink_phase(m_blink_phase);
}
void esqpanel2x40_vfx_device::device_start()
{
esqpanel_device::device_start();
+ m_external_panel->set_keyboard(owner()->shortname());
+ m_external_panel->set_version("1");
+
m_blink_timer = timer_alloc(FUNC(esqpanel2x40_vfx_device::update_blink), this);
m_blink_timer->enable(false);
}
@@ -927,37 +486,50 @@ void esqpanel2x40_vfx_device::device_reset()
}
}
-void esqpanel2x40_vfx_device::send_display_contents(http_manager::websocket_connection_ptr conn)
+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++)
{
- auto attr = m_vfd->get_attr(row, column);
- m_external_panel_server->set_char(row, column, m_vfd->get_char(row, column), (attr & AT_UNDERLINE) != 0, (attr & AT_BLINK) != 0);
+ 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(http_manager::websocket_connection_ptr conn)
+void esqpanel2x40_vfx_device::send_analog_values()
{
// TODO(cbrunschen): read analog values from the emulated keyboard and send them to the panel(s)
}
-void esqpanel2x40_vfx_device::send_button_states(http_manager::websocket_connection_ptr conn)
+void esqpanel2x40_vfx_device::send_button_states()
{
- // send the button statuses
+ std::vector> button_states;
+ button_states.reserve(64);
for (int i = 0; i < 64; i++)
{
- bool pressed = m_pressed_buttons.find(i) != m_pressed_buttons.end();
- m_external_panel_server->set_button(i, pressed);
+ 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);
+}
- // and the states for the lights
+void esqpanel2x40_vfx_device::send_light_states()
+{
+ std::vector> light_states;
+ light_states.reserve(16);
for (int i = 0; i < 16; i++)
{
- m_external_panel_server->set_light(i, m_light_states[i]);
+ light_states.emplace_back(std::make_pair(i, m_light_states[i]));
}
+ m_external_panel->set_light_states(light_states);
}
diff --git a/src/mame/ensoniq/esqpanel.h b/src/mame/ensoniq/esqpanel.h
index 9ab819fc98f7c..70eb6d4173f2f 100644
--- a/src/mame/ensoniq/esqpanel.h
+++ b/src/mame/ensoniq/esqpanel.h
@@ -7,6 +7,7 @@
#include "esqvfd.h"
#include "esqlcd.h"
+#include "extpanel.h"
#include "diserial.h"
#include "http.h"
@@ -21,10 +22,6 @@
// ======================> esqpanel_device
-namespace esqpanel {
- class external_panel_server;
-}
-
class esqpanel_device : public device_t, public device_serial_interface
{
public:
@@ -32,7 +29,10 @@ class esqpanel_device : public device_t, public device_serial_interface
auto write_analog() { return m_write_analog.bind(); }
void xmit_char(uint8_t data);
+
+ void set_char(int row, int column, uint8_t c, uint8_t attr);
void set_analog_value(offs_t offset, uint16_t value);
+ void set_button(uint8_t button, bool pressed);
protected:
// construction/destruction
@@ -41,6 +41,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
@@ -50,22 +51,21 @@ 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 void send_display_contents(http_manager::websocket_connection_ptr conn) { }
- virtual void send_analog_values(http_manager::websocket_connection_ptr conn) { }
- virtual void send_button_states(http_manager::websocket_connection_ptr conn) { }
+ optional_device m_external_panel;
std::set m_pressed_buttons;
std::vector m_light_states;
bool m_eps_mode = false;
- bool m_bCalibSecondByte = false;
- bool m_bButtonLightSecondByte = false;
-
- esqpanel::external_panel_server *m_external_panel_server;
+ bool m_expect_calibration_second_byte = false;
+ bool m_expect_light_second_byte = false;
private:
static const int XMIT_RING_SIZE = 16;
@@ -76,7 +76,7 @@ class esqpanel_device : public device_t, public device_serial_interface
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 {
@@ -111,23 +111,21 @@ class esqpanel2x40_vfx_device : public esqpanel_device {
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 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 void send_display_contents(http_manager::websocket_connection_ptr conn) override;
- virtual void send_analog_values(http_manager::websocket_connection_ptr conn) override;
- virtual void send_button_states(http_manager::websocket_connection_ptr conn) 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;
required_device m_vfd;
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_UNDERLINE = 0x01;
+ static constexpr uint8_t AT_BLINK = 0x02;
TIMER_CALLBACK_MEMBER(update_blink);
diff --git a/src/mame/ensoniq/esqvfd.h b/src/mame/ensoniq/esqvfd.h
index f24aa19d1560c..acb992561b058 100644
--- a/src/mame/ensoniq/esqvfd.h
+++ b/src/mame/ensoniq/esqvfd.h
@@ -41,10 +41,8 @@ class esqvfd_device : public device_t {
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;
diff --git a/src/mame/ensoniq/extpanel.cpp b/src/mame/ensoniq/extpanel.cpp
new file mode 100644
index 0000000000000..b241f44b31b58
--- /dev/null
+++ b/src/mame/ensoniq/extpanel.cpp
@@ -0,0 +1,563 @@
+// 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
+#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, int value)
+{
+ std::lock_guard lock(m_mutex);
+ if (!m_connected) return;
+ send(util::string_format("A %d %d", channel, value));
+}
+
+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 %d", av.first, av.second);
+ }
+ 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("Handing 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..aa970474d15b0
--- /dev/null
+++ b/src/mame/ensoniq/extpanel.h
@@ -0,0 +1,144 @@
+// license:BSD-3-Clause
+// copyright-holders:C. Brunschen
+#ifndef MAME_ENSONIQ_EXTPANEL_H
+#define MAME_ENSONIQ_EXTPANEL_H
+
+#pragma once
+
+#include "emumem.h"
+
+#include "asio.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, int 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_DEVICE, esq_external_panel_device)
+
+#endif // MAME_ENSONIQ_EXTPANEL_H
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 a24607e3a20f9..0000000000000
--- a/web/esqpanel/vfx/FrontPanel.js
+++ /dev/null
@@ -1,1163 +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 < 0 || 95 < c) ? 0 : my.Display.segmentsByCharacter[c];
- 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.serverUrl = serverUrl;
- try {
- this.connect();
- } catch (e) {
- console.log("Unable to connect to '" + serverUrl + "': " + e);
- }
-
- this.blinkPhase = 0;
- }
-
- my.Panel.prototype.setBlinkPhase = function(blinkPhase) {
- this.blinkPhase = blinkPhase % 4;
- this.display.setBlinkPhase(this.blinkPhase & 2);
- var buttonPhase = (this.blinkPhase & 1) == 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]);
-
- var s = String.fromCharCode.apply(null, data);
- // console.log("Handling message '" + s + "'");
-
- 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 == 'L') {
- // console.log("handling Light state")
- panel.handleLightState(data.slice(1));
- } else if (c == 'P') {
- // console.log("handling blink Phase")
- panel.handleBlinkPhase(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) {
- var c = String.fromCharCode(data[0]);
- if (c == 'X') {
- // Clear the screen
- // console.log("Clearing the screen");
- this.display.clear();
- } else if (c == 'C') {
- // Character data
- var s = String.fromCharCode.apply(null, data.slice(1));
- // console.log("Displaying character: '" + s + "'");
- var parts = s.split(" ");
- if (parts.length == 5) {
- let row = parseInt(parts[0]);
- let column = parseInt(parts[1]);
- let ch = parseInt(parts[2]);
- let underline = parseInt(parts[3]) != 0;;
- let blink = parseInt(parts[4]) != 0;
- this.display.setChar(row, column, ch, underline, blink);
- }
- } else {
- var s = String.fromCharCode.apply(null, data);
- console.log("Unknown display message '" + s + "'");
- }
- }
-
- my.Panel.prototype.handleLightState = function(data) {
- var s = String.fromCharCode.apply(null, data);
- var parts = s.split(" ");
- if (parts.length == 2) {
- let whichLight = parseInt(parts[0]);
- let state = parseInt(parts[1]);
- var button = this.lightButtons[whichLight];
- if (button != null && button instanceof my.Button) {
- if (state == 2) {
- button.setLight(my.Light.ON);
- } else if (state == 3) {
- button.setLight(my.Light.BLINK);
- } else {
- button.setLight(my.Light.OFF);
- }
- }
- }
- }
-
- my.Panel.prototype.handleBlinkPhase = function(data) {
- var s = String.fromCharCode.apply(null, data);
- let phase = parseInt(s);
- this.setBlinkPhase(phase);
- }
-
- 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
From 53a8dafed110ac2a481dc642a28b419560fb4b28 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Thu, 11 Sep 2025 17:39:56 +0100
Subject: [PATCH 13/22] Remove the last reference to the http server from
esqpanel.
---
src/mame/ensoniq/esqpanel.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/mame/ensoniq/esqpanel.h b/src/mame/ensoniq/esqpanel.h
index 70eb6d4173f2f..82c3dbe86dc6b 100644
--- a/src/mame/ensoniq/esqpanel.h
+++ b/src/mame/ensoniq/esqpanel.h
@@ -10,7 +10,6 @@
#include "extpanel.h"
#include "diserial.h"
-#include "http.h"
#include
#include
From a65e04aec3aaf256140b9411509d7b2fe6a9025a Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Thu, 11 Sep 2025 19:01:41 +0100
Subject: [PATCH 14/22] esq_external_panel device declaration & definition
fixes.
---
src/mame/ensoniq/esqpanel.cpp | 2 +-
src/mame/ensoniq/extpanel.cpp | 2 +-
src/mame/ensoniq/extpanel.h | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/mame/ensoniq/esqpanel.cpp b/src/mame/ensoniq/esqpanel.cpp
index 1b0d7f13c36f2..e6ac362fbe9e8 100644
--- a/src/mame/ensoniq/esqpanel.cpp
+++ b/src/mame/ensoniq/esqpanel.cpp
@@ -314,7 +314,7 @@ esqpanel2x40_device::esqpanel2x40_device(const machine_config &mconfig, const ch
void esqpanel2x40_vfx_device::device_add_mconfig(machine_config &config)
{
ESQ2X40_VFX(config, m_vfd, 60);
- ESQ_EXTERNAL_PANEL_DEVICE(config, m_external_panel, 0);
+ ESQ_EXTERNAL_PANEL(config, m_external_panel, 0);
}
esqpanel2x40_vfx_device::esqpanel2x40_vfx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
diff --git a/src/mame/ensoniq/extpanel.cpp b/src/mame/ensoniq/extpanel.cpp
index b241f44b31b58..e9afc67c3eb7a 100644
--- a/src/mame/ensoniq/extpanel.cpp
+++ b/src/mame/ensoniq/extpanel.cpp
@@ -247,7 +247,7 @@ class esq_external_panel_device::context {
#define MT_DISPLAY 'D'
#define MT_INFO 'I'
-DEFINE_DEVICE_TYPE(ESQ_EXTERNAL_PANEL, esq_external_panel_device, "esq_external_panel", "Ensoniq_External_Panel")
+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)
diff --git a/src/mame/ensoniq/extpanel.h b/src/mame/ensoniq/extpanel.h
index aa970474d15b0..c06abea1b3bd6 100644
--- a/src/mame/ensoniq/extpanel.h
+++ b/src/mame/ensoniq/extpanel.h
@@ -139,6 +139,6 @@ class esq_external_panel_device : public device_t
};
// device type definition
-DECLARE_DEVICE_TYPE(ESQ_EXTERNAL_PANEL_DEVICE, esq_external_panel_device)
+DECLARE_DEVICE_TYPE(ESQ_EXTERNAL_PANEL, esq_external_panel_device)
#endif // MAME_ENSONIQ_EXTPANEL_H
From c393f5014b1adf4cbe1377c243d8c1e8c623c11f Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Thu, 11 Sep 2025 19:38:53 +0100
Subject: [PATCH 15/22] Include asio.h only in extpanel.cpp, not in extpanel.h
---
src/mame/ensoniq/extpanel.cpp | 2 ++
src/mame/ensoniq/extpanel.h | 2 --
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/mame/ensoniq/extpanel.cpp b/src/mame/ensoniq/extpanel.cpp
index e9afc67c3eb7a..5bd3902c07d9d 100644
--- a/src/mame/ensoniq/extpanel.cpp
+++ b/src/mame/ensoniq/extpanel.cpp
@@ -9,6 +9,8 @@
#include "extpanel.h"
#include "emuopts.h"
+#include "asio.h"
+
#include
#include
diff --git a/src/mame/ensoniq/extpanel.h b/src/mame/ensoniq/extpanel.h
index c06abea1b3bd6..2ed908f57a98a 100644
--- a/src/mame/ensoniq/extpanel.h
+++ b/src/mame/ensoniq/extpanel.h
@@ -7,8 +7,6 @@
#include "emumem.h"
-#include "asio.h"
-
#include
#include
#include
From a6c31ca76d7b46be4ba11b6cbc4a49b998aca3dd Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Fri, 12 Sep 2025 22:58:47 +0100
Subject: [PATCH 16/22] Correct int -> uint16_t iin set_analog_value.
---
src/mame/ensoniq/extpanel.cpp | 2 +-
src/mame/ensoniq/extpanel.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/mame/ensoniq/extpanel.cpp b/src/mame/ensoniq/extpanel.cpp
index 5bd3902c07d9d..17e26c8a54d68 100644
--- a/src/mame/ensoniq/extpanel.cpp
+++ b/src/mame/ensoniq/extpanel.cpp
@@ -336,7 +336,7 @@ void esq_external_panel_device::set_button(uint8_t button, bool pressed)
}
}
-void esq_external_panel_device::set_analog_value(uint8_t channel, int value)
+void esq_external_panel_device::set_analog_value(uint8_t channel, uint16_t value)
{
std::lock_guard lock(m_mutex);
if (!m_connected) return;
diff --git a/src/mame/ensoniq/extpanel.h b/src/mame/ensoniq/extpanel.h
index 2ed908f57a98a..91d1cc9eca239 100644
--- a/src/mame/ensoniq/extpanel.h
+++ b/src/mame/ensoniq/extpanel.h
@@ -57,7 +57,7 @@ class esq_external_panel_device : public device_t
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, int value);
+ void set_analog_value(uint8_t channel, uint16_t value);
void set_blink_phase(uint8_t phase);
bool get_next_command(Command &c) {
From e4c73d22bf5432866daafd575b8714e16be6dc00 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Sat, 13 Sep 2025 06:35:58 +0100
Subject: [PATCH 17/22] 527 -> 572 typo (character cell height)
---
src/mame/layout/esq2by40_vfx.lay | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mame/layout/esq2by40_vfx.lay b/src/mame/layout/esq2by40_vfx.lay
index e19f9a47b5c61..435e8e9b75a57 100644
--- a/src/mame/layout/esq2by40_vfx.lay
+++ b/src/mame/layout/esq2by40_vfx.lay
@@ -23,7 +23,7 @@ license:CC0-1.0
-
+
From d959fe62cc91665044717a008dfad79718560597 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Fri, 12 Sep 2025 22:56:54 +0100
Subject: [PATCH 18/22] Enable the esqpanel classes to read analog values from
the CPU, and send them to the external panel.
---
src/mame/ensoniq/esq5505.cpp | 19 ++++++++---
src/mame/ensoniq/esqpanel.cpp | 17 ++++++++--
src/mame/ensoniq/esqpanel.h | 3 ++
src/mame/ensoniq/extpanel.cpp | 10 +++---
src/mame/layout/vfx.lay | 61 +++++++++++++++++++++++++++++++++++
5 files changed, 99 insertions(+), 11 deletions(-)
create mode 100644 src/mame/layout/vfx.lay
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 e6ac362fbe9e8..1cc0d1890cfe6 100644
--- a/src/mame/ensoniq/esqpanel.cpp
+++ b/src/mame/ensoniq/esqpanel.cpp
@@ -47,7 +47,8 @@ esqpanel_device::esqpanel_device(const machine_config &mconfig, device_type type
m_external_panel(*this, "esq_external_panel"),
m_light_states(0x3f), // maximum number of lights
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);
}
@@ -241,6 +242,12 @@ 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)
{
uint8_t sendme = (pressed ? 0x80 : 0) | (button & 0xff);
@@ -507,7 +514,13 @@ void esqpanel2x40_vfx_device::send_display_contents()
void esqpanel2x40_vfx_device::send_analog_values()
{
- // TODO(cbrunschen): read analog values from the emulated keyboard and send them to the panel(s)
+ std::vector> values;
+ values.reserve(8);
+ for (offs_t offset = 0; offset < 8; offset++)
+ {
+ values.emplace_back(std::make_pair(offset, get_analog_value(offset)));
+ }
+ m_external_panel->set_analog_values(values);
}
void esqpanel2x40_vfx_device::send_button_states()
diff --git a/src/mame/ensoniq/esqpanel.h b/src/mame/ensoniq/esqpanel.h
index 82c3dbe86dc6b..256236836ee04 100644
--- a/src/mame/ensoniq/esqpanel.h
+++ b/src/mame/ensoniq/esqpanel.h
@@ -26,11 +26,13 @@ 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_char(int row, int column, uint8_t c, uint8_t attr);
void set_analog_value(offs_t offset, uint16_t value);
+ uint16_t get_analog_value(offs_t offset);
void set_button(uint8_t button, bool pressed);
protected:
@@ -71,6 +73,7 @@ class esqpanel_device : public device_t, public device_serial_interface
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;
diff --git a/src/mame/ensoniq/extpanel.cpp b/src/mame/ensoniq/extpanel.cpp
index 17e26c8a54d68..417674c5334be 100644
--- a/src/mame/ensoniq/extpanel.cpp
+++ b/src/mame/ensoniq/extpanel.cpp
@@ -14,7 +14,7 @@
#include
#include
-#define VERBOSE 0
+#define VERBOSE 1
#include "logmacro.h"
class esq_external_panel_device::context {
@@ -340,7 +340,7 @@ 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));
+ send(util::string_format("A %d %d", channel, value >> 6));
}
void esq_external_panel_device::set_blink_phase(uint8_t phase)
@@ -405,7 +405,7 @@ void esq_external_panel_device::set_analog_values(std::vector> 6);
}
send(os.str());
}
@@ -464,9 +464,9 @@ void esq_external_panel_device::handle_received_message(const std::string &messa
char mt;
is >> mt;
- LOG("Handing Message '%s' of type %c\r\n", message.c_str(), mt);
+ LOG("Handling Message '%s' of type %c\r\n", message.c_str(), mt);
-if (mt == MT_CONTROL)
+ if (mt == MT_CONTROL)
{
// For now, simply send all of the current state.
enqueue_send();
diff --git a/src/mame/layout/vfx.lay b/src/mame/layout/vfx.lay
new file mode 100644
index 0000000000000..e19f9a47b5c61
--- /dev/null
+++ b/src/mame/layout/vfx.lay
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 91b3a8987a6c089a70b007e173d3bb1a0c455a98 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Mon, 15 Sep 2025 14:44:02 +0100
Subject: [PATCH 19/22] Add layouts for the vfx family of keypoards, panel-only
for now, nbut reproducing the panels quite closely. UI elements are kept in
sync between the internal, layout-based panel, and any external panel.
---
src/mame/ensoniq/esqpanel.cpp | 195 ++++-
src/mame/ensoniq/esqpanel.h | 25 +-
src/mame/ensoniq/esqvfd.cpp | 5 +-
src/mame/layout/esq2by40_vfx.lay | 61 --
src/mame/layout/sd1.lay | 1161 ++++++++++++++++++++++++++++++
src/mame/layout/vfx.lay | 1100 ++++++++++++++++++++++++++--
src/mame/layout/vfxsd.lay | 1161 ++++++++++++++++++++++++++++++
7 files changed, 3577 insertions(+), 131 deletions(-)
delete mode 100644 src/mame/layout/esq2by40_vfx.lay
create mode 100644 src/mame/layout/sd1.lay
create mode 100644 src/mame/layout/vfxsd.lay
diff --git a/src/mame/ensoniq/esqpanel.cpp b/src/mame/ensoniq/esqpanel.cpp
index 1cc0d1890cfe6..3c8372c2b4ac9 100644
--- a/src/mame/ensoniq/esqpanel.cpp
+++ b/src/mame/ensoniq/esqpanel.cpp
@@ -4,9 +4,14 @@
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 "main.h"
//**************************************************************************
@@ -45,12 +50,13 @@ esqpanel_device::esqpanel_device(const machine_config &mconfig, device_type type
device_t(mconfig, type, tag, owner, clock),
device_serial_interface(mconfig, *this),
m_external_panel(*this, "esq_external_panel"),
- m_light_states(0x3f), // maximum number of lights
+ m_light_states(),
m_write_tx(*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);
}
@@ -247,9 +253,16 @@ 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)
{
+ printf("esqpanel.set_button(%d, %d)\r\n", button, pressed);
+ bool current = m_pressed_buttons.find(button) != m_pressed_buttons.end();
+ if (pressed == current)
+ {
+ printf("- button %d already %d, skipping\r\n", button, pressed);
+ return;
+ }
+
uint8_t sendme = (pressed ? 0x80 : 0) | (button & 0xff);
// printf("button %d %s : sending char to mainboard: %02x\n", button, down ? "down" : "up", sendme);
xmit_char(sendme);
@@ -270,6 +283,7 @@ TIMER_CALLBACK_MEMBER(esqpanel_device::check_external_panel_server) {
{
switch (c.kind) {
case esq_external_panel_device::Command::ButtonKind:
+ printf("Panel Handling button event from external panel\r\n");
set_button(c.button, c.pressed);
break;
@@ -318,17 +332,31 @@ 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_VFX(config, m_vfd, 60);
ESQ_EXTERNAL_PANEL(config, m_external_panel, 0);
-}
-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_eps_mode = false;
+ 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);
}
void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
@@ -376,7 +404,12 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
// 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
@@ -463,18 +496,34 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
}
}
+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;
+}
+
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()
{
esqpanel_device::device_start();
- m_external_panel->set_keyboard(owner()->shortname());
- m_external_panel->set_version("1");
+ m_variant.resolve();
+ m_lights.resolve();
m_blink_timer = timer_alloc(FUNC(esqpanel2x40_vfx_device::update_blink), this);
m_blink_timer->enable(false);
@@ -484,6 +533,23 @@ 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);
@@ -491,6 +557,8 @@ void esqpanel2x40_vfx_device::device_reset()
m_blink_timer->adjust(initial_delay, 0, sample_time);
m_blink_timer->enable(true);
}
+
+
}
void esqpanel2x40_vfx_device::device_reset_after_children()
@@ -545,6 +613,113 @@ void esqpanel2x40_vfx_device::send_light_states()
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.
+ printf("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);
+ printf("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);
+ printf("Setting data entry io port to %d\r\n", intvalue);
+ }
+ else if (offset == 5)
+ {
+ set_adjuster_value(m_analog_volume, intvalue);
+ printf("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 256236836ee04..471fd8ffe0149 100644
--- a/src/mame/ensoniq/esqpanel.h
+++ b/src/mame/ensoniq/esqpanel.h
@@ -31,9 +31,9 @@ class esqpanel_device : public device_t, public device_serial_interface
void xmit_char(uint8_t data);
void set_char(int row, int column, uint8_t c, uint8_t attr);
- void set_analog_value(offs_t offset, uint16_t value);
+ virtual void set_analog_value(offs_t offset, uint16_t value);
uint16_t get_analog_value(offs_t offset);
- void set_button(uint8_t button, bool pressed);
+ virtual void set_button(uint8_t button, bool pressed);
protected:
// construction/destruction
@@ -109,11 +109,15 @@ 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 { }
virtual void rcv_complete() override; // Rx completed receiving byte
@@ -123,6 +127,9 @@ class esqpanel2x40_vfx_device : public esqpanel_device {
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;
@@ -131,6 +138,9 @@ class esqpanel2x40_vfx_device : public esqpanel_device {
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;
@@ -138,6 +148,17 @@ class esqpanel2x40_vfx_device : public esqpanel_device {
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;
+
};
class esqpanel2x40_sq1_device : public esqpanel_device {
diff --git a/src/mame/ensoniq/esqvfd.cpp b/src/mame/ensoniq/esqvfd.cpp
index 1f4e3d7b14bc6..09fdd5cae1d54 100644
--- a/src/mame/ensoniq/esqvfd.cpp
+++ b/src/mame/ensoniq/esqvfd.cpp
@@ -10,7 +10,6 @@
#include "esq1by22.lh"
#include "esq2by40.lh"
-#include "esq2by40_vfx.lh"
DEFINE_DEVICE_TYPE(ESQ1X22, esq1x22_device, "esq1x22", "Ensoniq 1x22 VFD")
DEFINE_DEVICE_TYPE(ESQ2X40, esq2x40_device, "esq2x40", "Ensoniq 2x40 VFD")
@@ -424,7 +423,9 @@ void esq2x40_vfx_device::update_display()
void esq2x40_vfx_device::device_add_mconfig(machine_config &config)
{
- config.set_default_layout(layout_esq2by40_vfx);
+ // 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) {
diff --git a/src/mame/layout/esq2by40_vfx.lay b/src/mame/layout/esq2by40_vfx.lay
deleted file mode 100644
index 435e8e9b75a57..0000000000000
--- a/src/mame/layout/esq2by40_vfx.lay
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/mame/layout/sd1.lay b/src/mame/layout/sd1.lay
new file mode 100644
index 0000000000000..5d934184df596
--- /dev/null
+++ b/src/mame/layout/sd1.lay
@@ -0,0 +1,1161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mame/layout/vfx.lay b/src/mame/layout/vfx.lay
index e19f9a47b5c61..c61fe01e1f19b 100644
--- a/src/mame/layout/vfx.lay
+++ b/src/mame/layout/vfx.lay
@@ -1,61 +1,1049 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mame/layout/vfxsd.lay b/src/mame/layout/vfxsd.lay
new file mode 100644
index 0000000000000..60539e7a5f7ea
--- /dev/null
+++ b/src/mame/layout/vfxsd.lay
@@ -0,0 +1,1161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 392d5afea594c96dc8aec69144353677c0909b69 Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Mon, 15 Sep 2025 15:34:58 +0100
Subject: [PATCH 20/22] add more decorations and labels
---
src/mame/layout/sd1.lay | 59 +++++++++++++++++++++++++++++++--------
src/mame/layout/vfx.lay | 42 ++++++++++++++++++----------
src/mame/layout/vfxsd.lay | 59 +++++++++++++++++++++++++++++++--------
3 files changed, 122 insertions(+), 38 deletions(-)
diff --git a/src/mame/layout/sd1.lay b/src/mame/layout/sd1.lay
index 5d934184df596..b3d57588a9782 100644
--- a/src/mame/layout/sd1.lay
+++ b/src/mame/layout/sd1.lay
@@ -35,11 +35,13 @@
]]>
-
+
-
+
+
+
@@ -147,11 +149,14 @@
-
-
+
+
+
+
+
-
-
+
+
@@ -237,6 +242,9 @@
+
+
+
@@ -273,6 +281,9 @@
+
+
+
@@ -535,24 +546,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
+
+
+
@@ -605,10 +631,13 @@
-
+
+
+
+
-
+
@@ -707,6 +736,9 @@
+
+
+
@@ -761,6 +793,9 @@
+
+
+
diff --git a/src/mame/layout/vfx.lay b/src/mame/layout/vfx.lay
index c61fe01e1f19b..300042fb9e7b3 100644
--- a/src/mame/layout/vfx.lay
+++ b/src/mame/layout/vfx.lay
@@ -35,11 +35,13 @@
]]>
-
+
-
+
+
+
@@ -144,11 +146,14 @@
-
-
+
+
+
+
+
-
-
+
+
@@ -489,22 +494,28 @@
+
+
+
+
+
+
-
+
-
+
-
+
-
+
@@ -556,10 +567,13 @@
-
+
+
+
+
-
+
@@ -727,10 +741,10 @@
-
+
-
+
diff --git a/src/mame/layout/vfxsd.lay b/src/mame/layout/vfxsd.lay
index 60539e7a5f7ea..46446169f0baa 100644
--- a/src/mame/layout/vfxsd.lay
+++ b/src/mame/layout/vfxsd.lay
@@ -35,11 +35,13 @@
]]>
-
+
-
+
+
+
@@ -147,11 +149,14 @@
-
-
+
+
+
+
+
-
-
+
+
@@ -237,6 +242,9 @@
+
+
+
@@ -273,6 +281,9 @@
+
+
+
@@ -535,24 +546,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
+
+
+
@@ -605,10 +631,13 @@
-
+
+
+
+
-
+
@@ -707,6 +736,9 @@
+
+
+
@@ -761,6 +793,9 @@
+
+
+
From b16f6108c43ae48e8c7ed404afedf241cd3e6b5b Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Tue, 16 Sep 2025 23:20:48 +0200
Subject: [PATCH 21/22] printf -> osd_printf_debug
---
src/mame/ensoniq/esqpanel.cpp | 37 ++++++++++++++++-------------------
1 file changed, 17 insertions(+), 20 deletions(-)
diff --git a/src/mame/ensoniq/esqpanel.cpp b/src/mame/ensoniq/esqpanel.cpp
index 3c8372c2b4ac9..2b230d0823a10 100644
--- a/src/mame/ensoniq/esqpanel.cpp
+++ b/src/mame/ensoniq/esqpanel.cpp
@@ -123,16 +123,16 @@ 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_expect_calibration_second_byte ? "yes" : "no");
+// if (data >= 0xe0) prinosd_printf_debugtf("Got %02x from motherboard (second %s)\n", data, m_expect_calibration_second_byte ? "yes" : "no");
send_to_display(data);
if (m_expect_calibration_second_byte)
{
-// printf("second byte is %02x\n", data);
+// osd_printf_debug("second byte is %02x\n", data);
if (data == 0xfd) // calibration request
{
-// printf("let's send reply!\n");
+// osd_printf_debug("let's send reply!\n");
xmit_char(0xff); // this is the correct response for "calibration OK"
}
m_expect_calibration_second_byte = false;
@@ -164,9 +164,6 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte
// 2 = On
// 3 = Blinking
m_light_states[light_number] = (data & 0xc0) >> 6;
-
- // TODO: do something with the button information!
- // printf("Setting light %d to %s\n", light_number, lightState == 3 ? "Blink" : lightState == 2 ? "On" : "Off");
m_expect_light_second_byte = false;
}
else if (data == 0xfb) // request calibration
@@ -201,7 +198,7 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte
void esqpanel_device::tra_complete() // Tx completed sending byte
{
-// printf("panel Tx complete\n");
+// osd_printf_debug("panel Tx complete\n");
// is there more waiting to send?
if (m_xmit_read != m_xmit_write)
{
@@ -224,7 +221,7 @@ void esqpanel_device::tra_callback() // Tx send bit
void esqpanel_device::xmit_char(uint8_t data)
{
-// printf("Panel: xmit %02x\n", data);
+// osd_printf_debug("Panel: xmit %02x\n", data);
// if tx is busy it'll pick this up automatically when it completes
if (!m_tx_busy)
@@ -255,16 +252,16 @@ uint16_t esqpanel_device::get_analog_value(offs_t offset)
void esqpanel_device::set_button(uint8_t button, bool pressed)
{
- printf("esqpanel.set_button(%d, %d)\r\n", button, pressed);
+ // osd_printf_debug("esqpanel.set_button(%d, %d)\r\n", button, pressed);
bool current = m_pressed_buttons.find(button) != m_pressed_buttons.end();
if (pressed == current)
{
- printf("- button %d already %d, skipping\r\n", button, pressed);
+ // osd_printf_debug("- button %d already %d, skipping\r\n", button, pressed);
return;
}
uint8_t sendme = (pressed ? 0x80 : 0) | (button & 0xff);
- // printf("button %d %s : sending char to mainboard: %02x\n", button, down ? "down" : "up", sendme);
+ // osd_printf_debug("button %d %s : sending char to mainboard: %02x\n", button, down ? "down" : "up", sendme);
xmit_char(sendme);
xmit_char(0x00);
if (pressed)
@@ -283,7 +280,7 @@ TIMER_CALLBACK_MEMBER(esqpanel_device::check_external_panel_server) {
{
switch (c.kind) {
case esq_external_panel_device::Command::ButtonKind:
- printf("Panel Handling button event from external panel\r\n");
+ // osd_printf_debug("Panel Handling button event from external panel\r\n");
set_button(c.button, c.pressed);
break;
@@ -364,14 +361,14 @@ void esqpanel2x40_vfx_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_expect_calibration_second_byte ? "yes" : "no");
+// if (data >= 0xe0) osd_printf_debug("Got %02x from motherboard (second %s)\n", data, m_expect_calibration_second_byte ? "yes" : "no");
if (m_expect_calibration_second_byte)
{
-// printf("second byte is %02x\n", data);
+// osd_printf_debug("second byte is %02x\n", data);
if (data == 0xfd) // calibration request
{
-// printf("let's send reply!\n");
+// osd_printf_debug("let's send reply!\n");
xmit_char(0xff); // this is the correct response for "calibration OK"
}
m_expect_calibration_second_byte = false;
@@ -474,7 +471,7 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
break;
default:
- // printf("Unknown control code %02x\n", data);
+ // osd_printf_debug("Unknown control code %02x\n", data);
break;
}
}
@@ -671,7 +668,7 @@ INPUT_CHANGED_MEMBER(esqpanel2x40_vfx_device::analog_value_change)
if (oldval == 255)
{
// This is the initial write from the layout. Skip this.
- printf("skipping initial write from internal panel to channnel %d\r\n", param);
+ osd_printf_debug("skipping initial write from internal panel to channnel %d\r\n", param);
return;
}
@@ -679,7 +676,7 @@ INPUT_CHANGED_MEMBER(esqpanel2x40_vfx_device::analog_value_change)
int clamped = std::max(0, std::min(100, (int)newval));
int value = (1023 << 6) * clamped / 100;
esqpanel_device::set_analog_value(channel, value);
- printf("sending analog channel %d value %d to external panel\r\n", channel, value);
+ osd_printf_debug("sending analog channel %d value %d to external panel\r\n", channel, value);
m_external_panel->set_analog_value(channel, value);
}
@@ -695,12 +692,12 @@ void esqpanel2x40_vfx_device::set_analog_value(offs_t offset, uint16_t value)
if (offset == 3)
{
set_adjuster_value(m_analog_data_entry, intvalue);
- printf("Setting data entry io port to %d\r\n", intvalue);
+ osd_printf_debug("Setting data entry io port to %d\r\n", intvalue);
}
else if (offset == 5)
{
set_adjuster_value(m_analog_volume, intvalue);
- printf("Setting volume io port to %d\r\n", intvalue);
+ osd_printf_debug("Setting volume io port to %d\r\n", intvalue);
}
}
From 9181adc06a52a839120108448fb23ed46cc2158f Mon Sep 17 00:00:00 2001
From: Christian Brunschen <6741909+cbrunschen@users.noreply.github.com>
Date: Wed, 17 Sep 2025 07:21:06 +0200
Subject: [PATCH 22/22] osd_printf_debug -> logmacro.h + LOG + logerrer
---
src/mame/ensoniq/esqpanel.cpp | 39 +++++++++++++++++------------------
src/mame/ensoniq/esqpanel.h | 10 +++++++++
src/mame/ensoniq/extpanel.cpp | 2 +-
3 files changed, 30 insertions(+), 21 deletions(-)
diff --git a/src/mame/ensoniq/esqpanel.cpp b/src/mame/ensoniq/esqpanel.cpp
index 2b230d0823a10..afc46ea9fdafc 100644
--- a/src/mame/ensoniq/esqpanel.cpp
+++ b/src/mame/ensoniq/esqpanel.cpp
@@ -14,9 +14,8 @@
#include "main.h"
-//**************************************************************************
-// External panel support
-//**************************************************************************
+#define VERBOSE 0
+#include "logmacro.h"
#include
#include
@@ -123,16 +122,16 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte
receive_register_extract();
uint8_t data = get_received_char();
-// if (data >= 0xe0) prinosd_printf_debugtf("Got %02x from motherboard (second %s)\n", data, m_expect_calibration_second_byte ? "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);
if (m_expect_calibration_second_byte)
{
-// osd_printf_debug("second byte is %02x\n", data);
+// LOG("second byte is %02x\n", data);
if (data == 0xfd) // calibration request
{
-// osd_printf_debug("let's send reply!\n");
+// LOG("let's send reply!\n");
xmit_char(0xff); // this is the correct response for "calibration OK"
}
m_expect_calibration_second_byte = false;
@@ -198,7 +197,7 @@ void esqpanel_device::rcv_complete() // Rx completed receiving byte
void esqpanel_device::tra_complete() // Tx completed sending byte
{
-// osd_printf_debug("panel Tx complete\n");
+// LOG("panel Tx complete\n");
// is there more waiting to send?
if (m_xmit_read != m_xmit_write)
{
@@ -221,7 +220,7 @@ void esqpanel_device::tra_callback() // Tx send bit
void esqpanel_device::xmit_char(uint8_t data)
{
-// osd_printf_debug("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)
@@ -252,16 +251,16 @@ uint16_t esqpanel_device::get_analog_value(offs_t offset)
void esqpanel_device::set_button(uint8_t button, bool pressed)
{
- // osd_printf_debug("esqpanel.set_button(%d, %d)\r\n", button, pressed);
+ LOG("set_button(%d, %d)\r\n", button, pressed);
bool current = m_pressed_buttons.find(button) != m_pressed_buttons.end();
if (pressed == current)
{
- // osd_printf_debug("- button %d already %d, skipping\r\n", button, pressed);
+ LOG("- button %d already %d, skipping\r\n", button, pressed);
return;
}
uint8_t sendme = (pressed ? 0x80 : 0) | (button & 0xff);
- // osd_printf_debug("button %d %s : sending char to mainboard: %02x\n", button, down ? "down" : "up", sendme);
+ // LOG("button %d %s : sending char to mainboard: %02x\n", button, pressed ? "down" : "up", sendme);
xmit_char(sendme);
xmit_char(0x00);
if (pressed)
@@ -280,7 +279,7 @@ TIMER_CALLBACK_MEMBER(esqpanel_device::check_external_panel_server) {
{
switch (c.kind) {
case esq_external_panel_device::Command::ButtonKind:
- // osd_printf_debug("Panel Handling button event from external panel\r\n");
+ LOG("Panel Handling button event from external panel\r\n");
set_button(c.button, c.pressed);
break;
@@ -361,14 +360,14 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
receive_register_extract();
uint8_t data = get_received_char();
-// if (data >= 0xe0) osd_printf_debug("Got %02x from motherboard (second %s)\n", data, m_expect_calibration_second_byte ? "yes" : "no");
+// 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)
{
-// osd_printf_debug("second byte is %02x\n", data);
+// LOG("second byte is %02x\n", data);
if (data == 0xfd) // calibration request
{
-// osd_printf_debug("let's send reply!\n");
+// LOG("let's send reply!\n");
xmit_char(0xff); // this is the correct response for "calibration OK"
}
m_expect_calibration_second_byte = false;
@@ -471,7 +470,7 @@ void esqpanel2x40_vfx_device::rcv_complete() // Rx completed receiving byte
break;
default:
- // osd_printf_debug("Unknown control code %02x\n", data);
+ // LOG("Unknown control code %02x\n", data);
break;
}
}
@@ -668,7 +667,7 @@ INPUT_CHANGED_MEMBER(esqpanel2x40_vfx_device::analog_value_change)
if (oldval == 255)
{
// This is the initial write from the layout. Skip this.
- osd_printf_debug("skipping initial write from internal panel to channnel %d\r\n", param);
+ LOG("skipping initial write from internal panel to channnel %d\r\n", param);
return;
}
@@ -676,7 +675,7 @@ INPUT_CHANGED_MEMBER(esqpanel2x40_vfx_device::analog_value_change)
int clamped = std::max(0, std::min(100, (int)newval));
int value = (1023 << 6) * clamped / 100;
esqpanel_device::set_analog_value(channel, value);
- osd_printf_debug("sending analog channel %d value %d to external panel\r\n", channel, value);
+ LOG("sending analog channel %d value %d to external panel\r\n", channel, value);
m_external_panel->set_analog_value(channel, value);
}
@@ -692,12 +691,12 @@ void esqpanel2x40_vfx_device::set_analog_value(offs_t offset, uint16_t value)
if (offset == 3)
{
set_adjuster_value(m_analog_data_entry, intvalue);
- osd_printf_debug("Setting data entry io port to %d\r\n", intvalue);
+ LOG("Setting data entry io port to %d\r\n", intvalue);
}
else if (offset == 5)
{
set_adjuster_value(m_analog_volume, intvalue);
- osd_printf_debug("Setting volume io port to %d\r\n", intvalue);
+ LOG("Setting volume io port to %d\r\n", intvalue);
}
}
diff --git a/src/mame/ensoniq/esqpanel.h b/src/mame/ensoniq/esqpanel.h
index 471fd8ffe0149..2f0b085a14107 100644
--- a/src/mame/ensoniq/esqpanel.h
+++ b/src/mame/ensoniq/esqpanel.h
@@ -68,6 +68,16 @@ class esqpanel_device : public device_t, public device_serial_interface
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;
diff --git a/src/mame/ensoniq/extpanel.cpp b/src/mame/ensoniq/extpanel.cpp
index 417674c5334be..9006aabeb91fc 100644
--- a/src/mame/ensoniq/extpanel.cpp
+++ b/src/mame/ensoniq/extpanel.cpp
@@ -14,7 +14,7 @@
#include
#include
-#define VERBOSE 1
+#define VERBOSE 0
#include "logmacro.h"
class esq_external_panel_device::context {