News:

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

C++ Help

Started by RuiLoureiro, September 01, 2008, 11:57:57 AM

Previous topic - Next topic

RuiLoureiro

Hello !
         Some years ago, in DOS, i learned something about C++ (not deeply) to translate
         some procedures to asm and i was able to put it to work.
         Now, i am reading some documentation (in PlatformSDK) and i found this:

   switch( uMsg ) {
      case WM_PAINT:
      ...
      case WM_COMMAND:
      ...
      case WM_DESTROY:
      ...
      default:
         return( DefWindowProc( hWnd, msg, wParam, lParam ));
   }
   
        ( it is about processing messages ).

        and
       
    case WM_PAINT:
      hDC = BeginPaint( hWnd, &ps );
      TextOut( hDC, 10, 10, "Hello, World!", 13 );
      EndPaint( hWnd, &ps );
      break;

        and this:
       
      case WM_COMMAND:
         switch( wParam ) {
            case IDM_ABOUT:
            ...
            break;
         }

        Whats my doubt ? It is about break. What does it do between cases ?

        Can we put it there when we are thinking the processor needs "to take a coffee"
        or it is tired and needs to stop ?

        Seriously, what is the equivalent in asm ?

Rui       

Neil

break is used to stop the rest of the switch block from executing if the previous condition is true.

Mark Jones

Quote from: RuiLoureiro on September 01, 2008, 11:57:57 AM
Seriously, what is the equivalent in asm ?

Well, try it and see. :8) Seriously, that is the best answer, try the SWITCH and CASE macros in MASM32. Make a little test app, and load it into OllyDbg and press F7 and F8 repeatedly. :U
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

MichaelW

#3
Rui,

The asm equivalent to the C/C++ break statement would be an unconditional jump out of the switch block. If the jump were not taken, then the next case statement would be evaluated, for example:

switch(i)
{
  case 1:
    // case statements for i == 1
    break;
  case 2:
  case 3:
  case 4:
    // case statements for i == 2 || i == 3 || i == 4
    break;
  default:
    // case statements for i == any other value
}


The MASM32 switch/case macros (authored by Greg Falen) effectively include a break statement at the end of each case block (similar to a BASIC Select Case, in case you are familiar with that). The macros actually generate .IF/.ELSEIF/.ELSE/.ENDIF statements, so the jumps are generated by MASM, but the end effect is the same.

As an example of what the compiler produces, for this source:


#include <windows.h>
int main(void)
{
    int i;
    switch(i)  // line 5
    {
      case 1:
        break;  // line 8
      case 2:
      case 3:
      case 4:
        break;  // line 12
      default:
        i=1;    // this necessary for the source to compile line
    }
    return 0; // line 16
}


The Visual C++ Toolkit 2003 compiler produces this asm code:


; Line 5
mov eax, DWORD PTR _i$[ebp]
mov DWORD PTR tv64[ebp], eax
cmp DWORD PTR tv64[ebp], 1
je SHORT $L74000
cmp DWORD PTR tv64[ebp], 1
jle SHORT $L74002
cmp DWORD PTR tv64[ebp], 4
jle SHORT $L74001
jmp SHORT $L74002
$L74000:
; Line 8
jmp SHORT $L73997
$L74001:
; Line 12
jmp SHORT $L73997
$L74002:
; Line 14
mov DWORD PTR _i$[ebp], 1
$L73997:
; Line 16

eschew obfuscation

RuiLoureiro

Hi
        Thanks Neil, Mark & MichaelW

        Mark, Finally, i tried with OllyDbg. It was exactly what i expected except
        the use of the EAX register. The next case begins with a jump to the end
        and then it compares.

first experience with OllyDbg

; this the prog:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
      .586                      ; create 32 bit code
      .model flat, stdcall      ; 32 bit memory model
      option casemap :none      ; case sensitive

      include \masm32\macros\macros.asm
      include \masm32\include\kernel32.inc
      includelib \masm32\lib\kernel32.lib
.data
hInstance dd ?
.code
start:
      invoke GetModuleHandle, 0
      mov    hInstance, eax
               
      mov       ebx, 0AAAAAAAAh
      switch ebx
        case  0BBBBBBBBh
            mov     eax, 1
        case  0AAAAAAAAh
            mov     eax, 2
        default
            mov     eax, 0
        endsw
        ;
      mov     esi, 0FFFFFFF0h
      invoke ExitProcess, 0
end start
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««

                                     OLLYdbg
my note: »»» are my comments

; This is the result:

00401000 >/$ 6A 00          PUSH 0                                   ; /pModule = NULL
00401002  |. E8 3F000000    CALL <JMP.&kernel32.GetModuleHandleA>    ; \GetModuleHandleA
00401007  |. A3 00304000    MOV DWORD PTR DS:[403000],EAX

0040100C  |. BB AAAAAAAA    MOV EBX,AAAAAAAA
00401011  |. 8BC3           MOV EAX,EBX
                                                                                      »»» case BBBBBBBBh
00401013  |. 3D BBBBBBBB    CMP EAX,BBBBBBBB
00401018  |. 75 07          JNZ SHORT Test1.00401021
0040101A  |. B8 01000000    MOV EAX,1
0040101F  |. EB 13          JMP SHORT Test1.00401034                     »»» jump to endsw
                                                                                      »»» case AAAAAAAAh
00401021  |> 3D AAAAAAAA    CMP EAX,AAAAAAAA           
00401026  |. 75 07          JNZ SHORT Test1.0040102F
00401028  |. B8 02000000    MOV EAX,2
0040102D  |. EB 05          JMP SHORT Test1.00401034                     »»» jump to endw

