News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Switch / Case?

Started by Mark Jones, July 27, 2007, 01:52:02 AM

Previous topic - Next topic

Mark Jones

Hi Jorgon, thought I'd give GoAsm a try and so far it is nice. Great job. :wink

I'm curious, I see the include syntax was extended to support C-style includes, but there is no switch/case statement. I ask because I've been tinkering with some of Edgar's code and DialogProcs seem like they could benefit greatly from switch/case. The existing code is looking something like:


DlgProc FRAME hwnd,uMsg,wParam,lParam
mov eax,[uMsg]
cmp eax,WM_INITDIALOG
jne >.WMCOMMAND
jmp >.EXIT

.WMCOMMAND
cmp eax,WM_COMMAND
jne >.WMCLOSE
cmp D[wParam],IDC_BTN1 ; button 1 pressed?
jne >.WMCLOSE
; ok button 1 was pressed, do stuff...
cmp D[wParam],IDC_BTN2 ; button 2 pressed?
jne >.WMCLOSE
; ok button 2 was pressed, do stuff...
cmp D[wParam],IDC_BTN3 ; button 3 pressed?
jne >.WMCLOSE
; ok button 3 was pressed, do stuff...
cmp D[wParam],IDC_BTN4 ; button 4 pressed?
jne >.WMCLOSE
; ok button 4 was pressed, do stuff...
jmp >.EXIT

.WMCLOSE
cmp eax,WM_CLOSE
jne >.DEFPROC
invoke EndDialog,[hwnd],0

.DEFPROC
mov eax,FALSE
ret

.EXIT
mov eax,TRUE
ret
ENDF


May I ask if there is a reason switch/case is not implemented? Thanks for reading. :-)
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

1rDirEctoALgran0

With GoAsm you don't have any high-level instruction like :
- switch/case
- if/else/then/endif
- while/wend
...
And, for my taste, I don't want them... :toothy
I want to see what I write in a debugger and I like the Jcc family. :bg
The code generated with high-level instructions is not perfect (JMP on a JMP)
I would prefer that Jeremy optimize the forward jumps.

I explain :
when you write :
JMP >here
NOP
here: NOP

