JMPREL

I'm working on a little program that uses a state variable to jump to specific handlers:
loop jmprel state
jmp state_0
jmp state_1
jmp state_2
jmp state_3
At the top of each state handler the code looks to see if it should run based on an IO pin assignment -- if the associated pin # is negative the state doesn't run. The active pins do not change, so is there way way that I can check all of the pins in use and modify how the jump code works?
Comments
That description sounds more like a bit mask than a state select. Each pin controls one function each, right?
Well, it is setup like a simple state machine that will iterate through each of the possible IO pins; if a pin is used, a handler is called. My simple framework is fine, I was just wondering if there was a more elegant way to gate the JMPREL part.
This better illustrates the framework.
neg state, #1 next_state incmod state, #3 jmprel state jmp state_0 jmp state_1 jmp state_2 jmp state_3 check_0 testb s0pin, #31 wc ' state 0 pin used? if_nc call s0_handler ' if yes, run code for s0pin jmp #next_state
For a jump table, JMPREL is perfectly tidy. And as long as the table is located in cog address space then a double branch like that is efficient too.
I once chose to make an alternative, using a 16-bit table, because all the target routines were also located in cog address space.
getnib pb, cmdaddr, #7 ' first 4 bits is command altgw pb, #cmdjmp ' lookup table of 16-bit pointers getword pb jmp pb cmdjmp word cs_low ' CMD0 - Probably a null-command due to CS low unintentionally word queryconfig ' CMD1 - QueryFeatures word cs_low ' CMD2 word cs_low ' CMD3 word readblock ' CMD4 - BlockRead word writeblock ' CMD5 - BlockWrite word rdwrblock ' CMD6 - BlockReadWrite word cs_low ' CMD7 word cs_low ' CMD8 word cs_low ' CMD9 word cs_low ' CMD10 word cs_low ' CMD11 word cs_low ' CMD12 word cs_low ' CMD13 word cs_low ' CMD14 word cs_low ' CMD15
Do you know the used pins at compile time? In this case the new #ifdef could be used?
If the decision must be made at run time:
In my 4-axis stepper driver I work a lot with tables. One table could hold the flags "is used" a second table could hold the addresses of the driver routines. Or perhaps combined zero for unused. So you can iterate with a loop.
No; the app sets those pins as required.
Thanks for the feedback, guys.
You could create a bitfield of all enabled pins. Then you can simply do a TESTB on that and skip jumping around if the pin is disabled.
I had thought about that too, but on reading you comment I think I caught myself wanting to be clever when it's not necessary. This is simple and does the trick (I use this in my normal serial code).
pri test_loop(p0, p1, p2, p3, p4) org .main waitx ##CLK_FREQ testb p0, #31 wc if_nc call #.state0 testb p1, #31 wc if_nc call #.state1 testb p2, #31 wc if_nc call #.state2 testb p3, #31 wc if_nc call #.state3 testb p4, #31 wc if_nc call #.state4 jmp #.main .state0 debug("State 0 (RX0)") ret .state1 debug("State 1 (TX0)") ret .state2 debug("State 2 (RX1)") ret .state3 debug("State 3 (TX1)") ret .state4 debug("State 4 (DMX)") ret end
Given the way DMX works a state machine is required there (I have that working) -- I think just dragged the state machine idea into the main part of the program. Simple is best.
If you're not pinching at every cycle and byte, yeah, do the simpler thing
Round robin task switching.