diff --git a/README.md b/README.md
index 664eb4dee8d12c87f400be3b07f412c2d422373b..2f7a73884935c818ca2311440908fcbf4d9a7ee5 100644
--- a/README.md
+++ b/README.md
@@ -90,6 +90,12 @@ Other third party tools are adding lots of examples, etc.  See the following rep
 
 You can open a github ticket or join my Discord in the #ch32v003fun channel. https://discord.gg/CCeyWyZ
 
+## General notes about the CH32V003.
+
+CPI/Processor Speed:
+
+Ignoring branches and load/stores, compressed instructions run at 1 CPI. Non-compressed instructions run at 1 CPI for the first 2 instructions, then further ones take 2 CPI regardless of how many more you have.  Running from RAM and running from FLASH have slightly different performance characteristics depending on wait states that should be measured in-situation.
+
 ### Footnotes/links
 
  * https://raw.githubusercontent.com/openwch/ch32v003/main/RISC-V%20QingKeV2%20Microprocessor%20Debug%20Manual.pdf Debugging Manual
diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c
index f7f86c5b4f769b14fbd7b9a8542d88dc3ce46825..ad17c14cf6ac01715afc52e5a4a5131b50728d09 100644
--- a/ch32v003fun/ch32v003fun.c
+++ b/ch32v003fun/ch32v003fun.c
@@ -895,7 +895,7 @@ int putchar(int c)
 void handle_debug_input( int numbytes, uint8_t * data ) __attribute__((weak));
 void handle_debug_input( int numbytes, uint8_t * data ) { }
 
-static void internal_handle_input( uint32_t * dmdata0 )
+static void internal_handle_input( volatile uint32_t * dmdata0 )
 {
 	uint32_t dmd0 = *dmdata0;
 	int bytes = (dmd0 & 0x3f) - 4;
@@ -908,11 +908,12 @@ static void internal_handle_input( uint32_t * dmdata0 )
 
 void poll_input()
 {
-	uint32_t lastdmd = (*DMDATA0);
- 	if( !(lastdmd & 0x80) )
+	volatile uint32_t * dmdata0 = (volatile uint32_t *)DMDATA0;
+ 	if( ((*dmdata0) & 0x80) == 0 )
 	{
-		internal_handle_input( (uint32_t*)DMDATA0 );
-		*DMDATA0 = 0x84; // Negative
+		internal_handle_input( dmdata0 );
+		// Should be 0x80 or so, but for some reason there's a bug that retriggers.
+		*dmdata0 = 0x00;
 	}
 }
 
@@ -980,10 +981,6 @@ int putchar(int c)
 		if( timeout-- == 0 ) return 0;
 
 	// Simply seeking input.
-	lastdmd = (*DMDATA0);
-	if( lastdmd ) internal_handle_input( (uint32_t*)DMDATA0 );
-
-	while( (lastdmd = (*DMDATA0)) & 0x80 ) if( timeout-- == 0 ) return 0;
 	if( lastdmd ) internal_handle_input( (uint32_t*)DMDATA0 );
 	*DMDATA0 = 0x85 | ((const char)c<<8);
 	return 1;
diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h
index a6e7666f6cef2e93b1362d02c3cab5b1067ee079..a285ef282754e63e6e7b1a6128f37abbf480e3f4 100644
--- a/ch32v003fun/ch32v003fun.h
+++ b/ch32v003fun/ch32v003fun.h
@@ -4563,6 +4563,45 @@ RV_STATIC_INLINE void __disable_irq()
   __asm volatile ("csrw mstatus, %0" : : "r" (result) );
 }
 
+/*********************************************************************
+ * @fn      __isenabled_irq
+ *
+ * @brief   Is Global Interrupt enabled
+ *
+ * @return  1: yes, 0: no
+ */
+RV_STATIC_INLINE uint8_t __isenabled_irq(void)
+{
+    uint32_t result;
+
+    __asm volatile(
+#if __GNUC__ > 10
+    ".option arch, +zicsr\n"
+#endif
+    "csrr %0," "mstatus": "=r"(result));
+    return (result & 0x08) != 0u;
+}
+
+/*********************************************************************
+ * @fn      __get_cpu_sp
+ *
+ * @brief   Get stack pointer
+ *
+ * @return  stack pointer
+ */
+RV_STATIC_INLINE uint32_t __get_cpu_sp(void);
+RV_STATIC_INLINE uint32_t __get_cpu_sp(void)
+{
+  uint32_t result;
+
+  __asm volatile(
+#if __GNUC__ > 10
+    ".option arch, +zicsr\n"
+#endif
+  "mv %0, sp" : "=r"(result));
+  return result;
+}
+
 /*********************************************************************
  * @fn      __NOP
  *
diff --git a/examples/adc_fixed_fs/adc_fixed_fs.c b/examples/adc_fixed_fs/adc_fixed_fs.c
index c69b7ba6e965586b038ecc9b2d3e7f07bca1cf44..758b13c5d73a83cd4ad2a84771594d3d1e3ba4dd 100644
--- a/examples/adc_fixed_fs/adc_fixed_fs.c
+++ b/examples/adc_fixed_fs/adc_fixed_fs.c
@@ -68,8 +68,7 @@ void init_adc() {
     RCC->CFGR0 |= RCC_ADCPRE_DIV2;	// set it to 010xx for /2.
 
     // Keep CALVOL register with initial value
-    ADC1->CTLR1 |= ADC_ExternalTrigConv_T1_TRGO;
-    ADC1->CTLR2 = ADC_ADON | ADC_DMA | ADC_EXTTRIG;
+    ADC1->CTLR2 = ADC_ADON | ADC_DMA | ADC_EXTTRIG | ADC_ExternalTrigConv_T1_TRGO;
 
     // Possible times: 0->3,1->9,2->15,3->30,4->43,5->57,6->73,7->241 cycles
     ADC1->SAMPTR2 = 0/*3 cycles*/ << (3/*offset per channel*/ * 2/*channel*/);
diff --git a/examples/cap_touch_adc/cap_touch_adc.c b/examples/cap_touch_adc/cap_touch_adc.c
index 2bde0c92bc5091432592a47b6279288302836424..98457e9a96ddf11e3823df665af0b5d05bd5a3d4 100644
--- a/examples/cap_touch_adc/cap_touch_adc.c
+++ b/examples/cap_touch_adc/cap_touch_adc.c
@@ -21,101 +21,7 @@
 #include "ch32v003fun.h"
 #include <stdio.h>
 
-#define ADC_SAMPLE_TIME 2  // Tricky: Don't change this without a lot of experimentation.
-#define MAX_SCALECHECK  4
-
-// Can either be 0 or 1.
-// If 0: Measurement low and rises high.  So more pressed is smaller number.
-// If 1: Higher number = harder press. Good to pair with TOUCH_FLAT.
-// If you are doing more prox, use mode 0, otherwise, use mode 1.
-#define TOUCH_SLOPE     1
-
-// If you set this to 1, it will glitch the line, so it will only read
-// anything reasonable if the capacitance can overcome that initial spike.
-// Typically, it seems if you use this you probbly don't need to do
-// any pre-use calibration.
-#define TOUCH_FLAT      0
-
-void InitTouchADC( )
-{
-	// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide sys clock by 2
-	RCC->CFGR0 &= ~(0x1F<<11);
-
-	// Set up single conversion on chl 2
-	ADC1->RSQR1 = 0;
-	ADC1->RSQR2 = 0;
-
-	// turn on ADC and set rule group to sw trig
-	ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL;
-	
-	// Reset calibration
-	ADC1->CTLR2 |= ADC_RSTCAL;
-	while(ADC1->CTLR2 & ADC_RSTCAL);
-	
-	// Calibrate
-	ADC1->CTLR2 |= ADC_CAL;
-	while(ADC1->CTLR2 & ADC_CAL);
-}
-
-// Run from RAM to get even more stable timing.
-// This function call takes about 8.1uS to execute.
-uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) __attribute__((noinline, section(".srodata")));
-uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations )
-{
-	uint32_t ret = 0;
-
-	ADC1->RSQR3 = adcno;
-	ADC1->SAMPTR2 = ADC_SAMPLE_TIME<<(3*adcno);
-
-	uint32_t CFGBASE = io->CFGLR & (~(0xf<<(4*portpin)));
-	uint32_t CFGFLOAT = ((GPIO_CFGLR_IN_PUPD)<<(4*portpin)) | CFGBASE;
-	uint32_t CFGDRIVE = (GPIO_CFGLR_OUT_2Mhz_PP)<<(4*portpin) | CFGBASE;
-
-	// If we run multiple times with slightly different wait times, we can
-	// reduce the impact of the ADC's DNL.
-
-
-#if TOUCH_FLAT == 1
-#define RELEASEIO io->OUTDR = 1<<(portpin+16*TOUCH_SLOPE); io->CFGLR = CFGFLOAT;
-#else
-#define RELEASEIO io->CFGLR = CFGFLOAT; io->OUTDR = 1<<(portpin+16*TOUCH_SLOPE);
-#endif
-
-#define INNER_LOOP( n ) \
-	{ \
-		/* Only lock IRQ for a very narrow window. */                           \
-		__disable_irq();                                                        \
-                                                                                \
-		/* Tricky - we start the ADC BEFORE we transition the pin.  By doing    \
-			this We are catching it onthe slope much more effectively.  */      \
-		ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL;                      \
-                                                                                \
-		ADD_N_NOPS( n )                                                         \
-                                                                                \
-		RELEASEIO                                                               \
-																			    \
-		/* Sampling actually starts here, somewhere, so we can let other        \
-			interrupts run */                                                   \
-		__enable_irq();                                                         \
-		while(!(ADC1->STATR & ADC_EOC));                                        \
-		io->CFGLR = CFGDRIVE;                                                   \
-		io->OUTDR = 1<<(portpin+(16*(1-TOUCH_SLOPE)));                          \
-		ret += ADC1->RDATAR;                                                    \
-	}
-
-	int i;
-	for( i = 0; i < iterations; i++ )
-	{
-		// Wait a variable amount of time based on loop iteration, in order
-		// to get a variety of RC points and minimize DNL.
-
-		INNER_LOOP( 0 );
-		INNER_LOOP( 2 );
-		INNER_LOOP( 4 );
-	}
-
-	return ret;
-}
+#include "ch32v003_touch.h"
 
 int main()
 {
@@ -133,8 +39,6 @@ int main()
 	{
 		uint32_t sum[8] = { 0 };
 
-		int j;
-
 		uint32_t start = SysTick->CNT;
 
 		// Sampling all touch pads, 3x should take 6030 cycles, and runs at about 8kHz
@@ -156,28 +60,3 @@ int main()
 	}
 }
 
