shithub: ft²

Download patch

ref: e700653730cedd7be3760c76b94c946e02d381ab
parent: 539b5e6c4af7e1e8ef93393c052144f1d3da8386
parent: d501f401aefda30c7c20de806d4b3b48fe4d9218
author: qwx <qwx@sciops.net>
date: Tue Oct 3 06:07:49 EDT 2023

sync upstream

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,13 +27,30 @@
     set(SDL2_LIBRARIES "SDL2::SDL2")
 endif()
 
+find_package(Threads REQUIRED)
+
 target_link_libraries(ft2-clone
-    PRIVATE m asound pthread ${SDL2_LIBRARIES})
+    PRIVATE m Threads::Threads ${SDL2_LIBRARIES})
 
 target_compile_definitions(ft2-clone
     PRIVATE HAS_MIDI
-    PRIVATE __LINUX_ALSA__
     PRIVATE HAS_LIBFLAC)
+
+if(UNIX)
+    if(APPLE)
+        find_library(COREAUDIO CoreAudio REQUIRED)
+        find_library(COREMIDI CoreMIDI REQUIRED)
+        target_link_libraries(ft2-clone
+            PRIVATE ${COREAUDIO} ${COREMIDI})
+        target_compile_definitions(ft2-clone
+            PRIVATE __MACOSX_CORE__)
+    else()
+        target_link_libraries(ft2-clone
+            PRIVATE asound)
+        target_compile_definitions(ft2-clone
+            PRIVATE __LINUX_ALSA__)
+    endif()
+endif()
 
 if(EXTERNAL_LIBFLAC)
     find_package(PkgConfig REQUIRED)
--- a/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_assert.h
+++ b/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_assert.h
@@ -55,6 +55,8 @@
     #define SDL_TriggerBreakpoint() __builtin_debugtrap()
 #elif ( (!defined(__NACL__)) && ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))) )
     #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
+#elif (defined(__GNUC__) || defined(__clang__)) && defined(__riscv)
+    #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "ebreak\n\t" )
 #elif ( defined(__APPLE__) && (defined(__arm64__) || defined(__aarch64__)) )  /* this might work on other ARM targets, but this is a known quantity... */
     #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "brk #22\n\t" )
 #elif defined(__APPLE__) && defined(__arm__)
--- a/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_atomic.h
+++ b/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_atomic.h
@@ -240,7 +240,7 @@
 /* "REP NOP" is PAUSE, coded for tools that don't know it by that name. */
 #if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
     #define SDL_CPUPauseInstruction() __asm__ __volatile__("pause\n")  /* Some assemblers can't do REP NOP, so go with PAUSE. */
-#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)
+#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(__aarch64__)
     #define SDL_CPUPauseInstruction() __asm__ __volatile__("yield" ::: "memory")
 #elif (defined(__powerpc__) || defined(__powerpc64__))
     #define SDL_CPUPauseInstruction() __asm__ __volatile__("or 27,27,27");
--- a/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_hints.h
+++ b/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_hints.h
@@ -1008,6 +1008,15 @@
 #define SDL_HINT_JOYSTICK_THREAD "SDL_JOYSTICK_THREAD"
 
 /**
+  *  \brief  A variable controlling whether Windows.Gaming.Input should be used for controller handling.
+  *
+  *  This variable can be set to the following values:
+  *    "0"       - WGI is not used
+  *    "1"       - WGI is used (the default)
+  */
+#define SDL_HINT_JOYSTICK_WGI "SDL_JOYSTICK_WGI"
+
+/**
  * \brief Determines whether SDL enforces that DRM master is required in order
  *        to initialize the KMSDRM video backend.
  *
@@ -1464,6 +1473,17 @@
  *  By default SDL does not sync screen surface updates with vertical refresh.
  */
 #define SDL_HINT_RENDER_VSYNC               "SDL_RENDER_VSYNC"
+
+/**
+ *  \brief  A variable controlling whether the Metal render driver select low power device over default one
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - Use the prefered OS device
+ *    "1"       - Select a low power one
+ *
+ *  By default the prefered OS device is used.
+ */
+#define SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE "SDL_RENDER_METAL_PREFER_LOW_POWER_DEVICE"
 
 /**
  *  \brief  A variable controlling if VSYNC is automatically disable if doesn't reach the enough FPS
--- a/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_render.h
+++ b/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_render.h
@@ -1890,7 +1890,7 @@
  * Note that as of SDL 2.0.18, this will return NULL if Metal refuses to give
  * SDL a drawable to render to, which might happen if the window is
  * hidden/minimized/offscreen. This doesn't apply to command encoders for
- * render targets, just the window's backbacker. Check your return values!
+ * render targets, just the window's backbuffer. Check your return values!
  *
  * \param renderer The renderer to query
  * \returns an `id<MTLRenderCommandEncoder>` on success, or NULL if the
--- a/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_revision.h
+++ b/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_revision.h
@@ -1,7 +1,7 @@
 /* Generated by updaterev.sh, do not edit */
 #ifdef SDL_VENDOR_INFO
-#define SDL_REVISION "SDL-release-2.28.0-0-gffa78e6be (" SDL_VENDOR_INFO ")"
+#define SDL_REVISION "SDL-release-2.28.3-0-g8a5ba43d0 (" SDL_VENDOR_INFO ")"
 #else
-#define SDL_REVISION "SDL-release-2.28.0-0-gffa78e6be"
+#define SDL_REVISION "SDL-release-2.28.3-0-g8a5ba43d0"
 #endif
 #define SDL_REVISION_NUMBER 0
--- a/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_version.h
+++ b/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Headers/SDL_version.h
@@ -59,7 +59,7 @@
 */
 #define SDL_MAJOR_VERSION   2
 #define SDL_MINOR_VERSION   28
-#define SDL_PATCHLEVEL      0
+#define SDL_PATCHLEVEL      3
 
 /**
  * Macro to determine SDL version program was compiled against.
--- a/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Resources/Info.plist
+++ b/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/Resources/Info.plist
@@ -3,7 +3,7 @@
 <plist version="1.0">
 <dict>
 	<key>BuildMachineOSBuild</key>
-	<string>22F66</string>
+	<string>22F82</string>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>English</string>
 	<key>CFBundleExecutable</key>
@@ -19,7 +19,7 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2.28.0</string>
+	<string>2.28.3</string>
 	<key>CFBundleSignature</key>
 	<string>SDLX</string>
 	<key>CFBundleSupportedPlatforms</key>
@@ -27,7 +27,7 @@
 		<string>MacOSX</string>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>2.28.0</string>
+	<string>2.28.3</string>
 	<key>DTCompiler</key>
 	<string>com.apple.compilers.llvm.clang.1_0</string>
 	<key>DTPlatformBuild</key>
--- a/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/_CodeSignature/CodeResources
+++ b/release/macos/ft2-clone-macos.app/Contents/Frameworks/SDL2.framework/Versions/A/_CodeSignature/CodeResources
@@ -14,7 +14,7 @@
 		</data>
 		<key>Resources/Info.plist</key>
 		<data>
-		MP8SsWNX9fsyVRo11YwxKezmPug=
+		O0+yH6th+YqPy5qBlROGAOJkywk=
 		</data>
 		<key>Resources/License.txt</key>
 		<data>
@@ -46,11 +46,11 @@
 		<dict>
 			<key>hash</key>
 			<data>
-			bOToHmbN+xx6t5JDrzQswLbnEZM=
+			S50t60okfb3/ykWKavFC3tnkGgE=
 			</data>
 			<key>hash2</key>
 			<data>
-			2yPzeIi/yrrj/kXEyXW4yRLtxPe1iXPO7joeTUBcZaM=
+			uPZQOi25pO/BZWjREOhxmXsoreSNJyZrNOnVJ/TyYzs=
 			</data>
 		</dict>
 		<key>Headers/SDL_atomic.h</key>
@@ -57,11 +57,11 @@
 		<dict>
 			<key>hash</key>
 			<data>
-			FTCyRgMQ1IzdGYV2XbCDSQ7JNec=
+			yrv6uC/CjYpeCteZDUbBFr2nwiU=
 			</data>
 			<key>hash2</key>
 			<data>
-			4iFngSAorzMM520V+2V1sriu9mL12sLlPrhNfgT12Ck=
+			Mrfc980NeBAvQ5I040V8xusL6cHBcFfPaA9B2prA4dw=
 			</data>
 		</dict>
 		<key>Headers/SDL_audio.h</key>
@@ -255,11 +255,11 @@
 		<dict>
 			<key>hash</key>
 			<data>
-			M/nqf8jyrXJ9IrgLrVffOZG4Jlo=
+			SWTHcoPzInj/HwBHNEFfQGmL6Dw=
 			</data>
 			<key>hash2</key>
 			<data>
-			nCUBVEvtWTQ2Ab/H1a9n4I68tT572DhjMnne+N52bwY=
+			BQwW6YWVYXbaOLc9Id+ZjuKHs2VIfcSjM/Oo9JBI6Yk=
 			</data>
 		</dict>
 		<key>Headers/SDL_joystick.h</key>
@@ -552,11 +552,11 @@
 		<dict>
 			<key>hash</key>
 			<data>
-			sNcVNpmlpcvsqJmBOwyg6hjTWQ8=
+			5yyytzTD1eS/K13NSzW6faLAUlA=
 			</data>
 			<key>hash2</key>
 			<data>
-			1EY74HfczRb8zZ3CZfcoinmI8nVMwFaoIfcvG7kPBEE=
+			+ljWDPQHFdheD/Z6QxHzbpNQVg/XVlqx5+B3ovMbIF0=
 			</data>
 		</dict>
 		<key>Headers/SDL_revision.h</key>
@@ -563,11 +563,11 @@
 		<dict>
 			<key>hash</key>
 			<data>
-			sRLTPAglYSZ19Pdm+afA2j47taM=
+			ACsjWsXuUQwHVlG5aPLRf54/dIQ=
 			</data>
 			<key>hash2</key>
 			<data>
-			Fo7p72CdM/uZzX9nxLeCot/MrKYwSKmo0ESISIMTIOI=
+			FdzvRtvvNjmf4LikKB9CV7MkieGdfgsJbfoN4kxwtqs=
 			</data>
 		</dict>
 		<key>Headers/SDL_rwops.h</key>
@@ -706,11 +706,11 @@
 		<dict>
 			<key>hash</key>
 			<data>
-			BrMh/0kZeBHu8Xr+/5hS8IHcMfU=
+			RxJkcIBwFx85nADiCWZgVRs8k7g=
 			</data>
 			<key>hash2</key>
 			<data>
-			tIqaCIDNMsT9rQ9arELIo8WVF04pSMBtxaavylyyrK4=
+			0lwD8QVtsPTM6vhZbDu05D8v6so3iTdjrj3mVjcfhWA=
 			</data>
 		</dict>
 		<key>Headers/SDL_video.h</key>
@@ -783,11 +783,11 @@
 		<dict>
 			<key>hash</key>
 			<data>
-			MP8SsWNX9fsyVRo11YwxKezmPug=
+			O0+yH6th+YqPy5qBlROGAOJkywk=
 			</data>
 			<key>hash2</key>
 			<data>
-			JS+9bRfZuLgojqjIgCYRqSXUyJ5L/MJO+Cyss5R17Rs=
+			sIWVXC/W1heu51j6IrNeI7fFPvxyVxJPCvMlZ8YpVFA=
 			</data>
 		</dict>
 		<key>Resources/License.txt</key>
--- a/src/ft2_about.c
+++ b/src/ft2_about.c
@@ -7,8 +7,8 @@
 #include "ft2_video.h"
 #include "ft2_structs.h"
 
-#define NUM_STARS 2048
-#define ALPHA_FADE_MILLISECS 2100 /* amount of milliseconds until content is fully faded in */
+#define NUM_STARS 3000
+#define ALPHA_FADE_MILLISECS 2000 /* amount of milliseconds until content is fully faded in */
 #define ABOUT_SCREEN_W 626
 #define ABOUT_SCREEN_H 167
 #define ABOUT_LOGO_W 449
@@ -30,7 +30,7 @@
 static char *customText2 = "https://16-bits.org";
 static char customText3[256];
 static int16_t customText1Y, customText2Y, customText3Y, customText1X, customText2X, customText3X;
-static uint32_t alphaValue, randSeed, frameCounter;
+static uint32_t logoTimer, alphaValue, randSeed, frameCounter;
 static vector_t starPoints[NUM_STARS], rotation;
 static matrix_t matrix;
 
@@ -91,8 +91,26 @@
 	rotation.x = rotation.y = rotation.z = 0.0f;
 	alphaValue = 0;
 	frameCounter = 0;
+	logoTimer = 0;
 }
 
+static void blendPixel(int32_t x, int32_t y, int32_t r, int32_t g, int32_t b, int32_t alpha)
+{
+	uint32_t *p = &video.frameBuffer[(y * SCREEN_W) + x];
+
+	const uint32_t srcPixel = *p;
+
+	const int32_t srcR = RGB32_R(srcPixel);
+	const int32_t srcG = RGB32_G(srcPixel);
+	const int32_t srcB = RGB32_B(srcPixel);
+
+	r = ((srcR * (65536-alpha)) + (r * alpha)) >> 16;
+	g = ((srcG * (65536-alpha)) + (g * alpha)) >> 16;
+	b = ((srcB * (65536-alpha)) + (b * alpha)) >> 16;
+
+	*p = RGB32(r, g, b);
+}
+
 static void starfield(void)
 {
 	vector_t *star = starPoints;
@@ -136,7 +154,24 @@
 		if (b > 255)
 			b = 255;
 
-		video.frameBuffer[(outY * SCREEN_W) + outX] = RGB32(r, g, b);
+		// blend sides of star
+
+		const int32_t sidesAlpha = 13000;
+
+		if (outX-1 >= 3)
+			blendPixel(outX-1, outY, r, g, b, sidesAlpha);
+
+		if (outX+1 < 3+ABOUT_SCREEN_W)
+			blendPixel(outX+1, outY, r, g, b, sidesAlpha);
+
+		if (outY-1 >= 3)
+			blendPixel(outX, outY-1, r, g, b, sidesAlpha);
+
+		if (outY+1 < 3+ABOUT_SCREEN_H)
+			blendPixel(outX, outY+1, r, g, b, sidesAlpha);
+
+		// plot main star pixel
+		blendPixel(outX, outY, r, g, b, 60000);
 	}
 }
 
@@ -156,9 +191,16 @@
 	textOutAlpha(customText2X, customText2Y, PAL_FORGRND, customText2, alphaValue);
 	textOutAlpha(customText3X, customText3Y, PAL_FORGRND, customText3, alphaValue);
 
-	alphaValue += (uint32_t)((65536.0 / (ALPHA_FADE_MILLISECS / (1000.0 / VBLANK_HZ))) + 0.5);
-	if (alphaValue > 65536)
-		alphaValue = 65536;
+	if (logoTimer > (int32_t)(VBLANK_HZ/4.0))
+	{
+		alphaValue += (uint32_t)((65536.0 / (ALPHA_FADE_MILLISECS / (1000.0 / VBLANK_HZ))) + 0.5);
+		if (alphaValue > 65536)
+			alphaValue = 65536;
+	}
+	else
+	{
+		logoTimer++;
+	}
 
 	// the exit button has to be redrawn since it gets overwritten :)
 	showPushButton(PB_EXIT_ABOUT);
--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -24,12 +24,10 @@
 #pragma warning(disable: 4996)
 #endif
 
-#define INITIAL_DITHER_SEED 0x12345000
-
 static int32_t smpShiftValue;
-static uint32_t oldAudioFreq, tickTimeLenInt, randSeed = INITIAL_DITHER_SEED;
+static uint32_t oldAudioFreq, tickTimeLenInt;
 static uint64_t tickTimeLenFrac;
-static double dAudioNormalizeMul, dSqrtPanningTable[256+1], dPrngStateL, dPrngStateR;
+static double dAudioNormalizeMul, dSqrtPanningTable[256+1];
 static voice_t voice[MAX_CHANNELS * 2];
 
 // globalized
@@ -95,7 +93,7 @@
 
 		// if it didn't work to use the old settings again, then something is seriously wrong...
 		if (!setupAudio(CONFIG_HIDE_ERRORS))
-			okBox(0, "System message", "Couldn't find a working audio mode... You'll get no sound / replayer timer!");
+			okBox(0, "System message", "Couldn't find a working audio mode... You'll get no sound / replayer timer!", NULL);
 
 		resumeAudio();
 		return false;
@@ -421,47 +419,18 @@
 	}
 }
 
-void resetAudioDither(void)
+static void sendSamples16BitStereo(uint8_t *stream, uint32_t sampleBlockLength)
 {
-	randSeed = INITIAL_DITHER_SEED;
-	dPrngStateL = 0.0;
-	dPrngStateR = 0.0;
-}
-
-static inline int32_t random32(void)
-{
-	// LCG 32-bit random
-	randSeed *= 134775813;
-	randSeed++;
-
-	return (int32_t)randSeed;
-}
-
-static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLength)
-{
-	int32_t out32;
-	double dOut, dPrng;
-
 	int16_t *streamPointer16 = (int16_t *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
-		// left channel - 1-bit triangular dithering
-		dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5
-		dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
-		dOut = (dOut + dPrng) - dPrngStateL;
-		dPrngStateL = dPrng;
-		out32 = (int32_t)dOut;
-		CLAMP16(out32);
-		*streamPointer16++ = (int16_t)out32;
+		int32_t L = (int32_t)((double)audio.fMixBufferL[i] * dAudioNormalizeMul);
+		CLAMP16(L);
+		*streamPointer16++ = (int16_t)L;
 
-		// right channel - 1-bit triangular dithering
-		dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5
-		dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
-		dOut = (dOut + dPrng) - dPrngStateR;
-		dPrngStateR = dPrng;
-		out32 = (int32_t)dOut;
-		CLAMP16(out32);
-		*streamPointer16++ = (int16_t)out32;
+		int32_t R = (int32_t)((double)audio.fMixBufferR[i] * dAudioNormalizeMul);
+		CLAMP16(R);
+		*streamPointer16++ = (int16_t)R;
 
 		// clear what we read from the mixing buffer
 		audio.fMixBufferL[i] = 0.0f;
@@ -469,21 +438,18 @@
 	}
 }
 
-static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength)
+static void sendSamples32BitFloatStereo(uint8_t *stream, uint32_t sampleBlockLength)
 {
-	double dOut;
 	float *fStreamPointer32 = (float *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
-		// left channel
-		dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
-		dOut = CLAMP(dOut, -1.0, 1.0);
-		*fStreamPointer32++ = (float)dOut;
+		double dL = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
+		dL = CLAMP(dL, -1.0, 1.0);
+		*fStreamPointer32++ = (float)dL;
 
-		// right channel
-		dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
-		dOut = CLAMP(dOut, -1.0, 1.0);
-		*fStreamPointer32++ = (float)dOut;
+		double dR = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
+		dR = CLAMP(dR, -1.0, 1.0);
+		*fStreamPointer32++ = (float)dR;
 
 		// clear what we read from the mixing buffer
 		audio.fMixBufferL[i] = 0.0f;
@@ -536,9 +502,9 @@
 
 	// normalize mix buffer and send to audio stream
 	if (bitDepth == 16)
-		sendSamples16BitDitherStereo(stream, samplesToMix);
+		sendSamples16BitStereo(stream, samplesToMix);
 	else
-		sendSamples32BitStereo(stream, samplesToMix);
+		sendSamples32BitFloatStereo(stream, samplesToMix);
 }
 
 int32_t pattQueueReadSize(void)
@@ -879,14 +845,17 @@
 		if (audio.tickSampleCounter == 0) // new replayer tick
 		{
 			replayerBusy = true;
+			if (!musicPaused) // important, don't remove this check! (also used for safety)
+			{
+				if (audio.volumeRampingFlag)
+					resetRampVolumes();
 
-			if (audio.volumeRampingFlag)
-				resetRampVolumes();
+				tickReplayer();
+				updateVoices();
+				fillVisualsSyncBuffer();
+			}
+			replayerBusy = false;
 
-			tickReplayer();
-			updateVoices();
-			fillVisualsSyncBuffer();
-
 			audio.tickSampleCounter = audio.samplesPerTickInt;
 
 			audio.tickSampleCounterFrac += audio.samplesPerTickFrac;
@@ -895,8 +864,6 @@
 				audio.tickSampleCounterFrac &= BPM_FRAC_MASK;
 				audio.tickSampleCounter++;
 			}
-
-			replayerBusy = false;
 		}
 
 		uint32_t samplesToMix = samplesLeft;
@@ -911,9 +878,9 @@
 	}
 
 	if (config.specialFlags & BITDEPTH_16)
-		sendSamples16BitDitherStereo(stream, len);
+		sendSamples16BitStereo(stream, len);
 	else
-		sendSamples32BitStereo(stream, len);
+		sendSamples32BitFloatStereo(stream, len);
 
 	(void)userdata;
 }
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -150,7 +150,6 @@
 void pauseAudio(void);
 void resumeAudio(void);
 bool setNewAudioSettings(void);
-void resetAudioDither(void);
 void lockAudio(void);
 void unlockAudio(void);
 void lockMixerCallback(void);
--- a/src/ft2_audioselector.c
+++ b/src/ft2_audioselector.c
@@ -267,7 +267,7 @@
 				strcpy(audio.currOutputDevice, devString);
 
 			if (!setNewAudioSettings())
-				okBox(0, "System message", "Couldn't open audio input device!");
+				okBox(0, "System message", "Couldn't open audio input device!", NULL);
 			else
 				drawAudioOutputList();
 		}
--- a/src/ft2_config.c
+++ b/src/ft2_config.c
@@ -247,7 +247,7 @@
 
 void resetConfig(void)
 {
-	if (okBox(2, "System request", "Are you sure you want to reset your FT2 configuration?") != 1)
+	if (okBox(2, "System request", "Are you sure you want to reset your FT2 configuration?", NULL) != 1)
 		return;
 
 	const uint8_t oldWindowFlags = config.windowFlags;
@@ -305,7 +305,7 @@
 	if (editor.configFileLocationU == NULL)
 	{
 		if (showErrorFlag)
-			okBox(0, "System message", "Error opening config file for reading!");
+			okBox(0, "System message", "Error opening config file for reading!", NULL);
 
 		return false;
 	}
@@ -314,7 +314,7 @@
 	if (f == NULL)
 	{
 		if (showErrorFlag)
-			okBox(0, "System message", "Error opening config file for reading!");
+			okBox(0, "System message", "Error opening config file for reading!", NULL);
 
 		return false;
 	}
@@ -328,7 +328,7 @@
 	{
 		fclose(f);
 		if (showErrorFlag)
-			okBox(0, "System message", "Error loading config: the config file is not valid!");
+			okBox(0, "System message", "Error loading config: the config file is not valid!", NULL);
 
 		return false;
 	}
@@ -341,7 +341,7 @@
 	{
 		fclose(f);
 		if (showErrorFlag)
-			okBox(0, "System message", "Error opening config file for reading!");
+			okBox(0, "System message", "Error opening config file for reading!", NULL);
 
 		return false;
 	}
@@ -353,7 +353,7 @@
 	if (memcmp(&configBuffer[0], CFG_ID_STR, 35) != 0)
 	{
 		if (showErrorFlag)
-			okBox(0, "System message", "Error loading config: the config file is not valid!");
+			okBox(0, "System message", "Error loading config: the config file is not valid!", NULL);
 
 		return false;
 	}
@@ -387,7 +387,7 @@
 	if (editor.configFileLocationU == NULL)
 	{
 		if (showErrorFlag)
-			okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+			okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 
 		return false;
 	}
@@ -401,7 +401,7 @@
 	if (f == NULL)
 	{
 		if (showErrorFlag)
-			okBox(0, "System message", "General I/O error during config saving! Is the file in use?");
+			okBox(0, "System message", "General I/O error during config saving! Is the file in use?", NULL);
 
 		return false;
 	}
@@ -427,7 +427,7 @@
 		fclose(f);
 
 		if (showErrorFlag)
-			okBox(0, "System message", "General I/O error during config saving! Is the file in use?");
+			okBox(0, "System message", "General I/O error during config saving! Is the file in use?", NULL);
 
 		return false;
 	}
@@ -1789,7 +1789,7 @@
 {
 	config.specialFlags2 ^= HARDWARE_MOUSE;
 	if (!createMouseCursors())
-		okBox(0, "System message", "Error: Couldn't create/show mouse cursor!");
+		okBox(0, "System message", "Error: Couldn't create/show mouse cursor!", NULL);
 
 	if (config.specialFlags2 & HARDWARE_MOUSE)
 	{
@@ -1948,7 +1948,7 @@
 {
 	if (video.fullscreen)
 	{
-		okBox(0, "System message", "You can't change the window size while in fullscreen mode!");
+		okBox(0, "System message", "You can't change the window size while in fullscreen mode!", NULL);
 		return;
 	}
 
@@ -1962,7 +1962,7 @@
 {
 	if (video.fullscreen)
 	{
-		okBox(0, "System message", "You can't change the window size while in fullscreen mode!");
+		okBox(0, "System message", "You can't change the window size while in fullscreen mode!", NULL);
 		return;
 	}
 
@@ -1976,7 +1976,7 @@
 {
 	if (video.fullscreen)
 	{
-		okBox(0, "System message", "You can't change the window size while in fullscreen mode!");
+		okBox(0, "System message", "You can't change the window size while in fullscreen mode!", NULL);
 		return;
 	}
 
@@ -1990,18 +1990,14 @@
 {
 	if (video.fullscreen)
 	{
-		okBox(0, "System message", "You can't change the window size while in fullscreen mode!");
+		okBox(0, "System message", "You can't change the window size while in fullscreen mode!", NULL);
 		return;
 	}
 
-#ifdef __arm__
-	okBox(0, "System message", "3x video upscaling is not supported on ARM devices for performance reasons.");
-#else
 	config.windowFlags &= ~(WINSIZE_AUTO + WINSIZE_1X + WINSIZE_2X + WINSIZE_4X);
 	config.windowFlags |= WINSIZE_3X;
 	setWindowSizeFromConfig(true);
 	checkRadioButton(RB_CONFIG_WIN_SIZE_3X);
-#endif
 }
 
 void rbWinSize4x(void)
@@ -2008,18 +2004,14 @@
 {
 	if (video.fullscreen)
 	{
-		okBox(0, "System message", "You can't change the window size while in fullscreen mode!");
+		okBox(0, "System message", "You can't change the window size while in fullscreen mode!", NULL);
 		return;
 	}
 
-#ifdef __arm__
-	okBox(0, "System message", "4x video upscaling is not supported on ARM devices for performance reasons.");
-#else
 	config.windowFlags &= ~(WINSIZE_AUTO + WINSIZE_1X + WINSIZE_2X + WINSIZE_3X);
 	config.windowFlags |= WINSIZE_4X;
 	setWindowSizeFromConfig(true);
 	checkRadioButton(RB_CONFIG_WIN_SIZE_4X);
-#endif
 }
 
 void cbSampCutToBuff(void)
@@ -2085,7 +2077,7 @@
 	checkBoxes[CB_CONF_MIDI_ENABLE].checked = false;
 	drawCheckBox(CB_CONF_MIDI_ENABLE);
 
-	okBox(0, "System message", "This program was not compiled with MIDI functionality!");
+	okBox(0, "System message", "This program was not compiled with MIDI functionality!", NULL);
 #endif
 }
 
@@ -2114,7 +2106,7 @@
 	config.windowFlags ^= FORCE_VSYNC_OFF;
 
 	if (!(config.dontShowAgainFlags & DONT_SHOW_NOT_YET_APPLIED_WARNING_FLAG))
-		okBox(7, "System message", "This setting is not applied until you close and reopen the program.");
+		okBox(0, "System message", "This setting is not applied until you close and reopen the program.", configToggleNotYetAppliedWarning);
 }
 
 void cbFullScreen(void)
@@ -2122,7 +2114,7 @@
 	config.windowFlags ^= START_IN_FULLSCR;
 
 	if (!(config.dontShowAgainFlags & DONT_SHOW_NOT_YET_APPLIED_WARNING_FLAG))
-		okBox(7, "System message", "This setting is not applied until you close and reopen the program.");
+		okBox(0, "System message", "This setting is not applied until you close and reopen the program.", configToggleNotYetAppliedWarning);
 }
 
 void cbPixelFilter(void)
--- a/src/ft2_diskop.c
+++ b/src/ft2_diskop.c
@@ -467,18 +467,18 @@
 {
 	if (mouse.mode == MOUSE_MODE_DELETE)
 	{
-		okBox(8, "System complaint", "Very funny.");
+		okBox(0, "System message", "Drive deletion is not implemented!", NULL);
 		return;
 	}
 
 	if (str == NULL || *str == '\0')
 	{
-		okBox(0, "System message", "Couldn't open drive!");
+		okBox(0, "System message", "Couldn't open drive!", NULL);
 		return;
 	}
 
 	if (chdir(str) != 0)
-		okBox(0, "System message", "Couldn't open drive! Please make sure there's a disk in it.");
+		okBox(0, "System message", "Couldn't open drive! Please make sure there's a disk in it.", NULL);
 	else
 		editor.diskOpReadDir = true;
 }
@@ -575,12 +575,12 @@
 {
 	if (strU == NULL || UNICHAR_STRLEN(strU) == 0)
 	{
-		okBox(0, "System message", "Couldn't open directory! No permission or in use?");
+		okBox(0, "System message", "Couldn't open directory! No permission or in use?", NULL);
 		return;
 	}
 
 	if (UNICHAR_CHDIR(strU) != 0)
-		okBox(0, "System message", "Couldn't open directory! No permission or in use?");
+		okBox(0, "System message", "Couldn't open directory! No permission or in use?", NULL);
 	else
 		editor.diskOpReadDir = true;
 }
@@ -710,7 +710,7 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
 	if (f == NULL)
 	{
-		okBox(0, "System message", "Couldn't open file/directory! No permission or in use?");
+		okBox(0, "System message", "Couldn't open file/directory! No permission or in use?", NULL);
 		return;
 	}
 	fclose(f);
@@ -718,13 +718,13 @@
 	const int32_t filesize = getFileSize(filenameU);
 	if (filesize == -1) // >2GB
 	{
-		okBox(0, "System message", "The file is too big and can't be loaded (over 2GB).");
+		okBox(0, "System message", "The file is too big and can't be loaded (over 2GB).", NULL);
 		return;
 	}
 
 	if (filesize >= 128L*1024*1024) // 128MB
 	{
-		if (okBox(2, "System request", "Are you sure you want to load such a big file?") != 1)
+		if (okBox(2, "System request", "Are you sure you want to load such a big file?", NULL) != 1)
 			return;
 	}
 
@@ -740,7 +740,7 @@
 				FReq_EntrySelected = -1;
 				diskOp_DrawFilelist();
 
-				if (okBox(2, "System request", "You have unsaved changes in your song. Load new song and lose all changes?") != 1)
+				if (okBox(2, "System request", "You have unsaved changes in your song. Load new song and lose all changes?", NULL) != 1)
 					return;
 			}
 
@@ -871,7 +871,7 @@
 
 	if (FReq_FileName[0] == '\0')
 	{
-		okBox(0, "System message", "Filename can't be empty!");
+		okBox(0, "System message", "Filename can't be empty!", NULL);
 		return;
 	}
 
@@ -878,7 +878,7 @@
 	// test if the very first character has a dot...
 	if (FReq_FileName[0] == '.')
 	{
-		okBox(0, "System message", "The very first character in the filename can't be '.' (dot)!");
+		okBox(0, "System message", "The very first character in the filename can't be '.' (dot)!", NULL);
 		return;
 	}
 
@@ -885,7 +885,7 @@
 	// test for illegal file name
 	if (FReq_FileName[0] == '\0' || strpbrk(FReq_FileName, "\\/:*?\"<>|") != NULL)
 	{
-		okBox(0, "System message", "The filename can't contain the following characters: \\ / : * ? \" < > |");
+		okBox(0, "System message", "The filename can't contain the following characters: \\ / : * ? \" < > |", NULL);
 		return;
 	}
 
@@ -912,7 +912,7 @@
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
 				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
-				if (okBox(2, "System request", FReq_SysReqText) != 1)
+				if (okBox(2, "System request", FReq_SysReqText, NULL) != 1)
 					return;
 			}
 
@@ -919,7 +919,7 @@
 			fileNameU = cp437ToUnichar(FReq_FileName);
 			if (fileNameU == NULL)
 			{
-				okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+				okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 				return;
 			}
 
@@ -936,7 +936,7 @@
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
 				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
-				if (okBox(2, "System request", FReq_SysReqText) != 1)
+				if (okBox(2, "System request", FReq_SysReqText, NULL) != 1)
 					return;
 			}
 
@@ -943,7 +943,7 @@
 			fileNameU = cp437ToUnichar(FReq_FileName);
 			if (fileNameU == NULL)
 			{
-				okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+				okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 				return;
 			}
 
@@ -965,7 +965,7 @@
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
 				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
-				if (okBox(2, "System request", FReq_SysReqText) != 1)
+				if (okBox(2, "System request", FReq_SysReqText, NULL) != 1)
 					return;
 			}
 
@@ -972,7 +972,7 @@
 			fileNameU = cp437ToUnichar(FReq_FileName);
 			if (fileNameU == NULL)
 			{
-				okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+				okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 				return;
 			}
 
@@ -989,7 +989,7 @@
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
 				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
-				if (okBox(2, "System request", FReq_SysReqText) != 1)
+				if (okBox(2, "System request", FReq_SysReqText, NULL) != 1)
 					return;
 			}
 
@@ -996,7 +996,7 @@
 			fileNameU = cp437ToUnichar(FReq_FileName);
 			if (fileNameU == NULL)
 			{
-				okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+				okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 				return;
 			}
 
@@ -1012,7 +1012,7 @@
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
 				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
-				if (okBox(2, "System request", FReq_SysReqText) != 1)
+				if (okBox(2, "System request", FReq_SysReqText, NULL) != 1)
 					return;
 			}
 
@@ -1019,7 +1019,7 @@
 			fileNameU = cp437ToUnichar(FReq_FileName);
 			if (fileNameU == NULL)
 			{
-				okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+				okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 				return;
 			}
 
@@ -1086,13 +1086,13 @@
 
 				free(nameTmp);
 
