	Using SD and MMC cards with PICBASIC PRO
	Copyright 2009 microEngineering Labs, Inc.

The PICBASIC PRO programs included in this zip allow reading and writing to SD
(Secure Digital), SDHC (Secure Digital High Capacity) and MMC (Multi Media)
cards using the FAT16 and FAT32 file system. The code was written based on
information from the Microchip USB framework and Jan Axelson's book, USB Mass
Storage.  Details on SD and MMC cards and the FAT file system can be found
there.

The included code does not actually use the security features of SD cards.  It
reads and writes to the cards using SPI in MMC mode.  While the code can
certainly be modified to take advantage of the secure digital features, a
license to the SD consortium would need to be paid.  Information about this can
be found online.

Speaking of licenses, the FAT file system is owned by Microsoft and must be
licensed from them if you intend to sell a product that uses it.  The license
fee looks to be fairly nominal.  More information can be found on Microsoft's
web site.

The Cards

SD and MMC cards run on 3.3 volts or less.  Level conversion hardware, as well
as the lower voltage power supply, needs to be included on your board to
interface between the PIC and the card, if the PIC is running at more than 3.3
volts itself.  Microchip offers a PICtail card for SD and MMC cards that
includes this level conversion.  This is what was used for development of the
code.

The SD/MMC code supports reading and writing sectors on most SD, SDHC, and MMC
cards.  As very few cards, out of the many that are on the market, have been
tested, you will want to try the card you have in mind to make sure it will
work with the provided code.

The included file access code supports FAT16 and FAT32 as used by Windows.
There are actually several different file systems used by Windows, including
FAT12, FAT16, FAT32 and NTFS.  FAT16 (also know simply as FAT under Windows)
seems to be the most common file system used on moderately sized SD/MMC cards.
The sizes of the cards that can be used with FAT16 range from 32MB to 2GB.
FAT32 can be used to access SDHC cards.

Directories

Subdirectories are not supported with the current FAT16/FAT32 code.  All
files must reside in the root directory.
Using FAT16, About 512 files are allowed in the root directory.
With FAT32, the maximum number of files in the root directory varies according
to the size of the card and how it is formatted.  Generally, the maximum number
of root directory entries is equal to the number of sectors in a cluster
multiplied by 32 (32 entries per sector).  For an 8GB SD card, the maximum
number of root directory entries is 128.  See the table below:

SD card size	Maximum root directory entries using FAT32
< 256MB			16
256MB - 8GB		128
>8GB - 16GB		256
> 16GB - 32GB	512
> 32GB			1024 (>32GB SDXC cards not implemented yet)

If subdirectories are present, the maximum number of accessible directory
entires will be less than the maximum stated above, one less for each
subdirectory.

SD cards up to 2GB can be formatted in FAT16.  Some early 4GB non-SDHC card can
be formatted in FAT16, but don't rely on that as a 100% fact.

Most SD cards 4GB and larger must be formatted as FAT32.
Smaller capacity cards, down to about 512MB, can be formatted FAT16.

All filenames MUST be in 8.3 format (i.e. FILENAME.TXT), padded with spaces
if needed.  Long file names are NOT supported.


The Compiler

The included PICBASIC PRO code requires PBP version 2.50 or later.  It uses the
PIC18 long libraries that are part of that version of the compiler.  This also
means the files only work on PIC18 parts.  Be sure to specify the long version
of the compiler, PBPL, when compiling programs using these files.

The Files

The file in this directory called SDFSHC32c.BAS, where 'c' indicates the
revision of the library itself, contains the subroutines to read and write to
SD/MMC cards and to access files using the FAT16 file system.  The SDTEST.BAS
file is an example of what a program might look like that uses these
subroutines.

As shown in the example file SDTEST.BAS, your program first needs to create
aliases to the PIC I/O pins used by the SD/MMC card.  The PIC18F4550 is used
by this example program so the pin aliases reflect this.

