This is a GUI module of EuroTool program EuroCalc for Windows.
EUROASM CPU=X64,SIMD=Yes,Unicode=No
calcwing PROGRAM Format=COFF,Width=64
%DROPMACRO *
INCLUDEHEAD argument.htm
INCLUDE1 winansi.htm,fastcall.htm,winabi.htm,winsgui.htm,winsdlg.htm
INCLUDE1 cpuext.htm,memory64.htm,string64.htm,status32.htm
LINK ../objlib/winapi.lib
; Constants.
xBtn EQU 4 ; Left position of buttons array.
yBtn EQU 120 ; Upper position of buttons array.
wiBtn EQU 70 ; Width of one button.
heBtn EQU 30 ; Height of one button.
[.rodata] ; Constant data.
TitleWindow DB "EuroCalc",0
[.bss] ; Variable data.
Msg DS MSG
WndClassEx DS WNDCLASSEX
PaintStruct DS PAINTSTRUCT
hWindowMain D QWORD
hWindowStatus D QWORD
hDC D QWORD
hFont D QWORD
hBrushGrayBg D QWORD
hBrushWhiteBg D QWORD
InpBufferPtr D QWORD ; Pointer
LineSize D DWORD ; Max. number of characters in the input line and in the result line, depending on MainWidth.
AveCharWidth D DWORD ; Average character width in pixels.
[.text]
MsgLoopMain:: PROC
CALL WndCreateMain
WinABI ShowWindow,[hWindowMain],SW_SHOW
WinABI UpdateWindow,[hWindowMain]
.MsgLoop:
WinABI GetMessage, Msg,0,0,0
TEST RAX
JZ .MsgQuit: ; ZF signalizes message WM_QUIT - request for termination.
WinABI TranslateMessage, Msg ; Remap character keys from national keyboards.
WinABI DispatchMessage, Msg ; Let Windows call our WndProc.
JMP .MsgLoop: ; Wait for another message.
.MsgQuit:
TerminateProgram
ENDP MsgLoopMain
WndCreateMain:: PROC ; Register WndClassEx for the main window.
MOVD [WndClassEx.cbSize],SIZE# WndClassEx
MOVQ [WndClassEx.lpszClassName],TitleWindow
MOVD [WndClassEx.style],CS_HREDRAW|CS_VREDRAW
MOVQ [WndClassEx.lpfnWndProc],WndProcMain
WinABI GetModuleHandle,0
MOVQ [WndClassEx.hInstance],RAX
WinABI LoadIcon,RAX,1 ; The 1st and only icon from [.rsrc] section.
MOVQ [WndClassEx.hIcon],RAX
WinABI LoadCursor,0,IDC_IBEAM
MOVQ [WndClassEx.hCursor],RAX
MOVQ [WndClassEx.hbrBackground],15+1 ; COLOR_BTNFACE+1
WinABI RegisterClassExA, WndClassEx
; Create the main window.
WinABI CreateWindowExA,0, \
TitleWindow,TitleWindow::,WS_OVERLAPPEDWINDOW, \
CW_USEDEFAULT,CW_USEDEFAULT,6*(wiBtn+4)+20,7*(heBtn+4)+170, \
0, 0, [WndClassEx.hInstance], 0
MOV [hWindowMain],RAX
; Create other children windows of the main window.
; EuroCalc icon.
WinABI CreateWindowExA,0,="STATIC",="#1",WS_CHILD+WS_VISIBLE+SS_ICON,\
12,1,16,16,[hWindowMain],0,[WndClassEx.hInstance],0
WinABI SendMessage,RAX,STM_SETICON,[WndClassEx.hIcon],0
; Title EuroCalc version.
WinABI CreateWindowExA,0,="STATIC", Version::, WS_CHILD+WS_VISIBLE +SS_SIMPLE, \
50,10,280,20,[hWindowMain],0,[WndClassEx.hInstance],0
; Status window.
WinABI CreateWindowExA,0,=B'STATIC',=B"", WS_CHILD+WS_VISIBLE+SS_CENTER, \
4,92,6*(wiBtn+4)-2,24,[hWindowMain],0,[WndClassEx.hInstance],0
MOV [hWindowStatus],RAX
; Set the unproportional font of digits.
MOV ECX,8
MOV [AveCharWidth],ECX
WinABI CreateFont, 24, RCX, 0,0,FW_BOLD,0,0,0, ANSI_CHARSET,0,0,0,FIXED_PITCH|FF_MODERN,=B"Courier New"
MOV [hFont],RAX
WinABI SendMessage,[hWindowMain],WM_SETFONT,RAX,1
; Edit window width in pixels.
MOV EAX,6*(wiBtn+4)-2
XOR EDX,EDX
MOV ECX,[AveCharWidth]
DIV ECX
MOV [LineSize],EAX
MOV AL,' '
LEA RDI,[InpBuffer::]
MOV [InpBufferPtr],RDI
MOV ECX,640
REP STOSB
LEA RDI,[OutBuffer::]
MOV ECX,640
REP STOSB
WinABI CreateSolidBrush,0x00F0F0F0
MOV [hBrushGrayBg],RAX
WinABI CreateSolidBrush,0x00FFFFFF
MOV [hBrushWhiteBg],RAX
WinABI CreateCaret,[hWindowMain],0,10,3
; Array of 6 columns and 7 rows of buttons.
LEA RDI,[TitleBtns::]
MOV R14,0x6001 ; Here start WM_USER defined message numbers.
MOV R13,yBtn
.20:MOV R12,xBtn
.30:CMP R13,yBtn+7*heBtn+7*4
JAE .90:
CMP R12,xBtn+ 6*wiBtn+6*4
JAE .40:
LEA RCX,[TitleBtnsEnd::]
LEA RAX,[TitleBtns::]
SUB RCX,RAX
XOR EAX,EAX
REPNE SCASB
WinABI CreateWindowExA,0,=B'BUTTON',RDI, \
BS_PUSHBUTTON+WS_CHILD+WS_VISIBLE+WS_TABSTOP, \
R12,R13,wiBtn,heBtn, \
[hWindowMain],R14,[WndClassEx.hInstance],0
INC R14
ADD R12,wiBtn+4
JMP .30:
.40:ADD R13,heBtn+4
JMP .20:
.90:RET
ENDP WndCreateMain
This is a callback procedure which receives and handles messages for the MainWindow.
Message parameters are by FastCall convention provided in registers RCX, RDX, R8, R9,
we'll save them to shadow space with macro SaveToShadow
. Thanks to this their contents will be available by formal names (
[%hWnd], [%uMsg], [%wParam], [%lParam]), too, in the entire WndProc body.
Messages obtained from Windows are dispatched by WndProcMain to their handlers.
Unhandled messages are passed to DefWindowProc.
WndProcMain Procedure hWnd, uMsg, wParam, lParam ; These parameters are provided in RCX,RDX,R8,R9.
Uses RBX,RBP,RSI,RDI
SaveToShadow
; Fork message uMsg=RAX to its handler using macro Dispatch:
Dispatch EDX , WM_CHAR, WM_PAINT, WM_CTLCOLORSTATIC, WM_KEYDOWN, WM_SIZE, WM_COMMAND, \
WM_SETFOCUS, WM_KILLFOCUS, WM_LBUTTONUP, WM_DESTROY
.Def:WinABI DefWindowProc,[%hWnd],[%uMsg],[%wParam],[%lParam] ; Pass ignored event to DefWindowProc with unchanged arguments.
JMP .Ret: ; Go to EndProcedure with result value RAX as returned from DefWindowProc.
; All message handlers terminate with a jump to the label .Def: or .Ret0:.
.WM_SIZE:
MOVZX EDI,R9W ; Recalculate the new width of the window in pixels.
SUB EDI,7
MOV EAX,EDI
XOR EDX,EDX
DIVD [AveCharWidth]
MOV [LineSize],EAX
JMP .Ret0:
.WM_CTLCOLORSTATIC:
CMP R9,[hWindowStatus]
JNE .Def:
WinABI SetBkMode, R8, TRANSPARENT
WinABI SetTextColor, R8, 0x000000C0 ; Red ink.
MOV RAX,[hBrushGrayBg]
JMP .Ret:
.WM_SETFOCUS:
WinABI ShowCaret,[hWindowMain]
JMP .Ret0:
.WM_KILLFOCUS:
WinABI HideCaret,[hWindowMain]
JMP .Ret0:
.WM_LBUTTONUP: ; Mouse clicked.
MOVZX EAX,R9W ; X-position in pixels.
SHR R9,16 ; Y-position in pixels.
CMP R9W,28
JB .Def:
CMP R9W,50
JA .10:
ADD EAX,4 ; Clicked at the input line. Move caret.
MOV ECX,[AveCharWidth]
XOR EDX,EDX
DIV RCX
LEA RSI,[InpBuffer::]
ADD RAX,RSI
MOV [InpBufferPtr],RAX
JMP .10:
.WM_PAINT: ; Draw input and output line.
LEA RSI,[PaintStruct]
WinABI BeginPaint,[hWindowMain], RSI
MOV [hDC],RAX
WinABI SelectObject,[hDC],[hFont]
WinABI SetTextColor, [hDC], 0x00000000 ; Black ink.
LEA RSI,[InpBuffer::]
MOV RDI,[InpBufferPtr]
SUB RDI,RSI
MOV EAX,[AveCharWidth]
MUL RDI
ADD RAX,4
WinABI SetCaretPos,RAX,52
MOV ECX,[LineSize]
WinABI TextOut,[hDC],4,34,RSI,RCX
WinABI SetTextColor, [hDC], 0x00008000 ; Green ink.
LEA RSI,[OutBuffer::]
MOV ECX,[LineSize]
WinABI TextOut,[hDC],4,56,RSI,RCX
LEA RSI,[PaintStruct]
WinABI EndPaint,[hWindowMain],RSI
JMP .Ret0:
.WM_CHAR:
MOV RAX,R8
Dispatch AL,0x08,0x0D,0x3D,0x1B ; Backspace, Enter, =, Esc.
LEA RSI,[InpBuffer::] ; Otherwise AL is an ordinary character.
MOV ECX,[LineSize]
LEA RDI,[RSI+RCX-1]
LEA RSI,[RDI-1]
MOV RCX,RDI
SUB RCX,[InpBufferPtr]
JLE .Ret0:
STD
REP MOVSB ; Move characters at cursor one position right.
CLD
STOSB ; Store the new character.
MOV [InpBufferPtr],RDI
JNSt [Status::],ArgCalculated,.10:
RstSt [Status::],ArgCalculated
LEA RDI,[OutBuffer::]
MOV ECX,[LineSize]
MOV AL,' '
REP STOSB ; Erase the old output line if ArgCalculated.
.10: ; Common termination.
WinABI InvalidateRect,[hWindowMain],0,1
WinABI UpdateWindow,[hWindowMain]
WinABI SetFocus,[hWindowMain]
JMP .Ret0:
.0n2: ; Button #2 [ Bs ].
.0x08:MOV EAX,[LineSize] ; Key Backspace.
LEA RSI,[InpBuffer::]
MOV RDI,[InpBufferPtr]
CMP RDI,RSI
JNA .Ret0:
LEA RCX,[RSI+RAX-1]
DEC RDI
SUB RCX,RSI
MOV [InpBufferPtr],RDI
LEA RSI,[RDI+1]
REP MOVSB
JMP .10:
.WM_KEYDOWN: ; Noncharacter key was pressed.
MOV AX,R8W
Dispatch AX,VK_LEFT,VK_RIGHT,VK_HOME,VK_END,VK_DELETE,VK_ESCAPE
JMP .Def:
.VK_LEFT: ; Move caret left.
MOV RDI,[InpBufferPtr]
LEA RSI,[InpBuffer::]
CMP RDI,RSI
JNA .Ret0:
DEC RDI
MOV [InpBufferPtr],RDI
JMP .10:
.VK_RIGHT: ; Move caret right.
MOV RDI,[InpBufferPtr]
LEA RSI,[InpBuffer::]
MOV ECX,[LineSize]
LEA RCX,[RSI+RCX-2]
CMP RDI,RCX
JNB .Ret0:
INC RDI
MOV [InpBufferPtr],RDI
JMP .10:
.VK_END: ; Move caret at the end of the input.
LEA RDI,[InpBuffer::]
MOV ECX,[LineSize]
LEA RDI,[RDI+RCX-1]
MOV RSI,RDI
MOV AL,' '
STD
REPE SCASB
CLD
ADD RDI,2
CMP RDI,RSI
JBE .20:
DEC RDI
.20: MOV [InpBufferPtr],RDI
JMP .10:
.VK_HOME: ; Move caret at the beginning.
LEA RSI,[InpBuffer::]
MOV [InpBufferPtr],RSI
JMP .10:
.VK_DELETE: ; Erase the character below caret.
LEA RSI,[InpBuffer::]
MOV ECX,[LineSize]
ADD RCX,RSI
MOV RDI,[InpBufferPtr]
LEA RSI,[RDI+1]
SUB RCX,RSI
REP MOVSB
JMP .10:
.WM_COMMAND: ; Clicked somewhere int the main window.
MOVZX EDX,R8B ; Button ordinal.
CMP EDX,6*7
JA .Def:
Dispatch DL,0n1,0n2,0n42 ; DL=1..42 ordinal number of the clicked button.
LEA RDI,[TitleBtns::] ; Defined in calcmain.htm
.
LEA RCX,[TitleBtnsEnd::]
LEA RAX,[TitleBtns::]
SUB RCX,RAX
XOR EAX,EAX
.30: REPNE SCASB
DEC DL
JNZ .30:
MOV RSI,RDI ; Pointer at the button title.
MOV ECX,[LineSize]
LEA RBX,[InpBuffer::]
ADD RBX,RSI
.35: MOV RDI,[InpBufferPtr]
CMP RSI,RBX
JNB .10:
LODSB
CMP AL,0
JE .10:
STOSB ; Store characters form the title to InpBuffer.
MOV [InpBufferPtr],RDI
JMP .35:
.0n1: ; Button #1 [ Clr ].
WinABI SetWindowText,[hWindowStatus],=B"" ; Erase status.
LEA RDI,[InpBuffer::]
MOV RBX,RDI
MOV [InpBufferPtr],RDI
MOV ECX,640
MOV AL,' '
REP STOSB ; Erase InpBuffer.
LEA RDI,[OutBuffer::]
MOV ECX,640
REP STOSB ; Erase OutBuffer.
JMP .10:
.0n42: ; Button #42 [ = ].
.0x0D: ; Key Enter.
.0x3D: ; Key =.
; Perform the calculation. Uses functions included from calcmain.htm.
WinABI SetWindowText,[hWindowStatus],=B""
LEA RSI,[InpBuffer::]
MOV RCX,[InpBufferPtr]
SUB RCX,RSI
JZ .0n1:
.40: LODSB
CMP AL,' '
JA .50:
LOOP .40:
JMP .0n1:
.50: MOV RDI,[InpBufferPtr]
MOV AL,'='
STOSB
LEA RSI,[InpBuffer::]
MOV ECX,[LineSize]
LEA RCX,[RSI+RCX-1]
SUB RCX,RDI
JLE .10:
MOV AL,' '
REP STOSB
LEA RSI,[InpBuffer::]
LEA RDI,[Pbuffer::]
CALL Parse:: ; Parse the ASCIIZ string at RSI.
MOV [PbufferEnd::],RDI ; Store numbers, parenthesis and functions to RDI=Pbuffer.
JNC .60:
WinABI SetWindowText,[hWindowStatus],=B"Syntax error."
JMP .10:
.60: LEA RSI,[Pbuffer::]
MOV RDX,[PbufferEnd::]
LEA RDI,[InpBuffer::]
MOV [InpBufferPtr],RDI
CALL EchoExpression::
LEA RDI,[InpBuffer::]
MOV ECX,[LineSize]
MOV AL,'='
REPNE SCASB
MOV AL,' '
REP STOSB
LEA RSI,[Pbuffer::]
MOV RDX,[PbufferEnd::]
LEA RDI,[Nbuffer::]
CALL Deparent:: ; Remove N-records for parenthesis.
MOV [NbufferEnd::],RDI
JNC .70:
WinABI SetWindowText,[hWindowStatus],=B"Unbalanced parenthesis."
JMP .10:
.70: CALL Calculate::
JNC .80:
WinABI SetWindowText,[hWindowStatus],=B"Error in expression or division by 0."
JMP .10:
.80: CALL DisplayDecimalProxy:: ; Put the output string to OutBuffer.
SetSt [Status::],ArgCalculated ; This trigger tells to erase OutBuffer on new input.
LEA RDI,[OutBuffer::]
XOR EAX,EAX
MOV ECX,[LineSize]
REPNE SCASB
DEC RDI
MOV AL,' '
REP STOSB
JMP .10:
.0x1B: ; Key Esc.
.VK_ESCAPE:
.WM_DESTROY: ; Program terminates.
WinABI DestroyCaret
WinABI DeleteObject,[hFont]
WinABI ShowWindow,[hWindowMain],SW_HIDE
WinABI PostQuitMessage,0 ; Tell Windows to quit this program with errorlevel 0.
.Ret0:XOR EAX,EAX ; RAX=0 signalizes that the message was processed here.
.Ret:EndProcedure WndProcMain
ENDPROGRAM calcwing