I'm trying to get my head around the SPIN2 exit sequence. When a COG finally "returns", is there any way to pass the return value to the calling COG? Or is this something that is always needed to be done using some type of hub address mailbox being polled by a different COG?
I guess my question is can a completing SPIN2 COG optionally return a result back synchronously somehow in the same COG by returning to some provided hub exec address passed in as an argument instead of only doing a COGSTOP and shutting down?
I think this has been touched on before in the past and it might be useful to be able call a SPIN2 function from some other environment and return back to the original caller (which obviously will need its own cleanup/reloading of register state etc if the same COG is used by the caller). Maybe if some address argument is zero when launched it ultimately shuts down with COGSTOP, otherwise it can continue on with hub exec at that provided address and the stack can be used to return the result parameter from SPIN2?
In the above case it might also be handy to be able to launch SPIN2 without a COGINIT so the COG I/O state is preserved. I guess that could be done by setting things up appropriately before jumping directly to launch_spin instead of coginiting to it.
The ability for one program to load, run, and get a result back from a Spin program that is/has executed on another cog would be wonderful. Could a “mailbox” address to hold the result be passed to the Spin program at invocation?
My use case is this: I’d love to “boot” a Spin “process” on an idle cog from FlexC, FlexASM, or FlexBASIC, and occasionally “check-back” for a result. This would allow me to access Spin functionality without needing to load it as a class.
The way it is set up right now, when a Spin2 program ends, it stops its own cog. When launching a Spin2 program, though, you can pass it parameters. One of those parameters can be an address of a mailbox into which it deposits a final result before stopping itself.
We would need to develop some scheme for having a cog load up the Spin2 interpreter, run some Spin2 code, and then return back to the caller in hub RAM. Actually, it could even return right to the cog's register memory. Registers $000..$15B are currently free.
If WYPIN is not needed, just use 0 for the y parameter. No harm done.
Remember that the pin parameter can express additional pins in bits 10..6. All those PASM instructions work that way. So, you could start up a whole bunch of DACs or PWMs with one instruction and then go set their individual output values, as needed.
Looking at launch_spin I think a lot of it is mostly there, if we can just return to some given hub exec code address when the SPIN2 code "returns", it could do different things like either write the result to some nominated mailbox/address before shutting down, or returning itself to its own caller in the same COG with results on some stack. If something is going to be possible one way or another I'm happy. It looks like the "stopcog" byte code address below is the key to this, if it can be patched to use something else before startup.
I think having the ability to call SPIN2 code and get returned results from other environments is going to be great, even independent of whether it runs in different COGs or not, that's mainly just another optimisation to save a COG if it is going to be doing nothing useful while the SPIN2 code is running, though I see that in some cases preserving the IO pin state could be useful too if some IO work is done in one environment while other part are done in SPIN2 etc. This doesn't necessarily have to be a really fast interface, it's probably more for spawning tools and commands etc, so if a lot of COG/LUT state is saved/restored between these SPIN2 invocations when they are executed from the same COG as the caller, that might still work out okay in many cases.
' Launch Spin - runs via COGINIT
'
' on entry:
'
' ptra[0] = pbase
' ptra[1] = vbase | method<<20
' ptra[4+] = any parameters
'
' on exit:
'
' ptra[-4] = pbase | trap flag
' ptra[-3] = vbase
' ptra[-2] = dbase @params...
' ptra[-1] = return (w) @COGSTOP(COGID)
' ptra[ 0] = params...
'
launch_spin setq #2-1 'get pbase/vbase
rdlong pbase,ptra
or pbase,#%10 'set pbase 'trap' flag
mov dbase,ptra 'set dbase to @params
add dbase,#4*4
mov w,#@stopcog 'set return to COGSTOP(COGID) bytecodes
setq #4-1 'write pbase/vbase/dbase/w into stack
wrlong pbase,ptra++
andn pbase,#%11 'restore pbase address
setq #cog_end-cog_code-1 'load cog code (overwrites dcall register)
rdlong cog_code,#@cog_code
setq2 #$200-1 'load lut code
rdlong $000,##@lut_code
mov dcall,dbase 'set dcall (must be after loading cog code)
push #wrf_rd 'return to wrf_rd to start xbyte after callinit
skip ##%111100000000_000_0 'begin bytecode execution of method in vbase[31:20]
jmp #callinit
If you want to call some spin code from within spin, you only need to call it. It will return to you. Coming in from another language system is different, of course.
Some programming languages use SET and GET - as in SETPIN or GETPIN. So SETPIN would be CFGPIN, etc... and GETPIN would get the result value. Just a thought.
Some programming languages use SET and GET - as in SETPIN or GETPIN. So SETPIN would be CFGPIN, etc... and GETPIN would get the result value. Just a thought.
Pedward called tonight and we went over all these names. I think we made a big improvement:
I wasn't thinking so clearly, myself. I wanted shorter names. Turns out, we realized we could make first-4-letter aliases of all these names to keep code looking clean, if the programmer wants to use them:
I pulled method indexing out of the interpreter and compiler. I had been thinking about Eric's, Roy's, and David's dislike for them, since they do inhibit dead code removal in compilers. I came to agree that they were likely going to cause more problems than benefits. They are gone now!
Eric, yes, WRPIN, WXPIN, WYPIN, etc. are still needed for flexibility. PINMODE just consolidates 5 steps that have to happen to set up most modes. Some modes don't require the WXPIN and/or WYPIN, but if they get written with 0's during initialization, it's fine.
You can see we'll need some labels for common modes:
con dacpin = 56
pub go() | t, y
pinm(dacpin, %10100_00000000_01_00011_0, 256, 0) 'set 16-bit DAC mode
repeat
_,y := polxy($7FFF, t++ << 24) 'get rotating sine
wypin(dacpin, y ^ $8000) 'output to DAC
Eric, yes, WRPIN, WXPIN, WYPIN, etc. are still needed for flexibility. PINMODE just consolidates 5 steps that have to happen to set up most modes. Some modes don't require the WXPIN and/or WYPIN, but if they get written with 0's during initialization, it's fine.
Sounds good.
You can see we'll need some labels for common modes:
con dacpin = 56
pub go() | t, y
pinm(dacpin, %10100_00000000_01_00011_0, 256, 0) 'set 16-bit DAC mode
repeat
_,y := polxy($7FFF, t++ << 24) 'get rotating sine
wypin(dacpin, y ^ $8000) 'output to DAC
Again, couldn't the definitions be placed in objects? Does Spin2 still support notation like "dac#mode16" for getting the constant "mode16" from object "dac"?
I'm pondering requiring parentheses for all methods, since it would let us sensibly use a period, instead of the #, for object constants. Just seems cleaner:
object.method()
object.constant
Variable methods must have parentheses, anyway. If we enforce them everywhere, then you'll always know when your looking at a method. Not sure what to do, yet.
There is some pressure to dumb things down for education, making them foolproof. I think the fun thing about the P2 is that you can see an understandable bridge from Spin2 to assembly language, which enables you to learn a lot more about how things really work. To get really amazing things happening, the programmer is going to need to be able to use PASM, or rely on objects which use PASM code.
I'm pondering requiring parentheses for all methods, since it would let us sensibly use a period, instead of the #, for object constants. Just seems cleaner:
object.method()
object.constant
Variable methods must have parentheses, anyway. If we enforce them everywhere, then you'll always know when your looking at a method. Not sure what to do, yet.
I think that does look cleaner. I actually supported that notation in fastspin as an extension to Spin1 (it recognizes "object#constant" and "object.constant" interchangably). I'm not sure how your compiler works internally, but I have "kinds" associated with all symbols in an object so I can tell if a symbol is a constant, method, variable, or label. So fastspin don't actually need any special markers to distinguish constants and methods, although perhaps they would be helpful for readers. I don't have any strong feeling either way about what syntax Spin2 should use.
I'm pondering requiring parentheses for all methods, since it would let us sensibly use a period, instead of the #, for object constants. Just seems cleaner:
object.method()
object.constant
Variable methods must have parentheses, anyway. If we enforce them everywhere, then you'll always know when your looking at a method. Not sure what to do, yet.
If you are looking for votes, I'd like to cast mine for that syntax. IMHO, *all* methods need parentheses for exactly the reasons you have cited.
I'm not convinced that pinw, pinr is better than the ones spelled out: PinWrite, PinRead. TLAs and acronyms are a burden in learning. Yes it's slightly more typing. Think outside the ASM mnemonic box.
I'm not convinced that pinw, pinr is better than the ones spelled out: PinWrite, PinRead. TLAs and acronyms are a burden in learning. Yes it's slightly more typing. Think outside the ASM mnemonic box.
Chip, I'm with @whicker too. Typing is fast, remember abreviations think not.
I pulled method indexing out of the interpreter and compiler. I had been thinking about Eric's, Roy's, and David's dislike for them, since they do inhibit dead code removal in compilers.
Is the new compiler going to do dead code removal? That would be great. I've had a couple commercial P1 projects where I would edit in Propeller Tool, but be forced to compile to binary with BST to reduce size enough to fit into the EEPROM.
I'm pondering requiring parentheses for all methods, since it would let us sensibly use a period, instead of the #, for object constants. Just seems cleaner:
object.method()
object.constant
I agree with this. In big programs I force myself to use camelCase for variables so that I can distinguish between variables and methods, but I don't like it -- other than CONSTANTS, I want to use lower case. I support using () on methods that don't require arguments.
I'm not convinced that pinw, pinr is better than the ones spelled out: PinWrite, PinRead. TLAs and acronyms are a burden in learning. Yes it's slightly more typing. Think outside the ASM mnemonic box.
You can use either. These are the same thing, for example:
I pulled method indexing out of the interpreter and compiler. I had been thinking about Eric's, Roy's, and David's dislike for them, since they do inhibit dead code removal in compilers.
Is the new compiler going to do dead code removal? That would be great. I've had a couple commercial P1 projects where I would edit in Propeller Tool, but be forced to compile to binary with BST to reduce size enough to fit into the EEPROM.
I'm pondering requiring parentheses for all methods, since it would let us sensibly use a period, instead of the #, for object constants. Just seems cleaner:
object.method()
object.constant
I agree with this. In big programs I force myself to use camelCase for variables so that I can distinguish between variables and methods, but I don't like it -- other than CONSTANTS, I want to use lower case. I support using () on methods that don't require arguments.
Dead code removal would take some work. I'd have to scoreboard which methods get called and have that data carry through the object distiller, which eliminates redundant objects. I think, in time, I'll get there.
What I'd like to work on before too long is equation solving, so that if an equation can be solved at compile-time, it just generates a constant. Eric's compiler does this.
Dead code removal would take some work. I'd have to scoreboard which methods get called and have that data carry through the object distiller, which eliminates redundant objects. I think, in time, I'll get there.
Good, thanks. I'm sure it will be less a problem for the P2, but for the P1 it has been an issue for a few of my commercial apps.
Dead code removal would take some work. I'd have to scoreboard which methods get called and have that data carry through the object distiller, which eliminates redundant objects. I think, in time, I'll get there.
Good, thanks. I'm sure it will be less a problem for the P2, but for the P1 it has been an issue for a few of my commercial apps.
This is all getting very exciting.
I thought more about it since that last post.
If I can just use one upper bit in each method index long, I can track whether that method has been referenced. The object distiller, upon finding matching objects, would OR those bits together. At the top level, you'd have awareness of every method in every object, whether they've been called from within any of the compiled instances, or not. Then, as a final step that would only be done at the top level, you would strip all methods out of all objects, stack the objects sans methods together, then add back in only the methods which had been referenced, and update all the long index pointers to point to them properly. At that time, you could also check for any redundant methods that might have existed in different objects, and eliminate those, too.
Comments
It might also be useful to have this object if you use it to maintain the pin's current state too.
WRXPIN(pin, mode, x)
WRXYPIN(pin, mode, x, y)
I need to review the modes to see how these ought to work.
I guess my question is can a completing SPIN2 COG optionally return a result back synchronously somehow in the same COG by returning to some provided hub exec address passed in as an argument instead of only doing a COGSTOP and shutting down?
I think this has been touched on before in the past and it might be useful to be able call a SPIN2 function from some other environment and return back to the original caller (which obviously will need its own cleanup/reloading of register state etc if the same COG is used by the caller). Maybe if some address argument is zero when launched it ultimately shuts down with COGSTOP, otherwise it can continue on with hub exec at that provided address and the stack can be used to return the result parameter from SPIN2?
My use case is this: I’d love to “boot” a Spin “process” on an idle cog from FlexC, FlexASM, or FlexBASIC, and occasionally “check-back” for a result. This would allow me to access Spin functionality without needing to load it as a class.
We would need to develop some scheme for having a cog load up the Spin2 interpreter, run some Spin2 code, and then return back to the caller in hub RAM. Actually, it could even return right to the cog's register memory. Registers $000..$15B are currently free.
CFGPIN(pin,mode,x,y)
...does this...
FLTL pin
WRPIN mode,pin
WXPIN x,pin
WYPIN Y,pin
DRVL pin
If WYPIN is not needed, just use 0 for the y parameter. No harm done.
Remember that the pin parameter can express additional pins in bits 10..6. All those PASM instructions work that way. So, you could start up a whole bunch of DACs or PWMs with one instruction and then go set their individual output values, as needed.
I think having the ability to call SPIN2 code and get returned results from other environments is going to be great, even independent of whether it runs in different COGs or not, that's mainly just another optimisation to save a COG if it is going to be doing nothing useful while the SPIN2 code is running, though I see that in some cases preserving the IO pin state could be useful too if some IO work is done in one environment while other part are done in SPIN2 etc. This doesn't necessarily have to be a really fast interface, it's probably more for spawning tools and commands etc, so if a lot of COG/LUT state is saved/restored between these SPIN2 invocations when they are executed from the same COG as the caller, that might still work out okay in many cases.
Nice!
Pedward called tonight and we went over all these names. I think we made a big improvement:
I wasn't thinking so clearly, myself. I wanted shorter names. Turns out, we realized we could make first-4-letter aliases of all these names to keep code looking clean, if the programmer wants to use them:
These are shorthand for the full names.
Pedward had some ideas about helper instructions for smart pins:
More thought needs to go into the helpers, if they are to exist.
No more of these options:
object[index].method[index]
object.method[index]
method[index]
We still have these, which are most practical:
object[index].method
object.method
method
I'm still a bit skeptical of the need for DAC and PWM built-ins; if we had DAC and PWM objects then we could still have methods like: and the code for the object would be visible to the programmer, enabling them to learn from it, instead of being hidden inside the compiler.
Now that you have PINMODE are you still planning to provide seperate WRPIN, WXPIN, and WYPIN instructions in Spin2?
You can see we'll need some labels for common modes:
Again, couldn't the definitions be placed in objects? Does Spin2 still support notation like "dac#mode16" for getting the constant "mode16" from object "dac"?
I'm pondering requiring parentheses for all methods, since it would let us sensibly use a period, instead of the #, for object constants. Just seems cleaner:
object.method()
object.constant
Variable methods must have parentheses, anyway. If we enforce them everywhere, then you'll always know when your looking at a method. Not sure what to do, yet.
I think that does look cleaner. I actually supported that notation in fastspin as an extension to Spin1 (it recognizes "object#constant" and "object.constant" interchangably). I'm not sure how your compiler works internally, but I have "kinds" associated with all symbols in an object so I can tell if a symbol is a constant, method, variable, or label. So fastspin don't actually need any special markers to distinguish constants and methods, although perhaps they would be helpful for readers. I don't have any strong feeling either way about what syntax Spin2 should use.
If you are looking for votes, I'd like to cast mine for that syntax. IMHO, *all* methods need parentheses for exactly the reasons you have cited.
I would also like to make some switches for use within CON blocks to gate which constants become public.
I think I will make () mandatory for all methods.
Chip, I'm with @whicker too. Typing is fast, remember abreviations think not.
I agree with this. In big programs I force myself to use camelCase for variables so that I can distinguish between variables and methods, but I don't like it -- other than CONSTANTS, I want to use lower case. I support using () on methods that don't require arguments.
You can use either. These are the same thing, for example:
PINTOGGLE(pins)
PINT(pins)
Dead code removal would take some work. I'd have to scoreboard which methods get called and have that data carry through the object distiller, which eliminates redundant objects. I think, in time, I'll get there.
What I'd like to work on before too long is equation solving, so that if an equation can be solved at compile-time, it just generates a constant. Eric's compiler does this.
This is all getting very exciting.
I thought more about it since that last post.
If I can just use one upper bit in each method index long, I can track whether that method has been referenced. The object distiller, upon finding matching objects, would OR those bits together. At the top level, you'd have awareness of every method in every object, whether they've been called from within any of the compiled instances, or not. Then, as a final step that would only be done at the top level, you would strip all methods out of all objects, stack the objects sans methods together, then add back in only the methods which had been referenced, and update all the long index pointers to point to them properly. At that time, you could also check for any redundant methods that might have existed in different objects, and eliminate those, too.