Lately, I have been toying the Microsoft Assembler (MASM), and solving small programming challenges in it to learn about it how works. I typically use the GNU AS assembler because its so portable across instruction sets, and open source.

MASM is really interesting because unlike GNU As, which is simply a target for gcc, MASM is from an era where programmers were the target user. To that end, MASM ships with a lot of interesting features designed to improve the experience for the user including an advanced macro system including control flow logic, a type system, macro’s to build function stack frames, and call other functions. Sadly, with 64-bit assembly, some of the magic has been lost (.invoke macro abandoned).

Here is an example program I wrote with MASM to solve a Challenge to order digits in a number in reverse.

There is a UASM project that brings some of this to Linux, including support for (.invoke) macro calling functions with the SYS-V ABI.

Post your favorite assembly tools, your experiences with MASM, and what kind of Macro systems you use if any. Should I learn M4 to bake on top of GNU AS?

.CODE
    option casemap:none
    option prologue:PrologueDef
    option epilogue:EpilogueDef
    public maxpermute
 
;;
; @brief Permute the digits of the number such that number returned as digits
;        in descending order
maxpermute proc n:qword
    local    digit_arr[10]:byte
; prologue automatically written
    xor    eax,eax
    cmp    rcx, 0
    jz    @out
; save
    mov    [RBP+24], rdi ; save in shadow of rdx
    mov    n, rcx        ; save n in shadow space
; zero the digit count array (al=0 here already)
    lea    rdi, digit_arr
    mov    ecx, sizeof digit_arr
    rep    stosb
; Walk the number computing digits
    mov    rax, n
@@:
    xor    edx, edx ; zero edx
    mov    ecx, 10
    div    rcx      ; edx contains next digit
    inc    BYTE PTR [digit_arr+rdx]    
    test    rax, rax
    jnz    @B
; Process the digits in descending order
    xor    rax, rax ; accumulator
    mov    ecx, 9; offset to last digit
@outer:
    movzx rdi,BYTE PTR [digit_arr +rcx] ; fetch digit count
@inner: ; apply 10*acc + digit transform , rdi times
    test rdi, rdi
    jz @continue_outer    
    mov  r8, rax
    shl  r8, 3
    lea  rax, [rax*2]
    add  rax, r8
    add  rax, rcx
    dec  rdi
    test rdi, rdi
    jnz @inner
@continue_outer:
    loop @outer
    mov    rdi, n
@out:
    ret    
maxpermute endp
    END