When your program wants to use a constant value, such as the &58454241 “ABEX” constant from the hello world example, it can be a pain to have to manually declare that constant using DCD and use a label to reference it. Luckily ObjAsm has a range of functionality available which helps to make using constants easier
MOV R0,#-123
Unlike BASIC, ObjAsm handles “MOV immediate” as a pseudo-instruction. If the immediate constant is outside the range of values directly supported by MOV, ObjAsm will invert the value and see try generating a MVN instruction instead. If both of these fail and an ARMv6T2 or later CPU has been selected, ObjAsm will also try using MOVW.
LDR R1,=&58454241 ; "ABEX"
LDR Rd,=
is a pseudo-instruction which allows arbitrary constants to be loaded into registers. When faced with such an instruction, ObjAsm will automatically pick the “best” single ARM instruction which can be used to load the required value. Most of the time this will be MOV, MVN or MOVW, as described above.
For situations where the constant can’t be represented using a single data-processing instruction, ObjAsm will reserve space in the nearest “literal pool” for the value and will generate a PC-relative LDR. If there are multiple uses of the same constant then ObjAsm will try and re-use existing literal pool values wherever possible.
MOV32 R1,#&58454241 ; "ABEX"
MOV32 is another pseudo-instruction which can be used with constant values. It assembles to a MOVW and MOVT pair. ARM say that in most situations this gives better performance than using a literal pool, but the downsides are that the pseudo-instruction is only available with ARMv6T2 and later, and it will always assemble to two instructions (even if a single instruction would be sufficient).
Sadly there is currently no “best of both” available out of the box, although possibly one could be constructed using macros, at least for situations where the constant is a true constant and not a reference to a label (discussion)
For LDR Rd,=
to work in all situations, there must be a literal pool located within 4KB of the instruction (since LDR with an immediate offset is limited to an offset of +/-4KB from the base register). Use the LTORG
directive to place the literal pool:
LTORG
Since the pool will expand to an arbitrary size and will be filled with data, you should obviously avoid placing it within the flow of execution of a function.
You already know how to use *
to define symbolic constants. Although this can be used to define the layout of structures and workspace, there’s an alternative method available which is much more flexible and powerful.
For example, a simple linked list of key-value pairs could be described using *
as follows:
KV_Next * 0 ; Pointer to next node in list KV_Key * 4 ; One byte to act as a key KV_Value * 5 ; One byte to act as a value KV_Size * 6 ; Size of a KV list entry
The same thing, rewritten to use the “storage map” functionality would look like this:
^ 0 KV_Next # 4 ; Pointer to next node in list KV_Key # 1 ; One byte to act as a key KV_Value # 1 ; One byte to act as a value KV_Size # 0 ; Size of a KV list entry
Both describe the same structure, but the method the structure is defined is different. With *
you must manually keep track of the offsets of items relative to the base of the structure. With #
you just specify the sizes of the items and ObjAsm will keep track of the offsets for you. When you’re working with large structures this makes it much easier to add, remove or change individual elements.