diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h
index ba320ecf0082e95111d7f9ee4ab39ab5bf0f5223..c6ccd39a2d3b393645577cf9c368c4ab9205b399 100644
--- a/ch32v003fun/ch32v003fun.h
+++ b/ch32v003fun/ch32v003fun.h
@@ -1302,20 +1302,20 @@ typedef struct
 #define FLASH_STATR_EOP                         ((uint8_t)0x20) /* End of operation */
 
 /*******************  Bit definition for FLASH_CTLR register  *******************/
-#define FLASH_CTLR_PG                           ((uint16_t)0x0001)     /* Programming */
-#define FLASH_CTLR_PER                          ((uint16_t)0x0002)     /* Page Erase 1KByte*/
-#define FLASH_CTLR_MER                          ((uint16_t)0x0004)     /* Mass Erase */
-#define FLASH_CTLR_OPTPG                        ((uint16_t)0x0010)     /* Option Byte Programming */
-#define FLASH_CTLR_OPTER                        ((uint16_t)0x0020)     /* Option Byte Erase */
-#define FLASH_CTLR_STRT                         ((uint16_t)0x0040)     /* Start */
-#define FLASH_CTLR_LOCK                         ((uint16_t)0x0080)     /* Lock */
-#define FLASH_CTLR_OPTWRE                       ((uint16_t)0x0200)     /* Option Bytes Write Enable */
-#define FLASH_CTLR_ERRIE                        ((uint16_t)0x0400)     /* Error Interrupt Enable */
-#define FLASH_CTLR_EOPIE                        ((uint16_t)0x1000)     /* End of operation interrupt enable */
-#define FLASH_CTLR_PAGE_PG                      ((uint16_t)0x00010000) /* Page Programming 64Byte */
-#define FLASH_CTLR_PAGE_ER                      ((uint16_t)0x00020000) /* Page Erase 64Byte */
-#define FLASH_CTLR_BUF_LOAD                     ((uint16_t)0x00040000) /* Buffer Load */
-#define FLASH_CTLR_BUF_RST                      ((uint16_t)0x00080000) /* Buffer Reset */
+#define FLASH_CTLR_PG                           (0x0001)     /* Programming */
+#define FLASH_CTLR_PER                          (0x0002)     /* Page Erase 1KByte*/
+#define FLASH_CTLR_MER                          (0x0004)     /* Mass Erase */
+#define FLASH_CTLR_OPTPG                        (0x0010)     /* Option Byte Programming */
+#define FLASH_CTLR_OPTER                        (0x0020)     /* Option Byte Erase */
+#define FLASH_CTLR_STRT                         (0x0040)     /* Start */
+#define FLASH_CTLR_LOCK                         (0x0080)     /* Lock */
+#define FLASH_CTLR_OPTWRE                       (0x0200)     /* Option Bytes Write Enable */
+#define FLASH_CTLR_ERRIE                        (0x0400)     /* Error Interrupt Enable */
+#define FLASH_CTLR_EOPIE                        (0x1000)     /* End of operation interrupt enable */
+#define FLASH_CTLR_PAGE_PG                      (0x00010000) /* Page Programming 64Byte */
+#define FLASH_CTLR_PAGE_ER                      (0x00020000) /* Page Erase 64Byte */
+#define FLASH_CTLR_BUF_LOAD                     (0x00040000) /* Buffer Load */
+#define FLASH_CTLR_BUF_RST                      (0x00080000) /* Buffer Reset */
 
 /*******************  Bit definition for FLASH_ADDR register  *******************/
 #define FLASH_ADDR_FAR                          ((uint32_t)0xFFFFFFFF) /* Flash Address */
diff --git a/examples/flashtest/Makefile b/examples/flashtest/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..89a4c4c409495584772ed6a271dc715d6c6b2143
--- /dev/null
+++ b/examples/flashtest/Makefile
@@ -0,0 +1,9 @@
+all : flash
+
+TARGET:=flashtest
+
+include ../../ch32v003fun/ch32v003fun.mk
+
+flash : cv_flash
+clean : cv_clean
+
diff --git a/examples/flashtest/flashtest.c b/examples/flashtest/flashtest.c
new file mode 100644
index 0000000000000000000000000000000000000000..435dcc1861cfedab59d74c900fed40efcf8e0811
--- /dev/null
+++ b/examples/flashtest/flashtest.c
@@ -0,0 +1,103 @@
+// DOES NOT WORK HALP!!!!!!!!!!!!!!
+
+#define SYSTEM_CORE_CLOCK 48000000
+#define SYSTICK_USE_HCLK
+
+#include "ch32v003fun.h"
+#include <stdio.h>
+
+
+int main()
+{
+	int start;
+	int stop;
+
+	SETUP_SYSTICK_HCLK
+
+	SystemInit48HSI();
+	SetupDebugPrintf();
+
+	Delay_Ms(100);
+
+	printf( "Starting\n" );
+
+	// Unkock flash - be aware you need extra stuff for the bootloader.
+	FLASH->KEYR = 0x45670123;
+	FLASH->KEYR = 0xCDEF89AB;
+
+	// For option bytes.
+//	FLASH->OBKEYR = 0x45670123;
+//	FLASH->OBKEYR = 0xCDEF89AB;
+
+	FLASH->MODEKEYR = 0x45670123;
+	FLASH->MODEKEYR = 0xCDEF89AB;
+
+	printf( "FLASH->CTLR = %08lx\n", FLASH->CTLR );
+	if( FLASH->CTLR & 0x8080 ) 
+	{
+		printf( "Flash still locked\n" );
+		while(1);
+	}
+
+	uint32_t * ptr = (uint32_t*)0x08003700;
+	printf( "Memory at: %p: %08lx %08lx\n", ptr, ptr[0], ptr[1] );
+
+
+	printf( "FLASH->CTLR = %08lx\n", FLASH->CTLR );
+
+	//Erase Page
+	FLASH->CTLR = CR_PAGE_ER;
+	FLASH->ADDR = (intptr_t)ptr;
+	FLASH->CTLR = CR_STRT_Set | CR_PAGE_ER;
+	start = SysTick->CNT;
+	while( FLASH->STATR & FLASH_STATR_BSY );  // Takes about 3ms.
+	stop = SysTick->CNT;
+
+	printf( "FLASH->STATR = %08lx -> %d cycles for page erase\n", FLASH->STATR, stop - start );
+	printf( "Erase complete\n" );
+
+
+	printf( "Memory at %p: %08lx %08lx\n", ptr, ptr[0], ptr[1] );
+
+	// Clear buffer and prep for flashing.
+	FLASH->CTLR = CR_PAGE_PG;  // synonym of FTPG.
+	FLASH->CTLR = CR_BUF_RST | CR_PAGE_PG;
+	FLASH->ADDR = (intptr_t)ptr;  // This can actually happen about anywhere toward the end here.
+
+
+	// Note: It takes about 6 clock cycles for this to finish.
+	start = SysTick->CNT;
+	while( FLASH->STATR & FLASH_STATR_BSY );  // No real need for this.
+	stop = SysTick->CNT;
+	printf( "FLASH->STATR = %08lx -> %d cycles for buffer reset\n", FLASH->STATR, stop - start );
+
+
+	int i;
+	start = SysTick->CNT;
+	for( i = 0; i < 16; i++ )
+	{
+		ptr[i] = 0xabcd1234 + i; //Write to the memory
+		FLASH->CTLR = CR_PAGE_PG | FLASH_CTLR_BUF_LOAD; // Load the buffer.
+		while( FLASH->STATR & FLASH_STATR_BSY );  // Only needed if running from RAM.
+	}
+	stop = SysTick->CNT;
+	printf( "Write: %d cycles for writing data in\n", stop - start );
+
+	// Actually write the flash out. (Takes about 3ms)
+	FLASH->CTLR = CR_PAGE_PG|CR_STRT_Set;
+
+	start = SysTick->CNT;
+	while( FLASH->STATR & FLASH_STATR_BSY );
+	stop = SysTick->CNT;
+	printf( "FLASH->STATR = %08lx -> %d cycles for page write\n", FLASH->STATR, stop - start );
+
+	printf( "FLASH->STATR = %08lx\n", FLASH->STATR );
+
+	printf( "Memory at: %08lx: %08lx %08lx\n", (uint32_t)ptr, ptr[0], ptr[1] );
+
+	for( i = 0; i < 16; i++ )
+		printf( "%08lx ", ptr[i] );
+	printf( "\n" );
+	while(1);
+}
+
diff --git a/minichlink/ardulink.c b/minichlink/ardulink.c
index cd28635be0d3b4999302c21056d41fb392400c6a..962061945387bb6537d10be21d99274a5f2fe14f 100644
--- a/minichlink/ardulink.c
+++ b/minichlink/ardulink.c
@@ -14,156 +14,159 @@ static int ArdulinkControl3v3(void * dev, int power_on);
 static int ArdulinkExit(void * dev);
 
 typedef struct {
-    struct ProgrammerStructBase psb;
-    serial_dev_t serial;
+	struct ProgrammerStructBase psb;
+	serial_dev_t serial;
 } ardulink_ctx_t;
 
 int ArdulinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command)
 {
-    uint8_t buf[6];
-    buf[0] = 'w';
-    buf[1] = reg_7_bit;
+	uint8_t buf[6];
+	buf[0] = 'w';
+	buf[1] = reg_7_bit;
 
-    //fprintf(stderr, "WriteReg32: 0x%02x = 0x%08x\n", reg_7_bit, command);
+	//fprintf(stderr, "WriteReg32: 0x%02x = 0x%08x\n", reg_7_bit, command);
 
-    buf[2] = command & 0xff;
-    buf[3] = (command >> 8) & 0xff;
-    buf[4] = (command >> 16) & 0xff;
-    buf[5] = (command >> 24) & 0xff;
+	buf[2] = command & 0xff;
+	buf[3] = (command >> 8) & 0xff;
+	buf[4] = (command >> 16) & 0xff;
+	buf[5] = (command >> 24) & 0xff;
 
-    if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, buf, 6) == -1)
-        return -errno;
+	if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, buf, 6) == -1)
+		return -errno;
 
-    if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, buf, 1) == -1)
-        return -errno;
+	if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, buf, 1) == -1)
+		return -errno;
 
-    return buf[0] == '+' ? 0 : -EPROTO;
+	return buf[0] == '+' ? 0 : -71; // EPROTO
 }
 
 int ArdulinkReadReg32(void * dev, uint8_t reg_7_bit, uint32_t * commandresp)
 {
-    uint8_t buf[4];
-    buf[0] = 'r';
-    buf[1] = reg_7_bit;
+	uint8_t buf[4];
+	buf[0] = 'r';
+	buf[1] = reg_7_bit;
 
-    if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, buf, 2) == -1)
-        return -errno;
+	if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, buf, 2) == -1)
+		return -errno;
 
-    if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, buf, 4) == -1)
-        return -errno;
+	if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, buf, 4) == -1)
+		return -errno;
 
-    *commandresp = (uint32_t)buf[0] | (uint32_t)buf[1] << 8 | \
-        (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
+	*commandresp = (uint32_t)buf[0] | (uint32_t)buf[1] << 8 | \
+		(uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
 
-    //fprintf(stderr, "ReadReg32: 0x%02x = 0x%08x\n", reg_7_bit, *commandresp);
+	//fprintf(stderr, "ReadReg32: 0x%02x = 0x%08x\n", reg_7_bit, *commandresp);
 
-    return 0;
+	return 0;
 }
 
 int ArdulinkFlushLLCommands(void * dev)
 {
-    return 0;
+	return 0;
 }
 
 int ArdulinkControl3v3(void * dev, int power_on) {
-    char c;
+	char c;
 
-    fprintf(stderr, "Ardulink: target power %d\n", power_on);
+	fprintf(stderr, "Ardulink: target power %d\n", power_on);
 
-    c = power_on ? 'p' : 'P';
-    if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, &c, 1) == -1)
-        return -errno;
+	c = power_on ? 'p' : 'P';
+	if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, &c, 1) == -1)
+		return -errno;
 
-    if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, &c, 1) == -1)
-        return -errno;
+	if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, &c, 1) == -1)
+		return -errno;
 
-    if (c != '+')
-        return -EPROTO;
+	if (c != '+')
+		return -71; // EPROTO
 
-    usleep(20000);
-    return 0;
+	MCF.DelayUS(dev, 20000);
+	return 0;
 }
 
 int ArdulinkDelayUS(void * dev, int microseconds) {
-    //fprintf(stderr, "Ardulink: faking delay %d\n", microseconds);
-    //usleep(microseconds);
-    return 0;
+	//fprintf(stderr, "Ardulink: faking delay %d\n", microseconds);
+	//usleep(microseconds);
+	return 0;
 }
 
 int ArdulinkExit(void * dev)
 {
-    serial_dev_close(&((ardulink_ctx_t*)dev)->serial);
-    free(dev);
-    return 0;
+	serial_dev_close(&((ardulink_ctx_t*)dev)->serial);
+	free(dev);
+	return 0;
 }
 
+int ArdulinkSetupInterface( void * dev )
+{
+	char first;
+	// Let the bootloader do its thing.
+	MCF.DelayUS(dev, 3UL*1000UL*1000UL);
+
+	if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, &first, 1) == -1) {
+		perror("read");
+		return -1;
+	}
+
+	if (first != '!') {
+		fprintf(stderr, "Ardulink: not the sync character.\n");
+		return -1;
+	}
+}
 
 void * TryInit_Ardulink(const init_hints_t* hints)
 {
-    ardulink_ctx_t *ctx;
-    char first;
-
-    if (!(ctx = calloc(sizeof(ardulink_ctx_t), 1))) {
-        perror("calloc");
-        return NULL;
-    }
-
-    const char* serial_to_open = NULL;
-    // Get the serial port that shall be opened.
-    // First, if we have a directly set serial port hint, use that.
-    // Otherwise, use the environment variable MINICHLINK_SERIAL.
-    // If that also doesn't exist, fall back to the default serial.
-    if (hints && hints->serial_port != NULL) {
-        serial_to_open = hints->serial_port;
-    }
-    else if ((serial_to_open = getenv("MINICHLINK_SERIAL")) == NULL) {
-        // fallback
-        serial_to_open = DEFAULT_SERIAL_NAME;
-    }
-
-    if (serial_dev_create(&ctx->serial, serial_to_open, 115200) == -1) {
-        perror("create");
-        return NULL;
-    }
-
-    if (serial_dev_open(&ctx->serial) == -1) {
-        perror("open");
-        return NULL;
-    }
-
-    // Arduino DTR reset.
-    if (serial_dev_do_dtr_reset(&ctx->serial) == -1) {
-        perror("dtr reset");
-        return NULL;
-    }
-
-    // Flush anything that might be in the RX buffer, we need the sync char.
-    if (serial_dev_flush_rx(&ctx->serial) == -1) {
-        perror("flush rx");
-        return NULL;
-    }
-
-    // Let the bootloader do its thing.
-    usleep(3UL*1000UL*1000UL);
-
-    if (serial_dev_read(&ctx->serial, &first, 1) == -1) {
-        perror("read");
-        return NULL;
-    }
-
-    if (first != '!') {
-        fprintf(stderr, "Ardulink: not the sync character.\n");
-        return NULL;
-    }
-
-    fprintf(stderr, "Ardulink: synced.\n");
-
-    MCF.WriteReg32 = ArdulinkWriteReg32;
+	ardulink_ctx_t *ctx;
+
+	if (!(ctx = calloc(sizeof(ardulink_ctx_t), 1))) {
+		perror("calloc");
+		return NULL;
+	}
+
+	const char* serial_to_open = NULL;
+	// Get the serial port that shall be opened.
+	// First, if we have a directly set serial port hint, use that.
+	// Otherwise, use the environment variable MINICHLINK_SERIAL.
+	// If that also doesn't exist, fall back to the default serial.
+	if (hints && hints->serial_port != NULL) {
+		serial_to_open = hints->serial_port;
+	}
+	else if ((serial_to_open = getenv("MINICHLINK_SERIAL")) == NULL) {
+		// fallback
+		serial_to_open = DEFAULT_SERIAL_NAME;
+	}
+
+	if (serial_dev_create(&ctx->serial, serial_to_open, 115200) == -1) {
+		perror("create");
+		return NULL;
+	}
+
+	if (serial_dev_open(&ctx->serial) == -1) {
+		perror("open");
+		return NULL;
+	}
+
+	// Arduino DTR reset.
+	if (serial_dev_do_dtr_reset(&ctx->serial) == -1) {
+		perror("dtr reset");
+		return NULL;
+	}
+
+	// Flush anything that might be in the RX buffer, we need the sync char.
+	if (serial_dev_flush_rx(&ctx->serial) == -1) {
+		perror("flush rx");
+		return NULL;
+	}
+
+	fprintf(stderr, "Ardulink: synced.\n");
+
+	MCF.WriteReg32 = ArdulinkWriteReg32;
 	MCF.ReadReg32 = ArdulinkReadReg32;
-    MCF.FlushLLCommands = ArdulinkFlushLLCommands;
-    MCF.Control3v3 = ArdulinkControl3v3;
-    MCF.DelayUS = ArdulinkDelayUS;
+	MCF.FlushLLCommands = ArdulinkFlushLLCommands;
+	MCF.Control3v3 = ArdulinkControl3v3;
+	MCF.DelayUS = ArdulinkDelayUS;
 	MCF.Exit = ArdulinkExit;
+	MCF.SetupInterface = ArdulinkSetupInterface;
 
 	return ctx;
 }
