diff --git a/bootloaders/zero/drivers/cdc_enumerate.c b/bootloaders/zero/drivers/cdc_enumerate.c
index 3bb4fd6e587c7d4a2333e1afe65fe3b10f6c4708..5dda45ffb633d991179b387242511938e7ce4537 100644
--- a/bootloaders/zero/drivers/cdc_enumerate.c
+++ b/bootloaders/zero/drivers/cdc_enumerate.c
@@ -750,5 +750,16 @@ uint32_t cdc_read_buf_xmd(void* data, uint32_t length)
 		return 0;
 
 	/* Blocking read till specified number of bytes is received */
-	return USB_Read_blocking(&pCdc, (char *)data, length);
+	// XXX: USB_Read_blocking is not reliable
+	// return USB_Read_blocking(&pCdc, (char *)data, length);
+
+	char *dst = (char *)data;
+	uint32_t remaining = length;
+	while (remaining) {
+		uint32_t readed = USB_Read(&pCdc, (char *)dst, remaining);
+		remaining -= readed;
+		dst += readed;
+	}
+
+	return length;
 }
diff --git a/bootloaders/zero/sam_ba_monitor.c b/bootloaders/zero/sam_ba_monitor.c
index 1df6d0437017094c0b635edf36406d9b36f9a24f..29c9d8566be932144130fa75d6422affe772a9c1 100644
--- a/bootloaders/zero/sam_ba_monitor.c
+++ b/bootloaders/zero/sam_ba_monitor.c
@@ -36,6 +36,7 @@
 #include "cdc_enumerate.h"
 
 const char RomBOOT_Version[] = SAM_BA_VERSION;
+const char RomBOOT_ExtendedCapabilities[] = "[Arduino:XYZ]";
 
 /* Provides one common interface to handle both USART and USB-CDC */
 typedef struct
@@ -166,169 +167,288 @@ uint8_t command, *ptr_data, *ptr, data[SIZEBUFMAX];
 uint8_t j;
 uint32_t u32tmp;
 
+uint32_t PAGE_SIZE, PAGES, MAX_FLASH;
 
 /**
  * \brief This function starts the SAM-BA monitor.
  */
 void sam_ba_monitor_run(void)
 {
+	uint32_t pageSizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024 };
+	PAGE_SIZE = pageSizes[NVMCTRL->PARAM.bit.PSZ];
+	PAGES = NVMCTRL->PARAM.bit.NVMP;
+	MAX_FLASH = PAGE_SIZE * PAGES;
+
 	ptr_data = NULL;
 	command = 'z';
