1
 
 

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
2
 
 

cross-posted from: https://sopuli.xyz/post/45002077

If this isn't the right community for this post, let me know and I'll move it somewhere else.

I'm working my way through nandgame, and I'm stuck on the "call" macro in the function calls section of the stack machine unit. These are the instructions:

I was stuck on it for a while, so I looked up the solution and am going off the one given here (it's the only one I could find).

So far, I haven't gotten it to work. At first, it was giving me syntax errors because the labels weren't defined, so I replaced A = [LABEL] with label [LABEL] followed by A = [CONST], according to the specified calling convention. It also gave me a similar syntax error for TEMP_ADDR, which is never specified in the instructions, so I assigned it 0x7f00. So here is what I end up with (all in Assembly):

(note: the stack pointer, SP, is defined by a shared constant SP = 0)

Test code:

init.stack
call functionName 0
stop

function FunctionName 0
push.value x42
return

Macro: call

# Assembler code
## Push the current ARGS and LOCALS on the stack
push.static ARGS
push.static LOCALS

## Push the return address.
push.value after

## Set ARGS to point to the start of the arguments
A = SP
D = *A
A = argumentCount
D = D - A
A = 3
D = D - A
label ARGS
A = 1
*A = D

## Jump to functionName.
goto functionName

## Set return address:
label after

## Restore ARGS and LOCALS from the stack
A = ARGS
D = *A
label TEMP_ADDR
A = 0x7f00
*A = D
pop.D
label LOCALS
A = 2
*A = D
pop.D
A = ARGS
*A = D

## Push RETVAL.
label RETVAL
A = 6
D = *A
push.D

Macro: function

# Assembler code
## Define a label functionName
label functionName

## Set LOCALS to the current SP
A = SP
D = *A
label LOCALS
A = 2
*A = D

## Advance SP by localsCount
A = localsCount
D = A
A = SP
D = D + *A
*A = D

Macro: return

# Assembler Code
## Pop the top value into RETVAL
pop.D
label RETVAL
A = 6
*A = D

## Set SP to LOCALS
label LOCALS
A = 2
D = *A
A = SP
*A = D

# Pop the return address and jump to it
pop.D
A = D
JMP

Note: functionName, argumentsCount, and localsCount are all listed as placeholders above the relevant macro, but it doesn't describe how to define these. I'm guessing that comes in a later unit. The code block for the call macro gives me a red bar for each line with a placeholder, but this doesn't happen for the function macro which also includes placeholders. In any case, this doesn't seem to get in the way of the program as it still runs smoothly.

So as it is, when I run it, everything seems to work as intended. This is the result when it reaches the stop macro (infinite loop):

Which looks like it's doing everything it's supposed to do. But when I click "Check solution", this is what it says:

("Expected SP (RAM address 0) to be hex 101. (Was 104)")

Which is strange, because in the computer section you can clearly see that the SP content is hex 0101. So I don't know what's going wrong.

I stepped through the program tick by tick, and the only place SP ever reaches 104 is after the function macro, when it runs push.value x42, after which it runs the return macro which begins with pop.D, reducing SP back to 103.

Could it be the evaluator is assessing the value of SP at the end of the code block, instead of where it runs the stop macro? So perhaps I could try replacing stop with a jump to the end, followed by stop... Now that I think of that, I think it might work. I'm still going to post this though, because I already went through all the trouble. And in case it doesn't work, if anyone else has any ideas please share them!

I can expand the rest of the macros if need be, but they already passed the evaluation so they're all working according to specs.

Thanks in advance!

Edit:

I thought for sure that would work. I changed the test code block to this:

init.stack
call functionName 0
A = end
JMP

function FunctionName 0
push.value x42
return
label end
stop

The program runs exactly as it's supposed to, except now the stop loop is at the end of the code. The computer output still looks the same, except now the program counter loops between 60 and 61 at the end (as expected). But the evaluator is still giving me the same error! I'm stumped...

Edit 2: added annotations to code blocks to make it easier to read

3
 
 

Relevant part

Learn 6502 Assembly is a native GNOME app on Flathub as a learning environment around 6502 Assembly - the Assembly version used by the Apple II, Commodore 64, and Nintendo NES. Via various vintage games you can learn to program in Assembly.

4
5
6
7
 
 

Original title screen for Sinistar, now re-inserted into the game during the reverse-engineering project.

This is an article about reverse-engineering the 1983 arcade classic Sinistar. Sinistar is one of the most technically-impressive arcade games of the 1980s, and one of the first to use voice samples. The game is notoriously difficult, and the author, SynaMax, wanted to rebuild the game with an easier difficulty, and also restore stereo sound.

Since the original assembler for Sinistar is considered lost, SynaMax re-wrote the source code to target the Macro Assembler. One of their accomplishments was restoring the original title screen for Sinistar (see above) from the 1982 prototype release.

8
9
Welcome to Assembly! (programming.dev)
submitted 3 years ago* (last edited 3 years ago) by [M] to c/asm@programming.dev
 
 

I have put lots of resources for assembly and LC3 systems in the sidebar.

Have an awesome day :)