diff --git a/minichlink/hidapi.c b/minichlink/hidapi.c
index 1056c56104437fa54021e88f0fd33c20c8751f4a..04c6dced558f81fddba17a240c6dee98aa1c4e2d 100644
--- a/minichlink/hidapi.c
+++ b/minichlink/hidapi.c
@@ -2202,6 +2202,8 @@ int main(void)
 
 #include "hidapi.h"
 
+int g_hidapiSuppress;
+
 /* Definitions from linux/hidraw.h. Since these are new, some distros
    may not have header files which contain them. */
 #ifndef HIDIOCSFEATURE
@@ -2796,14 +2798,14 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
 
 		/* Get Report Descriptor Size */
 		res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
-		if (res < 0)
+		if (res < 0 && !g_hidapiSuppress)
 			perror("HIDIOCGRDESCSIZE");
 
 
 		/* Get Report Descriptor */
 		rpt_desc.size = desc_size;
 		res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc);
-		if (res < 0) {
+		if (res < 0 && !g_hidapiSuppress) {
 			perror("HIDIOCGRDESC");
 		} else {
 			/* Determine if this device uses numbered reports. */
@@ -2899,7 +2901,7 @@ int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char
 	int res;
 
 	res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data);
-	if (res < 0)
+	if (res < 0 && !g_hidapiSuppress)
 		perror("ioctl (SFEATURE)");
 
 	return res;
@@ -2910,7 +2912,7 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data,
 	int res;
 
 	res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data);
-	if (res < 0)
+	if (res < 0 && !g_hidapiSuppress)
 		perror("ioctl (GFEATURE)");
 
 
diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c
index efbda9d45331da8595d35d04766adeec56318295..0abb1ff5441d6c34c3593b22a156d198a2628e00 100644
--- a/minichlink/minichlink.c
+++ b/minichlink/minichlink.c
@@ -20,7 +20,6 @@ void Sleep(uint32_t dwMilliseconds);
 
 static int64_t StringToMemoryAddress( const char * number ) __attribute__((used));
 static void StaticUpdatePROGBUFRegs( void * dev ) __attribute__((used));
-static int InternalUnlockBootloader( void * dev ) __attribute__((used));
 int DefaultReadBinaryBlob( void * dev, uint32_t address_to_read_from, uint32_t read_size, uint8_t * blob );
 
 void TestFunction(void * v );
@@ -55,7 +54,16 @@ void * MiniCHLinkInitAsDLL( struct MiniChlinkFunctions ** MCFO, const init_hints
 		return 0;
 	}
 
+	struct InternalState * iss = calloc( 1, sizeof( struct InternalState ) );
+	((struct ProgrammerStructBase*)dev)->internal = iss;
+	iss->ram_base = 0x20000000;
+	iss->ram_size = 2048;
+	iss->sector_size = 64;
+	iss->flash_size = 16384;
+	iss->target_chip_type = 0;
+
 	SetupAutomaticHighLevelFunctions( dev );
+
 	if( MCFO )
 	{
 		*MCFO = &MCF;
@@ -63,37 +71,30 @@ void * MiniCHLinkInitAsDLL( struct MiniChlinkFunctions ** MCFO, const init_hints
 	return dev;
 }
 
-void parse_possible_init_hints(int argc, char **argv, init_hints_t *hints)
-{
-	if (!hints)
-		return;
-	int c;
-	opterr = 0;
-	/* we're only interested in the value for the COM port, given in a -c parameter */
-	/* the '-' is really important so that getopt does not permutate the argv array and messes up parsing later */
-	while ((c = getopt(argc, argv, "-c:")) != -1)
-	{
-		switch (c)
-		{
-		case 'c':
-			// we can use the pointer as-is because it points in our 
-			// argv array and that is stable.
-			hints->serial_port = optarg;
-			break;
-		}
-	}
-}
-
 #if !defined( MINICHLINK_AS_LIBRARY ) && !defined( MINICHLINK_IMPORT )
 int main( int argc, char ** argv )
 {
+	int i;
+
 	if( argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h' )
 	{
 		goto help;
 	}
 	init_hints_t hints;
 	memset(&hints, 0, sizeof(hints));
-	parse_possible_init_hints(argc, argv, &hints);
+
+	// Scan for possible hints.
+	for( i = 0; i < argc; i++ )
+	{
+		char * v = argv[i];
+		if( strncmp( v, "-c", 2 ) == 0 )
+		{
+			i++;
+			if( i < argc )
+				hints.serial_port = argv[i];
+		}
+	}
+
 	void * dev = MiniCHLinkInitAsDLL( 0, &hints );
 	if( !dev )
 	{
@@ -194,55 +195,55 @@ keep_going:
 					goto unimplemented;
 				break;
 			case 'b':  //reBoot
-				if( !MCF.HaltMode || MCF.HaltMode( dev, 1 ) )
+				if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_REBOOT ) )
 					goto unimplemented;
 				break;
 			case 'B':  //reBoot into Bootloader
-				if( !MCF.HaltMode || MCF.HaltMode( dev, 3 ) )
+				if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_GO_TO_BOOTLOADER ) )
 					goto unimplemented;
 				break;
 			case 'e':  //rEsume
-				if( !MCF.HaltMode || MCF.HaltMode( dev, 2 ) )
+				if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_RESUME ) )
 					goto unimplemented;
 				break;
 			case 'E':  //Erase whole chip.
-				if( MCF.HaltMode ) MCF.HaltMode( dev, 0 );
+				if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
 				if( !MCF.Erase || MCF.Erase( dev, 0, 0, 1 ) )
 					goto unimplemented;
 				break;
 			case 'a':
-				if( !MCF.HaltMode || MCF.HaltMode( dev, 0 ) )
+				if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ) )
 					goto unimplemented;
 				break;
 			case 'A':  // Halt without reboot
-				if( !MCF.HaltMode || MCF.HaltMode( dev, 5 ) )
+				if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET ) )
 					goto unimplemented;
 				break;
 
 			// disable NRST pin (turn it into a GPIO)
 			case 'd':  // see "RSTMODE" in datasheet
-				if( MCF.HaltMode ) MCF.HaltMode( dev, 0 );
+				if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
 				if( MCF.ConfigureNRSTAsGPIO )
 					MCF.ConfigureNRSTAsGPIO( dev, 0 );
 				else
 					goto unimplemented;
 				break;
 			case 'D': // see "RSTMODE" in datasheet
-				if( MCF.HaltMode ) MCF.HaltMode( dev, 0 );
+				if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
 				if( MCF.ConfigureNRSTAsGPIO )
 					MCF.ConfigureNRSTAsGPIO( dev, 1 );
 				else
 					goto unimplemented;
 				break;
 			case 'p': 
-				if( MCF.HaltMode ) MCF.HaltMode( dev, 0 );
+				if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
 				if( MCF.ConfigureReadProtection )
 					MCF.ConfigureReadProtection( dev, 0 );
 				else
 					goto unimplemented;
 				break;
 			case 'P':
-				if( MCF.HaltMode ) MCF.HaltMode( dev, 0 );
+				if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
 				if( MCF.ConfigureReadProtection )
 					MCF.ConfigureReadProtection( dev, 1 );
 				else
@@ -263,11 +264,10 @@ keep_going:
 				{
 					printf( "GDBServer Running\n" );
 				}
-				else
+				else if( argchar[1] == 'T' )
 				{
 					// In case we aren't running already.
-					//MCF.HaltMode( dev, 2 );
-					//XXX TODO: Why do some programmers start automatically, and others don't? 
+					MCF.HaltMode( dev, 2 );
 				}
 
 				do
@@ -385,7 +385,7 @@ keep_going:
 			}
 			case 'r':
 			{
-				if( MCF.HaltMode ) MCF.HaltMode( dev, 5 ); //No need to reboot.
+				if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET ); //No need to reboot.
 
 				if( argchar[2] != 0 )
 				{
@@ -463,6 +463,7 @@ keep_going:
 			}
 			case 'w':
 			{
+				struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
 				if( argchar[2] != 0 ) goto help;
 				iarg++;
 				argchar = 0; // Stop advancing
@@ -543,14 +544,14 @@ keep_going:
 					fprintf( stderr, "Error: File I/O Fault.\n" );
 					exit( -10 );
 				}
-				if( len > 16384 )
+				if( len > iss->flash_size )
 				{
 					fprintf( stderr, "Error: Image for CH32V003 too large (%d)\n", len );
 					exit( -9 );
 				}
 
-				int is_flash = ( offset & 0xff000000 ) == 0x08000000 || ( offset & 0x1FFFF800 ) == 0x1FFFF000;
-				if( MCF.HaltMode ) MCF.HaltMode( dev, is_flash?0:5 );
+				int is_flash = IsAddressFlash( offset );
+				if( MCF.HaltMode ) MCF.HaltMode( dev, is_flash ? HALT_MODE_HALT_AND_RESET : HALT_MODE_HALT_BUT_NO_RESET );
 
 				if( MCF.WriteBinaryBlob )
 				{
@@ -593,6 +594,7 @@ help:
 	fprintf( stderr, " -f Disable 5V\n" );
 	fprintf( stderr, " -c [serial port for Ardulink, try /dev/ttyACM0 or COM11 etc]\n" );
 	fprintf( stderr, " -u Clear all code flash - by power off (also can unbrick)\n" );
+	fprintf( stderr, " -E Erase chip\n" );
 	fprintf( stderr, " -b Reboot out of Halt\n" );
 	fprintf( stderr, " -e Resume from halt\n" );
 	fprintf( stderr, " -a Reboot into Halt\n" );
@@ -624,8 +626,6 @@ unimplemented:
 #define strtoll _strtoi64
 #endif
 
-static int StaticUnlockFlash( void * dev, struct InternalState * iss );
-
 int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber )
 {
 	if( !number || !number[0] ) return defaultNumber;
@@ -680,7 +680,7 @@ static int DefaultWaitForFlash( void * dev )
 		rw = 0;
 		MCF.ReadWord( dev, (intptr_t)&FLASH->STATR, &rw ); // FLASH_STATR => 0x4002200C
 		if( timeout++ > 100 ) return -1;
-	} while(rw & 1);  // BSY flag.
+	} while(rw & 3);  // BSY flag for 003, or WRBSY for other processors.
 
 	if( rw & FLASH_STATR_WRPRTERR )
 	{
@@ -734,7 +734,7 @@ int DefaultSetupInterface( void * dev )
 	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
 
 	if( MCF.Control3v3 ) MCF.Control3v3( dev, 1 );
-	if( MCF.DelayUS ) MCF.DelayUS( dev, 16000 );
+	MCF.DelayUS( dev, 16000 );
 	MCF.WriteReg32( dev, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg
 	MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave)
 	MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // Bug in silicon?  If coming out of cold boot, and we don't do our little "song and dance" this has to be called.
@@ -780,7 +780,7 @@ static void StaticUpdatePROGBUFRegs( void * dev )
 	MCF.WriteReg32( dev, DMCOMMAND, 0x0023100d );      // Copy data to x13
 }
 
-static int InternalUnlockBootloader( void * dev )
+int InternalUnlockBootloader( void * dev )
 {
 	if( !MCF.WriteWord ) return -99;
 	int ret = 0;
@@ -805,6 +805,23 @@ static int InternalUnlockBootloader( void * dev )
 }
 
 
+int InternalIsMemoryErased( struct InternalState * iss, uint32_t address )
+{
+	if(( address & 0xff000000 ) != 0x08000000 ) return 0;
+	int sector = (address & 0xffffff) / iss->sector_size;
+	if( sector >= MAX_FLASH_SECTORS )
+		return 0;
+	else
+		return iss->flash_sector_status[sector];
+}
+
+void InternalMarkMemoryNotErased( struct InternalState * iss, uint32_t address )
+{
+	if(( address & 0xff000000 ) != 0x08000000 ) return;
+	int sector = (address & 0xffffff) / iss->sector_size;
+	if( sector < MAX_FLASH_SECTORS )
+		iss->flash_sector_status[sector] = 0;
+}
 
 static int DefaultWriteHalfWord( void * dev, uint32_t address_to_write, uint16_t data )
 {
@@ -924,12 +941,7 @@ static int DefaultWriteWord( void * dev, uint32_t address_to_write, uint32_t dat
 	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
 	int ret = 0;
 
-	int is_flash = 0;
-	if( ( address_to_write & 0xff000000 ) == 0x08000000 || ( address_to_write & 0x1FFFF800 ) == 0x1FFFF000 )
-	{
-		// Is flash.
-		is_flash = 1;
-	}
+	int is_flash = IsAddressFlash( address_to_write );
 
 	if( iss->statetag != STTAG( "WRSQ" ) || is_flash != iss->lastwriteflags )
 	{
@@ -1023,17 +1035,24 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob
 
 	uint32_t rw;
 	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
-	int is_flash = 0;
+	int sectorsize = iss->sector_size;
+
+	// We can't write into flash when mapped to 0x00000000
+	if( address_to_write < 0x01000000 )
+		address_to_write |= 0x08000000;
+
+	int is_flash = IsAddressFlash( address_to_write );
 
 	if( blob_size == 0 ) return 0;
 
-	if( (address_to_write & 0xff000000) == 0x08000000 || (address_to_write & 0xff000000) == 0x00000000 || (address_to_write & 0x1FFFF800) == 0x1FFFF000 ) 
-		is_flash = 1;
 
-	// We can't write into flash when mapped to 0x00000000
-	if( is_flash )
-		address_to_write |= 0x08000000;
+	if( is_flash && !iss->flash_unlocked )
+	{
+		if( ( rw = InternalUnlockFlash( dev, iss ) ) )
+			return rw;
+	}
 
+	// Regardless of sector size, allow block write to do its thing if it can.
 	if( is_flash && MCF.BlockWrite64 && ( address_to_write & 0x3f ) == 0 && ( blob_size & 0x3f ) == 0 )
 	{
 		int i;
@@ -1049,50 +1068,48 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob
 		return 0;
 	}
 
-	if( is_flash && !iss->flash_unlocked )
-	{
-		if( ( rw = StaticUnlockFlash( dev, iss ) ) )
-			return rw;
-	}
-
-
-	uint8_t tempblock[64];
-	int sblock =  address_to_write >> 6;
-	int eblock = ( address_to_write + blob_size + 0x3f) >> 6;
+	uint8_t tempblock[sectorsize];
+	int sblock =  address_to_write / sectorsize;
+	int eblock = ( address_to_write + blob_size + (sectorsize-1) ) / sectorsize;
 	int b;
 	int rsofar = 0;
 
 	for( b = sblock; b < eblock; b++ )
 	{
-		int offset_in_block = address_to_write - (b * 64);
+		int offset_in_block = address_to_write - (b * sectorsize);
 		if( offset_in_block < 0 ) offset_in_block = 0;
-		int end_o_plus_one_in_block = ( address_to_write + blob_size ) - (b*64);
-		if( end_o_plus_one_in_block > 64 ) end_o_plus_one_in_block = 64;
-		int	base = b * 64;
+		int end_o_plus_one_in_block = ( address_to_write + blob_size ) - (b*sectorsize);
+		if( end_o_plus_one_in_block > sectorsize ) end_o_plus_one_in_block = sectorsize;
+		int	base = b * sectorsize;
 
-		if( offset_in_block == 0 && end_o_plus_one_in_block == 64 )
+		if( offset_in_block == 0 && end_o_plus_one_in_block == sectorsize )
 		{
-			if( MCF.BlockWrite64 ) 
+			if( MCF.BlockWrite64 )
 			{
-				int r = MCF.BlockWrite64( dev, base, blob + rsofar );
-				rsofar += 64;
-				if( r )
+				int i;
+				for( i = 0; i < sectorsize/64; i++ )
 				{
-					fprintf( stderr, "Error writing block at memory %08x (error = %d)\n", base, r );
-					return r;
+					int r = MCF.BlockWrite64( dev, base + i*64, blob + rsofar+i*64 );
+					rsofar += 64;
+					if( r )
+					{
+						fprintf( stderr, "Error writing block at memory %08x (error = %d)\n", base, r );
+						return r;
+					}
 				}
 			}
 			else 					// Block Write not avaialble
 			{
 				if( is_flash )
 				{
-					MCF.Erase( dev, base, 64, 0 );
+					if( !InternalIsMemoryErased( iss, base ) )
+						MCF.Erase( dev, base, sectorsize, 0 );
 					MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010
 					MCF.WriteWord( dev, 0x40022010, CR_BUF_RST | CR_PAGE_PG );  // (intptr_t)&FLASH->CTLR = 0x40022010
 				}
 
 				int j;
-				for( j = 0; j < 16; j++ )
+				for( j = 0; j < sectorsize/4; j++ )
 				{
 					uint32_t writeword;
 					memcpy( &writeword, blob + rsofar, 4 );
@@ -1103,8 +1120,10 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob
 				if( is_flash )
 				{
 					MCF.WriteWord( dev, 0x40022014, base );  //0x40022014 -> FLASH->ADDR
+					if( MCF.PrepForLongOp ) MCF.PrepForLongOp( dev );  // Give the programmer a headsup this next operation could take a while.
 					MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // 0x40022010 -> FLASH->CTLR
 					if( MCF.WaitForFlash ) MCF.WaitForFlash( dev );
+					InternalMarkMemoryNotErased( iss, base );
 				}
 			}
 		}
@@ -1113,7 +1132,7 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob
 			//Ok, we have to do something wacky.
 			if( is_flash )
 			{
-				MCF.ReadBinaryBlob( dev, base, 64, tempblock );
+				MCF.ReadBinaryBlob( dev, base, sectorsize, tempblock );
 
 				// Permute tempblock
 				int tocopy = end_o_plus_one_in_block - offset_in_block;
@@ -1122,23 +1141,29 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob
 
 				if( MCF.BlockWrite64 ) 
 				{
-					int r = MCF.BlockWrite64( dev, base, tempblock );
-					if( r ) return r;
+					int i;
+					for( i = 0; i < sectorsize/64; i++ )
+					{
+						int r = MCF.BlockWrite64( dev, base+i*64, tempblock+i*64 );
+						if( r ) return r;
+					}
 				}
 				else
 				{
-					MCF.Erase( dev, base, 64, 0 );
+					if( !InternalIsMemoryErased( iss, base ) )
+						MCF.Erase( dev, base, sectorsize, 0 );
 					MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010
 					MCF.WriteWord( dev, 0x40022010, CR_BUF_RST | CR_PAGE_PG );  // (intptr_t)&FLASH->CTLR = 0x40022010
 
 					int j;
-					for( j = 0; j < 16; j++ )
+					for( j = 0; j < sectorsize/4; j++ )
 					{
 						MCF.WriteWord( dev, j*4+base, *(uint32_t*)(tempblock + j * 4) );
 						rsofar += 4;
 					}
 					MCF.WriteWord( dev, 0x40022014, base );  //0x40022014 -> FLASH->ADDR
 					MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // 0x40022010 -> FLASH->CTLR
+					InternalMarkMemoryNotErased( iss, base );
 				}
 				if( MCF.WaitForFlash && MCF.WaitForFlash( dev ) ) goto timedout;
 			}
@@ -1146,7 +1171,7 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob
 			{
 				// Accessing RAM.  Be careful to only do the needed operations.
 				int j;
-				for( j = 0; j < 16; j++ )
+				for( j = 0; j < sectorsize; j++ )
 				{
 					uint32_t taddy = j*4;
 					if( offset_in_block <= taddy && end_o_plus_one_in_block >= taddy + 4 )
@@ -1206,7 +1231,7 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob
 	}
 #endif
 
-	if(MCF.DelayUS) MCF.DelayUS( dev, 100 ); // Why do we need this? (We seem to need this on the WCH programmers?)
+	MCF.DelayUS( dev, 100 ); // Why do we need this? (We seem to need this on the WCH programmers?)
 	return 0;
 timedout:
 	fprintf( stderr, "Timed out\n" );
@@ -1277,21 +1302,29 @@ static int DefaultReadWord( void * dev, uint32_t address_to_read, uint32_t * dat
 	return r;
 }
 
-static int StaticUnlockFlash( void * dev, struct InternalState * iss )
+int InternalUnlockFlash( void * dev, struct InternalState * iss )
 {
+	int ret = 0;
 	uint32_t rw;
-	MCF.ReadWord( dev, 0x40022010, &rw );  // FLASH->CTLR = 0x40022010
+	ret = MCF.ReadWord( dev, 0x40022010, &rw );  // FLASH->CTLR = 0x40022010
 	if( rw & 0x8080 ) 
 	{
+		ret = MCF.WriteWord( dev, 0x40022004, 0x45670123 ); // FLASH->KEYR = 0x40022004
+		if( ret ) goto reterr;
+		ret = MCF.WriteWord( dev, 0x40022004, 0xCDEF89AB );
+		if( ret ) goto reterr;
+		ret = MCF.WriteWord( dev, 0x40022008, 0x45670123 ); // OBKEYR = 0x40022008
+		if( ret ) goto reterr;
+		ret = MCF.WriteWord( dev, 0x40022008, 0xCDEF89AB );
+		if( ret ) goto reterr;
+		ret = MCF.WriteWord( dev, 0x40022024, 0x45670123 ); // MODEKEYR = 0x40022024
+		if( ret ) goto reterr;
+		ret = MCF.WriteWord( dev, 0x40022024, 0xCDEF89AB );
+		if( ret ) goto reterr;
+
+		ret = MCF.ReadWord( dev, 0x40022010, &rw ); // FLASH->CTLR = 0x40022010
+		if( ret ) goto reterr;
 
-		MCF.WriteWord( dev, 0x40022004, 0x45670123 ); // FLASH->KEYR = 0x40022004
-		MCF.WriteWord( dev, 0x40022004, 0xCDEF89AB );
-		MCF.WriteWord( dev, 0x40022008, 0x45670123 ); // OBKEYR = 0x40022008
-		MCF.WriteWord( dev, 0x40022008, 0xCDEF89AB );
-		MCF.WriteWord( dev, 0x40022024, 0x45670123 ); // MODEKEYR = 0x40022024
-		MCF.WriteWord( dev, 0x40022024, 0xCDEF89AB );
-
-		MCF.ReadWord( dev, 0x40022010, &rw ); // FLASH->CTLR = 0x40022010
 		if( rw & 0x8080 ) 
 		{
 			fprintf( stderr, "Error: Flash is not unlocked (CTLR = %08x)\n", rw );
@@ -1300,6 +1333,9 @@ static int StaticUnlockFlash( void * dev, struct InternalState * iss )
 	}
 	iss->flash_unlocked = 1;
 	return 0;
+reterr:
+	fprintf( stderr, "Error unlocking flash, got code %d from underlying system\n", ret );
+	return ret;
 }
 
 int DefaultErase( void * dev, uint32_t address, uint32_t length, int type )
@@ -1309,9 +1345,8 @@ int DefaultErase( void * dev, uint32_t address, uint32_t length, int type )
 
 	if( !iss->flash_unlocked )
 	{
-		if( ( rw = StaticUnlockFlash( dev, iss ) ) )
+		if( ( rw = InternalUnlockFlash( dev, iss ) ) )
 			return rw;
-		printf( "Flash unlocked\n" );
 	}
 
 	if( type == 1 )
@@ -1319,37 +1354,47 @@ int DefaultErase( void * dev, uint32_t address, uint32_t length, int type )
 		// Whole-chip flash
 		iss->statetag = STTAG( "XXXX" );
 		printf( "Whole-chip erase\n" );
-		MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, 0 );
-		MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, FLASH_CTLR_MER  );
-		MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_STRT_Set|FLASH_CTLR_MER );
+		if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, 0 ) ) goto flashoperr;
+		if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, FLASH_CTLR_MER  ) ) goto flashoperr;
+		if( MCF.PrepForLongOp ) MCF.PrepForLongOp( dev );  // Give the programmer a headsup this next operation could take a while.
+		if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_STRT_Set|FLASH_CTLR_MER ) ) goto flashoperr;
 		rw = MCF.WaitForDoneOp( dev, 0 );
 		if( MCF.WaitForFlash && MCF.WaitForFlash( dev ) ) { fprintf( stderr, "Error: Wait for flash error.\n" ); return -11; }
-		rw = MCF.WaitForDoneOp( dev, 0 );
-		MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, 0 );
-		rw = MCF.WaitForDoneOp( dev, 0 );
-		fprintf( stderr, "Whole Chip Erase Code: %d\n", rw );
+		MCF.VoidHighLevelState( dev );
+		memset( iss->flash_sector_status, 1, sizeof( iss->flash_sector_status ) );
 	}
 	else
 	{
 		// 16.4.7, Step 3: Check the BSY bit of the FLASH_STATR register to confirm that there are no other programming operations in progress.
 		// skip (we make sure at the end)
-
 		int chunk_to_erase = address;
-
 		while( chunk_to_erase < address + length )
 		{
+			if( ( chunk_to_erase & 0xff000000 ) == 0x08000000 )
+			{
+				int sector = ( chunk_to_erase & 0x00ffffff ) / iss->sector_size;
+				if( sector < MAX_FLASH_SECTORS )
+				{
+					iss->flash_sector_status[sector] = 1;
+				}
+			}
+
 			// Step 4:  set PAGE_ER of FLASH_CTLR(0x40022010)
-			MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_PAGE_ER ); // Actually FTER
+			if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_PAGE_ER ) ) goto flashoperr; // Actually FTER
 			// Step 5: Write the first address of the fast erase page to the FLASH_ADDR register.
-			MCF.WriteWord( dev, (intptr_t)&FLASH->ADDR, chunk_to_erase  );
-
+			if( MCF.WriteWord( dev, (intptr_t)&FLASH->ADDR, chunk_to_erase ) ) goto flashoperr;
 			// Step 6: Set the STAT bit of FLASH_CTLR register to '1' to initiate a fast page erase (64 bytes) action.
-			MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_STRT_Set | CR_PAGE_ER );
+			if( MCF.PrepForLongOp ) MCF.PrepForLongOp( dev );  // Give the programmer a headsup this next operation could take a while.
+			if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_STRT_Set | CR_PAGE_ER ) ) goto flashoperr;
 			if( MCF.WaitForFlash && MCF.WaitForFlash( dev ) ) return -99;
-			chunk_to_erase+=64;
+			chunk_to_erase+=iss->sector_size;
 		}
 	}
+
 	return 0;
+flashoperr:
+	fprintf( stderr, "Error: Flash operation error\n" );
+	return -93;
 }
 
 int DefaultReadBinaryBlob( void * dev, uint32_t address_to_read_from, uint32_t read_size, uint8_t * blob )
@@ -1518,27 +1563,26 @@ static int DefaultHaltMode( void * dev, int mode )
 	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
 	switch ( mode )
 	{
-	case 5: // Don't reboot.
-	case 0:
+	case HALT_MODE_HALT_BUT_NO_RESET: // Don't reboot.
+	case HALT_MODE_HALT_AND_RESET:
 		MCF.WriteReg32( dev, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg
 		MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave)
 		MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // Bug in silicon?  If coming out of cold boot, and we don't do our little "song and dance" this has to be called.
 		MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
 		if( mode == 0 ) MCF.WriteReg32( dev, DMCONTROL, 0x80000003 ); // Reboot.
 		MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Re-initiate a halt request.
-
 //		MCF.WriteReg32( dev, DMCONTROL, 0x00000001 ); // Clear Halt Request.  This is recommended, but not doing it seems more stable.
 		// Sometimes, even if the processor is halted but the MSB is clear, it will spuriously start?
 		MCF.FlushLLCommands( dev );
 		break;
-	case 1:
+	case HALT_MODE_REBOOT:
 		MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
 		MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate a halt request.
 		MCF.WriteReg32( dev, DMCONTROL, 0x80000003 ); // Reboot.
 		MCF.WriteReg32( dev, DMCONTROL, 0x40000001 ); // resumereq
 		MCF.FlushLLCommands( dev );
 		break;
-	case 2:
+	case HALT_MODE_RESUME:
 		MCF.WriteReg32( dev, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg
 		MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave)
 		MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // Bug in silicon?  If coming out of cold boot, and we don't do our little "song and dance" this has to be called.
@@ -1546,7 +1590,7 @@ static int DefaultHaltMode( void * dev, int mode )
 		MCF.WriteReg32( dev, DMCONTROL, 0x40000001 ); // resumereq
 		MCF.FlushLLCommands( dev );
 		break;
-	case 3:
+	case HALT_MODE_GO_TO_BOOTLOADER:
 		MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
 		MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate a halt request.
 
@@ -1564,18 +1608,13 @@ static int DefaultHaltMode( void * dev, int mode )
 	default:
 		fprintf( stderr, "Error: Unknown halt mode %d\n", mode );
 	}
-#if 0
-	int i;
-	for( i = 0; i < 100; i++ )
-	{
-		uint32_t temp = 0;
-		MCF.ReadReg32( dev, DMSTATUS, &temp );
-		fprintf( stderr, "DMSTATUS: %08x\n", temp );
-		usleep( 20000);
-	}
-#endif
 
+	iss->flash_unlocked = 0;
 	iss->processor_in_mode = mode;
