ARM Assembly Subroutines And Returning From A BASIC CALL Of An Assembly Code
Angel Perez (2908) 84 posts |
I am practicing the use of subroutines in an ARM assembler code in BASIC. The subroutines are said to be ended with a: MOVS R15,R14 call for each subroutine starting with the label: .some_subroutine But when I want to end the routine that calls subroutines within it, if I use: MOV R15,R14 the program tends to hang rather than to exit from that routine. I cannot use OS_Exit because it brings the entire program to an abrupt conclusion instead of continuing to the next BASIC line. Any example of routine with subroutines I can use as a guide? |
Clive Semmens (2335) 3276 posts |
Assuming you’ve done nothing to corrupt R14, it’s not the return that’s causing the hang: that’s a perfectly good return instruction. (Oh for Permission to Delete – how embarrassing!) |
Clive Semmens (2335) 3276 posts |
Rick’s following post is much more useful than this was. |
Rick Murray (539) 13840 posts |
Are you saving R14 before using it again?
This will fail because the exit address is in R14 on entry, and the BL call trashes that, so your program cannot ever exit as the return address is gone. Consider:
If you follow through this code, you’ll see that the line that it supposed to return to BASIC is the instruction following the BL, which means R14 will point to it, so it’ll just keep jumping to itself <cue spooky voice>forever!!!!! How to fix this? You need to preserve R14 prior to it being used again. Like this (assuming R13 is a valid stack, it is from BASIC):
The weird looking offsets are to write-back R13 to support a fully descending stack. The STR’s “ You might have come across it like this, but these days it is inefficient to use a multiple register instruction to store and load single registers (and indeed, ARM64 doesn’t support STM/LDM at all!).
|
Steve Drain (222) 1620 posts |
How sure about this are you? I can doubt my understanding, but Certainly, Anyway, for ease of programming I think it is preferreable to stick with |
Rick Murray (539) 13840 posts |
Using STMFD is simpler, yes. However it doesn’t dual issue and… well take a look at about halfway down http://www.heyrick.co.uk/blog/index.php?diary=20150727 That said, with objasm it is easier to have an ENTER and LEAVE macro (or whatever) and let the assembler generate the appropriate instruction. ;-) |
Steve Drain (222) 1620 posts |
I had a look at your blog. I can see that there is a small gain, but the one thing in the DeskLib example that made the big difference was using a I have written quite a number of BASIC Assembler macros, but I find myself writing raw code more often than not – probably it is more satisfying. However, I think I will look to see if ENTER and LEAVE can be done, because it would avoid that easily introduced bug of mismatched registers. |
Jeffrey Lee (213) 6048 posts |
If your assembler supports PUSH and POP then it should automatically switch between LDM/STM and LDR/STR depending on whether one or many registers need to be transferred (they are basically just aliases for the corresponding load/store instruction). Of course the downside is you can’t specify the base register or whether writeback is used, so they’re only good for basic stack interactions.
Possibly you’re thinking of AArch64 – there is no LDM/STM, only the single register LDR/STR and double register LDP/STP. Or maybe you’re thinking about the fact that modern ARMs allow interrupts to occur in the middle of an LDM/STM – if this happens then the ARM will point the exception return address at the LDM/STM so that the entire instruction is restarted once the interrupt has been dealt with (it also makes sure the base address register hasn’t been updated, but the state of the other registers/memory locations used by the instruction are undefined – so LDM/STM is no longer atomic from a single-CPU perspective) |
Steve Drain (222) 1620 posts |
I have searched back, and it was the StrongArm that decomposed |
Rick Murray (539) 13840 posts |
This is clever: https://www.riscosopen.org/viewer/view/castle/RiscOS/Sources/Programmer/HdrSrc/hdr/Macros?rev=4.13#l1317
|
Jeffrey Lee (213) 6048 posts |
That’s what Entry and EXIT (and friends) are for: https://www.riscosopen.org/viewer/view/castle/RiscOS/Sources/Programmer/HdrSrc/hdr/Proc?rev=4.6.2.1;content-type=text%2Fx-cvsweb-markup#l93
If you’re observant you’ll also spot that there’s an ENTRY macro which is equivalent to Entry, but that one isn’t used any more because objasm confuses it with the ENTRY directive. |
Rick Murray (539) 13840 posts |
Gah! I knew I’d seen it. Thanks for showing me where. And thank you also for explaining the other functions. I ought to make myself a small header file with some of this stuff in it. (no, I don’t use the full build system, I like things where I can find them ;-) ). |
Chris Evans (457) 1614 posts |
Sorry if I’ve got the wrong end of the stick but does the assembler Macro/Routine that Jeffrey has pointed to work out which registers are used in a called routine and push/pull to/from the stack just those registers that are corrupted? That would be neat! |
Jeffrey Lee (213) 6048 posts |
No, that would be magic :-) (More commonly known as a C compiler) You need to tell the “entry” macros what registers to save. The “exit” macros pop the registers that were saved by the preceeding “entry” (in terms of the order in the source file – so you if you jump from the middle of one routine directly to the middle of another then you’ll be in for a bad time) |
Steve Fryatt (216) 2105 posts |
Are these documented anywhere? The index of the printed Acorn Assembler manual says Page 72, but unless I’m missing something there’s nothing relevant on that page – or anywhere else that I can see. |
Jeffrey Lee (213) 6048 posts |
Looks like a mistake in the indexing. Searching the PDF version of the manual reveals that they are mentioned in a few places (including page 72), but the most useful place is probably in the “UAL differences” part of appendix A, where it explains that they’re just aliases for LDM/STM/LDR/STR. |
Steve Drain (222) 1620 posts |
I look things up through: http://infocenter.arm.com/help/index.jsp There I found:
I have lost the example where I found only a single register, so that may be false, except that it gets converted to |
Chris Evans (457) 1614 posts |
I did realise it would need multipasses. I presume given the move to higher level languages no one is going to invest the time in such enhancements. |
Rick Murray (539) 13840 posts |
It shouldn’t be too hard to write some code that would trip up such a scheme. One I can think of is APCS veneers where I don’t tend to stack things unless necessary, so I may have a function that stacks or not depending on the code path. |
Chris Evans (457) 1614 posts |
That would be where the magic comes in. Though a human will probably be more unreliable! |