diff --git a/barebones/Makefile b/barebones/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..e177a6d3ffbabad720d1ce9e940b498649e7696a
--- /dev/null
+++ b/barebones/Makefile
@@ -0,0 +1,36 @@
+TARGET:=GPIO_Toggle
+
+all : flash
+
+PREFIX:=riscv64-unknown-elf
+
+GPIO_Toggle:=EXAM/GPIO/GPIO_Toggle/User
+
+CFLAGS:= \
+	-Os -flto -ffunction-sections \
+	-march=rv32emc \
+	-mabi=ilp32e \
+	-I/usr/include/newlib \
+	-nostdlib \
+	-I.
+
+LDFLAGS:=-T ch32v003.ld
+
+SYSTEM_C:=startup_ch32v00x.S system_ch32v00x.c
+
+$(TARGET).elf : barebones.c $(SYSTEM_C)
+	$(PREFIX)-gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
+
+$(TARGET).hex : GPIO_Toggle.elf
+	$(PREFIX)-size $^
+	$(PREFIX)-objdump -S $^ > $(TARGET).lst
+	$(PREFIX)-objdump -t $^ > $(TARGET).map
+	#$(PREFIX)-objcopy -O binary $< $@
+	$(PREFIX)-objcopy -O ihex $< $@
+
+	
+flash : $(TARGET).hex
+
+clean :
+	rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).lst $(TARGET).map
+
diff --git a/barebones/barebones.c b/barebones/barebones.c
new file mode 100644
index 0000000000000000000000000000000000000000..46581d1bef81e30b611f08f07910f413221d9c03
--- /dev/null
+++ b/barebones/barebones.c
@@ -0,0 +1,16 @@
+#include "ch32v00x.h"
+
+int main()
+{
+	// Enable GPIOD.
+	RCC->APB2PCENR |= 0x20; //RCC_APB2Periph_GPIOD
+
+	// Push-Pull, 50MHz Output
+	GPIOD->CFGLR = (GPIOD->CFGLR & 0xfffffff0) | 3;
+	
+	while(1)
+	{
+		GPIOD->BSHR = 1;     // Turn on GPIOD0
+		GPIOD->BSHR = 1<<16; // Turn off GPIOD0
+	}
+}
\ No newline at end of file
diff --git a/official/Makefile b/official/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c4c7035915ff5e6448b5c5d3057a197399358e81
--- /dev/null
+++ b/official/Makefile
@@ -0,0 +1,46 @@
+TARGET:=GPIO_Toggle
+
+all : flash
+
+PREFIX:=riscv64-unknown-elf
+
+GPIO_Toggle:=EXAM/GPIO/GPIO_Toggle/User
+
+CFLAGS:= \
+	-Os -flto -ffunction-sections \
+	-march=rv32emc \
+	-mabi=ilp32e \
+	-I/usr/include/newlib \
+	-nostdlib \
+	-I$(GPIO_Toggle) \
+	-IEXAM/SRC/Core \
+	-IEXAM/SRC/Debug \
+	-IEXAM/SRC/Debug \
+	-IEXAM/SRC/Peripheral/inc
+
+LDFLAGS:=-T EXAM/SRC/Ld/Link.ld
+
+SYSTEM_C:=EXAM/SRC/Startup/startup_ch32v00x.S \
+	EXAM/SRC/Debug/debug.c \
+	EXAM/SRC/Peripheral/src/ch32v00x_gpio.c \
+	EXAM/SRC/Peripheral/src/ch32v00x_rcc.c \
+	EXAM/SRC/Peripheral/src/ch32v00x_usart.c \
+	EXAM/SRC/Peripheral/src/ch32v00x_misc.c \
+	embedlibc.c
+
+$(TARGET).elf : $(GPIO_Toggle)/ch32v00x_it.c $(GPIO_Toggle)/main.c $(GPIO_Toggle)/system_ch32v00x.c $(SYSTEM_C)
+	$(PREFIX)-gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
+
+$(TARGET).hex : GPIO_Toggle.elf
+	$(PREFIX)-size $^
+	$(PREFIX)-objdump -S $^ > $(TARGET).lst
+	$(PREFIX)-objdump -t $^ > $(TARGET).map
+	#$(PREFIX)-objcopy -O binary $< $@
+	$(PREFIX)-objcopy -O ihex $< $@
+
+	
+flash : $(TARGET).hex
+
+clean :
+	rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).lst $(TARGET).map
+
diff --git a/official/README.md b/official/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..23e1f591092c944e8cb23e078a916b23bcb2caa7
--- /dev/null
+++ b/official/README.md
@@ -0,0 +1,26 @@
+# General Tooling Notes
+
+This shows how to build the CH32V003 demo binaries without the horrifying MounRiver Studio.
+
+* Product page: https://www.wch.cn/products/CH32V003.html
+* or English: http://www.wch-ic.com/products/CH32V003.html
+* It has, for the English datasheet http://www.wch-ic.com/downloads/file/359.html "CH32V003DS0.PDF"
+* It also has the TRM http://www.wch-ic.com/downloads/CH32V003RM_PDF.html in English
+* Sadly, the EVT is CN only. https://www.wch.cn/downloads/CH32V003EVT_ZIP.html - It contains schematics for the devboard, evaluation board reference (instructions) and the rest of the dev system, minus the MounRiver Studio (MRS).
+* Software from WCH for WCH-Link: https://www.wch-ic.com/products/WCH-Link.html
+* Get https://www.wch.cn/downloads/WCH-LinkUtility_ZIP.html
+
+Steps:
+1. Install WCHLinkDrv_WHQL_S.exe from Wch-LinkUtil/Drv_Link
+2. Copy contents of EVT (CH32V003EVT.ZIP) "EXAM" Folder in to this folder.
+3. On WSL or Debian based OSes `apt-get install build-essential libnewlib-dev gcc-riscv64-unknown-elf`
+4. Type `make`
+5. Open Wch-LinkUtil
+6. Select series "CH32V00X" from pull-down.
+7. Open the produced .hex file.
+8. Target->Program (F10)
+9. Target->Reset (F12)
+
+Profit.
+
+
diff --git a/official/embedlibc.c b/official/embedlibc.c
new file mode 100644
index 0000000000000000000000000000000000000000..d31efbae24a127bd553f539c6984d363e93214f0
--- /dev/null
+++ b/official/embedlibc.c
@@ -0,0 +1,646 @@
+// Use with newlib headers.
+// Mixture of weblibc, mini-printf and ???
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+int errno;
+int _write(int fd, const char *buf, int size);
+
+int mini_vsnprintf(char *buffer, unsigned int buffer_len, const char *fmt, va_list va);
+int mini_vpprintf(int (*puts)(char* s, int len, void* buf), void* buf, const char *fmt, va_list va);
+
+static int __puts_uart(char *s, int len, void *buf)
+{
+	_write( 0, s, len );
+}
+
+int printf(const char* format, ...)
+{
+	va_list args;
+	va_start( args, format );
+	int ret_status = mini_vpprintf(__puts_uart, 0, format, args);
+	va_end( args );
+	return ret_status;
+}
+
+/* Some stuff from MUSL
+
+
+----------------------------------------------------------------------
+Copyright © 2005-2020 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+----------------------------------------------------------------------
+
+*/
+
+/*
+ * mbstate_t is an opaque object to keep conversion state, during multibyte
+ * stream conversions.  The content must not be referenced by user programs.
+ */
+
+#define CURRENT_UTF8 0
+#define IS_CODEUNIT(c) ((unsigned)(c)-0xdf80 < 0x80)
+#define MB_CUR_MAX (CURRENT_UTF8 ? 4 : 1)
+
+typedef void * mbstate_t;
+
+size_t wcrtomb(char *restrict s, wchar_t wc, mbstate_t *restrict st)
+{
+	if (!s) return 1;
+	if ((unsigned)wc < 0x80) {
+		*s = wc;
+		return 1;
+	} else if (MB_CUR_MAX == 1) {
+		if (!IS_CODEUNIT(wc)) {
+			errno = 0x02; // EILSEQ
+			return -1;
+		}
+		*s = wc;
+		return 1;
+	} else if ((unsigned)wc < 0x800) {
+		*s++ = 0xc0 | (wc>>6);
+		*s = 0x80 | (wc&0x3f);
+		return 2;
+	} else if ((unsigned)wc < 0xd800 || (unsigned)wc-0xe000 < 0x2000) {
+		*s++ = 0xe0 | (wc>>12);
+		*s++ = 0x80 | ((wc>>6)&0x3f);
+		*s = 0x80 | (wc&0x3f);
+		return 3;
+	} else if ((unsigned)wc-0x10000 < 0x100000) {
+		*s++ = 0xf0 | (wc>>18);
+		*s++ = 0x80 | ((wc>>12)&0x3f);
+		*s++ = 0x80 | ((wc>>6)&0x3f);
+		*s = 0x80 | (wc&0x3f);
+		return 4;
+	}
+	errno = 0x02;//EILSEQ;
+	return -1;
+}
+int wctomb(char *s, wchar_t wc)
+{
+	if (!s) return 0;
+	return wcrtomb(s, wc, 0);
+}
+size_t strlen(const char *s) { const char *a = s;for (; *s; s++);return s-a; }
+size_t strnlen(const char *s, size_t n) { const char *p = memchr(s, 0, n); return p ? p-s : n;}
+void *memset(void *dest, int c, size_t n) { unsigned char *s = dest; for (; n; n--, s++) *s = c; }
+char *strcpy(char *d, const char *s) { for (; (*d=*s); s++, d++); }
+char *strncpy(char *d, const char *s, size_t n) { for (; n && (*d=*s); n--, s++, d++); }
+int strcmp(const char *l, const char *r)
+{
+	for (; *l==*r && *l; l++, r++);
+	return *(unsigned char *)l - *(unsigned char *)r;
+}
+int strncmp(const char *_l, const char *_r, size_t n)
+{
+	const unsigned char *l=(void *)_l, *r=(void *)_r;
+	if (!n--) return 0;
+	for (; *l && *r && n && *l == *r ; l++, r++, n--);
+	return *l - *r;
+}
+
+static char *twobyte_strstr(const unsigned char *h, const unsigned char *n)
+{
+	uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
+	for (h++; *h && hw != nw; hw = hw<<8 | *++h);
+	return *h ? (char *)h-1 : 0;
+}
+
+static char *threebyte_strstr(const unsigned char *h, const unsigned char *n)
+{
+	uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8;
+	uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8;
+	for (h+=2; *h && hw != nw; hw = (hw|*++h)<<8);
+	return *h ? (char *)h-2 : 0;
+}
+
+static char *fourbyte_strstr(const unsigned char *h, const unsigned char *n)
+{
+	uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
+	uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
+	for (h+=3; *h && hw != nw; hw = hw<<8 | *++h);
+	return *h ? (char *)h-3 : 0;
+}
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#define BITOP(a,b,op) \
+ ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
+
+static char *twoway_strstr(const unsigned char *h, const unsigned char *n)
+{
+	const unsigned char *z;
+	size_t l, ip, jp, k, p, ms, p0, mem, mem0;
+	size_t byteset[32 / sizeof(size_t)] = { 0 };
+	size_t shift[256];
+
+	/* Computing length of needle and fill shift table */
+	for (l=0; n[l] && h[l]; l++)
+		BITOP(byteset, n[l], |=), shift[n[l]] = l+1;
+	if (n[l]) return 0; /* hit the end of h */
+
+	/* Compute maximal suffix */
+	ip = -1; jp = 0; k = p = 1;
+	while (jp+k<l) {
+		if (n[ip+k] == n[jp+k]) {
+			if (k == p) {
+				jp += p;
+				k = 1;
+			} else k++;
+		} else if (n[ip+k] > n[jp+k]) {
+			jp += k;
+			k = 1;
+			p = jp - ip;
+		} else {
+			ip = jp++;
+			k = p = 1;
+		}
+	}
+	ms = ip;
+	p0 = p;
+
+	/* And with the opposite comparison */
+	ip = -1; jp = 0; k = p = 1;
+	while (jp+k<l) {
+		if (n[ip+k] == n[jp+k]) {
+			if (k == p) {
+				jp += p;
+				k = 1;
+			} else k++;
+		} else if (n[ip+k] < n[jp+k]) {
+			jp += k;
+			k = 1;
+			p = jp - ip;
+		} else {
+			ip = jp++;
+			k = p = 1;
+		}
+	}
+	if (ip+1 > ms+1) ms = ip;
+	else p = p0;
+
+	/* Periodic needle? */
+	if (memcmp(n, n+p, ms+1)) {
+		mem0 = 0;
+		p = MAX(ms, l-ms-1) + 1;
+	} else mem0 = l-p;
+	mem = 0;
+
+	/* Initialize incremental end-of-haystack pointer */
+	z = h;
+
+	/* Search loop */
+	for (;;) {
+		/* Update incremental end-of-haystack pointer */
+		if (z-h < l) {
+			/* Fast estimate for MAX(l,63) */
+			size_t grow = l | 63;
+			const unsigned char *z2 = memchr(z, 0, grow);
+			if (z2) {
+				z = z2;
+				if (z-h < l) return 0;
+			} else z += grow;
+		}
+
+		/* Check last byte first; advance by shift on mismatch */
+		if (BITOP(byteset, h[l-1], &)) {
+			k = l-shift[h[l-1]];
+			if (k) {
+				if (k < mem) k = mem;
+				h += k;
+				mem = 0;
+				continue;
+			}
+		} else {
+			h += l;
+			mem = 0;
+			continue;
+		}
+
+		/* Compare right half */
+		for (k=MAX(ms+1,mem); n[k] && n[k] == h[k]; k++);
+		if (n[k]) {
+			h += k-ms;
+			mem = 0;
+			continue;
+		}
+		/* Compare left half */
+		for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
+		if (k <= mem) return (char *)h;
+		h += p;
+		mem = mem0;
+	}
+}
+
+char *strstr(const char *h, const char *n)
+{
+	/* Return immediately on empty needle */
+	if (!n[0]) return (char *)h;
+
+	/* Use faster algorithms for short needles */
+	h = strchr(h, *n);
+	if (!h || !n[1]) return (char *)h;
+	if (!h[1]) return 0;
+	if (!n[2]) return twobyte_strstr((void *)h, (void *)n);
+	if (!h[2]) return 0;
+	if (!n[3]) return threebyte_strstr((void *)h, (void *)n);
+	if (!h[3]) return 0;
+	if (!n[4]) return fourbyte_strstr((void *)h, (void *)n);
+
+	return twoway_strstr((void *)h, (void *)n);
+}
+
+char *strchr(const char *s, int c)
+{
+	c = (unsigned char)c;
+	if (!c) return (char *)s + strlen(s);
+	for (; *s && *(unsigned char *)s != c; s++);
+	return (char *)s;
+}
+
+
+void *__memrchr(const void *m, int c, size_t n)
+{
+	const unsigned char *s = m;
+	c = (unsigned char)c;
+	while (n--) if (s[n]==c) return (void *)(s+n);
+	return 0;
+}
+
+char *strrchr(const char *s, int c)
+{
+	return __memrchr(s, c, strlen(s) + 1);
+}
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+	unsigned char *d = dest;
+	const unsigned char *s = src;
+	for (; n; n--) *d++ = *s++;
+	return dest;
+}
+
+int memcmp(const void *vl, const void *vr, size_t n)
+{
+	const unsigned char *l=vl, *r=vr;
+	for (; n && *l == *r; n--, l++, r++);
+	return n ? *l-*r : 0;
+}
+
+
+void *memmove(void *dest, const void *src, size_t n)
+{
+	char *d = dest;
+	const char *s = src;
+
+	if (d==s) return d;
+	if ((uintptr_t)s-(uintptr_t)d-n <= -2*n) return memcpy(d, s, n);
+
+	if (d<s) {
+		for (; n; n--) *d++ = *s++;
+	} else {
+		while (n) n--, d[n] = s[n];
+	}
+
+	return dest;
+}
+void *memchr(const void *src, int c, size_t n)
+{
+	const unsigned char *s = src;
+	c = (unsigned char)c;
+	for (; n && *s != c; s++, n--);
+	return n ? (void *)s : 0;
+}
+
+int puts(const char *s)
+{
+	int sl = strlen( s );
+	_write(0, s, sl );
+	_write(0, "\n", 1 );
+	return sl + 1;
+}
+
+/*
+ * The Minimal snprintf() implementation
+ *
+ * Copyright (c) 2013,2014 Michal Ludvig <michal@logix.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the auhor nor the names of its contributors
+ *       may be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ----
+ *
+ * This is a minimal snprintf() implementation optimised
+ * for embedded systems with a very limited program memory.
+ * mini_snprintf() doesn't support _all_ the formatting
+ * the glibc does but on the other hand is a lot smaller.
+ * Here are some numbers from my STM32 project (.bin file size):
+ *      no snprintf():      10768 bytes
+ *      mini snprintf():    11420 bytes     (+  652 bytes)
+ *      glibc snprintf():   34860 bytes     (+24092 bytes)
+ * Wasting nearly 24kB of memory just for snprintf() on
+ * a chip with 32kB flash is crazy. Use mini_snprintf() instead.
+ *
+ */
+
+#define mini_strlen strlen
+
+static int
+mini_itoa(long value, unsigned int radix, int uppercase, int unsig,
+	 char *buffer)
+{
+	char	*pbuffer = buffer;
+	int	negative = 0;
+	int	i, len;
+
+	/* No support for unusual radixes. */
+	if (radix > 16)
+		return 0;
+
+	if (value < 0 && !unsig) {
+		negative = 1;
+		value = -value;
+	}
+
+	/* This builds the string back to front ... */
+	do {
+		int digit = value % radix;
+		*(pbuffer++) = (digit < 10 ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10);
+		value /= radix;
+	} while (value > 0);
+
+	if (negative)
+		*(pbuffer++) = '-';
+
+	*(pbuffer) = '\0';
+
+	/* ... now we reverse it (could do it recursively but will
+	 * conserve the stack space) */
+	len = (pbuffer - buffer);
+	for (i = 0; i < len / 2; i++) {
+		char j = buffer[i];
+		buffer[i] = buffer[len-i-1];
+		buffer[len-i-1] = j;
+	}
+
+	return len;
+}
+
+static int
+mini_pad(char* ptr, int len, char pad_char, int pad_to, char *buffer)
+{
+	int i;
+	int overflow = 0;
+	char * pbuffer = buffer;
+	if(pad_to == 0) pad_to = len;
+	if(len > pad_to) {
+		len = pad_to;
+		overflow = 1;
+	}
+	for(i = pad_to - len; i > 0; i --) {
+		*(pbuffer++) = pad_char;
+	}
+	for(i = len; i > 0; i --) {
+		*(pbuffer++) = *(ptr++);
+	}
+	len = pbuffer - buffer;
+	if(overflow) {
+		for (i = 0; i < 3 && pbuffer > buffer; i ++) {
+			*(pbuffer-- - 1) = '*';
+		}
+	}
+	return len;
+}
+
+struct mini_buff {
+	char *buffer, *pbuffer;
+	unsigned int buffer_len;
+};
+
+static int
+_puts(char *s, int len, void *buf)
+{
+	if(!buf) return len;
+	struct mini_buff *b = buf;
+	char * p0 = b->buffer;
+	int i;
+	/* Copy to buffer */
+	for (i = 0; i < len; i++) {
+		if(b->pbuffer == b->buffer + b->buffer_len - 1) {
+			break;
+		}
+		*(b->pbuffer ++) = s[i];
+	}
+	*(b->pbuffer) = 0;
+	return b->pbuffer - p0;
+}
+
+#ifdef MINI_PRINTF_ENABLE_OBJECTS
+static int (*mini_handler) (void* data, void* obj, int ch, int lhint, char** bf) = 0;
+static void (*mini_handler_freeor)(void* data, void*) = 0;
+static void * mini_handler_data = 0;
+
+void mini_printf_set_handler(
+	void* data,
+	int (*handler)(void* data, void* obj, int ch, int len_hint, char** buf),
+	void (*freeor)(void* data, void* buf))
+{
+	mini_handler = handler;
+	mini_handler_freeor = freeor;
+	mini_handler_data = data;
+}
+#endif
+
+int
+mini_vsnprintf(char *buffer, unsigned int buffer_len, const char *fmt, va_list va)
+{
+	struct mini_buff b;
+	b.buffer = buffer;
+	b.pbuffer = buffer;
+	b.buffer_len = buffer_len;
+	if(buffer_len == 0) buffer = (void*) 0;
+	int n = mini_vpprintf(_puts, (buffer != (void*)0)?&b:(void*)0, fmt, va);
+	if(buffer == (void*) 0) {
+		return n;
+	}
+	return b.pbuffer - b.buffer;
+}
+
+int
+mini_vpprintf(int (*puts)(char* s, int len, void* buf), void* buf, const char *fmt, va_list va)
+{
+	char bf[24];
+	char bf2[24];
+	char ch;
+#ifdef MINI_PRINTF_ENABLE_OBJECTS
+	void* obj;
+#endif
+	if(puts == (void*)0) {
+		/* run puts in counting mode. */
+		puts = _puts; buf = (void*)0;
+	}
+	int n = 0;
+	while ((ch=*(fmt++))) {
+		int len;
+		if (ch!='%') {
+			len = 1;
+			len = puts(&ch, len, buf);
+		} else {
+			char pad_char = ' ';
+			int pad_to = 0;
+			char l = 0;
+			char *ptr;
+
+			ch=*(fmt++);
+
+			/* Zero padding requested */
+			if (ch == '0') pad_char = '0';
+			while (ch >= '0' && ch <= '9') {
+				pad_to = pad_to * 10 + (ch - '0');
+				ch=*(fmt++);
+			}
+			if(pad_to > (signed int) sizeof(bf)) {
+				pad_to = sizeof(bf);
+			}
+			if (ch == 'l') {
+				l = 1;
+				ch=*(fmt++);
+			}
+
+			switch (ch) {
+				case 0:
+					goto end;
+				case 'u':
+				case 'd':
+					if(l) {
+						len = mini_itoa(va_arg(va, unsigned long), 10, 0, (ch=='u'), bf2);
+					} else {
+						if(ch == 'u') {
+							len = mini_itoa((unsigned long) va_arg(va, unsigned int), 10, 0, 1, bf2);
+						} else {
+							len = mini_itoa((long) va_arg(va, int), 10, 0, 0, bf2);
+						}
+					}
+					len = mini_pad(bf2, len, pad_char, pad_to, bf);
+					len = puts(bf, len, buf);
+					break;
+
+				case 'x':
+				case 'X':
+					if(l) {
+						len = mini_itoa(va_arg(va, unsigned long), 16, (ch=='X'), 1, bf2);
+					} else {
+						len = mini_itoa((unsigned long) va_arg(va, unsigned int), 16, (ch=='X'), 1, bf2);
+					}
+					len = mini_pad(bf2, len, pad_char, pad_to, bf);
+					len = puts(bf, len, buf);
+					break;
+
+				case 'c' :
+					ch = (char)(va_arg(va, int));
+					len = mini_pad(&ch, 1, pad_char, pad_to, bf);
+					len = puts(bf, len, buf);
+					break;
+
+				case 's' :
+					ptr = va_arg(va, char*);
+					len = mini_strlen(ptr);
+					if (pad_to > 0) {
+						len = mini_pad(ptr, len, pad_char, pad_to, bf);
+						len = puts(bf, len, buf);
+					} else {
+						len = puts(ptr, len, buf);
+					}
+					break;
+#ifdef MINI_PRINTF_ENABLE_OBJECTS
+				case 'O' :  /* Object by content (e.g. str) */
+				case 'R' :  /* Object by representation (e.g. repr)*/
+					obj = va_arg(va, void*);
+					len = mini_handler(mini_handler_data, obj, ch, pad_to, &ptr);
+					if (pad_to > 0) {
+						len = mini_pad(ptr, len, pad_char, pad_to, bf);
+						len = puts(bf, len, buf);
+					} else {
+						len = puts(ptr, len, buf);
+					}
+					mini_handler_freeor(mini_handler_data, ptr);
+					break;
+#endif
+				default:
+					len = 1;
+					len = puts(&ch, len, buf);
+					break;
+			}
+		}
+		n = n + len;
+	}
+end:
+	return n;
+}
+
+
+int
+mini_snprintf(char* buffer, unsigned int buffer_len, const char *fmt, ...)
+{
+	int ret;
+	va_list va;
+	va_start(va, fmt);
+	ret = mini_vsnprintf(buffer, buffer_len, fmt, va);
+	va_end(va);
+
+	return ret;
+}
+
+int
+mini_pprintf(int (*puts)(char*s, int len, void* buf), void* buf, const char *fmt, ...)
+{
+	int ret;
+	va_list va;
+	va_start(va, fmt);
+	ret = mini_vpprintf(puts, buf, fmt, va);
+	va_end(va);
+
+	return ret;
+}
+