0. Contents

1. About
2. Commandline Options
Options -0, -1, -2, ..., -10
Option -bin
Option -djgpp
Option -elf
Option -elf64
Option -eq
Option -Fd
Option -Fw
Option -FPi
Option -m
Option -mz
Option -nc
Option -nd, -nt
Option -nm
Option -win64
Option -Zd
Option -zf
Option -Zg
Option -Zi
Option -zlc, -zld, -zlf and zls
Option -Zm
Option -Zne
Option -zt
Option -Zv8
Option -zze
Option -zzs
3. Syntax Extensions
3.1 Directive INCBIN
3.2 FASTCALL Register Calling Convention
3.3 IDs enclosed in Back Quotes
3.4 Floating-Point Immediates in Instructions
3.5 Directive OPTION FIELDALIGN
3.6 Directive OPTION PROCALIGN
3.7 Directive OPTION MZ
3.8 Directive OPTION ELF
3.9 Directive OPTION WIN64
3.10 Directive OPTION FRAME
3.11 Directive OPTION RENAMEKEYWORD
3.12 Directive OPTION DLLIMPORT
3.13 Directives PUSHCONTEXT / POPCONTEXT ALIGNMENT
3.14 Directives .X64 and .X64p
3.15 Directive INVOKE in Win64
3.16 Attribute LABEL for first Macro Parameter
3.17 Member Argument for IF[N]DEF and .ERR[N]DEF
3.18 Initialization of Data Items of Type MMWORD/XMMWORD
3.19 Other Differences
a) Numeric constant __JWASM__
b) Operating System Argument for .MODEL
c) Accepted Parameters for IF[N]DEF Directive
d) Visibility of Procedures
4. Instruction Sets
5. Code Generation Differences
5.1 Forward References in Macro Expressions
6. Win64 Structured Exception Handling (SEH)
7. Errors and Warnings
8. Masm Bugs fixed in JWasm
9. Known Bugs and missing Features
10. License
Appendix A: Reserved Words
Appendix B: Source Samples

1. About

This document lists the differences between JWasm and Masm, as far as the user interface is concerned.

When Masm is mentioned, then usually Masm v8.00 is meant, unless stated otherwise. Masm v8.00 also was the first Masm version supporting 64-bit (ML64.EXE).

The Masm documentation itself can be found on numerous places in the web, in plain text, HTML, PDF and Windows Help format. However, it's usually the documentation that came whith Masm v6.1, hence is a bit outdated.

2. Commandline Options

Entering 'JWasm -?' or 'JWasm -h' will make JWasm display the options it understands. A lot of them exist in both Masm and JWasm, but some are valid for JWasm only; OTOH, a few options are missing in JWasm.

Options are usually entered via the command line. Additionally, when starting, JWasm will search for environment variable JWASM and handle it similar to the way Masm handles variable ML. Hence it is also possible to enter options via this method.

The options specific to JWasm - and also the options which are handled somewhat differently by JWasm compared to Masm - will be handled in the following chapters.

Options -0, -1, -2, ..., -10: Select Cpu

Option [0|1|..|10] selects cpu/instruction set. Most values correspond to cpu directives:

0.8086
1.186
2.286
3.386
4.486
5.586
6.686
7.686 and .MMX (P2)
8.686, .MMX and SSE instructions (P3)
9.686, .MMX, SSE and SSE2 instructions (P4)
10.x64 (x86-64 cpu)

Option -bin: Select Output Format Binary

Option -bin selects output format BINary. The output module's default file extension will be changed from .OBJ to .BIN. The contents of the file are just the raw data bytes emitted by the assembler, no header, relocations or symbol tables are generated.

The binary format is most useful for bootloaders or DOS COM files, but may be used to create any binary format. See sample Win32_5, that demonstrates how to use -bin for creating a Win32 application.

If a listing file is produced, a binary map will be added, which shows the file and memory layout of the image:

 
                                    .model tiny

                                    .data

00000000  0D0A48656C6C6F2C20    str1    db 13,10,"Hello, world!",13,10,'$'

00000000                            .code

                                    org 100h

00000100                        start:

00000100  B409                      mov ah, 09h
00000102  BA0000                    mov dx, offset str1
00000105  CD21                      int 21h
00000107  B8004C                    mov ax, 4c00h
0000010A  CD21                      int 21h

                                    end start


Binary Map:

Segment                  Pos(file)      VA  Size(fil) Size(mem)
---------------------------------------------------------------
_TEXT                           0      100         C         C
_DATA                           C      10C        12        12
---------------------------------------------------------------
                                                  1E        1E

Option -djgpp: Select Output Format for DJGPP (optional)

Option -djgpp selects Djgpp's variant of COFF as output format. Since it is rarely used nowadays, this option isn't activated in the precompiled binaries. See 9.3c how to enable it.

Option -elf: Select 32-bit Output Format ELF

Option -elf selects output format ELF. JWasm will produce an object module in 32-bit Elf format.

Use OPTION ELF to set values in the ELF header.

Option -elf64: Select 64-bit Output Format ELF

Option -elf64 selects output format ELF64. JWasm will produce an object module in 64-bit Elf format. Additionally, this option will set cpu to x86-64 and model to FLAT.

Use OPTION ELF to set values in the ELF header.

Option -eq: Suppress Error Messages on Screen

Option -eq will suppress displaying error messages on the screen. They are still written into an error file.

Option -Fd: Write Import Definitions

Option -Fd makes JWasm write import definitions in a format understood by Open Watcom's Wlink and JWlink. Such definitions will tell the linker how to resolve the external reference; no import library is needed. This option is only useful in conjunction with OPTION DLLIMPORT (chapter 3.12). Syntax is:
-Fd[=file_name]
If the optional <file_name> argument is given, the import definitions will be written into a file of this name. This is the only way for Open Watcom's Wlink to pass the information.

If JWlink is used, the <file_name> argument may be omitted. Then JWasm will write the import definitions directly into the object module's linker directive section (section ".drectve"). This works for output formats COFF and ELF only. See sample Win32_7 how to use JWasm and JWlink to create a Windows binary without import libs.

Option -Fw: Set Error File Name

Option -Fw will set the file name for warning and error messages. As default, these messages are written to a file with a name equal to the assembly source, but with extension .ERR. Syntax is:
-Fw file_name

Option -FPi: Activate Floating-Point Emulation

Option -FPi activates "inline FP instructions with emulation". This will make JWasm create fixups for floating-point instructions if code is 16bit. If supported by the linker or the OS, the FP instructions can then be replaced by calls to an FP emulator if no coprocessor exists.

Option -m: Select Memory Model

Option -m generates a line containing a .MODEL directive to select a memory model. Syntax is:
-m[t|s|m|c|l|h|f]
where the value behind 'm' means:
t = tiny
s = small
m = medium
c = compact
l = large
h = huge
f = flat
This option is ignored if a 64-bit output format (-win64 or -elf64) is active.

Option -mz: Select Output Format MZ

Option -mz selects output format MZ. This will write a binary in DOS MZ format. The module's default file extension will be changed from .OBJ to .EXE. All symbols in the module must resolve internally, no externals are allowed. Some values in the "MZ" header can be adjusted by directive OPTION MZ (see below).

Option -nc: Set Code Class Name

Option -nc sets the code segment's class name if simplified segment directives are used (default:CODE). Syntax is:
-nc=name
where <name> will be the code segment's class name.

Options -nd and -nt: Set DATA and CODE Segment Names

Options -nd and -nt will set the name of the data/code segments if simplified segment directives are used. Syntax is:
-nt=name_of_code
-nd=name_of_data
The default names are _TEXT for code and _DATA for data.

Option -nm: Set Module Name

Option -nm sets the module name. Syntax is:
-nm=module_name
The default value for <module_name> is the name of the source file without extension.

Option -win64: Select Output Format Win64

Option -win64 makes JWasm produce an object module in PE32+ format, the 64-bit format used for Win64 binaries.

Option -win64 will also set cpu to x86-64, model to FLAT and default calling convention to FASTCALL. This is to make JWasm compatible with Masm64 (ML64.EXE).

With OPTION WIN64, parameters specific to Win64 may be set.

Option -Zd: Emit Line Number Debugging Info

Option -Zd generates line number debug information for OMF and COFF output format. For other formats, this option is ignored. Line number information allows a debugger to trace the binary on the source code level. Debuggers which have been verified to work with this option: MS CodeView, CDB, WinDbg, MS VC++ EE 2008, OW WD/WDW, PellesC IDE.

Option -zf: Select FASTCALL Type

Option -zf selects the FASTCALL calling convention type for 16- and 32-bit code. Syntax is:
-zf[0|1]
The default value 0 is MS VC style, while value 1 activates the Open Watcom fastcall type.

Option -Zg: Masm-compatible Code Generation

Option -Zg makes JWasm try an exact copy of Masm's code generation, which results in the following changes:

Option -Zi: Emit Symbolic Debugging Info

Option -Zi generates symbolic debugging info in CodeView V4 style for OMF and COFF output format. For other formats, this option is accepted, but ignored. Debuggers which have been verified to work with this option: MS CodeView, CDB, WinDbg, MS VC++ EE 2008, OW WD/WDW, PellesC IDE.

Options -zlc, -zld, -zlf and -zls: Reduce Size of Output

Options -zlc, -zld, -zlf and -zls do reduce size of the output module. They might be useful if lots of - small - modules are to be assembled and put into a static library. Also, the OMF coment records written if -zlc or -zld is NOT set may not be accepted by all linkers.

