C64 emulation on STM32F429 Discovery board with builtin LCD and USB keyboard support (OTG). More info at davevw.com and/or github.com/davervw

Dependencies:   LCD_DISCO_F429ZI BSP_DISCO_F429ZI USBHOST

Revision:
4:8476be802690
Parent:
3:f978776f810c
Child:
6:65f96b9dd1d3
--- a/emuc64.cpp	Fri Apr 10 04:32:54 2020 +0000
+++ b/emuc64.cpp	Mon Apr 13 05:36:43 2020 +0000
@@ -1,9 +1,10 @@
-// emuc64.c - Commodore 64 Emulator
+// emuc64.cpp - Commodore 64 Emulator
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
-// c-simple-emu-cbm (C Portable Version)
-// C64/6502 Emulator for Microsoft Windows Console
+// C64-stm429_discovery
+// C64/6502 Emulator targeting STM32F429 LCD/USBHOST
+// [ported from c-simple-emu-cbm (C Portable Version - for console)]
 //
 // MIT License
 //
@@ -11,7 +12,6 @@
 // davevw.com
 //
 // 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
@@ -64,20 +64,141 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 #include <mbed.h>
-#include <LocalFileSystem.h>
+#include "LCD_DISCO_F429ZI.h"
+#include "USBHostKeyboard.h"
+//#include <LocalFileSystem.h>
 #include "emu6502.h"
-#include "cbmconsole.h"
-
-// global references
-extern Serial pc;
 
 // globals
 char* StartupPRG = 0;
 
 // locals
-static int startup_state = 0;
+
+LCD_DISCO_F429ZI lcd;
+
+//static int startup_state = 0;
+//LocalFileSystem local("local");
+
+static int scan_codes[9] = { 64, 64, 64, 64, 64, 64, 64, 64, 64 } ;
+
+// USB key code to C64 keyboard scan code, plus shift modifiers
+// +256 means to apply L.Shift
+// -512 means to take away Shift
+static int usb_to_c64[2][100] = {
+{ // normal/other modifier
+	64, 64,	64,	64, 10, 28, 20, 18, 14, 21, // na, na, na, na, a, b, c, d, e, f
+	26, 29, 33, 34, 37, 42, 36, 39, 38, 41, // g, h, i, j, k, l, m, n, o, p,
+	62, 17, 13, 22, 30, 31, 9, 23, 25, 12, // q, r, s, t, u, v, w, x, y, z
+	56, 59, 8, 11, 16, 19, 24, 27, 32, 35, // 1, 2, 3, 4, 5, 6, 7, 8, 9, 0
+	1, 63, 0, 58, 60, 43, 53, 256+45, 256+50, 48, // RET, STOP, DEL, CTRL, SPC, -, =, [, ], £
+	64, 50, 256+24, 64, 47, 44, 55, 64, 4, 256+4, // na, ;, ', na, ,, ., /, na, f1, f2
+	5, 256+5, 6, 256+6, 3, 256+3, 64, 64, 64, 64, // f3, f4, f5, f6, f7, f8, na, na, na, na
+	64, 64, 63, 256+0, 51, 64, 0, 64, 64, 2, // na, na, STOP, INS, HM, na, DEL, na, na, RT
+	256+2, 7, 256+7, 64, 55, 49, 43, 40, 1, 56, // LT, DN, UP, na, /, *, -, +, ENTER, 1
+	59, 8, 11, 16, 19, 24, 27, 32, 35, 44 // 2, 3, 4, 5, 6, 7, 8, 9, 0, . (keypad)
+},
+{ // shift modifier
+	64, 64,	64,	64, 10, 28, 20, 18, 14, 21, // na, na, na, na, a, b, c, d, e, f
+	26, 29, 33, 34, 37, 42, 36, 39, 38, 41, // g, h, i, j, k, l, m, n, o, p,
+	62, 17, 13, 22, 30, 31, 9, 23, 25, 12, // q, r, s, t, u, v, w, x, y, z
+	56, 512+46, 8, 11, 16, 512+54, 19, 512+49, 27, 32, // !, @, #, $, %, ^, &, *, (, )
+	1, 63, 0, 58, 60, 512+57, 512+40, 256+45, 256+50, 48, // RET, STOP, DEL, CTRL, SPC, L.Arrow, +, [, ], £
+	64, 512+45, 256+59, 64, 47, 44, 55, 64, 4, 256+4, // na, :, ", na, ,, ., /, na, f1, f2
+	5, 256+5, 6, 256+6, 3, 256+3, 64, 64, 64, 64, // f3, f4, f5, f6, f7, f8, na, na, na, na
+	64, 64, 63, 256+0, 51, 64, 0, 64, 64, 2, // na, na, STOP, INS, HM, na, DEL, na, na, RT
+	256+2, 7, 256+7, 64, 55, 49, 43, 40, 1, 56, // LT, DN, UP, na, /, *, -, +, ENTER, 1
+	59, 8, 11, 16, 19, 24, 27, 32, 35, 44 // 2, 3, 4, 5, 6, 7, 8, 9, 0, . (keypad)
+}
+};
+
+// The initial goal of the keyboard tables and logic is to provide access to at 
+// least the following keys from a standard USB keyboard, symbolic mapping more 
+// like a PC (not positional)
+// USB keyboard support is provided by USBHOST library and STM32F429 BSP
+//
+// STOP(ESC) F1 F2 F3 F4 F5 F6 F7 F8                                  Run/Stop(Pause/Break)
+//           1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) -_ += Del/Ins(Back)    Ins Hme/Clr     / * -
+// Ctrl(Tab) Q  W  E  R  T  Y  U  I  O  P  [  ]  £ (\)            Del           7 8 9 +
+//           A  S  D  F  G  H  J  K  L  ;: '" Return(ENTER)                     4 5 6
+// LShift    Z  X  C  V  B  N  M  ,< .> /?  RShift                     Up       1 2 3
+// C=(Ctrl)           SPACEBAR              C=(Ctrl)              Lft Down Rt   0 .   Enter
+//
+// Note C64 Ctrl key is the PC Tab key
+// Note C64 Commodore key is the PC Ctrl key
+// Note keys and modifiers not shown do nothing, not support for C64
+//
+// Also, most Commodore key combinations should work for alphanumeric, some will 
+// be different/missing for punctuation, full mapping of PETSCII will require 
+// additional development
 
-//LocalFileSystem local("local");
+static void onKeyData(uint8_t len, uint8_t* data)
+{
+//	static DigitalOut led2(LED2);
+//	led2 = !led2;
+//	
+//	printf("len=%d", len);
+//	for (int i=0; i<len; ++i)
+//	   printf(" %02X", data[i]);
+//	printf("\n");
+//	fflush(stdout);
+	
+	if (len == 8)
+	{ 
+		if ((data[0] & 1) != 0) // PC Ctrl => Commodore
+			scan_codes[6] = 61;
+		else
+			scan_codes[6] = 64;
+			
+		if ((data[0] & 2) != 0) // LShift
+			scan_codes[7] = 15;
+		else
+			scan_codes[7] = 64;
+			
+		if ((data[0] & 0x20) != 0) // RShift
+			scan_codes[8] = 52;
+		else
+			scan_codes[8] = 64;
+
+	    for (int i=0; i<6; ++i)
+	    {
+			if (data[i+2] < 100)
+			{
+				scan_codes[i] = usb_to_c64[((data[0] & 0x22) != 0) ? 1 : 0][data[i+2]]; // L.Shift/R.Shift handled by a different table
+				if ((scan_codes[i] & 256) != 0)
+					scan_codes[7] = 15; // LShift
+				if (i==0 && (scan_codes[i] & 512) != 0)
+					scan_codes[7] = 64; // No LShift
+				scan_codes[i] &= 127; // take out shift codes
+				//printf(" %d", scan_code);
+				fflush(stdout);
+			}
+			else
+				scan_codes[i] = 64;
+		}
+			
+//		printf("C64 scan_code = %d\n", scan_code);
+	}
+}
+
+void keyboard_task(void const *)
+{
+    USBHostKeyboard keyboard;
+    
+    while(1) {
+        // try to connect a USB keyboard
+        while(!keyboard.connect())
+            Thread::wait(500);
+    
+        // when connected, attach handler called on keyboard event
+        //keyboard.attach(onKey);
+        //keyboard.attach(onKeyEvent);
+        keyboard.attach(onKeyData);
+        
+        // wait until the keyboard is disconnected
+        while(keyboard.connected())
+            Thread::wait(500);
+    }
+}
 
 //static void File_ReadAllBytes(byte* bytes, unsigned int size, const char* filename)
 //{