-/*
- * MIT License
- * 
- * Copyright (c) 2023 Valve Corporation
- * 
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-
diff --git a/examples/color_lcd/Makefile b/examples/color_lcd/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c2417fba9d1ccb8e0fffd6e13978439be546ee73
--- /dev/null
+++ b/examples/color_lcd/Makefile
@@ -0,0 +1,10 @@
+all : flash
+
+TARGET:=color_lcd
+
+include ../../ch32v003fun/ch32v003fun.mk
+
+flash : cv_flash
+clean : cv_clean
+
+
diff --git a/examples/color_lcd/ch32v_hal.h b/examples/color_lcd/ch32v_hal.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4bc3650bf3533697ca30c58af4ad852d2dbd866
--- /dev/null
+++ b/examples/color_lcd/ch32v_hal.h
@@ -0,0 +1,69 @@
+//
+// CH32V Hardware Abstraction layer
+// written by Larry Bank
+// bitbank@pobox.com
+//
+// Copyright 2023 BitBank Software, Inc. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//    http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef CH32V_HAL_H_
+#define CH32V_HAL_H_
+
+#include "ch32v003fun.h"
+#include <stdio.h>
+#include <stdint.h>
+
+#define BITBANG
+// GPIO pin states
+enum {
+	OUTPUT = 0,
+	INPUT,
+	INPUT_PULLUP,
+	INPUT_PULLDOWN
+};
+
+#define PROGMEM
+#define memcpy_P memcpy
+#define pgm_read_byte(s) *(uint8_t *)s
+#define pgm_read_word(s) *(uint16_t *)s
+
+// Wrapper methods
+void delay(int i);
+//
+// Digital pin functions use a numbering scheme to make it easier to map the
+// pin number to a port name and number
+// The GPIO ports A-D become the most significant nibble of the pin number
+// for example, to use Port C pin 7 (C7), use the pin number 0xC7
+//
+void pinMode(uint8_t u8Pin, int iMode);
+uint8_t digitalRead(uint8_t u8Pin);
+void digitalWrite(uint8_t u8Pin, uint8_t u8Value);
+
+// The Wire library is a C++ class; I've created a work-alike to my
+// BitBang_I2C API which is a set of C functions to simplify I2C
+void I2CInit(uint8_t u8SDA, uint8_t u8SCL, int iSpeed);
+void I2CWrite(uint8_t u8Addr, uint8_t *pData, int iLen);
+int I2CRead(uint8_t u8Addr, uint8_t *pData, int iLen);
+void I2CReadRegister(uint8_t u8Addr, uint8_t u8Reg, uint8_t *pData, int iLen);
+int I2CTest(uint8_t u8Addr);
+void I2CSetSpeed(int iSpeed);
+
+// SPI1 (polling mode)
+void SPI_write(uint8_t *pData, int iLen);
+void SPI_begin(int iSpeed, int iMode);
+
+
+// Random stuff
+void Standby82ms(uint8_t iTicks);
+void breatheLED(uint8_t u8Pin, int iPeriod);
+
+
+#endif /* CH32V_HAL_H_ */
diff --git a/examples/color_lcd/ch32v_hal.inl b/examples/color_lcd/ch32v_hal.inl
new file mode 100644
index 0000000000000000000000000000000000000000..ff58e27932e39ba3224ad11e18f94eca808955cb
--- /dev/null
+++ b/examples/color_lcd/ch32v_hal.inl
@@ -0,0 +1,492 @@
+//
+// CH32V Hardware Abstraction layer
+// written by Larry Bank
+// bitbank@pobox.com
+//
+// Copyright 2023 BitBank Software, Inc. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//    http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "ch32v_hal.h"
+
+#ifdef BITBANG
+uint8_t u8SDA_Pin, u8SCL_Pin;
+int iDelay = 1;
+#endif
+
+void delay(int i)
+{
+	Delay_Ms(i);
+}
+// Arduino-like API defines and function wrappers for WCH MCUs
+
+void pinMode(uint8_t u8Pin, int iMode)
+{
+GPIO_TypeDef *pGPIO;
+
+    if (u8Pin < 0xa0 || u8Pin > 0xdf) return; // invalid pin number
+
+    switch (u8Pin & 0xf0) {
+    case 0xa0:
+	RCC->APB2PCENR |= RCC_APB2Periph_GPIOA;
+	pGPIO = GPIOA;
+    	break;
+    case 0xc0:
+        RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;
+	pGPIO = GPIOC;
+    	break;
+    case 0xd0:
+	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
+	pGPIO = GPIOD;
+    	break;
+    }
+    u8Pin &= 0xf; // isolate the pin from this port
+    pGPIO->CFGLR &= ~(0xf << (4 * u8Pin)); // unset all flags
+
+    switch (iMode) {
+	case OUTPUT:
+		pGPIO->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4*u8Pin);
+		break;
+	case INPUT:
+		pGPIO->CFGLR |= (GPIO_CNF_IN_FLOATING << (4*u8Pin));
+		break;
+	case INPUT_PULLUP:
+		pGPIO->CFGLR |= (GPIO_CNF_IN_PUPD << (4*u8Pin));
+		pGPIO->BSHR = (1 << u8Pin);
+		break;
+	case INPUT_PULLDOWN:
+		pGPIO->CFGLR |= (GPIO_CNF_IN_PUPD << (4 * u8Pin));
+		pGPIO->BCR = (1 << u8Pin);
+		break;
+    } // switch on iMode
+} /* pinMode() */
+
+uint8_t digitalRead(uint8_t u8Pin)
+{
+    uint32_t u32GPIO = 1 << (u8Pin & 0xf);
+    uint32_t u32Value = 0;
+    switch (u8Pin & 0xf0) {
+    case 0xa0:
+    	u32Value = GPIOA->INDR & u32GPIO;
+    	break;
+    case 0xc0:
+    	u32Value = GPIOC->INDR & u32GPIO;
+    	break;
+    case 0xd0:
+    	u32Value = GPIOD->INDR & u32GPIO;
+    	break;
+    }
+    return (u32Value == 0);
+} /* digitalRead() */
+
+void digitalWrite(uint8_t u8Pin, uint8_t u8Value)
+{
+	uint32_t u32Value = 1 << (u8Pin & 0xf); // turn on bit
+	if (!u8Value)
+		u32Value <<= 16; // turn off bit 
+
+	switch (u8Pin & 0xf0) {
+	case 0xa0:
+		GPIOA->BSHR = u32Value;
+		break;
+	case 0xc0:
+                GPIOC->BSHR = u32Value;
+		break;
+	case 0xd0:
+                GPIOD->BSHR = u32Value;
+		break;
+	}
+} /* digitalWrite() */
+
+#ifdef BITBANG
+uint8_t SDA_READ(void)
+{
+	return digitalRead(u8SDA_Pin);
+}
+void SDA_HIGH(void)
+{
+	pinMode(u8SDA_Pin, INPUT);
+}
+void SDA_LOW(void)
+{
+	pinMode(u8SDA_Pin, OUTPUT);
+	digitalWrite(u8SDA_Pin, 0);
+}
+void SCL_HIGH(void)
+{
+	pinMode(u8SCL_Pin, INPUT);
+}
+void SCL_LOW(void)
+{
+	pinMode(u8SCL_Pin, OUTPUT);
+	digitalWrite(u8SCL_Pin, 0);
+}
+void I2CSetSpeed(int iSpeed)
+{
+	if (iSpeed >= 400000) iDelay = 1;
+	else if (iSpeed >= 100000) iDelay = 10;
+	else iDelay = 20;
+}
+void I2CInit(uint8_t u8SDA, uint8_t u8SCL, int iSpeed)
+{
+	u8SDA_Pin = u8SDA;
+	u8SCL_Pin = u8SCL;
+	if (iSpeed >= 400000) iDelay = 1;
+	else if (iSpeed >= 100000) iDelay = 10;
+	else iDelay = 20;
+} /* I2CInit() */
+
+void my_sleep_us(int iDelay)
+{
+	Delay_Us(iDelay);
+}
+// Transmit a byte and read the ack bit
+// if we get a NACK (negative acknowledge) return 0
+// otherwise return 1 for success
+//
+
+int i2cByteOut(uint8_t b)
+{
+uint8_t i, ack;
+
+for (i=0; i<8; i++)
+{
+//    my_sleep_us(iDelay);
+    if (b & 0x80)
+      SDA_HIGH(); // set data line to 1
+    else
+      SDA_LOW(); // set data line to 0
+    b <<= 1;
+//    my_sleep_us(iDelay);
+    SCL_HIGH(); // clock high (slave latches data)
+    my_sleep_us(iDelay);
+    SCL_LOW(); // clock low
+    my_sleep_us(iDelay);
+} // for i
+//my_sleep_us(iDelay);
+// read ack bit
+SDA_HIGH(); // set data line for reading
+//my_sleep_us(iDelay);
+SCL_HIGH(); // clock line high
+my_sleep_us(iDelay); // DEBUG - delay/2
+ack = SDA_READ();
+//my_sleep_us(iDelay);
+SCL_LOW(); // clock low
+my_sleep_us(iDelay); // DEBUG - delay/2
+SDA_LOW(); // data low
+return (ack == 0); // a low ACK bit means success
+} /* i2cByteOut() */
+
+//
+// Receive a byte and read the ack bit
+// if we get a NACK (negative acknowledge) return 0
+// otherwise return 1 for success
+//
+uint8_t i2cByteIn(uint8_t bLast)
+{
+uint8_t i;
+uint8_t b = 0;
+
+     SDA_HIGH(); // set data line as input
+     for (i=0; i<8; i++)
+     {
+         my_sleep_us(iDelay); // wait for data to settle
+         SCL_HIGH(); // clock high (slave latches data)
+         my_sleep_us(iDelay);
+         b <<= 1;
+         if (SDA_READ() != 0) // read the data bit
+           b |= 1; // set data bit
+         SCL_LOW(); // clock low
+     } // for i
+     if (bLast)
+        SDA_HIGH(); // last byte sends a NACK
+     else
+        SDA_LOW();
+//     my_sleep_us(iDelay);
+     SCL_HIGH(); // clock high
+     my_sleep_us(iDelay);
+     SCL_LOW(); // clock low to send ack
+     my_sleep_us(iDelay);
+//     SDA_HIGH();
+     SDA_LOW(); // data low
+  return b;
+} /* i2cByteIn() */
+//
+// Send I2C STOP condition
+//
+void i2cEnd(void)
+{
+   SDA_LOW(); // data line low
+   my_sleep_us(iDelay);
+   SCL_HIGH(); // clock high
+   my_sleep_us(iDelay);
+   SDA_HIGH(); // data high
+   my_sleep_us(iDelay);
+} /* i2cEnd() */
+
+int i2cBegin(uint8_t addr, uint8_t bRead)
+{
+   int rc;
+//   SCL_HIGH();
+//   my_sleep_us(iDelay);
+   SDA_LOW(); // data line low first
+   my_sleep_us(iDelay);
+   SCL_LOW(); // then clock line low is a START signal
+   addr <<= 1;
+   if (bRead)
+      addr++; // set read bit
+   rc = i2cByteOut(addr); // send the slave address and R/W bit
+   return rc;
+} /* i2cBegin() */
+
+void I2CWrite(uint8_t addr, uint8_t *pData, int iLen)
+{
+uint8_t b;
+int rc;
+
+   i2cBegin(addr, 0);
+   rc = 1;
+   while (iLen && rc == 1)
+   {
+      b = *pData++;
+      rc = i2cByteOut(b);
+      if (rc == 1) // success
+      {
+         iLen--;
+      }
+   } // for each byte
+   i2cEnd();
+//return (rc == 1) ? (iOldLen - iLen) : 0; // 0 indicates bad ack from sending a byte
+} /* I2CWrite() */
+
+int I2CRead(uint8_t addr, uint8_t *pData, int iLen)
+{
+   i2cBegin(addr, 1);
+   while (iLen--)
+   {
+      *pData++ = i2cByteIn(iLen == 0);
+   } // for each byte
+   i2cEnd();
+   return 1;
+} /* I2CRead() */
+
+int I2CTest(uint8_t addr)
+{
+int response = 0;
+
+   if (i2cBegin(addr, 0)) // try to write to the given address
+   {
+      response = 1;
+   }
+   i2cEnd();
+return response;
+} /* I2CTest() */
+
+#else // hardware I2C
+
+void I2CSetSpeed(int iSpeed)
+{
+    I2C_InitTypeDef I2C_InitTSturcture={0};
+
+    I2C_InitTSturcture.I2C_ClockSpeed = iSpeed;
+    I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;
+    I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9;
+    I2C_InitTSturcture.I2C_OwnAddress1 = 0x02; //address; sender's unimportant address
+    I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;
+    I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
+    I2C_Init( I2C1, &I2C_InitTSturcture );
+} /* I2CSetSpeed() */
+
+void I2CInit(uint8_t iSDA, uint8_t iSCL, int iSpeed)
+{
+	(void)iSDA; (void)iSCL;
+
+    GPIO_InitTypeDef GPIO_InitStructure={0};
+
+    // Fixed to pins C1/C2 for now
+    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE );
+    RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );
+
+    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
+    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
+    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+    GPIO_Init( GPIOC, &GPIO_InitStructure );
+
+    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
+    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
+    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+    GPIO_Init( GPIOC, &GPIO_InitStructure );
+
+    I2C_DeInit(I2C1);
+    I2CSetSpeed(iSpeed);
+
+    I2C_Cmd( I2C1, ENABLE );
+
+    I2C_AcknowledgeConfig( I2C1, ENABLE );
+    while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET );
+} /* I2CInit() */
+
+//
+// Returns 0 for timeout error
+// returns 1 for success
+//
+int I2CRead(uint8_t u8Addr, uint8_t *pData, int iLen)
+{
+	int iTimeout = 0;
+
+    I2C_GenerateSTART( I2C1, ENABLE );
+    while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) );
+
+    I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Receiver );
+
+    while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) ) {
+    	iTimeout++;
+    }
+    if (iTimeout >= 10000) return 0; // error
+
+    iTimeout = 0;
+    while(iLen && iTimeout < 10000)
+    {
+        if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) !=  RESET )
+        {
+        	iTimeout = 0;
+            pData[0] = I2C_ReceiveData( I2C1 );
+            pData++;
+            iLen--;
+        } else {
+        	iTimeout++;
+        }
+    }
+
+    I2C_GenerateSTOP( I2C1, ENABLE );
+    return (iLen == 0);
+
+} /* I2CRead() */
+
+void I2CWrite(uint8_t u8Addr, uint8_t *pData, int iLen)
+{
+    I2C_GenerateSTART( I2C1, ENABLE );
+    while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) );
+
+    I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Transmitter );
+
+    while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );
+
+    while(iLen)
+    {
+        if( I2C_GetFlagStatus( I2C1, I2C_FLAG_TXE ) !=  RESET )
+        {
+            I2C_SendData( I2C1, pData[0] );
+            pData++;
+            iLen--;
+        }
+    }
+
+    while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
+    I2C_GenerateSTOP( I2C1, ENABLE );
+
+} /* I2CWrite() */
+
+int I2CTest(uint8_t u8Addr)
+{
+	int iTimeout = 0;
+
+	I2C_ClearFlag(I2C1, I2C_FLAG_AF);
+    I2C_GenerateSTART( I2C1, ENABLE );
+    while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ) {
+    	iTimeout++;
+    }
+    if (iTimeout >= 10000) return 0; // no pull-ups, open bus
+
+    I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Transmitter );
+
+    while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) ) {
+    	iTimeout++;
+    }
+    if (iTimeout >= 10000) return 0; // no device at that address; the MTMS flag will never get set
+
+    I2C_GenerateSTOP( I2C1, ENABLE );
+    // check ACK failure flag
+    return (I2C_GetFlagStatus(I2C1, /*I2C_FLAG_TXE*/I2C_FLAG_AF) == RESET); // 0 = fail, 1 = succeed
+
+} /* I2CTest() */
+#endif // !BITBANG
+
+//
+// Read N bytes starting at a specific I2C internal register
+// returns 1 for success, 0 for error
+//
+void I2CReadRegister(uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen)
+{
+  I2CWrite(iAddr, &u8Register, 1);
+  I2CRead(iAddr, pData, iLen);
+} /* I2CReadRegister() */
+
+void SPI_begin(int iSpeed, int iMode)
+{
+uint32_t u32Prescaler = 0;
+
+    if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/2))
+        u32Prescaler = SPI_BaudRatePrescaler_2;
+    else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/4))
+        u32Prescaler = SPI_BaudRatePrescaler_4;
+    else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/8))
+        u32Prescaler = SPI_BaudRatePrescaler_8;
+    else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/16))
+        u32Prescaler = SPI_BaudRatePrescaler_16;
+    else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/32))
+        u32Prescaler = SPI_BaudRatePrescaler_32;
+    else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/64))
+        u32Prescaler = SPI_BaudRatePrescaler_64;
+    else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/128))
+        u32Prescaler = SPI_BaudRatePrescaler_128;
+    else
+        u32Prescaler = SPI_BaudRatePrescaler_256;
+
+        // Enable GPIOC and SPI
+        RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
+
+	// PC5 is SCK, 10MHz Output, alt func, p-p
+        GPIOC->CFGLR &= ~(0xf<<(4*5));
+        GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*5);
+
+        // PC6 is MOSI, 10MHz Output, alt func, p-p
+        GPIOC->CFGLR &= ~(0xf<<(4*6));
+        GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*6);
+
+        // Configure SPI 
+        SPI1->CTLR1 =
+                SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_8b |
+                SPI_Mode_Master | SPI_Direction_1Line_Tx |
+                u32Prescaler;
+
+	// Enable DMA on SPI
+	SPI1->CTLR2 |= SPI_I2S_DMAReq_Tx;
+
+        // enable SPI port
+        SPI1->CTLR1 |= CTLR1_SPE_Set;
+	
+    //SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); // enable DMA on transmit
+
+    //SPI_Cmd( SPI1, ENABLE );
+} /* SPI_begin() */
+
+// polling write
+void SPI_write(uint8_t *pData, int iLen)
+{
+int i = 0;
+
+    while (i < iLen) {
+        while(!(SPI1->STATR & SPI_STATR_TXE)); // wait for TXE
+        SPI1->DATAR = *pData++; // send data
+	i++;
+    }
+    while(SPI1->STATR & SPI_STATR_BSY); // wait for not busy
+} /* SPI_write() */
diff --git a/examples/color_lcd/color_lcd.c b/examples/color_lcd/color_lcd.c
new file mode 100644
index 0000000000000000000000000000000000000000..0856503db67f8b97037e9157d8b3c9bb0b16a8ae
--- /dev/null
+++ b/examples/color_lcd/color_lcd.c
@@ -0,0 +1,80 @@
+//
+// color LCD demo
+// written by Larry Bank
+// bitbank@pobox.com
+//
+// Copyright 2023 BitBank Software, Inc. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//    http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "ch32v_hal.inl"
+#include "spi_lcd.inl" 
+#include <stdlib.h>
+
+// Pin definitions for the LCD project protoboard
+//#define BL_PIN 0xd2
+//#define CS_PIN 0xff
+#define BL_PIN 0xd5
+#define CS_PIN 0xd2
+#define DC_PIN 0xd3
+#define RST_PIN 0xd4
+
+static uint16_t usPal[8] = {COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN,
+						    COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW};
+
+/* White Noise Generator State */
+#define NOISE_BITS 8
+#define NOISE_MASK ((1<<NOISE_BITS)-1)
+#define NOISE_POLY_TAP0 31
+#define NOISE_POLY_TAP1 21
+#define NOISE_POLY_TAP2 1
+#define NOISE_POLY_TAP3 0
+uint32_t lfsr = 1;
+
+/*
+ * random byte generator
+ */
+uint8_t rand8(void)
+{
+        uint8_t bit;
+        uint32_t new_data;
+
+        for(bit=0;bit<NOISE_BITS;bit++)
+        {
+                new_data = ((lfsr>>NOISE_POLY_TAP0) ^
+                                        (lfsr>>NOISE_POLY_TAP1) ^
+                                        (lfsr>>NOISE_POLY_TAP2) ^
+                                        (lfsr>>NOISE_POLY_TAP3));
+                lfsr = (lfsr<<1) | (new_data&1);
+        }
+
+        return lfsr&NOISE_MASK;
+}
+
+int main(void)
+{
+int i, dx, iColor;
+
+    SystemInit();
+
+    lcdInit(LCD_ST7735_80x160, 24000000, CS_PIN, DC_PIN, RST_PIN, BL_PIN);
+    lcdFill(COLOR_GREEN);
+    i = 1; dx = 1;
+    while (1) {
+    	lcdRectangle(rand8() & 127, rand8() & 63, rand8() & 63, rand8() & 31, usPal[(iColor+1)&7], 1);
+    	lcdEllipse(rand8() & 127, rand8() & 63, rand8() & 63, rand8() & 31, usPal[iColor & 7], 1);
+    	iColor++;
+    	lcdWriteString(0,i,"CH32V003 is fast", COLOR_BLUE, COLOR_GREEN, FONT_8x8);
+    	lcdWriteString(0,i+8,"enough for me!", COLOR_RED, COLOR_GREEN, FONT_8x8);
+    	lcdWriteString(0,i+16,"Large Font!", COLOR_WHITE, COLOR_MAGENTA, FONT_12x16);
+    	i += dx;
+    	if (i >= 39 || i == 1) dx = -dx;
+    }
+}
diff --git a/examples/color_lcd/funconfig.h b/examples/color_lcd/funconfig.h
new file mode 100644
index 0000000000000000000000000000000000000000..998cf76fede1479f162c72ef34d3a4d747430093
--- /dev/null
+++ b/examples/color_lcd/funconfig.h
@@ -0,0 +1,7 @@
+#ifndef _FUNCONFIG_H
+#define _FUNCONFIG_H
+
+#define CH32V003           1
+
+#endif
+
diff --git a/examples/color_lcd/spi_lcd.h b/examples/color_lcd/spi_lcd.h
new file mode 100644
index 0000000000000000000000000000000000000000..03f9d407c1f3d568636ef0e1b5301f302e28113f
--- /dev/null
+++ b/examples/color_lcd/spi_lcd.h
@@ -0,0 +1,113 @@
+//
+// spi_lcd.h
+// a Sitronix LCD display library
+// Created on: Sep 11, 2023
+// written by Larry Bank (bitbank@pobox.com)
+//
+//
+// Copyright 2023 BitBank Software, Inc. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//    http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===========================================================================
+//
+
+#ifndef USER_SPI_LCD_H_
+#define USER_SPI_LCD_H_
+
+#include "funconfig.h"
+#include <stdint.h>
+#include "ch32v_hal.h"
+#include <string.h>
+
+enum {
+	LCD_ST7735_80x160 = 0,
+	LCD_ST7735_80x160_B,
+	LCD_ST7735_128x128,
+	LCD_ST7735_128x160,
+	LCD_ST7789_135x240,
+	LCD_ST7789_172x320,
+	LCD_ST7789_240x240,
+	LCD_ST7789_240x280,
+	LCD_ST7789_240x320,
+	LCD_GC9107_128x128,
+	LCD_ILI9341_240x320,
+	LCD_COUNT
+};
+
+enum {
+	ORIENTATION_0 = 0,
+	ORIENTATION_90,
+	ORIENTATION_180,
+	ORIENTATION_270
+};
+
+#ifdef CH32V003
+// 2 complete lines across a 320 pixel color LCD
+#define CACHE_SIZE (320*2)
+#else // larger RAM systems
+#define CACHED_LINES 16
+// enough memory to hold 16 lines of the display for fast character drawing
+#define CACHE_SIZE (320*CACHED_LINES)
+#endif
+// memory offset of visible area (80x160 out of 240x320)
+
+// Proportional font data taken from Adafruit_GFX library
+/// Font data stored PER GLYPH
+typedef struct {
+  uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
+  uint8_t width;         ///< Bitmap dimensions in pixels
+  uint8_t height;        ///< Bitmap dimensions in pixels
+  uint8_t xAdvance;      ///< Distance to advance cursor (x axis)
+  int8_t xOffset;        ///< X dist from cursor pos to UL corner
+  int8_t yOffset;        ///< Y dist from cursor pos to UL corner
+} GFXglyph;
+
+typedef struct {
+  uint8_t *bitmap;  ///< Glyph bitmaps, concatenated
+  GFXglyph *glyph;  ///< Glyph array
+  uint8_t first;    ///< ASCII extents (first char)
+  uint8_t last;     ///< ASCII extents (last char)
+  uint8_t yAdvance; ///< Newline distance (y axis)
+} GFXfont;
+
+void lcdFill(uint16_t u16Color);
+void lcdInit(int iLCDType, uint32_t u32Speed, uint8_t u8CSPin, uint8_t u8DCPin, uint8_t u8RSTPin, uint8_t u8BLPin);
+void lcdWriteCMD(uint8_t ucCMD);
+void lcdWriteDATA(uint8_t *pData, int iLen);
+int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize);
+int lcdDrawTile(int x, int y, int iTileWidth, int iTileHeight, unsigned char *pTile, int iPitch);
+int lcdWriteStringCustom(GFXfont *pFont, int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int bBlank);
+void lcdOrientation(int iOrientation);
+void lcdRectangle(int x, int y, int cx, int cy, uint16_t usColor, int bFill);
+void lcdEllipse(int centerX, int centerY, int radiusX, int radiusY, uint16_t color, int bFilled);
+
+void memset16(uint16_t *u16Dest, uint16_t u16Pattern, int iLen);
+#define COLOR_BLACK 0
+#define COLOR_WHITE 0xffff
+#define COLOR_RED 0xf800
+#define COLOR_GREEN 0x7e0
+#define COLOR_BLUE 0x1f
+#define COLOR_MAGENTA 0xf81f
+#define COLOR_CYAN 0x7ff
+#define COLOR_YELLOW 0xffe0
+
+// MADCTL flip bits
+#define MADCTL_YFLIP 0x80
+#define MADCTL_XFLIP 0x40
+#define MADCTL_VFLIP 0x20
+
+enum {
+	FONT_6x8 = 0,
+	FONT_8x8,
+	FONT_12x16,
+	FONT_COUNT
+};
+
+#endif /* USER_SPI_LCD_H_ */
diff --git a/examples/color_lcd/spi_lcd.inl b/examples/color_lcd/spi_lcd.inl
new file mode 100644
index 0000000000000000000000000000000000000000..796267668336054f62bb6ff2509cbf8ee820becf
--- /dev/null
+++ b/examples/color_lcd/spi_lcd.inl
@@ -0,0 +1,1198 @@
+//
+// spi_lcd.c
+// a Sitronix LCD display library
+// Created on: Sep 11, 2023
+// written by Larry Bank (bitbank@pobox.com)
+//
+//
+// Copyright 2023 BitBank Software, Inc. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//    http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===========================================================================
+//
+
+#include "spi_lcd.h"
+
+static uint8_t u8CS, u8DC, u8BL;
+static uint8_t u8MADCTL; // original value
+static int iCursorX, iCursorY;
+static int iNativeWidth, iNativeHeight, iNativeXOff, iNativeYOff, iLCDWidth, iLCDHeight, iLCDPitch, iLCDXOff, iLCDYOff;
+static uint8_t u8Cache0[CACHE_SIZE]; // ping-pong data buffers
+static uint8_t u8Cache1[CACHE_SIZE];
+static uint8_t *pCache0 = u8Cache0, *pCache1 = u8Cache1;
+volatile int bDMA = 0;
+
+const uint8_t ucILI9341InitList[] = {
+        4, 0xEF, 0x03, 0x80, 0x02,
+        4, 0xCF, 0x00, 0XC1, 0X30,
+        5, 0xED, 0x64, 0x03, 0X12, 0X81,
+        4, 0xE8, 0x85, 0x00, 0x78,
+        6, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02,
+        2, 0xF7, 0x20,
+        3, 0xEA, 0x00, 0x00,
+        2, 0xc0, 0x23, // Power control
+        2, 0xc1, 0x10, // Power control
+        3, 0xc5, 0x3e, 0x28, // VCM control
+        2, 0xc7, 0x86, // VCM control2
+        2, 0x36, 0x48, // Memory Access Control
+        1, 0x20,        // non inverted
+        2, 0x3a, 0x55,
+        3, 0xb1, 0x00, 0x18,
+        4, 0xb6, 0x08, 0x82, 0x27, // Display Function Control
+        2, 0xF2, 0x00, // Gamma Function Disable
+        2, 0x26, 0x01, // Gamma curve selected
+        16, 0xe0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08,
+            0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma
+	    16, 0xe1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07,
+            0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma
+        3, 0xb1, 0x00, 0x10, // FrameRate Control 119Hz
+        0
+};
+
+const uint8_t uc240x240InitList[] = {
+    1, 0x13, // partial mode off
+    1, 0x21, // display inversion off
+    2, 0x36,0x60,    // memory access 0xc0 for 180 degree flipped
+    2, 0x3a,0x55,    // pixel format; 5=RGB565
+    3, 0x37,0x00,0x00, //
+    6, 0xb2,0x0c,0x0c,0x00,0x33,0x33, // Porch control
+    2, 0xb7,0x35,    // gate control
+    2, 0xbb,0x1a,    // VCOM
+    2, 0xc0,0x2c,    // LCM
+    2, 0xc2,0x01,    // VDV & VRH command enable
+    2, 0xc3,0x0b,    // VRH set
+    2, 0xc4,0x20,    // VDV set
+    2, 0xc6,0x0f,    // FR control 2
+    3, 0xd0, 0xa4, 0xa1,     // Power control 1
+    15, 0xe0, 0x00,0x19,0x1e,0x0a,0x09,0x15,0x3d,0x44,0x51,0x12,0x03,
+        0x00,0x3f,0x3f,     // gamma 1
+    15, 0xe1, 0x00,0x18,0x1e,0x0a,0x09,0x25,0x3f,0x43,0x52,0x33,0x03,
+        0x00,0x3f,0x3f,        // gamma 2
+    1, 0x29,    // display on
+    0
+};
+
+const uint8_t ucGC9107InitList[] = {
+    1, 0x13, // partial mode off
+    1, 0x21, // display inversion off
+    2, 0x36,0x68,    // memory access 0xc0 for 180 degree flipped
+    2, 0x3a,0x55,    // pixel format; 5=RGB565
+    3, 0x37,0x00,0x00, //
+    6, 0xb2,0x0c,0x0c,0x00,0x33,0x33, // Porch control
+    2, 0xb7,0x35,    // gate control
+    2, 0xbb,0x1a,    // VCOM
+    2, 0xc0,0x2c,    // LCM
+    2, 0xc2,0x01,    // VDV & VRH command enable
+    2, 0xc3,0x0b,    // VRH set
+    2, 0xc4,0x20,    // VDV set
+    2, 0xc6,0x0f,    // FR control 2
+    3, 0xd0, 0xa4, 0xa1,     // Power control 1
+    15, 0xe0, 0x00,0x19,0x1e,0x0a,0x09,0x15,0x3d,0x44,0x51,0x12,0x03,
+        0x00,0x3f,0x3f,     // gamma 1
+    15, 0xe1, 0x00,0x18,0x1e,0x0a,0x09,0x25,0x3f,0x43,0x52,0x33,0x03,
+        0x00,0x3f,0x3f,        // gamma 2
+    1, 0x29,    // display on
+    0
+};
+
+const uint8_t uc80InitList[] = {
+    2, 0x3a, 0x05,    // pixel format RGB565
+    2, 0x36, 0x68, // MADCTL (0/90/180/270 and color/inversion)
+    17, 0xe0, 0x09, 0x16, 0x09,0x20,
+    0x21,0x1b,0x13,0x19,
+    0x17,0x15,0x1e,0x2b,
+    0x04,0x05,0x02,0x0e, // gamma sequence
+    17, 0xe1, 0x0b,0x14,0x08,0x1e,
+    0x22,0x1d,0x18,0x1e,
+    0x1b,0x1a,0x24,0x2b,
+    0x06,0x06,0x02,0x0f,
+    1, 0x20,    // display inversion off
+	1, 0x29, // display on
+    0
+};
+
+const uint8_t uc160InitList[] = {
+        2, 0x3a, 0x05,  // pixel format RGB565
+        2, 0x36, 0x60, // MADCTL
+        17, 0xe0, 0x09, 0x16, 0x09,0x20,
+                0x21,0x1b,0x13,0x19,
+                0x17,0x15,0x1e,0x2b,
+                0x04,0x05,0x02,0x0e, // gamma sequence
+        17, 0xe1, 0x0b,0x14,0x08,0x1e,
+                0x22,0x1d,0x18,0x1e,
+                0x1b,0x1a,0x24,0x2b,
+                0x06,0x06,0x02,0x0f,
+			    1, 0x20,    // display inversion off
+				1, 0x29, // display on
+        0
+};
+const uint8_t uc128InitList[] = {
+        2, 0x3a, 0x05,  // pixel format RGB565
+        2, 0x36, 0x68, // MADCTL
+        17, 0xe0, 0x09, 0x16, 0x09,0x20,
+                0x21,0x1b,0x13,0x19,
+                0x17,0x15,0x1e,0x2b,
+                0x04,0x05,0x02,0x0e, // gamma sequence
+        17, 0xe1, 0x0b,0x14,0x08,0x1e,
+                0x22,0x1d,0x18,0x1e,
+                0x1b,0x1a,0x24,0x2b,
+                0x06,0x06,0x02,0x0f,
+			    1, 0x20,    // display inversion off
+				1, 0x29, // display on
+        0
+};
+const uint8_t ucFont[]PROGMEM = {
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00,
+  0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14,
+  0x24,0x2e,0x2a,0x6b,0x6b,0x3a,0x12,0x46,0x66,0x30,0x18,0x0c,0x66,0x62,
+  0x30,0x7a,0x4f,0x5d,0x37,0x7a,0x48,0x00,0x04,0x07,0x03,0x00,0x00,0x00,
+  0x00,0x1c,0x3e,0x63,0x41,0x00,0x00,0x00,0x41,0x63,0x3e,0x1c,0x00,0x00,
+  0x08,0x2a,0x3e,0x1c,0x3e,0x2a,0x08,0x00,0x08,0x08,0x3e,0x3e,0x08,0x08,
+  0x00,0x00,0x80,0xe0,0x60,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,
+  0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,
+  0x3e,0x7f,0x59,0x4d,0x47,0x7f,0x3e,0x40,0x42,0x7f,0x7f,0x40,0x40,0x00,
+  0x62,0x73,0x59,0x49,0x6f,0x66,0x00,0x22,0x63,0x49,0x49,0x7f,0x36,0x00,
+  0x18,0x1c,0x16,0x53,0x7f,0x7f,0x50,0x27,0x67,0x45,0x45,0x7d,0x39,0x00,
+  0x3c,0x7e,0x4b,0x49,0x79,0x30,0x00,0x03,0x03,0x71,0x79,0x0f,0x07,0x00,
+  0x36,0x7f,0x49,0x49,0x7f,0x36,0x00,0x06,0x4f,0x49,0x69,0x3f,0x1e,0x00,
+  0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x80,0xe6,0x66,0x00,0x00,
+  0x08,0x1c,0x36,0x63,0x41,0x00,0x00,0x00,0x14,0x14,0x14,0x14,0x14,0x14,
+  0x00,0x41,0x63,0x36,0x1c,0x08,0x00,0x00,0x02,0x03,0x59,0x5d,0x07,0x02,
+  0x3e,0x7f,0x41,0x5d,0x5d,0x5f,0x0e,0x7c,0x7e,0x13,0x13,0x7e,0x7c,0x00,
+  0x41,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x1c,0x3e,0x63,0x41,0x41,0x63,0x22,
+  0x41,0x7f,0x7f,0x41,0x63,0x3e,0x1c,0x41,0x7f,0x7f,0x49,0x5d,0x41,0x63,
+  0x41,0x7f,0x7f,0x49,0x1d,0x01,0x03,0x1c,0x3e,0x63,0x41,0x51,0x33,0x72,
+  0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00,0x00,0x41,0x7f,0x7f,0x41,0x00,0x00,
+  0x30,0x70,0x40,0x41,0x7f,0x3f,0x01,0x41,0x7f,0x7f,0x08,0x1c,0x77,0x63,
+  0x41,0x7f,0x7f,0x41,0x40,0x60,0x70,0x7f,0x7f,0x0e,0x1c,0x0e,0x7f,0x7f,
+  0x7f,0x7f,0x06,0x0c,0x18,0x7f,0x7f,0x1c,0x3e,0x63,0x41,0x63,0x3e,0x1c,
+  0x41,0x7f,0x7f,0x49,0x09,0x0f,0x06,0x1e,0x3f,0x21,0x31,0x61,0x7f,0x5e,
+  0x41,0x7f,0x7f,0x09,0x19,0x7f,0x66,0x26,0x6f,0x4d,0x49,0x59,0x73,0x32,
+  0x03,0x41,0x7f,0x7f,0x41,0x03,0x00,0x7f,0x7f,0x40,0x40,0x7f,0x7f,0x00,
+  0x1f,0x3f,0x60,0x60,0x3f,0x1f,0x00,0x3f,0x7f,0x60,0x30,0x60,0x7f,0x3f,
+  0x63,0x77,0x1c,0x08,0x1c,0x77,0x63,0x07,0x4f,0x78,0x78,0x4f,0x07,0x00,
+  0x47,0x63,0x71,0x59,0x4d,0x67,0x73,0x00,0x7f,0x7f,0x41,0x41,0x00,0x00,
+  0x01,0x03,0x06,0x0c,0x18,0x30,0x60,0x00,0x41,0x41,0x7f,0x7f,0x00,0x00,
+  0x08,0x0c,0x06,0x03,0x06,0x0c,0x08,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+  0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x20,0x74,0x54,0x54,0x3c,0x78,0x40,
+  0x41,0x7f,0x3f,0x48,0x48,0x78,0x30,0x38,0x7c,0x44,0x44,0x6c,0x28,0x00,
+  0x30,0x78,0x48,0x49,0x3f,0x7f,0x40,0x38,0x7c,0x54,0x54,0x5c,0x18,0x00,
+  0x48,0x7e,0x7f,0x49,0x03,0x06,0x00,0x98,0xbc,0xa4,0xa4,0xf8,0x7c,0x04,
+  0x41,0x7f,0x7f,0x08,0x04,0x7c,0x78,0x00,0x44,0x7d,0x7d,0x40,0x00,0x00,
+  0x60,0xe0,0x80,0x84,0xfd,0x7d,0x00,0x41,0x7f,0x7f,0x10,0x38,0x6c,0x44,
+  0x00,0x41,0x7f,0x7f,0x40,0x00,0x00,0x7c,0x7c,0x18,0x78,0x1c,0x7c,0x78,
+  0x7c,0x78,0x04,0x04,0x7c,0x78,0x00,0x38,0x7c,0x44,0x44,0x7c,0x38,0x00,
+  0x84,0xfc,0xf8,0xa4,0x24,0x3c,0x18,0x18,0x3c,0x24,0xa4,0xf8,0xfc,0x84,
+  0x44,0x7c,0x78,0x4c,0x04,0x0c,0x18,0x48,0x5c,0x54,0x74,0x64,0x24,0x00,
+  0x04,0x04,0x3e,0x7f,0x44,0x24,0x00,0x3c,0x7c,0x40,0x40,0x3c,0x7c,0x40,
+  0x1c,0x3c,0x60,0x60,0x3c,0x1c,0x00,0x3c,0x7c,0x60,0x30,0x60,0x7c,0x3c,
+  0x44,0x6c,0x38,0x10,0x38,0x6c,0x44,0x9c,0xbc,0xa0,0xa0,0xfc,0x7c,0x00,
+  0x4c,0x64,0x74,0x5c,0x4c,0x64,0x00,0x08,0x08,0x3e,0x77,0x41,0x41,0x00,
+  0x00,0x00,0x00,0x77,0x77,0x00,0x00,0x41,0x41,0x77,0x3e,0x08,0x08,0x00,
+  0x02,0x03,0x01,0x03,0x02,0x03,0x01,0x70,0x78,0x4c,0x46,0x4c,0x78,0x70};
+  // 5x7 font (in 6x8 cell)
+const uint8_t ucSmallFont[] PROGMEM = {
+0x00,0x00,0x00,0x00,0x00,
+0x00,0x06,0x5f,0x06,0x00,
+0x07,0x03,0x00,0x07,0x03,
+0x24,0x7e,0x24,0x7e,0x24,
+0x24,0x2b,0x6a,0x12,0x00,
+0x63,0x13,0x08,0x64,0x63,
+0x36,0x49,0x56,0x20,0x50,
+0x00,0x07,0x03,0x00,0x00,
+0x00,0x3e,0x41,0x00,0x00,
+0x00,0x41,0x3e,0x00,0x00,
+0x08,0x3e,0x1c,0x3e,0x08,
+0x08,0x08,0x3e,0x08,0x08,
+0x00,0xe0,0x60,0x00,0x00,
+0x08,0x08,0x08,0x08,0x08,
+0x00,0x60,0x60,0x00,0x00,
+0x20,0x10,0x08,0x04,0x02,
+0x3e,0x51,0x49,0x45,0x3e,
+0x00,0x42,0x7f,0x40,0x00,
+0x62,0x51,0x49,0x49,0x46,
+0x22,0x49,0x49,0x49,0x36,
+0x18,0x14,0x12,0x7f,0x10,
+0x2f,0x49,0x49,0x49,0x31,
+0x3c,0x4a,0x49,0x49,0x30,
+0x01,0x71,0x09,0x05,0x03,
+0x36,0x49,0x49,0x49,0x36,
+0x06,0x49,0x49,0x29,0x1e,
+0x00,0x6c,0x6c,0x00,0x00,
+0x00,0xec,0x6c,0x00,0x00,
+0x08,0x14,0x22,0x41,0x00,
+0x24,0x24,0x24,0x24,0x24,
+0x00,0x41,0x22,0x14,0x08,
+0x02,0x01,0x59,0x09,0x06,
+0x3e,0x41,0x5d,0x55,0x1e,
+0x7e,0x11,0x11,0x11,0x7e,
+0x7f,0x49,0x49,0x49,0x36,
+0x3e,0x41,0x41,0x41,0x22,
+0x7f,0x41,0x41,0x41,0x3e,
+0x7f,0x49,0x49,0x49,0x41,
+0x7f,0x09,0x09,0x09,0x01,
+0x3e,0x41,0x49,0x49,0x7a,
+0x7f,0x08,0x08,0x08,0x7f,
+0x00,0x41,0x7f,0x41,0x00,
+0x30,0x40,0x40,0x40,0x3f,
+0x7f,0x08,0x14,0x22,0x41,
+0x7f,0x40,0x40,0x40,0x40,
+0x7f,0x02,0x04,0x02,0x7f,
+0x7f,0x02,0x04,0x08,0x7f,
+0x3e,0x41,0x41,0x41,0x3e,
+0x7f,0x09,0x09,0x09,0x06,
+0x3e,0x41,0x51,0x21,0x5e,
+0x7f,0x09,0x09,0x19,0x66,
+0x26,0x49,0x49,0x49,0x32,
+0x01,0x01,0x7f,0x01,0x01,
+0x3f,0x40,0x40,0x40,0x3f,
+0x1f,0x20,0x40,0x20,0x1f,
+0x3f,0x40,0x3c,0x40,0x3f,
+0x63,0x14,0x08,0x14,0x63,
+0x07,0x08,0x70,0x08,0x07,
+0x71,0x49,0x45,0x43,0x00,
+0x00,0x7f,0x41,0x41,0x00,
+0x02,0x04,0x08,0x10,0x20,
+0x00,0x41,0x41,0x7f,0x00,
+0x04,0x02,0x01,0x02,0x04,
+0x80,0x80,0x80,0x80,0x80,
+0x00,0x03,0x07,0x00,0x00,
+0x20,0x54,0x54,0x54,0x78,
+0x7f,0x44,0x44,0x44,0x38,
+0x38,0x44,0x44,0x44,0x28,
+0x38,0x44,0x44,0x44,0x7f,
+0x38,0x54,0x54,0x54,0x08,
+0x08,0x7e,0x09,0x09,0x00,
+0x18,0xa4,0xa4,0xa4,0x7c,
+0x7f,0x04,0x04,0x78,0x00,
+0x00,0x00,0x7d,0x40,0x00,
+0x40,0x80,0x84,0x7d,0x00,
+0x7f,0x10,0x28,0x44,0x00,
+0x00,0x00,0x7f,0x40,0x00,
+0x7c,0x04,0x18,0x04,0x78,
+0x7c,0x04,0x04,0x78,0x00,
+0x38,0x44,0x44,0x44,0x38,
+0xfc,0x44,0x44,0x44,0x38,
+0x38,0x44,0x44,0x44,0xfc,
+0x44,0x78,0x44,0x04,0x08,
+0x08,0x54,0x54,0x54,0x20,
+0x04,0x3e,0x44,0x24,0x00,
+0x3c,0x40,0x20,0x7c,0x00,
+0x1c,0x20,0x40,0x20,0x1c,
+0x3c,0x60,0x30,0x60,0x3c,
+0x6c,0x10,0x10,0x6c,0x00,
+0x9c,0xa0,0x60,0x3c,0x00,
+0x64,0x54,0x54,0x4c,0x00,
+0x08,0x3e,0x41,0x41,0x00,
+0x00,0x00,0x77,0x00,0x00,
+0x00,0x41,0x41,0x3e,0x08,
+0x02,0x01,0x02,0x01,0x00,
+0x3c,0x26,0x23,0x26,0x3c};
+
+void DMA1_Channel3_IRQHandler(void) __attribute__((interrupt));
+
+//
+// This function gets called when the current DMA transaction completes
+// We use this to disable the chip select (CS) signal on the LCD
+// and clear our volatile flag (DMA busy)
+//
+void DMA1_Channel3_IRQHandler(void)
+{
+	        // why is this needed? Can't just direct compare the reg in tests below
+        volatile uint16_t intfr = DMA1->INTFR;
+
+	if (intfr & DMA1_IT_TC3) {
+		DMA1->INTFCR = DMA1_IT_TC3;
+		DMA1_Channel3->CFGR &= ~DMA_CFGR1_EN;
+		if (u8CS != 0xff)
+			digitalWrite(u8CS, 1); // de-activate CS
+		bDMA = 0; // no longer active DMA transaction
+	}
+} /* DMA1_Channel3_IRQHandler() */
+
+//
+// Initialize the DMA channel for SPI transmit (3)
+//
+void DMA_Tx_Init(DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
+{
+	RCC->AHBPCENR |= RCC_AHBPeriph_DMA1; // Enable DMA peripheral
+        //DMA1_Channel3 is for SPI 
+        DMA1_Channel3->PADDR = ppadr;
+        DMA1_Channel3->MADDR = memadr;
+        DMA1_Channel3->CNTR  = bufsize;
+        DMA1_Channel3->CFGR  =
+                DMA_M2M_Disable |
+                DMA_Priority_VeryHigh |
+                DMA_MemoryDataSize_Byte |
+                DMA_PeripheralDataSize_Byte |
+                DMA_MemoryInc_Enable |
+                DMA_Mode_Normal |
+                DMA_DIR_PeripheralDST |
+                DMA_IT_TC;
+
+        NVIC_EnableIRQ( DMA1_Channel3_IRQn );
+        DMA1_Channel3->CFGR |= DMA_CFGR1_EN;
+} /* DMA_Tx_Init() */
+//
+// Faster way to write a 16-bit pattern into memory
+//
+void memset16(uint16_t *u16Dest, uint16_t u16Pattern, int iLen)
+{
+	uint32_t *u32D, u32;
+	u32 = u16Pattern | (u16Pattern << 16);
+	if (((uint32_t)u16Dest & 3) == 0) {
+		u32D = (uint32_t *)u16Dest;
+		while (iLen >= 2) {
+			*u32D++ = u32;
+			iLen -= 2;
+		}
+		u16Dest = (uint16_t *)u32D;
+	}
+	while (iLen) {
+		*u16Dest += u16Pattern;
+		iLen--;
+	}
+} /* memset16() */
+
+//
+// Write a command byte to the LCD (D/C = LOW)
+//
+void lcdWriteCMD(uint8_t ucCMD)
+{
+	while (bDMA) {}; // wait for old transaction to complete
+	digitalWrite(u8DC, 0);
+	if (u8CS != 0xff)
+		digitalWrite(u8CS, 0);
+	SPI_write(&ucCMD, 1);
+	if (u8CS != 0xff)
+		digitalWrite(u8CS, 1);
+	digitalWrite(u8DC, 1);
+
+} /* lcdWriteCMD() */
+//
+// Write a block of data to the LCD (D/C = high)
+// Use DMA if the block is longer than a certain threshold
+//
+void lcdWriteDATA(uint8_t *pData, int iLen)
+{
+	uint8_t *p;
+	while (bDMA) {}; // wait for old transaction to complete
+	if (iLen >= 32) { // arbitrary cutoff point
+		if (u8CS != 0xff)
+			digitalWrite(u8CS, 0); // activate CS
+		DMA1_Channel3->CNTR = iLen;
+		DMA1_Channel3->MADDR = (uint32_t)pCache0;
+
+		DMA1_Channel3->CFGR |= DMA_CFGR1_EN; //		DMA_Cmd(DMA1_Channel3, ENABLE); // have DMA send the data
+		bDMA = 1; // tell our code that DMA is currently active for next time
+		// swap buffers
+		p = pCache0;
+		pCache0 = pCache1;
+		pCache1 = p;
+	} else {
+		if (u8CS != 0xff)
+			digitalWrite(u8CS, 0);
+		SPI_write(pData, iLen);
+		if (u8CS != 0xff)
+			digitalWrite(u8CS, 1);
+	}
+} /* lcdWriteDATA() */
+
+//
+// Initialize the GPIOs needed to control the LCD
+// and send the command sequence to configure the display
+//
+void lcdInit(int iLCDType, uint32_t u32Speed, uint8_t u8CSPin, uint8_t u8DCPin, uint8_t u8RSTPin, uint8_t u8BLPin)
+{
+//    uint8_t iBGR = 0;
+	uint8_t *s = NULL;
+	int iCount;
+
+	if (iLCDType < 0 || iLCDType >= LCD_COUNT) return;
+	switch (iLCDType) {
+	case LCD_ST7789_135x240:
+		iNativeWidth = iLCDWidth = 240; // initialize in landscape mode
+		iNativeHeight = iLCDHeight = 135;
+		iNativeXOff = iLCDXOff = 40;
+		iNativeYOff = iLCDYOff = 53;
+		u8MADCTL = 0x60;
+	    s = (unsigned char *)uc240x240InitList;
+		break;
+	case LCD_ST7789_172x320:
+		iNativeWidth = iLCDWidth = 320; // initialize in landscape mode
+		iNativeHeight = iLCDHeight = 172;
+		iNativeXOff = iLCDXOff = 0;
+		iNativeYOff = iLCDYOff = 34;
+		u8MADCTL = 0x60;
+	    s = (unsigned char *)uc240x240InitList;
+		break;
+	case LCD_ST7789_240x280:
+		iNativeWidth = iLCDWidth = 280; // initialize in landscape mode
+		iNativeHeight = iLCDHeight = 240;
+		iNativeXOff = iLCDXOff = 20;
+		iNativeYOff = iLCDYOff = 0;
+		u8MADCTL = 0x60;
+	    s = (unsigned char *)uc240x240InitList;
+		break;
+	case LCD_ST7735_80x160:
+		iNativeWidth = iLCDWidth = 160; // initialize in landscape mode
+		iNativeHeight = iLCDHeight = 80;
+		iNativeXOff = iLCDXOff = 0;
+		iNativeYOff = iLCDYOff = 24;
+		u8MADCTL = 0x68;
+	    s = (unsigned char *)uc80InitList;
+		break;
+	case LCD_ILI9341_240x320:
+		iNativeWidth = iLCDWidth = 240;
+		iNativeHeight = iLCDHeight = 320;
+		iNativeXOff = iLCDXOff = 0;
+		iNativeYOff = iLCDYOff = 0;
+		u8MADCTL = 0x60;
+	    s = (unsigned char *)ucILI9341InitList;
+		break;
+	case LCD_GC9107_128x128:
+		iNativeWidth = iLCDWidth = 128;
+		iNativeHeight = iLCDHeight = 128;
+		iNativeXOff = iLCDXOff = 1;
+		iNativeYOff = iLCDYOff = 2;
+		u8MADCTL = 0x68;
+	    s = (unsigned char *)ucGC9107InitList;
+		break;
+	case LCD_ST7735_128x128:
+		iNativeWidth = iLCDWidth = 128;
+		iNativeHeight = iLCDHeight = 128;
+		iNativeXOff = iLCDXOff = 1;
+		iNativeYOff = iLCDYOff = 0;
+		u8MADCTL = 0x68;
+	    s = (unsigned char *)uc128InitList;
+		break;
+	case LCD_ST7735_128x160:
+		iNativeWidth = iLCDWidth = 160;
+		iNativeHeight = iLCDHeight = 128;
+		iNativeXOff = iLCDXOff = 0;
+		iNativeYOff = iLCDYOff = 0;
+		u8MADCTL = 0x60;
+	    s = (unsigned char *)uc160InitList;
+		break;
+	} // switch on LCD type
+	iLCDPitch = iLCDWidth*2;
+	u8CS = u8CSPin;
+	if (u8CSPin != 0xff) {
+		pinMode(u8CSPin, OUTPUT);
+		digitalWrite(u8CSPin, 1);
+	}
+	pinMode(u8RSTPin, OUTPUT);
+	digitalWrite(u8RSTPin, 0); // reset the display controller
+	Delay_Ms(100);
+	digitalWrite(u8RSTPin, 1);
+	Delay_Ms(200);
+
+	SPI_begin(u32Speed, 0);
+	u8DC = u8DCPin;
+	pinMode(u8DCPin, OUTPUT);
+	u8BL = u8BLPin;
+	pinMode(u8BL, OUTPUT);
+	digitalWrite(u8BL, 1); // turn on backlight
+//    if (pLCD->iLCDFlags & FLAGS_SWAP_RB)
+//        iBGR = 8;
+	lcdWriteCMD(0x01); // SW reset
+	Delay_Ms(200);
+	lcdWriteCMD(0x11); // sleep out
+	Delay_Ms(100);
+    iCount = 1;
+     while (s && iCount)
+     {
+		 iCount = *s++;
+		 if (iCount != 0)
+		 {
+			 lcdWriteCMD(s[0]);
+			 lcdWriteDATA(&s[1], iCount-1);
+			 s += iCount;
+		 } // if count
+     }// while
+     DMA_Tx_Init(DMA1_Channel3, (u32)&SPI1->DATAR, (u32)pCache0, 0);
+
+} /* lcdInit() */
+
+//
+// Sitronix LCDs support 4 possible orientations through use of the
+// X, Y and V mirror/flip bits
+// V swaps the X/Y axis
+//
+void lcdOrientation(int iOrientation)
+{
+	uint8_t u8 = u8MADCTL; // original value
+
+	switch (iOrientation) {
+	case ORIENTATION_0: // use original MADCTL value
+		iLCDWidth = iNativeWidth;
+		iLCDHeight = iNativeHeight;
+		iLCDPitch = iLCDWidth * 2;
+		iLCDXOff = iNativeXOff;
+		iLCDYOff = iNativeYOff;
+		break;
+	case ORIENTATION_90:
+		u8 ^= MADCTL_XFLIP;
+		u8 ^= MADCTL_VFLIP;
+		iLCDWidth = iNativeHeight;
+		iLCDHeight = iNativeWidth;
+		iLCDPitch = iLCDWidth * 2;
+		iLCDXOff = iNativeYOff;
+		iLCDYOff = iNativeXOff;
+		break;
+	case ORIENTATION_180:
+		u8 ^= MADCTL_XFLIP;
+		u8 ^= MADCTL_YFLIP;
+		iLCDWidth = iNativeWidth;
+		iLCDHeight = iNativeHeight;
+		iLCDPitch = iLCDWidth * 2;
+		iLCDXOff = iNativeXOff;
+		iLCDYOff = iNativeYOff;
+		break;
+	case ORIENTATION_270:
+		u8 ^= MADCTL_YFLIP;
+		u8 ^= MADCTL_VFLIP;
+		iLCDWidth = iNativeHeight;
+		iLCDHeight = iNativeWidth;
+		iLCDPitch = iLCDWidth * 2;
+		iLCDXOff = iNativeYOff;
+		iLCDYOff = iNativeXOff;
+		break;
+	}
+	 lcdWriteCMD(0x36); // MADCTL
+	 lcdWriteDATA(&u8, 1);
+} /* lcdOrientation() */
+
+//
+// Set the memory window (AKA write position)
+//
+void lcdSetPosition(int x, int y, int w, int h)
+{
+uint8_t ucBuf[8];
+
+     x += iLCDXOff;
+     y += iLCDYOff;
+     ucBuf[0] = (unsigned char)(x >> 8);
+     ucBuf[1] = (unsigned char)x;
+     x = x + w - 1;
+     ucBuf[2] = (unsigned char)(x >> 8);
+     ucBuf[3] = (unsigned char)x;
+     lcdWriteCMD(0x2a); // column address cmd
+     lcdWriteDATA(ucBuf, 4);
+     ucBuf[0] = (unsigned char)(y >> 8);
+     ucBuf[1] = (unsigned char)y;
+     y = y + h - 1;
+     ucBuf[2] = (unsigned char)(y >> 8);
+     ucBuf[3] = (unsigned char)y;
+     lcdWriteCMD(0x2b); // row address cmd
+     lcdWriteDATA(ucBuf, 4);
+     lcdWriteCMD(0x2c); // RAMWR - start writing
+} /* lcdSetPosition() */
+
+//
+// For drawing ellipses, a circle is drawn and the x and y pixels are scaled by a 16-bit integer fraction
+// This function draws a single pixel and scales its position based on the x/y fraction of the ellipse
+//
+void DrawScaledPixel(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor)
+{
+    if (iXFrac != 0x10000) x = (x * iXFrac) >> 16;
+    if (iYFrac != 0x10000) y = (y * iYFrac) >> 16;
+    x += iCX; y += iCY;
+    if (x < 0 || x >= iLCDWidth || y < 0 || y >= iLCDHeight)
+        return; // off the screen
+    lcdSetPosition(x, y, 1, 1);
+    lcdWriteDATA((uint8_t *)&usColor, 2);
+} /* DrawScaledPixel() */
+//
+// Draw the given x/y symmetrically across the center point
+// as a continuous line
+//
+void DrawScaledLine(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor)
+{
+    int32_t iLen, x2;
+    if (iXFrac != 0x10000) x = (x * iXFrac) >> 16;
+    if (iYFrac != 0x10000) y = (y * iYFrac) >> 16;
+    x2 = iCX + x;
+    x = iCX - x;
+    y += iCY;
+    if (x < 0) x = 0;
+    if (x2 >= iLCDWidth) x2 = iLCDWidth-1;
+    iLen = x2 - x + 1; // new length
+    lcdSetPosition(x, y, iLen, 1);
+    memset16((uint16_t *)pCache0, usColor, iLen);
+    lcdWriteDATA(pCache0, iLen*2);
+} /* DrawScaledLine() */
+//
+// Draw the 8 pixels around the Bresenham circle
+// (scaled to make an ellipse)
+//
+void BresenhamCircle(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor, int bFill)
+{
+    if (bFill) // draw a filled ellipse
+    {
+        // for a filled ellipse, draw 4 lines instead of 8 pixels
+        DrawScaledLine(iCX, iCY, y, x, iXFrac, iYFrac, usColor);
+        DrawScaledLine(iCX, iCY, y, -x, iXFrac, iYFrac, usColor);
+        DrawScaledLine(iCX, iCY, x, y, iXFrac, iYFrac, usColor);
+        DrawScaledLine(iCX, iCY, x, -y, iXFrac, iYFrac, usColor);
+    }
+    else // draw 8 pixels around the edges
+    {
+        DrawScaledPixel(iCX, iCY, x, y, iXFrac, iYFrac, usColor);
+        DrawScaledPixel(iCX, iCY, -x, y, iXFrac, iYFrac, usColor);
+        DrawScaledPixel(iCX, iCY, x, -y, iXFrac, iYFrac, usColor);
+        DrawScaledPixel(iCX, iCY, -x, -y, iXFrac, iYFrac, usColor);
+        DrawScaledPixel(iCX, iCY, y, x, iXFrac, iYFrac, usColor);
+        DrawScaledPixel(iCX, iCY, -y, x, iXFrac, iYFrac, usColor);
+        DrawScaledPixel(iCX, iCY, y, -x, iXFrac, iYFrac, usColor);
+        DrawScaledPixel(iCX, iCY, -y, -x, iXFrac, iYFrac, usColor);
+    }
+} /* BresenhamCircle() */
+//
+// Draw an ellipse (a circle if Rx==Ry)
+//
+void lcdEllipse(int iCenterX, int iCenterY, int iRadiusX, int iRadiusY, uint16_t usColor, int bFill)
+{
+    int32_t iRadius, iXFrac, iYFrac;
+    int32_t iDelta, x, y;
+
+    if (iRadiusX > iRadiusY) // use X as the primary radius
+    {
+        iRadius = iRadiusX;
+        iXFrac = 65536;
+        iYFrac = (iRadiusY * 65536) / iRadiusX;
+    }
+    else
+    {
+        iRadius = iRadiusY;
+        iXFrac = (iRadiusX * 65536) / iRadiusY;
+        iYFrac = 65536;
+    }
+    usColor = __builtin_bswap16(usColor); // swap byte order
+    iDelta = 3 - (2 * iRadius);
+    x = 0; y = iRadius;
+    while (x < y)
+    {
+        BresenhamCircle(iCenterX, iCenterY, x, y, iXFrac, iYFrac, usColor, bFill);
+        x++;
+        if (iDelta < 0)
+        {
+            iDelta += (4*x) + 6;
+        }
+        else
+        {
+            iDelta += 4 * (x-y) + 10;
+            y--;
+        }
+    }
+} /* lcdEllipse() */
+//
+// Draw an outline or filled rectangle
+// requesting a rectangle which draws past an edge will result it in being clipped to the edge
+//
+void lcdRectangle(int x, int y, int cx, int cy, uint16_t usColor, int bFill)
+{
+	int ty;
+	if (x >= iLCDWidth || y >= iLCDHeight) return; // not visible
+	if (x < 0) {
+		cx += x;
+		x = 0;
+	} else if (x + cx > iLCDWidth) cx = iLCDWidth - cx;
+	if (y < 0) {
+		cy += y;
+		y = 0;
+	} else if (y + cy > iLCDHeight) cy = iLCDHeight - cy;
+	usColor = __builtin_bswap16(usColor);
+	if (bFill) { // draw a filled rectangle
+		lcdSetPosition(x, y, cx, cy);
+		for (ty = 0; ty < cy; ty++) {
+			if (ty < 2) memset16((uint16_t *)pCache0, usColor, cx); // need to do it 2 times for buffers
+			lcdWriteDATA(pCache0, cx*2);
+		} // for ty
+	} else { // outline rectangle
+		// each side is drawn as a straight line
+		lcdSetPosition(x, y, cx, 1); // top line
+		memset16((uint16_t *)pCache0, usColor, cx);
+		lcdWriteDATA(pCache0, cx*2);
+		lcdSetPosition(x, y+cy-1, cx, 1); // bottom line
+		memset16((uint16_t *)pCache0, usColor, cx);
+		lcdWriteDATA(pCache0, cx*2);
+		lcdSetPosition(x, y, 1, cy); // left line
+		memset16((uint16_t *)pCache0, usColor, cy);
+		lcdWriteDATA(pCache0, cy*2);
+		lcdSetPosition(x+cx-1, y, 1, cy); // right line
+		memset16((uint16_t *)pCache0, usColor, cy);
+		lcdWriteDATA(pCache0, cy*2);
+	}
+} /* lcdRectangle() */
+//
+// Draw a NxN RGB565 tile
+// This reverses the pixel byte order and sets a memory "window"
+// of pixels so that the write can occur in one shot
+//
+int lcdDrawTile(int x, int y, int iTileWidth, int iTileHeight, unsigned char *pTile, int iPitch)
+{
+    int i, j;
+    uint16_t *s16, *d16;
+
+    if (iTileWidth*iTileHeight*2 > CACHE_SIZE) {
+        return -1; // tile must fit in SPI cache
+    }
+    // First convert to big-endian order
+    d16 = (uint16_t *)pCache0;
+    for (j=0; j<iTileHeight; j++)
+    {
+        s16 = (uint16_t*)&pTile[j*iPitch];
+        for (i=0; i<iTileWidth; i++)
+        {
+            *d16++ = __builtin_bswap16(*s16++);
+        } // for i;
+    } // for j
+    lcdSetPosition(x, y, iTileWidth, iTileHeight);
+    lcdWriteDATA(pCache0, iTileWidth*iTileHeight*2);
+    return 0;
+} /* lcdDrawTile() */
+//
+// Fill the display with a solid color
+//
+void lcdFill(uint16_t usData)
+{
+	int cx, cy;
+	uint16_t *d;
+
+    usData = (usData >> 8) | (usData << 8); // swap hi/lo byte for LCD
+    lcdSetPosition(0,0, iLCDWidth, iLCDHeight);
+    // fit within our temp buffer
+    for (cy = 0; cy < iLCDHeight; cy++) {
+    	if (cy < 2) { // both buffers need a copy
+    		d = (uint16_t *)pCache0; // pointer swapped after each write
+    		for (cx = 0; cx < iLCDWidth; cx++) {
+    			d[cx] = usData;
+    		}
+    	}
+        lcdWriteDATA(pCache0, iLCDWidth*2); // fill with data words
+   } // for y
+
+} /* lcdFill() */
+
+//
+// Draw a 1-bpp pattern with the given color and translucency
+// 1 bits are drawn as color, 0 are transparent
+// The translucency value can range from 1 (barely visible) to 32 (fully opaque)
+// If there is a backbuffer, the bitmap is draw only into memory
+// If there is no backbuffer, the bitmap is drawn on the screen with a black background color
+//
+void spilcdDrawPattern(uint8_t *pPattern, int iSrcPitch, int iDestX, int iDestY, int iCX, int iCY, uint16_t usColor)
+{
+    int x, y;
+    uint8_t *s, uc, ucMask;
+    uint16_t *d, u16Clr;
+
+     if (iDestX+iCX > iLCDWidth) // trim to fit on display
+         iCX = (iLCDWidth - iDestX);
+     if (iDestY+iCY > iLCDHeight)
+         iCY = (iLCDHeight - iDestY);
+     if (pPattern == NULL || iDestX < 0 || iDestY < 0 || iCX <=0 || iCY <= 0)
+         return;
+       u16Clr = (usColor >> 8) | (usColor << 8); // swap low/high bytes
+       lcdSetPosition(iDestX, iDestY, iCX, iCY);
+       for (y=0; y<iCY; y++)
+       {
+         s = &pPattern[y * iSrcPitch];
+         ucMask = uc = 0;
+         d = (uint16_t *)pCache0;
+         for (x=0; x<iCX; x++)
+         {
+             ucMask >>= 1;
+             if (ucMask == 0)
+             {
+                 ucMask = 0x80;
+                 uc = *s++;
+             }
+             if (uc & ucMask) // active pixel
+                *d++ = u16Clr;
+             else
+                *d++ = 0;
+         } // for x
+         lcdWriteDATA(pCache0, iCX*2);
+       } // for y
+} /* spilcdDrawPattern() */
+
+//
+// Draw a string of text with the built-in fonts
+//
+#if (CACHE_SIZE < 4096)
+// This slower version uses one DMA transaction per character
+int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize)
+{
+int i, j, k, iLen;
+unsigned char *s;
+unsigned short usFG = (usFGColor >> 8) | (usFGColor << 8);
+unsigned short usBG = (usBGColor >> 8) | (usBGColor << 8);
+
+    if (x == -1)
+        x = iCursorX;
+    if (y == -1)
+        y = iCursorY;
+    if (x < 0 || y < 0) return -1;
+	iLen = strlen(szMsg);
+    if (iFontSize == FONT_8x8 || iFontSize == FONT_6x8) // draw the 6x8 or 8x8 font
+	{
+		uint16_t *usD;
+        int cx;
+        uint8_t *pFont;
+
+        cx = (iFontSize == FONT_8x8) ? 8:6;
+        pFont = (iFontSize == FONT_8x8) ? (uint8_t *)ucFont : (uint8_t *)ucSmallFont;
+		if ((cx*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/cx; // can't display it all
+		if (iLen < 0)return -1;
+
+		for (i=0; i<iLen; i++)
+		{
+			s = &pFont[((unsigned char)szMsg[i]-32) * (cx-1)];
+			usD = (uint16_t *)pCache0;
+            lcdSetPosition(x+(i*cx), y, cx, 8);
+            uint8_t ucMask = 1;
+            for (k=0; k<8; k++) // for each scanline
+            {
+				for (j=0; j<cx-1; j++)
+				{
+					if (s[j] & ucMask)
+						*usD++ = usFG;
+					else
+						*usD++ = usBG;
+				} // for j
+				*usD++ = usBG; // last column is blank
+                ucMask <<= 1;
+            } // for k
+            // write the data in one shot
+            lcdWriteDATA(pCache0, cx*2*8);
+		} // for each character
+        x += (i*cx);
+    } // 6x8 and 8x8
+    if (iFontSize == FONT_12x16) // 6x8 stretched to 12x16 (with smoothing)
+    {
+        uint16_t *usD;
+
+        if ((12*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/12; // can't display it all
+        if (iLen < 0) return -1;
+
+        for (i=0; i<iLen; i++)
+        {
+            s = (uint8_t *)&ucSmallFont[((unsigned char)szMsg[i]-32) * 5];
+            usD = (uint16_t *)pCache0;;
+            lcdSetPosition(x+(i*12), y, 12, 16);
+            uint8_t ucMask = 1;
+            for (k=0; k<12*16; k++)
+               usD[k] = usBG;
+            for (k=0; k<8; k++) // for each scanline
+            {
+				uint8_t c0, c1;
+				for (j=0; j<5; j++)
+				{
+					c0 = s[j];
+					if (c0 & ucMask)
+					   usD[0] = usD[1] = usD[12] = usD[13] = usFG;
+					// test for smoothing diagonals
+					if (k < 7 && j < 5) {
+					   uint8_t ucMask2 = ucMask << 1;
+					   c1 = s[j+1];
+					   if ((c0 & ucMask) && (~c1 & ucMask) && (~c0 & ucMask2) && (c1 & ucMask2)) // first diagonal condition
+						   usD[14] = usD[25] = usFG;
+					   else if ((~c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask2) && (~c1 & ucMask2))
+						   usD[13] = usD[26] = usFG;
+					} // if not on last row and last col
+					usD+=2;
+				} // for j
+				usD[0] = usD[1] = usD[12] = usD[13] = usBG; // last column is blank
+				usD += 2;
+                usD += 12; // skip the extra line
+                ucMask <<= 1;
+            } // for k
+        // write the data in one shot
+        lcdWriteDATA(pCache0, 12*16*2);
+        } // for each character
+        x += i*12;
+    } // FONT_12x16
+    iCursorX = x;
+    iCursorY = y;
+	return 0;
+} /* lcdWriteString() */
+#else
+// This faster version uses one DMA transaction per line of text
+int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize)
+{
+int i, j, k, iLen;
+int iStride;
+uint8_t *s;
+uint16_t usFG = (usFGColor >> 8) | ((usFGColor & -1)<< 8);
+uint16_t usBG = (usBGColor >> 8) | ((usBGColor & -1)<< 8);
+uint16_t *usD;
+int cx;
+uint8_t *pFont;
+
+    if (iFontSize < 0 || iFontSize >= FONT_COUNT)
+        return -1; // invalid size
+    if (x == -1)
+        x = iCursorX;
+    if (y == -1)
+        y = iCursorY;
+    if (x < 0) return -1;
+    iLen = strlen(szMsg);
+
+    if (iFontSize == FONT_12x16) {
+        if ((12*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/12; // can't display it all
+        if (iLen < 0) return -1;
+        iStride = iLen*12;
+        lcdSetPosition(x, y, iStride, 16);
+        usD = (uint16_t *)pCache0;
+        for (i=0; i<iStride*16; i++)
+           usD[i] = usBG; // set to background color first
+        for (k = 0; k<8; k++) { // create a pair of scanlines from each original
+           uint8_t ucMask = (1 << k);
+           usD = (unsigned short *)&pCache0[k*iStride*4];
+           for (i=0; i<iLen; i++)
+           {
+               uint8_t c0, c1;
+               s = (uint8_t *)&ucSmallFont[((unsigned char)szMsg[i]-32) * 5];
+               for (j=1; j<6; j++)
+               {
+                   uint8_t ucMask1 = ucMask << 1;
+                   uint8_t ucMask2 = ucMask >> 1;
+                   c0 = s[j-1];
+                   if (c0 & ucMask)
+                      usD[0] = usD[1] = usD[iStride] = usD[iStride+1] = usFG;
+                   // test for smoothing diagonals
+                   if (j < 5) {
+                      c1 = s[j];
+                      if ((c0 & ucMask) && (~c1 & ucMask) && (~c0 & ucMask1) && (c1 & ucMask1)) { // first diagonal condition
+                          usD[iStride+2] = usFG;
+                      } else if ((~c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask1) && (~c1 & ucMask1)) { // second condition
+                          usD[iStride+1] = usFG;
+                      }
+                      if ((c0 & ucMask2) && (~c1 & ucMask2) && (~c0 & ucMask) && (c1 & ucMask)) { // repeat for previous line
+                          usD[1] = usFG;
+                      } else if ((~c0 & ucMask2) && (c1 & ucMask2) && (c0 & ucMask) && (~c1 & ucMask)) {
+                          usD[2] = usFG;
+                      }
+                   }
+                   usD+=2;
+               } // for j
+               usD += 2; // leave "6th" column blank
+            } // for each character
+        } // for each scanline
+        lcdWriteDATA(pCache0, iStride*32);
+        return 0;
+    } // 12x16
+
+    cx = (iFontSize == FONT_8x8) ? 8:6;
+    pFont = (iFontSize == FONT_8x8) ? (uint8_t *)ucFont : (uint8_t *)ucSmallFont;
+    if ((cx*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/cx; // can't display it all
+    iStride = iLen * cx*2;
+    for (i=0; i<iLen; i++)
+    {
+        s = &pFont[((unsigned char)szMsg[i]-32) * (cx-1)];
+        uint8_t ucMask = 1;
+        for (k=0; k<8; k++) // for each scanline
+        {
+            usD = (unsigned short *)&pCache0[(k*iStride) + (i * cx*2)];
+            for (j=0; j<cx-1; j++)
+            {
+                if (s[j] & ucMask)
+                    *usD++ = usFG;
+                else
+                    *usD++ = usBG;
+            } // for j
+	    *usD++ = usBG; // blank column
+            ucMask <<= 1;
+        } // for k
+    } // for i
+    // write the data in one shot
+    lcdSetPosition(x, y, cx*iLen, 8);
+    lcdWriteDATA(pCache0, iLen*cx*16);
+    iCursorX = x + (cx*iLen);
+    iCursorY = y;
+    return 0;
+} /* lcdWriteString() */
+#endif // cache size
+//
+// Draw a string in a proportional font you supply
+//
+int lcdWriteStringCustom(GFXfont *pFont, int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int bBlank)
+{
+int i, /*j, iLen, */ k, dx, dy, cx, cy, c, iBitOff;
+int tx, ty;
+uint8_t *s, bits, uc;
+GFXfont font;
+GFXglyph glyph, *pGlyph;
+#define TEMP_BUF_SIZE 64
+#define TEMP_HIGHWATER (TEMP_BUF_SIZE-8)
+uint16_t *d;
+
+   if (pFont == NULL)
+      return -1;
+    if (x == -1)
+        x = iCursorX;
+    if (y == -1)
+        y = iCursorY;
+    if (x < 0)
+        return -1;
+   // in case of running on AVR, get copy of data from FLASH
+   memcpy(&font, pFont, sizeof(font));
+   pGlyph = &glyph;
+   usFGColor = (usFGColor >> 8) | (usFGColor << 8); // swap h/l bytes
+   usBGColor = (usBGColor >> 8) | (usBGColor << 8);
+
+   i = 0;
+   while (szMsg[i] && x < iLCDWidth)
+   {
+      c = szMsg[i++];
+      if (c < font.first || c > font.last) // undefined character
+         continue; // skip it
+      c -= font.first; // first char of font defined
+      memcpy_P(&glyph, &font.glyph[c], sizeof(glyph));
+      // set up the destination window (rectangle) on the display
+      dx = x + pGlyph->xOffset; // offset from character UL to start drawing
+      dy = y + pGlyph->yOffset;
+      cx = pGlyph->width;
+      cy = pGlyph->height;
+      iBitOff = 0; // bitmap offset (in bits)
+      if (dy + cy > iLCDHeight)
+         cy = iLCDHeight - dy; // clip bottom edge
+      else if (dy < 0) {
+         cy += dy;
+         iBitOff += (pGlyph->width * (-dy));
+         dy = 0;
+      }
+      if (dx + cx > iLCDWidth)
+         cx = iLCDWidth - dx; // clip right edge
+      s = font.bitmap + pGlyph->bitmapOffset; // start of bitmap data
+      // Bitmap drawing loop. Image is MSB first and each pixel is packed next
+      // to the next (continuing on to the next character line)
+      bits = uc = 0; // bits left in this font byte
+
+      if (bBlank) { // erase the areas around the char to not leave old bits
+         int miny, maxy;
+         c = '0' - font.first;
+         miny = y + pGlyph->yOffset;
+         c = 'y' - font.first;
+         maxy = miny + pGlyph->height;
+         if (maxy > iLCDHeight)
+            maxy = iLCDHeight;
+         cx = pGlyph->xAdvance;
+         if (cx + x > iLCDWidth) {
+            cx = iLCDWidth - x;
+         }
+         lcdSetPosition(x, miny, cx, maxy-miny);
+            // blank out area above character
+//            cy = font.yAdvance - pGlyph->height;
+//            for (ty=miny; ty<miny+cy && ty < maxy; ty++) {
+//               for (tx=0; tx<cx; tx++)
+//                  u16Temp[tx] = usBGColor;
+//               myspiWrite(pLCD, (uint8_t *)u16Temp, cx*sizeof(uint16_t), MODE_DATA, iFlags);
+//            } // for ty
+            // character area (with possible padding on L+R)
+            for (ty=0; ty<pGlyph->height && ty+miny < maxy; ty++) {
+               d = (uint16_t *)pCache0;
+               for (tx=0; tx<pGlyph->xOffset && tx < cx; tx++) { // left padding
+                  *d++ = usBGColor;
+               }
+            // character bitmap (center area)
+               for (tx=0; tx<pGlyph->width; tx++) {
+                  if (bits == 0) { // need more data
+                     uc = s[iBitOff>>3];
+                     bits = 8;
+                     iBitOff += bits;
+                  }
+                  if (tx + pGlyph->xOffset < cx) {
+                     *d++ = (uc & 0x80) ? usFGColor : usBGColor;
+                  }
+                  bits--;
+                  uc <<= 1;
+               } // for tx
+               // right padding
+               k = pGlyph->xAdvance - (int)(d - (uint16_t*)pCache0); // remaining amount
+               for (tx=0; tx<k && (tx+pGlyph->xOffset+pGlyph->width) < cx; tx++)
+                  *d++ = usBGColor;
+               lcdWriteDATA(pCache0, cx*sizeof(uint16_t));
+            } // for ty
+            // padding below the current character
+            ty = y + pGlyph->yOffset + pGlyph->height;
+            for (; ty < maxy; ty++) {
+            	d = (uint16_t *)pCache0;
+               for (tx=0; tx<cx; tx++)
+                  d[tx] = usBGColor;
+               lcdWriteDATA(pCache0, cx*sizeof(uint16_t));
+            } // for ty
+      } else if (usFGColor == usBGColor) { // transparent
+          int iCount; // opaque pixel count
+          d = (uint16_t*)pCache0;
+          for (iCount=0; iCount < cx; iCount++)
+              d[iCount] = usFGColor; // set up a line of solid color
+          iCount = 0; // number of sequential opaque pixels
+             for (ty=0; ty<cy; ty++) {
+             for (tx=0; tx<pGlyph->width; tx++) {
+                if (bits == 0) { // need to read more font data
+                   uc = s[iBitOff>>3]; // get more font bitmap data
+                   bits = 8 - (iBitOff & 7); // we might not be on a byte boundary
+                   iBitOff += bits; // because of a clipped line
+                   uc <<= (8-bits);
+                } // if we ran out of bits
+                if (tx < cx) {
+                    if (uc & 0x80) {
+                        iCount++; // one more opaque pixel
+                    } else { // any opaque pixels to write?
+                        if (iCount) {
+                            lcdSetPosition(dx+tx-iCount, dy+ty, iCount, 1);
+                       d = (uint16_t *)pCache0; // point to start of output buffer
+                          lcdWriteDATA(pCache0, iCount*sizeof(uint16_t));
+                            iCount = 0;
+                        } // if opaque pixels to write
+                    } // if transparent pixel hit
+                }
+                bits--; // next bit
+                uc <<= 1;
+             } // for tx
+             } // for ty
+       // quicker drawing
+      } else { // just draw the current character box fast
+         lcdSetPosition(dx, dy, cx, cy);
+            d = (uint16_t *)pCache0; // point to start of output buffer
+            for (ty=0; ty<cy; ty++) {
+            for (tx=0; tx<pGlyph->width; tx++) {
+               if (bits == 0) { // need to read more font data
+                  uc = s[iBitOff>>3]; // get more font bitmap data
+                  bits = 8 - (iBitOff & 7); // we might not be on a byte boundary
+                  iBitOff += bits; // because of a clipped line
+                  uc <<= (8-bits);
+                  k = (int)(d-(uint16_t*)pCache0); // number of words in output buffer
+                  if (k >= TEMP_HIGHWATER) { // time to write it
+                     lcdWriteDATA(pCache0, k*sizeof(uint16_t));
+                     d = (uint16_t*)pCache0;
+                  }
+               } // if we ran out of bits
+               if (tx < cx) {
+                  *d++ = (uc & 0x80) ? usFGColor : usBGColor;
+               }
+               bits--; // next bit
+               uc <<= 1;
+            } // for tx
+            } // for ty
+            k = (int)(d-(uint16_t*)pCache0);
+            if (k) // write any remaining data
+               lcdWriteDATA(pCache0, k*sizeof(uint16_t));
+      } // quicker drawing
+      x += pGlyph->xAdvance; // width of this character
+   } // while drawing characters
+    iCursorX = x;
+    iCursorY = y;
+   return 0;
+} /* lcdWriteStringCustom() */
+// end of spi_lcd.inl
diff --git a/examples/direct_gpio/direct_gpio.c b/examples/direct_gpio/direct_gpio.c
index 313ef15be1a9d48c6c34154c2eb148005e144044..884b3c5636ddf6c27133aa32b18dbebca708e44b 100644
--- a/examples/direct_gpio/direct_gpio.c
+++ b/examples/direct_gpio/direct_gpio.c
@@ -1,8 +1,6 @@
 #include "ch32v003fun.h"
 #include <stdio.h>
 
-uint32_t count;
-
 int main()
 {
 	SystemInit();
@@ -41,7 +39,6 @@ int main()
 		GPIOC->OUTDR &= ~(1<<(4)); // CLEAR GPIO C4
 
 		Delay_Ms( 50 );
-		count++;
 	}
 }
 
diff --git a/examples/spi_max7219/Makefile b/examples/spi_max7219/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4017a9d934dfa7593e6cef4f7978e23c2507a522
--- /dev/null
+++ b/examples/spi_max7219/Makefile
@@ -0,0 +1,10 @@
+all : flash
+
+TARGET:=spi_max7219
+
+include ../../ch32v003fun/ch32v003fun.mk
+
+flash : cv_flash
+clean : cv_clean
+
+
diff --git a/examples/spi_max7219/README.md b/examples/spi_max7219/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2605e21cfb9e5dd76301cfa2a13b61cb164a0094
--- /dev/null
+++ b/examples/spi_max7219/README.md
@@ -0,0 +1,19 @@
+# MAX7219 8 digit 7 segment display demo
+
+This example for the `max7219_spi_driver` and `max7219_spi_driver_extended` library demonstrates controlling a MAX7219 or MAX7221 based display over SPI with basic and advanced text writing, and at least one example of every available display function in some capacity.
+
+---
+
+The MAX7219 and MAX7221 chipsets are used in many 8 character 7 segment and 8x8 single colour dot matrix displays which can be purchased preassembled on eBay and AliExpress very cheaply. The abundance and low cost of these displays makes them a great companion for small projects requiring a display output that is more capable than single LEDs but not as complex as something like an LCD.
+
+---
+
+The example expects you to connect a MAX7219 based 7 segment 8 character display to your CH32V003 like so:
+- `DIN` / `MOSI` to `PC6`
+- `SCLK` to `PC5`
+- `CS` to `PD0`
+
+You can choose which examples will be shown on the display by changing the `spi_max7219.c` file in the marked demo selection section. All examples are enabled by default.
+
+Once running, you'll see one of the many examples being displayed like so:
+![Demo of the MAX7219 based 8 digit 7 segment display connected to a CH32V003 dev board, in operation](demo_pic.jpg)
\ No newline at end of file
diff --git a/examples/spi_max7219/demo_pic.jpg b/examples/spi_max7219/demo_pic.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..33b15eb2cab5b746ab7105910e78c82bf92cab2e
Binary files /dev/null and b/examples/spi_max7219/demo_pic.jpg differ
diff --git a/examples/spi_max7219/funconfig.h b/examples/spi_max7219/funconfig.h
new file mode 100644
index 0000000000000000000000000000000000000000..37a2af8d0bacc627e3dc7745feecaf1bed95d6ae
--- /dev/null
+++ b/examples/spi_max7219/funconfig.h
@@ -0,0 +1,6 @@
+#ifndef _FUNCONFIG_H
+#define _FUNCONFIG_H
+
+#define CH32V003           1
+
+#endif
diff --git a/examples/spi_max7219/max7219_spi_driver.h b/examples/spi_max7219/max7219_spi_driver.h
new file mode 100644
index 0000000000000000000000000000000000000000..0a2ec35729de3d32b16df6302cc7c9bce38a5866
--- /dev/null
+++ b/examples/spi_max7219/max7219_spi_driver.h
@@ -0,0 +1,236 @@
+/* 
+ * SPI based driver for the MAX7219 display driver (https://www.analog.com/media/en/technical-documentation/data-sheets/MAX7219-MAX7221.pdf)
+ * 
+ * The driver can be used for 64 discrete LED control, or for 8 digits of 7 segment (+ decimal) displays. It includes a basic 7 segment display font called Code B
+ * which is made available with this library, as well as various other functions such as multiplexer scan limiting, digital brightness control and font decoding.
+ * 
+ * The one-byte segment arrangement in the digit registers appear like so:
+ * 
+ *     A
+ *    ---
+ * F | G | B
+ *    --- 
+ * E |   | C
+ *    --- 
+ *     D   . DP
+ * 
+ * +-----+----+----+----+----+----+----+----+----+
+ * | Bit | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
+ * +-----+----+----+----+----+----+----+----+----+
+ * | Seg | DP |  A |  B |  C |  D |  E |  F |  G |
+ * +-----+----+----+----+----+----+----+----+----+
+ * 
+ * On standard 8 digit boards, the 0th digit (MAX7219_REGISTER_DIGIT0) is on the right and the 7th digit (MAX7219_REGISTER_DIGIT7) is on the left.
+ */
+
+//Include guard
+#ifndef MAX7219_SPI_DRIVER_H
+#define MAX7219_SPI_DRIVER_H
+
+//Includes
+#include "ch32v003_SPI.h"
+#include <stdbool.h>
+
+//Instance struct
+struct MAX7219_Display
+{
+    uint8_t displays_in_chain;
+
+    GPIO_TypeDef* chip_select_port;
+    uint8_t chip_select_pin;
+
+    uint8_t last_set_decode_mode;
+};
+
+//Chip select methods
+void MAX7219_select(struct MAX7219_Display display)
+{
+    display.chip_select_port->BSHR |= 0b00000001 << display.chip_select_pin << 16; //Reset pin (active low)
+}
+
+void MAX7219_deselect(struct MAX7219_Display display)
+{
+    display.chip_select_port->BSHR |= 0b00000001 << display.chip_select_pin; //Set pin (active low)
+}
+
+//Raw communication
+#define MAX7219_REGISTER_NOOP 0x00
+/*#define MAX7219_REGISTER_DIGIT0 0x01
+#define MAX7219_REGISTER_DIGIT1 0x02
+#define MAX7219_REGISTER_DIGIT2 0x03
+#define MAX7219_REGISTER_DIGIT3 0x04
+#define MAX7219_REGISTER_DIGIT4 0x05
+#define MAX7219_REGISTER_DIGIT5 0x06
+#define MAX7219_REGISTER_DIGIT6 0x07
+#define MAX7219_REGISTER_DIGIT7 0x08*/
+#define MAX7219_REGISTER_DECODE_MODE 0x09
+#define MAX7219_REGISTER_INTENSITY 0x0A
+#define MAX7219_REGISTER_SCANLIMIT 0x0B
+#define MAX7219_REGISTER_SHUTDOWN 0x0C
+#define MAX7219_REGISTER_DISPLAYTEST 0x0F
+
+void MAX7219_write_register(struct MAX7219_Display display, uint8_t reg, uint8_t data)
+{
+    reg &= 0b00001111; //Remove the top 4 bits as they are not used for the register, and only retain the last 4 bits
+
+    uint16_t packet = reg << 8; //Apply the register address to the final packet in the top 8 bits
+
+    packet |= data; //Apply the data to the final packet in the bottom 8 bits
+
+    MAX7219_select(display); //Select the chip select line
+
+    //Write the packet to the display
+    SPI_begin_16();
+
+    SPI_write_16(packet);
+    SPI_wait_transmit_finished();
+    
+    SPI_end();
+
+    MAX7219_deselect(display); //Deselect the chip select line
+}
+
+//Register helpers
+void MAX7219_shutdown(struct MAX7219_Display display, bool set)
+{
+    MAX7219_write_register(display, MAX7219_REGISTER_SHUTDOWN, !set);
+}
+
+void MAX7219_test(struct MAX7219_Display display, bool set)
+{
+    MAX7219_write_register(display, MAX7219_REGISTER_DISPLAYTEST, set);
+}
+
+#define MAX7219_DECODE_MODE_NONE 0x00
+#define MAX7219_DECODE_MODE_0_ONLY 0x01
+#define MAX7219_DECODE_MODE_0_TO_3_ONLY 0x0F
+#define MAX7219_DECODE_MODE_ALL 0xFF
+
+void MAX7219_set_decode_mode(struct MAX7219_Display display, uint8_t mode)
+{
+    MAX7219_write_register(display, MAX7219_REGISTER_DECODE_MODE, mode);
+    display.last_set_decode_mode = mode;
+}
+
+#define MAX7219_SCANLIMIT_0_ONLY 0x00
+#define MAX7219_SCANLIMIT_01 0x01
+#define MAX7219_SCANLIMIT_012 0x02
+#define MAX7219_SCANLIMIT_0123 0x03
+#define MAX7219_SCANLIMIT_01234 0x04
+#define MAX7219_SCANLIMIT_012345 0x05
+#define MAX7219_SCANLIMIT_0123456 0x06
+#define MAX7219_SCANLIMIT_ALL 0x07
+
+void MAX7219_set_scan_limit(struct MAX7219_Display display, uint8_t limit)
+{
+    limit &= 0b00000111; //Only accept the 3 lsbs for this register
+    MAX7219_write_register(display, MAX7219_REGISTER_SCANLIMIT, limit);
+}
+
+#define MAX7219_BRIGHTNESS_MAX 0x0F
+#define MAX7219_MRIGHTNESS_MIN 0x00
+
+void MAX7219_set_brightness(struct MAX7219_Display display, uint8_t brightness)
+{
+    brightness &= 0b00001111; //Only accept the 4 lsbs for this register
+    MAX7219_write_register(display, MAX7219_REGISTER_INTENSITY, brightness);
+}
+
+//Built in CODE B font
+#define MAX7219_CODEB_ADD_DECPOINT 0x80
+#define MAX7219_CODEB_DASH 0x0A
+#define MAX7219_CODEB_BLANK 0x0F
+#define MAX7219_CODEB_0 0x00
+#define MAX7219_CODEB_1 0x01
+#define MAX7219_CODEB_2 0x02
+#define MAX7219_CODEB_3 0x03
+#define MAX7219_CODEB_4 0x04
+#define MAX7219_CODEB_5 0x05
+#define MAX7219_CODEB_6 0x06
+#define MAX7219_CODEB_7 0x07
+#define MAX7219_CODEB_8 0x08
+#define MAX7219_CODEB_9 0x09
+#define MAX7219_CODEB_E 0x0B
+#define MAX7219_CODEB_H 0x0C
+#define MAX7219_CODEB_L 0x0D
+#define MAX7219_CODEB_P 0x0E
+
+//Raw segment
+#define MAX7219_SEGMENT_DP 0b10000000
+#define MAX7219_SEGMENT_A  0b01000000
+#define MAX7219_SEGMENT_B  0b00100000
+#define MAX7219_SEGMENT_C  0b00010000
+#define MAX7219_SEGMENT_D  0b00001000
+#define MAX7219_SEGMENT_E  0b00000100
+#define MAX7219_SEGMENT_F  0b00000010
+#define MAX7219_SEGMENT_G  0b00000001
+
+void MAX7219_set_digit(struct MAX7219_Display display, uint32_t digit, uint8_t value)
+{
+    uint32_t literalDigit = digit % 8;
+    //uint32_t displayIndex = digit / 8;
+
+    MAX7219_write_register(display, literalDigit + 1, value);
+}
+
+void MAX7219_reset(struct MAX7219_Display display)
+{
+    //Set display brightness to maximum
+    MAX7219_set_brightness(display, MAX7219_BRIGHTNESS_MAX);
+
+    //Set the scan limit to all 8 digits enabled
+    MAX7219_set_scan_limit(display, MAX7219_SCANLIMIT_ALL);
+
+    //Enable Code-B decode on all digits
+    MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL);
+
+    //Clear all digits
+    for (size_t digitPos = 0; digitPos < display.displays_in_chain * 8; digitPos++)
+    {
+        MAX7219_set_digit(display, digitPos, MAX7219_CODEB_BLANK);
+    }
+
+    //Take the display out of shutdown
+    MAX7219_shutdown(display, false);
+
+    //Take the display out of test mode
+    MAX7219_test(display, false);
+}
+
+void MAX7219_init(struct MAX7219_Display display)
+{
+    //Default deselected
+    MAX7219_deselect(display);
+
+    //Ensure port is enabled
+    uint32_t selectedPortAddress = (uint32_t)display.chip_select_port;
+
+    switch (selectedPortAddress)
+    {
+        case GPIOA_BASE:
+            RCC->APB2PCENR |= RCC_APB2Periph_GPIOA;
+            break;
+        
+        case GPIOC_BASE:
+            RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;
+            break;
+
+        case GPIOD_BASE:
+            RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
+            break;
+        
+        default: break;
+    }
+
+    //Enable push-pull on pin
+    display.chip_select_port->CFGLR &= ~(0xf<<(4*display.chip_select_pin));
+    display.chip_select_port->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*display.chip_select_pin);
+
+    //Initialise SPI
+    SPI_init();
+
+    //Clear display to driver defaults
+    MAX7219_reset(display);
+}
+
+#endif //MAX7219_SPI_DRIVER_H include guard
\ No newline at end of file
diff --git a/examples/spi_max7219/max7219_spi_driver_extended.h b/examples/spi_max7219/max7219_spi_driver_extended.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb791c2236ca6a0e9ea4758f887728fe651d7b59
--- /dev/null
+++ b/examples/spi_max7219/max7219_spi_driver_extended.h
@@ -0,0 +1,60 @@
+//Include guard
+#ifndef MAX7219_SPI_DRIVER_EXTENDED_H
+#define MAX7219_SPI_DRIVER_EXTENDED_H
+
+//Includes
+#include "max7219_spi_driver.h"
+
+//---Extended Font---
+//Letters
+#define MAX7219_EXTFONT_A MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_B MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_C MAX7219_SEGMENT_A | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F
+#define MAX7219_EXTFONT_C_LOWER MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_D MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_E MAX7219_SEGMENT_A | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_F MAX7219_SEGMENT_A | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_H MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_H_LOWER MAX7219_SEGMENT_C | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_I MAX7219_SEGMENT_E | MAX7219_SEGMENT_F
+#define MAX7219_EXTFONT_I_LOWER MAX7219_SEGMENT_E
+#define MAX7219_EXTFONT_L MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F
+#define MAX7219_EXTFONT_L_LOWER MAX7219_SEGMENT_E | MAX7219_SEGMENT_F
+#define MAX7219_EXTFONT_N MAX7219_SEGMENT_C | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_O MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F
+#define MAX7219_EXTFONT_O_LOWER MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_P MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_S MAX7219_SEGMENT_A | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_T MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_U MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F
+#define MAX7219_EXTFONT_U_LOWER MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E
+#define MAX7219_EXTFONT_X MAX7219_SEGMENT_C | MAX7219_SEGMENT_F
+#define MAX7219_EXTFONT_Y MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_Z MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G
+
+//Numbers
+#define MAX7219_EXTFONT_0 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F
+#define MAX7219_EXTFONT_1 MAX7219_SEGMENT_B | MAX7219_SEGMENT_C
+#define MAX7219_EXTFONT_2 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_3 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_4 MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_5 MAX7219_SEGMENT_A | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_6 MAX7219_SEGMENT_A | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_7 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C
+#define MAX7219_EXTFONT_8 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_9 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+
+//Symbols
+#define MAX7219_EXTFONT_BLANK 0x00
+#define MAX7219_EXTFONT_DEGREES MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_EQUALS_TOP MAX7219_SEGMENT_A | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_EQUALS_BOTTOM MAX7219_SEGMENT_D | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_TRIEQUALS MAX7219_SEGMENT_A | MAX7219_SEGMENT_D | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_HYPHEN MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_UNDERSCORE MAX7219_SEGMENT_D
+#define MAX7219_EXTFONT_OVERSCORE MAX7219_SEGMENT_A
+#define MAX7219_EXTFONT_FORWARDSLASH MAX7219_SEGMENT_B | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_BACKSLASH MAX7219_SEGMENT_C | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G
+#define MAX7219_EXTFONT_DECIMAL MAX7219_SEGMENT_DP
+
+#endif //MAX7219_SPI_DRIVER_EXTENDED_H include guard
\ No newline at end of file
diff --git a/examples/spi_max7219/spi_max7219.c b/examples/spi_max7219/spi_max7219.c
new file mode 100644
index 0000000000000000000000000000000000000000..a430a2c5b89337dc7c463cf6ac7e364473aa14a0
--- /dev/null
+++ b/examples/spi_max7219/spi_max7219.c
@@ -0,0 +1,691 @@
+#define CH32V003_SPI_SPEED_HZ 1000000
+
+#define CH32V003_SPI_IMPLEMENTATION
+#define CH32V003_SPI_DIRECTION_1LINE_TX
+#define CH32V003_SPI_CLK_MODE_POL0_PHA0
+#define CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL
+
+//Control which demos you want to see here
+#define DEMO_DISPLAY_TEST
+#define DEMO_CODEB
+#define DEMO_EXTENDED_FONT
+#define DEMO_HELLO_BLINK
+#define DEMO_FIGURES_OF_EIGHT
+#define DEMO_TOGETHER_TWIRLS
+#define DEMO_COUNTER_TWIRLS
+#define DEMO_KNIGHTRIDER_DECIMAL
+#define DEMO_KNIGHTRIDER_DASH
+#define DEMO_INTENSITY_FADE_DECIMALS
+#define DEMO_INTENSITY_FADE_DIGITS
+#define DEMO_LED_CHASE
+
+#include "ch32v003fun.h"
+#include <stdio.h>
+#include "max7219_spi_driver.h"
+#include "max7219_spi_driver_extended.h"
+
+//MOSI on PC6, SCLK on PC5, software controlled CS on PD0
+
+int main()
+{
+    SystemInit();
+
+    struct MAX7219_Display display =
+    {
+        .displays_in_chain = 1,
+
+        .chip_select_port = GPIOD,
+        .chip_select_pin = 0
+    };
+
+    MAX7219_init(display);
+
+    while(1)
+    {
+        #ifdef DEMO_DISPLAY_TEST
+        //Display test on
+        MAX7219_test(display, true);
+        Delay_Ms(1000);
+
+        //Display test off
+        MAX7219_test(display, false);
+        #endif
+
+        #ifdef DEMO_CODEB
+        //Code B non-decimal
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL);
+
+        MAX7219_set_digit(display, 7, MAX7219_CODEB_0);
+        MAX7219_set_digit(display, 6, MAX7219_CODEB_1);
+        MAX7219_set_digit(display, 5, MAX7219_CODEB_2);
+        MAX7219_set_digit(display, 4, MAX7219_CODEB_3);
+        MAX7219_set_digit(display, 3, MAX7219_CODEB_4);
+        MAX7219_set_digit(display, 2, MAX7219_CODEB_5);
+        MAX7219_set_digit(display, 1, MAX7219_CODEB_6);
+        MAX7219_set_digit(display, 0, MAX7219_CODEB_7);
+
+        Delay_Ms(3000);
+
+        MAX7219_set_digit(display, 7, MAX7219_CODEB_8);
+        MAX7219_set_digit(display, 6, MAX7219_CODEB_9);
+        MAX7219_set_digit(display, 5, MAX7219_CODEB_DASH);
+        MAX7219_set_digit(display, 4, MAX7219_CODEB_H);
+        MAX7219_set_digit(display, 3, MAX7219_CODEB_E);
+        MAX7219_set_digit(display, 2, MAX7219_CODEB_L);
+        MAX7219_set_digit(display, 1, MAX7219_CODEB_P);
+        MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK);
+
+        Delay_Ms(3000);
+
+        //Code B decimal
+        MAX7219_set_digit(display, 7, MAX7219_CODEB_0 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 6, MAX7219_CODEB_1 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 5, MAX7219_CODEB_2 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 4, MAX7219_CODEB_3 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 3, MAX7219_CODEB_4 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 2, MAX7219_CODEB_5 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 1, MAX7219_CODEB_6 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 0, MAX7219_CODEB_7 | MAX7219_CODEB_ADD_DECPOINT);
+
+        Delay_Ms(3000);
+
+        MAX7219_set_digit(display, 7, MAX7219_CODEB_8 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 6, MAX7219_CODEB_9 | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 5, MAX7219_CODEB_DASH | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 4, MAX7219_CODEB_H | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 3, MAX7219_CODEB_E | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 2, MAX7219_CODEB_L | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 1, MAX7219_CODEB_P | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+
+        Delay_Ms(3000);
+        #endif
+
+        #ifdef DEMO_EXTENDED_FONT
+        //Extended font
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_A);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_B);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_C);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_C_LOWER);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_D);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_E);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_F);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_H);
+
+        Delay_Ms(3000);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_H_LOWER);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_I);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_I_LOWER);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_L);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_L_LOWER);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_N);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_O);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_O_LOWER);
+
+        Delay_Ms(3000);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_P);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_S);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_T);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_U);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_U_LOWER);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_X);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_Y);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_Z);
+
+        Delay_Ms(3000);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_0);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_1);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_2);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_3);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_4);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_5);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_6);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_7);
+
+        Delay_Ms(3000);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_8);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_9);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK);
+
+        Delay_Ms(3000);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_DEGREES);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_EQUALS_TOP);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_EQUALS_BOTTOM);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_TRIEQUALS);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_HYPHEN);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_UNDERSCORE);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_OVERSCORE);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_FORWARDSLASH);
+
+        Delay_Ms(3000);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_BACKSLASH);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_DECIMAL);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK);
+
+        Delay_Ms(3000);
+        #endif
+
+        #ifdef DEMO_HELLO_BLINK
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_H);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_E);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_L);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_L);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_O);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK);
+
+        Delay_Ms(500);
+
+        MAX7219_shutdown(display, true);
+
+        Delay_Ms(500);
+
+        MAX7219_shutdown(display, false);
+
+        Delay_Ms(500);
+
+        MAX7219_shutdown(display, true);
+
+        Delay_Ms(500);
+
+        MAX7219_shutdown(display, false);
+
+        Delay_Ms(500);
+
+        MAX7219_shutdown(display, true);
+
+        Delay_Ms(500);
+
+        MAX7219_shutdown(display, false);
+        #endif
+
+        #ifdef DEMO_FIGURES_OF_EIGHT
+        //Figures of 8 (HP style)
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE);
+        
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK);
+
+        for (size_t figureeightcycles = 0; figureeightcycles < 8; figureeightcycles++)
+        {
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_G);
+            Delay_Ms(75);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_B);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_B);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_B);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_B);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_B);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_B);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_B);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_B);
+            Delay_Ms(75);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_A);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_A);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_A);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_A);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_A);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_A);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_A);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_A);
+            Delay_Ms(75);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_F);
+            Delay_Ms(75);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_G);
+            Delay_Ms(75);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_C);
+            Delay_Ms(75);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_D);
+            Delay_Ms(75);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_E);
+            Delay_Ms(75);
+        }
+        #endif
+
+        #ifdef DEMO_TOGETHER_TWIRLS
+        //Together twirls
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE);
+
+        for (size_t togethertwirlcycles = 0; togethertwirlcycles < 8; togethertwirlcycles++)
+        {
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            Delay_Ms(250);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C);
+            Delay_Ms(250);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            Delay_Ms(250);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F);
+            Delay_Ms(250);
+        }
+        #endif
+
+        #ifdef DEMO_COUNTER_TWIRLS
+        //Counter twirls
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE);
+
+        for (size_t countertwirlcycles = 0; countertwirlcycles < 8; countertwirlcycles++)
+        {
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C);
+            Delay_Ms(250);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G);
+            Delay_Ms(250);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E);
+            Delay_Ms(250);
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D);
+            Delay_Ms(250);
+        }
+        #endif
+
+        #ifdef DEMO_KNIGHTRIDER_DECIMAL
+        //Decimal knight-rider-like bounce effect using Code B on decimals
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL);
+
+        MAX7219_set_digit(display, 7, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 6, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 5, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 4, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 3, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 2, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 1, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK);
+
+        Delay_Ms(125);
+
+        for (size_t ridercycles = 0; ridercycles < 4; ridercycles++)
+        {
+            size_t position = 7;
+
+            while (position > 0)
+            {
+                //If the next position is within bounds, place a DP there
+                if (position - 1 >= 0) MAX7219_set_digit(display, position - 1, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+
+                //Clear current position
+                MAX7219_set_digit(display, position, MAX7219_CODEB_BLANK);
+                
+                //Move onto the next position
+                position--;
+
+                Delay_Ms(125);
+            }
+
+            while (position < 7)
+            {
+                //If the next position is within bounds, place a DP there
+                if (position + 1 <= 7) MAX7219_set_digit(display, position + 1, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+
+                //Clear current position
+                MAX7219_set_digit(display, position, MAX7219_CODEB_BLANK);
+                
+                //Move onto the next position
+                position++;
+
+                Delay_Ms(125);
+            }
+        }
+        #endif
+
+        #ifdef DEMO_KNIGHTRIDER_DASH
+        //Decimal knight-rider-like bounce effect using Code B on dashes
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL);
+
+        MAX7219_set_digit(display, 7, MAX7219_CODEB_DASH);
+        MAX7219_set_digit(display, 6, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 5, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 4, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 3, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 2, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 1, MAX7219_CODEB_BLANK);
+        MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK);
+
+        Delay_Ms(125);
+
+        for (size_t ridercycles = 0; ridercycles < 4; ridercycles++)
+        {
+            size_t position = 7;
+
+            while (position > 0)
+            {
+                //If the next position is within bounds, place a DP there
+                if (position - 1 >= 0) MAX7219_set_digit(display, position - 1, MAX7219_CODEB_DASH);
+
+                //Clear current position
+                MAX7219_set_digit(display, position, MAX7219_CODEB_BLANK);
+                
+                //Move onto the next position
+                position--;
+
+                Delay_Ms(125);
+            }
+
+            while (position < 7)
+            {
+                //If the next position is within bounds, place a DP there
+                if (position + 1 <= 7) MAX7219_set_digit(display, position + 1, MAX7219_CODEB_DASH);
+
+                //Clear current position
+                MAX7219_set_digit(display, position, MAX7219_CODEB_BLANK);
+                
+                //Move onto the next position
+                position++;
+
+                Delay_Ms(125);
+            }
+        }
+        #endif
+
+        #ifdef DEMO_INTENSITY_FADE_DECIMALS
+        //All decimals intensity fade effect
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL);
+
+        MAX7219_set_digit(display, 7, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 6, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 5, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 4, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 3, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 2, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 1, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+        MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT);
+
+        MAX7219_set_brightness(display, MAX7219_BRIGHTNESS_MAX);
+
+        Delay_Ms(34); //~30FPS
+
+        for (size_t intensitycycles = 0; intensitycycles < 4; intensitycycles++)
+        {
+            size_t intensity = 0x0E;
+
+            while (intensity > 0x00)
+            {
+                MAX7219_set_brightness(display, intensity);
+
+                intensity--;
+
+                Delay_Ms(34); //~30FPS
+            }
+
+            while (intensity < 0x0F)
+            {
+                MAX7219_set_brightness(display, intensity);
+                
+                //Move onto the next position
+                intensity++;
+
+                Delay_Ms(34); //~30FPS
+            }
+        }
+        #endif
+
+        #ifdef DEMO_INTENSITY_FADE_DIGITS
+        //Numeric intensity fade effect
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL);
+
+        MAX7219_set_digit(display, 7, MAX7219_CODEB_0);
+        MAX7219_set_digit(display, 6, MAX7219_CODEB_1);
+        MAX7219_set_digit(display, 5, MAX7219_CODEB_2);
+        MAX7219_set_digit(display, 4, MAX7219_CODEB_3);
+        MAX7219_set_digit(display, 3, MAX7219_CODEB_4);
+        MAX7219_set_digit(display, 2, MAX7219_CODEB_5);
+        MAX7219_set_digit(display, 1, MAX7219_CODEB_6);
+        MAX7219_set_digit(display, 0, MAX7219_CODEB_7);
+
+        MAX7219_set_brightness(display, MAX7219_BRIGHTNESS_MAX);
+
+        Delay_Ms(34); //~30FPS
+
+        for (size_t intensitycycles = 0; intensitycycles < 4; intensitycycles++)
+        {
+            size_t intensity = 0x0E;
+
+            while (intensity > 0x00)
+            {
+                MAX7219_set_brightness(display, intensity);
+
+                intensity--;
+
+                Delay_Ms(34); //~30FPS
+            }
+
+            while (intensity < 0x0F)
+            {
+                MAX7219_set_brightness(display, intensity);
+                
+                //Move onto the next position
+                intensity++;
+
+                Delay_Ms(34); //~30FPS
+            }
+        }
+        #endif
+
+        #ifdef DEMO_LED_CHASE
+        //Spinny LED chase effect using raw segment control
+        MAX7219_reset(display);
+
+        MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE);
+
+        MAX7219_set_digit(display, 7, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 6, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK);
+        MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK);
+
+        for (size_t chasecycles = 0; chasecycles < 8; chasecycles++)
+        {
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_A);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 7, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_A);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 6, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_A);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_A);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_A);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_A);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_A);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_A);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_B);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_C);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 0, MAX7219_SEGMENT_D);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 1, MAX7219_SEGMENT_D);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 2, MAX7219_SEGMENT_D);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 3, MAX7219_SEGMENT_D);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 4, MAX7219_SEGMENT_D);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 5, MAX7219_SEGMENT_D);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 6, MAX7219_SEGMENT_D);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 6, MAX7219_EXTFONT_BLANK);
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_D);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_E);
+            Delay_Ms(34); //~30FPS
+
+            MAX7219_set_digit(display, 7, MAX7219_SEGMENT_F);
+            Delay_Ms(34); //~30FPS
+        }
+        #endif
+    }
+}
diff --git a/examples/spi_oled/font_8x8.h b/examples/spi_oled/font_8x8.h
new file mode 100644
index 0000000000000000000000000000000000000000..d1593abd9e2a56e536b50f3afe2e67fcc89f2cf2
--- /dev/null
+++ b/examples/spi_oled/font_8x8.h
@@ -0,0 +1,2569 @@
+/**********************************************/
+/*                                            */
+/*       Font file generated by cpi2fnt       */
+/*                                            */
+/**********************************************/
+
+const static unsigned char fontdata[] = {
+
+	/* 0 0x00 '^@' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 1 0x01 '^A' */
+	0x7e, /* 01111110 */
+	0x81, /* 10000001 */
+	0xa5, /* 10100101 */
+	0x81, /* 10000001 */
+	0xbd, /* 10111101 */
+	0x99, /* 10011001 */
+	0x81, /* 10000001 */
+	0x7e, /* 01111110 */
+
+	/* 2 0x02 '^B' */
+	0x7e, /* 01111110 */
+	0xff, /* 11111111 */
+	0xdb, /* 11011011 */
+	0xff, /* 11111111 */
+	0xc3, /* 11000011 */
+	0xe7, /* 11100111 */
+	0xff, /* 11111111 */
+	0x7e, /* 01111110 */
+
+	/* 3 0x03 '^C' */
+	0x6c, /* 01101100 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0x7c, /* 01111100 */
+	0x38, /* 00111000 */
+	0x10, /* 00010000 */
+	0x00, /* 00000000 */
+
+	/* 4 0x04 '^D' */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x7c, /* 01111100 */
+	0xfe, /* 11111110 */
+	0x7c, /* 01111100 */
+	0x38, /* 00111000 */
+	0x10, /* 00010000 */
+	0x00, /* 00000000 */
+
+	/* 5 0x05 '^E' */
+	0x38, /* 00111000 */
+	0x7c, /* 01111100 */
+	0x38, /* 00111000 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0xd6, /* 11010110 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+
+	/* 6 0x06 '^F' */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x7c, /* 01111100 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0x7c, /* 01111100 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+
+	/* 7 0x07 '^G' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 8 0x08 '^H' */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xe7, /* 11100111 */
+	0xc3, /* 11000011 */
+	0xc3, /* 11000011 */
+	0xe7, /* 11100111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+
+	/* 9 0x09 '^I' */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x42, /* 01000010 */
+	0x42, /* 01000010 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 10 0x0a '^J' */
+	0xff, /* 11111111 */
+	0xc3, /* 11000011 */
+	0x99, /* 10011001 */
+	0xbd, /* 10111101 */
+	0xbd, /* 10111101 */
+	0x99, /* 10011001 */
+	0xc3, /* 11000011 */
+	0xff, /* 11111111 */
+
+	/* 11 0x0b '^K' */
+	0x0f, /* 00001111 */
+	0x07, /* 00000111 */
+	0x0f, /* 00001111 */
+	0x7d, /* 01111101 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x78, /* 01111000 */
+
+	/* 12 0x0c '^L' */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+
+	/* 13 0x0d '^M' */
+	0x3f, /* 00111111 */
+	0x33, /* 00110011 */
+	0x3f, /* 00111111 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x70, /* 01110000 */
+	0xf0, /* 11110000 */
+	0xe0, /* 11100000 */
+
+	/* 14 0x0e '^N' */
+	0x7f, /* 01111111 */
+	0x63, /* 01100011 */
+	0x7f, /* 01111111 */
+	0x63, /* 01100011 */
+	0x63, /* 01100011 */
+	0x67, /* 01100111 */
+	0xe6, /* 11100110 */
+	0xc0, /* 11000000 */
+
+	/* 15 0x0f '^O' */
+	0x18, /* 00011000 */
+	0xdb, /* 11011011 */
+	0x3c, /* 00111100 */
+	0xe7, /* 11100111 */
+	0xe7, /* 11100111 */
+	0x3c, /* 00111100 */
+	0xdb, /* 11011011 */
+	0x18, /* 00011000 */
+
+	/* 16 0x10 '^P' */
+	0x80, /* 10000000 */
+	0xe0, /* 11100000 */
+	0xf8, /* 11111000 */
+	0xfe, /* 11111110 */
+	0xf8, /* 11111000 */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+
+	/* 17 0x11 '^Q' */
+	0x02, /* 00000010 */
+	0x0e, /* 00001110 */
+	0x3e, /* 00111110 */
+	0xfe, /* 11111110 */
+	0x3e, /* 00111110 */
+	0x0e, /* 00001110 */
+	0x02, /* 00000010 */
+	0x00, /* 00000000 */
+
+	/* 18 0x12 '^R' */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+
+	/* 19 0x13 '^S' */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+
+	/* 20 0x14 '^T' */
+	0x7f, /* 01111111 */
+	0xdb, /* 11011011 */
+	0xdb, /* 11011011 */
+	0x7b, /* 01111011 */
+	0x1b, /* 00011011 */
+	0x1b, /* 00011011 */
+	0x1b, /* 00011011 */
+	0x00, /* 00000000 */
+
+	/* 21 0x15 '^U' */
+	0x3e, /* 00111110 */
+	0x61, /* 01100001 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x86, /* 10000110 */
+	0x7c, /* 01111100 */
+
+	/* 22 0x16 '^V' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+
+	/* 23 0x17 '^W' */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+
+	/* 24 0x18 '^X' */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 25 0x19 '^Y' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 26 0x1a '^Z' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0xfe, /* 11111110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 27 0x1b '^[' */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xfe, /* 11111110 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 28 0x1c '^\' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 29 0x1d '^]' */
+	0x00, /* 00000000 */
+	0x24, /* 00100100 */
+	0x66, /* 01100110 */
+	0xff, /* 11111111 */
+	0x66, /* 01100110 */
+	0x24, /* 00100100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 30 0x1e '^^' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 31 0x1f '^_' */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0x7e, /* 01111110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 32 0x20 ' ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 33 0x21 '!' */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 34 0x22 '"' */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x24, /* 00100100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 35 0x23 '#' */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+
+	/* 36 0x24 '$' */
+	0x18, /* 00011000 */
+	0x3e, /* 00111110 */
+	0x60, /* 01100000 */
+	0x3c, /* 00111100 */
+	0x06, /* 00000110 */
+	0x7c, /* 01111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 37 0x25 '%' */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xcc, /* 11001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x66, /* 01100110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 38 0x26 '&' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 39 0x27 ''' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 40 0x28 '(' */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x00, /* 00000000 */
+
+	/* 41 0x29 ')' */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	/* 42 0x2a '*' */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0xff, /* 11111111 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 43 0x2b '+' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 44 0x2c ',' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+
+	/* 45 0x2d '-' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 46 0x2e '.' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 47 0x2f '/' */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+
+	/* 48 0x30 '0' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xd6, /* 11010110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	/* 49 0x31 '1' */
+	0x18, /* 00011000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+
+	/* 50 0x32 '2' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0x06, /* 00000110 */
+	0x1c, /* 00011100 */
+	0x30, /* 00110000 */
+	0x66, /* 01100110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+
+	/* 51 0x33 '3' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0x06, /* 00000110 */
+	0x3c, /* 00111100 */
+	0x06, /* 00000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 52 0x34 '4' */
+	0x1c, /* 00011100 */
+	0x3c, /* 00111100 */
+	0x6c, /* 01101100 */
+	0xcc, /* 11001100 */
+	0xfe, /* 11111110 */
+	0x0c, /* 00001100 */
+	0x1e, /* 00011110 */
+	0x00, /* 00000000 */
+
+	/* 53 0x35 '5' */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xfc, /* 11111100 */
+	0x06, /* 00000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 54 0x36 '6' */
+	0x38, /* 00111000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0xfc, /* 11111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 55 0x37 '7' */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	/* 56 0x38 '8' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 57 0x39 '9' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7e, /* 01111110 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	/* 58 0x3a ':' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 59 0x3b ';' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+
+	/* 60 0x3c '<' */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x06, /* 00000110 */
+	0x00, /* 00000000 */
+
+	/* 61 0x3d '=' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 62 0x3e '>' */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	/* 63 0x3f '?' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 64 0x40 '@' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xde, /* 11011110 */
+	0xde, /* 11011110 */
+	0xde, /* 11011110 */
+	0xc0, /* 11000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	/* 65 0x41 'A' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 66 0x42 'B' */
+	0xfc, /* 11111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0xfc, /* 11111100 */
+	0x00, /* 00000000 */
+
+	/* 67 0x43 'C' */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 68 0x44 'D' */
+	0xf8, /* 11111000 */
+	0x6c, /* 01101100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x6c, /* 01101100 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+
+	/* 69 0x45 'E' */
+	0xfe, /* 11111110 */
+	0x62, /* 01100010 */
+	0x68, /* 01101000 */
+	0x78, /* 01111000 */
+	0x68, /* 01101000 */
+	0x62, /* 01100010 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+
+	/* 70 0x46 'F' */
+	0xfe, /* 11111110 */
+	0x62, /* 01100010 */
+	0x68, /* 01101000 */
+	0x78, /* 01111000 */
+	0x68, /* 01101000 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x00, /* 00000000 */
+
+	/* 71 0x47 'G' */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xce, /* 11001110 */
+	0x66, /* 01100110 */
+	0x3a, /* 00111010 */
+	0x00, /* 00000000 */
+
+	/* 72 0x48 'H' */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 73 0x49 'I' */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 74 0x4a 'J' */
+	0x1e, /* 00011110 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	/* 75 0x4b 'K' */
+	0xe6, /* 11100110 */
+	0x66, /* 01100110 */
+	0x6c, /* 01101100 */
+	0x78, /* 01111000 */
+	0x6c, /* 01101100 */
+	0x66, /* 01100110 */
+	0xe6, /* 11100110 */
+	0x00, /* 00000000 */
+
+	/* 76 0x4c 'L' */
+	0xf0, /* 11110000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x62, /* 01100010 */
+	0x66, /* 01100110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+
+	/* 77 0x4d 'M' */
+	0xc6, /* 11000110 */
+	0xee, /* 11101110 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0xd6, /* 11010110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 78 0x4e 'N' */
+	0xc6, /* 11000110 */
+	0xe6, /* 11100110 */
+	0xf6, /* 11110110 */
+	0xde, /* 11011110 */
+	0xce, /* 11001110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 79 0x4f 'O' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 80 0x50 'P' */
+	0xfc, /* 11111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x00, /* 00000000 */
+
+	/* 81 0x51 'Q' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xce, /* 11001110 */
+	0x7c, /* 01111100 */
+	0x0e, /* 00001110 */
+
+	/* 82 0x52 'R' */
+	0xfc, /* 11111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x6c, /* 01101100 */
+	0x66, /* 01100110 */
+	0xe6, /* 11100110 */
+	0x00, /* 00000000 */
+
+	/* 83 0x53 'S' */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 84 0x54 'T' */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x5a, /* 01011010 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 85 0x55 'U' */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 86 0x56 'V' */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	/* 87 0x57 'W' */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+
+	/* 88 0x58 'X' */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 89 0x59 'Y' */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 90 0x5a 'Z' */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0x8c, /* 10001100 */
+	0x18, /* 00011000 */
+	0x32, /* 00110010 */
+	0x66, /* 01100110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+
+	/* 91 0x5b '[' */
+	0x3c, /* 00111100 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 92 0x5c '\' */
+	0xc0, /* 11000000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x06, /* 00000110 */
+	0x02, /* 00000010 */
+	0x00, /* 00000000 */
+
+	/* 93 0x5d ']' */
+	0x3c, /* 00111100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 94 0x5e '^' */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 95 0x5f '_' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+
+	/* 96 0x60 '`' */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 97 0x61 'a' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 98 0x62 'b' */
+	0xe0, /* 11100000 */
+	0x60, /* 01100000 */
+	0x7c, /* 01111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+
+	/* 99 0x63 'c' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 100 0x64 'd' */
+	0x1c, /* 00011100 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 101 0x65 'e' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 102 0x66 'f' */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x60, /* 01100000 */
+	0xf8, /* 11111000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x00, /* 00000000 */
+
+	/* 103 0x67 'g' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x7c, /* 01111100 */
+	0x0c, /* 00001100 */
+	0xf8, /* 11111000 */
+
+	/* 104 0x68 'h' */
+	0xe0, /* 11100000 */
+	0x60, /* 01100000 */
+	0x6c, /* 01101100 */
+	0x76, /* 01110110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0xe6, /* 11100110 */
+	0x00, /* 00000000 */
+
+	/* 105 0x69 'i' */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 106 0x6a 'j' */
+	0x06, /* 00000110 */
+	0x00, /* 00000000 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+
+	/* 107 0x6b 'k' */
+	0xe0, /* 11100000 */
+	0x60, /* 01100000 */
+	0x66, /* 01100110 */
+	0x6c, /* 01101100 */
+	0x78, /* 01111000 */
+	0x6c, /* 01101100 */
+	0xe6, /* 11100110 */
+	0x00, /* 00000000 */
+
+	/* 108 0x6c 'l' */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 109 0x6d 'm' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xec, /* 11101100 */
+	0xfe, /* 11111110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0x00, /* 00000000 */
+
+	/* 110 0x6e 'n' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xdc, /* 11011100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+
+	/* 111 0x6f 'o' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 112 0x70 'p' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xdc, /* 11011100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+
+	/* 113 0x71 'q' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x7c, /* 01111100 */
+	0x0c, /* 00001100 */
+	0x1e, /* 00011110 */
+
+	/* 114 0x72 'r' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xdc, /* 11011100 */
+	0x76, /* 01110110 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x00, /* 00000000 */
+
+	/* 115 0x73 's' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0xc0, /* 11000000 */
+	0x7c, /* 01111100 */
+	0x06, /* 00000110 */
+	0xfc, /* 11111100 */
+	0x00, /* 00000000 */
+
+	/* 116 0x74 't' */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0xfc, /* 11111100 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x36, /* 00110110 */
+	0x1c, /* 00011100 */
+	0x00, /* 00000000 */
+
+	/* 117 0x75 'u' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 118 0x76 'v' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	/* 119 0x77 'w' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+
+	/* 120 0x78 'x' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 121 0x79 'y' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7e, /* 01111110 */
+	0x06, /* 00000110 */
+	0xfc, /* 11111100 */
+
+	/* 122 0x7a 'z' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x4c, /* 01001100 */
+	0x18, /* 00011000 */
+	0x32, /* 00110010 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+
+	/* 123 0x7b '{' */
+	0x0e, /* 00001110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x70, /* 01110000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x0e, /* 00001110 */
+	0x00, /* 00000000 */
+
+	/* 124 0x7c '|' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 125 0x7d '}' */
+	0x70, /* 01110000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x0e, /* 00001110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	/* 126 0x7e '~' */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 127 0x7f '' */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+
+	/* 128 0x80 '€' */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x0c, /* 00001100 */
+	0x78, /* 01111000 */
+
+	/* 129 0x81 '' */
+	0xcc, /* 11001100 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 130 0x82 '‚' */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 131 0x83 'ƒ' */
+	0x7c, /* 01111100 */
+	0x82, /* 10000010 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 132 0x84 '„' */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 133 0x85 '…' */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 134 0x86 '†' */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 135 0x87 '‡' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0x7e, /* 01111110 */
+	0x0c, /* 00001100 */
+	0x38, /* 00111000 */
+
+	/* 136 0x88 'ˆ' */
+	0x7c, /* 01111100 */
+	0x82, /* 10000010 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 137 0x89 '‰' */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 138 0x8a 'Š' */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 139 0x8b '‹' */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 140 0x8c 'Œ' */
+	0x7c, /* 01111100 */
+	0x82, /* 10000010 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 141 0x8d '' */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 142 0x8e 'Ž' */
+	0xc6, /* 11000110 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 143 0x8f '' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 144 0x90 '' */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xf8, /* 11111000 */
+	0xc0, /* 11000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+
+	/* 145 0x91 '‘' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0xd8, /* 11011000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+
+	/* 146 0x92 '’' */
+	0x3e, /* 00111110 */
+	0x6c, /* 01101100 */
+	0xcc, /* 11001100 */
+	0xfe, /* 11111110 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xce, /* 11001110 */
+	0x00, /* 00000000 */
+
+	/* 147 0x93 '“' */
+	0x7c, /* 01111100 */
+	0x82, /* 10000010 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 148 0x94 '”' */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 149 0x95 '•' */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 150 0x96 '–' */
+	0x78, /* 01111000 */
+	0x84, /* 10000100 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 151 0x97 '—' */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 152 0x98 '˜' */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7e, /* 01111110 */
+	0x06, /* 00000110 */
+	0xfc, /* 11111100 */
+
+	/* 153 0x99 '™' */
+	0xc6, /* 11000110 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	/* 154 0x9a 'š' */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 155 0x9b '›' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 156 0x9c 'œ' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x64, /* 01100100 */
+	0xf0, /* 11110000 */
+	0x60, /* 01100000 */
+	0x66, /* 01100110 */
+	0xfc, /* 11111100 */
+	0x00, /* 00000000 */
+
+	/* 157 0x9d '' */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 158 0x9e 'ž' */
+	0xf8, /* 11111000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xfa, /* 11111010 */
+	0xc6, /* 11000110 */
+	0xcf, /* 11001111 */
+	0xc6, /* 11000110 */
+	0xc7, /* 11000111 */
+
+	/* 159 0x9f 'Ÿ' */
+	0x0e, /* 00001110 */
+	0x1b, /* 00011011 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0xd8, /* 11011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	/* 160 0xa0 ' ' */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 161 0xa1 '¡' */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 162 0xa2 '¢' */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+
+	/* 163 0xa3 '£' */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 164 0xa4 '¤' */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0xdc, /* 11011100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+
+	/* 165 0xa5 '¥' */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0xe6, /* 11100110 */
+	0xf6, /* 11110110 */
+	0xde, /* 11011110 */
+	0xce, /* 11001110 */
+	0x00, /* 00000000 */
+
+	/* 166 0xa6 '¦' */
+	0x3c, /* 00111100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x3e, /* 00111110 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 167 0xa7 '§' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 168 0xa8 '¨' */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x63, /* 01100011 */
+	0x3e, /* 00111110 */
+	0x00, /* 00000000 */
+
+	/* 169 0xa9 '©' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 170 0xaa 'ª' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 171 0xab '«' */
+	0x63, /* 01100011 */
+	0xe6, /* 11100110 */
+	0x6c, /* 01101100 */
+	0x7e, /* 01111110 */
+	0x33, /* 00110011 */
+	0x66, /* 01100110 */
+	0xcc, /* 11001100 */
+	0x0f, /* 00001111 */
+
+	/* 172 0xac '¬' */
+	0x63, /* 01100011 */
+	0xe6, /* 11100110 */
+	0x6c, /* 01101100 */
+	0x7a, /* 01111010 */
+	0x36, /* 00110110 */
+	0x6a, /* 01101010 */
+	0xdf, /* 11011111 */
+	0x06, /* 00000110 */
+
+	/* 173 0xad '­' */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 174 0xae '®' */
+	0x00, /* 00000000 */
+	0x33, /* 00110011 */
+	0x66, /* 01100110 */
+	0xcc, /* 11001100 */
+	0x66, /* 01100110 */
+	0x33, /* 00110011 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 175 0xaf '¯' */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0x66, /* 01100110 */
+	0x33, /* 00110011 */
+	0x66, /* 01100110 */
+	0xcc, /* 11001100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 176 0xb0 '°' */
+	0x22, /* 00100010 */
+	0x88, /* 10001000 */
+	0x22, /* 00100010 */
+	0x88, /* 10001000 */
+	0x22, /* 00100010 */
+	0x88, /* 10001000 */
+	0x22, /* 00100010 */
+	0x88, /* 10001000 */
+
+	/* 177 0xb1 '±' */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+
+	/* 178 0xb2 '²' */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+
+	/* 179 0xb3 '³' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 180 0xb4 '´' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 181 0xb5 'µ' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 182 0xb6 '¶' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf6, /* 11110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 183 0xb7 '·' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 184 0xb8 '¸' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 185 0xb9 '¹' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf6, /* 11110110 */
+	0x06, /* 00000110 */
+	0xf6, /* 11110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 186 0xba 'º' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 187 0xbb '»' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x06, /* 00000110 */
+	0xf6, /* 11110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 188 0xbc '¼' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf6, /* 11110110 */
+	0x06, /* 00000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 189 0xbd '½' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 190 0xbe '¾' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 191 0xbf '¿' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 192 0xc0 'À' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 193 0xc1 'Á' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 194 0xc2 'Â' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 195 0xc3 'Ã' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 196 0xc4 'Ä' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 197 0xc5 'Å' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 198 0xc6 'Æ' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 199 0xc7 'Ç' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x37, /* 00110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 200 0xc8 'È' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x37, /* 00110111 */
+	0x30, /* 00110000 */
+	0x3f, /* 00111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 201 0xc9 'É' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3f, /* 00111111 */
+	0x30, /* 00110000 */
+	0x37, /* 00110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 202 0xca 'Ê' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf7, /* 11110111 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 203 0xcb 'Ë' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0xf7, /* 11110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 204 0xcc 'Ì' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x37, /* 00110111 */
+	0x30, /* 00110000 */
+	0x37, /* 00110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 205 0xcd 'Í' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 206 0xce 'Î' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf7, /* 11110111 */
+	0x00, /* 00000000 */
+	0xf7, /* 11110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 207 0xcf 'Ï' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 208 0xd0 'Ð' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 209 0xd1 'Ñ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 210 0xd2 'Ò' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 211 0xd3 'Ó' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x3f, /* 00111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 212 0xd4 'Ô' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 213 0xd5 'Õ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 214 0xd6 'Ö' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3f, /* 00111111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 215 0xd7 '×' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xff, /* 11111111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 216 0xd8 'Ø' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 217 0xd9 'Ù' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 218 0xda 'Ú' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 219 0xdb 'Û' */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+
+	/* 220 0xdc 'Ü' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+
+	/* 221 0xdd 'Ý' */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+
+	/* 222 0xde 'Þ' */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+
+	/* 223 0xdf 'ß' */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 224 0xe0 'à' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0xc8, /* 11001000 */
+	0xdc, /* 11011100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+
+	/* 225 0xe1 'á' */
+	0x78, /* 01111000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xd8, /* 11011000 */
+	0xcc, /* 11001100 */
+	0xc6, /* 11000110 */
+	0xcc, /* 11001100 */
+	0x00, /* 00000000 */
+
+	/* 226 0xe2 'â' */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+
+	/* 227 0xe3 'ã' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+
+	/* 228 0xe4 'ä' */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+
+	/* 229 0xe5 'å' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	/* 230 0xe6 'æ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0xc0, /* 11000000 */
+
+	/* 231 0xe7 'ç' */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	/* 232 0xe8 'è' */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+
+	/* 233 0xe9 'é' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	/* 234 0xea 'ê' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0xee, /* 11101110 */
+	0x00, /* 00000000 */
+
+	/* 235 0xeb 'ë' */
+	0x0e, /* 00001110 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x3e, /* 00111110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 236 0xec 'ì' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0xdb, /* 11011011 */
+	0xdb, /* 11011011 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 237 0xed 'í' */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x7e, /* 01111110 */
+	0xdb, /* 11011011 */
+	0xdb, /* 11011011 */
+	0x7e, /* 01111110 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+
+	/* 238 0xee 'î' */
+	0x1e, /* 00011110 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x7e, /* 01111110 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x1e, /* 00011110 */
+	0x00, /* 00000000 */
+
+	/* 239 0xef 'ï' */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+
+	/* 240 0xf0 'ð' */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 241 0xf1 'ñ' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+
+	/* 242 0xf2 'ò' */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+
+	/* 243 0xf3 'ó' */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+
+	/* 244 0xf4 'ô' */
+	0x0e, /* 00001110 */
+	0x1b, /* 00011011 */
+	0x1b, /* 00011011 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 245 0xf5 'õ' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0x70, /* 01110000 */
+
+	/* 246 0xf6 'ö' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 247 0xf7 '÷' */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 248 0xf8 'ø' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 249 0xf9 'ù' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 250 0xfa 'ú' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 251 0xfb 'û' */
+	0x0f, /* 00001111 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0xec, /* 11101100 */
+	0x6c, /* 01101100 */
+	0x3c, /* 00111100 */
+	0x1c, /* 00011100 */
+
+	/* 252 0xfc 'ü' */
+	0x6c, /* 01101100 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 253 0xfd 'ý' */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 254 0xfe 'þ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 255 0xff 'ÿ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+};
diff --git a/examples/spi_oled/funconfig.h b/examples/spi_oled/funconfig.h
new file mode 100644
index 0000000000000000000000000000000000000000..998cf76fede1479f162c72ef34d3a4d747430093
--- /dev/null
+++ b/examples/spi_oled/funconfig.h
@@ -0,0 +1,7 @@
+#ifndef _FUNCONFIG_H
+#define _FUNCONFIG_H
+
+#define CH32V003           1
+
+#endif
+
diff --git a/examples/spi_oled/ssd1306.h b/examples/spi_oled/ssd1306.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea985d8d8a0108004beb495107b3c95570eb5b13
--- /dev/null
+++ b/examples/spi_oled/ssd1306.h
@@ -0,0 +1,663 @@
+/*
+ * Single-File-Header for using SPI OLED
+ * 05-05-2023 E. Brombaugh
+ */
+
+#ifndef _SSD1306_H
+#define _SSD1306_H
+
+#include <stdint.h>
+#include <string.h>
+#include "font_8x8.h"
+
+// comfortable packet size for this OLED
+#define SSD1306_PSZ 32
+
+// characteristics of each type
+#if !defined (SSD1306_64X32) && !defined (SSD1306_128X32) && !defined (SSD1306_128X64)
+	#error "Please define the SSD1306_WXH resolution used in your application"
+#endif
+
+#ifdef SSD1306_64X32
+#define SSD1306_W 64
+#define SSD1306_H 32
+#define SSD1306_FULLUSE
+#define SSD1306_OFFSET 32
+#endif
+
+#ifdef SSD1306_128X32
+#define SSD1306_W 128
+#define SSD1306_H 32
+#define SSD1306_OFFSET 0
+#endif
+
+#ifdef SSD1306_128X64
+#define SSD1306_W 128
+#define SSD1306_H 64
+#define SSD1306_FULLUSE
+#define SSD1306_OFFSET 0
+#endif
+
+/*
+ * send OLED command byte
+ */
+uint8_t ssd1306_cmd(uint8_t cmd)
+{
+	ssd1306_pkt_send(&cmd, 1, 1);
+	return 0;
+}
+
+/*
+ * send OLED data packet (up to 32 bytes)
+ */
+uint8_t ssd1306_data(uint8_t *data, uint8_t sz)
+{
+	ssd1306_pkt_send(data, sz, 0);
+	return 0;
+}
+
+#define SSD1306_SETCONTRAST 0x81
+#define SSD1306_SEGREMAP 0xA0
+#define SSD1306_DISPLAYALLON_RESUME 0xA4
+#define SSD1306_DISPLAYALLON 0xA5
+#define SSD1306_NORMALDISPLAY 0xA6
+#define SSD1306_INVERTDISPLAY 0xA7
+#define SSD1306_DISPLAYOFF 0xAE
+#define SSD1306_DISPLAYON 0xAF
+#define SSD1306_SETDISPLAYOFFSET 0xD3
+#define SSD1306_SETCOMPINS 0xDA
+#define SSD1306_SETVCOMDETECT 0xDB
+#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
+#define SSD1306_SETPRECHARGE 0xD9
+#define SSD1306_SETMULTIPLEX 0xA8
+#define SSD1306_SETLOWCOLUMN 0x00
+#define SSD1306_SETHIGHCOLUMN 0x10
+#define SSD1306_SETSTARTLINE 0x40
+#define SSD1306_MEMORYMODE 0x20
+#define SSD1306_COLUMNADDR 0x21
+#define SSD1306_PAGEADDR   0x22
+#define SSD1306_COMSCANINC 0xC0
+#define SSD1306_COMSCANDEC 0xC8
+#define SSD1306_CHARGEPUMP 0x8D
+#define SSD1306_EXTERNALVCC 0x1
+#define SSD1306_SWITCHCAPVCC 0x2
+#define SSD1306_TERMINATE_CMDS 0xFF
+
+/* choose VCC mode */
+#define SSD1306_EXTERNALVCC 0x1
+#define SSD1306_SWITCHCAPVCC 0x2
+//#define vccstate SSD1306_EXTERNALVCC
+#define vccstate SSD1306_SWITCHCAPVCC
+
+// OLED initialization commands for 128x32
+const uint8_t ssd1306_init_array[] =
+{
+    SSD1306_DISPLAYOFF,                    // 0xAE
+    SSD1306_SETDISPLAYCLOCKDIV,            // 0xD5
+    0x80,                                  // the suggested ratio 0x80
+    SSD1306_SETMULTIPLEX,                  // 0xA8
+#ifdef SSD1306_64X32
+	0x1F,                                  // for 64-wide displays
+#else
+	0x3F,                                  // for 128-wide displays
+#endif
+    SSD1306_SETDISPLAYOFFSET,              // 0xD3
+    0x00,                                  // no offset
+	SSD1306_SETSTARTLINE | 0x0,            // 0x40 | line
+    SSD1306_CHARGEPUMP,                    // 0x8D
+	0x14,                                  // enable?
+    SSD1306_MEMORYMODE,                    // 0x20
+    0x00,                                  // 0x0 act like ks0108
+    SSD1306_SEGREMAP | 0x1,                // 0xA0 | bit
+    SSD1306_COMSCANDEC,
+    SSD1306_SETCOMPINS,                    // 0xDA
+	0x12,                                  //
+    SSD1306_SETCONTRAST,                   // 0x81
+	0x8F,
+    SSD1306_SETPRECHARGE,                  // 0xd9
+	0xF1,
+    SSD1306_SETVCOMDETECT,                 // 0xDB
+    0x40,
+    SSD1306_DISPLAYALLON_RESUME,           // 0xA4
+    SSD1306_NORMALDISPLAY,                 // 0xA6
+	SSD1306_DISPLAYON,	                   // 0xAF --turn on oled panel
+	SSD1306_TERMINATE_CMDS                 // 0xFF --fake command to mark end
+};
+
+// the display buffer
+uint8_t ssd1306_buffer[SSD1306_W*SSD1306_H/8];
+
+/*
+ * set the buffer to a color
+ */
+void ssd1306_setbuf(uint8_t color)
+{
+	memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer));
+}
+
+#ifndef SSD1306_FULLUSE
+/*
+ * expansion array for OLED with every other row unused
+ */
+const uint8_t expand[16] =
+{
+	0x00,0x02,0x08,0x0a,
+	0x20,0x22,0x28,0x2a,
+	0x80,0x82,0x88,0x8a,
+	0xa0,0xa2,0xa8,0xaa,
+};
+#endif
+
+/*
+ * Send the frame buffer
+ */
+void ssd1306_refresh(void)
+{
+	uint16_t i;
+	
+	ssd1306_cmd(SSD1306_COLUMNADDR);
+	ssd1306_cmd(SSD1306_OFFSET);   // Column start address (0 = reset)
+	ssd1306_cmd(SSD1306_OFFSET+SSD1306_W-1); // Column end address (127 = reset)
+	
+	ssd1306_cmd(SSD1306_PAGEADDR);
+	ssd1306_cmd(0); // Page start address (0 = reset)
+	ssd1306_cmd(7); // Page end address
+
+#ifdef SSD1306_FULLUSE
+	/* for fully used rows just plow thru everything */
+    for(i=0;i<sizeof(ssd1306_buffer);i+=SSD1306_PSZ)
+	{
+		/* send PSZ block of data */
+		ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ);
+	}
+#else
+	/* for displays with odd rows unused expand bytes */
+	uint8_t tbuf[SSD1306_PSZ], j, k;
+    for(i=0;i<sizeof(ssd1306_buffer);i+=128)
+	{
+		/* low nybble */
+		for(j=0;j<128;j+=SSD1306_PSZ)
+		{
+			for(k=0;k<SSD1306_PSZ;k++)
+				tbuf[k] = expand[ssd1306_buffer[i+j+k]&0xf];
+			
+			/* send PSZ block of data */
+			ssd1306_data(tbuf, SSD1306_PSZ);
+		}
+		
+		/* high nybble */
+		for(j=0;j<128;j+=SSD1306_PSZ)
+		{
+			for(k=0;k<SSD1306_PSZ;k++)
+				tbuf[k] = expand[(ssd1306_buffer[i+j+k]>>4)&0xf];
+			
+			/* send PSZ block of data */
+			ssd1306_data(tbuf, SSD1306_PSZ);
+		}
+	}
+#endif
+}
+
+/*
+ * plot a pixel in the buffer
+ */
+void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color)
+{
+	uint16_t addr;
+	
+	/* clip */
+	if(x >= SSD1306_W)
+		return;
+	if(y >= SSD1306_H)
+		return;
+	
+	/* compute buffer address */
+	addr = x + SSD1306_W*(y/8);
+	
+	/* set/clear bit in buffer */
+	if(color)
+		ssd1306_buffer[addr] |= (1<<(y&7));
+	else
+		ssd1306_buffer[addr] &= ~(1<<(y&7));
+}
+
+/*
+ * plot a pixel in the buffer
+ */
+void ssd1306_xorPixel(uint8_t x, uint8_t y)
+{
+	uint16_t addr;
+	
+	/* clip */
+	if(x >= SSD1306_W)
+		return;
+	if(y >= SSD1306_H)
+		return;
+	
+	/* compute buffer address */
+	addr = x + SSD1306_W*(y/8);
+	
+	ssd1306_buffer[addr] ^= (1<<(y&7));
+}
+
+/*
+ * draw a an image from an array, directly into to the display buffer
+ * the color modes allow for overwriting and even layering (sprites!)
+ */
+void ssd1306_drawImage(uint8_t x, uint8_t y, const unsigned char* input, uint8_t width, uint8_t height, uint8_t color_mode) {
+	uint8_t x_absolute;
+	uint8_t y_absolute;
+	uint8_t pixel;
+	uint8_t bytes_to_draw = width / 8;
+	uint16_t buffer_addr;
+
+	for (uint8_t line = 0; line < height; line++) {
+		y_absolute = y + line;
+		if (y_absolute >= SSD1306_H) {
+			break;
+		}
+
+		// SSD1306 is in vertical mode, yet we want to draw horizontally, which necessitates assembling the output bytes from the input data
+		// bitmask for current pixel in vertical (output) byte
+		uint8_t v_mask = 1 << (y_absolute & 7);
+
+		for (uint8_t byte = 0; byte < bytes_to_draw; byte++) {
+			uint8_t input_byte = input[byte + line * bytes_to_draw];
+
+			for (pixel = 0; pixel < 8; pixel++) {
+				x_absolute = x + 8 * (bytes_to_draw - byte) + pixel;
+				if (x_absolute >= SSD1306_W) {
+					break;
+				}
+				// looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8
+				buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8);
+				// state of current pixel
+				uint8_t input_pixel = input_byte & (1 << pixel);
+
+				switch (color_mode) {
+					case 0:
+						// write pixels as they are
+						ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0);
+						break;
+					case 1:
+						// write pixels after inversion
+						ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0);
+						break;
+					case 2:
+						// 0 clears pixel
+						ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask;
+						break;
+					case 3:
+						// 1 sets pixel
+						ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0;
+						break;
+					case 4:
+						// 0 sets pixel
+						ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0;
+						break;
+					case 5:
+						// 1 clears pixel
+						ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF;
+						break;
+				}
+			}
+			#if SSD1306_LOG_IMAGE == 1
+			printf("%02x ", input_byte);
+			#endif
+		}
+		#if SSD1306_LOG_IMAGE == 1
+		printf("\n\r");
+		#endif
+	}
+}
+
+/*
+ *  fast vert line
+ */
+void ssd1306_drawFastVLine(uint8_t x, uint8_t y, uint8_t h, uint8_t color)
+{
+	// clipping
+	if((x >= SSD1306_W) || (y >= SSD1306_H)) return;
+	if((y+h-1) >= SSD1306_H) h = SSD1306_H-y;
+	while(h--)
+	{
+        ssd1306_drawPixel(x, y++, color);
+	}
+}
+
+/*
+ *  fast horiz line
+ */
+void ssd1306_drawFastHLine(uint8_t x, uint8_t y, uint8_t w, uint8_t color)
+{
+	// clipping
+	if((x >= SSD1306_W) || (y >= SSD1306_H)) return;
+	if((x+w-1) >= SSD1306_W)  w = SSD1306_W-x;
+
+	while (w--)
+	{
+        ssd1306_drawPixel(x++, y, color);
+	}
+}
+
+/*
+ * abs() helper function for line drawing
+ */
+int16_t gfx_abs(int16_t x)
+{
+	return (x<0) ? -x : x;
+}
+
+/*
+ * swap() helper function for line drawing
+ */
+void gfx_swap(uint16_t *z0, uint16_t *z1)
+{
+	uint16_t temp = *z0;
+	*z0 = *z1;
+	*z1 = temp;
+}
+
+/*
+ * Bresenham line draw routine swiped from Wikipedia
+ */
+void ssd1306_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t color)
+{
+	int16_t steep;
+	int16_t deltax, deltay, error, ystep, x, y;
+
+	/* flip sense 45deg to keep error calc in range */
+	steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0));
+
+	if(steep)
+	{
+		gfx_swap(&x0, &y0);
+		gfx_swap(&x1, &y1);
+	}
+
+	/* run low->high */
+	if(x0 > x1)
+	{
+		gfx_swap(&x0, &x1);
+		gfx_swap(&y0, &y1);
+	}
+
+	/* set up loop initial conditions */
+	deltax = x1 - x0;
+	deltay = gfx_abs(y1 - y0);
+	error = deltax/2;
+	y = y0;
+	if(y0 < y1)
+		ystep = 1;
+	else
+		ystep = -1;
+
+	/* loop x */
+	for(x=x0;x<=x1;x++)
+	{
+		/* plot point */
+		if(steep)
+			/* flip point & plot */
+			ssd1306_drawPixel(y, x, color);
+		else
+			/* just plot */
+			ssd1306_drawPixel(x, y, color);
+
+		/* update error */
+		error = error - deltay;
+
+		/* update y */
+		if(error < 0)
+		{
+			y = y + ystep;
+			error = error + deltax;
+		}
+	}
+}
+
+/*
+ *  draws a circle
+ */
+void ssd1306_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
+{
+    /* Bresenham algorithm */
+    int16_t x_pos = -radius;
+    int16_t y_pos = 0;
+    int16_t err = 2 - 2 * radius;
+    int16_t e2;
+
+    do {
+        ssd1306_drawPixel(x - x_pos, y + y_pos, color);
+        ssd1306_drawPixel(x + x_pos, y + y_pos, color);
+        ssd1306_drawPixel(x + x_pos, y - y_pos, color);
+        ssd1306_drawPixel(x - x_pos, y - y_pos, color);
+        e2 = err;
+        if (e2 <= y_pos) {
+            err += ++y_pos * 2 + 1;
+            if(-x_pos == y_pos && e2 <= x_pos) {
+              e2 = 0;
+            }
+        }
+        if (e2 > x_pos) {
+            err += ++x_pos * 2 + 1;
+        }
+    } while (x_pos <= 0);
+}
+
+/*
+ *  draws a filled circle
+ */
+void ssd1306_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
+{
+    /* Bresenham algorithm */
+    int16_t x_pos = -radius;
+    int16_t y_pos = 0;
+    int16_t err = 2 - 2 * radius;
+    int16_t e2;
+
+    do {
+        ssd1306_drawPixel(x - x_pos, y + y_pos, color);
+        ssd1306_drawPixel(x + x_pos, y + y_pos, color);
+        ssd1306_drawPixel(x + x_pos, y - y_pos, color);
+        ssd1306_drawPixel(x - x_pos, y - y_pos, color);
+        ssd1306_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color);
+        ssd1306_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color);
+        e2 = err;
+        if (e2 <= y_pos) {
+            err += ++y_pos * 2 + 1;
+            if(-x_pos == y_pos && e2 <= x_pos) {
+                e2 = 0;
+            }
+        }
+        if(e2 > x_pos) {
+            err += ++x_pos * 2 + 1;
+        }
+    } while(x_pos <= 0);
+}
+
+/*
+ *  draw a rectangle
+ */
+void ssd1306_drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
+{
+	ssd1306_drawFastVLine(x, y, h, color);
+	ssd1306_drawFastVLine(x+w-1, y, h, color);
+	ssd1306_drawFastHLine(x, y, w, color);
+	ssd1306_drawFastHLine(x, y+h-1, w, color);
+}
+
+/*
+ * fill a rectangle
+ */
+void ssd1306_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
+{
+	uint8_t m, n=y, iw = w;
+	
+	/* scan vertical */
+	while(h--)
+	{
+		m=x;
+		w=iw;
+		/* scan horizontal */
+		while(w--)
+		{
+			/* invert pixels */
+			ssd1306_drawPixel(m++, n, color);
+		}
+		n++;
+	}
+}
+
+/*
+ * invert a rectangle in the buffer
+ */
+void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
+{
+	uint8_t m, n=y, iw = w;
+	
+	/* scan vertical */
+	while(h--)
+	{
+		m=x;
+		w=iw;
+		/* scan horizontal */
+		while(w--)
+		{
+			/* invert pixels */
+			ssd1306_xorPixel(m++, n);
+		}
+		n++;
+	}
+}
+
+/*
+ * Draw character to the display buffer
+ */
+void ssd1306_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color)
+{
+	uint16_t i, j, col;
+	uint8_t d;
+	
+	for(i=0;i<8;i++)
+	{
+		d = fontdata[(chr<<3)+i];
+		for(j=0;j<8;j++)
+		{
+			if(d&0x80)
+				col = color;
+			else
+				col = (~color)&1;
+			
+			ssd1306_drawPixel(x+j, y+i, col);
+			
+			// next bit
+			d <<= 1;
+		}
+	}
+}
+
+/*
+ * draw a string to the display
+ */
+void ssd1306_drawstr(uint8_t x, uint8_t y, char *str, uint8_t color)
+{
+	uint8_t c;
+	
+	while((c=*str++))
+	{
+		ssd1306_drawchar(x, y, c, color);
+		x += 8;
+		if(x>120)
+			break;
+	}
+}
+
+/*
+ * enum for font size
+ */
+typedef enum {
+    fontsize_8x8 = 1,
+    fontsize_16x16 = 2,
+    fontsize_32x32 = 4,
+	fontsize_64x64 = 8,
+} font_size_t;
+
+/*
+ * Draw character to the display buffer, scaled to size
+ */
+void ssd1306_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_size_t font_size)
+{
+    uint16_t i, j, col;
+    uint8_t d;
+
+    // Determine the font scale factor based on the font_size parameter
+    uint8_t font_scale = (uint8_t)font_size;
+
+    // Loop through each row of the font data
+    for (i = 0; i < 8; i++)
+    {
+        // Retrieve the font data for the current row
+        d = fontdata[(chr << 3) + i];
+
+        // Loop through each column of the font data
+        for (j = 0; j < 8; j++)
+        {
+            // Determine the color to draw based on the current bit in the font data
+            if (d & 0x80)
+                col = color;
+            else
+                col = (~color) & 1;
+
+            // Draw the pixel at the original size and scaled size using nested for-loops
+            for (uint8_t k = 0; k < font_scale; k++) {
+                for (uint8_t l = 0; l < font_scale; l++) {
+                    ssd1306_drawPixel(x + (j * font_scale) + k, y + (i * font_scale) + l, col);
+                }
+            }
+
+            // Move to the next bit in the font data
+            d <<= 1;
+        }
+    }
+}
+
+/*
+ * draw a string to the display buffer, scaled to size
+ */
+void ssd1306_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t font_size)
+{
+	uint8_t c;
+	
+	while((c=*str++))
+	{
+		ssd1306_drawchar_sz(x, y, c, color, font_size);
+		x += 8 * font_size;
+		if(x>128 - 8 * font_size)
+			break;
+	}
+}
+
+/*
+ * initialize I2C and OLED
+ */
+uint8_t ssd1306_init(void)
+{
+	// pulse reset
+	ssd1306_rst();
+	
+	// initialize OLED
+	uint8_t *cmd_list = (uint8_t *)ssd1306_init_array;
+	while(*cmd_list != SSD1306_TERMINATE_CMDS)
+	{
+		if(ssd1306_cmd(*cmd_list++))
+			return 1;
+	}
+	
+	// clear display
+	ssd1306_setbuf(0);
+	ssd1306_refresh();
+	
+	return 0;
+}
+
+#endif
diff --git a/examples/standby_autowake/README.md b/examples/standby_autowake/README.md
index 205a20afd4985de0e516aefff821d97d109e04d1..2fe3d123f79270038312627ab679fc4e3442c57e 100644
--- a/examples/standby_autowake/README.md
+++ b/examples/standby_autowake/README.md
@@ -1,11 +1,12 @@
 # the deepest slumber
 
