aboutsummaryrefslogtreecommitdiff
path: root/src/chips
diff options
context:
space:
mode:
Diffstat (limited to 'src/chips')
-rw-r--r--src/chips/opl_serial_misc.h338
-rw-r--r--src/chips/opl_serial_port.cpp166
-rw-r--r--src/chips/opl_serial_port.h65
3 files changed, 569 insertions, 0 deletions
diff --git a/src/chips/opl_serial_misc.h b/src/chips/opl_serial_misc.h
new file mode 100644
index 0000000..2583a4b
--- /dev/null
+++ b/src/chips/opl_serial_misc.h
@@ -0,0 +1,338 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2023 Vitaly Novichkov (Wohlstand)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OPL_SERIAL_MISC_H
+#define OPL_SERIAL_MISC_H
+
+#if defined( __unix__) || defined(__APPLE__)
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <cstring>
+#include <cstdio>
+#include <errno.h>
+#include <sys/ioctl.h>
+#endif
+
+#ifdef __APPLE__
+#include <IOKit/serial/ioss.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <string>
+
+
+class ChipSerialPortBase
+{
+public:
+ ChipSerialPortBase() {}
+ virtual ~ChipSerialPortBase() {}
+
+ virtual bool isOpen()
+ {
+ return false;
+ }
+
+ virtual void close() {}
+
+ virtual bool open(const std::string &/*portName*/, unsigned /*baudRate*/)
+ {
+ return false;
+ }
+
+ virtual int write(uint8_t * /*data*/, size_t /*size*/)
+ {
+ return 0;
+ }
+};
+
+
+
+#if defined( __unix__) || defined(__APPLE__)
+class ChipSerialPort : public ChipSerialPortBase
+{
+ int m_port;
+ struct termios m_portSetup;
+
+ static unsigned int baud2enum(unsigned int baud)
+ {
+ if(baud == 0)
+ return B0;
+ else if(baud <= 50)
+ return B50;
+ else if(baud <= 75)
+ return B75;
+ else if(baud <= 110)
+ return B110;
+ else if(baud <= 134)
+ return B134;
+ else if(baud <= 150)
+ return B150;
+ else if(baud <= 200)
+ return B200;
+ else if(baud <= 300)
+ return B300;
+ else if(baud <= 600)
+ return B600;
+ else if(baud <= 1200)
+ return B1200;
+ else if(baud <= 1800)
+ return B1800;
+ else if(baud <= 2400)
+ return B2400;
+ else if(baud <= 4800)
+ return B4800;
+ else if(baud <= 9600)
+ return B9600;
+ else if(baud <= 19200)
+ return B19200;
+ else if(baud <= 38400)
+ return B38400;
+ else if(baud <= 57600)
+ return B57600;
+ else if(baud <= 115200)
+ return B115200;
+ else
+ return B230400;
+ }
+
+public:
+ ChipSerialPort() :
+ ChipSerialPortBase()
+ {
+ m_port = 0;
+ std::memset(&m_portSetup, 0, sizeof(struct termios));
+ }
+
+ virtual ~ChipSerialPort()
+ {
+ close();
+ }
+
+ bool isOpen()
+ {
+ return m_port != 0;
+ }
+
+ void close()
+ {
+ if(m_port)
+ ::close(m_port);
+
+ m_port = 0;
+ }
+
+ bool open(const std::string &portName, unsigned baudRate)
+ {
+ if(m_port)
+ this->close();
+
+ std::string portPath = "/dev/" + portName;
+ m_port = ::open(portPath.c_str(), O_WRONLY);
+
+ if(m_port < 0)
+ {
+ std::fprintf(stderr, "-- OPL Serial ERROR: failed to open tty device `%s': %s\n", portPath.c_str(), strerror(errno));
+ std::fflush(stderr);
+ m_port = 0;
+ return false;
+ }
+
+ if(tcgetattr(m_port, &m_portSetup) != 0)
+ {
+ std::fprintf(stderr, "-- OPL Serial ERROR: failed to retrieve setup `%s': %s\n", portPath.c_str(), strerror(errno));
+ std::fflush(stderr);
+ close();
+ return false;
+ }
+
+ m_portSetup.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+ m_portSetup.c_oflag &= ~OPOST;
+ m_portSetup.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ m_portSetup.c_cflag &= ~(CSIZE | PARENB);
+ m_portSetup.c_cflag |= CS8;
+
+#if defined (__linux__) || defined (__CYGWIN__)
+ m_portSetup.c_cflag &= ~CBAUD;
+#endif
+
+#if defined (BSD) || defined(__FreeBSD__)
+ cfsetispeed(&m_portSetup, baudRate);
+ cfsetospeed(&m_portSetup, baudRate);
+#elif !defined(__APPLE__)
+ cfsetospeed(&m_portSetup, baud2enum(baudRate));
+#endif
+
+ if(tcsetattr(m_port, TCSANOW, &m_portSetup) != 0)
+ {
+ std::fprintf(stderr, "-- OPL Serial ERROR: failed to apply setup `%s': %s\n", portPath.c_str(), strerror(errno));
+ std::fflush(stderr);
+ close();
+ return false;
+ }
+
+#ifdef __APPLE__
+ if(ioctl(m_port, IOSSIOSPEED, &baudRate) == -1)
+ {
+ std::fprintf(stderr, "-- OPL Serial ERROR: Failed to set MacOS specific tty attributes for `%s': %s", portPath.c_str(), strerror(errno));
+ std::fflush(stderr);
+ close();
+ return false;
+ }
+#endif
+
+ return true;
+ }
+
+ int write(uint8_t *data, size_t size)
+ {
+ if(!m_port)
+ return 0;
+
+ return ::write(m_port, data, size);
+ }
+};
+
+#endif // __unix__
+
+
+
+
+#ifdef _WIN32
+
+class ChipSerialPort : public ChipSerialPortBase
+{
+ HANDLE m_port;
+
+ static unsigned int baud2enum(unsigned int baud)
+ {
+ if(baud <= 110)
+ return CBR_110;
+ else if(baud <= 300)
+ return CBR_300;
+ else if(baud <= 600)
+ return CBR_600;
+ else if(baud <= 1200)
+ return CBR_1200;
+ else if(baud <= 2400)
+ return CBR_2400;
+ else if(baud <= 4800)
+ return CBR_4800;
+ else if(baud <= 9600)
+ return CBR_9600;
+ else if(baud <= 19200)
+ return CBR_19200;
+ else if(baud <= 38400)
+ return CBR_38400;
+ else if(baud <= 57600)
+ return CBR_57600;
+ else if(baud <= 115200)
+ return CBR_115200;
+ else
+ return CBR_115200;
+ }
+
+public:
+ ChipSerialPort() : ChipSerialPortBase()
+ {
+ m_port = NULL;
+ }
+
+ virtual ~ChipSerialPort()
+ {
+ close();
+ }
+
+ bool isOpen()
+ {
+ return m_port != NULL;
+ }
+
+ void close()
+ {
+ if(m_port)
+ CloseHandle(m_port);
+
+ m_port = NULL;
+ }
+
+ bool open(const std::string &portName, unsigned baudRate)
+ {
+ if(m_port)
+ this->close();
+
+ std::string portPath = "\\\\.\\" + portName;
+ m_port = CreateFileA(portPath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+ if(m_port == INVALID_HANDLE_VALUE)
+ {
+ m_port = NULL;
+ return false;
+ }
+
+ DCB dcb;
+ BOOL succ;
+
+ SecureZeroMemory(&dcb, sizeof(DCB));
+ dcb.DCBlength = sizeof(DCB);
+
+ succ = GetCommState(m_port, &dcb);
+ if(!succ)
+ {
+ this->close();
+ return false;
+ }
+
+ dcb.BaudRate = baud2enum(baudRate);
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+
+ succ = SetCommState(m_port, &dcb);
+
+ if(!succ)
+ {
+ this->close();
+ return false;
+ }
+
+ return true;
+ }
+
+ int write(uint8_t *data, size_t size)
+ {
+ if(!m_port)
+ return 0;
+
+ DWORD written = 0;
+
+ if(!WriteFile(m_port, data, size, &written, 0))
+ return 0;
+
+ return written;
+ }
+};
+
+#endif // _WIN32
+
+#endif // OPL_SERIAL_MISC_H
diff --git a/src/chips/opl_serial_port.cpp b/src/chips/opl_serial_port.cpp
new file mode 100644
index 0000000..1607d8c
--- /dev/null
+++ b/src/chips/opl_serial_port.cpp
@@ -0,0 +1,166 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2023 Vitaly Novichkov (Wohlstand)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifdef ENABLE_HW_OPL_SERIAL_PORT
+
+#include "opl_serial_port.h"
+#include "opl_serial_misc.h"
+
+
+static size_t retrowave_protocol_serial_pack(const uint8_t *buf_in, size_t len_in, uint8_t *buf_out)
+{
+ size_t in_cursor = 0;
+ size_t out_cursor = 0;
+
+ buf_out[out_cursor] = 0x00;
+ out_cursor += 1;
+
+ uint8_t shift_count = 0;
+
+ while(in_cursor < len_in)
+ {
+ uint8_t cur_byte_out = buf_in[in_cursor] >> shift_count;
+ if(in_cursor > 0)
+ cur_byte_out |= (buf_in[in_cursor - 1] << (8 - shift_count));
+
+ cur_byte_out |= 0x01;
+ buf_out[out_cursor] = cur_byte_out;
+
+ shift_count += 1;
+ in_cursor += 1;
+ out_cursor += 1;
+ if(shift_count > 7)
+ {
+ shift_count = 0;
+ in_cursor -= 1;
+ }
+ }
+
+ if(shift_count)
+ {
+ buf_out[out_cursor] = buf_in[in_cursor - 1] << (8 - shift_count);
+ buf_out[out_cursor] |= 0x01;
+ out_cursor += 1;
+ }
+
+ buf_out[out_cursor] = 0x02;
+ out_cursor += 1;
+
+ return out_cursor;
+}
+
+OPL_SerialPort::OPL_SerialPort()
+ : m_port(NULL), m_protocol(ProtocolUnknown)
+{}
+
+OPL_SerialPort::~OPL_SerialPort()
+{
+ delete m_port;
+ m_port = NULL;
+}
+
+bool OPL_SerialPort::connectPort(const std::string& name, unsigned baudRate, unsigned protocol)
+{
+ delete m_port;
+ m_port = NULL;
+
+ // ensure audio thread reads protocol atomically and in order,
+ // so chipType() will be correct after the port is live
+ m_protocol = protocol;
+
+ // QSerialPort *port = m_port = new QSerialPort(name);
+ // port->setBaudRate(baudRate);
+ // return port->open(QSerialPort::WriteOnly);
+
+ m_port = new ChipSerialPort;
+ return m_port->open(name, baudRate);
+}
+
+void OPL_SerialPort::writeReg(uint16_t addr, uint8_t data)
+{
+ uint8_t sendBuffer[16];
+ ChipSerialPortBase *port = m_port;
+
+ if(!port || !port->isOpen())
+ return;
+
+ switch(m_protocol)
+ {
+ default:
+ case ProtocolArduinoOPL2:
+ {
+ if(addr >= 0x100)
+ break;
+ sendBuffer[0] = (uint8_t)addr;
+ sendBuffer[1] = (uint8_t)data;
+ port->write(sendBuffer, 2);
+ break;
+ }
+ case ProtocolNukeYktOPL3:
+ {
+ sendBuffer[0] = (addr >> 6) | 0x80;
+ sendBuffer[1] = ((addr & 0x3f) << 1) | (data >> 7);
+ sendBuffer[2] = (data & 0x7f);
+ port->write(sendBuffer, 3);
+ break;
+ }
+ case ProtocolRetroWaveOPL3:
+ {
+ bool port1 = (addr & 0x100) != 0;
+ uint8_t buf[8] =
+ {
+ static_cast<uint8_t>(0x21 << 1), 0x12,
+ static_cast<uint8_t>(port1 ? 0xe5 : 0xe1), static_cast<uint8_t>(addr & 0xff),
+ static_cast<uint8_t>(port1 ? 0xe7 : 0xe3), static_cast<uint8_t>(data),
+ 0xfb, static_cast<uint8_t>(data)
+ };
+ size_t packed_len = retrowave_protocol_serial_pack(buf, sizeof(buf), sendBuffer);
+ port->write(sendBuffer, packed_len);
+ break;
+ }
+ }
+}
+
+void OPL_SerialPort::nativeGenerate(int16_t *frame)
+{
+ frame[0] = 0;
+ frame[1] = 0;
+}
+
+const char *OPL_SerialPort::emulatorName()
+{
+ return "OPL Serial Port Driver";
+}
+
+OPLChipBase::ChipType OPL_SerialPort::chipType()
+{
+ switch(m_protocol)
+ {
+ default:
+ case ProtocolArduinoOPL2:
+ return OPLChipBase::CHIPTYPE_OPL2;
+ case ProtocolNukeYktOPL3:
+ case ProtocolRetroWaveOPL3:
+ return OPLChipBase::CHIPTYPE_OPL3;
+ }
+}
+
+#endif // ENABLE_HW_OPL_SERIAL_PORT
diff --git a/src/chips/opl_serial_port.h b/src/chips/opl_serial_port.h
new file mode 100644
index 0000000..346c68e
--- /dev/null
+++ b/src/chips/opl_serial_port.h
@@ -0,0 +1,65 @@
+/*
+ * Interfaces over Yamaha OPL3 (YMF262) chip emulators
+ *
+ * Copyright (c) 2017-2023 Vitaly Novichkov (Wohlstand)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifndef OPL_SERIAL_PORT_H
+#define OPL_SERIAL_PORT_H
+
+#ifdef ENABLE_HW_OPL_SERIAL_PORT
+
+#include <string>
+#include "opl_chip_base.h"
+
+class ChipSerialPortBase;
+
+class OPL_SerialPort : public OPLChipBaseT<OPL_SerialPort>
+{
+public:
+ OPL_SerialPort();
+ virtual ~OPL_SerialPort() override;
+
+ enum Protocol
+ {
+ ProtocolUnknown,
+ ProtocolArduinoOPL2,
+ ProtocolNukeYktOPL3,
+ ProtocolRetroWaveOPL3
+ };
+
+ bool connectPort(const std::string &name, unsigned baudRate, unsigned protocol);
+
+ bool canRunAtPcmRate() const override { return false; }
+ void setRate(uint32_t /*rate*/) override {}
+ void reset() override {}
+ void writeReg(uint16_t addr, uint8_t data) override;
+ void nativePreGenerate() override {}
+ void nativePostGenerate() override {}
+ void nativeGenerate(int16_t *frame) override;
+ const char *emulatorName() override;
+ ChipType chipType() override;
+
+private:
+ ChipSerialPortBase *m_port;
+ int m_protocol;
+};
+
+#endif // ENABLE_HW_OPL_SERIAL_PORT
+
+#endif // OPL_SERIAL_PORT_H