software/libbase/bios: rename spi.c/h to spisdcard.h, also rename functions.
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Thu, 19 Mar 2020 10:02:15 +0000 (11:02 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Thu, 19 Mar 2020 10:02:15 +0000 (11:02 +0100)
litex/soc/software/bios/boot.c
litex/soc/software/bios/boot.h
litex/soc/software/bios/main.c
litex/soc/software/include/base/spi.h [deleted file]
litex/soc/software/include/base/spisdcard.h [new file with mode: 0644]
litex/soc/software/libbase/Makefile
litex/soc/software/libbase/spi.c [deleted file]
litex/soc/software/libbase/spisdcard.c [new file with mode: 0644]

index 355f20a08f836c9a238afc956ce4014e074f4906..4cf698cf4047c4a3db6d965e6e079d74d21f14fa 100644 (file)
@@ -489,10 +489,10 @@ void romboot(void)
 #endif
 
 // SPI HARDWARE BITBANG
-#ifdef CSR_SPI_BASE
-#include <spi.h>
+#ifdef CSR_SPISDCARD_BASE
+#include <spisdcard.h>
 
-void spisdboot(void)
+void spisdcardboot(void)
 {
     printf("SD Card via SPI Initialising\n");
     if(spi_sdcard_goidle() == 0) {
index fd1d06a2e9814be24bbfb59f69a7f7122256081e..f9517bad9f7417476a8134546d7ea8916aef4253 100644 (file)
@@ -6,8 +6,8 @@ void netboot(void);
 void flashboot(void);
 void romboot(void);
 
-#ifdef CSR_SPI_BASE
-void spisdboot(void);
+#ifdef CSR_SPISDCARD_BASE
+void spisdcardboot(void);
 #endif
 
 #endif /* __BOOT_H */
index 460563f8b4008fc1360bef973859f0271eefac61..5024e4f41dedba7ad9cade9f46a2b6c069e53ad4 100644 (file)
@@ -395,8 +395,8 @@ static void help(void)
        puts("sdram_mpr                       - read SDRAM MPR");
        puts("sdram_mrwr reg value            - write SDRAM mode registers");
 #endif
-#ifdef CSR_SPI_BASE
-        puts("spisdboot   - boot from SDCard via SPI hardware bitbang");
+#ifdef CSR_SPISDCARD_BASE
+        puts("spisdcardboot   - boot from SDCard via SPI hardware bitbang");
 #endif
 }
 
@@ -507,8 +507,8 @@ static void do_command(char *c)
                sdrhw();
        }
 #endif
-#ifdef CSR_SPI_BASE
-        else if(strcmp(token, "spisdboot") == 0) spisdboot();
+#ifdef CSR_SPISDCARD_BASE
+        else if(strcmp(token, "spisdcardboot") == 0) spisdcardboot();
 #endif
 
        else if(strcmp(token, "") != 0)
diff --git a/litex/soc/software/include/base/spi.h b/litex/soc/software/include/base/spi.h
deleted file mode 100644 (file)
index db6ade2..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-int spi_sdcard_init(uint32_t device);
-int spi_sdcard_read_sector(uint32_t device, unsigned long lba,unsigned char *buf);
-
-unsigned char spi_sdcard_goidle(void);
-unsigned char spi_sdcard_readMBR(void);
-unsigned char spi_sdcard_readFile(char *, char *, unsigned long);
diff --git a/litex/soc/software/include/base/spisdcard.h b/litex/soc/software/include/base/spisdcard.h
new file mode 100644 (file)
index 0000000..db6ade2
--- /dev/null
@@ -0,0 +1,6 @@
+int spi_sdcard_init(uint32_t device);
+int spi_sdcard_read_sector(uint32_t device, unsigned long lba,unsigned char *buf);
+
+unsigned char spi_sdcard_goidle(void);
+unsigned char spi_sdcard_readMBR(void);
+unsigned char spi_sdcard_readFile(char *, char *, unsigned long);
index 7ccb4d0b8c6786472a90f29ba15f236dd60b84e4..e3a80a0392f865b964cc724d8e3c96e14802954e 100755 (executable)
@@ -2,7 +2,7 @@ include ../include/generated/variables.mak
 include $(SOC_DIRECTORY)/software/common.mak
 
 OBJECTS=exception.o libc.o errno.o crc16.o crc32.o console.o \
-       system.o id.o uart.o time.o qsort.o strtod.o spiflash.o spi.o strcasecmp.o mdio.o
+       system.o id.o uart.o time.o qsort.o strtod.o spiflash.o spisdcard.o strcasecmp.o mdio.o
 
 all: crt0-$(CPU)-ctr.o crt0-$(CPU)-xip.o libbase.a libbase-nofloat.a
 
diff --git a/litex/soc/software/libbase/spi.c b/litex/soc/software/libbase/spi.c
deleted file mode 100644 (file)
index b602860..0000000
+++ /dev/null
@@ -1,585 +0,0 @@
-// SD CARD bitbanging code for loading files from a FAT16 forrmatted partition into memory
-//
-// Code is known to work on a de10nano with MiSTer SDRAM and IO Boards - IO Board has a secondary SD CARD interface connected to GPIO pins
-// SPI signals CLK, CS and MOSI are configured as GPIO output pins, and MISO is configued as GPIO input pins
-//
-// Protocol details developed from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/
-//
-// FAT16 details developed from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ and https://codeandlife.com/2012/04/07/simple-fat-and-sd-tutorial-part-2/
-
-// Import LiteX SoC details that are generated each time the SoC is compiled for the FPGA
-//      csr defines the SPI Control registers
-//      soc defines the clock CONFIG_CLOCK_FREQUENCY (50MHz for the VexRiscV processor on the MiSTer FPGA
-//      mem defines the addresses for the SDRAM MAIN_RAM_BASE and MAIN_RAM_SIZE
-#include <generated/csr.h>
-#include <generated/soc.h>
-#include <generated/mem.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <string.h>
-
-#ifdef CSR_SPI_BASE
-// Import prototypes for the functions
-#include <spi.h>
-
-// SPI 
-//      cs line - high to indicate DESELECT
-//              - low to indicate SELECT
-#define CS_HIGH         0x00
-#define CS_LOW          0x01
-
-//      control register values
-//      onebyte to indicate 1 byte being transferred
-//      spi_start to indicate START of transfer
-//      spi_done to indicate transfer DONE
-#define ONEBYTE         0x0800
-#define SPI_START       0x01
-#define SPI_DONE        0x01
-
-// Return values
-#define SUCCESS         0x01
-#define FAILURE         0x00
-
-// spi_write_byte
-//      Send a BYTE (8bits) to the SD CARD
-//      Seqeunce
-//          Set MOSI
-//          Set START bit and LENGTH=8
-//          Await DONE
-//
-//      No return values
-//
-//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
-void spi_write_byte(unsigned char char_to_send);
-void spi_write_byte(unsigned char char_to_send)
-{
-    // Place data into MOSI register
-    // Pulse the START bit and set LENGTH=8
-    spi_mosi_write(char_to_send);
-    spi_control_write(ONEBYTE | SPI_START);
-    
-    // Wait for DONE
-    while( (spi_status_read() != SPI_DONE)) {}
-    // Signal end of transfer
-    spi_control_write( 0x00 );
-}
-
-
-// spi_read_rbyte
-//      Read a command response from the SD CARD - Equivalent to and R1 response or first byte of an R7 response
-//      Sequence
-//          Read MISO
-//          If MSB != 0 then send dsummy byte and re-read MISO
-//
-//      Return value is the response from the SD CARD
-//          If the MSB is not 0, this would represent an ERROR
-//          Calling function to determine if the correct response has been received
-//
-//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
-unsigned char spi_read_rbyte(void);
-unsigned char spi_read_rbyte(void)
-{
-    int timeout=32;
-    unsigned char r=0;    
-    
-    // Check if MISO is 0x0xxxxxxx as MSB=0 indicates valid response
-    r = spi_miso_read();
-    while( ((r&0x80)!=0) && timeout>0) {
-        spi_mosi_write( 0xff );
-        spi_control_write(ONEBYTE | SPI_START);
-        while( (spi_status_read() != SPI_DONE)) {}
-        r = spi_miso_read();
-        spi_control_write( 0x00 );
-        timeout--;
-    }
-
-//    printf("Done\n");
-    return r;
-}
-
-// spi_read_byte
-//      Sequence
-//          Send dummy byte
-//          Read MISO
-//
-//      Read subsequenct bytes from the SD CARD - MSB first
-//      NOTE different from the spi_read_rbyte as no need to await an intial 0 bit as card is already responsing
-//      Used to read additional response bytes, or data bytes from the SD CARD
-//
-//      Return value is the byte read
-//          NOTE no error status as assumed bytes are read via CLK pulses
-//
-//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
-unsigned char spi_read_byte(void);
-unsigned char spi_read_byte(void)
-{
-    unsigned char r=0;
-    
-    spi_write_byte( 0xff );
-    r = spi_miso_read();
-    
-    return r;
-}
-
-//  SETSPIMODE
-//      Signal the SD CARD to switch to SPI mode
-//      Pulse the CLK line HIGH/LOW repeatedly with MOSI and CS_N HIGH
-//      Drop CS_N LOW and pulse the CLK
-//      Check MISO for HIGH
-//      Return 0 success, 1 failure
-//
-//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Initializing the SD Card"
-unsigned char spi_setspimode(void);
-unsigned char spi_setspimode(void)
-{
-    unsigned int i, r, timeout=32;
-
-    // Initialise SPI mode
-    // set CS to HIGH
-    // Send pulses
-     do {
-        // set CS HIGH and send pulses
-        spi_cs_write(CS_HIGH);
-         for (i=0; i<10; i++) {
-            spi_write_byte( 0xff );        
-        }
-        
-        // set CS LOW and send pulses
-        spi_cs_write(CS_LOW);
-        r = spi_read_rbyte();
-        
-        timeout--;
-    } while ( (timeout>0) && (r==0) );
-
-    if(timeout==0) return FAILURE;
-    
-    return SUCCESS;
-}
-
-// SPI_SDCARD_GOIDLE
-//      Function exposed to BIOS to initialise SPI mode
-//      
-//      Sequence
-//          Set 100KHz timer mode
-//          Send CLK pulses to set SD CARD to SPI mode
-//          Send CMD0 - Software RESET - force SD CARD IDLE
-//          Send CMD8 - Check SD CARD type
-//          Send CMD55+ACMD41 - Force SD CARD READY
-//          Send CMD58 - Read SD CARD OCR (status register)
-//          Send CMD16 - Set SD CARD block size to 512 - Sector Size for the SD CARD
-//      NOTE - Each command is prefixed with a dummy set of CLK pulses to prepare SD CARD to receive a command
-//      Return 0 success, 1 failure
-//
-//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Initializing the SD Card"
-unsigned char spi_sdcard_goidle(void)
-{
-    unsigned char r;                                                                                                                // Response from SD CARD
-    int i, timeout;                                                                                                                 // TIMEOUT loop to send CMD55+ACMD41 repeatedly
-
-    r = spi_setspimode();                                                                                                               // Set SD CARD to SPI mode
-    if( r != 0x01 ) return FAILURE;
-
-    // CMD0 - Software reset - SD CARD IDLE
-    // Command Sequence is DUMMY=0xff CMD0=0x40 0x00 0x00 0x00 0x00 CRC=0x95
-    // Expected R1 response is 0x01 indicating SD CARD is IDLE
-    spi_write_byte( 0xff ); spi_write_byte( 0x40 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x95 );
-    r = spi_read_rbyte();
-    if(r!=0x01) return FAILURE;
-    
-    // CMD8 - Check SD CARD type
-    // Command sequence is DUMMY=0xff CMD8=0x48 0x00 0x00 0x01 0xaa CRC=0x87
-    // Expected R7 response is 0x01 followed by 0x00 0x00 0x01 0xaa (these trailing 4 bytes not currently checked)
-    spi_write_byte( 0xff ); spi_write_byte( 0x48 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x01 ); spi_write_byte( 0xaa ); spi_write_byte( 0x87 );
-    r = spi_read_rbyte();
-    if(r!=0x01) return FAILURE;
-    // Receive the trailing 4 bytes for R7 response - FIXME should check for 0x00 0x00 0x01 0xaa
-    for(i=0; i<4; i++)
-        r=spi_read_byte();
-     
-    // CMD55+ACMD41 - Force SD CARD READY - prepare card for reading/writing
-    // Command sequence is CMD55 followed by ACMD41
-    //      Send commands repeatedly until SD CARD indicates READY 0x00
-    // CMD55 Sequence is DUMMY=0xff CMD55=0x77 0x00 0x00 0x00 0x00 CRC=0x00
-    // ACMD41 Sequence is DUMMY=0xff ACMD41=0x69 0x40 0x00 0x00 0x00 CRC=0x00
-    // Expected R1 response is 0x00 indicating SD CARD is READY
-    timeout=32;
-    do {
-        spi_write_byte( 0xff ); spi_write_byte( 0x77 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 );      
-        r = spi_read_rbyte();
-        
-        spi_write_byte( 0xff ); spi_write_byte( 0x69 ); spi_write_byte( 0x40 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 );      
-        r = spi_read_rbyte();
-        timeout--;
-    } while ((r != 0x00) && (timeout>0));
-    if(r!=0x00) return FAILURE;
-    
-    // CMD58 - Read SD CARD OCR (status register)
-    // FIXME - Find details on expected response from CMD58 to allow accurate checking of SD CARD R3 response
-    // Command sequence is DUMMY=0xff CMD58=0x7a 0x00 0x00 0x01 0xaa CRC=0xff
-    // Expected R3 response is 0x00 OR 0x01 followed by 4 (unchecked) trailing bytes
-    spi_write_byte( 0xff ); spi_write_byte( 0x7a ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0xff );
-    r = spi_read_rbyte();
-    if(r>0x01) return FAILURE;
-    // // Receive the trailing 4 bytes for R3 response
-    for(i=0; i<4; i++)
-        r=spi_read_byte();
-    
-    // CMD16 - Set SD CARD block size to 512 - Sector Size for the SD CARD
-    // Command Sequence is DUMMY=0xff (512 as unsigned long = 0x00000200) 0x00 0x00 0x02 0x00 CRC=0xff
-    // Expected R1 response is 0x00 indicating SD CARD is READY
-    spi_write_byte( 0xff ); spi_write_byte( 0x50 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x02 ); spi_write_byte( 0x00 ); spi_write_byte( 0xff );
-    r=spi_read_rbyte();    
-    if(r!=0x00) return FAILURE;
-
-    return SUCCESS;
-}
-
-// READSECTOR
-//      Read a 512 byte sector from the SD CARD
-//      Given SECTORNUMBER and memory STORAGE
-//
-//      Sequence
-//          Send CMD17 - Read Block
-//          Command Sequence is DUMMY=0xff CMD17=0x51 SECTORNUMBER (32bit UNSIGNED as bits 32-25,24-17, 16-9, 8-1) CRC=0xff
-//          Wait for SD CARD to send 0x00 indicating SD CARD is processing
-//          Wait for SD CARD to send 0xfe indicating SD CARD BLOCK START
-//          Read 512 bytes
-//          Read 8 DUMMY bytes
-//      Return 0 success, 1 failure
-//
-//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Read/Write SD Card"
-unsigned char readSector(unsigned int sectorNumber, unsigned char *storage);
-unsigned char readSector(unsigned int sectorNumber, unsigned char *storage)
-{
-    unsigned int n,timeout;                                                                                                             // Number of bytes loop, timeout loop awaiting response bytes
-    unsigned char r;                                                                                                                    // Response bytes from SD CARD
-    
-    // CMD17 - Read Block
-    // Command Sequence is DUMMY=0xff CMD17=0x51 SECTORNUMBER (32bit UNSIGNED as bits 32-25,24-17, 16-9, 8-1) CRC=0xff
-    // Expected R1 response is 0x00 indicating SD CARD is processing
-    spi_write_byte( 0xff ); spi_write_byte( 0x51 ); spi_write_byte( (sectorNumber>>24)&0xff ); spi_write_byte( (sectorNumber>>16)&0xff ); spi_write_byte( (sectorNumber>>8)&0xff ); spi_write_byte( (sectorNumber)&0xff ); spi_write_byte( 0xff ); 
-    r=spi_read_rbyte();
-    if( r!=0x00 ) return FAILURE;
-
-    // Await 0xfe to indicate BLOCK START
-    r=spi_read_byte();
-    timeout=16384;
-    while( (r!=0xfe) && (timeout>0) ) {
-        r=spi_read_byte();
-        timeout--;    
-    }
-    if( r!=0xfe ) return FAILURE;
-
-    // Read 512 bytes into storage
-    for(n=0; n<512; n++)
-        storage[n]=spi_read_byte();
-    
-    // Read 8 dummy bytes
-    for(n=0; n<8; n++)
-        r=spi_read_byte();
-
-    return SUCCESS;
-}
-
-
-// FAT16 Specific code starts here
-// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
-
-// Structure to store SD CARD partition table
-typedef struct {
-    unsigned char first_byte;
-    unsigned char start_chs[3];
-    unsigned char partition_type;
-    unsigned char end_chs[3];
-    unsigned long start_sector;
-    unsigned long length_sectors;
-} __attribute((packed)) PartitionTable;
-
-PartitionTable sdCardPartition;
-
-// Structure to store SD CARD FAT16 Boot Sector (boot code is ignored, provides layout of the FAT16 partition on the SD CARD)
-typedef struct {
-    unsigned char jmp[3];
-    char oem[8];
-    unsigned short sector_size;
-    unsigned char sectors_per_cluster;
-    unsigned short reserved_sectors;
-    unsigned char number_of_fats;
-    unsigned short root_dir_entries;
-    unsigned short total_sectors_short; // if zero, later field is used
-    unsigned char media_descriptor;
-    unsigned short fat_size_sectors;
-    unsigned short sectors_per_track;
-    unsigned short number_of_heads;
-    unsigned long hidden_sectors;
-    unsigned long total_sectors_long;
-    
-    unsigned char drive_number;
-    unsigned char current_head;
-    unsigned char boot_signature;
-    unsigned long volume_id;
-    char volume_label[11];
-    char fs_type[8];
-    char boot_code[448];
-    unsigned short boot_sector_signature;
-} __attribute((packed)) Fat16BootSector;
-
-Fat16BootSector sdCardFatBootSector;
-
-// Structure to store SD CARD FAT16 Root Directory Entries
-//      Allocated to MAIN RAM - hence pointer
-typedef struct {
-    unsigned char filename[8];
-    unsigned char ext[3];
-    unsigned char attributes;
-    unsigned char reserved[10];
-    unsigned short modify_time;
-    unsigned short modify_date;
-    unsigned short starting_cluster;
-    unsigned long file_size;
-} __attribute((packed)) Fat16Entry;                             
-
-Fat16Entry *sdCardFat16RootDir;
-
-// Structure to store SD CARD FAT16 Entries
-//      Array of UNSIGNED SHORTS (16bit integers)
-unsigned short *sdCardFatTable;
-
-// Calculated sector numbers on the SD CARD for the FAT16 Entries and ROOT DIRECTORY
-unsigned int fatSectorStart, rootDirSectorStart;
-
-// Storage for SECTOR read from SD CARD
-unsigned char sdCardSector[512];
-
-// SPI_SDCARD_READMBR
-//      Function exposed to BIOS to retrieve FAT16 partition details, FAT16 Entry Table, FAT16 Root Directory
-//      MBR = Master Boot Record - Sector 0x00000000 on SD CARD - Contains Partition 1 details at 0x1be
-//
-// FIXME only checks partition 1 out of 4
-//
-//      Return 0 success, 1 failure
-//
-// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
-unsigned char spi_sdcard_readMBR(void)
-{
-    int i, n;
-
-    // Read Sector 0x00000000    
-    printf("Reading MBR\n");
-    if( readSector(0x00000000, sdCardSector)==SUCCESS ) {
-        // Copy Partition 1 Entry from byte 0x1be
-        // FIXME should check 0x55 0xaa at end of sector
-        memcpy(&sdCardPartition, &sdCardSector[0x1be], sizeof(PartitionTable));
-        
-        // Check Partition 1 is valid, FIRST_BYTE=0x00 or 0x80
-        // Check Partition 1 has type 4, 6 or 14 (FAT16 of various sizes)
-        printf("Partition 1 Information: Active=0x%02x, Type=0x%02x, LBAStart=0x%08x\n", sdCardPartition.first_byte, sdCardPartition.partition_type, sdCardPartition.start_sector);
-        if( (sdCardPartition.first_byte!=0x80) && (sdCardPartition.first_byte!=0x00) ) {
-            printf("Partition 1 Not Valid\n");
-            return FAILURE;
-        }
-        if( (sdCardPartition.partition_type==4) || (sdCardPartition.partition_type==6) || (sdCardPartition.partition_type==14) ) {
-            printf("Partition 1 is FAT16\n");
-        }
-        else {
-            printf("Partition 1 Not FAT16\n");
-            return FAILURE;
-        }
-    }
-    else {
-        printf("Failed to read MBR\n");
-        return FAILURE;
-    }
-
-    // Read Parition 1 Boot Sector - Found from Partion Table
-    printf("\nRead FAT16 Boot Sector\n");
-    if( readSector(sdCardPartition.start_sector, sdCardSector)==SUCCESS ) {
-        memcpy(&sdCardFatBootSector, &sdCardSector, sizeof(Fat16BootSector));
-    }
-    else {
-        printf("Failed to read FAT16 Boot Sector\n");
-        return FAILURE;
-    }
-    
-    // Print details of Parition 1
-    printf("  Jump Code:              0x%02x 0x%02x 0x%02x\n",sdCardFatBootSector.jmp[0],sdCardFatBootSector.jmp[1],sdCardFatBootSector.jmp[2]);
-    printf("  OEM Code:               [");
-    for(n=0; n<8; n++)
-        printf("%c",sdCardFatBootSector.oem[n]);
-    printf("]\n");
-    printf("  Sector Size:            %d\n",sdCardFatBootSector.sector_size);
-    printf("  Sectors Per Cluster:    %d\n",sdCardFatBootSector.sectors_per_cluster);
-    printf("  Reserved Sectors:       %d\n",sdCardFatBootSector.reserved_sectors);
-    printf("  Number of Fats:         %d\n",sdCardFatBootSector.number_of_fats);
-    printf("  Root Dir Entries:       %d\n",sdCardFatBootSector.root_dir_entries);
-    printf("  Total Sectors Short:    %d\n",sdCardFatBootSector.total_sectors_short);
-    printf("  Media Descriptor:       0x%02x\n",sdCardFatBootSector.media_descriptor);
-    printf("  Fat Size Sectors:       %d\n",sdCardFatBootSector.fat_size_sectors);
-    printf("  Sectors Per Track:      %d\n",sdCardFatBootSector.sectors_per_track);
-    printf("  Number of Heads:        %d\n",sdCardFatBootSector.number_of_heads);
-    printf("  Hidden Sectors:         %d\n",sdCardFatBootSector.hidden_sectors);
-    printf("  Total Sectors Long:     %d\n",sdCardFatBootSector.total_sectors_long);
-    printf("  Drive Number:           0x%02x\n",sdCardFatBootSector.drive_number);
-    printf("  Current Head:           0x%02x\n",sdCardFatBootSector.current_head);
-    printf("  Boot Signature:         0x%02x\n",sdCardFatBootSector.boot_signature);
-    printf("  Volume ID:              0x%08x\n",sdCardFatBootSector.volume_id);
-    printf("  Volume Label:           [");
-    for(n=0; n<11; n++)
-        printf("%c",sdCardFatBootSector.volume_label[n]);
-    printf("]\n");
-     printf("  Volume Label:           [");
-    for(n=0; n<8; n++)
-        printf("%c",sdCardFatBootSector.fs_type[n]);
-    printf("]\n");
-    printf("  Boot Sector Signature:  0x%04x\n\n",sdCardFatBootSector.boot_sector_signature);
-
-    // Check Partition 1 is valid, not 0 length
-    if(sdCardFatBootSector.total_sectors_long==0) {
-        printf("Error reading FAT16 Boot Sector\n");
-        return FAILURE;
-    }
-    
-    // Read in FAT16 File Allocation Table, array of 16bit unsinged integers
-    // Calculate Storage from TOP of MAIN RAM
-    sdCardFatTable = (unsigned short *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors);
-    printf("sdCardFatTable = 0x%08x  Reading Fat16 Table (%d Sectors Long)\n\n",sdCardFatTable,sdCardFatBootSector.fat_size_sectors);
-    
-    // Calculate Start of FAT16 File Allocation Table (start of partition plus reserved sectors)
-    fatSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors;
-    for(n=0; n<sdCardFatBootSector.fat_size_sectors; n++) {
-        if( readSector(fatSectorStart+n, (unsigned char *)((unsigned char*)sdCardFatTable)+sdCardFatBootSector.sector_size*n)==FAILURE ) {
-            printf("Error reading FAT16 table - sector %d\n",n);
-            return FAILURE;
-        }
-    }
-    
-    // Read in FAT16 Root Directory
-    // Calculate Storage from TOP of MAIN RAM
-    sdCardFat16RootDir= (Fat16Entry *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors-sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry));
-    printf("sdCardFat16RootDir = 0x%08x  Reading Root Directory (%d Sectors Long)\n\n",sdCardFat16RootDir,sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry)/sdCardFatBootSector.sector_size);
-    
-    // Calculate Start of FAT ROOT DIRECTORY (start of partition plues reserved sectors plus size of File Allocation Table(s))
-    rootDirSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors+sdCardFatBootSector.number_of_fats*sdCardFatBootSector.fat_size_sectors;
-    for(n=0; n<sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry)/sdCardFatBootSector.sector_size; n++) {
-        if( readSector(rootDirSectorStart+n, (unsigned char *)(sdCardFatBootSector.sector_size*n+(unsigned char *)(sdCardFat16RootDir)))==FAILURE ) {
-            printf("Error reading Root Dir - sector %d\n",n);
-            return FAILURE;
-        }
-    }
-
-    // Print out Root Directory
-    // Alternates between valid and invalid directory entries for SIMPLE 8+3 file names, extended filenames in other entries
-    // Only print valid characters
-    printf("\nRoot Directory\n");
-    for(n=0; n<sdCardFatBootSector.root_dir_entries; n++) {
-        if( (sdCardFat16RootDir[n].filename[0]!=0) && (sdCardFat16RootDir[n].file_size>0)) {
-            printf("  File %d [",n);
-            for( i=0; i<8; i++) {
-                if( (sdCardFat16RootDir[n].filename[i]>31) && (sdCardFat16RootDir[n].filename[i]<127) )
-                    printf("%c",sdCardFat16RootDir[n].filename[i]);
-                else
-                    printf(" ");
-            }
-            printf(".");
-            for( i=0; i<3; i++) {
-                 if( (sdCardFat16RootDir[n].ext[i]>31) && (sdCardFat16RootDir[n].ext[i]<127) )
-                    printf("%c",sdCardFat16RootDir[n].ext[i]);
-                else
-                    printf(" ");
-            }
-            printf("] @ Cluster %d for %d bytes\n",sdCardFat16RootDir[n].starting_cluster,sdCardFat16RootDir[n].file_size);
-        }
-    }
-    
-    printf("\n");
-    return SUCCESS;
-}
-
-// SPI_SDCARD_READFILE
-//      Function exposed to BIOS to retrieve FILENAME+EXT into ADDRESS
-// 
-// FIXME only checks UPPERCASE 8+3 filenames
-//
-//      Return 0 success, 1 failure
-//
-// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
-unsigned char spi_sdcard_readFile(char *filename, char *ext, unsigned long address)
-{
-    int i, n, sector;
-    unsigned short fileClusterStart;
-    unsigned long fileLength, bytesRemaining, clusterSectorStart;
-    unsigned short nameMatch;
-    printf("Reading File [%s.%s] into 0x%08x : ",filename, ext, address);
-
-    // Find FILENAME+EXT in Root Directory
-    // Indicate FILE found by setting the starting cluster number
-    fileClusterStart=0; n=0;
-    while( (fileClusterStart==0) && (n<sdCardFatBootSector.root_dir_entries) ) {
-        nameMatch=0;
-        if( sdCardFat16RootDir[n].filename[0]!=0 ) {
-            nameMatch=1;
-            for(i=0; i<strlen(filename); i++)
-                if(sdCardFat16RootDir[n].filename[i]!=filename[i]) nameMatch=0;
-            for(i=0; i<strlen(ext); i++)
-                if(sdCardFat16RootDir[n].ext[i]!=ext[i]) nameMatch=0;
-        }
-
-        if(nameMatch==1) {
-            fileClusterStart=sdCardFat16RootDir[n].starting_cluster;
-            fileLength=sdCardFat16RootDir[n].file_size;
-        } else {
-            n++;
-        }
-    }
-    
-    // If starting cluster number is still 0 then file not found
-    if(fileClusterStart==0) {
-        printf("File not found\n");
-        return FAILURE;
-    }
-
-    printf("File starts at Cluster %d length %d\n",fileClusterStart,fileLength);
-
-    // ZERO Length file are automatically assumed to have been read SUCCESS
-    if( fileLength==0 ) return SUCCESS;
-    
-    // Read each cluster sector by sector, i being number of clusters
-    bytesRemaining=fileLength;
-    printf("Clusters: ");
-    // Calculate number of clusters (always >1)
-    for(i=0; i<1+((fileLength/sdCardFatBootSector.sectors_per_cluster)/sdCardFatBootSector.sector_size); i++) {
-        printf("%d ",fileClusterStart);
-
-        // Locate start of cluster on SD CARD and read appropraite number of sectors
-        clusterSectorStart=rootDirSectorStart+(fileClusterStart-1)*sdCardFatBootSector.sectors_per_cluster;
-        for(sector=0; sector<sdCardFatBootSector.sectors_per_cluster; sector++) {
-            // Read Sector from SD CARD
-            // If whole sector to be read, read directly into memory
-            // Otherwise, read to sdCardSector buffer and transfer appropriate number of bytes
-            if(bytesRemaining>sdCardFatBootSector.sector_size) {
-                if( readSector(clusterSectorStart+sector,(unsigned char *)address) == FAILURE ) {
-                    printf("Read Error\n");
-                    return FAILURE;
-                }
-                bytesRemaining=bytesRemaining-sdCardFatBootSector.sector_size;
-                address=address+sdCardFatBootSector.sector_size;
-            } else {
-                if( readSector(clusterSectorStart+sector,sdCardSector) == FAILURE ) {
-                    printf("Read Error\n");
-                    return FAILURE;
-                }
-                memcpy((unsigned char *)address, sdCardSector, bytesRemaining);
-                bytesRemaining=0;
-            }
-        }
-        
-        // Move to next cluster
-        fileClusterStart=sdCardFatTable[fileClusterStart];
-    }
-    printf("\n\n");
-    return SUCCESS;
-}
-#endif
diff --git a/litex/soc/software/libbase/spisdcard.c b/litex/soc/software/libbase/spisdcard.c
new file mode 100644 (file)
index 0000000..eafbf2f
--- /dev/null
@@ -0,0 +1,585 @@
+// SD CARD bitbanging code for loading files from a FAT16 forrmatted partition into memory
+//
+// Code is known to work on a de10nano with MiSTer SDRAM and IO Boards - IO Board has a secondary SD CARD interface connected to GPIO pins
+// SPI signals CLK, CS and MOSI are configured as GPIO output pins, and MISO is configued as GPIO input pins
+//
+// Protocol details developed from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/
+//
+// FAT16 details developed from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ and https://codeandlife.com/2012/04/07/simple-fat-and-sd-tutorial-part-2/
+
+// Import LiteX SoC details that are generated each time the SoC is compiled for the FPGA
+//      csr defines the SPI Control registers
+//      soc defines the clock CONFIG_CLOCK_FREQUENCY (50MHz for the VexRiscV processor on the MiSTer FPGA
+//      mem defines the addresses for the SDRAM MAIN_RAM_BASE and MAIN_RAM_SIZE
+#include <generated/csr.h>
+#include <generated/soc.h>
+#include <generated/mem.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+
+#ifdef CSR_SPISDCARD_BASE
+// Import prototypes for the functions
+#include <spisdcard.h>
+
+// SPI
+//      cs line - high to indicate DESELECT
+//              - low to indicate SELECT
+#define CS_HIGH         0x00
+#define CS_LOW          0x01
+
+//      control register values
+//      onebyte to indicate 1 byte being transferred
+//      spi_start to indicate START of transfer
+//      spi_done to indicate transfer DONE
+#define ONEBYTE         0x0800
+#define SPI_START       0x01
+#define SPI_DONE        0x01
+
+// Return values
+#define SUCCESS         0x01
+#define FAILURE         0x00
+
+// spi_write_byte
+//      Send a BYTE (8bits) to the SD CARD
+//      Seqeunce
+//          Set MOSI
+//          Set START bit and LENGTH=8
+//          Await DONE
+//
+//      No return values
+//
+//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
+void spi_write_byte(unsigned char char_to_send);
+void spi_write_byte(unsigned char char_to_send)
+{
+    // Place data into MOSI register
+    // Pulse the START bit and set LENGTH=8
+    spisdcard_mosi_write(char_to_send);
+    spisdcard_control_write(ONEBYTE | SPI_START);
+
+    // Wait for DONE
+    while( (spisdcard_status_read() != SPI_DONE)) {}
+
+    // Signal end of transfer
+    spisdcard_control_write( 0x00 );
+}
+
+
+// spi_read_rbyte
+//      Read a command response from the SD CARD - Equivalent to and R1 response or first byte of an R7 response
+//      Sequence
+//          Read MISO
+//          If MSB != 0 then send dsummy byte and re-read MISO
+//
+//      Return value is the response from the SD CARD
+//          If the MSB is not 0, this would represent an ERROR
+//          Calling function to determine if the correct response has been received
+//
+//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
+unsigned char spi_read_rbyte(void);
+unsigned char spi_read_rbyte(void)
+{
+    int timeout=32;
+    unsigned char r=0;
+
+    // Check if MISO is 0x0xxxxxxx as MSB=0 indicates valid response
+    r = spisdcard_miso_read();
+    while( ((r&0x80)!=0) && timeout>0) {
+        spisdcard_mosi_write( 0xff );
+        spisdcard_control_write(ONEBYTE | SPI_START);
+        while( (spisdcard_status_read() != SPI_DONE)) {}
+        r = spisdcard_miso_read();
+        spisdcard_control_write( 0x00 );
+        timeout--;
+    }
+
+//    printf("Done\n");
+    return r;
+}
+
+// spi_read_byte
+//      Sequence
+//          Send dummy byte
+//          Read MISO
+//
+//      Read subsequenct bytes from the SD CARD - MSB first
+//      NOTE different from the spi_read_rbyte as no need to await an intial 0 bit as card is already responsing
+//      Used to read additional response bytes, or data bytes from the SD CARD
+//
+//      Return value is the byte read
+//          NOTE no error status as assumed bytes are read via CLK pulses
+//
+//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
+unsigned char spi_read_byte(void);
+unsigned char spi_read_byte(void)
+{
+    unsigned char r=0;
+
+    spi_write_byte( 0xff );
+    r = spisdcard_miso_read();
+
+    return r;
+}
+
+//  SETSPIMODE
+//      Signal the SD CARD to switch to SPI mode
+//      Pulse the CLK line HIGH/LOW repeatedly with MOSI and CS_N HIGH
+//      Drop CS_N LOW and pulse the CLK
+//      Check MISO for HIGH
+//      Return 0 success, 1 failure
+//
+//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Initializing the SD Card"
+unsigned char spi_setspimode(void);
+unsigned char spi_setspimode(void)
+{
+    unsigned int i, r, timeout=32;
+
+    // Initialise SPI mode
+    // set CS to HIGH
+    // Send pulses
+     do {
+        // set CS HIGH and send pulses
+        spisdcard_cs_write(CS_HIGH);
+         for (i=0; i<10; i++) {
+            spi_write_byte( 0xff );
+        }
+
+        // set CS LOW and send pulses
+        spisdcard_cs_write(CS_LOW);
+        r = spi_read_rbyte();
+
+        timeout--;
+    } while ( (timeout>0) && (r==0) );
+
+    if(timeout==0) return FAILURE;
+
+    return SUCCESS;
+}
+
+// SPI_SDCARD_GOIDLE
+//      Function exposed to BIOS to initialise SPI mode
+//
+//      Sequence
+//          Set 100KHz timer mode
+//          Send CLK pulses to set SD CARD to SPI mode
+//          Send CMD0 - Software RESET - force SD CARD IDLE
+//          Send CMD8 - Check SD CARD type
+//          Send CMD55+ACMD41 - Force SD CARD READY
+//          Send CMD58 - Read SD CARD OCR (status register)
+//          Send CMD16 - Set SD CARD block size to 512 - Sector Size for the SD CARD
+//      NOTE - Each command is prefixed with a dummy set of CLK pulses to prepare SD CARD to receive a command
+//      Return 0 success, 1 failure
+//
+//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Initializing the SD Card"
+unsigned char spi_sdcard_goidle(void)
+{
+    unsigned char r;                                                                                                                // Response from SD CARD
+    int i, timeout;                                                                                                                 // TIMEOUT loop to send CMD55+ACMD41 repeatedly
+
+    r = spi_setspimode();                                                                                                               // Set SD CARD to SPI mode
+    if( r != 0x01 ) return FAILURE;
+
+    // CMD0 - Software reset - SD CARD IDLE
+    // Command Sequence is DUMMY=0xff CMD0=0x40 0x00 0x00 0x00 0x00 CRC=0x95
+    // Expected R1 response is 0x01 indicating SD CARD is IDLE
+    spi_write_byte( 0xff ); spi_write_byte( 0x40 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x95 );
+    r = spi_read_rbyte();
+    if(r!=0x01) return FAILURE;
+
+    // CMD8 - Check SD CARD type
+    // Command sequence is DUMMY=0xff CMD8=0x48 0x00 0x00 0x01 0xaa CRC=0x87
+    // Expected R7 response is 0x01 followed by 0x00 0x00 0x01 0xaa (these trailing 4 bytes not currently checked)
+    spi_write_byte( 0xff ); spi_write_byte( 0x48 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x01 ); spi_write_byte( 0xaa ); spi_write_byte( 0x87 );
+    r = spi_read_rbyte();
+    if(r!=0x01) return FAILURE;
+    // Receive the trailing 4 bytes for R7 response - FIXME should check for 0x00 0x00 0x01 0xaa
+    for(i=0; i<4; i++)
+        r=spi_read_byte();
+
+    // CMD55+ACMD41 - Force SD CARD READY - prepare card for reading/writing
+    // Command sequence is CMD55 followed by ACMD41
+    //      Send commands repeatedly until SD CARD indicates READY 0x00
+    // CMD55 Sequence is DUMMY=0xff CMD55=0x77 0x00 0x00 0x00 0x00 CRC=0x00
+    // ACMD41 Sequence is DUMMY=0xff ACMD41=0x69 0x40 0x00 0x00 0x00 CRC=0x00
+    // Expected R1 response is 0x00 indicating SD CARD is READY
+    timeout=32;
+    do {
+        spi_write_byte( 0xff ); spi_write_byte( 0x77 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 );
+        r = spi_read_rbyte();
+
+        spi_write_byte( 0xff ); spi_write_byte( 0x69 ); spi_write_byte( 0x40 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 );
+        r = spi_read_rbyte();
+        timeout--;
+    } while ((r != 0x00) && (timeout>0));
+    if(r!=0x00) return FAILURE;
+
+    // CMD58 - Read SD CARD OCR (status register)
+    // FIXME - Find details on expected response from CMD58 to allow accurate checking of SD CARD R3 response
+    // Command sequence is DUMMY=0xff CMD58=0x7a 0x00 0x00 0x01 0xaa CRC=0xff
+    // Expected R3 response is 0x00 OR 0x01 followed by 4 (unchecked) trailing bytes
+    spi_write_byte( 0xff ); spi_write_byte( 0x7a ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0xff );
+    r = spi_read_rbyte();
+    if(r>0x01) return FAILURE;
+    // // Receive the trailing 4 bytes for R3 response
+    for(i=0; i<4; i++)
+        r=spi_read_byte();
+
+    // CMD16 - Set SD CARD block size to 512 - Sector Size for the SD CARD
+    // Command Sequence is DUMMY=0xff (512 as unsigned long = 0x00000200) 0x00 0x00 0x02 0x00 CRC=0xff
+    // Expected R1 response is 0x00 indicating SD CARD is READY
+    spi_write_byte( 0xff ); spi_write_byte( 0x50 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x02 ); spi_write_byte( 0x00 ); spi_write_byte( 0xff );
+    r=spi_read_rbyte();
+    if(r!=0x00) return FAILURE;
+
+    return SUCCESS;
+}
+
+// READSECTOR
+//      Read a 512 byte sector from the SD CARD
+//      Given SECTORNUMBER and memory STORAGE
+//
+//      Sequence
+//          Send CMD17 - Read Block
+//          Command Sequence is DUMMY=0xff CMD17=0x51 SECTORNUMBER (32bit UNSIGNED as bits 32-25,24-17, 16-9, 8-1) CRC=0xff
+//          Wait for SD CARD to send 0x00 indicating SD CARD is processing
+//          Wait for SD CARD to send 0xfe indicating SD CARD BLOCK START
+//          Read 512 bytes
+//          Read 8 DUMMY bytes
+//      Return 0 success, 1 failure
+//
+//      Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Read/Write SD Card"
+unsigned char readSector(unsigned int sectorNumber, unsigned char *storage);
+unsigned char readSector(unsigned int sectorNumber, unsigned char *storage)
+{
+    unsigned int n,timeout;                                                                                                             // Number of bytes loop, timeout loop awaiting response bytes
+    unsigned char r;                                                                                                                    // Response bytes from SD CARD
+
+    // CMD17 - Read Block
+    // Command Sequence is DUMMY=0xff CMD17=0x51 SECTORNUMBER (32bit UNSIGNED as bits 32-25,24-17, 16-9, 8-1) CRC=0xff
+    // Expected R1 response is 0x00 indicating SD CARD is processing
+    spi_write_byte( 0xff ); spi_write_byte( 0x51 ); spi_write_byte( (sectorNumber>>24)&0xff ); spi_write_byte( (sectorNumber>>16)&0xff ); spi_write_byte( (sectorNumber>>8)&0xff ); spi_write_byte( (sectorNumber)&0xff ); spi_write_byte( 0xff );
+    r=spi_read_rbyte();
+    if( r!=0x00 ) return FAILURE;
+
+    // Await 0xfe to indicate BLOCK START
+    r=spi_read_byte();
+    timeout=16384;
+    while( (r!=0xfe) && (timeout>0) ) {
+        r=spi_read_byte();
+        timeout--;
+    }
+    if( r!=0xfe ) return FAILURE;
+
+    // Read 512 bytes into storage
+    for(n=0; n<512; n++)
+        storage[n]=spi_read_byte();
+
+    // Read 8 dummy bytes
+    for(n=0; n<8; n++)
+        r=spi_read_byte();
+
+    return SUCCESS;
+}
+
+
+// FAT16 Specific code starts here
+// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
+
+// Structure to store SD CARD partition table
+typedef struct {
+    unsigned char first_byte;
+    unsigned char start_chs[3];
+    unsigned char partition_type;
+    unsigned char end_chs[3];
+    unsigned long start_sector;
+    unsigned long length_sectors;
+} __attribute((packed)) PartitionTable;
+
+PartitionTable sdCardPartition;
+
+// Structure to store SD CARD FAT16 Boot Sector (boot code is ignored, provides layout of the FAT16 partition on the SD CARD)
+typedef struct {
+    unsigned char jmp[3];
+    char oem[8];
+    unsigned short sector_size;
+    unsigned char sectors_per_cluster;
+    unsigned short reserved_sectors;
+    unsigned char number_of_fats;
+    unsigned short root_dir_entries;
+    unsigned short total_sectors_short; // if zero, later field is used
+    unsigned char media_descriptor;
+    unsigned short fat_size_sectors;
+    unsigned short sectors_per_track;
+    unsigned short number_of_heads;
+    unsigned long hidden_sectors;
+    unsigned long total_sectors_long;
+
+    unsigned char drive_number;
+    unsigned char current_head;
+    unsigned char boot_signature;
+    unsigned long volume_id;
+    char volume_label[11];
+    char fs_type[8];
+    char boot_code[448];
+    unsigned short boot_sector_signature;
+} __attribute((packed)) Fat16BootSector;
+
+Fat16BootSector sdCardFatBootSector;
+
+// Structure to store SD CARD FAT16 Root Directory Entries
+//      Allocated to MAIN RAM - hence pointer
+typedef struct {
+    unsigned char filename[8];
+    unsigned char ext[3];
+    unsigned char attributes;
+    unsigned char reserved[10];
+    unsigned short modify_time;
+    unsigned short modify_date;
+    unsigned short starting_cluster;
+    unsigned long file_size;
+} __attribute((packed)) Fat16Entry;
+
+Fat16Entry *sdCardFat16RootDir;
+
+// Structure to store SD CARD FAT16 Entries
+//      Array of UNSIGNED SHORTS (16bit integers)
+unsigned short *sdCardFatTable;
+
+// Calculated sector numbers on the SD CARD for the FAT16 Entries and ROOT DIRECTORY
+unsigned int fatSectorStart, rootDirSectorStart;
+
+// Storage for SECTOR read from SD CARD
+unsigned char sdCardSector[512];
+
+// SPI_SDCARD_READMBR
+//      Function exposed to BIOS to retrieve FAT16 partition details, FAT16 Entry Table, FAT16 Root Directory
+//      MBR = Master Boot Record - Sector 0x00000000 on SD CARD - Contains Partition 1 details at 0x1be
+//
+// FIXME only checks partition 1 out of 4
+//
+//      Return 0 success, 1 failure
+//
+// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
+unsigned char spi_sdcard_readMBR(void)
+{
+    int i, n;
+
+    // Read Sector 0x00000000
+    printf("Reading MBR\n");
+    if( readSector(0x00000000, sdCardSector)==SUCCESS ) {
+        // Copy Partition 1 Entry from byte 0x1be
+        // FIXME should check 0x55 0xaa at end of sector
+        memcpy(&sdCardPartition, &sdCardSector[0x1be], sizeof(PartitionTable));
+
+        // Check Partition 1 is valid, FIRST_BYTE=0x00 or 0x80
+        // Check Partition 1 has type 4, 6 or 14 (FAT16 of various sizes)
+        printf("Partition 1 Information: Active=0x%02x, Type=0x%02x, LBAStart=0x%08x\n", sdCardPartition.first_byte, sdCardPartition.partition_type, sdCardPartition.start_sector);
+        if( (sdCardPartition.first_byte!=0x80) && (sdCardPartition.first_byte!=0x00) ) {
+            printf("Partition 1 Not Valid\n");
+            return FAILURE;
+        }
+        if( (sdCardPartition.partition_type==4) || (sdCardPartition.partition_type==6) || (sdCardPartition.partition_type==14) ) {
+            printf("Partition 1 is FAT16\n");
+        }
+        else {
+            printf("Partition 1 Not FAT16\n");
+            return FAILURE;
+        }
+    }
+    else {
+        printf("Failed to read MBR\n");
+        return FAILURE;
+    }
+
+    // Read Parition 1 Boot Sector - Found from Partion Table
+    printf("\nRead FAT16 Boot Sector\n");
+    if( readSector(sdCardPartition.start_sector, sdCardSector)==SUCCESS ) {
+        memcpy(&sdCardFatBootSector, &sdCardSector, sizeof(Fat16BootSector));
+    }
+    else {
+        printf("Failed to read FAT16 Boot Sector\n");
+        return FAILURE;
+    }
+
+    // Print details of Parition 1
+    printf("  Jump Code:              0x%02x 0x%02x 0x%02x\n",sdCardFatBootSector.jmp[0],sdCardFatBootSector.jmp[1],sdCardFatBootSector.jmp[2]);
+    printf("  OEM Code:               [");
+    for(n=0; n<8; n++)
+        printf("%c",sdCardFatBootSector.oem[n]);
+    printf("]\n");
+    printf("  Sector Size:            %d\n",sdCardFatBootSector.sector_size);
+    printf("  Sectors Per Cluster:    %d\n",sdCardFatBootSector.sectors_per_cluster);
+    printf("  Reserved Sectors:       %d\n",sdCardFatBootSector.reserved_sectors);
+    printf("  Number of Fats:         %d\n",sdCardFatBootSector.number_of_fats);
+    printf("  Root Dir Entries:       %d\n",sdCardFatBootSector.root_dir_entries);
+    printf("  Total Sectors Short:    %d\n",sdCardFatBootSector.total_sectors_short);
+    printf("  Media Descriptor:       0x%02x\n",sdCardFatBootSector.media_descriptor);
+    printf("  Fat Size Sectors:       %d\n",sdCardFatBootSector.fat_size_sectors);
+    printf("  Sectors Per Track:      %d\n",sdCardFatBootSector.sectors_per_track);
+    printf("  Number of Heads:        %d\n",sdCardFatBootSector.number_of_heads);
+    printf("  Hidden Sectors:         %d\n",sdCardFatBootSector.hidden_sectors);
+    printf("  Total Sectors Long:     %d\n",sdCardFatBootSector.total_sectors_long);
+    printf("  Drive Number:           0x%02x\n",sdCardFatBootSector.drive_number);
+    printf("  Current Head:           0x%02x\n",sdCardFatBootSector.current_head);
+    printf("  Boot Signature:         0x%02x\n",sdCardFatBootSector.boot_signature);
+    printf("  Volume ID:              0x%08x\n",sdCardFatBootSector.volume_id);
+    printf("  Volume Label:           [");
+    for(n=0; n<11; n++)
+        printf("%c",sdCardFatBootSector.volume_label[n]);
+    printf("]\n");
+     printf("  Volume Label:           [");
+    for(n=0; n<8; n++)
+        printf("%c",sdCardFatBootSector.fs_type[n]);
+    printf("]\n");
+    printf("  Boot Sector Signature:  0x%04x\n\n",sdCardFatBootSector.boot_sector_signature);
+
+    // Check Partition 1 is valid, not 0 length
+    if(sdCardFatBootSector.total_sectors_long==0) {
+        printf("Error reading FAT16 Boot Sector\n");
+        return FAILURE;
+    }
+
+    // Read in FAT16 File Allocation Table, array of 16bit unsinged integers
+    // Calculate Storage from TOP of MAIN RAM
+    sdCardFatTable = (unsigned short *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors);
+    printf("sdCardFatTable = 0x%08x  Reading Fat16 Table (%d Sectors Long)\n\n",sdCardFatTable,sdCardFatBootSector.fat_size_sectors);
+
+    // Calculate Start of FAT16 File Allocation Table (start of partition plus reserved sectors)
+    fatSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors;
+    for(n=0; n<sdCardFatBootSector.fat_size_sectors; n++) {
+        if( readSector(fatSectorStart+n, (unsigned char *)((unsigned char*)sdCardFatTable)+sdCardFatBootSector.sector_size*n)==FAILURE ) {
+            printf("Error reading FAT16 table - sector %d\n",n);
+            return FAILURE;
+        }
+    }
+
+    // Read in FAT16 Root Directory
+    // Calculate Storage from TOP of MAIN RAM
+    sdCardFat16RootDir= (Fat16Entry *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors-sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry));
+    printf("sdCardFat16RootDir = 0x%08x  Reading Root Directory (%d Sectors Long)\n\n",sdCardFat16RootDir,sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry)/sdCardFatBootSector.sector_size);
+
+    // Calculate Start of FAT ROOT DIRECTORY (start of partition plues reserved sectors plus size of File Allocation Table(s))
+    rootDirSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors+sdCardFatBootSector.number_of_fats*sdCardFatBootSector.fat_size_sectors;
+    for(n=0; n<sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry)/sdCardFatBootSector.sector_size; n++) {
+        if( readSector(rootDirSectorStart+n, (unsigned char *)(sdCardFatBootSector.sector_size*n+(unsigned char *)(sdCardFat16RootDir)))==FAILURE ) {
+            printf("Error reading Root Dir - sector %d\n",n);
+            return FAILURE;
+        }
+    }
+
+    // Print out Root Directory
+    // Alternates between valid and invalid directory entries for SIMPLE 8+3 file names, extended filenames in other entries
+    // Only print valid characters
+    printf("\nRoot Directory\n");
+    for(n=0; n<sdCardFatBootSector.root_dir_entries; n++) {
+        if( (sdCardFat16RootDir[n].filename[0]!=0) && (sdCardFat16RootDir[n].file_size>0)) {
+            printf("  File %d [",n);
+            for( i=0; i<8; i++) {
+                if( (sdCardFat16RootDir[n].filename[i]>31) && (sdCardFat16RootDir[n].filename[i]<127) )
+                    printf("%c",sdCardFat16RootDir[n].filename[i]);
+                else
+                    printf(" ");
+            }
+            printf(".");
+            for( i=0; i<3; i++) {
+                 if( (sdCardFat16RootDir[n].ext[i]>31) && (sdCardFat16RootDir[n].ext[i]<127) )
+                    printf("%c",sdCardFat16RootDir[n].ext[i]);
+                else
+                    printf(" ");
+            }
+            printf("] @ Cluster %d for %d bytes\n",sdCardFat16RootDir[n].starting_cluster,sdCardFat16RootDir[n].file_size);
+        }
+    }
+
+    printf("\n");
+    return SUCCESS;
+}
+
+// SPI_SDCARD_READFILE
+//      Function exposed to BIOS to retrieve FILENAME+EXT into ADDRESS
+//
+// FIXME only checks UPPERCASE 8+3 filenames
+//
+//      Return 0 success, 1 failure
+//
+// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
+unsigned char spi_sdcard_readFile(char *filename, char *ext, unsigned long address)
+{
+    int i, n, sector;
+    unsigned short fileClusterStart;
+    unsigned long fileLength, bytesRemaining, clusterSectorStart;
+    unsigned short nameMatch;
+    printf("Reading File [%s.%s] into 0x%08x : ",filename, ext, address);
+
+    // Find FILENAME+EXT in Root Directory
+    // Indicate FILE found by setting the starting cluster number
+    fileClusterStart=0; n=0;
+    while( (fileClusterStart==0) && (n<sdCardFatBootSector.root_dir_entries) ) {
+        nameMatch=0;
+        if( sdCardFat16RootDir[n].filename[0]!=0 ) {
+            nameMatch=1;
+            for(i=0; i<strlen(filename); i++)
+                if(sdCardFat16RootDir[n].filename[i]!=filename[i]) nameMatch=0;
+            for(i=0; i<strlen(ext); i++)
+                if(sdCardFat16RootDir[n].ext[i]!=ext[i]) nameMatch=0;
+        }
+
+        if(nameMatch==1) {
+            fileClusterStart=sdCardFat16RootDir[n].starting_cluster;
+            fileLength=sdCardFat16RootDir[n].file_size;
+        } else {
+            n++;
+        }
+    }
+
+    // If starting cluster number is still 0 then file not found
+    if(fileClusterStart==0) {
+        printf("File not found\n");
+        return FAILURE;
+    }
+
+    printf("File starts at Cluster %d length %d\n",fileClusterStart,fileLength);
+
+    // ZERO Length file are automatically assumed to have been read SUCCESS
+    if( fileLength==0 ) return SUCCESS;
+
+    // Read each cluster sector by sector, i being number of clusters
+    bytesRemaining=fileLength;
+    printf("Clusters: ");
+    // Calculate number of clusters (always >1)
+    for(i=0; i<1+((fileLength/sdCardFatBootSector.sectors_per_cluster)/sdCardFatBootSector.sector_size); i++) {
+        printf("%d ",fileClusterStart);
+
+        // Locate start of cluster on SD CARD and read appropraite number of sectors
+        clusterSectorStart=rootDirSectorStart+(fileClusterStart-1)*sdCardFatBootSector.sectors_per_cluster;
+        for(sector=0; sector<sdCardFatBootSector.sectors_per_cluster; sector++) {
+            // Read Sector from SD CARD
+            // If whole sector to be read, read directly into memory
+            // Otherwise, read to sdCardSector buffer and transfer appropriate number of bytes
+            if(bytesRemaining>sdCardFatBootSector.sector_size) {
+                if( readSector(clusterSectorStart+sector,(unsigned char *)address) == FAILURE ) {
+                    printf("Read Error\n");
+                    return FAILURE;
+                }
+                bytesRemaining=bytesRemaining-sdCardFatBootSector.sector_size;
+                address=address+sdCardFatBootSector.sector_size;
+            } else {
+                if( readSector(clusterSectorStart+sector,sdCardSector) == FAILURE ) {
+                    printf("Read Error\n");
+                    return FAILURE;
+                }
+                memcpy((unsigned char *)address, sdCardSector, bytesRemaining);
+                bytesRemaining=0;
+            }
+        }
+
+        // Move to next cluster
+        fileClusterStart=sdCardFatTable[fileClusterStart];
+    }
+    printf("\n\n");
+    return SUCCESS;
+}
+#endif