@@ -93,136 +214,136 @@
 //}
 
 // returns true if BASIC
-static bool LoadPRG(const char* filename)
-{
-	bool result;
-	byte lo, hi;
-	int file;
-	ushort loadaddr;
-	
-	file = open(filename, O_RDONLY);
-	if (file < 0
-		|| read(file, &lo, 1) != 1
-		|| read(file, &hi, 1) != 1
-		)
-	{
-		pc.printf("file ""%s"", errno=%d\n", filename, errno);
-		exit(1);
-	}
-	if (lo == 1)
-	{
-		loadaddr = (ushort)(GetMemory(43) | (GetMemory(44) << 8));
-		result = true;
-	}
-	else
-	{
-		loadaddr = (ushort)(lo | (hi << 8));
-		result = false;
-	}
-	while (true)
-	{
-		byte value;
-		if (read(file, &value, 1) == 1)
-			SetMemory(loadaddr++, value);
-		else
-			break;
-	}
-	close(file);
-	return result;
-}
+//static bool LoadPRG(const char* filename)
+//{
+//	bool result;
+//	byte lo, hi;
+//	int file;
+//	ushort loadaddr;
+//	
+//	file = open(filename, O_RDONLY);
+//	if (file < 0
+//		|| read(file, &lo, 1) != 1
+//		|| read(file, &hi, 1) != 1
+//		)
+//	{
+//		pc.printf("file ""%s"", errno=%d\n", filename, errno);
+//		exit(1);
+//	}
+//	if (lo == 1)
+//	{
+//		loadaddr = (ushort)(GetMemory(43) | (GetMemory(44) << 8));
+//		result = true;
+//	}
+//	else
+//	{
+//		loadaddr = (ushort)(lo | (hi << 8));
+//		result = false;
+//	}
+//	while (true)
+//	{
+//		byte value;
+//		if (read(file, &value, 1) == 1)
+//			SetMemory(loadaddr++, value);
+//		else
+//			break;
+//	}
+//	close(file);
+//	return result;
+//}
 
 bool ExecutePatch(void)
 {
-	if (PC == 0xFFD2) // CHROUT
-	{
-		CBM_Console_WriteChar((char)A);
-		// fall through to draw character in screen memory too
-	}
-	else if (PC == 0xFFCF) // CHRIN
-	{
-		A = CBM_Console_ReadChar();
-
-		// SetA equivalent for flags
-		Z = (A == 0);
-		N = ((A & 0x80) != 0);
-		C = false;
-
-		// RTS equivalent
-		byte lo = Pop();
-		byte hi = Pop();
-		PC = (ushort)(((hi << 8) | lo) + 1);
-
-		return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc.
-	}
-	else if (PC == 0xA474) // READY
-	{
-		if (StartupPRG != 0 && strlen(StartupPRG) > 0) // User requested program be loaded at startup
-		{
-			const char* filename = StartupPRG;
-			StartupPRG = 0;
-
-			if (LoadPRG(filename))
-			{
-				//UNNEW that I used in late 1980s, should work well for loang a program too, probably gleaned from BASIC ROM
-				//ldy #0
-				//lda #1
-				//sta(43),y
-				//iny
-				//sta(43),y
-				//jsr $a533 ; LINKPRG
-				//clc
-				//lda $22
-				//adc #2
-				//sta 45
-				//lda $23
-				//adc #0
-				//sta 46
-				//lda #0
-				//jsr $a65e ; CLEAR/CLR
-				//jmp $a474 ; READY
-
-				// This part shouldn't be necessary as we have loaded, not recovering from NEW, bytes should still be there
-				ushort addr = (ushort)(GetMemory(43) | (GetMemory(44) << 8));
-				SetMemory(addr, 1);
-				SetMemory((ushort)(addr + 1), 1);
-
-				// JSR equivalent
-				ushort retaddr = (ushort)(PC - 1);
-				Push(HI(retaddr));
-				Push(LO(retaddr));
-				PC = 0xA533; // LINKPRG
-
-				startup_state = 1; // should be able to regain control when returns...
-
-				return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc.
-			}
-		}
-		else if (startup_state == 1)
-		{
-			ushort addr = (ushort)(GetMemory(0x22) | (GetMemory(0x23) << 8) + 2);
-			SetMemory(45, (byte)addr);
-			SetMemory(46, (byte)(addr >> 8));
-
-			// JSR equivalent
-			ushort retaddr = (ushort)(PC - 1);
-			Push(HI(retaddr));
-			Push(LO(retaddr));
-			PC = 0xA65E; // CLEAR/CLR
-			A = 0;
-
-			startup_state = 2; // should be able to regain control when returns...
-
-			return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc.
-		}
-		else if (startup_state == 2)
-		{
-			CBM_Console_Push("RUN\r");
-			PC = 0xA47B; // skip READY message, but still set direct mode, and continue to MAIN
-			C = false;
-			startup_state = 0;
-			return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc.
-		}
-	}
+//	if (PC == 0xFFD2) // CHROUT
+//	{
+//		CBM_Console_WriteChar((char)A);
+//		// fall through to draw character in screen memory too
+//	}
+//	else if (PC == 0xFFCF) // CHRIN
+//	{
+//		A = CBM_Console_ReadChar();
+//
+//		// SetA equivalent for flags
+//		Z = (A == 0);
+//		N = ((A & 0x80) != 0);
+//		C = false;
+//
+//		// RTS equivalent
+//		byte lo = Pop();
+//		byte hi = Pop();
+//		PC = (ushort)(((hi << 8) | lo) + 1);
+//
+//		return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc.
+//	}
+//	else if (PC == 0xA474) // READY
+//	{
+//		if (StartupPRG != 0 && strlen(StartupPRG) > 0) // User requested program be loaded at startup
+//		{
+//			const char* filename = StartupPRG;
+//			StartupPRG = 0;
+//
+//			if (LoadPRG(filename))
+//			{
+//				//UNNEW that I used in late 1980s, should work well for loang a program too, probably gleaned from BASIC ROM
+//				//ldy #0
+//				//lda #1
+//				//sta(43),y
+//				//iny
+//				//sta(43),y
+//				//jsr $a533 ; LINKPRG
+//				//clc
+//				//lda $22
+//				//adc #2
+//				//sta 45
+//				//lda $23
+//				//adc #0
+//				//sta 46
+//				//lda #0
+//				//jsr $a65e ; CLEAR/CLR
+//				//jmp $a474 ; READY
+//
+//				// This part shouldn't be necessary as we have loaded, not recovering from NEW, bytes should still be there
+//				ushort addr = (ushort)(GetMemory(43) | (GetMemory(44) << 8));
+//				SetMemory(addr, 1);
+//				SetMemory((ushort)(addr + 1), 1);
+//
+//				// JSR equivalent
+//				ushort retaddr = (ushort)(PC - 1);
+//				Push(HI(retaddr));
+//				Push(LO(retaddr));
+//				PC = 0xA533; // LINKPRG
+//
+//				//startup_state = 1; // should be able to regain control when returns...
+//
+//				return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc.
+//			}
+//		}
+//		else if (startup_state == 1)
+//		{
+//			ushort addr = (ushort)(GetMemory(0x22) | (GetMemory(0x23) << 8) + 2);
+//			SetMemory(45, (byte)addr);
+//			SetMemory(46, (byte)(addr >> 8));
+//
+//			// JSR equivalent
+//			ushort retaddr = (ushort)(PC - 1);
+//			Push(HI(retaddr));
+//			Push(LO(retaddr));
+//			PC = 0xA65E; // CLEAR/CLR
+//			A = 0;
+//
+//			startup_state = 2; // should be able to regain control when returns...
+//
+//			return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc.
+//		}
+//		else if (startup_state == 2)
+//		{
+//			CBM_Console_Push("RUN\r");
+//			PC = 0xA47B; // skip READY message, but still set direct mode, and continue to MAIN
+//			C = false;
+//			startup_state = 0;
+//			return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc.
+//		}
+//	}
 	return false; // execute normally
 }
 