+
+	// In case processor halt process needs to complete, i.e. if it was in the middle of a flash op.
+	MCF.DelayUS( dev, 3000 );
+
 	return 0;
 }
 
@@ -1596,7 +1635,6 @@ int DefaultPollTerminal( void * dev, uint8_t * buffer, int maxlen, uint32_t leav
 	}
 	r = MCF.ReadReg32( dev, DMDATA0, &rr );
 	if( r < 0 ) return r;
-
 	if( maxlen < 8 ) return -9;
 
 	// DMDATA1:
@@ -1632,10 +1670,9 @@ int DefaultPollTerminal( void * dev, uint8_t * buffer, int maxlen, uint32_t leav
 
 int DefaultUnbrick( void * dev )
 {
-	// TODO: Why doesn't this work on the ESP32S2?
-
 	printf( "Entering Unbrick Mode\n" );
 	MCF.Control3v3( dev, 0 );
+
 	MCF.DelayUS( dev, 60000 );
 	MCF.DelayUS( dev, 60000 );
 	MCF.DelayUS( dev, 60000 );
@@ -1673,16 +1710,7 @@ int DefaultUnbrick( void * dev )
 	// After more experimentation, it appaers to work best by not clearing the halt request.
 
 	MCF.FlushLLCommands( dev );
-	if( MCF.DelayUS )
-		MCF.DelayUS( dev, 20000 );
-	else
-	{
-#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
-		Sleep(20);
-#else
-		usleep(20000);
-#endif
-	}
+	MCF.DelayUS( dev, 20000 );
 
 	if( timeout == max_timeout ) 
 	{
@@ -1698,138 +1726,6 @@ int DefaultConfigureNRSTAsGPIO( void * dev, int one_if_yes_gpio  )
 {
 	fprintf( stderr, "Error: DefaultConfigureNRSTAsGPIO does not work via the programmer here.  Please see the demo \"optionbytes\"\n" );
 	return -5;
-#if 0
-	int ret = 0;
-	uint32_t csw;
-
-
-	if( MCF.ReadWord( dev, 0x1FFFF800, &csw ) )
-	{
-		fprintf( stderr, "Error: failed to get user word\n" );
-		return -5;
-	}
-
-	printf( "CSW WAS : %08x\n", csw );
-
-	MCF.WriteWord( dev, 0x40022008, 0x45670123 ); // OBKEYR = 0x40022008
-	MCF.WriteWord( dev, 0x40022008, 0xCDEF89AB );
-	MCF.WriteWord( dev, 0x40022004, 0x45670123 ); // FLASH->KEYR = 0x40022004
-	MCF.WriteWord( dev, 0x40022004, 0xCDEF89AB );
-	MCF.WriteWord( dev, 0x40022024, 0x45670123 ); // MODEKEYR = 0x40022024
-	MCF.WriteWord( dev, 0x40022024, 0xCDEF89AB );
-
-//XXXX THIS DOES NOT WORK IT CANNOT ERASE.
-	uint32_t ctlr;
-	if( MCF.ReadWord( dev, 0x40022010, &ctlr ) ) // FLASH->CTLR = 0x40022010
-	{
-		return -9;
-	}
-	ctlr |= CR_OPTER_Set | CR_STRT_Set; // OBER
-	MCF.WriteWord( dev, 0x40022010, ctlr ); // FLASH->CTLR = 0x40022010
-	ret |= MCF.WaitForDoneOp( dev, 0 );
-	ret |= MCF.WaitForFlash( dev );
-
-	MCF.WriteHalfWord( dev, (intptr_t)&OB->RDPR, RDP_Key );
-
-    ctlr &=~CR_OPTER_Reset;
-	MCF.WriteWord( dev, 0x40022010, ctlr ); // FLASH->CTLR = 0x40022010
-	ret |= MCF.WaitForDoneOp( dev, 0 );
-	ret |= MCF.WaitForFlash( dev );
-    ctlr |= CR_OPTPG_Set;
-	MCF.WriteWord( dev, 0x40022010, ctlr ); // FLASH->CTLR = 0x40022010
-	ret |= MCF.WaitForDoneOp( dev, 0 );
-	ret |= MCF.WaitForFlash( dev );
-    ctlr &=~CR_OPTPG_Reset;
-	MCF.WriteWord( dev, 0x40022010, ctlr ); // FLASH->CTLR = 0x40022010
-	ret |= MCF.WaitForDoneOp( dev, 0 );
-	ret |= MCF.WaitForFlash( dev );
-
-
-// This does work to write the option bytes, but does NOT work to erase.
-
-	if( MCF.ReadWord( dev, 0x40022010, &ctlr ) ) // FLASH->CTLR = 0x40022010
-	{
-		return -9;
-	}
-	ctlr |= CR_OPTPG_Set; //OBPG
-	MCF.WriteWord( dev, 0x40022010, ctlr ); // FLASH->CTLR = 0x40022010
-	ret |= MCF.WaitForDoneOp( dev, 0 );
-	ret |= MCF.WaitForFlash( dev );
-
-	uint32_t config = OB_IWDG_HW | OB_STOP_NoRST | OB_STDBY_NoRST | (one_if_yes_gpio?OB_RST_NoEN:OB_RST_EN_DT1ms) | (uint16_t)0xE0;
-	printf( "Config (%08x): %08x\n", (intptr_t)&OB->USER, config );
-	MCF.WriteHalfWord( dev,  (intptr_t)&OB->USER, config );
-
-	ret |= MCF.WaitForDoneOp( dev, 0 );
-	ret |= MCF.WaitForFlash( dev );
-
-	ctlr &= CR_OPTPG_Reset;
-	MCF.WriteWord( dev, 0x40022010, ctlr ); // FLASH->CTLR = 0x40022010
-
-
-	if( MCF.ReadWord( dev, 0x1FFFF800, &csw ) )
-	{
-		fprintf( stderr, "Error: failed to get user word\n" );
-		return -5;
-	}
-
-	//csw >>= 16; // Only want bottom part of word.
-	printf( "CSW: %08x\n", csw );
-
-#if 0
-	uint32_t prevuser;
-	if( MCF.ReadWord( dev, 0x1FFFF800, &prevuser ) )
-	{
-		fprintf( stderr, "Error: failed to get user word\n" );
-		return -5;
-	}
-
-	ret |= MCF.WaitForFlash( dev );
-
-	// Erase.
-	MCF.ReadWord( dev, 0x40022010, &csw ); // FLASH->CTLR = 0x40022010
-	csw |= 1<<5;//OBER;
-	MCF.WriteWord( dev, 0x40022010, csw ); // FLASH->CTLR = 0x40022010
-	MCF.WriteHalfWord( dev, 0x1FFFF802, 0xffff );
-	ret |= MCF.WaitForDoneOp( dev, 0 );
-	ret |= MCF.WaitForFlash( dev );
-
-	MCF.ReadWord( dev, 0x40022010, &csw ); // FLASH->CTLR = 0x40022010
-	printf( "CTLR: %08x\n", csw );
-	csw |= 1<<9;//OBPG, OBWRE
-	MCF.WriteWord( dev, 0x40022010, csw );
-
-	int j;
-	for( j = 0; j < 5; j++ )
-	{
-		if( MCF.ReadWord( dev, 0x1FFFF800, &prevuser ) )
-		{
-			fprintf( stderr, "Error: failed to get user word\n" );
-			return -5;
-		}
-
-		//csw >>= 16; // Only want bottom part of word.
-		printf( "CSW was: %08x\n", prevuser );
-		csw = prevuser >> 16;
-		csw = csw & 0xe7e7;
-		csw |= (one_if_yes_gpio?0b11:0b00)<<(3+0);
-		csw |= (one_if_yes_gpio?0b00:0b11)<<(3+8);
-		printf( "CSW writing: %08x\n", csw );
-		MCF.WriteHalfWord( dev, 0x1FFFF802, csw );
-		ret |= MCF.WaitForDoneOp( dev, 0 );
-		ret |= MCF.WaitForFlash( dev );
-	}
-
-
-	MCF.ReadWord( dev, 0x40022010, &csw ); // FLASH->CTLR = 0x40022010
-	printf( "CTLR: %08x\n", csw );
-	csw &= ~(1<<9);//OBPG, OBWRE
-	MCF.WriteWord( dev, 0x40022010, csw );
-
-#endif
-	printf( "RET: %d\n", ret );
-	return 0;
-#endif
 }
 
 int DefaultConfigureReadProtection( void * dev, int one_if_yes_protect  )
@@ -1841,7 +1737,7 @@ int DefaultConfigureReadProtection( void * dev, int one_if_yes_protect  )
 int DefaultPrintChipInfo( void * dev )
 {
 	uint32_t reg;
-	MCF.HaltMode( dev, 5 );
+	MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET );
 	
 	if( MCF.ReadWord( dev, 0x1FFFF800, &reg ) ) goto fail;	
 	printf( "USER/RDPR  : %04x/%04x\n", reg>>16, reg&0xFFFF );
@@ -1872,10 +1768,20 @@ int DefaultVoidHighLevelState( void * dev )
 	return 0;
 }
 
+int DefaultDelayUS( void * dev, int us )
+{
+#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
+	Sleep( (us+9999) / 1000 );
+#else
+	usleep( us );
+#endif
+	return 0;
+}
+
 int SetupAutomaticHighLevelFunctions( void * dev )
 {
 	// Will populate high-level functions from low-level functions.
-	if( MCF.WriteReg32 == 0 || MCF.ReadReg32 == 0 ) return -5;
+	if( MCF.WriteReg32 == 0 && MCF.ReadReg32 == 0 && MCF.WriteWord == 0 ) return -5;
 
 	// Else, TODO: Build the high level functions from low level functions.
 	// If a high-level function alrady exists, don't override.
@@ -1926,12 +1832,9 @@ int SetupAutomaticHighLevelFunctions( void * dev )
 		MCF.ConfigureNRSTAsGPIO = DefaultConfigureNRSTAsGPIO;
 	if( !MCF.VoidHighLevelState )
 		MCF.VoidHighLevelState = DefaultVoidHighLevelState;
+	if( !MCF.DelayUS )
+		MCF.DelayUS = DefaultDelayUS;
 
-	struct InternalState * iss = calloc( 1, sizeof( struct InternalState ) );
-	iss->ram_base = 0x20000000;
-	iss->ram_size = 2048;
-
-	((struct ProgrammerStructBase*)dev)->internal = iss;
 	return 0;
 }
 
@@ -1988,3 +1891,5 @@ void TestFunction(void * dev )
 	}
 }
 
+
+
diff --git a/minichlink/minichlink.exe b/minichlink/minichlink.exe
index a52fdbc65872c0fc70a1facfb7fa9cbe22a35552..d3404712091bdd8dcc125d58deeb556c528a3c93 100644
Binary files a/minichlink/minichlink.exe and b/minichlink/minichlink.exe differ
diff --git a/minichlink/minichlink.h b/minichlink/minichlink.h
index c5f3bcba8fa1c2bf29f10a44a306f736a37ed4a3..dfdb2447ec9661a1af0ed0a095a6a9a600257d53 100644
--- a/minichlink/minichlink.h
+++ b/minichlink/minichlink.h
@@ -50,18 +50,15 @@ struct MiniChlinkFunctions
 
 	int (*SetEnableBreakpoints)( void * dev, int halt_on_break, int single_step );
 
+	int (*PrepForLongOp)( void * dev ); // Called before the command that will take a while.
 	int (*WaitForFlash)( void * dev );
 	int (*WaitForDoneOp)( void * dev, int ignore );
 
 	int (*PrintChipInfo)( void * dev );
 
-	// Geared for flash, but could be anything.
+	// Geared for flash, but could be anything.  Note: If in flash, must also erase.
 	int (*BlockWrite64)( void * dev, uint32_t address_to_write, uint8_t * data );
 
-	// TODO: What about 64-byte block-reads?
-	// TODO: What about byte read/write?
-	// TODO: What about half read/write?
-
 	// Returns positive if received text.
 	// Returns negative if error.
 	// Returns 0 if no text waiting.
@@ -86,6 +83,14 @@ struct MiniChlinkFunctions
 	FlushLLCommands
 */
 
+inline static int IsAddressFlash( uint32_t addy ) { return ( addy & 0xff000000 ) == 0x08000000 || ( addy & 0x1FFFF800 ) == 0x1FFFF000; }
+
+#define HALT_MODE_HALT_AND_RESET    0
+#define HALT_MODE_REBOOT            1
+#define HALT_MODE_RESUME            2
+#define HALT_MODE_GO_TO_BOOTLOADER  3
+#define HALT_MODE_HALT_BUT_NO_RESET 5
+
 // Convert a 4-character string to an int.
 #define STTAG( x ) (*((uint32_t*)(x)))
 
@@ -97,6 +102,8 @@ struct ProgrammerStructBase
 	// You can put other things here.
 };
 
+#define MAX_FLASH_SECTORS 262144
+
 struct InternalState
 {
 	uint32_t statetag;
@@ -107,6 +114,10 @@ struct InternalState
 	int autoincrement;
 	uint32_t ram_base;
 	uint32_t ram_size;
+	int sector_size;
+	int flash_size;
+	int target_chip_type; // 0 for unknown (or 003), otherwise a part number.
+	uint8_t flash_sector_status[MAX_FLASH_SECTORS];  // 0 means unerased/unknown. 1 means erased.
 };
 
 
@@ -168,6 +179,10 @@ int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber );
 
 // For drivers to call
 int DefaultVoidHighLevelState( void * dev );
+int InternalUnlockBootloader( void * dev );
+int InternalIsMemoryErased( struct InternalState * iss, uint32_t address );
+void InternalMarkMemoryNotErased( struct InternalState * iss, uint32_t address );
+int InternalUnlockFlash( void * dev, struct InternalState * iss );
 
 // GDBSever Functions
 int SetupGDBServer( void * dev );
diff --git a/minichlink/pgm-b003fun.c b/minichlink/pgm-b003fun.c
index 5e37aea3fc5b653577934ccb4fa626c0865592a9..a1681087a842e39d941c19a61872bb26cf8b13de 100644
--- a/minichlink/pgm-b003fun.c
+++ b/minichlink/pgm-b003fun.c
@@ -1,26 +1,224 @@
-#if 0
 #include <stdint.h>
 #include "hidapi.h"
 #include "minichlink.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "../ch32v003fun/ch32v003fun.h"
