diff --git a/build-scripts/makedep.sh b/build-scripts/makedep.sh
index 3b3863b4c..7e7897480 100755
--- a/build-scripts/makedep.sh
+++ b/build-scripts/makedep.sh
@@ -42,13 +42,38 @@ for src in $SOURCES
 do  echo "Generating dependencies for $src"
     ext=`echo $src | sed 's|.*\.\(.*\)|\1|'`
     obj=`echo $src | sed "s|^.*/\([^ ]*\)\..*|\1.lo|g"`
-    echo "\$(objects)/$obj: $src \\" >>${output}.new
-
+    if test x"$ext" != x"pica"; then
+      echo "\$(objects)/$obj: $src \\" >>${output}.new
+    else
+      stem=`echo $src | sed "s|^.*/\([^ ]*\)\..*|\1|g"`
+      obj=$stem.shbin.o
+      hdr=${stem}_shbin.h
+      shbin=$stem.shbin
+      cat >>${output}.new <<__EOF__
+\$(objects)/$stem.shbin.lo: \$(objects)/$obj
+	@echo "# $stem.shbin.lo - a libtool object file" > \$@
+	@echo "# Generated by ltmain.sh (GNU libtool) 2.2.6" >> \$@
+	@echo "pic_object=none" >> \$@
+	@echo "non_pic_object='$stem.shbin.o'" >> \$@
+
+\$(objects)/$obj \$(objects)/$hdr: \$(objects)/$shbin
+	@echo "extern const u8 ${stem}_shbin[];" > \$(objects)/$hdr
+	@echo "extern const u8 ${stem}_shbin_end[];" >> \$(objects)/$hdr
+	@echo "extern const u32 ${stem}_shbin_size;" >> \$(objects)/$hdr
+	@bin2s \$(objects)/$shbin | arm-none-eabi-as -o \$(objects)/$obj
+
+
+\$(objects)/$shbin: $src
+	picasso -o \$@ \$<
+__EOF__
+    fi
     # No search to be done with Windows resource files
     if test x"$ext" != x"rc"; then
         search_deps $src | sort | uniq >>${output}.new
     fi
     case $ext in
+        pica)
+        ;;
         c) cat >>${output}.new <<__EOF__
 
 	\$(LIBTOOL) --mode=compile \$(CC) \$(CFLAGS) \$(EXTRA_CFLAGS) -c $src  -o \$@
@@ -89,5 +114,7 @@ __EOF__
     esac
     echo "" >>${output}.new
 done
+echo "\$(objects)/SDL_n3dsvideo.lo : \$(objects)/vshader_shbin.h" >>${output}.new
+
 mv ${output}.new ${output}
 rm -f ${cache_prefix}*
diff --git a/configure.in b/configure.in
index 08c8e1e97..ae4c68b2b 100644
--- a/configure.in
+++ b/configure.in
@@ -62,7 +62,7 @@ AC_PROG_MAKE_SET
 AC_CHECK_TOOL(WINDRES, [windres], [:])
 
 dnl Set up the compiler and linker flags
-INCLUDE="-I$srcdir/include"
+INCLUDE="-I$srcdir/include -I\$(objects)"
 if test x$srcdir != x.; then
     # Remove SDL_config.h from the source directory, since it's the
     # default one, and we want to include the one that we generate.