After this, the SDFSHC32c.BAS program is included into the main program. This
gives the main program access to the subroutines and variables used to read
and write to the cards.

        Include "SDFSHC32c.BAS"

After the Include is a line that tells the routines whether to use software or
the hardware SSP module on the PIC for the SPI communications to the card. To
use the hardware SSP module, the line should read:

	SDC_UseHardSPI = TRUE

To use software SPI, the line should read:

	SDC_UseHardSPI = FALSE

After these lines of code, your program can do its own initialization and
whatever else it needs to do.  Once the program is ready to start talking
to the SD/MMC card, it would use Gosubs to the subroutines listed below.


Code space
-	21,056 bytes of code space used with default SDFSHC32c options used.
-	9,018 with minimal options - FAT16/Read-Only/Hardware SPI,
	all other DEFINEs are used
-	21,136 with all options enabled including SPEED define

Approximately 1401+ bytes of ram are used.  This amount may vary depending on
the configuration of the program, including DEFINEs used or not used.


DEFINEs

These defines can be added to the main program to remove functionality not
needed and are mainly used to save code space and/or to increase performance of
the SDFSHC file system code.

DEFINE:						Code Space Delta (code space saved or added)
DEFINE Description
-------------------------------------------------------------------------------
DEFINE NO_FSDIR				-1452
Subroutines to fill FAT_src with file directory will NOT be used

DEFINE NO_FSREMOVE			-160
No SD card files will be deleted

DEFINE NO_FSRENAME			-376
No SD card files will be renamed

DEFINE NO_FSFEOF			-52
End Of File function will not be needed

DEFINE NO_FSREWIND			-46
No need to set pointers back to beginning of opened file

DEFINE NO_FSFTELL			-18
No need to determine current file pointer

DEFINE NO_FSFCOPY			-726
No SD card files will be copied

DEFINE NO_FSF_FREE_SPACE	-932
No need to determine free space on SD card

DEFINE NO_SD_RAM			-252
No need to simulate large ram/eeprom with SD card

DEFINE NO_FS_WRITES			-6746
No need to write to SD card
if using REMOVE, RENAME or COPY, must not use this define

DEFINE NO_FAT32				-2694
No need for any FAT32 compatibility

DEFINE NO_FAT16				-1788
No need for any FAT16 compatibility

DEFINE NO_MACRO_HELPERS		-2262
No need for any assembly macros since they're not used in the main program

DEFINE FS_SPEED				+80
Speed is desired over code space usage
(Mainly 'unrolls' SOFTWARE bit banged SPI loops to in-line code)

DEFINE NO_HARDWARE_SPI		-74
Hardware SPI will NOT be used, only software/bit-banged SPI

DEFINE NO_SOFTWARE_SPI		-354
Software SPI will NOT be used, only HARDWARE SPI


SD_WE_used and SD_CD_used flags added.
IF either of these pins are actually used, then the code will check the pins
and act upon them accordingly.


Read/Write Performance

Approximately 9.7K/sec write speed using PIC18Fxxx(x) @ 40Mhz and software SPI
with 'DEFINE FSfast' used.

Hardware SPI @ 10Mhz (Fosc/4) performance can be roughly 70x faster than
software SPI at around 700K/sec write speed.

Connections

5v PIC connections to SD card as described below.
if running a PIC at about 3.3v, the pins can be connected directly to the PIC
without any extra resistors except for pin #8/#9.

SD_CS, SDO, SCK - 
can be either ST or TTL output pin on 3.3v/5v PIC.

SDI, SD_WE (if used), SD_CD (if used)
shoud be a TTL input pin on 5v PIC, usually PortB.

SD card pin #1,2,5 - PIC pin -> 2.2K -> SD card pin #1,
	3.3k pulldown to ground between 2.2K and SD card pin

SD card pin #3,6 - Ground

SD card pin #4 - +3.3v, decoupled at the SD card socket

SD card pin #7 - Direct connection to PIN input pin as noted above

SD card pin #8 & # 9 - 10K pullup to +3.3v

(Idea for simple SD card connector - A dual row header, back row straight, not
used, front row bent down ~10 degrees & wired as required, OR use a floppy
drive female connector)

Assembly macro helpers added:

FSINIT		macro
;fsinit
@ FSINIT

FSEXIT		macro
;fsexit
@ FSEXIT

FINDFIRST	macro
;findfirst
@ FINDFIRST

FINDNEXT	macro
;findnext
@ FINDNEXT

FSDIR		macro
;fill FAT_src with 32 8.3 filenames, 8 characters, a dot, 3 character
;extension, cr, lf, space, space,
@ FSDIR

FSDIRc		macro
;fill FAT_src with 46 8.3 filenames, no dot separator, no cr, no lf,
;no space, must parse elsewhere in program
@ FSDIRc

FSDIRs		macro
;fill FAT_src with 25 8.3 filenames plus file size information,
;"ffffffff.xxx 1234K <CR/LF>" (20 bytes per entry)
@ FSDIRs

FSDIRcont	macro
;fill FAT_src with the next bunch of filenames according to the mode used
;last time until file not found error return
@ FSDIRCONT

FSFCLOSE	macro
;fsclose
@ FSFCLOSE

FSFEOF		macro
;fsfeof
@ FSFEOF

FSREWIND	macro
;fsrewind
@ FSREWIND

FSFTELL		macro
;fsftell
@ FSFTELL

FSFOPENf	macro mode, macrostring
;fsfopen - filename, mode
@ FSFOPENF	"r","filename.txt"

FSFOPENff	macro mode, macrostring, bytecount
;fsfopen - mode,filename,bytecount for fixed length reads/writes
@ FSFOPENFF	"r","filename.txt",512

FSFOPEN		macro
;fsopen -
@ FSFOPEN

FSFREADf	macro
;fsfread - byte count is fixed
@ FSFREADf

FSFREAD		macro bytecount
;fsfread - byte count
@ FSFREAD

FSFWRITEf	macro
;fsfwrite - byte count is fixed
@ FSFWRITEF

FSFWRITE	macro bytecount
;fsfwrite - byte count
@ FSFWRITE

FSREMOVEf	macro macrostring
;fsremovef - remove filename
@ FSREMOVEF	"filename.txt"

FSREMOVE	macro
;fsremove - remove filename previously set in FAT_FILENAME[x]
@ FSREMOVE

FSRENAMEf	macro macrostring1, macrostring2
;fsrenamef - rename filename1 to filename2
@ FSRENAMEF	"filename.old","filename.new"

FSRENAME	macro
;fsrename - use filenames previously specified
@ FSRENAME

FSFSEEKo	macro offset
;fsfseek - seek to offset
@ FSFSEEKO	100000

FSFSEEK		macro
;fsfseek
@ FSFSEEK

FSFCOPYf	macro macrostring1, macrostring2
;fsfcopy - copy filename1 to filename2
@ FSFCOPYF	"filename.old","filename.new"

FSFCOPYfp	macro macrostring1, macrostring2
;fsfcopy - copy filename1 to filename2
@ FSFCOPYF	"filename.old","filename.new"

FSFCOPY		macro
;fsfcopy
@ FSFCOPY

FSFSPACEFREEs	macro spacefree
;fsfspacefree - check if -spacefree- is available on card
@ FSFSPACEFREEs	100

FSFSPACEFREEsp	macro spacefree
;fsfspacefree - check if -spacefree- is available on card
@ FSFSPACEFREEsp	100

FSFSPACEFREE	macro
;fsfspacefree
@ FSFSPACEFREE

FSSDREAD	macro address
@ fssdread	100000

FSSDWRITE	macro address
@ fssdread	100000


Functions available:

The Code

Finally, on to the actual code.  Talking to SD/MMC cards using the included
subroutines is quite simple.  The subroutines are accessed using Gosubs and
generally return an error byte, FAT_error, along with any other requested data.
If the error value is 0, the operation completed correctly.  If it is non-zero,
an error occurred.  The error table is included in the file SDFSHC32c.BAS.  It
is the constant table near the beginning of the file with names that all start
with CE_.

Most of the subroutines in the code are used internally, i.e. used by other
subroutines.  The only ones your program should call are:
	FSInit			FSExit			FSfopen			FSfclose		FSfread
	FSfwrite		FSremove		FSrename		FSfeof			FSrewind
	FSfseek			FSftell			FINDfirst		FINDnext		FSDIR
	FSDIRcompact	FSDIRsized		FSDIRcont
	FSFCOPY/FSFCOPYRETURN
	FSFSPACEFREE/FSFSPACEFREERETURN
	FSSDREAD		FSSDWRITE

FSInit

The subroutine FSInit initializes the I/O pins that are used and reads the
initial data on the card, including the Master Boot Record.  It returns
FAT_error as 0 if everything went OK.  It is the first subroutine that must be
called.  It should also be called again if the SD/MMC card has been replaced.

        Gosub FSInit
		If (FAT_error != 0) Then Stop


FSExit

The subroutine FSExit is used when the program is done talking to an SD/MMC
card and the card is to be removed.  It actually doesnt do alot other than
setting a few flags and releasing the hardware SSP port, if it was being used.
It does not return an error code.

        Gosub FSExit


FSfopen

FSfopen is the subroutine used to open an existing file or create a new one.  A
file can be opened in read mode, write mode, or append mode.  The variable
FAT_mode is used to set the mode before fopen is called.  Once the file is
opened, the variable FAT_error is set to 0 if there were no problems.  The card
must not be write protected if you intend to write or append to a file.

The file to open is specified by the byte array variable FAT_FileName.  The
file name is in the short, 8.3 format.  Any characters that are not used must
be filled with a space.  The dot in the filename is not used.  While the
comparison to match an existing name is case insensitive, the name must be
upper case to work with Windows.

	FAT_FileName[0] = "T" : FAT_FileName[1] = "E" : FAT_FileName[2] = "S"
	FAT_FileName[3] = "T" : FAT_FileName[4] = " " : FAT_FileName[5] = " "
	FAT_FileName[6] = " " : FAT_FileName[7] = " "
	FAT_FileName[8] = "T" : FAT_FileName[9] = "X" : FAT_FileName[10] = "T"

	FAT_mode = "r"		' Read mode
	FAT_mode = "w"		' Write mode
	FAT_mode = "a"		' Append mode
	Gosub FSfopen
	If (FAT_error != 0) Then Stop


FSfclose

FSfclose is used to close a previously opened file.  It does not require a file
name as only one file is allowed to be open at a time.  While it is not
technically necessary to close a file that has been opened for read, any files
that are opened for write or append must be closed to make sure the directory
information gets written.

	Gosub FSfclose
	If (FAT_error != 0) Then Stop


FSfread

The subroutine FSfread is used to read a file opened with FSfopen.  The number
of bytes to be read from the file is specified by the word variable FAT_count.
The number of bytes actually read is returned in the word variable
FAT_readCount.  The bytes are put into the byte array variable FAT_dest.  The
size of this array is the same as the sector size, 512, though this is not
necessary.  The constant FAT_BUFFER_SIZE may be changed, if desired.  This
buffer memory is shared with the buffer used by FSfwrite, FAT_src, as they are
usually not used at the same time with only one file open.  This can be changed
if desired, as well.

FAT_error returns 0 is the were no issues with the read.
It returns 61 to indicate end of file.

	FAT_mode = "r"		' Read mode
	Gosub FSfopen
	If (FAT_error != 0) Then Stop
	FAT_count = 1		' Read 1 byte to buffer at a time
    Gosub FSfread
	While (FAT_error = 0)
		Serout2 PORTC.6, 84, [FAT_dest[0]]
		FAT_count = 1	' Read 1 byte to buffer at a time
        Gosub FSfread
	Wend


FSfwrite

The subroutine FSfwrite is used to write to a file opened with FSfopen.  The
number of bytes to write to the file is specified by the word variable
FAT_count.  The bytes are taken from the byte array variable FAT_src.  The
size of this array is the same as the sector size, 512, though this is not
necessary.  The constant FAT_BUFFER_SIZE may be changed, if desired.  This
buffer memory is shared with the buffer used by fread, FAT_dest, as they are
usually not used at the same time with only one file open.  This can be
changed if desired, as well.

FAT_error returns 0 is the were no issues with the write.

	FAT_mode = "w"		' Write mode
    Gosub FSfopen
	If (FAT_error != 0) Then Stop
	FAT_src[0] = "A"
	FAT_src[1] = "B"
	FAT_src[2] = "C"
	FAT_count = 3
    Gosub FSfwrite
	If (FAT_error != 0) Then Stop
    Gosub FSfclose
	If (FAT_error != 0) Then Stop


FSremove

To delete a file, use the subroutine FSremove.  The file to remove is
specified by the byte array variable FAT_FileName as used above in FSfopen.
FAT_error equal to 0 is returned if the deletion was successful.  The card
must not be write protected if you intend to delete a file.

	FAT_FileName[0] = "T" : FAT_FileName[1] = "E" : FAT_FileName[2] = "S"
	FAT_FileName[3] = "T" : FAT_FileName[4] = " " : FAT_FileName[5] = " "
	FAT_FileName[6] = " " : FAT_FileName[7] = " "
	FAT_FileName[8] = "T" : FAT_FileName[9] = "X" : FAT_FileName[10] = "T"

    Gosub FSremove
	If (FAT_error != 0) Then Stop


FSrename

To rename an existing file, use the subroutine FSrename.  The file to rename
is specified by the byte array variable FAT_FileName as used above in
FSfopen. The new name is specified by the byte array variable FAT_FileName2.

FAT_error equal to 0 is returned if FSrename was successful.  The card must
not be write protected if you intend to rename a file.

	FAT_FileName[0] = "T" : FAT_FileName[1] = "E" : FAT_FileName[2] = "S"
	FAT_FileName[3] = "T" : FAT_FileName[4] = "1" : FAT_FileName[5] = " "
	FAT_FileName[6] = " " : FAT_FileName[7] = " "
	FAT_FileName[8] = "T" : FAT_FileName[9] = "X" : FAT_FileName[10] = "T"

	FAT_FileName2[0] = "T" : FAT_FileName2[1] = "E" : FAT_FileName2[2] = "S"
	FAT_FileName2[3] = "T" : FAT_FileName2[4] = "2" : FAT_FileName2[5] = " "
	FAT_FileName2[6] = " " : FAT_FileName2[7] = " "
	FAT_FileName2[8] = "T" : FAT_FileName2[9] = "X" : FAT_FileName2[10] = "T"

        Gosub FSrename
	If (FAT_error != 0) Then Stop


FSfeof

FSfeof returns byte variable FAT_status of TRUE if the file is at its end.
Otherwise FALSE is returned

        Gosub FSfeof
	If (FAT_status = TRUE) Then
		Serout2 PORTC.6, 84, ["At end of file."]
	Endif


FSRewind

FSRewind allows the pointer to be moved to the beginning of the opened file.

	Gosub FSRewind
	If (Fat_error != 0) then STOP


FSfseek

FSfseek allows the pointer to the current position in the file to be set.
The long variable FAT_offset is used to set the position to seek to.

	FAT_offset = 10
        Gosub FSfseek
	If (FAT_error != 0) Then Stop


FSftell

FSftell returns the current position in the file.
The long variable FAT_seek contains the current position.

        Gosub FSftell
	Serout2 PORTC.6, 84, [Dec FAT_seek]


FINDfirst, FINDnext

FINDfirst and FINDnext are used to read the directory of an SD/MMC card.
The first or next filename found is returned in the byte array variable
FAT_FileName.  A FAT_error of 0 indicates a file name was found.

	Gosub FINDfirst		' Find first file on card
	While (FAT_error = 0)
		Serout2 PORTC.6, 84, [Str FAT_FileName\11, $d, $a]
		Gosub FINDnext	' Find next file on card
	Wend


FSDIR, FSDIRcompact, FSDIRsized, FSdircont

FSDIR(xxxxxx)	routines are used to get the first 32, 46, or 25 filenames on
				the SD card into the FAT_src buffer.

FSDIR			fills FAT_src with the first 32 filenames in 16 byte records.
				8 characters for the filename, one period, 3 character for
				the extension, a CR, an LF, and 2 spaces.

				gosub FSDIR


FSDIRcompact	fills FAT_src with first 46 filenames in a 11 bytes records.
				8 characters for the filename, 3 characters for the
				extension.  No dots, no CR/LF.
				
				gosub FSDIRcompact

FSDIRsized		fills FAT_src with first 25 filenames with file size.
				8 characters for the filename, dot, 3 characters for the
				extension, one space, 4 numeric characters, B (byte)
				K (K-byte) M (M-byte), and a trailing CR/LF.
				
				GOSUB FSDIRsized

FSdircont		If the last file in the root directory has not been found
				yet, this routine can be used to get the next 32, 46, or
				25 filenames in the root directory to FAT_src.  The number
				of filename returned depends on the last method of FSDIR
				that was used (FSDIR=32, FSDIRcompact=46, FSDIRsized=25)

				GOSUB FSDircont


FSFCOPY, FSFcopyreturn

FSFCOPY copies a given filename to a 2nd filename.  This is a byte-for-byte
copy of the original file.  FSFCOPY can seem to run quite slow for large files
(on the order of only a few K per second in some cases).
A provision for a progress indicator is provided by setting:
FSFCOPYprogressflag = 1
After each loop, the loop will RETURN to the original program with the current
progress percentage value in FSFCOPYPROGRESS (0-99).  After this value is acted
upon, a GOSUB back into the FSFCOPY routine allows it to continue where it left
off.  This can really slow down the process though.  After returning to the
main program using the -callback-, care must be taken not to change any of the
variables used in FSFCOPY subroutine, or any of the subroutines FSFcopy calls.
The source filename must be defined in the byte array similar to FSRename:
FILENAME[0]-FILENAME[10].
The destination filename must be defined in the byte array similar to FSRename:
FILENAME2[0]-FILENAME2[10].
The routine also assumes that the source filename actually exists, and the
destination filename does NOT exist.  If the destination filename exists, it
will be erased and replaced by the copied file.
This routine also assumes that there is enough free space on the SD card for
the file to be copied.


			'FSfcopy without returning to main program
			'set up filenames to be used in advance
			gosub FSfcopy	'copy FILENAME to FILENAME2
			IF (FAT_error != 0) then STOP
			'FILENAME successfully copied to FILENAME2


			'FSfcopy that returns to main program for progress check
			'set up filenames to be used in advance
			FSFCOPYprogressflag = 1
			gosub FSfcopy	'copy FILENAME to FILENAME2
	copyreturnpoint:
			LCDOUT DEC2 FAT_copyprogress	'display percentage of file copy
											'completed thus far
			if FSFcopyprogressflag = 1 then	'if flag=1 then FSFcopy hasn't
				gosub FSFcopyreturn			'reset the progress flag then
				goto copyreturnpoint		'copy is not complete
			endif
			
			IF (FAT_error != 0) then STOP
			'FILENAME successfully copied to FILENAME2


FSFSPACEFREE, FSFFREEreturn
FSFSPACEFREE returns a value which is the amount of free space on the card in
KB (Kilo-Bytes = 1024 bytes).  This value may not be an exact amount, but the
resulting value will not be larger than the actual value.
Since this command can also take some time, a provision for a progress
indicator like FSFCOPY is also provided by setting:
FAT_freeprogress = 1
After returning to the main program using the -callback-, care must be taken
not to change any of the variables used in FSFSPACEFREE, or any of its
subroutines.
Also, if FAT_free_check is not equal to zero when FSFspacefree is started,
FSFSPACEFREE will only run as far as it needs to run to determine if the number
specified in FAT_FREE_CHECK is available, then it returns to the main program.
FAT_free_check = 100
FSFspacefree will only check if 100KB is free on the SD card, then return.

			'count the TOTAL amount of free space in KB available on the card
			FAT_free_check = 0
			GOSUB FSFspacefree
			LCDOUT DEC FAT_free
			
			'count the TOTAL amount of free space in KB available on the card
			'and return to the main program after each loop to display free
			'space
			FAT_free_check = 0
			FAT_freeprogress = 1
			GOSUB FSFspacefree
	freereturnpoint:
			lcdout DEC2 FAT_freeprogress	'display percentage completed
			LCDOUT DEC2 FAT_free			'display free space found so far
			if FAT_freeprogress = 1 then
				gosub FSFspacefree
				goto freereturnpoint
			endif
			
			'check if 100KB is free on the SD card
			FAT_free_check = 100
			GOSUB FSFspacefree
			LCDOUT DEC FAT_free	'should show 100 or slightly higher

			'check if 100KB is free on the SD card
			'and return to the main program after each loop to display free
			'space
			FAT_free_check = 100
			FAT_freeprogress = 1
			GOSUB FSFspacefree
	freereturnpoint:
			lcdout DEC2 FAT_freeprogress	'display percentage completed
			LCDOUT DEC2 FAT_free			'display free space found so far
			if FAT_freeprogress = 1 then
				gosub FSFspacefree
				goto freereturnpoint
			endif


FSSDREAD/FSSDWRITE
FSSDREAD reads a byte at the address specified by SDRAMADD into SDRAMDATA.
This subroutine will not read or write an address above the 2GB point.

			SDRAMADD = $10000
			gosub FSsdread
			'resulting by located at $10000 will be returned in SDRAMDATA
			
FSSDWRITE writes a byte in SDRAMDATA to the address specified by SDRAMADD.
This subroutine will not READ or WRITE to an address above the 2GB point.

			SDRAMADD = $10000
			SDRAMDATA = $EF
			GOSUB FSSDWRITE
			'byte value $EF will be written to $10000



NOTES:
The code initializes at slow speed, roughly 50Khz.  Afterwards, it switches over
to whatever mode you set up after that, either hardware or software based SPI.
ShiftIn/ShiftOut runs at roughly 50-77Khz.
Bit-Banged software SPI runs around 150Khz @ 40Mhz.
Hardware SPI can run up to 10Mhz using using hardware SPI.

If having problems with a larger capacity SD card, try using different (i.e. lower)
value voltage divider resistors on the SD card clock line.

This software uses the one-bit SPI mode only.  4-bit mode/SD bus mode, takes a lot
more programming than I want to deal with!
Not to mention the processing time it would take to calculate CRC's 'on-the-fly'.

No CRC's used except during initialization, then only pre-calculated CRC's are used.
CRC's can be, and are, disabled in SPI mode.

Single block commands only, one sector at a time.  The PIC18F's don't have enough ram
to be able to use multiple block transfers and have any practical ram space left over
for the program running itself.
Eventually, I'll get my hands on a static ram I2C chip from Microchip and integrate
that into SDFSHC32 somehow to make multi-block reads/write possible.  And even then,
multi-block transfers will be limited by the SD card itself.