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
Diff: emuc64.cpp
- 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; } -