News:

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

Expanding Macros

Started by savage, June 04, 2006, 06:39:44 PM

Previous topic - Next topic

savage

Is there a way I can see what my source code is transformed into AFTER the macros have been accounted for?
It would be a great way for me to see if my macros are working the way they should.

P.S. I'm having particular trouble in using ".type" and OPATTR to test if the input is an immediate, register, or memory location, and my macros are fairly complex so I'd like to just be able to see  their end result.

Ossa

Add the /Fl switch to the ML command line. Syntax:

/Fl[file]

It generates a code listing.

Ossa

[edit] Here's a random example of using OPATTR by the way:

return MACRO ARG
IFNB <ARG>
IF (OPATTR (ARG)) AND 00000100y
IF ARG EQ 0
xor eax, eax
ELSE
mov eax, ARG
ENDIF
ELSEIFDIFI <eax>, <ARG>
mov eax, ARG
ENDIF
ENDIF
ret
ENDM


[/edit]
Website (very old): ossa.the-wot.co.uk

savage

I got it to make a file listing... but at the place where it shows my code, it shows exactly what I typed, and the macro isn't expanded...

savage

Quote from: Ossa on June 04, 2006, 06:54:11 PM
   IF (OPATTR (ARG)) AND 00000100y
What is the 'y' for?
I've seen that before, and I couldn't get it to work using binary numbers, does that 'y' mean something?

Ossa

'y' means binary. 'b' can also mean binary, but is risky, because if your default radix type is hex, then 'b' is a valid hex character, so 00000100b could be interpreted as a valid (but very large) hex value. The 'y' is not used elsewhere, so it makes it safe. The full list is as follows

y for binary (or b if the default radix is not hexadecimal)
o or q for octal
t for decimal (or d if the default radix is not hexadecimal)
h for hexadecimal


Quote from: savage on June 04, 2006, 07:58:20 PM
I got it to make a file listing... but at the place where it shows my code, it shows exactly what I typed, and the macro isn't expanded...

What sort of macro is it? the listing method will only expand if it produces code. If it does non-opcode stuff, then you might be better off using echo or somethign similar. (Remember that for expanded echos you need a dummy string and then an line start expansion on the echo line... i.e. not:

echo %myvar

but

dummy textequ %myvar
% echo dummy


Hope that helps, but remember that macros can be tricky,
Ossa
Website (very old): ossa.the-wot.co.uk

savage

Instead of copying and pasting the glutton I've made my macro into (after several attempts at work-arounds), I'll just tell you exactly what I'm trying to do.

I'm trying to, eventually, make two macros: $float(), and $double().

These macros will basically make a global CONST variable, and the benefit to this is checking whether it already exists, and reusing it if it does.
The macros are virtually the same except one makes a REAL4, and one makes REAL8.

I have, for the most part, gotten this to work.

The way I made this work was, as an example, make $float(12.5) translate to:

.const
    const_float_12_5    REAL4    ?
.code


So basically all I did was change the decimal point "." to an underscore "_".
My first big obstacle was detecting the negative sign, which I ended up fixing by using another underscore:
So, $float(-12.5) =

.const
    const__float_12_5    REAL4    ?
.code


Also, when you call $float(12.5) a second, time, it already exists.  Therefore, I made it to return the already-existing variable. After much struggle with the macro syntax, I finally got this working.



So, you ask,  what am I complaining about if I got it working already????

Well, let me answer that:

I would like to make the $float (and $double) macro more flexible so that I can use it for more things.

I would like to make it detect if I'm inputting an  immediate floating point number, a memory location, and even perhaps a register (double will be harder to incorporate a register, so I don't care about that for now).

I wanted to simply pass back the input if it's not an immediate value. 
Sure, I could simply type "real4 ptr [eax]" instead of "$float(eax)", but this makes my macros break down.  I am trying to call macros from other macros, which may call other macros.  Sometimes, I'm not sure which macro it's going to.  The fact is, if I make $float auto-detect the input type, it will make it much easier to work with floating points.

Interestingly enough, I actually had this  working for a little bit, or so I thought.
I used the brute force method of detecting a "." in my input, because I figured if there's a  ".", it must be a number, and not a memory location!  I couldn't have been more wrong.

After trying to call $float(myPos.X), for example, it's pretty obvious what happens.  It detects the ".", so it thinks it's a floating point number! 

Here's a good pseudo-code form of how I'd like the macro to look:


$float MACRO num

  IF num is floating-point
     exitm <$immediate_float(num)>
  ELSEIF num is memory location
     exitm <$memory_float(num)>
  ELSEIF num is register
     exitm <$register_float(num)>
  ENDIF

ENDM

So, $float would encapsulate those 3 other macros, to detect which one should be used.

P.S. Thanks for the quick support Ossa.


Ossa

OK, well, I'm definately not sure that this is robust, but this will display the type of parameter hoepfully:

$float MACRO num
LOCAL DUMSTR
LOCAL OPVAL
LOCAL DECPT

IF (OPATTR (num)) AND 00010000y
;; Register
DUMSTR TEXTEQU <This is a register: &num&>
ELSEIF (OPATTR (num)) AND 00000100y
;; Immediate
DUMSTR TEXTEQU <This is an immediate: &num&>
ELSEIF (OPATTR (num)) AND 00000010y
;; Memory reference
DUMSTR TEXTEQU <This is a memory reference: &num&>
ELSE
;; Other expression (possibly float)
OPVAL TEXTEQU %OPATTR (num)
DECPT TEXTEQU %@InStr(1, num, <.>)
IF DECPT EQ 0
DUMSTR TEXTEQU <This is unknown: num -- OPATTR = OPVAL -- Not float>
ELSE
DUMSTR TEXTEQU <This is a float: &num&>
ENDIF
ENDIF

% echo DUMSTR
ENDM


I tested the following... I will probably go away and immediately think up a case that will mess this macro up:

$float([eax])
$float(eax)
$float(123)
$float(dwBytes)
$float([eax+4*ebx])
$float(eax+4*ebx)
$float([eax][RECT.left])
$float(RECT.left)
$float(123.456)


It gives the following output:

This is a memory reference: ([eax])
This is a register: (eax)
This is an immediate: (123)
This is a memory reference: (dwBytes)
This is a memory reference: ([eax+4*ebx])
This is unknown: (eax+4*ebx) -- OPATTR = 0 -- Not float
This is a memory reference: ([eax][RECT.left])
This is an immediate: (RECT.left)
This is a float: (123.456)


Ossa
Website (very old): ossa.the-wot.co.uk

Ossa

OK, a much better solution here:

$float MACRO thestr:REQ
LOCAL thecurnum
LOCAL dotcount
LOCAL minuscount
LOCAL outer

dotcount = 0
minuscount = 0

FORC thecurnum, thestr
outer INSTR 1, <0123456789>, <thecurnum>
IF outer EQ 0
;; Not numeric
outer INSTR 1, <.->, <thecurnum>
IF outer EQ 0
;; Not a valid character
dotcount = 2
EXITM
ELSEIF outer EQ 2
;; A minus
IF minuscount EQ 1
;; More than 1 minus
dotcount = 2
EXITM
ELSE
;; Is it at the start?
outer INSTR 1, <thestr>, <thecurnum>
IF outer EQ 1
;; It is at the start
minuscount = 1
ELSE
;; Somewhere else
dotcount = 2
EXITM
ENDIF
ENDIF
ELSE
;; A dot
IF dotcount EQ 0
;; Our first dot
dotcount = 1
ELSE
;; More than 1 dot
dotcount = 2
EXITM
ENDIF
ENDIF
ENDIF
ENDM

IF dotcount EQ 0
;; It is an integer (decimal) without the . though
EXITM <thestr>
ELSEIF dotcount EQ 1
;; It is a float
EXITM <eax>
ELSE
;; It is something else
EXITM <thestr>
ENDIF
ENDM


You must call this as a proper macro function (i.e. the value must be used). Executing it on a line on its own will cause MASM to add brackets to the beginning and end of the string ("thestr").

Ossa

[edit] whoops forgot the possibility of a minus... will add that in [/edit]
[edit] All fixed now [/edit]
[edit] You can also make it detect registers, etc by combining the two macros that I've posted: Use the second, but alter the ELSE clause of the final IF block by adding in a bunch of ELSEIFs [/edit]
Website (very old): ossa.the-wot.co.uk

savage

Thanks a lot, man.
But why do you say the second one is a much better solution? The first one looks like it's great.

Ratch

savage,
     If you code the MASM directive .LISTMACROALL at the beginning of your source code, the calling sequence of nested MACROS, and the variables within will be printed in the listing.  Ratch

savage

Quote from: Ratch on June 05, 2006, 02:02:06 AM
savage,
     If you code the MASM directive .LISTMACROALL at the beginning of your source code, the calling sequence of nested MACROS, and the variables within will be printed in the listing.  Ratch


