Hi there! Here I am with another beginner question about masm32.
Well, sorry for this topic; I have already searched the forum and read many posts about array of structs but I still have one thing I don't understand. It is about passing the address of structures inside an array to another proc. Let me explain the situation:
I have defined a struct type named SpriteInfo as the following:
SpriteInfo struct
x SDWORD ?
y SDWORD ?
xSpeed SDWORD ?
ySpeed SDWORD ?
spriteNumber DWORD ?
SpriteInfo ends
I want to control an array of SpriteInfos with many items in it. So I declared it like this:
TOTAL_ASTEROIDS equ 32
asteroids SpriteInfo TOTAL_ASTEROIDS dup (<?>)
So far, so good. I was able to initialize it. The problem is when I try to pass each struct of the array to another proc. Here is the proc's code (ok, it's not optimized yet):
DrawSprite proc uses eax ebx edx edi esi,
lpSpriteInfo: ptr SpriteInfo,
lpSpriteSheetInfo: ptr SpriteSheetInfo,
hDestDC: HDC
local row: DWORD
local column: DWORD
local ySrc: DWORD
local xSrc: DWORD
local nWidth: DWORD
local nHeight: DWORD
assume edi: ptr SpriteInfo
mov edi, lpSpriteInfo
assume esi: ptr SpriteSheetInfo
mov esi, lpSpriteSheetInfo
mov ySrc, 0
mov xSrc, 0
mov eax, [esi].spriteWidth
mov nWidth, eax
mov eax, [esi].spriteHeight
mov nHeight, eax
mov eax, [edi].spriteNumber
.IF eax > 0
mov ebx, [esi].totalColumns
xor edx, edx
div ebx
mov row, eax
mov column, edx
mov eax, row
.IF eax > 0
mov ebx, [esi].spriteHeight
mul ebx
mov ySrc, eax
.ENDIF
mov eax, column
.IF eax > 0
mov ebx, [esi].spriteWidth
mul ebx
mov xSrc, eax
.ENDIF
.ENDIF
invoke BitBlt, hDestDC, [edi].x, [edi].y, nWidth, nHeight, [esi].maskBitmapInfo.hDC, xSrc, ySrc, SRCPAINT
invoke BitBlt, hDestDC, [edi].x, [edi].y, nWidth, nHeight, [esi].imageBitmapInfo.hDC, xSrc, ySrc, SRCAND
assume edi: nothing
assume esi: nothing
ret
DrawSprite endp
My problem is being calling this proc passing each of the SpriteInfo struct inside asteroids. I tried many things, including this:
mov ecx, TOTAL_ASTEROIDS
xor edi, edi
lea eax, asteroids
.REPEAT
invoke DrawSprite, eax, addr spriteSheetInfo, hDC
add eax, sizeOf SpriteInfo
dec ecx
.UNTIL ecx == 0
Anyone could give me a hint about what am I doing wrong? I can attach the project if its size is not a problem.
Thank you all again for any help.
Edit: mistyped a few words.
Oh, I forgot to mention the error: the application crashes with Access Violation exception.
Eax will be trashed after an API call. For details, see my signature, The "register gets trashed" trap
Quote from: jj2007 on April 06, 2011, 06:11:41 PM
Eax will be trashed after an API call. For details, see my signature, The "register gets trashed" trap
Sorry, I don't get it. I declared "uses eax, ..." in the header of my procs to avoid that this characteristic could become a problem. Or is it something else?
Quote from: italogomes on April 06, 2011, 06:15:02 PM
Quote from: jj2007 on April 06, 2011, 06:11:41 PM
Eax will be trashed after an API call. For details, see my signature, The "register gets trashed" trap
Sorry, I don't get it. I declared "uses eax, ..." in the header of my procs to avoid that this characteristic could become a problem. Or is it something else?
proc uses esi edi ebx means that when entering the proc, these registers are saved on the stack
push esi
push edi
push ebx
...
pop ebx
pop edi
pop esi
ret
What happens in between is another story. Every call to a Win API (MessageBox, BitBlt, DrawSprite, ... whatever) trashes edx and ecx and returns some useful value in eax. So you cannot use ecx or eax to do other things. For a counter, use ebx; for a pointer, you may use esi or edi, these regs are not touched by Windows.
MasmBasic calls preserve also ecx, because it is a very useful and versatile register, but Windows doesn't care.
Hi,
ECX is being trashed inside DrawSprite, no ?
;---------------------------------
mov ecx, TOTAL_ASTEROIDS
xor edi, edi
lea eax, asteroids
.REPEAT
invoke DrawSprite, eax, addr spriteSheetInfo, hDC
add eax, sizeOf SpriteInfo
dec ecx <<<-----------------???
.UNTIL ecx == 0
DrawSprite proc uses eax ebx edx edi esi
Is just fine and the value of EAX is preserved across the call. Its not the problem since you don't use EAX as the pointer within the DrawSprite procedure. The problem is that you did not preserve the value of ECX which you are using as a loop counter, it is trashed by BitBlt.
[whoops]I see RuiLoureiro already spotted it[/whoops]
Jochen's tips and tricks page has what you need to know :bg
windows API functions preserve EBX, EBP, ESI, EDI, and the direction flag should be cleared and is retained
EAX, ECX, and EDX may be trashed, and are often used to return results
what you may not be understanding is that we generally try to write our routines using the same conventions
that way, our functions are call-back safe, the same as API calls
so - preserving EAX is not really the issue
your problem may be here
mov ecx,TOTAL_ASTEROIDS
xor edi, edi
lea eax, asteroids
we do not know how this is used, as the posted code is incomplete
but, you zero the EDI register, perhaps without preserving it
i see an issue with extraneous commas on the PROC line
and - not sure that PTR is good - never used it that way
DrawSprite proc uses eax ebx edx edi esi,
lpSpriteInfo: ptr SpriteInfo,
lpSpriteSheetInfo: ptr SpriteSheetInfo,
hDestDC: HDC
it is ok to preserve EAX and EDX this way, just not sure it is necessary
i would probably use something like this
DrawSprite proc uses ebx edi esi lpSpriteInfo:DWORD,lpSpriteSheetInfo:DWORD,hDestDC:HDC
and, at the beginning of the program (after the INCLUDES), i would add a PROTO directive
DrawSprite PROTO :DWORD,:DWORD,:HDC
this allows you to forward reference the PROC in an INVOKE
i made similar changes in your previous StarTreko file
I've said it a hundred times, it makes no sense to me that the ABI decided to preserve EBX which is only used in the nearly useless instruction XLATB and to trash ECX which is used in every REP prefix instruction and every instruction that uses a counter.
i am with you on that one Edgar :P
remember - it was created by a bunch of C programmers - not ASM guys
Quote from: italogomes on April 06, 2011, 06:15:02 PMSorry, I don't get it. I declared "uses eax, ..." in the header of my procs to avoid that this characteristic could become a problem.
My fault, I had not seen the
uses eax. So ecx is the problem, as the others already stated.
Well, yes, the problem was the ECX. Sorry for another begginer question and thank you all for the great help. It would take me ages to realize that by myself.
At least I learned one more characteristic about assembly under Windows environment...
About the commas ",", I think it makes the code more readable by isolating the registers used by the procedure from the parameters list. I hope it does not do any harm to my source.
Quote from: donkey on April 06, 2011, 06:47:51 PM
I've said it a hundred times, it makes no sense to me that the ABI decided to preserve EBX which is only used in the nearly useless instruction XLATB and to trash ECX which is used in every REP prefix instruction and every instruction that uses a counter.
It's derived from the 'rules' C compilers used at the time. Windows functions just got these rules through osmosis.
And then once it's in, everything else has to follow, whether it's relevant now or not.
As for why C compilers did it - I think it's entirely down to the fact that the registers are encoded in the order: eax,ecx,edx, and THEN ebx; so numerically, it made sense. And practically, C programmers shouldn't be concerning themselves with registers, so who cares :bdg