@@ -744,7 +865,138 @@
 '\xF6','\x6C','\x28','\x03','\x6C','\x2A','\x03','\x6C','\x2C','\x03','\x4C','\x9B','\xF6','\x4C','\x05','\xE5','\x4C','\x0A','\xE5','\x4C','\x00','\xE5','\x52','\x52','\x42','\x59','\x43','\xFE','\xE2','\xFC','\x48','\xFF'
 };
 
-static byte ram[24 * 1024];
+const byte chargen_rom[] = {
+'\x3C','\x66','\x6E','\x6E','\x60','\x62','\x3C','\x00','\x18','\x3C','\x66','\x7E','\x66','\x66','\x66','\x00','\x7C','\x66','\x66','\x7C','\x66','\x66','\x7C','\x00','\x3C','\x66','\x60','\x60','\x60','\x66','\x3C','\x00',
+'\x78','\x6C','\x66','\x66','\x66','\x6C','\x78','\x00','\x7E','\x60','\x60','\x78','\x60','\x60','\x7E','\x00','\x7E','\x60','\x60','\x78','\x60','\x60','\x60','\x00','\x3C','\x66','\x60','\x6E','\x66','\x66','\x3C','\x00',
+'\x66','\x66','\x66','\x7E','\x66','\x66','\x66','\x00','\x3C','\x18','\x18','\x18','\x18','\x18','\x3C','\x00','\x1E','\x0C','\x0C','\x0C','\x0C','\x6C','\x38','\x00','\x66','\x6C','\x78','\x70','\x78','\x6C','\x66','\x00',
+'\x60','\x60','\x60','\x60','\x60','\x60','\x7E','\x00','\x63','\x77','\x7F','\x6B','\x63','\x63','\x63','\x00','\x66','\x76','\x7E','\x7E','\x6E','\x66','\x66','\x00','\x3C','\x66','\x66','\x66','\x66','\x66','\x3C','\x00',
+'\x7C','\x66','\x66','\x7C','\x60','\x60','\x60','\x00','\x3C','\x66','\x66','\x66','\x66','\x3C','\x0E','\x00','\x7C','\x66','\x66','\x7C','\x78','\x6C','\x66','\x00','\x3C','\x66','\x60','\x3C','\x06','\x66','\x3C','\x00',
+'\x7E','\x18','\x18','\x18','\x18','\x18','\x18','\x00','\x66','\x66','\x66','\x66','\x66','\x66','\x3C','\x00','\x66','\x66','\x66','\x66','\x66','\x3C','\x18','\x00','\x63','\x63','\x63','\x6B','\x7F','\x77','\x63','\x00',
+'\x66','\x66','\x3C','\x18','\x3C','\x66','\x66','\x00','\x66','\x66','\x66','\x3C','\x18','\x18','\x18','\x00','\x7E','\x06','\x0C','\x18','\x30','\x60','\x7E','\x00','\x3C','\x30','\x30','\x30','\x30','\x30','\x3C','\x00',
+'\x0C','\x12','\x30','\x7C','\x30','\x62','\xFC','\x00','\x3C','\x0C','\x0C','\x0C','\x0C','\x0C','\x3C','\x00','\x00','\x18','\x3C','\x7E','\x18','\x18','\x18','\x18','\x00','\x10','\x30','\x7F','\x7F','\x30','\x10','\x00',
+'\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x18','\x18','\x18','\x18','\x00','\x00','\x18','\x00','\x66','\x66','\x66','\x00','\x00','\x00','\x00','\x00','\x66','\x66','\xFF','\x66','\xFF','\x66','\x66','\x00',
+'\x18','\x3E','\x60','\x3C','\x06','\x7C','\x18','\x00','\x62','\x66','\x0C','\x18','\x30','\x66','\x46','\x00','\x3C','\x66','\x3C','\x38','\x67','\x66','\x3F','\x00','\x06','\x0C','\x18','\x00','\x00','\x00','\x00','\x00',
+'\x0C','\x18','\x30','\x30','\x30','\x18','\x0C','\x00','\x30','\x18','\x0C','\x0C','\x0C','\x18','\x30','\x00','\x00','\x66','\x3C','\xFF','\x3C','\x66','\x00','\x00','\x00','\x18','\x18','\x7E','\x18','\x18','\x00','\x00',
+'\x00','\x00','\x00','\x00','\x00','\x18','\x18','\x30','\x00','\x00','\x00','\x7E','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x18','\x18','\x00','\x00','\x03','\x06','\x0C','\x18','\x30','\x60','\x00',
+'\x3C','\x66','\x6E','\x76','\x66','\x66','\x3C','\x00','\x18','\x18','\x38','\x18','\x18','\x18','\x7E','\x00','\x3C','\x66','\x06','\x0C','\x30','\x60','\x7E','\x00','\x3C','\x66','\x06','\x1C','\x06','\x66','\x3C','\x00',
+'\x06','\x0E','\x1E','\x66','\x7F','\x06','\x06','\x00','\x7E','\x60','\x7C','\x06','\x06','\x66','\x3C','\x00','\x3C','\x66','\x60','\x7C','\x66','\x66','\x3C','\x00','\x7E','\x66','\x0C','\x18','\x18','\x18','\x18','\x00',
+'\x3C','\x66','\x66','\x3C','\x66','\x66','\x3C','\x00','\x3C','\x66','\x66','\x3E','\x06','\x66','\x3C','\x00','\x00','\x00','\x18','\x00','\x00','\x18','\x00','\x00','\x00','\x00','\x18','\x00','\x00','\x18','\x18','\x30',
+'\x0E','\x18','\x30','\x60','\x30','\x18','\x0E','\x00','\x00','\x00','\x7E','\x00','\x7E','\x00','\x00','\x00','\x70','\x18','\x0C','\x06','\x0C','\x18','\x70','\x00','\x3C','\x66','\x06','\x0C','\x18','\x00','\x18','\x00',
+'\x00','\x00','\x00','\xFF','\xFF','\x00','\x00','\x00','\x08','\x1C','\x3E','\x7F','\x7F','\x1C','\x3E','\x00','\x18','\x18','\x18','\x18','\x18','\x18','\x18','\x18','\x00','\x00','\x00','\xFF','\xFF','\x00','\x00','\x00',
+'\x00','\x00','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\x00','\x00','\x30','\x30','\x30','\x30','\x30','\x30','\x30','\x30',
+'\x0C','\x0C','\x0C','\x0C','\x0C','\x0C','\x0C','\x0C','\x00','\x00','\x00','\xE0','\xF0','\x38','\x18','\x18','\x18','\x18','\x1C','\x0F','\x07','\x00','\x00','\x00','\x18','\x18','\x38','\xF0','\xE0','\x00','\x00','\x00',
+'\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xFF','\xFF','\xC0','\xE0','\x70','\x38','\x1C','\x0E','\x07','\x03','\x03','\x07','\x0E','\x1C','\x38','\x70','\xE0','\xC0','\xFF','\xFF','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0',
+'\xFF','\xFF','\x03','\x03','\x03','\x03','\x03','\x03','\x00','\x3C','\x7E','\x7E','\x7E','\x7E','\x3C','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\x00','\x36','\x7F','\x7F','\x7F','\x3E','\x1C','\x08','\x00',
+'\x60','\x60','\x60','\x60','\x60','\x60','\x60','\x60','\x00','\x00','\x00','\x07','\x0F','\x1C','\x18','\x18','\xC3','\xE7','\x7E','\x3C','\x3C','\x7E','\xE7','\xC3','\x00','\x3C','\x7E','\x66','\x66','\x7E','\x3C','\x00',
+'\x18','\x18','\x66','\x66','\x18','\x18','\x3C','\x00','\x06','\x06','\x06','\x06','\x06','\x06','\x06','\x06','\x08','\x1C','\x3E','\x7F','\x3E','\x1C','\x08','\x00','\x18','\x18','\x18','\xFF','\xFF','\x18','\x18','\x18',
+'\xC0','\xC0','\x30','\x30','\xC0','\xC0','\x30','\x30','\x18','\x18','\x18','\x18','\x18','\x18','\x18','\x18','\x00','\x00','\x03','\x3E','\x76','\x36','\x36','\x00','\xFF','\x7F','\x3F','\x1F','\x0F','\x07','\x03','\x01',
+'\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\xF0','\xF0','\xF0','\xF0','\xF0','\xF0','\xF0','\xF0','\x00','\x00','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00','\x00',
+'\x00','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xCC','\xCC','\x33','\x33','\xCC','\xCC','\x33','\x33','\x03','\x03','\x03','\x03','\x03','\x03','\x03','\x03',
+'\x00','\x00','\x00','\x00','\xCC','\xCC','\x33','\x33','\xFF','\xFE','\xFC','\xF8','\xF0','\xE0','\xC0','\x80','\x03','\x03','\x03','\x03','\x03','\x03','\x03','\x03','\x18','\x18','\x18','\x1F','\x1F','\x18','\x18','\x18',
+'\x00','\x00','\x00','\x00','\x0F','\x0F','\x0F','\x0F','\x18','\x18','\x18','\x1F','\x1F','\x00','\x00','\x00','\x00','\x00','\x00','\xF8','\xF8','\x18','\x18','\x18','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF',
+'\x00','\x00','\x00','\x1F','\x1F','\x18','\x18','\x18','\x18','\x18','\x18','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\x18','\x18','\x18','\x18','\x18','\x18','\xF8','\xF8','\x18','\x18','\x18',
+'\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xE0','\xE0','\xE0','\xE0','\xE0','\xE0','\xE0','\xE0','\x07','\x07','\x07','\x07','\x07','\x07','\x07','\x07','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00',
+'\xFF','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\xFF','\x03','\x03','\x03','\x03','\x03','\x03','\xFF','\xFF','\x00','\x00','\x00','\x00','\xF0','\xF0','\xF0','\xF0',
+'\x0F','\x0F','\x0F','\x0F','\x00','\x00','\x00','\x00','\x18','\x18','\x18','\xF8','\xF8','\x00','\x00','\x00','\xF0','\xF0','\xF0','\xF0','\x00','\x00','\x00','\x00','\xF0','\xF0','\xF0','\xF0','\x0F','\x0F','\x0F','\x0F',
+'\xC3','\x99','\x91','\x91','\x9F','\x99','\xC3','\xFF','\xE7','\xC3','\x99','\x81','\x99','\x99','\x99','\xFF','\x83','\x99','\x99','\x83','\x99','\x99','\x83','\xFF','\xC3','\x99','\x9F','\x9F','\x9F','\x99','\xC3','\xFF',
+'\x87','\x93','\x99','\x99','\x99','\x93','\x87','\xFF','\x81','\x9F','\x9F','\x87','\x9F','\x9F','\x81','\xFF','\x81','\x9F','\x9F','\x87','\x9F','\x9F','\x9F','\xFF','\xC3','\x99','\x9F','\x91','\x99','\x99','\xC3','\xFF',
+'\x99','\x99','\x99','\x81','\x99','\x99','\x99','\xFF','\xC3','\xE7','\xE7','\xE7','\xE7','\xE7','\xC3','\xFF','\xE1','\xF3','\xF3','\xF3','\xF3','\x93','\xC7','\xFF','\x99','\x93','\x87','\x8F','\x87','\x93','\x99','\xFF',
+'\x9F','\x9F','\x9F','\x9F','\x9F','\x9F','\x81','\xFF','\x9C','\x88','\x80','\x94','\x9C','\x9C','\x9C','\xFF','\x99','\x89','\x81','\x81','\x91','\x99','\x99','\xFF','\xC3','\x99','\x99','\x99','\x99','\x99','\xC3','\xFF',
+'\x83','\x99','\x99','\x83','\x9F','\x9F','\x9F','\xFF','\xC3','\x99','\x99','\x99','\x99','\xC3','\xF1','\xFF','\x83','\x99','\x99','\x83','\x87','\x93','\x99','\xFF','\xC3','\x99','\x9F','\xC3','\xF9','\x99','\xC3','\xFF',
+'\x81','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xFF','\x99','\x99','\x99','\x99','\x99','\x99','\xC3','\xFF','\x99','\x99','\x99','\x99','\x99','\xC3','\xE7','\xFF','\x9C','\x9C','\x9C','\x94','\x80','\x88','\x9C','\xFF',
+'\x99','\x99','\xC3','\xE7','\xC3','\x99','\x99','\xFF','\x99','\x99','\x99','\xC3','\xE7','\xE7','\xE7','\xFF','\x81','\xF9','\xF3','\xE7','\xCF','\x9F','\x81','\xFF','\xC3','\xCF','\xCF','\xCF','\xCF','\xCF','\xC3','\xFF',
+'\xF3','\xED','\xCF','\x83','\xCF','\x9D','\x03','\xFF','\xC3','\xF3','\xF3','\xF3','\xF3','\xF3','\xC3','\xFF','\xFF','\xE7','\xC3','\x81','\xE7','\xE7','\xE7','\xE7','\xFF','\xEF','\xCF','\x80','\x80','\xCF','\xEF','\xFF',
+'\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xE7','\xE7','\xFF','\xFF','\xE7','\xFF','\x99','\x99','\x99','\xFF','\xFF','\xFF','\xFF','\xFF','\x99','\x99','\x00','\x99','\x00','\x99','\x99','\xFF',
+'\xE7','\xC1','\x9F','\xC3','\xF9','\x83','\xE7','\xFF','\x9D','\x99','\xF3','\xE7','\xCF','\x99','\xB9','\xFF','\xC3','\x99','\xC3','\xC7','\x98','\x99','\xC0','\xFF','\xF9','\xF3','\xE7','\xFF','\xFF','\xFF','\xFF','\xFF',
+'\xF3','\xE7','\xCF','\xCF','\xCF','\xE7','\xF3','\xFF','\xCF','\xE7','\xF3','\xF3','\xF3','\xE7','\xCF','\xFF','\xFF','\x99','\xC3','\x00','\xC3','\x99','\xFF','\xFF','\xFF','\xE7','\xE7','\x81','\xE7','\xE7','\xFF','\xFF',
+'\xFF','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xCF','\xFF','\xFF','\xFF','\x81','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xFF','\xFF','\xFC','\xF9','\xF3','\xE7','\xCF','\x9F','\xFF',
+'\xC3','\x99','\x91','\x89','\x99','\x99','\xC3','\xFF','\xE7','\xE7','\xC7','\xE7','\xE7','\xE7','\x81','\xFF','\xC3','\x99','\xF9','\xF3','\xCF','\x9F','\x81','\xFF','\xC3','\x99','\xF9','\xE3','\xF9','\x99','\xC3','\xFF',
+'\xF9','\xF1','\xE1','\x99','\x80','\xF9','\xF9','\xFF','\x81','\x9F','\x83','\xF9','\xF9','\x99','\xC3','\xFF','\xC3','\x99','\x9F','\x83','\x99','\x99','\xC3','\xFF','\x81','\x99','\xF3','\xE7','\xE7','\xE7','\xE7','\xFF',
+'\xC3','\x99','\x99','\xC3','\x99','\x99','\xC3','\xFF','\xC3','\x99','\x99','\xC1','\xF9','\x99','\xC3','\xFF','\xFF','\xFF','\xE7','\xFF','\xFF','\xE7','\xFF','\xFF','\xFF','\xFF','\xE7','\xFF','\xFF','\xE7','\xE7','\xCF',
+'\xF1','\xE7','\xCF','\x9F','\xCF','\xE7','\xF1','\xFF','\xFF','\xFF','\x81','\xFF','\x81','\xFF','\xFF','\xFF','\x8F','\xE7','\xF3','\xF9','\xF3','\xE7','\x8F','\xFF','\xC3','\x99','\xF9','\xF3','\xE7','\xFF','\xE7','\xFF',
+'\xFF','\xFF','\xFF','\x00','\x00','\xFF','\xFF','\xFF','\xF7','\xE3','\xC1','\x80','\x80','\xE3','\xC1','\xFF','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xFF','\xFF','\xFF','\x00','\x00','\xFF','\xFF','\xFF',
+'\xFF','\xFF','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\xFF','\xFF','\xCF','\xCF','\xCF','\xCF','\xCF','\xCF','\xCF','\xCF',
+'\xF3','\xF3','\xF3','\xF3','\xF3','\xF3','\xF3','\xF3','\xFF','\xFF','\xFF','\x1F','\x0F','\xC7','\xE7','\xE7','\xE7','\xE7','\xE3','\xF0','\xF8','\xFF','\xFF','\xFF','\xE7','\xE7','\xC7','\x0F','\x1F','\xFF','\xFF','\xFF',
+'\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x00','\x00','\x3F','\x1F','\x8F','\xC7','\xE3','\xF1','\xF8','\xFC','\xFC','\xF8','\xF1','\xE3','\xC7','\x8F','\x1F','\x3F','\x00','\x00','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F',
+'\x00','\x00','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFF','\xC3','\x81','\x81','\x81','\x81','\xC3','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\xFF','\xC9','\x80','\x80','\x80','\xC1','\xE3','\xF7','\xFF',
+'\x9F','\x9F','\x9F','\x9F','\x9F','\x9F','\x9F','\x9F','\xFF','\xFF','\xFF','\xF8','\xF0','\xE3','\xE7','\xE7','\x3C','\x18','\x81','\xC3','\xC3','\x81','\x18','\x3C','\xFF','\xC3','\x81','\x99','\x99','\x81','\xC3','\xFF',
+'\xE7','\xE7','\x99','\x99','\xE7','\xE7','\xC3','\xFF','\xF9','\xF9','\xF9','\xF9','\xF9','\xF9','\xF9','\xF9','\xF7','\xE3','\xC1','\x80','\xC1','\xE3','\xF7','\xFF','\xE7','\xE7','\xE7','\x00','\x00','\xE7','\xE7','\xE7',
+'\x3F','\x3F','\xCF','\xCF','\x3F','\x3F','\xCF','\xCF','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xFF','\xFF','\xFC','\xC1','\x89','\xC9','\xC9','\xFF','\x00','\x80','\xC0','\xE0','\xF0','\xF8','\xFC','\xFE',
+'\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F','\x0F','\x0F','\x0F','\x0F','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF',
+'\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x33','\x33','\xCC','\xCC','\x33','\x33','\xCC','\xCC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC',
+'\xFF','\xFF','\xFF','\xFF','\x33','\x33','\xCC','\xCC','\x00','\x01','\x03','\x07','\x0F','\x1F','\x3F','\x7F','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xE7','\xE7','\xE7','\xE0','\xE0','\xE7','\xE7','\xE7',
+'\xFF','\xFF','\xFF','\xFF','\xF0','\xF0','\xF0','\xF0','\xE7','\xE7','\xE7','\xE0','\xE0','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x07','\x07','\xE7','\xE7','\xE7','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00',
+'\xFF','\xFF','\xFF','\xE0','\xE0','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\x07','\x07','\xE7','\xE7','\xE7',
+'\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x1F','\x1F','\x1F','\x1F','\x1F','\x1F','\x1F','\x1F','\xF8','\xF8','\xF8','\xF8','\xF8','\xF8','\xF8','\xF8','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF',
+'\x00','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\x00','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F',
+'\xF0','\xF0','\xF0','\xF0','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xE7','\x07','\x07','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F','\xFF','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F','\xF0','\xF0','\xF0','\xF0',
+'\x3C','\x66','\x6E','\x6E','\x60','\x62','\x3C','\x00','\x00','\x00','\x3C','\x06','\x3E','\x66','\x3E','\x00','\x00','\x60','\x60','\x7C','\x66','\x66','\x7C','\x00','\x00','\x00','\x3C','\x60','\x60','\x60','\x3C','\x00',
+'\x00','\x06','\x06','\x3E','\x66','\x66','\x3E','\x00','\x00','\x00','\x3C','\x66','\x7E','\x60','\x3C','\x00','\x00','\x0E','\x18','\x3E','\x18','\x18','\x18','\x00','\x00','\x00','\x3E','\x66','\x66','\x3E','\x06','\x7C',
+'\x00','\x60','\x60','\x7C','\x66','\x66','\x66','\x00','\x00','\x18','\x00','\x38','\x18','\x18','\x3C','\x00','\x00','\x06','\x00','\x06','\x06','\x06','\x06','\x3C','\x00','\x60','\x60','\x6C','\x78','\x6C','\x66','\x00',
+'\x00','\x38','\x18','\x18','\x18','\x18','\x3C','\x00','\x00','\x00','\x66','\x7F','\x7F','\x6B','\x63','\x00','\x00','\x00','\x7C','\x66','\x66','\x66','\x66','\x00','\x00','\x00','\x3C','\x66','\x66','\x66','\x3C','\x00',
+'\x00','\x00','\x7C','\x66','\x66','\x7C','\x60','\x60','\x00','\x00','\x3E','\x66','\x66','\x3E','\x06','\x06','\x00','\x00','\x7C','\x66','\x60','\x60','\x60','\x00','\x00','\x00','\x3E','\x60','\x3C','\x06','\x7C','\x00',
+'\x00','\x18','\x7E','\x18','\x18','\x18','\x0E','\x00','\x00','\x00','\x66','\x66','\x66','\x66','\x3E','\x00','\x00','\x00','\x66','\x66','\x66','\x3C','\x18','\x00','\x00','\x00','\x63','\x6B','\x7F','\x3E','\x36','\x00',
+'\x00','\x00','\x66','\x3C','\x18','\x3C','\x66','\x00','\x00','\x00','\x66','\x66','\x66','\x3E','\x0C','\x78','\x00','\x00','\x7E','\x0C','\x18','\x30','\x7E','\x00','\x3C','\x30','\x30','\x30','\x30','\x30','\x3C','\x00',
+'\x0C','\x12','\x30','\x7C','\x30','\x62','\xFC','\x00','\x3C','\x0C','\x0C','\x0C','\x0C','\x0C','\x3C','\x00','\x00','\x18','\x3C','\x7E','\x18','\x18','\x18','\x18','\x00','\x10','\x30','\x7F','\x7F','\x30','\x10','\x00',
+'\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x18','\x18','\x18','\x18','\x00','\x00','\x18','\x00','\x66','\x66','\x66','\x00','\x00','\x00','\x00','\x00','\x66','\x66','\xFF','\x66','\xFF','\x66','\x66','\x00',
+'\x18','\x3E','\x60','\x3C','\x06','\x7C','\x18','\x00','\x62','\x66','\x0C','\x18','\x30','\x66','\x46','\x00','\x3C','\x66','\x3C','\x38','\x67','\x66','\x3F','\x00','\x06','\x0C','\x18','\x00','\x00','\x00','\x00','\x00',
+'\x0C','\x18','\x30','\x30','\x30','\x18','\x0C','\x00','\x30','\x18','\x0C','\x0C','\x0C','\x18','\x30','\x00','\x00','\x66','\x3C','\xFF','\x3C','\x66','\x00','\x00','\x00','\x18','\x18','\x7E','\x18','\x18','\x00','\x00',
+'\x00','\x00','\x00','\x00','\x00','\x18','\x18','\x30','\x00','\x00','\x00','\x7E','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x18','\x18','\x00','\x00','\x03','\x06','\x0C','\x18','\x30','\x60','\x00',
+'\x3C','\x66','\x6E','\x76','\x66','\x66','\x3C','\x00','\x18','\x18','\x38','\x18','\x18','\x18','\x7E','\x00','\x3C','\x66','\x06','\x0C','\x30','\x60','\x7E','\x00','\x3C','\x66','\x06','\x1C','\x06','\x66','\x3C','\x00',
+'\x06','\x0E','\x1E','\x66','\x7F','\x06','\x06','\x00','\x7E','\x60','\x7C','\x06','\x06','\x66','\x3C','\x00','\x3C','\x66','\x60','\x7C','\x66','\x66','\x3C','\x00','\x7E','\x66','\x0C','\x18','\x18','\x18','\x18','\x00',
+'\x3C','\x66','\x66','\x3C','\x66','\x66','\x3C','\x00','\x3C','\x66','\x66','\x3E','\x06','\x66','\x3C','\x00','\x00','\x00','\x18','\x00','\x00','\x18','\x00','\x00','\x00','\x00','\x18','\x00','\x00','\x18','\x18','\x30',
+'\x0E','\x18','\x30','\x60','\x30','\x18','\x0E','\x00','\x00','\x00','\x7E','\x00','\x7E','\x00','\x00','\x00','\x70','\x18','\x0C','\x06','\x0C','\x18','\x70','\x00','\x3C','\x66','\x06','\x0C','\x18','\x00','\x18','\x00',
+'\x00','\x00','\x00','\xFF','\xFF','\x00','\x00','\x00','\x18','\x3C','\x66','\x7E','\x66','\x66','\x66','\x00','\x7C','\x66','\x66','\x7C','\x66','\x66','\x7C','\x00','\x3C','\x66','\x60','\x60','\x60','\x66','\x3C','\x00',
+'\x78','\x6C','\x66','\x66','\x66','\x6C','\x78','\x00','\x7E','\x60','\x60','\x78','\x60','\x60','\x7E','\x00','\x7E','\x60','\x60','\x78','\x60','\x60','\x60','\x00','\x3C','\x66','\x60','\x6E','\x66','\x66','\x3C','\x00',
+'\x66','\x66','\x66','\x7E','\x66','\x66','\x66','\x00','\x3C','\x18','\x18','\x18','\x18','\x18','\x3C','\x00','\x1E','\x0C','\x0C','\x0C','\x0C','\x6C','\x38','\x00','\x66','\x6C','\x78','\x70','\x78','\x6C','\x66','\x00',
+'\x60','\x60','\x60','\x60','\x60','\x60','\x7E','\x00','\x63','\x77','\x7F','\x6B','\x63','\x63','\x63','\x00','\x66','\x76','\x7E','\x7E','\x6E','\x66','\x66','\x00','\x3C','\x66','\x66','\x66','\x66','\x66','\x3C','\x00',
+'\x7C','\x66','\x66','\x7C','\x60','\x60','\x60','\x00','\x3C','\x66','\x66','\x66','\x66','\x3C','\x0E','\x00','\x7C','\x66','\x66','\x7C','\x78','\x6C','\x66','\x00','\x3C','\x66','\x60','\x3C','\x06','\x66','\x3C','\x00',
+'\x7E','\x18','\x18','\x18','\x18','\x18','\x18','\x00','\x66','\x66','\x66','\x66','\x66','\x66','\x3C','\x00','\x66','\x66','\x66','\x66','\x66','\x3C','\x18','\x00','\x63','\x63','\x63','\x6B','\x7F','\x77','\x63','\x00',
+'\x66','\x66','\x3C','\x18','\x3C','\x66','\x66','\x00','\x66','\x66','\x66','\x3C','\x18','\x18','\x18','\x00','\x7E','\x06','\x0C','\x18','\x30','\x60','\x7E','\x00','\x18','\x18','\x18','\xFF','\xFF','\x18','\x18','\x18',
+'\xC0','\xC0','\x30','\x30','\xC0','\xC0','\x30','\x30','\x18','\x18','\x18','\x18','\x18','\x18','\x18','\x18','\x33','\x33','\xCC','\xCC','\x33','\x33','\xCC','\xCC','\x33','\x99','\xCC','\x66','\x33','\x99','\xCC','\x66',
+'\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\xF0','\xF0','\xF0','\xF0','\xF0','\xF0','\xF0','\xF0','\x00','\x00','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00','\x00',
+'\x00','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xCC','\xCC','\x33','\x33','\xCC','\xCC','\x33','\x33','\x03','\x03','\x03','\x03','\x03','\x03','\x03','\x03',
+'\x00','\x00','\x00','\x00','\xCC','\xCC','\x33','\x33','\xCC','\x99','\x33','\x66','\xCC','\x99','\x33','\x66','\x03','\x03','\x03','\x03','\x03','\x03','\x03','\x03','\x18','\x18','\x18','\x1F','\x1F','\x18','\x18','\x18',
+'\x00','\x00','\x00','\x00','\x0F','\x0F','\x0F','\x0F','\x18','\x18','\x18','\x1F','\x1F','\x00','\x00','\x00','\x00','\x00','\x00','\xF8','\xF8','\x18','\x18','\x18','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF',
+'\x00','\x00','\x00','\x1F','\x1F','\x18','\x18','\x18','\x18','\x18','\x18','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\x18','\x18','\x18','\x18','\x18','\x18','\xF8','\xF8','\x18','\x18','\x18',
+'\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xC0','\xE0','\xE0','\xE0','\xE0','\xE0','\xE0','\xE0','\xE0','\x07','\x07','\x07','\x07','\x07','\x07','\x07','\x07','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00',
+'\xFF','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\xFF','\x01','\x03','\x06','\x6C','\x78','\x70','\x60','\x00','\x00','\x00','\x00','\x00','\xF0','\xF0','\xF0','\xF0',
+'\x0F','\x0F','\x0F','\x0F','\x00','\x00','\x00','\x00','\x18','\x18','\x18','\xF8','\xF8','\x00','\x00','\x00','\xF0','\xF0','\xF0','\xF0','\x00','\x00','\x00','\x00','\xF0','\xF0','\xF0','\xF0','\x0F','\x0F','\x0F','\x0F',
+'\xC3','\x99','\x91','\x91','\x9F','\x99','\xC3','\xFF','\xFF','\xFF','\xC3','\xF9','\xC1','\x99','\xC1','\xFF','\xFF','\x9F','\x9F','\x83','\x99','\x99','\x83','\xFF','\xFF','\xFF','\xC3','\x9F','\x9F','\x9F','\xC3','\xFF',
+'\xFF','\xF9','\xF9','\xC1','\x99','\x99','\xC1','\xFF','\xFF','\xFF','\xC3','\x99','\x81','\x9F','\xC3','\xFF','\xFF','\xF1','\xE7','\xC1','\xE7','\xE7','\xE7','\xFF','\xFF','\xFF','\xC1','\x99','\x99','\xC1','\xF9','\x83',
+'\xFF','\x9F','\x9F','\x83','\x99','\x99','\x99','\xFF','\xFF','\xE7','\xFF','\xC7','\xE7','\xE7','\xC3','\xFF','\xFF','\xF9','\xFF','\xF9','\xF9','\xF9','\xF9','\xC3','\xFF','\x9F','\x9F','\x93','\x87','\x93','\x99','\xFF',
+'\xFF','\xC7','\xE7','\xE7','\xE7','\xE7','\xC3','\xFF','\xFF','\xFF','\x99','\x80','\x80','\x94','\x9C','\xFF','\xFF','\xFF','\x83','\x99','\x99','\x99','\x99','\xFF','\xFF','\xFF','\xC3','\x99','\x99','\x99','\xC3','\xFF',
+'\xFF','\xFF','\x83','\x99','\x99','\x83','\x9F','\x9F','\xFF','\xFF','\xC1','\x99','\x99','\xC1','\xF9','\xF9','\xFF','\xFF','\x83','\x99','\x9F','\x9F','\x9F','\xFF','\xFF','\xFF','\xC1','\x9F','\xC3','\xF9','\x83','\xFF',
+'\xFF','\xE7','\x81','\xE7','\xE7','\xE7','\xF1','\xFF','\xFF','\xFF','\x99','\x99','\x99','\x99','\xC1','\xFF','\xFF','\xFF','\x99','\x99','\x99','\xC3','\xE7','\xFF','\xFF','\xFF','\x9C','\x94','\x80','\xC1','\xC9','\xFF',
+'\xFF','\xFF','\x99','\xC3','\xE7','\xC3','\x99','\xFF','\xFF','\xFF','\x99','\x99','\x99','\xC1','\xF3','\x87','\xFF','\xFF','\x81','\xF3','\xE7','\xCF','\x81','\xFF','\xC3','\xCF','\xCF','\xCF','\xCF','\xCF','\xC3','\xFF',
+'\xF3','\xED','\xCF','\x83','\xCF','\x9D','\x03','\xFF','\xC3','\xF3','\xF3','\xF3','\xF3','\xF3','\xC3','\xFF','\xFF','\xE7','\xC3','\x81','\xE7','\xE7','\xE7','\xE7','\xFF','\xEF','\xCF','\x80','\x80','\xCF','\xEF','\xFF',
+'\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xE7','\xE7','\xFF','\xFF','\xE7','\xFF','\x99','\x99','\x99','\xFF','\xFF','\xFF','\xFF','\xFF','\x99','\x99','\x00','\x99','\x00','\x99','\x99','\xFF',
+'\xE7','\xC1','\x9F','\xC3','\xF9','\x83','\xE7','\xFF','\x9D','\x99','\xF3','\xE7','\xCF','\x99','\xB9','\xFF','\xC3','\x99','\xC3','\xC7','\x98','\x99','\xC0','\xFF','\xF9','\xF3','\xE7','\xFF','\xFF','\xFF','\xFF','\xFF',
+'\xF3','\xE7','\xCF','\xCF','\xCF','\xE7','\xF3','\xFF','\xCF','\xE7','\xF3','\xF3','\xF3','\xE7','\xCF','\xFF','\xFF','\x99','\xC3','\x00','\xC3','\x99','\xFF','\xFF','\xFF','\xE7','\xE7','\x81','\xE7','\xE7','\xFF','\xFF',
+'\xFF','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xCF','\xFF','\xFF','\xFF','\x81','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xFF','\xFF','\xFC','\xF9','\xF3','\xE7','\xCF','\x9F','\xFF',
+'\xC3','\x99','\x91','\x89','\x99','\x99','\xC3','\xFF','\xE7','\xE7','\xC7','\xE7','\xE7','\xE7','\x81','\xFF','\xC3','\x99','\xF9','\xF3','\xCF','\x9F','\x81','\xFF','\xC3','\x99','\xF9','\xE3','\xF9','\x99','\xC3','\xFF',
+'\xF9','\xF1','\xE1','\x99','\x80','\xF9','\xF9','\xFF','\x81','\x9F','\x83','\xF9','\xF9','\x99','\xC3','\xFF','\xC3','\x99','\x9F','\x83','\x99','\x99','\xC3','\xFF','\x81','\x99','\xF3','\xE7','\xE7','\xE7','\xE7','\xFF',
+'\xC3','\x99','\x99','\xC3','\x99','\x99','\xC3','\xFF','\xC3','\x99','\x99','\xC1','\xF9','\x99','\xC3','\xFF','\xFF','\xFF','\xE7','\xFF','\xFF','\xE7','\xFF','\xFF','\xFF','\xFF','\xE7','\xFF','\xFF','\xE7','\xE7','\xCF',
+'\xF1','\xE7','\xCF','\x9F','\xCF','\xE7','\xF1','\xFF','\xFF','\xFF','\x81','\xFF','\x81','\xFF','\xFF','\xFF','\x8F','\xE7','\xF3','\xF9','\xF3','\xE7','\x8F','\xFF','\xC3','\x99','\xF9','\xF3','\xE7','\xFF','\xE7','\xFF',
+'\xFF','\xFF','\xFF','\x00','\x00','\xFF','\xFF','\xFF','\xE7','\xC3','\x99','\x81','\x99','\x99','\x99','\xFF','\x83','\x99','\x99','\x83','\x99','\x99','\x83','\xFF','\xC3','\x99','\x9F','\x9F','\x9F','\x99','\xC3','\xFF',
+'\x87','\x93','\x99','\x99','\x99','\x93','\x87','\xFF','\x81','\x9F','\x9F','\x87','\x9F','\x9F','\x81','\xFF','\x81','\x9F','\x9F','\x87','\x9F','\x9F','\x9F','\xFF','\xC3','\x99','\x9F','\x91','\x99','\x99','\xC3','\xFF',
+'\x99','\x99','\x99','\x81','\x99','\x99','\x99','\xFF','\xC3','\xE7','\xE7','\xE7','\xE7','\xE7','\xC3','\xFF','\xE1','\xF3','\xF3','\xF3','\xF3','\x93','\xC7','\xFF','\x99','\x93','\x87','\x8F','\x87','\x93','\x99','\xFF',
+'\x9F','\x9F','\x9F','\x9F','\x9F','\x9F','\x81','\xFF','\x9C','\x88','\x80','\x94','\x9C','\x9C','\x9C','\xFF','\x99','\x89','\x81','\x81','\x91','\x99','\x99','\xFF','\xC3','\x99','\x99','\x99','\x99','\x99','\xC3','\xFF',
+'\x83','\x99','\x99','\x83','\x9F','\x9F','\x9F','\xFF','\xC3','\x99','\x99','\x99','\x99','\xC3','\xF1','\xFF','\x83','\x99','\x99','\x83','\x87','\x93','\x99','\xFF','\xC3','\x99','\x9F','\xC3','\xF9','\x99','\xC3','\xFF',
+'\x81','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xFF','\x99','\x99','\x99','\x99','\x99','\x99','\xC3','\xFF','\x99','\x99','\x99','\x99','\x99','\xC3','\xE7','\xFF','\x9C','\x9C','\x9C','\x94','\x80','\x88','\x9C','\xFF',
+'\x99','\x99','\xC3','\xE7','\xC3','\x99','\x99','\xFF','\x99','\x99','\x99','\xC3','\xE7','\xE7','\xE7','\xFF','\x81','\xF9','\xF3','\xE7','\xCF','\x9F','\x81','\xFF','\xE7','\xE7','\xE7','\x00','\x00','\xE7','\xE7','\xE7',
+'\x3F','\x3F','\xCF','\xCF','\x3F','\x3F','\xCF','\xCF','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\xCC','\xCC','\x33','\x33','\xCC','\xCC','\x33','\x33','\xCC','\x66','\x33','\x99','\xCC','\x66','\x33','\x99',
+'\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F','\x0F','\x0F','\x0F','\x0F','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\x00','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF',
+'\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x33','\x33','\xCC','\xCC','\x33','\x33','\xCC','\xCC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC',
+'\xFF','\xFF','\xFF','\xFF','\x33','\x33','\xCC','\xCC','\x33','\x66','\xCC','\x99','\x33','\x66','\xCC','\x99','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xFC','\xE7','\xE7','\xE7','\xE0','\xE0','\xE7','\xE7','\xE7',
+'\xFF','\xFF','\xFF','\xFF','\xF0','\xF0','\xF0','\xF0','\xE7','\xE7','\xE7','\xE0','\xE0','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x07','\x07','\xE7','\xE7','\xE7','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00',
+'\xFF','\xFF','\xFF','\xE0','\xE0','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\xE7','\xE7','\xE7','\xE7','\xE7','\xE7','\x07','\x07','\xE7','\xE7','\xE7',
+'\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x3F','\x1F','\x1F','\x1F','\x1F','\x1F','\x1F','\x1F','\x1F','\xF8','\xF8','\xF8','\xF8','\xF8','\xF8','\xF8','\xF8','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF',
+'\x00','\x00','\x00','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x00','\x00','\x00','\xFE','\xFC','\xF9','\x93','\x87','\x8F','\x9F','\xFF','\xFF','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F',
+'\xF0','\xF0','\xF0','\xF0','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xE7','\x07','\x07','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F','\xFF','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F','\xF0','\xF0','\xF0','\xF0'
+};
+
+static byte ram[64 * 1024];
 static byte color_nybles[1024];
 
 // note ram starts at 0x0000