+
+//#define DEBUG_B003
+
+#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
+void Sleep(uint32_t dwMilliseconds);
+#define usleep( x ) Sleep( x / 1000 );
+#else
+#include <unistd.h>
+#endif
 
 
 struct B003FunProgrammerStruct
 {
-	void * internal;
+	void * internal; // Part of struct ProgrammerStructBase 
+
 	hid_device * hd;
 	uint8_t commandbuffer[128];
+	uint8_t respbuffer[128];
 	int commandplace;
+	int prepping_for_erase;
+};
+
+static const unsigned char byte_wise_read_blob[] = { // No alignment restrictions.
+	0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
+	0x2e, 0x96, 0x21, 0x07, 0x94, 0x21, 0x14, 0xa3, 0x85, 0x05, 0x05, 0x07,
+	0xe3, 0xcc, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff, 0x14, 0xc1, 0x82, 0x80,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const unsigned char half_wise_read_blob[] = {  // size and address must be aligned by 2.
+	0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
+	0x2e, 0x96, 0x21, 0x07, 0x96, 0x21, 0x16, 0xa3, 0x89, 0x05, 0x09, 0x07,
+	0xe3, 0xcc, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff, 0x14, 0xc1, 0x82, 0x80,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const unsigned char word_wise_read_blob[] = { // size and address must be aligned by 4.
+	0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
+	0x2e, 0x96, 0x21, 0x07, 0x94, 0x41, 0x14, 0xc3, 0x91, 0x05, 0x11, 0x07,
+	0xe3, 0xcc, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff, 0x14, 0xc1, 0x82, 0x80,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
 
+static const unsigned char word_wise_write_blob[] = { // size and address must be aligned by 4.
+	0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
+	0x2e, 0x96, 0x21, 0x07, 0x14, 0x43, 0x94, 0xc1, 0x91, 0x05, 0x11, 0x07,
+	0xe3, 0xcc, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff, 0x14, 0xc1, 0x82, 0x80, // NOTE: No readback!
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 /*
-	uint32_t state;
-	uint8_t reply[256];
-	int replylen;
-*/
+	0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
+	0x2e, 0x96, 0x21, 0x07, 0x14, 0x43, 0x94, 0xc1, 0x94, 0x41, 0x14, 0xc3, // With readback.
+	0x91, 0x05, 0x11, 0x07, 0xe3, 0xca, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff,
+	0x14, 0xc1, 0x82, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 */
+};
+
+static const unsigned char write64_flash[] = { // size and address must be aligned by 4.
+  0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x13, 0x86, 0x05, 0x04, 0x5c, 0x43,
+  0x8c, 0xc7, 0x14, 0x47, 0x94, 0xc1, 0xb7, 0x06, 0x05, 0x00, 0xd4, 0xc3,
+  0x94, 0x41, 0x91, 0x05, 0x11, 0x07, 0xe3, 0xc8, 0xc5, 0xfe, 0xc1, 0x66,
+  0x93, 0x86, 0x06, 0x04, 0xd4, 0xc3, 0xfd, 0x56, 0x14, 0xc1, 0x82, 0x80
+};
+
+static const unsigned char half_wise_write_blob[] = { // size and address must be aligned by 2
+	0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
+	0x2e, 0x96, 0x21, 0x07, 0x16, 0x23, 0x96, 0xa1, 0x96, 0x21, 0x16, 0xa3,
+	0x89, 0x05, 0x09, 0x07, 0xe3, 0xca, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff,
+	0x14, 0xc1, 0x82, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const unsigned char byte_wise_write_blob[] = { // no division requirements.
+	0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
+	0x2e, 0x96, 0x21, 0x07, 0x14, 0x23, 0x94, 0xa1, 0x94, 0x21, 0x14, 0xa3,
+	0x85, 0x05, 0x05, 0x07, 0xe3, 0xca, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff,
+	0x14, 0xc1, 0x82, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+// Just set the countdown to 0 to avoid any issues.
+//   li a3, 0; sw a3, 0(a1); li a3, -1; sw a3, 0(a0); ret;
+static const unsigned char halt_wait_blob[] = {
+	0x81, 0x46, 0x94, 0xc1, 0xfd, 0x56, 0x14, 0xc1, 0x82, 0x80 };
+
+// Set the countdown to -1 to cause main system to execute.
+//   li a3, -1; sw a3, 0(a1); li a3, -1; sw a3, 0(a0); ret;
+//static const unsigned char run_app_blob[] = {
+//	0xfd, 0x56, 0x94, 0xc1, 0xfd, 0x56, 0x14, 0xc1, 0x82, 0x80 };
+//
+// Alternatively, we do it ourselves.
+static const unsigned char run_app_blob[] = {
+	0x37, 0x07, 0x67, 0x45, 0xb7, 0x27, 0x02, 0x40, 0x13, 0x07, 0x37, 0x12,
+	0x98, 0xd7, 0x37, 0x97, 0xef, 0xcd, 0x13, 0x07, 0xb7, 0x9a, 0x98, 0xd7,
+	0x23, 0xa6, 0x07, 0x00, 0x13, 0x07, 0x00, 0x08, 0x98, 0xcb, 0xb7, 0xf7,
+	0x00, 0xe0, 0x37, 0x07, 0x00, 0x80, 0x23, 0xa8, 0xe7, 0xd0, 0x82, 0x80,
 };
 
 
+static void ResetOp( struct B003FunProgrammerStruct * eps )
+{
+	memset( eps->commandbuffer, 0, sizeof( eps->commandbuffer ) );
+	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 WriteOpArb( struct B003FunProgrammerStruct * eps, const uint8_t * data, int len )
+{
+	int place = eps->commandplace;
+	int newend = place + len;
+	if( newend < sizeof( eps->commandbuffer ) )
+	{
+		memcpy( eps->commandbuffer + place, data, len );
+	}
+	eps->commandplace = newend;
+}
+
+static int CommitOp( struct B003FunProgrammerStruct * eps )
+{
+	int retries = 0;
+	int r;
+
+	uint32_t magic_go = 0x1234abcd;
+	memcpy( eps->commandbuffer + 124, &magic_go, 4 );
+
+	#ifdef DEBUG_B003
+	{
+		int i;
+		printf( "Commit TX: %lu bytes\n", sizeof(eps->commandbuffer)  );
+		for( i = 0; i < sizeof(eps->commandbuffer) ; i++ )
+		{
+			printf( "%02x ", eps->commandbuffer[i] );
+			if( ( i & 0xf ) == 0xf ) printf( "\n" );
+		}
+		if( ( i & 0xf ) != 0xf ) printf( "\n" );
+	}
+	#endif
+
+resend:
+	r = hid_send_feature_report( eps->hd, eps->commandbuffer, sizeof(eps->commandbuffer) );
+	#ifdef DEBUG_B003
+	printf( "hid_send_feature_report = %d\n", r );
+	#endif
+	if( r < 0 )
+	{
+		fprintf( stderr, "Warning: Issue with hid_send_feature_report. Retrying\n" );
+		if( retries++ > 10 )
+			return r;
+		else
+			goto resend;
+	}
+
+
+	if( eps->prepping_for_erase )
+	{
+		usleep(4000);
+	}
+
+	int timeout = 0;
+
+	do
+	{
+		eps->respbuffer[0] = 0xaa;
+		r = hid_get_feature_report( eps->hd, eps->respbuffer, sizeof(eps->respbuffer) );
+
+		#ifdef DEBUG_B003
+		{
+			int i;
+			printf( "Commit RX: %d bytes\n", r );
+			for( i = 0; i < r; i++ )
+			{
+				printf( "%02x ", eps->respbuffer[i] );
+				if( ( i & 0xf ) == 0xf ) printf( "\n" );
+			}
+			if( ( i & 0xf ) != 0xf ) printf( "\n" );
+		}
+		#endif
+
+		if( r < 0 )
+		{
+			if( retries++ > 10 ) return r;
+			continue;
+		}
+
+		if( eps->respbuffer[1] == 0xff ) break;
+
+		if( timeout++ > 20 )
+		{
+			printf( "Error: Timed out waiting for stub to complete\n" );
+			return -99;
+		}
+	} while( 1 );
+	return 0;
+}
+
 static int B003FunFlushLLCommands( void * dev )
 {
+	// All commands are synchronous anyway.
+	return 0;
+}
+
+
+static int B003FunWaitForDoneOp( void * dev, int ignore )
+{
+	// It's synchronous, so no issue here.
 	return 0;
 }
 
@@ -30,78 +228,314 @@ static int B003FunDelayUS( void * dev, int microseconds )
 	return 0;
 }
 
-static int B003FunSetupInterface( void * dev )
+// Does not handle erasing
+static int InternalB003FunWriteBinaryBlob( void * dev, uint32_t address_to_write_to, uint32_t write_size, const uint8_t * blob )
 {
+	struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct *)dev;
+
+	int is_flash = IsAddressFlash( address_to_write_to );
+
+	if( ( address_to_write_to & 0x1 ) && write_size > 0 )
+	{
+		// Need to do byte-wise writing in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, byte_wise_write_blob, sizeof(byte_wise_write_blob) );
+		WriteOp4( eps, address_to_write_to ); // Base address to write.
+		WriteOp4( eps, 1 ); // write 1 bytes.
+		memcpy( &eps->commandbuffer[60], blob, 1 );
+		if( CommitOp( eps ) ) return -5;
+		if( is_flash && memcmp( &eps->commandbuffer[60], blob, 1 ) ) goto verifyfail;
+		blob++;
+		write_size --;
+		address_to_write_to++;
+	}
+	if( ( address_to_write_to & 0x2 ) && write_size > 1 )
+	{
+		// Need to do byte-wise writing in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, half_wise_write_blob, sizeof(half_wise_write_blob) );
+		WriteOp4( eps, address_to_write_to ); // Base address to write.
+		WriteOp4( eps, 2 ); // write 2 bytes.
+		memcpy( &eps->commandbuffer[60], blob, 2 );
+		if( CommitOp( eps ) ) return -5;
+		if( is_flash && memcmp( &eps->commandbuffer[60], blob, 2 ) ) goto verifyfail;
+		blob += 2;
+		write_size -= 2;
+		address_to_write_to+=2;
+	}
+	while( write_size > 3 )
+	{
+		int to_write_this_time = write_size & (~3);
+		if( to_write_this_time > 64 ) to_write_this_time = 64;
+
+		// Need to do byte-wise writing in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, word_wise_write_blob, sizeof(word_wise_write_blob) );
+		WriteOp4( eps, address_to_write_to ); // Base address to write.
+		WriteOp4( eps, to_write_this_time ); // write 4 bytes.
+		memcpy( &eps->commandbuffer[60], blob, to_write_this_time );
+		if( CommitOp( eps ) ) return -5;
+		if( is_flash && memcmp( &eps->commandbuffer[60], blob, to_write_this_time ) ) goto verifyfail;
+		blob += to_write_this_time;
+		write_size -= to_write_this_time;
+		address_to_write_to += to_write_this_time;
+	}
+	if( write_size > 1 )
+	{
+		// Need to do byte-wise writing in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, half_wise_write_blob, sizeof(half_wise_write_blob) );
+		WriteOp4( eps, address_to_write_to ); // Base address to write.
+		WriteOp4( eps, 2 ); // write 2 bytes.
+		memcpy( &eps->commandbuffer[60], blob, 2 );
+		if( CommitOp( eps ) ) return -5;
+		if( is_flash && memcmp( &eps->commandbuffer[60], blob, 2 ) ) goto verifyfail;
+		blob += 2;
+		write_size -= 2;
+		address_to_write_to += 2;
+	}
+	if( write_size )
+	{
+		// Need to do byte-wise writing in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, byte_wise_write_blob, sizeof(byte_wise_write_blob) );
+		WriteOp4( eps, address_to_write_to ); // Base address to write.
+		WriteOp4( eps, 1 ); // write 1 byte.
+		memcpy( &eps->commandbuffer[60], blob, 1 );
+		if( CommitOp( eps ) ) return -5;
+		if( is_flash && memcmp( &eps->commandbuffer[60], blob, 1 ) ) goto verifyfail;
+		blob += 1;
+		write_size -= 1;
+		address_to_write_to+=1;
+	}
+	eps->prepping_for_erase = 0;
 	return 0;
+verifyfail:
+	fprintf( stderr, "Error: Write Binary Blob: %d bytes to %08x\n", write_size, address_to_write_to );
+	return -6;
 }
 
-static int B003FunExit( void * dev )
+static int B003FunReadBinaryBlob( void * dev, uint32_t address_to_read_from, uint32_t read_size, uint8_t * blob )
 {
+	struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct *)dev;
+
+#ifdef DEBUG_B003
+	printf( "Read Binary Blob: %d bytes from %08x\n", read_size, address_to_read_from );
+#endif
+
+	if( ( address_to_read_from & 0x1 ) && read_size > 0 )
+	{
+		// Need to do byte-wise reading in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, byte_wise_read_blob, sizeof(byte_wise_read_blob) );
+		WriteOp4( eps, address_to_read_from ); // Base address to read.
+		WriteOp4( eps, 1 ); // Read 1 bytes.
+		if( CommitOp( eps ) ) return -5;
+		memcpy( blob, &eps->respbuffer[60], 1 );
+		blob++;
+		read_size --;
+		address_to_read_from++;
+	}
+	if( ( address_to_read_from & 0x2 ) && read_size > 1 )
+	{
+		// Need to do byte-wise reading in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, half_wise_read_blob, sizeof(half_wise_read_blob) );
+		WriteOp4( eps, address_to_read_from ); // Base address to read.
+		WriteOp4( eps, 2 ); // Read 2 bytes.
+		if( CommitOp( eps ) ) return -5;
+		memcpy( blob, &eps->respbuffer[60], 2 );
+		blob += 2;
+		read_size -= 2;
+		address_to_read_from+=2;
+	}
+	while( read_size > 3 )
+	{
+		int to_read_this_time = read_size & (~3);
+		if( to_read_this_time > 64 ) to_read_this_time = 64;
+
+		// Need to do byte-wise reading in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, word_wise_read_blob, sizeof(word_wise_read_blob) );
+		WriteOp4( eps, address_to_read_from ); // Base address to read.
+		WriteOp4( eps, to_read_this_time ); // Read 4 bytes.
+		if( CommitOp( eps ) ) return -5;
+		memcpy( blob, &eps->respbuffer[60], to_read_this_time );
+		blob += to_read_this_time;
+		read_size -= to_read_this_time;
+		address_to_read_from += to_read_this_time;
+	}
+	if( read_size > 1 )
+	{
+		// Need to do byte-wise reading in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, half_wise_read_blob, sizeof(half_wise_read_blob) );
+		WriteOp4( eps, address_to_read_from ); // Base address to read.
+		WriteOp4( eps, 2 ); // Read 2 bytes.
+		if( CommitOp( eps ) ) return -5;
+		memcpy( blob, &eps->respbuffer[60], 2 );
+		blob += 2;
+		read_size -= 2;
+		address_to_read_from += 2;
+	}
+	if( read_size )
+	{
+		// Need to do byte-wise reading in front to line up with word alignment.
+		ResetOp( eps );
+		WriteOpArb( eps, byte_wise_read_blob, sizeof(byte_wise_read_blob) );
+		WriteOp4( eps, address_to_read_from ); // Base address to read.
+		WriteOp4( eps, 1 ); // Read 1 byte.
+		if( CommitOp( eps ) ) return -5;
+		memcpy( blob, &eps->respbuffer[60], 1 );
+		blob += 1;
+		read_size -= 1;
+		address_to_read_from+=1;
+	}
 	return 0;
 }
 
-// MUST be 4-byte-aligned.
-static int B003FunWriteWord( void * dev, uint32_t address_to_write, uint32_t data )
+static int InternalB003FunBoot( void * dev )
 {
+	struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct*) dev;
+
+	printf( "Booting\n" );
+	ResetOp( eps );
+	WriteOpArb( eps, run_app_blob, sizeof(run_app_blob) );
+	if( CommitOp( eps ) ) return -5;
 	return 0;
 }
 
-static int B003FunReadWord( void * dev, uint32_t address_to_read, uint32_t * data )
+static int B003FunSetupInterface( void * dev )
 {
+	struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct*) dev;
+	printf( "Halting Boot Countdown\n" );
+	ResetOp( eps );
+	WriteOpArb( eps, halt_wait_blob, sizeof(halt_wait_blob) );
+	if( CommitOp( eps ) ) return -5;
 	return 0;
 }
 
-static int B003FunWaitForDoneOp( void * dev, int ignore )
-{
-	// ... Need this.
+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 InternalB003FunWriteBinaryBlob( dev, address_to_write, 4, (uint8_t*)&data );
+}
+
+static int B003FunReadWord( void * dev, uint32_t address_to_read, uint32_t * data )
+{
+	return B003FunReadBinaryBlob( dev, address_to_read, 4, (uint8_t*)data );
+}
 
 static int B003FunBlockWrite64( void * dev, uint32_t address_to_write, uint8_t * data )
 {
+	struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct*) dev;
+	struct InternalState * iss = eps->internal;
+
+	if( IsAddressFlash( address_to_write ) )
+	{
+		if( !iss->flash_unlocked )
+		{
+			int rw;
+			if( ( rw = InternalUnlockFlash( dev, iss ) ) )
+				return rw;
+		}
+
+		if( !InternalIsMemoryErased( iss, address_to_write ) )
+		{
+			if( MCF.Erase( dev, address_to_write, 64, 0 ) )
+			{
+				fprintf( stderr, "Error: Failed to erase sector at %08x\n", address_to_write );
+				return -9;
+			}
+		}
+
+		// Not actually needed.
+		MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG ); // (intptr_t)&FLASH->CTLR = 0x40022010
+		MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG | CR_BUF_RST); // (intptr_t)&FLASH->CTLR = 0x40022010
+
+		ResetOp( eps );
+		WriteOpArb( eps, write64_flash, sizeof(write64_flash) );
+		WriteOp4( eps, address_to_write ); // Base address to write. @52
+		WriteOp4( eps, 0x4002200c ); // FLASH STATR base address. @ 56
+		memcpy( &eps->commandbuffer[60], data, 64 ); // @60
+		if( MCF.PrepForLongOp ) MCF.PrepForLongOp( dev );  // Give the programmer a headsup this next operation could take a while.
+		if( CommitOp( eps ) ) return -5;
+
+		// This is actually built-in.
+//		MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set); // (intptr_t)&FLASH->CTLR = 0x40022010  (actually commit)
+	}
+	else
+	{
+		return InternalB003FunWriteBinaryBlob( dev, address_to_write, 64, data );
+	}
+
 	return 0;
 }
 
 static int B003FunWriteHalfWord( void * dev, uint32_t address_to_write, uint16_t data )
 {
+	return InternalB003FunWriteBinaryBlob( dev, address_to_write, 2, (uint8_t*)&data );
 }
 
 static int B003FunReadHalfWord( void * dev, uint32_t address_to_read, uint16_t * data )
 {
+	return B003FunReadBinaryBlob( dev, address_to_read, 2, (uint8_t*)data );
 }
 
 static int B003FunWriteByte( void * dev, uint32_t address_to_write, uint8_t data )
 {
+	return InternalB003FunWriteBinaryBlob( dev, address_to_write, 1, &data );
 }
 
 static int B003FunReadByte( void * dev, uint32_t address_to_read, uint8_t * data )
 {
+	return B003FunReadBinaryBlob( dev, address_to_read, 1, data );
 }
 
 
