This capacity has been around since MASM6 in 1990 but I have rarely ever seen it used. The capacity to assign and reassign a constant with the "=" sign allows code like the following. It requires a macro which allows you to check the arg count and it does not require a LOCAL variable for the return value as it uses the .DATA? section for a constant instead. The return value is always global in scope within a module but the name can be re-assigned at any time so you don't get the bad habits of allocation a large number of globals and getting naming conflicts.
I have never introduced the technique in the MASM32 SDK because it is a different animal with a different logic but it is highly suitable for making a simplified script style of language that assembles into pure assembler code without any bad habits.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include64\masm64rt.inc
MsgBox MACRO args:VARARG
LOCAL acnt,rval
acnt = argcount(args)
IF acnt ne 4
echo ---------------------------------
echo MsgBox : incorrect argument count
echo ---------------------------------
.err <* incorrect argument count *>
ENDIF
.data?
rval dq ?
.code
invoke __imp_MessageBoxA,args
mov rval, rax
EXITM < rval>
ENDM
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
entry_point proc
call Select_Option
.if choice == IDYES
void = MsgBox(0,"You chose YES","Result",MB_OK)
.else
void = MsgBox(0,"You chose NO","Result",MB_OK)
.endif
.exit
ret
entry_point endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Select_Option proc
choice = MsgBox(0,"Choose YES or NO","Choice",MB_YESNO)
ret
Select_Option endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end
comment # ------------------------------------------------------------------------------------------
.text:0000000140001000 C8800000 enter 0x80, 0x0
.text:0000000140001004 4883EC60 sub rsp, 0x60
.text:0000000140001008 E86D000000 call sub_14000107a
.text:000000014000100d 48833DAB11000006 cmp qword ptr [0x1400021c0], 6
.text:0000000140001015 752B jne 0x140001042
.text:0000000140001017 48C7C100000000 mov rcx, 0x0
.text:000000014000101e 488B1549100000 mov rdx, qword ptr [0x14000206e]
.text:0000000140001025 4C8B055B100000 mov r8, qword ptr [0x140002087]
.text:000000014000102c 49C7C100000000 mov r9, 0x0
.text:0000000140001033 FF1523110000 call qword ptr [MessageBoxA]
.text:0000000140001039 48890570110000 mov qword ptr [0x1400021b0], rax
.text:0000000140001040 EB29 jmp 0x14000106b
.text:0000000140001042
.text:0000000140001042 0x140001042:
.text:0000000140001042 48C7C100000000 mov rcx, 0x0
.text:0000000140001049 488B154D100000 mov rdx, qword ptr [0x14000209d]
.text:0000000140001050 4C8B0560100000 mov r8, qword ptr [0x1400020b7]
.text:0000000140001057 49C7C100000000 mov r9, 0x0
.text:000000014000105e FF15F8100000 call qword ptr [MessageBoxA]
.text:0000000140001064 4889054D110000 mov qword ptr [0x1400021b8], rax
.text:000000014000106b
.text:000000014000106b 0x14000106b:
.text:000000014000106b 48C7C100000000 mov rcx, 0x0
.text:0000000140001072 FF15D4100000 call qword ptr [ExitProcess]
.text:0000000140001078 C9 leave
.text:0000000140001079 C3 ret
; --------------------------------------------------------------------------
; sub_14000107a
; --------------------------------------------------------------------------
sub_14000107a proc
.text:000000014000107a C8800000 enter 0x80, 0x0
.text:000000014000107e 4883EC60 sub rsp, 0x60
.text:0000000140001082 48C7C100000000 mov rcx, 0x0
.text:0000000140001089 488B1541100000 mov rdx, qword ptr [0x1400020d1]
.text:0000000140001090 4C8B0550100000 mov r8, qword ptr [0x1400020e7]
.text:0000000140001097 49C7C104000000 mov r9, 4
.text:000000014000109e FF15B8100000 call qword ptr [MessageBoxA]
.text:00000001400010a4 48890515110000 mov qword ptr [0x1400021c0], rax
.text:00000001400010ab C9 leave
.text:00000001400010ac C3 ret
sub_14000107a endp
-------------------------------------------------------------------------------------------------- #
Interesting technique - I never thought of that ::)
But it's tricky. Attached the 32-bit version; see in particular the line that finishes with ???
Question : Why the hell is the executable 301k ?
debug-info included.
porebase.exe /SPLIT can split it.
Quote from: hutch-- on April 08, 2017, 12:48:00 PM
Question : Why the hell is the executable 301k ?
As Tim already noted, it has debug info. Here is the same without - 2048 bytes.
But if you run the "fat" version with Olly, you can see what it actually does, and why choosing YES fails :bgrin:
Really nice Macro and idea :t
Funny enough on both the original and your modified version, the choice of YES works correctly. I don't claim to understand what your mods do or why they are done. I am inclined to trust a disassembler, that is why I posted the disassembly.
Quote from: hutch-- on April 08, 2017, 11:16:38 PM
Funny enough on both the original and your modified version, the choice of YES works correctly. I don't claim to understand what your mods do or why they are done.
varCnt=varCnt+1
tmp$ CATSTR <MsgBoxRet>, %varCnt
Not a big deal, it just uses global names instead of local ones, so that you can identify them in the debugger as MsgBoxRet1, MsgBoxRet2 etc.
The problem with void returning 0 or 1 instead of always 1 aka IDOK is that the first void is not the same as the second one. It may have the same name, but it is a different variable (and that is what I call tricky ;)):
.if choice == IDYES
inc eax ; marker for debugging
void = MsgBox(0,"You chose YES","Result",MB_OK)
.else
dec eax ; marker for debugging
void = MsgBox(0,"You chose NO","Result",MB_OK)
.endif
print str$(void)
00401089 ³. CC int3
0040108A ³. 833D 98324000 06 cmp dword ptr [MsgBoxRet3], 6
00401091 ³. 75 1B jne short 004010AE
00401093 ³. 40 inc eax
00401094 ³. 6A 00 push 0 ; ÚType = MB_OK|MB_DEFBUTTON1|MB_APPLMODAL
00401096 ³. 68 8E304000 push offset 0040308E ; ³Caption = "Result"
0040109B ³. 68 80304000 push offset 00403080 ; ³Text = "You chose YES"
004010A0 ³. 6A 00 push 0 ; ³hOwner = NULL
004010A2 ³. E8 BB010000 call MessageBoxA ; ÀUSER32.MessageBoxA
004010A7 ³. A3 90324000 mov [MsgBoxRet1], eax
004010AC ³. EB 19 jmp short 004010C7
004010AE ³> 48 dec eax
004010AF ³. 6A 00 push 0 ; ÚType = MB_OK|MB_DEFBUTTON1|MB_APPLMODAL
004010B1 ³. 68 8E304000 push offset 0040308E ; ³Caption = "Result"
004010B6 ³. 68 B8304000 push offset 004030B8 ; ³Text = "You chose NO"
004010BB ³. 6A 00 push 0 ; ³hOwner = NULL
004010BD ³. E8 A0010000 call MessageBoxA ; ÀUSER32.MessageBoxA
004010C2 ³. A3 94324000 mov [MsgBoxRet2], eax
004010C7 ³> 68 ED304000 push offset ??003F ; ÚArg2 = HighLevelEmu.??003F
004010CC ³. FF35 94324000 push dword ptr [MsgBoxRet2] ; ³Arg1 = 0
004010D2 ³. E8 49000000 call dwtoa ; ÀHighLevelEmu.dwtoa
004010D7 ³. 68 ED304000 push offset ??003F ; ÚArg1 = HighLevelEmu.??003F
004010DC ³. E8 A7000000 call StdOut ; ÀHighLevelEmu.StdOut
You are printing only MsgBoxRet2 - and that's why clicking NO yields IDOK, while clicking Yes loads MsgBoxRet1 but you still print MsgBoxRet2 :(
Its a problem with your assumptions with the debugger, if you look at the macro, each time it is called it provides a unique .DATA? section address which you assign to any name you like using the standard MASM "=" assignment operator. When you re-use a name that has previously been assigned to an earlier address, you lose the address of the earlier assignment. If you want each one to remain unique, you must use unique names for each one.
As you would be familiar, the name "void" usually means that return value is not needed and in the context of the original example without the modified debug code, the YES or NO API calls do not need a return value as the MB style is MB_OK. The original example does exactly what it was designed to do, present a simple MessageBox, test its return value and use the same simple MessageBox to indicate which choice was taken.
The original dis-assembly said it all.
JJ:
What ml 32? I'm trying with ML14 but not success.
This works fine in 32 bit.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
; Build with "Assemble & Link"
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
MsgBox MACRO args:VARARG
LOCAL acnt,rval
acnt = argcount(args)
IF acnt ne 4
echo ---------------------------------
echo MsgBox : incorrect argument count
echo ---------------------------------
.err <* incorrect argument count *>
ENDIF
.data?
rval dd ?
.code
fn MessageBox,args
mov rval, eax
EXITM < rval>
ENDM
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
choice = MsgBox(0,"This is a test","Title",MB_YESNO)
.if choice == IDYES
void = MsgBox(0,"You selected YES","Result",MB_OK)
.else
void = MsgBox(0,"You selected NO","Result",MB_OK)
.endif
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
The DumpPE RESULT
00401000 start:
00401000 E807000000 call fn_0040100C
00401005 6A00 push 0
00401007 E85A000000 call jmp_ExitProcess
0040100C fn_0040100C: ; Xref 00401000
0040100C 6A04 push 4
0040100E 680F304000 push offset off_0040300F ; 'Title',000h
00401013 6800304000 push offset off_00403000 ; 'This is a test',000h
00401018 6A00 push 0
0040101A E841000000 call jmp_MessageBoxA
0040101F A350304000 mov [off_00403050],eax
00401024 833D5030400006 cmp dword ptr [off_00403050],6
0040102B 751A jnz loc_00401047
0040102D 6A00 push 0
0040102F 6826304000 push offset off_00403026 ; 'Result',000h
00401034 6815304000 push offset off_00403015 ; 'You selected YES',000h
00401039 6A00 push 0
0040103B E820000000 call jmp_MessageBoxA
00401040 A354304000 mov [off_00403054],eax
00401045 EB18 jmp loc_0040105F
00401047 loc_00401047: ; Xref 0040102B
00401047 6A00 push 0
00401049 683D304000 push offset off_0040303D ; 'Result',000h
0040104E 682D304000 push offset off_0040302D ; 'You selected NO',000h
00401053 6A00 push 0
00401055 E806000000 call jmp_MessageBoxA
0040105A A358304000 mov [off_00403058],eax
0040105F loc_0040105F: ; Xref 00401045
0040105F C3 ret
You eill note that this code in 32 bit does not need the argument count as ML already checks against a prototype.
MsgBox MACRO args:VARARG
LOCAL acnt,rval
acnt = argcount(args)
; IF acnt ne 4
; echo ---------------------------------
; echo MsgBox : incorrect argument count
; echo ---------------------------------
; .err <* incorrect argument count *>
; ENDIF
.data?
rval dd ?
.code
fn MessageBox,args
mov rval, eax
EXITM < rval>
ENDM
:biggrin: :t
Quote from: HSE on April 09, 2017, 01:36:32 AM
JJ:
What ml 32? I'm trying with ML14 but not success.
Sorry for that. Here is a version that assembles also with ML 6.14. Here are the most important lines:
; int 3
.if choice == IDYES
inc eax ; marker for debugging
result = MsgBox(0,chr$("You chose YES"), chr$("Result"), MB_YESNOCANCEL)
.else
dec eax ; marker for debugging
result = MsgBox(0, chr$("You chose NO"), chr$("Result"), MB_YESNOCANCEL)
.endif
print "in round #2, "
.if result==IDYES
print "you clicked YES"
.elseif result==IDNO
print "you clicked NO"
Thank JJ! I was wandering what kind of ML can accept quoted text in Invoke :t
32 bit ML cannot directly use quoted text, it requires a macro that gets the text address. the "fn" notation in MASM32 provides the quoted text support, the method that JJ uses is a macro for each text instance.
Now back to the serious stuff, 64 bit MASM. Here is a more advanced set of macros that give more information on any argument count error, it tells you if there are too many or not enough arguments and maintains the high level characteristics of the technique. It is a different brain to conventional MASM notation but it has its place with a dedicated command style language for a narrower range of tasks. The reason for the extra remote macros is to get each direct macro smaller which in turn would make the task of automating the macro production a lot cleaner so it could routinely be expanded up into a much larger macro count. The ".ma" macro is to put more data on each line where it is to your advantage.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include64\masm64rt.inc
check_count MACRO reqr,pname,args:VARARG
LOCAL acnt
acnt = argcount(args)
IF acnt gt reqr
echo ---------------------------------
echo pname : too many arguments
echo ---------------------------------
.err <* too many arguments *>
ELSEIF acnt lt reqr
echo ---------------------------------
echo pname : not enough arguments
echo ---------------------------------
.err <* not enough arguments *>
ENDIF
ENDM
set_data MACRO dname
.data?
dname dq ?
.code
ENDM
MsgBox MACRO args:VARARG
LOCAL rval
.ma check_count 4,"MsgBox",args # set_data rval # mov rval, rv(MessageBoxA,args)
EXITM < rval>
ENDM
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
entry_point proc
call Select_Option
.if choice == IDYES
void = MsgBox(0,"You chose YES","Result",MB_OK)
.else
void = MsgBox(0,"You chose NO","Result",MB_OK)
.endif
.exit
entry_point endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Select_Option proc
choice = MsgBox(0,"Choose YES or NO","Choice",MB_YESNO)
ret
Select_Option endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end
Sad to say the technique runs into problems with some API functions. GetModuleHandle() when run with this technique generates an error "The address must be relocatable". Some of the others as well. The macro to test arg count seems to be fine.
; -----------------------------------------------------
; required_count = The number of arguments to check for
; quoted_name = The MACRO name in double quotes
; args = variable number of arguments
; -----------------------------------------------------
test_arg_count MACRO required_count,quoted_name,args:VARARG
LOCAL acnt
acnt = argcount(args)
IF acnt gt required_count
echo ------------------------------------
echo quoted_name : too many arguments
echo ------------------------------------
.err <* too many arguments *>
ELSEIF acnt lt required_count
echo ------------------------------------
echo quoted_name : not enough arguments
echo ------------------------------------
.err <* not enough arguments *>
ENDIF
ENDM
; -----------------------------------------------------
MsgBox MACRO args:VARARG
test_arg_count 4,"MsgBox",args
invoke MessageBox,args
EXITM <rax>
ENDM
Allows this.
mov void, MsgBox(0,"Text Message","Title",MB_OK)
The only gain I could see with the "=" operator was one of notational familiarity so its no great loss.