+**WARNING: You MUST hard-reboot the CH32V003 to allow it to go into deep sleep. You cannot go from flashing to deep sleep without a hard power cycle.**
+
 This example serves to show how to put the CH32V003 into its lowest power state (standby) and have it wake periodically.  
 
 Power consumption should be around 10uA.  
 
-The MCU only toggles the LED and prints a message, then it goes to sleep.  
-The LED staying on demonstrates that GPIO keeps its state even when the rest of the mcu is in a coma.  
+Refer to the standby_btn example for GPIO settings.
 
 Based on the groundwork of Marek M.  
 
diff --git a/examples/standby_autowake/standby_autowake.c b/examples/standby_autowake/standby_autowake.c
index 4cfaaf34c1248cd27cac5e82a3cef6d78d754a5e..9e41489a4b595d10fec04f28871180dc49fffa92 100644
--- a/examples/standby_autowake/standby_autowake.c
+++ b/examples/standby_autowake/standby_autowake.c
@@ -3,30 +3,56 @@
 #include "ch32v003fun.h"
 #include <stdio.h>
 
-/* somehow this ISR won't get called??
-void AWU_IRQHandler( void ) __attribute__((interrupt));
-void AWU_IRQHandler( void ) {
-	GPIOD->OUTDR ^= (1 << 4);
-}
-*/
 
 int main()
 {
 	SystemInit();
-	Delay_Ms(100);
 
-	printf("\r\n\r\nlow power example\r\n\r\n");
+	// This delay gives us some time to reprogram the device. 
+	// Otherwise if the device enters standby mode we can't 
+	// program it any more.
+	Delay_Ms(5000);
 
-	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
-	// GPIO D4 Push-Pull
-	GPIOD->CFGLR &= ~(0xf<<(4*4));
-	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
-	GPIOD->OUTDR |= (1 << 4);
+	printf("\r\n\r\nlow power example\r\n\r\n");
 
-	// give the user time to open the terminal connection
-	//Delay_Ms(5000);
-	printf("5000ms wait over\r\n");
+	// Set all GPIOs to input pull up
+	RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD;
+	// GPIOA: Set to output
+	GPIOA->CFGLR = (GPIO_CNF_IN_PUPD<<(4*2)) |	
+				   (GPIO_CNF_IN_PUPD<<(4*1));
+	GPIOA->BSHR = GPIO_BSHR_BS2 | GPIO_BSHR_BR1;
+	GPIOC->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) |
+				   (GPIO_CNF_IN_PUPD<<(4*6)) |
+				   (GPIO_CNF_IN_PUPD<<(4*5)) |
+				   (GPIO_CNF_IN_PUPD<<(4*4)) |
+				   (GPIO_CNF_IN_PUPD<<(4*3)) |
+				   (GPIO_CNF_IN_PUPD<<(4*2)) |
+				   (GPIO_CNF_IN_PUPD<<(4*1)) |
+				   (GPIO_CNF_IN_PUPD<<(4*0));
+	GPIOC->BSHR = GPIO_BSHR_BS7 |
+				  GPIO_BSHR_BS6 |
+				  GPIO_BSHR_BS5 |
+				  GPIO_BSHR_BS4 |
+				  GPIO_BSHR_BS3 |
+				  GPIO_BSHR_BS2 |
+				  GPIO_BSHR_BS1 |
+				  GPIO_BSHR_BS0;
+	GPIOD->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) |
+				   (GPIO_CNF_IN_PUPD<<(4*6)) |
+				   (GPIO_CNF_IN_PUPD<<(4*5)) |
+				   (GPIO_CNF_IN_PUPD<<(4*4)) |
+				   (GPIO_CNF_IN_PUPD<<(4*3)) |
+				   (GPIO_CNF_IN_PUPD<<(4*2)) |
+				   (GPIO_CNF_IN_PUPD<<(4*0));
+	GPIOD->BSHR = GPIO_BSHR_BS7 |
+				  GPIO_BSHR_BS6 |
+				  GPIO_BSHR_BS5 |
+				  GPIO_BSHR_BS4 |
+				  GPIO_BSHR_BS3 |
+				  GPIO_BSHR_BS2 |
+				  GPIO_BSHR_BS0;
 	
