Quote from: hamper on November 29, 2013, 03:37:50 AM
.code
This is a segment directive to the assembler. It tells the assembler where the start of the executable code segment of the program begins. The very next line in your source code should consist only of a label, and the following one is traditional...
start:
You will often find code that doesn't follow this advice, like this snippet:
include \masm32\include\masm32rt.inc
; NOT NEEDED: mbox PROTO: DWORD, :DWORD, :DWORD
.code
mbox proc txt, xtitle, mode
MsgBox 0, txt, xtitle, mode
ret
mbox endp
; ### we move the entry point to the end of the program ###
start: invoke mbox, chr$("Hello World"), chr$("Masm32 is great:"), MB_OK
exit
end start
As Hutch already mentioned, assemblers are dumb animals: They pass the code from top to bottom, and if you want to call a proc that the assembler has not yet seen, it will complain bitterly. With the standard order (first
start: then below the procs), you will see such complaints frequently, unless you tell it beforehand "hey, below you will find a proc called 'mbox' with three dword args". You tell it through a mbox PROTO: DWORD, :DWORD, :DWORD
Now, the example above works without the PROTO. The reason is simple: When the assembler hits the invoke mbox, ..., it has already seen the
mbox proc and its args, so no 'announcement' needed.
Yep jj2007, but to quote Microsoft:
"Declaring procedure prototypes is good programming practice, but is optional.
Prototypes in MASM perform the same function as prototypes in C and other
high-level languages. A procedure prototype includes the procedure name, the
types, and (optionally) the names of all parameters the procedure expects.
Prototypes usually are placed at the beginning of an assembly program or in a
separate include file so the assembler encounters the prototype before the actual
procedure"
So what you are doing (for the sake of proving a point, I guess) is pointing out to someone just starting out in assembly language programming that they can ignore good programming practice and just define procedures immediately above the point in the code where they are called - no prototype needed.
Personally, I try to always follow good programming practice. I prototype every function at the top of my program (after the final includelib), followed by structure type declarations, then the data segments, then .code start: then the code itself, and finally define all my procedures just before the end start.
Others will do things in a different way, but I don't think it helps a newcomer to point out that bad programming practice is perfectly acceptable, or maybe even the norm. Surely we should try and guide newcomers towards a structured program layout?
heavens - i rarely PROTO all the PROC's :redface:
i prototype the ones that have parameters, so i can use INVOKE
i even prototype WndProc's - not generally required
i prototype thread procedures, so i can reference them before they appear in the source
a few other cases that are similar to the thread issue
sometimes, externals that are not otherwise prototyped
once in a while, all of the procedures in a program fall into one of the catagories above
then, i guess they're all prototyped :P
I think the example JJ posted was to demonstrate how MASM reads a source file from top to bottom so that if you have a procedure before it is called, it is already known by the assembler when it is called and often old assembler code is written that way. I don't personally like order dependent code and have always used prototypes for normal procedures but then with an assembler you can do other things as well, make a set of similar procedures then jump to a global label in another procedure that acts as a collector, it may not be politically correct but then most assembler is not either.
Then of course you can make a proc within a proc which is a no no in most higher level languages, the virtue of an assembler is design freedom and while it is worth developing good habits, its also very useful to know how to do things in other ways.
with masm v5.1, i could nest procedures
newer versions don't seem to like it :(
as you say, there is the public label - that does the job
Quote from: hamper on November 29, 2013, 09:31:06 AMI don't think it helps a newcomer to point out that bad programming practice is perfectly acceptable, or maybe even the norm. Surely we should try and guide newcomers towards a structured program layout?
OMG I feel so ashamed :redface: :redface: :redface:
As Hutch wrote, I simply wanted to demonstrate why the assembler, reading top-down, needs a PROTO if the invoked procedure sits below.
But OK, since we are now in flaming mode, let's put the record straight: I don't care a s**t what Micros**t declares as good programming practice. Fact is that
- 99% of all PROTOs are hidden in the include files, and NOBODY ever looks at them, except Hutch because he has to write them; all others check the documentation at MSDN or in Win32.hlp
- 99% of all user-defined PROTOs are of the form myprog PROTO :DWORD,:DWORD,:DWORD, and therefore don't tell the user anything about the paras other than that they are DWORDs (and if it's a REAL4, they come here asking)
- 99% of all users write that bloody PROTO once on top and than never ever look at it again, except when they added a para to the proc below and the assembler complaints bitterly "why didn't you tell me explicitly that you have changed your mind?"
- 99% of all users, when asking themselves "why does my invoke not work?", go straight to the proc, not to the PROTO, and look at the paras there.
So, in short: PROTOs are a crappy bad relic, and their only reason to be there is that MASM is too dumb to look forward.
As to myself, I do as Dave, and call my procs. Occasionally I put PROTOs, but only because the assembler otherwise doesn't understand me.
Have a nice day :biggrin:
The great thing about assembly is the control you have.
I don't prototype anything (except externals) and try to avoid INVOKE, for me it's bare-bones.
No such thing as "best programming practice" with ASM. Find one way and be consistent.
include \masm32\include\masm32rt.inc
.code
start: call fwd ;OK
call fwd2 ;OK
invoke fwd2 ;error A2006:undefined symbol : fwd2
ret
fwd: ret
fwd2 proc
ret
fwd2 endp
end start
INVOKE requires PROTO
Just for fun, ML64 will use PROTO but not INVOKE and only uses END, not END <label>. The entry is set by the linker but must be PUBLIC for ML64.
Hardcore (i.e. entangled :greensml:) invoke, pointer protorypes, name decoration and polymorphism example :biggrin:
Take notice that the label uses name decoration specifics that allows declare a "proc" label without defining the proc itself. Could be used inside procs to provide "inner" local procs... so this is incapsulation example as well :lol:
include \masm32\include\masm32rt.inc
.686
.mmx
.xmm
.data
dd1 dd 1
dd2 dd 2
dd3 dd 3
dd4 dd 4
fl1 REAL8 123.456
fl2 REAL8 789.012
.code
stdcall@dword@dword@dword@dword@dword typedef PROTO stdcall :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
stdcall@dword@qword@qword typedef PROTO stdcall :DWORD, :QWORD, :QWORD
ProcOverloaded PROTO :DWORD, :DWORD, :QWORD, :DWORD
start proc
mov ebx,offset ProcOverloaded
invoke stdcall@dword@dword@dword@dword@dword ptr ebx,-1,dd1,dd2,dd3,dd4
invoke stdcall@dword@qword@qword ptr ebx,-2,fl1,fl2
invoke ProcOverloaded, -3,dd1,fl1,dd2
invoke crt__getch
invoke crt_exit,0
start endp
PUBLIC ProcOverloaded@20
ProcOverloaded@20:
switch dword ptr [esp+4]
case -1
invoke crt_printf,CTXT("Invoked with 5 dwords: %d, %d, %d, %d, %d",13,10),dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4]
case -2
invoke crt_printf,CTXT("Invoked with dword and two doubles: %d, %lf, %lf",13,10),dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4]
case -3
invoke crt_printf,CTXT("Invoked with two dwords, double and dword: %d, %d, %lf, %d",13,10),dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4]
endsw
ret 20
end start
The output:
Invoked with 5 dwords: -1, 1, 2, 3, 4
Invoked with dword and two doubles: -2, 123.456000, 789.012000
Invoked with two dwords, double and dword: -3, 1, 123.456000, 2
Hi Alex,
invoke crt_printf,CTXT("Invoked with 5 dwords: %d, %d, %d, %d, %d",13,10),dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4],\
dword ptr [esp+4+4*4]
The red part is not n00b-safe, you should add some comments ;-)
Here is an example of bad programming practiceTM - a nested proc that can be invoked:
include \masm32\MasmBasic\MasmBasic.inc ; download (http://masm32.com/board/index.php?topic=94.0)
.code
MainBox proc arg1, arg2, arg3
invoke MessageBox, 0, arg1, arg2, arg3
mov CreateInvoke(MyBox, 3*dword), @F
invoke MyBox, chr$("Invoked from inside a proc"), chr$("Nested proc:"), MB_OK
ret
@@: ; internal anonymous label
push ebp ; stack frame
mov ebp, esp
invoke MessageBox, 0, [ebp+8], arg2, [ebp+16] ; <<<< how to obfuscate a source ;-)
leave
retn 3*DWORD
MainBox endp
Init
fn MainBox, "This is MainBox", "Just for fun:", MB_OK
Exit
end start