Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

* 'master' of https://github.com/tildearrow/furnace: (70 commits)
  whoops
  GUI: AY8930 credits
  GUI: fix inability to close subsongs
  BANK
  OPN: wire up ExtCh system
  fix build failure
  dev95 - multiple songs in a single file (READ)
  DO NOT USE - THIS FAILS - WORK IN PROGRESS
  enforce asset limits
  old .dmf loading improvements
  add AICA and YMZ ADPCM formats
  allocate ID for YMZ280B
  harden .fur file saver
  Fix AY VGM output, Fix presets
  preparations for UI improvements
  GUI: add more presets
  prepare for ExtCh OPN/OPNA
  GUI: clarify that lock layout doesn't work yet
  GUI: remember last state of order edit mode
  GUI: store edit/followOrders/followPattern state
  ...

# Conflicts:
#	src/engine/fileOps.cpp
#	src/engine/platform/ym2610.cpp
#	src/engine/platform/ym2610b.cpp
#	src/engine/sample.cpp
#	src/engine/sample.h
#	src/engine/sysDef.cpp
#	src/gui/doAction.cpp
#	src/gui/sysConf.cpp
This commit is contained in:
cam900 2022-05-18 03:09:55 +09:00
commit 028adf2c8e
84 changed files with 7825 additions and 1146 deletions

1
.gitignore vendored
View file

@ -17,3 +17,4 @@ android/app/build/
android/app/.cxx/ android/app/.cxx/
.vs/ .vs/
CMakeSettings.json CMakeSettings.json
CMakePresets.json

View file

@ -45,7 +45,13 @@ option(SYSTEM_SDL2 "Use a system-installed version of SDL2 instead of the vendor
option(WARNINGS_ARE_ERRORS "Whether warnings in furnace's C++ code should be treated as errors" OFF) option(WARNINGS_ARE_ERRORS "Whether warnings in furnace's C++ code should be treated as errors" OFF)
set(DEPENDENCIES_INCLUDE_DIRS "") set(DEPENDENCIES_INCLUDE_DIRS "")
if (ANDROID)
set(DEPENDENCIES_DEFINES "IS_MOBILE")
else()
set(DEPENDENCIES_DEFINES "") set(DEPENDENCIES_DEFINES "")
endif()
set(DEPENDENCIES_COMPILE_OPTIONS "") set(DEPENDENCIES_COMPILE_OPTIONS "")
set(DEPENDENCIES_LIBRARIES "") set(DEPENDENCIES_LIBRARIES "")
set(DEPENDENCIES_LIBRARY_DIRS "") set(DEPENDENCIES_LIBRARY_DIRS "")
@ -319,6 +325,8 @@ src/engine/platform/sound/es550x/es5506.cpp
src/engine/platform/sound/scc/scc.cpp src/engine/platform/sound/scc/scc.cpp
src/engine/platform/oplAInterface.cpp
src/engine/platform/ym2608Interface.cpp
src/engine/platform/ym2610Interface.cpp src/engine/platform/ym2610Interface.cpp
src/engine/blip_buf.c src/engine/blip_buf.c
@ -352,6 +360,9 @@ src/engine/platform/nes.cpp
src/engine/platform/c64.cpp src/engine/platform/c64.cpp
src/engine/platform/arcade.cpp src/engine/platform/arcade.cpp
src/engine/platform/tx81z.cpp src/engine/platform/tx81z.cpp
src/engine/platform/ym2203.cpp
src/engine/platform/ym2203ext.cpp
src/engine/platform/ym2608.cpp
src/engine/platform/ym2610.cpp src/engine/platform/ym2610.cpp
src/engine/platform/ym2610ext.cpp src/engine/platform/ym2610ext.cpp
src/engine/platform/ym2610b.cpp src/engine/platform/ym2610b.cpp
@ -371,6 +382,7 @@ src/engine/platform/lynx.cpp
src/engine/platform/su.cpp src/engine/platform/su.cpp
src/engine/platform/swan.cpp src/engine/platform/swan.cpp
src/engine/platform/vera.cpp src/engine/platform/vera.cpp
src/engine/platform/zxbeeper.cpp
src/engine/platform/bubsyswsg.cpp src/engine/platform/bubsyswsg.cpp
src/engine/platform/n163.cpp src/engine/platform/n163.cpp
src/engine/platform/pet.cpp src/engine/platform/pet.cpp
@ -441,6 +453,7 @@ src/gui/settings.cpp
src/gui/songInfo.cpp src/gui/songInfo.cpp
src/gui/songNotes.cpp src/gui/songNotes.cpp
src/gui/stats.cpp src/gui/stats.cpp
src/gui/subSongs.cpp
src/gui/sysConf.cpp src/gui/sysConf.cpp
src/gui/sysEx.cpp src/gui/sysEx.cpp
src/gui/util.cpp src/gui/util.cpp

View file

@ -11,11 +11,7 @@
- input pad - input pad
- settings - settings
- RF5C68 system - RF5C68 system
- OPN system - ZX beeper system overlay percussion
- OPNA system
- ZX beeper system
- Y8950 system
- maybe YMU759 ADPCM channel
- ADPCM chips - ADPCM chips
- Game Boy envelope macro/sequence - Game Boy envelope macro/sequence
- rewrite the system name detection function anyway - rewrite the system name detection function anyway
@ -26,8 +22,6 @@
- try to find out why does VSlider not accept keyboard input - try to find out why does VSlider not accept keyboard input
- finish lock layout - finish lock layout
- if macros have release, note off should release them - if macros have release, note off should release them
- add ability to select entire row when clicking on row number
- store edit/followOrders/followPattern state in config
- add ability to select a column by double clicking - add ability to select a column by double clicking
- add ability to move selection by dragging - add ability to move selection by dragging
- Apply button in settings - Apply button in settings

View file

@ -17,6 +17,7 @@ this is a list of systems that Furnace supports, including each system's effects
- [Yamaha YM2612 standalone](ym2612.md) - [Yamaha YM2612 standalone](ym2612.md)
- [Yamaha YM2151 standalone](ym2151.md) - [Yamaha YM2151 standalone](ym2151.md)
- [SegaPCM](segapcm.md) - [SegaPCM](segapcm.md)
- [Capcom QSound](qsound.md)
- [Atari 2600](tia.md) - [Atari 2600](tia.md)
- [Philips SAA1099](saa1099.md) - [Philips SAA1099](saa1099.md)
- [Microchip AY8930](ay8930.md) - [Microchip AY8930](ay8930.md)
@ -28,6 +29,7 @@ this is a list of systems that Furnace supports, including each system's effects
- [Yamaha OPL](opl.md) - [Yamaha OPL](opl.md)
- [PC Speaker](pcspkr.md) - [PC Speaker](pcspkr.md)
- [Commodore PET](pet.md) - [Commodore PET](pet.md)
- [Konami SCC](scc.md)
- [Commodore VIC-20](vic20.md) - [Commodore VIC-20](vic20.md)
- [Konami VRC6](vrc6.md) - [Konami VRC6](vrc6.md)
- [Famicom Disk System](fds.md) - [Famicom Disk System](fds.md)

View file

@ -1,14 +1,12 @@
# Bubble System WSG # Bubble System WSG
a Konami's 2 channel wavetable sound generator logic used at their arcade hardware Bubble System. a Konami-made 2 channel wavetable sound generator logic used on the Bubble System arcade board, configured with K005289, a 4-bit PROM and DAC.
It's configured with K005289, 4 bit PROM and DAC. however, the K005289 is just part of the logic used for pitch and wavetable ROM address.
waveform select and volume control are tied with single AY-3-8910 IO for both channels.
another AY-3-8910 IO is used for reading sound hardware status.
Also known as K005289, but that's just part of the logic used for pitch and wavetable ROM address. Furnace emulates this configuration as single system with 32x16 wavetables.
Waveform select and Volume control are tied with single AY-3-8910 IO for both channels.
Another AY-3-8910 IO is used for reading sound hardware status.
Furnace emulates this configurations as single system, waveform format is 15 level and 32 width.
# effects # effects

View file

@ -0,0 +1,11 @@
# Konami SCC/SCC+
the Sound Creative Chip (SCC) adds 5 channels of wavetable to your MSX!
it was used in (of course) several Konami games, which had better audio quality due to the extra channels provided by this chip (poor AY since nobody used the envelope for bass).
the only problem? the waveform of the fourth channel is shared with the fifth one due to not enough memory in the chip!
the SCC+ fixes this issue though (while being compatible with SCC games).
# effects
- `10xx`: change wave.

View file

@ -4,7 +4,7 @@
yep, that's right! 16 channels of PCM! yep, that's right! 16 channels of PCM!
a chip used in the Sega OutRun/X/Y arcade boards. eventually the MultiPCM surpassed it with 24 channels, and later they joined the software mixing gang. a chip used in the Sega OutRun/X/Y arcade boards. eventually the MultiPCM surpassed it with 28 channels, and later they joined the software mixing gang.
# effects # effects
- `20xx`: set PCM frequency. - `20xx`: set PCM frequency.

View file

@ -0,0 +1,101 @@
# Yamaha YM2203 (OPN)
a cost-reduced version of the YM2151 (OPM).
it only has 3 FM channels instead of 8 and removes stereo, the LFO and DT2 (coarse detune).
however it does contain an AY/SSG part which provides 3 channels of square wave with noise and envelope.
this chip was used in the NEC PC-88/PC-98 series of computers, the Fujitsu FM-7AV and in some arcade boards.
several variants of this chip were released as well, with more features.
# effects
- `11xx`: set feedback of channel.
- `12xx`: set operator 1 level.
- `13xx`: set operator 2 level.
- `14xx`: set operator 3 level.
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- `y` is the mutliplier.
- `18xx`: toggle extended channel 3 mode.
- 0 disables it and 1 enables it.
- only in extended channel 3 system.
- `19xx`: set attack of all operators.
- `1Axx`: set attack of operator 1.
- `1Bxx`: set attack of operator 2.
- `1Cxx`: set attack of operator 3.
- `1Dxx`: set attack of operator 4.
- `20xx`: set SSG channel mode. `xx` may be one of the following:
- `00`: square
- `01`: noise
- `02`: square and noise
- `03`: nothing (apparently)
- `04`: envelope and square
- `05`: envelope and noise
- `06`: envelope and square and noise
- `07`: nothing
- `21xx`: set noise frequency. `xx` is a value between 00 and 1F.
- `22xy`: set envelope mode.
- `x` sets the envelope shape, which may be one of the following:
- `0: \___` decay
- `4: /___` attack once
- `8: \\\\` saw
- `9: \___` decay
- `A: \/\/` inverse obelisco
- `B: \¯¯¯` decay once
- `C: ////` inverse saw
- `D: /¯¯¯` attack
- `E: /\/\` obelisco
- `F: /___` attack once
- if `y` is 1 then the envelope will affect this channel.
- `23xx`: set envelope period low byte.
- `24xx`: set envelope period high byte.
- `25xx`: slide envelope period up.
- `26xx`: slide envelope period down.
- `29xy`: enable SSG auto-envelope mode.
- in this mode the envelope period is set to the channel's notes, multiplied by a fraction.
- `x` is the numerator.
- `y` is the denominator.
- if `x` or `y` are 0 this will disable auto-envelope mode.
- `30xx`: enable envelope hard reset.
- this works by inserting a quick release and tiny delay before a new note.
- `50xy`: set AM of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` determines whether AM is on.
- `51xy` set SL of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value.
- `52xy` set RR of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value.
- `53xy` set DT of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value:
- 0: +0
- 1: +1
- 2: +2
- 3: +3
- 4: -0
- 5: -3
- 6: -2
- 7: -1
- `54xy` set RS of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value.
- `55xy` set SSG-EG of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value (0-8).
- values between 0 and 7 set SSG-EG.
- value 8 disables it.
- `56xx`: set DR of all operators.
- `57xx`: set DR of operator 1.
- `58xx`: set DR of operator 2.
- `59xx`: set DR of operator 3.
- `5Axx`: set DR of operator 4.
- `5Bxx`: set D2R/SR of all operators.
- `5Cxx`: set D2R/SR of operator 1.
- `5Dxx`: set D2R/SR of operator 2.
- `5Exx`: set D2R/SR of operator 3.
- `5Fxx`: set D2R/SR of operator 4.

View file

@ -0,0 +1,101 @@
# Yamaha YM2608 (OPNA)
like YM2203, but with twice the FM channels, stereo, an ADPCM channel and built-in drums ("rhythm")!
it was one of the available sound chips for the NEC PC-88VA and PC-98 series of computers.
the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built-in drums have been replaced with 6 sample channels.
# effects
- `10xy`: set LFO parameters.
- `x` toggles the LFO.
- `y` sets its speed.
- `11xx`: set feedback of channel.
- `12xx`: set operator 1 level.
- `13xx`: set operator 2 level.
- `14xx`: set operator 3 level.
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- `y` is the mutliplier.
- `18xx`: toggle extended channel 3 mode.
- 0 disables it and 1 enables it.
- only in extended channel 3 system.
- `19xx`: set attack of all operators.
- `1Axx`: set attack of operator 1.
- `1Bxx`: set attack of operator 2.
- `1Cxx`: set attack of operator 3.
- `1Dxx`: set attack of operator 4.
- `20xx`: set SSG channel mode. `xx` may be one of the following:
- `00`: square
- `01`: noise
- `02`: square and noise
- `03`: nothing (apparently)
- `04`: envelope and square
- `05`: envelope and noise
- `06`: envelope and square and noise
- `07`: nothing
- `21xx`: set noise frequency. `xx` is a value between 00 and 1F.
- `22xy`: set envelope mode.
- `x` sets the envelope shape, which may be one of the following:
- `0: \___` decay
- `4: /___` attack once
- `8: \\\\` saw
- `9: \___` decay
- `A: \/\/` inverse obelisco
- `B: \¯¯¯` decay once
- `C: ////` inverse saw
- `D: /¯¯¯` attack
- `E: /\/\` obelisco
- `F: /___` attack once
- if `y` is 1 then the envelope will affect this channel.
- `23xx`: set envelope period low byte.
- `24xx`: set envelope period high byte.
- `25xx`: slide envelope period up.
- `26xx`: slide envelope period down.
- `29xy`: enable SSG auto-envelope mode.
- in this mode the envelope period is set to the channel's notes, multiplied by a fraction.
- `x` is the numerator.
- `y` is the denominator.
- if `x` or `y` are 0 this will disable auto-envelope mode.
- `30xx`: enable envelope hard reset.
- this works by inserting a quick release and tiny delay before a new note.
- `50xy`: set AM of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` determines whether AM is on.
- `51xy` set SL of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value.
- `52xy` set RR of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value.
- `53xy` set DT of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value:
- 0: +0
- 1: +1
- 2: +2
- 3: +3
- 4: -0
- 5: -3
- 6: -2
- 7: -1
- `54xy` set RS of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value.
- `55xy` set SSG-EG of operator.
- `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value (0-8).
- values between 0 and 7 set SSG-EG.
- value 8 disables it.
- `56xx`: set DR of all operators.
- `57xx`: set DR of operator 1.
- `58xx`: set DR of operator 2.
- `59xx`: set DR of operator 3.
- `5Axx`: set DR of operator 4.
- `5Bxx`: set D2R/SR of all operators.
- `5Cxx`: set D2R/SR of operator 1.
- `5Dxx`: set D2R/SR of operator 2.
- `5Exx`: set D2R/SR of operator 3.
- `5Fxx`: set D2R/SR of operator 4.

View file

@ -16,9 +16,9 @@ it is backward compatible with the original chip.
- `16xy`: set multiplier of operator. - `16xy`: set multiplier of operator.
- `x` is the operator (1-4). - `x` is the operator (1-4).
- `y` is the mutliplier. - `y` is the mutliplier.
- `18xx`: toggle extended channel 2 mode. - `18xx`: toggle extended channel 3 mode.
- 0 disables it and 1 enables it. - 0 disables it and 1 enables it.
- only in extended channel 2 system. - only in extended channel 3 system.
- `19xx`: set attack of all operators. - `19xx`: set attack of all operators.
- `1Axx`: set attack of operator 1. - `1Axx`: set attack of operator 1.
- `1Bxx`: set attack of operator 2. - `1Bxx`: set attack of operator 2.

View file

@ -29,6 +29,11 @@ furthermore, an `or reserved` indicates this field is always present, but is res
the format versions are: the format versions are:
- 95: Furnace dev95
- 94: Furnace dev94
- 93: Furnace dev93
- 92: Furnace dev92
- 91: Furnace dev91
- 90: Furnace dev90 - 90: Furnace dev90
- 89: Furnace dev89 - 89: Furnace dev89
- 88: Furnace dev88 - 88: Furnace dev88
@ -122,26 +127,26 @@ size | description
-----|------------------------------------ -----|------------------------------------
4 | "INFO" block ID 4 | "INFO" block ID
4 | reserved 4 | reserved
1 | time base 1 | time base (of first song)
1 | speed 1 1 | speed 1 (of first song)
1 | speed 2 1 | speed 2 (of first song)
1 | initial arpeggio time 1 | initial arpeggio time (of first song)
4f | ticks per second 4f | ticks per second (of first song)
| - 60 is NTSC | - 60 is NTSC
| - 50 is PAL | - 50 is PAL
2 | pattern length 2 | pattern length (of first song)
| - the limit is 256. | - the limit is 256.
2 | orders length 2 | orders length (of first song)
| - the limit is 256 (>=80) or 127 (<80). | - the limit is 256 (>=80) or 127 (<80).
1 | highlight A 1 | highlight A (of first song)
1 | highlight B 1 | highlight B (of first song)
2 | instrument count 2 | instrument count
| - the limit is 256. | - the limit is 256.
2 | wavetable count 2 | wavetable count
| - the limit is 256. | - the limit is 256.
2 | sample count 2 | sample count
| - the limit is 256. | - the limit is 256.
4 | pattern count 4 | pattern count (global)
32 | list of sound chips 32 | list of sound chips
| - possible soundchips: | - possible soundchips:
| - 0x00: end of list | - 0x00: end of list
@ -215,6 +220,7 @@ size | description
| - 0xb5: tildearrow Sound Unit - 8 channels | - 0xb5: tildearrow Sound Unit - 8 channels
| - 0xb6: OPN extended - 9 channels | - 0xb6: OPN extended - 9 channels
| - 0xb7: PC-98 extended - 19 channels | - 0xb7: PC-98 extended - 19 channels
| - 0xb8: YMZ280B - 8 channels
| - 0xde: YM2610B extended - 19 channels | - 0xde: YM2610B extended - 19 channels
| - 0xe0: QSound - 19 channels | - 0xe0: QSound - 19 channels
| - 0xfd: Dummy System - 8 channels | - 0xfd: Dummy System - 8 channels
@ -257,20 +263,20 @@ size | description
4?? | pointers to wavetables 4?? | pointers to wavetables
4?? | pointers to samples 4?? | pointers to samples
4?? | pointers to patterns 4?? | pointers to patterns
??? | orders ??? | orders (of first song)
| - a table of bytes | - a table of bytes
| - size=channels*ordLen | - size=channels*ordLen
| - read orders then channels | - read orders then channels
| - the maximum value of a cell is FF (>=80) or 7F (<80). | - the maximum value of a cell is FF (>=80) or 7F (<80).
??? | effect columns ??? | effect columns (of first song)
| - size=channels | - size=channels
1?? | channel hide status 1?? | channel hide status (of first song)
| - size=channels | - size=channels
1?? | channel collapse status 1?? | channel collapse status (of first song)
| - size=channels | - size=channels
S?? | channel names S?? | channel names (of first song)
| - a list of channelCount C strings | - a list of channelCount C strings
S?? | channel short names S?? | channel short names (of first song)
| - same as above | - same as above
STR | song comment STR | song comment
4f | master volume, 1.0f=100% (>=59) 4f | master volume, 1.0f=100% (>=59)
@ -291,6 +297,55 @@ size | description
1 | pitch macro is linear (>=90) or reserved 1 | pitch macro is linear (>=90) or reserved
1 | pitch slide speed in full linear pitch mode (>=94) or reserved 1 | pitch slide speed in full linear pitch mode (>=94) or reserved
18 | reserved 18 | reserved
--- | **additional subsongs** (>=95)
STR | first subsong name
STR | first subsong comment
1 | number of additional subsongs
3 | reserved
4?? | pointers to subsong data
```
# subsong
from version 95 onwards, Furnace supports storing multiple songs on a single file.
the way it's currently done is really weird, but it provides for some backwards compatibility (previous versions will only load the first subsong which is already defined in the `INFO` block).
```
size | description
-----|------------------------------------
4 | "SONG" block ID
4 | reserved
1 | time base
1 | speed 1
1 | speed 2
1 | initial arpeggio time
4f | ticks per second
| - 60 is NTSC
| - 50 is PAL
2 | pattern length
| - the limit is 256.
2 | orders length
| - the limit is 256.
1 | highlight A
1 | highlight B
4 | reserved
STR | subsong name
STR | subsong comment
??? | orders
| - a table of bytes
| - size=channels*ordLen
| - read orders then channels
| - the maximum value of a cell is FF.
??? | effect columns
| - size=channels
1?? | channel hide status
| - size=channels
1?? | channel collapse status
| - size=channels
S?? | channel names
| - a list of channelCount C strings
S?? | channel short names
| - same as above
``` ```
# instrument # instrument
@ -743,6 +798,8 @@ size | description
1 | depth 1 | depth
| - 0: ZX Spectrum overlay drum (1-bit) | - 0: ZX Spectrum overlay drum (1-bit)
| - 1: 1-bit NES DPCM (1-bit) | - 1: 1-bit NES DPCM (1-bit)
| - 2: AICA ADPCM
| - 3: YMZ ADPCM
| - 4: QSound ADPCM | - 4: QSound ADPCM
| - 5: ADPCM-A | - 5: ADPCM-A
| - 6: ADPCM-B | - 6: ADPCM-B
@ -769,7 +826,8 @@ size | description
4 | reserved 4 | reserved
2 | channel 2 | channel
2 | pattern index 2 | pattern index
4 | reserved 2 | subsong (>=95) or reserved
2 | reserved
??? | pattern data ??? | pattern data
| - size: rows*(4+effectColumns*2)*2 | - size: rows*(4+effectColumns*2)*2
| - read shorts in this order: | - read shorts in this order:

View file

@ -179,6 +179,7 @@ enum DivDispatchCmds {
DIV_CMD_ES5506_ENVELOPE_RVRAMP, // (ramp) DIV_CMD_ES5506_ENVELOPE_RVRAMP, // (ramp)
DIV_CMD_ES5506_ENVELOPE_K1RAMP, // (ramp, slowdown) DIV_CMD_ES5506_ENVELOPE_K1RAMP, // (ramp, slowdown)
DIV_CMD_ES5506_ENVELOPE_K2RAMP, // (ramp, slowdown) DIV_CMD_ES5506_ENVELOPE_K2RAMP, // (ramp, slowdown)
DIV_CMD_ES5506_PAUSE, // (value)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol

View file

@ -29,6 +29,9 @@
#include "platform/c64.h" #include "platform/c64.h"
#include "platform/arcade.h" #include "platform/arcade.h"
#include "platform/tx81z.h" #include "platform/tx81z.h"
#include "platform/ym2203.h"
#include "platform/ym2203ext.h"
#include "platform/ym2608.h"
#include "platform/ym2610.h" #include "platform/ym2610.h"
#include "platform/ym2610ext.h" #include "platform/ym2610ext.h"
#include "platform/ym2610b.h" #include "platform/ym2610b.h"
@ -58,6 +61,7 @@
#include "platform/scc.h" #include "platform/scc.h"
#include "platform/dummy.h" #include "platform/dummy.h"
#include "../ta-log.h" #include "../ta-log.h"
#include "platform/zxbeeper.h"
#include "song.h" #include "song.h"
void DivDispatchContainer::setRates(double gotRate) { void DivDispatchContainer::setRates(double gotRate) {
@ -235,6 +239,15 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_TIA: case DIV_SYSTEM_TIA:
dispatch=new DivPlatformTIA; dispatch=new DivPlatformTIA;
break; break;
case DIV_SYSTEM_OPN:
dispatch=new DivPlatformYM2203;
break;
case DIV_SYSTEM_OPN_EXT:
dispatch=new DivPlatformYM2203Ext;
break;
case DIV_SYSTEM_PC98:
dispatch=new DivPlatformYM2608;
break;
case DIV_SYSTEM_OPLL: case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_OPLL_DRUMS:
case DIV_SYSTEM_VRC7: case DIV_SYSTEM_VRC7:
@ -266,6 +279,14 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
dispatch=new DivPlatformOPL; dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(3,true); ((DivPlatformOPL*)dispatch)->setOPLType(3,true);
break; break;
case DIV_SYSTEM_Y8950:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(8950,false);
break;
case DIV_SYSTEM_Y8950_DRUMS:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(8950,true);
break;
case DIV_SYSTEM_OPZ: case DIV_SYSTEM_OPZ:
dispatch=new DivPlatformTX81Z; dispatch=new DivPlatformTX81Z;
break; break;
@ -279,6 +300,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_PCSPKR: case DIV_SYSTEM_PCSPKR:
dispatch=new DivPlatformPCSpeaker; dispatch=new DivPlatformPCSpeaker;
break; break;
case DIV_SYSTEM_SFX_BEEPER:
dispatch=new DivPlatformZXBeeper;
break;
case DIV_SYSTEM_LYNX: case DIV_SYSTEM_LYNX:
dispatch=new DivPlatformLynx; dispatch=new DivPlatformLynx;
break; break;

View file

@ -139,18 +139,18 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) {
int nextRow=0; int nextRow=0;
int effectVal=0; int effectVal=0;
DivPattern* pat[DIV_MAX_CHANS]; DivPattern* pat[DIV_MAX_CHANS];
for (int i=0; i<song.ordersLen; i++) { for (int i=0; i<curSubSong->ordersLen; i++) {
for (int j=0; j<chans; j++) { for (int j=0; j<chans; j++) {
pat[j]=song.pat[j].getPattern(song.orders.ord[j][i],false); pat[j]=curPat[j].getPattern(curOrders->ord[j][i],false);
} }
for (int j=nextRow; j<song.patLen; j++) { for (int j=nextRow; j<curSubSong->patLen; j++) {
nextRow=0; nextRow=0;
for (int k=0; k<chans; k++) { for (int k=0; k<chans; k++) {
for (int l=0; l<song.pat[k].effectCols; l++) { for (int l=0; l<curPat[k].effectCols; l++) {
effectVal=pat[k]->data[j][5+(l<<1)]; effectVal=pat[k]->data[j][5+(l<<1)];
if (effectVal<0) effectVal=0; if (effectVal<0) effectVal=0;
if (pat[k]->data[j][4+(l<<1)]==0x0d) { if (pat[k]->data[j][4+(l<<1)]==0x0d) {
if (nextOrder==-1 && (i<song.ordersLen-1 || !song.ignoreJumpAtEnd)) { if (nextOrder==-1 && (i<curSubSong->ordersLen-1 || !song.ignoreJumpAtEnd)) {
nextOrder=i+1; nextOrder=i+1;
nextRow=effectVal; nextRow=effectVal;
} }
@ -491,6 +491,86 @@ void DivEngine::notifyWaveChange(int wave) {
BUSY_END; BUSY_END;
} }
int DivEngine::loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret) {
ret = NULL;
if (path.empty()) {
return 0;
}
logI("loading ROM %s...",path);
FILE* f=ps_fopen(path.c_str(),"rb");
if (f==NULL) {
logE("error: %s",strerror(errno));
lastError=strerror(errno);
return -1;
}
if (fseek(f,0,SEEK_END)<0) {
logE("size error: %s",strerror(errno));
lastError=fmt::sprintf("on seek: %s",strerror(errno));
fclose(f);
return -1;
}
ssize_t len=ftell(f);
if (len==(SIZE_MAX>>1)) {
logE("could not get file length: %s",strerror(errno));
lastError=fmt::sprintf("on pre tell: %s",strerror(errno));
fclose(f);
return -1;
}
if (len<1) {
if (len==0) {
logE("that file is empty!");
lastError="file is empty";
} else {
logE("tell error: %s",strerror(errno));
lastError=fmt::sprintf("on tell: %s",strerror(errno));
}
fclose(f);
return -1;
}
if (len!=expectedSize) {
logE("ROM size mismatch, expected: %d bytes, was: %d bytes", expectedSize, len);
lastError=fmt::sprintf("ROM size mismatch, expected: %d bytes, was: %d", expectedSize, len);
return -1;
}
if (fseek(f,0,SEEK_SET)<0) {
logE("size error: %s",strerror(errno));
lastError=fmt::sprintf("on get size: %s",strerror(errno));
fclose(f);
return -1;
}
unsigned char* file=new unsigned char[len];
if (fread(file,1,(size_t)len,f)!=(size_t)len) {
logE("read error: %s",strerror(errno));
lastError=fmt::sprintf("on read: %s",strerror(errno));
fclose(f);
delete[] file;
return -1;
}
fclose(f);
ret = file;
return 0;
}
int DivEngine::loadSampleROMs() {
if (yrw801ROM!=NULL) {
delete[] yrw801ROM;
yrw801ROM=NULL;
}
if (tg100ROM!=NULL) {
delete[] tg100ROM;
tg100ROM=NULL;
}
if (mu5ROM!=NULL) {
delete[] mu5ROM;
mu5ROM=NULL;
}
int error = 0;
error += loadSampleROM(getConfString("yrw801Path",""), 0x200000, yrw801ROM);
error += loadSampleROM(getConfString("tg100Path",""), 0x200000, tg100ROM);
error += loadSampleROM(getConfString("mu5Path",""), 0x200000, mu5ROM);
return error;
}
void DivEngine::renderSamplesP() { void DivEngine::renderSamplesP() {
BUSY_BEGIN; BUSY_BEGIN;
renderSamples(); renderSamples();
@ -616,6 +696,7 @@ void DivEngine::createNew(const int* description) {
saveLock.lock(); saveLock.lock();
song.unload(); song.unload();
song=DivSong(); song=DivSong();
changeSong(0);
if (description!=NULL) { if (description!=NULL) {
initSongWithDesc(description); initSongWithDesc(description);
} }
@ -637,45 +718,55 @@ void DivEngine::swapChannels(int src, int dest) {
} }
for (int i=0; i<256; i++) { for (int i=0; i<256; i++) {
song.orders.ord[dest][i]^=song.orders.ord[src][i]; curOrders->ord[dest][i]^=curOrders->ord[src][i];
song.orders.ord[src][i]^=song.orders.ord[dest][i]; curOrders->ord[src][i]^=curOrders->ord[dest][i];
song.orders.ord[dest][i]^=song.orders.ord[src][i]; curOrders->ord[dest][i]^=curOrders->ord[src][i];
DivPattern* prev=song.pat[src].data[i]; DivPattern* prev=curPat[src].data[i];
song.pat[src].data[i]=song.pat[dest].data[i]; curPat[src].data[i]=curPat[dest].data[i];
song.pat[dest].data[i]=prev; curPat[dest].data[i]=prev;
} }
song.pat[src].effectCols^=song.pat[dest].effectCols; curPat[src].effectCols^=curPat[dest].effectCols;
song.pat[dest].effectCols^=song.pat[src].effectCols; curPat[dest].effectCols^=curPat[src].effectCols;
song.pat[src].effectCols^=song.pat[dest].effectCols; curPat[src].effectCols^=curPat[dest].effectCols;
String prevChanName=song.chanName[src]; String prevChanName=curSubSong->chanName[src];
String prevChanShortName=song.chanShortName[src]; String prevChanShortName=curSubSong->chanShortName[src];
bool prevChanShow=song.chanShow[src]; bool prevChanShow=curSubSong->chanShow[src];
bool prevChanCollapse=song.chanCollapse[src]; bool prevChanCollapse=curSubSong->chanCollapse[src];
song.chanName[src]=song.chanName[dest]; curSubSong->chanName[src]=curSubSong->chanName[dest];
song.chanShortName[src]=song.chanShortName[dest]; curSubSong->chanShortName[src]=curSubSong->chanShortName[dest];
song.chanShow[src]=song.chanShow[dest]; curSubSong->chanShow[src]=curSubSong->chanShow[dest];
song.chanCollapse[src]=song.chanCollapse[dest]; curSubSong->chanCollapse[src]=curSubSong->chanCollapse[dest];
song.chanName[dest]=prevChanName; curSubSong->chanName[dest]=prevChanName;
song.chanShortName[dest]=prevChanShortName; curSubSong->chanShortName[dest]=prevChanShortName;
song.chanShow[dest]=prevChanShow; curSubSong->chanShow[dest]=prevChanShow;
song.chanCollapse[dest]=prevChanCollapse; curSubSong->chanCollapse[dest]=prevChanCollapse;
} }
void DivEngine::stompChannel(int ch) { void DivEngine::stompChannel(int ch) {
logV("stomping channel %d",ch); logV("stomping channel %d",ch);
for (int i=0; i<256; i++) { for (int i=0; i<256; i++) {
song.orders.ord[ch][i]=0; curOrders->ord[ch][i]=0;
} }
song.pat[ch].wipePatterns(); curPat[ch].wipePatterns();
song.pat[ch].effectCols=1; curPat[ch].effectCols=1;
song.chanName[ch]=""; curSubSong->chanName[ch]="";
song.chanShortName[ch]=""; curSubSong->chanShortName[ch]="";
song.chanShow[ch]=true; curSubSong->chanShow[ch]=true;
song.chanCollapse[ch]=false; curSubSong->chanCollapse[ch]=false;
}
void DivEngine::changeSong(size_t songIndex) {
if (songIndex>=song.subsong.size()) return;
curSubSong=song.subsong[songIndex];
curPat=song.subsong[songIndex]->pat;
curOrders=&song.subsong[songIndex]->orders;
curSubSongIndex=songIndex;
curOrder=0;
curRow=0;
} }
void DivEngine::swapChannelsP(int src, int dest) { void DivEngine::swapChannelsP(int src, int dest) {
@ -688,6 +779,41 @@ void DivEngine::swapChannelsP(int src, int dest) {
BUSY_END; BUSY_END;
} }
void DivEngine::changeSongP(size_t index) {
if (index>=song.subsong.size()) return;
if (index==curSubSongIndex) return;
stop();
BUSY_BEGIN;
saveLock.lock();
changeSong(index);
saveLock.unlock();
BUSY_END;
}
int DivEngine::addSubSong() {
if (song.subsong.size()>=127) return -1;
BUSY_BEGIN;
saveLock.lock();
song.subsong.push_back(new DivSubSong);
saveLock.unlock();
BUSY_END;
return song.subsong.size()-1;
}
bool DivEngine::removeSubSong(int index) {
if (song.subsong.size()<=1) return false;
stop();
BUSY_BEGIN;
saveLock.lock();
song.subsong[index]->clearData();
delete song.subsong[index];
song.subsong.erase(song.subsong.begin()+index);
changeSong(0);
saveLock.unlock();
BUSY_END;
return true;
}
void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) { void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
int chanCount=chans; int chanCount=chans;
quitDispatch(); quitDispatch();
@ -760,6 +886,7 @@ bool DivEngine::addSystem(DivSystem which) {
return true; return true;
} }
// TODO: maybe issue with subsongs?
bool DivEngine::removeSystem(int index, bool preserveOrder) { bool DivEngine::removeSystem(int index, bool preserveOrder) {
if (song.systemLen<=1) { if (song.systemLen<=1) {
lastError="cannot remove the last one"; lastError="cannot remove the last one";
@ -1245,15 +1372,15 @@ void DivEngine::reset() {
} }
extValue=0; extValue=0;
extValuePresent=0; extValuePresent=0;
speed1=song.speed1; speed1=curSubSong->speed1;
speed2=song.speed2; speed2=curSubSong->speed2;
firstTick=false; firstTick=false;
nextSpeed=speed1; nextSpeed=speed1;
divider=60; divider=60;
if (song.customTempo) { if (curSubSong->customTempo) {
divider=song.hz; divider=curSubSong->hz;
} else { } else {
if (song.pal) { if (curSubSong->pal) {
divider=60; divider=60;
} else { } else {
divider=50; divider=50;
@ -1408,6 +1535,10 @@ int DivEngine::getRow() {
return curRow; return curRow;
} }
size_t DivEngine::getCurrentSubSong() {
return curSubSongIndex;
}
unsigned char DivEngine::getSpeed1() { unsigned char DivEngine::getSpeed1() {
return speed1; return speed1;
} }
@ -1417,9 +1548,9 @@ unsigned char DivEngine::getSpeed2() {
} }
float DivEngine::getHz() { float DivEngine::getHz() {
if (song.customTempo) { if (curSubSong->customTempo) {
return song.hz; return curSubSong->hz;
} else if (song.pal) { } else if (curSubSong->pal) {
return 60.0; return 60.0;
} else { } else {
return 50.0; return 50.0;
@ -1526,6 +1657,7 @@ void DivEngine::unmuteAll() {
} }
int DivEngine::addInstrument(int refChan) { int DivEngine::addInstrument(int refChan) {
if (song.ins.size()>=256) return -1;
BUSY_BEGIN; BUSY_BEGIN;
DivInstrument* ins=new DivInstrument; DivInstrument* ins=new DivInstrument;
int insCount=(int)song.ins.size(); int insCount=(int)song.ins.size();
@ -1544,7 +1676,9 @@ int DivEngine::addInstrument(int refChan) {
*ins=song.nullInsQSound; *ins=song.nullInsQSound;
} }
ins->name=fmt::sprintf("Instrument %d",insCount); ins->name=fmt::sprintf("Instrument %d",insCount);
if (prefType!=DIV_INS_NULL) {
ins->type=prefType; ins->type=prefType;
}
saveLock.lock(); saveLock.lock();
song.ins.push_back(ins); song.ins.push_back(ins);
song.insLen=insCount+1; song.insLen=insCount+1;
@ -1554,6 +1688,10 @@ int DivEngine::addInstrument(int refChan) {
} }
int DivEngine::addInstrumentPtr(DivInstrument* which) { int DivEngine::addInstrumentPtr(DivInstrument* which) {
if (song.ins.size()>=256) {
delete which;
return -1;
}
BUSY_BEGIN; BUSY_BEGIN;
saveLock.lock(); saveLock.lock();
song.ins.push_back(which); song.ins.push_back(which);
@ -1584,10 +1722,10 @@ void DivEngine::delInstrument(int index) {
song.insLen=song.ins.size(); song.insLen=song.ins.size();
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
for (int j=0; j<256; j++) { for (int j=0; j<256; j++) {
if (song.pat[i].data[j]==NULL) continue; if (curPat[i].data[j]==NULL) continue;
for (int k=0; k<song.patLen; k++) { for (int k=0; k<curSubSong->patLen; k++) {
if (song.pat[i].data[j]->data[k][2]>index) { if (curPat[i].data[j]->data[k][2]>index) {
song.pat[i].data[j]->data[k][2]--; curPat[i].data[j]->data[k][2]--;
} }
} }
} }
@ -1598,6 +1736,7 @@ void DivEngine::delInstrument(int index) {
} }
int DivEngine::addWave() { int DivEngine::addWave() {
if (song.wave.size()>=256) return -1;
BUSY_BEGIN; BUSY_BEGIN;
saveLock.lock(); saveLock.lock();
DivWavetable* wave=new DivWavetable; DivWavetable* wave=new DivWavetable;
@ -1610,37 +1749,48 @@ int DivEngine::addWave() {
} }
bool DivEngine::addWaveFromFile(const char* path) { bool DivEngine::addWaveFromFile(const char* path) {
if (song.wave.size()>=256) {
lastError="too many wavetables!";
return false;
}
FILE* f=ps_fopen(path,"rb"); FILE* f=ps_fopen(path,"rb");
if (f==NULL) { if (f==NULL) {
lastError=fmt::sprintf("%s",strerror(errno));
return false; return false;
} }
unsigned char* buf; unsigned char* buf;
ssize_t len; ssize_t len;
if (fseek(f,0,SEEK_END)!=0) { if (fseek(f,0,SEEK_END)!=0) {
fclose(f); fclose(f);
lastError=fmt::sprintf("could not seek to end: %s",strerror(errno));
return false; return false;
} }
len=ftell(f); len=ftell(f);
if (len<0) { if (len<0) {
fclose(f); fclose(f);
lastError=fmt::sprintf("could not determine file size: %s",strerror(errno));
return false; return false;
} }
if (len==(SIZE_MAX>>1)) { if (len==(SIZE_MAX>>1)) {
fclose(f); fclose(f);
lastError="file size is invalid!";
return false; return false;
} }
if (len==0) { if (len==0) {
fclose(f); fclose(f);
lastError="file is empty";
return false; return false;
} }
if (fseek(f,0,SEEK_SET)!=0) { if (fseek(f,0,SEEK_SET)!=0) {
fclose(f); fclose(f);
lastError=fmt::sprintf("could not seek to beginning: %s",strerror(errno));
return false; return false;
} }
buf=new unsigned char[len]; buf=new unsigned char[len];
if (fread(buf,1,len,f)!=(size_t)len) { if (fread(buf,1,len,f)!=(size_t)len) {
logW("did not read entire wavetable file buffer!"); logW("did not read entire wavetable file buffer!");
delete[] buf; delete[] buf;
lastError=fmt::sprintf("could not read entire file: %s",strerror(errno));
return false; return false;
} }
fclose(f); fclose(f);
@ -1720,6 +1870,7 @@ bool DivEngine::addWaveFromFile(const char* path) {
} catch (EndOfFileException& e) { } catch (EndOfFileException& e) {
delete wave; delete wave;
delete[] buf; delete[] buf;
lastError="premature end of file";
return false; return false;
} }
@ -1746,6 +1897,7 @@ void DivEngine::delWave(int index) {
} }
int DivEngine::addSample() { int DivEngine::addSample() {
if (song.sample.size()>=256) return -1;
BUSY_BEGIN; BUSY_BEGIN;
saveLock.lock(); saveLock.lock();
DivSample* sample=new DivSample; DivSample* sample=new DivSample;
@ -1760,6 +1912,10 @@ int DivEngine::addSample() {
} }
int DivEngine::addSampleFromFile(const char* path) { int DivEngine::addSampleFromFile(const char* path) {
if (song.sample.size()>=256) {
lastError="too many samples!";
return -1;
}
BUSY_BEGIN; BUSY_BEGIN;
warnings=""; warnings="";
@ -1957,18 +2113,18 @@ void DivEngine::delSample(int index) {
void DivEngine::addOrder(bool duplicate, bool where) { void DivEngine::addOrder(bool duplicate, bool where) {
unsigned char order[DIV_MAX_CHANS]; unsigned char order[DIV_MAX_CHANS];
if (song.ordersLen>=0xff) return; if (curSubSong->ordersLen>=0xff) return;
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
if (duplicate) { if (duplicate) {
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
order[i]=song.orders.ord[i][curOrder]; order[i]=curOrders->ord[i][curOrder];
} }
} else { } else {
bool used[256]; bool used[256];
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
memset(used,0,sizeof(bool)*256); memset(used,0,sizeof(bool)*256);
for (int j=0; j<song.ordersLen; j++) { for (int j=0; j<curSubSong->ordersLen; j++) {
used[song.orders.ord[i][j]]=true; used[curOrders->ord[i][j]]=true;
} }
order[i]=0xff; order[i]=0xff;
for (int j=0; j<256; j++) { for (int j=0; j<256; j++) {
@ -1982,19 +2138,19 @@ void DivEngine::addOrder(bool duplicate, bool where) {
if (where) { // at the end if (where) { // at the end
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
song.orders.ord[i][song.ordersLen]=order[i]; curOrders->ord[i][curSubSong->ordersLen]=order[i];
} }
song.ordersLen++; curSubSong->ordersLen++;
saveLock.unlock(); saveLock.unlock();
} else { // after current order } else { // after current order
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=song.ordersLen; j>curOrder; j--) { for (int j=curSubSong->ordersLen; j>curOrder; j--) {
song.orders.ord[i][j]=song.orders.ord[i][j-1]; curOrders->ord[i][j]=curOrders->ord[i][j-1];
} }
song.orders.ord[i][curOrder+1]=order[i]; curOrders->ord[i][curOrder+1]=order[i];
} }
song.ordersLen++; curSubSong->ordersLen++;
saveLock.unlock(); saveLock.unlock();
curOrder++; curOrder++;
if (playing && !freelance) { if (playing && !freelance) {
@ -2006,21 +2162,21 @@ void DivEngine::addOrder(bool duplicate, bool where) {
void DivEngine::deepCloneOrder(bool where) { void DivEngine::deepCloneOrder(bool where) {
unsigned char order[DIV_MAX_CHANS]; unsigned char order[DIV_MAX_CHANS];
if (song.ordersLen>=0xff) return; if (curSubSong->ordersLen>=0xff) return;
warnings=""; warnings="";
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
bool didNotFind=true; bool didNotFind=true;
logD("channel %d",i); logD("channel %d",i);
order[i]=song.orders.ord[i][curOrder]; order[i]=curOrders->ord[i][curOrder];
// find free slot // find free slot
for (int j=0; j<256; j++) { for (int j=0; j<256; j++) {
logD("finding free slot in %d...",j); logD("finding free slot in %d...",j);
if (song.pat[i].data[j]==NULL) { if (curPat[i].data[j]==NULL) {
int origOrd=order[i]; int origOrd=order[i];
order[i]=j; order[i]=j;
DivPattern* oldPat=song.pat[i].getPattern(origOrd,false); DivPattern* oldPat=curPat[i].getPattern(origOrd,false);
DivPattern* pat=song.pat[i].getPattern(j,true); DivPattern* pat=curPat[i].getPattern(j,true);
memcpy(pat->data,oldPat->data,256*32*sizeof(short)); memcpy(pat->data,oldPat->data,256*32*sizeof(short));
logD("found at %d",j); logD("found at %d",j);
didNotFind=false; didNotFind=false;
@ -2034,19 +2190,19 @@ void DivEngine::deepCloneOrder(bool where) {
if (where) { // at the end if (where) { // at the end
saveLock.lock(); saveLock.lock();
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
song.orders.ord[i][song.ordersLen]=order[i]; curOrders->ord[i][curSubSong->ordersLen]=order[i];
} }
song.ordersLen++; curSubSong->ordersLen++;
saveLock.unlock(); saveLock.unlock();
} else { // after current order } else { // after current order
saveLock.lock(); saveLock.lock();
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
for (int j=song.ordersLen; j>curOrder; j--) { for (int j=curSubSong->ordersLen; j>curOrder; j--) {
song.orders.ord[i][j]=song.orders.ord[i][j-1]; curOrders->ord[i][j]=curOrders->ord[i][j-1];
} }
song.orders.ord[i][curOrder+1]=order[i]; curOrders->ord[i][curOrder+1]=order[i];
} }
song.ordersLen++; curSubSong->ordersLen++;
saveLock.unlock(); saveLock.unlock();
curOrder++; curOrder++;
if (playing && !freelance) { if (playing && !freelance) {
@ -2057,17 +2213,17 @@ void DivEngine::deepCloneOrder(bool where) {
} }
void DivEngine::deleteOrder() { void DivEngine::deleteOrder() {
if (song.ordersLen<=1) return; if (curSubSong->ordersLen<=1) return;
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=curOrder; j<song.ordersLen; j++) { for (int j=curOrder; j<curSubSong->ordersLen; j++) {
song.orders.ord[i][j]=song.orders.ord[i][j+1]; curOrders->ord[i][j]=curOrders->ord[i][j+1];
} }
} }
song.ordersLen--; curSubSong->ordersLen--;
saveLock.unlock(); saveLock.unlock();
if (curOrder>=song.ordersLen) curOrder=song.ordersLen-1; if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1;
if (playing && !freelance) { if (playing && !freelance) {
playSub(false); playSub(false);
} }
@ -2082,9 +2238,9 @@ void DivEngine::moveOrderUp() {
} }
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1]; curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
song.orders.ord[i][curOrder-1]^=song.orders.ord[i][curOrder]; curOrders->ord[i][curOrder-1]^=curOrders->ord[i][curOrder];
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1]; curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
} }
saveLock.unlock(); saveLock.unlock();
curOrder--; curOrder--;
@ -2096,15 +2252,15 @@ void DivEngine::moveOrderUp() {
void DivEngine::moveOrderDown() { void DivEngine::moveOrderDown() {
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
if (curOrder>=song.ordersLen-1) { if (curOrder>=curSubSong->ordersLen-1) {
BUSY_END; BUSY_END;
return; return;
} }
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1]; curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
song.orders.ord[i][curOrder+1]^=song.orders.ord[i][curOrder]; curOrders->ord[i][curOrder+1]^=curOrders->ord[i][curOrder];
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1]; curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
} }
saveLock.unlock(); saveLock.unlock();
curOrder++; curOrder++;
@ -2117,12 +2273,12 @@ void DivEngine::moveOrderDown() {
void DivEngine::exchangeIns(int one, int two) { void DivEngine::exchangeIns(int one, int two) {
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
for (int j=0; j<256; j++) { for (int j=0; j<256; j++) {
if (song.pat[i].data[j]==NULL) continue; if (curPat[i].data[j]==NULL) continue;
for (int k=0; k<song.patLen; k++) { for (int k=0; k<curSubSong->patLen; k++) {
if (song.pat[i].data[j]->data[k][2]==one) { if (curPat[i].data[j]->data[k][2]==one) {
song.pat[i].data[j]->data[k][2]=two; curPat[i].data[j]->data[k][2]=two;
} else if (song.pat[i].data[j]->data[k][2]==two) { } else if (curPat[i].data[j]->data[k][2]==two) {
song.pat[i].data[j]->data[k][2]=one; curPat[i].data[j]->data[k][2]=one;
} }
} }
} }
@ -2244,9 +2400,9 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
// 1. check which channels are viable for this instrument // 1. check which channels are viable for this instrument
DivInstrument* insInst=getIns(ins); DivInstrument* insInst=getIns(ins);
if (getPreferInsType(finalChan)!=insInst->type && getPreferInsSecondType(finalChan)!=insInst->type) notInViableChannel=true; if (getPreferInsType(finalChan)!=insInst->type && getPreferInsSecondType(finalChan)!=insInst->type && getPreferInsType(finalChan)!=DIV_INS_NULL) notInViableChannel=true;
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
if (ins==-1 || ins>=song.insLen || getPreferInsType(i)==insInst->type || getPreferInsSecondType(i)==insInst->type) { if (ins==-1 || ins>=song.insLen || getPreferInsType(i)==insInst->type || (getPreferInsType(i)==DIV_INS_NULL && finalChanType==DIV_CH_NOISE) || getPreferInsSecondType(i)==insInst->type) {
if (insInst->type==DIV_INS_OPL) { if (insInst->type==DIV_INS_OPL) {
if (insInst->fm.ops==2 || getChannelType(i)==DIV_CH_OP) { if (insInst->fm.ops==2 || getChannelType(i)==DIV_CH_OP) {
isViable[i]=true; isViable[i]=true;
@ -2326,7 +2482,7 @@ void DivEngine::autoNoteOffAll() {
void DivEngine::setOrder(unsigned char order) { void DivEngine::setOrder(unsigned char order) {
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
curOrder=order; curOrder=order;
if (order>=song.ordersLen) curOrder=0; if (order>=curSubSong->ordersLen) curOrder=0;
if (playing && !freelance) { if (playing && !freelance) {
playSub(false); playSub(false);
} }
@ -2349,15 +2505,15 @@ void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) {
void DivEngine::setSongRate(float hz, bool pal) { void DivEngine::setSongRate(float hz, bool pal) {
BUSY_BEGIN; BUSY_BEGIN;
saveLock.lock(); saveLock.lock();
song.pal=!pal; curSubSong->pal=!pal;
song.hz=hz; curSubSong->hz=hz;
// what? // what?
song.customTempo=true; curSubSong->customTempo=true;
divider=60; divider=60;
if (song.customTempo) { if (curSubSong->customTempo) {
divider=song.hz; divider=curSubSong->hz;
} else { } else {
if (song.pal) { if (curSubSong->pal) {
divider=60; divider=60;
} else { } else {
divider=50; divider=50;
@ -2722,6 +2878,8 @@ bool DivEngine::init() {
loadConf(); loadConf();
loadSampleROMs();
// set default system preset // set default system preset
if (!hasLoadedSomething) { if (!hasLoadedSomething) {
logD("setting default preset"); logD("setting default preset");
@ -2795,5 +2953,9 @@ bool DivEngine::quit() {
active=false; active=false;
delete[] oscBuf[0]; delete[] oscBuf[0];
delete[] oscBuf[1]; delete[] oscBuf[1];
if (yrw801ROM!=NULL) delete[] yrw801ROM;
if (tg100ROM!=NULL) delete[] tg100ROM;
if (mu5ROM!=NULL) delete[] mu5ROM;
song.unload();
return true; return true;
} }

View file

@ -45,8 +45,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false; #define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev94" #define DIV_VERSION "dev95"
#define DIV_ENGINE_VERSION 94 #define DIV_ENGINE_VERSION 95
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
@ -188,6 +188,7 @@ typedef std::function<bool(int,unsigned char,unsigned char)> EffectProcess;
struct DivSysDef { struct DivSysDef {
const char* name; const char* name;
const char* nameJ; const char* nameJ;
const char* description;
unsigned char id; unsigned char id;
unsigned char id_DMF; unsigned char id_DMF;
int channels; int channels;
@ -203,7 +204,7 @@ struct DivSysDef {
EffectProcess postEffectFunc; EffectProcess postEffectFunc;
DivSysDef( DivSysDef(
const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans, const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans,
bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, const char* desc,
std::initializer_list<const char*> chNames, std::initializer_list<const char*> chNames,
std::initializer_list<const char*> chShortNames, std::initializer_list<const char*> chShortNames,
std::initializer_list<int> chTypes, std::initializer_list<int> chTypes,
@ -213,6 +214,7 @@ struct DivSysDef {
EffectProcess postFxHandler=[](int,unsigned char,unsigned char) -> bool {return false;}): EffectProcess postFxHandler=[](int,unsigned char,unsigned char) -> bool {return false;}):
name(sysName), name(sysName),
nameJ(sysNameJ), nameJ(sysNameJ),
description(desc),
id(fileID), id(fileID),
id_DMF(fileID_DMF), id_DMF(fileID_DMF),
channels(chans), channels(chans),
@ -302,6 +304,7 @@ class DivEngine {
bool hasLoadedSomething; bool hasLoadedSomething;
int softLockCount; int softLockCount;
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed; int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
size_t curSubSongIndex;
double divider; double divider;
int cycles; int cycles;
double clockDrift; double clockDrift;
@ -399,6 +402,8 @@ class DivEngine {
void loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret);
bool initAudioBackend(); bool initAudioBackend();
bool deinitAudioBackend(); bool deinitAudioBackend();
@ -409,8 +414,14 @@ class DivEngine {
void swapChannels(int src, int dest); void swapChannels(int src, int dest);
void stompChannel(int ch); void stompChannel(int ch);
// change song (UNSAFE)
void changeSong(size_t songIndex);
public: public:
DivSong song; DivSong song;
DivOrders* curOrders;
DivChannelData* curPat;
DivSubSong* curSubSong;
DivInstrument* tempIns; DivInstrument* tempIns;
DivSystem sysOfChan[DIV_MAX_CHANS]; DivSystem sysOfChan[DIV_MAX_CHANS];
int dispatchOfChan[DIV_MAX_CHANS]; int dispatchOfChan[DIV_MAX_CHANS];
@ -601,6 +612,9 @@ class DivEngine {
// get current row // get current row
int getRow(); int getRow();
// get current subsong
size_t getCurrentSubSong();
// get speed 1 // get speed 1
unsigned char getSpeed1(); unsigned char getSpeed1();
@ -785,6 +799,9 @@ class DivEngine {
// get register cheatsheet // get register cheatsheet
const char** getRegisterSheet(int sys); const char** getRegisterSheet(int sys);
// load sample ROMs
int loadSampleROMs();
// UNSAFE render samples - only execute when locked // UNSAFE render samples - only execute when locked
void renderSamples(); void renderSamples();
@ -794,6 +811,15 @@ class DivEngine {
// public swap channels // public swap channels
void swapChannelsP(int src, int dest); void swapChannelsP(int src, int dest);
// public change song
void changeSongP(size_t index);
// add subsong
int addSubSong();
// remove subsong
bool removeSubSong(int index);
// change system // change system
void changeSystem(int index, DivSystem which, bool preserveOrder=true); void changeSystem(int index, DivSystem which, bool preserveOrder=true);
@ -858,6 +884,10 @@ class DivEngine {
// terminate the engine. // terminate the engine.
bool quit(); bool quit();
unsigned char* yrw801ROM;
unsigned char* tg100ROM;
unsigned char* mu5ROM;
DivEngine(): DivEngine():
output(NULL), output(NULL),
exportThread(NULL), exportThread(NULL),
@ -891,6 +921,7 @@ class DivEngine {
curOrder(0), curOrder(0),
remainingLoops(-1), remainingLoops(-1),
nextSpeed(3), nextSpeed(3),
curSubSongIndex(0),
divider(60), divider(60),
cycles(0), cycles(0),
clockDrift(0), clockDrift(0),
@ -926,13 +957,18 @@ class DivEngine {
metroAmp(0.0f), metroAmp(0.0f),
metroVol(1.0f), metroVol(1.0f),
totalProcessed(0), totalProcessed(0),
curOrders(NULL),
curPat(NULL),
tempIns(NULL), tempIns(NULL),
oscBuf{NULL,NULL}, oscBuf{NULL,NULL},
oscSize(1), oscSize(1),
oscReadPos(0), oscReadPos(0),
oscWritePos(0), oscWritePos(0),
tickMult(1), tickMult(1),
processTime(0) { processTime(0),
yrw801ROM(NULL),
tg100ROM(NULL),
mu5ROM(NULL) {
memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool)); memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool));
memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool)); memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool));
memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int));
@ -947,6 +983,8 @@ class DivEngine {
sysFileMapFur[i]=DIV_SYSTEM_NULL; sysFileMapFur[i]=DIV_SYSTEM_NULL;
sysFileMapDMF[i]=DIV_SYSTEM_NULL; sysFileMapDMF[i]=DIV_SYSTEM_NULL;
} }
changeSong(0);
} }
}; };
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -385,6 +385,45 @@ struct DivInstrumentAmiga {
TransWave transWave; TransWave transWave;
std::vector<TransWaveMap> transWaveMap; std::vector<TransWaveMap> transWaveMap;
/**
* get the sample at specified note.
* @return the sample.
*/
inline short getSample(int note) {
if (useNoteMap) {
if (note<0) note=0;
if (note>119) note=119;
return noteMap[note].ind;
}
return initSample;
}
/**
* get the sample frequency at specified note.
* @return the frequency, or -1 if not using note map.
*/
inline int getFreq(int note) {
if (useNoteMap) {
if (note<0) note=0;
if (note>119) note=119;
return noteMap[note].freq;
}
return -1;
}
/**
* get the sample reversed flag at specified note.
* @return the reversed flag.
*/
inline bool getReversed(int note) {
if (useNoteMap) {
if (note<0) note=0;
if (note>119) note=119;
return noteMap[note].reversed;
}
return reversed;
}
DivInstrumentAmiga(): DivInstrumentAmiga():
initSample(0), initSample(0),
reversed(false), reversed(false),

View file

@ -49,7 +49,7 @@ void DivChannelData::wipePatterns() {
} }
} }
void DivPattern::copyOn(DivPattern *dest) { void DivPattern::copyOn(DivPattern* dest) {
dest->name=name; dest->name=name;
memcpy(dest->data,data,sizeof(data)); memcpy(dest->data,data,sizeof(data));
} }

View file

@ -220,7 +220,7 @@ void DivPlatformAmiga::tick(bool sysTick) {
} }
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) { if (chan[i].keyOn) {
@ -238,7 +238,6 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
double off=1.0;
if (ins->amiga.useWave) { if (ins->amiga.useWave) {
chan[c.chan].useWave=true; chan[c.chan].useWave=true;
chan[c.chan].audLen=(ins->amiga.waveLen+1)>>1; chan[c.chan].audLen=(ins->amiga.waveLen+1)>>1;
@ -250,19 +249,11 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
} }
} }
} else { } else {
chan[c.chan].sample=ins->amiga.initSample; chan[c.chan].sample=ins->amiga.getSample(c.value);
chan[c.chan].useWave=false; chan[c.chan].useWave=false;
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=8363.0/(double)s->centerRate;
}
}
} }
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value)); chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value));
} }
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
chan[c.chan].sample=-1; chan[c.chan].sample=-1;
@ -328,17 +319,8 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
break; break;
case DIV_CMD_NOTE_PORTA: { case DIV_CMD_NOTE_PORTA: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].sample=ins->amiga.initSample; chan[c.chan].sample=ins->amiga.getSample(c.value2);
double off=1.0; int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2));
if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=8363.0/(double)s->centerRate;
}
}
int destFreq=round(off*NOTE_PERIODIC_NOROUND(c.value2));
bool return2=false; bool return2=false;
if (destFreq>chan[c.chan].baseFreq) { if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value; chan[c.chan].baseFreq+=c.value;
@ -361,16 +343,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_LEGATO: { case DIV_CMD_LEGATO: {
double off=1.0; chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))));
if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=8363.0/(double)s->centerRate;
}
}
chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))));
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
break; break;

View file

@ -27,7 +27,7 @@
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(regRemap(a),v); if (dumpWrites) {addWrite(regRemap(a),v);} } #define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(regRemap(a),v); if (dumpWrites) {addWrite(regRemap(a),v);} }
#define CHIP_DIVIDER 8 #define CHIP_DIVIDER ((sunsoft||clockSel)?16:8)
const char* regCheatSheetAY[]={ const char* regCheatSheetAY[]={
"FreqL_A", "0", "FreqL_A", "0",
@ -589,6 +589,7 @@ void DivPlatformAY8910::poke(std::vector<DivRegWrite>& wlist) {
} }
void DivPlatformAY8910::setFlags(unsigned int flags) { void DivPlatformAY8910::setFlags(unsigned int flags) {
clockSel=(flags>>7)&1;
switch (flags&15) { switch (flags&15) {
case 1: case 1:
chipClock=COLOR_PAL*2.0/5.0; chipClock=COLOR_PAL*2.0/5.0;
@ -620,6 +621,12 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
case 10: case 10:
chipClock=2097152; chipClock=2097152;
break; break;
case 11:
chipClock=COLOR_NTSC;
break;
case 12:
chipClock=3600000;
break;
default: default:
chipClock=COLOR_NTSC/2.0; chipClock=COLOR_NTSC/2.0;
break; break;
@ -632,7 +639,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
if (ay!=NULL) delete ay; if (ay!=NULL) delete ay;
switch ((flags>>4)&3) { switch ((flags>>4)&3) {
case 1: case 1:
ay=new ym2149_device(rate); ay=new ym2149_device(rate,clockSel);
sunsoft=false; sunsoft=false;
intellivision=false; intellivision=false;
break; break;
@ -654,7 +661,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
} }
ay->device_start(); ay->device_start();
stereo=flags>>6; stereo=(flags>>6)&1;
} }
int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {

View file

@ -70,7 +70,7 @@ class DivPlatformAY8910: public DivDispatch {
int delay; int delay;
bool extMode; bool extMode;
bool stereo, sunsoft, intellivision; bool stereo, sunsoft, intellivision, clockSel;
bool ioPortA, ioPortB; bool ioPortA, ioPortB;
unsigned char portAVal, portBVal; unsigned char portAVal, portBVal;

View file

@ -27,7 +27,7 @@
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } #define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 4 #define CHIP_DIVIDER (clockSel?8:4)
const char* regCheatSheetAY8930[]={ const char* regCheatSheetAY8930[]={
"FreqL_A", "00", "FreqL_A", "00",
@ -64,7 +64,7 @@ const char* regCheatSheetAY8930[]={
void DivPlatformAY8930::immWrite(unsigned char a, unsigned char v) { void DivPlatformAY8930::immWrite(unsigned char a, unsigned char v) {
if ((int)bank!=(a>>4)) { if ((int)bank!=(a>>4)) {
bank=a>>4; bank=a>>4;
immWrite2(0x0d, 0xa0|(bank<<4)|ayEnvMode[0]); immWrite2(0x0d, 0xa0|(bank<<4)|chan[0].envelope.mode);
} }
if (a==0x0d) { if (a==0x0d) {
immWrite2(0x0d,0xa0|(bank<<4)|(v&15)); immWrite2(0x0d,0xa0|(bank<<4)|(v&15));
@ -258,8 +258,8 @@ void DivPlatformAY8930::tick(bool sysTick) {
rWrite(0x16+i,chan[i].std.ex1.val); rWrite(0x16+i,chan[i].std.ex1.val);
} }
if (chan[i].std.ex2.had) { if (chan[i].std.ex2.had) {
ayEnvMode[i]=chan[i].std.ex2.val; chan[i].envelope.mode=chan[i].std.ex2.val;
rWrite(regMode[i],ayEnvMode[i]); rWrite(regMode[i],chan[i].envelope.mode);
} }
if (chan[i].std.ex3.had) { if (chan[i].std.ex3.had) {
chan[i].autoEnvNum=chan[i].std.ex3.val; chan[i].autoEnvNum=chan[i].std.ex3.val;
@ -296,29 +296,29 @@ void DivPlatformAY8930::tick(bool sysTick) {
if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false; if (chan[i].keyOff) chan[i].keyOff=false;
if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) { if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) {
ayEnvPeriod[i]=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)>>4; chan[i].envelope.period=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)>>4;
immWrite(regPeriodL[i],ayEnvPeriod[i]); immWrite(regPeriodL[i],chan[i].envelope.period);
immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); immWrite(regPeriodH[i],chan[i].envelope.period>>8);
} }
chan[i].freqChanged=false; chan[i].freqChanged=false;
} }
if (ayEnvSlide[i]!=0) { if (chan[i].envelope.slide!=0) {
ayEnvSlideLow[i]+=ayEnvSlide[i]; chan[i].envelope.slideLow+=chan[i].envelope.slide;
while (ayEnvSlideLow[i]>7) { while (chan[i].envelope.slideLow>7) {
ayEnvSlideLow[i]-=8; chan[i].envelope.slideLow-=8;
if (ayEnvPeriod[i]<0xffff) { if (chan[i].envelope.period<0xffff) {
ayEnvPeriod[i]++; chan[i].envelope.period++;
immWrite(regPeriodL[i],ayEnvPeriod[i]); immWrite(regPeriodL[i],chan[i].envelope.period);
immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); immWrite(regPeriodH[i],chan[i].envelope.period>>8);
} }
} }
while (ayEnvSlideLow[i]<-7) { while (chan[i].envelope.slideLow<-7) {
ayEnvSlideLow[i]+=8; chan[i].envelope.slideLow+=8;
if (ayEnvPeriod[i]>0) { if (chan[i].envelope.period>0) {
ayEnvPeriod[i]--; chan[i].envelope.period--;
immWrite(regPeriodL[i],ayEnvPeriod[i]); immWrite(regPeriodL[i],chan[i].envelope.period);
immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); immWrite(regPeriodH[i],chan[i].envelope.period>>8);
} }
} }
} }
@ -435,8 +435,8 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
rWrite(0x06,c.value); rWrite(0x06,c.value);
break; break;
case DIV_CMD_AY_ENVELOPE_SET: case DIV_CMD_AY_ENVELOPE_SET:
ayEnvMode[c.chan]=c.value>>4; chan[c.chan].envelope.mode=c.value>>4;
rWrite(regMode[c.chan],ayEnvMode[c.chan]); rWrite(regMode[c.chan],chan[c.chan].envelope.mode);
if (c.value&15) { if (c.value&15) {
chan[c.chan].psgMode|=4; chan[c.chan].psgMode|=4;
} else { } else {
@ -449,19 +449,19 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
} }
break; break;
case DIV_CMD_AY_ENVELOPE_LOW: case DIV_CMD_AY_ENVELOPE_LOW:
ayEnvPeriod[c.chan]&=0xff00; chan[c.chan].envelope.period&=0xff00;
ayEnvPeriod[c.chan]|=c.value; chan[c.chan].envelope.period|=c.value;
immWrite(regPeriodL[c.chan],ayEnvPeriod[c.chan]); immWrite(regPeriodL[c.chan],chan[c.chan].envelope.period);
immWrite(regPeriodH[c.chan],ayEnvPeriod[c.chan]>>8); immWrite(regPeriodH[c.chan],chan[c.chan].envelope.period>>8);
break; break;
case DIV_CMD_AY_ENVELOPE_HIGH: case DIV_CMD_AY_ENVELOPE_HIGH:
ayEnvPeriod[c.chan]&=0xff; chan[c.chan].envelope.period&=0xff;
ayEnvPeriod[c.chan]|=c.value<<8; chan[c.chan].envelope.period|=c.value<<8;
immWrite(regPeriodL[c.chan],ayEnvPeriod[c.chan]); immWrite(regPeriodL[c.chan],chan[c.chan].envelope.period);
immWrite(regPeriodH[c.chan],ayEnvPeriod[c.chan]>>8); immWrite(regPeriodH[c.chan],chan[c.chan].envelope.period>>8);
break; break;
case DIV_CMD_AY_ENVELOPE_SLIDE: case DIV_CMD_AY_ENVELOPE_SLIDE:
ayEnvSlide[c.chan]=c.value; chan[c.chan].envelope.slide=c.value;
break; break;
case DIV_CMD_AY_NOISE_MASK_AND: case DIV_CMD_AY_NOISE_MASK_AND:
ayNoiseAnd=c.value; ayNoiseAnd=c.value;
@ -526,9 +526,9 @@ void DivPlatformAY8930::muteChannel(int ch, bool mute) {
void DivPlatformAY8930::forceIns() { void DivPlatformAY8930::forceIns() {
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
immWrite(regPeriodL[i],ayEnvPeriod[i]); immWrite(regPeriodL[i],chan[i].envelope.period);
immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); immWrite(regPeriodH[i],chan[i].envelope.period>>8);
immWrite(regMode[i],ayEnvMode[i]); immWrite(regMode[i],chan[i].envelope.mode);
} }
} }
@ -556,10 +556,10 @@ void DivPlatformAY8930::reset() {
chan[i]=DivPlatformAY8930::Channel(); chan[i]=DivPlatformAY8930::Channel();
chan[i].std.setEngine(parent); chan[i].std.setEngine(parent);
chan[i].vol=31; chan[i].vol=31;
ayEnvPeriod[i]=0; chan[i].envelope.period=0;
ayEnvMode[i]=0; chan[i].envelope.mode=0;
ayEnvSlide[i]=0; chan[i].envelope.slide=0;
ayEnvSlideLow[i]=0; chan[i].envelope.slideLow=0;
} }
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);
@ -610,6 +610,7 @@ void DivPlatformAY8930::poke(std::vector<DivRegWrite>& wlist) {
} }
void DivPlatformAY8930::setFlags(unsigned int flags) { void DivPlatformAY8930::setFlags(unsigned int flags) {
clockSel=(flags>>7)&1;
switch (flags&15) { switch (flags&15) {
case 1: case 1:
chipClock=COLOR_PAL*2.0/5.0; chipClock=COLOR_PAL*2.0/5.0;
@ -641,6 +642,12 @@ void DivPlatformAY8930::setFlags(unsigned int flags) {
case 10: case 10:
chipClock=2097152; chipClock=2097152;
break; break;
case 11:
chipClock=COLOR_NTSC;
break;
case 12:
chipClock=3600000;
break;
default: default:
chipClock=COLOR_NTSC/2.0; chipClock=COLOR_NTSC/2.0;
break; break;
@ -650,7 +657,7 @@ void DivPlatformAY8930::setFlags(unsigned int flags) {
oscBuf[i]->rate=rate; oscBuf[i]->rate=rate;
} }
stereo=flags>>6; stereo=(flags>>6)&1;
} }
int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
@ -662,7 +669,7 @@ int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned in
oscBuf[i]=new DivDispatchOscBuffer; oscBuf[i]=new DivDispatchOscBuffer;
} }
setFlags(flags); setFlags(flags);
ay=new ay8930_device(rate); ay=new ay8930_device(rate,clockSel);
ay->device_start(); ay->device_start();
ayBufLen=65536; ayBufLen=65536;
for (int i=0; i<3; i++) ayBuf[i]=new short[ayBufLen]; for (int i=0; i<3; i++) ayBuf[i]=new short[ayBufLen];

View file

@ -27,6 +27,17 @@
class DivPlatformAY8930: public DivDispatch { class DivPlatformAY8930: public DivDispatch {
protected: protected:
struct Channel { struct Channel {
struct Envelope {
unsigned char mode;
unsigned short period;
short slideLow;
short slide;
Envelope():
mode(0),
period(0),
slideLow(0),
slide(0) {}
} envelope;
unsigned char freqH, freqL; unsigned char freqH, freqL;
int freq, baseFreq, note, pitch, pitch2; int freq, baseFreq, note, pitch, pitch2;
int ins; int ins;
@ -59,16 +70,12 @@ class DivPlatformAY8930: public DivDispatch {
int delay; int delay;
bool extMode, stereo; bool extMode, stereo, clockSel;
bool ioPortA, ioPortB; bool ioPortA, ioPortB;
unsigned char portAVal, portBVal; unsigned char portAVal, portBVal;
short oldWrites[32]; short oldWrites[32];
short pendingWrites[32]; short pendingWrites[32];
unsigned char ayEnvMode[3];
unsigned short ayEnvPeriod[3];
short ayEnvSlideLow[3];
short ayEnvSlide[3];
short* ayBuf[3]; short* ayBuf[3];
size_t ayBufLen; size_t ayBufLen;

View file

@ -111,6 +111,9 @@ const char* DivPlatformES5506::getEffectName(unsigned char effect) {
case 0x11: case 0x11:
return "11xx: Set filter mode (00 to 03)"; return "11xx: Set filter mode (00 to 03)";
break; break;
case 0x12:
return "120x: Set pause (bit 0)";
break;
case 0x14: case 0x14:
return "14xx: Set filter coefficient K1 low byte"; return "14xx: Set filter coefficient K1 low byte";
break; break;
@ -456,6 +459,15 @@ void DivPlatformES5506::tick(bool sysTick) {
} }
} }
} }
// control macros
if (!chan[i].keyOn) {
if (chan[i].active && chan[i].std.alg.had) {
if (chan[i].pcm.pause!=(bool)(chan[i].std.alg.val&1)) {
chan[i].pcm.pause=chan[i].std.alg.val&1;
pageWriteMask(0x00|i,0x5f,0x00,chan[i].pcm.pause?0x0002:0x0000,0x0002);
}
}
}
// update registers // update registers
if (chan[i].volChanged.changed) { if (chan[i].volChanged.changed) {
if (!isMuted[i]) { // calculate volume (16 bit) if (!isMuted[i]) { // calculate volume (16 bit)
@ -631,6 +643,9 @@ void DivPlatformES5506::tick(bool sysTick) {
loopFlag|=0x0018; loopFlag|=0x0018;
break; break;
} }
if (chan[i].pcm.pause) {
loopFlag|=0x0002;
}
// Run sample // Run sample
pageWrite(0x00|i,0x06,chan[i].envelope.ecount); // Clear ECOUNT pageWrite(0x00|i,0x06,chan[i].envelope.ecount); // Clear ECOUNT
pageWriteMask(0x00|i,0x5f,0x00,loopFlag,0x3cff); pageWriteMask(0x00|i,0x5f,0x00,loopFlag,0x3cff);
@ -661,6 +676,10 @@ void DivPlatformES5506::tick(bool sysTick) {
int DivPlatformES5506::dispatch(DivCommand c) { int DivPlatformES5506::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
bool sampleVaild=false;
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (c.value>=0 && c.value<120)) ||
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (ins->amiga.transWave.ind>=0 && ins->amiga.transWave.ind<ins->amiga.transWaveMap.size())) ||
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins); DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[c.value]; DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[c.value];
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[ins->amiga.transWave.ind]; DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[ins->amiga.transWave.ind];
@ -668,10 +687,12 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (ins->amiga.transWave.enable) { if (ins->amiga.transWave.enable) {
sample=transWaveInd.ind; sample=transWaveInd.ind;
} else if (ins->amiga.useNoteMap) { } else if (ins->amiga.useNoteMap) {
ins->amiga.noteMap[c.value].ind; sample=noteMapind.ind;
} }
if (sample>=0 && sample<parent->song.sampleLen) { if (sample>=0 && sample<parent->song.sampleLen) {
sampleVaild=true;
chan[c.chan].pcm.index=sample; chan[c.chan].pcm.index=sample;
chan[c.chan].pcm.pause=(chan[c.chan].std.alg.will)?(chan[c.chan].std.alg.val&1):false;
chan[c.chan].transWave.enable=ins->amiga.transWave.enable; chan[c.chan].transWave.enable=ins->amiga.transWave.enable;
chan[c.chan].transWave.sliceEnable=ins->amiga.transWave.sliceEnable; chan[c.chan].transWave.sliceEnable=ins->amiga.transWave.sliceEnable;
chan[c.chan].transWave.ind=ins->amiga.transWave.ind; chan[c.chan].transWave.ind=ins->amiga.transWave.ind;
@ -732,7 +753,9 @@ int DivPlatformES5506::dispatch(DivCommand c) {
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xffff; chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xffff;
chan[c.chan].filter=ins->es5506.filter; chan[c.chan].filter=ins->es5506.filter;
chan[c.chan].envelope=ins->es5506.envelope; chan[c.chan].envelope=ins->es5506.envelope;
} else { }
}
if (!sampleVaild) {
chan[c.chan].pcm.index=-1; chan[c.chan].pcm.index=-1;
chan[c.chan].filter=DivInstrumentES5506::Filter(); chan[c.chan].filter=DivInstrumentES5506::Filter();
chan[c.chan].envelope=DivInstrumentES5506::Envelope(); chan[c.chan].envelope=DivInstrumentES5506::Envelope();
@ -864,6 +887,15 @@ int DivPlatformES5506::dispatch(DivCommand c) {
chan[c.chan].envelope.k2Slow=c.value2&1; chan[c.chan].envelope.k2Slow=c.value2&1;
chan[c.chan].envChanged.k2Ramp=1; chan[c.chan].envChanged.k2Ramp=1;
break; break;
// controls
case DIV_CMD_ES5506_PAUSE:
if (chan[c.chan].active) {
if (chan[c.chan].pcm.pause!=(bool)(c.value&1)) {
chan[c.chan].pcm.pause=c.value&1;
pageWriteMask(0x00|i,0x5f,0x00,chan[c.chan].pcm.pause?0x0002:0x0000,0x0002);
}
}
break;
case DIV_CMD_NOTE_PORTA: { case DIV_CMD_NOTE_PORTA: {
int nextFreq=chan[c.chan].baseFreq; int nextFreq=chan[c.chan].baseFreq;
const int destFreq=NOTE_ES5506(c.chan,c.value2); const int destFreq=NOTE_ES5506(c.chan,c.value2);

View file

@ -36,7 +36,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
int note; int note;
double freqOffs; double freqOffs;
double nextFreqOffs; double nextFreqOffs;
bool reversed; bool reversed, pause;
unsigned int bank; unsigned int bank;
unsigned int start; unsigned int start;
unsigned int end; unsigned int end;
@ -51,6 +51,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
freqOffs(1.0), freqOffs(1.0),
nextFreqOffs(1.0), nextFreqOffs(1.0),
reversed(false), reversed(false),
pause(false),
bank(0), bank(0),
start(0), start(0),
end(0), end(0),

View file

@ -549,7 +549,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (c.chan==5 && dacMode) { if (c.chan==5 && dacMode) {
if (skipRegisterWrites) break; if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { // Furnace mode if (ins->type==DIV_INS_AMIGA) { // Furnace mode
dacSample=ins->amiga.initSample; dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) { if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1; dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) addWrite(0xffff0002,0);

View file

@ -193,7 +193,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
if (c.chan==2) { // PCM if (c.chan==2) { // PCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
if (ins->type==DIV_INS_AMIGA) { if (ins->type==DIV_INS_AMIGA) {
dacSample=ins->amiga.initSample; dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) { if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1; dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) addWrite(0xffff0002,0);

View file

@ -382,7 +382,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
if (c.chan==4) { // PCM if (c.chan==4) { // PCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
if (ins->type==DIV_INS_AMIGA) { if (ins->type==DIV_INS_AMIGA) {
dacSample=ins->amiga.initSample; dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) { if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1; dacSample=-1;
if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0); if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0);

View file

@ -19,6 +19,7 @@
#include "opl.h" #include "opl.h"
#include "../engine.h" #include "../engine.h"
#include "../../ta-log.h"
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
@ -246,19 +247,53 @@ const char* DivPlatformOPL::getEffectName(unsigned char effect) {
void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static short o[2]; static short o[2];
static int os[2]; static int os[2];
static ymfm::ymfm_output<2> aOut;
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
os[0]=0; os[1]=0; os[0]=0; os[1]=0;
if (!writes.empty() && --delay<0) { if (!writes.empty() && --delay<0) {
delay=1; delay=1;
QueuedWrite& w=writes.front(); QueuedWrite& w=writes.front();
switch (w.addr) {
case 8:
if (adpcmChan>=0) {
adpcmB->write(w.addr-7,(w.val&15)|0x80);
OPL3_WriteReg(&fm,w.addr,w.val&0xc0);
} else {
OPL3_WriteReg(&fm,w.addr,w.val); OPL3_WriteReg(&fm,w.addr,w.val);
}
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:
if (adpcmChan>=0) {
adpcmB->write(w.addr-7,w.val);
} else {
OPL3_WriteReg(&fm,w.addr,w.val);
}
break;
default:
OPL3_WriteReg(&fm,w.addr,w.val);
break;
}
regPool[w.addr&511]=w.val; regPool[w.addr&511]=w.val;
writes.pop(); writes.pop();
} }
OPL3_Generate(&fm,o); os[0]+=o[0]; os[1]+=o[1]; OPL3_Generate(&fm,o); os[0]+=o[0]; os[1]+=o[1];
if (adpcmChan>=0) {
adpcmB->clock();
aOut.clear();
adpcmB->output<2>(aOut,0);
if (!isMuted[adpcmChan]) {
os[0]+=aOut.data[0];
os[1]+=aOut.data[0];
oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]+=aOut.data[0];
} else {
oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=0;
}
}
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
unsigned char ch=outChanMap[i]; unsigned char ch=outChanMap[i];
if (ch==255) continue; if (ch==255) continue;
@ -292,6 +327,15 @@ void DivPlatformOPL::acquire(short* bufL, short* bufR, size_t start, size_t len)
//} //}
} }
double DivPlatformOPL::NOTE_ADPCMB(int note) {
if (adpcmChan<0) return 0;
if (chan[adpcmChan].sample>=0 && chan[adpcmChan].sample<parent->song.sampleLen) {
double off=65535.0*(double)(parent->getSample(chan[adpcmChan].sample)->centerRate)/8363.0;
return parent->calcBaseFreq((double)chipClock/144,off,note,false);
}
return 0;
}
void DivPlatformOPL::tick(bool sysTick) { void DivPlatformOPL::tick(bool sysTick) {
for (int i=0; i<totalChans; i++) { for (int i=0; i<totalChans; i++) {
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
@ -501,6 +545,45 @@ void DivPlatformOPL::tick(bool sysTick) {
} }
} }
// ADPCM
if (adpcmChan>=0) {
if (chan[adpcmChan].furnacePCM) {
chan[adpcmChan].std.next();
if (chan[adpcmChan].std.vol.had) {
chan[adpcmChan].outVol=(chan[adpcmChan].vol*MIN(64,chan[adpcmChan].std.vol.val))/64;
immWrite(18,chan[adpcmChan].outVol);
}
if (chan[adpcmChan].std.arp.had) {
if (!chan[adpcmChan].inPorta) {
if (chan[adpcmChan].std.arp.mode) {
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].std.arp.val);
} else {
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].note+(signed char)chan[adpcmChan].std.arp.val);
}
}
chan[adpcmChan].freqChanged=true;
} else {
if (chan[adpcmChan].std.arp.mode && chan[adpcmChan].std.arp.finished) {
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].note);
chan[adpcmChan].freqChanged=true;
}
}
}
if (chan[adpcmChan].freqChanged) {
if (chan[adpcmChan].sample>=0 && chan[adpcmChan].sample<parent->song.sampleLen) {
double off=65535.0*(double)(parent->getSample(chan[adpcmChan].sample)->centerRate)/8363.0;
chan[adpcmChan].freq=parent->calcFreq(chan[adpcmChan].baseFreq,chan[adpcmChan].pitch,false,4,chan[adpcmChan].pitch2,(double)chipClock/144,off);
} else {
chan[adpcmChan].freq=0;
}
immWrite(16,chan[adpcmChan].freq&0xff);
immWrite(17,(chan[adpcmChan].freq>>8)&0xff);
chan[adpcmChan].freqChanged=false;
}
}
for (int i=0; i<512; i++) { for (int i=0; i<512; i++) {
if (pendingWrites[i]!=oldWrites[i]) { if (pendingWrites[i]!=oldWrites[i]) {
immWrite(i,pendingWrites[i]&0xff); immWrite(i,pendingWrites[i]&0xff);
@ -588,6 +671,7 @@ int DivPlatformOPL::toFreq(int freq) {
void DivPlatformOPL::muteChannel(int ch, bool mute) { void DivPlatformOPL::muteChannel(int ch, bool mute) {
isMuted[ch]=mute; isMuted[ch]=mute;
if (ch==adpcmChan) return;
if (oplType<3 && ch<melodicChans) { if (oplType<3 && ch<melodicChans) {
fm.channel[outChanMap[ch]].muted=mute; fm.channel[outChanMap[ch]].muted=mute;
} }
@ -625,13 +709,78 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
} }
int DivPlatformOPL::dispatch(DivCommand c) { int DivPlatformOPL::dispatch(DivCommand c) {
if (c.chan>=totalChans) return 0; if (c.chan>=totalChans && c.chan!=adpcmChan) return 0;
// ineffective in 4-op mode // ineffective in 4-op mode
if (oplType==3 && c.chan<14 && (c.chan&1) && c.cmd!=DIV_CMD_GET_VOLMAX && c.cmd!=DIV_ALWAYS_SET_VOLUME) { if (oplType==3 && c.chan!=adpcmChan && c.chan<14 && (c.chan&1) && c.cmd!=DIV_CMD_GET_VOLMAX && c.cmd!=DIV_ALWAYS_SET_VOLUME) {
if (chan[c.chan-1].fourOp) return 0; if (chan[c.chan-1].fourOp) return 0;
} }
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
if (c.chan==adpcmChan) { // ADPCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].furnacePCM=true;
} else {
chan[c.chan].furnacePCM=false;
}
if (skipRegisterWrites) break;
if (chan[c.chan].furnacePCM) {
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
immWrite(18,chan[c.chan].outVol);
}
chan[c.chan].sample=ins->amiga.getSample(c.value);
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1;
immWrite(11,(end>>2)&0xff);
immWrite(12,(end>>10)&0xff);
immWrite(8,2);
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note);
chan[c.chan].freqChanged=true;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
} else {
immWrite(7,0x01); // reset
immWrite(9,0);
immWrite(10,0);
immWrite(11,0);
immWrite(12,0);
break;
}
} else {
chan[c.chan].sample=-1;
chan[c.chan].macroInit(NULL);
chan[c.chan].outVol=chan[c.chan].vol;
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
immWrite(7,0x01); // reset
immWrite(9,0);
immWrite(10,0);
immWrite(11,0);
immWrite(12,0);
break;
}
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1;
immWrite(11,(end>>2)&0xff);
immWrite(12,(end>>10)&0xff);
immWrite(8,2);
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
int freq=(65536.0*(double)s->rate)/(double)rate;
immWrite(16,freq&0xff);
immWrite(17,(freq>>8)&0xff);
}
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPL); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPL);
if (chan[c.chan].insChanged) { if (chan[c.chan].insChanged) {
@ -714,11 +863,19 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF:
if (c.chan==adpcmChan) {
immWrite(7,0x01); // reset
break;
}
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false; chan[c.chan].keyOn=false;
chan[c.chan].active=false; chan[c.chan].active=false;
break; break;
case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_NOTE_OFF_ENV:
if (c.chan==adpcmChan) {
immWrite(7,0x01); // reset
break;
}
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false; chan[c.chan].keyOn=false;
chan[c.chan].active=false; chan[c.chan].active=false;
@ -728,7 +885,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
chan[c.chan].std.release(); chan[c.chan].std.release();
break; break;
case DIV_CMD_VOLUME: { case DIV_CMD_VOLUME: {
if (pretendYMU) { if (pretendYMU && c.chan!=adpcmChan) {
c.value=pow(((double)c.value/127.0),0.5)*63.0; c.value=pow(((double)c.value/127.0),0.5)*63.0;
if (c.value<0) c.value=0; if (c.value<0) c.value=0;
if (c.value>63) c.value=63; if (c.value>63) c.value=63;
@ -737,6 +894,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) { if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value; chan[c.chan].outVol=c.value;
} }
if (c.chan==adpcmChan) { // ADPCM-B
immWrite(18,chan[c.chan].outVol);
break;
}
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
unsigned char slot=slots[i][c.chan]; unsigned char slot=slots[i][c.chan];
@ -757,7 +918,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_GET_VOLUME: { case DIV_CMD_GET_VOLUME: {
if (pretendYMU) { if (pretendYMU && c.chan!=adpcmChan) {
return pow(((double)chan[c.chan].vol/63.0),2.0)*127.0; return pow(((double)chan[c.chan].vol/63.0),2.0)*127.0;
} }
return chan[c.chan].vol; return chan[c.chan].vol;
@ -771,6 +932,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
if (oplType!=3) break; if (oplType!=3) break;
if (c.chan==adpcmChan) break;
if (c.value==0 && c.value2==0) { if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3; chan[c.chan].pan=3;
} else { } else {
@ -791,12 +953,15 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_PITCH: { case DIV_CMD_PITCH: {
if (c.chan==adpcmChan) {
if (!chan[c.chan].furnacePCM) break;
}
chan[c.chan].pitch=c.value; chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;
} }
case DIV_CMD_NOTE_PORTA: { case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_FREQUENCY(c.value2); int destFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value2)):(NOTE_FREQUENCY(c.value2));
int newFreq; int newFreq;
bool return2=false; bool return2=false;
if (destFreq>chan[c.chan].baseFreq) { if (destFreq>chan[c.chan].baseFreq) {
@ -827,13 +992,22 @@ int DivPlatformOPL::dispatch(DivCommand c) {
} }
break; break;
} }
case DIV_CMD_SAMPLE_BANK:
if (adpcmChan<0) break;
sampleBank=c.value;
if (sampleBank>(int)(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
}
iface.sampleBank=sampleBank;
break;
case DIV_CMD_LEGATO: { case DIV_CMD_LEGATO: {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value));
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;
} }
case DIV_CMD_FM_LFO: { case DIV_CMD_FM_LFO: {
if (c.chan==adpcmChan) break;
if (c.value&2) { if (c.value&2) {
dvb=c.value&1; dvb=c.value&1;
} else { } else {
@ -843,6 +1017,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_FB: { case DIV_CMD_FM_FB: {
if (c.chan==adpcmChan) break;
chan[c.chan].state.fb=c.value&7; chan[c.chan].state.fb=c.value&7;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (isMuted[c.chan]) { if (isMuted[c.chan]) {
@ -859,6 +1034,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_MULT: { case DIV_CMD_FM_MULT: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value>=ops) break; if (c.value>=ops) break;
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
@ -870,6 +1046,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_TL: { case DIV_CMD_FM_TL: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value>=ops) break; if (c.value>=ops) break;
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
@ -889,6 +1066,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_AR: { case DIV_CMD_FM_AR: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
@ -911,6 +1089,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_DR: { case DIV_CMD_FM_DR: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
@ -933,6 +1112,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_SL: { case DIV_CMD_FM_SL: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
@ -955,6 +1135,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_RR: { case DIV_CMD_FM_RR: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
@ -977,6 +1158,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_AM: { case DIV_CMD_FM_AM: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
@ -999,6 +1181,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_VIB: { case DIV_CMD_FM_VIB: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
@ -1021,6 +1204,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_SUS: { case DIV_CMD_FM_SUS: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
@ -1043,6 +1227,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_KSR: { case DIV_CMD_FM_KSR: {
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
for (int i=0; i<ops; i++) { for (int i=0; i<ops; i++) {
@ -1065,6 +1250,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_WS: { case DIV_CMD_FM_WS: {
if (c.chan==adpcmChan) break;
if (oplType<2) break; if (oplType<2) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
@ -1088,6 +1274,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_RS: { case DIV_CMD_FM_RS: {
if (c.chan==adpcmChan) break;
if (oplType<2) break; if (oplType<2) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) { if (c.value<0) {
@ -1143,12 +1330,14 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_FM_HARD_RESET: case DIV_CMD_FM_HARD_RESET:
if (c.chan==adpcmChan) break;
chan[c.chan].hardReset=c.value; chan[c.chan].hardReset=c.value;
break; break;
case DIV_ALWAYS_SET_VOLUME: case DIV_ALWAYS_SET_VOLUME:
return 0; return 0;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:
if (c.chan==adpcmChan) return 255;
if (pretendYMU) return 127; if (pretendYMU) return 127;
return 63; return 63;
break; break;
@ -1237,7 +1426,7 @@ unsigned char* DivPlatformOPL::getRegisterPool() {
} }
int DivPlatformOPL::getRegisterPoolSize() { int DivPlatformOPL::getRegisterPoolSize() {
return 512; return (oplType<3)?256:512;
} }
void DivPlatformOPL::reset() { void DivPlatformOPL::reset() {
@ -1273,6 +1462,21 @@ void DivPlatformOPL::reset() {
chan[i].outVol=0x3f; chan[i].outVol=0x3f;
} }
if (adpcmChan>=0) {
chan[adpcmChan]=DivPlatformOPL::Channel();
chan[adpcmChan].std.setEngine(parent);
chan[adpcmChan].vol=0xff;
chan[adpcmChan].outVol=0xff;
adpcmB->reset();
// volume
immWrite(18,0xff);
// ADPCM limit
immWrite(20,0xff);
immWrite(19,0xff);
}
if (oplType<3) for (int i=0; i<melodicChans; i++) { if (oplType<3) for (int i=0; i<melodicChans; i++) {
fm.channel[outChanMap[i]].muted=isMuted[i]; fm.channel[outChanMap[i]].muted=isMuted[i];
} }
@ -1285,6 +1489,7 @@ void DivPlatformOPL::reset() {
lastBusy=60; lastBusy=60;
lfoValue=8; lfoValue=8;
drumState=0; drumState=0;
sampleBank=0;
drumVol[0]=0; drumVol[0]=0;
drumVol[1]=0; drumVol[1]=0;
@ -1349,8 +1554,9 @@ void DivPlatformOPL::setYMFM(bool use) {
void DivPlatformOPL::setOPLType(int type, bool drums) { void DivPlatformOPL::setOPLType(int type, bool drums) {
pretendYMU=false; pretendYMU=false;
adpcmChan=-1;
switch (type) { switch (type) {
case 1: case 2: case 1: case 2: case 8950:
slotsNonDrums=slotsOPL2; slotsNonDrums=slotsOPL2;
slotsDrums=slotsOPL2Drums; slotsDrums=slotsOPL2Drums;
slots=drums?slotsDrums:slotsNonDrums; slots=drums?slotsDrums:slotsNonDrums;
@ -1360,6 +1566,9 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
chans=9; chans=9;
melodicChans=drums?6:9; melodicChans=drums?6:9;
totalChans=drums?11:9; totalChans=drums?11:9;
if (type==8950) {
adpcmChan=drums?11:9;
}
break; break;
case 3: case 759: case 3: case 759:
slotsNonDrums=slotsOPL3; slotsNonDrums=slotsOPL3;
@ -1371,11 +1580,16 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
chans=18; chans=18;
melodicChans=drums?15:18; melodicChans=drums?15:18;
totalChans=drums?20:18; totalChans=drums?20:18;
if (type==759) pretendYMU=true; if (type==759) {
pretendYMU=true;
adpcmChan=16;
}
break; break;
} }
if (type==759) { if (type==759) {
oplType=3; oplType=3;
} else if (type==8950) {
oplType=1;
} else { } else {
oplType=type; oplType=type;
} }
@ -1425,6 +1639,45 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
} }
} }
const void* DivPlatformOPL::getSampleMem(int index) {
return (index==0 && adpcmChan>=0) ? adpcmBMem : NULL;
}
size_t DivPlatformOPL::getSampleMemCapacity(int index) {
return (index==0 && adpcmChan>=0) ? 262144 : 0;
}
size_t DivPlatformOPL::getSampleMemUsage(int index) {
return (index==0 && adpcmChan>=0) ? adpcmBMemLen : 0;
}
void DivPlatformOPL::renderSamples() {
if (adpcmChan<0) return;
memset(adpcmBMem,0,getSampleMemCapacity(0));
size_t memPos=0;
for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i];
int paddedLen=(s->lengthB+255)&(~0xff);
if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) {
memPos=(memPos+0xfffff)&0xf00000;
}
if (memPos>=getSampleMemCapacity(0)) {
logW("out of ADPCM memory for sample %d!",i);
break;
}
if (memPos+paddedLen>=getSampleMemCapacity(0)) {
memcpy(adpcmBMem+memPos,s->dataB,getSampleMemCapacity(0)-memPos);
logW("out of ADPCM memory for sample %d!",i);
} else {
memcpy(adpcmBMem+memPos,s->dataB,paddedLen);
}
s->offB=memPos;
memPos+=paddedLen;
}
adpcmBMemLen=memPos+256;
}
int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
parent=p; parent=p;
dumpWrites=false; dumpWrites=false;
@ -1437,6 +1690,14 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f
} }
setFlags(flags); setFlags(flags);
if (adpcmChan>=0) {
adpcmBMem=new unsigned char[getSampleMemCapacity(0)];
adpcmBMemLen=0;
iface.adpcmBMem=adpcmBMem;
iface.sampleBank=0;
adpcmB=new ymfm::adpcm_b_engine(iface,2);
}
reset(); reset();
return totalChans; return totalChans;
} }
@ -1445,6 +1706,10 @@ void DivPlatformOPL::quit() {
for (int i=0; i<18; i++) { for (int i=0; i<18; i++) {
delete oscBuf[i]; delete oscBuf[i];
} }
if (adpcmChan>=0) {
delete adpcmB;
delete[] adpcmBMem;
}
} }
DivPlatformOPL::~DivPlatformOPL() { DivPlatformOPL::~DivPlatformOPL() {

View file

@ -23,6 +23,16 @@
#include "../macroInt.h" #include "../macroInt.h"
#include <queue> #include <queue>
#include "../../../extern/opl/opl3.h" #include "../../../extern/opl/opl3.h"
#include "sound/ymfm/ymfm_adpcm.h"
class DivOPLAInterface: public ymfm::ymfm_interface {
public:
unsigned char* adpcmBMem;
int sampleBank;
uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address);
void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data);
DivOPLAInterface(): adpcmBMem(NULL), sampleBank(0) {}
};
class DivPlatformOPL: public DivDispatch { class DivPlatformOPL: public DivDispatch {
protected: protected:
@ -30,8 +40,8 @@ class DivPlatformOPL: public DivDispatch {
DivInstrumentFM state; DivInstrumentFM state;
DivMacroInt std; DivMacroInt std;
unsigned char freqH, freqL; unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, note, ins; int freq, baseFreq, pitch, pitch2, note, ins, sample;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp, hardReset; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnacePCM, inPorta, fourOp, hardReset;
int vol, outVol; int vol, outVol;
unsigned char pan; unsigned char pan;
void macroInit(DivInstrument* which) { void macroInit(DivInstrument* which) {
@ -47,13 +57,14 @@ class DivPlatformOPL: public DivDispatch {
pitch2(0), pitch2(0),
note(0), note(0),
ins(-1), ins(-1),
sample(-1),
active(false), active(false),
insChanged(true), insChanged(true),
freqChanged(false), freqChanged(false),
keyOn(false), keyOn(false),
keyOff(false), keyOff(false),
portaPause(false), portaPause(false),
furnaceDac(false), furnacePCM(false),
inPorta(false), inPorta(false),
fourOp(false), fourOp(false),
hardReset(false), hardReset(false),
@ -73,13 +84,18 @@ class DivPlatformOPL: public DivDispatch {
}; };
std::queue<QueuedWrite> writes; std::queue<QueuedWrite> writes;
opl3_chip fm; opl3_chip fm;
unsigned char* adpcmBMem;
size_t adpcmBMemLen;
DivOPLAInterface iface;
ymfm::adpcm_b_engine* adpcmB;
const unsigned char** slotsNonDrums; const unsigned char** slotsNonDrums;
const unsigned char** slotsDrums; const unsigned char** slotsDrums;
const unsigned char** slots; const unsigned char** slots;
const unsigned short* chanMap; const unsigned short* chanMap;
const unsigned char* outChanMap; const unsigned char* outChanMap;
double chipFreqBase; double chipFreqBase;
int delay, oplType, chans, melodicChans, totalChans; int delay, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank;
unsigned char lastBusy; unsigned char lastBusy;
unsigned char drumState; unsigned char drumState;
unsigned char drumVol[5]; unsigned char drumVol[5];
@ -97,6 +113,7 @@ class DivPlatformOPL: public DivDispatch {
int octave(int freq); int octave(int freq);
int toFreq(int freq); int toFreq(int freq);
double NOTE_ADPCMB(int note);
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);
@ -127,6 +144,10 @@ class DivPlatformOPL: public DivDispatch {
void poke(unsigned int addr, unsigned short val); void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char* getEffectName(unsigned char effect); const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index);
void renderSamples();
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformOPL(); ~DivPlatformOPL();

View file

@ -0,0 +1,38 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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 "sound/ymfm/ymfm.h"
#include "opl.h"
#include "../engine.h"
uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t address) {
switch (type) {
case ymfm::ACCESS_ADPCM_B:
if (adpcmBMem==NULL) {
return 0;
}
return adpcmBMem[address&0xffffff];
default:
return 0;
}
return 0;
}
void DivOPLAInterface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
}

View file

@ -270,7 +270,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
if (chan[c.chan].pcm) { if (chan[c.chan].pcm) {
if (skipRegisterWrites) break; if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].dacSample=ins->amiga.initSample; chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1; chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);

View file

@ -390,7 +390,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].sample=ins->amiga.initSample; chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value); chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value);
} }

View file

@ -21,9 +21,9 @@
#include "../engine.h" #include "../engine.h"
#include <math.h> #include <math.h>
#define CHIP_DIVIDER 32 #define CHIP_DIVIDER 16
#define rWrite(a,v) {if(!skipRegisterWrites) {scc->scc_w(true,a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v); }} #define rWrite(a,v) {if (!skipRegisterWrites) {scc->scc_w(true,a,v); regPool[a]=v; if (dumpWrites) addWrite(a,v); }}
const char* regCheatSheetSCC[]={ const char* regCheatSheetSCC[]={
"Ch1_Wave", "00", "Ch1_Wave", "00",
@ -105,6 +105,11 @@ void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len)
void DivPlatformSCC::updateWave(int ch) { void DivPlatformSCC::updateWave(int ch) {
int dstCh=(!isPlus && ch>=4)?3:ch; int dstCh=(!isPlus && ch>=4)?3:ch;
if (ch==3) {
lastUpdated34=3;
} else if (ch==4) {
lastUpdated34=4;
}
for (int i=0; i<32; i++) { for (int i=0; i<32; i++) {
rWrite(dstCh*32+i,(unsigned char)chan[ch].ws.output[i]-128); rWrite(dstCh*32+i,(unsigned char)chan[ch].ws.output[i]-128);
} }
@ -153,11 +158,11 @@ void DivPlatformSCC::tick(bool sysTick) {
} }
} }
if (chan[i].freqChanged) { if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].pitch2; chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>4096) chan[i].freq=4096; if (chan[i].freq>4095) chan[i].freq=4095;
rWrite(regBase+0+i*2,(chan[i].freq-1)&0xff); rWrite(regBase+0+i*2,chan[i].freq&0xff);
rWrite(regBase+1+i*2,(chan[i].freq-1)>>8); rWrite(regBase+1+i*2,chan[i].freq>>8);
chan[i].freqChanged=false; chan[i].freqChanged=false;
} }
} }
@ -281,8 +286,15 @@ void DivPlatformSCC::forceIns() {
for (int i=0; i<5; i++) { for (int i=0; i<5; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
chan[i].freqChanged=true; chan[i].freqChanged=true;
if (isPlus || i<3) {
updateWave(i); updateWave(i);
} }
}
if (!isPlus) {
if (lastUpdated34>=3) {
updateWave(lastUpdated34);
}
}
} }
void* DivPlatformSCC::getChanState(int ch) { void* DivPlatformSCC::getChanState(int ch) {
@ -316,6 +328,7 @@ void DivPlatformSCC::reset() {
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);
} }
lastUpdated34=0;
} }
bool DivPlatformSCC::isStereo() { bool DivPlatformSCC::isStereo() {
@ -326,9 +339,11 @@ void DivPlatformSCC::notifyWaveChange(int wave) {
for (int i=0; i<5; i++) { for (int i=0; i<5; i++) {
if (chan[i].wave==wave) { if (chan[i].wave==wave) {
chan[i].ws.changeWave1(chan[i].wave); chan[i].ws.changeWave1(chan[i].wave);
if (chan[i].active) {
updateWave(i); updateWave(i);
} }
} }
}
} }
void DivPlatformSCC::notifyInsDeletion(void* ins) { void DivPlatformSCC::notifyInsDeletion(void* ins) {
@ -358,8 +373,8 @@ int DivPlatformSCC::init(DivEngine* p, int channels, int sugRate, unsigned int f
isMuted[i]=false; isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer; oscBuf[i]=new DivDispatchOscBuffer;
} }
chipClock=COLOR_NTSC; chipClock=COLOR_NTSC/2.0;
rate=chipClock/16; rate=chipClock/8;
for (int i=0; i<5; i++) { for (int i=0; i<5; i++) {
oscBuf[i]->rate=rate; oscBuf[i]->rate=rate;
} }

View file

@ -57,6 +57,7 @@ class DivPlatformSCC: public DivDispatch {
DivDispatchOscBuffer* oscBuf[5]; DivDispatchOscBuffer* oscBuf[5];
bool isMuted[5]; bool isMuted[5];
unsigned char writeOscBuf; unsigned char writeOscBuf;
int lastUpdated34;
scc_core* scc; scc_core* scc;
bool isPlus; bool isPlus;

View file

@ -162,7 +162,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
if (skipRegisterWrites) break; if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].pcm.sample=ins->amiga.initSample; chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) { if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
chan[c.chan].pcm.sample=-1; chan[c.chan].pcm.sample=-1;
if (dumpWrites) { if (dumpWrites) {

View file

@ -1021,10 +1021,8 @@ void ay8910_device::ay8910_write_reg(int r, int v)
m_tone[2].set_duty(m_regs[AY_CDUTY]); m_tone[2].set_duty(m_regs[AY_CDUTY]);
break; break;
case AY_NOISEAND: case AY_NOISEAND:
m_noise_and=m_regs[AY_NOISEAND];
break;
case AY_NOISEOR: case AY_NOISEOR:
m_noise_or=m_regs[AY_NOISEOR]; // No action required
break; break;
default: default:
m_regs[r] = 0; // reserved, set as 0 m_regs[r] = 0; // reserved, set as 0
@ -1063,8 +1061,8 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
for (int chan = 0; chan < NUM_CHANNELS; chan++) for (int chan = 0; chan < NUM_CHANNELS; chan++)
{ {
tone = &m_tone[chan]; tone = &m_tone[chan];
const int period = std::max<int>(1,tone->period); const int period = tone->period * (m_step_mul << 1);
tone->count += is_expanded_mode() ? 16 : (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; tone->count += is_expanded_mode() ? 32 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 1 : 2);
while (tone->count >= period) while (tone->count >= period)
{ {
tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f; tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f;
@ -1073,8 +1071,8 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
} }
} }
m_count_noise++; const int period_noise = (int)(noise_period()) * m_step_mul;
if (m_count_noise >= noise_period()) if ((++m_count_noise) >= period_noise)
{ {
/* toggle the prescaler output. Noise is no different to /* toggle the prescaler output. Noise is no different to
* channels. * channels.
@ -1082,39 +1080,27 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
m_count_noise = 0; m_count_noise = 0;
m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1); m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1);
if (!m_prescale_noise || is_expanded_mode()) // AY8930 noise generator rate is twice compares as compatibility mode if (is_expanded_mode()) // AY8930 noise generator rate is twice? compares as compatibility mode
{ {
if (is_expanded_mode()) {
// This is called "Noise value" on the docs, but is a counter whose period is determined by the LFSR. // This is called "Noise value" on the docs, but is a counter whose period is determined by the LFSR.
// Using AND/OR gates, specific periods can be "filtered" out. // Using AND/OR gates, specific periods can be "filtered" out.
// A square wave can be generated through this behavior, which can be used for crude AM pulse width modulation. // A square wave can be generated through this behavior, which can be used for crude AM pulse width modulation.
// The period of the noise is determined by this value. // The period of the noise is determined by this value.
// The least significant byte of the LFSR is bitwise ANDed with the AND mask, and then bitwise ORed with the OR mask. // The least significant byte of the LFSR is bitwise ANDed with the AND mask, and then bitwise ORed with the OR mask.
unsigned int noiseValuePeriod = ((m_rng & 0xFF & m_noise_and) | m_noise_or); if ((++m_noise_value) >= (((unsigned char)(m_rng) & noise_and()) | noise_or())) // Clock the noise value.
{
// Clock the noise value.
if (m_noise_value >= noiseValuePeriod) {
m_noise_value = 0; m_noise_value = 0;
// When everything is finally said and done, a 1bit latch is flipped. // When everything is finally said and done, a 1bit latch is flipped.
// This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel. // This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel.
m_noise_latch ^= 1; m_noise_out ^= 1;
// The 17-bit LFSR is updated, using an XOR across bits 0 and 2. noise_rng_tick();
unsigned int feedback = (m_rng & 1) ^ ((m_rng >> 2) & 1);
m_rng >>= 1;
m_rng |= (feedback << 16);
}
m_noise_value++;
} else {
/* The Random Number Generator of the 8910 is a 17-bit shift */
/* register. The input to the shift register is bit0 XOR bit3 */
/* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */
m_rng ^= (((m_rng & 1) ^ ((m_rng >> 3) & 1)) << 17);
m_rng >>= 1;
} }
} }
else if (!m_prescale_noise)
noise_rng_tick();
} }
for (int chan = 0; chan < NUM_CHANNELS; chan++) for (int chan = 0; chan < NUM_CHANNELS; chan++)
@ -1129,9 +1115,8 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
envelope = &m_envelope[chan]; envelope = &m_envelope[chan];
if (envelope->holding == 0) if (envelope->holding == 0)
{ {
const int period = envelope->period * m_step; const int period = envelope->period * m_env_step_mul;
envelope->count++; if ((++envelope->count) >= period)
if (envelope->count >= period)
{ {
envelope->count = 0; envelope->count = 0;
envelope->step--; envelope->step--;
@ -1263,20 +1248,14 @@ void ay8910_device::device_start()
} }
void ay8910_device::ay8910_reset_ym(bool ay8930) void ay8910_device::ay8910_reset_ym()
{ {
m_active = false; m_active = false;
m_register_latch = 0; m_register_latch = 0;
if (ay8930) { m_rng = (m_feature & PSG_HAS_EXPANDED_MODE) ? 0x1ffff : 1;
m_rng = 0x1ffff;
} else {
m_rng = 1;
}
m_mode = 0; // ay-3-8910 compatible mode m_mode = 0; // ay-3-8910 compatible mode
m_noise_and = 0xff;
m_noise_or = 0;
m_noise_value = 0; m_noise_value = 0;
m_noise_latch = 0; m_noise_out = 0;
for (int chan = 0; chan < NUM_CHANNELS; chan++) for (int chan = 0; chan < NUM_CHANNELS; chan++)
{ {
m_tone[chan].reset(); m_tone[chan].reset();
@ -1335,7 +1314,7 @@ void ay8910_device::ay8910_write_ym(int addr, unsigned char data)
unsigned char ay8910_device::ay8910_read_ym() unsigned char ay8910_device::ay8910_read_ym()
{ {
int r = m_register_latch + get_register_bank(); unsigned char r = m_register_latch + get_register_bank();
if (!m_active) return 0xff; // high impedance if (!m_active) return 0xff; // high impedance
@ -1380,7 +1359,7 @@ unsigned char ay8910_device::ay8910_read_ym()
void ay8910_device::device_reset() void ay8910_device::device_reset()
{ {
ay8910_reset_ym(chip_type == AY8930); ay8910_reset_ym();
} }
/************************************* /*************************************
@ -1452,7 +1431,7 @@ ay8910_device::ay8910_device(unsigned int clock)
} }
ay8910_device::ay8910_device(device_type type, unsigned int clock, ay8910_device::ay8910_device(device_type type, unsigned int clock,
psg_type_t psg_type, int streams, int ioports, int feature) psg_type_t psg_type, int streams, int ioports, int feature, bool clk_sel)
: chip_type(type), : chip_type(type),
m_type(psg_type), m_type(psg_type),
m_streams(streams), m_streams(streams),
@ -1461,19 +1440,18 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock,
m_register_latch(0), m_register_latch(0),
m_last_enable(0), m_last_enable(0),
m_prescale_noise(0), m_prescale_noise(0),
m_noise_value(0),
m_count_noise(0), m_count_noise(0),
m_rng(0), m_rng(0),
m_noise_and(0), m_noise_out(0),
m_noise_or(0),
m_noise_value(0),
m_noise_latch(0),
m_mode(0), m_mode(0),
m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f), m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f),
m_step( (feature & PSG_HAS_EXPANDED_MODE) || (psg_type == PSG_TYPE_AY) ? 2 : 1), m_step_mul( ((feature & PSG_HAS_INTERNAL_DIVIDER) || ((feature & PSG_PIN26_IS_CLKSEL) && clk_sel)) ? 2 : 1),
m_env_step_mul( ((feature & PSG_HAS_EXPANDED_MODE) || (psg_type == PSG_TYPE_AY)) ? (m_step_mul << 1) : m_step_mul),
m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0),
m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param),
m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env),
m_flags(AY8910_LEGACY_OUTPUT), m_flags(AY8910_LEGACY_OUTPUT | (((feature & PSG_PIN26_IS_CLKSEL) && clk_sel) ? YM2149_PIN26_LOW : 0)),
m_feature(feature) m_feature(feature)
{ {
memset(&m_regs,0,sizeof(m_regs)); memset(&m_regs,0,sizeof(m_regs));
@ -1485,16 +1463,16 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock,
m_res_load[0] = m_res_load[1] = m_res_load[2] = 1000; //Default values for resistor loads m_res_load[0] = m_res_load[1] = m_res_load[2] = 1000; //Default values for resistor loads
// TODO : measure ay8930 volume parameters (PSG_TYPE_YM for temporary 5 bit handling) // TODO : measure ay8930 volume parameters (PSG_TYPE_YM for temporary 5 bit handling)
set_type((m_feature & PSG_HAS_EXPANDED_MODE) ? PSG_TYPE_YM : psg_type); set_type((m_feature & PSG_HAS_EXPANDED_MODE) ? PSG_TYPE_YM : psg_type, clk_sel);
} }
void ay8910_device::set_type(psg_type_t psg_type) void ay8910_device::set_type(psg_type_t psg_type, bool clk_sel)
{ {
m_type = psg_type; m_type = psg_type;
if (psg_type == PSG_TYPE_AY) if (psg_type == PSG_TYPE_AY)
{ {
m_env_step_mask = 0x0f; m_env_step_mask = 0x0f;
m_step = 2; m_env_step_mul = is_clock_divided() ? 4 : 2;
m_zero_is_off = 1; m_zero_is_off = 1;
m_par = &ay8910_param; m_par = &ay8910_param;
m_par_env = &ay8910_param; m_par_env = &ay8910_param;
@ -1502,11 +1480,15 @@ void ay8910_device::set_type(psg_type_t psg_type)
else else
{ {
m_env_step_mask = 0x1f; m_env_step_mask = 0x1f;
m_step = (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; m_env_step_mul = is_clock_divided() ? 2 : 1;
m_zero_is_off = 0; m_zero_is_off = 0;
m_par = &ym2149_param; m_par = &ym2149_param;
m_par_env = &ym2149_param_env; m_par_env = &ym2149_param_env;
} }
if (m_feature & PSG_HAS_EXPANDED_MODE)
m_env_step_mul <<= 1;
set_clock_sel(clk_sel);
} }
@ -1535,24 +1517,24 @@ ay8914_device::ay8914_device(unsigned int clock)
ay8930_device::ay8930_device(unsigned int clock) ay8930_device::ay8930_device(unsigned int clock, bool clk_sel)
: ay8910_device(AY8930, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL | PSG_HAS_EXPANDED_MODE) : ay8910_device(AY8930, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL | PSG_HAS_EXPANDED_MODE, clk_sel)
{ {
} }
ym2149_device::ym2149_device(unsigned int clock) ym2149_device::ym2149_device(unsigned int clock, bool clk_sel)
: ay8910_device(YM2149, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL) : ay8910_device(YM2149, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL, clk_sel)
{ {
} }
ym3439_device::ym3439_device(unsigned int clock) ym3439_device::ym3439_device(unsigned int clock, bool clk_sel)
: ay8910_device(YM3439, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL) : ay8910_device(YM3439, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL, clk_sel)
{ {
} }

View file

@ -3,6 +3,10 @@
#ifndef MAME_SOUND_AY8910_H #ifndef MAME_SOUND_AY8910_H
#define MAME_SOUND_AY8910_H #define MAME_SOUND_AY8910_H
#pragma once
#include <algorithm>
#define ALL_8910_CHANNELS -1 #define ALL_8910_CHANNELS -1
/* Internal resistance at Volume level 7. */ /* Internal resistance at Volume level 7. */
@ -89,7 +93,8 @@ public:
// configuration helpers // configuration helpers
void set_flags(int flags) { m_flags = flags; } void set_flags(int flags) { m_flags = flags; }
void set_psg_type(psg_type_t psg_type) { set_type(psg_type); } void set_psg_type(psg_type_t psg_type) { set_type(psg_type, m_flags & YM2149_PIN26_LOW); }
void set_psg_type(psg_type_t psg_type, bool clk_sel) { set_type(psg_type, clk_sel); }
void set_resistors_load(int res_load0, int res_load1, int res_load2) { m_res_load[0] = res_load0; m_res_load[1] = res_load1; m_res_load[2] = res_load2; } void set_resistors_load(int res_load0, int res_load1, int res_load2) { m_res_load[0] = res_load0; m_res_load[1] = res_load1; m_res_load[2] = res_load2; }
unsigned char data_r() { return ay8910_read_ym(); } unsigned char data_r() { return ay8910_read_ym(); }
@ -97,7 +102,24 @@ public:
void data_w(unsigned char data); void data_w(unsigned char data);
// /RES // /RES
void reset_w(unsigned char data = 0) { ay8910_reset_ym(chip_type == AY8930); } void reset_w(unsigned char data = 0) { ay8910_reset_ym(); }
// Clock select pin
void set_clock_sel(bool clk_sel)
{
if (m_feature & PSG_PIN26_IS_CLKSEL)
{
if (clk_sel)
m_flags |= YM2149_PIN26_LOW;
else
m_flags &= ~YM2149_PIN26_LOW;
m_step_mul = is_clock_divided() ? 2 : 1;
m_env_step_mul = (!(m_feature & PSG_HAS_EXPANDED_MODE)) && (m_type == PSG_TYPE_AY) ? (m_step_mul << 1) : m_step_mul;
if (m_feature & PSG_HAS_EXPANDED_MODE)
m_env_step_mul <<= 1;
}
}
// use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address' // use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address'
void data_address_w(int offset, unsigned char data) { ay8910_write_ym(~offset & 1, data); } // note that directly connecting BC1 to A0 puts data on 0 and address on 1 void data_address_w(int offset, unsigned char data) { ay8910_write_ym(~offset & 1, data); } // note that directly connecting BC1 to A0 puts data on 0 and address on 1
@ -127,7 +149,7 @@ public:
// internal interface for PSG component of YM device // internal interface for PSG component of YM device
// FIXME: these should be private, but vector06 accesses them directly // FIXME: these should be private, but vector06 accesses them directly
ay8910_device(device_type type, unsigned int clock, psg_type_t psg_type, int streams, int ioports, int feature = PSG_DEFAULT); ay8910_device(device_type type, unsigned int clock, psg_type_t psg_type, int streams, int ioports, int feature = PSG_DEFAULT, bool clk_sel = false);
// device-level overrides // device-level overrides
void device_start(); void device_start();
@ -138,7 +160,7 @@ public:
void ay8910_write_ym(int addr, unsigned char data); void ay8910_write_ym(int addr, unsigned char data);
unsigned char ay8910_read_ym(); unsigned char ay8910_read_ym();
void ay8910_reset_ym(bool ay8930); void ay8910_reset_ym();
private: private:
static constexpr int NUM_CHANNELS = 3; static constexpr int NUM_CHANNELS = 3;
@ -189,7 +211,7 @@ private:
void reset() void reset()
{ {
period = 0; period = 1;
volume = 0; volume = 0;
duty = 0; duty = 0;
count = 0; count = 0;
@ -199,7 +221,7 @@ private:
void set_period(unsigned char fine, unsigned char coarse) void set_period(unsigned char fine, unsigned char coarse)
{ {
period = fine | (coarse << 8); period = std::max<unsigned int>(1, fine | (coarse << 8));
} }
void set_volume(unsigned char val) void set_volume(unsigned char val)
@ -223,7 +245,7 @@ private:
void reset() void reset()
{ {
period = 0; period = 1;
count = 0; count = 0;
step = 0; step = 0;
volume = 0; volume = 0;
@ -235,7 +257,7 @@ private:
void set_period(unsigned char fine, unsigned char coarse) void set_period(unsigned char fine, unsigned char coarse)
{ {
period = fine | (coarse << 8); period = std::max<unsigned int>(1, fine | (coarse << 8));
} }
void set_shape(unsigned char shape, unsigned char mask) void set_shape(unsigned char shape, unsigned char mask)
@ -258,6 +280,18 @@ private:
} }
}; };
inline void noise_rng_tick()
{
// The Random Number Generator of the 8910 is a 17-bit shift
// register. The input to the shift register is bit0 XOR bit3
// (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips.
if (m_feature & PSG_HAS_EXPANDED_MODE) // AY8930 LFSR algorithm is slightly different, verified from manual
m_rng = (m_rng >> 1) | ((BIT(m_rng, 0) ^ BIT(m_rng, 2)) << 16);
else
m_rng = (m_rng >> 1) | ((BIT(m_rng, 0) ^ BIT(m_rng, 3)) << 16);
}
// inlines // inlines
inline bool tone_enable(int chan) { return BIT(m_regs[AY_ENABLE], chan); } inline bool tone_enable(int chan) { return BIT(m_regs[AY_ENABLE], chan); }
inline unsigned char tone_volume(tone_t *tone) { return tone->volume & (is_expanded_mode() ? 0x1f : 0x0f); } inline unsigned char tone_volume(tone_t *tone) { return tone->volume & (is_expanded_mode() ? 0x1f : 0x0f); }
@ -266,14 +300,19 @@ private:
inline unsigned char get_envelope_chan(int chan) { return is_expanded_mode() ? chan : 0; } inline unsigned char get_envelope_chan(int chan) { return is_expanded_mode() ? chan : 0; }
inline bool noise_enable(int chan) { return BIT(m_regs[AY_ENABLE], 3 + chan); } inline bool noise_enable(int chan) { return BIT(m_regs[AY_ENABLE], 3 + chan); }
inline unsigned char noise_period() { return is_expanded_mode() ? m_regs[AY_NOISEPER] & 0xff : m_regs[AY_NOISEPER] & 0x1f; } inline unsigned char noise_period() { return std::max<unsigned char>(1, is_expanded_mode() ? (m_regs[AY_NOISEPER] & 0xff) : (m_regs[AY_NOISEPER] & 0x1f)); }
inline unsigned char noise_output() { return is_expanded_mode() ? m_noise_latch & 1 : m_rng & 1; } inline unsigned char noise_output() { return is_expanded_mode() ? m_noise_out & 1 : m_rng & 1; }
inline bool is_expanded_mode() { return ((m_feature & PSG_HAS_EXPANDED_MODE) && ((m_mode & 0xe) == 0xa)); } inline bool is_expanded_mode() { return ((m_feature & PSG_HAS_EXPANDED_MODE) && ((m_mode & 0xe) == 0xa)); }
inline unsigned char get_register_bank() { return is_expanded_mode() ? (m_mode & 0x1) << 4 : 0; } inline unsigned char get_register_bank() { return is_expanded_mode() ? (m_mode & 0x1) << 4 : 0; }
inline unsigned char noise_and() { return m_regs[AY_NOISEAND] & 0xff; }
inline unsigned char noise_or() { return m_regs[AY_NOISEOR] & 0xff; }
inline bool is_clock_divided() { return ((m_feature & PSG_HAS_INTERNAL_DIVIDER) || ((m_feature & PSG_PIN26_IS_CLKSEL) && (m_flags & YM2149_PIN26_LOW))); }
// internal helpers // internal helpers
void set_type(psg_type_t psg_type); void set_type(psg_type_t psg_type, bool clk_sel);
inline float mix_3D(); inline float mix_3D();
void ay8910_write_reg(int r, int v); void ay8910_write_reg(int r, int v);
void build_mixer_table(); void build_mixer_table();
@ -284,22 +323,21 @@ private:
int m_ready; int m_ready;
//sound_stream *m_channel; //sound_stream *m_channel;
bool m_active; bool m_active;
int m_register_latch; unsigned char m_register_latch;
unsigned char m_regs[16 * 2]; unsigned char m_regs[16 * 2];
int m_last_enable; int m_last_enable;
tone_t m_tone[NUM_CHANNELS]; tone_t m_tone[NUM_CHANNELS];
envelope_t m_envelope[NUM_CHANNELS]; envelope_t m_envelope[NUM_CHANNELS];
unsigned char m_prescale_noise; unsigned char m_prescale_noise;
int m_count_noise; signed short m_noise_value;
int m_rng; signed short m_count_noise;
unsigned int m_noise_and; unsigned int m_rng;
unsigned int m_noise_or; unsigned char m_noise_out;
unsigned int m_noise_value;
unsigned int m_noise_latch;
unsigned char m_mode; unsigned char m_mode;
unsigned char m_env_step_mask; unsigned char m_env_step_mask;
/* init parameters ... */ /* init parameters ... */
int m_step; int m_step_mul;
int m_env_step_mul;
int m_zero_is_off; int m_zero_is_off;
unsigned char m_vol_enabled[NUM_CHANNELS]; unsigned char m_vol_enabled[NUM_CHANNELS];
const ay_ym_param *m_par; const ay_ym_param *m_par;
@ -337,19 +375,19 @@ public:
class ay8930_device : public ay8910_device class ay8930_device : public ay8910_device
{ {
public: public:
ay8930_device(unsigned int clock); ay8930_device(unsigned int clock, bool clk_sel = false);
}; };
class ym2149_device : public ay8910_device class ym2149_device : public ay8910_device
{ {
public: public:
ym2149_device(unsigned int clock); ym2149_device(unsigned int clock, bool clk_sel = false);
}; };
class ym3439_device : public ay8910_device class ym3439_device : public ay8910_device
{ {
public: public:
ym3439_device(unsigned int clock); ym3439_device(unsigned int clock, bool clk_sel = false);
}; };
class ymz284_device : public ay8910_device class ymz284_device : public ay8910_device

View file

@ -0,0 +1,543 @@
/*
This data is derived from the chip's output - internal ROM can't be read.
It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal.
*/
const unsigned char YM2608_ADPCM_ROM[0x2000] = {
/* Source: 01BD.ROM */
/* Length: 448 / 0x000001C0 */
0x88,0x08,0x08,0x08,0x00,0x88,0x16,0x76,0x99,0xB8,0x22,0x3A,0x84,0x3C,0xB1,0x54,
0x10,0xA9,0x98,0x32,0x80,0x33,0x9A,0xA7,0x4A,0xB4,0x58,0xBC,0x15,0x29,0x8A,0x97,
0x9B,0x44,0xAC,0x80,0x12,0xDE,0x13,0x1B,0xC0,0x58,0xC8,0x11,0x0A,0xA2,0x1A,0xA0,
0x00,0x98,0x0B,0x93,0x9E,0x92,0x0A,0x88,0xBE,0x14,0x1B,0x98,0x08,0xA1,0x4A,0xC1,
0x30,0xD9,0x33,0x98,0x10,0x89,0x17,0x1A,0x82,0x29,0x37,0x0C,0x83,0x50,0x9A,0x24,
0x1A,0x83,0x10,0x23,0x19,0xB3,0x72,0x8A,0x16,0x10,0x0A,0x93,0x70,0x99,0x23,0x99,
0x02,0x20,0x91,0x18,0x02,0x41,0xAB,0x24,0x18,0x81,0x99,0x4A,0xE8,0x28,0x9A,0x99,
0xA1,0x2F,0xA8,0x9D,0x90,0x08,0xCC,0xA3,0x1D,0xCA,0x82,0x0B,0xD8,0x08,0xB9,0x09,
0xBC,0xB8,0x00,0xBE,0x90,0x1B,0xCA,0x00,0x9B,0x8A,0xA8,0x91,0x0F,0xB3,0x3D,0xB8,
0x31,0x0B,0xA5,0x0A,0x11,0xA1,0x48,0x92,0x10,0x50,0x91,0x30,0x23,0x09,0x37,0x39,
0xA2,0x72,0x89,0x92,0x30,0x83,0x1C,0x96,0x28,0xB9,0x24,0x8C,0xA1,0x31,0xAD,0xA9,
0x13,0x9C,0xBA,0xA8,0x0B,0xBF,0xB8,0x9B,0xCA,0x88,0xDB,0xB8,0x19,0xFC,0x92,0x0A,
0xBA,0x89,0xAB,0xB8,0xAB,0xD8,0x08,0xAD,0xBA,0x33,0x9D,0xAA,0x83,0x3A,0xC0,0x40,
0xB9,0x15,0x39,0xA2,0x52,0x89,0x02,0x63,0x88,0x13,0x23,0x03,0x52,0x02,0x54,0x00,
0x11,0x23,0x23,0x35,0x20,0x01,0x44,0x41,0x80,0x24,0x40,0xA9,0x45,0x19,0x81,0x12,
0x81,0x02,0x11,0x21,0x19,0x02,0x61,0x8A,0x13,0x3A,0x10,0x12,0x23,0x8B,0x37,0x18,
0x91,0x24,0x10,0x81,0x34,0x20,0x05,0x32,0x82,0x53,0x20,0x14,0x33,0x31,0x34,0x52,
0x00,0x43,0x32,0x13,0x52,0x22,0x13,0x52,0x11,0x43,0x11,0x32,0x32,0x32,0x22,0x02,
0x13,0x12,0x89,0x22,0x19,0x81,0x81,0x08,0xA8,0x08,0x8B,0x90,0x1B,0xBA,0x8A,0x9B,
0xB9,0x89,0xCA,0xB9,0xAB,0xCA,0x9B,0xCA,0xB9,0xAB,0xDA,0x99,0xAC,0xBB,0x9B,0xAC,
0xAA,0xBA,0xAC,0xAB,0x9A,0xAA,0xAA,0xBA,0xB8,0xA9,0xBA,0x99,0xA9,0x9A,0xA0,0x8A,
0xA9,0x08,0x8A,0xA9,0x00,0x99,0x89,0x88,0x98,0x08,0x99,0x00,0x89,0x80,0x08,0x98,
0x00,0x88,0x88,0x80,0x90,0x80,0x90,0x80,0x81,0x99,0x08,0x88,0x99,0x09,0x00,0x1A,
0xA8,0x10,0x9A,0x88,0x08,0x0A,0x8A,0x89,0x99,0xA8,0x98,0xA9,0x99,0x99,0xA9,0x99,
0xAA,0x8A,0xAA,0x9B,0x8A,0x9A,0xA9,0x9A,0xBA,0x99,0x9A,0xAA,0x99,0x89,0xA9,0x99,
0x98,0x9A,0x98,0x88,0x09,0x89,0x09,0x08,0x08,0x09,0x18,0x18,0x00,0x12,0x00,0x11,
0x11,0x11,0x12,0x12,0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22,0x32,0x31,0x32,0x31,
0x32,0x32,0x21,0x31,0x21,0x32,0x21,0x12,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
/* Source: 02SD.ROM */
/* Length: 640 / 0x00000280 */
0x0A,0xDC,0x14,0x0B,0xBA,0xBC,0x01,0x0F,0xF5,0x2F,0x87,0x19,0xC9,0x24,0x1B,0xA1,
0x31,0x99,0x90,0x32,0x32,0xFE,0x83,0x48,0xA8,0xA9,0x23,0x19,0xBC,0x91,0x02,0x41,
0xDE,0x81,0x28,0xA8,0x0A,0xB1,0x72,0xDA,0x23,0xBC,0x04,0x19,0xB8,0x21,0x8A,0x03,
0x29,0xBA,0x14,0x21,0x0B,0xC0,0x43,0x08,0x91,0x50,0x93,0x0F,0x86,0x1A,0x91,0x18,
0x21,0xCB,0x27,0x0A,0xA1,0x42,0x8C,0xA9,0x21,0x10,0x08,0xAB,0x94,0x2A,0xDA,0x02,
0x8B,0x91,0x09,0x98,0xAE,0x80,0xA9,0x02,0x0A,0xE9,0x21,0xBB,0x15,0x20,0xBE,0x92,
0x42,0x09,0xA9,0x11,0x34,0x08,0x12,0x0A,0x27,0x29,0xA1,0x52,0x12,0x8E,0x92,0x28,
0x92,0x2B,0xD1,0x23,0xBF,0x81,0x10,0x99,0xA8,0x0A,0xC4,0x3B,0xB9,0xB0,0x00,0x62,
0xCF,0x92,0x29,0x92,0x2B,0xB1,0x1C,0xB2,0x72,0xAA,0x88,0x11,0x18,0x80,0x13,0x9E,
0x03,0x18,0xB0,0x60,0xA1,0x28,0x88,0x08,0x04,0x10,0x8F,0x96,0x19,0x90,0x01,0x09,
0xC8,0x50,0x91,0x8A,0x01,0xAB,0x03,0x50,0xBA,0x9D,0x93,0x68,0xBA,0x80,0x22,0xCB,
0x41,0xBC,0x92,0x60,0xB9,0x1A,0x95,0x4A,0xC8,0x20,0x88,0x33,0xAC,0x92,0x38,0x83,
0x09,0x80,0x16,0x09,0x29,0xD0,0x54,0x8C,0xA2,0x28,0x91,0x89,0x93,0x60,0xCD,0x85,
0x1B,0xA1,0x49,0x90,0x8A,0x80,0x34,0x0C,0xC9,0x14,0x19,0x98,0xA0,0x40,0xA9,0x21,
0xD9,0x34,0x0A,0xA9,0x10,0x23,0xCB,0x25,0xAA,0x25,0x9B,0x13,0xCD,0x16,0x09,0xA0,
0x80,0x01,0x19,0x90,0x88,0x21,0xAC,0x33,0x8B,0xD8,0x27,0x3B,0xB8,0x81,0x31,0x80,
0xAF,0x97,0x0A,0x82,0x0A,0xA0,0x21,0x89,0x8A,0xA2,0x32,0x8D,0xBB,0x87,0x19,0x21,
0xC9,0xBC,0x45,0x09,0x90,0x09,0xA1,0x24,0x1A,0xD0,0x10,0x08,0x11,0xA9,0x21,0xE8,
0x60,0xA9,0x14,0x0C,0xD1,0x32,0xAB,0x04,0x0C,0x81,0x90,0x29,0x83,0x9B,0x01,0x8F,
0x97,0x0B,0x82,0x18,0x88,0xBA,0x06,0x39,0xC8,0x23,0xBC,0x04,0x09,0x92,0x08,0x1A,
0xBB,0x74,0x8C,0x81,0x18,0x81,0x9D,0x83,0x41,0xCD,0x81,0x40,0x9A,0x90,0x10,0x12,
0x9C,0xA1,0x68,0xD8,0x33,0x9C,0x91,0x01,0x12,0xBE,0x02,0x09,0x12,0x99,0x9A,0x36,
0x0A,0xB0,0x30,0x88,0xA3,0x2D,0x12,0xBC,0x03,0x3A,0x11,0xBD,0x08,0xC8,0x62,0x80,
0x8B,0xD8,0x23,0x38,0xF9,0x12,0x08,0x99,0x91,0x21,0x99,0x85,0x2F,0xB2,0x30,0x90,
0x88,0xD9,0x53,0xAC,0x82,0x19,0x91,0x20,0xCC,0x96,0x29,0xC9,0x24,0x89,0x80,0x99,
0x12,0x08,0x18,0x88,0x99,0x23,0xAB,0x73,0xCB,0x33,0x9F,0x04,0x2B,0xB1,0x08,0x03,
0x1B,0xC9,0x21,0x32,0xFA,0x33,0xDB,0x02,0x33,0xAE,0xB9,0x54,0x8B,0xA1,0x20,0x89,
0x90,0x11,0x88,0x09,0x98,0x23,0xBE,0x37,0x8D,0x81,0x20,0xAA,0x34,0xBB,0x13,0x18,
0xB9,0x40,0xB1,0x18,0x83,0x8E,0xB2,0x72,0xBC,0x82,0x30,0xA9,0x9A,0x24,0x8B,0x27,
0x0E,0x91,0x20,0x90,0x08,0xB0,0x32,0xB9,0x21,0xB0,0xAC,0x45,0x9A,0xA1,0x50,0xA9,
0x80,0x0A,0x26,0x9B,0x11,0xBB,0x23,0x71,0xCB,0x12,0x10,0xB8,0x40,0xA9,0xA5,0x39,
0xC0,0x30,0xB2,0x20,0xAA,0xBA,0x76,0x1C,0xC1,0x48,0x98,0x80,0x18,0x81,0xAA,0x23,
0x9C,0xA2,0x32,0xAC,0x9A,0x43,0x9C,0x12,0xAD,0x82,0x72,0xBC,0x00,0x82,0x39,0xD1,
0x3A,0xB8,0x35,0x9B,0x10,0x40,0xF9,0x22,0x0A,0xC0,0x51,0xB9,0x82,0x18,0x98,0xA3,
0x79,0xD0,0x20,0x88,0x09,0x01,0x99,0x82,0x11,0x38,0xFC,0x33,0x09,0xC8,0x40,0xA9,
0x11,0x29,0xAA,0x94,0x3A,0xC2,0x4A,0xC0,0x89,0x52,0xBC,0x11,0x08,0x09,0xB8,0x71,
0xA9,0x08,0xA8,0x62,0x8D,0x92,0x10,0x00,0x9E,0x94,0x38,0xBA,0x13,0x88,0x90,0x4A,
0xE2,0x30,0xBA,0x02,0x00,0x19,0xD9,0x62,0xBB,0x04,0x0B,0xA3,0x68,0xB9,0x21,0x88,
0x9D,0x04,0x10,0x8C,0xC8,0x62,0x99,0xAA,0x24,0x1A,0x80,0x9A,0x14,0x9B,0x26,0x8C,
0x92,0x30,0xB9,0x09,0xA3,0x71,0xBB,0x10,0x19,0x82,0x39,0xDB,0x02,0x44,0x9F,0x10,
/* Source: 04TOP.ROM */
/* Length: 5952 / 0x00001740 */
0x07,0xFF,0x7C,0x3C,0x31,0xC6,0xC4,0xBB,0x7F,0x7F,0x7B,0x82,0x8A,0x4D,0x5F,0x7C,
0x3E,0x44,0xD2,0xB3,0xA0,0x19,0x1B,0x6C,0x81,0x28,0xC4,0xA1,0x1C,0x4B,0x18,0x00,
0x2A,0xA2,0x0A,0x7C,0x2A,0x00,0x01,0x89,0x98,0x48,0x8A,0x3C,0x28,0x2A,0x5B,0x3E,
0x3A,0x1A,0x3B,0x3D,0x4B,0x3B,0x4A,0x08,0x2A,0x1A,0x2C,0x4A,0x3B,0x82,0x99,0x3C,
0x5D,0x29,0x2B,0x39,0x0B,0x23,0xAB,0x1A,0x4C,0x79,0xA3,0x01,0xC1,0x2A,0x0A,0x38,
0xA7,0xB9,0x12,0x1F,0x29,0x08,0x82,0xA1,0x08,0xA9,0x42,0xAA,0x95,0xB3,0x90,0x81,
0x09,0xD4,0x1A,0x80,0x1B,0x07,0xB8,0x12,0x8E,0x49,0x81,0x92,0xD3,0x90,0xA1,0x2A,
0x02,0xE1,0xA3,0x99,0x02,0xB3,0x94,0xB3,0xB0,0xF4,0x98,0x93,0x90,0x13,0xE1,0x81,
0x99,0x38,0x91,0xA6,0xD3,0x99,0x94,0xC1,0x83,0xB1,0x92,0x98,0x49,0xC4,0xB2,0xA4,
0xA3,0xD0,0x1A,0x30,0xBA,0x59,0x02,0xD4,0xA0,0xA4,0xA2,0x8A,0x01,0x00,0xB7,0xA8,
0x18,0x2A,0x2B,0x1E,0x23,0xC8,0x1A,0x00,0x39,0xA0,0x18,0x92,0x4F,0x2D,0x5A,0x10,
0x89,0x81,0x2A,0x8B,0x6A,0x02,0x09,0xB3,0x8D,0x48,0x1B,0x80,0x19,0x34,0xF8,0x29,
0x0A,0x7B,0x2A,0x28,0x81,0x0C,0x02,0x1E,0x29,0x09,0x12,0xC2,0x94,0xE1,0x18,0x98,
0x02,0xC4,0x89,0x91,0x1A,0x20,0xA9,0x02,0x1B,0x48,0x8E,0x20,0x88,0x2D,0x08,0x59,
0x1B,0x02,0xA3,0xB1,0x8A,0x1E,0x58,0x80,0xC2,0xB6,0x88,0x91,0x88,0x11,0xA1,0xA3,
0xE2,0x01,0xB0,0x19,0x11,0x09,0xF4,0x88,0x09,0x88,0x19,0x89,0x12,0xF1,0x2A,0x28,
0x8C,0x25,0x99,0xA4,0x98,0x39,0xA1,0x00,0xD0,0x58,0xAA,0x59,0x01,0x0C,0x00,0x2B,
0x00,0x08,0x89,0x6B,0x69,0x90,0x01,0x90,0x98,0x12,0xB3,0xF3,0xA0,0x89,0x02,0x3B,
0x0C,0x50,0xA9,0x4E,0x6B,0x19,0x28,0x09,0xA2,0x08,0x2F,0x20,0x88,0x92,0x8A,0x11,
0xC4,0x93,0xF1,0x18,0x88,0x11,0xF2,0x80,0x92,0xA8,0x02,0xA8,0xB7,0xB3,0xA3,0xA0,
0x88,0x1A,0x40,0xE2,0x91,0x19,0x88,0x18,0x91,0x83,0xC1,0xB5,0x92,0xA9,0xC6,0x90,
0x01,0xC2,0x81,0x98,0x03,0xF0,0x00,0x2C,0x2A,0x92,0x2C,0x83,0x1F,0x3A,0x29,0x00,
0xB8,0x70,0xAB,0x69,0x18,0x89,0x10,0x0D,0x12,0x0B,0x88,0x4A,0x3A,0x9B,0x70,0xA8,
0x28,0x2F,0x2A,0x3A,0x1B,0x85,0x88,0x8B,0x6A,0x29,0x00,0x91,0x91,0x1B,0x7C,0x29,
0x01,0x88,0x90,0x19,0x2B,0x2B,0x00,0x39,0xA8,0x5E,0x21,0x89,0x91,0x09,0x3A,0x6F,
0x2A,0x18,0x18,0x8B,0x50,0x89,0x2B,0x19,0x49,0x88,0x29,0xF5,0x89,0x08,0x09,0x12,
0xAA,0x15,0xB0,0x82,0xAC,0x38,0x00,0x3F,0x81,0x10,0xB0,0x49,0xA2,0x81,0x3A,0xC8,
0x87,0x90,0xC4,0xA3,0x99,0x19,0x83,0xE1,0x84,0xE2,0xA2,0x90,0x80,0x93,0xB5,0xC4,
0xB3,0xA1,0x0A,0x18,0x92,0xC4,0xA0,0x93,0x0C,0x3A,0x18,0x01,0x1E,0x20,0xB1,0x82,
0x8C,0x03,0xB5,0x2E,0x82,0x19,0xB2,0x1B,0x1B,0x6B,0x4C,0x19,0x12,0x8B,0x5A,0x11,
0x0C,0x3A,0x2C,0x18,0x3D,0x08,0x2A,0x5C,0x18,0x00,0x88,0x3D,0x29,0x80,0x2A,0x09,
0x00,0x7A,0x0A,0x10,0x0B,0x69,0x98,0x10,0x81,0x3F,0x00,0x18,0x19,0x91,0xB7,0x9A,
0x28,0x8A,0x48,0x92,0xF3,0xA2,0x88,0x98,0x87,0xA1,0x88,0x80,0x81,0x95,0xD1,0xA3,
0x1B,0x1C,0x39,0x10,0xA1,0x2A,0x0B,0x7A,0x4B,0x80,0x13,0xC1,0xD1,0x2B,0x2A,0x85,
0xB2,0xA2,0x93,0xB2,0xD3,0x80,0xD1,0x18,0x08,0x08,0xB7,0x98,0x81,0x3F,0x01,0x88,
0x01,0xE2,0x00,0x9A,0x59,0x08,0x10,0xC3,0x99,0x84,0xA9,0xA5,0x91,0x91,0x91,0x80,
0xB5,0x94,0xC0,0x01,0x98,0x09,0x84,0xB0,0x80,0x7A,0x08,0x18,0x90,0xA8,0x6A,0x1C,
0x39,0x2A,0xB7,0x98,0x19,0x10,0x2A,0xA1,0x10,0xBD,0x39,0x18,0x2D,0x39,0x3F,0x10,
0x3F,0x01,0x09,0x19,0x0A,0x38,0x8C,0x40,0xB3,0xB4,0x93,0xAD,0x20,0x2B,0xD4,0x81,
0xC3,0xB0,0x39,0xA0,0x23,0xD8,0x04,0xB1,0x9B,0xA7,0x1A,0x92,0x08,0xA5,0x88,0x81,
0xE2,0x01,0xB8,0x01,0x81,0xC1,0xC7,0x90,0x92,0x80,0xA1,0x97,0xA0,0xA2,0x82,0xB8,
0x18,0x00,0x9C,0x78,0x98,0x83,0x0B,0x0B,0x32,0x7D,0x19,0x10,0xA1,0x19,0x09,0x0A,
0x78,0xA8,0x10,0x1B,0x29,0x29,0x1A,0x14,0x2F,0x88,0x4A,0x1B,0x10,0x10,0xAB,0x79,
0x0D,0x49,0x18,0xA0,0x02,0x1F,0x19,0x3A,0x2B,0x11,0x8A,0x88,0x79,0x8A,0x20,0x49,
0x9B,0x58,0x0B,0x28,0x18,0xA9,0x3A,0x7D,0x00,0x29,0x88,0x82,0x3D,0x1A,0x38,0xBA,
0x15,0x09,0xAA,0x51,0x8B,0x83,0x3C,0x8A,0x58,0x1B,0xB5,0x01,0xBB,0x50,0x19,0x99,
0x24,0xCA,0x21,0x1B,0xA2,0x87,0xA8,0xB1,0x68,0xA1,0xA6,0xA2,0xA8,0x29,0x8B,0x24,
0xB4,0xE2,0x92,0x8A,0x00,0x19,0x93,0xB5,0xB4,0xB1,0x81,0xB1,0x03,0x9A,0x82,0xA7,
0x90,0xD6,0xA0,0x80,0x1B,0x29,0x01,0xA4,0xE1,0x18,0x0A,0x2A,0x29,0x92,0xC7,0xA8,
0x81,0x19,0x89,0x30,0x10,0xE0,0x30,0xB8,0x10,0x0C,0x1A,0x79,0x1B,0xA7,0x80,0xA0,
0x00,0x0B,0x28,0x18,0xB1,0x85,0x1E,0x00,0x20,0xA9,0x18,0x18,0x1C,0x13,0xBC,0x15,
0x99,0x2E,0x12,0x00,0xE1,0x00,0x0B,0x3B,0x21,0x90,0x06,0xC9,0x2A,0x49,0x0A,0x18,
0x20,0xD1,0x3C,0x08,0x00,0x83,0xC9,0x41,0x8E,0x18,0x08,0x02,0xA0,0x09,0xA4,0x7B,
0x90,0x19,0x2A,0x10,0x2A,0xA8,0x71,0xBA,0x10,0x4A,0x0E,0x22,0xB2,0xB2,0x1B,0x8C,
0x78,0x1A,0xB5,0x93,0xA9,0x1B,0x49,0x19,0x29,0xA3,0xC6,0x88,0xAA,0x32,0x0D,0x1B,
0x22,0x08,0xC2,0x18,0xB9,0x79,0x3F,0x01,0x10,0xA9,0x84,0x1C,0x09,0x21,0xB0,0xA7,
0x0A,0x99,0x50,0x0C,0x81,0x28,0x8B,0x48,0x2E,0x00,0x08,0x99,0x38,0x5B,0x88,0x14,
0xA9,0x08,0x11,0xAA,0x72,0xC1,0xB3,0x09,0x8A,0x05,0x91,0xF2,0x81,0xA1,0x09,0x02,
0xF2,0x92,0x99,0x1A,0x49,0x80,0xC5,0x90,0x90,0x18,0x09,0x12,0xA1,0xF2,0x81,0x98,
0xC6,0x91,0xA0,0x11,0xA0,0x94,0xB4,0xF2,0x81,0x8B,0x03,0x80,0xD2,0x93,0xA8,0x88,
0x69,0xA0,0x03,0xB8,0x88,0x32,0xBC,0x97,0x80,0xB1,0x3B,0x1A,0xA6,0x00,0xD1,0x01,
0x0B,0x3B,0x30,0x9B,0x31,0x3E,0x92,0x19,0x8A,0xD3,0x5C,0x1B,0x41,0xA0,0x93,0xA2,
0xAF,0x39,0x4C,0x01,0x92,0xA8,0x81,0x3C,0x0D,0x78,0x98,0x00,0x19,0x0A,0x20,0x2D,
0x29,0x3C,0x1B,0x48,0x88,0x99,0x7A,0x2D,0x29,0x2A,0x82,0x80,0xA8,0x49,0x3E,0x19,
0x11,0x98,0x82,0x9A,0x3B,0x28,0x2F,0x20,0x4C,0x90,0x29,0x19,0x9A,0x7A,0x29,0x28,
0x98,0x88,0x33,0xCD,0x11,0x3A,0xC1,0xA4,0xA0,0xC4,0x82,0xC8,0x50,0x98,0xB2,0x21,
0xC0,0xB6,0x98,0x82,0x80,0x9C,0x23,0x00,0xF8,0x30,0xA8,0x1A,0x68,0xA8,0x86,0x9A,
0x01,0x2A,0x0A,0x97,0x91,0xC1,0x18,0x89,0x02,0x83,0xE0,0x01,0x8B,0x29,0x30,0xE2,
0x91,0x0B,0x18,0x3B,0x1C,0x11,0x28,0xAC,0x78,0x80,0x93,0x91,0xA9,0x49,0x8B,0x87,
0x90,0x99,0x3D,0x5A,0x81,0x08,0xA1,0x11,0x2F,0x1A,0x21,0x9B,0x15,0xA2,0xB0,0x11,
0xC0,0x91,0x5B,0x98,0x24,0xA2,0xF2,0x92,0x8B,0x6A,0x18,0x81,0xB5,0xB1,0x88,0x4C,
0x00,0x00,0xA4,0xC1,0x2B,0x1A,0x59,0x0A,0x02,0x80,0x1E,0x02,0x08,0xB3,0x80,0x9A,
0x23,0xB8,0xF2,0x84,0xAB,0x01,0x48,0x90,0xA7,0x90,0x0A,0x29,0x09,0x95,0x99,0xA0,
0x59,0x2B,0x00,0x97,0xB0,0x29,0x89,0x2A,0x03,0xD0,0xB7,0x1B,0x81,0x00,0xA6,0xB1,
0x90,0x09,0x48,0xC0,0x11,0x00,0x8A,0x00,0x5B,0x83,0x9A,0x18,0x2F,0x3C,0x18,0x11,
0xA9,0x04,0x1A,0x4F,0x01,0x98,0x81,0x09,0x09,0x4A,0x18,0xB4,0xA2,0x0B,0x59,0x90,
0x3B,0x49,0xBC,0x40,0x6A,0x88,0x3A,0x08,0x3E,0x3A,0x80,0x93,0xB0,0xE1,0x5A,0x00,
0xA4,0xB3,0xE3,0x90,0x0D,0x38,0x09,0x82,0xC4,0xA1,0xB1,0x4C,0x18,0x10,0x91,0xB2,
0x13,0xEA,0x34,0x99,0x88,0xA6,0x89,0x92,0x91,0xC1,0x20,0xB2,0xC2,0x86,0xD2,0xB3,
0x80,0xB2,0x08,0x09,0x87,0x91,0xC0,0x11,0x89,0x90,0x28,0xB9,0x79,0x19,0xA4,0x82,
0xD0,0x03,0x0C,0xA3,0xA5,0xB2,0xB2,0x1B,0x29,0x13,0xF1,0xB4,0x81,0x9D,0x38,0x00,
0xC4,0xA1,0x89,0x59,0x1A,0x81,0xA4,0xA9,0x1C,0x6A,0x19,0x02,0xB1,0x1A,0x4A,0x0B,
0x78,0x89,0x81,0x1C,0x2A,0x29,0x4A,0xA3,0x3E,0x1C,0x49,0x1A,0x08,0x21,0xAE,0x28,
0x4B,0x19,0x20,0x8C,0x10,0x3A,0xAB,0x26,0x8B,0x18,0x59,0x99,0x13,0xA2,0xAB,0x79,
0x2F,0x18,0x10,0xB2,0x80,0x1B,0x4D,0x5A,0x80,0x82,0x98,0x81,0x80,0x09,0xA5,0x90,
0x91,0x03,0xC2,0xE2,0x81,0xA8,0x82,0x09,0xC6,0xA3,0xB1,0x08,0x5B,0x08,0x05,0xD1,
0xA2,0x89,0x2A,0x28,0x91,0xA6,0x88,0xB0,0x49,0x80,0x09,0x08,0x88,0x07,0xB8,0x05,
0x99,0x81,0x88,0x18,0xE2,0x00,0xC3,0x18,0x0D,0x10,0x30,0xD0,0x93,0x8A,0x09,0x10,
0x2F,0x11,0x90,0xA1,0x20,0x9B,0xB1,0x73,0xC8,0x94,0x98,0x3B,0x01,0x0C,0x30,0x19,
0xF8,0x12,0x90,0xBA,0x78,0x0A,0x11,0x98,0xA0,0x79,0x8A,0x30,0x2B,0xC2,0x11,0x0D,
0x09,0x7A,0x00,0x82,0xB9,0x01,0x7A,0x89,0x21,0x09,0xA1,0x0A,0x7C,0x10,0x88,0xB5,
0x88,0x0A,0x2B,0x69,0x1A,0x10,0xA0,0x5B,0x19,0x1A,0x10,0x19,0x1A,0x6C,0x20,0x90,
0xA5,0x98,0x1B,0x0A,0x69,0x82,0xD1,0x18,0x09,0x19,0x2A,0x93,0xD4,0x9A,0x01,0x49,
0xA2,0xA2,0x82,0xD8,0x22,0xAA,0x97,0xA9,0x2D,0x38,0x2A,0xB6,0x80,0x90,0x0A,0x3C,
0x82,0x94,0xB8,0x21,0x0E,0x2A,0x22,0xB8,0x00,0x4F,0x2B,0x3A,0x81,0xA1,0x29,0x2C,
0x6A,0x13,0xD1,0xA2,0x98,0x28,0x0C,0x01,0xD5,0x08,0xA9,0x31,0xB3,0xB0,0xA7,0xB0,
0x29,0x1B,0x87,0xA2,0xA1,0xB2,0x4A,0x89,0x11,0xC3,0xF3,0x98,0x08,0x03,0xA0,0xA3,
0xC5,0x90,0xB3,0xB5,0xB4,0xB8,0x02,0x91,0x91,0xD3,0xA4,0xC1,0x1B,0x82,0x28,0xA4,
0xD1,0x94,0x8A,0x28,0x08,0x03,0xE0,0x80,0xD4,0x90,0x91,0xA1,0x3B,0x3D,0x02,0xE4,
0xA1,0x92,0x89,0x1A,0x4B,0x95,0xB3,0x90,0x99,0x6A,0x0A,0x30,0xA1,0x93,0xA6,0xA9,
0x85,0x8B,0x82,0x10,0xB1,0xA3,0x94,0xF8,0x38,0x9A,0x30,0x1A,0x8B,0xA7,0x89,0x01,
0x5B,0x19,0x18,0x11,0xF0,0x18,0x1C,0x39,0x19,0x0C,0x12,0x1C,0x2A,0x7B,0x3A,0x88,
0x2B,0x18,0x2B,0x5C,0x20,0x92,0x8D,0x38,0x8A,0x3A,0x5B,0x2E,0x3A,0x2B,0x10,0x12,
0xBB,0x6A,0x4D,0x18,0x10,0xB1,0x81,0x2A,0x8B,0x79,0x80,0x01,0x0A,0x09,0x5B,0x2D,
0x84,0x8A,0x08,0x02,0xA2,0x91,0x82,0xE8,0x50,0x9B,0x85,0xA3,0xB0,0xA3,0x1B,0x02,
0x18,0xF3,0xA2,0x88,0xAB,0x53,0xD1,0xB4,0xA3,0x09,0x09,0x18,0xD4,0x08,0xB0,0x09,
0x58,0xD1,0x82,0x89,0x81,0x1A,0x18,0x05,0xB9,0xC3,0x30,0xC0,0x95,0x80,0xC3,0x89,
0x89,0x13,0x88,0xF2,0x93,0x0E,0x18,0x01,0x92,0xA5,0xB8,0x2A,0x39,0xAA,0x33,0x9A,
0xB1,0x11,0xF5,0xA1,0xA1,0x0A,0x50,0xB8,0x03,0xC4,0xA0,0x4E,0x29,0x10,0x88,0xC2,
0x1A,0x39,0x1D,0x28,0x98,0x94,0x0E,0x10,0x2A,0x3C,0x02,0x2D,0x1B,0x4B,0x3B,0x49,
0x19,0xA9,0x48,0x2F,0x29,0x10,0x89,0x02,0x0C,0x10,0x09,0xB9,0x70,0x1B,0x8A,0x50,
0xA8,0x2B,0x49,0x89,0x69,0x88,0x95,0x89,0x90,0x92,0x4C,0x19,0x82,0xC1,0x01,0x80,
0xA0,0x2B,0x7A,0x81,0x10,0xC2,0xB7,0x98,0x88,0x19,0x2C,0x03,0xB1,0xA4,0xA1,0x0C,
0x3B,0x78,0x88,0x85,0xB1,0xA0,0x1B,0x3A,0x4A,0x08,0x94,0x81,0xF1,0x80,0x00,0x0C,
0x59,0x09,0x18,0x90,0xA6,0x92,0x8C,0x1A,0x79,0x92,0xA8,0x00,0x81,0x2E,0x2A,0x13,
0xA2,0xB0,0xA5,0x88,0x88,0x89,0x11,0x19,0xA0,0xF3,0x82,0xB0,0x83,0x5F,0x2A,0x01,
0xA1,0x94,0xB0,0x09,0x78,0x98,0xA3,0xA6,0xA0,0x91,0x80,0x93,0x98,0xC1,0x12,0x18,
0xC9,0x17,0xA0,0xA0,0x1A,0x21,0x80,0x99,0xD4,0x30,0x9D,0x00,0x10,0x2F,0x08,0x1C,
0x21,0x08,0xB4,0xC3,0x2B,0xA9,0x52,0xD2,0xA3,0xD1,0x09,0x10,0x8B,0x24,0x92,0xD1,
0x80,0x19,0xA0,0x2C,0x12,0x49,0xAA,0xB6,0x95,0xB8,0x08,0x3A,0x2B,0x01,0xF3,0xB3,
0x0B,0x09,0x79,0x18,0xA2,0xA4,0xA0,0x18,0x0C,0x20,0x08,0xA9,0x16,0x0C,0x00,0x1B,
0x08,0x2B,0x7B,0x01,0x01,0xB9,0x59,0x19,0x8B,0x45,0xA8,0x80,0x0C,0x1A,0x41,0x1E,
0x00,0x28,0xA8,0x5A,0x00,0xC1,0x49,0x99,0x21,0x1D,0x08,0x85,0x99,0x95,0x89,0x90,
0x11,0x90,0xD1,0x28,0xB2,0xA7,0x99,0x81,0x02,0xAC,0x13,0x81,0xB2,0xA6,0xA9,0x28,
0x1C,0xB1,0x33,0xD1,0xC1,0x58,0xA8,0x14,0xB0,0xB7,0x91,0xA0,0x82,0x89,0xC2,0x28,
0xA1,0xB2,0x49,0xD2,0x94,0xC8,0x12,0x80,0x99,0x85,0x08,0xD3,0x09,0xA2,0xB3,0x1E,
0x08,0x21,0xB9,0x23,0xB4,0xAB,0x41,0xAC,0x87,0x09,0xA2,0xC5,0x0B,0x2A,0x5A,0x91,
0x20,0x9A,0x89,0x78,0x9B,0x31,0x89,0x80,0x29,0x0A,0xB7,0x3C,0x98,0x48,0x1D,0x00,
0x01,0xB0,0x20,0x2F,0x29,0x4A,0x89,0x94,0x1C,0x88,0x28,0x2B,0x10,0x88,0x9A,0x71,
0x9A,0x08,0x4A,0x2F,0x18,0x2B,0x18,0x02,0xA8,0x4B,0x7A,0x99,0x48,0x80,0xA8,0x20,
0x1D,0x40,0xA8,0x10,0x08,0xA8,0xC5,0x88,0xC2,0x18,0x88,0x2A,0x12,0xF3,0x82,0xD8,
0x20,0x0A,0x09,0xA6,0x98,0x04,0xB9,0x11,0x18,0xC3,0xE1,0x29,0xA1,0x11,0xC1,0x03,
0xE2,0x9A,0x33,0xA9,0xB5,0x98,0x92,0xA1,0x02,0xF8,0x21,0xA8,0x10,0x02,0xC1,0xB7,
0x1B,0x90,0x5B,0x3C,0x83,0x93,0xE0,0x19,0x1A,0x11,0x11,0xF1,0x92,0x89,0x19,0x2C,
0x2C,0x41,0x99,0x92,0x90,0x3F,0x18,0x4B,0x00,0x08,0xD2,0x01,0xB2,0xAA,0x78,0x09,
0x01,0x91,0xA2,0x98,0x2F,0x3A,0x2C,0x01,0x00,0x93,0xE0,0x28,0x2C,0x2B,0x01,0x12,
0xE1,0x80,0xB3,0x3D,0x3A,0x0A,0x50,0x98,0xC2,0xA0,0x11,0xAA,0x30,0x87,0x90,0xC2,
0x29,0x88,0x38,0xC8,0xB5,0x90,0xBA,0x70,0x1A,0x02,0x94,0xD0,0x80,0x1A,0x82,0xA6,
0xB0,0x91,0x18,0xB3,0x00,0x13,0xF1,0xA2,0xC1,0x82,0xB0,0x00,0x15,0x0B,0xD3,0x02,
0xA8,0x91,0x2B,0x1F,0x49,0x88,0xA6,0x80,0x88,0x08,0x1B,0xA5,0x80,0xB9,0x06,0x0B,
0x90,0x21,0x9D,0x48,0x18,0xA0,0x15,0xC9,0x82,0x2B,0x1A,0x42,0x9A,0xC4,0x39,0xBC,
0x69,0x00,0xA0,0x29,0x8C,0x39,0x59,0x08,0x09,0x49,0xA9,0x6B,0x81,0x00,0x98,0xB0,
0x68,0x3D,0x81,0x88,0x18,0x19,0x1D,0x12,0x80,0xB2,0x3A,0x3F,0x85,0x92,0xD0,0x00,
0x0A,0x19,0x12,0xF1,0x02,0x9B,0x19,0x40,0xB9,0x11,0x02,0xF2,0x1A,0x08,0x94,0x0A,
0xC2,0x83,0x0B,0xB4,0xA4,0xC0,0x32,0xD8,0x86,0x98,0x90,0x95,0x89,0xA3,0x83,0xC2,
0x92,0xE1,0x92,0x82,0xD9,0x03,0x08,0xA9,0x85,0x92,0xA2,0x80,0xE0,0x30,0x8B,0xB3,
0x87,0x89,0x90,0x83,0xA0,0x08,0x92,0x93,0x3E,0xAB,0x43,0x89,0xE3,0x80,0x83,0x2F,
0x00,0xA3,0x80,0xC9,0x22,0x3F,0x08,0x81,0x0B,0x33,0x9A,0xA3,0x7B,0x0C,0x29,0x4A,
0x1B,0x21,0xAA,0x70,0x1B,0x0D,0x48,0x1A,0x81,0x88,0xB1,0x39,0x3F,0x08,0x58,0xA0,
0x81,0x1A,0x1A,0x2B,0x6D,0x11,0x0A,0x91,0x01,0x1A,0x98,0x5A,0x0C,0x03,0xB1,0x84,
0xA3,0xAD,0x58,0x2A,0xA1,0x84,0xB1,0xA0,0x5C,0x2B,0x13,0xA8,0x95,0x83,0xE8,0x10,
0x81,0xB0,0x00,0xC2,0x96,0xA0,0x91,0x00,0x2C,0x90,0x30,0xF2,0x80,0xA8,0x39,0x21,
0xC1,0x03,0xAC,0x39,0x7C,0x29,0x91,0x1A,0x00,0x19,0x2C,0x3A,0x93,0xB0,0x29,0x8F,
0x28,0x02,0x93,0xF3,0xA9,0x01,0x03,0xE0,0x08,0x09,0x1D,0x58,0xA1,0x83,0xA9,0x6B,
0x2A,0x3C,0x21,0x89,0xC2,0x2C,0x4B,0x8A,0x50,0x81,0x98,0xA8,0x32,0x0C,0x8E,0x24,
0x0B,0x1A,0x81,0x92,0xA1,0x4F,0x18,0x3A,0x0A,0xB4,0x18,0x2E,0x39,0x82,0x19,0xD3,
0xD0,0x28,0x1B,0x11,0x98,0x07,0xAA,0x28,0x00,0x88,0xB4,0x89,0x1B,0x1F,0x22,0x00,
0xB3,0xC9,0x33,0xAB,0x2B,0xB5,0x48,0x98,0x98,0xA7,0x10,0xD2,0xC1,0x23,0xCA,0x93,
0xC6,0x80,0xA1,0x88,0x02,0x89,0xE2,0x09,0x38,0xBA,0x40,0x89,0x21,0xD8,0x49,0x10,
0x8D,0x02,0x90,0xC3,0x9A,0x24,0x89,0x08,0x84,0xA5,0x9C,0x10,0x11,0x9C,0x88,0x30,
0x3C,0xA1,0x94,0x58,0x8C,0x0B,0x69,0x29,0x9A,0x81,0x12,0x2B,0x8B,0x79,0x94,0xB0,
0xC1,0x84,0xC2,0x99,0x25,0x99,0x11,0xA2,0x93,0xE4,0x99,0x80,0x0A,0x00,0x10,0xB7,
0xB0,0x31,0xBA,0x3C,0x21,0xB3,0xF1,0x18,0xA0,0x2A,0x20,0xA3,0x06,0xE8,0x28,0xA1,
0xB4,0x08,0x0B,0x11,0x4B,0xB7,0x90,0xA5,0x98,0x3D,0x19,0x02,0xA1,0xC4,0xB2,0x19,
0x28,0xC0,0xA5,0x92,0xB1,0xA3,0x0A,0x0A,0x08,0x2B,0x70,0xC4,0xB3,0x00,0xBC,0x4B,
0x39,0x12,0xE3,0xA0,0x00,0x3F,0x18,0x29,0x94,0xD1,0x19,0x09,0x00,0xA1,0x83,0x99,
0x9B,0x35,0x80,0xC4,0xB1,0x6A,0x1A,0x1C,0x29,0x38,0x0E,0x19,0x5A,0x1A,0x82,0x8A,
0x59,0x2A,0x2E,0x20,0x88,0xA8,0x3A,0x38,0x3D,0x00,0xB3,0x29,0xAD,0x49,0x10,0x0C,
0x01,0x01,0xA3,0x8F,0x85,0x09,0x1B,0x88,0x10,0xA3,0xD2,0x90,0x3C,0x5C,0x39,0x03,
0xD1,0xA0,0x00,0x2A,0x0B,0x04,0xA7,0x90,0xA0,0x11,0x90,0x99,0x83,0xB4,0xB1,0xF1,
0x84,0x88,0x90,0x18,0x18,0xD3,0xD2,0xB3,0xA0,0x1A,0x21,0xA7,0xB2,0xB3,0x92,0x9A,
0x22,0xB9,0x28,0x38,0xBD,0x87,0x2A,0xB1,0x13,0x0D,0x0A,0x38,0xC9,0x24,0xC0,0x19,
0x23,0x0F,0x01,0x88,0xC0,0x2A,0x82,0x18,0x28,0xF0,0x18,0x2A,0x29,0x4B,0x35,0xB8,
0xA3,0x9D,0x18,0x1B,0x40,0x00,0x9A,0x5C,0x3A,0x09,0x2F,0x38,0x8A,0x3B,0x3B,0x11,
0x5C,0x19,0x2B,0x4A,0x08,0x0A,0x3D,0x20,0x4F,0x3A,0x19,0x2A,0x18,0x4D,0x1B,0x3A,
0x11,0x0D,0x3A,0x3C,0x4B,0x93,0x81,0xAA,0x6B,0x4A,0x18,0x00,0xC3,0xC3,0x9A,0x59,
0x2A,0x1B,0xA7,0xA1,0x81,0x88,0x88,0x58,0xB2,0xB1,0x2B,0x83,0xD4,0x81,0x08,0x0F,
0x00,0x20,0xC2,0xE2,0x80,0x08,0x1C,0x29,0x04,0xB1,0xA2,0x01,0x1C,0x91,0x00,0x0C,
0x49,0xB0,0x43,0xF2,0x99,0x39,0x3F,0x00,0x81,0x94,0xC1,0x09,0x1A,0x69,0x90,0x80,
0x94,0xAA,0x20,0x2A,0x91,0xB1,0x39,0x7A,0x38,0xD1,0x10,0x8A,0x8C,0x5A,0x01,0xB5,
0x98,0x80,0x2A,0x0B,0x32,0x92,0xF1,0x81,0x9A,0x23,0x8A,0xA3,0xB7,0x09,0x03,0x08,
0xD0,0x94,0x9A,0x09,0x01,0x93,0xB7,0xC2,0x8C,0x3A,0x83,0x99,0x05,0xA0,0x0B,0x29,
0x93,0xE5,0x80,0x89,0x38,0x90,0x8A,0xD7,0xA1,0x19,0x1B,0x48,0x98,0x92,0xC3,0xA1,
0x09,0x3F,0x02,0x0C,0x22,0xC3,0xB2,0xA1,0x01,0x9F,0x4A,0x01,0xA3,0xD3,0xB0,0x28,
0x3F,0x29,0x20,0xA2,0xC2,0xB1,0x08,0x5A,0x98,0x13,0xD2,0xC1,0x01,0xB2,0x80,0x3D,
0x03,0xC1,0x89,0x96,0x90,0x90,0x3A,0x1A,0x9A,0x32,0xB6,0xA2,0x8E,0x4A,0x28,0x8A,
0x84,0xA2,0x8A,0x2D,0x49,0x09,0x88,0x18,0x30,0x9D,0x2C,0x23,0xB1,0x0C,0x92,0x2D,
0x39,0x82,0xC4,0x2E,0x10,0x1A,0x10,0xB9,0x48,0x19,0x39,0xBA,0x34,0xDA,0x2D,0x48,
0x1A,0xA6,0x98,0x83,0x9A,0x1D,0x38,0x04,0xD0,0x18,0x90,0x2C,0x11,0x93,0xD3,0x9A,
0x11,0x08,0x82,0xF1,0x01,0xA0,0x2A,0x93,0xD3,0xB4,0xB8,0x82,0x2F,0x11,0xA3,0xB3,
0xA8,0x3B,0x09,0x23,0x96,0xC8,0x3B,0x3F,0x93,0x82,0xA1,0x90,0x3F,0x28,0x81,0xD1,
0x93,0x08,0x2D,0x18,0x91,0xB3,0xB5,0x98,0x2A,0x2B,0x84,0xB1,0x5B,0x8A,0x31,0x18,
0x80,0x8B,0x7E,0x39,0x2B,0x02,0xC1,0x8B,0x6C,0x49,0x09,0x10,0xA1,0x08,0x01,0x0C,
0x20,0xA1,0x09,0x4F,0x18,0x00,0x01,0xA0,0x5C,0x1B,0x5B,0x10,0x92,0x90,0x2B,0x5A,
0x3D,0x18,0x91,0x19,0x98,0x2D,0x39,0x89,0x2D,0x3A,0x48,0x2C,0x11,0xB5,0x9A,0x19,
0x5B,0x28,0x90,0x95,0x98,0x89,0x2B,0x40,0x08,0x90,0xF3,0x0A,0x08,0xA6,0x80,0x91,
0xB2,0xA0,0x02,0xF2,0xA1,0xB7,0x89,0x81,0x82,0x91,0xB1,0x21,0xAB,0x32,0xE9,0x04,
0xA2,0x8D,0x12,0x91,0xA3,0xA3,0xD2,0x8B,0x39,0xD1,0x84,0xE2,0x90,0x00,0x2B,0x29,
0xA3,0xD4,0xA1,0x91,0x1D,0x5A,0x08,0x19,0x11,0x99,0x08,0x18,0x49,0x0F,0x18,0x10,
0x82,0xF1,0x00,0x89,0x2F,0x3A,0x01,0xB3,0xC2,0x81,0x3F,0x29,0x08,0x10,0xA1,0xA1,
0x3B,0x5D,0x19,0x28,0x0B,0x38,0x82,0x91,0x19,0xBD,0x3B,0x7A,0x80,0x12,0xB3,0xE0,
0x0B,0x6A,0x01,0x88,0xA4,0x08,0x0B,0x08,0x59,0x80,0x80,0x1D,0x49,0x89,0x00,0x84,
0x99,0x1A,0x2B,0x32,0xE3,0xB4,0xA9,0x3A,0x99,0x31,0xE3,0xAA,0x58,0x3B,0x88,0x95,
0xC0,0x18,0x4A,0x09,0x30,0xF2,0xA3,0x1C,0x1B,0x49,0x00,0xD3,0xB2,0xA0,0x18,0x11,
0x92,0xD3,0xB2,0x91,0x80,0xE7,0xA1,0x91,0x98,0x19,0x22,0xC2,0xD2,0x18,0x8D,0x3B,
0x10,0xA5,0x91,0x98,0x02,0x3E,0x80,0x01,0x90,0xAA,0x13,0xF1,0x02,0xD1,0x08,0x19,
0x49,0xB4,0x91,0xB4,0x99,0x2A,0x0C,0x32,0xC0,0x05,0x88,0x0B,0x80,0x2C,0x81,0x10,
0x0B,0x51,0xA9,0x19,0x05,0xBF,0x28,0x20,0xE1,0x90,0x80,0x28,0x19,0x08,0x26,0xB1,
0xA1,0x18,0x88,0x2A,0xF0,0x12,0x8A,0xB3,0x14,0x1B,0xD4,0xD8,0x10,0x08,0x8A,0x17,
0xA0,0x98,0x2B,0x3A,0x29,0x48,0xA4,0x99,0x0E,0x4A,0x12,0x8B,0x31,0x8B,0x4E,0x1A,
0x11,0xB5,0x89,0x91,0x29,0x89,0xC2,0x97,0x90,0x0A,0x19,0x11,0x91,0xC1,0xD5,0x08,
0x89,0x20,0x91,0xB1,0x1A,0x2D,0x18,0x29,0xD2,0x3B,0x3E,0x3A,0x2A,0x90,0x82,0x1C,
0x49,0x3B,0x93,0xB6,0xC8,0x4C,0x02,0x91,0x93,0xF2,0x88,0x2D,0x28,0x81,0x82,0xC1,
0x89,0x2D,0x6B,0x19,0x82,0x80,0x18,0x8B,0x39,0x39,0xC8,0x3A,0x6A,0x0A,0x22,0xD2,
0x09,0x2C,0x1A,0x68,0x92,0xE2,0x89,0x2A,0x2A,0x30,0xC2,0xA3,0xB4,0x1D,0x2A,0x09,
0x93,0x18,0xF2,0x89,0x28,0xB3,0x01,0x8F,0x18,0x11,0xA1,0x93,0x90,0xD1,0x7A,0x20,
0xC3,0xA2,0xA8,0x88,0x1D,0x28,0xA5,0xA2,0xA2,0x0B,0x29,0x2B,0x87,0xC1,0x80,0x0A,
0x19,0x01,0x12,0xF1,0x10,0x80,0x0A,0x18,0x08,0x2F,0x4A,0x02,0x89,0x1B,0x29,0x5D,
0x4C,0x08,0x82,0xA1,0x0A,0x3A,0x4B,0x29,0xC6,0xC3,0x09,0x09,0x88,0x39,0x98,0x82,
0xA5,0x1A,0x30,0x11,0xBD,0x3F,0x12,0x8B,0x28,0xC3,0x88,0x3F,0x2B,0x3B,0x48,0xA1,
0x80,0x8A,0x4D,0x39,0x01,0x93,0xA2,0xF1,0x19,0x19,0x0A,0x02,0xB2,0x8B,0x24,0xD2,
0x4B,0x12,0xC8,0x2E,0x10,0xB5,0x89,0x01,0x09,0x1C,0x2A,0x03,0xD4,0x91,0x98,0x99,
0x11,0x2B,0xE4,0x00,0x00,0x01,0xE0,0xA5,0x89,0x99,0x31,0x18,0xD0,0xB7,0x98,0x18,
0x0A,0x10,0x94,0xC2,0x90,0x18,0x00,0x99,0x87,0xA0,0x90,0x2A,0x3C,0x02,0xB8,0xC1,
0x79,0x1A,0x20,0x08,0xA1,0xD2,0x1C,0x29,0x03,0xD1,0x29,0x99,0x2C,0x50,0xB3,0xD1,
0x08,0x09,0x3C,0x10,0x04,0xB2,0x0D,0x2B,0x59,0x80,0x90,0x01,0x0F,0x3A,0x18,0x01,
0xA2,0x9B,0x5B,0x3D,0x81,0x03,0xD2,0x98,0x59,0x90,0x81,0x92,0xB4,0x8B,0x1B,0x40,
0xB2,0xB5,0x08,0x4B,0x01,0x09,0xD1,0x91,0x8B,0x7A,0x10,0xB3,0xC3,0x99,0x49,0x1A,
0x29,0xB5,0xA2,0xAB,0x40,0x81,0x19,0xB7,0xB0,0x20,0x2B,0xD4,0x88,0xA1,0x91,0x3C,
0x82,0x37,0xD3,0xB1,0x8A,0x1B,0x30,0xB3,0xF4,0xA1,0x91,0x09,0x10,0x03,0xD0,0x83,
0xA9,0x8F,0x10,0x01,0x90,0x18,0x80,0x20,0x2B,0xF1,0x28,0x99,0x2A,0x41,0xF0,0x12,
0xAA,0x83,0x82,0xD1,0xC1,0x08,0x89,0x59,0x09,0x83,0x87,0xB0,0x2A,0x4D,0x18,0x09,
0x19,0xB3,0x4B,0x3F,0x39,0x19,0x09,0x01,0x89,0x03,0x1F,0x00,0x1A,0x0B,0x10,0x68,
0xA0,0x18,0x8C,0x6A,0x09,0x08,0x97,0xA1,0x81,0x1B,0x2B,0x4C,0x03,0xB4,0xA8,0x92,
0x4B,0x3C,0xA1,0x81,0x95,0xA8,0x81,0x12,0xBB,0x92,0x45,0xB9,0x93,0xF4,0x88,0x0A,
0x2D,0x28,0x00,0xA3,0xA3,0x8A,0x3F,0x48,0xB1,0x92,0xB4,0xA8,0x30,0x80,0xD3,0x80,
0xD1,0x19,0x3B,0xC4,0x81,0xC1,0x29,0x0D,0x20,0x13,0xC8,0xB4,0x4C,0x09,0x00,0x82,
0xC2,0x3B,0x0D,0x30,0x0B,0x12,0xF0,0x1B,0x20,0x0A,0xA6,0x80,0x0A,0x4A,0x4A,0x80,
0x94,0xB1,0x2E,0x3B,0x1A,0x10,0x93,0x10,0x4C,0x3D,0x08,0x82,0xC9,0x19,0x6A,0x2B,
0x38,0xD1,0x08,0x19,0x2A,0x5A,0x82,0xB1,0x8D,0x29,0x78,0x09,0x82,0x0A,0x2C,0x1B,
0x19,0x41,0xB8,0x8C,0x79,0x2B,0x11,0x88,0x82,0x91,0xDC,0x28,0x11,0xB0,0x11,0x18,
0xC9,0x62,0xA1,0x91,0x98,0x3B,0x3A,0xB0,0xF4,0x01,0xC0,0x29,0x39,0xF8,0x95,0x91,
0x88,0x88,0x91,0x03,0xA1,0xE2,0x18,0x82,0xD1,0xA2,0xD1,0x80,0x19,0x20,0x83,0xB1,
0xE3,0x80,0x91,0x4D,0x1A,0x03,0xB2,0x09,0x18,0xD1,0x19,0x09,0x92,0xA6,0xA0,0xB6,
0xB2,0x8B,0x38,0x10,0x42,0xD3,0xD0,0xA8,0x20,0x2C,0x10,0x01,0xB1,0xB4,0xAB,0x5B,
0x79,0x80,0x10,0x1A,0xA8,0x3D,0x18,0x20,0xB3,0x8F,0x18,0x01,0x00,0x09,0xF3,0x89,
0x69,0x88,0x81,0x91,0x08,0xE1,0x1A,0x08,0x11,0x81,0x1E,0x29,0xA0,0x01,0x00,0x90,
0x3E,0x7B,0x18,0x82,0xC3,0xA1,0x2A,0x2C,0x5B,0x81,0xA5,0x90,0x81,0x00,0x0B,0x1A,
0x1C,0x2C,0x32,0xC0,0xF3,0x80,0x2D,0x2A,0x10,0x02,0xE4,0xC1,0x89,0x4A,0x09,0x01,
0x03,0xD2,0x98,0x2A,0x39,0x8A,0x89,0x26,0xB1,0xB2,0x12,0xC0,0x0A,0x5A,0x18,0x98,
0xF3,0x92,0x99,0x99,0x79,0x01,0xB5,0xA1,0x80,0x80,0x90,0x83,0xA0,0xE2,0x81,0x29,
0x93,0x8A,0x0A,0x6A,0x1F,0x18,0x02,0xC8,0x01,0x19,0x3B,0x4A,0x98,0x17,0xA8,0x0D,
0x38,0xA1,0x91,0x10,0xA2,0x2B,0x4C,0xA6,0x81,0xBA,0x21,0x4C,0x80,0x21,0xD1,0x92,
0x2C,0x08,0x30,0x9F,0x93,0x2A,0x89,0x03,0x8B,0x87,0x0A,0x0D,0x12,0x98,0xA4,0x93,
0xBB,0x59,0x18,0xA1,0x32,0xE9,0x84,0x08,0x8A,0x02,0xA1,0x91,0x4B,0xB4,0x20,0x88,
0xF0,0x3A,0x1A,0x88,0x87,0xB1,0x92,0x0A,0x08,0x6B,0x83,0xC3,0x91,0xC0,0x2B,0x79,
0x08,0x8A,0x84,0xA0,0x89,0x40,0x1B,0xA1,0x39,0x98,0x17,0xC2,0xA2,0x12,0xCD,0x20,
0x89,0x92,0x25,0xB0,0x2D,0x3A,0x8B,0x58,0x2A,0xA0,0x4C,0x08,0x30,0xAE,0x82,0x59,
0x89,0x1A,0x10,0xC2,0x18,0x2C,0x40,0x1E,0x01,0xA3,0x8A,0x81,0x2C,0x29,0x29,0xA9,
0x13,0x51,0xAD,0x12,0x89,0x8F,0x18,0x2C,0x39,0x00,0xC1,0x10,0x3C,0x2A,0x41,0xC8,
0xA2,0x91,0x0A,0x6C,0x10,0x12,0x88,0xE8,0x30,0x91,0x81,0xD8,0x01,0x1B,0x0D,0x07,
0x00,0xA8,0x92,0x0A,0x28,0xD2,0xC3,0x02,0xAA,0x94,0x81,0xB4,0xB3,0x1A,0x0B,0x13,
0xF9,0x16,0xA1,0x8A,0x59,0x19,0x02,0xC1,0x91,0x8B,0x3D,0x18,0x3B,0xA4,0x94,0x80,
0x99,0x88,0x1C,0x79,0x0A,0x02,0x03,0xF8,0x90,0x39,0x5B,0x19,0x02,0xC3,0x90,0xBB,
0x58,0x6A,0x09,0x02,0x89,0x91,0x88,0x1A,0x69,0x8A,0x19,0x15,0xA0,0xA2,0x00,0x9A,
0x6B,0x49,0x88,0xA3,0x92,0xBB,0x6B,0x3D,0x38,0x01,0x98,0x91,0x3F,0x09,0x18,0x20,
0x90,0x80,0xAC,0x70,0x91,0x9B,0x51,0x09,0x88,0x99,0x14,0x8B,0x98,0x83,0x79,0xA0,
0x99,0x13,0x01,0x19,0xE0,0x83,0x0B,0xB0,0x0C,0x31,0x95,0xB5,0xC2,0x8A,0x39,0x20,
0x80,0x39,0xF3,0xB1,0x10,0x88,0x5E,0x18,0x94,0xA1,0x88,0xA1,0x98,0x15,0xAA,0x39,
0xD4,0x84,0xC0,0xA2,0xA2,0x0C,0x81,0x86,0xB5,0xA1,0xB1,0x14,0x1B,0xB1,0x02,0x92,
0xC3,0xE0,0x88,0x11,0xAA,0x69,0x18,0x81,0xA3,0xB0,0x01,0xBF,0x2A,0x31,0x93,0xF1,
0x00,0x89,0x18,0x19,0x11,0xD3,0xE0,0x10,0x18,0xB1,0x18,0x24,0x9A,0x2B,0xA4,0xC0,
0xB0,0x31,0x6C,0x19,0xB4,0x12,0xA8,0xEA,0x58,0x10,0x8B,0x93,0x82,0x88,0x9A,0x41,
0x10,0xC3,0xEA,0x41,0xA9,0x9C,0x34,0xA1,0x2A,0x79,0xA2,0x01,0xA8,0xB3,0x28,0xCC,
0x41,0x9A,0xB3,0x4B,0xB3,0x27,0x8B,0x83,0x2B,0x2F,0x08,0x28,0xB2,0x80,0x2C,0x30,
0x5E,0x09,0x12,0x9B,0x09,0x22,0x5B,0x19,0x8A,0x11,0x59,0x99,0xA4,0x32,0xCD,0x18,
0x08,0x10,0x85,0xB3,0xB4,0x1E,0x88,0x28,0x8A,0x11,0x09,0xC0,0x79,0x80,0x91,0x3B,
0x80,0x10,0x0F,0x01,0x80,0x91,0x19,0x3D,0x92,0x28,0xA8,0x37,0x9A,0x0A,0x3A,0x8A,
0x45,0xA9,0xA4,0x00,0xAA,0x09,0x3D,0x59,0x20,0xE1,0x08,0x98,0x90,0x59,0x10,0x09,
0xA3,0xC3,0x93,0x99,0x2B,0x69,0x11,0xD1,0xB1,0xA4,0x91,0x3C,0x89,0x83,0xF0,0x10,
0x91,0xA1,0x89,0x59,0x05,0x99,0x93,0x94,0xC8,0x08,0x0A,0x09,0x17,0xB1,0x83,0xC1,
0x91,0x40,0xA2,0xC2,0x98,0xC3,0xBA,0x28,0x23,0x0F,0x80,0x50,0xB8,0x19,0x10,0x96,
0x98,0x8C,0x05,0x98,0x19,0x29,0x2B,0x3B,0x0A,0xE2,0x01,0x0F,0x3C,0x38,0x08,0x09,
0x81,0x4A,0x6C,0x08,0x00,0x88,0x98,0x38,0x2C,0x5A,0x1B,0x20,0x1A,0x39,0xB0,0x09,
0xCB,0x5B,0x49,0x09,0x71,0x00,0xC1,0x0E,0x08,0x38,0x0C,0x02,0x10,0x0E,0x10,0x8A,
0x48,0x19,0x90,0x92,0x0D,0xA3,0x98,0x3B,0x79,0x19,0x01,0x10,0xE1,0x80,0x19,0x2B,
0x10,0xF2,0x02,0xAB,0x84,0x9A,0x29,0xB4,0x80,0x92,0x03,0x88,0x95,0xD0,0x03,0x90,
0xA0,0xC7,0xA1,0xB0,0xA2,0x02,0x18,0xB5,0xD4,0x01,0xC0,0x08,0xA2,0x93,0xA8,0xA0,
0xC3,0x20,0xF3,0x90,0x00,0xD5,0x08,0x89,0xA5,0x80,0xA0,0x81,0x82,0xC2,0x09,0xD1,
0x13,0xCB,0x03,0x84,0x91,0xE1,0x1B,0x12,0x08,0xAB,0x87,0x18,0xAB,0x58,0x89,0x28,
0x81,0xC9,0x33,0xA9,0x80,0x2E,0x20,0x83,0xB9,0x20,0x3B,0x9E,0x7A,0x08,0x81,0x18,
0x0B,0x88,0x79,0x80,0x8B,0x00,0x12,0x0E,0x89,0x51,0x1B,0x81,0xA0,0x3A,0x01,0xAF,
0x11,0x28,0xBA,0x35,0x98,0x88,0x52,0xC0,0x83,0x2F,0xA9,0x11,0x0A,0x19,0x25,0xD0,
0x30,0x9C,0x08,0x21,0x98,0x81,0x2A,0xF3,0x2A,0x80,0xB6,0x2B,0x08,0x93,0xE9,0x02,
0x81,0x8C,0x21,0x00,0xA6,0xA9,0x94,0x01,0x8F,0x80,0x94,0x98,0x93,0xB4,0x00,0x08,
0xC0,0x14,0x98,0xB3,0xB4,0xC1,0x09,0x18,0xA7,0x00,0xA3,0xC8,0x0A,0x3C,0x19,0x96,
0x83,0xC1,0x99,0x19,0x4A,0x85,0x80,0xC1,0x91,0x99,0x90,0x2A,0x17,0x95,0x99,0x88,
0x12,0xAE,0x39,0x08,0x92,0x84,0xB0,0xA8,0x79,0x09,0x19,0x01,0xB2,0xA3,0x8F,0x28,
0x2B,0xA2,0x40,0x82,0xA0,0x4C,0xA9,0x39,0x8D,0x81,0x70,0x88,0xA0,0x1A,0x49,0x2D,
0x1A,0x26,0xA8,0x98,0x08,0x29,0x0B,0x12,0x96,0xB1,0xB2,0x3A,0x13,0x9B,0x60,0xA0,
0x88,0xB2,0x34,0xEA,0x1A,0x2A,0x79,0x98,0x10,0x04,0x8C,0x1C,0x81,0x04,0x8C,0x83,
0x19,0x2F,0x81,0x93,0x98,0x10,0x08,0x30,0x2A,0xFA,0x05,0x08,0x2A,0x89,0x91,0xA3,
0xFA,0x11,0x11,0x00,0x8C,0x04,0x8A,0x2A,0xB5,0x10,0xA9,0xC2,0x3D,0x1B,0x32,0x04,
0x0A,0x1A,0x09,0x40,0x1F,0x92,0x1D,0x2A,0x91,0x10,0x30,0x2F,0x0B,0x68,0x99,0xA2,
0x92,0x88,0x78,0xA9,0x20,0x28,0xE2,0x92,0x1A,0x99,0x4B,0x19,0x22,0xA1,0xE2,0x21,
0x2F,0x98,0x29,0x18,0x91,0x08,0xB0,0x79,0x1A,0x82,0x3B,0xB1,0xA7,0x8A,0xB3,0x98,
0x5B,0x23,0xCA,0x42,0x83,0xF0,0x90,0x18,0x98,0x08,0xB4,0x20,0xA3,0xC0,0x43,0xD8,
0x80,0x81,0xA3,0x99,0xD9,0xA7,0x19,0x90,0x10,0x05,0xB1,0x8B,0x02,0xA4,0xBD,0x23,
0x93,0x8A,0x99,0x4B,0x03,0xC1,0xF8,0x38,0x09,0x2B,0x14,0xD0,0x03,0x8A,0x2A,0x39,
0xB9,0x97,0x90,0xAA,0x50,0x01,0x99,0x51,0xD1,0x09,0x1A,0xB5,0x00,0x8B,0x93,0x08,
0x98,0x11,0xF9,0x85,0x2B,0x08,0x96,0x89,0x90,0x2A,0x12,0x4A,0xD8,0x85,0x2B,0x0E,
0x10,0x00,0x01,0xB1,0x9B,0x69,0x1A,0x90,0x40,0xB8,0x01,0x08,0x0A,0x2C,0x09,0x14,
0x4B,0xE2,0x82,0x88,0xB1,0x78,0x0A,0x01,0xC2,0x93,0x19,0xCE,0x20,0x3C,0x82,0xB4,
0x1B,0x20,0x8C,0x3B,0x29,0xAB,0x86,0x23,0xD8,0x81,0x9A,0x5A,0x49,0xB0,0x16,0xA0,
0xB0,0x28,0x1B,0x13,0x93,0xE4,0xA2,0xA9,0x08,0x5A,0xB3,0x12,0xC1,0xE1,0x10,0x88,
0x01,0x0C,0x92,0x08,0x89,0xB7,0x88,0x81,0x10,0x9A,0x17,0xA0,0xB0,0x13,0x99,0xE0,
0x39,0x31,0xD2,0xB2,0x80,0x0B,0x2D,0x49,0x80,0x01,0xB0,0x06,0x09,0x0C,0x3A,0x69,
0xA0,0x08,0xB2,0xA1,0x69,0x2B,0x5A,0x81,0x92,0xBA,0x21,0xB1,0x7D,0x10,0x80,0x08,
0x88,0x82,0x32,0x0D,0xB0,0x1A,0x1C,0x21,0x94,0xA9,0x58,0xB9,0x5A,0x4A,0xA0,0x13,
0xA9,0x80,0x7C,0x00,0x20,0x8A,0x04,0x0C,0x00,0x82,0x2A,0xB2,0xAC,0x4B,0x69,0xA0,
0xA6,0x81,0x9B,0x19,0x38,0x8B,0x17,0xB2,0x81,0x2A,0xBB,0x94,0x29,0xA2,0x15,0xBA,
0x97,0xA3,0xB9,0x79,0x01,0xB2,0x02,0xF1,0x90,0x0A,0x29,0x11,0x88,0xE5,0xA0,0x81,
0x19,0x91,0x90,0x28,0xB3,0x14,0xD0,0xB5,0x91,0x9A,0x29,0x0B,0x07,0xA2,0xB3,0x01,
0x9D,0x28,0x41,0xD0,0x91,0x90,0x82,0x1A,0xA8,0x44,0x9A,0xA9,0x21,0xE3,0xA9,0x4B,
0x19,0x78,0x89,0x83,0xA3,0xB9,0x5A,0x3D,0x80,0x82,0xA2,0xA0,0x6C,0x10,0x20,0x8B,
0x93,0x8B,0x0E,0x33,0xA9,0xB1,0x68,0x8A,0x31,0xAC,0x94,0xB4,0x8B,0x32,0x0B,0xB4,
0x81,0x91,0x1D,0x33,0xD9,0x31,0xE1,0x8B,0x3B,0x30,0x12,0x49,0xD2,0x8E,0x29,0x18,
0x8A,0x92,0x02,0xAA,0x59,0x1C,0x32,0x88,0x01,0x23,0xFB,0x83,0x29,0xDA,0x59,0x01,
0x81,0x92,0xE1,0x18,0x8A,0x1D,0x30,0x93,0xF1,0x00,0x01,0x0B,0x39,0x92,0x89,0xA0,
0x11,0x5B,0xE0,0x82,0x09,0x13,0xAA,0xB4,0x16,0xD8,0x91,0x2A,0x29,0x84,0x1B,0xC5,
0x98,0x98,0x31,0x98,0x99,0x17,0xA9,0x20,0x92,0xC3,0x18,0x9D,0x20,0x3D,0x89,0x94,
0xA2,0x1C,0x5C,0x29,0x39,0xA0,0xB3,0x00,0x0C,0x4C,0x48,0x92,0x0A,0x91,0x85,0x9A,
0x01,0x82,0x1F,0x10,0x99,0x15,0xC1,0xA0,0x39,0x1A,0x1D,0x85,0xB4,0x90,0x1A,0x2A,
0x4B,0x01,0xB2,0x93,0xBE,0x12,0x83,0xC9,0x18,0x09,0x20,0x78,0xF1,0x08,0x19,0x88,
0x3A,0x83,0xB3,0xA9,0x93,0x7A,0x0A,0x96,0x98,0x00,0xA8,0x3A,0x30,0x92,0xF2,0x9B,
0x3D,0x38,0x92,0x92,0xC3,0xB8,0x6B,0x29,0x01,0x01,0xB2,0x2F,0x09,0x19,0x18,0x01,
0x3B,0x7B,0x10,0xA1,0x90,0x39,0x0F,0x38,0x0A,0xB5,0xA4,0x89,0x8B,0x6A,0x2B,0x12,
0xC8,0x90,0x40,0x2A,0x9E,0x22,0x88,0x18,0x09,0x3A,0xC3,0xE8,0x09,0x59,0x08,0x12,
0x94,0xD0,0x1A,0x2C,0x38,0x00,0xA1,0x83,0xE8,0x08,0x3A,0x08,0x10,0x9E,0x83,0x1D,
0x92,0x19,0x2C,0x39,0x3B,0x59,0x04,0xE1,0x80,0x08,0x8D,0x21,0x81,0xB2,0xB2,0x02,
0x99,0x91,0xA4,0xD6,0x98,0x99,0x03,0x80,0x98,0xA7,0x91,0x09,0xA1,0xB2,0xB3,0xE1,
0x12,0x92,0xB1,0x81,0x06,0x99,0x0A,0x23,0xC4,0xB1,0xF2,0x89,0x19,0x3A,0x94,0x82,
0xE0,0x89,0x38,0x0B,0xA4,0xA5,0x80,0x80,0x8C,0x34,0xB9,0xA9,0x23,0x13,0xB9,0xC1,
0xC7,0x1B,0x89,0x10,0x20,0x11,0xE3,0xA8,0x4B,0x0B,0x40,0x91,0x90,0x1B,0x5F,0x2A,
0x18,0x82,0x91,0x0B,0x4A,0x28,0xCA,0x40,0x80,0x5B,0x2C,0x13,0xB0,0x8A,0xA9,0x5A,
0x58,0x89,0x82,0x88,0x2E,0x3B,0x31,0xA1,0x9B,0x01,0x7A,0x2C,0x01,0x91,0x93,0x3F,
0x88,0x39,0x10,0xF1,0x91,0x8B,0x48,0x0A,0x12,0xE3,0xA8,0x18,0x28,0x92,0x97,0x98,
0x99,0x19,0xA1,0x11,0xB6,0x88,0x3B,0x10,0xD3,0xC3,0xA1,0x2A,0x8A,0x49,0x04,0xF1,
0x91,0x02,0x8A,0x89,0x04,0xF1,0x98,0x80,0x18,0x12,0xE3,0x81,0x98,0x80,0x01,0xB3,
0xF2,0x99,0x12,0x2A,0xB5,0xB3,0x92,0xAA,0x19,0x50,0xB2,0xC3,0x92,0xD0,0x2B,0x68,
0x93,0x99,0xC0,0x2C,0x3E,0x80,0x20,0x08,0x93,0x0D,0x2A,0x31,0x8D,0x02,0x2B,0x91,
0x08,0x0A,0x03,0x2C,0x3C,0x52,0xB9,0xA0,0x12,0xBF,0x3A,0x29,0x01,0x88,0xC0,0x6A,
0x3C,0x0A,0x49,0x18,0x0B,0x39,0x2B,0x69,0x0A,0x84,0x2A,0x2A,0x1C,0x2A,0xC3,0x8C,
0x19,0x50,0x09,0x91,0xA7,0x8D,0x18,0x1A,0x28,0x00,0xA0,0x94,0x10,0x1F,0x20,0x90,
0x8A,0x12,0xD0,0x1A,0x5A,0x81,0x04,0xBC,0x23,0x10,0xE0,0x90,0x90,0x18,0x1A,0xA6,
0x12,0xB1,0xD0,0x4A,0x08,0x82,0x92,0xB6,0x9A,0x0A,0x12,0x88,0xC3,0xC5,0x8A,0x89,
0x20,0xB5,0x93,0x0B,0x18,0x00,0x09,0xF2,0x88,0x2A,0x4A,0x08,0x05,0xB2,0xA9,0x3B,
0x5D,0x28,0xA4,0xB1,0x00,0x19,0x19,0x7A,0xA3,0xB3,0x0A,0x90,0xA1,0xC4,0x80,0xBA,
0x50,0x13,0xC1,0xC2,0x9A,0x2A,0x7B,0x28,0x84,0xC1,0x09,0x3B,0x4E,0x20,0x91,0xA1,
0x18,0xAB,0x79,0x10,0xB4,0x08,0x9A,0x11,0x2B,0xF0,0x93,0xAA,0x01,0x6A,0x01,0x93,
0x80,0xB8,0x2A,0x5B,0x10,0x80,0x89,0x4A,0x5B,0x92,0x15,0xB2,0xA0,0x2F,0x19,0x93,
0xB8,0x95,0x80,0x1C,0x21,0xA9,0x02,0x0B,0xA0,0x5A,0x18,0x98,0x39,0x1B,0x68,0x00,
0x91,0x91,0x9C,0x39,0x3E,0x18,0x84,0xB3,0x9B,0x7A,0x08,0x18,0x0A,0xB5,0x91,0x0B,
0x28,0x39,0x19,0x90,0x0A,0x50,0xAC,0x11,0x01,0xAB,0x88,0x52,0x1B,0x83,0xC4,0xA2,
0x9A,0xAB,0x03,0x90,0x19,0x93,0x81,0x08,0x92,0x9A,0x68,0x98,0x19,0x39,0xC1,0x92,
0x8A,0x38,0x4E,0x02,0xB1,0x90,0xC3,0x18,0x2B,0x04,0xC3,0xD2,0x91,0x90,0x81,0x89,
0x13,0xF1,0x88,0x93,0xA2,0x00,0x91,0xC0,0x5B,0x21,0x99,0x93,0x06,0x9A,0x1B,0x48,
0x99,0xB7,0x90,0x89,0x18,0x1B,0x11,0xA4,0xB2,0x81,0x9A,0x08,0x97,0x98,0x91,0x10,
0xB8,0x06,0xA2,0xA0,0x29,0x2B,0x21,0xC2,0xD1,0x10,0x1A,0x4A,0x29,0xF1,0x98,0x29,
0x1B,0x31,0x10,0xA0,0xA1,0x1D,0x5A,0x29,0xB2,0x82,0xA8,0x0F,0x28,0x21,0x09,0x91,
0x82,0x4D,0x10,0xA3,0xB0,0x89,0x4C,0x39,0xA0,0xA4,0xA1,0x89,0x1E,0x28,0x29,0xA3,
0xC3,0x2D,0x19,0x01,0x49,0x01,0x9B,0x0C,0x21,0xC2,0xA2,0x93,0x7C,0x2A,0x10,0x90,
/* Source: 08HH.ROM */
/* Length: 384 / 0x00000180 */
0x75,0xF2,0xAB,0x7D,0x7E,0x5C,0x3B,0x4B,0x3C,0x4D,0x4A,0x02,0xB3,0xC5,0xE7,0xE3,
0x92,0xB3,0xC4,0xB3,0xC3,0x8A,0x3B,0x5D,0x5C,0x3A,0x84,0xC2,0x91,0xA4,0xE7,0xF7,
0xF7,0xF4,0xA1,0x1B,0x49,0xA5,0xB1,0x1E,0x7F,0x5A,0x00,0x89,0x39,0xB7,0xA8,0x3D,
0x4A,0x84,0xE7,0xF7,0xE2,0x2D,0x4C,0x3A,0x4E,0x7D,0x04,0xB0,0x2D,0x4B,0x10,0x80,
0xA3,0x99,0x10,0x0E,0x59,0x93,0xC4,0xB1,0x81,0xC4,0xA2,0xB2,0x88,0x08,0x3F,0x3B,
0x28,0xA6,0xC3,0xA2,0xA2,0xC5,0xC1,0x3F,0x7E,0x39,0x81,0x93,0xC2,0xA3,0xE5,0xD2,
0x80,0x93,0xB8,0x6D,0x49,0x82,0xD4,0xA1,0x90,0x01,0xA0,0x09,0x04,0xE3,0xB2,0x91,
0xB7,0xB3,0xA8,0x2A,0x03,0xF3,0xA1,0x92,0xC5,0xC3,0xB2,0x0B,0x30,0xB3,0x8E,0x6D,
0x4A,0x01,0xB4,0xB4,0xC4,0xC3,0x99,0x3B,0x12,0xE3,0xA1,0x88,0x82,0xB4,0x9A,0x5C,
0x3A,0x18,0x93,0xC3,0xB3,0xB4,0xA8,0x19,0x04,0xF3,0xA8,0x3B,0x10,0xA2,0x88,0xA5,
0xB2,0x0B,0x6D,0x4B,0x10,0x91,0x89,0x3C,0x18,0x18,0xA6,0xC4,0xC3,0x98,0x19,0x2B,
0x20,0x91,0xA0,0x4E,0x28,0x93,0xB3,0xC2,0x92,0xA9,0x5A,0x96,0xC4,0xC2,0x09,0x01,
0xC4,0xA1,0x92,0xC4,0xA1,0x89,0x10,0xA3,0xA1,0x90,0x1C,0x5A,0x01,0xC5,0xA1,0x92,
0xD4,0xB3,0xC4,0xC4,0xC3,0xA1,0x88,0x1A,0x28,0x89,0x3C,0x3A,0x3D,0x29,0x00,0x93,
0xB0,0x3D,0x28,0x80,0x91,0x82,0xE3,0x99,0x2A,0x11,0xD6,0xC3,0x99,0x29,0x82,0xC4,
0xC3,0xA1,0x0A,0x3B,0x3D,0x3A,0x02,0xC3,0xA2,0x99,0x3B,0x2C,0x7C,0x28,0x81,0xA3,
0xB2,0xA3,0xB1,0x08,0x1A,0x3C,0x18,0x2E,0x4C,0x39,0xA5,0xB3,0xB4,0xC2,0x88,0x08,
0x19,0x0A,0x49,0xB7,0xB3,0xA2,0xA1,0x92,0xA1,0x93,0xB1,0x0C,0x7D,0x39,0x93,0xB3,
0xB1,0x1A,0x19,0x5D,0x28,0xA6,0xC4,0xB2,0x90,0x09,0x2A,0x18,0x1B,0x5B,0x28,0x88,
0x2C,0x29,0x82,0xA0,0x18,0x91,0x2D,0x29,0x2B,0x5C,0x4C,0x3B,0x4C,0x28,0x80,0x92,
0x90,0x09,0x2B,0x28,0x1D,0x6B,0x11,0xC5,0xB2,0x0B,0x39,0x09,0x4D,0x28,0x88,0x00,
0x1B,0x28,0x94,0xE3,0xA0,0x1A,0x28,0xB5,0xB4,0xB3,0xB2,0x93,0xE2,0x91,0x92,0xD4,
0xA0,0x1B,0x4A,0x01,0xA1,0x88,0x2D,0x5C,0x3B,0x28,0x08,0x93,0xD4,0xB2,0x91,0xB4,
0xA0,0x3E,0x3B,0x4B,0x3B,0x29,0x08,0x93,0x9B,0x7B,0x3A,0x19,0x00,0x80,0x80,0xA0,
/* Source: 10TOM.ROM */
/* Length: 640 / 0x00000280 */
0x77,0x27,0x87,0x01,0x2D,0x4F,0xC3,0xC1,0x92,0x91,0x89,0x59,0x83,0x1A,0x32,0xC2,
0x95,0xB1,0x81,0x88,0x81,0x4A,0x3D,0x11,0x9E,0x0B,0x88,0x0C,0x18,0x3B,0x11,0x11,
0x91,0x00,0xA0,0xE2,0x0A,0x48,0x13,0x24,0x81,0x48,0x1B,0x39,0x1C,0x83,0x84,0xA1,
0xD1,0x8E,0x8A,0x0B,0xC0,0x98,0x92,0xB8,0x39,0x90,0x10,0x92,0xF0,0xB5,0x88,0x32,
0x49,0x51,0x21,0x03,0x82,0x10,0x8A,0x7A,0x09,0x00,0xA2,0xCA,0x1B,0xCC,0x1C,0xB9,
0x8E,0x89,0x89,0xA1,0x89,0x92,0x29,0x11,0x60,0x40,0x14,0x22,0x32,0x78,0x40,0x01,
0x02,0x90,0x81,0xAB,0x0B,0x00,0xAF,0x99,0xCC,0xAB,0xDA,0xA9,0x99,0x1B,0x30,0x14,
0x92,0x22,0x19,0x68,0x32,0x14,0x26,0x13,0x23,0x23,0x20,0x12,0x9A,0xA8,0xB9,0xFA,
0xAA,0xCA,0xCC,0x0C,0xA8,0xAE,0x88,0xB9,0x88,0xA0,0x02,0x21,0x50,0x43,0x03,0x81,
0x2A,0x11,0x34,0x63,0x24,0x33,0x22,0x38,0x8B,0xEA,0xAE,0x99,0xA0,0x90,0x82,0x00,
0x89,0xBF,0x8A,0xE8,0xA9,0x90,0x01,0x12,0x13,0x12,0x08,0xA9,0xAA,0xC9,0x22,0x63,
0x63,0x12,0x44,0x00,0x10,0x88,0x9C,0x98,0xA1,0x85,0x03,0x32,0x36,0x80,0x89,0xDB,
0xDB,0xBB,0xB9,0xBA,0x01,0x81,0x28,0x19,0xCB,0xFA,0xBC,0x09,0x13,0x37,0x34,0x34,
0x23,0x31,0x20,0x10,0x00,0x00,0x28,0x38,0x10,0x88,0xEC,0x8D,0xCB,0xBC,0xCC,0xBB,
0xBB,0xC9,0x99,0x00,0x00,0x33,0x11,0x22,0x81,0x07,0x41,0x54,0x34,0x34,0x22,0x31,
0x00,0x88,0x9A,0x9B,0x98,0xAB,0x8E,0x9B,0xBD,0x9C,0xBC,0xBB,0xDA,0xAA,0xA9,0x99,
0x18,0x38,0x60,0x20,0x31,0x13,0x13,0x51,0x14,0x31,0x53,0x33,0x35,0x22,0x01,0x8A,
0x9C,0xA9,0xCA,0xC9,0xA8,0x00,0x10,0x81,0x9C,0x9E,0xAB,0xCC,0xAB,0xBA,0x98,0x30,
0x52,0x03,0x81,0x08,0x9C,0xAC,0xAC,0x18,0x11,0x03,0x51,0x61,0x41,0x31,0x31,0x02,
0x01,0x20,0x24,0x43,0x44,0x40,0x30,0x10,0xBC,0xBE,0xCB,0xDB,0xAB,0xBA,0x99,0x98,
0x99,0xAA,0xBD,0xAA,0xC8,0x90,0x11,0x53,0x37,0x23,0x43,0x34,0x33,0x33,0x33,0x11,
0x28,0x00,0x19,0xA9,0x9A,0xCB,0xCE,0xBB,0xEB,0xBC,0xBB,0xCA,0xBA,0xA8,0x88,0x11,
0x12,0x21,0x20,0x22,0x26,0x26,0x23,0x23,0x43,0x24,0x22,0x32,0x20,0x31,0x81,0x9A,
0xBC,0xBC,0xCB,0xBD,0x9A,0xA9,0x90,0x98,0xBA,0xCC,0xCB,0xBC,0x8B,0x88,0x22,0x35,
0x23,0x12,0x99,0x8B,0xAA,0xAA,0x89,0x82,0x93,0x31,0x42,0x23,0x23,0x21,0x32,0x11,
0x20,0x13,0x13,0x24,0x24,0x24,0x22,0x11,0x8A,0x9E,0xAC,0xAC,0xAA,0xBA,0xAA,0xAB,
0xBD,0xBC,0xCB,0xCB,0xA9,0xA8,0x91,0x12,0x44,0x43,0x44,0x34,0x34,0x42,0x33,0x42,
0x21,0x11,0x11,0x88,0x80,0xAA,0x0B,0xAC,0xCB,0xEC,0xAC,0xBA,0xCA,0xAB,0x9A,0x99,
0x80,0x91,0x09,0x08,0x10,0x22,0x44,0x43,0x44,0x33,0x43,0x22,0x13,0x21,0x22,0x20,
0x09,0x88,0xB9,0xC8,0xBB,0xAB,0xAB,0xA9,0xA9,0x9B,0x9B,0x99,0x90,0x90,0x00,0x81,
0x00,0x08,0x09,0x8A,0x9A,0xAA,0xA9,0xA9,0x99,0x90,0x80,0x01,0x80,0x00,0x09,0x31,
0x32,0x44,0x33,0x43,0x34,0x33,0x24,0x22,0x23,0x12,0x10,0x09,0x9B,0xAB,0xCA,0xCC,
0xBB,0xCB,0xDA,0xCA,0xAB,0xCA,0xAB,0xA9,0xA8,0x92,0x12,0x43,0x53,0x35,0x23,0x33,
0x43,0x43,0x52,0x22,0x22,0x21,0x01,0x09,0x89,0xA9,0xBB,0xBD,0xBC,0xCB,0xDA,0xAB,
0xAB,0xAB,0xAA,0xA9,0x99,0xA8,0x09,0x01,0x11,0x34,0x25,0x23,0x33,0x51,0x22,0x31,
0x12,0x20,0x21,0x12,0x10,0x80,0x99,0x9A,0x99,0x99,0x88,0x08,0x00,0x88,0xA9,0x99,
0x99,0x80,0x80,0x10,0x01,0x00,0x9A,0xAA,0xBB,0xBA,0xBA,0xA9,0x99,0x99,0x89,0x99,
0x99,0x00,0x01,0x33,0x35,0x24,0x23,0x34,0x23,0x33,0x34,0x33,0x43,0x32,0x21,0x88,
0xAB,0xBD,0xBB,0xDB,0xAB,0xBA,0xBB,0xDA,0xBB,0xCB,0xBB,0xBC,0xA8,0x90,0x01,0x12,
0x23,0x43,0x53,0x34,0x34,0x39,0x80,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,
/* Source: 20RIM.ROM */
/* Length: 128 / 0x00000080 */
0x0F,0xFF,0x73,0x8E,0x71,0xCD,0x00,0x49,0x10,0x90,0x21,0x49,0xA0,0xDB,0x02,0x3A,
0xE3,0x0A,0x50,0x98,0xC0,0x59,0xA2,0x99,0x09,0x22,0xA2,0x80,0x10,0xA8,0x5B,0xD2,
0x88,0x21,0x09,0x96,0xA8,0x10,0x0A,0xE0,0x08,0x48,0x19,0xAB,0x52,0xA8,0x92,0x0C,
0x03,0x19,0xE2,0x0A,0x12,0xC2,0x81,0x1E,0x01,0xD0,0x48,0x88,0x98,0x01,0x49,0x91,
0xAA,0x2C,0x25,0x89,0x88,0xB5,0x81,0xA2,0x9A,0x12,0x9E,0x38,0x3B,0x81,0x9B,0x59,
0x01,0x93,0xCA,0x4A,0x21,0xA0,0x3D,0x0A,0x39,0x3D,0x12,0xA8,0x3F,0x18,0x01,0x92,
0x1C,0x00,0xB2,0x48,0xB9,0x94,0xA3,0x19,0x4F,0x19,0xB2,0x32,0x90,0xBA,0x01,0xE6,
0x91,0x80,0xC1,0xA4,0x2A,0x08,0xA1,0xB1,0x25,0xD2,0x88,0x99,0x21,0x80,0x88,0x80,
};

View file

@ -465,6 +465,8 @@ public:
// generate one sample of sound // generate one sample of sound
void generate(output_data *output, uint32_t numsamples = 1); void generate(output_data *output, uint32_t numsamples = 1);
fm_engine* debug_fm_engine() { return &m_fm; }
protected: protected:
// internal helpers // internal helpers
void update_prescale(uint8_t prescale); void update_prescale(uint8_t prescale);
@ -546,6 +548,12 @@ public:
// generate one sample of sound // generate one sample of sound
void generate(output_data *output, uint32_t numsamples = 1); void generate(output_data *output, uint32_t numsamples = 1);
// get the engine
fm_engine* debug_fm_engine() { return &m_fm; }
ssg_engine* debug_ssg_engine() { return &m_ssg; }
adpcm_a_engine* debug_adpcm_a_engine() { return &m_adpcm_a; }
adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; }
protected: protected:
// internal helpers // internal helpers
void update_prescale(uint8_t prescale); void update_prescale(uint8_t prescale);

View file

@ -186,7 +186,8 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
if (chan[i].pcm) { if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
DivSample* sample=parent->getSample(ins->amiga.initSample); // TODO: sample map?
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
if (sample!=NULL) { if (sample!=NULL) {
double off=0.25; double off=0.25;
if (sample->centerRate<1) { if (sample->centerRate<1) {
@ -204,7 +205,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
if (chan[i].keyOn) { if (chan[i].keyOn) {
if (chan[i].pcm) { if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
DivSample* sample=parent->getSample(ins->amiga.initSample); DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
if (sample!=NULL) { if (sample!=NULL) {
unsigned int sampleEnd=sample->offSU+sample->samples; unsigned int sampleEnd=sample->offSU+sample->samples;
if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1; if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1;

View file

@ -263,7 +263,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
dacPos=0; dacPos=0;
dacPeriod=0; dacPeriod=0;
if (ins->type==DIV_INS_AMIGA) { if (ins->type==DIV_INS_AMIGA) {
dacSample=ins->amiga.initSample; dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) { if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1; dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) addWrite(0xffff0002,0);
@ -448,6 +448,7 @@ void DivPlatformSwan::muteChannel(int ch, bool mute) {
} }
void DivPlatformSwan::forceIns() { void DivPlatformSwan::forceIns() {
noise=0;
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
chan[i].freqChanged=true; chan[i].freqChanged=true;

View file

@ -260,7 +260,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
if (c.chan<16) { if (c.chan<16) {
rWriteLo(c.chan,2,chan[c.chan].vol); rWriteLo(c.chan,2,chan[c.chan].vol);
} else { } else {
chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.initSample; chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.getSample(c.value);
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) { if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
chan[16].pcm.sample=-1; chan[16].pcm.sample=-1;
} }
@ -414,7 +414,7 @@ void DivPlatformVERA::muteChannel(int ch, bool mute) {
} }
float DivPlatformVERA::getPostAmp() { float DivPlatformVERA::getPostAmp() {
return 8.0f; return 4.0f;
} }
bool DivPlatformVERA::isStereo() { bool DivPlatformVERA::isStereo() {

View file

@ -243,7 +243,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
if (chan[c.chan].pcm) { if (chan[c.chan].pcm) {
if (skipRegisterWrites) break; if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].dacSample=ins->amiga.initSample; chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1; chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);

View file

@ -559,7 +559,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
if (chan[c.chan].furnacePCM) { if (chan[c.chan].furnacePCM) {
chan[c.chan].pcm=true; chan[c.chan].pcm=true;
chan[c.chan].macroInit(ins); chan[c.chan].macroInit(ins);
chan[c.chan].sample=ins->amiga.initSample; chan[c.chan].sample=ins->amiga.getSample(c.value);
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) { if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample); DivSample* s=parent->getSample(chan[c.chan].sample);
chWrite(c.chan,4,(s->offX1_010>>12)&0xff); chWrite(c.chan,4,(s->offX1_010>>12)&0xff);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,132 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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.
*/
#ifndef _YM2203_H
#define _YM2203_H
#include "../dispatch.h"
#include "../macroInt.h"
#include <queue>
#include "sound/ymfm/ymfm_opn.h"
#include "ay.h"
class DivYM2203Interface: public ymfm::ymfm_interface {
};
class DivPlatformYM2203: public DivDispatch {
protected:
const unsigned short chanOffs[3]={
0x00, 0x01, 0x02
};
struct Channel {
DivInstrumentFM state;
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
unsigned char psgMode, autoEnvNum, autoEnvDen;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
int vol, outVol;
int sample;
DivMacroInt std;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
}
Channel():
freqH(0),
freqL(0),
freq(0),
baseFreq(0),
pitch(0),
pitch2(0),
portaPauseFreq(0),
note(0),
ins(-1),
psgMode(1),
autoEnvNum(0),
autoEnvDen(0),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
portaPause(false),
inPorta(false),
furnacePCM(false),
hardReset(false),
vol(0),
outVol(15),
sample(-1) {}
};
Channel chan[6];
DivDispatchOscBuffer* oscBuf[6];
bool isMuted[6];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
ymfm::ym2203* fm;
ymfm::ym2203::output_data fmout;
DivYM2203Interface iface;
unsigned char regPool[512];
unsigned char lastBusy;
DivPlatformAY8910* ay;
unsigned char sampleBank;
int delay;
bool extMode;
short oldWrites[256];
short pendingWrites[256];
friend void putDispatchChan(void*,int,int);
public:
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void setSkipRegisterWrites(bool val);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
void setFlags(unsigned int flags);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformYM2203();
};
#endif

View file

@ -0,0 +1,564 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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 "ym2203ext.h"
#include "../engine.h"
#include <math.h>
#include "ym2610shared.h"
#include "fmshared_OPN.h"
int DivPlatformYM2203Ext::dispatch(DivCommand c) {
if (c.chan<2) {
return DivPlatformYM2203::dispatch(c);
}
if (c.chan>5) {
c.chan-=3;
return DivPlatformYM2203::dispatch(c);
}
int ch=c.chan-2;
int ordch=orderedOps[ch];
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
// TODO: how does this work?!
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
}
}
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+0x70,op.d2r&31);
rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+0x90,op.ssgEnv&15);
}
if (opChan[ch].insChanged) { // TODO how does this work?
rWrite(chanOffs[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
}
opChan[ch].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
opChan[ch].portaPause=false;
opChan[ch].freqChanged=true;
}
opChan[ch].keyOn=true;
opChan[ch].active=true;
break;
}
case DIV_CMD_NOTE_OFF:
opChan[ch].keyOff=true;
opChan[ch].keyOn=false;
opChan[ch].active=false;
break;
case DIV_CMD_VOLUME: {
opChan[ch].vol=c.value;
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
}
break;
}
case DIV_CMD_GET_VOLUME: {
return opChan[ch].vol;
break;
}
case DIV_CMD_INSTRUMENT:
if (opChan[ch].ins!=c.value || c.value2==1) {
opChan[ch].insChanged=true;
}
opChan[ch].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.value==0 && c.value2==0) {
opChan[ch].pan=3;
} else {
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
}
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (parent->song.sharedExtStat) {
for (int i=0; i<4; i++) {
if (ch==i) continue;
opChan[i].pan=opChan[ch].pan;
}
}
rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
break;
}
case DIV_CMD_PITCH: {
opChan[ch].pitch=c.value;
opChan[ch].freqChanged=true;
break;
}
case DIV_CMD_NOTE_PORTA: {
if (parent->song.linearPitch==2) {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>opChan[ch].baseFreq) {
opChan[ch].baseFreq+=c.value;
if (opChan[ch].baseFreq>=destFreq) {
opChan[ch].baseFreq=destFreq;
return2=true;
}
} else {
opChan[ch].baseFreq-=c.value;
if (opChan[ch].baseFreq<=destFreq) {
opChan[ch].baseFreq=destFreq;
return2=true;
}
}
opChan[ch].freqChanged=true;
if (return2) {
//opChan[ch].inPorta=false;
return 2;
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (opChan[ch].portaPause) {
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
}
if (destFreq>opChan[ch].baseFreq) {
newFreq=opChan[ch].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=opChan[ch].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// what the heck!
if (!opChan[ch].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
}
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
}
}
}
opChan[ch].portaPause=false;
opChan[ch].freqChanged=true;
opChan[ch].baseFreq=newFreq;
if (return2) return 2;
break;
}
case DIV_CMD_LEGATO: {
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
opChan[ch].freqChanged=true;
break;
}
case DIV_CMD_FM_LFO: {
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
break;
}
case DIV_CMD_FM_MULT: { // TODO
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
break;
}
case DIV_CMD_FM_TL: { // TODO
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (isOutput[ins->fm.alg][c.value]) {
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
} else {
rWrite(baseAddr+0x40,c.value2);
}
break;
}
case DIV_CMD_FM_AR: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.ar=c.value2&31;
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
}
} else {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.ar=c.value2&31;
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
}
break;
}
case DIV_CMD_FM_RS: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.rs=c.value2&3;
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.rs=c.value2&3;
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
}
break;
}
case DIV_CMD_FM_AM: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.am=c.value2&1;
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.am=c.value2&1;
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
break;
}
case DIV_CMD_FM_DR: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.dr=c.value2&31;
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.dr=c.value2&31;
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
break;
}
case DIV_CMD_FM_SL: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.sl=c.value2&15;
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.sl=c.value2&15;
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
break;
}
case DIV_CMD_FM_RR: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.rr=c.value2&15;
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.rr=c.value2&15;
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
break;
}
case DIV_CMD_FM_D2R: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.d2r=c.value2&31;
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.d2r=c.value2&31;
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
}
break;
}
case DIV_CMD_FM_DT: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.dt=c.value&7;
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.dt=c.value2&7;
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
}
break;
}
case DIV_CMD_FM_SSG: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[2].state.op[i];
op.ssgEnv=8^(c.value2&15);
unsigned short baseAddr=chanOffs[2]|opOffs[i];
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.ssgEnv=8^(c.value2&15);
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
break;
}
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;
case DIV_CMD_PRE_PORTA:
break;
default:
//printf("WARNING: unimplemented command %d\n",c.cmd);
break;
}
return 1;
}
static int opChanOffsL[4]={
0xa9, 0xaa, 0xa8, 0xa2
};
static int opChanOffsH[4]={
0xad, 0xae, 0xac, 0xa6
};
void DivPlatformYM2203Ext::tick(bool sysTick) {
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
for (int i=0; i<4; i++) {
writeMask|=opChan[i].active<<(4+i);
if (opChan[i].keyOn || opChan[i].keyOff) {
writeSomething=true;
writeMask&=~(1<<(4+i));
opChan[i].keyOff=false;
}
}
if (writeSomething) {
immWrite(0x28,writeMask);
}
}
DivPlatformYM2203::tick(sysTick);
bool writeNoteOn=false;
unsigned char writeMask=2;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch==2) {
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11);
} else {
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2);
int block=(opChan[i].baseFreq&0xf800)>>11;
if (fNum<0) fNum=0;
if (fNum>2047) {
while (block<7) {
fNum>>=1;
block++;
}
if (fNum>2047) fNum=2047;
}
opChan[i].freq=(block<<11)|fNum;
}
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
immWrite(opChanOffsH[i],opChan[i].freq>>8);
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
}
writeMask|=opChan[i].active<<(4+i);
if (opChan[i].keyOn) {
writeNoteOn=true;
writeMask|=1<<(4+i);
opChan[i].keyOn=false;
}
}
if (writeNoteOn) {
immWrite(0x28,writeMask);
}
}
void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
if (ch<2) {
DivPlatformYM2203::muteChannel(ch,mute);
return;
}
if (ch>5) {
DivPlatformYM2203::muteChannel(ch-3,mute);
return;
}
isOpMuted[ch-2]=mute;
int ordch=orderedOps[ch-2];
DivInstrument* ins=parent->getIns(opChan[ch-2].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOpMuted[ch-2]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
}
void DivPlatformYM2203Ext::forceIns() {
for (int i=0; i<3; i++) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
if (chan[i].active) {
chan[i].keyOn=true;
chan[i].freqChanged=true;
}
}
for (int i=3; i<6; i++) {
chan[i].insChanged=true;
}
ay->forceIns();
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
immWrite(i.addr&15,i.val);
}
ay->getRegisterWrites().clear();
for (int i=0; i<4; i++) {
opChan[i].insChanged=true;
if (opChan[i].active) {
opChan[i].keyOn=true;
opChan[i].freqChanged=true;
}
}
}
void* DivPlatformYM2203Ext::getChanState(int ch) {
if (ch>=6) return &chan[ch-3];
if (ch>=2) return &opChan[ch-2];
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) {
if (ch>=6) return oscBuf[ch-3];
if (ch<3) return oscBuf[ch];
return NULL;
}
void DivPlatformYM2203Ext::reset() {
DivPlatformYM2203::reset();
for (int i=0; i<4; i++) {
opChan[i]=DivPlatformYM2203Ext::OpChannel();
opChan[i].vol=127;
}
// channel 2 mode
immWrite(0x27,0x40);
extMode=true;
}
bool DivPlatformYM2203Ext::keyOffAffectsArp(int ch) {
return (ch>8);
}
void DivPlatformYM2203Ext::notifyInsChange(int ins) {
DivPlatformYM2203::notifyInsChange(ins);
for (int i=0; i<4; i++) {
if (opChan[i].ins==ins) {
opChan[i].insChanged=true;
}
}
}
int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, unsigned int flags) {
DivPlatformYM2203::init(parent,channels,sugRate,flags);
for (int i=0; i<4; i++) {
isOpMuted[i]=false;
}
reset();
return 19;
}
void DivPlatformYM2203Ext::quit() {
DivPlatformYM2203::quit();
}
DivPlatformYM2203Ext::~DivPlatformYM2203Ext() {
}

View file

@ -0,0 +1,51 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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 "../dispatch.h"
#include "ym2203.h"
class DivPlatformYM2203Ext: public DivPlatformYM2203 {
struct OpChannel {
DivMacroInt std;
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
int vol;
unsigned char pan;
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
};
OpChannel opChan[4];
bool isOpMuted[4];
friend void putDispatchChan(void*,int,int);
public:
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformYM2203Ext();
};

View file

@ -0,0 +1,45 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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.
*/
static unsigned short opOffs[4]={
0x00, 0x04, 0x08, 0x0c
};
static bool isOutput[8][4]={
// 1 3 2 4
{false,false,false,true},
{false,false,false,true},
{false,false,false,true},
{false,false,false,true},
{false,false,true ,true},
{false,true ,true ,true},
{false,true ,true ,true},
{true ,true ,true ,true},
};
static unsigned char dtTable[8]={
7,6,5,0,1,2,3,4
};
static int orderedOps[4]={
0,2,1,3
};
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_FREQBASE 4720270

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,148 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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.
*/
#ifndef _YM2608_H
#define _YM2608_H
#include "../dispatch.h"
#include "../macroInt.h"
#include <queue>
#include "sound/ymfm/ymfm_opn.h"
#include "ay.h"
class DivYM2608Interface: public ymfm::ymfm_interface {
public:
unsigned char* adpcmBMem;
int sampleBank;
uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address);
void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data);
DivYM2608Interface(): adpcmBMem(NULL), sampleBank(0) {}
};
class DivPlatformYM2608: public DivDispatch {
protected:
const unsigned short chanOffs[6]={
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
};
struct Channel {
DivInstrumentFM state;
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
unsigned char psgMode, autoEnvNum, autoEnvDen;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
int vol, outVol;
int sample;
unsigned char pan;
DivMacroInt std;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
}
Channel():
freqH(0),
freqL(0),
freq(0),
baseFreq(0),
pitch(0),
pitch2(0),
portaPauseFreq(0),
note(0),
ins(-1),
psgMode(1),
autoEnvNum(0),
autoEnvDen(0),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
portaPause(false),
inPorta(false),
furnacePCM(false),
hardReset(false),
vol(0),
outVol(15),
sample(-1),
pan(3) {}
};
Channel chan[16];
DivDispatchOscBuffer* oscBuf[16];
bool isMuted[16];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
ymfm::ym2608* fm;
ymfm::ym2608::output_data fmout;
unsigned char regPool[512];
unsigned char lastBusy;
unsigned char* adpcmBMem;
size_t adpcmBMemLen;
DivYM2608Interface iface;
DivPlatformAY8910* ay;
unsigned char sampleBank;
unsigned char writeRSSOff, writeRSSOn;
int delay;
bool extMode;
short oldWrites[512];
short pendingWrites[512];
double NOTE_OPNB(int ch, int note);
double NOTE_ADPCMB(int note);
friend void putDispatchChan(void*,int,int);
public:
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void setSkipRegisterWrites(bool val);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index);
void renderSamples();
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformYM2608();
};
#endif

View file

@ -0,0 +1,42 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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 "sound/ymfm/ymfm.h"
#include "ym2608.h"
#include "../engine.h"
#include "sound/rss.h"
uint8_t DivYM2608Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) {
switch (type) {
case ymfm::ACCESS_ADPCM_A:
return YM2608_ADPCM_ROM[address&0x1fff];
case ymfm::ACCESS_ADPCM_B:
if (adpcmBMem==NULL) {
return 0;
}
return adpcmBMem[address&0x3ffff];
default:
return 0;
}
return 0;
}
void DivYM2608Interface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
}

View file

@ -470,7 +470,7 @@ double DivPlatformYM2610::NOTE_ADPCMB(int note) {
void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) {
static int os[2]; static int os[2];
ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); ymfm::ym2610::fm_engine* fme=fm->debug_fm_engine();
ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine();
ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine();
@ -795,7 +795,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol; chan[c.chan].outVol=chan[c.chan].vol;
immWrite(0x1b,chan[c.chan].outVol); immWrite(0x1b,chan[c.chan].outVol);
} }
chan[c.chan].sample=ins->amiga.initSample; chan[c.chan].sample=ins->amiga.getSample(c.value);
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) { if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample); DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(0x12,(s->offB>>8)&0xff); immWrite(0x12,(s->offB>>8)&0xff);
@ -839,9 +839,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
immWrite(0x14,(end>>8)&0xff); immWrite(0x14,(end>>8)&0xff);
immWrite(0x15,end>>16); immWrite(0x15,end>>16);
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
immWrite(0x10,((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && (s->loopStart>=0))?0x90:0x80); // start/repeat immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
chan[c.chan].freqChanged=true; immWrite(0x19,freq&0xff);
immWrite(0x1a,(freq>>8)&0xff);
} }
break; break;
} }
@ -997,6 +998,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_PITCH: { case DIV_CMD_PITCH: {
if (c.chan==13 && !chan[c.chan].furnacePCM) break;
chan[c.chan].pitch=c.value; chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;
@ -1077,6 +1079,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
iface.sampleBank=sampleBank; iface.sampleBank=sampleBank;
break; break;
case DIV_CMD_LEGATO: { case DIV_CMD_LEGATO: {
if (c.chan==13 && !chan[c.chan].furnacePCM) break;
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;

View file

@ -450,7 +450,7 @@ double DivPlatformYM2610B::NOTE_ADPCMB(int note) {
void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) {
static int os[2]; static int os[2];
ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); ymfm::ym2610b::fm_engine* fme=fm->debug_fm_engine();
ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine();
ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine();
@ -773,7 +773,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol; chan[c.chan].outVol=chan[c.chan].vol;
immWrite(0x1b,chan[c.chan].outVol); immWrite(0x1b,chan[c.chan].outVol);
} }
chan[c.chan].sample=ins->amiga.initSample; chan[c.chan].sample=ins->amiga.getSample(c.value);
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) { if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample); DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(0x12,(s->offB>>8)&0xff); immWrite(0x12,(s->offB>>8)&0xff);
@ -817,9 +817,10 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
immWrite(0x14,(end>>8)&0xff); immWrite(0x14,(end>>8)&0xff);
immWrite(0x15,end>>16); immWrite(0x15,end>>16);
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
immWrite(0x10,((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && (s->loopStart>=0))?0x90:0x80); // start/repeat immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
chan[c.chan].freqChanged=true; immWrite(0x19,freq&0xff);
immWrite(0x1a,(freq>>8)&0xff);
} }
break; break;
} }
@ -975,6 +976,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_PITCH: { case DIV_CMD_PITCH: {
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
chan[c.chan].pitch=c.value; chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;
@ -1055,6 +1057,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
iface.sampleBank=sampleBank; iface.sampleBank=sampleBank;
break; break;
case DIV_CMD_LEGATO: { case DIV_CMD_LEGATO: {
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;

View file

@ -0,0 +1,334 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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 "zxbeeper.h"
#include "../engine.h"
#include <math.h>
#define CHIP_FREQBASE 8192*6
const char** DivPlatformZXBeeper::getRegisterSheet() {
return NULL;
}
const char* DivPlatformZXBeeper::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
case 0x11:
return "11xx: Toggle noise mode";
break;
case 0x12:
return "12xx: Setup LFO (0: disabled; 1: 1x depth; 2: 16x depth; 3: 256x depth)";
break;
case 0x13:
return "13xx: Set LFO speed";
break;
case 0x17:
return "17xx: Toggle PCM mode";
break;
}
return NULL;
}
void DivPlatformZXBeeper::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
// clock here
bool o=false;
unsigned short oldPos=chan[curChan].sPosition;
if (sOffTimer) {
sOffTimer--;
o=true;
}
chan[curChan].sPosition+=chan[curChan].freq;
if (oldPos>chan[curChan].sPosition) {
if (!isMuted[curChan] && chan[curChan].outVol) sOffTimer+=chan[curChan].duty;
}
if (++curChan>=6) curChan=0;
bufL[h]=o?16384:0;
}
}
void DivPlatformZXBeeper::tick(bool sysTick) {
for (int i=0; i<6; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&1)*MIN(1,chan[i].std.vol.val));
}
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
chan[i].freqChanged=true;
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
}
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
if (chan[i].active) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,0,chan[i].pitch2,chipClock,CHIP_FREQBASE);
if (chan[i].freq>65535) chan[i].freq=65535;
}
if (chan[i].keyOn) {
//rWrite(16+i*5,0x80);
//chWrite(i,0x04,0x80|chan[i].vol);
}
if (chan[i].keyOff) {
chan[i].freq=0;
}
if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false;
}
}
}
int DivPlatformZXBeeper::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
chan[c.chan].insChanged=false;
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
chan[c.chan].pcm=false;
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].ins=c.value;
chan[c.chan].insChanged=true;
}
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
}
break;
case DIV_CMD_GET_VOLUME:
if (chan[c.chan].std.vol.has) {
return chan[c.chan].vol;
}
return chan[c.chan].outVol;
break;
case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
case DIV_CMD_STD_NOISE_MODE:
chan[c.chan].duty=c.value;
break;
case DIV_CMD_SAMPLE_MODE:
chan[c.chan].pcm=c.value;
break;
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
}
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 1;
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformZXBeeper::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
}
void DivPlatformZXBeeper::forceIns() {
for (int i=0; i<6; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
}
}
void* DivPlatformZXBeeper::getChanState(int ch) {
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformZXBeeper::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformZXBeeper::getRegisterPool() {
ulaOut=sOffTimer?0x10:0x08;
return &ulaOut;
}
int DivPlatformZXBeeper::getRegisterPoolSize() {
return 1;
}
void DivPlatformZXBeeper::reset() {
while (!writes.empty()) writes.pop();
memset(regPool,0,128);
for (int i=0; i<6; i++) {
chan[i]=DivPlatformZXBeeper::Channel();
chan[i].std.setEngine(parent);
}
if (dumpWrites) {
addWrite(0xffffffff,0);
}
lastPan=0xff;
memset(tempL,0,32*sizeof(int));
memset(tempR,0,32*sizeof(int));
cycles=0;
curChan=0;
sOffTimer=0;
sampleBank=0;
ulaOut=0;
}
bool DivPlatformZXBeeper::keyOffAffectsArp(int ch) {
return true;
}
void DivPlatformZXBeeper::notifyWaveChange(int wave) {
}
void DivPlatformZXBeeper::notifyInsDeletion(void* ins) {
for (int i=0; i<6; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformZXBeeper::setFlags(unsigned int flags) {
if (flags&1) { // technically there is no PAL PC Engine but oh well...
chipClock=COLOR_PAL*4.0/5.0;
} else {
chipClock=COLOR_NTSC;
}
rate=chipClock/4;
for (int i=0; i<6; i++) {
oscBuf[i]->rate=rate;
}
}
void DivPlatformZXBeeper::poke(unsigned int addr, unsigned short val) {
}
void DivPlatformZXBeeper::poke(std::vector<DivRegWrite>& wlist) {
}
int DivPlatformZXBeeper::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<6; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
setFlags(flags);
reset();
return 6;
}
void DivPlatformZXBeeper::quit() {
for (int i=0; i<6; i++) {
delete oscBuf[i];
}
}
DivPlatformZXBeeper::~DivPlatformZXBeeper() {
}

View file

@ -0,0 +1,108 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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.
*/
#ifndef _ZXBEEPER_H
#define _ZXBEEPER_H
#include "../dispatch.h"
#include <queue>
#include "../macroInt.h"
class DivPlatformZXBeeper: public DivDispatch {
struct Channel {
int freq, baseFreq, pitch, pitch2, note;
int dacPeriod, dacRate;
unsigned int dacPos;
int dacSample, ins;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac;
signed char vol, outVol;
unsigned short sPosition;
unsigned char duty;
DivMacroInt std;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
}
Channel():
freq(0),
baseFreq(0),
pitch(0),
pitch2(0),
note(0),
dacPeriod(0),
dacRate(0),
dacPos(0),
dacSample(-1),
ins(-1),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
inPorta(false),
noise(false),
pcm(false),
furnaceDac(false),
vol(1),
outVol(1),
sPosition(0),
duty(64) {}
};
Channel chan[6];
DivDispatchOscBuffer* oscBuf[6];
bool isMuted[6];
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
unsigned char lastPan, ulaOut;
int cycles, curChan, sOffTimer, delay;
int tempL[32];
int tempR[32];
unsigned char sampleBank, lfoMode, lfoSpeed;
unsigned char regPool[128];
friend void putDispatchChan(void*,int,int);
public:
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void setFlags(unsigned int flags);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformZXBeeper();
};
#endif

View file

@ -30,7 +30,7 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
void DivEngine::nextOrder() { void DivEngine::nextOrder() {
curRow=0; curRow=0;
if (repeatPattern) return; if (repeatPattern) return;
if (++curOrder>=song.ordersLen) { if (++curOrder>=curSubSong->ordersLen) {
endOfSong=true; endOfSong=true;
curOrder=0; curOrder=0;
} }
@ -181,6 +181,7 @@ const char* cmdName[]={
"ES5506_ENVELOPE_RVRAMP", "ES5506_ENVELOPE_RVRAMP",
"ES5506_ENVELOPE_K1RAMP", "ES5506_ENVELOPE_K1RAMP",
"ES5506_ENVELOPE_K2RAMP", "ES5506_ENVELOPE_K2RAMP",
"ES5506_PAUSE",
"ALWAYS_SET_VOLUME" "ALWAYS_SET_VOLUME"
}; };
@ -277,14 +278,37 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
void DivEngine::processRow(int i, bool afterDelay) { void DivEngine::processRow(int i, bool afterDelay) {
int whatOrder=afterDelay?chan[i].delayOrder:curOrder; int whatOrder=afterDelay?chan[i].delayOrder:curOrder;
int whatRow=afterDelay?chan[i].delayRow:curRow; int whatRow=afterDelay?chan[i].delayRow:curRow;
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][whatOrder],false); DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][whatOrder],false);
// pre effects // pre effects
if (!afterDelay) for (int j=0; j<song.pat[i].effectCols; j++) { if (!afterDelay) {
bool returnAfterPre=false;
for (int j=0; j<curPat[i].effectCols; j++) {
short effect=pat->data[whatRow][4+(j<<1)]; short effect=pat->data[whatRow][4+(j<<1)];
short effectVal=pat->data[whatRow][5+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)];
if (effectVal==-1) effectVal=0; if (effectVal==-1) effectVal=0;
if (effect==0xed && effectVal!=0) {
switch (effect) {
case 0x09: // speed 1
if (effectVal>0) speed1=effectVal;
break;
case 0x0f: // speed 2
if (effectVal>0) speed2=effectVal;
break;
case 0x0b: // change order
if (changeOrd==-1) {
changeOrd=effectVal;
changePos=0;
}
break;
case 0x0d: // next order
if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
changeOrd=-2;
changePos=effectVal;
}
break;
case 0xed: // delay
if (effectVal!=0) {
if (effectVal<=nextSpeed) { if (effectVal<=nextSpeed) {
chan[i].rowDelay=effectVal+1; chan[i].rowDelay=effectVal+1;
chan[i].delayOrder=whatOrder; chan[i].delayOrder=whatOrder;
@ -294,11 +318,15 @@ void DivEngine::processRow(int i, bool afterDelay) {
} else { } else {
chan[i].delayLocked=false; chan[i].delayLocked=false;
} }
return; returnAfterPre=true;
} else { } else {
chan[i].delayLocked=false; chan[i].delayLocked=false;
} }
} }
break;
}
}
if (returnAfterPre) return;
} }
if (chan[i].delayLocked) return; if (chan[i].delayLocked) return;
@ -389,7 +417,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
bool panChanged=false; bool panChanged=false;
// effects // effects
for (int j=0; j<song.pat[i].effectCols; j++) { for (int j=0; j<curPat[i].effectCols; j++) {
short effect=pat->data[whatRow][4+(j<<1)]; short effect=pat->data[whatRow][4+(j<<1)];
short effectVal=pat->data[whatRow][5+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)];
@ -397,24 +425,6 @@ void DivEngine::processRow(int i, bool afterDelay) {
// per-system effect // per-system effect
if (!perSystemEffect(i,effect,effectVal)) switch (effect) { if (!perSystemEffect(i,effect,effectVal)) switch (effect) {
case 0x09: // speed 1
if (effectVal>0) speed1=effectVal;
break;
case 0x0f: // speed 2
if (effectVal>0) speed2=effectVal;
break;
case 0x0b: // change order
if (changeOrd==-1) {
changeOrd=effectVal;
changePos=0;
}
break;
case 0x0d: // next order
if (changeOrd<0 && (curOrder<(song.ordersLen-1) || !song.ignoreJumpAtEnd)) {
changeOrd=-2;
changePos=effectVal;
}
break;
case 0x08: // panning (split 4-bit) case 0x08: // panning (split 4-bit)
chan[i].panL=(effectVal>>4)|(effectVal&0xf0); chan[i].panL=(effectVal>>4)|(effectVal&0xf0);
chan[i].panR=(effectVal&15)|((effectVal&15)<<4); chan[i].panR=(effectVal&15)|((effectVal&15)<<4);
@ -550,7 +560,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
break; break;
case 0xe0: // arp speed case 0xe0: // arp speed
if (effectVal>0) { if (effectVal>0) {
song.arpLen=effectVal; curSubSong->arpLen=effectVal;
} }
break; break;
case 0xe1: // portamento up case 0xe1: // portamento up
@ -730,7 +740,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].noteOnInhibit=false; chan[i].noteOnInhibit=false;
// post effects // post effects
for (int j=0; j<song.pat[i].effectCols; j++) { for (int j=0; j<curPat[i].effectCols; j++) {
short effect=pat->data[whatRow][4+(j<<1)]; short effect=pat->data[whatRow][4+(j<<1)];
short effectVal=pat->data[whatRow][5+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)];
@ -748,10 +758,10 @@ void DivEngine::nextRow() {
strcpy(pb1,""); strcpy(pb1,"");
strcpy(pb3,""); strcpy(pb3,"");
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
snprintf(pb,4095," %.2x",song.orders.ord[i][curOrder]); snprintf(pb,4095," %.2x",curOrders->ord[i][curOrder]);
strcat(pb1,pb); strcat(pb1,pb);
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][curOrder],false); DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
snprintf(pb2,4095,"\x1b[37m %s", snprintf(pb2,4095,"\x1b[37m %s",
formatNote(pat->data[curRow][0],pat->data[curRow][1])); formatNote(pat->data[curRow][0],pat->data[curRow][1]));
strcat(pb3,pb2); strcat(pb3,pb2);
@ -767,7 +777,7 @@ void DivEngine::nextRow() {
snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]); snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]);
strcat(pb3,pb2); strcat(pb3,pb2);
} }
for (int j=0; j<song.pat[i].effectCols; j++) { for (int j=0; j<curPat[i].effectCols; j++) {
if (pat->data[curRow][4+(j<<1)]==-1) { if (pat->data[curRow][4+(j<<1)]==-1) {
strcat(pb3,"\x1b[m--"); strcat(pb3,"\x1b[m--");
} else { } else {
@ -799,32 +809,32 @@ void DivEngine::nextRow() {
if (changeOrd==-2) changeOrd=curOrder+1; if (changeOrd==-2) changeOrd=curOrder+1;
if (changeOrd<=curOrder) endOfSong=true; if (changeOrd<=curOrder) endOfSong=true;
curOrder=changeOrd; curOrder=changeOrd;
if (curOrder>=song.ordersLen) { if (curOrder>=curSubSong->ordersLen) {
curOrder=0; curOrder=0;
endOfSong=true; endOfSong=true;
} }
changeOrd=-1; changeOrd=-1;
} }
if (haltOn==DIV_HALT_PATTERN) halted=true; if (haltOn==DIV_HALT_PATTERN) halted=true;
} else if (playing) if (++curRow>=song.patLen) { } else if (playing) if (++curRow>=curSubSong->patLen) {
nextOrder(); nextOrder();
if (haltOn==DIV_HALT_PATTERN) halted=true; if (haltOn==DIV_HALT_PATTERN) halted=true;
} }
if (song.brokenSpeedSel) { if (song.brokenSpeedSel) {
if ((song.patLen&1) && curOrder&1) { if ((curSubSong->patLen&1) && curOrder&1) {
ticks=((curRow&1)?speed2:speed1)*(song.timeBase+1); ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1);
nextSpeed=(curRow&1)?speed1:speed2; nextSpeed=(curRow&1)?speed1:speed2;
} else { } else {
ticks=((curRow&1)?speed1:speed2)*(song.timeBase+1); ticks=((curRow&1)?speed1:speed2)*(curSubSong->timeBase+1);
nextSpeed=(curRow&1)?speed2:speed1; nextSpeed=(curRow&1)?speed2:speed1;
} }
} else { } else {
if (speedAB) { if (speedAB) {
ticks=speed2*(song.timeBase+1); ticks=speed2*(curSubSong->timeBase+1);
nextSpeed=speed1; nextSpeed=speed1;
} else { } else {
ticks=speed1*(song.timeBase+1); ticks=speed1*(curSubSong->timeBase+1);
nextSpeed=speed2; nextSpeed=speed2;
} }
speedAB=!speedAB; speedAB=!speedAB;
@ -832,7 +842,7 @@ void DivEngine::nextRow() {
// post row details // post row details
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][curOrder],false); DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) { if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) {
if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) { if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) {
if (!chan[i].legato) { if (!chan[i].legato) {
@ -841,7 +851,7 @@ void DivEngine::nextRow() {
if (song.oneTickCut) { if (song.oneTickCut) {
bool doPrepareCut=true; bool doPrepareCut=true;
for (int j=0; j<song.pat[i].effectCols; j++) { for (int j=0; j<curPat[i].effectCols; j++) {
if (pat->data[curRow][4+(j<<1)]==0x03) { if (pat->data[curRow][4+(j<<1)]==0x03) {
doPrepareCut=false; doPrepareCut=false;
break; break;
@ -1010,7 +1020,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
} }
if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) { if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) {
if (--chan[i].arpTicks<1) { if (--chan[i].arpTicks<1) {
chan[i].arpTicks=song.arpLen; chan[i].arpTicks=curSubSong->arpLen;
chan[i].arpStage++; chan[i].arpStage++;
if (chan[i].arpStage>2) chan[i].arpStage=0; if (chan[i].arpStage>2) chan[i].arpStage=0;
switch (chan[i].arpStage) { switch (chan[i].arpStage) {
@ -1051,7 +1061,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
} }
} }
if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond); if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,curSubSong->ordersLen,curRow,curSubSong->patLen,cmdsPerSecond);
} }
if (haltOn==DIV_HALT_TICK) halted=true; if (haltOn==DIV_HALT_TICK) halted=true;
@ -1328,11 +1338,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
if (!freelance && stepPlay!=-1 && subticks==1) { if (!freelance && stepPlay!=-1 && subticks==1) {
unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC); unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
if (realPos>=size) realPos=size-1; if (realPos>=size) realPos=size-1;
if (song.hilightA>0) { if (curSubSong->hilightA>0) {
if ((curRow%song.hilightA)==0 && ticks==1) metroTick[realPos]=1; if ((curRow%curSubSong->hilightA)==0 && ticks==1) metroTick[realPos]=1;
} }
if (song.hilightB>0) { if (curSubSong->hilightB>0) {
if ((curRow%song.hilightB)==0 && ticks==1) metroTick[realPos]=2; if ((curRow%curSubSong->hilightB)==0 && ticks==1) metroTick[realPos]=2;
} }
} }
if (nextTick()) { if (nextTick()) {

View file

@ -97,6 +97,18 @@ bool DivSample::initInternal(DivSampleDepth d, int count) {
dataDPCM=new unsigned char[lengthDPCM]; dataDPCM=new unsigned char[lengthDPCM];
memset(dataDPCM,0,lengthDPCM); memset(dataDPCM,0,lengthDPCM);
break; break;
case DIV_SAMPLE_DEPTH_AICA_ADPCM: // AICA ADPCM
if (dataAICA!=NULL) delete[] dataAICA;
lengthAICA=(count+1)/2;
dataAICA=new unsigned char[(lengthAICA+255)&(~0xff)];
memset(dataAICA,0,(lengthAICA+255)&(~0xff));
break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: // YMZ ADPCM
if (dataZ!=NULL) delete[] dataZ;
lengthZ=(count+1)/2;
dataZ=new unsigned char[(lengthZ+255)&(~0xff)];
memset(dataZ,0,(lengthZ+255)&(~0xff));
break;
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: // QSound ADPCM case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: // QSound ADPCM
if (dataQSoundA!=NULL) delete[] dataQSoundA; if (dataQSoundA!=NULL) delete[] dataQSoundA;
lengthQSoundA=(count+1)/2; lengthQSoundA=(count+1)/2;
@ -666,6 +678,12 @@ void DivSample::render() {
} }
break; break;
} }
case DIV_SAMPLE_DEPTH_AICA_ADPCM: // AICA ADPCM
aica_decode(dataAICA,data16,samples);
break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: // YMZ ADPCM
ymz_decode(dataZ,data16,samples);
break;
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: // QSound ADPCM case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: // QSound ADPCM
bs_decode(dataQSoundA,data16,samples); bs_decode(dataQSoundA,data16,samples);
break; break;
@ -718,6 +736,14 @@ void DivSample::render() {
if (accum>127) accum=127; if (accum>127) accum=127;
} }
} }
if (depth!=DIV_SAMPLE_DEPTH_AICA_ADPCM) { // AICA ADPCM
if (!initInternal(DIV_SAMPLE_DEPTH_AICA_ADPCM,samples)) return;
aica_encode(data16,dataAICA,(samples+511)&(~0x1ff));
}
if (depth!=DIV_SAMPLE_DEPTH_YMZ_ADPCM) { // YMZ ADPCM
if (!initInternal(DIV_SAMPLE_DEPTH_YMZ_ADPCM,samples)) return;
ymz_encode(data16,dataZ,(samples+511)&(~0x1ff));
}
if (depth!=DIV_SAMPLE_DEPTH_QSOUND_ADPCM) { // QSound ADPCM if (depth!=DIV_SAMPLE_DEPTH_QSOUND_ADPCM) { // QSound ADPCM
if (!initInternal(DIV_SAMPLE_DEPTH_QSOUND_ADPCM,samples)) return; if (!initInternal(DIV_SAMPLE_DEPTH_QSOUND_ADPCM,samples)) return;
bs_encode(data16,dataQSoundA,samples); bs_encode(data16,dataQSoundA,samples);
@ -754,6 +780,10 @@ void* DivSample::getCurBuf() {
return data1; return data1;
case DIV_SAMPLE_DEPTH_1BIT_DPCM: case DIV_SAMPLE_DEPTH_1BIT_DPCM:
return dataDPCM; return dataDPCM;
case DIV_SAMPLE_DEPTH_AICA_ADPCM:
return dataAICA;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
return dataZ;
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
return dataQSoundA; return dataQSoundA;
case DIV_SAMPLE_DEPTH_ADPCM_A: case DIV_SAMPLE_DEPTH_ADPCM_A:
@ -782,6 +812,10 @@ unsigned int DivSample::getCurBufLen() {
return length1; return length1;
case DIV_SAMPLE_DEPTH_1BIT_DPCM: case DIV_SAMPLE_DEPTH_1BIT_DPCM:
return lengthDPCM; return lengthDPCM;
case DIV_SAMPLE_DEPTH_AICA_ADPCM:
return lengthAICA;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
return lengthZ;
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
return lengthQSoundA; return lengthQSoundA;
case DIV_SAMPLE_DEPTH_ADPCM_A: case DIV_SAMPLE_DEPTH_ADPCM_A:
@ -896,6 +930,8 @@ DivSample::~DivSample() {
if (data16) delete[] data16; if (data16) delete[] data16;
if (data1) delete[] data1; if (data1) delete[] data1;
if (dataDPCM) delete[] dataDPCM; if (dataDPCM) delete[] dataDPCM;
if (dataAICA) delete[] dataAICA;
if (dataZ) delete[] dataZ;
if (dataQSoundA) delete[] dataQSoundA; if (dataQSoundA) delete[] dataQSoundA;
if (dataA) delete[] dataA; if (dataA) delete[] dataA;
if (dataB) delete[] dataB; if (dataB) delete[] dataB;

View file

@ -38,6 +38,8 @@ enum DivResampleFilters {
enum DivSampleDepth: unsigned char { enum DivSampleDepth: unsigned char {
DIV_SAMPLE_DEPTH_1BIT=0, DIV_SAMPLE_DEPTH_1BIT=0,
DIV_SAMPLE_DEPTH_1BIT_DPCM=1, DIV_SAMPLE_DEPTH_1BIT_DPCM=1,
DIV_SAMPLE_DEPTH_AICA_ADPCM=2,
DIV_SAMPLE_DEPTH_YMZ_ADPCM=3,
DIV_SAMPLE_DEPTH_QSOUND_ADPCM=4, DIV_SAMPLE_DEPTH_QSOUND_ADPCM=4,
DIV_SAMPLE_DEPTH_ADPCM_A=5, DIV_SAMPLE_DEPTH_ADPCM_A=5,
DIV_SAMPLE_DEPTH_ADPCM_B=6, DIV_SAMPLE_DEPTH_ADPCM_B=6,
@ -103,6 +105,8 @@ struct DivSample {
// valid values are: // valid values are:
// - 0: ZX Spectrum overlay drum (1-bit) // - 0: ZX Spectrum overlay drum (1-bit)
// - 1: 1-bit NES DPCM (1-bit) // - 1: 1-bit NES DPCM (1-bit)
// - 2: AICA ADPCM
// - 3: YMZ ADPCM
// - 4: QSound ADPCM // - 4: QSound ADPCM
// - 5: ADPCM-A // - 5: ADPCM-A
// - 6: ADPCM-B // - 6: ADPCM-B
@ -118,6 +122,8 @@ struct DivSample {
short* data16; // 16 short* data16; // 16
unsigned char* data1; // 0 unsigned char* data1; // 0
unsigned char* dataDPCM; // 1 unsigned char* dataDPCM; // 1
unsigned char* dataAICA; // 2
unsigned char* dataZ; // 3
unsigned char* dataQSoundA; // 4 unsigned char* dataQSoundA; // 4
unsigned char* dataA; // 5 unsigned char* dataA; // 5
unsigned char* dataB; // 6 unsigned char* dataB; // 6
@ -125,8 +131,8 @@ struct DivSample {
unsigned char* dataBRR; // 9 unsigned char* dataBRR; // 9
unsigned char* dataVOX; // 10 unsigned char* dataVOX; // 10
unsigned int length8, length16, length1, lengthDPCM, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX; unsigned int length8, length16, length1, lengthDPCM, lengthAICA, lengthZ, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX;
unsigned int off8, off16, off1, offDPCM, offQSoundA, offA, offB, offX68, offBRR, offVOX; unsigned int off8, off16, off1, offDPCM, offAICA, offZ, offQSoundA, offA, offB, offX68, offBRR, offVOX;
unsigned int offSegaPCM, offQSound, offX1_010, offES5506, offSU; unsigned int offSegaPCM, offQSound, offX1_010, offES5506, offSU;
unsigned int samples; unsigned int samples;
@ -267,6 +273,8 @@ struct DivSample {
data16(NULL), data16(NULL),
data1(NULL), data1(NULL),
dataDPCM(NULL), dataDPCM(NULL),
dataAICA(NULL),
dataZ(NULL),
dataQSoundA(NULL), dataQSoundA(NULL),
dataA(NULL), dataA(NULL),
dataB(NULL), dataB(NULL),
@ -277,6 +285,8 @@ struct DivSample {
length16(0), length16(0),
length1(0), length1(0),
lengthDPCM(0), lengthDPCM(0),
lengthAICA(0),
lengthZ(0),
lengthQSoundA(0), lengthQSoundA(0),
lengthA(0), lengthA(0),
lengthB(0), lengthB(0),
@ -287,6 +297,8 @@ struct DivSample {
off16(0), off16(0),
off1(0), off1(0),
offDPCM(0), offDPCM(0),
offAICA(0),
offZ(0),
offQSoundA(0), offQSoundA(0),
offA(0), offA(0),
offB(0), offB(0),

View file

@ -19,7 +19,7 @@
#include "song.h" #include "song.h"
void DivSong::clearSongData() { void DivSubSong::clearData() {
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
pat[i].wipePatterns(); pat[i].wipePatterns();
} }
@ -28,6 +28,15 @@ void DivSong::clearSongData() {
ordersLen=1; ordersLen=1;
} }
void DivSong::clearSongData() {
for (DivSubSong* i: subsong) {
i->clearData();
delete i;
}
subsong.clear();
subsong.push_back(new DivSubSong);
}
void DivSong::clearInstruments() { void DivSong::clearInstruments() {
for (DivInstrument* i: ins) { for (DivInstrument* i: ins) {
delete i; delete i;
@ -71,7 +80,9 @@ void DivSong::unload() {
sample.clear(); sample.clear();
sampleLen=0; sampleLen=0;
for (int i=0; i<DIV_MAX_CHANS; i++) { for (DivSubSong* i: subsong) {
pat[i].wipePatterns(); i->clearData();
delete i;
} }
subsong.clear();
} }

View file

@ -65,7 +65,9 @@ enum DivSystem {
DIV_SYSTEM_MMC5, DIV_SYSTEM_MMC5,
DIV_SYSTEM_N163, DIV_SYSTEM_N163,
DIV_SYSTEM_OPN, DIV_SYSTEM_OPN,
DIV_SYSTEM_OPN_EXT,
DIV_SYSTEM_PC98, DIV_SYSTEM_PC98,
DIV_SYSTEM_PC98_EXT,
DIV_SYSTEM_OPL, DIV_SYSTEM_OPL,
DIV_SYSTEM_OPL2, DIV_SYSTEM_OPL2,
DIV_SYSTEM_OPL3, DIV_SYSTEM_OPL3,
@ -109,6 +111,44 @@ enum DivSystem {
DIV_SYSTEM_MAX // boundary for max system number DIV_SYSTEM_MAX // boundary for max system number
}; };
struct DivSubSong {
String name, notes;
unsigned char hilightA, hilightB;
unsigned char timeBase, speed1, speed2, arpLen;
bool pal;
bool customTempo;
float hz;
int patLen, ordersLen;
DivOrders orders;
DivChannelData pat[DIV_MAX_CHANS];
bool chanShow[DIV_MAX_CHANS];
unsigned char chanCollapse[DIV_MAX_CHANS];
String chanName[DIV_MAX_CHANS];
String chanShortName[DIV_MAX_CHANS];
void clearData();
DivSubSong():
hilightA(4),
hilightB(16),
timeBase(0),
speed1(6),
speed2(6),
arpLen(1),
pal(true),
customTempo(false),
hz(60.0),
patLen(64),
ordersLen(1) {
for (int i=0; i<DIV_MAX_CHANS; i++) {
chanShow[i]=true;
chanCollapse[i]=0;
}
}
};
struct DivSong { struct DivSong {
// version number used for saving the song. // version number used for saving the song.
// Furnace will save using the latest possible version, // Furnace will save using the latest possible version,
@ -155,14 +195,21 @@ struct DivSong {
// - introduces Genesis system // - introduces Genesis system
// - introduces system number // - introduces system number
// - patterns now stored in current known format // - patterns now stored in current known format
// - 8: ???
// - only used in the Medivo YMU cover
// - 7: ??? // - 7: ???
// - only present in a later version of First.dmf
// - pattern format changes: empty field is 0xFF instead of 0x80
// - instrument now stored in pattern
// - 5: BETA 3 // - 5: BETA 3
// - adds arpeggio tick // - adds arpeggio tick
// - 4: BETA 2 // - 4: BETA 2
// - possibly adds instrument number (stored in channel)?
// - cannot confirm as I don't have any version 4 modules
// - 3: BETA 1 // - 3: BETA 1
// - possibly the first version that could save // - possibly the first version that could save
// - basic format, no system number, 16 instruments, one speed, YMU759-only // - basic format, no system number, 16 instruments, one speed, YMU759-only
// - patterns were stored in a different format (chars instead of shorts) // - patterns were stored in a different format (chars instead of shorts) and no instrument
// - if somebody manages to find a version 2 or even 1 module, please tell me as it will be worth more than a luxury vehicle // - if somebody manages to find a version 2 or even 1 module, please tell me as it will be worth more than a luxury vehicle
unsigned short version; unsigned short version;
bool isDMF; bool isDMF;
@ -216,13 +263,19 @@ struct DivSong {
// - 8: 0.83MHz (Sunsoft 5B on PAL) // - 8: 0.83MHz (Sunsoft 5B on PAL)
// - 9: 1.10MHz (Gamate/VIC-20 PAL) // - 9: 1.10MHz (Gamate/VIC-20 PAL)
// - 10: 2.097152MHz (Game Boy) // - 10: 2.097152MHz (Game Boy)
// - 11: 3.58MHz (Darky)
// - 12: 3.6MHz (Darky)
// - bit 4-5: chip type (ignored on AY8930) // - bit 4-5: chip type (ignored on AY8930)
// - 0: AY-3-8910 or similar // - 0: AY-3-8910 or similar
// - 1: YM2149 // - 1: YM2149
// - 2: Sunsoft 5B // - 2: Sunsoft 5B
// - bit 6: stereo // - 3: AY-3-8914
// - bit 6: stereo (ignored on Sunsoft 5B)
// - 0: mono // - 0: mono
// - 1: stereo ABC // - 1: stereo ABC
// - bit 7: clock divider pin (YM2149, AY8930)
// - 0: high (disable divider)
// - 1: low (internally divided to half)
// - SAA1099: // - SAA1099:
// - bit 0-1: clock rate // - bit 0-1: clock rate
// - 0: 8MHz (SAM Coupé, Game Blaster) // - 0: 8MHz (SAM Coupé, Game Blaster)
@ -284,19 +337,10 @@ struct DivSong {
String nameJ, authorJ, categoryJ; String nameJ, authorJ, categoryJ;
// other things // other things
String chanName[DIV_MAX_CHANS];
String chanShortName[DIV_MAX_CHANS];
String notes; String notes;
// highlight
unsigned char hilightA, hilightB;
// module details // module details
unsigned char timeBase, speed1, speed2, arpLen; int insLen, waveLen, sampleLen;
bool pal;
bool customTempo;
float hz;
int patLen, ordersLen, insLen, waveLen, sampleLen;
float masterVol; float masterVol;
float tuning; float tuning;
@ -344,14 +388,11 @@ struct DivSong {
bool snDutyReset; bool snDutyReset;
bool pitchMacroIsLinear; bool pitchMacroIsLinear;
DivOrders orders;
std::vector<DivInstrument*> ins; std::vector<DivInstrument*> ins;
DivChannelData pat[DIV_MAX_CHANS];
std::vector<DivWavetable*> wave; std::vector<DivWavetable*> wave;
std::vector<DivSample*> sample; std::vector<DivSample*> sample;
bool chanShow[DIV_MAX_CHANS]; std::vector<DivSubSong*> subsong;
unsigned char chanCollapse[DIV_MAX_CHANS];
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound; DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound;
DivWavetable nullWave; DivWavetable nullWave;
@ -377,7 +418,6 @@ struct DivSong {
*/ */
void clearSamples(); void clearSamples();
/** /**
* unloads the song, freeing all memory associated with it. * unloads the song, freeing all memory associated with it.
* use before destroying the object. * use before destroying the object.
@ -401,17 +441,6 @@ struct DivSong {
manInfo(""), manInfo(""),
createdDate(""), createdDate(""),
revisionDate(""), revisionDate(""),
hilightA(4),
hilightB(16),
timeBase(0),
speed1(6),
speed2(6),
arpLen(1),
pal(true),
customTempo(false),
hz(60.0),
patLen(64),
ordersLen(1),
insLen(0), insLen(0),
waveLen(0), waveLen(0),
sampleLen(0), sampleLen(0),
@ -457,10 +486,7 @@ struct DivSong {
systemPan[i]=0; systemPan[i]=0;
systemFlags[i]=0; systemFlags[i]=0;
} }
for (int i=0; i<DIV_MAX_CHANS; i++) { subsong.push_back(new DivSubSong);
chanShow[i]=true;
chanCollapse[i]=0;
}
system[0]=DIV_SYSTEM_YM2612; system[0]=DIV_SYSTEM_YM2612;
system[1]=DIV_SYSTEM_SMS; system[1]=DIV_SYSTEM_SMS;

View file

@ -283,7 +283,7 @@ bool DivEngine::isSTDSystem(DivSystem sys) {
const char* DivEngine::getChannelName(int chan) { const char* DivEngine::getChannelName(int chan) {
if (chan<0 || chan>chans) return "??"; if (chan<0 || chan>chans) return "??";
if (!song.chanName[chan].empty()) return song.chanName[chan].c_str(); if (!curSubSong->chanName[chan].empty()) return curSubSong->chanName[chan].c_str();
if (sysDefs[sysOfChan[chan]]==NULL) return "??"; if (sysDefs[sysOfChan[chan]]==NULL) return "??";
const char* ret=sysDefs[sysOfChan[chan]]->chanNames[dispatchChanOfChan[chan]]; const char* ret=sysDefs[sysOfChan[chan]]->chanNames[dispatchChanOfChan[chan]];
@ -293,7 +293,7 @@ const char* DivEngine::getChannelName(int chan) {
const char* DivEngine::getChannelShortName(int chan) { const char* DivEngine::getChannelShortName(int chan) {
if (chan<0 || chan>chans) return "??"; if (chan<0 || chan>chans) return "??";
if (!song.chanShortName[chan].empty()) return song.chanShortName[chan].c_str(); if (!curSubSong->chanShortName[chan].empty()) return curSubSong->chanShortName[chan].c_str();
if (sysDefs[sysOfChan[chan]]==NULL) return "??"; if (sysDefs[sysOfChan[chan]]==NULL) return "??";
const char* ret=sysDefs[sysOfChan[chan]]->chanShortNames[dispatchChanOfChan[chan]]; const char* ret=sysDefs[sysOfChan[chan]]->chanShortNames[dispatchChanOfChan[chan]];
@ -734,6 +734,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef(
"Yamaha YMU759 (MA-2)", NULL, 0x01, 0x01, 17, true, false, 0, false, "Yamaha YMU759 (MA-2)", NULL, 0x01, 0x01, 17, true, false, 0, false,
"a chip which found its way inside mobile phones in the 2000's.\nas proprietary as it is, it passed away after losing to MP3 in the mobile hardware battle.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM }, // type {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM }, // type
@ -742,16 +743,19 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_GENESIS]=new DivSysDef( sysDefs[DIV_SYSTEM_GENESIS]=new DivSysDef(
"Sega Genesis/Mega Drive", "セガメガドライブ", 0x02, 0x02, 10, true, true, 0, true, "Sega Genesis/Mega Drive", "セガメガドライブ", 0x02, 0x02, 10, true, true, 0, true,
"<COMPOUND SYSTEM!>",
{}, {}, {}, {} {}, {}, {}, {}
); );
sysDefs[DIV_SYSTEM_GENESIS_EXT]=new DivSysDef( sysDefs[DIV_SYSTEM_GENESIS_EXT]=new DivSysDef(
"Sega Genesis Extended Channel 3", NULL, 0x42, 0x42, 13, true, true, 0, true, "Sega Genesis Extended Channel 3", NULL, 0x42, 0x42, 13, true, true, 0, true,
"<COMPOUND SYSTEM!>",
{}, {}, {}, {} {}, {}, {}, {}
); );
sysDefs[DIV_SYSTEM_SMS]=new DivSysDef( sysDefs[DIV_SYSTEM_SMS]=new DivSysDef(
"TI SN76489", NULL, 0x03, 0x03, 4, false, true, 0x150, false, "TI SN76489", NULL, 0x03, 0x03, 4, false, true, 0x150, false,
"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"}, {"Square 1", "Square 2", "Square 3", "Noise"},
{"S1", "S2", "S3", "NO"}, {"S1", "S2", "S3", "NO"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE},
@ -771,11 +775,13 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_SMS_OPLL]=new DivSysDef( sysDefs[DIV_SYSTEM_SMS_OPLL]=new DivSysDef(
"Sega Master System + FM Expansion", NULL, 0x43, 0x43, 13, true, true, 0, true, "Sega Master System + FM Expansion", NULL, 0x43, 0x43, 13, true, true, 0, true,
"<COMPOUND SYSTEM!>",
{}, {}, {}, {} {}, {}, {}, {}
); );
sysDefs[DIV_SYSTEM_GB]=new DivSysDef( sysDefs[DIV_SYSTEM_GB]=new DivSysDef(
"Game Boy", NULL, 0x04, 0x04, 4, false, true, 0x161, false, "Game Boy", NULL, 0x04, 0x04, 4, false, true, 0x161, false,
"the most popular portable game console of the era.",
{"Pulse 1", "Pulse 2", "Wavetable", "Noise"}, {"Pulse 1", "Pulse 2", "Wavetable", "Noise"},
{"S1", "S2", "WA", "NO"}, {"S1", "S2", "WA", "NO"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE},
@ -804,6 +810,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_PCE]=new DivSysDef( sysDefs[DIV_SYSTEM_PCE]=new DivSysDef(
"PC Engine/TurboGrafx-16", NULL, 0x05, 0x05, 6, false, true, 0x161, false, "PC Engine/TurboGrafx-16", NULL, 0x05, 0x05, 6, false, true, 0x161, false,
"an '80's game console with a wavetable sound chip, popular in Japan.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
@ -835,6 +842,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_NES]=new DivSysDef( sysDefs[DIV_SYSTEM_NES]=new DivSysDef(
"NES (Ricoh 2A03)", NULL, 0x06, 0x06, 5, false, true, 0x161, false, "NES (Ricoh 2A03)", NULL, 0x06, 0x06, 5, false, true, 0x161, false,
"also known as Famicom in Japan, it's the most well-known game console of the '80's.",
{"Pulse 1", "Pulse 2", "Triangle", "Noise", "PCM"}, {"Pulse 1", "Pulse 2", "Triangle", "Noise", "PCM"},
{"S1", "S2", "TR", "NO", "PCM"}, {"S1", "S2", "TR", "NO", "PCM"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM},
@ -866,16 +874,19 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_NES_VRC7]=new DivSysDef( sysDefs[DIV_SYSTEM_NES_VRC7]=new DivSysDef(
"NES + Konami VRC7", NULL, 0x46, 0x46, 11, true, true, 0, true, "NES + Konami VRC7", NULL, 0x46, 0x46, 11, true, true, 0, true,
"<COMPOUND SYSTEM!>",
{}, {}, {}, {} {}, {}, {}, {}
); );
sysDefs[DIV_SYSTEM_NES_FDS]=new DivSysDef( sysDefs[DIV_SYSTEM_NES_FDS]=new DivSysDef(
"Famicom Disk System", NULL, 0, 0x86, 6, false, true, 0, true, "Famicom Disk System", NULL, 0, 0x86, 6, false, true, 0, true,
"<COMPOUND SYSTEM!>",
{}, {}, {}, {} {}, {}, {}, {}
); );
sysDefs[DIV_SYSTEM_C64_6581]=new DivSysDef( sysDefs[DIV_SYSTEM_C64_6581]=new DivSysDef(
"Commodore 64 (6581)", NULL, 0x47, 0x47, 3, false, true, 0, false, "Commodore 64 (6581)", NULL, 0x47, 0x47, 3, false, true, 0, false,
"this computer is powered by the SID chip, which had synthesizer features like a filter and ADSR.",
{"Channel 1", "Channel 2", "Channel 3"}, {"Channel 1", "Channel 2", "Channel 3"},
{"CH1", "CH2", "CH3"}, {"CH1", "CH2", "CH3"},
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
@ -887,6 +898,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_C64_8580]=new DivSysDef( sysDefs[DIV_SYSTEM_C64_8580]=new DivSysDef(
"Commodore 64 (8580)", NULL, 0x07, 0x07, 3, false, true, 0, false, "Commodore 64 (8580)", NULL, 0x07, 0x07, 3, false, true, 0, false,
"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"}, {"Channel 1", "Channel 2", "Channel 3"},
{"CH1", "CH2", "CH3"}, {"CH1", "CH2", "CH3"},
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
@ -898,6 +910,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_ARCADE]=new DivSysDef( sysDefs[DIV_SYSTEM_ARCADE]=new DivSysDef(
"DefleCade", NULL, 0x08, 0x08, 13, true, false, 0, true, "DefleCade", NULL, 0x08, 0x08, 13, true, false, 0, true,
"<COMPOUND SYSTEM!>",
{}, {}, {}, {} {}, {}, {}, {}
); );
@ -914,6 +927,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2610]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2610]=new DivSysDef(
"Neo Geo CD", NULL, 0x09, 0x09, 13, true, true, 0x151, false, "Neo Geo CD", NULL, 0x09, 0x09, 13, true, true, 0x151, false,
"like Neo Geo, but lacking the ADPCM-B channel since they couldn't connect the pins.",
{"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6"}, {"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6"},
{"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, {"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -925,6 +939,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2610_EXT]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2610_EXT]=new DivSysDef(
"Neo Geo CD Extended Channel 2", NULL, 0x49, 0x49, 16, true, true, 0x151, false, "Neo Geo CD Extended Channel 2", NULL, 0x49, 0x49, 16, true, true, 0x151, false,
"like Neo Geo, but lacking the ADPCM-B channel since they couldn't connect the pins.\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies.",
{"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6"}, {"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6"},
{"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, {"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"},
{DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -936,6 +951,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_AY8910]=new DivSysDef( sysDefs[DIV_SYSTEM_AY8910]=new DivSysDef(
"AY-3-8910", NULL, 0x80, 0, 3, false, true, 0x151, false, "AY-3-8910", NULL, 0x80, 0, 3, false, true, 0x151, false,
"this chip is everywhere! ZX Spectrum, MSX, Amstrad CPC, Intellivision, Vectrex...\nthe discovery of envelope bass helped it beat the SN76489 with ease.",
{"PSG 1", "PSG 2", "PSG 3"}, {"PSG 1", "PSG 2", "PSG 3"},
{"S1", "S2", "S3"}, {"S1", "S2", "S3"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE},
@ -947,6 +963,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_AMIGA]=new DivSysDef( sysDefs[DIV_SYSTEM_AMIGA]=new DivSysDef(
"Amiga", NULL, 0x81, 0, 4, false, true, 0, false, "Amiga", NULL, 0x81, 0, 4, false, true, 0, false,
"a computer from the '80's with full sampling capabilities, giving it a sound ahead of its time.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"}, {"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -973,6 +990,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2151]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2151]=new DivSysDef(
"Yamaha YM2151 (OPM)", NULL, 0x82, 0, 8, true, false, 0x150, false, "Yamaha YM2151 (OPM)", NULL, 0x82, 0, 8, true, false, 0x150, false,
"this was Yamaha's first integrated FM chip.\nit was used in several synthesizers, computers and arcade boards.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -998,6 +1016,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2612]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2612]=new DivSysDef(
"Yamaha YM2612 (OPN2)", NULL, 0x83, 0, 6, true, false, 0x150, false, "Yamaha YM2612 (OPN2)", NULL, 0x83, 0, 6, true, false, 0x150, false,
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"},
{"F1", "F2", "F3", "F4", "F5", "F6"}, {"F1", "F2", "F3", "F4", "F5", "F6"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -1009,6 +1028,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_TIA]=new DivSysDef( sysDefs[DIV_SYSTEM_TIA]=new DivSysDef(
"Atari 2600", NULL, 0x84, 0, 2, false, true, 0, false, "Atari 2600", NULL, 0x84, 0, 2, false, true, 0, false,
"it's a challenge to make music on this chip which barely has musical capabilities...",
{"Channel 1", "Channel 2"}, {"Channel 1", "Channel 2"},
{"CH1", "CH2"}, {"CH1", "CH2"},
{DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE},
@ -1029,6 +1049,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_SAA1099]=new DivSysDef( sysDefs[DIV_SYSTEM_SAA1099]=new DivSysDef(
"Philips SAA1099", NULL, 0x97, 0, 6, false, true, 0x171, false, "Philips SAA1099", NULL, 0x97, 0, 6, false, true, 0x171, false,
"supposedly an upgrade from the AY-3-8910, this was present on the Creative Music System (Game Blaster) and SAM Coupé.",
{"PSG 1", "PSG 2", "PSG 3", "PSG 4", "PSG 5", "PSG 6"}, {"PSG 1", "PSG 2", "PSG 3", "PSG 4", "PSG 5", "PSG 6"},
{"S1", "S2", "S3", "S4", "S5", "S6"}, {"S1", "S2", "S3", "S4", "S5", "S6"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE},
@ -1055,6 +1076,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_AY8930]=new DivSysDef( sysDefs[DIV_SYSTEM_AY8930]=new DivSysDef(
"Microchip AY8930", NULL, 0x9a, 0, 3, false, true, 0x151, false, "Microchip AY8930", NULL, 0x9a, 0, 3, false, true, 0x151, false,
"an improved version of the AY-3-8910 with a bigger frequency range, duty cycles, configurable noise and per-channel envelopes!",
{"PSG 1", "PSG 2", "PSG 3"}, {"PSG 1", "PSG 2", "PSG 3"},
{"S1", "S2", "S3"}, {"S1", "S2", "S3"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE},
@ -1077,6 +1099,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_VIC20]=new DivSysDef( sysDefs[DIV_SYSTEM_VIC20]=new DivSysDef(
"Commodore VIC-20", NULL, 0x85, 0, 4, false, true, 0, false, "Commodore VIC-20", NULL, 0x85, 0, 4, false, true, 0, false,
"Commodore's successor to the PET.\nits square wave channels are more than just square...",
{"Low", "Mid", "High", "Noise"}, {"Low", "Mid", "High", "Noise"},
{"LO", "MID", "HI", "NO"}, {"LO", "MID", "HI", "NO"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE},
@ -1087,6 +1110,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_PET]=new DivSysDef( sysDefs[DIV_SYSTEM_PET]=new DivSysDef(
"Commodore PET", NULL, 0x86, 0, 1, false, true, 0, false, "Commodore PET", NULL, 0x86, 0, 1, false, true, 0, false,
"one channel of 1-bit wavetable which is better (and worse) than the PC Speaker.",
{"Wave"}, {"Wave"},
{"PET"}, {"PET"},
{DIV_CH_PULSE}, {DIV_CH_PULSE},
@ -1097,6 +1121,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_SNES]=new DivSysDef( sysDefs[DIV_SYSTEM_SNES]=new DivSysDef(
"SNES", NULL, 0x87, 0, 8, false, true, 0, false, "SNES", NULL, 0x87, 0, 8, false, true, 0, false,
"FM? nah... samples! Nintendo's answer to Sega.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1105,6 +1130,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_VRC6]=new DivSysDef( sysDefs[DIV_SYSTEM_VRC6]=new DivSysDef(
"Konami VRC6", NULL, 0x88, 0, 3, false, true, 0, false, "Konami VRC6", NULL, 0x88, 0, 3, false, true, 0, false,
"an expansion chip for the Famicom, featuring a quirky sawtooth channel.",
{"VRC6 1", "VRC6 2", "VRC6 Saw"}, {"VRC6 1", "VRC6 2", "VRC6 Saw"},
{"V1", "V2", "VS"}, {"V1", "V2", "VS"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE},
@ -1138,6 +1164,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPLL]=new DivSysDef( sysDefs[DIV_SYSTEM_OPLL]=new DivSysDef(
"Yamaha YM2413 (OPLL)", NULL, 0x89, 0, 9, true, false, 0x150, false, "Yamaha YM2413 (OPLL)", NULL, 0x89, 0, 9, true, false, 0x150, false,
"cost-reduced version of the OPL with 16 patches and only one of them is user-configurable.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -1149,6 +1176,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_FDS]=new DivSysDef( sysDefs[DIV_SYSTEM_FDS]=new DivSysDef(
"Famicom Disk System (chip)", NULL, 0x8a, 0, 1, false, true, 0x161, false, "Famicom Disk System (chip)", NULL, 0x8a, 0, 1, false, true, 0x161, false,
"a disk drive for the Famicom which also contains one wavetable channel.",
{"FDS"}, {"FDS"},
{"FDS"}, {"FDS"},
{DIV_CH_WAVE}, {DIV_CH_WAVE},
@ -1183,6 +1211,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_MMC5]=new DivSysDef( sysDefs[DIV_SYSTEM_MMC5]=new DivSysDef(
"MMC5", NULL, 0x8b, 0, 3, false, true, 0, false, "MMC5", NULL, 0x8b, 0, 3, false, true, 0, false,
"an expansion chip for the Famicom, featuring a little-known PCM channel.",
{"Pulse 1", "Pulse 2", "PCM"}, {"Pulse 1", "Pulse 2", "PCM"},
{"S1", "S2", "PCM"}, {"S1", "S2", "PCM"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM},
@ -1205,6 +1234,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_N163]=new DivSysDef( sysDefs[DIV_SYSTEM_N163]=new DivSysDef(
"Namco 163", NULL, 0x8c, 0, 8, false, true, 0, false, "Namco 163", NULL, 0x8c, 0, 8, false, true, 0, false,
"an expansion chip for the Famicom, with full wavetable.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
@ -1259,23 +1289,56 @@ void DivEngine::registerSystems() {
); );
sysDefs[DIV_SYSTEM_OPN]=new DivSysDef( sysDefs[DIV_SYSTEM_OPN]=new DivSysDef(
"Yamaha YM2203 (OPN)", NULL, 0x8d, 0, 6, true, false, 0, false, "Yamaha YM2203 (OPN)", NULL, 0x8d, 0, 6, true, true, 0x151, false,
"cost-reduced version of the OPM with a different register layout and no stereo...\n...but it has a built-in AY-3-8910! (actually an YM2149)",
{"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"}, {"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"},
{"F1", "F2", "F3", "S1", "S2", "S3"}, {"F1", "F2", "F3", "S1", "S2", "S3"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY} {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY},
{},
fmHardResetEffectHandler,
fmPostEffectHandler
);
sysDefs[DIV_SYSTEM_OPN_EXT]=new DivSysDef(
"Yamaha YM2203 (OPN) Extended Channel 3", NULL, 0xb6, 0, 9, true, true, 0x151, false,
"cost-reduced version of the OPM with a different register layout and no stereo...\n...but it has a built-in AY-3-8910! (actually an YM2149)\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies",
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "PSG 1", "PSG 2", "PSG 3"},
{"F1", "F2", "O1", "O2", "O3", "O4", "S1", "S2", "S3"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY},
{},
fmHardResetEffectHandler,
fmPostEffectHandler
); );
sysDefs[DIV_SYSTEM_PC98]=new DivSysDef( sysDefs[DIV_SYSTEM_PC98]=new DivSysDef(
"Yamaha YM2608 (OPNA)", NULL, 0x8e, 0, 16, true, false, 0, false, "Yamaha YM2608 (OPNA)", NULL, 0x8e, 0, 16, true, true, 0, false,
"OPN but twice the FM channels, stereo makes a come-back and has rhythm and ADPCM channels.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"},
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"}, {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA},
{},
fmHardResetEffectHandler,
fmPostEffectHandler
);
sysDefs[DIV_SYSTEM_PC98_EXT]=new DivSysDef(
"Yamaha YM2608 (OPNA) Extended Channel 3", NULL, 0xb7, 0, 19, true, true, 0, false,
"OPN but twice the FM channels, stereo makes a come-back and has rhythm and ADPCM channels.\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies",
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"},
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA},
{},
fmHardResetEffectHandler,
fmPostEffectHandler
); );
sysDefs[DIV_SYSTEM_OPL]=new DivSysDef( sysDefs[DIV_SYSTEM_OPL]=new DivSysDef(
"Yamaha YM3526 (OPL)", NULL, 0x8f, 0, 9, true, false, 0x151, false, "Yamaha YM3526 (OPL)", NULL, 0x8f, 0, 9, true, false, 0x151, false,
"OPN, but what if you only had two operators, no stereo, no detune and a lower ADSR parameter range?",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -1287,6 +1350,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPL2]=new DivSysDef( sysDefs[DIV_SYSTEM_OPL2]=new DivSysDef(
"Yamaha YM3812 (OPL2)", NULL, 0x90, 0, 9, true, false, 0x151, false, "Yamaha YM3812 (OPL2)", NULL, 0x90, 0, 9, true, false, 0x151, false,
"OPL, but what if you had more waveforms to choose than the normal sine?",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -1298,6 +1362,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPL3]=new DivSysDef( sysDefs[DIV_SYSTEM_OPL3]=new DivSysDef(
"Yamaha YMF262 (OPL3)", NULL, 0x91, 0, 18, true, false, 0x151, false, "Yamaha YMF262 (OPL3)", NULL, 0x91, 0, 18, true, false, 0x151, false,
"OPL2, but what if you had twice the channels, 4-op mode, stereo and even more waveforms?",
{"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"},
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"},
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -1309,6 +1374,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_MULTIPCM]=new DivSysDef( sysDefs[DIV_SYSTEM_MULTIPCM]=new DivSysDef(
"MultiPCM", NULL, 0x92, 0, 28, false, true, 0, false, "MultiPCM", NULL, 0x92, 0, 28, false, true, 0, false,
"how many channels of PCM do you want?\nMultiPCM: yes",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28"},
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1317,14 +1383,16 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_PCSPKR]=new DivSysDef( sysDefs[DIV_SYSTEM_PCSPKR]=new DivSysDef(
"PC Speaker", NULL, 0x93, 0, 1, false, true, 0, false, "PC Speaker", NULL, 0x93, 0, 1, false, true, 0, false,
"good luck!",
{"Square"}, {"Square"},
{"SQ"}, {"SQ"},
{DIV_CH_PULSE}, {DIV_CH_PULSE},
{DIV_INS_STD} {DIV_INS_BEEPER}
); );
sysDefs[DIV_SYSTEM_POKEY]=new DivSysDef( sysDefs[DIV_SYSTEM_POKEY]=new DivSysDef(
"POKEY", NULL, 0x94, 0, 4, false, true, 0, false, "POKEY", NULL, 0x94, 0, 4, false, true, 0, false,
"TIA, but better and more flexible.\nused in the Atari 8-bit family of computers (400/800/XL/XE).",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"}, {"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
@ -1333,6 +1401,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_RF5C68]=new DivSysDef( sysDefs[DIV_SYSTEM_RF5C68]=new DivSysDef(
"Ricoh RF5C68", NULL, 0x95, 0, 8, false, true, 0, false, "Ricoh RF5C68", NULL, 0x95, 0, 8, false, true, 0, false,
"this is like SNES' sound chip but without interpolation and the rest of nice bits.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1341,6 +1410,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_SWAN]=new DivSysDef( sysDefs[DIV_SYSTEM_SWAN]=new DivSysDef(
"WonderSwan", NULL, 0x96, 0, 4, false, true, 0x171, false, "WonderSwan", NULL, 0x96, 0, 4, false, true, 0x171, false,
"developed by the same team under the Game Boy and the Virtual Boy...",
{"Wave", "Wave/PCM", "Wave", "Wave/Noise"}, {"Wave", "Wave/PCM", "Wave", "Wave/Noise"},
{"CH1", "CH2", "CH3", "CH4"}, {"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_WAVE, DIV_CH_PCM, DIV_CH_WAVE, DIV_CH_NOISE}, {DIV_CH_WAVE, DIV_CH_PCM, DIV_CH_WAVE, DIV_CH_NOISE},
@ -1372,6 +1442,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPZ]=new DivSysDef( sysDefs[DIV_SYSTEM_OPZ]=new DivSysDef(
"Yamaha YM2414 (OPZ)", NULL, 0x98, 0, 8, true, false, 0, false, "Yamaha YM2414 (OPZ)", NULL, 0x98, 0, 8, true, false, 0, false,
"like OPM, but with more waveforms, fixed frequency mode and totally... undocumented.\nused in the Yamaha TX81Z and some other synthesizers.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -1392,6 +1463,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_POKEMINI]=new DivSysDef( sysDefs[DIV_SYSTEM_POKEMINI]=new DivSysDef(
"Pokémon Mini", NULL, 0x99, 0, 1, false, true, 0, false, "Pokémon Mini", NULL, 0x99, 0, 1, false, true, 0, false,
"this one is like PC Speaker but has duty cycles.",
{"Square"}, {"Square"},
{"SQ"}, {"SQ"},
{DIV_CH_PULSE}, {DIV_CH_PULSE},
@ -1400,6 +1472,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_SEGAPCM]=new DivSysDef( sysDefs[DIV_SYSTEM_SEGAPCM]=new DivSysDef(
"SegaPCM", NULL, 0x9b, 0, 16, false, true, 0x151, false, "SegaPCM", NULL, 0x9b, 0, 16, false, true, 0x151, false,
"used in some Sega arcade boards (like OutRun), and usually paired with a YM2151.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16"},
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1411,6 +1484,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_VBOY]=new DivSysDef( sysDefs[DIV_SYSTEM_VBOY]=new DivSysDef(
"Virtual Boy", NULL, 0x9c, 0, 6, false, true, 0, false, "Virtual Boy", NULL, 0x9c, 0, 6, false, true, 0, false,
"a console which failed to sell well due to its headache-inducing features.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Noise"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Noise"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "NO"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "NO"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE},
@ -1419,6 +1493,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_VRC7]=new DivSysDef( sysDefs[DIV_SYSTEM_VRC7]=new DivSysDef(
"Konami VRC7", NULL, 0x9d, 0, 6, true, false, 0x151, false, "Konami VRC7", NULL, 0x9d, 0, 6, true, false, 0x151, false,
"like OPLL, but even more cost reductions applied. three less FM channels, and no drums mode...",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"},
{"F1", "F2", "F3", "F4", "F5", "F6"}, {"F1", "F2", "F3", "F4", "F5", "F6"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -1430,6 +1505,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2610B]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2610B]=new DivSysDef(
"Yamaha YM2610B (OPNB-B)", NULL, 0x9e, 0, 16, true, false, 0x151, false, "Yamaha YM2610B (OPNB-B)", NULL, 0x9e, 0, 16, true, false, 0x151, false,
"so Taito asked Yamaha if they could get the two missing FM channels back, and Yamaha gladly provided them with this chip.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"},
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1441,6 +1517,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_SFX_BEEPER]=new DivSysDef( sysDefs[DIV_SYSTEM_SFX_BEEPER]=new DivSysDef(
"ZX Spectrum Beeper", NULL, 0x9f, 0, 6, false, true, 0, false, "ZX Spectrum Beeper", NULL, 0x9f, 0, 6, false, true, 0, false,
"the ZX Spectrum only had a basic beeper capable of...\n...a bunch of thin pulses and tons of other interesting stuff!\nFurnace provides a thin pulse system.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
@ -1449,6 +1526,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2612_EXT]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2612_EXT]=new DivSysDef(
"Yamaha YM2612 (OPN2) Extended Channel 3", NULL, 0xa0, 0, 9, true, false, 0x150, false, "Yamaha YM2612 (OPN2) Extended Channel 3", NULL, 0xa0, 0, 9, true, false, 0x150, false,
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies.",
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6"}, {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6"},
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6"}, {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM},
@ -1459,11 +1537,14 @@ void DivEngine::registerSystems() {
); );
sysDefs[DIV_SYSTEM_SCC]=new DivSysDef( sysDefs[DIV_SYSTEM_SCC]=new DivSysDef(
"Konami SCC", NULL, 0xa1, 0, 5, false, true, 0, false, "Konami SCC", NULL, 0xa1, 0, 5, false, true, 0x161, false,
"a wavetable chip made by Konami for use with the MSX.\nthe last channel shares its wavetable with the previous one though.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"},
{"CH1", "CH2", "CH3", "CH4", "CH5"}, {"CH1", "CH2", "CH3", "CH4", "CH5"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
{DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC} {DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC},
{},
waveOnlyEffectHandler
); );
auto oplDrumsEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { auto oplDrumsEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool {
@ -1482,6 +1563,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPL_DRUMS]=new DivSysDef( sysDefs[DIV_SYSTEM_OPL_DRUMS]=new DivSysDef(
"Yamaha YM3526 (OPL) with drums", NULL, 0xa2, 0, 11, true, false, 0x151, false, "Yamaha YM3526 (OPL) with drums", NULL, 0xa2, 0, 11, true, false, 0x151, false,
"the OPL chip but with drums mode enabled.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"},
{"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
@ -1493,6 +1575,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPL2_DRUMS]=new DivSysDef( sysDefs[DIV_SYSTEM_OPL2_DRUMS]=new DivSysDef(
"Yamaha YM3812 (OPL2) with drums", NULL, 0xa3, 0, 11, true, false, 0x151, false, "Yamaha YM3812 (OPL2) with drums", NULL, 0xa3, 0, 11, true, false, 0x151, false,
"the OPL2 chip but with drums mode enabled.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"},
{"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
@ -1504,6 +1587,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPL3_DRUMS]=new DivSysDef( sysDefs[DIV_SYSTEM_OPL3_DRUMS]=new DivSysDef(
"Yamaha YMF262 (OPL3) with drums", NULL, 0xa4, 0, 20, true, false, 0x151, false, "Yamaha YMF262 (OPL3) with drums", NULL, 0xa4, 0, 20, true, false, 0x151, false,
"the OPL3 chip but with drums mode enabled.",
{"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat"}, {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat"},
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "BD", "SD", "TM", "TP", "HH"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "BD", "SD", "TM", "TP", "HH"},
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
@ -1515,6 +1599,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2610_FULL]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2610_FULL]=new DivSysDef(
"Yamaha YM2610 (OPNB)", NULL, 0xa5, 0, 14, true, false, 0x151, false, "Yamaha YM2610 (OPNB)", NULL, 0xa5, 0, 14, true, false, 0x151, false,
"this chip was used in SNK's Neo Geo arcade board and video game console.\nit's like OPNA but the rhythm channels are ADPCM channels and two FM channels went missing.",
{"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"},
{"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1526,6 +1611,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2610_FULL_EXT]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2610_FULL_EXT]=new DivSysDef(
"Yamaha YM2610 (OPNB) Extended Channel 2", NULL, 0xa6, 0, 17, true, false, 0x151, false, "Yamaha YM2610 (OPNB) Extended Channel 2", NULL, 0xa6, 0, 17, true, false, 0x151, false,
"this chip was used in SNK's Neo Geo arcade board and video game console.\nit's like OPNA but the rhythm channels are ADPCM channels and two FM channels went missing.\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies.",
{"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"},
{"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"},
{DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1537,6 +1623,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPLL_DRUMS]=new DivSysDef( sysDefs[DIV_SYSTEM_OPLL_DRUMS]=new DivSysDef(
"Yamaha YM2413 (OPLL) with drums", NULL, 0xa7, 0, 11, true, false, 0x150, false, "Yamaha YM2413 (OPLL) with drums", NULL, 0xa7, 0, 11, true, false, 0x150, false,
"the OPLL chips but with drums mode turned on.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"},
{"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
@ -1548,6 +1635,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_LYNX]=new DivSysDef( sysDefs[DIV_SYSTEM_LYNX]=new DivSysDef(
"Atari Lynx", NULL, 0xa8, 0, 4, false, true, 0, false, "Atari Lynx", NULL, 0xa8, 0, 4, false, true, 0, false,
"a portable console made by Atari. it has all of Atari's trademark waveforms.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"}, {"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
@ -1566,6 +1654,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_QSOUND]=new DivSysDef( sysDefs[DIV_SYSTEM_QSOUND]=new DivSysDef(
"Capcom QSound", NULL, 0xe0, 0, 19, false, true, 0x161, false, "Capcom QSound", NULL, 0xe0, 0, 19, false, true, 0x161, false,
"used in some of Capcom's arcade boards. surround-like sampled sound with echo.",
{"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"}, {"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"},
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "A1", "A2", "A3"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "A1", "A2", "A3"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
@ -1596,6 +1685,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_VERA]=new DivSysDef( sysDefs[DIV_SYSTEM_VERA]=new DivSysDef(
"VERA", NULL, 0xac, 0, 17, false, true, 0, false, "VERA", NULL, 0xac, 0, 17, false, true, 0, false,
"the chip used in a computer design created by The 8-Bit Guy.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM"},
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM},
@ -1618,6 +1708,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_YM2610B_EXT]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2610B_EXT]=new DivSysDef(
"Yamaha YM2610B (OPNB-B) Extended Channel 3", NULL, 0xde, 0, 19, true, false, 0x151, false, "Yamaha YM2610B (OPNB-B) Extended Channel 3", NULL, 0xde, 0, 19, true, false, 0x151, false,
"so Taito asked Yamaha if they could get the two missing FM channels back, and Yamaha gladly provided them with this chip.\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies.",
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"},
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1629,6 +1720,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_SEGAPCM_COMPAT]=new DivSysDef( sysDefs[DIV_SYSTEM_SEGAPCM_COMPAT]=new DivSysDef(
"SegaPCM (compatible 5-channel mode)", NULL, 0xa9, 0, 5, false, true, 0x151, false, "SegaPCM (compatible 5-channel mode)", NULL, 0xa9, 0, 5, false, true, 0x151, false,
"this is the same thing as SegaPCM, but only exposes 5 of the channels for compatibility with DefleMask.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"},
{"P1", "P2", "P3", "P4", "P5"}, {"P1", "P2", "P3", "P4", "P5"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1640,6 +1732,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_X1_010]=new DivSysDef( sysDefs[DIV_SYSTEM_X1_010]=new DivSysDef(
"Seta/Allumer X1-010", NULL, 0xb0, 0, 16, false, true, 0x171, false, "Seta/Allumer X1-010", NULL, 0xb0, 0, 16, false, true, 0x171, false,
"a sound chip used in several Seta/Allumer-manufactured arcade boards with too many channels of wavetable sound, which also are capable of sampled sound.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16"},
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
@ -1690,6 +1783,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_BUBSYS_WSG]=new DivSysDef( sysDefs[DIV_SYSTEM_BUBSYS_WSG]=new DivSysDef(
"Konami Bubble System WSG", NULL, 0xad, 0, 2, false, true, 0, false, "Konami Bubble System WSG", NULL, 0xad, 0, 2, false, true, 0, false,
"this is the wavetable part of the Bubble System, which also had two AY-3-8910s.",
{"Channel 1", "Channel 2"}, {"Channel 1", "Channel 2"},
{"CH1", "CH2"}, {"CH1", "CH2"},
{DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE},
@ -1701,6 +1795,7 @@ void DivEngine::registerSystems() {
// to Grauw: feel free to change this to 24 during development of OPL4's PCM part. // to Grauw: feel free to change this to 24 during development of OPL4's PCM part.
sysDefs[DIV_SYSTEM_OPL4]=new DivSysDef( sysDefs[DIV_SYSTEM_OPL4]=new DivSysDef(
"Yamaha OPL4", NULL, 0xae, 0, 42, true, true, 0, false, "Yamaha OPL4", NULL, 0xae, 0, 42, true, true, 0, false,
"like OPL3, but this time it also has a 24-channel version of MultiPCM.",
{"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "PCM 17", "PCM 18", "PCM 19", "PCM 20", "PCM 21", "PCM 22", "PCM 23", "PCM 24"}, {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "PCM 17", "PCM 18", "PCM 19", "PCM 20", "PCM 21", "PCM 22", "PCM 23", "PCM 24"},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1709,6 +1804,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_OPL4_DRUMS]=new DivSysDef( sysDefs[DIV_SYSTEM_OPL4_DRUMS]=new DivSysDef(
"Yamaha OPL4 with drums", NULL, 0xaf, 0, 44, true, true, 0, false, "Yamaha OPL4 with drums", NULL, 0xaf, 0, 44, true, true, 0, false,
"the OPL4 but with drums mode turned on.",
{"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "PCM 17", "PCM 18", "PCM 19", "PCM 20", "PCM 21", "PCM 22", "PCM 23", "PCM 24"}, {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "PCM 17", "PCM 18", "PCM 19", "PCM 20", "PCM 21", "PCM 22", "PCM 23", "PCM 24"},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1717,6 +1813,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_ES5506]=new DivSysDef( sysDefs[DIV_SYSTEM_ES5506]=new DivSysDef(
"Ensoniq ES5506", NULL, 0xb1, 0, 32, false, true, 0/*0x171*/, false, "Ensoniq ES5506", NULL, 0xb1, 0, 32, false, true, 0/*0x171*/, false,
"a sample chip used in the Ensoniq's unique transwave synthesizers, \nand SoundScape series PC ISA soundcards...\nthat's just yet another (partially)SB compatible one with emulated OPL3 and MIDI ROMpler.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28", "Channel 29", "Channel 30", "Channel 31", "Channel 32"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28", "Channel 29", "Channel 30", "Channel 31", "Channel 32"},
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1742,14 +1839,6 @@ void DivEngine::registerSystems() {
case 0x17: // filter coefficient K2, 8 bit MSB case 0x17: // filter coefficient K2, 8 bit MSB
dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K2,ch,(effectVal&0xff)<<8,0xff00)); dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K2,ch,(effectVal&0xff)<<8,0xff00));
break; break;
case 0x18: // filter coefficient K1 slide up
case 0x19: // filter coefficient K1 slide down
dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K1_SLIDE,ch,effectVal,effect&0x01));
break;
case 0x1a: // filter coefficient K2 slide up
case 0x1b: // filter coefficient K2 slide down
dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K2_SLIDE,ch,effectVal,effect&0x01));
break;
case 0x20: case 0x20:
case 0x21: // envelope ECOUNT case 0x21: // envelope ECOUNT
dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_COUNT,ch,((effect&0x01)<<8)|effectVal)); dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_COUNT,ch,((effect&0x01)<<8)|effectVal));
@ -1779,35 +1868,66 @@ void DivEngine::registerSystems() {
break; break;
} }
return true; return true;
},
[this](int ch, unsigned char effect, unsigned char effectVal) -> bool {
switch (effect) {
case 0x12: // pause
dispatchCmd(DivCommand(DIV_CMD_ES5506_PAUSE,ch,effectVal&1));
break;
case 0x18: // filter coefficient K1 slide up
case 0x19: // filter coefficient K1 slide down
dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K1_SLIDE,ch,effectVal,effect&0x01));
break;
case 0x1a: // filter coefficient K2 slide up
case 0x1b: // filter coefficient K2 slide down
dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K2_SLIDE,ch,effectVal,effect&0x01));
break;
default:
return false;
break;
}
return true;
} }
); );
sysDefs[DIV_SYSTEM_Y8950]=new DivSysDef( sysDefs[DIV_SYSTEM_Y8950]=new DivSysDef(
"Yamaha Y8950", NULL, 0xb2, 0, 10, true, false, 0, false, "Yamaha Y8950", NULL, 0xb2, 0, 10, true, false, 0, false,
"like OPL but with an ADPCM channel.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9", "PCM"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9", "PCM"},
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "PCM"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "PCM"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA},
{},
oplEffectHandler,
fmOPLPostEffectHandler
); );
sysDefs[DIV_SYSTEM_Y8950_DRUMS]=new DivSysDef( sysDefs[DIV_SYSTEM_Y8950_DRUMS]=new DivSysDef(
"Yamaha Y8950 with drums", NULL, 0xb3, 0, 12, true, false, 0, false, "Yamaha Y8950 with drums", NULL, 0xb3, 0, 12, true, false, 0, false,
"the Y8950 chip, in drums mode.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM"}, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM"},
{"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH", "PCM"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH", "PCM"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA},
{},
oplEffectHandler,
fmOPLPostEffectHandler
); );
sysDefs[DIV_SYSTEM_SCC_PLUS]=new DivSysDef( sysDefs[DIV_SYSTEM_SCC_PLUS]=new DivSysDef(
"Konami SCC+", NULL, 0xb4, 0, 5, false, true, 0, false, "Konami SCC+", NULL, 0xb4, 0, 5, false, true, 0x161, false,
"this is a variant of Konami's SCC chip with the last channel's wavetable being independent.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"},
{"CH1", "CH2", "CH3", "CH4", "CH5"}, {"CH1", "CH2", "CH3", "CH4", "CH5"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
{DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC} {DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC},
{},
waveOnlyEffectHandler
); );
sysDefs[DIV_SYSTEM_SOUND_UNIT]=new DivSysDef( sysDefs[DIV_SYSTEM_SOUND_UNIT]=new DivSysDef(
"tildearrow Sound Unit", NULL, 0xb5, 0, 8, false, true, 0, false, "tildearrow Sound Unit", NULL, 0xb5, 0, 8, false, true, 0, false,
"tildearrow's fantasy sound chip. put SID, AY and VERA in a blender, and you get this!",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
@ -1817,6 +1937,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_MSM6295]=new DivSysDef( sysDefs[DIV_SYSTEM_MSM6295]=new DivSysDef(
"OKI MSM6295", NULL, 0xaa, 0, 4, false, true, 0, false, "OKI MSM6295", NULL, 0xaa, 0, 4, false, true, 0, false,
"an ADPCM sound chip manufactured by OKI and used in many arcade boards.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"}, {"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1825,6 +1946,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef( sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef(
"OKI MSM6258", NULL, 0xab, 0, 1, false, true, 0, false, "OKI MSM6258", NULL, 0xab, 0, 1, false, true, 0, false,
"an ADPCM sound chip manufactured by OKI and used in the Sharp X68000.",
{"Sample"}, {"Sample"},
{"PCM"}, {"PCM"},
{DIV_CH_PCM}, {DIV_CH_PCM},
@ -1833,6 +1955,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false,
"this is a system designed for testing purposes..",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},

View file

@ -564,6 +564,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
break; break;
} }
break; break;
case DIV_SYSTEM_OPN:
w->writeC(5|baseAddr1);
w->writeC(write.addr&0xff);
w->writeC(write.val);
break;
case DIV_SYSTEM_OPLL: case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_OPLL_DRUMS:
case DIV_SYSTEM_VRC7: case DIV_SYSTEM_VRC7:
@ -637,6 +642,71 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
break; break;
} }
break; break;
case DIV_SYSTEM_SCC:
if (write.addr<0x80) {
w->writeC(0xd2);
w->writeC(baseAddr2|0);
w->writeC(write.addr&0x7f);
w->writeC(write.val&0xff);
} else if (write.addr<0x8a) {
w->writeC(0xd2);
w->writeC(baseAddr2|1);
w->writeC((write.addr-0x80)&0x7f);
w->writeC(write.val&0xff);
} else if (write.addr<0x8f) {
w->writeC(0xd2);
w->writeC(baseAddr2|2);
w->writeC((write.addr-0x8a)&0x7f);
w->writeC(write.val&0xff);
} else if (write.addr<0x90) {
w->writeC(0xd2);
w->writeC(baseAddr2|3);
w->writeC((write.addr-0x8f)&0x7f);
w->writeC(write.val&0xff);
} else if (write.addr>=0xe0) {
w->writeC(0xd2);
w->writeC(baseAddr2|5);
w->writeC((write.addr-0xe0)&0x7f);
w->writeC(write.val&0xff);
} else {
logW("SCC: writing to unmapped address %.2x!",write.addr);
}
break;
case DIV_SYSTEM_SCC_PLUS:
if (write.addr<0x80) {
w->writeC(0xd2);
w->writeC(baseAddr2|0);
w->writeC(write.addr&0x7f);
w->writeC(write.val&0xff);
} else if (write.addr<0xa0) {
w->writeC(0xd2);
w->writeC(baseAddr2|4);
w->writeC(write.addr);
w->writeC(write.val&0xff);
} else if (write.addr<0xaa) {
w->writeC(0xd2);
w->writeC(baseAddr2|1);
w->writeC((write.addr-0xa0)&0x7f);
w->writeC(write.val&0xff);
} else if (write.addr<0xaf) {
w->writeC(0xd2);
w->writeC(baseAddr2|2);
w->writeC((write.addr-0xaa)&0x7f);
w->writeC(write.val&0xff);
} else if (write.addr<0xb0) {
w->writeC(0xd2);
w->writeC(baseAddr2|3);
w->writeC((write.addr-0xaf)&0x7f);
w->writeC(write.val&0xff);
} else if (write.addr>=0xe0) {
w->writeC(0xd2);
w->writeC(baseAddr2|5);
w->writeC((write.addr-0xe0)&0x7f);
w->writeC(write.val&0xff);
} else {
logW("SCC+: writing to unmapped address %.2x!",write.addr);
}
break;
default: default:
logW("write not handled!"); logW("write not handled!");
break; break;
@ -876,11 +946,41 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
} }
break; break;
case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930: case DIV_SYSTEM_AY8930: {
if (!hasAY) { if (!hasAY) {
bool hasClockDivider=false; // Configurable clock divider
bool hasStereo=true; // Stereo
hasAY=disCont[i].dispatch->chipClock; hasAY=disCont[i].dispatch->chipClock;
ayConfig=(song.system[i]==DIV_SYSTEM_AY8930)?3:0;
ayFlags=1; ayFlags=1;
if (song.system[i]==DIV_SYSTEM_AY8930) { // AY8930
ayConfig=0x03;
hasClockDivider=true;
} else {
switch ((song.systemFlags[i]>>4)&3) {
default:
case 0: // AY8910
ayConfig=0x00;
break;
case 1: // YM2149
ayConfig=0x10;
hasClockDivider=true;
break;
case 2: // Sunsoft 5B
ayConfig=0x10;
ayFlags|=0x12; // Clock internally divided, Single sound output
hasStereo=false; // due to above, can't be per-channel stereo configurable
break;
case 3: // AY8914
ayConfig=0x04;
break;
}
}
if (hasClockDivider && ((song.systemFlags[i]>>7)&1)) {
ayFlags|=0x10;
}
if (hasStereo && ((song.systemFlags[i]>>6)&1)) {
ayFlags|=0x80;
}
willExport[i]=true; willExport[i]=true;
} else if (!(hasAY&0x40000000)) { } else if (!(hasAY&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
@ -889,6 +989,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
howManyChips++; howManyChips++;
} }
break; break;
}
case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_SAA1099:
if (!hasSAA) { if (!hasSAA) {
hasSAA=disCont[i].dispatch->chipClock; hasSAA=disCont[i].dispatch->chipClock;
@ -924,6 +1025,18 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
howManyChips++; howManyChips++;
} }
break; break;
case DIV_SYSTEM_OPN:
if (!hasOPN) {
hasOPN=disCont[i].dispatch->chipClock;
willExport[i]=true;
writeDACSamples=true;
} else if (!(hasOPN&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
hasOPN|=0x40000000;
howManyChips++;
}
break;
case DIV_SYSTEM_OPLL: case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_OPLL_DRUMS:
case DIV_SYSTEM_VRC7: case DIV_SYSTEM_VRC7:
@ -1042,6 +1155,24 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
howManyChips++; howManyChips++;
} }
break; break;
case DIV_SYSTEM_SCC:
case DIV_SYSTEM_SCC_PLUS:
if (!hasK051649) {
hasK051649=disCont[i].dispatch->chipClock;
if (song.system[i]==DIV_SYSTEM_SCC_PLUS) {
hasK051649|=0x80000000;
}
willExport[i]=true;
} else if (!(hasK051649&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
hasK051649|=0x40000000;
if (song.system[i]==DIV_SYSTEM_SCC_PLUS) {
hasK051649|=0x80000000;
}
howManyChips++;
}
break;
default: default:
break; break;
} }

View file

@ -91,7 +91,7 @@ const char* aboutLine[]={
"ymfm by Aaron Giles", "ymfm by Aaron Giles",
"MAME SN76496 by Nicola Salmoria", "MAME SN76496 by Nicola Salmoria",
"MAME AY-3-8910 by Couriersud", "MAME AY-3-8910 by Couriersud",
"with AY8930 fixes by Eulous", "with AY8930 fixes by Eulous, cam900 and Grauw",
"MAME SAA1099 by Juergen Buchmueller and Manuel Abadia", "MAME SAA1099 by Juergen Buchmueller and Manuel Abadia",
"SAASound by Dave Hooper and Simon Owen", "SAASound by Dave Hooper and Simon Owen",
"SameBoy by Lior Halphon", "SameBoy by Lior Halphon",

View file

@ -20,7 +20,7 @@
#define DETERMINE_FIRST \ #define DETERMINE_FIRST \
int firstChannel=0; \ int firstChannel=0; \
for (int i=0; i<e->getTotalChannelCount(); i++) { \ for (int i=0; i<e->getTotalChannelCount(); i++) { \
if (e->song.chanShow[i]) { \ if (e->curSubSong->chanShow[i]) { \
firstChannel=i; \ firstChannel=i; \
break; \ break; \
} \ } \
@ -29,7 +29,7 @@
#define DETERMINE_LAST \ #define DETERMINE_LAST \
int lastChannel=0; \ int lastChannel=0; \
for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \ for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \
if (e->song.chanShow[i]) { \ if (e->curSubSong->chanShow[i]) { \
lastChannel=i+1; \ lastChannel=i+1; \
break; \ break; \
} \ } \

View file

@ -38,7 +38,7 @@ void FurnaceGUI::drawChannels() {
ImGui::PushID(i); ImGui::PushID(i);
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Checkbox("##Visible",&e->song.chanShow[i]); ImGui::Checkbox("##Visible",&e->curSubSong->chanShow[i]);
ImGui::SameLine(); ImGui::SameLine();
ImGui::BeginDisabled(i==0); ImGui::BeginDisabled(i==0);
if (ImGui::Button(ICON_FA_CHEVRON_UP)) { if (ImGui::Button(ICON_FA_CHEVRON_UP)) {
@ -53,10 +53,10 @@ void FurnaceGUI::drawChannels() {
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->song.chanName[i]); ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->curSubSong->chanName[i]);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->song.chanShortName[i]); ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->curSubSong->chanShortName[i]);
ImGui::PopID(); ImGui::PopID();
} }
ImGui::EndTable(); ImGui::EndTable();

View file

@ -21,10 +21,21 @@
#include "actionUtil.h" #include "actionUtil.h"
void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) { void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) {
DETERMINE_FIRST_LAST;
if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) { if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) {
curNibble=false; curNibble=false;
} }
if (fullRow) {
selStart.xCoarse=firstChannel;
selStart.xFine=0;
selEnd.xCoarse=lastChannel-1;
selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2;
selStart.y=y;
selEnd.y=y;
} else {
cursor.xCoarse=xCoarse; cursor.xCoarse=xCoarse;
cursor.xFine=xFine; cursor.xFine=xFine;
cursor.y=y; cursor.y=y;
@ -34,15 +45,24 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) {
selEnd.xCoarse=xCoarse; selEnd.xCoarse=xCoarse;
selEnd.xFine=xFine; selEnd.xFine=xFine;
selEnd.y=y; selEnd.y=y;
}
selecting=true; selecting=true;
selectingFull=fullRow;
e->setMidiBaseChan(cursor.xCoarse); e->setMidiBaseChan(cursor.xCoarse);
} }
void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) { void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) {
if (!selecting) return; if (!selecting) return;
if (selectingFull) {
DETERMINE_LAST;
selEnd.xCoarse=lastChannel-1;
selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2;
selEnd.y=y;
} else {
selEnd.xCoarse=xCoarse; selEnd.xCoarse=xCoarse;
selEnd.xFine=xFine; selEnd.xFine=xFine;
selEnd.y=y; selEnd.y=y;
}
} }
void FurnaceGUI::finishSelection() { void FurnaceGUI::finishSelection() {
@ -66,6 +86,7 @@ void FurnaceGUI::finishSelection() {
selEnd.xFine^=selStart.xFine; selEnd.xFine^=selStart.xFine;
} }
selecting=false; selecting=false;
selectingFull=false;
// boundary check // boundary check
int chanCount=e->getTotalChannelCount(); int chanCount=e->getTotalChannelCount();
@ -73,21 +94,21 @@ void FurnaceGUI::finishSelection() {
if (selStart.xCoarse<0) selStart.xCoarse=0; if (selStart.xCoarse<0) selStart.xCoarse=0;
if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1; if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1;
if (selStart.y<0) selStart.y=0; if (selStart.y<0) selStart.y=0;
if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1; if (selStart.y>=e->curSubSong->patLen) selStart.y=e->curSubSong->patLen-1;
if (selEnd.xCoarse<0) selEnd.xCoarse=0; if (selEnd.xCoarse<0) selEnd.xCoarse=0;
if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1; if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1;
if (selEnd.y<0) selEnd.y=0; if (selEnd.y<0) selEnd.y=0;
if (selEnd.y>=e->song.patLen) selEnd.y=e->song.patLen-1; if (selEnd.y>=e->curSubSong->patLen) selEnd.y=e->curSubSong->patLen-1;
if (cursor.xCoarse<0) cursor.xCoarse=0; if (cursor.xCoarse<0) cursor.xCoarse=0;
if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1; if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1;
if (cursor.y<0) cursor.y=0; if (cursor.y<0) cursor.y=0;
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
if (e->song.chanCollapse[selStart.xCoarse]==3) { if (e->curSubSong->chanCollapse[selStart.xCoarse]==3) {
selStart.xFine=0; selStart.xFine=0;
} }
if (e->song.chanCollapse[selEnd.xCoarse] && selEnd.xFine>=(3-e->song.chanCollapse[selEnd.xCoarse])) { if (e->curSubSong->chanCollapse[selEnd.xCoarse] && selEnd.xFine>=(3-e->curSubSong->chanCollapse[selEnd.xCoarse])) {
selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; selEnd.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
} }
e->setMidiBaseChan(cursor.xCoarse); e->setMidiBaseChan(cursor.xCoarse);
@ -105,7 +126,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
demandScrollX=true; demandScrollX=true;
if (x>0) { if (x>0) {
for (int i=0; i<x; i++) { for (int i=0; i<x; i++) {
if (++cursor.xFine>=(e->song.chanCollapse[cursor.xCoarse]?(4-e->song.chanCollapse[cursor.xCoarse]):(3+e->song.pat[cursor.xCoarse].effectCols*2))) { if (++cursor.xFine>=(e->curSubSong->chanCollapse[cursor.xCoarse]?(4-e->curSubSong->chanCollapse[cursor.xCoarse]):(3+e->curPat[cursor.xCoarse].effectCols*2))) {
cursor.xFine=0; cursor.xFine=0;
if (++cursor.xCoarse>=lastChannel) { if (++cursor.xCoarse>=lastChannel) {
if (settings.wrapHorizontal!=0 && !select) { if (settings.wrapHorizontal!=0 && !select) {
@ -113,10 +134,10 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (settings.wrapHorizontal==2) y++; if (settings.wrapHorizontal==2) y++;
} else { } else {
cursor.xCoarse=lastChannel-1; cursor.xCoarse=lastChannel-1;
cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?(3-e->song.chanCollapse[cursor.xCoarse]):(2+e->song.pat[cursor.xCoarse].effectCols*2); cursor.xFine=e->curSubSong->chanCollapse[cursor.xCoarse]?(3-e->curSubSong->chanCollapse[cursor.xCoarse]):(2+e->curPat[cursor.xCoarse].effectCols*2);
} }
} else { } else {
while (!e->song.chanShow[cursor.xCoarse]) { while (!e->curSubSong->chanShow[cursor.xCoarse]) {
cursor.xCoarse++; cursor.xCoarse++;
if (cursor.xCoarse>=e->getTotalChannelCount()) break; if (cursor.xCoarse>=e->getTotalChannelCount()) break;
} }
@ -129,21 +150,21 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (--cursor.xCoarse<firstChannel) { if (--cursor.xCoarse<firstChannel) {
if (settings.wrapHorizontal!=0 && !select) { if (settings.wrapHorizontal!=0 && !select) {
cursor.xCoarse=lastChannel-1; cursor.xCoarse=lastChannel-1;
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; cursor.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
if (settings.wrapHorizontal==2) y--; if (settings.wrapHorizontal==2) y--;
} else { } else {
cursor.xCoarse=firstChannel; cursor.xCoarse=firstChannel;
cursor.xFine=0; cursor.xFine=0;
} }
} else { } else {
while (!e->song.chanShow[cursor.xCoarse]) { while (!e->curSubSong->chanShow[cursor.xCoarse]) {
cursor.xCoarse--; cursor.xCoarse--;
if (cursor.xCoarse<0) break; if (cursor.xCoarse<0) break;
} }
if (e->song.chanCollapse[cursor.xCoarse]) { if (e->curSubSong->chanCollapse[cursor.xCoarse]) {
cursor.xFine=3-e->song.chanCollapse[cursor.xCoarse]; cursor.xFine=3-e->curSubSong->chanCollapse[cursor.xCoarse];
} else { } else {
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; cursor.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
} }
} }
} }
@ -154,18 +175,18 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (y>0) { if (y>0) {
for (int i=0; i<y; i++) { for (int i=0; i<y; i++) {
cursor.y++; cursor.y++;
if (cursor.y>=e->song.patLen) { if (cursor.y>=e->curSubSong->patLen) {
if (settings.wrapVertical!=0 && !select) { if (settings.wrapVertical!=0 && !select) {
cursor.y=0; cursor.y=0;
if (settings.wrapVertical==2) { if (settings.wrapVertical==2) {
if ((!e->isPlaying() || !followPattern) && curOrder<(e->song.ordersLen-1)) { if ((!e->isPlaying() || !followPattern) && curOrder<(e->curSubSong->ordersLen-1)) {
setOrder(curOrder+1); setOrder(curOrder+1);
} else { } else {
cursor.y=e->song.patLen-1; cursor.y=e->curSubSong->patLen-1;
} }
} }
} else { } else {
cursor.y=e->song.patLen-1; cursor.y=e->curSubSong->patLen-1;
} }
} }
} }
@ -174,7 +195,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
cursor.y--; cursor.y--;
if (cursor.y<0) { if (cursor.y<0) {
if (settings.wrapVertical!=0 && !select) { if (settings.wrapVertical!=0 && !select) {
cursor.y=e->song.patLen-1; cursor.y=e->curSubSong->patLen-1;
if (settings.wrapVertical==2) { if (settings.wrapVertical==2) {
if ((!e->isPlaying() || !followPattern) && curOrder>0) { if ((!e->isPlaying() || !followPattern) && curOrder>0) {
setOrder(curOrder-1); setOrder(curOrder-1);
@ -208,7 +229,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
do { do {
cursor.xCoarse--; cursor.xCoarse--;
if (cursor.xCoarse<0) break; if (cursor.xCoarse<0) break;
} while (!e->song.chanShow[cursor.xCoarse]); } while (!e->curSubSong->chanShow[cursor.xCoarse]);
if (cursor.xCoarse<firstChannel) { if (cursor.xCoarse<firstChannel) {
if (overflow) { if (overflow) {
cursor.xCoarse=lastChannel-1; cursor.xCoarse=lastChannel-1;
@ -232,7 +253,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) {
do { do {
cursor.xCoarse++; cursor.xCoarse++;
if (cursor.xCoarse>=e->getTotalChannelCount()) break; if (cursor.xCoarse>=e->getTotalChannelCount()) break;
} while (!e->song.chanShow[cursor.xCoarse]); } while (!e->curSubSong->chanShow[cursor.xCoarse]);
if (cursor.xCoarse>=lastChannel) { if (cursor.xCoarse>=lastChannel) {
if (overflow) { if (overflow) {
cursor.xCoarse=firstChannel; cursor.xCoarse=firstChannel;
@ -269,14 +290,14 @@ void FurnaceGUI::moveCursorTop(bool select) {
void FurnaceGUI::moveCursorBottom(bool select) { void FurnaceGUI::moveCursorBottom(bool select) {
finishSelection(); finishSelection();
curNibble=false; curNibble=false;
if (cursor.y==e->song.patLen-1) { if (cursor.y==e->curSubSong->patLen-1) {
DETERMINE_LAST; DETERMINE_LAST;
cursor.xCoarse=lastChannel-1; cursor.xCoarse=lastChannel-1;
if (cursor.xCoarse<0) cursor.xCoarse=0; if (cursor.xCoarse<0) cursor.xCoarse=0;
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; cursor.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
demandScrollX=true; demandScrollX=true;
} else { } else {
cursor.y=e->song.patLen-1; cursor.y=e->curSubSong->patLen-1;
} }
if (!select) { if (!select) {
selStart=cursor; selStart=cursor;
@ -289,7 +310,7 @@ void FurnaceGUI::moveCursorBottom(bool select) {
void FurnaceGUI::editAdvance() { void FurnaceGUI::editAdvance() {
finishSelection(); finishSelection();
cursor.y+=editStep; cursor.y+=editStep;
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
selStart=cursor; selStart=cursor;
selEnd=cursor; selEnd=cursor;
updateScroll(cursor.y); updateScroll(cursor.y);

View file

@ -153,6 +153,8 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("rate: %d",sample->rate); ImGui::Text("rate: %d",sample->rate);
ImGui::Text("centerRate: %d",sample->centerRate); ImGui::Text("centerRate: %d",sample->centerRate);
ImGui::Text("loopStart: %d",sample->loopStart); ImGui::Text("loopStart: %d",sample->loopStart);
ImGui::Text("loopEnd: %d",sample->loopEnd);
ImGui::Text("loopMode: %d",(int)(sample->loopMode));
ImGui::Text("loopOffP: %d",sample->loopOffP); ImGui::Text("loopOffP: %d",sample->loopOffP);
ImGui::Text("depth: %d",sample->depth); ImGui::Text("depth: %d",sample->depth);
ImGui::Text("length8: %d",sample->length8); ImGui::Text("length8: %d",sample->length8);

View file

@ -470,7 +470,7 @@ void FurnaceGUI::doAction(int what) {
e->unmuteAll(); e->unmuteAll();
break; break;
case GUI_ACTION_PAT_NEXT_ORDER: case GUI_ACTION_PAT_NEXT_ORDER:
if (curOrder<e->song.ordersLen-1) { if (curOrder<e->curSubSong->ordersLen-1) {
setOrder(curOrder+1); setOrder(curOrder+1);
} }
break; break;
@ -481,21 +481,21 @@ void FurnaceGUI::doAction(int what) {
break; break;
case GUI_ACTION_PAT_COLLAPSE: case GUI_ACTION_PAT_COLLAPSE:
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
if (e->song.chanCollapse[cursor.xCoarse]==0) { if (e->curSubSong->chanCollapse[cursor.xCoarse]==0) {
e->song.chanCollapse[cursor.xCoarse]=3; e->curSubSong->chanCollapse[cursor.xCoarse]=3;
} else if (e->song.chanCollapse[cursor.xCoarse]>0) { } else if (e->curSubSong->chanCollapse[cursor.xCoarse]>0) {
e->song.chanCollapse[cursor.xCoarse]--; e->curSubSong->chanCollapse[cursor.xCoarse]--;
} }
break; break;
case GUI_ACTION_PAT_INCREASE_COLUMNS: case GUI_ACTION_PAT_INCREASE_COLUMNS:
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
e->song.pat[cursor.xCoarse].effectCols++; e->curPat[cursor.xCoarse].effectCols++;
if (e->song.pat[cursor.xCoarse].effectCols>8) e->song.pat[cursor.xCoarse].effectCols=8; if (e->curPat[cursor.xCoarse].effectCols>8) e->curPat[cursor.xCoarse].effectCols=8;
break; break;
case GUI_ACTION_PAT_DECREASE_COLUMNS: case GUI_ACTION_PAT_DECREASE_COLUMNS:
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
e->song.pat[cursor.xCoarse].effectCols--; e->curPat[cursor.xCoarse].effectCols--;
if (e->song.pat[cursor.xCoarse].effectCols<1) e->song.pat[cursor.xCoarse].effectCols=1; if (e->curPat[cursor.xCoarse].effectCols<1) e->curPat[cursor.xCoarse].effectCols=1;
break; break;
case GUI_ACTION_PAT_INTERPOLATE: case GUI_ACTION_PAT_INTERPOLATE:
doInterpolate(); doInterpolate();
@ -525,15 +525,23 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_INS_LIST_ADD: case GUI_ACTION_INS_LIST_ADD:
curIns=e->addInstrument(cursor.xCoarse); curIns=e->addInstrument(cursor.xCoarse);
if (curIns==-1) {
showError("too many instruments!");
} else {
MARK_MODIFIED; MARK_MODIFIED;
}
break; break;
case GUI_ACTION_INS_LIST_DUPLICATE: case GUI_ACTION_INS_LIST_DUPLICATE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) { if (curIns>=0 && curIns<(int)e->song.ins.size()) {
int prevIns=curIns; int prevIns=curIns;
curIns=e->addInstrument(cursor.xCoarse); curIns=e->addInstrument(cursor.xCoarse);
if (curIns==-1) {
showError("too many instruments!");
} else {
(*e->song.ins[curIns])=(*e->song.ins[prevIns]); (*e->song.ins[curIns])=(*e->song.ins[prevIns]);
MARK_MODIFIED; MARK_MODIFIED;
} }
}
break; break;
case GUI_ACTION_INS_LIST_OPEN: case GUI_ACTION_INS_LIST_OPEN:
openFileDialog(GUI_FILE_INS_OPEN); openFileDialog(GUI_FILE_INS_OPEN);
@ -571,15 +579,23 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_WAVE_LIST_ADD: case GUI_ACTION_WAVE_LIST_ADD:
curWave=e->addWave(); curWave=e->addWave();
if (curWave==-1) {
showError("too many wavetables!");
} else {
MARK_MODIFIED; MARK_MODIFIED;
}
break; break;
case GUI_ACTION_WAVE_LIST_DUPLICATE: case GUI_ACTION_WAVE_LIST_DUPLICATE:
if (curWave>=0 && curWave<(int)e->song.wave.size()) { if (curWave>=0 && curWave<(int)e->song.wave.size()) {
int prevWave=curWave; int prevWave=curWave;
curWave=e->addWave(); curWave=e->addWave();
if (curWave==-1) {
showError("too many wavetables!");
} else {
(*e->song.wave[curWave])=(*e->song.wave[prevWave]); (*e->song.wave[curWave])=(*e->song.wave[prevWave]);
MARK_MODIFIED; MARK_MODIFIED;
} }
}
break; break;
case GUI_ACTION_WAVE_LIST_OPEN: case GUI_ACTION_WAVE_LIST_OPEN:
openFileDialog(GUI_FILE_WAVE_OPEN); openFileDialog(GUI_FILE_WAVE_OPEN);
@ -614,14 +630,20 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_LIST_ADD: case GUI_ACTION_SAMPLE_LIST_ADD:
curSample=e->addSample(); curSample=e->addSample();
updateSampleTex=true; if (curSample==-1) {
showError("too many samples!");
} else {
MARK_MODIFIED; MARK_MODIFIED;
}
updateSampleTex=true;
break; break;
case GUI_ACTION_SAMPLE_LIST_DUPLICATE: case GUI_ACTION_SAMPLE_LIST_DUPLICATE:
if (curSample>=0 && curSample<(int)e->song.sample.size()) { if (curSample>=0 && curSample<(int)e->song.sample.size()) {
DivSample* prevSample=e->getSample(curSample); DivSample* prevSample=e->getSample(curSample);
curSample=e->addSample(); curSample=e->addSample();
updateSampleTex=true; if (curSample==-1) {
showError("too many samples!");
} else {
e->lockEngine([this,prevSample]() { e->lockEngine([this,prevSample]() {
DivSample* sample=e->getSample(curSample); DivSample* sample=e->getSample(curSample);
if (sample!=NULL) { if (sample!=NULL) {
@ -642,6 +664,8 @@ void FurnaceGUI::doAction(int what) {
}); });
MARK_MODIFIED; MARK_MODIFIED;
} }
updateSampleTex=true;
}
break; break;
case GUI_ACTION_SAMPLE_LIST_OPEN: case GUI_ACTION_SAMPLE_LIST_OPEN:
openFileDialog(GUI_FILE_SAMPLE_OPEN); openFileDialog(GUI_FILE_SAMPLE_OPEN);
@ -1156,11 +1180,15 @@ void FurnaceGUI::doAction(int what) {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
curIns=e->addInstrument(cursor.xCoarse); curIns=e->addInstrument(cursor.xCoarse);
if (curIns==-1) {
showError("too many instruments!");
} else {
e->song.ins[curIns]->type=DIV_INS_AMIGA; e->song.ins[curIns]->type=DIV_INS_AMIGA;
e->song.ins[curIns]->name=sample->name; e->song.ins[curIns]->name=sample->name;
e->song.ins[curIns]->amiga.initSample=curSample; e->song.ins[curIns]->amiga.initSample=curSample;
nextWindow=GUI_WINDOW_INS_EDIT; nextWindow=GUI_WINDOW_INS_EDIT;
MARK_MODIFIED; MARK_MODIFIED;
}
break; break;
} }
@ -1170,7 +1198,7 @@ void FurnaceGUI::doAction(int what) {
} }
break; break;
case GUI_ACTION_ORDERS_DOWN: case GUI_ACTION_ORDERS_DOWN:
if (curOrder<e->song.ordersLen-1) { if (curOrder<e->curSubSong->ordersLen-1) {
setOrder(curOrder+1); setOrder(curOrder+1);
} }
break; break;
@ -1183,7 +1211,7 @@ void FurnaceGUI::doAction(int what) {
orderCursor=firstChannel; orderCursor=firstChannel;
break; break;
} }
} while (!e->song.chanShow[orderCursor]); } while (!e->curSubSong->chanShow[orderCursor]);
break; break;
} }
case GUI_ACTION_ORDERS_RIGHT: { case GUI_ACTION_ORDERS_RIGHT: {
@ -1195,20 +1223,20 @@ void FurnaceGUI::doAction(int what) {
orderCursor=lastChannel-1; orderCursor=lastChannel-1;
break; break;
} }
} while (!e->song.chanShow[orderCursor]); } while (!e->curSubSong->chanShow[orderCursor]);
break; break;
} }
case GUI_ACTION_ORDERS_INCREASE: { case GUI_ACTION_ORDERS_INCREASE: {
if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break;
if (e->song.orders.ord[orderCursor][curOrder]<0x7f) { if (e->curOrders->ord[orderCursor][curOrder]<0x7f) {
e->song.orders.ord[orderCursor][curOrder]++; e->curOrders->ord[orderCursor][curOrder]++;
} }
break; break;
} }
case GUI_ACTION_ORDERS_DECREASE: { case GUI_ACTION_ORDERS_DECREASE: {
if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break;
if (e->song.orders.ord[orderCursor][curOrder]>0) { if (e->curOrders->ord[orderCursor][curOrder]>0) {
e->song.orders.ord[orderCursor][curOrder]--; e->curOrders->ord[orderCursor][curOrder]--;
} }
break; break;
} }

View file

@ -45,7 +45,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::Text("Edit Step"); ImGui::Text("Edit Step");
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::InputInt("##EditStep",&editStep,1,1)) { if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
if (editStep>=e->song.patLen) editStep=e->song.patLen-1; if (editStep>=e->curSubSong->patLen) editStep=e->curSubSong->patLen-1;
if (editStep<0) editStep=0; if (editStep<0) editStep=0;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
@ -148,7 +148,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(96.0f*dpiScale); ImGui::SetNextItemWidth(96.0f*dpiScale);
if (ImGui::InputInt("##EditStep",&editStep,1,1)) { if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
if (editStep>=e->song.patLen) editStep=e->song.patLen-1; if (editStep>=e->curSubSong->patLen) editStep=e->curSubSong->patLen-1;
if (editStep<0) editStep=0; if (editStep<0) editStep=0;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
@ -217,7 +217,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::Text("Step"); ImGui::Text("Step");
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
if (ImGui::InputInt("##EditStep",&editStep,0,0)) { if (ImGui::InputInt("##EditStep",&editStep,0,0)) {
if (editStep>=e->song.patLen) editStep=e->song.patLen-1; if (editStep>=e->curSubSong->patLen) editStep=e->curSubSong->patLen-1;
if (editStep<0) editStep=0; if (editStep<0) editStep=0;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
@ -317,7 +317,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::SetCursorPosX(cursor); ImGui::SetCursorPosX(cursor);
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
if (ImGui::InputInt("##EditStep",&editStep,1,1)) { if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
if (editStep>=e->song.patLen) editStep=e->song.patLen-1; if (editStep>=e->curSubSong->patLen) editStep=e->curSubSong->patLen-1;
if (editStep<0) editStep=0; if (editStep<0) editStep=0;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {

View file

@ -44,8 +44,8 @@ const char* noteNameNormal(short note, short octave) {
void FurnaceGUI::prepareUndo(ActionType action) { void FurnaceGUI::prepareUndo(ActionType action) {
switch (action) { switch (action) {
case GUI_UNDO_CHANGE_ORDER: case GUI_UNDO_CHANGE_ORDER:
oldOrders=e->song.orders; memcpy(&oldOrders,e->curOrders,sizeof(DivOrders));
oldOrdersLen=e->song.ordersLen; oldOrdersLen=e->curSubSong->ordersLen;
break; break;
case GUI_UNDO_PATTERN_EDIT: case GUI_UNDO_PATTERN_EDIT:
case GUI_UNDO_PATTERN_DELETE: case GUI_UNDO_PATTERN_DELETE:
@ -63,7 +63,7 @@ void FurnaceGUI::prepareUndo(ActionType action) {
case GUI_UNDO_PATTERN_COLLAPSE: case GUI_UNDO_PATTERN_COLLAPSE:
case GUI_UNDO_PATTERN_EXPAND: case GUI_UNDO_PATTERN_EXPAND:
for (int i=0; i<e->getTotalChannelCount(); i++) { for (int i=0; i<e->getTotalChannelCount(); i++) {
e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],false)->copyOn(oldPat[i]); e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false)->copyOn(oldPat[i]);
} }
break; break;
} }
@ -78,18 +78,19 @@ void FurnaceGUI::makeUndo(ActionType action) {
s.selEnd=selEnd; s.selEnd=selEnd;
s.order=curOrder; s.order=curOrder;
s.nibble=curNibble; s.nibble=curNibble;
size_t subSong=e->getCurrentSubSong();
switch (action) { switch (action) {
case GUI_UNDO_CHANGE_ORDER: case GUI_UNDO_CHANGE_ORDER:
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=0; j<128; j++) { for (int j=0; j<128; j++) {
if (oldOrders.ord[i][j]!=e->song.orders.ord[i][j]) { if (oldOrders.ord[i][j]!=e->curOrders->ord[i][j]) {
s.ord.push_back(UndoOrderData(i,j,oldOrders.ord[i][j],e->song.orders.ord[i][j])); s.ord.push_back(UndoOrderData(subSong,i,j,oldOrders.ord[i][j],e->curOrders->ord[i][j]));
} }
} }
} }
s.oldOrdersLen=oldOrdersLen; s.oldOrdersLen=oldOrdersLen;
s.newOrdersLen=e->song.ordersLen; s.newOrdersLen=e->curSubSong->ordersLen;
if (oldOrdersLen!=e->song.ordersLen) { if (oldOrdersLen!=e->curSubSong->ordersLen) {
doPush=true; doPush=true;
} }
if (!s.ord.empty()) { if (!s.ord.empty()) {
@ -112,11 +113,11 @@ void FurnaceGUI::makeUndo(ActionType action) {
case GUI_UNDO_PATTERN_COLLAPSE: case GUI_UNDO_PATTERN_COLLAPSE:
case GUI_UNDO_PATTERN_EXPAND: case GUI_UNDO_PATTERN_EXPAND:
for (int i=0; i<e->getTotalChannelCount(); i++) { for (int i=0; i<e->getTotalChannelCount(); i++) {
DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],false); DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false);
for (int j=0; j<e->song.patLen; j++) { for (int j=0; j<e->curSubSong->patLen; j++) {
for (int k=0; k<32; k++) { for (int k=0; k<32; k++) {
if (p->data[j][k]!=oldPat[i]->data[j][k]) { if (p->data[j][k]!=oldPat[i]->data[j][k]) {
s.pat.push_back(UndoPatternData(i,e->song.orders.ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->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]));
} }
} }
} }
@ -137,15 +138,15 @@ void FurnaceGUI::makeUndo(ActionType action) {
void FurnaceGUI::doSelectAll() { void FurnaceGUI::doSelectAll() {
finishSelection(); finishSelection();
curNibble=false; curNibble=false;
if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectCols*2) { if (selStart.xFine==0 && selEnd.xFine==2+e->curPat[selEnd.xCoarse].effectCols*2) {
if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern if (selStart.y==0 && selEnd.y==e->curSubSong->patLen-1) { // select entire pattern
selStart.xCoarse=0; selStart.xCoarse=0;
selStart.xFine=0; selStart.xFine=0;
selEnd.xCoarse=e->getTotalChannelCount()-1; selEnd.xCoarse=e->getTotalChannelCount()-1;
selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2; selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2;
} else { // select entire column } else { // select entire column
selStart.y=0; selStart.y=0;
selEnd.y=e->song.patLen-1; selEnd.y=e->curSubSong->patLen-1;
} }
} else { } else {
int selStartX=0; int selStartX=0;
@ -153,26 +154,26 @@ void FurnaceGUI::doSelectAll() {
// find row position // find row position
for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) { for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) {
i.xFine++; i.xFine++;
if (i.xFine>=3+e->song.pat[i.xCoarse].effectCols*2) { if (i.xFine>=3+e->curPat[i.xCoarse].effectCols*2) {
i.xFine=0; i.xFine=0;
i.xCoarse++; i.xCoarse++;
} }
} }
for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) { for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) {
i.xFine++; i.xFine++;
if (i.xFine>=3+e->song.pat[i.xCoarse].effectCols*2) { if (i.xFine>=3+e->curPat[i.xCoarse].effectCols*2) {
i.xFine=0; i.xFine=0;
i.xCoarse++; i.xCoarse++;
} }
} }
float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1); float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1);
if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->song.patLen-1)) { // up-down if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->curSubSong->patLen-1)) { // up-down
selStart.y=0; selStart.y=0;
selEnd.y=e->song.patLen-1; selEnd.y=e->curSubSong->patLen-1;
} else { // left-right } else { // left-right
selStart.xFine=0; selStart.xFine=0;
selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2; selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2;
} }
} }
} }
@ -198,9 +199,9 @@ void FurnaceGUI::doDelete() {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskDelete,iFine); maskOut(opMaskDelete,iFine);
for (int j=selStart.y; j<=selEnd.y; j++) { for (int j=selStart.y; j<=selEnd.y; j++) {
if (iFine==0) { if (iFine==0) {
@ -235,12 +236,12 @@ void FurnaceGUI::doPullDelete() {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskPullDelete,iFine); maskOut(opMaskPullDelete,iFine);
for (int j=selStart.y; j<e->song.patLen; j++) { for (int j=selStart.y; j<e->curSubSong->patLen; j++) {
if (j<e->song.patLen-1) { if (j<e->curSubSong->patLen-1) {
if (iFine==0) { if (iFine==0) {
pat->data[j][iFine]=pat->data[j+1][iFine]; pat->data[j][iFine]=pat->data[j+1][iFine];
} }
@ -267,11 +268,11 @@ void FurnaceGUI::doInsert() {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskInsert,iFine); maskOut(opMaskInsert,iFine);
for (int j=e->song.patLen-1; j>=selStart.y; j--) { for (int j=e->curSubSong->patLen-1; j>=selStart.y; j--) {
if (j==selStart.y) { if (j==selStart.y) {
if (iFine==0) { if (iFine==0) {
pat->data[j][iFine]=0; pat->data[j][iFine]=0;
@ -299,9 +300,9 @@ void FurnaceGUI::doTranspose(int amount, OperationMask& mask) {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(mask,iFine); maskOut(mask,iFine);
for (int j=selStart.y; j<=selEnd.y; j++) { for (int j=selStart.y; j<=selEnd.y; j++) {
if (iFine==0) { if (iFine==0) {
@ -367,9 +368,9 @@ void FurnaceGUI::doCopy(bool cut) {
} }
clipboard+='\n'; clipboard+='\n';
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
if (iFine==0) { if (iFine==0) {
clipboard+=noteNameNormal(pat->data[j][0],pat->data[j][1]); clipboard+=noteNameNormal(pat->data[j][0],pat->data[j][1]);
if (cut) { if (cut) {
@ -432,7 +433,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
int j=cursor.y; int j=cursor.y;
char note[4]; char note[4];
for (size_t i=2; i<data.size() && j<e->song.patLen; i++) { for (size_t i=2; i<data.size() && j<e->curSubSong->patLen; i++) {
size_t charPos=0; size_t charPos=0;
int iCoarse=cursor.xCoarse; int iCoarse=cursor.xCoarse;
int iFine=(startOff>2 && cursor.xFine>2)?(((cursor.xFine-1)&(~1))|1):startOff; int iFine=(startOff>2 && cursor.xFine>2)?(((cursor.xFine-1)&(~1))|1):startOff;
@ -440,10 +441,10 @@ void FurnaceGUI::doPaste(PasteMode mode) {
String& line=data[i]; String& line=data[i];
while (charPos<line.size() && iCoarse<lastChannel) { while (charPos<line.size() && iCoarse<lastChannel) {
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
if (line[charPos]=='|') { if (line[charPos]=='|') {
iCoarse++; iCoarse++;
if (iCoarse<lastChannel) while (!e->song.chanShow[iCoarse]) { if (iCoarse<lastChannel) while (!e->curSubSong->chanShow[iCoarse]) {
iCoarse++; iCoarse++;
if (iCoarse>=lastChannel) break; if (iCoarse>=lastChannel) break;
} }
@ -530,7 +531,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
break; break;
} }
if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) { if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) {
if (iFine<(3+e->song.pat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val; if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val;
} }
} }
} }
@ -543,7 +544,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
break; break;
} }
j++; j++;
if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->song.patLen && curOrder<e->song.ordersLen-1) { if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->curSubSong->patLen && curOrder<e->curSubSong->ordersLen-1) {
j=0; j=0;
curOrder++; curOrder++;
} }
@ -554,7 +555,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
} }
if (settings.cursorPastePos) { if (settings.cursorPastePos) {
cursor.y=j; cursor.y=j;
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
updateScroll(cursor.y); updateScroll(cursor.y);
} }
@ -567,8 +568,8 @@ void FurnaceGUI::doChangeIns(int ins) {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (int j=selStart.y; j<=selEnd.y; j++) { for (int j=selStart.y; j<=selEnd.y; j++) {
if (pat->data[j][2]!=-1 || !(pat->data[j][0]==0 && pat->data[j][1]==0)) { if (pat->data[j][2]!=-1 || !(pat->data[j][0]==0 && pat->data[j][1]==0)) {
pat->data[j][2]=ins; pat->data[j][2]=ins;
@ -587,9 +588,9 @@ void FurnaceGUI::doInterpolate() {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskInterpolate,iFine); maskOut(opMaskInterpolate,iFine);
points.clear(); points.clear();
if (iFine!=0) { if (iFine!=0) {
@ -646,9 +647,9 @@ void FurnaceGUI::doFade(int p0, int p1, bool mode) {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskFade,iFine); maskOut(opMaskFade,iFine);
if (iFine!=0) { if (iFine!=0) {
int absoluteTop=255; int absoluteTop=255;
@ -684,9 +685,9 @@ void FurnaceGUI::doInvertValues() {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskInvertVal,iFine); maskOut(opMaskInvertVal,iFine);
if (iFine!=0) { if (iFine!=0) {
int top=255; int top=255;
@ -715,9 +716,9 @@ void FurnaceGUI::doScale(float top) {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskScale,iFine); maskOut(opMaskScale,iFine);
if (iFine!=0) { if (iFine!=0) {
int absoluteTop=255; int absoluteTop=255;
@ -746,9 +747,9 @@ void FurnaceGUI::doRandomize(int bottom, int top, bool mode) {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskRandomize,iFine); maskOut(opMaskRandomize,iFine);
if (iFine!=0) { if (iFine!=0) {
int absoluteTop=255; int absoluteTop=255;
@ -792,9 +793,9 @@ void FurnaceGUI::doFlip() {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskFlip,iFine); maskOut(opMaskFlip,iFine);
for (int j=selStart.y; j<=selEnd.y; j++) { for (int j=selStart.y; j<=selEnd.y; j++) {
if (iFine==0) { if (iFine==0) {
@ -823,9 +824,9 @@ void FurnaceGUI::doCollapse(int divider) {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskCollapseExpand,iFine); maskOut(opMaskCollapseExpand,iFine);
for (int j=selStart.y; j<=selEnd.y; j++) { for (int j=selStart.y; j<=selEnd.y; j++) {
if (iFine==0) { if (iFine==0) {
@ -880,9 +881,9 @@ void FurnaceGUI::doExpand(int multiplier) {
int iCoarse=selStart.xCoarse; int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine; int iFine=selStart.xFine;
for (; iCoarse<=selEnd.xCoarse; iCoarse++) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
if (!e->song.chanShow[iCoarse]) continue; if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) { for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
maskOut(opMaskCollapseExpand,iFine); maskOut(opMaskCollapseExpand,iFine);
for (int j=selStart.y; j<=selEnd.y; j++) { for (int j=selStart.y; j<=selEnd.y; j++) {
if (iFine==0) { if (iFine==0) {
@ -891,7 +892,7 @@ void FurnaceGUI::doExpand(int multiplier) {
patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; patBuffer.data[j][iFine+1]=pat->data[j][iFine+1];
} }
for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) { for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) {
if ((j+selStart.y)>=e->song.patLen) break; if ((j+selStart.y)>=e->curSubSong->patLen) break;
if ((j%multiplier)!=0) { if ((j%multiplier)!=0) {
if (iFine==0) { if (iFine==0) {
pat->data[j+selStart.y][0]=0; pat->data[j+selStart.y][0]=0;
@ -921,9 +922,10 @@ void FurnaceGUI::doUndo() {
switch (us.type) { switch (us.type) {
case GUI_UNDO_CHANGE_ORDER: case GUI_UNDO_CHANGE_ORDER:
e->song.ordersLen=us.oldOrdersLen; e->curSubSong->ordersLen=us.oldOrdersLen;
for (UndoOrderData& i: us.ord) { for (UndoOrderData& i: us.ord) {
e->song.orders.ord[i.chan][i.ord]=i.oldVal; e->changeSongP(i.subSong);
e->curOrders->ord[i.chan][i.ord]=i.oldVal;
} }
break; break;
case GUI_UNDO_PATTERN_EDIT: case GUI_UNDO_PATTERN_EDIT:
@ -942,7 +944,8 @@ void FurnaceGUI::doUndo() {
case GUI_UNDO_PATTERN_COLLAPSE: case GUI_UNDO_PATTERN_COLLAPSE:
case GUI_UNDO_PATTERN_EXPAND: case GUI_UNDO_PATTERN_EXPAND:
for (UndoPatternData& i: us.pat) { for (UndoPatternData& i: us.pat) {
DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); e->changeSongP(i.subSong);
DivPattern* p=e->curPat[i.chan].getPattern(i.pat,true);
p->data[i.row][i.col]=i.oldVal; p->data[i.row][i.col]=i.oldVal;
} }
if (!e->isPlaying() || !followPattern) { if (!e->isPlaying() || !followPattern) {
@ -967,9 +970,10 @@ void FurnaceGUI::doRedo() {
switch (us.type) { switch (us.type) {
case GUI_UNDO_CHANGE_ORDER: case GUI_UNDO_CHANGE_ORDER:
e->song.ordersLen=us.newOrdersLen; e->curSubSong->ordersLen=us.newOrdersLen;
for (UndoOrderData& i: us.ord) { for (UndoOrderData& i: us.ord) {
e->song.orders.ord[i.chan][i.ord]=i.newVal; e->changeSongP(i.subSong);
e->curOrders->ord[i.chan][i.ord]=i.newVal;
} }
break; break;
case GUI_UNDO_PATTERN_EDIT: case GUI_UNDO_PATTERN_EDIT:
@ -988,7 +992,8 @@ void FurnaceGUI::doRedo() {
case GUI_UNDO_PATTERN_COLLAPSE: case GUI_UNDO_PATTERN_COLLAPSE:
case GUI_UNDO_PATTERN_EXPAND: case GUI_UNDO_PATTERN_EXPAND:
for (UndoPatternData& i: us.pat) { for (UndoPatternData& i: us.pat) {
DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); e->changeSongP(i.subSong);
DivPattern* p=e->curPat[i.chan].getPattern(i.pat,true);
p->data[i.row][i.col]=i.newVal; p->data[i.row][i.col]=i.newVal;
} }
if (!e->isPlaying()) { if (!e->isPlaying()) {

View file

@ -59,7 +59,7 @@ extern "C" {
#define BACKUP_FUR "/backup.fur" #define BACKUP_FUR "/backup.fur"
#endif #endif
#ifdef ANDROID #ifdef IS_MOBILE
#define MOBILE_UI_DEFAULT true #define MOBILE_UI_DEFAULT true
#else #else
#define MOBILE_UI_DEFAULT false #define MOBILE_UI_DEFAULT false
@ -194,21 +194,6 @@ void FurnaceGUI::decodeKeyMap(std::map<int,int>& map, String source) {
} }
} }
void FurnaceGUI::encodeMMLStr(String& target, unsigned char* macro, unsigned char macroLen, signed char macroLoop, signed char macroRel) {
target="";
char buf[32];
for (int i=0; i<macroLen; i++) {
if (i==macroLoop) target+="| ";
if (i==macroRel) target+="/ ";
if (i==macroLen-1) {
snprintf(buf,31,"%d",macro[i]);
} else {
snprintf(buf,31,"%d ",macro[i]);
}
target+=buf;
}
}
void FurnaceGUI::encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex) { void FurnaceGUI::encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex) {
target=""; target="";
char buf[32]; char buf[32];
@ -290,53 +275,6 @@ void FurnaceGUI::decodeMMLStrW(String& source, int* macro, int& macroLen, int ma
} }
} }
void FurnaceGUI::decodeMMLStr(String& source, unsigned char* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel) {
int buf=0;
bool hasVal=false;
macroLen=0;
macroLoop=-1;
macroRel=-1;
for (char& i: source) {
switch (i) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
hasVal=true;
buf*=10;
buf+=i-'0';
break;
case ' ':
if (hasVal) {
hasVal=false;
macro[macroLen]=buf;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
macroLen++;
buf=0;
}
break;
case '|':
if (macroLoop==-1) {
macroLoop=macroLen;
}
break;
case '/':
if (macroRel==-1) {
macroRel=macroLen;
}
break;
}
if (macroLen>=128) break;
}
if (hasVal && macroLen<128) {
hasVal=false;
macro[macroLen]=buf;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
macroLen++;
buf=0;
}
}
void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel) { void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel) {
int buf=0; int buf=0;
bool negaBuf=false; bool negaBuf=false;
@ -370,11 +308,29 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
} }
break; break;
case '|': case '|':
if (hasVal) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
macroLen++;
buf=0;
}
if (macroLoop==-1) { if (macroLoop==-1) {
macroLoop=macroLen; macroLoop=macroLen;
} }
break; break;
case '/': case '/':
if (hasVal) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
macroLen++;
buf=0;
}
if (macroRel==-1) { if (macroRel==-1) {
macroRel=macroLen; macroRel=macroLen;
} }
@ -837,9 +793,9 @@ void FurnaceGUI::prepareLayout() {
} }
float FurnaceGUI::calcBPM(int s1, int s2, float hz) { float FurnaceGUI::calcBPM(int s1, int s2, float hz) {
float hl=e->song.hilightA; float hl=e->curSubSong->hilightA;
if (hl<=0.0f) hl=4.0f; if (hl<=0.0f) hl=4.0f;
float timeBase=e->song.timeBase+1; float timeBase=e->curSubSong->timeBase+1;
float speedSum=s1+s2; float speedSum=s1+s2;
if (timeBase<1.0f) timeBase=1.0f; if (timeBase<1.0f) timeBase=1.0f;
if (speedSum<1.0f) speedSum=1.0f; if (speedSum<1.0f) speedSum=1.0f;
@ -901,7 +857,7 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) {
} }
void FurnaceGUI::noteInput(int num, int key, int vol) { void FurnaceGUI::noteInput(int num, int key, int vol) {
DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true); DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
prepareUndo(GUI_UNDO_PATTERN_EDIT); prepareUndo(GUI_UNDO_PATTERN_EDIT);
@ -945,7 +901,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
} }
void FurnaceGUI::valueInput(int num, bool direct, int target) { void FurnaceGUI::valueInput(int num, bool direct, int target) {
DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true); DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
prepareUndo(GUI_UNDO_PATTERN_EDIT); prepareUndo(GUI_UNDO_PATTERN_EDIT);
if (target==-1) target=cursor.xFine+1; if (target==-1) target=cursor.xFine+1;
if (direct) { if (direct) {
@ -1009,7 +965,7 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) {
editAdvance(); editAdvance();
} else { } else {
if (settings.effectCursorDir==2) { if (settings.effectCursorDir==2) {
if (++cursor.xFine>=(3+(e->song.pat[cursor.xCoarse].effectCols*2))) { if (++cursor.xFine>=(3+(e->curPat[cursor.xCoarse].effectCols*2))) {
cursor.xFine=3; cursor.xFine=3;
} }
} else { } else {
@ -1167,7 +1123,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
int num=valueKeys.at(ev.key.keysym.sym); int num=valueKeys.at(ev.key.keysym.sym);
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) { if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
e->lockSave([this,num]() { e->lockSave([this,num]() {
e->song.orders.ord[orderCursor][curOrder]=((e->song.orders.ord[orderCursor][curOrder]<<4)|num); e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
}); });
if (orderEditMode==2 || orderEditMode==3) { if (orderEditMode==2 || orderEditMode==3) {
curNibble=!curNibble; curNibble=!curNibble;
@ -1176,7 +1132,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
orderCursor++; orderCursor++;
if (orderCursor>=e->getTotalChannelCount()) orderCursor=0; if (orderCursor>=e->getTotalChannelCount()) orderCursor=0;
} else if (orderEditMode==3) { } else if (orderEditMode==3) {
if (curOrder<e->song.ordersLen-1) { if (curOrder<e->curSubSong->ordersLen-1) {
setOrder(curOrder+1); setOrder(curOrder+1);
} }
} }
@ -1507,6 +1463,19 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
dpiScale dpiScale
); );
break; break;
case GUI_FILE_YRW801_ROM_OPEN:
case GUI_FILE_TG100_ROM_OPEN:
case GUI_FILE_MU5_ROM_OPEN:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load ROM",
{"compatible files", "*.rom *.bin",
"all files", ".*"},
"compatible files{.rom,.bin},.*",
workingDirROM,
dpiScale
);
break;
} }
if (hasOpened) curFileDialog=type; if (hasOpened) curFileDialog=type;
//ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard; //ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard;
@ -2062,7 +2031,7 @@ void FurnaceGUI::editOptions(bool topMenu) {
ImGui::PopFont(); ImGui::PopFont();
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Set")) { if (ImGui::Button("Set")) {
DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true); DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
latchIns=pat->data[cursor.y][2]; latchIns=pat->data[cursor.y][2];
latchVol=pat->data[cursor.y][3]; latchVol=pat->data[cursor.y][3];
latchEffect=pat->data[cursor.y][4]; latchEffect=pat->data[cursor.y][4];
@ -2381,7 +2350,7 @@ bool FurnaceGUI::loop() {
} }
sampleDragActive=false; sampleDragActive=false;
if (selecting) { if (selecting) {
cursor=selEnd; if (!selectingFull) cursor=selEnd;
finishSelection(); finishSelection();
demandScrollX=true; demandScrollX=true;
if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y &&
@ -2453,6 +2422,15 @@ bool FurnaceGUI::loop() {
wantCaptureKeyboard=ImGui::GetIO().WantTextInput; wantCaptureKeyboard=ImGui::GetIO().WantTextInput;
if (wantCaptureKeyboard!=oldWantCaptureKeyboard) {
oldWantCaptureKeyboard=wantCaptureKeyboard;
if (wantCaptureKeyboard) {
SDL_StartTextInput();
} else {
SDL_StopTextInput();
}
}
if (wantCaptureKeyboard) { if (wantCaptureKeyboard) {
WAKE_UP; WAKE_UP;
} }
@ -2775,10 +2753,12 @@ bool FurnaceGUI::loop() {
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("settings")) { if (ImGui::BeginMenu("settings")) {
#ifndef IS_MOBILE
if (ImGui::MenuItem("full screen",BIND_FOR(GUI_ACTION_FULLSCREEN),fullScreen)) { if (ImGui::MenuItem("full screen",BIND_FOR(GUI_ACTION_FULLSCREEN),fullScreen)) {
doAction(GUI_ACTION_FULLSCREEN); doAction(GUI_ACTION_FULLSCREEN);
} }
if (ImGui::MenuItem("lock layout",NULL,lockLayout)) { #endif
if (ImGui::MenuItem("lock layout (not working!)",NULL,lockLayout)) {
lockLayout=!lockLayout; lockLayout=!lockLayout;
} }
if (ImGui::MenuItem("visualizer",NULL,fancyPattern)) { if (ImGui::MenuItem("visualizer",NULL,fancyPattern)) {
@ -2798,6 +2778,7 @@ bool FurnaceGUI::loop() {
} }
if (ImGui::BeginMenu("window")) { if (ImGui::BeginMenu("window")) {
if (ImGui::MenuItem("song information",BIND_FOR(GUI_ACTION_WINDOW_SONG_INFO),songInfoOpen)) songInfoOpen=!songInfoOpen; if (ImGui::MenuItem("song information",BIND_FOR(GUI_ACTION_WINDOW_SONG_INFO),songInfoOpen)) songInfoOpen=!songInfoOpen;
if (ImGui::MenuItem("subsongs",BIND_FOR(GUI_ACTION_WINDOW_SUBSONGS),subSongsOpen)) subSongsOpen=!subSongsOpen;
if (ImGui::MenuItem("instruments",BIND_FOR(GUI_ACTION_WINDOW_INS_LIST),insListOpen)) insListOpen=!insListOpen; if (ImGui::MenuItem("instruments",BIND_FOR(GUI_ACTION_WINDOW_INS_LIST),insListOpen)) insListOpen=!insListOpen;
if (ImGui::MenuItem("wavetables",BIND_FOR(GUI_ACTION_WINDOW_WAVE_LIST),waveListOpen)) waveListOpen=!waveListOpen; if (ImGui::MenuItem("wavetables",BIND_FOR(GUI_ACTION_WINDOW_WAVE_LIST),waveListOpen)) waveListOpen=!waveListOpen;
if (ImGui::MenuItem("samples",BIND_FOR(GUI_ACTION_WINDOW_SAMPLE_LIST),sampleListOpen)) sampleListOpen=!sampleListOpen; if (ImGui::MenuItem("samples",BIND_FOR(GUI_ACTION_WINDOW_SAMPLE_LIST),sampleListOpen)) sampleListOpen=!sampleListOpen;
@ -2837,12 +2818,12 @@ bool FurnaceGUI::loop() {
if (e->isPlaying()) { if (e->isPlaying()) {
int totalTicks=e->getTotalTicks(); int totalTicks=e->getTotalTicks();
int totalSeconds=e->getTotalSeconds(); int totalSeconds=e->getTotalSeconds();
ImGui::Text("| Speed %d:%d @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getSpeed1(),e->getSpeed2(),e->getCurHz(),calcBPM(e->getSpeed1(),e->getSpeed2(),e->getCurHz()),e->getOrder(),e->song.ordersLen,e->getRow(),e->song.patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000); ImGui::Text("| Speed %d:%d @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getSpeed1(),e->getSpeed2(),e->getCurHz(),calcBPM(e->getSpeed1(),e->getSpeed2(),e->getCurHz()),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
} else { } else {
bool hasInfo=false; bool hasInfo=false;
String info; String info;
if (cursor.xCoarse>=0 && cursor.xCoarse<e->getTotalChannelCount()) { if (cursor.xCoarse>=0 && cursor.xCoarse<e->getTotalChannelCount()) {
DivPattern* p=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],false); DivPattern* p=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],false);
if (cursor.xFine>=0) switch (cursor.xFine) { if (cursor.xFine>=0) switch (cursor.xFine) {
case 0: // note case 0: // note
if (p->data[cursor.y][0]>0) { if (p->data[cursor.y][0]>0) {
@ -2903,6 +2884,7 @@ bool FurnaceGUI::loop() {
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0); ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
drawSubSongs();
drawPattern(); drawPattern();
drawEditControls(); drawEditControls();
drawSongInfo(); drawSongInfo();
@ -3013,6 +2995,11 @@ bool FurnaceGUI::loop() {
case GUI_FILE_EXPORT_LAYOUT: case GUI_FILE_EXPORT_LAYOUT:
workingDirLayout=fileDialog->getPath()+DIR_SEPARATOR_STR; workingDirLayout=fileDialog->getPath()+DIR_SEPARATOR_STR;
break; break;
case GUI_FILE_YRW801_ROM_OPEN:
case GUI_FILE_TG100_ROM_OPEN:
case GUI_FILE_MU5_ROM_OPEN:
workingDirROM=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
} }
if (fileDialog->accepted()) { if (fileDialog->accepted()) {
fileName=fileDialog->getFileName(); fileName=fileDialog->getFileName();
@ -3176,8 +3163,11 @@ bool FurnaceGUI::loop() {
break; break;
} }
case GUI_FILE_WAVE_OPEN: case GUI_FILE_WAVE_OPEN:
e->addWaveFromFile(copyOfName.c_str()); if (!e->addWaveFromFile(copyOfName.c_str())) {
showError("cannot load wavetable! ("+e->getLastError()+")");
} else {
MARK_MODIFIED; MARK_MODIFIED;
}
break; break;
case GUI_FILE_EXPORT_VGM: { case GUI_FILE_EXPORT_VGM: {
SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion); SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion);
@ -3226,6 +3216,15 @@ bool FurnaceGUI::loop() {
case GUI_FILE_EXPORT_LAYOUT: case GUI_FILE_EXPORT_LAYOUT:
exportLayout(copyOfName); exportLayout(copyOfName);
break; break;
case GUI_FILE_YRW801_ROM_OPEN:
settings.yrw801Path=copyOfName;
break;
case GUI_FILE_TG100_ROM_OPEN:
settings.tg100Path=copyOfName;
break;
case GUI_FILE_MU5_ROM_OPEN:
settings.mu5Path=copyOfName;
break;
} }
curFileDialog=GUI_FILE_OPEN; curFileDialog=GUI_FILE_OPEN;
} }
@ -3278,7 +3277,7 @@ bool FurnaceGUI::loop() {
} }
ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
if (ImGui::BeginPopupModal("New Song",NULL,ImGuiWindowFlags_NoMove)) { if (ImGui::BeginPopupModal("New Song",NULL,ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
ImGui::SetWindowPos(ImVec2(((scrW*dpiScale)-ImGui::GetWindowSize().x)*0.5,((scrH*dpiScale)-ImGui::GetWindowSize().y)*0.5)); ImGui::SetWindowPos(ImVec2(((scrW*dpiScale)-ImGui::GetWindowSize().x)*0.5,((scrH*dpiScale)-ImGui::GetWindowSize().y)*0.5));
drawNewSong(); drawNewSong();
ImGui::EndPopup(); ImGui::EndPopup();
@ -3494,7 +3493,7 @@ bool FurnaceGUI::loop() {
stop(); stop();
e->lockEngine([this]() { e->lockEngine([this]() {
for (int i=0; i<e->getTotalChannelCount(); i++) { for (int i=0; i<e->getTotalChannelCount(); i++) {
DivPattern* pat=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],true); DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],true);
memset(pat->data,-1,256*32*sizeof(short)); memset(pat->data,-1,256*32*sizeof(short));
for (int j=0; j<256; j++) { for (int j=0; j<256; j++) {
pat->data[j][0]=0; pat->data[j][0]=0;
@ -3653,12 +3652,23 @@ bool FurnaceGUI::init() {
regViewOpen=e->getConfBool("regViewOpen",false); regViewOpen=e->getConfBool("regViewOpen",false);
logOpen=e->getConfBool("logOpen",false); logOpen=e->getConfBool("logOpen",false);
effectListOpen=e->getConfBool("effectListOpen",false); effectListOpen=e->getConfBool("effectListOpen",false);
subSongsOpen=e->getConfBool("subSongsOpen",true);
tempoView=e->getConfBool("tempoView",true); tempoView=e->getConfBool("tempoView",true);
waveHex=e->getConfBool("waveHex",false); waveHex=e->getConfBool("waveHex",false);
lockLayout=e->getConfBool("lockLayout",false); lockLayout=e->getConfBool("lockLayout",false);
#ifdef IS_MOBILE
fullScreen=true;
#else
fullScreen=e->getConfBool("fullScreen",false); fullScreen=e->getConfBool("fullScreen",false);
#endif
mobileUI=e->getConfBool("mobileUI",MOBILE_UI_DEFAULT); mobileUI=e->getConfBool("mobileUI",MOBILE_UI_DEFAULT);
edit=e->getConfBool("edit",false);
followOrders=e->getConfBool("followOrders",true);
followPattern=e->getConfBool("followPattern",true);
orderEditMode=e->getConfInt("orderEditMode",0);
if (orderEditMode<0) orderEditMode=0;
if (orderEditMode>3) orderEditMode=3;
syncSettings(); syncSettings();
@ -3666,6 +3676,8 @@ bool FurnaceGUI::init() {
dpiScale=settings.dpiScale; dpiScale=settings.dpiScale;
} }
initSystemPresets();
#if !(defined(__APPLE__) || defined(_WIN32)) #if !(defined(__APPLE__) || defined(_WIN32))
unsigned char* furIcon=getFurnaceIcon(); unsigned char* furIcon=getFurnaceIcon();
SDL_Surface* icon=SDL_CreateRGBSurfaceFrom(furIcon,256,256,32,256*4,0xff,0xff00,0xff0000,0xff000000); SDL_Surface* icon=SDL_CreateRGBSurfaceFrom(furIcon,256,256,32,256*4,0xff,0xff00,0xff0000,0xff000000);
@ -3679,6 +3691,7 @@ bool FurnaceGUI::init() {
#endif #endif
SDL_SetHint("SDL_HINT_VIDEO_ALLOW_SCREENSAVER","1"); SDL_SetHint("SDL_HINT_VIDEO_ALLOW_SCREENSAVER","1");
SDL_SetHint("SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH","1");
SDL_Init(SDL_INIT_VIDEO); SDL_Init(SDL_INIT_VIDEO);
@ -3831,6 +3844,7 @@ bool FurnaceGUI::finish() {
e->setConf("regViewOpen",regViewOpen); e->setConf("regViewOpen",regViewOpen);
e->setConf("logOpen",logOpen); e->setConf("logOpen",logOpen);
e->setConf("effectListOpen",effectListOpen); e->setConf("effectListOpen",effectListOpen);
e->setConf("subSongsOpen",subSongsOpen);
// commit last window size // commit last window size
e->setConf("lastWindowWidth",scrW); e->setConf("lastWindowWidth",scrW);
@ -3841,6 +3855,10 @@ bool FurnaceGUI::finish() {
e->setConf("lockLayout",lockLayout); e->setConf("lockLayout",lockLayout);
e->setConf("fullScreen",fullScreen); e->setConf("fullScreen",fullScreen);
e->setConf("mobileUI",mobileUI); e->setConf("mobileUI",mobileUI);
e->setConf("edit",edit);
e->setConf("followOrders",followOrders);
e->setConf("followPattern",followPattern);
e->setConf("orderEditMode",orderEditMode);
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
delete oldPat[i]; delete oldPat[i];
@ -3870,6 +3888,7 @@ FurnaceGUI::FurnaceGUI():
displayExporting(false), displayExporting(false),
vgmExportLoop(true), vgmExportLoop(true),
wantCaptureKeyboard(false), wantCaptureKeyboard(false),
oldWantCaptureKeyboard(false),
displayMacroMenu(false), displayMacroMenu(false),
displayNew(false), displayNew(false),
fullScreen(false), fullScreen(false),
@ -3946,6 +3965,7 @@ FurnaceGUI::FurnaceGUI():
logOpen(false), logOpen(false),
effectListOpen(false), effectListOpen(false),
chanOscOpen(false), chanOscOpen(false),
subSongsOpen(true),
/* /*
editControlsDocked(false), editControlsDocked(false),
ordersDocked(false), ordersDocked(false),
@ -3973,8 +3993,10 @@ FurnaceGUI::FurnaceGUI():
logDocked(false), logDocked(false),
effectListDocked(false), effectListDocked(false),
chanOscDocked(false), chanOscDocked(false),
subSongsDocked(false),
*/ */
selecting(false), selecting(false),
selectingFull(false),
curNibble(false), curNibble(false),
orderNibble(false), orderNibble(false),
followOrders(true), followOrders(true),
@ -4134,8 +4156,6 @@ FurnaceGUI::FurnaceGUI():
valueKeys[SDLK_KP_8]=8; valueKeys[SDLK_KP_8]=8;
valueKeys[SDLK_KP_9]=9; valueKeys[SDLK_KP_9]=9;
initSystemPresets();
memset(willExport,1,32*sizeof(bool)); memset(willExport,1,32*sizeof(bool));
peak[0]=0; peak[0]=0;

View file

@ -231,7 +231,8 @@ enum FurnaceGUIWindows {
GUI_WINDOW_REGISTER_VIEW, GUI_WINDOW_REGISTER_VIEW,
GUI_WINDOW_LOG, GUI_WINDOW_LOG,
GUI_WINDOW_EFFECT_LIST, GUI_WINDOW_EFFECT_LIST,
GUI_WINDOW_CHAN_OSC GUI_WINDOW_CHAN_OSC,
GUI_WINDOW_SUBSONGS
}; };
enum FurnaceGUIFileDialogs { enum FurnaceGUIFileDialogs {
@ -257,7 +258,10 @@ enum FurnaceGUIFileDialogs {
GUI_FILE_IMPORT_LAYOUT, GUI_FILE_IMPORT_LAYOUT,
GUI_FILE_EXPORT_COLORS, GUI_FILE_EXPORT_COLORS,
GUI_FILE_EXPORT_KEYBINDS, GUI_FILE_EXPORT_KEYBINDS,
GUI_FILE_EXPORT_LAYOUT GUI_FILE_EXPORT_LAYOUT,
GUI_FILE_YRW801_ROM_OPEN,
GUI_FILE_TG100_ROM_OPEN,
GUI_FILE_MU5_ROM_OPEN
}; };
enum FurnaceGUIWarnings { enum FurnaceGUIWarnings {
@ -334,6 +338,7 @@ enum FurnaceGUIActions {
GUI_ACTION_WINDOW_LOG, GUI_ACTION_WINDOW_LOG,
GUI_ACTION_WINDOW_EFFECT_LIST, GUI_ACTION_WINDOW_EFFECT_LIST,
GUI_ACTION_WINDOW_CHAN_OSC, GUI_ACTION_WINDOW_CHAN_OSC,
GUI_ACTION_WINDOW_SUBSONGS,
GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_COLLAPSE_WINDOW,
GUI_ACTION_CLOSE_WINDOW, GUI_ACTION_CLOSE_WINDOW,
@ -547,9 +552,10 @@ enum ActionType {
}; };
struct UndoPatternData { struct UndoPatternData {
int chan, pat, row, col; int subSong, chan, pat, row, col;
short oldVal, newVal; short oldVal, newVal;
UndoPatternData(int c, int p, int r, int co, short v1, short v2): UndoPatternData(int s, int c, int p, int r, int co, short v1, short v2):
subSong(s),
chan(c), chan(c),
pat(p), pat(p),
row(r), row(r),
@ -559,9 +565,10 @@ struct UndoPatternData {
}; };
struct UndoOrderData { struct UndoOrderData {
int chan, ord; int subSong, chan, ord;
unsigned char oldVal, newVal; unsigned char oldVal, newVal;
UndoOrderData(int c, int o, unsigned char v1, unsigned char v2): UndoOrderData(int s, int c, int o, unsigned char v1, unsigned char v2):
subSong(s),
chan(c), chan(c),
ord(o), ord(o),
oldVal(v1), oldVal(v1),
@ -718,11 +725,14 @@ struct FurnaceGUISysDef {
struct FurnaceGUISysCategory { struct FurnaceGUISysCategory {
const char* name; const char* name;
const char* description;
std::vector<FurnaceGUISysDef> systems; std::vector<FurnaceGUISysDef> systems;
FurnaceGUISysCategory(const char* n): FurnaceGUISysCategory(const char* n, const char* d):
name(n) {} name(n),
description(d) {}
FurnaceGUISysCategory(): FurnaceGUISysCategory():
name(NULL) {} name(NULL),
description(NULL) {}
}; };
static const char* modeDummy[1]={NULL}; static const char* modeDummy[1]={NULL};
@ -767,11 +777,11 @@ class FurnaceGUI {
bool updateSampleTex; bool updateSampleTex;
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile; String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile;
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout; String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout, workingDirROM;
String mmlString[17]; String mmlString[17];
String mmlStringW; String mmlStringW;
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, wantCaptureKeyboard, displayMacroMenu; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool displayNew, fullScreen, preserveChanPos; bool displayNew, fullScreen, preserveChanPos;
bool willExport[32]; bool willExport[32];
int vgmExportVersion; int vgmExportVersion;
@ -825,6 +835,9 @@ class FurnaceGUI {
int saaCore; int saaCore;
int nesCore; int nesCore;
int fdsCore; int fdsCore;
String yrw801Path;
String tg100Path;
String mu5Path;
int mainFont; int mainFont;
int patFont; int patFont;
int audioRate; int audioRate;
@ -863,6 +876,7 @@ class FurnaceGUI {
int roundedButtons; int roundedButtons;
int roundedMenus; int roundedMenus;
int loadJapanese; int loadJapanese;
int loadChinese;
int fmLayout; int fmLayout;
int sampleLayout; int sampleLayout;
int waveLayout; int waveLayout;
@ -892,6 +906,7 @@ class FurnaceGUI {
int hiddenSystems; int hiddenSystems;
int horizontalDataView; int horizontalDataView;
int noMultiSystem; int noMultiSystem;
int oldMacroVSlider;
unsigned int maxUndoSteps; unsigned int maxUndoSteps;
String mainFontPath; String mainFontPath;
String patFontPath; String patFontPath;
@ -911,6 +926,9 @@ class FurnaceGUI {
saaCore(1), saaCore(1),
nesCore(0), nesCore(0),
fdsCore(0), fdsCore(0),
yrw801Path(""),
tg100Path(""),
mu5Path(""),
mainFont(0), mainFont(0),
patFont(0), patFont(0),
audioRate(44100), audioRate(44100),
@ -947,6 +965,7 @@ class FurnaceGUI {
roundedButtons(1), roundedButtons(1),
roundedMenus(0), roundedMenus(0),
loadJapanese(0), loadJapanese(0),
loadChinese(0),
fmLayout(0), fmLayout(0),
sampleLayout(0), sampleLayout(0),
waveLayout(0), waveLayout(0),
@ -976,6 +995,7 @@ class FurnaceGUI {
hiddenSystems(0), hiddenSystems(0),
horizontalDataView(0), horizontalDataView(0),
noMultiSystem(0), noMultiSystem(0),
oldMacroVSlider(0),
maxUndoSteps(100), maxUndoSteps(100),
mainFontPath(""), mainFontPath(""),
patFontPath(""), patFontPath(""),
@ -996,16 +1016,18 @@ class FurnaceGUI {
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
bool subSongsOpen;
/* there ought to be a better way... /* there ought to be a better way...
bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked; bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked;
bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked; bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked;
bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked; bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked;
bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked; bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked;
bool subSongsDocked;
*/ */
SelectionPoint selStart, selEnd, cursor; SelectionPoint selStart, selEnd, cursor;
bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; bool selecting, selectingFull, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
FurnaceGUIWindows curWindow, nextWindow, curWindowLast; FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
float peak[2]; float peak[2];
@ -1110,7 +1132,7 @@ class FurnaceGUI {
ImVec2 patWindowPos, patWindowSize; ImVec2 patWindowPos, patWindowSize;
// pattern view specific // pattern view specific
ImVec2 threeChars, twoChars; ImVec2 fourChars, threeChars, twoChars;
SelectionPoint sel1, sel2; SelectionPoint sel1, sel2;
int dummyRows, demandX; int dummyRows, demandX;
int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax; int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax;
@ -1232,6 +1254,7 @@ class FurnaceGUI {
void drawNewSong(); void drawNewSong();
void drawLog(); void drawLog();
void drawEffectList(); void drawEffectList();
void drawSubSongs();
void parseKeybinds(); void parseKeybinds();
void promptKey(int which); void promptKey(int which);
@ -1251,8 +1274,8 @@ class FurnaceGUI {
void commitSettings(); void commitSettings();
void processDrags(int dragX, int dragY); void processDrags(int dragX, int dragY);
void startSelection(int xCoarse, int xFine, int y); void startSelection(int xCoarse, int xFine, int y, bool fullRow=false);
void updateSelection(int xCoarse, int xFine, int y); void updateSelection(int xCoarse, int xFine, int y, bool fullRow=false);
void finishSelection(); void finishSelection();
void moveCursor(int x, int y, bool select); void moveCursor(int x, int y, bool select);
@ -1309,8 +1332,6 @@ class FurnaceGUI {
void initSystemPresets(); void initSystemPresets();
void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex=false); void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex=false);
void encodeMMLStr(String& target, unsigned char* macro, unsigned char macroLen, signed char macroLoop, signed char macroRel);
void decodeMMLStr(String& source, unsigned char* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel);
void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel); void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel);
void decodeMMLStrW(String& source, int* macro, int& macroLen, int macroMax, bool hex=false); void decodeMMLStrW(String& source, int* macro, int& macroLen, int macroMax, bool hex=false);

View file

@ -101,7 +101,7 @@ const char* insTypes[DIV_INS_MAX]={
"Konami SCC/Bubble System WSG", "Konami SCC/Bubble System WSG",
"FM (OPZ)", "FM (OPZ)",
"POKEY", "POKEY",
"PC Beeper", "Beeper",
"WonderSwan", "WonderSwan",
"Atari Lynx", "Atari Lynx",
"VERA", "VERA",
@ -116,8 +116,8 @@ const char* insTypes[DIV_INS_MAX]={
const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={ const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={
"1-bit PCM", "1-bit PCM",
"1-bit DPCM", "1-bit DPCM",
NULL, "Yamaha AICA",
NULL, "YMZ/YMU ADPCM",
"QSound ADPCM", "QSound ADPCM",
"ADPCM-A", "ADPCM-A",
"ADPCM-B", "ADPCM-B",
@ -492,6 +492,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WINDOW_CHANNELS", "Channels", 0), D("WINDOW_CHANNELS", "Channels", 0),
D("WINDOW_REGISTER_VIEW", "Register View", 0), D("WINDOW_REGISTER_VIEW", "Register View", 0),
D("WINDOW_LOG", "Log Viewer", 0), D("WINDOW_LOG", "Log Viewer", 0),
D("WINDOW_SUBSONGS", "Subsongs", 0),
D("EFFECT_LIST", "Effect List", 0), D("EFFECT_LIST", "Effect List", 0),
D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0), D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
@ -838,14 +839,21 @@ const int availableSystems[]={
DIV_SYSTEM_AY8910, DIV_SYSTEM_AY8910,
DIV_SYSTEM_AMIGA, DIV_SYSTEM_AMIGA,
DIV_SYSTEM_PCSPKR, DIV_SYSTEM_PCSPKR,
DIV_SYSTEM_SFX_BEEPER,
DIV_SYSTEM_YMU759, DIV_SYSTEM_YMU759,
DIV_SYSTEM_DUMMY, DIV_SYSTEM_DUMMY,
DIV_SYSTEM_SOUND_UNIT, DIV_SYSTEM_SOUND_UNIT,
DIV_SYSTEM_OPN,
DIV_SYSTEM_OPN_EXT,
DIV_SYSTEM_PC98,
DIV_SYSTEM_PC98_EXT,
DIV_SYSTEM_OPLL, DIV_SYSTEM_OPLL,
DIV_SYSTEM_OPLL_DRUMS, DIV_SYSTEM_OPLL_DRUMS,
DIV_SYSTEM_VRC7, DIV_SYSTEM_VRC7,
DIV_SYSTEM_OPL, DIV_SYSTEM_OPL,
DIV_SYSTEM_OPL_DRUMS, DIV_SYSTEM_OPL_DRUMS,
DIV_SYSTEM_Y8950,
DIV_SYSTEM_Y8950_DRUMS,
DIV_SYSTEM_OPL2, DIV_SYSTEM_OPL2,
DIV_SYSTEM_OPL2_DRUMS, DIV_SYSTEM_OPL2_DRUMS,
DIV_SYSTEM_OPL3, DIV_SYSTEM_OPL3,

View file

@ -191,8 +191,12 @@ const char* es5506EnvelopeModes[3]={
"k1 slowdown", "k2 slowdown", NULL "k1 slowdown", "k2 slowdown", NULL
}; };
const char* es5506ControlModes[3]={ const char* es5506ControlModes[2]={
"pause", "transwave slice", NULL "pause", NULL
};
const char* transwaveControlModes[2]={
"slice", NULL
}; };
const char* oneBit[2]={ const char* oneBit[2]={
@ -1135,7 +1139,7 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
float loopIndicator[256]; float loopIndicator[256];
int index=0; int index=0;
float reservedSpace=ImGui::GetStyle().ScrollbarSize; float reservedSpace=(settings.oldMacroVSlider)?(20.0f*dpiScale+ImGui::GetStyle().ItemSpacing.x):ImGui::GetStyle().ScrollbarSize;
if (ImGui::BeginTable("MacroSpace",2)) { if (ImGui::BeginTable("MacroSpace",2)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
@ -1211,8 +1215,8 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
i.macro->vZoom=24; i.macro->vZoom=24;
i.macro->vScroll=120-12; i.macro->vScroll=120-12;
} else if (i.macro->name=="pitch") { } else if (i.macro->name=="pitch") {
i.macro->vZoom=64; i.macro->vZoom=128;
i.macro->vScroll=1024-32; i.macro->vScroll=2048-64;
} else { } else {
i.macro->vZoom=i.max-i.min; i.macro->vZoom=i.max-i.min;
i.macro->vScroll=0; i.macro->vScroll=0;
@ -1251,9 +1255,10 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
} }
if (i.macro->open) { if (i.macro->open) {
if (ImGui::IsItemHovered() && ctrlWheeling) { if (ImGui::IsItemHovered()) {
if (ctrlWheeling) {
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) {
i.macro->vZoom+=wheelY; i.macro->vZoom+=wheelY*(1+(i.macro->vZoom>>4));
if (i.macro->vZoom<1) i.macro->vZoom=1; if (i.macro->vZoom<1) i.macro->vZoom=1;
if (i.macro->vZoom>(i.max-i.min)) i.macro->vZoom=i.max-i.min; if (i.macro->vZoom>(i.max-i.min)) i.macro->vZoom=i.max-i.min;
if ((i.macro->vScroll+i.macro->vZoom)>(i.max-i.min)) { if ((i.macro->vScroll+i.macro->vZoom)>(i.max-i.min)) {
@ -1264,26 +1269,48 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
if (macroPointSize<1) macroPointSize=1; if (macroPointSize<1) macroPointSize=1;
if (macroPointSize>256) macroPointSize=256; if (macroPointSize>256) macroPointSize=256;
} }
} else if ((ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) && wheelY!=0) {
i.macro->vScroll+=wheelY*(1+(i.macro->vZoom>>4));
if (i.macro->vScroll<0) i.macro->vScroll=0;
if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom;
}
} }
// slider // slider
if (!i.isBitfield) { if (!i.isBitfield) {
if (settings.oldMacroVSlider) {
ImGui::SameLine(0.0f);
if (ImGui::VSliderInt("IMacroVScroll",ImVec2(20.0f*dpiScale,i.height*dpiScale),&i.macro->vScroll,0,(i.max-i.min)-i.macro->vZoom,"")) {
if (i.macro->vScroll<0) i.macro->vScroll=0;
if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom;
}
if (ImGui::IsItemHovered() && ctrlWheeling) {
i.macro->vScroll+=wheelY*(1+(i.macro->vZoom>>4));
if (i.macro->vScroll<0) i.macro->vScroll=0;
if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom;
}
} else {
ImS64 scrollV=(i.max-i.min-i.macro->vZoom)-i.macro->vScroll; ImS64 scrollV=(i.max-i.min-i.macro->vZoom)-i.macro->vScroll;
ImS64 availV=i.macro->vZoom; ImS64 availV=i.macro->vZoom;
ImS64 contentsV=(i.max-i.min); ImS64 contentsV=(i.max-i.min);
ImGui::SameLine(0.0f); ImGui::SameLine(0.0f);
ImGui::SetCursorPosX(ImGui::GetCursorPosX()-ImGui::GetStyle().ItemSpacing.x);
ImRect scrollbarPos=ImRect(ImGui::GetCursorScreenPos(),ImGui::GetCursorScreenPos()); ImRect scrollbarPos=ImRect(ImGui::GetCursorScreenPos(),ImGui::GetCursorScreenPos());
scrollbarPos.Min.x-=ImGui::GetStyle().ItemSpacing.x;
scrollbarPos.Max.x+=ImGui::GetStyle().ScrollbarSize; scrollbarPos.Max.x+=ImGui::GetStyle().ScrollbarSize;
scrollbarPos.Max.x-=ImGui::GetStyle().ItemSpacing.x;
scrollbarPos.Max.y+=i.height*dpiScale; scrollbarPos.Max.y+=i.height*dpiScale;
ImGui::Dummy(ImVec2(ImGui::GetStyle().ScrollbarSize,i.height*dpiScale)); ImGui::Dummy(ImVec2(ImGui::GetStyle().ScrollbarSize,i.height*dpiScale));
if (ImGui::IsItemHovered() && ctrlWheeling) {
i.macro->vScroll+=wheelY*(1+(i.macro->vZoom>>4));
if (i.macro->vScroll<0) i.macro->vScroll=0;
if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom;
}
if (ImGui::ScrollbarEx(scrollbarPos,ImGui::GetID("IMacroVScroll"),ImGuiAxis_Y,&scrollV,availV,contentsV,0)) { if (ImGui::ScrollbarEx(scrollbarPos,ImGui::GetID("IMacroVScroll"),ImGuiAxis_Y,&scrollV,availV,contentsV,0)) {
i.macro->vScroll=(i.max-i.min-i.macro->vZoom)-scrollV; i.macro->vScroll=(i.max-i.min-i.macro->vZoom)-scrollV;
} }
} }
}
// loop area // loop area
PlotCustom("##IMacroLoop",loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,&macroHoverLoop); PlotCustom("##IMacroLoop",loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,&macroHoverLoop);
@ -3110,7 +3137,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_GB) { if (ins->type==DIV_INS_GB) {
volMax=0; volMax=0;
} }
if (ins->type==DIV_INS_PET) { if (ins->type==DIV_INS_PET || ins->type==DIV_INS_BEEPER) {
volMax=1; volMax=1;
} }
if (ins->type==DIV_INS_FDS) { if (ins->type==DIV_INS_FDS) {
@ -3145,6 +3172,10 @@ void FurnaceGUI::drawInsEdit() {
dutyLabel="Duty/Int"; dutyLabel="Duty/Int";
dutyMax=10; dutyMax=10;
} }
if (ins->type==DIV_INS_BEEPER) {
dutyLabel="Pulse Width";
dutyMax=255;
}
if (ins->type==DIV_INS_AY8930) { if (ins->type==DIV_INS_AY8930) {
dutyMax=255; dutyMax=255;
} }
@ -3406,7 +3437,9 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc("Envelope K1 ramp",&ins->std.ex6Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Envelope K1 ramp",&ins->std.ex6Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("Envelope K2 ramp",&ins->std.ex7Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Envelope K2 ramp",&ins->std.ex7Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("Envelope mode",&ins->std.ex8Macro,0,2,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,es5506EnvelopeModes)); macroList.push_back(FurnaceGUIMacroDesc("Envelope mode",&ins->std.ex8Macro,0,2,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,es5506EnvelopeModes));
macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.algMacro,0,2,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,es5506ControlModes)); macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.algMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,es5506ControlModes));
macroList.push_back(FurnaceGUIMacroDesc("Transwave control",&ins->std.fbMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,transwaveControlModes));
macroList.push_back(FurnaceGUIMacroDesc("Transwave slice",&ins->std.fmsMacro,0,4095,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} }
if (ins->type==DIV_INS_SU) { if (ins->type==DIV_INS_SU) {
macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex3Macro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,suControlBits)); macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex3Macro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,suControlBits));

View file

@ -18,6 +18,7 @@
*/ */
#include "gui.h" #include "gui.h"
#include <imgui.h>
void FurnaceGUI::drawNewSong() { void FurnaceGUI::drawNewSong() {
bool accepted=false; bool accepted=false;
@ -27,7 +28,11 @@ void FurnaceGUI::drawNewSong() {
ImGui::Text("Choose a System!"); ImGui::Text("Choose a System!");
ImGui::PopFont(); ImGui::PopFont();
if (ImGui::BeginTable("sysPicker",2)) { ImVec2 avail=ImGui::GetContentRegionAvail();
avail.y-=ImGui::GetFrameHeightWithSpacing();
if (ImGui::BeginChild("sysPickerC",avail,false,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
if (ImGui::BeginTable("sysPicker",2,ImGuiTableFlags_BordersInnerV)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f); ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f);
@ -46,6 +51,9 @@ void FurnaceGUI::drawNewSong() {
if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \ if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \
newSongCategory=index; newSongCategory=index;
} }
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",i.description);
}
index++; index++;
} }
@ -65,6 +73,8 @@ void FurnaceGUI::drawNewSong() {
ImGui::EndTable(); ImGui::EndTable();
} }
}
ImGui::EndChild();
if (ImGui::Button("I'm feeling lucky")) { if (ImGui::Button("I'm feeling lucky")) {
if (sysCategories.size()==0) { if (sysCategories.size()==0) {

View file

@ -37,7 +37,7 @@ void FurnaceGUI::drawOrders() {
ImGui::SetColumnWidth(-1,regionX-24.0f*dpiScale); ImGui::SetColumnWidth(-1,regionX-24.0f*dpiScale);
int displayChans=0; int displayChans=0;
for (int i=0; i<e->getTotalChannelCount(); i++) { for (int i=0; i<e->getTotalChannelCount(); i++) {
if (e->song.chanShow[i]) displayChans++; if (e->curSubSong->chanShow[i]) displayChans++;
} }
ImGui::PushFont(patFont); ImGui::PushFont(patFont);
bool tooSmall=((displayChans+1)>((ImGui::GetContentRegionAvail().x)/(ImGui::CalcTextSize("AA").x+2.0*ImGui::GetStyle().ItemInnerSpacing.x))); bool tooSmall=((displayChans+1)>((ImGui::GetContentRegionAvail().x)/(ImGui::CalcTextSize("AA").x+2.0*ImGui::GetStyle().ItemInnerSpacing.x)));
@ -56,12 +56,12 @@ void FurnaceGUI::drawOrders() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_ORDER_ROW_INDEX]); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_ORDER_ROW_INDEX]);
for (int i=0; i<e->getTotalChannelCount(); i++) { for (int i=0; i<e->getTotalChannelCount(); i++) {
if (!e->song.chanShow[i]) continue; if (!e->curSubSong->chanShow[i]) continue;
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",e->getChannelShortName(i)); ImGui::Text("%s",e->getChannelShortName(i));
} }
ImGui::PopStyleColor(); ImGui::PopStyleColor();
for (int i=0; i<e->song.ordersLen; i++) { for (int i=0; i<e->curSubSong->ordersLen; i++) {
ImGui::TableNextRow(0,lineHeight); ImGui::TableNextRow(0,lineHeight);
if (oldOrder1==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE])); if (oldOrder1==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE]));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -84,16 +84,16 @@ void FurnaceGUI::drawOrders() {
} }
ImGui::PopStyleColor(); ImGui::PopStyleColor();
for (int j=0; j<e->getTotalChannelCount(); j++) { for (int j=0; j<e->getTotalChannelCount(); j++) {
if (!e->song.chanShow[j]) continue; if (!e->curSubSong->chanShow[j]) continue;
ImGui::TableNextColumn(); ImGui::TableNextColumn();
DivPattern* pat=e->song.pat[j].getPattern(e->song.orders.ord[j][i],false); DivPattern* pat=e->curPat[j].getPattern(e->curOrders->ord[j][i],false);
/*if (!pat->name.empty()) { /*if (!pat->name.empty()) {
snprintf(selID,4096,"%s##O_%.2x_%.2x",pat->name.c_str(),j,i); snprintf(selID,4096,"%s##O_%.2x_%.2x",pat->name.c_str(),j,i);
} else {*/ } else {*/
snprintf(selID,4096,"%.2X##O_%.2x_%.2x",e->song.orders.ord[j][i],j,i); snprintf(selID,4096,"%.2X##O_%.2x_%.2x",e->curOrders->ord[j][i],j,i);
//} //}
ImGui::PushStyleColor(ImGuiCol_Text,(curOrder==i || e->song.orders.ord[j][i]==e->song.orders.ord[j][curOrder])?uiColors[GUI_COLOR_ORDER_SIMILAR]:uiColors[GUI_COLOR_ORDER_INACTIVE]); ImGui::PushStyleColor(ImGuiCol_Text,(curOrder==i || e->curOrders->ord[j][i]==e->curOrders->ord[j][curOrder])?uiColors[GUI_COLOR_ORDER_SIMILAR]:uiColors[GUI_COLOR_ORDER_INACTIVE]);
if (ImGui::Selectable(selID,(orderEditMode!=0 && curOrder==i && orderCursor==j))) { if (ImGui::Selectable(selID,(orderEditMode!=0 && curOrder==i && orderCursor==j))) {
if (curOrder==i) { if (curOrder==i) {
if (orderEditMode==0) { if (orderEditMode==0) {
@ -101,10 +101,10 @@ void FurnaceGUI::drawOrders() {
e->lockSave([this,i,j]() { e->lockSave([this,i,j]() {
if (changeAllOrders) { if (changeAllOrders) {
for (int k=0; k<e->getTotalChannelCount(); k++) { for (int k=0; k<e->getTotalChannelCount(); k++) {
if (e->song.orders.ord[k][i]<0xff) e->song.orders.ord[k][i]++; if (e->curOrders->ord[k][i]<0xff) e->curOrders->ord[k][i]++;
} }
} else { } else {
if (e->song.orders.ord[j][i]<0xff) e->song.orders.ord[j][i]++; if (e->curOrders->ord[j][i]<0xff) e->curOrders->ord[j][i]++;
} }
}); });
e->walkSong(loopOrder,loopRow,loopEnd); e->walkSong(loopOrder,loopRow,loopEnd);
@ -137,10 +137,10 @@ void FurnaceGUI::drawOrders() {
e->lockSave([this,i,j]() { e->lockSave([this,i,j]() {
if (changeAllOrders) { if (changeAllOrders) {
for (int k=0; k<e->getTotalChannelCount(); k++) { for (int k=0; k<e->getTotalChannelCount(); k++) {
if (e->song.orders.ord[k][i]>0) e->song.orders.ord[k][i]--; if (e->curOrders->ord[k][i]>0) e->curOrders->ord[k][i]--;
} }
} else { } else {
if (e->song.orders.ord[j][i]>0) e->song.orders.ord[j][i]--; if (e->curOrders->ord[j][i]>0) e->curOrders->ord[j][i]--;
} }
}); });
e->walkSong(loopOrder,loopRow,loopEnd); e->walkSong(loopOrder,loopRow,loopEnd);

View file

@ -43,21 +43,21 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
return; return;
} }
// check if we are in range // check if we are in range
if (ord<0 || ord>=e->song.ordersLen) { if (ord<0 || ord>=e->curSubSong->ordersLen) {
return; return;
} }
if (i<0 || i>=e->song.patLen) { if (i<0 || i>=e->curSubSong->patLen) {
return; return;
} }
bool isPushing=false; bool isPushing=false;
ImVec4 activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE]; ImVec4 activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE];
ImVec4 inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE]; ImVec4 inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE];
ImVec4 rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX]; ImVec4 rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX];
if (e->song.hilightB>0 && !(i%e->song.hilightB)) { if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE_HI2]; activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE_HI2];
inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_HI2]; inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_HI2];
rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX_HI2]; rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX_HI2];
} else if (e->song.hilightA>0 && !(i%e->song.hilightA)) { } else if (e->curSubSong->hilightA>0 && !(i%e->curSubSong->hilightA)) {
activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE_HI1]; activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE_HI1];
inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_HI1]; inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_HI1];
rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX_HI1]; rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX_HI1];
@ -68,9 +68,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING])); ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
} else if (isPlaying && oldRow==i && ord==e->getOrder()) { } else if (isPlaying && oldRow==i && ord==e->getOrder()) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD])); ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
} else if (e->song.hilightB>0 && !(i%e->song.hilightB)) { } else if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2])); ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
} else if (e->song.hilightA>0 && !(i%e->song.hilightA)) { } else if (e->curSubSong->hilightA>0 && !(i%e->curSubSong->hilightA)) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1])); ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1]));
} }
} else { } else {
@ -79,24 +79,34 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING])); ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
} else if (isPlaying && oldRow==i && ord==e->getOrder()) { } else if (isPlaying && oldRow==i && ord==e->getOrder()) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD])); ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
} else if (e->song.hilightB>0 && !(i%e->song.hilightB)) { } else if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2])); ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
} else if (e->song.hilightA>0 && !(i%e->song.hilightA)) { } else if (e->curSubSong->hilightA>0 && !(i%e->curSubSong->hilightA)) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1])); ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1]));
} else { } else {
isPushing=false; isPushing=false;
} }
} }
// row number // row number
ImGui::PushStyleColor(ImGuiCol_Text,rowIndexColor);
if (settings.patRowsBase==1) { if (settings.patRowsBase==1) {
ImGui::TextColored(rowIndexColor," %.2X ",i); snprintf(id,31," %.2X ##PR_%d",i,i);
} else { } else {
ImGui::TextColored(rowIndexColor,"%3d ",i); snprintf(id,31,"%3d ##PR_%d",i,i);
} }
ImGui::Selectable(id,false,ImGuiSelectableFlags_NoPadWithHalfSpacing,fourChars);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) {
updateSelection(0,0,i,true);
}
if (ImGui::IsItemClicked()) {
startSelection(0,0,i,true);
}
ImGui::PopStyleColor();
// for each column // for each column
for (int j=0; j<chans; j++) { for (int j=0; j<chans; j++) {
// check if channel is not hidden // check if channel is not hidden
if (!e->song.chanShow[j]) { if (!e->curSubSong->chanShow[j]) {
patChanX[j]=ImGui::GetCursorPosX(); patChanX[j]=ImGui::GetCursorPosX();
continue; continue;
} }
@ -145,7 +155,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PopStyleColor(); ImGui::PopStyleColor();
// the following is only visible when the channel is not collapsed // the following is only visible when the channel is not collapsed
if (e->song.chanCollapse[j]<3) { if (e->curSubSong->chanCollapse[j]<3) {
// instrument // instrument
if (pat->data[i][2]==-1) { if (pat->data[i][2]==-1) {
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor); ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
@ -185,7 +195,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PopStyleColor(); ImGui::PopStyleColor();
} }
if (e->song.chanCollapse[j]<2) { if (e->curSubSong->chanCollapse[j]<2) {
// volume // volume
if (pat->data[i][3]==-1) { if (pat->data[i][3]==-1) {
sprintf(id,"..##PV_%d_%d",i,j); sprintf(id,"..##PV_%d_%d",i,j);
@ -219,9 +229,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PopStyleColor(); ImGui::PopStyleColor();
} }
if (e->song.chanCollapse[j]<1) { if (e->curSubSong->chanCollapse[j]<1) {
// effects // effects
for (int k=0; k<e->song.pat[j].effectCols; k++) { for (int k=0; k<e->curPat[j].effectCols; k++) {
int index=4+(k<<1); int index=4+(k<<1);
bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum); bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum);
bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum); bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum);
@ -348,7 +358,7 @@ void FurnaceGUI::drawPattern() {
int displayChans=0; int displayChans=0;
const DivPattern* patCache[DIV_MAX_CHANS]; const DivPattern* patCache[DIV_MAX_CHANS];
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
if (e->song.chanShow[i]) displayChans++; if (e->curSubSong->chanShow[i]) displayChans++;
} }
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]); ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
@ -373,7 +383,7 @@ void FurnaceGUI::drawPattern() {
} }
ImGui::TableSetupScrollFreeze(1,1); ImGui::TableSetupScrollFreeze(1,1);
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
if (!e->song.chanShow[i]) continue; if (!e->curSubSong->chanShow[i]) continue;
ImGui::TableSetupColumn(fmt::sprintf("c%d",i).c_str(),ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn(fmt::sprintf("c%d",i).c_str(),ImGuiTableColumnFlags_WidthFixed);
} }
ImGui::TableNextRow(); ImGui::TableNextRow();
@ -398,10 +408,10 @@ void FurnaceGUI::drawPattern() {
cmdStream.clear(); cmdStream.clear();
} }
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
if (!e->song.chanShow[i]) continue; if (!e->curSubSong->chanShow[i]) continue;
ImGui::TableNextColumn(); ImGui::TableNextColumn();
bool displayTooltip=false; bool displayTooltip=false;
if (e->song.chanCollapse[i]) { if (e->curSubSong->chanCollapse[i]) {
const char* chName=e->getChannelShortName(i); const char* chName=e->getChannelShortName(i);
if (strlen(chName)>3) { if (strlen(chName)>3) {
snprintf(chanID,2048,"...##_CH%d",i); snprintf(chanID,2048,"...##_CH%d",i);
@ -411,7 +421,7 @@ void FurnaceGUI::drawPattern() {
displayTooltip=true; displayTooltip=true;
} else { } else {
const char* chName=e->getChannelName(i); const char* chName=e->getChannelName(i);
size_t chNameLimit=6+4*e->song.pat[i].effectCols; size_t chNameLimit=6+4*e->curPat[i].effectCols;
if (strlen(chName)>chNameLimit) { if (strlen(chName)>chNameLimit) {
String shortChName=chName; String shortChName=chName;
shortChName.resize(chNameLimit-3); shortChName.resize(chNameLimit-3);
@ -473,7 +483,7 @@ void FurnaceGUI::drawPattern() {
e->toggleSolo(i); e->toggleSolo(i);
} }
if (extraChannelButtons==2) { if (extraChannelButtons==2) {
DivPattern* pat=e->song.pat[i].getPattern(e->song.orders.ord[i][ord],true); DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
ImGui::PushFont(mainFont); ImGui::PushFont(mainFont);
if (patNameTarget==i) { if (patNameTarget==i) {
snprintf(chanID,2048,"##PatNameI%d_%d",i,ord); snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
@ -500,30 +510,30 @@ void FurnaceGUI::drawPattern() {
} }
ImGui::PopFont(); ImGui::PopFont();
} else if (extraChannelButtons==1) { } else if (extraChannelButtons==1) {
snprintf(chanID,2048,"%c##_HCH%d",e->song.chanCollapse[i]?'+':'-',i); snprintf(chanID,2048,"%c##_HCH%d",e->curSubSong->chanCollapse[i]?'+':'-',i);
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale); ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale);
if (ImGui::SmallButton(chanID)) { if (ImGui::SmallButton(chanID)) {
if (e->song.chanCollapse[i]==0) { if (e->curSubSong->chanCollapse[i]==0) {
e->song.chanCollapse[i]=3; e->curSubSong->chanCollapse[i]=3;
} else if (e->song.chanCollapse[i]>0) { } else if (e->curSubSong->chanCollapse[i]>0) {
e->song.chanCollapse[i]--; e->curSubSong->chanCollapse[i]--;
} }
} }
if (!e->song.chanCollapse[i]) { if (!e->curSubSong->chanCollapse[i]) {
ImGui::SameLine(); ImGui::SameLine();
snprintf(chanID,2048,"<##_LCH%d",i); snprintf(chanID,2048,"<##_LCH%d",i);
ImGui::BeginDisabled(e->song.pat[i].effectCols<=1); ImGui::BeginDisabled(e->curPat[i].effectCols<=1);
if (ImGui::SmallButton(chanID)) { if (ImGui::SmallButton(chanID)) {
e->song.pat[i].effectCols--; e->curPat[i].effectCols--;
if (e->song.pat[i].effectCols<1) e->song.pat[i].effectCols=1; if (e->curPat[i].effectCols<1) e->curPat[i].effectCols=1;
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
ImGui::BeginDisabled(e->song.pat[i].effectCols>=8); ImGui::BeginDisabled(e->curPat[i].effectCols>=8);
snprintf(chanID,2048,">##_RCH%d",i); snprintf(chanID,2048,">##_RCH%d",i);
if (ImGui::SmallButton(chanID)) { if (ImGui::SmallButton(chanID)) {
e->song.pat[i].effectCols++; e->curPat[i].effectCols++;
if (e->song.pat[i].effectCols>8) e->song.pat[i].effectCols=8; if (e->curPat[i].effectCols>8) e->curPat[i].effectCols=8;
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
} }
@ -535,6 +545,7 @@ void FurnaceGUI::drawPattern() {
ImGui::TextColored(uiColors[GUI_COLOR_EE_VALUE]," %.2X",e->getExtValue()); ImGui::TextColored(uiColors[GUI_COLOR_EE_VALUE]," %.2X",e->getExtValue());
} }
float oneCharSize=ImGui::CalcTextSize("A").x; float oneCharSize=ImGui::CalcTextSize("A").x;
fourChars=ImVec2(oneCharSize*4.0f,lineHeight);
threeChars=ImVec2(oneCharSize*3.0f,lineHeight); threeChars=ImVec2(oneCharSize*3.0f,lineHeight);
twoChars=ImVec2(oneCharSize*2.0f,lineHeight); twoChars=ImVec2(oneCharSize*2.0f,lineHeight);
//ImVec2 oneChar=ImVec2(oneCharSize,lineHeight); //ImVec2 oneChar=ImVec2(oneCharSize,lineHeight);
@ -544,10 +555,10 @@ void FurnaceGUI::drawPattern() {
ImGui::BeginDisabled(); ImGui::BeginDisabled();
if (settings.viewPrevPattern) { if (settings.viewPrevPattern) {
if ((ord-1)>=0) for (int i=0; i<chans; i++) { if ((ord-1)>=0) for (int i=0; i<chans; i++) {
patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord-1],true); patCache[i]=e->curPat[i].getPattern(e->curOrders->ord[i][ord-1],true);
} }
for (int i=0; i<dummyRows-1; i++) { for (int i=0; i<dummyRows-1; i++) {
patternRow(e->song.patLen+i-dummyRows+1,e->isPlaying(),lineHeight,chans,ord-1,patCache,true); patternRow(e->curSubSong->patLen+i-dummyRows+1,e->isPlaying(),lineHeight,chans,ord-1,patCache,true);
} }
} else { } else {
for (int i=0; i<dummyRows-1; i++) { for (int i=0; i<dummyRows-1; i++) {
@ -558,16 +569,16 @@ void FurnaceGUI::drawPattern() {
ImGui::EndDisabled(); ImGui::EndDisabled();
// active area // active area
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord],true); patCache[i]=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
} }
for (int i=0; i<e->song.patLen; i++) { for (int i=0; i<e->curSubSong->patLen; i++) {
patternRow(i,e->isPlaying(),lineHeight,chans,ord,patCache,false); patternRow(i,e->isPlaying(),lineHeight,chans,ord,patCache,false);
} }
// next pattern // next pattern
ImGui::BeginDisabled(); ImGui::BeginDisabled();
if (settings.viewPrevPattern) { if (settings.viewPrevPattern) {
if ((ord+1)<e->song.ordersLen) for (int i=0; i<chans; i++) { if ((ord+1)<e->curSubSong->ordersLen) for (int i=0; i<chans; i++) {
patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord+1],true); patCache[i]=e->curPat[i].getPattern(e->curOrders->ord[i][ord+1],true);
} }
for (int i=0; i<=dummyRows; i++) { for (int i=0; i<=dummyRows; i++) {
patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache,true); patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache,true);
@ -601,7 +612,7 @@ void FurnaceGUI::drawPattern() {
if (curOrder>0) { if (curOrder>0) {
setOrder(curOrder-1); setOrder(curOrder-1);
ImGui::SetScrollY(ImGui::GetScrollMaxY()); ImGui::SetScrollY(ImGui::GetScrollMaxY());
updateScroll(e->song.patLen); updateScroll(e->curSubSong->patLen);
} }
haveHitBounds=false; haveHitBounds=false;
} else { } else {
@ -613,7 +624,7 @@ void FurnaceGUI::drawPattern() {
} else { } else {
if (ImGui::GetScrollY()>=ImGui::GetScrollMaxY()) { if (ImGui::GetScrollY()>=ImGui::GetScrollMaxY()) {
if (haveHitBounds) { if (haveHitBounds) {
if (curOrder<(e->song.ordersLen-1)) { if (curOrder<(e->curSubSong->ordersLen-1)) {
setOrder(curOrder+1); setOrder(curOrder+1);
ImGui::SetScrollY(0); ImGui::SetScrollY(0);
updateScroll(0); updateScroll(0);
@ -765,7 +776,7 @@ void FurnaceGUI::drawPattern() {
// note slides // note slides
ImVec2 arrowPoints[7]; ImVec2 arrowPoints[7];
if (e->isPlaying()) for (int i=0; i<chans; i++) { if (e->isPlaying()) for (int i=0; i<chans; i++) {
if (!e->song.chanShow[i]) continue; if (!e->curSubSong->chanShow[i]) continue;
DivChannelState* ch=e->getChanState(i); DivChannelState* ch=e->getChanState(i);
if (ch->portaSpeed>0) { if (ch->portaSpeed>0) {
ImVec4 col=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]; ImVec4 col=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH];

View file

@ -31,27 +31,41 @@
// )); // ));
void FurnaceGUI::initSystemPresets() { void FurnaceGUI::initSystemPresets() {
sysCategories.clear();
FurnaceGUISysCategory cat; FurnaceGUISysCategory cat;
cat=FurnaceGUISysCategory("FM"); cat=FurnaceGUISysCategory("FM","chips which use frequency modulation (FM) to generate sound.\nsome of these also pack more (like square and sample channels).");
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612", {
DIV_SYSTEM_YM2612, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612 (extended channel 3)", {
DIV_SYSTEM_YM2612_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2151", { "Yamaha YM2151", {
DIV_SYSTEM_YM2151, 64, 0, 0, DIV_SYSTEM_YM2151, 64, 0, 0,
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2203", {
DIV_SYSTEM_OPN, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2203 (extended channel 3)", {
DIV_SYSTEM_OPN_EXT, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2608", {
DIV_SYSTEM_PC98, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2608 (extended channel 3)", {
DIV_SYSTEM_PC98_EXT, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2610", { "Yamaha YM2610", {
DIV_SYSTEM_YM2610_FULL, 64, 0, 0, DIV_SYSTEM_YM2610_FULL, 64, 0, 0,
@ -76,6 +90,18 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612", {
DIV_SYSTEM_YM2612, 64, 0, (int)0x80000000,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612 (extended channel 3)", {
DIV_SYSTEM_YM2612_EXT, 64, 0, (int)0x80000000,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2413", { "Yamaha YM2413", {
DIV_SYSTEM_OPLL, 64, 0, 0, DIV_SYSTEM_OPLL, 64, 0, 0,
@ -94,6 +120,18 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3438", {
DIV_SYSTEM_YM2612, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3438 (extended channel 3)", {
DIV_SYSTEM_YM2612_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3526", { "Yamaha YM3526", {
DIV_SYSTEM_OPL, 64, 0, 0, DIV_SYSTEM_OPL, 64, 0, 0,
@ -106,6 +144,18 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha Y8950", {
DIV_SYSTEM_Y8950, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha Y8950 (drums mode)", {
DIV_SYSTEM_Y8950_DRUMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3812", { "Yamaha YM3812", {
DIV_SYSTEM_OPL2, 64, 0, 0, DIV_SYSTEM_OPL2, 64, 0, 0,
@ -130,11 +180,25 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
if (settings.hiddenSystems) {
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YMU759", {
DIV_SYSTEM_YMU759, 64, 0, 0,
0
}
));
}
sysCategories.push_back(cat); sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Square"); cat=FurnaceGUISysCategory("Square","these chips generate square/pulse tones only (but may include noise).");
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"TI SN76489", { "TI SN76489", {
DIV_SYSTEM_SMS, 64, 0, 4,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega PSG (SN76489-like)", {
DIV_SYSTEM_SMS, 64, 0, 0, DIV_SYSTEM_SMS, 64, 0, 0,
0 0
} }
@ -145,15 +209,33 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2149(F)", {
DIV_SYSTEM_AY8910, 64, 0, 16,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Philips SAA1099", { "Philips SAA1099", {
DIV_SYSTEM_SAA1099, 64, 0, 0, DIV_SYSTEM_SAA1099, 64, 0, 0,
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"PC Speaker", {
DIV_SYSTEM_PCSPKR, 32, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore VIC", {
DIV_SYSTEM_VIC20, 64, 0, 1,
0
}
));
sysCategories.push_back(cat); sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Sample"); cat=FurnaceGUISysCategory("Sample","chips/systems which use PCM or ADPCM samples for sound synthesis.");
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Amiga", { "Amiga", {
DIV_SYSTEM_AMIGA, 64, 0, 0, DIV_SYSTEM_AMIGA, 64, 0, 0,
@ -186,7 +268,129 @@ void FurnaceGUI::initSystemPresets() {
)); ));
sysCategories.push_back(cat); sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Game consoles"); cat=FurnaceGUISysCategory("Wavetable","chips which use user-specified waveforms to generate sound.");
cat.systems.push_back(FurnaceGUISysDef(
"PC Engine", {
DIV_SYSTEM_PCE, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore PET (pseudo-wavetable)", {
DIV_SYSTEM_PET, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Konami Bubble System WSG", {
DIV_SYSTEM_BUBSYS_WSG, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Konami SCC", {
DIV_SYSTEM_SCC, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Konami SCC+", {
DIV_SYSTEM_SCC_PLUS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Namco 163", {
DIV_SYSTEM_N163, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Famicom Disk System (chip)", {
DIV_SYSTEM_FDS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"WonderSwan", {
DIV_SYSTEM_SWAN, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Seta/Allumer X1-010", {
DIV_SYSTEM_X1_010, 64, 0, 0,
0
}
));
sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Specialized","chips/systems with unique sound synthesis methods.");
cat.systems.push_back(FurnaceGUISysDef(
"MOS Technology SID (6581)", {
DIV_SYSTEM_C64_6581, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MOS Technology SID (8580)", {
DIV_SYSTEM_C64_8580, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Microchip AY8930", {
DIV_SYSTEM_AY8930, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Game Boy", {
DIV_SYSTEM_GB, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Atari Lynx", {
DIV_SYSTEM_LYNX, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Atari TIA", {
DIV_SYSTEM_TIA, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commander X16 (VERA only)", {
DIV_SYSTEM_VERA, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"ZX Spectrum (beeper only)", {
DIV_SYSTEM_SFX_BEEPER, 64, 0, 0,
0
}
));
if (settings.hiddenSystems) {
cat.systems.push_back(FurnaceGUISysDef(
"Dummy System", {
DIV_SYSTEM_DUMMY, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"tildearrow Sound Unit", {
DIV_SYSTEM_SOUND_UNIT, 64, 0, 0,
0
}
));
}
sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Game consoles","let's play some chiptune making games!");
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis", { "Sega Genesis", {
DIV_SYSTEM_YM2612, 64, 0, 0, DIV_SYSTEM_YM2612, 64, 0, 0,
@ -263,7 +467,7 @@ void FurnaceGUI::initSystemPresets() {
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with Sunsoft 5B", { "NES with Sunsoft 5B", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 38, DIV_SYSTEM_AY8910, 64, 0, 32,
0 0
} }
)); ));
@ -345,7 +549,7 @@ void FurnaceGUI::initSystemPresets() {
)); ));
sysCategories.push_back(cat); sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Computers"); cat=FurnaceGUISysCategory("Computers","let's get to work on chiptune today.");
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Commodore PET", { "Commodore PET", {
DIV_SYSTEM_PET, 64, 0, 0, DIV_SYSTEM_PET, 64, 0, 0,
@ -417,6 +621,20 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + MSX-AUDIO", {
DIV_SYSTEM_AY8910, 64, 0, 16,
DIV_SYSTEM_Y8950, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + MSX-AUDIO (drums mode)", {
DIV_SYSTEM_AY8910, 64, 0, 16,
DIV_SYSTEM_Y8950_DRUMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"MSX + MSX-MUSIC", { "MSX + MSX-MUSIC", {
DIV_SYSTEM_AY8910, 64, 0, 16, DIV_SYSTEM_AY8910, 64, 0, 16,
@ -431,6 +649,15 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + Darky", {
DIV_SYSTEM_AY8910, 64, 0, 16,
DIV_SYSTEM_AY8930, 64, 0, 139, // 3.58MHz
DIV_SYSTEM_AY8930, 64, 0, 139, // 3.58MHz or 3.6MHz selectable via register
// per-channel mixer (soft panning, post processing) isn't emulated at all
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"MSX + SCC", { "MSX + SCC", {
DIV_SYSTEM_AY8910, 64, 0, 16, DIV_SYSTEM_AY8910, 64, 0, 16,
@ -445,6 +672,30 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"NEC PC-98 (with PC-9801-26K)", {
DIV_SYSTEM_OPN, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NEC PC-98 (with PC-9801-26K; extended channel 3)", {
DIV_SYSTEM_OPN_EXT, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NEC PC-98 (with PC-9801-86)", {
DIV_SYSTEM_PC98, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NEC PC-98 (with PC-9801-86; extended channel 3)", {
DIV_SYSTEM_PC98_EXT, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"ZX Spectrum (48K)", { "ZX Spectrum (48K)", {
DIV_SYSTEM_AY8910, 64, 0, 2, DIV_SYSTEM_AY8910, 64, 0, 2,
@ -605,13 +856,13 @@ void FurnaceGUI::initSystemPresets() {
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Commander X16", { "Commander X16", {
DIV_SYSTEM_VERA, 64, 0, 0, DIV_SYSTEM_VERA, 64, 0, 0,
DIV_SYSTEM_YM2151, 64, 0, 0, DIV_SYSTEM_YM2151, 32, 0, 0,
0 0
} }
)); ));
sysCategories.push_back(cat); sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Arcade systems"); cat=FurnaceGUISysCategory("Arcade systems","INSERT COIN");
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Bally Midway MCR", { "Bally Midway MCR", {
DIV_SYSTEM_AY8910, 64, 0, 0, DIV_SYSTEM_AY8910, 64, 0, 0,
@ -693,7 +944,7 @@ void FurnaceGUI::initSystemPresets() {
)); ));
sysCategories.push_back(cat); sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("DefleMask-compatible"); cat=FurnaceGUISysCategory("DefleMask-compatible","these configurations are compatible with DefleMask.\nselect this if you need to save as .dmf or work with that program.");
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis", { "Sega Genesis", {
DIV_SYSTEM_YM2612, 64, 0, 0, DIV_SYSTEM_YM2612, 64, 0, 0,

View file

@ -883,6 +883,33 @@ void FurnaceGUI::drawSettings() {
ImGui::Text("FDS core"); ImGui::Text("FDS core");
ImGui::SameLine(); ImGui::SameLine();
ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2);
ImGui::Separator();
ImGui::Text("Sample ROMs:");
ImGui::Text("OPL4 YRW801 path");
ImGui::SameLine();
ImGui::InputText("##YRW801Path",&settings.yrw801Path);
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FOLDER "##YRW801Load")) {
openFileDialog(GUI_FILE_YRW801_ROM_OPEN);
}
ImGui::Text("MultiPCM TG100 path");
ImGui::SameLine();
ImGui::InputText("##TG100Path",&settings.tg100Path);
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FOLDER "##TG100Load")) {
openFileDialog(GUI_FILE_TG100_ROM_OPEN);
}
ImGui::Text("MultiPCM MU5 path");
ImGui::SameLine();
ImGui::InputText("##MU5Path",&settings.mu5Path);
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FOLDER "##MU5Load")) {
openFileDialog(GUI_FILE_MU5_ROM_OPEN);
}
} }
ImGui::EndChild(); ImGui::EndChild();
ImGui::EndTabItem(); ImGui::EndTabItem();
@ -951,6 +978,19 @@ void FurnaceGUI::drawSettings() {
); );
} }
bool loadChineseB=settings.loadChinese;
if (ImGui::Checkbox("Display Chinese (Simplified) characters",&loadChineseB)) {
settings.loadChinese=loadChineseB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Only toggle this option if you have enough graphics memory.\n"
"This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n"
"请在确保你有足够的显存后再启动此设定\n"
"这是一个在ImGui实现动态字体加载之前的临时解决方案"
);
}
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Orders row number format:"); ImGui::Text("Orders row number format:");
@ -1147,6 +1187,11 @@ void FurnaceGUI::drawSettings() {
settings.sampleLayout=sampleLayoutB; settings.sampleLayout=sampleLayoutB;
} }
bool oldMacroVSliderB=settings.oldMacroVSlider;
if (ImGui::Checkbox("Use classic macro editor vertical slider",&oldMacroVSliderB)) {
settings.oldMacroVSlider=oldMacroVSliderB;
}
bool roundedWindowsB=settings.roundedWindows; bool roundedWindowsB=settings.roundedWindows;
if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) { if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) {
settings.roundedWindows=roundedWindowsB; settings.roundedWindows=roundedWindowsB;
@ -1450,6 +1495,7 @@ void FurnaceGUI::drawSettings() {
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST);
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT);
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SONG_INFO); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SONG_INFO);
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SUBSONGS);
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN);
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_LIST); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_LIST);
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT);
@ -1784,6 +1830,9 @@ void FurnaceGUI::syncSettings() {
settings.saaCore=e->getConfInt("saaCore",1); settings.saaCore=e->getConfInt("saaCore",1);
settings.nesCore=e->getConfInt("nesCore",0); settings.nesCore=e->getConfInt("nesCore",0);
settings.fdsCore=e->getConfInt("fdsCore",0); settings.fdsCore=e->getConfInt("fdsCore",0);
settings.yrw801Path=e->getConfString("yrw801Path","");
settings.tg100Path=e->getConfString("tg100Path","");
settings.mu5Path=e->getConfString("mu5Path","");
settings.mainFont=e->getConfInt("mainFont",0); settings.mainFont=e->getConfInt("mainFont",0);
settings.patFont=e->getConfInt("patFont",0); settings.patFont=e->getConfInt("patFont",0);
settings.mainFontPath=e->getConfString("mainFontPath",""); settings.mainFontPath=e->getConfString("mainFontPath","");
@ -1820,6 +1869,7 @@ void FurnaceGUI::syncSettings() {
settings.roundedButtons=e->getConfInt("roundedButtons",1); settings.roundedButtons=e->getConfInt("roundedButtons",1);
settings.roundedMenus=e->getConfInt("roundedMenus",0); settings.roundedMenus=e->getConfInt("roundedMenus",0);
settings.loadJapanese=e->getConfInt("loadJapanese",0); settings.loadJapanese=e->getConfInt("loadJapanese",0);
settings.loadChinese=e->getConfInt("loadChinese",0);
settings.fmLayout=e->getConfInt("fmLayout",0); settings.fmLayout=e->getConfInt("fmLayout",0);
settings.sampleLayout=e->getConfInt("sampleLayout",0); settings.sampleLayout=e->getConfInt("sampleLayout",0);
settings.waveLayout=e->getConfInt("waveLayout",0); settings.waveLayout=e->getConfInt("waveLayout",0);
@ -1849,6 +1899,7 @@ void FurnaceGUI::syncSettings() {
settings.hiddenSystems=e->getConfInt("hiddenSystems",0); settings.hiddenSystems=e->getConfInt("hiddenSystems",0);
settings.horizontalDataView=e->getConfInt("horizontalDataView",0); settings.horizontalDataView=e->getConfInt("horizontalDataView",0);
settings.noMultiSystem=e->getConfInt("noMultiSystem",0); settings.noMultiSystem=e->getConfInt("noMultiSystem",0);
settings.oldMacroVSlider=e->getConfInt("oldMacroVSlider",0);
clampSetting(settings.mainFontSize,2,96); clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96); clampSetting(settings.patFontSize,2,96);
@ -1895,6 +1946,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.roundedButtons,0,1); clampSetting(settings.roundedButtons,0,1);
clampSetting(settings.roundedMenus,0,1); clampSetting(settings.roundedMenus,0,1);
clampSetting(settings.loadJapanese,0,1); clampSetting(settings.loadJapanese,0,1);
clampSetting(settings.loadChinese,0,1);
clampSetting(settings.fmLayout,0,3); clampSetting(settings.fmLayout,0,3);
clampSetting(settings.susPosition,0,1); clampSetting(settings.susPosition,0,1);
clampSetting(settings.effectCursorDir,0,2); clampSetting(settings.effectCursorDir,0,2);
@ -1920,7 +1972,8 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.moveWindowTitle,0,1); clampSetting(settings.moveWindowTitle,0,1);
clampSetting(settings.hiddenSystems,0,1); clampSetting(settings.hiddenSystems,0,1);
clampSetting(settings.horizontalDataView,0,1); clampSetting(settings.horizontalDataView,0,1);
clampSetting(settings.noMultiSystem,0,1) clampSetting(settings.noMultiSystem,0,1);
clampSetting(settings.oldMacroVSlider,0,1);
settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys",""));
if (settings.initialSys.size()<4) { if (settings.initialSys.size()<4) {
@ -1953,6 +2006,10 @@ void FurnaceGUI::syncSettings() {
} }
void FurnaceGUI::commitSettings() { void FurnaceGUI::commitSettings() {
bool sampleROMsChanged = settings.yrw801Path!=e->getConfString("yrw801Path","") ||
settings.tg100Path!=e->getConfString("tg100Path","") ||
settings.mu5Path!=e->getConfString("mu5Path","");
e->setConf("mainFontSize",settings.mainFontSize); e->setConf("mainFontSize",settings.mainFontSize);
e->setConf("patFontSize",settings.patFontSize); e->setConf("patFontSize",settings.patFontSize);
e->setConf("iconSize",settings.iconSize); e->setConf("iconSize",settings.iconSize);
@ -1968,6 +2025,9 @@ void FurnaceGUI::commitSettings() {
e->setConf("saaCore",settings.saaCore); e->setConf("saaCore",settings.saaCore);
e->setConf("nesCore",settings.nesCore); e->setConf("nesCore",settings.nesCore);
e->setConf("fdsCore",settings.fdsCore); e->setConf("fdsCore",settings.fdsCore);
e->setConf("yrw801Path",settings.yrw801Path);
e->setConf("tg100Path",settings.tg100Path);
e->setConf("mu5Path",settings.mu5Path);
e->setConf("mainFont",settings.mainFont); e->setConf("mainFont",settings.mainFont);
e->setConf("patFont",settings.patFont); e->setConf("patFont",settings.patFont);
e->setConf("mainFontPath",settings.mainFontPath); e->setConf("mainFontPath",settings.mainFontPath);
@ -2004,6 +2064,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("roundedButtons",settings.roundedButtons); e->setConf("roundedButtons",settings.roundedButtons);
e->setConf("roundedMenus",settings.roundedMenus); e->setConf("roundedMenus",settings.roundedMenus);
e->setConf("loadJapanese",settings.loadJapanese); e->setConf("loadJapanese",settings.loadJapanese);
e->setConf("loadChinese",settings.loadChinese);
e->setConf("fmLayout",settings.fmLayout); e->setConf("fmLayout",settings.fmLayout);
e->setConf("sampleLayout",settings.sampleLayout); e->setConf("sampleLayout",settings.sampleLayout);
e->setConf("waveLayout",settings.waveLayout); e->setConf("waveLayout",settings.waveLayout);
@ -2034,6 +2095,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("initialSys",e->encodeSysDesc(settings.initialSys)); e->setConf("initialSys",e->encodeSysDesc(settings.initialSys));
e->setConf("horizontalDataView",settings.horizontalDataView); e->setConf("horizontalDataView",settings.horizontalDataView);
e->setConf("noMultiSystem",settings.noMultiSystem); e->setConf("noMultiSystem",settings.noMultiSystem);
e->setConf("oldMacroVSlider",settings.oldMacroVSlider);
// colors // colors
for (int i=0; i<GUI_COLOR_MAX; i++) { for (int i=0; i<GUI_COLOR_MAX; i++) {
@ -2055,6 +2117,12 @@ void FurnaceGUI::commitSettings() {
e->saveConf(); e->saveConf();
if (sampleROMsChanged) {
if (e->loadSampleROMs()) {
showError(e->getLastError());
}
}
if (!e->switchMaster()) { if (!e->switchMaster()) {
showError("could not initialize audio!"); showError("could not initialize audio!");
} }
@ -2587,6 +2655,9 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
if (settings.loadJapanese) { if (settings.loadJapanese) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesJapanese()); range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesJapanese());
} }
if (settings.loadChinese) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon());
}
// I'm terribly sorry // I'm terribly sorry
range.UsedChars[0x80>>5]=0; range.UsedChars[0x80>>5]=0;

View file

@ -18,6 +18,7 @@
*/ */
#include "gui.h" #include "gui.h"
#include "imgui.h"
#include "misc/cpp/imgui_stdlib.h" #include "misc/cpp/imgui_stdlib.h"
#include "intConst.h" #include "intConst.h"
@ -74,28 +75,28 @@ void FurnaceGUI::drawSongInfo() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
float avail=ImGui::GetContentRegionAvail().x; float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
unsigned char realTB=e->song.timeBase+1; unsigned char realTB=e->curSubSong->timeBase+1;
if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED
if (realTB<1) realTB=1; if (realTB<1) realTB=1;
if (realTB>16) realTB=16; if (realTB>16) realTB=16;
e->song.timeBase=realTB-1; e->curSubSong->timeBase=realTB-1;
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%.2f BPM",calcBPM(e->song.speed1,e->song.speed2,e->song.hz)); ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speed1,e->curSubSong->speed2,e->curSubSong->hz));
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Speed"); ImGui::Text("Speed");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speed1,&_ONE,&_THREE)) { MARK_MODIFIED
if (e->song.speed1<1) e->song.speed1=1; if (e->curSubSong->speed1<1) e->curSubSong->speed1=1;
if (e->isPlaying()) play(); if (e->isPlaying()) play();
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speed2,&_ONE,&_THREE)) { MARK_MODIFIED
if (e->song.speed2<1) e->song.speed2=1; if (e->curSubSong->speed2<1) e->curSubSong->speed2=1;
if (e->isPlaying()) play(); if (e->isPlaying()) play();
} }
@ -104,12 +105,12 @@ void FurnaceGUI::drawSongInfo() {
ImGui::Text("Highlight"); ImGui::Text("Highlight");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE)) { if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) {
MARK_MODIFIED; MARK_MODIFIED;
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE)) { if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) {
MARK_MODIFIED; MARK_MODIFIED;
} }
@ -118,11 +119,11 @@ void FurnaceGUI::drawSongInfo() {
ImGui::Text("Pattern Length"); ImGui::Text("Pattern Length");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
int patLen=e->song.patLen; int patLen=e->curSubSong->patLen;
if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED
if (patLen<1) patLen=1; if (patLen<1) patLen=1;
if (patLen>256) patLen=256; if (patLen>256) patLen=256;
e->song.patLen=patLen; e->curSubSong->patLen=patLen;
} }
ImGui::TableNextRow(); ImGui::TableNextRow();
@ -130,11 +131,11 @@ void FurnaceGUI::drawSongInfo() {
ImGui::Text("Song Length"); ImGui::Text("Song Length");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
int ordLen=e->song.ordersLen; int ordLen=e->curSubSong->ordersLen;
if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED
if (ordLen<1) ordLen=1; if (ordLen<1) ordLen=1;
if (ordLen>256) ordLen=256; if (ordLen>256) ordLen=256;
e->song.ordersLen=ordLen; e->curSubSong->ordersLen=ordLen;
if (curOrder>=ordLen) { if (curOrder>=ordLen) {
setOrder(ordLen-1); setOrder(ordLen-1);
} }
@ -147,7 +148,7 @@ void FurnaceGUI::drawSongInfo() {
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail); ImGui::SetNextItemWidth(avail);
float setHz=tempoView?e->song.hz*2.5:e->song.hz; 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,1.0f,"%g")) { MARK_MODIFIED
if (tempoView) setHz/=2.5; if (tempoView) setHz/=2.5;
if (setHz<10) setHz=10; if (setHz<10) setHz=10;
@ -156,13 +157,13 @@ void FurnaceGUI::drawSongInfo() {
} }
if (tempoView) { if (tempoView) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("= %gHz",e->song.hz); ImGui::Text("= %gHz",e->curSubSong->hz);
} else { } else {
if (e->song.hz>=49.98 && e->song.hz<=50.02) { if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("PAL"); ImGui::Text("PAL");
} }
if (e->song.hz>=59.9 && e->song.hz<=60.11) { if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("NTSC"); ImGui::Text("NTSC");
} }

94
src/gui/subSongs.cpp Normal file
View file

@ -0,0 +1,94 @@
#include "gui.h"
#include "imgui.h"
#include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h"
void FurnaceGUI::drawSubSongs() {
if (nextWindow==GUI_WINDOW_SUBSONGS) {
subSongsOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!subSongsOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
if (ImGui::Begin("Subsongs",&subSongsOpen,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
char id[1024];
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0f-ImGui::GetStyle().ItemSpacing.x);
if (e->curSubSong->name.empty()) {
snprintf(id,1023,"%d. <no name>",(int)e->getCurrentSubSong()+1);
} else {
snprintf(id,1023,"%d. %s",(int)e->getCurrentSubSong()+1,e->curSubSong->name.c_str());
}
if (ImGui::BeginCombo("##SubSong",id)) {
for (size_t i=0; i<e->song.subsong.size(); i++) {
if (e->song.subsong[i]->name.empty()) {
snprintf(id,1023,"%d. <no name>",(int)i+1);
} else {
snprintf(id,1023,"%d. %s",(int)i+1,e->song.subsong[i]->name.c_str());
}
if (ImGui::Selectable(id,i==e->getCurrentSubSong())) {
e->changeSongP(i);
updateScroll(0);
oldOrder=0;
oldOrder1=0;
oldRow=0;
cursor.xCoarse=0;
cursor.xFine=0;
cursor.y=0;
selStart=cursor;
selEnd=cursor;
curOrder=0;
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_PLUS "##SubSongAdd")) {
if (!e->addSubSong()) {
showError("too many subsongs!");
} else {
e->changeSongP(e->song.subsong.size()-1);
updateScroll(0);
oldOrder=0;
oldOrder1=0;
oldRow=0;
cursor.xCoarse=0;
cursor.xFine=0;
cursor.y=0;
selStart=cursor;
selEnd=cursor;
curOrder=0;
MARK_MODIFIED;
}
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_MINUS "##SubSongDel")) {
if (!e->removeSubSong(e->getCurrentSubSong())) {
showError("this is the only subsong!");
} else {
undoHist.clear();
redoHist.clear();
updateScroll(0);
oldOrder=0;
oldOrder1=0;
oldRow=0;
cursor.xCoarse=0;
cursor.xFine=0;
cursor.y=0;
selStart=cursor;
selEnd=cursor;
curOrder=0;
MARK_MODIFIED;
}
}
ImGui::Text("Name");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputText("##SubSongName",&e->curSubSong->name)) {
MARK_MODIFIED;
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SUBSONGS;
ImGui::End();
}

View file

@ -168,13 +168,13 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) {
copyOfFlags=(flags&(~15))|5; copyOfFlags=(flags&(~15))|5;
} }
if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { if (ImGui::RadioButton("0.89MHz (Pre-divided Sunsoft 5B)",(flags&15)==6)) {
copyOfFlags=(flags&(~15))|6; copyOfFlags=(flags&(~15))|6;
} }
if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) {
copyOfFlags=(flags&(~15))|7; copyOfFlags=(flags&(~15))|7;
} }
if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { if (ImGui::RadioButton("0.83MHz (Pre-divided Sunsoft 5B on PAL)",(flags&15)==8)) {
copyOfFlags=(flags&(~15))|8; copyOfFlags=(flags&(~15))|8;
} }
if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) { if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) {
@ -183,6 +183,14 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) { if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) {
copyOfFlags=(flags&(~15))|10; copyOfFlags=(flags&(~15))|10;
} }
if (ImGui::RadioButton("3.58MHz (Darky)",(flags&15)==11)) {
copyOfFlags=(flags&(~15))|11;
}
if (ImGui::RadioButton("3.6MHz (Darky)",(flags&15)==12)) {
copyOfFlags=(flags&(~15))|12;
}
if (type==DIV_SYSTEM_AY8910) { if (type==DIV_SYSTEM_AY8910) {
ImGui::Text("Chip type:"); ImGui::Text("Chip type:");
if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) {
@ -199,11 +207,18 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
} }
} }
bool stereo=flags&0x40; bool stereo=flags&0x40;
ImGui::BeginDisabled((flags&0x30)==32); ImGui::BeginDisabled((type==DIV_SYSTEM_AY8910) && ((flags&0x30)==32));
if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) {
copyOfFlags=(flags&(~0x40))|(stereo?0x40:0); copyOfFlags=(flags&(~0x40))|(stereo?0x40:0);
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
bool clockSel=flags&0x80;
ImGui::BeginDisabled((type==DIV_SYSTEM_AY8910) && ((flags&0x30)!=16));
if (ImGui::Checkbox("Half Clock divider##_AY_CLKSEL",&clockSel)) {
copyOfFlags=(flags&(~0x80))|(clockSel?0x80:0);
}
ImGui::EndDisabled();
break; break;
} }
case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_SAA1099:
@ -322,6 +337,21 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
} rightClickable } rightClickable
break; break;
} }
case DIV_SYSTEM_OPN: {
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) {
copyOfFlags=(flags&0x80000000)|0;
}
if (ImGui::RadioButton("PAL (3.54MHz)",(flags&3)==1)) {
copyOfFlags=(flags&0x80000000)|1;
}
if (ImGui::RadioButton("Arcade (4MHz)",(flags&3)==2)) {
copyOfFlags=(flags&0x80000000)|2;
}
if (ImGui::RadioButton("PC-9801-26K? TODO: CONFIRM (3MHz)",(flags&3)==3)) {
copyOfFlags=(flags&0x80000000)|3;
}
break;
}
case DIV_SYSTEM_GB: case DIV_SYSTEM_GB:
case DIV_SYSTEM_SWAN: case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_VERA: case DIV_SYSTEM_VERA: