diff --git a/src/tests/Makefile b/src/tests/Makefile new file mode 100644 index 0000000..ec89962 --- /dev/null +++ b/src/tests/Makefile @@ -0,0 +1,318 @@ +# Hey Emacs, this is a -*- makefile -*- + +#---------------------------------------------------------------------------- +# WinAVR Makefile Template written by Eric B. Weddington, Joerg Wunsch, et al. +# +# Released to the Public Domain +# +# Additional material for this makefile was written by: +# Peter Fleury +# Tim Henigan +# Colin O'Flynn +# Reiner Patommel +# Markus Pfaff +# Sander Pool +# Frederik Rouleau +# Carlos Lamas +# +# +# Extensively modified for sd2iec and later adapted for ARM by Ingo Korb +# +# To rebuild project do "make clean" then "make all". +#---------------------------------------------------------------------------- + +# Read configuration file +ifdef CONFIG + CONFIGSUFFIX = $(CONFIG:config%=%) +else + CONFIG = config + CONFIGSUFFIX = +endif + +# Enable verbose compilation with "make V=1" +ifdef V + Q := + E := @: +else + Q := @ + E := @echo +endif + +# Include the configuration file +include $(CONFIG) + +# Directory for all generated files +OBJDIR := obj$(CONFIGSUFFIX) + +# Output format. (can be srec, ihex, binary) +FORMAT = binary + +# Linker script +LINKERSCRIPT = lpc1754.ld + +# Target file name (without extension). +TARGET = $(OBJDIR)/sd2snes + + +# List C source files here. (C dependencies are automatically generated.) +SRC = main.c ff.c ccsbcs.c clock.c uart.c power.c led.c timer.c printf.c spi.c fileops.c rtc.c fpga.c fpga_spi.c snes.c smc.c memory.c filetypes.c faulthandler.c sort.c crc32.c cic.c cli.c xmodem.c irq.c rle.c sdnative.c msu1.c crc16.c tests.c + +# usbcontrol.c usb_hid.c usbhw_lpc.c usbinit.c usbstdreq.c + + +# List Assembler source files here. +# Make them always end in a capital .S. Files ending in a lowercase .s +# will not be considered source files but generated files (assembler +# output from the compiler), and will be deleted upon "make clean"! +# 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 crc.S + + +# Optimization level, can be [0, 1, 2, 3, s]. +# 0 = turn off optimization. s = optimize for size. +# (Note: 3 is not always the best optimization level. See avr-libc FAQ.) +# Use s -mcall-prologues when you really need size... +#OPT = 2 +OPT = 2 + +# Debugging format. +DEBUG = dwarf-2 + + +# List any extra directories to look for include files here. +# Each directory must be seperated by a space. +# Use forward slashes for directory separators. +# For a directory that has spaces, enclose it in quotes. +EXTRAINCDIRS = + + +# Compiler flag to set the C Standard level. +# c89 = "ANSI" C +# gnu89 = c89 plus GCC extensions +# c99 = ISO C99 standard (not yet fully implemented) +# gnu99 = c99 plus GCC extensions +CSTANDARD = -std=gnu99 + + +# Place -D or -U options here +CDEFS = -DF_OSC=$(CONFIG_MCU_FOSC)UL + + +# Place -I options here +CINCS = + + +# CPU-specific flags +ifndef CPUFLAGS + CPUFLAGS := -mthumb -mcpu=cortex-m3 +endif + +ifndef ARCH + ARCH := arm-none-eabi +endif + +# Define programs and commands. +# CC must be defined here to generate the correct CFLAGS +SHELL = sh +CC = $(ARCH)-gcc +OBJCOPY = $(ARCH)-objcopy +OBJDUMP = $(ARCH)-objdump +SIZE = $(ARCH)-size +NM = $(ARCH)-nm +REMOVE = rm -f +COPY = cp +AWK = awk + + +#---------------- Compiler Options ---------------- +# -g*: generate debugging information +# -O*: optimization level +# -f...: tuning, see GCC manual and avr-libc documentation +# -Wall...: warning level +# -Wa,...: tell GCC to pass this to the assembler. +# -adhlns...: create assembler listing +CFLAGS = -g$(DEBUG) +CFLAGS += $(CDEFS) $(CINCS) +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 += -I$(OBJDIR) +CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS)) +CFLAGS += $(CSTANDARD) +CFLAGS += -ffunction-sections -fdata-sections + + +#---------------- Assembler Options ---------------- +# -Wa,...: tell GCC to pass this to the assembler. +# -ahlms: create listing +# -gstabs: have the assembler create line number information; note that +# for use in COFF files, additional information about filenames +# and function names needs to be present in the assembler source +# files -- see avr-libc docs [FIXME: not yet described there] +ASFLAGS = $(CPUFLAGS) -Wa,-adhlns=$(OBJDIR)/$(<:.S=.lst),-gstabs -I$(OBJDIR) + + +#---------------- Linker Options ---------------- +# -Wl,...: tell GCC to pass this to linker. +# -Map: create map file +# --cref: add cross reference to map file +LDFLAGS = -Wl,-Map=$(TARGET).map,--cref +LDFLAGS += -T$(LINKERSCRIPT) +LDFLAGS += -Wl,--gc-sections +ifeq ($(CONFIG_LINKER_RELAX),y) + LDFLAGS += -Wl,-O9,--relax +endif + + +#============================================================================ + + +# De-dupe the list of C source files +CSRC := $(sort $(SRC)) + +# Define all object files. +OBJ := $(patsubst %,$(OBJDIR)/%,$(CSRC:.c=.o) $(ASRC:.S=.o)) + +# Define all listing files. +LST := $(patsubst %,$(OBJDIR)/%,$(CSRC:.c=.lst) $(ASRC:.S=.lst)) + + +# Compiler flags to generate dependency files. +GENDEPFLAGS = -MMD -MP -MF .dep/$(@F).d + + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = -I. $(CFLAGS) $(GENDEPFLAGS) +ALL_ASFLAGS = -I. -x assembler-with-cpp $(ASFLAGS) $(CDEFS) + + + + + +# Default target. +all: build + +build: elf bin hex + $(E) " SIZE $(TARGET).elf" + $(Q)$(ELFSIZE)|grep -v debug + cp $(TARGET).bin $(OBJDIR)/firmware.img + utils/genhdr $(OBJDIR)/firmware.img SNSD $(CONFIG_FWVER) + +elf: $(TARGET).elf +bin: $(TARGET).bin +hex: $(TARGET).hex +eep: $(TARGET).eep +lss: $(TARGET).lss +sym: $(TARGET).sym + +# # A little helper target for the maintainer =) +# copy2card: +# cp $(TARGET).bin /mbed/hw_LPC1768.bin + + +program: build + utils/lpcchksum $(TARGET).bin + openocd -f openocd-usb.cfg -f lpc1754.cfg -f flash.cfg + +debug: build + openocd -f openocd-usb.cfg -f lpc1754.cfg + +reset: + openocd -f openocd-usb.cfg -f lpc1754.cfg -f reset.cfg + +# Display size of file. +HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex +ELFSIZE = $(SIZE) -A $(TARGET).elf + + + +# Generate autoconf.h from config +.PRECIOUS : $(OBJDIR)/autoconf.h +$(OBJDIR)/autoconf.h: $(CONFIG) | $(OBJDIR) + $(E) " CONF2H $(CONFIG)" + $(Q)$(AWK) -f conf2h.awk $(CONFIG) > $(OBJDIR)/autoconf.h + +# Create final output files from ELF output file. +$(OBJDIR)/%.bin: $(OBJDIR)/%.elf + $(E) " BIN $@" + $(Q)$(OBJCOPY) -O binary $< $@ + +$(OBJDIR)/%.hex: $(OBJDIR)/%.elf + $(E) " HEX $@" + $(Q)$(OBJCOPY) -O $(FORMAT) $< $@ + +# Create extended listing file from ELF output file. +$(OBJDIR)/%.lss: $(OBJDIR)/%.elf + $(E) " LSS $<" + $(Q)$(OBJDUMP) -h -S $< > $@ + +# Create a symbol table from ELF output file. +$(OBJDIR)/%.sym: $(OBJDIR)/%.elf + $(E) " SYM $<" + $(E)$(NM) -n $< > $@ + + +# Link: create ELF output file from object files. +.SECONDARY : $(TARGET).elf +.PRECIOUS : $(OBJ) +$(TARGET).elf : $(OBJ) + $(E) " LINK $@" + $(Q)$(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS) + + +# Compile: create object files from C source files. +$(OBJDIR)/%.o : %.c | $(OBJDIR) $(OBJDIR)/autoconf.h + $(E) " CC $<" + $(Q)$(CC) -c $(ALL_CFLAGS) $< -o $@ + + +# Compile: create assembler files from C source files. +$(OBJDIR)/%.s : %.c | $(OBJDIR) $(OBJDIR)/autoconf.h + $(CC) -S $(ALL_CFLAGS) $< -o $@ + + +# Assemble: create object files from assembler source files. +$(OBJDIR)/%.o : %.S | $(OBJDIR) $(OBJDIR)/autoconf.h + $(E) " AS $<" + $(Q)$(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Create preprocessed source for use in sending a bug report. +$(OBJDIR)/%.i : %.c | $(OBJDIR) $(OBJDIR)/autoconf.h + $(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@ + +# Create the output directory +$(OBJDIR) : + $(E) " MKDIR $(OBJDIR)" + $(Q)mkdir $(OBJDIR) + +# Target: clean project. +clean: begin clean_list end + +clean_list : + $(E) " CLEAN" + $(Q)$(REMOVE) $(TARGET).hex + $(Q)$(REMOVE) $(TARGET).bin + $(Q)$(REMOVE) $(TARGET).elf + $(Q)$(REMOVE) $(TARGET).map + $(Q)$(REMOVE) $(TARGET).sym + $(Q)$(REMOVE) $(TARGET).lss + $(Q)$(REMOVE) $(OBJ) + $(Q)$(REMOVE) $(OBJDIR)/autoconf.h + $(Q)$(REMOVE) $(OBJDIR)/*.bin + $(Q)$(REMOVE) $(LST) + $(Q)$(REMOVE) $(CSRC:.c=.s) + $(Q)$(REMOVE) $(CSRC:.c=.d) + $(Q)$(REMOVE) .dep/* + -$(Q)rmdir $(OBJDIR) + +# Include the dependency files. +-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*) + +# Listing of phony targets. +.PHONY : all begin finish end sizebefore sizeafter \ +build elf hex lss sym clean clean_list diff --git a/src/tests/bits.h b/src/tests/bits.h new file mode 100644 index 0000000..5055737 --- /dev/null +++ b/src/tests/bits.h @@ -0,0 +1,21 @@ +#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) \ + ))) + +#define BITBAND_OFF(addr,offset,bit) \ + (*((volatile unsigned long *)( \ + (((unsigned long)&(addr) + offset) & 0x01ffffff)*32 + \ + (bit)*4 + 0x02000000 + (((unsigned long)&(addr) + offset) & 0xfe000000) \ + ))) + + +#endif diff --git a/src/tests/ccsbcs.c b/src/tests/ccsbcs.c new file mode 100644 index 0000000..eb0ab4e --- /dev/null +++ b/src/tests/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/tests/cic.c b/src/tests/cic.c new file mode 100644 index 0000000..20c7628 --- /dev/null +++ b/src/tests/cic.c @@ -0,0 +1,76 @@ +#include +#include "bits.h" +#include "config.h" +#include "uart.h" +#include "cic.h" + +char *cicstatenames[4] = { "CIC_OK", "CIC_FAIL", "CIC_PAIR", "CIC_SCIC" }; + +void print_cic_state() { + printf("CIC state: %s\n", get_cic_statename(get_cic_state())); +} + +inline char *get_cic_statename(enum cicstates state) { + return cicstatenames[state]; +} + +enum cicstates get_cic_state() { + uint32_t count; + uint32_t togglecount = 0; + uint8_t state, state_old; + + state_old = BITBAND(SNES_CIC_STATUS_REG->FIOPIN, SNES_CIC_STATUS_BIT); +/* this loop samples at ~10MHz */ + for(count=0; countFIOPIN, SNES_CIC_STATUS_BIT); + if(state != state_old) { + togglecount++; + } + state_old = state; + } +/* CIC_TOGGLE_THRESH_PAIR > CIC_TOGGLE_THRESH_SCIC */ + if(togglecount > CIC_TOGGLE_THRESH_PAIR) { + return CIC_PAIR; + } else if(togglecount > CIC_TOGGLE_THRESH_SCIC) { + return CIC_SCIC; + } else if(state) { + return CIC_OK; + } else return CIC_FAIL; +} + +void cic_init(int allow_pairmode) { + BITBAND(SNES_CIC_PAIR_REG->FIODIR, SNES_CIC_PAIR_BIT) = 1; + if(allow_pairmode) { + BITBAND(SNES_CIC_PAIR_REG->FIOCLR, SNES_CIC_PAIR_BIT) = 1; + } else { + BITBAND(SNES_CIC_PAIR_REG->FIOSET, SNES_CIC_PAIR_BIT) = 1; + } + BITBAND(SNES_CIC_D0_REG->FIODIR, SNES_CIC_D0_BIT) = 0; + BITBAND(SNES_CIC_D1_REG->FIODIR, SNES_CIC_D1_BIT) = 0; +} + +/* prepare GPIOs for pair mode + set initial modes */ +void cic_pair(int init_vmode, int init_d4) { + cic_videomode(init_vmode); + cic_d4(init_d4); + + BITBAND(SNES_CIC_D0_REG->FIODIR, SNES_CIC_D0_BIT) = 1; + BITBAND(SNES_CIC_D1_REG->FIODIR, SNES_CIC_D1_BIT) = 1; +} + +void cic_videomode(int value) { + if(value) { + BITBAND(SNES_CIC_D0_REG->FIOSET, SNES_CIC_D0_BIT) = 1; + } else { + BITBAND(SNES_CIC_D0_REG->FIOCLR, SNES_CIC_D0_BIT) = 1; + } +} + +void cic_d4(int value) { + if(value) { + BITBAND(SNES_CIC_D1_REG->FIOSET, SNES_CIC_D1_BIT) = 1; + } else { + BITBAND(SNES_CIC_D1_REG->FIOCLR, SNES_CIC_D1_BIT) = 1; + } +} + diff --git a/src/tests/cic.h b/src/tests/cic.h new file mode 100644 index 0000000..03e0648 --- /dev/null +++ b/src/tests/cic.h @@ -0,0 +1,23 @@ +#ifndef _CIC_H +#define _CIC_H + +#define CIC_SAMPLECOUNT (100000) +#define CIC_TOGGLE_THRESH_PAIR (2500) +#define CIC_TOGGLE_THRESH_SCIC (10) + +#include +#include "bits.h" + +enum cicstates { CIC_OK = 0, CIC_FAIL, CIC_PAIR, CIC_SCIC }; +enum cic_region { CIC_NTSC = 0, CIC_PAL }; + +void print_cic_state(void); +char *get_cic_statename(enum cicstates state); +enum cicstates get_cic_state(void); +void cic_init(int allow_pairmode); + +void cic_pair(int init_vmode, int init_d4); +void cic_videomode(int value); +void cic_d4(int value); + +#endif diff --git a/src/tests/cli.c b/src/tests/cli.c new file mode 100644 index 0000000..57af456 --- /dev/null +++ b/src/tests/cli.c @@ -0,0 +1,576 @@ +/* tapplay - TAP file playback for sd2iec hardware + Copyright (C) 2009 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 + + + cli.c: The command line interface + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "diskio.h" +#include "ff.h" +#include "timer.h" +#include "uart.h" +#include "fileops.h" +#include "memory.h" +#include "snes.h" +#include "fpga.h" +#include "fpga_spi.h" +#include "cic.h" +#include "xmodem.h" +#include "rtc.h" + +#include "cli.h" + +#define MAX_LINE 250 + +/* Variables */ +static char cmdbuffer[MAX_LINE+1]; +static char *curchar; + +/* Word lists */ +static char command_words[] = + "cd\0reset\0sreset\0dir\0ls\0test\0resume\0loadrom\0loadraw\0saveraw\0put\0rm\0d4\0vmode\0mapper\0settime\0time\0setfeature\0hexdump\0w8\0w16\0memsel\0"; +enum { CMD_CD = 0, CMD_RESET, CMD_SRESET, CMD_DIR, CMD_LS, CMD_TEST, CMD_RESUME, CMD_LOADROM, CMD_LOADRAW, CMD_SAVERAW, CMD_PUT, CMD_RM, CMD_D4, CMD_VMODE, CMD_MAPPER, CMD_SETTIME, CMD_TIME, CMD_SETFEATURE, CMD_HEXDUMP, CMD_W8, CMD_W16, CMD_MEMSEL }; + +/* ------------------------------------------------------------------------- */ +/* Parse functions */ +/* ------------------------------------------------------------------------- */ + +/* Skip spaces at curchar */ +static uint8_t skip_spaces(void) { + uint8_t res = (*curchar == ' ' || *curchar == 0); + + while (*curchar == ' ') + curchar++; + + return res; +} + +/* Parse the string in curchar for an integer with bounds [lower,upper] */ +static int32_t parse_unsigned(uint32_t lower, uint32_t upper, uint8_t base) { + char *end; + uint32_t result; + + if (strlen(curchar) == 1 && *curchar == '?') { + printf("Number between %ld[0x%lx] and %ld[0x%lx] expected\n",lower,lower,upper,upper); + return -2; + } + + result = strtoul(curchar, &end, base); + if ((*end != ' ' && *end != 0) || errno != 0) { + printf("Invalid numeric argument\n"); + return -1; + } + + curchar = end; + skip_spaces(); + + if (result < lower || result > upper) { + printf("Numeric argument out of range (%ld..%ld)\n",lower,upper); + return -1; + } + + return result; +} +/* Parse the string starting with curchar for a word in wordlist */ +static int8_t parse_wordlist(char *wordlist) { + uint8_t i, matched; + char *cur, *ptr; + char c; + + i = 0; + ptr = wordlist; + + // Command list on "?" + if (strlen(curchar) == 1 && *curchar == '?') { + printf("Commands available: \n "); + while (1) { + c = *ptr++; + if (c == 0) { + if (*ptr == 0) { + printf("\n"); + return -2; + } else { + printf("\n "); + } + } else + uart_putc(c); + } + } + + while (1) { + cur = curchar; + matched = 1; + c = *ptr; + do { + // If current word list character is \0: No match found + if (c == 0) { + printf("Unknown word: %s\n",curchar); + return -1; + } + + if (tolower(c) != tolower(*cur)) { + // Check for end-of-word + if (cur != curchar && (*cur == ' ' || *cur == 0)) { + // Partial match found, return that + break; + } else { + matched = 0; + break; + } + } + ptr++; + cur++; + c = *ptr; + } while (c != 0); + + if (matched) { + char *tmp = curchar; + + curchar = cur; + // Return match only if whitespace or end-of-string follows + // (avoids mismatching partial words) + if (skip_spaces()) { + return i; + } else { + printf("Unknown word: %s\n(use ? for help)\n",tmp); + return -1; + } + } else { + // Try next word in list + i++; + while (*ptr++ != 0) ; + } + } +} + +/* Read a line from serial, uses cmdbuffer as storage */ +static char *getline(char *prompt) { + int i=0; + char c; + + printf("\n%s",prompt); + memset(cmdbuffer,0,sizeof(cmdbuffer)); + + while (1) { + c = uart_getc(); + if (c == 13) + break; + + if (c == 27 || c == 3) { + printf("\\\n%s",prompt); + i = 0; + memset(cmdbuffer,0,sizeof(cmdbuffer)); + continue; + } + + if (c == 127 || c == 8) { + if (i > 0) { + i--; + uart_putc(8); // backspace + uart_putc(' '); // erase character + uart_putc(8); // backspace + } else + continue; + } else { + if (i < sizeof(cmdbuffer)-1) { + cmdbuffer[i++] = c; + uart_putc(c); + } + } + } + cmdbuffer[i] = 0; + return cmdbuffer; +} + + +/* ------------------------------------------------------------------------- */ +/* Command functions */ +/* ------------------------------------------------------------------------- */ + +/* Reset */ +static void cmd_reset(void) { + /* force watchdog reset */ + LPC_WDT->WDTC = 256; // minimal timeout + LPC_WDT->WDCLKSEL = BV(31); // internal RC, lock register + LPC_WDT->WDMOD = BV(0) | BV(1); // enable watchdog and reset-by-watchdog + LPC_WDT->WDFEED = 0xaa; + LPC_WDT->WDFEED = 0x55; // initial feed to really enable WDT +} + +/* Show the contents of the current directory */ +static void cmd_show_directory(void) { + FRESULT res; + DIR dh; + FILINFO finfo; + uint8_t *name; + + f_getcwd((TCHAR*)file_lfn, 255); + + res = f_opendir(&dh, (TCHAR*)file_lfn); + if (res != FR_OK) { + printf("f_opendir failed, result %d\n",res); + return; + } + + finfo.lfname = (TCHAR*)file_lfn; + finfo.lfsize = 255; + + do { + /* Read the next entry */ + res = f_readdir(&dh, &finfo); + if (res != FR_OK) { + printf("f_readdir failed, result %d\n",res); + return; + } + + /* Abort if none was found */ + if (!finfo.fname[0]) + break; + + /* Skip volume labels */ + if (finfo.fattrib & AM_VOL) + continue; + + /* Select between LFN and 8.3 name */ + if (finfo.lfname[0]) + name = (uint8_t*)finfo.lfname; + else { + name = (uint8_t*)finfo.fname; + strlwr((char *)name); + } + + printf("%s",name); + + /* Directory indicator (Unix-style) */ + if (finfo.fattrib & AM_DIR) + uart_putc('/'); + + printf("\n"); + } while (finfo.fname[0]); +} + + +static void cmd_loadrom(void) { + uint32_t address = 0; + uint8_t flags = LOADROM_WITH_SRAM | LOADROM_WITH_RESET; + load_rom((uint8_t*)curchar, address, flags); +} + +static void cmd_loadraw(void) { + uint32_t address = parse_unsigned(0,16777216,16); + load_sram((uint8_t*)curchar, address); +} + +static void cmd_saveraw(void) { + uint32_t address = parse_unsigned(0,16777216,16); + uint32_t length = parse_unsigned(0,16777216,16); + save_sram((uint8_t*)curchar, length, address); +} + +static void cmd_d4(void) { + int32_t hz; + + if(get_cic_state() != CIC_PAIR) { + printf("not in pair mode\n"); + } else { + hz = parse_unsigned(50,60,10); + if(hz==50) { + cic_d4(CIC_PAL); + } else { + cic_d4(CIC_NTSC); + } + printf("ok\n"); + } +} + +static void cmd_vmode(void) { + int32_t hz; + if(get_cic_state() != CIC_PAIR) { + printf("not in pair mode\n"); + } else { + hz = parse_unsigned(50,60,10); + if(hz==50) { + cic_videomode(CIC_PAL); + } else { + cic_videomode(CIC_NTSC); + } + printf("ok\n"); + } +} + +void cmd_put(void) { + if(*curchar != 0) { + file_open((uint8_t*)curchar, FA_CREATE_ALWAYS | FA_WRITE); + if(file_res) { + printf("FAIL: error opening file %s\n", curchar); + } else { + printf("OK, start xmodem transfer now.\n"); + xmodem_rxfile(&file_handle); + } + file_close(); + } else { + printf("Usage: put \n"); + } +} + +void cmd_rm(void) { + FRESULT res = f_unlink(curchar); + if(res) printf("Error %d removing %s\n", res, curchar); +} + +void cmd_mapper(void) { + int32_t mapper; + mapper = parse_unsigned(0,7,10); + set_mapper((uint8_t)mapper & 0x7); + printf("mapper set to %ld\n", mapper); +} + +void cmd_sreset(void) { + if(*curchar != 0) { + int32_t resetstate; + resetstate = parse_unsigned(0,1,10); + snes_reset(resetstate); + } else { + snes_reset(1); + delay_ms(20); + snes_reset(0); + } +} +void cmd_settime(void) { + struct tm time; + if(strlen(curchar) != 4+2+2 + 2+2+2) { + printf("invalid time format (need YYYYMMDDhhmmss)\n"); + } else { + time.tm_sec = atoi(curchar+4+2+2+2+2); + curchar[4+2+2+2+2] = 0; + time.tm_min = atoi(curchar+4+2+2+2); + curchar[4+2+2+2] = 0; + time.tm_hour = atoi(curchar+4+2+2); + curchar[4+2+2] = 0; + time.tm_mday = atoi(curchar+4+2); + curchar[4+2] = 0; + time.tm_mon = atoi(curchar+4); + curchar[4] = 0; + time.tm_year = atoi(curchar); + set_rtc(&time); + } +} + +void cmd_time(void) { + struct tm time; + read_rtc(&time); + printf("%04d-%02d-%02d %02d:%02d:%02d\n", time.tm_year, time.tm_mon, + time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); +} + +void cmd_setfeature(void) { + uint8_t feat = parse_unsigned(0, 255, 16); + fpga_set_features(feat); +} + +void cmd_hexdump(void) { + uint32_t offset = parse_unsigned(0, 16777215, 16); + uint32_t len = parse_unsigned(0, 16777216, 16); + sram_hexdump(offset, len); +} + +void cmd_w8(void) { + uint32_t offset = parse_unsigned(0, 16777215, 16); + uint8_t val = parse_unsigned(0, 255, 16); + sram_writebyte(val, offset); +} + +void cmd_w16(void) { + uint32_t offset = parse_unsigned(0, 16777215, 16); + uint16_t val = parse_unsigned(0, 65535, 16); + sram_writeshort(val, offset); +} + +void cmd_memsel(void) { + uint8_t unit = parse_unsigned(0,1,10); + fpga_select_mem(unit); +} +/* ------------------------------------------------------------------------- */ +/* CLI interface functions */ +/* ------------------------------------------------------------------------- */ + +void cli_init(void) { +} + +void cli_entrycheck() { + if(uart_gotc() && uart_getc() == 27) { + printf("*** BREAK\n"); + cli_loop(); + } +} + +void cli_loop(void) { + snes_reset(1); + while (1) { + curchar = getline(">"); + printf("\n"); + + /* Process medium changes before executing the command */ + if (disk_state != DISK_OK && disk_state != DISK_REMOVED) { + FRESULT res; + + printf("Medium changed... "); + res = f_mount(0,&fatfs); + if (res != FR_OK) { + printf("Failed to mount new medium, result %d\n",res); + } else { + printf("Ok\n"); + } + + } + + /* Remove whitespace */ + while (*curchar == ' ') curchar++; + while (strlen(curchar) > 0 && curchar[strlen(curchar)-1] == ' ') + curchar[strlen(curchar)-1] = 0; + + /* Ignore empty lines */ + if (strlen(curchar) == 0) + continue; + + /* Parse command */ + int8_t command = parse_wordlist(command_words); + if (command < 0) + continue; + + + FRESULT res; + switch (command) { + case CMD_CD: +#if _FS_RPATH + if (strlen(curchar) == 0) { + f_getcwd((TCHAR*)file_lfn, 255); + printf("%s\n",file_lfn); + break; + } + + res = f_chdir((const TCHAR *)curchar); + if (res != FR_OK) { + printf("chdir %s failed with result %d\n",curchar,res); + } else { + printf("Ok.\n"); + } +#else + printf("cd not supported.\n"); + res; +#endif + break; + case CMD_RESET: + cmd_reset(); + break; + + case CMD_SRESET: + cmd_sreset(); + break; + + case CMD_DIR: + case CMD_LS: + cmd_show_directory(); + break; + + case CMD_RESUME: + return; + break; + + case CMD_LOADROM: + cmd_loadrom(); + break; + + case CMD_LOADRAW: + cmd_loadraw(); + break; + + case CMD_SAVERAW: + cmd_saveraw(); + break; + + case CMD_RM: + cmd_rm(); + break; + + case CMD_D4: + cmd_d4(); + break; + + case CMD_VMODE: + cmd_vmode(); + break; + + case CMD_PUT: + cmd_put(); + break; + + case CMD_MAPPER: + cmd_mapper(); + break; + + case CMD_SETTIME: + cmd_settime(); + break; + + case CMD_TIME: + cmd_time(); + break; + + case CMD_TEST: + testbattery(); + break; + + case CMD_SETFEATURE: + cmd_setfeature(); + break; + + case CMD_HEXDUMP: + cmd_hexdump(); + break; + + case CMD_W8: + cmd_w8(); + break; + + case CMD_W16: + cmd_w16(); + break; + + case CMD_MEMSEL: + cmd_memsel(); + break; + + } + } +} diff --git a/src/tests/cli.h b/src/tests/cli.h new file mode 100644 index 0000000..69c2276 --- /dev/null +++ b/src/tests/cli.h @@ -0,0 +1,34 @@ +/* tapplay - TAP file playback for sd2iec hardware + Copyright (C) 2009 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 + + + cli.h: Definitions for cli.c + +*/ + +#ifndef CLI_H +#define CLI_H + +void cli_init(void); +void cli_loop(void); +void cli_entrycheck(void); + +#endif diff --git a/src/tests/clock.c b/src/tests/clock.c new file mode 100644 index 0000000..b032e6f --- /dev/null +++ b/src/tests/clock.c @@ -0,0 +1,109 @@ +/* ___DISCLAIMER___ */ + +/* clock.c: PLL, CCLK, PCLK controls */ + +#include +#include "clock.h" +#include "bits.h" +#include "uart.h" + +void clock_disconnect() { + disconnectPLL0(); + disablePLL0(); +} + +void clock_init() { + +/* set flash access time to 5 clks (80 DAC freq = 44099.5 Hz + -> FPGA freq = 11289473.7Hz + First, disable and disconnect PLL0. +*/ +// 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 +*/ + + +/* continue with PLL0 setup: + enable the xtal oscillator and wait for it to become stable + set the oscillator as clk source for PLL0 + set PLL0 multiplier+predivider + enable PLL0 + set CCLK divider + wait for PLL0 to lock + connect PLL0 + done + */ + enableMainOsc(); + setClkSrc(CLKSRC_MAINOSC); +// XXX setPLL0MultPrediv(429, 19); +// XXX setPLL0MultPrediv(23, 2); + setPLL0MultPrediv(12, 1); + enablePLL0(); + setCCLKDiv(3); + connectPLL0(); +} + +void setFlashAccessTime(uint8_t clocks) { + LPC_SC->FLASHCFG=FLASHTIM(clocks); +} + +void setPLL0MultPrediv(uint16_t mult, uint8_t prediv) { + LPC_SC->PLL0CFG=PLL_MULT(mult) | PLL_PREDIV(prediv); + PLL0feed(); +} + +void enablePLL0() { + LPC_SC->PLL0CON |= PLLE0; + PLL0feed(); +} + +void disablePLL0() { + LPC_SC->PLL0CON &= ~PLLE0; + PLL0feed(); +} + +void connectPLL0() { + while(!(LPC_SC->PLL0STAT&PLOCK0)); + LPC_SC->PLL0CON |= PLLC0; + PLL0feed(); +} + +void disconnectPLL0() { + LPC_SC->PLL0CON &= ~PLLC0; + PLL0feed(); +} + +void setCCLKDiv(uint8_t div) { + LPC_SC->CCLKCFG=CCLK_DIV(div); +} + +void enableMainOsc() { + LPC_SC->SCS=OSCEN; + while(!(LPC_SC->SCS&OSCSTAT)); +} + +void disableMainOsc() { + LPC_SC->SCS=0; +} + +void PLL0feed() { + LPC_SC->PLL0FEED=0xaa; + LPC_SC->PLL0FEED=0x55; +} + +void setClkSrc(uint8_t src) { + LPC_SC->CLKSRCSEL=src; +} diff --git a/src/tests/clock.h b/src/tests/clock.h new file mode 100644 index 0000000..56be21a --- /dev/null +++ b/src/tests/clock.h @@ -0,0 +1,79 @@ +#ifndef _CLOCK_H +#define _CLOCK_H + +#define PLL_MULT(x) ((x-1)&0x7fff) +#define PLL_PREDIV(x) (((x-1)<<16)&0xff0000) +#define CCLK_DIV(x) ((x-1)&0xff) +#define CLKSRC_MAINOSC (1) +#define PLLE0 (1<<0) +#define PLLC0 (1<<1) +#define PLOCK0 (1<<26) +#define OSCEN (1<<5) +#define OSCSTAT (1<<6) +#define FLASHTIM(x) (((x-1)<<12)|0x3A) + +#define PCLK_CCLK(x) (1<<(x)) +#define PCLK_CCLK4(x) (0) +#define PCLK_CCLK8(x) (3<<(x)) +#define PCLK_CCLK2(x) (2<<(x)) + +/* shift values for use with PCLKSEL0 */ +#define PCLK_WDT (0) +#define PCLK_TIMER0 (2) +#define PCLK_TIMER1 (4) +#define PCLK_UART0 (6) +#define PCLK_UART1 (8) +#define PCLK_PWM1 (12) +#define PCLK_I2C0 (14) +#define PCLK_SPI (16) +#define PCLK_SSP1 (20) +#define PCLK_DAC (22) +#define PCLK_ADC (24) +#define PCLK_CAN1 (26) +#define PCLK_CAN2 (28) +#define PCLK_ACF (30) + +/* shift values for use with PCLKSEL1 */ +#define PCLK_QEI (0) +#define PCLK_GPIOINT (2) +#define PCLK_PCB (4) +#define PCLK_I2C1 (6) +#define PCLK_SSP0 (10) +#define PCLK_TIMER2 (12) +#define PCLK_TIMER3 (14) +#define PCLK_UART2 (16) +#define PCLK_UART3 (18) +#define PCLK_I2C2 (20) +#define PCLK_I2S (22) +#define PCLK_RIT (26) +#define PCLK_SYSCON (28) +#define PCLK_MC (30) + +void clock_disconnect(void); + +void clock_init(void); + +void setFlashAccessTime(uint8_t clocks); + +void setPLL0MultPrediv(uint16_t mult, uint8_t prediv); + +void enablePLL0(void); + +void disablePLL0(void); + +void connectPLL0(void); + +void disconnectPLL0(void); + +void setCCLKDiv(uint8_t div); + +void enableMainOsc(void); + +void disableMainOsc(void); + +void PLL0feed(void); + +void setClkSrc(uint8_t src); + + +#endif diff --git a/src/tests/conf2h.awk b/src/tests/conf2h.awk new file mode 100644 index 0000000..3818f89 --- /dev/null +++ b/src/tests/conf2h.awk @@ -0,0 +1,29 @@ +#! /usr/bin/gawk -f + +# Trivial little script to convert from a makefile-style configuration +# file to a C header. No copyright claimed. + +BEGIN { + print "// autoconf.h generated from " ARGV[1] " at " strftime() "\n" \ + "#ifndef AUTOCONF_H\n" \ + "#define AUTOCONF_H" +} + +/^#/ { sub(/^#/,"//") } + +/^CONFIG_.*=/ { + if (/=n$/) { + sub(/^/,"// "); + } else { + sub(/^/,"#define ") + if (/=y$/) { + sub(/=.*$/,"") + } else { + sub(/=/," ") + } + } +} + +{ print } + +END { print "#endif" } diff --git a/src/tests/config b/src/tests/config new file mode 100644 index 0000000..8332a4f --- /dev/null +++ b/src/tests/config @@ -0,0 +1,4 @@ +CONFIG_VERSION=0.0.1 +CONFIG_FWVER=16777214 +#CONFIG_FWVER=1146310227 +CONFIG_MCU_FOSC=12000000 diff --git a/src/tests/config.h b/src/tests/config.h new file mode 100644 index 0000000..6900ac2 --- /dev/null +++ b/src/tests/config.h @@ -0,0 +1,97 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +// #define DEBUG_FS +// #define DEBUG_SD +// #define DEBUG_IRQ +// #define DEBUG_MSU1 + +#define VER "0.0.1(NSFW)" +#define IN_AHBRAM __attribute__ ((section(".ahbram"))) + +#define SD_DT_INT_SETUP() do {\ + BITBAND(LPC_GPIOINT->IO2IntEnR, SD_DT_BIT) = 1;\ + BITBAND(LPC_GPIOINT->IO2IntEnF, SD_DT_BIT) = 1;\ + } while(0) + +#define SD_CHANGE_DETECT (BITBAND(LPC_GPIOINT->IO2IntStatR, SD_DT_BIT)\ + |BITBAND(LPC_GPIOINT->IO2IntStatF, SD_DT_BIT)) + +#define SD_CHANGE_CLR() do {LPC_GPIOINT->IO2IntClr = BV(SD_DT_BIT);} while(0) + +#define SD_DT_REG LPC_GPIO0 +#define SD_DT_BIT 8 +#define SD_WP_REG LPC_GPIO0 +#define SD_WP_BIT 6 + +#define SDCARD_DETECT (!(BITBAND(SD_DT_REG->FIOPIN, SD_DT_BIT))) +#define SDCARD_WP (BITBAND(SD_WP_REG->FIOPIN, SD_WP_BIT)) +#define SD_SUPPLY_VOLTAGE (1L<<21) /* 3.3V - 3.4V */ +#define CONFIG_SD_BLOCKTRANSFER 1 +#define CONFIG_SD_AUTO_RETRIES 10 +// #define SD_CHANGE_VECT +// #define CONFIG_SD_DATACRC 1 + +#define CONFIG_UART_NUM 3 +// #define CONFIG_CPU_FREQUENCY 90315789 +#define CONFIG_CPU_FREQUENCY 96000000 +//#define CONFIG_CPU_FREQUENCY 46000000 +#define CONFIG_UART_PCLKDIV 1 +#define CONFIG_UART_TX_BUF_SHIFT 8 +#define CONFIG_UART_BAUDRATE 921600 +#define CONFIG_UART_DEADLOCKABLE + +#define SSP_CLK_DIVISOR_FAST 2 +#define SSP_CLK_DIVISOR_SLOW 250 + +#define SSP_CLK_DIVISOR_FPGA_FAST 6 +#define SSP_CLK_DIVISOR_FPGA_SLOW 20 + +#define SNES_RESET_REG LPC_GPIO1 +#define SNES_RESET_BIT 26 + +#define SNES_CIC_D0_REG LPC_GPIO0 +#define SNES_CIC_D0_BIT 1 + +#define SNES_CIC_D1_REG LPC_GPIO0 +#define SNES_CIC_D1_BIT 0 + +#define SNES_CIC_STATUS_REG LPC_GPIO1 +#define SNES_CIC_STATUS_BIT 29 + +#define SNES_CIC_PAIR_REG LPC_GPIO1 +#define SNES_CIC_PAIR_BIT 25 + +#define FPGA_MCU_RDY_REG LPC_GPIO2 +#define FPGA_MCU_RDY_BIT 9 + +#define QSORT_MAXELEM 2048 + +#define SSP_REGS LPC_SSP0 +#define SSP_PCLKREG PCLKSEL1 +// 1: PCLKSEL0 +#define SSP_PCLKBIT 10 +// 1: 20 +#define SSP_DMAID_TX 0 +// 1: 2 +#define SSP_DMAID_RX 1 +// 1: 3 +#define SSP_DMACH LPC_GPDMACH0 + +#define SD_CLKREG LPC_GPIO0 +#define SD_CMDREG LPC_GPIO0 +#define SD_DAT0REG LPC_GPIO2 +#define SD_DAT1REG LPC_GPIO2 +#define SD_DAT2REG LPC_GPIO2 +#define SD_DAT3REG LPC_GPIO2 + +#define SD_CLKPIN (7) +#define SD_CMDPIN (9) +#define SD_DAT0PIN (0) +#define SD_DAT1PIN (1) +#define SD_DAT2PIN (2) +#define SD_DAT3PIN (3) + +#define SD_DAT (LPC_GPIO2->FIOPIN0) + +#endif diff --git a/src/tests/crc.S b/src/tests/crc.S new file mode 100644 index 0000000..38a7479 --- /dev/null +++ b/src/tests/crc.S @@ -0,0 +1,92 @@ +/* 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/tests/crc.h b/src/tests/crc.h new file mode 100644 index 0000000..34e0053 --- /dev/null +++ b/src/tests/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/tests/crc32.c b/src/tests/crc32.c new file mode 100644 index 0000000..7928cf2 --- /dev/null +++ b/src/tests/crc32.c @@ -0,0 +1,110 @@ +/** + * \file crc32.c + * Functions and types for CRC checks. + * + * Generated on Sat Sep 25 18:06:34 2010, + * by pycrc v0.7.1, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 32 + * Poly = 0x04c11db7 + * XorIn = 0xffffffff + * ReflectIn = True + * XorOut = 0xffffffff + * ReflectOut = True + * Algorithm = table-driven + * Direct = True + *****************************************************************************/ +#include "crc32.h" +#include "config.h" +#include +#include + +/** + * Static table used for the table_driven implementation. + *****************************************************************************/ +static const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +/** + * Update the crc value with new data. + * + * \param crc The current crc value. + * \param data Pointer to a buffer of \a data_len bytes. + * \param data_len Number of bytes in the \a data buffer. + * \return The updated crc value. + *****************************************************************************/ +uint32_t crc32_update(uint32_t crc, const unsigned char data) +{ + unsigned int tbl_idx; + + tbl_idx = (crc ^ data) & 0xff; + crc = (crc32_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff; + + return crc & 0xffffffff; +} + + diff --git a/src/tests/crc32.h b/src/tests/crc32.h new file mode 100644 index 0000000..9da3054 --- /dev/null +++ b/src/tests/crc32.h @@ -0,0 +1,68 @@ +/** + * \file crc32.h + * Functions and types for CRC checks. + * + * Generated on Sat Sep 25 18:06:37 2010, + * by pycrc v0.7.1, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 32 + * Poly = 0x04c11db7 + * XorIn = 0xffffffff + * ReflectIn = True + * XorOut = 0xffffffff + * ReflectOut = True + * Algorithm = table-driven + * Direct = True + *****************************************************************************/ +#ifndef __CRC___H__ +#define __CRC___H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The definition of the used algorithm. + *****************************************************************************/ +#define CRC_ALGO_TABLE_DRIVEN 1 + +/** + * Calculate the initial crc value. + * + * \return The initial crc value. + *****************************************************************************/ +static inline uint32_t crc_init(void) +{ + return 0xffffffff; +} + +/** + * Update the crc value with new data. + * + * \param crc The current crc value. + * \param data Pointer to a buffer of \a data_len bytes. + * \param data_len Number of bytes in the \a data buffer. + * \return The updated crc value. + *****************************************************************************/ +uint32_t crc32_update(uint32_t crc, const unsigned char data); + +/** + * Calculate the final crc value. + * + * \param crc The current crc value. + * \return The final crc value. + *****************************************************************************/ +static inline uint32_t crc32_finalize(uint32_t crc) +{ + return crc ^ 0xffffffff; +} + + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif + +#endif /* __CRC___H__ */ diff --git a/src/tests/diskio.h b/src/tests/diskio.h new file mode 100644 index 0000000..40a002f --- /dev/null +++ b/src/tests/diskio.h @@ -0,0 +1,118 @@ +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file (C)ChaN, 2010 +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO + +#define _READONLY 0 /* 1: Remove write functions */ +#define _USE_IOCTL 1 /* 1: Use disk_ioctl fucntion */ + +#include + +#include "integer.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + 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 */ + +int assign_drives (int, int); +DSTATUS disk_initialize (BYTE); +DSTATUS disk_status (BYTE); +DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); +#if _READONLY == 0 +DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); +#endif +#define disk_ioctl(a,b,c) RES_OK + +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 int sd_offload, ff_sd_offload, sd_offload_tgt, newcard; +extern int sd_offload_partial; +extern uint16_t sd_offload_partial_start; +extern uint16_t sd_offload_partial_end; +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) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl fucntion */ + +/* Generic command (defined for FatFs) */ +#define CTRL_SYNC 0 /* Flush disk cache (for write functions) */ +#define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */ +#define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */ + +/* Generic command */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ + +/* MMC/SDC specific ioctl command */ +#define MMC_GET_TYPE 10 /* Get card type */ +#define MMC_GET_CSD 11 /* Get CSD */ +#define MMC_GET_CID 12 /* Get CID */ +#define MMC_GET_OCR 13 /* Get OCR */ +#define MMC_GET_SDSTAT 14 /* Get SD status */ + +/* ATA/CF specific ioctl command */ +#define ATA_GET_REV 20 /* Get F/W revision */ +#define ATA_GET_MODEL 21 /* Get model name */ +#define ATA_GET_SN 22 /* Get serial number */ + +/* NAND specific ioctl command */ +#define NAND_FORMAT 30 /* Create physical format */ + + +#define _DISKIO +#endif diff --git a/src/tests/faulthandler.c b/src/tests/faulthandler.c new file mode 100644 index 0000000..47d595f --- /dev/null +++ b/src/tests/faulthandler.c @@ -0,0 +1,20 @@ +#include +#include "uart.h" + +void HardFault_Handler(void) { + printf("HFSR: %lx\n", SCB->HFSR); + while (1) ; +} + +void MemManage_Handler(void) { + printf("MemManage - CFSR: %lx; MMFAR: %lx\n", SCB->CFSR, SCB->MMFAR); +} + +void BusFault_Handler(void) { + printf("BusFault - CFSR: %lx; BFAR: %lx\n", SCB->CFSR, SCB->BFAR); +} + +void UsageFault_Handler(void) { + printf("UsageFault - CFSR: %lx; BFAR: %lx\n", SCB->CFSR, SCB->BFAR); +} + diff --git a/src/tests/ff.c b/src/tests/ff.c new file mode 100644 index 0000000..bcd68c1 --- /dev/null +++ b/src/tests/ff.c @@ -0,0 +1,3805 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.08a (C)ChaN, 2010 +/-----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2010, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Feb 26,'06 R0.00 Prototype. +/ +/ Apr 29,'06 R0.01 First stable version. +/ +/ Jun 01,'06 R0.02 Added FAT12 support. +/ Removed unbuffered mode. +/ Fixed a problem on small (<32M) partition. +/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). +/ +/ Sep 22,'06 R0.03 Added f_rename(). +/ Changed option _FS_MINIMUM to _FS_MINIMIZE. +/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast. +/ Fixed f_mkdir() creates incorrect directory on FAT32. +/ +/ Feb 04,'07 R0.04 Supported multiple drive system. +/ Changed some interfaces for multiple drive system. +/ Changed f_mountdrv() to f_mount(). +/ Added f_mkfs(). +/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive. +/ Added a capability of extending file size to f_lseek(). +/ Added minimization level 3. +/ Fixed an endian sensitive code in f_mkfs(). +/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. +/ Added FSInfo support. +/ Fixed DBCS name can result FR_INVALID_NAME. +/ Fixed short seek (<= csize) collapses the file object. +/ +/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). +/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. +/ Fixed f_mkdir() on FAT32 creates incorrect directory. +/ Feb 03,'08 R0.05a Added f_truncate() and f_utime(). +/ Fixed off by one error at FAT sub-type determination. +/ Fixed btr in f_read() can be mistruncated. +/ Fixed cached sector is not flushed when create and close without write. +/ +/ Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). +/ Improved performance of f_lseek() on moving to the same or following cluster. +/ +/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option. (_FS_TINY) +/ Added long file name support. +/ Added multiple code page support. +/ Added re-entrancy for multitask operation. +/ Added auto cluster size selection to f_mkfs(). +/ Added rewind option to f_readdir(). +/ Changed result code of critical errors. +/ Renamed string functions to avoid name collision. +/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. +/ Added multiple sector size support. +/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. +/ Fixed wrong cache control in f_lseek(). +/ Added relative path feature. +/ Added f_chdir() and f_chdrive(). +/ Added proper case conversion to extended char. +/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. +/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. +/ Fixed name matching error on the 13 char boundary. +/ Added a configuration option, _LFN_UNICODE. +/ Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. +/ +/ May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN = 3) +/ Added file lock feature. (_FS_SHARE) +/ Added fast seek feature. (_USE_FASTSEEK) +/ Changed some types on the API, XCHAR->TCHAR. +/ Changed fname member in the FILINFO structure on Unicode cfg. +/ String functions support UTF-8 encoding files on Unicode cfg. +/ Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2) +/ Added sector erase feature. (_USE_ERASE) +/ Moved file lock semaphore table from fs object to the bss. +/ Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'. +/ Fixed f_mkfs() creates wrong FAT32 volume. +/---------------------------------------------------------------------------*/ + +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ +#include "config.h" +#include "uart.h" + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 8255 +#error Wrong include file (ff.h). +#endif + + +/* Definitions on sector size */ +#if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096 +#error Wrong sector size. +#endif +#if _MAX_SS != 512 +#define SS(fs) ((fs)->ssize) /* Multiple sector size */ +#else +#define SS(fs) 512U /* Fixed sector size */ +#endif + + +/* Reentrancy related */ +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area must not be used in re-entrant configuration. +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res +#endif + +#define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } + + +/* File shareing feature */ +#if _FS_SHARE +#if _FS_READONLY +#error _FS_SHARE must be 0 on read-only cfg. +#endif +typedef struct { + FATFS *fs; /* File ID 1, volume (NULL:blank entry) */ + DWORD clu; /* File ID 2, directory */ + WORD idx; /* File ID 3, directory index */ + WORD ctr; /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */ +} FILESEM; +#endif + + +/* Misc definitions */ +#define LD_CLUST(dir) (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO)) +#define ST_CLUST(dir,cl) {ST_WORD(dir+DIR_FstClusLO, cl); ST_WORD(dir+DIR_FstClusHI, (DWORD)cl>>16);} + + +/* Character code support macros */ +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S /* Code page is DBCS */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* Name status flags */ +#define NS 11 /* Offset of name status byte */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + +/* FAT sub-type boundaries */ +/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ +#define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */ +#define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure member because there are incompatibility of the packing option +/ between compilers. */ + +#define BS_jmpBoot 0 +#define BS_OEMName 3 +#define BPB_BytsPerSec 11 +#define BPB_SecPerClus 13 +#define BPB_RsvdSecCnt 14 +#define BPB_NumFATs 16 +#define BPB_RootEntCnt 17 +#define BPB_TotSec16 19 +#define BPB_Media 21 +#define BPB_FATSz16 22 +#define BPB_SecPerTrk 24 +#define BPB_NumHeads 26 +#define BPB_HiddSec 28 +#define BPB_TotSec32 32 +#define BS_DrvNum 36 +#define BS_BootSig 38 +#define BS_VolID 39 +#define BS_VolLab 43 +#define BS_FilSysType 54 +#define BPB_FATSz32 36 +#define BPB_ExtFlags 40 +#define BPB_FSVer 42 +#define BPB_RootClus 44 +#define BPB_FSInfo 48 +#define BPB_BkBootSec 50 +#define BS_DrvNum32 64 +#define BS_BootSig32 66 +#define BS_VolID32 67 +#define BS_VolLab32 71 +#define BS_FilSysType32 82 +#define FSI_LeadSig 0 +#define FSI_StrucSig 484 +#define FSI_Free_Count 488 +#define FSI_Nxt_Free 492 +#define MBR_Table 446 +#define BS_55AA 510 + +#define DIR_Name 0 +#define DIR_Attr 11 +#define DIR_NTres 12 +#define DIR_CrtTime 14 +#define DIR_CrtDate 16 +#define DIR_FstClusHI 20 +#define DIR_WrtTime 22 +#define DIR_WrtDate 24 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 +#define LDIR_Ord 0 +#define LDIR_Attr 11 +#define LDIR_Type 12 +#define LDIR_Chksum 13 +#define LDIR_FstClusLO 26 + + + +/*------------------------------------------------------------*/ +/* Work area */ + +#if _VOLUMES +static +FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +#else +#error Number of drives must not be 0. +#endif + +static +WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH +static +BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_SHARE +static +FILESEM Files[_FS_SHARE]; /* File lock semaphores */ +#endif + +#if _USE_LFN == 0 /* No LFN */ +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) (dobj).fn = sfn +#define FREE_BUF() + +#elif _USE_LFN == 1 /* LFN with static LFN working buffer */ +static WCHAR LfnBuf[_MAX_LFN+1]; +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } +#define FREE_BUF() + +#elif _USE_LFN == 2 /* LFN with dynamic LFN working buffer on the stack */ +#define DEF_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } +#define FREE_BUF() + +#elif _USE_LFN == 3 /* LFN with dynamic LFN working buffer on the heap */ +#define DEF_NAMEBUF BYTE sfn[12]; WCHAR *lfn +#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \ + if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \ + (dobj).lfn = lfn; (dobj).fn = sfn; } +#define FREE_BUF() ff_memfree(lfn) + +#else +#error Wrong LFN configuration. +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + +#if _WORD_ACCESS == 1 + while (cnt >= sizeof(int)) { + *(int*)d = *(int*)s; + d += sizeof(int); s += sizeof(int); + cnt -= sizeof(int); + } +#endif + while (cnt--) + *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + while (cnt--) + *d++ = (BYTE)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + while (cnt-- && (r = *d++ - *s++) == 0) ; + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { + while (*str && *str != chr) str++; + return *str; +} + + + +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +#if _FS_REENTRANT + +static +int lock_fs ( + FATFS *fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS *fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + res != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* File shareing control functions */ +/*-----------------------------------------------------------------------*/ +#if _FS_SHARE + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dj, /* Directory object pointing the file to be checked */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_SHARE; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dj->fs && /* Check if the file matched with an open file */ + Files[i].clu == dj->sclust && + Files[i].idx == dj->index) break; + } else { /* Blank entry */ + be++; + } + } + if (i == _FS_SHARE) /* The file is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new file? */ + + /* The file has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock ( /* Check if an entry is available for a new file */ + FATFS* fs /* File system object */ +) +{ + UINT i; + + for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ; + return (i == _FS_SHARE) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment file open counter and returns its index (0:int error) */ + DIR* dj, /* Directory object pointing the file to register or increment */ + int acc /* Desired access mode (0:Read, !0:Write) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_SHARE; i++) { /* Find the file */ + if (Files[i].fs == dj->fs && + Files[i].clu == dj->sclust && + Files[i].idx == dj->index) break; + } + + if (i == _FS_SHARE) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ; + if (i == _FS_SHARE) return 0; /* No space to register (int err) */ + Files[i].fs = dj->fs; + Files[i].clu = dj->sclust; + Files[i].idx = dj->index; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement file open counter */ + UINT i /* Semaphore index */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_SHARE) { + n = Files[i].ctr; + if (n == 0x100) n = 0; + if (n) n--; + Files[i].ctr = n; + if (!n) Files[i].fs = 0; + res = FR_OK; + } else { + res = FR_INT_ERR; + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_SHARE; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#if !_FS_READONLY + if (fs->wflag) { /* Write back dirty window if needed */ + if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) + return FR_DISK_ERR; + fs->wflag = 0; + if (wsect < (fs->fatbase + fs->fsize)) { /* In FAT area */ + BYTE nf; + for (nf = fs->n_fats; nf > 1; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) + return FR_DISK_ERR; + fs->winsect = sector; + } + } + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Clean-up cached data */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ + FATFS *fs /* File system object */ +) +{ + FRESULT res; + + + res = move_window(fs, 0); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + fs->winsect = 0; + mem_set(fs->win, 0, 512); + ST_WORD(fs->win+BS_55AA, 0xAA55); + ST_DWORD(fs->win+FSI_LeadSig, 0x41615252); + ST_DWORD(fs->win+FSI_StrucSig, 0x61417272); + ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust); + ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust); + disk_write(fs->drv, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drv, CTRL_SYNC, (void*)0) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + + +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= (fs->n_fatent - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + UINT wc, bc; + BYTE *p; + + + if (clst < 2 || clst >= fs->n_fatent) /* Chack range */ + return 1; + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; + wc = fs->win[bc % SS(fs)]; bc++; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; + wc |= fs->win[bc % SS(fs)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break; + p = &fs->win[clst * 2 % SS(fs)]; + return LD_WORD(p); + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break; + p = &fs->win[clst * 4 % SS(fs)]; + return LD_DWORD(p) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occurred at the disk I/O layer */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY + +FRESULT put_fat ( + FATFS *fs, /* File system object */ + DWORD clst, /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */ + DWORD val /* New value to mark the cluster */ +) +{ + UINT bc; + BYTE *p; + FRESULT res; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + ST_WORD(p, (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val |= LD_DWORD(p) & 0xF0000000; + ST_DWORD(p, val); + break; + + default : + res = FR_INT_ERR; + } + fs->wflag = 1; + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; +#if _USE_ERASE + DWORD scl = clst, ecl = clst, resion[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->n_fatent) { /* Not a last link? */ + nxt = get_fat(fs, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ + if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ + res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ + if (res != FR_OK) break; + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */ + fs->free_clust++; + fs->fsi_flag = 1; + } +#if _USE_ERASE + if (ecl + 1 == nxt) { /* Next cluster is contiguous */ + ecl = nxt; + } else { /* End of contiguous clusters */ + resion[0] = clust2sect(fs, scl); /* Start sector */ + resion[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, resion); /* Erase the block */ + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch or Create a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (!scl || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch the current chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ + return cs; + if (ncl == scl) return 0; /* No free cluster */ + } + + res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ + } + if (res == FR_OK) { + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + } + + return ncl; /* Return new cluster number or error code */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( + DIR *dj, /* Pointer to directory object */ + WORD idx /* Directory index number */ +) +{ + DWORD clst; + WORD ic; + + + dj->index = idx; + clst = dj->sclust; + if (clst == 1 || clst >= dj->fs->n_fatent) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dj->fs->dirbase; + + if (clst == 0) { /* Static table (root-dir in FAT12/16) */ + dj->clust = clst; + if (idx >= dj->fs->n_rootdir) /* Index is out of range */ + return FR_INT_ERR; + dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32); /* Sector# */ + } + else { /* Dynamic table (sub-dirs or root-dir in FAT32) */ + ic = SS(dj->fs) / 32 * dj->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dj->fs->n_fatent) /* Reached to end of table or int error */ + return FR_INT_ERR; + idx -= ic; + } + dj->clust = clst; + dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32); /* Sector# */ + } + + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32; /* Ptr to the entry in the sector */ + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */ + DIR *dj, /* Pointer to directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD clst; + WORD i; + + + i = dj->index + 1; + if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->clust == 0) { /* Static table */ + if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dj->fs, dj->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dj->fs->n_fatent) { /* When it reached end of dynamic table */ +#if !_FS_READONLY + BYTE c; + if (!stretch) return FR_NO_FILE; /* When do not stretch, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Stretch cluster chain */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + /* Clean-up stretched table */ + if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */ + mem_set(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */ + dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */ + for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */ + dj->fs->wflag = 1; + if (move_window(dj->fs, 0)) return FR_DISK_ERR; + dj->fs->winsect++; + } + dj->fs->winsect -= c; /* Rewind window address */ +#else + return FR_NO_FILE; /* Report EOT */ +#endif + } + dj->clust = clst; /* Initialize data for new cluster */ + dj->sect = clust2sect(dj->fs, clst); + } + } + } + + dj->index = i; + dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */ + + +static +int cmp_lfn ( /* 1:Matched, 0:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Get offset in the LFN buffer */ + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + wc = ff_wtoupper(uc); /* Convert it to upper case */ + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return 0; /* Not matched */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Repeat until all chars in the entry are checked */ + + if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i]) /* Last segment matched but different length */ + return 0; + + return 1; /* The part of LFN matched */ +} + + + +static +int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & 0x40) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; +} + + +#if !_FS_READONLY +static +void fit_lfn ( + const WCHAR *lfnbuf, /* Pointer to the LFN buffer */ + BYTE *dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir+LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40; /* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Create numbered name */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +void gen_numname ( + BYTE *dst, /* Pointer to generated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa */ + i = 7; + do { + c = (seq % 16) + '0'; + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Calculate sum of an SFN */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE *dir /* Ptr to directory entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + DIR *dj /* Pointer to the directory object linked to the file name */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dj, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; +#endif + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dj->lfn) { + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; /* LFN start order */ + dj->lfn_idx = dj->index; + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +FRESULT dir_read ( + DIR *dj /* Pointer to the directory object that pointing the entry to be read */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord = 0xFF, sum = 0xFF; +#endif + + res = FR_NO_FILE; + while (dj->sect) { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; + dj->lfn_idx = dj->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dj->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dj->sect = 0; + + return res; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ + DIR *dj /* Target directory with object name to be created */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN /* LFN configuration */ + WORD n, ne, is; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dj->fn; lfn = dj->lfn; + mem_cpy(sn, fn, 12); + + if (_FS_RPATH && (sn[NS] & NS_DOT)) /* Cannot create dot entry */ + return FR_INVALID_NAME; + + if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NS] = 0; dj->lfn = 0; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dj); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + fn[NS] = sn[NS]; dj->lfn = lfn; + } + + if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++) ; + ne = (ne + 25) / 13; + } else { /* Otherwise reserve only an SFN entry. */ + ne = 1; + } + + /* Reserve contiguous entries */ + res = dir_sdi(dj, 0); + if (res != FR_OK) return res; + n = is = 0; + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; /* Check the entry status */ + if (c == 0xE5 || c == 0) { /* Is it a blank entry? */ + if (n == 0) is = dj->index; /* First index of the contiguous entry */ + if (++n == ne) break; /* A contiguous entry that required count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, 1); /* Next entry with table stretch */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_sdi(dj, is); + if (res == FR_OK) { + sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */ + ne--; + do { /* Store LFN entries in bottom first */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum); + dj->fs->wflag = 1; + res = dir_next(dj, 0); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_sdi(dj, 0); + if (res == FR_OK) { + do { /* Find a blank entry for the SFN */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; + if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */ + res = dir_next(dj, 1); /* Next entry with table stretch */ + } while (res == FR_OK); + } +#endif + + if (res == FR_OK) { /* Initialize the SFN entry */ + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + dir = dj->dir; + mem_set(dir, 0, 32); /* Clean the entry */ + mem_cpy(dir, dj->fn, 11); /* Put SFN */ +#if _USE_LFN + dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + dj->fs->wflag = 1; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY && !_FS_MINIMIZE +static +FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ + DIR *dj /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + WORD i; + + i = dj->index; /* SFN index */ + res = dir_sdi(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dj, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_sdi(dj, dj->index); + if (res == FR_OK) { + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( + DIR *dj, /* Pointer to the directory object */ + const TCHAR **path /* Pointer to pointer to the segment in the path string */ +) +{ +#ifdef _EXCVT + static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */ +#endif + +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + si = di = 0; + p = *path; + lfn = dj->lfn; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return FR_INVALID_NAME; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(b)) + return FR_INVALID_NAME; /* Reject invalid sequence */ + w = (w << 8) + b; /* Create a DBC */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode char */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ +#if _FS_RPATH + if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */ + (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) { + lfn[di] = 0; + for (i = 0; i < 11; i++) + dj->fn[i] = (i < di) ? '.' : ' '; + dj->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di-1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject nul string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + mem_set(dj->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN char */ + if (!w) break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII char */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = excvt[w - 0x80]; /* Convert extended char to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Double byte char (always false on SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dj->fn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal chars for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dj->fn[i++] = (BYTE)w; + } + + if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dj->fn[NS] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + sfn = dj->fn; + mem_set(sfn, ' ', 11); + si = i = b = 0; ni = 8; + p = *path; +#if _FS_RPATH + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Return pointer to the next segment */ + sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; + if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') return FR_INVALID_NAME; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) { /* Extended char? */ + b |= 3; /* Eliminate NT flag */ +#ifdef _EXCVT + c = excvt[c-0x80]; /* Upper conversion (SBCS) */ +#else +#if !_DF1S /* ASCII only cfg */ + return FR_INVALID_NAME; +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } else { /* Single byte code */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { /* ASCII large capital? */ + b |= 2; + } else { + if (IsLower(c)) { /* ASCII small capital? */ + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Return pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject nul string */ + if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */ + + sfn[NS] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + DIR *dj, /* Pointer to the directory object */ + FILINFO *fno /* Pointer to the file information to be filled */ +) +{ + UINT i; + BYTE nt, *dir; + TCHAR *p, c; + + + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy name body */ + c = dir[i]; + if (c == ' ') break; + if (c == 0x05) c = (TCHAR)0xE5; + if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20; +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *p++ = c; + } + if (dir[8] != ' ') { /* Copy name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20; +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *p++ = c; + } + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ + fno->clust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) + | LD_WORD(dir+DIR_FstClusLO); + } + *p = 0; /* Terminate SFN str by a \0 */ + +#if _USE_LFN + if (fno->lfname && fno->lfsize) { + TCHAR *tp = fno->lfname; + WCHAR w, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN char */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM conversion */ + if (!w) { i = 0; break; } /* Could not convert, no LFN */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ + tp[i++] = (TCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overflow, no LFN */ + tp[i++] = (TCHAR)w; + } + } + tp[i] = 0; /* Terminate the LFN str by a \0 */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR *dj, /* Directory object to return last directory and found object */ + const TCHAR *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, ns; + + +#if _FS_RPATH + if (*path == '/' || *path == '\\') { /* There is a heading separator */ + path++; dj->sclust = 0; /* Strip it and start from the root dir */ + } else { /* No heading separator */ + dj->sclust = dj->fs->cdir; /* Start from the current dir */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dj->sclust = 0; /* Start from the root dir */ +#endif + + if ((UINT)*path < ' ') { /* Nul path means the start directory itself */ + res = dir_sdi(dj, 0); + dj->dir = 0; + + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + ns = *(dj->fn+NS); + if (res != FR_OK) { /* Failed to find the object */ + if (res != FR_NO_FILE) break; /* Abort if any hard error occured */ + /* Object not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exit */ + dj->sclust = 0; dj->dir = 0; /* It is the root dir */ + res = FR_OK; + if (!(ns & NS_LAST)) continue; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; + } + break; + } + if (ns & NS_LAST) break; /* Last segment match. Function completed. */ + dir = dj->dir; /* There is next segment. Follow the sub directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ + res = FR_NO_PATH; break; + } + dj->sclust = LD_CLUST(dir); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk error */ + FATFS *fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK) /* Load boot record */ + return 3; + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file system object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occurred */ + const TCHAR **path, /* Pointer to pointer to the path name (drive number) */ + FATFS **rfs, /* Pointer to pointer to the found file system object */ + BYTE chk_wp /* !=0: Check media write protection for write access */ +) +{ + BYTE fmt, b, *tbl; + UINT vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat; + WORD nrsv; + const TCHAR *p = *path; + FATFS *fs; + + /* Get logical drive number from the path name */ + vol = p[0] - '0'; /* Is there a drive number? */ + if (vol <= 9 && p[1] == ':') { /* Found a drive number, get and strip it */ + p += 2; *path = p; /* Return pointer to the path name */ + } else { /* No drive number is given */ +#if _FS_RPATH + vol = CurrVol; /* Use current drive */ +#else + vol = 0; /* Use drive 0 */ +#endif + } + + /* Check if the logical drive is valid or not */ + if (vol >= _VOLUMES) /* Is the drive number valid? */ + return FR_INVALID_DRIVE; + *rfs = fs = FatFs[vol]; /* Return pointer to the corresponding file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock file system */ + + if (fs->fs_type) { /* If the logical drive has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized (has not been changed), */ +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + return FR_OK; /* The file system object is valid */ + } + } + + /* The logical drive must be mounted. */ + /* Following code attempts to mount a volume. (analyze BPB and initialize the fs object) */ + + 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 */ + 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) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &fs->ssize) != RES_OK) + return FR_DISK_ERR; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + /* Search FAT partition on the drive. Supports only generic partitionings, FDISK and SFD. */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 if it is a VBR */ + if (fmt == 1) { /* Not an FAT-VBR, the disk may be partitioned */ + /* Check the partition listed in top of the partition table */ + tbl = &fs->win[MBR_Table + LD2PT(vol) * 16]; /* Partition table */ + if (tbl[4]) { /* Is the partition existing? */ + bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ + fmt = check_fs(fs, bsect); /* Check the partition */ + } + } + if (fmt == 3) return FR_DISK_ERR; + if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* Following code initializes the file system object */ + + if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + + fasize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = b = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + if (b != 1 && b != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= b; /* Number of sectors for FAT area */ + + fs->csize = b = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / 32)) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */ + + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + + nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (!nrsv) return FR_NO_FILESYSTEM; /* (BPB_RsvdSecCnt must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / 32); /* RSV+FAT+DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT12; + if (nclst >= MIN_FAT16) fmt = FS_FAT16; + if (nclst >= MIN_FAT32) fmt = FS_FAT32; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->database = bsect + sysect; /* Data start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + if (fmt == FS_FAT32) { + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Required FAT size) */ + } else { + if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Required FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (FAT size must not be less than FAT sectors */ + return FR_NO_FILESYSTEM; + +#if !_FS_READONLY + /* Initialize cluster allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->last_clust = 0; + + /* Get fsinfo if available */ + if (fmt == FS_FAT32) { + fs->fsi_flag = 0; + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + if (disk_read(fs->drv, fs->win, fs->fsi_sector, 1) == RES_OK && + LD_WORD(fs->win+BS_55AA) == 0xAA55 && + LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 && + LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) { + fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free); + fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count); + } + } +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ + fs->winsect = 0; /* Invalidate sector cache */ + fs->wflag = 0; +#if _FS_RPATH + fs->cdir = 0; /* Current directory (root dir) */ +#endif +#if _FS_SHARE /* Clear file lock semaphores */ + clear_lock(fs); +#endif + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/dir object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + FATFS *fs, /* Pointer to the file system object */ + WORD id /* Member id of the target object to be checked */ +) +{ +// printf("fs=%p fs->fs_type=%d fs->id=%d id=%d\n", fs, fs->fs_type, fs->id, id); + if (!fs || !fs->fs_type || fs->id != id) + return FR_INVALID_OBJECT; + + ENTER_FF(fs); /* Lock file system */ + + if (disk_status(fs->drv) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + BYTE vol, /* Logical drive number to be mounted/unmounted */ + FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ +) +{ + FATFS *rfs; + + + if (vol >= _VOLUMES) /* Check if the drive number is valid */ + return FR_INVALID_DRIVE; + rfs = FatFs[vol]; /* Get current fs object */ + + if (rfs) { +#if _FS_SHARE + clear_lock(rfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR; +#endif + rfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL *fp, /* Pointer to the blank file object */ + const TCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + fp->fs = 0; /* Clear file object */ + +#if !_FS_READONLY + mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; + res = chk_mounted(&path, &dj.fs, (BYTE)(mode & ~FA_READ)); +#else + mode &= FA_READ; + res = chk_mounted(&path, &dj.fs, 0); +#endif + INIT_BUF(dj); + if (res == FR_OK) + res = follow_path(&dj, path); /* Follow the file path */ + dir = dj.dir; + +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (!dir) /* Current dir itself */ + res = FR_INVALID_NAME; +#if _FS_SHARE + else + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD dw, cl; + + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ +#if _FS_SHARE + res = enq_lock(dj.fs) ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + mode |= FA_CREATE_ALWAYS; /* File is created */ + dir = dj.dir; /* New entry */ + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) { /* Cannot create new */ + res = FR_EXIST; + } else { + if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = get_fattime(); /* Created time */ + ST_DWORD(dir+DIR_CrtTime, dw); + dir[DIR_Attr] = 0; /* Reset attribute */ + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + cl = LD_CLUST(dir); /* Get start cluster */ + ST_CLUST(dir, 0); /* cluster = 0 */ + dj.fs->wflag = 1; + if (cl) { /* Remove the cluster chain if exist */ + dw = dj.fs->winsect; + res = remove_chain(dj.fs, cl); + if (res == FR_OK) { + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + res = move_window(dj.fs, dw); + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Follow succeeded */ + if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + res = FR_DENIED; + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA__WRITTEN; + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#if _FS_SHARE + fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->lockid) res = FR_INT_ERR; +#endif + } + +#else /* R/O configuration */ + if (res == FR_OK) { /* Follow succeeded */ + if (!dir) { /* Current dir itself */ + res = FR_INVALID_NAME; + } else { + if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + res = FR_NO_FILE; + } + } +#endif + FREE_BUF(); + + if (res == FR_OK) { + fp->flag = mode; /* File access mode */ + fp->org_clust = LD_CLUST(dir); /* File start cluster */ + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; /* File pointer */ + fp->dsect = 0; +#if _USE_FASTSEEK + fp->cltbl = 0; /* No cluster link map table */ +#endif + fp->fs = dj.fs; fp->id = dj.fs->id; /* Validate file object */ + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT *br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clst, sect, remain; + UINT rcnt, cc; + BYTE csect, *rbuff = buff; + + if(btr>512 && !ff_sd_offload) printf("WARNING: read >512 bytes but offloading is inactive!!\n"); + + *br = 0; /* Initialize byte counter */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data transferred */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_fat(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; +/* XXX OFFLOAD GOES HERE */ + if (ff_sd_offload) { + sd_offload = 1; + } + if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + sd_offload = 0; +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) + mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); +#else + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc){ + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); uart_putc('Y');} +#endif +#endif + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write sector I/O buffer if needed */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if(!ff_sd_offload) { + if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } + } +#endif + fp->dsect = sect; + } + rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; + if(!ff_sd_offload) { +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#else + mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ + } else { + sd_offload_partial_start = fp->fptr % SS(fp->fs); + sd_offload_partial_end = sd_offload_partial_start + rcnt; +// printf("partial dma. sect=%08lx start=%d end=%d\n", fp->dsect, sd_offload_partial_start, sd_offload_partial_end); + /* set start + end */ + sd_offload = 1; + sd_offload_partial = 1; + if(disk_read(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) { + sd_offload = 0; + ABORT(fp->fs, FR_DISK_ERR); + } + sd_offload = 0; +#endif + } + } + ff_sd_offload = 0; + LEAVE_FF(fp->fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT *bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clst, sect; + UINT wcnt, cc; + const BYTE *wbuff = buff; + BYTE csect; + + + *bw = 0; /* Initialize byte counter */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if (fp->fsize + btw < fp->fsize) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data transferred */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->org_clust; /* Follow from the origin */ + if (clst == 0) /* When there is no cluster chain, */ + fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ + clst = create_chain(fp->fs, fp->curr_clust); /* Follow or stretch cluster chain */ + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write back data buffer prior to following direct transfer */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write back data buffer prior to following direct transfer */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_TINY + if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */ + mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->fs->wflag = 0; + } +#else + if (fp->dsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } +#endif + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly buffer filling at growing edge */ + if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR); + fp->fs->winsect = sect; + } +#else + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ + if (wcnt > btw) wcnt = btw; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->fs->wflag = 1; +#else + mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->flag |= FA__DIRTY; +#endif + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file change flag */ + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tim; + BYTE *dir; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ +#if !_FS_TINY /* Write-back dirty buffer */ + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + /* Update the directory entry */ + res = move_window(fp->fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */ + ST_CLUST(dir, fp->org_clust); /* Update start cluster */ + tim = get_fattime(); /* Update updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync(fp->fs); + } + } + } + + LEAVE_FF(fp->fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + +#if _FS_READONLY + FATFS *fs = fp->fs; + res = validate(fs, fp->id); + if (res == FR_OK) fp->fs = 0; /* Discard file object */ + LEAVE_FF(fs, res); + +#else + res = f_sync(fp); /* Flush cached data */ +#if _FS_SHARE + if (res == FR_OK) { /* Decrement open counter */ +#if _FS_REENTRANT + res = validate(fp->fs, fp->id); + if (res == FR_OK) { + res = dec_lock(fp->lockid); + unlock_fs(fp->fs, FR_OK); + } +#else + res = dec_lock(fp->lockid); +#endif + } +#endif + if (res == FR_OK) fp->fs = 0; /* Discard file object */ + return res; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Current Drive/Directory Handlings */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH >= 1 + +FRESULT f_chdrive ( + BYTE drv /* Drive number */ +) +{ + if (drv >= _VOLUMES) return FR_INVALID_DRIVE; + + CurrVol = drv; + + return FR_OK; +} + + + +FRESULT f_chdir ( + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the path */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (!dj.dir) { + dj.fs->cdir = dj.sclust; /* Start directory itself */ + } else { + if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ + dj.fs->cdir = LD_CLUST(dj.dir); + else + res = FR_NO_PATH; /* Reached but a file */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR *path, /* Pointer to the directory path */ + UINT sz_path /* Size of path */ +) +{ + FRESULT res; + DIR dj; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEF_NAMEBUF; + + + *path = 0; + res = chk_mounted((const TCHAR**)&path, &dj.fs, 0); /* Get current volume */ + if (res == FR_OK) { + INIT_BUF(dj); + i = sz_path; /* Bottom of buffer (dir stack base) */ + dj.sclust = dj.fs->cdir; /* Start to follow upper dir from current dir */ + while ((ccl = dj.sclust) != 0) { /* Repeat while current dir is a sub-dir */ + res = dir_sdi(&dj, 1); /* Get parent dir */ + if (res != FR_OK) break; + res = dir_read(&dj); + if (res != FR_OK) break; + dj.sclust = LD_CLUST(dj.dir); /* Goto parent dir */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child dir */ + res = dir_read(&dj); + if (res != FR_OK) break; + if (ccl == LD_CLUST(dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; +#if _USE_LFN + fno.lfname = path; + fno.lfsize = i; +#endif + get_fileinfo(&dj, &fno); /* Get the dir name and push it to the buffer */ + tp = fno.fname; + if (_USE_LFN && *path) tp = path; + for (n = 0; tp[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) path[--i] = tp[--n]; + path[--i] = '/'; + } + tp = path; + if (res == FR_OK) { + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; + if (i == sz_path) { /* Root-dir */ + *tp++ = '/'; + } else { /* Sub-dir */ + do /* Add stacked path str */ + *tp++ = path[i++]; + while (i < sz_path); + } + } + *tp = 0; + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + +#if _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + DWORD cl, pcl, ncl, tcl, dsc, tlen, *tbl = fp->cltbl; + BYTE csc; + + tlen = *tbl++; + if (ofs == CREATE_LINKMAP) { /* Create link map table */ + cl = fp->org_clust; + if (cl) { + do { + if (tlen < 4) { /* Not enough table items */ + res = FR_NOT_ENOUGH_CORE; break; + } + tcl = cl; ncl = 0; + do { /* Get a fragment and store the top and length */ + pcl = cl; ncl++; + cl = get_fat(fp->fs, cl); + if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + } while (cl == pcl + 1); + *tbl++ = ncl; *tbl++ = tcl; + tlen -= 2; + } while (cl < fp->fs->n_fatent); + } + *tbl = 0; /* Terminate table */ + + } else { /* Fast seek */ + if (ofs > fp->fsize) /* Clip offset at the file size */ + ofs = fp->fsize; + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + dsc = (ofs - 1) / SS(fp->fs); + cl = dsc / fp->fs->csize; + for (;;) { + ncl = *tbl++; + if (!ncl) ABORT(fp->fs, FR_INT_ERR); + if (cl < ncl) break; + cl -= ncl; tbl++; + } + fp->curr_clust = cl + *tbl; + csc = (BYTE)(dsc & (fp->fs->csize - 1)); + dsc = clust2sect(fp->fs, fp->curr_clust); + if (!dsc) ABORT(fp->fs, FR_INT_ERR); + dsc += csc; + if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Flush dirty buffer if needed */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { + DWORD clst, bcs, nsect, ifptr; + + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->curr_clust; + } else { /* When seek to back cluster, */ + clst = fp->org_clust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->org_clust = clst; + } +#endif + fp->curr_clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR); + fp->curr_clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += ofs / SS(fp->fs); + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Flush dirty buffer if needed */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if(!ff_sd_offload) { + sd_offload_partial=0; + if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } else { + sd_offload_partial=1; + sd_offload_partial_start = fp->fptr % SS(fp->fs); + } +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set change flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + } + ff_sd_offload = 0; + LEAVE_FF(fp->fs, res); +} + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directroy Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR *dj, /* Pointer to directory object to create */ + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj->fs, 0); + if (res == FR_OK) { + INIT_BUF(*dj); + res = follow_path(dj, path); /* Follow the path to the directory */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (dj->dir) { /* It is not the root dir */ + if (dj->dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = LD_CLUST(dj->dir); + } else { /* The object is not a directory */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dj->id = dj->fs->id; + res = dir_sdi(dj, 0); /* Rewind dir */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequense */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DEF_NAMEBUF; + + + res = validate(dj->fs, dj->id); /* Check validity of the object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dj, 0); /* Rewind the directory object */ + } else { + INIT_BUF(*dj); + res = dir_read(dj); /* Read an directory item */ + if (res == FR_NO_FILE) { /* Reached end of dir */ + dj->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dj, fno); /* Get the object information */ + res = dir_next(dj, 0); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + FREE_BUF(); + } + } + + LEAVE_FF(dj->fs, res); +} + + +FRESULT l_opendirbycluster ( + FATFS *fs, + DIR *dj, + const TCHAR *path, + DWORD clust +) +{ + FRESULT res; + res = chk_mounted(&path, &fs, 0); + DEF_NAMEBUF; + INIT_BUF(*dj); + dj->sclust = clust; + dj->fs = fs; + dj->id = fs->id; + dj->dir = 0; + res = dir_sdi(dj, 0); + FREE_BUF(); + return res; +} + +FRESULT l_openfilebycluster ( + FATFS *fs, /* Pointer to file system object */ + FIL *fp, /* Pointer to the blank file object */ + const TCHAR *path, + DWORD clust, /* Cluster number to be opened */ + DWORD fsize /* File size to be assumed */ +) +{ + chk_mounted(&path, &fs, 0); + fp->flag = FA_READ; + fp->org_clust = clust; + fp->fsize = fsize; + fp->fptr = 0; + fp->dsect = 0; + fp->fs = fs; + + return FR_OK; +} + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR *path, /* Pointer to the logical drive number (root dir) */ + DWORD *nclst, /* Pointer to the variable to return number of free clusters */ + FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ +) +{ + FRESULT res; + DWORD n, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* Get drive number */ + res = chk_mounted(&path, fatfs, 0); + if (res == FR_OK) { + /* If free_clust is valid, return it without full cluster scan */ + if ((*fatfs)->free_clust <= (*fatfs)->n_fatent - 2) { + *nclst = (*fatfs)->free_clust; + } else { + /* Get number of free clusters */ + fat = (*fatfs)->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(*fatfs, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) n++; + } while (++clst < (*fatfs)->n_fatent); + } else { + clst = (*fatfs)->n_fatent; + sect = (*fatfs)->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(*fatfs, sect++); + if (res != FR_OK) break; + p = (*fatfs)->win; + i = SS(*fatfs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + (*fatfs)->free_clust = n; + if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; + *nclst = n; + } + } + LEAVE_FF(*fatfs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__ERROR) { /* Check abort flag */ + res = FR_INT_ERR; + } else { + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + res = FR_DENIED; + } + } + if (res == FR_OK) { + if (fp->fsize > fp->fptr) { + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(fp->fs, fp->org_clust); + fp->org_clust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->curr_clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fp->fs->n_fatent) { + res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF); + if (res == FR_OK) res = remove_chain(fp->fs, ncl); + } + } + } + if (res != FR_OK) fp->flag |= FA__ERROR; + } + + LEAVE_FF(fp->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + BYTE *dir; + DWORD dclst; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; /* Cannot remove dot entry */ +#if _FS_SHARE + if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open file */ +#endif + if (res == FR_OK) { /* The object is accessible */ + dir = dj.dir; + if (!dir) { + res = FR_INVALID_NAME; /* Cannot remove the start directory */ + } else { + if (dir[DIR_Attr] & AM_RDO) + res = FR_DENIED; /* Cannot remove R/O object */ + } + dclst = LD_CLUST(dir); + if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */ + if (dclst < 2) { + res = FR_INT_ERR; + } else { + mem_cpy(&sdj, &dj, sizeof(DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_sdi(&sdj, 2); /* Exclude dot entries */ + if (res == FR_OK) { + res = dir_read(&sdj); + if (res == FR_OK /* Not empty dir */ +#if _FS_RPATH + || dclst == sdj.fs->cdir /* Current dir */ +#endif + ) res = FR_DENIED; + if (res == FR_NO_FILE) res = FR_OK; /* Empty */ + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK) { + if (dclst) /* Remove the cluster chain if exist */ + res = remove_chain(dj.fs, dclst); + if (res == FR_OK) res = sync(dj.fs); + } + } + } + FREE_BUF(); + } + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, n; + DWORD dsc, dcl, pcl, tim = get_fattime(); + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) /* Flush FAT */ + res = move_window(dj.fs, 0); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(dj.fs, dcl); + dir = dj.fs->win; + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + ST_DWORD(dir+DIR_WrtTime, tim); + ST_CLUST(dir, dcl); + mem_cpy(dir+32, dir, 32); /* Create ".." entry */ + dir[33] = '.'; pcl = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) + pcl = 0; + ST_CLUST(dir+32, pcl); + for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + dj.fs->winsect = dsc++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res != FR_OK) break; + mem_set(dir, 0, SS(dj.fs)); + } + } + if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ + if (res != FR_OK) { + remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Created time */ + ST_CLUST(dir, dcl); /* Table start cluster */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = FR_INVALID_NAME; + } else { /* File or sub directory */ + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = FR_INVALID_NAME; + } else { /* File or sub-directory */ + ST_WORD(dir+DIR_WrtTime, fno->ftime); + ST_WORD(dir+DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR *path_old, /* Pointer to the old name */ + const TCHAR *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + BYTE buf[21], *dir; + DWORD dw; + DEF_NAMEBUF; + + + res = chk_mounted(&path_old, &djo.fs, 1); + if (res == FR_OK) { + djn.fs = djo.fs; + INIT_BUF(djo); + res = follow_path(&djo, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; +#if _FS_SHARE + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Old object is found */ + if (!djo.dir) { /* Is root dir? */ + res = FR_NO_FILE; + } else { + mem_cpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except for name */ + mem_cpy(&djn, &djo, sizeof(DIR)); /* Check new object */ + res = follow_path(&djn, path_new); + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */ +/* Start critical section that any interruption or error can cause cross-link */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy object information except for name */ + mem_cpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + djo.fs->wflag = 1; + if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) { /* Update .. entry in the directory if needed */ + dw = clust2sect(djn.fs, LD_CLUST(dir)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(djn.fs, dw); + dir = djn.fs->win+32; /* .. entry */ + if (res == FR_OK && dir[1] == '.') { + dw = (djn.fs->fs_type == FS_FAT32 && djn.sclust == djn.fs->dirbase) ? 0 : djn.sclust; + ST_CLUST(dir, dw); + djn.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) + res = sync(djo.fs); + } + } +/* End critical section */ + } + } + } + FREE_BUF(); + } + LEAVE_FF(djo.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly (available on only tiny cfg) */ +/*-----------------------------------------------------------------------*/ +#if _USE_FORWARD && _FS_TINY + +FRESULT f_forward ( + FIL *fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btr, /* Number of bytes to forward */ + UINT *bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + DWORD remain, clst, sect; + UINT rcnt; + BYTE csect; + + + *bf = 0; /* Initialize byte counter */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check error flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (!csect) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_fat(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + } + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + if (move_window(fp->fs, sect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + fp->dsect = sect; + rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ + if (rcnt > btr) rcnt = btr; + rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); + if (!rcnt) ABORT(fp->fs, FR_INT_ERR); + } + + LEAVE_FF(fp->fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create File System on the Drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Multiple of 32 */ +#define N_FATS 1 /* 1 or 2 */ + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ + UINT au /* Allocation unit size [bytes] */ +) +{ + static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; + static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; + BYTE fmt, md, *tbl; + DWORD n_clst, vs, n, wsect; + UINT i; + DWORD b_vol, b_fat, b_dir, b_data; /* Offset (LBA) */ + DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ + FATFS *fs; + DSTATUS stat; + + + /* Check mounted drive and clear work area */ + if (drv >= _VOLUMES) return FR_INVALID_DRIVE; + fs = FatFs[drv]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + drv = LD2PD(drv); + + /* Get disk statics */ + stat = disk_initialize(drv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; +#if _MAX_SS != 512 /* Get disk sector size */ + if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) + return FR_DISK_ERR; +#endif + if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) + return FR_DISK_ERR; + b_vol = (sfd) ? 0 : 63; /* Volume start sector */ + n_vol -= b_vol; + if (au & (au - 1)) au = 0; /* Check validity of the allocation unit size */ + if (!au) { /* AU auto selection */ + vs = n_vol / (2000 / (SS(fs) / 512)); + for (i = 0; vs < vst[i]; i++) ; + au = cst[i]; + } + au /= SS(fs); /* Number of sectors per cluster */ + if (au == 0) au = 1; + if (au > 128) au = 128; + + /* Pre-compute number of clusters and FAT syb-type */ + n_clst = n_vol / au; + fmt = FS_FAT12; + if (n_clst >= MIN_FAT16) fmt = FS_FAT16; + if (n_clst >= MIN_FAT32) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + if (fmt == FS_FAT32) { + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 32; + n_dir = 0; + } else { + n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; + n_fat = (n_fat + SS(fs) - 1) / SS(fs); + n_rsv = 1; + n_dir = N_ROOTDIR * 32UL / SS(fs); + } + b_fat = b_vol + n_rsv; /* FAT area start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ + b_data = b_dir + n_dir; /* Data area start sector */ + if (n_vol < b_data + au) return FR_MKFS_ABORTED; /* Too small volume */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; + n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ + n = (n - b_data) / N_FATS; + if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ + n_rsv += n; + b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + n_fat += n; + } + + /* Determine number of cluster and final check of validity of the FAT sub-type */ + n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; + if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) + || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) + return FR_MKFS_ABORTED; + + /* Create partition table if required */ + if (sfd) { + md = 0xF0; + } else { + DWORD n_disk = b_vol + n_vol; + + mem_set(fs->win, 0, SS(fs)); + tbl = fs->win+MBR_Table; + ST_DWORD(tbl, 0x00010180); /* Partition start in CHS */ + if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */ + n_disk = n_disk / 63 / 255; + tbl[7] = (BYTE)n_disk; + tbl[6] = (BYTE)((n_disk >> 2) | 63); + } else { + ST_WORD(&tbl[6], 0xFFFF); + } + tbl[5] = 254; + if (fmt != FS_FAT32) /* System ID */ + tbl[4] = (n_vol < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_vol); /* Partition size in LBA */ + ST_WORD(tbl+64, 0xAA55); /* Signature */ + if (disk_write(drv, fs->win, 0, 1) != RES_OK) + return FR_DISK_ERR; + md = 0xF8; + } + + /* Create volume boot record */ + tbl = fs->win; /* Clear sector */ + mem_set(tbl, 0, SS(fs)); + mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot code, OEM name */ + i = SS(fs); /* Sector size */ + ST_WORD(tbl+BPB_BytsPerSec, i); + tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of rootdir entries */ + ST_WORD(tbl+BPB_RootEntCnt, i); + if (n_vol < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl+BPB_TotSec16, n_vol); + } else { + ST_DWORD(tbl+BPB_TotSec32, n_vol); + } + tbl[BPB_Media] = md; /* Media descriptor */ + ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */ + ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */ + ST_DWORD(tbl+BPB_HiddSec, b_vol); /* Hidden sectors */ + n = get_fattime(); /* Use current time as VSN */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl+BS_VolID32, n); /* VSN */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory start cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (VBR+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (VBR+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + ST_DWORD(tbl+BS_VolID, n); /* VSN */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ + if (disk_write(drv, tbl, b_vol, 1) != RES_OK)/* Write original (VBR) */ + return FR_DISK_ERR; + if (fmt == FS_FAT32) /* Write backup (VBR+6) */ + disk_write(drv, tbl, b_vol + 6, 1); + + /* Initialize FAT area */ + wsect = b_fat; + for (i = 0; i < N_FATS; i++) { + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + n = md; /* Media descriptor byte */ + if (fmt != FS_FAT32) { + n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + n |= 0xFFFFFF00; + ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(tbl+4, 0xFFFFFFFF); + ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ + } + if (disk_write(drv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ + for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector write */ + if (disk_write(drv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize root directory */ + i = (fmt == FS_FAT32) ? au : n_dir; + do { + if (disk_write(drv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--i); + +#if _USE_ERASE /* Erase data area if needed */ + { + DWORD eb[2]; + + eb[0] = wsect; eb[1] = wsect + n_clst * au - 1; + disk_ioctl(drv, CTRL_ERASE_SECTOR, eb); + } +#endif + + /* Create FSInfo if needed */ + if (fmt == FS_FAT32) { + ST_WORD(tbl+BS_55AA, 0xAA55); + ST_DWORD(tbl+FSI_LeadSig, 0x41615252); + ST_DWORD(tbl+FSI_StrucSig, 0x61417272); + ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); + ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF); + disk_write(drv, tbl, b_vol + 1, 1); /* Write original (VBR+1) */ + disk_write(drv, tbl, b_vol + 7, 1); /* Write backup (VBR+7) */ + } + + return (disk_ioctl(drv, CTRL_SYNC, (void*)0) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fil /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, s, 1, &rc); + if (rc != 1) break; /* Break on EOF or error */ + c = s[0]; +#if _LFN_UNICODE /* Read a character in UTF-8 encoding */ + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequense */ + f_read(fil, s, 1, &rc); + if (rc != 1) break; + c = ((c & 0x1F) << 6) | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequense */ + f_read(fil, s, 2, &rc); + if (rc != 2) break; + c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F); + if (c < 0x800) c = '?'; + } else { /* Reject four-byte sequense */ + c = '?'; + } + } + } +#endif +#if _USE_STRFUNC >= 2 + if (c == '\r') continue; /* Strip '\r' */ +#endif + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + UINT bw, btw; + BYTE s[3]; + + +#if _USE_STRFUNC >= 2 + if (c == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ +#endif + +#if _LFN_UNICODE /* Write the character in UTF-8 encoding */ + if (c < 0x80) { /* 7-bit */ + s[0] = (BYTE)c; + btw = 1; + } else { + if (c < 0x800) { /* 11-bit */ + s[0] = (BYTE)(0xC0 | (c >> 6)); + s[1] = (BYTE)(0x80 | (c & 0x3F)); + btw = 2; + } else { /* 16-bit */ + s[0] = (BYTE)(0xE0 | (c >> 12)); + s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F)); + s[2] = (BYTE)(0x80 | (c & 0x3F)); + btw = 3; + } + } +#else /* Write the character without conversion */ + s[0] = (BYTE)c; + btw = 1; +#endif + f_write(fil, s, btw, &bw); /* Write the char to the file */ + return (bw == btw) ? 1 : EOF; /* Return the result */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + int n; + + + for (n = 0; *str; str++, n++) { + if (f_putc(*str, fil) == EOF) return EOF; + } + return n; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ +int f_printf ( + FIL* fil, /* Pointer to the file object */ + const TCHAR* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + BYTE f, r; + UINT i, w; + ULONG val; + TCHAR c, d, s[16]; + int res, cc; + + + va_start(arp, str); + + for (cc = res = 0; cc != EOF; res += cc) { + c = *str++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + cc = f_putc(c, fil); + if (cc != EOF) cc = 1; + continue; + } + w = f = 0; + c = *str++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *str++; + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *str++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 2; c = *str++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + cc = f_puts(va_arg(arp, TCHAR*), fil); continue; + case 'C' : /* Character */ + cc = f_putc((TCHAR)va_arg(arp, int), fil); continue; + case 'B' : /* Binary */ + r = 2; break; + case 'O' : /* Octal */ + r = 8; break; + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + case 'X' : /* Hexdecimal */ + r = 16; break; + default: /* Unknown */ + cc = f_putc(c, fil); continue; + } + + /* Get an argument */ + val = (f & 2) ? va_arg(arp, long) : ((d == 'D') ? (long)va_arg(arp, int) : va_arg(arp, unsigned int)); + if (d == 'D' && (val & 0x80000000)) { + val = 0 - val; + f |= 4; + } + /* Put it in numeral string */ + i = 0; + do { + d = (TCHAR)(val % r); val /= r; + if (d > 9) { + d += 7; + if (c == 'x') d += 0x20; + } + s[i++] = d + '0'; + } while (val && i < sizeof(s) / sizeof(s[0])); + if (f & 4) s[i++] = '-'; + cc = 0; + while (i < w-- && cc != EOF) { + cc = f_putc((TCHAR)((f & 1) ? '0' : ' '), fil); + res++; + } + do { + cc = f_putc(s[--i], fil); + res++; + } while (i && cc != EOF); + if (cc != EOF) cc = 0; + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/src/tests/ff.h b/src/tests/ff.h new file mode 100644 index 0000000..55abce9 --- /dev/null +++ b/src/tests/ff.h @@ -0,0 +1,547 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.08a (C)ChaN, 2010 +/----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2010, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/----------------------------------------------------------------------------*/ + +#ifndef _FATFS +#define _FATFS 8255 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONF +#error Wrong configuration file (ffconf.h). +#endif + + +/* DBCS code ranges and SBCS extend char conversion table */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + + +/* Definitions of volume management */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ +#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive# */ +#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition# */ +typedef struct { + BYTE pd; /* Physical drive# */ + BYTE pt; /* Partition # (0-3) */ +} PARTITION; +extern const PARTITION VolToPart[]; /* Volume - Physical location resolution table */ + +#else /* Single partition configuration */ +#define LD2PD(vol) (vol) /* Logical drive# is bound to the same physical drive# */ +#define LD2PT(vol) 0 /* Always mounts the 1st partition */ + +#endif + + + +/* Type of path name strings on FatFs API */ + +#if _LFN_UNICODE /* Unicode string */ +#if !_USE_LFN +#error _LFN_UNICODE must be 0 in non-LFN cfg. +#endif +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#endif + +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + +#endif + + + +/* File system object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* FAT sub-type (0:Not mounted) */ + BYTE drv; /* Physical drive number */ + BYTE csize; /* Sectors per cluster (1,2,4...128) */ + BYTE n_fats; /* Number of FAT copies (1,2) */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ +#if _MAX_SS != 512 + WORD ssize; /* Bytes per sector (512,1024,2048,4096) */ +#endif +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !_FS_READONLY + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector (FAT32) */ +#endif +#if _FS_RPATH + DWORD cdir; /* Current directory start cluster (0:root) */ +#endif + DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */ + DWORD fsize; /* Sectors per FAT */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and Data on tiny cfg) */ +} FATFS; + + + +/* File object structure (FIL) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE pad1; + DWORD fptr; /* File read/write pointer (0 on file open) */ + DWORD fsize; /* File size */ + DWORD org_clust; /* File start cluster (0 when fsize==0) */ + DWORD curr_clust; /* Current cluster */ + DWORD dsect; /* Current data sector */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Ponter to the directory entry in the window */ +#endif +#if _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (null on file open) */ +#endif +#if _FS_SHARE + UINT lockid; /* File lock ID (index of file semaphore table) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File data read/write buffer */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + WORD index; /* Current read/write index number */ + DWORD sclust; /* Table start cluster (0:Root dir) */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the current SFN entry in the win[] */ + BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ +#if _USE_LFN + WCHAR* lfn; /* Pointer to the LFN working buffer */ + WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ +#endif +} DIR; + + + +/* File status structure (FILINFO) */ + +typedef struct { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + TCHAR fname[13]; /* Short file name (8.3 format) */ + DWORD clust; /* start cluster */ +#if _USE_LFN + TCHAR* lfname; /* Pointer to the LFN buffer */ + UINT lfsize; /* Size of LFN buffer in TCHAR */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occured in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Acces denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Acces denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume on the physical drive */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file shareing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES /* (18) Number of open files > _FS_SHARE */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +/* Low Level functions */ +FRESULT l_openfilebycluster(FATFS *fs, FIL *fp, const TCHAR *path, DWORD clust, DWORD fsize); /* Open a file by its start cluster using supplied file size */ +FRESULT l_opendirbycluster (FATFS *fs, DIR *dj, const TCHAR *path, DWORD clust); + +/* application level functions */ +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const TCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (DIR*, const TCHAR*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const TCHAR*, FILINFO*); /* Get file status */ + +#if !_FS_READONLY +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_getfree (const TCHAR*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ +FRESULT f_truncate (FIL*); /* Truncate file */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (const TCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const TCHAR*); /* Create a new directory */ +FRESULT f_chmod (const TCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */ +FRESULT f_utime (const TCHAR*, const FILINFO*); /* Change timestamp of the file/dir */ +FRESULT f_rename (const TCHAR*, const TCHAR*); /* Rename/Move a file or directory */ +#endif + +#if _USE_FORWARD +FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ +#endif + +#if _USE_MKFS +FRESULT f_mkfs (BYTE, BYTE, UINT); /* Create a file system on the drive */ +#endif + +#if _FS_RPATH +FRESULT f_chdrive (BYTE); /* Change current drive */ +FRESULT f_chdir (const TCHAR*); /* Change current directory */ +FRESULT f_getcwd (TCHAR*, UINT); /* Get current directory */ +#endif + +#if _USE_STRFUNC +int f_putc (TCHAR, FIL*); /* Put a character to the file */ +int f_puts (const TCHAR*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const TCHAR*, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR*, int, FIL*); /* Get a string from the file */ +#ifndef EOF +#define EOF (-1) +#endif +#endif + +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->fsize) + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !_FS_READONLY +DWORD get_fattime (void); +#endif + +/* Unicode support functions */ +#if _USE_LFN /* Unicode - OEM code conversion */ +WCHAR ff_convert (WCHAR, UINT); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR); /* Unicode upper-case conversion */ +#if _USE_LFN == 3 /* Memory functions */ +void* ff_memalloc (UINT); /* Allocate memory block */ +void ff_memfree (void*); /* Free memory block */ +#endif +#endif + +/* Sync functions */ +#if _FS_REENTRANT +int ff_cre_syncobj (BYTE, _SYNC_t*);/* Create a sync object */ +int ff_del_syncobj (_SYNC_t); /* Delete a sync object */ +int ff_req_grant (_SYNC_t); /* Lock sync object */ +void ff_rel_grant (_SYNC_t); /* Unlock sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#define FA__ERROR 0x80 + +#if !_FS_READONLY +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif + + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* Fast seek function */ +#define CREATE_LINKMAP 0xFFFFFFFF + + +/*--------------------------------*/ +/* Multi-byte word access macros */ + +#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) +#else /* Use byte-by-byte access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8) +#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _FATFS */ diff --git a/src/tests/ffconf.h b/src/tests/ffconf.h new file mode 100644 index 0000000..14551ed --- /dev/null +++ b/src/tests/ffconf.h @@ -0,0 +1,188 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.08a (C)ChaN, 2010 +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FFCONF +#define _FFCONF 8255 /* Revision ID */ + + +/*---------------------------------------------------------------------------/ +/ Function and Buffer Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system +/ object instead of the sector buffer in the individual file object for file +/ data transfer. This reduces memory consumption 512 bytes each file object. */ + + +#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */ +/* Setting _FS_READONLY to 1 defines read only configuration. This removes +/ writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, +/ f_truncate and useless f_getfree. */ + + +#define _FS_MINIMIZE 0 /* 0 to 3 */ +/* The _FS_MINIMIZE option defines minimization level to remove some functions. +/ +/ 0: Full function. +/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename +/ are removed. +/ 2: f_opendir and f_readdir are removed in addition to 1. +/ 3: f_lseek is removed in addition to 2. */ + + +#define _USE_STRFUNC 0 /* 0:Disable or 1/2:Enable */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 0 /* 0:Disable or 1:Enable */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + +#define _USE_FASTSEEK 1 /* 0:Disable or 1:Enable */ +/* To enable fast seek feature, set _USE_FASTSEEK to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#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. +/ +/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +/ 1 - ASCII only (Valid for non LFN cfg.) +*/ + + +#define _USE_LFN 1 /* 0 to 3 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT reentrant. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. To enable LFN, +/ Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. When enable to use heap, memory control functions +/ ff_memalloc() and ff_memfree() must be added to the project. */ + + +#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. */ + + +#define _FS_RPATH 2 /* 0 to 2 */ +/* The _FS_RPATH option configures relative path feature. +/ +/ 0: Disable relative path feature and remove related functions. +/ 1: Enable relative path. f_chdrive() and f_chdir() are available. +/ 2: f_getcwd() is available in addition to 1. +/ +/ Note that output of the f_readdir fnction is affected by this option. */ + + + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* Maximum sector size to be handled. +/ Always set 512 for memory card and hard disk but a larger value may be +/ required for floppy disk (512/1024) and optical disk (512/2048). +/ When _MAX_SS is larger than 512, GET_SECTOR_SIZE command must be implememted +/ to the disk_ioctl function. */ + + +#define _MULTI_PARTITION 0 /* 0:Single partition or 1:Multiple partition */ +/* When set to 0, each volume is bound to the same physical drive number and +/ it can mount only first primaly partition. When it is set to 1, each volume +/ is tied to the partitions listed in VolToPart[]. */ + + +#define _USE_ERASE 0 /* 0:Disable or 1:Enable */ +/* To enable sector erase feature, set _USE_ERASE to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 /* 0 or 1 */ +/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS +/ option defines which access method is used to the word data on the FAT volume. +/ +/ 0: Byte-by-byte access. +/ 1: Word access. Do not choose this unless following condition is met. +/ +/ When the byte order on the memory is big-endian or address miss-aligned word +/ access results incorrect behavior, the _WORD_ACCESS must be set to 0. +/ If it is not the case, the value can also be set to 1 to improve the +/ performance and code size. */ + + +/* Include a header file here to define sync object types on the O/S */ +/* #include , , or ohters. */ + +#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */ +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */ + +/* The _FS_REENTRANT option switches the reentrancy of the FatFs module. +/ +/ 0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect. +/ 1: Enable reentrancy. Also user provided synchronization handlers, +/ ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj +/ function must be added to the project. */ + + +#define _FS_SHARE 0 /* 0:Disable or >=1:Enable */ +/* To enable file shareing feature, set _FS_SHARE to 1 or greater. The value + defines how many files can be opened simultaneously. */ + + +#endif /* _FFCONFIG */ diff --git a/src/tests/fileops.c b/src/tests/fileops.c new file mode 100644 index 0000000..4e681d1 --- /dev/null +++ b/src/tests/fileops.c @@ -0,0 +1,117 @@ +/* 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" +#include "diskio.h" + +/* +WCHAR ff_convert(WCHAR w, UINT dir) { + return w; +}*/ + +int newcard; + +void file_init() { + file_res=f_mount(0, &fatfs); + newcard = 0; +} + +void file_reinit(void) { + disk_init(); + file_init(); +} + +FRESULT dir_open_by_filinfo(DIR* dir, FILINFO* fno) { + return l_opendirbycluster(&fatfs, dir, (TCHAR*)"", fno->clust); +} + +void file_open_by_filinfo(FILINFO* fno) { + file_res = l_openfilebycluster(&fatfs, &file_handle, (TCHAR*)"", fno->clust, fno->fsize); +} + +void file_open(uint8_t* filename, BYTE flags) { + if (disk_state == DISK_CHANGED) { + file_reinit(); + newcard = 1; + } + file_res = f_open(&file_handle, (TCHAR*)filename, flags); + file_block_off = sizeof(file_buf); + file_block_max = sizeof(file_buf); + file_status = file_res ? FILE_ERR : FILE_OK; +} + +void file_close() { + file_res = f_close(&file_handle); +} + +void file_seek(uint32_t offset) { + file_res = f_lseek(&file_handle, (DWORD)offset); +} + +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); + if(bytes_written < sizeof(file_buf)) { + printf("wrote less than expected - card full?\n"); + } + 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; +} + +uint8_t file_getc() { + if(file_block_off == file_block_max) { + file_block_max = file_read(); + if(file_block_max == 0) file_status = FILE_EOF; + file_block_off = 0; + } + return file_buf[file_block_off++]; +} diff --git a/src/tests/fileops.h b/src/tests/fileops.h new file mode 100644 index 0000000..633ee25 --- /dev/null +++ b/src/tests/fileops.h @@ -0,0 +1,54 @@ +/* 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 +#include "ff.h" + +enum filestates { FILE_OK=0, FILE_ERR, FILE_EOF }; + +BYTE file_buf[512]; +FATFS fatfs; +FIL file_handle; +FRESULT file_res; +uint8_t file_lfn[258]; +uint16_t file_block_off, file_block_max; +enum filestates file_status; + +void file_init(void); +void file_open(uint8_t* filename, BYTE flags); +FRESULT dir_open_by_filinfo(DIR* dir, FILINFO* fno_param); +void file_open_by_filinfo(FILINFO* fno); +void file_close(void); +void file_seek(uint32_t offset); +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); + +uint8_t file_getc(void); +#endif diff --git a/src/tests/filetypes.c b/src/tests/filetypes.c new file mode 100644 index 0000000..fca7dfc --- /dev/null +++ b/src/tests/filetypes.c @@ -0,0 +1,315 @@ +/* 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 "config.h" +#include "uart.h" +#include "filetypes.h" +#include "ff.h" +#include "smc.h" +#include "fileops.h" +#include "crc32.h" +#include "memory.h" +#include "led.h" +#include "sort.h" + +uint16_t scan_flat(const char* path) { + DIR dir; + FRESULT res; + FILINFO fno; + fno.lfname = NULL; + res = f_opendir(&dir, (TCHAR*)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; +} + +uint32_t scan_dir(char* path, FILINFO* fno_param, char mkdb, uint32_t this_dir_tgt) { + DIR dir; + FILINFO fno; + FRESULT res; + uint8_t len; + TCHAR* fn; + static unsigned char depth = 0; + static uint32_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; + char buf[7]; + char *size_units[3] = {" ", "k", "M"}; + uint32_t entry_fsize; + uint8_t entry_unit_idx; + + 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; + printf("root dir @%lx\n", dir_tgt); + } + + fno.lfsize = 255; + fno.lfname = (TCHAR*)file_lfn; + numentries=0; + for(pass = 0; pass < 2; pass++) { + if(pass) { + dirsize = 4*(numentries); + next_subdir_tgt += dirsize + 4; + if(parent_tgt) next_subdir_tgt += 4; + if(next_subdir_tgt > dir_end) { + dir_end = next_subdir_tgt; + } +// printf("path=%s depth=%d ptr=%lx entries=%d parent=%lx next subdir @%lx\n", path, depth, db_tgt, numentries, parent_tgt, next_subdir_tgt); + if(mkdb) { +// printf("d=%d Saving %lx to Address %lx [end]\n", depth, 0L, next_subdir_tgt - 4); + sram_writelong(0L, next_subdir_tgt - 4); + } + } + if(fno_param) { + res = dir_open_by_filinfo(&dir, fno_param); + } else { + res = f_opendir(&dir, 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)) { + printf("switch! old=%lx ", db_tgt); + db_tgt &= 0xffff0000; + db_tgt += 0x00010000; + printf("new=%lx\n", db_tgt); + } +// printf("writing link to parent, %lx to address %lx [../]\n", parent_tgt-SRAM_MENU_ADDR, 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)0x81<<24), dir_tgt); + db_tgt += sizeof(next_subdir_tgt)+sizeof(len)+4; + dir_tgt += 4; + } + len = strlen((char*)path); + for (;;) { +// toggle_read_led(); + res = f_readdir(&dir, &fno); + if (res != FR_OK || fno.fname[0] == 0) { + if(pass) { + if(!numentries) was_empty=1; + } + break; + } + fn = *fno.lfname ? fno.lfname : fno.fname; + if ((*fn == '.') || !(memcmp(fn, SYS_DIR_NAME, sizeof(SYS_DIR_NAME)))) continue; + if (fno.fattrib & AM_DIR) { + depth++; + if(depth < FS_MAX_DEPTH) { + numentries++; + if(pass) { + path[len]='/'; + strncpy(path+len+1, (char*)fn, sizeof(fs_path)-len); + if(mkdb) { + uint16_t pathlen = strlen(path); +// printf("d=%d Saving %lx to Address %lx [dir]\n", depth, db_tgt, dir_tgt); + /* save element: + - path name + - pointer to sub dir structure */ + if((db_tgt&0xffff) > ((0x10000-(sizeof(next_subdir_tgt) + sizeof(len) + pathlen + 2))&0xffff)) { + printf("switch! old=%lx ", db_tgt); + db_tgt &= 0xffff0000; + db_tgt += 0x00010000; + printf("new=%lx\n", db_tgt); + } +// printf(" Saving dir descriptor to %lx tgt=%lx, path=%s\n", db_tgt, next_subdir_tgt, path); + /* write element pointer to current dir structure */ + sram_writelong((db_tgt-SRAM_MENU_ADDR)|((uint32_t)0x80<<24), dir_tgt); + /* save element: + - path name + - pointer to sub dir structure */ + 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); + db_tgt += sizeof(next_subdir_tgt) + sizeof(len) + pathlen + 2; + } + parent_tgt = this_dir_tgt; + scan_dir(path, &fno, mkdb, next_subdir_tgt); + dir_tgt += 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_IPS: + case TYPE_SMC: +/* file_open_by_filinfo(&fno); + if(file_res){ + printf("ZOMG NOOOO %d\n", file_res); + } + smc_id(&romprops); + file_close(); */ + + /* write element pointer to current dir structure */ + DBG_FS printf("d=%d Saving %lX to Address %lX [file %s]\n", depth, db_tgt, dir_tgt, path); + if((db_tgt&0xffff) > ((0x10000-(sizeof(len) + pathlen + sizeof(buf)-1 + 1))&0xffff)) { + printf("switch! old=%lx ", db_tgt); + db_tgt &= 0xffff0000; + db_tgt += 0x00010000; + printf("new=%lx\n", db_tgt); + } + sram_writelong((db_tgt-SRAM_MENU_ADDR) | ((uint32_t)type << 24), dir_tgt); + dir_tgt += 4; + /* save element: + - index of last slash character + - file name + - file size */ +/* sram_writeblock((uint8_t*)&romprops, db_tgt, sizeof(romprops)); */ + entry_fsize = fno.fsize; + entry_unit_idx = 0; + while(entry_fsize > 9999) { + entry_fsize >>= 10; + entry_unit_idx++; + } + snprintf(buf, sizeof(buf), "% 5ld", entry_fsize); + strncat(buf, size_units[entry_unit_idx], 1); + sram_writeblock(buf, db_tgt, sizeof(buf)-1); + sram_writebyte(len+1, db_tgt + sizeof(buf)-1); + sram_writeblock(path, db_tgt + sizeof(len) + sizeof(buf)-1, pathlen + 1); +// sram_writelong(fno.fsize, db_tgt + sizeof(len) + pathlen + 1); + db_tgt += sizeof(len) + pathlen + sizeof(buf)-1 + 1; + break; + case TYPE_UNKNOWN: + default: + break; + } + path[len]=0; +/* printf("%s ", path); + _delay_ms(30); */ + } + } else { + TCHAR* fn2 = fn; + while(*fn2 != 0) { + crc += crc32_update(crc, *((unsigned char*)fn2++)); + } + } + } +/* printf("%s/%s\n", path, fn); + _delay_ms(50); */ + } + } + } else uart_putc(0x30+res); + } +// printf("db_tgt=%lx dir_end=%lx\n", db_tgt, dir_end); + 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(ext+1, "SMC")) + ||(!strcasecmp(ext+1, "SFC")) + ||(!strcasecmp(ext+1, "FIG")) + ||(!strcasecmp(ext+1, "BS")) + ) { + return TYPE_SMC; + } + if( (!strcasecmp(ext+1, "IPS")) + ||(!strcasecmp(ext+1, "UPS")) + ) { + return TYPE_IPS; + } + /* 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(uint32_t* id) { + file_open((uint8_t*)"/sd2snes/sd2snes.db", FA_READ); + if(file_res == FR_OK) { + file_readblock(id, 0, 4); +/* XXX */// *id=0xdead; + file_close(); + } else { + *id=0xdeadbeef; + } + return file_res; +} + +int get_num_dirent(uint32_t addr) { + int result = 0; + while(sram_readlong(addr+result*4)) { + result++; + } + return result; +} + +void sort_all_dir(uint32_t endaddr) { + uint32_t entries = 0; + uint32_t current_base = SRAM_DIR_ADDR; + while(current_base<(endaddr)) { + while(sram_readlong(current_base+entries*4)) { + entries++; + } + printf("sorting dir @%lx, entries: %ld\n", current_base, entries); + sort_dir(current_base, entries); + current_base += 4*entries + 4; + entries = 0; + } +} diff --git a/src/tests/filetypes.h b/src/tests/filetypes.h new file mode 100644 index 0000000..d42546a --- /dev/null +++ b/src/tests/filetypes.h @@ -0,0 +1,58 @@ +/* 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 + +#ifdef DEBUG_FS +#define DBG_FS +#else +#define DBG_FS while(0) +#endif + +#include "ff.h" + +#define FS_MAX_DEPTH (10) +#define SYS_DIR_NAME ((const uint8_t*)"sd2snes") +typedef enum { + TYPE_UNKNOWN = 0, /* 0 */ + TYPE_SMC, /* 1 */ + TYPE_SRM, /* 2 */ + TYPE_SPC, /* 3 */ + TYPE_IPS /* 4 */ +} SNES_FTYPE; + + +char fs_path[256]; +SNES_FTYPE determine_filetype(char* filename); +//uint32_t scan_fs(); +uint16_t scan_flat(const char* path); +uint32_t scan_dir(char* path, FILINFO* fno_param, char mkdb, uint32_t this_subdir_tgt); +FRESULT get_db_id(uint32_t*); +int get_num_dirent(uint32_t addr); +void sort_all_dir(uint32_t endaddr); + +#endif diff --git a/src/tests/flash.cfg b/src/tests/flash.cfg new file mode 100644 index 0000000..2973ea1 --- /dev/null +++ b/src/tests/flash.cfg @@ -0,0 +1,4 @@ +# script running on reset +init +script flash.script + diff --git a/src/tests/flash.script b/src/tests/flash.script new file mode 100644 index 0000000..7d90b6e --- /dev/null +++ b/src/tests/flash.script @@ -0,0 +1,12 @@ +# mthomas 4/2008, tested with OpenOCD SVN555 + +#flash probe 0 +#flash erase_check 0 +#flash protect_check 0 +#flash info 0 + +reset init +flash write_image erase unlock obj/sd2snes.bin 0 +reset run +shutdown + diff --git a/src/tests/fpga.c b/src/tests/fpga.c new file mode 100644 index 0000000..b2328a4 --- /dev/null +++ b/src/tests/fpga.c @@ -0,0 +1,143 @@ +/* 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 + + fpga.c: FPGA (re)configuration +*/ + + +/* + FPGA pin mapping + ================ + CCLK P0.11 out + PROG_B P1.15 out + INIT_B P2.9 in + DIN P2.8 out + DONE P0.22 in + */ + +#include +#include "bits.h" + +#include "fpga.h" +#include "fpga_spi.h" +#include "config.h" +#include "uart.h" +#include "diskio.h" +#include "integer.h" +#include "ff.h" +#include "fileops.h" +#include "spi.h" +#include "led.h" +#include "timer.h" +#include "rle.h" + +void fpga_set_prog_b(uint8_t val) { + if(val) + BITBAND(PROGBREG->FIOSET, PROGBBIT) = 1; + else + BITBAND(PROGBREG->FIOCLR, PROGBBIT) = 1; +} + +void fpga_set_cclk(uint8_t val) { + if(val) + BITBAND(CCLKREG->FIOSET, CCLKBIT) = 1; + else + BITBAND(CCLKREG->FIOCLR, CCLKBIT) = 1; +} + +int fpga_get_initb() { + return BITBAND(INITBREG->FIOPIN, INITBBIT); +} + +void fpga_init() { +/* mainly GPIO directions */ + BITBAND(CCLKREG->FIODIR, CCLKBIT) = 1; /* CCLK */ + BITBAND(DONEREG->FIODIR, DONEBIT) = 0; /* DONE */ + BITBAND(PROGBREG->FIODIR, PROGBBIT) = 1; /* PROG_B */ + BITBAND(DINREG->FIODIR, DINBIT) = 1; /* DIN */ + BITBAND(INITBREG->FIODIR, INITBBIT) = 0; /* INIT_B */ + + LPC_GPIO2->FIOMASK1 = 0; + + SPI_OFFLOAD=0; + fpga_set_cclk(0); /* initial clk=0 */ +} + +int fpga_get_done(void) { + return BITBAND(DONEREG->FIOPIN, DONEBIT); +} + +void fpga_postinit() { + LPC_GPIO2->FIOMASK1 = 0; +} + +void fpga_pgm(uint8_t* filename) { + int MAXRETRIES = 10; + int retries = MAXRETRIES; + uint8_t data; + int i; + tick_t timeout; + do { + i=0; + timeout = getticks() + 100; + fpga_set_prog_b(0); + uart_putc('P'); + fpga_set_prog_b(1); + while(!fpga_get_initb()){ + if(getticks() > timeout) { + printf("no response from FPGA trying to initiate configuration!\n"); + led_panic(); + } + }; + LPC_GPIO2->FIOMASK1 = ~(BV(0)); + uart_putc('p'); + + + /* open configware file */ + file_open(filename, FA_READ); + if(file_res) { + uart_putc('?'); + uart_putc(0x30+file_res); + return; + } + uart_putc('C'); + + for (;;) { + data = rle_file_getc(); + i++; + if (file_status || file_res) break; /* error or eof */ + FPGA_SEND_BYTE_SERIAL(data); + } + uart_putc('c'); + file_close(); + printf("fpga_pgm: %d bytes programmed\n", i); + delay_ms(1); + } while (!fpga_get_done() && retries--); + if(!fpga_get_done()) { + printf("FPGA failed to configure after %d tries.\n", MAXRETRIES); + led_panic(); + } + printf("FPGA configured\n"); + fpga_postinit(); +} + diff --git a/src/tests/fpga.h b/src/tests/fpga.h new file mode 100644 index 0000000..c0b8ecd --- /dev/null +++ b/src/tests/fpga.h @@ -0,0 +1,68 @@ +/* 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 + + fpga.h: FPGA (re)configuration +*/ + +#ifndef FPGA_H +#define FPGA_H + +#include +#include "bits.h" + +void fpga_set_prog_b(uint8_t val); +void fpga_set_cclk(uint8_t val); +int fpga_get_initb(void); + +void fpga_init(void); +void fpga_postinit(void); +void fpga_pgm(uint8_t* filename); + +uint8_t SPI_OFFLOAD; + +#define CCLKREG LPC_GPIO0 +#define PROGBREG LPC_GPIO1 +#define INITBREG LPC_GPIO2 +#define DINREG LPC_GPIO2 +#define DONEREG LPC_GPIO0 + +#define CCLKBIT (11) +#define PROGBBIT (15) +#define INITBBIT (9) +#define DINBIT (8) +#define DONEBIT (22) + + +#define FPGA_TEST_TOKEN (0xa5) + +// some macros for bulk transfers (faster) +#define FPGA_SEND_BYTE_SERIAL(data) do {SET_FPGA_DIN(data>>7); CCLK();\ +SET_FPGA_DIN(data>>6); CCLK(); SET_FPGA_DIN(data>>5); CCLK();\ +SET_FPGA_DIN(data>>4); CCLK(); SET_FPGA_DIN(data>>3); CCLK();\ +SET_FPGA_DIN(data>>2); CCLK(); SET_FPGA_DIN(data>>1); CCLK();\ +SET_FPGA_DIN(data); CCLK();} while (0) +#define SET_CCLK() do {BITBAND(LPC_GPIO0->FIOSET, 11) = 1;} while (0) +#define CLR_CCLK() do {BITBAND(LPC_GPIO0->FIOCLR, 11) = 1;} while (0) +#define CCLK() do {SET_CCLK(); CLR_CCLK();} while (0) +#define SET_FPGA_DIN(data) do {LPC_GPIO2->FIOPIN1 = data;} while (0) +#endif diff --git a/src/tests/fpga_spi.c b/src/tests/fpga_spi.c new file mode 100644 index 0000000..380427e --- /dev/null +++ b/src/tests/fpga_spi.c @@ -0,0 +1,528 @@ +/* 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 + + fpga_spi.h: functions for SPI ctrl, SRAM interfacing and feature configuration +*/ +/* + + SPI commands + + cmd param function + ============================================= + 0t bbhhll set address to 0xbbhhll + t = target + target: 0 = RAM + 1 = MSU Audio buffer + 2 = MSU Data buffer + targets 1 & 2 only require 2 address bytes to + be written. + + 10 bbhhll set SNES input address mask to 0xbbhhll + 20 bbhhll set SRAM address mask to 0xbbhhll + + 3m - set mapper to m + 0=HiROM, 1=LoROM, 2=ExHiROM, 6=SF96, 7=Menu + + 4s - trigger SD DMA (512b from SD to memory) + s: Bit 2 = partial, Bit 1:0 = target + target: see above + + 60 sssseeee set SD DMA partial transfer start+end + ssss = start offset (msb first) + eeee = end offset (msb first) + + 8p - read (RAM only) + p: 0 = no increment after read + 8 = increment after read + + 9p {xx}* write xx + p: i-tt + tt = target (see above) + i = increment (see above) + + E0 ssrr set MSU-1 status register (=FPGA status [7:0]) + ss = bits to set in status register (1=set) + rr = bits to reset in status register (1=reset) + + E1 - pause DAC + E2 - resume/play DAC + E3 - reset DAC playback pointer (0) + E4 hhll set MSU read pointer + + E5 tt{7} set RTC (SPC7110 format + 1000s of year, + nibbles packed) + eg 0x20111210094816 is 2011-12-10, 9:48:16 + E6 ssrr set/reset BS-X status register [7:0] + E7 - reset SRTC state + E8 - reset DSP program and data ROM write pointers + E9 hhmmllxxxx write+incr. DSP program ROM (xxxx=dummy writes) + EA hhllxxxx write+incr. DSP data ROM (xxxx=dummy writes) + EB - put DSP into reset + EC - release DSP from reset + ED - set feature enable bits (see below) + EE - select memory (0: ROM (PSRAM), 1: RAM (SRAM)) + F0 - receive test token (to see if FPGA is alive) + F1 - receive status (16bit, MSB first), see below + + F2 - get MSU data address (32bit, MSB first) + F3 - get MSU audio track no. (16bit, MSB first) + F4 - get MSU volume (8bit) + + FE - get SNES master clock frequency (32bit, MSB first) + measured 1x/sec + FF {xx}* echo (returns the sent data in the next byte) + + FPGA status word: + bit function + ========================================================================== + 15 SD DMA busy (0=idle, 1=busy) + 14 DAC read pointer MSB + 13 MSU read pointer MSB + 12 [TODO SD DMA CRC status (0=ok, 1=error); valid after bit 15 -> 0] + 11 reserved (0) + 10 reserved (0) + 9 reserved (0) + 8 reserved (0) + 7 reserved (0) + 6 reserved (0) + 5 MSU1 Audio request from SNES + 4 MSU1 Data request from SNES + 3 reserved (0) + 2 MSU1 Audio control status: 0=no repeat, 1=repeat + 1 MSU1 Audio control status: 0=pause, 1=play + 0 MSU1 Audio control request + + FPGA feature enable bits: + bit function + ========================================================================== + 7 - + 6 - + 5 - + 4 - + 3 enable MSU1 registers + 2 enable SRTC registers + 1 enable ST0010 mapping + 0 enable DSPx mapping + +*/ + +#include +#include "bits.h" +#include "fpga.h" +#include "config.h" +#include "uart.h" +#include "spi.h" +#include "fpga_spi.h" +#include "timer.h" +#include "sdnative.h" + +void fpga_spi_init(void) { + spi_init(SPI_SPEED_FAST); + BITBAND(FPGA_MCU_RDY_REG->FIODIR, FPGA_MCU_RDY_BIT) = 0; +} + +void set_msu_addr(uint16_t address) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x02); + FPGA_TX_BYTE((address>>8)&0xff); + FPGA_TX_BYTE((address)&0xff); + FPGA_DESELECT(); +} + +void set_dac_addr(uint16_t address) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x01); + FPGA_TX_BYTE((address>>8)&0xff); + FPGA_TX_BYTE((address)&0xff); + FPGA_DESELECT(); +} + +void set_mcu_addr(uint32_t address) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x00); + FPGA_TX_BYTE((address>>16)&0xff); + FPGA_TX_BYTE((address>>8)&0xff); + FPGA_TX_BYTE((address)&0xff); + FPGA_DESELECT(); +} + +void set_saveram_mask(uint32_t mask) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x20); + FPGA_TX_BYTE((mask>>16)&0xff); + FPGA_TX_BYTE((mask>>8)&0xff); + FPGA_TX_BYTE((mask)&0xff); + FPGA_DESELECT(); +} + +void set_rom_mask(uint32_t mask) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x10); + FPGA_TX_BYTE((mask>>16)&0xff); + FPGA_TX_BYTE((mask>>8)&0xff); + FPGA_TX_BYTE((mask)&0xff); + FPGA_DESELECT(); +} + +void set_mapper(uint8_t val) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x30 | (val & 0x0f)); + FPGA_DESELECT(); +} + +uint8_t fpga_test() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF0); /* TEST */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint8_t result = FPGA_RX_BYTE(); + FPGA_DESELECT(); + return result; +} + +uint16_t fpga_status() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF1); /* STATUS */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint16_t result = (FPGA_RX_BYTE()) << 8; + result |= FPGA_RX_BYTE(); + FPGA_DESELECT(); + return result; +} + +void fpga_set_sddma_range(uint16_t start, uint16_t end) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x60); /* DMA_RANGE */ + FPGA_TX_BYTE(start>>8); + FPGA_TX_BYTE(start&0xff); + FPGA_TX_BYTE(end>>8); + FPGA_TX_BYTE(end&0xff); +//if(tgt==1 && (test=FPGA_RX_BYTE()) != 0x41) printf("!!!!!!!!!!!!!!! -%02x- \n", test); + FPGA_DESELECT(); +} + +void fpga_sddma(uint8_t tgt, uint8_t partial) { + uint32_t test = 0; + uint8_t status = 0; + BITBAND(SD_CLKREG->FIODIR, SD_CLKPIN) = 0; + FPGA_SELECT(); + FPGA_TX_BYTE(0x40 | (tgt & 0x3) | ((partial & 1) << 2) ); /* DO DMA */ + FPGA_TX_BYTE(0x00); /* dummy for falling DMA_EN edge */ +//if(tgt==1 && (test=FPGA_RX_BYTE()) != 0x41) printf("!!!!!!!!!!!!!!! -%02x- \n", test); + FPGA_DESELECT(); + FPGA_SELECT(); + FPGA_TX_BYTE(0xF1); /* STATUS */ + FPGA_TX_BYTE(0x00); /* dummy */ + DBG_SD printf("FPGA DMA request sent, wait for completion..."); + while((status=FPGA_RX_BYTE()) & 0x80) { + FPGA_RX_BYTE(); /* eat the 2nd status byte */ + test++; + } + DBG_SD printf("...complete\n"); + FPGA_DESELECT(); + if(test<5)printf("loopy: %ld %02x\n", test, status); + BITBAND(SD_CLKREG->FIODIR, SD_CLKPIN) = 1; +} + +void set_dac_vol(uint8_t volume) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x50); + FPGA_TX_BYTE(volume); + FPGA_TX_BYTE(0x00); /* latch rise */ + FPGA_TX_BYTE(0x00); /* latch fall */ + FPGA_DESELECT(); +} + +void dac_play() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe2); + FPGA_TX_BYTE(0x00); /* latch reset */ + FPGA_DESELECT(); +} + +void dac_pause() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe1); + FPGA_TX_BYTE(0x00); /* latch reset */ + FPGA_DESELECT(); +} + +void dac_reset() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe3); + FPGA_TX_BYTE(0x00); /* latch reset */ + FPGA_TX_BYTE(0x00); /* latch reset */ + FPGA_DESELECT(); +} + +void msu_reset(uint16_t address) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe4); + FPGA_TX_BYTE((address>>8) & 0xff); /* address hi */ + FPGA_TX_BYTE(address & 0xff); /* address lo */ + FPGA_TX_BYTE(0x00); /* latch reset */ + FPGA_TX_BYTE(0x00); /* latch reset */ + FPGA_DESELECT(); +} + +void set_msu_status(uint8_t set, uint8_t reset) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe0); + FPGA_TX_BYTE(set); + FPGA_TX_BYTE(reset); + FPGA_TX_BYTE(0x00); /* latch reset */ + FPGA_DESELECT(); +} + +uint8_t get_msu_volume() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF4); /* MSU_VOLUME */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint8_t result = FPGA_RX_BYTE(); + FPGA_DESELECT(); + return result; +} + +uint16_t get_msu_track() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF3); /* MSU_TRACK */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint16_t result = (FPGA_RX_BYTE()) << 8; + result |= FPGA_RX_BYTE(); + FPGA_DESELECT(); + return result; +} + +uint32_t get_msu_offset() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF2); /* MSU_OFFSET */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +uint32_t get_snes_romselclk() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF7); + FPGA_TX_BYTE(0x00); /* dummy */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +uint32_t get_snes_cpuclk() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF8); + FPGA_TX_BYTE(0x00); /* dummy */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +uint32_t get_snes_readclk() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF9); + FPGA_TX_BYTE(0x00); /* dummy */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +uint32_t get_snes_writeclk() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xFA); /* GET_SYSCLK */ + FPGA_TX_BYTE(0x00); /* dummy */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +uint32_t get_snes_pardclk() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xFB); /* GET_SYSCLK */ + FPGA_TX_BYTE(0x00); /* dummy */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +uint32_t get_snes_pawrclk() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xFC); /* GET_SYSCLK */ + FPGA_TX_BYTE(0x00); /* dummy */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +uint32_t get_snes_refreshclk() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xFD); /* GET_SYSCLK */ + FPGA_TX_BYTE(0x00); /* dummy */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +uint32_t get_snes_sysclk() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xFE); /* GET_SYSCLK */ + FPGA_TX_BYTE(0x00); /* dummy */ + FPGA_TX_BYTE(0x00); /* dummy */ + uint32_t result = (FPGA_RX_BYTE()) << 24; + result |= (FPGA_RX_BYTE()) << 16; + result |= (FPGA_RX_BYTE()) << 8; + result |= (FPGA_RX_BYTE()); + FPGA_DESELECT(); + return result; +} + +void set_bsx_regs(uint8_t set, uint8_t reset) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe6); + FPGA_TX_BYTE(set); + FPGA_TX_BYTE(reset); + FPGA_TX_BYTE(0x00); /* latch reset */ + FPGA_DESELECT(); +} + +void set_fpga_time(uint64_t time) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe5); + FPGA_TX_BYTE((time >> 48) & 0xff); + FPGA_TX_BYTE((time >> 40) & 0xff); + FPGA_TX_BYTE((time >> 32) & 0xff); + FPGA_TX_BYTE((time >> 24) & 0xff); + FPGA_TX_BYTE((time >> 16) & 0xff); + FPGA_TX_BYTE((time >> 8) & 0xff); + FPGA_TX_BYTE(time & 0xff); + FPGA_TX_BYTE(0x00); + FPGA_DESELECT(); +} + +void fpga_reset_srtc_state() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe7); + FPGA_TX_BYTE(0x00); + FPGA_TX_BYTE(0x00); + FPGA_DESELECT(); +} + +void fpga_reset_dspx_addr() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe8); + FPGA_TX_BYTE(0x00); + FPGA_TX_BYTE(0x00); + FPGA_DESELECT(); +} + +void fpga_write_bram_data(uint8_t data) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe9); + FPGA_TX_BYTE(data); + FPGA_TX_BYTE(0x00); + FPGA_TX_BYTE(0x00); + FPGA_DESELECT(); +} + +void fpga_write_dspx_dat(uint16_t data) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xea); + FPGA_TX_BYTE((data>>8)&0xff); + FPGA_TX_BYTE((data)&0xff); + FPGA_TX_BYTE(0x00); + FPGA_TX_BYTE(0x00); + FPGA_DESELECT(); +} + +void fpga_dspx_reset(uint8_t reset) { + FPGA_SELECT(); + FPGA_TX_BYTE(reset ? 0xeb : 0xec); + FPGA_TX_BYTE(0x00); + FPGA_DESELECT(); +} + +void fpga_set_features(uint8_t feat) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xed); + FPGA_TX_BYTE(feat); + FPGA_DESELECT(); +} + +void fpga_select_mem(uint8_t unit) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xee); + FPGA_TX_BYTE(unit); + FPGA_DESELECT(); +} + +void fpga_set_bram_addr(uint16_t addr) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xe8); + FPGA_TX_BYTE((addr>>8)&0xff); + FPGA_TX_BYTE((addr)&0xff); + FPGA_TX_BYTE(0x00); + FPGA_DESELECT(); +} + +uint8_t fpga_read_bram_data() { + uint8_t data; + FPGA_SELECT(); + FPGA_TX_BYTE(0xf5); + FPGA_TX_BYTE(0x00); + data = FPGA_RX_BYTE(); + FPGA_TX_BYTE(0x00); + FPGA_DESELECT(); + return data; +} diff --git a/src/tests/fpga_spi.h b/src/tests/fpga_spi.h new file mode 100644 index 0000000..6d9922e --- /dev/null +++ b/src/tests/fpga_spi.h @@ -0,0 +1,104 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + uC 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 + + fpga_spi.h: functions for SPI ctrl, SRAM interfacing and feature configuration +*/ + +#ifndef _FPGA_SPI_H +#define _FPGA_SPI_H + +#include +#include "bits.h" +#include "spi.h" +#include "config.h" + +#define FPGA_SS_BIT 16 +#define FPGA_SS_REG LPC_GPIO0 + +#define FPGA_SELECT() do {FPGA_TX_SYNC(); BITBAND(FPGA_SS_REG->FIOCLR, FPGA_SS_BIT) = 1;} while (0) +#define FPGA_SELECT_ASYNC() do {BITBAND(FPGA_SS_REG->FIOCLR, FPGA_SS_BIT) = 1;} while (0) +#define FPGA_DESELECT() do {FPGA_TX_SYNC(); BITBAND(FPGA_SS_REG->FIOSET, FPGA_SS_BIT) = 1;} while (0) +#define FPGA_DESELECT_ASYNC() do {BITBAND(FPGA_SS_REG->FIOSET, FPGA_SS_BIT) = 1;} while (0) + +#define FPGA_TX_SYNC() spi_tx_sync() +#define FPGA_TX_BYTE(x) spi_tx_byte(x) +#define FPGA_RX_BYTE() spi_rx_byte() +#define FPGA_TXRX_BYTE(x) spi_txrx_byte(x) +#define FPGA_TX_BLOCK(x,y) spi_tx_block(x,y) +#define FPGA_RX_BLOCK(x,y) spi_rx_block(x,y) + +#define FPGA_SPI_FAST() spi_set_speed(SPI_SPEED_FPGA_FAST) +#define FPGA_SPI_SLOW() spi_set_speed(SPI_SPEED_FPGA_SLOW) + +#define FEAT_CX4 (1 << 4) +#define FEAT_MSU1 (1 << 3) +#define FEAT_SRTC (1 << 2) +#define FEAT_ST0010 (1 << 1) +#define FEAT_DSPX (1 << 0) + +#define FPGA_WAIT_RDY() do {while(BITBAND(SSP_REGS->SR, SSP_BSY)); while(!BITBAND(FPGA_MCU_RDY_REG->FIOPIN, FPGA_MCU_RDY_BIT));} while (0) + +void fpga_spi_init(void); +uint8_t fpga_test(void); +uint16_t fpga_status(void); +void spi_fpga(void); +void spi_sd(void); +void spi_none(void); +void set_mcu_addr(uint32_t); +void set_dac_addr(uint16_t); +void set_dac_vol(uint8_t); +void dac_play(void); +void dac_pause(void); +void dac_reset(void); +void msu_reset(uint16_t); +void set_msu_addr(uint16_t); +void set_msu_status(uint8_t set, uint8_t reset); +void set_saveram_mask(uint32_t); +void set_rom_mask(uint32_t); +void set_mapper(uint8_t val); +void fpga_sddma(uint8_t tgt, uint8_t partial); +void fpga_set_sddma_range(uint16_t start, uint16_t end); +uint8_t get_msu_volume(void); +uint16_t get_msu_track(void); +uint32_t get_msu_offset(void); +uint32_t get_snes_sysclk(void); +uint32_t get_snes_readclk(void); +uint32_t get_snes_writeclk(void); +uint32_t get_snes_pardclk(void); +uint32_t get_snes_pawrclk(void); +uint32_t get_snes_refreshclk(void); +uint32_t get_snes_cpuclk(void); +uint32_t get_snes_romselclk(void); +void set_bsx_regs(uint8_t set, uint8_t reset); +void set_fpga_time(uint64_t time); +void fpga_reset_srtc_state(void); +void fpga_reset_dspx_addr(void); +void fpga_write_dspx_pgm(uint32_t data); +void fpga_write_dspx_dat(uint16_t data); +void fpga_dspx_reset(uint8_t reset); +void fpga_set_features(uint8_t feat); +void fpga_select_mem(uint8_t unit); +void fpga_set_bram_addr(uint16_t addr); +uint8_t fpga_read_bram_data(void); +void fpga_write_bram_data(uint8_t data); +#endif diff --git a/src/tests/integer.h b/src/tests/integer.h new file mode 100644 index 0000000..ccc97d3 --- /dev/null +++ b/src/tests/integer.h @@ -0,0 +1,37 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER +#define _INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include + +#else /* Embedded platform */ + +/* These types must be 16-bit, 32-bit or larger integer */ +typedef int INT; +typedef unsigned int UINT; + +/* These types must be 8-bit integer */ +typedef char CHAR; +typedef unsigned char UCHAR; +typedef unsigned char BYTE; + +/* These types must be 16-bit integer */ +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types must be 32-bit integer */ +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long DWORD; + +#endif + +#endif diff --git a/src/tests/irq.c b/src/tests/irq.c new file mode 100644 index 0000000..b4f3ee8 --- /dev/null +++ b/src/tests/irq.c @@ -0,0 +1,15 @@ +#include +#include "bits.h" +#include "config.h" +#include "sdnative.h" +#include "uart.h" + +void EINT3_IRQHandler(void) { + NVIC_ClearPendingIRQ(EINT3_IRQn); + if(SD_CHANGE_DETECT) { + SD_CHANGE_CLR(); + sdn_changed(); + } +} + + diff --git a/src/tests/led.c b/src/tests/led.c new file mode 100644 index 0000000..d3da43b --- /dev/null +++ b/src/tests/led.c @@ -0,0 +1,145 @@ +/* ___DISCLAIMER___ */ + +#include +#include "bits.h" +#include "timer.h" +#include "led.h" +#include "cli.h" + +static uint8_t led_bright[16]={255,253,252,251,249,247,244,239,232,223,210,191,165,127,74,0}; + +int led_rdyledstate = 0; +int led_readledstate = 0; +int led_writeledstate = 0; +int led_pwmstate = 0; + +/* LED connections (Rev.C) + + LED color IO PWM + --------------------------- + ready green P2.4 PWM1[5] + read yellow P2.5 PWM1[6] + write red P1.23 PWM1[4] +*/ + +void rdyled(unsigned int state) { + if(led_pwmstate) { + rdybright(state?15:0); + } else { + BITBAND(LPC_GPIO2->FIODIR, 4) = state; + } + led_rdyledstate = state; +} + +void readled(unsigned int state) { + if(led_pwmstate) { + readbright(state?15:0); + } else { + BITBAND(LPC_GPIO2->FIODIR, 5) = state; + } + led_readledstate = state; +} + +void writeled(unsigned int state) { + if(led_pwmstate) { + writebright(state?15:0); + } else { + BITBAND(LPC_GPIO1->FIODIR, 23) = state; + } + led_writeledstate = state; +} + +void rdybright(uint8_t bright) { + LPC_PWM1->MR5 = led_bright[(bright & 15)]; + BITBAND(LPC_PWM1->LER, 5) = 1; +} +void readbright(uint8_t bright) { + LPC_PWM1->MR6 = led_bright[(bright & 15)]; + BITBAND(LPC_PWM1->LER, 6) = 1; +} +void writebright(uint8_t bright) { + LPC_PWM1->MR4 = led_bright[(bright & 15)]; + BITBAND(LPC_PWM1->LER, 4) = 1; +} + +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); + } +} + +void toggle_rdy_led() { + rdyled(~led_rdyledstate); +} + +void toggle_read_led() { + readled(~led_readledstate); +} + +void toggle_write_led() { + writeled(~led_writeledstate); +} + +void led_panic() { + while(1) { + LPC_GPIO2->FIODIR |= BV(4) | BV(5); + LPC_GPIO1->FIODIR |= BV(23); + delay_ms(350); + LPC_GPIO2->FIODIR &= ~(BV(4) | BV(5)); + LPC_GPIO1->FIODIR &= ~BV(23); + delay_ms(350); + cli_entrycheck(); + } +} + +void led_pwm() { +/* Rev.C P2.4, P2.5, P1.23 */ + BITBAND(LPC_PINCON->PINSEL4, 9) = 0; + BITBAND(LPC_PINCON->PINSEL4, 8) = 1; + + BITBAND(LPC_PINCON->PINSEL4, 11) = 0; + BITBAND(LPC_PINCON->PINSEL4, 10) = 1; + + BITBAND(LPC_PINCON->PINSEL3, 15) = 1; + BITBAND(LPC_PINCON->PINSEL3, 14) = 0; + + BITBAND(LPC_PWM1->PCR, 12) = 1; + BITBAND(LPC_PWM1->PCR, 13) = 1; + BITBAND(LPC_PWM1->PCR, 14) = 1; + + led_pwmstate = 1; +} + +void led_std() { + BITBAND(LPC_PINCON->PINSEL4, 9) = 0; + BITBAND(LPC_PINCON->PINSEL4, 8) = 0; + + BITBAND(LPC_PINCON->PINSEL4, 11) = 0; + BITBAND(LPC_PINCON->PINSEL4, 10) = 0; + + BITBAND(LPC_PINCON->PINSEL3, 15) = 0; + BITBAND(LPC_PINCON->PINSEL3, 14) = 0; + + BITBAND(LPC_PWM1->PCR, 12) = 0; + BITBAND(LPC_PWM1->PCR, 13) = 0; + BITBAND(LPC_PWM1->PCR, 14) = 0; + + led_pwmstate = 0; +} + +void led_init() { +/* power is already connected by default */ +/* set PCLK divider to 8 */ + BITBAND(LPC_SC->PCLKSEL0, 13) = 1; + BITBAND(LPC_SC->PCLKSEL0, 12) = 1; + LPC_PWM1->MR0 = 255; + BITBAND(LPC_PWM1->LER, 0) = 1; + BITBAND(LPC_PWM1->TCR, 0) = 1; + BITBAND(LPC_PWM1->TCR, 3) = 1; + BITBAND(LPC_PWM1->MCR, 1) = 1; +} diff --git a/src/tests/led.h b/src/tests/led.h new file mode 100644 index 0000000..bb45b1a --- /dev/null +++ b/src/tests/led.h @@ -0,0 +1,21 @@ +/* ___DISCLAIMER___ */ + +#ifndef _LED_H +#define _LED_H + +void readbright(uint8_t bright); +void writebright(uint8_t bright); +void rdybright(uint8_t bright); +void readled(unsigned int state); +void writeled(unsigned int state); +void rdyled(unsigned int state); +void led_clkout32(uint32_t val); +void toggle_rdy_led(void); +void toggle_read_led(void); +void toggle_write_led(void); +void led_panic(void); +void led_pwm(void); +void led_std(void); +void led_init(void); + +#endif diff --git a/src/tests/lpc1754.cfg b/src/tests/lpc1754.cfg new file mode 100644 index 0000000..fa34d17 --- /dev/null +++ b/src/tests/lpc1754.cfg @@ -0,0 +1,77 @@ +# NXP LPC1754 Cortex-M3 with 128kB Flash and 16kB+16kB Local On-Chip SRAM, +# reset_config trst_and_srst + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME lpc1754 +} + +# After reset the chip is clocked by the ~4MHz internal RC oscillator. +# When board-specific code (reset-init handler or device firmware) +# configures another oscillator and/or PLL0, set CCLK to match; if +# you don't, then flash erase and write operations may misbehave. +# (The ROM code doing those updates cares about core clock speed...) +# +# CCLK is the core clock frequency in KHz +if { [info exists CCLK ] } { + set _CCLK $CCLK +} else { + set _CCLK 4000 +} +if { [info exists CPUTAPID ] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x4ba00477 +} + +#delays on reset lines +#if your OpenOCD version rejects "jtag_nsrst_delay" replace it with: +#adapter_nsrst_delay 200 +jtag_nsrst_delay 200 +jtag_ntrst_delay 200 + +# LPC2000 & LPC1700 -> SRST causes TRST +#reset_config srst_pulls_trst +reset_config trst_and_srst srst_push_pull trst_push_pull + +jtag newtap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID +#jtag newtap x3s tap -irlen 6 -ircapture 0x11 -irmask 0x11 -expected-id 0x0141c093 + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m3 -chain-position $_TARGETNAME -event reset-init 0 + +# LPC1754 has 16kB of SRAM In the ARMv7-M "Code" area (at 0x10000000) +# and 16K more on AHB, in the ARMv7-M "SRAM" area, (at 0x2007c000). +$_TARGETNAME configure -work-area-phys 0x10000000 -work-area-size 0x4000 +$_TARGETNAME configure -work-area-phys 0x2007c000 -work-area-size 0x4000 + +# LPC1754 has 128kB of flash memory, managed by ROM code (including a +# boot loader which verifies the flash exception table's checksum). +# flash bank lpc2000 0 0 [calc checksum] +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME lpc2000 0x0 0x20000 0 0 $_TARGETNAME \ + lpc1700 $_CCLK calc_checksum + +# Run with *real slow* clock by default since the +# boot rom could have been playing with the PLL, so +# we have no idea what clock the target is running at. +jtag_khz 1000 + +$_TARGETNAME configure -event reset-init { + # Do not remap 0x0000-0x0020 to anything but the flash (i.e. select + # "User Flash Mode" where interrupt vectors are _not_ remapped, + # and reside in flash instead). + # + # See Table 612. Memory Mapping Control register (MEMMAP - 0x400F C040) bit description + # Bit Symbol Value Description Reset + # value + # 0 MAP Memory map control. 0 + # 0 Boot mode. A portion of the Boot ROM is mapped to address 0. + # 1 User mode. The on-chip Flash memory is mapped to address 0. + # 31:1 - Reserved. The value read from a reserved bit is not defined. NA + # + # http://ics.nxp.com/support/documents/microcontrollers/?scope=LPC1768&type=user + + mww 0x400FC040 0x01 +} diff --git a/src/tests/lpc1754.ld b/src/tests/lpc1754.ld new file mode 100644 index 0000000..b10c20a --- /dev/null +++ b/src/tests/lpc1754.ld @@ -0,0 +1,131 @@ +/* Linker script for LPC1754 + * + * Written 2010 by Ingo Korb + * + * Partially based on the linker scripts of avr-libc + */ + +OUTPUT_FORMAT(elf32-littlearm) +ENTRY(_start) + +MEMORY +{ + flash (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000 /* leave room for bootldr + metadata */ + ram (rwx) : ORIGIN = 0x10000000, LENGTH = 0x04000 + ahbram (rwx) : ORIGIN = 0x2007C000, LENGTH = 0x04000 +} + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + KEEP(*(.init)) + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + + /* C++ con-/destructors */ + __ctors_start = . ; + *(.ctors) + __ctors_end = . ; + __dtors_start = . ; + *(.dtors) + __dtors_end = . ; + KEEP(SORT(*)(.ctors)) + KEEP(SORT(*)(.dtors)) + + KEEP(*(.fini)) + + __text_end = .; + } > flash + + /* .ARM.exidx is sorted, so has to go in its own output section. */ + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } >flash + __exidx_end = .; + + /* I hope this does what I think it does */ + .rodata : AT (ALIGN(__exidx_end,4)) + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + __rodata_end = .; + } > flash + + /* Data section */ + .data : AT (ALIGN(__rodata_end,4)) + { + __data_start = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + __data_end = .; + } > ram + + /* Addresses of in-rom data section */ + __data_load_start = LOADADDR(.data); + __data_load_end = __data_load_start + SIZEOF(.data); + + . = ALIGN(4); + + /* BSS */ + .bss : + { + __bss_start__ = .; + *(.bss) + *(.bss.*) + *(COMMON) + __bss_end__ = .; + } > ram + + /* second BSS in AHB ram */ + .ahbram (NOLOAD) : + { + __ahbram_start__ = .; + *(.ahbram) + *(.ahbram.*) + __ahbram_end__ = .; + } > ahbram + + __heap_start = ALIGN(__bss_end__, 4); + + /* Default stack starts at end of ram */ + PROVIDE(__stack = ORIGIN(ram) + LENGTH(ram)) ; + + + /* Everyone seems to copy the stuff below straight from somewhere else, so I'll do that too */ + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} diff --git a/src/tests/main.c b/src/tests/main.c new file mode 100644 index 0000000..25ba103 --- /dev/null +++ b/src/tests/main.c @@ -0,0 +1,164 @@ +#include +#include +#include "config.h" +#include "obj/autoconf.h" +#include "clock.h" +#include "uart.h" +#include "bits.h" +#include "power.h" +#include "timer.h" +#include "ff.h" +#include "diskio.h" +#include "spi.h" +#include "fileops.h" +#include "fpga.h" +#include "fpga_spi.h" +#include "filetypes.h" +#include "memory.h" +#include "snes.h" +#include "led.h" +#include "sort.h" +#include "cic.h" +#include "tests.h" +#include "cli.h" +#include "sdnative.h" +#include "crc.h" +#include "smc.h" +#include "msu1.h" +#include "rtc.h" +#include "tests.h" + +#define EMC0TOGGLE (3<<4) +#define MR0R (1<<1) + +int i; + +int sd_offload = 0, ff_sd_offload = 0, sd_offload_tgt = 0; +int sd_offload_partial = 0; +uint16_t sd_offload_partial_start = 0; +uint16_t sd_offload_partial_end = 0; + +volatile enum diskstates disk_state; +extern volatile tick_t ticks; +extern snes_romprops_t romprops; +extern volatile int reset_changed; + +enum system_states { + SYS_RTC_STATUS = 0 +}; + +int main(void) { + LPC_GPIO2->FIODIR = BV(4) | BV(5); + LPC_GPIO1->FIODIR = BV(23) | BV(SNES_CIC_PAIR_BIT); + BITBAND(SNES_CIC_PAIR_REG->FIOSET, SNES_CIC_PAIR_BIT) = 1; + LPC_GPIO0->FIODIR = BV(16); + + /* connect UART3 on P0[25:26] + SSP0 on P0[15:18] + MAT3.0 on P0[10] */ + LPC_PINCON->PINSEL1 = BV(18) | BV(19) | BV(20) | BV(21) /* UART3 */ + | BV(3) | BV(5); /* SSP0 (FPGA) except SS */ + LPC_PINCON->PINSEL0 = BV(31); /* SSP0 */ +/* | BV(13) | BV(15) | BV(17) | BV(19) SSP1 (SD) */ + + /* pull-down CIC data lines */ + LPC_PINCON->PINMODE0 = BV(0) | BV(1) | BV(2) | BV(3); + + clock_disconnect(); + snes_init(); + snes_reset(1); + power_init(); + timer_init(); + uart_init(); + fpga_spi_init(); + spi_preinit(); + led_init(); + /* do this last because the peripheral init()s change PCLK dividers */ + clock_init(); + LPC_PINCON->PINSEL0 |= BV(20) | BV(21); /* MAT3.0 (FPGA clock) */ +led_pwm(); + sdn_init(); + printf("\n\nsd2snes mk.2\n============\nfw ver.: " VER "\ncpu clock: %d Hz\n", CONFIG_CPU_FREQUENCY); + printf("PCONP=%lx\n", LPC_SC->PCONP); + + file_init(); + cic_init(0); +/* setup timer (fpga clk) */ + LPC_TIM3->CTCR=0; + LPC_TIM3->EMR=EMC0TOGGLE; + LPC_TIM3->MCR=MR0R; + LPC_TIM3->MR0=1; + LPC_TIM3->TCR=1; + fpga_init(); + + char *testnames[11] = { "SD ", "USB ", "RTC ", "CIC ", + "FPGA ", "RAM ", "CLK ", "DAC ", + "SNES IRQ", "SNES RAM", "SNES PA "}; + + char *teststate_names [3] = { "no run", "\x1b[32;1mPassed\x1b[m", "\x1b[31;1mFAILED\x1b[m" }; + + int testresults[11] = { NO_RUN, NO_RUN, NO_RUN, NO_RUN, NO_RUN, + NO_RUN, NO_RUN, NO_RUN, NO_RUN, NO_RUN, + NO_RUN }; + + testresults[TEST_SD] = test_sd(); +//testresults[TEST_USB] = test_usb(); + testresults[TEST_RTC] = test_rtc(); + delay_ms(209); + testresults[TEST_CIC] = test_cic(); + testresults[TEST_FPGA] = test_fpga(); + testresults[TEST_RAM] = test_mem(); + printf("Loading SNES test ROM\n=====================\n"); + load_rom((uint8_t*)"/sd2snes/test.bin", 0, LOADROM_WITH_RESET); + printf("\n\n\n"); + delay_ms(1000); + testresults[TEST_CLK] = test_clk(); + fpga_set_bram_addr(0x1fff); + fpga_write_bram_data(0x01); // tell SNES test program to continue + uint8_t snestest_irq_state, snestest_pa_state, snestest_mem_state, snestest_mem_bank; + uint8_t snestest_irq_done = 0, snestest_pa_done = 0, snestest_mem_done = 0; + uint8_t last_irq_state = 0x77, last_pa_state = 0x77, last_mem_state = 0x77, last_mem_bank = 0x77; + uint32_t failed_addr = 0; + while(!(snestest_irq_done & snestest_pa_done & snestest_mem_done)) { + fpga_set_bram_addr(0); + snestest_irq_state = fpga_read_bram_data(); + snestest_mem_state = fpga_read_bram_data(); + snestest_pa_state = fpga_read_bram_data(); + snestest_mem_bank = fpga_read_bram_data(); + if(snestest_irq_state != last_irq_state + || snestest_mem_state != last_mem_state + || snestest_pa_state != last_pa_state + || snestest_mem_bank != last_mem_bank) { + printf("SNES test status: IRQ: %02x PA: %02x MEM: %02x/%02x\r", snestest_irq_state, snestest_pa_state, snestest_mem_state, snestest_mem_bank); + } + last_irq_state = snestest_irq_state; + last_mem_state = snestest_mem_state; + last_pa_state = snestest_pa_state; + last_mem_bank = snestest_mem_bank; + if(snestest_pa_state != 0x00) snestest_pa_done = 1; + if(snestest_irq_state != 0x00) snestest_irq_done = 1; + if(snestest_mem_state == 0xff || snestest_mem_state == 0x5a) snestest_mem_done = 1; + cli_entrycheck(); + } + printf("\n"); + if(snestest_pa_state == 0xff) testresults[TEST_SNES_PA] = FAILED; + else testresults[TEST_SNES_PA] = PASSED; + if(snestest_irq_state == 0xff) testresults[TEST_SNES_IRQ] = FAILED; + else testresults[TEST_SNES_IRQ] = PASSED; + if(snestest_mem_state == 0xff) { + testresults[TEST_SNES_RAM] = FAILED; + fpga_set_bram_addr(4); + failed_addr = fpga_read_bram_data(); + failed_addr |= fpga_read_bram_data() << 8; + failed_addr |= fpga_read_bram_data() << 16; + printf("SNES MEM test FAILED (failed address: %06lx)\n", failed_addr); + } + else testresults[TEST_SNES_RAM] = PASSED; + printf("\n\nTEST SUMMARY\n============\n\n"); + printf("Test Result\n----------------\n"); + int testcount; + for(testcount=0; testcount < 11; testcount++) { + printf("%s %s\n", testnames[testcount], teststate_names[testresults[testcount]]); + } + cli_loop(); + while(1); +} + diff --git a/src/tests/memory.c b/src/tests/memory.c new file mode 100644 index 0000000..64cedae --- /dev/null +++ b/src/tests/memory.c @@ -0,0 +1,547 @@ +/* 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 + + memory.c: RAM operations +*/ + + +#include "config.h" +#include "uart.h" +#include "fpga.h" +#include "crc.h" +#include "crc32.h" +#include "ff.h" +#include "fileops.h" +#include "spi.h" +#include "fpga_spi.h" +#include "led.h" +#include "smc.h" +#include "memory.h" +#include "snes.h" +#include "timer.h" +#include "rle.h" +#include "diskio.h" +#include "msu1.h" + +#include +char* hex = "0123456789ABCDEF"; + +extern snes_romprops_t romprops; + +void sram_hexdump(uint32_t addr, uint32_t len) { + static uint8_t buf[16]; + uint32_t ptr; + for(ptr=0; ptr < len; ptr += 16) { + sram_readblock((void*)buf, ptr+addr, 16); + uart_trace(buf, 0, 16); + } +} + +void sram_writebyte(uint8_t val, uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x98); /* WRITE */ + FPGA_TX_BYTE(val); + FPGA_WAIT_RDY(); + FPGA_DESELECT(); +} + +uint8_t sram_readbyte(uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x88); /* READ */ + FPGA_WAIT_RDY(); + uint8_t val = FPGA_RX_BYTE(); + FPGA_DESELECT(); + return val; +} + +void sram_writeshort(uint16_t val, uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x98); /* WRITE */ + FPGA_TX_BYTE(val&0xff); + FPGA_WAIT_RDY(); + FPGA_TX_BYTE((val>>8)&0xff); + FPGA_WAIT_RDY(); + FPGA_DESELECT(); +} + +void sram_writelong(uint32_t val, uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x98); /* WRITE */ + FPGA_TX_BYTE(val&0xff); + FPGA_WAIT_RDY(); + FPGA_TX_BYTE((val>>8)&0xff); + FPGA_WAIT_RDY(); + FPGA_TX_BYTE((val>>16)&0xff); + FPGA_WAIT_RDY(); + FPGA_TX_BYTE((val>>24)&0xff); + FPGA_WAIT_RDY(); + FPGA_DESELECT(); +} + +uint16_t sram_readshort(uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x88); + FPGA_WAIT_RDY(); + uint32_t val = FPGA_RX_BYTE(); + FPGA_WAIT_RDY(); + val |= ((uint32_t)FPGA_RX_BYTE()<<8); + FPGA_DESELECT(); + return val; +} + +uint32_t sram_readlong(uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x88); + FPGA_WAIT_RDY(); + uint32_t val = FPGA_RX_BYTE(); + FPGA_WAIT_RDY(); + val |= ((uint32_t)FPGA_RX_BYTE()<<8); + FPGA_WAIT_RDY(); + val |= ((uint32_t)FPGA_RX_BYTE()<<16); + FPGA_WAIT_RDY(); + val |= ((uint32_t)FPGA_RX_BYTE()<<24); + FPGA_DESELECT(); + return val; +} + +void sram_readlongblock(uint32_t* buf, uint32_t addr, uint16_t count) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x88); + uint16_t i=0; + while(i (romprops.romsize_bytes + romprops.offset)) { + romprops.romsize_bytes <<= 1; + } + + if(romprops.header.ramsize == 0) { + rammask = 0; + } else { + rammask = romprops.ramsize_bytes - 1; + } + rommask = romprops.romsize_bytes - 1; + printf("ramsize=%x rammask=%lx\nromsize=%x rommask=%lx\n", romprops.header.ramsize, rammask, romprops.header.romsize, rommask); + set_saveram_mask(rammask); + set_rom_mask(rommask); + readled(0); + if(flags & LOADROM_WITH_SRAM) { + if(romprops.ramsize_bytes) { + strcpy(strrchr((char*)filename, (int)'.'), ".srm"); + printf("SRM file: %s\n", filename); + load_sram(filename, SRAM_SAVE_ADDR); + } else { + printf("No SRAM\n"); + } + } + + printf("check MSU..."); + if(msu1_check(filename)) { + romprops.fpga_features |= FEAT_MSU1; + romprops.has_msu1 = 1; + } else { + romprops.has_msu1 = 0; + } + printf("done\n"); + + romprops.fpga_features |= FEAT_SRTC; + + fpga_set_features(romprops.fpga_features); + + if(flags & LOADROM_WITH_RESET) { + fpga_dspx_reset(1); + snes_reset(1); + delay_ms(10); + snes_reset(0); + fpga_dspx_reset(0); + } + + return (uint32_t)filesize; +} + +uint32_t load_sram_offload(uint8_t* filename, uint32_t base_addr) { + set_mcu_addr(base_addr); + UINT bytes_read; + DWORD filesize; + file_open(filename, FA_READ); + filesize = file_handle.fsize; + if(file_res) return 0; + for(;;) { + ff_sd_offload=1; + sd_offload_tgt=0; + bytes_read = file_read(); + if (file_res || !bytes_read) break; + } + file_close(); + return (uint32_t)filesize; +} + +uint32_t load_sram(uint8_t* filename, uint32_t base_addr) { + set_mcu_addr(base_addr); + UINT bytes_read; + DWORD filesize; + file_open(filename, FA_READ); + filesize = file_handle.fsize; + if(file_res) { + printf("load_sram: could not open %s, res=%d\n", filename, file_res); + return 0; + } + for(;;) { + bytes_read = file_read(); + if (file_res || !bytes_read) break; + FPGA_SELECT(); + FPGA_TX_BYTE(0x98); + for(int j=0; j + 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 + + memory.h: RAM operations +*/ + +#ifndef MEMORY_H +#define MEMORY_H + +#include +#include "smc.h" + +#define SRAM_ROM_ADDR (0x000000L) +#define SRAM_SAVE_ADDR (0xE00000L) + +#define SRAM_MENU_ADDR (0xE00000L) +#define SRAM_DB_ADDR (0xE40000L) +#define SRAM_DIR_ADDR (0xE10000L) +#define SRAM_CMD_ADDR (0xFF1000L) +#define SRAM_PARAM_ADDR (0xFF1004L) +#define SRAM_STATUS_ADDR (0xFF1100L) +#define SRAM_MENU_SAVE_ADDR (0xFF0000L) +#define SRAM_SCRATCHPAD (0xFFFF00L) +#define SRAM_DIRID (0xFFFFF0L) +#define SRAM_RELIABILITY_SCORE (0x100) + +#define LOADROM_WITH_SRAM (1) +#define LOADROM_WITH_RESET (2) + +uint32_t load_rom(uint8_t* filename, uint32_t base_addr, uint8_t flags); +uint32_t load_sram(uint8_t* filename, uint32_t base_addr); +uint32_t load_sram_offload(uint8_t* filename, uint32_t base_addr); +uint32_t load_sram_rle(uint8_t* filename, uint32_t base_addr); +void load_dspx(const uint8_t* filename, uint8_t st0010); +void sram_hexdump(uint32_t addr, uint32_t len); +uint8_t sram_readbyte(uint32_t addr); +uint16_t sram_readshort(uint32_t addr); +uint32_t sram_readlong(uint32_t addr); +void sram_writebyte(uint8_t val, uint32_t addr); +void sram_writeshort(uint16_t val, uint32_t addr); +void sram_writelong(uint32_t val, uint32_t addr); +void sram_readblock(void* buf, uint32_t addr, uint16_t size); +void sram_readlongblock(uint32_t* buf, uint32_t addr, uint16_t count); +void sram_writeblock(void* buf, uint32_t addr, uint16_t size); +void save_sram(uint8_t* filename, uint32_t sram_size, uint32_t base_addr); +uint32_t calc_sram_crc(uint32_t base_addr, uint32_t size); +uint8_t sram_reliable(void); +void sram_memset(uint32_t base_addr, uint32_t len, uint8_t val); +uint64_t sram_gettime(uint32_t base_addr); + +#endif diff --git a/src/tests/msu1.c b/src/tests/msu1.c new file mode 100644 index 0000000..6dc298f --- /dev/null +++ b/src/tests/msu1.c @@ -0,0 +1,256 @@ +#include +#include "config.h" +#include "uart.h" +#include "ff.h" +#include "diskio.h" +#include "spi.h" +#include "fpga_spi.h" +#include "cli.h" +#include "fileops.h" +#include "msu1.h" +#include "snes.h" +#include "timer.h" +#include "smc.h" + +FIL msufile; +extern snes_romprops_t romprops; + +int msu1_check_reset(void) { + static tick_t rising_ticks; + + static uint8_t resbutton=0, resbutton_prev=0; + resbutton = get_snes_reset(); + if(resbutton && !resbutton_prev) { /* push */ + rising_ticks = getticks(); + } else if(resbutton && resbutton_prev) { /* hold */ + if(getticks() > rising_ticks + 99) { + return 1; + } + } + resbutton_prev = resbutton; + return 0; +} + +int msu1_check(uint8_t* filename) { +/* open MSU file */ + strcpy((char*)file_buf, (char*)filename); + strcpy(strrchr((char*)file_buf, (int)'.'), ".msu"); + printf("MSU datafile: %s\n", file_buf); + if(f_open(&msufile, (const TCHAR*)file_buf, FA_READ) != FR_OK) { + printf("MSU datafile not found\n"); + return 0; + } + romprops.fpga_features |= FEAT_MSU1; + return 1; +} + +int msu1_loop() { +/* it is assumed that the MSU file is already opened by calling msu1_check(). */ + UINT bytes_read = 1024; + UINT bytes_read2 = 1; + FRESULT res; + set_dac_vol(0x00); + while(fpga_status() & 0x4000); + uint16_t fpga_status_prev = fpga_status(); + uint16_t fpga_status_now = fpga_status(); + uint16_t dac_addr = 0; + uint16_t msu_addr = 0; + uint8_t msu_repeat = 0; + uint16_t msu_track = 0; + uint32_t msu_offset = 0; + uint32_t msu_loop_point = 0; + + uint32_t msu_page1_start = 0x0000; + uint32_t msu_page2_start = 0x2000; + uint32_t msu_page_size = 0x2000; + + set_msu_addr(0x0); + msu_reset(0x0); + ff_sd_offload=1; + sd_offload_tgt=2; + f_lseek(&msufile, 0L); + ff_sd_offload=1; + sd_offload_tgt=2; + f_read(&msufile, file_buf, 16384, &bytes_read2); + + set_dac_addr(dac_addr); + dac_pause(); + dac_reset(); +/* audio_start, data_start, 0, audio_ctrl[1:0], ctrl_start */ + while(1){ + cli_entrycheck(); + fpga_status_now = fpga_status(); + + /* Data buffer refill */ + if((fpga_status_now & 0x2000) != (fpga_status_prev & 0x2000)) { + DBG_MSU1 printf("data\n"); + uint8_t pageno = 0; + if(fpga_status_now & 0x2000) { + msu_addr = 0x0; + msu_page1_start = msu_page2_start + msu_page_size; + pageno = 1; + } else { + msu_addr = 0x2000; + msu_page2_start = msu_page1_start + msu_page_size; + pageno = 2; + } + set_msu_addr(msu_addr); + sd_offload_tgt=2; + ff_sd_offload=1; + res = f_read(&msufile, file_buf, 8192, &bytes_read2); + DBG_MSU1 printf("data buffer refilled. res=%d page1=%08lx page2=%08lx\n", res, msu_page1_start, msu_page2_start); + } + + /* Audio buffer refill */ + if((fpga_status_now & 0x4000) != (fpga_status_prev & 0x4000)) { + if(fpga_status_now & 0x4000) { + dac_addr = 0; + } else { + dac_addr = MSU_DAC_BUFSIZE/2; + } + set_dac_addr(dac_addr); + sd_offload_tgt=1; + ff_sd_offload=1; + f_read(&file_handle, file_buf, MSU_DAC_BUFSIZE/2, &bytes_read); + } + + if(fpga_status_now & 0x0020) { + char suffix[11]; + + /* get trackno */ + msu_track = get_msu_track(); + printf("Audio requested! Track=%d\n", msu_track); + + /* open file, fill buffer */ + f_close(&file_handle); + snprintf(suffix, sizeof(suffix), "-%d.pcm", msu_track); + strcpy((char*)file_buf, (char*)file_lfn); + strcpy(strrchr((char*)file_buf, (int)'.'), suffix); + printf("filename: %s\n", file_buf); + f_open(&file_handle, (const TCHAR*)file_buf, FA_READ); + f_lseek(&file_handle, 4L); + f_read(&file_handle, &msu_loop_point, 4, &bytes_read); + printf("loop point: %ld samples\n", msu_loop_point); + ff_sd_offload=1; + sd_offload_tgt=1; + f_lseek(&file_handle, 8L); + set_dac_addr(0); + dac_pause(); + dac_reset(); + ff_sd_offload=1; + sd_offload_tgt=1; + f_read(&file_handle, file_buf, MSU_DAC_BUFSIZE, &bytes_read); + + /* clear busy bit */ + set_msu_status(0x00, 0x20); /* set no bits, reset bit 5 */ + } + + if(fpga_status_now & 0x0010) { + /* get address */ + msu_offset=get_msu_offset(); + printf("Data requested! Offset=%08lx page1=%08lx page2=%08lx\n", msu_offset, msu_page1_start, msu_page2_start); + if( ((msu_offset < msu_page1_start) + || (msu_offset >= msu_page1_start + msu_page_size)) + && ((msu_offset < msu_page2_start) + || (msu_offset >= msu_page2_start + msu_page_size))) { + printf("offset %08lx out of range (%08lx-%08lx, %08lx-%08lx), reload\n", msu_offset, msu_page1_start, + msu_page1_start+msu_page_size-1, msu_page2_start, msu_page2_start+msu_page_size-1); + /* "cache miss" */ + /* fill buffer */ + set_msu_addr(0x0); + sd_offload_tgt=2; + ff_sd_offload=1; + res = f_lseek(&msufile, msu_offset); + DBG_MSU1 printf("seek to %08lx, res = %d\n", msu_offset, res); + sd_offload_tgt=2; + ff_sd_offload=1; + + res = f_read(&msufile, file_buf, 16384, &bytes_read2); + DBG_MSU1 printf("read res = %d\n", res); + DBG_MSU1 printf("read %d bytes\n", bytes_read2); + msu_reset(0x0); + msu_page1_start = msu_offset; + msu_page2_start = msu_offset + msu_page_size; + } else { + if (msu_offset >= msu_page1_start && msu_offset <= msu_page1_start + msu_page_size) { + msu_reset(0x0000 + msu_offset - msu_page1_start); + DBG_MSU1 printf("inside page1, new offset: %08lx\n", 0x0000 + msu_offset-msu_page1_start); + if(!(msu_page2_start == msu_page1_start + msu_page_size)) { + set_msu_addr(0x2000); + sd_offload_tgt=2; + ff_sd_offload=1; + f_read(&msufile, file_buf, 8192, &bytes_read2); + DBG_MSU1 printf("next page dirty (was: %08lx), loaded page2 (start now: ", msu_page2_start); + msu_page2_start = msu_page1_start + msu_page_size; + DBG_MSU1 printf("%08lx)\n", msu_page2_start); + } + } else if (msu_offset >= msu_page2_start && msu_offset <= msu_page2_start + msu_page_size) { + DBG_MSU1 printf("inside page2, new offset: %08lx\n", 0x2000 + msu_offset-msu_page2_start); + msu_reset(0x2000 + msu_offset - msu_page2_start); + if(!(msu_page1_start == msu_page2_start + msu_page_size)) { + set_msu_addr(0x0); + sd_offload_tgt=2; + ff_sd_offload=1; + f_read(&msufile, file_buf, 8192, &bytes_read2); + DBG_MSU1 printf("next page dirty (was: %08lx), loaded page1 (start now: ", msu_page1_start); + msu_page1_start = msu_page2_start + msu_page_size; + DBG_MSU1 printf("%08lx)\n", msu_page1_start); + } + } else printf("!!!WATWATWAT!!!\n"); + } + /* clear bank bit to mask bank reset artifact */ + fpga_status_now &= ~0x2000; + fpga_status_prev &= ~0x2000; + /* clear busy bit */ + set_msu_status(0x00, 0x10); + } + + if(fpga_status_now & 0x0001) { + if(fpga_status_now & 0x0004) { + msu_repeat = 1; + set_msu_status(0x04, 0x01); /* set bit 2, reset bit 0 */ + printf("Repeat set!\n"); + } else { + msu_repeat = 0; + set_msu_status(0x00, 0x05); /* set no bits, reset bit 0+2 */ + printf("Repeat clear!\n"); + } + + if(fpga_status_now & 0x0002) { + printf("PLAY!\n"); + set_msu_status(0x02, 0x01); /* set bit 0, reset bit 1 */ + dac_play(); + } else { + printf("PAUSE!\n"); + set_msu_status(0x00, 0x03); /* set no bits, reset bit 1+0 */ + dac_pause(); + } + } + + fpga_status_prev = fpga_status_now; + + /* handle loop / end */ + if(bytes_read < MSU_DAC_BUFSIZE / 2) { + ff_sd_offload=0; + sd_offload=0; + if(msu_repeat) { + printf("loop\n"); + ff_sd_offload=1; + sd_offload_tgt=1; + f_lseek(&file_handle, 8L+msu_loop_point*4); + ff_sd_offload=1; + sd_offload_tgt=1; + f_read(&file_handle, file_buf, (MSU_DAC_BUFSIZE / 2) - bytes_read, &bytes_read); + } else { + set_msu_status(0x00, 0x02); /* clear play bit */ + } + bytes_read = MSU_DAC_BUFSIZE; + } + if(msu1_check_reset()) { + f_close(&msufile); + f_close(&file_handle); + return 1; + } + } +} +/* END OF MSU1 STUFF */ diff --git a/src/tests/msu1.h b/src/tests/msu1.h new file mode 100644 index 0000000..1407a32 --- /dev/null +++ b/src/tests/msu1.h @@ -0,0 +1,15 @@ +#ifndef MSU1_H +#define MSU1_H + +#ifdef DEBUG_MSU1 +#define DBG_MSU1 +#else +#define DBG_MSU1 while(0) +#endif + +#define MSU_DAC_BUFSIZE (2048) + +int msu1_check(uint8_t*); +int msu1_loop(void); + +#endif diff --git a/src/tests/openocd-usb.cfg b/src/tests/openocd-usb.cfg new file mode 100644 index 0000000..1fcd482 --- /dev/null +++ b/src/tests/openocd-usb.cfg @@ -0,0 +1,12 @@ +# +# Hubert Hoegl's USB to JTAG +# +# http://www.hs-augsburg.de/~hhoegl/proj/usbjtag/usbjtag.html +# + +interface ft2232 +ft2232_vid_pid 0x0403 0x6010 +ft2232_device_desc "Dual RS232" +ft2232_layout "oocdlink" +ft2232_latency 2 +#adapter_khz 10 diff --git a/src/tests/power.c b/src/tests/power.c new file mode 100644 index 0000000..848159c --- /dev/null +++ b/src/tests/power.c @@ -0,0 +1,26 @@ +/* ___DISCLAIMER___ */ + +#include +#include "bits.h" +#include "power.h" + +/* + required units: + * SSP0 (FPGA interface) [enabled via spi_init] + * UART3 (debug console) [enabled via uart_init] + * TIM3 (FPGA clock) + * RTC + * GPIO + * GPDMA [enabled via spi_init] + * USB [enabled via usb_init] + * PWM1 +*/ +void power_init() { + LPC_SC->PCONP = BV(PCSSP0) + | BV(PCTIM3) + | BV(PCRTC) + | BV(PCGPIO) + | BV(PCPWM1) +// | BV(PCUSB) + ; +} diff --git a/src/tests/power.h b/src/tests/power.h new file mode 100644 index 0000000..65d3cac --- /dev/null +++ b/src/tests/power.h @@ -0,0 +1,43 @@ +/* ___DISCLAIMER___ */ + +#ifndef _POWER_H +#define _POWER_H + +#include "bits.h" + +#define PCUART0 (3) +#define PCUART1 (4) +#define PCUART2 (24) +#define PCUART3 (25) + +#define PCTIM0 (1) +#define PCTIM1 (2) +#define PCTIM2 (22) +#define PCTIM3 (23) +#define PCRTC (9) +#define PCRIT (16) + +#define PCCAN1 (13) +#define PCCAN2 (14) + +#define PCPWM1 (6) +#define PCMCPWM (17) + +#define PCSSP0 (21) +#define PCSSP1 (10) +#define PCSPI (8) + +#define PCI2C0 (7) +#define PCI2C1 (19) +#define PCI2C2 (26) + +#define PCI2S (27) +#define PCGPDMA (29) +#define PCENET (30) +#define PCUSB (31) +#define PCQEI (18) +#define PCGPIO (15) + +void power_init(void); + +#endif diff --git a/src/tests/printf.c b/src/tests/printf.c new file mode 100644 index 0000000..861c03d --- /dev/null +++ b/src/tests/printf.c @@ -0,0 +1,291 @@ +/* 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': + 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/tests/reset.cfg b/src/tests/reset.cfg new file mode 100644 index 0000000..defd589 --- /dev/null +++ b/src/tests/reset.cfg @@ -0,0 +1,3 @@ +init +reset run +shutdown diff --git a/src/tests/rle.c b/src/tests/rle.c new file mode 100644 index 0000000..74e3d6d --- /dev/null +++ b/src/tests/rle.c @@ -0,0 +1,66 @@ + +#include "rle.h" +#include "fileops.h" + +uint8_t rle_file_getc() { + static uint16_t rle_filled = 0; + static uint8_t data; + if(!rle_filled) { + data = file_getc(); + switch(data) { + case RLE_RUN: + data = file_getc(); + rle_filled = file_getc()-1; + break; + case RLE_RUNLONG: + data = file_getc(); + rle_filled = file_getc(); + rle_filled |= file_getc() << 8; + rle_filled--; + break; + case RLE_ESC: + data = file_getc(); + break; + } + } else { + rle_filled--; + } + if(file_status || file_res) rle_filled = 0; + return data; +} + +void rle_mem_init(const uint8_t* address, uint32_t len) { + rle_mem_ptr = address; + rle_mem_endptr = address+len; + rle_state = 0; +} + +uint8_t rle_mem_getc() { + static uint16_t rle_mem_filled = 0; + static uint8_t rle_mem_data; + if(!rle_mem_filled) { + rle_mem_data = *(rle_mem_ptr++); + switch(rle_mem_data) { + case RLE_RUN: + rle_mem_data = *(rle_mem_ptr)++; + rle_mem_filled = *(rle_mem_ptr)++ - 1; + break; + case RLE_RUNLONG: + rle_mem_data = *(rle_mem_ptr)++; + rle_mem_filled = *(rle_mem_ptr)++; + rle_mem_filled |= *(rle_mem_ptr)++ << 8; + rle_mem_filled--; + break; + case RLE_ESC: + rle_mem_data = *(rle_mem_ptr)++; + break; + } + } else { + rle_mem_filled--; + } + if(rle_mem_ptr>=rle_mem_endptr){ + rle_mem_filled = 0; + rle_state = 1; + } + return rle_mem_data; +} diff --git a/src/tests/rle.h b/src/tests/rle.h new file mode 100644 index 0000000..9747043 --- /dev/null +++ b/src/tests/rle.h @@ -0,0 +1,17 @@ +#ifndef RLE_H +#define RLE_H + +#include + +#define RLE_ESC (0x9b) +#define RLE_RUN (0x5b) +#define RLE_RUNLONG (0x77) + +uint8_t rle_file_getc(void); +uint8_t rle_mem_getc(void); +void rle_mem_init(const uint8_t *address, uint32_t len); +const uint8_t *rle_mem_ptr; +const uint8_t *rle_mem_endptr; +uint8_t rle_state; + +#endif diff --git a/src/tests/rtc.c b/src/tests/rtc.c new file mode 100644 index 0000000..f060945 --- /dev/null +++ b/src/tests/rtc.c @@ -0,0 +1,135 @@ +#include +#include +#include "config.h" +#include "rtc.h" +#include "uart.h" +#include "timer.h" +#include "power.h" + +rtcstate_t rtc_state; + +#define CLKEN 0 +#define CTCRST 1 + +uint8_t rtc_isvalid(void) { + if(LPC_RTC->GPREG0 == RTC_MAGIC) { + return RTC_OK; + } + return RTC_INVALID; +} + +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; + time->tm_wday = LPC_RTC->DOW; + } while (time->tm_sec != LPC_RTC->SEC); +} + +uint8_t calc_weekday(struct tm *time) { + int month = time->tm_mon; + int year = time->tm_year; + int day = time->tm_mday; + + /* Variation of Sillke for the Gregorian calendar. + * http://www.mathematik.uni-bielefeld.de/~sillke/ALGORITHMS/calendar/weekday.c */ + if (month <= 2) { + month += 10; + year--; + } else month -= 2; + return (83*month/32 + day + year + year/4 - year/100 + year/400) % 7; +} + +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; + LPC_RTC->DOW = calc_weekday(time); + LPC_RTC->CCR = BV(CLKEN); + LPC_RTC->GPREG0 = RTC_MAGIC; +} + +void invalidate_rtc() { + LPC_RTC->GPREG0 = 0; +} + +uint32_t get_fattime(void) { + struct tm time; + + read_rtc(&time); + return ((uint32_t)time.tm_year-1980) << 25 | + ((uint32_t)time.tm_mon) << 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; +} + +uint64_t get_bcdtime(void) { + struct tm time; + read_rtc(&time); + uint16_t year = time.tm_year; + + return ((uint64_t)(time.tm_wday % 7) << 56) + |((uint64_t)((year / 1000) % 10) << 52) + |((uint64_t)((year / 100) % 10) << 48) + |((uint64_t)((year / 10) % 10) << 44) + |((uint64_t)(year % 10) << 40) + |((uint64_t)(time.tm_mon / 10) << 36) + |((uint64_t)(time.tm_mon % 10) << 32) + |((time.tm_mday / 10) << 28) + |((time.tm_mday % 10) << 24) + |((time.tm_hour / 10) << 20) + |((time.tm_hour % 10) << 16) + |((time.tm_min / 10) << 12) + |((time.tm_min % 10) << 8) + |((time.tm_sec / 10) << 4) + |(time.tm_sec % 10); +} + +void set_bcdtime(uint64_t btime) { + struct tm time; + time.tm_sec = (btime & 0xf) + ((btime >> 4) & 0xf) * 10; + time.tm_min = ((btime >> 8) & 0xf) + ((btime >> 12) & 0xf) * 10; + time.tm_hour = ((btime >> 16) & 0xf) + ((btime >> 20) & 0xf) * 10; + time.tm_mday = ((btime >> 24) & 0xf) + ((btime >> 28) & 0xf) * 10; + time.tm_mon = ((btime >> 32) & 0xf) + ((btime >> 36) & 0xf) * 10; + time.tm_year = ((btime >> 40) & 0xf) + ((btime >> 44) & 0xf) * 10 + + ((btime >> 48) & 0xf) * 100 + ((btime >> 52) & 0xf) * 1000; + printtime(&time); + set_rtc(&time); +} + +void printtime(struct tm *time) { + printf("%04d-%02d-%02d %02d:%02d:%02d\n", time->tm_year, time->tm_mon, + time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); +} + +void testbattery() { + printf("%lx\n", LPC_RTC->GPREG0); + LPC_RTC->GPREG0 = RTC_MAGIC; + printf("%lx\n", LPC_RTC->GPREG0); + LPC_RTC->CCR = 0; + BITBAND(LPC_SC->PCONP, PCRTC) = 0; + delay_ms(20000); + BITBAND(LPC_SC->PCONP, PCRTC) = 1; + printf("%lx\n", LPC_RTC->GPREG0); + delay_ms(20); + LPC_RTC->CCR = BV(CLKEN); +} diff --git a/src/tests/rtc.h b/src/tests/rtc.h new file mode 100644 index 0000000..4e52a96 --- /dev/null +++ b/src/tests/rtc.h @@ -0,0 +1,78 @@ +/* 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 + +*/ + +#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 + uint16_t tm_year; // since 0 A.D. + 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 +}; + +#define RTC_MAGIC (0x43545253L) + +extern rtcstate_t rtc_state; + +void rtc_init(void); + +/* return RTC valid state based on magic token in backup register */ +uint8_t rtc_isvalid(void); + +/* Return current time in struct tm */ +void read_rtc(struct tm *time); + +/* Set time from struct tm, also sets RTC valid */ +void set_rtc(struct tm *time); + +/* Set RTC invalid */ +void invalidate_rtc(void); + +/* get current time in 60-bit BCD format (WYYYYMMDDHHMMSS) (W=DOW) */ +uint64_t get_bcdtime(void); + +/* set current time from 56-bit BCD format (YYYYMMDDHHMMSS) + DOW is calculated */ +void set_bcdtime(uint64_t btime); + +/* print the time to the console */ +void printtime(struct tm *time); + +void testbattery(void); +#endif diff --git a/src/tests/sdcard.h b/src/tests/sdcard.h new file mode 100644 index 0000000..e13e639 --- /dev/null +++ b/src/tests/sdcard.h @@ -0,0 +1,46 @@ +/* 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" + +#define SD_TX_BYTE(x) spi_tx_byte(x, SPI_SD); +#define SD_RX_BYTE(x) spi_rx_byte(x, SPI_SD); +#define SD_TX_BLOCK(x,y) spi_tx_block(x,y, SPI_SD); +#define SD_RX_BLOCK(x,y) spi_rx_block(x,y, SPI_SD); + +/* 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); + +void sd_changed(void); +#endif diff --git a/src/tests/sdnative.c b/src/tests/sdnative.c new file mode 100644 index 0000000..965d418 --- /dev/null +++ b/src/tests/sdnative.c @@ -0,0 +1,1015 @@ +#include +#include +#include "config.h" +#include "crc.h" +#include "diskio.h" +#include "spi.h" +#include "timer.h" +#include "uart.h" +#include "led.h" +#include "sdnative.h" +#include "fileops.h" +#include "bits.h" +#include "fpga_spi.h" + +#define MAX_CARDS 1 + +// SD/MMC commands +#define GO_IDLE_STATE 0 +#define SEND_OP_COND 1 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SWITCH_FUNC 6 +#define SELECT_CARD 7 +#define SEND_IF_COND 8 +#define SEND_CSD 9 +#define SEND_CID 10 +#define STOP_TRANSMISSION 12 +#define SEND_STATUS 13 +#define GO_INACTIVE_STATE 15 +#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_SET_BUS_WIDTH 6 +#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) + +/* + Rev.A Rev.C + 1 DAT3/SS P0.6 P2.3 + 2 CMD/DI P0.9 P0.9 + 5 Clock P0.7 P0.7 + 7 DAT0/DO P0.8 P2.0 + 8 DAT1/IRQ P1.14 P2.1 + 9 DAT2/NC P1.15 P2.2 +*/ + +/* SD init procedure + ================= + - initial clock frequency: ~100kHz + - cycle the clock for at least 74 cycles (some more may be safer) + - send CMD0 + - send CMD8 (SEND_OP_COND); if no response -> HCS=0; else HCS=1 + - send ACMD41 until OCR[31] (busy) becomes 1 (means: ready) + - if OCR[30] (CCS) set -> SDHC; else SDSC + - send CMD2 (read CID) (maybe log some stuff from the CID) + - send CMD3 (read RCA), store RCA +== end of initialisation == + - send CMD9 (read CSD) with RCA, maybe do sth with TRAN_SPEED + - send CMD7 with RCA, select card, put card in tran + - maybe send CMD13 with RCA to check state (tran) + - send ACMD51 with RCA to read SCR (maybe, to check 4bit support) + - increase clock speed + - send ACMD6 with RCA to set 4bit bus width + - send transfer cmds +*/ + +/* + static CMD payloads. (no CRC calc required) + - CMD0: 0x40 0x00 0x00 0x00 0x00 0x95 + - CMD8: 0x48 0x00 0x00 0x01 0xaa 0x87 + - CMD2: 0x42 0x00 0x00 0x00 0x00 0x4d + - CMD3: 0x43 0x00 0x00 0x00 0x00 0x21 + - CMD55: 0x77 0x00 0x00 0x00 0x00 0x65 +*/ + +uint8_t cmd[6]={0,0,0,0,0,0}; +uint8_t rsp[17]; +uint8_t csd[17]; +uint8_t ccs=0; +uint32_t rca; + +enum trans_state { TRANS_NONE = 0, TRANS_READ, TRANS_WRITE }; +enum cmd_state { CMD_RSP = 0, CMD_RSPDAT, CMD_DAT }; + +int during_blocktrans = TRANS_NONE; +uint32_t last_block = 0; + +volatile int sd_changed; + +/** + * 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; +} + +static inline void wiggle_slow_pos(uint16_t times) { + while(times--) { + delay_us(2); + BITBAND(SD_CLKREG->FIOSET, SD_CLKPIN) = 1; + delay_us(2); + BITBAND(SD_CLKREG->FIOCLR, SD_CLKPIN) = 1; + } +} + +static inline void wiggle_slow_neg(uint16_t times) { + while(times--) { + delay_us(2); + BITBAND(SD_CLKREG->FIOCLR, SD_CLKPIN) = 1; + delay_us(2); + BITBAND(SD_CLKREG->FIOSET, SD_CLKPIN) = 1; + } +} + +static inline void wiggle_fast_pos(uint16_t times) { + while(times--) { + BITBAND(SD_CLKREG->FIOSET, SD_CLKPIN) = 1; + BITBAND(SD_CLKREG->FIOCLR, SD_CLKPIN) = 1; + } +} + +static inline void wiggle_fast_neg(uint16_t times) { + while(times--) { + BITBAND(SD_CLKREG->FIOCLR, SD_CLKPIN) = 1; + BITBAND(SD_CLKREG->FIOSET, SD_CLKPIN) = 1; + } +} + +static inline void wiggle_fast_neg1(void) { + BITBAND(SD_CLKREG->FIOCLR, SD_CLKPIN) = 1; + BITBAND(SD_CLKREG->FIOSET, SD_CLKPIN) = 1; +} + +static inline void wiggle_fast_pos1(void) { + BITBAND(SD_CLKREG->FIOSET, SD_CLKPIN) = 1; + BITBAND(SD_CLKREG->FIOCLR, SD_CLKPIN) = 1; +} + + +int get_and_check_datacrc(uint8_t *buf) { + uint16_t crc0=0, crc1=0, crc2=0, crc3=0; + uint16_t sdcrc0=0, sdcrc1=0, sdcrc2=0, sdcrc3=0; + uint8_t d0=0, d1=0, d2=0, d3=0; + uint8_t datdata; + uint16_t datcnt; + /* get crcs from card */ + for (datcnt=0; datcnt < 16; datcnt++) { + datdata = SD_DAT; + wiggle_fast_neg1(); + sdcrc0 = ((sdcrc0 << 1) & 0xfffe) | ((datdata >> 3) & 0x0001); + sdcrc1 = ((sdcrc1 << 1) & 0xfffe) | ((datdata >> 2) & 0x0001); + sdcrc2 = ((sdcrc2 << 1) & 0xfffe) | ((datdata >> 1) & 0x0001); + sdcrc3 = ((sdcrc3 << 1) & 0xfffe) | ((datdata >> 0) & 0x0001); + } + wiggle_fast_neg1(); + /* calc crcs from data */ + for (datcnt=0; datcnt < 512; datcnt++) { + d0 = ((d0 << 2) & 0xfc) | ((buf[datcnt] >> 6) & 0x02) | ((buf[datcnt] >> 3) & 0x01) ; + d1 = ((d1 << 2) & 0xfc) | ((buf[datcnt] >> 5) & 0x02) | ((buf[datcnt] >> 2) & 0x01) ; + d2 = ((d2 << 2) & 0xfc) | ((buf[datcnt] >> 4) & 0x02) | ((buf[datcnt] >> 1) & 0x01) ; + d3 = ((d3 << 2) & 0xfc) | ((buf[datcnt] >> 3) & 0x02) | ((buf[datcnt] >> 0) & 0x01) ; + if((datcnt % 4) == 3) { + crc0 = crc_xmodem_update(crc0, d0); + crc1 = crc_xmodem_update(crc1, d1); + crc2 = crc_xmodem_update(crc2, d2); + crc3 = crc_xmodem_update(crc3, d3); + } + } + if((crc0 != sdcrc0) || (crc1 != sdcrc1) || (crc2 != sdcrc2) || (crc3 != sdcrc3)) { + printf("CRC mismatch\nSDCRC CRC\n %04x %04x\n %04x %04x\n %04x %04x\n %04x %04x\n", sdcrc0, crc0, sdcrc1, crc1, sdcrc2, crc2, sdcrc3, crc3); +while(1); + return 1; + } + return 0; +} + +static inline void wait_busy(void) { + while(!(BITBAND(SD_DAT0REG->FIOPIN, SD_DAT0PIN))) { + wiggle_fast_neg1(); + } + wiggle_fast_neg(4); +} + +/* + send_command_slow + send SD command and put response in rsp. + returns length of response or 0 if there was no response +*/ +int send_command_slow(uint8_t* cmd, uint8_t* rsp){ + uint8_t shift, i=6; + int rsplen; + uint8_t cmdno = *cmd & 0x3f; + wiggle_slow_pos(5); + switch(*cmd & 0x3f) { + case 0: + rsplen = 0; + break; + case 2: + case 9: + case 10: + rsplen = 17; + break; + default: + rsplen = 6; + } + /* send command */ + BITBAND(SD_CMDREG->FIODIR, SD_CMDPIN) = 1; + + while(i--) { + shift = 8; + do { + shift--; + uint8_t data = *cmd; + *cmd<<=1; + if(data&0x80) { + BITBAND(SD_CMDREG->FIOSET, SD_CMDPIN) = 1; + } else { + BITBAND(SD_CMDREG->FIOCLR, SD_CMDPIN) = 1; + } + wiggle_slow_pos(1); + } while (shift); + cmd++; + } + + wiggle_slow_pos(1); + BITBAND(SD_CMDREG->FIODIR, SD_CMDPIN) = 0; + + if(rsplen) { + uint16_t timeout=1000; + while((BITBAND(SD_CMDREG->FIOPIN, SD_CMDPIN)) && --timeout) { + wiggle_slow_neg(1); + } + if(!timeout) { + printf("CMD%d timed out\n", cmdno); + return 0; /* no response within timeout */ + } + + i=rsplen; + while(i--) { + shift = 8; + uint8_t data=0; + do { + shift--; + data |= (BITBAND(SD_CMDREG->FIOPIN, SD_CMDPIN)) << shift; + wiggle_slow_neg(1); + } while (shift); + *rsp=data; + rsp++; + } + } + return rsplen; +} + + +/* + send_command_fast + send SD command and put response in rsp. + returns length of response or 0 if there was no response +*/ +int send_command_fast(uint8_t* cmd, uint8_t* rsp, uint8_t* buf){ + uint8_t datshift=8, cmdshift, i=6; + uint8_t cmdno = *cmd & 0x3f; + int rsplen, dat=0, waitbusy=0, datcnt=512, j=0; + static int state=CMD_RSP; + wiggle_fast_pos(9); /* give the card >=8 cycles after last command */ + DBG_SD printf("send_command_fast: sending CMD%d; payload=%02x%02x%02x%02x%02x%02x...\n", cmdno, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]); + switch(*cmd & 0x3f) { + case 0: + rsplen = 0; + break; + case 2: + case 9: + case 10: + rsplen = 17; + break; + case 12: + rsplen = 6; + waitbusy = 1; + break; + case 13: + case 17: + case 18: + dat = 1; + default: + rsplen = 6; + } + if(dat && (buf==NULL)) { + printf("send_command_fast error: buf is null but data transfer expected.\n"); + return 0; + } + /* send command */ + BITBAND(SD_CMDREG->FIODIR, SD_CMDPIN) = 1; + + while(i--) { + uint8_t data = *cmd; + cmdshift = 8; + do { + cmdshift--; + if(data&0x80) { + BITBAND(SD_CMDREG->FIOSET, SD_CMDPIN) = 1; + } else { + BITBAND(SD_CMDREG->FIOCLR, SD_CMDPIN) = 1; + } + data<<=1; + wiggle_fast_pos1(); + } while (cmdshift); + cmd++; + } + + wiggle_fast_pos1(); + BITBAND(SD_CMDREG->FIODIR, SD_CMDPIN) = 0; + + if(rsplen) { + uint32_t timeout=2000000; + /* wait for response */ + while((BITBAND(SD_CMDREG->FIOPIN, SD_CMDPIN)) && --timeout) { + wiggle_fast_neg1(); + } + if(!timeout) { + printf("CMD%d timed out\n", cmdno); + return 0; /* no response within timeout */ + } + + i=rsplen; + uint8_t cmddata=0, datdata=0; + while(i--) { /* process response */ + cmdshift = 8; + do { + if(dat) { + if(!(BITBAND(SD_DAT0REG->FIOPIN, SD_DAT0PIN))) { + printf("data start during response\n"); + j=datcnt; + state=CMD_RSPDAT; + break; + } + } + cmdshift--; + cmddata |= (BITBAND(SD_CMDREG->FIOPIN, SD_CMDPIN)) << cmdshift; + wiggle_fast_neg1(); + } while (cmdshift); + if(state==CMD_RSPDAT)break; + *rsp=cmddata; + cmddata=0; + rsp++; + } + + if(state==CMD_RSPDAT) { /* process response+data */ + int startbit=1; + DBG_SD printf("processing rsp+data cmdshift=%d i=%d j=%d\n", cmdshift, i, j); + datshift=8; + while(1) { + cmdshift--; + cmddata |= (BITBAND(SD_CMDREG->FIOPIN, SD_CMDPIN)) << cmdshift; + if(!cmdshift) { + cmdshift=8; + *rsp=cmddata; + cmddata=0; + rsp++; + i--; + if(!i) { + DBG_SD printf("response end\n"); + if(j) state=CMD_DAT; /* response over, remaining data */ + break; + } + } + if(!startbit) { + datshift-=4; + datdata |= SD_DAT << datshift; + if(!datshift) { + datshift=8; + *buf=datdata; + datdata=0; + buf++; + j--; + if(!j) break; + } + } + startbit=0; + wiggle_fast_neg1(); + } + } + + if(dat && state != CMD_DAT) { /* response ended before data */ + BITBAND(SD_CMDREG->FIODIR, SD_CMDPIN) = 1; + state=CMD_DAT; + j=datcnt; + datshift=8; + DBG_SD printf("response over, waiting for data...\n"); + /* wait for data start bit on DAT0 */ + while((BITBAND(SD_DAT0REG->FIOPIN, SD_DAT0PIN)) && --timeout) { + wiggle_fast_neg1(); + } + DBG_SD if(!timeout) printf("timed out!\n"); + wiggle_fast_neg1(); /* eat the start bit */ + if(sd_offload) { + if(sd_offload_partial) { + fpga_set_sddma_range(sd_offload_partial_start, sd_offload_partial_end); + fpga_sddma(sd_offload_tgt, 1); + sd_offload_partial=0; + } else { + fpga_sddma(sd_offload_tgt, 0); + } + state=CMD_RSP; + return rsplen; + } + } + + if(state==CMD_DAT) { /* transfer rest of data */ + DBG_SD printf("remaining data: %d\n", j); + if(datshift==8) { + while(1) { + datdata |= SD_DAT << 4; + wiggle_fast_neg1(); + + datdata |= SD_DAT; + wiggle_fast_neg1(); + + *buf=datdata; + datdata=0; + buf++; + j--; + if(!j) break; + } + } else { + + while(1) { + datshift-=4; + datdata |= SD_DAT << datshift; + if(!datshift) { + datshift=8; + *buf=datdata; + datdata=0; + buf++; + j--; + if(!j) break; + } + wiggle_fast_neg1(); + } + } + } + if(dat) { +#ifdef CONFIG_SD_DATACRC + if(get_and_check_datacrc(buf-512)) { + return CRC_ERROR; + } +#else + /* eat the crcs */ + wiggle_fast_neg(17); +#endif + } + + if(waitbusy) { + DBG_SD printf("waitbusy after send_cmd\n"); + wait_busy(); + } + state=CMD_RSP; + } + rsp-=rsplen; + DBG_SD printf("send_command_fast: CMD%d response: %02x%02x%02x%02x%02x%02x\n", cmdno, rsp[0], rsp[1], rsp[2], rsp[3], rsp[4], rsp[5]); + BITBAND(SD_CMDREG->FIODIR, SD_CMDPIN) = 1; + return rsplen; +} + + +static inline void make_crc7(uint8_t* cmd) { + cmd[5]=crc7update(0, cmd[0]); + cmd[5]=crc7update(cmd[5], cmd[1]); + cmd[5]=crc7update(cmd[5], cmd[2]); + cmd[5]=crc7update(cmd[5], cmd[3]); + cmd[5]=crc7update(cmd[5], cmd[4]); + cmd[5]=(cmd[5] << 1) | 1; +} + +int cmd_slow(uint8_t cmd, uint32_t param, uint8_t crc, uint8_t* dat, uint8_t* rsp) { + uint8_t cmdbuf[6]; + cmdbuf[0] = 0x40 | cmd; + cmdbuf[1] = param >> 24; + cmdbuf[2] = param >> 16; + cmdbuf[3] = param >> 8; + cmdbuf[4] = param; + if(!crc) { + make_crc7(cmdbuf); + } else { + cmdbuf[5] = crc; + } + return send_command_slow(cmdbuf, rsp); +} + +int acmd_slow(uint8_t cmd, uint32_t param, uint8_t crc, uint8_t* dat, uint8_t* rsp) { + if(!(cmd_slow(APP_CMD, rca, 0, NULL, rsp))) { + return 0; + } + return cmd_slow(cmd, param, crc, dat, rsp); +} + +int cmd_fast(uint8_t cmd, uint32_t param, uint8_t crc, uint8_t* dat, uint8_t* rsp) { + uint8_t cmdbuf[6]; + cmdbuf[0] = 0x40 | cmd; + cmdbuf[1] = param >> 24; + cmdbuf[2] = param >> 16; + cmdbuf[3] = param >> 8; + cmdbuf[4] = param; + if(!crc) { + make_crc7(cmdbuf); + } else { + cmdbuf[5] = crc; + } + return send_command_fast(cmdbuf, rsp, dat); +} + +int acmd_fast(uint8_t cmd, uint32_t param, uint8_t crc, uint8_t* dat, uint8_t* rsp) { + if(!(cmd_fast(APP_CMD, rca, 0, NULL, rsp))) { + return 0; + } + return cmd_fast(cmd, param, crc, dat, rsp); +} + +void sdn_checkinit(BYTE drv) { + if(disk_state == DISK_CHANGED) { + disk_initialize(drv); + } +} + +int stream_datablock(uint8_t *buf) { +// uint8_t datshift=8; + int j=512; + uint8_t datdata=0; + uint32_t timeout=1000000; + + DBG_SD printf("stream_datablock: wait for ready...\n"); + while((BITBAND(SD_DAT0REG->FIOPIN, SD_DAT0PIN)) && --timeout) { + wiggle_fast_neg1(); + } + DBG_SD if(!timeout) printf("timeout!\n"); + + wiggle_fast_neg1(); /* eat the start bit */ + if(sd_offload) { + if(sd_offload_partial) { + fpga_set_sddma_range(sd_offload_partial_start, sd_offload_partial_end); + fpga_sddma(sd_offload_tgt, 1); + sd_offload_partial=0; + } else { + fpga_sddma(sd_offload_tgt, 0); + } + } else { + while(1) { + datdata = SD_DAT << 4; + wiggle_fast_neg1(); + + datdata |= SD_DAT; + wiggle_fast_neg1(); + + *buf=datdata; + buf++; + j--; + if(!j) break; + } +#ifdef CONFIG_SD_DATACRC + return get_and_check_datacrc(buf-512); +#else + /* eat the crcs */ + wiggle_fast_neg(17); +#endif + } + return 0; +} + +void send_datablock(uint8_t *buf) { + uint16_t crc0=0, crc1=0, crc2=0, crc3=0, cnt=512; + uint8_t dat0=0, dat1=0, dat2=0, dat3=0, crcshift, datshift; + + wiggle_fast_pos1(); + BITBAND(SD_DAT0REG->FIODIR, SD_DAT0PIN) = 1; + BITBAND(SD_DAT1REG->FIODIR, SD_DAT1PIN) = 1; + BITBAND(SD_DAT2REG->FIODIR, SD_DAT2PIN) = 1; + BITBAND(SD_DAT3REG->FIODIR, SD_DAT3PIN) = 1; + + BITBAND(SD_DAT0REG->FIOCLR, SD_DAT0PIN) = 1; + BITBAND(SD_DAT1REG->FIOCLR, SD_DAT1PIN) = 1; + BITBAND(SD_DAT2REG->FIOCLR, SD_DAT2PIN) = 1; + BITBAND(SD_DAT3REG->FIOCLR, SD_DAT3PIN) = 1; + + wiggle_fast_pos1(); /* send start bit to card */ + crcshift=8; + while(cnt--) { + datshift=8; + do { + datshift-=4; +/* if(((*buf)>>datshift) & 0x8) { + BITBAND(SD_DAT3REG->FIOSET, SD_DAT3PIN) = 1; + } else { + BITBAND(SD_DAT3REG->FIOCLR, SD_DAT3PIN) = 1; + } + if(((*buf)>>datshift) & 0x4) { + BITBAND(SD_DAT2REG->FIOSET, SD_DAT2PIN) = 1; + } else { + BITBAND(SD_DAT2REG->FIOCLR, SD_DAT2PIN) = 1; + } + if(((*buf)>>datshift) & 0x2){ + BITBAND(SD_DAT1REG->FIOSET, SD_DAT1PIN) = 1; + } else { + BITBAND(SD_DAT1REG->FIOCLR, SD_DAT1PIN) = 1; + } + if(((*buf)>>datshift) & 0x1){ + BITBAND(SD_DAT0REG->FIOSET, SD_DAT0PIN) = 1; + } else { + BITBAND(SD_DAT0REG->FIOCLR, SD_DAT0PIN) = 1; + }*/ + SD_DAT0REG->FIOPIN0 = (*buf) >> datshift; + wiggle_fast_pos1(); + } while (datshift); + + crcshift-=2; + dat0 |= (((*buf)&0x01) | (((*buf)&0x10) >> 3)) << crcshift; + dat1 |= ((((*buf)&0x02) >> 1) | (((*buf)&0x20) >> 4)) << crcshift; + dat2 |= ((((*buf)&0x04) >> 2) | (((*buf)&0x40) >> 5)) << crcshift; + dat3 |= ((((*buf)&0x08) >> 3) | (((*buf)&0x80) >> 6)) << crcshift; + if(!crcshift) { + crc0 = crc_xmodem_update(crc0, dat0); + crc1 = crc_xmodem_update(crc1, dat1); + crc2 = crc_xmodem_update(crc2, dat2); + crc3 = crc_xmodem_update(crc3, dat3); + crcshift=8; + dat0=0; + dat1=0; + dat2=0; + dat3=0; + } + buf++; + } +// printf("crc0=%04x crc1=%04x crc2=%04x crc3=%04x ", crc0, crc1, crc2, crc3); + /* send crcs */ + datshift=16; + do { + datshift--; + if((crc0 >> datshift) & 1) { + BITBAND(SD_DAT0REG->FIOSET, SD_DAT0PIN) = 1; + } else { + BITBAND(SD_DAT0REG->FIOCLR, SD_DAT0PIN) = 1; + } + if((crc1 >> datshift) & 1) { + BITBAND(SD_DAT1REG->FIOSET, SD_DAT1PIN) = 1; + } else { + BITBAND(SD_DAT1REG->FIOCLR, SD_DAT1PIN) = 1; + } + if((crc2 >> datshift) & 1) { + BITBAND(SD_DAT2REG->FIOSET, SD_DAT2PIN) = 1; + } else { + BITBAND(SD_DAT2REG->FIOCLR, SD_DAT2PIN) = 1; + } + if((crc3 >> datshift) & 1) { + BITBAND(SD_DAT3REG->FIOSET, SD_DAT3PIN) = 1; + } else { + BITBAND(SD_DAT3REG->FIOCLR, SD_DAT3PIN) = 1; + } + wiggle_fast_pos1(); + } while(datshift); + /* send end bit */ + BITBAND(SD_DAT0REG->FIOSET, SD_DAT0PIN) = 1; + BITBAND(SD_DAT1REG->FIOSET, SD_DAT1PIN) = 1; + BITBAND(SD_DAT2REG->FIOSET, SD_DAT2PIN) = 1; + BITBAND(SD_DAT3REG->FIOSET, SD_DAT3PIN) = 1; + + wiggle_fast_pos1(); + + BITBAND(SD_DAT0REG->FIODIR, SD_DAT0PIN) = 0; + BITBAND(SD_DAT1REG->FIODIR, SD_DAT1PIN) = 0; + BITBAND(SD_DAT2REG->FIODIR, SD_DAT2PIN) = 0; + BITBAND(SD_DAT3REG->FIODIR, SD_DAT3PIN) = 0; + + wiggle_fast_neg(3); + dat0=0; + + datshift=4; + do { + datshift--; + dat0 |= ((BITBAND(SD_DAT0REG->FIOPIN, SD_DAT0PIN)) << datshift); + wiggle_fast_neg1(); + } while (datshift); + DBG_SD printf("crc %02x\n", dat0); + if((dat0 & 7) != 2) { + printf("crc error! %02x\n", dat0); + while(1); + } + if(dat0 & 8) { + printf("missing start bit in CRC status response...\n"); + } + wiggle_fast_neg(2); + wait_busy(); +} + +void read_block(uint32_t address, uint8_t *buf) { + if(during_blocktrans == TRANS_READ && (last_block == address-1)) { +//uart_putc('r'); +#ifdef CONFIG_SD_DATACRC + int cmd_res; + if((cmd_res = stream_datablock(buf)) == CRC_ERROR) { + while(cmd_res == CRC_ERROR) { + cmd_fast(STOP_TRANSMISSION, 0, 0x61, NULL, rsp); + cmd_res = cmd_fast(READ_MULTIPLE_BLOCK, address, 0, buf, rsp); + } + } +#else + stream_datablock(buf); +#endif + last_block=address; + } else { + if(during_blocktrans) { +// uart_putc('_'); +//printf("nonseq read (%lx -> %lx), restarting transmission\n", last_block, address); + /* send STOP_TRANSMISSION to end an open READ/WRITE_MULTIPLE_BLOCK */ + cmd_fast(STOP_TRANSMISSION, 0, 0x61, NULL, rsp); + } + last_block=address; + if(!ccs) { + address <<= 9; + } +#ifdef CONFIG_SD_DATACRC + while(1) { + if(cmd_fast(READ_MULTIPLE_BLOCK, address, 0, buf, rsp) != CRC_ERROR) break; + cmd_fast(STOP_TRANSMISSION, 0, 0x61, NULL, rsp); + }; +#else + cmd_fast(READ_MULTIPLE_BLOCK, address, 0, buf, rsp); +#endif + during_blocktrans = TRANS_READ; + } +} + +void write_block(uint32_t address, uint8_t* buf) { + if(during_blocktrans == TRANS_WRITE && (last_block == address-1)) { + wait_busy(); + send_datablock(buf); + last_block=address; + } else { + if(during_blocktrans) { + /* send STOP_TRANSMISSION to end an open READ/WRITE_MULTIPLE_BLOCK */ + cmd_fast(STOP_TRANSMISSION, 0, 0x61, NULL, rsp); + } + wait_busy(); + last_block=address; + if(!ccs) { + address <<= 9; + } + /* only send cmd & get response */ + cmd_fast(WRITE_MULTIPLE_BLOCK, address, 0, NULL, rsp); + DBG_SD printf("write_block: CMD25 response = %02x%02x%02x%02x%02x%02x\n", rsp[0], rsp[1], rsp[2], rsp[3], rsp[4], rsp[5]); + wiggle_fast_pos(8); + send_datablock(buf); + during_blocktrans = TRANS_WRITE; + } +} + +// +// Public functions +// + +DRESULT sdn_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) { + uint8_t sec; + if(drv >= MAX_CARDS) { + return RES_PARERR; + } + readled(1); + for(sec=0; sec=MAX_CARDS) { + return STA_NOINIT|STA_NODISK; + } + + if(sdn_status(drv) & STA_NODISK) { + return STA_NOINIT|STA_NODISK; + } + /* if the card is sending data from before a reset we try to deselect it + prior to initialization */ + for(rsplen=0; rsplen<2042; rsplen++) { + if(!(BITBAND(SD_DAT3REG->FIOPIN, SD_DAT3PIN))) { + printf("card seems to be sending data, attempting deselect\n"); + cmd_slow(SELECT_CARD, 0, 0, NULL, rsp); + } + wiggle_slow_neg(1); + } + printf("sd_init start\n"); + BITBAND(SD_DAT3REG->FIODIR, SD_DAT3PIN) = 1; + BITBAND(SD_DAT3REG->FIOSET, SD_DAT3PIN) = 1; + cmd_slow(GO_IDLE_STATE, 0, 0x95, NULL, rsp); + + if((rsplen=cmd_slow(SEND_IF_COND, 0x000001aa, 0x87, NULL, rsp))) { + DBG_SD printf("CMD8 response:\n"); + DBG_SD uart_trace(rsp, 0, rsplen); + hcs=1; + } + while(1) { + if(!(acmd_slow(SD_SEND_OP_COND, (hcs << 30) | 0xfc0000, 0, NULL, rsp))) { + printf("ACMD41 no response!\n"); + } + if(rsp[1]&0x80) break; + } + + BITBAND(SD_DAT3REG->FIODIR, SD_DAT3PIN) = 0; + BITBAND(SD_DAT3REG->FIOCLR, SD_DAT3PIN) = 1; + + ccs = (rsp[1]>>6) & 1; /* SDHC/XC */ + + cmd_slow(ALL_SEND_CID, 0, 0x4d, NULL, rsp); + if(cmd_slow(SEND_RELATIVE_ADDR, 0, 0x21, NULL, rsp)) { + rca=(rsp[1]<<24) | (rsp[2]<<16); + printf("RCA: %04lx\n", rca>>16); + } else { + printf("CMD3 no response!\n"); + rca=0; + } + + /* record CSD for getinfo */ + cmd_slow(SEND_CSD, rca, 0, NULL, rsp); + + /* select the card */ + if(cmd_slow(SELECT_CARD, rca, 0, NULL, rsp)) { + printf("card selected!\n"); + } else { + printf("CMD7 no response!\n"); + } + + /* get card status */ + cmd_slow(SEND_STATUS, rca, 0, NULL, rsp); + + /* set bus width */ + acmd_slow(SD_SET_BUS_WIDTH, 0x2, 0, NULL, rsp); + + /* set block length */ + cmd_slow(SET_BLOCKLEN, 0x200, 0, NULL, rsp); + + printf("SD init complete. SDHC/XC=%d\n", ccs); + disk_state = DISK_OK; + during_blocktrans = TRANS_NONE; + return sdn_status(drv); +} + +DSTATUS disk_initialize(BYTE drv) __attribute__ ((weak, alias("sdn_initialize"))); + +void sdn_init(void) { + /* enable GPIO interrupt on SD detect pin, both edges */ +/* NVIC_EnableIRQ(EINT3_IRQn); + SD_DT_INT_SETUP(); */ + /* disconnect SSP1 */ + LPC_PINCON->PINSEL0 &= ~(BV(13) | BV(15) | BV(17) | BV(19)); + /* prepare GPIOs */ + BITBAND(SD_DAT3REG->FIODIR, SD_DAT3PIN) = 0; + BITBAND(SD_DAT2REG->FIODIR, SD_DAT2PIN) = 0; + BITBAND(SD_DAT1REG->FIODIR, SD_DAT1PIN) = 0; + BITBAND(SD_DAT0REG->FIODIR, SD_DAT0PIN) = 0; + BITBAND(SD_CLKREG->FIODIR, SD_CLKPIN) = 1; + BITBAND(SD_CMDREG->FIODIR, SD_CMDPIN) = 1; + BITBAND(SD_CMDREG->FIOPIN, SD_CMDPIN) = 1; + LPC_GPIO2->FIOPIN0 = 0x00; + LPC_GPIO2->FIOMASK0 = ~0xf; +} +void disk_init(void) __attribute__ ((weak, alias("sdn_init"))); + + +DSTATUS sdn_status(BYTE drv) { + 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("sdn_status"))); + +DRESULT sdn_getinfo(BYTE drv, BYTE page, void *buffer) { + uint32_t capacity; + + if (drv >= MAX_CARDS) { + return RES_NOTRDY; + } + if (sdn_status(drv) & STA_NODISK) { + return RES_NOTRDY; + } + if (page != 0) { + return RES_ERROR; + } + if (ccs) { + /* Special CSD for SDHC cards */ + capacity = (1 + getbits(csd,127-69+8,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(csd, 127-49+8, 3); + capacity = 1 + getbits(csd, 127-73+8, 12); + exponent += getbits(csd, 127-83+8,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; + + printf("card capacity: %lu sectors\n", capacity); + return RES_OK; +} +DRESULT disk_getinfo(BYTE drv, BYTE page, void *buffer) __attribute__ ((weak, alias("sdn_getinfo"))); + +DRESULT sdn_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) { + uint8_t sec; + uint8_t *buf = (uint8_t*)buffer; + if(drv >= MAX_CARDS) { + return RES_NOTRDY; + } + if (sdn_status(drv) & STA_NODISK) { + return RES_NOTRDY; + } + writeled(1); + for(sec=0; sec + 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 + + smc.c: SMC file related operations +*/ + +#include "fileops.h" +#include "config.h" +#include "uart.h" +#include "smc.h" +#include "string.h" +#include "fpga_spi.h" + +snes_romprops_t romprops; + +uint32_t hdr_addr[6] = {0xffb0, 0x101b0, 0x7fb0, 0x81b0, 0x40ffb0, 0x4101b0}; +uint8_t countAllASCII(uint8_t* data, int size) { + uint8_t res = 0; + do { + size--; + if(data[size] >= 0x20 && data[size] <= 0x7e) { + res++; + } + } while (size); + return res; +} + +uint8_t countAllJISX0201(uint8_t* data, int size) { + uint8_t res = 0; + do { + size--; + if((data[size] >= 0x20 && data[size] <= 0x7e) + ||(data[size] >= 0xa1 && data[size] <= 0xdf)) { + res++; + } + } while (size); + return res; +} + +uint8_t isFixed(uint8_t* data, int size, uint8_t value) { + uint8_t res = 1; + do { + size--; + if(data[size] != value) { + res = 0; + } + } while (size); + return res; +} + +uint8_t checkChksum(uint16_t cchk, uint16_t chk) { + uint32_t sum = cchk + chk; + uint8_t res = 0; + if(sum==0x0000ffff) { + res = 0x10; + } + return res; +} + +void smc_id(snes_romprops_t* props) { + uint8_t score, maxscore=1, score_idx=2; /* assume LoROM */ + snes_header_t* header = &(props->header); + + props->has_dspx = 0; + props->has_st0010 = 0; + props->has_cx4 = 0; + props->fpga_features = 0; + props->fpga_conf = NULL; + for(uint8_t num = 0; num < 6; num++) { + if(!file_readblock(header, hdr_addr[num], sizeof(snes_header_t)) + || file_res) { + score = 0; + } else { + score = smc_headerscore(header)/(1+(num&1)); + if((file_handle.fsize & 0x2ff) == 0x200) { + if(num&1) { + score+=20; + } else { + score=0; + } + } else { + if(!(num&1)) { + score+=20; + } else { + score=0; + } + } + } +//printf("%d: offset = %lX; score = %d\n", num, hdr_addr[num], score); // */ + if(score>=maxscore) { + score_idx=num; + maxscore=score; + } + } + + if(score_idx & 1) { + props->offset = 0x200; + } else { + props->offset = 0; + } + + /* restore the chosen one */ +/*dprintf("winner is %d\n", score_idx); */ + file_readblock(header, hdr_addr[score_idx], sizeof(snes_header_t)); + + if(header->name[0x13] == 0x00 || header->name[0x13] == 0xff) { + if(header->name[0x14] == 0x00) { + const uint8_t n15 = header->map; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c + || n15 == 0xbc || n15 == 0xfc) { + if(header->licensee == 0x33 || header->licensee == 0xff) { + props->mapper_id = 0; +/*XXX do this properly */ + props->ramsize_bytes = 0x8000; + props->romsize_bytes = 0x100000; + props->expramsize_bytes = 0; + props->mapper_id = 3; /* BS-X Memory Map */ + return; + } + } + } + } + switch(header->map & 0xef) { + + case 0x21: /* HiROM */ + props->mapper_id = 0; + if(header->map == 0x31 && (header->carttype == 0x03 || header->carttype == 0x05)) { + props->has_dspx = 1; + props->dsp_fw = DSPFW_1B; + props->fpga_features |= FEAT_DSPX; + } + break; + + case 0x20: /* LoROM */ + props->mapper_id = 1; + if (header->map == 0x20 && header->carttype == 0xf3) { + props->has_cx4 = 1; + props->dsp_fw = CX4FW; + props->fpga_conf = FPGA_CX4; + props->fpga_features |= FEAT_CX4; + } + else if ((header->map == 0x20 && header->carttype == 0x03) || + (header->map == 0x30 && header->carttype == 0x05 && header->licensee != 0xb2)) { + props->has_dspx = 1; + props->fpga_features |= FEAT_DSPX; + // Pilotwings uses DSP1 instead of DSP1B + if(!memcmp(header->name, "PILOTWINGS", 10)) { + props->dsp_fw = DSPFW_1; + } else { + props->dsp_fw = DSPFW_1B; + } + } else if (header->map == 0x20 && header->carttype == 0x05) { + props->has_dspx = 1; + props->dsp_fw = DSPFW_2; + props->fpga_features |= FEAT_DSPX; + } else if (header->map == 0x30 && header->carttype == 0x05 && header->licensee == 0xb2) { + props->has_dspx = 1; + props->dsp_fw = DSPFW_3; + props->fpga_features |= FEAT_DSPX; + } else if (header->map == 0x30 && header->carttype == 0x03) { + props->has_dspx = 1; + props->dsp_fw = DSPFW_4; + props->fpga_features |= FEAT_DSPX; + } else if (header->map == 0x30 && header->carttype == 0xf6 && header->romsize >= 0xa) { + props->has_dspx = 1; + props->has_st0010 = 1; + props->dsp_fw = DSPFW_ST0010; + props->fpga_features |= FEAT_ST0010; + header->ramsize = 2; + } + break; + + case 0x25: /* ExHiROM */ + props->mapper_id = 2; + break; + + case 0x22: /* ExLoROM */ + if(file_handle.fsize > 0x400200) { + props->mapper_id = 6; /* SO96 */ + } else { + props->mapper_id = 1; + } + break; + + default: /* invalid/unsupported mapper, use header location */ + switch(score_idx) { + case 0: + case 1: + props->mapper_id = 0; + break; + case 2: + case 3: + if(file_handle.fsize > 0x800200) { + props->mapper_id = 6; /* SO96 interleaved */ + } else if(file_handle.fsize > 0x400200) { + props->mapper_id = 1; /* ExLoROM */ + } else { + props->mapper_id = 1; /* LoROM */ + } + break; + case 4: + case 5: + props->mapper_id = 2; + break; + default: + props->mapper_id = 1; // whatever + } + } + if(header->romsize == 0 || header->romsize > 13) { + header->romsize = 13; + } + props->ramsize_bytes = (uint32_t)1024 << header->ramsize; + props->romsize_bytes = (uint32_t)1024 << header->romsize; + props->expramsize_bytes = (uint32_t)1024 << header->expramsize; +/*dprintf("ramsize_bytes: %ld\n", props->ramsize_bytes); */ + if(props->ramsize_bytes > 32768 || props->ramsize_bytes < 2048) { + props->ramsize_bytes = 0; + } +/*dprintf("ramsize_bytes: %ld\n", props->ramsize_bytes); */ +} + +uint8_t smc_headerscore(snes_header_t* header) { + uint8_t score=0; + score += countAllASCII(header->maker, sizeof(header->maker)); + score += countAllASCII(header->gamecode, sizeof(header->gamecode)); + score += isFixed(header->fixed_00, sizeof(header->fixed_00), 0x00); + score += countAllJISX0201(header->name, sizeof(header->name)); + score += 3*isFixed(&header->licensee, sizeof(header->licensee), 0x33); + score += checkChksum(header->cchk, header->chk); + return score; +} + diff --git a/src/tests/smc.h b/src/tests/smc.h new file mode 100644 index 0000000..8e77b7c --- /dev/null +++ b/src/tests/smc.h @@ -0,0 +1,79 @@ +/* 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 + + smc.h: SMC file structures +*/ + +#ifndef SMC_H +#define SMC_H + +#define DSPFW_1 ((const uint8_t*)"/sd2snes/dsp1.bin") +#define DSPFW_2 ((const uint8_t*)"/sd2snes/dsp2.bin") +#define DSPFW_3 ((const uint8_t*)"/sd2snes/dsp3.bin") +#define DSPFW_4 ((const uint8_t*)"/sd2snes/dsp4.bin") +#define DSPFW_1B ((const uint8_t*)"/sd2snes/dsp1b.bin") +#define DSPFW_ST0010 ((const uint8_t*)"/sd2snes/st0010.bin") +#define CX4FW ((const uint8_t*)"/sd2snes/cx4.bin") + +#define FPGA_CX4 ((const uint8_t*)"/sd2snes/fpga_cx4.bit") + +typedef struct _snes_header { + uint8_t maker[2]; /* 0xB0 */ + uint8_t gamecode[4]; /* 0xB2 */ + uint8_t fixed_00[7]; /* 0xB6 */ + uint8_t expramsize; /* 0xBD */ + uint8_t specver; /* 0xBE */ + uint8_t carttype2; /* 0xBF */ + uint8_t name[21]; /* 0xC0 */ + uint8_t map; /* 0xD5 */ + uint8_t carttype; /* 0xD6 */ + uint8_t romsize; /* 0xD7 */ + uint8_t ramsize; /* 0xD8 */ + uint8_t destcode; /* 0xD9 */ + uint8_t licensee; /* 0xDA */ + uint8_t ver; /* 0xDB */ + uint16_t cchk; /* 0xDC */ + uint16_t chk; /* 0xDE */ +} snes_header_t; + +typedef struct _snes_romprops { + uint16_t offset; /* start of actual ROM image */ + uint8_t mapper_id; /* FPGA mapper */ + uint8_t pad1; /* for alignment */ + uint32_t expramsize_bytes; /* ExpRAM size in bytes */ + uint32_t ramsize_bytes; /* CartRAM size in bytes */ + uint32_t romsize_bytes; /* ROM size in bytes (rounded up) */ + const uint8_t* dsp_fw; /* DSP (NEC / Hitachi) ROM filename */ + const uint8_t* fpga_conf; /* FPGA config file to load (default: base) */ + uint8_t has_dspx; /* DSP[1-4] presence flag */ + uint8_t has_st0010; /* st0010 presence flag (additional to dspx)*/ + uint8_t has_msu1; /* MSU1 presence flag */ + uint8_t has_cx4; /* CX4 presence flag */ + uint8_t fpga_features; /* feature/peripheral enable bits*/ + snes_header_t header; /* original header from ROM image */ +} snes_romprops_t; + +void smc_id(snes_romprops_t*); +uint8_t smc_headerscore(snes_header_t*); + +#endif diff --git a/src/tests/snes.c b/src/tests/snes.c new file mode 100644 index 0000000..3171502 --- /dev/null +++ b/src/tests/snes.c @@ -0,0 +1,174 @@ +/* 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 "bits.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" +#include "timer.h" +#include "cli.h" +#include "fpga.h" +#include "fpga_spi.h" + +uint8_t initloop=1; +uint32_t saveram_crc, saveram_crc_old; +extern snes_romprops_t romprops; + +volatile int reset_changed; + +void prepare_reset() { + snes_reset(1); + delay_ms(1); + if(romprops.ramsize_bytes && fpga_test() == FPGA_TEST_TOKEN) { + writeled(1); + save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); + writeled(0); + } + rdyled(1); + readled(1); + writeled(1); + snes_reset(0); + while(get_snes_reset()); + snes_reset(1); + fpga_dspx_reset(1); + delay_ms(200); +} + +void snes_init() { + /* put reset level on reset pin */ + BITBAND(SNES_RESET_REG->FIOCLR, SNES_RESET_BIT) = 1; + /* reset the SNES */ + snes_reset(1); +} + +/* + * sets the SNES reset state. + * + * state: put SNES in reset state when 1, release when 0 + */ +void snes_reset(int state) { + BITBAND(SNES_RESET_REG->FIODIR, SNES_RESET_BIT) = state; +} + +/* + * gets the SNES reset state. + * + * returns: 1 when reset, 0 when not reset + */ +uint8_t get_snes_reset() { + return !BITBAND(SNES_RESET_REG->FIOPIN, SNES_RESET_BIT); +} + +/* + * 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) { + printf("SaveRAM CRC: 0x%04lx; saving\n", saveram_crc); + writeled(1); + save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); + writeled(0); + didnotsave=0; + } + if(didnotsave>50) { + printf("periodic save (sram contents keep changing...)\n"); + diffcount=0; + writeled(1); + save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); + didnotsave=0; + writeled(0); + } + saveram_crc_old = saveram_crc; + } + printf("crc=%lx crc_valid=%d sram_valid=%d diffcount=%ld samecount=%ld, didnotsave=%ld\n", saveram_crc, 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())printf("hurr\n"); + cmd = sram_readbyte(SRAM_CMD_ADDR); + } + if(get_snes_reset()) { + cmd = 0; + } + sleep_ms(20); + cli_entrycheck(); + } + return cmd; +} + +void get_selected_name(uint8_t* fn) { + uint32_t addr; + addr = sram_readlong(SRAM_PARAM_ADDR); + printf("fd addr=%lx\n", addr); + sram_readblock(fn, addr + 7 + SRAM_MENU_ADDR, 256); +} + +void snes_bootprint(void* msg) { + sram_writeblock(msg, SRAM_CMD_ADDR, 33); +} + +void snes_menu_errmsg(int err, void* msg) { + sram_writeblock(msg, SRAM_CMD_ADDR+1, 64); + sram_writebyte(err, SRAM_CMD_ADDR); +} + diff --git a/src/tests/snes.h b/src/tests/snes.h new file mode 100644 index 0000000..fa7b762 --- /dev/null +++ b/src/tests/snes.h @@ -0,0 +1,48 @@ +/* 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 + +#define SNES_CMD_LOADROM (1) +#define SNES_CMD_SETRTC (2) + +#define MENU_ERR_OK (0) +#define MENU_ERR_NODSP (1) +#define MENU_ERR_NOBSX (2) + +uint8_t crc_valid; + +void prepare_reset(void); +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); +void snes_bootprint(void* msg); +void snes_menu_errmsg(int err, void* msg); +#endif diff --git a/src/tests/sort.c b/src/tests/sort.c new file mode 100644 index 0000000..377a077 --- /dev/null +++ b/src/tests/sort.c @@ -0,0 +1,122 @@ + +#include +#include +#include +#include "config.h" +#include "uart.h" +#include "memory.h" +#include "sort.h" + +/* + heap sort algorithm for data located outside RAM + addr: start address of pointer table + i: index (in 32-bit elements) + heapsize: size of heap (in 32-bit elements) +*/ + +uint32_t stat_getstring = 0; +static char sort_str1[21], sort_str2[21]; +uint32_t ptrcache[QSORT_MAXELEM] IN_AHBRAM; + +/* get element from pointer table in external RAM*/ +uint32_t sort_get_elem(uint32_t base, unsigned int index) { + return sram_readlong(base+4*index); +} + +/* put element from pointer table in external RAM */ +void sort_put_elem(uint32_t base, unsigned int index, uint32_t elem) { + sram_writelong(elem, base+4*index); +} + +/* compare strings pointed to by elements of pointer table */ +int sort_cmp_idx(uint32_t base, unsigned int index1, unsigned int index2) { + uint32_t elem1, elem2; + elem1 = sort_get_elem(base, index1); + elem2 = sort_get_elem(base, index2); + return sort_cmp_elem((void*)&elem1, (void*)&elem2); +} + +int sort_cmp_elem(const void* elem1, const void* elem2) { + uint32_t el1 = *(uint32_t*)elem1; + uint32_t el2 = *(uint32_t*)elem2; + sort_getstring_for_dirent(sort_str1, el1); + sort_getstring_for_dirent(sort_str2, el2); +/*printf("i1=%d i2=%d elem1=%lx elem2=%lx ; compare %s --- %s\n", index1, index2, elem1, elem2, sort_str1, sort_str2); */ + + if ((el1 & 0x80000000) && !(el2 & 0x80000000)) { + return -1; + } + + if (!(el1 & 0x80000000) && (el2 & 0x80000000)) { + return 1; + } +/* + uint16_t cmp_i; + for(cmp_i=0; cmp_i<8 && sort_long1[cmp_i] == sort_long2[cmp_i]; cmp_i++); + if(cmp_i==8) { + return 0; + } + return sort_long1[cmp_i]-sort_long2[cmp_i]; */ + return strcasecmp(sort_str1, sort_str2); +} + +/* get truncated string from database */ +void sort_getstring_for_dirent(char *ptr, uint32_t addr) { + uint8_t leaf_offset; + if(addr & 0x80000000) { + /* is directory link, name offset 4 */ + leaf_offset = sram_readbyte(addr + 4 + SRAM_MENU_ADDR); + sram_readblock(ptr, addr + 5 + leaf_offset + SRAM_MENU_ADDR, 20); + } else { + /* is file link, name offset 6 */ + leaf_offset = sram_readbyte(addr + 6 + SRAM_MENU_ADDR); + sram_readblock(ptr, addr + 7 + leaf_offset + SRAM_MENU_ADDR, 20); + } + ptr[20]=0; +} + +void sort_heapify(uint32_t addr, unsigned int i, unsigned int heapsize) +{ + while(1) { + unsigned int l = 2*i+1; + unsigned int r = 2*i+2; + unsigned int largest = (l < heapsize && sort_cmp_idx(addr, i, l) < 0) ? l : i; + + if(r < heapsize && sort_cmp_idx(addr, largest, r) < 0) + largest = r; + + if(largest != i) { + uint32_t tmp = sort_get_elem(addr, i); + sort_put_elem(addr, i, sort_get_elem(addr, largest)); + sort_put_elem(addr, largest, tmp); + i = largest; + } + else break; + } +} + +void sort_dir(uint32_t addr, unsigned int size) +{ +stat_getstring=0; + if(size > QSORT_MAXELEM) { + printf("more than %d dir entries, doing slower in-place sort\n", QSORT_MAXELEM); + ext_heapsort(addr, size); + } else { + /* retrieve, sort, and store dir table */ + sram_readblock(ptrcache, addr, size*4); + qsort((void*)ptrcache, size, 4, sort_cmp_elem); + sram_writeblock(ptrcache, addr, size*4); + } +} + +void ext_heapsort(uint32_t addr, unsigned int size) { + for(unsigned int i = size/2; i > 0;) sort_heapify(addr, --i, size); + + for(unsigned int i = size-1; i>0; --i) { + uint32_t tmp = sort_get_elem(addr, 0); + sort_put_elem(addr, 0, sort_get_elem(addr, i)); + sort_put_elem(addr, i, tmp); + sort_heapify(addr, 0, i); + } +} + diff --git a/src/tests/sort.h b/src/tests/sort.h new file mode 100644 index 0000000..bb3ff00 --- /dev/null +++ b/src/tests/sort.h @@ -0,0 +1,15 @@ +#ifndef _SORT_H +#define _SORT_H + +#include + +uint32_t sort_get_elem(uint32_t base, unsigned int index); +void sort_put_elem(uint32_t base, unsigned int index, uint32_t elem); +int sort_cmp_idx(uint32_t base, unsigned int index1, unsigned int index2); +int sort_cmp_elem(const void* elem1, const void* elem2); +void sort_getstring_for_dirent(char *ptr, uint32_t addr); +void sort_getlong_for_dirent(uint32_t* ptr, uint32_t addr); +void sort_heapify(uint32_t addr, unsigned int i, unsigned int heapsize); +void sort_dir(uint32_t addr, unsigned int size); +void ext_heapsort(uint32_t addr, unsigned int size); +#endif diff --git a/src/tests/spi.c b/src/tests/spi.c new file mode 100644 index 0000000..2893a7a --- /dev/null +++ b/src/tests/spi.c @@ -0,0 +1,213 @@ +/* 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 "bits.h" +#include "config.h" +#include "spi.h" +#include "uart.h" + +void spi_preinit() { + + /* Set clock prescaler to 1:1 */ + BITBAND(LPC_SC->SSP_PCLKREG, SSP_PCLKBIT) = 1; +} + +void spi_init(spi_speed_t speed) { + + /* 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 if (speed == SPI_SPEED_SLOW) { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW; + } else if (speed == SPI_SPEED_FPGA_FAST) { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_FAST; + } else { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_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_sync() { + /* Wait until TX fifo is flushed */ + while (BITBAND(SSP_REGS->SR, SSP_BSY)) ; +} + +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_txrx_byte(uint8_t data) { + /* 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 byte */ + SSP_REGS->DR = data; + + /* Wait until answer has been received */ + while (!BITBAND(SSP_REGS->SR, SSP_RNE)) ; + + return SSP_REGS->DR; +} + +uint8_t spi_rx_byte() { + /* 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 */ + SSP_DMACH->DMACCSrcAddr = (uint32_t)&SSP_REGS->DR; + SSP_DMACH->DMACCDestAddr = (uint32_t)ptr; + SSP_DMACH->DMACCLLI = 0; // no linked list + SSP_DMACH->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 + ; + SSP_DMACH->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 (SSP_DMACH->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 if (speed == SPI_SPEED_SLOW) { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW; + } else if (speed == SPI_SPEED_FPGA_FAST) { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_FAST; + } else { + SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_SLOW; + } + + /* Enable SSP */ + SSP_REGS->CR1 = BV(1); +} diff --git a/src/tests/spi.h b/src/tests/spi.h new file mode 100644 index 0000000..8bb7a8c --- /dev/null +++ b/src/tests/spi.h @@ -0,0 +1,68 @@ +/* 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 + +#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 + +/* Low speed 400kHz for init, fast speed <=20MHz (MMC limit) */ +typedef enum { SPI_SPEED_FAST, SPI_SPEED_SLOW, SPI_SPEED_FPGA_FAST, SPI_SPEED_FPGA_SLOW } spi_speed_t; + +/* Pre-Initialize SPI interface (PCLK divider before PLL setup) */ +void spi_preinit(void); + +/* Initialize SPI interface */ +void spi_init(spi_speed_t speed); + +/* Transmit a single byte */ +void spi_tx_byte(uint8_t data); + +/* Transmit a single byte and return received data */ +uint8_t spi_txrx_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); + +/* wait for SPI TX FIFO to become empty */ +void spi_tx_sync(void); + +#endif diff --git a/src/tests/startup.S b/src/tests/startup.S new file mode 100644 index 0000000..8604e91 --- /dev/null +++ b/src/tests/startup.S @@ -0,0 +1,101 @@ +/* startup code for LPC17xx + * + * Written 2010 by Ingo Korb + */ + .syntax unified + + .section .vectors + + .macro except label + .weak \label + .set \label, __unhandled_exception + .word \label + .endm + + /* Cortex M3 standard except vectors */ + .word __stack + .word _start + except NMI_Handler + except HardFault_Handler + except MemManage_Handler + except BusFault_Handler + except UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + except SVC_Handler + except DebugMon_Handler + .word 0 + except PendSV_Handler + except SysTick_Handler + + /* External interrupt vectors */ + except WDT_IRQHandler + except TIMER0_IRQHandler + except TIMER1_IRQHandler + except TIMER2_IRQHandler + except TIMER3_IRQHandler + except UART0_IRQHandler + except UART1_IRQHandler + except UART2_IRQHandler + except UART3_IRQHandler + except PWM1_IRQHandler + except I2C0_IRQHandler + except I2C1_IRQHandler + except I2C2_IRQHandler + except SPI_IRQHandler + except SSP0_IRQHandler + except SSP1_IRQHandler + except PLL0_IRQHandler + except RTC_IRQHandler + except EINT0_IRQHandler + except EINT1_IRQHandler + except EINT2_IRQHandler + except EINT3_IRQHandler + except ADC_IRQHandler + except BOD_IRQHandler + except USB_IRQHandler + except CAN_IRQHandler + except DMA_IRQHandler + except I2S_IRQHandler + except ENET_IRQHandler + except RIT_IRQHandler + except MCPWM_IRQHandler + except QEI_IRQHandler + except PLL1_IRQHandler + + .section .text + + .global _start + .thumb_func +_start: + /* copy data section to ram */ + ldr r0, =__data_load_start + ldr r1, =__data_load_end + ldr r2, =__data_start +dataloop: + ldr.w r3, [r0], #4 + str.w r3, [r2], #4 + cmp r0, r1 + blo dataloop + + /* clear bss section */ + ldr r0, =__bss_start__ + ldr r1, =__bss_end__ + ldr r2, =0 +bssloop: + str.w r2, [r0], #4 + cmp r0, r1 + blo bssloop + + /* start main() */ + b main + + /* endless loop */ + .weak __unhandled_exception + .thumb_func +__unhandled_exception: + b __unhandled_exception + + .end diff --git a/src/tests/tests.h b/src/tests/tests.h new file mode 100644 index 0000000..1ac404b --- /dev/null +++ b/src/tests/tests.h @@ -0,0 +1,27 @@ +/* ___DISCLAIMER___ */ + +#ifndef _TESTS_H +#define _TESTS_H + +int test_sd(void); +int test_rtc(void); +int test_cic(void); +int test_fpga(void); +int test_mem(void); +int test_clk(void); + +enum tests { TEST_SD = 0, + TEST_USB, + TEST_RTC, + TEST_CIC, + TEST_FPGA, + TEST_RAM, + TEST_CLK, + TEST_DAC, + TEST_SNES_IRQ, + TEST_SNES_RAM, + TEST_SNES_PA }; + +enum teststates { NO_RUN = 0, PASSED, FAILED }; + +#endif diff --git a/src/tests/timer.c b/src/tests/timer.c new file mode 100644 index 0000000..d6012ae --- /dev/null +++ b/src/tests/timer.c @@ -0,0 +1,133 @@ +/* ___INGO___ */ + +#include +#include "bits.h" +#include "config.h" +#include "timer.h" +#include "clock.h" +#include "uart.h" +#include "sdnative.h" +#include "snes.h" +#include "led.h" +/* bit definitions */ +#define RITINT 0 +#define RITEN 3 + +#define PCRIT 16 + +extern volatile int sd_changed; +extern volatile int reset_changed; +volatile tick_t ticks; +volatile int wokefromrit; + +void __attribute__((weak,noinline)) SysTick_Hook(void) { + /* Empty function for hooking the systick handler */ +} + +/* Systick interrupt handler */ +void SysTick_Handler(void) { + ticks++; + static uint16_t sdch_state = 0; + static uint16_t reset_state = 0; + static uint16_t led_test_state = 0; + sdch_state = (sdch_state << 1) | SDCARD_DETECT | 0xe000; + if((sdch_state == 0xf000) || (sdch_state == 0xefff)) { + sd_changed = 1; + } + reset_state = (reset_state << 1) | get_snes_reset() | 0xe000; + if((reset_state == 0xf000) || (reset_state == 0xefff)) { + reset_changed = 1; + } + switch(led_test_state&0xc0) { + case 0xc0: led_test_state = 0; break; + case 0x00: + rdyled(1); + readled(0); + writeled(0); + break; + + case 0x40: + rdyled(0); + readled(1); + writeled(0); + break; + + case 0x80: + rdyled(0); + readled(0); + writeled(1); + break; + } +// led_test_state++; + sdn_changed(); + SysTick_Hook(); +} + +void __attribute__((weak,noinline)) RIT_Hook(void) { +} + +void RIT_IRQHandler(void) { + LPC_RIT->RICTRL = BV(RITINT); + NVIC_ClearPendingIRQ(RIT_IRQn); + wokefromrit = 1; + RIT_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)); +} + +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->RICOMPVAL = (CONFIG_CPU_FREQUENCY / 1000) * 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 sleep_ms(unsigned int time) { + + wokefromrit = 0; + /* Prepare RIT */ + LPC_RIT->RICOUNTER = 0; + LPC_RIT->RICOMPVAL = (CONFIG_CPU_FREQUENCY / 1000) * time; + LPC_RIT->RICTRL = BV(RITEN) | BV(RITINT); + NVIC_EnableIRQ(RIT_IRQn); + + /* Wait until RIT signals an interrupt */ +//uart_putc(';'); + while(!wokefromrit) { + __WFI(); + } + NVIC_DisableIRQ(RIT_IRQn); + /* Disable RIT */ + LPC_RIT->RICTRL = BV(RITINT); +} diff --git a/src/tests/timer.h b/src/tests/timer.h new file mode 100644 index 0000000..f775015 --- /dev/null +++ b/src/tests/timer.h @@ -0,0 +1,51 @@ +#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); +void sleep_ms(unsigned int time); + +#endif diff --git a/src/tests/uart.c b/src/tests/uart.c new file mode 100644 index 0000000..4be863c --- /dev/null +++ b/src/tests/uart.c @@ -0,0 +1,288 @@ +/* + + uart.c: UART access routines + +*/ + +#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 +# define UART_PCONBIT 3 +# define UART_PCLKREG PCLKSEL0 +# define UART_PCLKBIT 6 +# define UART_REGS LPC_UART0 +# define UART_HANDLER UART0_IRQHandler +# define UART_IRQ UART0_IRQn +#elif CONFIG_UART_NUM == 1 +# define UART_PCONBIT 4 +# define UART_PCLKREG PCLKSEL0 +# define UART_PCLKBIT 8 +# define UART_REGS LPC_UART1 +# define UART_HANDLER UART1_IRQHandler +# define UART_IRQ UART1_IRQn +#elif CONFIG_UART_NUM == 2 +# define UART_PCONBIT 24 +# define UART_PCLKREG PCLKSEL1 +# define UART_PCLKBIT 16 +# define UART_REGS LPC_UART2 +# define UART_HANDLER UART2_IRQHandler +# define UART_IRQ UART2_IRQn +#elif CONFIG_UART_NUM == 3 +# define UART_PCONBIT 25 +# define UART_PCLKREG PCLKSEL1 +# define UART_PCLKBIT 18 +# define UART_REGS LPC_UART3 +# define UART_HANDLER UART3_IRQHandler +# define UART_IRQ UART3_IRQn +#else +# error CONFIG_UART_NUM is not set or has an invalid value! +#endif +static uint8_t uart_lookupratio(float f_fr) { + uint16_t errors[72]={0,67,71,77,83,91,100,111,125, + 133,143,154,167,182,200,214,222,231, + 250,267,273,286,300,308,333,357,364, + 375,385,400,417,429,444,455,462,467, + 500,533,538,545,556,571,583,600,615, + 625,636,643,667,692,700,714,727,733, + 750,769,778,786,800,818,833,846,857, + 867,875,889,900,909,917,923,929,933}; + + 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; + int err=0, lasterr=1000; + for(i=0; i<72; i++) { + if(fr990) { + int_ratio++; + } else if(error>10) { + f_fr=1.5; + f_dl=f_pclk / (16 * baudrate * (f_fr)); + dl = (int)f_dl; + f_fr=f_pclk / (16 * baudrate * dl); + fract_ratio = uart_lookupratio(f_fr); + } + if(!dl) { + return int_ratio; + } else { + return ((fract_ratio<<16)&0xff0000) | dl; + } +} + +static char txbuf[1 << CONFIG_UART_TX_BUF_SHIFT]; +static volatile unsigned int read_idx,write_idx; + +void UART_HANDLER(void) { + int iir = UART_REGS->IIR; + if (!(iir & 1)) { + /* Interrupt is pending */ + switch (iir & 14) { +#if CONFIG_UART_NUM == 1 + case 0: /* modem status */ + (void) UART_REGS->MSR; // dummy read to clear + break; +#endif + + case 2: /* THR empty - send */ + if (read_idx != write_idx) { + int maxchars = 16; + while (read_idx != write_idx && --maxchars > 0) { + UART_REGS->THR = (unsigned char)txbuf[read_idx]; + read_idx = (read_idx+1) & (sizeof(txbuf)-1); + } + if (read_idx == write_idx) { + /* buffer empty - turn off THRE interrupt */ + BITBAND(UART_REGS->IER, 1) = 0; + } + } + break; + + case 12: /* RX timeout */ + case 4: /* data received - not implemented yet */ + (void) UART_REGS->RBR; // dummy read to clear + break; + + case 6: /* RX error */ + (void) UART_REGS->LSR; // dummy read to clear + + default: break; + } + } +} + +void uart_putc(char c) { + if (c == '\n') + uart_putc('\r'); + + unsigned int tmp = (write_idx+1) & (sizeof(txbuf)-1) ; + + if (read_idx == write_idx && (BITBAND(UART_REGS->LSR, 5))) { + /* buffer empty, THR empty -> send immediately */ + UART_REGS->THR = (unsigned char)c; + } else { +#ifdef CONFIG_UART_DEADLOCKABLE + while (tmp == read_idx) ; +#endif + BITBAND(UART_REGS->IER, 1) = 0; // turn off UART interrupt + txbuf[write_idx] = c; + write_idx = tmp; + BITBAND(UART_REGS->IER, 1) = 1; + } +} + +/* Polling version only */ +unsigned char uart_getc(void) { + /* wait for character */ + while (!(BITBAND(UART_REGS->LSR, 0))) ; + return UART_REGS->RBR; +} + +/* Returns true if a char is ready */ +unsigned char uart_gotc(void) { + return BITBAND(UART_REGS->LSR, 0); +} + +void uart_init(void) { + uint32_t div; + + /* Turn on power to UART */ + BITBAND(LPC_SC->PCONP, UART_PCONBIT) = 1; + + /* UART clock = CPU clock - this block is reduced at compile-time */ + if (CONFIG_UART_PCLKDIV == 1) { + BITBAND(LPC_SC->UART_PCLKREG, UART_PCLKBIT ) = 1; + BITBAND(LPC_SC->UART_PCLKREG, UART_PCLKBIT+1) = 0; + } else if (CONFIG_UART_PCLKDIV == 2) { + BITBAND(LPC_SC->UART_PCLKREG, UART_PCLKBIT ) = 0; + BITBAND(LPC_SC->UART_PCLKREG, UART_PCLKBIT+1) = 1; + } else if (CONFIG_UART_PCLKDIV == 4) { + BITBAND(LPC_SC->UART_PCLKREG, UART_PCLKBIT ) = 0; + BITBAND(LPC_SC->UART_PCLKREG, UART_PCLKBIT+1) = 0; + } else { // Fallback: Divide by 8 + BITBAND(LPC_SC->UART_PCLKREG, UART_PCLKBIT ) = 1; + BITBAND(LPC_SC->UART_PCLKREG, UART_PCLKBIT+1) = 1; + } + + /* set baud rate - no fractional stuff for now */ + UART_REGS->LCR = BV(7) | 3; // always 8n1 + div = baud2divisor(CONFIG_UART_BAUDRATE); + 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; + } + + /* reset and enable FIFO */ + UART_REGS->FCR = BV(0); + + /* enable transmit interrupt */ + BITBAND(UART_REGS->IER, 1) = 1; + NVIC_EnableIRQ(UART_IRQ); + + UART_REGS->THR = '?'; +} + +/* --- generic code below --- */ +void uart_puthex(uint8_t num) { + uint8_t tmp; + tmp = (num & 0xf0) >> 4; + if (tmp < 10) + uart_putc('0'+tmp); + else + uart_putc('a'+tmp-10); + + tmp = num & 0x0f; + if (tmp < 10) + uart_putc('0'+tmp); + else + uart_putc('a'+tmp-10); +} + +void uart_trace(void *ptr, uint16_t start, uint16_t len) { + uint16_t i; + uint8_t j; + uint8_t ch; + uint8_t *data = ptr; + + data+=start; + for(i=0;i>8); + uart_puthex(start&0xff); + uart_putc('|'); + uart_putc(' '); + for(j=0;j<16;j++) { + if(i+j0x7e) + ch='.'; + uart_putc(ch); + } else { + uart_putc(' '); + } + } + uart_putc('|'); + uart_putcrlf(); + start+=16; + } +} + +void uart_flush(void) { + while (read_idx != write_idx) ; +} + +void uart_puts(const char *text) { + while (*text) { + uart_putc(*text++); + } +} diff --git a/src/tests/uart.h b/src/tests/uart.h new file mode 100644 index 0000000..32912de --- /dev/null +++ b/src/tests/uart.h @@ -0,0 +1,49 @@ +/* + + uart.h: Definitions for the UART access routines + +*/ + +#ifndef UART_H +#define UART_H + +#include +#include + +//#ifdef CONFIG_UART_DEBUG +#if 1 + +#ifdef __AVR__ +# include + void uart_puts_P(prog_char *text); +#else +# define uart_puts_P(str) uart_puts(str) +#endif + +void uart_init(void); +unsigned char uart_getc(void); +unsigned char uart_gotc(void); +void uart_putc(char c); +void uart_puts(const char *str); +void uart_puthex(uint8_t num); +void uart_trace(void *ptr, uint16_t start, uint16_t len); +void uart_flush(void); +int printf(const char *fmt, ...); +int snprintf(char *str, size_t size, const char *format, ...); +#define uart_putcrlf() uart_putc('\n') + +#else + +#define uart_init() do {} while(0) +#define uart_getc() 0 +#define uart_putc(x) do {} while(0) +#define uart_puthex(x) do {} while(0) +#define uart_flush() do {} while(0) +#define uart_puts_P(x) do {} while(0) +#define uart_puts(x) do {} while(0) +#define uart_putcrlf() do {} while(0) +#define uart_trace(a,b,c) do {} while(0) + +#endif + +#endif diff --git a/src/tests/xmodem.c b/src/tests/xmodem.c new file mode 100644 index 0000000..979f8d4 --- /dev/null +++ b/src/tests/xmodem.c @@ -0,0 +1,37 @@ +#include +#include "config.h" +#include "timer.h" +#include "uart.h" +#include "ff.h" +#include "xmodem.h" + +void xmodem_rxfile(FIL* fil) { + uint8_t rxbuf[XMODEM_BLKSIZE], sum=0, sender_sum; + uint8_t blknum, blknum2; + uint8_t count; + uint32_t totalbytes = 0; + uint32_t totalwritten = 0; + UINT written; + FRESULT res; + uart_flush(); + do { + delay_ms(3000); + uart_putc(ASC_NAK); + } while (uart_getc() != ASC_SOH); + do { + blknum=uart_getc(); + blknum2=uart_getc(); + for(count=0; count