+
 	// enable power interface module clock
 	RCC->APB1PCENR |= RCC_APB1Periph_PWR;
 
@@ -62,6 +88,5 @@ int main()
 		// restore clock to full speed
 		SystemInit();
 		printf("\r\nawake, %u\r\n", counter++);
-		GPIOD->OUTDR ^= (1 << 4);
 	}
 }
diff --git a/examples/standby_btn/README.md b/examples/standby_btn/README.md
index 794e8b4ccc30b278904c12658a303c200170b380..4b6421c99cef584df8d4a0f91fde4b1418dbe453 100644
--- a/examples/standby_btn/README.md
+++ b/examples/standby_btn/README.md
@@ -1,15 +1,30 @@
 # the deepest slumber
 
+**WARNING: You MUST hard-reboot the CH32V003 to allow it to go into deep sleep. You cannot go from flashing to deep sleep without a hard power cycle.**
+
 This example serves to show how to put the CH32V003 into its lowest power state (standby) and have it wake with a button press.  
 
-Power consumption should be around 10uA.  
+Power consumption should be around 9uA.
+
+To enter 10uA standby mode you must perform these steps:
+
+1. GPIOs other than the wake up pin can be set to either input or output mode (see notes).
+2. Set GPIO(s) for wake up to input mode with appropriate pull up/down.  
+3. Enable AFIO clock and set AFIO_EXTICR to the wakeup channel.
+4. Configure EXTI event.
+5. Set PWR_CTLR_PDDS bit in PWR_CTLR (Setting PWREN in RCC_APB1PCENR is not required hum?)
+6. Set SLEEPDEEP bit in PFIC_SCTLR
+7. Call __WFE() to enter standby mode.
 
-The MCU only toggles the LED and prints a message, then it goes back to sleep.  
-The LED staying on demonstrates that GPIO keeps its state even when the rest of the mcu is in a coma.  
+Note:
+* GPIOs in output mode will retain state during standby.
+* GPIO if set to input mode must have internal or external pulling resistor. Floating input pin will cause 100uA standby current. 
+* Once CH32V003 enters standby mode, it won't respond to any SWDIO command, therefor cannot be reprogrammed. User must provide a way to have the processor stay awake for reprogramming, e.g. some delay at startup.
+* Debug circuitry will consume power. If minichlink terminal is active (including immediately after flashing), standby current will stay around 1.2mA until power cycle.
 
 Based on the groundwork of Marek M.  
 
 ## circuit
 
-Connect LED to PD4 (with resistor), connect button to GND and PD2.  
+Connect button to GND and PD2.  
 There is no debouncing but it should suffice for waking the chip.
diff --git a/examples/standby_btn/standby_btn.c b/examples/standby_btn/standby_btn.c
index 33f55a89cb8c29e7ae7991cb01c0fac41ed183cb..be6c99b7f109db9e04af80dea2d94618a5aac4d8 100644
--- a/examples/standby_btn/standby_btn.c
+++ b/examples/standby_btn/standby_btn.c
@@ -3,40 +3,60 @@
 #include "ch32v003fun.h"
 #include <stdio.h>
 
