diff --git a/AUTHORS b/AUTHORS
index 604bd6bdf5a8a..d8f9da02678e3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,4 +1,4 @@
-The following authors have all licensed their contributions to Emscripten
+The following authors have all licensed their contributions to Emscripten
 under the licensing terms detailed in LICENSE.
 
 (Authors keep copyright of their contributions, of course; they just grant
@@ -97,4 +97,6 @@ a license to everyone to use it as detailed in LICENSE.)
 * Charlie Birks <admin@daftgames.net>
 * Ranger Harke <ranger.harke@autodesk.com> (copyright owned by Autodesk, Inc.)
 * Tobias Vrinssen <tobias@vrinssen.de>
+* Patrick R. Martin <patrick.martin.r@gmail.com>
+* Richard Quirk <richard.quirk@gmail.com>
 
diff --git a/emcc b/emcc
index 9a687bd8cd36b..95d77f10eb682 100755
--- a/emcc
+++ b/emcc
@@ -238,6 +238,9 @@ Options that are modified or new in %s include:
                            (see --llvm-opts), setting this has no
                            effect.
 
+                           Note that LLVM LTO is not perfectly stable yet,
+                           and can can cause code to behave incorrectly.
+
   --closure <on>           0: No closure compiler (default in -O2 and below)
                            1: Run closure compiler. This greatly reduces
                            code size and may in some cases increase
@@ -1279,6 +1282,10 @@ try:
         os.path.join('libc', 'gen', 'vwarnx.c'),
         os.path.join('libc', 'stdlib', 'strtod.c'),
       ]
+      musl_files = [
+      ]
+      for directory, sources in musl_files:
+        libc_files += [os.path.join('libc', 'musl', 'src', directory, source) for source in sources]
       return build_libc('libc.bc', libc_files)
 
     def apply_libc(need):
@@ -1316,6 +1323,32 @@ try:
           'wctrans.c',
           'wcwidth.c',
          ]],
+         ['locale', [
+          'iswalnum_l.c',
+          'iswalpha_l.c',
+          'iswblank_l.c',
+          'iswcntrl_l.c',
+          'iswctype_l.c',
+          'iswdigit_l.c',
+          'iswgraph_l.c',
+          'iswlower_l.c',
+          'iswprint_l.c',
+          'iswpunct_l.c',
+          'iswspace_l.c',
+          'iswupper_l.c',
+          'iswxdigit_l.c',
+          'strfmon.c',
+          'strxfrm.c',
+          'towctrans_l.c',
+          'towlower_l.c',
+          'towupper_l.c',
+          'wcscoll.c',
+          'wcscoll_l.c',
+          'wcsxfrm.c',
+          'wcsxfrm_l.c',
+          'wctrans_l.c',
+          'wctype_l.c',
+         ]],
          ['multibyte', [
           'btowc.c',
           'mblen.c',
@@ -1333,6 +1366,14 @@ try:
           'wctob.c',
           'wctomb.c',
          ]],
