# Tutorial 02 - Using constants

(Difference between revisions)
Jump to: navigation, search

## Data

In this tutorial, we shall look at how to get values into and out of registers. One of the fundamental operations in computing is manipulating data, but this can't happen until the data has been placed into processor registers.

### A little detour - BASIC variable passing

As a little detour, and something that may be useful for experimenting, BBC BASIC will take the contents of `A%` and place it into `R0` (`B%` into `R1`, `C%` into `R2`, and so on).
If you call the code with `USR` (instead of `CALL`), the value of `R0` on exit will be passed back to BASIC.

```  DIM code% 1024
FOR loop% = 0 TO 2 STEP 2
P% = code%
[ OPT loop%

ADD   R0, R0, R1
MOV   PC, LR
]
NEXT

A% = 1
B% = 2
PRINT USR(code%)
```

Running this, the result will be "3".

I shall not delve any deeper into this aspect of BASIC at this time, instead I'll pass the baton over to you. Try some different instructions, such as `ADD R0, R0, R1, LSL #3` or `MVR R0, R0` to see what the results are.

### Literal constants

Something that will happen a lot is a literal constant - for example range checking, embedded values, and so forth.

Consider the following:

```  DIM code% 1024
FOR loop% = 0 TO 2 STEP 2
P% = code%
[ OPT loop%

; handle regular characters
CMP   R0, #32
SWIGE "OS_WriteC"  ; BUG: doesn't trap <127>
MOVGE PC, LR

; convert Enter to newlines
CMP   R0, #13
SWIEQ "OS_NewLine"

; anything else is converted to a period
MOVNE R0, #46
SWINE "OS_WriteC"

MOV   PC, LR
]
NEXT

PRINT "Type stuff (ESC to exit):"
REPEAT
A% = INKEY(0)
IF (A%) <> -1 THEN CALL code%
UNTIL FALSE
```

What this program does is accept a keypress (the `INKEY`) and if there was a keypress (result not -1), call the code. The code will display the character entered, translating Enter key to a newline, and filtering out control codes. In order to maximise the use of the ARM processor, it makes extensive use of conditional execution.

However, we can see constants scattered throughout. Is it 32? Compare with 13. Set to 46... Your homework, should you accept it, is to modify the program to trap the backspace key and make it delete characters instead of printing dots.

There are, of course, limitations to this. Consider:

```  DIM code% 1024
FOR loop% = 0 TO 2 STEP 2
P% = code%
[ OPT loop%

MOV   R0, #123
MOV   R0, #1234
MOV   R0, #4352
MOV   R0, #12928
MOV   R0, #65536

MOV   PC, LR
]
NEXT
```

You will get `Bad Immediate Contant` if you try to assemble the program. This is because the constant is an eight bit value with an optional four bit shift. One of the above numbers cannot be built in this manner. Which?

### Oversize constants

In order to use oversized contants, you would place them into a word in memory and then load that into the register. For this, we would use the LDR instruction, as follows:

```  LDR   R0, address
MOV   PC, LR

.address
EQUD  &12345678
```

What happens here is, instead of stuffing a value into a register, you are instructing the processor to pick up the register from a memory location. There are limitations (roughly +/- 4KiB) as to the range of an LDR.

• Some assemblers (not BASIC) support a syntax similar to `LDR R0, =&12345678` which tells the assembler that you want to see that value loaded into that register, and leave it to the assembler to work out where convenient to stuff the data word.

### A different action is required

But, wait, this fails miserably!

```  DIM code% 1024
FOR loop% = 0 TO 2 STEP 2
P% = code%
[ OPT loop%

LDR   R0, message
SWI   "OS_Write0"

MOV   PC, LR

.message
EQUS  "Boo!" + CHR\$(13) + CHR\$(10) + CHR\$(0)
ALIGN
]
NEXT

CALL code%
```

Run that, you'll get a message telling you the application "may have gone wrong" (later versions of RISC OS) and/or a message reporting "Abort on data transfer at <address>".

Why?

Well, the answer is actually quite simple. What we want is to set R0 to point to the message, which we then pass to the OS routine to print it.
What we have is an instruction to load. So in essence we are actually picking up the first word from the location referred to and are loading it as data. The text "Boo!" is stored in memory as &216F6F42 and it is this that is being passed to the OS routine.
If you press `Ctrl-F12` to open a TaskWindow and at the star prompt type:

```  *Memory 216F6F42
```

you will see the same message - Abort on data transfer with a different faulting address (as it is a different OS routine now). Essentially, there is nothing at that address and the MMU (processor memory management unit) is faulting accesses to it.

What we need is a pseudoinstruction. This isn't a real instruction, but is rather a request for the assembler to generate the correct instruction (by addition and/or subtraction from PC) to place the correct address into R0.
The pseudoinstruction is ADR. Change that `LDR` into an `ADR`, the program will now work.

### Another detour - epic data shift

We will meet the LDM and STM instructions properly in another tutorial. For now, just know that they mean LoaD (or STore) Multiple. In other words, as an LDR loads a word into a register, these instructions handle multiple words in multiple registers at the same time.

Consider:

```  ; r12 = source pointer
; r13 = destination pointer
; r14 = (source) end pointer
.copyloop
LDMIA  r12!, {r0-r11}
STMIA  r13!, {r0-r11}
CMP    r12, r14
BLT    copyloop
```

This code will load up R0 to R11 (12 words, 48 bytes) in one instruction, then write them someplace else in one instruction. Two more instructions are used to check the loop status. This is an epic data shift that will max out the processor/memory bandwidth and copy data about as fast as the hardware is capable.

I mention this because loading multiple registers is a useful technique which we shall come back to in a later tutorial.

• Note that we trample on both `R13` (stack pointer) and `R14` (return address) in this routine. You can assume that both of these have been written to a predefined memory location for safe retrieval afterwards.

Next time, we'll look at branching.