7.2.2. Implementing interworking assembly language subroutines

To implement a simple subroutine call in assembly language you must:

In the case of non-interworking subroutine calls, you can carry out both operations in a single BL instruction.

In the interworking case, where the subroutine is coded for the other state, you must allow for state changes both when calling the subroutine, and when returning from the subroutine.

To call the subroutine and change the processor state, use a BX instruction as described in The Branch Exchange instruction.

Unlike the BL instruction, BX does not store the return address in the link register. You must ensure that the link register is loaded with the return address before you use the BX instruction. If the call is from Thumb code to ARM code you must also set bit 0 in the link register to ensure that the processor executes in Thumb state when the subroutine returns.

Calling an ARM subroutine from Thumb

The simplest way to carry out a Thumb-to-ARM interworking subroutine call is to BL to an intermediate Thumb code segment that executes the BX instruction. The BL instruction loads the link register immediately before the BX instruction is executed.

In addition, the Thumb instruction set version of BL sets bit 0 when it loads the link register with the return address. When a Thumb-to-ARM interworking subroutine call returns using a BX lr instruction, it causes the required state change to occur automatically.

If you always use the same register to store the address of the ARM subroutine that is being called from Thumb, this segment can be used to send an interworking call to any ARM subroutine. The __call_via_r4 procedure in Example 7.2 demonstrates this technique.

Note

You must use a BX lr instruction at the end of the ARM subroutine to return to the caller. You cannot use the MOV pc,lr instruction to return in this situation because it does not cause the required change of state.

If you do not use a BL instruction to call the BX instruction then you must ensure that the link register is updated and that bit 0 is set, either by the calling Thumb routine or by the called ARM routine.

Calling a Thumb subroutine from ARM

When carrying out an ARM-to-Thumb interworking subroutine call you do not need to set bit 0 of the link register because the routine is returning to ARM state. In this case, you can store the return address by copying the program counter into the link register with a MOV lr,pc instruction immediately before the BX instruction.

Remember that the address operand to the BX instruction that calls the Thumb subroutine must have bit 0 set so that the processor executes in Thumb state on arrival.

As with Thumb-to-ARM interworking subroutine calls, you must use a BX instruction to return.

Interworking subroutine call examples

Example 7.2 has an ARM code header and a Thumb code main routine. The program sets up two parameters (r0 and r1), and makes an interworking call to an ARM subroutine that adds the two parameters together and returns.

To build the example:

  1. Type asm -g armadd.s at the system command prompt to assemble the module.

  2. Type armlink armadd.o -o armadd to link the object file.

Example 7.2. 

	AREA  ArmAdd,CODE,READONLY
								; name this block of code.
	ENTRY							; Mark 1st instruction to call.
								; Assembler starts in ARM mode.
main
	ADR	r2, ThumbProg + 1
								; Generate branch target address and set bit 0,
								; hence arrive at target in Thumb state.
	BX	r2						; Branch exchange to ThumbProg.
	CODE16							; Subsequent instructions are Thumb.
ThumbProg
	MOV	r0, #2						; Load r0 with value 2.
	MOV	r1, #3						; Load r1 with value 3.
	ADR	r4, ARMSubroutine						; Generate branch target address, leaving bit 0
								; clear in order to arrive in ARM state.
	BL	__call_via_r4						; Branch and link to Thumb code segment that will
								; carry out the BX to the ARM subroutine. 
								; The BL causes bit 0 of lr to be set.
Stop								; Terminate execution.
	MOV	r0, #0x18						; angel_SWIreason_ReportException
	LDR	r1, =0x20026						; ADP_Stopped_ApplicationExit
	SWI	0xAB						; Angel semihosting Thumb SWI 
__call_via_r4								; This Thumb code segment will
								; BX to the address contained in r4.
	BX	r4						; Branch exchange.
	CODE32							; Subsequent instructions are ARM.
ARMSubroutine
	ADD	r0, r0, r1						; Add the numbers together
	BX	LR						; and return to Thumb caller
								; (bit 0 of LR set by Thumb BL).
	END							; Mark end of this file.

Example 7.3 is a modified form of Example 7.2. The main routine is now in ARM code and the subroutine is in Thumb code. Notice that the call sequence is now a MOV instruction followed by a BX instruction.

Example 7.3. 

	AREA  ThumbAdd,CODE,READONLY					; Name this block of code.
	ENTRY							; Mark 1st instruction to call.
								; Assembler starts in ARM mode.
main
	MOV	r0, #2						; Load r0 with value 2.
	MOV	r1, #3						; Load r1 with value 3.
	ADR	r4, ThumbSub + 1						; Generate branch target address and set bit 0,
								; hence arrive at target in Thumb state.
	MOV	lr, pc						; Store the return address.
	BX	r4						; Branch exchange to subroutine ThumbSub.
Stop								; Terminate execution.
	MOV	r0, #0x18						; angel_SWIreason_ReportException
	LDR	r1, =0x20026						; ADP_Stopped_ApplicationExit
	SWI	0x123456						; Angel semihosting ARM SWI 
	CODE16							; Subsequent instructions are Thumb.
ThumbSub
	ADD	r0, r0, r1						; Add the numbers together
	BX	LR						; and return to ARM caller.
	END							; Mark end of this file.
Copyright © 1997, 1998 ARM Limited. All rights reserved.ARM DUI 0040D
Non-Confidential