This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/MSP430F5529: Proper function calls in assembly

Part Number: MSP430F5529

Tool/software: Code Composer Studio

I am working on a problem where I have to do multiplication, and then place the results into an array.  I am doing this with nested subroutines.  I am calling my subroutine incorrectly somehow, I believe.  I am also unsure how to dump my product value back into the current array address.  If I could get some help on this, I would greatly appreciate it.  Thank you for your time in advance.

        .cdecls C,LIST,"msp430.h"       ;Include device header file

        .def    RESET                   ;Export program entry-point to
                                        ;make it known to linker.
        .def	calc_power
        .def	SW_Mult
        .def	HW_Mult

		.text                           ;Assemble into program memory.
		.retain                         ;Override ELF conditional linking
        		                        ;and retain current section.
		.retainrefs                     ;And retain any sections that have
        		                        ;references to current section.
		.data
b:		.int 	2						;Create variable and initialize it to 2
val:	.int	2						;Create variable for product placement init 0

RESET:  mov.w   #__STACK_END,SP         ;Initialize stack pointer
        mov.w   #WDTPW|WDTHOLD,&WDTCTL  ;Stop watchdog timer

;-------------------------------------------------------------------------------
; Main loop
;-------------------------------------------------------------------------------
main: 	mov.w	#hwarr, R7	;starting address of hwarr to R7
		mov.w	#swarr, R8	;starting address of swarr to R8
		clr.w	R9

hwnext:	mov.w	@R7+, R9	;get next hwarr element
		cmp		#0, R9		;is it a null?
		jeq		swnext		;if yes, go to swnext
		call	calc_power	;calculate powers of 2

swnext:	mov.w	@R8+, R9	;get next swarr element
		cmp		#0, R9		;is it a null?
		jeq		lend		;if yes, go to end

calc_power:
		call	HW_Mult
		mov.w	#val, R9

HW_Mult:
		mov.w	b, &MPY		;move b to R5
		mov.w	val, &OP2	;move val to R6
		nop					;3 clock cycles
		nop
		nop
		mov		RESLO, &val	;put product in val variable
		ret
SW_Mult:

hwarr:	.int	2, 2, 2, 2, 2	;hw mult array
swarr:	.int	2, 2, 2, 2, 2	;sw mult array

lend:	nop
;-------------------------------------------------------------------------------
; Stack Pointer definition
;-------------------------------------------------------------------------------
        .global __STACK_END
        .sect   .stack

;-------------------------------------------------------------------------------
; Interrupt Vectors
;-------------------------------------------------------------------------------
         .sect   ".reset"               ; MSP430 RESET Vector
         .short  RESET
         .end

  • I forgot to mention it is all to be done in assembly language for this processor.

  • When you use a C compiler, it handles the details of parameter passing. Some are passed in registers and if that doesn't provide enough space, the stack is used. In assembly, it is up to you and you can do it any way you like.

    The compiler groups the registers into two groups. One group must be save and restored before exit so that the calling programs data isn't mangled. The other group can be clobbered with no consequences. You can do something similar but it is up to you to manage it.

    All the details belong to you.

    One detail you have messed up is sections. You start out with text (.text directive) but switch to the data section and never return to text. Which means your code is going into a data section.

    Data sections are typically placed in SRAM. Which is going to be a problem for your code. In addition, initialized data will be a problem. The C runtime copies initial values into variables at startup but you are on your own.

    Before doing anything so complex, you should first get a program to blink an LED running. Then add to it.

  • I have already done the LED blinking, and it is very different from what I am doing here.  I have made some changes to my code to try and do it with the stack pointer instead.  I am only interested in making the HW multiplier work as a nested subroutine.  I will work on the SW multiplier after this is working.

            .cdecls C,LIST,"msp430.h"       ;Include device header file
    
            .def    RESET                   ;Export program entry-point to
                                            ;make it known to linker.
    
    		.text                           ;Assemble into program memory.
    		.retain                         ;Override ELF conditional linking
            		                        ;and retain current section.
    		.retainrefs                     ;And retain any sections that have
            		                        ;references to current section.
    calc_power:
     							; save the registers on the stack
    		push 	R7 			; save R7, temporal product
    		push 	R6 			; save R6, array length
    		push 	R4 			; save R4, pointer to array
    		clr.w 	R7 			; clear R7
    		mov.w 	10(SP), R6	; retrieve array length
    		mov.w 	12(SP), R4 	; retrieve starting address
    lnext:
    		mov.w 	@R4+, R7	; move to next element
    		cmp 	#0, R7 		; is it null
    		jeq 	lend 		; if yes, go to end
    		call	HW_Mult
    		mov.w	#val, R4
    		jnz		lnext
    lend:
    		pop 	R4			; restore R4
    		pop 	R6 			; restore R6
    		pop 	R7 			; restore R7
    		ret 				; return
    
    HW_Mult:
    		mov.w	b, &MPY		;move b to slot 1
    		mov.w	R7, &OP2	;move R7 to slot 2
    		nop					;3 clock cycles
    		nop
    		nop
    		mov		RESLO, &val	;put product in val
    		ret
    
    		.data
    b:		.int 	2						;Create variable and initialize it to 2
    val:	.int	0						;Create variable for product placement init 0
    
    RESET:  mov.w   #__STACK_END,SP         ;Initialize stack pointer
            mov.w   #WDTPW|WDTHOLD,&WDTCTL  ;Stop watchdog timer
    
    ;-------------------------------------------------------------------------------
    ; Main loop
    ;-------------------------------------------------------------------------------
    main:
    		push 	#hwarr 		; push the address of hwarr
    		push 	#5 			; push the number of elements
    		sub.w 	#2, SP 		; allocate space for the product
    		call 	#calc_power
    		mov.w 	@SP, &P1OUT ; store the sum in P2OUT&P1OUT
    		add.w 	#6,SP 		; collapse the stack
    next:
    		push 	#swarr ; push the address of hwarr
    		push 	#7 ; push the number of elements
    		sub 	#2, SP ; allocate space for the sum
    		call 	#calc_power
    		mov.w 	@SP, &P3OUT ; store the sume in P4OUT&P3OUT
    		add.w 	#6,SP ; collapse the stack
    
    		jmp		$
    		nop
    
    hwarr: .int		2, 2, 2, 2, 2 ; the first array
    swarr: .int	 	3, 3, 3, 3, 3 ; the second array
    
    ;-------------------------------------------------------------------------------
    ; Stack Pointer definition
    ;-------------------------------------------------------------------------------
            .global __STACK_END
            .sect   .stack
    
    ;-------------------------------------------------------------------------------
    ; Interrupt Vectors
    ;-------------------------------------------------------------------------------
             .sect   ".reset"               ; MSP430 RESET Vector
             .short  RESET
             .end
    

  • If you want to see a complete and fairly large example of assembly language for gcc, see:

    I even use the hardware multiplier.

  • Thank you very much!  Those examples helped me figure out the last bit I was doing wrong.  I have a fully functional hardware multiplier that places the products into an array successfully.  I am now working on getting a SW multiplier working (using a shift-and-add algorithm) in the same program for a second array.  If you have any input on that, I would greatly appreciate it, as well.  Here is my updated code with the working HW multiplier:

            .cdecls C,LIST,"msp430.h"       ;Include device header file
    
            .def    RESET                   ;Export program entry-point to
                                            ;make it known to linker.
            .def	hcalc_power
            .def	scalc_power
            .def	SW_Mult
            .def	HW_Mult
    
    		.text                           ;Assemble into program memory.
    		.retain                         ;Override ELF conditional linking
            		                        ;and retain current section.
    		.retainrefs                     ;And retain any sections that have
            		                        ;references to current section.
    		.data
    b:		.int 	2						;Create variable and initialize it to 2
    hval:	.int	1						;Create variable for product placement init 0
    sval:	.int	1						;Create variable for product placement init 0
    hwarr:	.int	2, 2, 2, 2, 2			;hw mult array
    swarr:	.int	1, 1, 1, 1, 1			;sw mult array
    
    RESET:  mov.w   #__STACK_END,SP         ;Initialize stack pointer
            mov.w   #WDTPW|WDTHOLD,&WDTCTL  ;Stop watchdog timer
    
    ;-------------------------------------------------------------------------------
    ; Main loop
    ;-------------------------------------------------------------------------------
    main: 	mov.w	#hwarr, R7	;starting address of hwarr to R7
    		mov.w	#swarr, R8	;starting address of swarr to R8
    		clr.w	R9
    
    hwnext:	mov.b	@R7+, R9	;get next hwarr element
    		cmp		#2, R9		;is it a 2?
    		jne		swnext		;if not, go to swnext
    		call	#hcalc_power;calculate powers of 2
    		mov.w	hval, 0(R7)	;put product into current array element
    		inc.w	R7			;increment R7 or it won't fully move to next element
    		jmp		hwnext
    
    swnext:	mov.w	@R8+, R9	;get next swarr element
    		cmp		#1, R9		;is it a 1?
    		jne		lend		;if not, go to end
    		call	#scalc_power
    		jmp		swnext
    
    hcalc_power:
    		mov.w	b, R5		;pass b to register for HW_Mult subroutine
    		mov.w	hval, R6	;ditto for hval
    		call	#HW_Mult
    		ret
    
    scalc_power:
    		mov.w	b, R5		;pass b to register for HW_Mult subroutine
    		mov.w	sval, R6	;ditto for hval
    		call	#SW_Mult
    		ret
    
    HW_Mult:
    		mov.w	R5, &MPY	;get R5
    		mov.w	R6, &OP2	;get R6
    		nop					;3 clock cycles
    		nop
    		nop
    		mov		RESLO, &hval;multiply and put product in hval
    		ret
    
    SW_Mult:
    
    		ret
    
    lend:	nop
    
    ;-------------------------------------------------------------------------------
    ; Stack Pointer definition
    ;-------------------------------------------------------------------------------
            .global __STACK_END
            .sect   .stack
    
    ;-------------------------------------------------------------------------------
    ; Interrupt Vectors
    ;-------------------------------------------------------------------------------
             .sect   ".reset"               ; MSP430 RESET Vector
             .short  RESET
             .end
    

  • I wouldn't bother with shift and add multiplication unless I had a target that lacked a hardware multiplier. TI has examples of that available if you must.

    If you need larger products than the hardware can provide, simply compute partial products using the hardware multiplier and add them together.

  • Oh, trust me, I would never use shift-and-add if it wasn't a requirement for calculating the efficiency of the hw multiplier over the sw multiplication.  Doing binary math in this program is completely inefficient.

**Attention** This is a public forum