0

Why in the code below used macro?I don't understand the notion of macro

This is the code, which converts a decimal string to an octal string (by converting to/from integer):

prnstr macro msg
   mov ah, 09h
  lea dx, msg
   int 21h
endm

data segment
 buf1 db "Vuvedete desetichno chislo: $"
 buf2 db 0ah, "Nevalidno chislo...$"
 buf3 db 0ah, "Chisloto v osmichna broina sistema e : $"
 buf4 db 6
 db 0
 db 6 dup(0)
 multiplier db 0ah
data ends

code segment
assume cs:code, ds:data

start :
 mov ax, data
 mov ds, ax
 mov es, ax

 prnstr buf1
 mov ah, 0ah
 lea dx, buf4
 int 21h

 mov si, offset buf4 + 2
 mov cl, byte ptr [si-1]
 mov ch, 00h

subtract :

mov al, byte ptr [si]
cmp al, 30h
jnb cont1
prnstr buf2
 jmp stop
cont1 :

cmp al, 3ah
jb cont2
prnstr buf2
jmp stop
cont2 :

sub al, 30h
mov byte ptr [si], al
inc si
loop subtract

mov si, offset buf4 + 2
mov cl, byte ptr [si-1]
mov ch, 00h
mov ax, 0000h

calc :
mul multiplier
mov bl, byte ptr [si]
mov bh, 00h
add ax, bx
inc si
loop calc

mov si, offset buf4 + 2
mov bx, ax
mov dx, 0000h
mov ax, 8000h

convert :
mov cx, 0000h
conv :
 cmp bx, ax
 jb cont3
 sub bx, ax
 inc cx
jmp conv
cont3 :

   add cl, 30h
  mov byte ptr [si], cl
  inc si
  mov cx, 0008h
  div cx
  cmp ax, 0000h
 jnz convert

 mov byte ptr [si], '$'
prnstr buf3
prnstr buf4+2
stop :
 mov ax, 4c00h
 int 21h

code ends
end star
Peter Cordes
  • 245,674
  • 35
  • 423
  • 606
  • [invoking macros in MASM](https://stackoverflow.com/posts/comments/70738942) suggests reading chapter 8 of http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/toc.html for MASM directives like macro declarations. Not quite a duplicate question though. – Peter Cordes Jan 06 '19 at 05:16
  • 1
    The code that converts from integer to octal is very inefficient. Octal is a power of 2, so you can simply shift/AND to get groups of 3 bits. `div` is a *very* slow way to divide by 8. The indenting is a mess, and the decimal->integer code is a mess, too. The actual `mul` loop is not terrible, but `digit - '0'` and range-checking could be done on the fly much more efficiently. Related: int->hex in 32-bit asm: [How to convert a number to hex?](https://stackoverflow.com/a/53823757). Pretty easy to port to 8086 if you need to. – Peter Cordes Jan 06 '19 at 05:40
  • A macro is a macro, asm or C they work the same way. Where you find the macro used in the code you substitute. #define plus_one(a) (a+1) then when you see it in the code x = plus_one(3); is replaced with x = (3+1); then compiled. Same with asm. Saves typing that code over and over again. Kinda like an inline function in C as well... – old_timer Jan 06 '19 at 06:31

1 Answers1

1

From MASM 6.1 Programmer's Guide:

A macro is a symbolic name you give to a series of characters (a text macro) or to one or more statements (a macro procedure or function).

When every time you use the macro in the code, like you write prnstr buf2 on some line, it will be replaced during assembling phase with the instructions from the macro definition, i.e. like you would write in original source there three lines:

   mov ah, 09h
   lea dx, buf2    ; argument `msg` was replaced by `buf2` value
   int 21h

So a good macro will save you some typing, and may somewhat help with source readability.

Now generally I frown upon macro usage in assembly, especially by beginners, and I will try to explain reasons why in following text, but the part above is answer to your question and if it didn't answer your question, let me know in comments.

Macros somewhat obfuscate machine code that will be generated, when you are doing code-review of some source, and you see prnstr buf2, you have to open also macro definition to review if the generated code as whole makes still sense, i.e. if the code around didn't expect for example value in dx to be preserved, and then later programmer added prnstr, forgetting that it does modify dx value. So it makes reviewing more cumbersome.

This theme continues also in debugger, in debugger you see disassembled actual machine code, so you will see all those real instruction instead of original macro used in source code. Which may make it a bit more difficult to quickly realize where in (source) code you are, and which part you are debugging and where the instructions came from. Also if the particular macro instruction doesn't fit and you need to "fix" the code, it may be a tiny bit more difficult than changing actual instruction in code (as any change in macro will propagate also to all other places, where it is used).

And also the macro saves only source code lines, but not the final machine code, so by overusing them without understanding the effect fully, you may end with lot larger binary than you expected.

Finally macros are feature of particular assembler (unless the assembler vendor is intentionally trying to be compatible with other vendor, like TASM being capable to compile almost all MASM sources and used that in promo-texts as "selling point"), while the instructions are defined by CPU, so in case your target platform (CPU + OS) has many competing assemblers available, the sources without macros may be more compatible across different assemblers (although often there are also tiny bits of syntax incompatibilities, making "porting" task to require manual editing of source any way).

The particular prnstr seems to me reasonable, although if I would be producing some example for beginners, I would rather still write those instructions every time in code - it's used 5 times, 5+5 lines vs 5x3=15 lines = only 5 lines shorter source, IMO not worth it, as "code writing" is usually only about 10-20% of development time and write-savings which don't also save code reading/review and bugfixing+maintenance (those two are often multiple times larger dev-time than writing) are in the end making things worse.

Common macro (mis)usage pattern of beginners is to mistake them for procedures. For procedures the x86 CPU has native support in form of instructions like call and ret, so in case of longer pieces of code, you can use those instead of macro, they make the code reading a bit easier, as every x86 asm coder should know them, and they are visible in disassembly in debugger in the similar way how source was written.

Now macros are of course big part of what made MASM excellent tool for programmers, so they certainly have also benefits:

  • performance vs procedure call ... the call+ret instruction pair costs a tiny bit of time to execute, while macro injects instructions directly into original flow. If we are talking about some intensive inner-loop code being executed millions of times per second, then macro instead of call may make some measurable difference, sometimes making result better (or worse). But in other cases this worry is pointless with modern x86, as they will execute call/ret within minimal time, and the shorter binary may bring benefits of not clogging the instruction cache so much as bloated expanded code.

  • nice way to produce different variants of code for different kind of builds. You can hide in macro for example different logic of LOG(..) code, being empty in production, but producing debug log in debug builds, or putting third argument for procedure calls into rcx or rdx, depending on target architecture, etc...

  • use of new instruction opcodes (not supported by the assembler yet) in convenient way

And probably many more, macros are powerful tool, sort of making low-level assembly into middle-level programming language.

Then again, if you need Assembly language today, you most often need it to tune performance. That means to rewrite tiny bit of other code written in high level programming language processing the large amount of data (the bottleneck found by profiling of original code), and at that point you usually don't need any "middle-level" features, as you need just to precisely produce the inner-loop "hot" part of code, probably in size of few tens/hundreds of lines of assembly, and connect that into original code, and you will probably not find any heavy use of macros in such case.

I can as well imagine many cases where macros would help (if you are for example writing some micro-benchmarks, and each testing loop has certain identical parts, but you don't want them to be part of "procedure", then macros are perfect fit), etc...

But if you are just learning basics of assembly, you shouldn't need macros at all, rather invest your time into reading more theory about computer architecture and exercising the assembly itself.

Ped7g
  • 15,245
  • 2
  • 24
  • 50