Hello.
I have been programming for about 2 years. I started out with Javascript, moved to vb6, then to vb.net, then to c#, and finally to native c/c++. After finally learning the windows api in c, I decided to chalange myself a bit further and learn assembly language. To my great surprise, it was actualy pretty easy, atleast compared to c++. Now for my question. This is more of a best practices type question. I have always been a traditionalist in how I code. In my c code you will see a lot of hungarian notation for instance. Another good example of this is the fact that I generally prefere to use CreateWindow, rather than CreateWindowEx, (Since the first parameter will usually be null anyway) unless I have use for the extended Window Styles. Now as for my assembly code, my question is this. I personally prefere to use the cmp/jmp (or what ever conditional jump instruction I might need at the moment) pair rather than the high level syntax. I also prefere to use API calls rather than use the macros provided by MASM, and accept for when I am calling functions with long argument lists prefere the old method of pushing the arguments on the stack in reverse order and calling the function via the call instruction (just like in the old days). Is there any advantage or disadvantage to this traditionalist approach to assembly programming?
Well manualy pushing of arguement is a bit tedious is it not? In practise it's probably more error prone/harder to debug. It makes source code with alot of api calls hard to read. Of course these are all personal things which you may feel differently about.
I suppose it really boils down to weither others will be reading your source code or not. If you think they will be then I'd suggest sticking to invoke for API calls but using cmp/j** over if/while.
Id use the invoke macro as manually pushing and balancing the stack can be error prone.
ie: you invariably forget something.
Besides, you should be spending more time on the finishing your good idea rather than
wrestling with the code to get it done.
Mike,
Both tecniques have their place and both have their advantages. Where you need more control or do things in non standard order, manualy push/call syntax is very useful but at the other end when you are shovelling trough a mountain of junk API calls, its just slow, harder to read and error prone.
Much the same with comparison/jump sequences, where you need real speed or additional flexibility, doing manual testing and branching on that basis does have some advantages in some circumstances but try and write a WndProc switch block for an aplication of any reasonable size and the automated methods are clearer, much faster to code and more reliable.
The trick with masm's preprocessor is to see it as an additional level of programmer control where you can actually control the form of code BEFORE it is fed to the assembler and this makes it a very powerful tool. Used in conjunction with library modules it gives capacity that is very efficient code, fast to code and generally more reliable as well.
The distinction to draw is what you get for the work you put into an app, putting your work into a high speed algo that can save large amounts of time in execution is worth the effort where creating te fstest messagebox on the planet most probably isn't.
Using a CMP/JMP pair is very traditional, and if you want to code other processors (such as microcontrollers), it is often the only way.
I don't see much point in avoiding INVOKE. When I worked with one mainframe, we did all of our ASM system calls through macros supplied by the manufacturer. And if you get into microcontrollers, most projects won't require an OS.
I guess I should have told you, I have always been a bit of a masochist when it comes to programming. I am known to use native c++ and api calls when c#.net would surfice (I actualy like c# but I like to be chalanged more). But I see the point, I guess my final question is, is the code generated by the .if .else macros as fast as the cmp/goto pair?
Mike,
Its pretty close but you risk having a redundant jump from time to time if you jump to an option from witin the .IF block. It is not a performace issue but you end up with an occasional unused instruction. Where te .IF blocks really show their advantage is when you perform multiple testing on te same line. The code generation is very good.
For me, one of the advantages of the .if blocks is that I don't have to scratch my head to invent different labels for the jumps. And it can make the code a lot easier to read with proper indentations.
Raymond
I prefer the high-level syntax, it's easier to write and it's less error-prone.
I think it's good to learn the traditional methods first, otherwise you can't pinch algotihms from all the old text books (or do the micro controller thang). Once you know how it works though, give it up. If you want to show off to your mates, write a program that converts you code back to trad style so when you've finished you can pretend like you done it all hard core.
Lately since the whole discussion about MASM and its funky use of brackets, I've been putting them around everything that references memory. Would this be considered "traditionalist coding style?"
.if CapMsg ; display one set of parameters
mov [CapMsg],0 ; only display one messagebox!
invoke dw2hex,[nCode],addr Cap1 ; convert returned vals to strings
invoke dw2hex,[wParam],addr Cap2
invoke dw2hex,[lParam],addr Cap3
invoke wsprintf,addr CapRes1,addr CapFmt1,addr Cap1,addr Cap2,addr Cap3
push edi ; save edi
mov edi,[lParam] ; EVENTMSG structure?
assume edi:ptr EVENTMSG
invoke dw2hex,[edi].message,addr Cap4
invoke dw2hex,[edi].paramL,addr Cap5
invoke dw2hex,[edi].paramH,addr Cap6
invoke dw2hex,[edi].time,addr Cap7
invoke dw2hex,[edi].hwnd,addr Cap8
assume edi:nothing
pop edi
invoke wsprintf,addr CapRes2,addr CapFmt2,addr Cap4,addr Cap5,addr Cap6,addr Cap7,addr Cap8
invoke lstrcat,addr CapRes1,addr CapRes2
invoke MessageBox,[hWnd],addr CapRes1,0,MB_OK
.endif
The only time I've ever moved away from the invoke style call was one time where I was saving bytes (size optimising to a ridiculous point). By pushing manually you can push all the arguments then do several calls, this saves bytes by pushing a register instead of the memory location (also using xor edx,edx rather than push zero).
invoke CreateMenu
mov hMenu, eax
invoke AppendMenu, eax , MF_STRING, CM_REFRESH, ADDR M1
invoke AppendMenu, hMenu, MF_STRING, CM_ABOUT, ADDR M2
invoke AppendMenu, hMenu, MF_SEPARATOR, NULL, NULL
invoke AppendMenu, hMenu, MF_STRING, CM_EXIT, ADDR M3
Becomes
invoke CreateMenu
mov hMenu, eax
xor edx, edx
push OFFSET M3
push CM_EXIT
push edx
push eax
push edx
push edx
push MF_SEPARATOR
push eax
push OFFSET M2
push CM_ABOUT
push edx
push eax
push OFFSET M1
push CM_REFRESH
push edx
push eax
call AppendMenu
call AppendMenu
call AppendMenu
call AppendMenu
But this really is an extreme case, and not something you really want to be doing.
Mirno
Quote from: Mark Jones on July 28, 2005, 03:37:02 PM
Lately since the whole discussion about MASM and its funky use of brackets, I've been putting them around everything that references memory. Would this be considered "traditionalist coding style?"
No.
The only other assembly language I've seen that uses bracketing characters around data address labels was for the Zilog Z-80. All the other non-x86 assemblers I can remember have no such need. They may (or may not) use brackets to indicate if a register name is used as an addressing register, as opposed to a data register. On some processors, brackets may mean indirect addressing
from memory. It doesn't on the x86 primarily because the processor itself does not support that addressing mode.
Well tenkey, FASM uses bracketing around memory references. The logic is that the variable name is really just an equate for its address value (be it absolute or relative) in memory and you would use brackets if you were entering the address so you use them with the name.
Its a minor and very personal thing I suppose, but it was one of the main reasons I switched to FASM.
I have never seen the square bracketing issue as anything other than a syntax requirement with some assemblers. TASM varies from MASM to ideal mode, NASM requires square brackets as does FASM but MASM does not require them around variable names at all but it will tolerate all sorts of superfluous bracketing as a style consideration.
mov var, eax
mov [var], eax
mov [[[[var]]]], eax
and a whole pile of nosense variations. Masm will let you write nonsense like,
mov eax, [[[[1111]]]]
because it strips it back to,
mov eax, 1111
I see it as very much the case of using a tool according to its documentation and with MASM, you are making the source ambiguous by using backets where they ae not required.
Some of the other assemblers around perform an abstraction of memory operand where MASM seperates an OFFSET from a stack location with different notation as they are different things.
Jeez, is nothing simple and sacred anymore? :toothy
Mark,
The world is a malicious plot to kneecap you into cowering complaince where I am a "a Rafferty's Rules" man myself. :bg
Jeese, I didn't figure on my thread becomming so popular. In any event, a lot of people seem to be interested in the square bracket issue now. I happen to know the answer to that. Assemblers like fasm, gas, etc use a very different style from MASM and TASM (TASM is the next closest thing to MASM). Square brackets are traditionally used for addressing memory via the complex memory addressing modes, since this is built into the CPU MASM also supports this feature. Assemblers also allow you to use the subscript notation such as this; myLabel[0].
I would not say that masm does not support indirect addressing. It just does it in another way. Using OFFSET or ADDR is a form of indirect addressing in the sense that it tells the assembler it is NOT an indirect address because the variable contains the data. A variable can be a pointer to another variable that contains the data. This is indirect addressing.
Paul
Quote from: MusicalMike on July 29, 2005, 01:18:51 AM
Jeese, I didn't figure on my thread becomming so popular. In any event, a lot of people seem to be interested in the square bracket issue now. I happen to know the answer to that. Assemblers like fasm, gas, etc use a very different style from MASM and TASM (TASM is the next closest thing to MASM). Square brackets are traditionally used for addressing memory via the complex memory addressing modes, since this is built into the CPU MASM also supports this feature. Assemblers also allow you to use the subscript notation such as this; myLabel[0].
At the CPU level, indirect addressing is specified by various bit patterns in the opcode. Brackets have nothing to do with it. Syntactically, you could use parenthesis (as Gas does), an asterisk (as C/C++ does), a caret (as Pascal does) or any other syntactical form.
In standard (original) Intel syntax, identifiers in the symbol table carry sufficient information to let the assembler know if they are a memory location or something else, so the assembler can determine whether an identifier references memory just from the identifier. However, this takes a more sophisticated data structure in the symbol table and more complex code in the assembler itself. It is no wonder that hobby level assemblers eschew the Intel syntax and go with the use of brackets to signify memory access -- it's much easier to code.
In the early days of computer, assembly language syntax was kept as simple as possible because assemblers had to be small. Early computers didn't have much memory and you couldn't afford all the extra code and data to support a sophisticated syntax. Several "old-timers" got used to that simplistic syntax and grumbled when Intel syntax came along, but Intel's syntax is better for many well-understood software engineering reasons.
As to the "hobby-level" assemblers, that's a choice they made and they're going to have to live with it. Some prefer that syntax, but it has it's own drawbacks.
Cheers,
Randy Hyde
(off topic) Wait, are you THE Randall Hyde the creator of the High Level Assembler?
[Grumble]
Is that an indirect grumble? I sure am a grumbler, though.
Paul
Quote from: MusicalMike on July 29, 2005, 04:40:38 PM
(off topic) Wait, are you THE Randall Hyde the creator of the High Level Assembler?
One and the same.
Cheers,
Randy Hyde
Wuh-oh, this thread is really goin' askew now. :toothy
Quote from: Eóin on July 28, 2005, 11:56:33 PM
Well tenkey, FASM uses bracketing around memory references. The logic is that the variable name is really just an equate for its address value (be it absolute or relative) in memory and you would use brackets if you were entering the address so you use them with the name.
I should have stated that the Zilog assembler was the only
non-x86 assembler that I've ever seen that uses bracketing (actually round brackets) in the same way as FASM/NASM/etc. I implied it in the sentence that followed.
Because we're talking about different syntaxes that describe
exactly the same machine, I just view it all as window dressing. I have my preferences, but I have no problems with whatever style you choose. Heck, if I was interested enough, I'd create an assembly language that looked like the mainframe assembler I used to use.
The question has been about "traditional" coding style, which uses minimal syntax. As Randy mentions, this was true of the really old (and thus to my mind, the "traditional") assemblers.
Pbrennick,
quote from AOA:
There are eight forms of this addressing mode on the 80x86, best demonstrated by the following instructions:
mov( [eax], al );
mov( [ebx], al );
mov( [ecx], al );
mov( [edx], al );
mov( [edi], al );
mov( [esi], al );
mov( [ebp], al );
I know I haven't learn asm that much but its because I read asm only 1 minute and then play or do something else.
But now I have mature a little bit, and the bore is getting drown away fool genes let me stay.
That sucks that masm doesn't let you support a normal syntax from Intel, damn ms. But LEA direct addressing mode, I didn't know much about OFFSET and ADDR but I know there functions.
MusicalMike,
all was derive from machine language 0101 to 0101 asm, asm with macros, etc... You can try Goasm the tuts is where you learn quickly and fast. HLA has its own book AOA, the best for description among overall, Nasm has its own doc it can be handy and Masm32 only has iczelion. programmersheaven.com Asm > a tutorial for 16-bits Adams or Cat like that the best
for getting started at DOS old days and then in Webster again you have to find the formal doc from ms imo good. Before you get lost... I forgot the Intel manuals don't get lost like Linux devs that they don't know that they're doing in Intel.
Just a note in Go, the tutorial was maded to learn efficiently and fast you might find it difficult but that's how binary and hexadecimal numbers are useful. But much of the theory you must find it in HLA and the rest, asm is for people that earned a medallion for learning it like another language.