Author Topic: equ routinename works differently than masm  (Read 261 times)

jimg

  • Member
  • ***
  • Posts: 254
equ routinename works differently than masm
« on: May 10, 2018, 01:07:59 AM »
While working on an old program, this bug took a long time to run down.
I think we have discussed before, but I couldn't find the thread.

When running the following program, masm correctly displays 1 in the messagebox, uasm display 2.
Code: [Select]
.686
include masm32rt.inc
.data
buff db 100 dup (?)
fmt db "return=%lu",0
.code

test1 proc x,y
    mov edx,x
    ret
test1 endp
test2 proc x,y
    mov edx,y
    ret
test2 endp

want1=1
ifdef want1
    testroutine = test1
else
    testroutine = test2
endif

.code
Program:
    invoke testroutine,1,2
    invoke wsprintf,addr buff,addr fmt,edx
    invoke MessageBox,0,addr buff,0,0
    ; uasm print out 2, masm prints out 1
    invoke ExitProcess,0

end Program

In the Masm programmers reference, it states-

Quote
The directives EQU and = have slightly different purposes. Integers defined with the = directive can be
redefined with another value in your source code, but those defined with EQU cannot. Once you’ve
defined a symbolic constant with the EQU directive, attempting to redefine it generates an error. The
syntax is:
symbol EQU expression
The symbol is a unique name of your choice, except for words reserved by MASM. The expression can
be an integer, a constant expression, a one- or two-character string constant (four-character on the
80386/486), or an expression that evaluates to an address.

In this context, it seems to me that testroutine is " an expression that evaluates to an address".

Before someone immediately jumps in with "It will work if you use .....", yes, I know several ways to make it work, but I feel it should work like masm in this instance.

Similarly, if you change the code to the following, masm works properly but uasm goes off into never-never land and crashes.

Quote
ifdef want1
   testroutine = test1
else
   testroutine = test2
endif


dedndave

  • Member
  • *****
  • Posts: 8789
  • Still using Abacus 2.0
    • DednDave
Re: equ routinename works differently than masm
« Reply #1 on: May 10, 2018, 01:58:46 AM »
the reference text you are looking at refers to numbers, strictly speaking
i don't know if the assembler resolves "test1" and "test2" to addresses
you might try it with "offset test1"

it may be that it is essentially a TEXTEQU, which is not quite the same as EQU or =

either way, it should work correctly   :P

jj2007

  • Member
  • *****
  • Posts: 8429
  • Assembler is fun ;-)
    • MasmBasic
Re: equ routinename works differently than masm
« Reply #2 on: May 10, 2018, 03:33:36 AM »
you might try it with "offset test1"

Masm throws an error. Btw Masm shows "1" for this code:
Code: [Select]
want1=2
ifdef want1
    testroutine = test1
else
    testroutine = test2
endif

dedndave

  • Member
  • *****
  • Posts: 8789
  • Still using Abacus 2.0
    • DednDave
Re: equ routinename works differently than masm
« Reply #3 on: May 10, 2018, 05:02:57 AM »
Code: [Select]
    .data

BranchTable dd test1,test2

    .code

    mov     ebx,4   ;or 0
    call dword ptr BranchTable[ebx]

you can use invoke if you do the EQU's and TYPEDEF's  :biggrin:

_japheth

  • Regular Member
  • *
  • Posts: 8
Re: equ routinename works differently than masm
« Reply #4 on: May 10, 2018, 05:44:34 AM »
This is indeed a bug, inherited from jwasm - so I feel a bit responsible for it.

The test case can be simplified to
Code: [Select]
.686
.model flat, stdcall
.code

test1 proc x,y
    mov edx,x
    ret
test1 endp

testroutine = test1

.code
Program:
    invoke test1,1,2
    invoke testroutine,1,2
end Program

and, after compiling it with options -Fl and -Sg, see the listing file:

Code: [Select]
                                .686
                                .model flat, stdcall
00000000                    *   _TEXT segment PARA FLAT PUBLIC 'CODE'
                            *   _TEXT ends
00000000                    *   _DATA segment PARA FLAT PUBLIC 'DATA'
                            *   _DATA ends
                            *   assume cs:flat,ds:flat,ss:flat,es:flat,fs:ERROR,gs:ERROR
                                .code
00000000                    *   _TEXT segment
                            *   assume cs:FLAT

00000000                        test1 proc x,y
00000000  55                *   push ebp
00000001  8BEC              *   mov ebp, esp
00000003  8B5508                    mov edx,x
00000006                            ret
00000006  5D                *   pop ebp
00000007  C20800            *   retn 8
0000000A                        test1 endp

0000000A  = 0                 C testroutine = test1

00000000                        .code
0000000A                    *   _TEXT ends
0000000A                    *   _TEXT segment
                            *   assume cs:FLAT
0000000A                        Program:
0000000A                            invoke test1,1,2
0000000A  6A02              *    push 2
0000000C  6A01              *    push 1
0000000E  E8EDFFFFFF        *    call test1
00000013                            invoke testroutine,1,2
00000013  6A01              *    push 1
00000015  6A02              *    push 2
00000017  E8FBFFFFFF        *    call testroutine
                                end Program