@@ -756,6 +1008,8 @@
 static const int open_addr = 0xC000;
 static const int open_size = 0x1000;
 
+static byte io[io_size];
+
 void C64_Init(const char* basic_file, const char* kernal_file)
 {
 	//File_ReadAllBytes(basic_rom, sizeof(basic_rom), basic_file);
@@ -765,10 +1019,73 @@
 		ram[i] = 0;
 	for (int i = 0; i < sizeof(color_nybles); ++i)
 		color_nybles[i] = 0;
+	for (int i = 0; i < sizeof(io); ++i)
+		io[i] = 0;
+}
 
-	//io = new byte[io_size];
-	//for (int i = 0; i < io.Length; ++i)
-	//	io[i] = 0;
+int C64ColorToLCDColor(byte value)
+{
+	switch (value & 0xF)
+	{
+		case 0: return LCD_COLOR_BLACK;
+		case 1: return LCD_COLOR_WHITE;
+		case 2: return LCD_COLOR_RED;
+		case 3: return LCD_COLOR_CYAN;
+		case 4: return LCD_COLOR_DARKMAGENTA; // PURPLE
+		case 5: return LCD_COLOR_GREEN;
+		case 6: return LCD_COLOR_BLUE;
+		case 7: return LCD_COLOR_YELLOW;
+		case 8: return LCD_COLOR_ORANGE;
+		case 9: return LCD_COLOR_BROWN;
+		case 10: return LCD_COLOR_LIGHTRED;
+		case 11: return LCD_COLOR_DARKGRAY;
+		case 12: return LCD_COLOR_DARKCYAN; // MED GRAY
+		case 13: return LCD_COLOR_LIGHTGREEN;
+		case 14: return LCD_COLOR_LIGHTBLUE;
+		case 15: return LCD_COLOR_LIGHTGRAY;
+		default: return 0;
+	}
+}
+
+void DrawChar(byte c, int col, int row, int fg, int bg)
+{
+	int offset = ((io[0x18] & 2) == 0) ? 0 : (8*256);
+	const byte* shape = &chargen_rom[c*8+offset];
+	int x0 = 20 + row*8;
+	int y0 = col*8;
+	for (int row_i=0; row_i<8; ++row_i)
+	{
+		int mask = 128;
+		for (int col_i=0; col_i<8; ++col_i)
+		{
+			lcd.DrawPixel(x0+row_i, 320 - (y0+col_i), ((shape[row_i] & mask) == 0) ? bg : fg);
+			mask = mask >> 1;
+		}
+	}
+}
+
+static void DrawChar(int offset)
+{
+	int col = offset % 40;
+	int row = offset / 40;
+	int fg = C64ColorToLCDColor(color_nybles[offset]);
+	int bg = C64ColorToLCDColor(io[0x21]);
+	DrawChar(ram[1024+offset], col, row, fg, bg);
+}
+
+static void RedrawScreen()
+{
+	int bg = C64ColorToLCDColor(io[0x21]);
+	int offset = 0;
+	for (int row = 0; row < 25; ++row)
+	{
+		for (int col = 0; col < 40; ++col)
+		{
+			int fg = C64ColorToLCDColor(color_nybles[offset]);
+			DrawChar(ram[1024 + offset], col, row, fg, bg);
+			++offset;
+		}
+	}
 }
 
 byte GetMemory(ushort addr)
