Assembly code snippets

Back to the snippets overview

Details

TitleSwitch / Case
AuthorbitRAKE
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