﻿ AssembleTime

Run-time and Assemble-time

Two terms frequently found in texts on computer architecture and assembly language programming are run-time and assemble-time (or sometimes compile-time). These two terms can be a little confusing for the novice programmer. Essentially, they describe when operations are carried out: during the creation and loading of a program (assemble-time) or during the execution of a program (run-time).

It is very important for programmers to be aware of these terms and their implications.

Consider the two assembly language operations:

1. ADD r0,r1,r2

2. MOV r3,#X+Y

These are two legal ARM instructions. The first instruction adds two registers together. The second instruction moves the sum of two literals to a register. Both instructions perform an addition. However, one addition takes place at run-time and the other takes place at assemble-time.

Let’s write these two instructions in ARM format and declare the literals X and Y using the equate statement. We get:

X  EQU  1

Y  EQU  2

ADD  r0,r1,r2

MOV  r3,#X+Y

After putting this code in an appropriate ARM environment (with the necessary start-up lines), we get the following from the assembler. The original code is in blue and the assembled code in red.

6:          ADD  r0,r1,r2

0x00000000  E0810002            ADD  R0,R1,R2

7:          MOV  r3,#X+Y

0x00000004  E3A03003            MOV  R3,#0x00000003

The additional operation, ADD r0,r1,r2, is translated into the appropriate op-code. However, the MOV instruction is treated differently. The original instruction MOV r3,#X+Y is pre-processed by the assembler. The value of X is declared as 1 and the value of Y is declared as 2 by the assemble-time equate operation. Recall that the EQU assembler directive simply equates a numeric value to a name in order to make code more human readable. The values of X and Y are added to get the literal 1 + 2 = 3 and that value is encoded in the instruction. In other words, the programmer wrote MOV r3,#X+Y and the assembler converted it into MOV r3,#3 at assemble-time into the equivalent (and identical) instruction.

So, what’s the point of such an assemble-time operation? Why didn’t the programmer just write MOV r3,#3 in the first place? The answer is simple – readability. Assembly language is not easy to understand and the programmer tries to write code that is as clear as possible.  If you write ADD r4,r4,#people*income, you are telling the reader that you are adding total income to a sum. Simply writing, say, ADD r4,r4,#0x1CA00000 is not as clear to the reader.

However, operations that take place at assemble-time are performed prior to the execution of the code and cannot, of course, use variables. Assemble-time operations are nothing more than a form of shorthand.

Assemble-time Operations and Data Storage

Consider the following three code fragments. The first in red, the second in blue and the third in black.

Fragment 1

Item  EQU   4             ;Equate the name Item to the value 4

ADD   r5,r5,#4      ;increment r5 by Item

Fragment 2

Item  DCW   4             ;Store initial value for Item in memory

ADR   r1,Item       ;r1 points at Item in memory

LDR   r2,[r1]       ;r2 contains the value of Item

ADD   r5,r5,r2      ;increment r5 by Item

Fragment 3

Item  DSW   1             ;Reserve space for Item in memory

ADR   r1,Item       ;r1 points at Item in memory

MOV   r0,#4         ;r0 contains the value of Item

STR   r0,[r1]       ;Store value of Item in memory

ADR   r1,Item       ;r1 points at Item in memory

LDR   r2,[r1]       ;r2 contains the value of Item

ADD   r5,r5,r2      ;increment r5 by Item

Fragment 1 uses entirely run-time code. When the code is executed the value of r0 is incremented by the literal 4.

Now look at code fragment 2. This has the same effect as code fragment 1. However, in this case Item is made a constant in memory. The value of this constant, 4, is loaded at assemble-time by the assembler directive Item DSW 4 (reserve a memory location called Item and place the value 4 in it before the program runs). Now, when we wish to increment r2 by Item we have to load it from memory using register-indirect addressing.

Finally, consider code fragment 3. Here we use the assembler directive Item DSW  1 to reserve one word of storage for the variable Item. Note that we write DSW 1 because we are creating one word of storage, whereas in Fragment 2 we wrote DCW 4 because we were reserving a word of storage and pre-loading it with the value 4.

The first three lines of code in Fragment 3 have the effect of loading the value 4 in the variable Item. This action takes place at run-time. The next lines are the code that is used to increment r5 by Item and these are the same as in Fragment 2.

We have three fragments of code that do exactly the same thing; each adds 4 to the contents of register r5. The first fragment has one line of executable code, the second had three lines, and the third six lines. What’s the practical difference?

Code Fragment 1 does not use a memory location to store the value of Item. Instead, it treats Item as a literal in an instruction. This is the most compact code. However, the value of Item (the increment) cannot be modified without rewriting and re-assembling the code. If the value of Item is a constant and is never going to be changed, this is the code you would write.

In the second fragment of code, we store the increment in memory. When we wish to use it, we have to load it from memory. However, because the increment is stored in memory, it can be changed at run-time by the code.

However, we have to be aware of a hidden and potential problem. Because the value of Item is initialized at run time by storing 4 in memory, if that value is modified and we later re-run the code, the initial value of Item will no longer be 4. Loading a value at compile-time sets up that value once and once only. Only a reset or reboot is going to initialize it again. This can be a problem when debugging code. You run a program once, reset it and then run it again. However, any data that has been loaded into memory at assemble-time will not be reinitialized.

In the third fragment of code we also use a memory location for Item and we can also change it at run-time. However, because we initialize its value at the start of the code and execute this at run-time, we ensure that the correct value is always recreated whenever we run the code.

Summary

Assemble-time operations take place at the time a program is created and loaded. In short, assemble-time operations include the assembling of a program and the loading data into the memory before the program is executed. It also includes evaluating arithmetic expressions whose values yield literals that are to be used by the machine code program.