Option -zlc:
suppresses writing OMF coment records about data in Code segments. This may help a disassembler to produce nicer listings.
Option -zld:
suppresses writing an OMF coment record for each Code segment telling the linker that far calls to targets in the same segments should be optimized. This is more or less a feature for 16-bit code only.
Option -zlf:
suppresses the @file entry in the COFF symbol table. This entry is usually not needed and hence a - pretty small - amount of space is saved.
Option -zls:
suppresses the auxiliary entries for sections in the COFF symbol table. These entries may not be needed in all cases and thus a little space is saved.

Option -Zm: Enable Masm v5 Compatibility

Option -Zm (or setting OPTION M510) will do:
- set OPTION OLDSTRUCTS
- set OPTION DOTNAME
- set OPTION SETIF2:TRUE
- set OPTION OFFSET:SEGMENT (if no model is set)
- set OPTION NOSCOPED (if no model with language specifier is set)
- allow to define data items behind code labels
- allow "invalid" use of REP/REPE/REPNE instruction prefixes
Other Masm v5.1 compatibility options aren't implemented yet.

Option -Zne: Disable JWasm Syntax Extensions

Option -Zne will disable syntax extensions which aren't supported by Masm. Currently these are:
- directive INCBIN
- FASTCALL calling convention
- IDs enclosed in backquotes
- floating-point immediate operands in instructions
- directive OPTION FIELDALIGN
- directive OPTION PROCALIGN
- directive OPTION MZ
- directive OPTION ELF
- directive OPTION WIN64
- directive OPTION RENAMEKEYWORD
- directive OPTION DLLIMPORT
- directives PUSHCONTEXT / POPCONTEXT ALIGNMENT
- attribute LABEL for first macro parameter
- member argument for IF[N]DEF and .ERR[N]DEF directives
- integer initializer values for items of type [X]MMWORD
- name argument for .DATA, .DATA? and .CONST directives
- forward references in arguments for INVOKEd procedures
- overrides inside square brackets for base/index registers

Option -zt: Set Name Decoration for STDCALL

Option -zt will fine-tune name decoration for STDCALL symbols. Syntax is:
-zt[0|1|2]
where value 0 will disable name decoration, value 1 will just add an underscore prefix and value 2 - which is the default - will emit full STDCALL name decoration as expected by most linkers.

Option -zt0 will make object modules compatible to ALINK + Win32.lib.

Option -Zv8: Enable Masm v8 Procedure Visibility

Option -Zv8 changes handling of procedure visibility to the way done by Masm v8+. See Visibility of Procedures for details.

Option -zze: Disable Export Name Decoration

Option -zze suppresses name decoration for procedures with the EXPORT attribute (exported name only).

Option -zzs: Avoid Wlink COFF Incompatibility

Option -zzs is kind of a workaround for a Wlink incompatibility. It's useful to be set if 1) the source module has a starting address, 2) output format is COFF AND 3) Wlink is to be used as linker.

3. Syntax Extensions

This chapter describes the syntax extensions of JWasm compared to Masm v8.

3.1 Directive INCBIN

This directive allows to include the contents of a file into the object module. Syntax is
INCBIN filename [, starting offset[, max size]]
<filename> should be enclosed in <> or double quotes.

3.2 FASTCALL Register Calling Convention

In 16- and 32-bit mode, one may use either the Microsoft or the Watcom register calling convention. It's selected by option -zf.

The Microsoft FASTCALL convention uses registers (E)CX and (E)DX for the first 2 parameters which are small enough to fit into a register.

The Open Watcom fastcall convention uses up to four registers ( E/AX, E/DX, E/BX, E/CX ).

In 64-bit mode, FASTCALL means the standard Windows 64 ABI if option -win64 was given and it is the default then. For -elf64, there is currently no FASTCALL support.

3.3 IDs enclosed in Back Quotes

IDs can be enclosed in back quotes (`) and thus they can contain characters not allowed in "normal" IDs.

3.4 Floating-Point Immediates in Instructions

Floating-point immediate values are accepted as instruction operands. As default, the type is a REAL4, which has a magnitude of 32 bits:
 
        mov eax, 1.0

With type coercion, it's also possible to define a 64-bit "double", although it's probably useful in 64-bit code only:

 
        mov rax, real8 ptr 1.0

Additionally, operators LOW32 and HIGH32 accept a floating-point constant as argument. In this case, the constant is assumed to have format REAL8. Thus it's possible to pass a double constant directly as a procedure argument in 32-bit code:

 
       push HIGH32 1.0
       push LOW32 1.0
       call WorkWithReal8Value

3.5 Directive OPTION FIELDALIGN

OPTION FIELDALIGN sets the default value for structure alignment. The default value is 1 or the value set by cmdline switch -Zp. Syntax is:
OPTION FIELDALIGN: [1|2|4|8|16|32]
The current default value can be saved/restored with directives PUSHCONTEXT / POPCONTEXT ALIGNMENT,

3.6 Directive OPTION PROCALIGN

With OPTION PROCALIGN parameter it's possible to automatically align procedures. Syntax is:
OPTION PROCALIGN: [1|2|4|8|16|32]
The default value is 1. The current value can be saved/restored with directives PUSHCONTEXT / POPCONTEXT ALIGNMENT,

Example:

    .386
    .model flat, stdcall
    option PROCALIGN:16
    .code

proc1 PROC
    ret
proc1 endp

proc2 PROC
    ret
proc2 endp

    end

The listing shows that start address of proc2 is aligned to 16 (=10h):

00000000                        proc1 PROC
00000000                            ret
00000000  C3                *   retn
00000001                        proc1 endp

00000010                        proc2 PROC
00000010                            ret
00000010  C3                *   retn
00000011                        proc2 endp

Note: to ensure that the procedures are aligned in the final binary as it is supposed by the OPTION PROCALIGN value, the alignment of the current code segment must be at least the value of OPTION PROCALIGN.

3.7 Directive OPTION MZ

Directive OPTION MZ allows to fine-tune the values written to the MZ header if output format MZ (see -mz cmdline option) is selected. For other output formats, this option has no effect. The syntax for the directive is:
OPTION MZ:[start_fixups][:header_align][:heap_min][:heap_max]
The parameters are:

start_fixupsoffset within the header where segment fixups will start. The size of the header will always be at least this value, even if there are no fixups at all. Default - and minimum - value is 1Eh.
header_alignalignment of the header (including segment fixups). Value must be a power of 2, 10h is the default and minimum.
heap_minthe additional space (in paragraphs) which is needed by the binary to run. Default is the total of the sizes of the uninitialized BSS and STACK segments.
heap_maxspace (in paragraphs) which the binary would like to have. Default is FFFFh.

3.8 Directive OPTION ELF

Directive OPTION ELF allows to fine-tune the values written to the ELF header if output format ELF (see -elf or -elf64) is selected. For other output formats, this option has no effect. The syntax for the directive is:
OPTION ELF:osabi
The only argument <osabi> will be copied to the ELF header field EI_OSABI. It's a numeric constant, and according to the elf specs some valid values are:

0ELFOSABI_NONE unspecified
1ELFOSABI_HPUX HP-UX
2ELFOSABI_NETBSD NetBSD
3ELFOSABI_LINUX Linux, default
6ELFOSABI_SOLARISSun Solaris
7ELFOSABI_AIX IBM AIX
9ELFOSABI_FREEBSDFreeBSD
12ELFOSABI_OPENBSDOpenBSD

3.9 Directive OPTION WIN64

Directive OPTION WIN64 allows to set parameters for the Win64 output format if this format (see -win64 cmdline option) is selected. For other output formats, this option has no effect. The syntax for the directive is:
OPTION WIN64: store_register_params
accepted values for store_register_params are:
- 0: Nothing is done, the "home locations" (also sometimes called "shadow space") of the first 4 register parameters are uninitialized. This is the default setting.
- 1: register contents of the PROC's first 4 parameters (RCX, RDX, R8 and R9 ) will be copied to the "home locations" within a PROC's prologue.

3.10 Directive OPTION FRAME

This option affects 64-bit only. It allows to make JWasm automatically generate prologues and epilogues for procedures with the FRAME attribute. Thus the code complies to the rules of Win64 SEH (Structured Exception Handling). Syntax is
OPTION FRAME:<AUTO | NOAUTO>
AUTO will enable this feature, NOAUTO (which is default) disables it.

The unwind information which is generated is "complete", that is, it contains the ".endprologue" pseudo-op already. To allow to save all non-volatile registers in the prologue, the "USES" phrase is more capable in this mode and will accept XMM registers to be saved and restored.

3.11 Directive OPTION RENAMEKEYWORD

This option allows to rename a keyword, so it can be used under a different name. Syntax:
OPTION RENAMEKEYWORD:<current_name>=new_name
current_name is the current name of the keyword and must be enclosed in angle brackets. new_name must be a valid identifier. If a keyword is to be renamed, it should be done at the beginning of the source, and a keyword shouldn't be renamed multiple times.

3.12 Directive OPTION DLLIMPORT

a) Using OPTION DLLIMPORT

This option makes the assembler assume that all PROTOs that follow this directive represent functions located in a dll. Syntax:
OPTION DLLIMPORT:<dll_name> | NONE
<dll_name> must be enclosed in angle brackets. Argument NONE will switch back to the default mode.

The effects of setting this options are subtle and useful only for MS Windows applications: if the function described by the prototype is called via INVOKE, slightly more efficient code than normal is generated, because the function's address in the IAT is used. Example:

INVOKE GetModuleHandle, NULL
code generation with OPTION DLLIMPORT:
 
        push NULL
        call DWORD PTR [_imp__GetModuleHandle@4]
code generation without OPTION DLLIMPORT:
 
        push NULL
        call _GetModuleHandle@4
        ...
    _GetModuleHandle@4:
        jmp DWORD PTR [_imp__GetModuleHandle@4]  ;stub added by the linker

b) Using OPTION DLLIMPORT in Conjunction with -Fd Switch

Optionally, by using cmdline option -Fd, JWasm will write the import information received through OPTION DLLIMPORT lines to either a file or directly into the object module (COFF and ELF only). Example:
 
        .386
        .model flat,stdcall
        option dllimport:<kernel32>
    GetModuleHandleA proto :dword
    ExitProcess proto :dword
        option dllimport:none
        .code
        invoke GetModuleHandleA, 0
        invoke ExitProcess, 0
        end
JWasm -coff -Fd=lnk.rsp sample.asm
After the assembly step, file lnk.rsp will contain:
import '_ExitProcess@4' kernel32.ExitProcess
import '_GetModuleHandleA@4' kernel32.GetModuleHandleA
Both Open Watcom's Wlink and JWlink will be able to directly use this information and hence, as a result, no further Windows import libraries are needed in the link step:
Wlink format windows pe file sample.obj @lnk.rsp
JWlink may even go one step further - it's able to read import definitions contained in a COFF or ELF module's linker directive section ( named ".drectve" ). Therefore one can omit the filename argument for -Fd. Sample Win32_7 demonstrates the usage.

3.13 Directives PUSHCONTEXT / POPCONTEXT ALIGNMENT

The PUSHCONTEXT / POPCONTEXT directives understand new qualifier ALIGNMENT, which saves/restores current values of FIELDALIGN and PROCALIGN options.

3.14 Directives .X64 and .X64p

These directives select a 64-bit (x86-64) cpu. In contrast to .X64, .X64p will allow to use privileged instructions.

The .X64 directive isn't needed usually, because for output formats WIN64 (see -win64) and ELF64 (see -elf64), .X64 is the default.

The .X64p directive is useful for mixed-model binaries or system software (see example DOS64 ).

When the cpu is set to 64-bit, the SEGMENT directive accepts a new 'size' value: USE64. It tells the assembler that this segment's offset is 64-bit wide and uses 64-bit instructions.

The SYSCALL calling convention is renamed to SYSCALL_ when 64-bit is on, because there exists a SYSCALL instruction mnemonic in this mode.

3.15 Directive INVOKE in Win64

The 64-bit version of Masm doesn't support INVOKE anymore. JWasm still does, but please be aware of some restrictions:
- in theory, using INVOKE requires the FRAME attribute for PROC. It will work without FRAME, but Win64 SEH won't be happy with it then.
- the implementation in 64-bit is very simple: for each INVOKE register RSP is reduced by the space required for the arguments, then the call is issued and finally register RSP is restored.
- there is no additional check that the stack is aligned to 16 byte. The PROC's FRAME attribute ensures that the stack is correctly aligned after the prologue is done. However, it's the programmers responsibility that the stack is still aligned when the code generated by INVOKE starts.

3.16 Attribute LABEL for first Macro Parameter

The LABEL attribute for the first macro parameter allows access to a label which is assigned to the macro. Syntax is:

<macro_name> MACRO <param_name>:LABEL [,<param_name>[, ...]]

The LABEL attribute is accepted for the first parameter only. A macro with such a parameter can be invoked in the following way:

<label> <macro_name> [<argument>, ...]

Example:

 
    foo macro lbl:LABEL, first, second
    lbl  db first
         dw second
    endm

    .data

    data1 foo 1,1000
    data2 foo 2,2000

3.17 Member Argument for IF[N]DEF and .ERR[N]DEF Directives

Since v2.07, JWasm's implementation of IF[N]DEF - and .ERR[N]DEF - will additionally accept a struct member as argument. This syntax requires a fully qualified name:

 
      IFDEF <struct_name>.<member_name>

3.18 Initialization of Data Items with Type MMWORD/XMMWORD

For data items of types MMWORD or XMMWORD, JWasm will accept integer values for initialization:

 
    vmm1  MMWORD  1122334455667788h
    vxmm1 XMMWORD 112233445566778899AABBCCDDEEFFh

Masm will accept just floating-point initializers for data items of type [X]MMWORD. It's even worse, since floating-point initializers are silently ignored for data items with sizes != 4, 8 and 10; since XMMWORD has size 16, it's impossible to initialize such an item directly. JWasm copies this Masm behavior, but to allow to initialize a XMMWORD with a floating-point value, one may use type coercion:

 
    vxmm1 XMMWORD real4 ptr 1.0   ;bytes 4-15 will be 0
    vxmm2 XMMWORD real8 ptr 1.0   ;bytes 8-15 will be 0

Variants that work in both JWasm and Masm, and also allow to initialize the full XMMWORD are:

 
    vxmm1 LABEL XMMWORD
      real4 1.0, 2.0, 3.0, 4.0
    vxmm2 LABEL XMMWORD
      real8 1.0, 2.0

3.19 Other Differences

a) Numeric constant __JWASM__

__JWASM__ is a predefined symbol, its value is the current JWasm version * 100, that is, for v1.9 the value is 190. The predefined text equate @Version won't contain JWasm's version, for compatibility reasons it has value <800> (since v2.06, previously the value was <615>).

b) Operating System Argument for .MODEL

The .MODEL directive has an optional "operating system" argument. Masm accepts value OS_DOS only, JWasm accepts values OS_DOS and OS_OS2. This setting will affect the generated code of directives .STARTUP and .EXIT for 16-bit memory models.

c) Accepted Parameters for IF[N]DEF Directive

Masm's IF[N]DEF directive accepts user-defined symbols and registers, but fails for instructions, directives and other reserved words. JWasm's IF[N}DEF implementation accepts those symbols as well. OTOH, JWasm is a bit more picky and will display a warning if more than one item is found behind the directive - Masm just takes the first and silently skips the rest.

Also see Member Argument for IF[N]DEF and .ERR[N]DEF Directives.

d) Visibility of Procedures

When a PROTO or EXTERNDEF directive for a symbol is located in a module before a matching PROC directive, the visibility of this Procedure ( "public" vs "private", or "external" vs. "static" ) is handled differently in Masm v6 or 7 and Masm v8 or newer:

Since Masm v8, a PROTO or EXTERNDEF for a symbol which is later defined as a PROC will make the procedure public, no matter what a possible visibility attribute of the PROC itself - or the default one set with OPTION PROC - is telling.

OTOH, with Masm v6/7, both the visibility attribute of the PROC directive and the current default setting of OPTION PROC will affect ths symbol's visibility.

                Masm6 Masm8 JWasm JWasm+Zv8
      -------------------------------------
      On,E,P            x             x
      On,E,Pn           x             x
      On,E,Pp     x     x      x      x
      Op,E,P      x     x      x      x
      Op,E,Pn     x     x             x
      Op,E,Pp     x     x      x      x

      On = OPTION PROC:PRIVATE
      Op = OPTION PROC:PUBLIC
      E  = PROTO or EXTERNDEF before PROC
      P  = PROC without visibility attribute
      Pn = PROC with PRIVATE visibility attribute
      Pp = PROC with PUBLIC visibility attribute
      x  = procedure will be public
As default, JWasm more or less copies the Masm v6/7 behavior. The difference is that an explicite visibility attribute behind PROC has the highest priority for JWasm. However, since v2.04, there's an additional cmdline option -Zv8 which will make JWasm behave like Masm v8+.

It should be noted that without a PROTO/EXTERNDEF before PROC, there are no differences between Masm v6, v8 and JWasm, and the -Zv8 switch also has no effect then.

4. Instruction Sets

JWasm supports all instructions sets supported by Masm v8. These are

- the instructions implemented by 8086, 80186, 80286, 80386, 80486.
- the Pentium and Pentium Pro instructions.
- the MMX and K3D instruction set extensions.
- the SSE, SSE2, SSE3 and SSSE3 instruction set extensions.
- the x86-64 64-bit instruction set (implemented by ML64).
Since JWasm v2.01, instruction sets SSE4.1 and SSE4.2 are supported. ( With Masm, these instructions require Masm v9 ).

Since JWasm v2.06, instruction set AVX is supported. ( With Masm, these instructions require Masm v10 ).

5. Code Generation Differences

JWasm might generate slightly different code than Masm on some occasions. Commandline option -Zg should eliminate most of these differences. However, some differences are due to fixed Masm bugs (see below), in which case option -Zg won't have any effect.

For a few instructions, the encoding differs between Masm versions.

Example:

    cmp al,dl

is encoded 38 D0 in Masm v6, but 3A C2 in Masm v8. In such cases, JWasm will prefer to copy the encoding of Masm v8.

5.1 Forward References in Macro Expressions

Like Masm, JWasm usually evaluates expressions in preprocessor directives during the first pass only. However, due to different jump optimization strategies of Masm and JWasm, the results may differ. This is very unlikely to impose a problem, but it is mentioned here for completeness. An example (found in README.TXT of Masm v6.14):

 
      Label1:
           JMP Label2
      Label2:

      REPEAT Label2 - Label1
           INC AX
      ENDM

Masm will - incorrectly - repeat the loop 10 times, although the result of expression Label2 - Label1 is 2 only. OTOH, JWasm will repeat the loop 2 times only, because it's using an "optimistic" strategy concerning forward references.

6. Win64 Structured Exception Handling (SEH)

SEH in Win64 differs significantly from the implementation in Win32. It's very well possible to ignore Win64 SEH for assembly. However, if an assembly routine wants to comply to these rules, a thorough understanding of the Win64 ABI is necessary. Masm ( the 64-bit version ) supplies some "primitives" for SEH support (.ALLOCSTACK, .PUSHREG, .SAVEREG, ...), along with a new FRAME attribute for the PROC directive. These features are also supported by JWasm. See sample Win64_3 how the "primitives" are to be used for SEH support.

The big disadvantage is that using the FRAME keyword in Masm "disables" most of the other high level features combined with PROC (function parameters, locals and registers saved with USES) because no function prologues and epilogues are generated anymore. Additionally, the implementation in some Masm versions seems to be a bit buggy. Because of this and to ease the usage of SEH in Win64 there is a new directive implemented in JWasm:

 
        OPTION FRAME:AUTO

If this option is set, JWasm will create Win64 SEH-compatible prologues and epilogues. If the option is off, JWasm will behave Masm-compatible, that is, FRAME found in a PROC directive will disable automatic prologue/epilogue generation. See sample Win64_3e how this option is supposed to be used.

As for the PROC syntax: The Masm documentation states that FRAME can be used in combination with USES and procedure parameters and must be located behind all parameters. However, this syntax isn't accepted by any Masm version. The only syntax which Masm will accept without being confused is FRAME as the one and only parameter for PROC. Therefore JWasm doesn't follow the Masm documentation in this point: the optional FRAME keyword is expected *before* the procedure parameters. The syntax in JWasm is:

 
    procname PROC [public] FRAME[:exc_handler] [USES <reglist>] [parameters]

The SEH "primitives" will generate some additional data in segments .pdata and .xdata. This data is somewhat hidden, but JWasm will display the corresponding data definitions in the listing if option -Sg is set.

7. Errors and Warnings

The warning and error numbers emitted by JWasm differ from Masm's. However, the texts of the messages are pretty much identical. There are a few messages which deserve a more detailed explanation:
- 'Text macro was used before definition': this is a warning only. However, using text macros before they have been defined will force JWasm to do a full second pass, which increases assembly time.

- 'IF[n]DEF expects a plain symbol as argument': this is a warning. Masm accepts any expression as argument for directives [ELSE]IF[N]DEF, but the result probably isn't always what has been expected.

- 'Size not specified, assuming: <type>': this is a warning. <type> may be BYTE, WORD or DWORD. The message may occur if an immediate value is written to an untyped memory reference:

 
       mov [ebx], 1

JWasm makes a guess and displays the warning, while Masm will display an error in such cases.

8. Masm bugs fixed in JWasm

- the infamous "invoke" bug: using invoke with variables of type BYTE (or WORD in 32bit code) causes bad code to be generated in Masm.
- PROTOs contained twice in the source caused an EXTDEF entry to be generated in the object module.
- "TYPE xmm0" will return 10 in Masm v6 and v7, JWasm returns 16, same as Masm v8.
- a nested structure might cause a GPF in Masm if the embedded STRUCT's starting offset has to be adjusted due to alignment.
- defining huge arrays in Masm is very slow and might even cause a deadlock if COFF has been selected as output format.
- for Masm v6 and v7, if an array > 64 kB is defined and output format OMF is selected, the array's size will be mod 0x10000 only.
- Masm doesn't flag invalid numbers in struct/array initializer strings.
- if an ALIAS is defined somewhere in the source and the symbol table is listed, a 'General Failure' error occurs in Masm if output format is OMF.
- Type "coerces" for DWORD data items defined in a 32bit segment are ignored by Masm, i.e., "dd far16 ptr <symbol>" will generate a near32 fixup instead of a far16 one.
- if the ALIGN directive has to add 5 bytes in 32bit code segments, Masm includes an "add eax,0" opcode, which isn't a no-op because flags are modified.
- silent truncation of immediate constants: Masm v6 and v7 will accept line "mov [word_variable],12345h" without error.
- preprocessed output with option -EP may erroneously contain text macros and macro function calls if the macros are located in the initialization string of a structured variable.
- Masm generates wrong code if a conditional jump is coupled with a type coercion which modifies offset magnitude. Examples: "jz near32 ptr ..." in 16bit code or "jz near16 ptr ..." in 32bit code).
- if the arguments given to Masm end with an option which expects a parameter (i.e. "ml -c -Fo"), a 'General Failure' may occur.
- floating-point data items in Masm can be followed by any suffix (example: REAL4 1.0foo, 2.0bar). JWasm won't accept this.
- If a local is defined inside a macro, Masm will create a unique name for it. The name is constructed by using '??' as prefix, followed by a hexadecimal number with 4 digits. There is no check for overflow, however, so if the total of locals in all macros exceeds 65536, strange errors will occur.
It's slightly dangerous to fix old Masm bugs, since some code might work only if the bugs exists. So no, JWasm won't achieve 100% Masm compatibility.

9. Known Bugs and missing Features

a) Bugs which are known but not fixed yet:

There are currently no known bugs.

b) Features which aren't implemented yet:

- directives PAGE, TITLE, SUBTITLE, SUBTTL. the directives are ignored and a warning (level 3) is displayed.
- the following parameters of the OPTION directive:
- OLDMACROS
- EXPR16
- READONLY
- optional parameter NONUNIQUE for structures is ignored.
- commandline option -Zd for ELF output format.
- commandline option -Zi for ELF output format.

c) Features which aren't active in the precompiled binaries:

- support for Djgpp's variant of COFF. [ -DDJGPP_SUPPORT ]
You'll have to recompile the sources with the appropriate macros defined ( see the values within [] above ) to enable these features.

d) Missing features which most likely won't be implemented:

- %OUT directive
- syntax "mm(n)" and "xmm(n)" (supported by Masm v6 and v7 only)

10. License

This manual was written by Andreas Grech ( aka Japheth ).

It may be redistributed as long as it is free of charge.

Appendix A: JWasm Reserved Words

Reserved Words are case-insensitive. Besides the items listed below all instruction mnemonics are also Reserved Words.

Registers 16- and 32-bit Modes

8-bit registers AL CL DL BL AH CH DH BH
16-bit registers AX CX DX BX SP BP SI DI
32-bit registers EAX ECX EDX EBX ESP EBP ESI EDI
Segment registers ES CS SS DS FS GS
Floating-point registers ST ST(1) ST(2) ST(3) ST(4) ST(5) ST(6) ST(7)
MMX registers MM0 MM1 MM2 MM3 MM4 MM5 MM6 MM7
SSE registers XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7
AVX registers YMM0 YMM1 YMM2 YMM3 YMM4 YMM5 YMM6 YMM7
Control registers CR0 CR2 CR3 CR4
Debug registers DR0 DR1 DR2 DR3 DR6 DR7
Trace registers TR3 TR4 TR5 TR6 TR7

Additional Registers in 64-bit Mode

8-bit registers SPL BPL SIL DIL
R8B R9B R10B R11B R12B R13B R14B R15B
16-bit registers R8W R9W R10W R11W R12W R13W R14W R15W
32-bit registers R8D R9D R10D R11D R12D R13D R14D R15D
64-bit registers RAX RCX RDX RBX RSP RBP RSI RDI
R8 R9 R10 R11 R12 R13 R14 R15
SSE registers XMM8 XMM9 XMM10 XMM11 XMM12 XMM13 XMM14 XMM15
AVX registers YMM8 YMM9 YMM10 YMM11 YMM12 YMM13 YMM14 YMM15
Control registers CR8

Types

BYTE
SBYTE
WORD
SWORD
DWORD
SDWORD
REAL4
FWORD
QWORD
SQWORD
REAL8
TBYTE
REAL10
OWORD
YMMWORD
NEAR
FAR
NEAR16
NEAR32
FAR16
FAR32
MMWORD
XMMWORD

Unary Operators

.TYPE
HIGH
HIGH32
HIGHWORD
IMAGEREL[1]
LENGTH
LENGTHOF
LOW
LOW32
LOWWORD
LROFFSET
MASK
OFFSET
OPATTR
SECTIONREL[1]
SEG
SHORT
SIZE
SIZEOF
THIS
TYPE
WIDTH
[1]: not for OMF output format.

Binary Operators

EQ
NE
GE
GT
LE
LT
MOD
PTR
DUP

Directives

.8086
.186
.286
.286C
.286P
.386
.386C
.386P
.486
.486P
.586
.586P
.686
.686P
.K3D
.MMX
.XMM
.X64
.X64P
.8087
.287
.387
.NO87
.CREF
.LFCOND
.LIST
.LISTALL
.LISTIF
.NOCREF
.NOLIST
.NOLISTIF
.SFCOND
.TFCOND
.XCREF
.XLIST
PAGE
SUBTITLE
SUBTTL
TITLE
.LISTMACRO
.LISTMACROALL
.NOLISTMACRO
.XALL
.LALL
.SALL
.ALPHA
.DOSSEG
.SEQ
DOSSEG
.CODE
.STACK
.DATA
.DATA?
.FARDATA
.FARDATA?
.CONST
.IF
.REPEAT
.WHILE
.BREAK
.CONTINUE
.ELSE
.ELSEIF
.ENDIF
.ENDW
.UNTIL
.UNTILCXZ
.EXIT
.STARTUP
.MODEL
.RADIX
.SAFESEH
.ERR
.ERR1
.ERR2
.ERRE
.ERRNZ
.ERRDIF
.ERRDIFI
.ERRIDN
.ERRIDNI
.ERRB
.ERRNB
.ERRDEF
.ERRNDEF
COMMENT
IF
IFE
IF1
IF2
IFDIF
IFDIFI
IFIDN
IFIDNI
IFB
IFNB
IFDEF
IFNDEF
ELSE
ELSEIF
ELSEIFE
ELSEIF1
ELSEIF2
ELSEIFDIF
ELSEIFDIFI
ELSEIFIDN
ELSEIFIDNI
ELSEIFB
ELSEIFNB
ELSEIFDEF
ELSEIFNDEF
ENDIF
FOR
FORC
IRP
IRPC
REPEAT
REPT
WHILE
MACRO
EXITM
ENDM
GOTO
PURGE
INCLUDE
TEXTEQU
CATSTR
SUBSTR
INSTR
SIZESTR
DB
DW
DD
DF
DQ
DT
STRUC
STRUCT
UNION
TYPEDEF
RECORD
COMM
EXTERN
EXTRN
EXTERNDEF
PUBLIC
PROTO
PROC
ENDP
LOCAL
LABEL
INVOKE
ORG
ALIGN
EVEN
SEGMENT
ENDS
GROUP
ASSUME
ALIAS
ECHO
END
EQU
INCBIN
INCLUDELIB
NAME
OPTION
POPCONTEXT
PUSHCONTEXT

Additional Directives in 64-bit Mode

.ALLOCSTACK
.ENDPROLOG
.PUSHFRAME
.PUSHREG
.SAVEREG
.SAVEXMM128
.SETFRAME

Other Reserved Words

ADDR
FLAT
VARARG
FRAME[1]
C
SYSCALL[2]
STDCALL
PASCAL
FORTRAN
BASIC
FASTCALL
[1]: in 64-bit mode only.
[2]: in 64-bit, calling convention SYSCALL is renamed to SYSCALL_, since in this mode there exists a SYSCALL instruction.

Appendix B: Source Samples

Win64_3 - SEH Support in Win64
Win64_3e - SEH Support in Win64 (JWasm specific)
DOS64 - Switch to Long Mode and Back
Win32_5 - Create a Win32 Binary with -bin
Win32_7 - Usage of OPTION DLLIMPORT and -Fd Switch

Win64_3 - SEH Support in Win64


;--- This sample shows how to use SEH primitives. It doesn't use hll
;--- directives. Thus this source can be assembled by both JWasm 
;--- and Masm64.
;---
;--- to assemble enter:
;---   JWasm -win64 Win64_3.asm
;--- or:
;---   ml64 -c Win64_3.asm
;---
;--- to link the binary enter:
;---   Link Win64_3.obj

    option casemap:none

    includelib kernel32.lib
    includelib user32.lib

HINSTANCE typedef QWORD
HWND      typedef QWORD
HMENU     typedef QWORD
HICON     typedef QWORD
HBRUSH    typedef QWORD
HCURSOR   typedef QWORD
WPARAM    typedef QWORD
LPARAM    typedef QWORD
LPSTR     typedef QWORD
LPVOID    typedef QWORD
UINT      typedef DWORD

NULL           equ 0
WS_OVERLAPPEDWINDOW equ 0CF0000h
CW_USEDEFAULT  equ 80000000h
SW_SHOWDEFAULT equ 10
SW_SHOWNORMAL  equ 1
IDC_ARROW      equ 32512
IDI_APPLICATION equ 32512
WM_DESTROY     equ 2
CS_VREDRAW     equ 1
CS_HREDRAW     equ 2
COLOR_WINDOW   equ 5

proto_WNDPROC typedef proto :HWND,:QWORD,:WPARAM,:LPARAM
WNDPROC typedef ptr proto_WNDPROC

WNDCLASSEXA struct 8
cbSize          DWORD   ?
style           DWORD   ?
lpfnWndProc     WNDPROC ?
cbClsExtra      DWORD   ?
cbWndExtra      DWORD   ?
hInstance       HINSTANCE ?
hIcon           HICON   ?
hCursor         HCURSOR ?
hbrBackground   HBRUSH  ?
lpszMenuName    LPSTR   ?
lpszClassName   LPSTR   ?
hIconSm         HICON   ?
WNDCLASSEXA ends

POINT   struct
x   SDWORD  ?
y   SDWORD  ?
POINT   ends

MSG struct 8
hwnd    HWND    ?
message DWORD   ?
wParam  WPARAM  ?
lParam  LPARAM  ?
time    DWORD   ?
pt      POINT   <>
MSG ends

GetModuleHandleA proto :LPSTR
GetCommandLineA  proto
ExitProcess      proto :UINT
LoadIconA        proto :HINSTANCE, :LPSTR
LoadCursorA      proto :HINSTANCE, :LPSTR
RegisterClassExA proto :ptr WNDCLASSEXA
CreateWindowExA  proto :DWORD, :LPSTR, :LPSTR, :DWORD, :SDWORD, :SDWORD, :SDWORD, :SDWORD, :HWND, :HMENU, :HINSTANCE, :LPVOID
ShowWindow       proto :HWND, :SDWORD
UpdateWindow     proto :HWND
GetMessageA      proto :ptr MSG, :HWND, :SDWORD, :SDWORD
TranslateMessage proto :ptr MSG
DispatchMessageA proto :ptr MSG
PostQuitMessage  proto :SDWORD
DefWindowProcA   proto :HWND, :UINT, :WPARAM, :LPARAM

;WinMain proto :HINSTANCE, :HINSTANCE, :LPSTR, :UINT

    .data

ClassName db "SimpleWinClass",0
AppName  db "Our First Window",0

    .data?

hInstance HINSTANCE ?
CommandLine LPSTR ?

    .code

WinMainCRTStartup proc FRAME
    push   rbp
    .pushreg rbp
    mov    rbp,rsp
    .setframe rbp, 0
    .endprolog

    sub    rsp,32
    mov    ecx,NULL
    call   GetModuleHandleA
    mov    hInstance, rax
    call   GetCommandLineA
    mov    CommandLine, rax
    mov    rcx, hInstance
    mov    rdx, NULL
    mov    r8, CommandLine
    mov    r9d, SW_SHOWDEFAULT
    call   WinMain
    mov    ecx, eax
    call   ExitProcess
    align 4
WinMainCRTStartup endp

WinMain proc FRAME

    push  rbp
    .pushreg rbp
    mov   rbp,rsp
    .setframe rbp, 0
    .endprolog
    sub   rsp, sizeof WNDCLASSEXA + sizeof MSG + sizeof HWND + 12*8

hInst     equ <[rbp+10h]>
hPrevInst equ <[rbp+18h]>
CmdLine   equ <[rbp+20h]>
CmdShow   equ <[rbp+28h]>

wc   equ <[rbp - sizeof WNDCLASSEXA].WNDCLASSEXA>
msg  equ <[rbp - sizeof WNDCLASSEXA - sizeof MSG].MSG>
hwnd equ <[rbp - sizeof WNDCLASSEXA - sizeof MSG - sizeof HWND]>

    mov   hInst, rcx  ;store param1 in shadow space

    mov   wc.cbSize, SIZEOF WNDCLASSEXA
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
;   mov   rax, OFFSET WndProc  ;using LEA is preferable
    lea   rax, [WndProc]
    mov   wc.lpfnWndProc, rax
    mov   wc.cbClsExtra, NULL
    mov   wc.cbWndExtra, NULL
    mov   wc.hInstance, rcx
    mov   wc.hbrBackground, COLOR_WINDOW+1
    mov   wc.lpszMenuName, NULL
;    mov   rax, OFFSET ClassName  ;using LEA is preferable
    lea   rax, [ClassName]
    mov   wc.lpszClassName, rax
    mov   ecx, NULL
    mov   edx, IDI_APPLICATION
    call  LoadIconA
    mov   wc.hIcon, rax
    mov   wc.hIconSm, rax
    mov   ecx, NULL
    mov   edx, IDC_ARROW
    call  LoadCursorA
    mov   wc.hCursor,rax
    lea   rcx, wc
    call  RegisterClassExA
    mov   ecx, NULL
    lea   rdx, [ClassName]
    lea   r8, [AppName]
    mov   r9d, WS_OVERLAPPEDWINDOW
    mov   dword ptr [rsp+4*8], CW_USEDEFAULT
    mov   dword ptr [rsp+5*8], CW_USEDEFAULT
    mov   dword ptr [rsp+6*8], CW_USEDEFAULT
    mov   dword ptr [rsp+7*8], CW_USEDEFAULT
    mov   qword ptr [rsp+8*8], NULL
    mov   qword ptr [rsp+9*8], NULL
    mov   rax, hInst
    mov   [rsp+10*8], rax
    mov   qword ptr [rsp+11*8], NULL
    call  CreateWindowExA
    mov   hwnd,rax
    mov   rcx, hwnd
    mov   edx, SW_SHOWNORMAL
    call  ShowWindow
    mov   rcx, hwnd
    call  UpdateWindow
;--- message loop
@@:
        lea rcx, msg
        mov rdx, NULL
        mov r8, 0
        mov r9, 0
        call GetMessageA
        and rax, rax
        jz @F
        lea rcx, msg
        call TranslateMessage
        lea rcx, msg
        call DispatchMessageA
        jmp @B
@@:
    mov   rax, msg.wParam
    add   rsp, sizeof WNDCLASSEXA + sizeof MSG + sizeof HWND + 12*8
    pop   rbp
    ret
    align 4
WinMain endp

WndProc proc FRAME

    sub   rsp, 4*8
    .allocstack 4*8
    .endprolog

    cmp edx, WM_DESTROY
    jnz @F
    mov ecx, NULL
    call PostQuitMessage
    xor rax,rax
    jmp exit
@@:
    call DefWindowProcA
exit:
    add rsp, 4*8
    ret
    align 4
WndProc endp

end

Win64_3e - SEH Support in Win64 (JWasm specific)


;--- SEH support in Win64. Unlike Win64_3, 
;--- this version uses hll directives, so it cannot be assembled
;--- with Masm64. Also, OPTION FRAME:AUTO is used.
;---
;--- to create the binary enter:
;---   JWasm -win64 Win64_3e.asm
;---   Link Win64_3e.obj

    option casemap:none
    option frame:auto    ;generate SEH-compatible prologues and epilogues

    includelib kernel32.lib
    includelib user32.lib

HINSTANCE typedef QWORD
HWND      typedef QWORD
HMENU     typedef QWORD
HICON     typedef QWORD
HBRUSH    typedef QWORD
HCURSOR   typedef QWORD
WPARAM    typedef QWORD
LPARAM    typedef QWORD
LPSTR     typedef QWORD
LPVOID    typedef QWORD
UINT      typedef DWORD

NULL           equ 0
WS_OVERLAPPEDWINDOW equ 0CF0000h
CW_USEDEFAULT  equ 80000000h
SW_SHOWDEFAULT equ 10
SW_SHOWNORMAL  equ 1
IDC_ARROW      equ 32512
IDI_APPLICATION equ 32512
WM_DESTROY     equ 2
CS_VREDRAW     equ 1
CS_HREDRAW     equ 2
COLOR_WINDOW   equ 5

proto_WNDPROC typedef proto :HWND,:QWORD,:WPARAM,:LPARAM
WNDPROC typedef ptr proto_WNDPROC

WNDCLASSEXA struct 8
cbSize          DWORD   ?
style           DWORD   ?
lpfnWndProc     WNDPROC ?
cbClsExtra      DWORD   ?
cbWndExtra      DWORD   ?
hInstance       HINSTANCE ?
hIcon           HICON   ?
hCursor         HCURSOR ?
hbrBackground   HBRUSH  ?
lpszMenuName    LPSTR   ?
lpszClassName   LPSTR   ?
hIconSm         HICON   ?
WNDCLASSEXA ends

POINT   struct
x   SDWORD  ?
y   SDWORD  ?
POINT   ends

MSG struct 8
hwnd    HWND    ?
message DWORD   ?
wParam  WPARAM  ?
lParam  LPARAM  ?
time    DWORD   ?
pt      POINT   <>
MSG ends

GetModuleHandleA proto :LPSTR
GetCommandLineA  proto
ExitProcess      proto :UINT
LoadIconA        proto :HINSTANCE, :LPSTR
LoadCursorA      proto :HINSTANCE, :LPSTR
RegisterClassExA proto :ptr WNDCLASSEXA
CreateWindowExA  proto :DWORD, :LPSTR, :LPSTR, :DWORD, :SDWORD, :SDWORD, :SDWORD, :SDWORD, :HWND, :HMENU, :HINSTANCE, :LPVOID
ShowWindow       proto :HWND, :SDWORD
UpdateWindow     proto :HWND
GetMessageA      proto :ptr MSG, :HWND, :SDWORD, :SDWORD
TranslateMessage proto :ptr MSG
DispatchMessageA proto :ptr MSG
PostQuitMessage  proto :SDWORD
DefWindowProcA   proto :HWND, :UINT, :WPARAM, :LPARAM

WinMain proto :HINSTANCE, :HINSTANCE, :LPSTR, :UINT

    .data

ClassName db "SimpleWinClass",0
AppName  db "Our First Window",0

    .data?

hInstance HINSTANCE ?
CommandLine LPSTR ?

    .code

WinMainCRTStartup proc FRAME

    invoke GetModuleHandleA, NULL
    mov    hInstance, rax
    invoke GetCommandLineA
    mov    CommandLine, rax
    invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
    invoke ExitProcess, eax

WinMainCRTStartup endp

WinMain proc FRAME hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:UINT

    local wc:WNDCLASSEXA
    local msg:MSG
    local hwnd:HWND

    mov   hInst, rcx
    mov   wc.cbSize, SIZEOF WNDCLASSEXA
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    lea   rax, [WndProc]
    mov   wc.lpfnWndProc, rax
    mov   wc.cbClsExtra, NULL
    mov   wc.cbWndExtra, NULL
    mov   wc.hInstance, rcx
    mov   wc.hbrBackground, COLOR_WINDOW+1
    mov   wc.lpszMenuName, NULL
    lea   rax, [ClassName]
    mov   wc.lpszClassName, rax
    invoke LoadIconA, NULL, IDI_APPLICATION
    mov   wc.hIcon, rax
    mov   wc.hIconSm, rax
    invoke LoadCursorA, NULL, IDC_ARROW
    mov   wc.hCursor,rax
    invoke RegisterClassExA, addr wc
    invoke CreateWindowExA, NULL, ADDR ClassName, ADDR AppName,\
           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,\
           CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, NULL, NULL,\
           hInst, NULL
    mov   hwnd,rax
    invoke ShowWindow, hwnd, SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
    .while (1)
        invoke GetMessageA, ADDR msg, NULL, 0, 0
        .break .if (!rax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessageA, ADDR msg
    .endw
    mov   rax, msg.wParam
    ret
WinMain endp

WndProc proc FRAME hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .if ( edx == WM_DESTROY )
        invoke PostQuitMessage, NULL
        xor rax,rax
    .else
        invoke DefWindowProcA, rcx, edx, r8, r9
    .endif
    ret
WndProc endp

end WinMainCRTStartup

DOS64 - Switch to Long Mode and Back


;--- DOS program which switches to long-mode and back.
;--- Note: requires at least JWasm v2.
;--- Also: needs a 64bit cpu in real-mode to run.
;--- Parts of the source are based on samples supplied by
;--- sinsi and Tomasz Grysztar in the FASM forum.
;--- To create the binary enter:
;---  JWasm -mz DOS64.asm

    .x64p

;--- 16bit start/exit code

_TEXT16 segment use16 para public 'CODE'

    assume ds:_TEXT16
    assume es:_TEXT16

GDTR label fword        ; Global Descriptors Table Register
    dw 4*8-1            ; limit of GDT (size minus one)
    dd offset GDT       ; linear address of GDT
IDTR label fword        ; Interrupt Descriptor Table Register
    dw 256*16-1         ; limit of IDT (size minus one)
    dd 0                ; linear address of IDT
nullidt label fword
    dw 3FFh
    dd 0
  
    align 8
GDT dq 0                    ; null desciptor
    dw 0FFFFh,0,9A00h,0AFh  ; 64-bit code descriptor
    dw 0FFFFh,0,9A00h,000h  ; compatibility mode code descriptor
    dw 0FFFFh,0,9200h,000h  ; compatibility mode data descriptor

wPICMask dw 0   ; variable to save/restore PIC masks

start16:
    push cs
    pop ds
    mov ax,cs
    movzx eax,ax
    shl eax,4
    add dword ptr [GDTR+2], eax ; convert offset to linear address
    mov word ptr [GDT+2*8+2], ax
    mov word ptr [GDT+3*8+2], ax
    shr eax,16
    mov byte ptr [GDT+2*8+4], al
    mov byte ptr [GDT+3*8+4], al

    mov ax,ss
    mov dx,es
    sub ax,dx
    mov bx,sp
    shr bx,4
    add bx,ax
    mov ah,4Ah
    int 21h         ; free unused memory
    push cs
    pop es
    mov ax,ss
    mov dx,cs
    sub ax,dx
    shl ax,4
    add ax,sp
    push ds
    pop ss
    mov sp,ax       ; make a TINY model, CS=SS=DS=ES

    smsw ax
    test al,1
    jz @F
    mov dx,offset err1
    mov ah,9
    int 21h
    mov ah,4Ch
    int 21h
err1 db "Mode is V86. Need REAL mode to switch to LONG mode!",13,10,'$'
@@:
    xor edx,edx
    mov eax,80000001h   ; test if long-mode is supported
    cpuid
    test edx,20000000h
    jnz @F
    mov dx,offset err2
    mov ah,9
    int 21h
    mov ah,4Ch
    int 21h
err2 db "No 64bit cpu detected.",13,10,'$'
@@:
    mov bx,1000h
    mov ah,48h
    int 21h
    jnc @F
    mov dx,offset err3
    mov ah,9
    int 21h
    mov ah,4Ch
    int 21h
err3 db "Out of memory",13,10,'$'
@@:
    add ax,100h-1   ; align to page boundary
    mov al,0
    mov es,ax

;--- setup page directories and tables

    sub di,di
    mov cx,4096
    sub eax,eax
    rep stosd       ; clear 4 pages

    sub di,di
    mov ax,es
    movzx eax,ax
    shl eax,4
    mov cr3,eax             ; load page-map level-4 base

    lea edx, [eax+5000h]
    mov dword ptr [IDTR+2], edx

    or eax,111b
    add eax, 1000h
    mov es:[di+0000h],eax   ; first PDP table
    add eax, 1000h
    mov es:[di+1000h],eax   ; first page directory
    add eax, 1000h
    mov es:[di+2000h],eax   ; first page table
    mov di,3000h            ; address of first page table
    mov eax,0 + 111b
    mov cx,256              ; number of pages to map (1 MB)
@@:
    stosd
    add di,4
    add eax,1000h
    loop @B

;--- setup ebx/rbx with linear address of _TEXT

    mov bx,_TEXT
    movzx ebx,bx
    shl ebx,4
    add [llg], ebx

;--- create IDT

    mov di,5000h
    mov cx,32
    mov edx, offset exception
    add edx, ebx
make_exc_gates:
    mov eax,edx
    stosw
    mov ax,8
    stosw
    mov ax,8E00h
    stosd
    xor eax, eax
    stosd
    stosd
    add edx,4
    loop make_exc_gates
    mov cx,256-32
make_int_gates:
    mov eax,offset interrupt
    add eax, ebx
    stosw
    mov ax,8
    stosw
    mov ax,8E00h
    stosd
    xor eax, eax
    stosd
    stosd
    loop make_int_gates

    mov di,5000h
    mov eax, ebx
    add eax, offset clock
    mov es:[di+80h*16+0],ax ; set IRQ 0 handler
    shr eax,16
    mov es:[di+80h*16+6],ax

    mov eax, ebx
    add eax, offset keyboard
    mov es:[di+81h*16+0],ax ; set IRQ 1 handler
    shr eax,16
    mov es:[di+81h*16+6],ax

;--- clear NT flag

    pushf
    pop ax
    and ah,0BFh
    push ax
    popf

;--- reprogram PIC: change IRQ 0-7 to INT 80h-87h, IRQ 8-15 to INT 88h-8Fh

    cli
    in al,0A1h
    mov ah,al
    in al,21h
    mov [wPICMask],ax
    mov al,10001b       ; begin PIC 1 initialization
    out 20h,al
    mov al,10001b       ; begin PIC 2 initialization
    out 0A0h,al
    mov al,80h          ; IRQ 0-7: interrupts 80h-87h
    out 21h,al
    mov al,88h          ; IRQ 8-15: interrupts 88h-8Fh
    out 0A1h,al
    mov al,100b         ; slave connected to IRQ2
    out 21h,al
    mov al,2
    out 0A1h,al
    mov al,1            ; Intel environment, manual EOI
    out 21h,al
    out 0A1h,al
    in al,21h
    mov al,11111100b    ; enable only clock and keyboard IRQ
    out 21h,al
    in al,0A1h
    mov al,11111111b
    out 0A1h,al

    mov eax,cr4
    or eax,1 shl 5
    mov cr4,eax         ; enable physical-address extensions (PAE)

    mov ecx,0C0000080h  ; EFER MSR

    rdmsr
    or eax,1 shl 8      ; enable long mode
    wrmsr

    lgdt [GDTR]
    lidt [IDTR]

    mov cx,ss
    movzx ecx,cx        ; get base of SS
    shl ecx,4
    add ecx, esp

    mov eax,cr0
    or eax,80000001h
    mov cr0,eax         ; enable paging + pmode

    db 66h, 0EAh        ; jmp 0008:oooooooo
llg dd offset long_start
    dw 8

;--- switch back to real-mode and exit

backtoreal:
    mov eax,cr0
    and eax,7FFFFFFFh   ; disable paging
    mov cr0,eax
    mov ecx,0C0000080h  ; EFER MSR
    rdmsr
    and ah,not 1h       ; disable long mode (EFER.LME=0)
    wrmsr
    mov ax,24
    mov ss,ax
    mov ds,ax
    mov es,ax
    mov eax,cr0
    and al,0FEh
    mov cr0, eax        ; back to real mode
    db 0eah
    dw $+4
    dw _TEXT16
    mov ax,STACK
    mov ss, ax
    mov sp,4096
    push cs
    pop ds
    lidt [nullidt]
    mov eax,cr4
    and al,not 20h
    mov cr4,eax         ; disable physical-address extensions

    mov al,10001b       ; begin PIC 1 initialization
    out 20h,al
    mov al,10001b       ; begin PIC 2 initialization
    out 0A0h,al
    mov al,08h          ; IRQ 0-7: back to ints 8h-Fh
    out 21h,al
    mov al,70h          ; IRQ 8-15: back to ints 70h-77h
    out 0A1h,al
    mov al,100b         ; slave connected to IRQ2
    out 21h,al
    mov al,2
    out 0A1h,al
    mov al,1            ; Intel environment, manual EOI
    out 21h,al
    out 0A1h,al
    in al,21h
    mov ax,[wPICMask]   ; restore PIC masks
    out 21h,al
    mov al,ah
    out 0A1h,al
    sti
    mov ax,4c00h
    int 21h

_TEXT16 ends

;--- here's the 64bit code segment.
;--- since 64bit code is always flat but the DOS mz format is segmented,
;--- there are restrictions, because the assembler doesn't know the
;--- linear address where the 64bit segment will be loaded:
;--- + direct addressing with constants isn't possible (mov [0B8000h],rax)
;---   since the rip-relative address will be calculated wrong.
;--- + 64bit offsets (mov rax, offset <var>) must be adjusted by the linear
;---   address where the 64bit segment was loaded (is in rbx).
;---
;--- rbx must preserve linear address of _TEXT

_TEXT segment para use64 public 'CODE'

    assume ds:FLAT, es:FLAT

long_start:

    xor eax,eax
    mov ss,eax
    mov esp,ecx
    sti             ; now interrupts can be used
    call WriteStrX
    db "Hello 64bit",10,0
nextcmd:
    mov r8b,0       ; r8b will be filled by the keyboard irq routine
nocmd:
    cmp r8b,0
    jz nocmd
    cmp r8b,1       ; ESC?
    jz esc_pressed
    cmp r8b,13h     ; 'r'?
    jz r_pressed
    call WriteStrX
    db "unknown key ",0
    mov al,r8b
    call WriteB
    call WriteStrX
    db 10,0
    jmp nextcmd

;--- 'r' key: display some register contents

r_pressed:
    call WriteStrX
    db 10,"cr0=",0
    mov rax,cr0
    call WriteQW
    call WriteStrX
    db 10,"cr2=",0
    mov rax,cr2
    call WriteQW
    call WriteStrX
    db 10,"cr3=",0
    mov rax,cr3
    call WriteQW
    call WriteStrX
    db 10,"cr4=",0
    mov rax,cr4
    call WriteQW
    call WriteStrX
    db 10,"cr8=",0
    mov rax,cr8
    call WriteQW
    call WriteStrX
    db 10,0
    jmp nextcmd

;--- ESC: back to real-mode

esc_pressed:
    jmp [bv]
bv  label fword
    dd offset backtoreal
    dw 16

;--- screen output helpers

;--- scroll screen up one line
;--- rsi = linear address start of last line
;--- rbp = linear address of BIOS area (0x400)
scroll_screen:
    cld
    mov rdi,rsi
    movzx rax,word ptr [rbp+4Ah]
    push rax
    lea rsi, [rsi+2*rax]
    mov CL, [rbp+84h]
    mul cl
    mov rcx,rax
    rep movsw
    pop rcx
    mov ax,0720h
    rep stosw
    ret

WriteChr:
    push rbp
    push rdi
    push rsi
    push rbx
    push rcx
    push rdx
    push rax
    mov rdi,0B8000h
    mov rbp,400h
    cmp BYTE ptr [rbp+63h],0B4h
    jnz @F
    xor DI,DI
@@:
    movzx rbx, WORD PTR [rbp+4Eh]
    add rdi, rbx
    movzx rbx, BYTE PTR [rbp+62h]
    mov rsi, rdi
    movzx rcx, BYTE PTR [rbx*2+rbp+50h+1] ;ROW
    movzx rax, WORD PTR [rbp+4Ah]
    mul rcx
    movzx rdx, BYTE PTR [rbx*2+rbp+50h]  ;COL
    add rax, rdx
    mov DH,CL
    lea rdi, [rdi+rax*2]
    mov AL, [rsp]
    cmp AL, 10
    jz newline
    mov [rdi], AL
    mov byte ptr [rdi+1], 07
    inc DL
    cmp DL, BYTE PTR [rbp+4Ah]
    jb @F
newline:
    mov DL, 00
    inc DH
    cmp DH, BYTE PTR [rbp+84h]
    jbe @F
    dec DH
    call scroll_screen
@@:
    mov [rbx*2+rbp+50h],DX
    pop rax
    pop rdx
    pop rcx
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    ret

WriteStr:   ;write string in rdx
    push rsi
    mov rsi, rdx
    cld
@@:
    lodsb
    and al,al
    jz @F
    call WriteChr
    jmp @B
@@:
    pop rsi
    ret

WriteStrX:  ;write string at rip
    push rsi
    mov rsi, [rsp+8]
    cld
@@:
    lodsb
    and al,al
    jz @F
    call WriteChr
    jmp @B
@@:
    mov [rsp+8],rsi
    pop rsi
    ret

WriteQW:        ;write QWord in rax
    push rax
    shr rax,32
    call WriteDW
    pop rax
WriteDW:
    push rax
    shr rax,16
    call WriteW
    pop rax
WriteW:
    push rax
    shr rax,8
    call WriteB
    pop rax
WriteB:     ;write Byte in al
    push rax
    shr rax,4
    call WriteNb
    pop rax
WriteNb:
    and al,0Fh
    add al,'0'
    cmp al,'9'
    jbe @F
    add al,7
@@:
    jmp WriteChr

;--- exception handler

exception:
excno = 0
    repeat 32
    push excno
    jmp @F
    excno = excno+1
    endm
@@:
    call WriteStrX
    db 10,"Exception ",0
    pop rax
    call WriteB
    call WriteStrX
    db " errcode=",0
    mov rax,[rsp+0]
    call WriteQW
    call WriteStrX
    db " rip=",0
    mov rax,[rsp+8]
    call WriteQW
    call WriteStrX
    db 10,0
@@:
    jmp $

;--- clock and keyboard interrupts

clock:
    push rbp
    mov rbp,400h
    inc dword ptr [rbp+6Ch]
    pop rbp
interrupt:              ; handler for all other interrupts
    push rax
    mov al,20h
    out 20h,al
    pop rax
    iretq

keyboard:
    push rax
    in al,60h
    test al,80h
    jnz @F
    mov r8b, al
@@:
    in al,61h           ; give finishing information
    out 61h,al          ; to keyboard...
    mov al,20h
    out 20h,al          ; ...and interrupt controller
    pop rax
    iretq

_TEXT ends

;--- 4k stack, used in both modes

STACK segment use16 para stack 'STACK'
    db 4096 dup (?)
STACK ends

    end start16

Win32_5 - Create a Win32 Binary with -bin


;--- Win32 "hello world" console application.
;--- Uses JWasm's bin output format, so no linker needed.
;--- assemble: JWasm -bin -Fo Win32_5.exe Win32_5.ASM

    .386
    option casemap:none

    .nolist
    include winnt.inc   ;include PE image definitions
    .list

STD_OUTPUT_HANDLE equ -11

IMAGEBASE equ 400000h

PEHDR segment dword FLAT

;--- define the DOS "MZ" header

    org IMAGEBASE

    IMAGE_DOS_HEADER <"ZM", 80h, 1, 0,4,0,-1,0,200h,0,0,0,0,0,<0>,0,0,<0>,IMAGEREL PEHdr>

    db 0Eh         ;push cs
    db 1Fh         ;pop ds
    db 0BAh,0Eh,0  ;mov dx,text
    db 0B4h,09h    ;mov ah,9
    db 0CDh,21h    ;int 21h
    db 0B8h,01h,4Ch;mov ax,4c01h
    db 0CDh,21h    ;int 21h
    db "This program cannot be run in DOS mode",13,10,'$'

    org IMAGEBASE+80h

;--- define the Win32 "PE" header

PEHdr label byte
    db "PE",0,0
    IMAGE_FILE_HEADER <IMAGE_FILE_MACHINE_I386, num_sections, 0, 0, 0, sizeof IMAGE_OPTIONAL_HEADER32,
        IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_LOCAL_SYMS_STRIPPED>

    IMAGE_OPTIONAL_HEADER32 { 10Bh, ;magic
        6,0,                        ;linker major, minor
        1000h,1000h,0,              ;sizeof code, initialized data, uninitialized data
        IMAGEREL mainCRTStartup,    ;entry point
        IMAGEREL start_text, IMAGEREL start_rdata,  ;baseof code, data
        IMAGEBASE,    ;imagebase
        1000h,200h,   ;section alignment, file alignment
        4,0,          ;OS major, minor
        0,0,          ;Image major, minor
        4,0,          ;Subsys major, minor
        0,            ;win32 version
        3000h,        ;sizeof image
        1000h,        ;sizeof header
        0,            ;checksum
        IMAGE_SUBSYSTEM_WINDOWS_CUI,
        0,            ;dll characteristics
        100000h,1000h,;stack res,com
        100000h,1000h,;heap res, com
        0,            ;loader flags
        16,           ;number of directories
        <<0,0>,       ;exports
        < IMAGEREL start_idata, SECTIONREL endof_idata >, ;imports
        <0,0>,<0,0>,     ;resource, exception
        <>,<>,<>,<>,     ;security, baserelocs, debug, architecture
        <>,<>,<>,<>,     ;globalptr, tls, load_config, bound_import
        <>,<>,<>,<>>}    ;iat, delay_import, com descriptor, reserved

;--- define the section table

sectiontable label byte
    IMAGE_SECTION_HEADER <".text", <sizeof_text>, IMAGEREL start_text, sizeof_text,
        200h, 0, 0, 0, 0, 060000020h >
    IMAGE_SECTION_HEADER <".rdata", <SECTIONREL endof_idata + sizeof_const>, IMAGEREL start_rdata, SECTIONREL endof_idata + sizeof_const,
        400h, 0, 0, 0, 0, 040000040h >
num_sections equ ( $ -  sectiontable ) / sizeof IMAGE_SECTION_HEADER

    org IMAGEBASE+200h   ;forces physical size of header to 200h and sets VA to 400200h

PEHDR ends

;--- the ALIGNx segments are needed because
;--- section alignment and file alignment are different

ALIGN1 segment dword public FLAT 'DATA'
    org 0E00h   ; change pc to RVA 1000h
ALIGN1 ends

_TEXT segment dword public FLAT 'CODE'
_TEXT ends

ALIGN2 segment dword public FLAT 'DATA'
    org 0E00h   ; change pc to RVA 2000h
ALIGN2 ends

_IDATA segment dword public FLAT 'DATA'
start_rdata label byte
start_idata label byte
;--- import descriptors go here
_IDATA ends
_IDATA$1 segment dword public FLAT 'DATA'
    IMAGE_IMPORT_DESCRIPTOR <<0>,0,0,0,0>
;--- ILT entries go here
_IDATA$1 ends
_IDATA$2 segment dword public FLAT 'DATA'
    dd 0    ;--- end of last ILT
;--- IAT entries go here
_IDATA$2 ends
_IDATA$3 segment dword public FLAT 'DATA'
    dd 0    ;--- end of last IAT
;--- import name strings go here
_IDATA$3 ends
_IDATA$4 segment dword public FLAT 'DATA'
endof_idata equ $
_IDATA$4 ends

CONST segment dword public FLAT 'DATA'
start_const label byte
CONST ends

DefineImpDll macro name
_IDATA segment
    IMAGE_IMPORT_DESCRIPTOR <<IMAGEREL name&ILT>,0,0,IMAGEREL name, IMAGEREL name&IAT>
_IDATA ends
_IDATA$1 segment
ifdef ImportDefined
    dd 0  ;terminate previous ILT
endif
name&ILT label dword
_IDATA$1 ends
_IDATA$2 segment
ifdef ImportDefined
    dd 0  ;terminate previous IAT
endif
name&IAT label dword
_IDATA$2 ends
_IDATA$3 segment
name db @CatStr(!",name, !"),0
    align 4
_IDATA$3 ends
ImportDefined equ 1
    endm

DefineImport macro name
_IDATA$1 segment
    dd IMAGEREL n&name
_IDATA$1 ends
_IDATA$2 segment
lp&name typedef ptr pr&name
name    lp&name IMAGEREL n&name
_IDATA$2 ends
_IDATA$3 segment
n&name dw 0
    db @CatStr(!",name, !"),0
    align 4
_IDATA$3 ends
    endm

prWriteConsoleA typedef proto stdcall :dword, :dword, :dword, :dword, :dword
prGetStdHandle  typedef proto stdcall :dword
prExitProcess   typedef proto stdcall :dword

    DefineImpDll kernel32
    DefineImport ExitProcess
    DefineImport WriteConsoleA
    DefineImport GetStdHandle

if 0 ;if further dlls are to be imported
prMessageBoxA   typedef proto stdcall :dword, :dword, :dword, :dword

    DefineImpDll user32
    DefineImport MessageBoxA
endif

CONST segment

string  db 13,10,"hello, world.",13,10

sizeof_const equ $ - start_const

CONST ends

_TEXT segment

    assume ds:FLAT,es:FLAT

start_text label near

;--- start of program

main proc

local dwWritten:dword
local hConsole:dword

    invoke  GetStdHandle, STD_OUTPUT_HANDLE
    mov     hConsole,eax

    invoke  WriteConsoleA, hConsole, addr string, sizeof string, addr dwWritten, 0

    xor     eax,eax
    ret
main endp

;--- entry

mainCRTStartup proc c

    invoke  main
    invoke  ExitProcess, eax

mainCRTStartup endp

sizeof_text equ $ - start_text

    org 200h    ;align size of _TEXT to next 512 byte boundary

_TEXT ends

    end

Win32_7 - Usage of OPTION DLLIMPORT and -Fd Switch


;--- Win32_7 - Shows how to use OPTION DLLIMPORT and switch -Fd.
;---           No import libraries are needed in the link step.
;---
;--- assemble: JWasm -coff -Fd Win32_7.ASM
;--- link:     JWlink format windows pe f Win32_7.OBJ

    .386
    .model FLAT, stdcall
    option casemap:none

STD_OUTPUT_HANDLE equ -11

   option dllimport:<kernel32>
WriteConsoleA proto :dword, :dword, :dword, :dword, :dword
GetStdHandle  proto :dword
ExitProcess   proto :dword
   option dllimport:<user32>
MessageBoxA   proto :dword, :dword, :dword, :dword
   option dllimport:<none>

    .const

msg db 13,10,"hello, world.",13,10
    db 0

    .code

main proc

local   written:dword

    invoke  GetStdHandle, STD_OUTPUT_HANDLE
    mov ebx, eax
    invoke  WriteConsoleA, ebx, addr msg, sizeof msg,
                addr written, 0
    invoke  MessageBoxA, 0, addr msg, 0, 0
    ret

main endp

;--- entry

start:

    invoke  main
    invoke  ExitProcess, 0

    end start