' KokkeKat FAT-free SD library code for SDSC, SDHC, and SDXC - INCLUDE at the end of your code
' Written by Niclas Arndt, Stockholm, Sweden, 2011.05.06
' License terms apply, as stated in the application note
#if Sdinitdetail = 1 ' Minimum detail
Sdinit:
Sdstatus = 9 ' Minimum initialization detail unspecified error 2
Set Sd_cs ' Pull CS high
Sdcommand = &HFF ' Send at least 74 clock cycles with cs high
For Sdtempb = 1 To 74
Gosub Sdcommand1
Next Sdtempb
Reset Sd_cs
Gosub Sdcommand1 ' Send 8 additional clock cycles with cs low
Sdcommand = &H40 ' Send CMD0: &H40 &H00 &H00 &H00 &H00 &H95
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &H95
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) = &B00000001 Then ' CMD0 was successful
Sdcommand = &HFF ' Send 8 additional clock cycles with cs low
Gosub Sdcommand1 ' The SanDisk Extreme III 4GB class 6 requires this
Sdcommand = &H48 ' Send CMD8: &H48 &H00 &H00 &H01 &HAA &H87
Gosub Sdcommand1
Sdcommand = &H00 ' If the response to CMD8 is the same, we have a v2.0 compliant card
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &H01 ' xxxx0001 specifies voltage range 2.7-3.6V
Gosub Sdcommand1
Sdcommand = &HAA ' Check pattern 10101010 as recommended
Gosub Sdcommand1
Sdcommand = &H87
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) <> 255 Then ' No timeout in CMD8
If Sdresponse(1).2 = 1 Then
Sdcardtype = 1 ' R1 illegal command in this case means v1.x SD card
Else
Sdcardtype = 2 ' v2+
Gosub Sdresponse2_5 ' Read the remaining 5 bytes (The first byte has already been checked and the last byte is only sent with CRC enabled)
If Sdresponse(1) <> &B00000001 Or Sdresponse(2) <> &H00 Or Sdresponse(3) <> &H00 Or Sdresponse(4) <> &H01 Or Sdresponse(5) <> &HAA Then ' Check if the CMD8 R7 response bytes 2-5 are identical to the command
Sdstatus = 8 ' Minimum Initialization Detail Unspecified Error 1
End If
End If
' Sdstatus = 0 & Sdcardtype = 1 -> v1.x
' Sdstatus = 0 & Sdcardtype = 2 -> v2+
If Sdstatus = 9 Then ' Correct response after CMD8
Sdcommand = &HFF ' Send 8 additional clock cycles with cs low
Gosub Sdcommand1 ' The SanDisk Extreme III 4GB class 6 requires this
Sdcommand = &H7A ' Send the first CMD58: &H7A &H00 &H00 &H00 &H00 &HFD
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &HFD
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) <> 255 Then ' No timeout after CMD58
Gosub Sdresponse2_5 ' Check OCR register response in bytes 2 & 3
If Sdresponse(1) = &B00000001 And Sdresponse(3) = 255 And Sdresponse(4).7 = 1 Then ' There are 9 voltage ranges 2.7 - 3.6V. All must be supported according to spec. OCR bit 15-23 must be high.
' The SD card is in a valid supply voltage
Sdtempw = 0
Do ' Send <= 64k times CMD55+CMD41
Sdcommand = &HFF ' Send 8 additional clock cycles with cs low
Gosub Sdcommand1
Sdcommand = &H77 ' Send CMD55 (required before all ACMD commands): &H77 &H00 &H00 &H00 &H00 &H65
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &H65
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) <> 255 Then ' No timeout after CMD55
Set Sd_cs ' Set CS, clock out 8 bits and reset CS
Sdcommand = &HFF
Gosub Sdcommand1
Reset Sd_cs
Sdcommand = &H69 ' Send CMD41: &H69 &H00 &H00 &H00 &H00 &HE5 ' SDSC compatible card (HDS = 0)
Gosub Sdcommand1 ' Send CMD41: &H69 &H40 &H00 &H00 &H00 &H77 ' SDHC / SDXC compatible card (HDS = 1)
If Sdcardtype = 1 Then ' SDSC compatible card (HDS = 0)
Sdcommand = &H00
Else ' SDHC / SDXC compatible card (HDS = 1)
Sdcommand = &H40
End If
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
If Sdcardtype = 1 Then ' SDSC compatible card (HDS = 0)
Sdcommand = &HE5
Else ' SDHC / SDXC compatible card (HDS = 1)
Sdcommand = &H77
End If
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
End If
Incr Sdtempw
Loop Until Sdresponse(1) = 0 Or Sdtempw = &HFFFF ' The sample cards' loop counts were 62 (Kingston 4GB C2), 56 (SanDisk Extreme III 4GB C6), 49 (SanDisk v2+ 1GB), 115 (Canon 32MB v1.x), and 558 (Transcend v2+ 64GB)
If Sdresponse(1) <> 255 Then ' The SD card has accepted the host
Sdstatus = 0 ' Initialization complete
If Sdcardtype = 2 Then ' For v2+ cards, an additional CMD58 is required according to spec.
' Some cards can respond the CCS info before ACMD41 (Kingston 4GB CL2),
' but 3 of my 4 cards could only return the voltage bits in the previous response.
Sdcommand = &HFF ' Send 8 additional clock cycles with cs low
Gosub Sdcommand1
Sdcommand = &H7A ' Send second CMD58: &H7A &H00 &H00 &H00 &H00 &HFD
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &HFD
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) <> 255 Then ' No timeout in CMD8
Gosub Sdresponse2_5 ' Check OCR register response in byte 2
If Sdresponse(1) = 0 And Sdresponse(2).7 <> 0 Then
If Sdresponse(2).6 = 1 Then ' Card capacity status CCS bit. If it is 1, then Sdcardtype = 2 (previously set)
Sdcardtype = 3 ' 1 = v1.x SDSC, 2 = v2+ SDSC, 3 = v2+ SDHC/SDXC
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If ' Close CMD8
Set Sd_cs
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
' In SPI mode, speed is not guaranteed by specification, but the minimum supported 4-bit bus mode clock speed is 12,5 MHz.
' The maximum SPI speed for AVR 8-bit is 10MHz, so we set SPI to maximum speed, f osc / 2
Spsr.0 = 1 ' Double speed (results in f osc / 2)
Spcr.1 = 0 ' f osc / 4
Spcr.0 = 0
' Spcr.1 = 0 ' f osc / 16
' Spcr.0 = 1
' Spcr.1 = 1 'f osc / 64
' Spcr.0 = 0
' Spcr.1 = 1 ' f osc / 128
' Spcr.0 = 1
Return
#else ' Full detail
Sdinit:
Set Sd_cs ' Pull CS high
Sdcommand = &HFF ' Send at least 74 clock cycles with cs high
For Sdtempb = 1 To 74
Gosub Sdcommand1
Next Sdtempb
Reset Sd_cs
Gosub Sdcommand1 ' Send 8 additional clock cycles with cs low
Sdcommand = &H40 ' Send CMD0: &H40 &H00 &H00 &H00 &H00 &H95
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &H95
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) = 255 Then
Sdstatus = 1 ' Timeout in CMD0
Else
Gosub Sdcheckr1 ' Check R1 error codes
If Sdstatus = 10 Then ' If Sdstatus = 0 and no timeout, the CMD0 was successful
Sdstatus = 0 ' Remap status 10
Sdcommand = &HFF ' Send 8 additional clock cycles with cs low
Gosub Sdcommand1 ' The SanDisk Extreme III 4GB class 6 requires this
Sdcommand = &H48 ' Send CMD8: &H48 &H00 &H00 &H01 &HAA &H87
Gosub Sdcommand1
Sdcommand = &H00 ' If the response to CMD8 is the same, we have a v2.0 compliant card
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &H01 ' xxxx0001 specifies voltage range 2.7-3.6V
Gosub Sdcommand1
Sdcommand = &HAA ' Check pattern 10101010 as recommended
Gosub Sdcommand1
Sdcommand = &H87
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) = 255 Then
Sdstatus = 2 ' Timeout in CMD8
Else
Gosub Sdcheckr1 ' Check R1 error codes
If Sdstatus = 12 Then
Sdcardtype = 1 ' v1.x
Sdstatus = 10 ' This is not an error - it signifies that the card is v1.x
Else
Sdcardtype = 2 ' v2+
Gosub Sdresponse2_5 ' Read the remaining 5 bytes (the last byte is only sent with CRC enabled)
If Sdresponse(2) <> &H00 Or Sdresponse(3) <> &H00 Or Sdresponse(5) <> &HAA Then ' Check if the CMD8 R7 response bytes 2-5 are identical to the command
Sdstatus = 20 ' The first byte has already been checked, the last byte (CRC) is not sent
Elseif Sdresponse(4) <> &H01 Then
Sdstatus = 19
End If
End If
' Sdstatus = 0 & Sdcardtype = 1 -> v1.x
' Sdstatus = 0 & Sdcardtype = 2 -> v2+
If Sdstatus = 10 Then
Sdstatus = 0 ' Remap status
Sdcommand = &HFF ' Send 8 additional clock cycles with cs low
Gosub Sdcommand1 ' The SanDisk Extreme III 4GB class 6 requires this
Sdcommand = &H7A ' Send the first CMD58: &H7A &H00 &H00 &H00 &H00 &HFD
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &HFD
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) = 255 Then
Sdstatus = 3 ' Timeout in first CMD58
Else
Gosub Sdcheckr1 ' Check R1 error codes
If Sdstatus = 12 Then
If Sdcardtype = 1 Then ' v1.x
Sdstatus = 17 ' Remap error code to indicate that it is not an SD card
End If
Else
If Sdstatus = 10 Then
Sdstatus = 0 ' Remap status
Gosub Sdresponse2_5
If Sdresponse(3) <> 255 Or Sdresponse(4).7 <> 1 Then ' Check OCR register response in bytes 2 & 3
Sdstatus = 18 ' There are 9 voltage ranges 2.7 - 3.6V. All must be supported according to spec. OCR bit 15-23 must be high.
End If
If Sdstatus = 0 Then ' If sdstatus = 0, the SD card is in a valid supply voltage
Sdtempw = 0
Do
Sdcommand = &HFF ' Send 8 additional clock cycles with cs low
Gosub Sdcommand1
Sdcommand = &H77 ' Send CMD55 (required before all ACMD commands): &H77 &H00 &H00 &H00 &H00 &H65
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &H65
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) <> 255 Then
Set Sd_cs ' Set CS, clock out 8 bits and reset CS
Sdcommand = &HFF
Gosub Sdcommand1
Reset Sd_cs
Sdcommand = &H69 ' Send CMD41: &H69 &H00 &H00 &H00 &H00 &HE5 ' SDSC compatible card (HDS = 0)
Gosub Sdcommand1 ' Send CMD41: &H69 &H40 &H00 &H00 &H00 &H77 ' SDHC / SDXC compatible card (HDS = 1)
If Sdcardtype = 1 Then ' SDSC compatible card (HDS = 0)
Sdcommand = &H00
Else ' SDHC / SDXC compatible card (HDS = 1)
Sdcommand = &H40
End If
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
If Sdcardtype = 1 Then ' SDSC compatible card (HDS = 0)
Sdcommand = &HE5
Else ' SDHC / SDXC compatible card (HDS = 1)
Sdcommand = &H77
End If
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
End If
Incr Sdtempw
Loop Until Sdresponse(1) = 0 Or Sdtempw = &HFFFF ' The sample cards' loop counts were 2 (Kingston 4GB C2), 17 (SanDisk Extreme III 4GB C6), 18 (SanDisk v2+ 1GB), and 672 (Canon 32MB v1.x)
If Sdresponse(1) = 255 Then
Sdstatus = 4 ' Timeout in CMD41
Else
Gosub Sdcheckr1
If Sdstatus = 0 Then ' If sdstatus = 0, the SD card has accepted the host
If Sdcardtype = 2 Then ' For v2+ cards, an additional CMD58 is required according to spec.
' Some cards can respond the CCS info before ACMD41 (Kingston 4GB CL2),
' but 3 of my 4 cards could only return the voltage bits in the previous response.
Sdcommand = &HFF ' Send 8 additional clock cycles with cs low
Gosub Sdcommand1
Sdcommand = &H7A ' Send second CMD58: &H7A &H00 &H00 &H00 &H00 &HFD
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &HFD
Gosub Sdcommand1
Gosub Sdresponseloop ' Receive the single byte of response
If Sdresponse(1) = 255 Then
Sdstatus = 5 ' Timeout in second CMD58
Else
Gosub Sdcheckr1 ' Check R1 error codes
If Sdstatus = 0 Then
Gosub Sdresponse2_5
If Sdresponse(2).7 = 0 Then ' Check OCR register response in byte 2
Sdstatus = 22 ' SD power up busy
End If
If Sdresponse(2).6 = 1 Then ' Card capacity status CCS bit. If it is 1, then Sdcardtype = 2 (previously set)
Sdcardtype = 3 ' 1 = v1.x SDSC, 2 = v2+ SDSC, 3 = v2+ SDHC/SDXC
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If ' Close CMD8
End If ' Close CMD0
Set Sd_cs
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
' In SPI mode, speed is not guaranteed by specification, but the minimum supported 4-bit bus mode clock speed is 12,5 MHz.
' The maximum SPI speed for AVR 8-bit is 10MHz, so we set SPI to maximum speed, f osc / 2
Spsr.0 = 1 ' Double speed (results in f osc / 2)
Spcr.1 = 0 ' f osc / 4
Spcr.0 = 0
' Spcr.1 = 0 ' f osc / 16
' Spcr.0 = 1
' Spcr.1 = 1 'f osc / 64
' Spcr.0 = 0
' Spcr.1 = 1 ' f osc / 128
' Spcr.0 = 1
Return
'-------------------------------------------------------------------------------
Sdcheckr1:
If Sdresponse(1).1 = 1 Then
Sdstatus = 11 ' R1 erase reset
Elseif Sdresponse(1).2 = 1 Then
Sdstatus = 12 ' R1 illegal command
Elseif Sdresponse(1).3 = 1 Then
Sdstatus = 13 ' R1 command CRC error
Elseif Sdresponse(1).4 = 1 Then
Sdstatus = 14 ' R1 erase sequence error
Elseif Sdresponse(1).5 = 1 Then
Sdstatus = 15 ' R1 address error
Elseif Sdresponse(1).6 = 1 Then
Sdstatus = 16 ' R1 parameter error
Elseif Sdresponse(1).0 = 1 Then
Sdstatus = 10 ' R1 idle (running initialization)
End If
Return
#endif
'-------------------------------------------------------------------------------
Sdcommand1:
Spiout Sdcommand , 1
Return
'-------------------------------------------------------------------------------
Sdresponse1:
Spiin Sdresponse(1) , 1
Return
'-------------------------------------------------------------------------------
Sdresponse2_5:
Spiin Sdresponse(2) , 5
Return
'-------------------------------------------------------------------------------
Sdresponseloop:
Sdtempb = 0
Do
Gosub Sdresponse1 ' Receive the single byte of response
Incr Sdtempb
Loop Until Sdresponse(1) <> 255 Or Sdtempb = 255
Return
'-------------------------------------------------------------------------------
Sdreadsector: ' Read sector from SD
If Sdcardtype <= 2 Then
Shift Sdsectord , Left , 9 ' Byte addressing for SDSC cards (shift left 9 = multiply by 512)
End If
Reset Sd_cs
Sdcommand = &HFF ' Clock SD
Gosub Sdcommand1
Sdcommand = &H51 ' Send CMD17
Gosub Sdcommand1
Sdcommand = Sdsectorb(4)
Gosub Sdcommand1
Sdcommand = Sdsectorb(3)
Gosub Sdcommand1
Sdcommand = Sdsectorb(2)
Gosub Sdcommand1
Sdcommand = Sdsectorb(1)
Gosub Sdcommand1
Sdcommand = &HFF
Gosub Sdcommand1
Sdexpectedresponse = &H00 ' First response after CMD17
Gosub Sdseekresponse
If Sdcardtype <= 2 Then
Shift Sdsectord , Right , 9 ' Byte addressing for SDSC cards (shift right 9 = divide by 512)
End If
If Sdstatus = 0 Then
Sdexpectedresponse = &HFE ' Start token after CMD17
Gosub Sdseekresponse
If Sdstatus = 0 Then
' 2 = write unbuffered
' 1 = write buffered
' 0 = not writing
#if Sdwmode <> 1
For Sdtempw = 1 To 512 ' Read the data block into Sdbuffer
Spiin Sdbuffer(sdtempw) , 1
Next Sdtempw
#endif
#if Sdwmode = 1
If Sdreaddestination = 0 Then
For Sdtempw = 1 To 512 ' Read the data block into Sdbuffer
Spiin Sdbuffer(sdtempw) , 1
Next Sdtempw
Else
For Sdtempw = 1 To 512 ' Read the data block into Sdfatbuffer
Spiin Sdfatbuffer(sdtempw) , 1
Next Sdtempw
End If
#endif
Gosub Sdresponse1 ' Read dummy CRC to conclude data block
Gosub Sdresponse1
End If
End If
Set Sd_cs
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
Return
'-------------------------------------------------------------------------------
Sdseekresponse: ' Wait for Sdexpectedresponse from SD or timeout
Sdtempw = 0
Do
Gosub Sdresponse1
Incr Sdtempw
Loop Until Sdresponse(1) = Sdexpectedresponse Or Sdtempw = &HFFFF
If Sdresponse(1) <> Sdexpectedresponse Then
Sdstatus = 7 ' Timeout
End If
Return
'-------------------------------------------------------------------------------
#if Sdwmode > 0
Sdwritesector: ' Write sector to SD
If Sdcardtype <= 2 Then
Shift Sdsectord , Left , 9 ' Byte addressing for SDSC cards (shift left 9 = multiply by 512)
End If
Reset Sd_cs
Sdcommand = &HFF ' Clock SD to prepare for new command
Gosub Sdcommand1
Sdcommand = &H58 ' Send CMD24
Gosub Sdcommand1
Sdcommand = Sdsectorb(4)
Gosub Sdcommand1
Sdcommand = Sdsectorb(3)
Gosub Sdcommand1
Sdcommand = Sdsectorb(2)
Gosub Sdcommand1
Sdcommand = Sdsectorb(1)
Gosub Sdcommand1
Sdcommand = &HFF
Gosub Sdcommand1
Sdexpectedresponse = &H00 ' First response after CMD24
Gosub Sdseekresponse
If Sdcardtype <= 2 Then
Shift Sdsectord , Right , 9 ' Byte addressing for SDSC cards (shift right 9 = divide by 512)
End If
If Sdstatus = 0 Then
Sdcommand = &HFE ' Start block token after CMD24
Gosub Sdcommand1
' 2 = write unbuffered
' 1 = write buffered
' 0 = not writing
#if Sdwmode = 1
If Sdwriteorigin = 0 Then ' Write the data block from Sdbuffer
For Sdtempw = 1 To 512
Spiout Sdbuffer(sdtempw) , 1
Next Sdtempw
Else ' Write the data block from Sdfatbuffer
For Sdtempw = 1 To 512
Spiout Sdfatbuffer(sdtempw) , 1
Next Sdtempw
End If
#else
For Sdtempw = 1 To 512 ' Write the data block from Sdbuffer
Spiout Sdbuffer(sdtempw) , 1
Next Sdtempw
' Sdtempw = 1
' Do
' Spiout Sdbuffer(Sdtempw) , 128
' Sdtempw = Sdtempw + 128 ' Max 255 bytes per turn
' Loop Until Sdtempw = 513
#endif
Sdcommand = &HFF ' Write dummy CRC to conclude data block
Gosub Sdcommand1
Gosub Sdcommand1
Sdtempw = 0
Do ' Wait for and receive data response token
Gosub Sdresponse1
Incr Sdtempw
Loop Until Sdresponse(1) <> &HFF Or Sdtempw = 512
Sdtempb = Sdresponse(1) And &H0F
Select Case Sdtempb
Case &H05 : Sdstatus = 0 ' Data accepted
Case &H0B : Sdstatus = 41 ' Data rejected due to a CRC error
Case &H0D : Sdstatus = 42 ' Data rejected due to a Write Error
End Select
If Sdstatus = 0 Then ' Wait until card is no longer busy
Sdexpectedresponse = &HFF
Gosub Sdseekresponse
End If
End If
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
Set Sd_cs
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
Return
#endif
'-------------------------------------------------------------------------------
#if Sdfsactive = 1
Sdinitfs:
Sdsectord = 0 ' Read the boot sector
Gosub Sdreadsector
Print "--> Readsector i.O = Sdstatus: " ; Sdstatus
If Sdstatus = 0 Then
' Locate the start of the BRB (Boot Record Block)
If Sdbuffer(1) = &HEB And Sdbuffer(3) = &H90 Then ' Sdsectord is already 0
Elseif Sdbuffer(1) = &HE9 Then ' Sdsectord is already 0
Else
Sdsectorb(1) = Sdbuffer(&H1c7) ' Sector number of first partition
Sdsectorb(2) = Sdbuffer(&H1c8)
Sdsectorb(3) = Sdbuffer(&H1c9)
Sdsectorb(4) = Sdbuffer(&H1ca)
Gosub Sdreadsector ' Read the boot record
End If
#if Sdusewipe = 1
Sdpartstart = Sdsectord
#endif
If Sdstatus = 0 Then
' Value Description
' 00h Unknown Or Nothing
' 01h 12-bit FAT
' 04h 16-bit FAT (Partition Smaller than 32MB)
' 05h Extended MS-DOS Partition
' 06h 16-bit FAT (Partition Larger than 32MB)
' 0Bh 32-bit FAT (Partition Up to 2048GB)
' 0Ch Same as 0BH, but uses LBA1 13h Extensions
' 0Eh Same as 06H, but uses LBA1 13h Extensions
' 0Fh Same as 05H, but uses LBA1 13h Extensions
Select Case Sdbuffer(&H1c3)
' Print
' Print "Filesystem ist: " ; Sdbuffer(&H1c3)
Print
Case &H0B To &H0C : Sdfattype = 4 : Print "FAT32" ' FAT32
Case &H04 : Sdfattype = 2 : print "FAT16" ' FAT16
Case &H06 : Sdfattype = 2 : print "FAT16" ' FAT16
Case &H0E : Sdfattype = 2 : Print "FAT16" ' FAT16
Case Else : ' My Kingston CL2 4GB SD card suddenly would only report &H72, even after formatting
If Sdbuffer(55) = 70 And Sdbuffer(56) = 65 And Sdbuffer(57) = 84 And Sdbuffer(58) = 49 And Sdbuffer(59) = 54 Then
Sdfattype = 2 : Print "FAT16" ' FAT16
Elseif Sdbuffer(83) = 70 And Sdbuffer(84) = 65 And Sdbuffer(85) = 84 And Sdbuffer(86) = 51 And Sdbuffer(87) = 50 Then
Sdfattype = 4 : Print "FAT32" ' FAT32
Else
Sdstatus = 23 : Print "FAT unbekannt" ' Not supported file system
End If
End Select
Print
If Sdstatus = 0 Then
If Sdbuffer(&H0c) = 0 And Sdbuffer(&H0d) = 2 Then ' (&H0b-c) Bytes Per Sec
Sdsecspercluster = Sdbuffer(&H0e) ' [$0d] Read Secs Per Cluster
If Sdsecspercluster > 64 Then
Sdstatus = 29 ' Unsupported number of sectors per cluster
Else
' Sdtempb represents Sdnumoffats
Sdtempb = Sdbuffer(&H11) ' [$10] Read Num of FATs
If Sdfattype = 2 Then ' FAT16
' Sdtempd represents Sdsecsperfatl
Sdtempdb(1) = Sdbuffer(&H17) ' [$16] Read Secs Per FAT
Sdtempdb(2) = Sdbuffer(&H18) ' [$17]
Sdtempdb(3) = 0
Sdtempdb(4) = 0
' Sdtempw represents Sdmaxrootentriesw
Sdtempwb(1) = Sdbuffer(&H12) ' [$11] Read maximum number of root entries (N/A for FAT32)
Sdtempwb(2) = Sdbuffer(&H13) ' [$12]
Sdsecsinroot = Sdtempw * 32 ' Calculate Sectors in Root Dir
Sdsecsinroot = Sdsecsinroot / Sdbytespersecw
Else ' FAT32
' Sdfat32rootclusterb(1) = Sdbuffer(45) ' Cluster number for the FAT32 root (normally 2 but could be higher (e.g. in case of bad cluster))
' Sdfat32rootclusterb(2) = Sdbuffer(46)
' Sdfat32rootclusterb(3) = Sdbuffer(47)
' Sdfat32rootclusterb(4) = Sdbuffer(48)
Sdtempdb(1) = Sdbuffer(45) ' Cluster number for the FAT32 root (normally 2 but could be higher (e.g. in case of bad cluster))
Sdtempdb(2) = Sdbuffer(46)
Sdtempdb(3) = Sdbuffer(47)
Sdtempdb(4) = Sdbuffer(48)
If Sdtempd = 2 Then
' Sdtempd represents Sdsecsperfatl
Sdtempdb(1) = Sdbuffer(&H25) ' [$24] Read Secs Per FAT
Sdtempdb(2) = Sdbuffer(&H26) ' [$25]
Sdtempdb(3) = Sdbuffer(&H27) ' [$26]
Sdtempdb(4) = Sdbuffer(&H28) ' [$27]
Sdsecsinroot = 0
#if Sdusefsinfo = 1
Sdfat32fsinfolocationb(1) = Sdbuffer(49) ' Sector number for the FAT32 fsinfo (normally 1)
Sdfat32fsinfolocationb(2) = Sdbuffer(50)
Sdfat32fsinfolocationw = Sdfat32fsinfolocationw + Sdsectord
#endif
Else
Sdstatus = 21 ' Unsupported FAT32 root cluster number (not 2)
End If
End If
#if Sdusewipe = 1
' First see if BPB_TotSec16 contains size:
Sdtotsecsb(1) = Sdbuffer(20) ' Read total number of sectors in the entire partition
Sdtotsecsb(2) = Sdbuffer(21)
Sdtotsecsb(3) = 0
Sdtotsecsb(4) = 0
' If not, look in BPB_TotSec32:
If Sdtotsecsd = 0 Then
Sdtotsecsb(1) = Sdbuffer(33) ' Read total number of sectors in the entire partition
Sdtotsecsb(2) = Sdbuffer(34)
Sdtotsecsb(3) = Sdbuffer(35)
Sdtotsecsb(4) = Sdbuffer(36)
End If
#endif
' Sdtempw represents Sdreservedsecsw
Sdtempwb(1) = Sdbuffer(&H0f) ' [$0e] Read Num of Reserved Sectors
Sdtempwb(2) = Sdbuffer(&H10) ' [$0f]
' Sdsectord represents Sdbootrecordl
' Sdtempw represents Sdreservedsecsw
Sdfat1location = Sdsectord + Sdtempw ' Locate FAT1
Sdfat2location = Sdfat1location + Sdtempd ' Locate FAT2 (Sdtempd represents Sdsecsperfatl)
' Sdtempb represents Sdnumoffats
' Sdtempd represents Sdsecsperfatl
Sdrootdirlocation = Sdtempb * Sdtempd ' Locate FAT16 Root Dir (Sdrootdirlocation is only relevant for FAT<32 and as a basis for the start of the data area)
' Sdrootdirlocation = Sdbootrecordl + Sdrootdirlocation
' Sdrootdirlocation = Sdreservedsecsw + Sdrootdirlocation
Sdrootdirlocation = Sdfat1location + Sdrootdirlocation
Sddataareastart = Sdrootdirlocation + Sdsecsinroot ' Locate start of data, starts with cluster 2 (where the FAT32 root directory is)
End If
Else
Sdstatus = 35 ' Unsupported sector size (not 512)
End If
End If
End If
End If
Return
'-------------------------------------------------------------------------------
Sdlocatesector:
' Input variable:
' Sdclusterd = current cluster
' Output variable:
' Sdsectord = the absolute sector number for this cluster
Sdsectord = Sdclusterd - 2
Sdsectord = Sdsectord * Sdsecspercluster
Sdsectord = Sdsectord + Sddataareastart
Return
'-------------------------------------------------------------------------------
Sdlocatenextcluster:
' Input variable:
' Sdclusterd = current cluster
' Output variable:
' Sdclusterd = next cluster
Gosub Sdcalculatefat1sector
Gosub Sdreadsector ' Read the FAT sector
If Sdstatus = 0 Then
Sdbufferpos = Sdbufferpos - 1
Sdtempw = 0
Do
Incr Sdtempw
Incr Sdbufferpos
Sdclusterb(sdtempw) = Sdbuffer(sdbufferpos)
Loop Until Sdtempw = Sdfattype
End If
Return
'-------------------------------------------------------------------------------
Sdcalculatefat1sector:
' Input variable:
' Sdclusterd = current cluster
' Output variables:
' Sdsectord = absolute FAT1 sector number
' Sdbufferpos = buffer pos in this sector
Sdtempd = Sdclusterd * Sdfattype ' Offset in bytes from sdfat1location (Sdfattype = 2 for FAT16 and 4 for FAT32)
Sdsectord = Sdtempd \ Sdbytespersecw ' Offset in sectors from sdfat1location
Sdtempd = Sdtempd Mod Sdbytespersecw ' Byte position in the resulting sector (1 too low!)
Sdbufferpos = Sdtempd + 1
Sdsectord = Sdsectord + Sdfat1location ' FAT sector for the current cluster
Return
#endif
'===============================================================================
#if Sdrmode = 1 And Sdfsactive = 1
Sdreadbyte:
' Used to read a single byte from the current file, as located by Gosub Sdfindentryindirectory or Gosub Sddirlist
' Before calling, make sure that Sdfilesized > 0, Sdstatus = 0, and Sdeof = 0
' Input variables:
' Sdclusterd = current file cluster
' Sdsecincluster = sector number in this cluster, set to 0 before the very first call
' Sdbufferpos = buffer pos in this sector
' Sdsectord = absolute sector number
' Output variables:
' Sdbyterw = the read byte
' Sdbytesread = read byte counter
' Sdeof 1 = End Of File found, else 0
' Sdstatus
If Sdsecincluster = 0 Then ' Very first call
#if Sdusedirlist = 1
Sdfilesizeb(1) = Sddirlistdirentry(29) ' Read file size in bytes
Sdfilesizeb(2) = Sddirlistdirentry(30)
Sdfilesizeb(3) = Sddirlistdirentry(31)
Sdfilesizeb(4) = Sddirlistdirentry(32)
Sdclusterb(1) = Sddirlistdirentry(27) ' Cluster number for the subdirectory we are about to enter
Sdclusterb(2) = Sddirlistdirentry(28)
Sdclusterb(3) = Sddirlistdirentry(21)
Sdclusterb(4) = Sddirlistdirentry(22)
#endif
Gosub Sdlocatesector ' Read the first sector of the cluster where the file starts
Gosub Sdreadsector
If Sdstatus = 0 Then
Sdsecincluster = 1 ' Initialize pointers and byte counter
Sdbufferpos = 1
Sdbytesread = 0
Sdeof = 0
End If
End If
If Sdbufferpos > Sdbytespersecw Then ' Have we passed the end of the sector?
Incr Sdsecincluster
If Sdsecincluster > Sdsecspercluster Then ' Have we passed the last sector of this cluster?
Gosub Sdlocatenextcluster ' Locate next cluster
Gosub Sdcheckclusterreference ' Check if it indicates EOC (in that case Sdstatus = 31)
If Sdstatus = 0 Then ' EOF has not been reached
Gosub Sdlocatesector ' Locate the sector start of the new cluster
Gosub Sdreadsector
Sdsecincluster = 1
End If
Else
Incr Sdsectord
Gosub Sdreadsector
End If
Sdbufferpos = 1
End If
If Sdstatus = 0 Then
Sdbyterw = Sdbuffer(sdbufferpos)
Incr Sdbufferpos
Incr Sdbytesread
If Sdbytesread >= Sdfilesized Then
Sdeof = 1
End If
Else
Sdeof = 1
End If
Return
#endif
'===============================================================================
#if Sdusefind = 1
Sdfindentryindirectory:
' Used to find a file, subdirectory, volume ID, or available directory entry
' Input variables:
' Sdclusterd = the cluster number for the directory in which you want to search (actual cluster or 0 for root directory)
' Sdsecincluster = 1 if you are doing your first search, otherwise the actual Sdsecincluster
' Sdbufferpos = 1 if you are doing your first search, otherwise the actual Sdbufferpos
' Sdentrynames = the file or subdirectory name
' Sddirectorymode 0 = find file, 1 = find subdirectory, 2 = find volume ID, 3 = find available directory entry, 4 find parent directory
' If Sdrwmode = 2 Or Sdrwmode = 4
' Sdreaddestination 0 = Sdbuffer(), 1 = Sdfatbuffer()
' Output variables:
' Sdstatus:
' 28 = An available directory space has been found
' 31 = EOC End OF Cluster chain found (directory)
' 32 = EOD End Of Directory marker found
' 34 = The volume ID has been found
' 47 = Parent directory pointer found
' 48 = File or subdirectory found
' Sddirectorymode = 2:
' Sdclusterd = Next cluster number (where file/subdirectory resides)
' Sdentrynames = The volume ID
' Sddirectorymode <> 2:
' Sddirclusterd = Cluster number for this directory entry
' Sddirsecincluster = Chain start sector number of the current directory cluster, 1 to Sdsecincluster
' Sddirbufferpos = Chain start directory buffer position, 1 to 512
' Sdclusterd = Next cluster number (where file/subdirectory resides)
' 2 = write unbuffered
' 1 = write buffered
' 0 = not writing
#if Sdwmode = 1
Sdreaddestination = 0 ' Only relevant for buffered writing
#endif
' Special handling of root directory
If Sdclusterd = 0 Then
If Sdfattype = 2 Then ' FAT16 root directory
Sdsectord = Sdrootdirlocation
Sdsectorend = Sdsecsinroot
Else
Sdclusterd = Sdfat32rootclusterd ' FAT32 root directory remapped to cluster 2
End If
End If
' If not in FAT16 root directory, locate the first directory sector
If Sdclusterd > 0 Then
Sdsectorend = Sdsecspercluster
Gosub Sdlocatesector ' Calculate the first sector in Sdclusterd and place in Sdsectord
End If
Do
Do ' Loop through this cluster
Sdbufferpos = 1
Gosub Sddirsectorloop
If Sdstatus = 0 Then
Incr Sdsectord
Sdbufferpos = 1
Incr Sdsecincluster
End If
' Loop through this cluster until:
' file or subdirectory is found or
' empty directory entry has been found or
' we have scanned all sectors in the cluster without finding the entry
Loop Until Sdstatus <> 0 Or Sdsecincluster > Sdsectorend
If Sdstatus = 0 Then ' Entry hasn't been found and we have reached the end of the current directory cluster or FAT16 root directory
If Sdclusterd = 0 Then
Sdstatus = 46 ' We have searched the entire FAT16 root directory but not found what we were looking for
Else
#if Sdwmode > 0
Sdpreviousclusterd = Sdclusterd ' If we are writing, we here want to store the previous cluster number for cluster extension
#endif
Gosub Sdlocatenextcluster ' Look up the next cluster in the FAT
Gosub Sdcheckclusterreference ' Check if it indicates EOC (in that case Sdstatus = 31)
If Sdstatus = 0 Then
Gosub Sdlocatesector ' Calculate the first sector in the next Sdclusterd and place in Sdsectord
Sdsecincluster = 1
#if Sdwmode > 0 ' Store directory position if we are using write mode
Else ' We have reached the EOC marker, store this directory
Sddirclusterd = Sdpreviousclusterd ' Store the directory cluster number in Sddirclusterd
Sddirsecincluster = Sdsecincluster ' Chain start sector number of the current directory cluster, 1 to Sdsecincluster
Sddirbufferpos = Sdbufferpos ' Chain start directory buffer position, 1 to 512
#endif
End If
End If
End If
Loop Until Sdstatus <> 0
Return
'-------------------------------------------------------------------------------
Sddirsectorloop:
' Called from Sdfindentryindirectory
' Input variables:
' Sdsectord = absolute directory sector number
' Sdbufferpos = 1 if you are doing your first search, otherwise the actual Sdbufferpos
' Sddirectorymode
' 0 = find file
' 1 = find subdirectory
' 2 = find volume ID
' 3 = find available directory entry
' 4 = find parent directory
' 5 = return entire entry unless EOD (only if enabled by Sdusefindstoreentry = 1)
' Output variables:
' Sdstatus:
' 28 = An available directory space has been found
' 32 = EOD End Of Directory marker found
' 34 = The volume ID has been found
' 47 = Parent directory pointer found
' 48 = File or subdirectory found
' Sddirectorymode = 2:
' Sdclusterd = Next cluster number (where file/subdirectory resides)
' Sdentrynames = The volume ID
' Sddirectorymode <> 2:
' Sddirclusterd = Cluster number for this directory entry
' Sddirsecincluster = Chain start sector number of the current directory cluster, 1 to Sdsecincluster
' Sddirbufferpos = Chain start directory buffer position, 1 to 512
' Sdclusterd = Next cluster number (where file/subdirectory resides)
Gosub Sdreadsector ' Read Sdsectord to buffer
If Sdstatus = 0 Then
Do
If Sdbuffer(sdbufferpos) = &H00 Then
Sdstatus = 32 ' EOD End Of Directory marker found
Else
#if Sdusefind = 1 And Sdusefindstoreentry = 1
If Sddirectorymode = 5 Then ' Store the entire directory entry in Sdfinddirentry()
Sdtempw = Sdbufferpos
For Sdtempb = 1 To 32
Sdfinddirentry(sdtempb) = Sdbuffer(sdtempw)
Incr Sdtempw
Next Sdtempb
Else
#endif
Sdtempw = Sdbufferpos + 11
Sdtempb = Sdbuffer(sdtempw) Or &B11110000
If Sdtempb <> &HFF Then ' Not Long FileName entry
Select Case Sddirectorymode
Case 0 To 1: ' Find file or find subdirectory
Sdtempb = 1
Sdtempw = Sdbufferpos
While Sdbuffer(sdtempw) = Sdentrynameb(sdtempb) And Sdtempb < 12
Incr Sdtempb
Incr Sdtempw
Wend
If Sdtempb = 12 Then
Sdstatus = 48 ' File or subdirectory found
End If
Case 2: ' Find volume ID
If Sdbuffer(sdtempw).3 = 1 Then
Sdstatus = 34 ' Volume ID found
Sdtempb = 1
Sdtempw = Sdbufferpos
While Sdtempb < 12
Sdentrynameb(sdtempb) = Sdbuffer(sdtempw)
Incr Sdtempb
Incr Sdtempw
Wend
End If
#if Sdwmode > 0 ' Find available directory entry is only relevant if we are using write mode
Case 3:
If Sdbuffer(sdbufferpos) = &HE5 Then
Sdstatus = 28 ' Available (previously erased) directory entry found
End If
#endif
Case 4: ' Find parent directory cluster
Sdtempw = Sdbufferpos + 1
If Sdbuffer(sdbufferpos) = &H2E And Sdbuffer(sdtempw) = &H2E Then
Sdstatus = 47 ' Parent directory pointer found
End If
End Select
End If
End If
If Sdstatus = 0 Then
Sdbufferpos = Sdbufferpos + 32 ' Matching entry not found - point to the next entry
Else
If Sddirectorymode <> 2 Then ' Point to the cluster where the file/subdirectory/parent directory/available directory entry resides
#if Sdwmode > 0 ' Store directory position if we are using write mode
Sddirclusterd = Sdclusterd ' Store the directory cluster number in Sddirclusterd
Sddirsecincluster = Sdsecincluster ' Chain start sector number of the current directory cluster, 1 to Sdsecincluster
Sddirbufferpos = Sdbufferpos ' Chain start directory buffer position, 1 to 512
#endif
Gosub Sdgetclusterandsize ' Place cluster number in Sdclusterd and filesize in Sdfilesized
End If
End If
#if Sdusefind = 1 And Sdusefindstoreentry = 1
End If
#endif
Loop Until Sdstatus <> 0 Or Sdbufferpos > Sdbytespersecw
End If
Return
'-------------------------------------------------------------------------------
Sdcheckclusterreference: ' Check if it indicates EOC (>=&HFFF8 or >=&H0FFFFFF8)
If Sdfattype = 2 Then ' FAT16
Sdclusterb(3) = 0
Sdclusterb(4) = 0
If Sdclusterd > &HFFF7 Then
Sdstatus = 31 ' EOC End OF Cluster chain found
End If
Elseif Sdfattype = 4 Then ' FAT32
Sdclusterb(4) = Sdclusterb(4) And &B00001111
If Sdclusterd > &H0FFFFFF7 Then
Sdstatus = 31 ' EOC End OF Cluster chain found
End If
End If
Return
'-------------------------------------------------------------------------------
Sdgetclusterandsize:
If Sdfattype = 4 Then ' FAT32
Sdtempw = Sdbufferpos + 20
Sdclusterb(3) = Sdbuffer(sdtempw) ' 21 File/subdirectory cluster number
Incr Sdtempw
Sdclusterb(4) = Sdbuffer(sdtempw) ' 22
Sdtempw = Sdtempw + 5
Else ' FAT16
Sdclusterb(3) = 0
Sdclusterb(4) = 0
Sdtempw = Sdbufferpos + 26
End If
Sdclusterb(1) = Sdbuffer(sdtempw) ' 27
Incr Sdtempw
Sdclusterb(2) = Sdbuffer(sdtempw) ' 28
Incr Sdtempw
Sdfilesizeb(1) = Sdbuffer(sdtempw) ' 29 File size in bytes
Incr Sdtempw
Sdfilesizeb(2) = Sdbuffer(sdtempw) ' 30
Incr Sdtempw
Sdfilesizeb(3) = Sdbuffer(sdtempw) ' 31
Incr Sdtempw
Sdfilesizeb(4) = Sdbuffer(sdtempw) ' 32
Return
#endif
'===============================================================================
' 2 = write unbuffered
' 1 = write buffered
' 0 = not writing
#if Sdwmode > 0 And Sdfsactive = 1
Sdcreatefileordir:
' Used as the first of three steps when creating a new file or the only step when creating a new subdirectory
' (Sdwritebyte and Sdclosefile are the subsequent two)
' Find free FAT entry, find free directory entry, and save the directory entry back to the SD card
' Input variables:
' Sdentrynames = the 8.3 filename
' Sdcreatemode 0 = file, 1 = subdirectory
' Sdstartdirclusterd = the directory cluster number (in which the file or subdirectory should be created). Set to 0 for root directory.
' Sdyear = the year
' Sdmonth = the month
' Sdday = the day
' Sdhours = the hours
' Sdminutes = the minutes
' Sdseconds = the seconds
' Sdmseconds = the milliseconds
' If Sdmaintainfsinfo = 1
' Sdfsinfofreeclustersd = the number of free clusters
' Sdfsinfonextfreeclusterd = the next unused cluster (typically not the first free re-usable cluster)
' Output variables:
' Sdpartitionfull = 1 if the partition is full (no more free FAT entries)
' Sdfatsectornum = the FAT sector number
' Sdfatbufferpos = the position in this sector
' Sddirclusterd = the directory cluster
' Sddirsecincluster = the sector in this cluster
' Sddirbufferpos = the position in this sector
' Sdwclusterd = the cluster number we are writing to
' Sdwsecincluster = the sector in this cluster (1)
' Sdwbufferpos = the position in this sector (0) - please notice!
' Sdwfilesized = the file size (0)
' If Sdmaintainfsinfo = 1
' Sdfsinfofreeclustersd = the number of free clusters
' Sdfsinfonextfreeclusterd = the next unused cluster (typically not the first free re-usable cluster)
' Find the first available directory entry in the specified directory (cluster) and store it in Sddirclusterd, Sddirsecincluster, and Sdirbufferpos
Sdclusterd = Sdstartdirclusterd
Sdsecincluster = 1
Sdbufferpos = 1
#if Sdusedirlist = 1
Sddirlistarraycounter = 0
Sddirlistdirection = 0
Do
Sdstatus = 0
Gosub Sddirlist
Loop Until Sdstatus = 28 Or Sdstatus = 32 Or Sddirlistendpoint = 1 ' Empty entry found or EOD marker found or EOD reached
' Store directory position for this directory entry
Sddirclusterd = Sdclusterd ' Chain start directory cluster number
Sddirsecincluster = Sdsecincluster ' Sector number in cluster
Sddirbufferpos = Sdbufferpos ' Buffer position in sector
If Sddirlistendpoint = 1 And Sddirclusterd <> 0 Then ' EOD found and not in FAT16 root
#else
#if Sdusefind = 1
Sddirectorymode = 3 ' Find available directory entry
Gosub Sdfindentryindirectory
If Sdstatus = 31 Then ' EOC End OF Cluster chain found (directory) - we must first extend the cluster (if not FAT16 root)
#endif
#endif
Sdstatus = 0
' Extend directory with additional cluster
Sdsectord = Sdfat1location ' Find free FAT1 entry and store position in Sdfatsectornum, and Sdfatbufferpos plus Sdfatclusterd
Sdbufferpos = 1
Gosub Sdfindfreefat
If Sdstatus = 0 Then
Sdclusterd = Sddirclusterd ' Read the FAT entry for the directory cluster that we are about to extend
Gosub Sdcalculatefat1sector
Gosub Sdreadsector
If Sdstatus = 0 Then ' Save the updated FAT sector holding the now extended directory cluster
Sdtempw = Sdbufferpos
Sdbuffer(sdtempw) = Sdfatclusterb(1)
Incr Sdtempw
Sdbuffer(sdtempw) = Sdfatclusterb(2)
If Sdfattype = 4 Then
Incr Sdtempw
Sdbuffer(sdtempw) = Sdfatclusterb(3)
Incr Sdtempw
Sdbuffer(sdtempw) = Sdfatclusterb(4)
End If
#if Sdwmode = 1
Sdwriteorigin = 0
#endif
Gosub Sdwritesector
If Sdstatus = 0 Then
Sdsectord = Sdfat1location - Sdsectord
Sdsectord = Sdsectord + Sdfat2location
Gosub Sdwritesector
If Sdstatus = 0 Then
Sdclusterd = &H0FFFFFFF ' Save EOC marker to the FAT entry in Sdfatclusterd
Gosub Sdsavefatsec
If Sdstatus = 0 Then
Sdclusterd = Sdfatclusterd ' Empty the extended directory cluster
Gosub Sdwclearcluster
If Sdstatus = 0 Then
Decr Sdsectord
Gosub Sdwritesector
If Sdstatus = 0 Then
#if Sdusefsinfo = 1
Gosub Sdupdatefsinfo ' Update the fsinfo variables
#endif
' Store directory position for this directory entry
Sddirclusterd = Sdfatclusterd ' Directory cluster number
Sddirsecincluster = 1 ' Sector number in cluster
Sddirbufferpos = 1 ' Buffer position in sector
Gosub Sdlocatesector
Sdstatus = 32
End If
End If
End If
End If
End If
End If
End If
Else
Sdfatsectornum = Sdfat1location
Sdfatbufferpos = 1
End If
' An available (previously erased) directory entry or EOD End Of Directory marker found
' Sddirclusterd, Sddirsecincluster, and Sddirbufferpos now point to this directory entry
If Sdstatus = 28 Or Sdstatus = 32 Then ' Free directory entry found
Sdstatus = 0 ' Remap Sdstatus
' Find free FAT1 entry and store position in Sdfatsectornum, and Sdfatbufferpos plus Sdfatclusterd
Sdsectord = Sdfatsectornum ' Continue looking in the same FAT sector
Sdbufferpos = Sdfatbufferpos
Gosub Sdfindfreefat
If Sdstatus = 0 Then
Gosub Sdlocatedirsector ' Point to the available directory entry again
#if Sdwmode = 1
Sdreaddestination = 0
#endif
Gosub Sdreadsector
If Sdstatus = 0 Then
#if Sdusefsinfo = 1
Gosub Sdupdatefsinfo ' Update the fsinfo variables
#endif
' Store all information in this new directory entry, except file size, that will be updated at the end in a second read+write to this directory sector
' Sdentrynames set before calling Sdcreatefileordir
Sdclusterd = Sdfatclusterd
Sdwfilesized = 0
Sdbufferpos = Sddirbufferpos
Gosub Sdstoredirentry ' Sdcreatemode set before calling Sdcreatefileordir
#if Sdwmode = 1
Sdwriteorigin = 0 ' Write from Sdbuffer
#endif
Gosub Sdwritesector ' Write the updated directory sector back to the SD card
End If
If Sdcreatemode = 1 And Sdstatus = 0 Then ' Create subdirectory ., .., (and EOD) entries in the new directory cluster
Sdclusterd = Sdfatclusterd ' Calculate the absolute sector number for the free cluster and write the last (sdsecspercluster - 1) sectors
Gosub Sdwclearcluster
If Sdstatus = 0 Then
Sdentrynames = Space(11) ' First clean out Sdentrynames
Sdentrynameb(1) = &H2E ' Store the subdirectory's starting cluster number in the . directory entry
Sdclusterd = Sdfatclusterd
Sdwfilesized = 0
Sdbufferpos = 1
Gosub Sdstoredirentry
Sdentrynameb(2) = &H2E ' Store the parent directory's starting cluster number in the .. directory entry
Sdclusterd = Sdstartdirclusterd
Sdbufferpos = 33
Gosub Sdstoredirentry
Decr Sdsectord ' Finally write the first sector
Gosub Sdwritesector
If Sdstatus = 0 Then
Sdclusterd = &H0FFFFFFF ' Save EOC marker to the FAT sector in Sdfatclusterd
Gosub Sdsavefatsec
End If
End If
End If
End If
End If
Sdclosefile = 0
If Sdstatus = 44 Then ' End of FAT and no free entry found (partition full)
Sdpartitionfull = 1 ' The partition is full (no more free FAT entries)
Sdstatus = 0
End If
Return
'-------------------------------------------------------------------------------
#if Sduseappend = 1
Sdpreparetoappend:
' Used to append to a pre-existing file
' Input variables:
' Sdentrynames = the 8.3 filename
' Sdyear = the year
' Sdmonth = the month
' Sdday = the day
' Sdhours = the hours
' Sdminutes = the minutes
' Sdseconds = the seconds
' Sdstartdirclusterd = the first cluster of this (sub-)directory
' Sdfsinfofreeclustersd = the number of free clusters
' Sdfsinfonextfreeclusterd = the next unused cluster (typically not the first free re-usable cluster)
' Output variables:
' Sdwclusterd = the cluster number we are writing to
' Sdwsecincluster = the sector in this cluster
' Sdwbufferpos = the position in this sector
' Sdwfilesized = the file size
' Sdfatsectornum = the FAT sector number
' Sdfatbufferpos = the position in this sector
' Sddirclusterd = the directory cluster
' Sddirsecincluster = the sector in this cluster
' Sddirbufferpos = the position in this sector
' Sdfsinfofreeclustersd = the number of free clusters
' Sdfsinfonextfreeclusterd = the next unused cluster (typically not the first free re-usable cluster)
' Sddirclusterd, Sddirsecincluster, and Sddirbufferpos point to this directory entry
' Sdwclusterd, Sdwsecincluster, and Sdwbufferpos point to the last byte of the file
' Sdfatclusterd, Sdfatsectornum, and Sdfatbufferpos point to the last fat entry(cluster number)
' Find the file
Sdclusterd = Sdstartdirclusterd
Sdsecincluster = 1
Sdbufferpos = 1
#if Sdusedirlist = 1
Sdappendfound = 0
Sddirlistarraycounter = 0
Sddirlistdirection = 0 ' Loop through this directory forward
Do
Do
Sdstatus = 0
Gosub Sddirlist
Loop Until Sdstatus = 26 Or Sdstatus = 32 Or Sddirlistendpoint = 1
If Sdstatus = 26 Then ' Look for a file named Sdentrynames
Sdappendcontinue = 1
Sdtempb = 1
Do
If Sddirlistdirentry(sdtempb) <> Sdentrynameb(sdtempb) Then
Sdappendcontinue = 0
End If
Incr Sdtempb
Loop Until Sdappendcontinue = 0 Or Sdtempb = 12
If Sdappendcontinue = 1 Then
Sdappendfound = 1
End If
End If
Loop Until Sdappendfound = 1 Or Sdstatus = 32 Or Sddirlistendpoint = 1
If Sdappendfound = 1 Then
Sdstatus = 0
Sddirclusterd = Sdclusterd ' Store this directory entry position
Sddirsecincluster = Sdsecincluster
Sddirbufferpos = Sdbufferpos
Sdwfilesizeb(1) = Sddirlistdirentry(29) ' Store original filesize in Sdwfilesized
Sdwfilesizeb(2) = Sddirlistdirentry(30)
Sdwfilesizeb(3) = Sddirlistdirentry(31)
Sdwfilesizeb(4) = Sddirlistdirentry(32)
Sdwclusterb(1) = Sddirlistdirentry(27) ' Store the file's starting cluster number in the new directory entry
Sdwclusterb(2) = Sddirlistdirentry(28)
Sdwclusterb(3) = Sddirlistdirentry(21)
Sdwclusterb(4) = Sddirlistdirentry(22)
Sdclusterd = Sdwclusterd
#else
#if Sdusefind = 1
Sddirectorymode = 0 ' Find file
Gosub Sdfindentryindirectory
If Sdstatus = 48 Then ' File found
Sdstatus = 0 ' Sddirclusterd, Sddirsecincluster, and Sddirbufferpos now point to this directory entry
Gosub Sdgetclusterandsize ' Place file's starting cluster number in Sdwclusterd and original filesize in Sdwfilesized
Sdwclusterd = Sdclusterd
Sdwfilesized = Sdfilesized
#endif
#endif
' Loop through the FAT1 chain until EOC is found
Do
Sdfatclusterd = Sdclusterd ' Store the last known cluster number
Gosub Sdlocatenextcluster ' Look up the next cluster number
Gosub Sdcheckclusterreference ' Check if it indicates EOC (in that case Sdstatus = 31)
Loop Until Sdstatus <> 0
If Sdstatus = 31 Then ' Unless bad cluster found (Assume the original file size is correct)
Sdstatus = 0
Sdwclusterd = Sdfatclusterd ' Store the file's last cluster number
Sdtempd = Sdwfilesized Mod Sdbytespersecw
'Sdwbufferposb(1) = Sdtempdb(1) ' Buffer position in the last sector
'Sdwbufferposb(2) = Sdtempdb(2)
Sdwbufferpos = Sdtempd
Sdtempd = Sdwfilesized \ Sdbytespersecw ' Number of entire sectors
Sdtempd = Sdtempd Mod Sdsecspercluster
Sdwsecincluster = Sdtempdb(1) + 1 ' Sector number in the last cluster
Sdclusterd = Sdwclusterd ' Calculate the FAT1 sector number and position for this cluster
Gosub Sdcalculatefat1sector
Sdfatsectornum = Sdsectord ' Store position
Sdfatbufferpos = Sdbufferpos
#if Sdwmode = 1
For Sdtempw = 1 To 512 ' Copy this FAT sector data into Sdfat1buffer
Sdfatbuffer(sdtempw) = Sdbuffer(sdtempw)
Next Sdtempw
#endif
Gosub Sdlocatedirsector ' Update the directory sector with the new write and access DateTime
#if Sdwmode = 1
Sdreaddestination = 0
#endif
Gosub Sdreadsector
If Sdstatus = 0 Then
Gosub Sdpreparedatetime ' Calculate and store date and time in the directory entry
Sdtempw = Sddirbufferpos + 18
Gosub Sdstore19_26
#if Sdwmode = 1
Sdwriteorigin = 0
#endif
Gosub Sdwritesector
If Sdstatus = 0 Then ' Read the file's last sector into Sdbuffer()
Sdclusterd = Sdwclusterd
Gosub Sdlocatesector
Sdsectord = Sdsectord + Sdwsecincluster
Decr Sdsectord
Sdstatus = 0
Gosub Sdreadsector
End If
End If
End If
End If
Return
#endif
'-------------------------------------------------------------------------------
Sdwritebyte:
' Used to write a byte to an open file
' Always make sure that Sdclosefile = 0 before writing
' Input variables:
' Sdclosefile must be 0 - otherwise the file should be closed
' Sdwclusterd = the cluster number we are writing to (initially set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sdwsecincluster = the sector in this cluster (initially set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sdwbufferpos = the position in this sector (initially set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sdwfilesized = the file size (initially set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sdfatsectornum = the FAT sector number (initially set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sdfatbufferpos = the position in this sector (initially set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sdbuffer() (touch only when writing to > 1 file)
' Sdbyterw = the data byte we are about to write
' Output variables:
' Sdwclusterd = the cluster number we are writing to
' Sdwsecincluster = the sector in this cluster
' Sdwbufferpos = the position in this sector
' Sdwfilesized = the file size
' Sdfatsectornum = the FAT sector number
' Sdfatbufferpos = the position in this sector
' Sdbuffer()
' Sdpartitionfull = 1 if the partition is full (there are no more free FAT entries)
' Sdclosefile = 1 if the file should be closed after the return from this gosub
' Returned statuses:
' Sdstatus = 0 Successful
' Sdstatus = 7 Timeout
' Sdstatus = 41 Data rejected due to a CRC error
' Sdstatus = 42 Data rejected due to a Write Error
' Sdstatus = 43 Free FAT entry found
' Sdstatus = 44 The partition is full - store FAT EOC marker
Incr Sdwbufferpos ' Start by pointing to the next byte
If Sdwbufferpos > Sdbytespersecw Then ' After saving the last byte in a sector, save the sector to the SD card
Sdclusterd = Sdwclusterd ' Calculate the absolute sector number for this sector
Gosub Sdlocatesector
Sdsectord = Sdsectord + Sdwsecincluster
Decr Sdsectord
#if Sdwmode = 1
Sdwriteorigin = 0 ' Save sector from Sdbuffer to SD card
#endif
Gosub Sdwritesector
Sdsavedatasector = 0
Incr Sdwsecincluster
If Sdwsecincluster > Sdsecspercluster Then ' After saving the last sector in the current cluster, look up the next cluster
Sdsectord = Sdfatsectornum ' Point to the same FAT sector
Sdbufferpos = Sdfatbufferpos + Sdfattype ' Point to the next FAT position
#if Sdwmode = 1
If Sdbufferpos < Sdbytespersecw Then ' Search for a free FAT entry in the current sector
Sdfatcontinue = 1
Do
Sdtempw = Sdbufferpos ' Look for &H0000 (FAT16) or &H00000000 (FAT32)
If Sdfatbuffer(sdtempw) = 0 Then ' Byte 1
Incr Sdtempw
If Sdfatbuffer(sdtempw) = 0 Then ' Byte 2
If Sdfattype = 2 Then ' FAT16
Sdstatus = 43 ' Free FAT entry found
Sdfatcontinue = 0
Else ' FAT32
Incr Sdtempw
If Sdfatbuffer(sdtempw) = 0 Then ' Byte 3
Incr Sdtempw
Sdtempb = Sdfatbuffer(sdtempw) And &B00001111
If Sdtempb = 0 Then ' Byte 4
Sdstatus = 43 ' Free FAT entry found
Sdfatcontinue = 0
End If
End If
End If
End If
End If
If Sdfatcontinue = 1 Then
Sdbufferpos = Sdbufferpos + Sdfattype
End If
Loop Until Sdfatcontinue = 0 Or Sdbufferpos > Sdbytespersecw
If Sdstatus = 43 Then ' New free FAT entry found in the same FAT sector
Gosub Sdcalculatefatentrycluster ' Calculate cluster number
Sdtempw = Sdfatbufferpos
For Sdtempb = 1 To Sdfattype ' Update this FAT entry with a pointer to the next cluster
Sdfatbuffer(sdtempw) = Sdclusterb(sdtempb)
Incr Sdtempw
Next Sdtempb
Sdwclusterd = Sdclusterd ' Update the write cluster pointer
Sdwsecincluster = 1 ' Reset write sector number (Sdwbufferpos is reset after return)
Sdfatbufferpos = Sdbufferpos ' Update the FAT buffer pointer
End If
End If
If Sdstatus <> 43 Then ' If none is found in the current FAT sector, loop through the next sector(s)
Sdbufferpos = 1
Sdsectord = Sdfatsectornum + 1 ' Point to the next FAT sector
If Sdsectord < Sdfat2location Then
Do
Gosub Sdfindfreefatentry ' Sdsectord and Sdbufferpos point to the next free FAT
Loop Until Sdstatus <> 0
If Sdstatus = 43 Then ' New free FAT entry found in another FAT sector
Sdstatus = 0
Sdtempd = Sdsectord ' Temporarily store the FAT sector number with the current empty entry
Gosub Sdcalculatefatentrycluster ' Calculate cluster number into Sdclusterd
Gosub Sdsavefatsec ' Store Sdclusterd at Sdfatsectornum & Sdfatbufferpos and save Sdfatbuffer() back to the card
If Sdstatus = 0 Then
Sdwclusterd = Sdclusterd ' Update the write cluster pointer
Sdwsecincluster = 1 ' Reset write sector number (Sdwbufferpos is reset after return)
Sdfatsectornum = Sdtempd ' Step to the next FAT entry and position
Sdfatbufferpos = Sdbufferpos ' Update the FAT buffer pointer
For Sdtempw = 1 To 512 ' Copy this FAT sector data into Sdfatbuffer
Sdfatbuffer(sdtempw) = Sdbuffer(sdtempw)
Next Sdtempw
End If
#if Sdusefsinfo = 1
Gosub Sdupdatefsinfo ' Update the fsinfo variables
#endif
Sdstatus = 0
Else ' End of FAT reached and no free FAT entry found (SDstatus = 44)
Sdsectord = Sdfatsectornum ' Point back to the last FAT sector
End If
Else ' End of FAT reached and no free FAT entry found (SDstatus = 44)
Sdsectord = Sdfatsectornum ' Point back to the last FAT sector
End If
Else
Sdstatus = 0
End If
#else ' Unbuffered FAT
If Sdsectord < Sdfat2location Then
Do
Gosub Sdfindfreefatentry
Loop Until Sdstatus <> 0
If Sdstatus = 43 Then ' New free FAT entry found in another FAT sector
Sdstatus = 0
Gosub Sdcalculatefatentrycluster ' Calculate Sdclusterd number to store in Sdfatsectornum & Sdfatbufferpos
Sdtempd = Sdsectord ' Temporarily store FAT sector number with free position
Sdsectord = Sdfatsectornum ' Read the sector containing the FAT entry about to be extended
Gosub Sdreadsector
If Sdstatus = 0 Then
Sdtempw = Sdfatbufferpos
For Sdtempb = 1 To Sdfattype ' Update the previous FAT entry with a pointer to the next cluster
Sdbuffer(sdtempw) = Sdclusterb(sdtempb)
Incr Sdtempw
Next Sdtempb
Gosub Sdwritesector ' Write the now extended FAT chain sector back to the SD card
If Sdstatus = 0 Then
Sdwclusterd = Sdclusterd ' Update the write cluster pointer
Sdwsecincluster = 1 ' Reset write sector number (Sdwbufferpos is reset after return)
Sdfatsectornum = Sdtempd ' Update the FAT sector number
Sdfatbufferpos = Sdbufferpos ' Update the FAT buffer pointer
#if Sdusefsinfo = 1
Gosub Sdupdatefsinfo ' Update the fsinfo variables
#endif
End If
End If
Else ' End of FAT reached and no free FAT entry found (SDstatus = 44)
Sdsectord = Sdfatsectornum ' Point back to the last FAT sector
End If
Else ' End of FAT reached and no free FAT entry found (SDstatus = 44)
Sdsectord = Sdfatsectornum ' Point back to the last FAT sector
End If
#endif
If Sdstatus = 44 Then ' The partition is full - store FAT EOC marker
Sdpartitionfull = 1 ' The partition is full (there are no more free FAT entries)
Sdclosefile = 1 ' Close the file
Sdstatus = 0 ' Remap Sdstatus
End If
End If
Sdwbufferpos = 1 ' Reset byte counter
End If
If Sdclosefile = 0 Then
Sdbuffer(sdwbufferpos) = Sdbyterw ' Store byte in sector buffer
Incr Sdwfilesized ' Increment the file size byte counter
Sdsavedatasector = 1
End If
Return
'-------------------------------------------------------------------------------
Sdfinalizeafterwriting:
' Used to close the file after the final call to Sdwritebyte
' Input variables:
' Sdwclusterd = the cluster number we are writing to (set by Sdwritebyte - touch only when writing to > 1 file)
' Sdwsecincluster = the sector in this cluster (set by Sdwritebyte - touch only when writing to > 1 file)
'' Sdwbufferpos = the position in this sector (set by Sdwritebyte - touch only when writing to > 1 file)
' Sdwfilesized = the file size (set by Sdwritebyte - touch only when writing to > 1 file)
' Sdfatsectornum = the FAT sector number (set by Sdwritebyte - touch only when writing to > 1 file)
' Sdfatbufferpos = the position in this sector (set by Sdwritebyte - touch only when writing to > 1 file)
' Sddirclusterd = the directory cluster getting file size (set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sddirsecincluster = the sector in this cluster (set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sddirbufferpos = the position in this sector (set by Sdcreatefileordir or Sdappendfile - touch only when writing to > 1 file)
' Sdbuffer() (touch only when writing to > 1 file)
' Sdfatbuffer() (touch only when writing to > 1 file)
' Sdsavedatasector = 1 if the data should be saved to the SD (set by Sdwritebyte - touch only when writing to > 1 file)
' Output variables:
' Sdclosefile = 0 if the file was closed successfully
' Returned statuses:
' Sdstatus = 0 Successful
' Sdstatus = 7 Timeout
' Sdstatus = 41 Data rejected due to a CRC error
' Sdstatus = 42 Data rejected due to a Write Error
Sdclosefile = 1
' Save the current file content sector to the SD card (if we haven't just saved a full data sector to the SD card)
If Sdsavedatasector = 1 Then ' Sdsavedatasector = 1 "inside" a data sector and 0 just after the sector has been saved by Sdwritebyte
Sdclusterd = Sdwclusterd ' Calculate the absolute sector number for this sector
Gosub Sdlocatesector
Sdsectord = Sdsectord + Sdwsecincluster
Decr Sdsectord
#if Sdcleartail = 1
Incr Sdwbufferpos
For Sdtempw = Sdwbufferpos To Sdbytespersecw
Sdbuffer(sdtempw) = 0
Next Sdtempw
Decr Sdwbufferpos
#endif
#if Sdwmode = 1
Sdwriteorigin = 0
#endif
Gosub Sdwritesector
End If
Sdclusterd = &H0FFFFFFF ' Save EOC marker in the FAT sector
Gosub Sdsavefatsec
Sdstatus = 0
' Update the directory entry with file size and save it to the SD card
Sdclusterd = Sddirclusterd ' Point to the directory entry again
Sdsecincluster = Sddirsecincluster
Sdbufferpos = Sddirbufferpos
Gosub Sdlocatedirsector ' Point to the file's directory entry
If Sddirclusterd > 0 Then ' Unless we are in the FAT16 root directory
Sdsectord = Sdsectord + Sddirsecincluster
Decr Sdsectord
End If
#if Sdwmode = 1
Sdreaddestination = 0
#endif
Gosub Sdreadsector ' Update the directory sector with the new write and access DateTime
If Sdstatus = 0 Then
Gosub Sdstore29_32 ' Enter file size into the directory entry
#if Sdwmode = 1
Sdwriteorigin = 0
#endif
Gosub Sdwritesector ' Write the directory entry back to the SD card
Sdclosefile = 0 ' The file was close successfully
End If
Return
'-------------------------------------------------------------------------------
Sdstoredirentry:
' Used to store all information in a new directory entry, except file size, that will be updated at the end in a second read+write to this directory sector
' Input variables:
' Sdentrynames = the name of the new file or subdirectory (or . .. in case of the leading subirectory entries)
' Sdcreatemode 0 = file, 1 = subdirectory
' Sdclusterd = the cluster number where the new file or subdirectory starts
' Sdwfilesized = the file size (0 for subdirectory or when creating a new file)
' Sdbufferpos = the buffer position in the current sector of the current directory cluster
' Sdbuffer() = the current directory sector
' Sdyear = the year
' Sdmonth = the month
' Sdday = the day
' Sdhours = the hours
' Sdminutes = the minutes
' Sdseconds = the seconds
' Sdmseconds = the milliseconds
' Output variables:
' Sdbuffer() = the current directory sector
' Sddatew = the date in DateTime format (normally not used afterwards)
' Sdtimew = the time in DateTime format (normally not used afterwards)
Sdtempw = Sdbufferpos
For Sdtempb = 1 To 11 ' Store the filename in the new directory entry
Sdbuffer(sdtempw) = Sdentrynameb(sdtempb)
Incr Sdtempw
Next Sdtempb
If Sdcreatemode = 0 Then ' Create file
Sdbuffer(sdtempw) = &H20 ' Set archive bit - has been changed since last backup
Else ' Create subdirectory
Sdbuffer(sdtempw) = &H10 ' Set subdirectory bit
End If
Sdtempw = Sdtempw + 9
Sdbuffer(sdtempw) = Sdclusterb(3) ' 21
Incr Sdtempw
Sdbuffer(sdtempw) = Sdclusterb(4) ' 22
Sdtempw = Sdtempw + 5
Sdbuffer(sdtempw) = Sdclusterb(1) ' 27 Store the file's starting cluster number in the new directory entry
Incr Sdtempw
Sdbuffer(sdtempw) = Sdclusterb(2) ' 28
Gosub Sdstore29_32 ' Enter file size into the directory entry
Gosub Sdpreparedatetime ' Calculate and store date and time in the new directory entry
Sdtempw = Sdbufferpos + 12
Sdbuffer(sdtempw) = 0 ' 13
Incr Sdtempw
Sdbuffer(sdtempw) = Sdseconds Mod 2 ' 14
Sdbuffer(sdtempw) = Sdbuffer(sdtempw) * 100 ' 14
Sdbuffer(sdtempw) = Sdbuffer(sdtempw) + Sdmseconds ' 14
Incr Sdtempw
Sdbuffer(sdtempw) = Sdtimeb(1) ' 15
Incr Sdtempw
Sdbuffer(sdtempw) = Sdtimeb(2) ' 16
Incr Sdtempw
Sdbuffer(sdtempw) = Sddateb(1) ' 17
Incr Sdtempw
Sdbuffer(sdtempw) = Sddateb(2) ' 18
Incr Sdtempw
Gosub Sdstore19_26
Return
'-------------------------------------------------------------------------------
Sdstore19_26:
Sdbuffer(sdtempw) = Sddateb(1) ' 19
Incr Sdtempw
Sdbuffer(sdtempw) = Sddateb(2) ' 20
Sdtempw = Sdtempw + 3
Sdbuffer(sdtempw) = Sdtimeb(1) ' 23
Incr Sdtempw
Sdbuffer(sdtempw) = Sdtimeb(2) ' 24
Incr Sdtempw
Sdbuffer(sdtempw) = Sddateb(1) ' 25
Incr Sdtempw
Sdbuffer(sdtempw) = Sddateb(2) ' 26
Return
'-------------------------------------------------------------------------------
Sdstore29_32:
Sdtempw = Sdbufferpos + 28 ' Enter file size into the directory entry
Sdbuffer(sdtempw) = Sdwfilesizeb(1) ' 29
Incr Sdtempw
Sdbuffer(sdtempw) = Sdwfilesizeb(2) ' 30
Incr Sdtempw
Sdbuffer(sdtempw) = Sdwfilesizeb(3) ' 31
Incr Sdtempw
Sdbuffer(sdtempw) = Sdwfilesizeb(4) ' 32
Return
'-------------------------------------------------------------------------------
Sdfindfreefat:
' Used to search FAT1 for a free entry, starting from Sdsectord, and store a number of positions and the FAT1 sector
' Input variables:
' Sdsectord = the FAT1 sector we are starting in (>= Sdfat1location, < Sdfat2location)
' Sdbufferpos = 1 or the actual position
' Output variables:
' Sdfatsectornum = the FAT1 sector in which we found a free entry
' Sdfatbufferpos = the position in this sector
' Sdfatclusterd = the cluster number corresponding to the above FAT1 position
' Sdwclusterd = the cluster number corresponding to the above FAT1 position
' Sdwsecincluster = 1
' Sdwbufferpos = 0 (more practical starting point than 1)
' Sdwfilesized = 0
' Returned statuses:
' 0 = Successful
' 7 = Timeout
Sdpartitionfull = 0 ' Assume the partition is not full (there are free FAT entries)
Gosub Sdfindfreefatentry
If Sdstatus = 43 Then ' Free FAT entry found
Sdstatus = 0 ' Remap Sdstatus
Sdfatsectornum = Sdsectord ' Store absolute sector position for this FAT entry
Sdfatbufferpos = Sdbufferpos ' (This is where we are later going to write the pointer to the next entry)
#if Sdwmode = 1 ' Buffered FAT sector writing
For Sdtempw = 1 To 512 ' Copy this FAT sector data into Sdfat1buffer
Sdfatbuffer(sdtempw) = Sdbuffer(sdtempw)
Next Sdtempw
#endif
Gosub Sdcalculatefatentrycluster ' Calculate cluster number for this FAT entry
Sdfatclusterd = Sdclusterd ' Store this FAT entry's corresponding cluster number (not the pointer to the next entry)
Sdwclusterd = Sdclusterd ' Set the initial write cluster pointer
Sdwsecincluster = 1 ' Set the initial write sector number
Sdwbufferpos = 0 ' Set the initial write buffer position (0 is more practical than 1 in this case)
Sdwfilesized = 0 ' Reset the file size counter
End If
Return
'-------------------------------------------------------------------------------
Sdfindfreefatentry:
' Used to search FAT1 for a free entry, starting from Sdsectord
' Input variables:
' Assign Sdsectord (>= Sdfat1location, < Sdfat2location)
' Assign Sdbufferpos = 1 or actual position
' Output variables:
' Sdsectord = the FAT1 sector in which we found a free entry
' Sdbufferpos = the position in this sector
' Returned statuses:
'' 0 = Successful
' 7 = Timeout
' 43 = Free FAT entry found
' 44 = End of FAT and no free entry found
Sdstatus = 0
Sdfatcontinue = 1
Do
' 2 = write unbuffered
' 1 = write buffered
' 0 = not writing
#if Sdwmode = 1
Sdreaddestination = 0
#endif
Gosub Sdreadsector
If Sdstatus = 0 Then
' Look for &H0000 (FAT16) or &H00000000 (FAT32)
Do
Sdtempw = Sdbufferpos
If Sdbuffer(sdtempw) = 0 Then ' Byte 1
Incr Sdtempw
If Sdbuffer(sdtempw) = 0 Then ' Byte 2
If Sdfattype = 2 Then ' FAT16
Sdstatus = 43 ' Free FAT entry found
Sdfatcontinue = 0
Else ' FAT32
Incr Sdtempw
If Sdbuffer(sdtempw) = 0 Then ' Byte 3
Incr Sdtempw
Sdtempb = Sdbuffer(sdtempw) And &B00001111
If Sdtempb = 0 Then ' Byte 4
Sdstatus = 43 ' Free FAT entry found
Sdfatcontinue = 0
End If
End If
End If
End If
End If
If Sdstatus <> 43 Then
Sdbufferpos = Sdbufferpos + Sdfattype
End If
Loop Until Sdfatcontinue = 0 Or Sdbufferpos > Sdbytespersecw
If Sdsectord = Sdfat2location Then
Sdfatcontinue = 0
If Sdstatus = 0 Then
Sdstatus = 44 ' End of FAT and no free entry found
End If
Else
If Sdstatus = 0 Then
Incr Sdsectord
Sdbufferpos = 1
End If
End If
Else
Sdfatcontinue = 0
End If
Loop Until Sdfatcontinue = 0
Return
'-------------------------------------------------------------------------------
Sdwclearcluster:
' Used to write zeroes to sectors 2-Sdsecspercluster (which is then followed either by zeroes to sector 1 or the new directory entry or the . .. entries)
' Input variables:
' Sdclusterd = the cluster we are about to clear
' Output variables:
' Sdbuffer() contains 512*zero
' Returned statuses:
' Sdstatus = 0 Successful
' Sdstatus = 7 Timeout
' Sdstatus = 41 Data rejected due to a CRC error
' Sdstatus = 42 Data rejected due to a Write Error
Gosub Sdlocatesector
Sdsectord = Sdsectord + Sdsecspercluster ' Now we are at the sector after the one we want...
For Sdtempw = 1 To 512 ' Keeping the directory nice and tidy
Sdbuffer(sdtempw) = 0
Next Sdtempw
For Sdtempb2 = 2 To Sdsecspercluster
Decr Sdsectord ' ... and start by subtracting 1 in the loop to start at the last sector in the current cluster
Gosub Sdwritesector
If Sdstatus <> 0 Then
Exit For
End If
Next Sdtempb2
Return
'----------------------------------------------------------------------------
Sdsavefatsec:
' Used to store next cluster or EOC marker in Sd/fat/buffer() at Sdfatbufferpos and save it back to the SD card (FAT1 and FAT2)
' Input variable:
' Sdfatbufferpos = the FAT entry position
' Sdfatsectornum = the FAT sector number
' Sdclusterd = the next cluster number or EOC marker
' Output variable:
' Sdbuffer()
' Returned statuses:
' Sdstatus = 0 Successful
' Sdstatus = 7 Timeout
' Sdstatus = 41 Data rejected due to a CRC error
' Sdstatus = 42 Data rejected due to a Write Error
#if Sdwmode = 2 ' Unbuffered FAT sector
Sdstatus = 0
Sdsectord = Sdfatsectornum ' FAT1
Gosub Sdreadsector
If Sdstatus = 0 Then
Sdtempw = Sdfatbufferpos
For Sdtempb = 1 To Sdfattype
Sdbuffer(sdtempw) = Sdclusterb(sdtempb) ' (Lowest EOC byte can range from &HF8 to &HFF)
Incr Sdtempw
Next Sdtempb
Sdsectord = Sdfatsectornum
Gosub Sdwritesector
If Sdstatus = 0 Then
Sdsectord = Sdsectord - Sdfat1location
Sdsectord = Sdsectord + Sdfat2location ' FAT2
Gosub Sdwritesector
End If
End If
#else ' Buffered FAT sector
Sdtempw = Sdfatbufferpos
For Sdtempb = 1 To Sdfattype
Sdfatbuffer(sdtempw) = Sdclusterb(sdtempb) ' (Lowest EOC byte can range from &HF8 to &HFF)
Incr Sdtempw
Next Sdtempb
Sdstatus = 0
Sdsectord = Sdfatsectornum ' FAT1
Sdwriteorigin = 1 ' Save from Sdfatbuffer
Gosub Sdwritesector
If Sdstatus = 0 Then
Sdsectord = Sdsectord - Sdfat1location
Sdsectord = Sdsectord + Sdfat2location ' FAT2
Gosub Sdwritesector
End If
#endif
Return
#endif
#if Sdwmode > 0 And Sdfsactive = 1
Sdcalculatefatentrycluster:
' Used to calculate the cluster number for the FAT entry specified by Sdsectord and Sdbufferpos
' Input variables:
' Sdsectord = the current FAT sector
' Sdbufferpos = the position within it
' Output variable:
' Sdclusterd holds the cluster number
' Calculate cluster number for this FAT entry
Sdclusterd = Sdsectord - Sdfat1location
Sdclusterd = Sdclusterd * Sdbytespersecw
Sdclusterd = Sdclusterd + Sdbufferpos
Decr Sdclusterd
Sdclusterd = Sdclusterd / Sdfattype
' Incr Sdclusterd ' The first FAT entry refers to cluster 0
Return
'-------------------------------------------------------------------------------
Sdlocatedirsector:
If Sddirclusterd = 0 And Sdfattype = 2 Then
Sdsectord = Sdrootdirlocation + Sddirsecincluster
Decr Sdsectord
Else
Sdclusterd = Sddirclusterd
Gosub Sdlocatesector
End If
Return
#endif
#if Sdwmode > 0
#if Sdfsactive = 1 Or Sdusewipe = 1
Sdpreparedatetime:
' Used to calculate date and time in the format used in directory entries
' Input variables:
' Sdyear
' Sdmonth
' Sdday
' Sdhours
' Sdminutes
' Sdseconds
' Output variables:
' Sddatew holds the date
' Sdtimew holds the time
' Internally used variables:
' Sdtempb
' Date and Time Formats
' Many FAT file systems do not support Date/Time other than DIR_WrtTime and DIR_WrtDate.
' For this reason, DIR_CrtTimeMil, DIR_CrtTime, DIR_CrtDate, and DIR_LstAccDate are actually optional fields.
' DIR_WrtTime and DIR_WrtDate must be supported, however.
' If the other date and time fields are not supported, they should be set to 0 on file create and ignored on other file operations.
' Date Format. A FAT directory entry date stamp is a 16-bit field that is basically a date relative to the MS-DOS epoch of 01/01/1980.
' Here is the format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the 16-bit word):
' Bits 0-4: Day of month, valid value range 1-31 inclusive.
' Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
' Bits 9-15: Count of years from 1980, valid value range 0-127 inclusive (1980-2107).
' Time Format. A FAT directory entry time stamp is a 16-bit field that has a granularity of 2 seconds.
' Here is the format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the 16-bit word).
' Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
' Bits 5-10: Minutes, valid value range 0-59 inclusive.
' Bits 11-15: Hours, valid value range 0-23 inclusive.
' The valid time range is from Midnight 00:00:00 to 23:59:58.
Sddatew = 0
Sddateb(2) = Sdyear - 1980
Sddateb(1) = Sdmonth
Shift Sddateb(1) , Left , 4
Shift Sddatew , Left , 1
Sddateb(1) = Sddateb(1) Or Sdday
Sdtimew = 0
Sdtimeb(2) = Sdhours
Sdtimeb(1) = Sdminutes
Shift Sdtimeb(1) , Left , 2
Shift Sdtimew , Left , 3
Sdtempb = Sdseconds / 2
Sdtimeb(1) = Sdtimeb(1) Or Sdtempb
Return
#endif
#endif
'===============================================================================
#if Sdusefsinfo = 1
Sdreadfsinfo:
' Used to read the fsinfo variables
' Input variable
' None
' Output variables:
' Sdfsinfofreeclustersd holds the number of free clusters
' Sdfsinfonextfreeclusterd points to the next unused cluster (typically not the first free re-usable cluster)
' Returned statuses:
' Sdstatus = 0 Successful
' Sdstatus = 7 Timeout
If Sdfattype = 4 Then
Sdsectord = Sdfat32fsinfolocationw
' 2 = write unbuffered
' 1 = write buffered
' 0 = not writing
#if Sdwmode = 1
Sdreaddestination = 0
#endif
Gosub Sdreadsector
If Sdstatus = 0 Then
Sdfsinfofreeclustersb(1) = Sdbuffer(489)
Sdfsinfofreeclustersb(2) = Sdbuffer(490)
Sdfsinfofreeclustersb(3) = Sdbuffer(491)
Sdfsinfofreeclustersb(4) = Sdbuffer(492)
Sdfsinfonextfreeclusterb(1) = Sdbuffer(493)
Sdfsinfonextfreeclusterb(2) = Sdbuffer(494)
Sdfsinfonextfreeclusterb(3) = Sdbuffer(495)
Sdfsinfonextfreeclusterb(4) = Sdbuffer(496)
End If
End If
Return
'-------------------------------------------------------------------------------
Sdwritefsinfo:
' Used to write the fsinfo variables back to the SD card (after being updated)
' Input variables:
' Sdfsinfofreeclustersd holds the number of free clusters
' Sdfsinfonextfreeclusterd points to the next unused cluster (typically not the first free re-usable cluster)
' Output variable
' None
' Returned statuses:
' Sdstatus = 0 Successful
' Sdstatus = 7 Timeout
' Sdstatus = 41 Data rejected due to a CRC error
' Sdstatus = 42 Data rejected due to a Write Error
If Sdfattype = 4 Then
Sdsectord = Sdfat32fsinfolocationw
' 2 = write unbuffered
' 1 = write buffered
' 0 = not writing
#if Sdwmode = 1
Sdreaddestination = 0
#endif
Gosub Sdreadsector
If Sdstatus = 0 Then
Sdbuffer(489) = Sdfsinfofreeclustersb(1)
Sdbuffer(490) = Sdfsinfofreeclustersb(2)
Sdbuffer(491) = Sdfsinfofreeclustersb(3)
Sdbuffer(492) = Sdfsinfofreeclustersb(4)
Sdbuffer(493) = Sdfsinfonextfreeclusterb(1)
Sdbuffer(494) = Sdfsinfonextfreeclusterb(2)
Sdbuffer(495) = Sdfsinfonextfreeclusterb(3)
Sdbuffer(496) = Sdfsinfonextfreeclusterb(4)
' 2 = write unbuffered
' 1 = write buffered
' 0 = not writing
#if Sdwmode = 1
Sdwriteorigin = 0
#endif
Gosub Sdwritesector
End If
End If
Return
'-------------------------------------------------------------------------------
Sdupdatefsinfo:
' Used when an additional cluster is being put into use
' Input variables:
' Sdfatclusterd = the cluster we have just started using
' Sdfsinfofreeclustersd holds the previous number of free clusters
' Sdfsinfonextfreeclusterd points to the previous unused cluster (typically not the first free re-usable cluster)
' Output variables:
' Sdfsinfofreeclustersd holds the updated number of free clusters
' Sdfsinfonextfreeclusterd points to the next unused cluster (typically not the first free re-usable cluster)
If Sdfattype = 4 Then
If Sdfatclusterd >= Sdfsinfonextfreeclusterd Then
Sdfsinfonextfreeclusterd = Sdfatclusterd + 1
End If
Decr Sdfsinfofreeclustersd
End If
Return
#endif
#if Sdusedirlist = 1
Sddirlist: ' Currently assumes we never fill the SD card completely !!!
' Used to scroll forward and backward through the current directory
' Call this routine in a loop in which you check for certain Sdstatus values
' Reset Sdstatus to 0 before the next step in the loop
' In the very first call to a new directory, assign real or dummy Sdclusterd. If you are listing the root, set it to dummy value 0 to also support FAT16 root dir.
' In the very first call to a new directory, assign Sddirlistarraycounter = 0
' Always assign Sddirlistdirection, 0 = forward, 1 = backward
' Routine-specific Sdstatuses:
' 26 = A file entry has been found
' 27 = A directory entry has been found
' 28 = An available directory space has been found
' 32 = EOD End Of Directory marker found
' 33 = Subdirectory . or .. entry found
' 34 = A volume ID has been found
' 36 = Long file name found
' Since the first directory entry can't have both status and start of directory status, this bit indicates that we have returned to the starting point
' Sddirliststartpoint = 1 ' The directory backward listing has reached the starting point
' Sddirlistendpoint = 1 ' The directory forward listing has reached the end point
' Sddirlistoutside = 1 ' We have scrolled outside of Sddirlistarray() array
' Special handling of root directory
If Sdclusterd = 0 Then
If Sdfattype = 2 Then ' FAT16 root directory
Sdsectorend = Sdsecsinroot
Else
Sdclusterd = Sdfat32rootclusterd ' FAT32 root directory remapped to (normally) cluster 2
End If
End If
' If not in FAT16 root directory, locate the first directory sector
If Sdclusterd > 0 Then
Sdsectorend = Sdsecspercluster
End If
If Sddirlistarraycounter = 0 Then ' Very first call
Sddirliststartpoint = 1 ' Prevent going backward from the starting point
Sddirlistendpoint = 0
Sddirlistarraycounter = 1
Sddirlistarraypointer = 1
Sddirlistoutside = 0
Sdbufferpos = 1
Sdsecincluster = 1
Sddirlistarray(1) = Sdclusterd
If Sdclusterd = 0 Then
Sdsectord = Sdrootdirlocation
Else
Gosub Sdlocatesector
End If
Gosub Sdreadsector
Else ' All subsequent calls
If Sddirlistdirection = 0 Then ' Forward
Sddirliststartpoint = 0
Sdbufferpos = Sdbufferpos + 32
If Sdbufferpos > Sdbytespersecw Then
Sdbufferpos = 1
Incr Sdsecincluster
If Sdsecincluster > Sdsectorend Then
If Sdclusterd > 0 Then
Sdsecincluster = 1
If Sddirlistarraypointer >= Sddirlistarraycounter Then ' We are pointing to or after the last stored cluster
If Sddirlistarraypointer >= Sddirlistarraysize Then ' We are pointing to or after the last entry in Sddirlistarray() array - continue outside of it
Sddirlistoutside = 1 ' We Have Scrolled Outside Of Sddirlistarray() Array
Sddirlistnextclusterd = Sdclusterd ' Store the soon-to-be-previous cluster som we can go back to it if EOD reached later
Gosub Sdlocatenextcluster ' Read next cluster
Gosub Sdcheckclusterreference ' Check if it indicates EOC (in that case Sdstatus = 31)
If Sdstatus = 0 And Sddirlistendpoint = 0 Then
Incr Sddirlistarraypointer
Gosub Sdlocatesector
Gosub Sdreadsector
Sdbufferpos = 1
Else ' In case of EOD marker found, point to the last valid directory entry
Sdclusterd = Sddirlistnextclusterd
Gosub Sdlocatesector
Sdsectord = Sdsectord + Sdsecspercluster
Decr Sdsectord
Sdbufferpos = Sdbytespersecw - 31
Sdsecincluster = Sdsecspercluster
Gosub Sdreadsector
End If
Else ' We are still before the end of the array
Gosub Sdlocatenextcluster ' Read and append next cluster to the cluster array
Gosub Sdcheckclusterreference ' Check if it indicates EOC (in that case Sdstatus = 31)
If Sdstatus = 0 And Sddirlistendpoint = 0 Then
Incr Sddirlistarraycounter
Incr Sddirlistarraypointer
Sddirlistarray(sddirlistarraypointer) = Sdclusterd
Gosub Sdlocatesector
Gosub Sdreadsector
Sdbufferpos = 1
Else ' In case of EOD marker found, point to the last valid directory entry
Sdclusterd = Sddirlistarray(sddirlistarraypointer)
Gosub Sdlocatesector
Sdsectord = Sdsectord + Sdsecspercluster
Decr Sdsectord
Sdbufferpos = Sdbytespersecw - 31
Sdsecincluster = Sdsecspercluster
Gosub Sdreadsector
End If
End If
Else ' Point to the next cluster in the array
Incr Sddirlistarraypointer
Sdclusterd = Sddirlistarray(sddirlistarraypointer)
Incr Sdsectord
Gosub Sdreadsector
End If
Else ' End of the FAT16 root
Sddirlistendpoint = 1
Decr Sdsecincluster ' Point back to the last entry
Sdbufferpos = Sdbytespersecw - 31
End If
Else
Incr Sdsectord
Gosub Sdreadsector
End If
End If
Else ' Backward
If Sddirliststartpoint = 0 Then ' Only allow backward if we are not at the starting point
Sddirlistendpoint = 0 ' We are no longer at the end of the directory
If Sdbufferpos > 32 Then
Sdbufferpos = Sdbufferpos - 32
Sddirliststartpoint = 0
Else
Sdbufferpos = Sdbytespersecw - 31
If Sdsecincluster > 1 Then
Decr Sdsecincluster
Decr Sdsectord
Gosub Sdreadsector
Else
Sdsecincluster = Sdsecspercluster
If Sddirlistoutside = 1 Then ' We are outside of Sddirlistarray() array
Sddirlistcurrclusterd = Sdclusterd ' Locate the cluster before the current cluster
Sdclusterd = Sddirlistarray(sddirlistarraysize)
Do
Sddirlistnextclusterd = Sdclusterd
Gosub Sdlocatenextcluster
Gosub Sdcheckclusterreference ' Check if it indicates EOC (in that case Sdstatus = 31)
Loop Until Sdstatus <> 0 Or Sdclusterd = Sddirlistcurrclusterd
If Sdstatus = 0 Then
Decr Sddirlistarraypointer
If Sddirlistarraypointer <= Sddirlistarraysize Then
Sddirlistoutside = 0
End If
Sdclusterd = Sddirlistnextclusterd
Gosub Sdlocatesector
Sdsectord = Sdsectord + Sdsecspercluster
Decr Sdsectord
Gosub Sdreadsector
End If
Elseif Sddirlistarraypointer > 1 Then
Decr Sddirlistarraypointer
Sdclusterd = Sddirlistarray(sddirlistarraypointer)
Gosub Sdlocatesector
Sdsectord = Sdsectord + Sdsecspercluster
Decr Sdsectord
Gosub Sdreadsector
End If
End If
End If
If Sdclusterd = Sddirlistarray(1) And Sdsecincluster = 1 And Sdbufferpos = 1 Then
Sddirliststartpoint = 1 ' Back at the starting point
End If
End If
End If
End If
' Check this directory entry
If Sdstatus = 0 Then
If Sdbuffer(sdbufferpos) = &H00 Then ' End of directory marker
Sdstatus = 32 ' EOD End Of Directory marker found
Elseif Sdbuffer(sdbufferpos) = &HE5 Then ' Empty directory entry - previous file was deleted
Sdstatus = 28 ' An available directory space has been found
Elseif Sdbuffer(sdbufferpos) = &H2E Then ' One of first two entries in a subdirectory, . contains the current cluster, .. points to parent directory
Sdstatus = 33 ' Subdirectory . or .. entry found
Else
Sdtempw = Sdbufferpos + 11 ' Check attribute byte
Sdtempb = Sdbuffer(sdtempw) Or &B11110000
If Sdtempb = &HFF Then
Sdstatus = 36 ' Long file name found
Elseif Sdbuffer(sdtempw).4 = 1 Then
Sdstatus = 27 ' Subdirectory found
Elseif Sdbuffer(sdtempw).3 = 1 Then
Sdstatus = 34 ' Volume ID found
Else
Sdstatus = 26 ' File found
End If
End If
' Store the current directory entry in Sddirlistdirentry
Sdtempw = Sdbufferpos ' Make a copy of this directory entry byte by byte
Sdtempb = 1
For Sdtempb = 1 To 32
Sddirlistdirentry(sdtempb) = Sdbuffer(sdtempw)
Incr Sdtempw
Next Sdtempb
If Sddirlistdirentry(1) = &H05 Then ' Substitute for 0xE5 in the Japanese KANJI character set
Sddirlistdirentry(1) = &HE5
End If
End If
Return
'-------------------------------------------------------------------------------
Sdcheckclusterreference: ' Check if it indicates EOC (>=&HFFF8 or >=&H0FFFFFF8)
If Sdfattype = 2 Then ' FAT16
Sdclusterb(3) = 0
Sdclusterb(4) = 0
If Sdclusterd > &HFFF7 Then ' EOC End OF Cluster chain found
Sddirlistendpoint = 1
#if Sduseappend = 1
Sdstatus = 31
#endif
End If
Elseif Sdfattype = 4 Then ' FAT32
Sdclusterb(4) = Sdclusterb(4) And &B00001111
If Sdclusterd > &H0FFFFFF7 Then ' EOC End OF Cluster chain found
Sddirlistendpoint = 1
#if Sduseappend = 1
Sdstatus = 31
#endif
End If
End If
Return
#endif
#if Sduselfn = 1
Sdlookforlfn:
' Call after Sddirlist has returned with Sdstatus = 26 (file) or 27 (subdirectory)
For Sdtempb = 1 To 32 ' Copy the directory entry
Sdlfndirentry(sdtempb) = Sddirlistdirentry(sdtempb)
Next Sdtempb
Sddirlistdirection = 1 ' Read the previous directory entry
Sdlfncontinue = 1
Sdlfnfound = 0
Sdtempb4 = 1 ' Expected starting point for the long filename ordinal
Do
If Sddirliststartpoint = 0 Then ' 0 = the directory backward listing has NOT reached the starting point
Sdstatus = 0
Gosub Sddirlist
If Sdstatus = 36 Then
Sdtempb = Sddirlistdirentry(1) And &B00011111
If Sdtempb = Sdtempb4 Then ' Expected ordinal number found
If Sdtempb = 1 Then
Gosub Sdcalculatelfnchecksum
End If
If Sdlfnchecksum = Sddirlistdirentry(14) Then ' Matching checksum found
Sdtempb = Sdtempb4 - 1 ' Calculate the starting point in the string for these 13 characters
Sdtempb = Sdtempb * 13
Sdtempb2 = 0
Do
Sdtempb2 = Sdtempb2 + 2
Incr Sdtempb
If Sdtempb2 = 12 Then
Sdtempb2 = 15
Elseif Sdtempb2 = 27 Then
Sdtempb2 = 29
End If
If Sddirlistdirentry(sdtempb2) > 0 And Sddirlistdirentry(sdtempb2) < 255 Then ' Don't append NULL &H0000 and padding &HFFFF at the end of the name
Sdlfnb(sdtempb) = Sddirlistdirentry(sdtempb2) ' Append the characters from this directory entry
End If
Loop Until Sdtempb2 = 31
Incr Sdtempb4
If Sddirlistdirentry(1).6 = 1 Then ' Stop bit found
Sdlfnfound = 1 ' We have read a complete long filename
Sdlfncontinue = 0
End If
Else
Sdlfncontinue = 0 ' Checksum is not matching
End If
Else
Sdlfncontinue = 0 ' Expected ordinal number not found
End If
Else
Sdlfncontinue = 0 ' Record is not a long filename entry
End If
Else
Sdlfncontinue = 0 ' Current record is the very first directory entry
End If
Loop Until Sdlfncontinue = 0
Return
'-------------------------------------------------------------------------------
Sdcalculatelfnchecksum:
Sdlfnchecksum = Sdlfndirentry(1)
For Sdtempb = 2 To 11
Sdtempb2 = Sdlfnchecksum
Shift Sdtempb2 , Right , 1
Sdtempb3 = Sdlfnchecksum And &B00000001
Shift Sdtempb3 , Left , 7
Sdlfnchecksum = Sdlfndirentry(sdtempb) + Sdtempb2
Sdlfnchecksum = Sdlfnchecksum + Sdtempb3
Next Sdtempb
Return
'-------------------------------------------------------------------------------
#if Sduselfncompare = 1
Sdcomparefilenames:
Sdlfnfound = 0
Sdlfncontinue = 1
Sdtempb = 0
Do
Incr Sdtempb
If Sdlfnb(sdtempb) <> 0 Then ' End marker
If Sdlfnb(sdtempb) <> Sdlfn2b(sdtempb) Then
Sdlfncontinue = 0
End If
Else
Sdlfncontinue = 0
If Sdtempb > 1 Then
Sdlfnfound = 1
End If
End If
Loop Until Sdlfncontinue = 0 Or Sdtempb = 255
Return
#endif
#endif
'-------------------------------------------------------------------------------
#if Sdusewipe = 1
Sdwipe:
' Used to clean out and reset FAT1, FAT2, root directory, and fsinfo sector.
' This is NOT a real formatting!
' Input variables
' Sdentrynames with all 11 characters including space characters
' Sdyear
' Sdmonth
' Sdday
' Sdhours
' Sdminutes
' Sdseconds
For Sdtempw = 1 To 512
Sdbuffer(sdtempw) = 0
Next Sdtempw
#if Sdwmode = 1
Sdwriteorigin = 0
#endif
If Sderaseall = 1 Then ' Erase everything from the beginning of FAT1 to the last sector in this partition
Sdtempd = Sdtotsecsd + Sdpartstart
Else
Sdtempd = Sddataareastart + Sdsecspercluster ' Erase everything from the beginning of FAT1 to the end of cluster 2
End If
Sdstatus = 0
Sdsectord = Sdfat1location
While Sdsectord < Sdtempd
Gosub Sdwritesector
Incr Sdsectord
Wend
Sdstatus = 0
' Then write FAT pointers to dummy clusters 0 and 1 (and 2 if FAT32)
Sdbuffer(1) = &HF8
Sdbuffer(2) = &HFF
Sdbuffer(3) = &HFF
If Sdfattype = 2 Then ' FAT16 F8FF FFFF
Sdbuffer(4) = &HFF
Else
Sdbuffer(4) = &H0F ' FAT32 F8FFFF0F FFFFFFFF FFFFFF0F
For Sdtempb = 5 To 11
Sdbuffer(sdtempb) = &HFF
Next Sdtempb
Sdbuffer(12) = &H0F
End If
Sdsectord = Sdfat1location
Gosub Sdwritesector
Sdsectord = Sdfat2location
Gosub Sdwritesector
' Then store the new volume ID in the root directory
' 1-11 = Name
' 12 = &H08
' 23-26 = timestamp
For Sdtempb = 1 To 11
Sdbuffer(sdtempb) = Sdentrynameb(sdtempb)
Next Sdtempb
Sdbuffer(12) = &H08
Gosub Sdpreparedatetime
Sdbuffer(23) = Sdtimeb(1)
Sdbuffer(24) = Sdtimeb(2)
Sdbuffer(25) = Sddateb(1)
Sdbuffer(26) = Sddateb(2)
If Sdfattype = 2 Then
Sdsectord = Sdrootdirlocation
Else
Sdsectord = Sddataareastart
End If
Gosub Sdwritesector
' Finally update fsinfo sector
#if Sdusefsinfo = 1
If Sdfattype = 4 Then
Gosub Sdreadfsinfo
Sdfsinfofreeclustersd = Sdtotsecsd - Sddataareastart
Incr Sdfsinfofreeclustersd
Sdfsinfofreeclustersd = Sdfsinfofreeclustersd / Sdsecspercluster
Decr Sdfsinfofreeclustersd ' The first cluster (2) is used by the FAT32 root directory
Sdfsinfonextfreeclusterd = 3
Sdbuffer(489) = Sdfsinfofreeclustersb(1)
Sdbuffer(490) = Sdfsinfofreeclustersb(2)
Sdbuffer(491) = Sdfsinfofreeclustersb(3)
Sdbuffer(492) = Sdfsinfofreeclustersb(4)
Sdbuffer(493) = Sdfsinfonextfreeclusterb(1)
Sdbuffer(494) = Sdfsinfonextfreeclusterb(2)
Sdbuffer(495) = Sdfsinfonextfreeclusterb(3)
Sdbuffer(496) = Sdfsinfonextfreeclusterb(4)
Gosub Sdwritefsinfo
End If
#endif
Return
#endif
'-------------------------------------------------------------------------------
#if Sdusesizeinfo = 1
Sdreceivebuffer18:
Spiin Sdbuffer(1) , 18
Return
'-------------------------------------------------------------------------------
Sdrequestcid:
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
' Gosub Sdcommand1
Reset Sd_cs
' Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
' Gosub Sdcommand1
Sdcommand = &H4A ' Send CMD10
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &H1D
Gosub Sdcommand1
Sdexpectedresponse = &H00 ' First response after CMD9
Gosub Sdseekresponse
If Sdstatus = 0 Then
Sdexpectedresponse = &HFE ' Start token after CMD9
Gosub Sdseekresponse
If Sdstatus = 0 Then
Gosub Sdreceivebuffer18
End If
End If
Reset Sd_cs
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
'Gosub Sdcommand1
Return
'-------------------------------------------------------------------------------
Sdrequestcsd:
' 16 bytes, MSB first
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
'Gosub Sdcommand1
Reset Sd_cs
' Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
' Gosub Sdcommand1
Sdcommand = &H49 ' Send CMD9
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &HAF
Gosub Sdcommand1
Sdexpectedresponse = &H00 ' First response after CMD9
Gosub Sdseekresponse
If Sdstatus = 0 Then
Sdexpectedresponse = &HFE ' Start token after CMD9
Gosub Sdseekresponse
If Sdstatus = 0 Then
Gosub Sdreceivebuffer18
End If
End If
Reset Sd_cs
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
'Gosub Sdcommand1
Return
'-------------------------------------------------------------------------------
Sdcalculatesize: ' Can be called after Sdrequestcsd
' v2+
' Bits 127-126 = 01 (byte 1.6 = 1, 1.7 = 0)
' C_SIZE 22 bits 69-48 (bytes 8&B00111111,9,10)
' Size = C_SIZE * 2^19
' v1.x:
' Bits 127-126 = 00
' C_SIZE 12 bits 73-62 (bytes 7&B00000011, 8, 9&B11000000)
' C_SIZE_MULT 3 bits 49-47 (bytes 10&B00000011, 11&B10000000)
' READ_BL_LEN 4 bits 84-80 (byte 6&B00001111)
' Size = (C_SIZE+1) * 2^(C_SIZE_MULT+2) * 2^READ_BL_LEN
If Sdbuffer(1).6 = 1 And Sdbuffer(1).7 = 0 Then
Sdcsize = &B00111111 And Sdbuffer(8) ' v2+
Sdcsize = Sdbuffer(8)
Shift Sdcsize , Left , 8
Sdcsize = Sdcsize + Sdbuffer(9)
Shift Sdcsize , Left , 8
Sdcsize = Sdcsize + Sdbuffer(10)
Incr Sdcsize
Shift Sdcsize , Right , 1 ' Size in MB (1k = 1024)
Else
Sdcsize = &B00000011 And Sdbuffer(7) ' v1.x ok
Shift Sdcsize , Left , 8
Sdcsize = Sdcsize + Sdbuffer(8)
Shift Sdcsize , Left , 8
Sdcsize = Sdcsize + Sdbuffer(9)
Shift Sdcsize , Right , 6
Sdcsizemult = Sdbuffer(10) And &B00000011
Shift Sdcsizemult , Left , 1
If Sdbuffer(11).7 = 1 Then
Incr Sdcsizemult
End If
Sdreadbllen = Sdbuffer(6) And &B00001111
Incr Sdcsize
Shift Sdcsize , Left , Sdcsizemult
Shift Sdcsize , Left , Sdreadbllen
Shift Sdcsize , Right , 18 ' Size in MB (1k = 1024)
' Size = (C_SIZE+1) * 2^(C_SIZE_MULT+2) * 2^READ_BL_LEN
End If
Return
'-------------------------------------------------------------------------------
Sdrequeststatus:
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
' Gosub Sdcommand1
Reset Sd_cs
' Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
' Gosub Sdcommand1
Sdcommand = &H4D ' Send CMD13
Gosub Sdcommand1
Sdcommand = &H00
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Gosub Sdcommand1
Sdcommand = &H0D
Gosub Sdcommand1
Sdtempb = 0
Do
Gosub Sdresponse1
Incr Sdtempb
Loop Until Sdresponse(1) <> 255 Or Sdtempb = 255
If Sdresponse(1) = 255 Then
Sdstatus = 6 ' Timeout in CMD13
Else
Gosub Sdresponse2_5
End If
Reset Sd_cs
Sdcommand = &HFF ' Clock SD to complete job
Gosub Sdcommand1
'Gosub Sdcommand1
Return
#endif
'-------------------------------------------------------------------------------
#if Sdusecrc7 = 1
Sdcheckresponsecrc7: ' Receives the SD response and checks the CRC
Sdcrc7byte = Sdcrc7(sdresponse(1))
If Sdresponse(6) <> Sdcrc7byte Then
Sdstatus = 129
End If
Return
'-------------------------------------------------------------------------------
Function Sdcrc7(byval Sdarray As Byte) As Byte ' Calculates CRC7 for the 5 first bytes of Sdarray
Local Sdm As Byte ' Array byte loop
Local Sdk As Byte ' Byte bit loop
Local Sdx As Byte ' Current array byte value (could be skipped and work only with Sdarray(m))
Local Sdcrc7sum As Byte ' The CRC7 being calculated
Local Sdcalcstep As Byte ' Intermediate step necessary for BASCOM
Sdcrc7sum = 0
For Sdm = 1 To 5 ' Loop through the 5 first array bytes
Sdx = Sdarray(sdm)
For Sdk = 0 To 7 ' Loop through the byte bitwise
Shift Sdcrc7sum , Left , 1 ' Shift CRC left
Sdcalcstep = Sdx Xor Sdcrc7sum
If Sdcalcstep.7 = 1 Then ' Do XOR if MSB = 1
Sdcrc7sum = Sdcrc7sum Xor &H9
End If
Shift Sdx , Left , 1 ' Shift current array byte value left
Next Sdk
Sdcrc7sum = Sdcrc7sum And &H7F
Next Sdm
Shift Sdcrc7sum , Left , 1 ' Shift left and add 1
Sdcrc7sum = Sdcrc7sum + 1
Sdcrc7 = Sdcrc7sum
End Function
#endif
'===============================================================================