Tool/software:
Hello,
I’m currently working with an MSP430 (MSP430X) device and have encountered a question regarding how the instruction code 0x03EF (ADDA R3, R15) is interpreted. Specifically, my C code is:
volatile unsigned int* p = START; p += 1;
After compiling, the assembly output shows:
ADDA #2, R15
Yet the actual machine code in the ELF file for this instruction is 0x03EF, which some disassemblers display as ADDA R3, R15.
I understand that R2/R3 serve as constant generator registers on the MSP430, and that certain addressing mode in the As bits can cause R3 to represent the immediate value #2. However, I’m trying to reconcile this with the documentation in SLAU367 (MSP430x5xx/MSP430x6xx Family User’s Guide)—particularly Table 4-2, which lists “Indirect register mode” to get value #2 from R3, while chapters 4.5.2.6 and 4.6.1 describe 0x0*E* machine code instruction as “Register mode.” (ADDA Rsrc Rdst)
I suspect this is the expected behavior, and that I may just be overlooking the relevant documentation. It makes sense if this is an exceptional case of ADDA (and SUBA) because it is pointless to get zero from R3 in these instructions.
I’d greatly appreciate any guidance or documentation references that illuminate this particular behavior.
Thank you in advance,
Seki
I understand your confusion about the interpretation of the instruction code 0x03EF. It's indeed a bit tricky when the disassembled output doesn't match the expected assembly code.
First of all, you're right about R2/R3 being constant generator registers on the MSP430. The special behavior you're observing is likely due to the interaction between the instruction encoding and the constant generation mechanism.
In the case of the `ADDA` instruction, the machine code `0x03EF` is an encoding that the disassembler interprets as `ADDA R3, R15`. However, because R3 can be used as a constant generator to represent the immediate value #2 in certain addressing modes, the actual effect is equivalent to `ADDA #2, R15`.
Regarding the documentation in SLAU367, the difference between the "Indirect register mode" in Table 4 - 2 and the "Register mode" in chapters 4.5.2.6 and 4.6.1 might seem conflicting at first glance. But in reality, these modes are defined based on different aspects of instruction encoding and execution.
The "Indirect register mode" might refer to how the value is retrieved from the register in a more complex memory - accessing scenario, while the "Register mode" is more about the basic operation of using registers directly in an instruction.
As for the exceptional case you mentioned about `ADDA` and `SUBA`, it makes perfect sense. Since getting zero from R3 in these instructions would be pointless, the hardware designers might have chosen to use R3 in a special way to represent immediate values like #2.
I would have to say that this behaviour is undocumented. The value you get from the constant generator when referring to R3 depends on the source address mode bits. But the ADDA instruction doesn't have these bits. So what happens isn't documented.
Kind of like PUSHX.A.
I tried but the GCC insists on using the documented immediate mode encoding. When coerced (asm(" .word 0x3ef");) the disassembler displays this as register mode. So the constant generator thing was a closely held secret.
I wonder what magic happens if you refer to R2?
I did further experiments on my device (MSP430FR5969 LaunchPad).
To summarize:
* ADDA R3, R4 increments R4 by 2
* SUBA R3, R4 decrements R4 by 2
* ADDA R2, R4 increments R4 by 4
* SUBA R2, R4 decrements R4 by 4. // note: edited typo
* MOVA R3, R4 set R4 to 0
* CMPA R3, #2 reset Z
So I think this is unique behaviour for ADDA and SUBA. The As bits seem to be "10" for accessing R3 on this instruction.
Here is the complete code and the output. (compiled with msp430-elf-gc)
#include <stdio.h> #include <msp430.h> int main(void) { WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer register unsigned int *p; // R4 = 8 // ADDA R3, R4 asm( "mova #8, r4\n\t" // ".word 0x03e4\n\t" "adda r3, r4\n\t" "mova r4, %0" : "=r"(p)); printf("p = 8; adda r3, p; p = %p\n", p); // R4 = 8 // SUBA R3, R4 asm( "mova #8, r4\n\t" // ".word 0x03f4\n\t" "suba r3, r4\n\t" "mova r4, %0" : "=r"(p)); printf("p = 8; suba r3, p; p = %p\n", p); // R4 = 8 // ADDA R2, R4 asm( "mova #8, r4\n\t" // ".word 0x02e4\n\t" "adda r2, r4\n\t" "mova r4, %0" : "=r"(p)); printf("p = 8; adda r2, p; p = %p\n", p); // R4 = 8 // SUBA R2, R4 asm( "mova #8, r4\n\t" // ".word 0x02f4\n\t" "suba r2, r4\n\t" "mova r4, %0" : "=r"(p)); printf("p = 8; suba r2, p; p = %p\n", p); // MOVA R3, p asm( "mova r3, r4\n\t" "mova r4, %0" : "=r"(p)); printf("mova r3, p; p = %p\n", p); // CMPA R3, p asm( "mova #2, r4\n\t" "cmpa r3, r4\n\t" "jeq .Leq\n\t" "mova #0, %0\n\t" "jmp .Lend\n\t" ".Leq:\n\t" "mova #1, %0\n\t" ".Lend:\n\t" : "=r"(p)); printf("cmpa r3, #2; Z = %d\n", p); return 0; }
p = 8; adda r3, p; p = 0xa p = 8; suba r3, p; p = 0x6 p = 8; adda r2, p; p = 0xc p = 8; suba r2, p; p = 0x4 mova r3, p; p = 0 cmpa r3, #2; Z = 0
004726: 140A PUSHM.A #1,R10 004728: 00B1 0008 SUBA #0x00008,SP 6 WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer 00472c: 40B2 5A80 015C MOV.W #0x5a80,&Watchdog_Timer_WDTCTL 12 asm( 004732: 0084 0008 MOVA #0x00008,R4 004736: 03E4 INCDA R4 004738: 04CC MOVA R4,R12 19 printf("p = 8; adda r3, p; p = %p\n", p); 00473a: 0C71 0004 MOVA R12,0x0004(SP) 00473e: 1800 40F1 4400 0000 MOVX.A #0x04400,0x00000(SP) 004746: 008A 47CA MOVA #0x047ca,R10 00474a: 134A CALLA R10 23 asm( 00474c: 0084 0008 MOVA #0x00008,R4 004750: 03F4 DECDA R4 004752: 04CC MOVA R4,R12 30 printf("p = 8; suba r3, p; p = %p\n", p); 004754: 0C71 0004 MOVA R12,0x0004(SP) 004758: 1800 40F1 441B 0000 MOVX.A #0x0441b,0x00000(SP) 004760: 134A CALLA R10 34 asm( 004762: 0084 0008 MOVA #0x00008,R4 004766: 02E4 ADDA SR,R4 004768: 04CC MOVA R4,R12 40 printf("p = 8; adda r2, p; p = %p\n", p); 00476a: 0C71 0004 MOVA R12,0x0004(SP) 00476e: 1800 40F1 4436 0000 MOVX.A #0x04436,0x00000(SP) 004776: 134A CALLA R10 44 asm( 004778: 0084 0008 MOVA #0x00008,R4 00477c: 02F4 SUBA SR,R4 00477e: 04CC MOVA R4,R12 50 printf("p = 8; suba r2, p; p = %p\n", p); 004780: 0C71 0004 MOVA R12,0x0004(SP) 004784: 1800 40F1 4451 0000 MOVX.A #0x04451,0x00000(SP) 00478c: 134A CALLA R10 53 asm( 00478e: 03C4 CLRA R4 004790: 04CC MOVA R4,R12 57 printf("mova r3, p; p = %p\n", p); 004792: 0C71 0004 MOVA R12,0x0004(SP) 004796: 1800 40F1 446C 0000 MOVX.A #0x0446c,0x00000(SP) 00479e: 134A CALLA R10 60 asm( 0047a0: 0084 0002 MOVA #0x00002,R4 0047a4: 03D4 TSTA R4 0047a6: 2403 JEQ (0x47ae) 0047a8: 008C 0000 MOVA #0x00000,R12 0047ac: 3C02 JMP (0x47b2) 0047ae: 008C 0001 MOVA #0x00001,R12 70 printf("cmpa r3, #2; Z = %d\n", p); 0047b2: 0C71 0004 MOVA R12,0x0004(SP) 0047b6: 1800 40F1 4480 0000 MOVX.A #0x04480,0x00000(SP) 0047be: 134A CALLA R10 73 } 0047c0: 434C CLR.B R12 0047c2: 00A1 0008 ADDA #0x00008,SP 0047c6: 160A POPM.A #1,R10 0047c8: 0110 RETA
Thank you for your comments. That's a good point to see so I experimented with R2 as well. I posted the results to the thread.
ADDA R2, R4 incremented R4 by four. So the situation is the same as R3.
> asm(" .word 0x3ef");
What a trick. I never think of that idea.
Thank you for your comment.
the hardware designers might have chosen to use R3 in a special way to represent immediate values like #2
Can I get a statement or document from TI for this behavior?
Still from what I decode the SLAU367 document, it is possible that it increments R15 by any number of the Table 4-2.
Please refer to these e2e for addtional reference:
That added nothing to this discussion. Which is about CPUX address instructions which have no As address mode bits for the constant generator to work with. The behaviour noted is undocumented in any way by TI.
Agreed. The designer's intention to enhance the machine code density is understandable. I want to see some clarifications from the official.
Sorry, all the MSP430 related designers are left TI.... So you can't get any formal declaration. All we can refer is the released docs.
Yes. Sorry, for MSP430, we just keep it in mantianness. Currently, we can only give some limited support from application level.
**Attention** This is a public forum