;------------------------------------------------------------------ ; ; 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,[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