0040102F  |> B8 00000000    MOV EAX,0
                                                                                      »»» endsw
00401034  |> BE F0FFFFFF    MOV ESI,-10                 

00401039  |. 6A 00          PUSH 0                                   ; /ExitCode = 0
0040103B  \. E8 00000000    CALL <JMP.&kernel32.ExitProcess>         ; \ExitProcess
00401040   .-FF25 04204000  JMP DWORD PTR DS:[<&kernel32.ExitProcess>;  kernel32.ExitProcess
00401046   $-FF25 00204000  JMP DWORD PTR DS:[<&kernel32.GetModuleHa>;  kernel32.GetModuleHandleA

.....................................................................................................................

MichaelW,
          «The asm equivalent to the C/C++ break statement would be an unconditional jump out
of the switch block. If the jump were not taken, then the next case statement
would be evaluated».

          In C++ do we need to put a break btw cases ? Isnt it implied ? If we dont
          put a break then the next case statement will be evaluated ?

          Could you compile this  (correct it if need)
          and show the asm code using Visual C++ Toolkit 2003

#include <windows.h>
int main(void)
{
    int i;
    int m;
    switch(i) 
    {
      case 1:
        m= 10;
        break; 
      case 2:
        m= 20;
      default:
        m=30;
        i=1;    // this necessary for the source to compile line
    }
    return 0;
}
Thanks
Rui

jj2007

Quote from: RuiLoureiro on September 02, 2008, 03:04:50 PM
          In C++ do we need to put a break btw cases ? Isnt it implied ? If we dont
          put a break then the next case statement will be evaluated ?

Exactly. C++ has the "fall through" behaviour, which can be nasty if you have


      mov eax, 1
      switch eax
        case  1
            mov eax, 2
        case  2
            mov eax, 123
      endsw


The C++ construct would give you 123 because it "falls through" to case 2, compares it to eax (=2, equal) and assigns 123. For my taste, this behaviour is counterintuitive.

The MASM switch gives back 2 in eax, same as in all BASIC dialects known to me.

MichaelW

Rui, compiled as posted, with all of the generated code included:

; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077

TITLE switch.c
.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif

INCLUDELIB LIBC
INCLUDELIB OLDNAMES

PUBLIC _main
; Function compile flags: /Odt
_TEXT SEGMENT
tv64 = -12 ; size = 4
_m$ = -8 ; size = 4
_i$ = -4 ; size = 4
_main PROC NEAR
; File c:\program files\microsoft visual c++ toolkit 2003\my\switch.c
; Line 3
push ebp
mov ebp, esp
sub esp, 12 ; 0000000cH
; Line 6
mov eax, DWORD PTR _i$[ebp]
mov DWORD PTR tv64[ebp], eax
cmp DWORD PTR tv64[ebp], 1
je SHORT $L74001
cmp DWORD PTR tv64[ebp], 2
je SHORT $L74002
jmp SHORT $L74003
$L74001:
; Line 9
mov DWORD PTR _m$[ebp], 10 ; 0000000aH
; Line 10
jmp SHORT $L73998
$L74002:
; Line 12
mov DWORD PTR _m$[ebp], 20 ; 00000014H
$L74003:
; Line 14
mov DWORD PTR _m$[ebp], 30 ; 0000001eH
; Line 15
mov DWORD PTR _i$[ebp], 1
$L73998:
; Line 17
xor eax, eax
; Line 18
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
eschew obfuscation

RuiLoureiro

Thanks jj & MichaelW

            I used SELECT/CASE and switch/case (Dbase ?) many years ago but i dont remember
            to use break. So that switch/case in C++ is not linear. It was out of my
            memory.
            Now, it is explaind why i found this

                case WM_PAINT:
                    hDC = BeginPaint( hWnd, &ps );
                    TextOut( hDC, 10, 10, "Hello, World!", 13 );
                    EndPaint( hWnd, &ps );
                    break;

            Thank you Michael for your code
Rui

RuiLoureiro

Hello !

      I decided to come here again
      because in the following example (see above) it seems that we need to have EAX=2
      after case 1 (btw case 1 and case 2). It doesnt compare. It goes on until the end.
      «...it "falls through" to case 2, compares it to eax (=2, equal) and assigns 123»
           
    mov    eax, 1
      switch eax
        case  1
            mov eax, 2
        case  2
            mov eax, 123
      endsw


; This is the code MichaelW posted (cleaned and commented by me):
     
   mov   eax, DWORD PTR _i$[ebp]
   mov   DWORD PTR tv64[ebp], eax
   cmp   DWORD PTR tv64[ebp], 1
   je   SHORT $L74001
   cmp   DWORD PTR tv64[ebp], 2
   je   SHORT $L74002
   jmp   SHORT $L74003
      ; »»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
      ; here is where we process the case 1 (means when i=1)
      ; »»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
$L74001:
   mov   DWORD PTR _m$[ebp], 10         
   jmp   SHORT $L73998                        <--- here is our "break"
      ; »»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
      ; here is where we process the case 2 (means when i=2)
      ; »»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
$L74002:
   mov   DWORD PTR _m$[ebp], 20         
                                                <--- here, there isnt "jmp SHORT $L73998[/b]"
                                                       so when i=2 then it continues to down
                                                       and goes to the default case
      ; »»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
      ; here is where we process default (means when i isnt 1 or 2)
      ; »»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
$L74003:
   mov   DWORD PTR _m$[ebp], 30         
   mov   DWORD PTR _i$[ebp], 1
$L73998:
   xor   eax, eax

      Thanks, again jj & MichaelW !
Rui