-void EXTI7_0_IRQHandler( void ) __attribute__((interrupt));
-void EXTI7_0_IRQHandler( void ) {
-	//GPIOD->OUTDR ^= (1 << 4);
-}
-
-
 
 int main()
 {
 	SystemInit();
-	Delay_Ms(100);
+	
+	// This delay gives us some time to reprogram the device. 
+	// Otherwise if the device enters standby mode we can't 
+	// program it any more.
+	Delay_Ms(5000);
 
 	printf("\n\nlow power example\n\n");
+	RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD;
+	// GPIOA: Set to output
+	GPIOA->CFGLR = ((GPIO_CNF_OUT_PP | GPIO_Speed_2MHz)<<(4*2)) |
+				   ((GPIO_CNF_OUT_PP | GPIO_Speed_2MHz)<<(4*1));
+	GPIOA->BSHR = GPIO_BSHR_BS2 | GPIO_BSHR_BR1;
+	// GPIOC: Set to input with mixed pull-up / pull-down
+	GPIOC->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) |
+				   (GPIO_CNF_IN_PUPD<<(4*6)) |
+				   (GPIO_CNF_IN_PUPD<<(4*5)) |
+				   (GPIO_CNF_IN_PUPD<<(4*4)) |
+				   (GPIO_CNF_IN_PUPD<<(4*3)) |
+				   (GPIO_CNF_IN_PUPD<<(4*2)) |
+				   (GPIO_CNF_IN_PUPD<<(4*1)) |
+				   (GPIO_CNF_IN_PUPD<<(4*0));
+	GPIOC->BSHR = GPIO_BSHR_BS7 |
+				  GPIO_BSHR_BR6 |
+				  GPIO_BSHR_BS5 |
+				  GPIO_BSHR_BR4 |
+				  GPIO_BSHR_BS3 |
+				  GPIO_BSHR_BR2 |
+				  GPIO_BSHR_BS1 |
+				  GPIO_BSHR_BR0;
+	// GPIOD: D2 set to input pull-up
+	GPIOD->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) |
+				   (GPIO_CNF_IN_PUPD<<(4*6)) |
+				   (GPIO_CNF_IN_PUPD<<(4*5)) |
+				   (GPIO_CNF_IN_PUPD<<(4*4)) |
+				   (GPIO_CNF_IN_PUPD<<(4*3)) |
+				   (GPIO_CNF_IN_PUPD<<(4*2)) |
+				   (GPIO_CNF_IN_PUPD<<(4*0));
+	GPIOD->BSHR = GPIO_BSHR_BR7 |
+				  GPIO_BSHR_BS6 |
+				  GPIO_BSHR_BR5 |
+				  GPIO_BSHR_BS4 |
+				  GPIO_BSHR_BR3 |
+				  GPIO_BSHR_BS2 |
+				  GPIO_BSHR_BR0;
 