+         ['stdio', [
+          'fwprintf.c',
+          'swprintf.c',
+          'vfwprintf.c',
+          'vswprintf.c',
+          'vwprintf.c',
+          'wprintf.c',
+         ]],
          ['stdlib', [
            'ecvt.c',
            'fcvt.c',
@@ -1342,7 +1383,7 @@ try:
            'wcpcpy.c',
            'wcpncpy.c',
            'wcscasecmp.c',
-           # 'wcscasecmp_l.c', # XXX: alltypes.h issue
+           'wcscasecmp_l.c',
            'wcscat.c',
            'wcschr.c',
            'wcscmp.c',
@@ -1351,7 +1392,7 @@ try:
            'wcsdup.c',
            'wcslen.c',
            'wcsncasecmp.c',
-           # 'wcsncasecmp_l.c', # XXX: alltypes.h issue
+           'wcsncasecmp_l.c',
            'wcsncat.c',
            'wcsncmp.c',
            'wcsncpy.c',
diff --git a/emscripten.py b/emscripten.py
index 4d744fdd64f46..19e2160d4420b 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -39,7 +39,7 @@ def scan(ll, settings):
   if len(blockaddrs) > 0:
     settings['NECESSARY_BLOCKADDRS'] = blockaddrs
 
-NUM_CHUNKS_PER_CORE = 1.25
+NUM_CHUNKS_PER_CORE = 1.0
 MIN_CHUNK_SIZE = 1024*1024
 MAX_CHUNK_SIZE = float(os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or 'inf') # configuring this is just for debugging purposes
 
@@ -209,7 +209,7 @@ def save_settings():
   if cores > 1:
     intended_num_chunks = int(round(cores * NUM_CHUNKS_PER_CORE))
     chunk_size = max(MIN_CHUNK_SIZE, total_ll_size / intended_num_chunks)
-    chunk_size += 3*len(meta) + len(forwarded_data)/3 # keep ratio of lots of function code to meta (expensive to process, and done in each parallel task) and forwarded data (less expensive but potentially significant)
+    chunk_size += 3*len(meta) # keep ratio of lots of function code to meta (expensive to process, and done in each parallel task)
     chunk_size = min(MAX_CHUNK_SIZE, chunk_size)
   else:
     chunk_size = MAX_CHUNK_SIZE # if 1 core, just use the max chunk size
diff --git a/src/intertyper.js b/src/intertyper.js
index 0f189863d8e91..e43cc298138e1 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -167,9 +167,9 @@ function intertyper(lines, sidePass, baseLineNums) {
 
   dprint('framework', 'Big picture: Starting intertyper, main pass=' + mainPass);
 
-  var unparsedBundles = [];
+  var finalResults = [];
 
-  // Line splitter. We break off some bunches of lines into unparsedBundles, which are
+  // Line splitter. We break off some bunches of lines into unparsed bundles, which are
   // parsed in separate passes later. This helps to keep memory usage low - we can start
   // from raw lines and end up with final JS for each function individually that way, instead
   // of intertyping them all, then analyzing them all, etc.
@@ -185,12 +185,12 @@ function intertyper(lines, sidePass, baseLineNums) {
         intertype: 'unparsedTypes',
         lines: []
       };
-      unparsedBundles.push(unparsedTypes);
+      finalResults.push(unparsedTypes);
       unparsedGlobals = {
         intertype: 'unparsedGlobals',
         lines: []
       };
-      unparsedBundles.push(unparsedGlobals);
+      finalResults.push(unparsedGlobals);
     }
     var baseLineNumPosition = 0;
     for (var i = 0; i < lines.length; i++) {
@@ -258,7 +258,7 @@ function intertyper(lines, sidePass, baseLineNums) {
 
           var ident = toNiceIdent(func.ident);
           if (!(ident in DEAD_FUNCTIONS)) {
-            unparsedBundles.push({
+            finalResults.push({
               intertype: 'unparsedFunction',
               // We need this early, to know basic function info - ident, params, varargs
               ident: ident,
@@ -370,8 +370,6 @@ function intertyper(lines, sidePass, baseLineNums) {
     throw 'Invalid token, cannot triage: ' + dump(item);
   }
 
-  var extraResults = [];
-
   // Line parsers to intermediate form
 
   // globals: type or variable
@@ -719,7 +717,7 @@ function intertyper(lines, sidePass, baseLineNums) {
     var result = makeCall.call(this, item, 'invoke');
     if (DISABLE_EXCEPTION_CATCHING == 1) {
       result.item.intertype = 'call';
-      extraResults.push({
+      finalResults.push({
         intertype: 'branch',
         label: result.item.toLabel,
         lineNum: (result.forward ? item.parentLineNum : item.lineNum) + 0.5
@@ -988,13 +986,13 @@ function intertyper(lines, sidePass, baseLineNums) {
 
   // Input
 
-  var ret = lineSplitter().map(tokenizer).map(triager).filter(function(result) {
-    if (!result) return false;
-    if (result.tokens) result.tokens = null; // We do not need tokens, past the intertyper. Clean them up as soon as possible here.
-    return true;
+  lineSplitter().forEach(function(line) {
+    var t = tokenizer(line);
+    var item = triager(t);
+    if (!item) return;
+    finalResults.push(item);
+    if (item.tokens) item.tokens = null; // We do not need tokens, past the intertyper. Clean them up as soon as possible here.
   });
-  if (unparsedBundles.length > 0) ret = ret.concat(unparsedBundles);
-  if (extraResults.length > 0) ret = ret.concat(extraResults);
-  return ret;
+  return finalResults;
 }
 
diff --git a/src/jsifier.js b/src/jsifier.js
index a8c8d32d14cc1..96cb8d9a3d574 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -675,10 +675,10 @@ function JSify(data, functionsOnly, givenFunctions) {
           ret += indent + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n';
         }
         
-        if (PRINT_SPLIT_FILE_MARKER && Debugging.on && Debugging.getAssociatedSourceFile(line.lineNum)) {
+        if (PRINT_SPLIT_FILE_MARKER && Debugging.on && Debugging.getAssociatedSourceFile(label.lines[label.lines.length-1].lineNum)) {
           // Overwrite the associated source file for every line. The last line should contain the source file associated to
           // the return value/address of outer most block (the marked function).
-          associatedSourceFile = Debugging.getAssociatedSourceFile(line.lineNum);
+          associatedSourceFile = Debugging.getAssociatedSourceFile(label.lines[label.lines.length-1].lineNum);
         }
         
         // for special labels we care about (for phi), mark that we visited them
@@ -1114,7 +1114,8 @@ function JSify(data, functionsOnly, givenFunctions) {
     if (!useIfs) {
       ret += 'switch(' + signedIdent + ') {\n';
     }
-    for (var targetLabel in targetLabels) {
+    // process target labels, sorting them so output is consistently ordered
+    keys(targetLabels).sort().forEach(function(targetLabel) {
       if (!first && useIfs) {
         ret += 'else ';
       } else {
@@ -1142,7 +1143,7 @@ function JSify(data, functionsOnly, givenFunctions) {
           labelJS: phiSet
         });
       }
-    }
+    });
     var phiSet = item.defaultLabelJS = getPhiSetsForLabel(phiSets, item.defaultLabel);
     if (useIfs) {
       if (item.switchLabels.length > 0) ret += 'else {\n';
@@ -1237,8 +1238,8 @@ function JSify(data, functionsOnly, givenFunctions) {
     var param1 = finalizeLLVMParameter(item.params[0]);
     var param2 = finalizeLLVMParameter(item.params[1]);
     switch (item.op) {
-      case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type, null, null, null, null, ',') + ',tempValue)';
-      case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type, null, null, null, null, ',') + ',tempValue)';
+      case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, asmCoercion('tempValue+' + param2, type), type, null, null, null, null, ',') + ',tempValue)';
+      case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, asmCoercion('tempValue-' + param2, type), type, null, null, null, null, ',') + ',tempValue)';
       case 'or': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue|' + param2, type, null, null, null, null, ',') + ',tempValue)';
       case 'and': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue&' + param2, type, null, null, null, null, ',') + ',tempValue)';
       case 'xor': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue^' + param2, type, null, null, null, null, ',') + ',tempValue)';
diff --git a/src/library.js b/src/library.js
index 5c2c858db05db..a01153738d128 100644
--- a/src/library.js
+++ b/src/library.js
@@ -1974,6 +1974,7 @@ LibraryManager.library = {
         var flagLeftAlign = false;
         var flagAlternative = false;
         var flagZeroPad = false;
+        var flagPadSign = false;
         flagsLoop: while (1) {
           switch (next) {
             case {{{ charCode('+') }}}:
@@ -1992,6 +1993,9 @@ LibraryManager.library = {
                 flagZeroPad = true;
                 break;
               }
+            case {{{ charCode(' ') }}}:
+              flagPadSign = true;
+              break;
             default:
               break flagsLoop;
           }
@@ -2158,14 +2162,20 @@ LibraryManager.library = {
             }
 
             // Add sign if needed
-            if (flagAlwaysSigned) {
-              if (currArg < 0) {
-                prefix = '-' + prefix;
-              } else {
+            if (currArg >= 0) {
+              if (flagAlwaysSigned) {
                 prefix = '+' + prefix;
+              } else if (flagPadSign) {
+                prefix = ' ' + prefix;
               }
             }
 
+            // Move sign to prefix so we zero-pad after the sign
+            if (argText.charAt(0) == '-') {
+              prefix = '-' + prefix;
+              argText = argText.substr(1);
+            }
+
             // Add padding.
             while (prefix.length + argText.length < width) {
               if (flagLeftAlign) {
@@ -2248,8 +2258,12 @@ LibraryManager.library = {
               if (next == {{{ charCode('E') }}}) argText = argText.toUpperCase();
 
               // Add sign.
-              if (flagAlwaysSigned && currArg >= 0) {
-                argText = '+' + argText;
+              if (currArg >= 0) {
+                if (flagAlwaysSigned) {
+                  argText = '+' + argText;
+                } else if (flagPadSign) {
+                  argText = ' ' + argText;
+                }
               }
             }
 
@@ -2887,6 +2901,13 @@ LibraryManager.library = {
   asprintf: function(s, format, varargs) {
     return _sprintf(-s, format, varargs);
   },
+  dprintf__deps: ['_formatString', 'write'],
+  dprintf: function(fd, format, varargs) {
+    var result = __formatString(format, varargs);
+    var stack = Runtime.stackSave();
+    var ret = _write(fd, allocate(result, 'i8', ALLOC_STACK), result.length);
+    Runtime.stackRestore(stack);
+  },
 
 #if TARGET_X86
   // va_arg is just like our varargs
@@ -2895,6 +2916,7 @@ LibraryManager.library = {
   vprintf: 'printf',
   vsprintf: 'sprintf',
   vasprintf: 'asprintf',
+  vdprintf: 'dprintf',
   vscanf: 'scanf',
   vfscanf: 'fscanf',
   vsscanf: 'sscanf',
@@ -2922,6 +2944,10 @@ LibraryManager.library = {
   vasprintf: function(s, format, va_arg) {
     return _asprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}});
   },
+  vdprintf__deps: ['dprintf'],
+  vdprintf: function (fd, format, va_arg) {
+    return _dprintf(fd, format, {{{ makeGetValue('va_arg', 0, '*') }}});
+  },
   vscanf__deps: ['scanf'],
   vscanf: function(format, va_arg) {
     return _scanf(format, {{{ makeGetValue('va_arg', 0, '*') }}});
@@ -3792,6 +3818,7 @@ LibraryManager.library = {
   },
   // We always assume ASCII locale.
   strcoll: 'strcmp',
+  strcoll_l: 'strcmp',
 
   strcasecmp__asm: true,
   strcasecmp__sig: 'iii',
@@ -4058,6 +4085,7 @@ LibraryManager.library = {
     }
   },
   _toupper: 'toupper',
+  toupper_l: 'toupper',
 
   tolower__asm: true,
   tolower__sig: 'ii',
@@ -4068,54 +4096,65 @@ LibraryManager.library = {
     return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0;
   },
   _tolower: 'tolower',
+  tolower_l: 'tolower',
 
   // The following functions are defined as macros in glibc.
   islower: function(chr) {
     return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}};
   },
+  islower_l: 'islower',
   isupper: function(chr) {
     return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}};
   },
+  isupper_l: 'isupper',
   isalpha: function(chr) {
     return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
            (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
   },
+  isalpha_l: 'isalpha',
   isdigit: function(chr) {
     return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}};
   },
-  isdigit_l: 'isdigit', // no locale support yet
+  isdigit_l: 'isdigit',
   isxdigit: function(chr) {
     return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
            (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) ||
            (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}});
   },
-  isxdigit_l: 'isxdigit', // no locale support yet
+  isxdigit_l: 'isxdigit',
   isalnum: function(chr) {
     return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
            (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
            (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
   },
+  isalnum_l: 'isalnum',
   ispunct: function(chr) {
     return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) ||
            (chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) ||
            (chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) ||
            (chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}});
   },
+  ispunct_l: 'ispunct',
   isspace: function(chr) {
     return (chr == 32) || (chr >= 9 && chr <= 13);
   },
+  isspace_l: 'isspace',
   isblank: function(chr) {
     return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}};
   },
+  isblank_l: 'isblank',
   iscntrl: function(chr) {
     return (0 <= chr && chr <= 0x1F) || chr === 0x7F;
   },
+  iscntrl_l: 'iscntrl',
   isprint: function(chr) {
     return 0x1F < chr && chr < 0x7F;
   },
+  isprint_l: 'isprint',
   isgraph: function(chr) {
     return 0x20 < chr && chr < 0x7F;
   },
+  isgraph_l: 'isgraph',
   // Lookup tables for glibc ctype implementation.
   __ctype_b_loc: function() {
     // http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html
@@ -6435,11 +6474,15 @@ LibraryManager.library = {
   // locale.h
   // ==========================================================================
 
+  newlocale__deps: ['malloc'],
   newlocale: function(mask, locale, base) {
-    return 0;
+    return _malloc({{{ QUANTUM_SIZE}}});
   },
 
-  freelocale: function(locale) {},
+  freelocale__deps: ['free'],
+  freelocale: function(locale) {
+    _free(locale);
+  },
 
   uselocale: function(locale) {
     return 0;
@@ -8854,7 +8897,7 @@ function autoAddDeps(object, name) {
 }
 
 // Add aborting stubs for various libc stuff needed by libc++
-['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'swprintf', 'pthread_join', 'pthread_detach', 'strcoll_l', 'strxfrm_l', 'wcscoll_l', 'toupper_l', 'tolower_l', 'iswspace_l', 'iswprint_l', 'iswcntrl_l', 'iswupper_l', 'iswlower_l', 'iswalpha_l', 'iswdigit_l', 'iswpunct_l', 'iswxdigit_l', 'iswblank_l', 'wcsxfrm_l', 'towupper_l', 'towlower_l', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) {
+['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'pthread_join', 'pthread_detach', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) {
   LibraryManager.library[aborter] = function() { throw 'TODO: ' + aborter };
 });
 
diff --git a/src/library_gl.js b/src/library_gl.js
index 16ea55313aea5..a4d35aff0d7fc 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -217,6 +217,23 @@ var LibraryGL = {
               throw 'Invalid format (' + format + ')';
           }
           break;
+        case 0x1403 /* GL_UNSIGNED_SHORT */:
+          if (format == 0x1902 /* GL_DEPTH_COMPONENT */) {
+            sizePerPixel = 2;
+          } else {
+            throw 'Invalid format (' + format + ')';
+          }
+          break;
+        case 0x1405 /* GL_UNSIGNED_INT */:
+          if (format == 0x1902 /* GL_DEPTH_COMPONENT */) {
+            sizePerPixel = 4;
+          } else {
+            throw 'Invalid format (' + format + ')';
+          }
+          break;
+        case 0x84FA /* UNSIGNED_INT_24_8_WEBGL */:
+          sizePerPixel = 4;
+          break;
         case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */:
         case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */:
         case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */:
@@ -244,6 +261,8 @@ var LibraryGL = {
         pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}};
       } else if (type == 0x1406 /* GL_FLOAT */) {
         pixels = {{{ makeHEAPView('F32', 'pixels', 'pixels+bytes') }}};
+      } else if (type == 0x1405 /* GL_UNSIGNED_INT */ || type == 0x84FA /* UNSIGNED_INT_24_8_WEBGL */) {
+        pixels = {{{ makeHEAPView('U32', 'pixels', 'pixels+bytes') }}};
       } else {
         pixels = {{{ makeHEAPView('U16', 'pixels', 'pixels+bytes') }}};
       }
@@ -302,6 +321,18 @@ var LibraryGL = {
     },
 #endif
 
+#if GL_ASSERTIONS
+    validateGLObjectID: function(objectHandleArray, objectID, callerFunctionName, objectReadableType) {
+      if (objectID != 0) {
+        if (objectHandleArray[objectID] === null) {
+          console.error(callerFunctionName + ' called with an already deleted ' + objectReadableType + ' ID ' + objectID + '!');
+        } else if (!objectHandleArray[objectID]) {
+          console.error(callerFunctionName + ' called with an invalid ' + objectReadableType + ' ID ' + objectID + '!');
+        }
+      }
+    },
+#endif
+
     initExtensions: function() {
       if (GL.initExtensions.done) return;
       GL.initExtensions.done = true;
@@ -334,6 +365,10 @@ var LibraryGL = {
 
       GL.elementIndexUintExt = Module.ctx.getExtension('OES_element_index_uint');
       GL.standardDerivativesExt = Module.ctx.getExtension('OES_standard_derivatives');
+
+      GL.depthTextureExt = Module.ctx.getExtension("WEBGL_depth_texture") ||
+                           Module.ctx.getExtension("MOZ_WEBGL_depth_texture") ||
+                           Module.ctx.getExtension("WEBKIT_WEBGL_depth_texture");
     }
   },
 
@@ -588,6 +623,9 @@ var LibraryGL = {
 
   glBindTexture__sig: 'vii',
   glBindTexture: function(target, texture) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.textures, texture, 'glBindTexture', 'texture');
+#endif
     Module.ctx.bindTexture(target, texture ? GL.textures[texture] : null);
   },
 
@@ -710,6 +748,9 @@ var LibraryGL = {
 
   glBindRenderbuffer__sig: 'vii',
   glBindRenderbuffer: function(target, renderbuffer) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.renderbuffers, renderbuffer, 'glBindRenderbuffer', 'renderbuffer');
+#endif
     Module.ctx.bindRenderbuffer(target, renderbuffer ? GL.renderbuffers[renderbuffer] : null);
   },
 
@@ -727,6 +768,9 @@ var LibraryGL = {
 
   glGetUniformfv__sig: 'viii',
   glGetUniformfv: function(program, location, params) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glGetUniformfv', 'program');
+#endif
     var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]);
     if (typeof data == 'number') {
       {{{ makeSetValue('params', '0', 'data', 'float') }}};
@@ -739,6 +783,9 @@ var LibraryGL = {
 
   glGetUniformiv__sig: 'viii',
   glGetUniformiv: function(program, location, params) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glGetUniformiv', 'program');
+#endif
     var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]);
     if (typeof data == 'number' || typeof data == 'boolean') {
       {{{ makeSetValue('params', '0', 'data', 'i32') }}};
@@ -751,6 +798,9 @@ var LibraryGL = {
 
   glGetUniformLocation__sig: 'iii',
   glGetUniformLocation: function(program, name) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glGetUniformLocation', 'program');
+#endif
     name = Pointer_stringify(name);
     var ptable = GL.uniformTable[program];
     if (!ptable) ptable = GL.uniformTable[program] = {};
@@ -810,6 +860,9 @@ var LibraryGL = {
 
   glGetActiveUniform__sig: 'viiiiiii',
   glGetActiveUniform: function(program, index, bufSize, length, size, type, name) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glGetActiveUniform', 'program');
+#endif
     program = GL.programs[program];
     var info = Module.ctx.getActiveUniform(program, index);
 
@@ -1018,6 +1071,9 @@ var LibraryGL = {
 
   glBindBuffer__sig: 'vii',
   glBindBuffer: function(target, buffer) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.buffers, buffer, 'glBindBuffer', 'buffer');
+#endif
     var bufferObj = buffer ? GL.buffers[buffer] : null;
 
     if (target == Module.ctx.ARRAY_BUFFER) {
@@ -1062,6 +1118,9 @@ var LibraryGL = {
 
   glGetActiveAttrib__sig: 'viiiiiii',
   glGetActiveAttrib: function(program, index, bufSize, length, size, type, name) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glGetActiveAttrib', 'program');
+#endif
     program = GL.programs[program];
     var info = Module.ctx.getActiveAttrib(program, index);
 
@@ -1094,6 +1153,9 @@ var LibraryGL = {
 
   glGetAttachedShaders__sig: 'viiii',
   glGetAttachedShaders: function(program, maxCount, count, shaders) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glGetAttachedShaders', 'program');
+#endif
     var result = Module.ctx.getAttachedShaders(GL.programs[program]);
     var len = result.length;
     if (len > maxCount) {
@@ -1109,12 +1171,18 @@ var LibraryGL = {
 
   glShaderSource__sig: 'viiii',
   glShaderSource: function(shader, count, string, length) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.shaders, shader, 'glShaderSource', 'shader');
+#endif
     var source = GL.getSource(shader, count, string, length);
     Module.ctx.shaderSource(GL.shaders[shader], source);
   },
 
   glGetShaderSource__sig: 'viiii',
   glGetShaderSource: function(shader, bufSize, length, source) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderSource', 'shader');
+#endif
     var result = Module.ctx.getShaderSource(GL.shaders[shader]);
     result = result.slice(0, Math.max(0, bufSize - 1));
     writeStringToMemory(result, source);
@@ -1125,11 +1193,17 @@ var LibraryGL = {
 
   glCompileShader__sig: 'vi',
   glCompileShader: function(shader) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.shaders, shader, 'glCompileShader', 'shader');
+#endif
     Module.ctx.compileShader(GL.shaders[shader]);
   },
 
   glGetShaderInfoLog__sig: 'viiii',
   glGetShaderInfoLog: function(shader, maxLength, length, infoLog) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderInfoLog', 'shader');
+#endif
     var log = Module.ctx.getShaderInfoLog(GL.shaders[shader]);
     // Work around a bug in Chromium which causes getShaderInfoLog to return null
     if (!log) {
@@ -1144,6 +1218,9 @@ var LibraryGL = {
 
   glGetShaderiv__sig: 'viii',
   glGetShaderiv : function(shader, pname, p) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderiv', 'shader');
+#endif
     if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH
       {{{ makeSetValue('p', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[shader]).length + 1', 'i32') }}};
     } else {
@@ -1153,6 +1230,9 @@ var LibraryGL = {
 
   glGetProgramiv__sig: 'viii',
   glGetProgramiv : function(program, pname, p) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glGetProgramiv', 'program');
+#endif
     if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH
       {{{ makeSetValue('p', '0', 'Module.ctx.getProgramInfoLog(GL.programs[program]).length + 1', 'i32') }}};
     } else {
@@ -1187,12 +1267,20 @@ var LibraryGL = {
 
   glAttachShader__sig: 'vii',
   glAttachShader: function(program, shader) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glAttachShader', 'program');
+    GL.validateGLObjectID(GL.shaders, shader, 'glAttachShader', 'shader');
+#endif
     Module.ctx.attachShader(GL.programs[program],
                             GL.shaders[shader]);
   },
 
   glDetachShader__sig: 'vii',
   glDetachShader: function(program, shader) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glDetachShader', 'program');
+    GL.validateGLObjectID(GL.shaders, shader, 'glDetachShader', 'shader');
+#endif
     Module.ctx.detachShader(GL.programs[program],
                             GL.shaders[shader]);
   },
@@ -1206,12 +1294,18 @@ var LibraryGL = {
 
   glLinkProgram__sig: 'vi',
   glLinkProgram: function(program) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glLinkProgram', 'program');
+#endif
     Module.ctx.linkProgram(GL.programs[program]);
     GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking
   },
 
   glGetProgramInfoLog__sig: 'viiii',
   glGetProgramInfoLog: function(program, maxLength, length, infoLog) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glGetProgramInfoLog', 'program');
+#endif
     var log = Module.ctx.getProgramInfoLog(GL.programs[program]);
     // Work around a bug in Chromium which causes getProgramInfoLog to return null
     if (!log) {
@@ -1226,11 +1320,17 @@ var LibraryGL = {
 
   glUseProgram__sig: 'vi',
   glUseProgram: function(program) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glUseProgram', 'program');
+#endif
     Module.ctx.useProgram(program ? GL.programs[program] : null);
   },
 
   glValidateProgram__sig: 'vi',
   glValidateProgram: function(program) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glValidateProgram', 'program');
+#endif
     Module.ctx.validateProgram(GL.programs[program]);
   },
 
@@ -1243,12 +1343,18 @@ var LibraryGL = {
 
   glBindAttribLocation__sig: 'viii',
   glBindAttribLocation: function(program, index, name) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.programs, program, 'glBindAttribLocation', 'program');
+#endif
     name = Pointer_stringify(name);
     Module.ctx.bindAttribLocation(GL.programs[program], index, name);
   },
 
   glBindFramebuffer__sig: 'vii',
   glBindFramebuffer: function(target, framebuffer) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.framebuffers, framebuffer, 'glBindFramebuffer', 'framebuffer');
+#endif
     Module.ctx.bindFramebuffer(target, framebuffer ? GL.framebuffers[framebuffer] : null);
   },
 
@@ -1276,12 +1382,18 @@ var LibraryGL = {
 
   glFramebufferRenderbuffer__sig: 'viiii',
   glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.renderbuffers, renderbuffer, 'glFramebufferRenderbuffer', 'renderbuffer');
+#endif
     Module.ctx.framebufferRenderbuffer(target, attachment, renderbuffertarget,
                                        GL.renderbuffers[renderbuffer]);
   },
 
   glFramebufferTexture2D__sig: 'viiiii',
   glFramebufferTexture2D: function(target, attachment, textarget, texture, level) {
+#if GL_ASSERTIONS
+    GL.validateGLObjectID(GL.textures, texture, 'glFramebufferTexture2D', 'texture');
+#endif
     Module.ctx.framebufferTexture2D(target, attachment, textarget,
                                     GL.textures[texture], level);
   },
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 116bf547c4ec9..63e8b2b128474 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -704,7 +704,7 @@ var LibrarySDL = {
     // since the browser engine handles that for us.  Therefore, in JS we just
     // maintain a list of channels and return IDs for them to the SDL consumer.
     allocateChannels: function(num) { // called from Mix_AllocateChannels and init
-      if (SDL.numChannels && SDL.numChannels >= num) return;
+      if (SDL.numChannels && SDL.numChannels >= num && num != 0) return;
       SDL.numChannels = num;
       SDL.channels = [];
       for (var i = 0; i < num; i++) {
@@ -1131,7 +1131,10 @@ var LibrarySDL = {
     } else {
       dr = { x: 0, y: 0, w: -1, h: -1 };
     }
+    var oldAlpha = dstData.ctx.globalAlpha;
+    dstData.ctx.globalAlpha = srcData.alpha/255;
     dstData.ctx.drawImage(srcData.canvas, sr.x, sr.y, sr.w, sr.h, dr.x, dr.y, sr.w, sr.h);
+    dstData.ctx.globalAlpha = oldAlpha;
     if (dst != SDL.screen) {
       // XXX As in IMG_Load, for compatibility we write out |pixels|
       console.log('WARNING: copying canvas data to memory for compatibility');
@@ -1454,60 +1457,240 @@ var LibrarySDL = {
 
   // SDL_Audio
 
-  // TODO fix SDL_OpenAudio, and add some tests for it.  It's currently broken.
   SDL_OpenAudio: function(desired, obtained) {
-    SDL.allocateChannels(32);
-
-    SDL.audio = {
-      freq: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.freq', 'i32', 0, 1) }}},
-      format: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.format', 'i16', 0, 1) }}},
-      channels: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.channels', 'i8', 0, 1) }}},
-      samples: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.samples', 'i16', 0, 1) }}},
-      callback: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.callback', 'void*', 0, 1) }}},
-      userdata: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.userdata', 'void*', 0, 1) }}},
-      paused: true,
-      timer: null
-    };
-
-    if (obtained) {
-      {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.freq', 'SDL.audio.freq', 'i32') }}}; // no good way for us to know if the browser can really handle this
-      {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.format', 33040, 'i16') }}}; // float, signed, 16-bit
-      {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.channels', 'SDL.audio.channels', 'i8') }}};
-      {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.silence', makeGetValue('desired', 'SDL.structs.AudioSpec.silence', 'i8', 0, 1), 'i8') }}}; // unclear if browsers can provide this
-      {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.samples', 'SDL.audio.samples', 'i16') }}};
-      {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.callback', 'SDL.audio.callback', '*') }}};
-      {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.userdata', 'SDL.audio.userdata', '*') }}};
-    }
-
-    var totalSamples = SDL.audio.samples*SDL.audio.channels;
-    SDL.audio.bufferSize = totalSamples*2; // hardcoded 16-bit audio
-    SDL.audio.buffer = _malloc(SDL.audio.bufferSize);
-    SDL.audio.caller = function() {
-      Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]);
-      SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize);
-    };
-    // Mozilla Audio API. TODO: Other audio APIs
     try {
-      SDL.audio.mozOutput = new Audio();
-      SDL.audio.mozOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler
-      SDL.audio.mozBuffer = new Float32Array(totalSamples);
-      SDL.audio.pushAudio = function(ptr, size) {
-        var mozBuffer = SDL.audio.mozBuffer;
-        for (var i = 0; i < totalSamples; i++) {
-          mozBuffer[i] = ({{{ makeGetValue('ptr', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?)
+      SDL.audio = {
+        freq: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.freq', 'i32', 0, 1) }}},
+        format: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.format', 'i16', 0, 1) }}},
+        channels: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.channels', 'i8', 0, 1) }}},
+        samples: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.samples', 'i16', 0, 1) }}}, // Samples in the CB buffer per single sound channel.
+        callback: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.callback', 'void*', 0, 1) }}},
+        userdata: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.userdata', 'void*', 0, 1) }}},
+        paused: true,
+        timer: null
+      };
+      // The .silence field tells the constant sample value that corresponds to the safe un-skewed silence value for the wave data.
+      if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) {
+        SDL.audio.silence = 128; // Audio ranges in [0, 255], so silence is half-way in between.
+      } else if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) {
+        SDL.audio.silence = 0; // Signed data in range [-32768, 32767], silence is 0.
+      } else {
+        throw 'Invalid SDL audio format ' + SDL.audio.format + '!';
+      }
+      // Round the desired audio frequency up to the next 'common' frequency value.
+      // Web Audio API spec states 'An implementation must support sample-rates in at least the range 22050 to 96000.'
+      if (SDL.audio.freq <= 0) {
+        throw 'Unsupported sound frequency ' + SDL.audio.freq + '!';
+      } else if (SDL.audio.freq <= 22050) {
+        SDL.audio.freq = 22050; // Take it safe and clamp everything lower than 22kHz to that.
+      } else if (SDL.audio.freq <= 32000) {
+        SDL.audio.freq = 32000;
+      } else if (SDL.audio.freq <= 44100) {
+        SDL.audio.freq = 44100;
+      } else if (SDL.audio.freq <= 48000) {
+        SDL.audio.freq = 48000;
+      } else if (SDL.audio.freq <= 96000) {
+        SDL.audio.freq = 96000;
+      } else {
+        throw 'Unsupported sound frequency ' + SDL.audio.freq + '!';
+      }
+      if (SDL.audio.channels == 0) {
+        SDL.audio.channels = 1; // In SDL both 0 and 1 mean mono.
+      } else if (SDL.audio.channels < 0 || SDL.audio.channels > 32) {
+        throw 'Unsupported number of audio channels for SDL audio: ' + SDL.audio.channels + '!';
+      } else if (SDL.audio.channels != 1 && SDL.audio.channels != 2) { // Unsure what SDL audio spec supports. Web Audio spec supports up to 32 channels.
+        console.log('Warning: Using untested number of audio channels ' + SDL.audio.channels);
+      }
+      if (SDL.audio.samples < 1024 || SDL.audio.samples > 524288 /* arbitrary cap */) {
+        throw 'Unsupported audio callback buffer size ' + SDL.audio.samples + '!';
+      } else if ((SDL.audio.samples & (SDL.audio.samples-1)) != 0) {
+        throw 'Audio callback buffer size ' + SDL.audio.samples + ' must be a power-of-two!';
+      }
+      
+      var totalSamples = SDL.audio.samples*SDL.audio.channels;
+      SDL.audio.bytesPerSample = (SDL.audio.format == 0x0008 /*AUDIO_U8*/ || SDL.audio.format == 0x8008 /*AUDIO_S8*/) ? 1 : 2;
+      SDL.audio.bufferSize = totalSamples*SDL.audio.bytesPerSample;
+      SDL.audio.buffer = _malloc(SDL.audio.bufferSize);
+      
+      // Create a callback function that will be routinely called to ask more audio data from the user application.
+      SDL.audio.caller = function() {
+        if (!SDL.audio) {
+          return;
+        }
+        Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]);
+        SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize);
+      };
+      
+      SDL.audio.audioOutput = new Audio();
+      // As a workaround use Mozilla Audio Data API on Firefox until it ships with Web Audio and sound quality issues are fixed.
+      if (typeof(SDL.audio.audioOutput['mozSetup'])==='function') {
+        SDL.audio.audioOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler
+        SDL.audio.mozBuffer = new Float32Array(totalSamples);
+        SDL.audio.nextPlayTime = 0;
+        SDL.audio.pushAudio = function(ptr, size) {
+          var mozBuffer = SDL.audio.mozBuffer;
+          // The input audio data for SDL audio is either 8-bit or 16-bit interleaved across channels, output for Mozilla Audio Data API
+          // needs to be Float32 interleaved, so perform a sample conversion.
+          if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) {
+            for (var i = 0; i < totalSamples; i++) {
+              mozBuffer[i] = ({{{ makeGetValue('ptr', 'i*2', 'i16', 0, 0) }}}) / 0x8000;
+            }
+          } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) {
+            for (var i = 0; i < totalSamples; i++) {
+              var v = ({{{ makeGetValue('ptr', 'i', 'i8', 0, 0) }}});
+              mozBuffer[i] = ((v >= 0) ? v-128 : v+128) /128;
+            }
+          }
+          // Submit the audio data to audio device.
+          SDL.audio.audioOutput['mozWriteAudio'](mozBuffer);
+          
+          // Compute when the next audio callback should be called.
+          var curtime = Date.now() / 1000.0 - SDL.audio.startTime;
+          if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) {
+            console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.');
+          }
+          var playtime = Math.max(curtime, SDL.audio.nextPlayTime);
+          var buffer_duration = SDL.audio.samples / SDL.audio.freq;
+          SDL.audio.nextPlayTime = playtime + buffer_duration;
+          // Schedule the next audio callback call.
+          SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1000.0 * (playtime-curtime));
+        }
+      } else {
+        // Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page,
+        // since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'.
+        if (!SDL.audioContext) {
+          if (typeof(AudioContext) === 'function') {
+            SDL.audioContext = new AudioContext();
+          } else if (typeof(webkitAudioContext) === 'function') {
+            SDL.audioContext = new webkitAudioContext();
+          } else {
+            throw 'Web Audio API is not available!';
+          }
+        }
+        SDL.audio.soundSource = new Array(); // Use an array of sound sources as a ring buffer to queue blocks of synthesized audio to Web Audio API.
+        SDL.audio.nextSoundSource = 0; // Index of the next sound buffer in the ring buffer queue to play.
+        SDL.audio.nextPlayTime = 0; // Time in seconds when the next audio block is due to start.
+        
+        // The pushAudio function with a new audio buffer whenever there is new audio data to schedule to be played back on the device.
+        SDL.audio.pushAudio=function(ptr,sizeBytes) {
+          try {
+            --SDL.audio.numAudioTimersPending;
+
+            var sizeSamples = sizeBytes / SDL.audio.bytesPerSample; // How many samples fit in the callback buffer?
+            var sizeSamplesPerChannel = sizeSamples / SDL.audio.channels; // How many samples per a single channel fit in the cb buffer?
+            if (sizeSamplesPerChannel != SDL.audio.samples) {
+              throw 'Received mismatching audio buffer size!';
+            }
+            // Allocate new sound buffer to be played.
+            var source = SDL.audioContext['createBufferSource']();
+            if (SDL.audio.soundSource[SDL.audio.nextSoundSource]) {
+              SDL.audio.soundSource[SDL.audio.nextSoundSource]['disconnect'](); // Explicitly disconnect old source, since we know it shouldn't be running anymore.
+            }
+            SDL.audio.soundSource[SDL.audio.nextSoundSource] = source;
+            var soundBuffer = SDL.audioContext['createBuffer'](SDL.audio.channels,sizeSamplesPerChannel,SDL.audio.freq);
+            SDL.audio.soundSource[SDL.audio.nextSoundSource]['connect'](SDL.audioContext['destination']);
+
+            // The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as
+            // supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data,
+            // so perform a buffer conversion for the data.
+            var numChannels = SDL.audio.channels;
+            for(var i = 0; i < numChannels; ++i) {
+              var channelData = soundBuffer['getChannelData'](i);
+              if (channelData.length != sizeSamplesPerChannel) {
+                throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + sizeSamplesPerChannel + ' samples!';
+              }
+              if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) {
+                for(var j = 0; j < sizeSamplesPerChannel; ++j) {
+                  channelData[j] = ({{{ makeGetValue('ptr', '(j*numChannels + i)*2', 'i16', 0, 0) }}}) / 0x8000;
+                }
+              } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) {
+                for(var j = 0; j < sizeSamplesPerChannel; ++j) {
+                  var v = ({{{ makeGetValue('ptr', 'j*numChannels + i', 'i8', 0, 0) }}});
+                  channelData[j] = ((v >= 0) ? v-128 : v+128) /128;
+                }
+              }
+            }
+            // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=883675 by setting the buffer only after filling. The order is important here!
+            source['buffer'] = soundBuffer;
+            
+            // Schedule the generated sample buffer to be played out at the correct time right after the previously scheduled
+            // sample buffer has finished.
+            var curtime = SDL.audioContext['currentTime'];
+//            if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) {
+//              console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.');
+//            }
+            var playtime = Math.max(curtime, SDL.audio.nextPlayTime);
+            SDL.audio.soundSource[SDL.audio.nextSoundSource]['start'](playtime);
+            var buffer_duration = sizeSamplesPerChannel / SDL.audio.freq;
+            SDL.audio.nextPlayTime = playtime + buffer_duration;
+            SDL.audio.nextSoundSource = (SDL.audio.nextSoundSource + 1) % 4;
+            var secsUntilNextCall = playtime-curtime;
+            
+            // Queue the next audio frame push to be performed when the previously queued buffer has finished playing.
+            if (SDL.audio.numAudioTimersPending == 0) {
+              var preemptBufferFeedMSecs = buffer_duration/2.0;
+              SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, Math.max(0.0, 1000.0*secsUntilNextCall-preemptBufferFeedMSecs));
+              ++SDL.audio.numAudioTimersPending;
+            }
+
+            // If we are risking starving, immediately queue an extra second buffer.
+            if (secsUntilNextCall <= buffer_duration && SDL.audio.numAudioTimersPending <= 1) {
+              ++SDL.audio.numAudioTimersPending;
+              Browser.safeSetTimeout(SDL.audio.caller, 1.0);
+            }
+          } catch(e) {
+            console.log('Web Audio API error playing back audio: ' + e.toString());
+          }
         }
-        SDL.audio.mozOutput['mozWriteAudio'](mozBuffer);
       }
+
+      if (obtained) {
+        // Report back the initialized audio parameters.
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.freq', 'SDL.audio.freq', 'i32') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.format', 'SDL.audio.format', 'i16') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.channels', 'SDL.audio.channels', 'i8') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.silence', 'SDL.audio.silence', 'i8') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.samples', 'SDL.audio.samples', 'i16') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.callback', 'SDL.audio.callback', '*') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.userdata', 'SDL.audio.userdata', '*') }}};
+      }
+      SDL.allocateChannels(32);
+
     } catch(e) {
+      console.log('Initializing SDL audio threw an exception: "' + e.toString() + '"! Continuing without audio.');
       SDL.audio = null;
+      SDL.allocateChannels(0);
+      if (obtained) {
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.freq', 0, 'i32') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.format', 0, 'i16') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.channels', 0, 'i8') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.silence', 0, 'i8') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.samples', 0, 'i16') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.callback', 0, '*') }}};
+        {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.userdata', 0, '*') }}};
+      }
+    }
+    if (!SDL.audio) {
+      return -1;
     }
-    if (!SDL.audio) return -1;
     return 0;
   },
 
   SDL_PauseAudio: function(pauseOn) {
-    if (SDL.audio.paused !== pauseOn) {
-      SDL.audio.timer = pauseOn ? SDL.audio.timer && clearInterval(SDL.audio.timer) : Browser.safeSetInterval(SDL.audio.caller, 1/35);
+    if (!SDL.audio) {
+      return;
+    }
+    if (pauseOn) {
+      if (SDL.audio.timer !== undefined) {
+        clearTimeout(SDL.audio.timer);
+        SDL.audio.numAudioTimersPending = 0;
+        SDL.audio.timer = undefined;
+      }
+    } else if (!SDL.audio.timer) {
+      // Start the audio playback timer callback loop.
+      SDL.audio.numAudioTimersPending = 1;
+      SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1);
+      SDL.audio.startTime = Date.now() / 1000.0; // Only used for Mozilla Audio Data API. Not needed for Web Audio API.
     }
     SDL.audio.paused = pauseOn;
   },
@@ -1515,9 +1698,18 @@ var LibrarySDL = {
   SDL_CloseAudio__deps: ['SDL_PauseAudio', 'free'],
   SDL_CloseAudio: function() {
     if (SDL.audio) {
+      try{
+        for(var i = 0; i < SDL.audio.soundSource.length; ++i) {
+          if (!(typeof(SDL.audio.soundSource[i]==='undefined'))) {
+            SDL.audio.soundSource[i].stop(0);
+          }
+        }
+      } catch(e) {}
+      SDL.audio.soundSource = null;
       _SDL_PauseAudio(1);
       _free(SDL.audio.buffer);
       SDL.audio = null;
+      SDL.allocateChannels(0);
     }
   },
 
diff --git a/src/parseTools.js b/src/parseTools.js
index 90c5acab265a8..8ce83adf7b229 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1196,7 +1196,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
     var typeData = Types.types[type];
     var ret = [];
     for (var i = 0; i < typeData.fields.length; i++) {
-      ret.push('f' + i + ': ' + makeGetValue(ptr, pos + typeData.flatIndexes[i], typeData.fields[i], noNeedFirst, unsigned));
+      ret.push('f' + i + ': ' + makeGetValue(ptr, pos + typeData.flatIndexes[i], typeData.fields[i], noNeedFirst, unsigned, 0, 0, noSafe));
     }
     return '{ ' + ret.join(', ') + ' }';
   }
@@ -1204,8 +1204,8 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
   // In double mode 1, in x86 we always assume unaligned because we can't trust that; otherwise in le32
   // we need this code path if we are not fully aligned.
   if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double' && (TARGET_X86 || align < 8)) {
-    return '(' + makeSetTempDouble(0, 'i32', makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align)) + ',' +
-                 makeSetTempDouble(1, 'i32', makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align)) + ',' +
+    return '(' + makeSetTempDouble(0, 'i32', makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align, noSafe)) + ',' +
+                 makeSetTempDouble(1, 'i32', makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align, noSafe)) + ',' +
             makeGetTempDouble(0, 'double') + ')';
   }
 
@@ -1218,12 +1218,12 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
       if (isIntImplemented(type)) {
         if (bytes == 4 && align == 2) {
           // Special case that we can optimize
-          ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '|' +
-                 '(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore) + '<<16)';
+          ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore, 2, noSafe) + '|' +
+                 '(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore, 2, noSafe) + '<<16)';
         } else { // XXX we cannot truly handle > 4... (in x86)
           ret = '';
           for (var i = 0; i < bytes; i++) {
-            ret += '(' + makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore) + (i > 0 ? '<<' + (8*i) : '') + ')';
+            ret += '(' + makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore, 1, noSafe) + (i > 0 ? '<<' + (8*i) : '') + ')';
             if (i < bytes-1) ret += '|';
           }
           ret = '(' + makeSignOp(ret, type, unsigned ? 'un' : 're', true);
@@ -1302,7 +1302,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
       value = range(typeData.fields.length).map(function(i) { return value + '.f' + i });
     }
     for (var i = 0; i < typeData.fields.length; i++) {
-      ret.push(makeSetValue(ptr, getFastValue(pos, '+', typeData.flatIndexes[i]), value[i], typeData.fields[i], noNeedFirst));
+      ret.push(makeSetValue(ptr, getFastValue(pos, '+', typeData.flatIndexes[i]), value[i], typeData.fields[i], noNeedFirst, 0, 0, noSafe));
     }
     return ret.join('; ');
   }
