diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 848eb8d3eaa0de56d10b0276516d97751513c3dd..93083f3694586747e80a1a8c3d9fba5597c3f704 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -128,6 +128,7 @@ add_definitions ( -DUSE_CMAKE_CONFIG_H )
 if(WIN32)
 	# Windows
 	if(MSVC) # MSVC Specifics
+		set(PLATFORM_LIBS dbghelp.lib ${PLATFORM_LIBS})
 		# Surpress some useless warnings
 		add_definitions ( /D "_CRT_SECURE_NO_DEPRECATE" /W1 )
 	else() # Probably MinGW = GCC
@@ -630,11 +631,11 @@ if(MSVC)
 	#set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/LTCG /NODEFAULTLIB:\"libcmtd.lib\" /NODEFAULTLIB:\"libcmt.lib\"")
 	set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/LTCG")
 
-	set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MDd /Zi /Ob0 /O1 /RTC1 /Wall")
+	set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MDd /Zi /Ob0 /O1 /RTC1")
 
 	# Debug build doesn't catch exceptions by itself
 	# Add some optimizations because otherwise it's VERY slow
-	set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Zi /Ob0 /Od /RTC1 /Wall")
+	set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Zi /Ob0 /Od /RTC1")
 
 	# Flags for C files (sqlite)
 	# /MT = Link statically with standard library stuff
diff --git a/src/debug.cpp b/src/debug.cpp
index 3b2fb641a132eb76b34449b64f29ac536c561c28..bd970a8e4b953de49265dad7d5f7e99b75a51497 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -29,6 +29,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "jthread/jmutex.h"
 #include "jthread/jmutexautolock.h"
 #include "config.h"
+
+#ifdef _MSC_VER
+	#include <dbghelp.h>
+	#include "version.h"
+	#include "filesys.h"
+#endif
+
 /*
 	Debug output
 */
@@ -57,7 +64,7 @@ void debugstreams_init(bool disable_stderr, const char *filename)
 
 	if(filename)
 		g_debugstreams[1] = fopen(filename, "a");
-		
+
 	if(g_debugstreams[1])
 	{
 		fprintf(g_debugstreams[1], "\n\n-------------\n");
@@ -91,7 +98,7 @@ class Debugbuf : public std::streambuf
 			//TODO: Is this slow?
 			fflush(g_debugstreams[i]);
 		}
-		
+
 		return c;
 	}
 	std::streamsize xsputn(const char *s, std::streamsize n)
@@ -111,7 +118,7 @@ class Debugbuf : public std::streambuf
 
 		return n;
 	}
-	
+
 private:
 	bool m_disable_stderr;
 };
@@ -133,7 +140,7 @@ void assert_fail(const char *assertion, const char *file,
 			"%s:%u: %s: Assertion '%s' failed.\n",
 			(unsigned long)get_current_thread_id(),
 			file, line, function, assertion);
-	
+
 	debug_stacks_print();
 
 	if(g_debugstreams[1])
@@ -151,7 +158,7 @@ struct DebugStack
 	DebugStack(threadid_t id);
 	void print(FILE *file, bool everything);
 	void print(std::ostream &os, bool everything);
-	
+
 	threadid_t threadid;
 	char stack[DEBUG_STACK_SIZE][DEBUG_STACK_TEXT_SIZE];
 	int stack_i; // Points to the lowest empty position
@@ -285,10 +292,10 @@ DebugStacker::DebugStacker(const char *text)
 DebugStacker::~DebugStacker()
 {
 	JMutexAutoLock lock(g_debug_stacks_mutex);
-	
+
 	if(m_overflowed == true)
 		return;
-	
+
 	m_stack->stack_i--;
 
 	if(m_stack->stack_i == 0)
@@ -301,35 +308,124 @@ DebugStacker::~DebugStacker()
 	}
 }
 
-
 #ifdef _MSC_VER