@@ -925,6 +925,41 @@ AC_HELP_STRING([--enable-ipod], [configure SDL to work with iPodLinux [[default=
     fi
 }
 
+Checkn3ds()
+{
+    AC_ARG_ENABLE(n3ds,
+AC_HELP_STRING([--enable-n3ds], [configure SDL to work with n3ds [[default=no]]]),
+        , enable_n3ds=no)
+	if test x$enable_n3ds = xyes; then
+        ARCH=n3ds
+        N3DS_CFLAGS="-ffunction-sections -fdata-sections -march=armv6k \
+                     -mtune=mpcore -mfloat-abi=hard -mword-relocations \
+                     -I$DEVKITPRO/libctru/include -DARM11 -D_3DS"
+        EXTRA_CFLAGS="$EXTRA_CFLAGS $N3DS_CFLAGS"
+        CFLAGS="$CFLAGS $N3DS_CFLAGS"
+        SDL_CFLAGS="$SDL_CFLAGS $N3DS_CFLAGS"
+        SDL_LIBS="-specs=3dsx.specs -march=armv6k -mfloat-abi=hard \
+                  -L${DEVKITPRO}/portlibs/3ds $SDL_LIBS \
+                  -L${DEVKITPRO}/libctru/lib -lcitro3d -lctru"
+ 		AC_DEFINE(SDL_TIMER_N3DS)
+		SOURCES="$SOURCES $srcdir/src/timer/n3ds/*.c"
+		have_timers=yes
+		AC_DEFINE(SDL_AUDIO_DRIVER_N3DS)
+		SOURCES="$SOURCES $srcdir/src/audio/n3ds/*.c"
+		have_audio=yes
+		AC_DEFINE(SDL_JOYSTICK_N3DS)
+		SOURCES="$SOURCES $srcdir/src/joystick/n3ds/*.c"
+		have_joystick=yes
+		AC_DEFINE(SDL_THREAD_N3DS)
+		SOURCES="$SOURCES $srcdir/src/thread/n3ds/*.c"
+		have_threads=yes
+		AC_DEFINE(SDL_VIDEO_DRIVER_N3DS)
+		SOURCES="$SOURCES $srcdir/src/video/n3ds/vshader.pica"
+		SOURCES="$SOURCES $srcdir/src/video/n3ds/*.c"
+		have_video=yes
+	fi
+}
+
 dnl Find the nanox include and library directories
 CheckNANOX()
 {
@@ -2314,6 +2349,9 @@ AC_HELP_STRING([--enable-rpath], [use an rpath when linking SDL [[default=yes]]]
 
 dnl Set up the configuration based on the host platform!
 case "$host" in
+    *arm-none-eabi*) 
+        Checkn3ds
+        ;;
     arm-*-elf*) # FIXME: Can we get more specific for iPodLinux?
         ARCH=linux
         CheckDummyVideo
@@ -2885,6 +2923,7 @@ OBJECTS=`echo $OBJECTS | sed 's,[[^ ]]*/\([[^ ]]*\)\.m,$(objects)/\1.lo,g'`
 OBJECTS=`echo $OBJECTS | sed 's,[[^ ]]*/\([[^ ]]*\)\.c,$(objects)/\1.lo,g'`
 OBJECTS=`echo $OBJECTS | sed 's,[[^ ]]*/\([[^ ]]*\)\.S,$(objects)/\1.lo,g'`
 OBJECTS=`echo $OBJECTS | sed 's,[[^ ]]*/\([[^ ]]*\)\.rc,$(objects)/\1.lo,g'`
+OBJECTS=`echo $OBJECTS | sed 's,[[^ ]]*/\([[^ ]]*\)\.pica,$(objects)/\1.shbin.lo,g'`
 
 SDLMAIN_OBJECTS=`echo $SDLMAIN_SOURCES | sed 's,[[^ ]]*/\([[^ ]]*\)\.cc,$(objects)/\1.lo,g'`
 SDLMAIN_OBJECTS=`echo $SDLMAIN_OBJECTS | sed 's,[[^ ]]*/\([[^ ]]*\)\.m,$(objects)/\1.lo,g'`
diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in
index 8bb1773c0..d6f349499 100644
--- a/include/SDL_config.h.in
+++ b/include/SDL_config.h.in
@@ -185,6 +185,7 @@
 #undef SDL_AUDIO_DRIVER_SNDMGR
 #undef SDL_AUDIO_DRIVER_SUNAUDIO
 #undef SDL_AUDIO_DRIVER_WAVEOUT
+#undef SDL_AUDIO_DRIVER_N3DS
 
 /* Enable various cdrom drivers */
 #undef SDL_CDROM_AIX
@@ -218,6 +219,7 @@
 #undef SDL_JOYSTICK_WINMM
 #undef SDL_JOYSTICK_USBHID
 #undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
+#undef SDL_JOYSTICK_N3DS
 
 /* Enable various shared object loading systems */
 #undef SDL_LOADSO_BEOS
@@ -239,6 +241,7 @@
 #undef SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP
 #undef SDL_THREAD_SPROC
 #undef SDL_THREAD_WIN32
+#undef SDL_THREAD_N3DS
 
 /* Enable various timer systems */
 #undef SDL_TIMER_BEOS
@@ -251,6 +254,7 @@
 #undef SDL_TIMER_UNIX
 #undef SDL_TIMER_WIN32
 #undef SDL_TIMER_WINCE
+#undef SDL_TIMER_N3DS
 
 /* Enable various video drivers */
 #undef SDL_VIDEO_DRIVER_AALIB
@@ -293,6 +297,7 @@
 #undef SDL_VIDEO_DRIVER_X11_XRANDR
 #undef SDL_VIDEO_DRIVER_X11_XV
 #undef SDL_VIDEO_DRIVER_XBIOS
+#undef SDL_VIDEO_DRIVER_N3DS
 
 /* Enable OpenGL support */
 #undef SDL_VIDEO_OPENGL
diff --git a/include/SDL_keyboard.h b/include/SDL_keyboard.h
index 9d7129c52..fc2def257 100644
--- a/include/SDL_keyboard.h
+++ b/include/SDL_keyboard.h
@@ -125,6 +125,9 @@ extern DECLSPEC void SDLCALL SDL_SetModState(SDLMod modstate);
  */
 extern DECLSPEC char * SDLCALL SDL_GetKeyName(SDLKey key);
 
+#ifdef _3DS
+void SDL_N3DSKeyBind(unsigned int hidkey, SDLKey key);
+#endif
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
diff --git a/include/SDL_platform.h b/include/SDL_platform.h
index 48540a85d..5fec876ed 100644
--- a/include/SDL_platform.h
+++ b/include/SDL_platform.h
@@ -106,5 +106,9 @@
 #undef __WIN32__
 #define __WIN32__	1
 #endif
+#if defined(_3DS)
+#undef __N3DS__
+#define __N3DS__	1
+#endif
 
 #endif /* _SDL_platform_h */
diff --git a/include/SDL_stdinc.h b/include/SDL_stdinc.h
index 35a4fdde5..d21e0d3db 100644
--- a/include/SDL_stdinc.h
+++ b/include/SDL_stdinc.h
@@ -147,7 +147,7 @@ typedef enum {
 	DUMMY_ENUM_VALUE
 } SDL_DUMMY_ENUM;
 
-#ifndef __NDS__
+#if !defined(__NDS__) && !defined(__N3DS__)
 SDL_COMPILE_TIME_ASSERT(enum, sizeof(SDL_DUMMY_ENUM) == sizeof(int));
 #endif
 /*@}*/
diff --git a/include/SDL_video.h b/include/SDL_video.h
index f9c4e0702..d184221da 100644
--- a/include/SDL_video.h
+++ b/include/SDL_video.h
@@ -142,7 +142,16 @@ typedef struct SDL_Surface {
 #define SDL_OPENGL      0x00000002      /**< Create an OpenGL rendering context */
 #define SDL_OPENGLBLIT	0x0000000A	/**< Create an OpenGL rendering context and use it for blitting */
 #define SDL_RESIZABLE	0x00000010	/**< This video mode may be resized */
-#define SDL_NOFRAME	0x00000020	/**< No window caption or edge frame */
+#define SDL_NOFRAME		0x00000020	/**< No window caption or edge frame */
+#ifdef __N3DS__
+#define SDL_TOPSCR		0x00100000	/**< N3DS Top Screen mode */
+#define SDL_BOTTOMSCR	0x00200000	/**< N3DS Bottom Screen mode */
+#define SDL_DUALSCR		0x00300000	/**< N3DS Top+Bottom Screen mode */
+#define SDL_FITWIDTH	0x00400000	/**> Render a resized surface to fit systen screen width */
+#define SDL_FITHEIGHT	0x00800000	/**> Render a resized surface to fit systen screen height */
+#define SDL_CONSOLETOP	0x00040000	/**> Enale console output on Top screen */
+#define SDL_CONSOLEBOTTOM	0x00080000	/**> Enale console output on Bottom screen */
+#endif
 /*@}*/
 
 /** Used internally (read-only) */
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index beb26e0b4..4fd548c70 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -104,6 +104,9 @@ static AudioBootStrap *bootstrap[] = {
 #if SDL_AUDIO_DRIVER_NDS
 	&NDSAUD_bootstrap,
 #endif
+#if SDL_AUDIO_DRIVER_N3DS
+	&N3DSAUD_bootstrap,
+#endif
 #if SDL_AUDIO_DRIVER_MMEAUDIO
 	&MMEAUDIO_bootstrap,
 #endif
diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
index 74ac21df0..2cba86dae 100644
--- a/src/audio/SDL_sysaudio.h
+++ b/src/audio/SDL_sysaudio.h
@@ -170,6 +170,9 @@ extern AudioBootStrap DCAUD_bootstrap;
 #if SDL_AUDIO_DRIVER_NDS
 extern AudioBootStrap NDSAUD_bootstrap;
 #endif
+#if SDL_AUDIO_DRIVER_N3DS
+extern AudioBootStrap N3DSAUD_bootstrap;
+#endif
 #if SDL_AUDIO_DRIVER_MMEAUDIO
 extern AudioBootStrap MMEAUDIO_bootstrap;
 #endif
diff --git a/src/audio/n3ds/SDL_n3dsaudio.c b/src/audio/n3ds/SDL_n3dsaudio.c
new file mode 100644
index 000000000..6815755f1
--- /dev/null
+++ b/src/audio/n3ds/SDL_n3dsaudio.c
@@ -0,0 +1,358 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2012 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+
+    This file written by Ryan C. Gordon (icculus@icculus.org)
+*/
+#include "SDL_config.h"
+
+/* Output audio to nowhere... */
+
+#include "SDL_rwops.h"
+#include "SDL_timer.h"
+#include "SDL_audio.h"
+#include "../SDL_audiomem.h"
+#include "../SDL_audio_c.h"
+#include "../SDL_audiodev_c.h"
+#include "SDL_n3dsaudio.h"
+
+#include <3ds.h>
+#include <assert.h>
+
+//extern volatile bool app_pause;
+extern volatile bool app_exiting;
+
+static Handle wbufDoneEvent;
+static aptHookCookie aptCookie;
+
+static const u64 EventTimeout = 1000LL * 1000LL * 100LL;	// 100 ms
+
+/* The tag name used by N3DS audio */
+#define N3DSAUD_DRIVER_NAME         "n3ds"
+
+/* Audio driver functions */
+static int N3DSAUD_OpenAudio(_THIS, SDL_AudioSpec *spec);
+static void N3DSAUD_WaitAudio(_THIS);
+static void N3DSAUD_PlayAudio(_THIS);
+static Uint8 *N3DSAUD_GetAudioBuf(_THIS);
+static void N3DSAUD_CloseAudio(_THIS);
+
+/* Audio driver bootstrap functions */
+static int N3DSAUD_Available(void)
+{
+	return(1);
+}
+
+static inline void contextLock(_THIS)
+{
+	LightLock_Lock(&this->hidden->lock);
+}
+
+static inline void contextUnlock(_THIS)
+{
+	LightLock_Unlock(&this->hidden->lock);
+}
+
+static void N3DSAUD_LockAudio(_THIS)
+{
+	contextLock(this);
+}
+
+static void N3DSAUD_UnlockAudio(_THIS)
+{
+	contextUnlock(this);
+}
+
+static void N3DSAUD_DeleteDevice(SDL_AudioDevice *device)
+{
+	if ( device->hidden->mixbuf != NULL ) {
+		SDL_FreeAudioMem(device->hidden->mixbuf);
+		device->hidden->mixbuf = NULL;
+	}
+	if 	( device->hidden->waveBuf[0].data_vaddr!= NULL ) {
+		linearFree(device->hidden->waveBuf[0].data_vaddr);
+		device->hidden->waveBuf[0].data_vaddr = NULL;
+	}
+
+	ndspExit();
+
+	SDL_free(device->hidden);
+
+	SDL_free(device);
+}
+
+static SDL_AudioDevice *N3DSAUD_CreateDevice(int devindex)
+{
+	SDL_AudioDevice *this;
+
+	/* Initialize all variables that we clean on shutdown */
+	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
+	if ( this ) {
+		SDL_memset(this, 0, (sizeof *this));
+		this->hidden = (struct SDL_PrivateAudioData *)
+				SDL_malloc((sizeof *this->hidden));
+	}
+	if ( (this == NULL) || (this->hidden == NULL) ) {
+		SDL_OutOfMemory();
+		if ( this ) {
+			SDL_free(this);
+		}
+		return(0);
+	}
+	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+
+	/* Set the function pointers */
+	this->OpenAudio = N3DSAUD_OpenAudio;
+	this->WaitAudio = N3DSAUD_WaitAudio;
+	this->PlayAudio = N3DSAUD_PlayAudio;
+	this->GetAudioBuf = N3DSAUD_GetAudioBuf;
+	this->CloseAudio = N3DSAUD_CloseAudio;
+	this->LockAudio = N3DSAUD_LockAudio;
+	this->UnlockAudio = N3DSAUD_UnlockAudio;
+	this->free = N3DSAUD_DeleteDevice;
+
+	return this;
+}
+
+AudioBootStrap N3DSAUD_bootstrap = {
+	N3DSAUD_DRIVER_NAME, "SDL N3DS audio driver",
+	N3DSAUD_Available, N3DSAUD_CreateDevice
+};
+
+
+static void aptCallback(APT_HookType hook, void* param)
+{
+	switch (hook)
+	{
+		case APTHOOK_ONEXIT:
+			app_exiting = true;
+			svcSignalEvent(wbufDoneEvent);
+			break;
+		default:
+			break;
+	}
+}
+
+/* called by ndsp thread on each audio frame */
+static void audioFrameFinished(void *context)
+{
+	SDL_AudioDevice *this = (SDL_AudioDevice *) context;
+
+	contextLock(this);
+	bool done = this->hidden->waveBuf[this->hidden->nextbuf].status == NDSP_WBUF_DONE;
+	contextUnlock(this);
+
+	if (done)
+	{
+		svcSignalEvent(wbufDoneEvent);
+	}
+}
+
+/* This function blocks until it is possible to write a full sound buffer */
+static void N3DSAUD_WaitAudio(_THIS)
+{
+	bool not_done;
+	size_t wait_index;
+
+	contextLock(this);
+	wait_index = this->hidden->nextbuf;
+
+retry:
+			
+	not_done = this->hidden->waveBuf[wait_index].status != NDSP_WBUF_DONE;
+	contextUnlock(this);
+
+	if (not_done)
+	{
+		svcWaitSynchronization(wbufDoneEvent, EventTimeout);
+		
+		if (app_exiting)
+		{
+			return;
+		}
+
+		contextLock(this);
+		goto retry;
+	}
+}
+
+static void N3DSAUD_PlayAudio(_THIS)
+{
+	if (app_exiting) return;
+
+	contextLock(this);
+
+	size_t nextbuf = this->hidden->nextbuf;
+	size_t sampleLen = this->hidden->mixlen;
+
+	assert(this->hidden->waveBuf[nextbuf].status == NDSP_WBUF_DONE);
+
+	this->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
+	contextUnlock(this);
+
+	if (this->hidden->format==NDSP_FORMAT_STEREO_PCM8 || this->hidden->format==NDSP_FORMAT_MONO_PCM8) {
+		memcpy(this->hidden->waveBuf[nextbuf].data_pcm8,this->hidden->mixbuf,sampleLen);
+		DSP_FlushDataCache(this->hidden->waveBuf[nextbuf].data_pcm8,sampleLen);
+	} else {
+		memcpy(this->hidden->waveBuf[nextbuf].data_pcm16,this->hidden->mixbuf,sampleLen);
+		DSP_FlushDataCache(this->hidden->waveBuf[nextbuf].data_pcm16,sampleLen);
+	}
+
+	this->hidden->waveBuf[nextbuf].offset=0;
+	this->hidden->waveBuf[nextbuf].status=NDSP_WBUF_QUEUED;
+	ndspChnWaveBufAdd(0, &this->hidden->waveBuf[nextbuf]);
+}
+
+static Uint8 *N3DSAUD_GetAudioBuf(_THIS)
+{
+	return(this->hidden->mixbuf);
+}
+
+static void N3DSAUD_CloseAudio(_THIS)
+{
+	contextLock(this);
+
+	if ( this->hidden->mixbuf != NULL ) {
+		SDL_FreeAudioMem(this->hidden->mixbuf);
+		this->hidden->mixbuf = NULL;
+	}
+	if ( this->hidden->waveBuf[0].data_vaddr!= NULL ) {
+		linearFree(this->hidden->waveBuf[0].data_vaddr);
+		this->hidden->waveBuf[0].data_vaddr = NULL;
+	}
+
+	contextUnlock(this);
+}
+
+static int N3DSAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
+{
+   //start 3ds DSP init
+	Result rc = ndspInit();
+	if (R_FAILED(rc)) {
+		if ((R_SUMMARY(rc) == RS_NOTFOUND) && (R_MODULE(rc) == RM_DSP))
+			SDL_SetError("DSP init failed: dspfirm.cdc missing!");
+		else
+			SDL_SetError("DSP init failed. Error code: 0x%X", rc);
+		return -1;
+	}
+
+	if(spec->channels > 2)
+		spec->channels = 2;
+
+    Uint16 test_format = SDL_FirstAudioFormat(spec->format);
+    int valid_datatype = 0;
+    while ((!valid_datatype) && (test_format)) {
+        spec->format = test_format;
+        switch (test_format) {
+
+			case AUDIO_S8:
+				/* Signed 8-bit audio supported */
+				this->hidden->format=(spec->channels==2)?NDSP_FORMAT_STEREO_PCM8:NDSP_FORMAT_MONO_PCM8;
+				this->hidden->isSigned=1;
+				this->hidden->bytePerSample = (spec->channels);
+				   valid_datatype = 1;
+				break;
+			case AUDIO_S16:
+				/* Signed 16-bit audio supported */
+				this->hidden->format=(spec->channels==2)?NDSP_FORMAT_STEREO_PCM16:NDSP_FORMAT_MONO_PCM16;
+				this->hidden->isSigned=1;
+				this->hidden->bytePerSample = (spec->channels) * 2;
+				   valid_datatype = 1;
+				break;
+			default:
+				test_format = SDL_NextAudioFormat();
+				break;
+		}
+	}
+
+    if (!valid_datatype) {  /* shouldn't happen, but just in case... */
+        SDL_SetError("Unsupported audio format");
+		ndspExit();
+        return (-1);
+    }
+
+	/* Update the fragment size as size in bytes */
+	SDL_CalculateAudioSpec(spec);
+
+	/* Allocate mixing buffer */
+	if (spec->size >= UINT32_MAX/2)
+	{
+		ndspExit();
+		return(-1);
+	}
+	this->hidden->mixlen = spec->size;
+	this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(spec->size); 
+	if ( this->hidden->mixbuf == NULL ) {
+		ndspExit();
+		return(-1);
+	}
+	SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
+
+	Uint8 * temp = (Uint8 *) linearAlloc(this->hidden->mixlen*NUM_BUFFERS);
+	if (temp == NULL ) {
+		SDL_free(this->hidden->mixbuf);
+		ndspExit();
+		return(-1);
+	}
+	memset(temp,0,this->hidden->mixlen*NUM_BUFFERS);
+	DSP_FlushDataCache(temp,this->hidden->mixlen*NUM_BUFFERS);
+
+	this->hidden->nextbuf = 0;
+	this->hidden->channels = spec->channels;
+	this->hidden->samplerate = spec->freq;
+
+	LightLock_Init(&this->hidden->lock);
+
+	ndspChnWaveBufClear(0);
+ 	ndspChnReset(0);
+
+	ndspSetOutputMode((this->hidden->channels==2)?NDSP_OUTPUT_STEREO:NDSP_OUTPUT_MONO);
+
+	ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
+	ndspChnSetRate(0, spec->freq);
+	ndspChnSetFormat(0, this->hidden->format);
+
+	float mix[12];
+	memset(mix, 0, sizeof(mix));
+	mix[0] = 1.0;
+	mix[1] = 1.0;
+	ndspChnSetMix(0, mix);
+
+	memset(this->hidden->waveBuf,0,sizeof(ndspWaveBuf)*NUM_BUFFERS);
+
+	this->hidden->waveBuf[0].data_vaddr = temp;
+	this->hidden->waveBuf[0].nsamples = this->hidden->mixlen / this->hidden->bytePerSample;
+	this->hidden->waveBuf[0].status = NDSP_WBUF_DONE;
+
+	this->hidden->waveBuf[1].data_vaddr = temp + this->hidden->mixlen;
+	this->hidden->waveBuf[1].nsamples = this->hidden->mixlen / this->hidden->bytePerSample;
+	this->hidden->waveBuf[1].status = NDSP_WBUF_DONE;
+
+	/* Setup callback */
+	svcCreateEvent(&wbufDoneEvent, RESET_ONESHOT);
+	ndspSetCallback(audioFrameFinished, this);
+
+	aptHook(&aptCookie, aptCallback, NULL);
+
+    // end 3ds DSP init
+
+	/* We're ready to rock and roll. :-) */
+	return(0);
+}
diff --git a/src/audio/n3ds/SDL_n3dsaudio.h b/src/audio/n3ds/SDL_n3dsaudio.h
new file mode 100644
index 000000000..9ec0c6e50
--- /dev/null
+++ b/src/audio/n3ds/SDL_n3dsaudio.h
@@ -0,0 +1,48 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2012 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include <3ds.h>
+#include "SDL_config.h"
+
+#ifndef _SDL_n3dsaudio_h
+#define _SDL_n3dsaudio_h
+
+#include "../SDL_sysaudio.h"
+
+#define _THIS	SDL_AudioDevice *this
+
+#define NUM_BUFFERS 2			/* -- Don't lower this! */
+
+struct SDL_PrivateAudioData {
+	/* The file descriptor for the audio device */
+	Uint8 *mixbuf;
+	Uint32 mixlen;
+	Uint32 format;
+	Uint32 samplerate;
+	Uint32 channels;
+	Uint8  bytePerSample;
+	Uint32 isSigned;
+	Uint32 nextbuf;
+	ndspWaveBuf waveBuf[NUM_BUFFERS];
+	LightLock lock;
+};
+
+#endif /* _SDL_n3dsaudio_h */
diff --git a/src/joystick/n3ds/SDL_sysjoystick.c b/src/joystick/n3ds/SDL_sysjoystick.c
new file mode 100644
index 000000000..bcb296d14
--- /dev/null
+++ b/src/joystick/n3ds/SDL_sysjoystick.c
@@ -0,0 +1,117 @@
+#include "SDL_config.h"
+
+#include <3ds.h>
+
+#include "SDL_error.h"
+#include "SDL_events.h"
+#include "SDL_joystick.h"
+#include "../SDL_sysjoystick.h"
+#include "../SDL_joystick_c.h"
+
+#include "../../video/n3ds/SDL_n3dsvideo.h"
+
+int old_x = 0, old_y = 0;
+u32 key_press, key_release = 0;
+
+int SDL_SYS_JoystickInit (void) {
+	SDL_numjoysticks = 1;
+
+	return 1;
+}
+
+const char *SDL_SYS_JoystickName (int index) {
+	if(!index)
+		return "3DS builtin joypad";
+
+	SDL_SetError ("No joystick available with that index");
+
+	return (NULL);
+}
+
+int SDL_SYS_JoystickOpen(SDL_Joystick *joystick) {
+	joystick->nbuttons = 8;
+	joystick->nhats = 0;
+	joystick->nballs = 0;
+	joystick->naxes = 2;
+
+	return 0;
+}
+
+void SDL_SYS_JoystickUpdate (SDL_Joystick *joystick) {
+
+	circlePosition circlePad;
+	hidCircleRead(&circlePad);
+	int x,y;
+	x = circlePad.dx;
+	y = circlePad.dy;
+	if (x > 156) x= 156; 
+	if (x < -156) x= -156; 
+	if (y > 156) y= 156; 
+	if (y < -156) y= -156; 
+	if (old_x != x) {
+		old_x = x;
+		SDL_PrivateJoystickAxis (joystick, 0, x * 210);
+	}
+	if (old_y != y) {
+		old_y = y;
+		SDL_PrivateJoystickAxis (joystick, 1, - y * 210);
+	}	
+
+	key_press = hidKeysDown ();
+	if ((key_press & KEY_A)) {
+		SDL_PrivateJoystickButton (joystick, 1, SDL_PRESSED);
+	}
+	if ((key_press & KEY_B)) {
+		SDL_PrivateJoystickButton (joystick, 2, SDL_PRESSED);
+	}
+	if ((key_press & KEY_X)) {
+		SDL_PrivateJoystickButton (joystick, 3, SDL_PRESSED);
+	}
+	if ((key_press & KEY_Y)) {
+		SDL_PrivateJoystickButton (joystick, 4, SDL_PRESSED);
+	}
+	if ((key_press & KEY_SELECT)) {
+		SDL_PrivateJoystickButton (joystick, 7, SDL_PRESSED);
+	}
+	if ((key_press & KEY_START)) {
+		SDL_PrivateJoystickButton (joystick, 8, SDL_PRESSED);
+	}
+	if ((key_press & KEY_L)) {
+		SDL_PrivateJoystickButton (joystick, 5, SDL_PRESSED);
+	}
+	if ((key_press&KEY_R)) {
+		SDL_PrivateJoystickButton(joystick, 6,SDL_PRESSED);
+	}
+
+	key_release = hidKeysUp ();
+	if ((key_release & KEY_A)) {
+		SDL_PrivateJoystickButton (joystick, 1, SDL_RELEASED);
+	}
+	if ((key_release & KEY_B)) {
+		SDL_PrivateJoystickButton (joystick, 2, SDL_RELEASED);
+	}
+	if ((key_release & KEY_X)) {
+		SDL_PrivateJoystickButton (joystick, 3, SDL_RELEASED);
+	}
+	if ((key_release & KEY_Y)) {
+		SDL_PrivateJoystickButton (joystick, 4, SDL_RELEASED);
+	}
+	if ((key_release & KEY_SELECT)) {
+		SDL_PrivateJoystickButton (joystick, 7, SDL_RELEASED);
+	}
+	if ((key_release & KEY_START)) {
+		SDL_PrivateJoystickButton (joystick, 8, SDL_RELEASED);
+	}
+	if ((key_release & KEY_L)) {
+		SDL_PrivateJoystickButton (joystick, 5, SDL_RELEASED);
+	}
+	if ((key_release & KEY_R)) {
+		SDL_PrivateJoystickButton (joystick, 6, SDL_RELEASED);
+	}
+}
+
+void SDL_SYS_JoystickClose (SDL_Joystick *joystick) {
+}
+
+void SDL_SYS_JoystickQuit (void) {
+}
diff --git a/src/thread/SDL_thread_c.h b/src/thread/SDL_thread_c.h
index 8af6e52f4..c48803ff2 100644
--- a/src/thread/SDL_thread_c.h
+++ b/src/thread/SDL_thread_c.h
@@ -43,6 +43,8 @@
 #include "win32/SDL_systhread_c.h"
 #elif SDL_THREAD_SYMBIAN
 #include "symbian/SDL_systhread_c.h"
+#elif SDL_THREAD_N3DS
+#include "n3ds/SDL_systhread_c.h"
 #else
 #error Need thread implementation for this platform
 #include "generic/SDL_systhread_c.h"
diff --git a/src/thread/n3ds/SDL_syscond.c b/src/thread/n3ds/SDL_syscond.c
new file mode 100644
index 000000000..56e20d4e8
--- /dev/null
+++ b/src/thread/n3ds/SDL_syscond.c
@@ -0,0 +1,222 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+//#include "../../SDL_internal.h"
+
+/* An implementation of condition variables using semaphores and mutexes */
+/*
+   This implementation borrows heavily from the BeOS condition variable
+   implementation, written by Christopher Tate and Owen Smith.  Thanks!
+ */
+#include "SDL_config.h"
+
+#include "SDL_thread.h"
+
+struct SDL_cond
+{
+    SDL_mutex *lock;
+    int waiting;
+    int signals;
+    SDL_sem *wait_sem;
+    SDL_sem *wait_done;
+};
+
+/* Create a condition variable */
+SDL_cond *
+SDL_CreateCond(void)
+{
+    SDL_cond *cond;
+
+    cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
+    if (cond) {
+        cond->lock = SDL_CreateMutex();
+        cond->wait_sem = SDL_CreateSemaphore(0);
+        cond->wait_done = SDL_CreateSemaphore(0);
+        cond->waiting = cond->signals = 0;
+        if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
+            SDL_DestroyCond(cond);
+            cond = NULL;
+        }
+    } else {
+        SDL_OutOfMemory();
+    }
+    return (cond);
+}
+
+/* Destroy a condition variable */
+void
+SDL_DestroyCond(SDL_cond * cond)
+{
+    if (cond) {
+        if (cond->wait_sem) {
+            SDL_DestroySemaphore(cond->wait_sem);
+        }
+        if (cond->wait_done) {
+            SDL_DestroySemaphore(cond->wait_done);
+        }
+        if (cond->lock) {
+            SDL_DestroyMutex(cond->lock);
+        }
+        SDL_free(cond);
+    }
+}
+
+/* Restart one of the threads that are waiting on the condition variable */
+int
+SDL_CondSignal(SDL_cond * cond)
+{
+    if (!cond) {
+        SDL_SetError("Passed a NULL condition variable");
+        return -1;
+    }
+
+    /* If there are waiting threads not already signalled, then
+       signal the condition and wait for the thread to respond.
+     */
+    SDL_LockMutex(cond->lock);
+    if (cond->waiting > cond->signals) {
+        ++cond->signals;
+        SDL_SemPost(cond->wait_sem);
+        SDL_UnlockMutex(cond->lock);
+        SDL_SemWait(cond->wait_done);
+    } else {
+        SDL_UnlockMutex(cond->lock);
+    }
+
+    return 0;
+}
+
+/* Restart all threads that are waiting on the condition variable */
+int
+SDL_CondBroadcast(SDL_cond * cond)
+{
+    if (!cond) {
+        SDL_SetError("Passed a NULL condition variable");
+        return -1;
+    }
+
+    /* If there are waiting threads not already signalled, then
+       signal the condition and wait for the thread to respond.
+     */
+    SDL_LockMutex(cond->lock);
+    if (cond->waiting > cond->signals) {
+        int i, num_waiting;
+
+        num_waiting = (cond->waiting - cond->signals);
+        cond->signals = cond->waiting;
+        for (i = 0; i < num_waiting; ++i) {
+            SDL_SemPost(cond->wait_sem);
+        }
+        /* Now all released threads are blocked here, waiting for us.
+           Collect them all (and win fabulous prizes!) :-)
+         */
+        SDL_UnlockMutex(cond->lock);
+        for (i = 0; i < num_waiting; ++i) {
+            SDL_SemWait(cond->wait_done);
+        }
+    } else {
+        SDL_UnlockMutex(cond->lock);
+    }
+
+    return 0;
+}
+
+/* Wait on the condition variable for at most 'ms' milliseconds.
+   The mutex must be locked before entering this function!
+   The mutex is unlocked during the wait, and locked again after the wait.
+
+Typical use:
+
+Thread A:
+    SDL_LockMutex(lock);
+    while ( ! condition ) {
+        SDL_CondWait(cond, lock);
+    }
+    SDL_UnlockMutex(lock);
+
+Thread B:
+    SDL_LockMutex(lock);
+    ...
+    condition = true;
+    ...
+    SDL_CondSignal(cond);
+    SDL_UnlockMutex(lock);
+ */
+int
+SDL_CondWaitTimeout(SDL_cond * cond, SDL_mutex * mutex, Uint32 ms)
+{
+    int retval;
+
+    if (!cond) {
+        SDL_SetError("Passed a NULL condition variable");
+        return -1;
+    }
+
+    /* Obtain the protection mutex, and increment the number of waiters.
+       This allows the signal mechanism to only perform a signal if there
+       are waiting threads.
+     */
+    SDL_LockMutex(cond->lock);
+    ++cond->waiting;
+    SDL_UnlockMutex(cond->lock);
+
+    /* Unlock the mutex, as is required by condition variable semantics */
+    SDL_UnlockMutex(mutex);
+
+    /* Wait for a signal */
+    if (ms == SDL_MUTEX_MAXWAIT) {
+        retval = SDL_SemWait(cond->wait_sem);
+    } else {
+        retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
+    }
+
+    /* Let the signaler know we have completed the wait, otherwise
+       the signaler can race ahead and get the condition semaphore
+       if we are stopped between the mutex unlock and semaphore wait,
+       giving a deadlock.  See the following URL for details:
+       http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
+     */
+    SDL_LockMutex(cond->lock);
+    if (cond->signals > 0) {
+        /* If we timed out, we need to eat a condition signal */
+        if (retval > 0) {
+            SDL_SemWait(cond->wait_sem);
+        }
+        /* We always notify the signal thread that we are done */
+        SDL_SemPost(cond->wait_done);
+
+        /* Signal handshake complete */
+        --cond->signals;
+    }
+    --cond->waiting;
+    SDL_UnlockMutex(cond->lock);
+
+    /* Lock the mutex, as is required by condition variable semantics */
+    SDL_LockMutex(mutex);
+
+    return retval;
+}
+
+/* Wait on the condition variable forever */
+int
+SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex)
+{
+    return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
+}
diff --git a/src/thread/n3ds/SDL_sysmutex.c b/src/thread/n3ds/SDL_sysmutex.c
new file mode 100644
index 000000000..860583deb
--- /dev/null
+++ b/src/thread/n3ds/SDL_sysmutex.c
@@ -0,0 +1,135 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+//#include "../../SDL_internal.h"
+
+/* An implementation of mutexes using semaphores */
+
+#include "SDL_config.h"
+
+#include "SDL_thread.h"
+#include "SDL_systhread_c.h"
+
+
+struct SDL_mutex
+{
+    int recursive;
+    Uint32 owner;
+    SDL_sem *sem;
+};
+
+/* Create a mutex */
+SDL_mutex *
+SDL_CreateMutex(void)
+{
+    SDL_mutex *mutex;
+
+    /* Allocate mutex memory */
+    mutex = (SDL_mutex *) SDL_malloc(sizeof(*mutex));
+    if (mutex) {
+        /* Create the mutex semaphore, with initial value 1 */
+        mutex->sem = SDL_CreateSemaphore(1);
+        mutex->recursive = 0;
+        mutex->owner = 0;
+        if (!mutex->sem) {
+            SDL_free(mutex);
+            mutex = NULL;
+        }
+    } else {
+        SDL_OutOfMemory();
+    }
+    return mutex;
+}
+
+/* Free the mutex */
+void
+SDL_DestroyMutex(SDL_mutex * mutex)
+{
+    if (mutex) {
+        if (mutex->sem) {
+            SDL_DestroySemaphore(mutex->sem);
+        }
+        SDL_free(mutex);
+    }
+}
+
+/* Lock the semaphore */
+int
+SDL_mutexP(SDL_mutex * mutex)
+{
+#if SDL_THREADS_DISABLED
+    return 0;
+#else
+    Uint32 this_thread;
+
+    if (mutex == NULL) {
+        SDL_SetError("Passed a NULL mutex");
+        return -1;
+    }
+
+    this_thread = SDL_ThreadID();
+    if (mutex->owner == this_thread) {
+        ++mutex->recursive;
+    } else {
+        /* The order of operations is important.
+           We set the locking thread id after we obtain the lock
+           so unlocks from other threads will fail.
+         */
+        SDL_SemWait(mutex->sem);
+        mutex->owner = this_thread;
+        mutex->recursive = 0;
+    }
+
+    return 0;
+#endif /* SDL_THREADS_DISABLED */
+}
+
+/* Unlock the mutex */
+int
+SDL_mutexV(SDL_mutex * mutex)
+{
+#if SDL_THREADS_DISABLED
+    return 0;
+#else
+    if (mutex == NULL) {
+        SDL_SetError("Passed a NULL mutex");
+        return -1;
+    }
+
+    /* If we don't own the mutex, we can't unlock it */
+    if (SDL_ThreadID() != mutex->owner) {
+        SDL_SetError("mutex not owned by this thread");
+        return -1;
+    }
+
+    if (mutex->recursive) {
+        --mutex->recursive;
+    } else {
+        /* The order of operations is important.
+           First reset the owner so another thread doesn't lock
+           the mutex and set the ownership before we reset it,
+           then release the lock semaphore.
+         */
+        mutex->owner = 0;
+        SDL_SemPost(mutex->sem);
+    }
+    return 0;
+#endif /* SDL_THREADS_DISABLED */
+}
diff --git a/src/thread/n3ds/SDL_sysmutex_c.h b/src/thread/n3ds/SDL_sysmutex_c.h
new file mode 100644
index 000000000..f868fade8
--- /dev/null
+++ b/src/thread/n3ds/SDL_sysmutex_c.h
@@ -0,0 +1,22 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/thread/n3ds/SDL_syssem.c b/src/thread/n3ds/SDL_syssem.c
new file mode 100644
index 000000000..4f9f893c5
--- /dev/null
+++ b/src/thread/n3ds/SDL_syssem.c
@@ -0,0 +1,132 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+//#include "../../SDL_internal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "SDL_config.h"
+
+#include "SDL_error.h"
+#include "SDL_thread.h"
+
+#include <3ds.h>
+
+struct SDL_semaphore {
+    Handle  semid;
+};
+
+
+/* Create a semaphore */
+SDL_sem *SDL_CreateSemaphore(Uint32 initial_value)
+{
+    SDL_sem *sem;
+
+    sem = (SDL_sem *) malloc(sizeof(*sem));
+    if (sem != NULL) {
+        /* TODO: Figure out the limit on the maximum value. */
+        svcCreateSemaphore(&sem->semid, initial_value, 255);
+        if (sem->semid < 0) {
+            SDL_SetError("Couldn't create semaphore");
+            free(sem);
+            sem = NULL;
+        }
+    } else {
+        SDL_OutOfMemory();
+    }
+
+    return sem;
+}
+
+/* Free the semaphore */
+void SDL_DestroySemaphore(SDL_sem *sem)
+{
+    if (sem != NULL) {
+        if (sem->semid > 0) {
+	    svcCloseHandle(sem->semid);
+            sem->semid = 0;
+        }
+
+        free(sem);
+    }
+}
+
+/* TODO: This routine is a bit overloaded.
+ * If the timeout is 0 then just poll the semaphore; if it's SDL_MUTEX_MAXWAIT, pass
+ * NULL to sceKernelWaitSema() so that it waits indefinitely; and if the timeout
+ * is specified, convert it to microseconds. */
+int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
+{
+    unsigned int res;
+
+    if (sem == NULL) {
+        SDL_SetError("Passed a NULL sem");
+        return 0;
+    }
+
+    res = svcWaitSynchronization(sem->semid, (timeout == SDL_MUTEX_MAXWAIT) ? U64_MAX : (signed long long)timeout*1000000);
+
+    return (res == 0) ? 0 : SDL_MUTEX_TIMEDOUT;
+}
+
+int SDL_SemTryWait(SDL_sem *sem)
+{
+    return SDL_SemWaitTimeout(sem, 0);
+}
+
+int SDL_SemWait(SDL_sem *sem)
+{
+    return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT);
+}
+
+/* Returns the current count of the semaphore */
+Uint32 SDL_SemValue(SDL_sem *sem)
+{
+    Sint32 value;
+
+    if (sem == NULL) {
+        SDL_SetError("Passed a NULL sem");
+        return 0;
+    }
+
+    svcReleaseSemaphore(&value, sem->semid, 0);
+
+    return (Uint32)value;
+}
+
+int SDL_SemPost(SDL_sem *sem)
+{
+    int res;
+    Sint32 count;
+
+    if (sem == NULL) {
+        SDL_SetError("Passed a NULL sem");
+        return -1;                
+    }
+
+    res = svcReleaseSemaphore(&count, sem->semid, 1);
+    if (res < 0) {
+        SDL_SetError("svcReleaseSemaphore() failed");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/src/thread/n3ds/SDL_systhread.c b/src/thread/n3ds/SDL_systhread.c
new file mode 100644
index 000000000..afbda60e6
--- /dev/null
+++ b/src/thread/n3ds/SDL_systhread.c
@@ -0,0 +1,93 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+	  claim that you wrote the original software. If you use this software
+	  in a product, an acknowledgment in the product documentation would be
+	  appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+	  misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include "SDL_config.h"
+
+#include "SDL_error.h"
+#include "SDL_thread.h"
+#include "../SDL_systhread.h"
+#include "../SDL_thread_c.h"
+#include <3ds.h>
+
+#define CURRENT_KTHREAD 0xFFFF8000
+#define STACKSIZE       (32 * 1024)
+#define APPCORE_CPUID   0
+
+void ThreadEntry(void *arg)
+{
+	SDL_RunThread(arg);
+}
+
+int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
+{
+//	s32 priority = 0x2F;
+	s32 priority = 0x30;
+
+	/* Set priority of new thread higher than the current thread */
+	svcGetThreadPriority(&priority, CURRENT_KTHREAD);
+	if(priority>0x19) priority--;
+	else priority = 0x19; //priority 0x18 is for video thread that is activated by a signal and than must run at maximum priority to avoid flickering
+	if(priority>0x2F) priority = 0x2F;
+
+	thread->handle = threadCreate(ThreadEntry, args,
+		STACKSIZE, priority, -2, false);
+
+	thread->threadid = (int) thread->handle;
+	if (!thread->threadid)
+	{
+	SDL_SetError("Create Thread failed");
+	return(-1);
+	}
+ 
+	return 0;
+}
+
+void SDL_SYS_SetupThread(void)
+{
+	 //Nothing, probably
+}
+
+Uint32 SDL_ThreadID(void)
+{
+	//Incompatible with SDL API, this function will NOT return
+	//a valid thread ID when called from the main thread.
+	Thread current = threadGetCurrent();
+	Handle handle = threadGetHandle(current);
+	u32 threadID;
+	svcGetThreadId(&threadID, handle);
+
+	return threadID;
+}
+
+void SDL_SYS_WaitThread(SDL_Thread *thread)
+{
+	threadJoin(thread->handle, U64_MAX);
+}
+
+void SDL_SYS_KillThread(SDL_Thread *thread)
+{
+	threadFree(thread->handle);
+}
+
diff --git a/src/thread/n3ds/SDL_systhread_c.h b/src/thread/n3ds/SDL_systhread_c.h
new file mode 100644
index 000000000..1baf0d721
--- /dev/null
+++ b/src/thread/n3ds/SDL_systhread_c.h
@@ -0,0 +1,24 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include <3ds.h>
+
+typedef Thread SYS_ThreadHandle;
diff --git a/src/timer/n3ds/SDL_systimer.c b/src/timer/n3ds/SDL_systimer.c
new file mode 100644
index 000000000..e0958dba9
--- /dev/null
+++ b/src/timer/n3ds/SDL_systimer.c
@@ -0,0 +1,74 @@
+#include "SDL_config.h"
+
+#include <sys/time.h>
+#include <3ds.h>
+#include <sys/select.h>
+
+
+#include "SDL_thread.h"
+#include "SDL_timer.h"
+#include "SDL_error.h"
+#include "../SDL_timer_c.h"
+
+static struct timeval start;
+
+void SDL_StartTicks (void) {
+	gettimeofday (&start, NULL);
+}
+
+Uint32 SDL_GetTicks (void) {
+	Uint32 ticks;
+	struct timeval now;
+
+	gettimeofday (&now, NULL);
+	ticks = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_usec - start.tv_usec) / 1000;
+
+	return (ticks);
+}
+
+void SDL_Delay (Uint32 ms) {
+#ifdef SDL_THREAD_N3DS
+	if (threadGetCurrent() != NULL)
+		svcSleepThread((Uint64)ms * 1000000);
+	else {
+#endif
+	int was_error;
+	struct timeval tv;
+	Uint32 then, now, elapsed;
+
+	then = SDL_GetTicks();
+
+	do {
+		now = SDL_GetTicks ();
+		elapsed = (now - then);
+		then = now;
+		if (elapsed >= ms) {
+			break;
+		}
+		ms -= elapsed;
+
+		tv.tv_sec = ms / 1000;
+		tv.tv_usec = (ms % 1000) * 1000;
+
+		was_error = select(0, NULL, NULL, NULL, &tv);
+	} while (was_error);
+#ifdef SDL_THREAD_N3DS
+	}
+#endif
+}
+
+int SDL_SYS_TimerInit (void) {
+	return 0;
+}
+
+void SDL_SYS_TimerQuit (void) {
+}
+
+int SDL_SYS_StartTimer (void) {
+	SDL_SetError ("Timers not implemented on 3DS");
+
+	return -1;
+}
+
+void SDL_SYS_StopTimer (void) {
+}
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 436450e33..50735f447 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -398,6 +398,9 @@ extern VideoBootStrap DC_bootstrap;
 #if SDL_VIDEO_DRIVER_NDS
 extern VideoBootStrap NDS_bootstrap;
 #endif
+#if SDL_VIDEO_DRIVER_N3DS
+extern VideoBootStrap N3DS_bootstrap;
+#endif
 #if SDL_VIDEO_DRIVER_RISCOS
 extern VideoBootStrap RISCOS_bootstrap;
 #endif
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 46285c990..94620ad04 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -114,6 +114,9 @@ static VideoBootStrap *bootstrap[] = {
 #if SDL_VIDEO_DRIVER_NDS
 	&NDS_bootstrap,
 #endif
+#if SDL_VIDEO_DRIVER_N3DS
+	&N3DS_bootstrap,
+#endif
 #if SDL_VIDEO_DRIVER_RISCOS
 	&RISCOS_bootstrap,
 #endif
diff --git a/src/video/dummy/SDL_nullvideo.c b/src/video/dummy/SDL_nullvideo.c
index 7e096e2f7..23af477cc 100644
--- a/src/video/dummy/SDL_nullvideo.c
+++ b/src/video/dummy/SDL_nullvideo.c
@@ -68,12 +68,15 @@ static void DUMMY_UpdateRects(_THIS, int numrects, SDL_Rect *rects);
 
 static int DUMMY_Available(void)
 {
+	return(1);
+/*	
 	const char *envr = SDL_getenv("SDL_VIDEODRIVER");
 	if ((envr) && (SDL_strcmp(envr, DUMMYVID_DRIVER_NAME) == 0)) {
 		return(1);
 	}
 
 	return(0);
+*/
 }
 
 static void DUMMY_DeleteDevice(SDL_VideoDevice *device)
diff --git a/src/video/n3ds/SDL_n3dsevents.c b/src/video/n3ds/SDL_n3dsevents.c
new file mode 100644
index 000000000..6ad575b20
--- /dev/null
+++ b/src/video/n3ds/SDL_n3dsevents.c
@@ -0,0 +1,188 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2012 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/* Being a null driver, there's no event stream. We just define stubs for
+   most of the API. */
+
+#include <3ds.h>
+
+#include "SDL.h"
+#include "../../events/SDL_sysevents.h"
+#include "../../events/SDL_events_c.h"
+
+#include "SDL_n3dsvideo.h"
+#include "SDL_n3dsevents_c.h"
+
+static aptHookCookie cookie;
+volatile bool app_pause = false;
+volatile bool app_exiting = false;
+
+static void task_apt_hook(APT_HookType hook, void* param) {
+    
+	switch(hook) {
+		case APTHOOK_ONSUSPEND:
+			app_pause = true;
+            break;
+        case APTHOOK_ONRESTORE:
+			app_pause = false;
+			app_exiting = false;
+            break;
+        case APTHOOK_ONEXIT:
+			app_exiting = true;
+            break;
+        default:
+            break;
+    }
+}
+
+void task_init() {
+    aptHook(&cookie, task_apt_hook, NULL);
+	app_pause = false;
+	app_exiting = false;
+}
+
+void task_exit() {
+    aptUnhook(&cookie);
+}
+
+static SDLKey keymap[N3DS_NUMKEYS];
+char keymem[N3DS_NUMKEYS];
+
+void N3DS_PumpEvents(_THIS)
+{
+	svcSleepThread(100000); // 0.1 ms
+	
+	if (app_pause) return;
+	
+	if(!aptMainLoop())
+	{
+		SDL_Event sdlevent;
+		sdlevent.type = SDL_QUIT;
+		SDL_PushEvent(&sdlevent);
+	} 
+	
+	hidScanInput();
+
+	int i;
+	SDL_keysym keysym;
+	keysym.mod = KMOD_NONE;
+
+	for (i = 0; i < N3DS_NUMKEYS; i++) {
+		keysym.scancode = i;
+		keysym.sym = keymap[i];
+
+		if (hidKeysHeld() & (1 << i) && !keymem[i]) {
+			keymem[i] = 1;
+
+			SDL_PrivateKeyboard (SDL_PRESSED, &keysym);
+		}
+
+		if (!(hidKeysHeld() & (1 << i)) && keymem[i]) {
+			keymem[i] = 0;
+
+			SDL_PrivateKeyboard (SDL_RELEASED, &keysym);
+		}
+	}
+
+	if (hidKeysHeld() & KEY_TOUCH) {
+		touchPosition touch;
+
+		hidTouchRead (&touch);
+		
+// TO DO: handle fit screen on x and y.Y and Y to be considered separately
+		
+		if(this->hidden->screens&SDL_TOPSCR && this->hidden->screens&SDL_BOTTOMSCR) {
+			if (touch.px != 0 || touch.py != 0) {
+				SDL_PrivateMouseMotion (0, 0, 
+					touch.px  + (this->hidden->w1 - 320)/2, 
+					this->hidden->y2 + touch.py + (this->hidden->h2 - 240)/2);
+				if (!SDL_GetMouseState (NULL, NULL))
+					SDL_PrivateMouseButton (SDL_PRESSED, 1, 0, 0);
+			}
+		} else {
+			if (touch.px != 0 || touch.py != 0) {
+				SDL_PrivateMouseMotion (0, 0, (touch.px * this->hidden->w1) / 320, (touch.py * this->hidden->h1) / 240);
+				if (!SDL_GetMouseState (NULL, NULL))
+					SDL_PrivateMouseButton (SDL_PRESSED, 1, 0, 0);
+			}
+		}
+	} else {
+		if (SDL_GetMouseState (NULL, NULL))
+			SDL_PrivateMouseButton (SDL_RELEASED, 1, 0, 0);
+	}
+}
+
+void N3DS_InitOSKeymap(_THIS)
+{
+	SDL_memset(keymem,1,N3DS_NUMKEYS);
+	keymap[0]=SDLK_a; //KEY_A
+	keymap[1]=SDLK_b; // KEY_B
+	keymap[2]=SDLK_ESCAPE; //KEY_SELECT
+	keymap[3]=SDLK_RETURN; //KEY_START
+	keymap[4]=SDLK_RIGHT; //KEY_RIGHT
+	keymap[5]=SDLK_LEFT; //KEY_LEFT
+	keymap[6]=SDLK_UP; // KEY_UP
+	keymap[7]=SDLK_DOWN; //KEY_DOWN
+	keymap[8]=SDLK_r; //KEY_R
+	keymap[9]=SDLK_l; //KEY_L
+	keymap[10]=SDLK_x; //KEY_X 
+	keymap[11]=SDLK_y; //KEY_Y 
+	keymap[12]=SDLK_UNKNOWN; 
+	keymap[13]=SDLK_UNKNOWN; 
+	keymap[14]=SDLK_LSHIFT;  //KEY_ZL 
+	keymap[15]=SDLK_RSHIFT;  //KEY_ZR
+	keymap[16]=SDLK_UNKNOWN; 
+	keymap[17]=SDLK_UNKNOWN; 
+	keymap[18]=SDLK_UNKNOWN; 
+	keymap[19]=SDLK_UNKNOWN; 
+	keymap[20]=SDLK_UNKNOWN; 
+	keymap[21]=SDLK_UNKNOWN; 
+	keymap[22]=SDLK_UNKNOWN; 
+	keymap[23]=SDLK_UNKNOWN; 
+	keymap[24]=SDLK_UNKNOWN; 
+	keymap[25]=SDLK_UNKNOWN; 
+	keymap[26]=SDLK_UNKNOWN; 
+	keymap[27]=SDLK_UNKNOWN; 
+	keymap[28]=SDLK_UNKNOWN; 
+	keymap[29]=SDLK_UNKNOWN; 
+	keymap[30]=SDLK_UNKNOWN; 
+	keymap[31]=SDLK_UNKNOWN; 
+
+// init the key state
+	int i;
+	hidScanInput();
+	for (i = 0; i < N3DS_NUMKEYS; i++)
+		keymem[i] = (hidKeysHeld() & (1 << i))?1:0;
+}
+
+void SDL_N3DSKeyBind(unsigned int hidkey, SDLKey key) {
+	int i,pos;
+	for (i = 0; i < N3DS_NUMKEYS; i++) {
+		pos= 1<<i;
+		if(hidkey&pos) keymap[i]=key;
+	}
+}
+
+
+/* end of SDL_nullevents.c ... */
+
diff --git a/src/video/n3ds/SDL_n3dsevents_c.h b/src/video/n3ds/SDL_n3dsevents_c.h
new file mode 100644
index 000000000..4f485474c
--- /dev/null
+++ b/src/video/n3ds/SDL_n3dsevents_c.h
@@ -0,0 +1,38 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2012 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#include "SDL_config.h"
+
+#include "SDL_n3dsvideo.h"
+
+#define N3DS_NUMKEYS 32
+/* Variables and functions exported by SDL_sysevents.c to other parts 
+   of the native video subsystem (SDL_sysvideo.c)
+*/
+extern void N3DS_InitOSKeymap(_THIS);
+extern void N3DS_PumpEvents(_THIS);
+
+void task_init();
+void task_exit();
+
+/* end of SDL_nullevents_c.h ... */
+
diff --git a/src/video/n3ds/SDL_n3dsmouse.c b/src/video/n3ds/SDL_n3dsmouse.c
new file mode 100644
index 000000000..1da84a4e6
--- /dev/null
+++ b/src/video/n3ds/SDL_n3dsmouse.c
@@ -0,0 +1,33 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2012 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#include "SDL_mouse.h"
+#include "../../events/SDL_events_c.h"
+
+#include "SDL_n3dsmouse_c.h"
+
+
+/* The implementation dependent data for the window manager cursor */
+struct WMcursor {
+	int unused;
+};
diff --git a/src/video/n3ds/SDL_n3dsmouse_c.h b/src/video/n3ds/SDL_n3dsmouse_c.h
new file mode 100644
index 000000000..cdbdd17ce
--- /dev/null
+++ b/src/video/n3ds/SDL_n3dsmouse_c.h
@@ -0,0 +1,26 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2012 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#include "SDL_n3dsvideo.h"
+
+/* Functions to be exported */
diff --git a/src/video/n3ds/SDL_n3dsvideo.c b/src/video/n3ds/SDL_n3dsvideo.c
new file mode 100644
index 000000000..e8aaa6973
--- /dev/null
+++ b/src/video/n3ds/SDL_n3dsvideo.c
@@ -0,0 +1,759 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2012 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#include "SDL_video.h"
+#include "SDL_mouse.h"
+#include "../SDL_sysvideo.h"
+#include "../SDL_pixels_c.h"
+#include "../../events/SDL_events_c.h"
+
+#include "SDL_thread.h"
+#include "SDL_timer.h"
+
+#include <3ds.h>
+#include <citro3d.h>
+
+#include "SDL_n3dsvideo.h"
+#include "SDL_n3dsevents_c.h"
+#include "SDL_n3dsmouse_c.h"
+
+#define N3DSVID_DRIVER_NAME "n3ds"
+
+static Uint32 n3ds_palette[256] = {0};
+
+/* Low level N3ds */
+
+// This header is generated by the build process
+#include "vshader_shbin.h"
+
+#define STACKSIZE (4 * 1024)
+
+#define TEX_MIN_SIZE 32
+#define CLEAR_COLOR0 0x000000FF
+#define CLEAR_COLOR1 0x000000
+#define CLEAR_COLOR2 0x000000
+#define CLEAR_COLOR3 0x000001
+
+// Used to transfer the final rendered display to the framebuffer
+#define DISPLAY_TRANSFER_FLAGS0 \
+	(GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \
+	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)) 
+#define DISPLAY_TRANSFER_FLAGS1 \
+	(GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \
+	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8)) 
+#define DISPLAY_TRANSFER_FLAGS2 \
+	(GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \
+	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB5A1) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565))
+#define DISPLAY_TRANSFER_FLAGS3 \
+	(GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \
+	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB5A1) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565))
+
+// Used to convert textures to 3DS tiled format
+// Note: vertical flip flag set so 0,0 is top left of texture
+#define TEXTURE_TRANSFER_FLAGS0 \
+	(GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \
+	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | \
+	GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
+#define TEXTURE_TRANSFER_FLAGS1 \
+	(GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \
+	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \
+	GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
+#define TEXTURE_TRANSFER_FLAGS2 \
+	(GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \
+	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB5A1) | \
+	GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
+#define TEXTURE_TRANSFER_FLAGS3 \
+	(GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \
+	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB5A1) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB5A1) | \
+	GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
+
+static DVLB_s* vshader_dvlb;
+static shaderProgram_s program;
+static int uLoc_projection;
+static C3D_Mtx projection;
+static C3D_Mtx projection2;
+
+C3D_RenderTarget *VideoSurface1;
+C3D_RenderTarget *VideoSurface2;
+static C3D_Tex spritesheet_tex;
+
+static int textureTranferFlags[4] = { TEXTURE_TRANSFER_FLAGS0, TEXTURE_TRANSFER_FLAGS1, TEXTURE_TRANSFER_FLAGS2, TEXTURE_TRANSFER_FLAGS3};
+static int displayTranferFlags[4] = { DISPLAY_TRANSFER_FLAGS0, DISPLAY_TRANSFER_FLAGS1, DISPLAY_TRANSFER_FLAGS2, DISPLAY_TRANSFER_FLAGS3};
+static unsigned int clearcolors[4] = { CLEAR_COLOR0, CLEAR_COLOR1, CLEAR_COLOR2, CLEAR_COLOR3};
+
+static void sceneInit(GSPGPU_FramebufferFormats mode, bool scale);
+static void sceneExit(void);
+void drawTexture( int x, int y, int width, int height, float left, float right, float top, float bottom);
+
+// video thread variables and functions
+volatile bool runThread = false;
+Handle privateVideoThreadRequest;
+Thread privateVideoThreadHandle = NULL;
+static void videoThread(void* data);
+extern volatile bool app_pause;
+extern volatile bool app_exiting;
+
+/* Initialization/Query functions */
+static int N3DS_VideoInit(_THIS, SDL_PixelFormat *vformat);
+static SDL_Rect **N3DS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
+static SDL_Surface *N3DS_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
+static int N3DS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
+static void N3DS_UpdateRects(_THIS, int numrects, SDL_Rect *rects);
+static void N3DS_VideoQuit(_THIS);
+
+/* Hardware surface functions */
+static int N3DS_AllocHWSurface(_THIS, SDL_Surface *surface);
+static int N3DS_LockHWSurface(_THIS, SDL_Surface *surface);
+static void N3DS_UnlockHWSurface(_THIS, SDL_Surface *surface);
+static void N3DS_FreeHWSurface(_THIS, SDL_Surface *surface);
+static int N3DS_FlipHWSurface (_THIS, SDL_Surface *surface); 
+
+int N3DS_ToggleFullScreen(_THIS, int on);
+
+
+//Copied from sf2dlib that grabbed it from: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+unsigned int next_pow2(unsigned int v)
+{
+	v--;
+	v |= v >> 1;
+	v |= v >> 2;
+	v |= v >> 4;
+	v |= v >> 8;
+	v |= v >> 16;
+	v++;
+	return v >= TEX_MIN_SIZE ? v : TEX_MIN_SIZE;
+}
+
+static int N3DS_Available(void)
+{
+	return(1); //what else?
+}
+
+static void N3DS_DeleteDevice(SDL_VideoDevice *device)
+{
+	if (device->hidden) SDL_free(device->hidden);
+	if (device) SDL_free(device);
+	device=NULL;
+}
+
+static SDL_VideoDevice *N3DS_CreateDevice(int devindex)
+{
+	SDL_VideoDevice *device;
+
+	/* Initialize all variables that we clean on shutdown */
+	device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
+	if ( device ) {
+		SDL_memset(device, 0, (sizeof *device));
+		device->hidden = (struct SDL_PrivateVideoData *)
+				SDL_malloc((sizeof *device->hidden));
+	}
+	if ( (device == NULL) || (device->hidden == NULL) ) {
+		SDL_OutOfMemory();
+		if ( device ) {
+			SDL_free(device);
+		}
+		return(0);
+	}
+
+	task_init();
+
+	SDL_memset(device->hidden, 0, (sizeof *device->hidden));
+	
+	/* Set the function pointers */
+	device->VideoInit = N3DS_VideoInit;
+	device->ListModes = N3DS_ListModes;
+	device->SetVideoMode = N3DS_SetVideoMode;
+	device->CreateYUVOverlay = NULL;
+	device->SetColors = N3DS_SetColors;
+	device->UpdateRects = N3DS_UpdateRects;
+	device->VideoQuit = N3DS_VideoQuit;
+	device->AllocHWSurface = N3DS_AllocHWSurface;
+	device->CheckHWBlit = NULL;
+	device->FillHWRect = NULL;
+	device->SetHWColorKey = NULL;
+	device->SetHWAlpha = NULL;
+	device->LockHWSurface = N3DS_LockHWSurface;
+	device->UnlockHWSurface = N3DS_UnlockHWSurface;
+	device->FlipHWSurface = N3DS_FlipHWSurface;
+	device->FreeHWSurface = N3DS_FreeHWSurface;
+	device->SetCaption = NULL;
+	device->SetIcon = NULL;
+	device->IconifyWindow = NULL;
+	device->GrabInput = NULL;
+	device->GetWMInfo = NULL;
+	device->InitOSKeymap = N3DS_InitOSKeymap;
+	device->PumpEvents = N3DS_PumpEvents;
+//	device->info.blit_hw = 1;
+
+	device->ToggleFullScreen = N3DS_ToggleFullScreen;
+
+	device->free = N3DS_DeleteDevice;
+
+	return device;
+}
+
+VideoBootStrap N3DS_bootstrap = {
+	N3DSVID_DRIVER_NAME, "N3ds video driver",
+	N3DS_Available, N3DS_CreateDevice
+};
+
+
+int N3DS_VideoInit(_THIS, SDL_PixelFormat *vformat)
+{
+	// Initialize graphics
+	gfxInitDefault();
+	gfxSet3D(false);
+	C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
+	
+	vformat->BitsPerPixel = 32;	
+	vformat->BytesPerPixel = 4;
+	vformat->Rmask = 0xff000000;
+	vformat->Gmask = 0x00ff0000;
+	vformat->Bmask = 0x0000ff00; 
+	vformat->Amask = 0x000000ff; 
+	
+	/* We're done! */
+	return(0);
+}
+
+SDL_Rect **N3DS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
+{
+   	 return (SDL_Rect **) -1;
+}
+
+void N3DS_SetScaling(_THIS)
+{
+	if(this->hidden->flags & SDL_FULLSCREEN) 
+//		flags |= (SDL_FITWIDTH | SDL_FITHEIGHT);
+		this->hidden->fitscreen = (SDL_FITWIDTH | SDL_FITHEIGHT);
+	else
+		this->hidden->fitscreen = this->hidden->flags & (SDL_FITWIDTH | SDL_FITHEIGHT);
+
+	if((this->hidden->fitscreen & SDL_FITWIDTH)&&(this->hidden->fitscreen & SDL_FITHEIGHT)) {
+		this->hidden->scalex= 400.0/(float)this->hidden->w1;
+		this->hidden->scaley= 240.0/(float)this->hidden->h1;
+	} else if(this->hidden->fitscreen & SDL_FITWIDTH) {
+		this->hidden->scalex= 400.0/(float)this->hidden->w1;
+		this->hidden->scaley= this->hidden->scalex;//1.0f;
+	} else 	if(this->hidden->fitscreen & SDL_FITHEIGHT) {
+		this->hidden->scaley= 240.0/(float)this->hidden->h1;
+		this->hidden->scalex= this->hidden->scaley;//1.0f;
+	} else {
+		this->hidden->scalex= 1.0f;
+		this->hidden->scaley= 1.0f;
+	}
+}
+
+int N3DS_ToggleFullScreen(_THIS, int on){
+   	if ((this->hidden->flags & SDL_FITWIDTH) && (this->hidden->flags & SDL_FITHEIGHT)) 
+		return -1;
+	
+	if ( this->hidden->flags & SDL_FULLSCREEN )
+		this->hidden->flags &= ~SDL_FULLSCREEN;
+	else 
+		this->hidden->flags |= SDL_FULLSCREEN;
+
+	N3DS_SetScaling(this);
+	return 1;
+}
+
+Uint32 setVideoModecount = 0;
+
+SDL_Surface *N3DS_SetVideoMode(_THIS, SDL_Surface *current,
+				int width, int height, int bpp, Uint32 flags)
+{
+
+Uint32 Rmask, Gmask, Bmask, Amask; 
+int hw = next_pow2(width);
+int hh= next_pow2(height);
+
+	setVideoModecount++;
+
+	this->hidden->screens = flags & (SDL_DUALSCR); // SDL_DUALSCR = SDL_TOPSCR | SDL_BOTTOMSCR
+	if(this->hidden->screens==0) this->hidden->screens = SDL_TOPSCR; //Default
+	flags &= ~SDL_DUALSCR;
+	flags |= this->hidden->screens;
+	
+	this->hidden->flags = flags;
+	
+	switch(bpp) {
+		case 0:
+			bpp = 32;
+		case 32:
+			Rmask = 0xff000000; 
+			Gmask = 0x00ff0000;
+			Bmask = 0x0000ff00;
+			Amask = 0x000000ff;
+			this->hidden->mode=GSP_RGBA8_OES;
+			this->hidden->byteperpixel=4;
+			this->hidden->bpp = 32;
+			break;
+		case 24:
+			Rmask = 0xff0000; 
+			Gmask = 0x00ff00;
+			Bmask = 0x0000ff;
+			Amask = 0x0;
+			this->hidden->mode=GSP_BGR8_OES;
+			this->hidden->byteperpixel=3;
+			this->hidden->bpp = 24;
+			break;
+		case 16:
+			Rmask = 0xF800;
+            Gmask = 0x07E0;
+            Bmask = 0x001F;
+            Amask = 0x0000;
+			this->hidden->mode=GSP_RGB565_OES;
+			this->hidden->byteperpixel=2;
+			this->hidden->bpp = 16;
+			break;
+		case 15:
+			bpp = 16;
+			Rmask = 0xF800;
+			Gmask = 0x07C0;
+			Bmask = 0x003E;
+			Amask = 0x0001;
+			this->hidden->mode=GSP_RGB5_A1_OES;
+			this->hidden->byteperpixel=2;
+			this->hidden->bpp = 16;
+			break;
+		case 8:
+			Rmask = 0;
+			Gmask = 0;
+			Bmask = 0;
+			Amask = 0;
+			this->hidden->mode=GSP_RGBA8_OES;
+			this->hidden->byteperpixel=4;
+			this->hidden->bpp = 8;
+			break;
+		default:
+			return NULL;
+			break;
+	}
+
+
+// if there is a video thread running, stop and free it
+	runThread = false;
+	if (privateVideoThreadHandle) {
+		svcSignalEvent(privateVideoThreadRequest);
+		threadJoin(privateVideoThreadHandle, U64_MAX);
+		privateVideoThreadHandle = NULL;
+	}
+
+	if ( this->hidden->buffer ) {
+		linearFree( this->hidden->buffer );
+		this->hidden->buffer = NULL;
+	}
+	if ( this->hidden->palettedbuffer ) {
+		free( this->hidden->palettedbuffer );
+		this->hidden->palettedbuffer = NULL;
+	}
+
+	this->hidden->buffer = (u8*) linearAlloc(hw * hh * this->hidden->byteperpixel);
+	if ( ! this->hidden->buffer ) {
+		SDL_SetError("Couldn't allocate buffer for requested mode");
+		return(NULL);
+	}
+
+	SDL_memset(this->hidden->buffer, 0, hw * hh * this->hidden->byteperpixel);
+
+	if(bpp==8) {
+		this->hidden->palettedbuffer = malloc(width * height);
+		if ( ! this->hidden->palettedbuffer ) {
+			SDL_SetError("Couldn't allocate buffer for requested mode");
+			linearFree(this->hidden->buffer);
+			return(NULL);
+		}
+		SDL_memset(this->hidden->palettedbuffer, 0, width * height);
+	}
+
+	/* Allocate the new pixel format for the screen */
+	if ( ! SDL_ReallocFormat(current, bpp, Rmask, Gmask, Bmask, Amask) ) {
+		linearFree(this->hidden->buffer);
+		this->hidden->buffer = NULL;
+		SDL_SetError("Couldn't allocate new pixel format for requested mode");
+		return(NULL);
+	}
+
+	if(setVideoModecount>1) sceneExit(); // unload rendertarget and the shader
+	
+	//setup the screens mode
+	sceneInit(this->hidden->mode, (this->hidden->fitscreen & (SDL_FITWIDTH | SDL_FITHEIGHT)) ? false:true);
+
+	if((flags & SDL_CONSOLETOP) && !(this->hidden->screens & SDL_TOPSCR)) {
+		consoleInit(GFX_TOP, NULL);
+		this->hidden->console = SDL_CONSOLETOP;
+		flags &= ~SDL_CONSOLEBOTTOM;
+		// todo here: setup bottom video for graphics;
+	} else if((flags & SDL_CONSOLEBOTTOM) && !(this->hidden->screens & SDL_BOTTOMSCR)) {
+		consoleInit(GFX_BOTTOM, NULL);
+		this->hidden->console = SDL_CONSOLEBOTTOM;
+	}
+	/* Set up the new mode framebuffer */
+	current->flags =  SDL_HWSURFACE | SDL_DOUBLEBUF;
+	this->hidden->w = hw;
+	this->hidden->h = hh;
+
+	this->hidden->x1 = 0;
+	this->hidden->y1 = 0;
+	this->hidden->x2 = 0;
+	this->hidden->y2 = 0;
+	if((this->hidden->screens & SDL_TOPSCR) && (this->hidden->screens & SDL_BOTTOMSCR)){
+		this->hidden->w1 = width;
+		this->hidden->h1 = height/2;
+		this->hidden->w2 = width;
+		this->hidden->h2 = height/2;
+		this->hidden->y2 = height/2;
+	} else {
+		this->hidden->w1 = width;
+		this->hidden->h1 = height;
+		this->hidden->w2 = width;
+		this->hidden->h2 = height;
+	}
+
+	this->hidden->l1 = 0.0f;
+	this->hidden->t1 = 0.0f;
+	this->hidden->r1 = (float)this->hidden->w1/(float)this->hidden->w;
+	this->hidden->b1 = (float)this->hidden->h1/(float)this->hidden->h;
+	this->hidden->l2 = 0.0f;
+	this->hidden->t2 = (float)this->hidden->y2/(float)this->hidden->h;
+	this->hidden->r2 = (float)this->hidden->w2/(float)this->hidden->w;
+	this->hidden->b2 = ((float)this->hidden->y2+(float)this->hidden->h2)/(float)this->hidden->h;
+
+//Set scaling
+
+	N3DS_SetScaling(this);
+	
+	this->info.current_w = current->w = width;
+	this->info.current_h = current->h = height;
+	if(bpp>8) {
+		current->pixels = this->hidden->buffer;
+		current->pitch = hw * this->hidden->byteperpixel;
+	} else {
+		current->pixels = this->hidden->palettedbuffer;
+		current->pitch = width;
+	}
+
+// NOTE: the following is a dirty hack to make work mode 2. Passing a RGB565 buffer to a RGB656 texture and rendering it to a RGB656 display results in some colors being transparent.
+// To fix this we are transfering a RGB656 buffer to a RGB5A1 texure and then rendering it to a RGB656 display. We lose 1 bit of Green color depth, but at least it works
+	int mode = this->hidden->mode;
+	if (mode==2) mode = 3; 
+
+	// Setup the textures
+	C3D_TexInit(&spritesheet_tex, hw, hh, this->hidden->mode);
+	C3D_TexSetFilter(&spritesheet_tex, GPU_LINEAR, GPU_NEAREST);
+//	C3D_TexBind(0, &spritesheet_tex);
+	
+	runThread = true;
+	svcCreateEvent(&privateVideoThreadRequest,0);
+ // ctrulib sys threads uses 0x18, so we use a lower priority, but higher than any other SDL thread
+	privateVideoThreadHandle = threadCreate(videoThread, (void *) this, STACKSIZE, 0x19, -2, true);
+	this->hidden->currentVideoSurface = current; 
+	/* We're done */
+
+	return(current);
+}
+
+/* We don't actually allow hardware surfaces other than the main one */
+static int N3DS_AllocHWSurface(_THIS, SDL_Surface *surface)
+{
+	return(-1);
+}
+static void N3DS_FreeHWSurface(_THIS, SDL_Surface *surface)
+{
+	return;
+}
+
+/* We need to wait for vertical retrace on page flipped displays */
+static int N3DS_LockHWSurface(_THIS, SDL_Surface *surface)
+{
+	return(0);
+}
+
+static void N3DS_UnlockHWSurface(_THIS, SDL_Surface *surface)
+{
+	return;
+}
+
+static unsigned int RenderClearColor;
+
+static void videoThread(void* data)
+{
+    _THIS = (SDL_VideoDevice *) data;
+
+	do {
+		if(!app_pause && !app_exiting) { 
+//			if (C3D_FrameBegin(C3D_FRAME_SYNCDRAW)){ 
+			if (C3D_FrameBegin(C3D_FRAME_NONBLOCK)){ 
+				if (this->hidden->screens & SDL_TOPSCR) {
+					C3D_RenderTargetClear(VideoSurface1, C3D_CLEAR_ALL, RenderClearColor, 0);
+					C3D_FrameDrawOn(VideoSurface1);
+					C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);
+					drawTexture((400-this->hidden->w1*this->hidden->scalex)/2,(240-this->hidden->h1*this->hidden->scaley)/2, this->hidden->w1*this->hidden->scalex, this->hidden->h1*this->hidden->scaley, this->hidden->l1, this->hidden->r1, this->hidden->t1, this->hidden->b1);  
+				}
+				if (this->hidden->screens & SDL_BOTTOMSCR) {
+					C3D_RenderTargetClear(VideoSurface2, C3D_CLEAR_ALL, RenderClearColor, 0);
+					C3D_FrameDrawOn(VideoSurface2);
+					C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection2);
+					if (this->hidden->fitscreen & SDL_FITWIDTH)
+						drawTexture(0,(240-this->hidden->h2*this->hidden->scaley)/2, this->hidden->w2*this->hidden->scalex, this->hidden->h2*this->hidden->scaley, this->hidden->l2, this->hidden->r2, this->hidden->t2, this->hidden->b2);  
+					else
+						drawTexture((400-this->hidden->w2*this->hidden->scalex*1.25)/2,(240-this->hidden->h2*this->hidden->scaley)/2, this->hidden->w2*this->hidden->scalex*1.25, this->hidden->h2*this->hidden->scaley, this->hidden->l2, this->hidden->r2, this->hidden->t2, this->hidden->b2);  
+				}
+				C3D_FrameEnd(0);
+			} 
+		} 
+
+		svcWaitSynchronization(privateVideoThreadRequest, U64_MAX);
+		svcClearEvent(privateVideoThreadRequest);
+	} while(runThread); 
+	threadExit(0);
+}
+
+static void drawBuffers(_THIS)
+{
+	if(this->hidden->buffer) {
+
+		if(app_pause || app_exiting) return; // Blocking video output if the application is closing 
+	
+		GSPGPU_FlushDataCache(this->hidden->buffer, this->hidden->w*this->hidden->h*this->hidden->byteperpixel);
+
+		C3D_SyncDisplayTransfer ((u32*)this->hidden->buffer, GX_BUFFER_DIM(this->hidden->w, this->hidden->h), (u32*)spritesheet_tex.data, GX_BUFFER_DIM(this->hidden->w, this->hidden->h), textureTranferFlags[this->hidden->mode]);
+
+		GSPGPU_FlushDataCache(spritesheet_tex.data, this->hidden->w*this->hidden->h*this->hidden->byteperpixel);
+
+		C3D_TexBind(0, &spritesheet_tex);
+		// Configure the first fragment shading substage to just pass through the texture color
+		// See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight
+		C3D_TexEnv* env = C3D_GetTexEnv(0);
+		C3D_TexEnvInit(env);
+		C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0);
+		
+		C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
+
+		gspWaitForVBlank();
+		svcSignalEvent(privateVideoThreadRequest); 
+	}
+}
+
+static void N3DS_UpdateRects(_THIS, int numrects, SDL_Rect *rects)
+{
+	if(app_pause || app_exiting) return; //Block video output on quitting
+
+	if( this->hidden->bpp == 8) {
+/*
+		int i;
+		for(i=0; i< numrects; i++) {
+			SDL_Rect *rect = &rects[i];
+			Uint8 *src_addr, *dst_addr, *src_baseaddr, *dst_baseaddr;
+			int cols, rows;
+			int x,y;
+			src_baseaddr = this->hidden->palettedbuffer;
+			dst_baseaddr = this->hidden->buffer;
+			cols = (rect->x + rect->w > this->info.current_w) ? this->info.current_w - rect->x : rect->w;
+			rows = (rect->y + rect->h > this->info.current_h) ? this->info.current_h - rect->y : rect->h;
+			for(y=0;y<rows;y++) {
+			  x = 0;
+			  src_addr = src_baseaddr + rect->x + x + (rect->y + y) * this->info.current_w;
+			  dst_addr = dst_baseaddr + (rect->x + x + (rect->y + y) * this->hidden->w) * 4;
+			  for(;x<cols;x++) {
+				*(u32*)dst_addr=n3ds_palette[*(Uint8 *)src_addr];
+				src_addr++;
+				dst_addr += 4;
+}
+			}
+		} 
+*/
+		Uint8 *src_addr, *dst_addr, *dst_baseaddr;
+		src_addr = this->hidden->palettedbuffer;
+		dst_baseaddr = this->hidden->buffer;
+		int x,y;
+		for(y=0;y<this->info.current_h;y++) {
+  		  dst_addr = dst_baseaddr + y*this->hidden->w*4;
+		  for(x=0;x<this->info.current_w;x++) {
+			*(u32*)dst_addr=n3ds_palette[*(Uint8 *)src_addr];
+			src_addr++;
+			dst_addr += 4;
+		  }
+		}
+	
+	}
+
+	drawBuffers(this);
+}
+
+#define N3DS_MAP_RGB(r, g, b)	((Uint32)r << 24 | (Uint32)g << 16 | (Uint32)b << 8 | 0xff)
+
+static int N3DS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
+{
+	int i;
+	for ( i = firstcolor; i < firstcolor + ncolors; ++i )
+		n3ds_palette[i] = N3DS_MAP_RGB(colors[i].r, colors[i].g, colors[i].b);
+	return(1);
+}
+
+static int N3DS_FlipHWSurface (_THIS, SDL_Surface *surface) {
+
+	if(app_pause || app_exiting) return(0); //Block video output on quitting
+
+	if( this->hidden->bpp == 8) {
+		Uint8 *src_addr, *dst_addr, *dst_baseaddr;
+		src_addr = this->hidden->palettedbuffer;
+		dst_baseaddr = this->hidden->buffer;
+		int x,y;
+		for(y=0;y<this->info.current_h;y++) {
+  		  dst_addr = dst_baseaddr + y*this->hidden->w*4;
+		  for(x=0;x<this->info.current_w;x++) {
+			*(u32*)dst_addr=n3ds_palette[*(Uint8 *)src_addr];
+			src_addr++;
+			dst_addr += 4;
+		  }
+		}
+	}
+
+	drawBuffers(this);
+
+	return (0);
+}
+
+/* Note:  If we are terminated, this could be called in the middle of
+   another SDL video routine -- notably UpdateRects.
+*/
+void N3DS_VideoQuit(_THIS)
+{
+	if (privateVideoThreadHandle) {
+		runThread = false;
+		svcSignalEvent(privateVideoThreadRequest);
+		threadJoin(privateVideoThreadHandle, U64_MAX);
+		privateVideoThreadHandle = NULL;
+	}
+	if (this->hidden->buffer)
+	{
+		linearFree(this->hidden->buffer);
+		this->hidden->buffer = NULL;
+	}
+	if (this->hidden->palettedbuffer)
+	{
+		free(this->hidden->palettedbuffer);
+		this->hidden->palettedbuffer = NULL;
+	}
+	this->hidden->currentVideoSurface->pixels = NULL; // set to buffer or to palettedbuffer, so now pointing to not allocated memory
+	
+	sceneExit();
+	C3D_Fini();
+	gfxExit();
+	task_exit();
+}
+
+
+//---------------------------------------------------------------------------------
+static void sceneInit(GSPGPU_FramebufferFormats mode, bool scale) {
+//---------------------------------------------------------------------------------
+	// Load the vertex shader, create a shader program and bind it
+	vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size);
+	shaderProgramInit(&program);
+	shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]);
+	C3D_BindProgram(&program);
+
+	// Get the location of the uniforms
+	uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection");
+
+	// Configure attributes for use with the vertex shader
+	// Attribute format and element count are ignored in immediate mode
+	C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
+	AttrInfo_Init(attrInfo);
+	AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position
+	AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v2=texcoord
+
+	// Compute the projection matrix
+	// Note: we're setting top to 240 here so origin is at top left.
+	Mtx_OrthoTilt(&projection, 0.0, 400.0, 240.0, 0.0, 0.0, 1.0, true);
+	Mtx_OrthoTilt(&projection2, 0.0, 400.0, 240.0, 0.0, 0.0, 1.0, true);
+
+	// Configure buffers
+	C3D_BufInfo* bufInfo = C3D_GetBufInfo();
+	BufInfo_Init(bufInfo);
+
+	if (mode<=1) {
+		gfxSetScreenFormat(GFX_TOP, GSP_BGR8_OES);
+		gfxSetScreenFormat(GFX_BOTTOM, GSP_BGR8_OES);
+	} else	{
+		gfxSetScreenFormat(GFX_TOP, GSP_RGB565_OES);
+		gfxSetScreenFormat(GFX_BOTTOM, GSP_RGB565_OES);
+	}
+
+	RenderClearColor = clearcolors[mode];
+
+	// Initialize the top screen render target
+	if (scale)
+		VideoSurface1 = C3D_RenderTargetCreate(240*2, 400*2, mode, GPU_RB_DEPTH24_STENCIL8);
+	else
+		VideoSurface1 = C3D_RenderTargetCreate(240, 400, mode, GPU_RB_DEPTH24_STENCIL8);
+	if(scale)	
+		C3D_RenderTargetSetOutput(VideoSurface1, GFX_TOP, GFX_LEFT, displayTranferFlags[mode] | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_XY));
+	else
+		C3D_RenderTargetSetOutput(VideoSurface1, GFX_TOP, GFX_LEFT, displayTranferFlags[mode] | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO));
+	
+	// Initialize the bottom screen render target
+	if (scale)
+		VideoSurface2 = C3D_RenderTargetCreate(240*2, 320*2, mode, GPU_RB_DEPTH24_STENCIL8);
+	else
+		VideoSurface2 = C3D_RenderTargetCreate(240, 320, mode, GPU_RB_DEPTH24_STENCIL8);
+	if (scale)	
+		C3D_RenderTargetSetOutput(VideoSurface2, GFX_BOTTOM, GFX_LEFT, displayTranferFlags[mode] | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_XY));
+	else
+		C3D_RenderTargetSetOutput(VideoSurface2, GFX_BOTTOM, GFX_LEFT, displayTranferFlags[mode] | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO));
+
+	// Configure depth test to overwrite pixels with the same depth (needed to draw overlapping sprites)
+//	C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL);
+}
+
+//---------------------------------------------------------------------------------
+static void sceneExit(void) {
+//---------------------------------------------------------------------------------
+
+	if(VideoSurface1) C3D_RenderTargetDelete(VideoSurface1);
+	if (VideoSurface2) C3D_RenderTargetDelete(VideoSurface2);
+	// Free the shader program
+	shaderProgramFree(&program);
+	DVLB_Free(vshader_dvlb);
+}
+
+//---------------------------------------------------------------------------------
+void drawTexture( int x, int y, int width, int height, float left, float right, float top, float bottom) {
+//---------------------------------------------------------------------------------
+
+	// Draw a textured quad directly
+	C3D_ImmDrawBegin(GPU_TRIANGLE_STRIP);
+		C3D_ImmSendAttrib(x, y, 0.5f, 0.0f); // v0=position
+		C3D_ImmSendAttrib( left, top, 0.0f, 0.0f);
+
+		C3D_ImmSendAttrib(x, y+height, 0.5f, 0.0f);
+		C3D_ImmSendAttrib( left, bottom, 0.0f, 0.0f);
+
+		C3D_ImmSendAttrib(x+width, y, 0.5f, 0.0f);
+		C3D_ImmSendAttrib( right, top, 0.0f, 0.0f);
+
+		C3D_ImmSendAttrib(x+width, y+height, 0.5f, 0.0f);
+		C3D_ImmSendAttrib( right, bottom, 0.0f, 0.0f);
+	C3D_ImmDrawEnd();
+}
diff --git a/src/video/n3ds/SDL_n3dsvideo.h b/src/video/n3ds/SDL_n3dsvideo.h
new file mode 100644
index 000000000..23749ff94
--- /dev/null
+++ b/src/video/n3ds/SDL_n3dsvideo.h
@@ -0,0 +1,63 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2012 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#ifndef _SDL_n3dsvideo_h
+#define _SDL_n3dsvideo_h
+
+#include "../SDL_sysvideo.h"
+
+#include <3ds.h>
+
+
+/* Hidden "this" pointer for the video functions */
+#define _THIS	SDL_VideoDevice *this
+
+/* Private display data */
+
+struct SDL_PrivateVideoData {
+//GPU drawing specific
+	int x1,y1,w1,h1; // drawing window for top screen          width and height of top screen part of the video buffer 
+	int x2,y2,w2,h2; //drawing window for bottom screen  and height of bottom part of the video buffer
+	float l1,r1,t1,b1; // GPU source window for the top part of the video buffer
+	float l2,r2,t2,b2; // GPU source window for the bottom part of the video buffer
+	float scalex,scaley; // scaling factors
+	float scalex2,scaley2; // scaling factors
+
+// framebuffer data
+    int w, h; // width and height of the video buffer
+    void *buffer;
+	Uint8 *palettedbuffer;
+	GSPGPU_FramebufferFormats mode;
+	unsigned int flags; // backup of create device flags
+	unsigned int screens; // SDL_TOPSCR, SDL_BOTTOMSCR, SDL_DUALSCR
+	unsigned int console; // SDL_CONSOLETOP, SDL_CONSOLEBOTTOM
+	unsigned int fitscreen; // SDL_TRIMBOTTOMSCR, SDL_FITWIDTH, SDL_FITHEIGHT (SDL_FULLSCREEN sets both SDL_FITWIDTH and SDL_FITHEIGHT)
+	int byteperpixel;
+	int bpp;
+// video surface
+	SDL_Surface* currentVideoSurface;	
+// Video process flags
+	bool blockVideo;  // block video output and events handlings on SDL_QUIT
+};
+
+#endif /* _SDL_n3dsvideo_h */
diff --git a/src/video/n3ds/vshader.pica b/src/video/n3ds/vshader.pica
new file mode 100644
index 000000000..ac5a64e5e
--- /dev/null
+++ b/src/video/n3ds/vshader.pica
@@ -0,0 +1,36 @@
+; Example PICA200 vertex shader
+
+; Uniforms
+.fvec projection[4]
+
+; Constants
+.constf myconst(0.0, 1.0, -1.0, 0.1)
+.constf myconst2(0.3, 0.0, 0.0, 0.0)
+.alias  zeros myconst.xxxx ; Vector full of zeros
+.alias  ones  myconst.yyyy ; Vector full of ones
+
+; Outputs
+.out outpos position
+.out outtc0 texcoord0
+
+; Inputs (defined as aliases for convenience)
+.alias inpos v0
+.alias intex v1
+
+.bool test
+
+.proc main
+	; Force the w component of inpos to be 1.0
+	mov r0.xyz, inpos
+	mov r0.w,   ones
+
+	; outpos = projectionMatrix * inpos
+	dp4 outpos.x, projection[0], r0
+	dp4 outpos.y, projection[1], r0
+	dp4 outpos.z, projection[2], r0
+	dp4 outpos.w, projection[3], r0
+
+ 	mov outtc0, intex
+
+	end
+.end
