diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h
index 1634bcb8e1d59b10b564fba9e94370168f4dddee..ba320ecf0082e95111d7f9ee4ab39ab5bf0f5223 100644
--- a/ch32v003fun/ch32v003fun.h
+++ b/ch32v003fun/ch32v003fun.h
@@ -5082,6 +5082,68 @@ int _write(int fd, const char *buf, int size);
 
 #endif
 
+// xw_ext.inc, thanks to @macyler, @jnk0le, @duk for this reverse engineering.
+
+/*
+Encoder for some of the proprietary 'XW' RISC-V instructions present on the QingKe RV32 processor.
+Examples:
+	XW_C_LBU(a3, a1, 27); // c.xw.lbu a3, 27(a1)
+	XW_C_SB(a0, s0, 13);  // c.xw.sb a0, 13(s0)
+
+	XW_C_LHU(a5, a5, 38); // c.xw.lhu a5, 38(a5)
+	XW_C_SH(a2, s1, 14);  // c.xw.sh a2, 14(s1)
+*/
+
+// Let us do some compile-time error checking.
+#define ASM_ASSERT(COND) .if (!(COND)); .err; .endif
+
+// Integer encodings of the possible compressed registers.
+#define C_s0 0
+#define C_s1 1
+#define C_a0 2
+#define C_a1 3
+#define C_a2 4
+#define C_a3 5
+#define C_a4 6
+#define C_a5 7
+
+// register to encoding
+#define REG2I(X) (C_ ## X)
+
+// XW opcodes
+#define XW_OP_LBUSP 0b1000000000000000
+#define XW_OP_STSP  0b1000000001000000
+
+#define XW_OP_LHUSP 0b1000000000100000
+#define XW_OP_SHSP  0b1000000001100000
+
+#define XW_OP_LBU   0b0010000000000000
+#define XW_OP_SB    0b1010000000000000
+
+#define XW_OP_LHU   0b0010000000000010
+#define XW_OP_SH    0b1010000000000010
+
+// The two different XW encodings supported at the moment.
+#define XW_ENCODE1(OP, R1, R2, IMM) ASM_ASSERT((IMM) >= 0 && (IMM) < 32); .2byte ((OP) | (REG2I(R1) << 2) | (REG2I(R2) << 7) | \
+	(((IMM) & 0b1) << 12) | (((IMM) & 0b110) << (5 - 1)) | (((IMM) & 0b11000) << (10 - 3)))
+
+#define XW_ENCODE2(OP, R1, R2, IMM) ASM_ASSERT((IMM) >= 0 && (IMM) < 32); .2byte ((OP) | (REG2I(R1) << 2) | (REG2I(R2) << 7) | \
+	(((IMM) & 0b11) << 5) | (((IMM) & 0b11100) << (10 - 2))
+
+// Compressed load byte, zero-extend result
+#define XW_C_LBU(RD, RS, IMM) XW_ENCODE1(XW_OP_LBU, RD, RS, IMM)
+
+// Compressed store byte
+#define XW_C_SB(RS1, RS2, IMM) XW_ENCODE1(XW_OP_SB, RS1, RS2, IMM)
+
+// Compressed load half, zero-extend result
+#define XW_C_LHU(RD, RS, IMM) ASM_ASSERT(((IMM) & 1) == 0); XW_ENCODE2(XW_OP_LHU, RD, RS, ((IMM) >> 1)))
+
+// Compressed store half
+#define XW_C_SH(RS1, RS2, IMM)  ASM_ASSERT(((IMM) & 1) == 0); XW_ENCODE2(XW_OP_SH, RS1, RS2, ((IMM) >> 1)))
+
+
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/ch32v003fun/xw_ext.inc b/ch32v003fun/xw_ext.inc
deleted file mode 100644
index ca210702fc69d9d31b87d9f69ddaf004c17e0b0c..0000000000000000000000000000000000000000
--- a/ch32v003fun/xw_ext.inc
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-Encoder for some of the proprietary 'XW' RISC-V instructions present on the QingKe RV32 processor.
-Examples:
-	XW_C_LBU(a3, a1, 27); // c.xw.lbu a3, 27(a1)
-	XW_C_SB(a0, s0, 13);  // c.xw.sb a0, 13(s0)
-
-	XW_C_LHU(a5, a5, 38); // c.xw.lhu a5, 38(a5)
-	XW_C_SH(a2, s1, 14);  // c.xw.sh a2, 14(s1)
-*/
-
-// Let us do some compile-time error checking.
-#define ASM_ASSERT(COND) .if (!(COND)); .err; .endif
-
-// Integer encodings of the possible compressed registers.
-#define C_s0 0
-#define C_s1 1
-#define C_a0 2
-#define C_a1 3
-#define C_a2 4
-#define C_a3 5
-#define C_a4 6
-#define C_a5 7
-
-// register to encoding
-#define REG2I(X) (C_ ## X)
-
-// XW opcodes
-#define XW_OP_LBUSP 0b1000000000000000
-#define XW_OP_STSP  0b1000000001000000
-
-#define XW_OP_LHUSP 0b1000000000100000
-#define XW_OP_SHSP  0b1000000001100000
-
-#define XW_OP_LBU   0b0010000000000000
-#define XW_OP_SB    0b1010000000000000
-
-#define XW_OP_LHU   0b0010000000000010
-#define XW_OP_SH    0b1010000000000010
-
-// The two different XW encodings supported at the moment.
-#define XW_ENCODE1(OP, R1, R2, IMM) ASM_ASSERT((IMM) >= 0 && (IMM) < 32); .2byte ((OP) | (REG2I(R1) << 2) | (REG2I(R2) << 7) | \
-	(((IMM) & 0b1) << 12) | (((IMM) & 0b110) << (5 - 1)) | (((IMM) & 0b11000) << (10 - 3)))
-
-#define XW_ENCODE2(OP, R1, R2, IMM) ASM_ASSERT((IMM) >= 0 && (IMM) < 32); .2byte ((OP) | (REG2I(R1) << 2) | (REG2I(R2) << 7) | \
-	(((IMM) & 0b11) << 5) | (((IMM) & 0b11100) << (10 - 2))
-
-// Compressed load byte, zero-extend result
-#define XW_C_LBU(RD, RS, IMM) XW_ENCODE1(XW_OP_LBU, RD, RS, IMM)
-
-// Compressed store byte
-#define XW_C_SB(RS1, RS2, IMM) XW_ENCODE1(XW_OP_SB, RS1, RS2, IMM)
-
-// Compressed load half, zero-extend result
-#define XW_C_LHU(RD, RS, IMM) ASM_ASSERT(((IMM) & 1) == 0); XW_ENCODE2(XW_OP_LHU, RD, RS, ((IMM) >> 1)))
-
-// Compressed store half
-#define XW_C_SH(RS1, RS2, IMM)  ASM_ASSERT(((IMM) & 1) == 0); XW_ENCODE2(XW_OP_SH, RS1, RS2, ((IMM) >> 1)))
diff --git a/minichlink/Makefile b/minichlink/Makefile
index 15d3c1a5a3f680519403ae26d9bdb8883b348351..964341c11b51b66accf732be82639d887df7c0cd 100644
--- a/minichlink/Makefile
+++ b/minichlink/Makefile
@@ -1,7 +1,7 @@
 TOOLS:=minichlink minichlink.so
 
 CFLAGS:=-O0 -g3 -Wall
