Spin Simulator
Dave Hein
Posts: 6,347
This thread started out as a request for information on simulators that execute spin code. I then decided to write my own spin simulator which is call spinsim. The latest version of spinsim is included in this post.
The original message is given below.
Are there any simulators that run Spin bytecodes? I read about Gear and pPropellerSim, which are machine-level simulators that run PASM code. Has anybody run the Spin interpreter in these simulators?
A Spin simulator would allow Spin programs to run on other platforms. It would open up the use of the Spin programming language for other processors in addition to the Propeller. Certain functions, such as console I/O and file I/O could be handle by special objects that interface to the platform's OS.
The original message is given below.
Are there any simulators that run Spin bytecodes? I read about Gear and pPropellerSim, which are machine-level simulators that run PASM code. Has anybody run the Spin interpreter in these simulators?
A Spin simulator would allow Spin programs to run on other platforms. It would open up the use of the Spin programming language for other processors in addition to the Propeller. Certain functions, such as console I/O and file I/O could be handle by special objects that interface to the platform's OS.
zip
144K
Comments
Gear RUN entire Programs that are written for Propeller with some Visualization possibility's.
The simulator runs in a DOS window on a Windows machine. Extract the zip file into a convenient directory, such as the desktop. Start up a DOS window, and go into the spinsim001 directory. Run "spinsim hello.binary" and it should print "Hello World". Run "spinsim demo.binary" and it will execute a demo program that can print files in ASCII or hex. Try the "type hello.spin" and "dump hello.spin" commands in the demo program.
You can also get a listing of the bytecodes as they are executed. This is done with the "-l" option. The simulator can run the first N instructions by specifying the number of instructions, such as -20 for the first 20 instructions. Try running "spinsim -l hello.binary" to see all the instructions executed. "spinsim -l -20 hello.binary" will run the first 20 instructions and print them out.
Console and file I/O is implemented through rendezvous locations at $7ff8 and $7ffc. The simulator looks at these locations after executing each instruction, and it performs the specified command.
I used the same mnemonics that I used with the spasm spin bytecode assembler. Most of the mnemonics are fairly intuitive, except for maybe the memory access ones. There are four types - ld..., st..., la... and ex... for load, store, load-address and execute. Memory addressing is done relative to the object base, variable base, local stack base or an absolute addess -- these are specified with the letter o, v, l or a. Memory access sizes are either byte, word or long, which is specified with the letter b, w or l. As an example, a word can be loaded from the local stack area with a "ldwl" instruction (LoaD Word Local). A byte can be stored in the variable area with a "stbv" instruction (STore Byte Variable). The indexed mode can be specified by appending an "x" to the instruction, such as "ldwlx" and "stbvx". The first 8 longs in the local stack and variable areas can be accessed with a compact opcode by appending a "c" character, such as "ldllc" and "stlvc" (LoaD Long Local Compact, and STore Long Variable Compact). I'll write up a more complete description of the mnemonics later on.
Let me know if you have any questions or comments.
Dave
Any chance to test this with wine on linux? I tried, but console IO fails.
When you have cognew and friends, I'll test it with my SPIN debugger
When it can run Sphinx, we'll have an open source solution (if you publish the source).
Intriguing. Maybe this is a step towards Big Spin? It may well be quicker to build it first as a simulation, then port it to the real chip with external memory. So you don't have to worry initially about the nuts and bolts of the memory access code.
It seems that in order to write your simulation you have had to consider how memory works. That is fantastic.
Simple things first - define a byte in memory at a location higher than 32k. Can you read and write a byte to a location?
Second, define an array at a high memory location. This ought to be an extension of the above.
Third, and this is more complicated, integrate code from high memory.
I get confused thinking this through, but as a simple example, say you have a PUB and it is at the end of a large program and the program is over 32k. This PUB is different, because already it is going to be slower than PUBs near the beginning of the program. So the programmer has to think about that and put the fast PUBs near the beginning of the code.
What things does this PUB need to do?
Access bytes at fixed locations in hub ram and external ram. That should be easy.
Access bytes at variable locations in hub (and external) ram. This is more complicated, because the variable Myvariable might exist anywhere and only the compiler knows where. This is where your simulator could be the key. If you know where Myvariable ended up after compilation, then any PUB can access it, even PUBs that are in high memory.
What else? Call PUBs elsewhere in the program. Again, only after compilation will you know where they are.
The most complicated thing is how you run this PUB from external memory. You can run it as a LMM simulation in pasm. But maybe there is another way. If you move that PUB into hub ram, and if external PUBs were never greater than a certain size, then you could run that PUB as if it came from hub ram.
Or you might bring in external spin mnemonics one at a time, complete with references to locations of MyVariable which have been allocated as part of the compilation.
The part I have always got stuck on is what happens if that external PUB references a variable, eg Myvariable, which might be a common variable to the program? Only a compiler (or simulator) knows where Myvariable is.
Maybe there are other solutions too. How cool would it be to start off with a simulator with megabytes of memory! You can do that sort of thing in a simulator, then think about how it ports over to a real chip.
I'd be very interested to hear your thoughts on how you might solve these problems in a simulator.
Dr_Acula, the simulator doesn't do any memory checking, so it is possible to execute out of a flat memory space that is much larger than 32K. The memory pointers in the Spin interpreter are all 32-bit longs. These are pbase, dbase, vbase, pcurr and dcurr. However, the interpreter currently uses 16-bit words for in the method table and the stack frame. These would have to be increased in size to make it work for larger hub RAM, like the Prop 2 will have.
With external memory, every memory access will have to be checked to see if it's referencing hub RAM or external RAM. Most operations use the stack, so it would be reasonable to require that the stack is always in hub RAM. Memory accesses could be localized to a few small routines, so it would be fairly easy to control which RAM is being accessed. The tough part is fitting it in a single cog. I think SpinLMM could be extended further so that external memory routines could be added.
But most the addresses in an object are jumps not absolute addresses so there is still a reasonable chance it will work.
I've implemented all of the Spin operators and I moved the cog variables into a struct. This allowed me to create 8 instances of the variables. The simulator can now run all 8 cogs. I added another test program call test.spin. It starts up a new cog once per second. I also added "dir" and "run" commands to demo.spin. You can run hello.binary or test.binary from demo.binary.
The attached zip file contains all the source and binary files. The simulator is implemented in the three C files -- spinsim.c, debug.c and interp.c. There are very few comments. I'll add that later.
jazzed, I tried to port Sphinx to the simulator. I was able to get sphinx, lex and codegen to run separately. However, I couldn't get it to compile conio.spn because it didn't like something in the syntax. The Sphinx compiler implements a limited syntax, so there must be something in conio.spn that it doesn't support.
Dave
What error message did Sphinx give?
I take it there is no PASM interpreter? There are a few floating around that can be added ....
Thanks and Merry Christmas.
--Steve
Steve, I'll look at the changes you made for linux, and I'll make sure I keep them in. Yes, there is no PASM interpreter in spinsim. That was intentional, but I may have to add one in at some point.
sphinx2 contains the same files as sphinx0, but they are name *.spn and *.bin instead. The *.bin files are generated by the sphinx compiler, except for codegen.bin. I seem to run out of memory when linking this file.
sphinx1 contains various applications, such as dir, copy and del. Run "spinsim sphinx.bin", and then run the applications from sphinx by typing dir, copy or del.
Steve, I didn't get around to merging your linux changes. I do that in the next version.
Dave
I love your idea. But, I have one big question for you. Can you run the simulations without downloading to the Propeller itself? If you can do this, it will be cool. Test your before you download it to the Propeller.
You can use the Parallax Prop Tool, BST or homespun to compile the spin code and generate a binary file. spinsim loads the binary file and executes it.
Dave
Thanks, I will download the today simulator and give a try.
This version should be identical to the previous version for Windows machines.
Thanks Dave. Works good so far. I'll test with sphinx later.
--Steve
I don't see anything in conio that Sphinx shouldn't be able to handle, but I don't have a real Sphinx setup to verify. Is it real Sphinx or simulated Sphinx that's generating the error? I'm so confused.
What did you have to do to get it to work?
For the most part the main components of lex, codegen and link are unchanged. I did have to modify bintree.spn to use the Spin version of one of the methods instead of the PASM version. Most of the changes are in the drivers -- fdserial, isxfs, isxtv, sxfile, sxfs, sxkb and sxtv. I replaced them with versions that use conio and fileio.
I implemented the Execute method by loading a program called loader.bin in high memory, which then loads the target program in low memory. It was a little tricky because I had to save the name of the last file that was opened, and then pass the file name to loader.bin.
Dave
Everything is open source under the MIT open source license. I'll add the notice in the next update.
That's a relief. Thanks, Dave. You've had a pretty good year, Propeller-wise, haven't you? Spinix, Spin+LMM, SpinSim—what else? I'm sure I'm forgetting something.
BTW, I'm very impressed by Sphinx. The compiler is much more complete than I first thought. It was an amazing feat to shoe-horn it into the prop's 32K of RAM.
The bits/types.h defines the following:
char - 8 bits
short - 16 bits
int - 32 bits
long - 64 bits
So changing a few long to int took care if the issue. Here is a diff that does just that:spinsim-64bit.txt
compiler packages have the header, but it's easy to include simple typedefs in the source.
To use stdint.h all 32 bit int and long would become int32_t (uint32_t for unsigned).
Can you guess what unsigned short would be?
And sofar I only tried the some simple programs. Will try the file I/O next and see what happens
File I/O works for me.