;------------------------------------------------------------------
;
;      Hello64World3 - copyright Jeremy Gordon 2005-6
;
;      SWITCHABLE Win32/64 "HELLO WORLD" WINDOWS GDI PROGRAM
;
;      Assemble using GoAsm /x64 Hello64World3 for 64-bit exe
;                     GoAsm /x86 Hello64World3 for 32-bit exe
;      Then link using:-
;      GoLink Hello64World3.obj user32.dll kernel32.dll gdi32.dll
;      GoLink automatically senses the type of file
;      (add -debug coff if you want to watch the program in the debugger)
;
;------------------------------------------------------------------
;
;      Using x86 causes:-
;      "x86" to be a defined word and identifiable using #if
;      All extended registers to be reduced to their 32-bit counterparts
;      Eg. RBX becomes EBX
;      ARG RAX causes PUSH EAX
;      ARG ADDR WNDCLASS causes a PUSH ADDR WNDCLASS
;      ARG 800h causes a PUSH 800h
;      ARG [hInst] causes PUSH [hInst]
;      ARG 'String' or ARG ADDR 'String' causes PUSH 'String' (push address of the string)
;      INVOKE works as STDCALL
;      ARG must be followed by an INVOKE before the next CALL or the next RET
;      FRAME...ENDF to work as in Win32
;
;      Using x64 causes:-
;      "x64" to be a defined word and identifiable using #if
;      ARG RAX (depending on position) causes MOV RCX or RDX or R8 or R9,RAX or PUSH RAX
;      ARG ADDR WNDCLASS (depending on position) becomes LEA RCX,WNDCLASS
;             or LEA RDX or R8 or R9 or PUSH ADDR WNDCLASS
;      ARG 800h causes a MOV RCX or RDX or R8 or R9,800h or PUSH 800h
;      ARG [hInst] causes a MOV RCX or RDX or R8 or R9,[hInst] or PUSH [hInst]
;      ARG 'String' or ARG ADDR 'String' causes MOV RCX or RDX or R8 or R9,ADDR 'String'
;             PUSH 'String' (put address of the string in register or on stack)
;      INVOKE works as FASTCALL, providing placeholders on stack for register params
;             using SUB RSP,20h before the call and ADD RSP,20h after it
;             it also corrects stack to allow for any PUSHed parameters
;             it also permits parameters to follow the called function as in "C"
;      ARG must be followed by an INVOKE before the next CALL or the next RET
;      FRAME...ENDF to work as in Win64
;
;------------------------------------------------------------------
;
RECT STRUCT
      left    DD ?
      top     DD ?
      right   DD ?
      bottom  DD ?
ENDS
;
;***************** structure to hold stuff from Windows on WM_PAINT (two different versions)
;
#if x64
PAINTSTRUCT STRUCT
hdc           DQ ?
fErase        DD ?
rcPaint       RECT
fRestore      DD ?
fIncUpdate    DD ?
rgbReserved   DB 32 DUP ?
              DD 0      ;padding to bring total size to 72 bytes
ENDS
#else
PAINTSTRUCT STRUCT
hdc           DD ?
fErase        DD ?
rcPaint       RECT
fRestore      DD ?
fIncUpdate    DD ?
rgbReserved   DB 32 DUP ?
ENDS
#endif
;
DATA SECTION
;
;***************** structure to hold message
#if x64
ALIGN 8               ;ensure 8-byte alignment for structure
MSG      DQ 0         ;+0 hWnd
         DD 0         ;+8 message
         DD 0         ;padding for next
  wParam DQ 0         ;+10 wParam
         DQ 0         ;+18 lParam
         DD 0         ;+20 time
         DD 0         ;+24 1st part of point structure
         DD 0         ;+28 2nd part of point structure
         DD 0         ;padding to bring total size to 48 bytes
#else
MSG      DD 0         ;+0 hWnd
         DD 0         ;+4 message
  wParam DD 0         ;+8 wParam
         DD 0         ;+C lParam
         DD 0         ;+10 time
         DD 0         ;+14 1st part of point structure
         DD 0         ;+18 2nd part of point structure
#endif
;
;***************** structure to send to RegisterClass holding data
#if x64
ALIGN 8               ;ensure 8-byte alignment for structure
WNDCLASS DD 1h+2h+40h ;+0 window class style (CS_VREDRAW+CS_HREDRAW+CS_CLASSDC)
         DD 0         ;padding for next
         DQ WndProc   ;+8 pointer to Window Procedure
         DD 0         ;+10 no. of extra bytes to allocate after structure
         DD 0         ;+14 no. of extra bytes to allocate after window instance
   hInst DQ 0         ;+18 handle to instance containing window procedure
         DQ 0         ;+20 handle to the class icon
 hCursor DQ 0         ;+28 handle to the class cursor
         DQ 6         ;+30 identifies the class background brush (6=COLOR_WINDOW+1)
         DQ 0         ;+38 pointer to resource name for class menu
         DQ WINDOW_CLASSNAME   ;+40 pointer to string for window class name
#else
WNDCLASS DD 1h+2h+40h ;+0 window class style (CS_VREDRAW+CS_HREDRAW+CS_CLASSDC)
         DD WndProc   ;+4 pointer to Window Procedure
         DD 0         ;+8 no. of extra bytes to allocate after structure
         DD 0         ;+C no. of extra bytes to allocate after window instance
   hInst DD 0         ;+10 handle to instance of this window class
         DD 0         ;+14 handle to the class icon
 hCursor DD 0         ;+18 handle to the class cursor
         DD 6         ;+1C identifies the class background brush (6=COLOR_WINDOW+1)
         DD 0         ;+20 pointer to resource name for class menu
         DD WINDOW_CLASSNAME      ;+24 pointer to string for window class name
