Having seen a bit to much php lately folowed by apache server settings etc ... I am a little off the pace with a macro design that would be useful if it could be got going.
Here is the play version so far, what I am trying to get is a "switch" type control block for matching words in a simple and convenient way. Here is the first attempt to test out the concept.
The macros.
switch$ MACRO lpstring
LOCAL lbl1
retadr equ <lbl1>
tstval equ <lpstring>
ENDM
case$ MACRO quoted_text
.if FUNC(szCmp,tstval,chr$(quoted_text)) != 0
ENDM
break$ MACRO
jmp retadr
.endif
ENDM
endsw$ MACRO
retadr:
ENDM
Next is the crude test code.
switch$ lpstr
case$ "word1"
nop
nop
break$
case$ "word2"
nop
nop
break$
case$ "word3"
nop
nop
break$
endsw$
It is rough at the moment, cannot be nested or duplicated but the idea sems to be sound if it can be got to work properly.
hutch--,
I used to write MACROs like that, duplicating IF-THEN-ELSE, REPEAT, and other flow control code sequences so dear to the hearts of programmers who like structured code. Maybe someone else knows a better way, but I found it necessary to keep a global variable that is increased by one each time the MACRO is called. By tacking this variable onto the labels needed within the MACRO, a unique label is formed. Otherwise you will generate the same label for each MACRO CALL, causing a duplicate label warning. Anyway, the MACROs start to become a work of art from all the housekeeping necessary, and probably only the author wants to/can support them. Sometimes the flow control statements are such that a single jump to a group end would avoid a series of cascaded jumps. The C compiler seems to be able to figure that out, but MASM MACROS do not have enough features to determine the end of a cascaded jump. At least I never could do it. Those structures still do not have the flexibility straight coding does, and I have tended not to use them. They are a "poor man's version of C". I prefer to minimize spagetti code, indent and annotate to show the "structured" flow, but that is me. Others have to find their own best system. Ratch
I think I finally got this worked out, but it needs some more testing.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm
printv MACRO varname:VARARG
FOR arg, <varname>
invoke StdOut, reparg(arg)
ENDM
ENDM
NL equ SADD(13,10)
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Macros for storing and retrieving text macros, based on
; the $casstk code from Greg Falen's Switch/Case macros.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$text_stack$ equ <#>
pushtext MACRO name:req
$text_stack$ CATSTR <name>, <#>, $text_stack$
ENDM
poptext MACRO name:req
LOCAL pos
pos INSTR $text_stack$, <#>
name SUBSTR $text_stack$, 1, pos-1
$text_stack$ SUBSTR $text_stack$, pos+1
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Macros to implement a string-comparison specific
; Switch/Case construct. Multiple instances and
; nesting supported.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$tstval$ equ <>
$end_sw$ equ <>
_sw_cnt_ = 0
switch$ MACRO lpstring:REQ
; Preserve values for previous Switch/Case.
pushtext $tstval$
pushtext $end_sw$
$tstval$ equ <lpstring>
$tstval$ CATSTR <ADDR >, $tstval$
; Generate unique label and preserve it.
_sw_cnt_ = _sw_cnt_ + 1
$end_sw$ CATSTR <end_sw>, %_sw_cnt_
pushtext $end_sw$
ENDM
case$ MACRO quoted_text:REQ
.IF FUNC(szCmp, $tstval$, chr$(quoted_text)) != 0
ENDM
break$ MACRO
; Because there could have been an intervening
; Switch/Case, we need to recover the correct
; label for this Switch/Case.
poptext $end_sw$
pushtext $end_sw$
jmp $end_sw$
.ENDIF
ENDM
endsw$ MACRO
; Remove label from stack.
poptext $end_sw$
$end_sw$:
; Recover values for previous Switch/Case.
poptext $end_sw$
poptext $tstval$
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
buffer db 30 dup(0)
str1 db "str1",0
str2 db "str2",0
str3 db "str3",0
str4 db "str4",0
str5 db "str5",0
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
switch$ str1
case$ "str1"
printv chr$("switch$ str1, str1"), NL
switch$ str2
case$ "str1"
printv chr$("switch$ str2, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str2, str2"), NL
switch$ str3
case$ "str1"
printv chr$("switch$ str3, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str3, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str3, str3"), NL
switch$ str4
case$ "str1"
printv chr$("switch$ str4, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str4, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str4, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str4, str4"), NL
break$
endsw$ ; str4
break$
case$ "str4"
printv chr$("switch$ str3, str4"), NL
break$
endsw$ ;str3
break$
case$ "str3"
printv chr$("switch$ str2, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str2, str4"), NL
break$
endsw$ ; str2
break$
case$ "str2"
printv chr$("switch$ str1, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str1, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str1, str4"), NL
break$
endsw$ ; str1
switch$ str5
case$ "str1"
printv chr$("switch$ str5, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str5, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str5, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str5, str4"), NL
break$
case$ "str5"
printv chr$("switch$ str5, str5"), NL
break$
endsw$ ;str5
printv NL, chr$("Press enter to exit..."), NL
invoke StdIn, addr buffer, 1
exit
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
I was hoping to use .IF/.ELSEIF/.ENDIF and eliminate the break statement, but I could find no way to make it work. Basically, the problem was that szCmp must be called for each case, and the generated jump for the else condition always bypassed the call. It would seem that MASM is determining the proper location for the jump destination by looking for the condition test code.
Michael,
This looks good, I will try and get a bit more brain running at the moment to have a play with it. Have you given it a decent hammering as far as testing ?
LATER: I removed the line,
;; $tstval$ CATSTR <ADDR >, $tstval$ ; <<<<<<<<<<<< disabled
From the switch$ macro so it would take a raw address and it works perfectly. About all we need now is an "else$" for default processing and the macro is up and running. I put it in a test piece but the forum upgrade will not allow file attachments at the moment so I cannot post it. This is the test procedure.
tstsw proc lpstr:DWORD
switch$ lpstr
case$ "word1"
fn MessageBox,hWnd,"word1","Title",MB_OK
break$
case$ "word2"
fn MessageBox,hWnd,"word2","Title",MB_OK
break$
case$ "word3"
fn MessageBox,hWnd,"word3","Title",MB_OK
break$
endsw$
ret
tstsw endp
Easy :P
else$ MACRO
;; do nothing
ENDM
Add the else as follows,
tstsw proc lpstr:DWORD
switch$ lpstr
case$ "word1"
fn MessageBox,hWnd,"word1","Title",MB_OK
break$
case$ "word2"
fn MessageBox,hWnd,"word2","Title",MB_OK
break$
case$ "word3"
fn MessageBox,hWnd,"word3","Title",MB_OK
break$
else$
fn MessageBox,hWnd,"Default","Title",MB_OK
endsw$
ret
tstsw endp
This could probably do with some tidying up but it seems to work OK.
I added the "$tstval$ CATSTR <ADDR >, $tstval$" as a quick adaptation to make the macro work in my test program, and then amid all the changes I forgot to remove it and rework the program code.
My function tests consisted mostly of variations of the test code I posted. These tests were sufficient to spot problems in all of the various arrangements I tried before I arrived at this one. In the course of diagnosing the problems I used the .ERR directive, first-pass listings, and disassembled listings, but once I had a version that preformed correctly in the tests I did not use these methods to verify that it was in fact working as I intended.
There are probably multiple ways to misuse these macros, and beyond the required parameters I made no attempt to detect programmer errors. For the pushtext and poptext macros I initially included code to check for a stack empty condition, but I eventually decided that any programmer who might need such a feature would have larger problems than just applying these macros. I think MASM will detect most of the likely sequencing errors (case$ w/o switch$, endsw$ w/o switch$, case$ w/o break$, etc).
You could achieve the same effect as else$ by just placing the else condition code between the last break$ and the endsw$, but making it obvious with an empty else$ macro is a better design.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm
printv MACRO varname:VARARG
FOR arg, <varname>
invoke StdOut, reparg(arg)
ENDM
ENDM
NL equ SADD(13,10)
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Macros for storing and retrieving text macros, based on
; the $casstk code from Greg Falen's Switch/Case macros.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$text_stack$ equ <#>
pushtext MACRO name:req
$text_stack$ CATSTR <name>, <#>, $text_stack$
ENDM
poptext MACRO name:req
LOCAL pos
pos INSTR $text_stack$, <#>
name SUBSTR $text_stack$, 1, pos-1
$text_stack$ SUBSTR $text_stack$, pos+1
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Macros to implement a string-comparison specific
; Switch/Case construct. Multiple instances and
; nesting supported.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$tstval$ equ <>
$end_sw$ equ <>
_sw_cnt_ = 0
switch$ MACRO lpstring:REQ
;; Preserve values for previous Switch/Case.
pushtext $tstval$
pushtext $end_sw$
$tstval$ equ <lpstring>
;; Generate unique label and preserve it.
_sw_cnt_ = _sw_cnt_ + 1
$end_sw$ CATSTR <end_sw>, %_sw_cnt_
pushtext $end_sw$
ENDM
case$ MACRO quoted_text:REQ
.IF FUNC(szCmp, $tstval$, chr$(quoted_text)) != 0
ENDM
break$ MACRO
;; Because there could have been an intervening
;; Switch/Case we need to recover the correct
;; label for this Switch/Case.
poptext $end_sw$
pushtext $end_sw$
jmp $end_sw$
.ENDIF
ENDM
else$ MACRO
;; Do nothing.
ENDM
endsw$ MACRO
;; Remove label from stack.
poptext $end_sw$
$end_sw$:
;; Recover values for previous Switch/Case.
poptext $end_sw$
poptext $tstval$
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
buffer db 30 dup(0)
str1 db "str1",0
str2 db "str2",0
str3 db "str3",0
str4 db "str4",0
str5 db "str5",0
str6 db "xxxx",0
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
switch$ ADDR str1
case$ "str1"
printv chr$("switch$ str1, str1"), NL
switch$ ADDR str2
case$ "str1"
printv chr$("switch$ str2, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str2, str2"), NL
switch$ ADDR str3
case$ "str1"
printv chr$("switch$ str3, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str3, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str3, str3"), NL
switch$ ADDR str4
case$ "str1"
printv chr$("switch$ str4, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str4, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str4, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str4, str4"), NL
break$
endsw$ ; str4
break$
case$ "str4"
printv chr$("switch$ str3, str4"), NL
break$
endsw$ ;str3
break$
case$ "str3"
printv chr$("switch$ str2, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str2, str4"), NL
break$
endsw$ ; str2
break$
case$ "str2"
printv chr$("switch$ str1, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str1, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str1, str4"), NL
break$
endsw$ ; str1
switch$ ADDR str5
case$ "str1"
printv chr$("switch$ str5, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str5, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str5, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str5, str4"), NL
break$
case$ "str5"
printv chr$("switch$ str5, str5"), NL
break$
endsw$ ;str5
switch$ ADDR str6
case$ "str1"
printv chr$("switch$ str6, str1"), NL
break$
case$ "str2"
printv chr$("switch$ str6, str2"), NL
break$
case$ "str3"
printv chr$("switch$ str6, str3"), NL
break$
case$ "str4"
printv chr$("switch$ str6, str4"), NL
break$
case$ "str5"
printv chr$("switch$ str6, str5"), NL
break$
else$
printv chr$("switch$ str6, else"), NL
endsw$ ;str6
printv NL, chr$("Press enter to exit..."), NL
invoke StdIn, addr buffer, 1
exit
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; This is a test of the pushtext and poptext macros.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$text_stack$ equ <#>
pushtext MACRO name:req
$text_stack$ CATSTR <name>, <#>, $text_stack$
ENDM
poptext MACRO name:req
LOCAL pos
pos INSTR $text_stack$, <#>
name SUBSTR $text_stack$, 1, pos-1
$text_stack$ SUBSTR $text_stack$, pos+1
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
text1 equ <aa>
text2 equ <bb>
text3 equ <cc>
text4 equ <>
.err $text_stack$
pushtext text1
.err $text_stack$
pushtext text2
.err $text_stack$
pushtext text3
.err $text_stack$
poptext text4
.err text4
.err $text_stack$
poptext text4
.err text4
.err $text_stack$
poptext text4
.err text4
.err $text_stack$
; Stack is empty
; "error A2091: index value past end of string"
poptext junk
pushtext <byob>
.err $text_stack$
poptext junk
.err $text_stack$
.err junk
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
Michael,
I am really pleased with the result, its clear to read and the underlying code generation is good as well. Now I wonder if its worth the effort to remove the break$ macro by changing the case$ macro to place an ".endif" before every ".if" except for the first one. It would have the logic as follows.
; ----------------
.if condition ; 1st instance
; ----------------
; ----------------
.endif
.if condition ; following instance(s)
; ----------------
; ----------------
.endif ; last instance
; ----------------
Let me know what you think here as the stuff you have done so far is great.
OK, I think I have a good implementation without break$. I looked into adding code that would generate meaningful error messages for the most likely syntax errors, but I decided that it would not be worth the substantial effort that would be required. The functionality, macro expansions, and generated code all look OK to me, but sometimes I can be blind to very obvious errors.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
.nolist
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm
printv MACRO varname:VARARG
FOR arg, <varname>
invoke StdOut, reparg(arg)
ENDM
ENDM
NL equ SADD(13,10)
.listall
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Macros for storing and retrieving text macros, based on
; the $casstk code from Greg Falen's Switch/Case macros.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$text_stack$ equ <#>
pushtext MACRO name:req
$text_stack$ CATSTR <name>, <#>, $text_stack$
ENDM
poptext MACRO name:req
LOCAL pos
pos INSTR $text_stack$, <#>
name SUBSTR $text_stack$, 1, pos-1
$text_stack$ SUBSTR $text_stack$, pos+1
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Macros to implement a string-comparison specific
; Switch/Case construct. Multiple instances and
; nesting supported.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$test_val$ equ <>
$end_sw$ equ <>
$sw_state$ equ <>
_sw_cnt_ = 0
switch$ MACRO lpstring:REQ
;; Preserve globals for previous Switch/Case.
pushtext $test_val$
pushtext $sw_state$
pushtext $end_sw$
;; Copy string address for this Select/Case
;; to global so case$ can access it.
$test_val$ equ <lpstring>
;; Set state global to starting value.
$sw_state$ equ <>
;; Generate a unique exit label for this
;; Select/Case and preserve it.
_sw_cnt_ = _sw_cnt_ + 1
$end_sw$ CATSTR <end_sw>, %_sw_cnt_
pushtext $end_sw$
ENDM
case$ MACRO quoted_text:REQ
;; The case statements will be any statements
;; between the case$ and the following case$,
;; else$, or endsw$.
;;
;; If this is a following case$, emit a jump
;; to the exit label for this Select/Case and
;; terminate the .IF block.
IFIDN $sw_state$, <if>
;; Because there could have been an intervening
;; Switch/Case we need to recover the correct
;; exit label for this Switch/Case.
poptext $end_sw$
pushtext $end_sw$
jmp $end_sw$
.ENDIF
ENDIF
; Start a new .IF block and update the state global.
.IF FUNC(szCmp, $test_val$, chr$(quoted_text)) != 0
$sw_state$ equ <if>
ENDM
else$ MACRO
;; If following a case$, terminate the .IF block.
;; The state global must be updated to stop the
;; endsw$ from terminatinmg the .IF block.
IFIDN $sw_state$, <if>
.ENDIF
$sw_state$ equ <>
ENDIF
ENDM
endsw$ MACRO
; If following a case$, terminate the .IF block.
IFIDN $sw_state$, <if>
.ENDIF
ENDIF
;; Remove the exit label from the stack.
poptext $end_sw$
$end_sw$:
;; Recover gobals for previous Switch/Case.
poptext $end_sw$
poptext $sw_state$
poptext $test_val$
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
buffer db 30 dup(0)
str1 db "str1",0
str2 db "str2",0
str3 db "str3",0
str4 db "str4",0
str5 db "str5",0
str6 db "xxxx",0
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
switch$ ADDR str1
case$ "str1"
printv chr$("switch$ str1, str1"), NL
switch$ ADDR str2
case$ "str1"
printv chr$("switch$ str2, str1"), NL
case$ "str2"
printv chr$("switch$ str2, str2"), NL
switch$ ADDR str3
case$ "str1"
printv chr$("switch$ str3, str1"), NL
case$ "str2"
printv chr$("switch$ str3, str2"), NL
case$ "str3"
printv chr$("switch$ str3, str3"), NL
switch$ ADDR str4
case$ "str1"
printv chr$("switch$ str4, str1"), NL
case$ "str2"
printv chr$("switch$ str4, str2"), NL
case$ "str3"
printv chr$("switch$ str4, str3"), NL
case$ "str4"
printv chr$("switch$ str4, str4"), NL
endsw$ ; str4
case$ "str4"
printv chr$("switch$ str3, str4"), NL
endsw$ ;str3
case$ "str3"
printv chr$("switch$ str2, str3"), NL
case$ "str4"
printv chr$("switch$ str2, str4"), NL
endsw$ ; str2
case$ "str2"
printv chr$("switch$ str1, str2"), NL
case$ "str3"
printv chr$("switch$ str1, str3"), NL
case$ "str4"
printv chr$("switch$ str1, str4"), NL
endsw$ ; str1
switch$ ADDR str5
case$ "str1"
printv chr$("switch$ str5, str1"), NL
case$ "str2"
printv chr$("switch$ str5, str2"), NL
case$ "str3"
printv chr$("switch$ str5, str3"), NL
case$ "str4"
printv chr$("switch$ str5, str4"), NL
case$ "str5"
printv chr$("switch$ str5, str5"), NL
endsw$ ;str5
switch$ ADDR str6
case$ "str1"
printv chr$("switch$ str6, str1"), NL
case$ "str2"
printv chr$("switch$ str6, str2"), NL
case$ "str3"
printv chr$("switch$ str6, str3"), NL
case$ "str4"
printv chr$("switch$ str6, str4"), NL
case$ "str5"
printv chr$("switch$ str6, str5"), NL
else$
printv chr$("switch$ str6, else"), NL
endsw$ ;str6
printv NL, chr$("Press enter to exit..."), NL
invoke StdIn, addr buffer, 1
exit
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
:bg
I think this one has found the Holy Grail. All I did was to copy and paste the code for the jump from the "case$" macro into the "else$" macro and its working perfectly so far. Code generation is correct and the output looks very efficient.
Now that I have some new smilies to give cheek with, this new set of macros is :U :dance::clap: TRULY GREAT :clap: :dance: :U
Let me know if you are happy with the small mod and I think its ready to rock 'n roll.
[attachment deleted by admin]
Here is the code generation for the above macros.
fn_004015C1:
push ebp
mov ebp, esp
push 403096h
push DWORD PTR [ebp+8]
call fn_00401620 ; case$ 1
or eax, eax
jz lbl0
nop
nop
jmp lbl4
lbl0:
push 40309Ch
push DWORD PTR [ebp+8]
call fn_00401620 ; case$ 2
or eax, eax
jz lbl1
nop
nop
jmp lbl4
lbl1:
push 4030A2h
push DWORD PTR [ebp+8]
call fn_00401620 ; case$ 3
or eax, eax
jz lbl2
nop
nop
jmp lbl4
lbl2:
push 4030A8h
push DWORD PTR [ebp+8]
call fn_00401620 ; case$ 4
or eax, eax
jz lbl3
nop
nop
jmp lbl4
lbl3: ; else$
nop
nop
lbl4:
leave
ret 4
As I stated, sometimes I can be blind to very obvious errors. I should have tested the else$ macro under conditions where the preceding case was true. I'll blame it on the slight hangover I had this morning :green
For consistency I moved the jump into the IFIDN block, and doing so had the (probably useless) side effect of allowing a switch/else/endsw block to function correctly.
else$ MACRO
;; If following a case$, emit a jump to the exit
;; label for this Select/Case and terminate the .IF
;; block. The jump is necessary, whenever the case
;; for the .IF block being terminated is true, to
;; bypass the else statements that follow.
IFIDN $sw_state$, <if>
poptext $end_sw$
pushtext $end_sw$
jmp $end_sw$
.ENDIF
;; The state global must be updated to stop the
;; endsw$ from terminatinmg the .IF block.
$sw_state$ equ <>
ENDIF
ENDM
I wish I still had the luxury but my days of hangovers are over. :P
I have encorporated the change, changed some of the formatting so the macros are a bit easier to read, put the credit at the top and a syntax example on how it is to be used. Code is as follows. Give it the nod and it goes into "macros.inc" so that everyone else can use it.
comment * ------------------------------------------------
The following macro system for a string comparison
switch block was designed by Michael Webster.
SYNTAX:
switch$ string_address ; adress of zero terminated string
case$ "quoted text" ; first string to test against
; your code here
case$ "another quoted text" ; optional additional quoted text
; your code here
else$ ; optional default processing
; default code here
endsw$
------------------------------------------------ *
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Macros for storing and retrieving text macros, based on
; the $casstk code from Greg Falen's Switch/Case macros.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$text_stack$ equ <#>
pushtext MACRO name:req
$text_stack$ CATSTR <name>, <#>, $text_stack$
ENDM
poptext MACRO name:req
LOCAL pos
pos INSTR $text_stack$, <#>
name SUBSTR $text_stack$, 1, pos-1
$text_stack$ SUBSTR $text_stack$, pos+1
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Macros to implement a string-comparison specific
; Switch/Case construct. Multiple instances and
; nesting supported.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
$test_val$ equ <>
$end_sw$ equ <>
$sw_state$ equ <>
_sw_cnt_ = 0
switch$ MACRO lpstring:REQ
pushtext $test_val$ ;; Preserve globals for previous Switch/Case.
pushtext $sw_state$
pushtext $end_sw$
$test_val$ equ <lpstring> ;; Copy string address for this Select/Case
;; to global so case$ can access it.
$sw_state$ equ <> ;; Set state global to starting value.
_sw_cnt_ = _sw_cnt_ + 1 ;; Generate a unique exit label for this
$end_sw$ CATSTR <end_sw>, %_sw_cnt_ ;; Select/Case and preserve it.
pushtext $end_sw$
ENDM
case$ MACRO quoted_text:REQ
;; The case statements will be any statements between the case$ and the following case$,
;; else$, or endsw$.
;;
;; If this is a following case$, emit a jump to the exit label for this Select/Case and
;; terminate the .IF block.
;; --------------------------------
IFIDN $sw_state$, <if>
poptext $end_sw$ ;; Because there could have been an intervening
pushtext $end_sw$ ;; Switch/Case we need to recover the correct
jmp $end_sw$ ;; exit label for this Switch/Case.
.ENDIF
ENDIF
;; --------------------------------
;; Start a new .IF block and update the state global.
.IF FUNC(szCmp, $test_val$, chr$(quoted_text)) != 0
$sw_state$ equ <if>
ENDM
else$ MACRO
IFIDN $sw_state$, <if> ;; If following a case$, emit a jump to the exit
poptext $end_sw$ ;; label for this Select/Case and terminate the .IF
pushtext $end_sw$ ;; block. The jump is necessary, whenever the case
jmp $end_sw$ ;; for the .IF block being terminated is true, to
.ENDIF ;; bypass the else statements that follow.
$sw_state$ equ <> ;; The state global must be updated to stop the
ENDIF ;; endsw$ from terminatinmg the .IF block.
ENDM
endsw$ MACRO
IFIDN $sw_state$, <if> ;; If following a case$, terminate the .IF block.
.ENDIF
ENDIF
poptext $end_sw$ ;; Remove the exit label from the stack.
$end_sw$:
poptext $end_sw$ ;; Recover gobals for previous Switch/Case.
poptext $sw_state$
poptext $test_val$
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
It looks OK to me, Hutch, except the syntax example doesn't make it clear that you can use an unlimited number of case clauses.
I have now tested case statements following an imbedded switch$, a switch$ embedded in an else clause, and an .IF/ENDIF block in a case clause, and everything worked OK.
Hi Hutch,
Be sure to download the "UCR Standard Library v2.0" code from Webster.
Though there is no "case/switch" statement there, your macro development is proceeding along a similar path that my experiments took in the middle 1990's. Though I eventually gave up (and wrote HLA to get what I really wanted), there are still a lot of good ideas in that code. Too bad it broke when MASM v6.11d came along (it was written for v6.11a), but many of the ideas are still valid. I mention this, because I notice that the techniques you're using to "push and pop labels" to achieve nesting is virtually identical to what I did 10 years ago.
Cheers,
Randy Hyde
Randy,
Thanks for the info, its always useful to tread a path that someone else has done before as it saves some of the hard work yet I must say I am very pleased with the latest macro from Michael, its testing up very well, I like the code output quality and it makes a particular common task a lot easier and cleaner to code.
Randy,
I have just climbed all over your site and I cannot find the library you mentioned, any chance of the link ?