diff --git a/src/boards/69.c b/src/boards/69.c index 3907b57..e29d4f0 100644 --- a/src/boards/69.c +++ b/src/boards/69.c @@ -27,6 +27,8 @@ static int32 IRQCount; static uint8 *WRAM = NULL; static uint32 WRAMSIZE; +static void(*sfun[3]) (void); + static SFORMAT StateRegs[] = { { &cmdreg, 1, "CMDR" }, @@ -41,7 +43,7 @@ static SFORMAT StateRegs[] = static void Sync(void) { uint8 i; if ((preg[3] & 0xC0) == 0xC0) - setprg8r(0x10, 0x6000, 0); + setprg8r(0x10, 0x6000, preg[3] & 0x3F); else setprg8(0x6000, preg[3] & 0x3F); setprg8(0x8000, preg[0]); @@ -89,16 +91,13 @@ static DECLFW(M69Write1) { case 0xA: preg[1] = V; Sync(); break; case 0xB: preg[2] = V; Sync(); break; case 0xC: mirr = V & 3; Sync();break; - /* 17/10/17- reg $0D should awknowledge IRQ and no other - * http://forums.nesdev.com/viewtopic.php?f=2&t=12436&start=15 - */ case 0xD: IRQa = V; X6502_IRQEnd(FCEU_IQEXT); break; case 0xE: IRQCount &= 0xFF00; IRQCount |= V; break; case 0xF: IRQCount &= 0x00FF; IRQCount |= V << 8; break; } } -/* SUNSOFT-5/FME-7 Sound */ +// SUNSOFT-5/FME-7 Sound static void AYSound(int Count); static void AYSoundHQ(void); @@ -114,6 +113,15 @@ static SFORMAT SStateRegs[] = { { &sndcmd, 1, "SCMD" }, { sreg, 14, "SREG" }, + { &dcount[0], 4 | FCEUSTATE_RLSB, "DCT0" }, + { &dcount[1], 4 | FCEUSTATE_RLSB, "DCT1" }, + { &dcount[2], 4 | FCEUSTATE_RLSB, "DCT2" }, + { &vcount[0], 4 | FCEUSTATE_RLSB, "VCT0" }, + { &vcount[1], 4 | FCEUSTATE_RLSB, "VCT1" }, + { &vcount[2], 4 | FCEUSTATE_RLSB, "VCT2" }, + { &CAYBC[0], 4 | FCEUSTATE_RLSB, "BC00" }, + { &CAYBC[1], 4 | FCEUSTATE_RLSB, "BC01" }, + { &CAYBC[2], 4 | FCEUSTATE_RLSB, "BC02" }, { 0 } }; @@ -125,22 +133,21 @@ static DECLFW(M69SWrite1) { int x; GameExpSound.Fill = AYSound; GameExpSound.HiFill = AYSoundHQ; - if (FSettings.SndRate) - switch (sndcmd) { - case 0: - case 1: - case 8: if (FSettings.soundq >= 1) DoAYSQHQ(0); else DoAYSQ(0); break; - case 2: - case 3: - case 9: if (FSettings.soundq >= 1) DoAYSQHQ(1); else DoAYSQ(1); break; - case 4: - case 5: - case 10: if (FSettings.soundq >= 1) DoAYSQHQ(2); else DoAYSQ(2); break; - case 7: - for (x = 0; x < 2; x++) - if (FSettings.soundq >= 1) DoAYSQHQ(x); else DoAYSQ(x); - break; - } + switch (sndcmd) { + case 0: + case 1: + case 8: if (sfun[0]) sfun[0](); break; + case 2: + case 3: + case 9: if (sfun[1]) sfun[1](); break; + case 4: + case 5: + case 10: if (sfun[2]) sfun[2](); break; + case 7: + if (sfun[0]) sfun[0](); + if (sfun[1]) sfun[1](); + break; + } sreg[sndcmd] = V; } @@ -190,19 +197,43 @@ static void DoAYSQHQ(int x) { CAYBC[x] = SOUNDTS; } +static void DoAYSQ1(void) { + DoAYSQ(0); +} + +static void DoAYSQ2(void) { + DoAYSQ(1); +} + +static void DoAYSQ3(void) { + DoAYSQ(2); +} + +static void DoAYSQ1HQ(void) { + DoAYSQHQ(0); +} + +static void DoAYSQ2HQ(void) { + DoAYSQHQ(1); +} + +static void DoAYSQ3HQ(void) { + DoAYSQHQ(2); +} + static void AYSound(int Count) { int x; - DoAYSQ(0); - DoAYSQ(1); - DoAYSQ(2); + DoAYSQ1(); + DoAYSQ2(); + DoAYSQ3(); for (x = 0; x < 3; x++) CAYBC[x] = Count; } static void AYSoundHQ(void) { - DoAYSQHQ(0); - DoAYSQHQ(1); - DoAYSQHQ(2); + DoAYSQ1HQ(); + DoAYSQ2HQ(); + DoAYSQ3HQ(); } static void AYHiSync(int32 ts) { @@ -218,6 +249,18 @@ void Mapper69_ESI(void) { memset(dcount, 0, sizeof(dcount)); memset(vcount, 0, sizeof(vcount)); memset(CAYBC, 0, sizeof(CAYBC)); + if (FSettings.SndRate) { + if (FSettings.soundq >= 1) { + sfun[0] = DoAYSQ1HQ; + sfun[1] = DoAYSQ2HQ; + sfun[2] = DoAYSQ3HQ; + } else { + sfun[0] = DoAYSQ1; + sfun[1] = DoAYSQ2; + sfun[2] = DoAYSQ3; + } + } else + memset(sfun, 0, sizeof(sfun)); } /* SUNSOFT-5/FME-7 Sound */ @@ -270,8 +313,8 @@ void Mapper69_Init(CartInfo *info) { } GameStateRestore = StateRestore; Mapper69_ESI(); - AddExState(&SStateRegs, ~0, 0, 0); AddExState(&StateRegs, ~0, 0, 0); + AddExState(&SStateRegs, ~0, 0, 0); } void NSFAY_Init(void) { diff --git a/src/boards/n106.c b/src/boards/n106.c index f786a57..e5e8473 100644 --- a/src/boards/n106.c +++ b/src/boards/n106.c @@ -224,6 +224,28 @@ static int32 CVBC; #define TOINDEX (16 + 1) +static SFORMAT N106_SStateRegs[] = +{ + { &PlayIndex[0], 4 | FCEUSTATE_RLSB, "IDX0" }, + { &PlayIndex[1], 4 | FCEUSTATE_RLSB, "IDX1" }, + { &PlayIndex[2], 4 | FCEUSTATE_RLSB, "IDX2" }, + { &PlayIndex[3], 4 | FCEUSTATE_RLSB, "IDX3" }, + { &PlayIndex[4], 4 | FCEUSTATE_RLSB, "IDX4" }, + { &PlayIndex[5], 4 | FCEUSTATE_RLSB, "IDX5" }, + { &PlayIndex[6], 4 | FCEUSTATE_RLSB, "IDX6" }, + { &PlayIndex[7], 4 | FCEUSTATE_RLSB, "IDX7" }, + { &vcount[0], 4 | FCEUSTATE_RLSB, "VCT0" }, + { &vcount[1], 4 | FCEUSTATE_RLSB, "VCT1" }, + { &vcount[2], 4 | FCEUSTATE_RLSB, "VCT2" }, + { &vcount[3], 4 | FCEUSTATE_RLSB, "VCT3" }, + { &vcount[4], 4 | FCEUSTATE_RLSB, "VCT4" }, + { &vcount[5], 4 | FCEUSTATE_RLSB, "VCT5" }, + { &vcount[6], 4 | FCEUSTATE_RLSB, "VCT6" }, + { &vcount[7], 4 | FCEUSTATE_RLSB, "VCT7" }, + { &CVBC, 4 | FCEUSTATE_RLSB, "BC00" }, + { 0 } +}; + /* 16:15 */ static void SyncHQ(int32 ts) { CVBC = ts; @@ -404,6 +426,7 @@ void Mapper19_Init(CartInfo *info) { AddExState(WRAM, 8192, 0, "WRAM"); AddExState(IRAM, 128, 0, "IRAM"); AddExState(N106_StateRegs, ~0, 0, 0); + AddExState(N106_SStateRegs, ~0, 0, 0); if (info->battery) { info->SaveGame[0] = WRAM; diff --git a/src/boards/vrc6.c b/src/boards/vrc6.c index c8772c7..e133e36 100644 --- a/src/boards/vrc6.c +++ b/src/boards/vrc6.c @@ -44,16 +44,32 @@ static SFORMAT StateRegs[] = }; static void(*sfun[3]) (void); + static uint8 vpsg1[8]; static uint8 vpsg2[4]; static int32 cvbc[3]; static int32 vcount[3]; static int32 dcount[2]; +static int32 saw1phaseacc; +static int32 phaseacc; +static uint8 b3; /* clock counter, resets phaseacc on the 7th clock */ static SFORMAT SStateRegs[] = { { vpsg1, 8, "PSG1" }, { vpsg2, 4, "PSG2" }, + /* rw - 2018-11-28 Added */ + { &cvbc[0], 4 | FCEUSTATE_RLSB, "BC01" }, + { &cvbc[1], 4 | FCEUSTATE_RLSB, "BC02" }, + { &cvbc[2], 4 | FCEUSTATE_RLSB, "BC03" }, + { &dcount[0], 4 | FCEUSTATE_RLSB, "DCT0" }, + { &dcount[1], 4 | FCEUSTATE_RLSB, "DCT1" }, + { &vcount[0], 4 | FCEUSTATE_RLSB, "VCT0" }, + { &vcount[1], 4 | FCEUSTATE_RLSB, "VCT1" }, + { &vcount[2], 4 | FCEUSTATE_RLSB, "VCT1" }, + { &saw1phaseacc, 4| FCEUSTATE_RLSB, "SAW1" }, + { &phaseacc, 4 | FCEUSTATE_RLSB, "PACC" }, + { &b3, 1, "CLKC" }, { 0 } }; @@ -177,15 +193,20 @@ static INLINE void DoSQV(int x) { } else { int32 thresh = (vpsg1[x << 2] >> 4) & 7; int32 freq = ((vpsg1[(x << 2) | 0x1] | ((vpsg1[(x << 2) | 0x2] & 15) << 8)) + 1) << 17; + int32 dc = dcount[x]; + int32 vc = vcount[x]; + for (V = start; V < end; V++) { - if (dcount[x] > thresh) + if (dc > thresh) Wave[V >> 4] += amp; - vcount[x] -= nesincsize; - while (vcount[x] <= 0) { - vcount[x] += freq; - dcount[x] = (dcount[x] + 1) & 15; + vc -= nesincsize; + while (vc <= 0) { + vc += freq; + dc = (dc + 1) & 15; } } + vcount[x] = vc; + dcount[x] = dc; } } } @@ -208,10 +229,7 @@ static void DoSawV(void) { cvbc[2] = end; if (vpsg2[2] & 0x80) { - static int32 saw1phaseacc = 0; uint32 freq3; - static uint8 b3 = 0; - static int32 phaseacc = 0; static uint32 duff = 0; freq3 = (vpsg2[1] + ((vpsg2[2] & 15) << 8) + 1); @@ -249,15 +267,20 @@ static INLINE void DoSQVHQ(int x) { WaveHi[V] += amp; } else { int32 thresh = (vpsg1[x << 2] >> 4) & 7; + int32 dc = dcount[x]; + int32 vc = vcount[x]; + for (V = cvbc[x]; V < (int)SOUNDTS; V++) { - if (dcount[x] > thresh) + if (dc > thresh) WaveHi[V] += amp; - vcount[x]--; - if (vcount[x] <= 0) { - vcount[x] = (vpsg1[(x << 2) | 0x1] | ((vpsg1[(x << 2) | 0x2] & 15) << 8)) + 1; - dcount[x] = (dcount[x] + 1) & 15; + vc--; + if (vc <= 0) { + vc = (vpsg1[(x << 2) | 0x1] | ((vpsg1[(x << 2) | 0x2] & 15) << 8)) + 1; + dc = (dc + 1) & 15; } } + dcount[x] = dc; + vcount[x] = vc; } } cvbc[x] = SOUNDTS; @@ -272,8 +295,6 @@ static void DoSQV2HQ(void) { } static void DoSawVHQ(void) { - static uint8 b3 = 0; - static int32 phaseacc = 0; int32 V; if (vpsg2[2] & 0x80) { diff --git a/src/drivers/libretro/libretro-common/include/libretro.h b/src/drivers/libretro/libretro-common/include/libretro.h index 7d01185..4e1a978 100644 --- a/src/drivers/libretro/libretro-common/include/libretro.h +++ b/src/drivers/libretro/libretro-common/include/libretro.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2017 The RetroArch team +/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro API header (libretro.h). @@ -128,7 +128,8 @@ extern "C" { /* LIGHTGUN device is similar to Guncon-2 for PlayStation 2. * It reports X/Y coordinates in screen space (similar to the pointer) - * in the range [-0x8000, 0x7fff] in both axes, with zero being center. + * in the range [-0x8000, 0x7fff] in both axes, with zero being center and + * -0x8000 being out of bounds. * As well as reporting on/off screen state. It features a trigger, * start/select buttons, auxiliary action buttons and a * directional pad. A forced off-screen shot can be requested for @@ -139,7 +140,8 @@ extern "C" { /* The ANALOG device is an extension to JOYPAD (RetroPad). * Similar to DualShock2 it adds two analog sticks and all buttons can * be analog. This is treated as a separate device type as it returns - * axis values in the full analog range of [-0x8000, 0x7fff]. + * axis values in the full analog range of [-0x7fff, 0x7fff], + * although some devices may return -0x8000. * Positive X axis is right. Positive Y axis is down. * Buttons are returned in the range [0, 0x7fff]. * Only use ANALOG type when polling for analog values. @@ -271,6 +273,7 @@ enum retro_language RETRO_LANGUAGE_POLISH = 14, RETRO_LANGUAGE_VIETNAMESE = 15, RETRO_LANGUAGE_ARABIC = 16, + RETRO_LANGUAGE_GREEK = 17, RETRO_LANGUAGE_LAST, /* Ensure sizeof(enum) == sizeof(int) */ @@ -450,6 +453,7 @@ enum retro_key RETROK_POWER = 320, RETROK_EURO = 321, RETROK_UNDO = 322, + RETROK_OEM_102 = 323, RETROK_LAST, @@ -599,9 +603,12 @@ enum retro_mod * GET_VARIABLE. * This allows the frontend to present these variables to * a user dynamically. - * This should be called as early as possible (ideally in - * retro_set_environment). - * + * This should be called the first time as early as + * possible (ideally in retro_set_environment). + * Afterward it may be called again for the core to communicate + * updated options to the frontend, but the number of core + * options must not change from the number in the initial call. + * * 'data' points to an array of retro_variable structs * terminated by a { NULL, NULL } element. * retro_variable::key should be namespaced to not collide @@ -654,6 +661,15 @@ enum retro_mod /* Environment 20 was an obsolete version of SET_AUDIO_CALLBACK. * It was not used by any known core at the time, * and was removed from the API. */ +#define RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK 21 + /* const struct retro_frame_time_callback * -- + * Lets the core know how much time has passed since last + * invocation of retro_run(). + * The frontend can tamper with the timing to fake fast-forward, + * slow-motion, frame stepping, etc. + * In this case the delta time will use the reference value + * in frame_time_callback.. + */ #define RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK 22 /* const struct retro_audio_callback * -- * Sets an interface which is used to notify a libretro core about audio @@ -680,15 +696,6 @@ enum retro_mod * A libretro core using SET_AUDIO_CALLBACK should also make use of * SET_FRAME_TIME_CALLBACK. */ -#define RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK 21 - /* const struct retro_frame_time_callback * -- - * Lets the core know how much time has passed since last - * invocation of retro_run(). - * The frontend can tamper with the timing to fake fast-forward, - * slow-motion, frame stepping, etc. - * In this case the delta time will use the reference value - * in frame_time_callback.. - */ #define RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE 23 /* struct retro_rumble_interface * -- * Gets an interface which is used by a libretro core to set @@ -775,17 +782,18 @@ enum retro_mod */ #define RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY 31 /* const char ** -- - * Returns the "save" directory of the frontend. - * This directory can be used to store SRAM, memory cards, - * high scores, etc, if the libretro core + * Returns the "save" directory of the frontend, unless there is no + * save directory available. The save directory should be used to + * store SRAM, memory cards, high scores, etc, if the libretro core * cannot use the regular memory interface (retro_get_memory_data()). * - * NOTE: libretro cores used to check GET_SYSTEM_DIRECTORY for - * similar things before. - * They should still check GET_SYSTEM_DIRECTORY if they want to - * be backwards compatible. - * The path here can be NULL. It should only be non-NULL if the - * frontend user has set a specific save path. + * If the frontend cannot designate a save directory, it will return + * NULL to indicate that the core should attempt to operate without a + * save directory set. + * + * NOTE: early libretro cores used the system directory for save + * files. Cores that need to be backwards-compatible can still check + * GET_SYSTEM_DIRECTORY. */ #define RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO 32 /* const struct retro_system_av_info * -- @@ -853,26 +861,39 @@ enum retro_mod #define RETRO_ENVIRONMENT_SET_CONTROLLER_INFO 35 /* const struct retro_controller_info * -- * This environment call lets a libretro core tell the frontend - * which controller types are recognized in calls to + * which controller subclasses are recognized in calls to * retro_set_controller_port_device(). * - * Some emulators such as Super Nintendo - * support multiple lightgun types which must be specifically - * selected from. - * It is therefore sometimes necessary for a frontend to be able - * to tell the core about a special kind of input device which is - * not covered by the libretro input API. + * Some emulators such as Super Nintendo support multiple lightgun + * types which must be specifically selected from. It is therefore + * sometimes necessary for a frontend to be able to tell the core + * about a special kind of input device which is not specifcally + * provided by the Libretro API. * - * In order for a frontend to understand the workings of an input device, - * it must be a specialized type - * of the generic device types already defined in the libretro API. + * In order for a frontend to understand the workings of those devices, + * they must be defined as a specialized subclass of the generic device + * types already defined in the libretro API. * - * Which devices are supported can vary per input port. * The core must pass an array of const struct retro_controller_info which - * is terminated with a blanked out struct. Each element of the struct - * corresponds to an ascending port index to - * retro_set_controller_port_device(). - * Even if special device types are set in the libretro core, + * is terminated with a blanked out struct. Each element of the + * retro_controller_info struct corresponds to the ascending port index + * that is passed to retro_set_controller_port_device() when that function + * is called to indicate to the core that the frontend has changed the + * active device subclass. SEE ALSO: retro_set_controller_port_device() + * + * The ascending input port indexes provided by the core in the struct + * are generally presented by frontends as ascending User # or Player #, + * such as Player 1, Player 2, Player 3, etc. Which device subclasses are + * supported can vary per input port. + * + * The first inner element of each entry in the retro_controller_info array + * is a retro_controller_description struct that specifies the names and + * codes of all device subclasses that are available for the corresponding + * User or Player, beginning with the generic Libretro device that the + * subclasses are derived from. The second inner element of each entry is the + * total number of subclasses that are listed in the retro_controller_description. + * + * NOTE: Even if special device types are set in the libretro core, * libretro should only poll input based on the base input device types. */ #define RETRO_ENVIRONMENT_SET_MEMORY_MAPS (36 | RETRO_ENVIRONMENT_EXPERIMENTAL) @@ -951,7 +972,37 @@ enum retro_mod * A frontend must make sure that the pointer obtained from this function is * writeable (and readable). */ - +#define RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE (41 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* const struct retro_hw_render_interface ** -- + * Returns an API specific rendering interface for accessing API specific data. + * Not all HW rendering APIs support or need this. + * The contents of the returned pointer is specific to the rendering API + * being used. See the various headers like libretro_vulkan.h, etc. + * + * GET_HW_RENDER_INTERFACE cannot be called before context_reset has been called. + * Similarly, after context_destroyed callback returns, + * the contents of the HW_RENDER_INTERFACE are invalidated. + */ +#define RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS (42 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* const bool * -- + * If true, the libretro implementation supports achievements + * either via memory descriptors set with RETRO_ENVIRONMENT_SET_MEMORY_MAPS + * or via retro_get_memory_data/retro_get_memory_size. + * + * This must be called before the first call to retro_run. + */ +#define RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE (43 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* const struct retro_hw_render_context_negotiation_interface * -- + * Sets an interface which lets the libretro core negotiate with frontend how a context is created. + * The semantics of this interface depends on which API is used in SET_HW_RENDER earlier. + * This interface will be used when the frontend is trying to create a HW rendering context, + * so it will be used after SET_HW_RENDER, but before the context_reset callback. + */ +#define RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS 44 + /* uint64_t * -- + * Sets quirk flags associated with serialization. The frontend will zero any flags it doesn't + * recognize or support. Should be set in either retro_init or retro_load_game, but not both. + */ #define RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT (44 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* N/A (null) * -- * The frontend will try to use a 'shared' hardware context (mostly applicable @@ -963,14 +1014,70 @@ enum retro_mod * This will do nothing on its own until SET_HW_RENDER env callbacks are * being used. */ - #define RETRO_ENVIRONMENT_GET_VFS_INTERFACE (45 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* struct retro_vfs_interface_info * -- * Gets access to the VFS interface. * VFS presence needs to be queried prior to load_game or any * get_system/save/other_directory being called to let front end know * core supports VFS before it starts handing out paths. - * It is recomended to do so in retro_set_environment */ + * It is recomended to do so in retro_set_environment + */ +#define RETRO_ENVIRONMENT_GET_LED_INTERFACE (46 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* struct retro_led_interface * -- + * Gets an interface which is used by a libretro core to set + * state of LEDs. + */ +#define RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE (47 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* int * -- + * Tells the core if the frontend wants audio or video. + * If disabled, the frontend will discard the audio or video, + * so the core may decide to skip generating a frame or generating audio. + * This is mainly used for increasing performance. + * Bit 0 (value 1): Enable Video + * Bit 1 (value 2): Enable Audio + * Bit 2 (value 4): Use Fast Savestates. + * Bit 3 (value 8): Hard Disable Audio + * Other bits are reserved for future use and will default to zero. + * If video is disabled: + * * The frontend wants the core to not generate any video, + * including presenting frames via hardware acceleration. + * * The frontend's video frame callback will do nothing. + * * After running the frame, the video output of the next frame should be + * no different than if video was enabled, and saving and loading state + * should have no issues. + * If audio is disabled: + * * The frontend wants the core to not generate any audio. + * * The frontend's audio callbacks will do nothing. + * * After running the frame, the audio output of the next frame should be + * no different than if audio was enabled, and saving and loading state + * should have no issues. + * Fast Savestates: + * * Guaranteed to be created by the same binary that will load them. + * * Will not be written to or read from the disk. + * * Suggest that the core assumes loading state will succeed. + * * Suggest that the core updates its memory buffers in-place if possible. + * * Suggest that the core skips clearing memory. + * * Suggest that the core skips resetting the system. + * * Suggest that the core may skip validation steps. + * Hard Disable Audio: + * * Used for a secondary core when running ahead. + * * Indicates that the frontend will never need audio from the core. + * * Suggests that the core may stop synthesizing audio, but this should not + * compromise emulation accuracy. + * * Audio output for the next frame does not matter, and the frontend will + * never need an accurate audio state in the future. + * * State will never be saved when using Hard Disable Audio. + */ +#define RETRO_ENVIRONMENT_GET_MIDI_INTERFACE (48 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* struct retro_midi_interface ** -- + * Returns a MIDI interface that can be used for raw data I/O. + */ + +#define RETRO_ENVIRONMENT_GET_FASTFORWARDING (49 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* bool * -- + * Boolean value that indicates whether or not the frontend is in + * fastforwarding mode. + */ /* VFS functionality */ @@ -1030,6 +1137,10 @@ typedef int (RETRO_CALLCONV *retro_vfs_close_t)(struct retro_vfs_file_handle *st * Introduced in VFS API v1 */ typedef int64_t (RETRO_CALLCONV *retro_vfs_size_t)(struct retro_vfs_file_handle *stream); +/* Truncate file to specified size. Returns 0 on success or -1 on error + * Introduced in VFS API v2 */ +typedef int64_t (RETRO_CALLCONV *retro_vfs_truncate_t)(struct retro_vfs_file_handle *stream, int64_t length); + /* Get the current read / write position for the file. Returns - 1 for error. * Introduced in VFS API v1 */ typedef int64_t (RETRO_CALLCONV *retro_vfs_tell_t)(struct retro_vfs_file_handle *stream); @@ -1060,6 +1171,7 @@ typedef int (RETRO_CALLCONV *retro_vfs_rename_t)(const char *old_path, const cha struct retro_vfs_interface { + /* VFS API v1 */ retro_vfs_get_path_t get_path; retro_vfs_open_t open; retro_vfs_close_t close; @@ -1071,6 +1183,8 @@ struct retro_vfs_interface retro_vfs_flush_t flush; retro_vfs_remove_t remove; retro_vfs_rename_t rename; + /* VFS API v2 */ + retro_vfs_truncate_t truncate; }; struct retro_vfs_interface_info @@ -1104,81 +1218,41 @@ struct retro_hw_render_interface unsigned interface_version; }; - -#define RETRO_ENVIRONMENT_GET_LED_INTERFACE (46 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* struct retro_led_interface * -- - * Gets an interface which is used by a libretro core to set - * state of LEDs. - */ - typedef void (RETRO_CALLCONV *retro_set_led_state_t)(int led, int state); struct retro_led_interface { retro_set_led_state_t set_led_state; }; -#define RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE (47 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* int * -- - * Tells the core if the frontend wants audio or video. - * If disabled, the frontend will discard the audio or video, - * so the core may decide to skip generating a frame or generating audio. - * This is mainly used for increasing performance. - * Bit 0 (value 1): Enable Video - * Bit 1 (value 2): Enable Audio - * Bit 2 (value 4): Use Fast Savestates. - * Bit 3 (value 8): Hard Disable Audio - * Other bits are reserved for future use and will default to zero. - * If video is disabled: - * * The frontend wants the core to not generate any video, - * including presenting frames via hardware acceleration. - * * The frontend's video frame callback will do nothing. - * * After running the frame, the video output of the next frame should be - * no different than if video was enabled, and saving and loading state - * should have no issues. - * If audio is disabled: - * * The frontend wants the core to not generate any audio. - * * The frontend's audio callbacks will do nothing. - * * After running the frame, the audio output of the next frame should be - * no different than if audio was enabled, and saving and loading state - * should have no issues. - * Fast Savestates: - * * Guaranteed to be created by the same binary that will load them. - * * Will not be written to or read from the disk. - * * Suggest that the core assumes loading state will succeed. - * * Suggest that the core updates its memory buffers in-place if possible. - * * Suggest that the core skips clearing memory. - * * Suggest that the core skips resetting the system. - * * Suggest that the core may skip validation steps. - * Hard Disable Audio: - * * Used for a secondary core when running ahead. - * * Indicates that the frontend will never need audio from the core. - * * Suggests that the core may stop synthesizing audio, but this should not - * compromise emulation accuracy. - * * Audio output for the next frame does not matter, and the frontend will - * never need an accurate audio state in the future. - * * State will never be saved when using Hard Disable Audio. - */ +/* Retrieves the current state of the MIDI input. + * Returns true if it's enabled, false otherwise. */ +typedef bool (RETRO_CALLCONV *retro_midi_input_enabled_t)(void); -#define RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE (41 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* const struct retro_hw_render_interface ** -- - * Returns an API specific rendering interface for accessing API specific data. - * Not all HW rendering APIs support or need this. - * The contents of the returned pointer is specific to the rendering API - * being used. See the various headers like libretro_vulkan.h, etc. - * - * GET_HW_RENDER_INTERFACE cannot be called before context_reset has been called. - * Similarly, after context_destroyed callback returns, - * the contents of the HW_RENDER_INTERFACE are invalidated. - */ +/* Retrieves the current state of the MIDI output. + * Returns true if it's enabled, false otherwise */ +typedef bool (RETRO_CALLCONV *retro_midi_output_enabled_t)(void); -#define RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS (42 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* const bool * -- - * If true, the libretro implementation supports achievements - * either via memory descriptors set with RETRO_ENVIRONMENT_SET_MEMORY_MAPS - * or via retro_get_memory_data/retro_get_memory_size. - * - * This must be called before the first call to retro_run. - */ +/* Reads next byte from the input stream. + * Returns true if byte is read, false otherwise. */ +typedef bool (RETRO_CALLCONV *retro_midi_read_t)(uint8_t *byte); + +/* Writes byte to the output stream. + * 'delta_time' is in microseconds and represent time elapsed since previous write. + * Returns true if byte is written, false otherwise. */ +typedef bool (RETRO_CALLCONV *retro_midi_write_t)(uint8_t byte, uint32_t delta_time); + +/* Flushes previously written data. + * Returns true if successful, false otherwise. */ +typedef bool (RETRO_CALLCONV *retro_midi_flush_t)(void); + +struct retro_midi_interface +{ + retro_midi_input_enabled_t input_enabled; + retro_midi_output_enabled_t output_enabled; + retro_midi_read_t read; + retro_midi_write_t write; + retro_midi_flush_t flush; +}; enum retro_hw_render_context_negotiation_interface_type { @@ -1193,13 +1267,6 @@ struct retro_hw_render_context_negotiation_interface enum retro_hw_render_context_negotiation_interface_type interface_type; unsigned interface_version; }; -#define RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE (43 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* const struct retro_hw_render_context_negotiation_interface * -- - * Sets an interface which lets the libretro core negotiate with frontend how a context is created. - * The semantics of this interface depends on which API is used in SET_HW_RENDER earlier. - * This interface will be used when the frontend is trying to create a HW rendering context, - * so it will be used after SET_HW_RENDER, but before the context_reset callback. - */ /* Serialized state is incomplete in some way. Set if serialization is * usable in typical end-user cases but should not be relied upon to @@ -1225,20 +1292,17 @@ struct retro_hw_render_context_negotiation_interface * dependence */ #define RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT (1 << 6) -#define RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS 44 - /* uint64_t * -- - * Sets quirk flags associated with serialization. The frontend will zero any flags it doesn't - * recognize or support. Should be set in either retro_init or retro_load_game, but not both. - */ - -#define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */ -#define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */ -#define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */ -#define RETRO_MEMDESC_ALIGN_4 (2 << 16) -#define RETRO_MEMDESC_ALIGN_8 (3 << 16) -#define RETRO_MEMDESC_MINSIZE_2 (1 << 24) /* All memory in this region is accessed at least 2 bytes at the time. */ -#define RETRO_MEMDESC_MINSIZE_4 (2 << 24) -#define RETRO_MEMDESC_MINSIZE_8 (3 << 24) +#define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */ +#define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */ +#define RETRO_MEMDESC_SYSTEM_RAM (1 << 2) /* The memory area is system RAM. This is main RAM of the gaming system. */ +#define RETRO_MEMDESC_SAVE_RAM (1 << 3) /* The memory area is save RAM. This RAM is usually found on a game cartridge, backed up by a battery. */ +#define RETRO_MEMDESC_VIDEO_RAM (1 << 4) /* The memory area is video RAM (VRAM) */ +#define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */ +#define RETRO_MEMDESC_ALIGN_4 (2 << 16) +#define RETRO_MEMDESC_ALIGN_8 (3 << 16) +#define RETRO_MEMDESC_MINSIZE_2 (1 << 24) /* All memory in this region is accessed at least 2 bytes at the time. */ +#define RETRO_MEMDESC_MINSIZE_4 (2 << 24) +#define RETRO_MEMDESC_MINSIZE_8 (3 << 24) struct retro_memory_descriptor { uint64_t flags; @@ -2138,17 +2202,26 @@ struct retro_system_info * Typically used for a GUI to filter * out extensions. */ - /* If true, retro_load_game() is guaranteed to provide a valid pathname - * in retro_game_info::path. - * ::data and ::size are both invalid. + /* Libretro cores that need to have direct access to their content + * files, including cores which use the path of the content files to + * determine the paths of other files, should set need_fullpath to true. * - * If false, ::data and ::size are guaranteed to be valid, but ::path - * might not be valid. + * Cores should strive for setting need_fullpath to false, + * as it allows the frontend to perform patching, etc. * - * This is typically set to true for libretro implementations that must - * load from file. - * Implementations should strive for setting this to false, as it allows - * the frontend to perform patching, etc. */ + * If need_fullpath is true and retro_load_game() is called: + * - retro_game_info::path is guaranteed to have a valid path + * - retro_game_info::data and retro_game_info::size are invalid + * + * If need_fullpath is false and retro_load_game() is called: + * - retro_game_info::path may be NULL + * - retro_game_info::data and retro_game_info::size are guaranteed + * to be valid + * + * See also: + * - RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY + * - RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY + */ bool need_fullpath; /* If true, the frontend is not allowed to extract any archives before @@ -2333,7 +2406,13 @@ RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info); * will only poll input based on that particular device type. It is only a * hint to the libretro core when a core cannot automatically detect the * appropriate input device type on its own. It is also relevant when a - * core can change its behavior depending on device type. */ + * core can change its behavior depending on device type. + * + * As part of the core's implementation of retro_set_controller_port_device, + * the core should call RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS to notify the + * frontend if the descriptions for any controls have changed as a + * result of changing the device type. + */ RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device); /* Resets the current game. */ @@ -2365,7 +2444,9 @@ RETRO_API bool retro_unserialize(const void *data, size_t size); RETRO_API void retro_cheat_reset(void); RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code); -/* Loads a game. */ +/* Loads a game. + * Return true to indicate successful loading and false to indicate load failure. + */ RETRO_API bool retro_load_game(const struct retro_game_info *game); /* Loads a "special" kind of game. Should not be used, @@ -2375,7 +2456,7 @@ RETRO_API bool retro_load_game_special( const struct retro_game_info *info, size_t num_info ); -/* Unloads a currently loaded game. */ +/* Unloads the currently loaded game. Called before retro_deinit(void). */ RETRO_API void retro_unload_game(void); /* Gets region of game. */ diff --git a/src/drivers/libretro/libretro.c b/src/drivers/libretro/libretro.c index 2abb4cd..f62ed8e 100644 --- a/src/drivers/libretro/libretro.c +++ b/src/drivers/libretro/libretro.c @@ -719,6 +719,13 @@ void retro_set_environment(retro_environment_t cb) { "fceumm_show_crosshair", "Show Crosshair; enabled|disabled" }, { "fceumm_overclocking", "Overclocking; disabled|2x-Postrender|2x-VBlank" }, { "fceumm_ramstate", "RAM power up state (Restart); fill $ff|fill $00|random" }, +#ifdef DEBUG + { "fceumm_apu_1", "Enable Sound Channel 1 (Square 1); enabled|disabled" }, + { "fceumm_apu_2", "Enable Sound Channel 2 (Square 2); enabled|disabled" }, + { "fceumm_apu_3", "Enable Sound Channel 3 (Triangle); enabled|disabled" }, + { "fceumm_apu_4", "Enable Sound Channel 4 (Noise) ; enabled|disabled" }, + { "fceumm_apu_5", "Enable Sound Channel 5 (PCM); enabled|disabled" }, +#endif { NULL, NULL }, }; @@ -957,11 +964,22 @@ static const keymap bindmap[] = { { RETRO_DEVICE_ID_JOYPAD_Y, JOY_B }, }; +static void set_apu_channels(int chan) +{ + FSettings.SquareVolume[1] = 255 * ((chan >> 0) & 1); + FSettings.SquareVolume[0] = 255 * ((chan >> 1) & 1); + FSettings.TriangleVolume = 255 * ((chan >> 2) & 1); + FSettings.NoiseVolume = 255 * ((chan >> 3) & 1); + FSettings.PCMVolume = 255 * ((chan >> 4) & 1); +} + static void check_variables(bool startup) { struct retro_variable var = {0}; struct retro_system_av_info av_info; bool geometry_update = false; + char key[256]; + int i, enable_apu; var.key = "fceumm_palette"; @@ -1169,7 +1187,6 @@ static void check_variables(bool startup) FCEUD_RegionOverride(setregion); } - var.key = "fceumm_aspect"; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) @@ -1221,6 +1238,27 @@ static void check_variables(bool startup) swapDuty = newval; } } + +#ifdef DEBUG + var.key = key; + + enable_apu = 0; + + strcpy(key, "fceumm_apu_x"); + for (i = 0; i < 5; i++) + { + key[strlen("fceumm_apu_")] = '1' + i; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && !strcmp(var.value, "disabled")) + { + enable_apu |= (1 << i); + } + } + set_apu_channels(enable_apu ^ 0xff); +#else + set_apu_channels(0xff); +#endif + } static int mzx = 0, mzy = 0; @@ -2043,7 +2081,6 @@ unsigned retro_get_region(void) return FSettings.PAL ? RETRO_REGION_PAL : RETRO_REGION_NTSC; } - void *retro_get_memory_data(unsigned type) { uint8_t* data; @@ -2051,9 +2088,9 @@ void *retro_get_memory_data(unsigned type) switch(type) { case RETRO_MEMORY_SAVE_RAM: - if (iNESCart.battery) + if (iNESCart.battery && iNESCart.SaveGame[0] && iNESCart.SaveGameLen[0]) return iNESCart.SaveGame[0]; - else if (UNIFCart.battery) + else if (UNIFCart.battery && UNIFCart.SaveGame[0] && UNIFCart.SaveGameLen[0]) return UNIFCart.SaveGame[0]; else data = NULL; @@ -2076,9 +2113,9 @@ size_t retro_get_memory_size(unsigned type) switch(type) { case RETRO_MEMORY_SAVE_RAM: - if (iNESCart.battery) + if (iNESCart.battery && iNESCart.SaveGame[0] && iNESCart.SaveGameLen[0]) size = iNESCart.SaveGameLen[0]; - else if (UNIFCart.battery) + else if (UNIFCart.battery && UNIFCart.SaveGame[0] && UNIFCart.SaveGameLen[0]) size = UNIFCart.SaveGameLen[0]; else size = 0; diff --git a/src/fceu.h b/src/fceu.h index b15d374..f50c5c0 100644 --- a/src/fceu.h +++ b/src/fceu.h @@ -83,6 +83,10 @@ typedef struct { int PAL; int NetworkPlay; int SoundVolume; + int TriangleVolume; + int SquareVolume[2]; + int NoiseVolume; + int PCMVolume; int GameGenie; /* Current first and last rendered scanlines. */ diff --git a/src/file.c b/src/file.c index 02a074c..2ceb96f 100644 --- a/src/file.c +++ b/src/file.c @@ -58,8 +58,6 @@ static MEMWRAP *MakeMemWrap(void *tz, int type) fread(tmp->data, 1, tmp->size, (FILE*)tz); doret: - if (type == 0) - fclose((FILE*)tz); return tmp; } @@ -97,6 +95,7 @@ FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, fseek((FILE*)t, 0, SEEK_SET); fceufp->fp = MakeMemWrap(t, 0); + fclose((FILE*)t); } return fceufp; } diff --git a/src/filter.c b/src/filter.c index 0627058..33fa188 100644 --- a/src/filter.c +++ b/src/filter.c @@ -100,7 +100,7 @@ int32 NeoFilterSound(int32 *in, int32 *out, uint32 inlen, int32 *leftover) { #endif max = (inlen - 1) << 16; - if (FSettings.soundq == 2) + if (FSettings.soundq == 2) { for (x = mrindex; x < max; x += mrratio) { int32 acc = 0, acc2 = 0; uint32 c; @@ -116,7 +116,7 @@ int32 NeoFilterSound(int32 *in, int32 *out, uint32 inlen, int32 *leftover) { out++; count++; } - else + } else { for (x = mrindex; x < max; x += mrratio) { int32 acc = 0, acc2 = 0; uint32 c; @@ -132,6 +132,7 @@ int32 NeoFilterSound(int32 *in, int32 *out, uint32 inlen, int32 *leftover) { out++; count++; } + } mrindex = x - max; diff --git a/src/sound.c b/src/sound.c index df4b878..69b333a 100644 --- a/src/sound.c +++ b/src/sound.c @@ -520,7 +520,8 @@ void RDoPCM(void) { uint32 V; for (V = ChannelBC[4]; V < SOUNDTS; V++) - WaveHi[V] += RawDALatch << 16; + /* TODO: get rid of floating calculations to binary. set log volume scaling. */ + WaveHi[V] += (((RawDALatch << 16) / 256) * FSettings.PCMVolume ) & (~0xFFFF); ChannelBC[4] = SOUNDTS; } @@ -546,7 +547,16 @@ static INLINE void RDoSQ(int x) { amp = EnvUnits[x].Speed; else amp = EnvUnits[x].decvolume; -/* printf("%d\n",amp); */ + /* printf("%d\n",amp); */ + + /* Modify Square wave volume based on channel volume modifiers + * Note: the formulat x = x * y /100 does not yield exact results, + * but is "close enough" and avoids the need for using double values + * or implicit cohersion which are slower (we need speed here) */ + /* TODO: Optimize this. */ + if (FSettings.SquareVolume[x] != 256) + amp = (amp * FSettings.SquareVolume[x]) / 256; + amp <<= 24; rthresh = RectDuties[(PSG[(x << 2)] & 0xC0) >> 6]; @@ -618,6 +628,15 @@ static void RDoSQLQ(void) { else amp[x] = EnvUnits[x].decvolume; + /* Modify Square wave volume based on channel volume modifiers + * Note: the formulat x = x * y /100 does not yield exact results, + * but is "close enough" and avoids the need for using double vales + * or implicit cohersion which are slower (we need speed here) + * fixed - setting up maximum volume for square2 caused complete mute square2 channel. + * TODO: Optimize this. */ + if (FSettings.SquareVolume[x] != 256) + amp[x] = (amp[x] * FSettings.SquareVolume[x]) / 256; + if (!inie[x]) amp[x] = 0; /* Correct? Buzzing in MM2, others otherwise... */ rthresh[x] = RectDuties[(PSG[x * 4] & 0xC0) >> 6]; @@ -637,7 +656,7 @@ static void RDoSQLQ(void) { if (!inie[0] && !inie[1]) { for (V = start; V < end; V++) Wave[V >> 4] += totalout; - } else + } else { for (V = start; V < end; V++) { /* int tmpamp=0; if(RectDutyCount[0]> 0xe) & 1]; } } - else + } else { for (V = ChannelBC[3]; V < SOUNDTS; V++) { WaveHi[V] += outo; wlcount[3]--; @@ -885,6 +923,7 @@ static void RDoNoise(void) { outo = amptab[(nreg >> 0xe) & 1]; } } + } ChannelBC[3] = SOUNDTS; } @@ -938,6 +977,7 @@ int FlushEmulateSound(void) { *tmpo = (b & 65535) + wlookup2[(b >> 16) & 255] + wlookup1[b >> 24]; tmpo++; } + end = NeoFilterSound(WaveHi, WaveFinal, SOUNDTS, &left); memmove(WaveHi, WaveHi + SOUNDTS - left, left * sizeof(uint32)); @@ -953,10 +993,9 @@ int FlushEmulateSound(void) { SexyFilter(Wave, WaveFinal, end >> 4); -#if 0 - if (FSettings.lowpass) - SexyFilter2(WaveFinal, end >> 4); -#endif + /* if (FSettings.lowpass) + SexyFilter2(WaveFinal, end >> 4); */ + if (end & 0xF) Wave[0] = Wave[(end >> 4)]; Wave[end >> 4] = 0; @@ -1023,6 +1062,8 @@ void FCEUSND_Reset(void) { DMCAddress = 0; DMCSize = 0; DMCShift = 0; + DMCacc=1; + DMCBitCount=0; } void FCEUSND_Power(void) { @@ -1162,7 +1203,7 @@ SFORMAT FCEUSND_STATEINFO[] = { { &DMCAddressLatch, 1, "5ADL" }, { &DMCFormat, 1, "5FMT" }, { &RawDALatch, 1, "RWDA" }, - + //these are important for smooth sound after loading state { &sqacc[0], sizeof(sqacc[0]) | FCEUSTATE_RLSB, "SAC1" }, { &sqacc[1], sizeof(sqacc[1]) | FCEUSTATE_RLSB, "SAC2" }, @@ -1171,7 +1212,7 @@ SFORMAT FCEUSND_STATEINFO[] = { { &tristep, sizeof(tristep) | FCEUSTATE_RLSB, "TRIS"}, { &lq_triacc, sizeof(lq_triacc) | FCEUSTATE_RLSB, "TACC" }, { &lq_noiseacc, sizeof(lq_noiseacc) | FCEUSTATE_RLSB, "NACC" }, - + //less important but still necessary { &ChannelBC[0], sizeof(ChannelBC[0]) | FCEUSTATE_RLSB, "CBC1" }, { &ChannelBC[1], sizeof(ChannelBC[1]) | FCEUSTATE_RLSB, "CBC2" }, @@ -1187,10 +1228,10 @@ SFORMAT FCEUSND_STATEINFO[] = { { &sexyfilter_acc1, sizeof(sexyfilter_acc1) | FCEUSTATE_RLSB, "FAC1" }, { &sexyfilter_acc2, sizeof(sexyfilter_acc2) | FCEUSTATE_RLSB, "FAC2" }, { &lq_tcout, sizeof(lq_tcout) | FCEUSTATE_RLSB, "TCOU"}, - + //wave buffer is used for filtering, only need first 17 values from it { &Wave, 32 * sizeof(int32), "WAVE"}, -{ 0 } + { 0 } }; void FCEUSND_SaveState(void) { @@ -1205,7 +1246,17 @@ void FCEUSND_LoadState(int version) { //minimal validation for (i = 0; i < 5; i++) { - if (ChannelBC[i] < 0 || ChannelBC[i] > 15) + int BC_max = 15; + + if (FSettings.soundq == 2) + { + BC_max = 1025; + } + else if (FSettings.soundq == 1) + { + BC_max = 485; + } + if (ChannelBC[i] < 0 || ChannelBC[i] > BC_max) { ChannelBC[i] = 0; }