#endif
;
;******************** Window message table 
;
;(In a real program this would deal with many more messages)
ALIGN 8               ;ensure 8-byte alignment for call (essential in 64-bits!)
MESSAGES DD 1h,  CREATE    ;the message value then the 32-bit code address of the label
         DD 2h,  DESTROY 
         DD 0Fh, PAINT   
;
;******************************************
;
ALIGN 8               ;ensure 8-byte alignment for string (essential in 64-bits!!)
WINDOW_CLASSNAME DB 'WC',0     ;string holding name of window class
;
;------------------------------------------------------------------
;
CODE SECTION
;
;*******************
CREATE:                 ;one of the few messages dealt with by this prog
XOR RAX,RAX             ;return zero to make window
RET
;
DESTROY:                ;one of the few messages dealt with by this prog
INVOKE PostQuitMessage,0       ;exit via the message loop
STC                     ;go to DefWindowProc too
RET
;
;**** This is the actual window procedure which works both for 32-bit and 64-bit assembly.
;the call to the code label found in the table is not made to CALL [EDX+ECX*8+4] since 
;all calls to addresses held in memory areas pointed to by registers or to addresses
;held in registers themselves are calls to 64-bit addresses in 64-bit assembly.
;But the table holds only 32-bit addresses.  So instead we extract, the 32-bit address in the
;table into ECX and then call RCX knowing that the high dword of RCX is zero.
;
WndProc:
FRAME hwnd,uMsg,wParam,lParam     ;establish stack frame and get parameters
;
MOV EAX,[uMsg]          ;get in eax message sent by Windows
MOV ECX,SIZEOF MESSAGES/8         ;get number of messages to look at
MOV EDX,ADDR MESSAGES
L2:
DEC ECX
JS >.notfound
CMP [EDX+ECX*8],EAX     ;see if its the correct message
JNZ L2                  ;no
MOV ECX,[EDX+ECX*8+4]
CALL RCX
JNC >.exit
.notfound
INVOKE DefWindowProcA,[hwnd],[uMsg],[wParam],[lParam]
.exit
RET
ENDF                    ;finish this stack frame
;
;**** and now a procedure, outside the FRAME, which must address
;**** the parameters held on the stack in the WndProc FRAME
;This paints an ellipse in the rectangle provided by Windows on the
;message WM_PAINT. This rectangle is the area which needs updating, eg
;on resizing or if the window is uncovered by another.  Painting is
;done using the device context provided by Windows.
;We set up LOCAL data here, specific to the windows message
;
PAINT:
USEDATA WndProc         ;use parameters sent to WndProc
USES RBX,RDI,RSI        ;save registers as required by Windows (not actually needed here)
LOCAL lpPaint:PAINTSTRUCT,hDC              ;establish local data
;
INVOKE BeginPaint,[hwnd],ADDR lpPaint      ;get in eax/rax the DC to use
MOV [hDC],RAX                              ;save it in local data
INVOKE Ellipse, [hDC],[lpPaint.rcPaint.left],   \
                      [lpPaint.rcPaint.top] ,   \
                      [lpPaint.rcPaint.right],  \
                      [lpPaint.rcPaint.bottom]
;
;       other paint procedures would go here
;
INVOKE EndPaint, [hwnd],ADDR lpPaint       ;give DC back to Windows
XOR RAX,RAX             ;return not carry and rax=0
RET
ENDU                    ;finish using parameters sent to WndProc
;
;*******************************************************************
START:
INVOKE GetModuleHandleA,0             ;get handle to the process
MOV [hInst],RAX         ;record it in data label hInst
INVOKE LoadCursorA,0,32512     ;get in eax, handle to IDC_ARROW (common arrow cursor)
MOV [hCursor],RAX       ;record it in data label hCursor (in WNDCLASS)
;********** now register the window class
INVOKE RegisterClassA,ADDR WNDCLASS   ;register the window class
;********** now create the window
ARG 0
ARG [hInst]
ARG 0
ARG 0                   ;owner=desktop
ARG 200D                ;height
ARG 320D                ;width
ARG 50D                 ;position y
ARG 50D                 ;position x
ARG 90000000h +0C00000h+40000h +80000h +20000h     +10000h
 ;(POPUP+VISIBLE)+CAPTION+SIZEBOX+SYSMENU+MINIMIZEBOX+MAXIMIZEBOX
ARG 'Hello 64 World window made by GoAsm'      ;window title
ARG ADDR WINDOW_CLASSNAME       ;window class name
ARG 0                   ;extended style
INVOKE CreateWindowExA  ;make window
;************************ now enter the main message loop
L1:
INVOKE GetMessageA,ADDR MSG,0,0,0
OR RAX,RAX              ;see if it is WM_QUIT
JZ >L2                  ;yes
INVOKE TranslateMessage,ADDR MSG
INVOKE DispatchMessageA,ADDR MSG
JMP L1                  ;after message dealt with, loop back for next one
L2:                     ;message was WM_QUIT
ARG [hInst]
ARG ADDR WINDOW_CLASSNAME
INVOKE UnregisterClassA   ;ensure class is removed
INVOKE ExitProcess,[wParam]    ;wParam=exit code