0000001C                    *   _TEXT ends


Macros:

                N a m e                 Type

@CatStr  . . . . . . . . . . . .        Func
@Environ . . . . . . . . . . . .        Func
@InStr . . . . . . . . . . . . .        Func
@SizeStr . . . . . . . . . . . .        Func
@SubStr  . . . . . . . . . . . .        Func


Segments and Groups:

                N a m e                 Size     Length   Align   Combine Class

FLAT . . . . . . . . . . . . . .        GROUP
_DATA  . . . . . . . . . . . . .        32 Bit   00000000 Para    Public  'DATA'
_TEXT  . . . . . . . . . . . . .        32 Bit   0000001C Para    Public  'CODE'


Procedures, parameters and locals:

                N a m e                 Type     Value    Segment  Length

test1  . . . . . . . . . . . . .        P Near   00000000 _TEXT    0000000A Public   STDCALL
  x  . . . . . . . . . . . . . .        DWord             ebp + 0008
  y  . . . . . . . . . . . . . .        DWord             ebp + 000C
testroutine  . . . . . . . . . .        P Near   00000000 _TEXT    00000000 Private 
  y  . . . . . . . . . . . . . .        DWord             ebp + 000C
  x  . . . . . . . . . . . . . .        DWord             ebp + 0008


Symbols:

                N a m e                 Type       Value     Attr

@CodeSize  . . . . . . . . . . .        Number             0h
@DataSize  . . . . . . . . . . .        Number             0h
@Interface . . . . . . . . . . .        Number             3h
@Model . . . . . . . . . . . . .        Number             7h
@code  . . . . . . . . . . . . .        Text   _TEXT
@data  . . . . . . . . . . . . .        Text   FLAT
@stack . . . . . . . . . . . . .        Text   FLAT
Program  . . . . . . . . . . . .        L Near             Ah _TEXT STDCALL


The assembler has "lost" the language information for the procedure equate "testroutine" and hence doesn't know in which order to push the arguments. The default which it chooses is obviously wrong.
The road to hell is paved with good intentions.

jj2007

  • Member
  • *****
  • Posts: 8429
  • Assembler is fun ;-)
    • MasmBasic
Re: equ routinename works differently than masm
« Reply #5 on: May 10, 2018, 06:48:41 AM »
This is indeed a bug

Hi Japheth,

You can call it a bug, and it should indeed be fixed if UAsm claims to be a 100% compatible MASM clone. But I would give it a low priority, because (and I hope Jim will agree) it's not exactly the best use of the "=" operator.

Below a version that performs the intended action, and behaves identical for ML and UAsm. What is different?

Instead of ...
Code: [Select]
want1=1
ifdef want1

... it uses an assignment that can be used as a switch, and is thus a lot more flexible:
Code: [Select]
wantX=2
if wantX eq 1
    testroutine equ <test1>
elseif wantX eq 2
    testroutine equ <test2>
else
    .err <please make up your mind, your instructions are confusing...>
endif

Why is that better? Because using ifdef is a good recipe for hard to recognise bugs:
Code: [Select]
want1=2
ifdef want1
    testroutine equ <test1>
else
    testroutine equ <test2>
endif

Crystal clear, isn't it? Furthermore, instead of hoping that the assembler somehow guesses that we want to assign a proc's start address when writing testroutine = test1, a simple text equate is being used: testroutine equ <test1>

Code: [Select]
.686
include masm32rt.inc
.data
buff db 100 dup (?)
fmt db "return=%lu",0
.code

test1 proc x,y
    mov edx,x
    ret
test1 endp
test2 proc x,y
    mov edx,y
    ret
test2 endp

wantX=1
if wantX eq 1
    testroutine equ <test1>
else
    testroutine equ <test2>
endif

.code
Program:
  tmp$ CATSTR <My routine is >, <testroutine>
  % echo tmp$
  invoke testroutine,1,2
  invoke wsprintf,addr buff,addr fmt,edx
  print addr buff, 13, 10

  testroutine equ test2
  tmp$ CATSTR <My routine is >, <testroutine>
  % echo tmp$
  invoke testroutine,1,2
  invoke wsprintf,addr buff,addr fmt,edx
  print addr buff, 13, 10

  ; both uasm and masm print exactly the same
  invoke ExitProcess,0

end Program

jimg

  • Member
  • ***
  • Posts: 254
Re: equ routinename works differently than masm
« Reply #6 on: May 10, 2018, 10:29:53 AM »
Without whining, justifying, or obfuscating it further, I feel this is a significant problem and should be corrected.

johnsa

  • Member
  • ****
  • Posts: 680
    • Uasm
Re: equ routinename works differently than masm
« Reply #7 on: May 10, 2018, 06:24:42 PM »
So it seems then as if there are 3 separate issues here:

1) There is a case that causes UASM to crash
2) There is the fact that the language type is not transferred from the proc to the equate causing invoke to incorrectly generate the argument order
3) the ifdef doesn't seem to be picking up the want1=1

I will add these to my todo list for 2.47 :)