;--- 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>,\        ;0 exports
        < IMAGEREL start_idata, SECTIONREL endof_idata >,\ ;1 imports
        <0,0>,<0,0>,\    ;2 resource, 3 exception
        <>,<>,<>,<>,\    ;4 security, 5 baserelocs, 6 debug, 7 architecture
        <>,<>,<>,<>,\    ;8 globalptr, 9 tls, 10 load_config, 11 bound_import
        <>,<>,<>,<> >}   ;12 iat, 13 delay_import, 14 com descriptor, 15 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