diff --git a/README.md b/README.md
index 95d0ace963f9abe23b676beee54473ed2a37da40..0b66a4cbdc0013a89bf11c8b607de4e762c0fca1 100644
--- a/README.md
+++ b/README.md
@@ -162,3 +162,4 @@ You can open a github ticket or join my Discord in the #ch32v003fun channel. htt
  * http://www.wch-ic.com/downloads/CH32V003RM_PDF.html Technical Reference Manual
  * http://www.wch-ic.com/downloads/CH32V003DS0_PDF.html Datasheet
  * https://github.com/CaiB/CH32V003-Architecture-Exploration/blob/main/InstructionTypes.md Details for the compressed ISA on this chip.
+ * The CH32V003 has xv extensions, you can use this customized version of rvcodec.js to work with its opcodes: https://xw.macyler.moe/
diff --git a/ch32v003fun/xw_ext.inc b/ch32v003fun/xw_ext.inc
new file mode 100644
index 0000000000000000000000000000000000000000..ca210702fc69d9d31b87d9f69ddaf004c17e0b0c
--- /dev/null
+++ b/ch32v003fun/xw_ext.inc
@@ -0,0 +1,57 @@
+/*
+Encoder for some of the proprietary 'XW' RISC-V instructions present on the QingKe RV32 processor.
+Examples:
+	XW_C_LBU(a3, a1, 27); // c.xw.lbu a3, 27(a1)
+	XW_C_SB(a0, s0, 13);  // c.xw.sb a0, 13(s0)
+
+	XW_C_LHU(a5, a5, 38); // c.xw.lhu a5, 38(a5)
+	XW_C_SH(a2, s1, 14);  // c.xw.sh a2, 14(s1)
+*/
+
+// Let us do some compile-time error checking.
+#define ASM_ASSERT(COND) .if (!(COND)); .err; .endif
+
+// Integer encodings of the possible compressed registers.
+#define C_s0 0
+#define C_s1 1
+#define C_a0 2
+#define C_a1 3
+#define C_a2 4
+#define C_a3 5
+#define C_a4 6
+#define C_a5 7
+
+// register to encoding
+#define REG2I(X) (C_ ## X)
+
+// XW opcodes
+#define XW_OP_LBUSP 0b1000000000000000
+#define XW_OP_STSP  0b1000000001000000
+
+#define XW_OP_LHUSP 0b1000000000100000
+#define XW_OP_SHSP  0b1000000001100000
+
+#define XW_OP_LBU   0b0010000000000000
+#define XW_OP_SB    0b1010000000000000
+
+#define XW_OP_LHU   0b0010000000000010
+#define XW_OP_SH    0b1010000000000010
+
+// The two different XW encodings supported at the moment.
+#define XW_ENCODE1(OP, R1, R2, IMM) ASM_ASSERT((IMM) >= 0 && (IMM) < 32); .2byte ((OP) | (REG2I(R1) << 2) | (REG2I(R2) << 7) | \
+	(((IMM) & 0b1) << 12) | (((IMM) & 0b110) << (5 - 1)) | (((IMM) & 0b11000) << (10 - 3)))
+
+#define XW_ENCODE2(OP, R1, R2, IMM) ASM_ASSERT((IMM) >= 0 && (IMM) < 32); .2byte ((OP) | (REG2I(R1) << 2) | (REG2I(R2) << 7) | \
+	(((IMM) & 0b11) << 5) | (((IMM) & 0b11100) << (10 - 2))
+
+// Compressed load byte, zero-extend result
+#define XW_C_LBU(RD, RS, IMM) XW_ENCODE1(XW_OP_LBU, RD, RS, IMM)
+
+// Compressed store byte
+#define XW_C_SB(RS1, RS2, IMM) XW_ENCODE1(XW_OP_SB, RS1, RS2, IMM)
+
+// Compressed load half, zero-extend result
+#define XW_C_LHU(RD, RS, IMM) ASM_ASSERT(((IMM) & 1) == 0); XW_ENCODE2(XW_OP_LHU, RD, RS, ((IMM) >> 1)))
+
+// Compressed store half
+#define XW_C_SH(RS1, RS2, IMM)  ASM_ASSERT(((IMM) & 1) == 0); XW_ENCODE2(XW_OP_SH, RS1, RS2, ((IMM) >> 1)))
diff --git a/examples/optiondata/Makefile b/examples/optiondata/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..5ad2b8e5ed62673531a142e2a44fa1e7ebf78b08
--- /dev/null
+++ b/examples/optiondata/Makefile
@@ -0,0 +1,8 @@
+all : flash
+
+TARGET:=optiondata
+
+include ../../ch32v003fun/ch32v003fun.mk
+
+flash : cv_flash
+clean : cv_clean
diff --git a/examples/optiondata/README.md b/examples/optiondata/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..840aee6057895eb3696b9e18407e37190a9f87cf
--- /dev/null
+++ b/examples/optiondata/README.md
@@ -0,0 +1,11 @@
+# Write to the user fields in the Option Data flash
+
+This is a simple example code for writing to the two 8-bit data0/data1 bytes that are a part of the Flash "User-selected words" (aka Option bytes).
+
+To reduce the code footpint no timeouts are implemented in the code. It simply waits for the busy-flag to be cleaed. The two wait-loops could be replaced with a Delay_Ms(10) if speed is not an issue.
+
+The two bytes could be used for remembering user settings between power-offs.
+
+After flashing you can test it with the command
+`../../minichlink/minichlink -a -b -T` 
+that will Halt, Reboot and enter Terminal mode. For each run the count will increment by one.
diff --git a/examples/optiondata/optiondata.c b/examples/optiondata/optiondata.c
new file mode 100644
index 0000000000000000000000000000000000000000..a3c8f273c0a07d9416ff30963cacea91651b6b17
--- /dev/null
+++ b/examples/optiondata/optiondata.c
@@ -0,0 +1,74 @@
+//
+// Example code for writing to the two 8-bit data0/data1 bytes that are
+// a part of the Flash "User-selected words" (aka Option bytes).
+// To reduce the code footpint no timeouts are implemented in the code. It simply
+// waits for the busy-flag to be cleaed. The two wait-loops could be replaced 
+// with a Delay_Ms(10) if speed is not an issue.
+//
+// June 13, 2023 Mats Engstrom (github.com/mengstr)
+//
+
+#define SYSTEM_CORE_CLOCK 48000000
+
+#include "ch32v003fun.h"
+#include <stdio.h>
+void FlashOptionData(uint8_t data0, uint8_t data1) {
+	volatile uint16_t hold[6]; 		// array to hold current values while erasing
+
+	// The entire 64 byte data block of the "User-selected words" will be erased
+	// so we need to keep a copy of the content for re-writing after erase.
+	// Save a few (20) bytes code space by moving 32 bits at a time.
+	// 			hold[0]=OB->RDPR;
+	// 			hold[1]=OB->USER;
+	// 			hold[2]=data0;
+	// 			hold[3]=data1;
+	// 			hold[4]=OB->WRPR0;
+	// 			hold[5]=OB->WRPR1;
+	uint32_t *hold32p=(uint32_t *)hold;
+	uint32_t *ob32p=(uint32_t *)OB_BASE;
+	hold32p[0]=ob32p[0]; 			// Copy RDPR and USER
+	hold32p[1]=data0+(data1<<16);	// Copy in the two Data values to be written
+	hold32p[2]=ob32p[2];			// Copy WRPR0 and WEPR1
+
+	// Unlock both the general Flash and the User-selected words
+	FLASH->KEYR = FLASH_KEY1;
+	FLASH->KEYR = FLASH_KEY2;
+	FLASH->OBKEYR = FLASH_KEY1;
+	FLASH->OBKEYR = FLASH_KEY2;
+
+	FLASH->CTLR |= CR_OPTER_Set;			// OBER RW Perform user-selected word erasure	
+	FLASH->CTLR |= CR_STRT_Set;    			// STRT RW1 Start. Set 1 to start an erase action,hw automatically clears to 0
+	while (FLASH->STATR & FLASH_BUSY);		// Wait for flash operation to be done
+	FLASH->CTLR &= CR_OPTER_Reset; 			// Disable erasure mode	
+
+	// Write the held values back one-by-one
+	FLASH->CTLR |= CR_OPTPG_Set;   			// OBG  RW Perform user-selected word programming
+	uint16_t *ob16p=(uint16_t *)OB_BASE;
+	for (int i=0;i<sizeof(hold)/sizeof(hold[0]); i++) {
+		ob16p[i]=hold[i];
+		while (FLASH->STATR & FLASH_BUSY);	// Wait for flash operation to be done
+	}
+	FLASH->CTLR &= CR_OPTPG_Reset;			// Disable programming mode
+
+	FLASH->CTLR|=CR_LOCK_Set;				// Lock flash memories again
+
+	return;
+}
+
+
+
+//
+//
+//
+int main() {
+	SystemInit48HSI();
+	SetupDebugPrintf();
+	Delay_Ms(250);
+	
+	uint8_t bootcnt=OB->Data0;
+	bootcnt++;
+	FlashOptionData(bootcnt,0);
+	printf("Boot count is %d\n",bootcnt);
+
+	for(;;);
+}
diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c
index 35b5a4720a6b9e1f88c9eaeb8bc3a3dc77eba821..4444013cc9d0f7f76679bf39f09db98d6eec1510 100644
--- a/minichlink/minichlink.c
+++ b/minichlink/minichlink.c
@@ -13,6 +13,7 @@
 
 #if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
 #define DISABLE_ARDULINK