-static void ResetOp( struct B003FunProgrammerStruct * eps )
+static int B003FunHaltMode( void * dev, int mode )
 {
-	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 ) )
+	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
+	switch ( mode )
 	{
-		memcpy( eps->commandbuffer + place, &opsend, 4 );
+	case HALT_MODE_HALT_BUT_NO_RESET: // Don't reboot.
+	case HALT_MODE_HALT_AND_RESET:    // Reboot and halt
+		// This programmer is always halted anyway.
+		break;
+
+	case HALT_MODE_REBOOT:            // Actually boot?
+		InternalB003FunBoot( dev );
+		break;
+
+	case HALT_MODE_RESUME:
+		fprintf( stderr, "Warning: this programmer cannot resume\n" );
+		// We can't do this.
+		break;
+
+	case HALT_MODE_GO_TO_BOOTLOADER:
+		fprintf( stderr, "Warning: this programmer is already a bootloader.  Can't go into bootloader\n" );
+		break;
+
+	default:
+		fprintf( stderr, "Error: Unknown halt mode %d\n", mode );
 	}
-	eps->commandplace = newend;
+
+	iss->processor_in_mode = mode;
+	return 0;
 }
 
-static void CommitOp( struct B003FunProgrammerStruct * eps )
+
+int B003FunPrepForLongOp( void * dev )
 {
+	struct B003FunProgrammerStruct * d = (struct B003FunProgrammerStruct*)dev;
+	d->prepping_for_erase = 1;
+	return 0;
 }
-	CommitOp( eps );
-
 
 
 void * TryInit_B003Fun()
@@ -112,6 +546,9 @@ void * TryInit_B003Fun()
 	hid_device * hd = hid_open( VID, PID, 0); // third parameter is "serial"
 	if( !hd ) return 0;
 
+	//extern int g_hidapiSuppress;
+	//g_hidapiSuppress = 1;  // Suppress errors for this device.  (don't do this yet)
+
 	struct B003FunProgrammerStruct * eps = malloc( sizeof( struct B003FunProgrammerStruct ) );
 	memset( eps, 0, sizeof( *eps ) );
 	eps->hd = hd;
@@ -133,18 +570,208 @@ void * TryInit_B003Fun()
 	MCF.WriteWord = B003FunWriteWord;
 	MCF.ReadWord = B003FunReadWord;
 
-	MCF.WaitForDoneOp = B003FunWaitForDoneOp;
+	MCF.WriteHalfWord = B003FunWriteHalfWord;
+	MCF.ReadHalfWord = B003FunReadHalfWord;
+
+	MCF.WriteByte = B003FunWriteByte;
+	MCF.ReadByte = B003FunReadByte;
 
+	MCF.WaitForDoneOp = B003FunWaitForDoneOp;
 	MCF.BlockWrite64 = B003FunBlockWrite64;
+	MCF.ReadBinaryBlob = B003FunReadBinaryBlob;
 
-	// Alert programmer.
-	ResetOp( eps )
-	WriteOp4( eps, 0x00b02023 );  //sw a1, 0       ; stop execution
-	WriteOp4( eps, 0x00008067 );  //jalr x0, x1, 0 ; ret
-	CommitOp( eps );
+	MCF.PrepForLongOp = B003FunPrepForLongOp;
+
+	MCF.HaltMode = B003FunHaltMode;
 
 	return eps;
 }
-#else
-void * TryInit_B003Fun() { return 0; }
-#endif
+
+
+
+
+
+// Utility for generating bootloader code:
+
+// make rv003usb.bin &&  xxd -i -s 100 -l 44 rv003usb.bin
+
+/*
+// Read data, arbitrarily from memory. (byte-wise)
+. =  0x66
+	sw x0, 0(a1);       // Stop Countdown
+	addi a4, a0, 52;    // Start reading properties, starting from scratchpad + 52.
+	c.lw a1, 0(a4);     // Get starting address to read
+	c.lw a2, 4(a4);     // Get length to read.
+	c.add a2, a1        // a2 is now ending address.
+	c.addi a4, 8		// start writing back at byte 60.
+1:
+	XW_C_LBU(a3, a1, 0);	//lbu a3, 0(a1)       // Read from RAM
+	XW_C_SB(a3, a4, 0);		//sb a3, 0(a4)       // Store into scratchpad
+	c.addi a1, 1        // Advance pointers
+	c.addi a4, 1
+	blt a1, a2, 1b      // Loop til all read.
+	addi a3, x0, -1
+	sw a3, 0(a0)		// Write -1 into 0x00 indicating all done.
+	ret
+	.long 0,0,0,0,0,0,0
+*/
+
+/*
+// Read data, arbitrarily from memory. (half-wise)
+
+. =  0x66
+	sw x0, 0(a1);       // Stop Countdown
+	addi a4, a0, 52;    // Start reading properties, starting from scratchpad + 52.
+	c.lw a1, 0(a4);     // Get starting address to read
+	c.lw a2, 4(a4);     // Get length to read.
+	c.add a2, a1        // a2 is now ending address.
+	c.addi a4, 8		// start writing back at byte 60.
+1:
+	XW_C_LHU(a3, a1, 0);	//lhu a3, 0(a1)       // Read from RAM
+	XW_C_SH(a3, a4, 0);		//sh a3, 0(a4)       // Store into scratchpad
+	c.addi a1, 2        // Advance pointers
+	c.addi a4, 2
+	blt a1, a2, 1b      // Loop til all read.
+	addi a3, x0, -1
+	sw a3, 0(a0)		// Write -1 into 0x00 indicating all done.
+	ret
+	.long 0,0,0,0,0,0,0
+*/
+
+/*
+// Read data, arbitrarily from memory. (word-wise)
+. =  0x66
+	sw x0, 0(a1);       // Stop Countdown
+	addi a4, a0, 52;    // Start reading properties, starting from scratchpad + 52.
+	c.lw a1, 0(a4);     // Get starting address to read
+	c.lw a2, 4(a4);     // Get length to read.
+	c.add a2, a1        // a2 is now ending address.
+	c.addi a4, 8		// start writing back at byte 60.
+1:
+	lw a3, 0(a1);		//lw a3, 0(a1)       // Read from RAM
+	sw a3, 0(a4);		//sw a3, 0(a4)       // Store into scratchpad
+	c.addi a1, 4        // Advance pointers
+	c.addi a4, 4
+	blt a1, a2, 1b      // Loop til all read.
+	addi a3, x0, -1
+	sw a3, 0(a0)		// Write -1 into 0x00 indicating all done.
+	ret
+	.long 0,0,0,0,0,0,0
+*/
+/*
+// Write data, arbitrarily to memory. (word-wise)
+. =  0x66
+	sw x0, 0(a1);       // Stop Countdown
+	addi a4, a0, 52;    // Start reading properties, starting from scratchpad + 52.
+	c.lw a1, 0(a4);     // Get starting address to read
+	c.lw a2, 4(a4);     // Get length to read.
+	c.add a2, a1        // a2 is now ending address.
+	c.addi a4, 8		// start writing back at byte 60.
+1:
+	lw a3, 0(a4);		//lw a3, 0(a1)       // Read from RAM
+	sw a3, 0(a1);		//sw a3, 0(a4)       // Store into scratchpad
+	lw a3, 0(a1);       // Read-back
+	sw a3, 0(a4);
+	c.addi a1, 4        // Advance pointers
+	c.addi a4, 4
+	blt a1, a2, 1b      // Loop til all read.
+	addi a3, x0, -1
+	sw a3, 0(a0)		// Write -1 into 0x00 indicating all done.
+	ret
+	.long 0,0,0,0,0,0
+*/
+
+/*
+// Write data, arbitrarily to memory. (word-wise)
+. =  0x66
+	sw x0, 0(a1);       // Stop Countdown
+	addi a4, a0, 52;    // Start reading properties, starting from scratchpad + 52.
+	c.lw a1, 0(a4);     // Get starting address to read
+	c.lw a2, 4(a4);     // Get length to read.
+	c.add a2, a1        // a2 is now ending address.
+	c.addi a4, 8		// start writing back at byte 60.
+1:
+	XW_C_LHU(a3, a4, 0);	//lbu a3, 0(a4)       // Read from scratchpad
+	XW_C_SH(a3, a1, 0);		//sb a3, 0(a1)       // Store into RAM
+	XW_C_LHU(a3, a1, 0);	//lbu a3, 0(a4)       //  Read back
+	XW_C_SH(a3, a4, 0);		//sb a3, 0(a1) 
+	c.addi a1, 2        // Advance pointers
+	c.addi a4, 2
+	blt a1, a2, 1b      // Loop til all read.
+	addi a3, x0, -1
+	sw a3, 0(a0)		// Write -1 into 0x00 indicating all done.
+	ret
+	.long 0,0,0,0,0,0
+*/
+
+/*
+// Write data, arbitrarily to memory. (byte-wise)
+. =  0x66
+	sw x0, 0(a1);       // Stop Countdown
+	addi a4, a0, 52;    // Start reading properties, starting from scratchpad + 52.
+	c.lw a1, 0(a4);     // Get starting address to read
+	c.lw a2, 4(a4);     // Get length to read.
+	c.add a2, a1        // a2 is now ending address.
+	c.addi a4, 8		// start writing back at byte 60.
+1:
+	XW_C_LBU(a3, a4, 0);	//lbu a3, 0(a4)       // Read from scratchpad
+	XW_C_SB(a3, a1, 0);		//sb a3, 0(a1)       // Store into RAM
+	XW_C_LBU(a3, a1, 0);	//Read back
+	XW_C_SB(a3, a4, 0);
+	c.addi a1, 1        // Advance pointers
+	c.addi a4, 1
+	blt a1, a2, 1b      // Loop til all read.
+	addi a3, x0, -1
+	sw a3, 0(a0)		// Write -1 into 0x00 indicating all done.
+	ret
+	.long 0,0,0,0,0,0
+*/
+
+
+/* Run app blob
+				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;
+*/
+
+
+/* Write flash block 64.
+
+. =  0x66
+	addi a4, a0, 52;    // Start reading properties, starting from scratchpad + 52.
+	c.lw a1, 0(a4);     // a1 = Address to write to.
+	addi a2, a1, 64     // a2 = end of section to write to
+	c.lw a5, 4(a4);     // a5 = Get flash address (0x40022010)
+
+	// Must be done outside.
+//	li a3, 0x00080000 | 0x00010000;
+	//c.sw a3, 0(a5);  //FLASH->CTLR = CR_BUF_RST | CR_PAGE_PG
+	c.sw a1, 8(a5);     //FLASH->ADDR = writing location.
+
+	1:
+		c.lw a3, 8(a4);		//lw a3, 0(a1)       // Read from RAM (Starting @60)
+		c.sw a3, 0(a1);		//sw a3, 0(a4)       // Store into flash
+
+		li a3, 0x00010000 | 0x00040000;	// CR_PAGE_PG | FLASH_CTLR_BUF_LOAD
+		c.sw a3, 4(a5);     // Load into flash write buffer.
+
+		c.lw a3, 0(a1);		//Tricky: By reading from flash here, we force it to wait for completion.
+		c.addi a1, 4        // Advance pointers
+		c.addi a4, 4
+
+	//	// Wait for write to complete.
+	//	2:	c.lw a3, 0(a5)   // read FLASH->STATR 
+	//		c.andi a3, 1     // Mask off BUSY bit.
+	//		c.bnez a3, 2b
+
+
+		blt a1, a2, 1b      // Loop til all read.
+
+	li a3, 0x00010000 | 0x00000040 //CR_PAGE_PG|CR_STRT_Set
+	c.sw a3, 4(a5);     //FLASH->CTRL = CR_PAGE_PG|CR_STRT_Set
+	li a3, -1
+	c.sw a3, 0(a0)		// Write -1 into 0x00 indicating all done.
+	ret
+*/
diff --git a/minichlink/pgm-esp32s2-ch32xx.c b/minichlink/pgm-esp32s2-ch32xx.c
index ef8a721e7c3cbfbaf335c47bb353c666c2156d39..090da555ff10f8cd2b1945d671a6e4861f9aaf9d 100644
--- a/minichlink/pgm-esp32s2-ch32xx.c
+++ b/minichlink/pgm-esp32s2-ch32xx.c
@@ -12,6 +12,8 @@ struct ESP32ProgrammerStruct
 	int commandplace;
 	uint8_t reply[256];
 	int replylen;
+
+	int dev_version;
 };
 
 int ESPFlushLLCommands( void * dev );
@@ -243,12 +245,18 @@ int ESPBlockWrite64( void * dev, uint32_t address_to_write, uint8_t * data )
 
 retry:
 
-	Write2LE( eps, 0x0bfe );
+	if( eps->dev_version >= 2 && InternalIsMemoryErased( (struct InternalState*)eps->internal, address_to_write ) )
+		Write2LE( eps, 0x0efe );
+	else
+		Write2LE( eps, 0x0bfe );
 	Write4LE( eps, address_to_write );
