News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

How Does str$ In Macros.asm Work?

Started by Fred Harris, January 14, 2016, 01:48:13 AM

Previous topic - Next topic

Fred Harris

Here is the MACRO from about in the middle of Macros.asm...


; ----------------------------------------------------------
; function position macros that takes a DWORD parameter and
; returns the address of the buffer that holds the result.
; The return format is for use within the INVOKE syntax.
; ----------------------------------------------------------
str$ MACRO DDvalue
  LOCAL rvstring
  .data
    rvstring db 20 dup (0)
    align 4
  .code
    invoke dwtoa,DDvalue,ADDR rvstring
    EXITM <ADDR rvstring>
ENDM


Actually, I think I'm good with everything up to the end where it says..


EXITM <ADDR rvstring>


I've looked up the EXITM keyword in my old copy of Kip Irvine's "Assembly Language", and it
apparently means Exit Macro.  But what use is it there right before the ENDM (End Macro)
keyword?  There really isn't any code to be short circuited.  The macro would have ended anyway?
And in terms of this...


<ADDR rvstring>


...I can't even find that at all in Kip's book. 

If I had to take a wild guess at what's going on, based on my limited understanding at this point,
and based on the comments at the top of the code, I'm thinking that somehow this macro is returning
a value, although that concept goes beyond anything I've read about macros.  My understanding of
macros is that they are text substitution engines, i.e., the body of the macro is inserted at the
macro call in the source code by the pre-processor.  I didn't know they could return values like
functions do.  I'd appreciate if someone could elaborate on this point.   

Assuming I'm somehow right about that, it would reconsile very well with the text comments at top...

Quote
function position macros that takes a DWORD parameter and
returns the address of the buffer that holds the result.
The return format is for use within the INVOKE syntax.

So what I'm seeing is rvstring is one of those auto-generated MACRO Locals, and the...

rvstring db 20 dup(0)

line causes a 20 byte buffer in the data segment to be created/allocated.  I suppose the align 4
statement enforces DWORD alignment. Then into the code segment we come to this..

invoke dwtoa, DDvalue, ADDR rvstring

That's not a MACRO but a function call exported presumably from masm32.lib where the 2nd parameter
is the buffer address of where the number converted to a z string should go...

dwtoa proc dwValue:DWORD, lpBuffer:DWORD

...and we provide the base address of rvstring in the data segment we just created with 20 dup(0). 
And then we hit where I'm stumped...

EXITM <ADDR rvstring>

I'd really appreciate an explanation of that, couched perhaps in terms of str$()'s use in this
macro call jj2007 provided to me last week...

MsgBox 0, cat$(str$(eax), " is the result"), "Hi:", MB_OK

I do apologize if I'm asking too much here. 

hutch--

Fred,

the EXITM notation is a high level simulation that MASM allows so that you can return a value from a macro ande use the macro something like a function.

str$ MACRO DDvalue                                       ; create the macro
  LOCAL rvstring                                               ; create an anonymous name that will not be duplicated
  .data                                                              ; start an initialised DATA section
    rvstring db 20 dup (0)                                 ; allocate space for a string of a known length
    align 4                                                           ; align the code section again
  .code
    invoke dwtoa,DDvalue,ADDR rvstring         ; call a procedure
    EXITM <ADDR rvstring>                              ; return the OFFSET using the ADDR of rvstring to the caller
ENDM                                                               ; end the macro

dedndave

it means the placement of the macro name may replace something else (my own words - lol)

one common example, is when used with the "print" macro
normally, the print macro wants an address

    print   offset szString
    print   ebx               ;EBX = address of string


but, "str$" may be used to substitute

    print   str$(edx)

in this case, the str$ macro provides the string address to the print macro

you can also do this

    mov     ebx,str$(edx)

EBX now holds the address of the string

don't feel bad - macros are hard to learn - lol
i can write simpler ones, but don't even begin to understand the more complicated ones
and, i've been writting MASM code for over 30 years   :lol:

Fred Harris

