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 e1ac94212..aeb705028 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 src/pch.cpp @@ -556,9 +559,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 @@ -810,6 +815,7 @@ src/gui/tutorial.cpp src/gui/util.cpp src/gui/waveEdit.cpp src/gui/volMeter.cpp +src/gui/xyOsc.cpp src/gui/gui.cpp ) @@ -1072,6 +1078,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 523d5e171..06951982a 100644 --- a/TODO.md +++ b/TODO.md @@ -4,14 +4,9 @@ # THE REAL TO-DO LIST -- Amiga's Period Modulation not working -- Song is silent after playing an order after loop point -- Select loaded instrument on open - rewrite because I want a setting... -- re-engineer volume handling? Sound Unit cries at me -- finish status view - 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/amiga/a_wave_powerhouse.fur b/demos/amiga/a_wave_powerhouse.fur new file mode 100644 index 000000000..105bea5fe Binary files /dev/null and b/demos/amiga/a_wave_powerhouse.fur differ 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/c64/deadlock.fur b/demos/c64/deadlock.fur new file mode 100644 index 000000000..3c4b32138 Binary files /dev/null and b/demos/c64/deadlock.fur differ 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/su_memory.fur b/demos/specs2/su_memory.fur deleted file mode 100644 index ff030dbe7..000000000 Binary files a/demos/specs2/su_memory.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/README.md b/doc/1-intro/README.md index cd0376e99..2a227456f 100644 --- a/doc/1-intro/README.md +++ b/doc/1-intro/README.md @@ -26,4 +26,6 @@ once familiar with the tracker, look to [9-guides](../9-guides/README.md) for us ## tutorial? +[How to Learn Chiptune Trackers](https://www.youtube.com/watch?v=Q37XuOLz0jw): video tutorial created by Button Masher. covers the basic mechanics of chiptune tracking using Furnace for demonstration. + [Furnace Tutorials](https://youtube.com/playlist?list=PLCELB6AsTZUnwv0PC5AAGHjvg47F44YQ1): video tutorials created by Spinning Square Waves. be noted that these may not apply to the current version. 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/MIDI-value-input-1.png b/doc/2-interface/MIDI-value-input-1.png new file mode 100644 index 000000000..4e5df7be8 Binary files /dev/null and b/doc/2-interface/MIDI-value-input-1.png differ diff --git a/doc/2-interface/MIDI-value-input-2.png b/doc/2-interface/MIDI-value-input-2.png new file mode 100644 index 000000000..5173a2ded Binary files /dev/null and b/doc/2-interface/MIDI-value-input-2.png differ diff --git a/doc/2-interface/asset-list.md b/doc/2-interface/asset-list.md index cc94312d4..2b5ffaaa9 100644 --- a/doc/2-interface/asset-list.md +++ b/doc/2-interface/asset-list.md @@ -55,5 +55,12 @@ everything from the wavetables list applies here also, with the addition of one samples are saved as standard wave (.wav) files. -- right-clicking the Save button brings up a menu with the following options: - - **save raw sample...**: saves the selected sample as raw data. +right-clicking the Save button brings up a menu with the following options: +- **save raw sample...**: saves the selected sample as raw data. + +right-clicking a sample in the list brings up a menu: +- **make instrument**: creates a new instrument which is set to use the selected sample. +- **duplicate**: makes a copy of the selected sample. +- **replace...**: opens a file dialog to choose a replacement sample. +- **save**: opens a file dialog to choose where to save the sample. +- **delete**: removes the sample. diff --git a/doc/2-interface/components.md b/doc/2-interface/components.md index 4ed6e6a44..695ecf1e4 100644 --- a/doc/2-interface/components.md +++ b/doc/2-interface/components.md @@ -21,7 +21,8 @@ the following keyboard shortcuts work while on a text field: these work similar to text fields, but you may only input numbers. -they also usually have `+` and `-` buttons which allow you to increase/decrease the amount when clicked (and rapidly do so when click-holding). +they also usually have `+` and `-` buttons which allow you to increase/decrease the value when clicked (and rapidly do so when holding). +additionally, Ctrl-clicking these buttons may increase/decrease the value by a coarse amount. ## sliders @@ -29,7 +30,8 @@ sliders are used for controlling values in a quick manner by being dragged. using the scroll wheel while holding Ctrl will change the slider's value by small amounts. -right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn it into a number input field for a short period of time, allowing you to input precise values. +right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn it into a number input field, allowing you to input precise values. +once you click away it will become a slider again. ## windows @@ -37,13 +39,13 @@ right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn i windows may be moved, collapsed, closed or even docked around the workspace. -to move a window, press and hold the mouse button while on title bar or any empty space on it. +to move a window, press and hold the left mouse button while on the title bar or any empty space on it. then drag your mouse, and release it to stop moving. to resize a window, drag the bottom right corner (marked by a triangular tab) or the borders. to collapse a window, click on the triangle in the title bar. -clicking again expands it. +clicking again expands the window. to close a window, click on the `X` at the top right corner, or select it from the "window" menu. 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 a4a1676a5..b3a93b04c 100644 --- a/doc/2-interface/settings.md +++ b/doc/2-interface/settings.md @@ -47,6 +47,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **When creating new song**: - **Display system preset selector** - **Start with initial system** + - **Default author name** ### Start-up @@ -92,7 +93,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - setting this to a high value increases latency. - **Exclusive mode**: enables Exclusive Mode, which may offer latency improvements. - only available on WASAPI devices in the PortAudio backend! -- **Low-latency mode (experimental!)**: reduces latency by running the engine faster than the tick rate. useful for live playback/jam mode. +- **Low-latency mode**: reduces latency by running the engine faster than the tick rate. useful for live playback/jam mode. - only enable if your buffer size is small (10ms or less). - **Force mono audio**: use if you're unable to hear stereo audio (e.g. single speaker or hearing loss in one ear). - **want:** displays requested audio configuration. @@ -104,6 +105,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **Software clipping**: clips output to nominal range (-1.0 to 1.0) before passing it to the audio device. - this avoids activating Windows' built-in limiter. - this option shall be enabled when using PortAudio backend with a DirectSound device. +- **DC offset correction**: apply a filter to remove DC bias, where the output is overall above or below zero. default is on. ### Metronome @@ -121,22 +123,21 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **Note input**: enables note input. disable if you intend to use this device only for binding actions. - **Velocity input**: enables velocity input when entering notes in the pattern. - **Map MIDI channels to direct channels**: when enabled, notes from MIDI channels will be mapped to channels rather than the cursor position. +- **Program change pass-through**: when enabled, program change events are sent to each channel as instrument change commands. + - this option is only available when the previous one is enabled. - **Map Yamaha FM voice data to instruments**: when enabled, Furnace will listen for any transmitted Yamaha SysEx patches. - this option is only useful if you have a Yamaha FM synthesizer (e.g. TX81Z). - selecting a voice or using the "Voice Transmit?" option will send a patch, and Furnace will create a new instrument with its data. - this may also be triggered by clicking on "Receive from TX81Z" in the instrument editor (OPZ only). - **Program change is instrument selection**: changes the current instrument when a program change event is received. + - this option is not available when "Program change pass-through" is enabled. - **Value input style**: changes the way values are entered when the pattern cursor is not in the Note column. the following styles are available: - **Disabled/custom**: no value input through MIDI. - **Two octaves (0 is C-4, F is D#5)**: maps keys in two octaves to single nibble input. the layout is: - - ` - octave n -- octave n+1 -` - - ` 1 3 6 8 A D F # # # ` - - `0 2 4 5 7 9 B C E # # # # #` + ![two octaves layout 1](MIDI-value-input-1.png) - **Raw (note number is value)**: the note number becomes the input value. not useful if you want to input anything above 7F. - **Two octaves alternate (lower keys are 0-9, upper keys are A-F)**: maps keys in two octaves, but with a different layout: - - ` - octave n -- octave n+1 -` - - ` A B C D E F # # # # ` - - `0 1 2 3 4 5 6 7 8 9 # # # #` + ![two octaves layout 2](MIDI-value-input-2.png) - **Use dual control change (one for each nibble)**: maps two control change events to the nibbles of a value. - **CC of upper nibble**: select the CC number that will change the upper nibble. - **CC of lower nibble**: select the CC number that will change the lower nibble. @@ -147,6 +148,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **Control**: select the CC number that will change the value. - **Per-column control change**: when enabled, you can map several control change events to a channel's columns. - **Volume curve**: adjust the velocity to volume curve. + - the default is 2.0, which matches General MIDI standard. - **Actions**: this allows you to bind note input and control change events to actions. - **`+`** button: adds a new action. - window-with-arrow button: new action with learning! press a button or move a slider/knob/something on your device. @@ -299,6 +301,15 @@ below all the binds, select a key from the dropdown list to add it. it will appe - applies when playback is stopped. - **Don't scroll when moving cursor** - **Move cursor with scroll wheel** + - **No** + - **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 @@ -430,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/3-pattern/README.md b/doc/3-pattern/README.md index f619ce287..31713ea64 100644 --- a/doc/3-pattern/README.md +++ b/doc/3-pattern/README.md @@ -38,18 +38,39 @@ clicking on a channel name mutes that channel. double-clicking or right-clicking it enables solo mode, in which only that channel will be audible. -clicking the `++` at the top left corner of the pattern view cycles through three channel bar view modes: -- **Compact**: shows only channel names. -- **Expanded**: as shown above. adds buttons: +clicking the `++` at the top left corner of the pattern view pops up a small menu to set view modes: +- **Effect columns/collapse**: displays extra options for collapsing channels and adding/removing effect columns: - **-**: collapse visible columns. changes to **+** when columns are hidden; click to expand them. - **<**: disables the last effect column and hides it. effects are not deleted... - - **>**: adds an effects column. if one previously existed, its contents will be preserved. + - **>**: adds an effect column. if one previously existed, its contents will be preserved. - **Pattern names**: displays pattern names (per channel). pattern names are also visible when hovering over a pattern in the order list. - -right-clicking the `++` toggles the visualizer, which is active only during playback. +- **Channel group hints**: display indicators when channels are paired in some way (e.g. OPL3 4-op mode). +- **Visualizer**: during playback, show visual effects in the pattern view. + - also can be toggled by right-clicking on the `++` button. +- **Channel status**: displays icons that indicate activity in the channel. see the "channel status" section below. to rename and/or hide channels, open [the Channels window](../8-advanced/channels.md) via the window menu. +### channel status + +- note status: + - ![note off](status-note-off.png) note off + - ![note on](status-note-on.png) note on + - ![macro released](status-note-on-rel.png) note on but macro released (`REL`) + - ![note released](status-note-off-rel.png) note released (`===`) +- pitch alteration: + - ![no effect](status-pitch-none.png) nothing + - ![pitch up](status-pitch-up.png) pitch slide up + - ![pitch down](status-pitch-down.png) pitch slide down + - ![portamento](status-pitch-porta.png) portamento + - ![arpeggio](status-pitch-arpeg.png) arpeggio +- volume alteration: + - ![no effect](status-volume-none.png) nothing + - ![volume up](status-volume-up.png) volume slide up + - ![volume down](status-volume-down.png) volume slide down + - ![tremolo](status-volume-tremolo.png) tremolo +- other icons may be present depending on the used chips. + ## input diff --git a/doc/3-pattern/effects.md b/doc/3-pattern/effects.md index b2622426c..c76826c02 100644 --- a/doc/3-pattern/effects.md +++ b/doc/3-pattern/effects.md @@ -154,16 +154,16 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx W | | LFO Shape | LFO Shape | Patch | Waveform | Waveform | | Waveform | 1 | | AMD | AMD | | | Duty | | FilterMode | 2 | | PMD | PMD | | Envelope | Envelope | | Resonance | - 3 | LFOSpd | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | - A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | | + 3 | LFOSpd | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | | + A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | Cutoff | B | FB | FB | FB | | | Noise AND | | | C | FMS | FMS | FMS | | | Noise OR | | | D | AMS | AMS | AMS | | | | | | - 4 | OpMask | OpMask | | | | | | Test/Gate | - 5 | | | AMD2 | | | | | | - 6 | | | PMD2 | | | | | | - 7 | | | LFO2Speed | | | | | | - 8 | | | LFO2Shape | | | | | | + 4 | OpMask | OpMask | | | | | | Special | + 5 | | | AMD2 | | | | | Attack | + 6 | | | PMD2 | | | | | Decay | + 7 | | | LFO2Speed | | | | | Sustain | + 8 | | | LFO2Shape | | | | | Release | ex | SAA1099 | X1-010 | Namco 163 | FDS | Sound Unit | ES5506 | MSM6258 | ---|----------|------------|------------|-----------|------------|-----------|----------| diff --git a/doc/3-pattern/status-note-off-rel.png b/doc/3-pattern/status-note-off-rel.png new file mode 100644 index 000000000..c522cf6ed Binary files /dev/null and b/doc/3-pattern/status-note-off-rel.png differ diff --git a/doc/3-pattern/status-note-off.png b/doc/3-pattern/status-note-off.png new file mode 100644 index 000000000..817a0c75c Binary files /dev/null and b/doc/3-pattern/status-note-off.png differ diff --git a/doc/3-pattern/status-note-on-rel.png b/doc/3-pattern/status-note-on-rel.png new file mode 100644 index 000000000..bb9e4160e Binary files /dev/null and b/doc/3-pattern/status-note-on-rel.png differ diff --git a/doc/3-pattern/status-note-on.png b/doc/3-pattern/status-note-on.png new file mode 100644 index 000000000..92de9979b Binary files /dev/null and b/doc/3-pattern/status-note-on.png differ diff --git a/doc/3-pattern/status-pitch-arpeg.png b/doc/3-pattern/status-pitch-arpeg.png new file mode 100644 index 000000000..67cd4b52f Binary files /dev/null and b/doc/3-pattern/status-pitch-arpeg.png differ diff --git a/doc/3-pattern/status-pitch-down.png b/doc/3-pattern/status-pitch-down.png new file mode 100644 index 000000000..1a2f9f2b1 Binary files /dev/null and b/doc/3-pattern/status-pitch-down.png differ diff --git a/doc/3-pattern/status-pitch-none.png b/doc/3-pattern/status-pitch-none.png new file mode 100644 index 000000000..21b9d1871 Binary files /dev/null and b/doc/3-pattern/status-pitch-none.png differ diff --git a/doc/3-pattern/status-pitch-porta.png b/doc/3-pattern/status-pitch-porta.png new file mode 100644 index 000000000..ab06ba0d2 Binary files /dev/null and b/doc/3-pattern/status-pitch-porta.png differ diff --git a/doc/3-pattern/status-pitch-up.png b/doc/3-pattern/status-pitch-up.png new file mode 100644 index 000000000..51a0e6f50 Binary files /dev/null and b/doc/3-pattern/status-pitch-up.png differ diff --git a/doc/3-pattern/status-volume-down.png b/doc/3-pattern/status-volume-down.png new file mode 100644 index 000000000..a788b4ea8 Binary files /dev/null and b/doc/3-pattern/status-volume-down.png differ diff --git a/doc/3-pattern/status-volume-none.png b/doc/3-pattern/status-volume-none.png new file mode 100644 index 000000000..bbf32e95e Binary files /dev/null and b/doc/3-pattern/status-volume-none.png differ diff --git a/doc/3-pattern/status-volume-tremolo.png b/doc/3-pattern/status-volume-tremolo.png new file mode 100644 index 000000000..a1fe64d51 Binary files /dev/null and b/doc/3-pattern/status-volume-tremolo.png differ diff --git a/doc/3-pattern/status-volume-up.png b/doc/3-pattern/status-volume-up.png new file mode 100644 index 000000000..525b8add1 Binary files /dev/null and b/doc/3-pattern/status-volume-up.png differ diff --git a/doc/4-instrument/README.md b/doc/4-instrument/README.md index a1681388b..56386082a 100644 --- a/doc/4-instrument/README.md +++ b/doc/4-instrument/README.md @@ -96,31 +96,55 @@ every macro can be defined though one of three methods, selectable with the left - ![ADSR](macro-button-ADSR.png) **ADSR:** this is a traditional ADSR envelope, defined by the rate of increase and decrease of value over time. - ![LFO](macro-button-LFO.png) **LFO:** the Low Frequency Oscillator generates a repeating wave of values. -some macros are "bitmap" style. they represent a number of "bits" that can be toggled individually, and the values listed represent the sum of which bits are turned on. - ### sequence ![sequence macro editor](macro-seq.png) +![clipped sequence macro editor](macro-seq-clip.png) + +![bitmask sequence macro editor](macro-seq-bitmask.png) + the number between the macro type label and the macro type button is the macro length in steps. the `-` and `+` buttons change the length of the macro. start out by adding at least a few steps. the values of the macro can be drawn in the "bar graph" box. +- arpeggio and pitch macros may have values above or below the visible area; small chevrons will be shown until they are scrolled into view. +- bitmask-style macros show labels for each of their bits, and these are edited as toggles. -just beneath the box is a shorter bar that controls looping. +arpeggio macros have a short bar for setting whether to interpret the values as being "relative" or "fixed". +- by default, values are offsets **relative** to the note. +- if clicked on, a value becomes **fixed** and will be played at its corresponding note without regard to the note entered into the pattern. + - values are counted from `C-0`. for example, a fixed value of 48 produces a `C-4` note. + - fixed values are especially useful for noise instruments with preset periods. + +below this is a short bar that controls macro loop and release. - click to set the start point of a loop; the end point is the last value or release point. it appears as half-height bars. right-click to remove the loop. - shift-click to set the release point. when played, the macro will hold here until the note is released. it appears as a full-height bar. right-click to remove the release point. finally, the sequence of values can be directly edited in the text box at the bottom. - the loop start is entered as a `|`. - the release point is entered as a `/`. -- in arpeggio macros, a value starting with a `@` is an absolute note (instead of a relative shift). no matter the note entered in the pattern, `@` values will be played at that exact note. this is especially useful for noise instruments with preset periods. +- 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/c64.md b/doc/4-instrument/c64.md index bd864b031..8442560bf 100644 --- a/doc/4-instrument/c64.md +++ b/doc/4-instrument/c64.md @@ -5,15 +5,14 @@ the C64 instrument editor consists of two tabs: "C64" to control various paramet ## C64 - **Waveform**: allows selecting a waveform. - - more than one waveform can be selected at once. in that case logical AND mix of waves will occur. + - more than one waveform can be selected at once. in that case a logical AND mix of waves will occur... + - due to hardware flaws, the mixing is a bit weird and sounds different between the 6581 and the 8580. - noise is an exception. it cannot be used with any of the other waveforms. - **Attack**: determines the rising time for the sound. the bigger the value, the slower the attack. (0 to 15). - **Decay**: determines the diminishing time for the sound. the higher the value, the longer the decay (0 to 15). - **Sustain**: sets the volume level at which the sound stops decaying and holds steady (0 to 15). - **Release**: determines the rate at which the sound fades out after note off. the higher the value, the longer the release (0 to 15). - - **Duty**: specifies the width of a pulse wave (0 to 4095). - - **Ring Modulation**: when enabled, the channel's output will be multiplied with the previous channel's. - **Oscillator Sync**: enables oscillator hard sync. as the previous channel's oscillator finishes a cycle, it resets the period of the channel's oscillator, forcing the latter to have the same base frequency. this can produce a harmonically rich sound, the timbre of which can be altered by varying the synchronized oscillator's frequency. @@ -25,10 +24,9 @@ the C64 instrument editor consists of two tabs: "C64" to control various paramet - **low**: a low-pass filter. the lower the cutoff, the darker the sound. - **high**: a high-pass filter. higher cutoff values result in a less "bassy" sound. - **band**: a band-pass filter. cutoff determines which part of the sound is heard (from bass to treble). - - **ch3off**: mutes channel 3 when enabled. not sure why is this part of the chip's design, but it is. + - **ch3off**: mutes channel 3 when enabled. it was originally planned for usage with two registers where program could read current oscillator and envelope outputs, thus making vibrato and SFX generation easier. but who wanted to sacrifice one channel out of three! so aforementioned was just done in software, and the feature was never used. + - multiple filter modes can be selected simultaneously. for example, selecting both "low" and "high" results in a bandstop (notch) filter. -- **Volume Macro is Cutoff Macro**: turns the volume macro in the Macros tab into a filter cutoff macro. - - volume control is global (affects entire chip output), hence the option. - **Absolute Cutoff Macro**: when enabled, the cutoff macro will go from 0 to 2047, and it will be absolute (in other words, control the cutoff directly rather than being relative). - **Absolute Duty Macro**: when enabled, the duty macro will go from 0 to 4095. - **Don't test before new note**: this option disables the one-tick hard reset and test bit before a new note. @@ -37,12 +35,26 @@ the C64 instrument editor consists of two tabs: "C64" to control various paramet - **Volume**: volume sequence. - warning: volume sequence is global! this means it controls the chip's volume and therefore affects all channels. - - this macro becomes **Cutoff** when the **Volume Macro is Cutoff Macro** option is enabled in the C64 tab. - **Arpeggio**: pitch sequence. - **Duty**: pulse width sequence. - **Waveform**: select the waveform used by instrument. - **Pitch**: fine pitch. +- **Cutoff**: filter cutoff. - **Filter mode**: select the filter mode. - **Resonance**: filter resonance sequence. -- **Special**: ring and oscillator sync selector. -- **Test/Gate**: when on, the test bit is set, which mutes the channel. +- **Special**: ring and oscillator sync selector, as well as: + - **gate bit**: + - set (1): key on. if previous state was 0 it triggers envelope start/restart; if previous state was 1, it does nothing. + - reset (0): key off. if previous state was 1 it triggers envelope release; if previous state was 0, it does nothing. + - **test bit**: + - set (1): immediately mute channel + - if the channel is a source of ring mod and/or hard sync, those stop working until the bit is reset. + - reset (0): unmute channel and restore ring mod/hard sync. +- **Attack**: sets envelope attack speed. + - if you modify attack speed when the envelope is in attack phase it immediately changes. +- **Decay**: sets envelope decay speed. + - if you modify decay speed when envelope is in decay phase it immediately changes. +- **Sustain**: sets envelope sustain level. + - if you modify sustain level when envelope is in sustain phase it immediately changes, although you can only go down. for example, 9-to-8 and 8-to-8 both work, but 8-to-9 immediately mutes the channel. +- **Release**: sets envelope release speed. + - if you modify release speed when envelope is in release phase it immediately changes. 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/macro-seq-bitmask.png b/doc/4-instrument/macro-seq-bitmask.png new file mode 100644 index 000000000..ce4c323f4 Binary files /dev/null and b/doc/4-instrument/macro-seq-bitmask.png differ diff --git a/doc/4-instrument/macro-seq-clip.png b/doc/4-instrument/macro-seq-clip.png new file mode 100644 index 000000000..063e92faa Binary files /dev/null and b/doc/4-instrument/macro-seq-clip.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 a7d9382fd..55ba48766 100644 --- a/doc/6-sample/README.md +++ b/doc/6-sample/README.md @@ -154,8 +154,22 @@ 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. - **Zoom**: shows and sets sample view zoom level. - **Zoom mode**: switches between "Auto" (entire sample fits in window) and "100%" (each horizontal pixel represents one sample point). + +in the sample viewer: +- left-click and drag to select a region of the sample. +- right-click to display a menu: + - **cut**: puts the selection in the sample clipboard and deletes it from the sample. + - **copy**: copies the selection into the sample clipboard. + - **paste**: inserts the sample clipboard at the start of the selection. + - **paste (replace)**: replaces the selection with the sample clipboard. + - **paste (mix)**: mixes the sample clipboard into the existing sample, beginning at the start of the selection. + - **set loop to selection**: changes loop region to match selection. + - **create wavetable from selection**: copies the selection into a new wavetable entry. 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/amiga.md b/doc/7-systems/amiga.md index af6bfb67f..981edf771 100644 --- a/doc/7-systems/amiga.md +++ b/doc/7-systems/amiga.md @@ -6,6 +6,28 @@ in this very computer music trackers were born... imported MOD files use this chip, and will set A-4 tuning to 436. +## amplitude/period modulation + +Amiga has support for (rather primitive) amplitude and period (frequency) modulation. +however, nobody has used this feature as it is rather useless, not well-documented and works in a complicated way. + +Amiga sample playback is done by two chips: Paula (the one that you probably know) and Agnus (the one that actually feeds Paula with samples). +Agnus has several DMA (direct memory access) units which read from chip memory independent of the CPU. four of these DMA units are used for samples. + +when DMA is enabled, Paula requests sample data from Agnus, and then plays these samples back. +there's a catch though. since the data bus is 16-bit, Paula requests **two** 8-bit samples at once! this explains why: +- the sample length registers are in words rather than bytes (thereby allowing samples up to 131070 in length) +- the maximum playback rate (31250Hz PAL; ~31469Hz NTSC) is two times the HBlank rate (Agnus fetches samples on HBlank, around 15625Hz on PAL or ~15734Hz on NTSC) + +during normal sample playback, the first sample is output and then the second. afterwards, two more samples are fetched, and so on. + +now, when amplitude or period modulation are enabled, things work differently. +the channel is silenced, and the two 8-bit samples are **treated as a big-endian 16-bit number**, which is then written to the next channel's volume or period. + +in the case of amplitude modulation, only the second sample is significant because the volume register uses 7 bits (to represent 0 to 64 (65 to 127 are treated as 64)) and the other bits are ignored. + +in the case of period modulation, both samples are significant. the first sample is the upper byte, and the second is the lower byte. + ## effects - `10xx`: **toggle low-pass filter.** `0` turns it off and `1` turns it on. diff --git a/doc/7-systems/c219.md b/doc/7-systems/c219.md index 44d0cafd0..89cffc7d6 100644 --- a/doc/7-systems/c219.md +++ b/doc/7-systems/c219.md @@ -7,6 +7,13 @@ this chip features: - stereo soft panning - accepts either 8-bit PCM or proprietary 8-bit µ-law compressed PCM samples +## sample memory notice + +this chip is rather unique when it comes to sample memory. be sure to read this notice. + +the channels are in groups of four. a sample bank (128KB) may be selected for each group. +if a sample that is on a different bank plays in a group, the group is switched to that bank, and other channels will be silenced. + ## effects - `11xx`: **set noise mode.** diff --git a/doc/7-systems/c64.md b/doc/7-systems/c64.md index 1688f5e70..97b434e75 100644 --- a/doc/7-systems/c64.md +++ b/doc/7-systems/c64.md @@ -68,3 +68,14 @@ two versions of aforementioned chip exist - 6581 (original chip) and 8580 (impro ## info this chip uses the [C64](../4-instrument/c64.md) instrument editor. + +## channel status + +the following icons are displayed when channel status is enabled in the pattern view: + +- channel is silent: + - ![not muted](status-C64-none.png) it's not + - ![gate bit disabled](status-C64-gate-off.png) gate bit disabled + - ![gate bit disabled and test bit enabled](status-C64-gate-off-test-on.png) gate bit disabled and test bit enabled + - ![text bit enabled](status-C64-test-on.png) test bit enabled + - ![ch3off enabled in filter mode](status-C64-ch3off.png) ch3off enabled in filter mode diff --git a/doc/7-systems/n163.md b/doc/7-systems/n163.md index a810f8106..78cc2562c 100644 --- a/doc/7-systems/n163.md +++ b/doc/7-systems/n163.md @@ -6,7 +6,7 @@ it has 256 nibbles (128 bytes) of internal RAM which is shared between channel s wavetables are variable in size and may be allocated anywhere in RAM. at least 128 nibbles (64 bytes) can be dedicated to waves, with more available if not all channels are used - waveform RAM area becomes smaller as more channels are activated, since channel registers consume 8 bytes for each channel. -Namco 163 uses time-division multiplexing for its output. this means that only one channel is output per sample (like OPLL and OPN2). therefore, its sound quality gets worse as more channels are activated. +Namco 163 uses time-division multiplexing (TDM) for its output. this means that only one channel is output per sample (like OPLL and OPN2). therefore, its sound quality gets worse as more channels are activated. ## waveform load position versus waveform position @@ -39,6 +39,13 @@ if the waveform changes (e.g. ins change, wave macro or wave synth), or the **lo - make sure to use `21xx` first! - `21xx`: **set position for 20xx.** +## chip options + +- **Initial channel limit**: sets the number of channels that will be active. higher values reduce volume and make TDM artifacts more noticeable. +- **Disable hissing**: remove TDM artifacts by mixing. sacrifices some accuracy! +- **Scale frequency to wave length**: automatically adjusts note frequency to account for differing waveform lengths. + - if disabled, note frequencies ignore waveveform length. this is how FamiTracker behaves. + ## info this chip uses the [Namco 163](../4-instrument/n163.md) instrument editor. 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/opll.md b/doc/7-systems/opll.md index 2cf068c13..4de890b22 100644 --- a/doc/7-systems/opll.md +++ b/doc/7-systems/opll.md @@ -15,8 +15,8 @@ the YM2413 is equipped with the following features: - a drum/percussion mode, replacing the last 3 voices with 5 rhythm channels, with drum mode tones hard-defined in the chip itself, like FM instruments. only pitch might be altered. - drum mode works like following: FM channel 7 is for Kick Drum, which is a normal FM channel but routed through mixer twice for 2× volume, like all drum sounds. FM channel 8 splits to Snare, Drum, and Hi-Hat. Snare Drum is the carrier and it works with a special 1 bit noise generator combined with a square wave, all possible by overriding phase-generator with some different synthesis method. Hi-Hat is the modulator and it works with the noise generator and also the special synthesis. channel 9 splits to Top-Cymbal and Tom-Tom, Top-Cymbal is the carrier and only has the special synthesis, while Tom-Tom is basically a 1op wave. - special synthesis mentioned already is: 5 square waves are gathered from 4×, 64× and 128× the pitch of channel 8 and 16× and 64× the pitch of channel 9 and they go through a process where 2 HH bits OR'd together, then 1 HH and 1 TC bit OR'd, then the two TC bits OR'd together, and those 3 results get XOR'd. -- 1 user-definable patch (this patch can be changed throughout the course of the song) -- 15 pre-defined patches which can all be used at the same time +- **1 user-definable patch (this patch can be changed throughout the course of the song)** +- **15 pre-defined patches which can all be used at the same time** - support for ADSR on both the modulator and the carrier - sine and half-sine based FM synthesis - 9 octave note control diff --git a/doc/7-systems/pce.md b/doc/7-systems/pce.md index ec6633c3e..15eda9551 100644 --- a/doc/7-systems/pce.md +++ b/doc/7-systems/pce.md @@ -19,8 +19,16 @@ furthermore, it has some PCM and LFO! - when LFO is enabled, channel 2 is muted and its output is passed to channel 1's frequency. - `13xx`: **set LFO speed.** - `17xx`: **toggle LEGACY sample mode.** - - **this effect exists only for compatibility reasons! its use is NOT recommented. use Sample type instruments instead.** + - **this effect exists only for compatibility reasons! its use is NOT recommended. use Sample type instruments instead.** ## info this chip uses the [PC Engine](../4-instrument/pce.md) instrument editor. + +## channel status + +the following icons are displayed when channel status is enabled in the pattern view: + +- noise mode (channels 5 and 6 only): + - ![noise mode off](status-PCE-noise-off.png) off + - ![noise mode on](status-PCE-noise-on.png) on 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 cf518a0d9..76da944ff 100644 --- a/doc/7-systems/snes.md +++ b/doc/7-systems/snes.md @@ -65,6 +65,22 @@ 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: + +- envelope/gain status: + - ![direct gain, envelope off](status-SNES-gain-direct.png) direct gain + - ![linear gain decrease](status-SNES-gain-dec-lin.png) linear gain decrease + - ![logarithmic gain decrease](status-SNES-gain-dec-log.png) logarithmic gain decrease + - ![linear gain increase](status-SNES-gain-inc-lin.png) linear gain increase + - ![bent-line gain increase](status-SNES-gain-inc-bent.png) bent-line gain increase + - ![envelope attack](status-SNES-env-A.png) envelope attack + - ![envelope decay](status-SNES-env-D.png) envelope decay + - ![envelope sustain](status-SNES-env-S.png) envelope sustain + - ![envelope release](status-SNES-env-R.png) envelope release ## ADSR diff --git a/doc/7-systems/status-C64-ch3off.png b/doc/7-systems/status-C64-ch3off.png new file mode 100644 index 000000000..8fd21961a Binary files /dev/null and b/doc/7-systems/status-C64-ch3off.png differ diff --git a/doc/7-systems/status-C64-gate-off-test-on.png b/doc/7-systems/status-C64-gate-off-test-on.png new file mode 100644 index 000000000..d3aab0688 Binary files /dev/null and b/doc/7-systems/status-C64-gate-off-test-on.png differ diff --git a/doc/7-systems/status-C64-gate-off.png b/doc/7-systems/status-C64-gate-off.png new file mode 100644 index 000000000..fc53a30e9 Binary files /dev/null and b/doc/7-systems/status-C64-gate-off.png differ diff --git a/doc/7-systems/status-C64-none.png b/doc/7-systems/status-C64-none.png new file mode 100644 index 000000000..d1fafa859 Binary files /dev/null and b/doc/7-systems/status-C64-none.png differ diff --git a/doc/7-systems/status-C64-test-on.png b/doc/7-systems/status-C64-test-on.png new file mode 100644 index 000000000..132d39117 Binary files /dev/null and b/doc/7-systems/status-C64-test-on.png differ diff --git a/doc/7-systems/status-PCE-noise-off.png b/doc/7-systems/status-PCE-noise-off.png new file mode 100644 index 000000000..874c8606c Binary files /dev/null and b/doc/7-systems/status-PCE-noise-off.png differ diff --git a/doc/7-systems/status-PCE-noise-on.png b/doc/7-systems/status-PCE-noise-on.png new file mode 100644 index 000000000..373022d41 Binary files /dev/null and b/doc/7-systems/status-PCE-noise-on.png differ diff --git a/doc/7-systems/status-SNES-env-A.png b/doc/7-systems/status-SNES-env-A.png new file mode 100644 index 000000000..60abd875b Binary files /dev/null and b/doc/7-systems/status-SNES-env-A.png differ diff --git a/doc/7-systems/status-SNES-env-D.png b/doc/7-systems/status-SNES-env-D.png new file mode 100644 index 000000000..e52b20784 Binary files /dev/null and b/doc/7-systems/status-SNES-env-D.png differ diff --git a/doc/7-systems/status-SNES-env-R.png b/doc/7-systems/status-SNES-env-R.png new file mode 100644 index 000000000..635121059 Binary files /dev/null and b/doc/7-systems/status-SNES-env-R.png differ diff --git a/doc/7-systems/status-SNES-env-S.png b/doc/7-systems/status-SNES-env-S.png new file mode 100644 index 000000000..0cdce9ea4 Binary files /dev/null and b/doc/7-systems/status-SNES-env-S.png differ diff --git a/doc/7-systems/status-SNES-gain-dec-lin.png b/doc/7-systems/status-SNES-gain-dec-lin.png new file mode 100644 index 000000000..5ad203ed5 Binary files /dev/null and b/doc/7-systems/status-SNES-gain-dec-lin.png differ diff --git a/doc/7-systems/status-SNES-gain-dec-log.png b/doc/7-systems/status-SNES-gain-dec-log.png new file mode 100644 index 000000000..d48907bde Binary files /dev/null and b/doc/7-systems/status-SNES-gain-dec-log.png differ diff --git a/doc/7-systems/status-SNES-gain-direct.png b/doc/7-systems/status-SNES-gain-direct.png new file mode 100644 index 000000000..6c5fd6422 Binary files /dev/null and b/doc/7-systems/status-SNES-gain-direct.png differ diff --git a/doc/7-systems/status-SNES-gain-inc-bent.png b/doc/7-systems/status-SNES-gain-inc-bent.png new file mode 100644 index 000000000..6d41a56d7 Binary files /dev/null and b/doc/7-systems/status-SNES-gain-inc-bent.png differ diff --git a/doc/7-systems/status-SNES-gain-inc-lin.png b/doc/7-systems/status-SNES-gain-inc-lin.png new file mode 100644 index 000000000..e10d8a9ed Binary files /dev/null and b/doc/7-systems/status-SNES-gain-inc-lin.png differ diff --git a/doc/7-systems/status-Swan-PCM-off.png b/doc/7-systems/status-Swan-PCM-off.png new file mode 100644 index 000000000..4596bb70c Binary files /dev/null and b/doc/7-systems/status-Swan-PCM-off.png differ diff --git a/doc/7-systems/status-Swan-PCM-on.png b/doc/7-systems/status-Swan-PCM-on.png new file mode 100644 index 000000000..a03699f1b Binary files /dev/null and b/doc/7-systems/status-Swan-PCM-on.png differ diff --git a/doc/7-systems/status-Swan-noise-off.png b/doc/7-systems/status-Swan-noise-off.png new file mode 100644 index 000000000..874c8606c Binary files /dev/null and b/doc/7-systems/status-Swan-noise-off.png differ diff --git a/doc/7-systems/status-Swan-noise-on.png b/doc/7-systems/status-Swan-noise-on.png new file mode 100644 index 000000000..373022d41 Binary files /dev/null and b/doc/7-systems/status-Swan-noise-on.png differ diff --git a/doc/7-systems/status-Swan-sweep-off.png b/doc/7-systems/status-Swan-sweep-off.png new file mode 100644 index 000000000..aaa5246b6 Binary files /dev/null and b/doc/7-systems/status-Swan-sweep-off.png differ diff --git a/doc/7-systems/status-Swan-sweep-on.png b/doc/7-systems/status-Swan-sweep-on.png new file mode 100644 index 000000000..cb968d48f Binary files /dev/null and b/doc/7-systems/status-Swan-sweep-on.png differ diff --git a/doc/7-systems/wonderswan.md b/doc/7-systems/wonderswan.md index edd189649..2ff7ac961 100644 --- a/doc/7-systems/wonderswan.md +++ b/doc/7-systems/wonderswan.md @@ -24,3 +24,17 @@ it has 4 wavetable channels. some of them have additional capabilities: ## info this chip uses the [WonderSwan](../4-instrument/wonderswan.md) instrument editor. + +## channel status + +the following icons are displayed when channel status is enabled in the pattern view: + +- PCM mode (channel 2): + - ![PCM mode off](status-Swan-PCM-off.png) off + - ![PCM mode on](status-Swan-PCM-on.png) on +- sweep (channel 3): + - ![sweep mode off](status-Swan-sweep-off.png) disabled + - ![sweep mode on](status-Swan-sweep-on.png) enabled +- noise mode (channel 4): + - ![noise mode off](status-Swan-noise-off.png) off + - ![noise mode on](status-Swan-noise-on.png) on diff --git a/doc/8-advanced/README.md b/doc/8-advanced/README.md index 16d83afa8..4a9512823 100644 --- a/doc/8-advanced/README.md +++ b/doc/8-advanced/README.md @@ -18,8 +18,13 @@ as listed in the "Window" menu: - [piano](piano.md) - [oscilloscope](osc.md) +- [oscilloscope (X-Y)](xyosc.md) - [oscilloscopes (per-channel)](chanosc.md) - [clock](clock.md) - [register view](regview.md) - [log viewer](log-viewer.md) - [stats](stats.md) + +other: + +- [command line usage](command-line.md) diff --git a/doc/8-advanced/channels.md b/doc/8-advanced/channels.md index a57698559..5b70f00dd 100644 --- a/doc/8-advanced/channels.md +++ b/doc/8-advanced/channels.md @@ -5,8 +5,8 @@ the "Channels" dialog allows manipulation of the song's channels. ![channels dialog](channels.png) each channel has the following options: -- **Visible**: uncheck the box to hide the channel from view. pattern data will be kept. +- **Visible**: uncheck the box to hide the channel from the pattern view. pattern data will be kept. - crossed-arrows button: click and drag to rearrange pattern data throughout the song. - - note: this does _not_ move channels around within a chip! it only affects pattern data. -- **Name** is the name displayed at the top of each channel in the tracker view. -- to the right of that is the abbreviation used above each channel in the order view. + - note: this does **not** move channels around! it only moves the channel's pattern data. +- **Name**: the name displayed at the top of each channel in the pattern view. +- the next setting is "short name", which is displayed in the orders view and/or when a channel is collapsed. diff --git a/doc/8-advanced/chanosc.md b/doc/8-advanced/chanosc.md index b811add81..720b550fe 100644 --- a/doc/8-advanced/chanosc.md +++ b/doc/8-advanced/chanosc.md @@ -1,25 +1,24 @@ # oscilloscope (per-channel) -the "Oscilloscope (per-channel)" dialog shows an individual oscilloscope for each channel during playback. +the "Oscilloscope (per-channel)" window displays several oscilloscope views (one per channel). ![oscilloscope per-channel configuration view](chanosc.png) -right-clicking within the view will change it to the configuration view shown above: -- **Columns**: arranges oscilloscopes into this many columns. -- **Size (ms)**: sets what length of audio is visible in each oscilloscope. -- **Center waveform**: does its best to latch the waveform to the channel's note frequency and centers the display. +right-clicking the view will display the configuration view shown above: +- **Columns**: sets the amount of columns for the oscilloscope views. +- **Size (ms)**: sets how much of a channel's output is captured for the oscilloscope view. +- **Center waveform**: when enabled, the displayed waveforms will be centered using an auto-correlation algorithm. - **Automatic columns**: sets the number of columns based on the number of channels. - **Off**: use the Columns setting. - **Mode 1**: always fewer columns than rows. - **Mode 2**: bias slightly toward more columns. - **Mode 3**: always more columns than rows. -- **Amplitude**: scales amplitude for all oscilloscopes. -- **Gradient**: see below. -- the color selector sets the color for all waveforms. right-clicking on it pops up an option dialog: - - select between the square selector and the color wheel selector. - - **Alpha bar**: adds a transparency selector. -- the boxes below that are for selecting colors numerically by red-green-blue-alpha, hue-saturation-value-alpha, and HTML-style RGBA in hex. -- **Text format**: this string determins what text is shown in the top-left of each oscilloscope. it can be any text, and the following shortcodes will be replaced with information about the channel: +- **Amplitude**: scales amplitude for all oscilloscope views. +- **Gradient**: this allows you to use a gradient for determining the waveforms' colors instead of a single color. see the gradient section for more information. + - if this option is off, a color selector will be displayed. right-click on it for some options: + - select between the square selector and the color wheel selector. + - **Alpha bar**: display an opacity bar. +- **Text format**: this allows you to display some text on each oscilloscope view. the following codes may be used: - `%c`: channel name - `%C`: channel short name - `%d`: channel number (starting from 0) @@ -35,27 +34,32 @@ right-clicking within the view will change it to the configuration view shown ab - `%V`: volume (percentage) - `%b`: volume (hex) - `%%`: percent sign -- the OK button returns from options view to the oscilloscopes. + +click on OK to return to the main view. ## gradient ![oscilloscope per-channel gradient configuration view](chanosc-gradient.png) -in this mode, the color selector is replaced by a square field onto which circular "stops" can be placed. each stop adds a soft circle of color. the resulting image is used to look up the oscilloscope color as determined by each axis. +when enabling the Gradient setting, a gradient view is displayed in where circular "points" can be placed. +each point adds a color spot. +the resulting image is used to look up the waveform's color as determined by each axis. -- right-click to place a stop. -- left-click on a stop to change its color. the color selector is the same as above, with two additions: +- right-click to place a point. +- left-click on a point to change its color: + - a color picker is displayed, alongside two settings. - **Distance**: the size of the circle. - - **Spread**: the size of the solid center of the circle. increasing it fills more of the circle with the target color. + - **Spread**: the size of the solid center of the circle. increasing it fills more of the circle with the color. +- middle-click on a point to delete it. -- **Background**: sets background color for entire field. -- **X Axis**: determines what the horizontal maps to, from left to right. -- **Y Axis**: determines what the vertical maps to. from bottom to top. these can be set to the following: - - **None (0%)**: stays at the left or bottom. - - **None (50%)**: stays at the center. - - **None (100%)**: stays at the right or top. - - **Frequency**: changes color with note frequency. - - **Volume**: changes color with volume. - - **Channel**: changes color based on channel number. - - **Brightness**: currently does nothing. - - **Note Trigger**: changes color when a new note is played. +- **Background**: sets the gradient's background color. +- **X Axis**: determines what the horizontal axis maps to. +- **Y Axis**: determines what the vertical axis maps to. these can be set to the following: + - **None (0%)**: always left or bottom + - **None (50%)**: always center + - **None (100%)**: always right or top + - **Frequency**: changes color with frequency + - **Volume**: changes color with volume + - **Channel**: changes color based on channel number (first channel is 0% and last is 100%) + - **Brightness**: currently does nothing + - **Note Trigger**: changes color when a new note is played diff --git a/doc/8-advanced/chip-manager-change.png b/doc/8-advanced/chip-manager-change.png deleted file mode 100644 index 4e9a29f2c..000000000 Binary files a/doc/8-advanced/chip-manager-change.png and /dev/null differ diff --git a/doc/8-advanced/chip-manager-remove.png b/doc/8-advanced/chip-manager-remove.png index 3e80749d4..c7b8472ea 100644 Binary files a/doc/8-advanced/chip-manager-remove.png and b/doc/8-advanced/chip-manager-remove.png differ diff --git a/doc/8-advanced/chip-manager.md b/doc/8-advanced/chip-manager.md index d0cb89943..72d8ff66a 100644 --- a/doc/8-advanced/chip-manager.md +++ b/doc/8-advanced/chip-manager.md @@ -1,15 +1,15 @@ # chip manager -the **chip manager** window does exactly what it says. +the **chip manager** window allows you to manage chips, including adding, changing, moving or removing them. ![chip manager](chip-manager.png) -**Preserve channel order**: make existing pattern data stay in place even when chips are rearranged. if turned off, pattern data will rearrange to match (the default, and usually the desired behavior). +**Preserve channel order**: make existing pattern data stay in place even when chips are rearranged. when turned off, pattern data will be arranged to match (the default, and usually desired behavior). -to move a chip around, click and drag the ![crossed-arrows](chip-manager-move.png) button to its left. +to move a chip around, click and drag the ![crossed-arrows](chip-manager-move.png) button to the left. -to replace a chip with a different one, click the ![down-angle](chip-manager-change.png) and select the replacement. +to replace a chip with a different one, click the **Change** button. this will display the chip selector. -to remove a chip entirely, click the ![X](chip-manager-remove.png) button. +to remove a chip, click the ![X](chip-manager-remove.png) button. -click a chip's name to open its options, where one can set clock rate, chip variant, and other specifics. \ No newline at end of file +click on a chip's name to open chip configuration. this allows you to change chip options, such as clock rate, chip type and so on. diff --git a/doc/8-advanced/chip-manager.png b/doc/8-advanced/chip-manager.png index fbc5045c2..b746d85d0 100644 Binary files a/doc/8-advanced/chip-manager.png and b/doc/8-advanced/chip-manager.png differ diff --git a/doc/8-advanced/command-line.md b/doc/8-advanced/command-line.md new file mode 100644 index 000000000..cc5d4f0df --- /dev/null +++ b/doc/8-advanced/command-line.md @@ -0,0 +1,100 @@ +# command line usage + +## NAME + +Furnace - a chiptune tracker + +## SYNOPSIS + +`furnace [params...] [file]` + +## DESCRIPTION + +Furnace is a chiptune tracker that supports many systems and sound chips from the 8/16-bit era. +even though it is primarily controlled by using its graphical user interface, Furnace also offers a command line interface, which is described here. + +## USAGE + +starting Furnace without arguments will start the graphical user interface (GUI), as long as Furnace has been compiled with GUI enabled. + +passing the path to a file will open that file at start-up. if Furnace cannot open that file, it will report an error and quit. + +the following parameters may be used: + +**general** + +- `-help`: display the following help. +- `-console`: enable command-line interface (CLI) player. + - see the COMMAND LINE INTERFACE section for more information +- `-loglevel `: set the logging level to one of the following: + - `error`: critical errors only + - `warning`: errors and warnings + - `info`: errors, warnings, and useful information + - `debug`: all of the above, including debug information + - `trace`: like debug, but with even more details (default) + +- `-info`: get information about a song. + - you must provide a file, otherwise Furnace will quit. + +- `-version`: display version information. +- `-warranty`: view warranty disclaimer. + +**engine** + +- `-audio sdl|jack|portaudio`: override audio backend to one of the following: + - `sdl`: SDL (default) + - `jack`: JACK Audio Connection Kit + - `portaudio`: PortAudio +- `-view `: set visualization of data to one of the following: + - `pattern`: order and pattern + - `commands`: engine commands + - `nothing`: guess (default) +- `-loops `: set number of loops + - `-1` means loop forever. +- `-subsong `: set sub-song to play. +- `-safemode`: enable safe mode (software rendering without audio). +- `-safeaudio`: enable safe mode (software rendering with audio). +- `-benchmark render|seek`: run performance test and output total time. + - `render`: measure render time + - `seek`: measure time to seek through the entire song + - you must provide a file, otherwise Furnace will quit. + +**audio export** + +- `-output path`: export audio in .wav format to `path`. + - you must provide a file, otherwise Furnace will quit. +- `-outmode one|persys|perchan`: set audio export output mode. + - `one`: single file (default) + - `persys`: one file per chip (`_sXX` will be appended to file name, where `XX` is the chip number) + - `perchan`: one file per channel (`_cXX` will be appended to file name, where `XX` is the channel number) + +**VGM export** + +- `-vgmout path`: output VGM data to `path`. + - you must provide a file, otherwise Furnace will quit. +- `-direct`: enable VGM export direct stream mode. + - this mode is useful for DualPCM export. + - note that this will increase file size by a huge amount! + +**export (other)** + +- `-zsmout path`: output Zsound Music data for Commander X16. + - you must provide a file, otherwise Furnace will quit. + +- `-cmdout path`: output command stream dump to `path`. + - you must provide a file, otherwise Furnace will quit. +- `-binary`: set command stream output format to binary. + +## COMMAND LINE INTERFACE + +Furnace provides a command-line interface (CLI) player which may be activated through the `-console` option. + +the following controls may be used: + +- `Left`/`H`: go to previous order. +- `Right`/`L`: go to next order. +- `Space`: pause/resume playback. + +## SEE ALSO + +the Furnace user manual in the `manual.pdf` file. diff --git a/doc/8-advanced/comments.md b/doc/8-advanced/comments.md index ebb05d700..eb0fae144 100644 --- a/doc/8-advanced/comments.md +++ b/doc/8-advanced/comments.md @@ -4,4 +4,4 @@ comments, credits, or any arbitrary text may be entered here. it has no effect on the song. -there is no word wrap; long lines must be broken manually with the Enter key. +there is no word wrap; long lines must be broken manually with the Enter/Return key. diff --git a/doc/8-advanced/compat-flags.md b/doc/8-advanced/compat-flags.md index 6e050a410..e5ab02826 100644 --- a/doc/8-advanced/compat-flags.md +++ b/doc/8-advanced/compat-flags.md @@ -1,5 +1,5 @@ # compatibility flags -the **Compatibility Flags** window contains several tabs full of settings that change aspects of tracking and playback. a new Furnace song will have these disabled, while opening a DefleMask module, .mod, or earlier Furnace file will automatically set the appropriate options. +the **Compatibility Flags** window contains several tabs full of settings that change playback behavior. a new Furnace song will have these disabled, while opening a DefleMask module, .mod, or earlier Furnace file will automatically set the appropriate options. hovering over most options will bring up additional info about them. it is not recommended to change any of these, especially the ones in the DefleMask and Old Furnace sections. diff --git a/doc/8-advanced/grooves.md b/doc/8-advanced/grooves.md index 38775c5ab..3f566a7de 100644 --- a/doc/8-advanced/grooves.md +++ b/doc/8-advanced/grooves.md @@ -1,9 +1,6 @@ # grooves -grooves are macros for speed. - -a **groove** is the equivalent of repeating `0Fxx` commands on each row to get a cycle of speeds. for example, a groove of "6 4 5 3" makes the first row 6 ticks long, the next row 4 ticks, then 5, 3, 6, 4, 5, 3... - +a **groove** is the equivalent of repeating `0Fxx` effects on each row to get a cycle of speeds. for example, a groove of "6 4 5 3" makes the first row 6 ticks long, the next row 4 ticks, then 5, 3, 6, 4, 5, 3... ![groove](groove.png) @@ -13,18 +10,17 @@ to set the song's groove: - click again so it becomes "Groove". - enter a sequence of up to 16 speeds. - ![groove patterns](grooves.png) -the "Grooves" window is for entering preset groove patterns. -- the **`+`** button adds a new groove pattern; click in the pattern to edit it. +the "Grooves" window displays the list of groove patterns in the song. +- the **`+`** button adds a new groove pattern; click in the groove pattern to edit it. - the **`×`** buttons remove them. a single `09xx` command will switch to the matching numbered groove pattern. -## BPM +## tempo -this is a non-exhaustive list of grooves and their equivalent BPM. +this is a non-exhaustive list of grooves and their equivalent tempo in BPM. note: this table assumes a song's tick rate setting is left at its default value for the chosen engine speed: 60 for NTSC, or 50 for PAL. diff --git a/doc/8-advanced/osc.md b/doc/8-advanced/osc.md index db625e71c..145a26e39 100644 --- a/doc/8-advanced/osc.md +++ b/doc/8-advanced/osc.md @@ -1,9 +1,9 @@ # oscilloscope -the Oscilloscope shows the waveform of the mix of all currently playing sounds. +the Oscilloscope shows the audio output as a waveform. ![oscilloscope view](osc.png) -right-clicking on the oscilloscope toggles the adjustment sliders: -- waveform height zoom -- width of viewed audio (window size) in milliseconds. +right-clicking on the oscilloscope toggles adjustment sliders: +- waveform height (zoom) +- window size (how much of the output to display) in milliseconds. diff --git a/doc/8-advanced/xyosc.md b/doc/8-advanced/xyosc.md new file mode 100644 index 000000000..e03a1f945 --- /dev/null +++ b/doc/8-advanced/xyosc.md @@ -0,0 +1,19 @@ +# oscilloscope (X-Y) + +also called "vectorscope", this is similar to the normal oscilloscope in that it draws audio output as a waveform, but instead of being one-dimensional and going from left to right, it is plotted in two dimensions (usually X represents left output and Y represents right output). + +(TODO: image) + +right-clicking the X-Y oscilloscope window displays settings: +- **X Channel**: the output channel which will affect the X (horizontal) axis. default is 1 (left). + - **Invert**: flips the axis, therefore flipping the view horizontally. +- **Y Channel**: the output channel which will affect the Y (vertical) axis. default is 2 (right). + - **Invert**: flips the axis, therefore flipping the view vertically. +- **Zoom**: changes amplitude, making the plot bigger or smaller. +- **Samples**: the maximum number of samples to use for plotting. + - this setting may be replaced by another or removed in a future version. +- **Decay Time (ms)**: sets how long it takes for the points/lines to fade away. +- **Intensity**: changes the brightness of the dots/lines. +- **Line Thickness**: sets how thick lines are. + +click **OK** to return to the oscilloscope view. 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/res/macLicense/en.license.txt b/res/macLicense/en.license.txt index d159169d1..98c4f2f28 100644 --- a/res/macLicense/en.license.txt +++ b/res/macLicense/en.license.txt @@ -1,3 +1,24 @@ +YOU SHALL READ THE FOLLOWING TEXT BEFORE PROCEEDING!!! +IF YOU DO NOT READ IT, YOU MAY NOT BE ABLE TO USE FURNACE AT ALL. + + +if you are using a recent version of macOS, you may get an error saying that Furnace is damaged. +in that case, open Terminal, and type this: + +``` +xattr -d com.apple.quarantine /path/to/Furnace.app +``` + +(replace `/path/to/Furnace.app` with the path where Furnace.app is located, e.g. `/Applications/Furnace.app`) + +you may need to reboot after doing this before launching Furnace. + +END OF MACOS ISSUE ACKNOWLEDGEMENT - license text follows. + +------------------ + + + GNU GENERAL PUBLIC LICENSE Version 2, June 1991 diff --git a/src/divasm/divasm.h b/src/divasm/divasm.h new file mode 100644 index 000000000..2846c98f8 --- /dev/null +++ b/src/divasm/divasm.h @@ -0,0 +1,30 @@ +struct DivASMResult { + int line, err; + DivASMResult(): + line(-1), + err(0) {} +}; + +struct DivASMFile { + String name; + SafeReader* data; +}; + +enum DivASMTarget { + DIV_ASM_TARGET_DUMMY=0, + DIV_ASM_TARGET_6502, + DIV_ASM_TARGET_SPC700 +}; + +class DivASM { + std::vector files; + SafeWriter* result; + + public: + DivASMResult getError(); + SafeWriter* assemble(String name); + void addFile(String name, SafeReader* data); + + DivASM(DivASMTarget target); + ~DivASM(); +}; diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 2f6b7c04a..5860a3736 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) @@ -589,6 +591,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, from 0.0 to 1.0. + * @return output volume. + */ + virtual int mapVelocity(int ch, float vel); + /** * get the lowest note in a portamento. * @param ch the channel in question. @@ -782,7 +792,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 4148c2a3e..878232ca0 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -427,34 +427,74 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_OPL: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(1,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(1,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL2: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(2,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL2_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(2,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPL3: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(3,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3Core",0)); + } break; case DIV_SYSTEM_OPL3_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(3,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl3Core",0)); + } break; case DIV_SYSTEM_Y8950: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(8950,false); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_Y8950_DRUMS: dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(8950,true); + if (isRender) { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2CoreRender",0)); + } else { + ((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl2Core",0)); + } break; case DIV_SYSTEM_OPZ: dispatch=new DivPlatformTX81Z; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 00f940740..b2150ec1b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1519,6 +1519,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) { @@ -1530,6 +1535,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) { @@ -2175,6 +2185,13 @@ int DivEngine::getMaxVolumeChan(int ch) { return chan[ch].volMax>>8; } +int DivEngine::mapVelocity(int ch, float vel) { + if (ch<0) return 0; + if (ch>=chans) return 0; + if (disCont[dispatchOfChan[ch]].dispatch==NULL) return 0; + return disCont[dispatchOfChan[ch]].dispatch->mapVelocity(dispatchChanOfChan[ch],vel); +} + unsigned char DivEngine::getOrder() { return prevOrder; } @@ -3375,6 +3392,14 @@ void DivEngine::setMidiDirect(bool value) { midiIsDirect=value; } +void DivEngine::setMidiDirectProgram(bool value) { + midiIsDirectProgram=value; +} + +void DivEngine::setMidiVolExp(float value) { + midiVolExp=value; +} + void DivEngine::setMidiCallback(std::function what) { midiCallback=what; } diff --git a/src/engine/engine.h b/src/engine/engine.h index 43af4127f..4170119e9 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 @@ -176,25 +176,25 @@ struct DivNoteEvent { signed char channel; unsigned char ins; signed char note, volume; - bool on, nop, pad1, pad2; - DivNoteEvent(int c, int i, int n, int v, bool o): + bool on, nop, insChange, fromMIDI; + DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false): channel(c), ins(i), note(n), volume(v), on(o), nop(false), - pad1(false), - pad2(false) {} + insChange(ic), + fromMIDI(fm) {} DivNoteEvent(): channel(-1), ins(0), note(0), - volume(0), + volume(-1), on(false), nop(true), - pad1(false), - pad2(false) {} + insChange(false), + fromMIDI(false) {} }; struct DivDispatchContainer { @@ -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_), @@ -409,6 +415,7 @@ class DivEngine { bool firstTick; bool skipping; bool midiIsDirect; + bool midiIsDirectProgram; bool lowLatency; bool systemsRegistered; bool hasLoadedSomething; @@ -417,6 +424,7 @@ class DivEngine { bool midiOutProgramChange; int midiOutMode; int midiOutTimeRate; + float midiVolExp; int softLockCount; int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed; size_t curSubSongIndex; @@ -641,6 +649,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 @@ -842,6 +852,9 @@ class DivEngine { // get channel max volume int getMaxVolumeChan(int chan); + // map MIDI velocity to volume + int mapVelocity(int ch, float vel); + // get current order unsigned char getOrder(); @@ -1176,6 +1189,12 @@ class DivEngine { // set MIDI direct channel map void setMidiDirect(bool value); + // set MIDI direct program change + void setMidiDirectProgram(bool value); + + // set MIDI volume curve exponent + void setMidiVolExp(float value); + // set MIDI input callback // if the specified function returns -2, note feedback will be inhibited. void setMidiCallback(std::function what); @@ -1245,6 +1264,7 @@ class DivEngine { firstTick(false), skipping(false), midiIsDirect(false), + midiIsDirectProgram(false), lowLatency(false), systemsRegistered(false), hasLoadedSomething(false), @@ -1253,6 +1273,7 @@ class DivEngine { midiOutProgramChange(false), midiOutMode(DIV_MIDI_MODE_NOTE), midiOutTimeRate(0), + midiVolExp(2.0f), // General MIDI standard softLockCount(0), subticks(0), ticks(0), diff --git a/src/engine/export/amigaValidation.cpp b/src/engine/export/amigaValidation.cpp index 56d3814a7..e1aa4dfdb 100644 --- a/src/engine/export/amigaValidation.cpp +++ b/src/engine/export/amigaValidation.cpp @@ -149,7 +149,7 @@ std::vector DivExportAmigaValidation::go(DivEngine* e) { } else if (waveNum<65536) { seq->writeC((i<<4)|4); seq->writeS_BE(waveNum); - } else{ + } else { seq->writeC((i<<4)|1); seq->writeC(waves[waveNum].pos>>16); seq->writeC(waves[waveNum].pos>>8); 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..9886dae37 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, float vel) { + const int volMax=MAX(1,dispatch(DivCommand(DIV_CMD_GET_VOLMAX,MAX(ch,0)))); + return round(vel*volMax); +} + int DivDispatch::getPortaFloor(int ch) { return 0x00; } diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index a4e279dad..a884c21ce 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -81,7 +81,18 @@ const char** DivPlatformAmiga::getRegisterSheet() { void DivPlatformAmiga::acquire(short** buf, size_t len) { thread_local int outL, outR, output; + for (size_t h=0; h=64) { + if ((amiga.audVol[i]&127)>=64) { output=amiga.nextOut[i]<<6; - } else if (amiga.audVol[i]<=0) { + } else if ((amiga.audVol[i]&127)==0) { output=0; } else { - output=amiga.nextOut[i]*volTable[amiga.audVol[i]][amiga.volPos]; + output=amiga.nextOut[i]*volTable[amiga.audVol[i]&63][amiga.volPos]; } if (i==0 || i==3) { outL+=(output*sep1)>>7; @@ -168,7 +182,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) { outL+=(output*sep2)>>7; outR+=(output*sep1)>>7; } - oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]))<<1; + oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]&127))<<1; } else { oscBuf[i]->data[oscBuf[i]->needle++]=0; } @@ -190,11 +204,6 @@ void DivPlatformAmiga::irq(int ch) { if (chan[ch].irLocL==0x400 && chan[ch].irLocH==0 && chan[ch].irLen==1) { // turn off DMA rWrite(0x96,1<>1]=val; - - if (!skipRegisterWrites && dumpWrites) { - addWrite(addr,val); - } - switch (addr&0x1fe) { case 0x96: { // DMACON if (val&32768) { - if (val&1) amiga.audEn[0]=true; - if (val&2) amiga.audEn[1]=true; - if (val&4) amiga.audEn[2]=true; - if (val&8) amiga.audEn[3]=true; - if (val&512) amiga.dmaEn=true; + if (val&1) audEn[0]=true; + if (val&2) audEn[1]=true; + if (val&4) audEn[2]=true; + if (val&8) audEn[3]=true; + if (val&512) dmaEn=true; } else { if (val&1) { - amiga.audEn[0]=false; - UPDATE_DMA(0); + audEn[0]=false; } if (val&2) { - amiga.audEn[1]=false; - UPDATE_DMA(1); + audEn[1]=false; } if (val&4) { - amiga.audEn[2]=false; - UPDATE_DMA(2); + audEn[2]=false; } if (val&8) { - amiga.audEn[3]=false; - UPDATE_DMA(3); + audEn[3]=false; } if (val&512) { - amiga.dmaEn=false; + dmaEn=false; } } break; } case 0x9a: { // INTENA if (val&32768) { - if (val&128) amiga.audInt[0]=true; - if (val&256) amiga.audInt[1]=true; - if (val&512) amiga.audInt[2]=true; - if (val&1024) amiga.audInt[3]=true; + if (val&128) audInt[0]=true; + if (val&256) audInt[1]=true; + if (val&512) audInt[2]=true; + if (val&1024) audInt[3]=true; } else { - if (val&128) amiga.audInt[0]=false; - if (val&256) amiga.audInt[1]=false; - if (val&512) amiga.audInt[2]=false; - if (val&1024) amiga.audInt[3]=false; + if (val&128) audInt[0]=false; + if (val&256) audInt[1]=false; + if (val&512) audInt[2]=false; + if (val&1024) audInt[3]=false; } break; } case 0x9c: { // INTREQ if (val&32768) { if (val&128) { - amiga.audIr[0]=true; - irq(0); + audIr[0]=true; } if (val&256) { - amiga.audIr[1]=true; - irq(1); + audIr[1]=true; } if (val&512) { - amiga.audIr[2]=true; - irq(2); + audIr[2]=true; } if (val&1024) { - amiga.audIr[3]=true; - irq(3); + audIr[3]=true; } } else { - if (val&128) amiga.audIr[0]=false; - if (val&256) amiga.audIr[1]=false; - if (val&512) amiga.audIr[2]=false; - if (val&1024) amiga.audIr[3]=false; + if (val&128) audIr[0]=false; + if (val&256) audIr[1]=false; + if (val&512) audIr[2]=false; + if (val&1024) audIr[3]=false; } break; } case 0x9e: { // ADKCON if (val&32768) { - if (val&1) amiga.useV[0]=true; - if (val&2) amiga.useV[1]=true; - if (val&4) amiga.useV[2]=true; - if (val&8) amiga.useV[3]=true; - if (val&16) amiga.useP[0]=true; - if (val&32) amiga.useP[1]=true; - if (val&64) amiga.useP[2]=true; - if (val&128) amiga.useP[3]=true; + if (val&1) useV[0]=true; + if (val&2) useV[1]=true; + if (val&4) useV[2]=true; + if (val&8) useV[3]=true; + if (val&16) useP[0]=true; + if (val&32) useP[1]=true; + if (val&64) useP[2]=true; + if (val&128) useP[3]=true; } else { - if (val&1) amiga.useV[0]=false; - if (val&2) amiga.useV[1]=false; - if (val&4) amiga.useV[2]=false; - if (val&8) amiga.useV[3]=false; - if (val&16) amiga.useP[0]=false; - if (val&32) amiga.useP[1]=false; - if (val&64) amiga.useP[2]=false; - if (val&128) amiga.useP[3]=false; + if (val&1) useV[0]=false; + if (val&2) useV[1]=false; + if (val&4) useV[2]=false; + if (val&8) useV[3]=false; + if (val&16) useP[0]=false; + if (val&32) useP[1]=false; + if (val&64) useP[2]=false; + if (val&128) useP[3]=false; } break; } @@ -316,31 +310,31 @@ void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) { bool updateDMA=false; switch (addr&15) { case 0: // LCH - amiga.audLoc[ch]&=0xffff; - amiga.audLoc[ch]|=val<<16; + audLoc[ch]&=0xffff; + audLoc[ch]|=val<<16; updateDMA=true; break; case 2: // LCL - amiga.audLoc[ch]&=0xffff0000; - amiga.audLoc[ch]|=val&0xfffe; + audLoc[ch]&=0xffff0000; + audLoc[ch]|=val&0xfffe; updateDMA=true; break; case 4: // LEN - amiga.audLen[ch]=val; + audLen[ch]=val; updateDMA=true; break; case 6: // PER - amiga.audPer[ch]=val; + audPer[ch]=val; break; case 8: // VOL - amiga.audVol[ch]=val; + audVol[ch]=val; break; case 10: // DAT - amiga.audDat[0][ch]=val&0xff; - amiga.audDat[1][ch]=val>>8; + audDat[0][ch]=val&0xff; + audDat[1][ch]=val>>8; break; } - if (updateDMA && !amiga.audEn[ch]) { + if (updateDMA && !mustDMA[ch]) { UPDATE_DMA(ch); } } @@ -349,6 +343,20 @@ void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) { } } +void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) { + if (addr&1) return; + + //logV("%.3x = %.4x",addr,val); + if (!skipRegisterWrites) { + writes.push(QueuedWrite(addr,val)); + regPool[addr>>1]=val; + + if (dumpWrites) { + addWrite(addr,val); + } + } +} + void DivPlatformAmiga::updateWave(int ch) { for (int i=0; i=0 && chan[i].samplesong.sampleLen) { @@ -509,9 +524,16 @@ void DivPlatformAmiga::tick(bool sysTick) { if (dmaOn) rWrite(0x96,0x8000|dmaOn); for (int i=0; i<4; i++) { - if ((dmaOn&(1< writes; + friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); friend class DivExportAmigaValidation; diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 9928372cc..f8c5bde9e 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -719,6 +719,10 @@ DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformAY8910::mapVelocity(int ch, float vel) { + return round(15.0*pow(vel,0.33)); +} + unsigned char* DivPlatformAY8910::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 354557fda..642ae59cf 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -131,6 +131,7 @@ class DivPlatformAY8910: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void flushWrites(); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index a337a2a31..2199d5ccd 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -718,6 +718,10 @@ DivDispatchOscBuffer* DivPlatformAY8930::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformAY8930::mapVelocity(int ch, float vel) { + return round(31.0*pow(vel,0.22)); +} + unsigned char* DivPlatformAY8930::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index ba2f9abea..64f8e9318 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -132,6 +132,7 @@ class DivPlatformAY8930: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/fmshared_OPN.h b/src/engine/platform/fmshared_OPN.h index 32ea4c002..e7d06c7f5 100644 --- a/src/engine/platform/fmshared_OPN.h +++ b/src/engine/platform/fmshared_OPN.h @@ -186,6 +186,17 @@ class DivPlatformOPN: public DivPlatformFMBase { void setCombo(bool combo) { useCombo=combo; } + virtual int mapVelocity(int ch, float vel) { + if (ch==csmChan) return vel*127.0; + if (ch==adpcmBChanOffs) return vel*255.0; + if (ch>=adpcmAChanOffs) { + if (vel==0) return 0; + if (vel>=1.0) return 31; + return CLAMP(round(32.0-(56.0-log2(vel*127.0)*8.0)),0,31); + } + if (ch>=psgChanOffs) return round(15.0*pow(vel,0.33)); + return DivPlatformFMBase::mapVelocity(ch,vel); + } }; #endif diff --git a/src/engine/platform/fmsharedbase.h b/src/engine/platform/fmsharedbase.h index e458904eb..f20292a0d 100644 --- a/src/engine/platform/fmsharedbase.h +++ b/src/engine/platform/fmsharedbase.h @@ -121,6 +121,20 @@ class DivPlatformFMBase: public DivDispatch { } } } + + virtual int mapVelocity(int ch, float vel) { + // -0.75dB per step + // -6: 64: 8 + // -12: 32: 16 + // -18: 16: 24 + // -24: 8: 32 + // -30: 4: 40 + // -36: 2: 48 + // -42: 1: 56 + if (vel==0) return 0; + if (vel>=1.0) return 127; + return CLAMP(round(128.0-(56.0-log2(vel*127.0)*8.0)),0,127); + } friend void putDispatchChan(void*,int,int); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 4973a323d..c90404eae 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); @@ -1294,6 +1300,12 @@ DivDispatchOscBuffer* DivPlatformGenesis::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformGenesis::mapVelocity(int ch, float vel) { + if (ch==csmChan) return DivPlatformOPN::mapVelocity(ch,vel); + if (ch>5) return DivPlatformOPN::mapVelocity(5,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + unsigned char* DivPlatformGenesis::getRegisterPool() { return regPool; } @@ -1309,7 +1321,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 +1410,7 @@ int DivPlatformGenesis::getPortaFloor(int ch) { return 0; } -void DivPlatformGenesis::setYMFM(bool use) { +void DivPlatformGenesis::setYMFM(unsigned char use) { useYMFM=use; } @@ -1441,7 +1455,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..a12e6625d 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); @@ -109,6 +112,7 @@ class DivPlatformGenesis: public DivPlatformOPN { virtual unsigned short getPan(int chan); DivSamplePos getSamplePos(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); + virtual int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); @@ -116,7 +120,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/genesisext.cpp b/src/engine/platform/genesisext.cpp index aab02e1e1..0097f763f 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -818,6 +818,12 @@ DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) { return NULL; } +int DivPlatformGenesisExt::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformGenesis::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformGenesis::mapVelocity(extChanOffs,vel); + return DivPlatformGenesis::mapVelocity(ch,vel); +} + void DivPlatformGenesisExt::reset() { DivPlatformGenesis::reset(); diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index 63112c069..d6ab86924 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -36,6 +36,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 52968a1a5..c68271743 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) { @@ -1609,6 +2104,21 @@ DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformOPL::mapVelocity(int ch, float vel) { + if (ch==adpcmChan) return vel*255.0; + // -0.75dB per step + // -6: 64: 8 + // -12: 32: 16 + // -18: 16: 24 + // -24: 8: 32 + // -30: 4: 40 + // -36: 2: 48 + // -42: 1: 56 + if (vel==0) return 0; + if (vel>=1.0) return 63; + return CLAMP(round(64.0-(56.0-log2(vel*127.0)*8.0)),0,63); +} + unsigned char* DivPlatformOPL::getRegisterPool() { return regPool; } @@ -1620,17 +2130,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 +2305,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 +2452,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 +2553,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 +2598,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..bef02db5e 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); @@ -117,6 +150,7 @@ class DivPlatformOPL: public DivDispatch { unsigned short getPan(int chan); DivChannelPair getPaired(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); @@ -124,7 +158,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/opll.cpp b/src/engine/platform/opll.cpp index 3f1621c02..9228568ef 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -962,6 +962,13 @@ DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformOPLL::mapVelocity(int ch, float vel) { + // -3dB per step + if (vel==0) return 0; + if (vel>=1.0) return 15; + return CLAMP(round(16.0-(14.0-log2(vel*127.0)*2.0)),0,15); +} + unsigned char* DivPlatformOPLL::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index e8bd627a4..70ece9c1e 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -95,6 +95,7 @@ class DivPlatformOPLL: public DivDispatch { void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 037a1cd69..1cff48278 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -539,6 +539,10 @@ DivDispatchOscBuffer* DivPlatformPCE::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformPCE::mapVelocity(int ch, float vel) { + return round(31.0*pow(vel,0.22)); +} + unsigned char* DivPlatformPCE::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index c2649eb05..591c94cef 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -86,6 +86,7 @@ class DivPlatformPCE: public DivDispatch { DivChannelModeHints getModeHints(int chan); DivSamplePos getSamplePos(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); 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/sms.cpp b/src/engine/platform/sms.cpp index 7aa0455ca..0b5e6183e 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -462,6 +462,10 @@ DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformSMS::mapVelocity(int ch, float vel) { + return round(15.0*pow(vel,0.33)); +} + unsigned char* DivPlatformSMS::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 5eaf204ba..092010a56 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -79,6 +79,7 @@ class DivPlatformSMS: public DivDispatch { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); 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/t6w28.cpp b/src/engine/platform/t6w28.cpp index f2dfe7120..8fd0cb2ee 100644 --- a/src/engine/platform/t6w28.cpp +++ b/src/engine/platform/t6w28.cpp @@ -308,6 +308,10 @@ DivDispatchOscBuffer* DivPlatformT6W28::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformT6W28::mapVelocity(int ch, float vel) { + return round(15.0*pow(vel,0.33)); +} + unsigned char* DivPlatformT6W28::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/t6w28.h b/src/engine/platform/t6w28.h index 5e69c6268..eae0f92f9 100644 --- a/src/engine/platform/t6w28.h +++ b/src/engine/platform/t6w28.h @@ -65,6 +65,7 @@ class DivPlatformT6W28: public DivDispatch { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); 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/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index adb312ad8..bcb84e19b 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -692,6 +692,12 @@ DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) { return NULL; } +int DivPlatformYM2203Ext::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + void DivPlatformYM2203Ext::reset() { DivPlatformYM2203::reset(); diff --git a/src/engine/platform/ym2203ext.h b/src/engine/platform/ym2203ext.h index 731e2a202..f0e468158 100644 --- a/src/engine/platform/ym2203ext.h +++ b/src/engine/platform/ym2203ext.h @@ -34,6 +34,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 { void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 50424d4eb..589bd542a 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -767,6 +767,12 @@ DivDispatchOscBuffer* DivPlatformYM2608Ext::getOscBuffer(int ch) { return NULL; } +int DivPlatformYM2608Ext::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + void DivPlatformYM2608Ext::reset() { DivPlatformYM2608::reset(); diff --git a/src/engine/platform/ym2608ext.h b/src/engine/platform/ym2608ext.h index 0c9c1a418..159a6c807 100644 --- a/src/engine/platform/ym2608ext.h +++ b/src/engine/platform/ym2608ext.h @@ -35,6 +35,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 545a80e98..c0b0282b2 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -757,6 +757,12 @@ DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) { return NULL; } +int DivPlatformYM2610BExt::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + void DivPlatformYM2610BExt::reset() { DivPlatformYM2610B::reset(); diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index 024119cff..e7feaf3c2 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -35,6 +35,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 39e341675..063fd5c36 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -757,6 +757,12 @@ DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) { return NULL; } +int DivPlatformYM2610Ext::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + void DivPlatformYM2610Ext::reset() { DivPlatformYM2610::reset(); diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index f860a36cd..666f0d40f 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -35,6 +35,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 2211cf0f9..2ab9ead8e 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-" }; @@ -1370,9 +1370,20 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { pendingNotes.pop_front(); continue; } + if (note.insChange) { + dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,0)); + pendingNotes.pop_front(); + continue; + } 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 (!(midiIsDirect && midiIsDirectProgram && note.fromMIDI)) { + dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); + } + if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { + float curvedVol=pow((float)note.volume/127.0f,midiVolExp); + int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],curvedVol); + 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; @@ -1828,7 +1839,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi case TA_MIDI_NOTE_OFF: { if (chan<0 || chan>=chans) break; if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); + pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } @@ -1843,13 +1854,13 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi if (chan<0 || chan>=chans) break; if (msg.data[1]==0) { if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); + pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } } else { if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true)); + pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true,false,true)); } else { autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]); } @@ -1857,7 +1868,9 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi break; } case TA_MIDI_PROGRAM: { - // TODO: change instrument event thingy + if (midiIsDirect && midiIsDirectProgram) { + pendingNotes.push_back(DivNoteEvent(chan,msg.data[0],0,0,false,true,true)); + } break; } } diff --git a/src/engine/song.h b/src/engine/song.h index 7a7e267c2..5ecccaaaa 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -164,6 +164,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]; @@ -184,6 +185,7 @@ struct DivSubSong { ordersLen(1) { for (int i=0; i ins; std::vector wave; @@ -500,7 +503,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"}, @@ -652,13 +653,13 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_SMS_OPLL]=new DivSysDef( - "Sega Master System + FM Expansion", NULL, 0x43, 0x43, 13, true, true, 0, true, 0, + "Sega Master System + FM Expansion", NULL, 0x43, 0x43, 13, true, true, 0, true, 0, 0, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_GB]=new DivSysDef( - "Game Boy", NULL, 0x04, 0x04, 4, false, true, 0x161, false, 0, + "Game Boy", NULL, 0x04, 0x04, 4, false, true, 0x161, false, 0, 32, 16, "the most popular portable game console of the era.", {"Pulse 1", "Pulse 2", "Wavetable", "Noise"}, {"S1", "S2", "WA", "NO"}, @@ -675,7 +676,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_PCE]=new DivSysDef( - "PC Engine/TurboGrafx-16", NULL, 0x05, 0x05, 6, false, true, 0x161, false, 1U<", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_NES_FDS]=new DivSysDef( - "Famicom Disk System", NULL, 0, 0x86, 6, false, true, 0, true, 0, + "Famicom Disk System", NULL, 0, 0x86, 6, false, true, 0, true, 0, 0, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_C64_6581]=new DivSysDef( - "Commodore 64 (6581)", NULL, 0x47, 0x47, 3, false, true, 0, false, 0, + "Commodore 64 (6581)", NULL, 0x47, 0x47, 3, false, true, 0, false, 0, 0, 0, "this computer is powered by the SID chip, which had synthesizer features like a filter and ADSR.", {"Channel 1", "Channel 2", "Channel 3"}, {"CH1", "CH2", "CH3"}, @@ -738,7 +739,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_C64_8580]=new DivSysDef( - "Commodore 64 (8580)", NULL, 0x07, 0x07, 3, false, true, 0, false, 0, + "Commodore 64 (8580)", NULL, 0x07, 0x07, 3, false, true, 0, false, 0, 0, 0, "this computer is powered by the SID chip, which had synthesizer features like a filter and ADSR.\nthis is the newer revision of the chip.", {"Channel 1", "Channel 2", "Channel 3"}, {"CH1", "CH2", "CH3"}, @@ -750,13 +751,13 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_ARCADE]=new DivSysDef( - "DefleCade", NULL, 0x08, 0x08, 13, true, false, 0, true, 0, + "DefleCade", NULL, 0x08, 0x08, 13, true, false, 0, true, 0, 0, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_YM2610]=new DivSysDef( - "Neo Geo CD", NULL, 0x09, 0x09, 13, true, true, 0x151, false, (1U<", + "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", @@ -175,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/dataList.cpp b/src/gui/dataList.cpp index 62736b549..b45739dcb 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -76,7 +76,7 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) { const char* insType="Bug!"; if (i>=0 && isong.insLen) { DivInstrument* ins=e->song.ins[i]; - insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type][0]; + insType=(ins->type>=DIV_INS_MAX)?"Unknown":insTypes[ins->type][0]; const char** insIcon=NULL; if (ins->type>=DIV_INS_MAX) { @@ -202,6 +202,8 @@ void FurnaceGUI::waveListItem(int i, float* wavePreview, int dir, int asset) { void FurnaceGUI::sampleListItem(int i, int dir, int asset) { bool memWarning=false; + ImGui::PushID(i); + DivSample* sample=e->song.sample[i]; for (int j=0; jsong.systemLen; j++) { DivDispatch* dispatch=e->getDispatch(j); @@ -268,6 +270,7 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) { ImGui::EndPopup(); } if (wantScrollList && curSample==i) ImGui::SetScrollHereY(); + ImGui::PopID(); } void FurnaceGUI::drawInsList(bool asChild) { diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 1279f5447..5bf40a028 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -303,6 +303,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_GROOVES: nextWindow=GUI_WINDOW_GROOVES; break; + case GUI_ACTION_WINDOW_XY_OSC: + nextWindow=GUI_WINDOW_XY_OSC; + break; case GUI_ACTION_COLLAPSE_WINDOW: collapseWindow=true; @@ -399,6 +402,9 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_GROOVES: groovesOpen=false; break; + case GUI_WINDOW_XY_OSC: + xyOscOpen=false; + break; default: break; } @@ -696,12 +702,14 @@ void FurnaceGUI::doAction(int what) { if (e->moveInsUp(curIns)) { curIns--; wantScrollList=true; + MARK_MODIFIED; } break; case GUI_ACTION_INS_LIST_MOVE_DOWN: if (e->moveInsDown(curIns)) { curIns++; wantScrollList=true; + MARK_MODIFIED; } break; case GUI_ACTION_INS_LIST_DELETE: @@ -733,16 +741,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; @@ -776,12 +816,14 @@ void FurnaceGUI::doAction(int what) { if (e->moveWaveUp(curWave)) { curWave--; wantScrollList=true; + MARK_MODIFIED; } break; case GUI_ACTION_WAVE_LIST_MOVE_DOWN: if (e->moveWaveDown(curWave)) { curWave++; wantScrollList=true; + MARK_MODIFIED; } break; case GUI_ACTION_WAVE_LIST_DELETE: @@ -876,6 +918,7 @@ void FurnaceGUI::doAction(int what) { curSample--; wantScrollList=true; updateSampleTex=true; + MARK_MODIFIED; } break; case GUI_ACTION_SAMPLE_LIST_MOVE_DOWN: @@ -883,6 +926,7 @@ void FurnaceGUI::doAction(int what) { curSample++; wantScrollList=true; updateSampleTex=true; + MARK_MODIFIED; } break; case GUI_ACTION_SAMPLE_LIST_DELETE: @@ -1348,6 +1392,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 b7b39c589..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; @@ -591,6 +595,10 @@ void FurnaceGUI::drawMobileControls() { if (ImGui::Button("Compat Flags")) { compatFlagsOpen=!compatFlagsOpen; } + ImGui::SameLine(); + if (ImGui::Button("XYOsc")) { + xyOscOpen=!xyOscOpen; + } ImGui::Separator(); 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 f6bf5eedd..5e2825231 100644 --- a/src/gui/findReplace.cpp +++ b/src/gui/findReplace.cpp @@ -103,6 +103,8 @@ void FurnaceGUI::doFind() { int lastRow=e->curSubSong->patLen-1; if (curQueryRangeY==1) { + finishSelection(); + firstRow=selStart.y; lastRow=selEnd.y; } @@ -922,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; } @@ -951,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; } @@ -982,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; } @@ -1011,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 5c3eac0cc..b323b1160 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\ @@ -1211,7 +1216,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { if (latchVol!=-1) { pat->data[cursor.y][3]=MIN(maxVol,latchVol); } else if (vol!=-1) { - pat->data[cursor.y][3]=(vol*maxVol)/127; + pat->data[cursor.y][3]=e->mapVelocity(cursor.xCoarse,pow((float)vol/127.0f,midiMap.volExp)); } if (latchEffect!=-1) pat->data[cursor.y][4]=latchEffect; if (latchEffectVal!=-1) pat->data[cursor.y][5]=latchEffectVal; @@ -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( @@ -2232,6 +2246,8 @@ int FurnaceGUI::load(String path) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } pushRecentFile(path); + // walk song + e->walkSong(loopOrder,loopRow,loopEnd); // do not auto-play a backup if (path.find(backupPath)!=0) { if (settings.playOnLoad==2 || (settings.playOnLoad==1 && wasPlaying)) { @@ -2519,7 +2535,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; } @@ -2883,7 +2899,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; @@ -2891,7 +2907,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; @@ -2915,7 +2931,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; } @@ -2926,7 +2942,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; @@ -2935,7 +2951,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; } @@ -3441,6 +3457,7 @@ bool FurnaceGUI::loop() { DECLARE_METRIC(readOsc) DECLARE_METRIC(osc) DECLARE_METRIC(chanOsc) + DECLARE_METRIC(xyOsc) DECLARE_METRIC(volMeter) DECLARE_METRIC(settings) DECLARE_METRIC(debug) @@ -3617,17 +3634,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) { @@ -3759,7 +3789,7 @@ bool FurnaceGUI::loop() { noteInput( msg.data[0]-12, 0, - midiMap.volInput?((int)(pow((double)msg.data[1]/127.0,midiMap.volExp)*127.0)):-1 + midiMap.volInput?msg.data[1]:-1 ); } } else { @@ -3786,7 +3816,7 @@ bool FurnaceGUI::loop() { } break; case TA_MIDI_PROGRAM: - if (midiMap.programChange) { + if (midiMap.programChange && !(midiMap.directChannel && midiMap.directProgram)) { curIns=msg.data[0]; if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1; wavePreviewInit=true; @@ -4193,7 +4223,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; } @@ -4245,6 +4275,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( @@ -4436,6 +4476,7 @@ bool FurnaceGUI::loop() { if (ImGui::MenuItem("piano/input pad",BIND_FOR(GUI_ACTION_WINDOW_PIANO),pianoOpen)) pianoOpen=!pianoOpen; if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen; if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen; + if (ImGui::MenuItem("oscilloscope (X-Y)",BIND_FOR(GUI_ACTION_WINDOW_XY_OSC),xyOscOpen)) xyOscOpen=!xyOscOpen; if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen; if (ImGui::MenuItem("clock",BIND_FOR(GUI_ACTION_WINDOW_CLOCK),clockOpen)) clockOpen=!clockOpen; if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen; @@ -4656,6 +4697,7 @@ bool FurnaceGUI::loop() { MEASURE(readOsc,readOsc()); MEASURE(osc,drawOsc()); MEASURE(chanOsc,drawChanOsc()); + MEASURE(xyOsc,drawXYOsc()); MEASURE(grooves,drawGrooves()); MEASURE(regView,drawRegView()); } else { @@ -4685,6 +4727,7 @@ bool FurnaceGUI::loop() { MEASURE(osc,drawOsc()); MEASURE(chanOsc,drawChanOsc()); + MEASURE(xyOsc,drawXYOsc()); MEASURE(volMeter,drawVolMeter()); MEASURE(settings,drawSettings()); MEASURE(debug,drawDebug()); @@ -4816,6 +4859,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; @@ -4907,7 +4951,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) { @@ -5143,8 +5187,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; } } } @@ -5194,7 +5242,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()); @@ -5202,6 +5252,9 @@ bool FurnaceGUI::loop() { showError("cannot load wavetable! ("+e->getLastError()+")"); } } else { + if (settings.selectAssetOnLoad) { + curWave=waveCount-1; + } MARK_MODIFIED; RESET_WAVE_MACRO_ZOOM; } @@ -5274,6 +5327,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); @@ -5390,6 +5464,11 @@ bool FurnaceGUI::loop() { ImGui::OpenPopup("InsTypeList"); } + if (displayWaveSizeList) { + displayWaveSizeList=false; + ImGui::OpenPopup("WaveSizeList"); + } + if (displayExporting) { displayExporting=false; ImGui::OpenPopup("Rendering..."); @@ -5916,6 +5995,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 @@ -6016,7 +6118,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); @@ -6441,6 +6543,7 @@ bool FurnaceGUI::init() { mixerOpen=e->getConfBool("mixerOpen",false); oscOpen=e->getConfBool("oscOpen",true); chanOscOpen=e->getConfBool("chanOscOpen",false); + xyOscOpen=e->getConfBool("xyOscOpen",false); volMeterOpen=e->getConfBool("volMeterOpen",true); statsOpen=e->getConfBool("statsOpen",false); compatFlagsOpen=e->getConfBool("compatFlagsOpen",false); @@ -6536,6 +6639,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); @@ -6549,6 +6653,16 @@ bool FurnaceGUI::init() { chanOscGrad.fromString(e->getConfString("chanOscGrad","")); chanOscGrad.render(); + xyOscXChannel=e->getConfInt("xyOscXChannel",0); + xyOscXInvert=e->getConfBool("xyOscXInvert",false); + xyOscYChannel=e->getConfInt("xyOscYChannel",1); + xyOscYInvert=e->getConfBool("xyOscYInvert",false); + xyOscZoom=e->getConfFloat("xyOscZoom",1.0f); + xyOscSamples=e->getConfInt("xyOscSamples",32768); + xyOscDecayTime=e->getConfFloat("xyOscDecayTime",10.0f); + xyOscIntensity=e->getConfFloat("xyOscIntensity",2.0f); + xyOscThickness=e->getConfFloat("xyOscThickness",2.0f); + syncSettings(); syncTutorial(); @@ -6934,6 +7048,7 @@ bool FurnaceGUI::init() { return -2; } + if (midiMap.directChannel && midiMap.directProgram) return -1; return curIns; }); @@ -6996,6 +7111,7 @@ void FurnaceGUI::commitState() { e->setConf("mixerOpen",mixerOpen); e->setConf("oscOpen",oscOpen); e->setConf("chanOscOpen",chanOscOpen); + e->setConf("xyOscOpen",xyOscOpen); e->setConf("volMeterOpen",volMeterOpen); e->setConf("statsOpen",statsOpen); e->setConf("compatFlagsOpen",compatFlagsOpen); @@ -7078,6 +7194,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); @@ -7090,6 +7207,17 @@ void FurnaceGUI::commitState() { e->setConf("chanOscUseGrad",chanOscUseGrad); e->setConf("chanOscGrad",chanOscGrad.toString()); + // commit x-y osc state + e->setConf("xyOscXChannel",xyOscXChannel); + e->setConf("xyOscXInvert",xyOscXInvert); + e->setConf("xyOscYChannel",xyOscYChannel); + e->setConf("xyOscYInvert",xyOscYInvert); + e->setConf("xyOscZoom",xyOscZoom); + e->setConf("xyOscSamples",xyOscSamples); + e->setConf("xyOscDecayTime",xyOscDecayTime); + e->setConf("xyOscIntensity",xyOscIntensity); + e->setConf("xyOscThickness",xyOscThickness); + // commit recent files for (int i=0; i<30; i++) { String key=fmt::sprintf("recentFile%d",i); @@ -7332,6 +7460,7 @@ FurnaceGUI::FurnaceGUI(): clockOpen(false), speedOpen(true), groovesOpen(false), + xyOscOpen(false), basicMode(true), shortIntro(false), insListDir(false), @@ -7530,6 +7659,8 @@ FurnaceGUI::FurnaceGUI(): sampleFilterRes(0.25f), sampleFilterCutStart(16000.0f), sampleFilterCutEnd(100.0f), + sampleCrossFadeLoopLength(0), + sampleCrossFadeLoopLaw(50), sampleFilterPower(1), sampleClipboard(NULL), sampleClipboardLen(0), @@ -7538,6 +7669,7 @@ FurnaceGUI::FurnaceGUI(): openSampleAmplifyOpt(false), openSampleSilenceOpt(false), openSampleFilterOpt(false), + openSampleCrossFadeOpt(false), selectedPortSet(0x1fff), selectedSubPort(-1), hoveredPortSet(0x1fff), @@ -7566,12 +7698,24 @@ 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), chanOscGrad(64,64), chanOscGradTex(NULL), chanOscWorkPool(NULL), + xyOscPointTex(NULL), + xyOscOptions(false), + xyOscXChannel(0), + xyOscXInvert(false), + xyOscYChannel(1), + xyOscYInvert(false), + xyOscZoom(1.0f), + xyOscSamples(32768), + xyOscDecayTime(10.0f), + xyOscIntensity(2.0f), + xyOscThickness(2.0f), followLog(true), #ifdef IS_MOBILE pianoOctaves(7), @@ -7702,15 +7846,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 56c09b6b8..fbdebcb49 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -419,6 +419,7 @@ enum FurnaceGUIWindows { GUI_WINDOW_FIND, GUI_WINDOW_CLOCK, GUI_WINDOW_GROOVES, + GUI_WINDOW_XY_OSC, GUI_WINDOW_INTRO_MON, GUI_WINDOW_SPOILER }; @@ -464,6 +465,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, @@ -581,6 +583,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_FIND, GUI_ACTION_WINDOW_CLOCK, GUI_ACTION_WINDOW_GROOVES, + GUI_ACTION_WINDOW_XY_OSC, GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_CLOSE_WINDOW, @@ -729,6 +732,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, @@ -941,7 +945,7 @@ struct MIDIMap { int**** map; std::vector binds; - bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse; + bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse, directProgram; // 0: disabled // // 1: C- C# D- D# E- F- F# G- G# A- A# B- @@ -1004,11 +1008,12 @@ struct MIDIMap { midiClock(false), midiTimeCode(false), yamahaFMResponse(false), + directProgram(false), valueInputStyle(1), valueInputControlMSB(0), valueInputControlLSB(0), valueInputControlSingle(0), - volExp(1.0f), + volExp(2.0f), valueInputCurMSB(0), valueInputCurLSB(0), valueInputCurSingle(0) { @@ -1280,6 +1285,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 { }; @@ -1371,11 +1390,12 @@ class FurnaceGUI { std::vector paletteSearchResults; 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, displayPalette, fullScreen, preserveChanPos, wantScrollList, noteInputPoly, notifyWaveChange; @@ -1484,6 +1504,8 @@ class FurnaceGUI { int c64Core; int pokeyCore; int opnCore; + int opl2Core; + int opl3Core; int arcadeCoreRender; int ym2612CoreRender; int snCoreRender; @@ -1492,6 +1514,8 @@ class FurnaceGUI { int c64CoreRender; int pokeyCoreRender; int opnCoreRender; + int opl2CoreRender; + int opl3CoreRender; int pcSpeakerOutMethod; String yrw801Path; String tg100Path; @@ -1640,6 +1664,7 @@ class FurnaceGUI { int fontBitmap; int fontAutoHint; int fontAntiAlias; + int selectAssetOnLoad; unsigned int maxUndoSteps; String mainFontPath; String headFontPath; @@ -1677,6 +1702,8 @@ class FurnaceGUI { c64Core(0), pokeyCore(1), opnCore(1), + opl2Core(0), + opl3Core(0), arcadeCoreRender(1), ym2612CoreRender(0), snCoreRender(0), @@ -1685,6 +1712,8 @@ class FurnaceGUI { c64CoreRender(1), pokeyCoreRender(1), opnCoreRender(1), + opl2CoreRender(0), + opl3CoreRender(0), pcSpeakerOutMethod(0), yrw801Path(""), tg100Path(""), @@ -1830,6 +1859,7 @@ class FurnaceGUI { fontBitmap(0), fontAutoHint(1), fontAntiAlias(1), + selectAssetOnLoad(1), maxUndoSteps(100), mainFontPath(""), headFontPath(""), @@ -1884,7 +1914,7 @@ class FurnaceGUI { bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen; - bool groovesOpen; + bool groovesOpen, xyOscOpen; bool basicMode, shortIntro; bool insListDir, waveListDir, sampleListDir; @@ -2104,10 +2134,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 @@ -2131,7 +2162,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; @@ -2152,7 +2183,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; @@ -2170,6 +2201,7 @@ class FurnaceGUI { relatedCh(0), pitch(0.0f), windowSize(1.0f), + phaseOff(0.0f), needle(0), ready(false), loudEnough(false), @@ -2178,6 +2210,19 @@ class FurnaceGUI { planI(NULL) {} } chanOscChan[DIV_MAX_CHANS]; + // x-y oscilloscope + FurnaceGUITexture* xyOscPointTex; + bool xyOscOptions; + int xyOscXChannel; + bool xyOscXInvert; + int xyOscYChannel; + bool xyOscYInvert; + float xyOscZoom; + int xyOscSamples; + float xyOscDecayTime; + float xyOscIntensity; + float xyOscThickness; + // visualizer float keyHit[DIV_MAX_CHANS]; float keyHit1[DIV_MAX_CHANS]; @@ -2369,6 +2414,7 @@ class FurnaceGUI { void drawSpoiler(); void drawClock(); void drawTutorial(); + void drawXYOsc(); void parseKeybinds(); void promptKey(int which); @@ -2413,6 +2459,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 347ad2fa1..43d57864b 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -538,7 +538,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), @@ -583,7 +587,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), @@ -603,6 +611,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f), D("WINDOW_CLOCK", "Clock", 0), D("WINDOW_GROOVES", "Grooves", 0), + D("WINDOW_XY_OSC", "Oscilloscope (X-Y)", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), @@ -752,6 +761,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 35c7273f5..6a0f204d6 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1858,7 +1858,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; } @@ -2064,7 +2064,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; } @@ -2250,7 +2250,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; \ @@ -2824,7 +2824,11 @@ void FurnaceGUI::drawInsEdit() { updateFMPreview=false; } if (settings.insEditColorize) { - pushAccentColors(uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],ImVec4(0.0f,0.0f,0.0f,0.0f)); + if (ins->type>=DIV_INS_MAX) { + pushAccentColors(uiColors[GUI_COLOR_INSTR_UNKNOWN],uiColors[GUI_COLOR_INSTR_UNKNOWN],uiColors[GUI_COLOR_INSTR_UNKNOWN],ImVec4(0.0f,0.0f,0.0f,0.0f)); + } else { + pushAccentColors(uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],ImVec4(0.0f,0.0f,0.0f,0.0f)); + } } if (ImGui::BeginTable("InsProp",3)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); @@ -2885,14 +2889,8 @@ void FurnaceGUI::drawInsEdit() { ImGui::Text("Type"); ImGui::TableNextColumn(); - if (ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM; int insType=ins->type; ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - /* - if (ImGui::Combo("##Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) { - ins->type=(DivInstrumentType)insType; - } - */ bool warnType=true; for (DivInstrumentType i: e->getPossibleInsTypes()) { if (i==insType) { @@ -2901,7 +2899,7 @@ void FurnaceGUI::drawInsEdit() { } pushWarningColor(warnType,warnType && failedNoteOn); - if (ImGui::BeginCombo("##Type",insTypes[insType][0])) { + if (ImGui::BeginCombo("##Type",(insType>=DIV_INS_MAX)?"Unknown":insTypes[insType][0])) { std::vector insTypeList; if (settings.displayAllInsTypes) { for (int i=0; insTypes[i][0]; i++) { @@ -4799,7 +4797,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; @@ -4818,7 +4816,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; @@ -5159,7 +5157,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; @@ -5354,7 +5352,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; } @@ -5938,23 +5936,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; } } @@ -5969,7 +5967,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTabItem(); } } - if (ImGui::BeginTabItem("Macros")) { + if (ins->typetype==DIV_INS_VRC6) { insTabSample(ins); } + if (ins->type>=DIV_INS_MAX) { + if (ImGui::BeginTabItem("Error")) { + ImGui::Text("invalid instrument type! change it first."); + ImGui::EndTabItem(); + } + } ImGui::EndTabBar(); } if (settings.insEditColorize) { diff --git a/src/gui/intConst.cpp b/src/gui/intConst.cpp index 167fdd7e8..09a988e84 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 082f421a7..153fc868c 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/midiMap.cpp b/src/gui/midiMap.cpp index 602347107..fac0399b8 100644 --- a/src/gui/midiMap.cpp +++ b/src/gui/midiMap.cpp @@ -139,6 +139,7 @@ bool MIDIMap::read(String path) { UNDERSTAND_OPTION(midiClock) else UNDERSTAND_OPTION(midiTimeCode) else UNDERSTAND_OPTION(yamahaFMResponse) else + UNDERSTAND_OPTION(directProgram) else UNDERSTAND_OPTION(valueInputStyle) else UNDERSTAND_OPTION(valueInputControlMSB) else UNDERSTAND_OPTION(valueInputControlLSB) else @@ -205,6 +206,7 @@ bool MIDIMap::write(String path) { WRITE_OPTION(midiClock); WRITE_OPTION(midiTimeCode); WRITE_OPTION(yamahaFMResponse); + WRITE_OPTION(directProgram); WRITE_OPTION(valueInputStyle); WRITE_OPTION(valueInputControlMSB); WRITE_OPTION(valueInputControlLSB); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 215949ebb..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) { @@ -932,7 +939,8 @@ void FurnaceGUI::drawPattern() { ImGuiWindow* win=ImGui::GetCurrentWindow(); ImVec2 posMin=win->DC.CursorPos; ImGui::Dummy(ImVec2(dpiScale,settings.iconSize*dpiScale)); - ImVec2 posMax=ImGui::GetContentRegionMax(); + ImVec2 posMax=ImVec2(win->WorkRect.Max.x,win->WorkRect.Max.y); + posMin.y-=ImGui::GetStyle().ItemSpacing.y*0.5; ImDrawList* dl=ImGui::GetWindowDrawList(); ImVec2 iconPos[6]; DivChannelState* cs=e->getChanState(i); diff --git a/src/gui/plot_nolerp.cpp b/src/gui/plot_nolerp.cpp index eefb4ea1b..f4d03717d 100644 --- a/src/gui/plot_nolerp.cpp +++ b/src/gui/plot_nolerp.cpp @@ -394,6 +394,8 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett window->DrawList->AddLine(ImLerp(inner_bb.Min,inner_bb.Max,ImVec2(0.0f,histogram_zero_line_t)),ImLerp(inner_bb.Min,inner_bb.Max,ImVec2(1.0f,histogram_zero_line_t)),col_base); } + ImVec2 chevron[3]; + for (int n = 0; n < res_w; n++) { const float t1 = t0 + t_step; @@ -421,11 +423,30 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett if (values_highlight!=NULL) { if (values_highlight[v1_idx]) rCol=ImGui::GetColorU32(highlightColor); } - window->DrawList->AddRectFilled(pos0, pos1, rCol); + if (blockMode) { + if ((int)v0>=(int)(scale_max+0.5)) { + float chScale=(pos1.x-pos0.x)*0.125; + chevron[0]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.25,pos1.y+4.0f*chScale); + chevron[1]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.5,pos1.y+2.0f*chScale); + chevron[2]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.75,pos1.y+4.0f*chScale); + window->DrawList->AddPolyline(chevron, 3, rCol, 0, chScale); + } else if ((int)v0<(int)(scale_min)) { + float chScale=(pos1.x-pos0.x)*0.125; + chevron[0]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.25,pos1.y-4.0f*chScale); + chevron[1]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.5,pos1.y-2.0f*chScale); + chevron[2]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.75,pos1.y-4.0f*chScale); + window->DrawList->AddPolyline(chevron, 3, rCol, 0, chScale); + } else { + window->DrawList->AddRectFilled(pos0, pos1, rCol); + } + } else { + window->DrawList->AddRectFilled(pos0, pos1, rCol); + } } t0 = t1; tp0 = tp1; + v0 = v1; } } 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 d018a2f71..51f0bde01 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; @@ -1126,9 +1139,21 @@ void FurnaceGUI::drawSettings() { // TODO //ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume); //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); - if (ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel)) settingsChanged=true; + if (ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel)) { + e->setMidiDirect(midiMap.directChannel); + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); + settingsChanged=true; + } + if (midiMap.directChannel) { + if (ImGui::Checkbox("Program change pass-through",&midiMap.directProgram)) { + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); + settingsChanged=true; + } + } if (ImGui::Checkbox("Map Yamaha FM voice data to instruments",&midiMap.yamahaFMResponse)) settingsChanged=true; - if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; + if (!(midiMap.directChannel && midiMap.directProgram)) { + if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; + } //ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); //ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); if (ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7)) settingsChanged=true; @@ -1185,6 +1210,7 @@ void FurnaceGUI::drawSettings() { if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) { if (midiMap.volExp<0.01) midiMap.volExp=0.01; if (midiMap.volExp>8.0) midiMap.volExp=8.0; + e->setMidiVolExp(midiMap.volExp); settingsChanged=true; } rightClickable float curve[128]; @@ -1487,10 +1513,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 +1583,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(); @@ -1750,7 +1799,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; @@ -2330,6 +2379,21 @@ void FurnaceGUI::drawSettings() { } ImGui::Unindent(); + // SUBSECTION ASSETS + CONFIG_SUBSECTION("Assets"); + + bool insTypeMenuB=settings.insTypeMenu; + if (ImGui::Checkbox("Display instrument type menu when adding instrument",&insTypeMenuB)) { + settings.insTypeMenu=insTypeMenuB; + 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") { @@ -2352,7 +2416,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; @@ -2388,7 +2452,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; @@ -2407,7 +2471,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; @@ -2426,7 +2490,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; @@ -2603,8 +2667,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)) { @@ -2893,12 +2957,6 @@ void FurnaceGUI::drawSettings() { settingsChanged=true; } - bool insTypeMenuB=settings.insTypeMenu; - if (ImGui::Checkbox("Display instrument type menu when adding instrument",&insTypeMenuB)) { - settings.insTypeMenu=insTypeMenuB; - settingsChanged=true; - } - // SUBSECTION MACRO EDITOR CONFIG_SUBSECTION("Macro Editor"); ImGui::Text("Macro editor layout:"); @@ -3596,6 +3654,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); @@ -3604,6 +3664,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",""); @@ -3761,6 +3823,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); @@ -3773,21 +3836,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); @@ -3922,6 +3989,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; @@ -3975,6 +4043,8 @@ void FurnaceGUI::syncSettings() { midiMap.compile(); e->setMidiDirect(midiMap.directChannel); + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); + e->setMidiVolExp(midiMap.volExp); e->setMetronomeVol(((float)settings.metroVol)/100.0f); e->setSamplePreviewVol(((float)settings.sampleVol)/100.0f); } @@ -3993,6 +4063,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) || @@ -4001,6 +4073,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) ); @@ -4028,6 +4102,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); @@ -4036,6 +4112,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); @@ -4194,6 +4272,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 b286550ee..99baf47e4 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: @@ -2285,7 +2306,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 new file mode 100644 index 000000000..28136d645 --- /dev/null +++ b/src/gui/xyOsc.cpp @@ -0,0 +1,232 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * 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. + */ + +#include "gui.h" +#include "imgui_internal.h" +#include +#include "../ta-log.h" +#include "../engine/filter.h" + +void FurnaceGUI::drawXYOsc() { + if (nextWindow==GUI_WINDOW_XY_OSC) { + xyOscOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!xyOscOpen) return; + 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)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); + } + if (ImGui::Begin("Oscilloscope (X-Y)",&xyOscOpen,globalWinFlags)) { + if (xyOscOptions) { + int xyOscXChannelP1 = xyOscXChannel+1; + int xyOscYChannelP1 = xyOscYChannel+1; + + ImGui::Text("X Channel"); + ImGui::SameLine(); + if (ImGui::DragInt("##XChannel",&xyOscXChannelP1,1.0f,1,DIV_MAX_OUTPUTS)) { + xyOscXChannel=MIN(MAX(xyOscXChannelP1,1),DIV_MAX_OUTPUTS)-1; + } rightClickable + ImGui::SameLine(); + ImGui::Checkbox("Invert##X",&xyOscXInvert); + ImGui::Text("Y Channel"); + ImGui::SameLine(); + if (ImGui::DragInt("##YChannel",&xyOscYChannelP1,1.0f,1,DIV_MAX_OUTPUTS)) { + xyOscXChannel=MIN(MAX(xyOscYChannelP1,1),DIV_MAX_OUTPUTS)-1; + } rightClickable + ImGui::SameLine(); + ImGui::Checkbox("Invert##Y",&xyOscYInvert); + if (ImGui::SliderFloat("Zoom",&xyOscZoom,0.5f,4.0f,"%.2fx")) { + xyOscZoom=MAX(xyOscZoom,0.0f); + } rightClickable + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("%.1fdB",20.0f*log10f(xyOscZoom)); + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Middle)) { + xyOscZoom=1.0f; + } + if (ImGui::SliderInt("Samples",&xyOscSamples,2,32768)) { + xyOscSamples=MIN(MAX(xyOscSamples,2),32768); + } rightClickable + if (ImGui::SliderFloat("Decay Time (ms)",&xyOscDecayTime,1.0f,1000.0f,"%.1f",ImGuiSliderFlags_Logarithmic)) { + xyOscDecayTime=MAX(xyOscDecayTime,0.0f); + } rightClickable + if (ImGui::SliderFloat("Intensity",&xyOscIntensity,0.0f,5.0f,"%.2f")) { + xyOscIntensity=MAX(xyOscIntensity,0.0f); + } rightClickable + if (ImGui::SliderFloat("Line Thickness",&xyOscThickness,0.0f,10.0f,"%.2f")) { + xyOscThickness=MAX(xyOscThickness,0.0f); + } rightClickable + if (ImGui::Button("OK")) { + xyOscOptions=false; + } + } else { + ImDrawList* dl=ImGui::GetWindowDrawList(); + ImGuiWindow* window=ImGui::GetCurrentWindow(); + ImVec2 size=ImGui::GetContentRegionAvail(); + + ImVec2 minArea=window->DC.CursorPos; + ImVec2 maxArea=ImVec2( + minArea.x+size.x, + minArea.y+size.y + ); + ImRect rect=ImRect(minArea,maxArea); + ImRect inRect=rect; + inRect.Min.x+=dpiScale; + inRect.Min.y+=dpiScale; + inRect.Max.x-=dpiScale; + inRect.Max.y-=dpiScale; + ImVec2 inSqrCenter=rect.GetCenter(); + float inSqrLength; + ImRect inSqr=inRect; + if (rect.GetWidth() > rect.GetHeight()) { + inSqrLength=inSqr.GetHeight()/2; + inSqr.Min.x=inSqrCenter.x-inSqrLength; + inSqr.Max.x=inSqrCenter.x+inSqrLength; + } else { + inSqrLength=inSqr.GetWidth()/2; + inSqr.Min.y=inSqrCenter.y-inSqrLength; + inSqr.Max.y=inSqrCenter.y+inSqrLength; + } + float scaleX=xyOscZoom*inSqrLength*(xyOscXInvert?-1:1); + float scaleY=xyOscZoom*inSqrLength*(xyOscYInvert?1:-1); + const ImGuiStyle& style=ImGui::GetStyle(); + ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE]); + color&=~IM_COL32_A_MASK; + ImU32 borderColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BORDER]); + ImU32 refColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_REF]); + ImU32 guideColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_GUIDE]); + ImGui::ItemSize(size,style.FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID("wsDisplay"))) { + // background + dl->AddRectFilledMultiColor( + inRect.Min, + inRect.Max, + ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG1]), + ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG2]), + ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG4]), + ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG3]), + settings.oscRoundedCorners?(8.0f*dpiScale):0.0f + ); + + // axis guides + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.0f,0.5f)), + ImLerp(rect.Min,rect.Max,ImVec2(1.0f,0.5f)), + refColor, + dpiScale + ); + + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.5f,0.0f)), + ImLerp(rect.Min,rect.Max,ImVec2(0.5f,1.0f)), + refColor, + dpiScale + ); + + bool reflect=xyOscXInvert!=xyOscYInvert; + dl->AddLine( + ImLerp(inSqr.Min,inSqr.Max,ImVec2(0.0f,reflect?0.0f:1.0f)), + ImLerp(inSqr.Min,inSqr.Max,ImVec2(1.0f,reflect?1.0f:0.0f)), + refColor, + dpiScale + ); + + for (int i=1; i<5; i++) { + float ip = (float)i/8.f; + dl->AddRect( + ImLerp(inSqr.Min,inSqr.Max,ImVec2(0.5f-ip,0.5f-ip)), + ImLerp(inSqr.Min,inSqr.Max,ImVec2(0.5f+ip,0.5f+ip)), + guideColor, + 0.0f,0,dpiScale + ); + } + + // line + const float* oscBufX=e->oscBuf[xyOscXChannel]; + const float* oscBufY=e->oscBuf[xyOscYChannel]; + if (oscBufX!=NULL && oscBufY!=NULL) { + int pos=e->oscWritePos; + float lx=inSqrCenter.x; + float ly=inSqrCenter.y; + float maxA=xyOscIntensity*256.f; + float decay=exp2f(-1e3f/e->getAudioDescGot().rate/xyOscDecayTime); + ImDrawListFlags prevFlags=dl->Flags; + dl->Flags|=ImDrawFlags_RoundCornersNone; + if (!settings.oscAntiAlias || safeMode) { + dl->Flags&=~(ImDrawListFlags_AntiAliasedLines|ImDrawListFlags_AntiAliasedLinesUseTex); + } + if (settings.oscEscapesBoundary) { + dl->PushClipRectFullScreen(); + } + for (int i=0; i=1) { + a=MIN(a,255); + dl->AddLine(ImVec2(lx,ly),ImVec2(x,y),(color|((ImU32)a<PopClipRect(); + } + dl->Flags=prevFlags; + } + if (settings.oscBorder) { + dl->AddRect(inRect.Min,inRect.Max,borderColor,settings.oscRoundedCorners?(8.0f*dpiScale):0.0f,0,1.5f*dpiScale); + } + } + if (ImGui::IsItemHovered()) { + float valX=20.0f*log10f(fabsf((ImGui::GetMousePos().x-inSqrCenter.x)/scaleX)); + float valY=20.0f*log10f(fabsf((ImGui::GetMousePos().y-inSqrCenter.y)/scaleY)); + if (valX<=-INFINITY && valY<=-INFINITY) { + ImGui::SetTooltip("(-Infinity)dB,(-Infinity)dB"); + } else if (valX<=-INFINITY) { + ImGui::SetTooltip("(-Infinity)dB,%.1fdB",valY); + } else if (valY<=-INFINITY) { + ImGui::SetTooltip("%.1fdB,(-Infinity)dB",valY); + } else { + ImGui::SetTooltip("%.1fdB,%.1fdB",valX,valY); + } + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + xyOscOptions=true; + } + } + } + if (noPadding) { + ImGui::PopStyleVar(3); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_XY_OSC; + ImGui::End(); +} 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 25bb1804d..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"); @@ -370,7 +373,7 @@ void initParams() { params.push_back(TAParam("C","cmdout",true,pCmdOut,"","output command stream")); params.push_back(TAParam("b","binary",false,pBinary,"","set command stream output format to binary")); params.push_back(TAParam("L","loglevel",true,pLogLevel,"debug|info|warning|error","set the log level (info by default)")); - params.push_back(TAParam("v","view",true,pView,"pattern|commands|nothing","set visualization (pattern by default)")); + params.push_back(TAParam("v","view",true,pView,"pattern|commands|nothing","set visualization (nothing by default)")); params.push_back(TAParam("i","info",false,pInfo,"","get info about a song")); params.push_back(TAParam("c","console",false,pConsole,"","enable console mode")); @@ -554,7 +557,7 @@ int main(int argc, char** argv) { if (safeMode && (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) { logE("you can't use safe mode and console/export mode together."); - return 0; + return 1; } if (safeMode && !safeModeWithAudio) {