News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

bitRAKE's SWITCH MACRO

Started by habran, February 22, 2016, 08:57:29 PM

Previous topic - Next topic

habran

Hello everyone :biggrin:
Does anyone know how to use this macro :dazzled::


Title Switch / Case
Author bitRAKE
Submitted by: bitRAKE
Date added: 2002-02-17 21:02:01
Date modified: 2002-02-17 21:02:01
Comments
This offers an optimized branching method for a large set of unordered cases.

Snippet
;#############################################
; Optimized Switch/Case Macro
; by bitRAKE (aka Rickey Bowers Jr.)
;#############################################


SwitchThreshHold = 3 ;when is a bad idea to partition


.switch MACRO
  SwitchNodes = 0
  SwitchDefault TEXTEQU <>
  SwitchExit TEXTEQU <>
ENDM


.case MACRO xVal:REQ, xNode:REQ
  @CatStr(<SwitchValue>, %SwitchNodes) = &xVal
  @CatStr(<SwitchNode>, %SwitchNodes) TEXTEQU <&xNode>
  SwitchNodes = SwitchNodes + 1
ENDM


.default MACRO def:REQ
  SwitchDefault TEXTEQU <&def>
ENDM


.endswitch MACRO sexit
  LOCAL TheEnd, ww, oo, temp1, temp2, temp3, temp4

  SwitchExit TEXTEQU <&sexit>

;; This is a bubble sort on the values of the case statements
;; the labels follow their associated values.
  ww = SwitchNodes
  WHILE ww
    ww = ww - 1
    oo = ww
    WHILE oo
      oo = oo - 1
      temp1 = @CatStr(<SwitchValue>, %oo)
      temp2 = @CatStr(<SwitchValue>, %ww)
;; We need MASM to evaluate this at assemble-time
%     IF &temp1 GT &temp2
;; Numberic values can be swaped easily
        @CatStr(<SwitchValue>, %oo) = &temp2
        @CatStr(<SwitchValue>, %ww) = &temp1
;; Strings are a little harder...
;; Get the variable names
        temp3 TEXTEQU @CatStr(<SwitchNode>, %oo)
        temp4 TEXTEQU @CatStr(<SwitchNode>, %ww)
;; Get the value of those varibles
;; MASM doesn't allow &@CatStr(...)!
        temp3 TEXTEQU &temp3
        temp4 TEXTEQU &temp4
;; Swap them
        @CatStr(<SwitchNode>, %oo) TEXTEQU &temp4
        @CatStr(<SwitchNode>, %ww) TEXTEQU &temp3
      ENDIF
    ENDM
  ENDM


;; This starts the trasversal of the array as if it were a bbtree
  .SwitchPartition 0, SwitchNodes - 1


;; Output the code for the case nodes that haven't been done already
  ww = SwitchNodes
  WHILE ww
    ww = ww - 1
;; Previously output nodes are cleared in the transveral
%   IFNB <@CatStr(<SwitchNode>, %ww)>
      @CatStr(<SwitchLabel>, %ww, <:>)
%     @CatStr(<SwitchNode>, %ww)
      IFNB <&SwitchExit>
%       &SwitchExit
      ELSE
        jmp SwitchExitLabel
      ENDIF
    ENDIF
  ENDM


SwitchDefaultLabel:

  IFNB <&SwitchDefault>
%   &SwitchDefault

    IFNB <&SwitchExit>
%     &SwitchExit
    ENDIF
  ENDIF

SwitchExitLabel:

ENDM



;; Transverse the sorted array of variables that was created for each
;; case statement like a balanced binary tree...
.SwitchPartition MACRO _min:REQ, _max:REQ
  LOCAL delta, mmin, HighHalf

  delta = _max - _min
  mmin = _min

  IF delta LT SwitchThreshHold
;; Output a string of nodes comparisons and a node on the end
    WHILE delta GT 0
%     cmp eax, @CatStr(<SwitchValue>, %mmin)
      je @CatStr(<SwitchLabel>, %mmin)
      mmin = mmin + 1
      delta = delta - 1
    ENDM
%   cmp eax, @CatStr(<SwitchValue>, %mmin)
    jne SwitchDefaultLabel
%   @CatStr(<SwitchNode>, %mmin)
;; Clear this label variable so we don't output the code again
    @CatStr(<SwitchNode>, %mmin) TEXTEQU <>
    IFNB <&SwitchExit>
%     &SwitchExit
    ELSE
      jmp SwitchExitLabel
    ENDIF
  ELSE
;; Output a branch test
    delta = _min + (delta/2)
%   cmp eax, @CatStr(<SwitchValue>, %delta)
    jg HighHalf
    je @CatStr(<SwitchLabel>, %delta)