Thanks Hutch and Dedndave!  Been working at it hard and losing a lot of hide!  But the full realization hit me quite some time ago that the macro part of 'macro assembler' is of some not inconsiderable importance.  In other words, if one wants to learn masm macros can't be ignored.  And they come hard for me because I've never been a big user of them in any of my C or PowerBASIC coding.  Occasionally I use single line macros, and that's it.  So yea, I'll be asking more dumb questions I'm afraid!  :(

hutch--

No problems Fred, the pre-processor in MASM is a cantankerous old pig that irked everybody while learning it but its because MASM has never been seen by Microsoft as a consumer product so they simply don't care if its hard to use. It is a good capacity once understood and particularly for an experienced programmer it gives you much of the capacity to design your own language.

jj2007

Quote from: hutch-- on January 14, 2016, 11:00:30 AM... it gives you much of the capacity to design your own language.

Some examples here ;-)

"cantankerous old pig" is an understatement, of course 8)

Fred Harris

I had been working on this str$() thing because it was showing up in a lot of print examples I've been seeing.  What I've basically been working on in my attempts to learn masm is console output.  I figured that is kind of a 1st fundamental step.  I've found piles of ways to output stuff to the console, but what I've found so far is that the printf macro or crt_printf function suits me best.  I say that because I can use it exactly like in C, e.g.,


mov dwNumber, 5
printf("dwNumber = %u\n",dwNumber)
   

Output:
dwNumber = 5

While I love basic family languages I haven't found as much success with the print macro.  I can't seem to get it, or StdOut, or anything else to do what  I want, which is output exactly like above where I have a leading quoted string literal followed by the value to be output.  Here are some of my many attempts...


; C:\Code\MASM\Projects\Demo3>ml /c /coff /Cp Test8.asm
; C:\Code\MASM\Projects\Demo3>link /SUBSYSTEM:CONSOLE Test8.obj
include \masm32\include\masm32rt.inc
IncrOne proto iNum:DWORD

.data
CrLf     db 13, 10, 0

.data?
dwNumber dd ?

.code
start:
  mov dwNumber, 5        ; assign 5 to dwNumber
  push OFFSET dwNumber   ; push address dwNumber on stack
  call IncrOne           ; call IncrOne
  mov dwNumber, eax      ; copy result to eax

  print cat$(str$(dwNumber), " is the result", OFFSET CrLf)              ; assembles, runs OK
  invoke crt_printf, cat$(str$(dwNumber), " is the result", OFFSET CrLf) ; assembles, runs OK
  printf(cat$(str$(dwNumber), " is the result", OFFSET CrLf))            ; assembles, runs OK
  invoke StdOut, cat$(str$(dwNumber), " is the result", OFFSET CrLf)     ; assembles, runs OK
  printf(str$(dwNumber))                                                 ; assembles, runs OK
  print ADDR CrLf                                                        ; assembles, runs OK
  ; printf(cat$("dwNumber = ", str$(dwNumber), OFFSET CrLf))             ; won't assemble
  print str$(dwNumber)                                                   ; assembles, runs OK
  print ADDR CrLf                                                        ; assembles, runs OK
  ; print "dwNumber = "  str$(dwNumber)                                  ; won't assemble
  ; print "dwNumber = ", str$(dwNumber)                                  ; won't assemble
  invoke StdOut, str$(dwNumber)                                          ; assembles, runs OK
  print ADDR CrLf                                                        ; assembles, runs OK
  invoke crt_getchar
  exit

  IncrOne proc pNum:DWORD
    mov eax, pNum
    mov eax, [eax]
    add eax, 1
    ret
  IncrOne endp
end start

6 is the result
6 is the result
6 is the result
6 is the result
6
6
6


In other words, '6 is the result' doesn't suit me.  But 'result = 6' would.  I know I'm being kind of ridiculous, but I guess I'm picky like that.  You can see in my commented out code above where I've tried it, but it won't assemble.  Would anyone know how to get that result with print?

Also, every time str$() appears in the source code that rvstring db 20 dup(0) thing in the str$() macro is going to add 20 bytes to the data segment, right???  That strikes me as an odd way to allocate memory if that's what is happening.  Kind of like calling a memory allocation function every time you need memory but never releasing any.  And if it is, what happens if a str$() call is put in loop code?  Will that just use the same 20 bytes for every call, or allocate another 20 bytes for every iteration of the loop?  I can't imagine how that could be, actually.