-				if (okBox(2, "System request", FReq_SysReqText) == 1)
+				if (okBox(2, "System request", FReq_SysReqText, NULL) == 1)
 				{
 					if (dirEntry->isDir)
 					{
 						result = deleteDirRecursive(dirEntry->nameU);
 						if (!result)
-							okBox(0, "System message", "Couldn't delete folder: Access denied!");
+							okBox(0, "System message", "Couldn't delete folder: Access denied!", NULL);
 						else
 							editor.diskOpReadDir = true;
 					}
@@ -1100,7 +1100,7 @@
 					{
 						result = (UNICHAR_REMOVE(dirEntry->nameU) == 0);
 						if (!result)
-							okBox(0, "System message", "Couldn't delete file: Access denied!");
+							okBox(0, "System message", "Couldn't delete file: Access denied!", NULL);
 						else
 							editor.diskOpReadDir = true;
 					}
@@ -1129,7 +1129,7 @@
 				{
 					if (FReq_NameTemp == NULL || FReq_NameTemp[0] == '\0')
 					{
-						okBox(0, "System message", "New name can't be empty!");
+						okBox(0, "System message", "New name can't be empty!", NULL);
 						break;
 					}
 
@@ -1136,9 +1136,9 @@
 					if (!renameAnsi(dirEntry->nameU, FReq_NameTemp))
 					{
 						if (dirEntry->isDir)
-							okBox(0, "System message", "Couldn't rename directory: Access denied, or dir already exists!");
+							okBox(0, "System message", "Couldn't rename directory: Access denied, or dir already exists!", NULL);
 						else
-							okBox(0, "System message", "Couldn't rename file: Access denied, or file already exists!");
+							okBox(0, "System message", "Couldn't rename file: Access denied, or file already exists!", NULL);
 					}
 					else
 					{
@@ -1675,7 +1675,7 @@
 				{
 					if (p1 != NULL) free(p1);
 					if (p2 != NULL) free(p2);
-					okBox(0, "System message", "Not enough memory!");
+					okBox(0, "System message", "Not enough memory!", NULL);
 					return;
 				}
 
@@ -1771,7 +1771,7 @@
 	char *asciiPath = unicharToCp437(FReq_CurPathU, true);
 	if (asciiPath == NULL)
 	{
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return;
 	}
 
@@ -1934,13 +1934,13 @@
 		{
 			findClose();
 
-			okBoxThreadSafe(0, "System message", "Not enough memory!");
+			okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 
 			FReq_Buffer = bufferCreateEmptyDir();
 			if (FReq_Buffer != NULL)
 				FReq_FileCount = 1;
 			else
-				okBoxThreadSafe(0, "System message", "Not enough memory!");
+				okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 
 			setMouseBusy(false);
 			return false;
@@ -1960,7 +1960,7 @@
 			if (newPtr == NULL)
 			{
 				freeDirRecBuffer();
-				okBoxThreadSafe(0, "System message", "Not enough memory!");
+				okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 				break;
 			}
 
@@ -1984,7 +1984,7 @@
 		if (FReq_Buffer != NULL)
 			FReq_FileCount = 1;
 		else
-			okBoxThreadSafe(0, "System message", "Not enough memory!");
+			okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 	}
 
 	editor.diskOpReadDone = true;
@@ -2004,7 +2004,7 @@
 	if (thread == NULL)
 	{
 		editor.diskOpReadDone = true;
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -2426,7 +2426,7 @@
 	{
 		if (FReq_NameTemp[0] == '\0')
 		{
-			okBox(0, "System message", "Name can't be empty!");
+			okBox(0, "System message", "Name can't be empty!", NULL);
 			return;
 		}
 
@@ -2433,7 +2433,7 @@
 		if (makeDirAnsi(FReq_NameTemp))
 			editor.diskOpReadDir = true;
 		else
-			okBox(0, "System message", "Couldn't create directory: Access denied, or a dir with the same name already exists!");
+			okBox(0, "System message", "Couldn't create directory: Access denied, or a dir with the same name already exists!", NULL);
 	}
 }
 
@@ -2452,7 +2452,7 @@
 	{
 		if (FReq_NameTemp[0] == '\0')
 		{
-			okBox(0, "System message", "Name can't be empty!");
+			okBox(0, "System message", "Name can't be empty!", NULL);
 			return;
 		}
 
@@ -2459,7 +2459,7 @@
 		if (chdir(FReq_NameTemp) == 0)
 			editor.diskOpReadDir = true;
 		else
-			okBox(0, "System message", "Couldn't set directory path!");
+			okBox(0, "System message", "Couldn't set directory path!", NULL);
 	}
 }
 
--- a/src/ft2_edit.c
+++ b/src/ft2_edit.c
@@ -26,19 +26,13 @@
 
 // for block cut/copy/paste
 static bool blockCopied;
-static int16_t markXSize, markYSize;
 static uint16_t ptnBufLen, trkBufLen;
-
-// for transposing - these are set and tested accordingly
-static int8_t lastTranspVal;
-static uint8_t lastInsMode, lastTranspMode;
-static uint32_t transpDelNotes; // count of under-/overflowing notes for warning message
-static note_t clearNote;
-
+static int32_t markXSize, markYSize;
 static note_t blkCopyBuff[MAX_PATT_LEN * MAX_CHANNELS];
 static note_t ptnCopyBuff[MAX_PATT_LEN * MAX_CHANNELS];
 static note_t trackCopyBuff[MAX_PATT_LEN];
 
+// for recordNote()
 static const int8_t tickArr[16] = { 16, 8, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1 };
 
 void recordNote(uint8_t note, int8_t vol);
@@ -52,14 +46,19 @@
 		// inserts "note off" if editing song
 		if (playMode == PLAYMODE_EDIT || playMode == PLAYMODE_RECPATT || playMode == PLAYMODE_RECSONG)
 		{
-			if (!allocatePattern(editor.editPattern))
+			pauseMusic();
+			const volatile uint16_t curPattern = editor.editPattern;
+			int16_t row = editor.row;
+			resumeMusic();
+
+			if (!allocatePattern(curPattern))
 				return true; // key pressed
 
-			pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch].note = NOTE_OFF;
+			pattern[curPattern][(row * MAX_CHANNELS) + cursor.ch].note = NOTE_OFF;
 
-			const uint16_t numRows = patternNumRows[editor.editPattern];
+			const uint16_t numRows = patternNumRows[curPattern];
 			if (playMode == PLAYMODE_EDIT && numRows >= 1)
-				setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
+				setPos(-1, (row + editor.editRowSkip) % numRows, true);
 
 			ui.updatePatternEditor = true;
 			setSongModifiedFlag();
@@ -155,12 +154,17 @@
 			i = -1; // invalid key for slot
 	}
 
-	if (i == -1 || !allocatePattern(editor.editPattern))
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	int16_t row = editor.row;
+	resumeMusic();
+
+	if (i == -1 || !allocatePattern(curPattern))
 		return false; // no edit to be done
 
 	// insert slot data
 
-	note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
+	note_t *p = &pattern[curPattern][(row * MAX_CHANNELS) + cursor.ch];
 	switch (cursor.object)
 	{
 		case CURSOR_INST1:
@@ -251,12 +255,12 @@
 
 	// increase row (only in edit mode)
 
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	const int16_t numRows = patternNumRows[curPattern];
 	if (playMode == PLAYMODE_EDIT && numRows >= 1)
-		setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
+		setPos(-1, (row + editor.editRowSkip) % numRows, true);
 
-	if (i == 0) // if we inserted a zero, check if pattern is empty, for killing
-		killPatternIfUnused(editor.editPattern);
+	if (i == 0) // if we inserted a zero, check if pattern is empty
+		killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	return true;
@@ -269,7 +273,7 @@
 	int16_t outRow = editor.row;
 	int16_t outTick = editor.speed - editor.tick;
 
-	outTick = CLAMP(outTick, 0, editor.speed - 1);
+	outTick = CLAMP(outTick, 0, editor.speed-1);
 
 	// this is needed, but also breaks quantization on speed>15
 	if (outTick > 15)
@@ -359,7 +363,7 @@
 
 		if ((config.multiEdit && editmode) || (config.multiRec && recmode))
 		{
-			time = 0x7FFFFFFF;
+			time = INT32_MAX;
 			for (i = 0; i < song.numChannels; i++)
 			{
 				if (editor.chnMode[i] && config.multiRecChn[i] && editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0)
@@ -385,7 +389,7 @@
 		// find out what channel is the most suitable in idle/play mode (jamming)
 		if (config.multiKeyJazz)
 		{
-			time = 0x7FFFFFFF;
+			time = INT32_MAX;
 			c = 0;
 
 			if (songPlaying)
@@ -400,7 +404,7 @@
 				}
 			}
 
-			if (time == 0x7FFFFFFF)
+			if (time == INT32_MAX)
 			{
 				for (i = 0; i < song.numChannels; i++)
 				{
@@ -563,10 +567,15 @@
 		if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT)
 			return false; // we're not editing, test other keys
 
-		if (pattern[editor.editPattern] == NULL)
+		pauseMusic();
+		const volatile uint16_t curPattern = editor.editPattern;
+		int16_t row = editor.row;
+		resumeMusic();
+
+		if (pattern[curPattern] == NULL)
 			return true;
 
-		note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
+		note_t *p = &pattern[curPattern][(row * MAX_CHANNELS) + cursor.ch];
 
 		if (keyb.leftShiftPressed)
 		{
@@ -601,12 +610,12 @@
 			}
 		}
 
-		killPatternIfUnused(editor.editPattern);
+		killPatternIfUnused(curPattern);
 
 		// increase row (only in edit mode)
-		const int16_t numRows = patternNumRows[editor.editPattern];
+		const int16_t numRows = patternNumRows[curPattern];
 		if (playMode == PLAYMODE_EDIT && numRows >= 1)
-			setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
+			setPos(-1, (row + editor.editRowSkip) % numRows, true);
 
 		ui.updatePatternEditor = true;
 		setSongModifiedFlag();
@@ -626,12 +635,17 @@
 
 void writeToMacroSlot(uint8_t slot)
 {
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	int16_t row = editor.row;
+	resumeMusic();
+
 	uint16_t writeVol = 0;
 	uint16_t writeEfx = 0;
 
-	if (pattern[editor.editPattern] != NULL)
+	if (pattern[curPattern] != NULL)
 	{
-		note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
+		note_t *p = &pattern[curPattern][(row * MAX_CHANNELS) + cursor.ch];
 		writeVol = p->vol;
 		writeEfx = (p->efx << 8) | p->efxData;
 	}
@@ -644,13 +658,18 @@
 
 void writeFromMacroSlot(uint8_t slot)
 {
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	int16_t row = editor.row;
+	resumeMusic();
+
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT)
 		return;
 
-	if (!allocatePattern(editor.editPattern))
+	if (!allocatePattern(curPattern))
 		return;
 	
-	note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
+	note_t *p = &pattern[curPattern][(row * MAX_CHANNELS) + cursor.ch];
 	if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
 	{
 		p->vol = (uint8_t)config.volMacro[slot];
@@ -671,11 +690,11 @@
 		}
 	}
 
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	const int16_t numRows = patternNumRows[curPattern];
 	if (playMode == PLAYMODE_EDIT && numRows >= 1)
-		setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
+		setPos(-1, (row + editor.editRowSkip) % numRows, true);
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -683,15 +702,19 @@
 
 void insertPatternNote(void)
 {
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	int16_t row = editor.row;
+	resumeMusic();
+
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
 		return;
 
-	note_t *p = pattern[editor.editPattern];
+	note_t *p = pattern[curPattern];
 	if (p == NULL)
 		return;
 
-	const int16_t row = editor.row;
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	const int16_t numRows = patternNumRows[curPattern];
 
 	if (numRows > 1)
 	{
@@ -701,7 +724,7 @@
 
 	memset(&p[(row * MAX_CHANNELS) + cursor.ch], 0, sizeof (note_t));
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -709,16 +732,20 @@
 
 void insertPatternLine(void)
 {
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	int16_t row = editor.row;
+	resumeMusic();
+
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
 		return;
 
-	setPatternLen(editor.editPattern, patternNumRows[editor.editPattern] + config.recTrueInsert); // config.recTrueInsert is 0 or 1
+	setPatternLen(curPattern, patternNumRows[curPattern] + config.recTrueInsert); // config.recTrueInsert is 0 or 1
 
-	note_t *p = pattern[editor.editPattern];
+	note_t *p = pattern[curPattern];
 	if (p != NULL)
 	{
-		const int16_t row = editor.row;
-		const int16_t numRows = patternNumRows[editor.editPattern];
+		const int16_t numRows = patternNumRows[curPattern];
 
 		if (numRows > 1)
 		{
@@ -731,7 +758,7 @@
 
 		memset(&p[row * MAX_CHANNELS], 0, TRACK_WIDTH);
 
-		killPatternIfUnused(editor.editPattern);
+		killPatternIfUnused(curPattern);
 	}
 
 	ui.updatePatternEditor = true;
@@ -740,13 +767,17 @@
 
 void deletePatternNote(void)
 {
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	int16_t row = editor.row;
+	resumeMusic();
+
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
 		return;
 
-	int16_t row = editor.row;
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	const int16_t numRows = patternNumRows[curPattern];
 
-	note_t *p = pattern[editor.editPattern];
+	note_t *p = pattern[curPattern];
 	if (p != NULL)
 	{
 		if (row > 0)
@@ -769,7 +800,7 @@
 		}
 	}
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -777,13 +808,16 @@
 
 void deletePatternLine(void)
 {
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	int16_t row = editor.row;
+	resumeMusic();
+
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
 		return;
 
-	int16_t row = editor.row;
-	const int16_t numRows = patternNumRows[editor.editPattern];
-
-	note_t *p = pattern[editor.editPattern];
+	const int16_t numRows = patternNumRows[curPattern];
+	note_t *p = pattern[curPattern];
 	if (p != NULL)
 	{
 		if (row > 0)
@@ -810,9 +844,9 @@
 	}
 
 	if (config.recTrueInsert && numRows > 1)
-		setPatternLen(editor.editPattern, numRows-1);
+		setPatternLen(curPattern, numRows-1);
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -820,26 +854,28 @@
 
 // ----- TRANSPOSE FUNCTIONS -----
 
-static void countOverflowingNotes(uint8_t currInsOnly, uint8_t transpMode, int8_t addVal)
+static uint32_t countOverflowingNotes(uint8_t mode, int8_t addValue, bool allInstrumentsFlag,
+	uint16_t curPattern, int32_t numRows, int32_t markX1, int32_t markX2, int32_t markY1, int32_t markY2)
 {
-	transpDelNotes = 0;
-	switch (transpMode)
+	uint32_t notesToDelete = 0;
+
+	// "addValue" is never <-12 or >12, so unsigned 8-bit testing for >96 is safe
+	switch (mode)
 	{
 		case TRANSP_TRACK:
 		{
-			note_t *p = pattern[editor.editPattern];
+			note_t *p = pattern[curPattern];
 			if (p == NULL)
-				return; // empty pattern
+				return 0; // empty pattern
 
 			p += cursor.ch;
 
-			const int32_t numRows = patternNumRows[editor.editPattern];
 			for (int32_t row = 0; row < numRows; row++, p += MAX_CHANNELS)
 			{
-				if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
+				if ((p->note >= 1 && p->note <= 96) && (allInstrumentsFlag || p->instr == editor.curInstr))
 				{
-					if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
-						transpDelNotes++;
+					if ((int8_t)p->note+addValue > 96 || (int8_t)p->note+addValue <= 0)
+						notesToDelete++;
 				}
 			}
 		}
@@ -847,21 +883,19 @@
 
 		case TRANSP_PATT:
 		{
-			note_t *p = pattern[editor.editPattern];
+			note_t *p = pattern[curPattern];
 			if (p == NULL)
-				return; // empty pattern
+				return 0; // empty pattern
 
-			const int32_t numRows = patternNumRows[editor.editPattern];
 			const int32_t pitch = MAX_CHANNELS-song.numChannels;
-
 			for (int32_t row = 0; row < numRows; row++, p += pitch)
 			{
 				for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
 				{
-					if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
+					if ((p->note >= 1 && p->note <= 96) && (allInstrumentsFlag || p->instr == editor.curInstr))
 					{
-						if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
-							transpDelNotes++;
+						if ((int8_t)p->note+addValue > 96 || (int8_t)p->note+addValue <= 0)
+							notesToDelete++;
 					}
 				}
 			}
@@ -870,22 +904,21 @@
 
 		case TRANSP_SONG:
 		{
-			const int32_t pitch = MAX_CHANNELS-song.numChannels;
+			const int32_t pitch = MAX_CHANNELS - song.numChannels;
 			for (int32_t i = 0; i < MAX_PATTERNS; i++)
 			{
 				note_t *p = pattern[i];
 				if (p == NULL)
-					continue; // empty pattern
+					return 0; // empty pattern
 
-				const int32_t numRows = patternNumRows[i];
-				for (int32_t row = 0; row < numRows; row++, p += pitch)
+				for (int32_t row = 0; row < patternNumRows[i]; row++, p += pitch)
 				{
 					for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
 					{
-						if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
+						if ((p->note >= 1 && p->note <= 96) && (allInstrumentsFlag || p->instr == editor.curInstr))
 						{
-							if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
-								transpDelNotes++;
+							if ((int8_t)p->note+addValue > 96 || (int8_t)p->note+addValue <= 0)
+								notesToDelete++;
 						}
 					}
 				}
@@ -895,24 +928,36 @@
 
 		case TRANSP_BLOCK:
 		{
-			if (pattMark.markY1 == pattMark.markY2)
-				return; // no pattern marking
+			if (markY1 == markY2 || markY1 > markY2)
+				return 0;
 
-			note_t *p = pattern[editor.editPattern];
-			if (p == NULL)
-				return; // empty pattern
+			if (markX1 >= song.numChannels-1)
+				markX1 = song.numChannels-2;
 
-			p += (pattMark.markY1 * MAX_CHANNELS) + pattMark.markX1;
+			if (markX2 >= song.numChannels)
+				markX2 = (song.numChannels-1)-markX1;
 
-			const int32_t pitch = MAX_CHANNELS - ((pattMark.markX2 + 1) - pattMark.markX1);
-			for (int32_t row = pattMark.markY1; row < pattMark.markY2; row++, p += pitch)
+			if (markY1 >= numRows)
+				markY1 = numRows-1;
+
+			if (markY2 > numRows)
+				markY2 = numRows-markY1;
+
+			note_t *p = pattern[curPattern];
+			if (p == NULL || markX1 < 0 || markY1 < 0 || markX2 < 0 || markY2 < 0)
+				return 0;
+
+			p += (markY1 * MAX_CHANNELS) + markX1;
+
+			const int32_t pitch = MAX_CHANNELS - ((markX2 + 1) - markX1);
+			for (int32_t row = markY1; row < markY2; row++, p += pitch)
 			{
-				for (int32_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++, p++)
+				for (int32_t ch = markX1; ch <= markX2; ch++, p++)
 				{
-					if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
+					if ((p->note >= 1 && p->note <= 96) && (allInstrumentsFlag || p->instr == editor.curInstr))
 					{
-						if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
-							transpDelNotes++;
+						if ((int8_t)p->note+addValue > 96 || (int8_t)p->note+addValue <= 0)
+							notesToDelete++;
 					}
 				}
 			}
@@ -921,38 +966,48 @@
 
 		default: break;
 	}
+
+	return notesToDelete;
 }
 
-void doTranspose(void)
+static void doTranspose(uint8_t mode, int8_t addValue, bool allInstrumentsFlag)
 {
-	char text[48];
-
-	countOverflowingNotes(lastInsMode, lastTranspMode, lastTranspVal);
-	if (transpDelNotes > 0)
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	const int32_t numRows = patternNumRows[curPattern];
+	volatile int32_t markX1 = pattMark.markX1;
+	volatile int32_t markX2 = pattMark.markX2;
+	volatile int32_t markY1 = pattMark.markY1;
+	volatile int32_t markY2 = pattMark.markY2;
+	resumeMusic();
+	
+	uint32_t overflowingNotes = countOverflowingNotes(mode, addValue, allInstrumentsFlag,
+		curPattern, numRows, markX1, markX2, markY1, markY2);
+	if (overflowingNotes > 0)
 	{
-		sprintf(text, "%d note(s) will be erased! Proceed?", (int32_t)transpDelNotes);
-		if (okBox(2, "System request", text) != 1)
+		char text[48];
+		sprintf(text, "%u note(s) will be erased! Proceed?", overflowingNotes);
+		if (okBox(2, "System request", text, NULL) != 1)
 			return;
 	}
 
-	// lastTranspVal is never <-12 or >12, so unsigned testing for >96 is safe
-	switch (lastTranspMode)
+	// "addValue" is never <-12 or >12, so unsigned 8-bit testing for >96 is safe
+	switch (mode)
 	{
 		case TRANSP_TRACK:
 		{
-			note_t *p = pattern[editor.editPattern];
+			note_t *p = pattern[curPattern];
 			if (p == NULL)
-				return; // empty pattern
+				return;
 
 			p += cursor.ch;
 
-			const int32_t numRows = patternNumRows[editor.editPattern];
 			for (int32_t row = 0; row < numRows; row++, p += MAX_CHANNELS)
 			{
-				uint8_t note = p->note;
-				if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
+				volatile uint8_t note = p->note;
+				if ((note >= 1 && note <= 96) && (allInstrumentsFlag || p->instr == editor.curInstr))
 				{
-					note += lastTranspVal;
+					note += addValue;
 					if (note > 96)
 						note = 0; // also handles underflow
 
@@ -964,21 +1019,19 @@
 
 		case TRANSP_PATT:
 		{
-			note_t *p = pattern[editor.editPattern];
+			note_t *p = pattern[curPattern];
 			if (p == NULL)
-				return; // empty pattern
+				return;
 
-			const int32_t numRows = patternNumRows[editor.editPattern];
 			const int32_t pitch = MAX_CHANNELS - song.numChannels;
-
 			for (int32_t row = 0; row < numRows; row++, p += pitch)
 			{
 				for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
 				{
-					uint8_t note = p->note;
-					if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
+					volatile uint8_t note = p->note;
+					if ((note >= 1 && note <= 96) && (allInstrumentsFlag || p->instr == editor.curInstr))
 					{
-						note += lastTranspVal;
+						note += addValue;
 						if (note > 96)
 							note = 0; // also handles underflow
 
@@ -998,15 +1051,14 @@
 				if (p == NULL)
 					continue; // empty pattern
 
-				const int32_t numRows = patternNumRows[i];
-				for (int32_t row = 0; row < numRows; row++, p += pitch)
+				for (int32_t row = 0; row < patternNumRows[i]; row++, p += pitch)
 				{
 					for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
 					{
-						uint8_t note = p->note;
-						if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
+						volatile uint8_t note = p->note;
+						if ((note >= 1 && note <= 96) && (allInstrumentsFlag || p->instr == editor.curInstr))
 						{
-							note += lastTranspVal;
+							note += addValue;
 							if (note > 96)
 								note = 0; // also handles underflow
 
@@ -1020,24 +1072,36 @@
 
 		case TRANSP_BLOCK:
 		{
-			if (pattMark.markY1 == pattMark.markY2)
-				return; // no pattern marking
+			if (markY1 == markY2 || markY1 > markY2)
+				return;
 
-			note_t *p = pattern[editor.editPattern];
-			if (p == NULL)
-				return; // empty pattern
+			if (markX1 >= song.numChannels-1)
+				markX1 = song.numChannels-2;
 
-			p += (pattMark.markY1 * MAX_CHANNELS) + pattMark.markX1;
+			if (markX2 >= song.numChannels)
+				markX2 = (song.numChannels-1)-markX1;
 
-			const int32_t pitch = MAX_CHANNELS - ((pattMark.markX2 + 1) - pattMark.markX1);
-			for (int32_t row = pattMark.markY1; row < pattMark.markY2; row++, p += pitch)
+			if (markY1 >= numRows)
+				markY1 = numRows-1;
+
+			if (markY2 > numRows)
+				markY2 = numRows-markY1;
+
+			note_t *p = pattern[curPattern];
+			if (p == NULL || markX1 < 0 || markY1 < 0 || markX2 < 0 || markY2 < 0)
+				return;
+
+			p += (markY1 * MAX_CHANNELS) + markX1;
+
+			const int32_t pitch = MAX_CHANNELS - ((markX2 + 1) - markX1);
+			for (int32_t row = markY1; row < markY2; row++, p += pitch)
 			{
-				for (int32_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++, p++)
+				for (int32_t ch = markX1; ch <= markX2; ch++, p++)
 				{
-					uint8_t note = p->note;
-					if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
+					volatile uint8_t note = p->note;
+					if ((note >= 1 && note <= 96) && (allInstrumentsFlag || p->instr == editor.curInstr))
 					{
-						note += lastTranspVal;
+						note += addValue;
 						if (note > 96)
 							note = 0; // also handles underflow
 
@@ -1057,269 +1121,182 @@
 
 void trackTranspCurInsUp(void)
 {
-	lastTranspMode = TRANSP_TRACK;
-	lastTranspVal = 1;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_TRACK, 1, TRANSP_CUR_INSTRUMENT);
 }
 
 void trackTranspCurInsDn(void)
 {
-	lastTranspMode = TRANSP_TRACK;
-	lastTranspVal = -1;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_TRACK, -1, TRANSP_CUR_INSTRUMENT);
 }
 
 void trackTranspCurIns12Up(void)
 {
-	lastTranspMode = TRANSP_TRACK;
-	lastTranspVal = 12;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_TRACK, 12, TRANSP_CUR_INSTRUMENT);
 }
 
 void trackTranspCurIns12Dn(void)
 {
-	lastTranspMode = TRANSP_TRACK;
-	lastTranspVal = -12;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_TRACK, -12, TRANSP_CUR_INSTRUMENT);
 }
 
 void trackTranspAllInsUp(void)
 {
-	lastTranspMode = TRANSP_TRACK;
-	lastTranspVal = 1;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_TRACK, 1, TRANSP_ALL_INSTRUMENTS);
 }
 
 void trackTranspAllInsDn(void)
 {
-	lastTranspMode = TRANSP_TRACK;
-	lastTranspVal = -1;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_TRACK, -1, TRANSP_ALL_INSTRUMENTS);
 }
 
 void trackTranspAllIns12Up(void)
 {
-	lastTranspMode = TRANSP_TRACK;
-	lastTranspVal = 12;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_TRACK, 12, TRANSP_ALL_INSTRUMENTS);
 }
 
 void trackTranspAllIns12Dn(void)
 {
-	lastTranspMode = TRANSP_TRACK;
-	lastTranspVal = -12;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_TRACK, -12, TRANSP_ALL_INSTRUMENTS);
 }
 
 void pattTranspCurInsUp(void)
 {
-	lastTranspMode = TRANSP_PATT;
-	lastTranspVal = 1;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_PATT, 1, TRANSP_CUR_INSTRUMENT);
 }
 
 void pattTranspCurInsDn(void)
 {
-	lastTranspMode = TRANSP_PATT;
-	lastTranspVal = -1;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_PATT, -1, TRANSP_CUR_INSTRUMENT);
 }
 
 void pattTranspCurIns12Up(void)
 {
-	lastTranspMode = TRANSP_PATT;
-	lastTranspVal = 12;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_PATT, 12, TRANSP_CUR_INSTRUMENT);
 }
 
 void pattTranspCurIns12Dn(void)
 {
-	lastTranspMode = TRANSP_PATT;
-	lastTranspVal = -12;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_PATT, -12, TRANSP_CUR_INSTRUMENT);
 }
 
 void pattTranspAllInsUp(void)
 {
-	lastTranspMode = TRANSP_PATT;
-	lastTranspVal = 1;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_PATT, 1, TRANSP_ALL_INSTRUMENTS);
 }
 
 void pattTranspAllInsDn(void)
 {
-	lastTranspMode = TRANSP_PATT;
-	lastTranspVal = -1;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_PATT, -1, TRANSP_ALL_INSTRUMENTS);
 }
 
 void pattTranspAllIns12Up(void)
 {
-	lastTranspMode = TRANSP_PATT;
-	lastTranspVal = 12;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_PATT, 12, TRANSP_ALL_INSTRUMENTS);
 }
 
 void pattTranspAllIns12Dn(void)
 {
-	lastTranspMode = TRANSP_PATT;
-	lastTranspVal = -12;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_PATT, -12, TRANSP_ALL_INSTRUMENTS);
 }
 
 void songTranspCurInsUp(void)
 {
-	lastTranspMode = TRANSP_SONG;
-	lastTranspVal = 1;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_SONG, 1, TRANSP_CUR_INSTRUMENT);
 }
 
 void songTranspCurInsDn(void)
 {
-	lastTranspMode = TRANSP_SONG;
-	lastTranspVal = -1;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_SONG, -1, TRANSP_CUR_INSTRUMENT);
 }
 
 void songTranspCurIns12Up(void)
 {
-	lastTranspMode = TRANSP_SONG;
-	lastTranspVal = 12;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_SONG, 12, TRANSP_CUR_INSTRUMENT);
 }
 
 void songTranspCurIns12Dn(void)
 {
-	lastTranspMode = TRANSP_SONG;
-	lastTranspVal = -12;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_SONG, -12, TRANSP_CUR_INSTRUMENT);
 }
 
 void songTranspAllInsUp(void)
 {
-	lastTranspMode = TRANSP_SONG;
-	lastTranspVal = 1;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_SONG, 1, TRANSP_ALL_INSTRUMENTS);
 }
 
 void songTranspAllInsDn(void)
 {
-	lastTranspMode = TRANSP_SONG;
-	lastTranspVal = -1;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_SONG, -1, TRANSP_ALL_INSTRUMENTS);
 }
 
 void songTranspAllIns12Up(void)
 {
-	lastTranspMode = TRANSP_SONG;
-	lastTranspVal = 12;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_SONG, 12, TRANSP_ALL_INSTRUMENTS);
 }
 
 void songTranspAllIns12Dn(void)
 {
-	lastTranspMode = TRANSP_SONG;
-	lastTranspVal = -12;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_SONG, -12, TRANSP_ALL_INSTRUMENTS);
 }
 
 void blockTranspCurInsUp(void)
 {
-	lastTranspMode = TRANSP_BLOCK;
-	lastTranspVal = 1;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_BLOCK, 1, TRANSP_CUR_INSTRUMENT);
 }
 
 void blockTranspCurInsDn(void)
 {
-	lastTranspMode = TRANSP_BLOCK;
-	lastTranspVal = -1;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_BLOCK, -1, TRANSP_CUR_INSTRUMENT);
 }
 
 void blockTranspCurIns12Up(void)
 {
-	lastTranspMode = TRANSP_BLOCK;
-	lastTranspVal = 12;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_BLOCK, 12, TRANSP_CUR_INSTRUMENT);
 }
 
 void blockTranspCurIns12Dn(void)
 {
-	lastTranspMode = TRANSP_BLOCK;
-	lastTranspVal = -12;
-	lastInsMode = TRANSP_CUR_INST;
-	doTranspose();
+	doTranspose(TRANSP_BLOCK, -12, TRANSP_CUR_INSTRUMENT);
 }
 
 void blockTranspAllInsUp(void)
 {
-	lastTranspMode = TRANSP_BLOCK;
-	lastTranspVal = 1;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_BLOCK, 1, TRANSP_ALL_INSTRUMENTS);
 }
 
 void blockTranspAllInsDn(void)
 {
-	lastTranspMode = TRANSP_BLOCK;
-	lastTranspVal = -1;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_BLOCK, -1, TRANSP_ALL_INSTRUMENTS);
 }
 
 void blockTranspAllIns12Up(void)
 {
-	lastTranspMode = TRANSP_BLOCK;
-	lastTranspVal = 12;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_BLOCK, 12, TRANSP_ALL_INSTRUMENTS);
 }
 
 void blockTranspAllIns12Dn(void)
 {
-	lastTranspMode = TRANSP_BLOCK;
-	lastTranspVal = -12;
-	lastInsMode = TRANSP_ALL_INST;
-	doTranspose();
+	doTranspose(TRANSP_BLOCK, -12, TRANSP_ALL_INSTRUMENTS);
 }
 
-void copyNote(note_t *src, note_t *dst)
+static void copyNote(note_t *src, note_t *dst)
 {
 	if (editor.copyMaskEnable)
 	{
-		if (editor.copyMask[0]) dst->note = src->note;
-		if (editor.copyMask[1]) dst->instr = src->instr;
-		if (editor.copyMask[2]) dst->vol = src->vol;
-		if (editor.copyMask[3]) dst->efx = src->efx;
-		if (editor.copyMask[4]) dst->efxData = src->efxData;
+		if (editor.copyMask[0])
+			dst->note = src->note;
+
+		if (editor.copyMask[1])
+			dst->instr = src->instr;
+
+		if (editor.copyMask[2])
+			dst->vol = src->vol;
+
+		if (editor.copyMask[3])
+			dst->efx = src->efx;
+
+		if (editor.copyMask[4])
+			dst->efxData = src->efxData;
 	}
 	else
 	{
@@ -1327,15 +1304,24 @@
 	}
 }
 