;; Re-Enter this macro until we've tested all the nodes
;; note that we skip the node we just tested for.
    .SwitchPartition _min, (delta-1) ;; Lower half of the range
HighHalf:
    .SwitchPartition (delta+1), _max ;; High half of the range
  ENDIF
ENDM


© 2016 by Thomas Bleeker (MadWizard)
Cod-Father

qWord

Seems there is an bug in it: there are two lines like this:
IFNB <&SwitchExit>
If adding the expansion operator to that lines
%   IFNB <&SwitchExit>
the macros could be used as (e.g.):
mov eax,7 ; value to switch in eax

.switch
.case 1, <invoke foo1>    ; angle brackets could be omitted in this example...
.case 2, <invoke foo2>
.case 3, <invoke foo3>
.case 4, <invoke foo4>
.case 5, <invoke foo5>
.case 6, <invoke foo6>
.case 7, <invoke foo7>
.default <invoke foo100>
.endswitch
Not sure if that is the intended usage...
MREAL macros - when you need floating point arithmetic while assembling!

HSE

It's some kind of tree:



        SwitchValue equ ebx  ; I imagine (SwitchValue it's not defined in the macro)

.switch
.case <evaluation1>, 0 ; number it's branch rank (up or down?)
       .case <evaluation2>, 1
       .case <evaluation3>, 1
                          .case <evaluation4> , 2
.case <evaluation5>,  0
.case <evaluation6>, 0
.default <invoke foo100>
.endswitch
Equations in Assembly: SmplMath

habran

Thanks qWord, thanks HSE :t
I have fixed that what qWord sugested %   IFNB <&SwitchExit> in the first post and now it works fine 8)
here is example:

;eax is the test value
.switch
.case 1, jmp subproc1    ; angle brackets could be omitted in this example...
.case 2, jmp subproc2
.case 3, jmp subproc3
.case 4, jmp subproc4
.case 5, jmp subproc5
.case 6, jmp subproc6
.case 7, jmp subproc7
.default jmp subproc8
.endswitch

produce this:
00007FF70A5110BC 83 F8 04           cmp         eax,4 
00007FF70A5110BF 7F 15                jg          main+1Eh (07FF70A5110D6h) 
00007FF70A5110C1 74 2A                je          main+35h (07FF70A5110EDh) 
00007FF70A5110C3 83 F8 01           cmp         eax,1 
00007FF70A5110C6 74 29                je          main+39h (07FF70A5110F1h) 
00007FF70A5110C8 83 F8 02           cmp         eax,2 
00007FF70A5110CB 74 22                je          main+37h (07FF70A5110EFh) 
00007FF70A5110CD 83 F8 03           cmp         eax,3 
00007FF70A5110D0 75 24                jne         main+3Eh (07FF70A5110F6h) 
00007FF70A5110D2 EB AC                jmp         subproc3 (07FF70A511080h) 
00007FF70A5110D4 EB 22                jmp         main+40h (07FF70A5110F8h) 
00007FF70A5110D6 83 F8 05             cmp         eax,5 
00007FF70A5110D9 74 10                je          main+33h (07FF70A5110EBh) 
00007FF70A5110DB 83 F8 06             cmp         eax,6 
00007FF70A5110DE 74 09                je          main+31h (07FF70A5110E9h) 
00007FF70A5110E0 83 F8 07             cmp         eax,7 
00007FF70A5110E3 75 11                jne         main+3Eh (07FF70A5110F6h) 
00007FF70A5110E5 EB B9                jmp         subproc7 (07FF70A5110A0h) 
00007FF70A5110E7 EB 0F                jmp         main+40h (07FF70A5110F8h) 

   280: ;eax is the test value
   281:    .switch
   282:    .case 1, jmp subproc1    ; angle brackets could be omitted in this example...
   283:    .case 2, jmp subproc2
   284:    .case 3, jmp subproc3
   285:    .case 4, jmp subproc4
   286:    .case 5, jmp subproc5
   287:    .case 6, jmp subproc6
   288:    .case 7, jmp subproc7
   289:    .default jmp subproc8
   290:    .endswitch
00007FF70A5110E9 EB AD               jmp         subproc6 (07FF70A511098h) 
00007FF70A5110EB EB A3               jmp         subproc5 (07FF70A511090h) 
00007FF70A5110ED EB 99               jmp         subproc4 (07FF70A511088h) 
00007FF70A5110EF EB 87                jmp         subproc2 (07FF70A511078h) 
00007FF70A5110F1 E9 7A FF FF FF   jmp         subproc1 (07FF70A511070h) 
00007FF70A5110F6 EB B0                jmp         subproc8 (07FF70A5110A8h) 

00007FF70A5110F8 
Cod-Father

HSE

