The Spaghetti Style   ..



This nomenclature refers to Sources performing wild jmps, backward and forward, every now and then, by authors who want to promote the structured programming style. It is always introduced as something you should never do.


Completely avoiding this style may be a good recommendation to beginners, but, for the Assembly Language, the spaghetti style is quite natural, as we are completely free to jump from any Instruction to any other one, and / or to play at will with the calls, jumps stack managements.


So, let us first see what is wrong with this style. The following example is taken from the RosAsm Source. This comes from one of the very first Routines I ever wrote for the RosAsm Assembler, years ago. At that time, I had had no experience with 32 Bit programming, and I applied the rules of programing I had retained from my A86 DOS days. Today, I would never write anything like this, but, I leave this Routine as is, inside the RosAsm Source, as a  relic of my own writing evolution.


This Source abstract is 100% unreadable, as you can see, but it does a lot of very complex parsing of an asm text Source, with as few operations as possible. In some way we could say that it is optimized at a logical level :)). It is a pure example of what we should never do:


L3: cmp B$edi-1 NoSpaceAfterThis | jb L9>>


L4: cmp al LowSigns | ja L7>

      mov B$InsideWinEqu &FALSE


L4: cmp al, CommaSign | jne L5>

      cmp B$esi ',' | je L9>>

        mov al, Space | jmp L8>

                          

L5: cmp al, EOI | jne L7>


L6:   cmp B$edi-1 Separators | jb L9>>

        jmp L8>

                   

L7: On W$edi e 0209,  dec edi

                             

    cmp al Openbracket | jne L8>

      cmp B$edi-1 EOI | jne L8>

        cmp B$edi-2 Closebracket | jne L8>

          dec edi | dec D$StripLen


L8: inc D$StripLen | stosb


L9: On esi b ecx, jmp L1<<



Nevertheless, in some cases, the spaghetti style may be accurate:


    ..Else

L6:     movsb | cmp B$esi '|' | je L7>

                cmp B$esi 13 | jne L6<

L7:     mov al 13 | stosb | mov al 10 | stosb | mov al ' ' | stosb

    ..End_If



To perform the same computing with a better structured formulation, we could propose something like:


..Else

movsb

While B$esi  <>  '|'

If B$esi  <>  13

      movsb

Else

Exit_While

  End_If

End_While

       mov al 13 | stosb | mov al 10 | stosb | mov al ' ' | stosb

..End_If


As you may guess, the second version would be a little bit slower, because of the extra jmps involved by the HLL Macros, but,  frankly, not that much more readable, is it? I even have some doubts it could be considered less readable...


For the Assembly Language, jmps are the natural way for routing the Code flow. Stating that we open the door to Spaghetti style as soon as we make use of wild jmps is both right and wrong: I think the good definition should state that we really open that bad door as soon as we make use of nested and crossed jmps, in cases when we could or should make use of calls and rets.


The general rule is that spaghetti style should be avoided, but a bit of this style is not a crime against purism. The only relevant criterium is readability. As soon as your hard branchings lead you to something you have pain to read, stop it. Under this limit, there is no shame, as long as it is simple, readable and very short scope.


This style is also particularly useful for implemeting very flexible calls to Routines flow organizations. Four examples:



Example 1


WriteSignedImm32:

    mov ebx D$esi | add esi 4 | jmp L0>

WriteSignedImm16:

    movsx ebx W$esi | add esi 2 | jmp L0>

WriteSignedImm8:

    movsx ebx B$esi | inc esi

L0: push 0-1

.....


In this example, the same chunk of Code, the one after L0, is accessed from 3 different entry points. The very stupid way would be to re-write entirely 3 different Routines. Another way would be of making a separate sub-Routine begining at L0, which would be called from the 3 variants, which, in turn would be ended by a ret. But what for??? Isn't it simpler this way???



Example 2


The second example is quite simple: In all complex computations, you need  error(s) management. As the Routines required for showing the error to the user will not be re-written as many times as error cases and locations, you will have, usually, only one error holding, for a wide range of Routines and sub-Routines. When the errors can occur at any depth level of your Application Routines' organization, you need something to restore the Stack Pointer, what ever Routines-calls depth the error happened. This is quite simple to do the wild way:


before any critical run, you save the Stack Pointer:


mov D$OldStackPointer esp


Then at the Entry Point of the Main Error Routine, something like:


mov esp D$OldStackPointer


Though it could be discussed, I range this  technique inside the Spaghetti Style category because it breaks with the clean usual organization of calls and rets.



Example 3


The third example is something we should avoid as much as possible (but I used it several times inside RosAsm Source, because of its simplicity):


Say, we are in some FirstRoutine (call from somewhere else). Then, from inside this FirstRoutine, we call another one (SecondRoutine), that will ask something of  the final user, and return either &TRUE or &FALSE. In the second case (&FALSE), we want to abort the whole process. That is, the proper way is to implement, inside FirstRoutine, some conditional Code to exit properly. If there is nothing else to do before leaving, I sometimes do this, inside SecondRoutine:


pop eax  |  ret


This is to say that the return Address (the one to FirstRoutine) is stripped off and that we return immediately to the Caller of FirstRoutine (the caller of the caller). No intermediate ret, no success Flag, no nothing. This technique is pretty bad because the source is later more difficult to maintain, and a later error introduction much more difficult to point out. Nevertheless, good in very simple cases...



Example 4


The fourth example is the routing I used in the RosAsm Disassembler. This feature is quite simple and linear, as each analysis of the Code Bytes simply routes the flow to a Routine for outputting the Source. Each time it was possible, I wrote this flow one way. This is to say that, instead of calling for some specific treatement Routine, I stated a jmp to it, so that multiple nested actions Routines are run in turn with only one final ret to the very first caller. This technique is completely accurate for very simple organization of Code flow. Though, it fully falls in the Spaghetti style definition, as it effectively makes use of jmps, in circumstances where, usually, we should make use of calls. This last example could be used as a demonstration that the Spaghetti style may be good and accurate, even if we have to firmly recommend that beginners should be careful to keep away from this.


~~~~~~~