Search code examples
assemblyarmmicrocontrollerdirectivenxp-microcontroller

Having issues changing .word to .equ


I'm having this lab class about microcontrollers (we're using FRDM-KL25Z128) and I'm having trouble with the directives .word and .equ.

First, the sample code our professor gave us is meant to blink the red LED on the FRDM board, and it's the following: http://www.dca.fee.unicamp.br/cursos/EA871/2s2016/UW/codes/exp2.s

We have some questions to answer before modifying the code to make it blink the three LED's at the same time and make a white light, but that's not the problem here.

In the bottom of the code, all of the registers addresses are being defined with .word directive (since we're using 32 bits words). One of the questions is to answer if we can substitute the directive .word to .equ.

My first though was that yes, I can substitute .word for .equ because the last directive is assigning a constant value to a label, and since I do not want to change the values of those labels, .equ should be fine to use.

But when I tested it, by commenting those lines:

SIM_SCGC5: @ Endereço do SIM_SCGC5
.word 0x40048038

And writing:

.equ SIM_SCGC5, 0x40048038

Which should assign the value 0x40048038 to the SIM_SCGC5 label. But the code won't work, I'll get an error on the following line:

ldr     r3,SIM_SCGC5

Saying:

invalid offset, value too big

So I'm not sure if I either screwed up in the .equ directive or if, by default, .equ assigns a value that occupies more bits than LDR can handle.

What can be the problem here?

Some notes:

  • I'm starting to code in Assembly together with a microcontroller, so I'm still missing a lot of info, like the hardware limitations. So I could just have written a bunch of random words without knowing.

  • Sorry for the long preparation. I know that long posts aren't the favorites of Stack Overflow (or Stack Exchange in general), but I didn't want to throw a question without a context.


Solution

  • .word assembles 4 bytes into the output file at the current position. If you want the data stored there, you have to load it from the address where you put it. If you put a label in front of it, the symbol's value is the address.

    .equ defines an assemble-time constant, for use as a value in future expressions. The symbol's value is the constant, there is no address, and no bytes in the object file. So you can use something defined with .equ as an immediate operand for other instructions, like add. You can't use it with loads/stores.


    Putting an immediate constant into a register is different from loading from a fixed address. Use ldr r3, =SIM_SCGC5 to have the assembler generate whatever sequence of instructions it decides is best to generate that constant in r3. They confusingly chose the same mnemonic for this pseudo-op as for an actual load instruction.

    See also Why use LDR over MOV (or vice versa) in ARM assembly?. This is probably a duplicate of an existing SO question, but I haven't found an ideal dup target yet.

    When SIM_SCGC5 is defined with a .equ the instruction ldr r3, SIM_SCGC5 would I think try to load from that absolute address. The error you get is because there's no way to encode that as a single instruction.

    It will use mov or movn with a shifted/rotated immediate if possible, otherwise falling back to a PC-relative load from a nearby constant pool. I think two instructions to set the low 16 bits and the high 16 bits might also be possible.

    See also Constants on ARM, and various SO questions like Arm cortex-m3 mov and ldr.