Is there a way to detect that macros parameter is memory expression?
I want to make an invoke macros in such a way that it accept strings as parameters. It can be done this way:

macro stdcall proc, ; call procedure
{ local ..cont
reverse
if arg eq +arg
push arg ; it's number
else
call ..cont
db arg, 0
..cont:
endif
common
call proc }

but when I pass memory expression (for example in another API call) as argument fasm generates error in the line with if.
Posted on 2002-10-03 21:08:00 by wanderer
If I am understanding your purposes, it left only to determine wether arg is a register or not. In such a case, a construction like:

if arg in <eax,ebx,...,edi>

Resuming:
- Constants OK
- Addresses OK
- Registers OK

the remaining is treated as a string.

I suggest also to replace the call with a jmp in

jmp ..cont
db arg, 0
..cont:

Regards,
Posted on 2002-10-04 03:27:47 by pelaillo
> If I am understanding your purposes, it left only to determine wether arg is a register or not. In such a case, a construction like:

> if arg in <eax,ebx,...,edi>

The problem is that when I determine if arg is number or string and the arg is memory expression, fasm generates error, because expression like + is incorrect. The solution would be to determine if arg is string or anything else, but I don't know a way to do this. It would be great if one could write something like:

invoke MessageBox, NULL, "Hello, world!", "Sample", MB_OK+MB_ICONINFORMATION

or

sz_text db "Hello, world!", 0
...
invoke MessageBox, NULL, sz_text, sz_text, MB_OK+MB_ICONINFORMATION

> I suggest also to replace the call with a jmp in

> jmp ..cont
> db arg, 0
> ..cont:

This part of macros should place string address in stack, that's why call is needed, it pushes address of the next command in stack and jumps over string placed on this address.
Posted on 2002-10-05 09:24:53 by wanderer
I think, I found a kind of solution. It's not what I exactly intended to do, but works.

format PE GUI 4.0
entry win_main

include 'macro\import.inc'
include 'kernel32.inc'
include 'user32.inc'

data import
library kernel32, "kernel32.dll", \
user32, "user32.dll"
kernel32:
import ExitProcess, "ExitProcess"
user32:
import MessageBox, "MessageBoxA"
end data

macro a v
{
lea eax, v
push eax
}

macro p a
{
push a
}

macro t s
{ local ..cont
call ..cont
db s, 0
..cont:
}

macro stdcall pr,
{
reverse
par
common
call pr
}

macro invoke pr,
{
reverse
par
common
call
}

win_main:
invoke MessageBox, p NULL, t "Hello, world!", t "Sample", p MB_OK+MB_ICONINFORMATION
invoke ExitProcess, p 0
Posted on 2002-10-05 12:10:12 by wanderer
It's very interesting problem, and quite hard to solve. My final solution needs two fixes in the fasm itself, I'll probably include them in 1.41 (coming soon) when I ensure they won't break the backward compatibility. Before it's released you can make these changes yourself. First, remove the lines 287-288 from PARSER.INC file:


cmp eax,8
ja string_argument

This check is not really needed and causes my solution to not work in some cases. The second change should be made in EXPRESSI.INC file, lines 2015-2016 and 2027-2028 contain the same two instructions:


call check_character
jc invalid_expression

Both two occurences should be replaced with:


mov al,[esi]
or al,al
jz invalid_expression
cmp al,0Fh
je invalid_expression

