Author Topic: Differences between NASM and MASM  (Read 19419 times)

mark allyn

  • Guest
Differences between NASM and MASM
« on: August 07, 2009, 01:55:48 AM »
Hi everyone -

At the suggestion of Frank I downloaded the NASMX material.  Looked over the win32 file and especially the demos shown there.  As is true of MASM32 the demos contain a large number of include/includelib declarations.  This is how all MASM32 programs invariably begin.  So be it.

This requirement for numerous includes at the front of MASM32 does NOT appear necessary in NASM code.  GREAT!  The use of extern seems to be sufficient, as far as I can tell, to get access to c functions like printf, scanf, and so forth, all of which I feel are like old friends.  

I know it is in some sense unfair to ask why the two languages are so different.  Syntactically, as I suppose everyone knows, they are very similar (altho there are some big differences too) But, as a novice, it really threw me off.  I was absolutely certain that NASM would require explicit include statements in order to get to the c runtime libs.  Evidently, not, based on the code snippet I saw on a related thread.

Can someone help me understand what is going on?  BTW, I don't want to violate any behavior standards for this site -- I'm not trying to get anyone to say whether either approach is better or worse!  I am simply trying to understand the mechanics and why they appear to be different in the two cases.

Mark Allyn

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Differences between NASM and MASM
« Reply #1 on: August 09, 2009, 04:16:26 AM »
There's no rule against saying "Nasm is better"! :)

Seriously, examining differences between Masm and Nasm, and between MASM32 and NASMX might be enlightening. To start with, Masm has "invoke" and "proc" (and "if" and "while" and "switch/case"?) "built in". If you want to use those in Nasm (and you probably do... or will), you'd need to include a file with macros to handle it. If you stick to "real instructions", no need. I don't think you "need" those includes in Masm, either - they're a convenience.

Lets look at Hutch's "minimum.asm":

; #########################################################################

.386
      .model flat, stdcall
      option casemap :none   ; case sensitive

; #########################################################################

include \masm32\include\windows.inc
      include \masm32\include\user32.inc
      include \masm32\include\kernel32.inc

includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib

; #########################################################################

.code

start:

jmp @F
      szDlgTitle    db "Minimum MASM",0
      szMsg         db "  --- Assembler Pure and Simple ---  ",0
    @@:

push MB_OK
    push offset szDlgTitle
    push offset szMsg
    push 0
    call MessageBox

push 0
    call ExitProcess

; --------------------------------------------------------
    ; The following are the same function calls using MASM
    ; "invoke" syntax. It is clearer code, it is type checked
    ; against a function prototype and it is less error prone.
    ; --------------------------------------------------------

; invoke MessageBox,0,ADDR szMsg,ADDR szDlgTitle,MB_OK
    ; invoke ExitProcess,0

end start

"windows.inc" is the big one. Starts by defining "TRUE" and "FALSE"... and a bazillion other "long names for small integers". Then it has a bunch of structure definitions. "user32.inc" and "kernel32.inc" seem to be mostly "PROTO"s - a feature Nasm does not have. They provide some error-checking, and probably avoid having to specify sizes of parameters(?). Nice to have, but not necessary.

