Kye, You're right (again) the card had some problems. I had previously formatted the card with a netbook prior to using version 2.0 demo to format it. I had used the "quick format" option, I'm now reformatting it the slow way. I thought the card was okay since it works with the version 2 demo.
I put in an older 1 Gig card and it worked. I still get "No Card Detected" if I don't put a -1 in for the card detect pin.
This is the test of a Kingston 1GB microSD.
Card Not Mounted >_ Running command: mount 0
Disk 0x00000000 with Volume ID 0x708967C8 a.k.a "USD1GIG" ready
>_ Running command: test
Byte write test... Writ 032 KBs at 003 KBs per second
Byte read test... Read 032 KBs at 003 KBs per second
Word write test... Writ 032 KBs at 006 KBs per second
Word read test... Read 032 KBs at 006 KBs per second
Long write test... Writ 032 KBs at 011 KBs per second
Long read test... Read 032 KBs at 013 KBs per second
Sector write test... Writ 032 KBs at 090 KBs per second
Sector read test... Read 032 KBs at 267 KBs per second
Memory write test... Writ 032 KBs at 091 KBs per second
Memory read test... Read 032 KBs at 285 KBs per second
Block append test... Writ 032 KBs at 109 KBs per second
Reverse seek test... Read 032 KBs at 350 KBs per second
Okay, the other card is done formatting. It's a Transcend 4GB Class 2 miroSD HC card (from Woot.com). I'm trying it now.
Disk 0x00000000 with Volume ID 0x86C13AD8 a.k.a "MAGICIR" ready
>_ Running command: test
Byte write test... Writ 032 KBs at 003 KBs per second
Byte read test... Read 032 KBs at 003 KBs per second
Word write test... Writ 032 KBs at 006 KBs per second
Word read test... Read 032 KBs at 006 KBs per second
Long write test... Writ 032 KBs at 011 KBs per second
Long read test... Read 032 KBs at 012 KBs per second
Sector write test... Writ 032 KBs at 078 KBs per second
Sector read test... Read 032 KBs at 179 KBs per second
Memory write test... Writ 032 KBs at 083 KBs per second
Memory read test... Read 032 KBs at 187 KBs per second
Block append test... Writ 032 KBs at 124 KBs per second
Reverse seek test... Read 032 KBs at 213 KBs per second
Well, now it works. Apparently the "fast format" option doesn't fix problems. Lesson learned.
As I mentioned earlier, The SD card is on a breadboard with about 4" of wire between it and the Propeller.
Thanks for your help and thanks for your great programs. I use your objects all the time.
I've also learned a lot about programming from reading your code. I wouldn't have know variables are optional in most methods and objects and result is the only local variable that is ever(almost) needed.
MacTuxLin, Yes, I have a RTC connected. I didn't think it was the problem since it works fine with version 2. You can see from my previous post, I got things working now. Thanks for taking time to make a suggestion.
First and foremost this code is outstanding and many thanks for all your efforts...I'm observing sector write speeds varying from 88 to 121 KB/sec on 4 unique cards varieties running a 64 MHz clock when writing 2.5MB files...super! Unfortunately however I'm encountering some coding hick-ups and was hoping that you or perhaps someone on this thread could steer me in the right direction.
The code seems to hang if the changeDirectory or newDirectory functions are called with invalid directories passed. For example:
fat.changeDirectory(string("BOB")) will fail if a directory named BOB doesn't exist in the currently active folder.
and
fat.newDirectory(string("SUSAN")) will fail if a directory by the name of SUSAN already exists in the currently active folder.
When either of the two conditions above occurs, code following the offending call does not execute indicating that the code is perhaps caught in an infinite loop (a repeat?). Rather than digging too deep into foreign code I was hoping you could help as I may be missing something obvious. Maybe I am calling the functions incorrectly, but valid directory passes with the same syntax seem to function as expected.
Am I coding incorrectly? I could work around most of these issues by using the list functions but hangs are scary critters in embedded systems especially those without native watchdogs.
Any errors are aborted. If you don't catch the abort then the cog running the code shutsdown.
Look at only PUB funtion in my demo and notice the "\" abort trap.
YOU NEED TO ABORT TRAP EVERY FUNCTION OR SETUP A SYSTEM LIKE IN MY DEMO TO HANDLE ABORTS GRACEFULLY
The aborts return a pointer to a string and set an error number. The error number can be gotten from calling the "partitionError" function. The function returns the error number (0 if no error) and then clears the error flag.
Note that errors "1 - 5" will unmount the file system and are not recoverable. You can get the nice names of the error numbers by looking in the FATEngine code and looking at the CON section.
@Deng - Check you hardware vs the hardware in the new driver. You can find how the setup should be configured in detail near the top of the FATEngine file. You don't need pull up resistors for CS,DO,DI,CLK lines.
@Dr_Acula - I confused by what you want.... Lets work togheter to develope "Kye Dos" You tell me what you want and I'll code it. Email me.
@bpranger - the way I have the code setup is optimal for flexibility and readability. If I did one big function then things would get realllllllllllllly messy.
I was talking only about changing your one shell routine to select the method to call based on what was typed, not on making your entire code one function.
If I understand your code right now, you drop thru multiple calls to every possible program that the person could have selected. You do a string compare to see if the word typed matches the method.
If a match does not occur, then you abort back and continue down to the next routine.
Why wouldn't replacing that same selection routine with one containing a Case statement that goes directly to the method requested be more efficient than calling every method looking for a match?
Maybe I don't still understand it or it compiles differently, but it seems that if I type in the word to run the last method, I believe you call every method before it and in each method, call a string compare function comparing the word of the command with stringpointer. Each StringCompare would fail and you would abort back and call to the next one until you reach the last method which would match, and you would execute it.
A Case statement would not make things messy, it would simply take the typed word, and directly execute the method requested without any calls to stringcompareCI and no additional calls to the earlier methods in the list.
I guess I don't understand why aborting from every method until there is a match is more efficient than calling the one you want thru a Case statement.
i don't mean to be critical, I just am trying to understand the underlying logic in your spin routine.
As far as I know a string is a multibyte value. You can't use a case statment.
I've just spent a day deep inside this code. The way it works is fine (albeit, having completely rewritten it *grin*).
I'm a basic programmer, and so much is done behind the scenes, even with the earliest of basics. The = sign is used for many different things. I know why other languages do it differently, because it is easier to write compilers, but for the end user consider:
A=B ' integers or single single or floating values
If A=B then.. ' equality test
A$=B$ ' strings made equal
If A$ = B$ then... ' equality test on strings
I've just been adding my favourite string routines to the string handler. left and mid etc. You can replicate a Case statement with a series of equality tests on strings. Logic is reversed but if you treat an ifnot as an if, you can use a series of
ifnot str.stringcompareci(@lineoftext1,@lineoftext2)
however, it isn't quite that simple as some routines change the strings when they tokenise, so if you did it his way you would have to copy all passed strings to another string.
Nothing impossible though. One could add a couple more methods to the string handler
str.SelectCase(@lineoftext1)
if str.case(@lineoftext2)
.. do something
if str.case(@lineoftext3)
.. do something
where lineoftext is a true string that can be changed, or is string("fixed string")
Ok, I've been coding all day and got a lot further with KyeDOS.
We now can run any file that ends with .exe by just typing the name of the file without the extension. To play Movie.exe (which is actually Movie.binary renamed), just type "movie"
And to make it easier to find all the .exe files on the card (mine is getting quite cluttered), type
dir *.exe
The reboot is so amazingly fast that this is going to lead to new ways of doing things:
1) All spin programs could have some sort of way of quitting apart from hitting the reset button. Reboot is a standard spin instruction so it is easy to add to any spin program. Programs that quit then return to Kyedos (which boots very fast as well).
2) One can think about batch files. Run a program "batch myfile" and it creates a temporary file myfile.$$$ and each time KyeDOS reboots, it runs the first file off that $$$ file, then moves the list up one.
3) One can add an autoexec. If "autoexec.bat" exists on bootup, then run whatever is in that file.
4) And this leads to a need for simple text editing so you can edit batch files etc. It is getting close. I can now boot into kyedos, type "CPM" and then "WS" and have a word processor running in only a few seconds.
5) It ought to be possible for programs to chain each other. If separate spin program has the ability to write to the sd card, it could write a file called "SHELL.$$$" which contains one line. It then shuts itself down with a Reboot. Kyedos restarts, looks for SHELL.$$$, if found, reads the line of text, deletes the file, then runs the program. The fast reboots within Kye's code means that splitting a spin program up into lots of bits becomes a practical alternative to using external memory for big programs.
I have also added some flags at the beginning of the code
_VGA_Enabled = true ' VGA display on
_LCD_Enabled = true ' false or true for 20x4 LCD display
_Serial_Enabled = true ' send output to serial port
What this means is that it ought to be possible for anyone with an SD card to get KyeDOS working.
Re
@Dr_Acula - I confused by what you want
I was trying to describe this bit of code that runs the .exe files.
PRI ProgramCommand(StringPointer) | buffer ' 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)
fat.bootpartition(buffer) ' reboot
Amazing stuff Dr_Acula & Kye! I'm beginning to think it is much more worth the while to just focus on writing the hardware test script on top of KyeDOS instead.
Here is an idea to run up the flagpole and see if anyone salutes.
Would it be possible to use KyeDOS to allow the Proptool to download directly to an SD card and bypass the EEPROM?
First thing is that the Proptool resets the propeller, but that can be bypassed with a switch or jumper.
Next thing = is Kye's code fast enough to handle a download and move the data over to an sd card. I suspect it is.
Then one would need to ask what name to save this file as, and run it. Maybe BST and homespun could do this (it is just a few more characters to be sent down the serial port).
I might put a trace on the serial port during an eeprom or ram download and see what data is going back and forth and see if it is possible to trick the PC side into thinking it is downloading to an eeprom when it is actually going to the SD card. I have xmodem working but the Proptool does not talk using xmodem. Hopefully the protocol is simple.
I believe that Chip already gave us the code for the loader as well as the spin interpreter that sits in ROM.
You could just modify that to make it work at 80MHz, and also write to the SDcard.
Upon further reading, I might set that little project aside. It looks complicated.
I've gone back to looking at xmodem. To get xmodem running, I need to write an xmodem program. And in keeping with the philosophy of an operating system, rather than try to fit it all into the OS, put it in a separate executable.
Executables need a command line to pass parameters. I tried putting the data in high ram, but it appears a reload wipes out all the ram even if the program is smaller than the ram. So the next experiment is to try passing parameters via a small text file. I certainly have no problems writing a file. However, if just one thing goes wrong, the subroutine aborts and drops back to the start. I think this is part of the problem discussed earlier today - if you have "abort" instead of a series of "select case", and something goes wrong somewhere else, where does it abort to?
So maybe we do need a structured "select case", either in strings, or in numbers that have been translated from strings.
Alternatively, maybe it is possible to write to an sd card without ever creating errors.
I'm looking at two scenarios - the file exists, in which case write to it, or it does not exist, in which case create it then write to it. I can't seem to get all the options right though. Eg if I try to delete it and it does not exist then it errors. And reboots or hangs things because the error trapping is not passed up the chain properly.
Is there a standard order to create a file for writing, whether it exists or whether it does not exist.
'fat.deleteentry(string("COMMAND.TXT")) ' delete old file
'fat.newFile(string("COMMAND.TXT")) ' create new file
'fat.openfile(string("COMMAND.TXT"),"W") ' open for writing
'fat.writestring(@commandline) ' write the string
'fat.closefile ' close the file
Thanks Kye! I knew I was missing something, but Spin is new to me, though I've used many other languages. Most have Case which works with strings, however, I forgot that for the Prop, a string is a multi-byte item, so of course, Case won't work,
at least not as I was thinking of it.
Thanks for a great SD routine and I'm looking forwards to KyeDOS. I am already using a SD card in my app, so the idea of calling and running spin files from the card is pretty neat.
Thanks Kye. I like your idea of a log file. I remember when DOS for the PC came out and someone showed me how to get the last command back and edit it, rather than having to retype it all.
So - where is the best place to store it?
1) Propeller RAM. But I think that gets completely wiped with a reboot. Is that correct?
2) External RAM. This would be very convenient for me, but it would mean the code would not run on all boards without external ram. So no to this idea.
3) EEPROM. This could be an option. EEPROM does wear out eventually.
4) The SD card. This will wear out too, but it can be replaced more easily than EEPROM.
Could you explain this a bit more "Use the "Fat.partitionError" function... and just catch the aborts."
Also, my example for opening a file looks like it has more commands than are needed. Is there a better way? eg - rather than trying to open a file that might not exist, or delete a file that does not exist, is there a routine that tests for the existence of a file?
On PropCade I designed Layout for Bill Henning -- I added Battery Backup to last 2 SPI SRAMS that are on-board for that possibilitys (We have totally 6xSPI Sockets SRAM/FLASH compatible)
Again, just try to open the file... put an abort trap infront of the call to fat.openFile. Then check the paritionError function after calling the fat.openfile function and see if an error occured. If an error did occur look at the error code and decide what to do next. If the error code was that the file did not exist then creat the file and try opening it again.
Hi Sapieha, that sounds very interesting. I've wondered about putting SPI ram on the dracblade too. Which chips are you using?
Hi Kye, that sounds simple enough. Sorry I'm being a bit thick with respect to abort traps. Are they nested? In other words, if you put an abort trap in front of the openfile function, will this upset the 'global' abort trap that is working on all the command translation?
Unfortunately, the manual seems to confirm what I'm worried about, viz, page 47 "When one of the simple methods determines a course of action, it can issue an abort that completely collapses the nested call chain and prevents all the intermediate methods from continuing." and "The ABORT command, however, repetitively pops data off the call stack until it reaches a caller with an Abort Trap (see below); returning to some higher-level caller that may have just been one call, or many calls, up the nested chain of calls. Any return points along the way between an aborting method and an abort trapping method are ignored and
essentially terminated."
Which might abort out of that PUB and back up to the next abort in the chain. I'll need to test it.
Maybe it is not such an issue, as the code to check for a command is the last one in the list.
I'm not that used to using aborts and similar structures. They seem a bit too much like "GOTO" in that code can jump around in unintended ways. I might see if I can change the global structure so it looks like a "select case".
Yeah, its gonna be a pain... But the file system has to be handled this way. Aborts are the name of the game. Read about "try" and "catch" google them.
Aborts and abort traps were something I was unaware of up until about a day ago, so take my inputs with a grain of salt. While reading the manual did help, it left a few critical questions unanswered about how they actually behave.
"Which might abort out of that PUB and back up to the next abort in the chain. I'll need to test it." - All that matters is the active abort and the closest abort trap up the call chain. Whether the call functions along the chain are terminated by an abort or a return doesn't matter they will be skipped over. In this way you can completely skip subsequent code that is likely to fail anyway due to the error that just occurred. If a line of code failed to create a directory then the very next line will also fail if you try to change into that same failed directory, by placing a trap much higher up you skip all that non-sense and can deal with the error in a more general way (perhaps stop the SD card and warn the user).
I see the abort and abort traps less like a GOTO and more like a GetTheHeckOut, you are retreating to safer ground to regroup. While in function they might be similar to a goto you aren't jumping to a specified singular address rather to the next abort trap up the call chain. So it is completely feasible for the same exact abort (ex. changeDirectory to a directory that doesn't exist) to be trapped in different regions of the calling code depending upon where they were called with respect to the abort traps in your application.
Thanks miner. I'm still completely confused with "abort", particularly how the Proptool links it with its little linking lines and the way it behaves when it follows an "if" vs no "if".
In any case, I can't get Kye's code to work, and as far as I can see there is no manual, so I am going to have to ask Kye really nicely to explain this in language that is easy to understand. ie a couple of lines of code. "try" and "catch" are fine and I use them all the time in vb.net but there is no "try" in spin.
result := \fat.newFile(string("TEST3.TXT")) ' All aborts traps must have "variable := \function" setup.
if(fat.partitionError == fat#Entry_Already_Exist)
' Um... are we okay with opening the entry here if it already exists??? Ifnot then do something else.
else
result := \fat.openFile(string("TEST3.TXT"))
' Other errors could have happened also here... robust code should have an error case to handle anything that could go wrong. Here is the list of errors.
CON
#1, Disk_IO_Error, Clock_IO_Error, {
} File_System_Corrupted, File_System_Unsupported, {
} Card_Not_Detected, Card_Write_Protected, {
} Disk_May_Be_Full, Directory_Full, {
} Expected_A_Entry, Expected_A_Directory, {
} Entry_Not_Accessible, Entry_Not_Modifiable, {
} Entry_Not_Found, Entry_Already_Exist, {
} Directory_Link_Missing, Directory_Not_Empty, {
} Not_A_Directory, Not_A_File
These errors occur in different functions in the file system. If you do stuff right you won't have to handle every error if you know your input is correct. But stuff like DISK I/O ERROR should be handled at all times.
Dr_Acula, I've been trying to write a universal remote program for the Prop using Nick's IrMagic. Kye released his newest SD driver a couple of days after I started writing my program. I've been trying to understand the abort stuff too.
I've started catching the return values from the fat methods (called with an abort trap). For now I'm just displaying the abort codes but I might add a case statement to take actions depending on what code was returned.
result := \fat.openFile(fat.newFile(@masterlist), "W")
Debug.str(string(13, "fat.openFile returned = "))
Debug.dec(result)
Kye lists the errors in the CON section of SD3.01_FATEngine.
You can have several abort traps chained but only the trap clossest to the abort is used.
I've also started listing the methods (with an indicator if it uses a trap) that might call a particular method here's an example.
PUB EnterButtonId(localDeviceIndex) : localIdValue '| localCharacter, localIdFlag
'' Called from Main\UseDevice|OpenDevice\ aborts to OpenDevice so it can close file.
'' Called from Main\LearnNewDevice|Record aborts to Main.
I use a "|" to indicate a call normal call and a "\" to show it was called with an abort trap.
In the above example, I call UseDevice from Main with a trap. UseDevice calls OpenDevice (no trap) and OpenDevice calls EnterButtonId with an abort trap. Even though there are two abort traps in the chain of calls, it aborts back to OpenDevice. OpenDevice then looks at the returned value to see it it's a normal expected value or one of the possible abort codes.
If the above method was called via "Main\LearnNewDevice|Record" then it aborts back to the Main method. As Miner points out sometimes you just want to jump back to the top and take care of things there.
Someone please correct me if I'm not understanding this correctly.
Duane
Edit: Kye just beat me too it. Maybe some my stuff will still help.
Woot! Something works. This code is not optimised, but it does run whether the file exists or does not exist. Does it need any more abort traps etc?
printstringcr(string("Saving command line in COMMAND.TXT and running program"))
result := \fat.newFile(string("COMMAND.TXT")) ' All aborts traps must have "variable := \function" setup.
if(fat.partitionError == fat#Entry_Already_Exist) ' file already exists
printstringcr(string("File already exists"))
fat.deleteEntry(string("COMMAND.TXT")) ' delete the old file
fat.newfile(string("COMMAND.TXT")) ' recreate the file
else
printstringcr(string("debug"))
fat.openFile(string("COMMAND.TXT"), "W") ' create file and open file
fat.writestring(@lineoftext3) ' store the command line as it was typed
fat.closefile
fat.bootpartition(@lineoftext3) ' reboot
Addit: Now this is getting fun. I can type a command "xmodem r myfile.txt" and it runs "xmodem.exe" and with a few lines of code in that new program I can retrieve the command line.
fat.openfile(string("COMMAND.TXT"),"R")
fat.readstring(@lineoftext1,80) ' read in the command
fat.closefile
printstringcr(@lineoftext1)
Next thing is to parse it and then run the code. I already have xmodem code so I just need to write the parser on the command line. I think there might already be one in Kye's code already.
Why is this useful?
Well, consider the very real problem of not having enough memory. KyeDOS itself is already almost filling the 32k, so adding more commands is not going to be practical. KyeDos v2 contained xmodem code for instance, but there isn't room in v3.
But the amazing thing about v3 code is that it is so fast. Shelling out to another binary, running the binary and returning to the main program is only taking a couple of seconds.
Suddenly this opens up a new way of coding that does not need external memory. Just run programs that pass messages via text files and which can chain each other.
And if KyeDOS can pass messages to other binaries, then the messages can go the other way as well. A program can run that leaves a message for KyeDOS when it restarts. eg - run this batch file.
Very nice. Remember however.... that all the file system functions can abort on you if there is a problem. You should have abort traps on all of them just in case... even if you don't handle the error.
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)
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
The Kyedos code is getting to a stage of being stable now, so I might start a new thread rather than hijacking this one too much
I have some other ideas for simple programs that can be run from the command line as if they are internal commands to the OS - eg TYPE to dump out a file and I also need to have some demo programs that put back the scope and a few other things I had to delete in the OS to make room.
KyeDOS is text and maybe I need to think about a "graphical" version that uses the mouse and icons?
Comments
I put in an older 1 Gig card and it worked. I still get "No Card Detected" if I don't put a -1 in for the card detect pin.
This is the test of a Kingston 1GB microSD.
Okay, the other card is done formatting. It's a Transcend 4GB Class 2 miroSD HC card (from Woot.com). I'm trying it now.
Well, now it works. Apparently the "fast format" option doesn't fix problems. Lesson learned.
As I mentioned earlier, The SD card is on a breadboard with about 4" of wire between it and the Propeller.
Thanks for your help and thanks for your great programs. I use your objects all the time.
I've also learned a lot about programming from reading your code. I wouldn't have know variables are optional in most methods and objects and result is the only local variable that is ever(almost) needed.
Duane
Duane
First and foremost this code is outstanding and many thanks for all your efforts...I'm observing sector write speeds varying from 88 to 121 KB/sec on 4 unique cards varieties running a 64 MHz clock when writing 2.5MB files...super! Unfortunately however I'm encountering some coding hick-ups and was hoping that you or perhaps someone on this thread could steer me in the right direction.
The code seems to hang if the changeDirectory or newDirectory functions are called with invalid directories passed. For example:
fat.changeDirectory(string("BOB")) will fail if a directory named BOB doesn't exist in the currently active folder.
and
fat.newDirectory(string("SUSAN")) will fail if a directory by the name of SUSAN already exists in the currently active folder.
When either of the two conditions above occurs, code following the offending call does not execute indicating that the code is perhaps caught in an infinite loop (a repeat?). Rather than digging too deep into foreign code I was hoping you could help as I may be missing something obvious. Maybe I am calling the functions incorrectly, but valid directory passes with the same syntax seem to function as expected.
Am I coding incorrectly? I could work around most of these issues by using the list functions but hangs are scary critters in embedded systems especially those without native watchdogs.
Look at only PUB funtion in my demo and notice the "\" abort trap.
YOU NEED TO ABORT TRAP EVERY FUNCTION OR SETUP A SYSTEM LIKE IN MY DEMO TO HANDLE ABORTS GRACEFULLY
The aborts return a pointer to a string and set an error number. The error number can be gotten from calling the "partitionError" function. The function returns the error number (0 if no error) and then clears the error flag.
Note that errors "1 - 5" will unmount the file system and are not recoverable. You can get the nice names of the error numbers by looking in the FATEngine code and looking at the CON section.
@Dr_Acula - I confused by what you want.... Lets work togheter to develope "Kye Dos" You tell me what you want and I'll code it. Email me.
@bpranger - the way I have the code setup is optimal for flexibility and readability. If I did one big function then things would get realllllllllllllly messy.
If I understand your code right now, you drop thru multiple calls to every possible program that the person could have selected. You do a string compare to see if the word typed matches the method.
If a match does not occur, then you abort back and continue down to the next routine.
Why wouldn't replacing that same selection routine with one containing a Case statement that goes directly to the method requested be more efficient than calling every method looking for a match?
Maybe I don't still understand it or it compiles differently, but it seems that if I type in the word to run the last method, I believe you call every method before it and in each method, call a string compare function comparing the word of the command with stringpointer. Each StringCompare would fail and you would abort back and call to the next one until you reach the last method which would match, and you would execute it.
A Case statement would not make things messy, it would simply take the typed word, and directly execute the method requested without any calls to stringcompareCI and no additional calls to the earlier methods in the list.
I guess I don't understand why aborting from every method until there is a match is more efficient than calling the one you want thru a Case statement.
i don't mean to be critical, I just am trying to understand the underlying logic in your spin routine.
As far as I know a string is a multibyte value. You can't use a case statment.
That's the problem.
I've just spent a day deep inside this code. The way it works is fine (albeit, having completely rewritten it *grin*).
I'm a basic programmer, and so much is done behind the scenes, even with the earliest of basics. The = sign is used for many different things. I know why other languages do it differently, because it is easier to write compilers, but for the end user consider:
A=B ' integers or single single or floating values
If A=B then.. ' equality test
A$=B$ ' strings made equal
If A$ = B$ then... ' equality test on strings
I've just been adding my favourite string routines to the string handler. left and mid etc. You can replicate a Case statement with a series of equality tests on strings. Logic is reversed but if you treat an ifnot as an if, you can use a series of
ifnot str.stringcompareci(@lineoftext1,@lineoftext2)
however, it isn't quite that simple as some routines change the strings when they tokenise, so if you did it his way you would have to copy all passed strings to another string.
Nothing impossible though. One could add a couple more methods to the string handler
where lineoftext is a true string that can be changed, or is string("fixed string")
Ok, I've been coding all day and got a lot further with KyeDOS.
We now can run any file that ends with .exe by just typing the name of the file without the extension. To play Movie.exe (which is actually Movie.binary renamed), just type "movie"
And to make it easier to find all the .exe files on the card (mine is getting quite cluttered), type
dir *.exe
The reboot is so amazingly fast that this is going to lead to new ways of doing things:
1) All spin programs could have some sort of way of quitting apart from hitting the reset button. Reboot is a standard spin instruction so it is easy to add to any spin program. Programs that quit then return to Kyedos (which boots very fast as well).
2) One can think about batch files. Run a program "batch myfile" and it creates a temporary file myfile.$$$ and each time KyeDOS reboots, it runs the first file off that $$$ file, then moves the list up one.
3) One can add an autoexec. If "autoexec.bat" exists on bootup, then run whatever is in that file.
4) And this leads to a need for simple text editing so you can edit batch files etc. It is getting close. I can now boot into kyedos, type "CPM" and then "WS" and have a word processor running in only a few seconds.
5) It ought to be possible for programs to chain each other. If separate spin program has the ability to write to the sd card, it could write a file called "SHELL.$$$" which contains one line. It then shuts itself down with a Reboot. Kyedos restarts, looks for SHELL.$$$, if found, reads the line of text, deletes the file, then runs the program. The fast reboots within Kye's code means that splitting a spin program up into lots of bits becomes a practical alternative to using external memory for big programs.
I have also added some flags at the beginning of the code
What this means is that it ought to be possible for anyone with an SD card to get KyeDOS working.
Re I was trying to describe this bit of code that runs the .exe files.
Attached new version
Would it be possible to use KyeDOS to allow the Proptool to download directly to an SD card and bypass the EEPROM?
First thing is that the Proptool resets the propeller, but that can be bypassed with a switch or jumper.
Next thing = is Kye's code fast enough to handle a download and move the data over to an sd card. I suspect it is.
Then one would need to ask what name to save this file as, and run it. Maybe BST and homespun could do this (it is just a few more characters to be sent down the serial port).
I might put a trace on the serial port during an eeprom or ram download and see what data is going back and forth and see if it is possible to trick the PC side into thinking it is downloading to an eeprom when it is actually going to the SD card. I have xmodem working but the Proptool does not talk using xmodem. Hopefully the protocol is simple.
Anyone else done something similar?
You could just modify that to make it work at 80MHz, and also write to the SDcard.
http://forums.parallax.com/showthread.php?101483-Propeller-ROM-source-code-HERE&highlight=hippy
I hope this helps.
I've gone back to looking at xmodem. To get xmodem running, I need to write an xmodem program. And in keeping with the philosophy of an operating system, rather than try to fit it all into the OS, put it in a separate executable.
Executables need a command line to pass parameters. I tried putting the data in high ram, but it appears a reload wipes out all the ram even if the program is smaller than the ram. So the next experiment is to try passing parameters via a small text file. I certainly have no problems writing a file. However, if just one thing goes wrong, the subroutine aborts and drops back to the start. I think this is part of the problem discussed earlier today - if you have "abort" instead of a series of "select case", and something goes wrong somewhere else, where does it abort to?
So maybe we do need a structured "select case", either in strings, or in numbers that have been translated from strings.
Alternatively, maybe it is possible to write to an sd card without ever creating errors.
I'm looking at two scenarios - the file exists, in which case write to it, or it does not exist, in which case create it then write to it. I can't seem to get all the options right though. Eg if I try to delete it and it does not exist then it errors. And reboots or hangs things because the error trapping is not passed up the chain properly.
Is there a standard order to create a file for writing, whether it exists or whether it does not exist.
The nature of the file system is complexity! So, whatever code you make for it will also be complex! No way arround that.
---
You should do this. Keep a log file of all the commands recently executed. Or use the EEPROM to keep a log of the last string entered.
If you used a log file then the last string executed could be recalled. Plus, it could be used for history.
at least not as I was thinking of it.
Thanks for a great SD routine and I'm looking forwards to KyeDOS. I am already using a SD card in my app, so the idea of calling and running spin files from the card is pretty neat.
Thanks for your help and patience.
Bruce
So - where is the best place to store it?
1) Propeller RAM. But I think that gets completely wiped with a reboot. Is that correct?
2) External RAM. This would be very convenient for me, but it would mean the code would not run on all boards without external ram. So no to this idea.
3) EEPROM. This could be an option. EEPROM does wear out eventually.
4) The SD card. This will wear out too, but it can be replaced more easily than EEPROM.
Could you explain this a bit more "Use the "Fat.partitionError" function... and just catch the aborts."
Also, my example for opening a file looks like it has more commands than are needed. Is there a better way? eg - rather than trying to open a file that might not exist, or delete a file that does not exist, is there a routine that tests for the existence of a file?
On PropCade I designed Layout for Bill Henning -- I added Battery Backup to last 2 SPI SRAMS that are on-board for that possibilitys (We have totally 6xSPI Sockets SRAM/FLASH compatible)
Hi Kye, that sounds simple enough. Sorry I'm being a bit thick with respect to abort traps. Are they nested? In other words, if you put an abort trap in front of the openfile function, will this upset the 'global' abort trap that is working on all the command translation?
What does the "abort trap" code look like?
23K256 dip8
Unfortunately, the manual seems to confirm what I'm worried about, viz, page 47 "When one of the simple methods determines a course of action, it can issue an abort that completely collapses the nested call chain and prevents all the intermediate methods from continuing." and "The ABORT command, however, repetitively pops data off the call stack until it reaches a caller with an Abort Trap (see below); returning to some higher-level caller that may have just been one call, or many calls, up the nested chain of calls. Any return points along the way between an aborting method and an abort trapping method are ignored and
essentially terminated."
Which might abort out of that PUB and back up to the next abort in the chain. I'll need to test it.
Maybe it is not such an issue, as the code to check for a command is the last one in the list.
I'm not that used to using aborts and similar structures. They seem a bit too much like "GOTO" in that code can jump around in unintended ways. I might see if I can change the global structure so it looks like a "select case".
I'll check it when I get home.
Aborts and abort traps were something I was unaware of up until about a day ago, so take my inputs with a grain of salt. While reading the manual did help, it left a few critical questions unanswered about how they actually behave.
"Which might abort out of that PUB and back up to the next abort in the chain. I'll need to test it." - All that matters is the active abort and the closest abort trap up the call chain. Whether the call functions along the chain are terminated by an abort or a return doesn't matter they will be skipped over. In this way you can completely skip subsequent code that is likely to fail anyway due to the error that just occurred. If a line of code failed to create a directory then the very next line will also fail if you try to change into that same failed directory, by placing a trap much higher up you skip all that non-sense and can deal with the error in a more general way (perhaps stop the SD card and warn the user).
I see the abort and abort traps less like a GOTO and more like a GetTheHeckOut, you are retreating to safer ground to regroup. While in function they might be similar to a goto you aren't jumping to a specified singular address rather to the next abort trap up the call chain. So it is completely feasible for the same exact abort (ex. changeDirectory to a directory that doesn't exist) to be trapped in different regions of the calling code depending upon where they were called with respect to the abort traps in your application.
In any case, I can't get Kye's code to work, and as far as I can see there is no manual, so I am going to have to ask Kye really nicely to explain this in language that is easy to understand. ie a couple of lines of code. "try" and "catch" are fine and I use them all the time in vb.net but there is no "try" in spin.
Ok, this is the very simple code fragment:
If I run it as above with the commented lines, it works fine.
If I uncomment the first two lines, and make sure that "TEXT3.TXT" does not exist it works fine.
If I uncomment the first two lines, and "TEXT3.TXT" does exist, it returns an error "Entry Already Exist"
What is the error trap that needs to be added to the first line to detect if the file already exists?
Or, to put it another way, here is some code that I expect to work but does not work
How do I get it to work so if it errors it stays in the current pub?
These errors occur in different functions in the file system. If you do stuff right you won't have to handle every error if you know your input is correct. But stuff like DISK I/O ERROR should be handled at all times.
I've started catching the return values from the fat methods (called with an abort trap). For now I'm just displaying the abort codes but I might add a case statement to take actions depending on what code was returned. Kye lists the errors in the CON section of SD3.01_FATEngine. I count 17 possible errors.
You can have several abort traps chained but only the trap clossest to the abort is used.
I've also started listing the methods (with an indicator if it uses a trap) that might call a particular method here's an example. I use a "|" to indicate a call normal call and a "\" to show it was called with an abort trap.
In the above example, I call UseDevice from Main with a trap. UseDevice calls OpenDevice (no trap) and OpenDevice calls EnterButtonId with an abort trap. Even though there are two abort traps in the chain of calls, it aborts back to OpenDevice. OpenDevice then looks at the returned value to see it it's a normal expected value or one of the possible abort codes.
If the above method was called via "Main\LearnNewDevice|Record" then it aborts back to the Main method. As Miner points out sometimes you just want to jump back to the top and take care of things there.
Someone please correct me if I'm not understanding this correctly.
Duane
Edit: Kye just beat me too it. Maybe some my stuff will still help.
Woot! Something works. This code is not optimised, but it does run whether the file exists or does not exist. Does it need any more abort traps etc?
Addit: Now this is getting fun. I can type a command "xmodem r myfile.txt" and it runs "xmodem.exe" and with a few lines of code in that new program I can retrieve the command line.
Next thing is to parse it and then run the code. I already have xmodem code so I just need to write the parser on the command line. I think there might already be one in Kye's code already.
Why is this useful?
Well, consider the very real problem of not having enough memory. KyeDOS itself is already almost filling the 32k, so adding more commands is not going to be practical. KyeDos v2 contained xmodem code for instance, but there isn't room in v3.
But the amazing thing about v3 code is that it is so fast. Shelling out to another binary, running the binary and returning to the main program is only taking a couple of seconds.
Suddenly this opens up a new way of coding that does not need external memory. Just run programs that pass messages via text files and which can chain each other.
And if KyeDOS can pass messages to other binaries, then the messages can go the other way as well. A program can run that leaves a message for KyeDOS when it restarts. eg - run this batch file.
All very useful!
The Kyedos code is getting to a stage of being stable now, so I might start a new thread rather than hijacking this one too much
I have some other ideas for simple programs that can be run from the command line as if they are internal commands to the OS - eg TYPE to dump out a file and I also need to have some demo programs that put back the scope and a few other things I had to delete in the OS to make room.
KyeDOS is text and maybe I need to think about a "graphical" version that uses the mouse and icons?