shithub: pt2-clone

Download patch

ref: 8950a756e1e732b610ec4e2c6b2f9cb7d1fe960d
parent: 6c56bc97933ef5a50092cff22b0f98033bbf2d30
author: Olav Sørensen <olav.sorensen@live.no>
date: Thu Dec 30 17:19:41 EST 2021

Pushed v1.38 code

--- a/release/macos/protracker.ini
+++ b/release/macos/protracker.ini
@@ -11,6 +11,25 @@
 ; ENTRY=VALUE (only strings can have spaces!)
 ;
 
+[SAMPLE LENGTH LIMIT]
+; Limit samples to 64kB (65534/$FFFE), like intended in ProTracker
+;        Syntax: TRUE or FALSE
+; Default value: TRUE
+;       Comment: Setting it to FALSE will remove the 64kB (65534/$FFFE) sample
+;         length limit and support 128kB samples (131070/$1FFFE).
+;         Keep in mind that >64kB samples are not officially supported in most
+;         ProTracker trackers and replayers, and will often result in bugs.
+;
+;         CAUTION: The 9xx command (Set Sample Offset) doesn't work at all if
+;                  the sample length is above 65534/$FFFE. This is a known bug
+;                  in all ProTracker versions. I am not fixing this bug as I
+;                  want to keep the ProTracker 2.3D replayer quirks.
+;
+;         Please do not change this setting unless you're aware of the problems
+;         you might face when creating >64k-sample PT MODs!
+;
+64K_LIMIT=TRUE
+
 [VIDEO SETTINGS]
 ; Video scaling factor
 ;        Syntax: 1X, 2X, 3X ... 9X
--- a/release/other/protracker.ini
+++ b/release/other/protracker.ini
@@ -11,6 +11,25 @@
 ; ENTRY=VALUE (only strings can have spaces!)
 ;
 
+[SAMPLE LENGTH LIMIT]
+; Limit samples to 64kB (65534/$FFFE), like intended in ProTracker
+;        Syntax: TRUE or FALSE
+; Default value: TRUE
+;       Comment: Setting it to FALSE will remove the 64kB (65534/$FFFE) sample
+;         length limit and support 128kB samples (131070/$1FFFE).
+;         Keep in mind that >64kB samples are not officially supported in most
+;         ProTracker trackers and replayers, and will often result in bugs.
+;
+;         CAUTION: The 9xx command (Set Sample Offset) doesn't work at all if
+;                  the sample length is above 65534/$FFFE. This is a known bug
+;                  in all ProTracker versions. I am not fixing this bug as I
+;                  want to keep the ProTracker 2.3D replayer quirks.
+;
+;         Please do not change this setting unless you're aware of the problems
+;         you might face when creating >64k-sample PT MODs!
+;
+64K_LIMIT=TRUE
+
 [VIDEO SETTINGS]
 ; Video scaling factor
 ;        Syntax: 1X, 2X, 3X ... 9X
--- a/release/win32/protracker.ini
+++ b/release/win32/protracker.ini
@@ -11,6 +11,25 @@
 ; ENTRY=VALUE (only strings can have spaces!)
 ;
 
+[SAMPLE LENGTH LIMIT]
+; Limit samples to 64kB (65534/$FFFE), like intended in ProTracker
+;        Syntax: TRUE or FALSE
+; Default value: TRUE
+;       Comment: Setting it to FALSE will remove the 64kB (65534/$FFFE) sample
+;         length limit and support 128kB samples (131070/$1FFFE).
+;         Keep in mind that >64kB samples are not officially supported in most
+;         ProTracker trackers and replayers, and will often result in bugs.
+;
+;         CAUTION: The 9xx command (Set Sample Offset) doesn't work at all if
+;                  the sample length is above 65534/$FFFE. This is a known bug
+;                  in all ProTracker versions. I am not fixing this bug as I
+;                  want to keep the ProTracker 2.3D replayer quirks.
+;
+;         Please do not change this setting unless you're aware of the problems
+;         you might face when creating >64k-sample PT MODs!
+;
+64K_LIMIT=TRUE
+
 [VIDEO SETTINGS]
 ; Video scaling factor
 ;        Syntax: 1X, 2X, 3X ... 9X
--- a/release/win64/protracker.ini
+++ b/release/win64/protracker.ini
@@ -11,6 +11,25 @@
 ; ENTRY=VALUE (only strings can have spaces!)
 ;
 
+[SAMPLE LENGTH LIMIT]
+; Limit samples to 64kB (65534/$FFFE), like intended in ProTracker
+;        Syntax: TRUE or FALSE
+; Default value: TRUE
+;       Comment: Setting it to FALSE will remove the 64kB (65534/$FFFE) sample
+;         length limit and support 128kB samples (131070/$1FFFE).
+;         Keep in mind that >64kB samples are not officially supported in most
+;         ProTracker trackers and replayers, and will often result in bugs.
+;
+;         CAUTION: The 9xx command (Set Sample Offset) doesn't work at all if
+;                  the sample length is above 65534/$FFFE. This is a known bug
+;                  in all ProTracker versions. I am not fixing this bug as I
+;                  want to keep the ProTracker 2.3D replayer quirks.
+;
+;         Please do not change this setting unless you're aware of the problems
+;         you might face when creating >64k-sample PT MODs!
+;
+64K_LIMIT=TRUE
+
 [VIDEO SETTINGS]
 ; Video scaling factor
 ;        Syntax: 1X, 2X, 3X ... 9X
--- a/src/gfx/pt2_gfx_editop.c
+++ b/src/gfx/pt2_gfx_editop.c
@@ -1,5 +1,28 @@
 #include <stdint.h>
 
+// Final unpack length: 352
+// Decoded length: 88 (first four bytes of buffer)
+const uint8_t fix128KPosPackedBMP[81] =
+{
+	0x00,0x00,0x00,0x58,0xCC,0x05,0x55,0x95,0x55,0x6A,0xCC,0x04,0xAA,0xDA,0xAA,0x6A,0xCC,0x04,0xAA,0xDA,
+	0xAA,0x65,0x56,0xA5,0x5A,0x95,0x5A,0xDA,0xAA,0x65,0xF5,0x97,0xD6,0x5F,0xFE,0xDA,0xAA,0x65,0x57,0xD7,
+	0x97,0x95,0x6A,0xDA,0xAA,0x65,0xFF,0x97,0x97,0xAF,0x5A,0xDA,0xAA,0x65,0xEA,0xA5,0x5F,0x55,0x7E,0xDA,
+	0xAA,0x6B,0xEA,0xAB,0xFE,0xBF,0xFA,0xDA,0xAA,0x6A,0xCC,0x04,0xAA,0xDA,0xAA,0xBF,0xCC,0x04,0xFF,0xEF,
+	0xFF
+};
+
+// Final unpack length: 528
+// Decoded length: 132 (first four bytes of buffer)
+const uint8_t fix128KChordPackedBMP[110] =
+{
+	0x00,0x00,0x00,0x84,0xCC,0x08,0x55,0x65,0x55,0x55,0x6A,0xCC,0x07,0xAA,0xB6,0xAA,0xAA,0x6A,0xCC,0x07,
+	0xAA,0xB6,0xAA,0xAA,0x6A,0xA9,0x6A,0xA5,0x55,0x96,0x96,0xAA,0xAA,0xB6,0xAA,0xAA,0x6A,0xA9,0x7A,0xA5,
+	0xFF,0xD5,0x97,0xAA,0xAA,0xB6,0xAA,0xAA,0x6A,0xA9,0x7A,0xA5,0x5A,0x95,0x57,0xAA,0xAA,0xB6,0xAA,0xAA,
+	0x6A,0xA9,0x7A,0xA5,0xFE,0x97,0x57,0x5A,0xAA,0xB6,0xAA,0xAA,0x6A,0xA9,0x55,0x65,0x55,0x97,0x97,0x5E,
+	0xAA,0xB6,0xAA,0xAA,0x6A,0xAA,0xFF,0xFB,0xFF,0xEF,0xAF,0xBE,0xAA,0xB6,0xAA,0xAA,0x6A,0xCC,0x07,0xAA,
+	0xB6,0xAA,0xAA,0xBF,0xCC,0x07,0xFF,0xFB,0xFF,0xFF
+};
+
 // Final unpack length: 336
 // Decoded length: 84 (first four bytes of buffer)
 const uint8_t editOpModeCharsPackedBMP[88] =
--- a/src/gfx/pt2_gfx_tracker.c
+++ b/src/gfx/pt2_gfx_tracker.c
@@ -1,5 +1,30 @@
 #include <stdint.h>
 
+// Final unpack length: 2108
+// Decoded length: 527 (first four bytes of buffer)
+const uint8_t tracker128KFixPackedBMP[363] =
+{
+	0x00,0x00,0x02,0x0F,0xCC,0x0C,0xAA,0xFF,0xFF,0xEA,0xCC,0x0B,0xAA,0xAD,0x55,0x55,0xCC,0x0C,0xAA,0xDA,
+	0xAA,0xAA,0xA9,0x6A,0xA5,0x55,0x96,0x96,0x95,0x59,0x55,0x65,0xA5,0xAA,0xAD,0xAA,0xAA,0xAA,0x97,0xAA,
+	0x5F,0xFD,0x59,0x75,0xFF,0xED,0x7F,0x5E,0x5E,0xAA,0xDA,0xAA,0xAA,0xA9,0x7A,0xA5,0x56,0x95,0x57,0x5D,
+	0x5A,0x97,0xA5,0x55,0xEA,0xAD,0xAA,0xAA,0xAA,0x97,0xAA,0x5F,0xF9,0x75,0x75,0xE5,0xE9,0x7A,0x5F,0x5E,
+	0xAA,0xDA,0xAA,0xAA,0xA9,0x55,0x65,0x55,0x97,0x97,0x95,0x5E,0x97,0xA5,0xE5,0xEA,0xAD,0xAA,0xAA,0xAA,
+	0xAF,0xFF,0xBF,0xFE,0xFA,0xFA,0xFF,0xEA,0xFA,0xBE,0xBE,0xAA,0xDA,0xCC,0x0D,0xAA,0xAD,0xCC,0x0E,0xAA,
+	0xDA,0xCC,0x0D,0xAA,0xAE,0xFF,0xFF,0xCC,0x0C,0xAA,0xD5,0x55,0x5A,0xCC,0x0B,0xAA,0xAD,0xAA,0xAA,0xAA,
+	0x95,0x5A,0x55,0x59,0x55,0xA5,0x55,0xA5,0x5A,0x55,0x5A,0xAA,0xDA,0xAA,0xAA,0xA9,0x7D,0x65,0xFF,0xD7,
+	0xD6,0x5F,0xFD,0x7D,0x6B,0x5F,0xEA,0xAD,0xAA,0xAA,0xAA,0x95,0x5F,0x55,0x69,0x55,0xF5,0x56,0x95,0x57,
+	0xA5,0xEA,0xAA,0xDA,0xAA,0xAA,0xA9,0x75,0xE5,0xFF,0x97,0xFE,0x5F,0xF9,0x7D,0x7A,0x5E,0xAA,0xAD,0xAA,
+	0xAA,0xAA,0x97,0x96,0x55,0x59,0x7A,0xA5,0x55,0x97,0x97,0xA5,0xEA,0xAA,0xDA,0xAA,0xAA,0xAA,0xFA,0xFB,
+	0xFF,0xEF,0xAA,0xBF,0xFE,0xFA,0xFA,0xBE,0xAA,0xAD,0xCC,0x0E,0xAA,0xDA,0xCC,0x0D,0xAA,0xAD,0xCC,0x0E,
+	0xAA,0xEF,0xFF,0xFA,0xCC,0x0B,0xAA,0xAD,0x55,0x55,0xCC,0x0C,0xAA,0xDA,0xAA,0xAA,0xA9,0x55,0xA5,0x55,
+	0x95,0x5A,0x5A,0xA9,0x55,0x65,0xA5,0xAA,0xAD,0xAA,0xAA,0xAA,0x97,0xD6,0x5F,0xFD,0x7D,0x65,0xEA,0x97,
+	0xFF,0x56,0x5E,0xAA,0xDA,0xAA,0xAA,0xA9,0x55,0xF5,0x56,0x95,0x5F,0x5E,0xA9,0x55,0xA5,0x55,0xEA,0xAD,
+	0xAA,0xAA,0xAA,0x97,0x5E,0x5F,0xF9,0x7F,0xE5,0xEA,0x97,0xFE,0x5D,0x5E,0xAA,0xDA,0xAA,0xAA,0xA9,0x79,
+	0x65,0x55,0x97,0xAA,0x55,0x59,0x55,0x65,0xE5,0xEA,0xAD,0xAA,0xAA,0xAA,0xAF,0xAF,0xBF,0xFE,0xFA,0xAB,
+	0xFF,0xEF,0xFF,0xBE,0xBE,0xAA,0xDA,0xCC,0x0D,0xAA,0xAD,0xCC,0x0E,0xAA,0xDA,0xAA,0xAF,0xCC,0x0B,0xFF,
+	0xFE,0xFF,0xFF
+};
+
 // Final unpack length: 81600
 // Decoded length: 20400 (first four bytes of buffer)
 const uint8_t trackerFramePackedBMP[8486] =
--- a/src/pt2_audio.c
+++ b/src/pt2_audio.c
@@ -34,6 +34,7 @@
 #include "pt2_rcfilter.h"
 #include "pt2_ledfilter.h"
 #include "pt2_downsample2x.h"
+#include "pt2_hpc.h"
 
 #define STEREO_NORM_FACTOR 0.5 /* cumulative mid/side normalization factor (1/sqrt(2))*(1/sqrt(2)) */
 
@@ -129,14 +130,13 @@
 
 	const double dAudioLatencySecs = audioBufferSize / (double)audioFreq;
 
-	dFrac = modf(dAudioLatencySecs * editor.dPerfFreq, &dInt);
+	dFrac = modf(dAudioLatencySecs * hpcFreq.dFreq, &dInt);
 
 	// integer part
-	audLatencyPerfValInt = (int32_t)dInt;
+	audLatencyPerfValInt = (uint32_t)dInt;
 
 	// fractional part (scaled to 0..2^32-1)
-	dFrac *= UINT32_MAX+1.0;
-	audLatencyPerfValFrac = (uint32_t)dFrac;
+	audLatencyPerfValFrac = (uint32_t)((dFrac * (UINT32_MAX+1.0)) + 0.5); // rounded
 }
 
 void setSyncTickTimeLen(uint32_t timeLen, uint32_t timeLenFrac)
@@ -181,7 +181,7 @@
 			const moduleSample_t *s = &song->samples[editor.currSample];
 
 			paulaSetData(i, ch->n_start + s->loopStart);
-			paulaSetLength(i, s->loopLength >> 1);
+			paulaSetLength(i, (uint16_t)(s->loopLength >> 1));
 		}
 	}
 
@@ -335,7 +335,7 @@
 	paulaVoice_t *v = &paula[ch];
 
 	if (src == NULL)
-		src = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample
+		src = &song->sampleData[config.reservedSampleOffset]; // 128K reserved sample
 
 	v->AUD_LC = src;
 
@@ -362,7 +362,7 @@
 	paulaVoice_t *v = &paula[ch];
 
 	if (v->AUD_LC == NULL)
-		v->AUD_LC = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample
+		v->AUD_LC = &song->sampleData[config.reservedSampleOffset]; // 128K reserved sample
 
 	/* This is not really accurate to what happens on Paula
 	** during DMA start, but it's good enough.
@@ -801,8 +801,8 @@
 		// render to sample (PAT2SMP)
 
 		int32_t samplesTodo = numSamples;
-		if (editor.pat2SmpPos+samplesTodo > MAX_SAMPLE_LEN)
-			samplesTodo = MAX_SAMPLE_LEN-editor.pat2SmpPos;
+		if (editor.pat2SmpPos+samplesTodo > config.maxSampleLength)
+			samplesTodo = config.maxSampleLength-editor.pat2SmpPos;
 
 		// mix channels (with 2x oversampling, PAT2SMP needs it)
 		mixChannels(samplesTodo*2);
@@ -821,7 +821,7 @@
 		}
 
 		editor.pat2SmpPos += samplesTodo;
-		if (editor.pat2SmpPos >= MAX_SAMPLE_LEN)
+		if (editor.pat2SmpPos >= config.maxSampleLength)
 		{
 			editor.smpRenderingDone = true;
 			updateWindowTitle(MOD_IS_MODIFIED);
@@ -1127,10 +1127,10 @@
 
 		// BPM -> Hz -> tick length for performance counter (syncing visuals to audio)
 		double dTimeInt;
-		double dTimeFrac = modf(editor.dPerfFreq / dHz, &dTimeInt);
+		double dTimeFrac = modf(hpcFreq.dFreq / dHz, &dTimeInt);
 		const int32_t timeInt = (int32_t)dTimeInt;
 	
-		dTimeFrac = floor((UINT32_MAX+1.0) * dTimeFrac); // fractional part (scaled to 0..2^32-1)
+		dTimeFrac = floor((dTimeFrac * (UINT32_MAX+1.0)) + 0.5); // fractional part (scaled to 0..2^32-1)
 
 		audio.tickLengthTable[bpm-32] = ((uint64_t)timeInt << 32) | (uint32_t)dTimeFrac;
 	}
--- a/src/pt2_bmp.c
+++ b/src/pt2_bmp.c
@@ -20,6 +20,11 @@
 uint32_t *yesNoDialogBMP   = NULL, *bigYesNoDialogBMP  = NULL, *sampleMonitorBMP   = NULL;
 uint32_t *samplingBoxBMP   = NULL;
 
+// fix-bitmaps for 128K sample mode
+uint32_t *fix128KTrackerBMP = NULL;
+uint32_t *fix128KPosBMP = NULL;
+uint32_t *fix128KChordBMP = NULL;
+
 void createBitmaps(void)
 {
 	uint8_t r8, g8, b8, r8_2, g8_2, b8_2;
@@ -161,6 +166,9 @@
 
 void freeBMPs(void)
 {
+	if (fix128KChordBMP != NULL) free(fix128KChordBMP);
+	if (fix128KPosBMP != NULL) free(fix128KPosBMP);
+	if (fix128KTrackerBMP != NULL) free(fix128KTrackerBMP);
 	if (trackerFrameBMP != NULL) free(trackerFrameBMP);
 	if (samplerScreenBMP != NULL) free(samplerScreenBMP);
 	if (samplerVolumeBMP != NULL) free(samplerVolumeBMP);
@@ -189,17 +197,19 @@
 	const uint8_t *packSrc;
 	uint8_t *tmpBuffer, *packDst, byteIn;
 	int16_t count;
-	uint32_t *dst, decodedLength, i;
+	uint32_t *dst;
+	
+	int32_t decodedLength, i;
 
 	// RLE decode
 	decodedLength = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
 
 	// 2-bit to 8-bit conversion
-	dst = (uint32_t *)malloc((decodedLength * 4) * sizeof (int32_t));
+	dst = (uint32_t *)malloc(((decodedLength * 4) * sizeof (int32_t)) + 8);
 	if (dst == NULL)
 		return NULL;
 
-	tmpBuffer = (uint8_t *)malloc(decodedLength + 512); // some margin is needed, the packer is buggy
+	tmpBuffer = (uint8_t *)malloc(decodedLength + 128); // some margin is needed, the packer is buggy
 	if (tmpBuffer == NULL)
 	{
 		free(dst);
@@ -206,10 +216,10 @@
 		return NULL;
 	}
 
-	packSrc = src + 4;
+	packSrc = src + 4; // skip "length" field
 	packDst = tmpBuffer;
 
-	i = packedLen - 4;
+	i = packedLen - 4; // subtract "length" field
 	while (i > 0)
 	{
 		byteIn = *packSrc++;
@@ -256,6 +266,9 @@
 
 bool unpackBMPs(void)
 {
+	fix128KChordBMP = unpackBMP(fix128KChordPackedBMP, sizeof (fix128KChordPackedBMP));
+	fix128KPosBMP = unpackBMP(fix128KPosPackedBMP, sizeof (fix128KPosPackedBMP));
+	fix128KTrackerBMP = unpackBMP(tracker128KFixPackedBMP, sizeof (tracker128KFixPackedBMP));
 	trackerFrameBMP = unpackBMP(trackerFramePackedBMP, sizeof (trackerFramePackedBMP));
 	samplerScreenBMP = unpackBMP(samplerScreenPackedBMP, sizeof (samplerScreenPackedBMP));
 	samplerVolumeBMP = unpackBMP(samplerVolumePackedBMP, sizeof (samplerVolumePackedBMP));
@@ -278,13 +291,14 @@
 	sampleMonitorBMP = unpackBMP(sampleMonitorPackedBMP, sizeof (sampleMonitorPackedBMP));
 	samplingBoxBMP = unpackBMP(samplingBoxPackedBMP, sizeof (samplingBoxPackedBMP));
 
-	if (trackerFrameBMP    == NULL || samplerScreenBMP   == NULL || samplerVolumeBMP  == NULL ||
-		clearDialogBMP     == NULL || diskOpScreenBMP    == NULL || mod2wavBMP        == NULL ||
-		posEdBMP           == NULL || spectrumVisualsBMP == NULL || yesNoDialogBMP    == NULL ||
-		editOpScreen1BMP   == NULL || editOpScreen2BMP   == NULL || editOpScreen3BMP  == NULL ||
-		editOpScreen4BMP   == NULL || aboutScreenBMP     == NULL || muteButtonsBMP    == NULL ||
-		editOpModeCharsBMP == NULL || samplerFiltersBMP  == NULL || yesNoDialogBMP    == NULL ||
-		bigYesNoDialogBMP  == NULL || sampleMonitorBMP   == NULL || samplingBoxBMP    == NULL)
+	if (fix128KTrackerBMP  == NULL || fix128KPosBMP      == NULL || fix128KChordBMP  == NULL || 
+		trackerFrameBMP    == NULL || samplerScreenBMP   == NULL || samplerVolumeBMP == NULL ||
+		clearDialogBMP     == NULL || diskOpScreenBMP    == NULL || mod2wavBMP       == NULL ||
+		posEdBMP           == NULL || spectrumVisualsBMP == NULL || yesNoDialogBMP   == NULL ||
+		editOpScreen1BMP   == NULL || editOpScreen2BMP   == NULL || editOpScreen3BMP == NULL ||
+		editOpScreen4BMP   == NULL || aboutScreenBMP     == NULL || muteButtonsBMP   == NULL ||
+		editOpModeCharsBMP == NULL || samplerFiltersBMP  == NULL || yesNoDialogBMP   == NULL ||
+		bigYesNoDialogBMP  == NULL || sampleMonitorBMP   == NULL || samplingBoxBMP   == NULL)
 	{
 		showErrorMsgBox("Out of memory!");
 		return false; // BMPs are free'd in cleanUp()
--- a/src/pt2_bmp.h
+++ b/src/pt2_bmp.h
@@ -21,6 +21,8 @@
 extern const uint8_t aboutScreenPackedBMP[1408];
 extern const uint8_t clearDialogPackedBMP[525];
 extern const uint8_t diskOpScreenPackedBMP[1898];
+extern const uint8_t fix128KChordPackedBMP[110];
+extern const uint8_t fix128KPosPackedBMP[81];
 extern const uint8_t editOpModeCharsPackedBMP[88];
 extern const uint8_t editOpScreen1PackedBMP[1481];
 extern const uint8_t editOpScreen2PackedBMP[1502];
@@ -34,6 +36,7 @@
 extern const uint8_t samplerFiltersPackedBMP[933];
 extern const uint8_t samplerScreenPackedBMP[3076];
 extern const uint8_t spectrumVisualsPackedBMP[2217];
+extern const uint8_t tracker128KFixPackedBMP[363];
 extern const uint8_t trackerFramePackedBMP[8486];
 extern const uint8_t yesNoDialogPackedBMP[476];
 extern const uint8_t bigYesNoDialogPackedBMP[472];
@@ -67,6 +70,11 @@
 extern uint32_t *pat2SmpDialogBMP;
 extern uint32_t *sampleMonitorBMP;
 extern uint32_t *samplingBoxBMP;
+
+// fix-bitmaps for 128K sample mode
+extern uint32_t *fix128KTrackerBMP;
+extern uint32_t *fix128KPosBMP;
+extern uint32_t *fix128KChordBMP;
 
 bool unpackBMPs(void);
 void createBitmaps(void);
--- a/src/pt2_chordmaker.c
+++ b/src/pt2_chordmaker.c
@@ -7,6 +7,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "pt2_header.h"
+#include "pt2_config.h"
 #include "pt2_mouse.h"
 #include "pt2_textout.h"
 #include "pt2_visuals.h"
@@ -111,10 +112,10 @@
 	sortNotes();
 	removeDuplicateNotes();
 
-	ui.updateNote1Text = true;
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote1Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	// setup some variables
 	smpLoopStart = s->loopStart;
@@ -151,7 +152,7 @@
 		editor.currSample = (int8_t)i;
 	}
 
-	double *dMixData = (double *)calloc(MAX_SAMPLE_LEN*2, sizeof (double));
+	double *dMixData = (double *)calloc(config.maxSampleLength*2, sizeof (double));
 	if (dMixData == NULL)
 	{
 		statusOutOfMemory();
@@ -158,7 +159,7 @@
 		return;
 	}
 
-	s->length = smpLoopFlag ? MAX_SAMPLE_LEN : editor.chordLength; // if sample loops, set max length
+	s->length = smpLoopFlag ? config.maxSampleLength : editor.chordLength; // if sample loops, set max length
 	s->loopLength = 2;
 	s->loopStart = 0;
 	s->text[21] = '!'; // chord sample indicator
@@ -189,7 +190,7 @@
 		if (!v->active || v->dDelta == 0.0)
 			continue;
 
-		for (int32_t j = 0; j < MAX_SAMPLE_LEN*2; j++)
+		for (int32_t j = 0; j < config.maxSampleLength*2; j++)
 		{
 			double dSmp = smpData[v->pos] * (1.0 / 128.0);
 
@@ -247,8 +248,8 @@
 	free(dMixData);
 
 	// clear unused sample data (if sample is not full already)
-	if (s->length < MAX_SAMPLE_LEN)
-		memset(&song->sampleData[s->offset + s->length], 0, MAX_SAMPLE_LEN - s->length);
+	if (s->length < config.maxSampleLength)
+		memset(&song->sampleData[s->offset + s->length], 0, config.maxSampleLength - s->length);
 
 	// we're done
 
@@ -294,14 +295,14 @@
 	else
 	{
 		len = (s->length * periodTable[(37 * s->fineTune) + note]) / periodTable[24];
-		if (len > MAX_SAMPLE_LEN)
-			len = MAX_SAMPLE_LEN;
+		if (len > config.maxSampleLength)
+			len = config.maxSampleLength;
 
-		editor.chordLength = len & 0xFFFE;
+		editor.chordLength = len & config.maxSampleLength;
 	}
 
 	if (ui.editOpScreenShown && ui.editOpScreen == 3)
-		ui.updateLengthText = true;
+		ui.updateChordLengthText = true;
 }
 
 void resetChord(void)
@@ -313,10 +314,10 @@
 
 	editor.chordLengthMin = false;
 
-	ui.updateNote1Text = true;
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote1Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -328,10 +329,10 @@
 	editor.note3 = editor.oldNote3;
 	editor.note4 = editor.oldNote4;
 
-	ui.updateNote1Text = true;
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote1Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -363,9 +364,9 @@
 
 	editor.note4 = 36;
 
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -388,9 +389,9 @@
 
 	editor.note4 = 36;
 
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -413,9 +414,9 @@
 
 	editor.note4 = 36;
 
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -438,9 +439,9 @@
 	if (editor.note3 >= 36) editor.note3 -= 12;
 	if (editor.note4 >= 36) editor.note4 -= 12;
 
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -463,9 +464,9 @@
 	if (editor.note3 >= 36) editor.note3 -= 12;
 	if (editor.note4 >= 36) editor.note4 -= 12;
 
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -488,9 +489,9 @@
 	if (editor.note3 >= 36) editor.note3 -= 12;
 	if (editor.note4 >= 36) editor.note4 -= 12;
 
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -513,9 +514,9 @@
 	if (editor.note3 >= 36) editor.note3 -= 12;
 	if (editor.note4 >= 36) editor.note4 -= 12;
 
-	ui.updateNote2Text = true;
-	ui.updateNote3Text = true;
-	ui.updateNote4Text = true;
+	ui.updateChordNote2Text = true;
+	ui.updateChordNote3Text = true;
+	ui.updateChordNote4Text = true;
 
 	recalcChordLength();
 }
@@ -533,7 +534,7 @@
 		pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
 	}
 
-	ui.updateNote1Text = true;
+	ui.updateChordNote1Text = true;
 }
 
 void selectChordNote2(void)
@@ -549,7 +550,7 @@
 		pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
 	}
 
-	ui.updateNote2Text = true;
+	ui.updateChordNote2Text = true;
 }
 
 void selectChordNote3(void)
@@ -565,7 +566,7 @@
 		pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
 	}
 
-	ui.updateNote3Text = true;
+	ui.updateChordNote3Text = true;
 }
 
 void selectChordNote4(void)
@@ -581,7 +582,7 @@
 		pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
 	}
 
-	ui.updateNote4Text = true;
+	ui.updateChordNote4Text = true;
 }
 
 void makeChord(void)
--- a/src/pt2_config.c
+++ b/src/pt2_config.c
@@ -68,6 +68,9 @@
 	config.integerScaling = true;
 	config.audioInputFrequency = 44100;
 
+	config.maxSampleLength = 65534;
+	config.reservedSampleOffset = (MOD_SAMPLES+1) * config.maxSampleLength;
+
 #ifndef _WIN32
 	getcwd(oldCwd, PATH_MAX);
 #endif
@@ -192,6 +195,21 @@
 		{
 			configLine = strtok(NULL, "\n");
 			continue;
+		}
+
+		// 64K_LIMIT
+		else if (!_strnicmp(configLine, "64K_LIMIT=", 10))
+		{
+			if (!_strnicmp(&configLine[10], "TRUE", 4))
+			{
+				config.maxSampleLength = 65534;
+				config.reservedSampleOffset = (MOD_SAMPLES+1) * config.maxSampleLength;
+			}
+			else if (!_strnicmp(&configLine[10], "FALSE", 5))
+			{
+				config.maxSampleLength = 131070;
+				config.reservedSampleOffset = (MOD_SAMPLES+1) * config.maxSampleLength;
+			}
 		}
 
 		// NO_DWNSMP_ON_SMP_LOAD (no dialog for 2x downsample after >22kHz sample load)
--- a/src/pt2_config.h
+++ b/src/pt2_config.h
@@ -19,7 +19,8 @@
 	int8_t stereoSeparation, videoScaleFactor, accidental;
 	uint8_t pixelFilter, filterModel;
 	uint16_t quantizeValue;
-	uint32_t soundFrequency, soundBufferSize, audioInputFrequency;
+	int32_t maxSampleLength;
+	uint32_t soundFrequency, soundBufferSize, audioInputFrequency, reservedSampleOffset;
 } config_t;
 
 extern config_t config; // pt2_config.c
--- a/src/pt2_edit.c
+++ b/src/pt2_edit.c
@@ -240,7 +240,10 @@
 
 				if (updateValue)
 				{
-					editor.samplePos = ui.tmpDisp16;
+					editor.samplePos = ui.tmpDisp32;
+					if (editor.samplePos > config.maxSampleLength)
+						editor.samplePos = config.maxSampleLength;
+
 					if (editor.samplePos > song->samples[editor.currSample].length)
 						editor.samplePos = song->samples[editor.currSample].length;
 
@@ -499,7 +502,9 @@
 
 				if (updateValue)
 				{
-					tmp32 = ui.tmpDisp16 & 0xFFFE; // even'ify
+					tmp32 = ui.tmpDisp32 & ~1; // even'ify
+					if (tmp32 > config.maxSampleLength)
+						tmp32 = config.maxSampleLength;
 
 					if (s->loopStart+s->loopLength > 2)
 					{
@@ -507,12 +512,12 @@
 							tmp32 = s->loopStart+s->loopLength;
 					}
 
-					tmp32 &= 0xFFFE;
+					tmp32 &= ~1;
 
 					if (s->length != tmp32)
 					{
 						turnOffVoices();
-						s->length = (uint16_t)tmp32;
+						s->length = tmp32;
 
 						ui.updateCurrSampleLength = true;
 						ui.updateSongSize = true;
@@ -534,7 +539,9 @@
 
 				if (updateValue)
 				{
-					tmp32 = ui.tmpDisp16 & 0xFFFE; // even'ify
+					tmp32 = ui.tmpDisp32 & ~1; // even'ify
+					if (tmp32 > config.maxSampleLength)
+						tmp32 = config.maxSampleLength;
 
 					if (s->length >= s->loopLength)
 					{
@@ -546,18 +553,18 @@
 						tmp32 = 0;
 					}
 
-					tmp32 &= 0xFFFE;
+					tmp32 &= ~1;
 
 					if (s->loopStart != tmp32)
 					{
 						turnOffVoices();
-						s->loopStart = (uint16_t)tmp32;
+						s->loopStart = tmp32;
 						mixerUpdateLoops();
 
 						ui.updateCurrSampleRepeat = true;
 
 						if (ui.editOpScreenShown && ui.editOpScreen == 3)
-							ui.updateLengthText = true;
+							ui.updateChordLengthText = true;
 
 						if (ui.samplerScreenShown)
 							setLoopSprites();
@@ -574,7 +581,9 @@
 
 				if (updateValue)
 				{
-					tmp32 = ui.tmpDisp16 & 0xFFFE; // even'ify
+					tmp32 = ui.tmpDisp32 & ~1; // even'ify
+					if (tmp32 > config.maxSampleLength)
+						tmp32 = config.maxSampleLength;
 
 					if (s->length >= s->loopStart)
 					{
@@ -586,7 +595,7 @@
 						tmp32 = 2;
 					}
 
-					tmp32 &= 0xFFFE;
+					tmp32 &= ~1;
 
 					if (tmp32 < 2)
 						tmp32 = 2;
@@ -594,12 +603,12 @@
 					if (s->loopLength != tmp32)
 					{
 						turnOffVoices();
-						s->loopLength = (uint16_t)tmp32;
+						s->loopLength = tmp32;
 						mixerUpdateLoops();
 
 						ui.updateCurrSampleReplen = true;
 						if (ui.editOpScreenShown && ui.editOpScreen == 3)
-							ui.updateLengthText = true;
+							ui.updateChordLengthText = true;
 
 						if (ui.samplerScreenShown)
 							setLoopSprites();
@@ -923,7 +932,7 @@
 
 	const int8_t *n_start = &song->sampleData[s->offset];
 	const int8_t vol = 64;
-	const uint16_t n_length = s->length >> 1;
+	const uint16_t n_length = (uint16_t)(s->length >> 1);
 	const uint16_t period = periodTable[((s->fineTune & 0xF) * 37) + noteVal];
 
 	paulaSetVolume(ch, vol);
@@ -980,9 +989,9 @@
 			chn->n_volume = s->volume;
 			chn->n_period = tempPeriod;
 			chn->n_start = &song->sampleData[s->offset];
-			chn->n_length = (s->loopStart > 0) ? (s->loopStart + s->loopLength) >> 1 : s->length >> 1;
+			chn->n_length = (uint16_t)((s->loopStart > 0) ? (s->loopStart + s->loopLength) >> 1 : s->length >> 1);
 			chn->n_loopstart = &song->sampleData[s->offset + s->loopStart];
-			chn->n_replen = s->loopLength >> 1;
+			chn->n_replen = (uint16_t)(s->loopLength >> 1);
 
 			if (chn->n_length == 0)
 				chn->n_length = 1;
@@ -1145,7 +1154,7 @@
 		smpTo->loopLengthDisp = &smpTo->loopLength;
 
 		// copy sample data
-		memcpy(&song->sampleData[smpTo->offset], &song->sampleData[smpFrom->offset], MAX_SAMPLE_LEN);
+		memcpy(&song->sampleData[smpTo->offset], &song->sampleData[smpFrom->offset], config.maxSampleLength);
 
 		updateCurrSample();
 		ui.updateSongSize = true;
@@ -1187,7 +1196,8 @@
 void exchSampleTrack(void)
 {
 	int8_t smp;
-	uint32_t i, tmpOffset;
+	int32_t i;
+	uint32_t tmpOffset;
 	moduleSample_t *smpFrom, *smpTo, smpTmp;
 	note_t *noteSrc;
 
@@ -1228,7 +1238,7 @@
 		smpTo->loopLengthDisp = &smpTo->loopLength;
 
 		// swap sample data
-		for (i = 0; i < MAX_SAMPLE_LEN; i++)
+		for (i = 0; i < config.maxSampleLength; i++)
 		{
 			smp = song->sampleData[smpFrom->offset+i];
 			song->sampleData[smpFrom->offset+i] = song->sampleData[smpTo->offset+i];
--- a/src/pt2_header.h
+++ b/src/pt2_header.h
@@ -14,7 +14,7 @@
 #include "pt2_unicode.h"
 #include "pt2_palette.h"
 
-#define PROG_VER_STR "1.37"
+#define PROG_VER_STR "1.38"
 
 #ifdef _WIN32
 #define DIR_DELIMITER '\\'
@@ -38,7 +38,7 @@
 
 /* Scopes are clocked at 64Hz instead of 60Hz to prevent the small +/- Hz 
 ** interference from monitors not being exactly 60Hz (and unstable non-vsync mode).
-** Sadly, the scopes might midly flicker from this in some cases.
+** Sadly, the scopes might mildly flicker from this in some cases.
 */
 #define SCOPE_HZ 64
 
