Handy P2 PASM2 feature: _RET_ PUSH {#}D
rogloh
Posts: 5,787
I just found a very handy feature I can use in PASM2 to determine where to jump next, and it sort of lets code sequences be chained together dynamically. I can already make very good use of this in my HyperRAM driver.
To use it you can do a _ret_ while you push new data in the same instruction. The top of stack element will essentially be swapped with a new value you decide on and you still get to return to the original caller at the same time, all in the same single instruction. I think this feature might be useful for quite a few things when you combine it with execf too.
I tested it and it seems to work but I didn't try it exhaustively. Hopefully no hidden gotchas if the stack wraps around while using it, etc.
To use it you can do a _ret_ while you push new data in the same instruction. The top of stack element will essentially be swapped with a new value you decide on and you still get to return to the original caller at the same time, all in the same single instruction. I think this feature might be useful for quite a few things when you combine it with execf too.
call #pusher .... _ret_ _any_instruction_ ' code will now branch off to something determined by the pusher routine pusher .... _ret_ push #return_addr
I tested it and it seems to work but I didn't try it exhaustively. Hopefully no hidden gotchas if the stack wraps around while using it, etc.
Comments
It won't be a race condition, at all. It's just a matter of the steering logic and what it does when both return and push are happening at the same time.
I need to get my head around the ramifications of using it.
Also these:
Also, the hardware is set up so that only one value can be popped at a time. So, I think the first example would do a return and put the return address into 'a'.
becomes
Not a drop-in replacement, but is it similar enough?
It will consume one stack entry and you have to branch between your code fragments at the same stack level so it is not quite the same as the JMPRET which is probably a little more versatile. But I do think this feature could be used in interesting ways and could save some COGRAM at times.
1. This is a potentially very useful discovery that risks becoming forgotten in a few days' or weeks' time. I suggest editing the title to include the most useful instruction, as follows:
Handy P2 PASM2 feature: _RET_ PUSH {#}D
(Aside. Are the pixel doubling routines buried deep somewhere in a video thread?)
2. Implementing _RET_ to POP the return address before the PUSH is the only way round that adds any value. Great design work!
3. Thinking about XBYTE, this could be exactly what I was looking for here assuming that $1FF is no longer top of stack after the PUSH. Apart from my example of a cleanup routine, it could be a way of passing a parameter from one bytecode to the next using the hardware stack.
4. A Where Am I? routine could be a single long:
N.B. PC register also contains copy of C and Z as high bits.
_ret_ push value
...what happens is the 'push value' occurs, while a jump to the return address also takes place. Afterwards, you have returned, as expected, and the bottom of the stack contains the pushed value, while the next level of the stack contains the address that was returned to. So, it kind of makes a mess of the stack.
Stack before:
7: ?
6: ?
5: ?
4: ?
3: ?
2: ?
1: ?
0: return address
Stack after:
7: ?
6: ?
5: ?
4: ?
3: ?
2: ?
1: return address
0: value
I could have made it NOT push or pop the stack if BOTH were happening, but it never occurred to me that anyone would write code like that.
So the stack creeps and wraps around ?
That's likely to be a big problem with interrupts, but may be ok/tolerable if you know interrupts will never be used.
How about debug of this code ? IIRC that uses interrupts to step, so this may not be step-able ?
I guess we should probably see what happens with the _RET_ POP {#}D case too and how it leaves things. It might be possible to gain some value from that if it does something interesting as well by creating some other side-effect behaviour with the stack etc.
Agreed, it's still useful.
EDIT:
I wouldn't say the stack gets "corrupted" as POPs could remove the unwanted already-used return addresses, if necessary. The effective stack depth is reduced, though.
ptr_w is the signal that the cog receives during its COGINIT. It's used to push the execution address onto the hardware stack, so that you can find out where the cog hung up, if need be.
It's good to know just what will happen tho.
I think that seven consecutive POPs will guarantee that all eight longs on the stack are the same and further POPs will keep reading this value, assuming no intervening CALLs or PUSHes.
Does reset clear the stack?
A chip reset clears the stacks, but not a cog start or stop.
Just a fun comment based on my reading of the Verilog posted above. The seven longs below top of stack move up one level for each POP and long furthest from top stays the same.