-C_S:=minichlink.c pgm-wch-linke.c pgm-esp32s2-ch32xx.c nhc-link042.c ardulink.c serial_dev.c minichgdb.c
+C_S:=minichlink.c pgm-wch-linke.c pgm-esp32s2-ch32xx.c nhc-link042.c ardulink.c serial_dev.c pgm-b003fun.c minichgdb.c
 
 # General Note: To use with GDB, gdb-multiarch
 # gdb-multilib {file}
diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c
index 191237b6a124779b5501355605dce8edbc06d705..592839d50ed12a6efeae5f34c3a278c5046bc807 100644
--- a/minichlink/minichlink.c
+++ b/minichlink/minichlink.c
@@ -41,6 +41,10 @@ void * MiniCHLinkInitAsDLL( struct MiniChlinkFunctions ** MCFO )
 	{
 		fprintf( stderr, "Found NHC-Link042 Programmer\n" );
 	}
+	else if ((dev = TryInit_B003Fun()))
+	{
+		fprintf( stderr, "Found B003Fun Bootloader\n" );
+	}
 	else if ((dev = TryInit_Ardulink()))
 	{
 		fprintf( stderr, "Found Ardulink Programmer\n" );
diff --git a/minichlink/minichlink.h b/minichlink/minichlink.h
index 6a974ff230635fa24a2a769512d61c356dc3013c..37960eacafe4751fdb2424adb80129134697a714 100644
--- a/minichlink/minichlink.h
+++ b/minichlink/minichlink.h
@@ -150,9 +150,10 @@ extern struct MiniChlinkFunctions MCF;
 
 
 // Returns 'dev' on success, else 0.
-void * TryInit_WCHLinkE();
-void * TryInit_ESP32S2CHFUN();
+void * TryInit_WCHLinkE(void);
+void * TryInit_ESP32S2CHFUN(void);
 void * TryInit_NHCLink042(void);
+void * TryInit_B003Fun(void);
 void * TryInit_Ardulink();
 
 // Returns 0 if ok, populated, 1 if not populated.
diff --git a/minichlink/pgm-b003fun.c b/minichlink/pgm-b003fun.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e37aea3fc5b653577934ccb4fa626c0865592a9
--- /dev/null
+++ b/minichlink/pgm-b003fun.c
@@ -0,0 +1,150 @@
+#if 0
+#include <stdint.h>
+#include "hidapi.h"
+#include "minichlink.h"
+
+
+struct B003FunProgrammerStruct
+{
+	void * internal;
+	hid_device * hd;
+	uint8_t commandbuffer[128];
+	int commandplace;
+
+/*
+	uint32_t state;
+	uint8_t reply[256];
+	int replylen;
+*/
+};
+
+
+static int B003FunFlushLLCommands( void * dev )
+{
+	return 0;
+}
+
+static int B003FunDelayUS( void * dev, int microseconds )
+{
+	usleep( microseconds );
+	return 0;
+}
+
+static int B003FunSetupInterface( void * dev )
+{
+	return 0;
+}
+
+static int B003FunExit( void * dev )
+{
+	return 0;
+}
+
+// MUST be 4-byte-aligned.
+static int B003FunWriteWord( void * dev, uint32_t address_to_write, uint32_t data )
+{
+	return 0;
+}
+
+static int B003FunReadWord( void * dev, uint32_t address_to_read, uint32_t * data )
+{
+	return 0;
+}
+
+static int B003FunWaitForDoneOp( void * dev, int ignore )
+{
+	// ... Need this.
+	return 0;
+}
+
+
+static int B003FunBlockWrite64( void * dev, uint32_t address_to_write, uint8_t * data )
+{
+	return 0;
+}
+
+static int B003FunWriteHalfWord( void * dev, uint32_t address_to_write, uint16_t data )
+{
+}
+
+static int B003FunReadHalfWord( void * dev, uint32_t address_to_read, uint16_t * data )
+{
+}
+
+static int B003FunWriteByte( void * dev, uint32_t address_to_write, uint8_t data )
+{
+}
+
+static int B003FunReadByte( void * dev, uint32_t address_to_read, uint8_t * data )
+{
+}
+
+
+static void ResetOp( struct B003FunProgrammerStruct * eps )
+{
+	memcpy( eps->commandbuffer, "\xaa\x00\x00\x00", 4 );
+	eps->commandplace = 4;
+}
+
+static void WriteOp4( struct B003FunProgrammerStruct * eps, uint32_t opsend )
+{
+	int place = eps->commandplace;
+	int newend = place + 4;
+	if( newend < sizeof( eps->commandbuffer ) )
+	{
+		memcpy( eps->commandbuffer + place, &opsend, 4 );
+	}
+	eps->commandplace = newend;
+}
+
+static void CommitOp( struct B003FunProgrammerStruct * eps )
+{
+}
+	CommitOp( eps );
+
+
+
+void * TryInit_B003Fun()
+{
+	#define VID 0x1209
+	#define PID 0xb003
+	hid_init();
+	hid_device * hd = hid_open( VID, PID, 0); // third parameter is "serial"
+	if( !hd ) return 0;
+
+	struct B003FunProgrammerStruct * eps = malloc( sizeof( struct B003FunProgrammerStruct ) );
+	memset( eps, 0, sizeof( *eps ) );
+	eps->hd = hd;
+	eps->commandplace = 1;
+
+	memset( &MCF, 0, sizeof( MCF ) );
+	MCF.WriteReg32 = 0;
+	MCF.ReadReg32 = 0;
+	MCF.FlushLLCommands = B003FunFlushLLCommands;
+	MCF.DelayUS = B003FunDelayUS;
+	MCF.Control3v3 = 0;
+	MCF.SetupInterface = B003FunSetupInterface;
+	MCF.Exit = B003FunExit;
+	MCF.HaltMode = 0;
+	MCF.VoidHighLevelState = 0;
+	MCF.PollTerminal = 0;
+
+	// These are optional. Disabling these is a good mechanismto make sure the core functions still work.
+	MCF.WriteWord = B003FunWriteWord;
+	MCF.ReadWord = B003FunReadWord;
+
+	MCF.WaitForDoneOp = B003FunWaitForDoneOp;
+
+	MCF.BlockWrite64 = B003FunBlockWrite64;
+
+	// Alert programmer.
+	ResetOp( eps )
+	WriteOp4( eps, 0x00b02023 );  //sw a1, 0       ; stop execution
+	WriteOp4( eps, 0x00008067 );  //jalr x0, x1, 0 ; ret
+	CommitOp( eps );
+
+	return eps;
+}
+#else
+void * TryInit_B003Fun() { return 0; }
+#endif