I looked at your code for setting up the Spin header, and I was confused at first until I realized that the first four bytes are missing. Because of this I think you will have to adjust the offset you add to PBASE and PCURR. It might be easier to include the first four bytes and not worry about the adjustment. Also, you are adding 4 to m_stack to get dbase, but m_stack is an int32 array, so that's adding 16 bytes. We should add 8 bytes to account for the initial stack frame, which is 2 int32's. Also, the space between dbase and dcurr should remain the same as it was in the original header. You are adding 32 to an int16 pointer, which is 64 bytes. I think it's better to compute the original difference between dbase and dcurr at the beginning, and then add it to the new dbase to get the new dcurr.
However, the 32 constant in the last line is actually calculated by spinwrap by subtracting dbase from dcurr as you suggest. There is no need to do that calculation at runtime.
I still need to figure out how to invoke a method of an arbitrary object instance. Right now I'm always calling a method of the first object instance that I create.
Yes, I see that the original difference between dbase and dcurr was 0x20 or 32. However, params->dbase is a uint16_t pointer, so "+ 32" applied to that is actually plus 64 bytes. It should be "+ 16" to add 32 bytes. Isn't C great? It automatically scales things to match the size of the type. Of course that means we have to undo the automatic scaling when we want to add byte offsets. I often cast the darn pointer to a char pointer or an int, add the offset, and then cast it back to the original pointer type.
Yes, I see that the original difference between dbase and dcurr was 0x20 or 32. However, params->dbase is a uint16_t pointer, so "+ 32" applied to that is actually plus 64 bytes. It should be "+ 16" to add 32 bytes. Isn't C great? It automatically scales things to match the size of the type. Of course that means we have to undo the automatic scaling when we want to add byte offsets. I often cast the darn pointer to a char pointer or an int, add the offset, and then cast it back to the original pointer type.
It's not a pointer. It's just a uint16_t. You can't use a pointer because that would be 32 bits wide.
I fixed a bunch of bugs and added some LED test code and it's starting to work. I'm at least running the Spin proxy code and am even able to invoke a method from the Spin object I'm trying to call. However, my mailbox interface doesn't seem to work correctly. No matter what value I pass as the method index it always seems to come through as zero.
If I run this on a QuickStart board I end up with LEDs 16, 17, and 18 lit but the method I'm trying to invoke has the index 1 so it should light LED 19 instead of 18. Anyone see the problem?
The spinwrap program takes as input the Spin object source file and produces a C++ class and a Spin proxy that calls the Spin object methods. It uses OpenSpin to compile the Spin proxy. I've attached the source for the working version of spinwrap.c. It is currently setup to work on the Mac but the only thing that should need to be changed to run under Windows or Linux is the name of the OpenSpin binary. I have it set to "openspin.osx" for the Mac but "openspin.exe" or just "openspin" should work for Windows and "openspin.linux" for Linux. I'll make that automatic in a future version of the code.
However, this version only works with a single instance of the Spin object. The C++ wrappers pass a pointer to a separate VAR section for each C++ instance of the Spin object but the Spin proxy doesn't do anything with that value. That means that it always uses the VARs for the first object created when the proxy program is first launched in a COG. I'd appreciate some help figuring out how to patch in the VAR pointer for other instances. I assume that I'd do this by patching the object table of the proxy object but I'm not sure how to do that since the object reference in the object table is an offset not an absolute address.
Congratulations! Spinwrap will be a useful tool for interfacing C/C++ code to the Spin objects in the OBEX. It's another nice addition to the Prop toolbox.
Congratulations! Spinwrap will be a useful tool for interfacing C/C++ code to the Spin objects in the OBEX. It's another nice addition to the Prop toolbox.
Thanks but I'm not sure it's really the right way to do this. The more I think about it the more I think that spin2cpp is a better way of doing this. The spinwrap tool creates C++ wrappers to call a Spin object that runs in another COG. However, given the structure of most OBEX objects, that Spin code probably has Spin wrappers to call PASM code. This means there are two levels of wrappers in the spinwrap solution where there is only one in the spin2cpp solution since the C++ wrappers get linked with the code that calls them. Does anyone see any reason that someone would want to choose spinwrap over spin2cpp? I can't really think of any advantages to the spinwrap approach.
Also, spinwrap is a proof of concept. Its parser is very dumb and only scans the Spin source files for PUB statements. It doesn't really understand Spin syntax especially comment syntax so it may find commented out PUB statements as well as live ones. Lastly, it doesn't parse the CON statements in the Spin source file but it should make CON values available to the C++ code like Spin does using the # syntax. Lastly, as I mentioned earlier, it currently only handles a single instance of the Spin object. I think that can be fixed but I'm wondering how far to take this given that it seems spin2cpp is a better solution.
Any opinions?
In the meantime, I've attached a zip file containing the source, a Makefile to build it, and the sample I've been using to test it.
One more thing, if it turns out that this ends up being a useful tool, I can relatively easily add an option to generate C code so it can work with Catalina C.
I got multiple object instances to work this morning. I've attached an updated zip file with the spinwrap tool as well as a simple test program that shows using multiple instances.
C++ test program:
#include <propeller.h>
#include "test.h"
int main(void)
{
test obj;
test obj2;
obj.set_pins(16, 17);
obj.blink();
obj.set_pin(18);
obj.blink();
obj2.set_pins(20, 21);
obj2.blink();
obj2.set_pin(22);
obj2.blink();
obj.blink();
obj2.blink();
while (1)
;
return 0;
}
Thanks Steve. At the moment the spinwrap program writes its output files to the current directory. Will you need a -o option to redirect to a different output directory? That wouldn't be too hard to add.
Running spinwrap on a .spin file produces two primary output files: a .h file containing the C++ class definition of the wrapper code and a .cpp file containing the wrapper code itself. Those files will need to be included in any SimpleIDE project that wants to use the Spin object. The spinwrap program also produces a .spin file containing the proxy code for calling the Spin object from C++ and a .binary file which is the result of compiling the proxy and the spin object with OpenSpin. Those files are not needed in the SimpleIDE project and should probably be automatically removed by the spinwrap tool.
Steve has been playing with my spinwrap code and has been having some problems trying to run the VGA_Text_Demo.spin program. I guess I'm hoping Dave Hein will chime in here. Do you know of any reason why the VGA text demo wouldn't work with the scheme I'm using to load and run Spin objects? One thing I wonder about is the nested Spin objects. In my test program I have a main proxy program that has one object and no sub-objects but the VGA text demo has VGA_TextDemo which includes VGA_Text which includes VGA. Are there any special considerations for relocating an object with nested objects that my code is missing?
It works OK for me -- at least in LMM mode. CMM didn't work for some reason. I just had to add the following main routine to the generated VGA_Text_Demo.cpp file.
int main()
{
VGA_Text_Demo xyz;
xyz.start();
return 0;
}
Also, I don't have OpenSpin on my system, so I used bstc instead. I ran this on my C3 board, so I also had to add "DIRA |= $8000" at the beginning of the start method in VGA_Text_Demo.spin.
I tried converting VGA_Text_Demo.spin to C, and making VGA_Text.spin the top object, and I get different results. It worked in CMM mode, but not in LMM. My main routine was this:
int main()
{
int i;
VGA_Text text;
char *tststr = "Now is the time";
DIRA |= 0x8000;
//start term
text.start(16);
text.str((int)"\r VGA Text Demo...\r\r\014\005 OBJ and VAR require only 2.6KB \014\001");
for (i = 0; i < 14; i++)
text.out(' ');
for (i = 0x0E; i <= 0xFF; i++)
text.out(i);
text.str((int)"\014\006 Uses internal ROM font \014\002");
while (1)
{
text.str((int)"\012\014\013\016");
text.hex(i++, 8);
}
return 0;
}
I found that calling text.out worked OK in LMM, but text.str did not. I tried turning optimization of with -O0, and neither CMM or LMM worked. So there's something odd going on, but I don't know exactly what it is.
I tried converting VGA_Text_Demo.spin to C, and making VGA_Text.spin the top object, and I get different results. It worked in CMM mode, but not in LMM. My main routine was this:
int main()
{
int i;
VGA_Text text;
char *tststr = "Now is the time";
DIRA |= 0x8000;
//start term
text.start(16);
text.str((int)"\r VGA Text Demo...\r\r\014\005 OBJ and VAR require only 2.6KB \014\001");
for (i = 0; i < 14; i++)
text.out(' ');
for (i = 0x0E; i <= 0xFF; i++)
text.out(i);
text.str((int)"\014\006 Uses internal ROM font \014\002");
while (1)
{
text.str((int)"\012\014\013\016");
text.hex(i++, 8);
}
return 0;
}
I found that calling text.out worked OK in LMM, but text.str did not. I tried turning optimization of with -O0, and neither CMM or LMM worked. So there's something odd going on, but I don't know exactly what it is.
Thanks for trying. I see now that Steve was building in LMM mode with no optimization. I tried switching to CMM mode with -Os and now the screen gets cleared to blank but the demo still doesn't show up. I guess there is a bug in spinwrap somewhere. By the way, I've checked spinwrap into the propgcc project under propgcc/demos/spinwrap. The version that is there has a few extra features like the ability to specify a different output path and a way to pass parameters that get used in the invocation of openspin. That is where I'll be making future changes rather than posting the sources to the forum.
''***************************************
''* VGA Text Demo v1.0 *
''* Author: Chip Gracey *
''* Copyright (c) 2006 Parallax, Inc. *
''* See end of file for terms of use. *
''***************************************
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
OBJ
text : "VGA_Text"
VAR
long stack[8]
PUB start | i
outa := 0
dira |= $8000
'cognew(blink, @stack) ' uncomment to blink the screen
text.start(16)
text.str(string(13," VGA Text Demo...",13,13,$C,5," OBJ and VAR require only 2.6KB ",$C,1))
repeat 14
text.out(" ")
repeat i from $0E to $FF
text.out(i)
text.str(string($C,6," Uses internal ROM font ",$C,2))
repeat
text.str(string($A,12,$B,14))
text.hex(i++, 8)
PUB blink
dira |= $8000
repeat
waitcnt(CLKFREQ<<3+CNT)
outa ^= $8000
waitcnt(CLKFREQ+CNT)
outa ^= $8000
The VGA_Text_Demo works for me with LMM mode now. The CMM variety is missing the palette colors for some reason.
How come you changed dira[15] to using an explicit bit mask? Was that necessary to get it working?
It's great that you were able to get this working but there is clearly something wrong with the way I'm loading and starting the Spin code. You shouldn't have to tweak this program to get it to work. I'll try to take a look at my code to make sure I'm getting all of the offsets right but it may not be for a few days. Sorry for the trouble you're having with this!!
Dave Hein: I'm trying to debug the VGA demo problem and am wondering about the following code in an example you posted:
' Zero the VAR area
longfill(vbase, 0, (dbase - vbase - 8) >> 2)
Where does the -8 come from? I've been calculating the size of the VAR area of an object by using dbase-vbase. Is the -8 because of the $fff9, 0, 0, 2 prefix to the return value that starts at dbase[0]? If so, I guess I'm allocating 8 too many bytes for the VAR space and 8 too few bytes for the stack. Maybe that's my problem.
Dave Hein: I'm trying to debug the VGA demo problem and am wondering about the following code in an example you posted:
' Zero the VAR area
longfill(vbase, 0, (dbase - vbase - 8) >> 2)
Where does the -8 come from? I've been calculating the size of the VAR area of an object by using dbase-vbase. Is the -8 because of the $fff9, 0, 0, 2 prefix to the return value that starts at dbase[0]? If so, I guess I'm allocating 8 too many bytes for the VAR space and 8 too few bytes for the stack. Maybe that's my problem.
Never mind. I answered my own question. I was allocating 8 bytes too much for the VAR area but that wasn't causing my problem. I had ordered a few statements wrong and was ending up setting up the initial stack incorrectly. I fixed the bug and now the VGA_Text_Demo works fine.
Never mind. I answered my own question. I was allocating 8 bytes too much for the VAR area but that wasn't causing my problem. I had ordered a few statements wrong and was ending up setting up the initial stack incorrectly. I fixed the bug and now the VGA_Text_Demo works fine.
Cool. Good fun David!
Yes, it works with CMM and LMM memory models. I'll do more testing now.
One more thing, if it turns out that this ends up being a useful tool, I can relatively easily add an option to generate C code so it can work with Catalina C.
Glad you found the problem. I had noticed the other issue about the extra 8 bytes in the VAR area. I guess I should have mentioned that earlier, but like you said it shouldn't cause any problems except for wasting 8 bytes.
I just pushed a new version of spinwrap that supports a -c option to generate C code instead of C++. I've tried this under PropGCC but haven't had a chance to try it under Catalina yet. I've attached a zip file containing the code for those who might not want to check out all of propgcc just to get this.
Comments
I still need to figure out how to invoke a method of an arbitrary object instance. Right now I'm always calling a method of the first object instance that I create.
If I run this on a QuickStart board I end up with LEDs 16, 17, and 18 lit but the method I'm trying to invoke has the index 1 so it should light LED 19 instead of 18. Anyone see the problem?
The Spin program I'm trying to invoke from C:
The C test program that tries to invoke the Spin object:
The generated Spin proxy code:
The generated C++ header file:
The generated C++ stub code:
Finally, I've attached the spinwrap.c code.
spinwrap.c
C++ program calling a Spin object:
Spin object being called:
The spinwrap program takes as input the Spin object source file and produces a C++ class and a Spin proxy that calls the Spin object methods. It uses OpenSpin to compile the Spin proxy. I've attached the source for the working version of spinwrap.c. It is currently setup to work on the Mac but the only thing that should need to be changed to run under Windows or Linux is the name of the OpenSpin binary. I have it set to "openspin.osx" for the Mac but "openspin.exe" or just "openspin" should work for Windows and "openspin.linux" for Linux. I'll make that automatic in a future version of the code.
However, this version only works with a single instance of the Spin object. The C++ wrappers pass a pointer to a separate VAR section for each C++ instance of the Spin object but the Spin proxy doesn't do anything with that value. That means that it always uses the VARs for the first object created when the proxy program is first launched in a COG. I'd appreciate some help figuring out how to patch in the VAR pointer for other instances. I assume that I'd do this by patching the object table of the proxy object but I'm not sure how to do that since the object reference in the object table is an offset not an absolute address.
spinwrap.c
Also, spinwrap is a proof of concept. Its parser is very dumb and only scans the Spin source files for PUB statements. It doesn't really understand Spin syntax especially comment syntax so it may find commented out PUB statements as well as live ones. Lastly, it doesn't parse the CON statements in the Spin source file but it should make CON values available to the C++ code like Spin does using the # syntax. Lastly, as I mentioned earlier, it currently only handles a single instance of the Spin object. I think that can be fixed but I'm wondering how far to take this given that it seems spin2cpp is a better solution.
Any opinions?
In the meantime, I've attached a zip file containing the source, a Makefile to build it, and the sample I've been using to test it.
spinwrap.zip
I'll do some integration testing tomorrow if possible and post some results.
C++ test program:
Spin object:
spinwrap.zip
I'll try testing this in SimpleIDE today.
Running spinwrap on a .spin file produces two primary output files: a .h file containing the C++ class definition of the wrapper code and a .cpp file containing the wrapper code itself. Those files will need to be included in any SimpleIDE project that wants to use the Spin object. The spinwrap program also produces a .spin file containing the proxy code for calling the Spin object from C++ and a .binary file which is the result of compiling the proxy and the spin object with OpenSpin. Those files are not needed in the SimpleIDE project and should probably be automatically removed by the spinwrap tool.
Thanks!
David
Thanks for helping!
David
The VGA_Text_Demo works for me with LMM mode now. The CMM variety is missing the palette colors for some reason.
Here's my main.c
It's great that you were able to get this working but there is clearly something wrong with the way I'm loading and starting the Spin code. You shouldn't have to tweak this program to get it to work. I'll try to take a look at my code to make sure I'm getting all of the offsets right but it may not be for a few days. Sorry for the trouble you're having with this!!
It's just easier for me to understand
Yes, it works with CMM and LMM memory models. I'll do more testing now.
Good stuff, David!
spinwrap.zip