diff --git a/src/Makefile b/src/Makefile index 32c2c69..d541f92 100644 --- a/src/Makefile +++ b/src/Makefile @@ -55,7 +55,7 @@ TARGET = $(OBJDIR)/sd2snes # List C source files here. (C dependencies are automatically generated.) -SRC = main.c ff.c clock.c uart.c +SRC = main.c ff.c ccsbcs.c clock.c uart.c power.c led.c timer.c printf.c sdcard.c spi.c fileops.c rtc.c # List Assembler source files here. @@ -65,7 +65,7 @@ SRC = main.c ff.c clock.c uart.c # Even though the DOS/Win* filesystem matches both .s and .S the same, # it will preserve the spelling of the filenames, and gcc itself does # care about how the name is spelled on its command-line. -ASRC = startup.S +ASRC = startup.S crc.S # Optimization level, can be [0, 1, 2, 3, s]. @@ -137,7 +137,7 @@ CFLAGS += -O$(OPT) CFLAGS += $(CPUFLAGS) -nostartfiles #CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums CFLAGS += -Wall -Wstrict-prototypes -Werror -#CFLAGS += -Wa,-adhlns=$(OBJDIR)/$(<:.c=.lst) +CFLAGS += -Wa,-adhlns=$(OBJDIR)/$(<:.c=.lst) CFLAGS += -I$(OBJDIR) CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS)) CFLAGS += $(CSTANDARD) diff --git a/src/bits.h b/src/bits.h new file mode 100644 index 0000000..37e3b80 --- /dev/null +++ b/src/bits.h @@ -0,0 +1,14 @@ +#ifndef _ARM_BITS_H +#define _ARM_BITS_H + +/* The classic macro */ +#define BV(x) (1<<(x)) + +/* CM3 bit-band access macro - no error checks! */ +#define BITBAND(addr,bit) \ + (*((volatile unsigned long *)( \ + ((unsigned long)&(addr) & 0x01ffffff)*32 + \ + (bit)*4 + 0x02000000 + ((unsigned long)&(addr) & 0xfe000000) \ + ))) + +#endif diff --git a/src/ccsbcs.c b/src/ccsbcs.c new file mode 100644 index 0000000..eb0ab4e --- /dev/null +++ b/src/ccsbcs.c @@ -0,0 +1,540 @@ +/*------------------------------------------------------------------------*/ +/* Unicode - Local code bidirectional converter (C)ChaN, 2009 */ +/* (SBCS code pages) */ +/*------------------------------------------------------------------------*/ +/* 437 U.S. (OEM) +/ 720 Arabic (OEM) +/ 1256 Arabic (Windows) +/ 737 Greek (OEM) +/ 1253 Greek (Windows) +/ 1250 Central Europe (Windows) +/ 775 Baltic (OEM) +/ 1257 Baltic (Windows) +/ 850 Multilingual Latin 1 (OEM) +/ 852 Latin 2 (OEM) +/ 1252 Latin 1 (Windows) +/ 855 Cyrillic (OEM) +/ 1251 Cyrillic (Windows) +/ 866 Russian (OEM) +/ 857 Turkish (OEM) +/ 1254 Turkish (Windows) +/ 858 Multilingual Latin 1 + Euro (OEM) +/ 862 Hebrew (OEM) +/ 1255 Hebrew (Windows) +/ 874 Thai (OEM, Windows) +/ 1258 Vietnam (OEM, Windows) +*/ + +#include "../ff.h" + + +#if _CODE_PAGE == 437 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 720 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */ + 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, + 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, + 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, + 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0xO650, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 737 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, + 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, + 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, + 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, + 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, + 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, + 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 775 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */ + 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, + 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, + 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, + 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, + 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, + 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, + 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, + 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, + 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, + 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, + 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 850 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 852 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, + 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, + 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, + 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, + 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, + 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, + 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, + 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, + 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 855 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */ + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, + 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, + 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, + 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, + 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, + 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, + 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, + 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, + 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, + 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, + 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, + 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 857 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, + 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 858 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP858(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 862 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */ + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 866 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 874 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP874(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, + 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, + 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, + 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, + 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, + 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, + 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, + 0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F, + 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, + 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, + 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, + 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +#elif _CODE_PAGE == 1250 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1250(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, + 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B, + 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, + 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, + 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 +}; + +#elif _CODE_PAGE == 1251 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1251(0x80-0xFF) to Unicode conversion table */ + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, + 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, + 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, + 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F +}; + +#elif _CODE_PAGE == 1252 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1252(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF +}; + +#elif _CODE_PAGE == 1253 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1253(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, + 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000 +}; + +#elif _CODE_PAGE == 1254 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1254(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF +}; + +#elif _CODE_PAGE == 1255 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1255(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, + 0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000 +}; + +#elif _CODE_PAGE == 1256 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1256(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, + 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA, + 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F, + 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, + 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643, + 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF, + 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, + 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2 +} + +#elif _CODE_PAGE == 1257 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1257(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000, + 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7, + 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, + 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, + 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, + 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, + 0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9 +}; + +#elif _CODE_PAGE == 1258 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1258(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF, + 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF, + 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF +}; + +#endif + + +#if !_TBLDEF || !_USE_LFN +#error This file is not needed in current configuration. Remove from the project. +#endif + + +WCHAR ff_convert ( /* Converted character, Returns zero on error */ + WCHAR src, /* Character code to be converted */ + UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ +) +{ + WCHAR c; + + + if (src < 0x80) { /* ASCII */ + c = src; + + } else { + if (dir) { /* OEMCP to Unicode */ + c = (src >= 0x100) ? 0 : Tbl[src - 0x80]; + + } else { /* Unicode to OEMCP */ + for (c = 0; c < 0x80; c++) { + if (src == Tbl[c]) break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + + +WCHAR ff_wtoupper ( /* Upper converted character */ + WCHAR chr /* Input character */ +) +{ + static const WCHAR tbl_lower[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0x00A2, 0x00A3, 0x00A5, 0x00AC, 0x00AF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x0FF, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177, 0x17A, 0x17C, 0x17E, 0x192, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x45E, 0x45F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0 }; + static const WCHAR tbl_upper[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x21, 0xFFE0, 0xFFE1, 0xFFE5, 0xFFE2, 0xFFE3, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C, 0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A, 0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145, 0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17B, 0x17D, 0x191, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x40E, 0x40F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0 }; + int i; + + + for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ; + + return tbl_lower[i] ? tbl_upper[i] : chr; +} diff --git a/src/clock.c b/src/clock.c index cb0d342..af51dc3 100644 --- a/src/clock.c +++ b/src/clock.c @@ -4,12 +4,18 @@ #include #include "clock.h" +#include "bits.h" -uint32_t f_cpu; +uint32_t f_cpu=4000000; uint16_t pll_mult = 1; uint8_t pll_prediv = 1; uint8_t cclk_div = 1; +void clock_disconnect() { + disconnectPLL0(); + disablePLL0(); +} + void clock_init() { /* set flash access time to 5 clks (80 FPGA freq = 11289473.7Hz First, disable and disconnect PLL0. */ - disconnectPLL0(); - disablePLL0(); + clock_disconnect(); /* PLL is disabled and disconnected. setup PCLK NOW as it cannot be changed reliably with PLL0 connected. see: http://ics.nxp.com/support/documents/microcontrollers/pdf/errata.lpc1754.pdf */ - LPC_SC->PCLKSEL1 = ( PCLK_CCLK(PCLK_TIMER3) - | PCLK_CCLK8(PCLK_UART3) ); + /* continue with PLL0 setup: enable the xtal oscillator and wait for it to become stable diff --git a/src/clock.h b/src/clock.h index 5ec8fac..56be21a 100644 --- a/src/clock.h +++ b/src/clock.h @@ -49,6 +49,8 @@ #define PCLK_SYSCON (28) #define PCLK_MC (30) +void clock_disconnect(void); + void clock_init(void); void setFlashAccessTime(uint8_t clocks); diff --git a/src/config.h b/src/config.h index 37befdc..1486589 100644 --- a/src/config.h +++ b/src/config.h @@ -1,10 +1,18 @@ #ifndef _CONFIG_H #define _CONFIG_H +#define SDCARD_DETECT (1) +#define SDCARD_WP (0) +#define SD_SUPPLY_VOLTAGE (1L<<21) /* 3.3V - 3.4V */ +#define CONFIG_SD_BLOCKTRANSFER 1 +#define CONFIG_SD_AUTO_RETRIES 10 +#define CONFIG_SD_DATACRC 1 + + #define CONFIG_UART_NUM 3 #define CONFIG_CPU_FREQUENCY 90315789 #define CONFIG_UART_PCLKDIV 1 -#define CONFIG_UART_TX_BUF_SHIFT 7 -#define CONFIG_UART_BAUDRATE 115200 - +#define CONFIG_UART_TX_BUF_SHIFT 5 +#define CONFIG_UART_BAUDRATE 921600 +#define CONFIG_UART_DEADLOCKABLE #endif diff --git a/src/crc.S b/src/crc.S new file mode 100644 index 0000000..e662f02 --- /dev/null +++ b/src/crc.S @@ -0,0 +1,91 @@ +/* CRC-7/CRC-16 (XModem) implementation for Cortex M3 + * + * Written 2010 by Ingo Korb + */ + + .syntax unified + .section .text + + /* uint8_t crc7update(uint8_t crc, const uint8_t data) */ + .global crc7update + .thumb_func +crc7update: + mov r2, #8 // number of bits to process + lsl r1, r1, #24 // pre-shift data byte to top of word + +loop: + lsl r0, r0, #1 // shift CRC + lsls r1, r1, #1 // shift data byte (highest bit now in C) + bcc 0f // jump if bit was 0 + eor r0, r0, #0x80 // invert top bit of CRC if not +0: tst r0, #0x80 // test top bit of CRC + beq 1f // skip if top bit is clear + eor r0, r0, #0x09 // apply polinomial +1: subs r2, r2, #1 // decrememt bit cointer + bne loop // loop for next bit + uxtb r0, r0 // clear top bits of result + bx lr // return + + + /* uint16_t crc_xmodem_block(uint16_t crc, uint8_t *data, uint32_t len) */ + .global crc_xmodem_block + .thumb_func +crc_xmodem_block: + adr r12, crc_table // load address of crc table +blockloop: + ldrb.w r3, [r1], #1 // read data byte + eor r3, r3, r0, lsr #8 // EOR data byte + ldrh r3, [r12, r3, lsl #1] // load value from CRC table + eor r0, r3, r0, lsl #8 // update CRC + uxth r0, r0 // clear top bits of result + subs r2, r2, #1 // decrement length + bne blockloop // loop while length > 0 + bx lr // return + + /* uint16_t crc_xmodem_block(uint16_t crc, uint8_t *data, uint32_t len) */ + .global crc_xmodem_update + .thumb_func +crc_xmodem_update: + adr r2, crc_table // load address of crc table + eor r1, r1, r0, lsr #8 // EOR data byte + ldrh r3, [r2, r1, lsl #1] // load value from CRC table + eor r0, r3, r0, lsl #8 // update CRC + uxth r0, r0 // clear top bits of result + bx lr // return + + +crc_table: + .short 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7 + .short 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef + .short 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6 + .short 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de + .short 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485 + .short 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d + .short 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4 + .short 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc + .short 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823 + .short 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b + .short 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12 + .short 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a + .short 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41 + .short 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49 + .short 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70 + .short 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78 + .short 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f + .short 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067 + .short 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e + .short 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256 + .short 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d + .short 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405 + .short 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c + .short 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634 + .short 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab + .short 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3 + .short 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a + .short 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92 + .short 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9 + .short 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1 + .short 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8 + .short 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + + .end diff --git a/src/crc.h b/src/crc.h new file mode 100644 index 0000000..34e0053 --- /dev/null +++ b/src/crc.h @@ -0,0 +1,11 @@ +#ifndef CRC_H +#define CRC_H + +uint8_t crc7update(uint8_t crc, uint8_t data); +uint16_t crc_xmodem_update(uint16_t crc, uint8_t data); +uint16_t crc_xmodem_block(uint16_t crc, const uint8_t *data, uint32_t length); + +// AVR-libc compatibility +#define _crc_xmodem_update(crc,data) crc_xmodem_update(crc,data) + +#endif diff --git a/src/diskio.h b/src/diskio.h index 6c83476..87a09f9 100644 --- a/src/diskio.h +++ b/src/diskio.h @@ -7,6 +7,8 @@ #define _READONLY 0 /* 1: Remove write functions */ #define _USE_IOCTL 1 /* 1: Use disk_ioctl fucntion */ +#include + #include "integer.h" @@ -22,6 +24,26 @@ typedef enum { RES_PARERR /* 4: Invalid Parameter */ } DRESULT; +/** + * struct diskinfo0_t - disk info data structure for page 0 + * @validbytes : Number of valid bytes in this struct + * @maxpage : Highest diskinfo page supported + * @disktype : type of the disk (DISK_TYPE_* values) + * @sectorsize : sector size divided by 256 + * @sectorcount: number of sectors on the disk + * + * This is the struct returned in the data buffer when disk_getinfo + * is called with page=0. + */ +typedef struct { + uint8_t validbytes; + uint8_t maxpage; + uint8_t disktype; + uint8_t sectorsize; /* divided by 256 */ + uint32_t sectorcount; /* 2 TB should be enough... (512 byte sectors) */ +} diskinfo0_t; + + /*---------------------------------------*/ /* Prototypes for disk control functions */ @@ -35,6 +57,20 @@ DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); #endif DRESULT disk_ioctl (BYTE, BYTE, void*); +void disk_init(void); + +/* Will be set to DISK_ERROR if any access on the card fails */ +enum diskstates { DISK_CHANGED = 0, DISK_REMOVED, DISK_OK, DISK_ERROR }; + +extern volatile enum diskstates disk_state; + +/* Disk type - part of the external API except for ATA2! */ +#define DISK_TYPE_ATA 0 +#define DISK_TYPE_ATA2 1 +#define DISK_TYPE_SD 2 +#define DISK_TYPE_DF 3 +#define DISK_TYPE_NONE 7 + /* Disk Status Bits (DSTATUS) */ diff --git a/src/ff.c b/src/ff.c index e1b04b7..a7e2c10 100644 --- a/src/ff.c +++ b/src/ff.c @@ -87,7 +87,7 @@ #include "ff.h" /* FatFs configurations and declarations */ #include "diskio.h" /* Declarations of low level disk I/O functions */ - +#include "uart.h" /*-------------------------------------------------------------------------- @@ -319,7 +319,7 @@ void mem_cpy (void* dst, const void* src, UINT cnt) { #if _WORD_ACCESS == 1 while (cnt >= sizeof(int)) { - *(int*)d = *(int*)s; + *((int*)d) = *((int*)s); d += sizeof(int); s += sizeof(int); cnt -= sizeof(int); } @@ -1815,6 +1815,8 @@ FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occurred */ fs->fs_type = 0; /* Clear the file system object */ fs->drv = (BYTE)LD2PD(vol); /* Bind the logical drive and a physical drive */ stat = disk_initialize(fs->drv); /* Initialize low level disk I/O layer */ +printf("disk_initialize status: %d\n", stat); + if (stat & STA_NOINIT) /* Check if the initialization succeeded */ return FR_NOT_READY; /* Failed to initialize due to no media or hard error */ #if _MAX_SS != 512 /* Get disk sector size (variable sector size cfg only) */ diff --git a/src/ffconf.h b/src/ffconf.h index 3358675..67b3fe7 100644 --- a/src/ffconf.h +++ b/src/ffconf.h @@ -48,7 +48,7 @@ /* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ -#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */ +#define _USE_FASTSEEK 1 /* 0:Disable or 1:Enable */ /* To enable fast seek feature, set _USE_FASTSEEK to 1. */ @@ -57,7 +57,7 @@ / Locale and Namespace Configurations /----------------------------------------------------------------------------*/ -#define _CODE_PAGE 932 +#define _CODE_PAGE 1252 /* The _CODE_PAGE specifies the OEM code page to be used on the target system. / Incorrect setting of the code page can cause a file open failure. / @@ -110,7 +110,7 @@ / enable LFN feature and set _LFN_UNICODE to 1. */ -#define _FS_RPATH 2 /* 0 to 2 */ +#define _FS_RPATH 0 /* 0 to 2 */ /* The _FS_RPATH option configures relative path feature. / / 0: Disable relative path feature and remove related functions. @@ -125,7 +125,7 @@ / Physical Drive Configurations /----------------------------------------------------------------------------*/ -#define _VOLUMES 2 +#define _VOLUMES 1 /* Number of volumes (logical drives) to be used. */ diff --git a/src/fileops.c b/src/fileops.c new file mode 100644 index 0000000..a9a367b --- /dev/null +++ b/src/fileops.c @@ -0,0 +1,80 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR firmware portion + + Inspired by and based on code from sd2iec, written by Ingo Korb et al. + See sdcard.c|h, config.h. + + FAT file system access based on code by ChaN, Jim Brain, Ingo Korb, + see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + fileops.c: simple file access functions +*/ + +#include "config.h" +#include "uart.h" +#include "ff.h" +#include "fileops.h" + +/*WCHAR ff_convert(WCHAR w, UINT dir) { + return w; +}*/ + +void file_init() { + file_res=f_mount(0, &fatfs); +} + +/*void file_open_by_filinfo(FILINFO* fno) { + file_res = l_openfilebycluster(&fatfs, &file_handle, (UCHAR*)"", fno->clust, fno->fsize); +}*/ + +void file_open(uint8_t* filename, BYTE flags) { + file_res = f_open(&file_handle, (TCHAR*)filename, flags); +} + +void file_close() { + file_res = f_close(&file_handle); +} + +UINT file_read() { + UINT bytes_read; + file_res = f_read(&file_handle, file_buf, sizeof(file_buf), &bytes_read); + return bytes_read; +} + +UINT file_write() { + UINT bytes_written; + file_res = f_write(&file_handle, file_buf, sizeof(file_buf), &bytes_written); + return bytes_written; +} + +UINT file_readblock(void* buf, uint32_t addr, uint16_t size) { + UINT bytes_read; + file_res = f_lseek(&file_handle, addr); + if(file_handle.fptr != addr) { + return 0; + } + file_res = f_read(&file_handle, buf, size, &bytes_read); + return bytes_read; +} + +UINT file_writeblock(void* buf, uint32_t addr, uint16_t size) { + UINT bytes_written; + file_res = f_lseek(&file_handle, addr); + if(file_res) return 0; + file_res = f_write(&file_handle, buf, size, &bytes_written); + return bytes_written; +} diff --git a/src/fileops.h b/src/fileops.h new file mode 100644 index 0000000..b2e8a21 --- /dev/null +++ b/src/fileops.h @@ -0,0 +1,45 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR firmware portion + + Inspired by and based on code from sd2iec, written by Ingo Korb et al. + See sdcard.c|h, config.h. + + FAT file system access based on code by ChaN, Jim Brain, Ingo Korb, + see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + fileops.h: simple file access functions +*/ + +#ifndef FILEOPS_H +#define FILEOPS_H +#include "ff.h" + +BYTE file_buf[4096]; +FATFS fatfs; +FIL file_handle; +FRESULT file_res; +uint8_t file_lfn[256]; + +void file_init(void); +void file_open(uint8_t* filename, BYTE flags); +void file_open_by_filinfo(FILINFO* fno); +void file_close(void); +UINT file_read(void); +UINT file_write(void); +UINT file_readblock(void* buf, uint32_t addr, uint16_t size); +UINT file_writeblock(void* buf, uint32_t addr, uint16_t size); +#endif diff --git a/src/filetypes.c b/src/filetypes.c new file mode 100644 index 0000000..a3618a3 --- /dev/null +++ b/src/filetypes.c @@ -0,0 +1,273 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR firmware portion + + Inspired by and based on code from sd2iec, written by Ingo Korb et al. + See sdcard.c|h, config.h. + + FAT file system access based on code by ChaN, Jim Brain, Ingo Korb, + see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + filetypes.c: directory scanning and file type detection +*/ + +#include +#include +#include +#include +#include "config.h" +#include "uart.h" +#include "filetypes.h" +#include "ff.h" +#include "smc.h" +#include "fileops.h" +#include "crc16.h" +#include "memory.h" +#include "led.h" + +uint16_t scan_flat(const char* path) { + DIR dir; + FRESULT res; + FILINFO fno; + fno.lfn = NULL; + res = f_opendir(&dir, (unsigned char*)path); + uint16_t numentries = 0; + if (res == FR_OK) { + for (;;) { + res = f_readdir(&dir, &fno); + if(res != FR_OK || fno.fname[0] == 0)break; + numentries++; + } + } + return numentries; +} + +uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { + DIR dir; + FILINFO fno; + FRESULT res; + uint8_t len; + unsigned char* fn; + static unsigned char depth = 0; + static uint16_t crc; + static uint32_t db_tgt; + static uint32_t next_subdir_tgt; + static uint32_t parent_tgt; + static uint32_t dir_end = 0; + static uint8_t was_empty = 0; + uint32_t dir_tgt; + uint16_t numentries; + uint32_t dirsize; + uint8_t pass = 0; + + dir_tgt = this_dir_tgt; + if(depth==0) { + crc = 0; + db_tgt = SRAM_DB_ADDR+0x10; + dir_tgt = SRAM_DIR_ADDR; + next_subdir_tgt = SRAM_DIR_ADDR; + this_dir_tgt = SRAM_DIR_ADDR; + parent_tgt = 0; + dprintf("root dir @%lx\n", dir_tgt); + } + + fno.lfn = file_lfn; + numentries=0; + for(pass = 0; pass < 2; pass++) { + if(pass) { + dirsize = 4*(numentries); +// dir_tgt_next = dir_tgt + dirsize + 4; // number of entries + end marker + next_subdir_tgt += dirsize + 4; + if(parent_tgt) next_subdir_tgt += 4; + if(next_subdir_tgt > dir_end) { + dir_end = next_subdir_tgt; + } + dprintf("path=%s depth=%d ptr=%lx entries=%d parent=%lx next subdir @%lx\n", path, depth, db_tgt, numentries, parent_tgt, next_subdir_tgt /*dir_tgt_next*/); +// _delay_ms(50); + if(mkdb) { + dprintf("d=%d Saving %lX to Address %lX [end]\n", depth, 0L, next_subdir_tgt - 4); +// _delay_ms(50); + sram_writelong(0L, next_subdir_tgt - 4); + } + } + res = f_opendir(&dir, (unsigned char*)path); + if (res == FR_OK) { + if(pass && parent_tgt && mkdb) { + // write backlink to parent dir + // switch to next bank if record does not fit in current bank + if((db_tgt&0xffff) > ((0x10000-(sizeof(next_subdir_tgt)+sizeof(len)+4))&0xffff)) { + dprintf("switch! old=%lx ", db_tgt); + db_tgt &= 0xffff0000; + db_tgt += 0x00010000; + dprintf("new=%lx\n", db_tgt); + } + sram_writelong((parent_tgt-SRAM_MENU_ADDR), db_tgt); + sram_writebyte(0, db_tgt+sizeof(next_subdir_tgt)); + sram_writeblock("../\0", db_tgt+sizeof(next_subdir_tgt)+sizeof(len), 4); + sram_writelong((db_tgt-SRAM_MENU_ADDR)|((uint32_t)0x80<<24), dir_tgt); + db_tgt += sizeof(next_subdir_tgt)+sizeof(len)+4; + dir_tgt += 4; + } + len = strlen((char*)path); + for (;;) { + toggle_busy_led(); + res = f_readdir(&dir, &fno); + if (res != FR_OK || fno.fname[0] == 0) { + if(pass) { + if(!numentries) was_empty=1; + } + break; + } + fn = *fno.lfn ? fno.lfn : fno.fname; + // dprintf("%s\n", fn); + // _delay_ms(100); + if (*fn == '.') continue; + if (fno.fattrib & AM_DIR) { + numentries++; + if(pass) { + path[len]='/'; + strncpy(path+len+1, (char*)fn, sizeof(fs_path)-len); + depth++; + if(mkdb) { + uint16_t pathlen = strlen(path); + // write element pointer to current dir structure + dprintf("d=%d Saving %lX to Address %lX [dir]\n", depth, db_tgt, dir_tgt); +// _delay_ms(50); + sram_writelong((db_tgt-SRAM_MENU_ADDR)|((uint32_t)0x80<<24), dir_tgt); +// sram_writeblock((uint8_t*)&db_tgt, dir_tgt_save, sizeof(dir_tgt_save)); + + // save element: + // - path name + // - pointer to sub dir structure + if((db_tgt&0xffff) > ((0x10000-(sizeof(next_subdir_tgt) + sizeof(len) + pathlen + 2))&0xffff)) { + dprintf("switch! old=%lx ", db_tgt); + db_tgt &= 0xffff0000; + db_tgt += 0x00010000; + dprintf("new=%lx\n", db_tgt); + } + dprintf(" Saving dir descriptor to %lX, tgt=%lX, path=%s\n", db_tgt, next_subdir_tgt, path); +// _delay_ms(100); + sram_writelong((next_subdir_tgt-SRAM_MENU_ADDR), db_tgt); + sram_writebyte(len+1, db_tgt+sizeof(next_subdir_tgt)); + sram_writeblock(path, db_tgt+sizeof(next_subdir_tgt)+sizeof(len), pathlen); + sram_writeblock("/\0", db_tgt + sizeof(next_subdir_tgt) + sizeof(len) + pathlen, 2); +// sram_writeblock((uint8_t*)&dir_tgt, db_tgt+256, sizeof(dir_tgt)); + db_tgt += sizeof(next_subdir_tgt) + sizeof(len) + pathlen + 2; + } + parent_tgt = this_dir_tgt; + scan_dir(path, mkdb, next_subdir_tgt); + dir_tgt += 4; +// if(was_empty)dir_tgt_next += 4; + was_empty = 0; + depth--; + path[len]=0; + } + } else { + SNES_FTYPE type = determine_filetype((char*)fn); + if(type != TYPE_UNKNOWN) { + numentries++; + if(pass) { + if(mkdb) { + snes_romprops_t romprops; + path[len]='/'; + strncpy(path+len+1, (char*)fn, sizeof(fs_path)-len); + uint16_t pathlen = strlen(path); + switch(type) { + case TYPE_SMC: +// XXX file_open_by_filinfo(&fno); +// XXX if(file_res){ +// XXX dprintf("ZOMG NOOOO %d\n", file_res); +// XXX _delay_ms(30); +// XXX } +// XXX smc_id(&romprops); +// XXX file_close(); + // _delay_ms(30); + // write element pointer to current dir structure +// dprintf("d=%d Saving %lX to Address %lX [file]\n", depth, db_tgt, dir_tgt); +// _delay_ms(50); + if((db_tgt&0xffff) > ((0x10000-(sizeof(romprops) + sizeof(len) + pathlen + 1))&0xffff)) { + dprintf("switch! old=%lx ", db_tgt); + db_tgt &= 0xffff0000; + db_tgt += 0x00010000; + dprintf("new=%lx\n", db_tgt); + } + sram_writelong((db_tgt-SRAM_MENU_ADDR), dir_tgt); +// sram_writeblock((uint8_t*)&db_tgt, dir_tgt, sizeof(db_tgt)); + dir_tgt += 4; + // save element: + // - SNES header information + // - file name + sram_writeblock((uint8_t*)&romprops, db_tgt, sizeof(romprops)); + sram_writebyte(len+1, db_tgt + sizeof(romprops)); + sram_writeblock(path, db_tgt + sizeof(romprops) + sizeof(len), pathlen + 1); + db_tgt += sizeof(romprops) + sizeof(len) + pathlen + 1; + break; + case TYPE_UNKNOWN: + default: + break; + } + path[len]=0; + // dprintf("%s ", path); + // _delay_ms(30); + } + } else { + unsigned char* sfn = fno.fname; + while(*sfn != 0) { + crc += crc16_update(crc, sfn++, 1); + } + } + } + // dprintf("%s/%s\n", path, fn); + // _delay_ms(50); + } + } + } else uart_putc(0x30+res); + } +// dprintf("%x\n", crc); +// _delay_ms(50); + sram_writelong(db_tgt, SRAM_DB_ADDR+4); + sram_writelong(dir_end, SRAM_DB_ADDR+8); + return crc; +} + + +SNES_FTYPE determine_filetype(char* filename) { + char* ext = strrchr(filename, '.'); + if(ext == NULL) + return TYPE_UNKNOWN; + if(!strcasecmp_P(ext+1, PSTR("SMC"))) { + return TYPE_SMC; + }/* later + if(!strcasecmp_P(ext+1, PSTR("SRM"))) { + return TYPE_SRM; + } + if(!strcasecmp_P(ext+1, PSTR("SPC"))) { + return TYPE_SPC; + }*/ + return TYPE_UNKNOWN; +} + +FRESULT get_db_id(uint16_t* id) { + file_open((uint8_t*)"/sd2snes/sd2snes.db", FA_READ); + if(file_res == FR_OK) { + file_readblock(id, 0, 2); +/* XXX */// *id=0xdead; + file_close(); + } else { + *id=0xdead; + } + return file_res; +} diff --git a/src/filetypes.h b/src/filetypes.h new file mode 100644 index 0000000..6cb443b --- /dev/null +++ b/src/filetypes.h @@ -0,0 +1,45 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR firmware portion + + Inspired by and based on code from sd2iec, written by Ingo Korb et al. + See sdcard.c|h, config.h. + + FAT file system access based on code by ChaN, Jim Brain, Ingo Korb, + see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + filetypes.h: directory scanning and file type detection +*/ + +#ifndef FILETYPES_H +#define FILETYPES_H + +#include "ff.h" +typedef enum { + TYPE_UNKNOWN = 0, /* 0 */ + TYPE_SMC, /* 1 */ + TYPE_SRM, /* 2 */ + TYPE_SPC /* 3 */ +} SNES_FTYPE; + + +char fs_path[256]; +SNES_FTYPE determine_filetype(char* filename); +//uint32_t scan_fs(); +uint16_t scan_dir(char* path, char mkdb, uint32_t this_subdir_tgt); +FRESULT get_db_id(uint16_t*); + +#endif diff --git a/src/flash.script b/src/flash.script index fcf702d..06faace 100644 --- a/src/flash.script +++ b/src/flash.script @@ -7,7 +7,6 @@ reset init flash write_image erase unlock obj/sd2snes.bin 0 bin -sleep 200 reset run shutdown diff --git a/src/init.gdb b/src/init.gdb new file mode 100644 index 0000000..6e7c668 --- /dev/null +++ b/src/init.gdb @@ -0,0 +1,4 @@ +target remote localhost:3333 +add-symbol-file obj/sd2snes.elf 0x0 +load obj/sd2snes.elf + diff --git a/src/led.c b/src/led.c new file mode 100644 index 0000000..2633ec7 --- /dev/null +++ b/src/led.c @@ -0,0 +1,24 @@ +/* ___DISCLAIMER___ */ + +#include +#include "bits.h" +#include "timer.h" + +void rdyled(unsigned int state) { + BITBAND(LPC_GPIO2->FIODIR, 0) = state; +} + +void readled(unsigned int state) { + BITBAND(LPC_GPIO2->FIODIR, 1) = state; +} + +void led_clkout32(uint32_t val) { + while(1) { + rdyled(1); + delay_ms(400); + readled((val & BV(31))>>31); + rdyled(0); + val<<=1; + delay_ms(400); + } +} diff --git a/src/led.h b/src/led.h new file mode 100644 index 0000000..088b113 --- /dev/null +++ b/src/led.h @@ -0,0 +1,10 @@ +/* ___DISCLAIMER___ */ + +#ifndef _LED_H +#define _LED_H + +void readled(unsigned int state); +void rdyled(unsigned int state); +void led_clkout32(uint32_t val); + +#endif diff --git a/src/lpc.h b/src/lpc.h deleted file mode 100644 index 199f185..0000000 --- a/src/lpc.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _LPC_H -#define _LPC_H - -#define BV(x) (1<<(x)) -#define BITBAND(addr,bit) (*((volatile unsigned long *)(((unsigned long)&(addr)-0x20000000)*32 + bit*4 + 0x22000000))) - -#endif - diff --git a/src/main.c b/src/main.c index 5aaed32..1448d00 100644 --- a/src/main.c +++ b/src/main.c @@ -1,35 +1,67 @@ #include #include "clock.h" #include "uart.h" -#include "lpc.h" +#include "bits.h" +#include "power.h" +#include "timer.h" +#include "ff.h" +#include "diskio.h" +#include "spi.h" +#include "sdcard.h" +#include "fileops.h" #define EMC0TOGGLE (3<<4) #define MR0R (1<<1) -#define PCTIM3 (1<<23) int i; +/* FIXME HACK */ +volatile enum diskstates disk_state; + int main(void) { LPC_GPIO2->FIODIR = BV(0) | BV(1) | BV(2); LPC_GPIO1->FIODIR = 0; uint32_t p1; + /* connect UART3 on P0[25:26] + SSP1 on P0[6:9] */ + LPC_PINCON->PINSEL1=(0xf<<18); + LPC_PINCON->PINSEL0=BV(13) | BV(15) | BV(17) | BV(19); - clock_init(); + clock_disconnect(); + + power_init(); + timer_init(); uart_init(); + delay_ms(10); + spi_init(SPI_SPEED_SLOW); + sd_init(); +/* do this last because the peripheral init()s change PCLK dividers */ + clock_init(); + + file_init(); + printf("fileres: %d diskstate: %d\n", file_res, disk_state); + file_open((uint8_t*)"/mon.smc", FA_READ); + spi_set_speed(SPI_SPEED_FAST); + printf("fileres: %d diskstate: %d\n", file_res, disk_state); + uart_putc('S'); + for(p1=0; p1<2048; p1++) { + file_read(); + } + uart_putc('E'); + uart_putcrlf(); + printf("sizeof(struct FIL): %d\n", sizeof(file_handle)); + uart_trace(file_buf, 0, 512); /* setup timer (fpga clk) */ - LPC_SC->PCONP |= PCTIM3; /* enable power */ LPC_TIM3->CTCR=0; LPC_TIM3->EMR=EMC0TOGGLE; LPC_PINCON->PINSEL0=(0x3<<20); LPC_TIM3->MCR=MR0R; LPC_TIM3->MR0=1; LPC_TIM3->TCR=1; - - + uart_puts("hurr durr derpderpderp\n"); while (1) { p1 = LPC_GPIO1->FIOPIN; - BITBAND(LPC_GPIO2->FIOPIN, 0) = (p1 & BV(29))>>29; + BITBAND(LPC_GPIO2->FIOPIN, 1) = (p1 & BV(29))>>29; } } diff --git a/src/power.c b/src/power.c new file mode 100644 index 0000000..50e06e9 --- /dev/null +++ b/src/power.c @@ -0,0 +1,10 @@ +/* ___DISCLAIMER___ */ + +#include +#include "bits.h" +#include "power.h" + + +void power_init() { + LPC_SC->PCONP |= (BV(PCTIM3) | BV(PCUART3)); +} diff --git a/src/power.h b/src/power.h new file mode 100644 index 0000000..d0463cc --- /dev/null +++ b/src/power.h @@ -0,0 +1,14 @@ +/* ___DISCLAIMER___ */ + +#ifndef _POWER_H +#define _POWER_H + +#include "bits.h" + +#define PCUART3 (25) +#define PCUART0 (3) +#define PCTIM3 (23) + +void power_init(void); + +#endif diff --git a/src/printf.c b/src/printf.c new file mode 100644 index 0000000..79249d7 --- /dev/null +++ b/src/printf.c @@ -0,0 +1,290 @@ +/* Small, noncompliant, not-full-featured printf implementation + * + * + * Copyright (c) 2010, Ingo Korb + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ingo Korb nor the + * names of the contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * FIXME: Selection of output function should be more flexible + */ + +#include +#include +#include +#include "config.h" +#include "uart.h" + +#define outfunc(x) uart_putc(x) + +#define FLAG_ZEROPAD 1 +#define FLAG_LEFTADJ 2 +#define FLAG_BLANK 4 +#define FLAG_FORCESIGN 8 +#define FLAG_WIDTH 16 +#define FLAG_LONG 32 +#define FLAG_UNSIGNED 64 +#define FLAG_NEGATIVE 128 + +/* Digits used for conversion */ +static const char hexdigits[] = "0123456789abcdef"; + +/* Temporary buffer used for numbers - just large enough for 32 bit in octal */ +static char buffer[12]; + +/* Output string length */ +static unsigned int outlength; + +/* Output pointer */ +static char *outptr; +static int maxlen; + +/* printf */ +static void outchar(char x) { + if (maxlen) { + maxlen--; + outfunc(x); + outlength++; + } +} + +/* sprintf */ +static void outstr(char x) { + if (maxlen) { + maxlen--; + *outptr++ = x; + outlength++; + } +} + +static int internal_nprintf(void (*output_function)(char c), const char *fmt, va_list ap) { + unsigned int width; + unsigned int flags; + unsigned int base = 0; + char *ptr = NULL; + + outlength = 0; + + while (*fmt) { + while (1) { + if (*fmt == 0) + goto end; + + if (*fmt == '%') { + fmt++; + if (*fmt != '%') + break; + } + + output_function(*fmt++); + } + + flags = 0; + width = 0; + + /* read all flags */ + do { + if (flags < FLAG_WIDTH) { + switch (*fmt) { + case '0': + flags |= FLAG_ZEROPAD; + continue; + + case '-': + flags |= FLAG_LEFTADJ; + continue; + + case ' ': + flags |= FLAG_BLANK; + continue; + + case '+': + flags |= FLAG_FORCESIGN; + continue; + } + } + + if (flags < FLAG_LONG) { + if (*fmt >= '0' && *fmt <= '9') { + unsigned char tmp = *fmt - '0'; + width = 10*width + tmp; + flags |= FLAG_WIDTH; + continue; + } + + if (*fmt == 'h') + continue; + + if (*fmt == 'l') { + flags |= FLAG_LONG; + continue; + } + } + + break; + } while (*fmt++); + + /* Strings */ + if (*fmt == 'c' || *fmt == 's') { + switch (*fmt) { + case 'c': + buffer[0] = va_arg(ap, int); + ptr = buffer; + break; + + case 's': + ptr = va_arg(ap, char *); + break; + } + + goto output; + } + + /* Numbers */ + switch (*fmt) { + case 'u': + flags |= FLAG_UNSIGNED; + case 'd': + base = 10; + break; + + case 'o': + base = 8; + flags |= FLAG_UNSIGNED; + break; + + case 'p': // pointer + output_function('0'); + output_function('x'); + width -= 2; + case 'x': + base = 16; + flags |= FLAG_UNSIGNED; + break; + } + + unsigned int num; + + if (!(flags & FLAG_UNSIGNED)) { + int tmp = va_arg(ap, int); + if (tmp < 0) { + num = -tmp; + flags |= FLAG_NEGATIVE; + } else + num = tmp; + } else { + num = va_arg(ap, unsigned int); + } + + /* Convert number into buffer */ + ptr = buffer + sizeof(buffer); + *--ptr = 0; + do { + *--ptr = hexdigits[num % base]; + num /= base; + } while (num != 0); + + /* Sign */ + if (flags & FLAG_NEGATIVE) { + output_function('-'); + width--; + } else if (flags & FLAG_FORCESIGN) { + output_function('+'); + width--; + } else if (flags & FLAG_BLANK) { + output_function(' '); + width--; + } + + output: + /* left padding */ + if ((flags & FLAG_WIDTH) && !(flags & FLAG_LEFTADJ)) { + while (strlen(ptr) < width) { + if (flags & FLAG_ZEROPAD) + output_function('0'); + else + output_function(' '); + width--; + } + } + + /* data */ + while (*ptr) { + output_function(*ptr++); + if (width) + width--; + } + + /* right padding */ + if (flags & FLAG_WIDTH) { + while (width) { + output_function(' '); + width--; + } + } + + fmt++; + } + + end: + return outlength; +} + +int printf(const char *format, ...) { + va_list ap; + int res; + + maxlen = -1; + va_start(ap, format); + res = internal_nprintf(outchar, format, ap); + va_end(ap); + return res; +} + +int snprintf(char *str, size_t size, const char *format, ...) { + va_list ap; + int res; + + maxlen = size; + outptr = str; + va_start(ap, format); + res = internal_nprintf(outstr, format, ap); + va_end(ap); + if (res < size) + str[res] = 0; + return res; +} + +/* Required for gcc compatibility */ +int puts(const char *str) { + uart_puts(str); + uart_putc('\n'); + return 0; +} + +#undef putchar +int putchar(int c) { + uart_putc(c); + return 0; +} diff --git a/src/rtc.c b/src/rtc.c new file mode 100644 index 0000000..5f379cd --- /dev/null +++ b/src/rtc.c @@ -0,0 +1,54 @@ +#include +#include +#include "config.h" +#include "rtc.h" + +rtcstate_t rtc_state; + +#define CLKEN 0 +#define CTCRST 1 + +void rtc_init(void) { + if (LPC_RTC->CCR & BV(CLKEN)) { + rtc_state = RTC_OK; + } else { + rtc_state = RTC_INVALID; + } +} + +void read_rtc(struct tm *time) { + do { + time->tm_sec = LPC_RTC->SEC; + time->tm_min = LPC_RTC->MIN; + time->tm_hour = LPC_RTC->HOUR; + time->tm_mday = LPC_RTC->DOM; + time->tm_mon = LPC_RTC->MONTH; + time->tm_year = LPC_RTC->YEAR - 1900; + time->tm_wday = LPC_RTC->DOW; + } while (time->tm_sec != LPC_RTC->SEC); +} + +void set_rtc(struct tm *time) { + LPC_RTC->CCR = BV(CTCRST); + LPC_RTC->SEC = time->tm_sec; + LPC_RTC->MIN = time->tm_min; + LPC_RTC->HOUR = time->tm_hour; + LPC_RTC->DOM = time->tm_mday; + LPC_RTC->MONTH = time->tm_mon; + LPC_RTC->YEAR = time->tm_year + 1900; + LPC_RTC->DOW = time->tm_wday; + LPC_RTC->CCR = BV(CLKEN); +} + + +uint32_t get_fattime(void) { + struct tm time; + + read_rtc(&time); + return ((uint32_t)time.tm_year-80) << 25 | + ((uint32_t)time.tm_mon+1) << 21 | + ((uint32_t)time.tm_mday) << 16 | + ((uint32_t)time.tm_hour) << 11 | + ((uint32_t)time.tm_min) << 5 | + ((uint32_t)time.tm_sec) >> 1; +} diff --git a/src/rtc.h b/src/rtc.h new file mode 100644 index 0000000..71a305c --- /dev/null +++ b/src/rtc.h @@ -0,0 +1,62 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-2010 Ingo Korb + + Inspiration and low-level SD/MMC access based on code from MMC2IEC + by Lars Pontoppidan et al., see sdcard.c|h and config.h. + + FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + rtc.h: Definitions for RTC support + + There is no rtc.c, the functions defined here are implemented by a + device-specific .c file, e.g. pcf8583.c. + +*/ + +#ifndef RTC_H +#define RTC_H + +#include + +typedef enum { + RTC_NOT_FOUND, /* No RTC present */ + RTC_INVALID, /* RTC present, but contents invalid */ + RTC_OK /* RTC present and working */ +} rtcstate_t; + +struct tm { + uint8_t tm_sec; // 0..59 + uint8_t tm_min; // 0..59 + uint8_t tm_hour; // 0..23 + uint8_t tm_mday; // 1..[28..31] + uint8_t tm_mon; // 0..11 + uint8_t tm_year; // since 1900, i.e. 2000 is 100 + uint8_t tm_wday; // 0 to 6, sunday is 6 + // A Unix struct tm has a few more fields we don't need in this application +}; + +extern rtcstate_t rtc_state; + +void rtc_init(void); + +/* Return current time in struct tm */ +void read_rtc(struct tm *time); + +/* Set time from struct tm */ +void set_rtc(struct tm *time); + +#endif diff --git a/src/sdcard.c b/src/sdcard.c new file mode 100644 index 0000000..4c6859e --- /dev/null +++ b/src/sdcard.c @@ -0,0 +1,750 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-2010 Ingo Korb + + Inspiration and low-level SD/MMC access based on code from MMC2IEC + by Lars Pontoppidan et al., see sdcard.c|h and config.h. + + FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + sdcard.c: SD/MMC access routines + + Extended, optimized and cleaned version of code from MMC2IEC, + original copyright header follows: + +// +// Title : SD/MMC Card driver +// Author : Lars Pontoppidan, Aske Olsson, Pascal Dufour, +// Date : Jan. 2006 +// Version : 0.42 +// Target MCU : Atmel AVR Series +// +// CREDITS: +// This module is developed as part of a project at the technical univerisity of +// Denmark, DTU. +// +// DESCRIPTION: +// This SD card driver implements the fundamental communication with a SD card. +// The driver is confirmed working on 8 MHz and 14.7456 MHz AtMega32 and has +// been tested successfully with a large number of different SD and MMC cards. +// +// DISCLAIMER: +// The author is in no way responsible for any problems or damage caused by +// using this code. Use at your own risk. +// +// LICENSE: +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// + + The exported functions in this file are weak-aliased to their corresponding + versions defined in diskio.h so when this file is the only diskio provider + compiled in they will be automatically used by the linker. + +*/ + +#include +#include +#include "config.h" +#include "crc.h" +#include "diskio.h" +#include "spi.h" +#include "timer.h" +#include "uart.h" +#include "sdcard.h" + +// FIXME: Move, make configurable +static void set_sd_led(uint8_t state) { + BITBAND(LPC_GPIO2->FIODIR, 2) = state; +} + +// FIXME: Move, add generic C or AVR ASM version +static uint32_t swap_word(uint32_t input) { + uint32_t result; + asm("rev %[result], %[input]" : [result] "=r" (result) : [input] "r" (input)); + return result; +} + +#ifdef CONFIG_TWINSD +# define MAX_CARDS 2 +#else +# define MAX_CARDS 1 +#endif + +// SD/MMC commands +#define GO_IDLE_STATE 0 +#define SEND_OP_COND 1 +#define SWITCH_FUNC 6 +#define SEND_IF_COND 8 +#define SEND_CSD 9 +#define SEND_CID 10 +#define STOP_TRANSMISSION 12 +#define SEND_STATUS 13 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define READ_MULTIPLE_BLOCK 18 +#define WRITE_BLOCK 24 +#define WRITE_MULTIPLE_BLOCK 25 +#define PROGRAM_CSD 27 +#define SET_WRITE_PROT 28 +#define CLR_WRITE_PROT 29 +#define SEND_WRITE_PROT 30 +#define ERASE_WR_BLK_STAR_ADDR 32 +#define ERASE_WR_BLK_END_ADDR 33 +#define ERASE 38 +#define LOCK_UNLOCK 42 +#define APP_CMD 55 +#define GEN_CMD 56 +#define READ_OCR 58 +#define CRC_ON_OFF 59 + +// SD ACMDs +#define SD_STATUS 13 +#define SD_SEND_NUM_WR_BLOCKS 22 +#define SD_SET_WR_BLK_ERASE_COUNT 23 +#define SD_SEND_OP_COND 41 +#define SD_SET_CLR_CARD_DETECT 42 +#define SD_SEND_SCR 51 + +// R1 status bits +#define STATUS_IN_IDLE 1 +#define STATUS_ERASE_RESET 2 +#define STATUS_ILLEGAL_COMMAND 4 +#define STATUS_CRC_ERROR 8 +#define STATUS_ERASE_SEQ_ERROR 16 +#define STATUS_ADDRESS_ERROR 32 +#define STATUS_PARAMETER_ERROR 64 + + +/* Card types - cardtype == 0 is MMC */ +#define CARD_SD (1<<0) +#define CARD_SDHC (1<<1) + +static uint8_t cardtype[MAX_CARDS]; + +/** + * getbits - read value from bit buffer + * @buffer: pointer to the data buffer + * @start : index of the first bit in the value + * @bits : number of bits in the value + * + * This function returns a value from the memory region passed as + * buffer, starting with bit "start" and "bits" bit long. The buffer + * is assumed to be MSB first, passing 0 for start will read starting + * from the highest-value bit of the first byte of the buffer. + */ +static uint32_t getbits(void *buffer, uint16_t start, int8_t bits) { + uint8_t *buf = buffer; + uint32_t result = 0; + + if ((start % 8) != 0) { + /* Unaligned start */ + result += buf[start / 8] & (0xff >> (start % 8)); + bits -= 8 - (start % 8); + start += 8 - (start % 8); + } + while (bits >= 8) { + result = (result << 8) + buf[start / 8]; + start += 8; + bits -= 8; + } + if (bits > 0) { + result = result << bits; + result = result + (buf[start / 8] >> (8-bits)); + } else if (bits < 0) { + /* Fraction of a single byte */ + result = result >> -bits; + } + return result; +} + +/** + * wait_for_response - waits for a response from the SD card + * @expected: expected data byte (0 for anything != 0) + * + * This function waits until reading from the SD card returns the + * byte in expected or until reading returns a non-zero byte if + * expected is 0. Returns false if the expected response wasn't + * received within 500ms or true if it was. + */ +static uint8_t wait_for_response(uint8_t expected) { + tick_t timeout = getticks() + HZ/2; + + while (time_before(getticks(), timeout)) { + uint8_t byte = spi_rx_byte(); + + if (expected == 0 && byte != 0) + return 1; + + if (expected != 0 && byte == expected) + return 1; + } + + return 0; +} + +static void deselectCard(uint8_t card) { + // Send 8 clock cycles + set_sd_led(0); + spi_rx_byte(); +} + +/** + * sendCommand - send a command to the SD card + * @card : card number to be accessed + * @command : command to be sent + * @parameter: parameter to be sent + * @deselect : Flags if the card should be deselected afterwards + * + * This function calculates the correct CRC7 for the command and + * parameter and transmits all of it to the SD card. If requested + * the card will be deselected afterwards. + */ +static int sendCommand(const uint8_t card, + const uint8_t command, + const uint32_t parameter, + const uint8_t deselect) { + union { + uint32_t l; + uint8_t c[4]; + } long2char; + + uint8_t i,crc,errorcount; + tick_t timeout; + + long2char.l = parameter; + crc = crc7update(0 , 0x40+command); + crc = crc7update(crc, long2char.c[3]); + crc = crc7update(crc, long2char.c[2]); + crc = crc7update(crc, long2char.c[1]); + crc = crc7update(crc, long2char.c[0]); + crc = (crc << 1) | 1; + + errorcount = 0; + while (errorcount < CONFIG_SD_AUTO_RETRIES) { + // Select card + set_sd_led(1); +#ifdef CONFIG_TWINSD + if (card == 0 && command == GO_IDLE_STATE) + /* Force both cards to SPI mode simultaneously */ + SPI_SS_LOW(1); +#endif + + // Transfer command + spi_tx_byte(0x40+command); + uint32_t tmp = swap_word(parameter); + spi_tx_block(&tmp, 4); + spi_tx_byte(crc); + + // Wait for a valid response + timeout = getticks() + HZ/2; + do { + i = spi_rx_byte(); + } while (i & 0x80 && time_before(getticks(), timeout)); + +#ifdef CONFIG_TWINSD + if (card == 0 && command == GO_IDLE_STATE) + SPI_SS_HIGH(1); +#endif + + // Check for CRC error + // can't reliably retry unless deselect is allowed + if (deselect && (i & STATUS_CRC_ERROR)) { + uart_putc('x'); + deselectCard(card); + errorcount++; + continue; + } + + if (deselect) deselectCard(card); + break; + } + + return i; +} + +// Extended init sequence for SDHC support +static uint8_t extendedInit(const uint8_t card) { + uint8_t i; + uint32_t answer; + + // Send CMD8: SEND_IF_COND + // 0b000110101010 == 2.7-3.6V supply, check pattern 0xAA + i = sendCommand(card, SEND_IF_COND, 0b000110101010, 0); + if (i > 1) { + // Card returned an error, ok (MMC or SD1.x) but not SDHC + deselectCard(card); + return 1; + } + + // No error, continue SDHC initialization + spi_rx_block(&answer, 4); + answer = swap_word(answer); + deselectCard(card); + + if (((answer >> 8) & 0x0f) != 0b0001) { + // Card didn't accept our voltage specification + return 0; + } + + // Verify echo-back of check pattern + if ((answer & 0xff) != 0b10101010) { + // Check pattern mismatch, working but not SD2.0 compliant + // The specs say we should not use the card, but let's try anyway. + return 1; + } + + return 1; +} + +// SD common initialisation +static void sdInit(const uint8_t card) { + uint8_t i; + uint16_t counter; +printf("sdInit\n"); + counter = 0xffff; + do { + // Prepare for ACMD, send CMD55: APP_CMD + i = sendCommand(card, APP_CMD, 0, 1); + if (i > 1) { + // Command not accepted, could be MMC + return; + } + + // Send ACMD41: SD_SEND_OP_COND + // 1L<<30 == Host has High Capacity Support + i = sendCommand(card, SD_SEND_OP_COND, 1L<<30, 1); + // Repeat while card card accepts command but isn't ready + } while (i == 1 && --counter > 0); + + // Ignore failures, there is at least one Sandisk MMC card + // that accepts CMD55, but not ACMD41. + if (i == 0) + /* We know that a card is SD if ACMD41 was accepted. */ + cardtype[card] |= CARD_SD; +} + +/* Detect changes of SD card 0 */ +#ifdef SD_CHANGE_VECT +ISR(SD_CHANGE_VECT) { + if (SDCARD_DETECT) + disk_state = DISK_CHANGED; + else + disk_state = DISK_REMOVED; +} +#endif + +#ifdef CONFIG_TWINSD +/* Detect changes of SD card 1 */ +ISR(SD2_CHANGE_VECT) { + if (SD2_DETECT) + disk_state = DISK_CHANGED; + else + disk_state = DISK_REMOVED; +} +#endif + +// +// Public functions +// +void sd_init(void) { + /* + SDCARD_DETECT_SETUP(); + SDCARD_WP_SETUP(); + SD_CHANGE_SETUP(); + */ +#ifdef CONFIG_TWINSD + /* Initialize the control lines for card 2 */ + SD2_SETUP(); + SD2_CHANGE_SETUP(); +#endif +} +void disk_init(void) __attribute__ ((weak, alias("sd_init"))); + + +DSTATUS sd_status(BYTE drv) { +#ifdef CONFIG_TWINSD + if (drv != 0) { + if (SD2_DETECT) { + if (SD2_PIN & SD2_WP) { + return STA_PROTECT; + } else { + return RES_OK; + } + } else { + return STA_NOINIT|STA_NODISK; + } + } else +#endif + if (SDCARD_DETECT) + if (SDCARD_WP) + return STA_PROTECT; + else + return RES_OK; + else + return STA_NOINIT|STA_NODISK; +} +DSTATUS disk_status(BYTE drv) __attribute__ ((weak, alias("sd_status"))); + + +/** + * sd_initialize - initialize SD card + * @drv : drive + * + * This function tries to initialize the selected SD card. + */ +DSTATUS sd_initialize(BYTE drv) { + uint8_t i; + uint16_t counter; + uint32_t answer; + + if (drv >= MAX_CARDS) + return STA_NOINIT|STA_NODISK; + /* Don't bother initializing a card that isn't there */ + if (sd_status(drv) & STA_NODISK) + return sd_status(drv); + /* JLB: Should be in sd_init, but some uIEC versions have + * IEC lines tied to SPI, so I moved it here to resolve the + * conflict. + */ + spi_init(SPI_SPEED_SLOW); + disk_state = DISK_ERROR; + + cardtype[drv] = 0; + + set_sd_led(0); + + // Send 80 clks + for (counter=0; counter<1000; counter++) { + spi_tx_byte(0xff); + } + + // Reset card + i = sendCommand(drv, GO_IDLE_STATE, 0, 1); + if (i != 1) { + return STA_NOINIT | STA_NODISK; + } + + if (!extendedInit(drv)) { + return STA_NOINIT | STA_NODISK; + } + + sdInit(drv); + + counter = 0xffff; + // According to the spec READ_OCR should work at this point + // without retries. One of my Sandisk-cards thinks otherwise. + do { + // Send CMD58: READ_OCR + i = sendCommand(drv, READ_OCR, 0, 0); + if (i > 1) + deselectCard(drv); + } while (i > 1 && counter-- > 0); + + if (counter > 0) { + spi_rx_block(&answer, 4); + answer = swap_word(answer); + + // See if the card likes our supply voltage + if (!(answer & SD_SUPPLY_VOLTAGE)) { + // The code isn't set up to completely ignore the card, + // but at least report it as nonworking + deselectCard(drv); + return STA_NOINIT | STA_NODISK; + } + + // See what card we've got + if (answer & 0x40000000) { + cardtype[drv] |= CARD_SDHC; + } + } + + // Keep sending CMD1 (SEND_OP_COND) command until zero response + counter = 0xffff; + do { + i = sendCommand(drv, SEND_OP_COND, 1L<<30, 1); + counter--; + } while (i != 0 && counter > 0); + + if (counter==0) { + return STA_NOINIT | STA_NODISK; + } + +#ifdef CONFIG_SD_DATACRC + // Enable CRC checking + // The SD spec says that the host "should" send CRC_ON_OFF before ACMD_SEND_OP_COND. + // The MMC manual I have says that CRC_ON_OFF isn't allowed before SEND_OP_COND. + // Let's just hope that all SD cards work with this order. =( + i = sendCommand(drv, CRC_ON_OFF, 1, 1); + if (i > 1) { + return STA_NOINIT | STA_NODISK; + } +#endif + + // Send MMC CMD16(SET_BLOCKLEN) to 512 bytes + i = sendCommand(drv, SET_BLOCKLEN, 512, 1); + if (i != 0) { + return STA_NOINIT | STA_NODISK; + } + + // Thats it! + spi_set_speed(SPI_SPEED_FAST); + disk_state = DISK_OK; + return sd_status(drv); +} +DSTATUS disk_initialize(BYTE drv) __attribute__ ((weak, alias("sd_initialize"))); + + +/** + * sd_read - reads sectors from the SD card to buffer + * @drv : drive + * @buffer: pointer to the buffer + * @sector: first sector to be read + * @count : number of sectors to be read + * + * This function reads count sectors from the SD card starting + * at sector to buffer. Returns RES_ERROR if an error occured or + * RES_OK if successful. Up to SD_AUTO_RETRIES will be made if + * the calculated data CRC does not match the one sent by the + * card. If there were errors during the command transmission + * disk_state will be set to DISK_ERROR and no retries are made. + */ +DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) { + uint8_t sec,res,errorcount; + uint16_t crc,recvcrc; + if (drv >= MAX_CARDS) + return RES_PARERR; + + for (sec=0;sec= CONFIG_SD_AUTO_RETRIES) return RES_ERROR; + } + + return RES_OK; +} +DRESULT disk_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_read"))); + + + +/** + * sd_write - writes sectors from buffer to the SD card + * @drv : drive + * @buffer: pointer to the buffer + * @sector: first sector to be written + * @count : number of sectors to be written + * + * This function writes count sectors from buffer to the SD card + * starting at sector. Returns RES_ERROR if an error occured, + * RES_WPRT if the card is currently write-protected or RES_OK + * if successful. Up to SD_AUTO_RETRIES will be made if the card + * signals a CRC error. If there were errors during the command + * transmission disk_state will be set to DISK_ERROR and no retries + * are made. + */ +DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) { + uint8_t res,sec,errorcount,status; + uint16_t crc; + + if (drv >= MAX_CARDS) + return RES_PARERR; + +#ifdef CONFIG_TWINSD + if (drv != 0) { + if (SD2_PIN & SD2_WP) + return RES_WRPRT; + } else +#endif + + if (SDCARD_WP) return RES_WRPRT; + + for (sec=0;sec> 8); + spi_tx_byte(crc & 0xff); + + // Get and check status feedback + status = spi_rx_byte(); + + // Retry if neccessary + if ((status & 0x0F) != 0x05) { + uart_putc('X'); + deselectCard(drv); + errorcount++; + continue; + } + + // Wait for write finish + if (!wait_for_response(0)) { + set_sd_led(0); + disk_state = DISK_ERROR; + return RES_ERROR; + } + break; + } + deselectCard(drv); + + if (errorcount >= CONFIG_SD_AUTO_RETRIES) { + if (!(status & STATUS_CRC_ERROR)) + disk_state = DISK_ERROR; + return RES_ERROR; + } + } + + return RES_OK; +} +DRESULT disk_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_write"))); + +DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer) { + uint8_t buf[18]; + uint32_t capacity; + + if (drv >= MAX_CARDS) + return RES_NOTRDY; + + if (sd_status(drv) & STA_NODISK) + return RES_NOTRDY; + + if (page != 0) + return RES_ERROR; + + /* Try to calculate the total number of sectors on the card */ + /* FIXME: Write a generic data read function and merge with sd_read */ + if (sendCommand(drv, SEND_CSD, 0, 0) != 0) { + deselectCard(drv); + return RES_ERROR; + } + + /* Wait for data token */ + if (!wait_for_response(0xfe)) { + deselectCard(drv); + return RES_ERROR; + } + + spi_rx_block(buf, 18); + deselectCard(drv); + + if (cardtype[drv] & CARD_SDHC) { + /* Special CSD for SDHC cards */ + capacity = (1 + getbits(buf,127-69,22)) * 1024; + } else { + /* Assume that MMC-CSD 1.0/1.1/1.2 and SD-CSD 1.1 are the same... */ + uint8_t exponent = 2 + getbits(buf, 127-49, 3); + capacity = 1 + getbits(buf, 127-73, 12); + exponent += getbits(buf, 127-83,4) - 9; + while (exponent--) capacity *= 2; + } + + diskinfo0_t *di = buffer; + di->validbytes = sizeof(diskinfo0_t); + di->disktype = DISK_TYPE_SD; + di->sectorsize = 2; + di->sectorcount = capacity; + + return RES_OK; +} +DRESULT disk_getinfo(BYTE drv, BYTE page, void *buffer) __attribute__ ((weak, alias("sd_getinfo"))); diff --git a/src/sdcard.h b/src/sdcard.h new file mode 100644 index 0000000..c1f88af --- /dev/null +++ b/src/sdcard.h @@ -0,0 +1,40 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-2010 Ingo Korb + + Inspiration and low-level SD/MMC access based on code from MMC2IEC + by Lars Pontoppidan et al., see sdcard.c|h and config.h. + + FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + sdcard.h: Definitions for the SD/MMC access routines + +*/ + +#ifndef SDCARD_H +#define SDCARD_H + +#include "diskio.h" + +/* These functions are weak-aliased to disk_... */ +void sd_init(void); +DSTATUS sd_status(BYTE drv); +DSTATUS sd_initialize(BYTE drv); +DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count); +DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count); +DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer); + +#endif diff --git a/src/snes.c b/src/snes.c new file mode 100644 index 0000000..e765fd1 --- /dev/null +++ b/src/snes.c @@ -0,0 +1,146 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR firmware portion + + Inspired by and based on code from sd2iec, written by Ingo Korb et al. + See sdcard.c|h, config.h. + + FAT file system access based on code by ChaN, Jim Brain, Ingo Korb, + see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + snes.c: SNES hardware control and monitoring +*/ + +#include +#include "avrcompat.h" +#include "config.h" +#include "uart.h" +#include "snes.h" +#include "memory.h" +#include "fileops.h" +#include "ff.h" +#include "led.h" +#include "smc.h" + +uint8_t initloop=1; +uint32_t saveram_crc, saveram_crc_old; +void snes_init() { + DDRD |= _BV(PD5); // PD5 = RESET_DIR + DDRD |= _BV(PD6); // PD6 = RESET + snes_reset(1); +} + +/* + * sets the SNES reset state. + * + * state: put SNES in reset state when 1, release when 0 + */ +void snes_reset(int state) { + if(state) { + DDRD |= _BV(PD6); // /RESET pin -> out + PORTD &= ~_BV(PD6); // /RESET = 0 + PORTD |= _BV(PD5); // RESET_DIR = 1; + } else { + PORTD &= ~_BV(PD5); // RESET_DIR = 0; + DDRD &= ~_BV(PD6); // /RESET pin -> in + PORTD |= _BV(PD6); // /RESET = pullup + } +} + +/* + * gets the SNES reset state. + * + * returns: 1 when reset, 0 when not reset + */ +uint8_t get_snes_reset() { +// DDRD &= ~_BV(PD6); // /RESET pin -> in +// PORTD &= ~_BV(PD5); // RESET_DIR (external buffer) = 0 + return !(PIND & _BV(PD6)); +} + +/* + * SD2SNES main loop. + * monitors SRAM changes and other things + */ +uint32_t diffcount = 0, samecount = 0, didnotsave = 0; +uint8_t sram_valid = 0; +void snes_main_loop() { + if(!romprops.ramsize_bytes)return; + if(initloop) { + saveram_crc_old = calc_sram_crc(SRAM_SAVE_ADDR, romprops.ramsize_bytes); + initloop=0; + } + saveram_crc = calc_sram_crc(SRAM_SAVE_ADDR, romprops.ramsize_bytes); + sram_valid = sram_reliable(); + if(crc_valid && sram_valid) { + if(saveram_crc != saveram_crc_old) { + if(samecount) { + diffcount=1; + } else { + diffcount++; + didnotsave++; + } + samecount=0; + } + if(saveram_crc == saveram_crc_old) { + samecount++; + } + if(diffcount>=1 && samecount==5) { + uart_putc('U'); + uart_puthexshort(saveram_crc); + uart_putcrlf(); + set_busy_led(1); + save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); + set_busy_led(0); + didnotsave=0; + } + if(didnotsave>50) { + diffcount=0; + uart_putc('V'); + set_busy_led(1); + save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); + didnotsave=0; + set_busy_led(0); + } + saveram_crc_old = saveram_crc; + } + dprintf("crc_valid=%d sram_valid=%d diffcount=%ld samecount=%ld, didnotsave=%ld\n", crc_valid, sram_valid, diffcount, samecount, didnotsave); +} + +/* + * SD2SNES menu loop. + * monitors menu selection. return when selection was made. + */ +uint8_t menu_main_loop() { + uint8_t cmd = 0; + sram_writebyte(0, SRAM_CMD_ADDR); + while(!cmd) { + if(!get_snes_reset()) { + while(!sram_reliable()); + cmd = sram_readbyte(SRAM_CMD_ADDR); + } + if(get_snes_reset()) { + cmd = 0; + } + } + return cmd; +} + +void get_selected_name(uint8_t* fn) { + uint32_t addr = sram_readlong(SRAM_FD_ADDR); + dprintf("fd addr=%lX\n", addr); + sram_readblock(fn, addr+0x41+SRAM_MENU_ADDR, 256); +} diff --git a/src/snes.h b/src/snes.h new file mode 100644 index 0000000..48be5d4 --- /dev/null +++ b/src/snes.h @@ -0,0 +1,37 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR firmware portion + + Inspired by and based on code from sd2iec, written by Ingo Korb et al. + See sdcard.c|h, config.h. + + FAT file system access based on code by ChaN, Jim Brain, Ingo Korb, + see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + snes.h: SNES hardware control and monitoring +*/ + +#ifndef SNES_H +#define SNES_H +uint8_t crc_valid; + +void snes_init(void); +void snes_reset(int state); +uint8_t get_snes_reset(void); +void snes_main_loop(void); +uint8_t menu_main_loop(void); +void get_selected_name(uint8_t* lfn); +#endif diff --git a/src/spi.c b/src/spi.c new file mode 100644 index 0000000..7b721c6 --- /dev/null +++ b/src/spi.c @@ -0,0 +1,202 @@ +/* Sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-2010 Ingo Korb + + Inspiration and low-level SD/MMC access based on code from MMC2IEC + by Lars Pontoppidan et al., see sdcard.c|h and config.h. + + FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + spi.c: Low-level SPI routines + +*/ + +#include +#include +#include "config.h" +#include "spi.h" + +#define SSP_TFE 0 // Transmit FIFO empty +#define SSP_TNF 1 // Transmit FIFO not full +#define SSP_RNE 2 // Receive FIFO not empty +#define SSP_RFF 3 // Receive FIFO full +#define SSP_BSY 4 // Busy + +// FIXME: Move to config.h! +#define SSP_CLK_DIVISOR_FAST 4 +#define SSP_CLK_DIVISOR_SLOW 250 + +#define SSP_REGS LPC_SSP1 +#define SSP_PCLKREG PCLKSEL0 +/* SSP0: PCLKSEL1 + SSP1: PCLKSEL0 */ +#define SSP_PCLKBIT 20 +/* SSP0: 10 + SSP1: 20 */ +#define SSP_DMAID_TX 2 +/* SSP0: 0 + SSP1: 2 */ +#define SSP_DMAID_RX 3 +/* SSP0: 1 + SSP1: 3 */ + +void spi_init(spi_speed_t speed) { + /* Set clock prescaler to 1:1 */ + BITBAND(LPC_SC->SSP_PCLKREG, SSP_PCLKBIT) = 1; + + /* configure data format - 8 bits, SPI, CPOL=0, CPHA=0, 1 clock per bit */ + SSP_REGS->CR0 = (8-1); + + /* set clock prescaler */ + if (speed == SPI_SPEED_FAST) { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST; + } else { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW; + } + + /* Enable SSP */ + SSP_REGS->CR1 = BV(1); + + /* Enable DMA controller, little-endian mode */ + BITBAND(LPC_SC->PCONP, 29) = 1; + LPC_GPDMA->DMACConfig = 1; +} + +void spi_tx_byte(uint8_t data) { + /* Wait until TX fifo can accept data */ + while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ; + + /* Send byte */ + SSP_REGS->DR = data; +} + +uint8_t spi_rx_byte(void) { + /* Wait until SSP is not busy */ + while (BITBAND(SSP_REGS->SR, SSP_BSY)) ; + + /* Clear RX fifo */ + while (BITBAND(SSP_REGS->SR, SSP_RNE)) + (void) SSP_REGS->DR; + + /* Transmit a single dummy byte */ + SSP_REGS->DR = 0xff; + + /* Wait until answer has been received */ + while (!BITBAND(SSP_REGS->SR, SSP_RNE)) ; + + return SSP_REGS->DR; +} + +void spi_tx_block(const void *ptr, unsigned int length) { + const uint8_t *data = (const uint8_t *)ptr; + + while (length--) { + /* Wait until TX fifo can accept data */ + while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ; + + SSP_REGS->DR = *data++; + } +} + +void spi_rx_block(void *ptr, unsigned int length) { + uint8_t *data = (uint8_t *)ptr; + unsigned int txlen = length; + + /* Wait until SSP is not busy */ + while (BITBAND(SSP_REGS->SR, SSP_BSY)) ; + + /* Clear RX fifo */ + while (BITBAND(SSP_REGS->SR, SSP_RNE)) + (void) SSP_REGS->DR; + + if ((length & 3) != 0 || ((uint32_t)ptr & 3) != 0) { + /* Odd length or unaligned buffer */ + while (length > 0) { + /* Wait until TX or RX FIFO are ready */ + while (txlen > 0 && !BITBAND(SSP_REGS->SR, SSP_TNF) && + !BITBAND(SSP_REGS->SR, SSP_RNE)) ; + + /* Try to receive data */ + while (length > 0 && BITBAND(SSP_REGS->SR, SSP_RNE)) { + *data++ = SSP_REGS->DR; + length--; + } + + /* Send dummy data until TX full or RX ready */ + while (txlen > 0 && BITBAND(SSP_REGS->SR, SSP_TNF) && !BITBAND(SSP_REGS->SR, SSP_RNE)) { + txlen--; + SSP_REGS->DR = 0xff; + } + } + } else { + /* Clear interrupt flags of DMA channels 0 */ + LPC_GPDMA->DMACIntTCClear = BV(0); + LPC_GPDMA->DMACIntErrClr = BV(0); + + /* Set up RX DMA channel */ + LPC_GPDMACH0->DMACCSrcAddr = (uint32_t)&SSP_REGS->DR; + LPC_GPDMACH0->DMACCDestAddr = (uint32_t)ptr; + LPC_GPDMACH0->DMACCLLI = 0; // no linked list + LPC_GPDMACH0->DMACCControl = length + | (0 << 12) // source burst size 1 (FIXME: Check if larger possible/useful) + | (0 << 15) // destination burst size 1 + | (0 << 18) // source transfer width 1 byte + | (2 << 21) // destination transfer width 4 bytes + | (0 << 26) // source address not incremented + | (1 << 27) // destination address incremented + ; + LPC_GPDMACH0->DMACCConfig = 1 // enable channel + | (SSP_DMAID_RX << 1) // data source SSP RX + | (2 << 11) // transfer from peripheral to memory + ; + + /* Enable RX FIFO DMA */ + SSP_REGS->DMACR = 1; + + /* Write bytes into TX FIFO */ + // FIXME: Any value in doing this using DMA too? + while (txlen > 0) { + while (txlen > 0 && BITBAND(SSP_REGS->SR, SSP_TNF)) { + txlen--; + SSP_REGS->DR = 0xff; + } + } + + /* Wait until DMA channel disables itself */ + while (LPC_GPDMACH0->DMACCConfig & 1) ; + + /* Disable RX FIFO DMA */ + SSP_REGS->DMACR = 0; + } +} + +void spi_set_speed(spi_speed_t speed) { + /* Wait until TX fifo is empty */ + while (!BITBAND(SSP_REGS->SR, 0)) ; + + /* Disable SSP (FIXME: Is this required?) */ + SSP_REGS->CR1 = 0; + + /* Change clock divisor */ + if (speed == SPI_SPEED_FAST) { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST; + } else { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW; + } + + /* Enable SSP */ + SSP_REGS->CR1 = BV(1); +} diff --git a/src/spi.h b/src/spi.h new file mode 100644 index 0000000..493edd2 --- /dev/null +++ b/src/spi.h @@ -0,0 +1,53 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-2010 Ingo Korb + + Inspiration and low-level SD/MMC access based on code from MMC2IEC + by Lars Pontoppidan et al., see sdcard.c|h and config.h. + + FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + spi.h: Definitions for the low-level SPI routines + + Based on original code by Lars Pontoppidan et al., see spi.c + for full copyright details. + +*/ +#ifndef SPI_H +#define SPI_H + +/* Low speed 400kHz for init, fast speed <=20MHz (MMC limit) */ +typedef enum { SPI_SPEED_FAST, SPI_SPEED_SLOW } spi_speed_t; + +/* Initialize SPI interface */ +void spi_init(spi_speed_t speed); + +/* Transmit a single byte */ +void spi_tx_byte(uint8_t data); + +/* Transmit a data block */ +void spi_tx_block(const void *data, unsigned int length); + +/* Receive a single byte */ +uint8_t spi_rx_byte(void); + +/* Receive a data block */ +void spi_rx_block(void *data, unsigned int length); + +/* Switch speed of SPI interface */ +void spi_set_speed(spi_speed_t speed); + +#endif diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..5514156 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,69 @@ +/* ___INGO___ */ + +#include +#include "bits.h" +#include "config.h" +#include "timer.h" +#include "clock.h" + + +/* bit definitions */ +#define RITINT 0 +#define RITEN 3 + +#define PCRIT 16 + +volatile tick_t ticks; +extern uint32_t f_cpu; + +void __attribute__((weak,noinline)) SysTick_Hook(void) { + // Empty function for hooking the systick handler +} + +/* Systick interrupt handler */ +void SysTick_Handler(void) { + ticks++; + SysTick_Hook(); +} + +void timer_init(void) { + /* turn on power to RIT */ + BITBAND(LPC_SC->PCONP, PCRIT) = 1; + + /* clear RIT mask */ + LPC_RIT->RIMASK = 0; //xffffffff; + + /* PCLK = CCLK */ + BITBAND(LPC_SC->PCLKSEL1, 26) = 1; + BITBAND(LPC_SC->PCLKSEL1, PCLK_TIMER3) = 1; + /* enable SysTick */ + SysTick_Config(SysTick->CALIB & SysTick_CALIB_TENMS_Msk); +} + +extern int testval; + +void delay_us(unsigned int time) { + /* Prepare RIT */ + LPC_RIT->RICOUNTER = 0; + LPC_RIT->RICOMPVAL = (CONFIG_CPU_FREQUENCY / 1000000) * time; + LPC_RIT->RICTRL = BV(RITEN) | BV(RITINT); + + /* Wait until RIT signals an interrupt */ + while (!(BITBAND(LPC_RIT->RICTRL, RITINT))) ; + + /* Disable RIT */ + LPC_RIT->RICTRL = 0; +} + +void delay_ms(unsigned int time) { + /* Prepare RIT */ + LPC_RIT->RICOUNTER = 0; + LPC_RIT->RICTRL = BV(RITEN) | BV(RITINT); + LPC_RIT->RICOMPVAL = (f_cpu / 1000) * time; + + /* Wait until RIT signals an interrupt */ + while (!(BITBAND(LPC_RIT->RICTRL, RITINT))) ; + + /* Disable RIT */ + LPC_RIT->RICTRL = 0; +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..75cc3ef --- /dev/null +++ b/src/timer.h @@ -0,0 +1,50 @@ +#ifndef TIMER_H +#define TIMER_H + +#include + +typedef unsigned int tick_t; + +extern volatile tick_t ticks; +#define HZ 100 + +/** + * getticks - return the current system tick count + * + * This inline function returns the current system tick count. + */ +static inline tick_t getticks(void) { + return ticks; +} + +#define MS_TO_TICKS(x) (x/10) + +/* Adapted from Linux 2.6 include/linux/jiffies.h: + * + * These inlines deal with timer wrapping correctly. You are + * strongly encouraged to use them + * 1. Because people otherwise forget + * 2. Because if the timer wrap changes in future you won't have to + * alter your driver code. + * + * time_after(a,b) returns true if the time a is after time b. + * + * Do this with "<0" and ">=0" to only test the sign of the result. A + * good compiler would generate better code (and a really good compiler + * wouldn't care). Gcc is currently neither. + * (">=0" refers to the time_after_eq macro which wasn't copied) + */ +#define time_after(a,b) \ + ((int)(b) - (int)(a) < 0) +#define time_before(a,b) time_after(b,a) + + +void timer_init(void); + +/* delay for "time" microseconds - uses the RIT */ +void delay_us(unsigned int time); + +/* delay for "time" milliseconds - uses the RIT */ +void delay_ms(unsigned int time); + +#endif diff --git a/src/uart.c b/src/uart.c index 8e5331e..5dd1d2e 100644 --- a/src/uart.c +++ b/src/uart.c @@ -5,9 +5,10 @@ */ #include -#include +#include "bits.h" #include "config.h" #include "uart.h" +#include "led.h" /* A few symbols to make this code work for all four UARTs */ #if defined(CONFIG_UART_NUM) && CONFIG_UART_NUM == 0 @@ -51,14 +52,14 @@ static uint8_t uart_lookupratio(float f_fr) { 750,769,778,786,800,818,833,846,857, 867,875,889,900,909,917,923,929,933}; - uint8_t ratios[72]={0x01,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x2f,0x17,0x2d,0x16,0x2b,0x15,0x3e,0x29,0x3d, - 0x14,0x4f,0x3b,0x27,0x3a,0x4d,0x13,0x5e,0x4b, - 0x38,0x5d,0x25,0x5c,0x37,0x49,0x5b,0x6d,0x7f, - 0x12,0x8f,0x7d,0x6b,0x59,0x47,0x7c,0x35,0x8d, - 0x58,0x7b,0x9e,0x23,0x9d,0x7a,0x57,0x8b,0xbf, - 0x34,0xad,0x79,0xbe,0x45,0x9b,0x56,0xbd,0x67, - 0xdf,0x78,0x89,0x9a,0xab,0xbc,0xcd,0xde,0xef}; + uint8_t ratios[72]={0x10,0xf1,0xe1,0xd1,0xc1,0xb1,0xa1,0x91,0x81, + 0xf2,0x71,0xd2,0x61,0xb2,0x51,0xe3,0x92,0xd3, + 0x41,0xf4,0xb3,0x72,0xa3,0xd4,0x31,0xe5,0xb4, + 0x83,0xd5,0x52,0xc5,0x73,0x94,0xb5,0xd6,0xf7, + 0x21,0xf8,0xd7,0xb6,0x95,0x74,0xc7,0x53,0xd8, + 0x85,0xb7,0xe9,0x32,0xd9,0xa7,0x75,0xb8,0xfb, + 0x43,0xda,0x97,0xeb,0x54,0xb9,0x65,0xdb,0x76, + 0xfd,0x87,0x98,0xa9,0xba,0xcb,0xdc,0xed,0xfe}; int fr = (f_fr-1)*1000; int i=0, i_result=0; @@ -78,10 +79,10 @@ static uint8_t uart_lookupratio(float f_fr) { } static uint32_t baud2divisor(unsigned int baudrate) { - float f_ratio; uint32_t int_ratio; uint32_t error; uint32_t dl=0; + float f_ratio; float f_fr; float f_dl; float f_pclk = (float)CONFIG_CPU_FREQUENCY / CONFIG_UART_PCLKDIV; @@ -207,10 +208,11 @@ void uart_init(void) { UART_REGS->DLL = div & 0xff; UART_REGS->DLM = (div >> 8) & 0xff; + BITBAND(UART_REGS->LCR, 7) = 0; + if (div & 0xff0000) { UART_REGS->FDR = (div >> 16) & 0xff; } - BITBAND(UART_REGS->LCR, 7) = 0; /* reset and enable FIFO */ UART_REGS->FCR = BV(0);