;------------------------------------------------------------------
;
;      Hello64World2 - copyright Jeremy Gordon 2005-6
;
;      "HELLO WORLD" WINDOWS GDI PROGRAM - for 64 bits
;
;      Assemble using GoAsm /x64 Hello64World2
;
;      Then link using:-
;      GoLink Hello64World2.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)
;
;------------------------------------------------------------------
;
;***************** structure to hold stuff from Windows on WM_PAINT (64-bit version)
PAINTSTRUCT STRUCT
            DQ 0      ;+0 hDC
            DD 0      ;+8 fErase
      left  DD 0      ;+C  left   ) 
       top  DD 0      ;+10 top    ) RECT
     right  DD 0      ;+14 right  )
    bottom  DD 0      ;+18 bottom )
            DD 0      ;+1C fRestore
            DD 0      ;+20 fIncUpdate
            DB 32 DUP 0   ;+24 rgbReserved
            DD 0      ;padding to bring total size to 72 bytes
ENDS
;
DATA SECTION
;
ALIGN 8               ;ensure all the following data elements are 8-byte aligned
hDC   DQ 0            ;to keep the handle of the device context
PS PAINTSTRUCT 
;***************** structure to hold message
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
;***************** structure to send to RegisterClass holding data
WNDCLASS DD 1h+2h+40h ;+0 window class style (CS_VREDRAW+CS_HREDRAW+CS_CLASSDC)
         DD 0         ;padding for next
         DQ WndProcTable       ;+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
;******************** Window message table 
;          (in a real program this would deal with many more messages)
MESSAGES DD (ENDOF_MESSAGES-$-4)/8      ;=number to be done
         DD  1h,CREATE,2h,DESTROY,0Fh,PAINT
ENDOF_MESSAGES:           ;label used to work out how many messages
;******************************************
;
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 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.
PAINT:
PUSH RDI,RBX            ;save used non-volatile registers
MOV RDI,RCX             ;save hWnd for later use
LEA RBX,PS              ;keep paintstruct in rbx  
ARG RBX                 ;send it as a parameter
ARG RDI                 ;hWnd
INVOKE BeginPaint       ;get device context to use, initialise paint
MOV [hDC],RAX
;***************** use rectangle sent by system in paintstruct structure (PS) ..
XOR RAX,RAX
MOV EAX,[PS.bottom]
ARG RAX
MOV EAX,[PS.right]
ARG RAX
MOV EAX,[PS.top]
ARG RAX
MOV EAX,[PS.left]
ARG RAX
ARG [hDC]
INVOKE Ellipse          ;draw ellipse in update rectangle
;*****************
INVOKE EndPaint,RDI,RBX ;rdi=hWnd, rbx=paintstruct
POP RBX,RDI             ;restore used non-volatile register
XOR RAX,RAX             ;return rax=0 and nc (don't call DefWindowProc)
RET
;
;***************************************************************************
;the call to the code label found in the table is not made to CALL [EAX+R10D*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 R10D and then call R10 knowing that the high dword of R10 is zero.
GENERAL_WNDPROC64:      ;uMsg is in RDX (actually EDX)
MOV R10D,[EAX]          ;get number of messages to do
ADD EAX,4               ;jump over size dword
L2:
DEC R10D
JS >L3                  ;message not found in table
CMP [EAX+R10D*8],EDX    ;see if its the correct message
JNZ L2                  ;no
;RCX=hwnd, RDX=uMsg, R8=wParam, R9=lParam
PUSH R9,R8,RDX,RCX      ;save the parameters for DefWindowProcA
MOV R10D,[EAX+R10D*8+4] ;get the correct table procedure for the message
CALL R10D
POP RCX,RDX,R8,R9       ;restore the parameters for DefWindowProcA
JNC >L4                 ;nc=return value in eax - don't call DefWindowProc
L3:
SUB RSP,20h             ;provide placeholders on stack for register params as reqd.
CALL DefWindowProcA
ADD RSP,20h             ;restore stack after providing placeholders
L4:
RET
;
;***************************************************************************
;
;******************* This is the actual window procedure
WndProcTable:
MOV EAX,ADDR MESSAGES   ;give eax the list of messages to deal with
CALL GENERAL_WNDPROC64  ;call the generic message handler (64-bit version)
RET
;
;*******************************************************************
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,[hInst],0,0       ;owner=desktop
ARG 200D                ;height
ARG 320D                ;width
ARG 50D,50D             ;position y then 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],ADDR WINDOW_CLASSNAME
INVOKE UnregisterClassA   ;ensure class is removed
INVOKE ExitProcess,[wParam]    ;wParam=exit code
