diff --git a/.gitmodules b/.gitmodules index 5f0e538d..0dcbd0c8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "extern/Nuked-OPN2"] - path = extern/Nuked-OPN2 - url = https://github.com/nukeykt/Nuked-OPN2 [submodule "extern/SDL"] path = extern/SDL url = https://github.com/libsdl-org/SDL.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e37c88b..b705db85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -352,7 +352,7 @@ extern/adpcm/yma_codec.c extern/adpcm/ymb_codec.c extern/adpcm/ymz_codec.c -extern/Nuked-OPN2/ym3438.c +extern/opn/ym3438.c extern/Nuked-PSG/ympsg.c extern/opm/opm.c extern/Nuked-OPLL/opll.c diff --git a/extern/Nuked-OPN2 b/extern/Nuked-OPN2 deleted file mode 160000 index b0e9de0f..00000000 --- a/extern/Nuked-OPN2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b0e9de0f816943ad3820ddfefa0fff276d659250 diff --git a/extern/opn/LICENSE b/extern/opn/LICENSE new file mode 100644 index 00000000..8000a6fa --- /dev/null +++ b/extern/opn/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/extern/opn/MODIFIED.md b/extern/opn/MODIFIED.md new file mode 100644 index 00000000..33b00cb7 --- /dev/null +++ b/extern/opn/MODIFIED.md @@ -0,0 +1,5 @@ +# modification disclaimer + +this is a modified version of Nuked-OPN2 which implements high resolution output (for OPN/OPNA/OPNB). + +accuracy of OPN2 (YM2612/YM3438) should not be altered. diff --git a/extern/opn/README.md b/extern/opn/README.md new file mode 100644 index 00000000..f5fce509 --- /dev/null +++ b/extern/opn/README.md @@ -0,0 +1,34 @@ +# Nuked-OPN2 +High accuracy Yamaha YM3438(OPN2) emulator. + +The YM3438 is a CMOS variant of the YM2612 used in Sega MegaDrive(Genesis) and FM Towns. + +Genesis Plus GX fork with this core integrated is available here: https://github.com/nukeykt/Genesis-Plus-GX + +# Features: +- Based on YM3438 die shot reverse engineering and thus provides very high emulation accuracy. + +- Cycle-accurate. + +- Undocumented registers/features emulation. +- SSG-EG, CSM mode emulation. +- Compatible with the YM2612. + +# API documention +``` +void OPN2_Reset(ym3438_t *chip) - Reset emulated chip +void OPN2_Clock(ym3438_t *chip, Bit32s *buffer) - Advances emulated chip state by 1 internal clock(6 master clocks). Writes signed 9-bit MOL, MOR pin states to buffer. +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data) - Write 8-bit data to port. +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value) - Set TEST pin value. +Bit32u OPN2_ReadTestPin(ym3438_t *chip) - Read TEST pin value. +Bit32u OPN2_ReadIRQPin(ym3438_t *chip) - Read IRQ pin value. +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) - Read chip status. +``` + +# Samples +Sonic the Hedgehog: +https://youtu.be/ImmKy_-pJ8g + +Sega CD BIOS v1.10: +https://youtu.be/s-8ASMbtojQ + diff --git a/extern/opn/ym3438.c b/extern/opn/ym3438.c new file mode 100644 index 00000000..72b9cb9b --- /dev/null +++ b/extern/opn/ym3438.c @@ -0,0 +1,1435 @@ +/* + * Copyright (C) 2017-2022 Alexey Khokholov (Nuke.YKT) + * + * This file is part of Nuked OPN2. + * + * 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 + * + * Nuked OPN2(Yamaha YM3438) emulator. + * Thanks: + * Silicon Pr0n: + * Yamaha YM3438 decap and die shot(digshadow). + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.0.11 + */ + +#include +#include "ym3438.h" + +#define SIGN_EXTEND(bit_index, value) (((value) & ((1u << (bit_index)) - 1u)) - ((value) & (1u << (bit_index)))) + +enum { + eg_num_attack = 0, + eg_num_decay = 1, + eg_num_sustain = 2, + eg_num_release = 3 +}; + +/* logsin table */ +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* exp table */ +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, + 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, + 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, + 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, + 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, + 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, + 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, + 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, + 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, + 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, + 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, + 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, + 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, + 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, + 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, + 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, + 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +/* Note table */ +static const Bit32u fn_note[16] = { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 +}; + +/* Envelope generator */ +static const Bit32u eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +static const Bit8u eg_am_shift[4] = { + 7, 3, 1, 0 +}; + +/* Phase generator */ +static const Bit32u pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; + +static const Bit32u pg_lfo_sh1[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 1, 1 }, + { 7, 7, 7, 7, 1, 1, 1, 1 }, + { 7, 7, 7, 1, 1, 1, 1, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 } +}; + +static const Bit32u pg_lfo_sh2[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 2, 2, 2, 2 }, + { 7, 7, 7, 2, 2, 2, 7, 7 }, + { 7, 7, 2, 2, 7, 7, 2, 2 }, + { 7, 7, 2, 7, 7, 7, 2, 7 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 } +}; + +/* Address decoder */ +static const Bit32u op_offset[12] = { + 0x000, /* Ch1 OP1/OP2 */ + 0x001, /* Ch2 OP1/OP2 */ + 0x002, /* Ch3 OP1/OP2 */ + 0x100, /* Ch4 OP1/OP2 */ + 0x101, /* Ch5 OP1/OP2 */ + 0x102, /* Ch6 OP1/OP2 */ + 0x004, /* Ch1 OP3/OP4 */ + 0x005, /* Ch2 OP3/OP4 */ + 0x006, /* Ch3 OP3/OP4 */ + 0x104, /* Ch4 OP3/OP4 */ + 0x105, /* Ch5 OP3/OP4 */ + 0x106 /* Ch6 OP3/OP4 */ +}; + +static const Bit32u ch_offset[6] = { + 0x000, /* Ch1 */ + 0x001, /* Ch2 */ + 0x002, /* Ch3 */ + 0x100, /* Ch4 */ + 0x101, /* Ch5 */ + 0x102 /* Ch6 */ +}; + +/* LFO */ +static const Bit32u lfo_cycles[8] = { + 108, 77, 71, 67, 62, 44, 8, 5 +}; + +/* FM algorithm */ +static const Bit32u fm_algorithm[4][6][8] = { + { + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_0 */ + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 1 } /* Out */ + }, + { + { 0, 1, 0, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 1, 1, 1, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 1, 0, 0, 1, 1, 1, 1, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 1, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 1, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 1, 0, 0, 0, 0 }, /* OP2 */ + { 1, 1, 0, 1, 1, 0, 0, 0 }, /* Last operator */ + { 0, 0, 1, 0, 0, 0, 0, 0 }, /* Last operator */ + { 1, 1, 1, 1, 1, 1, 1, 1 } /* Out */ + } +}; + +static void OPN2_DoIO(ym3438_t *chip) +{ + /* Write signal check */ + chip->write_a_en = (chip->write_a & 0x03) == 0x01; + chip->write_d_en = (chip->write_d & 0x03) == 0x01; + chip->write_a <<= 1; + chip->write_d <<= 1; + /* Busy counter */ + chip->busy = chip->write_busy; + chip->write_busy_cnt += chip->write_busy; + chip->write_busy = (chip->write_busy && !(chip->write_busy_cnt >> 5)) || chip->write_d_en; + chip->write_busy_cnt &= 0x1f; +} + +static void OPN2_DoRegWrite(ym3438_t *chip) +{ + Bit32u i; + Bit32u slot = chip->cycles % 12; + Bit32u address; + Bit32u channel = chip->channel; + /* Update registers */ + if (chip->write_fm_data) + { + /* Slot */ + if (op_offset[slot] == (chip->address & 0x107)) + { + if (chip->address & 0x08) + { + /* OP2, OP4 */ + slot += 12; + } + address = chip->address & 0xf0; + switch (address) + { + case 0x30: /* DT, MULTI */ + chip->multi[slot] = chip->data & 0x0f; + if (!chip->multi[slot]) + { + chip->multi[slot] = 1; + } + else + { + chip->multi[slot] <<= 1; + } + chip->dt[slot] = (chip->data >> 4) & 0x07; + break; + case 0x40: /* TL */ + chip->tl[slot] = chip->data & 0x7f; + break; + case 0x50: /* KS, AR */ + chip->ar[slot] = chip->data & 0x1f; + chip->ks[slot] = (chip->data >> 6) & 0x03; + break; + case 0x60: /* AM, DR */ + chip->dr[slot] = chip->data & 0x1f; + chip->am[slot] = (chip->data >> 7) & 0x01; + break; + case 0x70: /* SR */ + chip->sr[slot] = chip->data & 0x1f; + break; + case 0x80: /* SL, RR */ + chip->rr[slot] = chip->data & 0x0f; + chip->sl[slot] = (chip->data >> 4) & 0x0f; + chip->sl[slot] |= (chip->sl[slot] + 1) & 0x10; + break; + case 0x90: /* SSG-EG */ + chip->ssg_eg[slot] = chip->data & 0x0f; + break; + default: + break; + } + } + + /* Channel */ + if (ch_offset[channel] == (chip->address & 0x103)) + { + address = chip->address & 0xfc; + switch (address) + { + case 0xa0: + chip->fnum[channel] = (chip->data & 0xff) | ((chip->reg_a4 & 0x07) << 8); + chip->block[channel] = (chip->reg_a4 >> 3) & 0x07; + chip->kcode[channel] = (chip->block[channel] << 2) | fn_note[chip->fnum[channel] >> 7]; + break; + case 0xa4: + chip->reg_a4 = chip->data & 0xff; + break; + case 0xa8: + chip->fnum_3ch[channel] = (chip->data & 0xff) | ((chip->reg_ac & 0x07) << 8); + chip->block_3ch[channel] = (chip->reg_ac >> 3) & 0x07; + chip->kcode_3ch[channel] = (chip->block_3ch[channel] << 2) | fn_note[chip->fnum_3ch[channel] >> 7]; + break; + case 0xac: + chip->reg_ac = chip->data & 0xff; + break; + case 0xb0: + chip->connect[channel] = chip->data & 0x07; + chip->fb[channel] = (chip->data >> 3) & 0x07; + break; + case 0xb4: + chip->pms[channel] = chip->data & 0x07; + chip->ams[channel] = (chip->data >> 4) & 0x03; + chip->pan_l[channel] = (chip->data >> 7) & 0x01; + chip->pan_r[channel] = (chip->data >> 6) & 0x01; + break; + default: + break; + } + } + } + + if (chip->write_a_en || chip->write_d_en) + { + /* Data */ + if (chip->write_a_en) + { + chip->write_fm_data = 0; + } + + if (chip->write_fm_address && chip->write_d_en) + { + chip->write_fm_data = 1; + } + + /* Address */ + if (chip->write_a_en) + { + if ((chip->write_data & 0xf0) != 0x00) + { + /* FM Write */ + chip->address = chip->write_data; + chip->write_fm_address = 1; + } + else + { + /* SSG write */ + chip->write_fm_address = 0; + } + } + + /* FM Mode */ + /* Data */ + if (chip->write_d_en && (chip->write_data & 0x100) == 0) + { + switch (chip->write_fm_mode_a) + { + case 0x21: /* LSI test 1 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_21[i] = (chip->write_data >> i) & 0x01; + } + break; + case 0x22: /* LFO control */ + if ((chip->write_data >> 3) & 0x01) + { + chip->lfo_en = 0x7f; + } + else + { + chip->lfo_en = 0; + } + chip->lfo_freq = chip->write_data & 0x07; + break; + case 0x24: /* Timer A */ + chip->timer_a_reg &= 0x03; + chip->timer_a_reg |= (chip->write_data & 0xff) << 2; + break; + case 0x25: + chip->timer_a_reg &= 0x3fc; + chip->timer_a_reg |= chip->write_data & 0x03; + break; + case 0x26: /* Timer B */ + chip->timer_b_reg = chip->write_data & 0xff; + break; + case 0x27: /* CSM, Timer control */ + chip->mode_ch3 = (chip->write_data & 0xc0) >> 6; + chip->mode_csm = chip->mode_ch3 == 2; + chip->timer_a_load = chip->write_data & 0x01; + chip->timer_a_enable = (chip->write_data >> 2) & 0x01; + chip->timer_a_reset = (chip->write_data >> 4) & 0x01; + chip->timer_b_load = (chip->write_data >> 1) & 0x01; + chip->timer_b_enable = (chip->write_data >> 3) & 0x01; + chip->timer_b_reset = (chip->write_data >> 5) & 0x01; + break; + case 0x28: /* Key on/off */ + for (i = 0; i < 4; i++) + { + chip->mode_kon_operator[i] = (chip->write_data >> (4 + i)) & 0x01; + } + if ((chip->write_data & 0x03) == 0x03) + { + /* Invalid address */ + chip->mode_kon_channel = 0xff; + } + else + { + chip->mode_kon_channel = (chip->write_data & 0x03) + ((chip->write_data >> 2) & 1) * 3; + } + break; + case 0x2a: /* DAC data */ + chip->dacdata &= 0x01; + chip->dacdata |= (chip->write_data ^ 0x80) << 1; + break; + case 0x2b: /* DAC enable */ + chip->dacen = chip->write_data >> 7; + break; + case 0x2c: /* LSI test 2 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_2c[i] = (chip->write_data >> i) & 0x01; + } + chip->dacdata &= 0x1fe; + chip->dacdata |= chip->mode_test_2c[3]; + chip->eg_custom_timer = !chip->mode_test_2c[7] && chip->mode_test_2c[6]; + break; + default: + break; + } + } + + /* Address */ + if (chip->write_a_en) + { + chip->write_fm_mode_a = chip->write_data & 0x1ff; + } + } + + if (chip->write_fm_data) + { + chip->data = chip->write_data & 0xff; + } +} + +static void OPN2_PhaseCalcIncrement(ym3438_t *chip) +{ + Bit32u chan = chip->channel; + Bit32u slot = chip->cycles; + Bit32u fnum = chip->pg_fnum; + Bit32u fnum_h = fnum >> 4; + Bit32u fm; + Bit32u basefreq; + Bit8u lfo = chip->lfo_pm; + Bit8u lfo_l = lfo & 0x0f; + Bit8u pms = chip->pms[chan]; + Bit8u dt = chip->dt[slot]; + Bit8u dt_l = dt & 0x03; + Bit8u detune = 0; + Bit8u block, note; + Bit8u sum, sum_h, sum_l; + Bit8u kcode = chip->pg_kcode; + + fnum <<= 1; + /* Apply LFO */ + if (lfo_l & 0x08) + { + lfo_l ^= 0x0f; + } + fm = (fnum_h >> pg_lfo_sh1[pms][lfo_l]) + (fnum_h >> pg_lfo_sh2[pms][lfo_l]); + if (pms > 5) + { + fm <<= pms - 5; + } + fm >>= 2; + if (lfo & 0x10) + { + fnum -= fm; + } + else + { + fnum += fm; + } + fnum &= 0xfff; + + basefreq = (fnum << chip->pg_block) >> 2; + + /* Apply detune */ + if (dt_l) + { + if (kcode > 0x1c) + { + kcode = 0x1c; + } + block = kcode >> 2; + note = kcode & 0x03; + sum = block + 9 + ((dt_l == 3) | (dt_l & 0x02)); + sum_h = sum >> 1; + sum_l = sum & 0x01; + detune = pg_detune[(sum_l << 2) | note] >> (9 - sum_h); + } + if (dt & 0x04) + { + basefreq -= detune; + } + else + { + basefreq += detune; + } + basefreq &= 0x1ffff; + chip->pg_inc[slot] = (basefreq * chip->multi[slot]) >> 1; + chip->pg_inc[slot] &= 0xfffff; +} + +static void OPN2_PhaseGenerate(ym3438_t *chip) +{ + Bit32u slot; + /* Mask increment */ + slot = (chip->cycles + 20) % 24; + if (chip->pg_reset[slot]) + { + chip->pg_inc[slot] = 0; + } + /* Phase step */ + slot = (chip->cycles + 19) % 24; + if (chip->pg_reset[slot] || chip->mode_test_21[3]) + { + chip->pg_phase[slot] = 0; + } + chip->pg_phase[slot] += chip->pg_inc[slot]; + chip->pg_phase[slot] &= 0xfffff; +} + +static void OPN2_EnvelopeSSGEG(ym3438_t *chip) +{ + Bit32u slot = chip->cycles; + Bit8u direction = 0; + chip->eg_ssg_pgrst_latch[slot] = 0; + chip->eg_ssg_repeat_latch[slot] = 0; + chip->eg_ssg_hold_up_latch[slot] = 0; + chip->eg_ssg_inv[slot] = 0; + if (chip->ssg_eg[slot] & 0x08) + { + direction = chip->eg_ssg_dir[slot]; + if (chip->eg_level[slot] & 0x200) + { + /* Reset */ + if ((chip->ssg_eg[slot] & 0x03) == 0x00) + { + chip->eg_ssg_pgrst_latch[slot] = 1; + } + /* Repeat */ + if ((chip->ssg_eg[slot] & 0x01) == 0x00) + { + chip->eg_ssg_repeat_latch[slot] = 1; + } + /* Inverse */ + if ((chip->ssg_eg[slot] & 0x03) == 0x02) + { + direction ^= 1; + } + if ((chip->ssg_eg[slot] & 0x03) == 0x03) + { + direction = 1; + } + } + /* Hold up */ + if (chip->eg_kon_latch[slot] + && ((chip->ssg_eg[slot] & 0x07) == 0x05 || (chip->ssg_eg[slot] & 0x07) == 0x03)) + { + chip->eg_ssg_hold_up_latch[slot] = 1; + } + direction &= chip->eg_kon[slot]; + chip->eg_ssg_inv[slot] = (chip->eg_ssg_dir[slot] ^ ((chip->ssg_eg[slot] >> 2) & 0x01)) + & chip->eg_kon[slot]; + } + chip->eg_ssg_dir[slot] = direction; + chip->eg_ssg_enable[slot] = (chip->ssg_eg[slot] >> 3) & 0x01; +} + +static void OPN2_EnvelopeADSR(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 22) % 24; + + Bit8u nkon = chip->eg_kon_latch[slot]; + Bit8u okon = chip->eg_kon[slot]; + Bit8u kon_event; + Bit8u koff_event; + Bit8u eg_off; + Bit16s level; + Bit16s nextlevel = 0; + Bit16s ssg_level; + Bit8u nextstate = chip->eg_state[slot]; + Bit16s inc = 0; + chip->eg_read[0] = chip->eg_read_inc; + chip->eg_read_inc = chip->eg_inc > 0; + + /* Reset phase generator */ + chip->pg_reset[slot] = (nkon && !okon) || chip->eg_ssg_pgrst_latch[slot]; + + /* KeyOn/Off */ + kon_event = (nkon && !okon) || (okon && chip->eg_ssg_repeat_latch[slot]); + koff_event = okon && !nkon; + + ssg_level = level = (Bit16s)chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + ssg_level = 512 - level; + ssg_level &= 0x3ff; + } + if (koff_event) + { + level = ssg_level; + } + if (chip->eg_ssg_enable[slot]) + { + eg_off = level >> 9; + } + else + { + eg_off = (level & 0x3f0) == 0x3f0; + } + nextlevel = level; + if (kon_event) + { + nextstate = eg_num_attack; + /* Instant attack */ + if (chip->eg_ratemax) + { + nextlevel = 0; + } + else if (chip->eg_state[slot] == eg_num_attack && level != 0 && chip->eg_inc && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + } + else + { + switch (chip->eg_state[slot]) + { + case eg_num_attack: + if (level == 0) + { + nextstate = eg_num_decay; + } + else if(chip->eg_inc && !chip->eg_ratemax && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + break; + case eg_num_decay: + if ((level >> 4) == (chip->eg_sl[1] << 1)) + { + nextstate = eg_num_sustain; + } + else if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + case eg_num_sustain: + case eg_num_release: + if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + default: + break; + } + if (!nkon) + { + nextstate = eg_num_release; + } + } + if (chip->eg_kon_csm[slot]) + { + nextlevel |= chip->eg_tl[1] << 3; + } + + /* Envelope off */ + if (!kon_event && !chip->eg_ssg_hold_up_latch[slot] && chip->eg_state[slot] != eg_num_attack && eg_off) + { + nextstate = eg_num_release; + nextlevel = 0x3ff; + } + + nextlevel += inc; + + chip->eg_kon[slot] = chip->eg_kon_latch[slot]; + chip->eg_level[slot] = (Bit16u)nextlevel & 0x3ff; + chip->eg_state[slot] = nextstate; +} + +static void OPN2_EnvelopePrepare(ym3438_t *chip) +{ + Bit8u rate; + Bit8u sum; + Bit8u inc = 0; + Bit32u slot = chip->cycles; + Bit8u rate_sel; + + /* Prepare increment */ + rate = (chip->eg_rate << 1) + chip->eg_ksv; + + if (rate > 0x3f) + { + rate = 0x3f; + } + + sum = ((rate >> 2) + chip->eg_shift_lock) & 0x0f; + if (chip->eg_rate != 0 && chip->eg_quotient == 2) + { + if (rate < 48) + { + switch (sum) + { + case 12: + inc = 1; + break; + case 13: + inc = (rate >> 1) & 0x01; + break; + case 14: + inc = rate & 0x01; + break; + default: + break; + } + } + else + { + inc = eg_stephi[rate & 0x03][chip->eg_timer_low_lock] + (rate >> 2) - 11; + if (inc > 4) + { + inc = 4; + } + } + } + chip->eg_inc = inc; + chip->eg_ratemax = (rate >> 1) == 0x1f; + + /* Prepare rate & ksv */ + rate_sel = chip->eg_state[slot]; + if ((chip->eg_kon[slot] && chip->eg_ssg_repeat_latch[slot]) + || (!chip->eg_kon[slot] && chip->eg_kon_latch[slot])) + { + rate_sel = eg_num_attack; + } + switch (rate_sel) + { + case eg_num_attack: + chip->eg_rate = chip->ar[slot]; + break; + case eg_num_decay: + chip->eg_rate = chip->dr[slot]; + break; + case eg_num_sustain: + chip->eg_rate = chip->sr[slot]; + break; + case eg_num_release: + chip->eg_rate = (chip->rr[slot] << 1) | 0x01; + break; + default: + break; + } + chip->eg_ksv = chip->pg_kcode >> (chip->ks[slot] ^ 0x03); + if (chip->am[slot]) + { + chip->eg_lfo_am = chip->lfo_am >> eg_am_shift[chip->ams[chip->channel]]; + } + else + { + chip->eg_lfo_am = 0; + } + /* Delay TL & SL value */ + chip->eg_tl[1] = chip->eg_tl[0]; + chip->eg_tl[0] = chip->tl[slot]; + chip->eg_sl[1] = chip->eg_sl[0]; + chip->eg_sl[0] = chip->sl[slot]; +} + +static void OPN2_EnvelopeGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 23) % 24; + Bit16u level; + + level = chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + level = 512 - level; + } + if (chip->mode_test_21[5]) + { + level = 0; + } + level &= 0x3ff; + + /* Apply AM LFO */ + level += chip->eg_lfo_am; + + /* Apply TL */ + if (!(chip->mode_csm && chip->channel == 2 + 1)) + { + level += chip->eg_tl[0] << 3; + } + if (level > 0x3ff) + { + level = 0x3ff; + } + chip->eg_out[slot] = level; +} + +static void OPN2_UpdateLFO(ym3438_t *chip) +{ + if ((chip->lfo_quotient & lfo_cycles[chip->lfo_freq]) == lfo_cycles[chip->lfo_freq]) + { + chip->lfo_quotient = 0; + chip->lfo_cnt++; + } + else + { + chip->lfo_quotient += chip->lfo_inc; + } + chip->lfo_cnt &= chip->lfo_en; +} + +static void OPN2_FMPrepare(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 6) % 24; + Bit32u channel = chip->channel; + Bit16s mod, mod1, mod2; + Bit32u op = slot / 6; + Bit8u connect = chip->connect[channel]; + Bit32u prevslot = (chip->cycles + 18) % 24; + + /* Calculate modulation */ + mod1 = mod2 = 0; + + if (fm_algorithm[op][0][connect]) + { + mod2 |= chip->fm_op1[channel][0]; + } + if (fm_algorithm[op][1][connect]) + { + mod1 |= chip->fm_op1[channel][1]; + } + if (fm_algorithm[op][2][connect]) + { + mod1 |= chip->fm_op2[channel]; + } + if (fm_algorithm[op][3][connect]) + { + mod2 |= chip->fm_out[prevslot]; + } + if (fm_algorithm[op][4][connect]) + { + mod1 |= chip->fm_out[prevslot]; + } + mod = mod1 + mod2; + if (op == 0) + { + /* Feedback */ + mod = mod >> (10 - chip->fb[channel]); + if (!chip->fb[channel]) + { + mod = 0; + } + } + else + { + mod >>= 1; + } + chip->fm_mod[slot] = mod; + + slot = (chip->cycles + 18) % 24; + /* OP1 */ + if (slot / 6 == 0) + { + chip->fm_op1[channel][1] = chip->fm_op1[channel][0]; + chip->fm_op1[channel][0] = chip->fm_out[slot]; + } + /* OP2 */ + if (slot / 6 == 2) + { + chip->fm_op2[channel] = chip->fm_out[slot]; + } +} + +static void OPN2_ChGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 18) % 24; + Bit32u channel = chip->channel; + Bit32u op = slot / 6; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s acc = chip->ch_acc[channel]; + Bit16s add = test_dac; + Bit16s sum = 0; + if (op == 0 && !test_dac) + { + acc = 0; + } + if (chip->chip_type & ym3438_mode_opn) { + if (fm_algorithm[op][5][chip->connect[channel]] && !test_dac) + { + add += chip->fm_out[slot]; + } + sum = acc + add; + } else { + if (fm_algorithm[op][5][chip->connect[channel]] && !test_dac) + { + add += chip->fm_out[slot] >> 5; + } + sum = acc + add; + /* Clamp */ + if (sum > 255) + { + sum = 255; + } + else if(sum < -256) + { + sum = -256; + } + } + + if (op == 0 || test_dac) + { + chip->ch_out[channel] = chip->ch_acc[channel]; + } + chip->ch_acc[channel] = sum; +} + +static void OPN2_ChOutput(ym3438_t *chip) +{ + Bit32u cycles = chip->cycles; + Bit32u slot = chip->cycles; + Bit32u channel = chip->channel; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s out; + Bit16s sign; + Bit32u out_en; + chip->ch_read = chip->ch_lock; + if (slot < 12) + { + /* Ch 4,5,6 */ + channel++; + } + if ((cycles & 3) == 0) + { + if (!test_dac) + { + /* Lock value */ + chip->ch_lock = chip->ch_out[channel]; + } + chip->ch_lock_l = chip->pan_l[channel]; + chip->ch_lock_r = chip->pan_r[channel]; + } + /* Ch 6 */ + if (((cycles >> 2) == 1 && chip->dacen) || test_dac) + { + out = (Bit16s)chip->dacdata; + out = SIGN_EXTEND(8, out); + } + else + { + out = chip->ch_lock; + } + chip->mol = 0; + chip->mor = 0; + + if (chip->chip_type & ym3438_mode_ym2612) + { + out_en = ((cycles & 3) == 3) || test_dac; + /* YM2612 DAC emulation(not verified) */ + sign = out >> 8; + if (out >= 0) + { + out++; + sign++; + } + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + else + { + chip->mol = sign; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + else + { + chip->mor = sign; + } + /* Amplify signal */ + chip->mol *= 3; + chip->mor *= 3; + } + else + { + out_en = ((cycles & 3) != 0) || test_dac; + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + } +} + +static void OPN2_FMGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 19) % 24; + /* Calculate phase */ + Bit16u phase = (chip->fm_mod[slot] + (chip->pg_phase[slot] >> 10)) & 0x3ff; + Bit16u quarter; + Bit16u level; + Bit16s output; + if (phase & 0x100) + { + quarter = (phase ^ 0xff) & 0xff; + } + else + { + quarter = phase & 0xff; + } + level = logsinrom[quarter]; + /* Apply envelope */ + level += chip->eg_out[slot] << 2; + /* Transform */ + if (level > 0x1fff) + { + level = 0x1fff; + } + output = ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 2) >> (level >> 8); + if (phase & 0x200) + { + output = ((~output) ^ (chip->mode_test_21[4] << 13)) + 1; + } + else + { + output = output ^ (chip->mode_test_21[4] << 13); + } + output = SIGN_EXTEND(13, output); + chip->fm_out[slot] = output; +} + +static void OPN2_DoTimerA(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_a_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_a_load_lock && chip->timer_a_load); + chip->timer_a_load_lock = chip->timer_a_load; + if (chip->mode_csm) + { + /* CSM KeyOn */ + chip->mode_kon_csm = load; + } + else + { + chip->mode_kon_csm = 0; + } + } + /* Load counter */ + if (chip->timer_a_load_latch) + { + time = chip->timer_a_reg; + } + else + { + time = chip->timer_a_cnt; + } + chip->timer_a_load_latch = load; + /* Increase counter */ + if ((chip->cycles == 1 && chip->timer_a_load_lock) || chip->mode_test_21[2]) + { + time++; + } + /* Set overflow flag */ + if (chip->timer_a_reset) + { + chip->timer_a_reset = 0; + chip->timer_a_overflow_flag = 0; + } + else + { + chip->timer_a_overflow_flag |= chip->timer_a_overflow & chip->timer_a_enable; + } + chip->timer_a_overflow = (time >> 10); + chip->timer_a_cnt = time & 0x3ff; +} + +static void OPN2_DoTimerB(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_b_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_b_load_lock && chip->timer_b_load); + chip->timer_b_load_lock = chip->timer_b_load; + } + /* Load counter */ + if (chip->timer_b_load_latch) + { + time = chip->timer_b_reg; + } + else + { + time = chip->timer_b_cnt; + } + chip->timer_b_load_latch = load; + /* Increase counter */ + if (chip->cycles == 1) + { + chip->timer_b_subcnt++; + } + if ((chip->timer_b_subcnt == 0x10 && chip->timer_b_load_lock) || chip->mode_test_21[2]) + { + time++; + } + chip->timer_b_subcnt &= 0x0f; + /* Set overflow flag */ + if (chip->timer_b_reset) + { + chip->timer_b_reset = 0; + chip->timer_b_overflow_flag = 0; + } + else + { + chip->timer_b_overflow_flag |= chip->timer_b_overflow & chip->timer_b_enable; + } + chip->timer_b_overflow = (time >> 8); + chip->timer_b_cnt = time & 0xff; +} + +static void OPN2_KeyOn(ym3438_t*chip) +{ + Bit32u slot = chip->cycles; + Bit32u chan = chip->channel; + /* Key On */ + chip->eg_kon_latch[slot] = chip->mode_kon[slot]; + chip->eg_kon_csm[slot] = 0; + if (chip->channel == 2 && chip->mode_kon_csm) + { + /* CSM Key On */ + chip->eg_kon_latch[slot] = 1; + chip->eg_kon_csm[slot] = 1; + } + if (chip->cycles == chip->mode_kon_channel) + { + /* OP1 */ + chip->mode_kon[chan] = chip->mode_kon_operator[0]; + /* OP2 */ + chip->mode_kon[chan + 12] = chip->mode_kon_operator[1]; + /* OP3 */ + chip->mode_kon[chan + 6] = chip->mode_kon_operator[2]; + /* OP4 */ + chip->mode_kon[chan + 18] = chip->mode_kon_operator[3]; + } +} + +void OPN2_Reset(ym3438_t *chip) +{ + Bit32u i; + memset(chip, 0, sizeof(ym3438_t)); + chip->chip_type = ym3438_mode_readmode; + for (i = 0; i < 24; i++) + { + chip->eg_out[i] = 0x3ff; + chip->eg_level[i] = 0x3ff; + chip->eg_state[i] = eg_num_release; + chip->multi[i] = 1; + } + for (i = 0; i < 6; i++) + { + chip->pan_l[i] = 1; + chip->pan_r[i] = 1; + } +} + +void OPN2_SetChipType(ym3438_t *chip, Bit32u type) +{ + chip->chip_type = type; +} + +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer) +{ + Bit32u slot = chip->cycles; + chip->lfo_inc = chip->mode_test_21[1]; + chip->pg_read >>= 1; + chip->eg_read[1] >>= 1; + chip->eg_cycle++; + /* Lock envelope generator timer value */ + if (chip->cycles == 1 && chip->eg_quotient == 2) + { + if (chip->eg_cycle_stop) + { + chip->eg_shift_lock = 0; + } + else + { + chip->eg_shift_lock = chip->eg_shift + 1; + } + chip->eg_timer_low_lock = chip->eg_timer & 0x03; + } + /* Cycle specific functions */ + switch (chip->cycles) + { + case 0: + chip->lfo_pm = chip->lfo_cnt >> 2; + if (chip->lfo_cnt & 0x40) + { + chip->lfo_am = chip->lfo_cnt & 0x3f; + } + else + { + chip->lfo_am = chip->lfo_cnt ^ 0x3f; + } + chip->lfo_am <<= 1; + break; + case 1: + chip->eg_quotient++; + chip->eg_quotient %= 3; + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer_inc |= chip->eg_quotient >> 1; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 2: + chip->pg_read = chip->pg_phase[21] & 0x3ff; + chip->eg_read[1] = chip->eg_out[0]; + break; + case 13: + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 23: + chip->lfo_inc |= 1; + break; + } + chip->eg_timer &= ~(chip->mode_test_21[5] << chip->eg_cycle); + if (((chip->eg_timer >> chip->eg_cycle) | (chip->pin_test_in & chip->eg_custom_timer)) & chip->eg_cycle_stop) + { + chip->eg_shift = chip->eg_cycle; + chip->eg_cycle_stop = 0; + } + + OPN2_DoIO(chip); + + OPN2_DoTimerA(chip); + OPN2_DoTimerB(chip); + OPN2_KeyOn(chip); + + OPN2_ChOutput(chip); + OPN2_ChGenerate(chip); + + OPN2_FMPrepare(chip); + OPN2_FMGenerate(chip); + + OPN2_PhaseGenerate(chip); + OPN2_PhaseCalcIncrement(chip); + + OPN2_EnvelopeADSR(chip); + OPN2_EnvelopeGenerate(chip); + OPN2_EnvelopeSSGEG(chip); + OPN2_EnvelopePrepare(chip); + + /* Prepare fnum & block */ + if (chip->mode_ch3) + { + /* Channel 3 special mode */ + switch (slot) + { + case 1: /* OP1 */ + chip->pg_fnum = chip->fnum_3ch[1]; + chip->pg_block = chip->block_3ch[1]; + chip->pg_kcode = chip->kcode_3ch[1]; + break; + case 7: /* OP3 */ + chip->pg_fnum = chip->fnum_3ch[0]; + chip->pg_block = chip->block_3ch[0]; + chip->pg_kcode = chip->kcode_3ch[0]; + break; + case 13: /* OP2 */ + chip->pg_fnum = chip->fnum_3ch[2]; + chip->pg_block = chip->block_3ch[2]; + chip->pg_kcode = chip->kcode_3ch[2]; + break; + case 19: /* OP4 */ + default: + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + break; + } + } + else + { + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + } + + OPN2_UpdateLFO(chip); + OPN2_DoRegWrite(chip); + chip->cycles = (chip->cycles + 1) % 24; + chip->channel = chip->cycles % 6; + + buffer[0] = chip->mol; + buffer[1] = chip->mor; + + if (chip->status_time) + chip->status_time--; +} + +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data) +{ + port &= 3; + chip->write_data = ((port << 7) & 0x100) | data; + if (port & 1) + { + /* Data */ + chip->write_d |= 1; + } + else + { + /* Address */ + chip->write_a |= 1; + } +} + +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value) +{ + chip->pin_test_in = value & 1; +} + +Bit32u OPN2_ReadTestPin(ym3438_t *chip) +{ + if (!chip->mode_test_2c[7]) + { + return 0; + } + return chip->cycles == 23; +} + +Bit32u OPN2_ReadIRQPin(ym3438_t *chip) +{ + return chip->timer_a_overflow_flag | chip->timer_b_overflow_flag; +} + +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) +{ + if ((port & 3) == 0 || (chip->chip_type & ym3438_mode_readmode)) + { + if (chip->mode_test_21[6]) + { + /* Read test data */ + Bit32u slot = (chip->cycles + 18) % 24; + Bit16u testdata = ((chip->pg_read & 0x01) << 15) + | ((chip->eg_read[chip->mode_test_21[0]] & 0x01) << 14); + if (chip->mode_test_2c[4]) + { + testdata |= chip->ch_read & 0x1ff; + } + else + { + testdata |= chip->fm_out[slot] & 0x3fff; + } + if (chip->mode_test_21[7]) + { + chip->status = testdata & 0xff; + } + else + { + chip->status = testdata >> 8; + } + } + else + { + chip->status = (chip->busy << 7) | (chip->timer_b_overflow_flag << 1) + | chip->timer_a_overflow_flag; + } + if (chip->chip_type & ym3438_mode_ym2612) + { + chip->status_time = 300000; + } + else + { + chip->status_time = 40000000; + } + } + if (chip->status_time) + { + return chip->status; + } + return 0; +} diff --git a/extern/opn/ym3438.h b/extern/opn/ym3438.h new file mode 100644 index 00000000..955b7317 --- /dev/null +++ b/extern/opn/ym3438.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2017-2021 Alexey Khokholov (Nuke.YKT) + * + * This file is part of Nuked OPN2. + * + * 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 + * + * Nuked OPN2(Yamaha YM3438) emulator. + * Thanks: + * Silicon Pr0n: + * Yamaha YM3438 decap and die shot(digshadow). + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.0.9 + */ + +#ifndef YM3438_H +#define YM3438_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + ym3438_mode_ym2612 = 0x01, /* Enables YM2612 emulation (MD1, MD2 VA2) */ + ym3438_mode_readmode = 0x02, /* Enables status read on any port (TeraDrive, MD1 VA7, MD2, etc) */ + ym3438_mode_opn = 0x04 /* Outputs full, unshifted signal per channel (OPN/OPNA/OPNB) */ +}; + +#include + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +typedef struct +{ + Bit32u cycles; + Bit32u channel; + Bit16s mol, mor; + /* IO */ + Bit16u write_data; + Bit8u write_a; + Bit8u write_d; + Bit8u write_a_en; + Bit8u write_d_en; + Bit8u write_busy; + Bit8u write_busy_cnt; + Bit8u write_fm_address; + Bit8u write_fm_data; + Bit16u write_fm_mode_a; + Bit16u address; + Bit8u data; + Bit8u pin_test_in; + Bit8u pin_irq; + Bit8u busy; + /* LFO */ + Bit8u lfo_en; + Bit8u lfo_freq; + Bit8u lfo_pm; + Bit8u lfo_am; + Bit8u lfo_cnt; + Bit8u lfo_inc; + Bit8u lfo_quotient; + /* Phase generator */ + Bit16u pg_fnum; + Bit8u pg_block; + Bit8u pg_kcode; + Bit32u pg_inc[24]; + Bit32u pg_phase[24]; + Bit8u pg_reset[24]; + Bit32u pg_read; + /* Envelope generator */ + Bit8u eg_cycle; + Bit8u eg_cycle_stop; + Bit8u eg_shift; + Bit8u eg_shift_lock; + Bit8u eg_timer_low_lock; + Bit16u eg_timer; + Bit8u eg_timer_inc; + Bit16u eg_quotient; + Bit8u eg_custom_timer; + Bit8u eg_rate; + Bit8u eg_ksv; + Bit8u eg_inc; + Bit8u eg_ratemax; + Bit8u eg_sl[2]; + Bit8u eg_lfo_am; + Bit8u eg_tl[2]; + Bit8u eg_state[24]; + Bit16u eg_level[24]; + Bit16u eg_out[24]; + Bit8u eg_kon[24]; + Bit8u eg_kon_csm[24]; + Bit8u eg_kon_latch[24]; + Bit8u eg_csm_mode[24]; + Bit8u eg_ssg_enable[24]; + Bit8u eg_ssg_pgrst_latch[24]; + Bit8u eg_ssg_repeat_latch[24]; + Bit8u eg_ssg_hold_up_latch[24]; + Bit8u eg_ssg_dir[24]; + Bit8u eg_ssg_inv[24]; + Bit32u eg_read[2]; + Bit8u eg_read_inc; + /* Settings */ + Bit8u chip_type; + /* FM */ + Bit16s fm_op1[6][2]; + Bit16s fm_op2[6]; + Bit16s fm_out[24]; + Bit16u fm_mod[24]; + /* Channel */ + Bit16s ch_acc[6]; + Bit16s ch_out[6]; + Bit16s ch_lock; + Bit8u ch_lock_l; + Bit8u ch_lock_r; + Bit16s ch_read; + /* Timer */ + Bit16u timer_a_cnt; + Bit16u timer_a_reg; + Bit8u timer_a_load_lock; + Bit8u timer_a_load; + Bit8u timer_a_enable; + Bit8u timer_a_reset; + Bit8u timer_a_load_latch; + Bit8u timer_a_overflow_flag; + Bit8u timer_a_overflow; + + Bit16u timer_b_cnt; + Bit8u timer_b_subcnt; + Bit16u timer_b_reg; + Bit8u timer_b_load_lock; + Bit8u timer_b_load; + Bit8u timer_b_enable; + Bit8u timer_b_reset; + Bit8u timer_b_load_latch; + Bit8u timer_b_overflow_flag; + Bit8u timer_b_overflow; + + /* Register set */ + Bit8u mode_test_21[8]; + Bit8u mode_test_2c[8]; + Bit8u mode_ch3; + Bit8u mode_kon_channel; + Bit8u mode_kon_operator[4]; + Bit8u mode_kon[24]; + Bit8u mode_csm; + Bit8u mode_kon_csm; + Bit8u dacen; + Bit16s dacdata; + + Bit8u ks[24]; + Bit8u ar[24]; + Bit8u sr[24]; + Bit8u dt[24]; + Bit8u multi[24]; + Bit8u sl[24]; + Bit8u rr[24]; + Bit8u dr[24]; + Bit8u am[24]; + Bit8u tl[24]; + Bit8u ssg_eg[24]; + + Bit16u fnum[6]; + Bit8u block[6]; + Bit8u kcode[6]; + Bit16u fnum_3ch[6]; + Bit8u block_3ch[6]; + Bit8u kcode_3ch[6]; + Bit8u reg_a4; + Bit8u reg_ac; + Bit8u connect[6]; + Bit8u fb[6]; + Bit8u pan_l[6], pan_r[6]; + Bit8u ams[6]; + Bit8u pms[6]; + Bit8u status; + Bit32u status_time; +} ym3438_t; + +void OPN2_Reset(ym3438_t *chip); +void OPN2_SetChipType(ym3438_t *chip, Bit32u type); +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer); +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value); +Bit32u OPN2_ReadTestPin(ym3438_t *chip); +Bit32u OPN2_ReadIRQPin(ym3438_t *chip); +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/opn/ym3438.svg b/extern/opn/ym3438.svg new file mode 100644 index 00000000..d1d86667 --- /dev/null +++ b/extern/opn/ym3438.svg @@ -0,0 +1,16454 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2CLSITEST + daSSSs e + 2ADAC + + 21LSITEST + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Read En + Busy flag + + + + + + + + + + A1 + IC + RD + A0 + CS + WR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LFO Counter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AMS + + AMS + + + + + + + + + + + + + + AM + + EG out + + TL + + + + + + + + + + + + + + + + + + + + + + + + + + + AM DR + + SL RR + + DT + MULTI + + SR + + RS KS + + AR + + SSG-EG + + AMS + + Data + + + + + + + + + + M + + GND + + D0 + + D1 + + D2 + + D3 + + D4 + + D5 + + D6 + + D7 + + TEST + + IC + + GND + + IRQ + + CS + + WR + + RD + + A0 + + A1 + + AGND + + MOR + + MOL + + AVcc + + VCC + MSB + + + + /D7 + D7 + + + + /D6 + D6 + + + /D5 + D5 + + + /D4 + D4 + /D3 + D3 + + + + + + 30 + + 40 + + + 60 + 50 + + 70 + + 80 + + 90 + + + + + + + + + + + PMS + FMS + R + L + + ALG + + Feedback + + + + + + + + + + D4 + /D4 + D3 + /D3 + D2 + /D2 + + + + + B4 + + B0 + + AC + + A8 + + A4 + + A0 + AC + + + Blck FN Hi + + CH3 Blck FN Hi + + CH3 FN Low + + FN Low + + Blck FN Hi + Data + Address + + + + + + + enable + + CS + WR + bank + IC + RD + A0 + Read Enable + write addr + IC + write data + + + + + + + + + + + + Read Mode + + + + Timer A + Timer B + Timer A + Timer B + + IRQ + Read Status + Timer A + Timer B + + + + + + + + + + + + + + + + + Write Data + Write address + + + + Write Data + + + + + + Address + Data + + + + Bank + + + 2BD EN + + 22LFO + + 24TM AMSB + + 25TM A + + MSB + MSB + MSB + + 22 + 26 + + 27 + + 25 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + + 24 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + + 28 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + + 28KeyOn + + 27CH3 + + 26TM B + + EG Rate select + + EG Rate Select + + Rate + + Rate + + KS + + KS + + + KCODE + + KCODE + + + FNUM + + NT + BLCK + + KC Latch + + MUL Latch + + MULT + + + + Feedback + + Algorithm + + + + Exponent + Sine + DACMODE + DAC EN + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + DAC Data + + + + + + + + + + + + DACEN + + DACEn + + + + + PAN + + + + + + + + Note + + PHASE + + EG + + ReadData + Out + + MSB + + + + + + + + + + + AND + + AND + + + + + + + + + + + + + + + MSB + b0 + b2 + b1 + n0 + n1 + DT0 + DT1 + DT2 + + DT0 | DT1 + + DT0 & DT1 + + + + + + + sum0 + + N0N1S0 + + !N0N1S0 + + N0!N1S0 + + !N0!N1S0 + + N0N1!S0 + + !N0N1!S0 + + + N0!N1!S0 + + !N0!N1!S0 + MSB + d0 + d1 + d2 + + + + d3 + + + + + + + + + + + + + Rising + + + 0 + + 1 + + 2 + + 3 + + 4 + + 5 + + 6 + + 7 + + 8 + + 9 + + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 3 + 4 + 1 + 2 + 3 + 4 + + + + 9 + 8 + 7 + + + + + 0 + + 1 + + 2 + + 3 + + 4 + + 5 + + 6 + + 7 + + + + + + + + FMS + MSB + + + + + + + 001 + + 100 + + 011 + + 010 + + 101 + + 111 + + 110 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A or (A << 1) (A << 1) or(A << 2) 1 + 0 + 2 + 1 + 2 + + + + + + + + + + + + + + 11X + + >=5 + + + + <=5 ==7 + ==6 + Enable + + + Repeat + + + + + + XX11 + + XX10 + + XX00 + + + + + + X101X011 + + XXX0 + + + + + + + + + + + + + + + + Enable + Inverse + XX11 + X101 X011 + XXX0 + XX10 + XX00 + + + + + + Timer A + + Timer B + KeyOn + + + 0 + + 1 + + 2 + 3 + 4 + 5 + 6 + 7 + 0 + + 2 + + 3 + + 4 + + 5 + + 6 + + 7 + + 8 + + 9 + + 10 + + 11 + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 1011 + 0 + 1 + 2 + 3 + 0 + 1 + 2 + 2 + 0 + 1 + + + + + + + + 0 + + 1 + + 2 + + 3 + + 4 + + 5 + + 6 + + 7 + + 5,6,7 + + 4,5,67 + + 2,5 + + 1,5 + + 0,34,5,6 + + + 0,1,3,4 + 0,1,2 + + OP2 Out + OP3 Out + + OperatorAccumulator + + Switch + + Op 1 + + Op3 + + Op2 + + Op4 + + + + + Op4Select + Op1Select + Op3Select + + Op2Select + + + + + + + + + + + + + 0 + 1 + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 6-11 + 0-5 + 12-17 + 18-23 + + 18-23 + + 0,4,8,12,16,20 + + 12-17 + + 2,8,14,20 + + 4-7 + + + Rise + + 0 + 1 + 2 + 3 + 0 + 1 + 2 + 3 + + + + + + + + + + + + + + + + + 0.5 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + + 0.5 + + + 1,3,5,7,9,1113 + + Switch + + 2,36,710,11 + + 4,5,6,7 + + 8,9,10,11,12,13 + + + 15 + 14 + + 14,15 + + 12,13 + + + Frequency + Frequency + + + + + 0 + 1 + 2 + + X00 + + X01 + + X10 + + X11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 14,15 + Reset + + M And + + PG Reset + + TL + + + TL + + AND OR + MSB + n0 + n1 + b0 + b1 + b2 + n0 + n1 + b0 + b1 + b2 + + 11->n010->n101->b000->b1 + + 11->n110->b001->b100->b2 + + 11->b010->b101->b2 + + 11->b110->b2 + + 11->b2 + 0 + + Non Zero + + + + + + + + + + + <12 + + + + + Max Rate + + + + + + + 1111XX + + 1110XX + + 1101XX + + 1100XX + + Non zero + + Clock 0 + + + 0,12or IC + + EG LSI + IC + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + + 0,2,4,6,8,10 + + 1,2,5,6,9,10 + + 3,4,5,6,11 + + 7,8,9,10,11 + + + + + + + + + + + + + + + + + + + LFO En + + + Bank + + + + + Top 4 non zero + + + AND OR + + IC + + AND OR + + + + + + + + + + + + + + + + + Exponent + + 3 + + 2 + + + 1 + 0 + + + + + + + + + + + Linear + + SSG Enable + + + + + + + + + + + + AND OR + + + + PG Reset + + 00 - AR + + 01 - DR + + 10-SR + + 11-RR + + + SL + + + + + + + + + + + + + + + + Sustain reach + 111111xxxx + + 00000xxxxx + + + Zero att + + + + AND OR + + Key on + + Key Off + + Attack!KonEvZeroAtt + Decay!KonEvSL reach + + + Release!KonEv + + + + + + CSM + CH3 mode + CH3 mode + + Load A + + Load B + + + Timer A inc + + Timer A LoadRise + + Timer AOverflow + + Timer Reset + + CSM Key + CSM + + CH select + CSM Key On + CH3 + + Is not CSM + + Key latch + + + Envelope off + + SL + + IC + + + + No Attack + + + + + LSI Test + + EG invert + + + Inverse + + Bit9xx10 + Bit9xx11 + + Non attack + Hold up + + Hold up + bit9xx00 + + Reset + + PG Reset + + bit9xxx0 + PG reset + Hold + + + Bank 1 + + Bank 2 + + + + + 2 + + 1 + + 0 + + 1 + + 2 + + + + FB ac + 3 + + 4 + + 5 + + 6 + + 7 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + + + OP2 + + OP1 + + OP1 + + OP1 + + + + + cur2 + + + + 19-24 + + + OP2 + OP1 old + + OP2Mod + + Clock + + + TimerLow + Rate 11Timer 01 + + Rate 01Timer 00 + + + + Rate 1XTimer X0 + + + + IC + + A0 + + A1 + + A2 + + A8 + + + + + And OR + + + + + + + + A0 + + A1 + + A9 + + + Standard + + OP2 + + OP3 + + OP1 + + Ch3 OP1 + + + + + + + + + + LFOenable + + + + LFO0 + LFO1 + LFO2 + + + LFOfreq + L + + 0 + + 1 + + + 2 + 3 + + 4 + + 5 + + 6 + + 7 + + 0 + + 1 + + 2 + + 3 + + 4 + + 5 + 6 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + Delay 5 + PG Freeze + + + !PG reset + + !PG reset& !TEST + + + + + Phaseread,cycle 2 + + Nonzero + Non zr<1213 + + Non zr<1212 + + Non zr<12 + 14 + + >12incr + + Inc 1 + + + Inc 2 + Inc 3 + + AttackKeyOn!ZeroAttn!RateMax + + + Key + + KONSSG en + + SSG inverse + + 1 + + 4 + + 2 + + 3 + + + Kon + + + + + + + + + + + + Cycle 2 + + 1 + + 2 + hold up + + Switchor + + Decay!KonEv!SL reach + Cur1 + + + + + Lock + + + Neg + + Load A + + Load B + + EnableA + + EnableB + + + ResetB + ResetA + Timer Test + Clock 1 + + + !ResetEnableOverflow + + + Load Up + + !ResetOvrfEnable + 0 + 1 + 2 + 3 + 4 + 5 + 6 + + 0 + 1 + 2 + 3 + OP1 Old + + Op2 + + OP1 + cur2 + cur1 + Op2 + diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 0017ea23..5768bde3 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -248,16 +248,20 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_FULL: dispatch=new DivPlatformYM2610; + ((DivPlatformYM2610*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1); break; case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL_EXT: dispatch=new DivPlatformYM2610Ext; + ((DivPlatformYM2610Ext*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1); break; case DIV_SYSTEM_YM2610B: dispatch=new DivPlatformYM2610B; + ((DivPlatformYM2610B*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1); break; case DIV_SYSTEM_YM2610B_EXT: dispatch=new DivPlatformYM2610BExt; + ((DivPlatformYM2610BExt*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1); break; case DIV_SYSTEM_AMIGA: dispatch=new DivPlatformAmiga; @@ -277,15 +281,19 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do break; case DIV_SYSTEM_YM2203: dispatch=new DivPlatformYM2203; + ((DivPlatformYM2203*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1); break; case DIV_SYSTEM_YM2203_EXT: dispatch=new DivPlatformYM2203Ext; + ((DivPlatformYM2203Ext*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1); break; case DIV_SYSTEM_YM2608: dispatch=new DivPlatformYM2608; + ((DivPlatformYM2608*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1); break; case DIV_SYSTEM_YM2608_EXT: dispatch=new DivPlatformYM2608Ext; + ((DivPlatformYM2608Ext*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1); break; case DIV_SYSTEM_OPLL: case DIV_SYSTEM_OPLL_DRUMS: diff --git a/src/engine/platform/fmshared_OPN.h b/src/engine/platform/fmshared_OPN.h index e24b9cb3..630a4970 100644 --- a/src/engine/platform/fmshared_OPN.h +++ b/src/engine/platform/fmshared_OPN.h @@ -21,6 +21,7 @@ #define _FMSHARED_OPN_H #include "fmsharedbase.h" +#include "../../../extern/opn/ym3438.h" #define PLEASE_HELP_ME(_targetChan) \ int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); \ @@ -153,7 +154,7 @@ class DivPlatformOPN: public DivPlatformFMBase { unsigned int ayDiv; unsigned char csmChan; unsigned char lfoValue; - bool extSys; + bool extSys, useCombo; DivConfig ayFlags; @@ -171,8 +172,12 @@ class DivPlatformOPN: public DivPlatformFMBase { ayDiv(a), csmChan(cc), lfoValue(0), - extSys(isExtSys) {} - + extSys(isExtSys), + useCombo(false) {} + public: + void setCombo(bool combo) { + useCombo=combo; + } }; #endif diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 76294524..2fb6e53b 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -1150,7 +1150,7 @@ void DivPlatformGenesis::reset() { fm_ymfm->reset(); } OPN2_Reset(&fm); - OPN2_SetChipType(ladder?ym3438_mode_ym2612:0); + OPN2_SetChipType(&fm,ladder?ym3438_mode_ym2612:0); if (dumpWrites) { addWrite(0xffffffff,0); } @@ -1248,7 +1248,7 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) { } ladder=flags.getBool("ladderEffect",false); noExtMacros=flags.getBool("noExtMacros",false); - OPN2_SetChipType(ladder?ym3438_mode_ym2612:0); + OPN2_SetChipType(&fm,ladder?ym3438_mode_ym2612:0); CHECK_CUSTOM_CLOCK; if (useYMFM) { if (fm_ymfm!=NULL) delete fm_ymfm; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 2a7b2c8d..d6608382 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -21,7 +21,6 @@ #define _GENESIS_H #include "fmshared_OPN.h" -#include "../../../extern/Nuked-OPN2/ym3438.h" #include "sound/ymfm/ymfm_opn.h" diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index d31c58f4..a968d49d 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -157,6 +157,80 @@ const char** DivPlatformYM2203::getRegisterSheet() { } void DivPlatformYM2203::acquire(short* bufL, short* bufR, size_t start, size_t len) { + if (useCombo) { + acquire_combo(bufL,bufR,start,len); + } else { + acquire_ymfm(bufL,bufR,start,len); + } +} + +void DivPlatformYM2203::acquire_combo(short* bufL, short* bufR, size_t start, size_t len) { + static int os; + static short ignored[2]; + + for (size_t h=start; hread(0)&0x80)) { + QueuedWrite& w=writes.front(); + + if (w.addr<=0x1c || w.addr==0x2d || w.addr==0x2e || w.addr==0x2f) { + // ymfm write + fm->write(0x0,w.addr); + fm->write(0x1,w.val); + + regPool[w.addr&0xff]=w.val; + writes.pop_front(); + delay=1; + } else { + // Nuked write + if (w.addrOrVal) { + OPN2_Write(&fm_nuked,0x1,w.val); + regPool[w.addr&0xff]=w.val; + writes.pop_front(); + } else { + lastBusy++; + if (fm_nuked.write_busy==0) { + OPN2_Write(&fm_nuked,0x0,w.addr); + w.addrOrVal=true; + } + } + } + } + } + + OPN2_Clock(&fm_nuked,ignored); + } + os=( + (fm_nuked.ch_out[0])+ + (fm_nuked.ch_out[1])+ + (fm_nuked.ch_out[2]) + ); + + os&=~3; + + // ymfm part + fm->generate(&fmout); + + os+=((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1); + if (os<-32768) os=-32768; + if (os>32767) os=32767; + + bufL[h]=os; + + for (int i=0; i<3; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]; + } + + for (int i=3; i<6; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]; + } + } +} + +void DivPlatformYM2203::acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len) { static int os; ymfm::ym2203::fm_engine* fme=fm->debug_fm_engine(); @@ -857,6 +931,8 @@ void DivPlatformYM2203::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } + OPN2_Reset(&fm_nuked); + OPN2_SetChipType(&fm_nuked,ym3438_mode_opn); fm->reset(); for (int i=0; i<6; i++) { chan[i]=DivPlatformOPN::OPNChannel(); @@ -946,18 +1022,21 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) { fmFreqBase=4720270.0/2.0, fmDivBase=18, ayDiv=8; + nukedMult=16; break; case 0x02: // /2 prescale=0x2f; fmFreqBase=4720270.0/3.0, fmDivBase=12, ayDiv=4; + nukedMult=24; break; default: // /6 prescale=0x2d; fmFreqBase=4720270.0, fmDivBase=36, ayDiv=16; + nukedMult=8; break; } CHECK_CUSTOM_CLOCK; diff --git a/src/engine/platform/ym2203.h b/src/engine/platform/ym2203.h index 0e0da7e4..3d4670f7 100644 --- a/src/engine/platform/ym2203.h +++ b/src/engine/platform/ym2203.h @@ -42,6 +42,7 @@ class DivPlatformYM2203: public DivPlatformOPN { OPNChannel chan[6]; DivDispatchOscBuffer* oscBuf[6]; bool isMuted[6]; + ym3438_t fm_nuked; ymfm::ym2203* fm; ymfm::ym2203::output_data fmout; DivYM2203Interface iface; @@ -50,9 +51,13 @@ class DivPlatformYM2203: public DivPlatformOPN { unsigned char sampleBank; bool extMode, noExtMacros; - unsigned char prescale; + unsigned char prescale, nukedMult; friend void putDispatchChip(void*,int); + + void acquire_combo(short* bufL, short* bufR, size_t start, size_t len); + void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len); + public: void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 98248472..924b6dbb 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -298,6 +298,116 @@ double DivPlatformYM2608::NOTE_ADPCMB(int note) { } void DivPlatformYM2608::acquire(short* bufL, short* bufR, size_t start, size_t len) { + if (useCombo) { + acquire_combo(bufL,bufR,start,len); + } else { + acquire_ymfm(bufL,bufR,start,len); + } +} + +void DivPlatformYM2608::acquire_combo(short* bufL, short* bufR, size_t start, size_t len) { + static int os[2]; + static short ignored[2]; + + ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); + ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); + ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); + + ymfm::ssg_engine::output_data ssgOut; + + ymfm::adpcm_a_channel* adpcmAChan[6]; + for (int i=0; i<6; i++) { + adpcmAChan[i]=aae->debug_channel(i); + } + + for (size_t h=start; hread(0)&0x80)) { + QueuedWrite& w=writes.front(); + + if (w.addr<=0x1c || w.addr==0x2d || w.addr==0x2e || w.addr==0x2f || (w.addr>=0x100 && w.addr<=0x12d)) { + // ymfm write + fm->write(0x0+((w.addr>>8)<<1),w.addr); + fm->write(0x1+((w.addr>>8)<<1),w.val); + + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + delay=1; + } else { + // Nuked write + if (w.addrOrVal) { + OPN2_Write(&fm_nuked,0x1+((w.addr>>8)<<1),w.val); + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + } else { + lastBusy++; + if (fm_nuked.write_busy==0) { + OPN2_Write(&fm_nuked,0x0+((w.addr>>8)<<1),w.addr); + w.addrOrVal=true; + } + } + } + } + } + + OPN2_Clock(&fm_nuked,ignored); + } + os[0]=( + (fm_nuked.pan_l[0]?fm_nuked.ch_out[0]:0)+ + (fm_nuked.pan_l[1]?fm_nuked.ch_out[1]:0)+ + (fm_nuked.pan_l[2]?fm_nuked.ch_out[2]:0)+ + (fm_nuked.pan_l[3]?fm_nuked.ch_out[3]:0)+ + (fm_nuked.pan_l[4]?fm_nuked.ch_out[4]:0)+ + (fm_nuked.pan_l[5]?fm_nuked.ch_out[5]:0) + ); + os[1]=( + (fm_nuked.pan_r[0]?fm_nuked.ch_out[0]:0)+ + (fm_nuked.pan_r[1]?fm_nuked.ch_out[1]:0)+ + (fm_nuked.pan_r[2]?fm_nuked.ch_out[2]:0)+ + (fm_nuked.pan_r[3]?fm_nuked.ch_out[3]:0)+ + (fm_nuked.pan_r[4]?fm_nuked.ch_out[4]:0)+ + (fm_nuked.pan_r[5]?fm_nuked.ch_out[5]:0) + ); + + os[0]>>=1; + os[1]>>=1; + + // ymfm part + fm->generate(&fmout); + + os[0]+=fmout.data[0]+(fmout.data[2]>>1); + if (os[0]<-32768) os[0]=-32768; + if (os[0]>32767) os[0]=32767; + + os[1]+=fmout.data[1]+(fmout.data[2]>>1); + if (os[1]<-32768) os[1]=-32768; + if (os[1]>32767) os[1]=32767; + + bufL[h]=os[0]; + bufR[h]=os[1]; + + + for (int i=0; idata[oscBuf[i]->needle++]=fm_nuked.ch_out[i]; + } + + ssge->get_last_out(ssgOut); + for (int i=psgChanOffs; idata[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]; + } + + for (int i=adpcmAChanOffs; idata[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1); + } + + oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + } +} + +void DivPlatformYM2608::acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; ymfm::ym2608::fm_engine* fme=fm->debug_fm_engine(); @@ -1256,6 +1366,8 @@ void DivPlatformYM2608::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } + OPN2_Reset(&fm_nuked); + OPN2_SetChipType(&fm_nuked,ym3438_mode_opn); fm->reset(); for (int i=0; i<16; i++) { chan[i]=DivPlatformOPN::OPNChannelStereo(); @@ -1407,18 +1519,21 @@ void DivPlatformYM2608::setFlags(const DivConfig& flags) { fmFreqBase=9440540.0/2.0, fmDivBase=36, ayDiv=16; + nukedMult=16; break; case 0x02: // /2 prescale=0x2f; fmFreqBase=9440540.0/3.0, fmDivBase=24, ayDiv=8; + nukedMult=24; break; default: // /6 prescale=0x2d; fmFreqBase=9440540.0, fmDivBase=72, ayDiv=32; + nukedMult=8; break; } CHECK_CUSTOM_CLOCK; diff --git a/src/engine/platform/ym2608.h b/src/engine/platform/ym2608.h index d1100ce9..4e6c59b9 100644 --- a/src/engine/platform/ym2608.h +++ b/src/engine/platform/ym2608.h @@ -47,6 +47,7 @@ class DivPlatformYM2608: public DivPlatformOPN { OPNChannelStereo chan[16]; DivDispatchOscBuffer* oscBuf[16]; bool isMuted[16]; + ym3438_t fm_nuked; ymfm::ym2608* fm; ymfm::ym2608::output_data fmout; @@ -62,12 +63,16 @@ class DivPlatformYM2608: public DivPlatformOPN { int globalRSSVolume; bool extMode, noExtMacros; - unsigned char prescale; + unsigned char prescale, nukedMult; double NOTE_OPNB(int ch, int note); double NOTE_ADPCMB(int note); friend void putDispatchChip(void*,int); + + void acquire_combo(short* bufL, short* bufR, size_t start, size_t len); + void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len); + public: void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 2a3b00ac..8b398a3e 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -233,6 +233,112 @@ const char** DivPlatformYM2610::getRegisterSheet() { } void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) { + if (useCombo) { + acquire_combo(bufL,bufR,start,len); + } else { + acquire_ymfm(bufL,bufR,start,len); + } +} + +void DivPlatformYM2610::acquire_combo(short* bufL, short* bufR, size_t start, size_t len) { + static int os[2]; + static short ignored[2]; + + ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); + ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); + ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); + + ymfm::ssg_engine::output_data ssgOut; + + ymfm::adpcm_a_channel* adpcmAChan[6]; + for (int i=0; i<6; i++) { + adpcmAChan[i]=aae->debug_channel(i); + } + + for (size_t h=start; hread(0)&0x80)) { + QueuedWrite& w=writes.front(); + + if (w.addr<=0x1c || (w.addr>=0x100 && w.addr<=0x12d)) { + // ymfm write + fm->write(0x0+((w.addr>>8)<<1),w.addr); + fm->write(0x1+((w.addr>>8)<<1),w.val); + + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + delay=32; + } else { + // Nuked write + if (w.addrOrVal) { + OPN2_Write(&fm_nuked,0x1+((w.addr>>8)<<1),w.val); + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + } else { + lastBusy++; + if (fm_nuked.write_busy==0) { + OPN2_Write(&fm_nuked,0x0+((w.addr>>8)<<1),w.addr); + w.addrOrVal=true; + } + } + } + } + } + + OPN2_Clock(&fm_nuked,ignored); + } + os[0]=( + (fm_nuked.pan_l[1]?fm_nuked.ch_out[1]:0)+ + (fm_nuked.pan_l[2]?fm_nuked.ch_out[2]:0)+ + (fm_nuked.pan_l[4]?fm_nuked.ch_out[4]:0)+ + (fm_nuked.pan_l[5]?fm_nuked.ch_out[5]:0) + ); + os[1]=( + (fm_nuked.pan_r[1]?fm_nuked.ch_out[1]:0)+ + (fm_nuked.pan_r[2]?fm_nuked.ch_out[2]:0)+ + (fm_nuked.pan_r[4]?fm_nuked.ch_out[4]:0)+ + (fm_nuked.pan_r[5]?fm_nuked.ch_out[5]:0) + ); + + os[0]>>=1; + os[1]>>=1; + + // ymfm part + fm->generate(&fmout); + + os[0]+=fmout.data[0]+(fmout.data[2]>>1); + if (os[0]<-32768) os[0]=-32768; + if (os[0]>32767) os[0]=32767; + + os[1]+=fmout.data[1]+(fmout.data[2]>>1); + if (os[1]<-32768) os[1]=-32768; + if (os[1]>32767) os[1]=32767; + + bufL[h]=os[0]; + bufR[h]=os[1]; + + + for (int i=0; idata[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]]; + } + + ssge->get_last_out(ssgOut); + for (int i=psgChanOffs; idata[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]; + } + + for (int i=adpcmAChanOffs; idata[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1); + } + + oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + } +} + +void DivPlatformYM2610::acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; ymfm::ym2610::fm_engine* fme=fm->debug_fm_engine(); @@ -254,13 +360,13 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l for (size_t h=start; hread(0)&0x80)) { QueuedWrite& w=writes.front(); fm->write(0x0+((w.addr>>8)<<1),w.addr); fm->write(0x1+((w.addr>>8)<<1),w.val); regPool[w.addr&0x1ff]=w.val; writes.pop_front(); - delay=4; + delay=1; } } diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index b07819f0..f2ffb9c7 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -37,6 +37,10 @@ class DivPlatformYM2610: public DivPlatformYM2610Base { }; friend void putDispatchChip(void*,int); + + void acquire_combo(short* bufL, short* bufR, size_t start, size_t len); + void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len); + public: void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 28c69353..af02fd9a 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -297,6 +297,116 @@ const char** DivPlatformYM2610B::getRegisterSheet() { } void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) { + if (useCombo) { + acquire_combo(bufL,bufR,start,len); + } else { + acquire_ymfm(bufL,bufR,start,len); + } +} + +void DivPlatformYM2610B::acquire_combo(short* bufL, short* bufR, size_t start, size_t len) { + static int os[2]; + static short ignored[2]; + + ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); + ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); + ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); + + ymfm::ssg_engine::output_data ssgOut; + + ymfm::adpcm_a_channel* adpcmAChan[6]; + for (int i=0; i<6; i++) { + adpcmAChan[i]=aae->debug_channel(i); + } + + for (size_t h=start; hread(0)&0x80)) { + QueuedWrite& w=writes.front(); + + if (w.addr<=0x1c || (w.addr>=0x100 && w.addr<=0x12d)) { + // ymfm write + fm->write(0x0+((w.addr>>8)<<1),w.addr); + fm->write(0x1+((w.addr>>8)<<1),w.val); + + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + delay=32; + } else { + // Nuked write + if (w.addrOrVal) { + OPN2_Write(&fm_nuked,0x1+((w.addr>>8)<<1),w.val); + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + } else { + lastBusy++; + if (fm_nuked.write_busy==0) { + OPN2_Write(&fm_nuked,0x0+((w.addr>>8)<<1),w.addr); + w.addrOrVal=true; + } + } + } + } + } + + OPN2_Clock(&fm_nuked,ignored); + } + os[0]=( + (fm_nuked.pan_l[0]?fm_nuked.ch_out[0]:0)+ + (fm_nuked.pan_l[1]?fm_nuked.ch_out[1]:0)+ + (fm_nuked.pan_l[2]?fm_nuked.ch_out[2]:0)+ + (fm_nuked.pan_l[3]?fm_nuked.ch_out[3]:0)+ + (fm_nuked.pan_l[4]?fm_nuked.ch_out[4]:0)+ + (fm_nuked.pan_l[5]?fm_nuked.ch_out[5]:0) + ); + os[1]=( + (fm_nuked.pan_r[0]?fm_nuked.ch_out[0]:0)+ + (fm_nuked.pan_r[1]?fm_nuked.ch_out[1]:0)+ + (fm_nuked.pan_r[2]?fm_nuked.ch_out[2]:0)+ + (fm_nuked.pan_r[3]?fm_nuked.ch_out[3]:0)+ + (fm_nuked.pan_r[4]?fm_nuked.ch_out[4]:0)+ + (fm_nuked.pan_r[5]?fm_nuked.ch_out[5]:0) + ); + + os[0]>>=1; + os[1]>>=1; + + // ymfm part + fm->generate(&fmout); + + os[0]+=fmout.data[0]+(fmout.data[2]>>1); + if (os[0]<-32768) os[0]=-32768; + if (os[0]>32767) os[0]=32767; + + os[1]+=fmout.data[1]+(fmout.data[2]>>1); + if (os[1]<-32768) os[1]=-32768; + if (os[1]>32767) os[1]=32767; + + bufL[h]=os[0]; + bufR[h]=os[1]; + + + for (int i=0; idata[oscBuf[i]->needle++]=fm_nuked.ch_out[i]; + } + + ssge->get_last_out(ssgOut); + for (int i=psgChanOffs; idata[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]; + } + + for (int i=adpcmAChanOffs; idata[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1); + } + + oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + } +} + +void DivPlatformYM2610B::acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; ymfm::ym2610b::fm_engine* fme=fm->debug_fm_engine(); @@ -316,13 +426,13 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t for (size_t h=start; hread(0)&0x80)) { QueuedWrite& w=writes.front(); fm->write(0x0+((w.addr>>8)<<1),w.addr); fm->write(0x1+((w.addr>>8)<<1),w.val); regPool[w.addr&0x1ff]=w.val; writes.pop_front(); - delay=4; + delay=1; } } diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 26a5cdee..6052a535 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -33,6 +33,10 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base { }; friend void putDispatchChip(void*,int); + + void acquire_combo(short* bufL, short* bufR, size_t start, size_t len); + void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len); + public: void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index 79dde4ab..9671c157 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -50,6 +50,7 @@ class DivPlatformYM2610Base: public DivPlatformOPN { DivDispatchOscBuffer* oscBuf[16]; bool isMuted[16]; + ym3438_t fm_nuked; ymfm::ym2610b* fm; ymfm::ym2610b::output_data fmout; DivPlatformAY8910* ay; @@ -95,6 +96,9 @@ class DivPlatformYM2610Base: public DivPlatformOPN { writeADPCMAOn=0; globalADPCMAVolume=0x3f; + OPN2_Reset(&fm_nuked); + OPN2_SetChipType(&fm_nuked,ym3438_mode_opn); + ay->reset(); ay->getRegisterWrites().clear(); ay->flushWrites(); @@ -217,7 +221,7 @@ class DivPlatformYM2610Base: public DivPlatformOPN { } CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); - rate=chipClock/16; + rate=fm->sample_rate(chipClock); for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; } @@ -240,7 +244,7 @@ class DivPlatformYM2610Base: public DivPlatformOPN { iface.adpcmBMem=adpcmBMem; iface.sampleBank=0; fm=new ymfm::ym2610b(iface); - fm->set_fidelity(ymfm::OPN_FIDELITY_MAX); + fm->set_fidelity(ymfm::OPN_FIDELITY_MED); setFlags(flags); // YM2149, 2MHz ay=new DivPlatformAY8910(true,chipClock,32); diff --git a/src/gui/gui.h b/src/gui/gui.h index cd2c3247..88c30651 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1179,6 +1179,7 @@ class FurnaceGUI { int fdsCore; int c64Core; int pokeyCore; + int opnCore; int pcSpeakerOutMethod; String yrw801Path; String tg100Path; @@ -1312,6 +1313,7 @@ class FurnaceGUI { fdsCore(0), c64Core(1), pokeyCore(1), + opnCore(1), pcSpeakerOutMethod(0), yrw801Path(""), tg100Path(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 41e87d19..2abcc67e 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -107,6 +107,11 @@ const char* pokeyCores[]={ "ASAP (C++ port)" }; +const char* opnCores[]={ + "ymfm only", + "Nuked-OPN2 (FM) + ymfm (SSG/ADPCM)" +}; + const char* pcspkrOutMethods[]={ "evdev SND_TONE", "KIOCSOUND on /dev/tty1", @@ -1078,6 +1083,10 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); ImGui::Combo("##POKEYCore",&settings.pokeyCore,pokeyCores,2); + ImGui::Text("OPN/OPNA/OPNB cores"); + ImGui::SameLine(); + ImGui::Combo("##OPNCore",&settings.opnCore,opnCores,2); + ImGui::Separator(); ImGui::Text("PC Speaker strategy"); @@ -2346,6 +2355,7 @@ void FurnaceGUI::syncSettings() { settings.fdsCore=e->getConfInt("fdsCore",0); settings.c64Core=e->getConfInt("c64Core",1); settings.pokeyCore=e->getConfInt("pokeyCore",1); + settings.opnCore=e->getConfInt("opnCore",1); settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0); settings.yrw801Path=e->getConfString("yrw801Path",""); settings.tg100Path=e->getConfString("tg100Path",""); @@ -2472,6 +2482,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.fdsCore,0,1); clampSetting(settings.c64Core,0,1); clampSetting(settings.pokeyCore,0,1); + clampSetting(settings.opnCore,0,1); clampSetting(settings.pcSpeakerOutMethod,0,4); clampSetting(settings.mainFont,0,6); clampSetting(settings.patFont,0,6); @@ -2616,7 +2627,8 @@ void FurnaceGUI::commitSettings() { settings.nesCore!=e->getConfInt("nesCore",0) || settings.fdsCore!=e->getConfInt("fdsCore",0) || settings.c64Core!=e->getConfInt("c64Core",1) || - settings.pokeyCore!=e->getConfInt("pokeyCore",1) + settings.pokeyCore!=e->getConfInt("pokeyCore",1) || + settings.opnCore!=e->getConfInt("opnCore",1) ); e->setConf("mainFontSize",settings.mainFontSize); @@ -2637,6 +2649,7 @@ void FurnaceGUI::commitSettings() { e->setConf("fdsCore",settings.fdsCore); e->setConf("c64Core",settings.c64Core); e->setConf("pokeyCore",settings.pokeyCore); + e->setConf("opnCore",settings.opnCore); e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod); e->setConf("yrw801Path",settings.yrw801Path); e->setConf("tg100Path",settings.tg100Path);