Microsoft MakeCode

Makecode assembler: problems parsing subroutine returns?

I haven’t studied assemblers in seriousness so I’m not up on all the mark-ups and complications. Maybe there is something that will solve this problem, I don’t know:

Why is that when I write a function in ARM assembler in my Makecode extension, it will not allow me to have multiple return points from a subroutine? Below is a reduced version of something I’ve got.

Why do I have to jump to the exit point using the b. return rather than just doing a pop {r4, pc} wherever I want? Why should it matter if there multiple exit points from a subroutine?

myRoutine:
    push {r4, lr}
    bl someOtherSubroutine
    adds r4, r4, r0
    beq .zero
.true:
    ldrb r3, [r4, r1]
    orrs r3, r2
    strb r3, [r4, r1]
    subs r1, #1
    bpl .true
    b .return   ;   <<<<< this is an exit point, so why can't I pop {r4, pc} here?
.zero:
    ldrb r3, [r4, r1] 
    bics r3, r2
    strb r3, [r4, r1]
    subs r1, #1
    bpl .zero
.return:
    pop {r4,pc}    ;<<<< the pop command exits the subroutine

What do you mean by “will not allow me”? Do you mean there’s a compiler error? If so, what? And if it’s a run-time error, how is it behaving?

It’s a compiler error. But not specified. When there is an error in the Typescript, the Makecode editor tells you exactly what the problem is even before you press the download butting. Conversely, if you have any C++ code in .cpp files or assembler in .asm files, the Makecode system will not tell you what the errors are or in which lines of code the errors are found. To get that sort of reporting you would need to use an off-line C++ compiler.

Notwithstanding the fact that there is no error reporting, it’s very clear from my experiments that Makecode won’t pass any ARM assembler code which has more than one exit point per subroutine, and that’s something that makes no sense to me since it really ought to be perfectly legitimate to have any number of return points. In much the same way that you can use the return command in Typescript of C++ any number of times within a function.

If you use the pxt build to build it from the command line you get to see the errors.

For any MakeCoder’s the error is:
-> Line 24 (’ pop {r4,pc} ;<<<< the pop comm’), error: stack underflow
At inline assembly:
-> Line 26 (’@stackempty func’), error: stack mismatch

From digging into the process (here https://github.com/microsoft/pxt), it looks like pxt converts .asm files to inline assembly.

The rest of this is partly speculation based on what little of the git repo I’ve skimmed:

  • Somewhere annotations are applied to asm code. I assume this is to ensure the stack isn’t being destroyed.
  • I’m guessing these annotations make the reasonable assumption that there are single entry and exit points. (Probably all other .asm code is generated from typescript and follows consistent practices, so this would be a reasonable sanity check).

Non-speculation / hack: You can insert a hack to mess up the accounting and make the error disappear (because the checks will pass). I don’t know if the code will run as expected, but I’d bet so:

myRoutine:
    push {r4, lr}
    bl someOtherSubroutine
    adds r4, r4, r0
    beq .zero
.true:
    ldrb r3, [r4, r1]
    orrs r3, r2
    strb r3, [r4, r1]
    subs r1, #1
    bpl .true
    pop {r4,pc}   ; <<<<<<<<< Your desired exit
    push {r4, lr}  ; <<<<<<<<<<< Gross hack (to appear to have the same size stack)
.zero:
    ldrb r3, [r4, r1] 
    bics r3, r2
    strb r3, [r4, r1]
    subs r1, #1
    bpl .zero
.return:
    pop {r4,pc}    ;<<<< the pop comm

It’s an interesting question and was worth exploring…On the other hand, I don’t know that this sort of instruction level optimization has much impact and I’d just stick with the single-exit-point (much easier to maintain anyway).

And an alternative: You can put the ASM in a .CPP file as in-line assembly…which probably won’t be checked in this way. The syntax is a bit clunky and you may want to look at the GCC docs…but here’s an example: https://github.com/bsiever/microbit-pxt-timeanddate/blob/master/timeanddate.cpp

Bill