+void Sleep(uint32_t dwMilliseconds);
 #else
 #include <unistd.h>
 #endif
@@ -201,6 +202,20 @@ keep_going:
 				else
 					goto unimplemented;
 				break;
+			case 'p': 
+				if( MCF.HaltMode ) MCF.HaltMode( dev, 0 );
+				if( MCF.ConfigureReadProtection )
+					MCF.ConfigureReadProtection( dev, 0 );
+				else
+					goto unimplemented;
+				break;
+			case 'P':
+				if( MCF.HaltMode ) MCF.HaltMode( dev, 0 );
+				if( MCF.ConfigureReadProtection )
+					MCF.ConfigureReadProtection( dev, 1 );
+				else
+					goto unimplemented;
+				break;
 			case 'G':
 			case 'T':
 			{
@@ -315,7 +330,7 @@ keep_going:
 					goto unimplemented;
 				break;
 			}
-			case 'p':
+			case 'i':
 			{
 				if( MCF.PrintChipInfo )
 					MCF.PrintChipInfo( dev ); 
@@ -553,12 +568,13 @@ help:
 	fprintf( stderr, " -A Go into Halt without reboot\n" );
 	fprintf( stderr, " -D Configure NRST as GPIO\n" );
 	fprintf( stderr, " -d Configure NRST as NRST\n" );
+	fprintf( stderr, " -i Show chip info\n" );
 	fprintf( stderr, " -s [debug register] [value]\n" );
 	fprintf( stderr, " -m [debug register]\n" );
 	fprintf( stderr, " -T Terminal Only\n" );
 	fprintf( stderr, " -G Terminal + GDB\n" );
-//	fprintf( stderr, " -P Enable Read Protection (UNTESTED)\n" );
-//	fprintf( stderr, " -p Disable Read Protection (UNTESTED)\n" );
+	fprintf( stderr, " -P Enable Read Protection\n" );
+	fprintf( stderr, " -p Disable Read Protection\n" );
 	fprintf( stderr, " -w [binary image to write] [address, decimal or 0x, try0x08000000]\n" );
 	fprintf( stderr, " -r [output binary image] [memory address, decimal or 0x, try 0x08000000] [size, decimal or 0x, try 16384]\n" );
 	fprintf( stderr, "   Note: for memory addresses, you can use 'flash' 'launcher' 'bootloader' 'option' 'ram' and say \"ram+0x10\" for instance\n" );
@@ -1786,19 +1802,25 @@ int DefaultConfigureNRSTAsGPIO( void * dev, int one_if_yes_gpio  )
 #endif
 }
 
