Tutorial 02 - Using constants

From ARMwiki
(Difference between revisions)
Jump to: navigation, search
(Created.)
 
m (Tutorial02 moved to Tutorial 02 - Using constants: Title now includes description.)

Revision as of 05:59, 7 March 2012

Contents

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.

Personal tools
Namespaces

Variants
Actions
Navigation
Contents
Toolbox