Structure is in the eye of the beholder

(Original version)

Assembly and VB6 being my preferred languages, I cop a lot of flak from ‘real programmers’. The most common are that VB isn’t OO (which is in fact the main reason I use it) and that ASM is ‘spaghetti code’. I’m not going to touch the VB one, but I intend to show (using examples, and hopefully I can get Geshi to highlight everything right) that the choice of language doesn’t necessarily determine the quality of the structure.

I’ve tried my hardest to contrive a fair example to use to compare C and Assembly; the result is massively optimisable (though my [old] version of GCC didn’t do a very good job when I tried it…) but uses nested if’s and function calls

Consider the following C code, part of an ‘initialisation’ procedure:

int value1, value2;
 
int checkCondition1();
int checkCondition2();
 
void main()
{
   if(checkCondition1() != 0)
   {
      value1 = 1;
   }
   else
   {
      if(checkCondition2() == 0)
      {
         value1 = 2;
      }
      else
      {
         value1 = 3;
      }
   }
 
   if(value1*2 < 3)
   {
      value2 = value1;
   }
   else
   {
      value2 = 0;
   }
}
 
int checkCondition1()
{
   return (1*1);
}
 
int checkCondition2()
{
   return (24-24);
}

The actual purpose and content of the code is irrelevant, it’s contrived solely to demonstrate that structure is determined by the programmer, not the language.

The above example is ‘good’ structure. Lets convert it to ‘bad’ structure:

int value1, value2;
void main(){
if((1*1)!=0) value1 = 1;
else {if((24-24)==0) value1 = 2;
else value1 = 3;}
 
if(value1*2 < 3)
value2 = value1;
else value2 = 0;
}

Infinitely harder to read, separate (and probably more complicated) operations have been inlined and all structure is gone. Yet C still understands, compiles and executes it.

Now lets look at the equivalent assembly language code:

.data?
        value1 DWORD ?
        value2 DWORD ?
 
    .code
 
Main:
    call    CheckCondition1
    cmp     eax, 0
    je      @F
      mov   value1, 1
      jmp   Value1Set
    @@:
      call  CheckCondition2
      cmp   eax, 0
      jne   @F
        mov value1, 2
        jmp Value1Set
      @@:
        mov value1, 3
  Value1Set:
 
    mov     eax, value1
    add     eax, eax
    cmp     eax, 3
    jge     @F
      mov   eax, value1
      mov   value2, eax
      jmp   Value2Set
    @@:
      mov   value2, 0
  Value2Set:
 
    xor     eax, eax
    retn
 
 
CheckCondition1:
    mov     eax, 1
    imul    eax, 1
    retn
 
CheckCondition2:
    mov     eax, 24
    sub     eax, 24
    retn
 
END

Looks a bit messy, doesn’t it. Of course, very few people program in ‘pure’ assembly anymore (obviously the purists do… and some of them don’t even use indents), many (most?) use the high level abilities of MASM, Microsoft’s Assembler.

.data?
        value1 DWORD ?
        value2 DWORD ?
 
    .code
 
Main PROC
    call    CheckCondition1
    .if(eax)
        mov   value1, 1
    .else
        call  CheckCondition2
        .if(eax)
            mov value1, 2
        .else
            mov value1, 3
        .endif
    .endif
 
    mov     eax, value1
    add     eax, eax
    .if(eax < 3)
        mov   eax, value1
        mov   value2, eax
    .else
        mov   value2, 0
    .endif
 
    xor     eax, eax
    ret
Main ENDP
 
 
CheckCondition1 PROC
    mov     eax, 1
    imul    eax, 1
    ret
CheckCondition1 ENDP
 
 
CheckCondition2 PROC
    mov     eax, 24
    sub     eax, 24
    ret
CheckCondition2 ENDP
 
END

Of course, once you add lines between procedures it becomes easier to read still. And the ‘PROC’ directive gives a stack-frame for free. And really, once the fear of one-operation-per-line has passed, it’s really not any harder to read than well structured C.

Poor programmers aren’t allowed to give a tool a bad name.