+
 	int i;
 	int timeout = 0;
 	for( i = 0; i < 64; i++ ) Write1( eps, data[i] );
 
+	InternalMarkMemoryNotErased( (struct InternalState*)eps->internal, address_to_write );
+
 	do
 	{
 		ESPFlushLLCommands( dev );
@@ -409,6 +417,7 @@ void * TryInit_ESP32S2CHFUN()
 	memset( eps, 0, sizeof( *eps ) );
 	eps->hd = hd;
 	eps->commandplace = 1;
+	eps->dev_version = 0;
 
 	memset( &MCF, 0, sizeof( MCF ) );
 	MCF.WriteReg32 = ESPWriteReg32;
@@ -431,9 +440,16 @@ void * TryInit_ESP32S2CHFUN()
 
 	MCF.BlockWrite64 = ESPBlockWrite64;
 	MCF.VendorCommand = ESPVendorCommand;
+
 	// Reset internal programmer state.
 	Write2LE( eps, 0x0afe );
-
+	ESPFlushLLCommands( eps );
+	Write2LE( eps, 0xfefe );
+	ESPFlushLLCommands( eps );
+	if( eps->replylen > 1 )
+	{
+		eps->dev_version = eps->reply[1];
+	}
 	return eps;
 }
 
diff --git a/minichlink/pgm-wch-linke.c b/minichlink/pgm-wch-linke.c
index 2a3e516906f7ca4593bda13fc0cbd99a82d303d6..e618135aab4d804281491a2a06fc15519ae052e9 100644
--- a/minichlink/pgm-wch-linke.c
+++ b/minichlink/pgm-wch-linke.c
@@ -18,8 +18,8 @@ struct LinkEProgrammerStruct
 
 // For non-ch32v003 chips.
 //static int LEReadBinaryBlob( void * d, uint32_t offset, uint32_t amount, uint8_t * readbuff );
-//static int InternalLinkEHaltMode( void * d, int mode );
-//static int LEWriteBinaryBlob( void * d, uint32_t address_to_write, uint32_t len, uint8_t * blob );
+static int InternalLinkEHaltMode( void * d, int mode );
+static int LEWriteBinaryBlob( void * d, uint32_t address_to_write, uint32_t len, uint8_t * blob );
 
 #define WCHTIMEOUT 5000
 #define WCHCHECK(x) if( (status = x) ) { fprintf( stderr, "Bad USB Operation on " __FILE__ ":%d (%d)\n", __LINE__, status ); exit( status ); }
@@ -191,6 +191,7 @@ int LEFlushLLCommands( void * dev )
 static int LESetupInterface( void * d )
 {
 	libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
+	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)d)->internal);
 	uint8_t rbuff[1024];
 	uint32_t transferred = 0;
 
@@ -223,6 +224,10 @@ static int LESetupInterface( void * d )
 	// TODO: What in the world is this?  It doesn't appear to be needed.
 	wch_link_command( dev, "\x81\x0c\x02\x09\x01", 5, 0, 0, 0 ); //Reply is: 820c0101
 
+	// Note from further debugging:
+	// My capture differs in this case: \x05 instead of \x09 -> But does not seem to be needed
+	//wch_link_command( dev, "\x81\x0c\x02\x05\x01", 5, 0, 0, 0 ); //Reply is: 820c0101
+
 	// This puts the processor on hold to allow the debugger to run.
 	wch_link_command( dev, "\x81\x0d\x01\x02", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Ignored, 820d050900300500
 	if (rbuff[0] == 0x81 && rbuff[1] == 0x55 && rbuff[2] == 0x01 && rbuff[3] == 0x01)
@@ -230,8 +235,18 @@ static int LESetupInterface( void * d )
 		fprintf(stderr, "link error, nothing connected to linker\n");
 		return -1;
 	}
-        uint32_t target_chip_type = ( rbuff[4] << 4) + (rbuff[5] >> 4);
-        fprintf(stderr, "Chip Type: %03x\n", target_chip_type);
+
+    uint32_t target_chip_type = ( rbuff[4] << 4) + (rbuff[5] >> 4);
+    fprintf(stderr, "Chip Type: %03x\n", target_chip_type);
+	if( target_chip_type == 0x307 || target_chip_type == 0x203 )
+	{
+		fprintf( stderr, "CH32V307 or CH32V203 Detected.  Allowing old-flash-mode for operation.\n" );
+		MCF.WriteBinaryBlob = LEWriteBinaryBlob;
+
+		iss->sector_size = 256;
+
+		wch_link_command( dev, "\x81\x0d\x01\x03", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Ignored, 820d050900300500
+	}
 
 	// For some reason, if we don't do this sometimes the programmer starts in a hosey mode.
 	MCF.WriteReg32( d, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
@@ -254,17 +269,36 @@ static int LESetupInterface( void * d )
 	}
 
 	// This puts the processor on hold to allow the debugger to run.
-	wch_link_command( dev, "\x81\x11\x01\x09", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Chip ID + Other data (see below)
+	// Recommended to switch to 05 from 09 by Alexander M
+	//	wch_link_command( dev, "\x81\x11\x01\x09", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Chip ID + Other data (see below)
+	wch_link_command( dev, "\x81\x11\x01\x05", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Chip ID + Other data (see below)
+
 	if( transferred != 20 )
 	{
 		fprintf( stderr, "Error: could not get part status\n" );
 		return -1;
 	}
-	fprintf( stderr, "Part Type (A): 0x%02x%02x (This is the capacity code, in KB)\n", rbuff[2], rbuff[3] );  // Is this Flash size?
+	fprintf( stderr, "Flash Storage: %d kB\n", (rbuff[2]<<8) | rbuff[3] );  // Is this Flash size?
 	fprintf( stderr, "Part UUID    : %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", rbuff[4], rbuff[5], rbuff[6], rbuff[7], rbuff[8], rbuff[9], rbuff[10], rbuff[11] );
 	fprintf( stderr, "PFlags       : %02x-%02x-%02x-%02x\n", rbuff[12], rbuff[13], rbuff[14], rbuff[15] );
 	fprintf( stderr, "Part Type (B): %02x-%02x-%02x-%02x\n", rbuff[16], rbuff[17], rbuff[18], rbuff[19] );
-	
+
+	// Check for read protection
+	wch_link_command( dev, "\x81\x06\x01\x01", 4, (int*)&transferred, rbuff, 1024 );
+	if(transferred != 4) {
+		fprintf(stderr, "Error: could not get read protection status\n");
+		return -1;
+	}
+
+	if(rbuff[3] == 0x01) {
+		fprintf(stderr, "Read protection: enabled\n");
+	} else {
+		fprintf(stderr, "Read protection: disabled\n");
+	}
+
+	iss->flash_size = ((rbuff[2]<<8) | rbuff[3])*1024;
+	iss->target_chip_type = target_chip_type;
+
 	return 0;
 }
 
@@ -365,49 +399,47 @@ void * TryInit_WCHLinkE()
 };
 
 
-#if 0
+#if 1
 
 // In case you are using a non-CH32V003 board.
 
-
 const uint8_t * bootloader = (const uint8_t*)
-"\x21\x11\x22\xca\x26\xc8\x93\x77\x15\x00\x99\xcf\xb7\x06\x67\x45" \
-"\xb7\x27\x02\x40\x93\x86\x36\x12\x37\x97\xef\xcd\xd4\xc3\x13\x07" \
-"\xb7\x9a\xd8\xc3\xd4\xd3\xd8\xd3\x93\x77\x25\x00\x9d\xc7\xb7\x27" \
-"\x02\x40\x98\x4b\xad\x66\x37\x33\x00\x40\x13\x67\x47\x00\x98\xcb" \
-"\x98\x4b\x93\x86\xa6\xaa\x13\x67\x07\x04\x98\xcb\xd8\x47\x05\x8b" \
-"\x63\x16\x07\x10\x98\x4b\x6d\x9b\x98\xcb\x93\x77\x45\x00\xa9\xcb" \
-"\x93\x07\xf6\x03\x99\x83\x2e\xc0\x2d\x63\x81\x76\x3e\xc4\xb7\x32" \
-"\x00\x40\xb7\x27\x02\x40\x13\x03\xa3\xaa\xfd\x16\x98\x4b\xb7\x03" \
-"\x02\x00\x33\x67\x77\x00\x98\xcb\x02\x47\xd8\xcb\x98\x4b\x13\x67" \
-"\x07\x04\x98\xcb\xd8\x47\x05\x8b\x69\xe7\x98\x4b\x75\x8f\x98\xcb" \
-"\x02\x47\x13\x07\x07\x04\x3a\xc0\x22\x47\x7d\x17\x3a\xc4\x79\xf7" \
-"\x93\x77\x85\x00\xf1\xcf\x93\x07\xf6\x03\x2e\xc0\x99\x83\x37\x27" \
-"\x02\x40\x3e\xc4\x1c\x4b\xc1\x66\x2d\x63\xd5\x8f\x1c\xcb\x37\x07" \
-"\x00\x20\x13\x07\x07\x20\xb7\x27\x02\x40\xb7\x03\x08\x00\xb7\x32" \
-"\x00\x40\x13\x03\xa3\xaa\x94\x4b\xb3\xe6\x76\x00\x94\xcb\xd4\x47" \
-"\x85\x8a\xf5\xfe\x82\x46\xba\x84\x37\x04\x04\x00\x36\xc2\xc1\x46" \
-"\x36\xc6\x92\x46\x84\x40\x11\x07\x84\xc2\x94\x4b\xc1\x8e\x94\xcb" \
-"\xd4\x47\x85\x8a\xb1\xea\x92\x46\xba\x84\x91\x06\x36\xc2\xb2\x46" \
-"\xfd\x16\x36\xc6\xf9\xfe\x82\x46\xd4\xcb\x94\x4b\x93\xe6\x06\x04" \
-"\x94\xcb\xd4\x47\x85\x8a\x85\xee\xd4\x47\xc1\x8a\x85\xce\xd8\x47" \
-"\xb7\x06\xf3\xff\xfd\x16\x13\x67\x07\x01\xd8\xc7\x98\x4b\x21\x45" \
-"\x75\x8f\x98\xcb\x52\x44\xc2\x44\x61\x01\x02\x90\x23\x20\xd3\x00" \
-"\xf5\xb5\x23\xa0\x62\x00\x3d\xb7\x23\xa0\x62\x00\x55\xb7\x23\xa0" \
-"\x62\x00\xc1\xb7\x82\x46\x93\x86\x06\x04\x36\xc0\xa2\x46\xfd\x16" \
-"\x36\xc4\xb5\xf2\x98\x4b\xb7\x06\xf3\xff\xfd\x16\x75\x8f\x98\xcb" \
-"\x41\x89\x05\xcd\x2e\xc0\x0d\x06\x02\xc4\x09\x82\xb7\x07\x00\x20" \
-"\x32\xc6\x93\x87\x07\x20\x98\x43\x13\x86\x47\x00\xa2\x47\x82\x46" \
-"\x8a\x07\xb6\x97\x9c\x43\x63\x1c\xf7\x00\xa2\x47\x85\x07\x3e\xc4" \
-"\xa2\x46\x32\x47\xb2\x87\xe3\xe0\xe6\xfe\x01\x45\x61\xb7\x41\x45" \
-"\x51\xb7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+"\x93\x77\x15\x00\x41\x11\x99\xcf\xb7\x06\x67\x45\xb7\x27\x02\x40" \
+"\x93\x86\x36\x12\x37\x97\xef\xcd\xd4\xc3\x13\x07\xb7\x9a\xd8\xc3" \
+"\xd4\xd3\xd8\xd3\x93\x77\x25\x00\x95\xc7\xb7\x27\x02\x40\x98\x4b" \
+"\xad\x66\x37\x38\x00\x40\x13\x67\x47\x00\x98\xcb\x98\x4b\x93\x86" \
+"\xa6\xaa\x13\x67\x07\x04\x98\xcb\xd8\x47\x05\x8b\x61\xeb\x98\x4b" \
+"\x6d\x9b\x98\xcb\x93\x77\x45\x00\xa9\xcb\x93\x07\xf6\x0f\xa1\x83" \
+"\x2e\xc0\x2d\x68\x81\x76\x3e\xc4\xb7\x08\x02\x00\xb7\x27\x02\x40" \
+"\x37\x33\x00\x40\x13\x08\xa8\xaa\xfd\x16\x98\x4b\x33\x67\x17\x01" \
+"\x98\xcb\x02\x47\xd8\xcb\x98\x4b\x13\x67\x07\x04\x98\xcb\xd8\x47" \
+"\x05\x8b\x41\xeb\x98\x4b\x75\x8f\x98\xcb\x02\x47\x13\x07\x07\x10" \
+"\x3a\xc0\x22\x47\x7d\x17\x3a\xc4\x69\xfb\x93\x77\x85\x00\xd5\xcb" \
+"\x93\x07\xf6\x0f\x2e\xc0\xa1\x83\x3e\xc4\x37\x27\x02\x40\x1c\x4b" \
+"\xc1\x66\x41\x68\xd5\x8f\x1c\xcb\xb7\x16\x00\x20\xb7\x27\x02\x40" \
+"\x93\x08\x00\x04\x37\x03\x20\x00\x98\x4b\x33\x67\x07\x01\x98\xcb" \
+"\xd8\x47\x05\x8b\x75\xff\x02\x47\x3a\xc2\x46\xc6\x32\x47\x0d\xef" \
+"\x98\x4b\x33\x67\x67\x00\x98\xcb\xd8\x47\x05\x8b\x75\xff\xd8\x47" \
+"\x41\x8b\x39\xc3\xd8\x47\xc1\x76\xfd\x16\x13\x67\x07\x01\xd8\xc7" \
+"\x98\x4b\x21\x45\x75\x8f\x98\xcb\x41\x01\x02\x90\x23\x20\xd8\x00" \
+"\x25\xb7\x23\x20\x03\x01\xa5\xb7\x12\x47\x13\x8e\x46\x00\x94\x42" \
+"\x14\xc3\x12\x47\x11\x07\x3a\xc2\x32\x47\x7d\x17\x3a\xc6\xd8\x47" \
+"\x09\x8b\x75\xff\xf2\x86\x5d\xb7\x02\x47\x13\x07\x07\x10\x3a\xc0" \
+"\x22\x47\x7d\x17\x3a\xc4\x49\xf3\x98\x4b\xc1\x76\xfd\x16\x75\x8f" \
+"\x98\xcb\x41\x89\x15\xc9\x2e\xc0\x0d\x06\x02\xc4\x09\x82\x32\xc6" \
+"\xb7\x17\x00\x20\x98\x43\x13\x86\x47\x00\xa2\x47\x82\x46\x8a\x07" \
+"\xb6\x97\x9c\x43\x63\x1c\xf7\x00\xa2\x47\x85\x07\x3e\xc4\xa2\x46" \
+"\x32\x47\xb2\x87\xe3\xe0\xe6\xfe\x01\x45\xbd\xbf\x41\x45\xad\xbf" \
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
 
 int bootloader_len = 512;
 #endif
 
-#if 0
 static int InternalLinkEHaltMode( void * d, int mode )
 {
 	libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
@@ -432,7 +464,6 @@ static int InternalLinkEHaltMode( void * d, int mode )
 	}
 	return 0;
 }
-#endif
 
 #if 0
 static int LEReadBinaryBlob( void * d, uint32_t offset, uint32_t amount, uint8_t * readbuff )
@@ -494,10 +525,10 @@ static int LEReadBinaryBlob( void * d, uint32_t offset, uint32_t amount, uint8_t
 }
 #endif
 