the instruction is 5 byte length.
So I reverse all my programs in the goal to have mainly jump to back.
But even I that case the 16-bit Jcc is not implemented (for x64 reason), grrrrr... :(

Patrick

Mark Jones

I have not looked into it much yet, but wonder if switch/case could be implemented as a macro. I too like the idea of being able to see the exact code being entered. But in the case of DialogProcs, they tend to be bulky as is and I really like to pare them down as much as possible. Especially when writing an application with many controls. I'll check into GoAsm's macro capability to see if this can be emulated.
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

Mark Jones

This does not appear to be possible in one pass, GoAsm returns the error "Macro contained a forward jump to nowhere" even when labels were defined explicitly in the macros.
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

Vortex

Mark,

Could you post here your code? I would be glad to see your work.

MichaelW

I would like to see it to. I made a small effort at a switch/case macro, and short of a pre-preprocessor I could see no reasonable way to do it.
eschew obfuscation

Mark Jones

Hi friends. Well I tried several complex things, but kept simplifying it until this [bare and incomplete version] was the result:


switchval   DD   ?

switch(%a)  macro
                push [%a]
                pop [switchval]
            endm

case1(%b)   macro a:
                cmp D[switchval],%b
                jnz b:
            endm

case2(%b)   macro b:
                cmp D[switchval],%b
                jnz c:
            endm

case3(%b)   macro c:
                cmp D[switchval],%b
                jnz d:
            endm


default     macro d:
                ;nop
            endm

endsw       macro e:
                ;nop
            endm


TestSW FRAME hwnd,uMsg,wParam,lParam
    switch(uMsg)
    case1(WM_INITDIALOG)
        nop                 ; do init here
    case2(WM_COMMAND)
        nop                 ; command code here
        nop
    case3(WM_CLOSE)
        invoke EndDialog,[hWnd],0
    default
        xor eax,eax
        ret
    endsw
        mov eax,1
        ret
ENDF


At compilation of the case1 macro, GoAsm responds with: "Jumped-to label was not found earlier in source script:- jnz b:"

I guess macros are expanded during compilation, not beforehand. I also thought about coding a pre-processor to handle the switch/case code, but this would get messy when using it with an IDE like RadASM, which will notice the changed .asm file and ask to load the new version.

Hmm, what about post-processing? Simply write blocks of filler data in place of the actual switch/case code, then patch the compiled executable with the missing code. Could work, of course the question then becomes, is all this effort worth it?
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

jorgon

Quote from: 1rDirEctoALgran0 on July 27, 2007, 01:26:01 PM
The code generated with high-level instructions is not perfect (JMP on a JMP)
I would prefer that Jeremy optimize the forward jumps.

I explain :
when you write :
JMP >here
NOP
here: NOP

the instruction is 5 byte length.
So I reverse all my programs in the goal to have mainly jump to back.

Patrick, if you use
JMP >.here
NOP
.here: NOP

GoAsm knows its a local jump and codes accordingly.  Otherwise GoAsm thinks "here" could be anywhere even in another object file.
Another way to ensure a short forward jump is to use one letter followed by a number for example
JMP >L34
NOP
L34: NOP

Or even just the colon, eg.
JMP >
NOP
: NOP

Author of the "Go" tools (GoAsm, GoLink, GoRC, GoBug)

jorgon

Dealing with the main point of the thread, the basic reason why switch/case or its masm variant is not implemented in GoAsm is that there are good and obvious mnemonics which do the same job (CMP and the conditional JMP).  This is why GoAsm provides plenty of extra support for local jumps of various types - so that the CMP + conditional JMP system can be used to full advantage. 

I appreciate that there are times, for example in a large window or dialog procedure, when the CMP + conditional JMP system can get unwieldly, but there are various forms of the tabular window procedure which can be used for such procedures. 

I've found this version to be the most adaptable (this is from the GoAsm HelloWorld examples not using FRAME..ENDF):-


;
DATA
;
;******************** Window message table
;          (in a real program this would deal with many more messages)
MESSAGES DD (ENDOF_MESSAGES-$-4)/8      ;=number to be done
         DD  1h,CREATE,2h,DESTROY,0Fh,PAINT
ENDOF_MESSAGES:         ;label used to work out how many messages
;******************************************
;
CODE
;
;********** this is a general window procedure which in an ordinary
;********** program deals with all messages sent to the window
GENERAL_WNDPROC:        ;eax can be used to convey information to the call
PUSH EBP                ;use ebp to avoid using eax which may hold information
MOV EBP,[ESP+10h]       ;uMsg
MOV ECX,[EDX]           ;get number of messages to do
ADD EDX,4               ;jump over size dword
L2:
DEC ECX
JS >L3
CMP [EDX+ECX*8],EBP     ;see if its the correct message
JNZ L2                  ;no
MOV EBP,ESP
PUSH ESP,EBX,EDI,ESI    ;save registers as required by Windows
ADD EBP,4               ;allow for the extra call to here
;now [EBP+8]=hwnd, [EBP+0Ch]=uMsg, [EBP+10h]=wParam, [EBP+14h]=lParam,
CALL [EDX+ECX*8+4]      ;call the correct procedure for the message
POP ESI,EDI,EBX,ESP
JNC >L4                 ;nc=return value in eax - don't call DefWindowProc
L3:
PUSH [ESP+18h],[ESP+18h],[ESP+18h],[ESP+18h]     ;allowing for change of ESP
CALL DefWindowProcA
L4:
POP EBP
RET
;
;******************* This is the actual window procedure
WndProcTable:
MOV EDX,ADDR MESSAGES   ;give edx the list of messages to deal with
CALL GENERAL_WNDPROC    ;call the generic message handler
RET 10h                 ;restore the stack as required by caller
;
CREATE:
RET
;
DESTROY:
RET
;
PAINT:
RET


Using FRAME..ENDF and LOCAL you can refer to the parameters and local data by name, so you don't have to rely on EBP.  Here is an example of this (from GoAsm's Helloworld3):-


;------------------------------------------------------------
;
CONST SECTION
;
;------------------------------------------------------------
;******************** Window message table
;(In a real program this would deal with many more messages)
;
MESSAGES DD WM_CREATE,    CREATE       ;the message then the code address
         DD WM_DESTROY,  DESTROY
         DD WM_PAINT,      PAINT
;------------------------------------------------------------
;
CODE SECTION
;
;------------------------------------------------------------
;******************* This is the actual window procedure
WndProc:
FRAME hwnd,uMsg,wParam,lParam     ;establish stack frame and get parameters
;
MOV EAX,[uMsg]          ;get in eax message sent by Windows
MOV ECX,SIZEOF MESSAGES/8         ;get number of messages to look at
MOV EDX,ADDR MESSAGES
L2:
DEC ECX
JS >.notfound
CMP [EDX+ECX*8],EAX     ;see if its the correct message
JNZ L2                  ;no
CALL [EDX+ECX*8+4]      ;call the correct procedure for the message
JNC >.exit
.notfound
INVOKE DefWindowProcA,[hwnd],[uMsg],[wParam],[lParam]
.exit
RET
ENDF                    ;finish this stack frame
;


With the wide variety of tabular options of this type, is switch/case or its masm variant (or a macro version) necessary?
Author of the "Go" tools (GoAsm, GoLink, GoRC, GoBug)

donkey

Just to sound off here, I don't see the need for these type of high level constructs myself but can understand how they can help to make large conditional blocks more readable. However, like Jeremy suggests, I have long since become used to a tabular style and have no real problem reading my code, actually I find it easier than reading my older MASM code using the constructs.

Mark,

Glad to see that you found my code useful and are using it to introduce you to the power and capabilities of Jeremy's GoAsm, in my opinion the best of class for low level assemblers.

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable