There are many times when I come back to a snippet of code after not using it for a couple of years and scratch my head wondering how it works. I should know, I wrote it, but there are times when the register designations are a bit complex and hard to follow. Lately I've been trying to write a particularly complicated peice of code that I want to avoid using memory based storage for. Well, avoiding using memory means a lot of registers holding the data and keeping track of which register has what data in a particular procedure can get you pulling your hair out. Enter #LOCALDEF, using it I can assign an ALIAS to a register then use that alias in its place. There are a couple of advantages to this, changing the register the alias is describing changes it through the whole procedure automatically and most importantly its easy to read and understand what the procedure is doing, finally since we are using #LOCALDEF, the definitions are local to the procedure, in other words you can reuse them as much as you like in other procedures.
Here's an example of a simple procedure that counts spaces:
CountSpaces FRAME pString
mov EDI,[pString]
mov ESI,0
mov EAX,0
.GETNEXTCHAR
mov DL,[EDI+ESI]
inc ESI
cmp DL,0
je >.ENDOFSTRING
cmp DL,' '
jne .GETNEXTCHAR
inc EAX
jmp .GETNEXTCHAR
.ENDOFSTRING
ret
endf
Now granted that procedure is really easy to follow but I'm not trying to confuse anyone, just demonstrating how using an alias can make your code more readable. Here's the same code using aliases:
CountSpaces FRAME pString
#LOCALDEF @BASEPTR EDI
#LOCALDEF @CHARINDEX ESI
#LOCALDEF @SPACECOUNT EAX
#LOCALDEF @CURRENTCHAR DL
mov @BASEPTR,[pString]
mov @CHARINDEX,0
mov @SPACECOUNT,0
.GETNEXTCHAR
mov @CURRENTCHAR,[@BASEPTR+@CHARINDEX]
inc @CHARINDEX
cmp @CURRENTCHAR,0
je >.ENDOFSTRING
cmp @CURRENTCHAR,' '
jne .GETNEXTCHAR
inc @SPACECOUNT
jmp .GETNEXTCHAR
.ENDOFSTRING
ret
endf
Note that above I started aliases with a @, that is not necessary but it helps me to remember that the label actually directly resolves to a register.
Edgar
Edgar,
In my experience it takes only a few months until I have not the faintest idea what rubbish I was programming a few months ago :wink
Here is the Masm equivalent - I usually add the name of the register to remember it is a register:
RunPipe proc
LOCAL hReadOut, hWriteOut
PipeEditEsi equ <esi>
mov PipeEditEsi, hPipeEdit
invoke ShowWindow, PipeEditEsi, SW_RESTORE
SetMyFocus PipeEditEsi
It's a good technique, although during development, typing 100 times @CURRENTCHAR is prohibitive. Ex-post Find & Replace is better :bg
TEXTEQU has an advantage for textual aliasing in that the label is "re-usable" - it may be assigned new values later in the source
_OutBufSize TEXTEQU <[EBP+32]> ;output buffer size in bytes
_OutBufBase TEXTEQU <[EBP+28]> ;output buffer base address
_InpValSize TEXTEQU <[EBP+24]> ;input value size in bytes
_InpValBase TEXTEQU <[EBP+20]> ;input value base address
; [EBP+16] PROC return address
; [EBP+12] saved ESI contents
; [EBP+8] saved EDI contents
; [EBP+4] saved EBX contents
; [EBP] saved EBP contents
_SignXorMask TEXTEQU <[EBP-4]> ;sign XOR mask
_OutLastDword TEXTEQU <[EBP-8]> ;address of last dword in output buffer
i also use "=" equates for numeric values - for the same reason
StyleFlags = WS_CHILD or WS_VISIBLE or WS_CLIPCHILDREN
INVOKE CreateWindowEx,edx,offset szClientClass,edx,
StyleFlags,ecx,ecx,ecx,ecx,eax,edx,ebx,esp
;
;
;
StyleFlags = WS_CHILD or WS_VISIBLE or WS_CLIPSIBLINGS
INVOKE CreateWindowEx,edx,offset szClientClass,edx,
StyleFlags,ecx,ecx,ecx,ecx,eax,edx,ebx,esp
not that i have created any huge projects, but i try to reduce symbol space where practical