-#if 0
 static int LEWriteBinaryBlob( void * d, uint32_t address_to_write, uint32_t len, uint8_t * blob )
 {
 	libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
+	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
 
 	InternalLinkEHaltMode( d, 0 );
 
@@ -506,7 +537,7 @@ static int LEWriteBinaryBlob( void * d, uint32_t address_to_write, uint32_t len,
 	uint8_t rbuff[1024];
 	int transferred;
 
-	int padlen = ((len-1) & (~0x3f)) + 0x40;
+	int padlen = ((len-1) & (~(iss->sector_size-1))) + iss->sector_size;
 
 	wch_link_command( (libusb_device_handle *)dev, "\x81\x06\x01\x01", 4, 0, 0, 0 );
 	wch_link_command( (libusb_device_handle *)dev, "\x81\x06\x01\x01", 4, 0, 0, 0 ); // Not sure why but it seems to work better when we request twice.
@@ -521,9 +552,9 @@ static int LEWriteBinaryBlob( void * d, uint32_t address_to_write, uint32_t len,
 	wch_link_command( (libusb_device_handle *)dev, "\x81\x02\x01\x05", 4, 0, 0, 0 );
 	
 	int pplace = 0;
-	for( pplace = 0; pplace < bootloader_len; pplace += 64 )
+	for( pplace = 0; pplace < bootloader_len; pplace += iss->sector_size )
 	{
-		WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, (uint8_t*)(bootloader+pplace), 64, &transferred, WCHTIMEOUT ) );
+		WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, (uint8_t*)(bootloader+pplace), iss->sector_size, &transferred, WCHTIMEOUT ) );
 	}
 	
 	for( i = 0; i < 10; i++ )
@@ -542,24 +573,23 @@ static int LEWriteBinaryBlob( void * d, uint32_t address_to_write, uint32_t len,
 	
 	wch_link_command( (libusb_device_handle *)dev, "\x81\x02\x01\x02", 4, 0, 0, 0 );
 
-	for( pplace = 0; pplace < padlen; pplace += 64 )
+	for( pplace = 0; pplace < padlen; pplace += iss->sector_size )
 	{
-		if( pplace + 64 > len )
+		if( pplace + iss->sector_size > len )
 		{
-			uint8_t paddeddata[64];
-			int gap = pplace + 64 - len;
+			uint8_t paddeddata[iss->sector_size];
+			int gap = pplace + iss->sector_size - len;
 			int okcopy = len - pplace;
 			memcpy( paddeddata, blob + pplace, okcopy );
 			memset( paddeddata + okcopy, 0xff, gap );
-			WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, paddeddata, 64, &transferred, WCHTIMEOUT ) );
+			WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, paddeddata, iss->sector_size, &transferred, WCHTIMEOUT ) );
 		}
 		else
 		{
-			WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, blob+pplace, 64, &transferred, WCHTIMEOUT ) );
+			WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, blob+pplace, iss->sector_size, &transferred, WCHTIMEOUT ) );
 		}
 	}
 	return 0;
 }
 
 
-#endif
diff --git a/minichlink/serial_dev.c b/minichlink/serial_dev.c
index f39780fa580f833a47a6ecb8ee756957937637fc..af5543118357662082d8b7b257322bdd2ff54355 100644
--- a/minichlink/serial_dev.c
+++ b/minichlink/serial_dev.c
@@ -1,173 +1,173 @@
 #include "serial_dev.h"
 
 int serial_dev_create(serial_dev_t *dev, const char* port, unsigned baud) {
-    if (!dev) 
-        return -1;
-    dev->port = port;
-    dev->baud = baud;
-    #ifdef IS_WINDOWS
-    dev->handle = INVALID_HANDLE_VALUE;
-    #else
-    dev->fd = -1;
-    #endif
-    return 0;
+	if (!dev) 
+		return -1;
+	dev->port = port;
+	dev->baud = baud;
+	#ifdef IS_WINDOWS
+	dev->handle = INVALID_HANDLE_VALUE;
+	#else
+	dev->fd = -1;
+	#endif
+	return 0;
 }
 
 int serial_dev_open(serial_dev_t *dev) {
-    fprintf(stderr, "Opening serial port %s at %u baud.\n", dev->port, dev->baud);
+	fprintf(stderr, "Opening serial port %s at %u baud.\n", dev->port, dev->baud);
 #ifdef IS_WINDOWS
-    // Windows quirk: port = "COM10" is invalid, has to be encoded as "\\.\COM10".
-    // This also works for COM below 9. So, let's give the user the ability to use
-    // any "COMx" string and just prepend the "\\.\".
-    char winPortName[64];
-    if(dev->port[0] != '\\') {
-        snprintf(winPortName, sizeof(winPortName), "\\\\.\\%s", dev->port);
-    } else {
-        // copy verbatim if string already starts with a '\'
-        snprintf(winPortName, sizeof(winPortName), "%s", dev->port);
-    }
-    dev->handle = CreateFileA(winPortName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0,0);
-    if (dev->handle == INVALID_HANDLE_VALUE) {
-        if (GetLastError() == ERROR_FILE_NOT_FOUND) {
-            fprintf(stderr, "Serial port %s not found.\n", dev->port);
-            // weird: without this, errno = 0 (no error).
-            _set_errno(ERROR_FILE_NOT_FOUND);
-            return -1; // Device not found
-        }
-        // Error while opening the device
-        return -1;
-    }
-    DCB dcbSerialParams;
-    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
-    if (!GetCommState(dev->handle, &dcbSerialParams)) {
-        return -1;
-    }
-    // set baud and 8N1 serial formatting
-    dcbSerialParams.BaudRate = dev->baud;
-    dcbSerialParams.ByteSize = 8;
-    dcbSerialParams.StopBits = ONESTOPBIT;
-    dcbSerialParams.Parity = NOPARITY;
-    // write back
-    if (!SetCommState(dev->handle, &dcbSerialParams)){ 
-        return -1;
-    }
-    // Set the timeout parameters to "no timeout" (blocking).
-    // see https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-commtimeouts
-    COMMTIMEOUTS timeouts;
-    timeouts.ReadIntervalTimeout = 0;
-    timeouts.ReadTotalTimeoutConstant = MAXDWORD;
-    timeouts.ReadTotalTimeoutMultiplier = 0;
-    timeouts.WriteTotalTimeoutConstant = MAXDWORD;
-    timeouts.WriteTotalTimeoutMultiplier = 0;
-    // Write the parameters
-    if (!SetCommTimeouts(dev->handle, &timeouts)) {
-        return -1;
-    }
+	// Windows quirk: port = "COM10" is invalid, has to be encoded as "\\.\COM10".
+	// This also works for COM below 9. So, let's give the user the ability to use
+	// any "COMx" string and just prepend the "\\.\".
+	char winPortName[64];
+	if(dev->port[0] != '\\') {
+		snprintf(winPortName, sizeof(winPortName), "\\\\.\\%s", dev->port);
+	} else {
+		// copy verbatim if string already starts with a '\'
+		snprintf(winPortName, sizeof(winPortName), "%s", dev->port);
+	}
+	dev->handle = CreateFileA(winPortName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0,0);
+	if (dev->handle == INVALID_HANDLE_VALUE) {
+		if (GetLastError() == ERROR_FILE_NOT_FOUND) {
+			fprintf(stderr, "Serial port %s not found.\n", dev->port);
+			// weird: without this, errno = 0 (no error).
+			_set_errno(ERROR_FILE_NOT_FOUND);
+			return -1; // Device not found
+		}
+		// Error while opening the device
+		return -1;
+	}
+	DCB dcbSerialParams;
+	dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
+	if (!GetCommState(dev->handle, &dcbSerialParams)) {
+		return -1;
+	}
+	// set baud and 8N1 serial formatting
+	dcbSerialParams.BaudRate = dev->baud;
+	dcbSerialParams.ByteSize = 8;
+	dcbSerialParams.StopBits = ONESTOPBIT;
+	dcbSerialParams.Parity = NOPARITY;
+	// write back
+	if (!SetCommState(dev->handle, &dcbSerialParams)){ 
+		return -1;
+	}
+	// Set the timeout parameters to "no timeout" (blocking).
+	// see https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-commtimeouts
+	COMMTIMEOUTS timeouts;
+	timeouts.ReadIntervalTimeout = 0;
+	timeouts.ReadTotalTimeoutConstant = MAXDWORD;
+	timeouts.ReadTotalTimeoutMultiplier = 0;
+	timeouts.WriteTotalTimeoutConstant = MAXDWORD;
+	timeouts.WriteTotalTimeoutMultiplier = 0;
+	// Write the parameters
+	if (!SetCommTimeouts(dev->handle, &timeouts)) {
+		return -1;
+	}
 #else
-    struct termios attr;
-    if ((dev->fd = open(dev->port, O_RDWR | O_NOCTTY)) == -1) {
-        perror("open");
-        return -1;
-    }
+	struct termios attr;
+	if ((dev->fd = open(dev->port, O_RDWR | O_NOCTTY)) == -1) {
+		perror("open");
+		return -1;
+	}
 
-    if (tcgetattr(dev->fd, &attr) == -1) {
-        perror("tcgetattr");
-        return -1;
-    }
+	if (tcgetattr(dev->fd, &attr) == -1) {
+		perror("tcgetattr");
+		return -1;
+	}
 
-    cfmakeraw(&attr);
-    cfsetspeed(&attr, dev->baud);
+	cfmakeraw(&attr);
+	cfsetspeed(&attr, dev->baud);
 
-    if (tcsetattr(dev->fd, TCSANOW, &attr) == -1) {
-        perror("tcsetattr");
-        return -1;
-    }
+	if (tcsetattr(dev->fd, TCSANOW, &attr) == -1) {
+		perror("tcsetattr");
+		return -1;
+	}
 #endif
-    // all okay if we get here
-    return 0;
+	// all okay if we get here
+	return 0;
 }
 
 int serial_dev_write(serial_dev_t *dev, const void* data, size_t len) {
 #ifdef IS_WINDOWS
-    DWORD dwBytesWritten;
-    if (!WriteFile(dev->handle, data, len, &dwBytesWritten,NULL)) {
-        return -1;
-    }
-    return (int) dwBytesWritten;
+	DWORD dwBytesWritten;
+	if (!WriteFile(dev->handle, data, len, &dwBytesWritten,NULL)) {
+		return -1;
+	}
+	return (int) dwBytesWritten;
 #else
-    return write(dev->fd, data, len);
+	return write(dev->fd, data, len);
 #endif
 }
 
 int serial_dev_read(serial_dev_t *dev, void* data, size_t len) {
 #ifdef IS_WINDOWS
-    DWORD dwBytesRead = 0;
-    if (!ReadFile(dev->handle, data, len, &dwBytesRead, NULL)) {
-        return -1;
-    }
-    return (int) dwBytesRead;
+	DWORD dwBytesRead = 0;
+	if (!ReadFile(dev->handle, data, len, &dwBytesRead, NULL)) {
+		return -1;
+	}
+	return (int) dwBytesRead;
 #else
-    return read(dev->fd, data, len);
+	return read(dev->fd, data, len);
 #endif
 }
 
 int serial_dev_do_dtr_reset(serial_dev_t *dev) {
 #ifdef IS_WINDOWS
-    // EscapeCommFunction returns 0 on fail
-    if(EscapeCommFunction(dev->handle, SETDTR) == 0) {
-        return -1;
-    }
-    if(EscapeCommFunction(dev->handle, CLRDTR) == 0) {
-        return -1;
-    }
+	// EscapeCommFunction returns 0 on fail
+	if(EscapeCommFunction(dev->handle, SETDTR) == 0) {
+		return -1;
+	}
+	if(EscapeCommFunction(dev->handle, CLRDTR) == 0) {
+		return -1;
+	}
 #else
-    int argp = TIOCM_DTR;
-    // Arduino DTR reset.
-    if (ioctl(dev->fd, TIOCMBIC, &argp) == -1) {
-        perror("ioctl");
-        return -1;
-    }
+	int argp = TIOCM_DTR;
+	// Arduino DTR reset.
+	if (ioctl(dev->fd, TIOCMBIC, &argp) == -1) {
+		perror("ioctl");
+		return -1;
+	}
 
-    if (tcdrain(dev->fd) == -1) {
-        perror("tcdrain");
-        return -1;
-    }
+	if (tcdrain(dev->fd) == -1) {
+		perror("tcdrain");
+		return -1;
+	}
 
-    if (ioctl(dev->fd, TIOCMBIS, &argp) == -1) {
-        perror("ioctl");
-        return -1;
-    }
+	if (ioctl(dev->fd, TIOCMBIS, &argp) == -1) {
+		perror("ioctl");
+		return -1;
+	}
 #endif
-    return 0;
+	return 0;
 }
 
 int serial_dev_flush_rx(serial_dev_t *dev) {
 #ifdef IS_WINDOWS
-    // PurgeComm returns 0 on fail
-    if (PurgeComm(dev->handle, PURGE_RXCLEAR) == 0) {
-        return -1;
-    }
+	// PurgeComm returns 0 on fail
+	if (PurgeComm(dev->handle, PURGE_RXCLEAR) == 0) {
+		return -1;
+	}
 #else
-    if (tcflush(dev->fd, TCIFLUSH) == -1) {
-        perror("tcflush");
-        return -1;
-    }
+	if (tcflush(dev->fd, TCIFLUSH) == -1) {
+		perror("tcflush");
+		return -1;
+	}
 #endif
-    return 0;
+	return 0;
 }
 
 int serial_dev_close(serial_dev_t *dev) {
 #ifdef IS_WINDOWS
-    if(!CloseHandle(dev->handle)) {
-        return -1;
-    }
-    dev->handle = INVALID_HANDLE_VALUE;
+	if(!CloseHandle(dev->handle)) {
+		return -1;
+	}
+	dev->handle = INVALID_HANDLE_VALUE;
 #else
-    int ret = 0;
-    if((ret = close(dev->fd)) != 0) {
-        return ret;
-    }
-    dev->fd = -1;
+	int ret = 0;
+	if((ret = close(dev->fd)) != 0) {
+		return ret;
+	}
+	dev->fd = -1;
 #endif
-    return 0;
+	return 0;
 }
\ No newline at end of file
diff --git a/minichlink/serial_dev.h b/minichlink/serial_dev.h
index a75733f41870308408ce0b2f5b82d20ef7ccfe54..7c7e4f1eea1f1b369b08f0508739112a84c60ca5 100644
--- a/minichlink/serial_dev.h
+++ b/minichlink/serial_dev.h
@@ -9,6 +9,7 @@
 #define IS_WINDOWS
 #define DEFAULT_SERIAL_NAME "\\\\.\\COM3"
 #else
+#include <unistd.h>
 #include <termios.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
@@ -17,7 +18,6 @@
 #endif
 /* these are available on all platforms */
 #include <errno.h>
-#include <unistd.h>
 #include <stdio.h>
 
 typedef struct {
diff --git a/minichlink/winbuild.bat b/minichlink/winbuild.bat
index 785472441f6d5d1db88cd6282afe61fc9f21a39b..9c4095bc4ce65dcea556e50f2dc1a094cc04f303 100644
--- a/minichlink/winbuild.bat
+++ b/minichlink/winbuild.bat
@@ -1 +1 @@
-tcc minichlink.c pgm-esp32s2-ch32xx.c  pgm-wch-linke.c minichgdb.c nhc-link042.c -DWIN32 -lws2_32 -lsetupapi libusb-1.0.dll 
+tcc minichlink.c pgm-esp32s2-ch32xx.c serial_dev.c ardulink.c pgm-b003fun.c pgm-wch-linke.c minichgdb.c nhc-link042.c -DWIN32 -lws2_32 -lsetupapi libusb-1.0.dll