And I guess one more somewhat unrelated question.  Ultimately, whether we call printf, print, StdOut, or maybe even some I've missed, WriteConsole() or WriteFile() has to be used, right?  I'm guessing that call must be in the assembled masmlib.lib file?

Fred Harris

And seeing as I was worried some about the code expansion in the data segment that might occur with use of the str$() and perhaps other similiar macros, and noting that str$() internally called dwtoa to do the conversion, I tried that directly with good success.  And that helped me understand better the invoke verses call / ret distinction....


; C:\Code\MASM\Projects\Demo3>ml /c /coff /Cp Test9.asm
; C:\Code\MASM\Projects\Demo3>link /SUBSYSTEM:CONSOLE Test9.obj
include \masm32\include\masm32rt.inc
IncrOne proto iNum:DWORD

.data
CrLf     db 13, 10, 0
lpBuffer db 20  dup(0)

.data?
dwNumber dd ?
RetVal   dd ?

.code
start:
  mov dwNumber, 5        ; assign 5 to dwNumber
  push OFFSET dwNumber   ; push address dwNumber on stack
  call IncrOne           ; call IncrOne
  mov dwNumber, eax      ; copy result to eax

  printf("dwNumber        = %u\n",dwNumber);
  push OFFSET lpBuffer
  push dwNumber
  call dwtoa
  ;invoke dwtoa, dwNumber, OFFSET lpBuffer
  printf("OFFSET lpBuffer = %s\n",OFFSET lpBuffer)
  invoke StdOut, OFFSET lpBuffer
  invoke crt_getchar
  exit

  IncrOne proc pNum:DWORD
    mov eax, pNum
    mov eax, [eax]
    add eax, 1
    ret
  IncrOne endp
end start

; Output
; ===================
; dwNumber        = 6
; OFFSET lpBuffer = 6
; 6


jj2007

Quote from: Fred Harris on January 15, 2016, 05:40:44 AMWhile I love basic family languages I haven't found as much success with the print macro.

Print Str$(...) does what you want, and a few things more...

Print Str$(ebx)                             ; simple
Print Str$(MyReal10)                        ; works for most kinds of arguments
Print Str$(ebx*MyReal8+123.0e45)                  ; multiply two arguments, add an immediate float
MsgBox 0, Str$("Profits increased by %3f per cent", 103.45/100-1*100), "Three digits precision:", MB_OK
Print Str$("The number PI is %Jf", PI)            ; precision J = 19 digits, f=float
Print Str$("The number PI is %Je", PI)            ; precision J = 19 digits, e=force exponential
Print Str$("The number 19 is %__i", 19)           ; integers only: two understrokes to get one leading space with a two-digit number
Print Str$("The number 9 is %000i", 9)            ; integers only: 3 zeros to get four leading zeros with a one-digit number

dedndave

the macros are quite handy for simpler programs
and - i recall, they do take a little getting used to

there are some associated macros, too
ustr$, left$, right$, and so on

in the latest release of the masm32 package, i found a little bug in the ustr$ code
you can modify your macros.asm file to fix it...

http://masm32.com/board/index.php?topic=1811.msg33372#msg33372

Jochen gave you several ways to use str$

let me give you one that really makes life easy if you only want to display a byte or word of hex

    mov     eax,0FFFF1234h
    print   right$(uhex$(eax),4),13,10


here's another

    mov     eax,0FF12FFFFh
    print   left$(right$(uhex$(eax),6),2)

dedndave

oh - and, there is a file of interest

masm32\help\hlhelp.chm

hutch--

Fred,

> In other words, '6 is the result' doesn't suit me.  But 'result = 6' would.

Its basically simpler to use a few "print" macros than call the code to do a CAT$ style operation. To do what you want is just a little more work.


    print "Result = "
    print str$(whatever),13,10


If you don't append the 13,10 at the end of the 1st print line then the following print line displays on the same line as the first one at the console.

Fred Harris

Thanks jj207, DednDave, and Hutch.  Will try some more today!