Then comes the "includelib" directives. I initially thought that this caused "ml" to pass the library names to "link" on the command line - but that's not how it's done! Look at a disassembly (Agner Fog's "objconv") of the resulting .obj file:

; Disassembly of file: min.obj
; Fri Aug  7 15:38:21 2009
; Mode: 32 bits
; Syntax: YASM/NASM
; Instruction set: 80386


global _start

extern _ExitProcess@4                                   ; near
extern _MessageBoxA@16                                  ; near

@comp.id equ 000E1C83H                                  ; 924803


SECTION .text   align=4 execute                         ; section number 1, code

_start: ; Function begin
        jmp     ?_001                                   ; 0000 _ EB, 33

szDlgTitle:
;       dec     ebp                                     ; 0002 _ 4D
        db 4DH
;       imul    ebp, dword [esi+69H], 544044397         ; 0003 _ 69. 6E, 69, 206D756D
        db 69H, 6EH, 69H, 6DH, 75H, 6DH, 20H
;       dec     ebp                                     ; 000A _ 4D
        db 4DH
;       inc     ecx                                     ; 000B _ 41
        db 41H
;       push    ebx                                     ; 000C _ 53
        db 53H
;       dec     ebp                                     ; 000D _ 4D
        db 4DH
; Error: Instruction out of phase with next label
;       add     byte [eax], ah                          ; 000E _ 00
        db 00H
szMsg:; and     byte [eax], ah                          ; 000F _ 20. 20
        db 20H, 20H
;       sub     eax, 1092627757                         ; 0011 _ 2D, 41202D2D
        db 2DH, 2DH, 2DH, 20H, 41H
;       jnc     8BH                                     ; 0016 _ 73, 73
        db 73H, 73H
; Note: Prefix bit or byte has no meaning in this context
;       insd                                            ; 0018 _ 65: 6D
        db 65H, 6DH
; Note: SIB byte unnecessary here
; Note: Address has scale factor but no index register
;       bound   ebp, dword [ebp+72H]                    ; 001A _ 62. 6C 65, 72
        db 62H, 6CH, 65H, 72H
;       and     byte [eax+75H], dl                      ; 001E _ 20. 50, 75
        db 20H, 50H, 75H
;       jc      88H                                     ; 0021 _ 72, 65
        db 72H, 65H
;       and     byte [ecx+6EH], ah                      ; 0023 _ 20. 61, 6E
        db 20H, 61H, 6EH
;       and     byte [fs:ebx+69H], dl                   ; 0026 _ 64: 20. 53, 69
        db 64H, 20H, 53H, 69H
;       insd                                            ; 002A _ 6D
        db 6DH
;       jo      99H                                     ; 002B _ 70, 6C
        db 70H, 6CH
; Note: Absolute memory address without relocation
;       and     byte [gs:20202D2DH], ch                 ; 002D _ 65: 20. 2D, 20202D2D
        db 65H, 20H, 2DH, 2DH, 2DH, 20H, 20H
; Error: Instruction out of phase with next label
; Note: Zero displacement could be omitted
;       add     byte [edx], ch                          ; 0034 _ 00
        db 00H
?_001:  push    0                                       ; 0035 _ 6A, 00
        push    szDlgTitle                              ; 0037 _ 68, 00000000(d)
        push    szMsg                                   ; 003C _ 68, 00000000(d)
        push    0                                       ; 0041 _ 6A, 00
        call    _MessageBoxA@16                         ; 0043 _ E8, 00000000(rel)
        push    0                                       ; 0048 _ 6A, 00
; Note: Function does not end with ret or jmp
        call    _ExitProcess@4                          ; 004A _ E8, 00000000(rel)
; _start End of function


SECTION .data   align=4 noexecute                       ; section number 2, data

resb    79                                      ; 0000 _


SECTION .drectve align=1 noexecute                      ; section number 3, const

db 2DH, 64H, 65H, 66H, 61H, 75H, 6CH, 74H       ; 0000 _ -default
        db 6CH, 69H, 62H, 3AH, 5CH, 6DH, 61H, 73H       ; 0008 _ lib:\mas
        db 6DH, 33H, 32H, 5CH, 6CH, 69H, 62H, 5CH       ; 0010 _ m32\lib\
        db 75H, 73H, 65H, 72H, 33H, 32H, 2EH, 6CH       ; 0018 _ user32.l
        db 69H, 62H, 20H, 2DH, 64H, 65H, 66H, 61H       ; 0020 _ ib -defa
        db 75H, 6CH, 74H, 6CH, 69H, 62H, 3AH, 5CH       ; 0028 _ ultlib:\
        db 6DH, 61H, 73H, 6DH, 33H, 32H, 5CH, 6CH       ; 0030 _ masm32\l
        db 69H, 62H, 5CH, 6BH, 65H, 72H, 6EH, 65H       ; 0038 _ ib\kerne
        db 6CH, 33H, 32H, 2EH, 6CH, 69H, 62H, 20H       ; 0040 _ l32.lib
        db 2DH, 65H, 6EH, 74H, 72H, 79H, 3AH, 73H       ; 0048 _ -entry:s
        db 74H, 61H, 72H, 74H, 20H                      ; 0050 _ tart

Nasm doesn't have "includelib", but we can do:

section .drectve
db "-defaultlib:blablabla"

So an "includelib" macro would probably be possible in Nasm. I don't know what, if any, linkers besides MS's would honor the ".drectve" section. Pretty neat trick!

Also notice that the .obj file uses the "true names" - "_MessageBoxA@16" (I remembered right!) and "_ExitProcess@4". I expected these to be defined in one of the include files, but the .lib files seem to be the only place these show up. Dunno how that works. "__imp__MessageBoxA@16" also appears in the .lib files. Cygwin's lib/win32api/libuser32.a does *not* appear to be the same thing. :(

I was going to post "DEMO1" from the predecessor to NASMX, NASM32, for comparison. But it's quite a different system. It uses "-f obj", which accepts the "import" directive, which gets us around having to use libraries. The "import" directive is hidden away in the "invoke" macro, so we don't see it and it "just works". Knowing which API is in which .dll - which "import" needs - is handled in the other include files, looks like. "TRUE", "FALSE", etc. and the structure definitions are in "windows.inc", similar to MASM32.

This is a fine system for "-f obj", but "-f win32" would be the more "modern" output format. I don't know what, if any, difference there is in the resulting executable, but... we wanna use "-f win32"!

I think the new NASMX has gone to "-f win32"... and "-f win64" probably. Some self-extracting executables can be extracted under Linux, apparently, but I haven't found the trick to extracting this one.

I say "-f win32" is more modern, but I have an ancient "package" that uses it:

http://home.comcast.net/~fbkotler/win32nasmbase.zip

Looking again at this, that's pretty useless. That's not the "good" version of the file. I guess it's the "legal" version - missing the /bin directory, which includes "link.exe" (along with a really old version of Nasm!), and the /lib directory - which is what makes the whole thing go! (looks like it uses the .drectve trick) If you've got MASM32... I think it's the same thing...

Mingw ld ought to work, as well as gcc. The "-L" switch you have to give gcc (if you're using a makefile - that's really strange!) may be a partial clue. That gives you "printf, etc.", right? Libraries for the Windows API may be somewhere around there. I dunno.

Best,
Frank

mark allyn

  • Guest
Re: Differences between NASM and MASM
« Reply #2 on: August 09, 2009, 03:34:00 PM »
Frank -

I can't thank you enough for taking time out to create this very detailed comparison and explanation.  It is very meaty; it explains a great deal.  I am still putting your pieces together.  Takes a beginner like me awhile to sort things out.

One of my fundamental confusions right from the get-go has been why MASM(32) had all these includes (to be fair, there is one "master include" that can include all the includes...), and they are necessary in order to get to the cruntime library for c function calls.   As you  know, none of this is required in NASM and I just couldn't understand why not.  The NASM approach is much simpler, produces the same effect, and is much much easier for the beginner to understand.  But I was bothered by this difference between the two assemblers.

To add to my confusion:  DrPaulCarter's wonderful to tut made it appear as if NASM actually had  to have a C calling function (he calls it a "driver") in order to get to the c runime lib.  In fact, on p. 18 of his tut he writes this:

"There are several advantages in using the C driver routine.  First, this lets the C system set up the program to run correctly in protected mode.  All the segments and their corresponding segment registers will be initialized by C.  The assembly code need not worry about any of this.  Secodly, the C library will also be available to be used by the assembly code."

So, when I was working thru Carter's tut I just kept assuming that this c driver thing was pretty much essential.  BUT, it isn't.  What blew me away was that I could go into MinGW, do the NASM assembler that I stuck in MinGW's bin directory, and then use gcc to do the link and that was that!  I was amazed.

Thanks again for your response.  It's a gem.

Mark Allyn

nobody

  • Guest
Re: Differences between NASM and MASM
« Reply #3 on: August 11, 2009, 10:58:24 AM »
> One of my fundamental confusions right from the get-go has been why MASM(32)
> had all these includes
[snip]
> The NASM approach is much simpler, produces the same effect, and is much much
> easier for the beginner to understand. But I was bothered by this difference between
> the two assemblers.

There are many differences between Masm and Nasm, but the "need for includes" isn't one of them. Masm can also live perfectly well without includes, and it supports the very same "approach" which Nasm usually uses.

> So an "includelib" macro would probably be possible in Nasm. I don't know what,
> if any, linkers besides MS's would honor the ".drectve" section. Pretty neat trick!

Other linkers I know which support this "trick" are OW's WLink and Pelle's PoLink.

japheth