@@ -49,11 +49,6 @@
 #define MOD_SAMPLES 31
 #define MOD_ORDERS 128
 #define MAX_PATTERNS 100
-
-#define MAX_SAMPLE_LEN 65534
-
-// for NULL pointers
-#define RESERVED_SAMPLE_OFFSET ((31+1) * MAX_SAMPLE_LEN)
 
 #define AMIGA_VOICES 4
 #define SCOPE_WIDTH 40
--- a/src/pt2_helpers.c
+++ b/src/pt2_helpers.c
@@ -21,44 +21,6 @@
 #include "pt2_structs.h"
 #include "pt2_config.h"
 
-// used for Windows usleep() implementation
-#ifdef _WIN32
-static NTSTATUS (__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval);
-#endif
-
-// usleep() implementation for Windows
-#ifdef _WIN32
-void usleep(uint32_t usec)
-{
-	LARGE_INTEGER lpDueTime;
-
-	if (NtDelayExecution == NULL)
-	{
-		// NtDelayExecution() is not available (shouldn't happen), use regular sleep()
-		Sleep(usec / 1000);
-	}
-	else
-	{
-		// this prevents a 64-bit MUL (will not overflow with typical values anyway)
-		lpDueTime.HighPart = 0xFFFFFFFF;
-		lpDueTime.LowPart = (DWORD)(-10 * (int32_t)usec);
-
-		NtDelayExecution(false, &lpDueTime);
-	}
-}
-
-void setupWin32Usleep(void)
-{
-	NtDelayExecution = (NTSTATUS (__stdcall *)(BOOL, PLARGE_INTEGER))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");
-	timeBeginPeriod(0); // enter highest timer resolution
-}
-
-void freeWin32Usleep(void)
-{
-	timeEndPeriod(0); // exit highest timer resolution
-}
-#endif
-
 void showErrorMsgBox(const char *fmt, ...)
 {
 	char strBuf[1024];
--- a/src/pt2_helpers.h
+++ b/src/pt2_helpers.h
@@ -53,13 +53,6 @@
 
 void showErrorMsgBox(const char *fmt, ...);
 
-#ifdef _WIN32
-// Windows usleep() implementation
-void usleep(uint32_t usec);
-void setupWin32Usleep(void);
-void freeWin32Usleep(void);
-#endif
-
 void sanitizeFilenameChar(char *chr);
 bool sampleNameIsEmpty(char *name);
 bool moduleNameIsEmpty(char *name);
--- /dev/null
+++ b/src/pt2_hpc.c
@@ -1,0 +1,121 @@
+/*
+** High Performance Counter delay routines
+*/
+
+#ifdef _WIN32
+#define WIN32_MEAN_AND_LEAN
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#include <SDL2/SDL.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "pt2_hpc.h"
+
+hpcFreq_t hpcFreq;
+
+#ifdef _WIN32 // Windows usleep() implementation
+
+static NTSTATUS (__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval);
+static NTSTATUS (__stdcall *NtQueryTimerResolution)(PULONG MinimumResolution, PULONG MaximumResolution, PULONG ActualResolution);
+static NTSTATUS (__stdcall *NtSetTimerResolution)(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
+
+static void (*usleep)(int32_t usec);
+
+static void usleepGood(int32_t usec)
+{
+	LARGE_INTEGER delayInterval;
+
+	// NtDelayExecution() delays in 100ns-units, and negative value = delay from current time
+	usec *= -10;
+
+	delayInterval.HighPart = 0xFFFFFFFF;
+	delayInterval.LowPart = usec;
+	NtDelayExecution(false, &delayInterval);
+}
+
+static void usleepWeak(int32_t usec) // fallback if no NtDelayExecution()
+{
+	Sleep((usec + 500) / 1000);
+}
+
+static void windowsSetupUsleep(void)
+{
+	NtDelayExecution = (NTSTATUS (__stdcall *)(BOOL, PLARGE_INTEGER))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");
+	NtQueryTimerResolution = (NTSTATUS (__stdcall *)(PULONG, PULONG, PULONG))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryTimerResolution");
+	NtSetTimerResolution = (NTSTATUS (__stdcall *)(ULONG, BOOLEAN, PULONG))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtSetTimerResolution");
+
+	usleep = (NtDelayExecution != NULL) ? usleepGood : usleepWeak;
+}
+#endif
+
+void hpc_Init(void)
+{
+#ifdef _WIN32
+	windowsSetupUsleep();
+#endif
+	hpcFreq.freq64 = SDL_GetPerformanceFrequency();
+	hpcFreq.dFreq = (double)hpcFreq.freq64;
+	hpcFreq.dFreqMulMicro = (1000.0 * 1000.0) / hpcFreq.dFreq;
+}
+
+void hpc_SetDurationInHz(hpc_t *hpc, const double dHz)
+{
+	const double dDuration = hpcFreq.dFreq / dHz;
+
+	// break down duration into integer and frac parts
+	double dDurationInt;
+	double dDurationFrac = modf(dDuration, &dDurationInt);
+
+	// set 64:32 values
+	hpc->duration64Int = (uint64_t)floor(dDurationInt);
+	hpc->duration64Frac = (uint64_t)((dDurationFrac * (UINT32_MAX+1.0)) + 0.5); // rounded
+}
+
+void hpc_ResetEndTime(hpc_t *hpc)
+{
+	hpc->endTime64Int = SDL_GetPerformanceCounter() + hpc->duration64Int;
+	hpc->endTime64Frac = hpc->duration64Frac;
+}
+
+void hpc_Wait(hpc_t *hpc)
+{
+#ifdef _WIN32 // set resolution to 0.5ms (safest minium) - this is confirmed to improve NtDelayExecution() and Sleep()
+	ULONG originalTimerResolution, minRes, maxRes, curRes;
+
+	if (NtQueryTimerResolution != NULL && NtSetTimerResolution != NULL)
+	{
+		if (!NtQueryTimerResolution(&minRes, &maxRes, &originalTimerResolution))
+		{
+			if (originalTimerResolution != 5000 && maxRes <= 5000)
+				NtSetTimerResolution(5000, TRUE, &curRes); // set to 0.5ms (safest minimum)
+		}
+	}
+#endif
+
+	const uint64_t currTime64 = SDL_GetPerformanceCounter();
+	if (currTime64 < hpc->endTime64Int)
+	{
+		uint64_t timeLeft64 = hpc->endTime64Int - currTime64;
+		if (timeLeft64 > INT32_MAX)
+			timeLeft64 = INT32_MAX;
+
+		const int32_t timeLeft32 = (int32_t)timeLeft64;
+
+		int32_t microSecsLeft = (int32_t)((timeLeft32 * hpcFreq.dFreqMulMicro) + 0.5); // rounded
+		if (microSecsLeft > 0)
+			usleep(microSecsLeft);
+	}
+
+	// set next end time
+
+	hpc->endTime64Int += hpc->duration64Int;
+
+	hpc->endTime64Frac += hpc->duration64Frac;
+	if (hpc->endTime64Frac > UINT32_MAX)
+	{
+		hpc->endTime64Frac &= UINT32_MAX;
+		hpc->endTime64Int++;
+	}
+}
--- /dev/null
+++ b/src/pt2_hpc.h
@@ -1,0 +1,23 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct
+{
+	uint64_t freq64;
+	double dFreq, dFreqMulMicro;
+} hpcFreq_t;
+
+typedef struct
+{
+	uint64_t duration64Int, duration64Frac;
+	uint64_t endTime64Int, endTime64Frac;
+} hpc_t;
+
+extern hpcFreq_t hpcFreq;
+
+void hpc_Init(void);
+void hpc_SetDurationInHz(hpc_t *hpc, double dHz);
+void hpc_ResetEndTime(hpc_t *hpc);
+void hpc_Wait(hpc_t *hpc);
--- a/src/pt2_keyboard.c
+++ b/src/pt2_keyboard.c
@@ -3586,22 +3586,22 @@
 			if (ui.changingChordNote == 1)
 			{
 				editor.note1 = rawKey;
-				ui.updateNote1Text = true;
+				ui.updateChordNote1Text = true;
 			}
 			else if (ui.changingChordNote == 2)
 			{
 				editor.note2 = rawKey;
-				ui.updateNote2Text = true;
+				ui.updateChordNote2Text = true;
 			}
 			else if (ui.changingChordNote == 3)
 			{
 				editor.note3 = rawKey;
-				ui.updateNote3Text = true;
+				ui.updateChordNote3Text = true;
 			}
 			else if (ui.changingChordNote == 4)
 			{
 				editor.note4 = rawKey;
-				ui.updateNote4Text = true;
+				ui.updateChordNote4Text = true;
 			}
 
 			ui.changingChordNote = false;
@@ -4094,15 +4094,28 @@
 				else if (textChar <= 'f')
 					textChar -= 'a'-10;
 
-				if (ui.numBits == 16)
+				if (ui.numBits == 17)
 				{
-					*ui.numPtr16 &= ~(0xF000 >> (ui.dstPos << 2));
-					*ui.numPtr16 |= (textChar << (12 - (ui.dstPos << 2)));
+					*ui.numPtr32 &= ~(0xF0000 >> (ui.dstPos << 2));
+					*ui.numPtr32 |= textChar << (16 - (ui.dstPos << 2));
 				}
+				else if (ui.numBits == 16)
+				{
+					if (ui.force32BitNumPtr)
+					{
+						*ui.numPtr32 &= ~(0xF000 >> (ui.dstPos << 2));
+						*ui.numPtr32 |= textChar << (12 - (ui.dstPos << 2));
+					}
+					else
+					{
+						*ui.numPtr16 &= ~(0xF000 >> (ui.dstPos << 2));
+						*ui.numPtr16 |= textChar << (12 - (ui.dstPos << 2));
+					}
+				}
 				else if (ui.numBits == 8)
 				{
 					*ui.numPtr8 &= ~(0xF0 >> (ui.dstPos << 2));
-					*ui.numPtr8 |= (textChar << (4 - (ui.dstPos << 2)));
+					*ui.numPtr8 |= textChar << (4 - (ui.dstPos << 2));
 				}
 
 				textMarkerMoveRight();
--- a/src/pt2_main.c
+++ b/src/pt2_main.c
@@ -36,6 +36,7 @@
 #include "pt2_bmp.h"
 #include "pt2_sync.h"
 #include "pt2_sampling.h"
+#include "pt2_hpc.h"
 
 #define CRASH_TEXT "Oh no!\nThe ProTracker 2 clone has crashed...\n\nA backup .mod was hopefully " \
                    "saved to the current module directory.\n\nPlease report this bug if you can.\n" \
@@ -181,7 +182,6 @@
 		return 0;
 	}
 
-	setupWin32Usleep();
 	disableWasapi(); // disable problematic WASAPI SDL2 audio driver on Windows (causes clicks/pops sometimes...)
 	                 // 13.03.2020: This is still needed with SDL 2.0.12...
 #endif
@@ -233,6 +233,9 @@
 	makeSureDirIsProgramDir();
 #endif
 
+	hpc_Init();
+	hpc_SetDurationInHz(&video.vblankHpc, VBLANK_HZ);
+
 	if (!initializeVars() || !initKaiserTable())
 	{
 		cleanUp();
@@ -261,8 +264,6 @@
 	SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
 #endif
 
-	setupPerfFreq();
-
 	if (!setupAudio() || !unpackBMPs())
 	{
 		cleanUp();
@@ -329,7 +330,7 @@
 
 	SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
 
-	setupWaitVBL();
+	hpc_ResetEndTime(&video.vblankHpc);
 	while (editor.programRunning)
 	{
 		sinkVisualizerBars();
@@ -370,20 +371,11 @@
 			else if (event.window.event == SDL_WINDOWEVENT_SHOWN)
 				video.windowHidden = false;
 
-			if (video.vsync60HzPresent)
-			{
-				/* if we minimize the window and vsync is present, vsync is temporarily turned off.
-				** recalc waitVBL() vars so that it can sleep properly in said mode.
-				*/
-				if (event.window.event == SDL_WINDOWEVENT_MINIMIZED ||
-					event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
-				{
-					setupWaitVBL();
-				}
-			}
+			// reset vblank end time if we minimize window
+			if (event.window.event == SDL_WINDOWEVENT_MINIMIZED || event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
+				hpc_ResetEndTime(&video.vblankHpc);
 		}
 
-
 #ifdef _WIN32
 		handleSysMsg(event);
 #endif
@@ -485,13 +477,10 @@
 
 	config.defModulesDir = (char *)calloc(PATH_MAX + 1, sizeof (char));
 	config.defSamplesDir = (char *)calloc(PATH_MAX + 1, sizeof (char));
-	editor.tempSample = (int8_t *)calloc(MAX_SAMPLE_LEN, 1);
+	editor.tempSample = (int8_t *)calloc(131070, 1);
 
-	if (config.defModulesDir == NULL || config.defSamplesDir == NULL ||
-		editor.tempSample == NULL)
-	{
+	if (config.defModulesDir == NULL || config.defSamplesDir == NULL || editor.tempSample == NULL)
 		goto oom;
-	}
 
 	turnOffVoices();
 
@@ -906,7 +895,6 @@
 	if (editor.tempSample != NULL) free(editor.tempSample);
 
 #ifdef _WIN32
-	freeWin32Usleep();
 #ifndef _DEBUG
 	UnhookWindowsHookEx(g_hKeyboardHook);
 #endif
--- a/src/pt2_module_loader.c
+++ b/src/pt2_module_loader.c
@@ -17,6 +17,7 @@
 #include <sys/stat.h>
 #include "pt2_mouse.h"
 #include "pt2_header.h"
+#include "pt2_config.h"
 #include "pt2_sampler.h"
 #include "pt2_textout.h"
 #include "pt2_audio.h"
@@ -312,7 +313,7 @@
 		}
 
 		realSampleLengths[i] = ((mgetc(m) << 8) | mgetc(m)) * 2;
-		s->length = (realSampleLengths[i] > MAX_SAMPLE_LEN) ? MAX_SAMPLE_LEN : (uint16_t)realSampleLengths[i];
+		s->length = (realSampleLengths[i] > config.maxSampleLength) ? config.maxSampleLength : realSampleLengths[i];
 
 		/* Only late versions of Ultimate SoundTracker could have samples larger than 9999 bytes.
 		** If found, we know for sure that this is a late STK module.
@@ -338,8 +339,8 @@
 		if (loopLength < 2)
 			loopLength = 2; // fixes empty samples in .MODs saved from FT2
 
-		// we don't support samples bigger than 65534 bytes, disable uncompatible loops
-		if (loopStart > MAX_SAMPLE_LEN || loopStart+loopLength > MAX_SAMPLE_LEN)
+		// we don't support samples bigger than 65534 (or 128kB) bytes, disable uncompatible loops
+		if (loopStart > config.maxSampleLength || loopStart+loopLength > config.maxSampleLength)
 		{
 			s->loopStart = 0;
 			s->loopLength = 2;
@@ -346,8 +347,8 @@
 		}
 		else
 		{
-			s->loopStart = (uint16_t)loopStart;
-			s->loopLength = (uint16_t)loopLength;
+			s->loopStart = loopStart;
+			s->loopLength = loopLength;
 		}
 
 		// in The Ultimate SoundTracker, sample loop start is in bytes, not words
@@ -596,7 +597,7 @@
 
 	// set sample data offsets (sample data = one huge buffer to rule them all)
 	for (i = 0; i < MOD_SAMPLES; i++)
-		newMod->samples[i].offset = MAX_SAMPLE_LEN * i;
+		newMod->samples[i].offset = config.maxSampleLength * i;
 
 	// load sample data
 	numSamples = (modFormat == FORMAT_STK) ? 15 : 31;
@@ -617,11 +618,11 @@
 		** so skip overflown data in .MOD file if present.
 		*/
 		int32_t bytesToSkip = 0;
-		if (realSampleLengths[i] > MAX_SAMPLE_LEN)
-			bytesToSkip = realSampleLengths[i] - MAX_SAMPLE_LEN;
+		if (realSampleLengths[i] > config.maxSampleLength)
+			bytesToSkip = realSampleLengths[i] - config.maxSampleLength;
 
 		// For Ultimate SoundTracker modules, don't load sample data after loop end
-		uint16_t loopEnd = s->loopStart + s->loopLength;
+		int32_t loopEnd = s->loopStart + s->loopLength;
 		if (modFormat == FORMAT_STK && loopEnd > 2 && s->length > loopEnd)
 		{
 			bytesToSkip += s->length-loopEnd;
@@ -643,9 +644,9 @@
 		if (s->length > 0 && s->loopLength > 2 && s->loopStart+s->loopLength > s->length)
 		{
 			loopOverflowVal = (s->loopStart + s->loopLength) - s->length;
-			if (s->length+loopOverflowVal <= MAX_SAMPLE_LEN)
+			if (s->length+loopOverflowVal <= config.maxSampleLength)
 			{
-				s->length = (uint16_t)(s->length + loopOverflowVal); // this is safe, we're allocating 65534 bytes per sample slot
+				s->length += loopOverflowVal; // this is safe, we're allocating 65534 bytes per sample slot
 			}
 			else
 			{
@@ -1205,7 +1206,7 @@
 	moduleSample_t *s = newMod->samples;
 	for (i = 0; i < MOD_SAMPLES; i++, s++)
 	{
-		s->offset = MAX_SAMPLE_LEN * i;
+		s->offset = config.maxSampleLength * i;
 		s->loopLength = 2;
 
 		// setup GUI text pointers
--- a/src/pt2_module_saver.c
+++ b/src/pt2_module_saver.c
@@ -42,8 +42,8 @@
 		fputc(s->fineTune & 0xF, f);
 		fputc(((uint8_t)s->volume > 64) ? 64 : s->volume, f);
 
-		uint16_t loopStart = s->loopStart;
-		uint16_t loopLength = s->loopLength;
+		int32_t loopStart = s->loopStart;
+		int32_t loopLength = s->loopLength;
 
 		if (loopLength < 2)
 			loopLength = 2;
@@ -54,11 +54,11 @@
 			loopLength = 2;
 		}
 
-		loopLength = SWAP16(loopLength >> 1);
-		loopStart = SWAP16(loopStart >> 1);
+		uint16_t loopStart16 = SWAP16(loopStart >> 1);
+		uint16_t loopLength16 = SWAP16(loopLength >> 1);
 
-		fwrite(&loopStart, sizeof (int16_t), 1, f);
-		fwrite(&loopLength, sizeof (int16_t), 1, f);
+		fwrite(&loopStart16, sizeof (int16_t), 1, f);
+		fwrite(&loopLength16, sizeof (int16_t), 1, f);
 	}
 
 	fputc((uint8_t)song->header.numOrders, f);
--- a/src/pt2_mouse.c
+++ b/src/pt2_mouse.c
@@ -457,7 +457,7 @@
 	if (editor.note1 > 36)
 		editor.note1 = 36;
 
-	ui.updateNote1Text = true;
+	ui.updateChordNote1Text = true;
 	recalcChordLength();
 }
 
@@ -471,7 +471,7 @@
 	if (editor.note1 < 0)
 		editor.note1 = 0;
 
-	ui.updateNote1Text = true;
+	ui.updateChordNote1Text = true;
 	recalcChordLength();
 }
 
@@ -485,7 +485,7 @@
 	if (editor.note2 > 36)
 		editor.note2 = 36;
 
-	ui.updateNote2Text = true;
+	ui.updateChordNote2Text = true;
 	recalcChordLength();
 }
 
@@ -499,7 +499,7 @@
 	if (editor.note2 < 0)
 		editor.note2 = 0;
 
-	ui.updateNote2Text = true;
+	ui.updateChordNote2Text = true;
 	recalcChordLength();
 }
 
@@ -513,7 +513,7 @@
 	if (editor.note3 > 36)
 		editor.note3 = 36;
 
-	ui.updateNote3Text = true;
+	ui.updateChordNote3Text = true;
 	recalcChordLength();
 }
 
@@ -527,7 +527,7 @@
 	if (editor.note3 < 0)
 		editor.note3 = 0;
 
-	ui.updateNote3Text = true;
+	ui.updateChordNote3Text = true;
 	recalcChordLength();
 }
 
@@ -541,7 +541,7 @@
 	if (editor.note4 > 36)
 		editor.note4 = 36;
 
-	ui.updateNote4Text = true;
+	ui.updateChordNote4Text = true;
 	recalcChordLength();
 }
 
@@ -555,7 +555,7 @@
 	if (editor.note4 < 0)
 		editor.note4 = 0;
 
-	ui.updateNote4Text = true;
+	ui.updateChordNote4Text = true;
 	recalcChordLength();
 }
 
@@ -565,17 +565,17 @@
 	{
 		if (fast)
 		{
-			if (editor.samplePos <= 0xFFFF-64)
+			if (editor.samplePos <= config.maxSampleLength-64)
 				editor.samplePos += 64;
 			else
-				editor.samplePos = 0xFFFF;
+				editor.samplePos = config.maxSampleLength;
 		}
 		else
 		{
-			if (editor.samplePos <= 0xFFFF-16)
+			if (editor.samplePos <= config.maxSampleLength-16)
 				editor.samplePos += 16;
 			else
-				editor.samplePos = 0xFFFF;
+				editor.samplePos = config.maxSampleLength;
 		}
 	}
 	else
@@ -582,17 +582,17 @@
 	{
 		if (fast)
 		{
-			if (editor.samplePos <= 0xFFFF-64)
+			if (editor.samplePos <= config.maxSampleLength-64)
 				editor.samplePos += 64;
 			else
-				editor.samplePos = 0xFFFF;
+				editor.samplePos = config.maxSampleLength;
 		}
 		else
 		{
-			if (editor.samplePos < 0xFFFF)
+			if (editor.samplePos < config.maxSampleLength)
 				editor.samplePos++;
 			else
-				editor.samplePos = 0xFFFF;
+				editor.samplePos = config.maxSampleLength;
 		}
 	}
 
@@ -794,7 +794,7 @@
 {
 	int32_t val;
 
-	if (song->samples[editor.currSample].length == MAX_SAMPLE_LEN)
+	if (song->samples[editor.currSample].length == config.maxSampleLength)
 		return;
 
 	val = song->samples[editor.currSample].length;
@@ -813,10 +813,10 @@
 			val += 2;
 	}
 
-	if (val > MAX_SAMPLE_LEN)
-		val = MAX_SAMPLE_LEN;
+	if (val > config.maxSampleLength)
+		val = config.maxSampleLength;
 
-	song->samples[editor.currSample].length = (uint16_t)val;
+	song->samples[editor.currSample].length = val;
 	ui.updateCurrSampleLength = true;
 }
 
@@ -862,7 +862,7 @@
 			val = s->loopStart+s->loopLength;
 	}
 
-	s->length = (uint16_t)val;
+	s->length = val;
 
 	ui.updateCurrSampleLength = true;
 }
@@ -899,7 +899,7 @@
 	if (val > len-loopLen)
 		val = len-loopLen;
 
-	song->samples[editor.currSample].loopStart = (uint16_t)val;
+	song->samples[editor.currSample].loopStart = val;
 	ui.updateCurrSampleRepeat = true;
 
 	mixerUpdateLoops();
@@ -908,7 +908,7 @@
 		setLoopSprites();
 
 	if (ui.editOpScreenShown && ui.editOpScreen == 3) // sample chord editor
-		ui.updateLengthText = true;
+		ui.updateChordLengthText = true;
 }
 
 void sampleRepeatDownButton(bool fast)
@@ -942,7 +942,7 @@
 	if (val < 0)
 		val = 0;
 
-	song->samples[editor.currSample].loopStart = (uint16_t)val;
+	song->samples[editor.currSample].loopStart = val;
 	ui.updateCurrSampleRepeat = true;
 
 	mixerUpdateLoops();
@@ -951,7 +951,7 @@
 		setLoopSprites();
 
 	if (ui.editOpScreenShown && ui.editOpScreen == 3) // sample chord editor
-		ui.updateLengthText = true;
+		ui.updateChordLengthText = true;
 }
 
 void sampleRepeatLengthUpButton(bool fast)
@@ -986,7 +986,7 @@
 	if (val > len-loopStart)
 		val = len-loopStart;
 
-	song->samples[editor.currSample].loopLength = (uint16_t)val;
+	song->samples[editor.currSample].loopLength = val;
 	ui.updateCurrSampleReplen = true;
 
 	mixerUpdateLoops();
@@ -995,7 +995,7 @@
 		setLoopSprites();
 
 	if (ui.editOpScreenShown && ui.editOpScreen == 3) // sample chord editor
-		ui.updateLengthText = true;
+		ui.updateChordLengthText = true;
 }
 
 void sampleRepeatLengthDownButton(bool fast)
@@ -1029,7 +1029,7 @@
 	if (val < 2)
 		val = 2;
 
-	song->samples[editor.currSample].loopLength = (uint16_t)val;
+	song->samples[editor.currSample].loopLength = val;
 	ui.updateCurrSampleReplen = true;
 
 	mixerUpdateLoops();
@@ -1038,7 +1038,7 @@
 		setLoopSprites();
 
 	if (ui.editOpScreenShown && ui.editOpScreen == 3) // sample chord editor
-		ui.updateLengthText = true;
+		ui.updateChordLengthText = true;
 }
 
 void tempoUpButton(void)