-	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
-	// GPIO D4 Push-Pull
-	GPIOD->CFGLR &= ~(0xf<<(4*4));
-	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
-	GPIOD->OUTDR |= (1 << 4);
-
-	// give the user time to open the terminal connection
-	//Delay_Ms(5000);
-	//printf("5000ms wait over\r\n");
-	
-	// enable alternate IO function module clock
+	// AFIO is needed for EXTI
 	RCC->APB2PCENR |= RCC_AFIOEN;
 
-	// configure button on PD2 as input, pullup
-	GPIOD->CFGLR &= ~(0xf<<(2*4));
-	GPIOD->CFGLR |= (GPIO_CNF_IN_PUPD)<<(2*4);
-	GPIOD->BSHR = (1 << 2);
-
 	// assign pin 2 interrupt from portD (0b11) to EXTI channel 2
-	AFIO->EXTICR |= (uint32_t)(0b11 << (2 * 2));
+	AFIO->EXTICR |= (uint32_t)(0b11 << (2*2));
 
 	// enable line2 interrupt event
 	EXTI->EVENR |= EXTI_Line2;
@@ -56,6 +76,6 @@ int main()
 		// restore clock to full speed
 		SystemInit();
 		printf("\nawake, %u\n", counter++);
-		GPIOD->OUTDR ^= (1 << 4);
+		Delay_Ms(5000);	// wake and reflash can happen here
 	}
 }