-void pasteNote(note_t *src, note_t *dst)
+static void pasteNote(note_t *src, note_t *dst)
 {
 	if (editor.copyMaskEnable)
 	{
-		if (editor.copyMask[0] && (src->note    != 0 || !editor.transpMask[0])) dst->note = src->note;
-		if (editor.copyMask[1] && (src->instr   != 0 || !editor.transpMask[1])) dst->instr = src->instr;
-		if (editor.copyMask[2] && (src->vol     != 0 || !editor.transpMask[2])) dst->vol = src->vol;
-		if (editor.copyMask[3] && (src->efx     != 0 || !editor.transpMask[3])) dst->efx = src->efx;
-		if (editor.copyMask[4] && (src->efxData != 0 || !editor.transpMask[4])) dst->efxData = src->efxData;
+		if (editor.copyMask[0] && (src->note != 0 || !editor.transpMask[0]))
+			dst->note = src->note;
+
+		if (editor.copyMask[1] && (src->instr != 0 || !editor.transpMask[1]))
+			dst->instr = src->instr;
+
+		if (editor.copyMask[2] && (src->vol != 0 || !editor.transpMask[2]))
+			dst->vol = src->vol;
+
+		if (editor.copyMask[3] && (src->efx != 0 || !editor.transpMask[3]))
+			dst->efx = src->efx;
+
+		if (editor.copyMask[4] && (src->efxData != 0 || !editor.transpMask[4]))
+			dst->efxData = src->efxData;
 	}
 	else
 	{
@@ -1345,15 +1331,18 @@
 
 void cutTrack(void)
 {
-	note_t *p = pattern[editor.editPattern];
+	const volatile uint16_t curPattern = editor.editPattern;
+
+	note_t *p = pattern[curPattern];
 	if (p == NULL)
 		return;
 
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	const int16_t numRows = patternNumRows[curPattern];
 
 	if (config.ptnCutToBuffer)
 	{
-		memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (note_t));
+		memset(trackCopyBuff, 0, sizeof (trackCopyBuff));
+
 		for (int16_t i = 0; i < numRows; i++)
 			copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]);
 
@@ -1362,10 +1351,10 @@
 
 	pauseMusic();
 	for (int16_t i = 0; i < numRows; i++)
-		pasteNote(&clearNote, &p[(i * MAX_CHANNELS) + cursor.ch]);
+		memset(&p[(i * MAX_CHANNELS) + cursor.ch], 0, sizeof (note_t));
 	resumeMusic();
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -1373,26 +1362,30 @@
 
 void copyTrack(void)
 {
-	note_t *p = pattern[editor.editPattern];
-	if (p == NULL)
-		return;
+	const volatile uint16_t curPattern = editor.editPattern;
 
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	note_t *p = pattern[curPattern];
+	if (p != NULL)
+	{
+		memset(trackCopyBuff, 0, sizeof (trackCopyBuff));
 
-	memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (note_t));
-	for (int16_t i = 0; i < numRows; i++)
-		copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]);
+		const int16_t numRows = patternNumRows[curPattern];
+		for (int16_t i = 0; i < numRows; i++)
+			copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]);
 
-	trkBufLen = numRows;
+		trkBufLen = numRows;
+	}
 }
 
 void pasteTrack(void)
 {
-	if (trkBufLen == 0 || !allocatePattern(editor.editPattern))
+	const volatile uint16_t curPattern = editor.editPattern;
+
+	if (trkBufLen == 0 || !allocatePattern(curPattern))
 		return;
 
-	note_t *p = pattern[editor.editPattern];
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	note_t *p = pattern[curPattern];
+	const int16_t numRows = patternNumRows[curPattern];
 
 	pauseMusic();
 	for (int16_t i = 0; i < numRows; i++)
@@ -1399,7 +1392,7 @@
 		pasteNote(&trackCopyBuff[i], &p[(i * MAX_CHANNELS) + cursor.ch]);
 	resumeMusic();
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -1407,15 +1400,18 @@
 
 void cutPattern(void)
 {
-	note_t *p = pattern[editor.editPattern];
+	const volatile uint16_t curPattern = editor.editPattern;
+
+	note_t *p = pattern[curPattern];
 	if (p == NULL)
 		return;
 
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	const int16_t numRows = patternNumRows[curPattern];
 
 	if (config.ptnCutToBuffer)
 	{
 		memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t));
+
 		for (int16_t x = 0; x < song.numChannels; x++)
 		{
 			for (int16_t i = 0; i < numRows; i++)
@@ -1429,11 +1425,11 @@
 	for (int16_t x = 0; x < song.numChannels; x++)
 	{
 		for (int16_t i = 0; i < numRows; i++)
-			pasteNote(&clearNote, &p[(i * MAX_CHANNELS) + x]);
+			memset(&p[(i * MAX_CHANNELS) + x], 0, sizeof (note_t));
 	}
 	resumeMusic();
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -1441,41 +1437,43 @@
 
 void copyPattern(void)
 {
-	note_t *p = pattern[editor.editPattern];
-	if (p == NULL)
-		return;
+	const volatile uint16_t curPattern = editor.editPattern;
 
-	const int16_t numRows = patternNumRows[editor.editPattern];
-
-	memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t));
-	for (int16_t x = 0; x < song.numChannels; x++)
+	note_t *p = pattern[curPattern];
+	if (p != NULL)
 	{
-		for (int16_t i = 0; i < numRows; i++)
-			copyNote(&p[(i * MAX_CHANNELS) + x], &ptnCopyBuff[(i * MAX_CHANNELS) + x]);
-	}
+		memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t));
 
-	ptnBufLen = numRows;
+		const int16_t numRows = patternNumRows[curPattern];
+		for (int16_t x = 0; x < song.numChannels; x++)
+		{
+			for (int16_t i = 0; i < numRows; i++)
+				copyNote(&p[(i * MAX_CHANNELS) + x], &ptnCopyBuff[(i * MAX_CHANNELS) + x]);
+		}
 
-	ui.updatePatternEditor = true;
+		ptnBufLen = numRows;
+	}
 }
 
 void pastePattern(void)
 {
+	const volatile uint16_t curPattern = editor.editPattern;
+
 	if (ptnBufLen == 0)
 		return;
 
-	if (patternNumRows[editor.editPattern] != ptnBufLen)
+	if (patternNumRows[curPattern] != ptnBufLen)
 	{
-		if (okBox(1, "System request", "Change pattern length to copybuffer's length?") == 1)
-			setPatternLen(editor.editPattern, ptnBufLen);
+		if (okBox(2, "System request", "Adjust pattern length to match copied pattern length?", NULL) == 1)
+			setPatternLen(curPattern, ptnBufLen);
 	}
 
-	if (!allocatePattern(editor.editPattern))
+	if (!allocatePattern(curPattern))
 		return;
 
-	note_t *p = pattern[editor.editPattern];
-	const int16_t numRows = patternNumRows[editor.editPattern];
-
+	note_t *p = pattern[curPattern];
+	const int16_t numRows = patternNumRows[curPattern];
+	
 	pauseMusic();
 	for (int16_t x = 0; x < song.numChannels; x++)
 	{
@@ -1484,7 +1482,7 @@
 	}
 	resumeMusic();
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -1492,117 +1490,177 @@
 
 void cutBlock(void)
 {
-	if (pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
-		return;
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	volatile int32_t markX1 = pattMark.markX1;
+	volatile int32_t markX2 = pattMark.markX2;
+	volatile int32_t markY1 = pattMark.markY1;
+	volatile int32_t markY2 = pattMark.markY2;
+	resumeMusic();
 
-	note_t *p = pattern[editor.editPattern];
-	if (p == NULL)
+	const int16_t numRows = patternNumRows[curPattern];
+
+	if (markY1 == markY2 || markY1 > markY2)
 		return;
 
-	if (config.ptnCutToBuffer)
+	if (markX1 >= song.numChannels-1)
+		markX1 = song.numChannels-2;
+
+	if (markX2 >= song.numChannels)
+		markX2 = (song.numChannels-1)-markX1;
+
+	if (markY1 >= numRows)
+		markY1 = numRows-1;
+
+	if (markY2 > numRows)
+		markY2 = numRows-markY1;
+
+	note_t *p = pattern[curPattern];
+	if (p != NULL && markY1 >= 0 && markX1 >= 0 && markX2 >= 0 && markY2 >= 0)
 	{
-		for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++)
+		pauseMusic();
+		for (int32_t x = markX1; x < markX2; x++)
 		{
-			for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
+			for (int32_t y = markY1; y < markY2; y++)
 			{
-				assert(x < song.numChannels && y < patternNumRows[editor.editPattern]);
-				copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - pattMark.markY1) * MAX_CHANNELS) + (x - pattMark.markX1)]);
+				note_t *n = &p[(y * MAX_CHANNELS) + x];
+
+				if (config.ptnCutToBuffer)
+					copyNote(n, &blkCopyBuff[((y - markY1) * MAX_CHANNELS) + (x - markX1)]);
+
+				memset(n, 0, sizeof (note_t));
 			}
 		}
-	}
+		resumeMusic();
 
-	pauseMusic();
-	for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++)
-	{
-		for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
-			pasteNote(&clearNote, &p[(y * MAX_CHANNELS) + x]);
-	}
-	resumeMusic();
+		killPatternIfUnused(curPattern);
 
-	markXSize = pattMark.markX2 - pattMark.markX1;
-	markYSize = pattMark.markY2 - pattMark.markY1;
-	blockCopied = true;
+		if (config.ptnCutToBuffer)
+		{
+			markXSize = markX2 - markX1;
+			markYSize = markY2 - markY1;
+			blockCopied = true;
+		}
 
-	killPatternIfUnused(editor.editPattern);
-
-	ui.updatePatternEditor = true;
-	setSongModifiedFlag();
+		ui.updatePatternEditor = true;
+		setSongModifiedFlag();
+	}
 }
 
 void copyBlock(void)
 {
-	if (pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
-		return;
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	volatile int32_t markX1 = pattMark.markX1;
+	volatile int32_t markX2 = pattMark.markX2;
+	volatile int32_t markY1 = pattMark.markY1;
+	volatile int32_t markY2 = pattMark.markY2;
+	resumeMusic();
 
-	note_t *p = pattern[editor.editPattern];
-	if (p == NULL)
+	const int16_t numRows = patternNumRows[curPattern];
+
+	if (markY1 == markY2 || markY1 > markY2)
 		return;
 
-	for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++)
+	if (markX1 >= song.numChannels-1)
+		markX1 = song.numChannels-2;
+
+	if (markX2 >= song.numChannels)
+		markX2 = (song.numChannels-1)-markX1;
+
+	if (markY1 >= numRows)
+		markY1 = numRows-1;
+
+	if (markY2 > numRows)
+		markY2 = numRows-markY1;
+
+	note_t *p = pattern[curPattern];
+	if (p != NULL && markY1 >= 0 && markX1 >= 0 && markX2 >= 0 && markY2 >= 0)
 	{
-		for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
+		for (int32_t x = markX1; x < markX2; x++)
 		{
-			assert(x < song.numChannels && y < patternNumRows[editor.editPattern]);
-			copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - pattMark.markY1) * MAX_CHANNELS) + (x - pattMark.markX1)]);
+			for (int32_t y = markY1; y < markY2; y++)
+				copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - markY1) * MAX_CHANNELS) + (x - markX1)]);
 		}
-	}
 
