Tamagotchi. Finite-state machine
asm-file; masm windows gui #
.686
.model flat
;include c:\masm32\include\windows.inc
include c:\masm32\include\gdi32.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\oleaut32.inc
include c:\masm32\include\ole32.inc
includelib c:\masm32\lib\gdi32.lib
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\oleaut32.lib
includelib c:\masm32\lib\ole32.lib
BitmapFromPicture PROTO :DWORD
BitmapFromMemory PROTO :DWORD,:DWORD
WS_EX_TOPMOST equ 8
WS_CHILD equ 40000000h
SW_SHOWNORMAL equ 1
WM_COMMAND equ 111h
SW_SHOWDEFAULT equ 10
WM_LBUTTONDOWN equ 201h
TRANSPARENT equ 1
DT_SINGLELINE equ 20h
HIMETRIC_INCH EQU 2540
exebase equ 400000h
TRUE equ 1
SM_CYSCREEN equ 1
SM_CYCAPTION equ 4
SM_CXDLGFRAME equ 7
SM_CYDLGFRAME equ 8
SRCCOPY equ 0CC0020h
E_POINTER equ 80004003h
E_OUTOFMEMORY equ 8007000Eh
E_INVALIDARG equ 80070057h
E_FAIL equ 80004005h
WM_PAINT equ 0Fh
WM_DESTROY equ 2h
WS_VISIBLE equ 10000000h
WS_POPUP equ 80000000h
WS_EX_LEFT equ 00000000h
ERROR_FILE_NOT_FOUND equ 2
ERROR_INVALID_PARAMETER equ 87
COLOR_BTNFACE equ 15
WS_CAPTION equ 0C00000h
WS_SYSMENU equ 80000h
LOGPIXELSX equ 88
LOGPIXELSY equ 90
DT_VCENTER equ 4h
DT_CENTER equ 1h
ID_JPG1 equ 1
ID_JPG2 equ 2
ID_JPG3 equ 3
ID_JPG4 equ 4
ID_JPG5 equ 5
ID_JPG6 equ 6
ID_BTN_EXIT equ 100
POINT STRUCT
x DWORD ?
y DWORD ?
POINT ENDS
RECT STRUCT
left dd ?
top dd ?
right dd ?
bottom dd ?
RECT ENDS
MSG STRUCT
hwnd DWORD ?
message DWORD ?
wParam DWORD ?
lParam DWORD ?
time DWORD ?
pt POINT <>
MSG ENDS
PAINTSTRUCT STRUCT
hdc DWORD ?
fErase DWORD ?
rcPaint RECT <>
fRestore DWORD ?
fIncUpdate DWORD ?
rgbReserved BYTE 32 dup(?)
PAINTSTRUCT ENDS
IPicture STRUCT
; IUnknown methods
QueryInterface DWORD ?
AddRef DWORD ?
Release DWORD ?
; IPicture methods
get_Handle DWORD ?
get_hPal DWORD ?
get_Type DWORD ?
get_Width DWORD ?
get_Height DWORD ?
Render DWORD ?
set_hPal DWORD ?
get_CurDC DWORD ?
SelectPicture DWORD ?
get_KeepOriginalFormat DWORD ?
put_KeepOriginalFormat DWORD ?
PictureChanged DWORD ?
SaveAsFile DWORD ?
get_Attributes DWORD ?
IPicture ENDS
.code
start:
xor ebx,ebx
mov esi,exebase
mov edi,offset wTitle
push offset CursorFile
call LoadCursorFromFileA
push edi
push ebx
push COLOR_BTNFACE+1
push eax
push eax
push esi
push ebx
push ebx
push offset WndProc
push ebx
push esp
call RegisterClassA
mov ecx,Cpt
mov eax,[WinW+ecx*4-4]
mov x2Reg,eax
mov eax,[WinH+ecx*4-4]
mov y2Reg,eax
push SM_CYDLGFRAME
call GetSystemMetrics
mov y1Reg,eax
add y2Reg,eax
shl eax,1
add WinH,eax
add WinW,eax
push SM_CXDLGFRAME
call GetSystemMetrics
mov x1Reg,eax
inc eax
add x2Reg,eax
; Hauteur du bandeau titre
; ------------------------
push SM_CYCAPTION
call GetSystemMetrics
add WinH,eax
add y1Reg,eax
inc eax
add y2Reg,eax
; Obtenir la largeur en pixels de l'ecran
; ---------------------------------------
push ebx;SM_CXSCREEN
call GetSystemMetrics
mov WScreen,eax
sub eax,WinW
shr eax,1
mov WinX,eax
; Obtenir la hauteur en pixels de l'ecran
; ---------------------------------------
push SM_CYSCREEN
call GetSystemMetrics
mov HScreen,eax
sub eax,WinH
shr eax,1
mov WinY,eax
;--------------------------+
; creating the main window |
;--------------------------+
restart: mov eax,Cpt
mov esi,exebase
mov edi,offset wTitle
push ebx
push esi
push ebx
push ebx
push [WinH+eax*4-4]
push [WinW+eax*4-4]
push WinY
push WinX
push WS_CAPTION+WS_SYSMENU
push edi
push edi
push WS_EX_LEFT or WS_EX_TOPMOST
call CreateWindowExA
mov hWnd,eax
mov ecx,Cpt
mov eax,[WinW+ecx*4-4]
mov x2Reg,eax
mov eax,[WinH+ecx*4-4]
mov y2Reg,eax
push 100
push 100
push y2Reg
push x2Reg
push y1Reg
push x1Reg
call CreateRoundRectRgn
push ebx
push eax
push hWnd
call SetWindowRgn
push SW_SHOWDEFAULT
push hWnd
call ShowWindow
call BitmapFromResource
mov hBmp, eax
mov eax,Cpt
mov eax,[WinW+eax*4-4]
sub eax,60
push ebx
push exebase
push ID_BTN_EXIT
push hWnd
push 18
push 20
push 18
push eax
push WS_CHILD or WS_VISIBLE
push offset lpText
push offset btnClass
push ebx
call CreateWindowEx
; ---------------------------
; macros for unchanging code
; ---------------------------
mov ebp,esp
StartLoop:push ebx
push ebx
push ebx
push ebp
call GetMessage
or eax,eax
jz ExitProgram
push ebp
call DispatchMessage
jmp StartLoop
ExitProgram:cmp ClickMouse,ebx
jz a0
inc Cpt
mov eax,Cpt
jmp [handler+eax*4-8]
a1: mov WinX,10
jmp a6
a2: mov eax,WScreen
sub eax,WinW
sub eax,160
mov WinX,eax
jmp a6
a3: mov WinX,10
mov eax,HScreen
sub eax,WinH
sub eax,160
jmp a7
a4: mov eax,WScreen
sub eax,[WinW+3*4]
shr eax,1
mov WinX,eax
mov eax,HScreen
sub eax,[WinH+3*4]
shr eax,1
a7: mov WinY,eax
jmp restart
a5: mov eax,WScreen
sub eax,[WinW+4*4]
shr eax,1
mov WinX,eax
a6: mov WinY,ebx;0
jmp restart
a0: push ebx
call ExitProcess
WndProc proc hWin,uMsg,wParam,lParam
LOCAL hOld:DWORD
LOCAL ps:PAINTSTRUCT
mov eax,uMsg
mov edi,hWin
sub eax,WM_DESTROY
jz wmDESTROY
sub eax,WM_PAINT-WM_DESTROY
jz wmPAINT
sub eax,WM_COMMAND-WM_PAINT
jz wmCOMMAND
sub eax,WM_LBUTTONDOWN-WM_COMMAND
jz wmLBUTTONDOWN
leave
jmp DefWindowProc
wmDESTROY:mov ClickMouse,ebx
jmp @f
wmCOMMAND: cmp wParam,ID_BTN_EXIT
jne wmBYE
push ebx
push ebx
push WM_DESTROY
push edi
call PostMessage
jmp wmBYE
wmLBUTTONDOWN:push edi
call DestroyWindow
inc ClickMouse
@@: push ebx
call PostQuitMessage
jmp wmBYE
wmPAINT:lea eax,ps
push eax
push edi
call BeginPaint
push eax;hDC
call CreateCompatibleDC
mov esi,eax
push hBmp
push eax
call SelectObject
mov hOld,eax
mov eax,Cpt
push SRCCOPY
push ebx;0
push ebx;0
push esi;memDC
push [WinH+eax*4-4]
push [WinW+eax*4-4]
push 1
push ebx
push ps.hdc
call BitBlt
push TRANSPARENT
push ps.hdc
call SetBkMode
mov eax,Cpt
push DT_SINGLELINE+DT_VCENTER+DT_CENTER
push offset rectTxt
push -1
push dword ptr [handler2+eax*4-4]
push ps.hdc
call DrawText
push ps.hdc
call SelectObject
push esi;memDC
call DeleteDC
lea eax,ps
push eax;ADDR ps
push edi;hWin
call EndPaint
wmBYE: ret
WndProc endp
BitmapFromResource PROC
LOCAL hResource:DWORD, dwFileSize:DWORD, hImage:DWORD
; get a resource handle (address) and resource length from the executable
push offset szImage
push Cpt
push esi
call FindResource
or eax,eax
jz error
mov hResource, eax
push eax
push esi
call LoadResource
push eax
call LockResource
mov hImage, eax
push hResource
push esi
call SizeofResource
mov dwFileSize, eax
or eax,eax ; we use the resource size to determine if we got a
jz error ; legit image file to open
push dwFileSize
push hImage
call BitmapFromMemory
jmp @f
error: push ERROR_FILE_NOT_FOUND
call SetLastError
xor eax,eax
@@: ; everything's been done for us now, just return
ret ; we're all done
BitmapFromResource ENDP
BitmapFromMemory PROC uses esi edi pMemory:DWORD, dwFileSize:DWORD
LOCAL hResource:DWORD, pGlobal:DWORD, pStream:DWORD
LOCAL hImage:DWORD, pPicture:DWORD, hBitmap:DWORD
;invoke CoInitialize, ebx
mov pStream, ebx
mov pPicture, ebx ; ebx pointers for later use
invoke CoTaskMemAlloc, dwFileSize ; copy picture into task memory
or eax,eax
jz error
; oops! we didn't get the memory
; the last error code was set for us, and EAX is zero, so just return
mov pGlobal, eax
mov esi, pMemory
mov edi, eax;pGlobal
mov ecx, dwFileSize
push ecx
shr ecx, 2
rep movsd
pop ecx
and ecx, 11y
rep movsb
; create a stream for the picture object's creator
invoke CreateStreamOnHGlobal, pGlobal, TRUE, ADDR pStream
or eax,eax
jz @f
invoke CoTaskMemFree,pGlobal
xor eax,eax
jmp error
@@: lea eax,pPicture
push eax
push offset IID_IPicture
push TRUE
push ebx
push pStream
call OleLoadPicture
or eax,eax
jz @f
mov eax, pStream
call release_pStream
xor eax,eax
jmp error
@@:
; now we are ready to get the hBipmap, we farm this out for reuseability
invoke BitmapFromPicture, pPicture
mov hBitmap, eax
mov eax, pStream
call release_pStream
mov eax, pPicture
call release_pPicture
mov eax, hBitmap ; hBitmap is our return value, stuff it
error: ret ; we're all done
BitmapFromMemory ENDP
; release the stream
release_pStream PROC
push eax
mov eax, [eax]
call [eax].IPicture.Release
ret
release_pStream ENDP
; release the Picture object
release_pPicture PROC
push eax
mov eax, [eax]
call [eax].IPicture.Release
ret
release_pPicture ENDP
BitmapFromPicture PROC pPicture:DWORD
LOCAL tempDC:DWORD, tempBitmap:DWORD, OldBitmap:DWORD
LOCAL dwWidth:DWORD, dwHeight:DWORD, compDC:DWORD
LOCAL hmWidth:DWORD, hmHeight:DWORD, neghmHeight:DWORD
; check we have an object
cmp pPicture,0
jnz @f
; whoops, no object passed in
push ERROR_INVALID_PARAMETER
jmp seterror
@@:
; get a DC to work with
invoke GetDC, ebx ; screen DC
mov compDC, eax
invoke CreateCompatibleDC, compDC
or eax,eax
jnz @f
; whoops, didn't get a DC
; but at least we had SetLastError called for us
invoke ReleaseDC,ebx,compDC
jmp error
@@: mov tempDC, eax
; read out the width and height of the IPicture object
; (IPicture)pPicture::get_Width(*hmWidth)
lea eax, hmWidth
push eax
mov eax, pPicture
push eax
mov eax, [eax]
call [eax].IPicture.get_Width
; (IPicture)pPicture::get_Height(*hmHeight)
lea eax, hmHeight
push eax
mov eax, pPicture
push eax
mov eax, [eax]
call [eax].IPicture.get_Height
; convert himetric to pixels
invoke GetDeviceCaps, compDC, LOGPIXELSX
invoke MulDiv, hmWidth, eax, HIMETRIC_INCH
mov dwWidth, eax
invoke GetDeviceCaps, compDC, LOGPIXELSY
invoke MulDiv, hmHeight, eax, HIMETRIC_INCH
mov dwHeight, eax
xor eax, eax
sub eax, hmHeight
mov neghmHeight, eax
invoke CreateCompatibleBitmap, compDC, dwWidth, dwHeight
or eax,eax;.IF !eax
jz err
; whoops, didn't get a bitmap
; but at least we had SetLastError called for us\
; clean up the DC
mov tempBitmap, eax
invoke SelectObject, tempDC, tempBitmap
or eax,eax;.IF !eax
jnz @f
; whoops, didn't select our bitmap
; but at least we had SetLastError called for us
invoke DeleteObject, tempBitmap
err: invoke ReleaseDC,ebx,compDC
invoke DeleteDC, tempDC
jmp error
@@: mov OldBitmap, eax
; ok, now we have our bitmap mounted onto our temporary DC, let's blit to it
; (IPicture)pPicture::Render(hdc, x, y, cx, cy, \
; xpos_himetric, ypos_himetric, \
; xsize_himetric, ysize_himetric, *rectBounds)
push ebx ; *rectBounds
push neghmHeight
push hmWidth
push hmHeight
push ebx;0
push dwHeight
push dwWidth
push ebx;0
push ebx;0
push tempDC
mov eax, pPicture
push eax
mov eax, [eax]
call [eax].IPicture.Render
test eax, eax
jns @f;.IF SIGN?
; the call failed!
push eax
; do some clean up first
invoke ReleaseDC,ebx,compDC
invoke DeleteDC, tempDC
invoke DeleteObject, tempBitmap
; need to parse out the return fail value
seterror:call SetLastError
error: xor eax,eax
jmp exit
@@: ;.ENDIF
; we now have the bitmap blitted, let's get it off the dc and clean up.
; we're not going to check for errors, cause we did our importaint thing
; and if these fail now, other things will fall apart anyway
invoke ReleaseDC,ebx,compDC
invoke SelectObject, tempDC, OldBitmap
invoke DeleteDC, tempDC
mov eax, tempBitmap ; the bitmap handle is the return value
exit: ret ; we're all done
BitmapFromPicture ENDP
.data
wTitle db "SEX-Tamagochi",0
CursorFile db "Images\heart.cur",0
btnClass db 'BUTTON',0
lpText db 'X',0
ClickMouse dd ?
handler dd a1,a2,a3,a4,a5,a0
hBmp dd ?
Cpt dd 1
WScreen dd ?
HScreen dd ?
WinX dd ?
WinY dd ?
x1Reg dd ?
y1Reg dd ?
x2Reg dd ?
y2Reg dd ?
hWnd dd ?
WinW dd 247,535+5,396+5,326+5,335+5,600+5
WinH dd 295,268+25,208+25,396+25,236+25,244+25
szImage db "IMAGE",0
IID_IPicture dd 07BF80980h,101ABF32h,0AA00BB8Bh,0AB0C3000h
Txt0 db 'oh! My Dear, click me!',0
Txt1 db 'hmmm! again, again...',0
Txt2 db "aaah! it's too much....",0
Txt3 db "iiih! You roast my circuits!...",0
Txt4 db "Stop Programmer, WORK!",0
Txt5 db "GoodBye My Dear...",0
handler2 dd Txt0,Txt1,Txt2,Txt3,Txt4,Txt5
rectTxt RECT <16,0,200,85>
end start
rsrc.rc#define ID_JPG1 1
#define ID_JPG2 2
#define ID_JPG3 3
#define ID_JPG4 4
#define ID_JPG5 5
#define ID_JPG6 6
ID_JPG1 IMAGE "Images\\1.jpg"
ID_JPG2 IMAGE "Images\\2.jpg"
ID_JPG3 IMAGE "Images\\3.jpg"
ID_JPG4 IMAGE "Images\\4.jpg"
ID_JPG5 IMAGE "Images\\5.jpg"
ID_JPG6 IMAGE "Images\\6.jpg"
And I tried... 14 downloaded and there are no questions...
works very well. :t
Hi, six_L!
Thanx!
Nice :t
Hi, Siekmanski!
dank u!
The code does break the WinABI. Also, you can't make any assumptions about the register content inside API callbacks (e.g. that EBX is still zero when WndProc is called).
regards
Quoteyou can't make any assumptions about the register content inside API callbacks
Hi,
qWord!
Should not be treated with the same yardstick to all applications. The first line of the program after the label "start" is command "xor ebx,ebx". You can put in the EBX-register of any other value, then using debbagera sure that this value will be retained inside API callbacks. This technique can not be used when writing drivers, but for writing gui- and console- applications zeroing EBX-register is well suited.
regards
Quote from: Mikl__ on December 22, 2015, 12:55:36 PMYou can put in the EBX-register of any other value, then using debbagera sure that this value will be retained inside API callbacks.
Even if this was the case (it isn't), that would be an undocumented feature.
A quick test on Win7-64 shows that on
every entry to WndProc,
ebx = 0 ; lucky you, that was exactly what you needed :biggrin:
esi = 4010FBh
edi= 0
Of course, every Windows version can show a different behaviour - this is what they call "undocumented feature". For example, in the past I relied on Windows being nice to my xmm regs - they seemed magically protected. Then I wrote 32-bit applications on a 64-bit OS and bang!, they got merciless trashed by the OS. Relying on such empirically observed behaviour is a dangerously bad programming practice. MSDN tells you what is allowed and what isn't, and nobody else.
Hi, jj!
thanks for the support! (http://www.cyberforum.ru/images/smilies/ap.gif)
Not sure if you realized what is the point:
- the WndProc must preserve EBX, ESI, EDI and EBP due to the calling convention stdcall.
- the content of the registers ESI, EDI, EBX, EBP, EAX, EDX and ECX is undefined when the WndProc is called.
Quote from: Grand InquisitorNot sure if you realized what is the point:
- the WndProc must preserve EBX, ESI, EDI and EBP due to the calling convention stdcall.
- the content of the registers ESI, EDI, EBX, EBP, EAX, EDX and ECX is undefined when the WndProc is called.
Quote from: René DescartesI would give everything I know by the half of what I don't know
Any reference to this info? Maybe only affect adversely to the program running in some cases? Many times don't seem to affect
Thank you
:biggrin:
I still find humour in folks who think they can second guess the Intel ABI. I am not sure what they are supposed to be gaining from living dangerously and writing code that risks going BANG on another OS version. Write you code in full compliance with the Intel ABI or when you least expect it, it will jump up and bite you (not BYTE you) and the OS will say nasty things about your code and it will be right. Do it the right way the first time and you can expect it to run on anything from Win95 OEM to Win12 Universe. :P
Hi, hutch--!
I not understand well, but you were talking about me? I do not pretend to create a monumental code, but the trick of zeroing ebx-register worked in 32-bit Win 95/98/XP and continues to work in 64-bit Win7 without risks going BANG on another OS version.
P.S. it is strange that in Examples for Win64 Iczelion tutorial (http://masm32.com/board/index.php?topic=4190.0) is no one outraged
Maybe not every one can understand the quote from Descartes. What it says is that what I know is very little compared to what I don't know. It's a way to say "I assume my ignorance, may anyone light a bit please?"
Hi, avcaballero!
to whom you direct quote from Descartes? Please decrypt the name of the interlocutor...
"ἓν οἶδα ὅτι οὐδὲν οἶδα"or "scio me nihil scire" or "scio me nescire" these are the words of Socrates
Why do you give the hint, Mikl? I talked to qWord, beginning for assuming my own ignorance and requesting information.
Todo este malentendido es muy gracioso, y sólo por usar una referencia a Descartes :biggrin:
Contaba un pasaje que había dos sabios hablando.
El primero afirmó levantando el dedo índice que, en efecto, había un sólo Dios.
El segundo al ver tal gesto pensó que sin duda el primer sabio quería sacarle un ojo con el dedo, por lo que contesta levantando el puño que le daría con él en la cabeza
El primero, al verlo, piensa que quiere decirle que no hay sólo uno sino muchos, y vuelve a insistirle levantando el dedo índice que sólo puede haber uno.
Etc.
No, its pointed at everyone, write risky code and you might be lucky, write reliable code and you will always be lucky. :P
ehem!
Quote from: avcaballero on December 23, 2015, 08:06:41 PM
Quote from: René DescartesI would give everything I know by the half of what I don't know
Any reference to this info? Maybe only affect adversely to the program running in some cases? Many times don't seem to affect
actual jj's and hutch's posts did exlpain it very well...
- MSDN: Calling Conventions (https://msdn.microsoft.com/en-us/library/k2b2ssfy.aspx)
- Agner Fog: Calling conventions for different C++ compilers and operating systems (http://agner.org/optimize/calling_conventions.pdf)
- your favorite search engine: stdcall __stdcall calling convention ...
Quote from: qWord on December 22, 2015, 11:12:39 PM
- the WndProc must preserve EBX, ESI, EDI and EBP due to the calling convention stdcall.
Hmmmm. It was a misunderstanding because I didn't see Mikl__ code. I already know about different kind of calling conventions, what I understood is that it is necessary to save ebx, esi and edi (ebp is saved automatically becuase you have used local variables) on WndProc, ie, something like this:
proc WindowProc uses ebx esi edi, hwnd,wmsg,wparam,lparam
[...]
endp
Of course you can't assume any value of them inside WindowProc, what I didn't know is that it is necessary to save them there.
Regards
What have may happen (the origin of the misundertunded error)?
It seems that in 64 bits many things go in registers instead of the stack in 32 bits, for example the code attached. The hWnd comes in the rcx register
... the fu..ing attacked file ... :icon_mrgreen:
x64/WIN64 use __fastcall only ?
Less things to worry about :t
For x86-64 the calling convention fastcall is used, which has different register and stack usage than stdcall or cdecl. Lately (probably with VS2015) MS also introduce vectorcall, which is a extension of fastcall.
And it seems that FPU instructions don't work well, instead it is best to use SSE for example. I have tried (many times) FPU till I realized that.
I've read in msdn.Microsoft that "All floating point operations are done using the 16 XMM registers"
Quote from: avcaballero on December 24, 2015, 02:03:40 AM
And it seems that FPU instructions don't work well, instead it is best to use SSE for example. I have tried (many times) FPU till I realized that.
I've read in msdn.Microsoft that "All floating point operations are done using the 16 XMM registers"
you can use the FPU also for x64 in user mode, but the registers are not part of the calling convention (except the control word). Otheriwse the same rules as for x86-32 applies.
EDIT: confusing statments about the FPU stack removed ...