Shop OBEX P1 Docs P2 Docs Learn Events
Better than sliced bread! SD3.01 FAT16/32 Driver Updated! - Page 4 — Parallax Forums

Better than sliced bread! SD3.01 FAT16/32 Driver Updated!

12467

Comments

  • MacTuxLinMacTuxLin Posts: 821
    edited 2011-05-07 00:37
    Jorge P wrote: »
    Put a donate link and I'd be glad to donate something for your ds1302 version..

    Agree. I will too...
  • william chanwilliam chan Posts: 1,326
    edited 2011-05-07 00:57
    Kye,

    Do you have a paypal account?
    I can transfer USD 5 to your account.
  • william chanwilliam chan Posts: 1,326
    edited 2011-05-08 21:06
    Hi Kye,

    I am confused with the mountPartition command.
    Is this the simple way to do it?

    if fat.FATEngineStart(sd_do_pin,sd_clk_pin,sd_di_pin,sd_cs_pin,-1,-1,-1,-1,-1)
    \fat.mountPartition(0)
    if fat.partitionMounted
    lcd.str(string("SDCard Mounted"))
    else
    lcd.str(string("No SDCard !"))
  • KyeKye Posts: 2,200
    edited 2011-05-09 07:17
    Yeah, that works. Also use the "partitionError" command to get the error number - which are enumed in the constant section. If no error occured partition error returns zero.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-05-09 07:23
    @william chan, this is a copy of kyedos, which shows one way to start the driver and mount it. The RTC has been disabled.
    {{
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // SD3.01 FATEngine Demo
    //
    // Author: Kwabena W. Agyeman
    // Updated: 1/2/2011
    // Designed For: P8X32A
    // Version: 1.0
    //
    // Copyright (c) 2011 Kwabena W. Agyeman
    // See end of file for terms of use.
    //
    // Update History:
    //
    // v1.0 - Original release - 7/27/2010.
    // v1.1 - Updated code for new file system driver - 1/2/2011.
    // Nyamekye
    
     modifications by J. Moxham Dr_Acula to create KyeDOS v3. Pins setup for the Dracblade (similar to the Propeller Demo Board)
     - name this file KyeDOS3
     - changed sd pins to 12,13,14,15. WP and CD = -1
     - RTC DAT and CLK -1
     - Audio and Microphone pins all set to -1
     comment out   'adc: "PCM2.1_ADCEngine.spin"   and compile to find all PUBs referenced by this
     comment out adcengine start and PRI programTapeWavFile  and   dac: "PCM2.1_DACEngine.spin"  and PRI programPlayWavFile
     comment out programPlayWavFile(stringPointer) and  programTapeWavFile(stringPointer) and PRI scope
     comment out   if(_VGA_Enabled) group
     startup is   ifnot( fat.FATEngineStart(_SD_DO, _SD_CLK, _SD_DI, _SD_CS, _SD_WP, _SD_CD, _RTC_DAT, _RTC_CLK, -1) and {
           } com.COMEngineStart(_receiverPin, _transmitterPin, _baudRateSpeed))
      comment out   'bmp: "VGA64_BMPEngine.spin"   _VGA_BG_Color = bmp#black   _VGA_FG_Color = bmp#green      
      add OBJ   VT100: "VGA_1024_VT100"       ' VGA VT100 Terminal Driver
      copied VGA_1024_VT100.spin and VGA_80x40.spin to the working folder
      added VT100 startup code - now have a blinking white cursor on a blue screen. 80x40 text.
      commented out '  long scopeSwtich, scopeStack[17], scopeHeap[((_VGA_H_Res * _VGA_V_Res) * 2) / 32]  
      (need to watch memory F8 here while adding more objects to the OBJ section)
      added   key : "Pocketerm_Keyboard"           ' pocketerm keyboard
      added   lcd : "DracLCD"                     ' 20x4 LCD driver for dracblade
      added   delay: "Timing"                     ' for millisecond delays
      added the keyboard startup   key.startx(26, 27, %100, 40)             'Start Keyboard Driver  pin,pin,num,repeatrate
      added lcd.start    - this is dracblade specific with a 20x4 local keyboard. You can leave this out if not used
      commented out the existing .com driver. There is a bug with xmodem with corrupted bytes above 100 in the buffer, this does not show up with typing as the buffer never gets this full
      added 2 port serial driver (variation on Tim Moore's code). Copied object to current directory    sio     : "pcFullDuplexSerial2FC"  
      F8 now fails with all instances of .com
      Added some printstring routines that send data to VGA and to the serial port and the LCD display and do CRLF properly
      added sio.addport. removed startup for the .com
      global search and replace "com.transmitstring" to "printstring"
      global search and replace "com.transmitbyte" to "printchar"
      edited a line with #comquotation marks. Keep doing F8 to find errors
      edited PRI shellline so it can accept Backspace and can read from keyboard and serial port
      found that the string engine syntax has changed. putcharacter is now buildstring
      found that the string engine syntax has chnaged. getcharacters is now builtstring
      rewrote PRI shelldecision
      replaced instances of quotationmarks from the serial driver to a local variable quote (ascii 34)
      added local contstants Horizontal_tab, Linefeed, CarriageReturn. Did global find/replace
      F8 finally compiles with no errors!
      added sio.start, print signon message
      added PRI DisplayBinaryFiles. Changed to the new syntax for reading entries
      ** edited the string engine - added PUB endsWithString(stringToSearch, stringToFind) '' 12 Stack Longs 
      baud rate 38400 for testing
      comment out '  _newLineCharacter = 13  , replace with crlf
      go through the DAT shellProgramHelpStrings and replace _newlinecharacter with byte 13 and byte 10  
      commented out the wav file commands in the help section (can always uncomment them later) amd the PRI
      replace instances of     PrintChar(_newLineCharacter) with crlf or with printstringcr. Multiple instances
      removed "are you sure" from the boot command
      edited the 'help' commands so they each fit on one line
      moved the check for mounted card after every command to above the loop so only checks once.
      working on a very strange bug in programDIR - if send more than a certain number of tabs to the VT100 the whole spin program shuts down. Possibly overrunning a buffer and overwriting code. Need a better solution to line up files in columns
      also shows up with the LS command.
      added a constant _LCD_Enabled so can easily turn this off for boards that do not have it
      added a constant for VGA_enabled and also Serial_Enabled - can turn each of these on or off
      added a printdecimal for debugging
      added running .exe files, eg to run MOVIE.EXE type movie see programCommand
      added simple wildcards to the "DIR" command, so can type dir *.exe and see just these files
      changed the command line (.exe files) so it stores the command line in a text file COMMAND.TXT ready for other programs to read.
     
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    }}
    
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      _leftChannelVolume = 32
      _rightChannelVolume = 32
    
      _baudRateSpeed = 115200 ' Teraterm does not work at this speed. Use hyperterminal or other programs
    '  _newLineCharacter = 13
      _clearScreenCharacter = 16
      _homeCursorCharacter = 1
    
      _receiverPin = 31
      _transmitterPin = 30
    
      _RTC_DAT = -1 ' -1 ifnot installed.
      _RTC_CLK = -1 ' -1 ifnot installed.
    
      _VGA_Enabled = true ' VGA display on
      _VGA_PinGroup = 2
    
      _LCD_Enabled = true ' false or true for 20x4 LCD display
      _Serial_Enabled = true ' send output to serial port 
    
      _leftChannelAudioPin = -1 ' -1 ifnot installed.
      _rightChannelAudioPin = -1 ' -1 ifnot installed.
    
      _leftMicFeedBackPin = -1 ' -1 ifnot installed.
      _leftMicInputPin = -1 ' -1 ifnot installed.
    
      _rightMicFeedBackPin = -1 ' -1 ifnot installed.
      _rightMicInputPin = -1 ' -1 ifnot installed.
    
      _SD_DO = 12
      _SD_CLK = 13
      _SD_DI = 14
      _SD_CS = 15
      _SD_WP = -1 ' -1 ifnot installed.
      _SD_CD = -1 ' -1 ifnot installed.
      
      Quote = 34
      Horizontal_Tab = 9
      Line_Feed = 10
      Carriage_Return =13
    
    OBJ
    
      fat: "SD3.01_FATEngine.spin"
      taf: "SD3.01_FATEngine.spin"
      rtc: "DS1307_RTCEngine.spin"
      'com: "RS232_COMEngine.spin"
      str: "ASCII0_STREngine.spin"
      'adc: "PCM2.1_ADCEngine.spin"
      'dac: "PCM2.1_DACEngine.spin"
      'bmp: "VGA64_BMPEngine.spin"
      VT100: "VGA_1024_VT100"       ' VGA VT100 Terminal Driver
      key : "Pocketerm_Keyboard"           ' pocketerm keyboard
      lcd : "DracLCD"                     ' 20x4 LCD driver for dracblade
      delay: "Timing"                     ' for millisecond delays
      sio     : "pcFullDuplexSerial2FC" 
    
    VAR
    '
    '  long scopeSwtich, scopeStack[17], scopeHeap[((_VGA_H_Res * _VGA_V_Res) * 2) / 32]
       byte CommandLine[80] ' complete command line as typed in
       byte LineOfText1[80] ' general purpose string buffer
       byte LineOfText2[80] ' general purpose string buffer
       byte LineOfText3[80] ' general purpose string buffer
    
    PUB shell
    
      VT100.start(16)                                       ' start the vga driver VGA starts on pin 16
      VT100.startcursor                                     ' start the cursor
      VT100.color(%00001000_11111100)                       ' Blue_White  blue =3/4 power 10, white =full                                  
      VT100.cursorset(5)                                    ' cursor type 5
      VT100.clearscreen                                     ' clear the screen
      'vt100.str(string("Testing for SD card",13,10))        ' helpful message if card is out, better than just a blank screen                                  
      key.startx(26, 27, %100, 40)             'Start Keyboard Driver  pin,pin,num,repeatrate
      sio.AddPort(0, _receiverPin, _transmitterPin, -1, -1, 0, 0, _baudRateSpeed)
      'sio.AddPort(1, _port2receiverPin, _port2transmitterPin, -1, -1, 0, 0, 120000) ' shamcom works 108000 to 120000 (teraterm can't do 112800 even though it can select it)
      'sio.tx(1,65)    ' test port 2
    
      if(_LCD_Enabled)
        lcd.start ' start the LCD display
    
      ifnot( fat.FATEngineStart(_SD_DO, _SD_CLK, _SD_DI, _SD_CS, _SD_WP, _SD_CD, _RTC_DAT, _RTC_CLK, -1))
    '        com.COMEngineStart(_receiverPin, _transmitterPin, _baudRateSpeed)) 
    '        adc.ADCEngineStart(_leftMicInputPin, _leftMicFeedBackPin, _rightMicInputPin, _rightMicFeedBackPin, 16_000)
    '        dac.DACEngineStart(_leftChannelAudioPin, _rightChannelAudioPin, 0) )
        reboot
    
    '  dac.leftChannelVolume(_leftChannelVolume)
    '  dac.rightChannelVolume(_rightChannelVolume)
    
    '  if(_VGA_Enabled)
    '    ifnot( bmp.BMPEngineStart(_VGA_PinGroup, 1, _VGA_H_Res, _VGA_V_Res, @scopeHeap) and {
    '         } (cognew(scope, @scopeStack) <> -1))
    '      reboot
    
      sio.start             ' start the com port  note can't print anything to screen till sio.start unless only do to vga screen
      PrintStringCR(string("Testing for SD card"))  ' helpful message if card is out, better than just a blank screen
      fat.mountPartition(0)     ' mount the sd card
      VT100.clearscreen                                     ' if the sd card is not inserted, will leave the message on the screen as a reminder. If it is inserted, start with a blank screen
      PrintStringCR(string("*** KyeDOS SD card operating system v3.01 by Kwabena W. Agyeman & J. Moxham ***"))
      PrintString(string("Type Help for command listing"))
      crlf
      'DisplayBinaryFiles                    ' display all files ending in .BIN
      sio.rxflush(0)                                        ' in case try to send an xmodem transfer
    
      ifnot(fat.partitionMounted)
        printstring(string("Card Not Mounted "))   
    
      repeat
    
        result := shellLine(string(">"))
        'printstring(string("Running command: ")) ' commands run so quickly no need to print this
        'printstring(result)
        str.copy(result,@commandline)' save for later on if this is a command line with parameters eg "xmodem r filename.txt"
        crlf
    
        result := \shellCommands(result)
        printstring(result)
        crlf
    
        if(fat.partitionError)
          crlf
    
    PRI shellCommands(stringPointer)
    
      stringPointer := str.tokenizeString(stringPointer)
    
      programClear(stringPointer)
      programEcho(stringPointer)
    
      programReboot(stringPointer)
      programHelp(stringPointer)
    
      programMount(stringPointer)
      programUnmount(stringPointer)
    
      programFreeSpace(stringPointer)
      programUsedSpace(stringPointer)
    
      programList(stringPointer)
      programDIR(stringPointer)
      programConcatenate(stringPointer)
    
      programChangeDirectory(stringPointer)
      programChangeAttributes(stringPointer)
    
      programMove(stringPointer)
      programRemove(stringPointer)
      programEra(stringPointer)  ' same as programremove
    
      programMakeFile(stringPointer)
      programMakeDirectory(stringPointer)
    
      programCopy(stringPointer)
      programDifference(stringPointer)
    
      programBoot(stringPointer)
      programFormat(stringPointer)
    
      programDate(stringPointer)                                      
      programTime(stringPointer)
    
    '  programRamSet(stringPointer)
    '  programRamGet(stringPointer)
    
    '  programSQW(stringPointer)
    '  programOUT(stringPointer)
    
    '  programPlayWavFile(stringPointer)
    '  programTapeWavFile(stringPointer)
    
      programDump(stringPointer)
      programTest(stringPointer)
    
      programCommand(StringPointer) ' is it a command ie a .exe file?
    
      abort string("Command Not Found! Try Help")
      crlf
    {
    PRI shellLine(prompt)
    
      printstring(prompt)
    
      repeat
        result := com.receivedByte
        str.buildString(result)
      while((result <> carriage_return) and (result <> carriage_return) and (result <> com#null))
    
      return str.trimString(str.builtString(true))
    }
    
    PRI shellLine(prompt)
    
      PrintString(prompt)
      repeat
    
          result:=0
          repeat
            if key.gotkey <>0        ' scan both the local keyboard and the com port
              result:=key.getkey
            if sio.rxavail(0)
              result :=sio.rx(0)  
          until result <>0           ' until something comes in either port
        
        if result==200
          result :=8                ' backspace keycode is 200 but convert to 8
        PrintChar(result)             ' send to vga and to com port
        str.buildString(result)     ' build an input string
      while((result <> 13) and (result <> 10) and (result <> 0))
    
      return str.trimString(str.builtstring(true))
    
    {
    PRI shellDecision(prompt, characterToFind, otherCharacterToFind)
    
      printstring(prompt)
    
      repeat
    
        prompt := com.receivedByte
        result or= ((prompt == characterToFind) or (prompt == otherCharacterToFind))
    
        if(prompt == com#backspace)
          return false
    
      while((prompt <> carriage_return) and (prompt <> carriage_return) and (prompt <> com#null))
    }
    PRI shellDecision(prompt, characterToFind, otherCharacterToFind)
    
      PrintString(prompt~)  
    
      repeat
        result := key.getkey
        prompt or= ((result == characterToFind) or (result == otherCharacterToFind))
      while((result <> 13) and (result <> 10) and (result <> 0))  
    
      return prompt  
    
    PRI programClear(stringPointer)
    
      ifnot(str.stringCompareCI(string("clear"), stringPointer))
        rtc.pauseForMilliseconds(500)
        printstring(string(_clearScreenCharacter, _homeCursorCharacter))
        abort false
    
    PRI programEcho(stringPointer)
    
      ifnot(str.stringCompareCI(string("echo"), stringPointer))
        printstring(str.trimString(stringPointer += 5))
        crlf
        abort false
    
    PRI programReboot(stringPointer)
    
      ifnot(str.stringCompareCI(string("reboot"), stringPointer))
        printstring(string("Rebooting "))
    
        repeat 3
          rtc.pauseForMilliseconds(500)
          printstring(string(". "))
    
        rtc.pauseForMilliseconds(500)
        reboot
    
    PRI programHelp(stringPointer)
    
      ifnot(str.stringCompareCI(string("help"), stringPointer))
        printstring(@shellProgramHelpStrings)
        abort false
    
    DAT shellProgramHelpStrings
    
      byte 13,10
      byte "Command Listing"
      byte 13,10
      byte "<clear>                        - Clear the screen."
      byte 13,10
      byte "<echo> <string>                - Echo the string argument."
      byte 13,10
      byte "<reboot>                       - Reboot the propeller chip."
      byte 13,10
      byte "<help>                         - Show the command listing."
      byte 13,10
      byte "<mount> <partition>            - Mount the file system."
      byte 13,10
      byte "<unmount>                      - Unmount the file system."
      byte 13,10
      byte "<df> <fast>                    - Free sector count. F=Fast. 1 Sector = 512 Bytes"
      byte 13,10
      byte "<du> <fast>                    - Used sector count. F=Fast. 1 Sector = 512 Bytes"
      byte 13,10
      byte "<ls> <all>                     - List contents of the working directory. A=All."
      byte 13,10
      byte "<dir>                          - List contents. Includes wildcards eg dir *.exe"
      byte 13,10
      byte "<cat> <file> <hex>             - Print the ASCII interpreted contents of a file."
      byte 13,10
      byte "<cd> <directory>               - Change directory."
      byte 13,10
      byte "<chmod> <entry> <attributes>   - Change Attrib: R=R/O, H=Hidden, S=Sys, A=Arch"
      byte 13,10
      byte "<mv> <oldPath> <newPath>       - Move a file or directory."
      byte 13,10
      byte "<rm> <path> or <era>           - Remove a file or directory."
      byte 13,10
      byte "<mkfile> <name>                - Make a new file."
      byte 13,10
      byte "<mkdir> <name>                 - Make a new directory."
      byte 13,10
      byte "<cp> <oldPath> <newPath>       - Copy a file."
      byte 13
      byte 10
      byte "<diff> <path> <path>           - Print differences between two files."
      byte 13,10
      byte "<boot> <file>                  - Reboot the propeller chip from a file."
      byte 13,10
      byte "<filename>                     - Reboot and run filename.exe"
      byte 13,10
      byte "<format> <partition>           - Format and mount the file system."
      byte 13,10
      byte "<date>                         - Display the current date and time."
      byte 13,10
      byte "<time>                         - Change the current date and time."
      byte 13,10
    '  byte "<set> <byteNumber> <byteValue> - Set the value of a SRAM byte on the RTClock."
    '  byte 13,10
    '  byte "<get> <byteNumber>             - Get the value of a SRAM byte on the RTClock."
    '  byte 13,10
    '  byte "<sqw> <frequencyNumber>        - Set the frequecny of the SWQ / OUT pin. (0=1Hz, 1=4096Hz, 2=8192Hz, 3=32768Hz)."
    '  byte 13,10
    '  byte "<out> <digitalState>           - Set the state of the SWQ / OUT pin. (0=low, 1=high)."
    '  byte 13,10
    '  byte "<play> <file>                  - Play a WAV file."
    '  byte 13,10
    '  byte "<tape> <file>                  - Tape a WAV file."
    '  byte 13,10
      byte "<dump>                         - Dump the ROM font."
      byte 13,10
      byte "<test>                         - Speed test the file system."
      byte 13,10, 0
    
    PRI programMount(stringPointer)
    
      ifnot(str.stringCompareCI(string("mount"), stringPointer))
        if(fat.mountPartition(stringPointer := str.decimalToInteger(str.tokenizeString(0))))
          printstringcr(string("Please unmount the disk next time"))
        taf.mountPartition(stringPointer)
    
        printstring(fat.partitionWriteProtected & string("Write Protected "))
        printstring(string("Disk 0x"))
        printstring(str.integerToHexadecimal(fat.partitionDiskSignature, 8))
        printstring(string(" with Volume ID 0x"))
        printstring(str.integerToHexadecimal(fat.partitionVolumeIdentification, 8))
        printstring(string(" a.k.a "))
        printstring(str.trimString(fat.partitionVolumeLabel))
        printstringcr(string(" ready"))
    
        abort 0
    
    PRI programUnmount(stringPointer)
    
      ifnot(str.stringCompareCI(string("unmount"), stringPointer))
        taf.unmountPartition
        fat.unmountPartition
        printstringcr(string("Disk ready for removal"))
    
        abort 0
    
    PRI programFreeSpace(stringPointer)
    
      ifnot(str.stringCompareCI(string("df"), stringPointer))
    
        printstring(string("The ", quote))
        printstring(str.trimString(fat.partitionFileSystemType))
        printstring(string(quote, " ", quote))
        printstring(str.trimString(fat.partitionVolumeLabel))
        printstring(string(quote, " parition has "))
        printstring(1 + str.integerToDecimal(fat.partitionCountOfClusters, 10))
        printstring(string(" clusters and "))
        printstring(1 + str.integerToDecimal(fat.partitionDataSectors, 10))
        printstring(string(" total sectors, where each cluster is "))
        printstring(1 + str.integerToDecimal(fat.partitionSectorsPerCluster, 3))
        printstring(string(" sectors large and each sector is "))
        printstring(1 + str.integerToDecimal(fat.partitionBytesPerSector, 3))
        printstringcr(string(" bytes"))
        printstring(string("And there are... please wait... "))
        printstring(1 + str.integerToDecimal(fat.partitionFreeSectorCount(byte[str.tokenizeString(0)]), 10))
        printstringcr(string(" Free Sectors"))
    
        abort 0
    
    PRI programUsedSpace(stringPointer)
    
      ifnot(str.stringCompareCI(string("du"), stringPointer))
    
        printstring(string("The ", quote))
        printstring(str.trimString(fat.partitionFileSystemType))
        printstring(string(quote, " ", quote))
        printstring(str.trimString(fat.partitionVolumeLabel))
        printstring(string(quote, " parition has "))
        printstring(1 + str.integerToDecimal(fat.partitionCountOfClusters, 10))
        printstring(string(" clusters and "))
        printstring(1 + str.integerToDecimal(fat.partitionDataSectors, 10))
        printstring(string(" total sectors where each cluster is "))
        printstring(1 + str.integerToDecimal(fat.partitionSectorsPerCluster, 3))
        printstring(string(" sectors large and each sector is "))
        printstring(1 + str.integerToDecimal(fat.partitionBytesPerSector, 3))
        printstringcr(string(" bytes"))
        printstring(string("And there are... please wait... "))
        printstring(1 + str.integerToDecimal(fat.partitionUsedSectorCount(byte[str.tokenizeString(0)]), 10))
        printstringcr(string(" Used Sectors"))
    
        abort 0
    
    PRI programList(stringPointer) : counter | buffer, flag
    
      ifnot(str.stringCompareCI(string("ls"), stringPointer))
        stringPointer := str.tokenizeString(0)
        flag := (str.findCharacter(stringPointer, "A") or str.findCharacter(stringPointer, "a"))
        fat.listEntries("W")
    
        repeat while(buffer := fat.listEntries("N"))
          printstring(buffer)
          ifnot(flag)
    
            if(++counter // 8)
              PrintChar(horizontal_tab)
            else
              crlf
    
          else
    
            if(fat.listIsReadOnly)
              printstring(string(" [ R"))
            else
              printstring(string(" [ _"))
    
            if(fat.listIsHidden)
              PrintChar("H")
            else
              PrintChar("_")
    
            if(fat.listIsSystem)
              PrintChar("S")
            else
              PrintChar("_")
    
            if(fat.listIsDirectory)
              PrintChar("D")
            else
              PrintChar("_")
    
            if(fat.listIsArchive)
              PrintChar("A")
            else
              PrintChar("_")
    
            printstring(string(" ] [ "))
            printstring(1 + str.integerToDecimal(fat.listSize, 10))
    
            printstring(string(" ] [ Accessed "))
            printstring(1 + str.integerToDecimal(fat.listAccessMonth, 2))
            PrintChar("/")
            printstring(1 + str.integerToDecimal(fat.listAccessDay, 2))
            PrintChar("/")
            printstring(1 + str.integerToDecimal(fat.listAccessYear, 4))
    
            printstring(string(" ] [ Created "))
            printstring(1 + str.integerToDecimal(fat.listCreationHours, 2))
            PrintChar(":")
            printstring(1 + str.integerToDecimal(fat.listCreationMinutes, 2))
            PrintChar(":")
            printstring(1 + str.integerToDecimal(fat.listCreationSeconds, 2))
    
            PrintChar(" ")
            printstring(1 + str.integerToDecimal(fat.listCreationMonth, 2))
            PrintChar("/")
            printstring(1 + str.integerToDecimal(fat.listCreationDay, 2))
            PrintChar("/")
            printstring(1 + str.integerToDecimal(fat.listCreationYear, 4))
    
            printstring(string(" ] [ Modified "))
            printstring(1 + str.integerToDecimal(fat.listModificationHours, 2))
            PrintChar(":")
            printstring(1 + str.integerToDecimal(fat.listModificationMinutes, 2))
            PrintChar(":")
            printstring(1 + str.integerToDecimal(fat.listModificationSeconds, 2))
    
            PrintChar(" ")
            printstring(1 + str.integerToDecimal(fat.listModificationMonth, 2))
            PrintChar("/")
            printstring(1 + str.integerToDecimal(fat.listModificationDay, 2))
            PrintChar("/")
            printstring(1 + str.integerToDecimal(fat.listModificationYear, 4))
    
            printstringcr(string(" ]"))
        if((counter // 8) and counter) 
          crlf
    
        abort 0
    
    PRI programConcatenate(stringPointer) : counter | buffer, flag
    
      ifnot(str.stringCompareCI(string("cat"), stringPointer))
        fat.openFile(str.tokenizeString(0), "R")
        stringPointer := str.tokenizeString(0)
        flag := (str.findCharacter(stringPointer, "H") or str.findCharacter(stringPointer, "h"))
    
        repeat until(fat.fileSize == fat.fileTell)
          if(flag)
            PrintChar("$")
            printstring(str.integerToHexadecimal(fat.readByte, 2))
    
            if(++counter // 16)
              PrintChar(horizontal_tab)
            else
              crlf
    
          else
            PrintChar(fat.readByte)
    
        if((counter // 8) and counter)
          crlf
    
        fat.closeFile
        abort 0
    
    PRI programChangeDirectory(stringPointer)
    
      ifnot(str.stringCompareCI(string("cd"), stringPointer))
        stringPointer := str.tokenizeString(0)
        fat.changeDirectory(stringPointer)
        taf.changeDirectory(stringPointer)
        abort 0
    
    PRI programChangeAttributes(stringPointer)
    
      ifnot(str.stringCompareCI(string("chmod"), stringPointer))
        fat.changeAttributes(str.tokenizeString(0), str.tokenizeString(0))
        abort 0
    
    PRI programMove(stringPointer)
    
      ifnot(str.stringCompareCI(string("mv"), stringPointer))
        fat.moveEntry(str.tokenizeString(0), str.tokenizeString(0))
        abort 0
    
    PRI programRemove(stringPointer)
    
      ifnot(str.stringCompareCI(string("rm"), stringPointer)) 
        fat.deleteEntry(str.tokenizeString(0))
        abort 0
    
    PRI programERA(stringPointer) ' same as ProgramRemove
    
      ifnot(str.stringCompareCI(string("era"), stringPointer)) 
        fat.deleteEntry(str.tokenizeString(0))
        abort 0
    
    PRI programMakeFile(stringPointer)
    
      ifnot(str.stringCompareCI(string("mkfile"), stringPointer))
        fat.newFile(str.tokenizeString(0))
        abort 0
    
    PRI programMakeDirectory(stringPointer)
    
      ifnot(str.stringCompareCI(string("mkdir"), stringPointer))
        fat.newDirectory(str.tokenizeString(0))
        abort 0
    
    PRI programCopy(stringPointer) | sector[128]
    
      ifnot(str.stringCompareCI(string("cp"), stringPointer))
        fat.openfile(str.tokenizeString(0), "R")
        stringPointer := str.tokenizeString(0)
        taf.newFile(stringPointer)
        taf.openfile(stringPointer, "W")
    
        repeat
          result := fat.readData(@sector, 512)
          taf.writeData(@sector, result)
        while(result == 512)
    
        fat.closeFile
        taf.closeFile
        abort 0
    
    PRI programDifference(stringPointer) | byte0, byte1
    
      ifnot(str.stringCompareCI(string("diff"), stringPointer))
        fat.openfile(str.tokenizeString(0), "R")
        taf.openfile(str.tokenizeString(0), "R")
    
        repeat until(fat.fileSize == fat.fileTell)
          byte0 := fat.readByte
          byte1 := taf.readByte
    
          if(byte0 <> byte1)
            printstring(string("Difference: $"))
            printstring(1 + str.integerToHexadecimal(byte0, 2))
            printstring(string(" <> $"))
            printstring(1 + str.integerToHexadecimal(byte1, 2))
            printstring(string(" at $"))
            printstringcr(1 + str.integerToHexadecimal(result, 8))
    
          result += 1
    
        fat.closeFile
        taf.closeFile
        abort 0
    
    PRI programBoot(stringPointer)
    
      ifnot(str.stringCompareCI(string("boot"), stringPointer))
          stringPointer := str.tokenizeString(0)
        'printstring(string("Are you sure you want to reboot from "))
        'printstring(stringPointer)
        'if(shellDecision(string(" - [y]es or [n]o? : "), "Y", "y"))
          fat.bootPartition(stringPointer)
    
        abort 0
    
    PRI programFormat(stringPointer)
    
      ifnot(str.stringCompareCI(string("format"), stringPointer))
        stringPointer := str.tokenizeString(0)
    
        printstring(string("Are you sure you want to format ", quote))
        printstring(fat.partitionVolumeLabel)
        if(shellDecision(string(quote," All data will be erased! - [y]es or [n]o? : "), "Y", "y"))
          if(shellDecision(string("Are you absolutely sure? [y]es or [n]o : "), "Y", "y"))
            printstringcr(string("Formatting Partition..."))
            fat.formatPartition(str.decimalToInteger(str.tokenizeString(0)))
    
        abort 0
    
    PRI programDate(stringPointer)
    
      ifnot(str.stringCompareCI(string("date"), stringPointer))
    
        ifnot(rtc.readTime)
          abort string("Operation Failed")
          crlf
    
        printstring(lookup(rtc.clockDay: string("Sunday"), {
                                              } string("Monday"), {
                                              } string("Tuesday"),  {
                                              } string("Wednesday"), {
                                              } string("Thursday"), {
                                              } string("Friday"), {
                                              } string("Saturday")))
        printstring(string(", "))
        printstring(lookup(rtc.clockMonth: string("January"), {
                                                } string("Feburary"), {
                                                } string("March"), {
                                                } string("April"), {
                                                } string("May"), {
                                                } string("June"), {
                                                } string("July"),  {
                                                } string("August"), {
                                                } string("September"), {
                                                } string("October"), {
                                                } string("November"), {
                                                } string("December")))
    
        PrintChar(" ")
        printstring(1 + str.integerToDecimal(rtc.clockDate, 2))
        printstring(string(", "))
        printstring(1 + str.integerToDecimal(rtc.clockYear, 4))
        PrintChar(" ")
        printstring(1 + str.integerToDecimal(rtc.clockMeridiemHour, 2))
        PrintChar(":")
        printstring(1 + str.integerToDecimal(rtc.clockMinute, 2))
        PrintChar(":")
        printstring(1 + str.integerToDecimal(rtc.clockSecond, 2))
        PrintChar(" ")
    
        if(rtc.clockMeridiemTime)
          printstringcr(string("PM"))
        else
          printstringcr(string("AM"))
    
        abort false
    
    PRI programTime(stringPointer) | time[6]
    
      ifnot(str.stringCompareCI(string("time"), stringPointer))
        printstringcr(string("Please enter the exact time,"))
    
        time := str.decimalToInteger(shellLine(string("Year (2000 - 2127): ")))
        time[1] := str.decimalToInteger(shellLine(string("Month (1 - 12): ")))
        time[2] := str.decimalToInteger(shellLine(string("Date (1 - 31): ")))
        time[3] := str.decimalToInteger(shellLine(string("Day (1 - 7): ")))
        time[4] := str.decimalToInteger(shellLine(string("Hours (0 - 23): ")))
        time[5] := str.decimalToInteger(shellLine(string("Minutes (0 - 59): ")))
        ifnot(rtc.writeTime(str.decimalToInteger(shellLine(string("Seconds (0 - 59): "))), {
                           } time[5], {
                           } time[4], {
                           } time[3], {
                           } time[2], {
                           } time[1], {
                           } time))
          abort string("Operation Failed")
          crlf
    
        crlf
        programDate(string("date"))
        printstringcr(string("Operation Successful"))
        abort false
        
    {
    PRI programRamSet(stringPointer)
    
      ifnot(str.stringCompareCI(string("set"), stringPointer))
        result := rtc.writeSRAM(str.decimalToInteger(str.tokenizeString(0)), str.decimalToInteger(str.tokenizeString(0)))
    
        stringPointer := string("Failure!", _newLineCharacter)
        if(result)
          stringPointer := string("Success!", _newLineCharacter)
    
        printstring(stringPointer)
        abort false
    
    PRI programRamGet(stringPointer)
    
      ifnot(str.stringCompareCI(string("get"), stringPointer))
        printstring(1 + str.integerToDecimal(rtc.readSRAM(str.decimalToInteger(str.tokenizeString(0))), 3))
        PrintChar(_newLineCharacter)
        abort false
    }
    {
    PRI programSQW(stringPointer)
    
      ifnot(str.stringCompareCI(string("sqw"), stringPointer))
        result := rtc.clockSquareWaveOut(str.decimalToInteger(str.tokenizeString(0)) & 1, 1)
    
        stringPointer := string("Failure!", _newLineCharacter)
        if(result)
          stringPointer := string("Success!", _newLineCharacter)
    
        printstring(stringPointer)
        abort false
    
    PRI programOUT(stringPointer)
    
      ifnot(str.stringCompareCI(string("out"), stringPointer))
        result := rtc.clockSquareWaveOut(0, ((str.decimalToInteger(str.tokenizeString(0)) & 1) << 1))
    
        stringPointer := string("Failure!", _newLineCharacter)
        if(result)
          stringPointer := string("Success!", _newLineCharacter)
    
        printstring(stringPointer)
        abort false
    }    
    {
    PRI programPlayWavFile(stringPointer) | numberOfChannels, sampleRate ' Google "WAV format" for info about this function...
    
      ifnot(str.stringCompareCI(string("play"), stringPointer))
        printstring(string("Please wait...", _newLineCharacter))
        stringPointer := fat.openFile(str.tokenizeString(0), "R")
    
        if(fat.readLong <> $46_46_49_52)
          abort string("Not RIFF file", _newLineCharacter)
    
        if((fat.readlong + 8) <> fat.fileSize)
          abort string("Chunk size incorrect", _newLineCharacter)
    
        if(fat.readLong <> $45_56_41_57)
          abort string("Not WAVE file", _newLineCharacter)
    
        if(fat.readLong <> $20_74_6D_66)
          abort string(quote, "fmt ", quote, " missing",  _newLineCharacter)
    
        if(fat.readLong <> 16)
          abort string("Subchunk 1 size incorrect", _newLineCharacter)
    
        if(fat.readShort <> 1)
          abort string("Not LPCM file", _newLinecharacter)
    
        dac.numberOfChannelsSetup(numberOfChannels := fat.readShort)
        dac.sampleRateSetup(sampleRate := fat.readLong)
    
        fat.fileSeek(34)
        dac.bitsPerSampleSetup(result := fat.readShort)
        dac.sampleSignSetup(result == 16)
    
        fat.fileSeek(28)
    
        if(fat.readLong <> (sampleRate * numberOfChannels * result / 8))
          abort string("Byte rate incorrect", _newLineCharacter)
    
        if(fat.readShort <> (numberOfChannels * result / 8))
          abort string("Block align incorrect", _newLineCharacter)
    
        fat.fileSeek(36)
    
        if(fat.readLong <> $61_74_61_64)
          abort string(quote, "data", quote, " missing",  _newLineCharacter)
    
        result := fat.readlong
        if((result + constant(36 + 8)) <> fat.fileSize)
          abort string("Subchunk 2 size incorrect", _newLineCharacter)
    
        printstring(string("Now playing "))
        printstring(stringPointer)
        printstring(string(_newLineCharacter, "Press enter to quit ", _newLineCharacter))
    
        dac.startPlayer
        scopeSwtich := true
    
        repeat ((result + 511) / 512)
          fat.readData(dac.transferData, 512)
    
          if(com.receivedNumber)
            result := com.receivedByte
            if((result == carriage_return) or (result == carriage_return) or (result == com#null))
              printstring(string("Quiting", _newLineCharacter))
              quit
    
        scopeSwtich := false
        dac.stopPlayer
        dac.clearData
        fat.closeFile
        abort 0
    }    
    {
    PRI programTapeWavFile(stringPointer) ' Google "WAV format" for info about this function...
    
      ifnot(str.stringCompareCI(string("tape"), stringPointer))
        printstring(string("Please wait...", _newLineCharacter))
    
        stringPointer := str.tokenizeString(0)
        fat.newFile(stringPointer)
        stringPointer := fat.openFile(stringPointer, "W")
    
        printstring(string("Now taping "))
        printstring(stringPointer)
        printstring(string(_newLineCharacter, "Press enter to save ", _newLineCharacter))
    
        fat.writeString(string("RIFF____WAVEfmt "))
        fat.writeLong(16)
        fat.writeShort(1)
        fat.writeShort(1)
        fat.writeLong(16_000)
        fat.writeLong(16_000)
        fat.writeShort(1)
        fat.writeShort(8)
        fat.writeString(string("data____"))
    
        adc.sampleRateSetup(16_000)
        adc.numberOfChannelsSetup(1)
        adc.bitsPerSampleSetup(8)
        adc.sampleSignSetup(0)
    
        adc.clearData
        repeat while(result < (posx - constant(36 + 8)))
          fat.writeData(adc.transferData, 512)
          result += 512
    
          if(com.receivedNumber)
            stringPointer := com.receivedByte
            if((stringPointer == carriage_return) or (stringPointer == carriage_return) or (stringPointer == com#null))
              printstring(string("Saving", _newLineCharacter))
              quit
    
        fat.fileSeek(40)
        fat.writeLong(result)
        fat.fileSeek(4)
        fat.writeLong(result + 36)
    
        fat.closeFile
        abort 0
    }
    PRI programDump(stringPointer) | outsideCounter, insideCounter
    
      ifnot(str.stringCompareCI(string("dump"), stringPointer))
        fat.openFile(fat.newFile(string("P8X32A.TXT")), "W")
    
        fat.writeString(string("P8X32A Character Set", carriage_return, line_feed, carriage_return, line_feed))
        repeat outsideCounter from 0 to 255
    
          printstring(string("Dumping character #"))
          printstringcr(1 + str.integerToDecimal(outsideCounter, 3))
    
          fat.writeString(string("Character #"))
          fat.writeString(1 + str.integerToDecimal(outsideCounter, 3))
          fat.writeString(string(carriage_return, carriage_return, carriage_return, carriage_return))
    
          repeat result from 0 to 31
            repeat insideCounter from (outsideCounter & 1) to 31 step 2
    
              if(long[32_768][result + ((outsideCounter >> 1) << 5)] & (|<insideCounter))
                fat.writeByte(".")
              else
                fat.writeByte(" ")
    
            fat.writeString(string(carriage_return, carriage_return))
          fat.writeString(string(carriage_return, carriage_return))
    
        fat.closeFile
        abort 0
    
    PRI programTest(stringPointer) | counter, buffer
    
      ifnot(str.stringCompareCI(string("test"), stringPointer))
        fat.newFile(stringPointer := string("P8X32A.ROM"))
    
        buffer := startTest(stringPointer, "W", string("Byte write test...   "))
        repeat counter from 32_768 to 65_535 step 1
          fat.writeByte(byte[counter])
        stopTest("W", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "R", string("Byte read test...    "))
        repeat counter from 32_768 to 65_535 step 1
          if(fat.readByte <> byte[counter])
            fat.closeFile
            abort string("Byte read back failed")
            crlf
        stopTest("R", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "W", string("Word write test...   "))
        repeat counter from 32_768 to 65_535 step 2
          fat.writeShort(word[counter])
        stopTest("W", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "R", string("Word read test...    "))
        repeat counter from 32_768 to 65_535 step 2
          if(fat.readShort <> word[counter])
            fat.closeFile
            abort string("Word read back failed")
            crlf
        stopTest("R", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "W", string("Long write test...   "))
        repeat counter from 32_768 to 65_535 step 4
          fat.writeLong(long[counter])
        stopTest("W", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "R", string("Long read test...    "))
        repeat counter from 32_768 to 65_535 step 4
          if(fat.readLong <> long[counter])
            fat.closeFile
            abort string("Long read back failed")
            crlf
        stopTest("R", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "W", string("Sector write test... "))
        repeat counter from 32_768 to 65_535 step 512
          fat.writeData(counter, 512)
        stopTest("W", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "R", string("Sector read test...  "))
        repeat counter from 32_768 to 65_535 step 512
          fat.readData(counter, 512)
        stopTest("R", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "W", string("Memory write test... "))
        repeat counter from 32_768 to 65_535 step 32_768
           fat.writeData(counter, 32_768)
        stopTest("W", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "R", string("Memory read test...  "))
        repeat counter from 32_768 to 65_535 step 32_768
          fat.readData(counter, 32_768)
        stopTest("R", 32_768, (cnt - buffer))
    
        fat.newFile(fat.deleteEntry(stringPointer))
        buffer := startTest(stringPointer, "A", string("Block append test... "))
        fat.writeData(32_768, 32_768)
        stopTest("W", 32_768, (cnt - buffer))
    
        buffer := startTest(stringPointer, "R", string("Reverse seek test... "))
        repeat counter from 32_767 to 0 step 512
          fat.fileSeek(counter)
        stopTest("R", 32_768, (cnt - buffer))
    
        fat.deleteEntry(stringPointer)
        abort 0
    
    PRI startTest(path, mode, message)
    
      fat.openFile(path, mode)
      printstring(message)
      return cnt
    
    PRI stopTest(mode, count, cycles)
    
      if(mode == "W")
        printstring(string("Writ "))
      else
        printstring(string("Read "))
    
      printstring(1 + str.integerToDecimal((count >> 10), 3))
      printstring(string(" KBs at "))
      printstring(1 + str.integerToDecimal((multiplyDivide(count, cycles) >> 10), 3))
      printstringcr(string(" KBs per second"))
      fat.closeFile
    
    PRI multiplyDivide(dividen, divisor) | productHigh, productLow
    
      productHigh := clkfreq ** dividen
      productLow := clkfreq * dividen
      if(productHigh => divisor)
        return posx
    
      repeat 32
    
        dividen := (productHigh < 0)
        productHigh := ((productHigh << 1) + (productLow >> 31))
        productLow <<= 1
        result <<= 1
    
        if((productHigh => divisor) or dividen)
          productHigh -= divisor
          result += 1
    {
    PRI scope | displayPointer, currentDisplay
    
      bmp.displayColor(0, _VGA_BG_Color)
      bmp.displayColor(1, _VGA_FG_Color)
      displayPointer := constant((_VGA_H_Res * _VGA_V_Res) / 32)
      repeat
    
        bmp.displayWait(1)
        bmp.displayPointer(@scopeHeap[displayPointer & currentDisplay])
        not currentDisplay
        bmp.displayClear(0, @scopeHeap[displayPointer & currentDisplay])
    
        repeat result from 0 to constant(_VGA_H_Res - 1)
    
          if(scopeSwtich)
    
            bmp.plotPixel( 1, {
                         } result, {
                         } (constant(_VGA_V_Res / 2) + (dac.leftChannelOutput ~> constant(16 - (>|_VGA_V_Res)))), {
                         } @scopeHeap[displayPointer & currentDisplay])
          else
    
            bmp.plotPixel( 1, {
                         } result, {
                         } (constant(_VGA_V_Res / 2) + (adc.leftChannelInput ~> constant(16 - (>|_VGA_V_Res)))), {
                         } @scopeHeap[displayPointer & currentDisplay])
    }                     
    
    PUB PrintStringCR(pstring)' sends pstring to the vga and serial port
         PrintString(pstring)                               ' print the string  
         crlf
    
    PUB PrintString(pstring)' sends pstring to the vga and serial port with no CRLF
        if (_VGA_Enabled)  
          vt100.str(pstring)                                 ' send to vga screen
        if (_Serial_Enabled)   
        sio.str(0,pstring)                                 ' send to com port
        if (_lcd_Enabled)
           lcd.str(pstring)                                   ' send to 20x4 lCD display
    
    PUB PrintChar(c) ' sends c to vga and serial port
        if (_VGA_Enabled)
          vt100.out(c)                                        ' send to vga
        if (_Serial_Enabled)
          sio.tx(0,c)                                         ' send to com port
        if (_lcd_Enabled)
          LCD.out(c)                                          ' send to LCD display      
    PUB CRLF
        PrintChar(13)                                        ' send to vga
        PrintChar(10)                                        ' send to vga
    
    PUB PrintDecimal(number)                                ' print the decimal value, useful for debuggin
         PrintString(str.integerToDecimal(number, 10))   
    
    PRI DisplayBinaryFiles | buffer
      PrintString(string("Use the BOOT command to run the following binary Spin programs/emulations:"))
      crlf
      fat.listEntries("W")   
      repeat while(buffer := fat.listEntries("N"))   
          buffer := str.trimstring(buffer)   
          if str.endswithstring(buffer,string(".BIN")) == -1
            PrintString(buffer)
             crlf
      crlf
    
    PRI ProgramDir(StringPointer) | buffer,counter,match
      ifnot(str.stringCompareCI(string("dir"), stringPointer))
        stringPointer := str.tokenizeString(0)              ' get the *.exe bit
        str.copy(stringpointer,@lineoftext1)                ' move to lineoftext1
        str.mid(@lineoftext1,@lineoftext2,str.instr(@lineoftext1,"."),4) ' get string from . to end = extension
        counter:=0
        crlf
        fat.listEntries("W")   
        repeat while(buffer := fat.listEntries("N"))
          str.copy(buffer,@LineOfText1)                ' copy filename to LineOfText
          str.trimstring(@lineoftext1)
          str.mid(@lineoftext1,@lineoftext3,str.instr(@lineoftext1,"."),4) ' get string from . to end = extension     
          match := false                                    ' setup for below
          if str.len(@lineoftext2) == 0
            match := true                                   ' no extension so always print it
          else
            ifnot str.stringcompareci(@lineoftext2,@lineoftext3) ' reverse logic ?!
              match := true                                 ' found a match
          if match == true
            PrintString(@lineoftext1)                       ' print file name
            counter++                                       ' add one to counter
            repeat 13-str.len(@lineoftext1)                 ' add spaces so columns line up
                printchar(" ")                              ' print space
          if counter == 6                                   ' new line
            counter :=0
            crlf 
        crlf
        abort 0                                             ' so knows a command was run
    
    PRI ProgramCommand(StringPointer) | buffer,i ' look for .exe files (ie .binary files renamed to .exe) and if found, run the program
        fat.listEntries("W")
        str.copy(stringpointer,@lineoftext2)                ' copy the command to lineoftext2
        str.stringConcatenate(@lineoftext2, string(".exe")) ' add extension for executable files to lineoftext2
         repeat while(buffer := fat.listEntries("N"))
           str.copy(buffer,@LineOfText1)                    ' copy filename to LineOfText1
           str.trimstring(@lineoftext1)                     ' remove any leading/trailing spaces  
           ifnot str.stringcompareci(@lineoftext1,@lineoftext2)' does it match? (ignore case)
             str.copy(buffer,@lineoftext3)                  ' can't use "buffer" as it gets overwritten
             'printstringcr(string("Saving command line in COMMAND.TXT and running program"))
             'printstring(string("Command line: "))
             'printstring(@commandline)
              ' above commented out as 1) about to blank the screen, 2) debugged and 3) if running xmodem don't want any further data on the serial lines
              ' possibly could put this output back if use the vga screen only.                   
             result := \fat.newFile(string("COMMAND.TXT")) ' All aborts traps must have "variable := \function" setup.
             if(fat.partitionError == fat#Entry_Already_Exist)  ' file already exists
                result := \fat.deleteEntry(string("COMMAND.TXT"))    ' delete the old file
                result := \fat.newfile(string("COMMAND.TXT")) ' recreate the file
             result := \fat.openFile(string("COMMAND.TXT"), "W") ' create file and open file
             result := \fat.writestring(@commandline)       ' store the command line as it was typed
             result := \fat.closefile                       ' close the file
             result := \fat.bootpartition(@lineoftext3)     ' reboot
    
    
    ' ***************************************************************************************
          'printstring(@lineoftext1)                         ' testing string functions
          'str.left(@lineoftext1,@lineoftext2,3)  ' find leftmost characters in a string
          'printstring(@lineoftext2)
          'str.trimstring(@lineoftext1)
          'printstring(@lineoftext1)
          'str.trimstring(@lineoftext1)
          'str.mid(@lineoftext1,@lineoftext2,4,2) ' find middle characters in a string
          'printstring(@lineoftext2)
          'printstring(str.integertodecimal(str.len(@lineoftext2),5))
          'str.copy(string("hello"),@lineoftext1) ' copy fixed value into a string
          'printstring(@lineoftext1)
          'str.str(@lineoftext1,55) ' decimal to string with no leading zeros
          'printstring(@lineoftext1)
          'str.stringfill(@lineoftext1,10,65)
          'printstring(@lineoftext1)
          'str.str(@lineoftext1,1234)' convert to string
          'str.str(@lineoftext1,str.decimaltointeger(@lineoftext1)) ' convert back
          'printstring(@lineoftext1)
          ' str.left(@lineoftext1,@lineoftext2,str.len(@lineoftext1)-4) ' remove extension
          ' printstring(@lineoftext2) ' print it out
            
    {{
    
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //                                                  TERMS OF USE: MIT License
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
    // files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
    // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    // Software is furnished to do so, subject to the following conditions:
    //
    // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
    // Software.
    //
    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    }}
    
  • william chanwilliam chan Posts: 1,326
    edited 2011-05-11 08:00
    Why do you call this procedure "code"?

    PRI code ' Put the file system calls in a separate method to trap aborts.

    Shouldn't it be called

    PRI perform_sd_operations ' Put the file system calls in a separate method to trap aborts.

    for better clarity?
  • KyeKye Posts: 2,200
    edited 2011-05-11 15:08
    Could be. Just an oversight - I guess.
  • william chanwilliam chan Posts: 1,326
    edited 2011-05-12 00:46
    Hi Kye,

    I am still a bit confused with the Start and Mount operations.
    On FSRW 2.6 I usually mount first at Propeller start up and delay file operations until much later.
    Can I start the FATEngine, Mount the SD card but delay the file operations until much later? (maybe hours later)

    But the way the example code is shown in the Application Note, once "code" returns it means an error has occurred and the Supervisor Cog cannot proceed as usual.

    Do you have an example code where the Supervisor Cog starts the FATEngine, Mounts the SD card and then proceeds with its normal execution without any file operations.

    Sorry for being such a slow learner.
  • KyeKye Posts: 2,200
    edited 2011-05-12 06:34
    Yeah, you can. The start operation just launches the SD SPI bus mater cog to start listening for commands. Nothing more.The mount operation actaully tells the SPI bus master card to start communicating with the SD card.

    Um... for your other question... so, that's the idea. Abort and Return both return error codes to the upper level spin method. So... it's just a mater of chaning that if you want to. But the easist way of writing the error handler is to call a function that actualy has all your code in it and assume that nothing critically worng happens in that code. If it does abort back to the upper level.

    Look at the demo SD_card_profilier tester code again. Its best to write your code like that. Because it will be robust. I could have broken up the large function where I preform all SD card operations into smaller functions with different error handlers but there wsa no need to. May you do have a need to.
  • william chanwilliam chan Posts: 1,326
    edited 2011-05-19 17:12
    Hi Kye,

    Are you working for Parallax now?

    Another question,
    I am now using the latest SD-MMC-FATEngine. ( without RTC )
    Previously in FSRW, i use

    sd.setdate(years,months,days,hours,minutes,seconds)

    to set the date and time before creating a new file so that the file date and time stamp is correct even though there is no RTC chip.
    I can't find the equivalent in FATEngine. How do I do this in FATEngine?
  • KyeKye Posts: 2,200
    edited 2011-05-20 07:20
    That feature was removed because if the time stamp on a file is not correct there is no point to having it. The driver without the RTC just ouputs all 0's for the time and date.

    If you want to add time stamping features... look at the "readTime" funtion in the versions with the RTC and then put the code that you think you need for that function in the "readTime" function in the version without the RTCs. Its really simple. You'll understand what to do no problem.
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-05-20 07:28
    Does FSRW continue to update the time after calling setdate? There's no reason this couldn't be done without a RTC.
  • lonesocklonesock Posts: 917
    edited 2011-05-20 10:26
    In FSRW 2.6 it does not keep updating the time for you. There was a "get_seconds" command added to the block driver that tracks elapsed seconds (I was using the code for automatically releasing the card after a period of disuse). Thus this functionality _could_ be added to FSRW, but my reasoning was that if someone provided a time-stamp once, they could do it again, saving FSRW from code bloat. I'm not saying my reasoning was valid...[8^) Once again, I really wish the standard propeller tool supported conditional compilation.

    Jonathan

    P.S. Dave, I'm working on back-porting your directory support into the FSRW C source, for an intermediate release of FSRW...I already have the safe block driver reading and writing at > 1MB/s (w/ 32kB clusters...slightly slower writing under FAT32 with 8kB clusters).
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-05-20 10:49
    lonesock wrote: »
    P.S. Dave, I'm working on back-porting your directory support into the FSRW C source, for an intermediate release of FSRW...I already have the safe block driver reading and writing at > 1MB/s (w/ 32kB clusters...slightly slower writing under FAT32 with 8kB clusters).
    That's great! There are limitations on how I did the directory support, such as the directory must be in the first 2GB of the file system. It could be fixed for locations greater than 2GB with a few changes.
  • william chanwilliam chan Posts: 1,326
    edited 2011-05-25 05:45
    Hi Kye,

    It seems that openFile with append mode does not create the file if it does not exist, unlike popen in FSRW.
    How do I make openFile create a file automatically if the filename does not exist?
    How to check for existence of a file?

    Sorry for so many questions.
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-01-20 21:21
    Kye: I am confused. What is the latest version please?
    SD_MMC_FATEngine.spin (version 2.0) from Obex and AN006 dated 25/9/2011
    SD_3.01_FATEngine.spin (version log stops at 1.7) from Dracs KyeDos3.0 dated 6/1/2011

    BTW here are my results for the version 2.0
    ### SD Card Profile ###
    Disk Signature: 0x00000000
    Partition 0 - 0xDC666872
    R3_20120120 FAT16
    512 - Bytes Per Sector
    32 - Sectors Per Cluster
    1982944 - Total Sectors
    61967 - Total Clusters
    64 - Used Sectors
    1982880 - Free Sectors
    32 KB Stride Speed Test:
    writeByte - 4 KBs
    writeShort - 8 KBs
    writeLong - 14 KBs
    writeData - 128 KBs
    readByte - 4 KBs
    readShort - 8 KBs
    readLong - 16 KBs
    readData - 330 KBs

    ;););)
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-01-21 01:07
    Kye: Another question... Is there a method to find the current sector on the SD card?

    I open a number of files for read only to determine the starting sector address in ZiCog. The I access the SD pasm driver directly to read and write sectors from CPM. I have tried a number of the PRI routines but nothing returns the files first sector address on the card. Any tips on how to get this?
  • KyeKye Posts: 2,200
    edited 2012-01-21 06:36
    The newest version is in the OBEX. Parallax has not updated their version they posted on the Parallax Semiconductor website.

    ...

    That's a nice fast SD card you have there!

    ...

    The readWriteCurrentSector function returns the sector number it was called on. That function basically encapsulates the logic for figure out what sector to read or write given the "current cluster" and "current byte" and it sets the "current sector". The "current sector" variable is equal to the "current byte" variable divided by 512. However, the "current byte" variable may be ahead of the "current sector" variable - this allows me to know when to switch clusters... if you look at the readWriteCurrentCluster code...

    Anyway, just look at what "readWriteCurrentSector" returns to get the sector number from the start of the partition... then add in the "hiddenSectors" variable to get the absolute sector number. You could make a function that does exactly what "readWriteCurrentSector" does without the reading and writing part. If you plan on using the "readWriteBLock" function you don't need to add in the "hiddenSectors" variable.

    Thanks,
  • StefanL38StefanL38 Posts: 2,292
    edited 2012-01-21 12:15
    Hi Dr_Acula,

    reads really good what you have coded so far. How much would it take to add constants (or maybe conditional compiling directives) to switch the SD-card-routines
    to the Prop-C3-board?

    Some months ago I made some quick tries to get KyeDOS working on the C3 but had no success mainly because I did not rty it long enough to understand what I have to change.

    keep up the good work
    best regards
    Stefan
  • richaj45richaj45 Posts: 179
    edited 2012-01-21 13:17
    Does the SD card physical interface is a one data pin SPI interface or the four data pin SPI like interface?

    Thank you
    rich
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-01-21 15:48
    @ stefan, it ought to be possible to just change the pin numbers for the SD card. Do you have a link for the C3 schematic by any chance? (There are many links to a low resolution copy but I once did manage to find a proper resolution one, but I have forgotton where!).

    @richaj45, the SD card needs 4 pins - chip select (low to enable), data out, data in, clock.

    Of course, if you are feeling cunning you can have multiple SPI devices sharing data out, data in and clock, and then you only need one pin per device to drive chip select.
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-01-21 16:30
    kye: Thanks for that info. I can now try it out. I do know what numbers I expect, so it is easy to verify.
    My card is a 1GB SanDisk. The test was done on a RamBlade3 - I am running at 6.5MHz (104MHz) ;) Its a nice speed improvement!

    richaj: I presume you are meaning is it a 1bit SPI interface or the faster 4bit parallel interface. Sorry, its the 1bit because the 4bit requires a licence to use and the payment of big$ to the card consortium.
  • StefanL38StefanL38 Posts: 2,292
    edited 2012-01-22 02:19
    Hi Dr_Acula,

    no it is not done with just changing the pin-numbers because the C3 has a SPI-MUX-bus.
    So the driver must be adapted to that.
    I attach two pictures that I found on the FTP-server for the C3 ftp://ftp.propeller-chip.com/PropC3

    To be really honest. In this case I'm really lazy. I don't want to dive into the details of FSRW, SPI-routines and the FAT-Engine.

    What I would like to do is download a ready to run KyeDOS-Version adapated to the C3-board.
    Download demofiles. Copying them on an empty micro-SD-card download a demofile into the EEPROM.
    Switching on the C3-board and have a a DOS-prompt on the serial terminal. Not on VGA (eating too much memory) , No TV (don't have a small TV handy).
    Serial is what you have as soon as you want to download code into the EEPROM or RAM.

    I know that's asking for a finished version where others have done all the work. Nobody is forced to fiddle around with the code
    If somebody enjoys doing it and testing it until it's running reliably please do it or if somebody has done it already please attach the code
    to a posting or upload to the OBEX.

    Edit 21.01.2012 this forum-software needs manual quality-checks after uploading. T´he original png-picture was blurred to unreadability through converting it to a "LEGO"-JPG
    I uploaded the original png-file as zip to keep the good quality.

    bumb: if you (the forum-members) think my wish is to much work for others and too less work on my own I would like to read a clear statement telling the truth what you think about it.

    best regards

    Stefan
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-01-22 19:57
    Kye: Here are the results for the same card on the same pc & prop, but with 5MHz (=80MHz).

    ### SD Card Profile ###
    Disk Signature: 0x00000000
    Partition 0 - 0x146A7AD4
    R3_20120120 FAT16
    512 - Bytes Per Sector
    32 - Sectors Per Cluster
    1982944 - Total Sectors
    61967 - Total Clusters
    217568 - Used Sectors
    1765376 - Free Sectors
    32 KB Stride Speed Test:
    writeByte - 3 KBs
    writeShort - 6 KBs
    writeLong - 12 KBs
    writeData - 206 KBs
    readByte - 3 KBs
    readShort - 6 KBs
    readLong - 12 KBs
    readData - 266 KBs

    readData... 266 KBs @ 5MHz and 330 KBs @ 6.5MHz nice improvement here!
    The writeData is interesting... 206 KBs @ 5MHz and 128 KBs @ 6.5MHz opposite to what was expected!
  • KyeKye Posts: 2,200
    edited 2012-01-22 20:17
    Yeah, no clue. Maybe it was just a hiccup that time. There's a lot of stuff going on in the SD card that can make write performance dreadful out of nowhere.

    Read performance should stay constant, however.

    ---

    I'm done collecting data measurements BTW. But, thanks for posting anyway. I think the driver is pretty solid right now. The performance isn't bad on it either.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-01-22 20:27
    @StefanL38
    To be really honest. In this case I'm really lazy. I don't want to dive into the details of FSRW, SPI-routines and the FAT-Engine.

    Well I guess the truth of it is that I don't have a C3 so I haven't written any code for the C3. Sorry.

    Looking at the schematic, I think that is a 161 counter chip, so you clear it and then send a certain number of pulses and that selects the SPI device. I don't know which device is the SD card, but I think the code ought to be fairly simple, and you could write it in Spin if you like.

    I found a good description here http://www.c3emulation.com/ and there is a description and code here http://www.c3emulation.com/index.php/memory/113-c3-spi-set-channel
  • StefanL38StefanL38 Posts: 2,292
    edited 2012-01-23 04:12
    @Dr_Acula,

    thank you for pointing to the C3-emulationsite.
    The problem for me is how do I quickly find all the places in the code that are affected by this.

    If I understood right. the spi-driver is PASM. So I guess I have to change things there.
    Or do you think I setup the spi-channel in SPIN in my own mtehod and then call un unchanged KyeDOS?
    I guess it won't work this way.

    best regards

    Stefan
  • KyeKye Posts: 2,200
    edited 2012-01-23 11:17
    Um, if you search the forums for a post by Mike Green about my driver application note documentation you'll find a version of the driver made for the C3 by Mike Green.

    Thanks,
  • StefanL38StefanL38 Posts: 2,292
    edited 2012-01-23 15:21
    Hi Kye,

    thanks for pointing me to this version. Is it called C3 FATEngine?

    OK so I have a FATEngine. But how does this FatEngine work together with KyeDos?

    Do I simply replace the file "SD3.01_FATEngine.spin"
    with the C3-version of the file "SD-MMC_FATEngine.spin" ?

    I'm a beginner about the FATEngine and KyeDos and have no overview about what is based on what (like experts have)

    So it would be very nice if somebody could post a description of which files to replace where
    or attaching a version that works properly on the C3-board.

    best regards
    Stefan
Sign In or Register to comment.