-	
-	// Start waiting some cmd
-	while (1)
+	while (1) {
+		sam_ba_monitor_loop();
+	}
+}
+
+// Prints a 32-bit integer in hex.
+void put_uint32(uint32_t n) {
+	char buff[8];
+	int i;
+	for (i=0; i<8; i++) {
+		int d = n & 0XF;
+		n = (n >> 4);
+
+		buff[7-i] = d > 9 ? 'A' + d - 10 : '0' + d;
+	}
+	ptr_monitor_if->putdata(buff, 8);
+}
+
+void sam_ba_monitor_loop(void)
+{
+	length = ptr_monitor_if->getdata(data, SIZEBUFMAX);
+	ptr = data;
+	for (i = 0; i < length; i++, ptr++)
 	{
-		length = ptr_monitor_if->getdata(data, SIZEBUFMAX);
-		ptr = data;
-		for (i = 0; i < length; i++)
+		if (*ptr == 0xff) continue;
+
+		if (*ptr == '#')
 		{
-			if (*ptr != 0xff)
+			if (b_terminal_mode)
 			{
-				if (*ptr == '#')
+				ptr_monitor_if->putdata("\n\r", 2);
+			}
+			if (command == 'S')
+			{
+				//Check if some data are remaining in the "data" buffer
+				if(length>i)
 				{
-					if (b_terminal_mode)
-					{
-						ptr_monitor_if->putdata("\n\r", 2);
-					}
-					if (command == 'S')
-					{
-						//Check if some data are remaining in the "data" buffer
-						if(length>i)
-						{
-							//Move current indexes to next avail data (currently ptr points to "#")
-							ptr++;
-							i++;
-							//We need to add first the remaining data of the current buffer already read from usb
-							//read a maximum of "current_number" bytes
-							u32tmp=min((length-i),current_number);
-							memcpy(ptr_data, ptr, u32tmp);
-							i += u32tmp;
-							ptr += u32tmp;
-							j = u32tmp;
-						}
-						//update i with the data read from the buffer
-						i--;
-						ptr--;
-						//Do we expect more data ?
-						if(j<current_number)
-							ptr_monitor_if->getdata_xmd(ptr_data, current_number-j);
-						
-						__asm("nop");
-					}
-					else if (command == 'R')
-					{
-						ptr_monitor_if->putdata_xmd(ptr_data, current_number);
-					}
-					else if (command == 'O')
-					{
-						*ptr_data = (char) current_number;
-					}
-					else if (command == 'H')
-					{
-						*((uint16_t *) ptr_data) = (uint16_t) current_number;
-					}
-					else if (command == 'W')
-					{
-						*((int *) ptr_data) = current_number;
-					}
-					else if (command == 'o')
-					{
-						sam_ba_putdata_term(ptr_data, 1);
-					}
-					else if (command == 'h')
-					{
-						current_number = *((uint16_t *) ptr_data);
-						sam_ba_putdata_term((uint8_t*) &current_number, 2);
-					}
-					else if (command == 'w')
-					{
-						current_number = *((uint32_t *) ptr_data);
-						sam_ba_putdata_term((uint8_t*) &current_number, 4);
-					}
-					else if (command == 'G')
-					{
-						call_applet(current_number);
-						/* Rebase the Stack Pointer */
-						__set_MSP(sp);
-						cpu_irq_enable();
-						if (b_sam_ba_interface_usart) {
-							ptr_monitor_if->put_c(0x6);
-						}
-					}
-					else if (command == 'T')
-					{
-						b_terminal_mode = 1;
-						ptr_monitor_if->putdata("\n\r", 2);
-					}
-					else if (command == 'N')
-					{
-						if (b_terminal_mode == 0)
-						{
-							ptr_monitor_if->putdata("\n\r", 2);
-						}
-						b_terminal_mode = 0;
-					}
-					else if (command == 'V')
-					{
-						ptr_monitor_if->putdata("v", 1);
-						ptr_monitor_if->putdata((uint8_t *) RomBOOT_Version,
-								strlen(RomBOOT_Version));
-						ptr_monitor_if->putdata(" ", 1);
-						ptr = (uint8_t*) &(__DATE__);
-						i = 0;
-						while (*ptr++ != '\0')
-							i++;
-						ptr_monitor_if->putdata((uint8_t *) &(__DATE__), i);
-						ptr_monitor_if->putdata(" ", 1);
-						i = 0;
-						ptr = (uint8_t*) &(__TIME__);
-						while (*ptr++ != '\0')
-							i++;
-						ptr_monitor_if->putdata((uint8_t *) &(__TIME__), i);
-						ptr_monitor_if->putdata("\n\r", 2);
-					}
+					//Move current indexes to next avail data (currently ptr points to "#")
+					ptr++;
+					i++;
+					//We need to add first the remaining data of the current buffer already read from usb
+					//read a maximum of "current_number" bytes
+					u32tmp=min((length-i),current_number);
+					memcpy(ptr_data, ptr, u32tmp);
+					i += u32tmp;
+					ptr += u32tmp;
+					j = u32tmp;
+				}
+				//update i with the data read from the buffer
+				i--;
+				ptr--;
+				//Do we expect more data ?
+				if(j<current_number)
+					ptr_monitor_if->getdata_xmd(ptr_data, current_number-j);
+				
+				__asm("nop");
+			}
+			else if (command == 'R')
+			{
+				ptr_monitor_if->putdata_xmd(ptr_data, current_number);
+			}
+			else if (command == 'O')
+			{
+				*ptr_data = (char) current_number;
+			}
+			else if (command == 'H')
+			{
+				*((uint16_t *) ptr_data) = (uint16_t) current_number;
+			}
+			else if (command == 'W')
+			{
+				*((int *) ptr_data) = current_number;
+			}
+			else if (command == 'o')
+			{
+				sam_ba_putdata_term(ptr_data, 1);
+			}
+			else if (command == 'h')
+			{
+				current_number = *((uint16_t *) ptr_data);
+				sam_ba_putdata_term((uint8_t*) &current_number, 2);
+			}
+			else if (command == 'w')
+			{
+				current_number = *((uint32_t *) ptr_data);
+				sam_ba_putdata_term((uint8_t*) &current_number, 4);
+			}
+			else if (command == 'G')
+			{
+				call_applet(current_number);
+				/* Rebase the Stack Pointer */
+				__set_MSP(sp);
+				cpu_irq_enable();
+				if (b_sam_ba_interface_usart) {
+					ptr_monitor_if->put_c(0x6);
+				}
+			}
+			else if (command == 'T')
+			{
+				b_terminal_mode = 1;
+				ptr_monitor_if->putdata("\n\r", 2);
+			}
+			else if (command == 'N')
+			{
+				if (b_terminal_mode == 0)
+				{
+					ptr_monitor_if->putdata("\n\r", 2);
+				}
+				b_terminal_mode = 0;
+			}
+			else if (command == 'V')
+			{
+				ptr_monitor_if->putdata("v", 1);
+				ptr_monitor_if->putdata((uint8_t *) RomBOOT_Version,
+						strlen(RomBOOT_Version));
+				ptr_monitor_if->putdata(" ", 1);
+				ptr_monitor_if->putdata((uint8_t *) RomBOOT_ExtendedCapabilities,
+						strlen(RomBOOT_ExtendedCapabilities));
+				ptr_monitor_if->putdata(" ", 1);
+				ptr = (uint8_t*) &(__DATE__);
+				i = 0;
+				while (*ptr++ != '\0')
+					i++;
+				ptr_monitor_if->putdata((uint8_t *) &(__DATE__), i);
+				ptr_monitor_if->putdata(" ", 1);
+				i = 0;
+				ptr = (uint8_t*) &(__TIME__);
+				while (*ptr++ != '\0')
+					i++;
+				ptr_monitor_if->putdata((uint8_t *) &(__TIME__), i);
+				ptr_monitor_if->putdata("\n\r", 2);
+			}
+			else if (command == 'X')
+			{
+				// Syntax: X[ADDR]#
+				// Erase the flash memory starting from ADDR to the end of flash.
 
-					command = 'z';
-					current_number = 0;
+				// Note: the flash memory is erased in ROWS, that is in block of 4 pages.
+				//       Even if the starting address is the last byte of a ROW the entire
+				//       ROW is erased anyway.
 
-					if (b_terminal_mode)
-					{
-						ptr_monitor_if->putdata(">", 1);
-					}
+				uint32_t dst_addr = current_number; // starting address
+
+				while (dst_addr < MAX_FLASH) {
+					// Execute "ER" Erase Row
+					NVMCTRL->ADDR.reg = dst_addr / 2;
+					NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
+					while (NVMCTRL->INTFLAG.bit.READY == 0)
+						;
+					dst_addr += PAGE_SIZE * 4; // Skip a ROW
 				}
-				else
-				{
-					if (('0' <= *ptr) && (*ptr <= '9'))
-					{
-						current_number = (current_number << 4) | (*ptr - '0');
 
-					}
-					else if (('A' <= *ptr) && (*ptr <= 'F'))
-					{
-						current_number = (current_number << 4)
-								| (*ptr - 'A' + 0xa);
+				// Notify command completed
+				ptr_monitor_if->putdata("X\n\r", 3);
+			}
+			else if (command == 'Y')
+			{
+				// This command writes the content of a buffer in SRAM into flash memory.
 
-					}
-					else if (('a' <= *ptr) && (*ptr <= 'f'))
-					{
-						current_number = (current_number << 4)
-								| (*ptr - 'a' + 0xa);
+				// Syntax: Y[ADDR],0#
+				// Set the starting address of the SRAM buffer.
 
-					}
-					else if (*ptr == ',')
-					{
-						ptr_data = (uint8_t *) current_number;
-						current_number = 0;
+				// Syntax: Y[ROM_ADDR],[SIZE]#
+				// Write the first SIZE bytes from the SRAM buffer (previously set) into
+				// flash memory starting from address ROM_ADDR
 
-					}
-					else
-					{
-						command = *ptr;
-						current_number = 0;
+				static uint32_t *src_buff_addr = NULL;
+
+				if (current_number == 0) {
+					// Set buffer address
+					src_buff_addr = ptr_data;
+
+				} else {
+					// Write to flash
+					uint32_t size = current_number/4;
+					uint32_t *src_addr = src_buff_addr;
+					uint32_t *dst_addr = ptr_data;
+
+					// Set automatic page write
+					NVMCTRL->CTRLB.bit.MANW = 0;
+
+					// Do writes in pages
+					while (size) {
+						// Execute "PBC" Page Buffer Clear
+						NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
+						while (NVMCTRL->INTFLAG.bit.READY == 0)
+							;
+
+						// Fill page buffer
+						uint32_t i;
+						for (i=0; i<(PAGE_SIZE/4) && i<size; i++) {
+							dst_addr[i] = src_addr[i];
+						}
+
+						// Execute "WP" Write Page
+						//NVMCTRL->ADDR.reg = ((uint32_t)dst_addr) / 2;
+						NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
+						while (NVMCTRL->INTFLAG.bit.READY == 0)
+							;
+
+						// Advance to next page
+						dst_addr += i;
+						src_addr += i;
+						size     -= i;
 					}
 				}
-				ptr++;
+
+				// Notify command completed
+				ptr_monitor_if->putdata("Y\n\r", 3);
+			}
+			else if (command == 'Z')
+			{
+				// This command calculate CRC for a given area of memory.
+				// It's useful to quickly check if a transfer has been done
+				// successfully.
+
+				// Syntax: Z[START_ADDR],[SIZE]#
+				// Returns: Z[CRC]#
+
+				uint8_t *data = (uint8_t *)ptr_data;
+				uint32_t size = current_number;
+				uint16_t crc = 0;
+				uint32_t i = 0;
+				for (i=0; i<size; i++)
+					crc = add_crc(*data++, crc);
+
+				// Send response
+				ptr_monitor_if->putdata("Z", 1);
+				put_uint32(crc);
+				ptr_monitor_if->putdata("#\n\r", 3);
+			}
+
+			command = 'z';
+			current_number = 0;
+
+			if (b_terminal_mode)
+			{
+				ptr_monitor_if->putdata(">", 1);
+			}
+		}
+		else
+		{
+			if (('0' <= *ptr) && (*ptr <= '9'))
+			{
+				current_number = (current_number << 4) | (*ptr - '0');
+			}
+			else if (('A' <= *ptr) && (*ptr <= 'F'))
+			{
+				current_number = (current_number << 4) | (*ptr - 'A' + 0xa);
+			}
+			else if (('a' <= *ptr) && (*ptr <= 'f'))
+			{
+				current_number = (current_number << 4) | (*ptr - 'a' + 0xa);
+			}
+			else if (*ptr == ',')
+			{
+				ptr_data = (uint8_t *) current_number;
+				current_number = 0;
+			}
+			else
+			{
+				command = *ptr;
+				current_number = 0;
 			}
 		}
 	}
 }
+
+
+
+
diff --git a/bootloaders/zero/samd21_sam_ba.bin b/bootloaders/zero/samd21_sam_ba.bin
index 042c36320aa8edf90875b9cbc5ed4d23beebcac2..69ced0a312f83d769f7f62e0fe460551481eadce 100644
Binary files a/bootloaders/zero/samd21_sam_ba.bin and b/bootloaders/zero/samd21_sam_ba.bin differ
diff --git a/bootloaders/zero/usart_sam_ba.c b/bootloaders/zero/usart_sam_ba.c
index f17f319554a93d80831bc4d29340d97ac14089d0..645e725e953e661994c434bae14f541064aa60b7 100644
--- a/bootloaders/zero/usart_sam_ba.c
+++ b/bootloaders/zero/usart_sam_ba.c
@@ -203,24 +203,47 @@ uint32_t usart_getdata(void* data, uint32_t length) {
 	return (1);
 }
 
+static const uint16_t crc16Table[256] = {
+	0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
+	0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
+	0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
+	0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
+	0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
+	0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
+	0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
+	0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
+	0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
+	0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
+	0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
+	0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
+	0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
+	0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
+	0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
+	0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
+	0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
+	0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
+	0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
+	0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
+	0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
+	0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
+	0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
+	0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
+	0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
+	0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
+	0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
+	0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
+	0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
+	0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
+	0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
+	0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
+};
+
 //*----------------------------------------------------------------------------
 //* \fn    add_crc
 //* \brief Compute the CRC
 //*----------------------------------------------------------------------------
 unsigned short add_crc(char ptr, unsigned short crc) {
-
-	unsigned short cmpt;
-
-	crc = crc ^ (int) ptr << 8;
-
-	for (cmpt = 0; cmpt < 8; cmpt++) {
-		if (crc & 0x8000)
-			crc = crc << 1 ^ CRC16POLY;
-		else
-			crc = crc << 1;
-	}
-
-	return (crc & 0xFFFF);
+	return (crc << 8) ^ crc16Table[((crc >> 8) ^ ptr) & 0xff];
 }
 
 //*----------------------------------------------------------------------------
diff --git a/bootloaders/zero/usart_sam_ba.h b/bootloaders/zero/usart_sam_ba.h
index f319d049157428cb95a1e7494d75a42eeefe0f34..d498dc791f41739bea2d60e8b221524d7fd6e91a 100644
--- a/bootloaders/zero/usart_sam_ba.h
+++ b/bootloaders/zero/usart_sam_ba.h
@@ -142,13 +142,13 @@ uint32_t usart_putdata_xmd(void const* data, uint32_t length); //Send given data
 uint32_t usart_getdata_xmd(void* data, uint32_t length); //Get data from comm. device using xmodem (if necessary)
 
 /**
- * \brief Gets data from usart line using Xmodem protocol
+ * \brief Compute the CRC
  *
- * \param data pointer
- * \param number of data to get
- * \return value read on usart line
+ * \param Char to add to CRC
+ * \param Previous CRC
+ * \return The new computed CRC
  */
-unsigned short add_crc(char ptr, unsigned short crc);
+unsigned short add_crc(char c, unsigned short crc);
 
 uint8_t getPacket(uint8_t *pData, uint8_t sno);
 
diff --git a/platform.txt b/platform.txt
index 6bca9e4c633d0b943fe97554ad984d4bda7b05c4..0f9ebb08285c40a1d135d3882d574315facdd7a2 100644
--- a/platform.txt
+++ b/platform.txt
@@ -80,7 +80,7 @@ recipe.size.regex=\.text\s+([0-9]+).*
 #
 tools.bossac.cmd=bossac
 tools.bossac.cmd.windows=bossac.exe
-tools.bossac.path={runtime.ide.path}/hardware/tools/bossac-1.4-arduino
+tools.bossac.path={runtime.ide.path}/hardware/tools/bossac-1.5-arduino
 
 tools.bossac.upload.params.verbose=-i -d
 tools.bossac.upload.params.quiet=