@@ -1329,17 +1329,17 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
         if (bytes == 4 && align == 2) {
           // Special case that we can optimize
           ret += 'tempBigInt=' + value + sep;
-          ret += makeSetValue(ptr, pos, 'tempBigInt&0xffff', 'i16', noNeedFirst, ignore, 2) + sep;
-          ret += makeSetValue(ptr, getFastValue(pos, '+', 2), 'tempBigInt>>16', 'i16', noNeedFirst, ignore, 2);
+          ret += makeSetValue(ptr, pos, 'tempBigInt&0xffff', 'i16', noNeedFirst, ignore, 2, noSafe) + sep;
+          ret += makeSetValue(ptr, getFastValue(pos, '+', 2), 'tempBigInt>>16', 'i16', noNeedFirst, ignore, 2, noSafe);
         } else {
           ret += 'tempBigInt=' + value + sep;
           for (var i = 0; i < bytes; i++) {
-            ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore, 1);
+            ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore, 1, noSafe);
             if (i < bytes-1) ret += sep + 'tempBigInt = tempBigInt>>8' + sep;
           }
         }
       } else {
-        ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8, null, null, true) + sep;
+        ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8, noSafe, null, true) + sep;
         ret += makeCopyValues(getFastValue(ptr, '+', pos), 'tempDoublePtr', Runtime.getNativeTypeSize(type), type, null, align, sep);
       }
       return ret;