+int DefaultConfigureReadProtection( void * dev, int one_if_yes_protect  )
+{
+	fprintf( stderr, "Error: DefaultConfigureReadProtection does not work via the programmer here.  Please see the demo \"optionbytes\"\n" );
+	return -5;
+}
+
 int DefaultPrintChipInfo( void * dev )
 {
 	uint32_t reg;
 	MCF.HaltMode( dev, 5 );
 
 	if( MCF.ReadWord( dev, 0x1FFFF800, &reg ) ) goto fail;	
-	printf( "USER/RDPR: %08x\n", reg );
-/*	if( MCF.ReadWord( dev, 0x1FFFF804, &reg ) ) goto fail;	
-	printf( "NDATA: %08x\n", reg );
+	printf( "USER/RDPR  : %04x/%04x\n", reg>>16, reg&0xFFFF );
+	if( MCF.ReadWord( dev, 0x1FFFF804, &reg ) ) goto fail;	
+	printf( "DATA1/DATA0: %04x/%04x\n", reg>>16, reg&0xFFFF );
 	if( MCF.ReadWord( dev, 0x1FFFF808, &reg ) ) goto fail;	
-	printf( "WRPR01: %08x\n", reg );
+	printf( "WRPR1/WRPR0: %04x/%04x\n", reg>>16, reg&0xFFFF );
 	if( MCF.ReadWord( dev, 0x1FFFF80c, &reg ) ) goto fail;	
-	printf( "WRPR23: %08x\n", reg );*/
+	printf( "WRPR3/WRPR2: %04x/%04x\n", reg>>16, reg&0xFFFF );
 	if( MCF.ReadWord( dev, 0x1FFFF7E0, &reg ) ) goto fail;
 	printf( "Flash Size: %d kB\n", (reg&0xffff) );
 	if( MCF.ReadWord( dev, 0x1FFFF7E8, &reg ) ) goto fail;	
diff --git a/minichlink/minichlink.h b/minichlink/minichlink.h
index b212d85223ce9252923640a28cf7276c6af44a18..6a974ff230635fa24a2a769512d61c356dc3013c 100644
--- a/minichlink/minichlink.h
+++ b/minichlink/minichlink.h
@@ -23,6 +23,7 @@ struct MiniChlinkFunctions
 
 	int (*HaltMode)( void * dev, int mode ); //0 for halt, 1 for reset, 2 for resume
 	int (*ConfigureNRSTAsGPIO)( void * dev, int one_if_yes_gpio );
+	int (*ConfigureReadProtection)( void * dev, int one_if_yes_protect );
 
 	// No boundary or limit rules.  Must support any combination of alignment and size.
 	int (*WriteBinaryBlob)( void * dev, uint32_t address_to_write, uint32_t blob_size, uint8_t * blob );
diff --git a/minichlink/pgm-wch-linke.c b/minichlink/pgm-wch-linke.c
index e314875464e5545833cd1d8dab7092c723f31f42..70c43eba8571425820b95f1c484f6f02ba1fefd1 100644
--- a/minichlink/pgm-wch-linke.c
+++ b/minichlink/pgm-wch-linke.c
@@ -309,6 +309,21 @@ static int LEConfigureNRSTAsGPIO( void * d, int one_if_yes_gpio )
 	return 0;
 }
 
+static int LEConfigureReadProtection( void * d, int one_if_yes_protect )
+{
+	libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
+
+	if( one_if_yes_protect )
+	{
+		wch_link_multicommands( (libusb_device_handle *)dev, 2, 11, "\x81\x06\x08\x03\xf7\xff\xff\xff\xff\xff\xff", 4, "\x81\x0b\x01\x01" );
+	}
+	else
+	{
+		wch_link_multicommands( (libusb_device_handle *)dev, 2, 11, "\x81\x06\x08\x02\xf7\xff\xff\xff\xff\xff\xff", 4, "\x81\x0b\x01\x01" );
+	}
+	return 0;
+}
+
 int LEExit( void * d )
 {
 	libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
@@ -337,6 +352,7 @@ void * TryInit_WCHLinkE()
 	MCF.Control5v = LEControl5v;
 	MCF.Unbrick = LEUnbrick;
 	MCF.ConfigureNRSTAsGPIO = LEConfigureNRSTAsGPIO;
+	MCF.ConfigureReadProtection = LEConfigureReadProtection;
 
 	MCF.Exit = LEExit;
 	return ret;