-	markXSize = pattMark.markX2 - pattMark.markX1;
-	markYSize = pattMark.markY2 - pattMark.markY1;
-	blockCopied = true;
+		markXSize = markX2 - markX1;
+		markYSize = markY2 - markY1;
+		blockCopied = true;
+	}
 }
 
 void pasteBlock(void)
 {
-	if (!blockCopied || !allocatePattern(editor.editPattern))
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	const volatile uint16_t curRow = editor.row;
+	resumeMusic();
+
+	if (!blockCopied || !allocatePattern(curPattern))
 		return;
 
-	const int16_t numRows = patternNumRows[editor.editPattern];
+	int32_t chStart = cursor.ch;
+	int32_t rowStart = curRow;
+	const int16_t numRows = patternNumRows[curPattern];
 
-	const int32_t xpos = cursor.ch;
-	const int32_t ypos = editor.row;
+	if (chStart >= song.numChannels)
+		chStart = song.numChannels-1;
 
-	int32_t j = markXSize;
-	if (j+xpos >= song.numChannels)
-		j = song.numChannels - xpos - 1;
+	if (rowStart >= numRows)
+		rowStart = numRows-1;
 
-	int32_t k = markYSize;
-	if (k+ypos >= numRows)
-		k = numRows-ypos;
+	int32_t markedChannels = markXSize + 1;
+	if (chStart+markedChannels > song.numChannels)
+		markedChannels = song.numChannels - chStart;
 
-	note_t *p = pattern[editor.editPattern];
+	int32_t markedRows = markYSize;
+	if (rowStart+markedRows > numRows)
+		markedRows = numRows - rowStart;
 
-	pauseMusic();
-	for (int32_t x = xpos; x <= xpos+j; x++)
+	if (markedChannels > 0 && markedRows > 0)
 	{
-		for (int32_t y = ypos; y < ypos+k; y++)
+		note_t *p = pattern[curPattern];
+
+		pauseMusic();
+		for (int32_t x = chStart; x < chStart+markedChannels; x++)
 		{
-			assert(x < song.numChannels && y < numRows);
-			pasteNote(&blkCopyBuff[((y - ypos) * MAX_CHANNELS) + (x - xpos)], &p[(y * MAX_CHANNELS) + x]);
+			for (int32_t y = rowStart; y < rowStart+markedRows; y++)
+				pasteNote(&blkCopyBuff[((y - rowStart) * MAX_CHANNELS) + (x - chStart)], &p[(y * MAX_CHANNELS) + x]);
 		}
+		resumeMusic();
 	}
-	resumeMusic();
 
-	killPatternIfUnused(editor.editPattern);
+	killPatternIfUnused(curPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
 }
 
-static void remapInstrXY(uint16_t pattNum, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t src, uint8_t dst)
+static void remapInstrXY(int32_t pattNum, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t src, uint8_t dst)
 {
-	// this routine is only used sanely, so no need to check input
-
 	note_t *pattPtr = pattern[pattNum];
 	if (pattPtr == NULL)
 		return;
 
-	note_t *p = &pattPtr[(y1 * MAX_CHANNELS) + x1];
+	if (x1 >= song.numChannels-1)
+		x1 = song.numChannels-2;
 
+	if (x2 >= song.numChannels)
+		x2 = (song.numChannels-1)-x1;
+
+	const int16_t numRows = patternNumRows[pattNum];
+	if (y1 >= numRows)
+		y1 = numRows-1;
+
+	if (y2 > numRows)
+		y2 = numRows-y1;
+
+	note_t *p = &pattPtr[(y1 * MAX_CHANNELS) + x1];
 	const int32_t pitch = MAX_CHANNELS - ((x2 + 1) - x1);
-	for (uint16_t y = y1; y <= y2; y++, p += pitch)
+
+	for (int32_t y = y1; y <= y2; y++, p += pitch)
 	{
-		for (uint16_t x = x1; x <= x2; x++, p++)
+		for (int32_t x = x1; x <= x2; x++, p++)
 		{
 			if (p->instr == src)
 				p->instr = dst;
@@ -1612,13 +1670,20 @@
 
 void remapBlock(void)
 {
-	if (editor.srcInstr == editor.curInstr || pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	volatile int32_t markX1 = pattMark.markX1;
+	volatile int32_t markX2 = pattMark.markX2;
+	volatile int32_t markY1 = pattMark.markY1;
+	volatile int32_t markY2 = pattMark.markY2;
+	resumeMusic();
+
+	if (editor.srcInstr == editor.curInstr || markY1 == markY2 || markY1 > markY2)
 		return;
 
-	pauseMusic();
-	remapInstrXY(editor.editPattern,
-	             pattMark.markX1, pattMark.markY1,
-	             pattMark.markX2, pattMark.markY2 - 1,
+	remapInstrXY(curPattern,
+	             markX1, markY1,
+	             markX2, markY2 - 1,
 	             editor.srcInstr, editor.curInstr);
 	resumeMusic();
 
@@ -1628,13 +1693,15 @@
 
 void remapTrack(void)
 {
+	const volatile uint16_t curPattern = editor.editPattern;
+
 	if (editor.srcInstr == editor.curInstr)
 		return;
 
 	pauseMusic();
-	remapInstrXY(editor.editPattern,
+	remapInstrXY(curPattern,
 	             cursor.ch, 0,
-	             cursor.ch, patternNumRows[editor.editPattern] - 1,
+	             cursor.ch, patternNumRows[curPattern]-1,
 	             editor.srcInstr, editor.curInstr);
 	resumeMusic();
 
@@ -1644,13 +1711,15 @@
 
 void remapPattern(void)
 {
+	const volatile uint16_t curPattern = editor.editPattern;
+
 	if (editor.srcInstr == editor.curInstr)
 		return;
 
 	pauseMusic();
-	remapInstrXY(editor.editPattern,
+	remapInstrXY(curPattern,
 	             0, 0,
-	             (uint16_t)(song.numChannels - 1), patternNumRows[editor.editPattern] - 1,
+	             song.numChannels-1, patternNumRows[curPattern]-1,
 	             editor.srcInstr, editor.curInstr);
 	resumeMusic();
 
@@ -1666,11 +1735,10 @@
 	pauseMusic();
 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
 	{
-		const uint8_t pattNum = (uint8_t)i;
-
-		remapInstrXY(pattNum,
+		// remapInstrXY() also checks if pattern is not allocated!
+		remapInstrXY(i,
 		             0, 0,
-		             (uint16_t)(song.numChannels - 1), patternNumRows[pattNum] - 1,
+		             song.numChannels-1, patternNumRows[i]-1,
 		             editor.srcInstr, editor.curInstr);
 	}
 	resumeMusic();
@@ -1713,6 +1781,9 @@
 	if (newVol < 0)
 		return;
 
+	if (newVol > 64)
+		newVol = 64;
+
 	const int8_t oldv = getNoteVolume(p);
 	if (p->vol == oldv)
 		return; // volume is the same
@@ -1723,12 +1794,12 @@
 		p->vol = 0x10 + newVol; // volume column
 }
 
-static void scaleNote(uint16_t pattNum, int8_t ch, int16_t row, double dScale)
+static void scaleNote(int32_t pattNum, int32_t ch, int32_t row, double dScale)
 {
 	if (pattern[pattNum] == NULL)
 		return;
 
-	const int16_t numRows = patternNumRows[pattNum];
+	const int32_t numRows = patternNumRows[pattNum];
 	if (row < 0 || row >= numRows || ch < 0 || ch >= song.numChannels)
 		return;
 
@@ -1748,22 +1819,22 @@
 	char volstr[32+1];
 
 	sprintf(volstr, "%0.2f,%0.2f", dVolScaleFK1, dVolScaleFK2);
-	if (inputBox(1, msg, volstr, sizeof (volstr) - 1) != 1)
+	if (inputBox(1, msg, volstr, sizeof (volstr)-1) != 1)
 		return false;
 
-	bool err = false;
+	bool error = false;
 
 	char *val1 = volstr;
 	if (strlen(val1) < 3)
-		err = true;
+		error = true;
 
 	char *val2 = strchr(volstr, ',');
 	if (val2 == NULL || strlen(val2) < 3)
-		err = true;
+		error = true;
 
-	if (err)
+	if (error)
 	{
-		okBox(0, "System message", "Invalid constant expressions.");
+		okBox(0, "System message", "Invalid constant expressions.", NULL);
 		return false;
 	}
 
@@ -1778,10 +1849,12 @@
 	if (!askForScaleFade("Volume scale-fade track (start-, end scale)"))
 		return;
 
-	if (pattern[editor.editPattern] == NULL)
+	const volatile uint16_t curPattern = editor.editPattern;
+
+	if (pattern[curPattern] == NULL)
 		return;
 
-	const int32_t numRows = patternNumRows[editor.editPattern];
+	const int32_t numRows = patternNumRows[curPattern];
 
 	double dVolDelta = 0.0;
 	if (numRows > 0)
@@ -1790,9 +1863,9 @@
 	double dVol = dVolScaleFK1;
 
 	pauseMusic();
-	for (int16_t row = 0; row < numRows; row++)
+	for (int32_t row = 0; row < numRows; row++)
 	{
-		scaleNote(editor.editPattern, cursor.ch, row, dVol);
+		scaleNote(curPattern, cursor.ch, row, dVol);
 		dVol += dVolDelta;
 	}
 	resumeMusic();
@@ -1803,10 +1876,12 @@
 	if (!askForScaleFade("Volume scale-fade pattern (start-, end scale)"))
 		return;
 
-	if (pattern[editor.editPattern] == NULL)
+	const volatile uint16_t curPattern = editor.editPattern;
+
+	if (pattern[curPattern] == NULL)
 		return;
 
-	const int32_t numRows = patternNumRows[editor.editPattern];
+	const int32_t numRows = patternNumRows[curPattern];
 
 	double dVolDelta = 0.0;
 	if (numRows > 0)
@@ -1815,10 +1890,10 @@
 	double dVol = dVolScaleFK1;
 
 	pauseMusic();
-	for (int16_t row = 0; row < numRows; row++)
+	for (int32_t row = 0; row < numRows; row++)
 	{
-		for (int8_t ch = 0; ch < song.numChannels; ch++)
-			scaleNote(editor.editPattern, ch, row, dVol);
+		for (int32_t ch = 0; ch < song.numChannels; ch++)
+			scaleNote(curPattern, ch, row, dVol);
 
 		dVol += dVolDelta;
 	}
@@ -1830,10 +1905,18 @@
 	if (!askForScaleFade("Volume scale-fade block (start-, end scale)"))
 		return;
 
-	if (pattern[editor.editPattern] == NULL || pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
+	volatile int32_t markX1 = pattMark.markX1;
+	volatile int32_t markX2 = pattMark.markX2;
+	volatile int32_t markY1 = pattMark.markY1;
+	volatile int32_t markY2 = pattMark.markY2;
+	resumeMusic();
+
+	if (pattern[curPattern] == NULL || markY1 == markY2 || markY1 > markY2)
 		return;
 
-	const int32_t numRows = pattMark.markY2 - pattMark.markY1;
+	const int32_t numRows = markY2 - markY1;
 
 	double dVolDelta = 0.0;
 	if (numRows > 0)
@@ -1842,10 +1925,10 @@
 	double dVol = dVolScaleFK1;
 
 	pauseMusic();
-	for (int16_t row = pattMark.markY1; row < pattMark.markY2; row++)
+	for (int32_t row = markY1; row < markY2; row++)
 	{
-		for (int16_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++)
-			scaleNote(editor.editPattern, (uint8_t)ch, row, dVol);
+		for (int32_t ch = markX1; ch <= markX2; ch++)
+			scaleNote(curPattern, ch, row, dVol);
 
 		dVol += dVolDelta;
 	}
--- a/src/ft2_edit.h
+++ b/src/ft2_edit.h
@@ -64,7 +64,6 @@
 void blockTranspAllInsDn(void);
 void blockTranspAllIns12Up(void);
 void blockTranspAllIns12Dn(void);
-void doTranspose(void); // called from buttons above or specific sys req.
 void cutTrack(void);
 void copyTrack(void);
 void pasteTrack(void);
--- a/src/ft2_events.c
+++ b/src/ft2_events.c
@@ -64,7 +64,7 @@
 {
 	if (okBoxData.active)
 	{
-		okBoxData.returnData = okBox(okBoxData.type, okBoxData.headline, okBoxData.text);
+		okBoxData.returnData = okBox(okBoxData.type, okBoxData.headline, okBoxData.text, okBoxData.checkBoxCallback);
 		okBoxData.active = false;
 	}
 }
--- a/src/ft2_gui.c
+++ b/src/ft2_gui.c
@@ -43,12 +43,11 @@
 
 void unstuckLastUsedGUIElement(void)
 {
+	/* If last object ID is OBJECT_ID_NONE, check if we moved
+	** the sample data loop pins, and unstuck them if so.
+	*/
 	if (mouse.lastUsedObjectID == OBJECT_ID_NONE)
 	{
-		/* If last object ID is OBJECT_ID_NONE, check if we moved the
-		** sample data loop pins, and unstuck them if so
-		*/
-
 		if (ui.leftLoopPinMoving)
 		{
 			setLeftLoopPinState(false);
@@ -321,15 +320,8 @@
 		{
 			for (int32_t x = 0; x < FONT3_CHAR_W; x++)
 			{
-#ifdef __arm__
 				if (srcPtr[x] != 0)
 					dstPtr[x] = color;
-#else
-				// carefully written like this to generate conditional move instructions (font data is hard to predict)
-				uint32_t tmp = dstPtr[x];
-				if (srcPtr[x] != 0) tmp = color;
-				dstPtr[x] = tmp;
-#endif
 			}
 
 			srcPtr += FONT3_WIDTH;
@@ -369,15 +361,8 @@
 	{
 		for (uint32_t x = 0; x < FONT1_CHAR_W; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = pixVal;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = pixVal;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT1_WIDTH;
@@ -448,7 +433,7 @@
 	for (int32_t y = 0; y < FONT1_CHAR_H; y++)
 	{
 		for (int32_t x = 0; x < FONT1_CHAR_W-1; x++)
-			dstPtr[x] = srcPtr[x] ? fg : bg; // compiles nicely into conditional move instructions
+			dstPtr[x] = srcPtr[x] ? fg : bg;
 
 		srcPtr += FONT1_WIDTH;
 		dstPtr += SCREEN_W;
@@ -483,22 +468,11 @@
 	{
 		for (int32_t x = 0; x < FONT1_CHAR_W; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 			{
 				dstPtr2[x] = pixVal2;
 				dstPtr1[x] = pixVal1;
 			}
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr2[x];
-			if (srcPtr[x] != 0) tmp = pixVal2;
-			dstPtr2[x] = tmp;
-
-			tmp = dstPtr1[x];
-			if (srcPtr[x] != 0) tmp = pixVal1;
-			dstPtr1[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT1_WIDTH;
@@ -530,15 +504,8 @@
 	{
 		for (int32_t x = 0; x < width; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = pixVal;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = pixVal;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT1_WIDTH;
@@ -562,15 +529,8 @@
 	{
 		for (int32_t x = 0; x < FONT2_CHAR_W; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = pixVal;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = pixVal;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT2_WIDTH;
@@ -596,22 +556,11 @@
 	{
 		for (int32_t x = 0; x < FONT2_CHAR_W; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 			{
 				dstPtr2[x] = pixVal2;
 				dstPtr1[x] = pixVal1;
 			}
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr2[x];
-			if (srcPtr[x] != 0) tmp = pixVal2;
-			dstPtr2[x] = tmp;
-
-			tmp = dstPtr1[x];
-			if (srcPtr[x] != 0) tmp = pixVal1;
-			dstPtr1[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT2_WIDTH;
@@ -771,15 +720,8 @@
 		{
 			for (int32_t x = 0; x < FONT6_CHAR_W; x++)
 			{
-#ifdef __arm__
 				if (srcPtr[x] != 0)
 					dstPtr[x] = pixVal;
-#else
-				// carefully written like this to generate conditional move instructions (font data is hard to predict)
-				uint32_t tmp = dstPtr[x];
-				if (srcPtr[x] != 0) tmp = pixVal;
-				dstPtr[x] = tmp;
-#endif
 			}
 
 			srcPtr += FONT6_WIDTH;
@@ -807,7 +749,7 @@
 		for (int32_t y = 0; y < FONT6_CHAR_H; y++)
 		{
 			for (int32_t x = 0; x < FONT6_CHAR_W; x++)
-				dstPtr[x] = srcPtr[x] ? fg : bg; // compiles nicely into conditional move instructions
+				dstPtr[x] = srcPtr[x] ? fg : bg;
 
 			srcPtr += FONT6_WIDTH;
 			dstPtr += SCREEN_W;
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -13,7 +13,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.69"
+#define PROG_VER_STR "1.70"
 
 // do NOT change these! It will only mess things up...
 
--- a/src/ft2_help.c
+++ b/src/ft2_help.c
@@ -109,7 +109,7 @@
 	helpRec *tempText = (helpRec *)malloc(HELP_SIZE * MAX_HELP_LINES);
 	if (tempText == NULL)
 	{
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return;
 	}
 	
@@ -251,7 +251,7 @@
 		subjPtrArr[subj] = (helpRec *)malloc(HELP_SIZE * textLine);
 		if (subjPtrArr[subj] == NULL)
 		{
-			okBox(0, "System message", "Not enough memory!");
+			okBox(0, "System message", "Not enough memory!", NULL);
 			break;
 		}
 
--- a/src/ft2_inst_ed.c
+++ b/src/ft2_inst_ed.c
@@ -212,7 +212,7 @@
 	resumeAudio();
 
 	if (error)
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
+		okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 
 	// do not change instrument names!
 
@@ -237,7 +237,7 @@
 	thread = SDL_CreateThread(copyInstrThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -2001,7 +2001,7 @@
 
 static void drawVolEnvCoords(int16_t tick, int16_t val)
 {
-	char str[4];
+	char str[8];
 
 	tick = CLAMP(tick, 0, 324);
 	sprintf(str, "%03d", tick);
@@ -2014,13 +2014,14 @@
 
 static void drawPanEnvCoords(int16_t tick, int16_t val)
 {
-	bool negative = false;
-	char str[4];
+	char str[8];
 
 	tick = CLAMP(tick, 0, 324);
 	sprintf(str, "%03d", tick);
 	textOutTinyOutline(326, 277, str);
 	
+	bool negative = false;
+	
 	val -= 32;
 	val = CLAMP(val, -32, 31);
 	if (val < 0)
@@ -2914,7 +2915,7 @@
 
 	if (editor.tmpFilenameU == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -2921,7 +2922,7 @@
 	const int32_t numSamples = getUsedSamples(saveInstrNum);
 	if (numSamples == 0 || instr[saveInstrNum] == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "Instrument is empty!");
+		okBoxThreadSafe(0, "System message", "Instrument is empty!", NULL);
 		return false;
 	}
 
@@ -2928,7 +2929,7 @@
 	FILE *f = UNICHAR_FOPEN(editor.tmpFilenameU, "wb");
 	if (f == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -3025,7 +3026,7 @@
 	if (result != 1)
 	{
 		fclose(f);
-		okBoxThreadSafe(0, "System message", "Error saving instrument: general I/O error!");
+		okBoxThreadSafe(0, "System message", "Error saving instrument: general I/O error!", NULL);
 		return false;
 	}
 
@@ -3047,7 +3048,7 @@
 			{
 				resumeAudio();
 				fclose(f);
-				okBoxThreadSafe(0, "System message", "Error saving instrument: general I/O error!");
+				okBoxThreadSafe(0, "System message", "Error saving instrument: general I/O error!", NULL);
 				return false;
 			}
 		}
@@ -3076,7 +3077,7 @@
 	thread = SDL_CreateThread(saveInstrThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -3107,7 +3108,7 @@
 
 	if (editor.tmpInstrFilenameU == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -3114,7 +3115,7 @@
 	FILE *f = UNICHAR_FOPEN(editor.tmpInstrFilenameU, "rb");
 	if (f == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -3129,7 +3130,7 @@
 
 		if (xi_h.version != 0x0101 && xi_h.version != 0x0102)
 		{
-			okBoxThreadSafe(0, "System message", "Incompatible format version!");
+			okBoxThreadSafe(0, "System message", "Incompatible format version!", NULL);
 			goto loadDone;
 		}
 
@@ -3157,7 +3158,7 @@
 			if (!allocateInstr(editor.curInstr))
 			{
 				resumeAudio();
-				okBoxThreadSafe(0, "System message", "Not enough memory!");
+				okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 				goto loadDone;
 			}
 
@@ -3197,7 +3198,7 @@
 			{
 				freeInstr(editor.curInstr);
 				resumeAudio();
-				okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+				okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 				goto loadDone;
 			}
 
@@ -3260,7 +3261,7 @@
 				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
-					okBoxThreadSafe(0, "System message", "Not enough memory!");
+					okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 					goto loadDone;
 				}
 
@@ -3269,7 +3270,7 @@
 				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
-					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 					goto loadDone;
 				}
 
@@ -3309,7 +3310,7 @@
 
 			if (pat_h.layers > 1 || pat_h.numSamples > MAX_SMP_PER_INST)
 			{
-				okBoxThreadSafe(0, "System message", "Incompatible instrument!");
+				okBoxThreadSafe(0, "System message", "Incompatible instrument!", NULL);
 				goto loadDone;
 			}
 
@@ -3320,7 +3321,7 @@
 
 			if (!allocateInstr(editor.curInstr))
 			{
-				okBoxThreadSafe(0, "System message", "Not enough memory!");
+				okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 				goto loadDone;
 			}
 
@@ -3336,7 +3337,7 @@
 				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
-					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 					goto loadDone;
 				}
 
@@ -3358,7 +3359,7 @@
 				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
-					okBoxThreadSafe(0, "System message", "Not enough memory!");
+					okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 					goto loadDone;
 				}
 
@@ -3413,7 +3414,7 @@
 				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
-					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+					okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 					goto loadDone;
 				}
 
@@ -3456,10 +3457,10 @@
 	editor.updateCurInstr = true; // setMouseBusy(false) is called in the input/video thread when done
 
 	if (numLoadedSamples > MAX_SMP_PER_INST)
-		okBoxThreadSafe(0, "System message", "Warning: The instrument contained >16 samples. The extra samples were discarded!");
+		okBoxThreadSafe(0, "System message", "Warning: The instrument contained >16 samples. The extra samples were discarded!", NULL);
 
 	if (stereoWarning)
-		okBoxThreadSafe(0, "System message", "Warning: The instrument contained stereo sample(s). They were mixed to mono!");
+		okBoxThreadSafe(0, "System message", "Warning: The instrument contained stereo sample(s). They were mixed to mono!", NULL);
 
 	return true;
 	(void)ptr;
@@ -3485,7 +3486,7 @@
 {
 	if (editor.curInstr == 0)
 	{
-		okBox(0, "System message", "The zero-instrument cannot hold intrument data.");
+		okBox(0, "System message", "The zero-instrument cannot hold intrument data.", NULL);
 		return;
 	}
 
@@ -3498,7 +3499,7 @@
 		thread = SDL_CreateThread(loadInstrThread, NULL, NULL);
 		if (thread == NULL)
 		{
-			okBox(0, "System message", "Couldn't create thread!");
+			okBox(0, "System message", "Couldn't create thread!", NULL);
 			return;
 		}
 
--- a/src/ft2_keyboard.c
+++ b/src/ft2_keyboard.c
@@ -296,7 +296,7 @@
 					if (editor.curInstr == 0 || instr[editor.curInstr] == NULL)
 						return;
 
-					if (okBox(1, "System request", "Clear instrument?") == 1)
+					if (okBox(1, "System request", "Clear instrument?", NULL) == 1)
 					{
 						freeInstr(editor.curInstr);
 						memset(song.instrName[editor.curInstr], 0, sizeof(song.instrName[editor.curInstr]));
@@ -636,9 +636,8 @@
 			if (audioWasntLocked)
 				lockAudio();
 
-			if (song.row >= 16)
-				song.row -= 16;
-			else
+			song.row -= 16;
+			if (song.row < 0)
 				song.row = 0;
 
 			if (!songPlaying)
@@ -658,9 +657,8 @@
 			if (audioWasntLocked)
 				lockAudio();
 
-			if (song.row < song.currNumRows-16)
-				song.row += 16;
-			else
+			song.row += 16;
+			if (song.row >= song.currNumRows)
 				song.row = song.currNumRows-1;
 
 			if (!songPlaying)
@@ -698,7 +696,7 @@
 			if (audioWasntLocked)
 				lockAudio();
 
-			song.row = patternNumRows[song.pattNum] - 1;
+			song.row = song.currNumRows - 1;
 			if (!songPlaying)
 			{
 				editor.row = (uint8_t)song.row;
--- a/src/ft2_main.c
+++ b/src/ft2_main.c
@@ -274,7 +274,7 @@
 	memset(&keyb, 0, sizeof (keyb));
 	memset(&mouse, 0, sizeof (mouse));
 	memset(&editor, 0, sizeof (editor));
-	memset(&pattMark, 0, sizeof (pattMark));
+	memset((void *)&pattMark, 0, sizeof (pattMark));
 	memset(&pattSync, 0, sizeof (pattSync));
 	memset(&chSync, 0, sizeof (chSync));
 	memset(&song, 0, sizeof (song));
@@ -320,11 +320,11 @@
 #endif
 
 	editor.diskOpReadOnOpen = true;
-	editor.programRunning = true;
 
 	audio.linearPeriodsFlag = true;
-
 	calcReplayerLogTab();
+
+	editor.programRunning = true;
 }
 
 static void cleanUpAndExit(void) // never call this inside the main loop!
--- a/src/ft2_module_loader.c
+++ b/src/ft2_module_loader.c
@@ -246,7 +246,7 @@
 	if (thread == NULL)
 	{
 		editor.loadMusicEvent = EVENT_NONE;
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		musicIsLoading = false;
 		return;
 	}
@@ -537,7 +537,7 @@
 	UNICHAR *tmpPathU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR));
 	if (tmpPathU == NULL)
 	{
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return false;
 	}
 
@@ -545,7 +545,7 @@
 	if (filenameU == NULL)
 	{
 		free(tmpPathU);
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return false;
 	}
 
@@ -571,7 +571,7 @@
 		UNICHAR_CHDIR(tmpPathU); // set old path back
 		free(tmpPathU);
 
-		okBox(0, "System message", "Error: The module is too big to be loaded!");
+		okBox(0, "System message", "Error: The module is too big to be loaded!", NULL);
 		return false;
 	}
 
@@ -654,7 +654,7 @@
 	UNICHAR *fullPathU = (UNICHAR *)malloc((fullPathLen + 1) * sizeof (UNICHAR));
 	if (fullPathU == NULL)
 	{
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return;
 	}
 
@@ -670,7 +670,7 @@
 
 	if (filesize == -1) // >2GB
 	{
-		okBox(0, "System message", "The file is too big and can't be loaded (over 2GB).");
+		okBox(0, "System message", "The file is too big and can't be loaded (over 2GB).", NULL);
 		free(fullPathU);
 		return;
 	}
@@ -677,7 +677,7 @@
 
 	if (filesize >= 128L*1024*1024) // 128MB
 	{
-		if (okBox(2, "System request", "Are you sure you want to load such a big file?") != 1)
+		if (okBox(2, "System request", "Are you sure you want to load such a big file?", NULL) != 1)
 		{
 			free(fullPathU);
 			return;
--- a/src/ft2_module_saver.c
+++ b/src/ft2_module_saver.c
@@ -42,7 +42,7 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?");
+		okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?", NULL);
 		return false;
 	}
 
@@ -100,7 +100,7 @@
 	if (fwrite(&h, sizeof (h), 1, f) != 1)
 	{
 		fclose(f);
-		okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+		okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 		return false;
 	}
 
@@ -127,7 +127,7 @@
 			if (fwrite(&ph, ph.headerSize, 1, f) != 1)
 			{
 				fclose(f);
-				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 				return false;
 			}
 		}
@@ -141,7 +141,7 @@
 			if (result != 2) // write was not OK
 			{
 				fclose(f);
-				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 				return false;
 			}
 		}
@@ -245,7 +245,7 @@
 		if (fwrite(&ih, ih.instrSize + (a * sizeof (xmSmpHdr_t)), 1, f) != 1)
 		{
 			fclose(f);
-			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 			return false;
 		}
 
@@ -265,7 +265,7 @@
 				if (result != (size_t)SAMPLE_LENGTH_BYTES(s)) // write not OK
 				{
 					fclose(f);
-					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 					return false;
 				}
 			}
@@ -300,7 +300,7 @@
 	if (songLength > 128)
 	{
 		songLength = 128;
-		okBoxThreadSafe(0, "System message", "Warning: Song length is above 128!");
+		okBoxThreadSafe(0, "System message", "Warning: Song length is above 128!", NULL);
 	}
 	
 	// calculate number of patterns referenced (max 128 orders)
@@ -315,7 +315,7 @@
 	if (numPatterns > 100)
 	{
 		numPatterns = 100;
-		okBoxThreadSafe(0, "System message", "Warning: Song has more than 100 patterns!");
+		okBoxThreadSafe(0, "System message", "Warning: Song has more than 100 patterns!", NULL);
 	}
 
 	// check if song has more than 31 instruments
@@ -323,7 +323,7 @@
 	{
 		if (getRealUsedSamples(i) > 0)
 		{
-			okBoxThreadSafe(0, "System message", "Warning: Song has more than 31 instruments!");
+			okBoxThreadSafe(0, "System message", "Warning: Song has more than 31 instruments!", NULL);
 			break;
 		}
 	}
@@ -344,8 +344,8 @@
 		else if (smp->length > 65534)
 			test2 = true;
 	}
-	if (test) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths that are too long for the MOD format!");
-	else if (test2) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths above 65534! Not all MOD players support this.");
+	if (test) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths that are too long for the MOD format!", NULL);
+	else if (test2) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths above 65534! Not all MOD players support this.", NULL);
 
 	// check if XM instrument features are being used
 	test = false;
@@ -374,7 +374,7 @@
 			}
 		}
 	}
-	if (test) okBoxThreadSafe(0, "System message", "Warning: Song is using XM instrument features!");
+	if (test) okBoxThreadSafe(0, "System message", "Warning: Song is using XM instrument features!", NULL);
 
 	bool tooLongPatterns = false;
 	bool tooManyInstr = false;
@@ -388,7 +388,7 @@
 
 		if (patternNumRows[i] < 64)
 		{
-			okBoxThreadSafe(0, "System message", "Error: Pattern lengths can't be below 64! Module wasn't saved.");
+			okBoxThreadSafe(0, "System message", "Error: Pattern lengths can't be below 64! Module wasn't saved.", NULL);
 			return false;
 		}
 
@@ -414,10 +414,10 @@
 		}
 	}
 
-	if (tooLongPatterns) okBoxThreadSafe(0, "System message", "Warning: Song has pattern lengths above 64!");
-	if (tooManyInstr) okBoxThreadSafe(0, "System message", "Warning: Patterns have instrument numbers above 31!");
-	if (incompatEfx) okBoxThreadSafe(0, "System message", "Warning: Patterns have incompatible effects!");
-	if (noteUnderflow) okBoxThreadSafe(0, "System message", "Warning: Patterns have notes below A-0!");
+	if (tooLongPatterns) okBoxThreadSafe(0, "System message", "Warning: Song has pattern lengths above 64!", NULL);
+	if (tooManyInstr) okBoxThreadSafe(0, "System message", "Warning: Patterns have instrument numbers above 31!", NULL);
+	if (incompatEfx) okBoxThreadSafe(0, "System message", "Warning: Patterns have incompatible effects!", NULL);
+	if (noteUnderflow) okBoxThreadSafe(0, "System message", "Warning: Patterns have notes below A-0!", NULL);
 
 	// save module now
 
@@ -499,7 +499,7 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?");
+		okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?", NULL);
 		return false;
 	}
 
@@ -506,7 +506,7 @@
 	// write header
 	if (fwrite(&hdr, 1, sizeof (hdr), f) != sizeof (hdr))
 	{
-		okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+		okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 		goto modSaveError;
 	}
 
@@ -572,7 +572,7 @@
 
 		if (fwrite(modPattData, 1, patternBytes, f) != (size_t)patternBytes)
 		{
-			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 			goto modSaveError;
 		}
 	}
@@ -612,7 +612,7 @@
 				if (fwrite(dstPtr, 1, samplesToWrite, f) != (size_t)samplesToWrite)
 				{
 					fixSample(smp);
-					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 					goto modSaveError;
 				}
 
@@ -624,7 +624,7 @@
 			if (fwrite(smp->dataPtr, 1, sampleBytes, f) != (size_t)sampleBytes)
 			{
 				fixSample(smp);
-				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
+				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
 				goto modSaveError;
 			}
 		}
@@ -672,7 +672,7 @@
 	thread = SDL_CreateThread(saveMusicThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "Couldn't create thread!");
+		okBoxThreadSafe(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
--- a/src/ft2_nibbles.c
+++ b/src/ft2_nibbles.c
@@ -299,7 +299,7 @@
 {
 	if (editor.NI_Play)
 	{
-		okBox(0, "Nibbles message", "The highscore table is not available during play.");
+		okBox(0, "Nibbles message", "The highscore table is not available during play.", NULL);
 		return;
 	}
 
@@ -430,20 +430,20 @@
 
 	if (l1+l2 == 2)
 	{
-		okBox(0, "Nibbles message", "Both players died!");
+		okBox(0, "Nibbles message", "Both players died!", NULL);
 	}
 	else
 	{
 		if (l2 == 0)
-			okBox(0, "Nibbles message", "Player 1 died!");
+			okBox(0, "Nibbles message", "Player 1 died!", NULL);
 		else
-			okBox(0, "Nibbles message", "Player 2 died!");
+			okBox(0, "Nibbles message", "Player 2 died!", NULL);
 	}
 
 	if (NI_P1Lives == 0 || NI_P2Lives == 0)
 	{
 		editor.NI_Play = false;
-		okBox(0, "Nibbles message", "GAME OVER");
+		okBox(0, "Nibbles message", "GAME OVER", NULL);
 
 		// prevent highscore table from showing overflowing level graphics
 		if (NI_Level >= NI_MAXLEVEL)
@@ -462,7 +462,7 @@
 				memcpy(&config.NI_HighScore[k+1], &config.NI_HighScore[k], sizeof (highScoreType));
 
 			if (i == 0)
-				okBox(0, "Nibbles message", "You've probably cheated!");
+				okBox(0, "Nibbles message", "You've probably cheated!", NULL);
 
 			h = &config.NI_HighScore[i];
 
@@ -488,7 +488,7 @@
 				memcpy(&config.NI_HighScore[k+1], &config.NI_HighScore[k], sizeof (highScoreType));
 
 			if (i == 0)
-				okBox(0, "Nibbles message", "You've probably cheated!");
+				okBox(0, "Nibbles message", "You've probably cheated!", NULL);
 
 			h = &config.NI_HighScore[i];
 			k = (int16_t)strlen(name);
@@ -521,7 +521,7 @@
 	char text[24];
 
 	sprintf(text, "Level %d finished!", NI_Level+1);
-	okBox(0, "Nibbles message", text);
+	okBox(0, "Nibbles message", text, NULL);
 
 	// cast to int16_t to simulate a bug in FT2
 	NI_P1Score += 0x10000 + (int16_t)((12 - NI_CurSpeed) * 0x2000);
@@ -814,18 +814,18 @@
 {
 	if (editor.NI_Play)
 	{
-		if (okBox(2, "Nibbles request", "Restart current game of Nibbles?") != 1)
+		if (okBox(2, "Nibbles request", "Restart current game of Nibbles?", NULL) != 1)
 			return;
 	}
 
 	if (config.NI_Surround && config.NI_NumPlayers == 0)
 	{
-		okBox(0, "Nibbles message", "Surround mode is not appropriate in one-player mode.");
+		okBox(0, "Nibbles message", "Surround mode is not appropriate in one-player mode.", NULL);
 		return;
 	}
 
 	if (wallColorsAreCloseToBlack())
-		okBox(0, "Nibbles warning", "The Desktop/Button colors are set to values that make the walls hard to see!");
+		okBox(0, "Nibbles warning", "The Desktop/Button colors are set to values that make the walls hard to see!", NULL);
 
 	assert(config.NI_Speed < 4);
 	NI_CurSpeed = NI_Speeds[config.NI_Speed];
@@ -848,7 +848,7 @@
 {
 	if (editor.NI_Play)
 	{
-		okBox(0, "System message", "Help is not available during play.");
+		okBox(0, "System message", "Help is not available during play.", NULL);
 		return;
 	}
 
@@ -863,7 +863,7 @@
 {
 	if (editor.NI_Play)
 	{
-		if (okBox(2, "System request", "Quit current game of Nibbles?") == 1)
+		if (okBox(2, "System request", "Quit current game of Nibbles?", NULL) == 1)
 		{
 			editor.NI_Play = false;
 			exitNibblesScreen();
@@ -946,7 +946,7 @@
 {
 	if (scancode == SDL_SCANCODE_ESCAPE)
 	{
-		if (okBox(2, "System request", "Quit current game of Nibbles?") == 1)
+		if (okBox(2, "System request", "Quit current game of Nibbles?", NULL) == 1)
 		{
 			editor.NI_Play = false;
 			exitNibblesScreen();
@@ -1013,9 +1013,9 @@
 			{
 				NI_EternalLives ^= 1;
 				if (NI_EternalLives)
-					okBox(0, "Triton productions declares:", "Eternal lives activated!");
+					okBox(0, "Triton productions declares:", "Eternal lives activated!", NULL);
 				else
-					okBox(0, "Triton productions declares:", "Eternal lives deactivated!");
+					okBox(0, "Triton productions declares:", "Eternal lives deactivated!", NULL);
 			}
 		}
 
--- a/src/ft2_palette.c
+++ b/src/ft2_palette.c
@@ -69,12 +69,12 @@
 
 static void showColorErrorMsg(void)
 {
-	okBox(0, "System message", "Default colors cannot be modified.");
+	okBox(0, "System message", "Default colors cannot be modified.", NULL);
 }
 
 static void showMouseColorErrorMsg(void)
 {
-	okBox(0, "System message", "Mouse color can only be changed when \"Software mouse\" is enabled.");
+	okBox(0, "System message", "Mouse color can only be changed when \"Software mouse\" is enabled.", NULL);
 }
 
 static double palPow(double dX, double dY)
--- a/src/ft2_pattern_draw.c
+++ b/src/ft2_pattern_draw.c
@@ -745,7 +745,7 @@
 		drawChannelNumbering(pattCoord->upperRowsTextY);
 }
 
-// ========== OPTIMIZED CHARACTER DRAWING ROUTINES FOR PATTERN EDITOR ==========
+// ========== CHARACTER DRAWING ROUTINES FOR PATTERN EDITOR ==========
 
 void pattTwoHexOut(uint32_t xPos, uint32_t yPos, uint8_t val, uint32_t color)
 {
@@ -757,19 +757,8 @@
 	{
 		for (int32_t x = 0; x < FONT4_CHAR_W; x++)
 		{
-#ifdef __arm__
 			if (ch1Ptr[x] != 0) dstPtr[x] = color;
 			if (ch2Ptr[x] != 0) dstPtr[FONT4_CHAR_W+x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (ch1Ptr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-
-			tmp = dstPtr[FONT4_CHAR_W+x];
-			if (ch2Ptr[x] != 0) tmp = color;
-			dstPtr[FONT4_CHAR_W+x] = tmp;
-#endif
 		}
 
 		ch1Ptr += FONT4_WIDTH;
@@ -782,9 +771,6 @@
 {
 	const uint8_t *srcPtr;
 	int32_t x, y;
-#ifndef __arm__
-	uint32_t tmp;
-#endif
 
 	uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
 
@@ -795,15 +781,8 @@
 		{
 			for (x = 0; x < FONT3_CHAR_W; x++)
 			{
-#ifdef __arm__
 				if (srcPtr[x] != 0)
 					dstPtr[x] = color;
-#else
-				// carefully written like this to generate conditional move instructions (font data is hard to predict)
-				tmp = dstPtr[x];
-				if (srcPtr[x] != 0) tmp = color;
-				dstPtr[x] = tmp;
-#endif
 			}
 
 			srcPtr += FONT3_WIDTH;
@@ -817,15 +796,8 @@
 		{
 			for (x = 0; x < FONT4_CHAR_W; x++)
 			{
-#ifdef __arm__
 				if (srcPtr[x] != 0)
 					dstPtr[x] = color;
-#else
-				// carefully written like this to generate conditional move instructions (font data is hard to predict)
-				tmp = dstPtr[x];
-				if (srcPtr[x] != 0) tmp = color;
-				dstPtr[x] = tmp;
-#endif
 			}
 
 			srcPtr += FONT4_WIDTH;
@@ -839,15 +811,8 @@
 		{
 			for (x = 0; x < FONT5_CHAR_W; x++)
 			{
-#ifdef __arm__
 				if (srcPtr[x] != 0)
 					dstPtr[x] = color;
-#else
-				// carefully written like this to generate conditional move instructions (font data is hard to predict)
-				tmp = dstPtr[x];
-				if (srcPtr[x] != 0) tmp = color;
-				dstPtr[x] = tmp;
-#endif
 			}
 
 			srcPtr += FONT5_WIDTH;
@@ -861,15 +826,8 @@
 		{
 			for (x = 0; x < FONT7_CHAR_W; x++)
 			{
-#ifdef __arm__
 				if (srcPtr[x] != 0)
 					dstPtr[x] = color;
-#else
-				// carefully written like this to generate conditional move instructions (font data is hard to predict)
-				tmp = dstPtr[x];
-				if (srcPtr[x] != 0) tmp = color;
-				dstPtr[x] = tmp;
-#endif
 			}
 
 			srcPtr += FONT7_WIDTH;
@@ -887,15 +845,8 @@
 	{
 		for (int32_t x = 0; x < FONT7_CHAR_W*3; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT7_WIDTH;
@@ -912,15 +863,8 @@
 	{
 		for (int32_t x = 0; x < FONT7_CHAR_W*2; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT7_WIDTH;
@@ -957,24 +901,9 @@
 	{
 		for (int32_t x = 0; x < FONT7_CHAR_W; x++)
 		{
-#ifdef __arm__
 			if (ch1Ptr[x] != 0) dstPtr[x] = color;
 			if (ch2Ptr[x] != 0) dstPtr[FONT7_CHAR_W+x] = color;
 			if (ch3Ptr[x] != 0) dstPtr[((FONT7_CHAR_W*2)-2)+x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (ch1Ptr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-
-			tmp = dstPtr[FONT7_CHAR_W+x];
-			if (ch2Ptr[x] != 0) tmp = color;
-			dstPtr[FONT7_CHAR_W+x] = tmp;
-
-			tmp = dstPtr[((FONT7_CHAR_W*2)-2)+x]; // -2 to get correct alignment for ending glyph
-			if (ch3Ptr[x] != 0) tmp = color;
-			dstPtr[((FONT7_CHAR_W*2)-2)+x] = tmp;
-#endif
 		}
 
 		ch1Ptr += FONT7_WIDTH;
@@ -993,15 +922,8 @@
 	{
 		for (int32_t x = 0; x < FONT4_CHAR_W*3; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT4_WIDTH;
@@ -1018,15 +940,8 @@
 	{
 		for (int32_t x = 0; x < FONT4_CHAR_W*3; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT4_WIDTH;
@@ -1063,24 +978,9 @@
 	{
 		for (int32_t x = 0; x < FONT4_CHAR_W; x++)
 		{
-#ifdef __arm__
 			if (ch1Ptr[x] != 0) dstPtr[x] = color;
 			if (ch2Ptr[x] != 0) dstPtr[FONT4_CHAR_W+x] = color;
 			if (ch3Ptr[x] != 0) dstPtr[(FONT4_CHAR_W*2)+x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (ch1Ptr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-
-			tmp = dstPtr[FONT4_CHAR_W+x];
-			if (ch2Ptr[x] != 0) tmp = color;
-			dstPtr[FONT4_CHAR_W+x] = tmp;
-
-			tmp = dstPtr[(FONT4_CHAR_W*2)+x];
-			if (ch3Ptr[x] != 0) tmp = color;
-			dstPtr[(FONT4_CHAR_W*2)+x] = tmp;
-#endif
 		}
 
 		ch1Ptr += FONT4_WIDTH;
@@ -1099,15 +999,8 @@
 	{
 		for (int32_t x = 0; x < FONT4_CHAR_W*6; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT4_WIDTH;
@@ -1124,15 +1017,8 @@
 	{
 		for (int32_t x = 0; x < FONT4_CHAR_W*6; x++)
 		{
-#ifdef __arm__
 			if (srcPtr[x] != 0)
 				dstPtr[x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (srcPtr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-#endif
 		}
 
 		srcPtr += FONT4_WIDTH;
@@ -1169,24 +1055,9 @@
 	{
 		for (int32_t x = 0; x < FONT5_CHAR_W; x++)
 		{
-#ifdef __arm__
 			if (ch1Ptr[x] != 0) dstPtr[x] = color;
 			if (ch2Ptr[x] != 0) dstPtr[FONT5_CHAR_W+x] = color;
 			if (ch3Ptr[x] != 0) dstPtr[(FONT5_CHAR_W*2)+x] = color;
-#else
-			// carefully written like this to generate conditional move instructions (font data is hard to predict)
-			uint32_t tmp = dstPtr[x];
-			if (ch1Ptr[x] != 0) tmp = color;
-			dstPtr[x] = tmp;
-
-			tmp = dstPtr[FONT5_CHAR_W+x];
-			if (ch2Ptr[x] != 0) tmp = color;
-			dstPtr[FONT5_CHAR_W+x] = tmp;
-
-			tmp = dstPtr[(FONT5_CHAR_W*2)+x];
-			if (ch3Ptr[x] != 0) tmp = color;
-			dstPtr[(FONT5_CHAR_W*2)+x] = tmp;
-#endif
 		}
 
 		ch1Ptr += FONT5_WIDTH;
--- a/src/ft2_pattern_ed.c
+++ b/src/ft2_pattern_ed.c
@@ -43,7 +43,7 @@
 
 static note_t tmpPattern[MAX_CHANNELS * MAX_PATT_LEN];
 
-pattMark_t pattMark; // globalized
+volatile pattMark_t pattMark; // globalized
 
 bool allocatePattern(uint16_t pattNum) // for tracker use only, not in loader!
 {
@@ -721,7 +721,7 @@
 
 void clearPattMark(void)
 {
-	memset(&pattMark, 0, sizeof (pattMark));
+	memset((void *)&pattMark, 0, sizeof (pattMark));
 
 	lastMarkX1 = -1;
 	lastMarkX2 = -1;
@@ -731,17 +731,27 @@
 
 void checkMarkLimits(void)
 {
-	const uint16_t limitY = patternNumRows[editor.editPattern];
-	pattMark.markY1 = CLAMP(pattMark.markY1, 0, limitY);
-	pattMark.markY2 = CLAMP(pattMark.markY2, 0, limitY);
+	volatile int16_t markX1 = pattMark.markX1;
+	volatile int16_t markX2 = pattMark.markX2;
+	volatile int16_t markY1 = pattMark.markY1;
+	volatile int16_t markY2 = pattMark.markY2;
 
-	const uint16_t limitX = (uint16_t)(song.numChannels - 1);
-	pattMark.markX1 = CLAMP(pattMark.markX1, 0, limitX);
-	pattMark.markX2 = CLAMP(pattMark.markX2, 0, limitX);
+	const int16_t limitY = patternNumRows[editor.editPattern];
+	markY1 = CLAMP(markY1, 0, limitY);
+	markY2 = CLAMP(markY2, 0, limitY);
 
+	const int16_t limitX = (int16_t)(song.numChannels - 1);
+	markX1 = CLAMP(markX1, 0, limitX);
+	markX2 = CLAMP(markX2, 0, limitX);
+
 	// XXX: will probably never happen? FT2 has this in CheckMarkLimits() though...
-	if (pattMark.markX1 > pattMark.markX2)
-		pattMark.markX1 = pattMark.markX2;
+	if (markX1 > markX2)
+		markX1 = markX2;
+
+	pattMark.markX1 = markX1;
+	pattMark.markX2 = markX2;
+	pattMark.markY1 = markY1;
+	pattMark.markY2 = markY2;
 }
 
 static int8_t mouseXToCh(void) // used to get channel num from mouse x (for pattern marking)
@@ -951,11 +961,15 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	song.row = (song.row - 1 + song.currNumRows) % song.currNumRows;
-	if (!songPlaying)
+	if (song.currNumRows > 0)
 	{
-		editor.row = (uint8_t)song.row;
-		ui.updatePatternEditor = true;
+		song.row = (song.row - 1 + song.currNumRows) % song.currNumRows;
+
+		if (!songPlaying)
+		{
+			editor.row = (uint8_t)song.row;
+			ui.updatePatternEditor = true;
+		}
 	}
 
 	if (audioWasntLocked)
@@ -972,7 +986,7 @@
 	{
 		song.tick = 2;
 	}
-	else
+	else if (song.currNumRows > 0)
 	{
 		song.row = (song.row + 1 + song.currNumRows) % song.currNumRows;
 		editor.row = (uint8_t)song.row;
@@ -1160,19 +1174,19 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
 	if (f == NULL)
 	{
-		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		return false;
 	}
 
 	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
 	{
-		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		goto trackLoadError;
 	}
 
 	if (h.version != 1)
 	{
-		okBox(0, "System message", "Incompatible format version!");
+		okBox(0, "System message", "Incompatible format version!", NULL);
 		goto trackLoadError;
 	}
 
@@ -1185,13 +1199,13 @@
 
 	if (fread(loadBuff, numRows * sizeof (note_t), 1, f) != 1)
 	{
-		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		goto trackLoadError;
 	}
 
 	if (!allocatePattern(editor.editPattern))
 	{
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		goto trackLoadError;
 	}
 
@@ -1241,7 +1255,7 @@
 	note_t *p = pattern[editor.editPattern];
 	if (p == NULL)
 	{
-		okBox(0, "System message", "The current pattern is empty!");
+		okBox(0, "System message", "The current pattern is empty!", NULL);
 		return false;
 	}
 
@@ -1248,7 +1262,7 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
 	{
-		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -1261,7 +1275,7 @@
 	if (fwrite(&h, sizeof (h), 1, f) !=  1)
 	{
 		fclose(f);
-		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -1268,7 +1282,7 @@
 	if (fwrite(saveBuff, h.numRows * sizeof (note_t), 1, f) != 1)
 	{
 		fclose(f);
-		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -1283,25 +1297,25 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
 	if (f == NULL)
 	{
-		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		return false;
 	}
 
 	if (!allocatePattern(editor.editPattern))
 	{
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		goto loadPattError;
 	}
 
 	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
 	{
-		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		goto loadPattError;
 	}
 
 	if (h.version != 1)
 	{
-		okBox(0, "System message", "Incompatible format version!");
+		okBox(0, "System message", "Incompatible format version!", NULL);
 		goto loadPattError;
 	}
 
@@ -1314,7 +1328,7 @@
 	if (fread(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
 	{
 		unlockMixerCallback();
-		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBox(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		goto loadPattError;
 	}
 
@@ -1373,7 +1387,7 @@
 	note_t *p = pattern[editor.editPattern];
 	if (p == NULL)
 	{
-		okBox(0, "System message", "The current pattern is empty!");
+		okBox(0, "System message", "The current pattern is empty!", NULL);
 		return false;
 	}
 
@@ -1380,7 +1394,7 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
 	{
-		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -1390,7 +1404,7 @@
 	if (fwrite(&h, 1, sizeof (h), f) != sizeof (h))
 	{
 		fclose(f);
-		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -1397,7 +1411,7 @@
 	if (fwrite(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
 	{
 		fclose(f);
-		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBox(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -2635,7 +2649,7 @@
 
 void pbZap(void)
 {
-	const int16_t choice = okBox(4, "System request", "Total devastation of the...");
+	const int16_t choice = okBox(3, "System request", "Total devastation of the...", NULL);
 
 	if (choice == 1) // zap all
 	{
@@ -2695,16 +2709,23 @@
 
 void shrinkPattern(void)
 {
-	if (okBox(2, "System request", "Shrink pattern?") != 1)
-		return;
-
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
 	int16_t numRows = patternNumRows[editor.editPattern];
+	resumeMusic();
+
 	if (numRows <= 1)
+	{
+		okBox(0, "System message", "Pattern is too short to be shrunk!", NULL);
 		return;
+	}
 
+	if (okBox(2, "System request", "Shrink pattern?", NULL) != 1)
+		return;
+
 	lockMixerCallback();
 
-	note_t *p = pattern[editor.editPattern];
+	note_t *p = pattern[curPattern];
 	if (p != NULL)
 	{
 		for (int32_t i = 0; i < numRows / 2; i++)
@@ -2714,10 +2735,10 @@
 		}
 	}
 
-	patternNumRows[editor.editPattern] /= 2;
-	numRows = patternNumRows[editor.editPattern];
+	patternNumRows[curPattern] /= 2;
+	numRows = patternNumRows[curPattern];
 
-	if (song.pattNum == editor.editPattern)
+	if (song.pattNum == curPattern)
 		song.currNumRows = numRows;
 
 	song.row /= 2;
@@ -2735,45 +2756,48 @@
 
 void expandPattern(void)
 {
+	pauseMusic();
+	const volatile uint16_t curPattern = editor.editPattern;
 	int16_t numRows = patternNumRows[editor.editPattern];
+	resumeMusic();
+
 	if (numRows > MAX_PATT_LEN/2)
 	{
-		okBox(0, "System message", "Pattern is too long to be expanded.");
+		okBox(0, "System message", "Pattern is too long to be expanded!", NULL);
+		return;
 	}
-	else
+
+	lockMixerCallback();
+
+	note_t *p = pattern[curPattern];
+	if (p != NULL)
 	{
-		lockMixerCallback();
+		memcpy(tmpPattern, p, numRows * TRACK_WIDTH);
 
-		if (pattern[editor.editPattern] != NULL)
+		for (int32_t i = 0; i < numRows; i++)
 		{
-			note_t *p = pattern[editor.editPattern];
-			memcpy(tmpPattern, p, numRows * TRACK_WIDTH);
-			
-			for (int32_t i = 0; i < numRows; i++)
-			{
-				for (int32_t j = 0; j < MAX_CHANNELS; j++)
-					p[((i * 2) * MAX_CHANNELS) + j] = tmpPattern[(i * MAX_CHANNELS) + j];
+			for (int32_t j = 0; j < MAX_CHANNELS; j++)
+				p[((i * 2) * MAX_CHANNELS) + j] = tmpPattern[(i * MAX_CHANNELS) + j];
 
-				memset(&p[((i * 2) + 1) * MAX_CHANNELS], 0, TRACK_WIDTH);
-			}
+			memset(&p[((i * 2) + 1) * MAX_CHANNELS], 0, TRACK_WIDTH);
 		}
+	}
+	
+	patternNumRows[curPattern] *= 2;
+	numRows = patternNumRows[curPattern];
 
-		patternNumRows[editor.editPattern] *= 2;
-		numRows = patternNumRows[editor.editPattern];
+	if (song.pattNum == curPattern)
+		song.currNumRows = numRows;
 
-		if (song.pattNum == editor.editPattern)
-			song.currNumRows = numRows;
+	song.row *= 2;
+	if (song.row >= numRows)
+		song.row = numRows-1;
 
-		song.row *= 2;
-		if (song.row >= numRows)
-			song.row = numRows-1;
+	editor.row = song.row;
 
-		editor.row = song.row;
+	ui.updatePatternEditor = true;
+	ui.updatePosSections = true;
 
-		ui.updatePatternEditor = true;
-		ui.updatePosSections = true;
-
-		unlockMixerCallback();
-		setSongModifiedFlag();
-	}
+	unlockMixerCallback();
+	setSongModifiedFlag();
 }
--- a/src/ft2_pattern_ed.h
+++ b/src/ft2_pattern_ed.h
@@ -10,13 +10,13 @@
 	VOLUME_COLUMN_HIDDEN = 0,
 	VOLUME_COLUMN_SHOWN = 1,
 
-	TRANSP_ALL_INST = 0,
-	TRANSP_CUR_INST = 1,
-
 	TRANSP_TRACK = 0,
 	TRANSP_PATT = 1,
 	TRANSP_SONG = 2,
-	TRANSP_BLOCK = 3
+	TRANSP_BLOCK = 3,
+
+	TRANSP_CUR_INSTRUMENT = false,
+	TRANSP_ALL_INSTRUMENTS = true,
 };
 
 typedef struct xtHdr_t
@@ -58,7 +58,7 @@
 	int16_t markX1, markX2, markY1, markY2;
 } pattMark_t;
 
-extern pattMark_t pattMark; // ft2_pattern_ed.c
+extern volatile pattMark_t pattMark; // ft2_pattern_ed.c
 
 void resetPlaybackTime(void);
 
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -271,7 +271,7 @@
 	if (period == 0)
 		return 0.0; // in FT2, a period of 0 results in 0Hz
 
-	return (double)(8363 * 1712) / period;
+	return (8363.0 * 1712.0) / period;
 }
 
 double dPeriod2Hz(int32_t period)
@@ -2236,7 +2236,7 @@
 	int32_t i;
 	channel_t *ch;
 
-	if (musicPaused || !songPlaying)
+	if (!songPlaying)
 	{
 		ch = channel;
 		for (i = 0; i < song.numChannels; i++, ch++)
@@ -2799,7 +2799,7 @@
 
 	calcPanningTable();
 
-	setPos(0, 0, true);
+	setPos(0, 0, true); // important!
 
 	if (!allocateInstr(0))
 	{
@@ -3113,7 +3113,6 @@
 	editor.curPlaySmp = 255;
 
 	stopAllScopes();
-	resetAudioDither();
 	resetCachedMixerVars();
 
 	// wait for scope thread to finish, so that we know pointers aren't deprecated
--- a/src/ft2_sample_ed.c
+++ b/src/ft2_sample_ed.c
@@ -728,7 +728,7 @@
 
 error:
 	resumeAudio();
-	okBoxThreadSafe(0, "System message", "Not enough memory!");
+	okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 	return true;
 
 	(void)ptr;
@@ -743,7 +743,7 @@
 	thread = SDL_CreateThread(copySampleThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -1644,7 +1644,7 @@
 	}
 	else
 	{
-		okBox(0, "System message", "Cannot show empty range!");
+		okBox(0, "System message", "Cannot show empty range!", NULL);
 	}
 }
 
@@ -1829,7 +1829,7 @@
 
 	if (smpEd_Rx1 == smpEd_Rx2)
 	{
-		okBox(0, "System message", "No range specified!");
+		okBox(0, "System message", "No range specified!", NULL);
 		return;
 	}
 
@@ -1839,19 +1839,19 @@
 
 	if (smpEd_SysReqText[0] == '\0')
 	{
-		okBox(0, "System message", "Filename can't be empty!");
+		okBox(0, "System message", "Filename can't be empty!", NULL);
 		return;
 	}
 
 	if (smpEd_SysReqText[0] == '.')
 	{
-		okBox(0, "System message", "The very first character in the filename can't be '.' (dot)!");
+		okBox(0, "System message", "The very first character in the filename can't be '.' (dot)!", NULL);
 		return;
 	}
 
 	if (strpbrk(smpEd_SysReqText, "\\/:*?\"<>|") != NULL)
 	{
-		okBox(0, "System message", "The filename can't contain the following characters: \\ / : * ? \" < > |");
+		okBox(0, "System message", "The filename can't contain the following characters: \\ / : * ? \" < > |", NULL);
 		return;
 	}
 
@@ -1865,7 +1865,7 @@
 	UNICHAR *filenameU = cp437ToUnichar(smpEd_SysReqText);
 	if (filenameU == NULL)
 	{
-		okBox(0, "System message", "Out of memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return;
 	}
 
@@ -1873,7 +1873,7 @@
 	{
 		char buf[256];
 		createFileOverwriteText(smpEd_SysReqText, buf);
-		if (okBox(2, "System request", buf) != 1)
+		if (okBox(2, "System request", buf, NULL) != 1)
 			return;
 	}
 
@@ -1904,7 +1904,7 @@
 				fixSample(s);
 				resumeAudio();
 
-				okBoxThreadSafe(0, "System message", "Not enough memory!");
+				okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 				return false;
 			}
 
@@ -1926,7 +1926,7 @@
 			if (!cropMode)
 				resumeAudio();
 
-			okBoxThreadSafe(0, "System message", "Not enough memory!");
+			okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 			return false;
 		}
 
@@ -1986,7 +1986,7 @@
 static int32_t SDLCALL sampCutThread(void *ptr)
 {
 	if (!cutRange(false, smpEd_Rx1, smpEd_Rx2))
-		okBoxThreadSafe(0, "System message", "Not enough memory! (Disable \"cut to buffer\")");
+		okBoxThreadSafe(0, "System message", "Not enough memory! (Disable \"cut to buffer\")", NULL);
 	else
 		writeSampleFlag = true;
 
@@ -2005,7 +2005,7 @@
 	thread = SDL_CreateThread(sampCutThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -2020,7 +2020,7 @@
 
 	if (!getCopyBuffer(smpEd_Rx2- smpEd_Rx1, sample16Bit))
 	{
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
+		okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 		return true;
 	}
 
@@ -2053,7 +2053,7 @@
 	thread = SDL_CreateThread(sampCopyThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -2066,7 +2066,7 @@
 
 	if (!reallocateSmpData(s, smpCopySize, sample16Bit))
 	{
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
+		okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 		return;
 	}
 
@@ -2152,7 +2152,7 @@
 
 	if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr))
 	{
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
+		okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 		return true;
 	}
 
@@ -2167,7 +2167,7 @@
 
 	if (s->length+smpCopySize > MAX_SAMPLE_LEN)
 	{
-		okBoxThreadSafe(0, "System message", "Not enough room in sample!");
+		okBoxThreadSafe(0, "System message", "Not enough room in sample!", NULL);
 		return true;
 	}
 
@@ -2177,13 +2177,13 @@
 
 	if (newLength > MAX_SAMPLE_LEN)
 	{
-		okBoxThreadSafe(0, "System message", "Not enough room in sample!");
+		okBoxThreadSafe(0, "System message", "Not enough room in sample!", NULL);
 		return true;
 	}
 
 	if (!allocateSmpDataPtr(&sp, newLength, sample16Bit))
 	{
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
+		okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 		return true;
 	}
 
@@ -2255,7 +2255,7 @@
 		sample_t *s = getCurSample();
 		if (s != NULL && s->dataPtr != NULL && s->length > 0)
 		{
-			if (okBox(2, "System request", "The current sample is not empty. Do you really want to overwrite it?") != 1)
+			if (okBox(2, "System request", "The current sample is not empty. Do you really want to overwrite it?", NULL) != 1)
 				return;
 		}
 	}
@@ -2264,7 +2264,7 @@
 	thread = SDL_CreateThread(sampPasteThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -2319,7 +2319,7 @@
 	thread = SDL_CreateThread(sampCropThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -2337,7 +2337,7 @@
 	// check if the sample has the loop flag enabled
 	if (GET_LOOPTYPE(s->flags) == LOOP_OFF)
 	{
-		okBox(0, "System message", "X-Fade can only be used on a loop-enabled sample!");
+		okBox(0, "System message", "X-Fade can only be used on a loop-enabled sample!", NULL);
 		return;
 	}
 
@@ -2344,7 +2344,7 @@
 	// check if we selected a range
 	if (smpEd_Rx2 == 0)
 	{
-		okBox(0, "System message", "No range selected! Make a small range that includes loop start or loop end.");
+		okBox(0, "System message", "No range selected! Make a small range that includes loop start or loop end.", NULL);
 		return;
 	}
 
@@ -2351,7 +2351,7 @@
 	// check if we selected a valid range length
 	if (smpEd_Rx2-smpEd_Rx1 <= 2)
 	{
-		okBox(0, "System message", "Invalid range!");
+		okBox(0, "System message", "Invalid range!", NULL);
 		return;
 	}
 
@@ -2367,7 +2367,7 @@
 		{
 			if (x2 <= y1 || x2 >= s->loopStart+s->loopLength)
 			{
-				okBox(0, "System message", "Error: No loop point found inside marked data.");
+				okBox(0, "System message", "Error: No loop point found inside marked data.", NULL);
 				return;
 			}
 
@@ -2380,13 +2380,13 @@
 
 			if (d1 < 1 || d2 < 1 || d3 < 1)
 			{
-				okBox(0, "System message", "Invalid range! Try to mark more data.");
+				okBox(0, "System message", "Invalid range! Try to mark more data.", NULL);
 				return;
 			}
 
 			if (y1-d1 < 0 || y1+d1 >= s->length)
 			{
-				okBox(0, "System message", "Not enough sample data outside loop!");
+				okBox(0, "System message", "Not enough sample data outside loop!", NULL);
 				return;
 			}
 
@@ -2430,7 +2430,7 @@
 			y1 += s->loopLength;
 			if (x1 >= y1 || x2 <= y1 || x2 >= s->length)
 			{
-				okBox(0, "System message", "Error: No loop point found inside marked data.");
+				okBox(0, "System message", "Error: No loop point found inside marked data.", NULL);
 				return;
 			}
 
@@ -2443,13 +2443,13 @@
 
 			if (d1 < 1 || d2 < 1 || d3 < 1)
 			{
-				okBox(0, "System message", "Invalid range! Try to mark more data.");
+				okBox(0, "System message", "Invalid range! Try to mark more data.", NULL);
 				return;
 			}
 
 			if (y1-d1 < 0 || y1+d1 >= s->length)
 			{
-				okBox(0, "System message", "Not enough sample data outside loop!");
+				okBox(0, "System message", "Not enough sample data outside loop!", NULL);
 				return;
 			}
 
@@ -2499,7 +2499,7 @@
 
 		if (x1 < 0 || x2 <= x1 || x2 >= s->length)
 		{
-			okBox(0, "System message", "Invalid range!");
+			okBox(0, "System message", "Invalid range!", NULL);
 			return;
 		}
 
@@ -2511,7 +2511,7 @@
 
 		if (y1 < 0 || y2+length >= s->length)
 		{
-			okBox(0, "System message", "Not enough sample data outside loop!");
+			okBox(0, "System message", "Not enough sample data outside loop!", NULL);
 			return;
 		}
 
@@ -2521,7 +2521,7 @@
 
 		if (y1+length <= s->loopStart || d1 == 0 || d3 == 0 || d1 > s->loopLength)
 		{
-			okBox(0, "System message", "Invalid range!");
+			okBox(0, "System message", "Invalid range!", NULL);
 			return;
 		}
 
@@ -2680,13 +2680,13 @@
 	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	if (okBox(2, "System request", "Convert sampledata?") == 1)
+	if (okBox(2, "System request", "Pre-convert sample data?", NULL) == 1)
 	{
 		mouseAnimOn();
 		thread = SDL_CreateThread(convSmp8Bit, NULL, NULL);
 		if (thread == NULL)
 		{
-			okBox(0, "System message", "Couldn't create thread!");
+			okBox(0, "System message", "Couldn't create thread!", NULL);
 			return;
 		}
 
@@ -2720,7 +2720,7 @@
 
 	if (!reallocateSmpData(s, s->length, true))
 	{
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
+		okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 		return true;
 	}
 
@@ -2748,13 +2748,13 @@
 	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	if (okBox(2, "System request", "Convert sampledata?") == 1)
+	if (okBox(2, "System request", "Pre-convert sample data?", NULL) == 1)
 	{
 		mouseAnimOn();
 		thread = SDL_CreateThread(convSmp16Bit, NULL, NULL);
 		if (thread == NULL)
 		{
-			okBox(0, "System message", "Couldn't create thread!");
+			okBox(0, "System message", "Couldn't create thread!", NULL);
 			return;
 		}
 
@@ -2785,7 +2785,7 @@
 	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	if (okBox(1, "System request", "Clear sample?") != 1)
+	if (okBox(1, "System request", "Clear sample?", NULL) != 1)
 		return;
 
 	freeSample(editor.curInstr, editor.curSmp);
@@ -2802,17 +2802,17 @@
 	const bool hasLoop = GET_LOOPTYPE(s->flags) != LOOP_OFF;
 	if (!hasLoop)
 	{
-		okBox(0, "System message", "Only a looped sample can be minimized!");
+		okBox(0, "System message", "Only a looped sample can be minimized!", NULL);
 		return;
 	}
 
 	if (s->loopStart+s->loopLength >= s->length)
 	{
-		okBox(0, "System message", "The sample can't be minimized any further.");
+		okBox(0, "System message", "This sample is already minimized.", NULL);
 		return;
 	}
 
-	if (okBox(1, "System request", "Minimize sample?") != 1)
+	if (okBox(1, "System request", "Minimize sample?", NULL) != 1)
 		return;
 	
 	lockMixerCallback();
@@ -3613,7 +3613,7 @@
 	thread = SDL_CreateThread(sampleBackwardsThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -3662,7 +3662,7 @@
 	thread = SDL_CreateThread(sampleChangeSignThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -3708,7 +3708,7 @@
 
 	if (!(s->flags & SAMPLE_16BIT))
 	{
-		if (okBox(2, "System request", "Byte swapping only makes sense on a 16-bit sample. Continue?") != 1)
+		if (okBox(2, "System request", "Byte swapping rarely makes sense on an 8-bit sample. Continue?", NULL) != 1)
 			return;
 	}
 
@@ -3716,7 +3716,7 @@
 	thread = SDL_CreateThread(sampleByteSwapThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -3828,7 +3828,7 @@
 	thread = SDL_CreateThread(fixDCThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
--- a/src/ft2_sample_ed_features.c
+++ b/src/ft2_sample_ed_features.c
@@ -178,7 +178,7 @@
 	thread = SDL_CreateThread(resampleThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -345,7 +345,7 @@
 	windowClose(false);
 
 	if (outOfMemory)
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 }
 
 static void cbEchoAddMemory(void)
@@ -559,7 +559,7 @@
 	thread = SDL_CreateThread(createEchoThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -803,7 +803,7 @@
 	windowClose(echo_AddMemory ? false : true);
 
 	if (outOfMemory)
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 }
 
 static int32_t SDLCALL mixThread(void *ptr)
@@ -946,7 +946,7 @@
 	thread = SDL_CreateThread(mixThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -1101,7 +1101,7 @@
 	windowClose(false);
 
 	if (outOfMemory)
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 }
 
 static void sbSetStartVolPos(uint32_t pos)
@@ -1285,7 +1285,7 @@
 	thread = SDL_CreateThread(applyVolumeThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
@@ -1391,7 +1391,7 @@
 	thread = SDL_CreateThread(getMaxScaleThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
--- a/src/ft2_sample_saver.c
+++ b/src/ft2_sample_saver.c
@@ -122,7 +122,7 @@
 	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0)
 	{
-		okBoxThreadSafe(0, "System message", "The sample is empty!");
+		okBoxThreadSafe(0, "System message", "The sample is empty!", NULL);
 		return false;
 	}
 
@@ -143,7 +143,7 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -150,7 +150,7 @@
 	if (fwrite(samplePtr, sampleLen, 1, f) != 1)
 	{
 		fclose(f);
-		okBoxThreadSafe(0, "System message", "Error saving sample: General I/O error!");
+		okBoxThreadSafe(0, "System message", "Error saving sample: General I/O error!", NULL);
 		return false;
 	}
 
@@ -206,7 +206,7 @@
 	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0)
 	{
-		okBoxThreadSafe(0, "System message", "The sample is empty!");
+		okBoxThreadSafe(0, "System message", "The sample is empty!", NULL);
 		return false;
 	}
 
@@ -215,7 +215,7 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -331,7 +331,7 @@
 	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0)
 	{
-		okBoxThreadSafe(0, "System message", "The sample is empty!");
+		okBoxThreadSafe(0, "System message", "The sample is empty!", NULL);
 		return false;
 	}
 
@@ -340,7 +340,7 @@
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -503,7 +503,7 @@
 {
 	if (editor.tmpFilenameU == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during saving! Is the file in use?", NULL);
 		return false;
 	}
 
@@ -538,7 +538,7 @@
 	thread = SDL_CreateThread(saveSampleThread, NULL, NULL);
 	if (thread == NULL)
 	{
-		okBoxThreadSafe(0, "System message", "Couldn't create thread!");
+		okBoxThreadSafe(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
--- a/src/ft2_sampling.c
+++ b/src/ft2_sampling.c
@@ -298,7 +298,7 @@
 	{
 		outOfMemoryFlag = false;
 		stopSampling();
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return;
 	}
 
@@ -306,7 +306,7 @@
 	{
 		noMoreRoomFlag = false;
 		stopSampling();
-		okBox(0, "System message", "Not more room in sample!");
+		okBox(0, "System message", "Not more room in sample!", NULL);
 		return;
 	}
 
@@ -326,7 +326,7 @@
 void startSampling(void)
 {
 #if SDL_MAJOR_VERSION == 2 && SDL_MINOR_VERSION == 0 && SDL_PATCHLEVEL < 5
-	okBox(0, "System message", "This program needs to be compiled with SDL 2.0.5 or later to support audio sampling.");
+	okBox(0, "System message", "This program needs to be compiled with SDL 2.0.5 or later to support audio sampling.", NULL);
 	return;
 #else
 	SDL_AudioSpec want, have;
@@ -334,7 +334,7 @@
 	if (editor.samplingAudioFlag || editor.curInstr == 0)
 		return;
 
-	int16_t result = okBox(9, "System request", "Stereo sampling will use the next sample slot. While ongoing, press any key to stop.");
+	int16_t result = okBox(5, "System request", "Stereo sampling will use the next sample slot. While ongoing, press any key to stop.", NULL);
 	if (result == 0 || result == 3)
 		return;
 
@@ -358,7 +358,7 @@
 	recordDev = SDL_OpenAudioDevice(audio.currInputDevice, true, &want, &have, 0);
 	if (recordDev == 0)
 	{
-		okBox(0, "System message", "Couldn't open the input device! Try adjusting the input rate at the config screen.");
+		okBox(0, "System message", "Couldn't open the input device! Try adjusting the input rate at the config screen.", NULL);
 		return;
 	}
 
@@ -370,7 +370,7 @@
 	if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr))
 	{
 		stopSampling();
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return;
 	}
 
@@ -377,7 +377,7 @@
 	if (sampleInStereo && editor.curSmp+1 >= MAX_SMP_PER_INST)
 	{
 		stopSampling();
-		okBox(0, "System message", "Error: No free sample slot for the right channel!");
+		okBox(0, "System message", "Error: No free sample slot for the right channel!", NULL);
 		return;
 	}
 
--- a/src/ft2_sysreqs.c
+++ b/src/ft2_sysreqs.c
@@ -15,40 +15,38 @@
 #define SYSTEM_REQUEST_Y 249
 #define SYSTEM_REQUEST_Y_EXT 91
 
-#define NUM_SYSREQ_TYPES 10
-
 // globalized
 okBoxData_t okBoxData;
 void (*loaderMsgBox)(const char *, ...);
-int16_t (*loaderSysReq)(int16_t, const char *, const char *);
+int16_t (*loaderSysReq)(int16_t, const char *, const char *, void (*)(void));
 // ----------------
 
+#define NUM_SYSREQ_TYPES 6
+
 static char *buttonText[NUM_SYSREQ_TYPES][5] =
 {
+	// generic dialogs
 	{ "OK", "","","","" },
 	{ "OK", "Cancel", "","","" },
 	{ "Yes", "No", "","","" },
-	{ "","","","","" }, // removed (TODO: Re-arrange and fix all sysreq call offsets...)
-	{ "All", "Song", "Instruments", "Cancel", "" },
-	{ "Read left", "Read right", "Convert", "", "" },
-	{ "OK", "","","","" },
-	{ "OK", "","","","" },
-	{ "Sorry...", "","","","" },
-	{ "Mono", "Stereo", "Cancel", "","" }
+
+	// custom dialogs
+	{ "All", "Song", "Instruments", "Cancel", "" },   // "song clear" dialog
+	{ "Read left", "Read right", "Convert", "", "" }, // "stereo sample loader" dialog
+	{ "Mono", "Stereo", "Cancel", "","" }             // "audio sampling" dialog
 };
 
 static SDL_Keycode shortCut[NUM_SYSREQ_TYPES][5] =
 {
+	// generic dialogs
 	{ SDLK_o, 0,      0,      0,      0 },
 	{ SDLK_o, SDLK_c, 0,      0,      0 },
 	{ SDLK_y, SDLK_n, 0,      0,      0 },
-	{ 0, 0, 0, 0, 0 }, // deprecated
-	{ SDLK_a, SDLK_s, SDLK_i, SDLK_c, 0 },
-	{ SDLK_l, SDLK_r, SDLK_c, 0,      0 },
-	{ SDLK_o, 0,      0,      0,      0 },
-	{ SDLK_o, 0,      0,      0,      0 },
-	{ SDLK_s, 0,      0,      0,      0 },
-	{ SDLK_m, SDLK_s, SDLK_c, 0,      0 },
+
+	// custom dialogs
+	{ SDLK_a, SDLK_s, SDLK_i, SDLK_c, 0 }, // "song clear" dialog
+	{ SDLK_l, SDLK_r, SDLK_c, 0,      0 }, // "stereo sample loader" dialog 
+	{ SDLK_m, SDLK_s, SDLK_c, 0,      0 }, // "audio sampling" dialog
 };
 
 typedef struct quitType_t
@@ -61,7 +59,7 @@
 
 static quitType_t quitMessage[QUIT_MESSAGES] =
 {
-	// removed unsuitable/offensive ones...
+	// edited (and removed) some of the original quit texts...
 
 	{ "Do you really want to quit?", 2 },
 	{ "Tired already?", 2 },
@@ -72,7 +70,7 @@
 	{ "Did you really press the right key?", 2 },
 	{ "Hope ya did some good. Press >OK< to quit.", 1 },
 	{ "Quit? Only for a good reason you are allowed to press >OK<.", 1 },
-	{ "Are we at the end of a Fasttracker round?", 2 },
+	{ "Are we at the end of a Fasttracker II round?", 2 },
 	{ "Hope you're doing the compulsory \"Exit ceremony\" before pressing >OK<.", 1 },
 };
 
@@ -86,7 +84,7 @@
 	vsnprintf(strBuf, sizeof (strBuf), fmt, args);
 	va_end(args);
 
-	okBoxThreadSafe(0, "System message", fmt);
+	okBoxThreadSafe(0, "System message", fmt, NULL);
 }
 
 void myLoaderMsgBox(const char *fmt, ...)
@@ -99,13 +97,13 @@
 	vsnprintf(strBuf, sizeof (strBuf), fmt, args);
 	va_end(args);
 
-	okBox(0, "System message", fmt);
+	okBox(0, "System message", fmt, NULL);
 }
 
 static void drawWindow(uint16_t w)
 {
 	const uint16_t h = SYSTEM_REQUEST_H;
-	const uint16_t x = (SCREEN_W - w) >> 1;
+	const uint16_t x = (SCREEN_W - w) / 2;
 	const uint16_t y = ui.extended ? 91 : SYSTEM_REQUEST_Y;
 
 	// main fill
@@ -181,13 +179,12 @@
 }
 
 // WARNING: This routine must ONLY be called from the main input/video thread!
-int16_t okBox(int16_t type, const char *headline, const char *text)
+// If the checkBoxCallback argument is set, then you get a "Do not show again" checkbox.
+int16_t okBox(int16_t type, const char *headline, const char *text, void (*checkBoxCallback)(void))
 {
 #define PUSHBUTTON_W 80
 
-	uint16_t i;
 	SDL_Event inputEvent;
-	pushButton_t *p;
 
 	if (editor.editTextFlag)
 		exitTextEditing();
@@ -227,19 +224,19 @@
 	if (wlen > 600)
 		wlen = 600;
 
-	uint16_t headlineX = (SCREEN_W - hlen) >> 1;
-	uint16_t textX = (SCREEN_W - tlen) >> 1;
-	uint16_t x = (SCREEN_W - wlen) >> 1;
+	const uint16_t headlineX = (SCREEN_W - hlen) / 2;
+	const uint16_t textX = (SCREEN_W - tlen) / 2;
+	const uint16_t x = (SCREEN_W - wlen) / 2;
 
-	// the box y position differs in extended pattern editor mode
-	uint16_t y = ui.extended ? SYSTEM_REQUEST_Y_EXT : SYSTEM_REQUEST_Y;
+	// the dialog's y position differs in extended pattern editor mode
+	const uint16_t y = ui.extended ? SYSTEM_REQUEST_Y_EXT : SYSTEM_REQUEST_Y;
 
 	// set up buttons
-	for (i = 0; i < numButtons; i++)
+	for (uint16_t i = 0; i < numButtons; i++)
 	{
-		p = &pushButtons[i];
+		pushButton_t *p = &pushButtons[i];
 
-		p->x = ((SCREEN_W - tx) >> 1) + (i * 100);
+		p->x = ((SCREEN_W - tx) / 2) + (i * 100);
 		p->y = y + 42;
 		p->w = PUSHBUTTON_W;
 		p->h = 16;
@@ -247,8 +244,9 @@
 		p->visible = true;
 	}
 
-	// set up checkbox (special okBox types only!)
-	if (type >= 6 && type <= 7)
+	// set up "don't show again" checkbox (if callback present)
+	bool hasCheckbox = (checkBoxCallback != NULL);
+	if (hasCheckbox)
 	{
 		checkBox_t *c = &checkBoxes[0];
 		c->x = x + 5;
@@ -256,18 +254,7 @@
 		c->clickAreaWidth = 116;
 		c->clickAreaHeight = 12;
 		c->checked = false;
-
-		if (type == 6)
-		{
-			// S3M load warning
-			c->callbackFunc = configToggleImportWarning;
-		}
-		else if (type == 7)
-		{
-			// "setting not yet applied"
-			c->callbackFunc = configToggleNotYetAppliedWarning;
-		}
-
+		c->callbackFunc = checkBoxCallback;
 		c->visible = true;
 	}
 
@@ -313,7 +300,7 @@
 					keyb.ignoreCurrKeyUp = true; // don't handle key up event for any keys that were pressed
 				}
 
-				for (i = 0; i < numButtons; i++)
+				for (uint16_t i = 0; i < numButtons; i++)
 				{
 					if (shortCut[type][i] == inputEvent.key.keysym.sym)
 					{
@@ -328,7 +315,7 @@
 			{
 				if (mouseButtonUpLogic(inputEvent.button.button))
 				{
-					if (type >= 6 && type <= 7)
+					if (hasCheckbox)
 						testCheckBoxMouseRelease();
 
 					returnVal = testPushButtonMouseRelease(false) + 1;
@@ -361,8 +348,8 @@
 		drawWindow(wlen);
 		textOutShadow(headlineX, y +  4, PAL_FORGRND, PAL_BUTTON2, headline);
 		textOutShadow(textX,     y + 24, PAL_FORGRND, PAL_BUTTON2, text);
-		for (i = 0; i < numButtons; i++) drawPushButton(i);
-		if (type >= 6 && type <= 7)
+		for (uint16_t i = 0; i < numButtons; i++) drawPushButton(i);
+		if (hasCheckbox)
 		{
 			drawCheckBox(0);
 			textOutShadow(x + 21, y + 52, PAL_FORGRND, PAL_BUTTON2, "Don't show again");
@@ -372,10 +359,10 @@
 		endFPSCounter();
 	}
 
-	for (i = 0; i < numButtons; i++)
+	for (uint16_t i = 0; i < numButtons; i++)
 		hidePushButton(i);
 
-	if (type >= 6 && type <= 7)
+	if (hasCheckbox)
 		hideCheckBox(0);
 
 	mouse.lastUsedObjectID = oldLastUsedObjectID;
@@ -390,7 +377,7 @@
 
 /* WARNING:
 ** - This routine must ONLY be called from the main input/video thread!!
-** - edText must be null-terminated
+** - edText must be NUL-terminated
 */
 int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxStrLen)
 {
@@ -397,7 +384,6 @@
 #define PUSHBUTTON_W 80
 #define TEXTBOX_W 250
 
-	uint16_t wlen, i;
 	SDL_Event inputEvent;
 
 	if (editor.editTextFlag)
@@ -426,12 +412,12 @@
 	t->changeMouseCursor = true;
 	t->renderBufW = (9 + 1) * t->maxChars; // 9 = max character/glyph width possible
 	t->renderBufH = 10; // 10 = max character height possible
-	t->renderW = t->w - (t->tx << 1);
+	t->renderW = t->w - (t->tx * 2);
 
 	t->renderBuf = (uint8_t *)malloc(t->renderBufW * t->renderBufH * sizeof (int8_t));
 	if (t->renderBuf == NULL)
 	{
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return 0;
 	}
 
@@ -440,8 +426,8 @@
 	ui.sysReqShown = true;
 	mouseAnimOff();
 
-	wlen = textWidth(headline);
-	uint16_t headlineX = (SCREEN_W - wlen) >> 1;
+	uint16_t wlen = textWidth(headline);
+	const uint16_t headlineX = (SCREEN_W - wlen) / 2;
 
 	// count number of buttons
 	uint16_t numButtons = 0;
@@ -461,10 +447,10 @@
 		wlen = 600;
 
 	// the box y position differs in extended pattern editor mode
-	uint16_t y = ui.extended ? SYSTEM_REQUEST_Y_EXT : SYSTEM_REQUEST_Y;
+	const uint16_t y = ui.extended ? SYSTEM_REQUEST_Y_EXT : SYSTEM_REQUEST_Y;
 
 	// set further text box settings
-	t->x = (SCREEN_W - TEXTBOX_W) >> 1;
+	t->x = (SCREEN_W - TEXTBOX_W) / 2;
 	t->y = y + 24;
 	t->visible = true;
 
@@ -471,11 +457,11 @@
 	// setup buttons
 
 	pushButton_t *p = pushButtons;
-	for (i = 0; i < numButtons; i++, p++)
+	for (uint16_t i = 0; i < numButtons; i++, p++)
 	{
 		p->w = PUSHBUTTON_W;
 		p->h = 16;
-		p->x = ((SCREEN_W - tx) >> 1) + (i * 100);
+		p->x = ((SCREEN_W - tx) / 2) + (i * 100);
 		p->y = y + 42;
 		p->caption = buttonText[type][i];
 		p->visible = true;
@@ -554,7 +540,7 @@
 				}
 				else
 				{
-					for (i = 0; i < numButtons; i++)
+					for (uint16_t i = 0; i < numButtons; i++)
 					{
 						if (shortCut[1][i] == inputEvent.key.keysym.sym)
 						{
@@ -605,7 +591,7 @@
 		hLine(t->x,        t->y + t->h, t->w + 1, PAL_BUTTON1);
 		vLine(t->x + t->w, t->y,        t->h,     PAL_BUTTON1);
 		drawTextBox(0);
-		for (i = 0; i < numButtons; i++) drawPushButton(i);
+		for (uint16_t i = 0; i < numButtons; i++) drawPushButton(i);
 
 		flipFrame();
 		endFPSCounter();
@@ -614,7 +600,7 @@
 	editor.editTextFlag = false;
 	SDL_StopTextInput();
 
-	for (i = 0; i < numButtons; i++)
+	for (uint16_t i = 0; i < numButtons; i++)
 		hidePushButton(i);
 	hideTextBox(0);
 
@@ -631,15 +617,20 @@
 }
 
 // WARNING: This routine must NOT be called from the main input/video thread!
-int16_t okBoxThreadSafe(int16_t type, const char *headline, const char *text)
+// If the checkBoxCallback argument is set, then you get a "Do not show again" checkbox.
+int16_t okBoxThreadSafe(int16_t type, const char *headline, const char *text, void (*checkBoxCallback)(void))
 {
 	if (!editor.mainLoopOngoing)
 		return 0; // main loop was not even started yet, bail out.
 
-	// block multiple calls before they are completed (for safety)
+	// the amount of time to wait is not important, but close to one video frame makes sense
+	const uint32_t waitTime = (uint32_t)((1000.0 / VBLANK_HZ) + 0.5);
+
+	// block multiple calls before they are completed (just in case, for safety)
 	while (okBoxData.active)
-		SDL_Delay(1000 / VBLANK_HZ); // accuracy is not important here
+		SDL_Delay(waitTime);
 
+	okBoxData.checkBoxCallback = checkBoxCallback;
 	okBoxData.type = type;
 	okBoxData.headline = headline;
 	okBoxData.text = text;
@@ -646,7 +637,7 @@
 	okBoxData.active = true;
 
 	while (okBoxData.active)
-		SDL_Delay(1000 / VBLANK_HZ); // accuracy is not important here
+		SDL_Delay(waitTime);
 
 	return okBoxData.returnData;
 }
@@ -653,9 +644,9 @@
 
 static bool askQuit_RandomMsg(void)
 {
-	uint8_t msg = rand() % QUIT_MESSAGES;
-	int16_t button = okBox(quitMessage[msg].type, "System request", quitMessage[msg].text);
+	quitType_t *q = &quitMessage[rand() % QUIT_MESSAGES];
 
+	int16_t button = okBox(q->type, "System request", q->text, NULL);
 	return (button == 1) ? true : false;
 }
 
@@ -666,12 +657,12 @@
 	if (type == ASK_TYPE_QUIT)
 	{
 		button = okBox(2, "System request",
-			"You have unsaved changes in your song. Do you still want to quit and lose ALL changes?");
+			"You have unsaved changes in your song. Do you still want to quit and lose ALL changes?", NULL);
 	}
 	else
 	{
 		button = okBox(2, "System request",
-			"You have unsaved changes in your song. Load new song and lose ALL changes?");
+			"You have unsaved changes in your song. Load new song and lose ALL changes?", NULL);
 	}
 
 	return (button == 1) ? true : false;
--- a/src/ft2_sysreqs.h
+++ b/src/ft2_sysreqs.h
@@ -16,10 +16,14 @@
 	volatile bool active;
 	int16_t type, returnData;
 	const char *headline, *text;
+	void (*checkBoxCallback)(void);
 } okBoxData_t;
 
-int16_t okBoxThreadSafe(int16_t type, const char *headline, const char *text);
-int16_t okBox(int16_t type, const char *headline, const char *text);
+// If the checkBoxCallback argument is set, then you get a "Do not show again" checkbox.
+int16_t okBoxThreadSafe(int16_t type, const char *headline, const char *text, void (*checkBoxCallback)(void));
+int16_t okBox(int16_t type, const char *headline, const char *text, void (*checkBoxCallback)(void));
+// -----------
+
 int16_t quitBox(bool skipQuitMsg);
 int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxStrLen);
 bool askUnsavedChanges(uint8_t type);
@@ -30,7 +34,7 @@
  // ft2_sysreqs.c
 extern okBoxData_t okBoxData;
 extern void (*loaderMsgBox)(const char *, ...);
-extern int16_t (*loaderSysReq)(int16_t, const char *, const char *);
+extern int16_t (*loaderSysReq)(int16_t, const char *, const char *, void (*)(void));
 // ---------------
 
 #endif
--- a/src/ft2_textboxes.c
+++ b/src/ft2_textboxes.c
@@ -145,7 +145,7 @@
 	for (int32_t i = start; i < end; i++)
 		deleteTextWidth += charWidth(t->textPtr[i]);
 
-	// copy markEnd part to markStart, and add null termination
+	// copy markEnd part to markStart, and add NUL-termination
 	const int32_t length = (int32_t)strlen(&t->textPtr[end]);
 	if (length > 0)
 		memcpy(&t->textPtr[start], &t->textPtr[end], length);
@@ -298,7 +298,7 @@
 		if (endPart == NULL)
 		{
 			free(copiedText);
-			okBox(0, "System message", "Not enough memory!");
+			okBox(0, "System message", "Not enough memory!", NULL);
 			return;
 		}
 	}
--- a/src/ft2_trim.c
+++ b/src/ft2_trim.c
@@ -711,7 +711,7 @@
 
 	if (!setTmpInstruments())
 	{
-		okBox(0, "System message", "Not enough memory!");
+		okBox(0, "System message", "Not enough memory!", NULL);
 		return 0;
 	}
 
@@ -834,7 +834,7 @@
 
 	if (!setTmpInstruments())
 	{
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
+		okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 		return true;
 	}
 
@@ -1184,7 +1184,7 @@
 	if (!removePatt && !removeInst && !removeSamp && !removeChans && !removeSmpDataAfterLoop && !convSmpsTo8Bit)
 		return; // nothing to trim...
 
-	if (okBox(2, "System request", "Are you sure you want to trim the song? Making a backup of the song first is recommended.") != 1)
+	if (okBox(2, "System request", "Are you sure you want to trim the song? Making a backup of the song first is recommended.", NULL) != 1)
 		return;
 
 	mouseAnimOn();
--- a/src/ft2_wav_renderer.c
+++ b/src/ft2_wav_renderer.c
@@ -297,13 +297,14 @@
 void dump_TickReplayer(void)
 {
 	replayerBusy = true;
+	if (!musicPaused)
+	{
+		if (audio.volumeRampingFlag)
+			resetRampVolumes();
 
-	if (audio.volumeRampingFlag)
-		resetRampVolumes();
-
-	tickReplayer();
-	updateVoices();
-
+		tickReplayer();
+		updateVoices();
+	}
 	replayerBusy = false;
 }
 
@@ -337,7 +338,7 @@
 	if (!dump_Init(WDFrequency, WDAmp, WDStartPos))
 	{
 		resumeAudio();
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
+		okBoxThreadSafe(0, "System message", "Not enough memory!", NULL);
 		return true;
 	}
 
@@ -425,7 +426,7 @@
 	resumeAudio();
 
 	if (overflow)
-		okBoxThreadSafe(0, "System message", "Rendering stopped, file exceeded 2GB!");
+		okBoxThreadSafe(0, "System message", "Rendering stopped, file exceeded 2GB!", NULL);
 
 	editor.diskOpReadOnOpen = true;
 	return true;
@@ -445,7 +446,7 @@
 	{
 		char buf[256];
 		createFileOverwriteText(filename, buf);
-		if (okBox(2, "System request", buf) != 1)
+		if (okBox(2, "System request", buf, NULL) != 1)
 			return;
 	}
 
@@ -452,7 +453,7 @@
 	editor.wavRendererFileHandle = fopen(filename, "wb");
 	if (editor.wavRendererFileHandle == NULL)
 	{
-		okBox(0, "System message", "General I/O error while writing to WAV (is the file in use)?");
+		okBox(0, "System message", "General I/O error while writing to WAV (is the file in use)?", NULL);
 		return;
 	}
 
@@ -461,7 +462,7 @@
 	if (thread == NULL)
 	{
 		fclose((FILE *)editor.wavRendererFileHandle);
-		okBox(0, "System message", "Couldn't create thread!");
+		okBox(0, "System message", "Couldn't create thread!", NULL);
 		return;
 	}
 
--- a/src/modloaders/ft2_load_s3m.c
+++ b/src/modloaders/ft2_load_s3m.c
@@ -623,7 +623,7 @@
 		loaderMsgBox("Warning: The module contains unsupported AdLib instruments!");
 
 	if (!(config.dontShowAgainFlags & DONT_SHOW_IMPORT_WARNING_FLAG))
-		loaderSysReq(6, "System message", "Loading of this format is not fully supported and can have issues.");
+		loaderSysReq(0, "System message", "Loading of this format is not fully supported and can have issues.", configToggleImportWarning);
 
 	return true;
 }
--- a/src/smploaders/ft2_load_aiff.c
+++ b/src/smploaders/ft2_load_aiff.c
@@ -149,7 +149,7 @@
 
 	int16_t stereoSampleLoadMode = -1;
 	if (aiffIsStereo(f))
-		stereoSampleLoadMode = loaderSysReq(5, "System request", "This is a stereo sample...");
+		stereoSampleLoadMode = loaderSysReq(4, "System request", "This is a stereo sample...", NULL);
 
 	// read sample data
 
--- a/src/smploaders/ft2_load_flac.c
+++ b/src/smploaders/ft2_load_flac.c
@@ -208,7 +208,7 @@
 
 		stereoSampleLoadMode = -1;
 		if (numChannels == 2)
-			stereoSampleLoadMode = loaderSysReq(5, "System request", "This is a stereo sample...");
+			stereoSampleLoadMode = loaderSysReq(4, "System request", "This is a stereo sample...", NULL);
 	}
 
 	// check for RIFF chunks (loop/vol/pan information)
@@ -338,7 +338,7 @@
 	{
 		if (!allocateSmpData(s, sampleLength, sample16Bit))
 		{
-			loaderMsgBox("Error loading sample: Out of memory!");
+			loaderMsgBox("Error loading sample: Not enough memory!");
 			return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
 		}
 
--- a/src/smploaders/ft2_load_raw.c
+++ b/src/smploaders/ft2_load_raw.c
@@ -23,7 +23,7 @@
 
 	if (fread(s->dataPtr, filesize, 1, f) != 1)
 	{
-		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?", NULL);
 		return false;
 	}
 
--- a/src/smploaders/ft2_load_wav.c
+++ b/src/smploaders/ft2_load_wav.c
@@ -180,7 +180,7 @@
 
 	int16_t stereoSampleLoadMode = -1;
 	if (wavIsStereo(f))
-		stereoSampleLoadMode = loaderSysReq(5, "System request", "This is a stereo sample...");
+		stereoSampleLoadMode = loaderSysReq(4, "System request", "This is a stereo sample...", NULL);
 
 	if (bitsPerSample == 8) // 8-BIT INTEGER SAMPLE
 	{
--- a/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_assert.h
+++ b/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_assert.h
@@ -55,6 +55,8 @@
     #define SDL_TriggerBreakpoint() __builtin_debugtrap()
 #elif ( (!defined(__NACL__)) && ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))) )
     #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
+#elif (defined(__GNUC__) || defined(__clang__)) && defined(__riscv)
+    #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "ebreak\n\t" )
 #elif ( defined(__APPLE__) && (defined(__arm64__) || defined(__aarch64__)) )  /* this might work on other ARM targets, but this is a known quantity... */
     #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "brk #22\n\t" )
 #elif defined(__APPLE__) && defined(__arm__)
--- a/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_atomic.h
+++ b/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_atomic.h
@@ -240,7 +240,7 @@
 /* "REP NOP" is PAUSE, coded for tools that don't know it by that name. */
 #if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
     #define SDL_CPUPauseInstruction() __asm__ __volatile__("pause\n")  /* Some assemblers can't do REP NOP, so go with PAUSE. */
-#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)
+#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(__aarch64__)
     #define SDL_CPUPauseInstruction() __asm__ __volatile__("yield" ::: "memory")
 #elif (defined(__powerpc__) || defined(__powerpc64__))
     #define SDL_CPUPauseInstruction() __asm__ __volatile__("or 27,27,27");
--- a/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_hints.h
+++ b/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_hints.h
@@ -1008,6 +1008,15 @@
 #define SDL_HINT_JOYSTICK_THREAD "SDL_JOYSTICK_THREAD"
 
 /**
+  *  \brief  A variable controlling whether Windows.Gaming.Input should be used for controller handling.
+  *
+  *  This variable can be set to the following values:
+  *    "0"       - WGI is not used
+  *    "1"       - WGI is used (the default)
+  */
+#define SDL_HINT_JOYSTICK_WGI "SDL_JOYSTICK_WGI"
+
+/**
  * \brief Determines whether SDL enforces that DRM master is required in order
  *        to initialize the KMSDRM video backend.
  *
@@ -1464,6 +1473,17 @@
  *  By default SDL does not sync screen surface updates with vertical refresh.
  */
 #define SDL_HINT_RENDER_VSYNC               "SDL_RENDER_VSYNC"
+
+/**
+ *  \brief  A variable controlling whether the Metal render driver select low power device over default one
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - Use the prefered OS device
+ *    "1"       - Select a low power one
+ *
+ *  By default the prefered OS device is used.
+ */
+#define SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE "SDL_RENDER_METAL_PREFER_LOW_POWER_DEVICE"
 
 /**
  *  \brief  A variable controlling if VSYNC is automatically disable if doesn't reach the enough FPS
--- a/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_render.h
+++ b/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_render.h
@@ -1890,7 +1890,7 @@
  * Note that as of SDL 2.0.18, this will return NULL if Metal refuses to give
  * SDL a drawable to render to, which might happen if the window is
  * hidden/minimized/offscreen. This doesn't apply to command encoders for
- * render targets, just the window's backbacker. Check your return values!
+ * render targets, just the window's backbuffer. Check your return values!
  *
  * \param renderer The renderer to query
  * \returns an `id<MTLRenderCommandEncoder>` on success, or NULL if the
--- a/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_revision.h
+++ b/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_revision.h
@@ -1,7 +1,7 @@
 /* Generated by updaterev.sh, do not edit */
 #ifdef SDL_VENDOR_INFO
-#define SDL_REVISION "SDL-release-2.28.0-0-gffa78e6be (" SDL_VENDOR_INFO ")"
+#define SDL_REVISION "SDL-release-2.28.3-0-g8a5ba43d0 (" SDL_VENDOR_INFO ")"
 #else
-#define SDL_REVISION "SDL-release-2.28.0-0-gffa78e6be"
+#define SDL_REVISION "SDL-release-2.28.3-0-g8a5ba43d0"
 #endif
 #define SDL_REVISION_NUMBER 0
--- a/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_version.h
+++ b/vs2019_project/ft2-clone/sdl/include/SDL2/SDL_version.h
@@ -59,7 +59,7 @@
 */
 #define SDL_MAJOR_VERSION   2
 #define SDL_MINOR_VERSION   28
-#define SDL_PATCHLEVEL      0
+#define SDL_PATCHLEVEL      3
 
 /**
  * Macro to determine SDL version program was compiled against.