@@ -1499,7 +1499,9 @@ function getFastValue(a, op, b, type) {
   }
   if (op in MUL_DIV) {
     if (op == '*') {
-      if (a == 0 || b == 0) {
+      // We can't eliminate where a or b are 0 as that would break things for creating
+      // a negative 0.
+      if ((a == 0 || b == 0) && !(type in Runtime.FLOAT_TYPES)) {
         return '0';
       } else if (a == 1) {
         return b;
@@ -1520,8 +1522,8 @@ function getFastValue(a, op, b, type) {
         }
         return '(Math.imul(' + a + ',' + b + ')|0)';
       }
-    } else {
-      if (a == '0') {
+    } else { // div
+      if (a == '0' && !(type in Runtime.FLOAT_TYPES)) { // careful on floats, since 0*NaN is not 0
         return '0';
       } else if (b == 1) {
         return a;
@@ -1758,7 +1760,7 @@ function checkBitcast(item) {
       } else {
         warnOnce('Casting a function pointer type to a potentially incompatible one (use -s VERBOSE=1 to see more)');
       }
-      warnOnce('See https://github.com/kripken/emscripten/wiki/CodeGuidlinesAndLimitations#function-pointer-issues for more information on dangerous function pointer casts');
+      warnOnce('See https://github.com/kripken/emscripten/wiki/CodeGuidelinesAndLimitations#function-pointer-issues for more information on dangerous function pointer casts');
       if (ASM_JS) warnOnce('Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these');
     }
     if (oldCount != newCount && oldCount && newCount) showWarning();
diff --git a/src/preamble.js b/src/preamble.js
index acff665f8e629..183fd0c8340c6 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -299,11 +299,10 @@ function ccallFunc(func, returnType, argTypes, args) {
   function toC(value, type) {
     if (type == 'string') {
       if (value === null || value === undefined || value === 0) return 0; // null string
-      if (!stack) stack = Runtime.stackSave();
-      var ret = Runtime.stackAlloc(value.length+1);
-      writeStringToMemory(value, ret);
-      return ret;
-    } else if (type == 'array') {
+      value = intArrayFromString(value);
+      type = 'array';
+    }
+    if (type == 'array') {
       if (!stack) stack = Runtime.stackSave();
       var ret = Runtime.stackAlloc(value.length);
       writeArrayToMemory(value, ret);
@@ -717,7 +716,7 @@ var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}};
 // Initialize the runtime's memory
 #if USE_TYPED_ARRAYS
 // check for full engine support (use string 'subarray' to avoid closure compiler confusion)
-assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
+assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
        'Cannot fallback to non-typed array case: Code is too specialized');
 
 #if USE_TYPED_ARRAYS == 1
diff --git a/src/settings.js b/src/settings.js
index 15bca4db5251c..0daafa352e220 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -202,6 +202,7 @@ var SOCKET_WEBRTC = 0; // Select socket backend, either webrtc or websockets.
 
 var OPENAL_DEBUG = 0; // Print out debugging information from our OpenAL implementation.
 
+var GL_ASSERTIONS = 0; // Adds extra checks for error situations in the GL library. Can impact performance.
 var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you can set a runtime
                   // option, in this case GL.debug.
 var GL_TESTING = 0; // When enabled, sets preserveDrawingBuffer in the context, to allow tests to work (but adds overhead)
diff --git a/system/include/libc/assert.h b/system/include/libc/assert.h
index ab745db18022a..c64d3e52db15e 100644
--- a/system/include/libc/assert.h
+++ b/system/include/libc/assert.h
@@ -12,7 +12,7 @@
 extern "C" {
 #endif
 
-void __assert_fail (const char *, const char *, int, const char *);
+_Noreturn void __assert_fail (const char *, const char *, int, const char *);
 
 #ifdef __cplusplus
 }
diff --git a/system/lib/libc/musl/readme.txt b/system/lib/libc/musl/readme.txt
index 16c584237c538..9ca04036025a7 100644
--- a/system/lib/libc/musl/readme.txt
+++ b/system/lib/libc/musl/readme.txt
@@ -6,3 +6,4 @@ Differences from upstream musl include:
 * various 64 bit types are 32 bit instead including off_t,
   ino_t, dev_t, blkcnt_t, fsblkcnt_t, fsfilcnt_t, rlim_t.
 * We don't define _POSIX_SHARED_MEMORY_OBJECTS.
+* We flag __assert_fail as _Noreturn.
diff --git a/system/lib/libc/musl/src/internal/locale_impl.h b/system/lib/libc/musl/src/internal/locale_impl.h
new file mode 100644
index 0000000000000..c268124fabf24
--- /dev/null
+++ b/system/lib/libc/musl/src/internal/locale_impl.h
@@ -0,0 +1,5 @@
+#include <locale.h>
+
+struct __locale {
+	int dummy;
+};
diff --git a/system/lib/libc/musl/src/internal/stdio_impl.h b/system/lib/libc/musl/src/internal/stdio_impl.h
new file mode 100644
index 0000000000000..2083b2fe427d6
--- /dev/null
+++ b/system/lib/libc/musl/src/internal/stdio_impl.h
@@ -0,0 +1,92 @@
+#ifndef _STDIO_IMPL_H
+#define _STDIO_IMPL_H
+
+#include <stdio.h>
+#include "syscall.h"
+#include "libc.h"
+
+#define UNGET 8
+
+#define FFINALLOCK(f) ((f)->lock>=0 ? __lockfile((f)) : 0)
+#define FLOCK(f) int __need_unlock = ((f)->lock>=0 ? __lockfile((f)) : 0)
+#define FUNLOCK(f) if (__need_unlock) __unlockfile((f)); else
+
+#define F_PERM 1
+#define F_NORD 4
+#define F_NOWR 8
+#define F_EOF 16
+#define F_ERR 32
+#define F_SVB 64
+
+struct _IO_FILE {
+	unsigned flags;
+	unsigned char *rpos, *rend;
+	int (*close)(FILE *);
+	unsigned char *wend, *wpos;
+	unsigned char *mustbezero_1;
+	unsigned char *wbase;
+	size_t (*read)(FILE *, unsigned char *, size_t);
+	size_t (*write)(FILE *, const unsigned char *, size_t);
+	off_t (*seek)(FILE *, off_t, int);
+	unsigned char *buf;
+	size_t buf_size;
+	FILE *prev, *next;
+	int fd;
+	int pipe_pid;
+	long lockcount;
+	short dummy3;
+	signed char mode;
+	signed char lbf;
+	int lock;
+	int waiters;
+	void *cookie;
+	off_t off;
+	char *getln_buf;
+	void *mustbezero_2;
+	unsigned char *shend;
+	off_t shlim, shcnt;
+};
+
+size_t __stdio_read(FILE *, unsigned char *, size_t);
+size_t __stdio_write(FILE *, const unsigned char *, size_t);
+size_t __stdout_write(FILE *, const unsigned char *, size_t);
+off_t __stdio_seek(FILE *, off_t, int);
+int __stdio_close(FILE *);
+
+size_t __string_read(FILE *, unsigned char *, size_t);
+
+int __toread(FILE *);
+int __towrite(FILE *);
+
+#if defined(__PIC__) && (100*__GNUC__+__GNUC_MINOR__ >= 303)
+__attribute__((visibility("protected")))
+#endif
+int __overflow(FILE *, int), __uflow(FILE *);
+
+int __fseeko(FILE *, off_t, int);
+int __fseeko_unlocked(FILE *, off_t, int);
+off_t __ftello(FILE *);
+off_t __ftello_unlocked(FILE *);
+size_t __fwritex(const unsigned char *, size_t, FILE *);
+int __putc_unlocked(int, FILE *);
+
+FILE *__fdopen(int, const char *);
+int __fmodeflags(const char *);
+
+#define OFLLOCK() LOCK(libc.ofl_lock)
+#define OFLUNLOCK() UNLOCK(libc.ofl_lock)
+
+#define feof(f) ((f)->flags & F_EOF)
+#define ferror(f) ((f)->flags & F_ERR)
+
+#define getc_unlocked(f) \
+	( ((f)->rpos < (f)->rend) ? *(f)->rpos++ : __uflow((f)) )
+
+#define putc_unlocked(c, f) ( ((c)!=(f)->lbf && (f)->wpos<(f)->wend) \
+	? *(f)->wpos++ = (c) : __overflow((f),(c)) )
+
+/* Caller-allocated FILE * operations */
+FILE *__fopen_rb_ca(const char *, FILE *, unsigned char *, size_t);
+int __fclose_ca(FILE *);
+
+#endif
diff --git a/system/lib/libc/musl/src/locale/iswalnum_l.c b/system/lib/libc/musl/src/locale/iswalnum_l.c
new file mode 100644
index 0000000000000..c888060c4ce55
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswalnum_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswalnum_l(wint_t c, locale_t l)
+{
+	return iswalnum(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswalpha_l.c b/system/lib/libc/musl/src/locale/iswalpha_l.c
new file mode 100644
index 0000000000000..cd2be91e58281
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswalpha_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswalpha_l(wint_t c, locale_t l)
+{
+	return iswalpha(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswblank_l.c b/system/lib/libc/musl/src/locale/iswblank_l.c
new file mode 100644
index 0000000000000..f3a2691f581ff
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswblank_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswblank_l(wint_t c, locale_t l)
+{
+	return iswblank(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswcntrl_l.c b/system/lib/libc/musl/src/locale/iswcntrl_l.c
new file mode 100644
index 0000000000000..7681fe092b6f8
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswcntrl_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswcntrl_l(wint_t c, locale_t l)
+{
+	return iswcntrl(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswctype_l.c b/system/lib/libc/musl/src/locale/iswctype_l.c
new file mode 100644
index 0000000000000..13dfb1ed359f4
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswctype_l.c
@@ -0,0 +1,9 @@
+#include <wctype.h>
+#include "libc.h"
+
+int iswctype_l(wint_t c, wctype_t t, locale_t l)
+{
+	return iswctype(c, t);
+}
+
+weak_alias(iswctype_l, __iswctype_l);
diff --git a/system/lib/libc/musl/src/locale/iswdigit_l.c b/system/lib/libc/musl/src/locale/iswdigit_l.c
new file mode 100644
index 0000000000000..3de678c24f132
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswdigit_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswdigit_l(wint_t c, locale_t l)
+{
+	return iswdigit(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswgraph_l.c b/system/lib/libc/musl/src/locale/iswgraph_l.c
new file mode 100644
index 0000000000000..34df64fc3ba33
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswgraph_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswgraph_l(wint_t c, locale_t l)
+{
+	return iswgraph(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswlower_l.c b/system/lib/libc/musl/src/locale/iswlower_l.c
new file mode 100644
index 0000000000000..c52421a043c86
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswlower_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswlower_l(wint_t c, locale_t l)
+{
+	return iswlower(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswprint_l.c b/system/lib/libc/musl/src/locale/iswprint_l.c
new file mode 100644
index 0000000000000..73d83ab356333
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswprint_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswprint_l(wint_t c, locale_t l)
+{
+	return iswprint(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswpunct_l.c b/system/lib/libc/musl/src/locale/iswpunct_l.c
new file mode 100644
index 0000000000000..831e0e5441963
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswpunct_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswpunct_l(wint_t c, locale_t l)
+{
+	return iswpunct(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswspace_l.c b/system/lib/libc/musl/src/locale/iswspace_l.c
new file mode 100644
index 0000000000000..b507e9e3a613c
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswspace_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswspace_l(wint_t c, locale_t l)
+{
+	return iswspace(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswupper_l.c b/system/lib/libc/musl/src/locale/iswupper_l.c
new file mode 100644
index 0000000000000..fc988ef119b49
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswupper_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswupper_l(wint_t c, locale_t l)
+{
+	return iswupper(c);
+}
diff --git a/system/lib/libc/musl/src/locale/iswxdigit_l.c b/system/lib/libc/musl/src/locale/iswxdigit_l.c
new file mode 100644
index 0000000000000..9527cf3e1d8c6
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/iswxdigit_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+int iswxdigit_l(wint_t c, locale_t l)
+{
+	return iswxdigit(c);
+}
diff --git a/system/lib/libc/musl/src/locale/strfmon.c b/system/lib/libc/musl/src/locale/strfmon.c
new file mode 100644
index 0000000000000..f510d9a42f748
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/strfmon.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <monetary.h>
+#include <errno.h>
+#include <stdarg.h>
+
+static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_list ap)
+{
+	size_t l;
+	double x;
+	int fill, nogrp, negpar, nosym, left, intl;
+	int lp, rp, w, fw;
+	char *s0=s;
+	for (; n && *fmt; ) {
+		if (*fmt != '%') {
+		literal:
+			*s++ = *fmt++;
+			n--;
+			continue;
+		}
+		fmt++;
+		if (*fmt == '%') goto literal;
+
+		fill = ' ';
+		nogrp = 0;
+		negpar = 0;
+		nosym = 0;
+		left = 0;
+		for (; ; fmt++) {
+			switch (*fmt) {
+			case '=':
+				fill = *++fmt;
+				continue;
+			case '^':
+				nogrp = 1;
+				continue;
+			case '(':
+				negpar = 1;
+			case '+':
+				continue;
+			case '!':
+				nosym = 1;
+				continue;
+			case '-':
+				left = 1;
+				continue;
+			}
+			break;
+		}
+
+		for (fw=0; isdigit(*fmt); fmt++)
+			fw = 10*fw + (*fmt-'0');
+		lp = 0;
+		rp = 2;
+		if (*fmt=='#') for (lp=0, fmt++; isdigit(*fmt); fmt++)
+			lp = 10*lp + (*fmt-'0');
+		if (*fmt=='.') for (rp=0, fmt++; isdigit(*fmt); fmt++)
+			rp = 10*rp + (*fmt-'0');
+
+		intl = *fmt++ == 'i';
+
+		w = lp + 1 + rp;
+		if (!left && fw>w) w = fw;
+
+		x = va_arg(ap, double);
+		l = snprintf(s, n, "%*.*f", w, rp, x);
+		if (l >= n) {
+			errno = E2BIG;
+			return -1;
+		}
+		s += l;
+		n -= l;
+	}
+	return s-s0;
+}
+
+ssize_t strfmon_l(char *restrict s, size_t n, locale_t loc, const char *restrict fmt, ...)
+{
+	va_list ap;
+	ssize_t ret;
+
+	va_start(ap, fmt);
+	ret = vstrfmon_l(s, n, loc, fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
+
+
+ssize_t strfmon(char *restrict s, size_t n, const char *restrict fmt, ...)
+{
+	va_list ap;
+	ssize_t ret;
+
+	va_start(ap, fmt);
+	ret = vstrfmon_l(s, n, 0, fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
diff --git a/system/lib/libc/musl/src/locale/strxfrm.c b/system/lib/libc/musl/src/locale/strxfrm.c
new file mode 100644
index 0000000000000..32c461939dd42
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/strxfrm.c
@@ -0,0 +1,18 @@
+#include <string.h>
+#include <locale.h>
+#include "libc.h"
+
+/* collate only by code points */
+size_t __strxfrm_l(char *restrict dest, const char *restrict src, size_t n, locale_t loc)
+{
+	size_t l = strlen(src);
+	if (n > l) strcpy(dest, src);
+	return l;
+}
+
+size_t strxfrm(char *restrict dest, const char *restrict src, size_t n)
+{
+	return __strxfrm_l(dest, src, n, 0);
+}
+
+weak_alias(__strxfrm_l, strxfrm_l);
diff --git a/system/lib/libc/musl/src/locale/towctrans_l.c b/system/lib/libc/musl/src/locale/towctrans_l.c
new file mode 100644
index 0000000000000..6222058c750cf
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/towctrans_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+wint_t towctrans_l(wint_t c, wctrans_t t, locale_t l)
+{
+	return towctrans(c, t);
+}
diff --git a/system/lib/libc/musl/src/locale/towlower_l.c b/system/lib/libc/musl/src/locale/towlower_l.c
new file mode 100644
index 0000000000000..aaaea370f81fa
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/towlower_l.c
@@ -0,0 +1,9 @@
+#include <wctype.h>
+#include "libc.h"
+
+wint_t towlower_l(wint_t c, locale_t l)
+{
+	return towlower(c);
+}
+
+weak_alias(towlower_l, __towlower_l);
diff --git a/system/lib/libc/musl/src/locale/towupper_l.c b/system/lib/libc/musl/src/locale/towupper_l.c
new file mode 100644
index 0000000000000..ad02a4beab3ec
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/towupper_l.c
@@ -0,0 +1,9 @@
+#include <wctype.h>
+#include "libc.h"
+
+wint_t towupper_l(wint_t c, locale_t l)
+{
+	return towupper(c);
+}
+
+weak_alias(towupper_l, __towupper_l);
diff --git a/system/lib/libc/musl/src/locale/wcscoll.c b/system/lib/libc/musl/src/locale/wcscoll.c
new file mode 100644
index 0000000000000..20a60900e0386
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/wcscoll.c
@@ -0,0 +1,16 @@
+#include <wchar.h>
+#include <locale.h>
+#include "libc.h"
+
+/* FIXME: stub */
+int __wcscoll_l(const wchar_t *l, const wchar_t *r, locale_t locale)
+{
+	return wcscmp(l, r);
+}
+
+int wcscoll(const wchar_t *l, const wchar_t *r)
+{
+	return __wcscoll_l(l, r, 0);
+}
+
+weak_alias(__wcscoll_l, wcscoll_l);
diff --git a/system/lib/libc/musl/src/locale/wcscoll_l.c b/system/lib/libc/musl/src/locale/wcscoll_l.c
new file mode 100644
index 0000000000000..f257ec8d98002
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/wcscoll_l.c
@@ -0,0 +1,6 @@
+#include <wchar.h>
+
+int wcscoll_l(const wchar_t *l, const wchar_t *r, locale_t locale)
+{
+	return wcscoll(l, r);
+}
diff --git a/system/lib/libc/musl/src/locale/wcsxfrm.c b/system/lib/libc/musl/src/locale/wcsxfrm.c
new file mode 100644
index 0000000000000..cb79c97e79efd
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/wcsxfrm.c
@@ -0,0 +1,21 @@
+#include <wchar.h>
+#include <locale.h>
+#include "libc.h"
+
+/* collate only by code points */
+size_t __wcsxfrm_l(wchar_t *restrict dest, const wchar_t *restrict src, size_t n, locale_t loc)
+{
+	size_t l = wcslen(src);
+	if (l >= n) {
+		wmemcpy(dest, src, n-1);
+		dest[n-1] = 0;
+	} else wcscpy(dest, src);
+	return l;
+}
+
+size_t wcsxfrm(wchar_t *restrict dest, const wchar_t *restrict src, size_t n)
+{
+	return __wcsxfrm_l(dest, src, n, 0);
+}
+
+weak_alias(__wcsxfrm_l, wcsxfrm_l);
diff --git a/system/lib/libc/musl/src/locale/wcsxfrm_l.c b/system/lib/libc/musl/src/locale/wcsxfrm_l.c
new file mode 100644
index 0000000000000..66a00193c7827
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/wcsxfrm_l.c
@@ -0,0 +1,6 @@
+#include <wchar.h>
+
+size_t wcsxfrm_l(wchar_t *restrict dest, const wchar_t *restrict src, size_t n, locale_t locale)
+{
+	return wcsxfrm(dest, src, n);
+}
diff --git a/system/lib/libc/musl/src/locale/wctrans_l.c b/system/lib/libc/musl/src/locale/wctrans_l.c
new file mode 100644
index 0000000000000..dae3381e11cf8
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/wctrans_l.c
@@ -0,0 +1,6 @@
+#include <wctype.h>
+
+wctrans_t wctrans_l(const char *s, locale_t l)
+{
+	return wctrans(s);
+}
diff --git a/system/lib/libc/musl/src/locale/wctype_l.c b/system/lib/libc/musl/src/locale/wctype_l.c
new file mode 100644
index 0000000000000..601bab37d163f
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/wctype_l.c
@@ -0,0 +1,9 @@
+#include <wctype.h>
+#include "libc.h"
+
+wctype_t wctype_l(const char *s, locale_t l)
+{
+	return wctype(s);
+}
+
+weak_alias(wctype_l, __wctype_l);
diff --git a/system/lib/libc/musl/src/stdio/fwprintf.c b/system/lib/libc/musl/src/stdio/fwprintf.c
new file mode 100644
index 0000000000000..9ce4f0102e1b5
--- /dev/null
+++ b/system/lib/libc/musl/src/stdio/fwprintf.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+
+int fwprintf(FILE *restrict f, const wchar_t *restrict fmt, ...)
+{
+	int ret;
+	va_list ap;
+	va_start(ap, fmt);
+	ret = vfwprintf(f, fmt, ap);
+	va_end(ap);
+	return ret;
+}
diff --git a/system/lib/libc/musl/src/stdio/swprintf.c b/system/lib/libc/musl/src/stdio/swprintf.c
new file mode 100644
index 0000000000000..cbf83d235d8e0
--- /dev/null
+++ b/system/lib/libc/musl/src/stdio/swprintf.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+
+int swprintf(wchar_t *restrict s, size_t n, const wchar_t *restrict fmt, ...)
+{
+	int ret;
+	va_list ap;
+	va_start(ap, fmt);
+	ret = vswprintf(s, n, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
diff --git a/system/lib/libc/musl/src/stdio/vfwprintf.c b/system/lib/libc/musl/src/stdio/vfwprintf.c
new file mode 100644
index 0000000000000..eb0793121ed1b
--- /dev/null
+++ b/system/lib/libc/musl/src/stdio/vfwprintf.c
@@ -0,0 +1,361 @@
+#include "stdio_impl.h"
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <string.h>
+#include <stdarg.h>
+#include <wchar.h>
+#include <inttypes.h>
+
+/* Convenient bit representation for modifier flags, which all fall
+ * within 31 codepoints of the space character. */
+
+#define ALT_FORM   (1U<<'#'-' ')
+#define ZERO_PAD   (1U<<'0'-' ')
+#define LEFT_ADJ   (1U<<'-'-' ')
+#define PAD_POS    (1U<<' '-' ')
+#define MARK_POS   (1U<<'+'-' ')
+#define GROUPED    (1U<<'\''-' ')
+
+#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED)
+
+#if UINT_MAX == ULONG_MAX
+#define LONG_IS_INT
+#endif
+
+#if SIZE_MAX != ULONG_MAX || UINTMAX_MAX != ULLONG_MAX
+#define ODD_TYPES
+#endif
+
+/* State machine to accept length modifiers + conversion specifiers.
+ * Result is 0 on failure, or an argument type to pop on success. */
+
+enum {
+	BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE,
+	ZTPRE, JPRE,
+	STOP,
+	PTR, INT, UINT, ULLONG,
+#ifndef LONG_IS_INT
+	LONG, ULONG,
+#else
+#define LONG INT
+#define ULONG UINT
+#endif
+	SHORT, USHORT, CHAR, UCHAR,
+#ifdef ODD_TYPES
+	LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR,
+#else
+#define LLONG ULLONG
+#define SIZET ULONG
+#define IMAX LLONG
+#define UMAX ULLONG
+#define PDIFF LONG
+#define UIPTR ULONG
+#endif
+	DBL, LDBL,
+	NOARG,
+	MAXSTATE
+};
+
+#define S(x) [(x)-'A']
+
+static const unsigned char states[]['z'-'A'+1] = {
+	{ /* 0: bare types */
+		S('d') = INT, S('i') = INT,
+		S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT,
+		S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL,
+		S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL,
+		S('c') = CHAR, S('C') = INT,
+		S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR,
+		S('m') = NOARG,
+		S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE,
+		S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE,
+	}, { /* 1: l-prefixed */
+		S('d') = LONG, S('i') = LONG,
+		S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG,
+		S('c') = INT, S('s') = PTR, S('n') = PTR,
+		S('l') = LLPRE,
+	}, { /* 2: ll-prefixed */
+		S('d') = LLONG, S('i') = LLONG,
+		S('o') = ULLONG, S('u') = ULLONG,
+		S('x') = ULLONG, S('X') = ULLONG,
+		S('n') = PTR,
+	}, { /* 3: h-prefixed */
+		S('d') = SHORT, S('i') = SHORT,
+		S('o') = USHORT, S('u') = USHORT,
+		S('x') = USHORT, S('X') = USHORT,
+		S('n') = PTR,
+		S('h') = HHPRE,
+	}, { /* 4: hh-prefixed */
+		S('d') = CHAR, S('i') = CHAR,
+		S('o') = UCHAR, S('u') = UCHAR,
+		S('x') = UCHAR, S('X') = UCHAR,
+		S('n') = PTR,
+	}, { /* 5: L-prefixed */
+		S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL,
+		S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL,
+		S('n') = PTR,
+	}, { /* 6: z- or t-prefixed (assumed to be same size) */
+		S('d') = PDIFF, S('i') = PDIFF,
+		S('o') = SIZET, S('u') = SIZET,
+		S('x') = SIZET, S('X') = SIZET,
+		S('n') = PTR,
+	}, { /* 7: j-prefixed */
+		S('d') = IMAX, S('i') = IMAX,
+		S('o') = UMAX, S('u') = UMAX,
+		S('x') = UMAX, S('X') = UMAX,
+		S('n') = PTR,
+	}
+};
+
+#define OOB(x) ((unsigned)(x)-'A' > 'z'-'A')
+
+union arg
+{
+	uintmax_t i;
+	long double f;
+	void *p;
+};
+
+static void pop_arg(union arg *arg, int type, va_list *ap)
+{
+	/* Give the compiler a hint for optimizing the switch. */
+	if ((unsigned)type > MAXSTATE) return;
+	switch (type) {
+	       case PTR:	arg->p = va_arg(*ap, void *);
+	break; case INT:	arg->i = va_arg(*ap, int);
+	break; case UINT:	arg->i = va_arg(*ap, unsigned int);
+#ifndef LONG_IS_INT
+	break; case LONG:	arg->i = va_arg(*ap, long);
+	break; case ULONG:	arg->i = va_arg(*ap, unsigned long);
+#endif
+	break; case ULLONG:	arg->i = va_arg(*ap, unsigned long long);
+	break; case SHORT:	arg->i = (short)va_arg(*ap, int);
+	break; case USHORT:	arg->i = (unsigned short)va_arg(*ap, int);
+	break; case CHAR:	arg->i = (signed char)va_arg(*ap, int);
+	break; case UCHAR:	arg->i = (unsigned char)va_arg(*ap, int);
+#ifdef ODD_TYPES
+	break; case LLONG:	arg->i = va_arg(*ap, long long);
+	break; case SIZET:	arg->i = va_arg(*ap, size_t);
+	break; case IMAX:	arg->i = va_arg(*ap, intmax_t);
+	break; case UMAX:	arg->i = va_arg(*ap, uintmax_t);
+	break; case PDIFF:	arg->i = va_arg(*ap, ptrdiff_t);
+	break; case UIPTR:	arg->i = (uintptr_t)va_arg(*ap, void *);
+#endif
+	break; case DBL:	arg->f = va_arg(*ap, double);
+	break; case LDBL:	arg->f = va_arg(*ap, long double);
+	}
+}
+
+static void out(FILE *f, const wchar_t *s, size_t l)
+{
+	while (l--) fputwc(*s++, f);
+}
+
+static int getint(wchar_t **s) {
+	int i;
+	for (i=0; iswdigit(**s); (*s)++)
+		i = 10*i + (**s-'0');
+	return i;
+}
+
+static const char sizeprefix['y'-'a'] = {
+['a'-'a']='L', ['e'-'a']='L', ['f'-'a']='L', ['g'-'a']='L',
+['d'-'a']='j', ['i'-'a']='j', ['o'-'a']='j', ['u'-'a']='j', ['x'-'a']='j',
+['p'-'a']='j'
+};
+
+static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_arg, int *nl_type)
+{
+	wchar_t *a, *z, *s=(wchar_t *)fmt, *s0;
+	unsigned l10n=0, litpct, fl;
+	int w, p;
+	union arg arg;
+	int argpos;
+	unsigned st, ps;
+	int cnt=0, l=0;
+	int i;
+	int t;
+	char *bs;
+	char charfmt[16];
+	wchar_t wc;
+
+	for (;;) {
+		/* Update output count, end loop when fmt is exhausted */
+		if (cnt >= 0) {
+			if (l > INT_MAX - cnt) {
+				if (!ferror(f)) errno = EOVERFLOW;
+				cnt = -1;
+			} else cnt += l;
+		}
+		if (!*s) break;
+
+		/* Handle literal text and %% format specifiers */
+		for (a=s; *s && *s!='%'; s++);
+		litpct = wcsspn(s, L"%")/2; /* Optimize %%%% runs */
+		z = s+litpct;
+		s += 2*litpct;
+		l = z-a;
+		if (f) out(f, a, l);
+		if (l) continue;
+
+		if (iswdigit(s[1]) && s[2]=='$') {
+			l10n=1;
+			argpos = s[1]-'0';
+			s+=3;
+		} else {
+			argpos = -1;
+			s++;
+		}
+
+		/* Read modifier flags */
+		for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*s-' ')); s++)
+			fl |= 1U<<*s-' ';
+
+		/* Read field width */
+		if (*s=='*') {
+			if (iswdigit(s[1]) && s[2]=='$') {
+				l10n=1;
+				nl_type[s[1]-'0'] = INT;
+				w = nl_arg[s[1]-'0'].i;
+				s+=3;
+			} else if (!l10n) {
+				w = f ? va_arg(*ap, int) : 0;
+				s++;
+			} else return -1;
+			if (w<0) fl|=LEFT_ADJ, w=-w;
+		} else if ((w=getint(&s))<0) return -1;
+
+		/* Read precision */
+		if (*s=='.' && s[1]=='*') {
+			if (isdigit(s[2]) && s[3]=='$') {
+				nl_type[s[2]-'0'] = INT;
+				p = nl_arg[s[2]-'0'].i;
+				s+=4;
+			} else if (!l10n) {
+				p = f ? va_arg(*ap, int) : 0;
+				s+=2;
+			} else return -1;
+		} else if (*s=='.') {
+			s++;
+			p = getint(&s);
+		} else p = -1;
+
+		/* Format specifier state machine */
+		s0=s;
+		st=0;
+		do {
+			if (OOB(*s)) return -1;
+			ps=st;
+			st=states[st]S(*s++);
+		} while (st-1<STOP);
+		if (!st) return -1;
+
+		/* Check validity of argument type (nl/normal) */
+		if (st==NOARG) {
+			if (argpos>=0) return -1;
+			else if (!f) continue;
+		} else {
+			if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos];
+			else if (f) pop_arg(&arg, st, ap);
+			else return 0;
+		}
+
+		if (!f) continue;
+		t = s[-1];
+		if (ps && (t&15)==3) t&=~32;
+
+		switch (t) {
+		case 'n':
+			switch(ps) {
+			case BARE: *(int *)arg.p = cnt; break;
+			case LPRE: *(long *)arg.p = cnt; break;
+			case LLPRE: *(long long *)arg.p = cnt; break;
+			case HPRE: *(unsigned short *)arg.p = cnt; break;
+			case HHPRE: *(unsigned char *)arg.p = cnt; break;
+			case ZTPRE: *(size_t *)arg.p = cnt; break;
+			case JPRE: *(uintmax_t *)arg.p = cnt; break;
+			}
+			continue;
+		case 'c':
+			fputwc(btowc(arg.i), f);
+			l = 1;
+			continue;
+		case 'C':
+			fputwc(arg.i, f);
+			l = 1;
+			continue;
+		case 'S':
+			a = arg.p;
+			z = wmemchr(a, 0, p);
+			if (!z) z=a+p;
+			else p=z-a;
+			if (w<p) w=p;
+			if (!(fl&LEFT_ADJ)) fprintf(f, "%.*s", w-p, "");
+			out(f, a, p);
+			if ((fl&LEFT_ADJ)) fprintf(f, "%.*s", w-p, "");
+			l=w;
+			continue;
+		case 's':
+			bs = arg.p;
+			if (p<0) p = INT_MAX;
+			for (i=l=0; l<p && (i=mbtowc(&wc, bs, MB_LEN_MAX))>0; bs+=i, l++);
+			if (i<0) return -1;
+			p=l;
+			if (w<p) w=p;
+			if (!(fl&LEFT_ADJ)) fprintf(f, "%.*s", w-p, "");
+			bs = arg.p;
+			while (l--) {
+				i=mbtowc(&wc, bs, MB_LEN_MAX);
+				bs+=i;
+				fputwc(wc, f);
+			}
+			if ((fl&LEFT_ADJ)) fprintf(f, "%.*s", w-p, "");
+			l=w;
+			continue;
+		}
+
+		snprintf(charfmt, sizeof charfmt, "%%%s%s%s%s%s*.*%c%c",
+			"#"+!(fl & ALT_FORM),
+			"+"+!(fl & MARK_POS),
+			"-"+!(fl & LEFT_ADJ),
+			" "+!(fl & PAD_POS),
+			"0"+!(fl & ZERO_PAD),
+			sizeprefix[(t|32)-'a'], t);
+
+		switch (t|32) {
+		case 'a': case 'e': case 'f': case 'g':
+			l = fprintf(f, charfmt, w, p, arg.f);
+			break;
+		case 'd': case 'i': case 'o': case 'u': case 'x': case 'p':
+			l = fprintf(f, charfmt, w, p, arg.i);
+			break;
+		}
+	}
+
+	if (f) return cnt;
+	if (!l10n) return 0;
+
+	for (i=1; i<=NL_ARGMAX && nl_type[i]; i++)
+		pop_arg(nl_arg+i, nl_type[i], ap);
+	for (; i<=NL_ARGMAX && !nl_type[i]; i++);
+	if (i<=NL_ARGMAX) return -1;
+	return 1;
+}
+
+int vfwprintf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)
+{
+	va_list ap2;
+	int nl_type[NL_ARGMAX] = {0};
+	union arg nl_arg[NL_ARGMAX];
+	int ret;
+
+	va_copy(ap2, ap);
+	if (wprintf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) return -1;
+
+	FLOCK(f);
+	ret = wprintf_core(f, fmt, &ap2, nl_arg, nl_type);
+	FUNLOCK(f);
+	va_end(ap2);
+	return ret;
+}
diff --git a/system/lib/libc/musl/src/stdio/vswprintf.c b/system/lib/libc/musl/src/stdio/vswprintf.c
new file mode 100644
index 0000000000000..7d237bae72e94
--- /dev/null
+++ b/system/lib/libc/musl/src/stdio/vswprintf.c
@@ -0,0 +1,53 @@
+#include "stdio_impl.h"
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <wchar.h>
+
+struct cookie {
+	wchar_t *ws;
+	size_t l;
+};
+
+static size_t sw_write(FILE *f, const unsigned char *s, size_t l)
+{
+	size_t l0 = l;
+	int i = 0;
+	struct cookie *c = f->cookie;
+	if (s!=f->wbase && sw_write(f, f->wbase, f->wpos-f->wbase)==-1)
+		return -1;
+	while (c->l && l && (i=mbtowc(c->ws, (void *)s, l))>=0) {
+		s+=i;
+		l-=i;
+		c->l--;
+		c->ws++;
+	}
+	*c->ws = 0;
+	return i<0 ? i : l0;
+}
+
+int vswprintf(wchar_t *restrict s, size_t n, const wchar_t *restrict fmt, va_list ap)
+{
+	int r;
+	FILE f;
+	unsigned char buf[256];
+	struct cookie c = { s, n-1 };
+
+	memset(&f, 0, sizeof(FILE));
+	f.lbf = EOF;
+	f.write = sw_write;
+	f.buf_size = sizeof buf;
+	f.buf = buf;
+	f.lock = -1;
+	f.cookie = &c;
+	if (!n) {
+		return -1;
+	} else if (n > INT_MAX) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+	r = vfwprintf(&f, fmt, ap);
+	sw_write(&f, 0, 0);
+	return r>=n ? -1 : r;
+}
diff --git a/system/lib/libc/musl/src/stdio/vwprintf.c b/system/lib/libc/musl/src/stdio/vwprintf.c
new file mode 100644
index 0000000000000..eeeecdc7c62ae
--- /dev/null
+++ b/system/lib/libc/musl/src/stdio/vwprintf.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <wchar.h>
+
+int vwprintf(const wchar_t *restrict fmt, va_list ap)
+{
+	return vfwprintf(stdout, fmt, ap);
+}
diff --git a/system/lib/libc/musl/src/stdio/wprintf.c b/system/lib/libc/musl/src/stdio/wprintf.c
new file mode 100644
index 0000000000000..342cd97911d36
--- /dev/null
+++ b/system/lib/libc/musl/src/stdio/wprintf.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+
+int wprintf(const wchar_t *restrict fmt, ...)
+{
+	int ret;
+	va_list ap;
+	va_start(ap, fmt);
+	ret = vwprintf(fmt, ap);
+	va_end(ap);
+	return ret;
+}
diff --git a/system/lib/libcextra.symbols b/system/lib/libcextra.symbols
index a365271d1f5a7..806b49dbe47ee 100644
--- a/system/lib/libcextra.symbols
+++ b/system/lib/libcextra.symbols
@@ -1,20 +1,41 @@
+         W __iswctype_l
+         T __strxfrm_l
+         W __towlower_l
+         W __towupper_l
+         T __wcscoll_l
+         T __wcsxfrm_l
+         W __wctype_l
          T btowc
          T ecvt
          T fcvt
+         T fwprintf
          T gcvt
          T iswalnum
+         T iswalnum_l
          T iswalpha
+         T iswalpha_l
          T iswblank
+         T iswblank_l
          T iswcntrl
+         T iswcntrl_l
          T iswctype
+         T iswctype_l
          T iswdigit
+         T iswdigit_l
          T iswgraph
+         T iswgraph_l
          T iswlower
+         T iswlower_l
          T iswprint
+         T iswprint_l
          T iswpunct
+         T iswpunct_l
          T iswspace
+         T iswspace_l
          T iswupper
+         T iswupper_l
          T iswxdigit
+         T iswxdigit_l
          T mblen
          T mbrlen
          T mbrtowc
@@ -23,21 +44,36 @@
          T mbsrtowcs
          T mbstowcs
          T mbtowc
+         T strfmon
+         T strfmon_l
+         T strxfrm
+         W strxfrm_l
+         T swprintf
          T towctrans
+         T towctrans_l
          T towlower
+         T towlower_l
          T towupper
+         T towupper_l
+         T vfwprintf
+         T vswprintf
+         T vwprintf
          T wcpcpy
          T wcpncpy
          T wcrtomb
          T wcscasecmp
+         T wcscasecmp_l
          T wcscat
          T wcschr
          T wcscmp
+         T wcscoll
+         T wcscoll_l
          T wcscpy
          T wcscspn
          T wcsdup
          T wcslen
          T wcsncasecmp
+         T wcsncasecmp_l
          T wcsncat
          T wcsncmp
          T wcsncpy
@@ -52,13 +88,18 @@
          T wcstombs
          T wcswcs
          T wcswidth
+         T wcsxfrm
+         T wcsxfrm_l
          T wctob
          T wctomb
          T wctrans
+         T wctrans_l
          T wctype
+         T wctype_l
          T wcwidth
          T wmemchr
          T wmemcmp
          T wmemcpy
          T wmemmove
          T wmemset
+         T wprintf
diff --git a/tests/cases/atomicrmw_unaligned.emcc b/tests/cases/atomicrmw_unaligned.emcc
new file mode 100644
index 0000000000000..9faeda24c5b53
--- /dev/null
+++ b/tests/cases/atomicrmw_unaligned.emcc
@@ -0,0 +1 @@
+["-s", "UNALIGNED_MEMORY=1"]
diff --git a/tests/cases/atomicrmw_unaligned.ll b/tests/cases/atomicrmw_unaligned.ll
new file mode 100644
index 0000000000000..fe479dce038ed
--- /dev/null
+++ b/tests/cases/atomicrmw_unaligned.ll
@@ -0,0 +1,21 @@
+; ModuleID = 'tests/hello_world.bc'
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+@.str = private unnamed_addr constant [15 x i8] c"hello, %d,%d!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+; [#uses=0]
+define i32 @main() {
+entry:
+  %t = alloca i32, align 4                  ; [#uses=2 type=i32**]
+  store i32 50, i32* %t, align 4
+  %0 = load i32* %t
+  %1 = atomicrmw add i32* %t, i32 3 seq_cst, ; [#uses=0 type=i32] [debug line = 21:12]
+  %2 = load i32* %t
+  %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0), i32 %0, i32 %2) ; [#uses=0 type=i32]
+  %3 = atomicrmw volatile add i32* %t, i32 3 seq_cst, ; [#uses=0 type=i32] [debug line = 21:12]
+  ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
diff --git a/tests/cases/atomicrmw_unaligned.txt b/tests/cases/atomicrmw_unaligned.txt
new file mode 100644
index 0000000000000..45d16fb1f30e2
--- /dev/null
+++ b/tests/cases/atomicrmw_unaligned.txt
@@ -0,0 +1 @@
+hello, 50,53!
diff --git a/tests/printf/output.txt b/tests/printf/output.txt
index 19a6c1c2b2f24..0155f0da7b288 100644
--- a/tests/printf/output.txt
+++ b/tests/printf/output.txt
@@ -3,10 +3,15 @@ n=7
 
 Characters: a A
 Decimals: 1977 650000 12 4
-Preceding with blanks:       1977
-Preceding with zeros: 0000001977
+Preceding with blanks:       1977      -1977
+Preceding with zeros: 0000001977 -000001977
+Force sign: +1977 -1977  +1977  -1977
+Force sign or space:  1977 -1977   1977  -1977
+Sign overrides space: +1977 -1977  +1977  -1977
 Some different radixes: 100 64 144 0x64 0144
-floats: 3.14 +3e+00 3.141600E+00
+floats: 3.14 +3e+00 3.141600E+00 00003.14
+negative floats: -3.14 -3e+00 -3.141600E+00 -0003.14
+Force sign or space:  3.14 -3.14   3.14  -3.14
 Width trick:    10
 A string %
 Null string:  (null)
diff --git a/tests/printf/output_i64_1.txt b/tests/printf/output_i64_1.txt
index 775f3f8d01f6a..e38fb78f1881a 100644
--- a/tests/printf/output_i64_1.txt
+++ b/tests/printf/output_i64_1.txt
@@ -3,10 +3,15 @@ n=7
 
 Characters: a A
 Decimals: 1977 650000 12 4
-Preceding with blanks:       1977
-Preceding with zeros: 0000001977
+Preceding with blanks:       1977      -1977
+Preceding with zeros: 0000001977 -000001977
+Force sign: +1977 -1977  +1977  -1977
+Force sign or space:  1977 -1977   1977  -1977
+Sign overrides space: +1977 -1977  +1977  -1977
 Some different radixes: 100 64 144 0x64 0144
-floats: 3.14 +3e+00 3.141600E+00
+floats: 3.14 +3e+00 3.141600E+00 00003.14
+negative floats: -3.14 -3e+00 -3.141600E+00 -0003.14
+Force sign or space:  3.14 -3.14   3.14  -3.14
 Width trick:    10
 A string %
 Null string:  (null)
diff --git a/tests/printf/test.c b/tests/printf/test.c
index d05ba0965130b..1c8ad9f7691c2 100644
--- a/tests/printf/test.c
+++ b/tests/printf/test.c
@@ -8,10 +8,15 @@ int main() {
   printf("\n");
   printf("Characters: %c %c\n", 'a', 65);
   printf("Decimals: %d %ld %lld %d\n", 1977, 650000L, 12LL, 4);
-  printf("Preceding with blanks: %10d\n", 1977);
-  printf("Preceding with zeros: %010d\n", 1977);
+  printf("Preceding with blanks: %10d %10d\n", 1977, -1977);
+  printf("Preceding with zeros: %010d %010d\n", 1977, -1977);
+  printf("Force sign: %+d %+d %+6d %+6d\n", 1977, -1977, 1977, -1977);
+  printf("Force sign or space: % d % d % 6d % 6d\n", 1977, -1977, 1977, -1977);
+  printf("Sign overrides space: % +d % +d % +6d % +6d\n", 1977, -1977, 1977, -1977);
   printf("Some different radixes: %d %x %o %#x %#o\n", 100, 100, 100, 100, 100);
-  printf("floats: %4.2f %+.0e %E\n", 3.1416, 3.1416, 3.1416);
+  printf("floats: %4.2f %+.0e %E %08.2f\n", 3.1416, 3.1416, 3.1416, 3.1416);
+  printf("negative floats: %4.2f %+.0e %E %08.2f\n", -3.1416, -3.1416, -3.1416, -3.1416);
+  printf("Force sign or space: % .2f % .2f % 6.2f % 6.2f\n", 3.1416, -3.1416, 3.1416, -3.1416);
   printf("Width trick: %*d\n", 5, 10);
   printf("%s %%\n", "A string");
   printf("Null string: %7s\n", NULL);
diff --git a/tests/sdl_audio_beep.cpp b/tests/sdl_audio_beep.cpp
new file mode 100644
index 0000000000000..95a5a7e8c05f1
--- /dev/null
+++ b/tests/sdl_audio_beep.cpp
@@ -0,0 +1,246 @@
+#include <SDL/SDL.h>
+#include <SDL/SDL_audio.h>
+#include <queue>
+#include <cmath>
+#include <stdio.h>
+#include <assert.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846f
+#endif
+
+#ifdef EMSCRIPTEN
+#include "emscripten/emscripten.h"
+#endif
+
+#ifdef main
+#undef main
+#endif
+
+const int tone_duration = 1000;
+
+struct BeepObject {
+  double toneFrequency;
+  int samplesLeft;
+};
+
+class Beeper {
+private:
+  double phase;
+  int frequency;
+  int numChannels;
+  int mutedChannel;
+public:
+  Beeper(int frequency, int numChannels, int sdlAudioFormat);
+  ~Beeper();
+  void beep(double toneFrequency, int durationMSecs);
+  template<typename T>
+  void generateSamples(T *stream, int length);
+  void wait();
+
+  std::queue<BeepObject> beeps;
+  int sdlAudioFormat;
+};
+
+void audio_callback(void*, Uint8*, int);
+
+Beeper::Beeper(int frequency_, int numChannels_, int sdlAudioFormat_) {
+  phase = 0.0;
+  mutedChannel = 1;
+
+  SDL_AudioSpec desiredSpec;
+
+  desiredSpec.freq = frequency_;
+  desiredSpec.format = sdlAudioFormat_;
+  desiredSpec.channels = numChannels_;
+  desiredSpec.samples = 1024; // This is samples per channel.
+  desiredSpec.callback = audio_callback;
+  desiredSpec.userdata = this;
+
+  SDL_AudioSpec obtainedSpec;
+
+  // you might want to look for errors here
+  SDL_OpenAudio(&desiredSpec, &obtainedSpec);
+
+  // In this test, we require *exactly* the identical SDL result that we provide, since we test
+  // all various configurations individually.
+  if (obtainedSpec.freq != desiredSpec.freq || obtainedSpec.format != desiredSpec.format
+    || obtainedSpec.channels != desiredSpec.channels || obtainedSpec.samples != desiredSpec.samples) {
+    SDL_CloseAudio();
+    throw std::runtime_error("Failed to initialize desired SDL_OpenAudio!");
+  }
+
+  frequency = obtainedSpec.freq;
+  numChannels = obtainedSpec.channels;
+  sdlAudioFormat = obtainedSpec.format;
+
+  // Immediately start producing audio.
+  SDL_PauseAudio(0);
+}
+
+Beeper::~Beeper() {
+  SDL_CloseAudio();
+}
+
+template<typename T>
+void Beeper::generateSamples(T *stream, int length) {
+  const int AMPLITUDE = (sizeof(T) == 2) ? 28000 : 120;
+  const int offset = (sdlAudioFormat == AUDIO_U8) ? 120 : 0;
+
+  int i = 0;
+  length /= numChannels;
+  while (i < length) {
+    if (beeps.empty()) {
+      memset(stream + numChannels*i, 0, sizeof(T)*numChannels*(length-i));
+      return;
+    }
+    BeepObject& bo = beeps.front();
+
+    // In Stereo tests, mute one of the channels to be able to distinguish that Stereo output works.
+    if (bo.samplesLeft > tone_duration * frequency / 2 / 1000) {
+      mutedChannel = 1;
+    } else {
+      mutedChannel = 0;
+    }
+
+    int samplesToDo = std::min(i + bo.samplesLeft, length);
+    bo.samplesLeft -= samplesToDo - i;
+
+    while (i < samplesToDo) {
+      for(int j = 0; j < numChannels; ++j) {
+        stream[numChannels*i+j] = (T)(offset + (int)(AMPLITUDE * std::sin(phase * 2 * M_PI / frequency)));
+        if (numChannels > 1 && j == mutedChannel) {
+          stream[numChannels*i+j] = 0;
+        }
+      }
+      phase += bo.toneFrequency;
+      i++;
+    }
+
+    if (bo.samplesLeft == 0) {
+      beeps.pop();
+    }
+  }
+}
+
+void Beeper::beep(double toneFrequency, int durationMSecs) {
+  BeepObject bo;
+  bo.toneFrequency = toneFrequency;
+  bo.samplesLeft = durationMSecs * frequency / 1000;
+
+  SDL_LockAudio();
+  beeps.push(bo);
+  SDL_UnlockAudio();
+}
+
+Beeper *beep = 0;
+
+// Test all kinds of various possible formats. Not all are supported, but running this
+// test will report you which work.
+const int freqs[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 };
+const int channels[] = { 1, 2 };
+const int sdlAudioFormats[] = { AUDIO_U8, AUDIO_S16LSB /*, AUDIO_S8, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16MSB */ };
+
+const char *SdlAudioFormatToString(int sdlAudioType) {
+  switch(sdlAudioType) {
+  case AUDIO_U8: return "AUDIO_U8";
+  case AUDIO_S8: return "AUDIO_S8";
+  case AUDIO_U16LSB: return "AUDIO_U16LSB";
+  case AUDIO_U16MSB: return "AUDIO_U16MSB";
+  case AUDIO_S16LSB: return "AUDIO_S16LSB";
+  case AUDIO_S16MSB: return "AUDIO_S16MSB";
+  default: return "(unknown)";
+  }
+}
+
+#define NUM_ELEMS(x) (sizeof(x)/sizeof((x)[0]))
+
+// Indices to the currently running test.
+int f = -1;
+int c = 0;
+int s = 0;
+
+void nextTest(void *unused = 0) {
+  ++f;
+  if (f >= NUM_ELEMS(freqs)) {
+    f = 0;
+    ++c;
+    if (c >= NUM_ELEMS(channels)) {
+      c = 0;
+      ++s;
+      if (s >= NUM_ELEMS(sdlAudioFormats)) {
+        printf("All tests done. Quit.\n");
+#ifdef EMSCRIPTEN
+        emscripten_cancel_main_loop();
+#ifdef REPORT_RESULT
+        int result = 1;
+        REPORT_RESULT();
+#endif
+#endif
+        return;
+      }
+    }
+  }
+
+  double Hz = 440;
+  try {
+    beep = new Beeper(freqs[f], channels[c], sdlAudioFormats[s]);
+  } catch(...) {
+    printf("FAILED to play beep for %d msecs at %d Hz tone with audio format %s, %d channels, and %d samples/sec.\n",
+        tone_duration, (int)Hz, SdlAudioFormatToString(sdlAudioFormats[s]), channels[c], freqs[f]);
+    nextTest();
+    return;
+  }
+
+  printf("Playing back a beep for %d msecs at %d Hz tone with audio format %s, %d channels, and %d samples/sec.\n",
+      tone_duration, (int)Hz, SdlAudioFormatToString(sdlAudioFormats[s]), channels[c], freqs[f]);
+  beep->beep(Hz, tone_duration);
+}
+
+void update() {
+  SDL_LockAudio();
+  int size = beep->beeps.size();
+  SDL_UnlockAudio();
+  if (size == 0 && beep) {
+    delete beep;
+    beep = 0;
+#ifdef EMSCRIPTEN
+    emscripten_async_call(nextTest, 0, 1500);
+#else
+    SDL_Delay(1500);
+    nextTest();
+#endif
+  }
+}
+
+void audio_callback(void *_beeper, Uint8 *_stream, int _length) {
+  Beeper* beeper = (Beeper*) _beeper;
+
+  if (beeper->sdlAudioFormat == AUDIO_U8) {
+    Uint8 *stream = (Uint8*) _stream;
+    beeper->generateSamples(stream, _length);
+  } else if (beeper->sdlAudioFormat == AUDIO_S16LSB) {
+    Sint16 *stream = (Sint16*) _stream;
+    int length = _length / 2;
+    beeper->generateSamples(stream, length);
+  } else {
+    assert(false && "Audio sample generation not implemented for current format!\n");
+  }
+}
+
+int main(int argc, char** argv) {
+  SDL_Init(SDL_INIT_AUDIO);
+
+  nextTest();
+   
+#ifdef EMSCRIPTEN
+  emscripten_set_main_loop(update, 60, 0);
+#else
+  while(beep) {
+    SDL_Delay(20);
+    update();
+  }
+#endif
+
+  return 0;
+}
diff --git a/tests/sdl_canvas_alpha.c b/tests/sdl_canvas_alpha.c
new file mode 100644
index 0000000000000..1a41d115b090f
--- /dev/null
+++ b/tests/sdl_canvas_alpha.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_ttf.h>
+#include <emscripten.h>
+
+
+int main(int argc, char **argv) {
+  SDL_Init(SDL_INIT_VIDEO);
+  SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
+
+  printf("Init: %d\n", TTF_Init());
+
+  TTF_Font *font = TTF_OpenFont("sans-serif", 40);
+  printf("Font: %p\n", font);
+
+  SDL_Color color = { 0xff, 0x99, 0x00, 0xff };
+  SDL_Surface *text = TTF_RenderText_Solid(font, "hello orange world", color);
+
+  // render
+  for (int i = 0; i < 255; i++) {
+    SDL_Rect dest = { i, i, 0, 0 };
+    SDL_SetAlpha(text, 0, (((float)i)/255)*(((float)i)/255)*255);
+    SDL_BlitSurface (text, NULL, screen, &dest);
+  }
+
+  SDL_Flip(screen); 
+
+  SDL_LockSurface(screen);
+
+  int width, height, isFullscreen;
+  emscripten_get_canvas_size(&width, &height, &isFullscreen);
+
+  if (width != 600 && height != 450)
+  {
+    printf("error: wrong width/height\n");
+    abort();
+  }
+
+  SDL_Quit();
+
+  printf("done.\n");
+
+  return 0;
+}
+
diff --git a/tests/sdl_canvas_alpha.png b/tests/sdl_canvas_alpha.png
new file mode 100644
index 0000000000000..fb9d61651fa5f
Binary files /dev/null and b/tests/sdl_canvas_alpha.png differ
diff --git a/tests/test_browser.py b/tests/test_browser.py
index a3b9a1c31910d..8d000cd7a331c 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -19,6 +19,7 @@ def audio():
       'test_sdl_audio_mix_channels',
       'test_sdl_audio_mix',
       'test_sdl_audio_quickload',
+      'test_sdl_audio_beeps',
       'test_openal_playback',
       'test_openal_buffers',
       'test_freealut'
@@ -649,6 +650,9 @@ def post():
 
     self.btest('sdl_canvas_proxy.c', reference='sdl_canvas_proxy.png', args=['--proxy-to-worker', '--preload-file', 'data.txt'], manual_reference=True, post_build=post)
 
+  def test_sdl_canvas_alpha(self):
+    self.btest('sdl_canvas_alpha.c', reference='sdl_canvas_alpha.png')
+
   def test_sdl_key(self):
     open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
       Module.postRun = function() {
@@ -923,6 +927,13 @@ def test_sdl_audio_quickload(self):
     Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate()
     self.run_browser('page.html', '', '/report_result?1')
 
+  def test_sdl_audio_beeps(self):
+    open(os.path.join(self.get_dir(), 'sdl_audio_beep.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_beep.cpp')).read()))
+
+    # use closure to check for a possible bug with closure minifying away newer Audio() attributes
+    Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_beep.cpp'), '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-o', 'page.html']).communicate()
+    self.run_browser('page.html', '', '/report_result?1')
+
   def test_sdl_gl_read(self):
     # SDL, OpenGL, readPixels
     open(os.path.join(self.get_dir(), 'sdl_gl_read.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_gl_read.c')).read()))
diff --git a/tests/test_core.py b/tests/test_core.py
index 4c17a942ee090..d1d3bab08e64e 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1372,6 +1372,50 @@ def test_floatvars(self):
       '''
       self.do_run(src, '*1,10,10.5,1,1.2340,0.00*\n0.50, 3.30, 3.30, 3.30\nsmall: 0.0000010000\n')
 
+  def test_zerodiv(self):
+    self.do_run(r'''
+      #include <stdio.h>
+      int main(int argc, const char* argv[])
+      {
+        float f1 = 1.0f;
+        float f2 = 0.0f;
+        float f_zero = 0.0f;
+
+        float f3 = 0.0f / f2;
+        float f4 = f2 / 0.0f;
+        float f5 = f2 / f2;
+        float f6 = f2 / f_zero;
+
+        printf("f3: %f\n", f3);
+        printf("f4: %f\n", f4);
+        printf("f5: %f\n", f5);
+        printf("f6: %f\n", f6);
+
+        return 0;
+      }
+    ''', '''f3: nan
+f4: nan
+f5: nan
+f6: nan
+''')
+
+  def test_zero_multiplication(self):
+    src = '''
+      #include <stdio.h>
+      int main(int argc, char * argv[]) {
+        int one = argc;
+
+        printf("%d ", 0 * one);
+        printf("%d ", 0 * -one);
+        printf("%d ", -one * 0);
+        printf("%g ", 0.0 * one);
+        printf("%g ", 0.0 * -one);
+        printf("%g", -one * 0.0);
+        return 0;
+      }
+    '''
+    self.do_run(src, '0 0 0 0 -0 -0')
+
   def test_isnan(self):
     src = r'''
       #include <stdio.h>
@@ -8937,6 +8981,8 @@ def test_lifetime(self):
   def test_cases(self):
     if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly")
 
+    emcc_args = self.emcc_args
+
     try:
       os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1'
       Settings.CHECK_OVERFLOWS = 0
@@ -8950,6 +8996,10 @@ def test_cases(self):
         if '_noasm' in shortname and Settings.ASM_JS:
           print self.skip('case "%s" not relevant for asm.js' % shortname)
           continue
+        self.emcc_args = emcc_args
+        if os.path.exists(shortname + '.emcc'):
+          if not self.emcc_args: continue
+          self.emcc_args = self.emcc_args + json.loads(open(shortname + '.emcc').read())
         print >> sys.stderr, "Testing case '%s'..." % shortname
         output_file = path_from_root('tests', 'cases', shortname + '.txt')
         if Settings.QUANTUM_SIZE == 1:
@@ -8970,6 +9020,7 @@ def test_cases(self):
 
     finally:
       del os.environ['EMCC_LEAVE_INPUTS_RAW']
+      self.emcc_args = emcc_args
 
   def test_fuzz(self):
     if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2')
diff --git a/tests/test_other.py b/tests/test_other.py
index 56c13650c5750..9f331439911e2 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -1665,10 +1665,10 @@ def test_chunking(self):
     if multiprocessing.cpu_count() < 2: return self.skip('need multiple cores')
     try:
       os.environ['EMCC_DEBUG'] = '1'
-      os.environ['EMCC_CORES'] = '2'
+      os.environ['EMCC_CORES'] = '2' # standardize over machines
       for asm, linkable, chunks, js_chunks in [
-          (0, 0, 3, 2), (0, 1, 3, 4),
-          (1, 0, 3, 2), (1, 1, 3, 4)
+          (0, 0, 2, 2), (0, 1, 2, 4),
+          (1, 0, 2, 2), (1, 1, 2, 4)
         ]:
         print asm, linkable, chunks, js_chunks
         output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate()
diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py
index 12e76223e6110..d6f8921c9bb21 100644
--- a/tools/js_optimizer.py
+++ b/tools/js_optimizer.py
@@ -377,5 +377,7 @@ def run(filename, passes, js_engine=shared.NODE_JS, jcache=False, source_map=Fal
     sys.argv = sys.argv[:-1]
   else:
     extra_info = None
-  run(sys.argv[1], sys.argv[2:], extra_info=extra_info)
+  out = run(sys.argv[1], sys.argv[2:], extra_info=extra_info)
+  import shutil
+  shutil.copyfile(out, sys.argv[1] + '.jsopt.js')
 
diff --git a/tools/shared.py b/tools/shared.py
index 4e1a3ebf163fe..4d0f92b5508b9 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -304,7 +304,7 @@ def find_temp_directory():
 # we re-check sanity when the settings are changed)
 # We also re-check sanity and clear the cache when the version changes
 
-EMSCRIPTEN_VERSION = '1.6.1'
+EMSCRIPTEN_VERSION = '1.6.2'
 
 def generate_sanity():
   return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT
@@ -1413,6 +1413,9 @@ def ensure_relooper(relooper):
       emcc_debug = os.environ.get('EMCC_DEBUG')
       if emcc_debug: del os.environ['EMCC_DEBUG']
 
+      emcc_leave_inputs_raw = os.environ.get('EMCC_LEAVE_INPUTS_RAW')
+      if emcc_leave_inputs_raw: del os.environ['EMCC_LEAVE_INPUTS_RAW']
+
       def make(opt_level):
         raw = relooper + '.raw.js'
         Building.emcc(os.path.join('relooper', 'Relooper.cpp'), ['-I' + os.path.join('relooper'), '--post-js',
@@ -1442,8 +1445,10 @@ def make(opt_level):
     finally:
       os.chdir(curr)
       if emcc_debug: os.environ['EMCC_DEBUG'] = emcc_debug
+      if emcc_leave_inputs_raw: os.environ['EMCC_LEAVE_INPUTS_RAW'] = emcc_leave_inputs_raw
       if not ok:
         logging.error('bootstrapping relooper failed. You may need to manually create relooper.js by compiling it, see src/relooper/emscripten')
+        try_delete(relooper) # do not leave a phase-1 version if phase 2 broke
         1/0
 
   @staticmethod