diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01817d11b..fe4d4579e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,8 +22,8 @@ jobs: - { name: 'Windows MSVC x86_64', os: windows-latest, compiler: msvc, arch: x86_64 } #- { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 } #- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 } - - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } - - { name: 'macOS ARM', os: macos-latest, arch: arm64 } + #- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } + #- { name: 'macOS ARM', os: macos-latest, arch: arm64 } - { name: 'Linux x86_64', os: ubuntu-20.04, arch: x86_64 } #- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf } fail-fast: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 33ce050be..d6816c696 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) @@ -489,6 +489,9 @@ 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 +extern/YMF276-LLE/fmopn2.c extern/ESFMu/esfm.c extern/ESFMu/esfm_registers.c @@ -558,9 +561,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 @@ -1075,6 +1080,7 @@ if (NOT ANDROID OR TERMUX) set(CPACK_BUNDLE_PLIST ${CMAKE_SOURCE_DIR}/res/Info.plist) set(CPACK_BUNDLE_ICON ${CMAKE_SOURCE_DIR}/res/icon.icns) set(CPACK_BUNDLE_STARTUP_COMMAND "furnace") + set(CPACK_BUNDLE_APPLE_CERT_APP "-") endif() include(CPack) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f8a9d74ca..c93655462 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,6 +35,7 @@ the coding style is described here: - preprocessor directives not intended - if macro comprises more than one line, indent - no new line after `template<>` +- do not use `_t` types, except for 64-bit integers and `size_t`. - prefer built-in types: - `bool` - `signed char` or `unsigned char` are 8-bit @@ -48,6 +49,7 @@ the coding style is described here: - `long long int` or `unsigned long long int` are 64-bit - avoid using 64-bit numbers as I still build for 32-bit systems. - two `long`s are required to make Windows happy. + - prefer using `int64_t` or `uint64_t` for this specific case. - `size_t` are 32-bit or 64-bit, depending on architecture. - in float/double operations, always use decimal and `f` if single-precision. - e.g. `1.0f` or `1.0` instead of `1`. diff --git a/TODO.md b/TODO.md index af6e0f6c3..06951982a 100644 --- a/TODO.md +++ b/TODO.md @@ -4,11 +4,9 @@ # THE REAL TO-DO LIST -- Song is silent after playing an order after loop point -- Select loaded instrument on open - rewrite because I want a setting... - finish auto-clone -once you have done all of this (maybe not the first one) and merged the two or so pending pull requests, release 0.6.1 +once you have done all of this (maybe not the first one), release 0.6.1 Furnace is like alcohol... diff --git a/demos/misc/QSound_smile.fur b/demos/arcade/QSound_smile.fur similarity index 100% rename from demos/misc/QSound_smile.fur rename to demos/arcade/QSound_smile.fur diff --git a/demos/genesis/Carnage.fur b/demos/genesis/Carnage.fur index 5d8b2d5cc..8f4acfefd 100644 Binary files a/demos/genesis/Carnage.fur and b/demos/genesis/Carnage.fur differ diff --git a/demos/genesis/Checknobankh.fur b/demos/genesis/Checknobankh.fur deleted file mode 100644 index ac465df98..000000000 Binary files a/demos/genesis/Checknobankh.fur and /dev/null differ diff --git a/demos/genesis/MegadriveOverdrive.fur b/demos/genesis/MegadriveOverdrive.fur index cc895f8d6..b985151ad 100644 Binary files a/demos/genesis/MegadriveOverdrive.fur and b/demos/genesis/MegadriveOverdrive.fur differ diff --git a/demos/genesis/Plok_Beach.fur b/demos/genesis/Plok_Beach.fur index 3c2fd8288..ed3fca617 100644 Binary files a/demos/genesis/Plok_Beach.fur and b/demos/genesis/Plok_Beach.fur differ diff --git a/demos/genesis/Solar_Man.fur b/demos/genesis/Solar_Man.fur index a50427a87..477d445ba 100644 Binary files a/demos/genesis/Solar_Man.fur and b/demos/genesis/Solar_Man.fur differ diff --git a/demos/genesis/yky.fur b/demos/genesis/yky.fur index fc8188c19..cb8732a6b 100644 Binary files a/demos/genesis/yky.fur and b/demos/genesis/yky.fur differ diff --git a/demos/misc/Optimistic_SharpX68k.fur b/demos/misc/Optimistic_SharpX68k.fur deleted file mode 100644 index c49e71a8f..000000000 Binary files a/demos/misc/Optimistic_SharpX68k.fur and /dev/null differ diff --git a/demos/misc/waterworld_map_opm.fur b/demos/misc/waterworld_map_opm.fur new file mode 100644 index 000000000..51cafbd63 Binary files /dev/null and b/demos/misc/waterworld_map_opm.fur differ diff --git a/demos/multichip/HoldOn.fur b/demos/multichip/HoldOn.fur index 6eb2e5df0..b0a79b95c 100644 Binary files a/demos/multichip/HoldOn.fur and b/demos/multichip/HoldOn.fur differ diff --git a/demos/multichip/government funding breakcore-ish remix.fur b/demos/multichip/government funding breakcore-ish remix.fur index 7acbabc49..900075d3b 100644 Binary files a/demos/multichip/government funding breakcore-ish remix.fur and b/demos/multichip/government funding breakcore-ish remix.fur differ diff --git a/demos/multichip/second_dimension_demo.fur b/demos/multichip/second_dimension_demo.fur new file mode 100644 index 000000000..bbbbda54b Binary files /dev/null and b/demos/multichip/second_dimension_demo.fur differ diff --git a/demos/multichip/urgency.fur b/demos/multichip/urgency.fur new file mode 100644 index 000000000..f39602e35 Binary files /dev/null and b/demos/multichip/urgency.fur differ diff --git a/demos/nes/spamton.fur b/demos/nes/spamton.fur deleted file mode 100644 index d702c6b57..000000000 Binary files a/demos/nes/spamton.fur and /dev/null differ diff --git a/demos/sn7/456nm_TI994A.fur b/demos/sn7/456nm_TI994A.fur new file mode 100644 index 000000000..06f6b07a2 Binary files /dev/null and b/demos/sn7/456nm_TI994A.fur differ diff --git a/demos/sms/FlowOfSN7.fur b/demos/sn7/FlowOfSN7.fur similarity index 100% rename from demos/sms/FlowOfSN7.fur rename to demos/sn7/FlowOfSN7.fur diff --git a/demos/misc/Night_Market_TI994A.fur b/demos/sn7/Night_Market_TI994A.fur similarity index 100% rename from demos/misc/Night_Market_TI994A.fur rename to demos/sn7/Night_Market_TI994A.fur diff --git a/demos/sms/doorintosummer.fur b/demos/sn7/doorintosummer.fur similarity index 100% rename from demos/sms/doorintosummer.fur rename to demos/sn7/doorintosummer.fur diff --git a/demos/sms/gg_silver_surfer_st1.fur b/demos/sn7/gg_silver_surfer_st1.fur similarity index 100% rename from demos/sms/gg_silver_surfer_st1.fur rename to demos/sn7/gg_silver_surfer_st1.fur diff --git a/demos/sms/gg_soniccd_collision_chaos.fur b/demos/sn7/gg_soniccd_collision_chaos.fur similarity index 100% rename from demos/sms/gg_soniccd_collision_chaos.fur rename to demos/sn7/gg_soniccd_collision_chaos.fur diff --git a/demos/sms/thunderblade-type-ii.fur b/demos/sn7/thunderblade-type-ii.fur similarity index 100% rename from demos/sms/thunderblade-type-ii.fur rename to demos/sn7/thunderblade-type-ii.fur diff --git a/demos/snes/Unreal_Something_SNES.fur b/demos/snes/Unreal_Something_SNES.fur new file mode 100644 index 000000000..d6ed8b750 Binary files /dev/null and b/demos/snes/Unreal_Something_SNES.fur differ diff --git a/demos/snes/changeyourheart.fur b/demos/snes/changeyourheart.fur index 7b671c3a2..60f35aa7d 100644 Binary files a/demos/snes/changeyourheart.fur and b/demos/snes/changeyourheart.fur differ diff --git a/demos/specs2/Silver Surfer - Stage Music 1.fur b/demos/specs2/Silver Surfer - Stage Music 1.fur deleted file mode 100644 index bbe6a3c77..000000000 Binary files a/demos/specs2/Silver Surfer - Stage Music 1.fur and /dev/null differ diff --git a/demos/specs2/vaportrail_staffroll.fur b/demos/specs2/vaportrail_staffroll.fur deleted file mode 100644 index 659bb0a66..000000000 Binary files a/demos/specs2/vaportrail_staffroll.fur and /dev/null differ diff --git a/demos/virtualboy/4_dimensionnal_goddess_of_existance.fur b/demos/virtualboy/4_dimensionnal_goddess_of_existance.fur new file mode 100644 index 000000000..fe5279389 Binary files /dev/null and b/demos/virtualboy/4_dimensionnal_goddess_of_existance.fur differ diff --git a/demos/virtualboy/Cherry_Vertex.fur b/demos/virtualboy/Cherry_Vertex.fur new file mode 100644 index 000000000..148cbf4cb Binary files /dev/null and b/demos/virtualboy/Cherry_Vertex.fur differ diff --git a/demos/virtualboy/Dimensional.fur b/demos/virtualboy/Dimensional.fur new file mode 100644 index 000000000..449dbeb50 Binary files /dev/null and b/demos/virtualboy/Dimensional.fur differ diff --git a/demos/virtualboy/Double_Puzzle_Trouble.fur b/demos/virtualboy/Double_Puzzle_Trouble.fur new file mode 100644 index 000000000..002b00685 Binary files /dev/null and b/demos/virtualboy/Double_Puzzle_Trouble.fur differ diff --git a/demos/virtualboy/DoublingDown.fur b/demos/virtualboy/DoublingDown.fur new file mode 100644 index 000000000..4d27b2750 Binary files /dev/null and b/demos/virtualboy/DoublingDown.fur differ diff --git a/demos/virtualboy/Evil_Incarnate.fur b/demos/virtualboy/Evil_Incarnate.fur new file mode 100644 index 000000000..c07c21cfb Binary files /dev/null and b/demos/virtualboy/Evil_Incarnate.fur differ diff --git a/demos/virtualboy/God_Rest_Ye_Deadly_Gentlemen.fur b/demos/virtualboy/God_Rest_Ye_Deadly_Gentlemen.fur new file mode 100644 index 000000000..e629b49b5 Binary files /dev/null and b/demos/virtualboy/God_Rest_Ye_Deadly_Gentlemen.fur differ diff --git a/demos/virtualboy/Iridion_3D_Stage_3.fur b/demos/virtualboy/Iridion_3D_Stage_3.fur new file mode 100644 index 000000000..c8552e951 Binary files /dev/null and b/demos/virtualboy/Iridion_3D_Stage_3.fur differ diff --git a/demos/virtualboy/LN2StreetLoader-VB.fur b/demos/virtualboy/LN2StreetLoader-VB.fur new file mode 100644 index 000000000..f94601279 Binary files /dev/null and b/demos/virtualboy/LN2StreetLoader-VB.fur differ diff --git a/demos/virtualboy/La_Folia_ground_bass.fur b/demos/virtualboy/La_Folia_ground_bass.fur new file mode 100644 index 000000000..ccbc1d8ab Binary files /dev/null and b/demos/virtualboy/La_Folia_ground_bass.fur differ diff --git a/demos/virtualboy/No_Such_Thing.fur b/demos/virtualboy/No_Such_Thing.fur new file mode 100644 index 000000000..a26fdbfba Binary files /dev/null and b/demos/virtualboy/No_Such_Thing.fur differ diff --git a/demos/virtualboy/Professional_Beat.fur b/demos/virtualboy/Professional_Beat.fur new file mode 100644 index 000000000..f101ca32f Binary files /dev/null and b/demos/virtualboy/Professional_Beat.fur differ diff --git a/demos/virtualboy/Red_And_Black_Orchid.fur b/demos/virtualboy/Red_And_Black_Orchid.fur new file mode 100644 index 000000000..2ebc8c17c Binary files /dev/null and b/demos/virtualboy/Red_And_Black_Orchid.fur differ diff --git a/demos/virtualboy/Red_Revolver.fur b/demos/virtualboy/Red_Revolver.fur new file mode 100644 index 000000000..234ff69e9 Binary files /dev/null and b/demos/virtualboy/Red_Revolver.fur differ diff --git a/demos/virtualboy/Redworld.fur b/demos/virtualboy/Redworld.fur new file mode 100644 index 000000000..d52ee8d37 Binary files /dev/null and b/demos/virtualboy/Redworld.fur differ diff --git a/demos/virtualboy/Scarlet_Horizons.fur b/demos/virtualboy/Scarlet_Horizons.fur new file mode 100644 index 000000000..40ea47989 Binary files /dev/null and b/demos/virtualboy/Scarlet_Horizons.fur differ diff --git a/demos/virtualboy/StuckTerminologyRealVB.fur b/demos/virtualboy/StuckTerminologyRealVB.fur new file mode 100644 index 000000000..bc9bc99c0 Binary files /dev/null and b/demos/virtualboy/StuckTerminologyRealVB.fur differ diff --git a/demos/virtualboy/Virtual_Promises.fur b/demos/virtualboy/Virtual_Promises.fur new file mode 100644 index 000000000..86503a77d Binary files /dev/null and b/demos/virtualboy/Virtual_Promises.fur differ diff --git a/demos/virtualboy/air_fight.fur b/demos/virtualboy/air_fight.fur new file mode 100644 index 000000000..3d3715351 Binary files /dev/null and b/demos/virtualboy/air_fight.fur differ diff --git a/demos/virtualboy/blueseed.fur b/demos/virtualboy/blueseed.fur new file mode 100644 index 000000000..549406b07 Binary files /dev/null and b/demos/virtualboy/blueseed.fur differ diff --git a/demos/virtualboy/devil_crash.fur b/demos/virtualboy/devil_crash.fur new file mode 100644 index 000000000..f5b972708 Binary files /dev/null and b/demos/virtualboy/devil_crash.fur differ diff --git a/demos/virtualboy/eek.fur b/demos/virtualboy/eek.fur new file mode 100644 index 000000000..bef297b6d Binary files /dev/null and b/demos/virtualboy/eek.fur differ diff --git a/demos/virtualboy/ghx_melodic.fur b/demos/virtualboy/ghx_melodic.fur new file mode 100644 index 000000000..3df3fe4cd Binary files /dev/null and b/demos/virtualboy/ghx_melodic.fur differ diff --git a/demos/virtualboy/groove_in_the_virtual_realm.fur b/demos/virtualboy/groove_in_the_virtual_realm.fur new file mode 100644 index 000000000..aa18a06ea Binary files /dev/null and b/demos/virtualboy/groove_in_the_virtual_realm.fur differ diff --git a/demos/virtualboy/honeydippedkiwis.fur b/demos/virtualboy/honeydippedkiwis.fur new file mode 100644 index 000000000..5897ed677 Binary files /dev/null and b/demos/virtualboy/honeydippedkiwis.fur differ diff --git a/demos/virtualboy/lastninja-sewersloader.fur b/demos/virtualboy/lastninja-sewersloader.fur new file mode 100644 index 000000000..c70847258 Binary files /dev/null and b/demos/virtualboy/lastninja-sewersloader.fur differ diff --git a/demos/virtualboy/mission_breafing.fur b/demos/virtualboy/mission_breafing.fur new file mode 100644 index 000000000..faa2ea740 Binary files /dev/null and b/demos/virtualboy/mission_breafing.fur differ diff --git a/demos/virtualboy/needforspeed2semainmenu.fur b/demos/virtualboy/needforspeed2semainmenu.fur new file mode 100644 index 000000000..8b650664a Binary files /dev/null and b/demos/virtualboy/needforspeed2semainmenu.fur differ diff --git a/demos/virtualboy/red_joke.fur b/demos/virtualboy/red_joke.fur new file mode 100644 index 000000000..f2afeb53d Binary files /dev/null and b/demos/virtualboy/red_joke.fur differ diff --git a/demos/virtualboy/redboas.fur b/demos/virtualboy/redboas.fur new file mode 100644 index 000000000..07726d40a Binary files /dev/null and b/demos/virtualboy/redboas.fur differ diff --git a/demos/virtualboy/redshift.fur b/demos/virtualboy/redshift.fur new file mode 100644 index 000000000..fdeb1c981 Binary files /dev/null and b/demos/virtualboy/redshift.fur differ diff --git a/demos/virtualboy/virtual_namachuukei_pennant_race.fur b/demos/virtualboy/virtual_namachuukei_pennant_race.fur new file mode 100644 index 000000000..4abb90f26 Binary files /dev/null and b/demos/virtualboy/virtual_namachuukei_pennant_race.fur differ diff --git a/demos/x68000/Optimistic.fur b/demos/x68000/Optimistic.fur new file mode 100644 index 000000000..5265c0b15 Binary files /dev/null and b/demos/x68000/Optimistic.fur differ diff --git a/demos/misc/TimeMan_SharpX68k.fur b/demos/x68000/TimeMan.fur similarity index 100% rename from demos/misc/TimeMan_SharpX68k.fur rename to demos/x68000/TimeMan.fur diff --git a/demos/misc/Wicked_Express_X68000.fur b/demos/x68000/Wicked_Express.fur similarity index 100% rename from demos/misc/Wicked_Express_X68000.fur rename to demos/x68000/Wicked_Express.fur diff --git a/doc/1-intro/concepts.md b/doc/1-intro/concepts.md index 43361d49b..b1ab44304 100644 --- a/doc/1-intro/concepts.md +++ b/doc/1-intro/concepts.md @@ -23,7 +23,7 @@ the **order list** is a smaller spreadsheet showing the overall song structure. - patterns may be used multiple times in the order list. changing a pattern's data in one order will affect the same pattern used in other orders. - each pattern is made of the same number of rows as seen in the tracker view. - during playback, the **playhead** moves down as described previously. when it reaches the end of the pattern view, it will go to the next order. -- if the last order is reached and the playhear reaches the end of the pattern view, it will go back to the beginning of the song. +- if the last order is reached and the playhead reaches the end of the pattern view, it will go back to the beginning of the song. ## time diff --git a/doc/2-interface/menu-bar.md b/doc/2-interface/menu-bar.md index 6d6b899bc..943ff05c3 100644 --- a/doc/2-interface/menu-bar.md +++ b/doc/2-interface/menu-bar.md @@ -35,6 +35,7 @@ items in _italic_ don't appear in basic mode and are only available in advanced - **export audio...**: opens the file picker, allowing you to export your song to a .wav file. see next section for more details. - **export VGM...**: opens the file picker, allowing you to export your song to a .vgm file. see next section for more details. +- **export text...**: opens the file picker, allowing you to export your song to a .txt file. - **export ZSM...**: opens the file picker, allowing you to export your song to a .zsm file. see next section for more details. - only available when there's a YM2151 and/or VERA. - **export command stream...**: export song data to a command stream file. see next section for more details. @@ -140,6 +141,7 @@ it's not really useful, unless you're a developer and want to use a command stre - **cut**: moves the current selection in the pattern view to clipboard. - **copy**: copies the current selection in the pattern view to clipboard. - **paste**: inserts the clipboard's contents in the cursor position. + - you may be able to paste from OpenMPT as well. - _**paste special...**:_ variants of the paste feature. - **paste mix**: inserts the clipboard's contents in the cursor position, but does not erase the occupied region. - **paste mix (background)**: does the same thing as paste mix, but doesn't alter content which is already there. diff --git a/doc/2-interface/settings.md b/doc/2-interface/settings.md index e80eeaecf..3571e7dd6 100644 --- a/doc/2-interface/settings.md +++ b/doc/2-interface/settings.md @@ -305,6 +305,12 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **Yes** - **Inverted** +### Assets + +- **Display instrument type menu when adding instrument** + - if turned off, the menu can still be opened by right-clicking the add button. +- **Select asset after opening one** + ## Appearance ### Scaling @@ -435,8 +441,6 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **Graphical icons** - **Letter icons** - **Colorize instrument editor using instrument type** -- **Display instrument type menu when adding instrument** - - if turned off, the menu can still be opened by right-clicking the add button. ### Macro Editor diff --git a/doc/4-instrument/README.md b/doc/4-instrument/README.md index b85c957e5..56386082a 100644 --- a/doc/4-instrument/README.md +++ b/doc/4-instrument/README.md @@ -126,14 +126,25 @@ finally, the sequence of values can be directly edited in the text box at the bo - in arpeggio macros, a value starting with a `@` is a fixed value as described above. - in bitmask-style macros, the values are added up in binary and converted to decimal. see [the hexadecimal guide](../1-intro/hex.md) for more info. - +in all cases except bitmask macros, right-clicking on the graph opens up an editing menu: +- **copy**: copies the macro. +- **paste**: pastes the macro in the clipboard. +- **clear**: clears the macro. +- **clear contents**: resets all values to 0. +- **offset**: + - **X**: slides the data "horizontally" within the macro, filling the gap with zeroes. data moved past the start or end is lost. + - **Y**: increases or decreases all values, clipping them if they would move past the allowed range. +- **scale**: + - **X**: stretches the macro. + - **Y**: multiplies all values by the scale factor, clipping them if they would exceed the allowed range. +- **randomize**: replaces all values with random values between **Min** and **Max**. ### ADSR ![ADSR macro editor](macro-ADSR.png) - **Bottom** and **Top** determine the range of outputs generated by the macro. (Bottom can be larger than Top to invert the envelope!) All outputs will be between these two values. -- attack, Decay, Sustain, SusDecay, and Release accept inputs between 0 to 255. these are scaled to the distance between Bottom and Top. +- Attack, Decay, Sustain, SusDecay, and Release accept inputs between 0 to 255. these are scaled to the distance between Bottom and Top. - **Attack** is how much the value moves toward Top with each tick. - **Hold** sets how many ticks to stay at Top before Decay. - **Decay** is how much the value moves to the Sustain level. diff --git a/doc/4-instrument/macro-ADSR.png b/doc/4-instrument/macro-ADSR.png index 8d12c7c63..df3ff43fa 100644 Binary files a/doc/4-instrument/macro-ADSR.png and b/doc/4-instrument/macro-ADSR.png differ diff --git a/doc/4-instrument/macro-LFO.png b/doc/4-instrument/macro-LFO.png index 57863312a..4d9d195d3 100644 Binary files a/doc/4-instrument/macro-LFO.png and b/doc/4-instrument/macro-LFO.png differ diff --git a/doc/4-instrument/macro-button-ADSR.png b/doc/4-instrument/macro-button-ADSR.png index f76ed6585..9a3d4691a 100644 Binary files a/doc/4-instrument/macro-button-ADSR.png and b/doc/4-instrument/macro-button-ADSR.png differ diff --git a/doc/4-instrument/macro-button-LFO.png b/doc/4-instrument/macro-button-LFO.png index 93da266cd..188f89aa4 100644 Binary files a/doc/4-instrument/macro-button-LFO.png and b/doc/4-instrument/macro-button-LFO.png differ diff --git a/doc/4-instrument/wavetable.png b/doc/4-instrument/wavetable.png index 64589ae04..8ab9d7857 100644 Binary files a/doc/4-instrument/wavetable.png and b/doc/4-instrument/wavetable.png differ diff --git a/doc/6-sample/README.md b/doc/6-sample/README.md index a6129b41a..55ba48766 100644 --- a/doc/6-sample/README.md +++ b/doc/6-sample/README.md @@ -154,6 +154,9 @@ in there, you can modify certain data pertaining to your sample, such as the: - **Low-pass**: amount to attenuate everything above cutoff. - **Band-pass**: amount to attenuate everything outside cutoff. - **High-pass**: amount to attenuate everything below cutoff. +- **Crossfade loop points**: applies a "fade" between the loop's starting point and the end. + - **Number of samples**: how many samples in the loop region to take into account for crossfade. + - **Linear <-> Equal power**: the curve used to crossfade. - **Preview sample**: plays sample at base frequency. - **Stop sample preview**: stops preview. - **Create instrument from sample**: creates a new instrument with its sample set to the current sample. diff --git a/doc/6-sample/sample-editor-buttons.png b/doc/6-sample/sample-editor-buttons.png index 7d2351db9..fdcb88a5e 100644 Binary files a/doc/6-sample/sample-editor-buttons.png and b/doc/6-sample/sample-editor-buttons.png differ diff --git a/doc/6-sample/sample-editor.png b/doc/6-sample/sample-editor.png index 155f2ce10..30dce72da 100644 Binary files a/doc/6-sample/sample-editor.png and b/doc/6-sample/sample-editor.png differ diff --git a/doc/7-systems/opl.md b/doc/7-systems/opl.md index 2937fa893..d0699f07d 100644 --- a/doc/7-systems/opl.md +++ b/doc/7-systems/opl.md @@ -82,3 +82,5 @@ afterwards everyone moved to Windows and software mixed PCM streaming... ## info this chip uses the [FM (OPL)](../4-instrument/fm-opl.md) instrument editor. + +when two channels are joined for 4-op mode, the channel bar will show `4OP` on a bracket tying them together. \ No newline at end of file diff --git a/doc/7-systems/pokey.md b/doc/7-systems/pokey.md index eff37cd8c..ca7d21b1d 100644 --- a/doc/7-systems/pokey.md +++ b/doc/7-systems/pokey.md @@ -39,3 +39,7 @@ a sound and input chip developed by Atari for their 8-bit computers (Atari 400, ## info this chip uses the [POKEY](../4-instrument/pokey.md) instrument editor. + +when two channels are joined for filtered output, the channel bar will show `filter` on a bracket tying them together. + +when two channels are joined for wide period range, the channel bar will show `16-bit` on a bracket tying them together. diff --git a/doc/7-systems/snes.md b/doc/7-systems/snes.md index d10b20c39..76da944ff 100644 --- a/doc/7-systems/snes.md +++ b/doc/7-systems/snes.md @@ -65,6 +65,8 @@ Furnace also allows the SNES to use wavetables (and the wavetable synthesizer) i this chip uses the [SNES](../4-instrument/snes.md) instrument editor. +when two channels are joined for pitch modulation, the channel bar will show `mod` on a bracket tying them together. + ## channel status the following icons are displayed when channel status is enabled in the pattern view: diff --git a/doc/8-advanced/chanosc.md b/doc/8-advanced/chanosc.md index 8521c099f..720b550fe 100644 --- a/doc/8-advanced/chanosc.md +++ b/doc/8-advanced/chanosc.md @@ -1,6 +1,6 @@ # oscilloscope (per-channel) -the "Oscilloscope (per-channel)" windows displays several oscilloscope views (one per channel). +the "Oscilloscope (per-channel)" window displays several oscilloscope views (one per channel). ![oscilloscope per-channel configuration view](chanosc.png) diff --git a/doc/9-guides/README.md b/doc/9-guides/README.md index 66748e615..4d438bfd6 100644 --- a/doc/9-guides/README.md +++ b/doc/9-guides/README.md @@ -1,7 +1,8 @@ # guides -this is collection of user-contributed Furnace guides which may be useful during composition. +this is a collection of user-contributed Furnace guides which may be useful during composition. +- [tuning samples](tuning-samples.md) - [using samples with limited playback rates](limited-samples.md) - [choosing emulation cores](emulation-cores.md) - [using OPLL patch macro](opllswitching.md) diff --git a/doc/9-guides/emulation-cores.md b/doc/9-guides/emulation-cores.md index 323e180c4..2624f6ddc 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**: +- **YM2612 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**: 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**: 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! + diff --git a/doc/9-guides/tuning-samples.md b/doc/9-guides/tuning-samples.md new file mode 100644 index 000000000..1bda41c4b --- /dev/null +++ b/doc/9-guides/tuning-samples.md @@ -0,0 +1,16 @@ +# tuning samples + +loading a new sample into Furnace is easy... but getting it transposed and tuned to match the song can be tricky at first. + +it's important to remember that the "Hz" and "Note" as shown in the sample editor are unrelated to the note heard in the sample itself. a sample shown as having a "Note" of C-4 will use a sample rate of 4181, even though it may contain a note played at a different pitch than C. + +for this example, we'll use a sample of a note played at E and recorded at 22050Hz. + +- if needed, use the "Create instrument from sample" button to make an instrument to use in the track. +- calculate the semitone difference in Hz between the note your recorded sample is playing and C. in this example, the nearest C is 4 semitones down from E. +- set "Note" to 4 semitones lower than it shows. in this case, it starts at `F-6`, so set it to `C#6`. + - or, use a pitch calculator like https://www.omnicalculator.com/other/semitone. input Frequency 1 as 22050Hz, input -4 semitones, and receive a Frequency 2 of 17501.10Hz. enter that value into "Hz". +- now, using the "Preview sample" button should play the note at C. entering an E in the pattern will now play it at or near E. +- if the sample still sounds out of tune, adjust "Hz" or "Fine" to bring it in line. + +if notes seem "capped" – for example, playing anything over D-6 sounds like a D-6 – those notes exceed the maximum sample playback rate for the chip. the only solution is to use "Resample" to change the sample to a lower rate. diff --git a/doc/README.md b/doc/README.md index 4241b92c4..9870cb925 100644 --- a/doc/README.md +++ b/doc/README.md @@ -23,7 +23,6 @@ the index follows. - freq-mod - host12prog - Lunathir -- nicco1690 - tildearrow ## information 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..77dde824a --- /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 and per-chan osc. + +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..5245f1d50 --- /dev/null +++ b/extern/YM3812-LLE/fmopl2.c @@ -0,0 +1,1503 @@ +/* + * 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; + + 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; + 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..c94c1770d --- /dev/null +++ b/extern/YM3812-LLE/fmopl2.h @@ -0,0 +1,276 @@ +/* + * 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; + + int op_value_debug; + +} fmopl2_t; + +// modification +void FMOPL2_Clock(fmopl2_t *chip); 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..14a7126d4 --- /dev/null +++ b/extern/YMF262-LLE/Readme.md @@ -0,0 +1,14 @@ +# 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 and per-chan osc. +it also brings in a small optimization. + +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..f534ba724 --- /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->input.mclk; /* Temp: disable prescaler for performance reasons */ + + 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..6fcf95f57 --- /dev/null +++ b/extern/YMF262-LLE/fmopl3.h @@ -0,0 +1,335 @@ +/* + * 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; + +// modification +void FMOPL3_Clock(fmopl3_t *chip); 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/instruments/GB/closed hi-hat.fui b/instruments/GB/closed hi-hat.fui new file mode 100644 index 000000000..7db73f6b7 Binary files /dev/null and b/instruments/GB/closed hi-hat.fui differ diff --git a/instruments/GB/open hihat.fui b/instruments/GB/open hihat.fui new file mode 100644 index 000000000..afc51e768 Binary files /dev/null and b/instruments/GB/open hihat.fui differ diff --git a/instruments/GB/square fade-in.fui b/instruments/GB/square fade-in.fui new file mode 100644 index 000000000..d7e2f547e Binary files /dev/null and b/instruments/GB/square fade-in.fui differ diff --git a/instruments/OPN/bass/dxbass1.dmp b/instruments/OPN/bass/dxbass1.dmp deleted file mode 100644 index d77bb762f..000000000 Binary files a/instruments/OPN/bass/dxbass1.dmp and /dev/null differ diff --git a/instruments/OPN/bass/dxbass2.dmp b/instruments/OPN/bass/dxbass2.dmp deleted file mode 100644 index 800646cf6..000000000 Binary files a/instruments/OPN/bass/dxbass2.dmp and /dev/null differ diff --git a/instruments/OPN/bass/dxbass3.dmp b/instruments/OPN/bass/dxbass3.dmp deleted file mode 100644 index 7bf0a5534..000000000 Binary files a/instruments/OPN/bass/dxbass3.dmp and /dev/null differ diff --git a/instruments/OPN/bass/dxbass4.dmp b/instruments/OPN/bass/dxbass4.dmp deleted file mode 100644 index a7db224a7..000000000 Binary files a/instruments/OPN/bass/dxbass4.dmp and /dev/null differ diff --git a/instruments/OPN/bass/dxbass5.dmp b/instruments/OPN/bass/dxbass5.dmp deleted file mode 100644 index abe46a08f..000000000 Binary files a/instruments/OPN/bass/dxbass5.dmp and /dev/null differ diff --git a/instruments/OPN/bass/dxbass5slap.dmp b/instruments/OPN/bass/dxbass5slap.dmp deleted file mode 100644 index 0ee63eb9a..000000000 Binary files a/instruments/OPN/bass/dxbass5slap.dmp and /dev/null differ diff --git a/instruments/OPN/bass/nicco1690 Slap Bass.dmp b/instruments/OPN/bass/nicco1690 Slap Bass.dmp deleted file mode 100644 index 32a80d194..000000000 Binary files a/instruments/OPN/bass/nicco1690 Slap Bass.dmp and /dev/null differ diff --git a/instruments/OPN/keys/Acoustic E-Piano.fui b/instruments/OPN/keys/Acoustic E-Piano.fui new file mode 100644 index 000000000..38ccc1c95 Binary files /dev/null and b/instruments/OPN/keys/Acoustic E-Piano.fui differ diff --git a/instruments/OPN/keys/nicco1690 Church Organ.dmp b/instruments/OPN/keys/nicco1690 Church Organ.dmp deleted file mode 100644 index 543bf3522..000000000 Binary files a/instruments/OPN/keys/nicco1690 Church Organ.dmp and /dev/null differ diff --git a/instruments/OPZ/Synth Bass.fui b/instruments/OPZ/Synth Bass.fui new file mode 100644 index 000000000..e78046ddf Binary files /dev/null and b/instruments/OPZ/Synth Bass.fui differ diff --git a/instruments/OPZ/accordion.fui b/instruments/OPZ/accordion.fui new file mode 100644 index 000000000..d512a9acb Binary files /dev/null and b/instruments/OPZ/accordion.fui differ diff --git a/instruments/OPZ/accordion2.fui b/instruments/OPZ/accordion2.fui new file mode 100644 index 000000000..b8f862d13 Binary files /dev/null and b/instruments/OPZ/accordion2.fui differ diff --git a/instruments/OPZ/bagpipe.fui b/instruments/OPZ/bagpipe.fui new file mode 100644 index 000000000..d59e346ef Binary files /dev/null and b/instruments/OPZ/bagpipe.fui differ diff --git a/instruments/OPZ/distortedhalfsinepad.fui b/instruments/OPZ/distortedhalfsinepad.fui new file mode 100644 index 000000000..b95079d4a Binary files /dev/null and b/instruments/OPZ/distortedhalfsinepad.fui differ diff --git a/instruments/OPZ/funkysynthbass.fui b/instruments/OPZ/funkysynthbass.fui new file mode 100644 index 000000000..6bc0975aa Binary files /dev/null and b/instruments/OPZ/funkysynthbass.fui differ diff --git a/instruments/OPZ/halfsine_pad.fui b/instruments/OPZ/halfsine_pad.fui new file mode 100644 index 000000000..7a5d92598 Binary files /dev/null and b/instruments/OPZ/halfsine_pad.fui differ diff --git a/instruments/OPZ/horrorpad.fui b/instruments/OPZ/horrorpad.fui new file mode 100644 index 000000000..2f053d068 Binary files /dev/null and b/instruments/OPZ/horrorpad.fui differ diff --git a/instruments/OPZ/overdriven_guitar.fui b/instruments/OPZ/overdriven_guitar.fui new file mode 100644 index 000000000..2b503ee7b Binary files /dev/null and b/instruments/OPZ/overdriven_guitar.fui differ diff --git a/instruments/OPZ/pickbass.fui b/instruments/OPZ/pickbass.fui new file mode 100644 index 000000000..322c41b25 Binary files /dev/null and b/instruments/OPZ/pickbass.fui differ diff --git a/instruments/OPZ/reedorgan.fui b/instruments/OPZ/reedorgan.fui new file mode 100644 index 000000000..97c471520 Binary files /dev/null and b/instruments/OPZ/reedorgan.fui differ diff --git a/instruments/OPZ/rimshot.fui b/instruments/OPZ/rimshot.fui new file mode 100644 index 000000000..6a2b23963 Binary files /dev/null and b/instruments/OPZ/rimshot.fui differ diff --git a/instruments/OPZ/sitar.fui b/instruments/OPZ/sitar.fui new file mode 100644 index 000000000..8bab269a8 Binary files /dev/null and b/instruments/OPZ/sitar.fui differ diff --git a/instruments/OPZ/synthdulcimer.fui b/instruments/OPZ/synthdulcimer.fui new file mode 100644 index 000000000..05c8e2ef3 Binary files /dev/null and b/instruments/OPZ/synthdulcimer.fui differ diff --git a/instruments/OPZ/timpani_oct2.fui b/instruments/OPZ/timpani_oct2.fui new file mode 100644 index 000000000..f837b6e20 Binary files /dev/null and b/instruments/OPZ/timpani_oct2.fui differ diff --git a/instruments/README.md b/instruments/README.md index e4fa16f9d..b4efd5582 100644 --- a/instruments/README.md +++ b/instruments/README.md @@ -18,7 +18,6 @@ these are organized in the respective chip folders. - jvsTSX - Laggy - LovelyA72 -- nicco1690 - PacorexTheTrex - paynspch - pedipanol diff --git a/papers/format.md b/papers/format.md index 54c2c0275..242e8bcc4 100644 --- a/papers/format.md +++ b/papers/format.md @@ -363,7 +363,8 @@ size | description 1 | pre note (C64) does not compensate for portamento or legato (>=168) 1 | disable new NES DPCM features (>=183) 1 | reset arp effect phase on new note (>=184) - 3 | reserved + 1 | linear volume scaling rounds up (>=188) + 2 | reserved --- | **speed pattern of first song** (>=139) 1 | length of speed pattern (fail if this is lower than 0 or higher than 16) 16 | speed pattern (this overrides speed 1 and speed 2 settings) diff --git a/res/Info.plist b/res/Info.plist index 3eeb8ae02..7895024da 100644 --- a/res/Info.plist +++ b/res/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion English CFBundleExecutable - furnace + Furnace CFBundleGetInfoString CFBundleIconFile diff --git a/res/docpdf/make_paper.py b/res/docpdf/make_paper.py index 10de2e1f5..fed4e9fee 100644 --- a/res/docpdf/make_paper.py +++ b/res/docpdf/make_paper.py @@ -341,7 +341,6 @@ if __name__ == "__main__":
  • freq-mod
  • host12prog
  • Lunathir
  • -
  • nicco1690
  • tildearrow
  • special thanks to ZoomTen for providing tools which assisted in the production of this document!

    diff --git a/res/icons.sfd b/res/icons.sfd index a04f40897..769b73fbc 100644 --- a/res/icons.sfd +++ b/res/icons.sfd @@ -21,7 +21,7 @@ OS2Version: 0 OS2_WeightWidthSlopeOnly: 0 OS2_UseTypoMetrics: 0 CreationTime: 1691897631 -ModificationTime: 1698523374 +ModificationTime: 1701817435 PfmFamily: 81 TTFWeight: 400 TTFWidth: 5 @@ -47,13 +47,13 @@ LangName: 1033 Encoding: UnicodeBmp UnicodeInterp: none NameList: AGL For New Fonts -DisplaySize: -24 +DisplaySize: -96 AntiAlias: 1 FitToEm: 0 -WinInfo: 57552 16 10 +WinInfo: 57672 8 5 BeginPrivate: 0 EndPrivate -BeginChars: 65536 95 +BeginChars: 65536 96 StartChar: space Encoding: 32 32 0 @@ -6732,5 +6732,412 @@ SplineSet 1595 691.666666667 1611.33333333 700 1632 700 c 0 EndSplineSet EndChar + +StartChar: uniE159 +Encoding: 57689 57689 95 +Width: 1792 +Flags: HWO +LayerCount: 2 +UndoRedoHistory +Layer: 0 +Undoes +UndoOperation +Index: 0 +Type: 1 +WasModified: 0 +WasOrder2: 0 +Layer: 1 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +1632 70 m 1 + 1632 -50 l 1025 +160 1210 m 1 + 160 1330 l 1025 +EndSplineSet +EndUndoOperation +UndoOperation +Index: 1 +Type: 1 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +160 1270 m 25 + 160 1270 560 1270 896 640 c 0 + 1232 10 1632 10 1632 10 c 1025 +1632 70 m 5 + 1632 -50 l 1029 +160 1210 m 5 + 160 1330 l 1029 +EndSplineSet +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Back +SplineSet +160 1270 m 25 + 160 1270 560 1270 896 640 c 0 + 1232 10 1632 10 1632 10 c 1025 +1632 70 m 1 + 1632 -50 l 1025 +160 1210 m 1 + 160 1330 l 1025 +EndSplineSet +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 1 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +1632 1210.01171875 m 1 + 1632 1329.98828125 l 1 + 1619.75878906 1329.98828125 1193.45019531 1325.24316406 843.048828125 668.2421875 c 0 + 755.229492188 503.581054688 663.75 384.705078125 577.82421875 298.780273438 c 0 + 473.91796875 194.873046875 377.610351562 138.502929688 303.73046875 107.581054688 c 0 + 262.674804688 90.396484375 228.48828125 81.078125 203.702148438 76.0087890625 c 0 + 176.166992188 70.3779296875 160.23046875 70 159.536132812 69.986328125 c 1 + 159.698242188 69.9873046875 160 69.98828125 160 69.98828125 c 1 + 160 -49.98828125 l 1 + 172.241210938 -49.98828125 598.549804688 -45.2431640625 948.951171875 611.7578125 c 0 + 1036.77050781 776.418945312 1128.25 895.294921875 1214.17578125 981.219726562 c 0 + 1318.08203125 1085.12695312 1414.38964844 1141.49707031 1488.26953125 1172.41894531 c 0 + 1529.32519531 1189.60351562 1563.51171875 1198.921875 1588.29785156 1203.99121094 c 0 + 1615.83300781 1209.62207031 1631.76953125 1210 1632.46386719 1210.01367188 c 1 + 1632.30175781 1210.01269531 1632 1210.01171875 1632 1210.01171875 c 1 +159.999999952 1210.01176537 m 1 + 159.999999952 1329.98823463 l 1 + 170.809951102 1329.98823463 504.536374276 1326.28810718 823.734604363 872.948478072 c 1 + 800.889124326 837.280193024 778.251310121 799.419416229 755.928040313 759.232259114 c 1 + 695.916689064 850.735795855 635.614260663 923.430234862 577.824293264 981.220202232 c 0 + 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 0 + 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 0 + 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 1 + 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 1 +1632 1090.03515625 m 5 + 1632 1449.96484375 l 5 + 1558.47725996 1449.96484375 1167.58409474 1409.79522486 823.734604363 872.948478072 c 5 + 865.988935134 812.936984165 907.988680731 745.046220598 948.951069976 668.241740764 c 4 + 977.706682766 614.324966782 1006.85482384 565.317167145 1036.07195969 520.767740886 c 5 + 1042.35821703 532.084488601 1048.61953126 543.58571637 1054.85351562 555.274414062 c 4 + 1093.06152344 626.915039062 1265.13574219 948.919921875 1534.69042969 1061.74121094 c 4 + 1536.87304688 1062.65429688 1591.23828125 1085.29492188 1632 1090.03515625 c 5 +1036.07195969 520.767740886 m 1 + 1096.08331094 429.264204145 1156.38573934 356.569765138 1214.17570674 298.779797768 c 0 + 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 0 + 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 0 + 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 1 + 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 1 + 1632.00000005 -49.9882346324 l 1 + 1621.1900489 -49.9882346324 1287.46362572 -46.2881071791 968.265395637 407.051521928 c 1 + 991.110875674 442.719806976 1013.74868988 480.580583771 1036.07195969 520.767740886 c 1 +968.265395637 407.051521928 m 5 + 926.011064866 467.063015835 884.011319269 534.953779402 843.048930024 611.758259236 c 4 + 814.293317234 665.675033218 785.145176159 714.682832855 755.928040313 759.232259114 c 5 + 749.641782968 747.9155114 743.380468741 736.41428363 737.146484375 724.725585938 c 4 + 698.938476562 653.084960938 526.864257812 331.080078125 257.309570312 218.258789062 c 4 + 255.126953125 217.345703125 200.76171875 194.705078125 160 189.96484375 c 5 + 160 -169.96484375 l 5 + 233.522740042 -169.96484375 624.415905262 -129.795224857 968.265395637 407.051521928 c 5 +EndSplineSet +EndUndoOperation +UndoOperation +Index: 1 +Type: 1 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +1632 1090.03515625 m 5 + 1632 1449.96484375 l 5 + 1552.21679688 1449.96484375 1098.71386719 1402.66308594 737.146484375 724.725585938 c 4 + 698.938476562 653.084960938 526.864257812 331.080078125 257.309570312 218.258789062 c 4 + 255.126953125 217.345703125 200.76171875 194.705078125 160 189.96484375 c 5 + 160 -169.96484375 l 5 + 239.783203125 -169.96484375 693.286132812 -122.663085938 1054.85351562 555.274414062 c 4 + 1093.06152344 626.915039062 1265.13574219 948.919921875 1534.69042969 1061.74121094 c 4 + 1536.87304688 1062.65429688 1591.23828125 1085.29492188 1632 1090.03515625 c 5 +1632 1210.01171875 m 1 + 1632 1329.98828125 l 1 + 1619.75878906 1329.98828125 1193.45019531 1325.24316406 843.048828125 668.2421875 c 0 + 755.229492188 503.581054688 663.75 384.705078125 577.82421875 298.780273438 c 0 + 473.91796875 194.873046875 377.610351562 138.502929688 303.73046875 107.581054688 c 0 + 262.674804688 90.396484375 228.48828125 81.078125 203.702148438 76.0087890625 c 0 + 176.166992188 70.3779296875 160.23046875 70 159.536132812 69.986328125 c 1 + 159.698242188 69.9873046875 160 69.98828125 160 69.98828125 c 1 + 160 -49.98828125 l 1 + 172.241210938 -49.98828125 598.549804688 -45.2431640625 948.951171875 611.7578125 c 0 + 1036.77050781 776.418945312 1128.25 895.294921875 1214.17578125 981.219726562 c 0 + 1318.08203125 1085.12695312 1414.38964844 1141.49707031 1488.26953125 1172.41894531 c 0 + 1529.32519531 1189.60351562 1563.51171875 1198.921875 1588.29785156 1203.99121094 c 0 + 1615.83300781 1209.62207031 1631.76953125 1210 1632.46386719 1210.01367188 c 1 + 1632.30175781 1210.01269531 1632 1210.01171875 1632 1210.01171875 c 1 +159.999999952 1210.01176537 m 5 + 159.999999952 1329.98823463 l 5 + 172.240934135 1329.98823462 598.550052583 1325.24364837 948.951069976 668.241740764 c 4 + 1036.77007573 503.581104976 1128.25010681 384.70539769 1214.17570674 298.779797768 c 4 + 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 4 + 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 4 + 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 5 + 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 5 + 1632.00000005 -49.9882346324 l 5 + 1619.75906587 -49.9882346228 1193.44994742 -45.2436483754 843.048930024 611.758259236 c 4 + 755.229924271 776.418895024 663.749893187 895.29460231 577.824293264 981.220202232 c 4 + 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 4 + 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 4 + 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 5 + 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 5 +EndSplineSet +EndUndoOperation +UndoOperation +Index: 2 +Type: 1 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +160 1270 m 29 + 160 1270 560 1270 896 640 c 4 + 1232 10 1632 10 1632 10 c 1029 +1632 1210.01171875 m 1 + 1632 1329.98828125 l 1 + 1619.75878906 1329.98828125 1193.45019531 1325.24316406 843.048828125 668.2421875 c 0 + 755.229492188 503.581054688 663.75 384.705078125 577.82421875 298.780273438 c 0 + 473.91796875 194.873046875 377.610351562 138.502929688 303.73046875 107.581054688 c 0 + 262.674804688 90.396484375 228.48828125 81.078125 203.702148438 76.0087890625 c 0 + 176.166992188 70.3779296875 160.23046875 70 159.536132812 69.986328125 c 1 + 159.698242188 69.9873046875 160 69.98828125 160 69.98828125 c 1 + 160 -49.98828125 l 1 + 172.241210938 -49.98828125 598.549804688 -45.2431640625 948.951171875 611.7578125 c 0 + 1036.77050781 776.418945312 1128.25 895.294921875 1214.17578125 981.219726562 c 0 + 1318.08203125 1085.12695312 1414.38964844 1141.49707031 1488.26953125 1172.41894531 c 0 + 1529.32519531 1189.60351562 1563.51171875 1198.921875 1588.29785156 1203.99121094 c 0 + 1615.83300781 1209.62207031 1631.76953125 1210 1632.46386719 1210.01367188 c 1 + 1632.30175781 1210.01269531 1632 1210.01171875 1632 1210.01171875 c 1 +1632 1090.03515625 m 1 + 1632 1449.96484375 l 1 + 1552.21679688 1449.96484375 1098.71386719 1402.66308594 737.146484375 724.725585938 c 0 + 698.938476562 653.084960938 526.864257812 331.080078125 257.309570312 218.258789062 c 0 + 255.126953125 217.345703125 200.76171875 194.705078125 160 189.96484375 c 1 + 160 -169.96484375 l 1 + 239.783203125 -169.96484375 693.286132812 -122.663085938 1054.85351562 555.274414062 c 0 + 1093.06152344 626.915039062 1265.13574219 948.919921875 1534.69042969 1061.74121094 c 0 + 1536.87304688 1062.65429688 1591.23828125 1085.29492188 1632 1090.03515625 c 1 +EndSplineSet +EndUndoOperation +UndoOperation +Index: 3 +Type: 3 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +UndoOperation +Index: 4 +Type: 1 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +159.999999952 1210.01176537 m 5 + 159.999999952 1329.98823463 l 5 + 172.240934135 1329.98823462 598.550052583 1325.24364837 948.951069976 668.241740764 c 4 + 1036.77007573 503.581104976 1128.25010681 384.70539769 1214.17570674 298.779797768 c 4 + 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 4 + 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 4 + 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 5 + 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 5 + 1632.00000005 -49.9882346324 l 5 + 1619.75906587 -49.9882346228 1193.44994742 -45.2436483754 843.048930024 611.758259236 c 4 + 755.229924271 776.418895024 663.749893187 895.29460231 577.824293264 981.220202232 c 4 + 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 4 + 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 4 + 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 5 + 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 5 +159.999999857 1090.0352961 m 5 + 159.999999857 1449.9647039 l 5 + 239.782893731 1449.96470383 693.286263441 1402.66324695 1054.85320993 724.725222292 c 4 + 1093.06152816 653.084625607 1265.13598183 331.080480771 1534.69080463 218.25857115 c 4 + 1536.87267214 217.345352617 1591.23876559 194.705394151 1632.00000014 189.964703897 c 5 + 1632.00000014 -169.964703897 l 5 + 1552.21710627 -169.964703835 1098.71373656 -122.663246954 737.146790073 555.274777708 c 4 + 698.938471842 626.915374393 526.864018169 948.919519229 257.309195374 1061.74142885 c 4 + 255.127327856 1062.65464738 200.761234414 1085.29460585 159.999999857 1090.0352961 c 5 +EndSplineSet +EndUndoOperation +UndoOperation +Index: 5 +Type: 3 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +UndoOperation +Index: 6 +Type: 1 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +160 1270 m 29 + 160 1270 560 1270 896 640 c 4 + 1232 10 1632 10 1632 10 c 1029 +159.999999952 1210.01176537 m 1 + 159.999999952 1329.98823463 l 1 + 172.240934135 1329.98823462 598.550052583 1325.24364837 948.951069976 668.241740764 c 0 + 1036.77007573 503.581104976 1128.25010681 384.70539769 1214.17570674 298.779797768 c 0 + 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 0 + 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 0 + 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 1 + 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 1 + 1632.00000005 -49.9882346324 l 1 + 1619.75906587 -49.9882346228 1193.44994742 -45.2436483754 843.048930024 611.758259236 c 0 + 755.229924271 776.418895024 663.749893187 895.29460231 577.824293264 981.220202232 c 0 + 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 0 + 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 0 + 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 1 + 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 1 +EndSplineSet +EndUndoOperation +UndoOperation +Index: 7 +Type: 3 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +UndoOperation +Index: 8 +Type: 1 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +160 1270 m 29 + 160 1270 560 1270 896 640 c 4 + 1232 10 1632 10 1632 10 c 1029 +EndSplineSet +EndUndoOperation +UndoOperation +Index: 9 +Type: 3 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +UndoOperation +Index: 10 +Type: 2 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +160 1270 m 29 + 160 1270 560 1270 896 640 c 4 + 1232 10 1632 10 1632 10 c 1029 +EndSplineSet +EndUndoOperation +UndoOperation +Index: 11 +Type: 1 +WasModified: 1 +WasOrder2: 0 +Layer: 1 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +1632 1210.01171875 m 1 + 1632 1329.98828125 l 1 + 1619.75878906 1329.98828125 1193.45019531 1325.24316406 843.048828125 668.2421875 c 0 + 755.229492188 503.581054688 663.75 384.705078125 577.82421875 298.780273438 c 0 + 473.91796875 194.873046875 377.610351562 138.502929688 303.73046875 107.581054688 c 0 + 262.674804688 90.396484375 228.48828125 81.078125 203.702148438 76.0087890625 c 0 + 176.166992188 70.3779296875 160.23046875 70 159.536132812 69.986328125 c 1 + 159.698242188 69.9873046875 160 69.98828125 160 69.98828125 c 1 + 160 -49.98828125 l 1 + 172.241210938 -49.98828125 598.549804688 -45.2431640625 948.951171875 611.7578125 c 0 + 1036.77050781 776.418945312 1128.25 895.294921875 1214.17578125 981.219726562 c 0 + 1318.08203125 1085.12695312 1414.38964844 1141.49707031 1488.26953125 1172.41894531 c 0 + 1529.32519531 1189.60351562 1563.51171875 1198.921875 1588.29785156 1203.99121094 c 0 + 1615.83300781 1209.62207031 1631.76953125 1210 1632.46386719 1210.01367188 c 1 + 1632.30175781 1210.01269531 1632 1210.01171875 1632 1210.01171875 c 1 +159.999999952 1210.01176537 m 1 + 159.999999952 1329.98823463 l 1 + 170.809951102 1329.98823463 504.536374276 1326.28810718 823.734604363 872.948478072 c 1 + 800.889124326 837.280193024 778.251310121 799.419416229 755.928040313 759.232259114 c 1 + 695.916689064 850.735795855 635.614260663 923.430234862 577.824293264 981.220202232 c 0 + 473.917602143 1085.12689335 377.61065751 1141.49706263 303.730893464 1172.41936621 c 0 + 262.674555191 1189.60345677 228.488523437 1198.92230209 203.702403502 1203.99126863 c 0 + 176.167181121 1209.62244935 160.230079948 1210.00005891 159.536222574 1210.01396385 c 1 + 159.698263781 1210.01275358 159.999999952 1210.01176537 159.999999952 1210.01176537 c 1 +1036.07195969 520.767740886 m 1 + 1096.08331094 429.264204145 1156.38573934 356.569765138 1214.17570674 298.779797768 c 0 + 1318.08239786 194.873106647 1414.38934249 138.502937368 1488.26910654 107.580633787 c 0 + 1529.32544481 90.3965432265 1563.51147656 81.0776979108 1588.2975965 76.0087313726 c 0 + 1615.83281888 70.3775506458 1631.76992005 69.9999410877 1632.46377743 69.986036148 c 1 + 1632.30173622 69.9872464204 1632.00000005 69.9882346324 1632.00000005 69.9882346324 c 1 + 1632.00000005 -49.9882346324 l 1 + 1621.1900489 -49.9882346324 1287.46362572 -46.2881071791 968.265395637 407.051521928 c 1 + 991.110875674 442.719806976 1013.74868988 480.580583771 1036.07195969 520.767740886 c 1 +EndSplineSet +EndChar EndChars EndSplineFont diff --git a/res/icons.ttf b/res/icons.ttf index 0062e4bcb..499cb84a1 100644 Binary files a/res/icons.ttf and b/res/icons.ttf differ diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 5336c6491..68cc381ec 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -48,7 +48,9 @@ enum DivDispatchCmds { DIV_CMD_ENV_RELEASE, DIV_CMD_INSTRUMENT, // (ins, force) DIV_CMD_VOLUME, // (vol) + // TODO: think of possibly moving this DIV_CMD_GET_VOLUME, // () -> vol + // TODO: move. shouldn't be a command. DIV_CMD_GET_VOLMAX, // () -> volMax DIV_CMD_NOTE_PORTA, // (target, speed) -> 2 if target reached DIV_CMD_PITCH, // (pitch) @@ -594,6 +596,14 @@ class DivDispatch { */ virtual bool isVolGlobal(); + /** + * map MIDI velocity (from 0 to 127) to chip volume. + * @param ch the chip channel. -1 means N/A. + * @param vel input velocity. + * @return output volume. + */ + virtual int mapVelocity(int ch, unsigned char vel); + /** * get the lowest note in a portamento. * @param ch the channel in question. @@ -787,7 +797,7 @@ class DivDispatch { #define NOTE_FNUM_BLOCK(x,bits) parent->calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits) // this is for volume scaling calculation. -#define VOL_SCALE_LINEAR(x,y,range) (((x)*(y))/(range)) +#define VOL_SCALE_LINEAR(x,y,range) ((parent->song.ceilVolumeScaling)?((((x)*(y))+(range-1))/(range)):(((x)*(y))/(range))) #define VOL_SCALE_LOG(x,y,range) (CLAMP(((x)+(y))-(range),0,(range))) #define VOL_SCALE_LINEAR_BROKEN(x,y,range) ((parent->song.newVolumeScaling)?(VOL_SCALE_LINEAR(x,y,range)):(VOL_SCALE_LOG(x,y,range))) #define VOL_SCALE_LOG_BROKEN(x,y,range) ((parent->song.newVolumeScaling)?(VOL_SCALE_LOG(x,y,range)):(VOL_SCALE_LINEAR(x,y,range))) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 433397b59..126c84571 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -428,34 +428,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/engine.cpp b/src/engine/engine.cpp index 61d97b009..e5973681a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1522,6 +1522,11 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { while (playing && curOrdersetSkipRegisterWrites(false); + if (goal>0 || goalRow>0) { + for (int i=0; iforceIns(); + } return; } if (!preserveDrift) { @@ -1533,6 +1538,11 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { while (playing && (curRow1)) { if (nextTick(preserveDrift)) { skipping=false; + cmdStream.clear(); + for (int i=0; isetSkipRegisterWrites(false); + if (goal>0 || goalRow>0) { + for (int i=0; iforceIns(); + } return; } if (!preserveDrift) { diff --git a/src/engine/engine.h b/src/engine/engine.h index 43af4127f..db8cb7b34 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -54,8 +54,8 @@ class DivWorkPool; #define DIV_UNSTABLE -#define DIV_VERSION "dev187" -#define DIV_ENGINE_VERSION 187 +#define DIV_VERSION "dev189" +#define DIV_ENGINE_VERSION 189 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -190,7 +190,7 @@ struct DivNoteEvent { channel(-1), ins(0), note(0), - volume(0), + volume(-1), on(false), nop(true), pad1(false), @@ -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_), @@ -641,6 +647,8 @@ class DivEngine { SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true, bool optimize=true); // dump command stream. SafeWriter* saveCommand(bool binary=false); + // export to text + SafeWriter* saveText(bool separatePatterns=true); // export to an audio file bool saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime=0.0); // wait for audio export to finish diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index c2dea0085..cb6f4fa90 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1873,6 +1873,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<184) { ds.resetArpPhaseOnNewNote=false; } + if (ds.version<188) { + ds.ceilVolumeScaling=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -2182,7 +2185,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version>=39) { for (int i=0; ichanShow[i]=reader.readC(); + if (ds.version<189) { + subSong->chanShow[i]=reader.readC(); + subSong->chanShowChanOsc[i]=subSong->chanShow[i]; + } else { + unsigned char tempchar=reader.readC(); + subSong->chanShow[i]=tempchar&1; + subSong->chanShowChanOsc[i]=(tempchar&2); + } } for (int i=0; i=188) { + ds.ceilVolumeScaling=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<2; i++) { reader.readC(); } } @@ -2580,7 +2595,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } for (int i=0; ichanShow[i]=reader.readC(); + if (ds.version<189) { + subSong->chanShow[i]=reader.readC(); + subSong->chanShowChanOsc[i]=subSong->chanShow[i]; + } else { + unsigned char tempchar=reader.readC(); + subSong->chanShow[i]=tempchar&1; + subSong->chanShowChanOsc[i]=tempchar&2; + } } for (int i=0; ichanShow[i]=true; + ds.subsong[0]->chanShowChanOsc[i]=true; ds.subsong[0]->chanName[i]=fmt::sprintf("Channel %d",i+1); ds.subsong[0]->chanShortName[i]=fmt::sprintf("C%d",i+1); } for(int i=chCount; ipat[i].effectCols=1; ds.subsong[0]->chanShow[i]=false; + ds.subsong[0]->chanShowChanOsc[i]=false; } // instrument creation @@ -5402,7 +5426,10 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) { } for (int i=0; iwriteC(subSong->chanShow[i]); + w->writeC( + (subSong->chanShow[i]?1:0)| + (subSong->chanShowChanOsc[i]?2:0) + ); } for (int i=0; iwriteC(song.preNoteNoEffect); w->writeC(song.oldDPCM); w->writeC(song.resetArpPhaseOnNewNote); - for (int i=0; i<3; i++) { + w->writeC(song.ceilVolumeScaling); + for (int i=0; i<2; i++) { w->writeC(0); } @@ -5559,7 +5587,10 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) { } for (int i=0; iwriteC(subSong->chanShow[i]); + w->writeC( + (subSong->chanShow[i]?1:0)| + (subSong->chanShowChanOsc[i]?2:0) + ); } for (int i=0; iwriteText("- macros:\n"); + wroteMacroHeader=true; + } + w->writeText(fmt::sprintf(" - %s:",name)); + int len=m.len; + switch (m.open&6) { + case 2: + len=16; + w->writeText(" [ADSR]"); + break; + case 4: + len=16; + w->writeText(" [LFO]"); + break; + } + if (m.mode) { + w->writeText(fmt::sprintf(" [MODE %d]",m.mode)); + } + if (m.delay>0) { + w->writeText(fmt::sprintf(" [DELAY %d]",m.delay)); + } + if (m.speed>1) { + w->writeText(fmt::sprintf(" [SPEED %d]",m.speed)); + } + for (int i=0; iwriteText(" |"); + } + if (i==m.rel) { + w->writeText(" /"); + } + w->writeText(fmt::sprintf(" %d",m.val[i])); + } + w->writeText("\n"); +} + +SafeWriter* DivEngine::saveText(bool separatePatterns) { + saveLock.lock(); + + SafeWriter* w=new SafeWriter; + w->init(); + + w->writeText(fmt::sprintf("# Furnace Text Export\n\ngenerated by Furnace %s (%d)\n\n# Song Information\n\n",DIV_VERSION,DIV_ENGINE_VERSION)); + w->writeText(fmt::sprintf("- name: %s\n",song.name)); + w->writeText(fmt::sprintf("- author: %s\n",song.author)); + w->writeText(fmt::sprintf("- album: %s\n",song.category)); + w->writeText(fmt::sprintf("- system: %s\n",song.systemName)); + w->writeText(fmt::sprintf("- tuning: %g\n\n",song.tuning)); + + w->writeText(fmt::sprintf("- instruments: %d\n",song.insLen)); + w->writeText(fmt::sprintf("- wavetables: %d\n",song.waveLen)); + w->writeText(fmt::sprintf("- samples: %d\n\n",song.sampleLen)); + + w->writeText("# Sound Chips\n\n"); + + for (int i=0; iwriteText(fmt::sprintf("- %s\n",getSystemName(song.system[i]))); + w->writeText(fmt::sprintf(" - id: %.2X\n",(int)song.system[i])); + w->writeText(fmt::sprintf(" - volume: %g\n",song.systemVol[i])); + w->writeText(fmt::sprintf(" - panning: %g\n",song.systemPan[i])); + w->writeText(fmt::sprintf(" - front/rear: %g\n",song.systemPanFR[i])); + + String sysFlags=song.systemFlags[i].toString(); + + if (!sysFlags.empty()) { + w->writeText(fmt::sprintf(" - flags:\n```\n%s\n```\n",sysFlags)); + } + } + + if (!song.notes.empty()) { + w->writeText("\n# Song Comments\n\n"); + w->writeText(song.notes); + } + + w->writeText("\n# Instruments\n\n"); + + for (int i=0; iwriteText(fmt::sprintf("## %.2X: %s\n\n",i,ins->name)); + + w->writeText(fmt::sprintf("- type: %d\n",(int)ins->type)); + + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPM) { + w->writeText("- FM parameters:\n"); + w->writeText(fmt::sprintf(" - ALG: %d\n",ins->fm.alg)); + w->writeText(fmt::sprintf(" - FB: %d\n",ins->fm.fb)); + w->writeText(fmt::sprintf(" - FMS: %d\n",ins->fm.fms)); + w->writeText(fmt::sprintf(" - AMS: %d\n",ins->fm.ams)); + w->writeText(fmt::sprintf(" - FMS2: %d\n",ins->fm.fms2)); + w->writeText(fmt::sprintf(" - AMS2: %d\n",ins->fm.ams2)); + w->writeText(fmt::sprintf(" - operators: %d\n",ins->fm.ops)); + w->writeText(fmt::sprintf(" - OPLL patch: %d\n",ins->fm.opllPreset)); + w->writeText(fmt::sprintf(" - fixed drum freq: %s\n",trueFalse[ins->fm.fixedDrums?1:0])); + w->writeText(fmt::sprintf(" - kick freq: %.4X\n",ins->fm.kickFreq)); + w->writeText(fmt::sprintf(" - snare/hat freq: %.4X\n",ins->fm.snareHatFreq)); + w->writeText(fmt::sprintf(" - tom/top freq: %.4X\n",ins->fm.tomTopFreq)); + + for (int j=0; jfm.ops; j++) { + DivInstrumentFM::Operator& op=ins->fm.op[j]; + + w->writeText(fmt::sprintf(" - operator %d:\n",j)); + w->writeText(fmt::sprintf(" - enabled: %s\n",trueFalse[op.enable?1:0])); + w->writeText(fmt::sprintf(" - AM: %d\n",op.am)); + w->writeText(fmt::sprintf(" - AR: %d\n",op.ar)); + w->writeText(fmt::sprintf(" - DR: %d\n",op.dr)); + w->writeText(fmt::sprintf(" - MULT: %d\n",op.mult)); + w->writeText(fmt::sprintf(" - RR: %d\n",op.rr)); + w->writeText(fmt::sprintf(" - SL: %d\n",op.sl)); + w->writeText(fmt::sprintf(" - TL: %d\n",op.tl)); + w->writeText(fmt::sprintf(" - DT2: %d\n",op.dt2)); + w->writeText(fmt::sprintf(" - RS: %d\n",op.rs)); + w->writeText(fmt::sprintf(" - DT: %d\n",op.dt)); + w->writeText(fmt::sprintf(" - D2R: %d\n",op.d2r)); + w->writeText(fmt::sprintf(" - SSG-EG: %d\n",op.ssgEnv)); + w->writeText(fmt::sprintf(" - DAM: %d\n",op.dam)); + w->writeText(fmt::sprintf(" - DVB: %d\n",op.dvb)); + w->writeText(fmt::sprintf(" - EGT: %d\n",op.egt)); + w->writeText(fmt::sprintf(" - KSL: %d\n",op.ksl)); + w->writeText(fmt::sprintf(" - SUS: %d\n",op.sus)); + w->writeText(fmt::sprintf(" - VIB: %d\n",op.vib)); + w->writeText(fmt::sprintf(" - WS: %d\n",op.ws)); + w->writeText(fmt::sprintf(" - KSR: %d\n",op.ksr)); + w->writeText(fmt::sprintf(" - TL volume scale: %d\n",op.kvs)); + } + } + + if (ins->type==DIV_INS_GB) { + w->writeText("- Game Boy parameters:\n"); + w->writeText(fmt::sprintf(" - volume: %d\n",ins->gb.envVol)); + w->writeText(fmt::sprintf(" - direction: %d\n",gbEnvDir[ins->gb.envDir?1:0])); + w->writeText(fmt::sprintf(" - length: %d\n",ins->gb.envLen)); + w->writeText(fmt::sprintf(" - sound length: %d\n",ins->gb.soundLen)); + w->writeText(fmt::sprintf(" - use software envelope: %s\n",trueFalse[ins->gb.softEnv?1:0])); + w->writeText(fmt::sprintf(" - always initialize: %s\n",trueFalse[ins->gb.softEnv?1:0])); + if (ins->gb.hwSeqLen>0) { + w->writeText(" - hardware sequence:\n"); + for (int j=0; jgb.hwSeqLen; j++) { + w->writeText(fmt::sprintf(" - %d: %.2X %.4X\n",j,ins->gb.hwSeq[j].cmd,ins->gb.hwSeq[j].data)); + } + } + } + + bool header=false; + writeTextMacro(w,ins->std.volMacro,"vol",header); + writeTextMacro(w,ins->std.arpMacro,"arp",header); + writeTextMacro(w,ins->std.dutyMacro,"duty",header); + writeTextMacro(w,ins->std.waveMacro,"wave",header); + writeTextMacro(w,ins->std.pitchMacro,"pitch",header); + writeTextMacro(w,ins->std.panLMacro,"panL",header); + writeTextMacro(w,ins->std.panRMacro,"panR",header); + writeTextMacro(w,ins->std.phaseResetMacro,"phaseReset",header); + writeTextMacro(w,ins->std.ex1Macro,"ex1",header); + writeTextMacro(w,ins->std.ex2Macro,"ex2",header); + writeTextMacro(w,ins->std.ex3Macro,"ex3",header); + writeTextMacro(w,ins->std.ex4Macro,"ex4",header); + writeTextMacro(w,ins->std.ex5Macro,"ex5",header); + writeTextMacro(w,ins->std.ex6Macro,"ex6",header); + writeTextMacro(w,ins->std.ex7Macro,"ex7",header); + writeTextMacro(w,ins->std.ex8Macro,"ex8",header); + writeTextMacro(w,ins->std.algMacro,"alg",header); + writeTextMacro(w,ins->std.fbMacro,"fb",header); + writeTextMacro(w,ins->std.fmsMacro,"fms",header); + writeTextMacro(w,ins->std.amsMacro,"ams",header); + + // TODO: the rest + w->writeText("\n"); + } + + w->writeText("\n# Wavetables\n\n"); + + for (int i=0; iwriteText(fmt::sprintf("- %d (%dx%d):",i,wave->len+1,wave->max+1)); + for (int j=0; j<=wave->len; j++) { + w->writeText(fmt::sprintf(" %d",wave->data[j])); + } + w->writeText("\n"); + } + + w->writeText("\n# Samples\n\n"); + + for (int i=0; iwriteText(fmt::sprintf("## %.2X: %s\n\n",i,sample->name)); + + w->writeText(fmt::sprintf("- format: %d\n",(int)sample->depth)); + w->writeText(fmt::sprintf("- data length: %d\n",sample->getCurBufLen())); + w->writeText(fmt::sprintf("- samples: %d\n",sample->samples)); + w->writeText(fmt::sprintf("- rate: %d\n",sample->centerRate)); + w->writeText(fmt::sprintf("- compat rate: %d\n",sample->rate)); + w->writeText(fmt::sprintf("- loop: %s\n",trueFalse[sample->loop?1:0])); + if (sample->loop) { + w->writeText(fmt::sprintf(" - start: %d\n",sample->loopStart)); + w->writeText(fmt::sprintf(" - end: %d\n",sample->loopEnd)); + w->writeText(fmt::sprintf(" - mode: %s\n",sampleLoopModes[sample->loopMode&3])); + } + w->writeText(fmt::sprintf("- BRR emphasis: %s\n",trueFalse[sample->brrEmphasis?1:0])); + w->writeText(fmt::sprintf("- dither: %s\n",trueFalse[sample->dither?1:0])); + + // TODO' render matrix + unsigned char* buf=(unsigned char*)sample->getCurBuf(); + unsigned int bufLen=sample->getCurBufLen(); + w->writeText("\n```"); + for (unsigned int i=0; iwriteText(fmt::sprintf("\n%.8X:",i)); + w->writeText(fmt::sprintf(" %.2X",buf[i])); + } + w->writeText("\n```\n\n"); + } + + w->writeText("\n# Subsongs\n\n"); + + for (size_t i=0; iwriteText(fmt::sprintf("## %d: %s\n\n",(int)i,s->name)); + + w->writeText(fmt::sprintf("- tick rate: %g\n",s->hz)); + w->writeText(fmt::sprintf("- speeds:")); + for (int j=0; jspeeds.len; j++) { + w->writeText(fmt::sprintf(" %d",s->speeds.val[j])); + } + w->writeText("\n"); + w->writeText(fmt::sprintf("- virtual tempo: %d/%d\n",s->virtualTempoN,s->virtualTempoD)); + w->writeText(fmt::sprintf("- time base: %d\n",s->timeBase)); + w->writeText(fmt::sprintf("- pattern length: %d\n",s->patLen)); + w->writeText(fmt::sprintf("\norders:\n```\n")); + + for (int j=0; jordersLen; j++) { + w->writeText(fmt::sprintf("%.2X |",j)); + for (int k=0; kwriteText(fmt::sprintf(" %.2X",s->orders.ord[k][j])); + } + w->writeText("\n"); + } + w->writeText("```\n\n## Patterns\n\n"); + + if (separatePatterns) { + w->writeText("TODO: separate patterns\n\n"); + } else { + for (int j=0; jordersLen; j++) { + w->writeText(fmt::sprintf("----- ORDER %.2X\n",j)); + + for (int k=0; kpatLen; k++) { + w->writeText(fmt::sprintf("%.2X ",k)); + + for (int l=0; lpat[l].getPattern(s->orders.ord[l][j],false); + + int note=p->data[k][0]; + int octave=p->data[k][1]; + + if (note==0 && octave==0) { + w->writeText("|... "); + } else if (note==100) { + w->writeText("|OFF "); + } else if (note==101) { + w->writeText("|=== "); + } else if (note==102) { + w->writeText("|REL "); + } else if ((octave>9 && octave<250) || note>12) { + w->writeText("|??? "); + } else { + if (octave>=128) octave-=256; + if (note>11) { + note-=12; + octave++; + } + w->writeText(fmt::sprintf("|%s%d ",(octave<0)?notesNegative[note]:notes[note],(octave<0)?(-octave):octave)); + } + + if (p->data[k][2]==-1) { + w->writeText(".. "); + } else { + w->writeText(fmt::sprintf("%.2X ",p->data[k][2]&0xff)); + } + + if (p->data[k][3]==-1) { + w->writeText(".."); + } else { + w->writeText(fmt::sprintf("%.2X",p->data[k][3]&0xff)); + } + + for (int m=0; mpat[l].effectCols; m++) { + if (p->data[k][4+(m<<1)]==-1) { + w->writeText(" .."); + } else { + w->writeText(fmt::sprintf(" %.2X",p->data[k][4+(m<<1)]&0xff)); + } + if (p->data[k][5+(m<<1)]==-1) { + w->writeText(".."); + } else { + w->writeText(fmt::sprintf("%.2X",p->data[k][5+(m<<1)]&0xff)); + } + } + } + + w->writeText("\n"); + } + } + } + + } + + saveLock.unlock(); + return w; +} diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index e0b0229cb..aaa27a494 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -102,6 +102,11 @@ bool DivDispatch::isVolGlobal() { return false; } +int DivDispatch::mapVelocity(int ch, unsigned char vel) { + const int volMax=MAX(1,dispatch(DivCommand(DIV_CMD_GET_VOLMAX,MAX(ch,0)))); + return (vel*volMax)/127; +} + int DivDispatch::getPortaFloor(int ch) { return 0x00; } 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/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 52968a1a5..8eb95fc28 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -292,12 +292,507 @@ 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); + } + } + } +} + +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++) { + 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); + 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); + } + } +} + +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; + } + + if (properDrums) { + for (int i=0; i<16; 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) { + 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); + } + } + } +} + +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]; + 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; + } + } + + fm_lle2.input.mclk=1; + FMOPL2_Clock(&fm_lle2); + fm_lle2.input.mclk=0; + FMOPL2_Clock(&fm_lle2); + + if (waitingBusy) { + if (--delay<0) waitingBusy=false; + } + + if (!(++subCycle&3)) { + if (properDrums) { + chOut[cycleMapDrums[curCycle]]+=fm_lle2.op_value_debug; + } else { + chOut[cycleMap[curCycle]]+=fm_lle2.op_value_debug; + } + curCycle++; + } + + if (fm_lle2.o_sy && !lastSY) { + dacVal>>=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; + break; + } + } + + 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]; + } + + 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; + } +} + +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) { - //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 +2115,68 @@ 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); + + 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) { + 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; + } + } 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 +2290,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 +2437,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 +2538,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 +2583,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..c4346a21e 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,16 @@ class DivPlatformOPL: public DivDispatch { QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {} }; FixedQueue writes; - opl3_chip fm; + + unsigned int dacVal; + unsigned int dacVal2; + int dacOut; + int dacOut3[4]; + bool lastSH; + bool lastSH2; + bool lastSY; + bool waitingBusy; + unsigned char* adpcmBMem; size_t adpcmBMemLen; DivOPLAInterface iface; @@ -93,11 +107,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 +134,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 +157,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/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(); 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.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..8a2dd5147 --- /dev/null +++ b/src/engine/platform/sound/ymfm/ymfm_opl.h @@ -0,0 +1,911 @@ +// 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); + + fm_engine* debug_fm_engine() { return &m_fm; } +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); + + 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 + 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); + + fm_engine* debug_fm_engine() { return &m_fm; } + +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); + + fm_engine* debug_fm_engine() { return &m_fm; } + +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 diff --git a/src/engine/platform/vb.cpp b/src/engine/platform/vb.cpp index 588676aff..c94867fa7 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; @@ -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: { @@ -407,8 +414,14 @@ void DivPlatformVB::forceIns() { chan[i].insChanged=true; chan[i].freqChanged=true; updateWave(i); + if (romMode) { + chWrite(i,0x06,chan[i].wave); + } chWrite(i,0x01,isMuted[i]?0:chan[i].pan); } + if (chan[5].active) { + writeEnv(5,true); + } } void* DivPlatformVB::getChanState(int ch) { @@ -464,8 +477,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 +499,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 +527,7 @@ void DivPlatformVB::notifyWaveChange(int wave) { updateWave(i); } } + updateROMWaves(); } void DivPlatformVB::notifyInsDeletion(void* ins) { @@ -504,6 +544,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/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 6d97c9c4e..38b166bd1 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; @@ -136,6 +144,7 @@ void DivPlatformVERA::reset() { } chan[16].vol=15; chan[16].pan=3; + lastCenterRate=-1; } int DivPlatformVERA::calcNoteFreq(int ch, int note) { @@ -218,11 +227,12 @@ void DivPlatformVERA::tick(bool sysTick) { double off=65536.0; if (chan[16].pcm.sample>=0 && chan[16].pcm.samplesong.sampleLen) { DivSample* s=parent->getSample(chan[16].pcm.sample); - if (s->centerRate<1) { - off=65536.0; - } else { + lastCenterRate=s->centerRate; + if (s->centerRate>=1) { off=65536.0*(s->centerRate/8363.0); } + } else if (lastCenterRate>=1) { + off=65536.0*(lastCenterRate/8363.0); } chan[16].freq=parent->calcFreq(chan[16].baseFreq,chan[16].pitch,chan[16].fixedArp?chan[16].baseNoteOverride:chan[16].arpOff,chan[16].fixedArp,false,8,chan[16].pitch2,chipClock,off); if (chan[16].freq>128) chan[16].freq=128; diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 515e17e56..992bb0798 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) {} @@ -54,6 +54,7 @@ class DivPlatformVERA: public DivDispatch { unsigned char regPool[69]; struct VERA_PSG* psg; struct VERA_PCM* pcm; + int lastCenterRate; int calcNoteFreq(int ch, int note); friend void putDispatchChip(void*,int); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index c790ed417..ada786c9d 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -39,7 +39,7 @@ void DivEngine::nextOrder() { } } -const char* notes[12]={ +static const char* notes[12]={ "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" }; @@ -1378,7 +1378,11 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } if (note.on) { dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); - //dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,(note.volume*(chan[note.channel].volMax>>8))/127)); + if (note.volume>=0) { + int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(note.channel,note.volume); + logV("dispatching volume (%d -> %d)",note.volume,mappedVol); + dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,mappedVol)); + } dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,note.channel,note.note)); keyHit[note.channel]=true; chan[note.channel].releasing=false; diff --git a/src/engine/song.h b/src/engine/song.h index 4545ed126..3cb645b3e 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -165,6 +165,7 @@ struct DivSubSong { DivChannelData pat[DIV_MAX_CHANS]; bool chanShow[DIV_MAX_CHANS]; + bool chanShowChanOsc[DIV_MAX_CHANS]; unsigned char chanCollapse[DIV_MAX_CHANS]; String chanName[DIV_MAX_CHANS]; String chanShortName[DIV_MAX_CHANS]; @@ -185,6 +186,7 @@ struct DivSubSong { ordersLen(1) { for (int i=0; i ins; std::vector wave; @@ -501,7 +504,8 @@ struct DivSong { brokenFMOff(false), preNoteNoEffect(false), oldDPCM(false), - resetArpPhaseOnNewNote(false) { + resetArpPhaseOnNewNote(false), + ceilVolumeScaling(false) { for (int i=0; i", {}, {}, {}, {} ); 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"}, @@ -702,13 +703,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"}, @@ -725,7 +726,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"}, @@ -788,7 +789,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"}, @@ -800,13 +801,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<", + "Notakin", "NyaongI", + "Pale Moon", + "PeyPey", "PichuMario", "potatoTeto", "psxdominator", "Raijin", "railzen7", + "RepellantMold", + "RetroCarrot", "RevvoBolt", + "scooblee", + "sillygoose", "SnugglyBun", "SuperJet Spade", + "Supper_E1", "SwapXFO", + "System64", "TakuikaNinja", "tapekeep", "TCORPStudios", @@ -176,7 +191,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, 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/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index abdf708b0..f702fdb96 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -92,7 +92,7 @@ void FurnaceGUI::calcChanOsc() { if (--tryAgain<0) break; buf=e->getOscBuffer(tryAgain); } - if (buf!=NULL && e->curSubSong->chanShow[i]) { + if (buf!=NULL && e->curSubSong->chanShowChanOsc[i]) { // 30ms should be enough int displaySize=(float)(buf->rate)*0.03f; if (e->isRunning()) { @@ -135,7 +135,7 @@ void FurnaceGUI::drawChanOsc() { ImGui::Text("Columns"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##COSColumns",&chanOscCols,1,1)) { + if (ImGui::InputInt("##COSColumns",&chanOscCols,1,3)) { if (chanOscCols<1) chanOscCols=1; if (chanOscCols>64) chanOscCols=64; } @@ -144,7 +144,7 @@ void FurnaceGUI::drawChanOsc() { ImGui::Text("Size (ms)"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputFloat("##COSWinSize",&chanOscWindowSize,1.0f,1.0f)) { + if (ImGui::InputFloat("##COSWinSize",&chanOscWindowSize,1.0f,10.0f)) { if (chanOscWindowSize<1.0f) chanOscWindowSize=1.0f; if (chanOscWindowSize>50.0f) chanOscWindowSize=50.0f; } @@ -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(); } @@ -376,7 +381,7 @@ void FurnaceGUI::drawChanOsc() { // fill buffers for (int i=0; igetOscBuffer(i); - if (buf!=NULL && e->curSubSong->chanShow[i]) { + if (buf!=NULL && e->curSubSong->chanShowChanOsc[i]) { oscBufs.push_back(buf); oscFFTs.push_back(&chanOscChan[i]); oscChans.push_back(i); @@ -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/channels.cpp b/src/gui/channels.cpp index 48dad94f5..608604ea8 100644 --- a/src/gui/channels.cpp +++ b/src/gui/channels.cpp @@ -38,23 +38,39 @@ void FurnaceGUI::drawChannels() { //ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH)); } if (ImGui::Begin("Channels",&channelsOpen,globalWinFlags)) { - if (ImGui::BeginTable("ChannelList",3)) { + if (ImGui::BeginTable("ChannelList",5)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); - ImGui::Text("Visible"); + ImGui::Text("Pat"); + ImGui::TableNextColumn(); + ImGui::Text("Osc"); + ImGui::TableNextColumn(); + ImGui::Text("Swap"); ImGui::TableNextColumn(); ImGui::Text("Name"); for (int i=0; igetTotalChannelCount(); i++) { ImGui::PushID(i); ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Checkbox("##Visible",&e->curSubSong->chanShow[i])) { + if (ImGui::Checkbox("##VisiblePat",&e->curSubSong->chanShow[i])) { MARK_MODIFIED; } - ImGui::SameLine(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Show in pattern"); + } + ImGui::TableNextColumn(); + if (ImGui::Checkbox("##VisibleChanOsc",&e->curSubSong->chanShowChanOsc[i])) { + MARK_MODIFIED; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Show in per-channel oscilloscope"); + } + ImGui::TableNextColumn(); if (ImGui::Button(ICON_FA_ARROWS)) { } if (ImGui::BeginDragDropSource()) { diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index d5419c01c..a11200544 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -336,6 +336,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("when enabled, arpeggio effect (00xy) position is reset on a new note."); } + ImGui::Checkbox("Volume scaling rounds up",&e->song.ceilVolumeScaling); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, volume macros round up when applied\nthis prevents volume scaling from causing vol=0, which is silent on some chips\n\nineffective on logarithmic channels"); + } ImGui::EndTabItem(); } ImGui::EndTabBar(); 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; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 0d1bcf623..3acc4bc59 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -717,16 +717,48 @@ void FurnaceGUI::doAction(int what) { insListDir=!insListDir; break; - case GUI_ACTION_WAVE_LIST_ADD: + case GUI_ACTION_WAVE_LIST_ADD: { + waveSizeList.clear(); + for (int i=0; isong.systemLen; i++) { + const DivSysDef* sysDef=e->getSystemDef(e->song.system[i]); + if (sysDef==NULL) continue; + + if (sysDef->waveHeight==0) continue; + if (sysDef->waveWidth==0) { + // add three preset sizes + waveSizeList.push_back(FurnaceGUIWaveSizeEntry(32,sysDef->waveHeight,sysDef->name)); + waveSizeList.push_back(FurnaceGUIWaveSizeEntry(64,sysDef->waveHeight,sysDef->name)); + waveSizeList.push_back(FurnaceGUIWaveSizeEntry(128,sysDef->waveHeight,sysDef->name)); + } else { + waveSizeList.push_back(FurnaceGUIWaveSizeEntry(sysDef->waveWidth,sysDef->waveHeight,sysDef->name)); + } + } + + int finalWidth=32; + int finalHeight=32; + if (waveSizeList.size()==1) { + finalWidth=waveSizeList[0].width; + finalHeight=waveSizeList[0].height; + } else if (waveSizeList.size()>1) { + displayWaveSizeList=true; + break; + } + curWave=e->addWave(); if (curWave==-1) { showError("too many wavetables!"); } else { wantScrollList=true; + e->song.wave[curWave]->len=finalWidth; + e->song.wave[curWave]->max=finalHeight-1; + for (int j=0; jsong.wave[curWave]->data[j]=(j*finalHeight)/finalWidth; + } MARK_MODIFIED; RESET_WAVE_MACRO_ZOOM; } break; + } case GUI_ACTION_WAVE_LIST_DUPLICATE: if (curWave>=0 && curWave<(int)e->song.wave.size()) { int prevWave=curWave; @@ -1336,6 +1368,10 @@ void FurnaceGUI::doAction(int what) { MARK_MODIFIED; break; } + case GUI_ACTION_SAMPLE_CROSSFADE_LOOP: + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + openSampleCrossFadeOpt=true; + break; case GUI_ACTION_SAMPLE_FILTER: if (curSample<0 || curSample>=(int)e->song.sample.size()) break; openSampleFilterOpt=true; diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 8a199997e..b9b8a7409 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -534,6 +534,10 @@ void FurnaceGUI::drawMobileControls() { if (ImGui::Button("CmdStream Text")) { openFileDialog(GUI_FILE_EXPORT_CMDSTREAM); } + ImGui::SameLine(); + if (ImGui::Button("Text")) { + openFileDialog(GUI_FILE_EXPORT_TEXT); + } if (ImGui::Button("Restore Backup")) { mobileMenuOpen=false; diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 45603e68c..d27c55a13 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -24,6 +24,17 @@ #include "actionUtil.h" +static const char* modPlugFormatHeaders[]={ + "ModPlug Tracker MOD", + "ModPlug Tracker S3M", + "ModPlug Tracker XM", + "ModPlug Tracker XM", + "ModPlug Tracker IT", + "ModPlug Tracker IT", + "ModPlug Tracker MPT", + NULL, +}; + const char* FurnaceGUI::noteNameNormal(short note, short octave) { if (note==100) { // note cut return "OFF"; @@ -77,6 +88,7 @@ void FurnaceGUI::prepareUndo(ActionType action) { void FurnaceGUI::makeUndo(ActionType action) { bool doPush=false; + bool shallWalk=false; UndoStep s; s.type=action; s.cursor=cursor; @@ -125,6 +137,18 @@ void FurnaceGUI::makeUndo(ActionType action) { for (int k=0; kdata[j][k]!=oldPat[i]->data[j][k]) { s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->data[j][k])); + + if (k>=4) { + if (oldPat[i]->data[j][k&(~1)]==0x0b || + p->data[j][k&(~1)]==0x0b || + oldPat[i]->data[j][k&(~1)]==0x0d || + p->data[j][k&(~1)]==0x0d || + oldPat[i]->data[j][k&(~1)]==0xff || + p->data[j][k&(~1)]==0xff) { + shallWalk=true; + } + } + } } } @@ -145,6 +169,9 @@ void FurnaceGUI::makeUndo(ActionType action) { redoHist.clear(); if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); } + if (shallWalk) { + e->walkSong(loopOrder,loopRow,loopEnd); + } } void FurnaceGUI::doSelectAll() { @@ -433,36 +460,8 @@ String FurnaceGUI::doCopy(bool cut, bool writeClipboard, const SelectionPoint& s return clipb; } -void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) { - if (readClipboard) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PASTE); - char* clipText=SDL_GetClipboardText(); - if (clipText!=NULL) { - if (clipText[0]) { - clipboard=clipText; - } - SDL_free(clipText); - } - clipb=clipboard; - } - std::vector data; - String tempS; - for (char i: clipb) { - if (i=='\r') continue; - if (i=='\n') { - data.push_back(tempS); - tempS=""; - continue; - } - tempS+=i; - } - data.push_back(tempS); - - int startOff=-1; - bool invalidData=false; - if (data.size()<2) return; - if (data[0].find("org.tildearrow.furnace - Pattern Data")!=0) return; +void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData) +{ if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return; if (startOff<0) return; @@ -607,6 +606,605 @@ void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String cli } } +unsigned int convertEffectMPT_MOD(unsigned char symbol, unsigned int val) { + switch (symbol) { + case '0': + return (0x00<<8)|val; + break; + case '1': + return (0x01<<8)|val; + break; + case '2': + return (0x02<<8)|val; + break; + case '3': + return (0x03<<8)|val; + break; + case '4': + return (0x04<<8)|val; + break; + case '5': + return (0x0a<<8)|val|(0x03<<24); // Axy+300 + break; + case '6': + return (0x0a<<8)|val|(0x04<<24); // Axy+400 + break; + case '7': + return (0x07<<8)|val; + break; + case '8': + return (0x80<<8)|val; + break; + case '9': + return (0x90<<8)|val; + break; + case 'A': + return (0x0A<<8)|val; + break; + case 'B': + return (0x0B<<8)|val; + break; + case 'C': + return (0x0C<<8)|val; // interpreted as volume later + break; + case 'D': { + unsigned char newParam=(val&0xf)+((val&0xff)>>4)*10; // hex to decimal, Protracker (and XM too!) lol + return (0x0D<<8)|newParam; + break; + } + case 'E': { + switch (val>>4) { + case 1: + return (0xF1<<8)|(val&0xf); + break; + case 2: + return (0xF2<<8)|(val&0xf); + break; + // glissando and vib shape not supported in Furnace + case 5: + return (0xF5<<8)|((val&0xf)<<4); + break; + // pattern loop not supported + case 8: + return (0x80<<8)|((val&0xf)<<4); + break; + case 9: + return (0x0C<<8)|(val&0xf); + break; + case 0xA: + return (0xF3<<8)|(val&0xf); + break; + case 0xB: + return (0xF4<<8)|(val&0xf); + break; + case 0xC: + return (0xFC<<8)|(val&0xf); + break; + case 0xD: + return (0xFD<<8)|(val&0xf); + break; + default: + break; + } + break; + } + case 'F': + if (val<0x20) { + return (0x0F<<8)|val; + } else { + return (0xF0<<8)|val; + } + break; + } + + return 0; +} + +unsigned int convertEffectMPT_S3M(unsigned char symbol, unsigned int val) { + switch (symbol) { + case 'A': + return (0x09<<8)|val; + break; + case 'B': + return (0x0B<<8)|val; + break; + case 'C': + return (0x0D<<8)|val; + break; + case 'D': + if ((val&0xf0)==0xf0) { + return (0xF4<<8)|(val&0xf); + } else if ((val&0xf)==0xf) { + return (0xF3<<8)|((val&0xf0)>>4); + } else { + return (0x0A<<8)|val; + } + break; + case 'E': + if (val<0xe0) { + return (0x02<<8)|val; + } else if (val>=0xe0 && val<0xf0) { + return (0xF2<<8)|(val&0xf); + } else { + return (0xF2<<8)|((val&0xf)>>1); + } + break; + case 'F': + if (val<0xe0) { + return (0x01<<8)|val; + } else if (val>=0xe0 && val<0xf0) { + return (0xF1<<8)|(val&0xf); + } else { + return (0xF1<<8)|((val&0xf)>>1); + } + break; + case 'G': + return (0x03<<8)|val; + break; + case 'H': + return (0x04<<8)|val; + break; + case 'J': + return (0x00<<8)|val; + break; + case 'K': + return (0x0a<<8)|val|(0x04<<24); // Axy+400 + break; + case 'L': + return (0x0a<<8)|val|(0x03<<24); // Axy+300 + break; + case 'O': + return (0x90<<8)|val; + break; + case 'Q': + return (0xC0<<8)|(val&0xf); + break; + case 'R': + return (0x07<<8)|(val&0xf); + break; + case 'S': { + switch (val>>4) { + case 2: + return (0xE5<<8)|((val&0xf)<<4); + break; + case 8: + return (0x80<<8)|((val&0xf)<<4); + break; + case 0xC: + return (0xFC<<8)|(val&0xf); + break; + case 0xD: + return (0xFD<<8)|(val&0xf); + break; + default: + break; + } + break; + } + case 'T': + return (0xF0<<8)|(val&0xf); + break; + case 'U': + return (0x04<<8)|MAX(1,((val&0xf0)>>6)<<4)|MAX(1,(val&0xf)>>2); + break; + case 'X': + return (0x80<<8)|val; + break; + default: + return 0; + break; + } + + return 0; +} + +unsigned int convertEffectMPT_XM(unsigned char symbol, unsigned int val) { + if (symbol=='K') { + return (0xEC<<8)|val; + } + + return convertEffectMPT_MOD(symbol,val); // for now +} + +unsigned int convertEffectMPT_IT(unsigned char symbol, unsigned int val) { + return convertEffectMPT_S3M(symbol,val); // for now +} + +unsigned int convertEffectMPT_MPTM(unsigned char symbol, unsigned int val) { + if (symbol==':') { + return (0xED<<8)|((val&0xf0)>>4)|(0xEC<<24)|((((val&0xf0)>>4)+(val&0xf))<<16); + } + + return convertEffectMPT_IT(symbol,val); +} + +// TODO: fix code style +void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat) +{ + DETERMINE_LAST; + + int j=cursor.y; + char note[4]; + bool invalidData=false; + + memset(note,0,4); + + for(size_t i=1; icurSubSong->patLen; i++) + { + size_t charPos=1; + int iCoarse=cursor.xCoarse; + int iFine=0; + + String& line=data[i]; + + while (charPoscurPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true); + if (line[charPos]=='|' && charPos != 0) // MPT format starts every pattern line with '|' + { + iCoarse++; + + if (iCoarsecurSubSong->chanShow[iCoarse]) + { + iCoarse++; + if (iCoarse>=lastChannel) break; + } + + iFine=0; + charPos++; + continue; + } + if (iFine==0) // note + { + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[1]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[2]=line[charPos++]; + note[3]=0; + + if (iFine==0 && !opMaskPaste.note) { + iFine++; + continue; + } + + if (strcmp(note,"...")==0 || strcmp(note," ")==0) + { + // do nothing. + } + + else + { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->data[j][0]==0 && pat->data[j][1]==0)) + { + if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) + { + if(strcmp(note, "^^^") == 0) + { + pat->data[j][0]=100; + pat->data[j][1]=0; + } + else if(strcmp(note, "~~~") == 0 || strcmp(note, "===") == 0) + { + pat->data[j][0]=101; + pat->data[j][1]=0; + } + else + { + invalidData=true; + } + + break; + } + else + { + pat->data[j][1]--; // MPT is one octave higher... + } + + if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->data[j][2]=arg; + } + } + } + else if (iFine==1) // instrument + { + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[1]=line[charPos++]; + note[2]=0; + + if (iFine==1) + { + if (!opMaskPaste.ins || mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) + { + iFine++; + continue; + } + } + + if (strcmp(note,"..")==0 || strcmp(note," ")==0) + { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG || + mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) + { + pat->data[j][iFine+1]=-1; + } + } + else + { + unsigned int val=0; + if (sscanf(note,"%2X",&val)!=1) + { + invalidData=true; + break; + } + + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) + { + pat->data[j][iFine+1]=val; + } + } + } + else + { // volume and effects + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[1]=line[charPos++]; + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[2]=line[charPos++]; + note[3]=0; + + if (iFine==2) + { + if (!opMaskPaste.vol) + { + iFine++; + continue; + } + } + + else if ((iFine&1)==0) + { + if (!opMaskPaste.effectVal) + { + iFine++; + continue; + } + } + else if ((iFine&1)==1) + { + if (!opMaskPaste.effect) + { + iFine++; + continue; + } + } + + if (strcmp(note,"...")==0 || strcmp(note," ")==0) + { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG || + mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) + { + pat->data[j][iFine+1]=-1; + } + } + else + { + unsigned int val=0; + unsigned char symbol = '\0'; + + symbol = note[0]; + + if(iFine == 2) + { + sscanf(¬e[1],"%2d",&val); + } + else + { + sscanf(¬e[1],"%2X",&val); + } + + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) + { + // if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val; + if(iFine == 2) // volume + { + switch(symbol) + { + case 'v': + { + pat->data[j][iFine+1]=val; + break; + } + + default: + break; + } + } + else // effect + { + unsigned int eff = 0; + + if(mptFormat == 0) + { + eff = convertEffectMPT_MOD(symbol, val); // up to 4 effects stored in one variable + + if(((eff & 0x0f00) >> 8) == 0x0C) // set volume + { + pat->data[j][iFine]=eff & 0xff; + } + } + + if(mptFormat == 1) + { + eff = convertEffectMPT_S3M(symbol, val); + } + + if(mptFormat == 2 || mptFormat == 3) // set volume + { + eff = convertEffectMPT_XM(symbol, val); + + if(((eff & 0x0f00) >> 8) == 0x0C) + { + pat->data[j][iFine]=eff & 0xff; + } + } + + if(mptFormat == 4 || mptFormat == 5) + { + eff = convertEffectMPT_IT(symbol, val); + } + + if(mptFormat == 6) + { + eff = convertEffectMPT_MPTM(symbol, val); + } + + pat->data[j][iFine+1]=((eff & 0xff00) >> 8); + pat->data[j][iFine+2]=(eff & 0xff); + + if(eff > 0xffff) + { + pat->data[j][iFine+3]=((eff & 0xff000000) >> 24); + pat->data[j][iFine+4]=((eff & 0xff0000) >> 16); + } + + } + } + } + } + + iFine++; + + if(charPos >= line.size() - 1) + { + invalidData = false; + break; + } + } + + if (invalidData) + { + logW("invalid OpenMPT clipboard data! failed at line %d char %d",i,charPos); + logW("%s",line.c_str()); + break; + } + + j++; + if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->curSubSong->patLen && curOrdercurSubSong->ordersLen-1) + { + j=0; + curOrder++; + } + + if (mode==GUI_PASTE_MODE_FLOOD && i==data.size()-1) + { + i=1; + } + } + + if (readClipboard) { + if (settings.cursorPastePos) { + cursor.y=j; + if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; + selStart=cursor; + selEnd=cursor; + updateScroll(cursor.y); + } + + makeUndo(GUI_UNDO_PATTERN_PASTE); + } +} + +void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) { + if (readClipboard) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PASTE); + char* clipText=SDL_GetClipboardText(); + if (clipText!=NULL) { + if (clipText[0]) { + clipboard=clipText; + } + SDL_free(clipText); + } + clipb=clipboard; + } + std::vector data; + String tempS; + bool foundString=false; + bool isFurnace=false; + bool isModPlug=false; + int mptFormat=0; + for (char i: clipb) { + if (i=='\r') continue; + if (i=='\n') { + data.push_back(tempS); + tempS=""; + continue; + } + tempS+=i; + } + data.push_back(tempS); + + int startOff=-1; + bool invalidData=false; + if (data.size()<2) return; + + if (data[0].find("org.tildearrow.furnace - Pattern Data")==0) { + foundString=true; + isFurnace=true; + } else { + for (int i=0; modPlugFormatHeaders[i]; i++) { + if (data[0].find(modPlugFormatHeaders[i])==0) { + foundString=true; + isModPlug=true; + mptFormat=i; + break; + } + } + } + + if (!foundString) return; + + if (isFurnace) { + doPasteFurnace(mode,arg,readClipboard,clipb,data,startOff,invalidData); + } else if (isModPlug) { + doPasteMPT(mode,arg,readClipboard,clipb,data,mptFormat); + } +} + void FurnaceGUI::doChangeIns(int ins) { finishSelection(); prepareUndo(GUI_UNDO_PATTERN_CHANGE_INS); @@ -1188,6 +1786,7 @@ void FurnaceGUI::doUndo() { setOrder(us.order); } } + e->walkSong(loopOrder,loopRow,loopEnd); break; } @@ -1263,7 +1862,7 @@ void FurnaceGUI::doRedo() { setOrder(us.order); } } - + e->walkSong(loopOrder,loopRow,loopEnd); break; } diff --git a/src/gui/findReplace.cpp b/src/gui/findReplace.cpp index 5f0d63218..5e2825231 100644 --- a/src/gui/findReplace.cpp +++ b/src/gui/findReplace.cpp @@ -924,14 +924,14 @@ void FurnaceGUI::drawFindReplace() { if (queryReplaceIns>255) queryReplaceIns=255; } } else if (queryReplaceInsMode==GUI_QUERY_REPLACE_ADD || queryReplaceInsMode==GUI_QUERY_REPLACE_ADD_OVERFLOW) { - if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,12)) { + if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,16)) { if (queryReplaceIns<-255) queryReplaceIns=-255; if (queryReplaceIns>255) queryReplaceIns=255; } } else if (queryReplaceInsMode==GUI_QUERY_REPLACE_SCALE) { if (queryReplaceIns<0) queryReplaceIns=0; if (queryReplaceIns>400) queryReplaceIns=400; - if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,12)) { + if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,10)) { if (queryReplaceIns<0) queryReplaceIns=0; if (queryReplaceIns>400) queryReplaceIns=400; } @@ -953,14 +953,14 @@ void FurnaceGUI::drawFindReplace() { if (queryReplaceVol>255) queryReplaceVol=255; } } else if (queryReplaceVolMode==GUI_QUERY_REPLACE_ADD || queryReplaceVolMode==GUI_QUERY_REPLACE_ADD_OVERFLOW) { - if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,12)) { + if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,16)) { if (queryReplaceVol<-255) queryReplaceVol=-255; if (queryReplaceVol>255) queryReplaceVol=255; } } else if (queryReplaceVolMode==GUI_QUERY_REPLACE_SCALE) { if (queryReplaceVol<0) queryReplaceVol=0; if (queryReplaceVol>400) queryReplaceVol=400; - if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,12)) { + if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,10)) { if (queryReplaceVol<0) queryReplaceVol=0; if (queryReplaceVol>400) queryReplaceVol=400; } @@ -984,14 +984,14 @@ void FurnaceGUI::drawFindReplace() { if (queryReplaceEffect[i]>255) queryReplaceEffect[i]=255; } } else if (queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_ADD || queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_ADD_OVERFLOW) { - if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,12)) { + if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,16)) { if (queryReplaceEffect[i]<-255) queryReplaceEffect[i]=-255; if (queryReplaceEffect[i]>255) queryReplaceEffect[i]=255; } } else if (queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_SCALE) { if (queryReplaceEffect[i]<0) queryReplaceEffect[i]=0; if (queryReplaceEffect[i]>400) queryReplaceEffect[i]=400; - if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,12)) { + if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,10)) { if (queryReplaceEffect[i]<0) queryReplaceEffect[i]=0; if (queryReplaceEffect[i]>400) queryReplaceEffect[i]=400; } @@ -1013,14 +1013,14 @@ void FurnaceGUI::drawFindReplace() { if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255; } } else if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD || queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD_OVERFLOW) { - if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,12)) { + if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,16)) { if (queryReplaceEffectVal[i]<-255) queryReplaceEffectVal[i]=-255; if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255; } } else if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_SCALE) { if (queryReplaceEffectVal[i]<0) queryReplaceEffectVal[i]=0; if (queryReplaceEffectVal[i]>400) queryReplaceEffectVal[i]=400; - if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,12)) { + if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,10)) { if (queryReplaceEffectVal[i]<0) queryReplaceEffectVal[i]=0; if (queryReplaceEffectVal[i]>400) queryReplaceEffectVal[i]=400; } diff --git a/src/gui/font_furicon.cpp b/src/gui/font_furicon.cpp index 7a112bb57..188053cb7 100644 --- a/src/gui/font_furicon.cpp +++ b/src/gui/font_furicon.cpp @@ -1,366 +1,370 @@ #include "fonts.h" -// File: 'icons.ttf' (31848 bytes) +// File: 'icons.ttf' (32076 bytes) // Exported using binary_to_compressed_c.cpp -const unsigned int furIcons_compressed_size = 17176; -const unsigned int furIcons_compressed_data[17176/4] = +const unsigned int furIcons_compressed_size = 17366; +const unsigned int furIcons_compressed_data[17368/4] = { - 0x0000bc57, 0x00000000, 0x687c0000, 0x00000400, 0x00010037, 0x000e0000, 0x00030080, 0x54464660, 0x81b6a24d, 0x7c00002e, 0x2815824c, 0x4544471c, - 0x00150046, 0x200f8214, 0x2b0f8330, 0x322f534f, 0x8c52fa8c, 0x68010000, 0x562b0f82, 0x70616d63, 0x13d444cb, 0x82020000, 0x0e022d13, 0x20747663, - 0x6f043b00, 0x9c040000, 0x04261f82, 0x70736167, 0x5982ffff, 0x287c0022, 0x082b0f82, 0x66796c67, 0x8d352477, 0x82050000, 0xfc70293f, 0x64616568, - 0x52610025, 0xec201b82, 0x36210382, 0x23108268, 0x05068209, 0x24205f82, 0x24270f82, 0x78746d68, 0x8218da2d, 0xc001212f, 0xcc280f82, 0x61636f6c, - 0xa00d4029, 0x03825f82, 0x6dc60028, 0x00707861, 0x3b8203ac, 0x82480121, 0x6e202c1f, 0xae656d61, 0x00ccc486, 0x82647600, 0x70e93043, 0xb374736f, - 0x00c25c09, 0x00507800, 0x84d60300, 0x00012deb, 0x0bac9800, 0x3c0f5f95, 0x070b00f5, 0x00253682, 0xfffde000, 0x2b08839f, 0x6e1963e1, 0x68fe80fc, - 0xe0050007, 0x08220f82, 0x05820200, 0x33820283, 0xff000622, 0x07290982, 0x0080fc00, 0x00000700, 0x8b0c8201, 0x84042002, 0x03622511, 0x000800bb, - 0x02210083, 0x83238300, 0x00402225, 0x820b822e, 0x00072b0d, 0x05009001, 0x8c040000, 0x1182e604, 0x0785fa20, 0x005c032b, 0x00cf0159, 0x05000200, - 0x85158209, 0x10012102, 0x05840785, 0x72754629, 0x00400054, 0x8558e120, 0x84062083, 0x8404856a, 0x833b208b, 0x82072026, 0x25038304, 0x090280fc, - 0x0c829100, 0x20005222, 0xa52c7982, 0x4b003500, 0x91002900, 0x78006d01, 0x60349b82, 0x52004700, 0x0a001701, 0x8f004a00, 0x1b003201, 0x2e004f00, - 0xca263182, 0x36011801, 0x1b82a900, 0x33002825, 0x82004d01, 0x38238291, 0x01220003, 0x007a017c, 0x01390039, 0x00ce0127, 0x014c0013, 0x00220073, - 0x240d8247, 0x0085000b, 0x260f82b5, 0x012e005b, 0x845c004b, 0x822a2001, 0x82532005, 0x004b3203, 0x0053005e, 0x015c00c8, 0x002e0156, 0x001e006b, - 0x21018446, 0x0185007d, 0x13220d83, 0x018ca000, 0x64006422, 0x0320b582, 0x1c200386, 0x0123df85, 0x84030008, 0x001c2409, 0x82ec0004, 0x00082e1b, - 0x00020008, 0xe0200000, 0xff58e1f4, 0x260984ff, 0xff00e1f0, 0x82e3ffff, 0x8400200b, 0x0006222b, 0x260b820e, 0x00050004, 0x82070006, 0x8209202f, - 0x000b24c9, 0x820d000c, 0x000f2617, 0x00110010, 0x32c98212, 0x00150014, 0x00170016, 0x00190018, 0x001b001a, 0x821d001c, 0x1f8408a7, 0x21002000, - 0x23002200, 0x25002400, 0x27002600, 0x29002800, 0x2b002a00, 0x2d002c00, 0x2f002e00, 0x31003000, 0x33003200, 0x35003400, 0x37003600, 0x39003800, - 0x3b003a00, 0x3d003c00, 0x3f003e00, 0x41004000, 0x43004200, 0x45004400, 0x47004600, 0x49004800, 0x4b004a00, 0x4d004c00, 0x4f004e00, 0x51005000, - 0x53005200, 0x55005400, 0x57005600, 0x59005800, 0x5b005a00, 0x5d005c00, 0x5f005e00, 0x61006000, 0x0620c682, 0xc782ca85, 0x02010022, 0x02210582, - 0x23008d00, 0x03000001, 0x0d40118d, 0x043b22d3, 0x21d6826f, 0x0185002c, 0x003cba08, 0x04c60084, 0x05240564, 0x07d406c4, 0x08ea0758, 0x093409ce, - 0x0ab00a76, 0x0b880bfe, 0x0c2e0cee, 0x0d6a0de6, 0x0e640ed8, 0x10a40fca, 0x11bc105a, 0x12c6117e, 0x130c1392, 0x14d8138c, 0x16481598, 0x17121738, - 0x1b1e1b80, 0x1c341cae, 0x1fea1dc0, 0x20bc1f08, 0x21342180, 0x22fc218a, 0x27c82386, 0x28ca272a, 0x295229b0, 0x2a0a2ac6, 0x2ba42bbe, 0x2e162ef4, - 0x2e682e44, 0x30f02f8e, 0x306a300c, 0x311e31d2, 0x31ac3156, 0x32ec31de, 0x336433ba, 0x344034c6, 0x353a35bc, 0x36de35b6, 0x3642361a, 0x37e43690, - 0x378e371c, 0x37b437a0, 0x37dc37c8, 0x380438f0, 0x384c3816, 0x08c5827e, 0x3b000221, 0x15020000, 0x0300aa04, 0x2e000700, 0x2f0001b1, 0x0407b23c, - 0xb132ed00, 0x3cdc0506, 0x820203b2, 0xb100220a, 0x20168303, 0x27168305, 0x010607b2, 0x01b23cfc, 0x33371783, 0x25112111, 0x3b211121, 0x61feda01, - 0x9cfe6401, 0x56fbaa04, 0x8334043b, 0x80fc2557, 0x8003f5ff, 0x01200982, 0x002e5982, 0x21252105, 0x9a0533fd, 0x0007b3f9, 0x15820b0b, 0x02032508, - 0x040f0009, 0x00fd03f7, 0x0025001b, 0x0100002f, 0x16322130, 0x010e1415, 0x15070607, 0x17161716, 0x1415011e, 0x0e2f0b82, 0x37212301, 0x3d363221, - 0x23263401, 0x85333521, 0x2b4d0809, 0x010a0201, 0x1f8e7dbd, 0x1e202031, 0x2025251e, 0x14132b1f, 0x385f2322, 0x01a217fe, 0x48483f18, 0xfce8fe3f, - 0x3d42423d, 0x8dfd03fc, 0x324f3877, 0x08030c0d, 0x1c0d0d02, 0x3c3f581c, 0x27273536, 0x3e428b2c, 0x88423e31, 0x3a2e393e, 0x088f833e, 0x00910030, - 0x036f0663, 0x000f00a9, 0x002b0017, 0x27302500, 0x07302130, 0x12362330, 0x12163337, 0x23300117, 0x33300706, 0x11300126, 0x33302700, 0x03851330, - 0x0702062f, 0xea023011, 0x49c7fe4c, 0x2fbf2f8a, 0x080382ab, 0x068dfe34, 0x3cf73d3c, 0xf7fe8402, 0x04cc9b17, 0xce3794cb, 0xe3e3631d, 0x8c2e028c, - 0x8cd2fd8c, 0xb9b9c902, 0x01f0fdb9, 0x28d4014a, 0x75018bfe, 0x329afe61, 0x8282b3fe, 0x82030021, 0x07ab3a04, 0x03610300, 0x038c0358, 0x370000ba, - 0x33270226, 0x17021d30, 0x1d17041d, 0x91029003, 0x8211a314, 0x9126f147, 0x26869571, 0x3317011e, 0x4137013e, 0x0d860673, 0x37033d22, 0x04200289, - 0x17850b8a, 0x298211a3, 0x719126f1, 0x332e86a1, 0x23070206, 0x2e270226, 0x0e232701, 0x0d820701, 0x2622052d, 0x37013e27, 0x3233011e, 0x83263435, - 0x27262918, 0x36343526, 0x17163233, 0x2e2b2182, 0x06222301, 0x16171415, 0x85011e17, 0x14152302, 0x3b822506, 0x27222322, 0x27262882, 0x36373426, - 0x3c823e37, 0x07202182, 0x1d352c84, 0x33161401, 0x023e3732, 0x3523013d, 0x8e231133, 0x68185e18, 0x3400cc01, 0x0c040e04, 0x0e030e04, 0x0f7b0e39, - 0x0f030e39, 0x0e030d03, 0x2761cc04, 0x62186401, 0x3a0e7a19, 0x0c205c83, 0x0f3b5c82, 0xf0020f3c, 0x0b276e4b, 0x4d1d0c2f, 0x342a7731, 0x5a0a2409, - 0x65702a2b, 0x82246348, 0x1a3c0813, 0x37373140, 0x09311616, 0x44300924, 0x74131615, 0x090c4302, 0x2c34484f, 0x1320212b, 0x22121212, 0x5a3d5f22, - 0x531e3837, 0x483c4611, 0x1e485050, 0x16271a1a, 0xb75adf7c, 0x70be0170, 0x0010b8cd, 0x2d47014d, 0x1919681a, 0x01441967, 0xfe444410, 0x0a8244f0, - 0x1a681922, 0x015b0110, 0x4d474147, 0xfe70af08, 0x01457042, 0x671a4513, 0x1a671919, 0x45edfe45, 0x0b36380b, 0x2c290b2a, 0x0d2f2c6d, 0x15020702, - 0x5c512d2c, 0x0a343161, 0x24250b29, 0x162a342d, 0x08020c15, 0x171f0b02, 0x65293f17, 0x3e306e68, 0x292a1313, 0x42b84242, 0x282a2a42, 0x31472627, - 0x525c3931, 0x085c52a4, 0x1e312109, 0x9efe573b, 0x00070000, 0x0602ff52, 0x001a05ae, 0x0024001a, 0x00490038, 0x00780053, 0x05000080, 0x34352622, - 0x37023e37, 0x06070633, 0x07010e07, 0x33013e33, 0x14151632, 0x3d322706, 0x22233401, 0x0314011d, 0x33270226, 0x16171216, 0x37363317, 0x2a821236, - 0x21070227, 0x32211123, 0x27278317, 0x2e231307, 0x37232701, 0x27077948, 0x26220115, 0x34012e27, 0x37222a82, 0x23823336, 0x45061721, 0x98080b58, - 0x011e3736, 0x05010e17, 0x01150111, 0x01350111, 0x106d6ba2, 0x314f3a0f, 0x2a2a3b8a, 0x09251d1e, 0x3147110c, 0x6a6e5f51, 0x1f6c6d6d, 0x6b218321, - 0x0e145013, 0x0e0e0c0e, 0x69144e14, 0x01208321, 0x0901658f, 0x392b2c56, 0x0871803a, 0x9b781756, 0x24262624, 0x3aad029b, 0x2321225d, 0x2e222123, - 0x33563b2e, 0x490d1f33, 0x44353e12, 0x36444c4c, 0x380f1242, 0xfe69200e, 0xfe2801e4, 0xfdd8fe72, 0x3d3c7f84, 0x22606f3c, 0x2928292d, 0x2f325628, - 0x6a676a31, 0x19775577, 0x08028277, 0x70180356, 0x466fbf01, 0x4e46e7fe, 0x464e4f4f, 0x6f461901, 0x027041fe, 0x6034339e, 0xfe11604d, 0x36c913e7, - 0x3e2a2655, 0xfede262a, 0x2a2a278e, 0x2a84b684, 0x2514142a, 0x29074a25, 0x525f3239, 0x365e53a1, 0x0921083b, 0xfeb24e4a, 0x82100134, 0xcd0192fe, - 0x0082effe, 0x043f0082, 0xab002000, 0x6103e006, 0x15000b00, 0x74004a00, 0x30370000, 0x32213011, 0x2b061416, 0x49011901, 0x012008b7, 0x2205ce46, - 0x82161716, 0x06d04612, 0x35012e28, 0x33023e34, 0xd1461732, 0x06072c08, 0x1f161415, 0x15011e01, 0x46020e14, 0x2e2105cd, 0x21248502, 0xc6461716, - 0x3736230c, 0xc846013e, 0x21d10806, 0x665d2001, 0xadb45d66, 0x292f2e2a, 0x55dd02ad, 0x320c2a76, 0x2b2b230d, 0x2e414136, 0x0a270a3b, 0x3d216262, - 0x384c3656, 0x320d2837, 0x384b1a0d, 0x34202039, 0x5d643c37, 0x025a3f20, 0x5e0a04dd, 0x4a663c4c, 0x6c4d2a29, 0x237d5a42, 0x3d511359, 0x50616150, - 0x161d1c20, 0xf38a1916, 0x9e02b75f, 0xfe6db96d, 0x2b6a01f5, 0x2b292e28, 0x343db6fd, 0x2b0c2f0b, 0x313a1716, 0x020c2e27, 0x5c120207, 0x31482c51, - 0x321a1b1b, 0x210b2d0b, 0x2c181729, 0x0c0b292a, 0x2f505d13, 0x711d374d, 0x572d4031, 0x81545482, 0x47502e58, 0x663b2f34, 0x665d6e5d, 0x0f100809, - 0x5d34202f, 0x05009bfe, 0xab002e00, 0x6103d206, 0x1f001200, 0x34002a00, 0x00004601, 0x07684225, 0x32013e28, 0x0e14021e, 0x83422702, 0x06222a05, - 0x1614011d, 0x32211105, 0x0f5e4117, 0x012e052c, 0x071d2327, 0x170f1d17, 0x02870e1d, 0x05820b85, 0x23110133, 0x12163311, 0x17011e17, 0x27053d33, - 0x3d270f3d, 0x8802870e, 0x11042d0b, 0x26231133, 0x3c330102, 0x23222360, 0x5f3a0282, 0x2344607a, 0x3c614423, 0x904f4f48, 0xdf014f4f, 0x2c560901, - 0xa356572b, 0x0082259a, 0x63029a26, 0x0c092208, 0x28073244, 0x85217562, 0x08230822, 0x2310880d, 0xac852176, 0x2d074c42, 0x84542828, 0x275484b6, - 0xa2525d5a, 0x04845d52, 0x9e024f33, 0x67c13433, 0x6901f1fe, 0x293d2a25, 0x5315b226, 0x4d7f4415, 0x02284dae, 0x9e0270fe, 0x43f4fe43, 0x26808840, - 0x62fd9001, 0x430c0143, 0xa53a055f, 0x5b065400, 0x2f00b803, 0x54004800, 0x00005e00, 0x0e233025, 0x2e222301, 0x8a443502, 0x083e4307, 0xe3490620, - 0x37362106, 0x3d350182, 0x21352301, 0x21012311, 0x14151632, 0x010e0706, 0x021e1507, 0x300b8217, 0x23020e07, 0x32333721, 0x013d3637, 0x2b262734, - 0x0a5d4d01, 0x04e54108, 0x4a60740e, 0x35345c80, 0x43433030, 0x2b9c7153, 0x18124a12, 0x79634d65, 0x24286379, 0x101b1b24, 0x2f01ac10, 0x01060177, - 0x1b776972, 0x1a341514, 0x11353d1a, 0x39101113, 0x68fe2f4f, 0x1e35e987, 0x6a080082, 0x33d2e935, 0xd2333737, 0x38503de2, 0x686aa26d, 0x1c3738a2, - 0x0b59631c, 0x493b0b2b, 0x7588757f, 0x130b0a7f, 0x271e1d14, 0x41fe7541, 0x62764603, 0x1515422f, 0x01070314, 0x25242f16, 0x2d2c3235, 0x1b752541, - 0x3429331c, 0x33711c1b, 0x33302730, 0x05000000, 0xab003500, 0x6103cb06, 0x46001f00, 0x60005300, 0x00006600, 0x44262225, 0x362c131b, 0x17011e37, - 0x21060706, 0x27012e22, 0x36232182, 0x46013e37, 0x14820506, 0x1632072a, 0x33033e17, 0x15021e32, 0x08105843, 0x352135a8, 0x33371236, 0x23153311, - 0x11332515, 0x01010e23, 0x2696834e, 0x58416949, 0x115b2176, 0x594d3b48, 0x4c3d4d59, 0x0e3a0f12, 0x013c3d21, 0x3c5738fc, 0x232c1011, 0x90295523, - 0x212d2d39, 0x0d161722, 0x0b010402, 0x2030251c, 0x1d35482a, 0x35573e21, 0x7541413b, 0xa1024242, 0xb52dc9fe, 0x5b5b8e2e, 0x07dbbefe, 0xb1ac8e23, - 0x588154a9, 0x324b4d2e, 0x5d6a3930, 0x3f6a5d66, 0x09230931, 0x24292949, 0x3d303044, 0x36368049, 0x27291c53, 0x2d292826, 0x0101362d, 0x0f1b2315, - 0x2f4d371c, 0x213c5333, 0x0d3d3e58, 0x04843e3d, 0x834d2f08, 0x2a014a5d, 0x553afe4a, 0x6301d883, 0x0800ed3b, 0x08ff4b00, 0x1505b506, 0x16000c00, - 0x28001c00, 0x68005c00, 0x9b007200, 0x30170000, 0x35443011, 0x11012112, 0x3323ec83, 0x87152111, 0x06a64501, 0x4c2d754c, 0x232406b4, 0x03133313, - 0x0f2a0b85, 0x3e253301, 0x0e233701, 0x02820701, 0x02821382, 0x011e2327, 0x11231117, 0x05af4c33, 0x1e823320, 0x33290282, 0x01b02311, 0x2c2c5609, - 0x07564458, 0x66ca0130, 0x850186f4, 0x0301e1fe, 0x1f01fdfe, 0xdd4be7fa, 0x0925210c, 0x2207dd4b, 0x4b0c2e0c, 0x300811dd, 0x09220938, 0x082309e1, - 0xca81c964, 0x030e04f1, 0x040e030d, 0xd002b03b, 0x0b010402, 0x16071f08, 0x57151657, 0x081f0815, 0x0105010b, 0x48127862, 0x200b8212, 0x08038209, - 0x12471254, 0x02f86277, 0xc233349f, 0x01f1fe67, 0x3e292669, 0xbcfd2529, 0xbbfd9f02, 0x5a9f025a, 0x5acf58c4, 0x35395703, 0x2a0b2b0b, 0x2f2c6e2c, - 0x0208020c, 0x512c2d14, 0x3431605d, 0x240a290a, 0x2a342d25, 0x020b1616, 0x200a0308, 0x293f1717, 0x1f0b6964, 0x02821f7c, 0x61334082, 0x4111e001, - 0x11411010, 0x4c1390d3, 0x134b1314, 0x822db62d, 0x49132202, 0x290b8213, 0x9f0263fe, 0x16279d27, 0x02821658, 0xfd2c0882, 0x00000061, 0x00290004, - 0x03d706ab, 0x22055347, 0x4746003a, 0x40471a53, 0x011d2611, 0x33161714, 0x07044332, 0xdc412520, 0x2921080a, 0x655e2001, 0xadb35d66, 0x292e2e29, - 0x840f03ad, 0x69492695, 0x3b3b5741, 0x0f3d0f22, 0x4e3b4811, 0x3500822c, 0x134c3c4e, 0x220f3a0e, 0x2d013d3c, 0xbffeae01, 0xddfe2301, 0x05474101, - 0xa9b13410, 0x2d598154, 0x084b2627, 0x39300822, 0x665d3535, 0x4235355d, 0x0b2a07c6, 0xba609e02, 0x0060c460, 0x5f510300, 0x01082e82, 0x0502ff6d, - 0x001a0593, 0x00550044, 0x2991826d, 0x00ab009e, 0x00e300d5, 0x58472500, 0x22232c05, 0x012e0706, 0x3e373627, 0x44363701, 0x0722051b, 0x8f52020e, - 0x07062109, 0x232a0182, 0x27022e22, 0x16171637, 0xd6481e17, 0x01352109, 0x37066444, 0x33023e37, 0x06101632, 0x023e3227, 0x2e34013d, 0x010e2202, - 0x011d0607, 0x8205a14f, 0x35022525, 0x35373634, 0x2005f248, 0x4a428232, 0x15240563, 0x1415011e, 0xe1476b82, 0x4713200c, 0x25200bee, 0x07211e83, - 0x82998223, 0x26222302, 0x2d820627, 0x27222323, 0x204f822e, 0x20b08337, 0x204c8533, 0x82c98807, 0x16550873, 0x3c3e4b02, 0x4330313a, 0x0c300c17, - 0x3314130f, 0x2f2b2021, 0x0a213a50, 0x1a2f240a, 0x131a191d, 0x1f221613, 0x362c2d1f, 0x2a36472f, 0x0e0c500f, 0x2e11120e, 0x45413e1c, 0x9802473f, - 0x0e3c5c3e, 0x3c0e0f0f, 0x767d3e5c, 0x31237d76, 0x1e0e0e1e, 0x1f314531, 0x08008207, 0x31100fb3, 0x593bf8fd, 0x3c4a1f3d, 0x371e3e34, 0x29296853, - 0x3f0f0f38, 0x104a3d34, 0x3a5a3c10, 0x7942423c, 0x373d4242, 0x3a6f3b3b, 0x1652033a, 0x29564516, 0x225b3890, 0x010d2d21, 0x0e0a0105, 0x2030260e, - 0x3524242a, 0x1f11101d, 0x3836561f, 0x113c2c2b, 0x413bec10, 0x413b3a42, 0x29359941, 0x2c312e07, 0x0a2a0a26, 0x1d141516, 0x2d17090a, 0x1a202841, - 0x06192819, 0x0d0d0604, 0x25391415, 0x191a482c, 0x21140e0e, 0x143e172c, 0x0c0c1111, 0x07343a0d, 0xfe5c3634, 0x40582f6a, 0x41525340, 0xb72f5940, - 0x5cb6b8fe, 0x2c49331c, 0x34482c75, 0x24341c1c, 0x08098224, 0x1a23252e, 0x06031c1a, 0x2a47341d, 0x07115143, 0x263a4e13, 0x0d192d40, 0x20202d0c, - 0x134e3a26, 0x43511107, 0x3424232a, 0x3238571d, 0x37373213, 0x38270482, 0x2e303a01, 0x84302e10, 0x1f2a0804, 0x6b404149, 0x4e291b55, 0x365a2828, - 0x12140101, 0x0e0f1b12, 0x304c360f, 0x1d2b2a32, 0x1312221e, 0x93303044, 0x3c0e3c3f, 0x04823e3e, 0x82003f21, 0x00043700, 0x06b70078, 0x00560388, - 0x0027000f, 0x00400037, 0x11300100, 0x03842330, 0x2130352a, 0x13301530, 0x33303530, 0x1b830f8a, 0x17823320, 0x27302122, 0x81542782, 0x010e3c0c, - 0x012e3307, 0xc86cac01, 0x5b53fc01, 0x5c24015b, 0x3c2b025c, 0x266f3afb, 0x82882699, 0xfe430803, 0x411005d7, 0x4010c610, 0xc2fdf502, 0x60603e02, - 0x0158c2fd, 0xfe5858ee, 0xb5b55812, 0x70be0170, 0x7042fe70, 0xc5313a02, 0x00c53131, 0x08000500, 0xf806ab00, 0x34006103, 0x46003e00, 0x58005000, - 0x4b370000, 0x272a329f, 0x13230723, 0x17121633, 0xa1862301, 0x42081190, 0x2a7655fd, 0x230d310d, 0x40372b2b, 0x0a3a2e41, 0x62620a28, 0x36563c22, - 0x2738384c, 0x1a0d320d, 0x203a374b, 0x3c36341f, 0x3e215d65, 0x3dd0025a, 0xe46e3bfa, 0x26982689, 0x1104d7fe, 0x10c51040, 0x91280340, 0x4bac2013, - 0x0b242c82, 0x9e02b5b5, 0x210a0041, 0x108ef7fd, 0x00820020, 0x60000334, 0xa006ab00, 0x15006103, 0x4b002b00, 0x30250000, 0xaa463003, 0x0206240e, - 0x41352107, 0x052013b1, 0x2d1d8748, 0x70db3c01, 0x0a124712, 0x0b040b2b, 0x09820b2b, 0x95256d25, 0x414b0125, 0x012605c7, 0x26968384, 0x6148684a, - 0x02b72814, 0x37dd379e, 0x82279b27, 0x83088202, 0x07d641c3, 0x41480b20, 0x2acb8217, 0x00470004, 0x03b906b7, 0x820b0056, 0x002122cd, 0x183f4629, - 0x280a1b46, 0x11231101, 0x15213523, 0x074c4d48, 0x2e2e2928, 0x1402ad29, 0x0246ad01, 0x7b012507, 0xfc01c86c, 0x200f0846, 0x06ea45c1, 0x423e0221, - 0x7f820579, 0x934f0620, 0x001e3408, 0x003d0027, 0x0058004e, 0x0500007e, 0x35262722, 0x84363734, 0x33302501, 0x0607020e, 0x2005944f, 0x08954f17, - 0x011d2224, 0x944f0114, 0x0a125606, 0x964f0620, 0x010e212d, 0x3f12974f, 0x366b7a03, 0x1d0f1037, 0x3128271d, 0x3a553b8a, 0x0c0a1212, 0x51314711, - 0x6a6e2f30, 0x09fed96d, 0x2606924f, 0x0c051304, 0x4f051205, 0x14221294, 0x944f144d, 0x3a0e2217, 0x0e954f0e, 0x4242fd35, 0x3d3d3c7f, 0x2f303836, - 0x50522d23, 0x2f332a2b, 0x4f353531, 0x1a221390, 0x02821a69, 0x2211924f, 0x4f2eb62e, 0x08221592, 0x934f0820, 0x06003c0d, 0x08ff1701, 0x1505e905, - 0x17000b00, 0x4b002d00, 0x5f005500, 0x30050000, 0x82333011, 0x30212203, 0x87078415, 0x05c6500b, 0x36343533, 0x32013e37, 0x011e1716, 0x0e070614, - 0x36322701, 0x2b018237, 0x2734013d, 0x012e2726, 0x07062223, 0xbf460182, 0x11052107, 0x2412d14f, 0x11016ccc, 0x08038263, 0x4210fd22, 0x2926266a, - 0x6a262629, 0x26256b83, 0x25262a2a, 0x4026416b, 0x0c0d1617, 0x17160d0c, 0x3f282640, 0x3f220b87, 0x8348f401, 0x02f8250c, 0x60c1fd9f, 0x032b0484, - 0x2b2c2d57, 0x82545582, 0x832d2c2b, 0x83a92009, 0x1b612d08, 0x25251919, 0x252e692e, 0x1b191925, 0x562f0c8c, 0xb86e9f02, 0x01f5fe6e, 0x2e282c6a, - 0x82002c28, 0x0a0036f0, 0xf606ab00, 0x12006103, 0x3a003000, 0x4a004400, 0x22250000, 0x0640502e, 0xaf07494f, 0x33112fe6, 0x01152111, 0x4c6a422c, - 0x6a4c2929, 0x07854142, 0x5d21e09b, 0x08644966, 0x13022208, 0xac10016d, 0x5482572d, 0x2d588254, 0xa882582d, 0x602d5782, 0x25191a1b, 0x2e6a2e24, - 0x1a192524, 0x200c8c1b, 0x0e6d5055, 0x9e02c128, 0x0060c2fd, 0xcb820400, 0xb606ab2c, 0x13006103, 0x2d002200, 0xd3436600, 0x15302306, 0x03882130, - 0x01301126, 0x17323330, 0x0124e883, 0x012b0607, 0x2405ba52, 0x012b2627, 0x064d5811, 0x24077e51, 0x2e272627, 0x22028201, 0x51373435, 0x06230d80, - 0x51171415, 0x24080680, 0x0e070607, 0xa3014a01, 0x1301cafe, 0xc301edfe, 0x343441ed, 0x4a29294a, 0xed413434, 0x2e5c4bed, 0x02804b2e, 0x05de45dc, - 0x2b2a2427, 0x17414037, 0x05df4517, 0x3d111124, 0xe0453556, 0x4a2b0807, 0x1a3f3a38, 0x653c361a, 0x1f10115d, 0x02b75920, 0x60ba609e, 0x9e02dcfe, - 0x7d541515, 0x15547da8, 0x5b5e6015, 0x2f2f5b6c, 0x456b22fe, 0x17210ad6, 0x075a5117, 0x51242421, 0x2908095b, 0x142a2c2f, 0x130c0b15, 0x272f515c, - 0x1d1c1b26, 0x00040000, 0x0663008f, 0x00a90371, 0x002f0015, 0x00430039, 0x01302500, 0xec503330, 0x06535a06, 0x07020628, 0x16322101, 0xd4491415, - 0x22df840b, 0x52372123, 0x064f08a2, 0xa2012e09, 0x168dedfe, 0x360f1659, 0x380f050b, 0x3709820b, 0x2fb92f89, 0x7301c201, 0x291b7768, 0x1e191934, - 0x241b1a1f, 0x1d1c1110, 0x2106f54e, 0xf34e3c3c, 0x03633407, 0xecfe4546, 0x29c53645, 0x4525c837, 0x8c451401, 0x4e8cd2fd, 0x2a3605e4, 0x01070314, - 0x18170b0b, 0x2c323549, 0x2521202d, 0x29333775, 0xe34e3734, 0x32013409, 0xce0508ff, 0x26001405, 0x78003300, 0x9b008a00, 0x4e050000, 0x01202ec4, - 0x3241d14a, 0x2327012e, 0x33112311, 0x17011e13, 0x11331133, 0x82352123, 0x020e2604, 0x36272607, 0x08108237, 0x70021530, 0x103d5738, 0x23222c10, - 0x39902a55, 0x21222d2d, 0x010c1717, 0x1c0a0105, 0x2b1f3125, 0x221e3547, 0x3a35573d, 0x41754242, 0x3f440241, 0x884a393b, 0x34132108, 0x2206884a, - 0x4a230a0b, 0x3c080788, 0x2c1f1f23, 0x472f362c, 0x4f0f2937, 0x110e0e0d, 0x3e1d2d12, 0x47404541, 0x3408b2fd, 0x7968030e, 0x08340edb, 0x01796703, - 0x1708bc43, 0x02123e34, 0xa5565540, 0x4425f8a3, 0x493c3030, 0x0d284f81, 0x24144708, 0x361d0f1b, 0x53332f4d, 0x3e59223c, 0x3f3c0d3d, 0x3d0d3c3f, - 0x353d013e, 0x312e0729, 0x2a0a262b, 0x1414160a, 0x1709091e, 0x2028402d, 0x19271a1a, 0x0c060506, 0x3815140e, 0x1a482c25, 0x150e0e1a, 0x3f172b21, - 0x594a1015, 0x33350806, 0x4a035c36, 0xfe1b6310, 0xfe9e0200, 0x10631b8d, 0x62fd0102, 0x18f5015a, 0x02134338, 0xfd5d5c3a, 0x03005abc, 0xab001b00, - 0x6103e506, 0x5c003900, 0x00007f00, 0x0d474925, 0x22056843, 0x43262726, 0xea541069, 0x0607260a, 0x21010e07, 0x051d4722, 0x33013e23, 0x0fd05132, - 0x5b563620, 0x2722a205, 0x77541001, 0x0c320d2a, 0x43057049, 0x312c0691, 0x3c111131, 0x374c3657, 0x330c2738, 0x080b7349, 0x1f101123, 0x29025a1f, - 0x13139584, 0x42682524, 0x0f227657, 0x48120f3d, 0x58584e3a, 0x124c3c4e, 0x220f3a0f, 0x491e9e79, 0xb4430b8b, 0x2e2e2306, 0xb5432c51, 0x1717230b, - 0x11552a2d, 0x26272d06, 0xb11d1c1b, 0x404154a9, 0x4d2e2c2c, 0x51053b4e, 0x52200a00, 0x00281a9a, 0x4f000500, 0xb106ab00, 0x21092b55, 0x2b554000, - 0x21012234, 0x08028235, 0x06152128, 0x01210702, 0x22603d54, 0x22232322, 0x60796022, 0x44242444, 0x4e483c60, 0x4f4f904e, 0x0901de01, 0x572c2b57, - 0x8957a357, 0x60032e05, 0x420141fe, 0xa601cdfe, 0x0136d636, 0x2bd9544c, 0xbcfd2a08, 0x59ea015b, 0xbafe525b, 0x00070052, 0x0608ff2e, 0x001505d2, - 0x0033001c, 0x00530047, 0x0073005d, 0x37000091, 0x15300706, 0x05034c30, 0x33301132, 0x37363736, 0x07063330, 0x23171216, 0x1101012e, 0x450ae545, - 0x26230cf1, 0x49333027, 0x33200629, 0x1122cf82, 0x7d570130, 0x012e2118, 0x082df347, 0x1a42f736, 0x48046d6d, 0x7f5a5a18, 0xba32ae41, 0x92278316, - 0xad01a501, 0x2301c0fe, 0x4001ddfe, 0x73742201, 0x1b6d1b7c, 0x1b6c1b03, 0x2da41878, 0x20014afb, 0x5e65655e, 0x20075d50, 0x29144817, 0x4b412308, - 0x9f02d01e, 0x1e5cacfe, 0xce4d6d6d, 0x21e8fe4b, 0xe2fee13d, 0xbb609f02, 0x0160c460, 0x32cbcc08, 0x028232c7, 0xe2fe2927, 0x03f6fe4e, 0x0df84762, - 0x48b5fd21, 0x00212d37, 0x8f836200, 0xca000535, 0x360608ff, 0x0f001505, 0x33001700, 0x92006600, 0x4e050000, 0x062a1105, 0x26333007, 0x27012e05, - 0x494e3023, 0x30332605, 0x17011e13, 0x06e74933, 0x59302321, 0x26211063, 0x0a625927, 0x5905ad5a, 0x26230d61, 0x82332702, 0x17162142, 0x53078e53, - 0x3727069c, 0x06333736, 0x82230702, 0x27262f6c, 0x0e070623, 0xf8020701, 0x6e3afb3d, 0x704e9826, 0x31303407, 0x190230c6, 0x0208340e, 0x08db7968, - 0x67030e34, 0x445bfc79, 0x0e48057c, 0x3a2e3505, 0x62621e1e, 0x36563d22, 0x2738374c, 0x4a1a4903, 0x201f3a38, 0x0807eb4d, 0x1a90015e, 0x0d6e1a6a, - 0x1718092e, 0x0a240803, 0x790f360b, 0x090a360e, 0x31030923, 0x1c6b2323, 0x0d7a1b6e, 0x19191039, 0x10191902, 0xb6f80d3b, 0xbf0170b6, 0x41fe7070, - 0x943b0270, 0x1b349494, 0xfffd0f64, 0x8dfe9f02, 0x021b640f, 0x0361fd01, 0x0c343d57, 0x162b0b2f, 0x27313a16, 0x05060b2e, 0x35069459, 0x02321b1a, - 0x17282241, 0x2a2a2c17, 0x5c130d0a, 0x364d2f51, 0x4b830b1e, 0x2ddb3b33, 0x981e706f, 0x3cdc2b29, 0x252bdc3c, 0xa1de2594, 0x235f83a2, 0x663fea32, - 0x3f370082, 0x040032ea, 0x0eff1801, 0x0f05e805, 0x3a001b00, 0x57004300, 0x41250000, 0x21221a7d, 0xfc542330, 0x23072405, 0x8237013e, 0x0898430a, - 0x07010e27, 0x1101011e, 0x07c54333, 0x76411120, 0x82332006, 0x06b54327, 0x0ccd3608, 0x67030c32, 0x310ddb78, 0x7968030c, 0x1b7f3f03, 0x1b021b6d, - 0x24781b6b, 0x8b232491, 0x66198023, 0x671a0219, 0x8c24781a, 0xfb932524, 0x11016cd9, 0x9a260101, 0x07804326, 0x27772308, 0x1781279a, 0xfffd185f, - 0x8efe9e02, 0x02175f18, 0x2d62fd00, 0xb22c2cb2, 0x39e5392d, 0x2936db36, 0x028229a7, 0x36da363e, 0x2903e639, 0xc2fd9e02, 0x44080160, 0x32430f01, - 0xc73131c7, 0xf2fe4332, 0x00f6fe43, 0x06220082, 0xf3823601, 0xf382ca20, 0x1e00142f, 0x32002a00, 0x52004600, 0x30050000, 0x05574423, 0x06141522, - 0x1720cb82, 0x220f195d, 0x65072327, 0x0b500830, 0x03022107, 0x220e3f4f, 0x4e210302, 0xa33a0b70, 0x5a21016d, 0x18414268, 0x16791861, 0xac85165a, - 0x292f2e2a, 0x3db603ac, 0x66423bfa, 0x4010340a, 0x4010c510, 0x6e6e79fd, 0x11481171, 0x040a2b0b, 0x820b2c0b, 0x706e2409, 0x4e68016f, 0x2008099c, - 0x6c9e02f2, 0x145f485d, 0x2d2fbc2f, 0x2b5b2db5, 0x2b282e29, 0xb696fed5, 0xbf0170b6, 0x41fe6f6f, 0x06fd5070, 0x01590125, 0x4f4f014f, 0xfe240b55, - 0x02b1feb1, 0x3007ae54, 0xa9000200, 0x57066300, 0x1d00a903, 0x00003300, 0x05d34125, 0x8d510620, 0x0eb64105, 0x50051621, 0x1949050d, 0x30400808, - 0x30153033, 0x27a07603, 0x64041390, 0xc2189664, 0x17bc3335, 0x207f20a0, 0x20812002, 0x33be1796, 0xeb2d018a, 0x4f411c0a, 0x95500316, 0x63ccce41, - 0xa720ed41, 0x330127a7, 0x26014f53, 0x34d03424, 0x24340282, 0xd74fdbfe, 0x720271d7, 0x1754461e, 0x47a14802, 0x00712bfd, 0x01e71e10, 0x01063481, - 0x0502ff28, 0x001a05d8, 0x0025000f, 0x00560043, 0x537c0074, 0x5b4f11d3, 0x4e012033, 0xa6512ba1, 0x67022807, 0xfb01c76d, 0x4f417301, 0x82210770, - 0x05824f6b, 0x27416b22, 0x2509794f, 0x16402727, 0x00820d16, 0x4016162c, 0x6b41d0fd, 0x4c29294c, 0x0787416b, 0x87412621, 0x2641341c, 0x17163f28, - 0x0c0d0d0c, 0x023f1617, 0x01c86ced, 0x514c01fc, 0xfd3605cf, 0x2c2b2db7, 0x81555482, 0x2d2d2c2c, 0xa9812c2c, 0x2d2b2c82, 0xc04e1a61, 0x24252106, - 0x2105c04e, 0xcd4e2524, 0x031a2406, 0x83572d01, 0x2d58232e, 0x2c82582d, 0x2b8a5720, 0x4e09c24f, 0x1a2205f9, 0x3352e801, 0x00073608, 0x06fdfe33, - 0x000905c2, 0x0016000c, 0x0061003c, 0x009c0086, 0x19b95aa2, 0x510f8b61, 0x012014f4, 0x2106835a, 0x7f5a010b, 0x2c24a51a, 0x32161411, 0x33113536, - 0x0e071411, 0x05934d02, 0x3e5b1120, 0x09012406, 0x4a2b2c57, 0x022a0828, 0x215e3aad, 0x21232321, 0xb2612f21, 0x0c1d5205, 0x0e390e39, 0xc3026920, - 0x0b010501, 0x82081f08, 0x081f0782, 0x0104010b, 0x5a137862, 0x0729058b, 0x47120820, 0xfb627712, 0x202282c0, 0x261d820c, 0x1e088282, 0x83020c08, - 0x49122222, 0x25338212, 0x07200808, 0x22824812, 0x38640133, 0x0c65388e, 0x7f58350c, 0x0c0c3657, 0xf4663602, 0x0fc15af8, 0x27b12008, 0xb6842a2b, - 0x132b2a83, 0x4a252514, 0x39082008, 0xa1535e32, 0x3c365e53, 0x4a082109, 0x5aa8014f, 0xfe230592, 0x5a1001f0, 0x04211790, 0x0823a1ff, 0xfe9f0232, - 0x4d4d514e, 0xfeb20151, 0x3232445e, 0x411f1f41, 0x01443232, 0x0261fda2, 0x5abbfd9f, 0x04000000, 0x02ff4d01, 0x1a05b305, 0x51001700, 0x9d008b00, - 0x50059f52, 0xd54a09bb, 0x14aa5009, 0x4823414d, 0x7a4d0efe, 0x4e012029, 0x012110f1, 0x095a4685, 0x544b0138, 0x320c2a76, 0x2c2a240c, 0x17424036, - 0x280a3b17, 0x3131610a, 0xd9481110, 0x330c2306, 0x53621a0c, 0x653b2906, 0x2010105d, 0x8bfd5a1f, 0x0d233284, 0x862b2b23, 0x0a272e32, 0x11313162, - 0x36563c11, 0x2837384c, 0x2132890d, 0x3282643c, 0x5b1f1f2b, 0x310ce201, 0x7968020d, 0x230682db, 0xf2796703, 0x32069246, 0x0c343d0b, 0x162b0c2e, - 0x27313916, 0x020b1718, 0x4d120207, 0x22250d5e, 0x2c171728, 0x0b5e4d2b, 0xb1620321, 0x7e012133, 0x2f0f3c48, 0x02000000, 0x5400cf00, 0xb8033106, - 0x4a003400, 0x2010d94e, 0x05666a26, 0x4a08805c, 0x4052095e, 0x020e2207, 0x0d354301, 0x2e278708, 0x02113502, 0x34946a02, 0x2d0f3f0f, 0x50443635, - 0x25493a52, 0x3d3d7b25, 0x446b4c2a, 0x3146465e, 0x5d21045b, 0x28284746, 0x4b442021, 0x4e29747e, 0x52f90170, 0x108551c5, 0xae764b11, 0x214b3b3b, - 0x0f404d55, 0x1c360f3a, 0x313d481b, 0x06070e3a, 0x65393a17, 0x213e5938, 0x513e2121, 0x1d322a03, 0x1a34381c, 0x18100d1a, 0x613a6573, 0x54032544, - 0x6c6bfcfd, 0x04026b6c, 0x435c11fe, 0x152b5744, 0x5c865816, 0xdb82ef01, 0x07732d10, 0xff0a393d, 0x05f60608, 0x000e0015, 0x002b0018, 0x00530049, - 0x0063005d, 0x33300100, 0x2009505c, 0x067e5a2b, 0x012b2623, 0x2ce24c11, 0x3a146b5c, 0x21113311, 0xed760215, 0x25256841, 0x25252929, 0xeded4168, - 0x4b5c5c4b, 0x5b49fe80, 0x013c3a98, 0x2a292ba7, 0x2a7ea77e, 0x5f602b29, 0x5f5b6b5b, 0xf70221fe, 0x5582572d, 0x2d578254, 0xa9200782, 0x61290682, - 0x2519191b, 0x2f692f24, 0x07bf4c24, 0x825c0c88, 0xc0fd2e0e, 0xc2fd9f02, 0x00050061, 0x06ab0003, 0x0aa756fd, 0xa7565920, 0x4c052034, 0x082024bd, - 0x6b11c056, 0xa4200dae, 0x2006c056, 0x05744c96, 0x4c830721, 0x61200774, 0x560f0067, 0xa73a2bd4, 0x13134c13, 0xeffe124b, 0x4a121101, 0x134a1312, - 0x9e0263fe, 0x16279c27, 0x02821659, 0xfd220882, 0x00820062, 0x22000331, 0xde06ab00, 0x1b006103, 0x6c003300, 0x53130000, 0xe7561c41, 0x14114c15, - 0x59012e21, 0xbc5c1052, 0x53d7200f, 0xdb28054d, 0x030c320c, 0x40017968, 0x200abc56, 0x2fb85c4c, 0x4b2a0221, 0x18240a21, 0x62fd0102, 0x6e09a14b, - 0xb55c080f, 0x00250827, 0x7c010700, 0x840502ff, 0x37001a05, 0x68005b00, 0x9c007500, 0xd500a900, 0x30010000, 0x07010e21, 0x3e373633, 0x07886602, - 0x0607012e, 0x2e272223, 0x27262701, 0x1e171637, 0x15791782, 0x06222205, 0x23148207, 0x2137013e, 0x663a5e66, 0x246b07bf, 0x011e220d, 0x17246b33, - 0x82352121, 0x05296071, 0x22232624, 0x8383020e, 0x02233c82, 0x82171632, 0x06c97901, 0x3708c282, 0xfe260321, 0x020c03d7, 0x0d0d0b06, 0x2b1c2b20, - 0x101f364b, 0x2c2d3d10, 0x23232d39, 0x10131434, 0x0d0e0c4f, 0x3b1c2b23, 0x2b3b4141, 0x3c0f1234, 0x0411040f, 0x69017c01, 0x37245d66, 0x17fe3b3b, - 0x103c5837, 0x23232c11, 0x38902955, 0x22212d2e, 0x010c1716, 0x21097d6b, 0x7d6b221e, 0x41590806, 0xfe500341, 0x248f2339, 0x3a1b1b33, 0x1f291c32, - 0x3f100614, 0x12120b10, 0x50664c38, 0x0f0e1c1b, 0x1e1d2d19, 0x1a691a20, 0x4e015201, 0x14289e27, 0x0c171010, 0x314e361c, 0x3d2a2a32, 0x0a0a1011, - 0x18151621, 0x1110143e, 0x3a410d19, 0x21413a0b, 0x02090214, 0xfd3ef93e, 0x374e6657, 0x6bd10121, 0x262d0be6, 0x2e282827, 0x0101352d, 0x101b2314, - 0x12e66b1d, 0x20683f08, 0x2a2e207f, 0x310c2e2a, 0x271c1138, 0x06190616, 0x2e1d1d20, 0x1b1a1e1b, 0x282b2524, 0x1c1c3e45, 0x175a171c, 0x06000000, - 0x02ff7a01, 0x1a058605, 0x36002800, 0x92006c00, 0xca009f00, 0x40680000, 0x263f680c, 0x06212522, 0x24188342, 0x1e37013e, 0x06345f03, 0x22073869, - 0x5e252113, 0x6b6d14a7, 0x35212219, 0x06886136, 0x201e4542, 0x05456852, 0x225a3935, 0x030d2c22, 0x0e0e0a04, 0x2b1f3026, 0x1e342424, 0x681e1111, - 0x45080544, 0xec10103d, 0x3b41413b, 0x0342423a, 0x05d6fe34, 0x0d0b070c, 0x1d2a210c, 0x1f364a2b, 0x2d3e0f10, 0x222d392c, 0x14133523, 0x0d340e10, - 0x2b221b0d, 0x40403c1c, 0x12342b3c, 0x070f3c0e, 0xfd7c0112, 0x3c583810, 0x23821110, 0x9029552c, 0x212e2d38, 0x0c171622, 0xc76d0303, 0x05cd5e07, - 0x75414122, 0x25054942, 0x1b336b6b, 0x48423b1c, 0x400f2105, 0x2e074842, 0x0f0e1b1c, 0x1d1e2c19, 0x1a691b20, 0x68b35201, 0xf2222d30, 0x7542a449, - 0x2a0a2414, 0x4221140a, 0x74230b76, 0x42b90101, 0x5f212d3b, 0x243a4260, 0x00060038, 0x0608ff39, 0x001505c7, 0x001c000f, 0x00320028, 0x007a0057, - 0xd1580500, 0x13303407, 0x12163330, 0x27260317, 0x07062330, 0x33300730, 0x6e01012e, 0x99531553, 0x1325750f, 0x25147353, 0x011e1716, 0x88443317, - 0x23113c05, 0x240a1e04, 0x2406e206, 0x82c9640a, 0xf1218721, 0x0b0c0b0a, 0x0bb13c0a, 0x6e9bfc29, 0x30080d30, 0x5d3bad02, 0x24242121, 0x2e2f2121, - 0x3433553b, 0x114a0d1f, 0x4c44343f, 0x4335444c, 0x0e380e12, 0xc4026a1f, 0x0c010501, 0x82042109, 0x09220382, 0x3e0c820c, 0x37367862, 0x08052109, - 0x52051718, 0xf8627715, 0x18187f23, 0x9f02237f, 0x7041fe70, 0x8231e001, 0x24d32400, 0x53a5018c, 0x07211d4e, 0x124d5329, 0x0952163f, 0x1001f0fe, - 0x14165108, 0x63fe124b, 0x75769f02, 0x420e5d19, 0x2fb10b42, 0x000061fd, 0x08674107, 0x2000162c, 0x35002a00, 0x64003f00, 0x077f8700, 0x07062308, - 0x1c6c3215, 0x710e2006, 0x362305a5, 0x7134013d, 0x01200ca3, 0x5613ff73, 0x9b760578, 0x2275411e, 0x98022208, 0x504e1001, 0x171d3437, 0x090a2218, - 0x22392816, 0xa766d5fe, 0x25262625, 0x252197a7, 0xfd972125, 0x507d413b, 0x59a7013f, 0x05484154, 0x280b0b0c, 0x2b211c1c, 0x5a1c3347, 0x2c2c2b27, - 0x28245727, 0x0123292b, 0x5185411d, 0x05000034, 0x02ff2701, 0x1a05d905, 0x34002a00, 0x71003c00, 0x14827d00, 0x24095973, 0x3233023e, 0x19717616, - 0x5410e76a, 0x5e650e7b, 0x0d5d6508, 0x5b0d7c5d, 0x02210a0c, 0x1f8676f4, 0x3c730226, 0xe56f3afb, 0x2607ed6b, 0x10c61140, 0x76b6fd40, 0x5d6b12ea, - 0x5e642613, 0x015a3f21, 0x09d3542e, 0x96768c20, 0x2d592107, 0x2e089676, 0x10080865, 0x341f2f10, 0xb69bfe5d, 0x5b9e02b6, 0x4e200b34, 0x220a5154, - 0x76020b2f, 0x82540ef3, 0x515c2509, 0x1d374d2f, 0x3d09fd6f, 0x01020000, 0x0516ffce, 0x000e0432, 0x003b0021, 0x23300500, 0x35262722, 0x012e3530, - 0x08822627, 0x2f05b042, 0x17161732, 0x1415011e, 0x33150702, 0x013e3227, 0x22059874, 0x6f23022e, 0x28080843, 0x7004021e, 0x2728558e, 0x2f2f8855, - 0x383e191b, 0xc4505039, 0x38395050, 0x9aa0bb3e, 0x44603af0, 0x12131312, 0x3b3a6044, 0x3109855f, 0x2b2cea5f, 0x4d0b4c49, 0x5c5b4141, 0x42c27f73, - 0x00822241, 0xc2424134, 0xfcfee17f, 0x28f2671c, 0x4537374c, 0x3737459e, 0x0a8a284c, 0x00030029, 0x06b70013, 0x825603ed, 0x003d2207, 0x05616751, - 0x5f610320, 0x06944b0f, 0x03300322, 0x23075358, 0x11302330, 0x230f2b6c, 0x21302311, 0x35220182, 0x05840130, 0x30212308, 0x07020615, 0xcfe33021, - 0x175d1772, 0x175b170d, 0x228a2270, 0x05010903, 0x1e080c01, 0x08828208, 0x9744071f, 0x49123605, 0x08200712, 0x081f0808, 0x77124812, 0xfe9a0262, - 0xfe410141, 0x067662ce, 0x0301b732, 0xc8329b01, 0x32c83131, 0x45eefe44, 0x9d01fdfe, 0x6221874b, 0x0028097d, 0x4c000400, 0xb406ab00, 0x1426e382, - 0x28001e00, 0xe5835f00, 0x421ddb5d, 0x112107a6, 0x32994a01, 0xe45db920, 0x60192106, 0x2408e45d, 0x02ac2a2e, 0x07566813, 0xfe580327, 0x030c03d7, - 0x23244a07, 0x4a0e1321, 0xb7200624, 0x270eec5d, 0x2b292e28, 0x0296fed5, 0x2a065a68, 0x279e2840, 0x17101014, 0x49351d0c, 0x11210fc5, 0x0ec54910, - 0x00820020, 0x73010430, 0x8d0502ff, 0x4c001a05, 0xb4007d00, 0x1772e000, 0x4a172017, 0x072106e2, 0x22018406, 0x4a011e15, 0x0d8405f2, 0x22230622, - 0x21051157, 0x17823727, 0x4b0c2272, 0x27210a32, 0x06596b26, 0x2306364b, 0x32363701, 0x54855586, 0x07010e23, 0x33684121, 0x648a0120, 0x211d5149, - 0x92675002, 0x28620811, 0x101d1d28, 0x120a0a11, 0x1a171812, 0x1313331d, 0x11110b0b, 0x2c2c1f20, 0x24232f36, 0x15141c1b, 0x1c0d4f0f, 0x1d2d1211, - 0x4045423d, 0xfe800347, 0x238f2439, 0x1d1b1c33, 0x2a1b321e, 0x0a0b0f0f, 0x0f401005, 0x3713110b, 0x51662626, 0x0e0e1c1b, 0x1d161619, 0x691a201e, - 0xfd52011b, 0x03d6fea7, 0x9349030b, 0x1e373508, 0x2c3e100f, 0x222d392d, 0x14143423, 0x0d0d4e0f, 0x1b2c220e, 0x21069249, 0x074c3b0f, 0x50022405, - 0x4b2438fe, 0x2a2107b3, 0x08b34b1e, 0x5e854b20, 0xb34b1820, 0x10357208, 0x0b0c2708, 0x1f211617, 0x191a2029, 0x0d0c1414, 0x1a060406, 0x1c1d1415, - 0x24242c25, 0x0e0e191a, 0x11110a0a, 0x3e171615, 0x3d722214, 0x4b752009, 0x1c260961, 0x0e0e111c, 0x644b1314, 0x0e0d2107, 0x2305654b, 0x1f1f4429, - 0x2105664b, 0x224c4605, 0x4b62202b, 0x042028bb, 0x2806274f, 0x000f0056, 0x0337002e, 0x135b5e8f, 0x26243f6f, 0x27022605, 0x18011d33, 0x18223546, - 0xc9158a45, 0xa65fa638, 0x4a461826, 0x18152019, 0x22153d46, 0x183d3701, 0x94147745, 0xb3451814, 0xa6119111, 0xcc26a638, 0x3335215f, 0x103f4618, - 0x663a0128, 0x01c901b2, 0xf6706b06, 0x27282105, 0x2312f670, 0x5f18eb01, 0x4f034618, 0x0e03012d, 0x0e040c04, 0x0e380e04, 0x82390e7c, 0x04461807, - 0x64012852, 0x7a186219, 0x820f390f, 0x040b3a6b, 0x3d0f030e, 0xfdfc020f, 0x594502bb, 0x42b0fd59, 0x3d3c7f42, 0x2f38373c, 0x077a7130, 0x71343021, - 0x4a200a7a, 0x03da4510, 0x00002f55, 0x47000500, 0xb906ab00, 0x1e006103, 0x0b822f00, 0x70005828, 0x0e010000, 0xd76c0701, 0x05c67a0a, 0x14823320, - 0x7b06d96c, 0x0520243e, 0x013925a4, 0x103d0f10, 0x10046c6c, 0x791e1040, 0x9f287e1e, 0x2bad2b28, 0x02882283, 0x18d27a63, 0x07060627, 0x0231100f, - 0x08209e52, 0x12f00122, 0x02cf1246, 0x15acfe9e, 0x91251451, 0x2fbc2f24, 0x41fefe41, 0xf0fed134, 0x4040592e, 0x41405253, 0x3506797a, 0x2b49341b, - 0x34482c76, 0x24341b1b, 0x2b762c24, 0x1a1b2325, 0x24a35c1b, 0x06000022, 0x3a08934e, 0x0042002f, 0x008d0063, 0x00a00098, 0x21300500, 0x37303530, - 0x35363736, 0x49343530, 0x362a0adb, 0x32013e37, 0x1617011e, 0x814a1415, 0x0e072505, 0x05210701, 0x21052a56, 0x1d823435, 0x7c363721, 0x3d830693, - 0x2105554d, 0x01822726, 0x2f852220, 0x2305dd75, 0x01161716, 0x08514218, 0x2022f74e, 0x0cf27912, 0xfe450324, 0x1153d639, 0x055a5509, 0x4c1c1c2c, - 0x0e375066, 0x160c0d0f, 0x13531d17, 0x49012606, 0x3c2e2e3f, 0x0800820e, 0x2e2e3c23, 0x76767c3f, 0x1819227c, 0x06070f10, 0x100f0706, 0x18451918, - 0x06100f19, 0x10060707, 0xfe18190f, 0x200e4f89, 0xfb3d7426, 0x72726e3a, 0x2c0c0f4f, 0x2ebf68f2, 0x0c2f292a, 0x1d113831, 0x06c34926, 0x1b17172a, - 0x2524351e, 0x2223282b, 0x2207c349, 0x7c181767, 0x1733072e, 0xb8feb718, 0x0e0e5cb6, 0x24231a1a, 0x242c752d, 0x821a1a24, 0x1a0e29a1, 0x2c24231b, - 0x23242d75, 0x03210e83, 0x1f2d4f77, 0x014f0123, 0x0a2f4f4f, 0x00002008, 0x0b010600, 0xf50568fe, 0x1f000f05, 0x41003900, 0x55004b00, 0x00006f00, - 0x22233001, 0x4e013d26, 0x062014fd, 0x671afd4e, 0x012007f6, 0x20128777, 0x65411805, 0x112e080d, 0x13331123, 0x33373633, 0xe5022311, 0x3835395f, - 0x11201f5b, 0x25262a12, 0x36823536, 0x29262635, 0xa1676b7c, 0x0d2d4027, 0x2d0d0c0c, 0x09822740, 0x0d0d0c2d, 0x02402d0c, 0x01c76ded, 0x793bfbfb, - 0x042b0cff, 0x23090515, 0x18621909, 0x82196218, 0x67053908, 0x5a06b480, 0xfe677b59, 0x32313a68, 0x2b2b3407, 0x554c3e3d, 0x172b2c81, 0x2b3d0082, - 0x9655812c, 0xa24513ad, 0x2525331a, 0x252e6a2d, 0x1b1b3324, 0x2e252433, 0x25252d6a, 0x08ec6733, 0x76240121, 0x292d09e2, 0x3e2b282e, 0x2c124912, - 0xb32d2db3, 0x3908822c, 0x9e02fffd, 0xaaababfe, 0x000062fd, 0x85000200, 0x7b065400, 0x3400b803, 0x7f644d00, 0x131a4135, 0x33133d08, 0xb7012311, - 0x10359469, 0x362c103e, 0x51514435, 0x25264939, 0x2a3e3d7a, 0x5f436c4c, 0x5b314546, 0x465d2103, 0x20272848, 0x7e4a4421, 0x714e2975, 0x0b06fa03, - 0x81130b2c, 0x13812423, 0x06270882, 0x07e1a182, 0x64829ae0, 0x022a2d8c, 0x175a178f, 0x4040ec22, 0x088222ec, 0x037ffd38, 0x0156fe46, 0x00bafcaa, - 0xb5000300, 0x4b066300, 0x0b00a903, 0xad7b1500, 0x01092119, 0x3c11d576, 0x746801b6, 0xe1747f7f, 0x393934d8, 0x5a03d834, 0x178cedfe, 0x370a1758, - 0x380f050f, 0x3409820a, 0x2eba2e88, 0x89460363, 0xb2fe89e6, 0x3236c501, 0xfd36323a, 0x05a17631, 0x37c82522, 0x200aa176, 0x50008200, 0x6120071b, - 0x2006bf45, 0x46bd457e, 0x0834ce4e, 0x3d101521, 0x036d6d0f, 0x1f104010, 0x287f1e78, 0xac2b27a0, 0x8722832b, 0x5c3f6302, 0x0e0e0f3b, 0x445c3b0f, - 0x31280543, 0x1f0d0d1f, 0x1f314531, 0x22053b44, 0x501a0331, 0xdf453250, 0xef01213e, 0x3a2c7150, 0xff5b0105, 0x05a50502, 0x0034001a, 0x0048003e, - 0x00a20081, 0x26220500, 0x79373627, 0x3f54079f, 0x07022510, 0x2223012e, 0x180f3f54, 0x8914654b, 0x29016945, 0x570d5976, 0x162a0eb1, 0x02010e17, - 0x2a765550, 0xc77f2625, 0x1a0b2314, 0xc87f091e, 0x2d01210d, 0x35054544, 0x2e2aacb3, 0xfeac2a2e, 0x2b76542c, 0x2a242526, 0x4240372b, 0x40691618, - 0x190d2111, 0x760e4069, 0x21080574, 0x41692425, 0x2d227558, 0x3b48112e, 0x4e58584e, 0x0a134b3d, 0xfd79224d, 0x2323343d, 0x3916162b, 0x8c542731, - 0x160a230e, 0x8d54081b, 0xbf4b1812, 0x2e292507, 0x18012b28, 0x50693d89, 0xb12a0826, 0x404055a9, 0x4d2d2c2d, 0x3019194b, 0x665d6a39, 0x313e695d, - 0x52492f06, 0x04000000, 0xb7002e00, 0x5603d206, 0x27000f00, 0x41183600, 0xd55e15e7, 0x61132015, 0x6225182d, 0xfc01c86c, 0x09ad5570, 0x40ed832b, - 0x28252569, 0x69252528, 0x05f66040, 0x41188120, 0x022009e8, 0x2705bb70, 0x2a2a9e02, 0x7da87d2a, 0x60270582, 0x5b6c5b5e, 0x8222fe5e, 0x06002d9e, - 0x02ff4b01, 0x1a05b505, 0x1e001500, 0x4930ab82, 0xd8016e00, 0x30050000, 0x30213035, 0x37123635, 0x2e053d74, 0x30233015, 0x30253015, 0x30113033, - 0x5e010e23, 0x3e200714, 0x08d34418, 0x3d480120, 0x012e2307, 0x197e0622, 0x021e2407, 0x6a262201, 0x816d0800, 0x06072206, 0x8146181d, 0x33352b0e, - 0x010f2311, 0x150e0f15, 0x05840d0f, 0x0b910882, 0x012e0527, 0x3f353727, 0x2002840b, 0x8805840a, 0x9306200b, 0x33073c17, 0x02153311, 0x2dc9fea1, - 0x5c8e2db5, 0xdbbffe5c, 0x028e2307, 0x3c5c3eaf, 0x181d0f0e, 0x2009bd44, 0x26008207, 0x45310f0f, 0x820f1031, 0x1f073909, 0x83eefd31, 0x68492796, - 0x3b3b5841, 0x103c0f21, 0x4d3b4811, 0x2c2d2d2c, 0x0ad84918, 0x08bc2f22, 0x2e3b4c18, 0x0b222eeb, 0x6ed20b2c, 0x53d20220, 0xa6010124, 0x4a18f2a3, - 0xfe290ea5, 0x40582fe2, 0x83515241, 0xaa451858, 0x25243b07, 0x252b752c, 0x1c1a1a24, 0x241a1a1c, 0x2c752b25, 0x1c332425, 0xa9b10603, 0x48188055, - 0x342c0f61, 0x2309313e, 0x29294909, 0xf5015a0b, 0x54d0b1d4, 0x0a280a22, 0x508953d0, 0x8655b641, 0xbcfd235f, 0x0082005a, 0x00023508, 0x065f025c, - 0x007105a4, 0x001b0003, 0x15211300, 0x37270121, 0x15333517, 0x33071737, 0x07172315, 0x35231527, 0x23372707, 0x48065c35, 0xc204b8f9, 0x3c8c2a8c, - 0xc6210382, 0x240887c6, 0x0246a502, 0x84118c22, 0x0000261a, 0xff5c0001, 0x225b8225, 0x821400d6, 0x32380859, 0x053e012c, 0x2e341135, 0x23022c03, - 0x34019d5c, 0xc7f60501, 0x2e5b7fad, 0xccaa6f3c, 0xe5fef8fe, 0x02ada9fe, 0x725633a5, 0x667a837e, 0x4ff90e47, 0x907d5b15, 0x3e688691, 0x0124478d, - 0x2e012c22, 0x3e204783, 0x33274782, 0xfe9da406, 0x89fbfecc, 0x08012548, 0x57011b01, 0xef824896, 0x2a000532, 0xd60638ff, 0x04009405, 0x44004000, - 0x08014b00, 0x302b9b82, 0x04210721, 0x07030e22, 0x5707040e, 0x352e06eb, 0x32333634, 0x32041e17, 0x3e37033e, 0x0c833704, 0x16171624, 0x1e831415, - 0x03032e39, 0x01211521, 0x25052315, 0x34253523, 0x3233023e, 0x32361716, 0x8217031e, 0x06142126, 0x36210583, 0x25058332, 0x37013e17, 0x4782033e, - 0x40843620, 0x1e222883, 0x0c863101, 0x33231784, 0x83021e32, 0x215b8529, 0x61851415, 0x2e272629, 0x35343501, 0x8327022e, 0x010e2e6e, 0x0e272223, - 0x22060701, 0x0e070627, 0x830c8603, 0x82022006, 0x83b0840e, 0x21038626, 0xba82022e, 0x34273b08, 0x0e35010e, 0x27262701, 0x35161726, 0x37062734, - 0x23363734, 0x015c2622, 0x62fe46e4, 0x6154e004, 0x0f2f4f50, 0x403c1507, 0x4b522c5b, 0x354da793, 0x1b151d08, 0x4f2f0f0f, 0x1b9a6150, 0x01ddd408, - 0xfe62fee4, 0x00019610, 0xfc960001, 0x240e047a, 0x0b1b131a, 0x0c171f0e, 0x11020208, 0x0211151b, 0x38040706, 0x1010072e, 0x10020c0e, 0x0c060308, - 0x090a0c13, 0x39530b06, 0x2c040d13, 0x180c1128, 0x0c2f411e, 0x040d151d, 0x13241a0e, 0x1e271420, 0x0a11140c, 0x2f090909, 0x012c2149, 0x0f121a13, - 0x06100418, 0x1425210f, 0x192a100f, 0x2410171c, 0x0d210912, 0x1a132e0e, 0x0718133b, 0x380d0607, 0x1b0e2909, 0x0d111a14, 0x07122f34, 0x11191008, - 0x10160b0a, 0x171c1010, 0x0c22180f, 0x1108221a, 0x01030d16, 0x27061716, 0x01021a03, 0x03011008, 0x07080201, 0xb9021a12, 0x422fbb6e, 0x09173c52, - 0x4b424a1d, 0x4ea72d18, 0x150f0d4d, 0x3c17161d, 0x972f4252, 0x01900818, 0x25016e58, 0xfaf0f0fa, 0x291d0da0, 0x071b151a, 0x111d110e, 0x3a26170e, - 0x0d0c020b, 0x37050507, 0x100b0910, 0x0c080409, 0x41040d67, 0x373b1f27, 0x070e1860, 0x17121d11, 0x0f150926, 0x43520110, 0x12043804, 0x08340105, - 0x0c101711, 0x030b122d, 0x423e0804, 0x1d2f070b, 0x450b2411, 0x1c141110, 0x0e1d0f33, 0x4d0a5d09, 0x0c18180c, 0x17022d41, 0x08172116, 0x1b0c1105, - 0x05222614, 0x0319220e, 0x0c020717, 0x0502030c, 0x02041102, 0x01040202, 0x02240382, 0x00171a07, 0x0807b343, 0x0b004925, 0x00000f00, 0x15331501, - 0x35231523, 0x35333523, 0x21152101, 0xb4b49805, 0xfbb4b464, 0xf9480628, 0x834905b8, 0xb464380b, 0x00465cfd, 0xff530005, 0x058c063e, 0x000600a7, - 0x002c0028, 0x82370030, 0x3311293d, 0x1133010b, 0x27072327, 0x03200183, 0x0b260584, 0x021b3502, 0x01821737, 0x05841320, 0x21331729, 0x022b1533, - 0x85373335, 0x042e082d, 0xcecea0a1, 0x4ac936a0, 0x2f303f33, 0x373a3a3f, 0x36103119, 0x37272737, 0x19311036, 0x3f3a3a37, 0x333f302f, 0xf201c94a, - 0x73bc8a8a, 0x2c838873, 0x08024708, 0x2201b001, 0x50fedefe, 0xa93ed63a, 0xfee3cd72, 0x6a8b54fc, 0xd4fe92df, 0xeb010401, 0x01eb0189, 0xb7affe04, - 0x548b6adf, 0xcde3fcfe, 0xd63ea972, 0xfe3a6161, 0x01defe50, 0x00b00122, 0x5c000300, 0xa40637ff, 0xbb82e005, 0x3b000d22, 0x2326b783, 0x11230109, - 0x06841103, 0x012f0122, 0x0585bf85, 0x0f840f20, 0x1f020f22, 0x3f23c482, 0x85011f01, 0x280586c4, 0x01c83403, 0xc8140114, 0x3d068598, 0x7238d802, - 0x484b604e, 0x545a5a60, 0x54184b27, 0x4b394b45, 0x39155a36, 0x15392525, 0x0c82365a, 0x18544531, 0x5a54274b, 0x4b48605a, 0x01724e60, 0x84fcfe5d, - 0x02042294, 0x83b9825d, 0xfc3008d1, 0x381bc8fe, 0x512d4204, 0x37216659, 0x8448582a, 0x4b645a70, 0x438f523b, 0x8f4a656c, 0x644b3b52, 0x4884705a, - 0x21372a58, 0x2d515966, 0x00380442, 0x023b0082, 0x22ff4b00, 0xd005b506, 0x2a000900, 0x15050000, 0x2115012d, 0x35010d35, 0x8b171301, 0x8d3f20ad, - 0x070324d9, 0x836d0103, 0x2604227e, 0x087a8201, 0x4ed3fa26, 0x621f6d6e, 0x75756d32, 0x7d615e7d, 0x49499466, 0x617d6694, 0x75757d5e, 0x1f62326d, - 0x3e4e6e6d, 0xa0cecea0, 0x33080383, 0x7efe5403, 0xb073eccd, 0xcc426e54, 0x8459a1b2, 0x35369931, 0x59843199, 0x42ccb2a1, 0x90b0546e, 0xfecd0901, - 0x0200007e, 0x6aff5e00, 0x7b05a206, 0x1f000b00, 0x300e5b42, 0x27032703, 0x1127020b, 0x37021b37, 0x21133713, 0x06694215, 0x87c7fe21, 0x21868774, - 0x7742a702, 0xf0fc3307, 0xfe629dfe, 0xbefeb2f7, 0x67fe6501, 0x84070584, 0x098267fe, 0xfeb2be2f, 0x9dfe62f7, 0x00030074, 0x063eff53, 0x069342af, - 0x00002f2c, 0x0d352113, 0x03213501, 0x07412115, 0x05804205, 0x35021b22, 0xa0427982, 0x03072109, 0xcb202685, 0x22075442, 0x42450355, 0x3b21068c, - 0x0b8c4236, 0x423b3621, 0x9620068c, 0x28074b42, 0xcea00304, 0xfcfea0ce, 0x26864261, 0x839bfe21, 0x8200202f, 0x00042800, 0x065effc8, 0x82a60538, - 0x000f2bb5, 0x001f001b, 0x21352500, 0x7f430315, 0x2e0b8b0b, 0x04112311, 0xb4cc016c, 0xb464b4b4, 0x84c0fcb4, 0x31022706, 0x6464a05a, 0x95439c04, - 0x5cfe2105, 0x02250785, 0x06b8f90e, 0x21628248, 0x8f470100, 0xa5022105, 0x002c6382, 0x21152113, 0xf948065c, 0x46a502b8, 0x053a1c82, 0x08ff5601, - 0x1505aa05, 0x39000f00, 0x65004500, 0x00008f00, 0x11333505, 0x715e0623, 0x11332a05, 0x14011533, 0x07020e07, 0x058b5523, 0x82012e21, 0x020e211a, - 0x12525018, 0x50180420, 0xe84b0a86, 0x0d146308, 0x1e373628, 0x07061701, 0xce672506, 0x2726210f, 0x3210136a, 0x15210706, 0x07bc9c01, 0x2c0b3765, - 0xa56e3d0b, 0x671c02a3, 0x382c05da, 0x2d22215b, 0x0104020d, 0x250e0f0a, 0x12205018, 0x75d9fe32, 0x41754242, 0x958456fe, 0x41694926, 0x5b227657, - 0x1806104e, 0x0808aa52, 0xfee2025f, 0x3307cf39, 0x323b1b1c, 0x151e2a1b, 0x0a332c05, 0x4c371312, 0x1b1c5066, 0x2c190e0f, 0x4f201e1d, 0xf852014f, - 0x6bf5015b, 0x0a270a3b, 0xbcfd7743, 0x49a5015b, 0x556b4140, 0x284d2a1b, 0x01365929, 0x12121401, 0x0e0f101a, 0x31304c37, 0x1e1e2a2b, 0x44131221, - 0x3e923030, 0x3f3c0e3c, 0x3104823f, 0xa9b2ca01, 0x2d588154, 0x2f314b4d, 0x655d6a39, 0x8b4a6a5d, 0xb8682f08, 0x2a292f07, 0x38310c2e, 0x16271d10, - 0x9f691411, 0x1d1b390d, 0x5c43441c, 0x04000000, 0x08ff2e01, 0x1505c705, 0x35001300, 0x86004d00, 0x23130379, 0x30013011, 0x2310b552, 0x13303330, - 0x11220386, 0x21822330, 0x6d15514e, 0x01213727, 0x088e612e, 0x0905c437, 0x62180923, 0x18621819, 0x05082409, 0x06b38168, 0xfc687bb4, 0x3b3a6d2b, - 0x9f02f832, 0xfe60bb60, 0x120102dc, 0xb32d1248, 0x2db32c2c, 0xfd2b0882, 0xfe9f02ff, 0xfd5501ab, 0x84620361, 0x61c32220, 0xc441180b, 0x7817200a, - 0x450805ec, 0x242c515c, 0x1a1b3124, 0x2d0b321b, 0x2e28220b, 0x15152a2c, 0x5c130d0a, 0x27262f51, 0x001e1b1b, 0x006b0001, 0x049506d3, 0x00440031, - 0x16320100, 0x0e071415, 0x23060702, 0x27052e22, 0x23262726, 0x07060722, 0x1083010e, 0x35264508, 0x37363734, 0x3233063e, 0x1617051e, 0x32331617, - 0x3f013e37, 0x06023e01, 0x072a1e4c, 0x3266240b, 0x522a6e72, 0x362f4442, 0x2149131b, 0x43305d70, 0x10125946, 0x1e14110d, 0x0a04122a, 0x30381c14, - 0x264d4043, 0x3108218b, 0x572a4832, 0x0b031615, 0x2aca021f, 0x160f111e, 0x72329341, 0x39472a1e, 0x84242e55, 0x4643a831, 0x08151e98, 0x1c1e2a0a, - 0x22110614, 0x443a522e, 0x1d891d2a, 0x7c2a4828, 0x0e072828, 0x00820013, 0x1e00033e, 0xce0649ff, 0x0b00cc05, 0x4e002b00, 0x14040000, 0x22212306, - 0x33363426, 0x22253221, 0x0127b883, 0x3233013e, 0x82210117, 0x82e28203, 0x012729de, 0x09272221, 0x13010e01, 0x0620f384, 0x1582e682, 0x82012f21, - 0x3336252c, 0x03011f32, 0x17210885, 0x08e48213, 0x1d0b0238, 0x159ffe15, 0x01151d1d, 0x8afe1561, 0x01021d15, 0x111a0561, 0x82010f19, 0x0f1c8c01, - 0x1d078d01, 0xfe0f1c15, 0x1977fe82, 0xfeaafe0f, 0x9a1a05bc, 0x011a0f0a, 0x54820301, 0x05055408, 0x0a0f0fd2, 0xa38b0505, 0x0e0a0f03, 0x0111a408, - 0x1d2a700e, 0x5d1d2a1d, 0x0707151d, 0x1410a604, 0x18effd15, 0x0e0c80fd, 0x02181d15, 0xd4011568, 0x1410bbfb, 0x0a0f4a05, 0x0403e101, 0x02040407, - 0x0a10075b, 0x013d020f, 0x0a070615, 0xe9fe0c0f, 0x840d0995, 0x844620f3, 0x841420f3, 0xad5020f3, 0x830120f3, 0x010f22f3, 0x24e98225, 0x07141516, - 0x06734b05, 0x30012f25, 0x82272623, 0x363726f8, 0x0336013f, 0x20f5829c, 0x23f5846f, 0xf9fc1591, 0x033ef5a2, 0x040f0a52, 0x042d0148, 0x110f0a04, - 0x0d89c7fe, 0x05070a0f, 0x030501ba, 0x02010104, 0xf7a3076c, 0x0b0e033d, 0x68790607, 0x120b0e01, 0x074d6d05, 0x030f0a0f, 0x06050369, 0x02040408, - 0x8a0cb403, 0x84b020f7, 0xae5220f7, 0x363421f7, 0x1322f982, 0x06833736, 0x06071424, 0x04822707, 0x83222321, 0x262722f4, 0x06f14131, 0x05171623, - 0x20f98241, 0x23f9845b, 0x54fb15a5, 0xb32ef9a3, 0x010f140f, 0x1408068d, 0x2196050f, 0xf1831202, 0x0607092b, 0x0f056b4f, 0x5a070d0a, 0x08faa337, - 0x0f0a8d29, 0x54fe0a0f, 0x0f0806bd, 0xc706090a, 0x0513052d, 0x06060101, 0x0906916a, 0x790a0f0a, 0x0300004a, 0x49ff4600, 0x4105e206, 0xec4135f3, - 0x07022206, 0x21098537, 0x854d010f, 0x23342105, 0x2406eb42, 0x0636011f, 0x20f982ce, 0x23f98473, 0xc7f9158d, 0x053ef9a2, 0x0a1206e6, 0x285f010f, - 0x0a0707a3, 0x07e00b0f, 0x07060708, 0x02620103, 0x06110a0f, 0xf8a22a4c, 0x9e045108, 0x040a0f11, 0x74e2fe04, 0x0a0f046b, 0x0493070e, 0x01070404, - 0x05040d01, 0xcc110f0a, 0x0000007d, 0xff7d0002, 0x0583067c, 0x000600b6, 0x01000012, 0x010b3711, 0x14001117, 0x01272206, 0x32363426, 0xcc050117, - 0xb7e2e2b7, 0x3e2c0d01, 0x1690fa16, 0x05350582, 0xfeb60570, 0xdffe469a, 0x01462101, 0x3e31fa66, 0x7005162c, 0x21058216, 0x4f8d90fa, 0x4f881f20, - 0x06140129, 0x2c312223, 0x41002701, 0x1626060f, 0x00011217, 0x5c891e05, 0xfe011f38, 0xbd14fedd, 0x017588fe, 0x291c1f2c, 0x57016c05, 0x12025201, - 0x6b8c2b1f, 0x2c1f1238, 0x01bdc605, 0x06ae0278, 0x242c1f07, 0xfe82fd1b, 0x0aaefea9, 0xc7842c01, 0xc7825f20, 0xc7858320, 0x07110534, 0x1127011b, - 0x34262224, 0x32360137, 0x01071416, 0xc7837605, 0x8a1bfb21, 0x01a02bae, 0x21014666, 0xfe46dffe, 0xd8841d9a, 0xc583de83, 0x4f87c785, 0x00002f22, - 0xf0444f86, 0x37362605, 0x3637063e, 0x06e24324, 0x06070626, 0x07020e04, 0x06220282, 0x65860e07, 0xfcfa2808, 0x31062c1f, 0x3f232253, 0x4938402a, - 0xb6019227, 0x04034c01, 0x1d272c1f, 0xd0e2feb6, 0x1f3472a3, 0x532b2f46, 0x8c260833, 0x1f240881, 0xd7760e0f, 0x5699595a, 0x275d557b, 0x0120a892, - 0x2b1d1f2c, 0x5c431203, 0x3b649586, 0xd96f789f, 0x00191578, 0x04340082, 0x44ff4600, 0xb405ce06, 0x0e000d00, 0x30002000, 0x33010000, 0x270a7d45, - 0x03253123, 0x23010e01, 0x280a9d45, 0x16320109, 0x01071415, 0x23148606, 0x64ab0436, 0x280a5d45, 0xef6ffe87, 0x1a05bcfe, 0x0a954211, 0x31021529, - 0xfc0c2c1f, 0x82291649, 0xb7032406, 0x45ee0216, 0xa126094a, 0xbbfb4801, 0x66451410, 0x84fe2a08, 0x1f2c3002, 0x27fa1216, 0x22068323, 0x8223d905, - 0x010025a6, 0x09004600, 0x1425a782, 0x00001f00, 0x1e2b4637, 0x10437820, 0x450a2022, 0x00201bd7, 0x13286f82, 0xed0654ff, 0x3f00ae05, 0x32267e82, - 0x011b1617, 0x03881737, 0x03170226, 0x27032707, 0x0b230482, 0x83270701, 0x03342503, 0x07300706, 0x2c081086, 0x37133703, 0x17013f30, 0x1a323713, - 0x02013f01, 0x0239018b, 0x39393242, 0x39392c72, 0x3939173f, 0x7045278c, 0x4c393984, 0x39391f2f, 0x2413825e, 0x39393f6b, 0x20408246, 0x080e828c, - 0x39394824, 0x07247154, 0x55393958, 0x221a013d, 0xae05380a, 0x4bfd0239, 0x38384702, 0x8d010bfd, 0x61fe3838, 0x05825303, 0xcbfe2026, 0xfb0d5602, - 0x032a3282, 0x85fb9760, 0x65023939, 0x05826ffe, 0x24fdc831, 0x02083939, 0x6d5a37da, 0x3939cffd, 0x82fc6d03, 0xa005314a, 0x649efd06, 0xfc3838ec, - 0x9a01f5e3, 0x380b9f01, 0xe382d482, 0xffa02008, 0x056006ce, 0x00070032, 0x15212500, 0x35231121, 0x04940133, 0x7cbcfacc, 0x047846f4, 0x920078ec, - 0x35212c23, 0x044c0221, 0xfe74fb14, 0x86ac01cc, 0x29279625, 0x5c030403, 0x14fe2cfc, 0x279e6402, 0xa402bc28, 0x5cfde4fc, 0x279d1c03, 0x01740429, - 0xfc9cfdec, 0x9dd403a4, 0x2c052927, 0x54fe3401, 0x8c04ecfb, 0x33232796, 0x82112315, 0xe40527c7, 0x34fbf47c, 0xeb884405, 0xceff6428, 0x32059c06, - 0xd0821f00, 0x15163226, 0x01011930, 0x42058445, 0x112006ed, 0x2c09f442, 0x23198003, 0x16117d02, 0xfd152319, 0x21068320, 0x0c8483fd, 0x11e00233, - 0x19233205, 0xcafdccfd, 0x230e2202, 0xfd121c19, 0x2306828a, 0xdefd6a04, 0x02220d84, 0x7b410e76, 0x00642805, 0x049c063b, 0x851a00c4, 0x8214206b, - 0x27222262, 0x37608709, 0x32360130, 0x36010917, 0x23196006, 0x1290fe0b, 0x51fd123e, 0x1f12c1fe, 0x01210d82, 0x330d8270, 0x3f01af02, 0x23bc0212, - 0xfd101319, 0x031919f8, 0x193dfecb, 0x02280c83, 0xfc191908, 0x19c30135, 0x00256483, 0x00ae000e, 0x83098401, 0x86022004, 0x0001220b, 0x85e1820d, - 0x00022417, 0x863d0007, 0x00032417, 0x86930026, 0x8204200b, 0x86d62023, 0x0005240b, 0x8604010f, 0x00062b0b, 0x002e010c, 0x04010003, 0x00850009, - 0x01200b85, 0x0320c782, 0x02201786, 0x2d207782, 0x03240b86, 0x45004c00, 0x04200b86, 0xba202382, 0x05240b86, 0xe4001e00, 0x06240b86, 0x14011800, - 0x46304c83, 0x72007500, 0x61006e00, 0x65006300, 0x49002000, 0x6f200782, 0x732e0f82, 0x75460000, 0x63616e72, 0x63492065, 0x0e826e6f, 0x1f825220, - 0x2d826720, 0x2b826c20, 0x00007228, 0x75676552, 0x0882616c, 0x83004621, 0x0074212f, 0x72220783, 0x45846700, 0x49823a20, 0x20215999, 0x281f8300, - 0x00380032, 0x0031002d, 0x20058230, 0x22058232, 0x82330032, 0x6e6f2a73, 0x726f4674, 0x3a206567, 0x827f8c20, 0x3832280f, 0x2d30312d, 0x83323032, - 0x20b6a827, 0x24908256, 0x00730072, 0x20a68469, 0x20688220, 0x20748230, 0x3007842e, 0x56000030, 0x69737265, 0x30206e6f, 0x302e3130, 0x8d108230, - 0x110e41b6, 0x24060d41, 0x02000000, 0x20008b00, 0x840c8b01, 0x8262200b, 0x01c80805, 0x03000200, 0x03010201, 0x05010401, 0x07010601, 0x09010801, - 0x0b010a01, 0x0d010c01, 0x0f010e01, 0x11011001, 0x13011201, 0x15011401, 0x17011601, 0x19011801, 0x1b011a01, 0x1d011c01, 0x1f011e01, 0x21012001, - 0x23012201, 0x25012401, 0x27012601, 0x29012801, 0x2b012a01, 0x2d012c01, 0x2f012e01, 0x31013001, 0x33013201, 0x35013401, 0x37013601, 0x39013801, - 0x3b013a01, 0x3d013c01, 0x3f013e01, 0x41014001, 0x43014201, 0x45014401, 0x47014601, 0x49014801, 0x4b014a01, 0x4d014c01, 0x4f014e01, 0x51015001, - 0x53015201, 0x55015401, 0x57015601, 0x59015801, 0x5b015a01, 0x5d015c01, 0x5f015e01, 0x696e7507, 0x30463045, 0x31200786, 0x32200786, 0x33200786, - 0x34200786, 0x31210784, 0x21278530, 0x27853031, 0x85303121, 0x30312127, 0x31212785, 0x20278730, 0x202f8635, 0x20078636, 0x20078637, 0x20078638, - 0x20078639, 0x20078641, 0x20078642, 0x20078643, 0x20078644, 0x20078645, 0x21078546, 0x07863031, 0x07863120, 0x31207f86, 0x31207f86, 0x31207f86, - 0x31207f86, 0x31207f86, 0x31207f86, 0x31207f86, 0x31207f86, 0x31207f86, 0x31207f86, 0x31207f86, 0x31207f86, 0x31207f86, 0x32217f86, 0x21778530, - 0x07863132, 0x32207f86, 0x32207f86, 0x32207f86, 0x32207f86, 0x32207f86, 0x32207f86, 0x32207f86, 0x32207f86, 0x32207f86, 0x32207f86, 0x32207f86, - 0x32207f86, 0x32207f86, 0x33217f86, 0x21778530, 0x07863133, 0x33207f86, 0x33207f86, 0x33207f86, 0x33207f86, 0x33207f86, 0x33207f86, 0x33207f86, - 0x33207f86, 0x33207f86, 0x33207f86, 0x33207f86, 0x33207f86, 0x33207f86, 0x34217f86, 0x21778530, 0x07863134, 0x34207f86, 0x34207f86, 0x34207f86, - 0x34207f86, 0x34207f86, 0x34207f86, 0x34207f86, 0x34207f86, 0x34207f86, 0x34207f86, 0x34207f86, 0x34207f86, 0x34207f86, 0x35217f86, 0x21778530, - 0x07863135, 0x35207f86, 0x35207f86, 0x35207f86, 0x35207f86, 0x35207f86, 0x35227f86, 0x00830038, 0xffff0123, 0x07b94500, 0x0c000026, 0x04001400, - 0x02241682, 0x01000000, 0x09850386, 0x55e00024, 0x11822068, 0xfde0002c, 0x00009fff, 0x63e10000, 0xfa056e19, 0x72e94484, + 0x0000bc57, 0x00000000, 0x4c7d0000, 0x00000400, 0x00010037, 0x000e0000, 0x00030080, 0x54464660, 0x276aa24d, 0x7d0000ac, 0x28158230, 0x4544471c, + 0x00150046, 0x820f8214, 0x1c002d03, 0x322f534f, 0x8d52fa8c, 0x68010000, 0x562c1f82, 0x70616d63, 0x0dceb1d1, 0x90020000, 0x102c0382, 0x20747663, + 0x6f043b00, 0xa0040000, 0x04261f82, 0x70736167, 0x5982ffff, 0x0c7d0022, 0x08380f82, 0x66796c67, 0xf2e6a629, 0x6c050000, 0xcc710000, 0x64616568, + 0xbfa43225, 0xec201b82, 0x36210382, 0x23108268, 0x05068209, 0x24205f82, 0x24280f82, 0x78746d68, 0x8a18782e, 0xc0200f82, 0xce280f82, 0x61636f6c, + 0x7c463629, 0xa4205f82, 0xc8270f82, 0x7078616d, 0x8203ad00, 0x4801213b, 0x202c0f82, 0x656d616e, 0x8ba58b77, 0x38770000, 0xf2303382, 0x74736f70, + 0x5b5739aa, 0x2c790000, 0xe0030000, 0x012deb84, 0x6ca50000, 0x0f5f48b3, 0x0b00f53c, 0x25368207, 0xfde00000, 0x08839fff, 0x5c95e12b, 0xfe80fcdb, + 0x05000768, 0x220f82e0, 0x82020008, 0x82028305, 0x00062233, 0x290982ff, 0x80fc0007, 0x00070000, 0x0c820100, 0x0420028b, 0x63251184, 0x0800bb03, + 0x21008300, 0x23830002, 0x40222583, 0x0b822e00, 0x072b0d82, 0x00900100, 0x04000005, 0x82e6048c, 0x85fa2011, 0x5c032b07, 0xcf015900, 0x00020000, + 0x15820905, 0x01210285, 0x84078510, 0x75462905, 0x40005472, 0x59e12000, 0x06208385, 0x04856a84, 0x3b208b84, 0x07202683, 0x03830482, 0x0280fc25, + 0x82910009, 0x0052220c, 0x2c798220, 0x003500a5, 0x0029004b, 0x006d0191, 0x349b8278, 0x00470060, 0x00170152, 0x004a000a, 0x0032018f, 0x004f001b, + 0x2631822e, 0x011801ca, 0x82a90036, 0x0028251b, 0x004d0133, 0x23829182, 0x22000338, 0x7a017c01, 0x39003900, 0xce012701, 0x4c001300, 0x22007301, + 0x0d824700, 0x85000b24, 0x0f82b500, 0x2e005b26, 0x5c004b01, 0x5b220182, 0x05822a00, 0x03825320, 0x5e004b32, 0xc8005300, 0x56015c00, 0x6b002e01, + 0x46001e00, 0x7d210184, 0x22018500, 0x82450046, 0x00a0214b, 0x6324018b, 0x9f006300, 0x0022b782, 0x05820300, 0x1c200383, 0x0123e385, 0x8403000a, + 0x001c2409, 0x82ee0004, 0x00082e1b, 0x00020008, 0xe0200000, 0xff59e1f4, 0x260984ff, 0xff00e1f0, 0x82e3ffff, 0x8400200b, 0x0006222b, 0x260b820e, + 0x00050004, 0x82070006, 0x8209202f, 0x000b24cd, 0x820d000c, 0x000f2617, 0x00110010, 0x32818212, 0x00150014, 0x00170016, 0x00190018, 0x001b001a, + 0x821d001c, 0x001f34ab, 0x00210020, 0x00230022, 0x00250024, 0x00270026, 0x82290028, 0x2b3208db, 0x2d002c00, 0x2f002e00, 0x31003000, 0x33003200, + 0x35003400, 0x37003600, 0x39003800, 0x3b003a00, 0x3d003c00, 0x3f003e00, 0x41004000, 0x43004200, 0xe7824400, 0x00463808, 0x00480047, 0x004a0049, + 0x004c004b, 0x004e004d, 0x0050004f, 0x00520051, 0x00540053, 0x00560055, 0x00580057, 0x005a0059, 0x005c005b, 0x005e005d, 0x0060005f, 0x82620061, + 0x850620c8, 0x22c982cc, 0x82020100, 0x00022105, 0x0123008d, 0x8d030000, 0xd10d4011, 0x6f043b22, 0x2c21d482, 0x08018500, 0x84003cdf, 0x6404c600, + 0xc4052405, 0x5807d406, 0xce08ea07, 0x76093409, 0xfe0ab00a, 0xee0b880b, 0xe60c2e0c, 0xd80d6a0d, 0xca0e640e, 0x5a10a40f, 0x7e11bc10, 0x9212c611, + 0x8c130c13, 0x9814d813, 0x38164815, 0x80171217, 0xae1b1e1b, 0xc01c341c, 0x081fea1d, 0x8020bc1f, 0x8a213421, 0x8622fc21, 0x2a27c823, 0xb028ca27, + 0xc6295229, 0xbe2a0a2a, 0xf42ba42b, 0x442e162e, 0x8e2e682e, 0x0c30f02f, 0xd2306a30, 0x56311e31, 0xde31ac31, 0xba32ec31, 0xc6336433, 0xbc344034, + 0xb6353a35, 0x1a36de35, 0x90364236, 0x1c37e436, 0x9e378c37, 0xc637b237, 0xee37da37, 0x14380238, 0x7c384a38, 0x0200e638, 0x00003b00, 0xaa041502, + 0x07000300, 0x01b12e00, 0xb23c2f00, 0xed000407, 0x0506b132, 0x03b23cdc, 0x220a8202, 0x8303b100, 0x83052016, 0x07b22716, 0x3cfc0106, 0x178301b2, + 0x21113338, 0x11212511, 0xda013b21, 0x640161fe, 0xaa049cfe, 0x043b56fb, 0x52820034, 0xff80fc25, 0x828003f5, 0x82012009, 0x05002e59, 0xfd212521, + 0xf99a0533, 0x0b0007b3, 0x0815820b, 0x09020325, 0xf7040f00, 0x1b00fd03, 0x2f002500, 0x30010000, 0x15163221, 0x07010e14, 0x16150706, 0x1e171617, + 0x82141501, 0x010e2f0b, 0x21372123, 0x013d3632, 0x21232634, 0x09853335, 0x012b4d08, 0xbd010a02, 0x311f8e7d, 0x1e1e2020, 0x1f202525, 0x2214132b, + 0xfe385f23, 0x1801a217, 0x3f48483f, 0x3dfce8fe, 0xfc3d4242, 0x778dfd03, 0x0d324f38, 0x0208030c, 0x1c1c0d0d, 0x363c3f58, 0x2c272735, 0x313e428b, + 0x3e88423e, 0x3e3a2e39, 0x30088f83, 0x63009100, 0xa9036f06, 0x17000f00, 0x00002b00, 0x30273025, 0x30073021, 0x37123623, 0x17121633, 0x06233001, + 0x26333007, 0x00113001, 0x30333027, 0x2f038513, 0x11070206, 0x4cea0230, 0x8a49c7fe, 0xab2fbf2f, 0x34080382, 0x3c068dfe, 0x023cf73d, 0x17f7fe84, + 0xcb04cc9b, 0x1dce3794, 0x8ce3e363, 0x8c8c2e02, 0x028cd2fd, 0xb9b9b9c9, 0x4a01f0fd, 0xfe28d401, 0x6175018b, 0xfe329afe, 0x218282b3, 0x04820300, + 0x0007ab3a, 0x58036103, 0xba038c03, 0x26370000, 0x30332702, 0x1d17021d, 0x031d1704, 0x14910290, 0x478211a3, 0x719126f1, 0x1e268695, 0x3e331701, + 0x73413701, 0x220d8606, 0x8937033d, 0x8a042002, 0xa317850b, 0xf1298211, 0xa1719126, 0x06332e86, 0x26230702, 0x012e2702, 0x010e2327, 0x2d0d8207, + 0x27262205, 0x1e37013e, 0x35323301, 0x18832634, 0x26272629, 0x33363435, 0x82171632, 0x012e2b21, 0x15062223, 0x17161714, 0x0285011e, 0x06141523, + 0x223b8225, 0x82272223, 0x26272628, 0x37363734, 0x823c823e, 0x84072021, 0x011d352c, 0x32331614, 0x3d023e37, 0x33352301, 0x188e2311, 0x0168185e, + 0x043400cc, 0x040c040e, 0x390e030e, 0x390f7b0e, 0x030f030e, 0x040e030d, 0x012761cc, 0x19621864, 0x833a0e7a, 0x820c205c, 0x3c0f3b5c, 0x4bf0020f, + 0x2f0b276e, 0x314d1d0c, 0x09342a77, 0x2b5a0a24, 0x4865702a, 0x13822463, 0x401a3c08, 0x16373731, 0x24093116, 0x15443009, 0x02741316, 0x4f090c43, + 0x2b2c3448, 0x12132021, 0x22221212, 0x375a3d5f, 0x11531e38, 0x50483c46, 0x1a1e4850, 0x7c16271a, 0x70b75adf, 0xcd70be01, 0x4d0010b8, 0x1a2d4701, + 0x67191968, 0x10014419, 0xf0fe4444, 0x220a8244, 0x101a6819, 0x47015b01, 0x084d4741, 0x42fe70af, 0x13014570, 0x19671a45, 0x451a6719, 0x0b45edfe, + 0x2a0b3638, 0x6d2c290b, 0x020d2f2c, 0x2c150207, 0x615c512d, 0x290a3431, 0x2d24250b, 0x15162a34, 0x0208020c, 0x17171f0b, 0x6865293f, 0x133e306e, + 0x42292a13, 0x4242b842, 0x27282a2a, 0x31314726, 0xa4525c39, 0x09085c52, 0x3b1e3121, 0x009efe57, 0x52000700, 0xae0602ff, 0x1a001a05, 0x38002400, + 0x53004900, 0x80007800, 0x22050000, 0x37343526, 0x3337023e, 0x07060706, 0x3307010e, 0x3233013e, 0x06141516, 0x013d3227, 0x1d222334, 0x26031401, + 0x16332702, 0x17161712, 0x36373633, 0x272a8212, 0x23210702, 0x17322111, 0x07272783, 0x012e2313, 0x48372327, 0x15270779, 0x27262201, 0x8234012e, + 0x3637222a, 0x21238233, 0x58450617, 0x3698080b, 0x17011e37, 0x1105010e, 0x11011501, 0xa2013501, 0x0f106d6b, 0x8a314f3a, 0x1e2a2a3b, 0x0c09251d, + 0x51314711, 0x6d6a6e5f, 0x211f6c6d, 0x136b2183, 0x0e0e1450, 0x140e0e0c, 0x2169144e, 0x8f012083, 0x56090165, 0x3a392b2c, 0x56087180, 0x249b7817, + 0x9b242626, 0x5d3aad02, 0x23232122, 0x2e2e2221, 0x3333563b, 0x12490d1f, 0x4c44353e, 0x4236444c, 0x0e380f12, 0xe4fe6920, 0x72fe2801, 0x84fdd8fe, + 0x3c3d3c7f, 0x2d22606f, 0x28292829, 0x312f3256, 0x776a676a, 0x77197755, 0x56080282, 0x01701803, 0xfe466fbf, 0x4f4e46e7, 0x01464e4f, 0xfe6f4619, + 0x9e027041, 0x4d603433, 0xe7fe1160, 0x5536c913, 0x2a3e2a26, 0x8efede26, 0x842a2a27, 0x2a2a84b6, 0x25251414, 0x3929074a, 0xa1525f32, 0x3b365e53, + 0x4a092108, 0x34feb24e, 0xfe821001, 0xfecd0192, 0x820082ef, 0x00043f00, 0x06ab0020, 0x006103e0, 0x0015000b, 0x0074004a, 0x11303700, 0x16322130, + 0x012b0614, 0xb7490119, 0x46012008, 0x162205ce, 0x12821617, 0x2806d046, 0x3435012e, 0x3233023e, 0x08d14617, 0x1506072c, 0x011f1614, 0x1415011e, + 0xcd46020e, 0x022e2105, 0x16212485, 0x0cc64617, 0x3e373623, 0x06c84601, 0x0121d108, 0x66665d20, 0x2aadb45d, 0xad292f2e, 0x7655dd02, 0x0d320c2a, + 0x362b2b23, 0x3b2e4141, 0x620a270a, 0x563d2162, 0x37384c36, 0x0d320d28, 0x39384b1a, 0x37342020, 0x205d643c, 0xdd025a3f, 0x4c5e0a04, 0x294a663c, + 0x426c4d2a, 0x59237d5a, 0x503d5113, 0x20506161, 0x16161d1c, 0x5ff38a19, 0x6d9e02b7, 0xf5fe6db9, 0x282b6a01, 0xfd2b292e, 0x0b343db6, 0x162b0c2f, + 0x27313a17, 0x07020c2e, 0x515c1202, 0x1b31482c, 0x0b321a1b, 0x29210b2d, 0x2a2c1817, 0x130c0b29, 0x4d2f505d, 0x31711d37, 0x82572d40, 0x58815454, + 0x3447502e, 0x5d663b2f, 0x09665d6e, 0x2f0f1008, 0xfe5d3420, 0x0005009b, 0x06ab002e, 0x006103d2, 0x001f0012, 0x0134002a, 0x25000046, 0x28076842, + 0x1e32013e, 0x020e1402, 0x05834227, 0x1d06222a, 0x05161401, 0x17322111, 0x2c0f5e41, 0x27012e05, 0x17071d23, 0x1d170f1d, 0x8502870e, 0x3305820b, + 0x11231101, 0x17121633, 0x3317011e, 0x3d27053d, 0x0e3d270f, 0x0b880287, 0x3311042d, 0x02262311, 0x603c3301, 0x82232223, 0x7a5f3a02, 0x23234460, + 0x483c6144, 0x4f904f4f, 0x01df014f, 0x2b2c5609, 0x9aa35657, 0x26008225, 0x0863029a, 0x440c0922, 0x62280732, 0x22852175, 0x0d082308, 0x76231088, + 0x42ac8521, 0x282d074c, 0xb6845428, 0x5a275484, 0x52a2525d, 0x3304845d, 0x339e024f, 0xfe67c134, 0x256901f1, 0x26293d2a, 0x155315b2, 0xae4d7f44, + 0xfe02284d, 0x439e0270, 0x4043f4fe, 0x01268088, 0x4362fd90, 0x5f430c01, 0x00a53a05, 0x035b0654, 0x002f00b8, 0x00540048, 0x2500005e, 0x010e2330, + 0x022e2223, 0x078a4435, 0x20083e43, 0x06e34906, 0x82373621, 0x013d3501, 0x11213523, 0x32210123, 0x06141516, 0x07010e07, 0x17021e15, 0x07300b82, + 0x2123020e, 0x37323337, 0x34013d36, 0x012b2627, 0x080a5d4d, 0x0e04e541, 0x804a6074, 0x3035345c, 0x53434330, 0x122b9c71, 0x6518124a, 0x7979634d, + 0x24242863, 0x10101b1b, 0x772f01ac, 0x72010601, 0x141b7769, 0x1a1a3415, 0x1311353d, 0x4f391011, 0x8768fe2f, 0x821e35e9, 0x356a0800, 0x3733d2e9, + 0xe2d23337, 0x6d38503d, 0xa2686aa2, 0x1c1c3738, 0x2b0b5963, 0x7f493b0b, 0x7f758875, 0x14130b0a, 0x41271e1d, 0x0341fe75, 0x2f627646, 0x14151542, + 0x16010703, 0x3525242f, 0x412d2c32, 0x1c1b7525, 0x1b342933, 0x3033711c, 0x00333027, 0x00050000, 0x06ab0035, 0x006103cb, 0x0046001f, 0x00600053, + 0x25000066, 0x1b442622, 0x37362c13, 0x0617011e, 0x22210607, 0x8227012e, 0x37362321, 0x0646013e, 0x2a148205, 0x17163207, 0x3233033e, 0x4315021e, + 0xa8081058, 0x36352135, 0x11333712, 0x15231533, 0x23113325, 0x4e01010e, 0x49269683, 0x76584169, 0x48115b21, 0x59594d3b, 0x124c3d4d, 0x210e3a0f, + 0xfc013c3d, 0x113c5738, 0x23232c10, 0x39902955, 0x22212d2d, 0x020d1617, 0x1c0b0104, 0x2a203025, 0x211d3548, 0x3b35573e, 0x42754141, 0xfea10242, + 0x2eb52dc9, 0xfe5b5b8e, 0x2307dbbe, 0xa9b1ac8e, 0x2e588154, 0x30324b4d, 0x665d6a39, 0x313f6a5d, 0x49092309, 0x44242929, 0x493d3030, 0x53363680, + 0x2627291c, 0x2d2d2928, 0x15010136, 0x1c0f1b23, 0x332f4d37, 0x58213c53, 0x3d0d3d3e, 0x0804843e, 0x5d834d2f, 0x4a2a014a, 0x83553afe, 0x3b6301d8, + 0x000800ed, 0x0608ff4b, 0x001505b5, 0x0016000c, 0x0028001c, 0x0068005c, 0x009b0072, 0x11301700, 0x12354430, 0x83110121, 0x113323ec, 0x01871521, + 0x4c06a645, 0xb44c2d75, 0x13232406, 0x85031333, 0x010f2a0b, 0x013e2533, 0x010e2337, 0x82028207, 0x27028213, 0x17011e23, 0x33112311, 0x2005af4c, + 0x821e8233, 0x11332902, 0x0901b023, 0x582c2c56, 0x30075644, 0xf466ca01, 0xfe850186, 0xfe0301e1, 0xfa1f01fd, 0x0cdd4be7, 0x4b092521, 0x0c2207dd, + 0xdd4b0c2e, 0x38300811, 0xe1092209, 0x64082309, 0xf1ca81c9, 0x0d030e04, 0x3b040e03, 0x02d002b0, 0x080b0104, 0x5716071f, 0x15571516, 0x0b081f08, + 0x62010501, 0x12481278, 0x09200b82, 0x54080382, 0x77124712, 0x9f02f862, 0x67c23334, 0x6901f1fe, 0x293e2926, 0x02bcfd25, 0x5abbfd9f, 0xc45a9f02, + 0x035acf58, 0x0b353957, 0x2c2a0b2b, 0x0c2f2c6e, 0x14020802, 0x5d512c2d, 0x0a343160, 0x25240a29, 0x162a342d, 0x08020b16, 0x17200a03, 0x64293f17, + 0x7c1f0b69, 0x8202821f, 0x01613340, 0x104111e0, 0xd3114110, 0x144c1390, 0x2d134b13, 0x02822db6, 0x13491322, 0xfe290b82, 0x279f0263, 0x5816279d, + 0x82028216, 0x61fd2c08, 0x04000000, 0xab002900, 0x4703d706, 0x3a220553, 0x53474600, 0x1140471a, 0x14011d26, 0x32331617, 0x20070443, 0x0adc4125, + 0x01292108, 0x66655e20, 0x29adb35d, 0xad292e2e, 0x95840f03, 0x41694926, 0x223b3b57, 0x110f3d0f, 0x2c4e3b48, 0x4e350082, 0x0e134c3c, 0x3c220f3a, + 0x012d013d, 0x01bffeae, 0x01ddfe23, 0x10054741, 0x54a9b134, 0x272d5981, 0x22084b26, 0x35393008, 0x5d665d35, 0xc6423535, 0x020b2a07, 0x60ba609e, + 0x000060c4, 0x825f5103, 0x6d01082e, 0x930502ff, 0x44001a05, 0x6d005500, 0x9e299182, 0xd500ab00, 0x0000e300, 0x05584725, 0x0622232c, 0x27012e07, + 0x013e3736, 0x1b443637, 0x0e072205, 0x098f5202, 0x82070621, 0x22232a01, 0x3727022e, 0x17161716, 0x09d6481e, 0x44013521, 0x37370664, 0x3233023e, + 0x27061016, 0x3d023e32, 0x022e3401, 0x07010e22, 0x4f011d06, 0x258205a1, 0x34350225, 0x48353736, 0x322005f2, 0x634a4282, 0x1e152405, 0x82141501, + 0x0ce1476b, 0xee471320, 0x8325200b, 0x2307211e, 0x02829982, 0x27262223, 0x232d8206, 0x2e272223, 0x37204f82, 0x3320b083, 0x07204c85, 0x7382c988, + 0x02165508, 0x3a3c3e4b, 0x17433031, 0x0f0c300c, 0x21331413, 0x502f2b20, 0x0a0a213a, 0x1d1a2f24, 0x13131a19, 0x1f1f2216, 0x2f362c2d, 0x0f2a3647, + 0x0e0e0c50, 0x1c2e1112, 0x3f45413e, 0x3e980247, 0x0f0e3c5c, 0x5c3c0e0f, 0x76767d3e, 0x1e31237d, 0x311e0e0e, 0x071f3145, 0xb3080082, 0xfd31100f, + 0x3d593bf8, 0x343c4a1f, 0x53371e3e, 0x38292968, 0x343f0f0f, 0x10104a3d, 0x3c3a5a3c, 0x42794242, 0x3b373d42, 0x3a3a6f3b, 0x16165203, 0x90295645, + 0x21225b38, 0x05010d2d, 0x0e0e0a01, 0x2a203026, 0x1d352424, 0x1f1f1110, 0x2b383656, 0x10113c2c, 0x42413bec, 0x41413b3a, 0x07293599, 0x262c312e, + 0x160a2a0a, 0x0a1d1415, 0x412d1709, 0x191a2028, 0x04061928, 0x150d0d06, 0x2c253914, 0x0e191a48, 0x2c21140e, 0x11143e17, 0x0d0c0c11, 0x3407343a, + 0x6afe5c36, 0x4040582f, 0x40415253, 0xfeb72f59, 0x1c5cb6b8, 0x752c4933, 0x1c34482c, 0x2424341c, 0x2e080982, 0x1a1a2325, 0x1d06031c, 0x432a4734, + 0x13071151, 0x40263a4e, 0x0c0d192d, 0x2620202d, 0x07134e3a, 0x2a435111, 0x1d342423, 0x13323857, 0x82373732, 0x01382704, 0x102e303a, 0x0484302e, + 0x491f2a08, 0x556b4041, 0x284e291b, 0x01365a28, 0x12121401, 0x0f0e0f1b, 0x32304c36, 0x1e1d2b2a, 0x44131222, 0x3f933030, 0x3e3c0e3c, 0x2104823e, + 0x0082003f, 0x78000437, 0x8806b700, 0x0f005603, 0x37002700, 0x00004000, 0x30113001, 0x2a038423, 0x30213035, 0x30133015, 0x8a333035, 0x201b830f, + 0x22178233, 0x82273021, 0x0c815427, 0x07010e3c, 0x01012e33, 0x01c86cac, 0x5b5b53fc, 0x5c5c2401, 0xfb3c2b02, 0x99266f3a, 0x03828826, 0xd7fe4308, + 0x10411005, 0x024010c6, 0x02c2fdf5, 0xfd60603e, 0xee0158c2, 0x12fe5858, 0x70b5b558, 0x7070be01, 0x027042fe, 0x31c5313a, 0x0000c531, 0x00080005, + 0x03f806ab, 0x00340061, 0x0046003e, 0x00580050, 0x9f4b3700, 0x23272a32, 0x33132307, 0x01171216, 0x90a18623, 0xfd420811, 0x0d2a7655, 0x2b230d31, + 0x4140372b, 0x280a3a2e, 0x2262620a, 0x4c36563c, 0x0d273838, 0x4b1a0d32, 0x1f203a37, 0x653c3634, 0x5a3e215d, 0xfa3dd002, 0x89e46e3b, 0xfe269826, + 0x401104d7, 0x4010c510, 0x13912803, 0x824bac20, 0xb50b242c, 0x419e02b5, 0xfd210a00, 0x20108ef7, 0x34008200, 0x00600003, 0x03a006ab, 0x00150061, + 0x004b002b, 0x03302500, 0x0eaa4630, 0x07020624, 0xb1413521, 0x48052013, 0x012d1d87, 0x1270db3c, 0x2b0a1247, 0x2b0b040b, 0x2509820b, 0x2595256d, + 0xc7414b01, 0x84012605, 0x4a269683, 0x14614868, 0x9e02b728, 0x2737dd37, 0x0282279b, 0xc3830882, 0x2007d641, 0x1741480b, 0x042acb82, 0xb7004700, + 0x5603b906, 0xcd820b00, 0x29002122, 0x46183f46, 0x01280a1b, 0x23112311, 0x48152135, 0x28074c4d, 0x292e2e29, 0x011402ad, 0x070246ad, 0x6c7b0125, + 0x46fc01c8, 0xc1200f08, 0x2106ea45, 0x79423e02, 0x207f8205, 0x08934f06, 0x27001e34, 0x4e003d00, 0x7e005800, 0x22050000, 0x34352627, 0x01843637, + 0x0e333025, 0x4f060702, 0x17200594, 0x2408954f, 0x14011d22, 0x06944f01, 0x200a1256, 0x2d964f06, 0x4f010e21, 0x033f1297, 0x37366b7a, 0x1d1d0f10, + 0x8a312827, 0x123a553b, 0x110c0a12, 0x30513147, 0x6d6a6e2f, 0x4f09fed9, 0x04260692, 0x050c0513, 0x944f0512, 0x4d142212, 0x17944f14, 0x0e3a0e22, + 0x350e954f, 0x7f4242fd, 0x363d3d3c, 0x232f3038, 0x2b50522d, 0x312f332a, 0x904f3535, 0x691a2213, 0x4f02821a, 0x2e221192, 0x924f2eb6, 0x20082215, + 0x0d934f08, 0x0106003c, 0x0508ff17, 0x001505e9, 0x0017000b, 0x004b002d, 0x005f0055, 0x11300500, 0x03823330, 0x15302122, 0x0b870784, 0x3305c650, + 0x37363435, 0x1632013e, 0x14011e17, 0x010e0706, 0x37363227, 0x3d2b0182, 0x26273401, 0x23012e27, 0x82070622, 0x07bf4601, 0x4f110521, 0xcc2412d1, + 0x6311016c, 0x22080382, 0x6a4210fd, 0x29292626, 0x836a2626, 0x2a26256b, 0x6b25262a, 0x17402641, 0x0c0c0d16, 0x4017160d, 0x873f2826, 0x013f220b, + 0x0c8348f4, 0x9f02f825, 0x8460c1fd, 0x57032b04, 0x822b2c2d, 0x2b825455, 0x09832d2c, 0x0883a920, 0x191b612d, 0x2e252519, 0x25252e69, 0x8c1b1919, + 0x02562f0c, 0x6eb86e9f, 0x6a01f5fe, 0x282e282c, 0xf082002c, 0x000a0036, 0x03f606ab, 0x00120061, 0x003a0030, 0x004a0044, 0x2e222500, 0x4f064050, + 0xe6af0749, 0x1133112f, 0x2c011521, 0x294c6a42, 0x426a4c29, 0x9b078541, 0x665d21e0, 0x08086449, 0x6d130222, 0x2dac1001, 0x54548257, 0x2d2d5882, + 0x82a88258, 0x1b602d57, 0x2425191a, 0x242e6a2e, 0x1b1a1925, 0x55200c8c, 0x280e6d50, 0xfd9e02c1, 0x000060c2, 0x2ccb8204, 0x03b606ab, 0x00130061, + 0x002d0022, 0x06d34366, 0x30153023, 0x26038821, 0x30013011, 0x83173233, 0x070124e8, 0x52012b06, 0x272405ba, 0x11012b26, 0x51064d58, 0x2724077e, + 0x012e2726, 0x35220282, 0x80513734, 0x1506230d, 0x80511714, 0x07240806, 0x010e0706, 0xfea3014a, 0xfe1301ca, 0xedc301ed, 0x4a343441, 0x344a2929, + 0xeded4134, 0x2e2e5c4b, 0xdc02804b, 0x2705de45, 0x372b2a24, 0x17174140, 0x2405df45, 0x563d1111, 0x07e04535, 0x384a2b08, 0x1a1a3f3a, 0x5d653c36, + 0x201f1011, 0x9e02b759, 0xfe60ba60, 0x159e02dc, 0xa87d5415, 0x1515547d, 0x6c5b5e60, 0xfe2f2f5b, 0xd6456b22, 0x1717210a, 0x21075a51, 0x5b512424, + 0x2f290809, 0x15142a2c, 0x5c130c0b, 0x26272f51, 0x001d1c1b, 0x8f000400, 0x71066300, 0x1500a903, 0x39002f00, 0x00004300, 0x30013025, 0x06ec5033, + 0x2806535a, 0x01070206, 0x15163221, 0x0bd44914, 0x2322df84, 0xa2523721, 0x09064f08, 0xfea2012e, 0x59168ded, 0x0b360f16, 0x0b380f05, 0x89370982, + 0x012fb92f, 0x687301c2, 0x34291b77, 0x1f1e1919, 0x10241b1a, 0x4e1d1c11, 0x3c2106f5, 0x07f34e3c, 0x46036334, 0x45ecfe45, 0x3729c536, 0x014525c8, + 0xfd8c4514, 0xe44e8cd2, 0x142a3605, 0x0b010703, 0x4918170b, 0x2d2c3235, 0x75252120, 0x34293337, 0x09e34e37, 0xff320134, 0x05ce0508, 0x00260014, + 0x00780033, 0x009b008a, 0xc44e0500, 0x4a01202e, 0x2e3241d1, 0x11232701, 0x13331123, 0x3317011e, 0x23113311, 0x04823521, 0x07020e26, 0x37362726, + 0x30081082, 0x38700215, 0x10103d57, 0x5523222c, 0x2d39902a, 0x1721222d, 0x05010c17, 0x251c0a01, 0x472b1f31, 0x3d221e35, 0x423a3557, 0x41417542, + 0x3b3f4402, 0x08884a39, 0x4a341321, 0x0b220688, 0x884a230a, 0x233c0807, 0x2c2c1f1f, 0x37472f36, 0x0d4f0f29, 0x12110e0e, 0x413e1d2d, 0xfd474045, + 0x0e3408b2, 0xdb796803, 0x0308340e, 0x43017967, 0x341708bc, 0x4002123e, 0xa3a55655, 0x304425f8, 0x81493c30, 0x080d284f, 0x1b241447, 0x4d361d0f, + 0x3c53332f, 0x3d3e5922, 0x3f3f3c0d, 0x3e3d0d3c, 0x29353d01, 0x2b312e07, 0x0a2a0a26, 0x1e141416, 0x2d170909, 0x1a202840, 0x0619271a, 0x0e0c0605, + 0x25381514, 0x1a1a482c, 0x21150e0e, 0x153f172b, 0x06594a10, 0x36333508, 0x104a035c, 0x00fe1b63, 0x8dfe9e02, 0x0210631b, 0x5a62fd01, 0x3818f501, + 0x3a021343, 0xbcfd5d5c, 0x0003005a, 0x06ab001b, 0x006103e5, 0x005c0039, 0x2500007f, 0x430d4749, 0x26220568, 0x69432627, 0x0aea5410, 0x07060726, + 0x2221010e, 0x23051d47, 0x3233013e, 0x200fd051, 0x055b5636, 0x012722a2, 0x2a775410, 0x490c320d, 0x91430570, 0x31312c06, 0x573c1111, 0x38374c36, + 0x49330c27, 0x23080b73, 0x1f1f1011, 0x8429025a, 0x24131395, 0x57426825, 0x3d0f2276, 0x3a48120f, 0x4e58584e, 0x0f124c3c, 0x79220f3a, 0x8b491e9e, + 0x06b4430b, 0x512e2e23, 0x0bb5432c, 0x2d171723, 0x0611552a, 0x1b26272d, 0xa9b11d1c, 0x2c404154, 0x4e4d2e2c, 0x0051053b, 0x9a52200a, 0x0000281a, + 0x004f0005, 0x55b106ab, 0x0021092b, 0x342b5540, 0x35210122, 0x28080282, 0x02061521, 0x54012107, 0x2222603d, 0x22222323, 0x44607960, 0x60442424, + 0x4e4e483c, 0x014f4f90, 0x570901de, 0x57572c2b, 0x058957a3, 0xfe60032e, 0xfe420141, 0x36a601cd, 0x4c0136d6, 0x082bd954, 0x5bbcfd2a, 0x5b59ea01, + 0x52bafe52, 0x2e000700, 0xd20608ff, 0x1c001505, 0x47003300, 0x5d005300, 0x91007300, 0x06370000, 0x30153007, 0x3205034c, 0x36333011, 0x30373637, + 0x16070633, 0x2e231712, 0x45110101, 0xf1450ae5, 0x2726230c, 0x29493330, 0x82332006, 0x301122cf, 0x187d5701, 0x47012e21, 0x36082df3, 0x6d1a42f7, + 0x1848046d, 0x417f5a5a, 0x16ba32ae, 0x01922783, 0xfead01a5, 0xfe2301c0, 0x014001dd, 0x7c737422, 0x031b6d1b, 0x781b6c1b, 0xfb2da418, 0x5e20014a, + 0x505e6565, 0x1720075d, 0x08291448, 0x1e4b4123, 0xfe9f02d0, 0x6d1e5cac, 0x4bce4d6d, 0x3d21e8fe, 0x02e2fee1, 0x60bb609f, 0x080160c4, 0xc732cbcc, + 0x27028232, 0x4ee2fe29, 0x6203f6fe, 0x210df847, 0x3748b5fd, 0x0000212d, 0x358f8362, 0xffca0005, 0x05360608, 0x000f0015, 0x00330017, 0x00920066, + 0x054e0500, 0x07062a11, 0x05263330, 0x2327012e, 0x05494e30, 0x13303326, 0x3317011e, 0x2106e749, 0x63593023, 0x27262110, 0x5a0a6259, 0x615905ad, + 0x0226230d, 0x42823327, 0x53171621, 0x9c53078e, 0x36372706, 0x02063337, 0x6c822307, 0x2327262f, 0x010e0706, 0x3df80207, 0x266e3afb, 0x07704e98, + 0xc6313034, 0x0e190230, 0x68020834, 0x3408db79, 0x7967030e, 0x7c445bfc, 0x050e4805, 0x1e3a2e35, 0x2262621e, 0x4c36563d, 0x03273837, 0x384a1a49, + 0x4d201f3a, 0x5e0807eb, 0x6a1a9001, 0x2e0d6e1a, 0x03171809, 0x0b0a2408, 0x0e790f36, 0x23090a36, 0x23310309, 0x6e1c6b23, 0x390d7a1b, 0x02191910, + 0x3b101919, 0xb6b6f80d, 0x70bf0170, 0x7041fe70, 0x94943b02, 0x641b3494, 0x02fffd0f, 0x0f8dfe9f, 0x01021b64, 0x570361fd, 0x2f0c343d, 0x16162b0b, + 0x2e27313a, 0x5905060b, 0x1a350694, 0x4102321b, 0x17172822, 0x0a2a2a2c, 0x515c130d, 0x1e364d2f, 0x334b830b, 0x6f2ddb3b, 0x29981e70, 0x3c3cdc2b, + 0x94252bdc, 0xa2a1de25, 0x32235f83, 0x82663fea, 0xea3f3700, 0x01040032, 0x050eff18, 0x000f05e8, 0x003a001b, 0x00570043, 0x7d412500, 0x3021221a, + 0x05fc5423, 0x3e230724, 0x0a823701, 0x27089843, 0x1e07010e, 0x33110101, 0x2007c543, 0x06764111, 0x27823320, 0x0806b543, 0x320ccd36, 0x7867030c, + 0x0c310ddb, 0x03796803, 0x6d1b7f3f, 0x6b1b021b, 0x9124781b, 0x238b2324, 0x19661980, 0x1a671a02, 0x248c2478, 0xd9fb9325, 0x0111016c, 0x269a2601, + 0x08078043, 0x9a277723, 0x5f178127, 0x02fffd18, 0x188efe9e, 0x0002175f, 0xb22d62fd, 0x2db22c2c, 0x3639e539, 0xa72936db, 0x3e028229, 0x3936da36, + 0x022903e6, 0x60c2fd9e, 0x01440801, 0xc732430f, 0x32c73131, 0x43f2fe43, 0x8200f6fe, 0x01062200, 0x20f38236, 0x2ff382ca, 0x001e0014, 0x0032002a, + 0x00520046, 0x23300500, 0x22055744, 0x82061415, 0x5d1720cb, 0x27220f19, 0x30650723, 0x070b5008, 0x4f030221, 0x02220e3f, 0x704e2103, 0x6da33a0b, + 0x685a2101, 0x61184142, 0x5a167918, 0x2aac8516, 0xac292f2e, 0xfa3db603, 0x0a66423b, 0x10401034, 0xfd4010c5, 0x716e6e79, 0x0b114811, 0x0b040a2b, + 0x09820b2c, 0x6f706e24, 0x9c4e6801, 0xf2200809, 0x5d6c9e02, 0x2f145f48, 0xb52d2fbc, 0x292b5b2d, 0xd52b282e, 0xb6b696fe, 0x6fbf0170, 0x7041fe6f, + 0x2506fd50, 0x4f015901, 0x554f4f01, 0xb1fe240b, 0x5402b1fe, 0x003007ae, 0x00a90002, 0x03570663, 0x001d00a9, 0x25000033, 0x2005d341, 0x058d5106, + 0x210eb641, 0x0d500516, 0x08194905, 0x33304008, 0x03301530, 0x9027a076, 0x64640413, 0x35c21896, 0xa017bc33, 0x02207f20, 0x96208120, 0x8a33be17, + 0x0aeb2d01, 0x164f411c, 0x41955003, 0x4163ccce, 0xa7a720ed, 0x53330127, 0x2426014f, 0x8234d034, 0xfe243402, 0xd7d74fdb, 0x1e720271, 0x02175446, + 0xfd47a148, 0x1000712b, 0x8101e71e, 0x28010634, 0xd80502ff, 0x0f001a05, 0x43002500, 0x74005600, 0xd3537c00, 0x335b4f11, 0xa14e0120, 0x07a6512b, + 0x6d670228, 0x01fb01c7, 0x704f4173, 0x6b822107, 0x2205824f, 0x4f27416b, 0x27250979, 0x16164027, 0x2c00820d, 0xfd401616, 0x4c6b41d0, 0x6b4c2929, + 0x21078741, 0x1c874126, 0x28264134, 0x0c17163f, 0x170c0d0d, 0xed023f16, 0xfc01c86c, 0xcf514c01, 0xb7fd3605, 0x822c2b2d, 0x2c815554, 0x2c2d2d2c, + 0x82a9812c, 0x612d2b2c, 0x06c04e1a, 0x4e242521, 0x242105c0, 0x06cd4e25, 0x01031a24, 0x2e83572d, 0x2d2d5823, 0x202c8258, 0x4f2b8a57, 0xf94e09c2, + 0x011a2205, 0x083352e8, 0x33000736, 0xc206fdfe, 0x0c000905, 0x3c001600, 0x86006100, 0xa2009c00, 0x6119b95a, 0xf4510f8b, 0x5a012014, 0x0b210683, + 0x1a7f5a01, 0x112c24a5, 0x36321614, 0x11331135, 0x020e0714, 0x2005934d, 0x063e5b11, 0x57090124, 0x284a2b2c, 0xad022a08, 0x21215e3a, 0x21212323, + 0x05b2612f, 0x390c1d52, 0x200e390e, 0x01c30269, 0x080b0105, 0x8282081f, 0x0b081f07, 0x62010401, 0x8b5a1378, 0x20072905, 0x12471208, 0xc0fb6277, + 0x0c202282, 0x82261d82, 0x081e0882, 0x2283020c, 0x12491222, 0x08253382, 0x12072008, 0x33228248, 0x8e386401, 0x0c0c6538, 0x577f5835, 0x020c0c36, + 0xf8f46636, 0x080fc15a, 0x2b27b120, 0x83b6842a, 0x14132b2a, 0x084a2525, 0x32390820, 0x53a1535e, 0x093c365e, 0x4f4a0821, 0x925aa801, 0xf0fe2305, + 0x905a1001, 0xff042117, 0x320823a1, 0x4efe9f02, 0x514d4d51, 0x5efeb201, 0x41323244, 0x32411f1f, 0xa2014432, 0x9f0261fd, 0x005abbfd, 0x01040000, + 0x0502ff4d, 0x001a05b3, 0x00510017, 0x529d008b, 0xbb50059f, 0x09d54a09, 0x4d14aa50, 0xfe482341, 0x297a4d0e, 0xf14e0120, 0x85012110, 0x38095a46, + 0x76544b01, 0x0c320c2a, 0x362c2a24, 0x17174240, 0x0a280a3b, 0x10313161, 0x06d94811, 0x0c330c23, 0x0653621a, 0x5d653b29, 0x1f201010, 0x848bfd5a, + 0x230d2332, 0x32862b2b, 0x620a272e, 0x11113131, 0x4c36563c, 0x0d283738, 0x3c213289, 0x2b328264, 0x015b1f1f, 0x0d310ce2, 0xdb796802, 0x03230682, + 0x46f27967, 0x0b320692, 0x2e0c343d, 0x16162b0c, 0x18273139, 0x07020b17, 0x5e4d1202, 0x2822250d, 0x2b2c1717, 0x210b5e4d, 0x33b16203, 0x487e0121, + 0x002f0f3c, 0x00020000, 0x065400cf, 0x00b80331, 0x4e4a0034, 0x262010d9, 0x5c05666a, 0x5e4a0880, 0x07405209, 0x01020e22, 0x080d3543, 0x022e2787, + 0x02021135, 0x0f34946a, 0x352d0f3f, 0x52504436, 0x2525493a, 0x2a3d3d7b, 0x5e446b4c, 0x5b314646, 0x465d2104, 0x21282847, 0x7e4b4420, 0x704e2974, + 0xc552f901, 0x11108551, 0x3bae764b, 0x55214b3b, 0x3a0f404d, 0x1b1c360f, 0x3a313d48, 0x1706070e, 0x3865393a, 0x21213e59, 0x03513e21, 0x1c1d322a, + 0x1a1a3438, 0x7318100d, 0x44613a65, 0xfd540325, 0x6c6c6bfc, 0xfe04026b, 0x44435c11, 0x16152b57, 0x015c8658, 0x10db82ef, 0x3d07732d, 0x08ff0a39, + 0x1505f606, 0x18000e00, 0x49002b00, 0x5d005300, 0x00006300, 0x5c333001, 0x2b200950, 0x23067e5a, 0x11012b26, 0x5c2ce24c, 0x113a146b, 0x15211133, + 0x41ed7602, 0x29252568, 0x68252529, 0x4beded41, 0x804b5c5c, 0x985b49fe, 0xa7013c3a, 0x7e2a292b, 0x292a7ea7, 0x5b5f602b, 0xfe5f5b6b, 0x2df70221, + 0x54558257, 0x822d5782, 0x82a92007, 0x1b612906, 0x24251919, 0x242f692f, 0x8807bf4c, 0x0e825c0c, 0x02c0fd2e, 0x61c2fd9f, 0x03000500, 0xfd06ab00, + 0x200aa756, 0x34a75659, 0xbd4c0520, 0x56082024, 0xae6b11c0, 0x56a4200d, 0x962006c0, 0x2105744c, 0x744c8307, 0x67612007, 0xd4560f00, 0x13a73a2b, + 0x4b13134c, 0x01effe12, 0x124a1211, 0xfe134a13, 0x279e0263, 0x5916279c, 0x82028216, 0x62fd2208, 0x31008200, 0x00220003, 0x03de06ab, 0x001b0061, + 0x006c0033, 0x41531300, 0x15e7561c, 0x2114114c, 0x5259012e, 0x0fbc5c10, 0x4d53d720, 0x0cdb2805, 0x68030c32, 0x56400179, 0x4c200abc, 0x212fb85c, + 0x214b2a02, 0x0218240a, 0x4b62fd01, 0x0f6e09a1, 0x27b55c08, 0x00002508, 0xff7c0107, 0x05840502, 0x0037001a, 0x0068005b, 0x009c0075, 0x00d500a9, + 0x21300100, 0x3307010e, 0x023e3736, 0x2e078866, 0x23060701, 0x012e2722, 0x37272627, 0x821e1716, 0x05157917, 0x07062222, 0x3e231482, 0x66213701, + 0xbf663a5e, 0x0d246b07, 0x33011e22, 0x2117246b, 0x71823521, 0x24052960, 0x0e222326, 0x82838302, 0x3202233c, 0x01821716, 0x8206c979, 0x213708c2, + 0xd7fe2603, 0x06020c03, 0x200d0d0b, 0x4b2b1c2b, 0x10101f36, 0x392c2d3d, 0x3423232d, 0x4f101314, 0x230d0e0c, 0x413b1c2b, 0x342b3b41, 0x0f3c0f12, + 0x01041104, 0x6669017c, 0x3b37245d, 0x3717fe3b, 0x11103c58, 0x5523232c, 0x2e389029, 0x1622212d, 0x6b010c17, 0x1e21097d, 0x067d6b22, 0x41415908, + 0x39fe5003, 0x33248f23, 0x323a1b1b, 0x141f291c, 0x103f1006, 0x3812120b, 0x1b50664c, 0x190f0e1c, 0x201e1d2d, 0x011a691a, 0x274e0152, 0x1014289e, + 0x1c0c1710, 0x32314e36, 0x113d2a2a, 0x210a0a10, 0x3e181516, 0x19111014, 0x0b3a410d, 0x1421413a, 0x3e020902, 0x57fd3ef9, 0x21374e66, 0xe66bd101, + 0x27262d0b, 0x2d2e2828, 0x14010135, 0x1d101b23, 0x0812e66b, 0x7f20683f, 0x2a2a2e20, 0x38310c2e, 0x16271c11, 0x20061906, 0x1b2e1d1d, 0x241b1a1e, + 0x45282b25, 0x1c1c1c3e, 0x00175a17, 0x01060000, 0x0502ff7a, 0x001a0586, 0x00360028, 0x0092006c, 0x00ca009f, 0x0c406800, 0x22263f68, 0x42062125, + 0x3e241883, 0x031e3701, 0x6906345f, 0x13220738, 0xa75e2521, 0x196b6d14, 0x36352122, 0x42068861, 0x52201e45, 0x35054568, 0x22225a39, 0x04030d2c, + 0x260e0e0a, 0x242b1f30, 0x111e3424, 0x44681e11, 0x3d450805, 0x3bec1010, 0x3a3b4141, 0x34034242, 0x0c05d6fe, 0x0c0d0b07, 0x2b1d2a21, 0x101f364a, + 0x2c2d3e0f, 0x23222d39, 0x10141335, 0x0d0d340e, 0x1c2b221b, 0x3c40403c, 0x0e12342b, 0x12070f3c, 0x10fd7c01, 0x103c5838, 0x2c238211, 0x38902955, + 0x22212e2d, 0x030c1716, 0x07c76d03, 0x2205cd5e, 0x42754141, 0x6b250549, 0x1c1b336b, 0x0548423b, 0x42400f21, 0x1c2e0748, 0x190f0e1b, 0x201d1e2c, + 0x011a691b, 0x3068b352, 0x49f2222d, 0x147542a4, 0x0a2a0a24, 0x76422114, 0x0174230b, 0x3b42b901, 0x605f212d, 0x38243a42, 0x39000600, 0xc70608ff, + 0x0f001505, 0x28001c00, 0x57003200, 0x00007a00, 0x07d15805, 0x30133034, 0x17121633, 0x30272603, 0x30070623, 0x2e333007, 0x536e0101, 0x0f995315, + 0x53132575, 0x16251473, 0x17011e17, 0x05884433, 0x0423113c, 0x06240a1e, 0x0a2406e2, 0x2182c964, 0x0af12187, 0x0a0b0c0b, 0x290bb13c, 0x306e9bfc, + 0x0230080d, 0x215d3bad, 0x21242421, 0x3b2e2f21, 0x1f343355, 0x3f114a0d, 0x4c4c4434, 0x12433544, 0x1f0e380e, 0x01c4026a, 0x090c0105, 0x82820421, + 0x0c092203, 0x623e0c82, 0x09373678, 0x18080521, 0x15520517, 0x23f86277, 0x7f18187f, 0x709f0223, 0x017041fe, 0x008231e0, 0x8c24d324, 0x4e53a501, + 0x2907211d, 0x3f124d53, 0xfe095216, 0x081001f0, 0x4b141651, 0x0263fe12, 0x1975769f, 0x42420e5d, 0xfd2fb10b, 0x07000061, 0x2c086741, 0x00200016, + 0x0035002a, 0x0064003f, 0x08077f87, 0x15070623, 0x061c6c32, 0xa5710e20, 0x3d362305, 0xa3713401, 0x7301200c, 0x785613ff, 0x1e9b7605, 0x08227541, + 0x01980222, 0x37504e10, 0x18171d34, 0x16090a22, 0xfe223928, 0x25a766d5, 0xa7252626, 0x25252197, 0x3bfd9721, 0x3f507d41, 0x5459a701, 0x0c054841, + 0x1c280b0b, 0x472b211c, 0x275a1c33, 0x272c2c2b, 0x2b282457, 0x1d012329, 0x34518541, 0x01050000, 0x0502ff27, 0x001a05d9, 0x0034002a, 0x0071003c, + 0x7314827d, 0x3e240959, 0x16323302, 0x6a197176, 0x7b5410e7, 0x085e650e, 0x5d0d5d65, 0x0c5b0d7c, 0xf402210a, 0x261f8676, 0xfb3c7302, 0x6be56f3a, + 0x402607ed, 0x4010c611, 0xea76b6fd, 0x135d6b12, 0x215e6426, 0x2e015a3f, 0x2009d354, 0x0796768c, 0x762d5921, 0x652e0896, 0x10100808, 0x5d341f2f, + 0xb6b69bfe, 0x345b9e02, 0x544e200b, 0x2f220a51, 0xf376020b, 0x0982540e, 0x2f515c25, 0x6f1d374d, 0x003d09fd, 0xce010200, 0x320516ff, 0x21000e04, + 0x00003b00, 0x22233005, 0x30352627, 0x27012e35, 0x42088226, 0x322f05b0, 0x1e171617, 0x02141501, 0x27331507, 0x74013e32, 0x2e220598, 0x436f2302, + 0x1e280808, 0x8e700402, 0x55272855, 0x1b2f2f88, 0x39383e19, 0x50c45050, 0x3e383950, 0xf09aa0bb, 0x1244603a, 0x44121313, 0x5f3b3a60, 0x5f310985, + 0x492b2cea, 0x414d0b4c, 0x735c5b41, 0x4142c27f, 0x34008222, 0x7fc24241, 0x1cfcfee1, 0x4c28f267, 0x9e453737, 0x4c373745, 0x290a8a28, 0x13000300, + 0xed06b700, 0x07825603, 0x51003d22, 0x20056167, 0x0f5f6103, 0x2206944b, 0x58033003, 0x30230753, 0x6c113023, 0x11230f2b, 0x82213023, 0x30352201, + 0x08058401, 0x15302123, 0x21070206, 0x72cfe330, 0x0d175d17, 0x70175b17, 0x03228a22, 0x01050109, 0x081e080c, 0x1f088282, 0x05974407, 0x12491236, + 0x08082007, 0x12081f08, 0x62771248, 0x41fe9a02, 0xcefe4101, 0x32067662, 0x010301b7, 0x31c8329b, 0x4432c831, 0xfe45eefe, 0x4b9d01fd, 0x7d622187, + 0x00002809, 0x004c0004, 0x82b406ab, 0x001426e3, 0x0028001e, 0x5de5835f, 0xa6421ddb, 0x01112107, 0x2032994a, 0x06e45db9, 0x5d601921, 0x2e2408e4, + 0x1302ac2a, 0x27075668, 0xd7fe5803, 0x07030c03, 0x2123244a, 0x244a0e13, 0x5db72006, 0x28270eec, 0xd52b292e, 0x680296fe, 0x402a065a, 0x14279e28, + 0x0c171010, 0xc549351d, 0x1011210f, 0x200ec549, 0x30008200, 0xff730104, 0x058d0502, 0x004c001a, 0x00b4007d, 0x171772e0, 0xe24a1720, 0x06072106, + 0x15220184, 0xf24a011e, 0x220d8405, 0x57222306, 0x27210511, 0x72178237, 0x324b0c22, 0x2627210a, 0x4b06596b, 0x01230636, 0x86323637, 0x23548555, + 0x2107010e, 0x20336841, 0x49648a01, 0x02211d51, 0x11926750, 0x28286208, 0x11101d1d, 0x12120a0a, 0x1d1a1718, 0x0b131333, 0x2011110b, 0x362c2c1f, + 0x1b24232f, 0x0f15141c, 0x111c0d4f, 0x3d1d2d12, 0x47404542, 0x39fe8003, 0x33238f24, 0x1e1d1b1c, 0x0f2a1b32, 0x050a0b0f, 0x0b0f4010, 0x26371311, + 0x1b516626, 0x190e0e1c, 0x1e1d1616, 0x1b691a20, 0xa7fd5201, 0x0b03d6fe, 0x08934903, 0x0f1e3735, 0x2d2c3e10, 0x23222d39, 0x0f141434, 0x0e0d0d4e, + 0x491b2c22, 0x0f210692, 0x05074c3b, 0xfe500224, 0xb34b2438, 0x1e2a2107, 0x2008b34b, 0x205e854b, 0x08b34b18, 0x08103572, 0x170b0c27, 0x291f2116, + 0x14191a20, 0x060d0c14, 0x151a0604, 0x251c1d14, 0x1a24242c, 0x0a0e0e19, 0x1511110a, 0x143e1716, 0x093d7222, 0x614b7520, 0x1c1c2609, 0x140e0e11, + 0x07644b13, 0x4b0e0d21, 0x29230565, 0x4b1f1f44, 0x05210566, 0x2b224c46, 0xbb4b6220, 0x4f042028, 0x56280627, 0x2e000f00, 0x8f033700, 0x6f135b5e, + 0x0526243f, 0x33270226, 0x4618011d, 0x45182235, 0x38c9158a, 0x26a65fa6, 0x194a4618, 0x46181520, 0x0122153d, 0x45183d37, 0x14941477, 0x11b34518, + 0x38a61191, 0x5fcc26a6, 0x18333521, 0x28103f46, 0xb2663a01, 0x0601c901, 0x05f6706b, 0x70272821, 0x012312f6, 0x185f18eb, 0x2d4f0346, 0x040e0301, + 0x040e040c, 0x7c0e380e, 0x0782390e, 0x52044618, 0x19640128, 0x0f7a1862, 0x6b820f39, 0x0e040b3a, 0x0f3d0f03, 0xbbfdfc02, 0x59594502, 0x4242b0fd, + 0x3c3d3c7f, 0x302f3837, 0x21077a71, 0x7a713430, 0x104a200a, 0x5503da45, 0x0000002f, 0x00470005, 0x03b906ab, 0x001e0061, 0x280b822f, 0x00700058, + 0x010e0100, 0x0ad76c07, 0x2005c67a, 0x6c148233, 0x3e7b06d9, 0xa4052024, 0x10013925, 0x6c103d0f, 0x4010046c, 0x1e791e10, 0x289f287e, 0x832bad2b, + 0x63028822, 0x2718d27a, 0x0f070606, 0x52023110, 0x2208209e, 0x4612f001, 0x9e02cf12, 0x5115acfe, 0x24912514, 0x412fbc2f, 0x3441fefe, 0x2ef0fed1, + 0x53404059, 0x7a414052, 0x1b350679, 0x762b4934, 0x1b34482c, 0x2424341b, 0x252b762c, 0x1b1a1b23, 0x2224a35c, 0x4e060000, 0x2f3a0893, 0x63004200, + 0x98008d00, 0x0000a000, 0x30213005, 0x36373035, 0x30353637, 0xdb493435, 0x37362a0a, 0x1e32013e, 0x15161701, 0x05814a14, 0x010e0725, 0x56052107, + 0x3521052a, 0x211d8234, 0x937c3637, 0x4d3d8306, 0x26210555, 0x20018227, 0x752f8522, 0x162305dd, 0x18011617, 0x4e085142, 0x122022f7, 0x240cf279, + 0x39fe4503, 0x091153d6, 0x2c055a55, 0x664c1c1c, 0x0f0e3750, 0x17160c0d, 0x0613531d, 0x3f490126, 0x0e3c2e2e, 0x23080082, 0x3f2e2e3c, 0x7c76767c, + 0x10181922, 0x0606070f, 0x18100f07, 0x19184519, 0x0706100f, 0x0f100607, 0x89fe1819, 0x26200e4f, 0x3afb3d74, 0x4f72726e, 0xf22c0c0f, 0x2a2ebf68, + 0x310c2f29, 0x261d1138, 0x2a06c349, 0x1e1b1717, 0x2b252435, 0x49222328, 0x672207c3, 0x2e7c1817, 0x18173307, 0xb6b8feb7, 0x1a0e0e5c, 0x2d24231a, + 0x24242c75, 0xa1821a1a, 0x1b1a0e29, 0x752c2423, 0x8323242d, 0x7703210e, 0x231f2d4f, 0x4f014f01, 0x080a2f4f, 0x00000020, 0xfe0b0106, 0x05f50568, + 0x001f000f, 0x00410039, 0x0055004b, 0x0100006f, 0x26222330, 0xfd4e013d, 0x4e062014, 0xf6671afd, 0x77012007, 0x05201287, 0x0d654118, 0x23112e08, + 0x33133311, 0x11333736, 0x5fe50223, 0x5b383539, 0x1211201f, 0x3625262a, 0x35368235, 0x7c292626, 0x27a1676b, 0x0c0d2d40, 0x402d0d0c, 0x2d098227, + 0x0c0d0d0c, 0xed02402d, 0xfb01c76d, 0xff793bfb, 0x15042b0c, 0x09230905, 0x18186219, 0x08821962, 0x80670539, 0x595a06b4, 0x68fe677b, 0x0732313a, + 0x3d2b2b34, 0x81554c3e, 0x82172b2c, 0x2c2b3d00, 0xad965581, 0x1aa24513, 0x2d252533, 0x24252e6a, 0x331b1b33, 0x6a2e2524, 0x3325252d, 0x2108ec67, + 0xe2762401, 0x2e292d09, 0x123e2b28, 0xb32c1249, 0x2cb32d2d, 0xfd390882, 0xfe9e02ff, 0xfdaaabab, 0x00000062, 0x00850002, 0x037b0654, 0x003400b8, + 0x357f644d, 0x08131a41, 0x1133133d, 0x69b70123, 0x3e103594, 0x35362c10, 0x39515144, 0x7a252649, 0x4c2a3e3d, 0x465f436c, 0x035b3145, 0x48465d21, + 0x21202728, 0x757e4a44, 0x03714e29, 0x2c0b06fa, 0x2381130b, 0x82138124, 0x82062708, 0xe007e1a1, 0x8c64829a, 0x8f022a2d, 0x22175a17, 0xec4040ec, + 0x38088222, 0x46037ffd, 0xaa0156fe, 0x0000bafc, 0x00b50003, 0x034b0663, 0x000b00a9, 0x19ad7b15, 0x76010921, 0xb63c11d5, 0x7f746801, 0xd8e1747f, + 0x34393934, 0xfe5a03d8, 0x58178ced, 0x0f370a17, 0x0a380f05, 0x88340982, 0x632eba2e, 0xe6894603, 0x01b2fe89, 0x3a3236c5, 0x31fd3632, 0x2205a176, + 0x7637c825, 0x00200aa1, 0x1b500082, 0x45612007, 0x7e2006bf, 0x4e46bd45, 0x210834ce, 0x0f3d1015, 0x10036d6d, 0x781f1040, 0xa0287f1e, 0x2bac2b27, + 0x02872283, 0x3b5c3f63, 0x0f0e0e0f, 0x43445c3b, 0x1f312805, 0x311f0d0d, 0x441f3145, 0x3122053b, 0x50501a03, 0x3edf4532, 0x50ef0121, 0x053a2c71, + 0x02ff5b01, 0x1a05a505, 0x3e003400, 0x81004800, 0x0000a200, 0x27262205, 0x9f793736, 0x103f5407, 0x2e070225, 0x54222301, 0x4b180f3f, 0x45891465, + 0x76290169, 0xb1570d59, 0x17162a0e, 0x5002010e, 0x252a7655, 0x14c77f26, 0x1e1a0b23, 0x0dc87f09, 0x442d0121, 0xb3350545, 0x2e2e2aac, 0x2cfeac2a, + 0x262b7654, 0x2b2a2425, 0x18424037, 0x11406916, 0x69190d21, 0x74760e40, 0x25210805, 0x58416924, 0x2e2d2275, 0x4e3b4811, 0x3d4e5858, 0x4d0a134b, + 0x3dfd7922, 0x2b232334, 0x31391616, 0x0e8c5427, 0x1b160a23, 0x128d5408, 0x07bf4b18, 0x282e2925, 0x8918012b, 0x2650693d, 0xa9b12a08, 0x2d404055, + 0x4b4d2d2c, 0x39301919, 0x5d665d6a, 0x06313e69, 0x0052492f, 0x00040000, 0x06b7002e, 0x005603d2, 0x0027000f, 0xe7411836, 0x15d55e15, 0x2d611320, + 0x6c622518, 0x70fc01c8, 0x2b09ad55, 0x6940ed83, 0x28282525, 0x40692525, 0x2005f660, 0xe8411881, 0x70022009, 0x022705bb, 0x2a2a2a9e, 0x827da87d, + 0x5e602705, 0x5e5b6c5b, 0x9e8222fe, 0x0106002d, 0x0502ff4b, 0x001a05b5, 0x821e0015, 0x004930ab, 0x00d8016e, 0x35300500, 0x35302130, 0x74371236, + 0x152e053d, 0x15302330, 0x33302530, 0x23301130, 0x145e010e, 0x183e2007, 0x2008d344, 0x073d4801, 0x22012e23, 0x07197e06, 0x01021e24, 0x006a2622, + 0x06816d08, 0x1d060722, 0x0e814618, 0x1133352b, 0x15010f23, 0x0f150e0f, 0x8205840d, 0x270b9108, 0x27012e05, 0x0b3f3537, 0x0a200284, 0x0b880584, + 0x17930620, 0x1133073c, 0xa1021533, 0xb52dc9fe, 0x5c5c8e2d, 0x07dbbffe, 0xaf028e23, 0x0e3c5c3e, 0x44181d0f, 0x072009bd, 0x0f260082, 0x3145310f, + 0x09820f10, 0x311f0739, 0x9683eefd, 0x41684927, 0x213b3b58, 0x11103c0f, 0x2c4d3b48, 0x182c2d2d, 0x220ad849, 0x1808bc2f, 0xeb2e3b4c, 0x2c0b222e, + 0x206ed20b, 0x2453d202, 0xa3a60101, 0xa54a18f2, 0xe2fe290e, 0x4140582f, 0x58835152, 0x07aa4518, 0x2c25243b, 0x24252b75, 0x1c1c1a1a, 0x25241a1a, + 0x252c752b, 0x031c3324, 0x55a9b106, 0x61481880, 0x3e342c0f, 0x09230931, 0x0b292949, 0xd4f5015a, 0x2254d0b1, 0xd00a280a, 0x41508953, 0x5f8655b6, + 0x5abcfd23, 0x08008200, 0x5c000235, 0xa4065f02, 0x03007105, 0x00001b00, 0x21152113, 0x17372701, 0x37153335, 0x15330717, 0x27071723, 0x07352315, + 0x35233727, 0xf948065c, 0x8cc204b8, 0x823c8c2a, 0xc6c62103, 0x02240887, 0x220246a5, 0x1a84118c, 0x01000026, 0x25ff5c00, 0xd6225b82, 0x59821400, + 0x2c323808, 0x35053e01, 0x032e3411, 0x5c23022c, 0x0134019d, 0xadc7f605, 0x3c2e5b7f, 0xfeccaa6f, 0xfee5fef8, 0xa502ada9, 0x7e725633, 0x47667a83, + 0x154ff90e, 0x91907d5b, 0x823e6886, 0x895b2047, 0x22012447, 0x832e012c, 0x823e2047, 0x06332747, 0xccfe9da4, 0x4889fbfe, 0x01080125, 0x9657011b, + 0x32ef8248, 0xff2a0005, 0x05d60638, 0x00040094, 0x00440040, 0x8208014b, 0x21302b9b, 0x22042107, 0x0e07030e, 0xeb570704, 0x34352e06, 0x17323336, + 0x3e32041e, 0x043e3703, 0x240c8337, 0x15161716, 0x391e8314, 0x2103032e, 0x15012115, 0x23250523, 0x3e342535, 0x16323302, 0x1e323617, 0x26821703, + 0x83061421, 0x32362105, 0x17250583, 0x3e37013e, 0x20478203, 0x83408436, 0x011e2228, 0x840c8631, 0x32332317, 0x2983021e, 0x15215b85, 0x29618514, + 0x012e2726, 0x2e353435, 0x6e832702, 0x23010e2e, 0x010e2722, 0x27220607, 0x030e0706, 0x06830c86, 0x0e820220, 0x2683b084, 0x2e210386, 0x08ba8202, + 0x0e34273b, 0x010e3501, 0x26272627, 0x34351617, 0x34370627, 0x22233637, 0xe4015c26, 0x0462fe46, 0x506154e0, 0x070f2f4f, 0x5b403c15, 0x934b522c, + 0x08354da7, 0x0f1b151d, 0x504f2f0f, 0x081b9a61, 0xe401ddd4, 0x10fe62fe, 0x01000196, 0x7afc9600, 0x1a240e04, 0x0e0b1b13, 0x080c171f, 0x1b110202, + 0x06021115, 0x2e380407, 0x0e101007, 0x0810020c, 0x130c0603, 0x06090a0c, 0x1339530b, 0x282c040d, 0x1e180c11, 0x1d0c2f41, 0x0e040d15, 0x2013241a, + 0x0c1e2714, 0x090a1114, 0x492f0909, 0x13012c21, 0x180f121a, 0x0f061004, 0x0f142521, 0x1c192a10, 0x12241017, 0x0e0d2109, 0x3b1a132e, 0x07071813, + 0x09380d06, 0x141b0e29, 0x340d111a, 0x0807122f, 0x0a111910, 0x1010160b, 0x0f171c10, 0x1a0c2218, 0x16110822, 0x1601030d, 0x03270617, 0x0801021a, + 0x01030110, 0x12070802, 0x6eb9021a, 0x52422fbb, 0x1d09173c, 0x184b424a, 0x4d4ea72d, 0x1d150f0d, 0x523c1716, 0x18972f42, 0x58019008, 0xfa25016e, + 0xa0faf0f0, 0x1a291d0d, 0x0e071b15, 0x0e111d11, 0x0b3a2617, 0x070d0c02, 0x10370505, 0x09100b09, 0x670c0804, 0x2741040d, 0x60373b1f, 0x11070e18, + 0x2617121d, 0x100f1509, 0x04435201, 0x05120438, 0x11083401, 0x2d0c1017, 0x04030b12, 0x0b423e08, 0x111d2f07, 0x10450b24, 0x331c1411, 0x090e1d0f, + 0x0c4d0a5d, 0x410c1818, 0x1617022d, 0x05081721, 0x141b0c11, 0x0e052226, 0x17031922, 0x0c0c0207, 0x02050203, 0x02020411, 0x82010402, 0x07022403, + 0x4300171a, 0x250807b3, 0x000b0049, 0x0100000f, 0x23153315, 0x23352315, 0x01353335, 0x05211521, 0x64b4b498, 0x28fbb4b4, 0xb8f94806, 0x0b834905, + 0xfdb46438, 0x0500465c, 0x3eff5300, 0xa7058c06, 0x28000600, 0x30002c00, 0x3d823700, 0x0b331129, 0x27113301, 0x83270723, 0x84032001, 0x020b2605, + 0x37021b35, 0x20018217, 0x29058413, 0x33213317, 0x35022b15, 0x2d853733, 0xa1042e08, 0xa0cecea0, 0x334ac936, 0x3f2f303f, 0x19373a3a, 0x37361031, + 0x36372727, 0x37193110, 0x2f3f3a3a, 0x4a333f30, 0x8af201c9, 0x7373bc8a, 0x082c8388, 0x01080247, 0xfe2201b0, 0x3a50fede, 0x72a93ed6, 0xfcfee3cd, + 0xdf6a8b54, 0x01d4fe92, 0x89eb0104, 0x0401eb01, 0xdfb7affe, 0xfe548b6a, 0x72cde3fc, 0x61d63ea9, 0x50fe3a61, 0x2201defe, 0x0000b001, 0xff5c0003, + 0x05a40637, 0x22bb82e0, 0x833b000d, 0x092326b7, 0x03112301, 0x22068411, 0x85012f01, 0x200585bf, 0x220f840f, 0x821f020f, 0x013f23c4, 0xc485011f, + 0x03280586, 0x1401c834, 0x98c81401, 0x023d0685, 0x4e7238d8, 0x60484b60, 0x27545a5a, 0x4554184b, 0x364b394b, 0x2539155a, 0x5a153925, 0x310c8236, + 0x4b185445, 0x5a5a5427, 0x604b4860, 0x5d01724e, 0x9484fcfe, 0x5d020422, 0xd183b982, 0xfefc3008, 0x04381bc8, 0x59512d42, 0x2a372166, 0x70844858, + 0x3b4b645a, 0x6c438f52, 0x528f4a65, 0x5a644b3b, 0x58488470, 0x6621372a, 0x422d5159, 0x82003804, 0x00023b00, 0x0622ff4b, 0x00d005b5, 0x002a0009, + 0x2d150500, 0x35211501, 0x0135010d, 0xad8b1713, 0xd98d3f20, 0x03070324, 0x7e836d01, 0x01260422, 0x26087a82, 0x6e4ed3fa, 0x32621f6d, 0x7d75756d, + 0x667d615e, 0x94494994, 0x5e617d66, 0x6d75757d, 0x6d1f6232, 0xa03e4e6e, 0x83a0cece, 0x03330803, 0xcd7efe54, 0x54b073ec, 0xb2cc426e, 0x318459a1, + 0x99353699, 0xa1598431, 0x6e42ccb2, 0x0190b054, 0x7efecd09, 0x00020000, 0x066aff5e, 0x007b05a2, 0x421f000b, 0x03300e5b, 0x0b270327, 0x37112702, + 0x1337021b, 0x15211337, 0x21066942, 0x7487c7fe, 0x02218687, 0x077742a7, 0xfef0fc33, 0xf7fe629d, 0x01befeb2, 0x8467fe65, 0xfe840705, 0x2f098267, + 0xf7feb2be, 0x749dfe62, 0x53000300, 0xaf063eff, 0x2c069342, 0x1300002f, 0x010d3521, 0x15032135, 0x05074121, 0x22058042, 0x8235021b, 0x09a04279, + 0x85030721, 0x42cb2026, 0x55220754, 0x8c424503, 0x363b2106, 0x210b8c42, 0x8c423b36, 0x42962006, 0x0428074b, 0xcecea003, 0x61fcfea0, 0x21268642, + 0x2f839bfe, 0x00820020, 0xc8000428, 0x38065eff, 0xb582a605, 0x1b000f2b, 0x00001f00, 0x15213525, 0x0b7f4303, 0x112e0b8b, 0x6c041123, 0xb4b4cc01, + 0xb4b464b4, 0x0684c0fc, 0x5a310227, 0x046464a0, 0x0595439c, 0x855cfe21, 0x0e022507, 0x4806b8f9, 0x00216282, 0x058f4701, 0x82a50221, 0x13002c63, + 0x5c211521, 0xb8f94806, 0x8246a502, 0x01053a1c, 0x0508ff56, 0x001505aa, 0x0039000f, 0x00650045, 0x0500008f, 0x23113335, 0x05715e06, 0x3311332a, + 0x07140115, 0x2307020e, 0x21058b55, 0x1a82012e, 0x18020e21, 0x20125250, 0x86501804, 0x08e84b0a, 0x280d1463, 0x011e3736, 0x06070617, 0x0fce6725, + 0x6a272621, 0x06321013, 0x01152107, 0x6507bc9c, 0x0b2c0b37, 0xa3a56e3d, 0xda671c02, 0x5b382c05, 0x0d2d2221, 0x0a010402, 0x18250e0f, 0x32122050, + 0x4275d9fe, 0xfe417542, 0x26958456, 0x57416949, 0x4e5b2276, 0x52180610, 0x5f0808aa, 0x39fee202, 0x1c3307cf, 0x1b323b1b, 0x05151e2a, 0x120a332c, + 0x664c3713, 0x0f1b1c50, 0x1d2c190e, 0x4f4f201e, 0x5bf85201, 0x3b6bf501, 0x430a270a, 0x5bbcfd77, 0x4049a501, 0x1b556b41, 0x29284d2a, 0x01013659, + 0x1a121214, 0x370e0f10, 0x2b31304c, 0x211e1e2a, 0x30441312, 0x3c3e9230, 0x3f3f3c0e, 0x01310482, 0x54a9b2ca, 0x4d2d5881, 0x392f314b, 0x5d655d6a, + 0x088b4a6a, 0x07b8682f, 0x2e2a292f, 0x1038310c, 0x1116271d, 0x0d9f6914, 0x1c1d1b39, 0x005c4344, 0x01040000, 0x0508ff2e, 0x001505c7, 0x00350013, + 0x7986004d, 0x11231303, 0x52300130, 0x302310b5, 0x86133033, 0x30112203, 0x4e218223, 0x276d1551, 0x2e012137, 0x37088e61, 0x230905c4, 0x19621809, + 0x09186218, 0x68050824, 0xb406b381, 0x2bfc687b, 0x323b3a6d, 0x609f02f8, 0xdcfe60bb, 0x48120102, 0x2cb32d12, 0x822db32c, 0xfffd2b08, 0xabfe9f02, + 0x61fd5501, 0x20846203, 0x0b61c322, 0x0ac44118, 0xec781720, 0x5c450805, 0x24242c51, 0x1b1a1b31, 0x0b2d0b32, 0x2c2e2822, 0x0a15152a, 0x515c130d, + 0x1b27262f, 0x01001e1b, 0xd3006b00, 0x31049506, 0x00004400, 0x15163201, 0x020e0714, 0x22230607, 0x2627052e, 0x22232627, 0x0e070607, 0x08108301, + 0x34352645, 0x3e373637, 0x1e323306, 0x17161705, 0x37323316, 0x013f013e, 0x4c06023e, 0x0b072a1e, 0x72326624, 0x42522a6e, 0x1b362f44, 0x70214913, + 0x4643305d, 0x0d101259, 0x2a1e1411, 0x140a0412, 0x4330381c, 0x8b264d40, 0x32310821, 0x15572a48, 0x1f0b0316, 0x1e2aca02, 0x41160f11, 0x1e723293, + 0x5539472a, 0x3184242e, 0x984643a8, 0x0a08151e, 0x141c1e2a, 0x2e221106, 0x2a443a52, 0x281d891d, 0x287c2a48, 0x130e0728, 0x3e008200, 0xff1e0003, + 0x05ce0649, 0x000b00cc, 0x004e002b, 0x06140400, 0x26222123, 0x21333634, 0x83222532, 0x3e0127b8, 0x17323301, 0x03822101, 0xde82e282, 0x21012729, + 0x01092722, 0x8413010e, 0x820620f3, 0x211582e6, 0x2c82012f, 0x32333625, 0x8503011f, 0x13172108, 0x3808e482, 0x151d0b02, 0x1d159ffe, 0x6101151d, + 0x158afe15, 0x6101021d, 0x19111a05, 0x0182010f, 0x010f1c8c, 0x151d078d, 0x82fe0f1c, 0x0f1977fe, 0xbcfeaafe, 0x0a9a1a05, 0x01011a0f, 0x08548203, + 0xd2050554, 0x050a0f0f, 0x03a38b05, 0x080e0a0f, 0x0e0111a4, 0x1d1d2a70, 0x1d5d1d2a, 0x04070715, 0x151410a6, 0xfd18effd, 0x150e0c80, 0x6802181d, + 0xfbd40115, 0x051410bb, 0x010a0f4a, 0x070403e1, 0x5b020404, 0x0f0a1007, 0x15013d02, 0x0f0a0706, 0x95e9fe0c, 0xf3840d09, 0xf3844620, 0xf3841420, + 0xf3ad5020, 0xf3830120, 0x25010f22, 0x1624e982, 0x05071415, 0x2506734b, 0x2330012f, 0xf8822726, 0x3f363726, 0x9c033601, 0x6f20f582, 0x9123f584, + 0xa2f9fc15, 0x52033ef5, 0x48040f0a, 0x04042d01, 0xfe110f0a, 0x0f0d89c7, 0xba05070a, 0x04030501, 0x6c020101, 0x3df7a307, 0x070b0e03, 0x01687906, + 0x05120b0e, 0x0f074d6d, 0x69030f0a, 0x08060503, 0x03020404, 0xf78a0cb4, 0xf784b020, 0xf7ae5220, 0x82363421, 0x361322f9, 0x24068337, 0x07060714, + 0x21048227, 0xf4832223, 0x31262722, 0x2306f141, 0x41051716, 0x5b20f982, 0xa523f984, 0xa354fb15, 0x0fb32ef9, 0x8d010f14, 0x0f140806, 0x02219605, + 0x2bf18312, 0x4f060709, 0x0a0f056b, 0x375a070d, 0x2908faa3, 0x0f0f0a8d, 0xbd54fe0a, 0x0a0f0806, 0x2dc70609, 0x01051305, 0x6a060601, 0x0a090691, + 0x4a790a0f, 0x00030000, 0x0649ff46, 0xf34105e2, 0x06ec4135, 0x37070222, 0x0f210985, 0x05854d01, 0x42233421, 0x1f2406eb, 0xce063601, 0x7320f982, + 0x8d23f984, 0xa2c7f915, 0xe6053ef9, 0x0f0a1206, 0xa3285f01, 0x0f0a0707, 0x0807e00b, 0x03070607, 0x0f026201, 0x4c06110a, 0x08f8a22a, 0x119e0451, + 0x04040a0f, 0x6b74e2fe, 0x0e0a0f04, 0x04049307, 0x01010704, 0x0a05040d, 0x7dcc110f, 0x02000000, 0x7cff7d00, 0xb6058306, 0x12000600, 0x11010000, + 0x17010b37, 0x06140011, 0x26012722, 0x17323634, 0xb7cc0501, 0x01b7e2e2, 0x163e2c0d, 0x821690fa, 0x70053505, 0x9afeb605, 0x01dffe46, 0x66014621, + 0x2c3e31fa, 0x16700516, 0xfa210582, 0x204f8d90, 0x294f881f, 0x23061401, 0x012c3122, 0x0f410027, 0x17162606, 0x05000112, 0x385c891e, 0xddfe011f, + 0xfebd14fe, 0x2c017588, 0x05291c1f, 0x0157016c, 0x1f120252, 0x386b8c2b, 0x052c1f12, 0x7801bdc6, 0x0706ae02, 0x1b242c1f, 0xa9fe82fd, 0x010aaefe, + 0x20c7842c, 0x20c7825f, 0x34c78583, 0x1b071105, 0x24112701, 0x37342622, 0x16323601, 0x05010714, 0x21c78376, 0xae8a1bfb, 0x6601a02b, 0xfe210146, + 0x9afe46df, 0x83d8841d, 0x85c583de, 0x224f87c7, 0x8600002f, 0x05f0444f, 0x3e373626, 0x24363706, 0x2606e243, 0x04060706, 0x8207020e, 0x07062202, + 0x0865860e, 0x1ffcfa28, 0x5331062c, 0x2a3f2322, 0x27493840, 0x01b60192, 0x1f04034c, 0xb61d272c, 0xa3d0e2fe, 0x461f3472, 0x33532b2f, 0x818c2608, + 0x0f1f2408, 0x5ad7760e, 0x7b569959, 0x92275d55, 0x2c0120a8, 0x032b1d1f, 0x865c4312, 0x9f3b6495, 0x78d96f78, 0x82001915, 0x00043400, 0x0644ff46, + 0x00b405ce, 0x000e000d, 0x00300020, 0x45330100, 0x23270a7d, 0x01032531, 0x4523010e, 0x09280a9d, 0x15163201, 0x06010714, 0x36231486, 0x4564ab04, + 0x87280a5d, 0xfeef6ffe, 0x111a05bc, 0x290a9542, 0x1f310215, 0x49fc0c2c, 0x06822916, 0x16b70324, 0x4a45ee02, 0x01a12609, 0x10bbfb48, 0x08664514, + 0x0284fe2a, 0x161f2c30, 0x2327fa12, 0x05220683, 0xa68223d9, 0x0001002e, 0x06090045, 0x001505cf, 0x3700001f, 0x201e2b46, 0x22104378, 0xd7450a20, + 0x8200201b, 0xff13286f, 0x05ed0654, 0x823e00af, 0x1732267e, 0x37011b16, 0x26038817, 0x07031702, 0x82270327, 0x010b2304, 0x03832707, 0x06033424, + 0x0f86010f, 0x37032c08, 0x3f303713, 0x37131701, 0x3f011a32, 0x018b0201, 0x32420239, 0x2c723939, 0x173f3939, 0x278c3939, 0x39847045, 0x1f2f4c39, + 0x825e3939, 0x3f6b2813, 0x03463939, 0x828c0706, 0x4824080e, 0x71543939, 0x39580724, 0x013d5539, 0x380a221a, 0x0239ae05, 0x47024bfd, 0x0bfd3838, + 0x38388d01, 0x530361fe, 0x20260582, 0x5602cbfe, 0x3282fb0d, 0x9760032a, 0x393985fb, 0x6ffe6502, 0xc8310582, 0x393924fd, 0x37da0208, 0xcffd6d5a, + 0x6d033939, 0x314a82fc, 0xfd06a005, 0x38ec649e, 0xf5e3fc38, 0x9f019a01, 0xdf82380b, 0xffa02008, 0x056006ce, 0x00070032, 0x15212500, 0x35231121, + 0x04940133, 0x7cbcfacc, 0x047846f4, 0x920078ec, 0x35212c23, 0x044c0221, 0xfe74fb14, 0x86ac01cc, 0x29279625, 0x5c030403, 0x14fe2cfc, 0x279e6402, + 0xa402bc28, 0x5cfde4fc, 0x279d1c03, 0x01740429, 0xfc9cfdec, 0x9dd403a4, 0x2c052927, 0x54fe3401, 0x8c04ecfb, 0x33232796, 0x82112315, 0xe40527c7, + 0x34fbf47c, 0xeb884405, 0xceff6328, 0x32059d06, 0xd0821f00, 0x15163226, 0x01011930, 0x42058045, 0x112006e9, 0x2c09f042, 0x23198003, 0x16117d02, + 0xfd152319, 0x21068320, 0x0c8483fd, 0x11e00233, 0x19233205, 0xcafdccfd, 0x230e2202, 0xfd121c19, 0x2306828a, 0xdefd6a04, 0x02230d84, 0x82000e76, + 0x00012a00, 0x063b0063, 0x00c5049d, 0x206b851a, 0x22628214, 0x87092722, 0x01303760, 0x09173236, 0x60063601, 0xfe0b2319, 0x123e1290, 0xc1fe51fd, + 0x0d821f12, 0x82700121, 0xaf02330d, 0x02123f01, 0x131923bc, 0x19f8fd10, 0xfecb0319, 0x0c83193d, 0x19080228, 0x0135fc19, 0x638219c3, 0x9f000322, + 0x6120cf82, 0x2124cf82, 0x47003400, 0x35256782, 0x07050e22, 0x24018506, 0x3e321523, 0x052b6105, 0x2136372a, 0x051e3235, 0x26070617, 0x2105d66b, + 0xfe6b1601, 0x15332505, 0x27052e22, 0x0b2f8482, 0x986b6825, 0x7748a48e, 0x348e8492, 0x8e101c30, 0x50fa2d0f, 0x57541d09, 0x4190797d, 0x5e542123, + 0x03212284, 0x2008865c, 0x301a8810, 0x0378ba04, 0x86642e1b, 0x92df86da, 0x0a163b84, 0x2a0e8f06, 0x45201302, 0x375d965c, 0x845e803b, 0x4ffd211f, + 0x78200886, 0xd5821987, 0xae000e24, 0x07820100, 0x02200285, 0x01240b86, 0x1f000d00, 0x02240b86, 0x3d000700, 0x03240b86, 0x99002900, 0x04200b86, + 0xdf202382, 0x05240b86, 0x0d010f00, 0x062a0b86, 0x37010c00, 0x01000300, 0x54850904, 0x01220b86, 0x09821a00, 0x02241785, 0x2d000e00, 0x03241786, + 0x45005200, 0x04200b86, 0xc3202382, 0x05240b86, 0xed001e00, 0x06240b86, 0x1d011800, 0x46304d83, 0x72007500, 0x61006e00, 0x65006300, 0x49002000, + 0x6f200782, 0x732e0f82, 0x75460000, 0x63616e72, 0x63492065, 0x0e826e6f, 0x1f825220, 0x2d826720, 0x2b826c20, 0x00007228, 0x75676552, 0x0882616c, + 0x6c004522, 0x63221982, 0x47827400, 0x3d826920, 0x4b002022, 0x65200f82, 0x20220f82, 0x51823a00, 0x1f856199, 0xb8823520, 0x32003122, 0x32220582, + 0x07823000, 0x00003332, 0x63656c45, 0x63697274, 0x65654b20, 0x203a2074, 0x0f82898c, 0x312d3527, 0x30322d32, 0x992a8232, 0x20bf8f5d, 0x26958256, + 0x00730072, 0x826f0069, 0x822020df, 0x0030246b, 0x842e0031, 0x00303007, 0x72655600, 0x6e6f6973, 0x31303020, 0x8230302e, 0x41598d10, 0x16411117, + 0x00002306, 0x008b0002, 0x0c8b0120, 0x63200b84, 0xca080582, 0x00020001, 0x01020103, 0x01040103, 0x01060105, 0x01080107, 0x010a0109, 0x010c010b, + 0x010e010d, 0x0110010f, 0x01120111, 0x01140113, 0x01160115, 0x01180117, 0x011a0119, 0x011c011b, 0x011e011d, 0x0120011f, 0x01220121, 0x01240123, + 0x01260125, 0x01280127, 0x012a0129, 0x012c012b, 0x012e012d, 0x0130012f, 0x01320131, 0x01340133, 0x01360135, 0x01380137, 0x013a0139, 0x013c013b, + 0x013e013d, 0x0140013f, 0x01420141, 0x01440143, 0x01460145, 0x01480147, 0x014a0149, 0x014c014b, 0x014e014d, 0x0150014f, 0x01520151, 0x01540153, + 0x01560155, 0x01580157, 0x015a0159, 0x015c015b, 0x015e015d, 0x0760015f, 0x45696e75, 0x86304630, 0x86312007, 0x86322007, 0x86332007, 0x84342007, + 0x30312107, 0x31212785, 0x21278530, 0x27853031, 0x85303121, 0x30312127, 0x35202787, 0x36202f86, 0x37200786, 0x38200786, 0x39200786, 0x41200786, + 0x42200786, 0x43200786, 0x44200786, 0x45200786, 0x46200786, 0x31210785, 0x20078630, 0x86078631, 0x8631207f, 0x8631207f, 0x8631207f, 0x8631207f, + 0x8631207f, 0x8631207f, 0x8631207f, 0x8631207f, 0x8631207f, 0x8631207f, 0x8631207f, 0x8631207f, 0x8631207f, 0x3032217f, 0x32217785, 0x86078631, + 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, 0x8632207f, + 0x8632207f, 0x3033217f, 0x33217785, 0x86078631, 0x8633207f, 0x8633207f, 0x8633207f, 0x8633207f, 0x8633207f, 0x8633207f, 0x8633207f, 0x8633207f, + 0x8633207f, 0x8633207f, 0x8633207f, 0x8633207f, 0x8633207f, 0x3034217f, 0x34217785, 0x86078631, 0x8634207f, 0x8634207f, 0x8634207f, 0x8634207f, + 0x8634207f, 0x8634207f, 0x8634207f, 0x8634207f, 0x8634207f, 0x8634207f, 0x8634207f, 0x8634207f, 0x8634207f, 0x3035217f, 0x35217785, 0x86078631, + 0x8635207f, 0x8635207f, 0x8635207f, 0x8635207f, 0x8635207f, 0x8635207f, 0x3935287f, 0x01000000, 0x4500ffff, 0x002607c9, 0x14000c00, 0x15820400, + 0x00000224, 0x03860100, 0x00240985, 0x31cbd6df, 0x002c1182, 0x9ffffde0, 0x00000000, 0xdb5c95e1, 0x9b97fa05, 0x0000342b, }; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 70fb9508e..a9efea15b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1036,6 +1036,11 @@ Pos=60,60\n\ Size=145,184\n\ Collapsed=0\n\ \n\ +[Window][Oscilloscope (X-Y)]\n\ +Pos=60,60\n\ +Size=300,300\n\ +Collapsed=0\n\ +\n\ [Docking][Data]\n\ DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,24 Size=1280,776 Split=Y Selected=0x6C01C512\n\ DockNode ID=0x00000001 Parent=0x8B93E3BD SizeRef=1280,217 Split=X Selected=0xF3094A52\n\ @@ -1872,6 +1877,15 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { dpiScale ); break; + case GUI_FILE_EXPORT_TEXT: + if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir(); + hasOpened=fileDialog->openSave( + "Export Command Stream", + {"text file", "*.txt"}, + workingDirROMExport, + dpiScale + ); + break; case GUI_FILE_EXPORT_CMDSTREAM: if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir(); hasOpened=fileDialog->openSave( @@ -2508,7 +2522,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 (y96) transposeAmount=96; } @@ -2872,7 +2886,7 @@ void FurnaceGUI::editOptions(bool topMenu) { if (!basicMode) { if (ImGui::BeginMenu("gradient/fade...")) { - if (ImGui::InputInt("Start",&fadeMin,1,1)) { + if (ImGui::InputInt("Start",&fadeMin,1,16)) { if (fadeMin<0) fadeMin=0; if (fadeMode) { if (fadeMin>15) fadeMin=15; @@ -2880,7 +2894,7 @@ void FurnaceGUI::editOptions(bool topMenu) { if (fadeMin>255) fadeMin=255; } } - if (ImGui::InputInt("End",&fadeMax,1,1)) { + if (ImGui::InputInt("End",&fadeMax,1,16)) { if (fadeMax<0) fadeMax=0; if (fadeMode) { if (fadeMax>15) fadeMax=15; @@ -2904,7 +2918,7 @@ void FurnaceGUI::editOptions(bool topMenu) { ImGui::EndMenu(); } if (ImGui::BeginMenu("scale...")) { - if (ImGui::InputFloat("##ScaleMax",&scaleMax,1,1,"%.1f%%")) { + if (ImGui::InputFloat("##ScaleMax",&scaleMax,1,10,"%.1f%%")) { if (scaleMax<0.0f) scaleMax=0.0f; if (scaleMax>25600.0f) scaleMax=25600.0f; } @@ -2915,7 +2929,7 @@ void FurnaceGUI::editOptions(bool topMenu) { ImGui::EndMenu(); } if (ImGui::BeginMenu("randomize...")) { - if (ImGui::InputInt("Minimum",&randomizeMin,1,1)) { + if (ImGui::InputInt("Minimum",&randomizeMin,1,16)) { if (randomizeMin<0) randomizeMin=0; if (randomMode) { if (randomizeMin>15) randomizeMin=15; @@ -2924,7 +2938,7 @@ void FurnaceGUI::editOptions(bool topMenu) { } if (randomizeMin>randomizeMax) randomizeMin=randomizeMax; } - if (ImGui::InputInt("Maximum",&randomizeMax,1,1)) { + if (ImGui::InputInt("Maximum",&randomizeMax,1,16)) { if (randomizeMax<0) randomizeMax=0; if (randomizeMax256) collapseAmount=256; } @@ -3607,17 +3621,30 @@ bool FurnaceGUI::loop() { if (!e->getWarnings().empty()) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } + int instrumentCount=-1; for (DivInstrument* i: instruments) { - e->addInstrumentPtr(i); + instrumentCount=e->addInstrumentPtr(i); + } + if (instrumentCount>=0 && settings.selectAssetOnLoad) { + curIns=instrumentCount-1; } nextWindow=GUI_WINDOW_INS_LIST; MARK_MODIFIED; } else if ((droppedWave=e->waveFromFile(ev.drop.file,false))!=NULL) { - e->addWavePtr(droppedWave); + int waveCount=-1; + waveCount=e->addWavePtr(droppedWave); + if (waveCount>=0 && settings.selectAssetOnLoad) { + curWave=waveCount-1; + } nextWindow=GUI_WINDOW_WAVE_LIST; MARK_MODIFIED; } else if ((droppedSample=e->sampleFromFile(ev.drop.file))!=NULL) { - e->addSamplePtr(droppedSample); + int sampleCount=-1; + sampleCount=e->addSamplePtr(droppedSample); + if (sampleCount>=0 && settings.selectAssetOnLoad) { + curSample=sampleCount; + updateSampleTex=true; + } nextWindow=GUI_WINDOW_SAMPLE_LIST; MARK_MODIFIED; } else if (modified) { @@ -4185,7 +4212,7 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("export ZSM...")) { exitDisabledTimer=1; ImGui::Text("Commander X16 Zsound Music File"); - if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,2)) { + if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,10)) { if (zsmExportTickRate<1) zsmExportTickRate=1; if (zsmExportTickRate>44100) zsmExportTickRate=44100; } @@ -4237,6 +4264,16 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } } + if (ImGui::BeginMenu("export text...")) { + exitDisabledTimer=1; + ImGui::Text( + "this option exports the song to a text file.\n" + ); + if (ImGui::Button("export")) { + openFileDialog(GUI_FILE_EXPORT_TEXT); + } + ImGui::EndMenu(); + } if (ImGui::BeginMenu("export command stream...")) { exitDisabledTimer=1; ImGui::Text( @@ -4809,6 +4846,7 @@ bool FurnaceGUI::loop() { workingDirZSMExport=fileDialog->getPath()+DIR_SEPARATOR_STR; break; case GUI_FILE_EXPORT_ROM: + case GUI_FILE_EXPORT_TEXT: case GUI_FILE_EXPORT_CMDSTREAM: case GUI_FILE_EXPORT_CMDSTREAM_BINARY: workingDirROMExport=fileDialog->getPath()+DIR_SEPARATOR_STR; @@ -4900,7 +4938,7 @@ bool FurnaceGUI::loop() { if (curFileDialog==GUI_FILE_EXPORT_ZSM) { checkExtension(".zsm"); } - if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM) { + if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM || curFileDialog==GUI_FILE_EXPORT_TEXT) { checkExtension(".txt"); } if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM_BINARY) { @@ -5136,8 +5174,12 @@ bool FurnaceGUI::loop() { displayPendingIns=true; pendingInsSingle=false; } else { // load the only instrument + int instrumentCount=-1; for (DivInstrument* i: instruments) { - e->addInstrumentPtr(i); + instrumentCount=e->addInstrumentPtr(i); + } + if (instrumentCount>=0 && settings.selectAssetOnLoad) { + curIns=instrumentCount-1; } } } @@ -5187,7 +5229,9 @@ bool FurnaceGUI::loop() { showError("cannot load wavetable! ("+e->getLastError()+")"); } } else { - if (e->addWavePtr(wave)==-1) { + int waveCount=-1; + waveCount=e->addWavePtr(wave); + if (waveCount==-1) { if (fileDialog->getFileName().size()>1) { warn=true; errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError()); @@ -5195,6 +5239,9 @@ bool FurnaceGUI::loop() { showError("cannot load wavetable! ("+e->getLastError()+")"); } } else { + if (settings.selectAssetOnLoad) { + curWave=waveCount-1; + } MARK_MODIFIED; RESET_WAVE_MACRO_ZOOM; } @@ -5267,6 +5314,27 @@ bool FurnaceGUI::loop() { case GUI_FILE_EXPORT_ROM: showError("Coming soon!"); break; + case GUI_FILE_EXPORT_TEXT: { + SafeWriter* w=e->saveText(false); + if (w!=NULL) { + FILE* f=ps_fopen(copyOfName.c_str(),"wb"); + if (f!=NULL) { + fwrite(w->getFinalBuf(),1,w->size(),f); + fclose(f); + pushRecentSys(copyOfName.c_str()); + } else { + showError("could not open file!"); + } + w->finish(); + delete w; + if (!e->getWarnings().empty()) { + showWarning(e->getWarnings(),GUI_WARN_GENERIC); + } + } else { + showError(fmt::sprintf("could not write text! (%s)",e->getLastError())); + } + break; + } case GUI_FILE_EXPORT_CMDSTREAM: case GUI_FILE_EXPORT_CMDSTREAM_BINARY: { bool isBinary=(curFileDialog==GUI_FILE_EXPORT_CMDSTREAM_BINARY); @@ -5383,6 +5451,11 @@ bool FurnaceGUI::loop() { ImGui::OpenPopup("InsTypeList"); } + if (displayWaveSizeList) { + displayWaveSizeList=false; + ImGui::OpenPopup("WaveSizeList"); + } + if (displayExporting) { displayExporting=false; ImGui::OpenPopup("Rendering..."); @@ -5892,6 +5965,29 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + if (ImGui::BeginPopup("WaveSizeList",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) { + char temp[1024]; + for (FurnaceGUIWaveSizeEntry i: waveSizeList) { + snprintf(temp,1023,"%d×%d (%s)",i.width,i.height,i.sys); + if (ImGui::MenuItem(temp)) { + // create wave + curWave=e->addWave(); + if (curWave==-1) { + showError("too many wavetables!"); + } else { + e->song.wave[curWave]->len=i.width; + e->song.wave[curWave]->max=i.height-1; + for (int j=0; jsong.wave[curWave]->data[j]=(j*i.height)/i.width; + } + MARK_MODIFIED; + RESET_WAVE_MACRO_ZOOM; + } + } + } + ImGui::EndPopup(); + } + // TODO: // - multiple selection // - replace instrument @@ -5992,7 +6088,7 @@ bool FurnaceGUI::loop() { ImGui::Text("Channels"); ImGui::SameLine(); ImGui::SetNextItemWidth(120.0f*dpiScale); - if (ImGui::InputInt("##RSChans",&pendingRawSampleChannels)) { + if (ImGui::InputInt("##RSChans",&pendingRawSampleChannels,1,2)) { } ImGui::Text("(will be mixed down to mono)"); ImGui::Checkbox("Unsigned",&pendingRawSampleUnsigned); @@ -6513,6 +6609,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 +7163,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); @@ -7527,6 +7625,8 @@ FurnaceGUI::FurnaceGUI(): sampleFilterRes(0.25f), sampleFilterCutStart(16000.0f), sampleFilterCutEnd(100.0f), + sampleCrossFadeLoopLength(0), + sampleCrossFadeLoopLaw(50), sampleFilterPower(1), sampleClipboard(NULL), sampleClipboardLen(0), @@ -7535,6 +7635,7 @@ FurnaceGUI::FurnaceGUI(): openSampleAmplifyOpt(false), openSampleSilenceOpt(false), openSampleFilterOpt(false), + openSampleCrossFadeOpt(false), selectedPortSet(0x1fff), selectedSubPort(-1), hoveredPortSet(0x1fff), @@ -7563,6 +7664,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), @@ -7710,15 +7812,15 @@ FurnaceGUI::FurnaceGUI(): waveGenAmp[0]=1.0f; waveGenFMCon0[0]=false; - waveGenFMCon1[0]= true; - waveGenFMCon2[1]= true; - waveGenFMCon3[2] = true; - waveGenFMCon4[0]= false; + waveGenFMCon1[0]=true; + waveGenFMCon2[1]=true; + waveGenFMCon3[2]=true; + waveGenFMCon4[0]=false; - waveGenFMCon0[4] = false; - waveGenFMCon1[4] = false; - waveGenFMCon2[4] = false; - waveGenFMCon3[4] = true; + waveGenFMCon0[4]=false; + waveGenFMCon1[4]=false; + waveGenFMCon2[4]=false; + waveGenFMCon3[4]=true; memset(keyHit,0,sizeof(float)*DIV_MAX_CHANS); memset(keyHit1,0,sizeof(float)*DIV_MAX_CHANS); diff --git a/src/gui/gui.h b/src/gui/gui.h index 0b2c755eb..1219bd88b 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -466,6 +466,7 @@ enum FurnaceGUIFileDialogs { GUI_FILE_EXPORT_ZSM, GUI_FILE_EXPORT_CMDSTREAM, GUI_FILE_EXPORT_CMDSTREAM_BINARY, + GUI_FILE_EXPORT_TEXT, GUI_FILE_EXPORT_ROM, GUI_FILE_LOAD_MAIN_FONT, GUI_FILE_LOAD_HEAD_FONT, @@ -723,6 +724,7 @@ enum FurnaceGUIActions { GUI_ACTION_SAMPLE_INVERT, GUI_ACTION_SAMPLE_SIGN, GUI_ACTION_SAMPLE_FILTER, + GUI_ACTION_SAMPLE_CROSSFADE_LOOP, GUI_ACTION_SAMPLE_PREVIEW, GUI_ACTION_SAMPLE_STOP_PREVIEW, GUI_ACTION_SAMPLE_ZOOM_IN, @@ -1274,6 +1276,20 @@ struct FurnaceGUIQueryResult { } }; +struct FurnaceGUIWaveSizeEntry { + short width, height; + const char* sys; + + FurnaceGUIWaveSizeEntry(short w, short h, const char* s): + width(w), + height(h), + sys(s) {} + FurnaceGUIWaveSizeEntry(): + width(-1), + height(-1), + sys(NULL) {} +}; + class FurnaceGUITexture { }; @@ -1364,11 +1380,12 @@ class FurnaceGUI { std::vector newSongSearchResults; FixedQueue recentFile; std::vector makeInsTypeList; + std::vector waveSizeList; std::vector availRenderDrivers; std::vector availAudioDrivers; bool quit, warnQuit, willCommit, edit, editClone, isPatUnique, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, zsmExportOptimize, vgmExportPatternHints; - bool vgmExportDirectStream, displayInsTypeList; + bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList; bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly, notifyWaveChange; @@ -1478,6 +1495,8 @@ class FurnaceGUI { int c64Core; int pokeyCore; int opnCore; + int opl2Core; + int opl3Core; int arcadeCoreRender; int ym2612CoreRender; int snCoreRender; @@ -1486,6 +1505,8 @@ class FurnaceGUI { int c64CoreRender; int pokeyCoreRender; int opnCoreRender; + int opl2CoreRender; + int opl3CoreRender; int pcSpeakerOutMethod; String yrw801Path; String tg100Path; @@ -1634,6 +1655,7 @@ class FurnaceGUI { int fontBitmap; int fontAutoHint; int fontAntiAlias; + int selectAssetOnLoad; unsigned int maxUndoSteps; String mainFontPath; String headFontPath; @@ -1671,6 +1693,8 @@ class FurnaceGUI { c64Core(0), pokeyCore(1), opnCore(1), + opl2Core(0), + opl3Core(0), arcadeCoreRender(1), ym2612CoreRender(0), snCoreRender(0), @@ -1679,6 +1703,8 @@ class FurnaceGUI { c64CoreRender(1), pokeyCoreRender(1), opnCoreRender(1), + opl2CoreRender(0), + opl3CoreRender(0), pcSpeakerOutMethod(0), yrw801Path(""), tg100Path(""), @@ -1824,6 +1850,7 @@ class FurnaceGUI { fontBitmap(0), fontAutoHint(1), fontAntiAlias(1), + selectAssetOnLoad(1), maxUndoSteps(100), mainFontPath(""), headFontPath(""), @@ -2097,10 +2124,11 @@ class FurnaceGUI { ImVec2 sampleDragAreaSize; unsigned int sampleDragLen; float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd; + int sampleCrossFadeLoopLength, sampleCrossFadeLoopLaw; unsigned char sampleFilterPower; short* sampleClipboard; size_t sampleClipboardLen; - bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt; + bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleSilenceOpt, openSampleFilterOpt, openSampleCrossFadeOpt; // mixer // 0xxx: output @@ -2124,7 +2152,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; @@ -2145,7 +2173,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; @@ -2163,6 +2191,7 @@ class FurnaceGUI { relatedCh(0), pitch(0.0f), windowSize(1.0f), + phaseOff(0.0f), needle(0), ready(false), loudEnough(false), @@ -2421,6 +2450,8 @@ class FurnaceGUI { void doInsert(); void doTranspose(int amount, OperationMask& mask); String doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd); + void doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData); + void doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat); void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0, bool readClipboard=true, String clipb=""); void doChangeIns(int ins); void doInterpolate(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index ce712578c..b467cd57a 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -539,7 +539,11 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("SAVE", "Save file", FURKMOD_CMD|SDLK_s), D("SAVE_AS", "Save as", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_s), D("UNDO", "Undo", FURKMOD_CMD|SDLK_z), +#ifdef __APPLE__ + D("REDO", "Redo", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_z), +#else D("REDO", "Redo", FURKMOD_CMD|SDLK_y), +#endif D("PLAY_TOGGLE", "Play/Stop (toggle)", SDLK_RETURN), D("PLAY", "Play", 0), D("STOP", "Stop", 0), @@ -575,7 +579,11 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_SAMPLE_LIST", "Sample List", 0), D("WINDOW_SAMPLE_EDIT", "Sample Editor", 0), D("WINDOW_ABOUT", "About", 0), +#ifdef __APPLE__ + D("WINDOW_SETTINGS", "Settings", FURKMOD_CMD|SDLK_COMMA), +#else D("WINDOW_SETTINGS", "Settings", 0), +#endif D("WINDOW_MIXER", "Mixer", 0), D("WINDOW_DEBUG", "Debug Menu", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_d), D("WINDOW_OSCILLOSCOPE", "Oscilloscope (master)", 0), @@ -744,6 +752,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("SAMPLE_INVERT", "Invert", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_t), D("SAMPLE_SIGN", "Signed/unsigned exchange", FURKMOD_CMD|SDLK_u), D("SAMPLE_FILTER", "Apply filter", FURKMOD_CMD|SDLK_f), + D("SAMPLE_CROSSFADE_LOOP", "Crossfade loop points", NOT_AN_ACTION), D("SAMPLE_PREVIEW", "Preview sample", 0), D("SAMPLE_STOP_PREVIEW", "Stop sample preview", 0), D("SAMPLE_ZOOM_IN", "Zoom in", FURKMOD_CMD|SDLK_EQUALS), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 17e06e070..f80757b67 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1997,7 +1997,7 @@ void FurnaceGUI::drawMacros(std::vector& macros, FurnaceGUI float lenAvail=ImGui::GetContentRegionAvail().x; //ImGui::Dummy(ImVec2(120.0f*dpiScale,dpiScale)); ImGui::SetNextItemWidth(120.0f*dpiScale); - if (ImGui::InputInt("##MacroPointSize",¯oPointSize,1,16)) { + if (ImGui::InputInt("##MacroPointSize",¯oPointSize,1,4)) { if (macroPointSize<1) macroPointSize=1; if (macroPointSize>256) macroPointSize=256; } @@ -2203,7 +2203,7 @@ void FurnaceGUI::drawMacros(std::vector& macros, FurnaceGUI ImGui::Button(ICON_FA_SEARCH_PLUS "##MacroZoomB"); if (ImGui::BeginPopupContextItem("MacroZoomP",ImGuiPopupFlags_MouseButtonLeft)) { ImGui::SetNextItemWidth(120.0f*dpiScale); - if (ImGui::InputInt("##MacroPointSize",¯oPointSize,1,16)) { + if (ImGui::InputInt("##MacroPointSize",¯oPointSize,1,4)) { if (macroPointSize<1) macroPointSize=1; if (macroPointSize>256) macroPointSize=256; } @@ -2389,7 +2389,7 @@ void FurnaceGUI::alterSampleMap(int column, int val) { } \ } \ ImGui::TableNextColumn(); \ - if (ImGui::InputInt(df,&fNum,1,1)) { \ + if (ImGui::InputInt(df,&fNum,1,16)) { \ if (fNum<0) fNum=0; \ if (ins->type==DIV_INS_OPLL) { \ if (fNum>511) fNum=511; \ @@ -5519,7 +5519,7 @@ void FurnaceGUI::drawInsEdit() { int len=ins->gb.hwSeq[i].data+1; curFrame+=ins->gb.hwSeq[i].data+1; - if (ImGui::InputInt("Ticks",&len)) { + if (ImGui::InputInt("Ticks",&len,1,4)) { if (len<1) len=1; if (len>255) len=256; somethingChanged=true; @@ -5538,7 +5538,7 @@ void FurnaceGUI::drawInsEdit() { case DivInstrumentGB::DIV_GB_HWCMD_LOOP_REL: { int pos=ins->gb.hwSeq[i].data; - if (ImGui::InputInt("Position",&pos)) { + if (ImGui::InputInt("Position",&pos,1,1)) { if (pos<0) pos=0; if (pos>(ins->gb.hwSeqLen-1)) pos=(ins->gb.hwSeqLen-1); somethingChanged=true; @@ -5879,7 +5879,7 @@ void FurnaceGUI::drawInsEdit() { case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP_REL: { int pos=ins->su.hwSeq[i].val; - if (ImGui::InputInt("Position",&pos)) { + if (ImGui::InputInt("Position",&pos,1,4)) { if (pos<0) pos=0; if (pos>(ins->su.hwSeqLen-1)) pos=(ins->su.hwSeqLen-1); somethingChanged=true; @@ -6074,7 +6074,7 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("only use for compatibility with .dmf modules!\n- initializes modulation table with first wavetable\n- does not alter modulation parameters on instrument change"); } - if (ImGui::InputInt("Modulation depth",&ins->fds.modDepth,1,32)) { + if (ImGui::InputInt("Modulation depth",&ins->fds.modDepth,1,4)) { if (ins->fds.modDepth<0) ins->fds.modDepth=0; if (ins->fds.modDepth>63) ins->fds.modDepth=63; } @@ -6658,23 +6658,23 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTable(); } - if (ImGui::InputScalar("Update Rate",ImGuiDataType_U8,&ins->ws.rateDivider,&_ONE,&_SEVEN)) { + if (ImGui::InputScalar("Update Rate",ImGuiDataType_U8,&ins->ws.rateDivider,&_ONE,&_EIGHT)) { wavePreviewInit=true; } int speed=ins->ws.speed+1; - if (ImGui::InputInt("Speed",&speed,1,16)) { + if (ImGui::InputInt("Speed",&speed,1,8)) { if (speed<1) speed=1; if (speed>256) speed=256; ins->ws.speed=speed-1; wavePreviewInit=true; } - if (ImGui::InputScalar("Amount",ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_SEVEN)) { + if (ImGui::InputScalar("Amount",ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_EIGHT)) { wavePreviewInit=true; } if (ins->ws.effect==DIV_WS_PHASE_MOD) { - if (ImGui::InputScalar("Power",ImGuiDataType_U8,&ins->ws.param2,&_ONE,&_SEVEN)) { + if (ImGui::InputScalar("Power",ImGuiDataType_U8,&ins->ws.param2,&_ONE,&_EIGHT)) { wavePreviewInit=true; } } diff --git a/src/gui/intConst.cpp b/src/gui/intConst.cpp index da5c6065e..741a3ddff 100644 --- a/src/gui/intConst.cpp +++ b/src/gui/intConst.cpp @@ -22,7 +22,9 @@ const int _ZERO=0; const int _ONE=1; const int _THREE=3; +const int _FOUR=4; const int _SEVEN=7; +const int _EIGHT=8; const int _TEN=10; const int _FIFTEEN=15; const int _SIXTEEN=16; diff --git a/src/gui/intConst.h b/src/gui/intConst.h index 7acbbcf6b..f0b7bc952 100644 --- a/src/gui/intConst.h +++ b/src/gui/intConst.h @@ -24,7 +24,9 @@ extern const int _ZERO; extern const int _ONE; extern const int _THREE; +extern const int _FOUR; extern const int _SEVEN; +extern const int _EIGHT; extern const int _TEN; extern const int _FIFTEEN; extern const int _SIXTEEN; 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) { diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 5a57483dd..3ab8fe7a3 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1123,11 +1123,11 @@ void FurnaceGUI::drawSampleEdit() { float highP=sampleFilterH*100.0f; float resP=sampleFilterRes*100.0f; ImGui::Text("Cutoff:"); - if (ImGui::InputFloat("From",&sampleFilterCutStart,1.0f,100.0f,"%.0f")) { + if (ImGui::InputFloat("From",&sampleFilterCutStart,10.0f,1000.0f,"%.0f")) { if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0; if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5; } - if (ImGui::InputFloat("To",&sampleFilterCutEnd,1.0f,100.0f,"%.0f")) { + if (ImGui::InputFloat("To",&sampleFilterCutEnd,10.0f,1000.0f,"%.0f")) { if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0; if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5; } @@ -1226,6 +1226,74 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SameLine(); ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); sameLineMaybe(); + ImGui::Button(ICON_FUR_CROSSFADE "##CrossFade"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Crossfade loop points"); + } + if (openSampleCrossFadeOpt) { + openSampleCrossFadeOpt=false; + ImGui::OpenPopup("SCrossFadeOpt"); + } + if (ImGui::BeginPopupContextItem("SCrossFadeOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (sampleCrossFadeLoopLength>sample->loopStart) sampleCrossFadeLoopLength=sample->loopStart; + if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) sampleCrossFadeLoopLength=sample->loopEnd-sample->loopStart; + if (ImGui::SliderInt("Number of samples",&sampleCrossFadeLoopLength,0,100000)) { + if (sampleCrossFadeLoopLength<0) sampleCrossFadeLoopLength=0; + if (sampleCrossFadeLoopLength>sample->loopStart) sampleCrossFadeLoopLength=sample->loopStart; + if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) sampleCrossFadeLoopLength=sample->loopEnd-sample->loopStart; + if (sampleCrossFadeLoopLength>100000) sampleCrossFadeLoopLength=100000; + } + if (ImGui::SliderInt("Linear <-> Equal power",&sampleCrossFadeLoopLaw,0,100)) { + if (sampleCrossFadeLoopLaw<0) sampleCrossFadeLoopLaw=0; + if (sampleCrossFadeLoopLaw>100) sampleCrossFadeLoopLaw=100; + } + if (ImGui::Button("Apply")) { + if (sampleCrossFadeLoopLength>sample->loopStart) { + showError("Crossfade: length would go out of bounds. Aborted..."); + ImGui::CloseCurrentPopup(); + } else if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) { + showError("Crossfade: length would overflow loopStart. Try a smaller random value."); + ImGui::CloseCurrentPopup(); + } else { + sample->prepareUndo(true); + e->lockEngine([this,sample] { + SAMPLE_OP_BEGIN; + double l=1.0/(double)sampleCrossFadeLoopLength; + double evar=1.0-sampleCrossFadeLoopLaw/200.0; + if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) { + unsigned int crossFadeInput=sample->loopStart-sampleCrossFadeLoopLength; + unsigned int crossFadeOutput=sample->loopEnd-sampleCrossFadeLoopLength; + for (int i=0; idata8[crossFadeInput])*f1+((double)sample->data8[crossFadeOutput])*f2); + sample->data8[crossFadeOutput]=out; + crossFadeInput++; + crossFadeOutput++; + } + } else if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) { + unsigned int crossFadeInput=sample->loopStart-sampleCrossFadeLoopLength; + unsigned int crossFadeOutput=sample->loopEnd-sampleCrossFadeLoopLength; + for (int i=0; idata16[crossFadeInput])*f1+((double)sample->data16[crossFadeOutput])*f2); + sample->data16[crossFadeOutput]=out; + crossFadeInput++; + crossFadeOutput++; + } + } + updateSampleTex=true; + + e->renderSamples(curSample); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + } + ImGui::EndPopup(); + } + ImGui::SameLine(); if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { e->previewSample(curSample); } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 86be7e547..381c2eb9e 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[]={ @@ -156,6 +157,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", @@ -461,7 +474,7 @@ void FurnaceGUI::drawSettings() { settingsChanged=true; } - if (ImGui::InputInt("Number of recent files",&settings.maxRecentFile)) { + if (ImGui::InputInt("Number of recent files",&settings.maxRecentFile,1,5)) { if (settings.maxRecentFile<0) settings.maxRecentFile=0; if (settings.maxRecentFile>30) settings.maxRecentFile=30; settingsChanged=true; @@ -944,7 +957,7 @@ void FurnaceGUI::drawSettings() { ImGui::AlignTextToFramePadding(); ImGui::Text("Outputs"); ImGui::TableNextColumn(); - if (ImGui::InputInt("##AudioChansI",&settings.audioChans,1,1)) { + if (ImGui::InputInt("##AudioChansI",&settings.audioChans,1,2)) { if (settings.audioChans<1) settings.audioChans=1; if (settings.audioChans>16) settings.audioChans=16; settingsChanged=true; @@ -1487,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(); @@ -1557,6 +1570,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(); @@ -1745,7 +1781,7 @@ void FurnaceGUI::drawSettings() { ImGui::TableNextColumn(); if (i.val<100) { snprintf(id,4095,"##SNValue_%d",i.scan); - if (ImGui::InputInt(id,&i.val,1,1)) { + if (ImGui::InputInt(id,&i.val,1,12)) { if (i.val<0) i.val=0; if (i.val>96) i.val=96; noteKeys[i.scan]=i.val; @@ -2334,6 +2370,12 @@ void FurnaceGUI::drawSettings() { settingsChanged=true; } + bool selectAssetOnLoadB=settings.selectAssetOnLoad; + if (ImGui::Checkbox("Select asset after opening one",&selectAssetOnLoadB)) { + settings.selectAssetOnLoad=selectAssetOnLoadB; + settingsChanged=true; + } + END_SECTION; } CONFIG_SECTION("Appearance") { @@ -2356,7 +2398,7 @@ void FurnaceGUI::drawSettings() { } rightClickable } - if (ImGui::InputInt("Icon size",&settings.iconSize)) { + if (ImGui::InputInt("Icon size",&settings.iconSize,1,3)) { if (settings.iconSize<3) settings.iconSize=3; if (settings.iconSize>48) settings.iconSize=48; settingsChanged=true; @@ -2392,7 +2434,7 @@ void FurnaceGUI::drawSettings() { settingsChanged=true; } } - if (ImGui::InputInt("Size##MainFontSize",&settings.mainFontSize)) { + if (ImGui::InputInt("Size##MainFontSize",&settings.mainFontSize,1,3)) { if (settings.mainFontSize<3) settings.mainFontSize=3; if (settings.mainFontSize>96) settings.mainFontSize=96; settingsChanged=true; @@ -2411,7 +2453,7 @@ void FurnaceGUI::drawSettings() { settingsChanged=true; } } - if (ImGui::InputInt("Size##HeadFontSize",&settings.headFontSize)) { + if (ImGui::InputInt("Size##HeadFontSize",&settings.headFontSize,1,3)) { if (settings.headFontSize<3) settings.headFontSize=3; if (settings.headFontSize>96) settings.headFontSize=96; settingsChanged=true; @@ -2430,7 +2472,7 @@ void FurnaceGUI::drawSettings() { settingsChanged=true; } } - if (ImGui::InputInt("Size##PatFontSize",&settings.patFontSize)) { + if (ImGui::InputInt("Size##PatFontSize",&settings.patFontSize,1,3)) { if (settings.patFontSize<3) settings.patFontSize=3; if (settings.patFontSize>96) settings.patFontSize=96; settingsChanged=true; @@ -2607,8 +2649,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)) { @@ -3594,6 +3636,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 +3646,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",""); @@ -3759,6 +3805,7 @@ void FurnaceGUI::syncSettings() { settings.fontBitmap=e->getConfInt("fontBitmap",0); settings.fontAutoHint=e->getConfInt("fontAutoHint",1); settings.fontAntiAlias=e->getConfInt("fontAntiAlias",1); + settings.selectAssetOnLoad=e->getConfInt("selectAssetOnLoad",1); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.headFontSize,2,96); @@ -3771,21 +3818,25 @@ 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); 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.ym2612CoreRender,0,2); clampSetting(settings.snCoreRender,0,1); clampSetting(settings.nesCoreRender,0,1); clampSetting(settings.fdsCoreRender,0,1); 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); @@ -3920,6 +3971,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.fontBitmap,0,1); clampSetting(settings.fontAutoHint,0,2); clampSetting(settings.fontAntiAlias,0,1); + clampSetting(settings.selectAssetOnLoad,0,1); if (settings.exportLoops<0.0) settings.exportLoops=0.0; if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; @@ -3991,6 +4043,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 +4053,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 +4082,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 +4092,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); @@ -4192,6 +4252,7 @@ void FurnaceGUI::commitSettings() { e->setConf("fontBitmap",settings.fontBitmap); e->setConf("fontAutoHint",settings.fontAutoHint); e->setConf("fontAntiAlias",settings.fontAntiAlias); + e->setConf("selectAssetOnLoad",settings.selectAssetOnLoad); // colors for (int i=0; isong.tuning; float avail=ImGui::GetContentRegionAvail().x; ImGui::SetNextItemWidth(avail); - if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED + if (ImGui::InputFloat("##Tuning",&tune,1.0f,10.0f,"%g")) { MARK_MODIFIED if (tune<220.0f) tune=220.0f; if (tune>880.0f) tune=880.0f; e->song.tuning=tune; diff --git a/src/gui/speed.cpp b/src/gui/speed.cpp index 593467852..b9b4988bc 100644 --- a/src/gui/speed.cpp +++ b/src/gui/speed.cpp @@ -53,7 +53,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { float halfAvail=(avail-ImGui::GetStyle().ItemSpacing.x)*0.5; ImGui::SetNextItemWidth(halfAvail); float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz; - if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED + if (ImGui::InputFloat("##Rate",&setHz,1.0f,10.0f,"%g")) { MARK_MODIFIED if (tempoView) setHz/=2.5; if (setHz<1) setHz=1; if (setHz>999) setHz=999; @@ -169,7 +169,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::Text("Virtual Tempo"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(halfAvail); - if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_THREE)) { MARK_MODIFIED + if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_TEN)) { MARK_MODIFIED if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1; if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255; } @@ -178,7 +178,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { } ImGui::SameLine(); ImGui::SetNextItemWidth(halfAvail); - if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_THREE)) { MARK_MODIFIED + if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_TEN)) { MARK_MODIFIED if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1; if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255; } @@ -208,12 +208,12 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::Text("Highlight"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(halfAvail); - if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) { + if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_FOUR)) { MARK_MODIFIED; } ImGui::SameLine(); ImGui::SetNextItemWidth(halfAvail); - if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) { + if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_FOUR)) { MARK_MODIFIED; } ImGui::EndTable(); @@ -233,7 +233,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { float avail=ImGui::GetContentRegionAvail().x; ImGui::SetNextItemWidth(avail); int patLen=e->curSubSong->patLen; - if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED + if (ImGui::InputInt("##PatLength",&patLen,1,16)) { MARK_MODIFIED if (patLen<1) patLen=1; if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS; e->curSubSong->patLen=patLen; @@ -247,7 +247,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); int ordLen=e->curSubSong->ordersLen; - if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED + if (ImGui::InputInt("##OrdLength",&ordLen,1,4)) { MARK_MODIFIED if (ordLen<1) ordLen=1; if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS; e->curSubSong->ordersLen=ordLen; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index f1d39870d..8f094ab6d 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -754,42 +754,50 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl clockSel=0; altered=true; } - if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",clockSel==1)) { + if (ImGui::RadioButton("1.77MHz (ZX Spectrum PAL)",clockSel==1)) { clockSel=1; altered=true; } - if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",clockSel==2)) { - clockSel=2; - altered=true; - } - if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",clockSel==3)) { - clockSel=3; - altered=true; - } - if (ImGui::RadioButton("1.5MHz (Vectrex)",clockSel==4)) { - clockSel=4; - altered=true; - } - if (ImGui::RadioButton("1MHz (Amstrad CPC)",clockSel==5)) { - clockSel=5; - altered=true; - } - if (ImGui::RadioButton("0.89MHz (Pre-divided Sunsoft 5B)",clockSel==6)) { - clockSel=6; - altered=true; - } - if (ImGui::RadioButton("1.67MHz (?)",clockSel==7)) { - clockSel=7; - altered=true; - } if (ImGui::RadioButton("0.83MHz (Pre-divided Sunsoft 5B on PAL)",clockSel==8)) { clockSel=8; altered=true; } + if (ImGui::RadioButton("0.89MHz (Pre-divided Sunsoft 5B)",clockSel==6)) { + clockSel=6; + altered=true; + } + if (ImGui::RadioButton("1MHz (Amstrad CPC)",clockSel==5)) { + clockSel=5; + altered=true; + } if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",clockSel==9)) { clockSel=9; altered=true; } + if (ImGui::RadioButton("1.25MHz (Mag Max)",clockSel==13)) { + clockSel=13; + altered=true; + } + if (ImGui::RadioButton("1.5MHz (Vectrex)",clockSel==4)) { + clockSel=4; + altered=true; + } + if (ImGui::RadioButton("1.536MHz (Kyugo)",clockSel==14)) { + clockSel=14; + altered=true; + } + if (ImGui::RadioButton("1.67MHz (?)",clockSel==7)) { + clockSel=7; + altered=true; + } + if (ImGui::RadioButton("1.75MHz (ZX Spectrum 48K)",clockSel==2)) { + clockSel=2; + altered=true; + } + if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",clockSel==3)) { + clockSel=3; + altered=true; + } if (ImGui::RadioButton("2^21Hz (Game Boy)",clockSel==10)) { clockSel=10; altered=true; @@ -802,14 +810,6 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl clockSel=12; altered=true; } - if (ImGui::RadioButton("1.25MHz (Mag Max)",clockSel==13)) { - clockSel=13; - altered=true; - } - if (ImGui::RadioButton("1.536MHz (Kyugo)",clockSel==14)) { - clockSel=14; - altered=true; - } ImGui::Unindent(); if (type==DIV_SYSTEM_AY8910) { ImGui::Text("Chip type:"); @@ -1468,36 +1468,40 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl ImGui::Text("Clock rate:"); ImGui::Indent(); + if (ImGui::RadioButton("0.875MHz",clockSel==10)) { + clockSel=10; + altered=true; + } + if (ImGui::RadioButton("0.89MHz",clockSel==7)) { + clockSel=7; + altered=true; + } + if (ImGui::RadioButton("0.9375MHz",clockSel==11)) { + clockSel=11; + altered=true; + } if (ImGui::RadioButton("1MHz",clockSel==0)) { clockSel=0; altered=true; } - if (ImGui::RadioButton("1.056MHz",clockSel==1)) { - clockSel=1; - altered=true; - } - if (ImGui::RadioButton("4MHz",clockSel==2)) { - clockSel=2; - altered=true; - } - if (ImGui::RadioButton("4.224MHz",clockSel==3)) { - clockSel=3; - altered=true; - } - if (ImGui::RadioButton("3.58MHz",clockSel==4)) { - clockSel=4; - altered=true; - } - if (ImGui::RadioButton("1.79MHz",clockSel==5)) { - clockSel=5; - altered=true; - } if (ImGui::RadioButton("1.02MHz",clockSel==6)) { clockSel=6; altered=true; } - if (ImGui::RadioButton("0.89MHz",clockSel==7)) { - clockSel=7; + if (ImGui::RadioButton("1.056MHz",clockSel==1)) { + clockSel=1; + altered=true; + } + if (ImGui::RadioButton("1.193MHz (Atari)",clockSel==14)) { + clockSel=14; + altered=true; + } + if (ImGui::RadioButton("1.5MHz",clockSel==12)) { + clockSel=12; + altered=true; + } + if (ImGui::RadioButton("1.79MHz",clockSel==5)) { + clockSel=5; altered=true; } if (ImGui::RadioButton("2MHz",clockSel==8)) { @@ -1508,24 +1512,20 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl clockSel=9; altered=true; } - if (ImGui::RadioButton("0.875MHz",clockSel==10)) { - clockSel=10; - altered=true; - } - if (ImGui::RadioButton("0.9375MHz",clockSel==11)) { - clockSel=11; - altered=true; - } - if (ImGui::RadioButton("1.5MHz",clockSel==12)) { - clockSel=12; - altered=true; - } if (ImGui::RadioButton("3MHz",clockSel==13)) { clockSel=13; altered=true; } - if (ImGui::RadioButton("1.193MHz (Atari)",clockSel==14)) { - clockSel=14; + if (ImGui::RadioButton("3.58MHz",clockSel==4)) { + clockSel=4; + altered=true; + } + if (ImGui::RadioButton("4MHz",clockSel==2)) { + clockSel=2; + altered=true; + } + if (ImGui::RadioButton("4.224MHz",clockSel==3)) { + clockSel=3; altered=true; } ImGui::Unindent(); @@ -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: @@ -2286,7 +2307,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl altered=true; } ImGui::Indent(); - if (ImGui::InputInt("Hz",&customClock)) { + if (ImGui::InputInt("Hz",&customClock,100,10000)) { if (customClockMAX_CUSTOM_CLOCK) customClock=MAX_CUSTOM_CLOCK; altered=true; diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index de7cee606..cb4cf0462 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -455,7 +455,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(80.0f*dpiScale); - if (ImGui::InputInt("##CurWave",&curWave,1,1)) { + if (ImGui::InputInt("##CurWave",&curWave,1,10)) { if (curWave<0) curWave=0; if (curWave>=(int)e->song.wave.size()) curWave=e->song.wave.size()-1; } @@ -499,7 +499,7 @@ void FurnaceGUI::drawWaveEdit() { } ImGui::SameLine(); ImGui::SetNextItemWidth(96.0f*dpiScale); - if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { + if (ImGui::InputInt("##_WTW",&wave->len,1,16)) { if (wave->len>256) wave->len=256; if (wave->len<1) wave->len=1; e->notifyWaveChange(curWave); @@ -514,7 +514,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::SameLine(); ImGui::SetNextItemWidth(96.0f*dpiScale); int realMax=wave->max+1; - if (ImGui::InputInt("##_WTH",&realMax,1,2)) { + if (ImGui::InputInt("##_WTH",&realMax,1,16)) { if (realMax>256) realMax=256; if (realMax<2) realMax=2; wave->max=realMax-1; diff --git a/src/gui/xyOsc.cpp b/src/gui/xyOsc.cpp index 3a00b811d..28136d645 100644 --- a/src/gui/xyOsc.cpp +++ b/src/gui/xyOsc.cpp @@ -30,7 +30,7 @@ void FurnaceGUI::drawXYOsc() { nextWindow=GUI_WINDOW_NOTHING; } if (!xyOscOpen) return; - ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(canvasW,canvasH)); + ImGui::SetNextWindowSizeConstraints(ImVec2(128.0f*dpiScale,128.0f*dpiScale),ImVec2(canvasW,canvasH)); bool noPadding=settings.oscTakesEntireWindow && !xyOscOptions; if (noPadding) { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); diff --git a/src/icon/furIcons.h b/src/icon/furIcons.h index 718e6822b..5d5cec4cb 100644 --- a/src/icon/furIcons.h +++ b/src/icon/furIcons.h @@ -1,7 +1,7 @@ // not auto-generated. update every time you change icons.ttf! #define ICON_MIN_FUR 0xe0f0 -#define ICON_MAX_FUR 0xe158 +#define ICON_MAX_FUR 0xe159 // test #define ICON_FUR_TEST0 u8"\ue0f0" @@ -105,4 +105,5 @@ #define ICON_FUR_INC_LINEAR u8"\ue14b" #define ICON_FUR_INC_BENT u8"\ue14c" #define ICON_FUR_VOL_DIRECT u8"\ue14d" -#define ICON_FUR_ADSR u8"\ue14e" \ No newline at end of file +#define ICON_FUR_ADSR u8"\ue14e" +#define ICON_FUR_CROSSFADE u8"\ue159" diff --git a/src/main.cpp b/src/main.cpp index e5dc006bd..b3b9f3b04 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,11 +210,14 @@ 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("- 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");