commit e121b34cf4bc2e110b9c5728a6c0565df5f1136d Author: ikari Date: Thu Jul 2 11:14:04 2009 +0200 Initial Commit diff --git a/pcb/DIL-Labels.sch b/pcb/DIL-Labels.sch new file mode 100644 index 0000000..3c26fba Binary files /dev/null and b/pcb/DIL-Labels.sch differ diff --git a/pcb/breakout1.brd b/pcb/breakout1.brd new file mode 100644 index 0000000..ac8b2c6 Binary files /dev/null and b/pcb/breakout1.brd differ diff --git a/pcb/breakout2.brd b/pcb/breakout2.brd new file mode 100644 index 0000000..51ab903 Binary files /dev/null and b/pcb/breakout2.brd differ diff --git a/pcb/breakout2.sch b/pcb/breakout2.sch new file mode 100644 index 0000000..9ae9aa3 Binary files /dev/null and b/pcb/breakout2.sch differ diff --git a/pcb/breakouts1-old.brd b/pcb/breakouts1-old.brd new file mode 100644 index 0000000..a13dafa Binary files /dev/null and b/pcb/breakouts1-old.brd differ diff --git a/pcb/snesbreakout.brd b/pcb/snesbreakout.brd new file mode 100644 index 0000000..7aae3d8 Binary files /dev/null and b/pcb/snesbreakout.brd differ diff --git a/pcb/snesbreakout.sch b/pcb/snesbreakout.sch new file mode 100644 index 0000000..3a9ad45 Binary files /dev/null and b/pcb/snesbreakout.sch differ diff --git a/pcb/test.brd b/pcb/test.brd new file mode 100644 index 0000000..608e02c --- /dev/null +++ b/pcb/test.brd @@ -0,0 +1,247 @@ +PCBNEW-BOARD Version 1 date 18/5/2009-17:20:42 + +$GENERAL +LayerCount 2 +Ly 1FFF8001 +Links 0 +NoConn 0 +Di 8440 7940 13560 13060 +Ndraw 0 +Ntrack 0 +Nzone 0 +Nmodule 2 +Nnets 0 +$EndGENERAL + +$SHEETDESCR +Sheet A4 11700 8267 +Title "" +Date "18 may 2009" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndSHEETDESCR + +$SETUP +InternalUnit 0.000100 INCH +UserGridSize 0.010000 0.010000 mm +ZoneGridSize 250 +Layers 2 +Layer[0] Copper signal +Layer[15] Component signal +TrackWidth 170 +TrackWidthHistory 170 +TrackClearence 60 +ZoneClearence 150 +DrawSegmWidth 150 +EdgeSegmWidth 150 +ViaSize 450 +ViaDrill 250 +ViaSizeHistory 450 +MicroViaSize 200 +MicroViaDrill 80 +MicroViasAllowed 0 +TextPcbWidth 120 +TextPcbSize 600 800 +EdgeModWidth 150 +TextModSize 600 600 +TextModWidth 120 +PadSize 600 600 +PadDrill 320 +AuxiliaryAxisOrg 0 0 +$EndSETUP + +$EQUIPOT +Na 0 "" +St ~ +$EndEQUIPOT +$MODULE PIN_ARRAY_5x2 +Po 11000 12000 0 15 3FCF2109 4A11978A ~~ +Li PIN_ARRAY_5x2 +Cd Double rangee de contacts 2 x 5 pins +Kw CONN +Sc 4A11978A +AR /4A11978A +Op 0 0 0 +T0 250 -1500 400 400 0 80 N V 21 "P102" +T1 0 -1500 400 400 0 80 N I 21 "CONN_5X2" +DS -2500 -1000 2500 -1000 120 21 +DS 2500 -1000 2500 1000 120 21 +DS 2500 1000 -2500 1000 120 21 +DS -2500 1000 -2500 -1000 120 21 +$PAD +Sh "1" R 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po -2000 500 +$EndPAD +$PAD +Sh "2" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po -2000 -500 +$EndPAD +$PAD +Sh "3" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po -1000 500 +$EndPAD +$PAD +Sh "4" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po -1000 -500 +$EndPAD +$PAD +Sh "5" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 0 500 +$EndPAD +$PAD +Sh "6" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 0 -500 +$EndPAD +$PAD +Sh "7" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 1000 500 +$EndPAD +$PAD +Sh "8" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 1000 -500 +$EndPAD +$PAD +Sh "9" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 2000 500 +$EndPAD +$PAD +Sh "10" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 2000 -500 +$EndPAD +$SHAPE3D +Na "pin_array/pins_array_5x2.wrl" +Sc 1.000000 1.000000 1.000000 +Of 0.000000 0.000000 0.000000 +Ro 0.000000 0.000000 0.000000 +$EndSHAPE3D +$EndMODULE PIN_ARRAY_5x2 +$MODULE PIN_ARRAY_5x2 +Po 11000 9000 0 15 3FCF2109 4A119787 ~~ +Li PIN_ARRAY_5x2 +Cd Double rangee de contacts 2 x 5 pins +Kw CONN +Sc 4A119787 +AR /4A119787 +Op 0 0 0 +T0 250 -1500 400 400 0 80 N V 21 "P101" +T1 0 -1500 400 400 0 80 N I 21 "CONN_5X2" +DS -2500 -1000 2500 -1000 120 21 +DS 2500 -1000 2500 1000 120 21 +DS 2500 1000 -2500 1000 120 21 +DS -2500 1000 -2500 -1000 120 21 +$PAD +Sh "1" R 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po -2000 500 +$EndPAD +$PAD +Sh "2" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po -2000 -500 +$EndPAD +$PAD +Sh "3" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po -1000 500 +$EndPAD +$PAD +Sh "4" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po -1000 -500 +$EndPAD +$PAD +Sh "5" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 0 500 +$EndPAD +$PAD +Sh "6" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 0 -500 +$EndPAD +$PAD +Sh "7" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 1000 500 +$EndPAD +$PAD +Sh "8" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 1000 -500 +$EndPAD +$PAD +Sh "9" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 2000 500 +$EndPAD +$PAD +Sh "10" C 600 600 0 0 0 +Dr 400 0 0 +At STD N 00E0FFFF +Ne 0 "" +Po 2000 -500 +$EndPAD +$SHAPE3D +Na "pin_array/pins_array_5x2.wrl" +Sc 1.000000 1.000000 1.000000 +Of 0.000000 0.000000 0.000000 +Ro 0.000000 0.000000 0.000000 +$EndSHAPE3D +$EndMODULE PIN_ARRAY_5x2 +$TRACK +$EndTRACK +$ZONE +$EndZONE +$EndBOARD diff --git a/pcb/test.sch b/pcb/test.sch new file mode 100644 index 0000000..d41d485 --- /dev/null +++ b/pcb/test.sch @@ -0,0 +1,34 @@ +EESchema Schematic File Version 2 +LIBS:power,device,conn,linear,regul,74xx,cmos4000,adc-dac,memory,xilinx,special,microcontrollers,dsp,microchip,analog_switches,motorola,texas,intel,audio,interface,digital-audio,philips,display,cypress,siliconi,contrib,valves +EELAYER 24 0 +EELAYER END +$Descr A4 11700 8267 +Sheet 1 1 +Title "" +Date "18 may 2009" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L CONN_5X2 P101 +U 1 1 4A119787 +P 2100 1250 +F 0 "P101" H 2100 1550 60 0000 C C +F 1 "CONN_5X2" V 2100 1250 50 0000 C C + 1 2100 1250 + 1 0 0 -1 +$EndComp +$Comp +L CONN_5X2 P102 +U 1 1 4A11978A +P 2100 2200 +F 0 "P102" H 2100 2500 60 0000 C C +F 1 "CONN_5X2" V 2100 2200 50 0000 C C + 1 2100 2200 + 1 0 0 -1 +$EndComp +$EndSCHEMATC diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..3371ab8 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,618 @@ +# Hey Emacs, this is a -*- makefile -*- + +# Define version number +MAJOR = 0 +MINOR = 0 +PATCHLEVEL = 1 +FIX = + +# Forces bootloader version to 0, comment out for release +#PRERELEASE = + +#---------------------------------------------------------------------------- +# 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 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) + +# Set MCU name and length of binary for bootloader +# WARNING: Fuse settings not tested! +MCU := $(CONFIG_MCU) +ifeq ($(MCU),atmega128) + BINARY_LENGTH = 0x1f000 +# EFUSE = 0xff +# HFUSE = 0x91 +# LFUSE = 0xaf +else ifeq ($(MCU),atmega1281) + BINARY_LENGTH = 0x1f000 + BOOTLDRSIZE = 0x0800 + EFUSE = 0xff + HFUSE = 0xd2 + LFUSE = 0xfc +else ifeq ($(MCU),atmega2561) + BINARY_LENGTH = 0x3f000 + EFUSE = 0xfd + HFUSE = 0x93 + LFUSE = 0xef +else ifeq ($(MCU),atmega644) + BINARY_LENGTH = 0xf000 + EFUSE = 0xfd + HFUSE = 0x91 + LFUSE = 0xef +else ifeq ($(MCU),atmega644p) + BINARY_LENGTH = 0xf000 + EFUSE = 0xfd + HFUSE = 0x91 + LFUSE = 0xef +else +.PHONY: nochip +nochip: + @echo '==============================================================' + @echo 'No known target chip specified.' + @echo + @echo 'Please edit the Makefile.' + @exit 1 +endif + +# Directory for all generated files +OBJDIR := obj-$(CONFIG_MCU:atmega%=m%)$(CONFIGSUFFIX) + +# Output format. (can be srec, ihex, binary) +FORMAT = ihex + + +# Target file name (without extension). +TARGET = $(OBJDIR)/sd2snes + +# List C source files here. (C dependencies are automatically generated.) +SRC = main.c ff.c utils.c timer.c led.c diskio.c sdcard.c spi.c crc7.c snes.c fpga.c memory.c crc16.c fileops.c + +ifeq ($(CONFIG_UART_DEBUG),y) + SRC += uart.c +endif + +# 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 = + +# 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 = 3 -finline-functions + +# Debugging format. +# Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs. +# AVR Studio 4.10 requires dwarf-2. +# AVR [Extended] COFF format requires stabs, plus an avr-objcopy run. +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_CPU=$(CONFIG_MCU_FREQ)UL + +# Calculate bootloader version +ifdef PRERELEASE +BOOT_VERSION := 0 +else +BOOT_VERSION := 0x$(MAJOR)$(MINOR)$(PATCHLEVEL)$(FIX) +endif + +# Create a version number define +ifdef PATCHLEVEL +ifdef FIX +PROGRAMVERSION := $(MAJOR).$(MINOR).$(PATCHLEVEL).$(FIX) +else +PROGRAMVERSION := $(MAJOR).$(MINOR).$(PATCHLEVEL) +BOOT_VERSION := $(BOOT_VERSION)0 +endif +else +PROGRAMVERSION := $(MAJOR).$(MINOR) +BOOT_VERSION := $(BOOT_VERSION)00 +endif + +ifdef PRERELEASE +PROGRAMVERSION := $(PROGRAMVERSION)$(PRERELEASE) +endif + +LONGVERSION := -$(CONFIG_MCU:atmega%=m%)$(CONFIGSUFFIX) + +CDEFS += -DVERSION=\"$(PROGRAMVERSION)\" -DLONGVERSION=\"$(LONGVERSION)\" + +# Place -I options here +CINCS = + + +# Define programs and commands. +# CC must be defined here to generate the correct CFLAGS +SHELL = sh +CC = avr-gcc +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size +NM = avr-nm +AVRDUDE = avrdude +REMOVE = rm -f +COPY = cp +WINSHELL = cmd + + +#---------------- 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 += -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 +#CFLAGS += -mcall-prologues + +# these are needed for GCC 4.3.2, which is more aggressive at inlining +# gcc-4.2 knows one of those, but it tends to increase code size +ifeq ($(shell $(CC) --version|gawk -f gcctest.awk),YES) +CFLAGS += --param inline-call-cost=3 +CFLAGS += -fno-inline-small-functions +CFLAGS += -fno-move-loop-invariants +CFLAGS += -fno-split-wide-types + +# turn these on to keep the functions in the same order as in the source +# this is only useful if you're looking at disassembly +#CFLAGS += -fno-reorder-blocks +#CFLAGS += -fno-reorder-blocks-and-partition +#CFLAGS += -fno-reorder-functions +#CFLAGS += -fno-toplevel-reorder +endif + +ifeq ($(CONFIG_STACK_TRACKING),y) + CFLAGS += -finstrument-functions +endif + + +#---------------- 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 = -Wa,-adhlns=$(OBJDIR)/$(<:.S=.lst),-gstabs -I$(OBJDIR) + + +#---------------- Library Options ---------------- +# Minimalistic printf version +PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min + +# Floating point printf version (requires MATH_LIB = -lm below) +PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt + +# If this is left blank, then it will use the Standard printf version. +PRINTF_LIB = +#PRINTF_LIB = $(PRINTF_LIB_MIN) +#PRINTF_LIB = $(PRINTF_LIB_FLOAT) + + +# Minimalistic scanf version +SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min + +# Floating point + %[ scanf version (requires MATH_LIB = -lm below) +SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt + +# If this is left blank, then it will use the Standard scanf version. +SCANF_LIB = +#SCANF_LIB = $(SCANF_LIB_MIN) +#SCANF_LIB = $(SCANF_LIB_FLOAT) + + +MATH_LIB = -lm + + + +#---------------- External Memory Options ---------------- + +# 64 KB of external RAM, starting after internal RAM (ATmega128!), +# used for variables (.data/.bss) and heap (malloc()). +#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff + +# 64 KB of external RAM, starting after internal RAM (ATmega128!), +# only used for heap (malloc()). +#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff + +EXTMEMOPTS = + + + +#---------------- 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 += $(EXTMEMOPTS) +LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB) +LDFLAGS += -Wl,--gc-sections +ifeq ($(CONFIG_LINKER_RELAX),y) + LDFLAGS += -Wl,-O9,--relax +endif + + + +#---------------- Programming Options (avrdude) ---------------- + +# Programming hardware: alf avr910 avrisp bascom bsd +# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500 stk500v2 +# +# Type: avrdude -c ? +# to get a full listing. +# +AVRDUDE_PROGRAMMER = stk200 + +# com1 = serial port. Use lpt1 to connect to parallel port. +AVRDUDE_PORT = /dev/parport0 # programmer connected to serial device + +AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex +# AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep + +# Allow fuse overrides from the config file +ifdef CONFIG_EFUSE + EFUSE := CONFIG_EFUSE +endif +ifdef CONFIG_HFUSE + HFUSE := CONFIG_HFUSE +endif +ifdef CONFIG_LFUSE + LFUSE := CONFIG_LFUSE +endif + +# Calculate command line arguments for fuses +AVRDUDE_WRITE_FUSES := +ifdef EFUSE + AVRDUDE_WRITE_FUSES += -U efuse:w:$(EFUSE):m +endif +ifdef HFUSE + AVRDUDE_WRITE_FUSES += -U hfuse:w:$(HFUSE):m +endif +ifdef LFUSE + AVRDUDE_WRITE_FUSES += -U lfuse:w:$(LFUSE):m +endif + + +# Uncomment the following if you want avrdude's erase cycle counter. +# Note that this counter needs to be initialized first using -Yn, +# see avrdude manual. +#AVRDUDE_ERASE_COUNTER = -y + +# Uncomment the following if you do /not/ wish a verification to be +# performed after programming the device. +#AVRDUDE_NO_VERIFY = -V + +# Increase verbosity level. Please use this when submitting bug +# reports about avrdude. See +# to submit bug reports. +#AVRDUDE_VERBOSE = -v -v + +AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) +AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY) +AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE) +AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER) + + + +#---------------- Debugging Options ---------------- + +# For simulavr only - target MCU frequency. +DEBUG_MFREQ = $(CONFIG_MCU_FREQ) + +# Set the DEBUG_UI to either gdb or insight. +# DEBUG_UI = gdb +DEBUG_UI = insight + +# Set the debugging back-end to either avarice, simulavr. +DEBUG_BACKEND = avarice +#DEBUG_BACKEND = simulavr + +# GDB Init Filename. +GDBINIT_FILE = __avr_gdbinit + +# When using avarice settings for the JTAG +JTAG_DEV = /dev/com1 + +# Debugging port used to communicate between GDB / avarice / simulavr. +DEBUG_PORT = 4242 + +# Debugging host used to communicate between GDB / avarice / simulavr, normally +# just set to localhost unless doing some sort of crazy debugging when +# avarice is running on a different computer. +DEBUG_HOST = localhost + + + +#============================================================================ + + +# 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 = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS) +ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) $(CDEFS) + + + + + +# Default target. +all: build + +build: elf bin hex + $(E) " SIZE $(TARGET).elf" + $(Q)$(ELFSIZE)|grep -v debug + +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: + mount /mnt + cp $(TARGET).bin /mnt + umount /mnt + sync + + +# Doxygen output: +doxygen: + -rm -rf doxyinput + mkdir doxyinput + cp *.h *.c doxyinput + src2doxy.pl doxyinput/*.h doxyinput/*.c + doxygen doxygen.conf + +# Display size of file. +HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex +ELFSIZE = $(SIZE) -A $(TARGET).elf +AVRMEM = avr-mem.sh $(TARGET).elf $(MCU) + +# Program the device. +program: bin hex eep + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM) + +# Set fuses of the device +fuses: $(CONFIG) + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FUSES) + +# Generate avr-gdb config/init file which does the following: +# define the reset signal, load the target file, connect to target, and set +# a breakpoint at main(). +gdb-config: + @$(REMOVE) $(GDBINIT_FILE) + @echo define reset >> $(GDBINIT_FILE) + @echo SIGNAL SIGHUP >> $(GDBINIT_FILE) + @echo end >> $(GDBINIT_FILE) + @echo file $(TARGET).elf >> $(GDBINIT_FILE) + @echo target remote $(DEBUG_HOST):$(DEBUG_PORT) >> $(GDBINIT_FILE) +ifeq ($(DEBUG_BACKEND),simulavr) + @echo load >> $(GDBINIT_FILE) +endif + @echo break main >> $(GDBINIT_FILE) + +debug: gdb-config $(TARGET).elf +ifeq ($(DEBUG_BACKEND), avarice) + @echo Starting AVaRICE - Press enter when "waiting to connect" message displays. + @$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \ + $(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT) + @$(WINSHELL) /c pause + +else + @$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \ + $(DEBUG_MFREQ) --port $(DEBUG_PORT) +endif + @$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE) + + + + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + + +coff: $(TARGET).elf + $(COFFCONVERT) -O coff-avr $< $(TARGET).cof + + +extcoff: $(TARGET).elf + $(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof + + +# Generate autoconf.h from config +.PRECIOUS : $(OBJDIR)/autoconf.h +$(OBJDIR)/autoconf.h: $(CONFIG) | $(OBJDIR) + $(E) " CONF2H $(CONFIG)" + $(Q)gawk -f conf2h.awk $(CONFIG) > $(OBJDIR)/autoconf.h + +# Create final output files (.hex, .eep) from ELF output file. +ifeq ($(CONFIG_BOOTLOADER),y) +$(OBJDIR)/%.bin: $(OBJDIR)/%.elf + $(E) " BIN $@" + $(Q)$(OBJCOPY) -O binary -R .eeprom $< $@ + $(E) " CRCGEN $@" + -$(Q)crcgen-new $@ $(BINARY_LENGTH) $(CONFIG_BOOT_DEVID) $(BOOT_VERSION) + $(E) " COPY $(CONFIG_HARDWARE_NAME)-firmware-$(PROGRAMVERSION).bin" + $(Q)$(COPY) $@ $(OBJDIR)/$(CONFIG_HARDWARE_NAME)-firmware-$(PROGRAMVERSION).bin +else +$(OBJDIR)/%.bin: $(OBJDIR)/%.elf + $(E) " BIN $@" + $(Q)$(OBJCOPY) -O binary -R .eeprom $< $@ +endif + + +$(OBJDIR)/%.hex: $(OBJDIR)/%.elf + $(E) " HEX $@" + $(Q)$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +$(OBJDIR)/%.eep: $(OBJDIR)/%.elf + -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -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) +$(OBJDIR)/%.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).eep + $(Q)$(REMOVE) $(TARGET).cof + $(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)$(REMOVE) -rf codedoc + $(Q)$(REMOVE) -rf doxyinput + -$(Q)rmdir $(OBJDIR) + +# Include the dependency files. +-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*) + +# Manual dependency for the assembler module +# $(OBJDIR)/fastloader-ll.o: config.h $(OBJDIR)/autoconf.h + +# Listing of phony targets. +.PHONY : all begin finish end sizebefore sizeafter gccversion \ +build elf hex eep lss sym coff extcoff \ +clean clean_list program debug gdb-config doxygen + diff --git a/src/avrcompat.h b/src/avrcompat.h new file mode 100644 index 0000000..814182d --- /dev/null +++ b/src/avrcompat.h @@ -0,0 +1,162 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + avrcompat.h: Compatibility defines for multiple target chips +*/ + +#ifndef AVRCOMPAT_H +#define AVRCOMPAT_H + +/* USART */ + +#if defined __AVR_ATmega644__ || defined __AVR_ATmega644P__ || defined __AVR_ATmega1281__ || defined __AVR_ATmega2561__ + +# ifdef USE_UART1 +# define RXC RXC1 +# define RXEN RXEN1 +# define TXC TXC1 +# define TXEN TXEN1 +# define UBRRH UBRR1H +# define UBRRL UBRR1L +# define UCSRA UCSR1A +# define UCSRB UCSR1B +# define UCSRC UCSR1C +# define UCSZ0 UCSZ10 +# define UCSZ1 UCSZ11 +# define UDR UDR1 +# define UDRIE UDRIE1 +# define RXCIE RXCIE1 +# define USART_UDRE_vect USART1_UDRE_vect +# define USART_RX_vect USART1_RX_vect +# else + /* Default is USART0 */ +# define RXC RXC0 +# define RXEN RXEN0 +# define TXC TXC0 +# define TXEN TXEN0 +# define UBRRH UBRR0H +# define UBRRL UBRR0L +# define UCSRA UCSR0A +# define UCSRB UCSR0B +# define UCSRC UCSR0C +# define UCSZ0 UCSZ00 +# define UCSZ1 UCSZ01 +# define UDR UDR0 +# define UDRIE UDRIE0 +# define RXCIE RXCIE0 +# define USART_UDRE_vect USART0_UDRE_vect +# define USART_RX_vect USART0_RX_vect +# endif + +#elif defined __AVR_ATmega48__ || defined __AVR_ATmega88__ || defined __AVR_ATmega168__ +/* These chips include the USART number in their register names, */ +/* but not in the ISR name. */ +# define RXC RXC0 +# define RXEN RXEN0 +# define TXC TXC0 +# define TXEN TXEN0 +# define UBRRH UBRR0H +# define UBRRL UBRR0L +# define UCSRA UCSR0A +# define UCSRB UCSR0B +# define UCSRC UCSR0C +# define UCSZ0 UCSZ00 +# define UCSZ1 UCSZ01 +# define UDR UDR0 +# define UDRIE UDRIE0 + +#elif defined __AVR_ATmega32__ +# define TIMER2_COMPA_vect TIMER2_COMP_vect +# define TCCR0B TCCR0 +# define TCCR2A TCCR2 +# define TCCR2B TCCR2 +# define TIFR0 TIFR +# define TIMSK2 TIMSK +# define OCIE2A OCIE2 +# define OCR2A OCR2 + +#elif defined __AVR_ATmega128__ +# define UBRRH UBRR0H +# define UBRRL UBRR0L +# define UCSRA UCSR0A +# define UCSRB UCSR0B +# define UCSRC UCSR0C +# define UDR UDR0 +# define USART_UDRE_vect USART0_UDRE_vect +# define TIMER2_COMPA_vect TIMER2_COMP_vect +# define TCCR0B TCCR0 +# define TCCR2A TCCR2 +# define TCCR2B TCCR2 +# define TIFR0 TIFR +# define TIMSK1 TIMSK +# define TIMSK2 TIMSK +# define OCIE2A OCIE2 +# define OCR2A OCR2 + +#else +# error Unknown chip! (USART) +#endif + +/* SPI and I2C */ +#if defined __AVR_ATmega32__ || defined __AVR_ATmega644__ || defined __AVR_ATmega644P__ + +# define SPI_PORT PORTB +# define SPI_DDR DDRB +# define SPI_SS _BV(PB4) +# define SPI_MOSI _BV(PB5) +# define SPI_MISO _BV(PB6) +# define SPI_SCK _BV(PB7) +# define HWI2C_PORT PORTC +# define HWI2C_SDA _BV(PC1) +# define HWI2C_SCL _BV(PC0) + +#elif defined __AVR_ATmega128__ || defined __AVR_ATmega1281__ || defined __AVR_ATmega2561__ + +# define SPI_PORT PORTB +# define SPI_DDR DDRB +# define SPI_SS _BV(PB0) +# define SPI_SCK _BV(PB1) +# define SPI_MOSI _BV(PB2) +# define SPI_MISO _BV(PB3) +# define HWI2C_PORT PORTD +# define HWI2C_SDA _BV(PD1) +# define HWI2C_SCL _BV(PD0) + +#elif defined __AVR_ATmega48__ || defined __AVR_ATmega88__ || defined __AVR_ATmega168__ + +# define SPI_PORT PORTB +# define SPI_DDR DDRB +# define SPI_SS _BV(PB2) +# define SPI_SCK _BV(PB5) +# define SPI_MOSI _BV(PB3) +# define SPI_MISO _BV(PB4) +# define HWI2C_PORT PORTC +# define HWI2C_SDA _BV(PC4) +# define HWI2C_SCL _BV(PC5) + +#else +# error Unknown chip! (SPI/TWI) +#endif + +#define SPI_MASK (SPI_SS|SPI_MOSI|SPI_MISO|SPI_SCK) + +#endif /* AVRCOMPAT_H */ diff --git a/src/conf2h.awk b/src/conf2h.awk new file mode 100644 index 0000000..3818f89 --- /dev/null +++ b/src/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/config b/src/config new file mode 100644 index 0000000..1227775 --- /dev/null +++ b/src/config @@ -0,0 +1,41 @@ +# This may not look like it, but it's a -*- makefile -*- +# +# sd2iec - SD/MMC to Commodore serial bus interface/controller +# Copyright (C) 2007-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, see tff.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 +# +# +# This file is included in the main sd2iec Makefile and also parsed +# into autoconf.h. + +CONFIG_MCU=atmega644p +CONFIG_LINKER_RELAX=n +CONFIG_MCU_FREQ=14318180 +CONFIG_BOOTLOADER=y +CONFIG_BOOT_DEVID=0x4e534453 +CONFIG_UART_DEBUG=y +CONFIG_UART_BAUDRATE=38400 +CONFIG_UART_BUF_SHIFT=7 +CONFIG_HARDWARE_NAME=sd2snes +CONFIG_SD_AUTO_RETRIES=10 +#CONFIG_SD_DATACRC=y +CONFIG_EEPROM_SIZE=512 +CONFIG_EEPROM_OFFSET=512 +CONFIG_MAX_PARTITIONS=2 diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..6036cfe --- /dev/null +++ b/src/config.h @@ -0,0 +1,113 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + config.h: User-configurable options to simplify hardware changes and/or + reduce the code/ram requirements of the code. + + + Based on MMC2IEC, original copyright header follows: + +// +// Title : MMC2IEC - Configuration +// Author : Lars Pontoppidan +// Date : Jan. 2007 +// Version : 0.7 +// Target MCU : AtMega32(L) at 8 MHz +// +// +// DISCLAIMER: +// The author is in no way responsible for any problems or damage caused by +// using this code. Use at your own risk. +// +// LICENSE: +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// + +*/ + +#ifndef CONFIG_H +#define CONFIG_H + +#include "autoconf.h" + +# define HW_NAME "SD2IEC" +# define HAVE_SD +# define SDCARD_DETECT (!(PIND & _BV(PD2))) +# define SDCARD_DETECT_SETUP() do { DDRD &= ~_BV(PD2); PORTD |= _BV(PD2); } while(0) +# if defined __AVR_ATmega32__ +# define SD_CHANGE_SETUP() do { MCUCR |= _BV(ISC00); GICR |= _BV(INT0); } while(0) +# elif defined __AVR_ATmega644__ || defined __AVR_ATmega644P__ +# define SD_CHANGE_SETUP() do { EICRA |= _BV(ISC00); EIMSK |= _BV(INT0); } while(0) +# else +# error Unknown chip! +# endif +# define SD_CHANGE_VECT INT0_vect +# define SDCARD_WP (PIND & _BV(PD6)) +# define SDCARD_WP_SETUP() do { DDRD &= ~ _BV(PD6); PORTD |= _BV(PD6); } while(0) +# define SD_CHANGE_ICR MCUCR +# define SD_SUPPLY_VOLTAGE (1L<<21) +# define DEVICE_SELECT (8+!(PINA & _BV(PA2))+2*!(PINA & _BV(PA3))) +# define DEVICE_SELECT_SETUP() do { \ + DDRA &= ~(_BV(PA2)|_BV(PA3)); \ + PORTA |= _BV(PA2)|_BV(PA3); \ + } while (0) +# define BUSY_LED_SETDDR() DDRA |= _BV(PA0) +# define BUSY_LED_ON() PORTA &= ~_BV(PA0) +# define BUSY_LED_OFF() PORTA |= _BV(PA0) +# define DIRTY_LED_SETDDR() DDRA |= _BV(PA1) +# define DIRTY_LED_ON() PORTA &= ~_BV(PA1) +# define DIRTY_LED_OFF() PORTA |= _BV(PA1) +# define DIRTY_LED_PORT PORTA +# define DIRTY_LED_BIT() _BV(PA1) +# define AUX_LED_SETDDR() do {} while (0) +# define AUX_LED_ON() do {} while (0) +# define AUX_LED_OFF() do {} while (0) +# define BUTTON_PIN PINA +# define BUTTON_PORT PORTA +# define BUTTON_DDR DDRA +# define BUTTON_MASK (_BV(PA4)|_BV(PA5)) +# define BUTTON_NEXT _BV(PA4) +# define BUTTON_PREV _BV(PA5) + +/* An interrupt for detecting card changes implies hotplugging capability */ +#if defined(SD_CHANGE_VECT) || defined (CF_CHANGE_VECT) +# define HAVE_HOTPLUG +#endif + +/* Generate dummy functions for the BUSY LED if required */ +#ifdef SINGLE_LED +# define BUSY_LED_SETDDR() do {} while(0) +# define BUSY_LED_ON() do {} while(0) +# define BUSY_LED_OFF() do {} while(0) +#endif + +/* Create some temporary symbols so we can calculate the number of */ +/* enabled storage devices. */ +#ifdef HAVE_SD +# define TMP_SD 1 +#endif + +/* Remove the temporary symbols */ +#undef TMP_SD + +#endif diff --git a/src/crc16.c b/src/crc16.c new file mode 100644 index 0000000..056d75b --- /dev/null +++ b/src/crc16.c @@ -0,0 +1,51 @@ +/** + * \file stdout + * Functions and types for CRC checks. + * + * Generated on Tue Jun 30 23:02:59 2009, + * by pycrc v0.7.1, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 16 + * Poly = 0x8005 + * XorIn = 0x0000 + * ReflectIn = True + * XorOut = 0x0000 + * ReflectOut = True + * Algorithm = bit-by-bit-fast + * Direct = True + *****************************************************************************/ +#include +#include "crc16.h" +/** + * 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. + *****************************************************************************/ +crc_t crc16_update(crc_t crc, const unsigned char *data, size_t data_len) +{ + unsigned int i; + uint8_t bit; + unsigned char c; + + while (data_len--) { + c = *data++; + for (i = 0x01; i & 0xff; i <<= 1) { + bit = (crc & 0x8000 ? 1 : 0); + if (c & i) { + bit ^= 1; + } + crc <<= 1; + if (bit) { + crc ^= 0x8005; + } + } + crc &= 0xffff; + } + return crc & 0xffff; +} + + + diff --git a/src/crc16.h b/src/crc16.h new file mode 100644 index 0000000..5b5e491 --- /dev/null +++ b/src/crc16.h @@ -0,0 +1,54 @@ +/** + * \file stdout + * Functions and types for CRC checks. + * + * Generated on Tue Jun 30 23:02:56 2009, + * by pycrc v0.7.1, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 16 + * Poly = 0x8005 + * XorIn = 0x0000 + * ReflectIn = True + * XorOut = 0x0000 + * ReflectOut = True + * Algorithm = bit-by-bit-fast + * Direct = True + *****************************************************************************/ +#ifndef __STDOUT__ +#define __STDOUT__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The definition of the used algorithm. + *****************************************************************************/ +#define CRC_ALGO_BIT_BY_BIT_FAST 1 + +/** + * The type of the CRC values. + * + * This type must be big enough to contain at least 16 bits. + *****************************************************************************/ +typedef uint16_t crc_t; + +/** + * 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. + *****************************************************************************/ +crc_t crc16_update(crc_t crc, const unsigned char *data, size_t data_len); + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif + +#endif /* __STDOUT__ */ + diff --git a/src/crc32.c b/src/crc32.c new file mode 100644 index 0000000..a522512 --- /dev/null +++ b/src/crc32.c @@ -0,0 +1,74 @@ +/** + * \file stdout + * Functions and types for CRC checks. + * + * Generated on Tue Jun 30 21:35:13 2009, + * 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 = bit-by-bit-fast + * Direct = True + *****************************************************************************/ +#include +#include "crc32.h" + +/** + * Reflect all bits of a \a data word of \a data_len bytes. + * + * \param data The data word to be reflected. + * \param data_len The width of \a data expressed in number of bits. + * \return The reflected data. + *****************************************************************************/ +long crc_reflect(long data, size_t data_len) +{ + unsigned int i; + long ret; + + ret = data & 0x01; + for (i = 1; i < data_len; i++) + { + data >>= 1; + ret = (ret << 1) | (data & 0x01); + } + return ret; +} + + +/** + * 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. + *****************************************************************************/ +crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len) +{ + unsigned int i; + uint8_t bit; + unsigned char c; + + while (data_len--) { + c = *data++; + for (i = 0x01; i & 0xff; i <<= 1) { + bit = ((crc & 0x80000000) ? 1 : 0); + if (c & i) { + bit ^= 1; + } + crc <<= 1; + if (bit) { + crc ^= 0x04c11db7; + } + } + crc &= 0xffffffff; + } + return crc & 0xffffffff; +} + + + diff --git a/src/crc32.h b/src/crc32.h new file mode 100644 index 0000000..6f360b9 --- /dev/null +++ b/src/crc32.h @@ -0,0 +1,85 @@ +/** + * \file stdout + * Functions and types for CRC checks. + * + * Generated on Tue Jun 30 21:35:19 2009, + * 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 = bit-by-bit-fast + * Direct = True + *****************************************************************************/ +#ifndef __STDOUT__ +#define __STDOUT__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The definition of the used algorithm. + *****************************************************************************/ +#define CRC_ALGO_BIT_BY_BIT_FAST 1 + +/** + * The type of the CRC values. + * + * This type must be big enough to contain at least 32 bits. + *****************************************************************************/ +typedef uint32_t crc_t; + +/** + * Reflect all bits of a \a data word of \a data_len bytes. + * + * \param data The data word to be reflected. + * \param data_len The width of \a data expressed in number of bits. + * \return The reflected data. + *****************************************************************************/ +long crc_reflect(long data, size_t data_len); + +/** + * Calculate the initial crc value. + * + * \return The initial crc value. + *****************************************************************************/ +static inline crc_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. + *****************************************************************************/ +crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len); + +/** + * Calculate the final crc value. + * + * \param crc The current crc value. + * \return The final crc value. + *****************************************************************************/ +static inline crc_t crc_finalize(crc_t crc) +{ + return crc_reflect(crc, 32) ^ 0xffffffff; +} + + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif + +#endif /* __STDOUT__ */ + diff --git a/src/crc7.c b/src/crc7.c new file mode 100644 index 0000000..232e385 --- /dev/null +++ b/src/crc7.c @@ -0,0 +1,107 @@ +/* This file is a modified version of the output of pycrc. + * + * Licensing terms of pycrc: + * + * Copyright (c) 2006-2007, Thomas Pircher + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * + * crc7.c: Calculate CRC7 for SD card commands + * + */ + +/* + * Functions and types for CRC checks. + * + * Generated on Thu Nov 8 13:52:05 2007, + * by pycrc v0.6.3, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 7 + * Poly = 0x09 + * XorIn = 0x00 + * ReflectIn = False + * XorOut = 0x00 + * ReflectOut = False + * Algorithm = bit-by-bit-fast + */ +#include +#include +#include + +/** +; MMC/SD CRC Polynom X^7 + X^3 + 1 +crc7taba: + mov dptr,#CRC7_TABA ; (2) + clr c ; (1) + rrc a ; (1) + xrl a,crc7ta ; (1) + movc a,@a+dptr ; (2) + jnc crc7m ; (2) + xrl a,#00001001b ; (1) +crc7m: mov crc7ta,a ; (1) + ret ; (2) (LCALL=2) +*/ +uint8_t crc7tab[128] = { + 0x00,0x12,0x24,0x36,0x48,0x5a,0x6c,0x7e, + 0x19,0x0b,0x3d,0x2f,0x51,0x43,0x75,0x67, + 0x32,0x20,0x16,0x04,0x7a,0x68,0x5e,0x4c, + 0x2b,0x39,0x0f,0x1d,0x63,0x71,0x47,0x55, + 0x64,0x76,0x40,0x52,0x2c,0x3e,0x08,0x1a, + 0x7d,0x6f,0x59,0x4b,0x35,0x27,0x11,0x03, + 0x56,0x44,0x72,0x60,0x1e,0x0c,0x3a,0x28, + 0x4f,0x5d,0x6b,0x79,0x07,0x15,0x23,0x31, + 0x41,0x53,0x65,0x77,0x09,0x1b,0x2d,0x3f, + 0x58,0x4a,0x7c,0x6e,0x10,0x02,0x34,0x26, + 0x73,0x61,0x57,0x45,0x3b,0x29,0x1f,0x0d, + 0x6a,0x78,0x4e,0x5c,0x22,0x30,0x06,0x14, + 0x25,0x37,0x01,0x13,0x6d,0x7f,0x49,0x5b, + 0x3c,0x2e,0x18,0x0a,0x74,0x66,0x50,0x42, + 0x17,0x05,0x33,0x21,0x5f,0x4d,0x7b,0x69, + 0x0e,0x1c,0x2a,0x38,0x46,0x54,0x62,0x70 +}; + +uint8_t crc7update(uint8_t crc, const uint8_t data) { + uint8_t a = data; + uint8_t b = data & 1; + a = crc7tab[(a>>1) ^ crc]; + a ^= ((b<<3)|(b)); + crc = a; + return a; +} + +/*uint8_t crc7update(uint8_t crc, const uint8_t data) { + uint8_t i; + bool bit; + uint8_t c; + + c = data; + for (i = 0x80; i > 0; i >>= 1) { + bit = crc & 0x40; + if (c & i) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x09; + } + } + crc &= 0x7f; + return crc & 0x7f; +}*/ diff --git a/src/crc7.h b/src/crc7.h new file mode 100644 index 0000000..1ccba9d --- /dev/null +++ b/src/crc7.h @@ -0,0 +1,33 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + crc7.h: Definitions for CRC7 calculation routines + +*/ + +#ifndef CRC7_H +#define CRC7_H + +uint8_t crc7update(uint8_t crc, const uint8_t data); + +#endif + diff --git a/src/crcgen-new.c b/src/crcgen-new.c new file mode 100644 index 0000000..5e59c80 --- /dev/null +++ b/src/crcgen-new.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +#define lo8(x) (x & 0xFF) +#define hi8(x) ((x >> 8) & 0xFF) +#define xhi8(x) ((x >> 16) & 0xff) +#define xxhi8(x) ((x >> 24) & 0xff) + +unsigned short crc_ccitt_update (uint16_t crc, uint8_t data) +{ + data ^= lo8 (crc); + data ^= data << 4; + return ((((uint16_t)data << 8) | hi8 (crc)) ^ (uint8_t)(data >> 4) ^ ((uint16_t)data << 3)); +} + +int main(int argc, char *argv[]) { + if (argc != 5) { + printf("Usage: crcgen \r\n"); + return 1; + } + + unsigned long length = strtol(argv[2],NULL,0); + unsigned long devid = strtol(argv[3],NULL,0); + unsigned long version = strtol(argv[4],NULL,0); + + if (length > length+8) { + printf("Ha ha, very funny.\n"); + return 1; + } + + uint8_t *data = malloc(length+8); + + if (!data) { + perror("malloc"); + return 1; + } + + memset(data, 0xff, length); + + FILE *f; + + f = fopen(argv[1], "rb+"); + + if (f == 0) { + printf("Unable to open file %s\r\n", argv[1]); + return 1; + } + + fread(data, 1, length, f); + + data[length-8] = lo8(devid); + data[length-7] = hi8(devid); + data[length-6] = xhi8(devid); + data[length-5] = xxhi8(devid); + data[length-4] = lo8(version); + data[length-3] = hi8(version); + + unsigned long l; + unsigned short crc = 0xFFFF; + + for (l=0; l < length-2; l++) + crc = crc_ccitt_update(crc, data[l]); + + data[length-2] = lo8(crc); + data[length-1] = hi8(crc); + + if (fseek(f, 0, SEEK_SET)) { + perror("fseek"); + return 1; + } + + if (fwrite(data, length, 1, f) != 1) { + perror("fwrite"); + return 1; + } + + fclose(f); + + return 0; +} + diff --git a/src/diskio.c b/src/diskio.c new file mode 100644 index 0000000..b539516 --- /dev/null +++ b/src/diskio.c @@ -0,0 +1,197 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + diskio.c: Generic disk access routines and supporting stuff + +*/ + +#include "config.h" +#include "diskio.h" +#include "sdcard.h" + +volatile enum diskstates disk_state; + +#ifdef NEED_DISKMUX + +uint32_t drive_config; + +/* This function calculates the default drive configuration. */ +/* Its result is static after compilation, but doing this in */ +/* C in less messy than doing it with the preprocessor. */ +uint32_t get_default_driveconfig(void) { + uint32_t result = 0xffffffffL; + + /* Order matters: Whatever is checked first will be last in the config */ +#ifdef HAVE_DF + result = (result << 4) + (DISK_TYPE_DF << DRIVE_BITS) + 0; +#endif +#ifdef CONFIG_TWINSD + result = (result << 4) + (DISK_TYPE_SD << DRIVE_BITS) + 1; +#endif +#ifdef HAVE_SD + result = (result << 4) + (DISK_TYPE_SD << DRIVE_BITS) + 0; +#endif +#ifdef HAVE_ATA + result = (result << 4) + (DISK_TYPE_ATA << DRIVE_BITS) + 0; +#endif + return result; +} + +void disk_init(void) { +#ifdef HAVE_SD + sd_init(); +#endif +#ifdef HAVE_ATA + ata_init(); +#endif +#ifdef HAVE_DF + df_init(); +#endif +} + +DSTATUS disk_status(BYTE drv) { + switch(drv >> DRIVE_BITS) { +#ifdef HAVE_DF + case DISK_TYPE_DF: + return df_status(drv & DRIVE_MASK); +#endif + +#ifdef HAVE_ATA + case DISK_TYPE_ATA: + return ata_status(drv & DRIVE_MASK); + + case DISK_TYPE_ATA2: + return ata_status((drv & DRIVE_MASK) + 2); +#endif + +#ifdef HAVE_SD + case DISK_TYPE_SD: + return sd_status(drv & DRIVE_MASK); +#endif + + default: + return STA_NOINIT|STA_NODISK; + } +} + +DSTATUS disk_initialize(BYTE drv) { + switch(drv >> DRIVE_BITS) { +#ifdef HAVE_DF + case DISK_TYPE_DF: + return df_initialize(drv & DRIVE_MASK); +#endif + +#ifdef HAVE_ATA + case DISK_TYPE_ATA: + return ata_initialize(drv & DRIVE_MASK); + + case DISK_TYPE_ATA2: + return ata_initialize((drv & DRIVE_MASK) + 2); +#endif + +#ifdef HAVE_SD + case DISK_TYPE_SD: + return sd_initialize(drv & DRIVE_MASK); +#endif + + default: + return STA_NOINIT|STA_NODISK; + } +} + +DRESULT disk_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) { + switch(drv >> DRIVE_BITS) { +#ifdef HAVE_DF + case DISK_TYPE_DF: + return df_read(drv & DRIVE_MASK,buffer,sector,count); +#endif + +#ifdef HAVE_ATA + case DISK_TYPE_ATA: + return ata_read(drv & DRIVE_MASK,buffer,sector,count); + + case DISK_TYPE_ATA2: + return ata_read((drv & DRIVE_MASK) + 2,buffer,sector,count); +#endif + +#ifdef HAVE_SD + case DISK_TYPE_SD: + return sd_read(drv & DRIVE_MASK,buffer,sector,count); +#endif + + default: + return RES_ERROR; + } +} + +DRESULT disk_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) { + switch(drv >> DRIVE_BITS) { +#ifdef HAVE_DF + case DISK_TYPE_DF: + return df_write(drv & DRIVE_MASK,buffer,sector,count); +#endif + +#ifdef HAVE_ATA + case DISK_TYPE_ATA: + return ata_write(drv & DRIVE_MASK,buffer,sector,count); + + case DISK_TYPE_ATA2: + return ata_write((drv & DRIVE_MASK) + 2,buffer,sector,count); +#endif + +#ifdef HAVE_SD + case DISK_TYPE_SD: + return sd_write(drv & DRIVE_MASK,buffer,sector,count); +#endif + + default: + return RES_ERROR; + } +} + +DRESULT disk_getinfo(BYTE drv, BYTE page, void *buffer) { + switch(drv >> DRIVE_BITS) { +#ifdef HAVE_DF + case DISK_TYPE_DF: + return df_getinfo(drv & DRIVE_MASK,page,buffer); +#endif + +#ifdef HAVE_ATA + case DISK_TYPE_ATA: + return ata_getinfo(drv & DRIVE_MASK,page,buffer); + + case DISK_TYPE_ATA2: + return ata_getinfo((drv & DRIVE_MASK) + 2,page,buffer); +#endif + +#ifdef HAVE_SD + case DISK_TYPE_SD: + return sd_getinfo(drv & DRIVE_MASK,page,buffer); +#endif + + default: + return RES_ERROR; + } +} + + +#endif diff --git a/src/diskio.h b/src/diskio.h new file mode 100644 index 0000000..f9fb70c --- /dev/null +++ b/src/diskio.h @@ -0,0 +1,125 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + diskio.h: Definitions for the disk access routines + + Based on diskio.h from FatFS by ChaN, see ff.c for + for full copyright details. + +*/ + +#ifndef DISKIO_H +#define DISKIO_H + +#include "integer.h" + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* 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 */ + +/* 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 */ + +DSTATUS disk_initialize (BYTE); +DSTATUS disk_status (BYTE); +DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); +DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); +#define disk_ioctl(a,b,c) RES_OK +DRESULT disk_getinfo(BYTE drv, BYTE page, void *buffer); + +void disk_init(void); + +/* Will be set to DISK_ERROR if any access on the card fails */ +enum diskstates { DISK_CHANGED = 0, DISK_REMOVED, DISK_OK, DISK_ERROR }; + +extern volatile enum diskstates disk_state; + +/* Disk type - part of the external API except for ATA2! */ +#define DISK_TYPE_ATA 0 +#define DISK_TYPE_ATA2 1 +#define DISK_TYPE_SD 2 +#define DISK_TYPE_DF 3 +#define DISK_TYPE_NONE 7 + +#ifdef NEED_DISKMUX + +/* Disk mux configuration */ + +extern uint32_t drive_config; + +uint32_t get_default_driveconfig(void); + +# define set_map_drive(drv,val) (drive_config = \ + (drive_config & (0xffffffff - (0x0f << (drv * 4)))) \ + | (val << (drv * 4))) +# define map_drive(drv) ((drive_config >> (4 * drv)) & 0x0f) +# define set_drive_config(config) drive_config = config + +/* Number of bits used for the drive, the disk type */ +/* uses the remainder (4 bits per entry). */ +# define DRIVE_BITS 1 + +/* Calculate mask from the shift value */ +# define DRIVE_MASK ((1 << DRIVE_BITS)-1) + +#else // NEED_DISKMUX + +# define set_map_drive(drv,val) do {} while(0) +# define map_drive(drv) (drv) +# define set_drive_config(conf) do {} while(0) +# define get_default_driveconfig() 0 + +#endif // NEED_DISKMUX + +#endif diff --git a/src/eeprom.c b/src/eeprom.c new file mode 100644 index 0000000..1ddfbef --- /dev/null +++ b/src/eeprom.c @@ -0,0 +1,170 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + eeprom.c: Persistent configuration storage + +*/ + +#include +#include +#include "config.h" +#include "diskio.h" +#include "fatops.h" +#include "flags.h" +#include "iec.h" +#include "timer.h" +#include "eeprom.h" + +#include "uart.h" + +/** + * struct storedconfig - in-eeprom data structure + * @dummy : EEPROM position 0 is unused + * @checksum : Checksum over the EEPROM contents + * @structsize : size of the eeprom structure + * @osccal : stored value of OSCCAL + * @globalflags: subset of the globalflags variable + * @address : device address set by software + * @hardaddress: device address set by jumpers + * @fileexts : file extension mapping mode + * @drvflags0 : 16 bits of drv mappings, organized as 4 nybbles. + * @drvflags1 : 16 bits of drv mappings, organized as 4 nybbles. + * + * This is the data structure for the contents of the EEPROM. + */ +static EEMEM struct { + uint8_t dummy; + uint8_t checksum; + uint16_t structsize; + uint8_t osccal; + uint8_t global_flags; + uint8_t address; + uint8_t hardaddress; + uint8_t fileexts; + uint16_t drvconfig0; + uint16_t drvconfig1; +} storedconfig; + +/** + * read_configuration - reads configuration from EEPROM + * + * This function reads the stored configuration values from the EEPROM. + * If the stored checksum doesn't match the calculated one nothing will + * be changed. + */ +void read_configuration(void) { + uint16_t i,size; + uint8_t checksum, tmp; + + /* Set default values */ + globalflags |= JIFFY_ENABLED; /* JiffyDos enabled */ + globalflags |= POSTMATCH; /* Post-* matching enabled */ + globalflags |= FAT32_FREEBLOCKS; /* Calculate the number of free blocks on FAT32 */ + file_extension_mode = 1; /* Store x00 extensions except for PRG */ + set_drive_config(get_default_driveconfig()); /* Set the default drive configuration */ + + /* Use the NEXT button to skip reading the EEPROM configuration */ + if (!(BUTTON_PIN & BUTTON_NEXT)) { + ignore_keys(); + return; + } + + size = eeprom_read_word(&storedconfig.structsize); + + /* Calculate checksum of EEPROM contents */ + checksum = 0; + for (i=2; i 9) { + uint32_t tmpconfig; + tmpconfig = eeprom_read_word(&storedconfig.drvconfig0); + tmpconfig |= (uint32_t)eeprom_read_word(&storedconfig.drvconfig1) << 16; + set_drive_config(tmpconfig); + } + + /* sanity check. If the user has truly turned off all drives, turn the + * defaults back on + */ + if(drive_config == 0xffffffff) + set_drive_config(get_default_driveconfig()); +#endif + + /* Paranoia: Set EEPROM address register to the dummy entry */ + EEAR = 0; +} + +/** + * write_configuration - stores configuration data to EEPROM + * + * This function stores the current configuration values to the EEPROM. + */ +void write_configuration(void) { + uint16_t i; + uint8_t checksum; + + /* Write configuration to EEPROM */ + eeprom_write_word(&storedconfig.structsize, sizeof(storedconfig)); + eeprom_write_byte(&storedconfig.osccal, OSCCAL); + eeprom_write_byte(&storedconfig.global_flags, + globalflags & (JIFFY_ENABLED | POSTMATCH | + EXTENSION_HIDING | FAT32_FREEBLOCKS)); + eeprom_write_byte(&storedconfig.address, device_address); + eeprom_write_byte(&storedconfig.hardaddress, DEVICE_SELECT); + eeprom_write_byte(&storedconfig.fileexts, file_extension_mode); +#ifdef NEED_DISKMUX + eeprom_write_word(&storedconfig.drvconfig0, drive_config); + eeprom_write_word(&storedconfig.drvconfig1, drive_config >> 16); +#endif + + /* Calculate checksum over EEPROM contents */ + checksum = 0; + for (i=2;i + + 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 + + + eeprom.h: Persistent configuration storage + +*/ + +#ifndef EEPROM_H +#define EEPROM_H + +void read_configuration(void); +void write_configuration(void); + +#endif diff --git a/src/ff.c b/src/ff.c new file mode 100644 index 0000000..5b38038 --- /dev/null +++ b/src/ff.c @@ -0,0 +1,2936 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.07a (C)ChaN, 2009 +/-----------------------------------------------------------------------------/ +/ FatFs module is an open source software to implement FAT file system to +/ small embedded systems. This is a free software and is opened for education, +/ research and commercial developments under license policy of following trems. +/ +/ Copyright (C) 2009, 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 use 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) patition. +/ 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 algolithm 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 plysical 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. +/ 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. +/---------------------------------------------------------------------------*/ + +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#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); } + +#ifndef NULL +#define NULL 0 +#endif + + +/*-------------------------------------------------------------------------- + + Private Work Area + +---------------------------------------------------------------------------*/ + +static +FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */ +static +WORD Fsid; /* File system mount ID */ + + +#if _USE_LFN == 1 /* LFN with static LFN working buffer */ +static +WORD LfnBuf[_MAX_LFN + 1]; +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR *lp = LfnBuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#elif _USE_LFN > 1 /* LFN with dynamic LFN working buffer */ +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#else /* No LFN */ +#define NAMEBUF(sp,lp) BYTE sp[12] +#define INITBUF(dj,sp,lp) dj.fn = sp + +#endif + + + + +/*-------------------------------------------------------------------------- + + Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, int cnt) { + char *d = (char*)dst; + const char *s = (const char *)src; + while (cnt--) *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, int cnt) { + char *d = (char*)dst; + while (cnt--) *d++ = (char)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, int cnt) { + const char *d = (const char *)dst, *s = (const char *)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 +BOOL 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 + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make apperance 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->drive, fs->win, wsect, 1) != RES_OK) + return FR_DISK_ERR; + fs->wflag = 0; + if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ + BYTE nf; + for (nf = fs->n_fats; nf >= 2; nf--) { /* Refrect the change to FAT copy */ + wsect += fs->sects_fat; + disk_write(fs->drive, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drive, 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->drive, 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->drive, CTRL_SYNC, (void*)NULL) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get a cluster status */ +/*-----------------------------------------------------------------------*/ + +static +DWORD get_cluster ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + WORD wc, bc; + DWORD fsect; + + + if (clst < 2 || clst >= fs->max_clust) /* Check cluster address range */ + return 1; + + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (WORD)clst * 3 / 2; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc = fs->win[bc & (SS(fs) - 1)]; bc++; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break; + return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]); + + case FS_FAT32 : + if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break; + return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occured at the disk I/O layer */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change a cluster status */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT put_cluster ( + FATFS *fs, /* File system object */ + DWORD clst, /* Cluster# to be changed (must be 2 to fs->max_clust-1) */ + DWORD val /* New value to mark the cluster */ +) +{ + WORD bc; + BYTE *p; + DWORD fsect; + FRESULT res; + + + if (clst < 2 || clst >= fs->max_clust) { /* Check cluster address range */ + res = FR_INT_ERR; + + } else { + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (WORD)clst * 3 / 2; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fsect + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fsect + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val); + break; + + default : + res = FR_INT_ERR; + } + fs->wflag = 1; + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove chain from */ +) +{ + FRESULT res; + DWORD nxt; + + + if (clst < 2 || clst >= fs->max_clust) { /* Check cluster address range */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->max_clust) { /* Not a last link? */ + nxt = get_cluster(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_cluster(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; + } + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* 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, mcl; + + + mcl = fs->max_clust; + if (clst == 0) { /* Create new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (scl == 0 || scl >= mcl) scl = 1; + } + else { /* Stretch existing chain */ + cs = get_cluster(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < mcl) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= mcl) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free custer */ + } + cs = get_cluster(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */ + return cs; + if (ncl == scl) return 0; /* No free custer */ + } + + if (put_cluster(fs, ncl, 0x0FFFFFFF)) /* Mark the new cluster "in use" */ + return 0xFFFFFFFF; + if (clst != 0) { /* Link it to previous one if needed */ + if (put_cluster(fs, clst, ncl)) + return 0xFFFFFFFF; + } + + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + + return ncl; /* Return new cluster number */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + +static +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->max_clust - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Seek directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_seek ( + 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->max_clust) /* Check start cluster range */ + return FR_INT_ERR; + + if (clst == 0) { /* Static table */ + 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); + } + else { /* Dynamic table */ + ic = SS(dj->fs) / 32 * dj->fs->csize; /* Indexes per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_cluster(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dj->fs->max_clust) /* 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); + } + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32; + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Move directory index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */ + DIR *dj, /* Pointer to directory object */ + BOOL streach /* FALSE: Do not streach table, TRUE: Streach 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->sclust == 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_cluster(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->max_clust) { /* When it reached end of dinamic table */ +#if !_FS_READONLY + BYTE c; + if (!streach) return FR_NO_FILE; /* When do not streach, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Streach 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 streached 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; +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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 +BOOL test_lfn ( /* TRUE:Matched, FALSE:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + int i, s; + WCHAR wc1, wc2; + + + i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Offset in the LFN buffer */ + s = 0; + do { + if (i >= _MAX_LFN) return FALSE; /* Out of buffer range? */ + wc1 = LD_WORD(dir+LfnOfs[s]); /* Get both characters to compare */ + wc2 = lfnbuf[i++]; + if (IsLower(wc1)) wc1 -= 0x20; /* Compare it (ignore case) */ + if (IsLower(wc2)) wc2 -= 0x20; + if (wc1 != wc2) return FALSE; + } while (++s < 13 && wc1); /* Repeat until last char or a NUL char is processed */ + + return TRUE; /* The LFN entry matched */ +} + + + +static +BOOL pick_lfn ( /* TRUE:Succeeded, FALSE:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + int i, s; + WCHAR wchr; + + + i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Offset in the LFN buffer */ + s = 0; + do { + wchr = LD_WORD(dir+LfnOfs[s]); /* Get an LFN char */ + if (!wchr) break; /* End of LFN? */ + if (i >= _MAX_LFN) return FALSE; /* Buffer overflow */ + lfnbuf[i++] = wchr; /* Store it */ + } while (++s < 13); /* Repeat until last char is copied */ + if (dir[LDIR_Ord] & 0x40) lfnbuf[i] = 0; /* Put terminator if last LFN entry */ + + return TRUE; +} + + +#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 */ +) +{ + int i, s; + WCHAR wchr; + + + 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; /* Offset in the LFN buffer */ + s = wchr = 0; + do { + if (wchr != 0xFFFF) wchr = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir+LfnOfs[s], wchr); /* Put it */ + if (!wchr) wchr = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wchr == 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 genartated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD num /* Sequense number */ +) +{ + char ns[8]; + int i, j; + + + mem_cpy(dst, src, 11); + + if (num > 5) { /* On many collisions, generate a hash number instead of sequencial number */ + do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa */ + i = 7; + do { + ns[i--] = (num % 10) + '0'; + num /= 10; + } while (num); + 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; + int n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* 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 a, c, lfen, ord, sum, *dir; + + + res = dir_seek(dj, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + + ord = sum = 0xFF; lfen = *(dj->fn+11) & 1; + 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 */ + a = dir[DIR_Attr] & AM_MASK; +#if _USE_LFN /* LFN configuration */ + if (c == 0xE5 || 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 (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 LFN validity. Compare LFN if it is out of 8.3 format */ + ord = (c == ord && sum == dir[LDIR_Chksum] && (!lfen || test_lfn(dj->lfn, dir))) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) { /* Did not LFN match? */ + dj->lfn_idx = 0xFFFF; + ord = 0xFF; + } + if (lfen) { /* Match LFN if it is out of 8.3 format */ + if (ord == 0) break; + } else { /* Match SFN if LFN is in 8.3 format */ + if (!mem_cmp(dir, dj->fn, 11)) break; + } + } + } +#else /* Non LFN configuration */ + if (c != 0xE5 && c != '.' && !(a & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 2 +static +FRESULT dir_read ( + DIR *dj /* Pointer to the directory object to store read object name */ +) +{ + FRESULT res; + BYTE a, c, ord, sum, *dir; + + + ord = sum = 0xFF; + 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 */ + a = dir[DIR_Attr] & AM_MASK; +#if _USE_LFN /* LFN configuration */ + if (c == 0xE5 || 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 entry? */ + dj->lfn_idx = 0xFFFF; /* No LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != 0xE5 && c != '.' && !(a & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* 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 (sn[11] & 1) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[11] = 0; dj->lfn = NULL; /* 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[11] = sn[11]; dj->lfn = lfn; + } + if (sn[11] & 2) { /* When eliminate LFN, reserve only an SFN entry. */ + ne = 1; + } else { /* Otherwise reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++) ; + ne = (ne + 25) / 13; + } + + /* Reserve contiguous entries */ + res = dir_seek(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 contigulus entry */ + if (++n == ne) break; /* A contiguous entry that requiered count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_seek(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, FALSE); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_seek(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, TRUE); /* Next entry with table streach */ + } 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 */ + dir[DIR_NTres] = *(dj->fn+11) & 0x18; /* Put NT flag */ + 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_seek(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 SFN is deleted, all entries of the object is deleted. */ + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_seek(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 char **path /* Pointer to pointer to the segment in the path string */ +) +{ +#if _USE_LFN + BYTE c, b, cf, *sfn; + WCHAR w, *lfn; + int i, ni, si, di; + const char *p; + + /* Create LFN in Unicode */ + si = di = 0; + p = *path; + lfn = dj->lfn; + for (;;) { + w = (BYTE)p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (IsDBCS1(w)) { /* If it is DBC 1st byte */ + c = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(c)) /* Reject invalid DBC */ + return FR_INVALID_NAME; + w = (w << 8) + c; + } else { + if (chk_chr("\"*:<>\?|\x7F", w)) /* Reject unallowable chars for LFN */ + return FR_INVALID_NAME; + } + w = ff_convert(w, 1); /* Convert OEM to Unicode, store it */ + if (!w || di >= _MAX_LFN) /* Reject invalid code or too long name */ + return FR_INVALID_NAME; + lfn[di++] = w; + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + cf = (w < ' ') ? 4 : 0; /* Set last segment flag if end of path */ + + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject null string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + sfn = dj->fn; + mem_set(sfn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= 1; + 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 == 0) break; /* Break when enf of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= 1; continue; + } + if (i >= ni || si == di) { /* Here is extension or end of SFN */ + if (ni == 11) { /* Extension is longer than 3 bytes */ + cf |= 1; break; + } + if (si != di) cf |= 1; /* File name is longer than 8 bytes */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w >= 0x80) cf |= 0x20; /* If there is any extended char, force create an LFN */ + if (w >= 0x100) { /* Double byte char */ + if (i >= ni - 1) { + cf |= 1; i = ni; continue; + } + sfn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (chk_chr("+,;[=]", w)) { /* Replace unallowable chars for SFN */ + w = '_'; cf |= 1; + } else { + if (IsUpper(w)) { /* Large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* Small capital */ + b |= 1; w -= 0x20; + } + } + } + } + sfn[i++] = (BYTE)w; + } + if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((cf & 0x21) == 0) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= 0x10; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= 0x08; /* NT flag (Filename has only small capital) */ + if ((b & 0x0C) != 0x0C && (b & 0x03) != 0x03) cf |= 2; /* Eliminate LFN when non composite capitals */ + } + + sfn[11] = cf; /* SFN is created */ + +#else + BYTE c, d, b, *sfn; + int 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; + for (;;) { + c = 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) b |= 3; /* If there is any extended char, eliminate NT flag */ + if (IsDBCS1(c)) { /* If it is DBC 1st byte */ + d = 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 { + if (chk_chr(" +,;[=]\"*:<>\?|\x7F", c)) /* Reject unallowable chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { + b |= 2; + } else { + if (IsLower(c)) { + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + c = (c < ' ') ? 4 : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject null 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 |= 0x10; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= 0x08; /* NT flag (Filename has only small capital) */ + + sfn[11] = c; /* Store NT flag, File name is created */ +#endif + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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 store the file information */ +) +{ + int i; + BYTE c, nt, *dir; + char *p; + + + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy file name body */ + c = dir[i]; + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if ((nt & 0x08) && IsUpper(c)) c += 0x20; + *p++ = c; + } + if (dir[8] != ' ') { /* Copy file name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + if ((nt & 0x10) && IsUpper(c)) c += 0x20; + *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 */ + } + *p = 0; + +#if _USE_LFN + p = fno->lfname; + if (p) { + WCHAR wchr, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((wchr = *lfn++) != 0) { /* Get an LFN char */ + wchr = ff_convert(wchr, 0); /* Unicode -> OEM code */ + if (!wchr) { i = 0; break; } /* Conversion error, no LFN */ + if (_DF1S && wchr >= 0x100) /* Put 1st byte if it is a DBC */ + p[i++] = (char)(wchr >> 8); + p[i++] = (char)wchr; + if (i >= fno->lfsize) { i = 0; break; } /* Buffer overrun, no LFN */ + } + } + p[i] = 0; /* Terminator */ + } +#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 char *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, last; + + + if (*path == '/' || *path == '\\' ) path++; /* Strip heading separator */ + + dj->sclust = /* Set start directory (root dir) */ + (dj->fs->fs_type == FS_FAT32) ? dj->fs->dirbase : 0; + + if ((BYTE)*path < ' ') { /* Null path means the root directory */ + res = dir_seek(dj, 0); + dj->dir = NULL; + + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + last = *(dj->fn+11) & 4; + if (res != FR_OK) { /* Could not find the object */ + if (res == FR_NO_FILE && !last) + res = FR_NO_PATH; + break; + } + if (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 = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3: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->drive, 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 (!mem_cmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */ + return 0; + if (!mem_cmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80)) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Make sure that the file system is valid */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ + const char **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 */ +) +{ + FRESULT res; + BYTE vol, fmt, *tbl; + DSTATUS stat; + DWORD bsect, fsize, tsect, mclst; + const char *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] == ':') { + p += 2; /* Found a drive number, get and strip it */ + *path = p; /* Return pointer to the path name */ + } else { + vol = 0; /* No drive number is given, use drive number 0 as default */ + } + + /* Check if the logical drive number is valid or not */ + if (vol >= _DRIVES) return FR_INVALID_DRIVE; /* Is the drive number valid? */ + *rfs = fs = FatFs[vol]; /* Returen pointer to the corresponding file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object registered? */ + + ENTER_FF(fs); /* Lock file system */ + + if (fs->fs_type) { /* If the logical drive has been mounted */ + stat = disk_status(fs->drive); + if (!(stat & STA_NOINIT)) { /* and 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 re-mounted. Following code attempts to mount the volume */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drive = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drive); /* Initialize low level disk I/O layer */ + if (stat & STA_NOINIT) /* Check if the drive is ready */ + return FR_NOT_READY; +#if _MAX_SS != 512 /* Get disk sector size if needed */ + if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS) + return FR_NO_FILESYSTEM; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + /* Search FAT partition on the drive */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 as an SFD format */ + if (fmt == 1) { /* Not an FAT boot record, it may be patitioned */ + /* Check a 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 || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* No valid FAT patition is found */ + return FR_NO_FILESYSTEM; + + /* Initialize the file system object */ + fsize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32); + fs->sects_fat = fsize; + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + fsize *= fs->n_fats; /* (Number of sectors in FAT area) */ + fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */ + fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Nmuber of root directory entries */ + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the file system */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + fs->max_clust = mclst = (tsect /* Last cluster# + 1 */ + - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32) + ) / fs->csize + 2; + + fmt = FS_FAT12; /* Determine the FAT sub type */ + if (mclst >= 0xFF7) fmt = FS_FAT16; /* Number of clusters >= 0xFF5 */ + if (mclst >= 0xFFF7) fmt = FS_FAT32; /* Number of clusters >= 0xFFF5 */ + + if (fmt == FS_FAT32) + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ + else + fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */ + fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32); /* Data start sector (lba) */ + +#if !_FS_READONLY + /* Initialize allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->wflag = 0; + /* Get fsinfo if needed */ + if (fmt == FS_FAT32) { + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + fs->fsi_flag = 0; + if (disk_read(fs->drive, 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->winsect = 0; + fs->fs_type = fmt; /* FAT syb-type */ + fs->id = ++Fsid; /* File system mount ID */ + res = FR_OK; + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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 */ +) +{ + if (!fs || !fs->fs_type || fs->id != id) + return FR_INVALID_OBJECT; + + ENTER_FF(fs); /* Lock file system */ + + if (disk_status(fs->drive) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Locical 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 >= _DRIVES) /* Check if the drive number is valid */ + return FR_INVALID_DRIVE; + rfs = FatFs[vol]; /* Get current state */ + + if (rfs) { +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(fs->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 char *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + fp->fs = NULL; /* Clear file object */ +#if !_FS_READONLY + mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW); + res = auto_mount(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW))); +#else + mode &= FA_READ; + res = auto_mount(&path, &dj.fs, 0); +#endif + if (res != FR_OK) LEAVE_FF(dj.fs, res); + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + +#if !_FS_READONLY + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD ps, cl; + + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) + res = dir_register(&dj); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + mode |= FA_CREATE_ALWAYS; + dir = dj.dir; + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) /* Cannot create new */ + LEAVE_FF(dj.fs, FR_EXIST); + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR))) /* Cannot overwrite it (R/O or DIR) */ + LEAVE_FF(dj.fs, FR_DENIED); + if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero if needed */ + cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); /* Get start cluster */ + ST_WORD(dir+DIR_FstClusHI, 0); /* cluster = 0 */ + ST_WORD(dir+DIR_FstClusLO, 0); + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + dj.fs->wflag = 1; + ps = dj.fs->winsect; /* Remove the cluster chain */ + if (cl) { + res = remove_chain(dj.fs, cl); + if (res) LEAVE_FF(dj.fs, res); + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + } + res = move_window(dj.fs, ps); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + } + } + if (mode & FA_CREATE_ALWAYS) { + dir[DIR_Attr] = 0; /* Reset attribute */ + ps = get_fattime(); + ST_DWORD(dir+DIR_CrtTime, ps); /* Created time */ + dj.fs->wflag = 1; + mode |= FA__WRITTEN; /* Set file changed flag */ + } + } + /* Open an existing file */ + else { +#endif /* !_FS_READONLY */ + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ + LEAVE_FF(dj.fs, FR_NO_FILE); +#if !_FS_READONLY + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + LEAVE_FF(dj.fs, FR_DENIED); + } + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#endif + fp->flag = mode; /* File access mode */ + fp->org_clust = /* File start cluster */ + ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; fp->csect = 255; /* File pointer */ + fp->dsect = 0; + fp->fs = dj.fs; fp->id = dj.fs->id; /* Owner file system object of the file */ + + LEAVE_FF(dj.fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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 *rbuff = buff; + + + *br = 0; + + 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? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_cluster(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 */ + fp->csect = 0; /* Reset sector offset in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + 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->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA__DIRTY; + } +#endif + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + fp->csect++; /* Next sector address in the cluster */ + } + rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; +#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 */ +#endif + } + + + 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; + + + *bw = 0; + + 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? */ + if (fp->csect >= fp->fs->csize) { /* 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 streach 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 */ + fp->csect = 0; /* Reset sector address in the 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->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + 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->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + fp->csect++; /* Next sector address in the cluster */ + } + 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 changed 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->drive, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= (BYTE)~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_WORD(dir+DIR_FstClusLO, fp->org_clust); /* Update start cluster */ + ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16); + tim = get_fattime(); /* Updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + fp->flag &= (BYTE)~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 + res = validate(fp->fs, fp->id); + if (res == FR_OK) fp->fs = NULL; + LEAVE_FF(fp->fs, res); +#else + res = f_sync(fp); + if (res == FR_OK) fp->fs = NULL; + return res; +#endif +} + + + + +#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; + DWORD clst, bcs, nsect, ifptr; + + + 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 (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 = 0; fp->csect = 255; + nsect = 0; + if (ofs > 0) { + 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 streached if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_cluster(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->max_clust) ABORT(fp->fs, FR_INT_ERR); + fp->curr_clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + fp->csect = (BYTE)(ofs / SS(fp->fs)); /* Sector offset in the cluster */ + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += fp->csect; + fp->csect++; + } + } + } + if (nsect && nsect != fp->dsect && fp->fptr % SS(fp->fs)) { +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set changed flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + + 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 char *path /* Pointer to the directory path */ +) +{ + FRESULT res; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = auto_mount(&path, &dj->fs, 0); + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + res = follow_path(dj, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + dir = dj->dir; + if (dir) { /* It is not the root dir */ + if (dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } else { /* The object is not a directory */ + res = FR_NO_PATH; + } + } else { /* It is the root dir */ + dj->sclust = (dj->fs->fs_type == FS_FAT32) ? dj->fs->dirbase : 0; + } + if (res == FR_OK) res = dir_seek(dj, 0); + dj->id = dj->fs->id; + } else { + 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; + NAMEBUF(sfn, lfn); + + + res = validate(dj->fs, dj->id); /* Check validity of the object */ + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + if (!fno) { + res = dir_seek(dj, 0); + } else { + res = dir_read(dj); + if (res == FR_NO_FILE) { + 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, FALSE); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + } + } + + LEAVE_FF(dj->fs, res); +} + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const char *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + + + res = auto_mount(&path, &dj.fs, 0); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follwo completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* 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) 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 > 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_cluster(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->max_clust) { + res = put_cluster(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); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const char *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; + BYTE fat, f, *p; + + + /* Get drive number */ + res = auto_mount(&path, fatfs, 0); + if (res != FR_OK) LEAVE_FF(*fatfs, res); + + /* If number of free cluster is valid, return it without cluster scan. */ + if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) { + *nclst = (*fatfs)->free_clust; + LEAVE_FF(*fatfs, FR_OK); + } + + /* Get number of free clusters */ + fat = (*fatfs)->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + if ((WORD)get_cluster(*fatfs, clst) == 0) n++; + } while (++clst < (*fatfs)->max_clust); + } else { + clst = (*fatfs)->max_clust; + sect = (*fatfs)->fatbase; + f = 0; p = 0; + do { + if (!f) { + res = move_window(*fatfs, sect++); + if (res != FR_OK) + LEAVE_FF(*fatfs, res); + p = (*fatfs)->win; + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; f += 1; + } else { + if (LD_DWORD(p) == 0) n++; + p += 4; f += 2; + } + } while (--clst); + } + (*fatfs)->free_clust = n; + if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; + *nclst = n; + + LEAVE_FF(*fatfs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const char *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + NAMEBUF(sfn, lfn); + BYTE *dir; + DWORD dclst; + + + res = auto_mount(&path, &dj.fs, 1); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + + dir = dj.dir; + if (!dir) /* Is it the root directory? */ + LEAVE_FF(dj.fs, FR_INVALID_NAME); + if (dir[DIR_Attr] & AM_RDO) /* Is it a R/O object? */ + LEAVE_FF(dj.fs, FR_DENIED); + dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + + if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */ + if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR); + mem_cpy(&sdj, &dj, sizeof(DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_seek(&sdj, 0); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + res = dir_read(&sdj); + if (res == FR_OK) res = FR_DENIED; /* Not empty sub-dir */ + if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res); + } + + res = dir_remove(&dj); /* Remove directory entry */ + if (res == FR_OK) { + if (dclst) + res = remove_chain(dj.fs, dclst); /* Remove the cluster chain */ + if (res == FR_OK) res = sync(dj.fs); + } + + LEAVE_FF(dj.fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const char *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir, n; + DWORD dsect, dclst, pclst, tim; + + + res = auto_mount(&path, &dj.fs, 1); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any file or directory is already existing */ + if (res != FR_NO_FILE) /* Any error occured */ + LEAVE_FF(dj.fs, res); + + dclst = create_chain(dj.fs, 0); /* Allocate a new cluster for new directory table */ + res = FR_OK; + if (dclst == 0) res = FR_DENIED; + if (dclst == 1) res = FR_INT_ERR; + if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) + res = move_window(dj.fs, 0); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + dsect = clust2sect(dj.fs, dclst); + + dir = dj.fs->win; /* Initialize the new directory table */ + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + tim = get_fattime(); + ST_DWORD(dir+DIR_WrtTime, tim); + ST_WORD(dir+DIR_FstClusLO, dclst); + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + mem_cpy(dir+32, dir, 32); /* Create ".." entry */ + dir[33] = '.'; + pclst = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase) + pclst = 0; + ST_WORD(dir+32+DIR_FstClusLO, pclst); + ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16); + for (n = 0; n < dj.fs->csize; n++) { /* Write dot entries and clear left sectors */ + dj.fs->winsect = dsect++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res) LEAVE_FF(dj.fs, res); + mem_set(dir, 0, SS(dj.fs)); + } + + res = dir_register(&dj); + if (res != FR_OK) { + remove_chain(dj.fs, dclst); + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Crated time */ + ST_WORD(dir+DIR_FstClusLO, dclst); /* Table start cluster */ + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change File Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const char *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = auto_mount(&path, &dj.fs, 1); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + 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 char *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the timestamp to be set */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = auto_mount(&path, &dj.fs, 1); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + 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 char *path_old, /* Pointer to the old name */ + const char *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR dj_old, dj_new; + NAMEBUF(sfn, lfn); + BYTE buf[21], *dir; + DWORD dw; + + + INITBUF(dj_old, sfn, lfn); + res = auto_mount(&path_old, &dj_old.fs, 1); + if (res == FR_OK) { + dj_new.fs = dj_old.fs; + res = follow_path(&dj_old, path_old); /* Check old object */ + } + if (res != FR_OK) LEAVE_FF(dj_old.fs, res); /* The old object is not found */ + + if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE); /* Is root dir? */ + mem_cpy(buf, dj_old.dir+DIR_Attr, 21); /* Save the object information */ + + mem_cpy(&dj_new, &dj_old, sizeof(DIR)); + res = follow_path(&dj_new, path_new); /* Check new object */ + 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? */ + res = dir_register(&dj_new); /* Register the new object */ + if (res == FR_OK) { + dir = dj_new.dir; /* Copy object information into new entry */ + mem_cpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0]; + dj_old.fs->wflag = 1; + if (dir[DIR_Attr] & AM_DIR) { /* Update .. entry in the directory if needed */ + dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(dj_new.fs, dw); + dir = dj_new.fs->win+32; + if (res == FR_OK && dir[1] == '.') { + dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust; + ST_WORD(dir+DIR_FstClusLO, dw); + ST_WORD(dir+DIR_FstClusHI, dw >> 16); + dj_new.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj_old); /* Remove old entry */ + if (res == FR_OK) + res = sync(dj_old.fs); + } + } + } + + LEAVE_FF(dj_old.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 _FS_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; + + + *bf = 0; + + 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)(NULL, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_cluster(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 */ + fp->csect = 0; /* Reset sector address in the cluster */ + } + fp->csect++; /* Next sector address in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect - 1; + 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 and <= 2048 */ +#define N_FATS 1 /* 1 or 2 */ +#define MAX_SECTOR 131072000UL /* Maximum partition size */ +#define MIN_SECTOR 2000UL /* Minimum partition size */ + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE partition, /* Partitioning rule 0:FDISK, 1:SFD */ + WORD allocsize /* Allocation unit size [bytes] */ +) +{ + static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 }; + static const WORD cstbl[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 }; + BYTE fmt, m, *tbl; + DWORD b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */ + DWORD n_part, n_rsv, n_fat, n_dir; /* Area size */ + DWORD n_clst, n; + WORD as; + FATFS *fs; + DSTATUS stat; + + + /* Check validity of the parameters */ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + if (partition >= 2) return FR_MKFS_ABORTED; + + /* Check mounted drive and clear work area */ + 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 (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR) + return FR_MKFS_ABORTED; + if (n_part > MAX_SECTOR) n_part = MAX_SECTOR; + b_part = (!partition) ? 63 : 0; /* Boot sector */ + n_part -= b_part; +#if _MAX_SS == 512 + if (!allocsize) { /* Auto selection of cluster size */ + for (n = 0; n_part < sstbl[n]; n++) ; + allocsize = cstbl[n]; + } +#endif + for (as = 512; as <= 32768U && as != allocsize; as <<= 1); + if (as != allocsize) return FR_MKFS_ABORTED; +#if _MAX_SS != 512 /* Check disk sector size */ + if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) > _MAX_SS + || SS(fs) > allocsize) + return FR_MKFS_ABORTED; +#endif + allocsize /= SS(fs); /* Number of sectors per cluster */ + + /* Pre-compute number of clusters and FAT type */ + n_clst = n_part / allocsize; + fmt = FS_FAT12; + if (n_clst >= 0xFF5) fmt = FS_FAT16; + if (n_clst >= 0xFFF5) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + switch (fmt) { + case FS_FAT12: + n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + case FS_FAT16: + n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + default: + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 33 - partition; + n_dir = 0; + } + b_fat = b_part + n_rsv; /* FATs start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */ + b_data = b_dir + n_dir; /* Data start sector */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED; + n = (b_data + n - 1) & ~(n - 1); + n_fat += (n - b_data) / N_FATS; + /* b_dir and b_data are no longer used below */ + + /* Determine number of cluster and final check of validity of the FAT type */ + n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize; + if ( (fmt == FS_FAT16 && n_clst < 0xFF5) + || (fmt == FS_FAT32 && n_clst < 0xFFF5)) + return FR_MKFS_ABORTED; + + /* Create partition table if needed */ + if (!partition) { + DWORD n_disk = b_part + n_part; + + 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_part < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_part); /* Partition size in LBA */ + ST_WORD(tbl+64, 0xAA55); /* Signature */ + if (disk_write(drv, fs->win, 0, 1) != RES_OK) + return FR_DISK_ERR; + } + + /* Create boot record */ + tbl = fs->win; /* Clear buffer */ + mem_set(tbl, 0, SS(fs)); + ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB); /* Boot code (jmp $, nop) */ + ST_WORD(tbl+BPB_BytsPerSec, SS(fs)); /* Sector size */ + tbl[BPB_SecPerClus] = (BYTE)allocsize; /* Sectors per cluster */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */ + if (n_part < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl+BPB_TotSec16, n_part); + } else { + ST_DWORD(tbl+BPB_TotSec32, n_part); + } + tbl[BPB_Media] = 0xF8; /* Media descripter */ + 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_part); /* Hidden sectors */ + n = get_fattime(); /* Use current time as a VSN */ + if (fmt != FS_FAT32) { + ST_DWORD(tbl+BS_VolID, n); /* Volume serial number */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of secters 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 lavel, FAT signature */ + } else { + ST_DWORD(tbl+BS_VolID32, n); /* Volume serial number */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of secters per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (bs+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (bs+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab32, "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ + } + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature */ + if (disk_write(drv, tbl, b_part+0, 1) != RES_OK) + return FR_DISK_ERR; + if (fmt == FS_FAT32) + disk_write(drv, tbl, b_part+6, 1); + + /* Initialize FAT area */ + for (m = 0; m < N_FATS; m++) { + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + if (fmt != FS_FAT32) { + n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8; + ST_DWORD(tbl, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + ST_DWORD(tbl+0, 0xFFFFFFF8); /* 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, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Following FAT entries are filled by zero */ + for (n = 1; n < n_fat; n++) { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize Root directory */ + m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir); + do { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--m); + + /* Create FSInfo record 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_part+1, 1); + disk_write(drv, tbl, b_part+7, 1); + } + + return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +char* f_gets ( + char* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer */ + FIL* fil /* Pointer to the file object */ +) +{ + int i = 0; + char *p = buff; + UINT rc; + + + while (i < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, p, 1, &rc); + if (rc != 1) break; /* Break when no data to read */ +#if _USE_STRFUNC >= 2 + if (*p == '\r') continue; /* Strip '\r' */ +#endif + i++; + if (*p++ == '\n') break; /* Break when reached end of line */ + } + *p = 0; + return i ? buff : NULL; /* When no data read (eof or error), return with error. */ +} + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int f_putc ( + int chr, /* A character to be output */ + FIL* fil /* Ponter to the file object */ +) +{ + UINT bw; + char c; + + +#if _USE_STRFUNC >= 2 + if (chr == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ +#endif + if (!fil) { /* Special value may be used to switch the destination to any other device */ + /* put_console(chr); */ + return chr; + } + c = (char)chr; + f_write(fil, &c, 1, &bw); /* Write a byte to the file */ + return bw ? chr : EOF; /* Return the result */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int f_puts ( + const char* 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 char* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + UCHAR c, f, r; + ULONG val; + char s[16]; + int i, w, 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 cahracter */ + 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 (c >= '0' && c <= '9') { /* Precision */ + w = w * 10 + (c - '0'); + c = *str++; + } + if (c == 'l') { /* Prefix: Size is long int */ + f |= 2; c = *str++; + } + if (c == 's') { /* Type is string */ + cc = f_puts(va_arg(arp, char*), fil); + continue; + } + if (c == 'c') { /* Type is character */ + cc = f_putc(va_arg(arp, int), fil); + if (cc != EOF) cc = 1; + continue; + } + r = 0; + if (c == 'd') r = 10; /* Type is signed decimal */ + if (c == 'u') r = 10; /* Type is unsigned decimal */ + if (c == 'X') r = 16; /* Type is unsigned hexdecimal */ + if (r == 0) break; /* Unknown type */ + if (f & 2) { /* Get the value */ + val = (ULONG)va_arg(arp, long); + } else { + val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int); + } + /* Put numeral string */ + if (c == 'd') { + if (val & 0x80000000) { + val = 0 - val; + f |= 4; + } + } + i = sizeof(s) - 1; s[i] = 0; + do { + c = (UCHAR)(val % r + '0'); + if (c > '9') c += 7; + s[--i] = c; + val /= r; + } while (i && val); + if (i && (f & 4)) s[--i] = '-'; + w = sizeof(s) - 1 - w; + while (i && i > w) s[--i] = (f & 1) ? '0' : ' '; + cc = f_puts(&s[i], fil); + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/src/ff.h b/src/ff.h new file mode 100644 index 0000000..567c853 --- /dev/null +++ b/src/ff.h @@ -0,0 +1,547 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.07a (C)ChaN, 2009 +/----------------------------------------------------------------------------/ +/ FatFs module is an open source software to implement FAT file system to +/ small embedded systems. This is a free software and is opened for education, +/ research and commercial developments under license policy of following trems. +/ +/ Copyright (C) 2009, 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 use UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/----------------------------------------------------------------------------*/ + +#include "integer.h" + +/*---------------------------------------------------------------------------/ +/ FatFs Configuration Options +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FATFS +#define _FATFS + +#define _WORD_ACCESS 0 +/* The _WORD_ACCESS option defines which access method is used to the word +/ data in the FAT structure. +/ +/ 0: Byte-by-byte access. Always compatible with all platforms. +/ 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 efficiency. */ + + +#define _FS_READONLY 0 +/* 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 +/* 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 level 1. +/ 3: f_lseek is removed in addition to level 2. */ + + +#define _FS_TINY 0 +/* 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 _USE_STRFUNC 0 +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 0 +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + +#define _DRIVES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 512 +/* Maximum sector size to be handled. (512/1024/2048/4096) */ +/* 512 for memroy card and hard disk, 1024 for floppy disk, 2048 for MO disk */ + + +#define _MULTI_PARTITION 0 +/* When _MULTI_PARTITION is set to 0, each volume is bound to the same physical +/ drive number and can mount only first primaly partition. When it is set to 1, +/ each volume is tied to the partitions listed in Drives[]. */ + + +#define _CODE_PAGE 850 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ When it is non LFN configuration, there is no difference between SBCS code +/ pages. When LFN is enabled, the code page must always be set correctly. +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 775 - Baltic +/ 850 - Multilingual Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 858 - Multilingual Latin 1 + Euro +/ 862 - Hebrew +/ 866 - Russian +/ 874 - Thai +/ 932 - Japanese Shift-JIS (DBCS) +/ 936 - Simplified Chinese GBK (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese Big5 (DBCS) +/ 1258 - Vietnam +*/ + + +#define _USE_LFN 0 +#define _MAX_LFN 255 /* Maximum LFN length to handle (max:255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN. +/ 1: Enable LFN with static working buffer on the bss. NOT REENTRANT. +/ 2: Enable LFN with dynamic working buffer on the caller's STACK. +/ +/ The working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN, +/ a Unicode - OEM code conversion function ff_convert() must be added to +/ the project. */ + + +#define _FS_REENTRANT 0 +#define _TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* Type of sync object used on the OS. */ + /* e.g. HANDLE, OS_EVENT*, ID and etc.. */ +/* To make the FatFs module re-entrant, set _FS_REENTRANT to 1 and add user +/ provided synchronization handlers, ff_req_grant, ff_rel_grant, +/ ff_del_syncobj and ff_cre_syncobj function to the project. */ + + + +/* End of configuration options. Do not change followings without care. */ +/*--------------------------------------------------------------------------*/ + + + +/* Definitions corresponds to multiple sector size */ + +#if _MAX_SS == 512 +#define SS(fs) 512 +#else +#if _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096 +#define SS(fs) ((fs)->s_size) +#else +#error Sector size must be 512, 1024, 2048 or 4096. +#endif +#endif + + + +/* File system object structure */ + +typedef struct _FATFS { + BYTE fs_type; /* FAT sub type */ + BYTE drive; /* Physical drive number */ + BYTE csize; /* Number of sectors per cluster */ + BYTE n_fats; /* Number of FAT copies */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ + BYTE pad1; + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */ +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if _MAX_SS != 512U + WORD s_size; /* Sector size */ +#endif +#if !_FS_READONLY + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + BYTE pad2; + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector */ +#endif + DWORD sects_fat; /* Sectors per fat */ + DWORD max_clust; /* Maximum cluster# + 1. Number of clusters is max_clust - 2 */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (Cluster# on FAT32) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS];/* Disk access window for Directory/FAT */ +} FATFS; + + + +/* Directory object structure */ + +typedef struct _DIR { + WORD id; /* Owner file system mount ID */ + WORD index; /* Current index number */ + FATFS* fs; /* Pointer to the owner file system object */ + DWORD sclust; /* Table start cluster (0:Static table) */ + 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 (0xFFFF:No LFN) */ +#endif +} DIR; + + + +/* File object structure */ + +typedef struct _FIL { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE csect; /* Sector address in the cluster */ + DWORD fptr; /* File R/W pointer */ + DWORD fsize; /* File size */ + DWORD org_clust; /* File start cluster */ + 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 !_FS_TINY + BYTE buf[_MAX_SS];/* File R/W buffer */ +#endif +} FIL; + + + +/* File status structure */ + +typedef struct _FILINFO { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + char fname[13]; /* Short file name (8.3 format) */ +#if _USE_LFN + char *lfname; /* Pointer to the LFN buffer */ + int lfsize; /* Size of LFN buffer [bytes] */ +#endif +} FILINFO; + + + +/* DBCS code ranges */ + +#if _CODE_PAGE == 932 /* CP932 (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 /* CP936 (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 /* CP949 (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 /* CP950 (Traditional Chinese Big5) */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#else /* SBCS code pages */ +#define _DF1S 0 + +#endif + + + +/* 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 /* DBCS configuration */ + +#if _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 + +#if _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 /* SBCS configuration */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + + +/* Definitions corresponds to multi partition */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ + +typedef struct _PARTITION { + BYTE pd; /* Physical drive# */ + BYTE pt; /* Partition # (0-3) */ +} PARTITION; + +extern +const PARTITION Drives[]; /* Logical drive# to physical location conversion table */ +#define LD2PD(drv) (Drives[drv].pd) /* Get physical drive# */ +#define LD2PT(drv) (Drives[drv].pt) /* Get partition# */ + +#else /* Single partition configuration */ + +#define LD2PD(drv) (drv) /* Physical drive# is equal to the logical drive# */ +#define LD2PT(drv) 0 /* Always mounts the 1st partition */ + +#endif + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* 0 */ + FR_DISK_ERR, /* 1 */ + FR_INT_ERR, /* 2 */ + FR_NOT_READY, /* 3 */ + FR_NO_FILE, /* 4 */ + FR_NO_PATH, /* 5 */ + FR_INVALID_NAME, /* 6 */ + FR_DENIED, /* 7 */ + FR_EXIST, /* 8 */ + FR_INVALID_OBJECT, /* 9 */ + FR_WRITE_PROTECTED, /* 10 */ + FR_INVALID_DRIVE, /* 11 */ + FR_NOT_ENABLED, /* 12 */ + FR_NO_FILESYSTEM, /* 13 */ + FR_MKFS_ABORTED, /* 14 */ + FR_TIMEOUT /* 15 */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const char*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to 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 char*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const char*, FILINFO*); /* Get file status */ +FRESULT f_getfree (const char*, 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 char*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const char*); /* Create a new directory */ +FRESULT f_chmod (const char*, BYTE, BYTE); /* Change attriburte of the file/dir */ +FRESULT f_utime (const char*, const FILINFO*); /* Change timestamp of the file/dir */ +FRESULT f_rename (const char*, const char*); /* Rename/Move a file or directory */ +FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ +FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ + +#if _USE_STRFUNC +int f_putc (int, FIL*); /* Put a character to the file */ +int f_puts (const char*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const char*, ...); /* Put a formatted string to the file */ +char* f_gets (char*, int, FIL*); /* Get a string from the file */ +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#ifndef EOF +#define EOF -1 +#endif +#endif + + + +/*--------------------------------------------------------------*/ +/* User defined functions */ + +/* Real time clock */ +#if !_FS_READONLY +DWORD get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ + /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ +#endif + +/* Unicode - OEM code conversion */ +#if _USE_LFN +WCHAR ff_convert (WCHAR, UINT); +#endif + +/* Sync functions */ +#if _FS_REENTRANT +BOOL ff_cre_syncobj(BYTE, _SYNC_t*); +BOOL ff_del_syncobj(_SYNC_t); +BOOL ff_req_grant(_SYNC_t); +void ff_rel_grant(_SYNC_t); +#endif + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#if _FS_READONLY == 0 +#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 +#define FA__ERROR 0x80 + + +/* 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 */ + + +/* FatFs refers the members in the FAT structures with byte offset instead +/ of structure member because there are incompatibility of the packing option +/ between various 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_55AA 510 + +#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 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 + + + +/*--------------------------------*/ +/* 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 + + +#endif /* _FATFS */ diff --git a/src/fileops.c b/src/fileops.c new file mode 100644 index 0000000..6f8f1cb --- /dev/null +++ b/src/fileops.c @@ -0,0 +1,31 @@ +// insert cool lenghty disclaimer here +// fileops.c: fatfs wrapping for convenience + +#include "config.h" +#include "uart.h" +#include "ff.h" +#include "fileops.h" + +void file_init() { + f_mount(0, &fatfs); +} + +void file_open(char* filename, BYTE flags) { + file_res = f_open(&file_handle, filename, flags); +} + +void file_close() { + file_res = f_close(&file_handle); +} + +UINT file_read() { + UINT bytes_read; + file_res = f_read(&file_handle, file_buf, sizeof(file_buf), &bytes_read); + return bytes_read; +} + +UINT file_write() { + UINT bytes_written; + file_res = f_write(&file_handle, file_buf, sizeof(file_buf), &bytes_written); + return bytes_written; +} diff --git a/src/fileops.h b/src/fileops.h new file mode 100644 index 0000000..604fb44 --- /dev/null +++ b/src/fileops.h @@ -0,0 +1,18 @@ +// insert cool lenghty disclaimer here +// fileops.h + +#ifndef FILEOPS_H +#define FILEOPS_H +#include "ff.h" + +BYTE file_buf[512]; +FATFS fatfs; +FIL file_handle; +FRESULT file_res; + +void file_init(void); +void file_open(char* filename, BYTE flags); +void file_close(void); +UINT file_read(void); +UINT file_write(void); +#endif diff --git a/src/fpga.c b/src/fpga.c new file mode 100644 index 0000000..32c3cd1 --- /dev/null +++ b/src/fpga.c @@ -0,0 +1,186 @@ +// insert cool lenghty disclaimer here + +// fpga.c: FPGA (re)programming + +/* + + FPGA pin mapping + ================ + + FPGA AVR dir + ------------------------ + PROG_B PD3 OUT + CCLK PD4 OUT + CS_B PD7 OUT + INIT_B PB2 IN + RDWR_B PB3 OUT + D7 PC0 OUT + D6 PC1 OUT + D5 PC2 OUT + D4 PC3 OUT + D3 PC4 OUT + D2 PC5 OUT + D1 PC6 OUT + D0 PC7 OUT + + */ + +#include +#include "fpga.h" +#include "config.h" +#include "uart.h" +#include "sdcard.h" +#include "diskio.h" +#include "ff.h" +#include "fileops.h" + +DWORD get_fattime(void) { + return 0L; +} +void set_prog_b(uint8_t val) { + if(val) { + PORTD |= _BV(PD3); + } else { + PORTD &= ~_BV(PD3); + } +} + +void set_cs_b(uint8_t val) { + if(val) { + PORTD |= _BV(PD7); + } else { + PORTD &= ~_BV(PD7); + } +} + +void set_rdwr_b(uint8_t val) { + if(val) { + PORTB |= _BV(PB3); + } else { + PORTB &= ~_BV(PB3); + } +} + +void set_cclk(uint8_t val) { + if(val) { + PORTD |= _BV(PD4); + } else { + PORTD &= ~_BV(PD4); + } +} + +void fpga_init() { + DDRB |= _BV(PB3); // PB3 is output + DDRB &= ~_BV(PB2); // PB2 is input + + DDRC = 0xff; // for FPGA config, all PORTC pins are outputs + + DDRD |= _BV(PD3) | _BV(PD4) | _BV(PD7); // PD3, PD4, PD7 are outputs + + set_cclk(0); // initial clk=0 +} + +int fpga_get_done(void) { + return 0; +} + +void fpga_postinit() { + DDRA |= _BV(PA0) | _BV(PA1) | _BV(PA2) | _BV(PA4) | _BV(PA5) | _BV(PA6); // MAPPER+NEXTADDR output + DDRB |= _BV(PB2) | _BV(PB1) | _BV(PB0); // turn PB2 into output, enable AVR_BANK +} + +void fpga_pgm(char* filename) { + set_prog_b(0); + uart_putc('P'); + set_prog_b(1); + loop_until_bit_is_set(PINB, PB2); + uart_putc('p'); + + FIL in; + FRESULT res; + UINT bytes_read; + + // open configware file + res=f_open(&in, filename, FA_READ); + if(res) { + uart_putc('?'); + return; + } + // file open successful + set_cs_b(0); + set_rdwr_b(0); + + for (;;) { + res = f_read(&in, file_buf, sizeof(file_buf), &bytes_read); + if (res || bytes_read == 0) break; // error or eof + for(int i=0; i 0) { + major = 0+ver[1]; + minor = 0+ver[2]; + if (major > 4 || (major == 4 && minor > 2)) { + print "YES"; + } + } +} diff --git a/src/integer.h b/src/integer.h new file mode 100644 index 0000000..c120dc7 --- /dev/null +++ b/src/integer.h @@ -0,0 +1,30 @@ +/* Integer definitions for ff, based on example code from ChaN */ + +#ifndef _INTEGER + +#include + +/* These types are assumed as 16-bit or larger integer */ +typedef int16_t INT; +typedef uint16_t UINT; + +/* These types are assumed as 8-bit integer */ +typedef int8_t CHAR; +typedef uint8_t UCHAR; +typedef uint8_t BYTE; + +/* These types are assumed as 16-bit integer */ +typedef int16_t SHORT; +typedef uint16_t USHORT; +typedef uint16_t WORD; + +/* These types are assumed as 32-bit integer */ +typedef int32_t LONG; +typedef uint32_t ULONG; +typedef uint32_t DWORD; + +/* Boolean type */ +typedef enum { FALSE = 0, TRUE } BOOL; + +#define _INTEGER +#endif diff --git a/src/led.c b/src/led.c new file mode 100644 index 0000000..d49563d --- /dev/null +++ b/src/led.c @@ -0,0 +1,43 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + led.c: Overdesigned LED handling + +*/ + +#include +#include "config.h" +#include "led.h" + +volatile uint8_t led_state; + +/** + * update_leds - set LEDs to correspond to the buffer status + * + * This function sets the busy/dirty LEDs to correspond to the current state + * of the buffers, i.e. busy on of at least one non-system buffer is + * allocated and dirty on if at least one buffer is allocated for writing. + * Call if you have manually changed the LEDs and you want to restore the + * "default" state. + */ +void update_leds(void) { +} diff --git a/src/led.h b/src/led.h new file mode 100644 index 0000000..7992aad --- /dev/null +++ b/src/led.h @@ -0,0 +1,54 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + led.h: Definitions for the LEDs + +*/ + +#ifndef LED_H +#define LED_H + +#include "config.h" +#include "uart.h" + +/* LED-to-bit mapping - BUSY/DIRTY are only used for SINGLE_LED */ +#define LED_ERROR 1 +#define LED_BUSY 2 +#define LED_DIRTY 4 + +extern volatile uint8_t led_state; + +/* Update the LEDs to match the buffer state */ +void update_leds(void); + +/* Wrapped in do..while to avoid "ambigious else" warnings */ +#ifdef SINGLE_LED +# define set_dirty_led(x) do{if (x) { led_state |= LED_DIRTY; } else { led_state &= (uint8_t)~LED_DIRTY; }}while(0) +# define set_busy_led(x) do{if (x) { led_state |= LED_BUSY ; } else { led_state &= (uint8_t)~LED_BUSY ; }}while(0) +# define set_error_led(x) do{if (x) { led_state |= LED_ERROR; } else { led_state &= (uint8_t)~LED_ERROR; }}while(0) +#else +# define set_dirty_led(x) do{if (x) { DIRTY_LED_ON(); } else { DIRTY_LED_OFF(); }}while(0) +# define set_busy_led(x) do{if (x) { BUSY_LED_ON(); } else { BUSY_LED_OFF(); }}while(0) +# define set_error_led(x) do{if (x) { led_state |= LED_ERROR; } else { led_state &= (uint8_t)~LED_ERROR; update_leds(); }}while(0) +#endif + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a4310e8 --- /dev/null +++ b/src/main.c @@ -0,0 +1,200 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + main.c: Lots of init calls for the submodules + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "diskio.h" +#include "ff.h" +#include "led.h" +#include "timer.h" +#include "fpga.h" +#include "uart.h" +#include "ustring.h" +#include "utils.h" +#include "snes.h" +#include "fileops.h" +#include "memory.h" + +char stringbuf[100]; + +/* Make sure the watchdog is disabled as soon as possible */ +/* Copy this code to your bootloader if you use one and your */ +/* MCU doesn't disable the WDT after reset! */ +void get_mcusr(void) \ + __attribute__((naked)) \ + __attribute__((section(".init3"))); +void get_mcusr(void) +{ + MCUSR = 0; + wdt_disable(); +} + +#ifdef CONFIG_MEMPOISON +void poison_memory(void) \ + __attribute__((naked)) \ + __attribute__((section(".init1"))); +void poison_memory(void) { + register uint16_t i; + register uint8_t *ptr; + + asm("clr r1\n"); + /* There is no RAMSTARt variable =( */ + if (RAMEND > 2048 && RAMEND < 4096) { + /* 2K memory */ + ptr = (void *)RAMEND-2047; + for (i=0;i<2048;i++) + ptr[i] = 0x55; + } else if (RAMEND > 4096 && RAMEND < 8192) { + /* 4K memory */ + ptr = (void *)RAMEND-4095; + for (i=0;i<4096;i++) + ptr[i] = 0x55; + } else { + /* Assume 8K memory */ + ptr = (void *)RAMEND-8191; + for (i=0;i<8192;i++) + ptr[i] = 0x55; + } +} +#endif + +void avr_goto_addr(const uint32_t val) { + AVR_ADDR_RESET(); + for(uint32_t i=0; i 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1) +int main(void) __attribute__((OS_main)); +#endif +int main(void) { +#if defined __AVR_ATmega644__ || defined __AVR_ATmega644P__ || defined __AVR_ATmega2561__ + asm volatile("in r24, %0\n" + "ori r24, 0x80\n" + "out %0, r24\n" + "out %0, r24\n" + : + : "I" (_SFR_IO_ADDR(MCUCR)) + : "r24" + ); +#elif defined __AVR_ATmega32__ + asm volatile ("in r24, %0\n" + "ori r24, 0x80\n" + "out %0, r24\n" + "out %0, r24\n" + : + : "I" (_SFR_IO_ADDR(MCUCSR)) + : "r24" + ); +#elif defined __AVR_ATmega128__ || defined __AVR_ATmega1281__ + /* Just assume that JTAG doesn't hurt us on the m128 */ +#else +# error Unknown chip! +#endif + +#ifdef CLOCK_PRESCALE + clock_prescale_set(CLOCK_PRESCALE); +#endif + +/* BUSY_LED_SETDDR(); + DIRTY_LED_SETDDR(); + AUX_LED_SETDDR(); + + AUX_LED_OFF(); + set_busy_led(1); + set_dirty_led(0); +*/ + snes_reset(1); + uart_init(); + sei(); + _delay_ms(100); + disk_init(); + snes_init(); + timer_init(); + uart_puts_P(PSTR("\nsd2snes " VERSION)); + uart_putcrlf(); + + file_init(); + + FATFS fatfs; + f_mount(0,&fatfs); + set_busy_led(0); + uart_putc('W'); + fpga_init(); + fpga_pgm("/sd2snes/main.bit"); + uart_putc('!'); + _delay_ms(100); + set_avr_bank(0); + set_avr_ena(0); + set_avr_read(1); + set_avr_write(1); + AVR_ADDR_RESET(); + set_avr_addr_en(0); + snes_reset(1); + + uart_putc('('); + load_rom("/test.smc"); + uart_putc(')'); + + uart_putc('['); + load_sram("/test.srm"); + uart_putc(']'); + + AVR_ADDR_RESET(); + set_avr_mapper(0); + set_avr_ena(1); + set_avr_read(0); + set_avr_bank(0); + _delay_ms(100); + uart_puts_P(PSTR("SNES GO!")); + snes_reset(0); + DDRC = 0x00; + + while(1) { + snes_main_loop(); + } + while(1) { + uint8_t data=PINC; + _delay_ms(2); + if(data>=0x20 && data <= 0x7a) { + uart_putc(data); + } else { + uart_putc('.'); + } + SET_AVR_NEXTADDR(); + CLR_AVR_NEXTADDR(); +// set_avr_bank(3); + } + while(1); +} + diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..19caa8a --- /dev/null +++ b/src/memory.c @@ -0,0 +1,121 @@ +// insert cool lenghty disclaimer here +// memory.c: ROM+RAM operations + +#include +#include +#include +#include "config.h" +#include "uart.h" +#include "fpga.h" +#include "crc16.h" +#include "ff.h" +#include "fileops.h" + +char* hex = "0123456789ABCDEF"; + +uint32_t load_rom(char* filename) { +// TODO Mapper, Mirroring, Bankselect +// snes_rom_properties_t romprops; + set_avr_bank(0); + AVR_ADDR_RESET(); + SET_AVR_READ(); + UINT bytes_read; + DWORD filesize; + file_open(filename, FA_READ); + filesize = file_handle.fsize; + if(file_res) return 0; +// snes_rom_id(&romprops, &file_handle); + for(;;) { + bytes_read = file_read(); + if (file_res || !bytes_read) break; + for(int j=0; j>28)&0xf]); + uart_putc(hex[(crc>>24)&0xf]); + uart_putc(hex[(crc>>20)&0xf]); + uart_putc(hex[(crc>>16)&0xf]); */ + uart_putc(hex[(crc>>12)&0xf]); + uart_putc(hex[(crc>>8)&0xf]); + uart_putc(hex[(crc>>4)&0xf]); + uart_putc(hex[(crc)&0xf]); + uart_putcrlf(); + return crc; +} diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..541c371 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,10 @@ +// insert cool lengthy disclaimer here +// memory.h + +#ifndef MEMORY_H +#define MEMORY_H + uint32_t load_rom(char* filename); + uint32_t load_sram(char* filename); + void save_sram(char* filename, uint32_t sram_size); + uint32_t calc_sram_crc(uint32_t size); +#endif diff --git a/src/sdcard.c b/src/sdcard.c new file mode 100644 index 0000000..a27aff0 --- /dev/null +++ b/src/sdcard.c @@ -0,0 +1,749 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + sdcard.c: SD/MMC access routines + + Extended, optimized and cleaned version of code from MMC2IEC, + original copyright header follows: + +// +// Title : SD/MMC Card driver +// Author : Lars Pontoppidan, Aske Olsson, Pascal Dufour, +// Date : Jan. 2006 +// Version : 0.42 +// Target MCU : Atmel AVR Series +// +// CREDITS: +// This module is developed as part of a project at the technical univerisity of +// Denmark, DTU. +// +// DESCRIPTION: +// This SD card driver implements the fundamental communication with a SD card. +// The driver is confirmed working on 8 MHz and 14.7456 MHz AtMega32 and has +// been tested successfully with a large number of different SD and MMC cards. +// +// DISCLAIMER: +// The author is in no way responsible for any problems or damage caused by +// using this code. Use at your own risk. +// +// LICENSE: +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// + + The exported functions in this file are weak-aliased to their corresponding + versions defined in diskio.h so when this file is the only diskio provider + compiled in they will be automatically used by the linker. + +*/ + +#include +#include +#include +#include +#include "config.h" +#include "avrcompat.h" +#include "crc7.h" +#include "diskio.h" +#include "spi.h" +#include "uart.h" +#include "sdcard.h" + +#ifndef TRUE +#define TRUE -1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef CONFIG_TWINSD +# define MAX_CARDS 2 +#else +# define MAX_CARDS 1 +#endif + +// SD/MMC commands +#define GO_IDLE_STATE 0 +#define SEND_OP_COND 1 +#define SWITCH_FUNC 6 +#define SEND_IF_COND 8 +#define SEND_CSD 9 +#define SEND_CID 10 +#define STOP_TRANSMISSION 12 +#define SEND_STATUS 13 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define READ_MULTIPLE_BLOCK 18 +#define WRITE_BLOCK 24 +#define WRITE_MULTIPLE_BLOCK 25 +#define PROGRAM_CSD 27 +#define SET_WRITE_PROT 28 +#define CLR_WRITE_PROT 29 +#define SEND_WRITE_PROT 30 +#define ERASE_WR_BLK_STAR_ADDR 32 +#define ERASE_WR_BLK_END_ADDR 33 +#define ERASE 38 +#define LOCK_UNLOCK 42 +#define APP_CMD 55 +#define GEN_CMD 56 +#define READ_OCR 58 +#define CRC_ON_OFF 59 + +// SD ACMDs +#define SD_STATUS 13 +#define SD_SEND_NUM_WR_BLOCKS 22 +#define SD_SET_WR_BLK_ERASE_COUNT 23 +#define SD_SEND_OP_COND 41 +#define SD_SET_CLR_CARD_DETECT 42 +#define SD_SEND_SCR 51 + +// R1 status bits +#define STATUS_IN_IDLE 1 +#define STATUS_ERASE_RESET 2 +#define STATUS_ILLEGAL_COMMAND 4 +#define STATUS_CRC_ERROR 8 +#define STATUS_ERASE_SEQ_ERROR 16 +#define STATUS_ADDRESS_ERROR 32 +#define STATUS_PARAMETER_ERROR 64 + + +/* Card types - cardtype == 0 is MMC */ +#define CARD_SD (1<<0) +#define CARD_SDHC (1<<1) + +static uint8_t cardtype[MAX_CARDS]; + +/** + * getbits - read value from bit buffer + * @buffer: pointer to the data buffer + * @start : index of the first bit in the value + * @bits : number of bits in the value + * + * This function returns a value from the memory region passed as + * buffer, starting with bit "start" and "bits" bit long. The buffer + * is assumed to be MSB first, passing 0 for start will read starting + * from the highest-value bit of the first byte of the buffer. + */ +static uint32_t getbits(void *buffer, uint16_t start, int8_t bits) { + uint8_t *buf = buffer; + uint32_t result = 0; + + if ((start % 8) != 0) { + /* Unaligned start */ + result += buf[start / 8] & (0xff >> (start % 8)); + bits -= 8 - (start % 8); + start += 8 - (start % 8); + } + while (bits >= 8) { + result = (result << 8) + buf[start / 8]; + start += 8; + bits -= 8; + } + if (bits > 0) { + result = result << bits; + result = result + (buf[start / 8] >> (8-bits)); + } else if (bits < 0) { + /* Fraction of a single byte */ + result = result >> -bits; + } + return result; +} + +static uint8_t sdResponse(uint8_t expected) +{ + unsigned short count = 0x0FFF; + + while ((spiTransferByte(0xFF) != expected) && count ) + count--; + + // If count didn't run out, return success + return (count != 0); +} + +static uint8_t sdWaitWriteFinish(void) +{ + unsigned short count = 0xFFFF; // wait for quite some time + + while ((spiTransferByte(0xFF) == 0) && count ) + count--; + + // If count didn't run out, return success + return (count != 0); +} + +static void deselectCard(uint8_t card) { + // Send 8 clock cycles + SPI_SS_HIGH(card); + spiTransferByte(0xff); +} + +/** + * sendCommand - send a command to the SD card + * @card : card number to be accessed + * @command : command to be sent + * @parameter: parameter to be sent + * @deselect : Flags if the card should be deselected afterwards + * + * This function calculates the correct CRC7 for the command and + * parameter and transmits all of it to the SD card. If requested + * the card will be deselected afterwards. + */ +static int sendCommand(const uint8_t card, + const uint8_t command, + const uint32_t parameter, + const uint8_t deselect) { + union { + uint32_t l; + uint8_t c[4]; + } long2char; + + uint8_t i,crc=0,errorcount; + uint16_t counter; + + long2char.l = parameter; + crc = crc7update(0 , 0x40+command); + crc = crc7update(crc, long2char.c[3]); + crc = crc7update(crc, long2char.c[2]); + crc = crc7update(crc, long2char.c[1]); + crc = crc7update(crc, long2char.c[0]); + crc = (crc << 1) | 1; + + errorcount = 0; + while (errorcount < CONFIG_SD_AUTO_RETRIES) { + // Select card + SPI_SS_LOW(card); +#ifdef CONFIG_TWINSD + if (card == 0 && command == GO_IDLE_STATE) + /* Force both cards to SPI mode simultaneously */ + SPI_SS_LOW(1); +#endif + + // Transfer command + spiTransferByte(0x40+command); + spiTransferLong(parameter); + spiTransferByte(crc); + + // Wait for a valid response + counter = 0; + do { + i = spiTransferByte(0xff); + counter++; + } while (i & 0x80 && counter < 0x1000); + +#ifdef CONFIG_TWINSD + if (card == 0 && command == GO_IDLE_STATE) + SPI_SS_HIGH(1); +#endif + + // Check for CRC error + // can't reliably retry unless deselect is allowed + if (deselect && (i & STATUS_CRC_ERROR)) { + uart_putc('x'); + deselectCard(card); + errorcount++; + continue; + } + + if (deselect) deselectCard(card); + break; + } + + return i; +} + +// Extended init sequence for SDHC support +static uint8_t extendedInit(const uint8_t card) { + uint8_t i; + uint32_t answer; + + // Send CMD8: SEND_IF_COND + // 0b000110101010 == 2.7-3.6V supply, check pattern 0xAA + i = sendCommand(card, SEND_IF_COND, 0b000110101010, 0); + if (i > 1) { + // Card returned an error, ok (MMC oder SD1.x) but not SDHC + deselectCard(card); + return TRUE; + } + + // No error, continue SDHC initialization + answer = spiTransferLong(0); + deselectCard(card); + + if (((answer >> 8) & 0x0f) != 0b0001) { + // Card didn't accept our voltage specification + return FALSE; + } + + // Verify echo-back of check pattern + if ((answer & 0xff) != 0b10101010) { + // Check pattern mismatch, working but not SD2.0 compliant + // The specs say we should not use the card, but let's try anyway. + return TRUE; + } + + return TRUE; +} + +// SD common initialisation +static void sdInit(const uint8_t card) { + uint8_t i; + uint16_t counter; + + counter = 0xffff; + do { + // Prepare for ACMD, send CMD55: APP_CMD + i = sendCommand(card, APP_CMD, 0, 1); + if (i > 1) { + // Command not accepted, could be MMC + return; + } + + // Send ACMD41: SD_SEND_OP_COND + // 1L<<30 == Host has High Capacity Support + i = sendCommand(card, SD_SEND_OP_COND, 1L<<30, 1); + // Repeat while card card accepts command but isn't ready + } while (i == 1 && --counter > 0); + + // Ignore failures, there is at least one Sandisk MMC card + // that accepts CMD55, but not ACMD41. + if (i == 0) + /* We know that a card is SD if ACMD41 was accepted. */ + cardtype[card] |= CARD_SD; +} + +/* Detect changes of SD card 0 */ +#ifdef SD_CHANGE_VECT +ISR(SD_CHANGE_VECT) { + if (SDCARD_DETECT) + disk_state = DISK_CHANGED; + else + disk_state = DISK_REMOVED; +} +#endif + +#ifdef CONFIG_TWINSD +/* Detect changes of SD card 1 */ +ISR(SD2_CHANGE_VECT) { + if (SD2_DETECT) + disk_state = DISK_CHANGED; + else + disk_state = DISK_REMOVED; +} +#endif + +// +// Public functions +// +void sd_init(void) { + SDCARD_DETECT_SETUP(); + SDCARD_WP_SETUP(); + SD_CHANGE_SETUP(); +#ifdef CONFIG_TWINSD + /* Initialize the control lines for card 2 */ + SD2_SETUP(); + SD2_CHANGE_SETUP(); +#endif +} +void disk_init(void) __attribute__ ((weak, alias("sd_init"))); + + +DSTATUS sd_status(BYTE drv) { +#ifdef CONFIG_TWINSD + if (drv != 0) { + if (SD2_DETECT) { + if (SD2_PIN & SD2_WP) { + return STA_PROTECT; + } else { + return RES_OK; + } + } else { + return STA_NOINIT|STA_NODISK; + } + } else +#endif + if (SDCARD_DETECT) { +// uart_putc('0'); + if (SDCARD_WP) { +// uart_putc('1'); + return STA_PROTECT; + } else { +// uart_putc('2'); + return RES_OK; + } + } else { +// uart_putc('3'); + return STA_NOINIT|STA_NODISK; + } +} +DSTATUS disk_status(BYTE drv) __attribute__ ((weak, alias("sd_status"))); + + +/** + * sd_initialize - initialize SD card + * @drv : drive + * + * This function tries to initialize the selected SD card. + */ +DSTATUS sd_initialize(BYTE drv) { + uint8_t i; + uint16_t counter; + uint32_t answer; + + if (drv >= MAX_CARDS) + return STA_NOINIT|STA_NODISK; + + /* Don't bother initializing a card that isn't there */ +// uart_putc('#'); + if (sd_status(drv) & STA_NODISK) + return sd_status(drv); + + /* JLB: Should be in sd_init, but some uIEC versions have + * IEC lines tied to SPI, so I moved it here to resolve the + * conflict. + */ + spiInit(); + + disk_state = DISK_ERROR; + + cardtype[drv] = 0; + + SPI_SS_HIGH(drv); + + // Send 80 clks + for (i=0; i<10; i++) { + spiTransferByte(0xFF); + } + + // Reset card + i = sendCommand(drv, GO_IDLE_STATE, 0, 1); + if (i != 1) { + return STA_NOINIT | STA_NODISK; + } + + if (!extendedInit(drv)) + return STA_NOINIT | STA_NODISK; + + sdInit(drv); + + counter = 0xffff; + // According to the spec READ_OCR should work at this point + // without retries. One of my Sandisk-cards thinks otherwise. + do { + // Send CMD58: READ_OCR + i = sendCommand(drv, READ_OCR, 0, 0); + if (i > 1) + deselectCard(drv); + } while (i > 1 && counter-- > 0); + + if (counter > 0) { + answer = spiTransferLong(0); + + // See if the card likes our supply voltage + if (!(answer & SD_SUPPLY_VOLTAGE)) { + // The code isn't set up to completely ignore the card, + // but at least report it as nonworking + deselectCard(drv); + return STA_NOINIT | STA_NODISK; + } + + // See what card we've got + if (answer & 0x40000000) { + cardtype[drv] |= CARD_SDHC; + } + } + + // Keep sending CMD1 (SEND_OP_COND) command until zero response + counter = 0xffff; + do { + i = sendCommand(drv, SEND_OP_COND, 1L<<30, 1); + counter--; + } while (i != 0 && counter > 0); + + if (counter==0) { + return STA_NOINIT | STA_NODISK; + } + +#ifdef CONFIG_SD_DATACRC + // Enable CRC checking + // The SD spec says that the host "should" send CRC_ON_OFF before ACMD_SEND_OP_COND. + // The MMC manual I have says that CRC_ON_OFF isn't allowed before SEND_OP_COND. + // Let's just hope that all SD cards work with this order. =( + i = sendCommand(drv, CRC_ON_OFF, 1, 1); + if (i > 1) { + return STA_NOINIT | STA_NODISK; + } +#endif + + // Send MMC CMD16(SET_BLOCKLEN) to 512 bytes + i = sendCommand(drv, SET_BLOCKLEN, 512, 1); + if (i != 0) { + return STA_NOINIT | STA_NODISK; + } + + // Thats it! + disk_state = DISK_OK; + return sd_status(drv); +} +DSTATUS disk_initialize(BYTE drv) __attribute__ ((weak, alias("sd_initialize"))); + + +/** + * sd_read - reads sectors from the SD card to buffer + * @drv : drive + * @buffer: pointer to the buffer + * @sector: first sector to be read + * @count : number of sectors to be read + * + * This function reads count sectors from the SD card starting + * at sector to buffer. Returns RES_ERROR if an error occured or + * RES_OK if successful. Up to SD_AUTO_RETRIES will be made if + * the calculated data CRC does not match the one sent by the + * card. If there were errors during the command transmission + * disk_state will be set to DISK_ERROR and no retries are made. + */ +DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) { + uint8_t sec,res,tmp,errorcount; + uint16_t crc,recvcrc; + + if (drv >= MAX_CARDS) + return RES_PARERR; + + for (sec=0;sec= CONFIG_SD_AUTO_RETRIES) return RES_ERROR; + } + + return RES_OK; +} +DRESULT disk_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_read"))); + + + +/** + * sd_write - writes sectors from buffer to the SD card + * @drv : drive + * @buffer: pointer to the buffer + * @sector: first sector to be written + * @count : number of sectors to be written + * + * This function writes count sectors from buffer to the SD card + * starting at sector. Returns RES_ERROR if an error occured, + * RES_WPRT if the card is currently write-protected or RES_OK + * if successful. Up to SD_AUTO_RETRIES will be made if the card + * signals a CRC error. If there were errors during the command + * transmission disk_state will be set to DISK_ERROR and no retries + * are made. + */ +DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) { + uint8_t res,sec,errorcount,status; + uint16_t crc; + + if (drv >= MAX_CARDS) + return RES_PARERR; + +#ifdef CONFIG_TWINSD + if (drv != 0) { + if (SD2_PIN & SD2_WP) + return RES_WRPRT; + } else +#endif + + if (SDCARD_WP) return RES_WRPRT; + + for (sec=0;sec> 8); + spiTransferByte(crc & 0xff); + + // Get and check status feedback + status = spiTransferByte(0xFF); + + // Retry if neccessary + if ((status & 0x0F) != 0x05) { + uart_putc('X'); + deselectCard(drv); + errorcount++; + buffer = oldbuffer; + continue; + } + + // Wait for write finish + if (!sdWaitWriteFinish()) { + SPI_SS_HIGH(drv); + disk_state = DISK_ERROR; + return RES_ERROR; + } + break; + } + deselectCard(drv); + + if (errorcount >= CONFIG_SD_AUTO_RETRIES) { + if (!(status & STATUS_CRC_ERROR)) + disk_state = DISK_ERROR; + return RES_ERROR; + } + } + + return RES_OK; +} +DRESULT disk_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_write"))); + +DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer) { + uint8_t i; + uint8_t buf[18]; + uint32_t capacity; + + if (drv >= MAX_CARDS) + return RES_NOTRDY; + + if (sd_status(drv) & STA_NODISK) + return RES_NOTRDY; + + if (page != 0) + return RES_ERROR; + + /* Try to calculate the total number of sectors on the card */ + /* FIXME: Write a generic data read function and merge with sd_read */ + if (sendCommand(drv, SEND_CSD, 0, 0) != 0) { + deselectCard(drv); + return RES_ERROR; + } + + /* Wait for data token */ + if (!sdResponse(0xfe)) { + deselectCard(drv); + return RES_ERROR; + } + + for (i=0;i<18;i++) { + buf[i] = spiTransferByte(0xff); + } + deselectCard(drv); + + if (cardtype[drv] & CARD_SDHC) { + /* Special CSD for SDHC cards */ + capacity = (1 + getbits(buf,127-69,22)) * 1024; + } else { + /* Assume that MMC-CSD 1.0/1.1/1.2 and SD-CSD 1.1 are the same... */ + uint8_t exponent = 2 + getbits(buf, 127-49, 3); + capacity = 1 + getbits(buf, 127-73, 12); + exponent += getbits(buf, 127-83,4) - 9; + while (exponent--) capacity *= 2; + } + + diskinfo0_t *di = buffer; + di->validbytes = sizeof(diskinfo0_t); + di->disktype = DISK_TYPE_SD; + di->sectorsize = 2; + di->sectorcount = capacity; + + return RES_OK; +} +DRESULT disk_getinfo(BYTE drv, BYTE page, void *buffer) __attribute__ ((weak, alias("sd_getinfo"))); diff --git a/src/sdcard.h b/src/sdcard.h new file mode 100644 index 0000000..84a1df4 --- /dev/null +++ b/src/sdcard.h @@ -0,0 +1,40 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + sdcard.h: Definitions for the SD/MMC access routines + +*/ + +#ifndef SDCARD_H +#define SDCARD_H + +#include "diskio.h" + +/* These functions are weak-aliased to disk_... */ +void sd_init(void); +DSTATUS sd_status(BYTE drv); +DSTATUS sd_initialize(BYTE drv); +DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count); +DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count); +DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer); + +#endif diff --git a/src/snes.c b/src/snes.c new file mode 100644 index 0000000..46f5907 --- /dev/null +++ b/src/snes.c @@ -0,0 +1,54 @@ +// insert cool lengthy disclaimer here + +// snes.c: SNES hardware control (resetting) + +#include +#include "avrcompat.h" +#include "config.h" +#include "uart.h" +#include "snes.h" +#include "memory.h" +#include "fileops.h" +#include "ff.h" + + +uint8_t initloop=1; +uint32_t sram_crc, sram_crc_old; +uint32_t sram_size = 8192; // sane default + +void snes_init() { + DDRD |= _BV(PD5); // PD5 = OUTPUT + snes_reset(1); +} + +/* + * sets the SNES reset state. + * + * state: put SNES in reset state when 1, release when 0 + */ +void snes_reset(int state) { + if(state) { + PORTD &= ~ _BV(PD5); + } else { + PORTD |= _BV(PD5); + } +} + +/* + * SD2SNES main loop. + * monitors SRAM changes, menu selections and other things + */ +void snes_main_loop() { + if(initloop) { + sram_crc_old = calc_sram_crc(sram_size); + initloop=0; + } + sram_crc = calc_sram_crc(sram_size); + if(sram_crc != sram_crc_old) { + uart_putc('U'); + uart_putcrlf(); + save_sram("/test.srm", sram_size); + } + sram_crc_old = sram_crc; + uart_putc('.'); +} diff --git a/src/snes.h b/src/snes.h new file mode 100644 index 0000000..354f2b7 --- /dev/null +++ b/src/snes.h @@ -0,0 +1,11 @@ +// insert cool lenghty disclaimer here + +// snes.h + +#ifndef SNES_H +#define SNES_H + +void snes_init(void); +void snes_reset(int state); +void snes_main_loop(void); +#endif diff --git a/src/spi.c b/src/spi.c new file mode 100644 index 0000000..c01dc14 --- /dev/null +++ b/src/spi.c @@ -0,0 +1,131 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + spi.c: Low level SPI access + + Extended, optimized and cleaned version of code from MMC2IEC, + original copyright header follows: + +// +// Title : SPI module +// Author : Lars Pontoppidan +// Date : January, 2007 +// Version : 1.03 +// Target MCU : Atmel AVR Series +// +// DESCRIPTION: +// This module implements initialization, sending and receiving bytes using +// hardware SPI on an AVR. +// +// DISCLAIMER: +// The author is in no way responsible for any problems or damage caused by +// using this code. Use at your own risk. +// +// LICENSE: +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// + +*/ + +// FIXME: Integrate into sdcard.c + +#include +#include "avrcompat.h" +#include "spi.h" + +// access routines +void spiInit(void) +{ + uint8_t dummy; + + // setup SPI I/O pins + SPI_PORT = (SPI_PORT & ~SPI_MASK) | SPI_SCK | SPI_SS | SPI_MISO; + SPI_DDR = (SPI_DDR & ~SPI_MASK) | SPI_SCK | SPI_SS | SPI_MOSI; + + // setup SPI interface: + // interrupts disabled, SPI enabled, MSB first, master mode, + // leading edge rising, sample on leading edge, clock = f/4 + SPCR = 0b01010000; + + // Enable SPI double speed mode -> clock = f/8 + SPSR = _BV(SPI2X); + + // clear status + dummy = SPSR; + + // clear receive buffer + dummy = SPDR; +} + + +uint8_t spiTransferByte(uint8_t data) +{ + // send the given data + SPDR = data; + + // wait for transfer to complete + loop_until_bit_is_set(SPSR, SPIF); + // *** reading of the SPSR and SPDR are crucial + // *** to the clearing of the SPIF flag + // *** in non-interrupt mode + + // return the received data + return SPDR; +} + + +uint32_t spiTransferLong(const uint32_t data) +{ + // It seems to be necessary to use the union in order to get efficient + // assembler code. + // Beware, endian unsafe union + union { + uint32_t l; + uint8_t c[4]; + } long2char; + + long2char.l = data; + + // send the given data + SPDR = long2char.c[3]; + // wait for transfer to complete + loop_until_bit_is_set(SPSR, SPIF); + long2char.c[3] = SPDR; + + SPDR = long2char.c[2]; + // wait for transfer to complete + loop_until_bit_is_set(SPSR, SPIF); + long2char.c[2] = SPDR; + + SPDR = long2char.c[1]; + // wait for transfer to complete + loop_until_bit_is_set(SPSR, SPIF); + long2char.c[1] = SPDR; + + SPDR = long2char.c[0]; + // wait for transfer to complete + loop_until_bit_is_set(SPSR, SPIF); + long2char.c[0] = SPDR; + + return long2char.l; +} diff --git a/src/spi.h b/src/spi.h new file mode 100644 index 0000000..44f0695 --- /dev/null +++ b/src/spi.h @@ -0,0 +1,68 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + 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 + +// function prototypes + +// SPI interface initializer +void spiInit(void); + +// spiTransferByte(u08 data) waits until the SPI interface is ready +// and then sends a single byte over the SPI port. The function also +// returns the byte that was received during transmission. +uint8_t spiTransferByte(uint8_t data); + +// spiTransferLong(u08 data) waits until the SPI interface is ready +// and then sends the long with MSB first over the SPI port. +uint32_t spiTransferLong(uint32_t data); + +// Macros for setting slave select: +#ifdef CONFIG_TWINSD +# define SPI_SS_HIGH(card) do { \ + if (card == 0) { \ + SPI_PORT |= SPI_SS; \ + } else { \ + SD2_PORT |= SD2_CS; \ + } \ + } while (0) +#define SPI_SS_LOW(card) do { \ + if (card == 0) { \ + SPI_PORT &= (uint8_t)~SPI_SS; \ + } else { \ + SD2_PORT &= (uint8_t)~SD2_CS; \ + } \ + } while (0) +#else +# define SPI_SS_HIGH(card) SPI_PORT |= SPI_SS +# define SPI_SS_LOW(card) SPI_PORT &= (uint8_t)~SPI_SS +#endif + +#endif diff --git a/src/src2doxy.pl b/src/src2doxy.pl new file mode 100755 index 0000000..466a6ab --- /dev/null +++ b/src/src2doxy.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl -w -i~ + +# src2doxy.pl - create doxygen-compatible comments from readable C source +# Copyright (C) 2008 Ingo Korb +# +# 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 +# +# This script parses a subset of kernel-doc style comments and rewrites +# them as something doxygen will understand. +use strict; + +my $incomment = 0; +my $inspecialcomment = 0; +my @postcomment = (); +my @outputstack; + +while (<>) { + my $filename; + ($filename = $ARGV) =~ s!.*/!!; + + chomp; + s/\r$//; + s/\s+$//; + + # doxygen is too stupid to understand after-the-variable comments + # without external help. WARNING: Will substitute within strings! + s!([^ \t]\s+)/// !$1///< !; + + $incomment = 1 if m!^/\*!; + if (m!^/\*\*$!) { + $inspecialcomment = 1; + # Kill output + $_ = ""; + next; + } + + if ($incomment) { + if (m!\*/$!) { + # End of comment + $incomment = 0; + $inspecialcomment = 0; + @outputstack = @postcomment; + @postcomment = (); + } elsif (/$filename:\s+(.*).?\s*$/) { + # Add file description + push @postcomment, "\n/*! \\file $ARGV\n * \\brief $1. */\n"; + } + if ($inspecialcomment == 1) { + # First line of special comment: Brief description + $inspecialcomment = 2; + m/^\s*\*\s*((struct |union )?[^: \t]+):?\s+-?\s*(.*)\s*$/; + $_ = "/*! \\brief $3\n *"; + } elsif ($inspecialcomment == 2) { + # Modify parameters + s/\@([^: \t]+)\s*:\s+(.*)\s*$/\\param $1 $2/; + } + } +} continue { + print "$_\n"; + while (scalar(@outputstack)) { + print shift @outputstack,"\n"; + } +} + diff --git a/src/time.h b/src/time.h new file mode 100644 index 0000000..2ee8803 --- /dev/null +++ b/src/time.h @@ -0,0 +1,16 @@ +#ifndef TIME_H +#define TIME_H + +typedef uint32_t time_t; +struct tm { + uint8_t tm_sec; // 0..59 + uint8_t tm_min; // 0..59 + uint8_t tm_hour; // 0..23 + uint8_t tm_mday; // 1..[28..31] + uint8_t tm_mon; // 0..11 + uint8_t tm_year; // since 1900, i.e. 2000 is 100 + uint8_t tm_wday; // 0 to 6, sunday is 6 + // A Unix struct tm has a few more fields we don't need in this application +}; + +#endif /* TIME_H */ diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..799d013 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,130 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + timer.c: System timer (and button debouncer) + +*/ + +#include "config.h" +#include +#include +#include "avrcompat.h" +#include "led.h" +#include "timer.h" + +volatile tick_t ticks; +// Logical buttons +volatile uint8_t active_keys; + +// Physical buttons +uint8_t buttonstate; +tick_t lastbuttonchange; + +/* Called by the timer interrupt when the button state has changed */ +static void buttons_changed(void) { + /* Check if the previous state was stable for two ticks */ + if (time_after(ticks, lastbuttonchange+2)) { + if (active_keys & IGNORE_KEYS) { + active_keys &= ~IGNORE_KEYS; + } else if (!(buttonstate & (BUTTON_PREV|BUTTON_NEXT))) { + /* Both buttons held down */ + active_keys |= KEY_HOME; + } else if (!(buttonstate & BUTTON_NEXT) && + (BUTTON_PIN & BUTTON_NEXT)) { + /* "Next" button released */ + active_keys |= KEY_NEXT; + } else if (!(buttonstate & BUTTON_PREV) && + (BUTTON_PIN & BUTTON_NEXT)) { + active_keys |= KEY_PREV; + } + } + + lastbuttonchange = ticks; + buttonstate = BUTTON_PIN & BUTTON_MASK; +} + +/* The main timer interrupt */ +ISR(TIMER1_COMPA_vect) { + uint8_t tmp = BUTTON_PIN & BUTTON_MASK; + + if (tmp != buttonstate) { + buttons_changed(); + } + + ticks++; + +#ifdef SINGLE_LED + if (led_state & LED_ERROR) { + if ((ticks & 15) == 0) + DIRTY_LED_PORT ^= DIRTY_LED_BIT(); + } else { + if ((led_state & LED_BUSY) || (led_state & LED_DIRTY)) { + DIRTY_LED_ON(); + } else { + DIRTY_LED_OFF(); + } + } +#else + if (led_state & LED_ERROR) + if ((ticks & 15) == 0) + DIRTY_LED_PORT ^= DIRTY_LED_BIT(); +#endif + + /* Sleep button triggers when held down for 2sec */ + if (time_after(ticks, lastbuttonchange+2)) { + if (!(buttonstate & BUTTON_NEXT) && + (buttonstate & BUTTON_PREV) && + time_after(ticks, lastbuttonchange+2*HZ) && + !key_pressed(KEY_SLEEP)) { + /* Set ignore flag so the release doesn't trigger KEY_NEXT */ + active_keys |= KEY_SLEEP | IGNORE_KEYS; + /* Avoid triggering for the next two seconds */ + lastbuttonchange = ticks; + } + } +#if CONFIG_RTC_VARIANT == 1 + increment_rtc(); +#endif + +#ifdef CONFIG_REMOTE_DISPLAY + /* Check if the display wants to be queried */ + if (!(SOFTI2C_PIN & _BV(SOFTI2C_BIT_INTRQ))) { + active_keys |= KEY_DISPLAY; + } +#endif +} + +void timer_init(void) { + /* Count F_CPU/8 in timer 0 */ + TCCR0B = _BV(CS01); + + /* Set up a 100Hz interrupt using timer 1 */ + OCR1A = 1249; + TCNT1 = 0; + TCCR1A = 0; + TCCR1B = _BV(WGM12) | _BV(CS10) | _BV(CS11); + TIMSK1 |= _BV(OCIE1A); + + /* Buttons */ + BUTTON_DDR &= (uint8_t)~BUTTON_MASK; + BUTTON_PORT |= BUTTON_MASK; +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..3a97dd5 --- /dev/null +++ b/src/timer.h @@ -0,0 +1,116 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + timer.h: System timer (and button-debouncer) + +*/ + +#ifndef TIMER_H +#define TIMER_H + +#include + +// Bit masks for the (simulated) keys +#define KEY_NEXT (1<<0) +#define KEY_PREV (1<<1) +#define KEY_HOME (1<<2) +#define KEY_SLEEP (1<<3) +/* Remote display service request */ +#define KEY_DISPLAY (1<<4) + +#define IGNORE_KEYS (1<<7) + +/// Logical keys that were pressed - must be reset by the reader. +extern volatile uint8_t active_keys; + +#define key_pressed(x) (active_keys & (x)) +#define reset_key(x) active_keys &= (uint8_t)~(x) +#define ignore_keys() active_keys = IGNORE_KEYS; + +typedef uint16_t tick_t; + +/// Global timing variable, 100 ticks per second +/// Use atomic access or getticks() ! +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) { + tick_t tmp; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + tmp = ticks; + } + return tmp; +} + +#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) \ + ((int16_t)(b) - (int16_t)(a) < 0) +#define time_before(a,b) time_after(b,a) + + +/// Calculate timer start value for given timeout in microseconds +#define TIMEOUT_US(x) (256-((float)F_CPU/10000000.0)*x) + +/** + * start_timeout - start a timeout using timer0 + * @startval: starting value for timer + * + * This function sets timer 0 to the specified value and clears its overflow + * flag. Use in conjunction with TIMEOUT_US to cause a timer overflow after + * a specified number of microseconds. DON'T use a variable as a parameter to + * the TIMEOUT_US macro because that would require run-time float calculations. + */ +#define start_timeout(startval) do { TCNT0 = startval; TIFR0 |= _BV(TOV0); } while (0) + +/** + * has_timed_out - returns true if timeout was reached + * + * This function returns true if the overflow flag of timer 0 is set which + * (together with start_timeout and TIMEOUT_US) will happen when the + * specified time has elapsed. + */ +#define has_timed_out() (TIFR0 & _BV(TOV0)) + +void timer_init(void); + +#endif diff --git a/src/uart.c b/src/uart.c new file mode 100644 index 0000000..f4adf01 --- /dev/null +++ b/src/uart.c @@ -0,0 +1,164 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + uart.c: UART access routines + +*/ + +#include +#include +#include +#include +#include "config.h" +#include "avrcompat.h" +#include "uart.h" + +static uint8_t txbuf[1 << CONFIG_UART_BUF_SHIFT]; +static volatile uint16_t read_idx; +static volatile uint16_t write_idx; + +ISR(USART_UDRE_vect) { + if (read_idx == write_idx) return; + UDR = txbuf[read_idx]; + read_idx = (read_idx+1) & (sizeof(txbuf)-1); + if (read_idx == write_idx) + UCSRB &= ~ _BV(UDRIE); +} + +void uart_putc(char c) { + uint16_t t=(write_idx+1) & (sizeof(txbuf)-1); +#ifndef CONFIG_DEADLOCK_ME_HARDER // :-) + UCSRB &= ~ _BV(UDRIE); // turn off RS232 irq +#else + while (t == read_idx); // wait for free space +#endif + txbuf[write_idx] = c; + write_idx = t; + //if (read_idx == write_idx) PORTD |= _BV(PD7); + UCSRB |= _BV(UDRIE); +} + +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; + } +} + +static int ioputc(char c, FILE *stream) { + if (c == '\n') uart_putc('\r'); + uart_putc(c); + return 0; +} + +uint8_t uart_getc(void) { + loop_until_bit_is_set(UCSRA,RXC); + return UDR; +} + +void uart_flush(void) { + while (read_idx != write_idx) ; +} + +void uart_puts_P(prog_char *text) { + uint8_t ch; + + while ((ch = pgm_read_byte(text++))) { + uart_putc(ch); + } +} + +void uart_putcrlf(void) { + uart_putc(13); + uart_putc(10); +} + +static FILE mystdout = FDEV_SETUP_STREAM(ioputc, NULL, _FDEV_SETUP_WRITE); + +void uart_init(void) { + /* Seriellen Port konfigurieren */ + + UBRRH = (int)((double)F_CPU/(16.0*CONFIG_UART_BAUDRATE)-1) >> 8; + UBRRL = (int)((double)F_CPU/(16.0*CONFIG_UART_BAUDRATE)-1) & 0xff; + + UCSRB = _BV(RXEN) | _BV(TXEN); + // I really don't like random #ifdefs in the code =( +#if defined __AVR_ATmega32__ + UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); +#else + UCSRC = _BV(UCSZ1) | _BV(UCSZ0); +#endif + + stdout = &mystdout; + + //UCSRB |= _BV(UDRIE); + read_idx = 0; + write_idx = 0; +} diff --git a/src/uart.h b/src/uart.h new file mode 100644 index 0000000..4f821b9 --- /dev/null +++ b/src/uart.h @@ -0,0 +1,59 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + uart.h: Definitions for the UART access routines + +*/ + +#ifndef UART_H +#define UART_H + +#ifdef CONFIG_UART_DEBUG + +#include + +void uart_init(void); +unsigned char uart_getc(void); +void uart_putc(char c); +void uart_puthex(uint8_t num); +void uart_trace(void *ptr, uint16_t start, uint16_t len); +void uart_flush(void); +void uart_puts_P(prog_char *text); +void uart_putcrlf(void); + +#include +#define dprintf(str,...) printf_P(PSTR(str), ##__VA_ARGS__) + +#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_putcrlf() do {} while(0) +#define uart_trace(a,b,c) do {} while(0) + +#endif + +#endif diff --git a/src/ustring.h b/src/ustring.h new file mode 100644 index 0000000..4ccddce --- /dev/null +++ b/src/ustring.h @@ -0,0 +1,42 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + ustring.h: uint8_t wrappers for string.h-functions + +*/ + +#ifndef USTRING_H +#define USTRING_H + +#include + +#define ustrcasecmp_P(s1,s2) (strcasecmp_P((char *)(s1), (s2))) +#define ustrchr(s,c) ((uint8_t *)strchr((char *)(s), (c))) +#define ustrcmp(s1,s2) (strcmp((char *)(s1), (char *)(s2))) +#define ustrcmp_P(s1,s2) (strcmp_P((char *)(s1), (s2))) +#define ustrcpy(s1,s2) (strcpy((char *)(s1), (char *)(s2))) +#define ustrcpy_P(s1,s2) (strcpy_P((char *)(s1), (s2))) +#define ustrncpy(s1,s2,n) (strncpy((char *)(s1), (char *)(s2),(n))) +#define ustrlen(s) (strlen((char *)(s))) +#define ustrrchr(s,c) ((uint8_t *)strrchr((char *)(s), (c))) + +#endif diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..6404e81 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,77 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + utils.c: Misc. utility functions that didn't fit elsewhere + +*/ + +#include +#include "ustring.h" + +uint8_t *appendnumber(uint8_t *msg, uint8_t value) { + if (value >= 100) { + *msg++ = '0' + value/100; + value %= 100; + } + + *msg++ = '0' + value/10; + *msg++ = '0' + value%10; + + return msg; +} + +/* Convert a one-byte BCD value to a normal integer */ +uint8_t bcd2int(uint8_t value) { + return (value & 0x0f) + 10*(value >> 4); +} + +/* Convert a uint8_t into a BCD value */ +uint8_t int2bcd(uint8_t value) { + return (value % 10) + 16*(value/10); +} + +/* Similiar to strtok_r, but only a single delimiting character */ +uint8_t *ustr1tok(uint8_t *str, const uint8_t delim, uint8_t **saveptr) { + uint8_t *tmp; + + if (str == NULL) + str = *saveptr; + + /* Skip leading delimiters */ + while (*str == delim) str++; + + /* If there is anything left... */ + if (*str) { + /* Search for the next delimiter */ + tmp = str; + while (*tmp && *tmp != delim) tmp++; + + /* Terminate the string there */ + if (*tmp != 0) + *tmp++ = 0; + + *saveptr = tmp; + + return str; + } else + return NULL; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..766b5b9 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,52 @@ +/* sd2iec - SD/MMC to Commodore serial bus interface/controller + Copyright (C) 2007-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 + + + utils.c: Misc. utility functions that didn't fit elsewhere + +*/ + +#ifndef UTILS_H +#define UTILS_H + +/* Side-effect safe min/max */ +#define max(a,b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define min(a,b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a < _b ? _a : _b; }) + + +/* Write a number to a string as ASCII */ +uint8_t *appendnumber(uint8_t *msg, uint8_t value); + +/* Convert between integer and BCD */ +uint8_t bcd2int(uint8_t value); +uint8_t int2bcd(uint8_t value); + +/* Tokenize a string like strtok_r, but with a single delimiter character only */ +uint8_t *ustr1tok(uint8_t *str, const uint8_t delim, uint8_t **saveptr); + +#endif diff --git a/verilog/sd2snes/address.v b/verilog/sd2snes/address.v new file mode 100644 index 0000000..3f6fb9d --- /dev/null +++ b/verilog/sd2snes/address.v @@ -0,0 +1,118 @@ +`timescale 1 ns / 1 ns +////////////////////////////////////////////////////////////////////////////////// +// Company: Rehkopf +// Engineer: Rehkopf +// +// Create Date: 01:13:46 05/09/2009 +// Design Name: +// Module Name: address +// Project Name: +// Target Devices: +// Tool versions: +// Description: Address logic w/ SaveRAM masking +// +// Dependencies: +// +// Revision: +// Revision 0.02 - All new combinatorial glory. fucking slow. +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module address( + input CLK, + input [2:0] MAPPER, // AVR detected mapper + input [23:0] SNES_ADDR, // requested address from SNES + input SNES_CS, // "CART" pin from SNES (active low) + output [20:0] SRAM_ADDR, // Address to request from SRAM + output [3:0] ROM_SEL, // which SRAM unit to access (active low) + input AVR_ADDR_RESET, // reset AVR sequence (active low) + input AVR_NEXTADDR, // next byte request from AVR + input AVR_ENA, // enable AVR master mode (active low) + input AVR_ADDR_EN, // enable address counter (active low) + input [1:0] AVR_BANK, // which bank does the AVR want + input MODE, // AVR(1) or SNES(0) ("bus phase") + output IS_SAVERAM, // address/CS mapped as SRAM? + output IS_ROM, // address mapped as ROM? + input AVR_NEXTADDR_CURR, + input AVR_NEXTADDR_PREV + ); + +reg [22:0] SRAM_ADDR_BUF; +reg [3:0] ROM_SEL_BUF; +reg [3:0] AVR_ROM_SEL_BUF; +reg [20:0] AVR_ADDR; +reg [3:0] CS_ARRAY[3:0]; + +wire [3:0] CURRENT_ROM_SEL; +wire [22:0] SRAM_ADDR_FULL; + +initial begin + AVR_ADDR = 21'b0; + CS_ARRAY[0] = 4'b1110; + CS_ARRAY[1] = 4'b1101; + CS_ARRAY[2] = 4'b1011; + CS_ARRAY[3] = 4'b0111; +end + +/* currently supported mappers: + Index Mapper + 000 HiROM + 001 LoROM +*/ + +/* HiROM: SRAM @ Bank 0x20-0x3f, 0xa0-0xbf + Offset 6000-7fff */ +assign IS_SAVERAM = ((MAPPER == 3'b000) ? (!SNES_ADDR[22] + & SNES_ADDR[21] + & &SNES_ADDR[14:13] + & !SNES_ADDR[15] + ) +/* LoROM: SRAM @ Bank 0x70-0x7f, 0xf0-0xff + Offset 0000-7fff */ + :(MAPPER == 3'b001) ? (&SNES_ADDR[22:20] + & !SNES_ADDR[15] + & !SNES_CS) + : 1'b0); + +assign IS_ROM = ((MAPPER == 3'b000) ? ( (!SNES_ADDR[22] + & SNES_ADDR[15]) + |(SNES_ADDR[22])) + :(MAPPER == 3'b001) ? ( (SNES_ADDR[15]) ) + : 1'b0); + +assign SRAM_ADDR_FULL = (MODE) ? AVR_ADDR + : ((MAPPER == 3'b000) ? + (IS_SAVERAM ? SNES_ADDR[14:0] - 15'h6000 + : SNES_ADDR[22:0]) + :(MAPPER == 3'b001) ? + (IS_SAVERAM ? SNES_ADDR[14:0] + : {1'b0, SNES_ADDR[22:16], SNES_ADDR[14:0]}) + : 21'b0); + +assign SRAM_BANK = SRAM_ADDR_FULL[22:21]; +assign SRAM_ADDR = SRAM_ADDR_FULL[20:0]; + +assign ROM_SEL = (MODE) ? CS_ARRAY[AVR_BANK] : IS_SAVERAM ? 4'b0111 : 4'b1110; // CS_ARRAY[SRAM_BANK]; +//assign ROM_SEL = 4'b1110; + +always @(posedge CLK) begin + if(AVR_NEXTADDR_CURR) begin + if(!AVR_NEXTADDR_PREV) begin + if(!AVR_ADDR_RESET) + AVR_ADDR <= 21'b0; + else if (!AVR_ADDR_EN) + AVR_ADDR <= AVR_ADDR + 1; + end + end +end + + +/* +always @(posedge AVR_NEXTADDR) begin + if (!AVR_ADDR_RESET) + AVR_ADDR <= 21'b0; + else if (!AVR_ADDR_EN) + AVR_ADDR <= AVR_ADDR + 1; +end +*/ +endmodule diff --git a/verilog/sd2snes/data.v b/verilog/sd2snes/data.v new file mode 100644 index 0000000..01d7ee8 --- /dev/null +++ b/verilog/sd2snes/data.v @@ -0,0 +1,83 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 23:03:06 05/13/2009 +// Design Name: +// Module Name: data +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module data( + input CLK, + input SNES_READ, + input SNES_WRITE, + input AVR_READ, + input AVR_WRITE, + inout [7:0] SNES_DATA, + inout [7:0] SRAM_DATA, + inout [7:0] AVR_DATA, + input MODE, + input SNES_DATA_TO_MEM, + input AVR_DATA_TO_MEM, + input SRAM_DATA_TO_SNES_MEM, + input SRAM_DATA_TO_AVR_MEM, + input AVR_ENA, + input AVR_NEXTADDR_PREV, + input AVR_NEXTADDR_CURR + ); + +reg [7:0] SNES_IN_MEM; +reg [7:0] SNES_OUT_MEM; +reg [7:0] AVR_IN_MEM; +reg [7:0] AVR_OUT_MEM; + +assign SNES_DATA = SNES_READ ? 8'bZ : SNES_OUT_MEM; + +assign AVR_DATA = !AVR_ENA ? (!AVR_READ ? SRAM_DATA : 8'bZ) + : (AVR_READ ? 8'bZ : AVR_OUT_MEM); + +assign SRAM_DATA = !AVR_ENA ? (!AVR_WRITE ? AVR_DATA : 8'bZ)// /**/ : 8'bZ; + : MODE ? (!AVR_WRITE ? AVR_IN_MEM : 8'bZ) + : (!SNES_WRITE ? SNES_IN_MEM : 8'bZ); + +always @(posedge CLK) begin + if(SNES_DATA_TO_MEM) + SNES_IN_MEM <= SNES_DATA; + if(AVR_DATA_TO_MEM) + AVR_IN_MEM <= AVR_DATA; + if(SRAM_DATA_TO_SNES_MEM) + SNES_OUT_MEM <= SRAM_DATA; + if(SRAM_DATA_TO_AVR_MEM) + AVR_OUT_MEM <= SRAM_DATA; +end + + +/* +always @(posedge SNES_DATA_TO_MEM) begin + SNES_IN_MEM <= SNES_DATA; +end + +always @(posedge AVR_DATA_TO_MEM) begin + AVR_IN_MEM <= AVR_DATA; +end + +always @(posedge SRAM_DATA_TO_SNES_MEM) begin + SNES_OUT_MEM <= SRAM_DATA; +end + +always @(posedge SRAM_DATA_TO_AVR_MEM) begin + AVR_OUT_MEM <= SRAM_DATA; +end +*/ +endmodule diff --git a/verilog/sd2snes/dcm.v b/verilog/sd2snes/dcm.v new file mode 100644 index 0000000..9b8552a --- /dev/null +++ b/verilog/sd2snes/dcm.v @@ -0,0 +1,70 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 13:06:52 06/28/2009 +// Design Name: +// Module Name: dcm +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module my_dcm ( + input CLKIN, + input CLKFB, + output CLK2X + ); + + // DCM: Digital Clock Manager Circuit + // Spartan-3 + // Xilinx HDL Language Template, version 11.1 + + DCM #( + .SIM_MODE("SAFE"), // Simulation: "SAFE" vs. "FAST", see "Synthesis and Simulation Design Guide" for details + .CLKDV_DIVIDE(2.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5 + // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0 + .CLKFX_DIVIDE(1), // Can be any integer from 1 to 32 + .CLKFX_MULTIPLY(16), // Can be any integer from 2 to 32 + .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature + .CLKIN_PERIOD(0.0), // Specify period of input clock + .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE + .CLK_FEEDBACK("2X"), // Specify clock feedback of NONE, 1X or 2X + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or + // an integer from 0 to 15 + .DFS_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for frequency synthesis + .DLL_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for DLL + .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE + .FACTORY_JF(16'hC080), // FACTORY JF values + .PHASE_SHIFT(0), // Amount of fixed phase shift from -255 to 255 + .STARTUP_WAIT("FALSE") // Delay configuration DONE until DCM LOCK, TRUE/FALSE + ) DCM_inst ( + .CLK0(CLK0), // 0 degree DCM CLK output + .CLK180(CLK180), // 180 degree DCM CLK output + .CLK270(CLK270), // 270 degree DCM CLK output + .CLK2X(CLK2X), // 2X DCM CLK output + .CLK2X180(CLK2X180), // 2X, 180 degree DCM CLK out + .CLK90(CLK90), // 90 degree DCM CLK output + .CLKDV(CLKDV), // Divided DCM CLK out (CLKDV_DIVIDE) + .CLKFX(CLKFX), // DCM CLK synthesis out (M/D) + .CLKFX180(CLKFX180), // 180 degree CLK synthesis out + .LOCKED(LOCKED), // DCM LOCK status output + .PSDONE(PSDONE), // Dynamic phase adjust done output + .STATUS(STATUS), // 8-bit DCM status bits output + .CLKFB(CLKFB), // DCM clock feedback + .CLKIN(CLKIN), // Clock input (from IBUFG, BUFG or DCM) + .PSCLK(PSCLK), // Dynamic phase adjust clock input + .PSEN(PSEN), // Dynamic phase adjust enable input + .PSINCDEC(PSINCDEC), // Dynamic phase adjust increment/decrement + .RST(RST) // DCM asynchronous reset input + ); + assign RST=0; +endmodule diff --git a/verilog/sd2snes/main.ucf b/verilog/sd2snes/main.ucf new file mode 100644 index 0000000..778c00b --- /dev/null +++ b/verilog/sd2snes/main.ucf @@ -0,0 +1,191 @@ +# INST "AVR_NEXTADDR_BUFGP" LOC = BUFGMUX7; +# INST "CLK_BUFGP" LOC = BUFGMUX0; +NET "AVR_BANK[0]" LOC = P56; +NET "AVR_BANK[1]" LOC = P57; +NET "AVR_ENA" LOC = P40; +NET "AVR_READ" LOC = P41; +NET "MAPPER[0]" LOC = P68; +NET "MAPPER[1]" LOC = P69; +NET "MAPPER[2]" LOC = P70; +NET "ROM_SEL[0]" LOC = P124; +NET "ROM_SEL[1]" LOC = P125; +NET "ROM_SEL[2]" LOC = P122; +NET "ROM_SEL[3]" LOC = P123; +NET "SNES_ADDR[0]" LOC = P1; +NET "SNES_ADDR[10]" LOC = P13; +NET "SNES_ADDR[11]" LOC = P14; +NET "SNES_ADDR[12]" LOC = P15; +NET "SNES_ADDR[13]" LOC = P17; +NET "SNES_ADDR[14]" LOC = P18; +NET "SNES_ADDR[15]" LOC = P20; +NET "SNES_ADDR[16]" LOC = P21; +NET "SNES_ADDR[17]" LOC = P23; +NET "SNES_ADDR[18]" LOC = P24; +NET "SNES_ADDR[19]" LOC = P25; +NET "SNES_ADDR[1]" LOC = P2; +NET "SNES_ADDR[20]" LOC = P26; +NET "SNES_ADDR[21]" LOC = P27; +NET "SNES_ADDR[22]" LOC = P28; +NET "SNES_ADDR[23]" LOC = P30; +NET "SNES_ADDR[2]" LOC = P4; +NET "SNES_ADDR[3]" LOC = P5; +NET "SNES_ADDR[4]" LOC = P6; +NET "SNES_ADDR[5]" LOC = P7; +NET "SNES_ADDR[6]" LOC = P8; +NET "SNES_ADDR[7]" LOC = P10; +NET "SNES_ADDR[8]" LOC = P11; +NET "SNES_ADDR[9]" LOC = P12; +NET "SRAM_ADDR[0]" LOC = P92; +NET "SRAM_ADDR[10]" LOC = P104; +NET "SRAM_ADDR[11]" LOC = P105; +NET "SRAM_ADDR[12]" LOC = P107; +NET "SRAM_ADDR[13]" LOC = P108; +NET "SRAM_ADDR[14]" LOC = P73; +NET "SRAM_ADDR[15]" LOC = P74; +NET "SRAM_ADDR[16]" LOC = P76; +NET "SRAM_ADDR[17]" LOC = P77; +NET "SRAM_ADDR[18]" LOC = P78; +NET "SRAM_ADDR[19]" LOC = P79; +NET "SRAM_ADDR[1]" LOC = P93; +NET "SRAM_ADDR[20]" LOC = P80; +NET "SRAM_ADDR[2]" LOC = P95; +NET "SRAM_ADDR[3]" LOC = P96; +NET "SRAM_ADDR[4]" LOC = P97; +NET "SRAM_ADDR[5]" LOC = P98; +NET "SRAM_ADDR[6]" LOC = P99; +NET "SRAM_ADDR[7]" LOC = P100; +NET "SRAM_ADDR[8]" LOC = P102; +NET "SRAM_ADDR[9]" LOC = P103; +NET "SRAM_DATA[0]" LOC = P82; +NET "SRAM_DATA[1]" LOC = P83; +NET "SRAM_DATA[2]" LOC = P84; +NET "SRAM_DATA[3]" LOC = P85; +NET "SRAM_DATA[4]" LOC = P86; +NET "SRAM_DATA[5]" LOC = P87; +NET "SRAM_DATA[6]" LOC = P89; +NET "SRAM_DATA[7]" LOC = P90; +NET "SRAM_OE" LOC = P118; +NET "SRAM_WE" LOC = P119; +NET "AVR_ADDR_RESET" LOC = P44; +NET "CLKIN" LOC = P55; +NET "AVR_NEXTADDR" LOC = P128; +NET "SNES_READ" LOC = P31; +NET "SNES_WRITE" LOC = P32; +NET "AVR_WRITE" LOC = P58; +NET "SNES_CS" LOC = P52; +NET "AVR_ADDR_EN" LOC = P53; +NET "CLK" TNM_NET = CLK; +NET "AVR_DATA[0]" LOC = P46; +NET "AVR_DATA[1]" LOC = P47; +NET "AVR_DATA[2]" LOC = P50; +NET "AVR_DATA[3]" LOC = P51; +NET "AVR_DATA[4]" LOC = P59; +NET "AVR_DATA[5]" LOC = P60; +NET "AVR_DATA[6]" LOC = P63; +NET "AVR_DATA[7]" LOC = P65; +NET "SNES_DATA[0]" LOC = P129; +NET "SNES_DATA[1]" LOC = P130; +NET "SNES_DATA[2]" LOC = P131; +NET "SNES_DATA[3]" LOC = P132; +NET "SNES_DATA[4]" LOC = P135; +NET "SNES_DATA[5]" LOC = P137; +NET "SNES_DATA[6]" LOC = P140; +NET "SNES_DATA[7]" LOC = P141; +NET "SNES_DATABUS_DIR" LOC = P35; +NET "SNES_DATABUS_OE" LOC = P33; +NET "MODE" LOC = P112; +NET "CLKIN" TNM_NET = CLKIN; +TIMESPEC TS_CLKIN = PERIOD "CLKIN" 30 MHz HIGH 50 %; +NET "AVR_ADDR_EN" IOSTANDARD = LVCMOS33; +NET "AVR_ADDR_RESET" IOSTANDARD = LVCMOS33; +NET "AVR_BANK[0]" IOSTANDARD = LVCMOS33; +NET "AVR_BANK[1]" IOSTANDARD = LVCMOS33; +NET "AVR_DATA[0]" IOSTANDARD = LVCMOS33; +NET "AVR_DATA[1]" IOSTANDARD = LVCMOS33; +NET "AVR_DATA[2]" IOSTANDARD = LVCMOS33; +NET "AVR_DATA[3]" IOSTANDARD = LVCMOS33; +NET "AVR_DATA[4]" IOSTANDARD = LVCMOS33; +NET "AVR_DATA[5]" IOSTANDARD = LVCMOS33; +NET "AVR_DATA[6]" IOSTANDARD = LVCMOS33; +NET "AVR_DATA[7]" IOSTANDARD = LVCMOS33; +NET "AVR_ENA" IOSTANDARD = LVCMOS33; +NET "AVR_NEXTADDR" IOSTANDARD = LVCMOS33; +NET "AVR_READ" IOSTANDARD = LVCMOS33; +NET "AVR_WRITE" IOSTANDARD = LVCMOS33; +NET "CLKIN" IOSTANDARD = LVCMOS33; +NET "MAPPER[0]" IOSTANDARD = LVCMOS33; +NET "MAPPER[1]" IOSTANDARD = LVCMOS33; +NET "MAPPER[2]" IOSTANDARD = LVCMOS33; +NET "MODE" IOSTANDARD = LVCMOS33; +NET "ROM_SEL[0]" IOSTANDARD = LVCMOS33; +NET "ROM_SEL[1]" IOSTANDARD = LVCMOS33; +NET "ROM_SEL[2]" IOSTANDARD = LVCMOS33; +NET "ROM_SEL[3]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[0]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[10]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[11]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[12]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[13]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[14]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[15]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[16]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[17]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[18]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[19]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[1]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[20]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[21]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[22]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[23]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[2]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[3]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[4]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[5]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[6]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[7]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[8]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR[9]" IOSTANDARD = LVCMOS33; +NET "SNES_CS" IOSTANDARD = LVCMOS33; +NET "SNES_DATABUS_DIR" IOSTANDARD = LVCMOS33; +NET "SNES_DATABUS_OE" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[0]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[1]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[2]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[3]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[4]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[5]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[6]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[7]" IOSTANDARD = LVCMOS33; +NET "SNES_READ" IOSTANDARD = LVCMOS33; +NET "SNES_WRITE" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[0]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[10]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[11]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[12]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[13]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[14]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[15]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[16]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[17]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[18]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[19]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[1]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[20]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[2]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[3]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[4]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[5]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[6]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[7]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[8]" IOSTANDARD = LVCMOS33; +NET "SRAM_ADDR[9]" IOSTANDARD = LVCMOS33; +NET "SRAM_DATA[0]" IOSTANDARD = LVCMOS33; +NET "SRAM_DATA[1]" IOSTANDARD = LVCMOS33; +NET "SRAM_DATA[2]" IOSTANDARD = LVCMOS33; +NET "SRAM_DATA[3]" IOSTANDARD = LVCMOS33; +NET "SRAM_DATA[4]" IOSTANDARD = LVCMOS33; +NET "SRAM_DATA[5]" IOSTANDARD = LVCMOS33; +NET "SRAM_DATA[6]" IOSTANDARD = LVCMOS33; +NET "SRAM_DATA[7]" IOSTANDARD = LVCMOS33; +NET "SRAM_OE" IOSTANDARD = LVCMOS33; +NET "SRAM_WE" IOSTANDARD = LVCMOS33; diff --git a/verilog/sd2snes/main.v b/verilog/sd2snes/main.v new file mode 100644 index 0000000..1fc770b --- /dev/null +++ b/verilog/sd2snes/main.v @@ -0,0 +1,299 @@ +`timescale 1 ns / 1 ns +////////////////////////////////////////////////////////////////////////////////// +// Company: Rehkopf +// Engineer: Rehkopf +// +// Create Date: 01:13:46 05/09/2009 +// Design Name: +// Module Name: main +// Project Name: +// Target Devices: +// Tool versions: +// Description: Master Control FSM +// +// Dependencies: address +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module main( + input CLKIN, + input [2:0] MAPPER, + input [23:0] SNES_ADDR, + input SNES_READ, + input SNES_WRITE, + input SNES_CS, + inout [7:0] SNES_DATA, + inout [7:0] SRAM_DATA, + inout [7:0] AVR_DATA, + output [20:0] SRAM_ADDR, + output [3:0] ROM_SEL, + output SRAM_OE, + output SRAM_WE, + output SNES_DATABUS_OE, + output SNES_DATABUS_DIR, + input AVR_ADDR_RESET, + input AVR_ADDR_EN, + input AVR_READ, + input AVR_WRITE, + input AVR_NEXTADDR, + input AVR_ENA, + input [1:0] AVR_BANK, + output MODE + ); + +my_dcm snes_dcm(.CLKIN(CLKIN), + .CLK2X(CLK), + .CLKFB(CLKFB) + ); + +assign CLKFB = CLK; + +address snes_addr( + .CLK(CLK), + .MAPPER(MAPPER), + .SNES_ADDR(SNES_ADDR), // requested address from SNES + .SNES_CS(SNES_CS), // "CART" pin from SNES (active low) + .SRAM_ADDR(SRAM_ADDR), // Address to request from SRAM (active low) + .ROM_SEL(ROM_SEL), // which SRAM unit to access + .AVR_ADDR_RESET(AVR_ADDR_RESET), // reset AVR sequence (active low) + .AVR_NEXTADDR(AVR_NEXTADDR), // next byte request from AVR + .AVR_ENA(AVR_ENA), // enable AVR mode (active low) + .AVR_ADDR_EN(AVR_ADDR_EN), // enable AVR address counter (active low) + .AVR_BANK(AVR_BANK), // which bank does the AVR want + .MODE(MODE), // AVR(1) or SNES(0) ("bus phase") + .IS_SAVERAM(IS_SAVERAM), + .IS_ROM(IS_ROM), + .AVR_NEXTADDR_PREV(AVR_NEXTADDR_PREV), + .AVR_NEXTADDR_CURR(AVR_NEXTADDR_CURR) + ); + +data snes_data(.CLK(CLK), + .SNES_READ(SNES_READ), + .SNES_WRITE(SNES_WRITE), + .AVR_READ(AVR_READ), + .AVR_WRITE(AVR_WRITE), + .SNES_DATA(SNES_DATA), + .SRAM_DATA(SRAM_DATA), + .AVR_DATA(AVR_DATA), + .MODE(MODE), + .SNES_DATA_TO_MEM(SNES_DATA_TO_MEM), + .AVR_DATA_TO_MEM(AVR_DATA_TO_MEM), + .SRAM_DATA_TO_SNES_MEM(SRAM_DATA_TO_SNES_MEM), + .SRAM_DATA_TO_AVR_MEM(SRAM_DATA_TO_AVR_MEM), + .AVR_ENA(AVR_ENA), + .AVR_NEXTADDR_PREV(AVR_NEXTADDR_PREV), + .AVR_NEXTADDR_CURR(AVR_NEXTADDR_CURR) + ); + +parameter MODE_SNES = 1'b0; +parameter MODE_AVR = 1'b1; + +parameter STATE_0 = 8'b00000001; +parameter STATE_1 = 8'b00000010; +parameter STATE_2 = 8'b00000100; +parameter STATE_3 = 8'b00001000; +parameter STATE_4 = 8'b00010000; +parameter STATE_5 = 8'b00100000; +parameter STATE_6 = 8'b01000000; +parameter STATE_7 = 8'b10000000; + +reg [7:0] STATE; +reg [2:0] STATEIDX; + +reg STATE_RESET, CYCLE_RESET, CYCLE_RESET_ACK; +reg SRAM_WE_MASK; +reg SRAM_OE_MASK; + +reg [7:0] SRAM_WE_ARRAY [3:0]; +reg [7:0] SRAM_OE_ARRAY [3:0]; + +reg [7:0] SNES_DATA_TO_MEM_ARRAY[1:0]; +reg [7:0] AVR_DATA_TO_MEM_ARRAY[1:0]; +reg [7:0] SRAM_DATA_TO_SNES_MEM_ARRAY[1:0]; +reg [7:0] SRAM_DATA_TO_AVR_MEM_ARRAY[1:0]; + +reg [7:0] MODE_ARRAY; + +reg SNES_READ_CYCLE; +reg SNES_WRITE_CYCLE; +reg AVR_READ_CYCLE; +reg AVR_WRITE_CYCLE; + +reg SNES_DATABUS_OE_BUF; +reg SNES_DATABUS_DIR_BUF; + +reg AVR_NEXTADDR_PREV_BUF; +reg AVR_NEXTADDR_CURR_BUF; + +wire SNES_RW; + +assign MODE = !AVR_ENA ? MODE_AVR : MODE_ARRAY[STATEIDX]; + +assign SNES_RW = (SNES_READ & SNES_WRITE); + +initial begin + CYCLE_RESET = 0; + CYCLE_RESET_ACK = 0; + + STATE = STATE_7; + STATEIDX = 7; + SRAM_WE_MASK = 1'b1; + SRAM_OE_MASK = 1'b1; + SNES_READ_CYCLE = 1'b1; + SNES_WRITE_CYCLE = 1'b1; + AVR_READ_CYCLE = 1'b1; + AVR_WRITE_CYCLE = 1'b1; + + MODE_ARRAY = 8'b00011111; + + SRAM_WE_ARRAY[2'b00] = 8'b10010011; + SRAM_WE_ARRAY[2'b01] = 8'b10011111; + SRAM_WE_ARRAY[2'b10] = 8'b11110011; + SRAM_WE_ARRAY[2'b11] = 8'b11111111; + + SRAM_OE_ARRAY[2'b00] = 8'b11111111; + SRAM_OE_ARRAY[2'b01] = 8'b11100000; + SRAM_OE_ARRAY[2'b10] = 8'b00011111; + SRAM_OE_ARRAY[2'b11] = 8'b00000000; + + SNES_DATA_TO_MEM_ARRAY[1'b0] = 8'b10000000; + SNES_DATA_TO_MEM_ARRAY[1'b1] = 8'b00000000; + + AVR_DATA_TO_MEM_ARRAY[1'b0] = 8'b00010000; + AVR_DATA_TO_MEM_ARRAY[1'b1] = 8'b00000000; + + SRAM_DATA_TO_SNES_MEM_ARRAY[1'b0] = 8'b00000000; + SRAM_DATA_TO_SNES_MEM_ARRAY[1'b1] = 8'b00100000; + + SRAM_DATA_TO_AVR_MEM_ARRAY[1'b0] = 8'b00000000; + SRAM_DATA_TO_AVR_MEM_ARRAY[1'b1] = 8'b00000010; + + AVR_NEXTADDR_PREV_BUF = 0; + AVR_NEXTADDR_CURR_BUF = 0; +end + +// falling edge of SNES /RD or /WR marks the beginning of a new cycle +// SNES READ or WRITE always starts @posedge CLK !! +// CPU cycle can be 6, 8 or 12 CLK cycles so we must satisfy +// the minimum of 6 cycles to get everything done. + +always @(posedge CLK) begin + if (!SNES_RW) begin + if (!CYCLE_RESET_ACK) + CYCLE_RESET <= 1; + else + CYCLE_RESET <= 0; + end +end + +always @(posedge CLK) begin + if (CYCLE_RESET && !CYCLE_RESET_ACK) begin + CYCLE_RESET_ACK <= 1; + STATE <= STATE_0; + end else begin + case (STATE) + STATE_0: + STATE <= STATE_1; + STATE_1: + STATE <= STATE_2; + STATE_2: + STATE <= STATE_3; + STATE_3: + STATE <= STATE_4; + STATE_4: + STATE <= STATE_5; + STATE_5: + STATE <= STATE_6; + STATE_6: + STATE <= STATE_7; + STATE_7: begin + if (SNES_RW) // check for end of SNES cycle to avoid looping + CYCLE_RESET_ACK <= 0; // ready for new cycle + STATE <= STATE_7; + end + default: + STATE <= STATE_7; + endcase + end +end + +always @(posedge CLK) begin + case (STATE) + + STATE_7: begin + SNES_READ_CYCLE <= SNES_READ; + SNES_WRITE_CYCLE <= SNES_WRITE; + AVR_READ_CYCLE <= AVR_READ; + AVR_WRITE_CYCLE <= AVR_WRITE; + STATEIDX <= 7; + end + STATE_0: begin + STATEIDX <= 6; + end + + STATE_1: begin + STATEIDX <= 5; + end + + STATE_2: begin + STATEIDX <= 4; + end + + STATE_3: begin + STATEIDX <= 3; + end + + STATE_4: begin + STATEIDX <= 2; + end + + STATE_5: begin + STATEIDX <= 1; + end + + STATE_6: begin + STATEIDX <= 0; + end + endcase +end + +// When in AVR mode, enable SRAM_WE according to AVR programming +// else enable SRAM_WE according to state&cycle +assign SRAM_WE = !AVR_ENA ? AVR_WRITE + : ((!IS_SAVERAM & !MODE) | SRAM_WE_ARRAY[{SNES_WRITE_CYCLE, AVR_WRITE_CYCLE}][STATEIDX]); + +// When in AVR mode, enable SRAM_OE whenever not writing +// else enable SRAM_OE according to state&cycle +assign SRAM_OE = !AVR_ENA ? AVR_READ + : SRAM_OE_ARRAY[{SNES_WRITE_CYCLE, AVR_WRITE_CYCLE}][STATEIDX]; + +// dumb version +//assign SRAM_OE = !AVR_ENA ? AVR_READ : SNES_READ; +//assign SRAM_WE = !AVR_ENA ? AVR_WRITE : 1'b1; + +always @(posedge CLK) begin + SNES_DATABUS_OE_BUF <= SNES_CS | (SNES_READ & SNES_WRITE); +end + +always @(posedge CLK) begin + AVR_NEXTADDR_PREV_BUF <= AVR_NEXTADDR_CURR_BUF; + AVR_NEXTADDR_CURR_BUF <= AVR_NEXTADDR; +end + +assign AVR_NEXTADDR_PREV = AVR_NEXTADDR_PREV_BUF; +assign AVR_NEXTADDR_CURR = AVR_NEXTADDR_CURR_BUF; + +//assign SNES_DATABUS_OE = (!IS_SAVERAM & SNES_CS) | (SNES_READ & SNES_WRITE); +assign SNES_DATABUS_OE = (IS_ROM & SNES_CS) | (!IS_ROM & !IS_SAVERAM) | (SNES_READ & SNES_WRITE); +assign SNES_DATABUS_DIR = !SNES_WRITE ? 1'b0 : 1'b1; + +assign SNES_DATA_TO_MEM = SNES_DATA_TO_MEM_ARRAY[SNES_WRITE_CYCLE][STATEIDX]; +assign AVR_DATA_TO_MEM = AVR_DATA_TO_MEM_ARRAY[AVR_WRITE_CYCLE][STATEIDX]; + +assign SRAM_DATA_TO_SNES_MEM = SRAM_DATA_TO_SNES_MEM_ARRAY[SNES_WRITE_CYCLE][STATEIDX]; +assign SRAM_DATA_TO_AVR_MEM = SRAM_DATA_TO_AVR_MEM_ARRAY[AVR_WRITE_CYCLE][STATEIDX]; + +endmodule diff --git a/verilog/sd2snes/sd2snes.xise b/verilog/sd2snes/sd2snes.xise new file mode 100644 index 0000000..368506e --- /dev/null +++ b/verilog/sd2snes/sd2snes.xise @@ -0,0 +1,80 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/verilog/sd2snes/tf_main.v b/verilog/sd2snes/tf_main.v new file mode 100644 index 0000000..ad7076a --- /dev/null +++ b/verilog/sd2snes/tf_main.v @@ -0,0 +1,136 @@ +`timescale 1ns / 1ps + +//////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 23:11:58 05/13/2009 +// Design Name: main +// Module Name: /home/ikari/prj/sd2snes/verilog/sd2snes/tf_main.v +// Project Name: sd2snes +// Target Device: +// Tool versions: +// Description: +// +// Verilog Test Fixture created by ISE for module: main +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +//////////////////////////////////////////////////////////////////////////////// + +module tf_main; + + // Inputs + reg CLK; + reg [2:0] MAPPER; + reg [23:0] SNES_ADDR; + reg SNES_READ; + reg SNES_WRITE; + reg SNES_CS; + reg AVR_ADDR_RESET; + reg AVR_WRITE; + reg AVR_READ; + reg AVR_NEXTADDR; + reg AVR_ENA; + reg [1:0] AVR_BANK; + reg AVR_ADDR_EN; + + // Outputs + wire [20:0] SRAM_ADDR; + wire [3:0] ROM_SEL; + wire SRAM_OE; + wire SRAM_WE; + wire SNES_DATABUS_OE; + wire SNES_DATABUS_DIR; + wire MODE; + + // Bidirs + wire [7:0] SNES_DATA; + wire [7:0] SRAM_DATA; + wire [7:0] AVR_DATA; + + reg [7:0] SRAM_DATA_BUF; + reg [7:0] SNES_DATA_BUF; + +// Instantiate the Unit Under Test (UUT) + main uut ( + .CLKIN(CLK), + .MAPPER(MAPPER), + .SNES_ADDR(SNES_ADDR), + .SNES_READ(SNES_READ), + .SNES_WRITE(SNES_WRITE), + .SNES_CS(SNES_CS), + .SNES_DATA(SNES_DATA), + .SRAM_DATA(SRAM_DATA), + .AVR_DATA(AVR_DATA), + .SRAM_ADDR(SRAM_ADDR), + .ROM_SEL(ROM_SEL), + .SRAM_OE(SRAM_OE), + .SRAM_WE(SRAM_WE), + .AVR_ADDR_RESET(AVR_ADDR_RESET), + .AVR_WRITE(AVR_WRITE), + .AVR_NEXTADDR(AVR_NEXTADDR), + .AVR_ENA(AVR_ENA), + .AVR_BANK(AVR_BANK), + .AVR_READ(AVR_READ), + .AVR_ADDR_EN(AVR_ADDR_EN), + .SNES_DATABUS_OE(SNES_DATABUS_OE), + .SNES_DATABUS_DIR(SNES_DATABUS_DIR), + .MODE(MODE) + ); + + assign SRAM_DATA = SRAM_DATA_BUF; + initial begin + // Initialize Inputs + CLK = 1; + MAPPER = 0; + SNES_ADDR = 24'h223456; + SNES_READ = 1; + SNES_WRITE = 1; + SNES_CS = 0; + AVR_ADDR_RESET = 1; + AVR_WRITE = 1; + AVR_READ = 0; + AVR_NEXTADDR = 0; + AVR_ENA = 1; + AVR_BANK = 0; + AVR_ADDR_EN = 0; + SRAM_DATA_BUF = 8'hff; + // Wait for global reset to finish + #276; + #276 AVR_NEXTADDR <= ~AVR_NEXTADDR; + #276 AVR_NEXTADDR <= ~AVR_NEXTADDR; + #276 AVR_NEXTADDR <= ~AVR_NEXTADDR; + #276 AVR_NEXTADDR <= ~AVR_NEXTADDR; + #276 AVR_NEXTADDR <= ~AVR_NEXTADDR; + #276 AVR_NEXTADDR <= ~AVR_NEXTADDR; + #276 AVR_NEXTADDR <= ~AVR_NEXTADDR; + #276 AVR_NEXTADDR <= ~AVR_NEXTADDR; + SNES_ADDR <= 24'h123456; + SNES_READ <= 0; + #176; + SNES_READ <= 1; + #100; + SNES_WRITE <= 0; + #176; + SNES_WRITE <= 1; + #100; + AVR_WRITE <= 0; + SNES_READ <= 0; + #276; +// AVR_READ <= 1; + // Add stimulus here + end + + always + #23 CLK <= ~CLK; +// always begin +// #234 AVR_NEXTADDR <= ~AVR_NEXTADDR; +// end + +endmodule +