Very different to the tree I was thinking!

Some dicussion remains in http://www.asmcommunity.net/forums/topic/?id=179

Is an optimization for runtime, not for coding  :(
Equations in Assembly: SmplMath

habran

HSE, I saw that before :biggrin:
qWord, there was not error actually, it works fine without your suggestion, so I edited again the macro in my first post
It is very interesting macro, however it could be improved a lot.
For example in my example above there is no need for a binary tree, it should create a jump table and execute a jump
like:
jmp [@jumptable+rax*8]
or create a hash table if cases are scattered

Cod-Father

qWord

Quote from: habran on February 23, 2016, 09:38:11 AM
it works fine without your suggestion,
Yes, but only for your example. However, the macro code is definitively  wrong, because IFNB <&textMacroID> is always true and the specified else-clause will never be called. Actual there are 4 bugs of this form in that macros.
MREAL macros - when you need floating point arithmetic while assembling!

hutch--

The version that Greg Falen designed years ago still works perfectly, the string version that Michael Webster did works just as well.

jj2007

Quote from: hutch-- on February 23, 2016, 12:36:44 PM
The version that Greg Falen designed years ago still works perfectly

And that's an understatement ;-)

Quote from: jj2007 on May 05, 2013, 08:58:54 AMWe have a Switch/Case macro, and it has a lot more power than its C equivalent.

That was a nice thread btw :greensml:

habran

>qWord, can you please fix it and post it again here.
If you have some spare time try to add a jump table feature.
>hutch--, there is nothing wrong with those macros as long as you have few cases, however, if you have plenty, they would not be efficient. This one is much faster and with  a bit of improvement it can be even better.

I am doing a research to design the SWITCH - CASE hll for HJWasm and I think to create first a good MACRO and test it before implementing it would be a good idea.

Cod-Father

hutch--

Habran,

Long ago I did message processing in a WndProc() that used a table instead of a sequential set of comparisons and you could not see the difference. Be careful what you wish for, added complexity for no gain.

jj2007

Quote from: habran on February 23, 2016, 02:51:14 PMIf you have some spare time try to add a jump table feature.

Switch_:
Quoteunder the hood it creates a jump table that is for long lists of cases much faster than the Masm32 macro

The emphasis is on "long". As Hutch rightly noted, the difference is rarely relevant.

habran

>hutch--, as jj2007 said, emphasis is on long lists of cases, and you probably already know my second name is CANDO,
however, you didn't know that my third and real name is actually SIMPLIFIER :biggrin:
I am not gonna decrease quality of HJWasm, but enhance it, I will implement it only if we in this forum conclude that it will be nice new feature 8)
>jj2007, can you show me source and disassembly of some example with your Switch_, please
Cod-Father

jj2007

Quote from: habran on February 23, 2016, 07:52:48 PM>jj2007, can you show me source and disassembly of some example with your Switch_, please

See below (the macro itself is lines 524...618 of \Masm32\MasmBasic\MasmBasic.inc). I only added the PrintLine "end of loop" to the sample code. Disassembly covers the loop only; see in particular the long list of dd MbSw1D - one reason why it can be inefficient. And guess what happens if you add a Case_ 12345 ::)

            include \masm32\MasmBasic\MasmBasic.inc
            Init                                    ; ## Switch with jump table ##
            m2m ecx, -5
            PrintLine "----------------------------- testing the new MasmBasic Switch_ macro -----------------------------"
            .Repeat
                        Print Str$(ecx), Tb$
                        Switch_ ecx
                        Case_ -2
                                    PrintLine "Case -2"
                        Case_ 0
                                    PrintLine "Case NULL"
                        Case_ 10
                                    PrintLine "Case 10"
                        Case_ 18
                                    PrintLine "Case 18"
                        Case_ 14 .. 16
                                    PrintLine "Case 14 .. 16"
                        Default_
                                    PrintLine "---"
                        Endsw_
                        inc ecx
            .Until signed ecx>20
            PrintLine "----------------------------- end of loop -----------------------------"
            EndOfCode


Output:
----------------------------- testing the new MasmBasic Switch_ macro -
-5      ---
-4      ---
-3      ---
-2      Case -2
-1      ---
0       Case NULL
1       ---
2       ---
3       ---
4       ---
5       ---
6       ---
7       ---
8       ---
9       ---
10      Case 10
11      ---
12      ---
13      ---
14      Case 14 .. 16
15      Case 14 .. 16
16      Case 14 .. 16
17      ---
18      Case 18
19      ---
20      ---
----------------------------- end of loop -----------------------------