@@ -1721,7 +1721,7 @@
 		}
 		else
 		{
-			memcpy(&song->sampleData[s->offset], editor.tempSample, MAX_SAMPLE_LEN);
+			memcpy(&song->sampleData[s->offset], editor.tempSample, config.maxSampleLength);
 			redrawSample();
 			updateWindowTitle(MOD_IS_MODIFIED);
 			renderSamplerFiltersBox();
@@ -2270,10 +2270,10 @@
 			ui.changingDrumPadNote = false;
 
 			ui.updateResampleNote = true;
-			ui.updateNote1Text = true;
-			ui.updateNote2Text = true;
-			ui.updateNote3Text = true;
-			ui.updateNote4Text = true;
+			ui.updateChordNote1Text = true;
+			ui.updateChordNote2Text = true;
+			ui.updateChordNote3Text = true;
+			ui.updateChordNote4Text = true;
 
 			setPrevStatusMessage();
 			pointerSetPreviousMode();
@@ -2402,6 +2402,8 @@
 	int32_t smp32, j, modPos, oldVal, tmp32;
 	double dSmp;
 	moduleSample_t *s;
+	
+	ui.force32BitNumPtr = false;
 
 	switch (button)
 	{
@@ -2656,7 +2658,7 @@
 					break;
 				}
 
-				ptr8_1 = (int8_t *)malloc(MAX_SAMPLE_LEN);
+				ptr8_1 = (int8_t *)malloc(config.maxSampleLength);
 				if (ptr8_1 == NULL)
 				{
 					statusOutOfMemory();
@@ -2663,7 +2665,7 @@
 					return true;
 				}
 
-				memcpy(ptr8_1, &song->sampleData[s->offset], MAX_SAMPLE_LEN);
+				memcpy(ptr8_1, &song->sampleData[s->offset], config.maxSampleLength);
 
 				ptr8_2 = &song->sampleData[s->offset+editor.samplePos];
 				ptr8_3 = &song->sampleData[s->offset+s->length-1];
@@ -2791,6 +2793,9 @@
 
 		case PTB_EO_POS_NUM:
 		{
+			if (config.maxSampleLength == 65534 && mouse.x < 244) // yuck!
+				break;
+
 			if (mouse.rightButtonPressed)
 			{
 				editor.samplePos = 0;
@@ -2798,12 +2803,25 @@
 			}
 			else
 			{
-				ui.tmpDisp16 = editor.samplePos;
-				editor.samplePosDisp = &ui.tmpDisp16;
-				ui.numPtr16 = &ui.tmpDisp16;
-				ui.numLen = 4;
-				ui.numBits = 16;
-				ui.editTextPos = 2391; // (y * 40) + x
+				ui.force32BitNumPtr = true;
+
+				ui.tmpDisp32 = editor.samplePos;
+				editor.samplePosDisp = &ui.tmpDisp32;
+				ui.numPtr32 = &ui.tmpDisp32;
+
+				if (config.maxSampleLength == 65534)
+				{
+					ui.numLen = 4;
+					ui.numBits = 16;
+					ui.editTextPos = 2391; // (y * 40) + x
+				}
+				else
+				{
+					ui.numLen = 5;
+					ui.numBits = 17;
+					ui.editTextPos = 2390; // (y * 40) + x
+				}
+
 				getNumLine(TEXT_EDIT_HEX, PTB_EO_POS_NUM);
 			}
 		}
@@ -2891,7 +2909,7 @@
 
 			ptr8_1 = &song->sampleData[s->offset];
 
-			ptr8_3 = (int8_t *)malloc(MAX_SAMPLE_LEN);
+			ptr8_3 = (int8_t *)malloc(config.maxSampleLength);
 			if (ptr8_3 == NULL)
 			{
 				statusOutOfMemory();
@@ -2900,7 +2918,7 @@
 
 			ptr8_2 = ptr8_3;
 
-			memcpy(ptr8_2, ptr8_1, MAX_SAMPLE_LEN);
+			memcpy(ptr8_2, ptr8_1, config.maxSampleLength);
 
 			editor.modulateOffset = 0;
 			editor.modulatePos = 0;
@@ -3047,8 +3065,8 @@
 
 			turnOffVoices();
 
-			memcpy(&song->sampleData[s->offset], &song->sampleData[s->offset + editor.samplePos], MAX_SAMPLE_LEN - editor.samplePos);
-			memset(&song->sampleData[s->offset + (MAX_SAMPLE_LEN - editor.samplePos)], 0, editor.samplePos);
+			memcpy(&song->sampleData[s->offset], &song->sampleData[s->offset + editor.samplePos], config.maxSampleLength - editor.samplePos);
+			memset(&song->sampleData[s->offset + (config.maxSampleLength - editor.samplePos)], 0, editor.samplePos);
 
 			if (editor.samplePos > s->loopStart)
 			{
@@ -3057,10 +3075,10 @@
 			}
 			else
 			{
-				s->loopStart = (s->loopStart - editor.samplePos) & 0xFFFE;
+				s->loopStart = (s->loopStart - editor.samplePos) & config.maxSampleLength;
 			}
 
-			s->length = (s->length - editor.samplePos) & 0xFFFE;
+			s->length = (s->length - editor.samplePos) & config.maxSampleLength;
 
 			editor.samplePos = 0;
 			fixSampleBeep(s);
@@ -3277,7 +3295,16 @@
 		case PTB_EO_NOTE4_DOWN: edNote4DownButton(); break;
 		case PTB_EO_RESET: resetChord(); break;
 		case PTB_EO_UNDO: undoChord(); break;
-		case PTB_EO_LENGTH: toggleChordLength(); break;
+
+		case PTB_EO_LENGTH:
+		{
+			if (config.maxSampleLength != 65534 && mouse.x > 157) // yuck!
+				break;
+
+			toggleChordLength();
+		}
+		break;
+
 		case PTB_EO_MAJOR: setChordMajor(); break;
 		case PTB_EO_MINOR: setChordMinor(); break;
 		case PTB_EO_SUS4: setChordSus4(); break;
@@ -3515,6 +3542,9 @@
 
 		case PTB_SLENGTHS:
 		{
+			if (config.maxSampleLength == 65534 && mouse.x < 62) // yuck!
+				break;
+
 			if (editor.sampleZero)
 			{
 				statusNotSampleZero();
@@ -3545,12 +3575,25 @@
 			}
 			else
 			{
-				ui.tmpDisp16 = song->samples[editor.currSample].length;
-				song->samples[editor.currSample].lengthDisp = &ui.tmpDisp16;
-				ui.numPtr16 = &ui.tmpDisp16;
-				ui.numLen = 4;
-				ui.numBits = 16;
-				ui.editTextPos = 2808; // (y * 40) + x
+				ui.force32BitNumPtr = true;
+
+				ui.tmpDisp32 = song->samples[editor.currSample].length;
+				song->samples[editor.currSample].lengthDisp = &ui.tmpDisp32;
+				ui.numPtr32 = &ui.tmpDisp32;
+
+				if (config.maxSampleLength == 65534)
+				{
+					ui.numLen = 4;
+					ui.numBits = 16;
+					ui.editTextPos = 2808; // (y * 40) + x
+				}
+				else
+				{
+					ui.numLen = 5;
+					ui.numBits = 17;
+					ui.editTextPos = 2807; // (y * 40) + x
+				}
+
 				getNumLine(TEXT_EDIT_HEX, PTB_SLENGTHS);
 			}
 		}
@@ -3558,6 +3601,9 @@
 
 		case PTB_SREPEATS:
 		{
+			if (config.maxSampleLength == 65534 && mouse.x < 62) // yuck!
+				break;
+
 			if (editor.sampleZero)
 			{
 				statusNotSampleZero();
@@ -3581,7 +3627,7 @@
 
 				ui.updateCurrSampleRepeat = true;
 				if (ui.editOpScreenShown && ui.editOpScreen == 3)
-					ui.updateLengthText = true;
+					ui.updateChordLengthText = true;
 
 				if (ui.samplerScreenShown)
 					setLoopSprites();
@@ -3591,12 +3637,25 @@
 			}
 			else
 			{
-				ui.tmpDisp16 = song->samples[editor.currSample].loopStart;
-				song->samples[editor.currSample].loopStartDisp = &ui.tmpDisp16;
-				ui.numPtr16 = &ui.tmpDisp16;
-				ui.numLen = 4;
-				ui.numBits = 16;
-				ui.editTextPos = 3248; // (y * 40) + x
+				ui.force32BitNumPtr = true;
+
+				ui.tmpDisp32 = song->samples[editor.currSample].loopStart;
+				song->samples[editor.currSample].loopStartDisp = &ui.tmpDisp32;
+				ui.numPtr32 = &ui.tmpDisp32;
+
+				if (config.maxSampleLength == 65534)
+				{
+					ui.numLen = 4;
+					ui.numBits = 16;
+					ui.editTextPos = 3248; // (y * 40) + x
+				}
+				else
+				{
+					ui.numLen = 5;
+					ui.numBits = 17;
+					ui.editTextPos = 3247; // (y * 40) + x
+				}
+
 				getNumLine(TEXT_EDIT_HEX, PTB_SREPEATS);
 			}
 		}
@@ -3604,6 +3663,9 @@
 
 		case PTB_SREPLENS:
 		{
+			if (config.maxSampleLength == 65534 && mouse.x < 62) // yuck!
+				break;
+
 			if (editor.sampleZero)
 			{
 				statusNotSampleZero();
@@ -3630,7 +3692,7 @@
 
 				ui.updateCurrSampleReplen = true;
 				if (ui.editOpScreenShown && ui.editOpScreen == 3)
-					ui.updateLengthText = true;
+					ui.updateChordLengthText = true;
 
 				if (ui.samplerScreenShown)
 					setLoopSprites();
@@ -3640,12 +3702,25 @@
 			}
 			else
 			{
-				ui.tmpDisp16 = song->samples[editor.currSample].loopLength;
-				song->samples[editor.currSample].loopLengthDisp = &ui.tmpDisp16;
-				ui.numPtr16 = &ui.tmpDisp16;
-				ui.numLen = 4;
-				ui.numBits = 16;
-				ui.editTextPos = 3688; // (y * 40) + x
+				ui.force32BitNumPtr = true;
+
+				ui.tmpDisp32 = song->samples[editor.currSample].loopLength;
+				song->samples[editor.currSample].loopLengthDisp = &ui.tmpDisp32;
+				ui.numPtr32 = &ui.tmpDisp32;
+
+				if (config.maxSampleLength == 0xFFFE)
+				{
+					ui.numLen = 4;
+					ui.numBits = 16;
+					ui.editTextPos = 3688; // (y * 40) + x
+				}
+				else
+				{
+					ui.numLen = 5;
+					ui.numBits = 17;
+					ui.editTextPos = 3687; // (y * 40) + x
+				}
+
 				getNumLine(TEXT_EDIT_HEX, PTB_SREPLENS);
 			}
 		}
@@ -4402,7 +4477,7 @@
 
 		case PTB_SLENGTHU:
 		{
-			if (!editor.sampleZero && song->samples[editor.currSample].length < MAX_SAMPLE_LEN)
+			if (!editor.sampleZero && song->samples[editor.currSample].length < config.maxSampleLength)
 			{
 				sampleLengthUpButton(INCREMENT_SLOW);
 				updateWindowTitle(MOD_IS_MODIFIED);
--- a/src/pt2_pat2smp.c
+++ b/src/pt2_pat2smp.c
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "pt2_header.h"
+#include "pt2_config.h"
 #include "pt2_helpers.h"
 #include "pt2_visuals.h"
 #include "pt2_mouse.h"
@@ -31,7 +32,7 @@
 		return;
 	}
 
-	editor.dPat2SmpBuf = (double *)malloc(MAX_SAMPLE_LEN * sizeof (double));
+	editor.dPat2SmpBuf = (double *)malloc(config.maxSampleLength * sizeof (double));
 	if (editor.dPat2SmpBuf == NULL)
 	{
 		statusOutOfMemory();
@@ -101,8 +102,8 @@
 	free(editor.dPat2SmpBuf);
 	
 	// clear the rest of the sample (if not full)
-	if (renderLength < MAX_SAMPLE_LEN)
-		memset(&song->sampleData[s->offset+renderLength], 0, MAX_SAMPLE_LEN - renderLength);
+	if (renderLength < config.maxSampleLength)
+		memset(&song->sampleData[s->offset+renderLength], 0, config.maxSampleLength - renderLength);
 
 	if (editor.pat2SmpHQ)
 	{
@@ -115,7 +116,7 @@
 		s->fineTune = 0;
 	}
 
-	s->length = (uint16_t)renderLength;
+	s->length = renderLength;
 	s->volume = 64;
 	s->loopStart = 0;
 	s->loopLength = 2;
--- a/src/pt2_replayer.c
+++ b/src/pt2_replayer.c
@@ -37,21 +37,10 @@
 
 int8_t *allocMemForAllSamples(void)
 {
-	/* Allocate memory for all sample data blocks.
-	**
-	** We need three extra sample slots:
-	** The 1st is extra safety padding since setting a Paula length of 0
-	** results in reading (1+65535)*2 bytes. The 2nd and 3rd (64K*2 = 1x 128K)
-	** are reserved for NULL pointers. This is needed for emulating a PT quirk.
-	**
-	** We have a padding of 4 bytes at the end for length=0 quirk safety.
-	**
-	** PS: I don't really know if it's possible for ProTracker to set a Paula
-	** length of 0, but I fully support this Paula behavior just in case.
-	*/
-	const size_t allocLen = ((MOD_SAMPLES + 3) * MAX_SAMPLE_LEN) + 4;
+	// allocate memory for all sample data blocks (+ 2 extra, for quirk + safety)
+	const size_t allocLen = (MOD_SAMPLES + 2) * config.maxSampleLength;
 
-	return (int8_t *)calloc(1, allocLen);
+	return (int8_t *)calloc(allocLen, 1);
 }
 
 void modSetSpeed(int32_t speed)
@@ -668,7 +657,7 @@
 	uint16_t newOffset = ch->n_sampleoffset << 7;
 
 	// this signed test is the reason for the 9xx "sample >64kB = silence" bug
-	if ((int16_t)newOffset < ch->n_length)
+	if ((int16_t)newOffset < (int16_t)ch->n_length)
 	{
 		ch->n_length -= newOffset;
 		ch->n_start += newOffset << 1;
@@ -871,10 +860,10 @@
 		ch->n_start = &song->sampleData[s->offset];
 		ch->n_finetune = s->fineTune & 0xF;
 		ch->n_volume = s->volume;
-		ch->n_length = s->length >> 1;
-		ch->n_replen = s->loopLength >> 1;
+		ch->n_length = (uint16_t)(s->length >> 1);
+		ch->n_replen = (uint16_t)(s->loopLength >> 1);
 
-		const uint16_t repeat = s->loopStart >> 1;
+		const uint16_t repeat = (uint16_t)(s->loopStart >> 1);
 		if (repeat > 0)
 		{
 			ch->n_loopstart = ch->n_start + (repeat << 1);
@@ -889,7 +878,7 @@
 
 		// non-PT2 requirement (set safe sample space for uninitialized voices - f.ex. "the ultimate beeper.mod")
 		if (ch->n_length == 0)
-			ch->n_loopstart = ch->n_wavestart = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample
+			ch->n_loopstart = ch->n_wavestart = &song->sampleData[config.reservedSampleOffset]; // 128K reserved sample
 	}
 
 	if ((ch->n_note & 0xFFF) > 0)
@@ -1491,7 +1480,7 @@
 		memset(s->text, 0, sizeof (s->text));
 	}
 
-	memset(song->sampleData, 0, (MOD_SAMPLES + 1) * MAX_SAMPLE_LEN);
+	memset(song->sampleData, 0, (MOD_SAMPLES + 1) * config.maxSampleLength);
 
 	editor.currSample = 0;
 	editor.hiLowInstr = 0;
--- a/src/pt2_sample_loader.c
+++ b/src/pt2_sample_loader.c
@@ -58,11 +58,11 @@
 	uint8_t *audioDataU8;
 	int16_t *audioDataS16, tempVol;
 	uint16_t audioFormat, numChannels, bitsPerSample;
-	int32_t *audioDataS32, smp32;
-	uint32_t *audioDataU32, i, nameLen, chunkID, chunkSize;
-	uint32_t sampleLength, sampleRate, filesize, loopFlags;
-	uint32_t loopStart, loopEnd, dataPtr, dataLen, fmtPtr, endOfChunk, bytesRead;
-	uint32_t fmtLen, inamPtr, inamLen, smplPtr, smplLen, xtraPtr, xtraLen;
+	int32_t *audioDataS32, smp32, sampleLength, loopStart, loopEnd, inamLen, nameLen, i;
+	uint32_t *audioDataU32, chunkID, chunkSize;
+	uint32_t sampleRate, filesize, loopFlags;
+	uint32_t dataPtr, dataLen, fmtPtr, endOfChunk, bytesRead;
+	uint32_t fmtLen, inamPtr, smplPtr, smplLen, xtraPtr, xtraLen;
 	float *fAudioDataFloat;
 	double *dAudioDataDouble;
 	FILE *f;
@@ -204,7 +204,7 @@
 	sampleLength = dataLen;
 	// ---------------------------
 
-	if (sampleRate == 0 || sampleLength == 0 || sampleLength >= filesize*(bitsPerSample/8))
+	if (sampleRate == 0 || sampleLength == 0 || sampleLength >= (int32_t)filesize*(bitsPerSample/8))
 	{
 		fclose(f);
 		displayErrorMsg("WAV CORRUPT !");
@@ -256,12 +256,12 @@
 	// ---- READ SAMPLE DATA ----
 	fseek(f, dataPtr, SEEK_SET);
 
-	int8_t *smpPtr = &song->sampleData[editor.currSample * MAX_SAMPLE_LEN];
+	int8_t *smpPtr = &song->sampleData[editor.currSample * config.maxSampleLength];
 
 	if (bitsPerSample == 8) // 8-BIT INTEGER SAMPLE
 	{
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataU8 = (uint8_t *)malloc(sampleLength * sizeof (uint8_t));
 		if (audioDataU8 == NULL)
@@ -272,7 +272,7 @@
 		}
 
 		// read sample data
-		if (fread(audioDataU8, 1, sampleLength, f) != sampleLength)
+		if (fread(audioDataU8, 1, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataU8);
@@ -299,8 +299,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		turnOffVoices();
 		for (i = 0; i < sampleLength; i++)
@@ -311,8 +311,8 @@
 	else if (bitsPerSample == 16) // 16-BIT INTEGER SAMPLE
 	{
 		sampleLength >>= 1;
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataS16 = (int16_t *)malloc(sampleLength * sizeof (int16_t));
 		if (audioDataS16 == NULL)
@@ -323,7 +323,7 @@
 		}
 
 		// read sample data
-		if (fread(audioDataS16, 2, sampleLength, f) != sampleLength)
+		if (fread(audioDataS16, 2, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataS16);
@@ -346,8 +346,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		double dAmp = 1.0;
 		if (forceDownSampling) // we already normalized
@@ -374,8 +374,8 @@
 	else if (bitsPerSample == 24) // 24-BIT INTEGER SAMPLE
 	{
 		sampleLength /= 3;
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
 		if (audioDataS32 == NULL)
@@ -412,8 +412,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		double dAmp = 1.0;
 		if (forceDownSampling) // we already normalized
@@ -440,8 +440,8 @@
 	else if (audioFormat == WAV_FORMAT_PCM && bitsPerSample == 32) // 32-BIT INTEGER SAMPLE
 	{
 		sampleLength >>= 2;
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
 		if (audioDataS32 == NULL)
@@ -452,7 +452,7 @@
 		}
 
 		// read sample data
-		if (fread(audioDataS32, 4, sampleLength, f) != sampleLength)
+		if (fread(audioDataS32, 4, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataS32);
@@ -478,8 +478,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		double dAmp = 1.0;
 		if (forceDownSampling) // we already normalized
@@ -506,8 +506,8 @@
 	else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 32) // 32-BIT FLOATING POINT SAMPLE
 	{
 		sampleLength >>= 2;
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataU32 = (uint32_t *)malloc(sampleLength * sizeof (uint32_t));
 		if (audioDataU32 == NULL)
@@ -518,7 +518,7 @@
 		}
 
 		// read sample data
-		if (fread(audioDataU32, 4, sampleLength, f) != sampleLength)
+		if (fread(audioDataU32, 4, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataU32);
@@ -543,8 +543,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		float fAmp = 1.0f;
 		const float fPeak = getFloatPeak(fAudioDataFloat, sampleLength);
@@ -564,8 +564,8 @@
 	else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 64) // 64-BIT FLOATING POINT SAMPLE
 	{
 		sampleLength >>= 3;
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataU32 = (uint32_t *)malloc(sampleLength * (sizeof (uint32_t) * 2));
 		if (audioDataU32 == NULL)
@@ -576,7 +576,7 @@
 		}
 
 		// read sample data
-		if (fread(audioDataU32, 8, sampleLength, f) != sampleLength)
+		if (fread(audioDataU32, 8, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataU32);
@@ -601,8 +601,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		double dAmp = 1.0;
 		const double dPeak = getDoublePeak(dAudioDataDouble, sampleLength);
@@ -620,17 +620,17 @@
 		free(audioDataU32);
 	}
 
-	if (sampleLength < MAX_SAMPLE_LEN) // clear rest of sample data
-		memset(&song->sampleData[s->offset + sampleLength], 0, MAX_SAMPLE_LEN - sampleLength);
+	if (sampleLength < config.maxSampleLength) // clear rest of sample data
+		memset(&song->sampleData[s->offset + sampleLength], 0, config.maxSampleLength - sampleLength);
 
 	// set sample length
 	if (sampleLength & 1)
 	{
-		if (++sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (++sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 	}
 
-	s->length = (uint16_t)sampleLength;
+	s->length = sampleLength;
 	s->fineTune = 0;
 	s->volume = 64;
 	s->loopStart = 0;
@@ -654,15 +654,15 @@
 			loopEnd >>= 1;
 		}
 
-		loopStart &= 0xFFFFFFFE;
-		loopEnd &= 0xFFFFFFFE;
+		loopStart &= ~1;
+		loopEnd &= ~1;
 
 		if (loopFlags)
 		{
 			if (loopStart+(loopEnd-loopStart) <= s->length)
 			{
-				s->loopStart = (uint16_t)loopStart;
-				s->loopLength = (uint16_t)(loopEnd - loopStart);
+				s->loopStart = loopStart;
+				s->loopLength = loopEnd - loopStart;
 
 				if (s->loopLength < 2)
 				{
@@ -716,7 +716,7 @@
 	// copy over sample name
 	if (!wavSampleNameFound)
 	{
-		nameLen = (uint32_t)strlen(entryName);
+		nameLen = (int32_t)strlen(entryName);
 		for (i = 0; i < 21; i++)
 			s->text[i] = (i < nameLen) ? (char)entryName[i] : '\0';
 
@@ -755,9 +755,9 @@
 	int16_t *ptr16;
 	int32_t filesize, smp32;
 	uint16_t sampleRate;
-	uint32_t i, sampleLength, sampleLoopStart, sampleLoopLength;
+	int32_t i, sampleLength, sampleLoopStart, sampleLoopLength, nameLen;
 	uint32_t sampleVolume, blockName, blockSize;
-	uint32_t vhdrPtr, vhdrLen, bodyPtr, bodyLen, namePtr, nameLen;
+	uint32_t vhdrPtr, vhdrLen, bodyPtr, bodyLen, namePtr;
 	FILE *f;
 	moduleSample_t *s;
 
@@ -898,7 +898,7 @@
 		forceDownSampling = false;
 	}
 
-	uint32_t maxSampleLength = MAX_SAMPLE_LEN;
+	int32_t maxSampleLength = config.maxSampleLength;
 	if (is16Bit)
 		maxSampleLength *= 2;
 
@@ -944,8 +944,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		double dAmp = 1.0;
 		if (forceDownSampling) // we already normalized
@@ -978,8 +978,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		memcpy(&song->sampleData[s->offset], sampleData, sampleLength);
 	}
@@ -986,18 +986,18 @@
 
 	free(sampleData);
 
-	if (sampleLength < MAX_SAMPLE_LEN) // clear rest of sample data
-		memset(&song->sampleData[s->offset + sampleLength], 0, MAX_SAMPLE_LEN - sampleLength);
+	if (sampleLength < config.maxSampleLength) // clear rest of sample data
+		memset(&song->sampleData[s->offset + sampleLength], 0, config.maxSampleLength - sampleLength);
 
 	// set sample length
 	if (sampleLength & 1)
 	{
-		if (++sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (++sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 	}
 
-	sampleLoopStart &= 0xFFFFFFFE;
-	sampleLoopLength &= 0xFFFFFFFE;
+	sampleLoopStart &= ~1;
+	sampleLoopLength &= ~1;
 
 	if (sampleLoopLength < 2)
 	{
@@ -1026,9 +1026,9 @@
 	// set sample attributes
 	s->volume = (int8_t)sampleVolume;
 	s->fineTune = 0;
-	s->length = (uint16_t)sampleLength;
-	s->loopStart = (uint16_t)sampleLoopStart;
-	s->loopLength = (uint16_t)sampleLoopLength;
+	s->length = sampleLength;
+	s->loopStart = sampleLoopStart;
+	s->loopLength = sampleLoopLength;
 
 	// read name
 	if (namePtr != 0 && nameLen > 0)
@@ -1090,7 +1090,7 @@
 bool loadRAWSample(UNICHAR *fileName, char *entryName)
 {
 	uint8_t i;
-	uint32_t nameLen, fileSize;
+	int32_t nameLen, fileSize;
 	FILE *f;
 	moduleSample_t *s;
 
@@ -1107,9 +1107,9 @@
 	fileSize = ftell(f);
 	fseek(f, 0, SEEK_SET);
 
-	fileSize &= 0xFFFFFFFE;
-	if (fileSize > MAX_SAMPLE_LEN)
-		fileSize = MAX_SAMPLE_LEN;
+	fileSize &= ~1;
+	if (fileSize > config.maxSampleLength)
+		fileSize = config.maxSampleLength;
 
 	turnOffVoices();
 
@@ -1116,13 +1116,13 @@
 	fread(&song->sampleData[s->offset], 1, fileSize, f);
 	fclose(f);
 
-	if (fileSize < MAX_SAMPLE_LEN)
-		memset(&song->sampleData[s->offset + fileSize], 0, MAX_SAMPLE_LEN - fileSize);
+	if (fileSize < config.maxSampleLength)
+		memset(&song->sampleData[s->offset + fileSize], 0, config.maxSampleLength - fileSize);
 
 	// set sample attributes
 	s->volume = 64;
 	s->fineTune = 0;
-	s->length = (uint16_t)fileSize;
+	s->length = fileSize;
 	s->loopStart = 0;
 	s->loopLength = 2;
 
@@ -1179,8 +1179,8 @@
 	uint8_t *audioDataU8, sampleRateBytes[10];
 	int16_t *audioDataS16;
 	uint16_t bitDepth, numChannels;
-	int32_t filesize, *audioDataS32, smp32;
-	uint32_t nameLen, i, offset, sampleRate, sampleLength, blockName, blockSize;
+	int32_t i, filesize, *audioDataS32, smp32, sampleLength, nameLen;
+	uint32_t offset, sampleRate, blockName, blockSize;
 	uint32_t commPtr, commLen, ssndPtr, ssndLen;
 	FILE *f;
 	moduleSample_t *s;
@@ -1336,12 +1336,12 @@
 		forceDownSampling = false;
 	}
 
-	int8_t *smpPtr = &song->sampleData[editor.currSample * MAX_SAMPLE_LEN];
+	int8_t *smpPtr = &song->sampleData[editor.currSample * config.maxSampleLength];
 
 	if (bitDepth == 8) // 8-BIT INTEGER SAMPLE
 	{
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataS8 = (int8_t *)malloc(sampleLength * sizeof (int8_t));
 		if (audioDataS8 == NULL)
@@ -1352,7 +1352,7 @@
 		}
 
 		// read sample data
-		if (fread(audioDataS8, 1, sampleLength, f) != sampleLength)
+		if (fread(audioDataS8, 1, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataS8);
@@ -1381,8 +1381,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		turnOffVoices();
 		for (i = 0; i < sampleLength; i++)
@@ -1393,8 +1393,8 @@
 	else if (bitDepth == 16) // 16-BIT INTEGER SAMPLE
 	{
 		sampleLength >>= 1;
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataS16 = (int16_t *)malloc(sampleLength * sizeof (int16_t));
 		if (audioDataS16 == NULL)
@@ -1405,7 +1405,7 @@
 		}
 
 		// read sample data
-		if (fread(audioDataS16, 2, sampleLength, f) != sampleLength)
+		if (fread(audioDataS16, 2, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataS16);
@@ -1432,8 +1432,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		double dAmp = 1.0;
 		if (forceDownSampling) // we already normalized
@@ -1460,8 +1460,8 @@
 	else if (bitDepth == 24) // 24-BIT INTEGER SAMPLE
 	{
 		sampleLength /= 3;
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
 		if (audioDataS32 == NULL)
@@ -1472,7 +1472,7 @@
 		}
 
 		// read sample data
-		if (fread(&audioDataS32[sampleLength >> 2], 3, sampleLength, f) != sampleLength)
+		if (fread(&audioDataS32[sampleLength >> 2], 3, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataS32);
@@ -1506,8 +1506,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		double dAmp = 1.0;
 		if (forceDownSampling) // we already normalized
@@ -1533,8 +1533,8 @@
 	else if (bitDepth == 32) // 32-BIT INTEGER SAMPLE
 	{
 		sampleLength >>= 2;
-		if (sampleLength > MAX_SAMPLE_LEN*4)
-			sampleLength = MAX_SAMPLE_LEN*4;
+		if (sampleLength > config.maxSampleLength*4)
+			sampleLength = config.maxSampleLength*4;
 
 		audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
 		if (audioDataS32 == NULL)
@@ -1545,7 +1545,7 @@
 		}
 
 		// read sample data
-		if (fread(audioDataS32, 4, sampleLength, f) != sampleLength)
+		if (fread(audioDataS32, 4, sampleLength, f) != (size_t)sampleLength)
 		{
 			fclose(f);
 			free(audioDataS32);
@@ -1575,8 +1575,8 @@
 			sampleLength >>= 1;
 		}
 
-		if (sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 
 		double dAmp = 1.0;
 		if (forceDownSampling) // we already normalized
@@ -1600,17 +1600,17 @@
 		free(audioDataS32);
 	}
 
-	if (sampleLength < MAX_SAMPLE_LEN) // clear rest of sample data
-		memset(&song->sampleData[s->offset + sampleLength], 0, MAX_SAMPLE_LEN - sampleLength);
+	if (sampleLength < config.maxSampleLength) // clear rest of sample data
+		memset(&song->sampleData[s->offset + sampleLength], 0, config.maxSampleLength - sampleLength);
 
 	// set sample length
 	if (sampleLength & 1)
 	{
-		if (++sampleLength > MAX_SAMPLE_LEN)
-			sampleLength = MAX_SAMPLE_LEN;
+		if (++sampleLength > config.maxSampleLength)
+			sampleLength = config.maxSampleLength;
 	}
 
-	s->length = (uint16_t)sampleLength;
+	s->length = sampleLength;
 	s->fineTune = 0;
 	s->volume = 64;
 	s->loopStart = 0;
@@ -1619,7 +1619,7 @@
 	fclose(f);
 
 	// copy over sample name
-	nameLen = (uint32_t)strlen(entryName);
+	nameLen = (int32_t)strlen(entryName);
 	for (i = 0; i < 21; i++)
 		s->text[i] = (i < nameLen) ? (char)entryName[i] : '\0';
 
--- a/src/pt2_sample_saver.c
+++ b/src/pt2_sample_saver.c
@@ -161,8 +161,8 @@
 
 	const int8_t *sampleData = &song->sampleData[s->offset];
 	const uint32_t sampleLength = s->length;
-	const uint32_t loopStart = s->loopStart & 0xFFFE;
-	const uint32_t loopLength = s->loopLength & 0xFFFE;
+	const uint32_t loopStart = s->loopStart & ~1;
+	const uint32_t loopLength = s->loopLength & ~1;
 
 	switch (diskop.smpSaveType)
 	{
--- a/src/pt2_sampler.c
+++ b/src/pt2_sampler.c
@@ -55,7 +55,7 @@
 
 	moduleSample_t *s = &song->samples[editor.currSample];
 
-	uint32_t newLength = (s->length >> 1) & 0xFFFE;
+	int32_t newLength = (s->length >> 1) & config.maxSampleLength;
 	if (newLength < 2)
 		return;
 
@@ -63,16 +63,16 @@
 
 	// upsample
 	int8_t *ptr8 = &song->sampleData[s->offset];
-	for (uint32_t i = 0; i < newLength; i++)
+	for (int32_t i = 0; i < newLength; i++)
 		ptr8[i] = ptr8[i << 1];
 
 	// clear junk after shrunk sample
-	if (newLength < MAX_SAMPLE_LEN)
-		memset(&ptr8[newLength], 0, MAX_SAMPLE_LEN - newLength);
+	if (newLength < config.maxSampleLength)
+		memset(&ptr8[newLength], 0, config.maxSampleLength - newLength);
 
-	s->length = (uint16_t)newLength;
-	s->loopStart = (s->loopStart >> 1) & 0xFFFE;
-	s->loopLength = (s->loopLength >> 1) & 0xFFFE;
+	s->length = newLength;
+	s->loopStart = (s->loopStart >> 1) & ~1;
+	s->loopLength = (s->loopLength >> 1) & ~1;
 
 	if (s->loopLength < 2)
 	{
@@ -97,9 +97,9 @@
 
 	moduleSample_t *s = &song->samples[editor.currSample];
 
-	uint32_t newLength = s->length << 1;
-	if (newLength > MAX_SAMPLE_LEN)
-		newLength = MAX_SAMPLE_LEN;
+	int32_t newLength = s->length << 1;
+	if (newLength > config.maxSampleLength)
+		newLength = config.maxSampleLength;
 
 	turnOffVoices();
 
@@ -113,12 +113,12 @@
 		ptr8_2[i<<1] = ptr8_2[i];
 	}
 
-	s->length = (uint16_t)newLength;
+	s->length = newLength;
 
 	if (s->loopLength > 2)
 	{
-		uint32_t loopStart = s->loopStart << 1;
-		uint32_t loopLength = s->loopLength << 1;
+		int32_t loopStart = s->loopStart << 1;
+		int32_t loopLength = s->loopLength << 1;
 
 		if (loopStart+loopLength > s->length)
 		{
@@ -126,8 +126,8 @@
 			loopLength = 2;
 		}
 
-		s->loopStart = (uint16_t)loopStart;
-		s->loopLength = (uint16_t)loopLength;
+		s->loopStart = loopStart;
+		s->loopLength = loopLength;
 	}
 
 	fixSampleBeep(s);
@@ -372,7 +372,7 @@
 	}
 
 	// render sample data
-	if (sampler.samDisplay >= 0 && sampler.samDisplay <= MAX_SAMPLE_LEN)
+	if (sampler.samDisplay >= 0 && sampler.samDisplay <= config.maxSampleLength)
 	{
 		y1 = SAMPLE_AREA_Y_CENTER - getScaledSample(scr2SmpPos(0));
 
@@ -426,10 +426,21 @@
 		return;
 
 	// render "sample display" text
-	if (sampler.samStart == sampler.blankSample)
-		printFiveDecimalsBg(272, 214, 0, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+
+	if (config.maxSampleLength == 0xFFFE)
+	{
+		if (sampler.samStart == sampler.blankSample)
+			printFiveDecimalsBg(272, 214, 0, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		else
+			printFiveDecimalsBg(272, 214, sampler.samDisplay, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+	}
 	else
-		printFiveDecimalsBg(272, 214, sampler.samDisplay, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+	{
+		if (sampler.samStart == sampler.blankSample)
+			printSixDecimalsBg(270, 214, 0, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		else
+			printSixDecimalsBg(270, 214, sampler.samDisplay, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+	}
 
 	setDragBar();
 	setLoopSprites();
@@ -745,12 +756,12 @@
 	{
 		memcpy(&song->sampleData[s->offset], editor.smpRedoBuffer[sample], editor.smpRedoLengths[sample]);
 
-		if (editor.smpRedoLengths[sample] < MAX_SAMPLE_LEN)
-			memset(&song->sampleData[s->offset + editor.smpRedoLengths[sample]], 0, MAX_SAMPLE_LEN - editor.smpRedoLengths[sample]);
+		if (editor.smpRedoLengths[sample] < config.maxSampleLength)
+			memset(&song->sampleData[s->offset + editor.smpRedoLengths[sample]], 0, config.maxSampleLength - editor.smpRedoLengths[sample]);
 	}
 	else
 	{
-		memset(&song->sampleData[s->offset], 0, MAX_SAMPLE_LEN);
+		memset(&song->sampleData[s->offset], 0, config.maxSampleLength);
 	}
 
 	s->fineTune = editor.smpRedoFinetunes[sample];
@@ -804,8 +815,8 @@
 
 bool allocSamplerVars(void)
 {
-	sampler.copyBuf = (int8_t *)malloc(MAX_SAMPLE_LEN);
-	sampler.blankSample = (int8_t *)calloc(MAX_SAMPLE_LEN, 1);
+	sampler.copyBuf = (int8_t *)malloc(131070);
+	sampler.blankSample = (int8_t *)calloc(131070, 1);
 
 	if (sampler.copyBuf == NULL || sampler.blankSample == NULL)
 		return false;
@@ -903,9 +914,9 @@
 #define INTRP_LINEAR_TAPS 2
 #define INTRP8_LINEAR(s1, s2, f) /* output: -127..128 */ \
 	s2 -= s1; \
-	s2 *= (int32_t)(f); \
+	s2 *= (int32_t)(f >> 16); \
 	s1 <<= 8; \
-	s2 >>= (16 - 8); \
+	s2 >>= 16-8; \
 	s1 += s2; \
 	s1 >>= 8; \
 
@@ -916,7 +927,7 @@
 	int16_t refPeriod, newPeriod;
 	int32_t samples[INTRP_LINEAR_TAPS], i, pos, readPos, writePos;
 	int32_t readLength, writeLength, loopStart, loopLength;
-	uint32_t posFrac, delta;
+	uint64_t frac64, delta64;
 	moduleSample_t *s;
 
 	if (editor.sampleZero)
@@ -962,18 +973,18 @@
 		return;
 	}
 
-	delta = ((uint32_t)readLength << 16) / (uint32_t)writeLength;
-	assert(delta != 0);
+	delta64 = ((uint64_t)readLength << 32) / writeLength;
+	assert(delta64 != 0);
 
-	writeLength = writeLength & 0xFFFFFFFE;
-	if (writeLength > MAX_SAMPLE_LEN)
-		writeLength = MAX_SAMPLE_LEN;
+	writeLength = writeLength & ~1;
+	if (writeLength > config.maxSampleLength)
+		writeLength = config.maxSampleLength;
 
 	memcpy(readData, writeData, readLength);
 
 	// resample
 
-	posFrac = 0;
+	frac64 = 0;
 
 	turnOffVoices();
 	while (writePos < writeLength)
@@ -988,28 +999,28 @@
 				samples[i] = readData[pos];
 		}
 
-		INTRP8_LINEAR(samples[0], samples[1], posFrac);
+		INTRP8_LINEAR(samples[0], samples[1], frac64);
 		writeData[writePos++] = (int8_t)samples[0];
 
-		posFrac += delta;
-		readPos += posFrac >> 16;
-		posFrac &= 0xFFFF;
+		frac64 += delta64;
+		readPos += frac64 >> 32;
+		frac64 &= 0xFFFFFFFF;
 	}
 	free(readData);
 
 	// wipe non-used data in new sample
-	if (writeLength < MAX_SAMPLE_LEN)
-		memset(&writeData[writePos], 0, MAX_SAMPLE_LEN - writeLength);
+	if (writeLength < config.maxSampleLength)
+		memset(&writeData[writePos], 0, config.maxSampleLength - writeLength);
 
 	// update sample attributes
-	s->length = (uint16_t)writeLength;
+	s->length = writeLength;
 	s->fineTune = 0;
 
 	// scale loop points (and deactivate if overflowing)
 	if ((s->loopStart + s->loopLength) > 2)
 	{
-		loopStart  = (int32_t)(((uint32_t)s->loopStart << 16) / delta) & 0xFFFFFFFE;
-		loopLength = (int32_t)(((uint32_t)s->loopLength << 16) / delta) & 0xFFFFFFFE;
+		loopStart = (int32_t)(((uint64_t)s->loopStart << 32) / delta64) & ~1;
+		loopLength = (int32_t)(((uint64_t)s->loopLength << 32) / delta64) & ~1;
 
 		if (loopStart+loopLength > s->length)
 		{
@@ -1018,8 +1029,8 @@
 		}
 		else
 		{
-			s->loopStart = (uint16_t)loopStart;
-			s->loopLength = (uint16_t)loopLength;
+			s->loopStart = loopStart;
+			s->loopLength = loopLength;
 		}
 	}
 
@@ -1120,12 +1131,12 @@
 	}
 
 	memcpy(&song->sampleData[s3->offset], mixPtr, mixLength);
-	if (mixLength < MAX_SAMPLE_LEN)
-		memset(&song->sampleData[s3->offset + mixLength], 0, MAX_SAMPLE_LEN - mixLength);
+	if (mixLength < config.maxSampleLength)
+		memset(&song->sampleData[s3->offset + mixLength], 0, config.maxSampleLength - mixLength);
 
 	free(mixPtr);
 
-	s3->length = (uint16_t)mixLength;
+	s3->length = mixLength;
 	s3->volume = 64;
 	s3->fineTune = 0;
 	s3->loopStart = 0;
@@ -1308,7 +1319,7 @@
 		}
 		invertRange();
 
-		editor.samplePos = (uint16_t)editor.markEndOfs;
+		editor.samplePos = editor.markEndOfs;
 	}
 
 	updateSamplePos();
@@ -1347,7 +1358,7 @@
 		}
 		invertRange();
 
-		editor.samplePos = (uint16_t)editor.markEndOfs;
+		editor.samplePos = editor.markEndOfs;
 	}
 
 	updateSamplePos();
@@ -1380,7 +1391,7 @@
 		}
 		invertRange();
 
-		editor.samplePos = (uint16_t)editor.markEndOfs;
+		editor.samplePos = editor.markEndOfs;
 	}
 
 	updateSamplePos();
@@ -1419,7 +1430,7 @@
 
 	sampler.copyBufSize = editor.markEndOfs - editor.markStartOfs;
 
-	if ((int32_t)(editor.markStartOfs + sampler.copyBufSize) > MAX_SAMPLE_LEN)
+	if ((int32_t)(editor.markStartOfs + sampler.copyBufSize) > config.maxSampleLength)
 	{
 		displayErrorMsg("COPY ERROR !");
 		return;
@@ -1471,7 +1482,7 @@
 	// if whole sample is marked, wipe it
 	if (editor.markEndOfs-editor.markStartOfs >= sampleLength)
 	{
-		memset(&song->sampleData[s->offset], 0, MAX_SAMPLE_LEN);
+		memset(&song->sampleData[s->offset], 0, config.maxSampleLength);
 
 		invertRange();
 		editor.markStartOfs = -1;
@@ -1497,7 +1508,7 @@
 	markStart = editor.markStartOfs;
 
 	copyLength = (editor.markStartOfs + sampleLength) - markEnd;
-	if (copyLength < 2 || copyLength > MAX_SAMPLE_LEN)
+	if (copyLength < 2 || copyLength > config.maxSampleLength)
 	{
 		displayErrorMsg("SAMPLE CUT FAIL !");
 		return;
@@ -1520,8 +1531,8 @@
 	// nuke sample data and copy over the result
 	memcpy(&song->sampleData[s->offset], tmpBuf, copyLength);
 
-	if (copyLength < MAX_SAMPLE_LEN)
-		memset(&song->sampleData[s->offset+copyLength], 0, MAX_SAMPLE_LEN - copyLength);
+	if (copyLength < config.maxSampleLength)
+		memset(&song->sampleData[s->offset+copyLength], 0, config.maxSampleLength - copyLength);
 
 	free(tmpBuf);
 
@@ -1556,11 +1567,11 @@
 			if (markStart < s->loopStart+s->loopLength)
 			{
 				// we cut data inside the loop, increase loop length
-				val32 = (s->loopLength - (markEnd - markStart)) & 0xFFFFFFFE;
+				val32 = (s->loopLength - (markEnd - markStart)) & ~1;
 				if (val32 < 2)
 					val32 = 2;
 
-				s->loopLength = (uint16_t)val32;
+				s->loopLength = val32;
 			}
 
 			// we cut data after the loop, don't modify loop points
@@ -1568,7 +1579,7 @@
 		else
 		{
 			// we cut data before the loop, adjust loop start point
-			val32 = (s->loopStart - (markEnd - markStart)) & 0xFFFFFFFE;
+			val32 = (s->loopStart - (markEnd - markStart)) & ~1;
 			if (val32 < 0)
 			{
 				s->loopStart = 0;
@@ -1576,12 +1587,12 @@
 			}
 			else
 			{
-				s->loopStart = (uint16_t)val32;
+				s->loopStart = val32;
 			}
 		}
 	}
 
-	s->length = copyLength & 0xFFFE;
+	s->length = copyLength & ~1;
 
 	if (sampler.samDisplay <= 2)
 	{
@@ -1604,7 +1615,7 @@
 		invertRange();
 	}
 
-	editor.samplePos = (uint16_t)editor.markStartOfs;
+	editor.samplePos = editor.markStartOfs;
 	fixSampleBeep(s);
 	updateSamplePos();
 	recalcChordLength();
@@ -1651,13 +1662,13 @@
 	if (s->length == 0)
 		markStart = 0;
 
-	if (s->length+sampler.copyBufSize > MAX_SAMPLE_LEN)
+	if (s->length+sampler.copyBufSize > config.maxSampleLength)
 	{
 		displayErrorMsg("NOT ENOUGH ROOM");
 		return;
 	}
 
-	tmpBuf = (int8_t *)malloc(MAX_SAMPLE_LEN);
+	tmpBuf = (int8_t *)malloc(config.maxSampleLength);
 	if (tmpBuf == NULL)
 	{
 		statusOutOfMemory();
@@ -1687,11 +1698,11 @@
 			memcpy(&tmpBuf[readPos], &song->sampleData[s->offset+markStart], s->length - markStart);
 	}
 
-	int32_t newLength = (s->length + sampler.copyBufSize) & 0xFFFFFFFE;
-	if (newLength > MAX_SAMPLE_LEN)
-		newLength = MAX_SAMPLE_LEN;
+	int32_t newLength = (s->length + sampler.copyBufSize) & ~1;
+	if (newLength > config.maxSampleLength)
+		newLength = config.maxSampleLength;
 
-	sampler.samLength = s->length = (uint16_t)newLength;
+	sampler.samLength = s->length = newLength;
 
 	if (s->loopLength > 2) // loop enabled?
 	{
@@ -1701,7 +1712,7 @@
 			{
 				// we pasted data inside the loop, increase loop length
 
-				if (s->loopLength+sampler.copyBufSize > MAX_SAMPLE_LEN)
+				if (s->loopLength+sampler.copyBufSize > config.maxSampleLength)
 				{
 					s->loopStart = 0;
 					s->loopLength = 2;
@@ -1708,7 +1719,7 @@
 				}
 				else
 				{
-					s->loopLength = (uint16_t)(s->loopLength + sampler.copyBufSize) & 0xFFFE;
+					s->loopLength = (s->loopLength + sampler.copyBufSize) & config.maxSampleLength;
 					if (s->loopStart+s->loopLength > s->length)
 					{
 						s->loopStart = 0;
@@ -1722,7 +1733,7 @@
 		else
 		{
 			// we pasted data before the loop, adjust loop start point
-			if (s->loopStart+sampler.copyBufSize > MAX_SAMPLE_LEN)
+			if (s->loopStart+sampler.copyBufSize > config.maxSampleLength)
 			{
 				s->loopStart = 0;
 				s->loopLength = 2;
@@ -1729,7 +1740,7 @@
 			}
 			else
 			{
-				s->loopStart = (uint16_t)(s->loopStart + sampler.copyBufSize) & 0xFFFE;
+				s->loopStart = (s->loopStart + sampler.copyBufSize) & config.maxSampleLength;
 				if (s->loopStart+s->loopLength > s->length)
 				{
 					s->loopStart = 0;
@@ -1742,8 +1753,8 @@
 	memcpy(&song->sampleData[s->offset], tmpBuf, s->length);
 
 	// clear data after sample's length (if present)
-	if (s->length < MAX_SAMPLE_LEN)
-		memset(&song->sampleData[s->offset+s->length], 0, MAX_SAMPLE_LEN - s->length);
+	if (s->length < config.maxSampleLength)
+		memset(&song->sampleData[s->offset+s->length], 0, config.maxSampleLength - s->length);
 
 	free(tmpBuf);
 
@@ -1786,9 +1797,9 @@
 	if (playWaveformFlag)
 	{
 		ch->n_start = &song->sampleData[s->offset];
-		ch->n_length = (s->loopStart > 0) ? (uint32_t)(s->loopStart + s->loopLength) >> 1 : s->length >> 1;
+		ch->n_length = (uint16_t)((s->loopStart > 0) ? (s->loopStart + s->loopLength) >> 1 : s->length >> 1);
 		ch->n_loopstart = &song->sampleData[s->offset + s->loopStart];
-		ch->n_replen = s->loopLength >> 1;
+		ch->n_replen = (uint16_t)(s->loopLength >> 1);
 	}
 	else
 	{
@@ -2357,9 +2368,9 @@
 		{
 			sampler.lastMouseX = mouseX;
 
-			tmpPos = (scr2SmpPos(mouseX - 1) - s->loopStart) & 0xFFFFFFFE;
-			if (tmpPos > MAX_SAMPLE_LEN)
-				tmpPos = MAX_SAMPLE_LEN;
+			tmpPos = (scr2SmpPos(mouseX - 1) - s->loopStart) & ~1;
+			if (tmpPos > config.maxSampleLength)
+				tmpPos = config.maxSampleLength;
 
 			if (s->loopStart+tmpPos >= (s->loopStart+s->loopLength)-2)
 			{
@@ -2368,10 +2379,10 @@
 			}
 			else
 			{
-				s->loopStart = (uint16_t)(s->loopStart + tmpPos);
+				s->loopStart = s->loopStart + tmpPos;
 
 				if (s->loopLength-tmpPos > 2)
-					s->loopLength -= (uint16_t)tmpPos;
+					s->loopLength -= tmpPos;
 				else
 					s->loopLength = 2;
 			}
@@ -2395,10 +2406,10 @@
 
 			s = &song->samples[editor.currSample];
 
-			tmpPos = (scr2SmpPos(mouseX - 4) - s->loopStart) & 0xFFFFFFFE;
-			tmpPos = CLAMP(tmpPos, 2, MAX_SAMPLE_LEN);
+			tmpPos = (scr2SmpPos(mouseX - 4) - s->loopStart) & ~1;
+			tmpPos = CLAMP(tmpPos, 2, config.maxSampleLength);
 
-			s->loopLength = (uint16_t)tmpPos;
+			s->loopLength = tmpPos;
 
 			ui.updateCurrSampleRepeat = true;
 			ui.updateCurrSampleReplen = true;
@@ -2445,7 +2456,7 @@
 			if (tmpPos > s->length)
 				tmpPos = s->length;
 
-			editor.samplePos = (uint16_t)tmpPos;
+			editor.samplePos = tmpPos;
 		}
 
 		updateSamplePos();
@@ -2457,7 +2468,7 @@
 
 	if (mouseX != sampler.lastSamPos)
 	{
-		sampler.lastSamPos = (uint16_t)mouseX;
+		sampler.lastSamPos = mouseX;
 
 		invertRange();
 		if (s->length == 0)
@@ -2494,7 +2505,7 @@
 		if (tmpPos > s->length)
 			tmpPos = s->length;
 
-		 editor.samplePos = (uint16_t)tmpPos;
+		 editor.samplePos = tmpPos;
 	}
 
 	updateSamplePos();
@@ -2539,8 +2550,8 @@
 		}
 		else
 		{
-			s->loopStart = (uint16_t)sampler.tmpLoopStart;
-			s->loopLength = (uint16_t)sampler.tmpLoopLength;
+			s->loopStart = sampler.tmpLoopStart;
+			s->loopLength = sampler.tmpLoopLength;
 
 			if (s->loopStart+s->loopLength > s->length)
 			{
--- a/src/pt2_sampler.h
+++ b/src/pt2_sampler.h
@@ -11,8 +11,7 @@
 	int16_t loopStartPos, loopEndPos;
 	uint16_t dragStart, dragEnd;
 	int32_t samPointWidth, samOffset, samDisplay, samLength, saveMouseX, lastSamPos;
-	int32_t lastMouseX, lastMouseY, tmpLoopStart, tmpLoopLength;
-	uint32_t copyBufSize;
+	int32_t lastMouseX, lastMouseY, tmpLoopStart, tmpLoopLength, copyBufSize;
 } sampler_t;
 
 extern sampler_t sampler; // pt2_sampler.c
--- a/src/pt2_sampling.c
+++ b/src/pt2_sampling.c
@@ -29,6 +29,7 @@
 #include "pt2_config.h"
 #include "pt2_sampling.h"
 #include "pt2_math.h" // PT2_PI
+#include "pt2_hpc.h"
 
 enum
 {
@@ -478,6 +479,7 @@
 {
 	changeStatusText("PLEASE WAIT ...");
 	flipFrame();
+	hpc_ResetEndTime(&video.vblankHpc);
 
 	editor.sampleZero = false;
 	editor.blockMarkFlag = false;
@@ -604,7 +606,7 @@
 
 	assert(roundedOutputFrequency > 0);
 
-	maxSamplingLength = (int32_t)(ceil(((double)MAX_SAMPLE_LEN*inputFrequency) / dOutputFrequency)) + 1;
+	maxSamplingLength = (int32_t)(ceil(((double)config.maxSampleLength*inputFrequency) / dOutputFrequency)) + 1;
 	
 	const int32_t allocLen = (SINC_TAPS/2) + maxSamplingLength + (SINC_TAPS/2);
 	dSamplingBufferOrig = (double *)malloc(allocLen * sizeof (double));
@@ -637,8 +639,8 @@
 	const double dRatio = dOutputFrequency / inputFrequency;
 	
 	int32_t writeLength = (int32_t)(readLength * dRatio);
-	if (writeLength > MAX_SAMPLE_LEN)
-		writeLength = MAX_SAMPLE_LEN;
+	if (writeLength > config.maxSampleLength)
+		writeLength = config.maxSampleLength;
 
 	double *dBuffer = (double *)malloc(writeLength * sizeof (double));
 	if (dBuffer == NULL)
@@ -726,7 +728,7 @@
 	}
 
 	moduleSample_t *s = &song->samples[editor.currSample];
-	s->length = (uint16_t)newLength;
+	s->length = newLength;
 	s->fineTune = samplingFinetune;
 	s->loopStart = 0;
 	s->loopLength = 2;
--- a/src/pt2_scopes.c
+++ b/src/pt2_scopes.c
@@ -11,12 +11,12 @@
 #include "pt2_tables.h"
 #include "pt2_structs.h"
 #include "pt2_config.h"
+#include "pt2_hpc.h"
 
 // this uses code that is not entirely thread safe, but I have never had any issues so far...
 
 static volatile bool scopesUpdatingFlag, scopesDisplayingFlag;
-static uint32_t scopeTimeLen, scopeTimeLenFrac;
-static uint64_t timeNext64, timeNext64Frac;
+static hpc_t scopeHpc;
 static SDL_Thread *scopeThread;
 
 scope_t scope[AMIGA_VOICES]; // global
@@ -36,7 +36,7 @@
 {
 	assert(song != NULL);
 	const int8_t *sampleData = song->sampleData;
-	const int32_t sampleSlotSize = MAX_SAMPLE_LEN;
+	const int32_t sampleSlotSize = config.maxSampleLength;
 
 	if (sampleData == NULL) // shouldn't really happen, but just in case
 		return -1;
@@ -118,7 +118,7 @@
 
 	const int8_t *newData = tempState.newData;
 	if (newData == NULL)
-		newData = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample
+		newData = &song->sampleData[config.reservedSampleOffset]; // 128K reserved sample
 
 	int32_t newLength = tempState.newLength; // in bytes, not words
 	if (newLength < 2)
@@ -320,9 +320,8 @@
 	// this is needed for scope stability (confirmed)
 	SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
 
-	// set next frame time
-	timeNext64 = SDL_GetPerformanceCounter() + scopeTimeLen;
-	timeNext64Frac = scopeTimeLenFrac;
+	hpc_SetDurationInHz(&scopeHpc, SCOPE_HZ);
+	hpc_ResetEndTime(&scopeHpc);
 
 	while (editor.programRunning)
 	{
@@ -331,31 +330,7 @@
 
 		updateScopes();
 
-		uint64_t time64 = SDL_GetPerformanceCounter();
-		if (time64 < timeNext64)
-		{
-			time64 = timeNext64 - time64;
-			if (time64 > UINT32_MAX)
-				time64 = UINT32_MAX;
-
-			const uint32_t diff32 = (uint32_t)time64;
-
-			// convert to microseconds and round to integer
-			const int32_t time32 = (int32_t)((diff32 * editor.dPerfFreqMulMicro) + 0.5);
-
-			// delay until we have reached the next frame
-			if (time32 > 0)
-				usleep(time32);
-		}
-
-		// update next tick time
-		timeNext64 += scopeTimeLen;
-		timeNext64Frac += scopeTimeLenFrac;
-		if (timeNext64Frac > 0xFFFFFFFF)
-		{
-			timeNext64Frac &= 0xFFFFFFFF;
-			timeNext64++;
-		}
+		hpc_Wait(&scopeHpc);
 	}
 
 	(void)ptr;
@@ -364,18 +339,6 @@
 
 bool initScopes(void)
 {
-	double dInt, dFrac;
-
-	// calculate scope time for performance counters and split into int/frac
-	dFrac = modf(editor.dPerfFreq / SCOPE_HZ, &dInt);
-
-	// integer part
-	scopeTimeLen = (int32_t)dInt;
-
-	// fractional part (scaled to 0..2^32-1)
-	dFrac *= UINT32_MAX+1.0;
-	scopeTimeLenFrac = (uint32_t)dFrac;
-
 	resetCachedScopePeriod();
 
 	scopeThread = SDL_CreateThread(scopeThreadFunc, NULL, NULL);
--- a/src/pt2_structs.h
+++ b/src/pt2_structs.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "pt2_header.h"
+#include "pt2_hpc.h"
 
 // for .WAV sample loading/saving
 typedef struct wavHeader_t
@@ -57,12 +58,11 @@
 typedef struct moduleSample_t
 {
 	volatile int8_t *volumeDisp;
-	volatile uint16_t *lengthDisp, *loopStartDisp, *loopLengthDisp;
+	volatile int32_t *lengthDisp, *loopStartDisp, *loopLengthDisp;
 	char text[22 + 1];
 	int8_t volume;
 	uint8_t fineTune;
-	uint16_t length, loopStart, loopLength;
-	int32_t offset;
+	int32_t offset, length, loopStart, loopLength;
 } moduleSample_t;
 
 typedef struct moduleChannel_t
@@ -131,6 +131,7 @@
 	int32_t renderX, renderY, renderW, renderH, displayW, displayH;
 	int32_t xScale, yScale;
 	float fMouseXMul, fMouseYMul;
+	hpc_t vblankHpc;
 	SDL_PixelFormat *pixelFormat;
 	uint32_t *frameBuffer, *frameBufferUnaligned;
 
@@ -153,7 +154,7 @@
 	volatile uint16_t *quantizeValueDisp, *metroSpeedDisp, *metroChannelDisp, *sampleVolDisp;
 	volatile uint16_t *vol1Disp, *vol2Disp, *currEditPatternDisp, *currPosDisp, *currPatternDisp;
 	volatile uint16_t *currPosEdPattDisp, *currLengthDisp, *lpCutOffDisp, *hpCutOffDisp;
-	volatile uint16_t *samplePosDisp, *chordLengthDisp;
+	volatile int32_t *samplePosDisp, *chordLengthDisp;
 
 	char mixText[16];
 	char *entryNameTmp, *currPath, *dropTempFileName;
@@ -174,13 +175,12 @@
 	uint8_t tuningNote, resampleNote, initialTempo, initialSpeed, editMoveAdd;
 
 	int16_t modulateSpeed;
-	uint16_t metroSpeed, metroChannel, sampleVol, samplePos, chordLength;
+	uint16_t metroSpeed, metroChannel, sampleVol;
 	uint16_t effectMacros[10], currPlayNote, vol1, vol2, lpCutOff, hpCutOff;
-	uint16_t smpRedoLoopStarts[MOD_SAMPLES], smpRedoLoopLengths[MOD_SAMPLES], smpRedoLengths[MOD_SAMPLES];
-	int32_t oldTempo, modulatePos, modulateOffset, markStartOfs, markEndOfs, pat2SmpPos;
-	uint32_t vblankTimeLen, vblankTimeLenFrac;
+	int32_t smpRedoLoopStarts[MOD_SAMPLES], smpRedoLoopLengths[MOD_SAMPLES], smpRedoLengths[MOD_SAMPLES];
+	int32_t oldTempo, modulatePos, modulateOffset, markStartOfs, markEndOfs, pat2SmpPos, samplePos, chordLength;
 	uint64_t musicTime64;
-	double dPerfFreq, dPerfFreqMulMicro, *dPat2SmpBuf;
+	double *dPat2SmpBuf;
 	note_t trackBuffer[MOD_ROWS], cmdsBuffer[MOD_ROWS], blockBuffer[MOD_ROWS];
 	note_t patternBuffer[MOD_ROWS * AMIGA_VOICES], undoBuffer[MOD_ROWS * AMIGA_VOICES];
 	SDL_Thread *mod2WavThread, *pat2SmpThread;
@@ -214,6 +214,7 @@
 
 	bool changingSamplingNote;
 
+	bool force32BitNumPtr;
 	int8_t *numPtr8, tmpDisp8, pointerMode, editOpScreen, editTextType, askScreenType;
 	int8_t visualizerMode, previousPointerMode, forceVolDrag, changingChordNote;
 	uint8_t numLen, numBits;
@@ -230,10 +231,10 @@
 	bool updateMixText, updatePosText, updateModText, updateVolText;
 
 	// edit op. #4 (sample chord editor)
-	bool updateLengthText, updateNote1Text, updateNote2Text;
-	bool updateNote3Text, updateNote4Text;
+	bool updateChordLengthText, updateChordNote1Text, updateChordNote2Text;
+	bool updateChordNote3Text, updateChordNote4Text;
 
-	//sampler
+	// sampler
 	bool updateResampleNote, updateVolFromText, updateVolToText, updateLPText;
 	bool updateHPText, updateNormFlag, update9xxPos;
 
@@ -256,7 +257,7 @@
 	int16_t lineCurX, lineCurY, editObject, sampleMarkingPos;
 	uint16_t *numPtr16, tmpDisp16, *dstOffset, dstPos, textLength, editTextPos;
 	uint16_t dstOffsetEnd, lastSampleOffset, diskOpPathTextOffset;
-	int32_t askTempData;
+	int32_t askTempData, *numPtr32, tmpDisp32;
 } ui_t;
 
 extern keyb_t keyb;
--- a/src/pt2_tables.c
+++ b/src/pt2_tables.c
@@ -265,15 +265,15 @@
 	{ 98, 55,108, 65, PTB_SVOLUMEU},
 	{109, 55,119, 65, PTB_SVOLUMED},
 
-	{ 62, 66, 97, 76, PTB_SLENGTHS},
+	{ 54, 66, 97, 76, PTB_SLENGTHS},
 	{ 98, 66,108, 76, PTB_SLENGTHU},
 	{109, 66,119, 76, PTB_SLENGTHD},
 
-	{ 62, 77, 97, 87, PTB_SREPEATS},
+	{ 54, 77, 97, 87, PTB_SREPEATS},
 	{ 98, 77,108, 87, PTB_SREPEATU},
 	{109, 77,119, 87, PTB_SREPEATD},
 
-	{ 62, 88, 97, 98, PTB_SREPLENS},
+	{ 54, 88, 97, 98, PTB_SREPLENS},
 	{ 98, 88,108, 98, PTB_SREPLENU},
 	{109, 88,119, 98, PTB_SREPLEND}
 };
@@ -395,8 +395,8 @@
 
 	{120, 55,165, 65, PTB_EO_MIX},
 	{166, 55,212, 65, PTB_EO_ECHO},
-	{213, 55,243, 65, PTB_DUMMY},
-	{244, 55,283, 65, PTB_EO_POS_NUM},
+	{213, 55,237, 65, PTB_DUMMY},
+	{238, 55,283, 65, PTB_EO_POS_NUM},
 	{284, 55,294, 65, PTB_EO_POS_UP},
 	{295, 55,305, 65, PTB_EO_POS_DOWN},
 	{306, 55,319, 65, PTB_EO_1},
@@ -454,8 +454,8 @@
 	{295, 77,305, 87, PTB_EO_NOTE3_DOWN},
 	{306, 77,319, 87, PTB_EO_3},
 
-	{120, 88,157, 98, PTB_EO_LENGTH},
-	{158, 88,204, 98, PTB_DUMMY},
+	{120, 88,165, 98, PTB_EO_LENGTH},
+	{166, 88,204, 98, PTB_DUMMY},
 	{205, 88,251, 98, PTB_EO_MINOR6},
 	{251, 88,283, 98, PTB_EO_NOTE4},
 	{284, 88,294, 98, PTB_EO_NOTE4_UP},
--- a/src/pt2_textout.h
+++ b/src/pt2_textout.h
@@ -34,11 +34,12 @@
 void printFiveHex(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
 void printOneHexBig(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
 void printTwoHexBig(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
-void printSixDecimals(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
+
 void printTwoDecimals(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
+void printThreeDecimals(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
 void printFourDecimals(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
 void printFiveDecimals(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
-void printThreeDecimals(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
+void printSixDecimals(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
 void printTwoDecimalsBig(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor);
 void printOneHexBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
 void printTwoHexBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
@@ -49,9 +50,9 @@
 void printTwoHexBigBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
 void printSixDecimalsBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
 void printTwoDecimalsBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
+void printThreeDecimalsBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
 void printFourDecimalsBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
 void printFiveDecimalsBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
-void printThreeDecimalsBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
 void printTwoDecimalsBigBg(uint32_t x, uint32_t y, uint32_t value, uint32_t fontColor, uint32_t backColor);
 
 void setPrevStatusMessage(void);
--- a/src/pt2_visuals.c
+++ b/src/pt2_visuals.c
@@ -42,6 +42,7 @@
 #include "pt2_bmp.h"
 #include "pt2_sampling.h"
 #include "pt2_chordmaker.h"
+#include "pt2_hpc.h"
 
 typedef struct sprite_t
 {
@@ -53,7 +54,6 @@
 } sprite_t;
 
 static uint32_t vuMetersBg[4 * (10 * 48)];
-static uint64_t timeNext64, timeNext64Frac;
 
 sprite_t sprites[SPRITE_NUM]; // globalized
 
@@ -200,64 +200,6 @@
 	displayErrorMsg("NOT SAMPLE 0 !");
 }
 
-void setupPerfFreq(void)
-{
-	uint64_t perfFreq64;
-	double dInt, dFrac;
-
-	perfFreq64 = SDL_GetPerformanceFrequency(); assert(perfFreq64 != 0);
-	editor.dPerfFreq = (double)perfFreq64;
-	editor.dPerfFreqMulMicro = 1000000.0 / editor.dPerfFreq;
-
-	// calculate vblank time for performance counters and split into int/frac
-	dFrac = modf(editor.dPerfFreq / VBLANK_HZ, &dInt);
-
-	// integer part
-	editor.vblankTimeLen = (int32_t)dInt;
-
-	// fractional part (scaled to 0..2^32-1)
-	dFrac *= UINT32_MAX+1.0;
-	editor.vblankTimeLenFrac = (uint32_t)dFrac;
-}
-
-void setupWaitVBL(void)
-{
-	// set next frame time
-	timeNext64 = SDL_GetPerformanceCounter() + editor.vblankTimeLen;
-	timeNext64Frac = editor.vblankTimeLenFrac;
-}
-
-void waitVBL(void)
-{
-	// this routine almost never delays if we have 60Hz vsync, but it's still needed in some occasions
-
-	uint64_t time64 = SDL_GetPerformanceCounter();
-	if (time64 < timeNext64)
-	{
-		time64 = timeNext64 - time64;
-		if (time64 > UINT32_MAX)
-			time64 = UINT32_MAX;
-
-		const uint32_t diff32 = (uint32_t)time64;
-
-		// convert to microseconds and round to integer
-		const int32_t time32 = (int32_t)((diff32 * editor.dPerfFreqMulMicro) + 0.5);
-
-		// delay until we have reached the next frame
-		if (time32 > 0)
-			usleep(time32);
-	}
-
-	// update next tick time
-	timeNext64 += editor.vblankTimeLen;
-	timeNext64Frac += editor.vblankTimeLenFrac;
-	if (timeNext64Frac > 0xFFFFFFFF)
-	{
-		timeNext64Frac &= 0xFFFFFFFF;
-		timeNext64++;
-	}
-}
-
 void renderFrame(void)
 {
 	updateMOD2WAVDialog(); // must be first to avoid flickering issues
@@ -491,19 +433,30 @@
 	{
 		ui.updateCurrSampleLength = false;
 		if (!editor.isWAVRendering)
-			printFourHexBg(64, 69, *currSample->lengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		{
+			if (config.maxSampleLength == 0xFFFE)
+				printFourHexBg(64, 69, *currSample->lengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+			else
+				printFiveHexBg(56, 69, *currSample->lengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		}
 	}
 
 	if (ui.updateCurrSampleRepeat)
 	{
 		ui.updateCurrSampleRepeat = false;
-		printFourHexBg(64, 80, *currSample->loopStartDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		if (config.maxSampleLength == 0xFFFE)
+			printFourHexBg(64, 80, *currSample->loopStartDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		else
+			printFiveHexBg(56, 80, *currSample->loopStartDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 	}
 
 	if (ui.updateCurrSampleReplen)
 	{
 		ui.updateCurrSampleReplen = false;
-		printFourHexBg(64, 91, *currSample->loopLengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		if (config.maxSampleLength == 0xFFFE)
+			printFourHexBg(64, 91, *currSample->loopLengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		else
+			printFiveHexBg(56, 91, *currSample->loopLengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 	}
 }
 
@@ -712,9 +665,7 @@
 	{
 		if (!ui.samplerVolBoxShown && !ui.samplerFiltersBoxShown && s->length > 0)
 		{
-			tmpSampleOffset = (scr2SmpPos(mouse.x-3) + (1 << 7)) >> 8; // rounded
-			tmpSampleOffset = 0x900 + CLAMP(tmpSampleOffset, 0x00, 0xFF);
-
+			tmpSampleOffset = 0x900 + (scr2SmpPos(mouse.x-3) >> 8);
 			if (tmpSampleOffset != ui.lastSampleOffset)
 			{
 				ui.lastSampleOffset = (uint16_t)tmpSampleOffset;
@@ -727,7 +678,10 @@
 	if (ui.update9xxPos)
 	{
 		ui.update9xxPos = false;
-		printThreeHexBg(288, 247, ui.lastSampleOffset, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		if (ui.lastSampleOffset <= 0x900 || ui.lastSampleOffset > 0x9FF)
+			textOutBg(288, 247, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+		else
+			printThreeHexBg(288, 247, ui.lastSampleOffset, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 	}
 
 	if (ui.updateResampleNote)
@@ -1239,6 +1193,15 @@
 
 	blit32(120, 44, 200, 55, srcPtr);
 
+	// fix graphics in 128K sample mode
+	if (config.maxSampleLength != 65534)
+	{
+		if (ui.editOpScreen == 2)
+			blit32(213, 55, 32, 11, fix128KPosBMP);
+		else if (ui.editOpScreen == 3)
+			blit32(120, 88, 48, 11, fix128KChordBMP);
+	}
+
 	renderEditOpMode();
 
 	// render text and content
@@ -1271,11 +1234,11 @@
 	{
 		textOut(128, 47, " SAMPLE CHORD EDITOR  ", video.palette[PAL_GENTXT]);
 
-		ui.updateLengthText = true;
-		ui.updateNote1Text = true;
-		ui.updateNote2Text = true;
-		ui.updateNote3Text = true;
-		ui.updateNote4Text = true;
+		ui.updateChordLengthText = true;
+		ui.updateChordNote1Text = true;
+		ui.updateChordNote2Text = true;
+		ui.updateChordNote3Text = true;
+		ui.updateChordNote4Text = true;
 	}
 }
 
@@ -1414,7 +1377,10 @@
 		if (ui.updatePosText)
 		{
 			ui.updatePosText = false;
-			printFourHexBg(248, 58, *editor.samplePosDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+			if (config.maxSampleLength == 0xFFFE)
+				printFourHexBg(248, 58, *editor.samplePosDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+			else
+				printFiveHexBg(240, 58, *editor.samplePosDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 		}
 
 		if (ui.updateModText)
@@ -1438,14 +1404,18 @@
 	}
 	else if (ui.editOpScreen == 3)
 	{
-		if (ui.updateLengthText)
+		if (ui.updateChordLengthText)
 		{
-			ui.updateLengthText = false;
+			ui.updateChordLengthText = false;
 
 			// clear background
-			textOutBg(168, 91, "    ", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
-			charOut(198, 91,    ':', video.palette[PAL_GENBKG]);
+			if (config.maxSampleLength != 65534)
+				textOutBg(160, 91, "     ", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+			else
+				textOutBg(168, 91, "    ", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 
+			charOut(198, 91, ':', video.palette[PAL_GENBKG]);
+
 			if (song->samples[editor.currSample].loopLength > 2 || song->samples[editor.currSample].loopStart >= 2)
 			{
 				textOut(168, 91, "LOOP", video.palette[PAL_GENTXT]);
@@ -1452,14 +1422,18 @@
 			}
 			else
 			{
-				printFourHex(168, 91, *editor.chordLengthDisp, video.palette[PAL_GENTXT]); // chord max length
+				if (config.maxSampleLength == 0xFFFE)
+					printFourHex(168, 91, *editor.chordLengthDisp, video.palette[PAL_GENTXT]); // chord max length
+				else
+					printFiveHex(160, 91, *editor.chordLengthDisp, video.palette[PAL_GENTXT]); // chord max length
+
 				charOut(198, 91, (editor.chordLengthMin) ? '.' : ':', video.palette[PAL_GENTXT]); // min/max flag
 			}
 		}
 
-		if (ui.updateNote1Text)
+		if (ui.updateChordNote1Text)
 		{
-			ui.updateNote1Text = false;
+			ui.updateChordNote1Text = false;
 			if (editor.note1 > 35)
 				textOutBg(256, 58, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 			else
@@ -1467,9 +1441,9 @@
 					video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 		}
 
-		if (ui.updateNote2Text)
+		if (ui.updateChordNote2Text)
 		{
-			ui.updateNote2Text = false;
+			ui.updateChordNote2Text = false;
 			if (editor.note2 > 35)
 				textOutBg(256, 69, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 			else
@@ -1477,9 +1451,9 @@
 					video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 		}
 
-		if (ui.updateNote3Text)
+		if (ui.updateChordNote3Text)
 		{
-			ui.updateNote3Text = false;
+			ui.updateChordNote3Text = false;
 			if (editor.note3 > 35)
 				textOutBg(256, 80, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 			else
@@ -1487,9 +1461,9 @@
 					video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 		}
 			
-		if (ui.updateNote4Text)
+		if (ui.updateChordNote4Text)
 		{
-			ui.updateNote4Text = false;
+			ui.updateChordNote4Text = false;
 			if (editor.note4 > 35)
 				textOutBg(256, 91, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
 			else
@@ -1524,14 +1498,26 @@
 	if (ui.samplerScreenShown)
 	{
 		if (!ui.diskOpScreenShown)
+		{
 			blit32(0, 0, 320, 121, trackerFrameBMP);
+
+			if (config.maxSampleLength != 65534)
+				blit32(1, 65, 62, 34, fix128KTrackerBMP); // fix for 128kB support mode
+		}
 	}
 	else
 	{
 		if (!ui.diskOpScreenShown)
+		{
 			blit32(0, 0, 320, 255, trackerFrameBMP);
+
+			if (config.maxSampleLength != 65534)
+				blit32(1, 65, 62, 34, fix128KTrackerBMP); // fix for 128kB support mode
+		}
 		else
+		{
 			blit32(0, 121, 320, 134, &trackerFrameBMP[121 * SCREEN_W]);
+		}
 
 		ui.updateSongBPM = true;
 		ui.updateCurrPattText = true;
@@ -1759,7 +1745,7 @@
 			s->loopLength = 2;
 
 			memset(s->text, 0, sizeof (s->text));
-			memset(&song->sampleData[(editor.currSample * MAX_SAMPLE_LEN)], 0, MAX_SAMPLE_LEN);
+			memset(&song->sampleData[(editor.currSample * config.maxSampleLength)], 0, config.maxSampleLength);
 
 			editor.samplePos = 0;
 			updateCurrSample();
@@ -2121,7 +2107,8 @@
 
 	if (!video.vsync60HzPresent)
 	{
-		waitVBL(); // we have no VSync, do crude thread sleeping to sync to ~60Hz
+		// we have no VSync, do crude thread sleeping to sync to ~60Hz
+		hpc_Wait(&video.vblankHpc);
 	}
 	else
 	{
@@ -2131,14 +2118,14 @@
 #ifdef __APPLE__
 		// macOS: VSync gets disabled if the window is 100% covered by another window. Let's add a (crude) fix:
 		if (minimized || !(windowFlags & SDL_WINDOW_INPUT_FOCUS))
-			waitVBL();
+			hpc_Wait(&video.vblankHpc);
 #elif __unix__
 		// *NIX: VSync gets disabled in fullscreen mode (at least on some distros/systems). Let's add a fix:
 		if (minimized || video.fullscreen)
-			waitVBL();
+			hpc_Wait(&video.vblankHpc);
 #else
 		if (minimized)
-			waitVBL();
+			hpc_Wait(&video.vblankHpc);
 #endif
 	}
 }
--- a/src/pt2_visuals.h
+++ b/src/pt2_visuals.h
@@ -3,6 +3,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "pt2_header.h"
+#include "pt2_hpc.h"
 
 enum
 {
@@ -35,9 +36,6 @@
 void statusNotSampleZero(void);
 void changeStatusText(const char *text);
 
-void setupPerfFreq(void);
-void setupWaitVBL(void);
-void waitVBL(void);
 void resetAllScreens(void);
 void handleAskNo(void);
 void handleAskYes(void);
--- a/vs2019_project/pt2-clone/protracker.ini
+++ b/vs2019_project/pt2-clone/protracker.ini
@@ -11,6 +11,25 @@
 ; ENTRY=VALUE (only strings can have spaces!)
 ;
 
+[SAMPLE LENGTH LIMIT]
+; Limit samples to 64kB (65534/$FFFE), like intended in ProTracker
+;        Syntax: TRUE or FALSE
+; Default value: TRUE
+;       Comment: Setting it to FALSE will remove the 64kB (65534/$FFFE) sample
+;         length limit and support 128kB samples (131070/$1FFFE).
+;         Keep in mind that >64kB samples are not officially supported in most
+;         ProTracker trackers and replayers, and will often result in bugs.
+;
+;         CAUTION: The 9xx command (Set Sample Offset) doesn't work at all if
+;                  the sample length is above 65534/$FFFE. This is a known bug
+;                  in all ProTracker versions. I am not fixing this bug as I
+;                  want to keep the ProTracker 2.3D replayer quirks.
+;
+;         Please do not change this setting unless you're aware of the problems
+;         you might face when creating >64k-sample PT MODs!
+;
+64K_LIMIT=TRUE
+
 [VIDEO SETTINGS]
 ; Video scaling factor
 ;        Syntax: 1X, 2X, 3X ... 9X
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj
@@ -246,6 +246,7 @@
     <ClInclude Include="..\..\src\pt2_edit.h" />
     <ClInclude Include="..\..\src\pt2_header.h" />
     <ClInclude Include="..\..\src\pt2_helpers.h" />
+    <ClInclude Include="..\..\src\pt2_hpc.h" />
     <ClInclude Include="..\..\src\pt2_keyboard.h" />
     <ClInclude Include="..\..\src\pt2_ledfilter.h" />
     <ClInclude Include="..\..\src\pt2_math.h" />
@@ -297,6 +298,7 @@
     <ClCompile Include="..\..\src\pt2_diskop.c" />
     <ClCompile Include="..\..\src\pt2_edit.c" />
     <ClCompile Include="..\..\src\pt2_helpers.c" />
+    <ClCompile Include="..\..\src\pt2_hpc.c" />
     <ClCompile Include="..\..\src\pt2_keyboard.c" />
     <ClCompile Include="..\..\src\pt2_ledfilter.c" />
     <ClCompile Include="..\..\src\pt2_main.c" />
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj.filters
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj.filters
@@ -105,6 +105,9 @@
     <ClInclude Include="..\..\src\pt2_math.h">
       <Filter>headers</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\pt2_hpc.h">
+      <Filter>headers</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\pt2_audio.c" />
@@ -194,6 +197,7 @@
     <ClCompile Include="..\..\src\pt2_chordmaker.c" />
     <ClCompile Include="..\..\src\pt2_downsample2x.c" />
     <ClCompile Include="..\..\src\pt2_math.c" />
+    <ClCompile Include="..\..\src\pt2_hpc.c" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\src\pt2-clone.rc" />