After applying these changes to source, recompile fasm, now you can try my solution (it's a bit tricky, but should work):



macro stdcall proc,[arg]
{ local ..cont
reverse
if arg in <(arg)> | arg +0 eq arg 0
push arg
else
call ..cont
db arg, 0
..cont:
end if
common call proc }
Posted on 2002-10-05 15:13:40 by Tomasz Grysztar
Your problem forced me to rethink and improve some parts of parser and so thanks to you 1.41 will have it much better, less strict and more flexible, while keeping all the needed error detection. I'm attaching the first alpha version of fasm 1.41 with all these fixes (and it has also the native AMD 3DNow! support). With this new version solution to your problem can be written even clearlier:


macro stdcall proc,[arg]
{ local ..cont
reverse
if +arg eq arg | arg +0 eq arg 0
push arg
else
call ..cont
db arg, 0
..cont:
end if
common call proc }

You have also asked me (privately) about MASM's ADDR equivalent. It can be done by further extending the stdcall macro, here is the working version (fasm 1.41+ only):


macro stdcall proc,[arg]
{ local ..x,..y
reverse
if +arg eq arg
label ..x at arg
virtual at 0
mov eax,[..x]
load ..y from 0
end virtual
if ..y = 0A1h
push arg
else
lea eax,[..x]
push eax
end if
else if arg +0 eq arg 0
push arg
else
call ..x
db arg, 0
..x:
end if
common call proc }

Addresses that cannot be pushed on stack directly (like ebp+something) will be pushed through eax (lea+pop).
Posted on 2002-10-06 06:42:10 by Tomasz Grysztar
Thank you very much!

You are wizard!
Posted on 2002-10-06 13:55:24 by wanderer
Privalov,

Do you think is it a good idea to place data in code section?
Intel says:
"Do not place data in the code segment."

Maybe you can add some support for on- the- fly data section from the assembler.
Posted on 2002-12-20 21:44:47 by Sergo
Read and Write data and constant (Read Only) is the difference data.
Constant data is the code! (IMHO)
Posted on 2002-12-21 02:27:13 by tserk

Do you think is it a good idea to place data in code section?
Intel says:
"Do not place data in the code segment."


As I understand, there is no trouble with placing strings like it's done in this macros, because it (string) anyway will be addressed using ds, and in flat memory model (used in win32) offsets are the same for code and for data. ;) I think one can also put variables inside code as long as cs and ds has the same base.
Posted on 2002-12-21 03:30:32 by wanderer

I think one can also put variables inside code as long as cs and ds has the same base.

Only if the code section has readable and writeable flags - there's also a page-based protection, not only selector-based.
Posted on 2002-12-21 04:44:59 by Tomasz Grysztar
Of course :)
Posted on 2002-12-21 04:56:01 by wanderer
I think one can also put variables inside code as long as cs and ds has the same base.


If you're using cs & ds, and you don't have a bloated big brother app running in protected mode watching over your should, you can do what ever you damn well please :)

Though, if you don't do any writes to the said vairiables (in other words, constants) then all is good.
Posted on 2002-12-21 04:57:45 by eet_1024
I'm not an expert in optimization.
Part of the problem is that data and code cache are distinct.
In this case, when there is no writes to code section, there is no need for the caches syncronization,
but I still don't think it's a good idea.
For example, what happen if future versions of image
loader decide to mark code section as EXECUTE_ONLY by default.

Another issue, that since the data follow unconditional branch they won't be prefetch, but
the same way the won't prefetched if they were in a separate data section.


But anyway another quote from "Intel ? Architecture Optimization Reference Manual":
========================================
During the process of instruction prefetch the address of a conditional
instruction is checked with the entries in the BTB. When the address is not
in the BTB, execution is predicted to fall through to the next instruction.
This suggests that branches should be followed by code that will be
executed. The code following the branch will be fetched, and in the case of
Pentium Pro, Pentium II processors, and Pentium III processor the fetched
instructions will be speculatively executed. Therefore, never follow a
branch instruction with data.
========================================
Posted on 2002-12-22 01:14:16 by Sergo
Addition:
I guess this doesn't matter
==
For example, what happen if future versions of image
loader decide to mark code section as EXECUTE_ONLY by default.
=========
since execute only is only is at segment level.
Pages have only read/write flag, so
in case of Win32 data in code section is still accessible using ds selector.
Posted on 2002-12-22 01:27:33 by Sergo