diff --git a/ch32v003fun/ch32v003fun-bootloader.ld b/ch32v003fun/ch32v003fun-bootloader.ld
new file mode 100644
index 0000000000000000000000000000000000000000..39d6580e88a0b572317c6a1821d730df795735a3
--- /dev/null
+++ b/ch32v003fun/ch32v003fun-bootloader.ld
@@ -0,0 +1,148 @@
+ENTRY( InterruptVector )
+
+MEMORY
+{
+	/* Actually at 0x1FFFF000 but the system maps it to 0x00000000 */
+	FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 1920
+	RAM (xrw)  : ORIGIN = 0x20000000, LENGTH = 2K
+}
+
+SECTIONS
+{
+    .init :
+    { 
+      _sinit = .;
+      . = ALIGN(4);
+      KEEP(*(SORT_NONE(.init)))
+      . = ALIGN(4);
+      _einit = .;
+    } >FLASH AT>FLASH
+
+    .text :
+    {
+      . = ALIGN(4);
+      *(.text)
+      *(.text.*)
+      *(.rodata)
+      *(.rodata*)
+      *(.gnu.linkonce.t.*)
+      . = ALIGN(4);
+    } >FLASH AT>FLASH 
+
+    .fini :
+    {
+      KEEP(*(SORT_NONE(.fini)))
+      . = ALIGN(4);
+    } >FLASH AT>FLASH
+
+    PROVIDE( _etext = . );
+    PROVIDE( _eitcm = . );  
+
+    .preinit_array :
+    {
+      PROVIDE_HIDDEN (__preinit_array_start = .);
+      KEEP (*(.preinit_array))
+      PROVIDE_HIDDEN (__preinit_array_end = .);
+    } >FLASH AT>FLASH 
+  
+    .init_array :
+    {
+      PROVIDE_HIDDEN (__init_array_start = .);
+      KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+      KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+      PROVIDE_HIDDEN (__init_array_end = .);
+    } >FLASH AT>FLASH 
+  
+    .fini_array :
+    {
+      PROVIDE_HIDDEN (__fini_array_start = .);
+      KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+      KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+      PROVIDE_HIDDEN (__fini_array_end = .);
+    } >FLASH AT>FLASH 
+  
+    .ctors :
+    {
+      /* gcc uses crtbegin.o to find the start of
+         the constructors, so we make sure it is
+         first.  Because this is a wildcard, it
+         doesn't matter if the user does not
+         actually link against crtbegin.o; the
+         linker won't look for a file to match a
+         wildcard.  The wildcard also means that it
+         doesn't matter which directory crtbegin.o
+         is in.  */
+      KEEP (*crtbegin.o(.ctors))
+      KEEP (*crtbegin?.o(.ctors))
+      /* We don't want to include the .ctor section from
+         the crtend.o file until after the sorted ctors.
+         The .ctor section from the crtend file contains the
+         end of ctors marker and it must be last */
+      KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+      KEEP (*(SORT(.ctors.*)))
+      KEEP (*(.ctors))
+    } >FLASH AT>FLASH 
+  
+    .dtors :
+    {
+      KEEP (*crtbegin.o(.dtors))
+      KEEP (*crtbegin?.o(.dtors))
+      KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+      KEEP (*(SORT(.dtors.*)))
+      KEEP (*(.dtors))
+    } >FLASH AT>FLASH 
+
+    .dalign :
+    {
+      . = ALIGN(4);
+      PROVIDE(_data_vma = .);
+    } >RAM AT>FLASH  
+
+    .dlalign :
+    {
+      . = ALIGN(4); 
+      PROVIDE(_data_lma = .);
+    } >FLASH AT>FLASH
+
+    .data :
+    {
+      . = ALIGN(4);
+      *(.gnu.linkonce.r.*)
+      *(.data .data.*)
+      *(.gnu.linkonce.d.*)
+      . = ALIGN(8);
+      PROVIDE( __global_pointer$ = . + 0x800 );
+      *(.sdata .sdata.*)
+      *(.sdata2*)
+      *(.gnu.linkonce.s.*)
+      . = ALIGN(8);
+      *(.srodata.cst16)
+      *(.srodata.cst8)
+      *(.srodata.cst4)
+      *(.srodata.cst2)
+      *(.srodata .srodata.*)
+      . = ALIGN(4);
+      PROVIDE( _edata = .);
+    } >RAM AT>FLASH
+
+    .bss :
+    {
+      . = ALIGN(4);
+      PROVIDE( _sbss = .);
+      *(.sbss*)
+      *(.gnu.linkonce.sb.*)
+      *(.bss*)
+      *(.gnu.linkonce.b.*)    
+      *(COMMON*)
+      . = ALIGN(4);
+      PROVIDE( _ebss = .);
+    } >RAM AT>FLASH
+
+    PROVIDE( _end = _ebss);
+	PROVIDE( end = . );
+
+	PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM));	
+}
+
+
+
diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c
index c800f905c9ef3ffd812bc2f8666a04149aa074c1..4363ed3a115f529e739dd67457b4c0e9d18fa331 100644
--- a/ch32v003fun/ch32v003fun.c
+++ b/ch32v003fun/ch32v003fun.c
@@ -664,10 +664,6 @@ mini_pprintf(int (*puts)(char*s, int len, void* buf), void* buf, const char *fmt
 int main() __attribute__((used));
 void SystemInit( void ) __attribute__((used));
 
-void InterruptVector()         __attribute__((naked)) __attribute((section(".init"))) __attribute__((used)) __attribute((weak));
-void handle_reset()            __attribute__((naked)) __attribute((section(".text.handle_reset"))) __attribute__((used));
-void DefaultIRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute__((naked)) __attribute__((used));
-
 extern uint32_t * _sbss;
 extern uint32_t * _ebss;
 extern uint32_t * _data_lma;
@@ -712,7 +708,11 @@ void TIM1_TRG_COM_IRQHandler( void )     __attribute__((section(".text.vector_ha
 void TIM1_CC_IRQHandler( void )          __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
 void TIM2_IRQHandler( void )             __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
 
-void InterruptVector()
+void InterruptVector()         __attribute__((naked)) __attribute((section(".init"))) __attribute((weak,alias("InterruptVectorDefault")));
+void InterruptVectorDefault()  __attribute__((naked)) __attribute((section(".init")));
+
+
+void InterruptVectorDefault()
 {
 	asm volatile( "\n\
 	.align  2\n\
@@ -759,7 +759,6 @@ void InterruptVector()
 	.word   TIM2_IRQHandler           /* TIM2 */                           \n");
 }
 
-
 void handle_reset()
 {
 	asm volatile( "\n\
diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h
index 9f25a84910c83e962c15c1fb5b860606776e0e24..56393414e1432ea9d3b1da24a4fd7cf98b19e023 100644
--- a/ch32v003fun/ch32v003fun.h
+++ b/ch32v003fun/ch32v003fun.h
@@ -4819,6 +4819,9 @@ extern "C" {
 #define DELAY_US_TIME (SYSTEM_CORE_CLOCK / 8000000)
 #define DELAY_MS_TIME (SYSTEM_CORE_CLOCK / 8000)
 
+void handle_reset()            __attribute__((naked)) __attribute((section(".text.handle_reset"))) __attribute__((used));
+void DefaultIRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute__((naked)) __attribute__((used));
+
 void DelaySysTick( uint32_t n );
 
 #define Delay_Us(n) DelaySysTick( n * DELAY_US_TIME )
diff --git a/examples/blink/blink.bin b/examples/blink/blink.bin
index b1c36a5c828db912241513fd46ac9f0fae76f4d5..f5244807298493f49c777f4e01a93a19666c80c3 100755
Binary files a/examples/blink/blink.bin and b/examples/blink/blink.bin differ
diff --git a/examples/bootload/Makefile b/examples/bootload/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..d39497d767c87bed9dd45d735f0758813c72142d
--- /dev/null
+++ b/examples/bootload/Makefile
@@ -0,0 +1,51 @@
+TARGET:=bootload
+
+all : flash
+
+PREFIX:=riscv64-unknown-elf
+
+GPIO_Toggle:=EXAM/GPIO/GPIO_Toggle/User
+
+EVT:=../../ch32v003evt
+
+MINICHLINK:=../../minichlink
+
+ifeq ($(OS),Windows_NT)
+# On Windows, all the major RISC-V GCC installs are missing the -ec libgcc.
+LIB_GCC=../../misc/libgcc.a
+else
+LIB_GCC=-lgcc
+endif
+
+CH32V003FUN:=../../ch32v003fun
+
+CFLAGS:= \
+	-g -Os -flto -ffunction-sections \
+	-static-libgcc $(LIB_GCC) \
+	-march=rv32ec \
+	-mabi=ilp32e \
+	-I/usr/include/newlib \
+	-I$(CH32V003FUN) \
+	-nostdlib \
+	-I. -DCUSTOM_INTERRUPT_VECTOR
+
+LDFLAGS:=-T $(CH32V003FUN)/ch32v003fun-bootloader.ld -Wl,--gc-sections
+
+SYSTEM_C:=$(CH32V003FUN)/ch32v003fun.c
+
+$(TARGET).elf : $(SYSTEM_C) $(TARGET).c
+	$(PREFIX)-gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
+
+$(TARGET).bin : $(TARGET).elf
+	$(PREFIX)-size $^
+	$(PREFIX)-objdump -S $^ > $(TARGET).lst
+	$(PREFIX)-objdump -t $^ > $(TARGET).map
+	$(PREFIX)-objcopy -O binary $< $(TARGET).bin
+	$(PREFIX)-objcopy -O ihex $< $(TARGET).hex
+
+flash : $(TARGET).bin
+	$(MINICHLINK)/minichlink -h -U -w $< bootloader -B
+
+clean :
+	rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).lst $(TARGET).map $(TARGET).hex
+
diff --git a/examples/bootload/bootload.c b/examples/bootload/bootload.c
new file mode 100644
index 0000000000000000000000000000000000000000..aa107360e5ce3b6789c65d1b125256b86ef26b34
--- /dev/null
+++ b/examples/bootload/bootload.c
@@ -0,0 +1,74 @@
+// Could be defined here, or in the processor defines.
+#define SYSTEM_CORE_CLOCK 48000000
+
+#include "ch32v003fun.h"
+#include <stdio.h>
+
+#define APB_CLOCK SYSTEM_CORE_CLOCK
+
+uint32_t count;
+
+// You can override the interrupt vector this way:
+void InterruptVector()         __attribute__((naked)) __attribute((section(".init")));
+void InterruptVector()
+{
+	asm volatile( "\n\
+	.align  2\n\
+	.option   norvc;\n\
+	j handle_reset");
+}
+
+uint32_t count;
+
+int main()
+{
+	SystemInit48HSI();
+
+	// From here, you can do whatever you'd like!
+	// This code will live up at 0x1ffff000.
+
+	// Enable GPIOD.
+	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC;
+
+	// GPIO D0 Push-Pull, 10MHz Output
+	GPIOD->CFGLR &= ~(0xf<<(4*0));
+	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0);
+
+	// GPIO D0 Push-Pull, 10MHz Output
+	GPIOD->CFGLR &= ~(0xf<<(4*4));
+	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
+
+	// GPIO D0 Push-Pull, 10MHz Output
+	GPIOC->CFGLR &= ~(0xf<<(4*0));
+	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
+
+	static const uint32_t marker[] = { 0xaaaaaaaa };
+	count = marker[0];
+
+	int i;
+	for( i = 0; i < 5; i++ )
+	{
+		GPIOD->BSHR = 1 | (1<<4);            // Turn on GPIOD0 + D4
+		GPIOC->BSHR = 1;                     // Turn on GPIOC0
+		Delay_Ms( 250 );
+		GPIOD->BSHR = (1<<16) | (1<<(16+4)); // Turn off GPIOD0 + D4
+		GPIOC->BSHR = 1;                     // Turn off GPIOC0
+		Delay_Ms( 20 );
+		count++;
+	}
+
+	// Exit bootloader after 5 blinks.
+
+	// Note we have to do this if we ended up in the bootloader because
+	// the main system booted us here.  If you don't care, you don't need
+	// to turn OBTKEYR back off.
+	FLASH->KEYR = FLASH_KEY1;
+	FLASH->KEYR = FLASH_KEY2;
+	FLASH->BOOT_MODEKEYR = FLASH_KEY1;
+	FLASH->BOOT_MODEKEYR = FLASH_KEY2;
+	FLASH->STATR = 0; // 1<<14 is zero, so, boot user code.
+	FLASH->CTLR = CR_LOCK_Set;
+
+	PFIC->SCTLR = 1<<31;
+	while(1);
+}
diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c
index 3160afaf05fbcdb4dc03e2f1dcb7f8eb717bef33..e31c7b9a836c419651fac200631e9e9626e5d230 100644
--- a/minichlink/minichlink.c
+++ b/minichlink/minichlink.c
@@ -14,6 +14,7 @@
 static int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber );
 static int64_t StringToMemoryAddress( const char * number );
 static void StaticUpdatePROGBUFRegs( void * dev );
+static int InternalUnlockBootloader( void * dev );
 
 void TestFunction(void * v );
 struct MiniChlinkFunctions MCF;
@@ -108,10 +109,19 @@ keep_going:
 				else
 					goto unimplemented;
 				break;
+			case 'U':
+				// Unlock Bootloader
+				if( InternalUnlockBootloader( dev ) )
+					goto unimplemented;
+				break;
 			case 'b':  //reBoot
 				if( !MCF.HaltMode || MCF.HaltMode( dev, 1 ) )
 					goto unimplemented;
 				break;
+			case 'B':  //reBoot into Bootloader
+				if( !MCF.HaltMode || MCF.HaltMode( dev, 3 ) )
+					goto unimplemented;
+				break;
 			case 'e':  //rEsume
 				if( !MCF.HaltMode || MCF.HaltMode( dev, 2 ) )
 					goto unimplemented;
@@ -345,7 +355,7 @@ keep_going:
 					goto unimplemented;
 				}
 
-				printf( "Image written successfully\n" );
+				printf( "Image written.\n" );
 
 				free( image );
 				break;
@@ -528,6 +538,30 @@ static void StaticUpdatePROGBUFRegs( void * dev )
 	MCF.WriteReg32( dev, DMCOMMAND, 0x0023100d ); // Copy data to x13
 }
 
+static int InternalUnlockBootloader( void * dev )
+{
+	if( !MCF.WriteWord ) return -99;
+	int ret = 0;
+	uint32_t OBTKEYR;
+	ret |= MCF.WriteWord( dev, 0x40022028, 0x45670123 ); //(FLASH_BOOT_MODEKEYP)
+	ret |= MCF.WriteWord( dev, 0x40022028, 0xCDEF89AB ); //(FLASH_BOOT_MODEKEYP)
+	ret |= MCF.ReadWord( dev, 0x40022008, &OBTKEYR ); //(FLASH_OBTKEYR)
+	if( ret )
+	{
+		fprintf( stderr, "Error operating with OBTKEYR\n" );
+		return -1;
+	}
+	if( OBTKEYR & (1<<15) )
+	{
+		fprintf( stderr, "Error: Could not unlock boot section (%08x)\n", OBTKEYR );
+	}
+	OBTKEYR |= (1<<14); // Configure for boot-to-bootload.
+	ret |= MCF.WriteWord( dev, 0x40022008, OBTKEYR );
+	ret |= MCF.ReadWord( dev, 0x40022008, &OBTKEYR ); //(FLASH_OBTKEYR)
+	printf( "FLASH_OBTKEYR = %08x (%d)\n", OBTKEYR, ret );
+	return ret;
+}
+
 static int DefaultWriteWord( void * dev, uint32_t address_to_write, uint32_t data )
 {
 	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
@@ -633,7 +667,7 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob
 
 	if( blob_size == 0 ) return 0;
 
-	if( (address_to_write & 0xff000000) == 0x08000000 || (address_to_write & 0xff000000) == 0x00000000 ) 
+	if( (address_to_write & 0xff000000) == 0x08000000 || (address_to_write & 0xff000000) == 0x00000000 || (address_to_write & 0x1FFFF800) == 0x1FFFF000 ) 
 		is_flash = 1;
 
 	if( is_flash && MCF.BlockWrite64 && ( address_to_write & 0x3f ) == 0 )
@@ -880,6 +914,21 @@ static int DefaultHaltMode( void * dev, int mode )
 		MCF.WriteReg32( dev, DMCONTROL, 0x40000001 ); // resumereq
 		MCF.FlushLLCommands( dev );
 		break;
+	case 3:
+		MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
+		MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate a halt request.
+
+		MCF.WriteWord( dev, (intptr_t)&FLASH->KEYR, FLASH_KEY1 );
+		MCF.WriteWord( dev, (intptr_t)&FLASH->KEYR, FLASH_KEY2 );
+		MCF.WriteWord( dev, (intptr_t)&FLASH->BOOT_MODEKEYR, FLASH_KEY1 );
+		MCF.WriteWord( dev, (intptr_t)&FLASH->BOOT_MODEKEYR, FLASH_KEY2 );
+		MCF.WriteWord( dev, (intptr_t)&FLASH->STATR, 1<<14 );
+		MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_LOCK_Set );
+
+		MCF.WriteReg32( dev, DMCONTROL, 0x80000003 ); // Reboot.
+		MCF.WriteReg32( dev, DMCONTROL, 0x40000001 ); // resumereq
+		MCF.FlushLLCommands( dev );
+		break;
 	}
 	iss->processor_in_mode = mode;
 	return 0;