Hmm... does it only show you if it successfully compiles?
Because my main purpose is to see what the macros reduce to before it tries to assemble.

savage

This is just too frustrating.

I am getting weird errors, like "invalid character: ',' ", in cases where no comma existed, neither in the macro call or in the macro itself.  Since I can't view the results of the macro, I'm not sure where this error is coming from.  If I could view the results of the macro after it is called, then I could see what exactly the assembler sees, so I could make sense out of the errors.  Until then I am completely frustrated and unable to make any progress.  Nothign I try seems to work, due to my lack of understand behind the macros.

Ossa

Quote from: savage on June 05, 2006, 01:28:40 AM
But why do you say the second one is a much better solution? The first one looks like it's great.

The first one is fine in almost any legal case, but consider:

$float(-34h.7--5q)

Although this is meaningless, the first macro will take it to be a legal float. The second looks for input strings that have the (regex) form:

-?[0-9]*.[0-9]*

i.e. an optional minus followed by a series of numbers followed by a dot followed by a series of numbers. I haven't attempted too hard to break either, but I'm sure that there are cases where either might break.

Quote from: Ratch on June 05, 2006, 02:02:06 AM
If you code the MASM directive .LISTMACROALL at the beginning of your source code, the calling sequence of nested MACROS, and the variables within will be printed in the listing.

savage, Ratch's idea works very nicely here; just tried it out myself.

Thanks Ratch! I always find debugging macros hard... this should make it muc much easier.

Quote from: savage on June 05, 2006, 02:27:11 AM
I am getting weird errors, like "invalid character: ',' ", in cases where no comma existed, neither in the macro call or in the macro itself.  Since I can't view the results of the macro, I'm not sure where this error is coming from.  If I could view the results of the macro after it is called, then I could see what exactly the assembler sees, so I could make sense out of the errors.  Until then I am completely frustrated and unable to make any progress.  Nothign I try seems to work, due to my lack of understand behind the macros.

Can you post the relevent lines of code and the call?

Quote from: savage on June 05, 2006, 02:27:11 AM
This is just too frustrating.

Don't give up! If it's frustrating now, it'll just feel that much better when you succeed

Ossa
Website (very old): ossa.the-wot.co.uk

savage

Lol, I think I was just tired last night. 

Anyway, is the DUMSTR really necessary?
I reformatted your original macro to how I would have done it before, and tell me what's different (if anything) about it since I'm not using a dummy string. I'm getting the same output, but I'm guessing there's something different going on.


$float MACRO num

OPVAL TEXTEQU %OPATTR (num)
DECPT TEXTEQU %@InStr(1, num, <.>)

IF (OPATTR (num)) AND 00010000y  ; Register
exitm <This is a register: &num&>
ELSEIF (OPATTR (num)) AND 00000100y  ; Immediate
exitm <This is an immediate: &num&>
ELSEIF (OPATTR (num)) AND 00000010y  ; Memory reference
exitm <This is a memory reference: &num&>
ELSEIF DECPT NE 0 ; Float
exitm <This is a float: &num&>
ELSE ; Unknown
exitm <This is unknown: num -- OPATTR = OPVAL -- Not float>
ENDIF
ENDM


; (test cases)

%$float([eax])
%$float(eax)
%$float(123)
%$float(dwBytes)
%$float([eax+4*ebx])
%$float(eax+4*ebx)
%$float([eax][RECT.left])
%$float(RECT.left)
%$float(123.456)
                %$float(123^456) ; Forced error (Just to stop it from completing compilation)



P.S.  That would be nice if we could use regex's  :boohoo:

Ossa

Quote from: savage on June 05, 2006, 02:48:45 PM
Anyway, is the DUMSTR really necessary?

I reformatted your original macro to how I would have done it before, and tell me what's different (if anything) about it since I'm not using a dummy string. I'm getting the same output, but I'm guessing there's something different going on.

I was using echos because I wasn't using the listing - just looking at the assembler's output... I needed the dummy string for those echos. If you are using EXITM, no dummy string is needed - but you won't be using those return strings anyway, I hope.  :bg

Your output is to the listing, right?

Oh, and the ampersands (&) aren't needed either - I had a bug so I was being safe and adding them in, but it will give the same result either way.

Quote from: savage on June 05, 2006, 02:48:45 PM
P.S.  That would be nice if we could use regex's  :boohoo:

Wouldn't it just... but MS is never going to put that in - they only seem to take stuff out.  :'(

Ossa
Website (very old): ossa.the-wot.co.uk