diff --git a/CMakeLists.txt b/CMakeLists.txt index fbaf944b..59f88820 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,7 @@ extern/adpcm/ymb_codec.c extern/adpcm/ymz_codec.c extern/Nuked-OPN2/ym3438.c +extern/Nuked-PSG/ympsg.c extern/opm/opm.c extern/Nuked-OPLL/opll.c extern/opl/opl3.c diff --git a/extern/Nuked-PSG/LICENSE b/extern/Nuked-PSG/LICENSE new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/extern/Nuked-PSG/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/Nuked-PSG/README.md b/extern/Nuked-PSG/README.md new file mode 100644 index 00000000..af533a79 --- /dev/null +++ b/extern/Nuked-PSG/README.md @@ -0,0 +1,6 @@ +# Nuked-PSG +Yamaha YM7101 PSG emulator + +# modification disclaimer + +this is a modified version of Nuked-PSG which adds support for changing the noise LFSR size and taps. diff --git a/extern/Nuked-PSG/ympsg.c b/extern/Nuked-PSG/ympsg.c new file mode 100644 index 00000000..8c1715ec --- /dev/null +++ b/extern/Nuked-PSG/ympsg.c @@ -0,0 +1,403 @@ +// Copyright (C) 2021 Nuke.YKT +// License: GPLv2+ +// Version 1.0.1 +#include +#include "ympsg.h" + +const float ympsg_vol[17] = { + 1.0, 0.772, 0.622, 0.485, 0.382, 0.29, 0.229, 0.174, 0.132, 0.096, 0.072, 0.051, 0.034, 0.019, 0.009, 0.0, -1.059 +}; + +static void YMPSG_WriteLatch(ympsg_t *chip) +{ + uint8_t data = chip->data; + if (chip->data_mask) + { + data = 0; + } + if (data & 128) + { + chip->latch = (data >> 4) & 7; + } +} + +static void YMPSG_UpdateRegisters(ympsg_t* chip) +{ + uint8_t data = chip->data; + if (chip->data_mask) + { + data = 0; + } + if (chip->reg_reset || (chip->write_flag_l && chip->latch == 1)) + { + chip->volume[0] = data & 15; + if (chip->data_mask) + { + chip->volume[0] = 15; + } + } + if (chip->reg_reset || (chip->write_flag_l && chip->latch == 3)) + { + chip->volume[1] = data & 15; + if (chip->data_mask) + { + chip->volume[1] = 15; + } + } + if (chip->reg_reset || (chip->write_flag_l && chip->latch == 4)) + { + if ((data & 128) || chip->reg_reset) + { + chip->freq[2] &= 1008; + chip->freq[2] |= data & 15; + } + if (!(data & 128)) + { + chip->freq[2] &= 15; + chip->freq[2] |= (data << 4) & 1008; + } + } + if (chip->reg_reset || (chip->write_flag_l && chip->latch == 2)) + { + if ((data & 128) || chip->reg_reset) + { + chip->freq[1] &= 1008; + chip->freq[1] |= data & 15; + } + if (!(data & 128)) + { + chip->freq[1] &= 15; + chip->freq[1] |= (data << 4) & 1008; + } + } + if (chip->reg_reset || (chip->write_flag_l && chip->latch == 5)) + { + chip->volume[2] = data & 15; + if (chip->data_mask) + { + chip->volume[2] = 15; + } + } + if (chip->reg_reset || (chip->write_flag_l && chip->latch == 0)) + { + if ((data & 128) || chip->reg_reset) + { + chip->freq[0] &= 1008; + chip->freq[0] |= data & 15; + } + if (!(data & 128)) + { + chip->freq[0] &= 15; + chip->freq[0] |= (data << 4) & 1008; + } + } + if (chip->reg_reset || (chip->write_flag_l && chip->latch == 7)) + { + chip->volume[3] = data & 15; + if (chip->data_mask) + { + chip->volume[3] = 15; + } + } + if (chip->reg_reset || (chip->write_flag_l && chip->latch == 6)) + { + chip->noise_data = data & 7; + chip->noise_trig = 1; + } +} + +static void YMPSG_ClockInternal1(ympsg_t *chip) +{ + uint16_t freq = 0; + uint8_t chan_sel = chip->chan_sel; + uint8_t noise_of, noise_bit1, noise_bit2, noise_next; + if ((chip->noise_data & 3) == 3) + { + noise_of = (chip->sign >> 1) & 1; + } + else + { + noise_of = chip->sign & 1; + } + if (chip->noise_trig_l || (chip->ic_latch2 & 1)) + { + chip->noise = 0; + } + else if (noise_of && !chip->noise_of) + { + noise_bit1 = (chip->noise >> 15) & 1; + noise_bit2 = (chip->noise >> 12) & 1; + noise_bit1 ^= noise_bit2; + noise_next = ((noise_bit1 && ((chip->noise_data >> 2) & 1)) || ((chip->noise & 32767) == 0)); + chip->noise <<= 1; + chip->noise |= noise_next; + } + chip->noise_of = noise_of; + if (chip->ic_latch2 & 2) + { + chan_sel = 0; + } + if (chip->chan_sel & 1) + { + freq |= chip->freq[0]; + } + if (chip->chan_sel & 2) + { + freq |= chip->freq[1]; + } + if (chip->chan_sel & 4) + { + freq |= chip->freq[2]; + } + if (chip->chan_sel & 8) + { + if ((chip->noise_data & 3) == 0) + { + freq |= 16; + } + if ((chip->noise_data & 3) == 1) + { + freq |= 32; + } + if ((chip->noise_data & 3) == 2) + { + freq |= 64; + } + } + if (chip->chan_sel & 1) + { + chip->sign ^= chip->counter_of & 15; + } + if (chip->ic_latch2 & 2) + { + chip->sign = 0; + } + chip->counter_of <<= 1; + if (chip->counter[chip->rot] >= freq) + { + chip->counter_of |= 1; + chip->counter[chip->rot] = 0; + } + if (chip->ic_latch2 & 2) + { + chip->counter[chip->rot] = 0; + } + chip->counter[chip->rot]++; + chip->counter[chip->rot] &= 1023; + if ((chip->ic_latch2 & 1) || (chip->chan_sel & 7) != 0) + { + chip->chan_sel <<= 1; + } + else + { + chip->chan_sel <<= 1; + chip->chan_sel |= 1; + } + chip->ic_latch2 <<= 1; + chip->ic_latch2 |= chip->ic & 1; + chip->noise_trig_l = chip->noise_trig; +} + +static void YMPSG_ClockInternal2(ympsg_t *chip) +{ + chip->data_mask = (chip->ic_latch2 >> 1) & 1; + chip->reg_reset = (chip->ic_latch2 >> 0) & 1; + if (chip->noise_trig_l) + { + chip->noise_trig = 0; + } + YMPSG_UpdateRegisters(chip); + + chip->rot = (chip->rot + 1) & 3; + chip->sign_l = chip->sign; + chip->noise_sign_l = (chip->noise >> 14) & 1; +} + +static void YMPSG_UpdateSample(ympsg_t *chip) +{ + uint32_t i; + uint8_t sign = chip->sign & 14; + sign |= chip->noise_sign_l; + if (chip->test & 1) + { + sign |= 15; + } + for (i = 0; i < 4; i++) + { + if ((sign >> (3 - i)) & 1) + { + chip->volume_out[i] = chip->volume[i]; + } + else + { + chip->volume_out[i] = 15; + } + } +} + +void YMPSG_Write(ympsg_t *chip, uint8_t data) +{ + chip->data = data; + chip->write_flag = 1; +} + +uint16_t YMPSG_Read(ympsg_t *chip) +{ + uint16_t data = 0; + uint32_t i; + YMPSG_UpdateSample(chip); + for (i = 0; i < 4; i++) + { + data |= chip->volume_out[i] << ((3 - i) * 4); + } + return data; +} + +void YMPSG_Init(ympsg_t *chip) +{ + uint32_t i; + memset(chip, 0, sizeof(ympsg_t)); + YMPSG_SetIC(chip, 1); + for (i = 0; i < 16; i++) + { + YMPSG_Clock(chip); + } + YMPSG_SetIC(chip, 0); +} + +void YMPSG_SetIC(ympsg_t *chip, uint32_t ic) +{ + chip->ic = (uint8_t)ic; +} + +void YMPSG_Clock(ympsg_t *chip) +{ + uint8_t prescaler2_latch; + prescaler2_latch = chip->prescaler_2; + chip->prescaler_2 += chip->prescaler_1; + chip->prescaler_2 &= 1; + chip->prescaler_1 ^= 1; + if ((chip->ic_latch1 & 3) == 2) + { + chip->prescaler_1 = 0; + chip->prescaler_2 = 0; + } + chip->ic_latch1 <<= 1; + chip->ic_latch1 |= chip->ic & 1; + YMPSG_UpdateRegisters(chip); + chip->write_flag_l = 0; + if (chip->write_flag) + { + YMPSG_WriteLatch(chip); + chip->write_flag = 0; + chip->write_flag_l = 1; + } + if (chip->prescaler_1) + { + if (!prescaler2_latch) + { + YMPSG_ClockInternal1(chip); + } + else + { + YMPSG_ClockInternal2(chip); + } + } +} + +float YMPSG_GetOutput(ympsg_t *chip) +{ + float sample = 0.f; + uint32_t i; + YMPSG_UpdateSample(chip); + if (chip->test & 1) + { + sample += ympsg_vol[chip->volume_out[chip->test >> 1]]; + sample += ympsg_vol[16] * 3.f; + } + else if (!chip->mute) + { + sample += ympsg_vol[chip->volume_out[0]]; + sample += ympsg_vol[chip->volume_out[1]]; + sample += ympsg_vol[chip->volume_out[2]]; + sample += ympsg_vol[chip->volume_out[3]]; + } + else + { + for (i = 0; i < 4; i++) + { + if (!((chip->mute>>i) & 1)) + sample += ympsg_vol[chip->volume_out[i]]; + } + } + return sample; +} + +void YMPSG_Test(ympsg_t *chip, uint16_t test) +{ + chip->test = (test >> 9) & 7; +} + + +void YMPSG_Generate(ympsg_t *chip, int32_t *buf) +{ + uint32_t i; + float out; + + for (i = 0; i < 16; i++) + { + YMPSG_Clock(chip); + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!chip->writebuf[chip->writebuf_cur].stat) + { + break; + } + chip->writebuf[chip->writebuf_cur].stat = 0; + YMPSG_Write(chip, chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % YMPSG_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; + } + out = YMPSG_GetOutput(chip); + *buf = (int32_t)(out * 8192.f); +} + +void YMPSG_WriteBuffered(ympsg_t *chip, uint8_t data) +{ + uint64_t time1, time2; + uint64_t skip; + + if (chip->writebuf[chip->writebuf_last].stat) + { + YMPSG_Write(chip, chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % YMPSG_WRITEBUF_SIZE; + skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + while (skip--) + { + YMPSG_Clock(chip); + } + } + + chip->writebuf[chip->writebuf_last].stat = 1; + chip->writebuf[chip->writebuf_last].data = data; + time1 = chip->writebuf_lasttime + YMPSG_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % YMPSG_WRITEBUF_SIZE; +} + +void YMPSG_SetMute(ympsg_t *chip, uint8_t mute) +{ + chip->mute = mute; +} diff --git a/extern/Nuked-PSG/ympsg.h b/extern/Nuked-PSG/ympsg.h new file mode 100644 index 00000000..f1596214 --- /dev/null +++ b/extern/Nuked-PSG/ympsg.h @@ -0,0 +1,77 @@ +// Copyright (C) 2021 Nuke.YKT +// License: GPLv2+ +// Version 1.0.1 +#ifndef _YMPSG_H_ +#define _YMPSG_H_ +#include + +#define YMPSG_WRITEBUF_SIZE 2048 +#define YMPSG_WRITEBUF_DELAY 8 + +typedef struct _ympsg_writebuf { + uint64_t time; + uint8_t stat; + uint8_t data; +} ympsg_writebuf; + +typedef struct { + // IO + uint8_t data; + uint8_t latch; + uint8_t write_flag; + uint8_t write_flag_l; + + uint8_t prescaler_1; + uint8_t prescaler_2; + uint8_t prescaler_2_l; + uint8_t reset_latch; + uint8_t ic; + uint8_t ic_latch1; + uint8_t ic_latch2; + uint8_t data_mask; + uint8_t reg_reset; + uint8_t volume[4]; + uint16_t freq[3]; + uint8_t noise_data; + uint8_t noise_of; + uint8_t noise_trig; + uint8_t noise_trig_l; + uint8_t rot; + + uint8_t chan_sel; + + uint16_t counter[4]; + uint8_t counter_of; + uint8_t sign; + uint8_t sign_l; + uint8_t noise_sign_l; + uint16_t noise; + uint8_t test; + uint8_t volume_out[4]; + + // + uint64_t writebuf_samplecnt; + uint32_t writebuf_cur; + uint32_t writebuf_last; + uint64_t writebuf_lasttime; + ympsg_writebuf writebuf[YMPSG_WRITEBUF_SIZE]; + + uint8_t mute; +} ympsg_t; + + +void YMPSG_Write(ympsg_t *chip, uint8_t data); +uint16_t YMPSG_Read(ympsg_t *chip); +void YMPSG_Init(ympsg_t *chip); +void YMPSG_SetIC(ympsg_t *chip, uint32_t ic); +void YMPSG_Clock(ympsg_t *chip); +float YMPSG_GetOutput(ympsg_t *chip); +void YMPSG_Test(ympsg_t *chip, uint16_t test); + + +void YMPSG_Generate(ympsg_t *chip, int32_t *buf); +void YMPSG_WriteBuffered(ympsg_t *chip, uint8_t data); + +void YMPSG_SetMute(ympsg_t *chip, uint8_t mute); + +#endif diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 4bda45ca..498ff5c3 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -191,6 +191,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do break; case DIV_SYSTEM_SMS: dispatch=new DivPlatformSMS; + ((DivPlatformSMS*)dispatch)->setNuked(eng->getConfInt("snCore",0)); break; case DIV_SYSTEM_GB: dispatch=new DivPlatformGB; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 93b2b255..302317e9 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -21,7 +21,7 @@ #include "../engine.h" #include -#define rWrite(v) {if (!skipRegisterWrites) {sn->write(v); if (dumpWrites) {addWrite(0x200,v);}}} +#define rWrite(v) {if (!skipRegisterWrites) {writes.push(v); if (dumpWrites) {addWrite(0x200,v);}}} const char* regCheatSheetSN[]={ "DATA", "0", @@ -41,7 +41,47 @@ const char* DivPlatformSMS::getEffectName(unsigned char effect) { return NULL; } -void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) { +void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { + for (size_t h=start; hdata[oscBuf[i]->needle++]=0; + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i); + } + }*/ + } +} + +void DivPlatformSMS::acquire_mame(short* bufL, short* bufR, size_t start, size_t len) { + while (!writes.empty()) { + unsigned char w=writes.front(); + sn->write(w); + writes.pop(); + } for (size_t h=start; hsound_stream_update(bufL+h,1); for (int i=0; i<4; i++) { @@ -54,6 +94,14 @@ void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) } } +void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) { + if (nuked) { + acquire_nuked(bufL,bufR,start,len); + } else { + acquire_mame(bufL,bufR,start,len); + } +} + int DivPlatformSMS::acquireOne() { short v; sn->sound_stream_update(&v,1); @@ -301,6 +349,7 @@ DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) { } void DivPlatformSMS::reset() { + while (!writes.empty()) writes.pop(); for (int i=0; i<4; i++) { chan[i]=DivPlatformSMS::Channel(); chan[i].std.setEngine(parent); @@ -309,6 +358,7 @@ void DivPlatformSMS::reset() { addWrite(0xffffffff,0); } sn->device_start(); + YMPSG_Init(&sn_nuked); snNoiseMode=3; rWrite(0xe7); updateSNMode=false; @@ -352,6 +402,7 @@ void DivPlatformSMS::setFlags(unsigned int flags) { chipClock=COLOR_NTSC; } resetPhase=!(flags&16); + if (sn!=NULL) delete sn; switch ((flags>>2)&3) { case 1: // TI @@ -377,6 +428,10 @@ void DivPlatformSMS::setFlags(unsigned int flags) { } } +void DivPlatformSMS::setNuked(bool value) { + nuked=value; +} + int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index d1b52881..3c97a9a5 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -23,6 +23,10 @@ #include "../dispatch.h" #include "../macroInt.h" #include "sound/sn76496.h" +extern "C" { + #include "../../../extern/Nuked-PSG/ympsg.h" +} +#include class DivPlatformSMS: public DivDispatch { struct Channel { @@ -59,8 +63,14 @@ class DivPlatformSMS: public DivDispatch { bool updateSNMode; bool resetPhase; bool isRealSN; + bool nuked; sn76496_base_device* sn; + ympsg_t sn_nuked; + std::queue writes; friend void putDispatchChan(void*,int,int); + + void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len); + void acquire_mame(short* bufL, short* bufR, size_t start, size_t len); public: int acquireOne(); void acquire(short* bufL, short* bufR, size_t start, size_t len); @@ -80,6 +90,7 @@ class DivPlatformSMS: public DivDispatch { void poke(std::vector& wlist); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); + void setNuked(bool value); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); ~DivPlatformSMS(); diff --git a/src/gui/gui.h b/src/gui/gui.h index ecbabb95..8a2c8f64 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -864,6 +864,7 @@ class FurnaceGUI { int audioQuality; int arcadeCore; int ym2612Core; + int snCore; int saaCore; int nesCore; int fdsCore; @@ -958,6 +959,7 @@ class FurnaceGUI { audioQuality(0), arcadeCore(0), ym2612Core(0), + snCore(0), saaCore(1), nesCore(0), fdsCore(0), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index a5c9e23b..0d16f389 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -79,6 +79,11 @@ const char* ym2612Cores[]={ "ymfm" }; +const char* snCores[]={ + "MAME", + "Nuked-PSG Mod" +}; + const char* saaCores[]={ "MAME", "SAASound" @@ -872,6 +877,10 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2); + ImGui::Text("SN76489 core"); + ImGui::SameLine(); + ImGui::Combo("##SNCore",&settings.snCore,snCores,2); + ImGui::Text("SAA1099 core"); ImGui::SameLine(); ImGui::Combo("##SAACore",&settings.saaCore,saaCores,2); @@ -1889,6 +1898,7 @@ void FurnaceGUI::syncSettings() { settings.audioRate=e->getConfInt("audioRate",44100); settings.arcadeCore=e->getConfInt("arcadeCore",0); settings.ym2612Core=e->getConfInt("ym2612Core",0); + settings.snCore=e->getConfInt("snCore",0); settings.saaCore=e->getConfInt("saaCore",1); settings.nesCore=e->getConfInt("nesCore",0); settings.fdsCore=e->getConfInt("fdsCore",0); @@ -1975,6 +1985,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.audioRate,8000,384000); clampSetting(settings.arcadeCore,0,1); clampSetting(settings.ym2612Core,0,1); + clampSetting(settings.snCore,0,1); clampSetting(settings.saaCore,0,1); clampSetting(settings.nesCore,0,1); clampSetting(settings.fdsCore,0,1); @@ -2089,6 +2100,7 @@ void FurnaceGUI::commitSettings() { e->setConf("audioRate",settings.audioRate); e->setConf("arcadeCore",settings.arcadeCore); e->setConf("ym2612Core",settings.ym2612Core); + e->setConf("snCore",settings.snCore); e->setConf("saaCore",settings.saaCore); e->setConf("nesCore",settings.nesCore); e->setConf("fdsCore",settings.fdsCore);