32-bit factorial calculator in x86 NASM assembly
There are a couple of things that may be of use to you for this code, and some general comments that should be useful for any assembly language programs you write in the future.

Make it modular

The code that implements the factorial routine does a je display_result to exit, but that needlessly ties it to the display routine.  In assembly language, it's generally better to use either a ret to return (implying it's written as a subroutine) or to have the routine "fall through" the end.  Either is acceptable, but implementing as subroutines is more common.

Prefer subroutines to macros

The macros you've got might, underneath, result in a subroutine call to some code other than that which you've posted, or more likely, it implements directly the code used to perform those functions.  There might be a slight performance advantage to replicating the code multiple times via a macro invocation, but more often, it just leads to code bloat.  (Although even with this "bloat" it's probably smaller than the corresponding C program!)

Comment fully

If we look at the comment for calc_factorial it currently reads this way:

    ; EAX - current value
    ; EBX - counter
    ; ECX - current total (for multiplication)
    ; EDX - counter (for multiplication)


This may be accurate, but which are inputs?  Which are outputs?  What does the routine do?  I'd recommend a comment style something more like this:

    ;****************************************************************************
    ; 
    ; PrintString 
    ; prints the string at DS:DX with length CX to stdout
    ;
    ; INPUT:     ds:dx ==&gt; string, cx = string length
    ; OUTPUT:    CF set on error, AX = error code if carry set
    ; DESTROYED: ax, bx
    ;
    ;****************************************************************************


Use mul rather than add

The mul instruction is definitely going to be faster than the corresponding loop with add instructions.  The mul instruction as a 32-bit instruction multiplies EAX by some other register, yielding a result in EDX:EAX meaning that the high part of the result will be in EDX and the low 32 bits of the result will be in EAX.

Structure code to avoid unconditional jumps

If we look at the code, (with comments removed for clarity) we can see that the code does a jmp mult_with_addition but it's jumping over an instruction that cannot be reached!  For that reason, you could eliminate both jmp instructions.  

    calc_factorial:
       dec   EBX
       cmp   EBX, 1
       je    display_result

       mov   ECX, EAX
       mov   EDX, EBX
       jmp   mult_with_addition

       jmp   calc_factorial

    mult_with_addition:
       dec   EDX
       cmp   EDX, 0
       je    calc_factorial

       add   EAX, ECX

       jmp   mult_with_addition


Minimize register moves

As much as practical, try to minimize register moves, preferring instead to have the data already in the register you need for instructions or subroutines which have register-specific needs.  For example, because the mul instruction uses EDX and EAX, it makes sense to try to make sure that the number being multiplied is already in that register pair.

Don't fail silently

As with any code in any language, failing silently must be avoided.  In the case of a factorial operation, an overflow condition should be checked and passed back to the calling code.  If you use the mul instruction, and return a 32-bit result in EAX, checking for overflow is as simple as checking EDX for a zero value.

Look carefully at instruction encodings

In one place in your code, you have the instruction cmp EDX,0 followed by a conditional jump based on the Z flag.  In 32-bit mode, that instruction is probably coded as 83FA00 but if you had used or EDX,EDX it is likely to be coded as 09D2 which is one byte shorter.  However, neither are really needed because the dec EDX instruction before it (encoded as 4A) already sets the Z flag. There may be no reason to care about one or two bytes one way or the other, but if that's the case, why code in assembly at all?  If you're going to do it, do it well.