News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Array of struct with invoke

Started by italogomes, April 06, 2011, 05:59:46 PM

Previous topic - Next topic

italogomes

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.

italogomes

Oh, I forgot to mention the error: the application crashes with Access Violation exception.

jj2007

Eax will  be trashed after an API call. For details, see my signature, The "register gets trashed" trap

italogomes

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?

jj2007

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.

RuiLoureiro

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

donkey

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]

"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

dedndave

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

donkey

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.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

dedndave

i am with you on that one Edgar   :P
remember - it was created by a bunch of C programmers - not ASM guys

jj2007

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.

italogomes

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.

Tedd

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
No snowflake in an avalanche feels responsible.