@@ -779,9 +1096,27 @@
 		return basic_rom[addr - basic_addr];
 	else if (addr >= color_addr && addr < color_addr + sizeof(color_nybles))
 		return color_nybles[addr - color_addr];
+	else if (addr == 0xDC01)
+	{
+		int value = 0;
+		
+		for (int i=0; i<9; ++i)
+		{
+			if (scan_codes[i] < 64)
+			{
+				int col = scan_codes[i] / 8;
+				int row = scan_codes[i] % 8;
+				
+				if ((io[0xC00] & (1 << col)) == 0)
+					value |= (1 << row);
+			}
+		}
+		
+		return ~value;
+	}
 	else if (addr >= io_addr && addr < io_addr + io_size)
-		return 0; // io[addr - io_addr];
-	else if (addr >= kernal_addr && addr < kernal_addr + sizeof(kernal_rom))
+		return io[addr - io_addr];
+	else if (addr >= kernal_addr && addr <= kernal_addr + sizeof(kernal_rom)-1)
 		return kernal_rom[addr - kernal_addr];
 	else
 		return 0xFF;
@@ -789,13 +1124,41 @@
 
 void SetMemory(ushort addr, byte value)
 {
-	if (addr < sizeof(ram) && (addr < io_addr || (addr >= kernal_addr && addr < kernal_addr + sizeof(kernal_rom))))
+	if (addr <= sizeof(ram)-1 && (addr < io_addr || (addr >= kernal_addr && addr <= kernal_addr + sizeof(kernal_rom)-1)))
+	{
 		ram[addr] = value;
+		if (addr >= 1024 && addr < 2024)
+			DrawChar(addr - 1024);
+	}
+	else if (addr == 0xD018) // VIC-II Chip Memory Control Register
+	{
+		io[addr - io_addr] = value;
+		RedrawScreen(); // upper to lower or lower to upper
+	}
+	else if (addr == 0xD020) // border
+	{
+		lcd.SetTextColor(C64ColorToLCDColor(value));
+		lcd.FillRect(0, 0, 20, 320);
+		lcd.FillRect(220, 0, 20, 320);
+		io[addr - io_addr] = value & 0xF;
+	}	
 	else if (addr == 0xD021) // background
-		;
+	{
+		lcd.SetBackColor(C64ColorToLCDColor(value));
+		lcd.SetTextColor(C64ColorToLCDColor(value));
+		io[addr - io_addr] = value & 0xF;
+		RedrawScreen();
+	}
 	else if (addr >= color_addr && addr < color_addr + sizeof(color_nybles))
-		color_nybles[addr - color_addr] = value;
+	{
+		int offset = addr - color_addr;
+		color_nybles[offset] = value;
+		DrawChar(offset);
+	}
+	else if (addr == 0xDC00)
+	{
+		io[addr - io_addr] = value;
+	}	
 	//else if (addr >= io_addr && addr < io_addr + io.Length)
 	//    io[addr - io_addr] = value;
 }
-