Shop OBEX P1 Docs P2 Docs Learn Events
Case statement problem — Parallax Forums

Case statement problem

hippyhippy Posts: 1,981
edited 2007-09-21 23:28 in Propeller 1
I have the need to execute one of a number of methods ( subroutines ) depending upon a variable's value, and want to do this quickly and with minimum of code space. ideally I'm looking for a Spin equivalent of 'ON var GOSUB sub0, sub1, sub2,sub3".

It's easy enough to use a Case statement, but that comes with quite an overhead of execution time as it steps through each CASE bytecode ...

====                                  ; PUB CaseTest
====                                  ;   Case result
====                                  ;     0 : Sub0
====                                  ;     1 : Sub1
====                                  ;     2 : Sub2
====                                  ;     3 : Sub3

0028         38 3A           S7       PUSH     58
002A         60                       PUSH     RESULT.LONG
002B         35 0D 0C                 CASE     0, J8
002E         36 0D 0D                 CASE     1, J9
0031         37 00 0D 0D              CASE     2, J10
0035         37 21 0D 0D              CASE     3, J11
0039         0C                       GOTO[noparse][[/noparse]]
003A         01 05 02        J8       CALLSUB  S13
003D         0C                       GOTO[noparse][[/noparse]]
003E         01 05 03        J9       CALLSUB  S14
0041         0C                       GOTO[noparse][[/noparse]]
0042         01 05 04        J10      CALLSUB  S15
0045         0C                       GOTO[noparse][[/noparse]]
0046         01 05 05        J11      CALLSUB  S16
0049         0C                       GOTO[noparse][[/noparse]]
004A         32              J12      RETURN   




The best way I can see of improving execution speed is to have a single CALLSUB opcode and poke the required method's ID number ( 3rd byte ) at run time. That requires a certain amount of trickery to determine the ID numbers to start with.

All PUB and all PRI methods have consecutive ID numbers sequentially from one as they appear in source code, but would still need to find the first numbered if I did not want to put them at the top of the source file. By making all methods PRI and only the first and those called in this jump code PUB would be a simple 'idNumber := jumpIndex+2'.

Finding the address to poke to for the CALLSUB is another challenge, but might be achievable by peeking what's on the stack in a method called from within a Case statement ( the first PUSH $3A is the address of the RETURN less base address of the object, $10 because it's a top-level object ). Could get interesting when this isn't the top-level object, but let's start simple (sic) first.

It's all quite complex, and I expect reasonably pointless to ask, "has anyone solved this problem ?", but if anyone has any suggestions I'm open to them.

For the Propeller Tool, it would be a great boon if something like the following could be added, and it doesn't look to me that this would require anything more than appropriate code generation, so current Rom firmware should not be an issue ...

optionalVar := gosub( indexVar : Sub0, Sub1, Sub2, Sub3 )

It couldn't be implemented quite how I described because methods may be out of order, or even in sub-objects, but an indexed table jump to a method call would be much quicker than a Case or long sequence of If-Elseif.

Any chance of such a thing happening ?

Comments

  • deSilvadeSilva Posts: 2,967
    edited 2007-09-21 15:03
    You should find useful stuff here: http://forums.parallax.com/showthread.php?p=593662
  • hippyhippy Posts: 1,981
    edited 2007-09-21 16:52
    @ deSilva : Some interesting stuff in there which I will work through. Thanks.

    Have got a self-modifying 'on-gosub' working but relies on knowing the address of the CALLSUB. Now to determine that address at run-time ...
  • hippyhippy Posts: 1,981
    edited 2007-09-21 18:42
    What a pain ...

    The first 16 bytes of Ram ( where stack pointer initialisation pointers are for reboot / reset ) do not change no matter how deep in subroutine calls when checking them so they are read only and the stack pointer is within the Cog and thus inaccessible. Obvious with hindsight.

    With a bit of twiddling and poking a POP bytecode into the executable code it is possible to extract the top of stack value and store in a variable. Unfortunately two problems; the address to put the POP bytecode needs to be found ( we're going round in circles now ), and it doesn't appear that the top of stack is the return address of the calling method. Presumably the return address is lower down the stack. So a simple 'pokeAddress := poppedReturnAddress-K' is out of the question.

    There is a mechanism by which the image 'object tree' can be traversed so an address to poke the CALLSUB as originally envisioned can be found regardless of whether a top object or sub-object, but that's more work than I'm prepared to take on at present.

    I've resorted to nested Case statements testing different bit fields of the index variable. It adds code bloat but increases average execution speed.
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-21 19:28
    PhiPi has addressed all those problems nicely - see his workarounds and "peeks"" and "pokes" in the link! A main issue will be how to find the context of another object!!
  • hippyhippy Posts: 1,981
    edited 2007-09-21 20:07
    deSilva said...
    PhiPi has addressed all those problems nicely - see his workarounds and "peeks"" and "pokes" in the link! A main issue will be how to find the context of another object!!

    I have to admit some of that was quite beyond me and will take some reading, but I did find another way to 'skin the cat' courtesy of PhiPi and his stack usage monitoring concept ... Put a known word on the stack, then find it, and the return address is found below it. Only works for a top-level object and initialisation has to be called before the known word is accidentally put on the stack, but it works for me ...
  • rokickirokicki Posts: 1,000
    edited 2007-09-21 23:28
    Look at my Singleton (do a search) object; it should have most of what you need.

    Here's the thread:

    http://forums.parallax.com/forums/default.aspx?f=25&m=165107
Sign In or Register to comment.