Disassembly:
0121107F             ³.  68 50902101        push offset ra_lbl2        ; ASCII "----------------------------- testing the new MasmBasic Switch_ macro -----------------------------"
01211084             ³.  6A 01              push 1
01211086             ³.  6A 02              push 2
01211088             ³.  E8 B9140000        call MbPrint
C0004                ³>  51                 push ecx
0121108E             ³.  6A 05              push 5
01211090             ³.  6A 00              push 0
01211092             ³.  6A 01              push 1
01211094             ³.  E8 8D2D0000        call Float2Asc
01211099             ³.  6A 7F              push 7F
0121109B             ³.  6A 02              push 2
0121109D             ³.  6A 02              push 2
0121109F             ³.  E8 A2140000        call MbPrint
012110A4             ³.  3B0D 2C112101      cmp ecx, [121112C]
012110AA             ³.  8B15 28112101      mov edx, [MbSw1L]
012110B0             ³. 7F 64              jg short MbSw1D
012110B2             ³.  03D1               add edx, ecx
012110B4             ³. 7C 60              jl short MbSw1D
012110B6             ³.  FF2495 30112101    jmp near [edx*4+1211130]
MbSw10               ³>  68 B4902101        push offset ra_lbl3        ; ASCII "Case -2"
012110C2             ³.  6A 01              push 1
012110C4             ³.  6A 02              push 2
012110C6             ³.  E8 7B140000        call MbPrint
012110CB             À. E9 B4000000        jmp MbSw1
MbSw11               Ú>  68 BC902101        push offset ra_lbl4        ; ASCII "Case NULL"
012110D5             ³.  6A 01              push 1
012110D7             ³.  6A 02              push 2
012110D9             ³.  E8 68140000        call MbPrint
012110DE             À. E9 A1000000        jmp MbSw1
MbSw12               Ú>  68 C8902101        push offset ra_lbl5        ; ASCII "Case 10"
012110E8             ³.  6A 01              push 1
012110EA             ³.  6A 02              push 2
012110EC             ³.  E8 55140000        call MbPrint
012110F1             À. E9 8E000000        jmp MbSw1
MbSw13               Ú>  68 D0902101        push offset ra_lbl6        ; ASCII "Case 18"
012110FB             ³.  6A 01              push 1
012110FD             ³.  6A 02              push 2
012110FF             ³.  E8 42140000        call MbPrint
01211104             À. EB 7E              jmp short MbSw1
MbSw14               Ú>  68 D8902101        push offset ra_lbl7        ; ASCII "Case 14 .. 16"
0121110B             ³.  6A 01              push 1
0121110D             ³.  6A 02              push 2
0121110F             ³.  E8 32140000        call MbPrint
01211114             ³. EB 6E              jmp short MbSw1
MbSw1D               ³>  68 E8902101        push offset ra_lbl8        ; ASCII "---"
0121111B             ³.  6A 01              push 1
0121111D             ³.  6A 02              push 2
0121111F             ³.  E8 22140000        call MbPrint
01211124             ³. EB 5E              jmp short MbSw1
01211126             ³   8BFF               mov edi, edi
MbSw1L               ³.  02000000           dd 00000002
0121112C             ³.  12000000           dd 00000012
01211130             ³.  BD102101           dd MbSw10
01211134             ³.  16112101           dd MbSw1D
01211138             ³.  D0102101           dd MbSw11
0121113C             ³.  16112101           dd MbSw1D
01211140             ³.  16112101           dd MbSw1D
01211144             ³.  16112101           dd MbSw1D
01211148             ³.  16112101           dd MbSw1D
0121114C             ³.  16112101           dd MbSw1D
01211150             ³.  16112101           dd MbSw1D
01211154             ³.  16112101           dd MbSw1D
01211158             ³.  16112101           dd MbSw1D
0121115C             ³.  16112101           dd MbSw1D
01211160             ³.  E3102101           dd MbSw12
01211164             ³.  16112101           dd MbSw1D
01211168             ³.  16112101           dd MbSw1D
0121116C             ³.  16112101           dd MbSw1D
01211170             ³.  06112101           dd MbSw14
01211174             ³.  06112101           dd MbSw14
01211178             ³.  06112101           dd MbSw14
0121117C             ³.  16112101           dd MbSw1D
01211180             ³.  F6102101           dd MbSw13
MbSw1                ³>  41                 inc ecx
01211185             ³.  83F9 14            cmp ecx, 14
01211188             ³. 0F8E FFFEFFFF      jle C0004
0121118E             ³.  68 EC902101        push offset ra_lbl9        ; ASCII "----------------------------- end of loop -----------------------------"
01211193             ³.  6A 01              push 1
01211195             ³.  6A 02              push 2
01211197             ³.  E8 AA130000        call MbPrint

habran

It looks impressive :t
Can you run it without .repeat, with only one case, for example Case_ 18, and than send the disassembly and oututput
Cod-Father