diff --git a/extralibs/ch32v003_SPI.h b/extralibs/ch32v003_SPI.h
index 0da599a3e1d0ee764de40930ce69002f07e94820..69a7e6a0edbb914651a4c4369da60b18b7242305 100644
--- a/extralibs/ch32v003_SPI.h
+++ b/extralibs/ch32v003_SPI.h
@@ -75,7 +75,7 @@ static inline void SPI_write_16(uint16_t data);
 // send a command and get a response from the SPI device
 // you'll use this for most devices
 static inline uint8_t SPI_transfer_8(uint8_t data);
-static inline uint8_t SPI_transfer_16(uint16_t data);
+static inline uint16_t SPI_transfer_16(uint16_t data);
 
 // SPI peripheral power enable / disable (default off, init() automatically enables)
 // send SPI peripheral to sleep
@@ -238,15 +238,15 @@ static inline void SPI_init() {
 }
 
 static inline void SPI_begin_8() {
-	SPI1->CTLR1 |= SPI_DataSize_8b;					// DFF 16bit data-length enable, writable only when SPE is 0
-	SPI1->CTLR1 |= CTLR1_SPE_Set;
+	SPI1->CTLR1 &= ~(SPI_CTLR1_DFF);			// DFF 16bit data-length enable, writable only when SPE is 0
+	SPI1->CTLR1 |= SPI_CTLR1_SPE;
 }
 static inline void SPI_begin_16() {
-	SPI1->CTLR1 |= SPI_DataSize_16b;				// DFF 16bit data-length enable, writable only when SPE is 0
-	SPI1->CTLR1 |= CTLR1_SPE_Set;
+	SPI1->CTLR1 |= SPI_CTLR1_DFF;				// DFF 16bit data-length enable, writable only when SPE is 0
+	SPI1->CTLR1 |= SPI_CTLR1_SPE;
 }
 static inline void SPI_end() {
-	SPI1->CTLR1 &= CTLR1_SPE_Reset;
+	SPI1->CTLR1 &= ~(SPI_CTLR1_SPE);
 }
 
 #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3)
@@ -290,7 +290,7 @@ static inline uint8_t SPI_transfer_8(uint8_t data) {
 	#endif
 	return SPI_read_8();
 }
-static inline uint8_t SPI_transfer_16(uint16_t data) {
+static inline uint16_t SPI_transfer_16(uint16_t data) {
 	#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) 
 		SPI_NSS_software_high();
 	#endif
@@ -333,7 +333,13 @@ static inline uint8_t SPI_is_RX_empty() {
 static inline void SPI_wait_RX_available() {
 	while(!(SPI1->STATR & SPI_STATR_RXNE)) {}
 }
-
+static inline void SPI_wait_not_busy() {
+	while((SPI1->STATR & SPI_STATR_BSY) != 0) {}
+}
+static inline void SPI_wait_transmit_finished() {
+	SPI_wait_TX_complete();
+	SPI_wait_not_busy();
+}
 
 
 //######## implementation block
diff --git a/extralibs/ch32v003_touch.h b/extralibs/ch32v003_touch.h
new file mode 100644
index 0000000000000000000000000000000000000000..88756afe48cb5464275262f1f06a84c613af72ef
--- /dev/null
+++ b/extralibs/ch32v003_touch.h
@@ -0,0 +1,201 @@
+#ifndef _CH32V003_TOUCH_H
+#define _CH32V003_TOUCH_H
+
+/** ADC-based Capactive Touch Control.
+
+	see cap_touch_adc.c for an example.
+
+	// Enable GPIOD, C and ADC
+	RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1;
+	InitTouchADC();
+
+
+	// Then do this any time you want to read some touches.
+	sum[0] += ReadTouchPin( GPIOA, 2, 0, iterations );
+	sum[1] += ReadTouchPin( GPIOA, 1, 1, iterations );
+	sum[2] += ReadTouchPin( GPIOC, 4, 2, iterations );
+	sum[3] += ReadTouchPin( GPIOD, 2, 3, iterations );
+	sum[4] += ReadTouchPin( GPIOD, 3, 4, iterations );
+	sum[5] += ReadTouchPin( GPIOD, 5, 5, iterations );
+	sum[6] += ReadTouchPin( GPIOD, 6, 6, iterations );
+	sum[7] += ReadTouchPin( GPIOD, 4, 7, iterations );
+*/
+
+
+
+#define TOUCH_ADC_SAMPLE_TIME 2  // Tricky: Don't change this without a lot of experimentation.
+
+// Can either be 0 or 1.
+// If 0: Measurement low and rises high.  So more pressed is smaller number.
+// If 1: Higher number = harder press. Good to pair with TOUCH_FLAT.
+// If you are doing more prox, use mode 0, otherwise, use mode 1.
+#define TOUCH_SLOPE     1
+
+// If you set this to 1, it will glitch the line, so it will only read
+// anything reasonable if the capacitance can overcome that initial spike.
+// Typically, it seems if you use this you probbly don't need to do
+// any pre-use calibration.
+#define TOUCH_FLAT      0
+
+static void InitTouchADC( );
+void InitTouchADC( )
+{
+	// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide sys clock by 2
+	RCC->CFGR0 &= ~(0x1F<<11);
+
+	// Set up single conversion on chl 2
+	ADC1->RSQR1 = 0;
+	ADC1->RSQR2 = 0;
+
+	// turn on ADC and set rule group to sw trig
+	ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL;
+	
+	// Reset calibration
+	ADC1->CTLR2 |= ADC_RSTCAL;
+	while(ADC1->CTLR2 & ADC_RSTCAL);
+	
+	// Calibrate
+	ADC1->CTLR2 |= ADC_CAL;
+	while(ADC1->CTLR2 & ADC_CAL);
+}
+
+// Run from RAM to get even more stable timing.
+// This function call takes about 8.1uS to execute.
+static uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) __attribute__((noinline, section(".srodata")));
+uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations )
+{
+	uint32_t ret = 0;
+
+	ADC1->RSQR3 = adcno;
+	ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME<<(3*adcno);
+
+	uint32_t CFGBASE = io->CFGLR & (~(0xf<<(4*portpin)));
+	uint32_t CFGFLOAT = ((GPIO_CFGLR_IN_PUPD)<<(4*portpin)) | CFGBASE;
+	uint32_t CFGDRIVE = (GPIO_CFGLR_OUT_2Mhz_PP)<<(4*portpin) | CFGBASE;
+
+	// If we run multiple times with slightly different wait times, we can
+	// reduce the impact of the ADC's DNL.
+
+
+#if TOUCH_FLAT == 1
+#define RELEASEIO io->BSHR = 1<<(portpin+16*TOUCH_SLOPE); io->CFGLR = CFGFLOAT;
+#else
+#define RELEASEIO io->CFGLR = CFGFLOAT; io->BSHR = 1<<(portpin+16*TOUCH_SLOPE);
+#endif
+
+#define INNER_LOOP( n ) \
+	{ \
+		/* Only lock IRQ for a very narrow window. */                           \
+		__disable_irq();                                                        \
+                                                                                \
+		/* Tricky - we start the ADC BEFORE we transition the pin.  By doing    \
+			this We are catching it onthe slope much more effectively.  */      \
+		ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL;                      \
+                                                                                \
+		ADD_N_NOPS( n )                                                         \
+                                                                                \
+		RELEASEIO                                                               \
+																			    \
+		/* Sampling actually starts here, somewhere, so we can let other        \
+			interrupts run */                                                   \
+		__enable_irq();                                                         \
+		while(!(ADC1->STATR & ADC_EOC));                                        \
+		io->CFGLR = CFGDRIVE;                                                   \
+		io->BSHR = 1<<(portpin+(16*(1-TOUCH_SLOPE)));                          \
+		ret += ADC1->RDATAR;                                                    \
+	}
+
+	int i;
+	for( i = 0; i < iterations; i++ )
+	{
+		// Wait a variable amount of time based on loop iteration, in order
+		// to get a variety of RC points and minimize DNL.
+
+		INNER_LOOP( 0 );
+		INNER_LOOP( 2 );
+		INNER_LOOP( 4 );
+	}
+
+	return ret;
+}
+
+// Run from RAM to get even more stable timing.
+// This function call takes about 8.1uS to execute.
+static uint32_t ReadTouchPinSafe( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) __attribute__((noinline, section(".srodata")));
+uint32_t ReadTouchPinSafe( GPIO_TypeDef * io, int portpin, int adcno, int iterations )
+{
+	uint32_t ret = 0;
+
+	ADC1->RSQR3 = adcno;
+	ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME<<(3*adcno);
+
+	// If we run multiple times with slightly different wait times, we can
+	// reduce the impact of the ADC's DNL.
+
+#define INNER_LOOP_SAFE( n ) \
+	{ \
+		/* Only lock IRQ for a very narrow window. */                           \
+		__disable_irq();                                                        \
+		                                                                        \
+                                                                                \
+		/* Tricky - we start the ADC BEFORE we transition the pin.  By doing    \
+			this We are catching it onthe slope much more effectively.  */      \
+		ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL;                      \
+                                                                                \
+		ADD_N_NOPS( n )                                                         \
+                                                                                \
+		io->CFGLR = ((GPIO_CFGLR_IN_PUPD)<<(4*portpin)) | (io->CFGLR & (~(0xf<<(4*portpin))));                                                 \
+        io->BSHR = 1<<(portpin+16*TOUCH_SLOPE);                                \
+																			    \
+		/* Sampling actually starts here, somewhere, so we can let other        \
+			interrupts run */                                                   \
+		__enable_irq();                                                         \
+		while(!(ADC1->STATR & ADC_EOC));                                        \
+		__disable_irq();                                                        \
+		io->CFGLR = (GPIO_CFGLR_OUT_2Mhz_PP)<<(4*portpin) | (io->CFGLR & (~(0xf<<(4*portpin))));                                                  \
+		__enable_irq();                                                         \
+		io->BSHR = 1<<(portpin+(16*(1-TOUCH_SLOPE)));                          \
+		ret += ADC1->RDATAR;                                                    \
+	}
+
+	int i;
+	for( i = 0; i < iterations; i++ )
+	{
+		// Wait a variable amount of time based on loop iteration, in order
+		// to get a variety of RC points and minimize DNL.
+
+		INNER_LOOP_SAFE( 0 );
+		INNER_LOOP_SAFE( 2 );
+		INNER_LOOP_SAFE( 4 );
+	}
+
+	return ret;
+}
+
+
+#endif
+
+/*
+ * MIT License
+ * 
+ * Copyright (c) 2023 Valve Corporation
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
diff --git a/minichlink/99-minichlink.rules b/minichlink/99-minichlink.rules
index 8cca6989adad49aceff2f035b04e8024dc76446e..9fa431775dad75e8aeb8b9155987136e879090cd 100644
--- a/minichlink/99-minichlink.rules
+++ b/minichlink/99-minichlink.rules
@@ -1,4 +1,6 @@
 SUBSYSTEM=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="8010", GROUP="plugdev", MODE="0660"
 SUBSYSTEM=="usb", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="4004", GROUP="plugdev", MODE="0660"
 KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="4004", GROUP="plugdev", MODE="0660"
-
+# rv003usb bootloader
+KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="b003", GROUP="plugdev", MODE="0660"
+#KERNEL=="hiddev*", SUBSYSTEM=="usbmisc", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="b003", GROUP="plugdev", MODE="0660"
diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c
index 3c610a48db6f2952b8e646e79fabc6eb790d0d89..dc897e69cc1d02c2def7bdc97f7a09ee740f0dfd 100644
--- a/minichlink/minichlink.c
+++ b/minichlink/minichlink.c
@@ -114,7 +114,7 @@ int main( int argc, char ** argv )
 			if( i < argc )
 				hints.serial_port = argv[i];
 		}
-		else if( strncmp( v, "-c", 2 ) == 0 )
+		else if( strncmp( v, "-C", 2 ) == 0 )
 		{
 			i++;
 			if( i < argc )
@@ -649,6 +649,7 @@ help:
 	fprintf( stderr, " -t Disable 3.3V\n" );
 	fprintf( stderr, " -f Disable 5V\n" );
 	fprintf( stderr, " -c [serial port for Ardulink, try /dev/ttyACM0 or COM11 etc]\n" );
+	fprintf( stderr, " -C [specified programmer, eg. b003boot, ardulink, esp32s2chfun]\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" );
diff --git a/platformio.ini b/platformio.ini
index b20948c308573b4c6a792953fa544b65c6b559c0..9f25b5b0b0706c3094c8314c6e701d73b4bf5cab 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -26,32 +26,125 @@ extra_libs_srcs = +<extralibs>
 ; 2. Add build_src_filter with fun base files + example folder (+ extra libraries if used) for source files
 ; 3. Add additional build flags as needed (see uartdemo)
 ; 4. Switch to new environment in VSCode bottom taskbar (https://docs.platformio.org/en/latest/integration/ide/vscode.html#project-tasks)
+[env:adc_dma_opamp]
+build_src_filter = ${fun_base.build_src_filter} +<examples/adc_dma_opamp>
+
+[env:adc_fixed_fs]
+build_src_filter = ${fun_base.build_src_filter} +<examples/adc_fixed_fs>
+
+[env:adc_polled]
+build_src_filter = ${fun_base.build_src_filter} +<examples/adc_polled>
+
 [env:blink]
 build_src_filter = ${fun_base.build_src_filter} +<examples/blink>
 
 [env:bootload]
 build_src_filter = ${fun_base.build_src_filter} +<examples/bootload>
 
+[env:cap_touch_adc]
+build_src_filter = ${fun_base.build_src_filter} +<examples/cap_touch_adc>
+
+[env:cap_touch_exti]
+build_src_filter = ${fun_base.build_src_filter} +<examples/cap_touch_exti>
+
+[env:cpp_virtual_methods]
+build_src_filter = ${fun_base.build_src_filter} +<examples/cpp_virtual_methods>
+
 [env:debugprintfdemo]
 build_src_filter = ${fun_base.build_src_filter} +<examples/debugprintfdemo>
 
+[env:direct_gpio]
+build_src_filter = ${fun_base.build_src_filter} +<examples/direct_gpio>
+
+[env:dma_gpio]
+build_src_filter = ${fun_base.build_src_filter} +<examples/dma_gpio>
+
 [env:external_crystal]
 build_src_filter = ${fun_base.build_src_filter} +<examples/external_crystal>
 
+[env:exti_pin_change_isr]
+build_src_filter = ${fun_base.build_src_filter} +<examples/exti_pin_change_isr>
+
+[env:flashtest]
+build_src_filter = ${fun_base.build_src_filter} +<examples/flashtest>
+
 [env:GPIO]
 build_src_filter = ${fun_base.build_src_filter} ${fun_base.extra_libs_srcs} +<examples/GPIO>
 
+[env:i2c_oled]
+build_src_filter = ${fun_base.build_src_filter}  +<examples/i2c_oled>
+
+[env:i2c_slave]
+build_src_filter = ${fun_base.build_src_filter}  +<examples/i2c_slave>
+
+[env:input_capture]
+build_src_filter = ${fun_base.build_src_filter}  +<examples/input_capture>
+
+[env:iwdg]
+build_src_filter = ${fun_base.build_src_filter} +<examples/iwdg>
+
+[env:hsitrim]
+build_src_filter = ${fun_base.build_src_filter}  +<examples/hsitrim>
+
+[env:MCOtest]
+build_src_filter = ${fun_base.build_src_filter}  +<examples/MCOtest>
+
 [env:optionbytes]
 build_src_filter = ${fun_base.build_src_filter} +<examples/optionbytes>
 
+[env:optiondata]
+build_src_filter = ${fun_base.build_src_filter} +<examples/optiondata>
+
 [env:run_from_ram]
 build_src_filter = ${fun_base.build_src_filter} +<examples/run_from_ram>
 
+[env:self_modify_code]
+build_src_filter = ${fun_base.build_src_filter} +<examples/self_modify_code>
+
+[env:spi_24L01_rx]
+build_src_filter = ${fun_base.build_src_filter} +<examples/spi_24L01_rx>
+
+[env:spi_24L01_tx]
+build_src_filter = ${fun_base.build_src_filter} +<examples/spi_24L01_tx>
+
+[env:spi_dac]
+build_src_filter = ${fun_base.build_src_filter} +<examples/spi_dac>
+
+[env:spi_oled]
+build_src_filter = ${fun_base.build_src_filter} ${fun_base.extra_libs_srcs} +<examples/spi_oled>
+
+[env:standby_autowake]
+build_src_filter = ${fun_base.build_src_filter} +<examples/standby_autowake>
+
+[env:standby_btn]
+build_src_filter = ${fun_base.build_src_filter} +<examples/standby_btn>
+
+[env:struct_direct_gpio]
+build_src_filter = ${fun_base.build_src_filter} +<examples/struct_direct_gpio>
+
+[env:struct_gpio]
+build_src_filter = ${fun_base.build_src_filter} +<examples/struct_gpio>
+
+[env:sysclk_config]
+build_src_filter = ${fun_base.build_src_filter} +<examples/sysclk_config>
+
+[env:systick_irq]
+build_src_filter = ${fun_base.build_src_filter} +<examples/systick_irq>
+
 [env:template]
 build_src_filter = ${fun_base.build_src_filter} +<examples/template>
 
-[env:self_modify_code]
-build_src_filter = ${fun_base.build_src_filter} +<examples/self_modify_code>
+[env:tim1_pwm]
+build_src_filter = ${fun_base.build_src_filter} +<examples/tim1_pwm>
+
+[env:tim2_encoder]
+build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_encoder>
+
+[env:tim2_pwm]
+build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_pwm>
+
+[env:tim2_pwm_remap]
+build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_pwm_remap>
 
 [env:uartdemo]
 build_flags = ${fun_base.build_flags} -DSTDOUT_UART