flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > switch-case with jump table

Author
Thread Post new topic Reply to topic
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7492
Location: Kraków, Poland
Tomasz Grysztar
I have designed a switch-case macro which generates a jump table, usage is like:
Code:
switch [wmsg]
 case WM_CREATE
  ; ...
 case WM_DESTROY
  ; ...
 default
  ; ...
endswitch    

and generated code looks like (WM_CREATE=1, WM_DESTROY=2):
Code:
mov eax,[wmsg]
sub eax,1
jb default
cmp eax,2-1
ja default
jmp [table+eax*4]
table dd case_1,case_2
case_1:
 ; ...
 jmp done
case_2:
 ; ...
 jmp done
default:
 ; ...
done:    

Of course this makes sense only for large amounts of cases.

You can omit defining the "default" block. Switches can be nested safely to any depth.

And here are the macros:
Code:
macro switch var
{ local table,min,max,def,done
   if ~ var eq eax
    mov eax,var
   end if
   if min
    sub eax,min
    jc def
   end if
   cmp eax,max-min
   ja def
   jmp [table+eax*4]
   label table dword
   times max+1-min dd def
  local cmin,cmax,flags
  flags = 0
  macro case value
  \{ if flags and 2 = 0
      flags = flags or 2
     else
      jmp done
     end if
     if flags and 1 = 0
      cmin = value
      cmax = value
      flags = flags or 1
     else
      if value < cmin
       cmin = value
      end if
      if value > cmax
       cmax = value
      end if
     end if
     virtual
      dd $
      load dest@case dword from $-4
     end virtual
     if value-min < max+1-min
      store dword dest@case at table+(value-min)*4 
     end if \}
  macro default
  \{ if flags and 2 = 0
      flags = flags or 2
     else
      jmp done
     end if
     flags = flags or 4
     def: \}
  macro finish@switch
  \{ if flags and 4 = 0
      def:
     end if
     done:
     max = cmax
     min = cmin \} }

macro endswitch
{ finish@switch
  purge case,default,finish@switch }    


Note that this macro mainly utilizes the assembly-time features.
Post 08 Jul 2005, 10:26
View user's profile Send private message Visit poster's website Reply with quote
Vasilev Vjacheslav



Joined: 11 Aug 2004
Posts: 392
Vasilev Vjacheslav
thanks, great macro

what about 'break' command? like in c/c++ switches

somethins like this
Code:
switch [wmsg]
 case WM_CREATE
  ; ...
     break (jump to label1) 
 case WM_DESTROY
  ; ...
     break (jump to label1)
 default
  ; ...
endswitch

label1:
    
Post 08 Jul 2005, 11:39
View user's profile Send private message Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7492
Location: Kraków, Poland
Tomasz Grysztar
This macro work's rather like a Pascal switch, where each case after executing its code goes to the end of switch instead of continuing to the next case.

However it's easy to modify the above macro the the C-like version:
Code:
macro switch var
{ local table,min,max,def,done
   if ~ var eq eax
    mov eax,var
   end if
   if min
    sub eax,min
    jc def
   end if
   cmp eax,max-min
   ja def
   jmp [table+eax*4]
   table dd max+1-min dup def
  local cmin,cmax,flags
  flags = 0
  macro case value
  \{ if flags and 1 = 0
      cmin = value
      cmax = value
      flags = flags or 1
     else
      if value < cmin
       cmin = value
      end if
      if value > cmax
       cmax = value
      end if
     end if
     virtual
      dd $
      load dest@case dword from $-4
     end virtual
     if value-min < max+1-min
      store dword dest@case at table+(value-min)*4
     end if \}
  macro default
  \{ flags = flags or 4
     def: \}
  macro break
  \{ jmp done \}
  macro finish@switch
  \{ if flags and 4 = 0
      def:
     end if
     done:
     max = cmax
     min = cmin \} }

macro endswitch
{ finish@switch
  purge case,default,break,finish@switch }    

used like:
Code:
switch [wmsg]
 case WM_CREATE
  ; ...
  break
 case WM_DESTROY
  ; ...
  break
 default
  ; ...
endswitch    
Post 08 Jul 2005, 12:10
View user's profile Send private message Visit poster's website Reply with quote
madmatt



Joined: 07 Oct 2003
Posts: 1046
Location: Michigan, USA
madmatt
How about a break command for the .while, .repeat, and .if macros too?
Post 09 Jul 2005, 05:25
View user's profile Send private message Reply with quote
mike.dld



Joined: 03 Oct 2003
Posts: 235
Location: Belarus, Minsk
mike.dld
'break' could be used not only at the end of case label, but also somewhere in the middle, allowing to jump to the next instruction after 'switch' (this is allowed in both C and Pascal). As an example, imagine a code like this:
Code:
...
case 123:
  if (a > b)
    break;
    ... // execute if a <= b
  break;
case ...:
...    
Post 09 Jul 2005, 06:06
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7492
Location: Kraków, Poland
Tomasz Grysztar
This is exactly how "break" works here, too.
Post 09 Jul 2005, 08:11
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7108
Location: Slovakia
vid
"break" is just a jump somewhere - all code of .while, switch etc is just linear code without any modifications of stack so, you can "jump" out of it from any place to anywhere in code with regular lable and "jmp".
Post 10 Jul 2005, 18:34
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
IceStudent



Joined: 19 Dec 2003
Posts: 60
Location: Ukraine
IceStudent
Hi! I tried to use this macro, it very helpful.

But it needs in a some trick, because the local labels, defined after it, are lost his visibility:
Code:
switch [wmsg] 
 case WM_CREATE 
  ; ... 
 case WM_DESTROY 
  ; ... 
 default 
  jmp .ret
endswitch
 ; ...
.ret:
  ret    


You must replace local def, done variables to ..def, ..done with corresponding corrections in macro code.
Post 21 Sep 2006, 10:06
View user's profile Send private message Reply with quote
Vasilev Vjacheslav



Joined: 11 Aug 2004
Posts: 392
Vasilev Vjacheslav
try to use 'jmp default.ret'
Post 21 Sep 2006, 10:59
View user's profile Send private message Reply with quote
IceStudent



Joined: 19 Dec 2003
Posts: 60
Location: Ukraine
IceStudent
Hm.. I tried it..

A switch table seems too big:
Code:
        switch [uMsg]
                case WM_CLOSE
                        mov             eax,1
                case WM_SHOWWINDOW
                        mov             eax,2
                default
                        xor             eax,eax
        endswitch
......
MOV     EAX,DWORD PTR SS:[EBP+C]
SUB     EAX,10                                     ;  Switch (cases 10..18)
JB      SHORT ntlm_tes.00011109
CMP     EAX,8
JA      SHORT ntlm_tes.00011109
JMP     DWORD PTR DS:[EAX*4+110D7]
DD      ntlm_tes.000110FB                          ;  Switch table used at 000110D0
DD      ntlm_tes.00011109
DD      ntlm_tes.00011109
DD      ntlm_tes.00011109
DD      ntlm_tes.00011109
DD      ntlm_tes.00011109
DD      ntlm_tes.00011109
DD      ntlm_tes.00011109
DD      ntlm_tes.00011102
MOV     EAX,1                                      ;  Case 10 of switch 000110C6
JMP     SHORT ntlm_tes.0001110B
MOV     EAX,2                                      ;  Case 18 of switch 000110C6
JMP     SHORT ntlm_tes.0001110B
XOR     EAX,EAX                                    ;  Default case of switch 000110C6    
Post 21 Sep 2006, 13:55
View user's profile Send private message Reply with quote
ghfphx



Joined: 08 Oct 2006
Posts: 3
Location: Kenya
ghfphx
Couldn't this also work for a switch case statement?

Code:
macro switch var {
  local end, dest, def
  value@switch equ var
  dest@switch equ dest
  flag@switch = 1
  end@switch equ end
  def@switch equ def
  jmp dest@switch
}

macro case value {
  if value = value@switch
    flag@switch = 0
    dest@switch:
  end if
}

macro default {
  if flag@switch
    flag@switch = 2
    def@switch:
  end if
}

macro break {
  jmp end@switch
}

macro endswitch {
  if flag@switch = 2
    dest@switch:
    flag@switch = 0
    jmp def@switch
  else if flag@switch = 1
    dest@switch:
  end if
  end@switch:
  restore value@switch
  restore dest@switch
;  restore flag@switch
  restore end@switch
  restore def@switch
}    


The only thing is that for some reason, the default doesn't goes through a continuous loop unless you use a break statement.

Also does anyone know how to restore constants to their old values?
Post 11 Oct 2006, 17:52
View user's profile Send private message Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7108
Location: Slovakia
vid
Quote:
Also does anyone know how to restore constants to their old values?

no direct way, as with equates. but you can store value in other constant
Post 11 Oct 2006, 18:16
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4633
Location: Argentina
LocoDelAssembly
mmm, how about adding a feature like "constantVal push" and "constantVal pop"? fasm already get rid of such things at preprosessing time, is it very hard to implement for assembling time constants? The difference with preprocessing time is that "push" is implicit where in assembling time it would be explicit.
Post 11 Oct 2006, 19:04
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
can't it be done without destroying value ?
I mean, when I'm doing something like this, it destroys value.
Code:
mov eax,0
switch eax
case 5
xor eax,eax
break
case 1
mov eax,5
break
default
mov ecx,-1
break
endswitch
nop    

after this, eax=-1(destroyed) and ecx=-1(default)
Should I do this with pushes and pops ? or can someone add some functions to not destroy value ? Thanks.
Post 09 Sep 2011, 02:11
View user's profile Send private message Reply with quote
typedef



Joined: 25 Jul 2010
Posts: 2914
Location: 0x77760000
typedef
I hope this will be in the next FASM update (if there's one at all)
Post 09 Sep 2011, 03:10
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1398
Location: Toronto, Canada
AsmGuru62
A simple CMP+JE/JNE is much more readable.
Post 09 Sep 2011, 15:22
View user's profile Send private message Send e-mail Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4633
Location: Argentina
LocoDelAssembly
AsmGuru62, Tomasz's macro assembles to something a little bit more complex than that (jump tables).

Overflowz, you'll need to test this, but I think that replacing:
Code:
   if min
    sub eax,min
    jc def
   end if
   cmp eax,max-min
   ja def
   jmp [table+eax*4]    

With:
Code:
   if min
    cmp eax,min
    jc def
   end if
   cmp eax,max
   ja def
   jmp [table+(eax-min)*4]    
Should work.
Post 09 Sep 2011, 17:40
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
as I see, this will only work with EAX register (my guess, I'm newbie.) If this is what I say like that, could you just replace it to work with every regs, mem locations, etc.. ?
Post 09 Sep 2011, 20:14
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2019, Tomasz Grysztar.

Powered by rwasa.