-#if CATCH_UNHANDLED_EXCEPTIONS == 1
-void se_trans_func(unsigned int u, EXCEPTION_POINTERS* pExp)
+
+const char *Win32ExceptionCodeToString(DWORD exception_code)
 {
-	dstream<<"In trans_func.\n";
-	if(u == EXCEPTION_ACCESS_VIOLATION)
-	{
-		PEXCEPTION_RECORD r = pExp->ExceptionRecord;
-		dstream<<"Access violation at "<<r->ExceptionAddress
-				<<" write?="<<r->ExceptionInformation[0]
-				<<" address="<<r->ExceptionInformation[1]
-				<<std::endl;
-		throw FatalSystemException
-		("Access violation");
-	}
-	if(u == EXCEPTION_STACK_OVERFLOW)
-	{
-		throw FatalSystemException
-		("Stack overflow");
-	}
-	if(u == EXCEPTION_ILLEGAL_INSTRUCTION)
-	{
-		throw FatalSystemException
-		("Illegal instruction");
+	switch (exception_code) {
+	case EXCEPTION_ACCESS_VIOLATION:
+		return "Access violation";
+	case EXCEPTION_DATATYPE_MISALIGNMENT:
+		return "Misaligned data access";
+	case EXCEPTION_BREAKPOINT:
+		return "Breakpoint reached";
+	case EXCEPTION_SINGLE_STEP:
+		return "Single debug step";
+	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+		return "Array access out of bounds";
+	case EXCEPTION_FLT_DENORMAL_OPERAND:
+		return "Denormal floating point operand";
+	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+		return "Floating point division by zero";
+	case EXCEPTION_FLT_INEXACT_RESULT:
+		return "Inaccurate floating point result";
+	case EXCEPTION_FLT_INVALID_OPERATION:
+		return "Invalid floating point operation";
+	case EXCEPTION_FLT_OVERFLOW:
+		return "Floating point exponent overflow";
+	case EXCEPTION_FLT_STACK_CHECK:
+		return "Floating point stack overflow or underflow";
+	case EXCEPTION_FLT_UNDERFLOW:
+		return "Floating point exponent underflow";
+	case EXCEPTION_INT_DIVIDE_BY_ZERO:
+		return "Integer division by zero";
+	case EXCEPTION_INT_OVERFLOW:
+		return "Integer overflow";
+	case EXCEPTION_PRIV_INSTRUCTION:
+		return "Privileged instruction executed";
+	case EXCEPTION_IN_PAGE_ERROR:
+		return "Could not access or load page";
+	case EXCEPTION_ILLEGAL_INSTRUCTION:
+		return "Illegal instruction encountered";
+	case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+		return "Attempted to continue after fatal exception";
+	case EXCEPTION_STACK_OVERFLOW:
+		return "Stack overflow";
+	case EXCEPTION_INVALID_DISPOSITION:
+		return "Invalid disposition returned to the exception dispatcher";
+	case EXCEPTION_GUARD_PAGE:
+		return "Attempted guard page access";
+	case EXCEPTION_INVALID_HANDLE:
+		return "Invalid handle";
 	}
+
+	return "Unknown exception";
 }
-#endif
-#endif
 
+long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
+{
+	char buf[512];
+	MINIDUMP_EXCEPTION_INFORMATION mdei;
+	MINIDUMP_USER_STREAM_INFORMATION mdusi;
+	MINIDUMP_USER_STREAM mdus;
+	bool minidump_created = false;
+	std::string version_str("Minetest ");
+
+	std::string dumpfile = porting::path_user + DIR_DELIM "minetest.dmp";
+
+	HANDLE hFile = CreateFileA(dumpfile.c_str(), GENERIC_WRITE,
+		FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+	if (hFile == INVALID_HANDLE_VALUE)
+		goto minidump_failed;
+
+	if (SetEndOfFile(hFile) == FALSE)
+		goto minidump_failed;
+
+	mdei.ClientPointers	   = NULL;
+	mdei.ExceptionPointers = pExceptInfo;
+	mdei.ThreadId		   = GetCurrentThreadId();
+
+	version_str += minetest_version_hash;
+
+	mdus.Type       = CommentStreamA;
+	mdus.BufferSize = version_str.size();
+	mdus.Buffer     = (PVOID)version_str.c_str();
+
+	mdusi.UserStreamArray = &mdus;
+	mdusi.UserStreamCount = 1;
+
+	if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
+			MiniDumpNormal, &mdei, &mdusi, NULL) == FALSE)
+		goto minidump_failed;
+
+	minidump_created = true;
+
+minidump_failed:
+
+	CloseHandle(hFile);
 
+	DWORD excode = pExceptInfo->ExceptionRecord->ExceptionCode;
+	_snprintf(buf, sizeof(buf),
+		" >> === FATAL ERROR ===\n"
+		" >> %s (Exception 0x%08X) at 0x%p\n",
+		Win32ExceptionCodeToString(excode), excode,
+		pExceptInfo->ExceptionRecord->ExceptionAddress);
+	dstream << buf;
+
+	if (minidump_created)
+		dstream << " >> Saved dump to " << dumpfile << std::endl;
+	else
+		dstream << " >> Failed to save dump" << std::endl;
+
+	return EXCEPTION_EXECUTE_HANDLER;
+}
+
+#endif
+
+void debug_set_exception_handler()
+{
+#ifdef _MSC_VER
+	SetUnhandledExceptionFilter(Win32ExceptionHandler);
+#endif
+}
 
diff --git a/src/debug.h b/src/debug.h
index ba2e8704ebd1cc7c892dcec67618c6009f75035a..1027fde695b89703b84ef6a6e167874e0d69f465 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -95,6 +95,8 @@ __NORETURN extern void assert_fail(
 
 #define assert(expr) ASSERT(expr)
 
+void debug_set_exception_handler();
+
 /*
 	DebugStack
 */
@@ -118,13 +120,12 @@ class DebugStacker
 	bool m_overflowed;
 };
 
-#define DSTACK(msg)\
+#define DSTACK(msg) \
 	DebugStacker __debug_stacker(msg);
 
-#define DSTACKF(...)\
-	char __buf[DEBUG_STACK_TEXT_SIZE];\
-	snprintf(__buf,\
-			DEBUG_STACK_TEXT_SIZE, __VA_ARGS__);\
+#define DSTACKF(...) \
+	char __buf[DEBUG_STACK_TEXT_SIZE];                   \
+	snprintf(__buf, DEBUG_STACK_TEXT_SIZE, __VA_ARGS__); \
 	DebugStacker __debug_stacker(__buf);
 
 /*
@@ -132,34 +133,13 @@ class DebugStacker
 */
 
 #if CATCH_UNHANDLED_EXCEPTIONS == 1
-	#define BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER try{
-	#define END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)\
-		}catch(std::exception &e){\
-			logstream<<"ERROR: An unhandled exception occurred: "\
-					<<e.what()<<std::endl;\
-			assert(0);\
+	#define BEGIN_DEBUG_EXCEPTION_HANDLER try {
+	#define END_DEBUG_EXCEPTION_HANDLER(logstream) \
+		} catch (std::exception &e) {                               \
+			logstream << "ERROR: An unhandled exception occurred: " \
+				<< e.what() << std::endl;                           \
+			assert(0);                                              \
 		}
-	#ifdef _WIN32 // Windows
-		#ifdef _MSC_VER // MSVC
-void se_trans_func(unsigned int, EXCEPTION_POINTERS*);
-			#define BEGIN_DEBUG_EXCEPTION_HANDLER \
-				BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER\
-				_set_se_translator(se_trans_func);
-
-			#define END_DEBUG_EXCEPTION_HANDLER(logstream) \
-				END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
-		#else // Probably mingw
-			#define BEGIN_DEBUG_EXCEPTION_HANDLER\
-				BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER
-			#define END_DEBUG_EXCEPTION_HANDLER(logstream)\
-				END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
-		#endif
-	#else // Posix
-		#define BEGIN_DEBUG_EXCEPTION_HANDLER\
-			BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER
-		#define END_DEBUG_EXCEPTION_HANDLER(logstream)\
-			END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
-	#endif
 #else
 	// Dummy ones
 	#define BEGIN_DEBUG_EXCEPTION_HANDLER
diff --git a/src/exceptions.h b/src/exceptions.h
index abd8c68ae31ee9b0917eb8fd529e6b826a425f16..5b716c2ca862691fdf195b946e43fded2dddf62e 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -110,12 +110,6 @@ class ServerError : public BaseException {
 	ServerError(const std::string &s): BaseException(s) {}
 };
 
-// Only used on Windows (SEH)
-class FatalSystemException : public BaseException {
-public:
-	FatalSystemException(const std::string &s): BaseException(s) {}
-};
-
 class ClientStateError : public BaseException {
 public:
 	ClientStateError(std::string s): BaseException(s) {}
diff --git a/src/main.cpp b/src/main.cpp
index 173050fed3c88e2ebeec49cac08418904d2fab2e..e7108a3e8e5b0e66e97e541d2ad3b9ab1ffdd76b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -780,6 +780,8 @@ int main(int argc, char *argv[])
 {
 	int retval;
 
+	debug_set_exception_handler();
+
 	log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
 	log_add_output_all_levs(&main_dstream_no_stderr_log_out);
 
diff --git a/src/porting.h b/src/porting.h
index e6574494a28753d4dda16efe759af0349f512809..8387453e8de589920ea336d21eee5ce3a66176f2 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -419,6 +419,10 @@ inline const char * getPlatformName()
 void setXorgClassHint(const video::SExposedVideoData &video_data,
 	const std::string &name);
 
+// This only needs to be called at the start of execution, since all future
+// threads in the process inherit this exception handler
+void setWin32ExceptionHandler();
+
 } // namespace porting
 
 #ifdef __ANDROID__