From cf84be0b71ca940b01b82044db0a5cd4e11a9483 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 20 Nov 2023 17:32:33 -0500 Subject: [PATCH 01/53] Virtual Boy: this works?! --- src/engine/platform/vb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/vb.cpp b/src/engine/platform/vb.cpp index 588676aff..27a93799b 100644 --- a/src/engine/platform/vb.cpp +++ b/src/engine/platform/vb.cpp @@ -96,7 +96,7 @@ const char** DivPlatformVB::getRegisterSheet() { void DivPlatformVB::acquire(short** buf, size_t len) { for (size_t h=0; hWrite(cycles,w.addr,w.val); regPool[w.addr>>2]=w.val; From c59e2e6e874e2639a6a2a4647dcd06d5b925ab59 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 20 Nov 2023 18:32:17 -0500 Subject: [PATCH 02/53] Virtual Boy: add static wave storage mode --- src/engine/platform/vb.cpp | 38 +++++++++++++++++++++++++++++++++++++- src/engine/platform/vb.h | 2 ++ src/gui/sysConf.cpp | 23 ++++++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/vb.cpp b/src/engine/platform/vb.cpp index 27a93799b..3e0055e1e 100644 --- a/src/engine/platform/vb.cpp +++ b/src/engine/platform/vb.cpp @@ -123,6 +123,7 @@ void DivPlatformVB::acquire(short** buf, size_t len) { } void DivPlatformVB::updateWave(int ch) { + if (romMode) return; if (ch>=5) return; for (int i=0; i<32; i++) { @@ -162,6 +163,9 @@ void DivPlatformVB::tick(bool sysTick) { if (chan[i].std.wave.had) { if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { chan[i].wave=chan[i].std.wave.val; + if (romMode) { + chWrite(i,0x06,chan[i].wave); + } chan[i].ws.changeWave1(chan[i].wave); if (!chan[i].keyOff) chan[i].keyOn=true; } @@ -282,6 +286,9 @@ int DivPlatformVB::dispatch(DivCommand c) { case DIV_CMD_WAVE: chan[c.chan].wave=c.value; chan[c.chan].ws.changeWave1(chan[c.chan].wave); + if (romMode) { + chWrite(c.chan,0x06,chan[c.chan].wave); + } chan[c.chan].keyOn=true; break; case DIV_CMD_NOTE_PORTA: { @@ -464,8 +471,13 @@ void DivPlatformVB::reset() { chWrite(i,0x01,isMuted[i]?0:chan[i].pan); chWrite(i,0x05,0x00); chWrite(i,0x00,0x80); - chWrite(i,0x06,i); + if (romMode) { + chWrite(i,0x06,0); + } else { + chWrite(i,0x06,i); + } } + updateROMWaves(); delay=500; } @@ -481,6 +493,27 @@ float DivPlatformVB::getPostAmp() { return 6.0f; } +void DivPlatformVB::updateROMWaves() { + if (romMode) { + // copy wavetables + for (int i=0; i<5; i++) { + int data=0; + DivWavetable* w=parent->getWave(i); + + for (int j=0; j<32; j++) { + if (w->max<1 || w->len<1) { + data=0; + } else { + data=w->data[j*w->len/32]*63/w->max; + if (data<0) data=0; + if (data>63) data=63; + } + rWrite((i<<7)+(j<<2),data); + } + } + } +} + void DivPlatformVB::notifyWaveChange(int wave) { for (int i=0; i<6; i++) { if (chan[i].wave==wave) { @@ -488,6 +521,7 @@ void DivPlatformVB::notifyWaveChange(int wave) { updateWave(i); } } + updateROMWaves(); } void DivPlatformVB::notifyInsDeletion(void* ins) { @@ -504,6 +538,8 @@ void DivPlatformVB::setFlags(const DivConfig& flags) { oscBuf[i]->rate=rate; } + romMode=flags.getBool("romMode",false); + if (vb!=NULL) { delete vb; vb=NULL; diff --git a/src/engine/platform/vb.h b/src/engine/platform/vb.h index 508e9a049..940491f60 100644 --- a/src/engine/platform/vb.h +++ b/src/engine/platform/vb.h @@ -57,10 +57,12 @@ class DivPlatformVB: public DivDispatch { int tempR; unsigned char modulation; bool modType; + bool romMode; signed char modTable[32]; VSU* vb; unsigned char regPool[0x600]; void updateWave(int ch); + void updateROMWaves(); void writeEnv(int ch, bool upperByteToo=false); friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index b286550ee..b4c4ff578 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -2242,10 +2242,31 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl } break; } + case DIV_SYSTEM_VBOY: { + bool romMode=flags.getBool("romMode",false); + + ImGui::Text("Waveform storage mode:"); + ImGui::Indent(); + if (ImGui::RadioButton("Dynamic (unconfirmed)",!romMode)) { + romMode=false; + altered=true; + } + if (ImGui::RadioButton("Static (up to 5 waves)",romMode)) { + romMode=true; + altered=true; + } + ImGui::Unindent(); + + if (altered) { + e->lockSave([&]() { + flags.set("romMode",romMode); + }); + } + break; + } case DIV_SYSTEM_SWAN: case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_PET: - case DIV_SYSTEM_VBOY: case DIV_SYSTEM_GA20: case DIV_SYSTEM_PV1000: case DIV_SYSTEM_VERA: From a9dd2ee6b54f44c4653e4de6a3ebb5c5dad7f58c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 20 Nov 2023 18:54:11 -0500 Subject: [PATCH 03/53] ES5506: remove 8-bit sample format flag issue #1601 --- src/engine/sysDef.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index ed4b74a33..5c044afc5 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1588,7 +1588,7 @@ void DivEngine::registerSystems() { // TODO: custom sample format sysDefs[DIV_SYSTEM_ES5506]=new DivSysDef( - "Ensoniq ES5506", NULL, 0xb1, 0, 32, false, true, 0/*0x171*/, false, (1U< Date: Wed, 22 Nov 2023 16:35:02 -0500 Subject: [PATCH 04/53] prepare the addition of more OPL emulation cores --- CMakeLists.txt | 2 + extern/YM3812-LLE/LICENSE | 339 +++ extern/YM3812-LLE/Readme.md | 13 + extern/YM3812-LLE/fmopl2.c | 1501 +++++++++++++ extern/YM3812-LLE/fmopl2.h | 272 +++ extern/YMF262-LLE/LICENSE | 339 +++ extern/YMF262-LLE/Readme.md | 13 + extern/YMF262-LLE/fmopl3.c | 1646 ++++++++++++++ extern/YMF262-LLE/fmopl3.h | 333 +++ src/engine/platform/sound/ymfm/ymfm_opl.cpp | 2139 +++++++++++++++++++ src/engine/platform/sound/ymfm/ymfm_opl.h | 902 ++++++++ src/engine/platform/sound/ymfm/ymfm_pcm.cpp | 714 +++++++ src/engine/platform/sound/ymfm/ymfm_pcm.h | 347 +++ 13 files changed, 8560 insertions(+) create mode 100644 extern/YM3812-LLE/LICENSE create mode 100644 extern/YM3812-LLE/Readme.md create mode 100644 extern/YM3812-LLE/fmopl2.c create mode 100644 extern/YM3812-LLE/fmopl2.h create mode 100644 extern/YMF262-LLE/LICENSE create mode 100644 extern/YMF262-LLE/Readme.md create mode 100644 extern/YMF262-LLE/fmopl3.c create mode 100644 extern/YMF262-LLE/fmopl3.h create mode 100644 src/engine/platform/sound/ymfm/ymfm_opl.cpp create mode 100644 src/engine/platform/sound/ymfm/ymfm_opl.h create mode 100644 src/engine/platform/sound/ymfm/ymfm_pcm.cpp create mode 100644 src/engine/platform/sound/ymfm/ymfm_pcm.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d50f19328..e0bbe826c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -556,9 +556,11 @@ src/engine/platform/sound/tia/AudioChannel.cpp src/engine/platform/sound/tia/Audio.cpp src/engine/platform/sound/ymfm/ymfm_adpcm.cpp +src/engine/platform/sound/ymfm/ymfm_opl.cpp src/engine/platform/sound/ymfm/ymfm_opm.cpp src/engine/platform/sound/ymfm/ymfm_opn.cpp src/engine/platform/sound/ymfm/ymfm_opz.cpp +src/engine/platform/sound/ymfm/ymfm_pcm.cpp src/engine/platform/sound/ymfm/ymfm_ssg.cpp src/engine/platform/sound/lynx/Mikey.cpp diff --git a/extern/YM3812-LLE/LICENSE b/extern/YM3812-LLE/LICENSE new file mode 100644 index 000000000..89e08fb00 --- /dev/null +++ b/extern/YM3812-LLE/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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 program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; 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. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/extern/YM3812-LLE/Readme.md b/extern/YM3812-LLE/Readme.md new file mode 100644 index 000000000..38aa0debb --- /dev/null +++ b/extern/YM3812-LLE/Readme.md @@ -0,0 +1,13 @@ +# YM3812-LLE + +Yamaha YM3812 (OPL2) emulator using YM3812 die shot. + +Special thanks to Travis Goodspeed for decapping YM3812. + +https://twitter.com/travisgoodspeed/status/1652334901230723072 + +# MODIFICATION DISCLAIMER + +this is a modified version of YM3812-LLE which adds functions to allow its usage. + +the original Git commit is 7f0c6537ccd61e9e7dbddb4e4a353e007ea69c50. diff --git a/extern/YM3812-LLE/fmopl2.c b/extern/YM3812-LLE/fmopl2.c new file mode 100644 index 000000000..a1a08f5c5 --- /dev/null +++ b/extern/YM3812-LLE/fmopl2.c @@ -0,0 +1,1501 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YM3812-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * YM3812 emulator + * Thanks: + * Travis Goodspeed: + * YM3812 decap and die shot + * + */ + +#include "fmopl2.h" + + +void FMOPL2_DoShiftRegisters(fmopl2_t *chip, int sel) +{ + int j; + int to = sel; + int from = sel ^ 1; + int rot = sel == 0 ? 1 : 0; +#define CH_ROTATE(x) rot ? ((x << 1) | ((x >> 8) & 1)) : x +#define OP_ROTATE(x) rot ? ((x << 1) | ((x >> 17) & 1)) : x + // channel registers + + // fnum + for (j = 0; j < 10; j++) + chip->ch_fnum[j][to] = CH_ROTATE(chip->ch_fnum[j][from]); + // block + for (j = 0; j < 3; j++) + chip->ch_block[j][to] = CH_ROTATE(chip->ch_block[j][from]); + // kon + chip->ch_keyon[to] = CH_ROTATE(chip->ch_keyon[from]); + // connect + chip->ch_connect[to] = CH_ROTATE(chip->ch_connect[from]); + // feedback + for (j = 0; j < 3; j++) + chip->ch_fb[j][to] = CH_ROTATE(chip->ch_fb[j][from]); + // multi + for (j = 0; j < 4; j++) + chip->op_multi[j][to] = OP_ROTATE(chip->op_multi[j][from]); + // ksr + chip->op_ksr[to] = OP_ROTATE(chip->op_ksr[from]); + // egt + chip->op_egt[to] = OP_ROTATE(chip->op_egt[from]); + // vib + chip->op_vib[to] = OP_ROTATE(chip->op_vib[from]); + // am + chip->op_am[to] = OP_ROTATE(chip->op_am[from]); + // tl + for (j = 0; j < 6; j++) + chip->op_tl[j][to] = OP_ROTATE(chip->op_tl[j][from]); + // ksl + for (j = 0; j < 2; j++) + chip->op_ksl[j][to] = OP_ROTATE(chip->op_ksl[j][from]); + // ar + for (j = 0; j < 4; j++) + chip->op_ar[j][to] = OP_ROTATE(chip->op_ar[j][from]); + // dr + for (j = 0; j < 4; j++) + chip->op_dr[j][to] = OP_ROTATE(chip->op_dr[j][from]); + // sl + for (j = 0; j < 4; j++) + chip->op_sl[j][to] = OP_ROTATE(chip->op_sl[j][from]); + // rr + for (j = 0; j < 4; j++) + chip->op_rr[j][to] = OP_ROTATE(chip->op_rr[j][from]); + // wf + for (j = 0; j < 2; j++) + chip->op_wf[j][to] = OP_ROTATE(chip->op_wf[j][from]); +#undef CH_ROTATE +#undef OP_ROTATE +} + +enum { + eg_state_attack = 0, + eg_state_decay, + eg_state_sustain, + eg_state_release +}; + +void FMOPL2_Clock(fmopl2_t *chip) +{ + int i; + + chip->mclk1 = !chip->input.mclk; + chip->mclk2 = chip->input.mclk; + + chip->reset1 = !chip->input.ic; + chip->io_rd = !chip->input.rd; + chip->io_wr = !chip->input.wr; + chip->io_cs = !chip->input.cs; + chip->io_a0 = chip->input.address & 1; + + if (chip->mclk1) + { + int prescaler_reset = !(chip->prescaler_reset_l[1] & 2) && chip->reset1; + chip->prescaler_reset_l[0] = (chip->prescaler_reset_l[1] << 1) | chip->reset1; + if (prescaler_reset) + chip->prescaler_cnt[0] = 0; + else + chip->prescaler_cnt[0] = (chip->prescaler_cnt[1] + 1) & 3; + + chip->prescaler_l1[0] = !prescaler_reset && chip->prescaler_cnt[1] == 1; + chip->prescaler_l2[0] = chip->prescaler_cnt[1] == 3; + } + if (chip->mclk2) + { + chip->prescaler_reset_l[1] = chip->prescaler_reset_l[0]; + chip->prescaler_cnt[1] = chip->prescaler_cnt[0]; + chip->prescaler_l1[1] = chip->prescaler_l1[0]; + chip->prescaler_l2[1] = chip->prescaler_l2[0]; + } + + chip->clk1 = chip->prescaler_l1[1]; + chip->clk2 = chip->prescaler_l2[1]; + + chip->io_read0 = !chip->reset1 && chip->io_cs && chip->io_rd && !chip->io_a0; + chip->io_read1 = !chip->reset1 && chip->io_cs && chip->io_rd && chip->io_a0; + chip->io_write = !chip->reset1 && chip->io_cs && chip->io_wr; + chip->io_write0 = !chip->reset1 && chip->io_cs && chip->io_wr && !chip->io_a0; + chip->io_write1 = !chip->reset1 && chip->io_cs && chip->io_wr && chip->io_a0; + chip->io_dir = chip->io_cs && chip->io_rd; + + int irq = chip->t1_status || chip->t2_status || chip->unk_status1 || chip->unk_status2; + + if (!chip->io_dir) + chip->io_data = chip->input.data_i; + + if (chip->io_write) + chip->data_latch = chip->io_data; + + if (chip->write0) + chip->write0_sr = 0; + else if (chip->io_write0) + chip->write0_sr = 1; + + if (chip->write1) + chip->write1_sr = 0; + else if (chip->io_write1) + chip->write1_sr = 1; + + if (chip->mclk1) + { + chip->write0_latch[1] = chip->write0_latch[0]; + chip->write1_latch[1] = chip->write1_latch[0]; + } + if (chip->mclk2) + { + chip->write0_latch[0] = chip->write0_sr; + chip->write0_latch[2] = chip->write0_latch[1]; + + chip->write1_latch[0] = chip->write1_sr; + chip->write1_latch[2] = chip->write1_latch[1]; + } + + if (chip->clk1) + { + chip->write0_latch[4] = chip->write0_latch[3]; + chip->write1_latch[4] = chip->write1_latch[3]; + } + if (chip->clk2) + { + chip->write0_latch[3] = chip->write0_latch[2]; + chip->write0_latch[5] = chip->write0_latch[4]; + + chip->write1_latch[3] = chip->write1_latch[2]; + chip->write1_latch[5] = chip->write1_latch[4]; + } + + chip->write0 = chip->write0_latch[5]; + chip->write1 = chip->write1_latch[5]; + + //// + + if (chip->o_clk1 == chip->clk1 && chip->o_clk2 == chip->clk2 && chip->o_reset1 == chip->reset1 + && chip->o_write0 == chip->write0 && chip->o_write1 == chip->write1 && chip->o_data_latch == chip->data_latch) + goto end; // opt + + chip->o_clk1 = chip->clk1; + chip->o_clk2 = chip->clk2; + chip->o_reset1 = chip->reset1; + chip->o_write0 = chip->write0; + chip->o_write1 = chip->write1; + chip->o_data_latch = chip->data_latch; + + if (chip->write0) + { + chip->reg_sel1 = chip->data_latch == 1; + chip->reg_sel2 = chip->data_latch == 2; + chip->reg_sel3 = chip->data_latch == 3; + chip->reg_sel4 = chip->data_latch == 4; + chip->reg_sel8 = chip->data_latch == 8; + chip->reg_selbd = chip->data_latch == 0xbd; + } + + chip->reg_sel4_wr = chip->write1 && chip->reg_sel4 && (chip->data_latch & 128) == 0; + chip->reg_sel4_rst = (chip->write1 && chip->reg_sel4 && (chip->data_latch & 128) != 0) || chip->reset1; + + if (chip->reset1) + { + chip->reg_test = 0; + chip->reg_timer1 = 0; + chip->reg_timer2 = 0; + chip->reg_notesel = 0; + chip->reg_csm = 0; + chip->rhythm = 0; + chip->reg_rh_kon = 0; + chip->reg_da = 0; + chip->reg_dv = 0; + } + else if (chip->write1) + { + if (chip->reg_sel1) + chip->reg_test = chip->data_latch & 255; + if (chip->reg_sel2) + chip->reg_timer1 = chip->data_latch & 255; + if (chip->reg_sel3) + chip->reg_timer2 = chip->data_latch & 255; + if (chip->reg_sel8) + { + chip->reg_notesel = (chip->data_latch & 64) != 0; + chip->reg_csm = (chip->data_latch & 128) != 0; + } + if (chip->reg_selbd) + { + chip->reg_rh_kon = chip->data_latch & 31; + chip->rhythm = (chip->data_latch & 32) != 0; + chip->reg_dv = (chip->data_latch & 64) != 0; + chip->reg_da = (chip->data_latch & 128) != 0; + } + } + + if (chip->reset1) + { + chip->reg_t1_mask = 0; + chip->reg_t2_mask = 0; + chip->reg_t1_start = 0; + chip->reg_t2_start = 0; + chip->reg_mode_b3 = 0; + chip->reg_mode_b4 = 0; + } + else if (chip->reg_sel4_wr) + { + chip->reg_t1_mask = (chip->data_latch & 64) != 0; + chip->reg_t2_mask = (chip->data_latch & 32) != 0; + chip->reg_t1_start = (chip->data_latch & 1) != 0; + chip->reg_t2_start = (chip->data_latch & 2) != 0; + chip->reg_mode_b3 = (chip->data_latch & 8) != 0; + chip->reg_mode_b4 = (chip->data_latch & 16) != 0; + } + + { + chip->fsm_reset = !(chip->fsm_reset_l[1] & 2) && chip->reset1; + chip->fsm_cnt1_of = (chip->fsm_cnt1[1] & 5) == 5; + chip->fsm_cnt2_of = chip->fsm_cnt1_of && (chip->fsm_cnt2[1] & 2) != 0; + + chip->fsm_cnt = (chip->fsm_cnt2[1] << 3) | chip->fsm_cnt1[1]; + + chip->fsm_sel[0] = chip->fsm_cnt == 20 && chip->rhythm; + chip->fsm_sel[1] = chip->fsm_cnt == 19 && chip->rhythm; + chip->fsm_sel[2] = chip->fsm_cnt == 18 && chip->rhythm; + chip->fsm_sel[3] = chip->fsm_cnt == 17 && chip->rhythm; + chip->fsm_sel[4] = chip->fsm_cnt == 16 && chip->rhythm; + chip->fsm_sel[5] = chip->fsm_cnt == 20 && chip->rhythm; + chip->fsm_sel[6] = chip->fsm_cnt == 19 && chip->rhythm; + chip->fsm_sel[7] = (chip->fsm_cnt & 5) == 4; + chip->fsm_sel[8] = chip->fsm_cnt == 16; + chip->fsm_sel[9] = (chip->fsm_cnt & 29) == 5; + chip->fsm_sel[10] = chip->fsm_cnt == 16; + chip->fsm_sel[11] = chip->fsm_cnt == 11; + chip->fsm_sel[12] = chip->fsm_cnt == 20; + + int fsm_mc = !(chip->fsm_sel[7] || (chip->fsm_cnt & 2) != 0); + + chip->fsm_out[0] = ((chip->connect_l[1] & 2) != 0 || chip->fsm_sel[0] || chip->fsm_sel[1] || fsm_mc) && !chip->fsm_sel[2]; + + chip->fsm_out[1] = fsm_mc && !chip->fsm_sel[3] && !chip->fsm_sel[4]; + + chip->fsm_out[2] = !fsm_mc && !chip->fsm_sel[5] && !chip->fsm_sel[6]; + + chip->fsm_out[3] = !(chip->fsm_l1[1] && 1); + + chip->fsm_out[4] = chip->fsm_l2[1]; + + chip->fsm_out[5] = chip->fsm_sel[10]; + + chip->fsm_out[6] = chip->fsm_sel[11]; + + chip->fsm_out[7] = chip->fsm_sel[12]; + + chip->fsm_out[8] = (chip->fsm_l3[1] & 1) != 0; + + chip->fsm_out[9] = (chip->fsm_l3[1] & 2) != 0; + + chip->fsm_out[10] = (chip->fsm_l3[1] & 2) != 0; + + chip->fsm_out[11] = (chip->fsm_l4[1] & 2) != 0 && chip->rhythm; + + chip->fsm_out[12] = (chip->fsm_l5[1] & 4) != 0; + + chip->fsm_out[13] = (chip->fsm_l6[1] & 4) != 0; + + chip->fsm_out[14] = !(chip->fsm_out[12] || (chip->fsm_cnt & 16) != 0); + + chip->fsm_out[15] = !(chip->fsm_out[12] || chip->fsm_out[13]); + } + + if (chip->clk1) + { + if (chip->fsm_reset || chip->fsm_cnt1_of) + chip->fsm_cnt1[0] = 0; + else + chip->fsm_cnt1[0] = (chip->fsm_cnt1[1] + 1) & 7; + if (chip->fsm_reset || chip->fsm_cnt2_of) + chip->fsm_cnt2[0] = 0; + else + chip->fsm_cnt2[0] = (chip->fsm_cnt2[1] + chip->fsm_cnt1_of) & 3; + + chip->fsm_reset_l[0] = (chip->fsm_reset_l[1] << 1) | chip->reset1; + + chip->fsm_l1[0] = !chip->fsm_sel[8] && !chip->fsm_sel[9] && (chip->fsm_cnt & 8) == 0; + + chip->fsm_l2[0] = chip->fsm_sel[10]; + + chip->fsm_l3[0] = (chip->fsm_l3[1] << 1) | chip->fsm_sel[12]; + + chip->fsm_l4[0] = (chip->fsm_l4[1] << 1) | ((chip->fsm_cnt & 16) != 0); + + chip->fsm_l5[0] = (chip->fsm_l5[1] << 1) | ((chip->fsm_cnt & 8) != 0); + + chip->fsm_l6[0] = (chip->fsm_l6[1] << 1) | ((chip->fsm_cnt & 16) != 0); + } + if (chip->clk2) + { + chip->fsm_cnt1[1] = chip->fsm_cnt1[0]; + chip->fsm_cnt2[1] = chip->fsm_cnt2[0]; + chip->fsm_reset_l[1] = chip->fsm_reset_l[0]; + chip->fsm_l1[1] = chip->fsm_l1[0]; + chip->fsm_l2[1] = chip->fsm_l2[0]; + chip->fsm_l3[1] = chip->fsm_l3[0]; + chip->fsm_l4[1] = chip->fsm_l4[0]; + chip->fsm_l5[1] = chip->fsm_l5[0]; + chip->fsm_l6[1] = chip->fsm_l6[0]; + } + + if (chip->clk1) + chip->timer_st_load_l = chip->fsm_out[8]; + chip->timer_st_load = chip->fsm_out[8] && !chip->timer_st_load_l; + + if (chip->timer_st_load) + chip->t1_start = chip->reg_t1_start; + + if (chip->clk1) + { + int lfo = chip->lfo_cnt[1]; + int add = chip->fsm_out[8]; + + chip->lfo_cnt[0] = (chip->reg_test & 128) != 0 ? 0 : (lfo + add) & 1023; + chip->vib_cnt[0] = (chip->reg_test & 128) != 0 ? 0 : (chip->vib_cnt[1] + chip->vib_step) & 7; + } + if (chip->clk2) + { + chip->lfo_cnt[1] = chip->lfo_cnt[0]; + chip->vib_cnt[1] = chip->vib_cnt[0]; + } + + { + int lfo = chip->lfo_cnt[1]; + int add = chip->fsm_out[8]; + + chip->t1_step = (((lfo & 3) + add) & 4) != 0; + chip->t2_step = (((lfo & 15) + add) & 16) != 0; + chip->am_step = (((lfo & 63) + add) & 64) != 0; + chip->vib_step = (((lfo & 1023) + add) & 1024) != 0; + chip->vib_step |= (chip->reg_test & 8) != 0 && add; + } + + if (chip->clk1) + { + int value = chip->t1_load ? chip->reg_timer1 : chip->t1_cnt[1]; + value += ((chip->t1_start_l[1] & 1) != 0 && chip->t1_step) || (chip->reg_test & 2) != 0; + chip->t1_of[0] = (value & 256) != 0; + chip->t1_cnt[0] = (chip->t1_start_l[1] & 1) == 0 ? 0 : (value & 255); + + value = (chip->t2_of[1] || (chip->t2_start_l[1] & 3) == 1) ? chip->reg_timer2 : chip->t2_cnt[1]; + value += ((chip->t2_start_l[1] & 1) != 0 && chip->t2_step) || (chip->reg_test & 2) != 0; + chip->t2_of[0] = (value & 256) != 0; + chip->t2_cnt[0] = (chip->t2_start_l[1] & 1) == 0 ? 0 : (value & 255); + + chip->t1_start_l[0] = (chip->t1_start_l[1] << 1) | chip->t1_start; + chip->t2_start_l[0] = (chip->t2_start_l[1] << 1) | chip->reg_t2_start; + } + if (chip->clk2) + { + chip->t1_cnt[1] = chip->t1_cnt[0]; + chip->t1_of[1] = chip->t1_of[0]; + chip->t2_cnt[1] = chip->t2_cnt[0]; + chip->t2_of[1] = chip->t2_of[0]; + + chip->t1_start_l[1] = chip->t1_start_l[0]; + chip->t2_start_l[1] = chip->t2_start_l[0]; + + chip->t1_load = (chip->t1_of[1] || (chip->t1_start_l[1] & 3) == 1); // opt + } + + if (chip->reg_sel4_rst || chip->reg_t1_mask) + chip->t1_status = 0; + else if (chip->t1_of[1]) + chip->t1_status = 1; + + if (chip->reg_sel4_rst || chip->reg_t2_mask) + chip->t2_status = 0; + else if (chip->t2_of[1]) + chip->t2_status = 1; + + if (chip->reg_sel4_rst || chip->reg_mode_b4) + chip->unk_status1 = 0; + else if (0) + chip->unk_status1 = 1; + + chip->unk_status2 = 0; + + if (chip->clk1) + chip->csm_load_l = chip->fsm_out[10]; + chip->csm_load = chip->fsm_out[10] && !chip->csm_load_l; + + if (chip->csm_load) + chip->csm_kon = chip->reg_csm && chip->t1_load; + + chip->rh_sel0 = chip->rhythm && chip->fsm_out[5]; + + if (chip->clk1) + { + chip->rh_sel[0] = (chip->rh_sel[1] << 1) | chip->rh_sel0; + } + if (chip->clk2) + { + chip->rh_sel[1] = chip->rh_sel[0]; + } + + //if (chip->clk1) // opt + { + chip->keyon_comb = chip->keyon || chip->csm_kon + || (chip->rh_sel0 && (chip->reg_rh_kon & 16) != 0) // bd0 + || ((chip->rh_sel[1] & 1) != 0 && (chip->reg_rh_kon & 1) != 0) // hh + || ((chip->rh_sel[1] & 2) != 0 && (chip->reg_rh_kon & 4) != 0) // tom + || ((chip->rh_sel[1] & 4) != 0 && (chip->reg_rh_kon & 16) != 0) // bd1 + || ((chip->rh_sel[1] & 8) != 0 && (chip->reg_rh_kon & 8) != 0) // sd + || ((chip->rh_sel[1] & 16) != 0 && (chip->reg_rh_kon & 2) != 0); // tc + } + + if (chip->reset1) + chip->address = 0; + else if ((chip->data_latch & 0xe0) != 0 && chip->write0) + chip->address = chip->data_latch; + + if (chip->write0) + chip->address_valid = (chip->data_latch & 0xe0) != 0; + + if (chip->reset1) + chip->data = 0; + else if (chip->address_valid && chip->write1) + chip->data = chip->data_latch; + + chip->address_valid2 = chip->address_valid_l[1] && !chip->write0; + + if (chip->clk1) + { + chip->address_valid_l[0] = (chip->address_valid && chip->write1) || chip->address_valid2; + + int slot_cnt1_of = (chip->slot_cnt1[1] & 5) == 5; + + if (chip->fsm_out[8] || slot_cnt1_of) + chip->slot_cnt1[0] = 0; + else + chip->slot_cnt1[0] = (chip->slot_cnt1[1] + 1) & 7; + + if (chip->fsm_out[8] || (slot_cnt1_of && (chip->slot_cnt2[1] & 2) != 0)) + chip->slot_cnt2[0] = 0; + else + chip->slot_cnt2[0] = (chip->slot_cnt2[1] + slot_cnt1_of) & 3; + } + if (chip->clk2) + { + chip->address_valid_l[1] = chip->address_valid_l[0]; + + chip->slot_cnt1[1] = chip->slot_cnt1[0]; + chip->slot_cnt2[1] = chip->slot_cnt2[0]; + + chip->slot_cnt = (chip->slot_cnt2[1] << 3) | chip->slot_cnt1[1]; // opt + } + + if (chip->clk1) + { + int sel_ch = (chip->address & 0xf0) == 0xa0 || (chip->address & 0xf0) == 0xb0 || (chip->address & 0xf0) == 0xc0; + int addr_add = sel_ch && ((chip->address & 8) != 0 || (chip->address & 6) == 6); + + int addr_sel = chip->address & 1; + + addr_sel |= (((chip->address >> 1) + addr_add) & 7) << 1; + + if (!sel_ch) + addr_sel |= chip->address & 16; + + int addr_match = addr_sel == chip->slot_cnt && chip->address_valid2; + + int sel_20 = (chip->address & 0xe0) == 0x20 && addr_match; + int sel_40 = (chip->address & 0xe0) == 0x40 && addr_match; + int sel_60 = (chip->address & 0xe0) == 0x60 && addr_match; + int sel_80 = (chip->address & 0xe0) == 0x80 && addr_match; + int sel_e0 = (chip->address & 0xe0) == 0xe0 && addr_match && (chip->reg_test & 32) != 0; + + int sel_a0 = (chip->address & 0xf0) == 0xa0 && addr_match; + int sel_b0 = (chip->address & 0xf0) == 0xb0 && addr_match; + int sel_c0 = (chip->address & 0xf0) == 0xc0 && addr_match; + + FMOPL2_DoShiftRegisters(chip, 0); + + if (chip->reset1) + { + for (i = 0; i < 10; i++) + chip->ch_fnum[i][0] &= ~1; + for (i = 0; i < 3; i++) + chip->ch_block[i][0] &= ~1; + chip->ch_keyon[0] &= ~1; + chip->ch_connect[0] &= ~1; + for (i = 0; i < 3; i++) + chip->ch_fb[i][0] &= ~1; + + for (i = 0; i < 4; i++) + chip->op_multi[i][0] &= ~1; + chip->op_ksr[0] &= ~1; + chip->op_egt[0] &= ~1; + chip->op_vib[0] &= ~1; + chip->op_am[0] &= ~1; + for (i = 0; i < 6; i++) + chip->op_tl[i][0] &= ~1; + for (i = 0; i < 2; i++) + chip->op_ksl[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_ar[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_dr[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_sl[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_rr[i][0] &= ~1; + for (i = 0; i < 2; i++) + chip->op_wf[i][0] &= ~1; + } + else + { + if (sel_a0) + { + for (i = 0; i < 8; i++) + chip->ch_fnum[i][0] &= ~1; + + for (i = 0; i < 8; i++) + chip->ch_fnum[i][0] |= (chip->data >> i) & 1; + } + if (sel_b0) + { + for (i = 8; i < 10; i++) + chip->ch_fnum[i][0] &= ~1; + for (i = 0; i < 3; i++) + chip->ch_block[i][0] &= ~1; + chip->ch_keyon[0] &= ~1; + + for (i = 8; i < 10; i++) + chip->ch_fnum[i][0] |= (chip->data >> (i - 8)) & 1; + for (i = 0; i < 3; i++) + chip->ch_block[i][0] |= (chip->data >> (i + 2)) & 1; + chip->ch_keyon[0] |= (chip->data >> 5) & 1; + } + if (sel_c0) + { + chip->ch_connect[0] &= ~1; + for (i = 0; i < 3; i++) + chip->ch_fb[i][0] &= ~1; + + chip->ch_connect[0] |= (chip->data >> 0) & 1; + for (i = 0; i < 3; i++) + chip->ch_fb[i][0] |= (chip->data >> (i + 1)) & 1; + } + if (sel_20) + { + for (i = 0; i < 4; i++) + chip->op_multi[i][0] &= ~1; + chip->op_ksr[0] &= ~1; + chip->op_egt[0] &= ~1; + chip->op_vib[0] &= ~1; + chip->op_am[0] &= ~1; + + for (i = 0; i < 4; i++) + chip->op_multi[i][0] |= (chip->data >> i) & 1; + chip->op_ksr[0] |= (chip->data >> 4) & 1; + chip->op_egt[0] |= (chip->data >> 5) & 1; + chip->op_vib[0] |= (chip->data >> 6) & 1; + chip->op_am[0] |= (chip->data >> 7) & 1; + } + if (sel_40) + { + for (i = 0; i < 6; i++) + chip->op_tl[i][0] &= ~1; + for (i = 0; i < 2; i++) + chip->op_ksl[i][0] &= ~1; + + for (i = 0; i < 6; i++) + chip->op_tl[i][0] |= (chip->data >> i) & 1; + for (i = 0; i < 2; i++) + chip->op_ksl[i][0] |= (chip->data >> (i + 6)) & 1; + } + if (sel_60) + { + for (i = 0; i < 4; i++) + chip->op_ar[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_dr[i][0] &= ~1; + + for (i = 0; i < 4; i++) + chip->op_ar[i][0] |= (chip->data >> (i + 4)) & 1; + for (i = 0; i < 4; i++) + chip->op_dr[i][0] |= (chip->data >> i) & 1; + } + if (sel_80) + { + for (i = 0; i < 4; i++) + chip->op_sl[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_rr[i][0] &= ~1; + + for (i = 0; i < 4; i++) + chip->op_sl[i][0] |= (chip->data >> (i + 4)) & 1; + for (i = 0; i < 4; i++) + chip->op_rr[i][0] |= (chip->data >> i) & 1; + } + if (sel_e0) + { + for (i = 0; i < 2; i++) + chip->op_wf[i][0] &= ~1; + + for (i = 0; i < 2; i++) + chip->op_wf[i][0] |= (chip->data >> i) & 1; + } + } + } + if (chip->clk2) + { + FMOPL2_DoShiftRegisters(chip, 1); + } + + //if (chip->clk2) // opt + { + int shift = 0; + + if (chip->fsm_out[13]) + shift = 8; + else if (chip->fsm_out[12]) + shift = 5; + else if (chip->fsm_out[15]) + shift = 2; + + chip->block = 0; + chip->fnum = 0; + for (i = 0; i < 3; i++) + chip->block |= ((chip->ch_block[i][1] >> shift) & 1) << i; + for (i = 0; i < 10; i++) + chip->fnum |= ((chip->ch_fnum[i][1] >> shift) & 1) << i; + chip->keyon = (chip->ch_keyon[1] >> shift) & 1; + chip->connect = (chip->ch_connect[1] >> shift) & 1; + + chip->fb = 0; + if (chip->fsm_out[13]) + shift = 5; + else if (chip->fsm_out[12]) + shift = 2; + else if (chip->fsm_out[15]) + shift = 8; + for (i = 0; i < 3; i++) + chip->fb |= ((chip->ch_fb[i][1] >> shift) & 1) << i; + + chip->multi = 0; + chip->tl = 0; + chip->ksl = 0; + chip->ar = 0; + chip->dr = 0; + chip->sl = 0; + chip->rr = 0; + chip->wf = 0; + + for (i = 0; i < 4; i++) + chip->multi |= ((chip->op_multi[i][1] >> 17) & 1) << i; + + chip->ksr = (chip->op_ksr[1] >> 17) & 1; + chip->egt = (chip->op_egt[1] >> 17) & 1; + chip->vib = (chip->op_vib[1] >> 17) & 1; + chip->am = (chip->op_am[1] >> 17) & 1; + + for (i = 0; i < 6; i++) + chip->tl |= ((chip->op_tl[i][1] >> 17) & 1) << i; + + for (i = 0; i < 2; i++) + chip->ksl |= ((chip->op_ksl[i][1] >> 17) & 1) << i; + + for (i = 0; i < 4; i++) + chip->ar |= ((chip->op_ar[i][1] >> 17) & 1) << i; + + for (i = 0; i < 4; i++) + chip->dr |= ((chip->op_dr[i][1] >> 17) & 1) << i; + + for (i = 0; i < 4; i++) + chip->sl |= ((chip->op_sl[i][1] >> 17) & 1) << i; + + for (i = 0; i < 4; i++) + chip->rr |= ((chip->op_rr[i][1] >> 17) & 1) << i; + + for (i = 0; i < 2; i++) + chip->wf |= ((chip->op_wf[i][1] >> 17) & 1) << i; + + } + + if (chip->clk1) + { + chip->connect_l[0] = (chip->connect_l[1] << 1) | chip->connect; + chip->fb_l[0][0] = chip->fb; + chip->fb_l[1][0] = chip->fb_l[0][1]; + } + if (chip->clk2) + { + chip->connect_l[1] = chip->connect_l[0]; + chip->fb_l[0][1] = chip->fb_l[0][0]; + chip->fb_l[1][1] = chip->fb_l[1][0]; + } + + if (chip->clk1) + { + chip->eg_load1_l = chip->fsm_out[8]; + chip->eg_load2_l = chip->fsm_out[9]; + chip->eg_load3_l = chip->eg_subcnt_l[1] && chip->eg_sync_l[1]; + } + chip->eg_load1 = !chip->eg_load1_l && chip->fsm_out[8]; + chip->eg_load2 = !chip->eg_load2_l && chip->fsm_out[9]; + chip->eg_load3 = !chip->eg_load3_l && chip->eg_subcnt_l[1] && chip->eg_sync_l[1]; + + { + + if (chip->eg_load1) + chip->trem_step = chip->am_step; + if (chip->eg_load2) + chip->trem_out = chip->trem_value[1] & 127; + + if (chip->clk1) + { + int bit = chip->trem_value[1] & 1; + int reset = chip->reset1 || (chip->reg_test & 128) != 0; + + int step = ((chip->trem_step || (chip->reg_test & 8) != 0) && (chip->fsm_out[9] || chip->trem_dir[1])) + && chip->fsm_out[14]; + int carry = chip->fsm_out[14] && chip->trem_carry[1]; + + bit += step + carry; + + int of = (chip->trem_out == 0) || (chip->trem_out & 105) == 105; + + chip->trem_carry[0] = (bit & 2) != 0; + chip->trem_value[0] = (chip->trem_value[1] >> 1) & 255; + if (!reset) + chip->trem_value[0] |= (bit & 1) << 8; + chip->trem_of[0] = of; + + if (reset) + chip->trem_dir[0] = 0; + else + chip->trem_dir[0] = chip->trem_dir[1] ^ (of && !chip->trem_of[1]); + } + if (chip->clk2) + { + chip->trem_carry[1] = chip->trem_carry[0]; + chip->trem_value[1] = chip->trem_value[0]; + chip->trem_of[1] = chip->trem_of[0]; + chip->trem_dir[1] = chip->trem_dir[0]; + } + } + + { + + if (chip->eg_load3) + { + chip->eg_timer_low = chip->eg_timer[1] & 3; + chip->eg_shift = 0; + if (chip->eg_timer_masked[1] & 0x1555) + chip->eg_shift |= 1; + if (chip->eg_timer_masked[1] & 0x666) + chip->eg_shift |= 2; + if (chip->eg_timer_masked[1] & 0x1878) + chip->eg_shift |= 4; + if (chip->eg_timer_masked[1] & 0x1f80) + chip->eg_shift |= 8; + } + + if (chip->clk1) + { + int bit = chip->eg_timer[1] & 1; + int bit2; + int carry = chip->eg_carry[1] || (chip->eg_subcnt[1] && chip->eg_sync_l[1]); + bit += carry; + + if (chip->reset1) + bit2 = 0; + else + bit2 = bit & 1; + + chip->eg_timer[0] = (chip->eg_timer[1] >> 1) & 0x1ffff; + chip->eg_timer[0] |= bit2 << 17; + chip->eg_carry[0] = (bit & 2) != 0; + chip->eg_sync_l[0] = chip->fsm_out[8]; + chip->eg_mask[0] = (chip->reset1 || chip->fsm_out[8]) ? 0 : + (chip->eg_mask[1] || bit2); + chip->eg_timer_masked[0] = (chip->eg_timer_masked[1] >> 1) & 0x1ffff; + if (!chip->eg_mask[1]) + chip->eg_timer_masked[0] |= bit2 << 17; + + if (chip->reset1) + chip->eg_subcnt[0] = 0; + else + chip->eg_subcnt[0] = chip->eg_subcnt[1] ^ chip->fsm_out[8]; + + chip->eg_subcnt_l[0] = chip->eg_subcnt[1]; + } + if (chip->clk2) + { + chip->eg_timer[1] = chip->eg_timer[0]; + chip->eg_carry[1] = chip->eg_carry[0]; + chip->eg_sync_l[1] = chip->eg_sync_l[0]; + chip->eg_mask[1] = chip->eg_mask[0]; + chip->eg_timer_masked[1] = chip->eg_timer_masked[0]; + chip->eg_subcnt[1] = chip->eg_subcnt[0]; + chip->eg_subcnt_l[1] = chip->eg_subcnt_l[0]; + } + } + + if (chip->clk1) + { + static const int eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } + }; + + int state = 0; + if (chip->eg_state[0][1] & 0x20000) + state |= 1; + if (chip->eg_state[1][1] & 0x20000) + state |= 2; + + int dokon = state == eg_state_release && chip->keyon_comb; + int rate_sel = dokon ? eg_state_attack : state; + int rate = 0; + int ksr; + if (rate_sel == 0) + rate |= chip->ar; + if (rate_sel == 1) + rate |= chip->dr; + if (rate_sel == 3 || (rate_sel == 2 && !chip->egt)) + rate |= chip->rr; + + int sl = chip->sl; + if (chip->sl == 15) + sl |= 16; + + int ns = chip->reg_notesel ? (chip->fnum & 256) != 0 : (chip->fnum & 512) != 0; + + if (chip->ksr) + ksr = (chip->block << 1) | ns; + else + ksr = chip->block >> 1; + + int rate_hi = rate + (ksr >> 2); + if (rate_hi & 16) + rate_hi = 15; + + int maxrate = rate_hi == 15; + + int rate12 = rate_hi == 12; + int rate13 = rate_hi == 13; + int rate14 = rate_hi == 14; + + int inclow = 0; + + if (rate_hi < 12 && rate != 0 && chip->eg_subcnt[1]) + { + int sum = (rate_hi + chip->eg_shift) & 15; + switch (sum) + { + case 12: + inclow = 1; + break; + case 13: + inclow = (ksr & 2) != 0; + break; + case 14: + inclow = (ksr & 1) != 0; + break; + } + } + + int stephi = eg_stephi[ksr & 3][chip->eg_timer_low]; + + int step1 = 0; + int step2 = 0; + int step3 = 0; + + switch (rate_hi) + { + case 12: + step1 = stephi || chip->eg_subcnt[1]; + break; + case 13: + if (stephi) + step2 = 1; + else + step1 = 1; + break; + case 14: + if (stephi) + step3 = 1; + else + step2 = 1; + break; + case 15: + step3 = 1; + break; + } + + step1 |= inclow; + + int level = 0; + + for (i = 0; i < 9; i++) + { + level |= ((chip->eg_level[i][1] >> 17) & 1) << i; + } + + int slreach = (level >> 4) == sl; + int zeroreach = level == 0; + int silent = (level & 0x1f8) == 0x1f8; + + int nextstate = eg_state_attack; + + if (chip->reset1) + nextstate = eg_state_release; + else if (dokon) + nextstate = eg_state_attack; + else + { + if (!chip->keyon_comb) + nextstate = eg_state_release; + else if (state == eg_state_attack) + nextstate = zeroreach ? eg_state_decay : eg_state_attack; + else if (state == eg_state_decay) + nextstate = slreach ? eg_state_sustain : eg_state_decay; + else if (state == eg_state_sustain) + nextstate = eg_state_sustain; + else if (state == eg_state_release) + nextstate = eg_state_release; + } + + int linear = !dokon && !silent && ((state & 2) != 0 || (state == eg_state_decay && !slreach)); + int exponent = state == eg_state_attack && chip->keyon_comb && !maxrate && !zeroreach; + int instantattack = dokon && maxrate; + int mute = chip->reset1 || (state != eg_state_attack && silent && !dokon); + + int level2 = mute ? 0x1ff : (instantattack ? 0 : level); + + int add = 0; + int addshift = 0; + + if (exponent) + add |= (~level) >> 1; + if (linear) + add |= 4; + + if (step1) + addshift |= add >> 2; + if (step2) + addshift |= add >> 1; + if (step3) + addshift |= add >> 0; + + int levelnext = level2 + addshift; + + static const int eg_ksltable[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 + }; + + int ksl; + ksl = eg_ksltable[chip->fnum >> 6] ^ 127; + + ksl += ((chip->block ^ 7) + 1) << 3; + if (ksl & 128) + ksl = 0; + else + ksl = (ksl ^ 63) & 63; + + static int eg_kslshift[4] = { + 31, 1, 2, 0 + }; + + ksl = (ksl << 2) >> eg_kslshift[chip->ksl]; + + int ksltl = ksl + (chip->tl << 2); + + int tremolo; + + if (!chip->am) + tremolo = 0; + else if (chip->reg_dv) + tremolo = chip->trem_out >> 2; + else + tremolo = chip->trem_out >> 4; + + int ksltltrem = ksltl + tremolo; + int levelof = 0; + + if (ksltltrem & 0x200) + levelof = 1; + + int totallevel = level + (ksltltrem & 0x1ff); + if (totallevel & 0x200) + levelof = 1; + + int totallevelclamp = (chip->reg_test & 1) != 0 ? 0 : (levelof ? 0x1ff : (totallevel & 0x1ff)); + + + chip->eg_dokon = dokon; + + chip->eg_state[0][0] = (chip->eg_state[0][1] << 1) | ((nextstate & 1) != 0); + chip->eg_state[1][0] = (chip->eg_state[1][1] << 1) | ((nextstate & 2) != 0); + + for (i = 0; i < 9; i++) + { + chip->eg_level[i][0] = (chip->eg_level[i][1] << 1) | ((levelnext >> i) & 1); + } + chip->eg_out[0] = totallevelclamp; + + if (chip->fsm_out[9]) + { + for (i = 0; i < 9; i++) + { + if (chip->eg_out[1] & (1 << i)) + chip->dbg_serial[0] |= 1 << (17 - i); + } + } + + chip->eg_mute[0] = (chip->eg_mute[1] << 1) | mute; + } + if (chip->clk2) + { + chip->eg_state[0][1] = chip->eg_state[0][0]; + chip->eg_state[1][1] = chip->eg_state[1][0]; + + for (i = 0; i < 9; i++) + { + chip->eg_level[i][1] = chip->eg_level[i][0]; + } + chip->eg_out[1] = chip->eg_out[0]; + chip->eg_mute[1] = chip->eg_mute[0]; + } + + if (chip->clk1) + { + static const int pg_multi[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 + }; + int fnum = chip->fnum; + int freq; + int pg_add; + int vib_sel1 = (chip->vib_cnt[1] & 3) == 2; + int vib_sel2 = (chip->vib_cnt[1] & 1) == 1; + int vib_sh0 = chip->reg_dv && chip->vib && vib_sel1; + int vib_sh1 = (chip->reg_dv && chip->vib && vib_sel2) + || (!chip->reg_dv && chip->vib && vib_sel1); + int vib_sh2 = !chip->reg_dv && chip->vib && vib_sel2; + int vib_sign = (chip->vib_cnt[1] & 4) != 0 && chip->vib; + int vib_add = 0; + int pg_out = 0; + int phase; + int noise_bit; + if (vib_sh0) + vib_add |= (chip->fnum >> 7) & 7; + if (vib_sh1) + vib_add |= (chip->fnum >> 8) & 3; + if (vib_sh2) + vib_add |= (chip->fnum >> 9) & 1; + if (vib_sign) + { + vib_add ^= 1023; + } + fnum += vib_add; + fnum += vib_sign; + if (vib_sign) + fnum &= 1023; + + freq = (fnum << chip->block) >> 1; + + pg_add = (freq * pg_multi[chip->multi]) >> 1; + + for (i = 0; i < 19; i++) + { + pg_out |= ((chip->pg_phase[i][1] >> 17) & 1) << i; + } + + phase = ((chip->eg_dokon || (chip->reg_test & 4) != 0) ? 0 : pg_out) + pg_add; + + for (i = 0; i < 19; i++) + { + chip->pg_phase[i][0] = chip->pg_phase[i][1] << 1; + chip->pg_phase[i][0] |= (phase >> i) & 1; + } + + chip->dbg_serial[0] = chip->dbg_serial[1] >> 1; + + if (chip->fsm_out[9]) + { + chip->dbg_serial[0] |= pg_out & 511; + } + + noise_bit = ((chip->noise_lfsr[1] >> 22) ^ (chip->noise_lfsr[1] >> 8)) & 1; + + if ((chip->noise_lfsr[1] & 0x7fffff) == 0) + noise_bit |= 1; + + noise_bit |= (chip->reg_test & 128) != 0; + + chip->noise_lfsr[0] = (chip->noise_lfsr[1] << 1) | noise_bit; + } + if (chip->clk2) + { + for (i = 0; i < 19; i++) + { + chip->pg_phase[i][1] = chip->pg_phase[i][0]; + } + + chip->noise_lfsr[1] = chip->noise_lfsr[0]; + + chip->pg_out = 0; + for (i = 0; i < 10; i++) + { + chip->pg_out |= ((chip->pg_phase[i+9][1] >> 17) & 1) << i; + } + + chip->dbg_serial[1] = chip->dbg_serial[0]; + } + + { + int hh = chip->fsm_out[4] && chip->rhythm; + int sd = chip->fsm_out[7] && chip->rhythm; + int tc = chip->fsm_out[8] && chip->rhythm; + int rhy = (chip->fsm_out[4] || chip->fsm_out[7] || chip->fsm_out[8]) && chip->rhythm; + if (chip->clk1) + chip->hh_load = chip->fsm_out[4]; + if (!chip->hh_load && chip->fsm_out[5]) + { + chip->hh_bit2 = (chip->pg_out >> 2) & 1; + chip->hh_bit3 = (chip->pg_out >> 3) & 1; + chip->hh_bit7 = (chip->pg_out >> 7) & 1; + chip->hh_bit8 = (chip->pg_out >> 8) & 1; + } + if (chip->clk1) + chip->tc_load = tc; + if (!chip->tc_load && tc) + { + chip->tc_bit3 = (chip->pg_out >> 3) & 1; + chip->tc_bit5 = (chip->pg_out >> 5) & 1; + } + + if (chip->clk1) // opt + { + int rm_bit; + int noise = (chip->noise_lfsr[1] >> 22) & 1; + + rm_bit = (chip->hh_bit2 ^ chip->hh_bit7) + | (chip->tc_bit5 ^ chip->hh_bit3) + | (chip->tc_bit5 ^ chip->tc_bit3); + + chip->pg_out_rhy = 0; + if (!rhy) + chip->pg_out_rhy |= chip->pg_out; + if (hh) + { + chip->pg_out_rhy |= rm_bit << 9; + if (noise ^ rm_bit) + chip->pg_out_rhy |= 0xd0; + else + chip->pg_out_rhy |= 0x34; + } + if (sd) + chip->pg_out_rhy |= (chip->hh_bit8 << 9) | ((noise ^ chip->hh_bit8) << 8); + if (tc) + chip->pg_out_rhy |= (rm_bit << 9) | 0x100; + } + } + + { + + if (chip->clk1) + { + static const int logsin[128] = { + 0x6c3, 0x58b, 0x4e4, 0x471, 0x41a, 0x3d3, 0x398, 0x365, 0x339, 0x311, 0x2ed, 0x2cd, 0x2af, 0x293, 0x279, 0x261, + 0x24b, 0x236, 0x222, 0x20f, 0x1fd, 0x1ec, 0x1dc, 0x1cd, 0x1be, 0x1b0, 0x1a2, 0x195, 0x188, 0x17c, 0x171, 0x166, + 0x15b, 0x150, 0x146, 0x13c, 0x133, 0x129, 0x121, 0x118, 0x10f, 0x107, 0x0ff, 0x0f8, 0x0f0, 0x0e9, 0x0e2, 0x0db, + 0x0d4, 0x0cd, 0x0c7, 0x0c1, 0x0bb, 0x0b5, 0x0af, 0x0a9, 0x0a4, 0x09f, 0x099, 0x094, 0x08f, 0x08a, 0x086, 0x081, + 0x07d, 0x078, 0x074, 0x070, 0x06c, 0x068, 0x064, 0x060, 0x05c, 0x059, 0x055, 0x052, 0x04e, 0x04b, 0x048, 0x045, + 0x042, 0x03f, 0x03c, 0x039, 0x037, 0x034, 0x031, 0x02f, 0x02d, 0x02a, 0x028, 0x026, 0x024, 0x022, 0x020, 0x01e, + 0x01c, 0x01a, 0x018, 0x017, 0x015, 0x014, 0x012, 0x011, 0x00f, 0x00e, 0x00d, 0x00c, 0x00a, 0x009, 0x008, 0x007, + 0x007, 0x006, 0x005, 0x004, 0x004, 0x003, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000 + }; + static const int logsin_d[128] = { + 0x196, 0x07c, 0x04a, 0x035, 0x029, 0x022, 0x01d, 0x019, 0x015, 0x013, 0x012, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x009, 0x008, 0x007, 0x007, 0x007, 0x007, 0x006, 0x007, 0x006, 0x006, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x003, 0x004, 0x003, 0x003, 0x003, + 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x002, 0x002, 0x002, 0x001, + 0x001, 0x001, 0x002, 0x002, 0x001, 0x001, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x000, 0x001, 0x000, 0x001, 0x000, 0x001, 0x001, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x001, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 + }; + static const int pow[128] = { + 0x3f5, 0x3ea, 0x3df, 0x3d4, 0x3c9, 0x3bf, 0x3b4, 0x3a9, 0x39f, 0x394, 0x38a, 0x37f, 0x375, 0x36a, 0x360, 0x356, + 0x34c, 0x342, 0x338, 0x32e, 0x324, 0x31a, 0x310, 0x306, 0x2fd, 0x2f3, 0x2e9, 0x2e0, 0x2d6, 0x2cd, 0x2c4, 0x2ba, + 0x2b1, 0x2a8, 0x29e, 0x295, 0x28c, 0x283, 0x27a, 0x271, 0x268, 0x25f, 0x257, 0x24e, 0x245, 0x23c, 0x234, 0x22b, + 0x223, 0x21a, 0x212, 0x209, 0x201, 0x1f9, 0x1f0, 0x1e8, 0x1e0, 0x1d8, 0x1d0, 0x1c8, 0x1c0, 0x1b8, 0x1b0, 0x1a8, + 0x1a0, 0x199, 0x191, 0x189, 0x181, 0x17a, 0x172, 0x16b, 0x163, 0x15c, 0x154, 0x14d, 0x146, 0x13e, 0x137, 0x130, + 0x129, 0x122, 0x11b, 0x114, 0x10c, 0x106, 0x0ff, 0x0f8, 0x0f1, 0x0ea, 0x0e3, 0x0dc, 0x0d6, 0x0cf, 0x0c8, 0x0c2, + 0x0bb, 0x0b5, 0x0ae, 0x0a8, 0x0a1, 0x09b, 0x094, 0x08e, 0x088, 0x082, 0x07b, 0x075, 0x06f, 0x069, 0x063, 0x05d, + 0x057, 0x051, 0x04b, 0x045, 0x03f, 0x039, 0x033, 0x02d, 0x028, 0x022, 0x01c, 0x016, 0x011, 0x00b, 0x006, 0x000, + }; + static const int pow_d[128] = { + 0x005, 0x005, 0x005, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x006, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, 0x004, 0x004, 0x004, + 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x003, 0x003, 0x004, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, + }; + int phase = chip->pg_out_rhy + chip->op_mod[1]; + int sign = (phase & 512) != 0; + int quarter = (phase & 256) != 0; + phase &= 255; + if (quarter) + phase ^= 255; + + int ls = logsin[phase >> 1]; + if ((phase & 1) == 0) + ls += logsin_d[phase >> 1]; + + int att = chip->op_logsin[1] + (chip->eg_out[1] << 3); + if (att & 4096) + att = 4095; + + int pw = pow[(att >> 1) & 127]; + if ((att & 1) == 0) + pw += pow_d[(att >> 1) & 127]; + + int value = 0; + + if (chip->op_mute[1] & 2) + { + value = ((chip->op_pow[1] | 0x400) << 1) >> chip->op_shift[1]; + } + + if (chip->op_sign[1] & 2) + value ^= 8191; + + int sign_wf = sign && chip->wf == 0; + int mute_wf = !((chip->wf == 1 && sign) || (chip->wf == 3 && quarter)); + + int fb1 = 0; + int fb2 = 0; + for (i = 0; i < 14; i++) + { + int j = i; + if (i == 13) + j = 12; + fb1 |= ((chip->op_fb[0][j][1] >> 5) & 1) << i; + fb2 |= ((chip->op_fb[1][j][1] >> 5) & 1) << i; + } + int fb_sum = fb1 + fb2; + fb_sum &= 16383; + if (fb_sum & 8192) + fb_sum |= ~8191; + + int mod = 0; + + if (chip->fsm_out[2] && !(chip->connect_l[1] & 2)) + mod |= value & 1023; + if (chip->fsm_out[1]) + { + if (chip->fb_l[1][1]) + { + mod |= (fb_sum >> (9 - chip->fb_l[1][1])) & 1023; + } + } + + chip->op_logsin[0] = ls; + chip->op_shift[0] = (att >> 8) & 15; + chip->op_pow[0] = pw; + chip->op_mute[0] = (chip->op_mute[1] << 1) | mute_wf; + chip->op_sign[0] = (chip->op_sign[1] << 1) | sign_wf; + + for (i = 0; i < 13; i++) + { + int bit; + chip->op_fb[0][i][0] = chip->op_fb[0][i][1] << 1; + if (chip->fsm_out[2]) + bit = (value >> i) & 1; + else + bit = (chip->op_fb[0][i][1] >> 8) & 1; + chip->op_fb[0][i][0] |= bit; + chip->op_fb[1][i][0] = chip->op_fb[1][i][1] << 1; + if (chip->fsm_out[2]) + bit = (chip->op_fb[0][i][1] >> 8) & 1; + else + bit = (chip->op_fb[1][i][1] >> 8) & 1; + chip->op_fb[1][i][0] |= bit; + } + chip->op_mod[0] = mod & 1023; + + chip->op_value = value; + } + if (chip->clk2) + { + chip->op_logsin[1] = chip->op_logsin[0]; + chip->op_shift[1] = chip->op_shift[0]; + chip->op_pow[1] = chip->op_pow[0]; + chip->op_mute[1] = chip->op_mute[0]; + chip->op_sign[1] = chip->op_sign[0]; + + for (i = 0; i < 13; i++) + { + chip->op_fb[0][i][1] = chip->op_fb[0][i][0]; + chip->op_fb[1][i][1] = chip->op_fb[1][i][0]; + } + chip->op_mod[1] = chip->op_mod[0]; + } + } + + { + int accm_out = chip->fsm_out[8] ? (chip->accm_value[1] & 0x7fff) : 0; + if (chip->fsm_out[8] && !(chip->accm_value[1] & 0x20000)) + accm_out |= 0x8000; + + int top = (chip->accm_value[1] >> 15) & 7; + + int clamplow = top == 4 || top == 5 || top == 6; + int clamphigh = top == 3 || top == 2 || top == 1; + + if (chip->clk1) + chip->accm_load1_l = chip->fsm_out[8]; + chip->accm_load1 = !chip->accm_load1_l && chip->fsm_out[8]; + + if (chip->accm_load1) + { + chip->accm_clamplow = clamplow; + chip->accm_clamphigh = clamphigh; + chip->accm_top = (accm_out >> 9) & 127; + } + + if (chip->clk1) + { + int add = 0; + int op_out = chip->op_value; + if (op_out & 0x1000) + op_out |= ~0xfff; + if (!(chip->eg_mute[1] & 2) && chip->fsm_out[0]) + add = chip->fsm_out[11] ? (op_out * 2) : op_out; + + int value = chip->fsm_out[8] ? 0 : chip->accm_value[1]; + value += add; + + int sign = ((chip->accm_top & 64) != 0 && !chip->accm_clamplow) || chip->accm_clamphigh; + + int top_unsigned = chip->accm_top & 63; + if (!sign) + top_unsigned ^= 63; + + int shift = 0; + + if (top_unsigned & 32) + shift |= 7; + if ((top_unsigned & 48) == 16) + shift |= 6; + if ((top_unsigned & 56) == 8) + shift |= 5; + if ((top_unsigned & 60) == 4) + shift |= 4; + if ((top_unsigned & 62) == 2) + shift |= 3; + if (top_unsigned == 1) + shift |= 2; + if (top_unsigned == 0) + shift |= 1; + if (chip->accm_clamplow) + shift |= 7; + if (chip->accm_clamphigh) + shift |= 7; + + int accm_bit = 0; + + if (chip->fsm_out[6]) + accm_bit |= sign; + if (chip->accm_sel[1] & 1) + accm_bit |= (shift & 1) != 0; + if (chip->accm_sel[1] & 2) + accm_bit |= (shift & 2) != 0; + if (chip->accm_sel[1] & 4) + accm_bit |= (shift & 4) != 0; + + if ((chip->accm_sel[1] & 7) == 0 && !chip->fsm_out[6]) + { + if (top_unsigned & 32) + accm_bit |= (chip->accm_shifter[1] >> 6) & 1; + if ((top_unsigned & 48) == 16) + accm_bit |= (chip->accm_shifter[1] >> 5) & 1; + if ((top_unsigned & 56) == 8) + accm_bit |= (chip->accm_shifter[1] >> 4) & 1; + if ((top_unsigned & 60) == 4) + accm_bit |= (chip->accm_shifter[1] >> 3) & 1; + if ((top_unsigned & 62) == 2) + accm_bit |= (chip->accm_shifter[1] >> 2) & 1; + if (top_unsigned == 1) + accm_bit |= (chip->accm_shifter[1] >> 1) & 1; + if (top_unsigned == 0) + accm_bit |= chip->accm_shifter[1] & 1; + if (chip->accm_clamphigh) + accm_bit |= 1; + if (chip->accm_clamplow) + accm_bit = 0; + } + + chip->accm_value[0] = value & 0x3ffff; + chip->accm_shifter[0] = (chip->accm_shifter[1] >> 1) & 0x7fff; + if (chip->fsm_out[8]) + chip->accm_shifter[0] |= accm_out; + chip->accm_sel[0] = (chip->accm_sel[1] << 1) | chip->fsm_out[6]; + chip->accm_mo[0] = accm_bit; + } + if (chip->clk2) + { + chip->accm_value[1] = chip->accm_value[0]; + chip->accm_shifter[1] = chip->accm_shifter[0]; + chip->accm_sel[1] = chip->accm_sel[0]; + chip->accm_mo[1] = chip->accm_mo[0]; + } + } + +end: + chip->o_sh = chip->fsm_out[3]; + chip->o_mo = chip->accm_mo[1]; + chip->o_irq_pull = irq; + chip->o_sy = chip->clk1; + + if (chip->io_read0) + { + chip->io_data &= ~6; + if (chip->reg_test & 64) + chip->io_data |= chip->dbg_serial[1] & 1; + if (irq) + chip->io_data |= 128; + if (chip->t1_status) + chip->io_data |= 64; + if (chip->t2_status) + chip->io_data |= 32; + if (chip->unk_status1) + chip->io_data |= 16; + if (chip->unk_status2) + chip->io_data |= 8; + } + + if (chip->io_dir) + { + chip->data_o = chip->io_data; + chip->data_z = 0; + } + else + chip->data_z = 1; +} diff --git a/extern/YM3812-LLE/fmopl2.h b/extern/YM3812-LLE/fmopl2.h new file mode 100644 index 000000000..8363886f9 --- /dev/null +++ b/extern/YM3812-LLE/fmopl2.h @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YM3812-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * YM3812 emulator + * Thanks: + * Travis Goodspeed: + * YM3812 decap and die shot + * + */ + +#pragma once + +typedef struct +{ + int mclk; + int address; + int data_i; + int ic; + int cs; + int rd; + int wr; +} fmopl2_input_t; + +typedef struct +{ + fmopl2_input_t input; + + int mclk1; + int mclk2; + int clk1; + int clk2; + + int prescaler_reset_l[2]; + int prescaler_cnt[2]; + int prescaler_l1[2]; + int prescaler_l2[2]; + + int reset1; + + int fsm_reset_l[2]; + int fsm_reset; // wire + int fsm_cnt1[2]; + int fsm_cnt2[2]; + int fsm_cnt1_of; // wire + int fsm_cnt2_of; // wire + int fsm_sel[13]; + int fsm_cnt; // wire + int fsm_ch_out; + int fsm_do_fb; + int fsm_load_fb; + int fsm_l1[2]; + int fsm_l2[2]; + int fsm_l3[2]; + int fsm_l4[2]; + int fsm_l5[2]; + int fsm_l6[2]; + int fsm_out[16]; + + int io_rd; + int io_wr; + int io_cs; + int io_a0; + + int io_read0; + int io_read1; + int io_write; + int io_write0; + int io_write1; + int io_dir; + int io_data; + + int data_latch; + + int write0; + int write0_sr; + int write0_latch[6]; + int write1; + int write1_sr; + int write1_latch[6]; + + int reg_sel1; + int reg_sel2; + int reg_sel3; + int reg_sel4; + int reg_sel8; + int reg_selbd; + int reg_test; + int reg_timer1; + int reg_timer2; + int reg_notesel; + int reg_csm; + int reg_da; + int reg_dv; + int rhythm; + int reg_rh_kon; + int reg_sel4_wr; // wire + int reg_sel4_rst; // wire + int reg_t1_mask; + int reg_t2_mask; + int reg_t1_start; + int reg_t2_start; + int reg_mode_b3; + int reg_mode_b4; + + int t1_cnt[2]; + int t2_cnt[2]; + int t1_of[2]; + int t2_of[2]; + int t1_status; + int t2_status; + int unk_status1; + int unk_status2; + int timer_st_load_l; + int timer_st_load; + int t1_start; + int t1_start_l[2]; + int t2_start_l[2]; + int t1_load; // wire + int csm_load_l; + int csm_load; + int csm_kon; + int rh_sel0; + int rh_sel[2]; + + int keyon_comb; + int address; + int address_valid; + int address_valid_l[2]; + int address_valid2; + int data; + int slot_cnt1[2]; + int slot_cnt2[2]; + int slot_cnt; + int sel_ch; + + int ch_fnum[10][2]; + int ch_block[3][2]; + int ch_keyon[2]; + int ch_connect[2]; + int ch_fb[3][2]; + int op_multi[4][2]; + int op_ksr[2]; + int op_egt[2]; + int op_vib[2]; + int op_am[2]; + int op_tl[6][2]; + int op_ksl[2][2]; + int op_ar[4][2]; + int op_dr[4][2]; + int op_sl[4][2]; + int op_rr[4][2]; + int op_wf[2][2]; + int op_mod[2]; + int op_value; // wire + + int eg_load1_l; + int eg_load1; + int eg_load2_l; + int eg_load2; + int eg_load3_l; + int eg_load3; + + int trem_carry[2]; + int trem_value[2]; + int trem_dir[2]; + int trem_step; + int trem_out; + int trem_of[2]; + + int eg_timer[2]; + int eg_timer_masked[2]; + int eg_carry[2]; + int eg_mask[2]; + int eg_subcnt[2]; + int eg_subcnt_l[2]; + int eg_sync_l[2]; + int eg_timer_low; + int eg_shift; + int eg_state[2][2]; + int eg_level[9][2]; + int eg_out[2]; + int eg_dokon; // wire + int eg_mute[2]; + + int block; + int fnum; + int keyon; + int connect; + int connect_l[2]; + int fb; + int fb_l[2][2]; + int multi; + int ksr; + int egt; + int vib; + int am; + int tl; + int ksl; + int ar; + int dr; + int sl; + int rr; + int wf; + + int lfo_cnt[2]; + int t1_step; // wire + int t2_step; // wire + int am_step; // wire + int vib_step; // wire + int vib_cnt[2]; + int pg_phase[19][2]; + int dbg_serial[2]; + + int noise_lfsr[2]; + + int hh_load; + int tc_load; + int hh_bit2; + int hh_bit3; + int hh_bit7; + int hh_bit8; + int tc_bit3; + int tc_bit5; + int op_logsin[2]; + int op_shift[2]; + int op_pow[2]; + int op_mute[2]; + int op_sign[2]; + int op_fb[2][13][2]; + + int pg_out; // wire + int pg_out_rhy; // wire + + int accm_value[2]; + int accm_shifter[2]; + int accm_load1_l; + int accm_load1; + int accm_clamplow; + int accm_clamphigh; + int accm_top; + int accm_sel[2]; + int accm_mo[2]; + + int o_sh; + int o_mo; + int o_irq_pull; + int o_sy; + + int data_o; + int data_z; + + int o_clk1; + int o_clk2; + int o_reset1; + int o_write0; + int o_write1; + int o_data_latch; + +} fmopl2_t; + diff --git a/extern/YMF262-LLE/LICENSE b/extern/YMF262-LLE/LICENSE new file mode 100644 index 000000000..89e08fb00 --- /dev/null +++ b/extern/YMF262-LLE/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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 program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; 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. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/extern/YMF262-LLE/Readme.md b/extern/YMF262-LLE/Readme.md new file mode 100644 index 000000000..f9cf1b581 --- /dev/null +++ b/extern/YMF262-LLE/Readme.md @@ -0,0 +1,13 @@ +# YMF262-LLE + +Yamaha YMF262 (OPL3) emulator using YMF262 die shot. + +Special thanks to John McMaster for decapping YMF262. + +https://siliconpr0n.org/map/yamaha/ymf262-m/ + +# MODIFICATION DISCLAIMER + +this is a modified version of YMF262-LLE which adds functions to allow its usage. + +the original Git commit is 63406354d05bc860a6762377ddbb9e2609bd6c36. diff --git a/extern/YMF262-LLE/fmopl3.c b/extern/YMF262-LLE/fmopl3.c new file mode 100644 index 000000000..4e82fb27c --- /dev/null +++ b/extern/YMF262-LLE/fmopl3.c @@ -0,0 +1,1646 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YMF262-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * YMF262 emulator + * Thanks: + * John McMaster (siliconpr0n.org): + * YMF262 decap and die shot + * + */ + +#include "fmopl3.h" + + +enum { + eg_state_attack = 0, + eg_state_decay, + eg_state_sustain, + eg_state_release +}; + +void FMOPL3_Clock(fmopl3_t *chip) +{ + int i; + + chip->mclk1 = !chip->input.mclk; + chip->mclk2 = chip->input.mclk; + + chip->io_rd = !chip->input.rd; + chip->io_wr = !chip->input.wr; + chip->io_cs = !chip->input.cs; + chip->io_a0 = chip->input.address & 1; + chip->io_a1 = (chip->input.address & 2) != 0; + + if (chip->mclk1) + { + chip->ic_latch[0] = (chip->ic_latch[1] << 1) | (!chip->input.ic); + } + if (chip->mclk2) + { + chip->ic_latch[1] = chip->ic_latch[0]; + + chip->reset0 = (chip->ic_latch[1] & 2) != 0; + } + + chip->io_read = !chip->reset0 && chip->io_cs && chip->io_rd && !chip->io_a0 && !chip->io_a1; + chip->io_write = !chip->reset0 && chip->io_cs && chip->io_wr; + chip->io_write0 = !chip->reset0 && chip->io_cs && chip->io_wr && !chip->io_a0; + chip->io_write1 = !chip->reset0 && chip->io_cs && chip->io_wr && chip->io_a0; + + if (chip->reset0) + chip->data_latch = 0; + else if (chip->io_write) + chip->data_latch = chip->input.data_i & 255; + if (chip->reset0) + chip->bank_latch = 0; + else if (chip->io_write0) + chip->bank_latch = chip->io_a1; + + if (chip->mclk2) + { + chip->prescaler1_reset[1] = chip->prescaler1_reset[0]; + chip->prescaler1_cnt[1] = chip->prescaler1_cnt[0]; + } + + int prescaler1_clk = (chip->reg_test1 & 0x40) != 0 ? chip->input.mclk : !(chip->prescaler1_cnt[1] & 2); + + chip->aclk1 = !prescaler1_clk; + chip->aclk2 = prescaler1_clk; + + if (chip->aclk2) + { + chip->prescaler2_reset_l[1] = chip->prescaler2_reset_l[0]; + chip->prescaler2_cnt[1] = chip->prescaler2_cnt[0]; + chip->prescaler2_l1[1] = chip->prescaler2_l1[0]; + chip->prescaler2_l3[1] = chip->prescaler2_l3[0]; + chip->prescaler2_l5[1] = chip->prescaler2_l5[0]; + chip->prescaler2_l6[1] = chip->prescaler2_l6[0]; + } + + + chip->clk1 = chip->prescaler2_l1[1] && !chip->prescaler2_l2; + chip->clk2 = chip->prescaler2_l3[1] && !chip->prescaler2_l4; + + chip->rclk1 = chip->prescaler2_l6[1]; + chip->rclk2 = chip->prescaler2_l5[1]; + + if (chip->aclk1) + { + + int ga = (chip->data_latch & 0xe0) != 0; + + int write0 = ga && chip->write0 && (chip->reg_test1 & 16) != 0; + int write = chip->write1 || write0; + + chip->ra_w1_l1 = write; + } + + if (chip->clk2) + { + chip->write0_l[1] = chip->write0_l[0]; + chip->write0_l[3] = chip->write0_l[2]; + + chip->write1_l[1] = chip->write1_l[0]; + chip->write1_l[3] = chip->write1_l[2]; + + chip->write0 = chip->write0_l[3] && !chip->write0_l[1]; + chip->write1 = chip->write1_l[3] && !chip->write1_l[1]; + } + + ////////////////////// + + //if (chip->o_clk1 == chip->clk1 && chip->o_clk2 == chip->clk2 && chip->o_rclk1 == chip->rclk1 && chip->o_rclk2 == chip->rclk2 && chip->o_reset0 == chip->reset0 + // && chip->o_ra_w1_l1 == chip->ra_w1_l1 && chip->o_bank_latch == chip->bank_latch && chip->o_data_latch == chip->data_latch) + // goto end; // opt + + chip->o_clk1 = chip->clk1; + chip->o_clk2 = chip->clk2; + chip->o_rclk1 = chip->rclk1; + chip->o_rclk2 = chip->rclk2; + chip->o_reset0 = chip->reset0; + chip->o_data_latch = chip->data_latch; + chip->o_bank_latch = chip->bank_latch; + chip->o_ra_w1_l1 = chip->ra_w1_l1; + + if (chip->reset0) + { + chip->reg_sel1 = 0; + chip->reg_sel2 = 0; + chip->reg_sel3 = 0; + chip->reg_sel4 = 0; + chip->reg_sel5 = 0; + chip->reg_sel8 = 0; + chip->reg_selbd = 0; + } + else if (chip->write0) + { + chip->reg_sel1 = chip->data_latch == 1; + chip->reg_sel2 = chip->data_latch == 2; + chip->reg_sel3 = chip->data_latch == 3; + chip->reg_sel4 = chip->data_latch == 4; + chip->reg_sel5 = chip->data_latch == 5; + chip->reg_sel8 = chip->data_latch == 8; + chip->reg_selbd = chip->data_latch == 0xbd; + } + + if (chip->reset0) + chip->reg_new = 0; + else if (chip->write1 && chip->bank_latch && chip->reg_sel5) + chip->reg_new = chip->data_latch & 1; + + int bank_masked = chip->reg_new && chip->bank_latch; + + if (chip->reset0) + { + chip->reg_test0 = 0; + chip->reg_test1 = 0; + chip->reg_timer1 = 0; + chip->reg_timer2 = 0; + chip->reg_notesel = 0; + chip->rhythm = 0; + chip->reg_rh_kon = 0; + chip->reg_da = 0; + chip->reg_dv = 0; + } + else if (chip->write1) + { + if (chip->reg_sel1 && !bank_masked) + chip->reg_test0 = chip->data_latch & 255; + if (chip->reg_sel2 && !bank_masked) + chip->reg_timer1 = chip->data_latch & 255; + if (chip->reg_sel3 && !bank_masked) + chip->reg_timer2 = chip->data_latch & 255; + if (chip->reg_sel8 && !bank_masked) + { + chip->reg_notesel = (chip->data_latch & 64) != 0; + } + if (chip->reg_selbd && !bank_masked) + { + chip->reg_rh_kon = chip->data_latch & 31; + chip->rhythm = (chip->data_latch & 32) != 0; + chip->reg_dv = (chip->data_latch & 64) != 0; + chip->reg_da = (chip->data_latch & 128) != 0; + } + if (chip->reg_sel1 && bank_masked) + chip->reg_test1 = chip->data_latch & 255; + if (chip->reg_sel4 && bank_masked) + chip->reg_4op = chip->data_latch & 63; + } + int reg_sel4_wr = chip->write1 && chip->reg_sel4 && !bank_masked && (chip->data_latch & 128) == 0; + int reg_sel4_rst = (chip->write1 && chip->reg_sel4 && !bank_masked && (chip->data_latch & 128) != 0) || chip->reset0; + + if (chip->reset0) + { + chip->reg_t1_mask = 0; + chip->reg_t2_mask = 0; + chip->reg_t1_start = 0; + chip->reg_t2_start = 0; + } + else if (reg_sel4_wr) + { + chip->reg_t1_mask = (chip->data_latch & 64) != 0; + chip->reg_t2_mask = (chip->data_latch & 32) != 0; + chip->reg_t1_start = (chip->data_latch & 1) != 0; + chip->reg_t2_start = (chip->data_latch & 2) != 0; + } + + chip->reset1 = chip->reset0 || (chip->reg_test1 & 0xc0) == 0xc0; + + { + //int bclk = !prescaler2_reset && chip->prescaler2_l7 && (chip->prescaler2_cnt[1] & 1) == 0; + + int ga = (chip->data_latch & 0xe0) != 0; + + if (chip->reset1) + chip->ra_address_latch = 0; + else if (chip->write0 && ga) + chip->ra_address_latch = (bank_masked << 8) | chip->data_latch; + if (chip->reset1) + chip->ra_address_good = 0; + else if (chip->write0) + chip->ra_address_good = ga; + if (chip->reset1) + chip->ra_data_latch = 0; + else if (chip->write1 && chip->ra_address_good) + chip->ra_data_latch = chip->data_latch; + + int write0 = ga && chip->write0 && (chip->reg_test1 & 16) != 0; + int write = chip->write1 || write0; + + if (chip->aclk1) + chip->ra_w1_l1 = write; + chip->ra_write = (write && !chip->ra_w1_l1) || (chip->reset1 && chip->clk2); + if (chip->clk1) + chip->ra_w1_l2 = write; + chip->ra_write_a = write && !chip->ra_w1_l2; + + if (chip->clk1) + { + chip->ra_rst_l[0] = chip->reset1; + int rst = (chip->reset1 && !chip->ra_rst_l[1]) || chip->fsm_out[5]; + + int of1 = (chip->ra_cnt1[1] & 5) == 5; + int of2 = (chip->ra_cnt2[1] & 2) == 2 && of1; + int of4 = (chip->ra_cnt4[1] & 2) == 2; + if (rst || of1) + chip->ra_cnt1[0] = 0; + else + chip->ra_cnt1[0] = (chip->ra_cnt1[1] + 1) & 7; + if (rst || of2) + chip->ra_cnt2[0] = 0; + else + chip->ra_cnt2[0] = (chip->ra_cnt2[1] + of1) & 3; + if (rst) + chip->ra_cnt3[0] = 0; + else + chip->ra_cnt3[0] = (chip->ra_cnt3[1] + of2) & 1; + + if (rst || of4 || of1) + chip->ra_cnt4[0] = 0; + else + chip->ra_cnt4[0] = (chip->ra_cnt4[1] + 1) & 3; + + } + if (chip->clk2) + { + chip->ra_rst_l[1] = chip->ra_rst_l[0]; + chip->ra_cnt1[1] = chip->ra_cnt1[0]; + chip->ra_cnt2[1] = chip->ra_cnt2[0]; + chip->ra_cnt3[1] = chip->ra_cnt3[0]; + chip->ra_cnt4[1] = chip->ra_cnt4[0]; + chip->ra_cnt = (chip->ra_cnt3[1] << 5) | (chip->ra_cnt2[1] << 3) | chip->ra_cnt1[1]; + } + + if (chip->ra_write || chip->clk1) + { + static const int ch_map[32] = { + 0, 1, 2, -1, + 3, 4, 5, -1, + 6, 7, 8, -1, + -1, -1, -1, -1, + 9, 10, 11, -1, + 12, 13, 14, -1, + 15, 16, 17, -1, + -1, -1, -1, -1 + }; + + static const int op_map[64] = { + 0, 1, 2, 3, 4, 5, -1, -1, + 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 18, 19, 20, 21, 22, 23, -1, -1, + 24, 25, 26, 27, 28, 29, -1, -1, + 30, 31, 32, 33, 34, 35, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 + }; + + int bank = (chip->ra_address_latch & 0x100) != 0; + int op_address = chip->ra_write_a ? ((chip->ra_address_latch & 0x1f) | (bank << 5)) : chip->ra_cnt; + int idx = op_map[op_address]; + if (chip->ra_write && idx != -1) + { + if ((chip->ra_address_latch & 0xe0) == 0x20 || write0 || chip->reset1) + { + chip->ra_multi[idx] = chip->ra_data_latch & 15; + chip->ra_ksr[idx] = (chip->ra_data_latch >> 4) & 1; + chip->ra_egt[idx] = (chip->ra_data_latch >> 5) & 1; + chip->ra_vib[idx] = (chip->ra_data_latch >> 6) & 1; + chip->ra_am[idx] = (chip->ra_data_latch >> 7) & 1; + } + if ((chip->ra_address_latch & 0xe0) == 0x40 || write0 || chip->reset1) + { + chip->ra_tl[idx] = chip->ra_data_latch & 63; + chip->ra_ksl[idx] = (chip->ra_data_latch >> 6) & 3; + } + if ((chip->ra_address_latch & 0xe0) == 0x60 || write0 || chip->reset1) + { + chip->ra_dr[idx] = chip->ra_data_latch & 15; + chip->ra_ar[idx] = (chip->ra_data_latch >> 4) & 15; + } + if ((chip->ra_address_latch & 0xe0) == 0x80 || write0 || chip->reset1) + { + chip->ra_rr[idx] = chip->ra_data_latch & 15; + chip->ra_sl[idx] = (chip->ra_data_latch >> 4) & 15; + } + if ((chip->ra_address_latch & 0xe0) == 0xe0 || write0 || chip->reset1) + { + int data = chip->ra_data_latch & 3; + if (chip->reg_new) + data |= chip->ra_data_latch & 4; + chip->ra_wf[idx] = data; + } + } + int ch_address_write = chip->ra_address_latch & 15; + int add = 0; + if (ch_address_write == 3 || ch_address_write == 4 || ch_address_write == 5) + add |= 1; + if (ch_address_write == 6 || ch_address_write == 7 || ch_address_write == 8) + add |= 2; + int ch_address_mapped = (ch_address_write & 1) + (add & 1); + ch_address_mapped |= add & 2; + ch_address_mapped += ch_address_write & 14; + ch_address_mapped &= 15; + ch_address_mapped |= bank << 4; + int ch_address_mapped2 = ch_address_mapped & 3; + if ((ch_address_mapped & 12) == 8) + ch_address_mapped2 |= 4; + if ((ch_address_mapped & 12) == 0) + ch_address_mapped2 |= 8; + if ((ch_address_mapped & 28) == 0 || (ch_address_mapped & 28) == 20 || (ch_address_mapped & 28) == 24) + ch_address_mapped2 |= 16; + + + int ch_address_read = (chip->ra_cnt4[1] & 3) | (chip->ra_cnt2[1] << 2) | (chip->ra_cnt3[1] << 4); + int ch_address = chip->ra_write_a ? ch_address_mapped : ch_address_read; + int ch_address_read_4op = ch_address_read; + if ((chip->ra_cnt2[1] & 2) == 0) + { + switch (chip->ra_cnt3[1] * 4 + chip->ra_cnt4[1]) + { + case 0: // 0, 3, 6, 9 + if (chip->reg_4op & 1) + ch_address_read_4op &= ~4; + break; + case 1: // 1, 4, 7, 10 + if (chip->reg_4op & 2) + ch_address_read_4op &= ~4; + break; + case 2: // 2, 5, 8, 11 + if (chip->reg_4op & 4) + ch_address_read_4op &= ~4; + break; + case 4: // 0, 3, 6, 9 + if (chip->reg_4op & 8) + ch_address_read_4op &= ~4; + break; + case 5: // 1, 4, 7, 10 + if (chip->reg_4op & 16) + ch_address_read_4op &= ~4; + break; + case 6: // 2, 5, 8, 11 + if (chip->reg_4op & 32) + ch_address_read_4op &= ~4; + break; + } + } + int ch_address_4op = chip->ra_write_a ? ch_address_mapped : ch_address_read_4op; + int ch_address_fb = chip->ra_write_a ? ch_address_mapped2 : ch_address_read; + + int idx1 = ch_map[ch_address]; + int idx2 = ch_map[ch_address_4op]; + int idx3 = ch_map[ch_address_fb]; + if (chip->ra_write && idx1 != -1) + { + if ((chip->ra_address_latch & 0xf0) == 0xc0 || write0 || chip->reset1) + { + chip->ra_connect[idx1] = chip->ra_data_latch & 1; + int pan_data = 0; + if (!chip->reg_new || chip->reset1) + pan_data |= 3; + if (chip->reg_new) + pan_data |= (chip->ra_data_latch >> 4) & 15; + chip->ra_pan[idx1] = pan_data; + } + } + if (chip->ra_write && idx2 != -1) + { + if ((chip->ra_address_latch & 0xf0) == 0xa0 || write0 || chip->reset1) + { + chip->ra_fnum[idx2] &= 0x300; + chip->ra_fnum[idx2] |= chip->ra_data_latch & 0xff; + } + if ((chip->ra_address_latch & 0xf0) == 0xb0 || write0 || chip->reset1) + { + chip->ra_fnum[idx2] &= 0xff; + chip->ra_fnum[idx2] |= (chip->ra_data_latch & 3) << 8; + chip->ra_block[idx2] = (chip->ra_data_latch >> 2) & 7; + chip->ra_keyon[idx2] = (chip->ra_data_latch >> 5) & 1; + } + } + if (chip->ra_write && idx3 != -1) + { + if ((chip->ra_address_latch & 0xf0) == 0xc0 || write0 || chip->reset1) + { + chip->ra_connect_pair[idx3] = chip->ra_data_latch & 1; + chip->ra_fb[idx3] = (chip->ra_data_latch >> 1) & 7; + } + } + + if (chip->clk1) + { + if (idx != -1) + { + chip->multi[0] = chip->ra_multi[idx]; + chip->ksr[0] = chip->ra_ksr[idx]; + chip->egt[0] = chip->ra_egt[idx]; + chip->am[0] = chip->ra_am[idx]; + chip->vib[0] = chip->ra_vib[idx]; + chip->tl[0] = chip->ra_tl[idx]; + chip->ksl[0] = chip->ra_ksl[idx]; + chip->ar[0] = chip->ra_ar[idx]; + chip->dr[0] = chip->ra_dr[idx]; + chip->sl[0] = chip->ra_sl[idx]; + chip->rr[0] = chip->ra_rr[idx]; + chip->wf[0] = chip->ra_wf[idx]; + } + if (idx1 != -1) + { + chip->connect[0] = chip->ra_connect[idx1]; + chip->pan[0] = chip->ra_pan[idx1]; + } + if (idx2 != -1) + { + chip->fnum[0] = chip->ra_fnum[idx2]; + chip->block[0] = chip->ra_block[idx2]; + chip->keyon[0] = chip->ra_keyon[idx2]; + } + if (idx3 != -1) + { + chip->connect_pair[0] = chip->ra_connect_pair[idx3]; + chip->fb[0] = chip->ra_fb[idx3]; + } + } + } + if (chip->clk2) + { + chip->multi[1] = chip->multi[0]; + chip->ksr[1] = chip->ksr[0]; + chip->egt[1] = chip->egt[0]; + chip->am[1] = chip->am[0]; + chip->vib[1] = chip->vib[0]; + chip->tl[1] = chip->tl[0]; + chip->ksl[1] = chip->ksl[0]; + chip->ar[1] = chip->ar[0]; + chip->dr[1] = chip->dr[0]; + chip->sl[1] = chip->sl[0]; + chip->rr[1] = chip->rr[0]; + chip->wf[1] = chip->wf[0]; + + chip->connect[1] = chip->connect[0]; + chip->pan[1] = chip->pan[0]; + + chip->fnum[1] = chip->fnum[0]; + chip->block[1] = chip->block[0]; + chip->keyon[1] = chip->keyon[0]; + + chip->connect_pair[1] = chip->connect_pair[0]; + chip->fb[1] = chip->fb[0]; + } + } + + if (chip->clk1) + { + chip->connect_l[0] = (chip->connect_l[1] << 1) | chip->connect[1]; + chip->connect_pair_l[0] = (chip->connect_pair_l[1] << 1) | chip->connect_pair[1]; + chip->fb_l[0][0] = chip->fb[1]; + chip->fb_l[1][0] = chip->fb_l[0][1]; + chip->pan_l[0][0] = chip->pan[1]; + chip->pan_l[1][0] = chip->pan_l[0][1]; + } + if (chip->clk2) + { + chip->connect_l[1] = chip->connect_l[0]; + chip->connect_pair_l[1] = chip->connect_pair_l[0]; + chip->fb_l[0][1] = chip->fb_l[0][0]; + chip->fb_l[1][1] = chip->fb_l[1][0]; + chip->pan_l[0][1] = chip->pan_l[0][0]; + chip->pan_l[1][1] = chip->pan_l[1][0]; + } + + { + int fsm_4op = 0; + switch (chip->fsm_cnt) + { + case 5: // 5 + fsm_4op = (chip->reg_4op & 1) != 0; + break; + case 8: // 6 + fsm_4op = (chip->reg_4op & 2) != 0; + break; + case 9: // 7 + fsm_4op = (chip->reg_4op & 4) != 0; + break; + case 37: // 23 + fsm_4op = (chip->reg_4op & 8) != 0; + break; + case 40: // 24 + fsm_4op = (chip->reg_4op & 16) != 0; + break; + case 41: // 25 + fsm_4op = (chip->reg_4op & 32) != 0; + break; + } + int con_4op = fsm_4op && (chip->fsm_l10[1] & 4) != 0; // 01 connect + + if (chip->clk1) + { + int fsm_reset = (chip->fsm_reset_l[1] & 2) == 0 && chip->reset1; + chip->fsm_reset_l[0] = (chip->fsm_reset_l[1] << 1) | chip->reset1; + + int fsm_of1 = (chip->fsm_cnt1[1] & 5) == 5; + int fsm_of2 = (chip->fsm_cnt2[1] & 2) == 2 && fsm_of1; + + if (fsm_reset || fsm_of1) + chip->fsm_cnt1[0] = 0; + else + chip->fsm_cnt1[0] = (chip->fsm_cnt1[1] + 1) & 7; + + if (fsm_reset || fsm_of2) + chip->fsm_cnt2[0] = 0; + else + chip->fsm_cnt2[0] = (chip->fsm_cnt2[1] + fsm_of1) & 3; + + if (fsm_reset) + chip->fsm_cnt3[0] = 0; + else + chip->fsm_cnt3[0] = (chip->fsm_cnt3[1] + fsm_of2) & 1; + + chip->fsm_l1[0] = chip->fsm_cnt == 53; + chip->fsm_l2[0] = chip->fsm_cnt == 16; + chip->fsm_l3[0] = chip->fsm_cnt == 20; + chip->fsm_l4[0] = chip->fsm_cnt == 52; + chip->fsm_l5[0] = (chip->fsm_l5[1] << 1) | ((chip->fsm_cnt & 56) == 0); + chip->fsm_l6[0] = (chip->fsm_l6[1] << 1) | ((chip->fsm_cnt & 56) == 8 || (chip->fsm_cnt & 62) == 16); + chip->fsm_l7[0] = (chip->fsm_l7[1] << 1) | ((chip->fsm_cnt & 56) == 40 || (chip->fsm_cnt & 62) == 48); + chip->fsm_l8[0] = (chip->fsm_l8[1] << 1) | ((chip->fsm_cnt & 48) == 16); + chip->fsm_l9[0] = (chip->fsm_l9[1] << 1) | con_4op; + chip->fsm_l10[0] = (chip->fsm_l10[1] << 1) | ((chip->connect_l[1] & 2) == 0 && (chip->connect_pair_l[1] & 2) != 0); + } + if (chip->clk2) + { + chip->fsm_reset_l[1] = chip->fsm_reset_l[0]; + chip->fsm_cnt1[1] = chip->fsm_cnt1[0]; + chip->fsm_cnt2[1] = chip->fsm_cnt2[0]; + chip->fsm_cnt3[1] = chip->fsm_cnt3[0]; + + chip->fsm_cnt = (chip->fsm_cnt3[1] << 5) | (chip->fsm_cnt2[1] << 3) | chip->fsm_cnt1[1]; + + chip->fsm_l1[1] = chip->fsm_l1[0]; + chip->fsm_l2[1] = chip->fsm_l2[0]; + chip->fsm_l3[1] = chip->fsm_l3[0]; + chip->fsm_l4[1] = chip->fsm_l4[0]; + chip->fsm_l5[1] = chip->fsm_l5[0]; + chip->fsm_l6[1] = chip->fsm_l6[0]; + chip->fsm_l7[1] = chip->fsm_l7[0]; + chip->fsm_l8[1] = chip->fsm_l8[0]; + chip->fsm_l9[1] = chip->fsm_l9[0]; + chip->fsm_l10[1] = chip->fsm_l10[0]; + } + { + chip->fsm_out[0] = chip->fsm_l1[1]; // 0 + chip->fsm_out[1] = chip->fsm_cnt == 16; // 12 + chip->fsm_out[2] = chip->fsm_l2[1]; // 13 + chip->fsm_out[3] = chip->fsm_cnt == 20; // 16 + chip->fsm_out[4] = chip->fsm_l3[1]; // 17 + chip->fsm_out[5] = chip->fsm_cnt == 52; // 34 + chip->fsm_out[6] = chip->fsm_l4[1]; // 35 + chip->fsm_out[7] = (chip->fsm_l5[1] & 4) != 0 || ((chip->fsm_cnt & 56) == 0); // 0-8 + chip->fsm_out[8] = (chip->fsm_cnt & 32) == 0; + chip->fsm_out[9] = (chip->fsm_l6[1] & 2) != 0; + chip->fsm_out[10] = (chip->fsm_l7[1] & 2) != 0; + chip->fsm_out[11] = chip->rhythm && (chip->fsm_l8[1] & 2) != 0; // r 14, 15, 16, 17, 18, 19 + + int fsm_mc = !((chip->fsm_cnt & 5) == 4 || (chip->fsm_cnt & 2) != 0); + int fsm_mc_4op = fsm_mc && !fsm_4op; + int rhy_19_20 = chip->rhythm && (chip->fsm_cnt == 19 || chip->fsm_cnt == 20); + + chip->fsm_out[12] = fsm_mc_4op && !(chip->rhythm && (chip->fsm_cnt == 16 || chip->fsm_cnt == 17)); // feedback + chip->fsm_out[14] = con_4op || (!fsm_4op && !(chip->fsm_l9[1] & 4) && (chip->connect_l[1] & 2) != 0); // connect + chip->fsm_out[13] = !(chip->rhythm && chip->fsm_cnt == 18) && (fsm_mc_4op || rhy_19_20 || chip->fsm_out[14]); // output + chip->fsm_out[15] = !fsm_mc && !rhy_19_20; // load fb + chip->fsm_out[16] = !fsm_mc_4op && !rhy_19_20; // modulate + } + } + + if (chip->clk1) + chip->timer_st_load_l = chip->fsm_out[6]; + chip->timer_st_load = chip->fsm_out[6] && !chip->timer_st_load_l; + + if (chip->timer_st_load) + { + chip->t1_start = chip->reg_t1_start; + chip->t2_start = chip->reg_t2_start; + } + + if (chip->clk1) + { + int lfo = chip->lfo_cnt[1]; + int add = chip->fsm_out[6]; + int reset = (chip->reg_test0 & 2) != 0 || chip->reset1; + + chip->lfo_cnt[0] = reset ? 0 : (lfo + add) & 1023; + chip->vib_cnt[0] = reset ? 0 : (chip->vib_cnt[1] + chip->vib_step) & 7; + } + if (chip->clk2) + { + chip->lfo_cnt[1] = chip->lfo_cnt[0]; + chip->vib_cnt[1] = chip->vib_cnt[0]; + } + + { + int lfo = chip->lfo_cnt[1]; + int add = chip->fsm_out[6]; + + chip->t1_step = (((lfo & 3) + add) & 4) != 0; + chip->t2_step = (((lfo & 15) + add) & 16) != 0; + chip->am_step = (((lfo & 63) + add) & 64) != 0; + chip->vib_step = (((lfo & 1023) + add) & 1024) != 0; + chip->vib_step |= (chip->reg_test0 & 16) != 0 && add; + } + + if (chip->clk1) + { + int value = (chip->t1_of[1] || (chip->t1_start_l[1] & 3) == 1) ? chip->reg_timer1 : chip->t1_cnt[1]; + value += ((chip->t1_start_l[1] & 1) != 0 && chip->t1_step) || (chip->reg_test1 & 8) != 0; + chip->t1_of[0] = (value & 256) != 0; + chip->t1_cnt[0] = (chip->t1_start_l[1] & 1) == 0 ? 0 : (value & 255); + + value = (chip->t2_of[1] || (chip->t2_start_l[1] & 3) == 1) ? chip->reg_timer2 : chip->t2_cnt[1]; + value += ((chip->t2_start_l[1] & 1) != 0 && chip->t2_step) || (chip->reg_test1 & 8) != 0; + chip->t2_of[0] = (value & 256) != 0; + chip->t2_cnt[0] = (chip->t2_start_l[1] & 1) == 0 ? 0 : (value & 255); + + chip->t1_start_l[0] = (chip->t1_start_l[1] << 1) | chip->t1_start; + chip->t2_start_l[0] = (chip->t2_start_l[1] << 1) | chip->t2_start; + } + if (chip->clk2) + { + chip->t1_cnt[1] = chip->t1_cnt[0]; + chip->t1_of[1] = chip->t1_of[0]; + chip->t2_cnt[1] = chip->t2_cnt[0]; + chip->t2_of[1] = chip->t2_of[0]; + + chip->t1_start_l[1] = chip->t1_start_l[0]; + chip->t2_start_l[1] = chip->t2_start_l[0]; + } + + if (reg_sel4_rst || chip->reg_t1_mask) + chip->t1_status = 0; + else if (chip->t1_of[1]) + chip->t1_status = 1; + + if (reg_sel4_rst || chip->reg_t2_mask) + chip->t2_status = 0; + else if (chip->t2_of[1]) + chip->t2_status = 1; + + chip->rh_sel0 = chip->rhythm && chip->fsm_out[1]; + + if (chip->clk1) + { + chip->rh_sel[0] = (chip->rh_sel[1] << 1) | chip->rh_sel0; + } + if (chip->clk2) + { + chip->rh_sel[1] = chip->rh_sel[0]; + } + + chip->keyon_comb = chip->keyon[1] + || (chip->rh_sel0 && (chip->reg_rh_kon & 16) != 0) // bd0 + || ((chip->rh_sel[1] & 1) != 0 && (chip->reg_rh_kon & 1) != 0) // hh + || ((chip->rh_sel[1] & 2) != 0 && (chip->reg_rh_kon & 4) != 0) // tom + || ((chip->rh_sel[1] & 4) != 0 && (chip->reg_rh_kon & 16) != 0) // bd1 + || ((chip->rh_sel[1] & 8) != 0 && (chip->reg_rh_kon & 8) != 0) // sd + || ((chip->rh_sel[1] & 16) != 0 && (chip->reg_rh_kon & 2) != 0); // tc + + + if (chip->clk1) + { + chip->trem_load_l = chip->fsm_out[0]; + chip->trem_st_load_l = chip->fsm_out[6]; + chip->eg_load_l = chip->eg_load_l1[1]; + } + chip->trem_load = !chip->trem_load_l && chip->fsm_out[0]; + chip->trem_st_load = !chip->trem_st_load_l && chip->fsm_out[6]; + chip->eg_load = !chip->eg_load_l && chip->eg_load_l1[1]; + + { + if (chip->trem_st_load) + chip->trem_step = chip->am_step; + if (chip->trem_load) + chip->trem_out = chip->trem_value[1] & 127; + + if (chip->clk1) + { + int bit = chip->trem_value[1] & 1; + int reset = chip->reset1 || (chip->reg_test0 & 2) != 0; + + int step = ((chip->trem_step || (chip->reg_test0 & 16) != 0) && (chip->fsm_out[0] || chip->trem_dir[1])) + && chip->fsm_out[7]; + int carry = chip->fsm_out[7] && chip->trem_carry[1]; + + bit += step + carry; + + int of = (chip->trem_out == 0) || (chip->trem_out & 105) == 105; + + chip->trem_carry[0] = (bit & 2) != 0; + chip->trem_value[0] = (chip->trem_value[1] >> 1) & 255; + if (!reset) + chip->trem_value[0] |= (bit & 1) << 8; + chip->trem_of[0] = of; + + if (reset) + chip->trem_dir[0] = 0; + else + chip->trem_dir[0] = chip->trem_dir[1] ^ (of && !chip->trem_of[1]); + } + if (chip->clk2) + { + chip->trem_carry[1] = chip->trem_carry[0]; + chip->trem_value[1] = chip->trem_value[0]; + chip->trem_of[1] = chip->trem_of[0]; + chip->trem_dir[1] = chip->trem_dir[0]; + } + } + + { + + if (chip->reset1) + { + chip->eg_timer_low = 0; + chip->eg_shift = 0; + } + else if (chip->eg_load) + { + chip->eg_timer_low = chip->eg_timer_o[3] | (chip->eg_timer_o[1] << 1); + chip->eg_shift = 0; + if (chip->eg_timer_masked[1] & 0x1555) + chip->eg_shift |= 1; + if (chip->eg_timer_masked[1] & 0x666) + chip->eg_shift |= 2; + if (chip->eg_timer_masked[1] & 0x1878) + chip->eg_shift |= 4; + if (chip->eg_timer_masked[1] & 0x1f80) + chip->eg_shift |= 8; + } + + if (chip->clk1) + { + int bit = chip->eg_timer_o[3]; + int bit2; + int carry = chip->eg_carry[1] || (chip->eg_subcnt[1] && chip->eg_sync_l[1]); + bit += carry; + + int rst = chip->reset1 || (chip->reg_test1 & 8) != 0; + + if (rst) + bit2 = 0; + else + bit2 = bit & 1; + + chip->eg_timer_i = bit2; + chip->eg_carry[0] = (bit & 2) != 0; + chip->eg_sync_l[0] = chip->fsm_out[6]; + chip->eg_mask[0] = (rst || chip->fsm_out[6]) ? 0 : + (chip->eg_mask[1] || bit2); + chip->eg_timer_masked[0] = (chip->eg_timer_masked[1] >> 1) & 0x7ffffffffLL; + if (!chip->eg_mask[1]) + chip->eg_timer_masked[0] |= ((int64_t)bit2) << 35; + if (!chip->eg_timer_dbg[1] && (chip->reg_test0 & 64) != 0) + chip->eg_timer_masked[0] |= 1LL << 35; + + if (chip->reset1) + chip->eg_subcnt[0] = 0; + else + chip->eg_subcnt[0] = chip->eg_subcnt[1] ^ chip->fsm_out[6]; + + chip->eg_load_l1[0] = chip->eg_subcnt[1] && chip->fsm_out[6]; + + chip->eg_timer_dbg[0] = (chip->reg_test0 & 64) != 0; + } + if (chip->clk2) + { + chip->eg_carry[1] = chip->eg_carry[0]; + chip->eg_sync_l[1] = chip->eg_sync_l[0]; + chip->eg_mask[1] = chip->eg_mask[0]; + chip->eg_timer_masked[1] = chip->eg_timer_masked[0]; + chip->eg_subcnt[1] = chip->eg_subcnt[0]; + chip->eg_load_l1[1] = chip->eg_load_l1[0]; + chip->eg_timer_dbg[1] = chip->eg_timer_dbg[0]; + } + } + + if (chip->clk1) + { + static const int eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } + }; + + int rst = chip->reset1 || (chip->reg_test1 & 32) != 0; + + int state = chip->eg_state_o[3]; + int dokon = state == eg_state_release && chip->keyon_comb; + int rate_sel = dokon ? eg_state_attack : state; + int rate = 0; + int ksr; + if (rate_sel == 0) + rate |= chip->ar[1]; + if (rate_sel == 1) + rate |= chip->dr[1]; + if (rate_sel == 3 || (rate_sel == 2 && !chip->egt[1])) + rate |= chip->rr[1]; + + int sl = chip->sl[1]; + if (chip->sl[1] == 15) + sl |= 16; + + int ns = chip->reg_notesel ? (chip->fnum[1] & 256) != 0 : (chip->fnum[1] & 512) != 0; + + if (chip->ksr[1]) + ksr = (chip->block[1] << 1) | ns; + else + ksr = chip->block[1] >> 1; + + int rate_hi = rate + (ksr >> 2); + if (rate_hi & 16) + rate_hi = 15; + + int maxrate = rate_hi == 15; + + int rate12 = rate_hi == 12; + int rate13 = rate_hi == 13; + int rate14 = rate_hi == 14; + + int inclow = 0; + + if (rate_hi < 12 && rate != 0 && chip->eg_subcnt[1]) + { + int sum = (rate_hi + chip->eg_shift) & 15; + switch (sum) + { + case 12: + inclow = 1; + break; + case 13: + inclow = (ksr & 2) != 0; + break; + case 14: + inclow = (ksr & 1) != 0; + break; + } + } + + int stephi = eg_stephi[ksr & 3][chip->eg_timer_low]; + + int step1 = 0; + int step2 = 0; + int step3 = 0; + + switch (rate_hi) + { + case 12: + step1 = stephi || chip->eg_subcnt[1]; + break; + case 13: + if (stephi) + step2 = 1; + else + step1 = 1; + break; + case 14: + if (stephi) + step3 = 1; + else + step2 = 1; + break; + case 15: + step3 = 1; + break; + } + + step1 |= inclow; + + int level = chip->eg_level_o[3]; + int slreach = (level >> 4) == sl; + int zeroreach = level == 0; + int silent = (level & 0x1f8) == 0x1f8; + + static const int eg_ksltable[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 + }; + + int nextstate = eg_state_attack; + + if (rst) + nextstate = eg_state_release; + else if (dokon) + nextstate = eg_state_attack; + else + { + if (!chip->keyon_comb) + nextstate = eg_state_release; + else if (state == eg_state_attack) + nextstate = zeroreach ? eg_state_decay : eg_state_attack; + else if (state == eg_state_decay) + nextstate = slreach ? eg_state_sustain : eg_state_decay; + else if (state == eg_state_sustain) + nextstate = eg_state_sustain; + else if (state == eg_state_release) + nextstate = eg_state_release; + } + + int linear = !dokon && !silent && ((state & 2) != 0 || (state == eg_state_decay && !slreach)); + int exponent = state == eg_state_attack && chip->keyon_comb && !maxrate && !zeroreach; + int instantattack = (dokon && maxrate) || (chip->reg_test0 & 16) != 0; + int mute = rst || (state != eg_state_attack && silent && !dokon && !(chip->reg_test0 & 16)); + + int level2 = mute ? 0x1ff : (instantattack ? 0 : level); + + int add = 0; + int addshift = 0; + + if (exponent) + add |= (level >> 1) ^ 0xff; + if (linear) + add |= 4; + + if (exponent && (step1 || step2 || step3)) + addshift |= 256; + + if (step1) + addshift |= (add >> 2) | (exponent << 6) | (exponent << 7) | linear; + if (step2) + addshift |= (add >> 1) | (exponent << 7) | (linear << 1); + if (step3) + addshift |= (add >> 0) | (linear << 2); + + int levelnext = level2 + addshift; + + int ksl; + ksl = eg_ksltable[chip->fnum[1] >> 6]; + int ksl_hi = (ksl & 64) != 0; + + ksl = (ksl & 63) + (chip->block[1] << 3); + if (ksl_hi || (ksl & 64) != 0) + ksl = ksl & 63; + else + ksl = 0; + + static int eg_kslshift[4] = { + 31, 1, 2, 0 + }; + + ksl = (ksl << 2) >> eg_kslshift[chip->ksl[1]]; + + int ksltl = ksl + (chip->tl[1] << 2); + + int tremolo; + + if (!chip->am[1]) + tremolo = 0; + else if (chip->reg_dv) + tremolo = chip->trem_out >> 2; + else + tremolo = chip->trem_out >> 4; + + int ksltltrem = ksltl + tremolo; + int levelof = 0; + + if (ksltltrem & 0x200) + levelof = 1; + + int totallevel = level + (ksltltrem & 0x1ff); + if (totallevel & 0x200) + levelof = 1; + + int totallevelclamp = (chip->reg_test0 & 1) != 0 ? 0 : (levelof ? 0x1ff : (totallevel & 0x1ff)); + + chip->eg_out[0] = totallevelclamp; + + chip->eg_dbg[0] = chip->eg_dbg[1] >> 1; + + if ((chip->reg_test0 & 32) != 0 && !chip->eg_dbg_load_l[1]) + { + chip->eg_dbg[0] |= chip->eg_out[1]; + } + chip->eg_dbg_load_l[0] = (chip->reg_test0 & 32) != 0; + + if (chip->fsm_out[4] || chip->fsm_out[6]) + chip->eg_index[0] = 0; + else + chip->eg_index[0] = chip->eg_index[1] + 1; + + if (chip->eg_index[1] < 18) + { + int index1 = chip->eg_index[1]; + int index2 = (index1 + 17) % 18; + chip->eg_cells[index2] = (nextstate & 3) | ((levelnext & 511) << 2) | (chip->eg_timer_i << 11); + chip->eg_cells[index2 + 18] = chip->eg_cells[index1]; + chip->eg_state_o[0] = chip->eg_cells[18 + index1] & 3; + chip->eg_level_o[0] = (chip->eg_cells[18 + index1] >> 2) & 511; + chip->eg_timer_o[0] = (chip->eg_cells[18 + index1] >> 11) & 1; + } + chip->eg_state_o[2] = chip->eg_state_o[1]; + chip->eg_level_o[2] = chip->eg_level_o[1]; + chip->eg_timer_o[2] = chip->eg_timer_o[1]; + } + if (chip->clk2) + { + chip->eg_out[1] = chip->eg_out[0]; + chip->eg_dbg[1] = chip->eg_dbg[0]; + chip->eg_dbg_load_l[1] = chip->eg_dbg_load_l[0]; + + chip->eg_index[1] = chip->eg_index[0]; + chip->eg_state_o[1] = chip->eg_state_o[0]; + chip->eg_state_o[3] = chip->eg_state_o[2]; + chip->eg_level_o[1] = chip->eg_level_o[0]; + chip->eg_level_o[3] = chip->eg_level_o[2]; + chip->eg_timer_o[1] = chip->eg_timer_o[0]; + chip->eg_timer_o[3] = chip->eg_timer_o[2]; + } + + if (chip->clk1) + { + static const int pg_multi[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 + }; + int fnum = chip->fnum[1]; + int freq; + int pg_add; + int vib_sel1 = (chip->vib_cnt[1] & 3) == 2; + int vib_sel2 = (chip->vib_cnt[1] & 1) == 1; + int vib_sh0 = chip->reg_dv && chip->vib[1] && vib_sel1; + int vib_sh1 = (chip->reg_dv && chip->vib[1] && vib_sel2) + || (!chip->reg_dv && chip->vib[1] && vib_sel1); + int vib_sh2 = !chip->reg_dv && chip->vib[1] && vib_sel2; + int vib_sign = (chip->vib_cnt[1] & 4) != 0 && chip->vib[1]; + int vib_add = 0; + int phase; + if (vib_sh0) + vib_add |= (chip->fnum[1] >> 7) & 7; + if (vib_sh1) + vib_add |= (chip->fnum[1] >> 8) & 3; + if (vib_sh2) + vib_add |= (chip->fnum[1] >> 9) & 1; + if (vib_sign) + { + vib_add ^= 1023; + } + fnum += vib_add; + fnum += vib_sign; + if (vib_sign) + fnum &= 1023; + + freq = (fnum << chip->block[1]) >> 1; + + pg_add = (freq * pg_multi[chip->multi[1]]) >> 1; + + int state = chip->eg_state_o[3]; + int dokon = state == eg_state_release && chip->keyon_comb; + + phase = ((dokon || (chip->reg_test0 & 4) != 0 || chip->reset1) ? 0 : chip->pg_phase_o[3]) + pg_add; + + if (chip->fsm_out[4] || chip->fsm_out[6]) + chip->pg_index[0] = 0; + else + chip->pg_index[0] = chip->pg_index[1] + 1; + + if (chip->pg_index[1] < 18) + { + int index1 = chip->pg_index[1]; + int index2 = (index1 + 17) % 18; + chip->pg_cells[index2] = phase; + chip->pg_cells[index2 + 18] = chip->pg_cells[index1]; + chip->pg_phase_o[0] = chip->pg_cells[18 + index1]; + } + chip->pg_phase_o[2] = chip->pg_phase_o[1]; + } + if (chip->clk2) + { + chip->pg_index[1] = chip->pg_index[0]; + chip->pg_phase_o[1] = chip->pg_phase_o[0]; + chip->pg_phase_o[3] = chip->pg_phase_o[2]; + } + + if (chip->rclk1) + { + int noise_bit; + + noise_bit = ((chip->noise_lfsr[1] >> 22) ^ (chip->noise_lfsr[1] >> 8)) & 1; + + if ((chip->noise_lfsr[1] & 0x7fffff) == 0) + noise_bit |= 1; + + noise_bit |= (chip->reg_test0 & 2) != 0; + + if (chip->reset1) + noise_bit = 0; + + chip->noise_lfsr[0] = (chip->noise_lfsr[1] << 1) | noise_bit; + } + if (chip->rclk2) + { + chip->noise_lfsr[1] = chip->noise_lfsr[0]; + } + + { + int pg_out = chip->pg_phase_o[3] >> 9; + int hh = chip->fsm_out[2] && chip->rhythm; + int sd = chip->fsm_out[3] && chip->rhythm; + int tc = chip->fsm_out[4] && chip->rhythm; + int rhy = (chip->fsm_out[2] || chip->fsm_out[3] || chip->fsm_out[4]) && chip->rhythm; + if (chip->clk1) + chip->hh_load = chip->fsm_out[2]; + if (!chip->hh_load && chip->fsm_out[2]) + { + chip->hh_bit2 = (pg_out >> 2) & 1; + chip->hh_bit3 = (pg_out >> 3) & 1; + chip->hh_bit7 = (pg_out >> 7) & 1; + chip->hh_bit8 = (pg_out >> 8) & 1; + } + if (chip->clk1) + chip->tc_load = tc; + if (!chip->tc_load && tc) + { + chip->tc_bit3 = (pg_out >> 3) & 1; + chip->tc_bit5 = (pg_out >> 5) & 1; + } + + if (chip->clk1) // opt + { + int rm_bit; + int noise = (chip->noise_lfsr[1] >> 22) & 1; + + rm_bit = (chip->hh_bit2 ^ chip->hh_bit7) + | (chip->tc_bit5 ^ chip->hh_bit3) + | (chip->tc_bit5 ^ chip->tc_bit3); + + chip->pg_out_rhy = 0; + if (!rhy) + chip->pg_out_rhy |= pg_out; + if (hh) + { + chip->pg_out_rhy |= rm_bit << 9; + if (noise ^ rm_bit) + chip->pg_out_rhy |= 0xd0; + else + chip->pg_out_rhy |= 0x34; + } + if (sd) + chip->pg_out_rhy |= (chip->hh_bit8 << 9) | ((noise ^ chip->hh_bit8) << 8); + if (tc) + chip->pg_out_rhy |= (rm_bit << 9) | 0x80; + } + + if (chip->clk1) + { + chip->pg_dbg[0] = chip->pg_dbg[1] >> 1; + + chip->pg_dbg_load_l[0] = (chip->reg_test0 & 8) != 0; + + if ((chip->reg_test0 & 8) != 0 && !chip->pg_dbg_load_l[1]) + { + chip->pg_dbg[0] |= chip->pg_phase_o[3] & 0x1ff; + chip->pg_dbg[0] |= (chip->pg_out_rhy & 0x3ff) << 9; + } + } + if (chip->clk2) + { + chip->pg_dbg_load_l[1] = chip->pg_dbg_load_l[0]; + + chip->pg_dbg[1] = chip->pg_dbg[0]; + } + } + + { + + if (chip->clk1) + { + static const int logsin[128] = { + 0x6c3, 0x58b, 0x4e4, 0x471, 0x41a, 0x3d3, 0x398, 0x365, 0x339, 0x311, 0x2ed, 0x2cd, 0x2af, 0x293, 0x279, 0x261, + 0x24b, 0x236, 0x222, 0x20f, 0x1fd, 0x1ec, 0x1dc, 0x1cd, 0x1be, 0x1b0, 0x1a2, 0x195, 0x188, 0x17c, 0x171, 0x166, + 0x15b, 0x150, 0x146, 0x13c, 0x133, 0x129, 0x121, 0x118, 0x10f, 0x107, 0x0ff, 0x0f8, 0x0f0, 0x0e9, 0x0e2, 0x0db, + 0x0d4, 0x0cd, 0x0c7, 0x0c1, 0x0bb, 0x0b5, 0x0af, 0x0a9, 0x0a4, 0x09f, 0x099, 0x094, 0x08f, 0x08a, 0x086, 0x081, + 0x07d, 0x078, 0x074, 0x070, 0x06c, 0x068, 0x064, 0x060, 0x05c, 0x059, 0x055, 0x052, 0x04e, 0x04b, 0x048, 0x045, + 0x042, 0x03f, 0x03c, 0x039, 0x037, 0x034, 0x031, 0x02f, 0x02d, 0x02a, 0x028, 0x026, 0x024, 0x022, 0x020, 0x01e, + 0x01c, 0x01a, 0x018, 0x017, 0x015, 0x014, 0x012, 0x011, 0x00f, 0x00e, 0x00d, 0x00c, 0x00a, 0x009, 0x008, 0x007, + 0x007, 0x006, 0x005, 0x004, 0x004, 0x003, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000 + }; + static const int logsin_d[128] = { + 0x196, 0x07c, 0x04a, 0x035, 0x029, 0x022, 0x01d, 0x019, 0x015, 0x013, 0x012, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x009, 0x008, 0x007, 0x007, 0x007, 0x007, 0x006, 0x007, 0x006, 0x006, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x003, 0x004, 0x003, 0x003, 0x003, + 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x002, 0x002, 0x002, 0x001, + 0x001, 0x001, 0x002, 0x002, 0x001, 0x001, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x000, 0x001, 0x000, 0x001, 0x000, 0x001, 0x001, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x001, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 + }; + static const int pow[128] = { + 0x3f5, 0x3ea, 0x3df, 0x3d4, 0x3c9, 0x3bf, 0x3b4, 0x3a9, 0x39f, 0x394, 0x38a, 0x37f, 0x375, 0x36a, 0x360, 0x356, + 0x34c, 0x342, 0x338, 0x32e, 0x324, 0x31a, 0x310, 0x306, 0x2fd, 0x2f3, 0x2e9, 0x2e0, 0x2d6, 0x2cd, 0x2c4, 0x2ba, + 0x2b1, 0x2a8, 0x29e, 0x295, 0x28c, 0x283, 0x27a, 0x271, 0x268, 0x25f, 0x257, 0x24e, 0x245, 0x23c, 0x234, 0x22b, + 0x223, 0x21a, 0x212, 0x209, 0x201, 0x1f9, 0x1f0, 0x1e8, 0x1e0, 0x1d8, 0x1d0, 0x1c8, 0x1c0, 0x1b8, 0x1b0, 0x1a8, + 0x1a0, 0x199, 0x191, 0x189, 0x181, 0x17a, 0x172, 0x16b, 0x163, 0x15c, 0x154, 0x14d, 0x146, 0x13e, 0x137, 0x130, + 0x129, 0x122, 0x11b, 0x114, 0x10c, 0x106, 0x0ff, 0x0f8, 0x0f1, 0x0ea, 0x0e3, 0x0dc, 0x0d6, 0x0cf, 0x0c8, 0x0c2, + 0x0bb, 0x0b5, 0x0ae, 0x0a8, 0x0a1, 0x09b, 0x094, 0x08e, 0x088, 0x082, 0x07b, 0x075, 0x06f, 0x069, 0x063, 0x05d, + 0x057, 0x051, 0x04b, 0x045, 0x03f, 0x039, 0x033, 0x02d, 0x028, 0x022, 0x01c, 0x016, 0x011, 0x00b, 0x006, 0x000, + }; + static const int pow_d[128] = { + 0x005, 0x005, 0x005, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x006, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, 0x004, 0x004, 0x004, + 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x003, 0x003, 0x004, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, + }; + int wf = chip->wf[1]; + int phase = chip->pg_out_rhy + chip->op_mod[1]; + int square = wf == 6; + int sawtooth = wf == 7; + + int phase2; + if (wf == 4 || wf == 5) + phase2 = phase << 1; + else + phase2 = phase; + phase2 &= 1023; + + if (wf == 7 ? (phase2 & 512) != 0 : (phase2 & 256) != 0) + phase2 ^= 511; + + int mute = ((phase & 512) != 0 && (wf == 1 || wf == 4 || wf == 5)) || ((phase & 256) != 0 && wf == 3); + int sign = (wf == 2 || wf == 3 || wf == 5) ? 0 : (phase2 & 512) != 0; + + int index = square ? 255 : (phase2 & 255); + + int ls = logsin[index >> 1]; + if ((index & 1) == 0) + ls += logsin_d[index >> 1]; + + chip->op_logsin[0] = ls; + chip->op_saw[0] = sawtooth; + chip->op_saw_phase[0] = phase2 & 511; + + int att = (chip->op_saw[1] ? (chip->op_saw_phase[1] << 3) : chip->op_logsin[1]) + (chip->eg_out[1] << 3); + if (att & 4096) + att = 4095; + + int pw = pow[(att >> 1) & 127]; + if ((att & 1) == 0) + pw += pow_d[(att >> 1) & 127]; + + chip->op_shift[0] = (att >> 8) & 15; + chip->op_pow[0] = pw; + + chip->op_mute[0] = (chip->op_mute[1] << 1) | mute; + chip->op_sign[0] = (chip->op_sign[1] << 1) | sign; + + int value = 0; + + if ((chip->op_mute[1] & 2) == 0) + { + value = ((chip->op_pow[1] | 0x400) << 1) >> chip->op_shift[1]; + } + + if ((chip->op_mute[1] & 2) == 0 && (chip->op_sign[1] & 2) != 0) + value ^= 8191; + + for (i = 0; i < 13; i++) + { + int bit; + chip->op_fb[0][i][0] = chip->op_fb[0][i][1] << 1; + if (chip->fsm_out[15]) + bit = (value >> i) & 1; + else + bit = (chip->op_fb[0][i][1] >> 8) & 1; + chip->op_fb[0][i][0] |= bit; + chip->op_fb[1][i][0] = chip->op_fb[1][i][1] << 1; + if (chip->fsm_out[15]) + bit = (chip->op_fb[0][i][1] >> 8) & 1; + else + bit = (chip->op_fb[1][i][1] >> 8) & 1; + chip->op_fb[1][i][0] |= bit; + chip->op_fb[2][i][0] = chip->op_fb[2][i][1] << 1; + if (chip->fsm_out[15]) + bit = (chip->op_fb[1][i][1] >> 8) & 1; + else + bit = (chip->op_fb[2][i][1] >> 8) & 1; + chip->op_fb[2][i][0] |= bit; + chip->op_fb[3][i][0] = chip->op_fb[3][i][1] << 1; + if (chip->fsm_out[15]) + bit = (chip->op_fb[2][i][1] >> 8) & 1; + else + bit = (chip->op_fb[3][i][1] >> 8) & 1; + chip->op_fb[3][i][0] |= bit; + } + + int fb1 = 0; + int fb2 = 0; + for (i = 0; i < 14; i++) + { + int j = i; + if (i == 13) + j = 12; + fb1 |= ((chip->op_fb[1][j][1] >> 5) & 1) << i; + fb2 |= ((chip->op_fb[3][j][1] >> 5) & 1) << i; + } + int fb_sum = fb1 + fb2; + fb_sum &= 16383; + if (fb_sum & 8192) + fb_sum |= ~8191; + + int mod = 0; + + if (chip->fsm_out[16] && !chip->fsm_out[14]) + mod |= value & 1023; + if (chip->fsm_out[12]) + { + if (chip->fb_l[1][1]) + { + mod |= (fb_sum >> (9 - chip->fb_l[1][1])) & 1023; + } + } + + chip->op_mod[0] = mod & 1023; + + chip->op_value = value; + } + if (chip->clk2) + { + chip->op_logsin[1] = chip->op_logsin[0]; + chip->op_saw[1] = chip->op_saw[0]; + chip->op_saw_phase[1] = chip->op_saw_phase[0]; + chip->op_shift[1] = chip->op_shift[0]; + chip->op_pow[1] = chip->op_pow[0]; + chip->op_mute[1] = chip->op_mute[0]; + chip->op_sign[1] = chip->op_sign[0]; + + for (i = 0; i < 13; i++) + { + chip->op_fb[0][i][1] = chip->op_fb[0][i][0]; + chip->op_fb[1][i][1] = chip->op_fb[1][i][0]; + chip->op_fb[2][i][1] = chip->op_fb[2][i][0]; + chip->op_fb[3][i][1] = chip->op_fb[3][i][0]; + } + chip->op_mod[1] = chip->op_mod[0]; + } + } + + { + if (chip->clk1) + { + chip->accm_load_ac_l = chip->fsm_out[6]; + chip->accm_load_bd_l = chip->fsm_out[4]; + } + chip->accm_load_ac = !chip->accm_load_ac_l && chip->fsm_out[6]; + chip->accm_load_bd = !chip->accm_load_bd_l && chip->fsm_out[4]; + + if (chip->accm_load_ac) + { + chip->accm_a_sign = (chip->accm_a[1] & 0x40000) == 0; + chip->accm_a_of = !((chip->accm_a[1] & 0x78000) == 0 || (chip->accm_a[1] & 0x78000) == 0x78000); + chip->accm_c_sign = (chip->accm_c[1] & 0x40000) == 0; + chip->accm_c_of = !((chip->accm_c[1] & 0x78000) == 0 || (chip->accm_c[1] & 0x78000) == 0x78000); + } + + if (chip->accm_load_bd) + { + chip->accm_b_sign = (chip->accm_b[1] & 0x40000) == 0; + chip->accm_b_of = !((chip->accm_b[1] & 0x78000) == 0 || (chip->accm_b[1] & 0x78000) == 0x78000); + chip->accm_d_sign = (chip->accm_d[1] & 0x40000) == 0; + chip->accm_d_of = !((chip->accm_d[1] & 0x78000) == 0 || (chip->accm_d[1] & 0x78000) == 0x78000); + } + + if (chip->clk1) + { + int value = 0; + + if (chip->fsm_out[13]) + { + if (chip->fsm_out[11]) + value = chip->op_value << 1; + else + { + value = chip->op_value; + if (chip->op_value & 0x1000) + value |= 0x2000; + } + } + if (value & 0x2000) + { + value |= 0x7c000; + } + + int sign; + + int accm_a = chip->fsm_out[6] ? 0 : chip->accm_a[1]; + accm_a += (chip->pan_l[1][1] & 1) != 0 ? value : 0; + chip->accm_a[0] = accm_a; + sign = (chip->accm_a[1] & 0x40000) == 0; + chip->accm_shift_a[0] = (chip->accm_shift_a[1] >> 1); + if (chip->fsm_out[6]) + { + chip->accm_shift_a[0] |= chip->accm_a[1] & 0x7fff; + if (sign) + chip->accm_shift_a[0] |= 0x8000; + } + + int accm_b = chip->fsm_out[4] ? 0 : chip->accm_b[1]; + accm_b += (chip->pan_l[1][1] & 2) != 0 ? value : 0; + chip->accm_b[0] = accm_b; + sign = (chip->accm_b[1] & 0x40000) == 0; + chip->accm_shift_b[0] = (chip->accm_shift_b[1] >> 1); + if (chip->fsm_out[4]) + { + chip->accm_shift_b[0] |= chip->accm_b[1] & 0x7fff; + if (sign) + chip->accm_shift_b[0] |= 0x8000; + } + + int accm_c = chip->fsm_out[6] ? 0 : chip->accm_c[1]; + accm_c += (chip->pan_l[1][1] & 4) != 0 ? value : 0; + chip->accm_c[0] = accm_c; + sign = (chip->accm_c[1] & 0x40000) == 0; + chip->accm_shift_c[0] = (chip->accm_shift_c[1] >> 1); + if (chip->fsm_out[6]) + { + chip->accm_shift_c[0] |= chip->accm_c[1] & 0x7fff; + if (sign) + chip->accm_shift_c[0] |= 0x8000; + } + + int accm_d = chip->fsm_out[4] ? 0 : chip->accm_d[1]; + accm_d += (chip->pan_l[1][1] & 8) != 0 ? value : 0; + chip->accm_d[0] = accm_d; + sign = (chip->accm_d[1] & 0x40000) == 0; + chip->accm_shift_d[0] = (chip->accm_shift_d[1] >> 1); + if (chip->fsm_out[4]) + { + chip->accm_shift_d[0] |= chip->accm_d[1] & 0x7fff; + if (sign) + chip->accm_shift_d[0] |= 0x8000; + } + } + if (chip->clk2) + { + chip->accm_a[1] = chip->accm_a[0]; + chip->accm_b[1] = chip->accm_b[0]; + chip->accm_c[1] = chip->accm_c[0]; + chip->accm_d[1] = chip->accm_d[0]; + + chip->accm_shift_a[1] = chip->accm_shift_a[0]; + chip->accm_shift_b[1] = chip->accm_shift_b[0]; + chip->accm_shift_c[1] = chip->accm_shift_c[0]; + chip->accm_shift_d[1] = chip->accm_shift_d[0]; + } + + if (chip->fsm_out[8]) + { + chip->o_doab = chip->accm_a_of ? chip->accm_a_sign : chip->accm_shift_a[1] & 1; + chip->o_docd = chip->accm_c_of ? chip->accm_c_sign : chip->accm_shift_c[1] & 1; + } + else + { + chip->o_doab = chip->accm_b_of ? chip->accm_b_sign : chip->accm_shift_b[1] & 1; + chip->o_docd = chip->accm_d_of ? chip->accm_d_sign : chip->accm_shift_d[1] & 1; + } + } + + chip->o_sy = chip->clk2; + chip->o_smpac = chip->fsm_out[10]; + chip->o_smpbd = chip->fsm_out[9]; + chip->o_irq_pull = chip->t1_status || chip->t2_status; + + if (chip->io_read) + { + chip->data_o = 0; + if (chip->t1_status || chip->t2_status) + chip->data_o |= 128; + if (chip->t1_status) + chip->data_o |= 64; + if (chip->t2_status) + chip->data_o |= 32; + chip->data_z = 0; + } + else + chip->data_z = 1; + + { + if (chip->clk1) + { + chip->ra_dbg1[0] = chip->ra_dbg1[1] >> 1; + chip->ra_dbg2[0] = chip->ra_dbg2[1] >> 1; + if ((chip->reg_test0 & 128) != 0 && !chip->ra_dbg_load[1]) + { + chip->ra_dbg1[0] |= (int64_t)chip->multi[1] << 0; + chip->ra_dbg1[0] |= (int64_t)chip->ksr[1] << 4; + chip->ra_dbg1[0] |= (int64_t)chip->egt[1] << 5; + chip->ra_dbg1[0] |= (int64_t)chip->vib[1] << 6; + chip->ra_dbg1[0] |= (int64_t)chip->am[1] << 7; + chip->ra_dbg1[0] |= (int64_t)chip->tl[1] << 8; + chip->ra_dbg1[0] |= (int64_t)chip->ksl[1] << 14; + chip->ra_dbg1[0] |= (int64_t)chip->dr[1] << 16; + chip->ra_dbg1[0] |= (int64_t)chip->ar[1] << 20; + chip->ra_dbg1[0] |= (int64_t)chip->rr[1] << 24; + chip->ra_dbg1[0] |= (int64_t)chip->sl[1] << 28; + chip->ra_dbg1[0] |= (int64_t)chip->wf[1] << 32; + chip->ra_dbg2[0] |= (int64_t)chip->fnum[1] << 0; + chip->ra_dbg2[0] |= (int64_t)chip->block[1] << 10; + chip->ra_dbg2[0] |= (int64_t)chip->keyon[1] << 13; + chip->ra_dbg2[0] |= (int64_t)chip->connect[1] << 14; + chip->ra_dbg2[0] |= (int64_t)chip->pan[1] << 15; + chip->ra_dbg2[0] |= (int64_t)chip->connect_pair[1] << 19; + chip->ra_dbg2[0] |= (int64_t)chip->fb[1] << 20; + } + chip->ra_dbg_load[0] = (chip->reg_test0 & 128) != 0; + } + if (chip->clk2) + { + chip->ra_dbg1[1] = chip->ra_dbg1[0]; + chip->ra_dbg2[1] = chip->ra_dbg2[0]; + chip->ra_dbg_load[1] = chip->ra_dbg_load[0]; + } + } + + switch (chip->reg_test1 & 7) + { + case 0: + chip->o_test = 0; + break; + case 1: + chip->o_test = (chip->ra_dbg1[1] & 1) != 0; + break; + case 2: + chip->o_test = (chip->ra_dbg2[1] & 1) != 0; + break; + case 3: + chip->o_test = (chip->pg_dbg[1] & 1) != 0; + break; + case 4: + chip->o_test = (chip->eg_dbg[1] & 1) != 0; + break; + } + +end: + + if (chip->io_write0) + chip->write0_sr = 1; + else if (chip->reset0 || chip->write0_l[1]) + chip->write0_sr = 0; + + if (chip->io_write1) + chip->write1_sr = 1; + else if (chip->reset0 || chip->write1_l[1]) + chip->write1_sr = 0; + + if (chip->clk1) + { + chip->write0_l[0] = chip->write0_sr; + chip->write0_l[2] = chip->write0_l[1]; + + chip->write1_l[0] = chip->write1_sr; + chip->write1_l[2] = chip->write1_l[1]; + } + + if (chip->mclk1) + { + chip->prescaler1_reset[0] = (chip->prescaler1_reset[1] << 1) | chip->reset1; + + if (!(chip->prescaler1_reset[1] & 2) && chip->reset1) + chip->prescaler1_cnt[0] = 0; + else + chip->prescaler1_cnt[0] = (chip->prescaler1_cnt[1] + 1) & 3; + } + + if (chip->aclk1) + { + int prescaler2_reset = !(chip->prescaler2_reset_l[1] & 2) && chip->reset1; + chip->prescaler2_reset_l[0] = (chip->prescaler2_reset_l[1] << 1) | chip->reset1; + + if (prescaler2_reset) + chip->prescaler2_cnt[0] = 0; + else + chip->prescaler2_cnt[0] = (chip->prescaler2_cnt[1] + 1) & 3; + + chip->prescaler2_l1[0] = !prescaler2_reset && (chip->prescaler2_cnt[1] & 1) == 0; + chip->prescaler2_l2 = chip->prescaler2_l1[1]; + + chip->prescaler2_l3[0] = !prescaler2_reset && (chip->prescaler2_cnt[1] & 1) != 0; + chip->prescaler2_l4 = chip->prescaler2_l3[1]; + + chip->prescaler2_l5[0] = !prescaler2_reset && chip->prescaler2_cnt[1] == 3; + + chip->prescaler2_l6[0] = !prescaler2_reset && chip->prescaler2_cnt[1] == 1; + + chip->prescaler2_l7 = (chip->prescaler2_cnt[1] & 1) == 0; + } +} diff --git a/extern/YMF262-LLE/fmopl3.h b/extern/YMF262-LLE/fmopl3.h new file mode 100644 index 000000000..8b465ee7e --- /dev/null +++ b/extern/YMF262-LLE/fmopl3.h @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YMF262-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * YMF262 emulator + * Thanks: + * John McMaster (siliconpr0n.org): + * YMF262 decap and die shot + * + */ + +#pragma once + +#include + +typedef struct +{ + int mclk; + int address; + int data_i; + int ic; + int cs; + int rd; + int wr; +} fmopl3_input_t; + +typedef struct +{ + fmopl3_input_t input; + + int mclk1; + int mclk2; + int aclk1; + int aclk2; + int clk1; + int clk2; + int rclk1; + int rclk2; + + int o_clk1; + int o_clk2; + int o_rclk1; + int o_rclk2; + int o_wrcheck; + int o_data_latch; + int o_bank_latch; + int o_reset0; + int o_ra_w1_l1; + + int prescaler1_reset[2]; + int prescaler1_cnt[2]; + + int prescaler2_reset_l[2]; + int prescaler2_cnt[2]; + int prescaler2_reset; + int prescaler2_l1[2]; + int prescaler2_l2; + int prescaler2_l3[2]; + int prescaler2_l4; + int prescaler2_l5[2]; + int prescaler2_l6[2]; + int prescaler2_l7; + + int fsm_cnt1[2]; + int fsm_cnt2[2]; + int fsm_cnt3[2]; + int fsm_cnt; + + int fsm_reset_l[2]; + int fsm_out[17]; + int fsm_l1[2]; + int fsm_l2[2]; + int fsm_l3[2]; + int fsm_l4[2]; + int fsm_l5[2]; + int fsm_l6[2]; + int fsm_l7[2]; + int fsm_l8[2]; + int fsm_l9[2]; + int fsm_l10[2]; + + int ic_latch[2]; + + int io_rd; + int io_wr; + int io_cs; + int io_a0; + int io_a1; + + int io_read; + int io_write; + int io_write0; + int io_write1; + int io_bank; + + int data_latch; + int bank_latch; + int bank_masked; + + int reg_sel1; + int reg_sel2; + int reg_sel3; + int reg_sel4; + int reg_sel5; + int reg_sel8; + int reg_selbd; + + int reg_test0; + int reg_timer1; + int reg_timer2; + int reg_notesel; + int rhythm; + int reg_rh_kon; + int reg_da; + int reg_dv; + + int reg_test1; + int reg_new; + int reg_4op; + + int reg_t1_mask; + int reg_t2_mask; + int reg_t1_start; + int reg_t2_start; + + int lfo_cnt[2]; + int vib_cnt[2]; + int t1_step; + int t2_step; + int am_step; + int vib_step; + + int rh_sel0; + int rh_sel[2]; + + int keyon_comb; + + int ra_address_latch; + int ra_address_good; + int ra_data_latch; + int ra_cnt1[2]; + int ra_cnt2[2]; + int ra_cnt3[2]; + int ra_cnt4[2]; + int ra_cnt; + int ra_rst_l[2]; + int ra_w1_l1; + int ra_w1_l2; + int ra_write; + int ra_write_a; + + int ra_multi[36]; + int ra_ksr[36]; + int ra_egt[36]; + int ra_am[36]; + int ra_vib[36]; + int ra_tl[36]; + int ra_ksl[36]; + int ra_ar[36]; + int ra_dr[36]; + int ra_sl[36]; + int ra_rr[36]; + int ra_wf[36]; + int ra_fnum[18]; + int ra_block[18]; + int ra_keyon[18]; + int ra_connect[18]; + int ra_fb[18]; + int ra_pan[18]; + int ra_connect_pair[18]; + int multi[2]; + int ksr[2]; + int egt[2]; + int am[2]; + int vib[2]; + int tl[2]; + int ksl[2]; + int ar[2]; + int dr[2]; + int sl[2]; + int rr[2]; + int wf[2]; + int fnum[2]; + int block[2]; + int keyon[2]; + int connect[2]; + int fb[2]; + int pan[2]; + int connect_pair[2]; + + int64_t ra_dbg1[2]; + int ra_dbg2[2]; + int ra_dbg_load[2]; + + int fb_l[2][2]; + int pan_l[2][2]; + + int write0_sr; + int write0_l[4]; + int write0; + + int write1_sr; + int write1_l[4]; + int write1; + + int connect_l[2]; + int connect_pair_l[2]; + + int t1_cnt[2]; + int t2_cnt[2]; + int t1_of[2]; + int t2_of[2]; + int t1_status; + int t2_status; + int timer_st_load_l; + int timer_st_load; + int t1_start; + int t2_start; + int t1_start_l[2]; + int t2_start_l[2]; + + int reset0; + int reset1; + + int pg_phase_o[4]; + int pg_dbg[2]; + int pg_dbg_load_l[2]; + int noise_lfsr[2]; + int pg_index[2]; + int pg_cells[36]; + int pg_out_rhy; + + int trem_load_l; + int trem_load; + int trem_st_load_l; + int trem_st_load; + int trem_carry[2]; + int trem_value[2]; + int trem_dir[2]; + int trem_step; + int trem_out; + int trem_of[2]; + + int eg_load_l1[2]; + int eg_load_l; + int eg_load; + + int64_t eg_timer_masked[2]; + int eg_carry[2]; + int eg_mask[2]; + int eg_subcnt[2]; + int eg_subcnt_l[2]; + int eg_sync_l[2]; + int eg_timer_low; + int eg_shift; + int eg_timer_dbg[2]; + + int eg_timer_i; + int eg_timer_o[4]; + int eg_state_o[4]; + int eg_level_o[4]; + int eg_index[2]; + int eg_cells[36]; + + int eg_out[2]; + int eg_dbg[2]; + int eg_dbg_load_l[2]; + + int hh_load; + int tc_load; + int hh_bit2; + int hh_bit3; + int hh_bit7; + int hh_bit8; + int tc_bit3; + int tc_bit5; + + int op_logsin[2]; + int op_saw[2]; + int op_saw_phase[2]; + int op_shift[2]; + int op_pow[2]; + int op_mute[2]; + int op_sign[2]; + int op_fb[4][13][2]; + int op_mod[2]; + + int op_value; + + int accm_a[2]; + int accm_b[2]; + int accm_c[2]; + int accm_d[2]; + int accm_shift_a[2]; + int accm_shift_b[2]; + int accm_shift_c[2]; + int accm_shift_d[2]; + int accm_load_ac_l; + int accm_load_ac; + int accm_load_bd_l; + int accm_load_bd; + int accm_a_of; + int accm_a_sign; + int accm_b_of; + int accm_b_sign; + int accm_c_of; + int accm_c_sign; + int accm_d_of; + int accm_d_sign; + + int o_doab; + int o_docd; + int o_sy; + int o_smpac; + int o_smpbd; + int o_irq_pull; + int o_test; + + int data_o; + int data_z; +} fmopl3_t; + diff --git a/src/engine/platform/sound/ymfm/ymfm_opl.cpp b/src/engine/platform/sound/ymfm/ymfm_opl.cpp new file mode 100644 index 000000000..bcc1144ce --- /dev/null +++ b/src/engine/platform/sound/ymfm/ymfm_opl.cpp @@ -0,0 +1,2139 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "ymfm_opl.h" +#include "ymfm_fm.ipp" + +namespace ymfm +{ + +//------------------------------------------------- +// opl_key_scale_atten - converts an +// OPL concatenated block (3 bits) and fnum +// (10 bits) into an attenuation offset; values +// here are for 6dB/octave, in 0.75dB units +// (matching total level LSB) +//------------------------------------------------- + +inline uint32_t opl_key_scale_atten(uint32_t block, uint32_t fnum_4msb) +{ + // this table uses the top 4 bits of FNUM and are the maximal values + // (for when block == 7). Values for other blocks can be computed by + // subtracting 8 for each block below 7. + static uint8_t const fnum_to_atten[16] = { 0,24,32,37,40,43,45,47,48,50,51,52,53,54,55,56 }; + int32_t result = fnum_to_atten[fnum_4msb] - 8 * (block ^ 7); + return std::max(0, result); +} + + +//********************************************************* +// OPL REGISTERS +//********************************************************* + +//------------------------------------------------- +// opl_registers_base - constructor +//------------------------------------------------- + +template +opl_registers_base::opl_registers_base() : + m_lfo_am_counter(0), + m_lfo_pm_counter(0), + m_noise_lfsr(1), + m_lfo_am(0) +{ + // create these pointers to appease overzealous compilers checking array + // bounds in unreachable code (looking at you, clang) + uint16_t *wf0 = &m_waveform[0][0]; + uint16_t *wf1 = &m_waveform[1 % WAVEFORMS][0]; + uint16_t *wf2 = &m_waveform[2 % WAVEFORMS][0]; + uint16_t *wf3 = &m_waveform[3 % WAVEFORMS][0]; + uint16_t *wf4 = &m_waveform[4 % WAVEFORMS][0]; + uint16_t *wf5 = &m_waveform[5 % WAVEFORMS][0]; + uint16_t *wf6 = &m_waveform[6 % WAVEFORMS][0]; + uint16_t *wf7 = &m_waveform[7 % WAVEFORMS][0]; + + // create the waveforms + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + wf0[index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); + + if (WAVEFORMS >= 4) + { + uint16_t zeroval = wf0[0]; + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + { + wf1[index] = bitfield(index, 9) ? zeroval : wf0[index]; + wf2[index] = wf0[index] & 0x7fff; + wf3[index] = bitfield(index, 8) ? zeroval : (wf0[index] & 0x7fff); + if (WAVEFORMS >= 8) + { + wf4[index] = bitfield(index, 9) ? zeroval : wf0[index * 2]; + wf5[index] = bitfield(index, 9) ? zeroval : wf0[(index * 2) & 0x1ff]; + wf6[index] = bitfield(index, 9) << 15; + wf7[index] = (bitfield(index, 9) ? (index ^ 0x13ff) : index) << 3; + } + } + } + + // OPL3/OPL4 have dynamic operators, so initialize the fourop_enable value here + // since operator_map() is called right away, prior to reset() + if (Revision > 2) + m_regdata[0x104 % REGISTERS] = 0; +} + + +//------------------------------------------------- +// reset - reset to initial state +//------------------------------------------------- + +template +void opl_registers_base::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void opl_registers_base::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_lfo_am_counter); + state.save_restore(m_lfo_pm_counter); + state.save_restore(m_lfo_am); + state.save_restore(m_noise_lfsr); + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// operator_map - return an array of operator +// indices for each channel; for OPL this is fixed +//------------------------------------------------- + +template +void opl_registers_base::operator_map(operator_mapping &dest) const +{ + if (Revision <= 2) + { + // OPL/OPL2 has a fixed map, all 2 operators + static const operator_mapping s_fixed_map = + { { + operator_list( 0, 3 ), // Channel 0 operators + operator_list( 1, 4 ), // Channel 1 operators + operator_list( 2, 5 ), // Channel 2 operators + operator_list( 6, 9 ), // Channel 3 operators + operator_list( 7, 10 ), // Channel 4 operators + operator_list( 8, 11 ), // Channel 5 operators + operator_list( 12, 15 ), // Channel 6 operators + operator_list( 13, 16 ), // Channel 7 operators + operator_list( 14, 17 ), // Channel 8 operators + } }; + dest = s_fixed_map; + } + else + { + // OPL3/OPL4 can be configured for 2 or 4 operators + uint32_t fourop = fourop_enable(); + + dest.chan[ 0] = bitfield(fourop, 0) ? operator_list( 0, 3, 6, 9 ) : operator_list( 0, 3 ); + dest.chan[ 1] = bitfield(fourop, 1) ? operator_list( 1, 4, 7, 10 ) : operator_list( 1, 4 ); + dest.chan[ 2] = bitfield(fourop, 2) ? operator_list( 2, 5, 8, 11 ) : operator_list( 2, 5 ); + dest.chan[ 3] = bitfield(fourop, 0) ? operator_list() : operator_list( 6, 9 ); + dest.chan[ 4] = bitfield(fourop, 1) ? operator_list() : operator_list( 7, 10 ); + dest.chan[ 5] = bitfield(fourop, 2) ? operator_list() : operator_list( 8, 11 ); + dest.chan[ 6] = operator_list( 12, 15 ); + dest.chan[ 7] = operator_list( 13, 16 ); + dest.chan[ 8] = operator_list( 14, 17 ); + + dest.chan[ 9] = bitfield(fourop, 3) ? operator_list( 18, 21, 24, 27 ) : operator_list( 18, 21 ); + dest.chan[10] = bitfield(fourop, 4) ? operator_list( 19, 22, 25, 28 ) : operator_list( 19, 22 ); + dest.chan[11] = bitfield(fourop, 5) ? operator_list( 20, 23, 26, 29 ) : operator_list( 20, 23 ); + dest.chan[12] = bitfield(fourop, 3) ? operator_list() : operator_list( 24, 27 ); + dest.chan[13] = bitfield(fourop, 4) ? operator_list() : operator_list( 25, 28 ); + dest.chan[14] = bitfield(fourop, 5) ? operator_list() : operator_list( 26, 29 ); + dest.chan[15] = operator_list( 30, 33 ); + dest.chan[16] = operator_list( 31, 34 ); + dest.chan[17] = operator_list( 32, 35 ); + } +} + + +//------------------------------------------------- +// write - handle writes to the register array +//------------------------------------------------- + +template +bool opl_registers_base::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask) +{ + assert(index < REGISTERS); + + // writes to the mode register with high bit set ignore the low bits + if (index == REG_MODE && bitfield(data, 7) != 0) + m_regdata[index] |= 0x80; + else + m_regdata[index] = data; + + // handle writes to the rhythm keyons + if (index == 0xbd) + { + channel = RHYTHM_CHANNEL; + opmask = bitfield(data, 5) ? bitfield(data, 0, 5) : 0; + return true; + } + + // handle writes to the channel keyons + if ((index & 0xf0) == 0xb0) + { + channel = index & 0x0f; + if (channel < 9) + { + if (IsOpl3Plus) + channel += 9 * bitfield(index, 8); + opmask = bitfield(data, 5) ? 15 : 0; + return true; + } + } + return false; +} + + +//------------------------------------------------- +// clock_noise_and_lfo - clock the noise and LFO, +// handling clock division, depth, and waveform +// computations +//------------------------------------------------- + +static int32_t opl_clock_noise_and_lfo(uint32_t &noise_lfsr, uint16_t &lfo_am_counter, uint16_t &lfo_pm_counter, uint8_t &lfo_am, uint32_t am_depth, uint32_t pm_depth) +{ + // OPL has a 23-bit noise generator for the rhythm section, running at + // a constant rate, used only for percussion input + noise_lfsr <<= 1; + noise_lfsr |= bitfield(noise_lfsr, 23) ^ bitfield(noise_lfsr, 9) ^ bitfield(noise_lfsr, 8) ^ bitfield(noise_lfsr, 1); + + // OPL has two fixed-frequency LFOs, one for AM, one for PM + + // the AM LFO has 210*64 steps; at a nominal 50kHz output, + // this equates to a period of 50000/(210*64) = 3.72Hz + uint32_t am_counter = lfo_am_counter++; + if (am_counter >= 210*64 - 1) + lfo_am_counter = 0; + + // low 8 bits are fractional; depth 0 is divided by 2, while depth 1 is times 2 + int shift = 9 - 2 * am_depth; + + // AM value is the upper bits of the value, inverted across the midpoint + // to produce a triangle + lfo_am = ((am_counter < 105*64) ? am_counter : (210*64+63 - am_counter)) >> shift; + + // the PM LFO has 8192 steps, or a nominal period of 6.1Hz + uint32_t pm_counter = lfo_pm_counter++; + + // PM LFO is broken into 8 chunks, each lasting 1024 steps; the PM value + // depends on the upper bits of FNUM, so this value is a fraction and + // sign to apply to that value, as a 1.3 value + static int8_t const pm_scale[8] = { 8, 4, 0, -4, -8, -4, 0, 4 }; + return pm_scale[bitfield(pm_counter, 10, 3)] >> (pm_depth ^ 1); +} + +template +int32_t opl_registers_base::clock_noise_and_lfo() +{ + return opl_clock_noise_and_lfo(m_noise_lfsr, m_lfo_am_counter, m_lfo_pm_counter, m_lfo_am, lfo_am_depth(), lfo_pm_depth()); +} + + +//------------------------------------------------- +// cache_operator_data - fill the operator cache +// with prefetched data; note that this code is +// also used by ymopna_registers, so it must +// handle upper channels cleanly +//------------------------------------------------- + +template +void opl_registers_base::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache) +{ + // set up the easy stuff + cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0]; + + // get frequency from the channel + uint32_t block_freq = cache.block_freq = ch_block_freq(choffs); + + // compute the keycode: block_freq is: + // + // 111 | + // 21098|76543210 + // BBBFF|FFFFFFFF + // ^^^?? + // + // the 4-bit keycode uses the top 3 bits plus one of the next two bits + uint32_t keycode = bitfield(block_freq, 10, 3) << 1; + + // lowest bit is determined by note_select(); note that it is + // actually reversed from what the manual says, however + keycode |= bitfield(block_freq, 9 - note_select(), 1); + + // no detune adjustment on OPL + cache.detune = 0; + + // multiple value, as an x.1 value (0 means 0.5) + // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 + uint32_t multiple = op_multiple(opoffs); + cache.multiple = ((multiple & 0xe) | bitfield(0xc2aa, multiple)) * 2; + if (cache.multiple == 0) + cache.multiple = 1; + + // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on block_freq, detune, + // and multiple, so compute it after we've done those + if (op_lfo_pm_enable(opoffs) == 0) + cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0); + else + cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC; + + // total level, scaled by 8 + cache.total_level = op_total_level(opoffs) << 3; + + // pre-add key scale level + uint32_t ksl = op_ksl(opoffs); + if (ksl != 0) + cache.total_level += opl_key_scale_atten(bitfield(block_freq, 10, 3), bitfield(block_freq, 6, 4)) << ksl; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = op_sustain_level(opoffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // determine KSR adjustment for enevlope rates + uint32_t ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1)); + cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_SUSTAIN] = op_eg_sustain(opoffs) ? 0 : effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_DEPRESS] = 0x3f; +} + + +//------------------------------------------------- +// compute_phase_step - compute the phase step +//------------------------------------------------- + +static uint32_t opl_compute_phase_step(uint32_t block_freq, uint32_t multiple, int32_t lfo_raw_pm) +{ + // OPL phase calculation has no detuning, but uses FNUMs like + // the OPN version, and computes PM a bit differently + + // extract frequency number as a 12-bit fraction + uint32_t fnum = bitfield(block_freq, 0, 10) << 2; + + // apply the phase adjustment based on the upper 3 bits + // of FNUM and the PM depth parameters + fnum += (lfo_raw_pm * bitfield(block_freq, 7, 3)) >> 1; + + // keep fnum to 12 bits + fnum &= 0xfff; + + // apply block shift to compute phase step + uint32_t block = bitfield(block_freq, 10, 3); + uint32_t phase_step = (fnum << block) >> 2; + + // apply frequency multiplier (which is cached as an x.1 value) + return (phase_step * multiple) >> 1; +} + +template +uint32_t opl_registers_base::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm) +{ + return opl_compute_phase_step(cache.block_freq, cache.multiple, op_lfo_pm_enable(opoffs) ? lfo_raw_pm : 0); +} + + +//------------------------------------------------- +// log_keyon - log a key-on event +//------------------------------------------------- + +template +std::string opl_registers_base::log_keyon(uint32_t choffs, uint32_t opoffs) +{ + return ""; +} + + +//********************************************************* +// OPLL SPECIFICS +//********************************************************* + +//------------------------------------------------- +// opll_registers - constructor +//------------------------------------------------- + +opll_registers::opll_registers() : + m_lfo_am_counter(0), + m_lfo_pm_counter(0), + m_noise_lfsr(1), + m_lfo_am(0) +{ + // create the waveforms + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); + + uint16_t zeroval = m_waveform[0][0]; + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[1][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index]; + + // initialize the instruments to something sane + for (uint32_t choffs = 0; choffs < CHANNELS; choffs++) + m_chinst[choffs] = &m_regdata[0]; + for (uint32_t opoffs = 0; opoffs < OPERATORS; opoffs++) + m_opinst[opoffs] = &m_regdata[bitfield(opoffs, 0)]; +} + + +//------------------------------------------------- +// reset - reset to initial state +//------------------------------------------------- + +void opll_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void opll_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_lfo_am_counter); + state.save_restore(m_lfo_pm_counter); + state.save_restore(m_lfo_am); + state.save_restore(m_noise_lfsr); + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// operator_map - return an array of operator +// indices for each channel; for OPLL this is fixed +//------------------------------------------------- + +void opll_registers::operator_map(operator_mapping &dest) const +{ + static const operator_mapping s_fixed_map = + { { + operator_list( 0, 1 ), // Channel 0 operators + operator_list( 2, 3 ), // Channel 1 operators + operator_list( 4, 5 ), // Channel 2 operators + operator_list( 6, 7 ), // Channel 3 operators + operator_list( 8, 9 ), // Channel 4 operators + operator_list( 10, 11 ), // Channel 5 operators + operator_list( 12, 13 ), // Channel 6 operators + operator_list( 14, 15 ), // Channel 7 operators + operator_list( 16, 17 ), // Channel 8 operators + } }; + dest = s_fixed_map; +} + + +//------------------------------------------------- +// write - handle writes to the register array; +// note that this code is also used by +// ymopl3_registers, so it must handle upper +// channels cleanly +//------------------------------------------------- + +bool opll_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask) +{ + // unclear the address is masked down to 6 bits or if writes above + // the register top are ignored; assuming the latter for now + if (index >= REGISTERS) + return false; + + // write the new data + m_regdata[index] = data; + + // handle writes to the rhythm keyons + if (index == 0x0e) + { + channel = RHYTHM_CHANNEL; + opmask = bitfield(data, 5) ? bitfield(data, 0, 5) : 0; + return true; + } + + // handle writes to the channel keyons + if ((index & 0xf0) == 0x20) + { + channel = index & 0x0f; + if (channel < CHANNELS) + { + opmask = bitfield(data, 4) ? 3 : 0; + return true; + } + } + return false; +} + + +//------------------------------------------------- +// clock_noise_and_lfo - clock the noise and LFO, +// handling clock division, depth, and waveform +// computations +//------------------------------------------------- + +int32_t opll_registers::clock_noise_and_lfo() +{ + // implementation is the same as OPL with fixed depths + return opl_clock_noise_and_lfo(m_noise_lfsr, m_lfo_am_counter, m_lfo_pm_counter, m_lfo_am, 1, 1); +} + + +//------------------------------------------------- +// cache_operator_data - fill the operator cache +// with prefetched data; note that this code is +// also used by ymopna_registers, so it must +// handle upper channels cleanly +//------------------------------------------------- + +void opll_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache) +{ + // first set up the instrument data + uint32_t instrument = ch_instrument(choffs); + if (rhythm_enable() && choffs >= 6) + m_chinst[choffs] = &m_instdata[8 * (15 + (choffs - 6))]; + else + m_chinst[choffs] = (instrument == 0) ? &m_regdata[0] : &m_instdata[8 * (instrument - 1)]; + m_opinst[opoffs] = m_chinst[choffs] + bitfield(opoffs, 0); + + // set up the easy stuff + cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0]; + + // get frequency from the channel + uint32_t block_freq = cache.block_freq = ch_block_freq(choffs); + + // compute the keycode: block_freq is: + // + // 11 | + // 1098|76543210 + // BBBF|FFFFFFFF + // ^^^^ + // + // the 4-bit keycode uses the top 4 bits + uint32_t keycode = bitfield(block_freq, 8, 4); + + // no detune adjustment on OPLL + cache.detune = 0; + + // multiple value, as an x.1 value (0 means 0.5) + // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 + uint32_t multiple = op_multiple(opoffs); + cache.multiple = ((multiple & 0xe) | bitfield(0xc2aa, multiple)) * 2; + if (cache.multiple == 0) + cache.multiple = 1; + + // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on + // block_freq, detune, and multiple, so compute it after we've done those + if (op_lfo_pm_enable(opoffs) == 0) + cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0); + else + cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC; + + // total level, scaled by 8; for non-rhythm operator 0, this is the total + // level from the instrument data; for other operators it is 4*volume + if (bitfield(opoffs, 0) == 1 || (rhythm_enable() && choffs >= 7)) + cache.total_level = op_volume(opoffs) * 4; + else + cache.total_level = ch_total_level(choffs); + cache.total_level <<= 3; + + // pre-add key scale level + uint32_t ksl = op_ksl(opoffs); + if (ksl != 0) + cache.total_level += opl_key_scale_atten(bitfield(block_freq, 9, 3), bitfield(block_freq, 5, 4)) << ksl; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = op_sustain_level(opoffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // The envelope diagram in the YM2413 datasheet gives values for these + // in ms from 0->48dB. The attack/decay tables give values in ms from + // 0->96dB, so to pick an equivalent decay rate, we want to find the + // closest match that is 2x the 0->48dB value: + // + // DP = 10ms (0->48db) -> 20ms (0->96db); decay of 12 gives 19.20ms + // RR = 310ms (0->48db) -> 620ms (0->96db); decay of 7 gives 613.76ms + // RS = 1200ms (0->48db) -> 2400ms (0->96db); decay of 5 gives 2455.04ms + // + // The envelope diagram for percussive sounds (eg_sustain() == 0) also uses + // "RR" to mean both the constant RR above and the Release Rate specified in + // the instrument data. In this case, Relief Pitcher's credit sound bears out + // that the Release Rate is used during sustain, and that the constant RR + // (or RS) is used during the release phase. + constexpr uint8_t DP = 12 * 4; + constexpr uint8_t RR = 7 * 4; + constexpr uint8_t RS = 5 * 4; + + // determine KSR adjustment for envelope rates + uint32_t ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1)); + cache.eg_rate[EG_DEPRESS] = DP; + cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval); + if (op_eg_sustain(opoffs)) + { + cache.eg_rate[EG_SUSTAIN] = 0; + cache.eg_rate[EG_RELEASE] = ch_sustain(choffs) ? RS : effective_rate(op_release_rate(opoffs) * 4, ksrval); + } + else + { + cache.eg_rate[EG_SUSTAIN] = effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_RELEASE] = ch_sustain(choffs) ? RS : RR; + } +} + + +//------------------------------------------------- +// compute_phase_step - compute the phase step +//------------------------------------------------- + +uint32_t opll_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm) +{ + // phase step computation is the same as OPL but the block_freq has one + // more bit, which we shift in + return opl_compute_phase_step(cache.block_freq << 1, cache.multiple, op_lfo_pm_enable(opoffs) ? lfo_raw_pm : 0); +} + + +//------------------------------------------------- +// log_keyon - log a key-on event +//------------------------------------------------- + +std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) +{ + return ""; +} + + + +//********************************************************* +// YM3526 +//********************************************************* + +//------------------------------------------------- +// ym3526 - constructor +//------------------------------------------------- + +ym3526::ym3526(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym3526::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym3526::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ym3526::read_status() +{ + return m_fm.status() | 0x06; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym3526::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 1) + { + case 0: // status port + result = read_status(); + break; + + case 1: // when A0=1 datasheet says "the data on the bus are not guaranteed" + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym3526::write_address(uint8_t data) +{ + // YM3526 doesn't expose a busy signal, and the datasheets don't indicate + // delays, but all other OPL chips need 12 cycles for address writes + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3526::write_data(uint8_t data) +{ + // YM3526 doesn't expose a busy signal, and the datasheets don't indicate + // delays, but all other OPL chips need 84 cycles for data writes + m_fm.intf().ymfm_set_busy_end(84 * m_fm.clock_prescale()); + + // write to FM + m_fm.write(m_address, data); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3526::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ym3526::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YM3526 need verification + m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS); + + // YM3526 uses an external DAC (YM3014) with mantissa/exponent format + // convert to 10.3 floating point value and back to simulate truncation + output->roundtrip_fp(); + } +} + + + +//********************************************************* +// Y8950 +//********************************************************* + +//------------------------------------------------- +// y8950 - constructor +//------------------------------------------------- + +y8950::y8950(ymfm_interface &intf) : + m_address(0), + m_io_ddr(0), + m_fm(intf), + m_adpcm_b(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void y8950::reset() +{ + // reset the engines + m_fm.reset(); + m_adpcm_b.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void y8950::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_io_ddr); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t y8950::read_status() +{ + // start with current FM status, masking out bits we might set + uint8_t status = m_fm.status() & ~(STATUS_ADPCM_B_EOS | STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_PLAYING); + + // insert the live ADPCM status bits + uint8_t adpcm_status = m_adpcm_b.status(); + if ((adpcm_status & adpcm_b_channel::STATUS_EOS) != 0) + status |= STATUS_ADPCM_B_EOS; + if ((adpcm_status & adpcm_b_channel::STATUS_BRDY) != 0) + status |= STATUS_ADPCM_B_BRDY; + if ((adpcm_status & adpcm_b_channel::STATUS_PLAYING) != 0) + status |= STATUS_ADPCM_B_PLAYING; + + // run it through the FM engine to handle interrupts for us + return m_fm.set_reset_status(status, ~status); +} + + +//------------------------------------------------- +// read_data - read the data port +//------------------------------------------------- + +uint8_t y8950::read_data() +{ + uint8_t result = 0xff; + switch (m_address) + { + case 0x05: // keyboard in + result = m_fm.intf().ymfm_external_read(ACCESS_IO, 1); + break; + + case 0x09: // ADPCM data + case 0x1a: + result = m_adpcm_b.read(m_address - 0x07); + break; + + case 0x19: // I/O data + result = m_fm.intf().ymfm_external_read(ACCESS_IO, 0); + break; + + default: + debug::log_unexpected_read_write("Unexpected read from Y8950 data port %02X\n", m_address); + break; + } + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t y8950::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 1) + { + case 0: // status port + result = read_status(); + break; + + case 1: // when A0=1 datasheet says "the data on the bus are not guaranteed" + result = read_data(); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void y8950::write_address(uint8_t data) +{ + // Y8950 doesn't expose a busy signal, but it does indicate that + // address writes should be no faster than every 12 clocks + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void y8950::write_data(uint8_t data) +{ + // Y8950 doesn't expose a busy signal, but it does indicate that + // data writes should be no faster than every 12 clocks for + // registers 00-1A, or every 84 clocks for other registers + m_fm.intf().ymfm_set_busy_end(((m_address <= 0x1a) ? 12 : 84) * m_fm.clock_prescale()); + + // handle special addresses + switch (m_address) + { + case 0x04: // IRQ control + m_fm.write(m_address, data); + read_status(); + break; + + case 0x06: // keyboard out + m_fm.intf().ymfm_external_write(ACCESS_IO, 1, data); + break; + + case 0x08: // split FM/ADPCM-B + m_adpcm_b.write(m_address - 0x07, (data & 0x0f) | 0x80); + m_fm.write(m_address, data & 0xc0); + break; + + case 0x07: // ADPCM-B registers + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x15: + case 0x16: + case 0x17: + m_adpcm_b.write(m_address - 0x07, data); + break; + + case 0x18: // I/O direction + m_io_ddr = data & 0x0f; + break; + + case 0x19: // I/O data + m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data & m_io_ddr); + break; + + default: // everything else to FM + m_fm.write(m_address, data); + break; + } +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void y8950::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void y8950::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + m_adpcm_b.clock(); + + // update the FM content; clipping need verification + m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS); + + // mix in the ADPCM; ADPCM-B is stereo, but only one channel + // not sure how it's wired up internally + m_adpcm_b.output(*output, 3); + + // Y8950 uses an external DAC (YM3014) with mantissa/exponent format + // convert to 10.3 floating point value and back to simulate truncation + output->roundtrip_fp(); + } +} + + + +//********************************************************* +// YM3812 +//********************************************************* + +//------------------------------------------------- +// ym3812 - constructor +//------------------------------------------------- + +ym3812::ym3812(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym3812::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym3812::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ym3812::read_status() +{ + return m_fm.status() | 0x06; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym3812::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 1) + { + case 0: // status port + result = read_status(); + break; + + case 1: // "inhibit" according to datasheet + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym3812::write_address(uint8_t data) +{ + // YM3812 doesn't expose a busy signal, but it does indicate that + // address writes should be no faster than every 12 clocks + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3812::write_data(uint8_t data) +{ + // YM3812 doesn't expose a busy signal, but it does indicate that + // data writes should be no faster than every 84 clocks + m_fm.intf().ymfm_set_busy_end(84 * m_fm.clock_prescale()); + + // write to FM + m_fm.write(m_address, data); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3812::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ym3812::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YM3812 need verification + m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS); + + // YM3812 uses an external DAC (YM3014) with mantissa/exponent format + // convert to 10.3 floating point value and back to simulate truncation + output->roundtrip_fp(); + } +} + + + +//********************************************************* +// YMF262 +//********************************************************* + +//------------------------------------------------- +// ymf262 - constructor +//------------------------------------------------- + +ymf262::ymf262(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf262::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf262::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf262::read_status() +{ + return m_fm.status(); +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf262::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 3) + { + case 0: // status port + result = read_status(); + break; + + case 1: + case 2: + case 3: + debug::log_unexpected_read_write("Unexpected read from YMF262 offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf262::write_address(uint8_t data) +{ + // YMF262 doesn't expose a busy signal, but it does indicate that + // address writes should be no faster than every 32 clocks + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write_data - handle a write to the data +// register +//------------------------------------------------- + +void ymf262::write_data(uint8_t data) +{ + // YMF262 doesn't expose a busy signal, but it does indicate that + // data writes should be no faster than every 32 clocks + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); + + // write to FM + m_fm.write(m_address, data); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf262::write_address_hi(uint8_t data) +{ + // YMF262 doesn't expose a busy signal, but it does indicate that + // address writes should be no faster than every 32 clocks + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); + + // just set the address + m_address = data | 0x100; + + // tests reveal that in compatibility mode, upper bit is masked + // except for register 0x105 + if (m_fm.regs().newflag() == 0 && m_address != 0x105) + m_address &= 0xff; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf262::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // address port + write_address_hi(data); + break; + + case 3: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ymf262::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YMF262 need verification + m_fm.output(output->clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // YMF262 output is 16-bit offset serial via YAC512 DAC + output->clamp16(); + } +} + + + +//********************************************************* +// YMF289B +//********************************************************* + +// YMF289B is a YMF262 with the following changes: +// * "Power down" mode added +// * Bulk register clear added +// * Busy flag added to the status register +// * Shorter busy times +// * All registers can be read +// * Only 2 outputs exposed + +//------------------------------------------------- +// ymf289b - constructor +//------------------------------------------------- + +ymf289b::ymf289b(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf289b::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf289b::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf289b::read_status() +{ + uint8_t result = m_fm.status(); + + // YMF289B adds a busy flag + if (ymf289b_mode() && m_fm.intf().ymfm_is_busy()) + result |= STATUS_BUSY_FLAGS; + return result; +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ymf289b::read_data() +{ + uint8_t result = 0xff; + + // YMF289B can read register data back + if (ymf289b_mode()) + result = m_fm.regs().read(m_address); + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf289b::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 3) + { + case 0: // status port + result = read_status(); + break; + + case 1: // data port + result = read_data(); + break; + + case 2: + case 3: + debug::log_unexpected_read_write("Unexpected read from YMF289B offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf289b::write_address(uint8_t data) +{ + m_address = data; + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write_data - handle a write to the data +// register +//------------------------------------------------- + +void ymf289b::write_data(uint8_t data) +{ + // write to FM + m_fm.write(m_address, data); + + // writes to 0x108 with the CLR flag set clear the registers + if (m_address == 0x108 && bitfield(data, 2) != 0) + m_fm.regs().reset(); + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf289b::write_address_hi(uint8_t data) +{ + // just set the address + m_address = data | 0x100; + + // tests reveal that in compatibility mode, upper bit is masked + // except for register 0x105 + if (m_fm.regs().newflag() == 0 && m_address != 0x105) + m_address &= 0xff; + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf289b::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // address port + write_address_hi(data); + break; + + case 3: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ymf289b::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YMF262 need verification + fm_engine::output_data full; + m_fm.output(full.clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // YMF278B output is 16-bit offset serial via YAC512 DAC, but + // only 2 of the 4 outputs are exposed + output->data[0] = full.data[0]; + output->data[1] = full.data[1]; + output->clamp16(); + } +} + + + +//********************************************************* +// YMF278B +//********************************************************* + +//------------------------------------------------- +// ymf278b - constructor +//------------------------------------------------- + +ymf278b::ymf278b(ymfm_interface &intf) : + m_address(0), + m_fm_pos(0), + m_load_remaining(0), + m_next_status_id(false), + m_fm(intf), + m_pcm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf278b::reset() +{ + // reset the engines + m_fm.reset(); + m_pcm.reset(); + + // next status read will return ID + m_next_status_id = true; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf278b::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_fm_pos); + state.save_restore(m_load_remaining); + state.save_restore(m_next_status_id); + m_fm.save_restore(state); + m_pcm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf278b::read_status() +{ + uint8_t result; + + // first status read after initialization returns a chip ID, which + // varies based on the "new" flags, indicating the mode + if (m_next_status_id) + { + if (m_fm.regs().new2flag()) + result = 0x02; + else if (m_fm.regs().newflag()) + result = 0x00; + else + result = 0x06; + m_next_status_id = false; + } + else + { + result = m_fm.status(); + if (m_fm.intf().ymfm_is_busy()) + result |= STATUS_BUSY; + if (m_load_remaining != 0) + result |= STATUS_LD; + + // if new2 flag is not set, we're in OPL2 or OPL3 mode + if (!m_fm.regs().new2flag()) + result &= ~(STATUS_BUSY | STATUS_LD); + } + return result; +} + + +//------------------------------------------------- +// write_data_pcm - handle a write to the PCM data +// register +//------------------------------------------------- + +uint8_t ymf278b::read_data_pcm() +{ + // read from PCM + if (bitfield(m_address, 9) != 0) + { + uint8_t result = m_pcm.read(m_address & 0xff); + if ((m_address & 0xff) == 0x02) + result |= 0x20; + + return result; + } + return 0; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf278b::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 7) + { + case 0: // status port + result = read_status(); + break; + + case 5: // PCM data port + result = read_data_pcm(); + break; + + default: + debug::log_unexpected_read_write("Unexpected read from ymf278b offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf278b::write_address(uint8_t data) +{ + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write_data - handle a write to the data +// register +//------------------------------------------------- + +void ymf278b::write_data(uint8_t data) +{ + // write to FM + if (bitfield(m_address, 9) == 0) + { + uint8_t old = m_fm.regs().new2flag(); + m_fm.write(m_address, data); + + // changing NEW2 from 0->1 causes the next status read to + // return the chip ID + if (old == 0 && m_fm.regs().new2flag() != 0) + m_next_status_id = true; + } + + // BUSY goes for 56 clocks on FM writes + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf278b::write_address_hi(uint8_t data) +{ + // just set the address + m_address = data | 0x100; + + // YMF262, in compatibility mode, treats the upper bit as masked + // except for register 0x105; assuming YMF278B works the same way? + if (m_fm.regs().newflag() == 0 && m_address != 0x105) + m_address &= 0xff; +} + + +//------------------------------------------------- +// write_address_pcm - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf278b::write_address_pcm(uint8_t data) +{ + // just set the address + m_address = data | 0x200; +} + + +//------------------------------------------------- +// write_data_pcm - handle a write to the PCM data +// register +//------------------------------------------------- + +void ymf278b::write_data_pcm(uint8_t data) +{ + // ignore data writes if new2 is not yet set + if (m_fm.regs().new2flag() == 0) + return; + + // write to FM + if (bitfield(m_address, 9) != 0) + { + uint8_t addr = m_address & 0xff; + m_pcm.write(addr, data); + + // writes to the waveform number cause loads to happen for "about 300usec" + // which is ~13 samples at the nominal output frequency of 44.1kHz + if (addr >= 0x08 && addr <= 0x1f) + m_load_remaining = 13; + } + + // BUSY goes for 88 clocks on PCM writes + m_fm.intf().ymfm_set_busy_end(88); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf278b::write(uint32_t offset, uint8_t data) +{ + switch (offset & 7) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // address port + write_address_hi(data); + break; + + case 3: // data port + write_data(data); + break; + + case 4: // PCM address port + write_address_pcm(data); + break; + + case 5: // PCM address port + write_data_pcm(data); + break; + + default: + debug::log_unexpected_read_write("Unexpected write to ymf278b offset %d\n", offset & 7); + break; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ymf278b::generate(output_data *output, uint32_t numsamples) +{ + static const int16_t s_mix_scale[8] = { 0x7fa, 0x5a4, 0x3fd, 0x2d2, 0x1fe, 0x169, 0xff, 0 }; + int32_t const pcm_l = s_mix_scale[m_pcm.regs().mix_pcm_l()]; + int32_t const pcm_r = s_mix_scale[m_pcm.regs().mix_pcm_r()]; + int32_t const fm_l = s_mix_scale[m_pcm.regs().mix_fm_l()]; + int32_t const fm_r = s_mix_scale[m_pcm.regs().mix_fm_r()]; + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm_pos += FM_EXTRA_SAMPLE_STEP; + if (m_fm_pos >= FM_EXTRA_SAMPLE_THRESH) + { + m_fm.clock(fm_engine::ALL_CHANNELS); + m_fm_pos -= FM_EXTRA_SAMPLE_THRESH; + } + m_fm.clock(fm_engine::ALL_CHANNELS); + m_pcm.clock(pcm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YMF278B need verification + fm_engine::output_data fmout; + m_fm.output(fmout.clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // update the PCM content + pcm_engine::output_data pcmout; + m_pcm.output(pcmout.clear(), pcm_engine::ALL_CHANNELS); + + // DO0 output: FM channels 2+3 only + output->data[0] = fmout.data[2]; + output->data[1] = fmout.data[3]; + + // DO1 output: wavetable channels 2+3 only + output->data[2] = pcmout.data[2]; + output->data[3] = pcmout.data[3]; + + // DO2 output: mixed FM channels 0+1 and wavetable channels 0+1 + output->data[4] = (fmout.data[0] * fm_l + pcmout.data[0] * pcm_l) >> 11; + output->data[5] = (fmout.data[1] * fm_r + pcmout.data[1] * pcm_r) >> 11; + + // YMF278B output is 16-bit 2s complement serial + output->clamp16(); + } + + // decrement the load waiting count + if (m_load_remaining > 0) + m_load_remaining -= std::min(m_load_remaining, numsamples); +} + + + +//********************************************************* +// OPLL BASE +//********************************************************* + +//------------------------------------------------- +// opll_base - constructor +//------------------------------------------------- + +opll_base::opll_base(ymfm_interface &intf, uint8_t const *instrument_data) : + m_address(0), + m_fm(intf) +{ + m_fm.regs().set_instrument_data(instrument_data); +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void opll_base::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void opll_base::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void opll_base::write_address(uint8_t data) +{ + // OPLL doesn't expose a busy signal, but datasheets are pretty consistent + // in indicating that address writes should be no faster than every 12 clocks + m_fm.intf().ymfm_set_busy_end(12); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void opll_base::write_data(uint8_t data) +{ + // OPLL doesn't expose a busy signal, but datasheets are pretty consistent + // in indicating that address writes should be no faster than every 84 clocks + m_fm.intf().ymfm_set_busy_end(84); + + // write to FM + m_fm.write(m_address, data); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void opll_base::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void opll_base::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; OPLL has a built-in 9-bit DAC + m_fm.output(output->clear(), 5, 256, fm_engine::ALL_CHANNELS); + + // final output is multiplexed; we don't simulate that here except + // to average over everything + output->data[0] = (output->data[0] * 128) / 9; + output->data[1] = (output->data[1] * 128) / 9; + } +} + + + +//********************************************************* +// YM2413 +//********************************************************* + +//------------------------------------------------- +// ym2413 - constructor +//------------------------------------------------- + +ym2413::ym2413(ymfm_interface &intf, uint8_t const *instrument_data) : + opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments) +{ +}; + +// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches +uint8_t const ym2413::s_default_instruments[] = +{ + //April 2015 David Viens, tweaked May 19-21th 2015 Hubert Lamontagne + 0x71, 0x61, 0x1E, 0x17, 0xEF, 0x7F, 0x00, 0x17, //Violin + 0x13, 0x41, 0x1A, 0x0D, 0xF8, 0xF7, 0x23, 0x13, //Guitar + 0x13, 0x01, 0x99, 0x00, 0xF2, 0xC4, 0x11, 0x23, //Piano + 0x31, 0x61, 0x0E, 0x07, 0x98, 0x64, 0x70, 0x27, //Flute + 0x22, 0x21, 0x1E, 0x06, 0xBF, 0x76, 0x00, 0x28, //Clarinet + 0x31, 0x22, 0x16, 0x05, 0xE0, 0x71, 0x0F, 0x18, //Oboe + 0x21, 0x61, 0x1D, 0x07, 0x82, 0x8F, 0x10, 0x07, //Trumpet + 0x23, 0x21, 0x2D, 0x14, 0xFF, 0x7F, 0x00, 0x07, //Organ + 0x41, 0x61, 0x1B, 0x06, 0x64, 0x65, 0x10, 0x17, //Horn + 0x61, 0x61, 0x0B, 0x18, 0x85, 0xFF, 0x81, 0x07, //Synthesizer + 0x13, 0x01, 0x83, 0x11, 0xFA, 0xE4, 0x10, 0x04, //Harpsichord + 0x17, 0x81, 0x23, 0x07, 0xF8, 0xF8, 0x22, 0x12, //Vibraphone + 0x61, 0x50, 0x0C, 0x05, 0xF2, 0xF5, 0x29, 0x42, //Synthesizer Bass + 0x01, 0x01, 0x54, 0x03, 0xC3, 0x92, 0x03, 0x02, //Acoustic Bass + 0x41, 0x41, 0x89, 0x03, 0xF1, 0xE5, 0x11, 0x13, //Electric Guitar + 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1 + 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2 + 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3 +}; + + + +//********************************************************* +// YM2423 +//********************************************************* + +//------------------------------------------------- +// ym2423 - constructor +//------------------------------------------------- + +ym2423::ym2423(ymfm_interface &intf, uint8_t const *instrument_data) : + opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments) +{ +}; + +// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches +uint8_t const ym2423::s_default_instruments[] = +{ + // May 4-6 2016 Hubert Lamontagne + // Doesn't seem to have any diff between opllx-x and opllx-y + // Drums seem identical to regular opll + 0x61, 0x61, 0x1B, 0x07, 0x94, 0x5F, 0x10, 0x06, //1 Strings Saw wave with vibrato Violin + 0x93, 0xB1, 0x51, 0x04, 0xF3, 0xF2, 0x70, 0xFB, //2 Guitar Jazz GuitarPiano + 0x41, 0x21, 0x11, 0x85, 0xF2, 0xF2, 0x70, 0x75, //3 Electric Guitar Same as OPLL No.15 Synth + 0x93, 0xB2, 0x28, 0x07, 0xF3, 0xF2, 0x70, 0xB4, //4 Electric Piano 2 Slow attack, tremoloDing-a-ling + 0x72, 0x31, 0x97, 0x05, 0x51, 0x6F, 0x60, 0x09, //5 Flute Same as OPLL No.4Clarinet + 0x13, 0x30, 0x18, 0x06, 0xF7, 0xF4, 0x50, 0x85, //6 Marimba Also be used as steel drumXyophone + 0x51, 0x31, 0x1C, 0x07, 0x51, 0x71, 0x20, 0x26, //7 Trumpet Same as OPLL No.7Trumpet + 0x41, 0xF4, 0x1B, 0x07, 0x74, 0x34, 0x00, 0x06, //8 Harmonica Harmonica synth + 0x50, 0x30, 0x4D, 0x03, 0x42, 0x65, 0x20, 0x06, //9 Tuba Tuba + 0x40, 0x20, 0x10, 0x85, 0xF3, 0xF5, 0x20, 0x04, //10 Synth Brass 2 Synth sweep + 0x61, 0x61, 0x1B, 0x07, 0xC5, 0x96, 0xF3, 0xF6, //11 Short Saw Saw wave with short envelopeSynth hit + 0xF9, 0xF1, 0xDC, 0x00, 0xF5, 0xF3, 0x77, 0xF2, //12 Vibraphone Bright vibraphoneVibes + 0x60, 0xA2, 0x91, 0x03, 0x94, 0xC1, 0xF7, 0xF7, //13 Electric Guitar 2 Clean guitar with feedbackHarmonic bass + 0x30, 0x30, 0x17, 0x06, 0xF3, 0xF1, 0xB7, 0xFC, //14 Synth Bass 2Snappy bass + 0x31, 0x36, 0x0D, 0x05, 0xF2, 0xF4, 0x27, 0x9C, //15 Sitar Also be used as ShamisenBanjo + 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1 + 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2 + 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3 +}; + + + +//********************************************************* +// YMF281 +//********************************************************* + +//------------------------------------------------- +// ymf281 - constructor +//------------------------------------------------- + +ymf281::ymf281(ymfm_interface &intf, uint8_t const *instrument_data) : + opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments) +{ +}; + +// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches +uint8_t const ymf281::s_default_instruments[] = +{ + // May 14th 2015 Hubert Lamontagne + 0x72, 0x21, 0x1A, 0x07, 0xF6, 0x64, 0x01, 0x16, // Clarinet ~~ Electric String Square wave with vibrato + 0x00, 0x10, 0x45, 0x00, 0xF6, 0x83, 0x73, 0x63, // Synth Bass ~~ Bow wow Triangular wave + 0x13, 0x01, 0x96, 0x00, 0xF1, 0xF4, 0x31, 0x23, // Piano ~~ Electric Guitar Despite of its name, same as Piano of YM2413. + 0x71, 0x21, 0x0B, 0x0F, 0xF9, 0x64, 0x70, 0x17, // Flute ~~ Organ Sine wave + 0x02, 0x21, 0x1E, 0x06, 0xF9, 0x76, 0x00, 0x28, // Square Wave ~~ Clarinet Same as ones of YM2413. + 0x00, 0x61, 0x82, 0x0E, 0xF9, 0x61, 0x20, 0x27, // Space Oboe ~~ Saxophone Saw wave with vibrato + 0x21, 0x61, 0x1B, 0x07, 0x84, 0x8F, 0x10, 0x07, // Trumpet ~~ Trumpet Same as ones of YM2413. + 0x37, 0x32, 0xCA, 0x02, 0x66, 0x64, 0x47, 0x29, // Wow Bell ~~ Street Organ Calliope + 0x41, 0x41, 0x07, 0x03, 0xF5, 0x70, 0x51, 0xF5, // Electric Guitar ~~ Synth Brass Same as Synthesizer of YM2413. + 0x36, 0x01, 0x5E, 0x07, 0xF2, 0xF3, 0xF7, 0xF7, // Vibes ~~ Electric Piano Simulate of Rhodes Piano + 0x00, 0x00, 0x18, 0x06, 0xC5, 0xF3, 0x20, 0xF2, // Bass ~~ Bass Electric bass + 0x17, 0x81, 0x25, 0x07, 0xF7, 0xF3, 0x21, 0xF7, // Vibraphone ~~ Vibraphone Same as ones of YM2413. + 0x35, 0x64, 0x00, 0x00, 0xFF, 0xF3, 0x77, 0xF5, // Vibrato Bell ~~ Chime Bell + 0x11, 0x31, 0x00, 0x07, 0xDD, 0xF3, 0xFF, 0xFB, // Click Sine ~~ Tom Tom II Tom + 0x3A, 0x21, 0x00, 0x07, 0x95, 0x84, 0x0F, 0xF5, // Noise and Tone ~~ Noise for S.E. + 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1 + 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2 + 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3 +}; + + + +//********************************************************* +// DS1001 +//********************************************************* + +//------------------------------------------------- +// ds1001 - constructor +//------------------------------------------------- + +ds1001::ds1001(ymfm_interface &intf, uint8_t const *instrument_data) : + opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments) +{ +}; + +// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches +uint8_t const ds1001::s_default_instruments[] = +{ + // May 15th 2015 Hubert Lamontagne & David Viens + 0x03, 0x21, 0x05, 0x06, 0xC8, 0x81, 0x42, 0x27, // Buzzy Bell + 0x13, 0x41, 0x14, 0x0D, 0xF8, 0xF7, 0x23, 0x12, // Guitar + 0x31, 0x11, 0x08, 0x08, 0xFA, 0xC2, 0x28, 0x22, // Wurly + 0x31, 0x61, 0x0C, 0x07, 0xF8, 0x64, 0x60, 0x27, // Flute + 0x22, 0x21, 0x1E, 0x06, 0xFF, 0x76, 0x00, 0x28, // Clarinet + 0x02, 0x01, 0x05, 0x00, 0xAC, 0xF2, 0x03, 0x02, // Synth + 0x21, 0x61, 0x1D, 0x07, 0x82, 0x8F, 0x10, 0x07, // Trumpet + 0x23, 0x21, 0x22, 0x17, 0xFF, 0x73, 0x00, 0x17, // Organ + 0x15, 0x11, 0x25, 0x00, 0x41, 0x71, 0x00, 0xF1, // Bells + 0x95, 0x01, 0x10, 0x0F, 0xB8, 0xAA, 0x50, 0x02, // Vibes + 0x17, 0xC1, 0x5E, 0x07, 0xFA, 0xF8, 0x22, 0x12, // Vibraphone + 0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x10, 0x16, // Tutti + 0x01, 0x02, 0xD3, 0x05, 0xF3, 0x92, 0x83, 0xF2, // Fretless + 0x61, 0x63, 0x0C, 0x00, 0xA4, 0xFF, 0x30, 0x06, // Synth Bass + 0x21, 0x62, 0x0D, 0x00, 0xA1, 0xFF, 0x50, 0x08, // Sweep + 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1 + 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2 + 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3 +}; + + +//********************************************************* +// EXPLICIT INSTANTIATION +//********************************************************* + +template class opl_registers_base<4>; +template class fm_engine_base>; + +} diff --git a/src/engine/platform/sound/ymfm/ymfm_opl.h b/src/engine/platform/sound/ymfm/ymfm_opl.h new file mode 100644 index 000000000..843e5b274 --- /dev/null +++ b/src/engine/platform/sound/ymfm/ymfm_opl.h @@ -0,0 +1,902 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef YMFM_OPL_H +#define YMFM_OPL_H + +#pragma once + +#include "ymfm.h" +#include "ymfm_adpcm.h" +#include "ymfm_fm.h" +#include "ymfm_pcm.h" + +namespace ymfm +{ + +//********************************************************* +// REGISTER CLASSES +//********************************************************* + +// ======================> opl_registers_base + +// +// OPL/OPL2/OPL3/OPL4 register map: +// +// System-wide registers: +// 01 xxxxxxxx Test register +// --x----- Enable OPL compatibility mode [OPL2 only] (1 = enable) +// 02 xxxxxxxx Timer A value (4 * OPN) +// 03 xxxxxxxx Timer B value +// 04 x------- RST +// -x------ Mask timer A +// --x----- Mask timer B +// ------x- Load timer B +// -------x Load timer A +// 08 x------- CSM mode [OPL/OPL2 only] +// -x------ Note select +// BD x------- AM depth +// -x------ PM depth +// --x----- Rhythm enable +// ---x---- Bass drum key on +// ----x--- Snare drum key on +// -----x-- Tom key on +// ------x- Top cymbal key on +// -------x High hat key on +// 101 --xxxxxx Test register 2 [OPL3 only] +// 104 --x----- Channel 6 4-operator mode [OPL3 only] +// ---x---- Channel 5 4-operator mode [OPL3 only] +// ----x--- Channel 4 4-operator mode [OPL3 only] +// -----x-- Channel 3 4-operator mode [OPL3 only] +// ------x- Channel 2 4-operator mode [OPL3 only] +// -------x Channel 1 4-operator mode [OPL3 only] +// 105 -------x New [OPL3 only] +// ------x- New2 [OPL4 only] +// +// Per-channel registers (channel in address bits 0-3) +// Note that all these apply to address+100 as well on OPL3+ +// A0-A8 xxxxxxxx F-number (low 8 bits) +// B0-B8 --x----- Key on +// ---xxx-- Block (octvate, 0-7) +// ------xx F-number (high two bits) +// C0-C8 x------- CHD output (to DO0 pin) [OPL3+ only] +// -x------ CHC output (to DO0 pin) [OPL3+ only] +// --x----- CHB output (mixed right, to DO2 pin) [OPL3+ only] +// ---x---- CHA output (mixed left, to DO2 pin) [OPL3+ only] +// ----xxx- Feedback level for operator 1 (0-7) +// -------x Operator connection algorithm +// +// Per-operator registers (operator in bits 0-5) +// Note that all these apply to address+100 as well on OPL3+ +// 20-35 x------- AM enable +// -x------ PM enable (VIB) +// --x----- EG type +// ---x---- Key scale rate +// ----xxxx Multiple value (0-15) +// 40-55 xx------ Key scale level (0-3) +// --xxxxxx Total level (0-63) +// 60-75 xxxx---- Attack rate (0-15) +// ----xxxx Decay rate (0-15) +// 80-95 xxxx---- Sustain level (0-15) +// ----xxxx Release rate (0-15) +// E0-F5 ------xx Wave select (0-3) [OPL2 only] +// -----xxx Wave select (0-7) [OPL3+ only] +// + +template +class opl_registers_base : public fm_registers_base +{ + static constexpr bool IsOpl2 = (Revision == 2); + static constexpr bool IsOpl2Plus = (Revision >= 2); + static constexpr bool IsOpl3Plus = (Revision >= 3); + static constexpr bool IsOpl4Plus = (Revision >= 4); + +public: + // constants + static constexpr uint32_t OUTPUTS = IsOpl3Plus ? 4 : 1; + static constexpr uint32_t CHANNELS = IsOpl3Plus ? 18 : 9; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr uint32_t OPERATORS = CHANNELS * 2; + static constexpr uint32_t WAVEFORMS = IsOpl3Plus ? 8 : (IsOpl2Plus ? 4 : 1); + static constexpr uint32_t REGISTERS = IsOpl3Plus ? 0x200 : 0x100; + static constexpr uint32_t REG_MODE = 0x04; + static constexpr uint32_t DEFAULT_PRESCALE = IsOpl4Plus ? 19 : (IsOpl3Plus ? 8 : 4); + static constexpr uint32_t EG_CLOCK_DIVIDER = 1; + static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS; + static constexpr bool DYNAMIC_OPS = IsOpl3Plus; + static constexpr bool MODULATOR_DELAY = !IsOpl3Plus; + static constexpr uint8_t STATUS_TIMERA = 0x40; + static constexpr uint8_t STATUS_TIMERB = 0x20; + static constexpr uint8_t STATUS_BUSY = 0; + static constexpr uint8_t STATUS_IRQ = 0x80; + + // constructor + opl_registers_base(); + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // map channel number to register offset + static constexpr uint32_t channel_offset(uint32_t chnum) + { + assert(chnum < CHANNELS); + if (!IsOpl3Plus) + return chnum; + else + return (chnum % 9) + 0x100 * (chnum / 9); + } + + // map operator number to register offset + static constexpr uint32_t operator_offset(uint32_t opnum) + { + assert(opnum < OPERATORS); + if (!IsOpl3Plus) + return opnum + 2 * (opnum / 6); + else + return (opnum % 18) + 2 * ((opnum % 18) / 6) + 0x100 * (opnum / 18); + } + + // return an array of operator indices for each channel + struct operator_mapping { uint32_t chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // OPL4 apparently can read back FM registers? + uint8_t read(uint16_t index) const { return m_regdata[index]; } + + // handle writes to the register array + bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + int32_t clock_noise_and_lfo(); + + // reset the LFO + void reset_lfo() { m_lfo_am_counter = m_lfo_pm_counter = 0; } + + // return the AM offset from LFO for the given channel + // on OPL this is just a fixed value + uint32_t lfo_am_offset(uint32_t choffs) const { return m_lfo_am; } + + // return LFO/noise states + uint32_t noise_state() const { return m_noise_lfsr >> 23; } + + // caching helpers + void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache); + + // compute the phase step, given a PM value + uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm); + + // log a key-on event + std::string log_keyon(uint32_t choffs, uint32_t opoffs); + + // system-wide registers + uint32_t test() const { return byte(0x01, 0, 8); } + uint32_t waveform_enable() const { return IsOpl2 ? byte(0x01, 5, 1) : (IsOpl3Plus ? 1 : 0); } + uint32_t timer_a_value() const { return byte(0x02, 0, 8) * 4; } // 8->10 bits + uint32_t timer_b_value() const { return byte(0x03, 0, 8); } + uint32_t status_mask() const { return byte(0x04, 0, 8) & 0x78; } + uint32_t irq_reset() const { return byte(0x04, 7, 1); } + uint32_t reset_timer_b() const { return byte(0x04, 7, 1) | byte(0x04, 5, 1); } + uint32_t reset_timer_a() const { return byte(0x04, 7, 1) | byte(0x04, 6, 1); } + uint32_t enable_timer_b() const { return 1; } + uint32_t enable_timer_a() const { return 1; } + uint32_t load_timer_b() const { return byte(0x04, 1, 1); } + uint32_t load_timer_a() const { return byte(0x04, 0, 1); } + uint32_t csm() const { return IsOpl3Plus ? 0 : byte(0x08, 7, 1); } + uint32_t note_select() const { return byte(0x08, 6, 1); } + uint32_t lfo_am_depth() const { return byte(0xbd, 7, 1); } + uint32_t lfo_pm_depth() const { return byte(0xbd, 6, 1); } + uint32_t rhythm_enable() const { return byte(0xbd, 5, 1); } + uint32_t rhythm_keyon() const { return byte(0xbd, 4, 0); } + uint32_t newflag() const { return IsOpl3Plus ? byte(0x105, 0, 1) : 0; } + uint32_t new2flag() const { return IsOpl4Plus ? byte(0x105, 1, 1) : 0; } + uint32_t fourop_enable() const { return IsOpl3Plus ? byte(0x104, 0, 6) : 0; } + + // per-channel registers + uint32_t ch_block_freq(uint32_t choffs) const { return word(0xb0, 0, 5, 0xa0, 0, 8, choffs); } + uint32_t ch_feedback(uint32_t choffs) const { return byte(0xc0, 1, 3, choffs); } + uint32_t ch_algorithm(uint32_t choffs) const { return byte(0xc0, 0, 1, choffs) | (IsOpl3Plus ? (8 | (byte(0xc3, 0, 1, choffs) << 1)) : 0); } + uint32_t ch_output_any(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 4, 4) : 1; } + uint32_t ch_output_0(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 4, 1) : 1; } + uint32_t ch_output_1(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 5, 1) : (IsOpl3Plus ? 1 : 0); } + uint32_t ch_output_2(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 6, 1) : 0; } + uint32_t ch_output_3(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 7, 1) : 0; } + + // per-operator registers + uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0x20, 7, 1, opoffs); } + uint32_t op_lfo_pm_enable(uint32_t opoffs) const { return byte(0x20, 6, 1, opoffs); } + uint32_t op_eg_sustain(uint32_t opoffs) const { return byte(0x20, 5, 1, opoffs); } + uint32_t op_ksr(uint32_t opoffs) const { return byte(0x20, 4, 1, opoffs); } + uint32_t op_multiple(uint32_t opoffs) const { return byte(0x20, 0, 4, opoffs); } + uint32_t op_ksl(uint32_t opoffs) const { uint32_t temp = byte(0x40, 6, 2, opoffs); return bitfield(temp, 1) | (bitfield(temp, 0) << 1); } + uint32_t op_total_level(uint32_t opoffs) const { return byte(0x40, 0, 6, opoffs); } + uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x60, 4, 4, opoffs); } + uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0x60, 0, 4, opoffs); } + uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0x80, 4, 4, opoffs); } + uint32_t op_release_rate(uint32_t opoffs) const { return byte(0x80, 0, 4, opoffs); } + uint32_t op_waveform(uint32_t opoffs) const { return IsOpl2Plus ? byte(0xe0, 0, newflag() ? 3 : 2, opoffs) : 0; } + +protected: + // return a bitfield extracted from a byte + uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const + { + return bitfield(m_regdata[offset + extra_offset], start, count); + } + + // return a bitfield extracted from a pair of bytes, MSBs listed first + uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } + + // helper to determine if the this channel is an active rhythm channel + bool is_rhythm(uint32_t choffs) const + { + return rhythm_enable() && (choffs >= 6 && choffs <= 8); + } + + // internal state + uint16_t m_lfo_am_counter; // LFO AM counter + uint16_t m_lfo_pm_counter; // LFO PM counter + uint32_t m_noise_lfsr; // noise LFSR state + uint8_t m_lfo_am; // current LFO AM value + uint8_t m_regdata[REGISTERS]; // register data + uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms +}; + +using opl_registers = opl_registers_base<1>; +using opl2_registers = opl_registers_base<2>; +using opl3_registers = opl_registers_base<3>; +using opl4_registers = opl_registers_base<4>; + + + +// ======================> opll_registers + +// +// OPLL register map: +// +// System-wide registers: +// 0E --x----- Rhythm enable +// ---x---- Bass drum key on +// ----x--- Snare drum key on +// -----x-- Tom key on +// ------x- Top cymbal key on +// -------x High hat key on +// 0F xxxxxxxx Test register +// +// Per-channel registers (channel in address bits 0-3) +// 10-18 xxxxxxxx F-number (low 8 bits) +// 20-28 --x----- Sustain on +// ---x---- Key on +// --- xxx- Block (octvate, 0-7) +// -------x F-number (high bit) +// 30-38 xxxx---- Instrument selection +// ----xxxx Volume +// +// User instrument registers (for carrier, modulator operators) +// 00-01 x------- AM enable +// -x------ PM enable (VIB) +// --x----- EG type +// ---x---- Key scale rate +// ----xxxx Multiple value (0-15) +// 02 xx------ Key scale level (carrier, 0-3) +// --xxxxxx Total level (modulator, 0-63) +// 03 xx------ Key scale level (modulator, 0-3) +// ---x---- Rectified wave (carrier) +// ----x--- Rectified wave (modulator) +// -----xxx Feedback level for operator 1 (0-7) +// 04-05 xxxx---- Attack rate (0-15) +// ----xxxx Decay rate (0-15) +// 06-07 xxxx---- Sustain level (0-15) +// ----xxxx Release rate (0-15) +// +// Internal (fake) registers: +// 40-48 xxxxxxxx Current instrument base address +// 4E-5F xxxxxxxx Current instrument base address + operator slot (0/1) +// 70-FF xxxxxxxx Data for instruments (1-16 plus 3 drums) +// + +class opll_registers : public fm_registers_base +{ +public: + static constexpr uint32_t OUTPUTS = 2; + static constexpr uint32_t CHANNELS = 9; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr uint32_t OPERATORS = CHANNELS * 2; + static constexpr uint32_t WAVEFORMS = 2; + static constexpr uint32_t REGISTERS = 0x40; + static constexpr uint32_t REG_MODE = 0x3f; + static constexpr uint32_t DEFAULT_PRESCALE = 4; + static constexpr uint32_t EG_CLOCK_DIVIDER = 1; + static constexpr uint32_t CSM_TRIGGER_MASK = 0; + static constexpr bool EG_HAS_DEPRESS = true; + static constexpr bool MODULATOR_DELAY = true; + static constexpr uint8_t STATUS_TIMERA = 0; + static constexpr uint8_t STATUS_TIMERB = 0; + static constexpr uint8_t STATUS_BUSY = 0; + static constexpr uint8_t STATUS_IRQ = 0; + + // OPLL-specific constants + static constexpr uint32_t INSTDATA_SIZE = 0x90; + + // constructor + opll_registers(); + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // map channel number to register offset + static constexpr uint32_t channel_offset(uint32_t chnum) + { + assert(chnum < CHANNELS); + return chnum; + } + + // map operator number to register offset + static constexpr uint32_t operator_offset(uint32_t opnum) + { + assert(opnum < OPERATORS); + return opnum; + } + + // return an array of operator indices for each channel + struct operator_mapping { uint32_t chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // read a register value + uint8_t read(uint16_t index) const { return m_regdata[index]; } + + // handle writes to the register array + bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + int32_t clock_noise_and_lfo(); + + // reset the LFO + void reset_lfo() { m_lfo_am_counter = m_lfo_pm_counter = 0; } + + // return the AM offset from LFO for the given channel + // on OPL this is just a fixed value + uint32_t lfo_am_offset(uint32_t choffs) const { return m_lfo_am; } + + // return LFO/noise states + uint32_t noise_state() const { return m_noise_lfsr >> 23; } + + // caching helpers + void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache); + + // compute the phase step, given a PM value + uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm); + + // log a key-on event + std::string log_keyon(uint32_t choffs, uint32_t opoffs); + + // set the instrument data + void set_instrument_data(uint8_t const *data) + { + std::copy_n(data, INSTDATA_SIZE, &m_instdata[0]); + } + + // system-wide registers + uint32_t rhythm_enable() const { return byte(0x0e, 5, 1); } + uint32_t rhythm_keyon() const { return byte(0x0e, 4, 0); } + uint32_t test() const { return byte(0x0f, 0, 8); } + uint32_t waveform_enable() const { return 1; } + uint32_t timer_a_value() const { return 0; } + uint32_t timer_b_value() const { return 0; } + uint32_t status_mask() const { return 0; } + uint32_t irq_reset() const { return 0; } + uint32_t reset_timer_b() const { return 0; } + uint32_t reset_timer_a() const { return 0; } + uint32_t enable_timer_b() const { return 0; } + uint32_t enable_timer_a() const { return 0; } + uint32_t load_timer_b() const { return 0; } + uint32_t load_timer_a() const { return 0; } + uint32_t csm() const { return 0; } + + // per-channel registers + uint32_t ch_block_freq(uint32_t choffs) const { return word(0x20, 0, 4, 0x10, 0, 8, choffs); } + uint32_t ch_sustain(uint32_t choffs) const { return byte(0x20, 5, 1, choffs); } + uint32_t ch_total_level(uint32_t choffs) const { return instchbyte(0x02, 0, 6, choffs); } + uint32_t ch_feedback(uint32_t choffs) const { return instchbyte(0x03, 0, 3, choffs); } + uint32_t ch_algorithm(uint32_t choffs) const { return 0; } + uint32_t ch_instrument(uint32_t choffs) const { return byte(0x30, 4, 4, choffs); } + uint32_t ch_output_any(uint32_t choffs) const { return 1; } + uint32_t ch_output_0(uint32_t choffs) const { return !is_rhythm(choffs); } + uint32_t ch_output_1(uint32_t choffs) const { return is_rhythm(choffs); } + uint32_t ch_output_2(uint32_t choffs) const { return 0; } + uint32_t ch_output_3(uint32_t choffs) const { return 0; } + + // per-operator registers + uint32_t op_lfo_am_enable(uint32_t opoffs) const { return instopbyte(0x00, 7, 1, opoffs); } + uint32_t op_lfo_pm_enable(uint32_t opoffs) const { return instopbyte(0x00, 6, 1, opoffs); } + uint32_t op_eg_sustain(uint32_t opoffs) const { return instopbyte(0x00, 5, 1, opoffs); } + uint32_t op_ksr(uint32_t opoffs) const { return instopbyte(0x00, 4, 1, opoffs); } + uint32_t op_multiple(uint32_t opoffs) const { return instopbyte(0x00, 0, 4, opoffs); } + uint32_t op_ksl(uint32_t opoffs) const { return instopbyte(0x02, 6, 2, opoffs); } + uint32_t op_waveform(uint32_t opoffs) const { return instchbyte(0x03, 3 + bitfield(opoffs, 0), 1, opoffs >> 1); } + uint32_t op_attack_rate(uint32_t opoffs) const { return instopbyte(0x04, 4, 4, opoffs); } + uint32_t op_decay_rate(uint32_t opoffs) const { return instopbyte(0x04, 0, 4, opoffs); } + uint32_t op_sustain_level(uint32_t opoffs) const { return instopbyte(0x06, 4, 4, opoffs); } + uint32_t op_release_rate(uint32_t opoffs) const { return instopbyte(0x06, 0, 4, opoffs); } + uint32_t op_volume(uint32_t opoffs) const { return byte(0x30, 4 * bitfield(~opoffs, 0), 4, opoffs >> 1); } + +private: + // return a bitfield extracted from a byte + uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const + { + return bitfield(m_regdata[offset + extra_offset], start, count); + } + + // return a bitfield extracted from a pair of bytes, MSBs listed first + uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } + + // helpers to read from instrument channel/operator data + uint32_t instchbyte(uint32_t offset, uint32_t start, uint32_t count, uint32_t choffs) const { return bitfield(m_chinst[choffs][offset], start, count); } + uint32_t instopbyte(uint32_t offset, uint32_t start, uint32_t count, uint32_t opoffs) const { return bitfield(m_opinst[opoffs][offset], start, count); } + + // helper to determine if the this channel is an active rhythm channel + bool is_rhythm(uint32_t choffs) const + { + return rhythm_enable() && choffs >= 6; + } + + // internal state + uint16_t m_lfo_am_counter; // LFO AM counter + uint16_t m_lfo_pm_counter; // LFO PM counter + uint32_t m_noise_lfsr; // noise LFSR state + uint8_t m_lfo_am; // current LFO AM value + uint8_t const *m_chinst[CHANNELS]; // pointer to instrument data for each channel + uint8_t const *m_opinst[OPERATORS]; // pointer to instrument data for each operator + uint8_t m_regdata[REGISTERS]; // register data + uint8_t m_instdata[INSTDATA_SIZE]; // instrument data + uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms +}; + + + +//********************************************************* +// OPL IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ym3526 + +class ym3526 +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + // constructor + ym3526(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); +protected: + // internal state + uint8_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + +// ======================> y8950 + +class y8950 +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + static constexpr uint8_t STATUS_ADPCM_B_PLAYING = 0x01; + static constexpr uint8_t STATUS_ADPCM_B_BRDY = 0x08; + static constexpr uint8_t STATUS_ADPCM_B_EOS = 0x10; + static constexpr uint8_t ALL_IRQS = STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_EOS | fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB; + + // constructor + y8950(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + uint8_t m_io_ddr; // data direction register for I/O + fm_engine m_fm; // core FM engine + adpcm_b_engine m_adpcm_b; // ADPCM-B engine +}; + + + +//********************************************************* +// OPL2 IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ym3812 + +class ym3812 +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + // constructor + ym3812(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + + +//********************************************************* +// OPL3 IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ymf262 + +class ymf262 +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + // constructor + ymf262(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint16_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + +// ======================> ymf289b + +class ymf289b +{ + static constexpr uint8_t STATUS_BUSY_FLAGS = 0x05; + +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = 2; + + // constructor + ymf289b(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal helpers + bool ymf289b_mode() { return ((m_fm.regs().read(0x105) & 0x04) != 0); } + + // internal state + uint16_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + + +//********************************************************* +// OPL4 IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ymf278b + +class ymf278b +{ + // Using the nominal datasheet frequency of 33.868MHz, the output of the + // chip will be clock/768 = 44.1kHz. However, the FM engine is clocked + // internally at clock/(19*36), or 49.515kHz, so the FM output needs to + // be downsampled. We treat this as needing to clock the FM engine an + // extra tick every few samples. The exact ratio is 768/(19*36) or + // 768/684 = 192/171. So if we always clock the FM once, we'll have + // 192/171 - 1 = 21/171 left. Thus we count 21 for each sample and when + // it gets above 171, we tick an extra time. + static constexpr uint32_t FM_EXTRA_SAMPLE_THRESH = 171; + static constexpr uint32_t FM_EXTRA_SAMPLE_STEP = 192 - FM_EXTRA_SAMPLE_THRESH; + +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t OUTPUTS = 6; + using output_data = ymfm_output; + + static constexpr uint8_t STATUS_BUSY = 0x01; + static constexpr uint8_t STATUS_LD = 0x02; + + // constructor + ymf278b(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return input_clock / 768; } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data_pcm(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write_address_pcm(uint8_t data); + void write_data_pcm(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint16_t m_address; // address register + uint32_t m_fm_pos; // FM resampling position + uint32_t m_load_remaining; // how many more samples until LD flag clears + bool m_next_status_id; // flag to track which status ID to return + fm_engine m_fm; // core FM engine + pcm_engine m_pcm; // core PCM engine +}; + + + +//********************************************************* +// OPLL IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> opll_base + +class opll_base +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + // constructor + opll_base(ymfm_interface &intf, uint8_t const *data); + + // configuration + void set_instrument_data(uint8_t const *data) { m_fm.regs().set_instrument_data(data); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access -- doesn't really have any, but provide these for consistency + uint8_t read_status() { return 0x00; } + uint8_t read(uint32_t offset) { return 0x00; } + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + +// ======================> ym2413 + +class ym2413 : public opll_base +{ +public: + // constructor + ym2413(ymfm_interface &intf, uint8_t const *instrument_data = nullptr); + +private: + // internal state + static uint8_t const s_default_instruments[]; +}; + + +// ======================> ym2413 + +class ym2423 : public opll_base +{ +public: + // constructor + ym2423(ymfm_interface &intf, uint8_t const *instrument_data = nullptr); + +private: + // internal state + static uint8_t const s_default_instruments[]; +}; + + +// ======================> ymf281 + +class ymf281 : public opll_base +{ +public: + // constructor + ymf281(ymfm_interface &intf, uint8_t const *instrument_data = nullptr); + +private: + // internal state + static uint8_t const s_default_instruments[]; +}; + + +// ======================> ds1001 + +class ds1001 : public opll_base +{ +public: + // constructor + ds1001(ymfm_interface &intf, uint8_t const *instrument_data = nullptr); + +private: + // internal state + static uint8_t const s_default_instruments[]; +}; + +} + +#endif // YMFM_OPL_H diff --git a/src/engine/platform/sound/ymfm/ymfm_pcm.cpp b/src/engine/platform/sound/ymfm/ymfm_pcm.cpp new file mode 100644 index 000000000..34417490c --- /dev/null +++ b/src/engine/platform/sound/ymfm/ymfm_pcm.cpp @@ -0,0 +1,714 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "ymfm_pcm.h" +#include "ymfm_fm.h" +#include "ymfm_fm.ipp" + +namespace ymfm +{ + +//********************************************************* +// PCM REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void pcm_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + m_regdata[0xf8] = 0x1b; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// cache_channel_data - update the cache with +// data from the registers +//------------------------------------------------- + +void pcm_registers::cache_channel_data(uint32_t choffs, pcm_cache &cache) +{ + // compute step from octave and fnumber; the math here implies + // a .18 fraction but .16 should be perfectly fine + int32_t octave = int8_t(ch_octave(choffs) << 4) >> 4; + uint32_t fnum = ch_fnumber(choffs); + cache.step = ((0x400 | fnum) << (octave + 7)) >> 2; + + // total level is computed as a .10 value for interpolation + cache.total_level = ch_total_level(choffs) << 10; + + // compute panning values in terms of envelope attenuation + int32_t panpot = int8_t(ch_panpot(choffs) << 4) >> 4; + if (panpot >= 0) + { + cache.pan_left = (panpot == 7) ? 0x3ff : 0x20 * panpot; + cache.pan_right = 0; + } + else if (panpot >= -7) + { + cache.pan_left = 0; + cache.pan_right = (panpot == -7) ? 0x3ff : -0x20 * panpot; + } + else + cache.pan_left = cache.pan_right = 0x3ff; + + // determine the LFO stepping value; this how much to add to a running + // x.18 value for the LFO; steps were derived from frequencies in the + // manual and come out very close with these values + static const uint8_t s_lfo_steps[8] = { 1, 12, 19, 25, 31, 35, 37, 42 }; + cache.lfo_step = s_lfo_steps[ch_lfo_speed(choffs)]; + + // AM LFO depth values, derived from the manual; note each has at most + // 2 bits to make the "multiply" easy in hardware + static const uint8_t s_am_depth[8] = { 0, 0x14, 0x20, 0x28, 0x30, 0x40, 0x50, 0x80 }; + cache.am_depth = s_am_depth[ch_am_depth(choffs)]; + + // PM LFO depth values; these are converted from the manual's cents values + // into f-numbers; the computations come out quite cleanly so pretty sure + // these are correct + static const uint8_t s_pm_depth[8] = { 0, 2, 3, 4, 6, 12, 24, 48 }; + cache.pm_depth = s_pm_depth[ch_vibrato(choffs)]; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = ch_sustain_level(choffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // compute the key scaling correction factor; 15 means don't do any correction + int32_t correction = ch_rate_correction(choffs); + if (correction == 15) + correction = 0; + else + correction = (octave + correction) * 2 + bitfield(fnum, 9); + + // compute the envelope generator rates + cache.eg_rate[EG_ATTACK] = effective_rate(ch_attack_rate(choffs), correction); + cache.eg_rate[EG_DECAY] = effective_rate(ch_decay_rate(choffs), correction); + cache.eg_rate[EG_SUSTAIN] = effective_rate(ch_sustain_rate(choffs), correction); + cache.eg_rate[EG_RELEASE] = effective_rate(ch_release_rate(choffs), correction); + cache.eg_rate[EG_REVERB] = 5; + + // if damping is on, override some things; essentially decay at a hardcoded + // rate of 48 until -12db (0x80), then at maximum rate for the rest + if (ch_damp(choffs) != 0) + { + cache.eg_rate[EG_DECAY] = 48; + cache.eg_rate[EG_SUSTAIN] = 63; + cache.eg_rate[EG_RELEASE] = 63; + cache.eg_sustain = 0x80; + } +} + + +//------------------------------------------------- +// effective_rate - return the effective rate, +// clamping and applying corrections as needed +//------------------------------------------------- + +uint32_t pcm_registers::effective_rate(uint32_t raw, uint32_t correction) +{ + // raw rates of 0 and 15 just pin to min/max + if (raw == 0) + return 0; + if (raw == 15) + return 63; + + // otherwise add the correction and clamp to range + return clamp(raw * 4 + correction, 0, 63); +} + + + +//********************************************************* +// PCM CHANNEL +//********************************************************* + +//------------------------------------------------- +// pcm_channel - constructor +//------------------------------------------------- + +pcm_channel::pcm_channel(pcm_engine &owner, uint32_t choffs) : + m_choffs(choffs), + m_baseaddr(0), + m_endpos(0), + m_looppos(0), + m_curpos(0), + m_nextpos(0), + m_lfo_counter(0), + m_eg_state(EG_RELEASE), + m_env_attenuation(0x3ff), + m_total_level(0x7f << 10), + m_format(0), + m_key_state(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +void pcm_channel::reset() +{ + m_baseaddr = 0; + m_endpos = 0; + m_looppos = 0; + m_curpos = 0; + m_nextpos = 0; + m_lfo_counter = 0; + m_eg_state = EG_RELEASE; + m_env_attenuation = 0x3ff; + m_total_level = 0x7f << 10; + m_format = 0; + m_key_state = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_baseaddr); + state.save_restore(m_endpos); + state.save_restore(m_looppos); + state.save_restore(m_curpos); + state.save_restore(m_nextpos); + state.save_restore(m_lfo_counter); + state.save_restore(m_eg_state); + state.save_restore(m_env_attenuation); + state.save_restore(m_total_level); + state.save_restore(m_format); + state.save_restore(m_key_state); +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +bool pcm_channel::prepare() +{ + // cache the data + m_regs.cache_channel_data(m_choffs, m_cache); + + // clock the key state + if ((m_key_state & KEY_PENDING) != 0) + { + uint8_t oldstate = m_key_state; + m_key_state = (m_key_state >> 1) & KEY_ON; + if (((oldstate ^ m_key_state) & KEY_ON) != 0) + { + if ((m_key_state & KEY_ON) != 0) + start_attack(); + else + start_release(); + } + } + + // set the total level directly if not interpolating + if (m_regs.ch_level_direct(m_choffs)) + m_total_level = m_cache.total_level; + + // we're active until we're quiet after the release + return (m_eg_state < EG_RELEASE || m_env_attenuation < EG_QUIET); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void pcm_channel::clock(uint32_t env_counter) +{ + // clock the LFO, which is an x.18 value incremented based on the + // LFO speed value + m_lfo_counter += m_cache.lfo_step; + + // clock the envelope + clock_envelope(env_counter); + + // determine the step after applying vibrato + uint32_t step = m_cache.step; + if (m_cache.pm_depth != 0) + { + // shift the LFO by 1/4 cycle for PM so that it starts at 0 + uint32_t lfo_shifted = m_lfo_counter + (1 << 16); + int32_t lfo_value = bitfield(lfo_shifted, 10, 7); + if (bitfield(lfo_shifted, 17) != 0) + lfo_value ^= 0x7f; + lfo_value -= 0x40; + step += (lfo_value * int32_t(m_cache.pm_depth)) >> 7; + } + + // advance the sample step and loop as needed + m_curpos = m_nextpos; + m_nextpos = m_curpos + step; + if (m_nextpos >= m_endpos) + m_nextpos += m_looppos - m_endpos; + + // interpolate total level if needed + if (m_total_level != m_cache.total_level) + { + // max->min volume takes 156.4ms, or pretty close to 19/1024 per 44.1kHz sample + // min->max volume is half that, so advance by 38/1024 per sample + if (m_total_level < m_cache.total_level) + m_total_level = std::min(m_total_level + 19, m_cache.total_level); + else + m_total_level = std::max(m_total_level - 38, m_cache.total_level); + } +} + + +//------------------------------------------------- +// output - return the computed output value, with +// panning applied +//------------------------------------------------- + +void pcm_channel::output(output_data &output) const +{ + // early out if the envelope is effectively off + uint32_t envelope = m_env_attenuation; + if (envelope > EG_QUIET) + return; + + // add in LFO AM modulation + if (m_cache.am_depth != 0) + { + uint32_t lfo_value = bitfield(m_lfo_counter, 10, 7); + if (bitfield(m_lfo_counter, 17) != 0) + lfo_value ^= 0x7f; + envelope += (lfo_value * m_cache.am_depth) >> 7; + } + + // add in the current interpolated total level value, which is a .10 + // value shifted left by 2 + envelope += m_total_level >> 8; + + // add in panning effect and clamp + uint32_t lenv = std::min(envelope + m_cache.pan_left, 0x3ff); + uint32_t renv = std::min(envelope + m_cache.pan_right, 0x3ff); + + // convert to volume as a .11 fraction + int32_t lvol = attenuation_to_volume(lenv << 2); + int32_t rvol = attenuation_to_volume(renv << 2); + + // fetch current sample and add + int16_t sample = fetch_sample(); + uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2; + output.data[outnum + 0] += (lvol * sample) >> 15; + output.data[outnum + 1] += (rvol * sample) >> 15; +} + + +//------------------------------------------------- +// keyonoff - signal key on/off +//------------------------------------------------- + +void pcm_channel::keyonoff(bool on) +{ + // mark the key state as pending + m_key_state |= KEY_PENDING | (on ? KEY_PENDING_ON : 0); + + // don't log masked channels + if ((m_key_state & (KEY_PENDING_ON | KEY_ON)) == KEY_PENDING_ON && ((debug::GLOBAL_PCM_CHANNEL_MASK >> m_choffs) & 1) != 0) + { + debug::log_keyon("KeyOn PCM-%02d: num=%3d oct=%2d fnum=%03X level=%02X%c ADSR=%X/%X/%X/%X SL=%X", + m_choffs, + m_regs.ch_wave_table_num(m_choffs), + int8_t(m_regs.ch_octave(m_choffs) << 4) >> 4, + m_regs.ch_fnumber(m_choffs), + m_regs.ch_total_level(m_choffs), + m_regs.ch_level_direct(m_choffs) ? '!' : '/', + m_regs.ch_attack_rate(m_choffs), + m_regs.ch_decay_rate(m_choffs), + m_regs.ch_sustain_rate(m_choffs), + m_regs.ch_release_rate(m_choffs), + m_regs.ch_sustain_level(m_choffs)); + + if (m_regs.ch_rate_correction(m_choffs) != 15) + debug::log_keyon(" RC=%X", m_regs.ch_rate_correction(m_choffs)); + + if (m_regs.ch_pseudo_reverb(m_choffs) != 0) + debug::log_keyon(" %s", "REV"); + if (m_regs.ch_damp(m_choffs) != 0) + debug::log_keyon(" %s", "DAMP"); + + if (m_regs.ch_vibrato(m_choffs) != 0 || m_regs.ch_am_depth(m_choffs) != 0) + { + if (m_regs.ch_vibrato(m_choffs) != 0) + debug::log_keyon(" VIB=%d", m_regs.ch_vibrato(m_choffs)); + if (m_regs.ch_am_depth(m_choffs) != 0) + debug::log_keyon(" AM=%d", m_regs.ch_am_depth(m_choffs)); + debug::log_keyon(" LFO=%d", m_regs.ch_lfo_speed(m_choffs)); + } + debug::log_keyon("%s", "\n"); + } +} + + +//------------------------------------------------- +// load_wavetable - load a wavetable by fetching +// its data from external memory +//------------------------------------------------- + +void pcm_channel::load_wavetable() +{ + // determine the address of the wave table header + uint32_t wavnum = m_regs.ch_wave_table_num(m_choffs); + uint32_t wavheader = 12 * wavnum; + + // above 384 it may be in a different bank + if (wavnum >= 384) + { + uint32_t bank = m_regs.wave_table_header(); + if (bank != 0) + wavheader = 512*1024 * bank + (wavnum - 384) * 12; + } + + // fetch the 22-bit base address and 2-bit format + uint8_t byte = read_pcm(wavheader + 0); + m_format = bitfield(byte, 6, 2); + m_baseaddr = bitfield(byte, 0, 6) << 16; + m_baseaddr |= read_pcm(wavheader + 1) << 8; + m_baseaddr |= read_pcm(wavheader + 2) << 0; + + // fetch the 16-bit loop position + m_looppos = read_pcm(wavheader + 3) << 8; + m_looppos |= read_pcm(wavheader + 4); + m_looppos <<= 16; + + // fetch the 16-bit end position, which is stored as a negative value + // for some reason that is unclear + m_endpos = read_pcm(wavheader + 5) << 8; + m_endpos |= read_pcm(wavheader + 6); + m_endpos = -int32_t(m_endpos) << 16; + + // remaining data values set registers + m_owner.write(0x80 + m_choffs, read_pcm(wavheader + 7)); + m_owner.write(0x98 + m_choffs, read_pcm(wavheader + 8)); + m_owner.write(0xb0 + m_choffs, read_pcm(wavheader + 9)); + m_owner.write(0xc8 + m_choffs, read_pcm(wavheader + 10)); + m_owner.write(0xe0 + m_choffs, read_pcm(wavheader + 11)); + + // reset the envelope so we don't continue playing mid-sample from previous key ons + m_env_attenuation = 0x3ff; +} + + +//------------------------------------------------- +// read_pcm - read a byte from the external PCM +// memory interface +//------------------------------------------------- + +uint8_t pcm_channel::read_pcm(uint32_t address) const +{ + return m_owner.intf().ymfm_external_read(ACCESS_PCM, address); +} + + +//------------------------------------------------- +// start_attack - start the attack phase +//------------------------------------------------- + +void pcm_channel::start_attack() +{ + // don't change anything if already in attack state + if (m_eg_state == EG_ATTACK) + return; + m_eg_state = EG_ATTACK; + + // reset the LFO if requested + if (m_regs.ch_lfo_reset(m_choffs)) + m_lfo_counter = 0; + + // if the attack rate == 63 then immediately go to max attenuation + if (m_cache.eg_rate[EG_ATTACK] == 63) + m_env_attenuation = 0; + + // reset the positions + m_curpos = m_nextpos = 0; +} + + +//------------------------------------------------- +// start_release - start the release phase +//------------------------------------------------- + +void pcm_channel::start_release() +{ + // don't change anything if already in release or reverb state + if (m_eg_state >= EG_RELEASE) + return; + m_eg_state = EG_RELEASE; +} + + +//------------------------------------------------- +// clock_envelope - clock the envelope generator +//------------------------------------------------- + +void pcm_channel::clock_envelope(uint32_t env_counter) +{ + // handle attack->decay transitions + if (m_eg_state == EG_ATTACK && m_env_attenuation == 0) + m_eg_state = EG_DECAY; + + // handle decay->sustain transitions + if (m_eg_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain) + m_eg_state = EG_SUSTAIN; + + // fetch the appropriate 6-bit rate value from the cache + uint32_t rate = m_cache.eg_rate[m_eg_state]; + + // compute the rate shift value; this is the shift needed to + // apply to the env_counter such that it becomes a 5.11 fixed + // point number + uint32_t rate_shift = rate >> 2; + env_counter <<= rate_shift; + + // see if the fractional part is 0; if not, it's not time to clock + if (bitfield(env_counter, 0, 11) != 0) + return; + + // determine the increment based on the non-fractional part of env_counter + uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3); + uint32_t increment = attenuation_increment(rate, relevant_bits); + + // attack is the only one that increases + if (m_eg_state == EG_ATTACK) + m_env_attenuation += (~m_env_attenuation * increment) >> 4; + + // all other cases are similar + else + { + // apply the increment + m_env_attenuation += increment; + + // clamp the final attenuation + if (m_env_attenuation >= 0x400) + m_env_attenuation = 0x3ff; + + // transition to reverb at -18dB if enabled + if (m_env_attenuation >= 0xc0 && m_eg_state < EG_REVERB && m_regs.ch_pseudo_reverb(m_choffs)) + m_eg_state = EG_REVERB; + } +} + + +//------------------------------------------------- +// fetch_sample - fetch a sample at the current +// position +//------------------------------------------------- + +int16_t pcm_channel::fetch_sample() const +{ + uint32_t addr = m_baseaddr; + uint32_t pos = m_curpos >> 16; + + // 8-bit PCM: shift up by 8 + if (m_format == 0) + return read_pcm(addr + pos) << 8; + + // 16-bit PCM: assemble from 2 halves + if (m_format == 2) + { + addr += pos * 2; + return (read_pcm(addr) << 8) | read_pcm(addr + 1); + } + + // 12-bit PCM: assemble out of half of 3 bytes + addr += (pos / 2) * 3; + if ((pos & 1) == 0) + return (read_pcm(addr + 0) << 8) | ((read_pcm(addr + 1) << 4) & 0xf0); + else + return (read_pcm(addr + 2) << 8) | ((read_pcm(addr + 1) << 0) & 0xf0); +} + + + +//********************************************************* +// PCM ENGINE +//********************************************************* + +//------------------------------------------------- +// pcm_engine - constructor +//------------------------------------------------- + +pcm_engine::pcm_engine(ymfm_interface &intf) : + m_intf(intf), + m_env_counter(0), + m_modified_channels(ALL_CHANNELS), + m_active_channels(ALL_CHANNELS) +{ + // create the channels + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique(*this, chnum); +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void pcm_engine::reset() +{ + // reset register state + m_regs.reset(); + + // reset each channel + for (auto &chan : m_channel) + chan->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_engine::save_restore(ymfm_saved_state &state) +{ + // save our data + state.save_restore(m_env_counter); + + // save channel state + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum]->save_restore(state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void pcm_engine::clock(uint32_t chanmask) +{ + // if something was modified, prepare + // also prepare every 4k samples to catch ending notes + if (m_modified_channels != 0 || m_prepare_count++ >= 4096) + { + // call each channel to prepare + m_active_channels = 0; + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + if (m_channel[chnum]->prepare()) + m_active_channels |= 1 << chnum; + + // reset the modified channels and prepare count + m_modified_channels = m_prepare_count = 0; + } + + // increment the envelope counter; the envelope generator + // only clocks every other sample in order to make the PCM + // envelopes line up with the FM envelopes (after taking into + // account the different FM sampling rate) + m_env_counter++; + + // now update the state of all the channels and operators + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->clock(m_env_counter >> 1); +} + + +//------------------------------------------------- +// update - master update function +//------------------------------------------------- + +void pcm_engine::output(output_data &output, uint32_t chanmask) +{ + // mask out some channels for debug purposes + chanmask &= debug::GLOBAL_PCM_CHANNEL_MASK; + + // compute the output of each channel + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->output(output); +} + + +//------------------------------------------------- +// read - handle reads from the PCM registers +//------------------------------------------------- + +uint8_t pcm_engine::read(uint32_t regnum) +{ + // handle reads from the data register + if (regnum == 0x06 && m_regs.memory_access_mode() != 0) + return m_intf.ymfm_external_read(ACCESS_PCM, m_regs.memory_address_autoinc()); + + return m_regs.read(regnum); +} + + +//------------------------------------------------- +// write - handle writes to the PCM registers +//------------------------------------------------- + +void pcm_engine::write(uint32_t regnum, uint8_t data) +{ + // handle reads to the data register + if (regnum == 0x06 && m_regs.memory_access_mode() != 0) + { + m_intf.ymfm_external_write(ACCESS_PCM, m_regs.memory_address_autoinc(), data); + return; + } + + // for now just mark all channels as modified + m_modified_channels = ALL_CHANNELS; + + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // however, process keyons immediately + if (regnum >= 0x68 && regnum <= 0x7f) + m_channel[regnum - 0x68]->keyonoff(bitfield(data, 7)); + + // and also wavetable writes + else if (regnum >= 0x08 && regnum <= 0x1f) + m_channel[regnum - 0x08]->load_wavetable(); +} + +} diff --git a/src/engine/platform/sound/ymfm/ymfm_pcm.h b/src/engine/platform/sound/ymfm/ymfm_pcm.h new file mode 100644 index 000000000..b471fa611 --- /dev/null +++ b/src/engine/platform/sound/ymfm/ymfm_pcm.h @@ -0,0 +1,347 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef YMFM_PCM_H +#define YMFM_PCM_H + +#pragma once + +#include "ymfm.h" + +namespace ymfm +{ + +/* +Note to self: Sega "Multi-PCM" is almost identical to this + +28 channels + +Writes: +00 = data reg, causes write +01 = target slot = data - (data / 8) +02 = address (clamped to 7) + +Slot data (registers with ADSR/KSR seem to be inaccessible): +0: xxxx---- panpot +1: xxxxxxxx wavetable low +2: xxxxxx-- pitch low + -------x wavetable high +3: xxxx---- octave + ----xxxx pitch hi +4: x------- key on +5: xxxxxxx- total level + -------x level direct (0=interpolate) +6: --xxx--- LFO frequency + -----xxx PM sensitivity +7: -----xxx AM sensitivity + +Sample data: ++00: start hi ++01: start mid ++02: start low ++03: loop hi ++04: loop low ++05: -end hi ++06: -end low ++07: vibrato (reg 6) ++08: attack/decay ++09: sustain level/rate ++0A: ksr/release ++0B: LFO amplitude (reg 7) + +*/ + +//********************************************************* +// INTERFACE CLASSES +//********************************************************* + +class pcm_engine; + + +// ======================> pcm_cache + +// this class holds data that is computed once at the start of clocking +// and remains static during subsequent sound generation +struct pcm_cache +{ + uint32_t step; // sample position step, as a .16 value + uint32_t total_level; // target total level, as a .10 value + uint32_t pan_left; // left panning attenuation + uint32_t pan_right; // right panning attenuation + uint32_t eg_sustain; // sustain level, shifted up to envelope values + uint8_t eg_rate[EG_STATES]; // envelope rate, including KSR + uint8_t lfo_step; // stepping value for LFO + uint8_t am_depth; // scale value for AM LFO + uint8_t pm_depth; // scale value for PM LFO +}; + + +// ======================> pcm_registers + +// +// PCM register map: +// +// System-wide registers: +// 00-01 xxxxxxxx LSI Test +// 02 -------x Memory access mode (0=sound gen, 1=read/write) +// ------x- Memory type (0=ROM, 1=ROM+SRAM) +// ---xxx-- Wave table header +// xxx----- Device ID (=1 for YMF278B) +// 03 --xxxxxx Memory address high +// 04 xxxxxxxx Memory address mid +// 05 xxxxxxxx Memory address low +// 06 xxxxxxxx Memory data +// F8 --xxx--- Mix control (FM_R) +// -----xxx Mix control (FM_L) +// F9 --xxx--- Mix control (PCM_R) +// -----xxx Mix control (PCM_L) +// +// Channel-specific registers: +// 08-1F xxxxxxxx Wave table number low +// 20-37 -------x Wave table number high +// xxxxxxx- F-number low +// 38-4F -----xxx F-number high +// ----x--- Pseudo-reverb +// xxxx---- Octave +// 50-67 xxxxxxx- Total level +// -------x Level direct +// 68-7F x------- Key on +// -x------ Damp +// --x----- LFO reset +// ---x---- Output channel +// ----xxxx Panpot +// 80-97 --xxx--- LFO speed +// -----xxx Vibrato +// 98-AF xxxx---- Attack rate +// ----xxxx Decay rate +// B0-C7 xxxx---- Sustain level +// ----xxxx Sustain rate +// C8-DF xxxx---- Rate correction +// ----xxxx Release rate +// E0-F7 -----xxx AM depth + +class pcm_registers +{ +public: + // constants + static constexpr uint32_t OUTPUTS = 4; + static constexpr uint32_t CHANNELS = 24; + static constexpr uint32_t REGISTERS = 0x100; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + + // constructor + pcm_registers() { } + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset to initial state + void reset(); + + // update cache information + void cache_channel_data(uint32_t choffs, pcm_cache &cache); + + // direct read/write access + uint8_t read(uint32_t index ) { return m_regdata[index]; } + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t memory_access_mode() const { return bitfield(m_regdata[0x02], 0); } + uint32_t memory_type() const { return bitfield(m_regdata[0x02], 1); } + uint32_t wave_table_header() const { return bitfield(m_regdata[0x02], 2, 3); } + uint32_t device_id() const { return bitfield(m_regdata[0x02], 5, 3); } + uint32_t memory_address() const { return (bitfield(m_regdata[0x03], 0, 6) << 16) | (m_regdata[0x04] << 8) | m_regdata[0x05]; } + uint32_t memory_data() const { return m_regdata[0x06]; } + uint32_t mix_fm_r() const { return bitfield(m_regdata[0xf8], 3, 3); } + uint32_t mix_fm_l() const { return bitfield(m_regdata[0xf8], 0, 3); } + uint32_t mix_pcm_r() const { return bitfield(m_regdata[0xf9], 3, 3); } + uint32_t mix_pcm_l() const { return bitfield(m_regdata[0xf9], 0, 3); } + + // per-channel registers + uint32_t ch_wave_table_num(uint32_t choffs) const { return m_regdata[choffs + 0x08] | (bitfield(m_regdata[choffs + 0x20], 0) << 8); } + uint32_t ch_fnumber(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x20], 1, 7) | (bitfield(m_regdata[choffs + 0x38], 0, 3) << 7); } + uint32_t ch_pseudo_reverb(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 3); } + uint32_t ch_octave(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 4, 4); } + uint32_t ch_total_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 1, 7); } + uint32_t ch_level_direct(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 0); } + uint32_t ch_keyon(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 7); } + uint32_t ch_damp(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 6); } + uint32_t ch_lfo_reset(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 5); } + uint32_t ch_output_channel(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 4); } + uint32_t ch_panpot(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 0, 4); } + uint32_t ch_lfo_speed(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 3, 3); } + uint32_t ch_vibrato(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 0, 3); } + uint32_t ch_attack_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 4, 4); } + uint32_t ch_decay_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 0, 4); } + uint32_t ch_sustain_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 4, 4); } + uint32_t ch_sustain_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 0, 4); } + uint32_t ch_rate_correction(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 4, 4); } + uint32_t ch_release_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 0, 4); } + uint32_t ch_am_depth(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xe0], 0, 3); } + + // return the memory address and increment it + uint32_t memory_address_autoinc() + { + uint32_t result = memory_address(); + uint32_t newval = result + 1; + m_regdata[0x05] = newval >> 0; + m_regdata[0x04] = newval >> 8; + m_regdata[0x03] = (newval >> 16) & 0x3f; + return result; + } + +private: + // internal helpers + uint32_t effective_rate(uint32_t raw, uint32_t correction); + + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> pcm_channel + +class pcm_channel +{ + static constexpr uint8_t KEY_ON = 0x01; + static constexpr uint8_t KEY_PENDING_ON = 0x02; + static constexpr uint8_t KEY_PENDING = 0x04; + + // "quiet" value, used to optimize when we can skip doing working + static constexpr uint32_t EG_QUIET = 0x200; + +public: + using output_data = ymfm_output; + + // constructor + pcm_channel(pcm_engine &owner, uint32_t choffs); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the channel state + void reset(); + + // return the channel offset + uint32_t choffs() const { return m_choffs; } + + // prepare prior to clocking + bool prepare(); + + // master clocking function + void clock(uint32_t env_counter); + + // return the computed output value, with panning applied + void output(output_data &output) const; + + // signal key on/off + void keyonoff(bool on); + + // load a new wavetable entry + void load_wavetable(); + +private: + // internal helpers + void start_attack(); + void start_release(); + void clock_envelope(uint32_t env_counter); + int16_t fetch_sample() const; + uint8_t read_pcm(uint32_t address) const; + + // internal state + uint32_t const m_choffs; // channel offset + uint32_t m_baseaddr; // base address + uint32_t m_endpos; // ending position + uint32_t m_looppos; // loop position + uint32_t m_curpos; // current position + uint32_t m_nextpos; // next position + uint32_t m_lfo_counter; // LFO counter + envelope_state m_eg_state; // envelope state + uint16_t m_env_attenuation; // computed envelope attenuation + uint32_t m_total_level; // total level with as 7.10 for interp + uint8_t m_format; // sample format + uint8_t m_key_state; // current key state + pcm_cache m_cache; // cached data + pcm_registers &m_regs; // reference to registers + pcm_engine &m_owner; // reference to our owner +}; + + +// ======================> pcm_engine + +class pcm_engine +{ +public: + static constexpr int OUTPUTS = pcm_registers::OUTPUTS; + static constexpr int CHANNELS = pcm_registers::CHANNELS; + static constexpr uint32_t ALL_CHANNELS = pcm_registers::ALL_CHANNELS; + using output_data = pcm_channel::output_data; + + // constructor + pcm_engine(ymfm_interface &intf); + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + void clock(uint32_t chanmask); + + // compute sum of channel outputs + void output(output_data &output, uint32_t chanmask); + + // read from the PCM registers + uint8_t read(uint32_t regnum); + + // write to the PCM registers + void write(uint32_t regnum, uint8_t data); + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + pcm_registers ®s() { return m_regs; } + +private: + // internal state + ymfm_interface &m_intf; // reference to the interface + uint32_t m_env_counter; // envelope counter + uint32_t m_modified_channels; // bitmask of modified channels + uint32_t m_active_channels; // bitmask of active channels + uint32_t m_prepare_count; // counter to do periodic prepare sweeps + std::unique_ptr m_channel[CHANNELS]; // array of channels + pcm_registers m_regs; // registers +}; + +} + +#endif // YMFM_PCM_H From 955682b240245ad98cfa3d69dbf6aa2cae796bf7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 22 Nov 2023 19:28:36 -0500 Subject: [PATCH 05/53] OPL: ymfm core, part 1 --- CMakeLists.txt | 2 + src/engine/dispatchContainer.cpp | 40 +++ src/engine/platform/opl.cpp | 338 +++++++++++++++++++-- src/engine/platform/opl.h | 32 +- src/engine/platform/sound/ymfm/ymfm_fm.h | 4 + src/engine/platform/sound/ymfm/ymfm_fm.ipp | 12 +- src/engine/platform/sound/ymfm/ymfm_opl.h | 9 + src/gui/gui.h | 8 + src/gui/settings.cpp | 51 ++++ 9 files changed, 467 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0bbe826c..20e6cf507 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,6 +489,8 @@ extern/Nuked-PSG/ympsg.c extern/opm/opm.c extern/Nuked-OPLL/opll.c extern/opl/opl3.c +extern/YM3812-LLE/fmopl2.c +extern/YMF262-LLE/fmopl3.c src/pch.cpp diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 4148c2a3e..878232ca0 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -427,34 +427,74 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_OPL: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(1,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(1,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL2: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(2,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL2_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(2,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL3: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(3,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3Core",0)); + } break; case DIV_SYSTEM_OPL3_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(3,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3Core",0)); + } break; case DIV_SYSTEM_Y8950: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(8950,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_Y8950_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(8950,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPZ: dispatch=new DivPlatformTX81Z; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 52968a1a5..f505b7d8d 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -292,12 +292,252 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) { } } +void DivPlatformOPL::acquire_ymfm1(short** buf, size_t len) { + ymfm::ymfm_output<1> out; + + ymfm::ym3526::fm_engine* fme=fm_ymfm1->debug_fm_engine(); + ymfm::fm_channel>* fmChan[9]; + + for (int i=0; i<9; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (size_t h=0; hwrite(0,w.addr); + fm_ymfm1->write(1,w.val); + + regPool[w.addr&511]=w.val; + writes.pop(); + } + + fm_ymfm1->generate(&out,1); + + buf[0][h]=out.data[0]; + + if (properDrums) { + for (int i=0; i<7; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + oscBuf[7]->data[oscBuf[7]->needle++]=CLAMP(fmChan[7]->debug_special1()<<2,-32768,32767); + oscBuf[8]->data[oscBuf[8]->needle++]=CLAMP(fmChan[8]->debug_special1()<<2,-32768,32767); + oscBuf[9]->data[oscBuf[9]->needle++]=CLAMP(fmChan[8]->debug_special2()<<2,-32768,32767); + oscBuf[10]->data[oscBuf[10]->needle++]=CLAMP(fmChan[7]->debug_special2()<<2,-32768,32767); + } else { + for (int i=0; i<9; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + } + } +} + +void DivPlatformOPL::acquire_ymfm2(short** buf, size_t len) { + ymfm::ymfm_output<1> out; + + ymfm::ym3812::fm_engine* fme=fm_ymfm2->debug_fm_engine(); + ymfm::fm_channel>* fmChan[9]; + + for (int i=0; i<9; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (size_t h=0; hwrite(0,w.addr); + fm_ymfm2->write(1,w.val); + + regPool[w.addr&511]=w.val; + writes.pop(); + } + + fm_ymfm2->generate(&out,1); + + buf[0][h]=out.data[0]; + + if (properDrums) { + for (int i=0; i<7; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + oscBuf[7]->data[oscBuf[7]->needle++]=CLAMP(fmChan[7]->debug_special1()<<2,-32768,32767); + oscBuf[8]->data[oscBuf[8]->needle++]=CLAMP(fmChan[8]->debug_special1()<<2,-32768,32767); + oscBuf[9]->data[oscBuf[9]->needle++]=CLAMP(fmChan[8]->debug_special2()<<2,-32768,32767); + oscBuf[10]->data[oscBuf[10]->needle++]=CLAMP(fmChan[7]->debug_special2()<<2,-32768,32767); + } else { + for (int i=0; i<9; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + } + } +} + +// TODO: ADPCM +void DivPlatformOPL::acquire_ymfm8950(short** buf, size_t len) { + ymfm::ymfm_output<1> out; + + ymfm::y8950::fm_engine* fme=fm_ymfm8950->debug_fm_engine(); + ymfm::fm_channel>* fmChan[9]; + + for (int i=0; i<9; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (size_t h=0; hwrite(0,w.addr); + fm_ymfm8950->write(1,w.val); + + regPool[w.addr&511]=w.val; + writes.pop(); + } + + fm_ymfm8950->generate(&out,1); + + buf[0][h]=out.data[0]; + + if (properDrums) { + for (int i=0; i<7; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + oscBuf[7]->data[oscBuf[7]->needle++]=CLAMP(fmChan[7]->debug_special1()<<2,-32768,32767); + oscBuf[8]->data[oscBuf[8]->needle++]=CLAMP(fmChan[8]->debug_special1()<<2,-32768,32767); + oscBuf[9]->data[oscBuf[9]->needle++]=CLAMP(fmChan[8]->debug_special2()<<2,-32768,32767); + oscBuf[10]->data[oscBuf[10]->needle++]=CLAMP(fmChan[7]->debug_special2()<<2,-32768,32767); + } else { + for (int i=0; i<9; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); + } + } + } +} + +void DivPlatformOPL::acquire_ymfm3(short** buf, size_t len) { + ymfm::ymfm_output<4> out; + + ymfm::ymf262::fm_engine* fme=fm_ymfm3->debug_fm_engine(); + ymfm::fm_channel>* fmChan[18]; + + for (int i=0; i<18; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (size_t h=0; hwrite((w.addr&0x100)?2:0,w.addr); + fm_ymfm3->write(1,w.val); + + regPool[w.addr&511]=w.val; + writes.pop(); + } + + fm_ymfm3->generate(&out,1); + + buf[0][h]=out.data[0]>>1; + if (totalOutputs>1) { + buf[1][h]=out.data[1]>>1; + } + if (totalOutputs>2) { + buf[2][h]=out.data[2]>>1; + } + if (totalOutputs>3) { + buf[3][h]=out.data[3]>>1; + } + if (totalOutputs==6) { + // placeholder for OPL4 + buf[4][h]=0; + buf[5][h]=0; + } + + // TODO: fix 4-op view + if (properDrums) { + for (int i=0; i<16; i++) { + unsigned char ch=outChanMap[i]; + if (ch==255) continue; + int chOut=fmChan[ch]->debug_output(0); + if (chOut==0) { + chOut=fmChan[ch]->debug_output(1); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(2); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(3); + } + if (i==15) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut,-32768,32767); + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767); + } + } + oscBuf[16]->data[oscBuf[16]->needle++]=CLAMP(fmChan[7]->debug_special2()<<1,-32768,32767); + oscBuf[17]->data[oscBuf[17]->needle++]=CLAMP(fmChan[8]->debug_special1()<<1,-32768,32767); + oscBuf[18]->data[oscBuf[18]->needle++]=CLAMP(fmChan[8]->debug_special2()<<1,-32768,32767); + oscBuf[19]->data[oscBuf[19]->needle++]=CLAMP(fmChan[7]->debug_special1()<<1,-32768,32767); + } else { + for (int i=0; i<18; i++) { + unsigned char ch=outChanMap[i]; + if (ch==255) continue; + int chOut=fmChan[ch]->debug_output(0); + if (chOut==0) { + chOut=fmChan[ch]->debug_output(1); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(2); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(3); + } + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767); + } + } + } +} + +void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { +} + +void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { +} + void DivPlatformOPL::acquire(short** buf, size_t len) { - //if (useYMFM) { - // acquire_ymfm(buf,len); - //} else { + if (emuCore==2) { // LLE + switch (chipType) { + case 1: case 2: case 8950: + acquire_nukedLLE2(buf,len); + break; + case 3: case 759: + acquire_nukedLLE3(buf,len); + break; + } + } else if (emuCore==1) { // ymfm + switch (chipType) { + case 1: + acquire_ymfm1(buf,len); + break; + case 2: + acquire_ymfm2(buf,len); + break; + case 8950: + acquire_ymfm8950(buf,len); + break; + case 3: case 759: + acquire_ymfm3(buf,len); + break; + } + } else { // OPL3 acquire_nuked(buf,len); - //} + } } double DivPlatformOPL::NOTE_ADPCMB(int note) { @@ -1620,17 +1860,34 @@ int DivPlatformOPL::getRegisterPoolSize() { void DivPlatformOPL::reset() { while (!writes.empty()) writes.pop(); memset(regPool,0,512); - /* - if (useYMFM) { - fm_ymfm->reset(); - } - */ - if (downsample) { - const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); - OPL3_Reset(&fm,downsampledRate); + + const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); + + if (emuCore==2) { + // TODO: LLE reset + } else if (emuCore==1) { + switch (chipType) { + case 1: + fm_ymfm1->reset(); + break; + case 2: + fm_ymfm2->reset(); + break; + case 8950: + fm_ymfm8950->reset(); + break; + case 3: case 759: + fm_ymfm3->reset(); + break; + } } else { - OPL3_Reset(&fm,rate); + if (downsample) { + OPL3_Reset(&fm,downsampledRate); + } else { + OPL3_Reset(&fm,rate); + } } + if (dumpWrites) { addWrite(0xffffffff,0); } @@ -1744,8 +2001,8 @@ int DivPlatformOPL::getPortaFloor(int ch) { return (ch>5)?12:0; } -void DivPlatformOPL::setYMFM(bool use) { - useYMFM=use; +void DivPlatformOPL::setCore(unsigned char which) { + emuCore=which; } void DivPlatformOPL::setOPLType(int type, bool drums) { @@ -1891,11 +2148,13 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { totalOutputs=4; break; } - if (downsample) { - const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); - OPL3_Resample(&fm,downsampledRate); - } else { - OPL3_Resample(&fm,rate); + if (emuCore!=1 && emuCore!=2) { + if (downsample) { + const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); + OPL3_Resample(&fm,downsampledRate); + } else { + OPL3_Resample(&fm,rate); + } } break; case 4: @@ -1990,6 +2249,29 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfi for (int i=0; i<20; i++) { oscBuf[i]=new DivDispatchOscBuffer; } + + fm_ymfm1=NULL; + fm_ymfm2=NULL; + fm_ymfm8950=NULL; + fm_ymfm3=NULL; + + if (emuCore==1) { + switch (chipType) { + case 1: + fm_ymfm1=new ymfm::ym3526(iface); + break; + case 2: + fm_ymfm2=new ymfm::ym3812(iface); + break; + case 8950: + fm_ymfm8950=new ymfm::y8950(iface); + break; + case 3: case 759: + fm_ymfm3=new ymfm::ymf262(iface); + break; + } + } + setFlags(flags); if (adpcmChan>=0) { @@ -2012,6 +2294,22 @@ void DivPlatformOPL::quit() { delete adpcmB; delete[] adpcmBMem; } + if (fm_ymfm1!=NULL) { + delete fm_ymfm1; + fm_ymfm1=NULL; + } + if (fm_ymfm2!=NULL) { + delete fm_ymfm2; + fm_ymfm2=NULL; + } + if (fm_ymfm8950!=NULL) { + delete fm_ymfm8950; + fm_ymfm8950=NULL; + } + if (fm_ymfm3!=NULL) { + delete fm_ymfm3; + fm_ymfm3=NULL; + } } DivPlatformOPL::~DivPlatformOPL() { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 54cf0c24d..23fd7b97e 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -23,7 +23,12 @@ #include "../dispatch.h" #include "../../fixedQueue.h" #include "../../../extern/opl/opl3.h" +extern "C" { +#include "../../../extern/YM3812-LLE/fmopl2.h" +#include "../../../extern/YMF262-LLE/fmopl3.h" +} #include "sound/ymfm/ymfm_adpcm.h" +#include "sound/ymfm/ymfm_opl.h" class DivOPLAInterface: public ymfm::ymfm_interface { public: @@ -68,7 +73,7 @@ class DivPlatformOPL: public DivDispatch { QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {} }; FixedQueue writes; - opl3_chip fm; + unsigned char* adpcmBMem; size_t adpcmBMemLen; DivOPLAInterface iface; @@ -93,11 +98,25 @@ class DivPlatformOPL: public DivDispatch { unsigned char lfoValue; - bool useYMFM, update4OpMask, pretendYMU, downsample, compatPan; + // 0: Nuked-OPL3 + // 1: ymfm + // 2: YM3812-LLE/YMF262-LLE + unsigned char emuCore; + + bool update4OpMask, pretendYMU, downsample, compatPan; short oldWrites[512]; short pendingWrites[512]; + // chips + opl3_chip fm; + ymfm::ym3526* fm_ymfm1; + ymfm::ym3812* fm_ymfm2; + ymfm::y8950* fm_ymfm8950; + ymfm::ymf262* fm_ymfm3; + fmopl2_t fm_lle2; + fmopl3_t fm_lle3; + int octave(int freq); int toFreq(int freq); double NOTE_ADPCMB(int note); @@ -106,8 +125,13 @@ class DivPlatformOPL: public DivDispatch { friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); + void acquire_nukedLLE2(short** buf, size_t len); + void acquire_nukedLLE3(short** buf, size_t len); void acquire_nuked(short** buf, size_t len); - //void acquire_ymfm(short** buf, size_t len); + void acquire_ymfm3(short** buf, size_t len); + void acquire_ymfm8950(short** buf, size_t len); + void acquire_ymfm2(short** buf, size_t len); + void acquire_ymfm1(short** buf, size_t len); public: void acquire(short** buf, size_t len); @@ -124,7 +148,7 @@ class DivPlatformOPL: public DivDispatch { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); int getOutputCount(); - void setYMFM(bool use); + void setCore(unsigned char which); void setOPLType(int type, bool drums); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.h b/src/engine/platform/sound/ymfm/ymfm_fm.h index df3b486af..3ac36b50d 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.h +++ b/src/engine/platform/sound/ymfm/ymfm_fm.h @@ -305,6 +305,8 @@ public: // simple getters for debugging fm_operator *debug_operator(uint32_t index) const { return m_op[index]; } int32_t debug_output(uint32_t index) const { return m_output[index]; } + int32_t debug_special1() const { return m_special1; } + int32_t debug_special2() const { return m_special2; } private: // helper to add values to the outputs based on channel enables @@ -343,6 +345,8 @@ private: RegisterType &m_regs; // direct reference to registers fm_engine_base &m_owner; // reference to the owning engine mutable int32_t m_output[4]; + mutable int32_t m_special1; + mutable int32_t m_special2; }; diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.ipp b/src/engine/platform/sound/ymfm/ymfm_fm.ipp index 81b351fe9..7a37ec8a3 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.ipp +++ b/src/engine/platform/sound/ymfm/ymfm_fm.ipp @@ -808,7 +808,9 @@ fm_channel::fm_channel(fm_engine_base &owner, uint32 m_op{ nullptr, nullptr, nullptr, nullptr }, m_regs(owner.regs()), m_owner(owner), - m_output{ 0, 0, 0, 0 } + m_output{ 0, 0, 0, 0 }, + m_special1(0), + m_special2(0) { } @@ -1139,13 +1141,13 @@ void fm_channel::output_rhythm_ch7(uint32_t phase_select, output_d // and a combination of noise and the operator 13/17 phase select // to compute the phase uint32_t phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select))); - int32_t result = m_op[0]->compute_volume(phase, am_offset) >> rshift; + int32_t result = m_special1 = m_op[0]->compute_volume(phase, am_offset) >> rshift; // Snare Drum: this uses the envelope from operator 16 (channel 7), // and a combination of noise and operator 13 phase to pick a phase uint32_t op13phase = m_op[0]->phase(); phase = (0x100 << bitfield(op13phase, 8)) ^ (noise_state << 8); - result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result += m_special2 = m_op[1]->compute_volume(phase, am_offset) >> rshift; result = clamp(result, -clipmax - 1, clipmax); // add to the output @@ -1166,12 +1168,12 @@ void fm_channel::output_rhythm_ch8(uint32_t phase_select, output_d uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); // Tom Tom: this is just a single operator processed normally - int32_t result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift; + int32_t result = m_special1 = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift; // Top Cymbal: this uses the envelope from operator 17 (channel 8), // and the operator 13/17 phase select to compute the phase uint32_t phase = 0x100 | (phase_select << 9); - result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result += m_special2 = m_op[1]->compute_volume(phase, am_offset) >> rshift; result = clamp(result, -clipmax - 1, clipmax); // add to the output diff --git a/src/engine/platform/sound/ymfm/ymfm_opl.h b/src/engine/platform/sound/ymfm/ymfm_opl.h index 843e5b274..8a2dd5147 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opl.h +++ b/src/engine/platform/sound/ymfm/ymfm_opl.h @@ -528,6 +528,8 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + + fm_engine* debug_fm_engine() { return &m_fm; } protected: // internal state uint8_t m_address; // address register @@ -575,6 +577,9 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + fm_engine* debug_fm_engine() { return &m_fm; } + adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; } + protected: // internal state uint8_t m_address; // address register @@ -623,6 +628,8 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + fm_engine* debug_fm_engine() { return &m_fm; } + protected: // internal state uint8_t m_address; // address register @@ -670,6 +677,8 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + fm_engine* debug_fm_engine() { return &m_fm; } + protected: // internal state uint16_t m_address; // address register diff --git a/src/gui/gui.h b/src/gui/gui.h index 39a412e9b..10bcd2c66 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1476,6 +1476,8 @@ class FurnaceGUI { int c64Core; int pokeyCore; int opnCore; + int opl2Core; + int opl3Core; int arcadeCoreRender; int ym2612CoreRender; int snCoreRender; @@ -1484,6 +1486,8 @@ class FurnaceGUI { int c64CoreRender; int pokeyCoreRender; int opnCoreRender; + int opl2CoreRender; + int opl3CoreRender; int pcSpeakerOutMethod; String yrw801Path; String tg100Path; @@ -1669,6 +1673,8 @@ class FurnaceGUI { c64Core(0), pokeyCore(1), opnCore(1), + opl2Core(0), + opl3Core(0), arcadeCoreRender(1), ym2612CoreRender(0), snCoreRender(0), @@ -1677,6 +1683,8 @@ class FurnaceGUI { c64CoreRender(1), pokeyCoreRender(1), opnCoreRender(1), + opl2CoreRender(0), + opl3CoreRender(0), pcSpeakerOutMethod(0), yrw801Path(""), tg100Path(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 86be7e547..c7ff6b29b 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -156,6 +156,18 @@ const char* opnCores[]={ "Nuked-OPN2 (FM) + ymfm (SSG/ADPCM)" }; +const char* opl2Cores[]={ + "Nuked-OPL3", + "ymfm", + "YM3812-LLE" +}; + +const char* opl3Cores[]={ + "Nuked-OPL3", + "ymfm", + "YMF262-LLE" +}; + const char* pcspkrOutMethods[]={ "evdev SND_TONE", "KIOCSOUND on /dev/tty1", @@ -1557,6 +1569,29 @@ void FurnaceGUI::drawSettings() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::Combo("##OPNCoreRender",&settings.opnCoreRender,opnCores,2)) settingsChanged=true; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("OPL/OPL2/Y8950"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##OPL2Core",&settings.opl2Core,opl2Cores,3)) settingsChanged=true; + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##OPL2CoreRender",&settings.opl2CoreRender,opl2Cores,3)) settingsChanged=true; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("OPL3"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##OPL3Core",&settings.opl3Core,opl3Cores,3)) settingsChanged=true; + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Combo("##OPL3CoreRender",&settings.opl3CoreRender,opl3Cores,3)) settingsChanged=true; + ImGui::EndTable(); } ImGui::Separator(); @@ -3594,6 +3629,8 @@ void FurnaceGUI::syncSettings() { settings.c64Core=e->getConfInt("c64Core",0); settings.pokeyCore=e->getConfInt("pokeyCore",1); settings.opnCore=e->getConfInt("opnCore",1); + settings.opl2Core=e->getConfInt("opl2Core",0); + settings.opl3Core=e->getConfInt("opl3Core",0); settings.arcadeCoreRender=e->getConfInt("arcadeCoreRender",1); settings.ym2612CoreRender=e->getConfInt("ym2612CoreRender",0); settings.snCoreRender=e->getConfInt("snCoreRender",0); @@ -3602,6 +3639,8 @@ void FurnaceGUI::syncSettings() { settings.c64CoreRender=e->getConfInt("c64CoreRender",1); settings.pokeyCoreRender=e->getConfInt("pokeyCoreRender",1); settings.opnCoreRender=e->getConfInt("opnCoreRender",1); + settings.opl2CoreRender=e->getConfInt("opl2CoreRender",0); + settings.opl3CoreRender=e->getConfInt("opl3CoreRender",0); settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0); settings.yrw801Path=e->getConfString("yrw801Path",""); settings.tg100Path=e->getConfString("tg100Path",""); @@ -3778,6 +3817,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.c64Core,0,2); clampSetting(settings.pokeyCore,0,1); clampSetting(settings.opnCore,0,1); + clampSetting(settings.opl2Core,0,2); + clampSetting(settings.opl3Core,0,2); clampSetting(settings.arcadeCoreRender,0,1); clampSetting(settings.ym2612CoreRender,0,1); clampSetting(settings.snCoreRender,0,1); @@ -3786,6 +3827,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.c64CoreRender,0,2); clampSetting(settings.pokeyCoreRender,0,1); clampSetting(settings.opnCoreRender,0,1); + clampSetting(settings.opl2CoreRender,0,2); + clampSetting(settings.opl3CoreRender,0,2); clampSetting(settings.pcSpeakerOutMethod,0,4); clampSetting(settings.mainFont,0,6); clampSetting(settings.patFont,0,6); @@ -3991,6 +4034,8 @@ void FurnaceGUI::commitSettings() { settings.c64Core!=e->getConfInt("c64Core",0) || settings.pokeyCore!=e->getConfInt("pokeyCore",1) || settings.opnCore!=e->getConfInt("opnCore",1) || + settings.opl2Core!=e->getConfInt("opl2Core",0) || + settings.opl3Core!=e->getConfInt("opl3Core",0) || settings.arcadeCoreRender!=e->getConfInt("arcadeCoreRender",0) || settings.ym2612CoreRender!=e->getConfInt("ym2612CoreRender",0) || settings.snCoreRender!=e->getConfInt("snCoreRender",0) || @@ -3999,6 +4044,8 @@ void FurnaceGUI::commitSettings() { settings.c64CoreRender!=e->getConfInt("c64CoreRender",0) || settings.pokeyCoreRender!=e->getConfInt("pokeyCoreRender",1) || settings.opnCoreRender!=e->getConfInt("opnCoreRender",1) || + settings.opl2CoreRender!=e->getConfInt("opl2CoreRender",0) || + settings.opl3CoreRender!=e->getConfInt("opl3CoreRender",0) || settings.audioQuality!=e->getConfInt("audioQuality",0) || settings.audioHiPass!=e->getConfInt("audioHiPass",1) ); @@ -4026,6 +4073,8 @@ void FurnaceGUI::commitSettings() { e->setConf("c64Core",settings.c64Core); e->setConf("pokeyCore",settings.pokeyCore); e->setConf("opnCore",settings.opnCore); + e->setConf("opl2Core",settings.opl2Core); + e->setConf("opl3Core",settings.opl3Core); e->setConf("arcadeCoreRender",settings.arcadeCoreRender); e->setConf("ym2612CoreRender",settings.ym2612CoreRender); e->setConf("snCoreRender",settings.snCoreRender); @@ -4034,6 +4083,8 @@ void FurnaceGUI::commitSettings() { e->setConf("c64CoreRender",settings.c64CoreRender); e->setConf("pokeyCoreRender",settings.pokeyCoreRender); e->setConf("opnCoreRender",settings.opnCoreRender); + e->setConf("opl2CoreRender",settings.opl2CoreRender); + e->setConf("opl3CoreRender",settings.opl3CoreRender); e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod); e->setConf("yrw801Path",settings.yrw801Path); e->setConf("tg100Path",settings.tg100Path); From 66a46d3439b6e9f8673a66238c8eb2fdb63b1d57 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 22 Nov 2023 19:32:14 -0500 Subject: [PATCH 06/53] modify the two LLE cores to allow usage --- extern/YM3812-LLE/fmopl2.h | 2 ++ extern/YMF262-LLE/fmopl3.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/extern/YM3812-LLE/fmopl2.h b/extern/YM3812-LLE/fmopl2.h index 8363886f9..0f6fdd88e 100644 --- a/extern/YM3812-LLE/fmopl2.h +++ b/extern/YM3812-LLE/fmopl2.h @@ -270,3 +270,5 @@ typedef struct } fmopl2_t; +// modification +void FMOPL2_Clock(fmopl2_t *chip); diff --git a/extern/YMF262-LLE/fmopl3.h b/extern/YMF262-LLE/fmopl3.h index 8b465ee7e..6fcf95f57 100644 --- a/extern/YMF262-LLE/fmopl3.h +++ b/extern/YMF262-LLE/fmopl3.h @@ -331,3 +331,5 @@ typedef struct int data_z; } fmopl3_t; +// modification +void FMOPL3_Clock(fmopl3_t *chip); From a75a99a80858ba0fc5313f0662b16bdbcf85704f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 23 Nov 2023 15:12:22 -0500 Subject: [PATCH 07/53] OPL: ymfm core, part 2 --- src/engine/platform/opl.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index f505b7d8d..d7e3ebb0e 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -376,11 +376,11 @@ void DivPlatformOPL::acquire_ymfm2(short** buf, size_t len) { } } -// TODO: ADPCM void DivPlatformOPL::acquire_ymfm8950(short** buf, size_t len) { ymfm::ymfm_output<1> out; ymfm::y8950::fm_engine* fme=fm_ymfm8950->debug_fm_engine(); + ymfm::adpcm_b_engine* abe=fm_ymfm8950->debug_adpcm_b_engine(); ymfm::fm_channel>* fmChan[9]; for (int i=0; i<9; i++) { @@ -411,10 +411,12 @@ void DivPlatformOPL::acquire_ymfm8950(short** buf, size_t len) { oscBuf[8]->data[oscBuf[8]->needle++]=CLAMP(fmChan[8]->debug_special1()<<2,-32768,32767); oscBuf[9]->data[oscBuf[9]->needle++]=CLAMP(fmChan[8]->debug_special2()<<2,-32768,32767); oscBuf[10]->data[oscBuf[10]->needle++]=CLAMP(fmChan[7]->debug_special2()<<2,-32768,32767); + oscBuf[11]->data[oscBuf[11]->needle++]=CLAMP(abe->get_last_out(0),-32768,32767); } else { for (int i=0; i<9; i++) { oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fmChan[i]->debug_output(0)<<2,-32768,32767); } + oscBuf[9]->data[oscBuf[9]->needle++]=CLAMP(abe->get_last_out(0),-32768,32767); } } } @@ -459,10 +461,9 @@ void DivPlatformOPL::acquire_ymfm3(short** buf, size_t len) { buf[5][h]=0; } - // TODO: fix 4-op view if (properDrums) { for (int i=0; i<16; i++) { - unsigned char ch=outChanMap[i]; + unsigned char ch=(i<12 && chan[i&(~1)].fourOp)?outChanMap[i^1]:outChanMap[i]; if (ch==255) continue; int chOut=fmChan[ch]->debug_output(0); if (chOut==0) { From c5f441a83a704cba148b71ce122294af892b9024 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 23 Nov 2023 18:28:39 -0500 Subject: [PATCH 08/53] OPL: YM3812-LLE?! part 1 --- src/engine/platform/opl.cpp | 85 ++++++++++++++++++++++++++++++++++++- src/engine/platform/opl.h | 6 +++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index d7e3ebb0e..f2fd6cd36 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -506,6 +506,71 @@ void DivPlatformOPL::acquire_ymfm3(short** buf, size_t len) { } void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { + for (size_t h=0; h>=1; + dacVal|=(fm_lle2.o_mo&1)<<17; + } + + if (!fm_lle2.o_sh && lastSH) { + int e=(dacVal>>15)&7; + int m=(dacVal>>5)&1023; + m-=512; + dacOut=(m<>1; + //logV("dacVal: %.8X",dacVal); + //dacVal=0; + //dacVal&=(1U<<18); + break; + } + } + + buf[0][h]=dacOut; + } } void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { @@ -1861,11 +1926,29 @@ int DivPlatformOPL::getRegisterPoolSize() { void DivPlatformOPL::reset() { while (!writes.empty()) writes.pop(); memset(regPool,0,512); + + dacVal=0; + dacOut=0; + lastSH=false; + lastSY=false; + waitingBusy=true; const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); if (emuCore==2) { - // TODO: LLE reset + // reset 2 + memset(&fm_lle2,0,sizeof(fmopl2_t)); + fm_lle2.input.ic=0; + for (int i=0; i<80; i++) { + fm_lle2.input.mclk=1; + FMOPL2_Clock(&fm_lle2); + fm_lle2.input.mclk=0; + FMOPL2_Clock(&fm_lle2); + } + fm_lle2.input.ic=1; + + // reset 3 + memset(&fm_lle3,0,sizeof(fmopl3_t)); } else if (emuCore==1) { switch (chipType) { case 1: diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 23fd7b97e..fa30e97f3 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -73,6 +73,12 @@ class DivPlatformOPL: public DivDispatch { QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {} }; FixedQueue writes; + + unsigned int dacVal; + int dacOut; + bool lastSH; + bool lastSY; + bool waitingBusy; unsigned char* adpcmBMem; size_t adpcmBMemLen; From bf5b565bb124c848740cdea09576a6b2b75f3abf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 23 Nov 2023 18:30:47 -0500 Subject: [PATCH 09/53] update credits --- src/gui/about.cpp | 3 ++- src/main.cpp | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 66d62b9c7..819c8c3ec 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -176,7 +176,8 @@ const char* aboutLine[]={ "FFTW by Matteo Frigo and Steven G. Johnson", "backward-cpp by Google", "adpcm by superctr", - "Nuked-OPL3/OPLL/OPM/OPN2/PSG by Nuke.YKT", + "Nuked-OPL3/OPLL/OPM/OPN2/PSG by nukeykt", + "YM3812-LLE and YMF262-LLE by nukeykt", "ymfm by Aaron Giles", "MAME SN76496 by Nicola Salmoria", "MAME AY-3-8910 by Couriersud", diff --git a/src/main.cpp b/src/main.cpp index e5dc006bd..e32f747d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,11 +210,13 @@ TAParamResult pVersion(String) { printf("- Portable File Dialogs by Sam Hocevar (WTFPL)\n"); printf("- Native File Dialog (modified version) by Frogtoss Games (zlib license)\n"); printf("- FFTW by Matteo Frigo and Steven G. Johnson (GPLv2)\n"); - printf("- Nuked-OPM by Nuke.YKT (LGPLv2.1)\n"); - printf("- Nuked-OPN2 by Nuke.YKT (LGPLv2.1)\n"); - printf("- Nuked-OPL3 by Nuke.YKT (LGPLv2.1)\n"); - printf("- Nuked-OPLL by Nuke.YKT (GPLv2)\n"); - printf("- Nuked-PSG (modified version) by Nuke.YKT (GPLv2)\n"); + printf("- Nuked-OPM by nukeykt (LGPLv2.1)\n"); + printf("- Nuked-OPN2 by nukeykt (LGPLv2.1)\n"); + printf("- Nuked-OPL3 by nukeykt (LGPLv2.1)\n"); + printf("- Nuked-OPLL by nukeykt (GPLv2)\n"); + printf("- Nuked-PSG (modified version) by nukeykt (GPLv2)\n"); + printf("- YM3812-LLE by nukeykt (GPLv2)\n"); + printf("- YMF262-LLE by nukeykt (GPLv2)\n"); printf("- ymfm by Aaron Giles (BSD 3-clause)\n"); printf("- adpcm by superctr (public domain)\n"); printf("- MAME SN76496 emulation core by Nicola Salmoria (BSD 3-clause)\n"); From ea2f92ffe359c22a29dda1e4b5aa2a4d8c44372a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 23 Nov 2023 18:48:08 -0500 Subject: [PATCH 10/53] document LLE cores --- doc/9-guides/emulation-cores.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/9-guides/emulation-cores.md b/doc/9-guides/emulation-cores.md index 323e180c4..58424e7f0 100644 --- a/doc/9-guides/emulation-cores.md +++ b/doc/9-guides/emulation-cores.md @@ -2,11 +2,11 @@ Furnace achieves the authentic sound of videogame hardware by emulating sound chips as accurately as possible, using **emulator cores**. in some cases there are multiple cores to choose from, each with different strengths and weaknesses. here are the major differences between them all. -- **Arcade/YM2151 core**: +- **YM2151 core**: - **ymfm**: default playback core. much less CPU usage than Nuked-OPM, but less accurate. recommended for users with last-gen or earlier hardware. - **Nuked-OPM**: default render core. much more accurate than ymfm, due to the emulator being based on an image of the die map taken from a real YM2151. very CPU heavy, only recommended for users with recent hardware. -- **Genesis/YM2612 core**: +- **GYM2612 core**: - **Nuked-OPN2**: default core. a little lighter on the CPU than Nuked-OPM. - **ymfm**: same as ymfm above. @@ -34,3 +34,16 @@ Furnace achieves the authentic sound of videogame hardware by emulating sound ch - **OPN/OPNA/OPNB cores**: - **ymfm only**: lower CPU usage, less accurate FM. - **Nuked-OPN2 (FM) + ymfm (SSG/ADPCM)**: default cores. more accurate FM at the cost of more CPU load. + +- **OPL/OPL2/Y8950 core**: + - **Nuked-OPL3**: high quality OPL emulation core. slightly off due to tiny differences between OPL and OPL3, but otherwise it is good. + - **ymfm**: lower (?) CPU usage. + - **YM3812-LLE**: a new core written by the author of the Nuked cores. it features extremely accurate emulation. + - this core uses a *lot* of CPU time. not suitable for playback! + +- **OPL3 core**: + - **Nuked-OPL3**: high quality OPL emulation core. + - **ymfm**: lower (?) CPU usage. + - **YMF262-LLE**: a new core written by the author of the Nuked cores. it features extremely accurate emulation. + - this core uses even more CPU than YM3812-LLE. not suitable for playback or even rendering if you're impatient! + From 817d066a57bd6abf97cb4050d214876baa24b500 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 24 Nov 2023 04:19:58 -0500 Subject: [PATCH 11/53] OPL: YM3812-LLE, part 2 --- extern/YM3812-LLE/fmopl2.c | 2 ++ extern/YM3812-LLE/fmopl2.h | 2 ++ src/engine/platform/opl.cpp | 23 +++++++++++++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/extern/YM3812-LLE/fmopl2.c b/extern/YM3812-LLE/fmopl2.c index a1a08f5c5..5245f1d50 100644 --- a/extern/YM3812-LLE/fmopl2.c +++ b/extern/YM3812-LLE/fmopl2.c @@ -1392,6 +1392,8 @@ void FMOPL2_Clock(fmopl2_t *chip) int value = chip->fsm_out[8] ? 0 : chip->accm_value[1]; value += add; + chip->op_value_debug = add; + int sign = ((chip->accm_top & 64) != 0 && !chip->accm_clamplow) || chip->accm_clamphigh; int top_unsigned = chip->accm_top & 63; diff --git a/extern/YM3812-LLE/fmopl2.h b/extern/YM3812-LLE/fmopl2.h index 0f6fdd88e..c94c1770d 100644 --- a/extern/YM3812-LLE/fmopl2.h +++ b/extern/YM3812-LLE/fmopl2.h @@ -268,6 +268,8 @@ typedef struct int o_write1; int o_data_latch; + int op_value_debug; + } fmopl2_t; // modification diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index f2fd6cd36..06113095b 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -506,7 +506,14 @@ void DivPlatformOPL::acquire_ymfm3(short** buf, size_t len) { } void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { + int chOut[11]; for (size_t h=0; h=0 && curCycle<9) { + // TODO: this + } + if (!(++subCycle&3)) curCycle++; + if (fm_lle2.o_sy && !lastSY) { dacVal>>=1; dacVal|=(fm_lle2.o_mo&1)<<17; @@ -570,6 +584,11 @@ void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { } buf[0][h]=dacOut; + //buf[0][h]=((fm_lle2.op_value+0x1000)&0x1fff)-0x1000; + + for (int i=0; i<11; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=chOut[i]; + } } } From 8500b1b43554cfd12cfa6e5cf5606ced92600c76 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 24 Nov 2023 15:16:00 -0500 Subject: [PATCH 12/53] OPL: YM3812-LLE, part 3 per-chan osc --- src/engine/platform/opl.cpp | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 06113095b..42d44863f 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -505,10 +505,20 @@ void DivPlatformOPL::acquire_ymfm3(short** buf, size_t len) { } } +static const int cycleMap[18]={ + 6, 7, 8, 6, 7, 8, 0, 1, 2, + 0, 1, 2, 3, 4, 5, 3, 4, 5, +}; + +static const int cycleMapDrums[18]={ + 6, 10, 8, 6, 7, 9, 0, 1, 2, + 0, 1, 2, 3, 4, 5, 3, 4, 5, +}; + void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { int chOut[11]; for (size_t h=0; h=0 && curCycle<9) { - // TODO: this + if (!(++subCycle&3)) { + if (properDrums) { + chOut[cycleMapDrums[curCycle]]+=fm_lle2.op_value_debug; + } else { + chOut[cycleMap[curCycle]]+=fm_lle2.op_value_debug; + } + curCycle++; } - if (!(++subCycle&3)) curCycle++; if (fm_lle2.o_sy && !lastSY) { dacVal>>=1; @@ -576,17 +590,20 @@ void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { int m=(dacVal>>5)&1023; m-=512; dacOut=(m<>1; - //logV("dacVal: %.8X",dacVal); - //dacVal=0; - //dacVal&=(1U<<18); break; } } buf[0][h]=dacOut; - //buf[0][h]=((fm_lle2.op_value+0x1000)&0x1fff)-0x1000; for (int i=0; i<11; i++) { + if (i>=6 && properDrums) { + chOut[i]<<=1; + } else { + chOut[i]<<=2; + } + if (chOut[i]<-32768) chOut[i]=-32768; + if (chOut[i]>32767) chOut[i]=32767; oscBuf[i]->data[oscBuf[i]->needle++]=chOut[i]; } } From 32030d67428364bb9d53dd3167e9a32244314d07 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 24 Nov 2023 15:29:00 -0500 Subject: [PATCH 13/53] OPL: YM3812-LLE, part 4 Y8950 --- src/engine/platform/opl.cpp | 71 +++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 42d44863f..5411bf298 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -517,9 +517,12 @@ static const int cycleMapDrums[18]={ void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { int chOut[11]; + thread_local ymfm::ymfm_output<2> aOut; + for (size_t h=0; hwrite(w.addr-7,(w.val&15)|0x80); + fm_lle2.input.cs=0; + fm_lle2.input.rd=1; + fm_lle2.input.wr=0; + fm_lle2.input.address=0; + fm_lle2.input.data_i=w.addr; + w.addrOrVal=true; + // weird. wasn't it 12? + delay=24; + break; + case 7: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 21: case 22: case 23: + adpcmB->write(w.addr-7,w.val); + regPool[w.addr&511]=w.val; + writes.pop(); + delay=108; + break; + default: + fm_lle2.input.cs=0; + fm_lle2.input.rd=1; + fm_lle2.input.wr=0; + fm_lle2.input.address=0; + fm_lle2.input.data_i=w.addr; + w.addrOrVal=true; + // weird. wasn't it 12? + delay=24; + break; + } + } else { + fm_lle2.input.cs=0; + fm_lle2.input.rd=1; + fm_lle2.input.wr=0; + fm_lle2.input.address=0; + fm_lle2.input.data_i=w.addr; + w.addrOrVal=true; + // weird. wasn't it 12? + delay=24; + } } waitingBusy=true; @@ -594,8 +629,6 @@ void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { } } - buf[0][h]=dacOut; - for (int i=0; i<11; i++) { if (i>=6 && properDrums) { chOut[i]<<=1; @@ -606,6 +639,24 @@ void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { if (chOut[i]>32767) chOut[i]=32767; oscBuf[i]->data[oscBuf[i]->needle++]=chOut[i]; } + + if (chipType==8950) { + adpcmB->clock(); + aOut.clear(); + adpcmB->output<2>(aOut,0); + + if (!isMuted[adpcmChan]) { + dacOut-=aOut.data[0]>>3; + oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=aOut.data[0]>>1; + } else { + oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=0; + } + } + + if (dacOut<-32768) dacOut=-32768; + if (dacOut>32767) dacOut=32767; + + buf[0][h]=dacOut; } } From a7b3f8edf715c2851eb971911ad4221f0b068363 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 24 Nov 2023 15:41:59 -0500 Subject: [PATCH 14/53] man really --- doc/9-guides/emulation-cores.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/9-guides/emulation-cores.md b/doc/9-guides/emulation-cores.md index 58424e7f0..d96600fc1 100644 --- a/doc/9-guides/emulation-cores.md +++ b/doc/9-guides/emulation-cores.md @@ -6,7 +6,7 @@ Furnace achieves the authentic sound of videogame hardware by emulating sound ch - **ymfm**: default playback core. much less CPU usage than Nuked-OPM, but less accurate. recommended for users with last-gen or earlier hardware. - **Nuked-OPM**: default render core. much more accurate than ymfm, due to the emulator being based on an image of the die map taken from a real YM2151. very CPU heavy, only recommended for users with recent hardware. -- **GYM2612 core**: +- **YM2612 core**: - **Nuked-OPN2**: default core. a little lighter on the CPU than Nuked-OPM. - **ymfm**: same as ymfm above. From 2558da9418a9678d647fc2ea8e51e573ea8f2f02 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 02:26:59 -0500 Subject: [PATCH 15/53] OPL: YMF262-LLE, part 1 --- src/engine/platform/opl.cpp | 142 +++++++++++++++++++++++++++++++++--- src/engine/platform/opl.h | 3 + 2 files changed, 133 insertions(+), 12 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 5411bf298..dcda2908d 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -661,6 +661,108 @@ void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) { } void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { + int chOut[20]; + + for (size_t h=0; h>=1; + dacVal|=(fm_lle3.o_doab&1)<<17; + dacVal2>>=1; + dacVal2|=(fm_lle3.o_docd&1)<<17; + } + + if (!fm_lle3.o_smpbd && lastSH2) { + dacOut3[0]=((dacVal>>1)&0xffff)-0x8000; + dacOut3[2]=((dacVal2>>1)&0xffff)-0x8000; + } + + if (!fm_lle3.o_smpac && lastSH) { + dacOut3[1]=((dacVal>>1)&0xffff)-0x8000; + dacOut3[3]=((dacVal2>>1)&0xffff)-0x8000; + break; + } + } + + for (int i=0; i<20; i++) { + if (i>=15 && properDrums) { + chOut[i]<<=1; + } else { + chOut[i]<<=2; + } + if (chOut[i]<-32768) chOut[i]=-32768; + if (chOut[i]>32767) chOut[i]=32767; + oscBuf[i]->data[oscBuf[i]->needle++]=chOut[i]; + } + + for (int i=0; i32767) dacOut3[i]=32767; + + buf[i][h]=dacOut3[i]; + } + } } void DivPlatformOPL::acquire(short** buf, size_t len) { @@ -2015,27 +2117,43 @@ void DivPlatformOPL::reset() { memset(regPool,0,512); dacVal=0; + dacVal2=0; dacOut=0; + dacOut3[0]=0; + dacOut3[1]=0; + dacOut3[2]=0; + dacOut3[3]=0; lastSH=false; + lastSH2=false; lastSY=false; waitingBusy=true; const unsigned int downsampledRate=(unsigned int)((double)rate*round(COLOR_NTSC/72.0)/(double)chipRateBase); if (emuCore==2) { - // reset 2 - memset(&fm_lle2,0,sizeof(fmopl2_t)); - fm_lle2.input.ic=0; - for (int i=0; i<80; i++) { - fm_lle2.input.mclk=1; - FMOPL2_Clock(&fm_lle2); - fm_lle2.input.mclk=0; - FMOPL2_Clock(&fm_lle2); + if (chipType==3 || chipType==759 || chipType==4) { + // reset 3 + memset(&fm_lle3,0,sizeof(fmopl3_t)); + fm_lle3.input.ic=0; + for (int i=0; i<400; i++) { + fm_lle3.input.mclk=1; + FMOPL3_Clock(&fm_lle3); + fm_lle3.input.mclk=0; + FMOPL3_Clock(&fm_lle3); + } + fm_lle3.input.ic=1; + } else { + // reset 2 + memset(&fm_lle2,0,sizeof(fmopl2_t)); + fm_lle2.input.ic=0; + for (int i=0; i<80; i++) { + fm_lle2.input.mclk=1; + FMOPL2_Clock(&fm_lle2); + fm_lle2.input.mclk=0; + FMOPL2_Clock(&fm_lle2); + } + fm_lle2.input.ic=1; } - fm_lle2.input.ic=1; - - // reset 3 - memset(&fm_lle3,0,sizeof(fmopl3_t)); } else if (emuCore==1) { switch (chipType) { case 1: diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index fa30e97f3..c4346a21e 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -75,8 +75,11 @@ class DivPlatformOPL: public DivDispatch { FixedQueue writes; unsigned int dacVal; + unsigned int dacVal2; int dacOut; + int dacOut3[4]; bool lastSH; + bool lastSH2; bool lastSY; bool waitingBusy; From dee91ab2ba39ccc880d3967b3320580af0172688 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 02:33:52 -0500 Subject: [PATCH 16/53] YM3812-LLE runs fine on my machine but eats 80% of a core --- doc/9-guides/emulation-cores.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/9-guides/emulation-cores.md b/doc/9-guides/emulation-cores.md index d96600fc1..e067976dd 100644 --- a/doc/9-guides/emulation-cores.md +++ b/doc/9-guides/emulation-cores.md @@ -39,7 +39,7 @@ Furnace achieves the authentic sound of videogame hardware by emulating sound ch - **Nuked-OPL3**: high quality OPL emulation core. slightly off due to tiny differences between OPL and OPL3, but otherwise it is good. - **ymfm**: lower (?) CPU usage. - **YM3812-LLE**: a new core written by the author of the Nuked cores. it features extremely accurate emulation. - - this core uses a *lot* of CPU time. not suitable for playback! + - this core uses a *lot* of CPU time. may not be suitable for playback! - **OPL3 core**: - **Nuked-OPL3**: high quality OPL emulation core. From 6eafedd03769fa5a6ccb97b2e4b6a9213301b07e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 02:35:42 -0500 Subject: [PATCH 17/53] ymfm somehow loses to Nuked-OPL3 in performance --- doc/9-guides/emulation-cores.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/9-guides/emulation-cores.md b/doc/9-guides/emulation-cores.md index e067976dd..2624f6ddc 100644 --- a/doc/9-guides/emulation-cores.md +++ b/doc/9-guides/emulation-cores.md @@ -37,13 +37,13 @@ Furnace achieves the authentic sound of videogame hardware by emulating sound ch - **OPL/OPL2/Y8950 core**: - **Nuked-OPL3**: high quality OPL emulation core. slightly off due to tiny differences between OPL and OPL3, but otherwise it is good. - - **ymfm**: lower (?) CPU usage. + - **ymfm**: this core is supposed to use less CPU than Nuked-OPL3, but for some reason it actually doesn't. - **YM3812-LLE**: a new core written by the author of the Nuked cores. it features extremely accurate emulation. - this core uses a *lot* of CPU time. may not be suitable for playback! - **OPL3 core**: - **Nuked-OPL3**: high quality OPL emulation core. - - **ymfm**: lower (?) CPU usage. + - **ymfm**: this core is supposed to use less CPU than Nuked-OPL3, but for some reason it actually doesn't. - **YMF262-LLE**: a new core written by the author of the Nuked cores. it features extremely accurate emulation. - this core uses even more CPU than YM3812-LLE. not suitable for playback or even rendering if you're impatient! From 0f8fba06ca68ed7566d67c581bdcc227a462fa32 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 03:10:49 -0500 Subject: [PATCH 18/53] OPL: YMF262-LLE optimization thanks nukeykt --- extern/YMF262-LLE/fmopl3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/YMF262-LLE/fmopl3.c b/extern/YMF262-LLE/fmopl3.c index 4e82fb27c..f534ba724 100644 --- a/extern/YMF262-LLE/fmopl3.c +++ b/extern/YMF262-LLE/fmopl3.c @@ -74,7 +74,7 @@ void FMOPL3_Clock(fmopl3_t *chip) chip->prescaler1_cnt[1] = chip->prescaler1_cnt[0]; } - int prescaler1_clk = (chip->reg_test1 & 0x40) != 0 ? chip->input.mclk : !(chip->prescaler1_cnt[1] & 2); + int prescaler1_clk = chip->input.mclk; /* Temp: disable prescaler for performance reasons */ chip->aclk1 = !prescaler1_clk; chip->aclk2 = prescaler1_clk; From e741c6a39391c84d269e6c5577c302ddbad51dc7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 03:13:14 -0500 Subject: [PATCH 19/53] OPL: reduce YMF262-LLE write delay --- src/engine/platform/opl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index dcda2908d..8eb95fc28 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -678,7 +678,7 @@ void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { // register control if (waitingBusy) { - if (delay<62) { + if (delay<15) { fm_lle3.input.cs=0; fm_lle3.input.rd=0; fm_lle3.input.wr=1; @@ -696,7 +696,7 @@ void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { fm_lle3.input.address=(w.addr&0x100)?3:1; fm_lle3.input.data_i=w.val; writes.pop(); - delay=64; + delay=16; } else { fm_lle3.input.cs=0; fm_lle3.input.rd=1; @@ -705,7 +705,7 @@ void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) { fm_lle3.input.data_i=w.addr&0xff; w.addrOrVal=true; // weird. wasn't it 12? - delay=64; + delay=16; } waitingBusy=true; From 29dd7ef42833efc8707d36fe4afefda625e45d9c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 12:04:46 -0500 Subject: [PATCH 20/53] Virtual Boy demo song contest entry by Eknous --- demos/virtualboy/honeydippedkiwis.fur | Bin 0 -> 2630 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/honeydippedkiwis.fur diff --git a/demos/virtualboy/honeydippedkiwis.fur b/demos/virtualboy/honeydippedkiwis.fur new file mode 100644 index 0000000000000000000000000000000000000000..5897ed677cf8a3cc5cd757c877b7e6df864b5499 GIT binary patch literal 2630 zcmV-M3c2-ooYh%fY*W`A|KEG=^|c+_HY5?Fc@nCNeX17D}ljqQWRjB zZHPx=6Wf7OY1+;B*eZ>6szTeftPhhW@z4h*@v?Of-qZFp^=0a$p~`(glbE)Ls!}y= zlK20g`@i;$iSq?a?D+nCzW(3mf6hHlw)IctGNXxN;TMfnsh9@POpMfCUpeuHQ-0B#|`5AYfy7yy_={2lSFAi!P3 zp$358BTi#zKSTT|4A2?@_%)*61o$iBVk2r;0q_=L%X0ul#L1NaA0U2**s}`xR|C9@ z_!#j35o!WBibx`U+6?d~#C^oi)&jhbxQl3v0t_NX5SI|w5N{&>hTA49!}CJW%-h6@%enh5Qg6g9Y;}y zQxURg`m8C%nbKs@Iq0fNB89*Ppj;|G*5xWy0W?QoWy<9ubs7t#wyHSg(JV+?g|T$z z0)D5i#(Pjuv`?=|bKb%~x>)iP0rR=7u&AF{s zK0B4OwpjVgsZ=4DwvtnsOcv$ZJ>aGfJ(`k#i3|I>g1PL( zk!&)x@7(BkJ{5$%g9FE#+@++%S1~7ihCdK&2!$giO|NvPL$ju7$~`v*22bpK73&XC z#9&_-1~U0VZfYWxDOg?5f218g$H}fA#A3Z>{rX7XYTUXZrCg>cn_;(6e)%BoYD+(+ z7nPwCD=67$i-Yn*1v+t$!J)zbsea+=*A!@asiHq#(XYY9%M*)MTO+-C#_=*c(sLE* zI$TI6E-b;TCbBlf&O{>Na42AC7{pq?=;#b^Tob)>`ZbzNF4r%;ay}{0qgJF}^7w_) zcP_!NDEZZhehKuR&yPDxq%|G^p_`9R|{1P;7s|m5+qjBMlVz?W{r@)44(9b^Z zMy+=qTvgzOopbD%5C=S;8O^1Z;C)9;yubGbosb-F)p1&zBj9H|n)74nf|Z|~T8icx z{~Bh!dMf@6EqCWEZfo4Eh>v(zX~(YR=;=SWxtchU&3g|YA3B{#U!2Spa+4G1&zw9u zaG-ln=caY7s~dy=nRN=z_PJ0oDj-wY%1g|82LkW5K<{(BPs1w48Xjw9{tbwY;QTDw!uSf0;XN2y z5O`mgK~h;ay?PqwGm6lK*w15MLwuEu!izTk8gql)R=M(k$=bd_7d%nExYSnwtmNDB5czrl`lF+a)b=IHNdYzOCNyNPAGM@?O$ zh^dqNO+2=SL#?nuDu$Z0?KP~@0Hf%q)H*;h*vF=8ey`WKUy_;*w znN=)+l&xd~+@lPjlG2IU4 zi?MM0Q7RLA7e}mv$AIe&9^b`u5sy)?0}JvRwQnA{$v+n;qLe2lEWHHgi&8sVPFt8l zy>%XVw|YBNcc%BqSUC??F%s z{#K(}(K#|YI}>Huz;%?zy>wKL^?iHr zD7V3g`-9UW?ymj16rw!nUzdZkthM14k5R zWbHlM_N&)oan-Og$#ESxZ9%VLR8D5&^@%}e=`BEoWqVz}37_a0iBGq~Qp8}*p+fN4X}-))+9 z??Z(u`@yTNrd>poxwa9#2;ka&lJl`)w7~8*NdGfq4$a6&?Umu96KzPvrNRB60vZGU2&q}yo{mjY17fnzy=kWhPS19F_FQvLXD%5}LZZh5 zojs60f>ll@IWp|Nv3ty+fEo1TTAJoV#V)poFr@S)TQ+_5R$6su;i=GV49R8P8a5x= zMc2@>$Iz#3z_JXL@!caMjsZ3jA^oaI;*pxb<24a-+UGp&+2hsKQ{DV~PWCYWjG>75 zxfqU2i=l{z&Ru06zf7IpcG*+t@}M->BCgCJKw?4%q=AYIA)BLXOk}Meg)_`hi ze*75t@yU;lA1XqARQxFPsqP=T!yDIpy0~NTbItdE*RH-ubvi`u7=H09L;TVZ?;GMh zL%d^%TZVYk5N{abCx&?45H}2Q)eyEJzHcmlx;^s8$z`b($zk!5aDrTuCt^fc4 literal 0 HcmV?d00001 From 90a4d2b8ada3fdc74606dce6fd0eceb5b696f95d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 16:29:38 -0500 Subject: [PATCH 21/53] Virtual Boy demo song contest entry by freq-mod --- demos/virtualboy/vboy.fur | Bin 0 -> 6615 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/vboy.fur diff --git a/demos/virtualboy/vboy.fur b/demos/virtualboy/vboy.fur new file mode 100644 index 0000000000000000000000000000000000000000..f5b972708d126ed77c0c21460f1efc4123807c7e GIT binary patch literal 6615 zcmaiXWl$7u_$^3xgOUOQiY!Pg0JW8J zR2qJUqOz5`qFMW+{Iti)L(e$zao-_-zGv)daQ|X>*luPS?g#Yb^s7^Jq zTxbjkXScTd(%t=~`%u2G-sn1XQZCSEhv$dGj&XP7(Lj9YN!M-|>hylHYqunH$FIB3 zb_*iu*TNU%_&Kz^-|4^Igy-s+&~Ni`?Qm9nR$#2NmktFRm#9LaY{H$;@I8a}znc7Y z(Kpwx??DT`eN76THxOs6K{L-R6D7O+oNr}3B^5dJfNXV&NtV9b{ETEcgZBctqnj_h# z7L=4Yr`*xx*k@R_MR8~~n=B#kz#V}~?m#Z07e|j+mf|n*7!&PPG*uLn(r+$Uh}V-a z+|Hzgf&mhY9)^FhD{s#VJ z`lBk42`4yE+(ud$B_;wxT5SRJqv*wnsi@||3SjD=N|Tr1H^szctJEoy?LFckihPs| zR!NQFh)u3bc#AMbG(~k4IEyb*$WwSkb&IR0A?VQGZRX5i{9Y7c6ap4%my8(l&sWc> zWC{gbKKYwFxKqsMrAy7(3>j7haZ8S@D4PEAj{`X4iz@B!4Lc zYIvj{2KbBbdNV+&nDoIl;D)8DQfIBrvlcAT2gtItc0*By3%|dgrRHRm%!4?Qaw_$ zAki?kBLux%GS-8r_fEkLUkgmji>>d@VVpis8}q zdgM=FP4rRPmMMs`7bk-BFOJVEzpqci$nP1Cj=4gp!#L`t5<^+SXvUAf<>AOvTH$HS zRWm&gC)+?|qBqe*Xa^(XMG@?J{fqI8MiZX^FsxWgtQ3|G1LX^0tq*=VWgQD7$rAMu@D6w?UKCg3 zVa2Pv$@*}FVy0hW2(UypZ(kWjeIGy}+Uq)*5U&v#J>LB>I(WY1C|V3VM$`)_ImL}e z*_?{E5n-jDcM<(ZB1`a>4${Nqk86$dVZl}g`wB}?9U=2g8DrUeDG$p;i=xGwcZH_< zqi-TUH9U;SWpa;mr+_8KaythRO@*TiZAM~MPVwk$M&2~D$r6pn3o}L$MV$fL0Foqv zvGE8s#07fcGsg0<5Q5|tp*&$9?ijn00xlG%pFoCVL=L}?xaD`UH~oWuh&9$?Utu|) zhYNip!iZrh7XLoJBg68cgE^LoWiRE0dAuaGb5}O6z~J4jo!g7uaX_B2WTcYKIt(_V znZrnwn=K$@32%YS9f-RvH1vaqzG-h4c0g8*;9q^dPg)xG)VaCd95vK=E_U?8_Z59L z-Bu9SDgScs6#y>iO|Qr2nSOD>6$MA8WmvamI`w$T3biw}!q}6ae5L2U<)=qo>AI0# zr~y@G_9LmKcuW_l6vH-z@Wn&4miEo4uPv~cKbGgYW;JBIm|!9F!r^-rP1YGr6ckP6 z5DiJg^HE~Q;QjhIst|oo_y0MB&TCU=q7^)j-{{yr`i}=Ar)$2jC~REtNvAzRDDh?R zHA8B+>TrQZ7yL?BYKQVtYOHGBjOr@ovY4#6B4@c`OZATB^?U9Xa)wdwTAbC+tDi;A zAl1U=KEpltX~g}@T#J-YUKrml+yKPxJE ztNV`!iIdJ`>42@yDcz2c78be8FMopmEv2VNpA7L>haOCYavOhxV!cO7|Cai$#vf=# z{);hm^>3RMI3T5Xp`~NRg_+#=VX&@W9=S+x0kDdDc~)F6TK0hLaS(C@UE4S}c-x-BSDV@3pC4r@ zyponvost`eDn2H=q5K{wW&1A1-eOu=#wvck&Jq}Z;9F$oK z;O|q;Zhh0!2l_A~T%*U3^|z`Zy8JDHIEGRy2+DApGrhQ)M9L5!Xg%1|%-R@xkZ|KQ z*durQA=0W1-4~auT=5G|NmG!Y9QJaP*4m7F zW!KS$a~zu$OjC-Zdbp?!-~E~u^fkU*BYU-B>w}o8kNatrt=Y_$c<=?>>;E7|9VeKI8Z zk6m|slD56?#!>sN_s`~z66=8d($_Vc`zmk>eMrhtAI-bhAz-RKBRP7tO_07ZYxfnz zcUv92M!ETuEqB|TRwTTs*c@q;X614*{j%ZJ*Xf-qv8oOVq|GA+{rEf%BXU^8*Y zCfB_@PJhnt&YcPNVgecx%z``26uHfx;xwgBkL)I8WJaZ;0GsqL*DsfRfY5B8l(?7E zEW$>WukErbjF)RKHOz~xT->H#x*tO{>#~;SwL#0|Z%TDY>vOlqh}|etU*k1&Kz@cG zg&X2o7Y2(#!yJjHAuOWQhZzfEaP6X;Y+tkX{FxR&QI7j|8-%3_%*C!hK-qpv=IDILSUm4nL<qrlM6SGs*Dzv89d`{rys+%&X&T1D}B59adUzJ2|ZD znalatFusMc-POwYqc7Shn)ChL7BsWzD-A)9s@~itlgT}O;C}hyg@;D(?~1Rt%ytc} za_?aDs^xCvRhMg<*W45O5~84X6$@4_j}++;yTj*eClsgTW8Z(~SN2KV26&Es0m@{T z*zaJrjvX9acKlpPKvZsaO9dt!Ij^pod2=#{^<0cab3p>BGfoje=r&)CBh!dc)oWn2 z6F*$4<-Z zitNm#M%ulT484-vcj@c4=vNRg+Cg-M5!ufq!VDN1g`8OIraCFo-D}Gqlua|Kof;m7 zd6RLO8@SZO0XX3{<%!2Ygov$b>;I*+XqT*Gg6d|O9~)ESrex!XFZ=B#Vts5g$19gb zS2bG@U8yT^O?wU}nd4nZ)!WGc*NvkUq6d-3_gfi(&@VpM$L$6odkBZ{9=YryHn`Nb zk!hFQU2kd;x6NbnXeH|F3Hd%Gld$i0CS9HfGYEqGqS!xZw#^-@@08LkT=(`Z~0up%(B^wrON1%1@XDe78z5Ai$BJ2>)B^;fY+TI)|CqQ zr)I?$I5=z@;=V4!&1C`--zj$pT(X<)iO80zZHb1cBiW5cK4CrH_KH?9o~=#uk$Bm~wVegU7U`pVvr)}+>@Rd%59XuCM+7;XYZn?Hi$#tLc^>r`D{( zYyFZEUd(;}Ps7o)T9pTm3+(Kl-G0Z5d-BTXQJjp8!_vP>I_{6Z`9A)Bu}ef~wHCJ( z8lckWi4v^u0_PTOs%#BcTLV)ZS>}gpx9Ugux7&>D9%2g;NSZ@3IqG87(#&h5Js;+! zertDat6d+bZYEqUkRNVBN4K_6G_O$@9^_-4VXs4)bM-=jqPCX8Tp9PGnYYatV3@df z217WHrhL>2H9Tzer)FlHnqE{|+Y+v4Lpkrh$&{!$)Cc~(%qshRV4H9D;YWV3iWl_YCZuT}w9d{@^0%9}9SW*X2h}Hnw8D^LFsZwkv1;gwT>#!l ziJRSoYkjl%QLjchbmb;`bpyUzVDiyuVv9$|2zAo@KexITEv6^4DsK%Ur%^=q({5k; zldEnxw!ZSMh<-T;crlYcEw@zhb$DN>AvdFU_8JSTBgs{v*~oR4W%~^o6f1a@%&S#4 zLT@39B$+u}8nM23tHrTzogCa4+`735v(DXCCnJY#aH^Fo@oct$i{2RgqM_TS2hg*C zlW3$Q#V@LO+tWhRv_F02$uy4D^!`t zr{-DEc~2rNB^|Kd)-0V(6o-<|!zJ;Xlc?b$x5~s*jt#o+A1P`uY2A(-L+90r4@jIN zwI-s4{e^@O0dkO#6G)%*9se=zE`q>|#DcI4E$H7{KVm%%5ZZ$OqAi5Nblf?v)xupy z;{D+SvN>#7cG2_zI+r`e*Y~po^_#2bRsEyCe|}Czk_q2`VEdb==cBh_VwNSDBgnW` z^DU)9v%YC@@!NDoQHPIrfn7kze=n(>9&+e^hl?FjD%@yHDkte`TTeL0O4+I3+2niA zr^O5PU$=94z7t~bK31rt6-4ahz9oQNG$G+?8$0Y`;<&1B7jA073d;YMwV2o9tYs=v zi9dW6BV4i-(Q)5iFz4x(VG?LclHxj8#vfsjxY*ZI|FL2Ftnk+vn%-^v43(b1S>mW)L6GIhO7PWg)&Wgnh45hmLZ)4hx9Ue~s9@K< zqo81ClIke4e&A?*2eoDEMLJd|f7PLTRP2Xe#{Scpr-UC))28slYNS6^X7g4@&g za7Ayr^W{A5xGMQFv~-ZYiI=TD(^Z#++gbAM%>e!nR|$)?3hMRvD|WO7d#MaB5!ZS6 z*>~?PJxKfpg>+1Go-~{ujoYNp+@+Y)(P_s1p~OW9^xicVcj9Ao4&8;D&C4a5yQfpq5a~Qh!Y8s!)_(clG%* zPJkkLx9v+)mR&Zl2U!Bxkz4=T2bR(^z#_4~Yy=`y{<@k@21D=D8A{|XgcVw*w>2N? zeu1T@wb$`%!H%m)V_uGfN|T$zz&wAmteJurdwP{u5jAREv}L~%%}P`NO(j$3NvY23 ztwA6FlH^bCplH+Sq>ZT$RKRpC;6G{4{yuF+Vh)S&@M~*S?p+?G)yI`cKojLyUEUW0 zUn|(C_EMDGEuqcM)X}d$((VI&Am-RV>jx}tc;;p_IYwJ#F9-Yn)c%*-az9z$<4HMmf0VdQZZJkW&P#Vi9-6skwdYo6_@iPyldczE^1(b z^c7fFyFsyf$yl`cxYM98x7NMYnmD_nZB(E1oi)RRLzkZK@cmM!_^n2DWxHba2hl*c zG6S;lZ|ybn%5uxxuBAaLSL*oyx1I=3{ z2$Kj5{%o+cHkgKPFn(-5yj-mhpJ;LD%3mg*9{-bX9Unen?68g;p1Db0UKBWf_y|yw z4S~RuUiPhqQ`<0@=j^1TOZ=}XNY3=uh*qG4P6VEPl7ZR>=Bh%&$vxKAB z8|~^j(pLn|+709EPiL&(WxM*k&gvR#2Nd%-r(S+^4Se;r9jCG3qpPvkK;`?lVlw*WP1S|7Q4%t1GeVji$o<;Yrd z61#^6jsNDR3VtRr?7?)>!^UnBs>1ww>RdeSCN$*UvUCN|5|FD6c$xllY+LpBxkda^ zrC-&^`W;}bU7z&4o16dZabv-v2Q1IsrFHG3HZk!E5_)PBvSKL?XKvYH+4l^b8y*e` z=ocymGzL5*(BV=QsM#j&xbfTxJO1^0df+G!{*IxmX|n!JKf^oGC3C**tv>125?E-C zD7;q5H2X({@x-a!lhB>yk{Qc|;cMR{ai1h{?<6)|yZzIL#|ysL3Vi*q@gX`SK2>zA z8Pd7U#lbJHdtATvYa5CV{f+!0Vw=GBgM%>P&x z;z3D66`v5u$Yptw@C;viwICsNC_^BH7q#Pl{#9R$4?G2jrTxjJJWCZe@>9k?OD#1N z$sGa(vzDA+gpAQnKn*=%kGsP&HJK0KZia!B@u(f21bQ#Y2==B;0FqRy_BHg4gOVc` zQ-!*XSvnjh=366w0!VZ8iR!KAu(USgXv-i#}h?7L?gtTX|;*!qyH63N&4{&G><8?GwS zUbM%ZfLIeIpT^4U$ew(&&R6-%RpZtw2C<+OIb9eug8^!uEf;9O+-siIs`E@;8_*g8 zq3VL}Jpsc!1%KrWNE!l9w>nh=yb}z(6OKwgsE>YfcIKL6&6)K|n04f}( zvk_9OIFM915T%2&|D%{m>a|M!7tVUMhT@za<(wY&MzW!s%k$rqe|S@#J;dkEn!~z9 d?Sj%OH%jswSAG@{exmVorSq&`oyEcZe*yGq3E=<$ literal 0 HcmV?d00001 From 898337dfa74c55244d44154428774d480b2c8f78 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 16:29:56 -0500 Subject: [PATCH 22/53] Virtual Boy demo song contest entry by ControlleronaHanger --- demos/virtualboy/VBTune.fur | Bin 0 -> 78675 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/VBTune.fur diff --git a/demos/virtualboy/VBTune.fur b/demos/virtualboy/VBTune.fur new file mode 100644 index 0000000000000000000000000000000000000000..aa18a06ea955c947a9fce0577c840b08e9f06790 GIT binary patch literal 78675 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

X@Z}XaC|=dDFuA;1!T+@M6Sjyq35ezS+~!KD>Tg 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 dV*e88ua?gGZ*|s>_ Date: Sat, 25 Nov 2023 16:32:29 -0500 Subject: [PATCH 23/53] GUI: update credits --- src/gui/about.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 819c8c3ec..3860bd9d6 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -81,6 +81,7 @@ const char* aboutLine[]={ "Burnt Fishy", "CaptainMalware", "Clingojam", + "ControlleronaHanger", "Crisps", "DeMOSic", "DevEd", From dff445fa4111e014f07d01c9078f20192f1cc236 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 16:53:51 -0500 Subject: [PATCH 24/53] Virtual Boy demo song contest entry by Brightonic --- demos/virtualboy/Red_Revolver.fur | Bin 0 -> 896 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/virtualboy/Red_Revolver.fur diff --git a/demos/virtualboy/Red_Revolver.fur b/demos/virtualboy/Red_Revolver.fur new file mode 100644 index 0000000000000000000000000000000000000000..234ff69e9c97e4436bbefedde1237292caaa547e GIT binary patch literal 896 zcmV-`1AqK@oXwQaYZE~f$KUMCZqg>3#9C`>twwKx*54Nq+Gs6AF^ZL{h*-5bh;6L3 z!5?^NwIW4&6-57x_TWuChzCLOCMfPr@T4Llcv!!0c6Jli*cJ-XxBFptX5aVCyf?G6 zt-m^6xp1kRAH6zU9Vu^{C!##ntXS$lHgDm@aRkxv1HF_Wm2=2OmkXZK!q&TpEh1kL z_TeB|mf$xb!agBdBvC(j48DMFMKlF|f`=$i-XWR?hZ97ffJhR(0pGx}CZh9TdoxiF zXi5=vfL>4rcT;)@w(t=R9?d~m=3BirOklDnQu3|BAQm_25w5@nFON@DFO1|5T)h>O z!ZwG)YSCL?01HTlSc*50FhB;g@sch~{hn;rN$$iR%ES3g#ZN+n* za#Sj~RIFT|7_W|&D--!0bZTIgDFT=lBw2N+#F`_GH9P5Op_8!YIp62nWwY64S+_QR z7_wH|C}gp5$Xy88%^_1^$c&q6N;=BIuZgY#iQawPo7-IL*VoUV-oJbO@`WD++}}9h zZUo%K0o#Ir8DnlbooY@dY$Yvh7=OV$_xznF8zz=;pbzr2!bGJYWk-3AHV(NLA#(^R zY|=^n_)Uql-`}^Tu3n2656acni~B&3Z7Ck~RAKPYEO+IN-^K;_U)FF^Y@8)mE=+_A zhuw^mn7H7CgM@I15FZ-6vw6#BKo;Zx@2?iH8nl8npbe}A?Vtm!1D#+!=mHze`2Vcb z2IJUa0=1d}RvB_;0wI|g?lM@?^2IF|BW}qUag#CP)^JkNRLC_|XrbDY)K@2L^YZaB zI;P!eV%6)VnP8}Gjp=#ZF4I0ux|J984O1Cju=a@=9 zmsKTiDmkVWdAY6RjU~AKU;kQuvXR-{WBfmST70mf0lJI; literal 0 HcmV?d00001 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 3860bd9d6..d664e9a1e 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -78,6 +78,7 @@ const char* aboutLine[]={ "BlueElectric05", "breakthetargets", "brickblock369", + "Brightonic", "Burnt Fishy", "CaptainMalware", "Clingojam", From a7be3e95140b43b471852558aeddb254030a614a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 17:28:30 -0500 Subject: [PATCH 25/53] VERA: fix possible PCM crash --- src/engine/platform/vera.cpp | 12 ++++++++++-- src/engine/platform/vera.h | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 6d97c9c4e..39560cf00 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -70,10 +70,18 @@ void DivPlatformVERA::acquire(short** buf, size_t len) { if (!isMuted[16]) { // TODO stereo samples once DivSample has a support for it if (chan[16].pcm.depth16) { - tmp_l=s->data16[chan[16].pcm.pos]; + if (chan[16].pcm.possamples) { + tmp_l=s->data16[chan[16].pcm.pos]; + } else { + tmp_l=0; + } tmp_r=tmp_l; } else { - tmp_l=s->data8[chan[16].pcm.pos]; + if (chan[16].pcm.possamples) { + tmp_l=s->data8[chan[16].pcm.pos]; + } else { + tmp_l=0; + } tmp_r=tmp_l; } if (!(chan[16].pan&1)) tmp_l=0; diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 515e17e56..63a3113c1 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -30,13 +30,13 @@ class DivPlatformVERA: public DivDispatch { protected: struct Channel: public SharedChannel { unsigned char pan; - unsigned accum; + unsigned int accum; int noiseval; struct PCMChannel { int sample; - unsigned pos; - unsigned len; + unsigned int pos; + unsigned int len; unsigned char freq; bool depth16; PCMChannel(): sample(-1), pos(0), len(0), freq(0), depth16(false) {} From c6a0d0e93e281f8e72342b74b4b40db784c4713c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 17:46:46 -0500 Subject: [PATCH 26/53] the final challenge --- extern/YM3812-LLE/Readme.md | 2 +- extern/YMF262-LLE/Readme.md | 3 +- extern/YMF276-LLE/LICENSE | 339 +++++ extern/YMF276-LLE/Readme.md | 15 + extern/YMF276-LLE/fmopn2.c | 2342 +++++++++++++++++++++++++++++++++++ extern/YMF276-LLE/fmopn2.h | 362 ++++++ src/gui/about.cpp | 2 +- src/main.cpp | 1 + 8 files changed, 3063 insertions(+), 3 deletions(-) create mode 100644 extern/YMF276-LLE/LICENSE create mode 100644 extern/YMF276-LLE/Readme.md create mode 100644 extern/YMF276-LLE/fmopn2.c create mode 100644 extern/YMF276-LLE/fmopn2.h diff --git a/extern/YM3812-LLE/Readme.md b/extern/YM3812-LLE/Readme.md index 38aa0debb..77dde824a 100644 --- a/extern/YM3812-LLE/Readme.md +++ b/extern/YM3812-LLE/Readme.md @@ -8,6 +8,6 @@ https://twitter.com/travisgoodspeed/status/1652334901230723072 # MODIFICATION DISCLAIMER -this is a modified version of YM3812-LLE which adds functions to allow its usage. +this is a modified version of YM3812-LLE which adds functions to allow its usage and per-chan osc. the original Git commit is 7f0c6537ccd61e9e7dbddb4e4a353e007ea69c50. diff --git a/extern/YMF262-LLE/Readme.md b/extern/YMF262-LLE/Readme.md index f9cf1b581..14a7126d4 100644 --- a/extern/YMF262-LLE/Readme.md +++ b/extern/YMF262-LLE/Readme.md @@ -8,6 +8,7 @@ https://siliconpr0n.org/map/yamaha/ymf262-m/ # MODIFICATION DISCLAIMER -this is a modified version of YMF262-LLE which adds functions to allow its usage. +this is a modified version of YMF262-LLE which adds functions to allow its usage and per-chan osc. +it also brings in a small optimization. the original Git commit is 63406354d05bc860a6762377ddbb9e2609bd6c36. diff --git a/extern/YMF276-LLE/LICENSE b/extern/YMF276-LLE/LICENSE new file mode 100644 index 000000000..89e08fb00 --- /dev/null +++ b/extern/YMF276-LLE/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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 program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; 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. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/extern/YMF276-LLE/Readme.md b/extern/YMF276-LLE/Readme.md new file mode 100644 index 000000000..e9ccb6cf1 --- /dev/null +++ b/extern/YMF276-LLE/Readme.md @@ -0,0 +1,15 @@ +# YMF276-LLE + +Yamaha YMF276/YM3438 (OPN2) emulator using YM3438 and YMF276 die shots. + +Special thanks to John McMaster for decapping YM3438 and org(ogamespec) for decapping YMF276. + +https://siliconpr0n.org/map/yamaha/ym3438/ + +https://drive.google.com/drive/u/0/folders/10IOhV4wf4A6SLQkS-i1wyJnyXH5o8DKn + +# MODIFICATION DISCLAIMER + +this is a modified version of YMF276-LLE which adds functions for per-chan osc. + +the original Git commit is 172b3a40011d0d89528a7fc0d606cc92d94cd033. diff --git a/extern/YMF276-LLE/fmopn2.c b/extern/YMF276-LLE/fmopn2.c new file mode 100644 index 000000000..2a11366c1 --- /dev/null +++ b/extern/YMF276-LLE/fmopn2.c @@ -0,0 +1,2342 @@ +/* + * Copyright (C) 2022-2023 nukeykt + * + * This file is part of YMF276-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * YMF276/YM3438 emulator. + * Thanks: + * John McMaster (siliconpr0n.org): + * Yamaha YM3438 & YM2610 decap and die shot. + * org, andkorzh, HardWareMan (emu-russia): + * help & support, YMF276 and YM2612 decap. + * + */ + +// YMF276/YM3438 core + +#include +#include + +#include "fmopn2.h" + +enum { + eg_state_attack = 0, + eg_state_decay, + eg_state_sustain, + eg_state_release +}; + +static const int 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 */ + } +}; + +void FMOPN2_ClockPhase1(fmopn2_t *chip); + +void FMOPN2_Prescaler(fmopn2_t *chip) +{ + if (!chip->pinput.phi) + { + int ic_check; + + chip->ic_latch[0] = chip->ic_latch[1] << 1; + chip->ic_latch[0] |= chip->pinput.ic; + + ic_check = (chip->ic_latch[1] & 0x800) == 0 && chip->pinput.ic; + + chip->prescaler_latch[0] = chip->prescaler_latch[1] << 1; + chip->prescaler_latch[0] |= !ic_check && (chip->prescaler_latch[1] & 0x1f) == 0; + + chip->ic_check_latch[0] = chip->ic_check_latch[1] << 1; + chip->ic_check_latch[0] |= ic_check; + + chip->phi1_latch[0] = (chip->prescaler_latch[1] & 0x21) != 0; + chip->phi2_latch[0] = (chip->prescaler_latch[1] & 0xc) != 0; + + if (!(chip->flags & fmopn2_flags_ym3438)) + { + chip->dphi1_latch[0] = (chip->prescaler_latch[1] & 0x9) != 0; + chip->dphi2_latch[0] = (chip->prescaler_latch[1] & 0x24) != 0; + + chip->dphi1_latch[2] = chip->dphi1_latch[1]; + chip->dphi2_latch[2] = chip->dphi2_latch[1]; + } + } + else + { + chip->ic_latch[1] = chip->ic_latch[0] & 0xfff; + chip->ic_check_latch[1] = chip->ic_check_latch[0] & 0xf; + chip->prescaler_latch[1] = chip->prescaler_latch[0] & 0x3f; + chip->phi1_latch[1] = chip->phi1_latch[0] & 0x1; + chip->phi2_latch[1] = chip->phi2_latch[0] & 0x1; + + if (!(chip->flags & fmopn2_flags_ym3438)) + { + chip->dphi1_latch[1] = chip->dphi1_latch[0]; + chip->dphi2_latch[1] = chip->dphi2_latch[0]; + + chip->dphi1_latch[3] = chip->dphi1_latch[2]; + } + } + + chip->fsm_reset = (chip->ic_check_latch[1] & 16) != 0; + + if (!(chip->flags & fmopn2_flags_ym3438)) + { + chip->dclk1 = chip->dphi1_latch[1] && !chip->dphi1_latch[2]; + chip->dclk2 = chip->dphi2_latch[1] && !chip->dphi2_latch[2]; + } +} + +void FMOPN2_YMF276DAC(fmopn2_t *chip) +{ + chip->fsm_load_l = (chip->fsm_shifter_ctrl[1] & 0x20) != 0 && chip->i_phi2; + chip->fsm_load_r = (chip->fsm_shifter_ctrl[1] & 0x20000) != 0 && chip->i_phi2; + + if (chip->dclk1) + { + chip->dac_shifter[0] = chip->dac_shifter[1] << 1; + + if (chip->fsm_load_l) + chip->dac_shifter[0] |= (chip->ch_accm_l[1] >> 1); + if (chip->fsm_load_r) + chip->dac_shifter[0] |= (chip->ch_accm_r[1] >> 1); + } + if (chip->dclk2) + { + chip->dac_shifter[1] = chip->dac_shifter[0]; + } + + if (!chip->pinput.phi) + { + chip->fsm_lro_l2[0] = chip->fsm_lro_l2[1] << 1; + if (chip->fsm_lro_l[1] & 2) + chip->fsm_lro_l2[0] |= 1; + chip->fsm_wco_l2[0] = chip->fsm_wco_l2[1] << 1; + if (chip->fsm_wco_l[1] & 2) + chip->fsm_wco_l2[0] |= 1; + + chip->dac_so_l[0] = chip->dac_so_l[1] << 1; + if (chip->dac_shifter[1] & 0x8000) + chip->dac_so_l[0] |= 1; + } + else + { + chip->fsm_lro_l2[1] = chip->fsm_lro_l2[0]; + chip->fsm_wco_l2[1] = chip->fsm_wco_l2[0]; + + chip->dac_so_l[1] = chip->dac_so_l[0]; + } + + chip->o_bco = chip->dphi1_latch[2] || chip->dphi1_latch[3]; + chip->o_wco = (chip->fsm_wco_l2[1] & 32) != 0; + chip->o_lro = (chip->fsm_lro_l2[1] & 32) != 0; + chip->o_so = (chip->dac_so_l[1] & 4) != 0; +} + +void FMOPN2_HandleIO(fmopn2_t *chip) +{ + int write_data = chip->input.cs && chip->input.wr && (chip->input.address & 1) == 1 && !chip->input.ic; + int write_addr = (chip->input.cs && chip->input.wr && (chip->input.address & 1) == 0) || chip->input.ic; + int read_enable = chip->input.cs && chip->input.rd && !chip->input.ic; + int io_dir = chip->input.cs && chip->input.rd && !chip->input.ic; + int data_enable = !io_dir && !chip->input.ic; + + if (chip->input.cs && chip->input.wr) + { + chip->data_latch = chip->input.data; + chip->bank_latch = (chip->input.address >> 1) & 1; + } + + if (write_addr) + chip->write_addr_trig = 1; + if (write_data) + chip->write_data_trig = 1; + + if (!read_enable) + { + chip->status_timer_a_dlatch = chip->timer_a_status[1]; + chip->status_timer_b_dlatch = chip->timer_b_status[1]; + } +} + +int FMOPN2_GetBus(fmopn2_t *chip) +{ + int data = 0; + int io_dir = chip->input.cs && chip->input.rd && !chip->input.ic; + int data_enable = !io_dir && !chip->input.ic; + if (data_enable) + data = chip->data_latch; + if (chip->io_ic_latch[1]) + data = 0; + return data; +} + + +int FMOPN2_ReadTest(fmopn2_t *chip) +{ + if (chip->mode_test_2c[1] & 128) + return chip->fsm_sel23; + return 0; // FIXME: high impedance +} + +int FMOPN2_ReadStatus(fmopn2_t *chip) +{ + int io_dir = chip->input.cs && chip->input.rd && !chip->input.ic; + int read_enable = chip->input.cs && chip->input.rd && !chip->input.ic && (chip->input.address & 3) == 0; + int status; + int testdata = 0; + if (!io_dir) + return 0; + + if (!read_enable) + { + return 0; // FIXME: floating bus + } + if (chip->mode_test_21[1] & 64) + { + testdata |= (chip->pg_debug[1] & 1) << 15; + if (chip->mode_test_21[1] & 1) + testdata |= ((chip->eg_debug[1] >> 9) & 1) << 14; + else + testdata |= (chip->eg_incsh_nonzero[1] & 1) << 14; + if (chip->mode_test_2c[1] & 16) + testdata |= chip->ch_out_debug[1] & 0x1ff; + else + testdata |= chip->op_output[1] & 0x3fff; + if (chip->mode_test_21[1] & 128) + status = testdata & 255; + else + status = testdata >> 8; + } + else + { + status = (chip->busy_latch[1] << 7) | (chip->status_timer_b_dlatch << 1) | chip->status_timer_a_dlatch; + } + return status; +} + +void FMOPN2_FSM1(fmopn2_t *chip) +{ + int i; + int connect = 0; + int reset = chip->input.i_fsm_reset; + chip->fsm_cnt1[0] = chip->fsm_cnt1[1] + 1; + if (reset || (chip->fsm_cnt1[1] & 2) != 0) + chip->fsm_cnt1[0] = 0; + chip->fsm_cnt2[0] = chip->fsm_cnt2[1]; + if ((chip->fsm_cnt1[1] & 2) != 0) + chip->fsm_cnt2[0]++; + if (reset) + chip->fsm_cnt2[0] = 0; + + if (!(chip->flags & fmopn2_flags_ym3438)) + { + int cnt_comb = (chip->fsm_cnt2[1] << 2) | chip->fsm_cnt1[1]; + + chip->fsm_clock_eg_l = cnt_comb == 30; + chip->fsm_op1_sel_l = cnt_comb == 6 || cnt_comb == 8 || cnt_comb == 9 || cnt_comb == 10 || cnt_comb == 12 || cnt_comb == 13; + chip->fsm_sel1_l = cnt_comb == 0; + chip->fsm_sel2_l = cnt_comb == 1; + chip->fsm_sel23_l = cnt_comb == 29; + chip->fsm_ch3_sel_l = cnt_comb == 1 || cnt_comb == 9 || cnt_comb == 17 || cnt_comb == 25; + chip->fsm_dac_load_l = cnt_comb == 30 || cnt_comb == 4 || cnt_comb == 9 || cnt_comb == 14 || cnt_comb == 20 || cnt_comb == 9; + chip->fsm_dac_out_sel_l = cnt_comb == 14 || cnt_comb == 16 || cnt_comb == 17 || cnt_comb == 18 || cnt_comb == 20 || cnt_comb == 21 || + cnt_comb == 22 || cnt_comb == 24 || cnt_comb == 25 || cnt_comb == 26 || cnt_comb == 28 || cnt_comb == 29; + chip->fsm_dac_ch6_l = cnt_comb == 4 || cnt_comb == 5 || cnt_comb == 6 || cnt_comb == 8; + + chip->fsm_wco_l[0] = (chip->fsm_wco_l[1] << 1) | ((cnt_comb & 8) == 0); + chip->fsm_lro_l[0] = chip->fsm_lro_l[1] << 1; + if (((cnt_comb >> 3) ^ (cnt_comb >> 4)) & 1) + chip->fsm_lro_l[0] |= 1; + } + +} + +void FMOPN2_FSM2(fmopn2_t *chip) +{ + int i, connect = 0; + int cnt_comb; + chip->fsm_cnt1[1] = chip->fsm_cnt1[0] & 0x3; + chip->fsm_cnt2[1] = chip->fsm_cnt2[0] & 0x7; + + cnt_comb = (chip->fsm_cnt2[1] << 2) | chip->fsm_cnt1[1]; + + if (!(chip->flags & fmopn2_flags_ym3438)) + { + chip->fsm_clock_eg = chip->fsm_clock_eg_l; + chip->fsm_op4_sel = cnt_comb == 0 || cnt_comb == 1 || cnt_comb == 2 || cnt_comb == 4 || cnt_comb == 5 || cnt_comb == 6; + chip->fsm_op1_sel = chip->fsm_op1_sel_l; + chip->fsm_op3_sel = cnt_comb == 16 || cnt_comb == 17 || cnt_comb == 18 || cnt_comb == 20 || cnt_comb == 21 || cnt_comb == 22; + chip->fsm_op2_sel = cnt_comb == 24 || cnt_comb == 25 || cnt_comb == 26 || cnt_comb == 28 || cnt_comb == 29 || cnt_comb == 30; + chip->fsm_sel2 = chip->fsm_sel2_l; + chip->fsm_sel23 = chip->fsm_sel23_l; + chip->fsm_ch3_sel = chip->fsm_ch3_sel_l; + chip->fsm_dac_load = chip->fsm_dac_load_l; + chip->fsm_dac_out_sel = chip->fsm_dac_out_sel_l; + chip->fsm_dac_ch6 = chip->fsm_dac_ch6_l; + chip->fsm_clock_timers = chip->fsm_sel2_l; + chip->fsm_clock_timers1 = chip->fsm_sel1_l; + + chip->fsm_wco_l[1] = chip->fsm_wco_l[0]; + chip->fsm_lro_l[1] = chip->fsm_lro_l[0]; + } + else + { + chip->fsm_clock_eg = cnt_comb == 0; + chip->fsm_op4_sel = cnt_comb == 0 || cnt_comb == 1 || cnt_comb == 2 || cnt_comb == 4 || cnt_comb == 5 || cnt_comb == 6; + chip->fsm_op1_sel = cnt_comb == 8 || cnt_comb == 9 || cnt_comb == 10 || cnt_comb == 12 || cnt_comb == 13 || cnt_comb == 14; + chip->fsm_op3_sel = cnt_comb == 16 || cnt_comb == 17 || cnt_comb == 18 || cnt_comb == 20 || cnt_comb == 21 || cnt_comb == 22; + chip->fsm_op2_sel = cnt_comb == 24 || cnt_comb == 25 || cnt_comb == 26 || cnt_comb == 28 || cnt_comb == 29 || cnt_comb == 30; + chip->fsm_sel2 = cnt_comb == 2; + chip->fsm_sel23 = cnt_comb == 30; + chip->fsm_ch3_sel = cnt_comb == 2 || cnt_comb == 10 || cnt_comb == 18 || cnt_comb == 26; + chip->fsm_dac_load = cnt_comb == 0 || cnt_comb == 5 || cnt_comb == 10 || cnt_comb == 16 || cnt_comb == 21 || cnt_comb == 26; + chip->fsm_dac_out_sel = cnt_comb == 16 || cnt_comb == 17 || cnt_comb == 18 || cnt_comb == 20 || cnt_comb == 21 || cnt_comb == 22 || + cnt_comb == 24 || cnt_comb == 25 || cnt_comb == 26 || cnt_comb == 28 || cnt_comb == 29 || cnt_comb == 30; + chip->fsm_dac_ch6 = cnt_comb == 5 || cnt_comb == 6 || cnt_comb == 8 || cnt_comb == 9; + chip->fsm_clock_timers = cnt_comb == 2; + chip->fsm_clock_timers1 = cnt_comb == 1; + } + + for (i = 0; i < 3; i++) + connect |= ((chip->chan_connect[i][1] >> 5) & 1) << i; + + chip->alg_mod_op1_0 = 0; + chip->alg_mod_op1_1 = 0; + chip->alg_mod_op2 = 0; + chip->alg_mod_prev_0 = 0; + chip->alg_mod_prev_1 = 0; + chip->alg_output = 0; + + if (chip->fsm_op2_sel) + { + chip->alg_mod_op1_0 |= fm_algorithm[0][0][connect]; + chip->alg_mod_op1_1 |= fm_algorithm[0][1][connect]; + chip->alg_mod_op2 |= fm_algorithm[0][2][connect]; + chip->alg_mod_prev_0 |= fm_algorithm[0][3][connect]; + chip->alg_mod_prev_1 |= fm_algorithm[0][4][connect]; + chip->alg_output |= fm_algorithm[2][5][connect]; + } + if (chip->fsm_op4_sel) + { + chip->alg_mod_op1_0 |= fm_algorithm[1][0][connect]; + chip->alg_mod_op1_1 |= fm_algorithm[1][1][connect]; + chip->alg_mod_op2 |= fm_algorithm[1][2][connect]; + chip->alg_mod_prev_0 |= fm_algorithm[1][3][connect]; + chip->alg_mod_prev_1 |= fm_algorithm[1][4][connect]; + chip->alg_output |= fm_algorithm[3][5][connect]; + } + if (chip->fsm_op1_sel) + { + chip->alg_mod_op1_0 |= fm_algorithm[2][0][connect]; + chip->alg_mod_op1_1 |= fm_algorithm[2][1][connect]; + chip->alg_mod_op2 |= fm_algorithm[2][2][connect]; + chip->alg_mod_prev_0 |= fm_algorithm[2][3][connect]; + chip->alg_mod_prev_1 |= fm_algorithm[2][4][connect]; + chip->alg_output |= fm_algorithm[0][5][connect]; + } + if (chip->fsm_op3_sel) + { + chip->alg_mod_op1_0 |= fm_algorithm[3][0][connect]; + chip->alg_mod_op1_1 |= fm_algorithm[3][1][connect]; + chip->alg_mod_op2 |= fm_algorithm[3][2][connect]; + chip->alg_mod_prev_0 |= fm_algorithm[3][3][connect]; + chip->alg_mod_prev_1 |= fm_algorithm[3][4][connect]; + chip->alg_output |= fm_algorithm[1][5][connect]; + } +} + +void FMOPN2_HandleIO1(fmopn2_t *chip) +{ + int write_data_en = !chip->write_data_sr[1] && chip->write_data_dlatch; + int write_addr_en = !chip->write_addr_sr[1] && chip->write_addr_dlatch; + int busy_cnt = chip->busy_cnt[1] + chip->busy_latch[1]; + int busy_of = (busy_cnt >> 5) & 1; + chip->write_addr_trig_sync = chip->write_addr_trig; + chip->write_data_trig_sync = chip->write_data_trig; + chip->write_addr_sr[0] = chip->write_addr_dlatch; + chip->write_data_sr[0] = chip->write_data_dlatch; + + chip->busy_latch[0] = write_data_en || (chip->busy_latch[1] && !(chip->input.ic || busy_of)); + if (chip->input.ic) + busy_cnt = 0; + chip->busy_cnt[0] = busy_cnt & 31; + chip->io_ic_latch[0] = chip->input.ic; +} + +void FMOPN2_HandleIO2(fmopn2_t *chip) +{ + chip->write_addr_dlatch = chip->write_addr_trig_sync; + if (chip->write_addr_dlatch) + chip->write_addr_trig = 0; + chip->write_data_dlatch = chip->write_data_trig_sync; + if (chip->write_data_dlatch) + chip->write_data_trig = 0; + chip->write_addr_sr[1] = chip->write_addr_sr[0] & 1; + chip->write_data_sr[1] = chip->write_data_sr[0] & 1; + chip->busy_cnt[1] = chip->busy_cnt[0] & 31; + chip->busy_latch[1] = chip->busy_latch[0] & 1; + chip->io_ic_latch[1] = chip->io_ic_latch[0] & 1; +} + +void FMOPN2_DoShiftRegisters(fmopn2_t *chip, int sel) +{ + int i, j; + int to = sel; + int from = sel ^ 1; + int rot = sel == 0 ? 1 : 0; +#define SLOT_ROTATE(x) rot ? ((x << 1) | ((x >> 11) & 1)) : x +#define CH_ROTATE(x) rot ? ((x << 1) | ((x >> 5) & 1)) : x + // slot registers + for (i = 0; i < 2; i++) + { + // multi + for (j = 0; j < 4; j++) + chip->slot_multi[i][j][to] = SLOT_ROTATE(chip->slot_multi[i][j][from]); + // dt + for (j = 0; j < 3; j++) + chip->slot_dt[i][j][to] = SLOT_ROTATE(chip->slot_dt[i][j][from]); + // tl + for (j = 0; j < 7; j++) + chip->slot_tl[i][j][to] = SLOT_ROTATE(chip->slot_tl[i][j][from]); + // ar + for (j = 0; j < 5; j++) + chip->slot_ar[i][j][to] = SLOT_ROTATE(chip->slot_ar[i][j][from]); + // ks + for (j = 0; j < 2; j++) + chip->slot_ks[i][j][to] = SLOT_ROTATE(chip->slot_ks[i][j][from]); + // dr + for (j = 0; j < 5; j++) + chip->slot_dr[i][j][to] = SLOT_ROTATE(chip->slot_dr[i][j][from]); + // am + for (j = 0; j < 1; j++) + chip->slot_am[i][j][to] = SLOT_ROTATE(chip->slot_am[i][j][from]); + // sr + for (j = 0; j < 5; j++) + chip->slot_sr[i][j][to] = SLOT_ROTATE(chip->slot_sr[i][j][from]); + // rr + for (j = 0; j < 4; j++) + chip->slot_rr[i][j][to] = SLOT_ROTATE(chip->slot_rr[i][j][from]); + // sl + for (j = 0; j < 4; j++) + chip->slot_sl[i][j][to] = SLOT_ROTATE(chip->slot_sl[i][j][from]); + // ssg eg + for (j = 0; j < 4; j++) + chip->slot_ssg_eg[i][j][to] = SLOT_ROTATE(chip->slot_ssg_eg[i][j][from]); + } + // channel registers + + // fnum + for (j = 0; j < 11; j++) + chip->chan_fnum[j][to] = CH_ROTATE(chip->chan_fnum[j][from]); + // fnum ch3 + for (j = 0; j < 11; j++) + chip->chan_fnum_ch3[j][to] = CH_ROTATE(chip->chan_fnum_ch3[j][from]); + // block + for (j = 0; j < 3; j++) + chip->chan_block[j][to] = CH_ROTATE(chip->chan_block[j][from]); + // block ch3 + for (j = 0; j < 3; j++) + chip->chan_block_ch3[j][to] = CH_ROTATE(chip->chan_block_ch3[j][from]); + // connect + for (j = 0; j < 3; j++) + chip->chan_connect[j][to] = CH_ROTATE(chip->chan_connect[j][from]); + // fb + for (j = 0; j < 3; j++) + chip->chan_fb[j][to] = CH_ROTATE(chip->chan_fb[j][from]); + // pms + for (j = 0; j < 3; j++) + chip->chan_pms[j][to] = CH_ROTATE(chip->chan_pms[j][from]); + // ams + for (j = 0; j < 2; j++) + chip->chan_ams[j][to] = CH_ROTATE(chip->chan_ams[j][from]); + // pan + for (j = 0; j < 2; j++) + chip->chan_pan[j][to] = CH_ROTATE(chip->chan_pan[j][from]); +#undef SLOT_ROTATE +#undef CH_ROTATE +} + +void FMOPN2_FMRegisters1(fmopn2_t *chip) +{ + int i, j; + int write_data_en = !chip->write_data_sr[1] && chip->write_data_dlatch; + int write_addr_en = !chip->write_addr_sr[1] && chip->write_addr_dlatch; + int bus = FMOPN2_GetBus(chip); + int address = bus | (chip->bank_latch << 8); + int fm_write = (bus & 0xf0) != 0; + + + if (write_addr_en) + chip->write_fm_address[0] = fm_write; + else + chip->write_fm_address[0] = chip->write_fm_address[1]; + + if (chip->input.ic) + chip->fm_address[0] = 0; + else if (fm_write && write_addr_en) + chip->fm_address[0] = address; + else + chip->fm_address[0] = chip->fm_address[1]; + + chip->write_fm_data[0] = (chip->write_fm_address[1] && write_data_en) || (chip->write_fm_data[1] && !write_addr_en); + + if (chip->input.ic) + chip->fm_data[0] = 0; + else if (chip->write_fm_address[1] && write_data_en) + chip->fm_data[0] = bus; + else + chip->fm_data[0] = chip->fm_data[1]; + + if (write_addr_en) + { + chip->write_mode_21[0] = address == 0x21; + chip->write_mode_22[0] = address == 0x22; + chip->write_mode_24[0] = address == 0x24; + chip->write_mode_25[0] = address == 0x25; + chip->write_mode_26[0] = address == 0x26; + chip->write_mode_27[0] = address == 0x27; + chip->write_mode_28[0] = address == 0x28; + chip->write_mode_2a[0] = address == 0x2a; + chip->write_mode_2b[0] = address == 0x2b; + chip->write_mode_2c[0] = address == 0x2c; + } + else + { + chip->write_mode_21[0] = chip->write_mode_21[1]; + chip->write_mode_22[0] = chip->write_mode_22[1]; + chip->write_mode_24[0] = chip->write_mode_24[1]; + chip->write_mode_25[0] = chip->write_mode_25[1]; + chip->write_mode_26[0] = chip->write_mode_26[1]; + chip->write_mode_27[0] = chip->write_mode_27[1]; + chip->write_mode_28[0] = chip->write_mode_28[1]; + chip->write_mode_2a[0] = chip->write_mode_2a[1]; + chip->write_mode_2b[0] = chip->write_mode_2b[1]; + chip->write_mode_2c[0] = chip->write_mode_2c[1]; + } + + if (chip->input.ic) + { + chip->mode_test_21[0] = 0; + chip->mode_lfo_en[0] = 0; + chip->mode_lfo_freq[0] = 0; + chip->mode_timer_a_reg[0] = 0; + chip->mode_timer_b_reg[0] = 0; + chip->mode_ch3[0] = 0; + chip->mode_timer_a_load[0] = 0; + chip->mode_timer_a_enable[0] = 0; + chip->mode_timer_a_reset[0] = 0; + chip->mode_timer_b_load[0] = 0; + chip->mode_timer_b_enable[0] = 0; + chip->mode_timer_b_reset[0] = 0; + chip->mode_kon_operator[0] = 0; + chip->mode_kon_channel[0] = 0; + chip->mode_dac_data[0] = 0; + chip->mode_dac_en[0] = 0; + chip->mode_test_2c[0] = 0; + // slot registers + for (i = 0; i < 2; i++) + { + // multi + for (j = 0; j < 4; j++) + chip->slot_multi[i][j][0] &= ~1; + // dt + for (j = 0; j < 3; j++) + chip->slot_dt[i][j][0] &= ~1; + // tl + for (j = 0; j < 7; j++) + chip->slot_tl[i][j][0] &= ~1; + // ar + for (j = 0; j < 5; j++) + chip->slot_ar[i][j][0] &= ~1; + // ks + for (j = 0; j < 2; j++) + chip->slot_ks[i][j][0] &= ~1; + // dr + for (j = 0; j < 5; j++) + chip->slot_dr[i][j][0] &= ~1; + // am + for (j = 0; j < 1; j++) + chip->slot_am[i][j][0] &= ~1; + // sr + for (j = 0; j < 5; j++) + chip->slot_sr[i][j][0] &= ~1; + // rr + for (j = 0; j < 4; j++) + chip->slot_rr[i][j][0] &= ~1; + // sl + for (j = 0; j < 4; j++) + chip->slot_sl[i][j][0] &= ~1; + // ssg eg + for (j = 0; j < 4; j++) + chip->slot_ssg_eg[i][j][0] &= ~1; + } + // channel registers + + // fn low + for (j = 0; j < 11; j++) + chip->chan_fnum[j][0] &= ~1; + // fn low ch3 + for (j = 0; j < 11; j++) + chip->chan_fnum_ch3[j][0] &= ~1; + // block fn high + for (j = 0; j < 3; j++) + chip->chan_block[j][0] &= ~1; + // block fn high ch3 + for (j = 0; j < 3; j++) + chip->chan_block_ch3[j][0] &= ~1; + // connect + for (j = 0; j < 3; j++) + chip->chan_connect[j][0] &= ~1; + // fb + for (j = 0; j < 3; j++) + chip->chan_fb[j][0] &= ~1; + // pms + for (j = 0; j < 3; j++) + chip->chan_pms[j][0] &= ~1; + // ams + for (j = 0; j < 2; j++) + chip->chan_ams[j][0] &= ~1; + // pan + for (j = 0; j < 2; j++) + chip->chan_pan[j][0] &= ~1; + + chip->chan_a4[0] = 0; + chip->chan_ac[0] = 0; + } + else + { + if (write_data_en && chip->write_mode_21[1] && !chip->bank_latch) + chip->mode_test_21[0] = bus & 255; + else + chip->mode_test_21[0] = chip->mode_test_21[1]; + if (write_data_en && chip->write_mode_22[1] && !chip->bank_latch) + { + chip->mode_lfo_en[0] = (bus >> 3) & 1; + chip->mode_lfo_freq[0] = bus & 7; + } + else + { + chip->mode_lfo_en[0] = chip->mode_lfo_en[1]; + chip->mode_lfo_freq[0] = chip->mode_lfo_freq[1]; + } + chip->mode_timer_a_reg[0] = 0; + if (write_data_en && chip->write_mode_24[1] && !chip->bank_latch) + chip->mode_timer_a_reg[0] |= (bus & 255) << 2; + else + chip->mode_timer_a_reg[0] |= chip->mode_timer_a_reg[1] & 0x3fc; + if (write_data_en && chip->write_mode_25[1] && !chip->bank_latch) + chip->mode_timer_a_reg[0] |= bus & 3; + else + chip->mode_timer_a_reg[0] |= chip->mode_timer_a_reg[1] & 0x3; + if (write_data_en && chip->write_mode_26[1] && !chip->bank_latch) + chip->mode_timer_b_reg[0] = bus & 255; + else + chip->mode_timer_b_reg[0] = chip->mode_timer_b_reg[1]; + if (write_data_en && chip->write_mode_27[1] && !chip->bank_latch) + { + chip->mode_ch3[0] = (bus >> 6) & 3; + chip->mode_timer_a_load[0] = bus & 1; + chip->mode_timer_a_enable[0] = (bus >> 2) & 1; + chip->mode_timer_a_reset[0] = (bus >> 4) & 1; + chip->mode_timer_b_load[0] = (bus >> 1) & 1; + chip->mode_timer_b_enable[0] = (bus >> 3) & 1; + chip->mode_timer_b_reset[0] = (bus >> 5) & 1; + } + else + { + chip->mode_ch3[0] = chip->mode_ch3[1]; + chip->mode_timer_a_load[0] = chip->mode_timer_a_load[1]; + chip->mode_timer_a_enable[0] = chip->mode_timer_a_enable[1]; + chip->mode_timer_a_reset[0] = 0; + chip->mode_timer_b_load[0] = chip->mode_timer_b_load[1]; + chip->mode_timer_b_enable[0] = chip->mode_timer_b_enable[1]; + chip->mode_timer_b_reset[0] = 0; + } + if (write_data_en && chip->write_mode_28[1] && !chip->bank_latch) + { + chip->mode_kon_operator[0] = (bus >> 4) & 15; + chip->mode_kon_channel[0] = bus & 15; + } + else + { + chip->mode_kon_operator[0] = chip->mode_kon_operator[1]; + chip->mode_kon_channel[0] = chip->mode_kon_channel[1]; + } + if (write_data_en && chip->write_mode_2a[1] && !chip->bank_latch) + chip->mode_dac_data[0] = (bus & 255) ^ 128; + else + chip->mode_dac_data[0] = chip->mode_dac_data[1]; + if (write_data_en && chip->write_mode_2b[1] && !chip->bank_latch) + chip->mode_dac_en[0] = (bus >> 7) & 1; + else + chip->mode_dac_en[0] = chip->mode_dac_en[1]; + if (write_data_en && chip->write_mode_2c[1] && !chip->bank_latch) + chip->mode_test_2c[0] = bus & 0xf8; + else + chip->mode_test_2c[0] = chip->mode_test_2c[1]; + } + if (chip->write_fm_data[1] && (chip->fm_address[1]&3) == chip->reg_cnt1[1] + && ((chip->fm_address[1]>>2)&1) == ((chip->reg_cnt2[1]>>1)&1) && ((chip->fm_address[1]>>8)&1) == (chip->reg_cnt2[1]&1)) + { + int bank = (chip->fm_address[1]>>3)&1; + switch (chip->fm_address[1] & 0xf0) + { + case 0x30: + // multi + for (j = 0; j < 4; j++) + { + chip->slot_multi[bank][j][0] &= ~1; + chip->slot_multi[bank][j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + // dt + for (j = 0; j < 3; j++) + { + chip->slot_dt[bank][j][0] &= ~1; + chip->slot_dt[bank][j][0] |= (chip->fm_data[1] >> (j + 4)) & 1; + } + break; + case 0x40: + // tl + for (j = 0; j < 7; j++) + { + chip->slot_tl[bank][j][0] &= ~1; + chip->slot_tl[bank][j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + break; + case 0x50: + // ar + for (j = 0; j < 5; j++) + { + chip->slot_ar[bank][j][0] &= ~1; + chip->slot_ar[bank][j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + // ks + for (j = 0; j < 2; j++) + { + chip->slot_ks[bank][j][0] &= ~1; + chip->slot_ks[bank][j][0] |= (chip->fm_data[1] >> (j + 6)) & 1; + } + break; + case 0x60: + // dr + for (j = 0; j < 5; j++) + { + chip->slot_dr[bank][j][0] &= ~1; + chip->slot_dr[bank][j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + // am + for (j = 0; j < 1; j++) + { + chip->slot_am[bank][j][0] &= ~1; + chip->slot_am[bank][j][0] |= (chip->fm_data[1] >> (j + 7)) & 1; + } + break; + case 0x70: + // sr + for (j = 0; j < 5; j++) + { + chip->slot_sr[bank][j][0] &= ~1; + chip->slot_sr[bank][j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + break; + case 0x80: + // rr + for (j = 0; j < 4; j++) + { + chip->slot_rr[bank][j][0] &= ~1; + chip->slot_rr[bank][j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + // sl + for (j = 0; j < 4; j++) + { + chip->slot_sl[bank][j][0] &= ~1; + chip->slot_sl[bank][j][0] |= (chip->fm_data[1] >> (j + 4)) & 1; + } + break; + case 0x90: + // ssg eg + for (j = 0; j < 4; j++) + { + chip->slot_ssg_eg[bank][j][0] &= ~1; + chip->slot_ssg_eg[bank][j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + break; + } + } + if (chip->write_fm_data[1] && (chip->fm_address[1] & 3) == chip->reg_cnt1[1] && ((chip->fm_address[1] >> 8) & 1) == (chip->reg_cnt2[1] & 1)) + { + switch (chip->fm_address[1] & 0xfc) + { + case 0xa0: + // fnum + for (j = 0; j < 8; j++) + { + chip->chan_fnum[j][0] &= ~1; + chip->chan_fnum[j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + for (j = 0; j < 3; j++) + { + chip->chan_fnum[8+j][0] &= ~1; + chip->chan_fnum[8+j][0] |= (chip->chan_a4[1] >> (j + 0)) & 1; + } + // block + for (j = 0; j < 3; j++) + { + chip->chan_block[j][0] &= ~1; + chip->chan_block[j][0] |= (chip->chan_a4[1] >> (j + 3)) & 1; + } + break; + case 0xa4: + chip->chan_a4[0] = chip->fm_data[1] & 0x3f; + break; + case 0xa8: + // fnum + for (j = 0; j < 8; j++) + { + chip->chan_fnum_ch3[j][0] &= ~1; + chip->chan_fnum_ch3[j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + for (j = 0; j < 3; j++) + { + chip->chan_fnum_ch3[8+j][0] &= ~1; + chip->chan_fnum_ch3[8+j][0] |= (chip->chan_ac[1] >> (j + 0)) & 1; + } + // block + for (j = 0; j < 3; j++) + { + chip->chan_block_ch3[j][0] &= ~1; + chip->chan_block_ch3[j][0] |= (chip->chan_ac[1] >> (j + 3)) & 1; + } + break; + case 0xac: + chip->chan_ac[0] = chip->fm_data[1] & 0x3f; + break; + case 0xb0: + // connect + for (j = 0; j < 3; j++) + { + chip->chan_connect[j][0] &= ~1; + chip->chan_connect[j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + // fb + for (j = 0; j < 3; j++) + { + chip->chan_fb[j][0] &= ~1; + chip->chan_fb[j][0] |= (chip->fm_data[1] >> (j + 3)) & 1; + } + break; + case 0xb4: + // pms + for (j = 0; j < 3; j++) + { + chip->chan_pms[j][0] &= ~1; + chip->chan_pms[j][0] |= (chip->fm_data[1] >> (j + 0)) & 1; + } + // ams + for (j = 0; j < 2; j++) + { + chip->chan_ams[j][0] &= ~1; + chip->chan_ams[j][0] |= (chip->fm_data[1] >> (j + 4)) & 1; + } + // pan + for (j = 0; j < 2; j++) + { + chip->chan_pan[j][0] &= ~1; + chip->chan_pan[j][0] |= !((chip->fm_data[1] >> (j + 6)) & 1); + } + break; + } + } + // keyon + chip->mode_kon[0][0] = chip->mode_kon[0][1] << 1; + chip->mode_kon[1][0] = chip->mode_kon[1][1] << 1; + chip->mode_kon[2][0] = chip->mode_kon[2][1] << 1; + chip->mode_kon[3][0] = chip->mode_kon[3][1] << 1; + if (chip->reg_cnt2[1] == ((chip->mode_kon_channel[1] >> 2) & 1) && chip->reg_cnt1[1] == (chip->mode_kon_channel[1] & 3)) + { + chip->mode_kon[0][0] |= (chip->mode_kon_operator[1] >> 0) & 1; + chip->mode_kon[1][0] |= (chip->mode_kon_operator[1] >> 3) & 1; + chip->mode_kon[2][0] |= (chip->mode_kon_operator[1] >> 1) & 1; + chip->mode_kon[3][0] |= (chip->mode_kon_operator[1] >> 2) & 1; + } + else + { + if (!chip->input.ic) + chip->mode_kon[0][0] |= (chip->mode_kon[3][1] >> 5) & 1; + chip->mode_kon[1][0] |= (chip->mode_kon[0][1] >> 5) & 1; + chip->mode_kon[2][0] |= (chip->mode_kon[1][1] >> 5) & 1; + chip->mode_kon[3][0] |= (chip->mode_kon[2][1] >> 5) & 1; + } +} + +void FMOPN2_FMRegisters2(fmopn2_t *chip) +{ + chip->write_fm_address[1] = chip->write_fm_address[0]; + chip->write_fm_data[1] = chip->write_fm_data[0]; + chip->fm_address[1] = chip->fm_address[0]; + chip->fm_data[1] = chip->fm_data[0]; + chip->write_mode_21[1] = chip->write_mode_21[0]; + chip->write_mode_22[1] = chip->write_mode_22[0]; + chip->write_mode_24[1] = chip->write_mode_24[0]; + chip->write_mode_25[1] = chip->write_mode_25[0]; + chip->write_mode_26[1] = chip->write_mode_26[0]; + chip->write_mode_27[1] = chip->write_mode_27[0]; + chip->write_mode_28[1] = chip->write_mode_28[0]; + chip->write_mode_2a[1] = chip->write_mode_2a[0]; + chip->write_mode_2b[1] = chip->write_mode_2b[0]; + chip->write_mode_2c[1] = chip->write_mode_2c[0]; + chip->mode_test_21[1] = chip->mode_test_21[0]; + chip->mode_lfo_en[1] = chip->mode_lfo_en[0]; + chip->mode_lfo_freq[1] = chip->mode_lfo_freq[0]; + chip->mode_timer_a_reg[1] = chip->mode_timer_a_reg[0]; + chip->mode_timer_b_reg[1] = chip->mode_timer_b_reg[0]; + chip->mode_ch3[1] = chip->mode_ch3[0]; + chip->mode_timer_a_load[1] = chip->mode_timer_a_load[0]; + chip->mode_timer_a_enable[1] = chip->mode_timer_a_enable[0]; + chip->mode_timer_a_reset[1] = chip->mode_timer_a_reset[0]; + chip->mode_timer_b_load[1] = chip->mode_timer_b_load[0]; + chip->mode_timer_b_enable[1] = chip->mode_timer_b_enable[0]; + chip->mode_timer_b_reset[1] = chip->mode_timer_b_reset[0]; + chip->mode_kon_operator[1] = chip->mode_kon_operator[0]; + chip->mode_kon_channel[1] = chip->mode_kon_channel[0]; + chip->mode_dac_data[1] = chip->mode_dac_data[0]; + chip->mode_dac_en[1] = chip->mode_dac_en[0]; + chip->mode_test_2c[1] = chip->mode_test_2c[0]; + chip->chan_a4[1] = chip->chan_a4[0]; + chip->chan_ac[1] = chip->chan_ac[0]; + chip->mode_kon[0][1] = chip->mode_kon[0][0]; + chip->mode_kon[1][1] = chip->mode_kon[1][0]; + chip->mode_kon[2][1] = chip->mode_kon[2][0]; + chip->mode_kon[3][1] = chip->mode_kon[3][0]; +} + +void FMOPN2_Misc1(fmopn2_t *chip) +{ + chip->reg_cnt1[0] = chip->reg_cnt1[1] + 1; + chip->reg_cnt2[0] = chip->reg_cnt2[1]; + if (chip->reg_cnt1[1] & 2) + { + chip->reg_cnt1[0] = 0; + chip->reg_cnt2[0]++; + } + if (chip->fsm_sel23 || chip->input.ic) + { + chip->reg_cnt1[0] = 0; + chip->reg_cnt2[0] = 0; + } +} + +void FMOPN2_Misc2(fmopn2_t *chip) +{ + chip->reg_cnt1[1] = chip->reg_cnt1[0] & 3; + chip->reg_cnt2[1] = chip->reg_cnt2[0] & 7; +} + +void FMOPN2_LFO1(fmopn2_t *chip) +{ + static const int lfo_cycles[8] = { + 108, 77, 71, 67, 62, 44, 8, 5 + }; + int inc = (chip->mode_test_21[1] & 2) != 0 || chip->fsm_sel23; + int freq = chip->mode_lfo_freq[1]; + int of = (chip->lfo_cnt1[1] & lfo_cycles[freq]) == lfo_cycles[freq]; + + chip->lfo_cnt1[0] = chip->lfo_cnt1[1] + inc; + + if (chip->input.ic || of) + chip->lfo_cnt1[0] = 0; + + chip->lfo_cnt2[0] = chip->lfo_cnt2[1] + of; + + if (!chip->mode_lfo_en[1]) + chip->lfo_cnt2[0] = 0; + + chip->lfo_inc_latch[0] = chip->fsm_sel23; + + chip->lfo_dlatch_load = chip->lfo_inc_latch[1]; +} + +void FMOPN2_LFO2(fmopn2_t *chip) +{ + chip->lfo_cnt1[1] = chip->lfo_cnt1[0] & 127; + chip->lfo_cnt2[1] = chip->lfo_cnt2[0] & 127; + chip->lfo_inc_latch[1] = chip->lfo_inc_latch[0]; + if (chip->lfo_inc_latch[1] && !chip->lfo_dlatch_load) + chip->lfo_dlatch = chip->lfo_cnt2[1]; +} + +void FMOPN2_PhaseGenerator1(fmopn2_t *chip) +{ + // Note table + static const int fn_note[16] = { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 + }; + // LFO shift + static const int 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 int 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 } + }; +#if 0 + // YM2610 decap + static const int 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, 2, 2, 7, 7, 2, 2, 7 }, + { 7, 2, 7, 2, 7, 2, 2, 1 }, + { 7, 2, 7, 2, 7, 2, 2, 1 }, + { 7, 2, 7, 2, 7, 2, 2, 1 } + }; +#endif + static const int pg_detune_add[4] = { + 0, 8, 10, 11 + }; + static const int pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; + int i; + int fnum = 0; + int fnum_h; + int block = 0; + int pms = 0; + int dt = 0; + int dt_l = 0; + int dt_sum; + int dt_sum_l; + int dt_sum_h; + int kcode; + int multi = 0; + int lfo; + int ch3_sel = chip->reg_cnt1[1] == 1 && (chip->reg_cnt2[1] & 1) == 0 && chip->mode_ch3[1] != 0; + int op_sel = chip->reg_cnt2[1] >> 1; + int bank = (chip->reg_cnt2[1] >> 2) & 1; + int carry = 0; + int pg_inc; + int reset; + if (ch3_sel && op_sel == 0) + { + for (i = 0; i < 11; i++) + fnum |= ((chip->chan_fnum_ch3[i][1] >> 5) & 1) << i; + for (i = 0; i < 3; i++) + block |= ((chip->chan_block_ch3[i][1] >> 5) & 1) << i; + } + else if (ch3_sel && op_sel == 1) + { + for (i = 0; i < 11; i++) + fnum |= ((chip->chan_fnum_ch3[i][1] >> 0) & 1) << i; + for (i = 0; i < 3; i++) + block |= ((chip->chan_block_ch3[i][1] >> 0) & 1) << i; + } + else if (ch3_sel && op_sel == 2) + { + for (i = 0; i < 11; i++) + fnum |= ((chip->chan_fnum_ch3[i][1] >> 4) & 1) << i; + for (i = 0; i < 3; i++) + block |= ((chip->chan_block_ch3[i][1] >> 4) & 1) << i; + } + else + { + for (i = 0; i < 11; i++) + fnum |= ((chip->chan_fnum[i][1] >> 4) & 1) << i; + for (i = 0; i < 3; i++) + block |= ((chip->chan_block[i][1] >> 4) & 1) << i; + } + for (i = 0; i < 3; i++) + pms |= ((chip->chan_pms[i][1] >> 5) & 1) << i; + for (i = 0; i < 3; i++) + dt |= ((chip->slot_dt[bank][i][1] >> 11) & 1) << i; + for (i = 0; i < 4; i++) + multi |= ((chip->slot_multi[bank][i][1] >> 11) & 1) << i; + + kcode = (block << 2) | fn_note[fnum >> 7]; + + chip->pg_kcode[0][0] = kcode; + chip->pg_kcode[1][0] = chip->pg_kcode[0][1]; + + chip->pg_fnum[0][0] = fnum; + chip->pg_fnum[1][0] = chip->pg_fnum[0][1]; + + lfo = (chip->lfo_dlatch >> 2) & 7; + + if (chip->lfo_dlatch & 32) + lfo ^= 7; + + fnum_h = chip->pg_fnum[0][1] >> 4; + + chip->pg_fnum_lfo1 = fnum_h >> pg_lfo_sh1[pms][lfo]; + chip->pg_fnum_lfo2 = fnum_h >> pg_lfo_sh2[pms][lfo]; + + chip->pg_lfo_shift = 2; + if (pms > 5) + chip->pg_lfo_shift = 7 - pms; + + chip->pg_lfo_sign = (chip->lfo_dlatch >> 6) & 1; + + chip->pg_freq1 = ((chip->pg_fnum[1][1] << 1) + chip->pg_lfo) & 0xfff; + block = chip->pg_kcode[1][1] >> 2; + chip->pg_block = block; + chip->pg_dt[0] = dt; + + dt_l = chip->pg_dt[1] & 3; + kcode = chip->pg_kcode[1][1]; + if (kcode > 28) + kcode = 28; + dt_sum = kcode + ((pg_detune_add[dt_l] + 1) << 2); + dt_sum_l = dt_sum & 7; + dt_sum_h = dt_sum >> 3; + chip->pg_detune[0] = dt_l ? pg_detune[dt_sum_l] >> (9 - dt_sum_h) : 0; + if (chip->pg_dt[1] & 0x04) + chip->pg_detune[0] = -chip->pg_detune[0]; + + chip->pg_freq3 = (chip->pg_freq2 + chip->pg_detune[1]) & 0x1ffff; + + chip->pg_multi[0][0] = multi; + chip->pg_multi[1][0] = chip->pg_multi[0][1]; + chip->pg_multi2 = chip->pg_multi[1][1]; + chip->pg_inc[0] = chip->pg_freq4; + + chip->pg_inc_mask[0] = chip->pg_inc[1]; + if (chip->pg_reset[1] & 2) + chip->pg_inc_mask[0] = 0; + + chip->pg_reset_latch[0] = (chip->pg_reset[1] & 2) != 0; + + pg_inc = chip->pg_inc_mask[1]; + + reset = chip->pg_reset_latch[1] || (chip->mode_test_21[1] & 8) != 0; + + for (i = 0; i < 20; i++) + { + if (!reset) + carry += (chip->pg_phase[i][1] >> 23) & 1; + carry += pg_inc & 1; + chip->pg_phase[i][0] = (chip->pg_phase[i][1] << 1) | (carry & 1); + pg_inc >>= 1; + carry >>= 1; + } + + chip->pg_debug[0] = chip->pg_debug[1] >> 1; + if (chip->fsm_sel2) + { + for (i = 0; i < 10; i++) + { + chip->pg_debug[0] |= ((chip->pg_phase[i][1] >> 23) & 1) << i; + } + } +} + +void FMOPN2_PhaseGenerator2(fmopn2_t *chip) +{ + int i; + int block = chip->pg_kcode[1][0]; + chip->pg_fnum[0][1] = chip->pg_fnum[0][0]; + chip->pg_fnum[1][1] = chip->pg_fnum[1][0]; + chip->pg_kcode[0][1] = chip->pg_kcode[0][0]; + chip->pg_kcode[1][1] = chip->pg_kcode[1][0]; + chip->pg_lfo = (chip->pg_fnum_lfo1 + chip->pg_fnum_lfo2) >> chip->pg_lfo_shift; + if (chip->pg_lfo_sign) + chip->pg_lfo = -chip->pg_lfo; + chip->pg_freq2 = (chip->pg_freq1 << chip->pg_block) >> 2; + chip->pg_dt[1] = chip->pg_dt[0]; + chip->pg_detune[1] = chip->pg_detune[0]; + chip->pg_multi[0][1] = chip->pg_multi[0][0]; + chip->pg_multi[1][1] = chip->pg_multi[1][0]; + if (!chip->pg_multi2) + chip->pg_freq4 = chip->pg_freq3 >> 1; + else + chip->pg_freq4 = chip->pg_freq3 * chip->pg_multi2; + chip->pg_inc[1] = chip->pg_inc[0]; + chip->pg_inc_mask[1] = chip->pg_inc_mask[0]; + chip->pg_reset_latch[1] = chip->pg_reset_latch[0]; + for (i = 0; i < 20; i++) + { + chip->pg_phase[i][1] = chip->pg_phase[i][0]; + } + + chip->pg_debug[1] = chip->pg_debug[0]; +} + +void FMOPN2_EnvelopeGenerator1(fmopn2_t *chip) +{ + int i; + int sum; + int add; + int timer_bit; + int timer_bit_masked; + int bank = (chip->reg_cnt2[1] >> 2) & 1; + int rate_sel; + int rate = 0; + int ks = 0; + int b0, b1; + int inc1; + int exp = 0; + int linear = 0; + int inc_total = 0; + int level = 0; + int level2 = 0; + int kon; + int kon2; + int okon; + int okon2; + int state; + int pg_reset; + int kon_event; + int ssg_eg = 0; + int ssg_inv_e = 0; + int ssg_dir = 0; + int ssg_inv = 0; + int ssg_holdup = 0; + int ssg_enable; + int ssg_pgreset = 0; + int ssg_pgrepeat = 0; + int eg_off; + int eg_slreach; + int sl = 0; + int nextlevel = 0; + int nextstate = eg_state_attack; + int tl = 0; + int istantattack = 0; + int eg_output; + int ams = 0; + int csm_kon; + static const int eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } + }; + chip->eg_prescaler_clock_l[0] = chip->fsm_clock_eg; + chip->eg_prescaler[0] = chip->eg_prescaler[1] + chip->fsm_clock_eg; + if (((chip->eg_prescaler[1] & 2) != 0 && chip->fsm_clock_eg) || chip->input.ic) + chip->eg_prescaler[0] = 0; + chip->eg_step[0] = chip->eg_prescaler[1] >> 1; + + chip->eg_clock_delay[0] = (chip->eg_clock_delay[1] << 1) | chip->fsm_clock_eg; + + chip->eg_timer_load = chip->eg_step[1] && chip->eg_prescaler_clock_l[1]; + + sum = (chip->eg_timer[1] >> 11) & 1; + add = chip->eg_timer_carry[1]; + if ((chip->eg_prescaler[1] & 2) != 0 && chip->eg_prescaler_clock_l[1]) + add = 1; + sum += add; + chip->eg_timer_carry[0] = sum >> 1; + sum &= 1; + if (chip->input.ic || (chip->mode_test_21[1] & 32) != 0) + sum = 0; + + chip->eg_timer[0] = (chip->eg_timer[1] << 1) | sum; + + timer_bit = sum; + if (chip->mode_test_2c[1] & 64) + { + if (chip->mode_test_2c[1] & 128) // Assuming TEST pin is NC + timer_bit |= FMOPN2_ReadTest(chip); + else + timer_bit |= chip->input.test & 1; + } + + chip->eg_timer_mask[0] = timer_bit | chip->eg_timer_mask[1]; + if (chip->fsm_clock_eg || ((chip->eg_clock_delay[1]>>11) & 1) != 0 || chip->input.ic) + chip->eg_timer_mask[0] = 0; + + timer_bit_masked = timer_bit; + if (chip->eg_timer_mask[1]) + timer_bit_masked = 0; + + chip->eg_timer_masked[0] = (chip->eg_timer_masked[1] << 1) | timer_bit_masked; + + for (i = 0; i < 10; i++) + { + level2 |= ((chip->eg_level[i][1] >> 20) & 1) << i; + } + + chip->eg_level_latch[0] = level2; + + csm_kon = chip->fsm_ch3_sel && chip->timer_csm_key_dlatch; + kon2 = ((chip->mode_kon[3][1] >> 5) & 1) | csm_kon; + chip->eg_kon_latch[0] = (chip->eg_kon_latch[1] << 1) | kon2; + chip->eg_kon_csm[0] = (chip->eg_kon_csm[1] << 1) | csm_kon; + + kon = (chip->eg_kon_latch[1] >> 1) & 1; + okon = (chip->eg_key[1] >> 23) & 1; + pg_reset = (kon && !okon) || (chip->eg_ssg_pgreset[1] & 2) != 0; + kon_event = (kon && !okon) || (okon && (chip->eg_ssg_pgrepeat[1] & 2) != 0); + chip->pg_reset[0] = (chip->pg_reset[1] << 1) | pg_reset; + if (chip->eg_ssg_pgreset[1] & 2) + chip->pg_reset[0] |= 1; + chip->eg_key[0] = (chip->eg_key[1] << 1) | kon; + + okon2 = (chip->eg_key[1] >> 21) & 1; + + for (i = 0; i < 4; i++) + ssg_eg |= ((chip->slot_ssg_eg[bank][i][1] >> 11) & 1) << i; + ssg_enable = (ssg_eg & 8) != 0; + ssg_inv_e = ssg_enable && (ssg_eg & 4) != 0; + if (ssg_enable) + { + if (okon2) + { + ssg_dir = (chip->eg_ssg_dir[1] >> 23) & 1; + if (level2 & 512) + { + if ((ssg_eg & 3) == 2) + ssg_dir ^= 1; + if ((ssg_eg & 3) == 3) + ssg_dir = 1; + } + } + if (kon2) + ssg_holdup = (ssg_eg & 7) == 3 || (ssg_eg & 7) == 5; + if (level2 & 512) + { + if ((ssg_eg & 3) == 0) + ssg_pgreset = 1; + if ((ssg_eg & 1) == 0) + ssg_pgrepeat = 1; + } + } + ssg_inv = okon2 & (((chip->eg_ssg_dir[1] >> 23) & 1) ^ ssg_inv_e); + chip->eg_ssg_dir[0] = (chip->eg_ssg_dir[1] << 1) | ssg_dir; + chip->eg_ssg_inv[0] = ssg_inv; + chip->eg_ssg_holdup[0] = (chip->eg_ssg_holdup[1] << 1) | ssg_holdup; + chip->eg_ssg_enable[0] = (chip->eg_ssg_enable[1] << 1) | ssg_enable; + chip->eg_ssg_pgreset[0] = (chip->eg_ssg_pgreset[1] << 1) | ssg_pgreset; + chip->eg_ssg_pgrepeat[0] = (chip->eg_ssg_pgrepeat[1] << 1) | ssg_pgrepeat; + + chip->eg_level_ssg[0] = eg_output = chip->eg_ssg_inv[1] ? chip->eg_level_latch_inv : chip->eg_level_latch[1]; + if (chip->mode_test_21[1] & 32) + eg_output = 0; + + for (i = 0; i < 4; i++) + sl |= ((chip->slot_sl[bank][i][1] >> 11) & 1) << i; + + if (sl == 15) + sl |= 16; + + chip->eg_sl[0][0] = sl; + chip->eg_sl[1][0] = chip->eg_sl[0][1]; + + for (i = 0; i < 7; i++) + tl |= ((chip->slot_tl[bank][i][1] >> 11) & 1) << i; + + chip->eg_tl[0][0] = tl; + chip->eg_tl[1][0] = chip->eg_tl[0][1]; + + b0 = (chip->eg_state[0][1] >> 21) & 1; + b1 = (chip->eg_state[1][1] >> 21) & 1; + + rate_sel = b1 * 2 + b0; + + if (okon2) + { + if (ssg_pgrepeat) + rate_sel = eg_state_attack; + } + else + { + if (kon2) + rate_sel = eg_state_attack; + } + + switch (rate_sel) + { + case eg_state_attack: + for (i = 0; i < 5; i++) + rate |= ((chip->slot_ar[bank][i][1] >> 11) & 1) << i; + break; + case eg_state_decay: + for (i = 0; i < 5; i++) + rate |= ((chip->slot_dr[bank][i][1] >> 11) & 1) << i; + break; + case eg_state_sustain: + for (i = 0; i < 5; i++) + rate |= ((chip->slot_sr[bank][i][1] >> 11) & 1) << i; + break; + case eg_state_release: + rate = 1; + for (i = 0; i < 4; i++) + rate |= ((chip->slot_rr[bank][i][1] >> 11) & 1) << (i + 1); + break; + } + + chip->eg_rate_nonzero[0] = rate != 0; + chip->eg_rate = rate; + + for (i = 0; i < 2; i++) + ks |= ((chip->slot_ks[bank][i][1] >> 11) & 1) << i; + + chip->eg_ksv = chip->pg_kcode[0][1] >> (ks ^ 3); + + rate = chip->eg_rate2; + if (rate & 64) + rate = 63; + + sum = (rate >> 2) + chip->eg_shift_lock; + + inc1 = 0; + if (rate < 48 && chip->eg_rate_nonzero[1]) + { + switch (sum & 15) + { + case 12: + inc1 = rate != 0; + break; + case 13: + inc1 = (rate >> 1) & 1; + break; + case 14: + inc1 = rate & 1; + break; + } + } + chip->eg_inc1 = inc1; + chip->eg_inc2 = eg_stephi[rate & 3][chip->eg_timer_low_lock]; + chip->eg_rate12 = (rate & 60) == 48; + chip->eg_rate13 = (rate & 60) == 52; + chip->eg_rate14 = (rate & 60) == 56; + chip->eg_rate15 = (rate & 60) == 60; + chip->eg_maxrate[0] = (rate & 62) == 62; + chip->eg_prescaler_l = (chip->eg_prescaler[1] & 2) != 0; + + chip->eg_incsh_nonzero[0] = chip->eg_incsh0 | chip->eg_incsh1 | chip->eg_incsh2 | chip->eg_incsh3; + + + if (okon && !kon) + { + level = chip->eg_level_ssg[1]; + } + else + { + for (i = 0; i < 10; i++) + { + level |= ((chip->eg_level[i][1] >> 22) & 1) << i; + } + } + + b0 = (chip->eg_state[0][1] >> 23) & 1; + b1 = (chip->eg_state[1][1] >> 23) & 1; + + state = b1 * 2 + b0; + + exp = kon && (state == eg_state_attack) && !chip->eg_maxrate[1] && level != 0; + + eg_off = (chip->eg_ssg_enable[1] & 2) != 0 ? (level & 512) != 0 : (level & 0x3f0) == 0x3f0; + eg_slreach = (level >> 4) == (chip->eg_sl[1][1] << 1); + + linear = !kon_event && !eg_off && (state == eg_state_sustain || state == eg_state_release); + linear |= !kon_event && !eg_off && !eg_slreach && state == eg_state_decay; + + if (exp) + { + if (chip->eg_incsh0) + inc_total |= ~level >> 4; + if (chip->eg_incsh1) + inc_total |= ~level >> 3; + if (chip->eg_incsh2) + inc_total |= ~level >> 2; + if (chip->eg_incsh3) + inc_total |= ~level >> 1; + } + if (linear) + { + if (chip->eg_ssg_enable[1] & 2) + { + if (chip->eg_incsh0) + inc_total |= 4; + if (chip->eg_incsh1) + inc_total |= 8; + if (chip->eg_incsh2) + inc_total |= 16; + if (chip->eg_incsh3) + inc_total |= 32; + } + else + { + if (chip->eg_incsh0) + inc_total |= 1; + if (chip->eg_incsh1) + inc_total |= 2; + if (chip->eg_incsh2) + inc_total |= 4; + if (chip->eg_incsh3) + inc_total |= 8; + } + } + + chip->eg_inc_total = inc_total; + + istantattack = chip->eg_maxrate[1] && (!chip->eg_maxrate[1] || kon_event); + + if (!istantattack) + nextlevel |= level; + + if (chip->eg_kon_csm[1] & 2) + nextlevel |= chip->eg_tl[1][1] << 3; + + if ((!kon_event && eg_off && (chip->eg_ssg_holdup[1] & 2) == 0 && state != eg_state_attack) || chip->input.ic) + { + nextlevel = 0x3ff; + nextstate |= eg_state_release; + } + + if (!kon_event && state == eg_state_sustain) + { + nextstate |= eg_state_sustain; + } + + if (!kon_event && state == eg_state_decay && !eg_slreach) + { + nextstate |= eg_state_decay; + } + if (!kon_event && state == eg_state_decay && eg_slreach) + { + nextstate |= eg_state_sustain; + } + + if (!kon && !kon_event) + { + nextstate |= eg_state_release; + } + if (!kon_event && state == eg_state_release) + { + nextstate |= eg_state_release; + } + + if (!kon_event && state == eg_state_attack && level == 0) + { + nextstate |= eg_state_decay; + } + if (chip->input.ic) + { + nextstate |= eg_state_release; + } + + + chip->eg_nextlevel[0] = nextlevel; + b0 = nextstate & 1; + b1 = (nextstate >> 1) & 1; + chip->eg_state[0][0] = (chip->eg_state[0][1] << 1) | b0; + chip->eg_state[1][0] = (chip->eg_state[1][1] << 1) | b1; + + nextlevel = chip->eg_nextlevel[1]; + + for (i = 0; i < 10; i++) + { + chip->eg_level[i][0] = (chip->eg_level[i][1] << 1) | (nextlevel & 1); + nextlevel >>= 1; + } + + + if (chip->slot_am[bank][0][1] & (1<<11)) + for (i = 0; i < 2; i++) + ams |= ((chip->chan_ams[i][1] >> 5) & 1) << i; + + chip->eg_ams = ams; + + if (chip->lfo_dlatch & 64) + chip->eg_lfo[0] = chip->lfo_dlatch & 63; + else + chip->eg_lfo[0] = chip->lfo_dlatch ^ 63; + + chip->eg_ch3_latch[0] = chip->fsm_ch3_sel; + + chip->eg_out_tl = chip->eg_tl[0][1]; + if (chip->eg_ch3_latch[1] && chip->mode_ch3[1] == 2) // CSM + chip->eg_out_tl = 0; + + chip->eg_out = eg_output + chip->eg_lfo[1]; + + chip->eg_debug[0] = chip->eg_debug[1] << 1; + if (chip->fsm_sel2) + chip->eg_debug[0] |= chip->eg_out_total; +} + +void FMOPN2_EnvelopeGenerator2(fmopn2_t *chip) +{ + int i; + int b0, b1, b2, b3; + static const int eg_am_shift[4] = { + 7, 3, 1, 0 + }; + chip->eg_prescaler_clock_l[1] = chip->eg_prescaler_clock_l[0]; + chip->eg_prescaler[1] = chip->eg_prescaler[0] & 3; + chip->eg_step[1] = chip->eg_step[0]; + + chip->eg_timer[1] = chip->eg_timer[0]; + chip->eg_clock_delay[1] = chip->eg_clock_delay[0]; + chip->eg_timer_carry[1] = chip->eg_timer_carry[0]; + chip->eg_timer_mask[1] = chip->eg_timer_mask[0]; + chip->eg_timer_masked[1] = chip->eg_timer_masked[0]; + + if (!chip->eg_timer_load && chip->eg_step[1] && chip->eg_prescaler_clock_l[1]) + { + b0 = (chip->eg_timer[1] >> 11) & 1; + b1 = (chip->eg_timer[1] >> 10) & 1; + chip->eg_timer_low_lock = b1 * 2 + b0; + + b0 = (chip->eg_timer_masked[1] & 0xaaa) != 0; + b1 = (chip->eg_timer_masked[1] & 0x666) != 0; + b2 = (chip->eg_timer_masked[1] & 0x1e1) != 0; + b3 = (chip->eg_timer_masked[1] & 0x1f) != 0; + chip->eg_shift_lock = b3 * 8 + b2 * 4 + b1 * 2 + b0; + } + + chip->eg_rate_nonzero[1] = chip->eg_rate_nonzero[0]; + + chip->eg_rate2 = (chip->eg_rate << 1) + chip->eg_ksv; + + chip->eg_maxrate[1] = chip->eg_maxrate[0]; + + chip->eg_incsh0 = 0; + chip->eg_incsh1 = 0; + chip->eg_incsh2 = 0; + chip->eg_incsh3 = 0; + + if (chip->eg_prescaler_l) + { + chip->eg_incsh0 = chip->eg_inc1; + chip->eg_incsh3 = chip->eg_rate15; + if (!chip->eg_inc2) + { + chip->eg_incsh0 |= chip->eg_rate12; + chip->eg_incsh1 = chip->eg_rate13; + chip->eg_incsh2 = chip->eg_rate14; + } + else + { + chip->eg_incsh1 = chip->eg_rate12; + chip->eg_incsh2 = chip->eg_rate13; + chip->eg_incsh3 |= chip->eg_rate14; + } + } + + chip->eg_incsh_nonzero[1] = chip->eg_incsh_nonzero[0]; + chip->eg_kon_latch[1] = chip->eg_kon_latch[0]; + + chip->eg_level_ssg[1] = chip->eg_level_ssg[0]; + + chip->pg_reset[1] = chip->pg_reset[0]; + + chip->eg_ssg_dir[1] = chip->eg_ssg_dir[0]; + chip->eg_ssg_inv[1] = chip->eg_ssg_inv[0]; + chip->eg_ssg_holdup[1] = chip->eg_ssg_holdup[0]; + chip->eg_ssg_enable[1] = chip->eg_ssg_enable[0]; + chip->eg_ssg_pgreset[1] = chip->eg_ssg_pgreset[0]; + chip->eg_ssg_pgrepeat[1] = chip->eg_ssg_pgrepeat[0]; + + chip->eg_level_latch[1] = chip->eg_level_latch[0]; + chip->eg_level_latch_inv = (512 - chip->eg_level_latch[0]) & 0x3ff; + + chip->eg_sl[0][1] = chip->eg_sl[0][0]; + chip->eg_sl[1][1] = chip->eg_sl[1][0]; + chip->eg_tl[0][1] = chip->eg_tl[0][0]; + chip->eg_tl[1][1] = chip->eg_tl[1][0]; + + chip->eg_nextlevel[1] = chip->eg_nextlevel[0] + chip->eg_inc_total; + + chip->eg_kon_csm[1] = chip->eg_kon_csm[0]; + + for (i = 0; i < 10; i++) + { + chip->eg_level[i][1] = chip->eg_level[i][0]; + } + + chip->eg_state[0][1] = chip->eg_state[0][0]; + chip->eg_state[1][1] = chip->eg_state[1][0]; + + chip->eg_lfo[1] = (chip->eg_lfo[0] << 1) >> eg_am_shift[chip->eg_ams]; + + chip->eg_ch3_latch[1] = chip->eg_ch3_latch[0]; + + chip->eg_out_total = (chip->eg_out & 1023) + (chip->eg_out_tl << 3); + if ((chip->eg_out & 1024) != 0 || (chip->eg_out_total & 1024) != 0) + chip->eg_out_total = 1023; + + chip->eg_debug[1] = chip->eg_debug[0]; + chip->eg_key[1] = chip->eg_key[0]; +} + +void FMOPN2_Operator1(fmopn2_t *chip) +{ + int i; + int carry = 0; + int phase = 0; + int quarter; + int index; + int atten = 0; + int output; + int mod1 = 0, mod2 = 0; + int mod; + int fb = 0; + static const int logsin[128] = { + 0x6c3, 0x58b, 0x4e4, 0x471, 0x41a, 0x3d3, 0x398, 0x365, 0x339, 0x311, 0x2ed, 0x2cd, 0x2af, 0x293, 0x279, 0x261, + 0x24b, 0x236, 0x222, 0x20f, 0x1fd, 0x1ec, 0x1dc, 0x1cd, 0x1be, 0x1b0, 0x1a2, 0x195, 0x188, 0x17c, 0x171, 0x166, + 0x15b, 0x150, 0x146, 0x13c, 0x133, 0x129, 0x121, 0x118, 0x10f, 0x107, 0x0ff, 0x0f8, 0x0f0, 0x0e9, 0x0e2, 0x0db, + 0x0d4, 0x0cd, 0x0c7, 0x0c1, 0x0bb, 0x0b5, 0x0af, 0x0a9, 0x0a4, 0x09f, 0x099, 0x094, 0x08f, 0x08a, 0x086, 0x081, + 0x07d, 0x078, 0x074, 0x070, 0x06c, 0x068, 0x064, 0x060, 0x05c, 0x059, 0x055, 0x052, 0x04e, 0x04b, 0x048, 0x045, + 0x042, 0x03f, 0x03c, 0x039, 0x037, 0x034, 0x031, 0x02f, 0x02d, 0x02a, 0x028, 0x026, 0x024, 0x022, 0x020, 0x01e, + 0x01c, 0x01a, 0x018, 0x017, 0x015, 0x014, 0x012, 0x011, 0x00f, 0x00e, 0x00d, 0x00c, 0x00a, 0x009, 0x008, 0x007, + 0x007, 0x006, 0x005, 0x004, 0x004, 0x003, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000 + }; + static const int logsin_d[128] = { + 0x196, 0x07c, 0x04a, 0x035, 0x029, 0x022, 0x01d, 0x019, 0x015, 0x013, 0x012, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x009, 0x008, 0x007, 0x007, 0x007, 0x007, 0x006, 0x007, 0x006, 0x006, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x003, 0x004, 0x003, 0x003, 0x003, + 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x002, 0x002, 0x002, 0x001, + 0x001, 0x001, 0x002, 0x002, 0x001, 0x001, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x000, 0x001, 0x000, 0x001, 0x000, 0x001, 0x001, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x001, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 + }; + static const int pow[128] = { + 0x3f5, 0x3ea, 0x3df, 0x3d4, 0x3c9, 0x3bf, 0x3b4, 0x3a9, 0x39f, 0x394, 0x38a, 0x37f, 0x375, 0x36a, 0x360, 0x356, + 0x34c, 0x342, 0x338, 0x32e, 0x324, 0x31a, 0x310, 0x306, 0x2fd, 0x2f3, 0x2e9, 0x2e0, 0x2d6, 0x2cd, 0x2c4, 0x2ba, + 0x2b1, 0x2a8, 0x29e, 0x295, 0x28c, 0x283, 0x27a, 0x271, 0x268, 0x25f, 0x257, 0x24e, 0x245, 0x23c, 0x234, 0x22b, + 0x223, 0x21a, 0x212, 0x209, 0x201, 0x1f9, 0x1f0, 0x1e8, 0x1e0, 0x1d8, 0x1d0, 0x1c8, 0x1c0, 0x1b8, 0x1b0, 0x1a8, + 0x1a0, 0x199, 0x191, 0x189, 0x181, 0x17a, 0x172, 0x16b, 0x163, 0x15c, 0x154, 0x14d, 0x146, 0x13e, 0x137, 0x130, + 0x129, 0x122, 0x11b, 0x114, 0x10c, 0x106, 0x0ff, 0x0f8, 0x0f1, 0x0ea, 0x0e3, 0x0dc, 0x0d6, 0x0cf, 0x0c8, 0x0c2, + 0x0bb, 0x0b5, 0x0ae, 0x0a8, 0x0a1, 0x09b, 0x094, 0x08e, 0x088, 0x082, 0x07b, 0x075, 0x06f, 0x069, 0x063, 0x05d, + 0x057, 0x051, 0x04b, 0x045, 0x03f, 0x039, 0x033, 0x02d, 0x028, 0x022, 0x01c, 0x016, 0x011, 0x00b, 0x006, 0x000, + }; + static const int pow_d[128] = { + 0x005, 0x005, 0x005, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x006, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, 0x004, 0x004, 0x004, + 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x003, 0x003, 0x004, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, + }; + for (i = 0; i < 10; i++) + { + carry += (chip->op_mod[i][1] >> 5) & 1; + carry += (chip->pg_phase[10 + i][1] >> 19) & 1; + phase += (carry & 1) << i; + carry >>= 1; + } + chip->op_phase[0] = phase; + + chip->op_sign[0] = (chip->op_sign[1] << 1) | ((chip->op_phase[1] >> 9) & 1); + + quarter = chip->op_phase[1] & 255; + if (chip->op_phase[1] & 256) + quarter ^= 255; + + chip->op_logsin_add_delta[0] = (quarter & 1) == 0; + + chip->op_logsin_base[0] = logsin[quarter >> 1]; + chip->op_logsin_delta[0] = logsin_d[quarter >> 1]; + + chip->op_env[0] = chip->eg_out_total; + + atten = chip->op_logsin_base[1]; + if (chip->op_logsin_add_delta[1]) + atten += chip->op_logsin_delta[1]; + + atten += chip->op_env[1] << 2; + + chip->op_atten[0] = atten; + + atten = chip->op_atten[1]; + if (atten & 4096) + atten = 4095; + + index = atten & 255; + chip->op_shift[0] = atten >> 8; + + chip->op_pow_add_delta[0] = (index & 1) == 0; + + chip->op_pow_base[0] = pow[index >> 1]; + chip->op_pow_delta[0] = pow_d[index >> 1]; + + output = chip->op_pow_base[1]; + if (chip->op_pow_add_delta[1]) + output += chip->op_pow_delta[1]; + + output |= 0x400; + + output = (output << 2) >> chip->op_shift[1]; + + if (chip->mode_test_21[1] & 16) + output ^= 1 << 13; + + if (chip->op_sign[1] & 4) + { + output ^= 0x3fff; + output++; + } + + chip->op_output[0] = output; + + for (i = 0; i < 14; i++) + { + chip->op_op1[0][i][0] = chip->op_op1[0][i][1] << 1; + chip->op_op1[1][i][0] = chip->op_op1[1][i][1] << 1; + chip->op_op2[i][0] = chip->op_op2[i][1] << 1; + if (chip->fsm_op1_sel) + { + chip->op_op1[0][i][0] |= (chip->op_output[1] >> i) & 1; + chip->op_op1[1][i][0] |= (chip->op_op1[0][i][1] >> 5) & 1; + } + else + { + chip->op_op1[0][i][0] |= (chip->op_op1[0][i][1] >> 5) & 1; + chip->op_op1[1][i][0] |= (chip->op_op1[1][i][1] >> 5) & 1; + } + if (chip->fsm_op2_sel) + chip->op_op2[i][0] |= (chip->op_output[1] >> i) & 1; + else + chip->op_op2[i][0] |= (chip->op_op2[i][1] >> 5) & 1; + } + + if (chip->alg_mod_op1_0) + { + for (i = 0; i < 14; i++) + mod2 |= ((chip->op_op1[0][i][1] >> 5) & 1) << i; + } + if (chip->alg_mod_op1_1) + { + for (i = 0; i < 14; i++) + mod1 |= ((chip->op_op1[1][i][1] >> 5) & 1) << i; + } + if (chip->alg_mod_op2) + { + for (i = 0; i < 14; i++) + mod1 |= ((chip->op_op2[i][1] >> 5) & 1) << i; + } + if (chip->alg_mod_prev_0) + { + mod2 |= chip->op_output[1]; + } + if (chip->alg_mod_prev_1) + { + mod1 |= chip->op_output[1]; + } + if (mod1 & (1 << 13)) + mod1 |= 1 << 14; + if (mod2 & (1 << 13)) + mod2 |= 1 << 14; + mod = (mod1 + mod2) >> 1; + mod &= 0x3fff; + + chip->op_mod_sum[0] = mod; + chip->op_dofeedback[0] = chip->fsm_op2_sel; + + if (chip->op_dofeedback[1]) + { + for (i = 0; i < 3; i++) + fb |= (chip->chan_fb[i][1] & 1) << i; + if (!fb) + mod = 0; + else + { + mod = chip->op_mod_sum[1]; + if (mod & (1 << 13)) + mod |= ~0x3fff; + + mod = mod >> (9 - fb); + } + } + else + mod = chip->op_mod_sum[1]; + + for (i = 0; i < 10; i++) + { + chip->op_mod[i][0] = (chip->op_mod[i][1] << 1) | (mod & 1); + mod >>= 1; + } +} + +void FMOPN2_Operator2(fmopn2_t *chip) +{ + int i; + for (i = 0; i < 10; i++) + { + chip->op_mod[i][1] = chip->op_mod[i][0]; + } + for (i = 0; i < 14; i++) + { + chip->op_op1[0][i][1] = chip->op_op1[0][i][0]; + chip->op_op1[1][i][1] = chip->op_op1[1][i][0]; + chip->op_op2[i][1] = chip->op_op2[i][0]; + } + chip->op_phase[1] = chip->op_phase[0]; + chip->op_sign[1] = chip->op_sign[0]; + chip->op_logsin_add_delta[1] = chip->op_logsin_add_delta[0]; + chip->op_logsin_base[1] = chip->op_logsin_base[0]; + chip->op_logsin_delta[1] = chip->op_logsin_delta[0]; + chip->op_env[1] = chip->op_env[0]; + chip->op_atten[1] = chip->op_atten[0]; + chip->op_pow_add_delta[1] = chip->op_pow_add_delta[0]; + chip->op_pow_base[1] = chip->op_pow_base[0]; + chip->op_pow_delta[1] = chip->op_pow_delta[0]; + chip->op_shift[1] = chip->op_shift[0]; + chip->op_output[1] = chip->op_output[0]; + chip->op_mod_sum[1] = chip->op_mod_sum[0]; + chip->op_dofeedback[1] = chip->op_dofeedback[0]; +} + +void FMOPN2_YM3438Accumulator1(fmopn2_t *chip) +{ + int i; + int sum; + int inp = 0; + int acc = 0; + int test_dac = (chip->mode_test_2c[1] & 32) != 0; + int load = test_dac || chip->fsm_op1_sel; + int acc_clear = load && !test_dac; + sum = test_dac; + if (chip->alg_output && !test_dac) + inp = (chip->op_output[1] >> 5) & 511; + if (!acc_clear) + for (i = 0; i < 9; i++) + acc += ((chip->ch_accm[i][1] >> 5) & 1) << i; + + sum = test_dac + inp + acc; + + sum &= 511; + + if ((inp & 256) != 0 && (acc & 256) != 0 && (sum & 256) == 0) + sum = 256; + else if ((inp & 256) == 0 && (acc & 256) == 0 && (sum & 256) != 0) + sum = 255; + + for (i = 0; i < 9; i++) + chip->ch_accm[i][0] = (chip->ch_accm[i][1] << 1) | ((sum >> i) & 1); + + for (i = 0; i < 9; i++) + { + chip->ch_out[i][0] = chip->ch_out[i][1] << 1; + if (load) + chip->ch_out[i][0] |= (chip->ch_accm[i][1] >> 5) & 1; + else + chip->ch_out[i][0] |= (chip->ch_out[i][1] >> 5) & 1; + } + + chip->ch_dac_load = chip->fsm_dac_load; + + chip->ch_out_debug[0] = chip->ch_out_dlatch; +} + +void FMOPN2_YM3438Accumulator2(fmopn2_t* chip) +{ + int i; + int test_dac = (chip->mode_test_2c[1] & 32) != 0; + int do_out = 0; + int sign; + int out; + for (i = 0; i < 9; i++) + { + chip->ch_accm[i][1] = chip->ch_accm[i][0]; + chip->ch_out[i][1] = chip->ch_out[i][0]; + } + if ((chip->fsm_dac_load && !chip->ch_dac_load) || test_dac) + { + chip->ch_out_dlatch = 0; + if (chip->fsm_dac_out_sel || test_dac) + { + for (i = 0; i < 9; i++) + chip->ch_out_dlatch |= ((chip->ch_out[i][1] >> 5) & 1) << i; + } + else + { + for (i = 0; i < 9; i++) + chip->ch_out_dlatch |= ((chip->ch_out[i][1] >> 4) & 1) << i; + } + } + if ((chip->fsm_dac_ch6 && chip->mode_dac_en[1]) || test_dac) + { + chip->dac_val = chip->mode_dac_data[1] << 1; + chip->dac_val |= (chip->mode_test_2c[1] & 8) != 0; + } + else + chip->dac_val = chip->ch_out_dlatch; + + if (chip->fsm_dac_load && !chip->ch_dac_load) + { + chip->ch_out_pan_dlatch = 0; + if (chip->fsm_dac_out_sel) + { + for (i = 0; i < 2; i++) + chip->ch_out_pan_dlatch |= (((chip->chan_pan[i][1] >> 5) & 1) ^ 1) << i; + } + else + { + for (i = 0; i < 2; i++) + chip->ch_out_pan_dlatch |= (((chip->chan_pan[i][1] >> 4) & 1) ^ 1) << i; + } + } + + do_out = test_dac || !chip->fsm_dac_load; + if (do_out && (chip->ch_out_pan_dlatch & 2) != 0) + chip->out_l = chip->dac_val; + else + chip->out_l = 0; + if (do_out && (chip->ch_out_pan_dlatch & 1) != 0) + chip->out_r = chip->dac_val; + else + chip->out_r = 0; + + if (chip->out_l & 256) + chip->out_l |= ~0x1ff; + if (chip->out_r & 256) + chip->out_r |= ~0x1ff; + + chip->ch_out_debug[1] = chip->ch_out_debug[0]; +} + +void FMOPN2_YMF276Accumulator1(fmopn2_t *chip) +{ + int i; + int sum1; + int sum2; + int sum; + int inp = 0; + int accm = 0; + int acc = 0; + int c; + int test_dac = (chip->mode_test_2c[1] & 32) != 0; + int test_dac2 = (chip->mode_test_2c[1] & 8) != 0; + int load = test_dac || chip->fsm_op1_sel; + int acc_clear = load && !test_dac; + int sel_dac = (chip->fsm_op1_sel_l2[1] & 16) != 0 && chip->fsm_op1_sel && chip->mode_dac_en[1]; + int sel_fm = chip->fsm_op1_sel && !sel_dac; + int out = 0; + int pan = 0; + int acc_l = 0; + int acc_r = 0; + + for (i = 0; i < 14; i++) + accm += ((chip->ch_accm[i][1] >> 5) & 1) << i; + if (chip->alg_output && !test_dac) + inp = chip->op_output[1] & 0x3fff; + if (test_dac2) + inp = 0x3fff; + if (!acc_clear) + acc = accm; + + sum1 = (acc & 31) + (inp & 31) + (test_dac && !test_dac2); + c = ((sum1 & 32) != 0 || test_dac) && !test_dac2; + sum2 = (acc >> 5) + (inp >> 5) + c; + + sum = ((sum2 & 511) << 5) | (sum1 & 31); + + if ((inp & 0x2000) != 0 && (acc & 0x2000) != 0 && (sum & 0x2000) == 0) + sum = 0x2000; + else if ((inp & 0x2000) == 0 && (acc & 0x2000) == 0 && (sum & 0x2000) != 0) + sum = 0x1fff; + + for (i = 0; i < 14; i++) + chip->ch_accm[i][0] = (chip->ch_accm[i][1] << 1) | ((sum >> i) & 1); + + for (i = 0; i < 9; i++) + { + chip->ch_out[i][0] = chip->ch_out[i][1] << 1; + if (load) + chip->ch_out[i][0] |= (chip->ch_accm[i+5][1] >> 5) & 1; + else + chip->ch_out[i][0] |= (chip->ch_out[i][1] >> 5) & 1; + } + + chip->ch_dac_load = chip->fsm_dac_load; + + chip->ch_out_debug[0] = chip->ch_out_dlatch; + + chip->fsm_op1_sel_l2[0] = (chip->fsm_op1_sel_l2[1] << 1) | chip->fsm_op1_sel; + chip->fsm_op1_sel_l3[0] = chip->fsm_op1_sel; + + if (sel_dac) + out |= chip->mode_dac_data[1] << 6; + if (sel_fm) + out |= accm; + + if (out & 0x2000) + out |= 0x1c000; + + for (i = 0; i < 2; i++) + pan |= (((chip->chan_pan[i][1] >> 5) & 1) ^ 1) << i; + + chip->fsm_shifter_ctrl[0] = chip->fsm_shifter_ctrl[1] << 1; + + if (chip->fsm_op1_sel && !chip->fsm_op1_sel_l3[1]) + { + acc_l = 0; + acc_r = 0; + chip->fsm_shifter_ctrl[0] |= 1; + } + else + { + acc_l = chip->ch_accm_l[1]; + acc_r = chip->ch_accm_r[1]; + } + + chip->ch_accm_l[0] = acc_l + ((pan & 2) != 0 ? out : 0); + chip->ch_accm_r[0] = acc_r + ((pan & 1) != 0 ? out : 0); +} + +void FMOPN2_YMF276Accumulator2(fmopn2_t *chip) +{ + int i; + int test_dac = (chip->mode_test_2c[1] & 32) != 0; + for (i = 0; i < 14; i++) + { + chip->ch_accm[i][1] = chip->ch_accm[i][0]; + } + for (i = 0; i < 9; i++) + { + chip->ch_out[i][1] = chip->ch_out[i][0]; + } + if ((chip->fsm_dac_load && !chip->ch_dac_load) || test_dac) + { + chip->ch_out_dlatch = 0; + if (chip->fsm_dac_out_sel || test_dac) + { + for (i = 0; i < 9; i++) + chip->ch_out_dlatch |= ((chip->ch_out[i][1] >> 5) & 1) << i; + } + else + { + for (i = 0; i < 9; i++) + chip->ch_out_dlatch |= ((chip->ch_out[i][1] >> 4) & 1) << i; + } + } + + chip->ch_out_debug[1] = chip->ch_out_debug[0]; + + chip->fsm_op1_sel_l2[1] = chip->fsm_op1_sel_l2[0]; + chip->fsm_op1_sel_l3[1] = chip->fsm_op1_sel_l3[0]; + + chip->ch_accm_l[1] = chip->ch_accm_l[0]; + chip->ch_accm_r[1] = chip->ch_accm_r[0]; + + chip->fsm_shifter_ctrl[1] = chip->fsm_shifter_ctrl[0]; +} + +void FMOPN2_Timers1(fmopn2_t *chip) +{ + int time; + int test_timers = (chip->mode_test_21[1] & 4) != 0; + int reset; + int subcnt; + if (chip->timer_a_load_latch[1]) + time = chip->mode_timer_a_reg[1]; + else + time = chip->timer_a_cnt[1]; + + if ((chip->timer_a_load_dlatch && chip->fsm_clock_timers1) || test_timers) + time++; + + reset = chip->mode_timer_a_reset[1] || chip->input.ic; + + if (reset) + chip->timer_a_status[0] = 0; + else + chip->timer_a_status[0] = chip->timer_a_status[1] || (chip->timer_a_of[1] && chip->mode_timer_a_enable[1]); + + chip->timer_a_load_old[0] = chip->timer_a_load_dlatch; + chip->timer_a_load_latch[0] = (!chip->timer_a_load_old[1] && chip->timer_a_load_dlatch) || chip->timer_a_of[1]; + if (!chip->timer_a_load_dlatch) + chip->timer_a_cnt[0] = 0; + else + chip->timer_a_cnt[0] = time; + chip->timer_a_of[0] = (time & 1024) != 0; + + + subcnt = chip->timer_b_subcnt[1]; + if (chip->fsm_clock_timers1) + subcnt++; + + if (chip->input.ic) + chip->timer_b_subcnt[0] = 0; + else + chip->timer_b_subcnt[0] = subcnt; + + chip->timer_b_subcnt_of[0] = (subcnt & 16) != 0; + + if (chip->timer_b_load_latch[1]) + time = chip->mode_timer_b_reg[1]; + else + time = chip->timer_b_cnt[1]; + + if ((chip->timer_b_load_dlatch && chip->timer_b_subcnt_of[1]) || test_timers) + time++; + + reset = chip->mode_timer_b_reset[1] || chip->input.ic; + + if (reset) + chip->timer_b_status[0] = 0; + else + chip->timer_b_status[0] = chip->timer_b_status[1] || (chip->timer_b_of[1] && chip->mode_timer_b_enable[1]); + + chip->timer_b_load_old[0] = chip->timer_b_load_dlatch; + chip->timer_b_load_latch[0] = (!chip->timer_b_load_old[1] && chip->timer_b_load_dlatch) || chip->timer_b_of[1]; + if (!chip->timer_b_load_dlatch) + chip->timer_b_cnt[0] = 0; + else + chip->timer_b_cnt[0] = time; + chip->timer_b_of[0] = (time & 256) != 0; + + chip->timer_dlatch = chip->fsm_clock_timers; +} + +void FMOPN2_Timers2(fmopn2_t *chip) +{ + int read_enable = chip->input.cs && chip->input.rd && !chip->input.ic; + chip->timer_a_load_latch[1] = chip->timer_a_load_latch[0]; + chip->timer_a_load_old[1] = chip->timer_a_load_old[0]; + chip->timer_a_cnt[1] = chip->timer_a_cnt[0] & 1023; + chip->timer_a_of[1] = chip->timer_a_of[0]; + chip->timer_a_status[1] = chip->timer_a_status[0]; + chip->timer_b_subcnt[1] = chip->timer_b_subcnt[0] & 15; + chip->timer_b_subcnt_of[1] = chip->timer_b_subcnt_of[0]; + chip->timer_b_load_latch[1] = chip->timer_b_load_latch[0]; + chip->timer_b_load_old[1] = chip->timer_b_load_old[0]; + chip->timer_b_cnt[1] = chip->timer_b_cnt[0] & 255; + chip->timer_b_of[1] = chip->timer_b_of[0]; + chip->timer_b_status[1] = chip->timer_b_status[0]; + if (!chip->timer_dlatch && chip->fsm_clock_timers) + { + chip->timer_a_load_dlatch = chip->mode_timer_a_load[1]; + chip->timer_b_load_dlatch = chip->mode_timer_b_load[1]; + chip->timer_csm_key_dlatch = chip->mode_ch3[1] == 2 && ((!chip->timer_a_load_old[1] && chip->timer_a_load_dlatch) || chip->timer_a_of[1]); + } + if (!read_enable) + { + chip->status_timer_a_dlatch = chip->timer_a_status[1]; + chip->status_timer_b_dlatch = chip->timer_b_status[1]; + } +} + +void FMOPN2_ClockPhase1(fmopn2_t *chip) +{ + FMOPN2_DoShiftRegisters(chip, 0); + FMOPN2_HandleIO1(chip); + FMOPN2_FMRegisters1(chip); + FMOPN2_FSM1(chip); + FMOPN2_Misc1(chip); + FMOPN2_LFO1(chip); + FMOPN2_PhaseGenerator1(chip); + FMOPN2_EnvelopeGenerator1(chip); + FMOPN2_Operator1(chip); + if (!(chip->flags & fmopn2_flags_ym3438)) + FMOPN2_YMF276Accumulator1(chip); + else + FMOPN2_YM3438Accumulator1(chip); + FMOPN2_Timers1(chip); +} + +void FMOPN2_ClockPhase2(fmopn2_t *chip) +{ + FMOPN2_DoShiftRegisters(chip, 1); + FMOPN2_HandleIO2(chip); + FMOPN2_FMRegisters2(chip); + FMOPN2_FSM2(chip); + FMOPN2_Misc2(chip); + FMOPN2_LFO2(chip); + FMOPN2_PhaseGenerator2(chip); + FMOPN2_EnvelopeGenerator2(chip); + FMOPN2_Operator2(chip); + if (!(chip->flags & fmopn2_flags_ym3438)) + FMOPN2_YMF276Accumulator2(chip); + else + FMOPN2_YM3438Accumulator2(chip); + FMOPN2_Timers2(chip); +} + +void FMOPN2_ClockFM(fmopn2_t *chip) +{ + FMOPN2_HandleIO(chip); + if (chip->i_phi1) + { + FMOPN2_ClockPhase1(chip); + } + if (chip->i_phi2) + { + FMOPN2_ClockPhase2(chip); + } +} + +void FMOPN2_Clock(fmopn2_t *chip, int clk) +{ + chip->pinput.phi = clk; + if (memcmp(&chip->pinput, &chip->pinput_old, sizeof(chip->pinput)) != 0) + { + FMOPN2_Prescaler(chip); + chip->pinput_old = chip->pinput; + chip->input.i_fsm_reset = chip->fsm_reset; + if (chip->phi1_latch[1]) + chip->input.phi_phase = 1; + if (chip->phi2_latch[1]) + chip->input.phi_phase = 2; + chip->i_phi1 = chip->phi1_latch[1]; + chip->i_phi2 = chip->phi2_latch[1]; + } + if (memcmp(&chip->input, &chip->input_old, sizeof(chip->input)) != 0) + { + FMOPN2_ClockFM(chip); + chip->input_old = chip->input; + } + + if (!(chip->flags & fmopn2_flags_ym3438)) + FMOPN2_YMF276DAC(chip); +} diff --git a/extern/YMF276-LLE/fmopn2.h b/extern/YMF276-LLE/fmopn2.h new file mode 100644 index 000000000..f1ba12839 --- /dev/null +++ b/extern/YMF276-LLE/fmopn2.h @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2022-2023 nukeykt + * + * This file is part of YMF276-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * YMF276/YM3438 emulator. + * Thanks: + * John McMaster (siliconpr0n.org): + * Yamaha YM3438 & YM2610 decap and die shot. + * org, andkorzh, HardWareMan (emu-russia): + * help & support, YMF276 and YM2612 decap. + * + */ + +#pragma once +#include + +enum { + fmopn2_flags_ym3438 = 1, +}; + +typedef struct { + int phi; + int ic; +} fmopn2_prescaler_input_t; + +typedef struct { + int phi_phase; + int ic; + int rd; + int wr; + int cs; + int address; + int data; + int test; + int i_fsm_reset; // (chip->ic_check_latch[1] & 16) != 0; +} fmopn2_input_t; + +typedef struct { + int flags; + // input + fmopn2_input_t input_old, input; + int i_phi1; + int i_phi2; + + fmopn2_prescaler_input_t pinput, pinput_old; + + // clock + int ic_latch[2]; // 12 + int ic_check_latch[2]; // 4 + int prescaler_latch[2]; // 6 + int phi1_latch[2]; + int phi2_latch[2]; + int dphi1_latch[4]; + int dphi2_latch[3]; + int dclk1; + int dclk2; + int fsm_reset; + + // output + int dac_val; + int out_l; + int out_r; + + // io + int write_addr_trig; + int write_addr_trig_sync; + int write_addr_dlatch; + int write_addr_sr[2]; + int write_data_trig; + int write_data_trig_sync; + int write_data_dlatch; + int write_data_sr[2]; + + int data_latch; + int bank_latch; + + int busy_cnt[2]; + int busy_latch[2]; + + int io_ic_latch[2]; + + int write_fm_address[2]; + int fm_address[2]; + int write_fm_data[2]; + int fm_data[2]; + int status_timer_a_dlatch; + int status_timer_b_dlatch; + + // mode registers + int write_mode_21[2]; + int write_mode_22[2]; + int write_mode_24[2]; + int write_mode_25[2]; + int write_mode_26[2]; + int write_mode_27[2]; + int write_mode_28[2]; + int write_mode_2a[2]; + int write_mode_2b[2]; + int write_mode_2c[2]; + + int mode_test_21[2]; + int mode_lfo_en[2]; + int mode_lfo_freq[2]; + int mode_timer_a_reg[2]; + int mode_timer_b_reg[2]; + int mode_ch3[2]; + int mode_timer_a_load[2]; + int mode_timer_a_enable[2]; + int mode_timer_a_reset[2]; + int mode_timer_b_load[2]; + int mode_timer_b_enable[2]; + int mode_timer_b_reset[2]; + int mode_kon_operator[2]; + int mode_kon_channel[2]; + int mode_dac_data[2]; + int mode_dac_en[2]; + int mode_test_2c[2]; + + int mode_kon[4][2]; + + // operator registers + int slot_multi[2][4][2]; + int slot_dt[2][3][2]; + int slot_tl[2][7][2]; + int slot_ar[2][5][2]; + int slot_ks[2][2][2]; + int slot_dr[2][5][2]; + int slot_am[2][1][2]; + int slot_sr[2][5][2]; + int slot_rr[2][4][2]; + int slot_sl[2][4][2]; + int slot_ssg_eg[2][4][2]; + // channel registers + int chan_fnum[11][2]; + int chan_fnum_ch3[11][2]; + int chan_block[3][2]; + int chan_block_ch3[3][2]; + int chan_a4[2]; + int chan_ac[2]; + int chan_connect[3][2]; + int chan_fb[3][2]; + int chan_pms[3][2]; + int chan_ams[2][2]; + int chan_pan[2][2]; + + int reg_cnt1[2]; + int reg_cnt2[2]; + + // lfo + + int lfo_cnt1[2]; + int lfo_cnt2[2]; + + int lfo_dlatch; + int lfo_dlatch_load; + int lfo_inc_latch[2]; + + // pg + int pg_fnum[2][2]; + int pg_kcode[2][2]; + int pg_fnum_lfo1; + int pg_fnum_lfo2; + int pg_lfo_shift; + int pg_lfo_sign; + int pg_lfo; + int pg_freq1; + int pg_freq2; + int pg_freq3; + int pg_freq4; + int pg_freq5[2]; + int pg_freq6; + int pg_freq_m1; + int pg_block; + int pg_dt[2]; + int pg_detune[2]; + int pg_multi[2][2]; + int pg_multi2; + int pg_inc[2]; + int pg_inc_mask[2]; + int pg_phase[20][2]; + int pg_reset_latch[2]; + int pg_debug[2]; + int pg_reset[2]; + + // eg + int eg_prescaler[2]; + int eg_prescaler_clock_l[2]; + int eg_prescaler_l; + int eg_clock_delay[2]; + int eg_step[2]; + int eg_timer_load; + int eg_timer[2]; + int eg_timer_carry[2]; + int eg_timer_mask[2]; + int eg_timer_masked[2]; + int eg_timer_low_lock; + int eg_shift_lock; + int eg_level[10][2]; + int eg_level_latch[2]; + int eg_level_latch_inv; + int eg_state[2][2]; + int eg_ssg_dir[2]; + int eg_ssg_inv[2]; + int eg_ssg_holdup[2]; + int eg_ssg_enable[2]; + int eg_ssg_pgreset[2]; + int eg_ssg_pgrepeat[2]; + int eg_key[2]; + int eg_rate_nonzero[2]; + int eg_rate; + int eg_ksv; + int eg_rate2; + int eg_inc1; + int eg_inc2; + int eg_rate12; + int eg_rate13; + int eg_rate14; + int eg_rate15; + int eg_maxrate[2]; + int eg_incsh0; + int eg_incsh1; + int eg_incsh2; + int eg_incsh3; + int eg_incsh_nonzero[2]; + int eg_inc_total; + int eg_level_ssg[2]; + int eg_sl[2][2]; + int eg_nextlevel[2]; + int eg_kon_csm[2]; + int eg_kon_latch[2]; + int eg_tl[2][2]; + int eg_ams; + int eg_lfo[2]; + int eg_ch3_latch[2]; + int eg_out; + int eg_out_tl; + int eg_out_total; + int eg_debug[2]; + + // op + int op_mod[10][2]; + int op_phase[2]; + int op_logsin_base[2]; + int op_logsin_delta[2]; + int op_logsin_add_delta[2]; + int op_atten[2]; + int op_env[2]; + int op_pow_base[2]; + int op_pow_delta[2]; + int op_pow_add_delta[2]; + int op_shift[2]; + int op_sign[2]; + int op_output[2]; + int op_op1[2][14][2]; + int op_op2[14][2]; + int op_mod_sum[2]; + int op_dofeedback[2]; + + // accumulator + + int ch_accm[14][2]; + int ch_out[9][2]; + int ch_out_dlatch; + int ch_out_pan_dlatch; + int ch_dac_load; + int ch_out_debug[2]; + int ch_accm_l[2]; + int ch_accm_r[2]; + + // timers + int timer_dlatch; + int timer_a_cnt[2]; + int timer_a_load_latch[2]; + int timer_a_load_old[2]; + int timer_a_load_dlatch; + int timer_a_of[2]; + int timer_a_status[2]; + int timer_b_subcnt[2]; + int timer_b_subcnt_of[2]; + int timer_b_cnt[2]; + int timer_b_load_latch[2]; + int timer_b_load_old[2]; + int timer_b_load_dlatch; + int timer_b_of[2]; + int timer_b_status[2]; + int timer_csm_key_dlatch; + + // fm algorithm + int alg_mod_op1_0; + int alg_mod_op1_1; + int alg_mod_op2; + int alg_mod_prev_0; + int alg_mod_prev_1; + int alg_output; + int alg_mod_op1_0_l; + int alg_mod_op1_1_l; + int alg_mod_op2_l; + int alg_mod_prev_0_l; + int alg_mod_prev_1_l; + int alg_output_l; + + // fsm + int fsm_cnt1[2]; + int fsm_cnt2[2]; + // fsm table output + int fsm_clock_eg; + int fsm_clock_timers1; + int fsm_clock_timers; + int fsm_op4_sel; + int fsm_op1_sel; + int fsm_op2_sel; + int fsm_op3_sel; + int fsm_sel2; + int fsm_sel23; + int fsm_ch3_sel; + int fsm_dac_load; + int fsm_dac_out_sel; + int fsm_dac_ch6; + + // ymf276 + int fsm_clock_eg_l; + int fsm_op1_sel_l; + int fsm_sel1_l; + int fsm_sel2_l; + int fsm_sel23_l; + int fsm_ch3_sel_l; + int fsm_dac_load_l; + int fsm_dac_out_sel_l; + int fsm_dac_ch6_l; + int fsm_lro_l[2]; + int fsm_wco_l[2]; + int fsm_lro_l2[2]; + int fsm_wco_l2[2]; + int fsm_op1_sel_l2[2]; + int fsm_op1_sel_l3[2]; + int fsm_shifter_ctrl[2]; + int fsm_load_l; + int fsm_load_r; + + int dac_shifter[2]; + int dac_so_l[2]; + int o_bco; + int o_wco; + int o_lro; + int o_so; +} fmopn2_t; + + +int FMOPN2_ReadStatus(fmopn2_t *chip); + +void FMOPN2_Clock(fmopn2_t *chip, int phi); diff --git a/src/gui/about.cpp b/src/gui/about.cpp index d664e9a1e..c667e1ec0 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -179,7 +179,7 @@ const char* aboutLine[]={ "backward-cpp by Google", "adpcm by superctr", "Nuked-OPL3/OPLL/OPM/OPN2/PSG by nukeykt", - "YM3812-LLE and YMF262-LLE by nukeykt", + "YM3812-LLE, YMF262-LLE and YMF276-LLE by nukeykt", "ymfm by Aaron Giles", "MAME SN76496 by Nicola Salmoria", "MAME AY-3-8910 by Couriersud", diff --git a/src/main.cpp b/src/main.cpp index e32f747d7..b3b9f3b04 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -217,6 +217,7 @@ TAParamResult pVersion(String) { printf("- Nuked-PSG (modified version) by nukeykt (GPLv2)\n"); printf("- YM3812-LLE by nukeykt (GPLv2)\n"); printf("- YMF262-LLE by nukeykt (GPLv2)\n"); + printf("- YMF276-LLE by nukeykt (GPLv2)\n"); printf("- ymfm by Aaron Giles (BSD 3-clause)\n"); printf("- adpcm by superctr (public domain)\n"); printf("- MAME SN76496 emulation core by Nicola Salmoria (BSD 3-clause)\n"); From 0c727ff6b761ecf03afafed8e3510c5cc0335216 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 18:02:21 -0500 Subject: [PATCH 27/53] YM2612: YMF276-LLE, part 1 --- CMakeLists.txt | 1 + src/engine/platform/genesis.cpp | 16 ++++++++++++---- src/engine/platform/genesis.h | 9 ++++++--- src/gui/settings.cpp | 11 ++++++----- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20e6cf507..4b8a94787 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -491,6 +491,7 @@ extern/Nuked-OPLL/opll.c extern/opl/opl3.c extern/YM3812-LLE/fmopl2.c extern/YMF262-LLE/fmopl3.c +extern/YMF276-LLE/fmopn2.c src/pch.cpp diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 4973a323d..633bf2886 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -289,8 +289,14 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { } } +void DivPlatformGenesis::acquire_nuked276(short** buf, size_t len) { + // TODO +} + void DivPlatformGenesis::acquire(short** buf, size_t len) { - if (useYMFM) { + if (useYMFM==2) { + acquire_nuked276(buf,len); + } else if (useYMFM==1) { acquire_ymfm(buf,len); } else { acquire_nuked(buf,len); @@ -1309,7 +1315,9 @@ float DivPlatformGenesis::getPostAmp() { void DivPlatformGenesis::reset() { writes.clear(); memset(regPool,0,512); - if (useYMFM) { + if (useYMFM==2) { + memset(&fm_276,0,sizeof(fmopn2_t)); + } else if (useYMFM==1) { fm_ymfm->reset(); } OPN2_Reset(&fm); @@ -1396,7 +1404,7 @@ int DivPlatformGenesis::getPortaFloor(int ch) { return 0; } -void DivPlatformGenesis::setYMFM(bool use) { +void DivPlatformGenesis::setYMFM(unsigned char use) { useYMFM=use; } @@ -1441,7 +1449,7 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) { break; } CHECK_CUSTOM_CLOCK; - if (useYMFM) { + if (useYMFM==1) { if (fm_ymfm!=NULL) delete fm_ymfm; if (chipType==1) { fm_ymfm=new ymfm::ym2612(iface); diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 8c9181dc9..82d4301b9 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -22,7 +22,7 @@ #include "fmshared_OPN.h" #include "sound/ymfm/ymfm_opn.h" - +#include "../../../extern/YMF276-LLE/fmopn2.h" class DivYM2612Interface: public ymfm::ymfm_interface { int setA, setB; @@ -77,6 +77,7 @@ class DivPlatformGenesis: public DivPlatformOPN { DivDispatchOscBuffer* oscBuf[10]; bool isMuted[10]; ym3438_t fm; + fmopn2_t fm_276; ymfm::ym2612* fm_ymfm; ymfm::ym2612::output_data out_ymfm; @@ -84,7 +85,8 @@ class DivPlatformGenesis: public DivPlatformOPN { int softPCMTimer; - bool extMode, softPCM, noExtMacros, useYMFM, canWriteDAC; + bool extMode, softPCM, noExtMacros, canWriteDAC; + unsigned char useYMFM; unsigned char chipType; short dacWrite; @@ -96,6 +98,7 @@ class DivPlatformGenesis: public DivPlatformOPN { inline void processDAC(int iRate); inline void commitState(int ch, DivInstrument* ins); void acquire_nuked(short** buf, size_t len); + void acquire_nuked276(short** buf, size_t len); void acquire_ymfm(short** buf, size_t len); friend void putDispatchChip(void*,int); @@ -116,7 +119,7 @@ class DivPlatformGenesis: public DivPlatformOPN { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); int getOutputCount(); - void setYMFM(bool use); + void setYMFM(unsigned char use); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); float getPostAmp(); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index c7ff6b29b..154b87331 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -127,7 +127,8 @@ const char* arcadeCores[]={ const char* ym2612Cores[]={ "Nuked-OPN2", - "ymfm" + "ymfm", + "YMF276-LLE" }; const char* snCores[]={ @@ -1499,10 +1500,10 @@ void FurnaceGUI::drawSettings() { ImGui::Text("YM2612"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2)) settingsChanged=true; + if (ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,3)) settingsChanged=true; ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::Combo("##YM2612CoreRender",&settings.ym2612CoreRender,ym2612Cores,2)) settingsChanged=true; + if (ImGui::Combo("##YM2612CoreRender",&settings.ym2612CoreRender,ym2612Cores,3)) settingsChanged=true; ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -3810,7 +3811,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.audioRate,8000,384000); clampSetting(settings.audioChans,1,16); clampSetting(settings.arcadeCore,0,1); - clampSetting(settings.ym2612Core,0,1); + clampSetting(settings.ym2612Core,0,2); clampSetting(settings.snCore,0,1); clampSetting(settings.nesCore,0,1); clampSetting(settings.fdsCore,0,1); @@ -3820,7 +3821,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.opl2Core,0,2); clampSetting(settings.opl3Core,0,2); clampSetting(settings.arcadeCoreRender,0,1); - clampSetting(settings.ym2612CoreRender,0,1); + clampSetting(settings.ym2612CoreRender,0,2); clampSetting(settings.snCoreRender,0,1); clampSetting(settings.nesCoreRender,0,1); clampSetting(settings.fdsCoreRender,0,1); From a2f86d297f6b027ff22a6676d339d6229001901a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 25 Nov 2023 18:37:50 -0500 Subject: [PATCH 28/53] GUI: fix wave drawing --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 70fb9508e..b8820aa61 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2508,7 +2508,7 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { int x=(dragX-waveDragStart.x)*waveDragLen/MAX(1,waveDragAreaSize.x); if (x<0) x=0; if (x>=waveDragLen) x=waveDragLen-1; - int y=round(waveDragMax-((dragY-waveDragStart.y)*(double(waveDragMax-waveDragMin)/(double)MAX(1,waveDragAreaSize.y)))); + int y=(waveDragMax+1)-((dragY-waveDragStart.y)*(double((waveDragMax+1)-waveDragMin)/(double)MAX(1,waveDragAreaSize.y))); if (y>waveDragMax) y=waveDragMax; if (y Date: Sun, 26 Nov 2023 04:01:48 -0500 Subject: [PATCH 29/53] Virtual Boy demo song contest entry by sillygoose --- demos/virtualboy/red_joke.fur | Bin 0 -> 1786 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/virtualboy/red_joke.fur diff --git a/demos/virtualboy/red_joke.fur b/demos/virtualboy/red_joke.fur new file mode 100644 index 0000000000000000000000000000000000000000..f2afeb53d5fff1fd2ce5335ec19c620cc1042be9 GIT binary patch literal 1786 zcmV$rG zwp-;Imn|E)RekbBF!4bXql6gS@IYcDF)_r%=!-FG%o?MH2YfLGqQ><(a~c z;FRB)Ju~0`o0)IEooQLpFwoc2-WjhxlI$8t#FvZ^Q8g7xc%PK$p81UGu~CbIe9ovhw<|L1~LpLni=yA7~5|W{Q#y}L=E6| z@GV$i6Sabi;1dvYh&sVtFys==4-s7hTf;;@g3buq!DoO)iJk%j;3~KU9w{Sw7Q79< z0~J$<9tBO{Jops+1J=Zd`oIU^7f@YJ)CJxG-+)_S`c$HA;1zHK`~_A`BT9nzz)xV| zbfOM$30wi!fiVMiAP(LHUjVy;Xa(2;y1*&$D!2;nfDJQ=E`aNR&mx))mV?b;FL)8W z4|1SlHrm0Mv2^XCw?fQ#S;U}SL9vK==R4n-s5ypei!BVk4sx47+Cr7Q!bWFDl~ zk-zWapXsN7AztYX{CH*e!M@Z$d!l-M@>r1+Wxk&?QuJk>0RwPg0t?u{0WJuEFo=LC zC<9YK3?N?6=b+C)pMyRJeGd8@^f~Br(C47fL7#&@2Yn9u9P~n~iop&0o?R|0hTT|q3X3!rm_*({!7Wr(c-@R!_ z82Xc_QZ>F#xJ`*f>f&rAoR!is&OE|7N0s0qFc-`#!NVFL!gUnU6NOZB4-WlV; zl{0^(!EM&r#vskxnre@uj`ydM$DSXE_qDfo^d0Ov5|^_~U-u|iSBz-O7nS3`ZM}Jo z&sfX1y|duQVVQwt8u+!ZI-?XNl!msFYb#A{rKPR3wUv&x($!F|mCBG(ibq;JvQA3mvFE_& zoY&*qNbA7xVeN@WHIy=k4UxmD!IoDwa#+5tQx`W?bu!oN%J4Qb&4x3)o27Y|B|mns zEbkN_iMOG3AItDPJj;gDyw%FEVd-YEbc3XYpz4$pH8FvcPMV)^vizuXUe?D)-=$=s zlwjzpq_EHMUi2p&+%3*o)Cj6G6trI|?M05BaH1KsW_cQllhSh*mZA;eaD=C``~@d4 zG0U4+w}B;Q`2i!%;|3OGoab$*yVZM}AtlSC1YxQ=;Z1CUL)A!~BO>jLyP9%oj7+R* z5plnau*!D%zYrXohFD+b&#@uCH`ozT9dUFVV1*6lVR6rT^PeMsqrwLB7JG>AvoD~2 zjqkCusHH)j`jv~E7CS~_BU@}#gS^ugA5XL4(=eA+i;avx>t0*h$<{Nx)$_Aa`my9d zvU{JXc(?z81i_%Y<-lqfnNWMAS=<}5H8>~!z;lvWW!iGTdCJZ5epvQm3Q6}oPvAYJ z-V<(MT<3wuEt7u4zID2ndAD2GGgE9$0*!}U>{+lQsycen(eD;E(-ga$@*2cnJ7i%q zKXN+4Zkl)C+uz}xCQD?O*v4HU95?9Bj`=AdGIT^-M`Y@VEFF=pBXTAuS}sNIghfSr c71EhvSVt6{oM@I5MT)YFW=B~5U*m3K-J@f7wg3PC literal 0 HcmV?d00001 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index c667e1ec0..22c29d485 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -126,6 +126,7 @@ const char* aboutLine[]={ "Raijin", "railzen7", "RevvoBolt", + "sillygoose", "SnugglyBun", "SuperJet Spade", "SwapXFO", From a1406e925603cee896b6104f92598a339380af44 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 04:04:42 -0500 Subject: [PATCH 30/53] Virtual Boy demo song contest entry by Pale Moon --- demos/virtualboy/Evil_Incarnate.fur | Bin 0 -> 1242 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/virtualboy/Evil_Incarnate.fur diff --git a/demos/virtualboy/Evil_Incarnate.fur b/demos/virtualboy/Evil_Incarnate.fur new file mode 100644 index 0000000000000000000000000000000000000000..c07c21cfb75e3fb109098790e000d528db406c06 GIT binary patch literal 1242 zcmV<01SR`;oW+$*Ok7nI$IrX(b3Wiv1k-?v7mOh`L4n37DV2``2`ESkGD1a|nNG{t zaR&LQl8`W{n@KdSi>eC~U1_?}Xu8qH1k(jEF4T0@l*MyFT{b2f(?v7>?|pY>-oOks z65}0y@A2+A|8vj#zx!s|hE|u-(RjjnEpv7?m1w(8L=Kh4@Yv7`U#a*<)0CQ{!~N~l zKw8)&4O5SSkLlpi`+)HP`Tsr!FD6sYa5^4^ODhTL&%|PD&iGO?4a+*Zx;mY8XFQrp zIHQ?N+UacXJPysyDI%tDwW1X2C`v6X`w092I&`8G_zjF0L|4JHCecl>U=iI1H36bF zFaqKr3*HA`fNkJ{`=BXEbPNoFIq)X90zLybzz+Bm)Q58(u6#l6mr)&=uyykye!FI+k=>vRx z4>5^{D8^KBX=OEJqjGzldSzz zv38hFrTn+q%&TiCfI6CrgM$CRC*_S&W5<0Kd1BdF6W6LDRq=Av{LsMTrS~W_(z~%euy{MWaq`X+8_{2H zo|x|3u^T==HeLVW<^C^kdrO}wE^X7~LNe`)M@F5Awe-pYl~ud*(=S=}*Xtd>f3p3w zdiUMdrn>y*Y4?YXAG>Z{`}VuW=E%emDr`a~-#YS^i1>^djbe#^S)R${aH)KxZ))&8 zKIh1oO@|-$A^A_5I?UkjcEzT+qvB^A@ULV1;P7vSG7K5hWG>ez@*J)}HCHO-R&@%$ zU{os?56W{$HsLQA^2KUp!7`GfrN2M zjd{U<2r3IL%otuE=3AQoUKa!N=xf$zilU34FWV@~&K0t~ zHOw-{d;zP<6Hx+{pNJO>%3w^cH|sNn?b$J`xMQ|<7c1--R@^b~?C+0N+cE5*9mDqS z7dXACd1JHFr}syQy|FDnXn*bH#aItYTjNZ&p_N E>!s{r7ytkO literal 0 HcmV?d00001 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 22c29d485..7174b3011 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -120,6 +120,7 @@ const char* aboutLine[]={ "niffuM", "", "NyaongI", + "Pale Moon", "PichuMario", "potatoTeto", "psxdominator", From 0c599a9ef4a02c68ffe9844cc13eff31269dbea0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 15:10:05 -0500 Subject: [PATCH 31/53] GUI: fix Appearance having a never-disabling Apply --- src/gui/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 154b87331..4d0eb1ca4 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2643,8 +2643,8 @@ void FurnaceGUI::drawSettings() { bool capitalMenuBarB=settings.capitalMenuBar; if (ImGui::Checkbox("Capitalize menu bar",&capitalMenuBarB)) { settings.capitalMenuBar=capitalMenuBarB; + settingsChanged=true; } - settingsChanged=true; bool classicChipOptionsB=settings.classicChipOptions; if (ImGui::Checkbox("Display add/configure/change/remove chip menus in File menu",&classicChipOptionsB)) { From e7f3e2042f29a1d0b31037a99f8d102218dc3a7b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 15:26:26 -0500 Subject: [PATCH 32/53] Virtual Boy demo song contest entry by RevvoBolt --- demos/virtualboy/Dimensional.fur | Bin 0 -> 1399 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/Dimensional.fur diff --git a/demos/virtualboy/Dimensional.fur b/demos/virtualboy/Dimensional.fur new file mode 100644 index 0000000000000000000000000000000000000000..449dbeb50140732e9da99aec115961fdd7a7dadc GIT binary patch literal 1399 zcmV--1&I21oXuECZxlxu{i>^bmYyEZm~{*mp5TN43B+MZ2oAQ#ERk6p3nDD%gf)zi zO+$mXD9Ry2EJay_K#GDCDZjwSDC7`sIdjM%hX{ex=SV@^qp0(~>gwqkd+-=5fD2yt z*Y(x=>eZ`q8L{WY?9|oqa;131yEyAs_S_|+BE@ZJ_{4|XB?8M5`NGiP06Apl6J()l z6YwJ~-v(N=7IZo(Ta9?W?byVX%GK!!@9MZqXDf4a-k|5sp#R}fG7P~ZgowrpaR+K2 z0^3ZYkAd%i2f%UI^#Je^7?vdZ9@w2C`YcWKgH7~ThNw47w7HGwV2-FgPxLqNFYs+U zQO7!>1>jfU+;h_*elRMq-D5P$(lzyvHH z0VIJGkOpiZ12{kyXajNpkq^EPz7M_+z7M_+z7M_+z7M_+z7M_+z7M_+z7IZgBQaK@ zvC%l(n*+W9PMj`%fUTqQ?DULx<*4hGFCXc#sdQ}UY*wuVjel;0X-MvLqE3rlmnQd_ zdZwY_bNl-6zDs!;E+uGW)}5}TB0UfUIs5R~D%$A`Jf;aZh6 zo%TK{4vtSxqfj>&N`(Ae*2&mup{o_5vLXm89H>>WNV8vIb~YTAvF&s^l}uQcDUFD0 zYVO-LZkys3ce>u}#mdyKMQgDa6Z`8XJvC9jjPaX!yrU(zwPmwTI%yhu^?cI#rVw8U zDi73FU#|0T*qcxu9_Bh*qB?5rsie}cnv{I-Nl5K-+BOWWEjC(2dq--j(bDq1>yc7K8(0~U=011=`6{pj z*a_?g-URjnJ-|Nw-l@kIblsE|^)-v=s#DE_jx2+|Caa5ZPz%bf!m)HSmm)#Cv}1?s z1oUY^y}%*h9X;kP?EtU%HSN?!3SDKVqocy_$o-@i;AnSno6}|r-EJz13$D`xH|oKT zUU>(FTe4pZW6|#c?*a$3z}I!XZ4_@Mb9U?eUcCrj_zs=#)V292jFdha9*S1dpQ}*L zrm849)qJ;eiK<0JJ>8B=)d55!-A;w77Z6=j*(##jD!YK_p32@w^hjkNBMMYsfatO6 zdxYq|V%$Ttpt83SRaN#Hq6;cJf@nZxdk_^>HjhZCzBQ~bU_&E?5)ty zwWv=S`bTuFS346n@vu}t@)8kONj-^rc$3tZtMV0!d*Xbs8dCGT8|E8_t(^2EzMF&y z56errn<{JgZVGqacwsv~`2u%7T$^09HkTP*Lq#pxiz~W|^T7&8lPfFgHLPvPOsz=a z+EV&g3W=Z9_m}D$r#x8_Ig%yubR27e{uUTq7IkV2r6xo2jGp;f-aON(Rqz`5N{#xL zI{$A$=*`PhQ^;iE&6DxweWkv!d0p2H2QPWucsgcUz-xi2Wf5Kjh}veh*WkJR&qdaC^A zD$loGmuuS5fbcnA7uOU2-)t<~ie;}?-z+)uB?X*i6i$^JX1_{{ZsV FsQS9Owrv0a literal 0 HcmV?d00001 From 3e854030db57fda754b1ca919a45b7c7ae7c1779 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 15:27:40 -0500 Subject: [PATCH 33/53] Virtual Boy demo song contest entry by NeoWar --- demos/virtualboy/VirtBoy_Song.fur | Bin 0 -> 4524 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/virtualboy/VirtBoy_Song.fur diff --git a/demos/virtualboy/VirtBoy_Song.fur b/demos/virtualboy/VirtBoy_Song.fur new file mode 100644 index 0000000000000000000000000000000000000000..40ea47989837fd68a495dc60db18abb44c7edc05 GIT binary patch literal 4524 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", From 7b2fa42d253f355619aeb3274ee8156e791cb6d6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 16:46:33 -0500 Subject: [PATCH 34/53] GUI: update sample tex on color change issue #1598 --- src/gui/settings.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 4d0eb1ca4..4c9cfad61 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -4915,6 +4915,8 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { sty.ScaleAllSizes(dpiScale); ImGui::GetStyle()=sty; + + updateSampleTex=true; ImGui::GetIO().ConfigInputTrickleEventQueue=settings.eventDelay; ImGui::GetIO().ConfigWindowsMoveFromTitleBarOnly=settings.moveWindowTitle; From 4e333787ff1ff3caf4c8a3d87698fae2426eec50 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 18:23:57 -0500 Subject: [PATCH 35/53] GUI: fix next/prev channel cursor out of bounds issue #1597 --- src/gui/cursor.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index da2028454..ca5f51b64 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -344,6 +344,10 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) { } e->setMidiBaseChan(cursor.xCoarse); + int xFineMax=(e->curSubSong->chanCollapse[cursor.xCoarse]?(4-e->curSubSong->chanCollapse[cursor.xCoarse]):(3+e->curPat[cursor.xCoarse].effectCols*2)); + if (cursor.xFine<0) cursor.xFine=0; + if (cursor.xFine>=xFineMax) cursor.xFine=xFineMax-1; + selStart=cursor; selEnd=cursor; demandScrollX=true; @@ -368,6 +372,10 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) { } e->setMidiBaseChan(cursor.xCoarse); + int xFineMax=(e->curSubSong->chanCollapse[cursor.xCoarse]?(4-e->curSubSong->chanCollapse[cursor.xCoarse]):(3+e->curPat[cursor.xCoarse].effectCols*2)); + if (cursor.xFine<0) cursor.xFine=0; + if (cursor.xFine>=xFineMax) cursor.xFine=xFineMax-1; + selStart=cursor; selEnd=cursor; demandScrollX=true; From 5ab70ff91fd1917543b09c8565de8a951de0a39c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 18:50:22 -0500 Subject: [PATCH 36/53] PCM DAC: fix possible overflow in linear interpola --- src/engine/platform/pcmdac.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index 1ddd22dae..adc8770ee 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -67,7 +67,7 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) { switch (interp) { case 1: // linear - output=s6+((s7-s6)*(chan[0].audSub&0xffff)>>16); + output=s6+(((int)((int)s7-(int)s6)*((chan[0].audSub>>1)&0x7fff))>>15); break; case 2: { // cubic float* cubicTable=DivFilterTables::getCubicTable(); @@ -188,7 +188,7 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) { switch (interp) { case 1: // linear - output=s6+((s7-s6)*(chan[0].audSub&0xffff)>>16); + output=s6+(((int)((int)s7-(int)s6)*((chan[0].audSub>>1)&0x7fff))>>15); break; case 2: { // cubic float* cubicTable=DivFilterTables::getCubicTable(); From 35316910bedc53acdd14af5b08eb89ac4244e164 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 20:32:05 -0500 Subject: [PATCH 37/53] Virtual Boy demo song contest entry by Laggy --- .../virtualboy/mission_breafing_virtual_boy.fur | Bin 0 -> 4186 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/mission_breafing_virtual_boy.fur diff --git a/demos/virtualboy/mission_breafing_virtual_boy.fur b/demos/virtualboy/mission_breafing_virtual_boy.fur new file mode 100644 index 0000000000000000000000000000000000000000..faa2ea74056b3221d36b8e6f762e8c58f5c9b2ef GIT binary patch literal 4186 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 literal 0 HcmV?d00001 From 2d73e45e0f06e1f8ef3e3c477f66fd14262bfcf4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 26 Nov 2023 21:55:09 -0500 Subject: [PATCH 38/53] they did the Dance and now you can do it too --- src/gui/chanOsc.cpp | 11 ++++++++++- src/gui/gui.cpp | 3 +++ src/gui/gui.h | 5 +++-- src/gui/pattern.cpp | 7 +++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index abdf708b0..165518f79 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -169,6 +169,11 @@ void FurnaceGUI::drawChanOsc() { if (ImGui::Checkbox("Center waveform",&chanOscWaveCorr)) { centerSettingReset=true; } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Checkbox("Randomize phase on note",&chanOscRandomPhase)) { + } ImGui::EndTable(); } @@ -513,7 +518,7 @@ void FurnaceGUI::drawChanOsc() { phase=(0.5+(atan2(dft[1],dft[0])/(2.0*M_PI))); if (fft->waveCorr) { - fft->needle-=phase*fft->waveLen; + fft->needle-=(phase+(fft->phaseOff*2))*fft->waveLen; } } } @@ -775,6 +780,10 @@ void FurnaceGUI::drawChanOsc() { if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { chanOscOptions=!chanOscOptions; } + if (ImGui::IsItemHovered() && CHECK_LONG_HOLD) { + NOTIFY_LONG_HOLD; + chanOscOptions=!chanOscOptions; + } } ImGui::PopStyleVar(); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b8820aa61..3f585047e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -6513,6 +6513,7 @@ bool FurnaceGUI::init() { chanOscWaveCorr=e->getConfBool("chanOscWaveCorr",true); chanOscOptions=e->getConfBool("chanOscOptions",false); chanOscNormalize=e->getConfBool("chanOscNormalize",false); + chanOscRandomPhase=e->getConfBool("chanOscRandomPhase",false); chanOscTextFormat=e->getConfString("chanOscTextFormat","%c"); chanOscColor.x=e->getConfFloat("chanOscColorR",1.0f); chanOscColor.y=e->getConfFloat("chanOscColorG",1.0f); @@ -7066,6 +7067,7 @@ void FurnaceGUI::commitState() { e->setConf("chanOscWaveCorr",chanOscWaveCorr); e->setConf("chanOscOptions",chanOscOptions); e->setConf("chanOscNormalize",chanOscNormalize); + e->setConf("chanOscRandomPhase",chanOscRandomPhase); e->setConf("chanOscTextFormat",chanOscTextFormat); e->setConf("chanOscColorR",chanOscColor.x); e->setConf("chanOscColorG",chanOscColor.y); @@ -7563,6 +7565,7 @@ FurnaceGUI::FurnaceGUI(): updateChanOscGradTex(true), chanOscUseGrad(false), chanOscNormalize(false), + chanOscRandomPhase(false), chanOscTextFormat("%c"), chanOscColor(1.0f,1.0f,1.0f,1.0f), chanOscTextColor(1.0f,1.0f,1.0f,0.75f), diff --git a/src/gui/gui.h b/src/gui/gui.h index 10bcd2c66..67b83484c 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2130,7 +2130,7 @@ class FurnaceGUI { // per-channel oscilloscope int chanOscCols, chanOscAutoColsType, chanOscColorX, chanOscColorY; float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify; - bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize; + bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize, chanOscRandomPhase; String chanOscTextFormat; ImVec4 chanOscColor, chanOscTextColor; Gradient2D chanOscGrad; @@ -2151,7 +2151,7 @@ class FurnaceGUI { double inBufPosFrac; double waveLen; int waveLenBottom, waveLenTop, relatedCh; - float pitch, windowSize; + float pitch, windowSize, phaseOff; unsigned short needle; bool ready, loudEnough, waveCorr; fftw_plan plan; @@ -2169,6 +2169,7 @@ class FurnaceGUI { relatedCh(0), pitch(0.0f), windowSize(1.0f), + phaseOff(0.0f), needle(0), ready(false), loudEnough(false), diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 212de12f2..9e5c6e538 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -550,6 +550,13 @@ void FurnaceGUI::drawPattern() { if (e->keyHit[i]) { keyHit1[i]=1.0f; + + if (chanOscRandomPhase) { + chanOscChan[i].phaseOff=(float)rand()/(float)RAND_MAX; + } else { + chanOscChan[i].phaseOff=0.0f; + } + if (settings.channelFeedbackStyle==1) { keyHit[i]=0.2; if (!muted) { From f7ee88a3611fa27ba5e1992449dc6d6ce4b53d23 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 27 Nov 2023 06:34:51 -0500 Subject: [PATCH 39/53] Virtual Boy demo song contest entry by Yuzu4K --- demos/virtualboy/StuckTerminologyRealVB.fur | Bin 0 -> 3798 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/StuckTerminologyRealVB.fur diff --git a/demos/virtualboy/StuckTerminologyRealVB.fur b/demos/virtualboy/StuckTerminologyRealVB.fur new file mode 100644 index 0000000000000000000000000000000000000000..bc9bc99c0195470714af9b030141b185846b4f95 GIT binary patch literal 3798 zcmV;{4k__?oZVV$j2u^$K2=@aRo&AwcN#mUZJcx^4#dvG8RAW1Cx-SIClFAKu`SOU zuZ+jz&dfCJF8B1zWHQF<=@^HY1jNe-6oU}rxmv*wj3WL;;-TG@*oU-&KkRCDS3&zD zLKgT3%L-<{bL!TkyQ;@_$S%p&_&fF8$2s@Zt#i-4HRF38ottTnPt@F#-if(J?Vj%w zkxS9_*xpCqzbgxp%Nc{^$9C_ctz-?CD37Z50heg?Yu7cdYpDOpuLHAl6CZXD)@Dvl zH@${8`INhPe{H;RXt#T)HnVjLov5E2Uzk2QchcSbu0s#GhvydOwm|I{?;_JQ*nnXK zrVQh|n8=?28*)S~;D^9To9HLN4u|M@;6H#R%zXd)qOmtg`=tbbiZ-Oo`w1((z;9r1W0nN2UYi}a@EKpiUbSrQ#@W7ji z_LPaHfs4TRfZNv--48qk{1@Lh5d8r-^A@821_m|~tp_~dbHGc$zXHDovNsc*1%3~l zzJ=&H;ETXFfw>CN3&7t1zXV=%iT)1w$gM>G34H!GqHh3~fx9;08G+-s6RmqIe0m$v z!@yqP=fE>}5N+FxxxuvmGw|=gi+2(I18~pX80Q|Me*-q(i)R}lIuEP>V{a$=r?*Eh z+As_=gUiZ>S57V)mxC*x&ld)`8(*erW-MH~kM5N($TfhgSS$_>^|77ny0ra#A=S3s zb2)OMv)DD+uJq03g0}dTXQvCr0pAC)E#0jiAACphdDbZm6xjna<7*_B#AwrfT#|+R z#5BZobJlamn=UU4_u$lYb8>5;;J(Lu(w%O*vs2@az@HCU|=?6kjbF>dL>cLBwy}U`hG~gvt2)maJTrs*0JL!f$ z92(Bj?(udTb|2=xBVBg0Jx|zk=?6A?o+X<;OwHKnp+`Q! z)YWa__O5vvuJ^j0zG;0@onnsDC*hPyI8hQ#kF*WoJ?dfI=V4vmrrYn<{cqR$I1v-? z{~S)d&*Ky?OEXB0mM#}{`&!+9V?U=D@6ZG9(1YKv9pEz*j=T6&^BuWUJM%W}5TBp; z6oI2T;TTUi+7pidgiiv(Cjy^O2%i&#PYmL-BY*7i`s8hZ-=wkHOx;KQU*BrDPwNlT ztGfpKIrR{Lj}F4e5b;rzcOFyU3%dRmJxlDw16t<+UA|Md5B2B{>H4ibI$L$Usqd*y z@oU+s!3y}Ql6M~@gx)JYD8~iAN{+Mb+QvImuv0$(zw%*i!_*I)>QoS{f2IwLXva3| zfkt$nU|?4p< zQ+TtFPPTbxXxtzb&oE)|Ii0G7QHkzkY={oEL|Yx$~C` zQn3&$$x6di26b%Qh(*U<&_Wk)v_V9Gr~}z;7A;fB_+R#8mfyp|5FEHVOqKSK%D7}% zR9Rql!M|BnWnLO8vnfTbh1z(LDy`5~+qc!~Y7R_w^{#lal6d)~c!?cZHL|%39yyyM zyPnN;Sdcx%Y^s}0G80kLBC1Q%5Y%-q@>w&Rli}xO_&f)pUF@(x2WE9D$WFfHS$^0F3RZsHv{sRaaqd#w;E0YQd-dqjh3)@Ei6b2$-+wIEzO%LyxB*m z-KSp~pNi?F@#ZR=?~_1Xm(#eH&R4?p%bz}?QNPC1{C}T~n*>&-fTYM!OmZa0| z(kauoQz8xX?+I;>)3Dc_hNq3wcVgswqUMMO~S$0d`+eLJnM8b-|f>}y~u2ySv%@2su-U|E8gaDTa#<*%o@5V z#3Gqb1jaEC=a`zQB-PAscO zdqYw~(2OT}j2_SQ#$b1*H->bk->XkdWFN~Z5f|)w4%<5r)f{2=7g}7fk1{)=MaMqO zLKqsNRAGiEHTCkTSQbDjN)@Krw^Jfjn0#VubE;@{r;4Jq@`>#*rwXYXkUFG~E!oS4 zzn*8L3eR7~eBoG9;7Q^*wmE85baYq{ zhog?khy~HviN#tJu}KXeIV2i>6sl85RpCBq>!) zkWs{&HrIg*L4FY<^IQquMdWF2k*DDRulON-g1RF7g!Fo=^vd+_b;;e?y$&+GWiIeK zn8!MZwf?QN@|O8QtOFB!TlozuqeYy_#TW5kNtLa$hMZDuCOF0)N-Xg3Dhj##plJvjE*RJiPu0QjD@c6 z_js|V)nn0&_q&#XR=NT4>955nOV(vp)}_sN79r`_W6X}QINIB2PiTUe>F#wr(e?Ta zN^I0vOoV2R>i*I9ot5Q{(ZP%ZO`rkXXt@>R^;c2$VtndVl$$X=dE;f74JB`ok4NeV zZy(BvaW?iQlM_Uk!0Uy#L7IzL4NgiCh$UI>tW|tMU06k$)-jtjHyi z9g$Bb_(v1`e1bm{@o2ZS2D*34Y_MDYlwVhtKe;O3B~?%*GC>Lj6_-`@4BDJhrmCb| zqMkmgyZ`@G@IaBQJGW zUNO!h_r7Dnz?0{$W~Kg+)bjzL`aLR`yGl4jmCccjdDAVM!y6s*&6aKDEdE1(#xgBq z@G_a7t}LLd&FRu|lFb*8-c5hhCnS_)D_<~FDd$|dqX^$GZq$ybhC#v3g%uX{&+xL^BB22p$(VqFKVuHkmT zN5o21-WhM+m2dgMWDr;$;S@;J>MA@1Vyt9Us~nDYDPtU4s`^cl-mA8B@6BHT2fd^C z0@}Yf+}FW^*Or}?Kb4c!HPU2IbsM*3&3DT*?vQD;-zd{~o|KURz2c5I z3XuEuZAPxU=2~}X6tCH1U82VAu8|3p|4Wt4bH+MF%H&t<7O&nK{r)YBZR}}30d}hD z<1|>X>56O7l3#cJwQiD;_sPieTj&!qWtod*!#ZC!vggW14sbeUqgXZu%f?XI7%m&7 zvhk*}v9@g7R5sR?jdIyoUp6+Bjg4jF=CW~1*>Gb|ge|%H;1`rqrdi6`pH<5K6o86U z{ycD@5C0_a9Plxq13U|y0TzKdr7ANR!~a|OCK;JeXS|loQzcT)c4h9miVcAGfU;>n M@2sr+4~mB4o18k8G5`Po literal 0 HcmV?d00001 From aad53ed46a2a999b38b6388fb294dfaf63515e93 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 27 Nov 2023 06:36:14 -0500 Subject: [PATCH 40/53] Virtual Boy demo song contest entry by sillygoose --- demos/virtualboy/air_fight.fur | Bin 0 -> 1977 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/air_fight.fur diff --git a/demos/virtualboy/air_fight.fur b/demos/virtualboy/air_fight.fur new file mode 100644 index 0000000000000000000000000000000000000000..3d37153515ded47c4f44de2068fb998ad1e20b09 GIT binary patch literal 1977 zcmV;q2S)gKoXuEWY#h}U{$}pX?0DDqIy4DRHn66Fhdu-na9dQU66eP>tzrU3+2GQo z_Qp=E?AnokfDpCz7ObjKt)vw{iAscAsRF7N@!&}Chc^2}^ubk9uyJqXLuf&OQ|P;4k= z0KY#tj}+Xlb~qe5NW6>s<-i7I1My8SedAL-{o`lP&0u=GP`G$@a&mfe$-9p6DNuD#B0PgVww1yC=dDmqF^ ztbgbCBk-dBvjT^jx{J5@&nqX!r)Fk{3q1!XFRl?~`EC#aBJ@SYAWT9L7GV<(ksw^6 zjYtwHqMhgWQ@%|3GUc^wKt{1R zk%Cp8<8JP757Bq{==0sOPvGSd9%`607|mvA`yr+0J7h~Nj_(OQmVk#HQ_wk)eMc+3K4vi*m@S9L|a zgCZq2xFuZpx`ET~0pQ4hrqLTu<8cHJ(z24qd^e@El4fL8jqFdWvRi6od-nGJkS6G) z2@C}>0jxx2JvOJ!1~fkL86-iXF^EcQf=#hCYCX<6+lt9jYS?m#vj z%063_wehp@{CV!YR|jYpxTa#_17hAG^kT)n^7L>&QT^@5GTs z-3<1c^=l>o@P>hw;_U(67Q$QS2xfIKql1|o%-VP$1`jrYtdDg`tyhdSFk=veuI0n_ zp^@KF2B}+u3hP+ zI1kEK7VI?Y=4RX&#qquu2Hp!;XL8QUJd9ZG%9Te2cFcdWNbc_&N*)79yaHngsBp*>-Wq}r@07(<21gl1VR36k7k4ANDq(}&UAOx9lTcx#mdYKWpSF{UtV>M`mBf|IB zehx$?kChAJ)EsIniGsXWjd|5j%@X}gHl@xZR);tFlJ5R4kUq(gAw*aho6MplEF{-z z8g6B?h{*RhKUjPsM!b@i5uwr7BudW`*4-koz9oJuMC`juB(@Sd38BM8_f44R44vo3 zn7%G7sWE1(ai*^^oxZ$InCA?)?!sI1oT2mFn3w0q^abGBSk{+Z$eP# zxfW?{p5E^EaX+m|Yqi>njD_CQule6Zx<=4VN|)0+mfcEZfeIGLH?GM;@pdAoEUlZh z6NH90Yp*lwsj3YeQwL6w@o;rXoo5IX84pDr54tlJ2^`a{vv49Na7-OIMK5rQ+GHF5 zsPi;tF&>TT*ho=$L7F;nTBNmkdYNKy9TtNNvSuXooT?2PRSlYybQv@cwbw+As*M^3 z1m9Rp%Q|z@SYA+h^QIpcTV>YPuM4KNX3kSojhSuI)q~kdBv%IR zP{YPzX2|gE!^UdR2uDKRR;|UXs>S{wU0Q5^dJ-KgS9!~F6D4(lnDi0pXU4Gd%aS?= z&vWOzkqfmWlUTW0SBpTmr->QjW#Sd$B5}zx;hYdtRbobrik1`OiZxfSXCLeC(rCm! z9x;29h{f08*M}8;eaM-z5^_$``lBLpP9Byyolh@$b9GM2{jye{lyuJJVmT)+&xx0s ze=O5HB-vqCROz`>F3*X(xVwGG4Xz)yR;#VZSa>+>>RI`Tba_^?g*Ml%ET}?Tvxw+3 zTc%GEgUvIN7)iPdY9!exMEZG=wr*cKKq&^IxnDHf&1 Date: Tue, 28 Nov 2023 15:50:07 -0500 Subject: [PATCH 41/53] Virtual Boy demo song contest entry by masicbemester --- .../La_Folia_ground_bass_VirtualBoy.fur | Bin 0 -> 3002 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/virtualboy/La_Folia_ground_bass_VirtualBoy.fur diff --git a/demos/virtualboy/La_Folia_ground_bass_VirtualBoy.fur b/demos/virtualboy/La_Folia_ground_bass_VirtualBoy.fur new file mode 100644 index 0000000000000000000000000000000000000000..ccbc1d8abd2a4c9c6864bd6771665a089bc62a24 GIT binary patch literal 3002 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 literal 0 HcmV?d00001 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 332831bb2..537e17d78 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -114,6 +114,7 @@ const char* aboutLine[]={ "LVintageNerd", "Mahbod Karamoozian", "Martin Demsky", + "masicbemester", "MelonadeM", "Miker", "NeoWar", From c758a14dc4bfd84c5a1ee424f8cfa24d653d33ec Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 28 Nov 2023 15:50:41 -0500 Subject: [PATCH 42/53] Virtual Boy demo song contest entry by Mahbod_Karamoozian --- demos/virtualboy/Iridion_3D_Stage_3.fur | Bin 0 -> 2516 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/Iridion_3D_Stage_3.fur diff --git a/demos/virtualboy/Iridion_3D_Stage_3.fur b/demos/virtualboy/Iridion_3D_Stage_3.fur new file mode 100644 index 0000000000000000000000000000000000000000..c8552e951955d1a642054cbb1af7486bb4c8ed86 GIT binary patch literal 2516 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 Date: Tue, 28 Nov 2023 15:51:42 -0500 Subject: [PATCH 43/53] Virtual Boy demo song contest entry by TakuikaNinja --- demos/virtualboy/Professional_Beat.fur | Bin 0 -> 1823 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/Professional_Beat.fur diff --git a/demos/virtualboy/Professional_Beat.fur b/demos/virtualboy/Professional_Beat.fur new file mode 100644 index 0000000000000000000000000000000000000000..f101ca32f473f2c85827d947fa0a6107e7764900 GIT binary patch literal 1823 zcmV+)2jKX4oW)mNY*a-QKKEyT?sm%-5G>gUi&0U7MhOB&u`U0lun>xXM1h5F?e=Ha zZg)!w61HHe#7aVp=z|YD_~M%n?7Nz%35moPV`5C)#3vJ)s1IuNVg2UJ+`Y56yOlOk zXZzhbbLO1yoH;XlclyMxVm_NXlTM7}`ijHpC(1;WAg^^NcOCfFf)EH8M6Yd2&rk_^2K0UJrEb=svWRQ%1wVGA!OU(-sp^cOpQx*bbqy z)f&5#y&HC;FH;*Oy8_gcIWt82yEYO|ZCka2fw zbSG+bLFz@V)8O0d<6EioSsve0z=X~jRYI)Q6&gg@o==SpG>8&y5+!lZQC_S?v1t!p z8vDDp>oLMrdPX{5KG62n(GHTWFTX}B45q}ZDn@e~*WMJ5 zR$&)_U95gmC2>}()xI=`ud9CR6?(L!SKpN3jRqClwc{}_gNfOXPJKDqoBaIs&0l@; z^I%VwoXYy#*k)3Y~Q}_GXL33Nje~f!d>c38MQAz#VNjFSt zJ@72Bbpb?Mm5=bn&phG!A&>4oHdFa5YnGIFlj3f`hgbl23%)k$?Qvz-rp7C@xehh& zCQO>_>NjX}x}RtVyACIVEa6&G^+u#dG8TU1-d|I2V)${h#vQWGIXwA-yGAJD=N&6h zfrUNni7o%)l z4d5}2N9bxGW|T|lLb9`EXNqoG%(0p0z!I}=VOCu8n|Q;}C|?O(Vv?!3{`=S;UojPj zL(KeBPBZnk);00C?s7iQocQ-5bWL(x7aVLPWLuL>O*WM5eHFzGoJWV$H<7I4!BYEO z%jyq`ry3j&da9SD>SZ1b1U46Ax`#>4CZuw3 zQVxci4o>>{#Cbj{=gzRK{aSxObMY%2hPXu&WkA2y3wVG7S|Z>GX1Z-E zi8+#-O3py;(d2F=_j`pCVz+-C_Tty!7v(w{8fXCnBH;Mkfa7|=agRh33*K4^-qG-D z{Q-j_;ON|=;i}kk)H@G8@t7vcfPSq%U}*6xobV38gm*OjTK~}qi-70qj>folG>Y2M zC~8L|?-edze>8@f)>-^I{Gwb(1B12Ea*M$+SMzlUr~J9tkiC3exEk+A7w^M^;#qt7 z{cN>YU7BEZ3c7H0lIJKcMw9%cc{Q%S#>n?D8}AQoVj*;mWu`vO)aJseRS7Oa=;vBM z=yQeBLioK_0FJbTFMOtWpPw>H?XB`%+@-F8WSuf6F@avlmg<|0U7Z-qpPrpeWcVlV Ng{-cx{tb(6qnT&rd0PMg literal 0 HcmV?d00001 From f646e5383f82241f987d3317747a1600dd21c83a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 28 Nov 2023 18:36:52 -0500 Subject: [PATCH 44/53] prepare for automatic wave size detection - READ if you added a chip to Furnace, or have a pull request in progress, edit your system definition in sysDef.cpp. --- src/engine/engine.h | 8 +- src/engine/sysDef.cpp | 199 +++++++++++++++++++++--------------------- 2 files changed, 107 insertions(+), 100 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 43af4127f..583ca7a85 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -294,6 +294,9 @@ struct DivSysDef { unsigned char id_DMF; int channels; bool isFM, isSTD, isCompound; + // width 0: variable + // height 0: no wavetable support + unsigned short waveWidth, waveHeight; unsigned int vgmVersion; unsigned int sampleFormatMask; const char* chanNames[DIV_MAX_CHANS]; @@ -307,7 +310,8 @@ struct DivSysDef { const EffectHandlerMap preEffectHandlers; DivSysDef( const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans, - bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, unsigned int formatMask, const char* desc, + bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, unsigned int formatMask, unsigned short waveWid, unsigned short waveHei, + const char* desc, std::initializer_list chNames, std::initializer_list chShortNames, std::initializer_list chTypes, @@ -325,6 +329,8 @@ struct DivSysDef { isFM(isFMChip), isSTD(isSTDChip), isCompound(compound), + waveWidth(waveWid), + waveHeight(waveHei), vgmVersion(vgmVer), sampleFormatMask(formatMask), effectHandlers(fxHandlers_), diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 5c044afc5..56c814c52 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -370,7 +370,8 @@ int DivEngine::minVGMVersion(DivSystem which) { // define systems like: // sysDefs[DIV_SYSTEM_ID]=new DivSysDef( -// "Name", "Name (japanese, optional)", fileID, fileID_DMF, channels, isFM, isSTD, vgmVersion, +// "Name", "Name (japanese, optional)", fileID, fileID_DMF, channels, isFM, isSTD, vgmVersion, waveWidth, waveHeight, +// "Description", // {"Channel Names", ...}, // {"Channel Short Names", ...}, // {chanTypes, ...}, @@ -618,7 +619,7 @@ void DivEngine::registerSystems() { // this chip uses YMZ ADPCM, but the emulator uses ADPCM-B because I got it wrong back then. sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( - "Yamaha YMU759 (MA-2)", NULL, 0x01, 0x01, 17, true, false, 0, false, (1U<", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_GENESIS_EXT]=new DivSysDef( - "Sega Genesis Extended Channel 3", NULL, 0x42, 0x42, 13, true, true, 0, true, 0, + "Sega Genesis Extended Channel 3", NULL, 0x42, 0x42, 13, true, true, 0, true, 0, 0, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_SMS]=new DivSysDef( - "TI SN76489", NULL, 0x03, 0x03, 4, false, true, 0x150, false, 0, + "TI SN76489", NULL, 0x03, 0x03, 4, false, true, 0x150, false, 0, 0, 0, "a square/noise sound chip found on the Sega Master System, ColecoVision, Tandy, TI's own 99/4A and a few other places.", {"Square 1", "Square 2", "Square 3", "Noise"}, {"S1", "S2", "S3", "NO"}, @@ -652,13 +653,13 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_SMS_OPLL]=new DivSysDef( - "Sega Master System + FM Expansion", NULL, 0x43, 0x43, 13, true, true, 0, true, 0, + "Sega Master System + FM Expansion", NULL, 0x43, 0x43, 13, true, true, 0, true, 0, 0, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_GB]=new DivSysDef( - "Game Boy", NULL, 0x04, 0x04, 4, false, true, 0x161, false, 0, + "Game Boy", NULL, 0x04, 0x04, 4, false, true, 0x161, false, 0, 32, 16, "the most popular portable game console of the era.", {"Pulse 1", "Pulse 2", "Wavetable", "Noise"}, {"S1", "S2", "WA", "NO"}, @@ -675,7 +676,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_PCE]=new DivSysDef( - "PC Engine/TurboGrafx-16", NULL, 0x05, 0x05, 6, false, true, 0x161, false, 1U<", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_NES_FDS]=new DivSysDef( - "Famicom Disk System", NULL, 0, 0x86, 6, false, true, 0, true, 0, + "Famicom Disk System", NULL, 0, 0x86, 6, false, true, 0, true, 0, 0, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_C64_6581]=new DivSysDef( - "Commodore 64 (6581)", NULL, 0x47, 0x47, 3, false, true, 0, false, 0, + "Commodore 64 (6581)", NULL, 0x47, 0x47, 3, false, true, 0, false, 0, 0, 0, "this computer is powered by the SID chip, which had synthesizer features like a filter and ADSR.", {"Channel 1", "Channel 2", "Channel 3"}, {"CH1", "CH2", "CH3"}, @@ -738,7 +739,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_C64_8580]=new DivSysDef( - "Commodore 64 (8580)", NULL, 0x07, 0x07, 3, false, true, 0, false, 0, + "Commodore 64 (8580)", NULL, 0x07, 0x07, 3, false, true, 0, false, 0, 0, 0, "this computer is powered by the SID chip, which had synthesizer features like a filter and ADSR.\nthis is the newer revision of the chip.", {"Channel 1", "Channel 2", "Channel 3"}, {"CH1", "CH2", "CH3"}, @@ -750,13 +751,13 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_ARCADE]=new DivSysDef( - "DefleCade", NULL, 0x08, 0x08, 13, true, false, 0, true, 0, + "DefleCade", NULL, 0x08, 0x08, 13, true, false, 0, true, 0, 0, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_YM2610]=new DivSysDef( - "Neo Geo CD", NULL, 0x09, 0x09, 13, true, true, 0x151, false, (1U< Date: Thu, 30 Nov 2023 22:17:08 +0100 Subject: [PATCH 45/53] more gb instruments two was not enough --- instruments/GB/closed hi-hat.fui | Bin 0 -> 67 bytes instruments/GB/open hihat.fui | Bin 0 -> 52 bytes instruments/GB/square fade-in.fui | Bin 0 -> 52 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 instruments/GB/closed hi-hat.fui create mode 100644 instruments/GB/open hihat.fui create mode 100644 instruments/GB/square fade-in.fui diff --git a/instruments/GB/closed hi-hat.fui b/instruments/GB/closed hi-hat.fui new file mode 100644 index 0000000000000000000000000000000000000000..7db73f6b7ffb523fb746041bab3ee726bc6afa5b GIT binary patch literal 67 zcmZ?s^b6k2z{KF^$jjiFS6ot5nwy$eqF}(_>nO*-!N9=&|33pG10z2-CmSm>10y4l Vdys)qn}NaMzq=C)gT4bJ0{|@h4=4Zt literal 0 HcmV?d00001 diff --git a/instruments/GB/open hihat.fui b/instruments/GB/open hihat.fui new file mode 100644 index 0000000000000000000000000000000000000000..afc51e768053c19ec0e724fad6c57b35d9240b60 GIT binary patch literal 52 zcmZ?s^b6k2z{KF^$jjiFS6ot5nwy$eqF~72>&VZ*!NAD)|3AY)21YFg28aLdPAm-i H4h#$cZQ=~z literal 0 HcmV?d00001 diff --git a/instruments/GB/square fade-in.fui b/instruments/GB/square fade-in.fui new file mode 100644 index 0000000000000000000000000000000000000000..d7e2f547ee098a8416a97dd9e974fe391ef960b2 GIT binary patch literal 52 zcmZ?s^b6k2z{KF^$jjiFS6ot5nwy$eqF}_}>&U~v!NA1$|33pG10&OacPDm+4Gs*9 H%nS?wX5kD! literal 0 HcmV?d00001 From 1b59b6b98f54b723be2f254dd9d94a38f3016e6c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 30 Nov 2023 16:19:33 -0500 Subject: [PATCH 46/53] Virtual Boy demo song contest entry by dmKaltsit --- demos/virtualboy/blueseed.fur | Bin 0 -> 3474 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/virtualboy/blueseed.fur diff --git a/demos/virtualboy/blueseed.fur b/demos/virtualboy/blueseed.fur new file mode 100644 index 0000000000000000000000000000000000000000..549406b07186d26079272200c8267145bf22f2b7 GIT binary patch literal 3474 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 A9{>OV literal 0 HcmV?d00001 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 537e17d78..fe0e5a461 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -88,6 +88,7 @@ const char* aboutLine[]={ "DevEd", "Dippy", "djtuBIG-MaliceX", + "dmKaltsit", "dumbut", "Eknous", "Electric Keet", From e1111db0402270d07d2c8e4e8a4b6c66ab7ee89c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 30 Nov 2023 18:53:38 -0500 Subject: [PATCH 47/53] fix build error --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b8a94787..752c1164c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,7 +143,7 @@ if (WIN32) endif() endif() -list(APPEND DEPENDENCIES_INCLUDE_DIRS "extern/SAASound/include") +#list(APPEND DEPENDENCIES_INCLUDE_DIRS "extern/SAASound/include") list(APPEND DEPENDENCIES_INCLUDE_DIRS "extern/vgsound_emu-modified") find_package(Threads REQUIRED) From 7c672f50a9552ee3fbbd19955410d04a2e2b1780 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 1 Dec 2023 02:43:35 -0500 Subject: [PATCH 48/53] Virtual Boy demo song contest by AquaDoesStuff --- demos/virtualboy/Redworld.fur | Bin 0 -> 4594 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/virtualboy/Redworld.fur diff --git a/demos/virtualboy/Redworld.fur b/demos/virtualboy/Redworld.fur new file mode 100644 index 0000000000000000000000000000000000000000..d52ee8d3788633f2a13ba2b31c2f07b179b513af GIT binary patch literal 4594 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 H-o*VM`8)j= literal 0 HcmV?d00001 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index fe0e5a461..8418b0168 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -72,6 +72,7 @@ const char* aboutLine[]={ "airconmanws", "akumanatt", "AmigaX", + "AquaDoesStuff", "AURORA*FIELDS", "battybeats", "Bernie", From d8e3c175634839772fa9a6aa4a0a6677f7756a4e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 1 Dec 2023 02:44:37 -0500 Subject: [PATCH 49/53] Virtual Boy demo song contest entry by PichuMario --- demos/virtualboy/redboas.fur | Bin 0 -> 3671 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/redboas.fur diff --git a/demos/virtualboy/redboas.fur b/demos/virtualboy/redboas.fur new file mode 100644 index 0000000000000000000000000000000000000000..07726d40a0f741ebbea2f4a327f5875ad90bc629 GIT binary patch literal 3671 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 Date: Fri, 1 Dec 2023 02:45:38 -0500 Subject: [PATCH 50/53] Virtual Boy demo song contest entry by tapekeep --- demos/virtualboy/needforspeed2semainmenu.fur | Bin 0 -> 5459 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/virtualboy/needforspeed2semainmenu.fur diff --git a/demos/virtualboy/needforspeed2semainmenu.fur b/demos/virtualboy/needforspeed2semainmenu.fur new file mode 100644 index 0000000000000000000000000000000000000000..8b650664a3eb74f654a9e55eb0f135b59030ad8c GIT binary patch literal 5459 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 Date: Fri, 1 Dec 2023 10:46:39 +0100 Subject: [PATCH 51/53] more OPZ instruments --- instruments/OPZ/funkysynthbass.fui | Bin 0 -> 65 bytes instruments/OPZ/horrorpad.fui | Bin 0 -> 108 bytes instruments/OPZ/rimshot.fui | Bin 0 -> 108 bytes instruments/OPZ/synthdulcimer.fui | Bin 0 -> 65 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 instruments/OPZ/funkysynthbass.fui create mode 100644 instruments/OPZ/horrorpad.fui create mode 100644 instruments/OPZ/rimshot.fui create mode 100644 instruments/OPZ/synthdulcimer.fui diff --git a/instruments/OPZ/funkysynthbass.fui b/instruments/OPZ/funkysynthbass.fui new file mode 100644 index 0000000000000000000000000000000000000000..6bc0975aa1e248a32fb96f22476073c7edd26137 GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF~72=BvW+g@J(~N?o4A;WGnsfS5ds!$t-s V1_gO8hc66FOic2e4nG+l001JC5AFZ} literal 0 HcmV?d00001 diff --git a/instruments/OPZ/horrorpad.fui b/instruments/OPZ/horrorpad.fui new file mode 100644 index 0000000000000000000000000000000000000000..2f053d0689e56aa98d874ae32192c9187f323162 GIT binary patch literal 108 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF~72=BvW+C0w2%N?pFe;WGns5QjXA!!8CU vCJuR4hhG<%n3#a{PlgA+j_M2?49txG|1&T$Ffy}38C7ghMj<$i9 wOdRrp4!;Z-n3?1!I{ai{@O4yY;9y{8{QsYUk%5t!70L);gEH#bp^UHp0S00m+yDRo literal 0 HcmV?d00001 diff --git a/instruments/OPZ/synthdulcimer.fui b/instruments/OPZ/synthdulcimer.fui new file mode 100644 index 0000000000000000000000000000000000000000..05c8e2ef33b49b1e140ce57f7c4478f46539d3ba GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+#hrnHSxcV7;WGn!vWh&5!$t;H SMkaYKho1}#iA?gGAQ}KA$PaA* literal 0 HcmV?d00001 From acc2a4b149c982c17747b02acd5ff04e774e42f7 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:57:08 +0100 Subject: [PATCH 52/53] even more OPZ instruments --- instruments/OPZ/accordion.fui | Bin 0 -> 65 bytes instruments/OPZ/accordion2.fui | Bin 0 -> 65 bytes instruments/OPZ/bagpipe.fui | Bin 0 -> 65 bytes instruments/OPZ/overdriven_guitar.fui | Bin 0 -> 65 bytes instruments/OPZ/reedorgan.fui | Bin 0 -> 65 bytes instruments/OPZ/sitar.fui | Bin 0 -> 65 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 instruments/OPZ/accordion.fui create mode 100644 instruments/OPZ/accordion2.fui create mode 100644 instruments/OPZ/bagpipe.fui create mode 100644 instruments/OPZ/overdriven_guitar.fui create mode 100644 instruments/OPZ/reedorgan.fui create mode 100644 instruments/OPZ/sitar.fui diff --git a/instruments/OPZ/accordion.fui b/instruments/OPZ/accordion.fui new file mode 100644 index 0000000000000000000000000000000000000000..d512a9acb296d9e5e940d6e72a19111d13b39710 GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+#hrnHQCyzE;WGnsk_wRC$iT|T O#LwdJlYt=-N&^5N+z%W8 literal 0 HcmV?d00001 diff --git a/instruments/OPZ/accordion2.fui b/instruments/OPZ/accordion2.fui new file mode 100644 index 0000000000000000000000000000000000000000..b8f862d13e5d8a5b6ca0442b6c284bcd8e175716 GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+C7gkQMOL1{;WGns1{)u{!%c>z QEKK|?4nG+d8A0f!05ZrB_5c6? literal 0 HcmV?d00001 diff --git a/instruments/OPZ/bagpipe.fui b/instruments/OPZ/bagpipe.fui new file mode 100644 index 0000000000000000000000000000000000000000..d59e346efb51aa5f436cdf652dae31c398c94c4e GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+#eso=MO~i3;WGnshKfA5!$k&G QMkan1ho1}#Nf4S903{m_UjP6A literal 0 HcmV?d00001 diff --git a/instruments/OPZ/overdriven_guitar.fui b/instruments/OPZ/overdriven_guitar.fui new file mode 100644 index 0000000000000000000000000000000000000000..2b503ee7b6f1e3fdd839898f2024825caabe9352 GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+MTvodQG}nt;Tr=(l7=vs!%c>T VtWwg<4nG-|8Zk+;`gt-e0{|<-5Lf^J literal 0 HcmV?d00001 diff --git a/instruments/OPZ/reedorgan.fui b/instruments/OPZ/reedorgan.fui new file mode 100644 index 0000000000000000000000000000000000000000..97c471520b748307c8b92ee1c8021aa7f2eddc87 GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+C4hl}MO~i3;WGns1{)u{!%YTO QMkan1ho1}#Nf4S903&A)L;wH) literal 0 HcmV?d00001 diff --git a/instruments/OPZ/sitar.fui b/instruments/OPZ/sitar.fui new file mode 100644 index 0000000000000000000000000000000000000000..8bab269a816939246c789059e3d83fc672cd595b GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+MV*0xQI?;<;Tr>UhPE)f!%c?8 T+)~mk4nG+d88J!o0O^GQFw_wl literal 0 HcmV?d00001 From 260a4f5f657e4c5041ec26cdf84f67eb0e2c4763 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:14:35 +0100 Subject: [PATCH 53/53] OPZ instruments --- instruments/OPZ/distortedhalfsinepad.fui | Bin 0 -> 65 bytes instruments/OPZ/halfsine_pad.fui | Bin 0 -> 65 bytes instruments/OPZ/pickbass.fui | Bin 0 -> 65 bytes instruments/OPZ/timpani_oct2.fui | Bin 0 -> 108 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 instruments/OPZ/distortedhalfsinepad.fui create mode 100644 instruments/OPZ/halfsine_pad.fui create mode 100644 instruments/OPZ/pickbass.fui create mode 100644 instruments/OPZ/timpani_oct2.fui diff --git a/instruments/OPZ/distortedhalfsinepad.fui b/instruments/OPZ/distortedhalfsinepad.fui new file mode 100644 index 0000000000000000000000000000000000000000..b95079d4ae1473342734981afdda3e0af4bbf720 GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+rHFxngPViF;X4CU8izcC!}bqS S985s|PavO3p2gv(0}}u=QxJ9l literal 0 HcmV?d00001 diff --git a/instruments/OPZ/halfsine_pad.fui b/instruments/OPZ/halfsine_pad.fui new file mode 100644 index 0000000000000000000000000000000000000000..7a5d92598543d897c3c95aa7a0e731253e85c2a5 GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+rHFxngPViF;X4CU8izcC!*&KH P4kjS~Cy>u1&jO+WAzKeF literal 0 HcmV?d00001 diff --git a/instruments/OPZ/pickbass.fui b/instruments/OPZ/pickbass.fui new file mode 100644 index 0000000000000000000000000000000000000000..322c41b2514701f61de74f1d2a675d1e964e7083 GIT binary patch literal 65 zcmZ?s^b6k2Ak5(B$jjiFS6ot5nwy$eqF}(_=BvW+MU{bpQCptD;Tr>6qM9g|!%c>T V90t!lCmh*ftvvf x*)*iN9ey$_GGvnGJm<-