Index


RISC World

ARM code for Beginners
Part 8:Relocatable Code, Floating Point and 32bit code

Brain Pickard explains how anyone can program in ARM code.

Rounding off this short introduction to ARM code I thought I had better mention some more advanced ideas.

Relocatable Code

So far we have written ARM code that has been assembled at a specific memory position using the line:

     P%=code%

This is fine for use in BASIC programs but what if we wish to use our routine within other routines? We can save the block of memory by using:

     OSCLI("Save routine "+STR$~(code%)+" "+STR$~(P%)) 

This will produce a copy of the routine on our discs current selected directory. (Note the use of the ~ character before the code% and P%, this changes the code% and P% into hexadecimal notation before the STR$ command makes them a string).

OR use

      SYS"OS_File",10,<full pathname of file>,&FFC,,code%,P%-code% 

This will save the code as an absolute file anywhere on your disc.

In our new BASIC program we could DIM code% and use:

      OSCLI("Load routine "+STR$~(code%))

This would load the routine starting at code%. But will it work at its new address? Consider the following part of an ARM code routine.

     MOV R0,#loopst%

Here R0 will contain the actual memory address of the label loopst%. If we load the routine at a different start address then all such MOV commands will no longer hold the correct addresses in their registers!

This does not apply to branch (or ADR) commands since they work out the relative movement forward or backward (in number of words). This is also true for all branch commands in effect they say branch forward or back n words. This means as long as we stay clear of MOV R0,#loopst% or have code that does not modifies itself our routines will be relocatable.

Offset Assembling

Using the variable O% and altering the value of the OPT when assembling we can make the assembler produce code as if it were assembled at another address. Consider the two listings:

     FOR pass%=0 TO 2 STEP 2          FOR pass%=4 TO 6 STEP 2
     P%=code%                         O%=0
     [OPT pass%                       P%=code%
     .                                [OPT pass%
     .                                .

The right hand listing produces code as if it were starting at position zero in memory. This method is often used when producing relocatable modules since it makes the memory addresses offsets from the start of the routine. Normally for simple short routines the above is not required.

If you wish to use the offset assembly as shown above with O% starting at zero then use:

     SYS"OS_File",10,<full path name>,&FFC,,code%,O%

to save it to disc.

Fractions

In all the routines I have not used any real numbers so any fractional values could not be represented. So how do we represent such values? Well there are many ways, but the two main ones are fixed point and floating point.

Fixed Point

As its name implies the point is fixed a known number of bits into the number and the bit representation is altered for example in an eight bit fixed point number it could be:

     32     16     8     4     2     1     0.5     0.25

The decimal point could be placed anywhere in the above examples, in the right most example it is two places in from the right. What if we wish to represent the value 1/8th? Clearly the above does not allow this and so its rather inaccurate.

Another problem with the fixed point representation is the range of values that can be coded. In the above example the largest positive only value is 63.75.

The RISC OS ColourTrans module does make use of fixed point values, their decimal point is 16 bits in from the right. The programmer must know where the point is to make sense of the represented value.

Floating Point

Here the point can be at any position within the value yet this does not matter to the programmer. Consider a DECIMAL number 300000000 (the velocity of light in metres/second).

This could be written as 3.0 x 10^8 (3 times 10 to the power of 8). This representation is called scientic notation. The value (3.0) is called the mantissa and the 10^8 is the exponent.

The mantissa holds the value and the exponent holds the number of places the decimal point has to be moved (positive value move to right, negative to left).

This method can be extended to binary numbers. If a 16 bit word is split into 10 bits for the mantissa and 6 for the exponent.

     *.*********          ******
     mantissa 10 bits     exponent 6 bits
Using the value 0.110000001 001001

Looking at the exponent this has a decimal value of 8+1=9, so the floating binary point in the mantissa must be moved 9 places to the right. So the mantissa becomes:

01100000.01 = 96.25

Negative numbers are coded by a process of two's compliment which in effect means the most significant bits of the mantissa and exponent are equal to their negative binary values.

E.g. 1.110000001 101001

Here both the mantissa and exponent have their most significant bit set and so are negative. The exponent has a value -32+8+1= -23.

To see what the actual value stored is we need to decode the mantissa to get its negative value. Reverse the bit pattern to get 0.001111110 then add 1 to get - 0.001111111. But the exponent requires the point to be moved 23 places to the left so the actual mantissa binary value is - 0.00000000000000000000000001111111 which is a very small number! This is an efficient way of coding.

Floating Point ARM code

It can be seen that floating point math needs quite a bit of processing to retrieve and code values. In most CPU's a built in co-processor does the work, it having its own set of floating point codes. In current ARM cpus their is no co-processor (except the ARM 7500 FPE) so an in-built software routine known as the Floating Point Emulator (FPE) is used. This is part of RISC OS. This shows how fast ARM cpus are!

The FPE has its own codes, these must be used by the programmer when using non integer values. The problem is the BASIC assembler does not understand these codes and so reports an error if any are used.

To get round this programmers have produced BASIC routines that can be included in the assembler to produce the FPE codes. It is beyond the scope of these articles to go into this programming in detail but here are the set of floating point commands. They all use floating point registers called F0 to F7 these registers cannot be directly accessed, any values must be saved to memory before they can be used. The working precision is 80 bits split into 64 bit mantissa and 15 bit exponent and a sign bit.

Floating Point Commands

     LDF     load floating point value     LDF F0,[R0]  LDF F1,[R0,#4] etc.(see STF)
     STF     store floating point value     STF F0,[R0],#4 STF F1,[R2,#4]! etc. (see LDF)
     LFM     as LDM some versions of FPE do not support this command
     SFM     as STM some versions of FPE do not support this command
     FLT     convert integer to floating point FLT F0,R0 FLT F1,#255
     FIX     convert floating point to integer FIX R3,F2

Binary commands

     ADF     add ADF Fdest,Fsource,Fsource as ADD
     DVF     Divide (see above)
     MUF     Multiply (see above)
     POW     Power POW Fanswer,Fbase,Fpower
     SUF     Subtract

Unary Operations

     ABS     absolute value ABS F0,F2 or ABS F3,#value
     ACS     Arc Cosine
     ASN     Arc Sine
     ATN     Arc Tangent
     COS     Cosine
     EXP     Exponent
     LOG     logarithm base 10
     LGN     Logarithm base e
     MVF     Move
     MNF     Move negative value
     NRM     Normalise - make mantissa into *.****** format and adjust exponent value
     RND     Round to integral value
     SIN     Sine
     SQT     Square root
     TAN     Tangent
     URD     Unnormalised Round

As you can see there are quite a few commands.

26 and 32 bit ARM code

In part 1 I stated that R15 contains the program counter and the status register. This means the program counter cannot be 32 bits wide. In fact it is 26 bits wide with the remaining bits used for the status register (NZCV flags and mode flags).

With the next generation of ARM chips (XScale from Intel) the architecture has been altered.

The PC is now 32 bits wide and the status register has a register of its own. This means some code will not work on the new chips.

     E.G. MOVS PC,R14

This is the standard way of returning from a sub routine and restoring the status flags. Since in 32 bit mode the status flags are not in the PC they will not be updated. There are work rounds this problem which I am not going into in detail but if you wish to try and produce 32 bit mode compatible code do not use any commands that load or save the PC and alter the status flags. These commands include:

     STMFD R13!,{R14}^ and LDMFD R13!,{PC}^ (the ^ means update status flags)

If you wish to read more about the different modes then check out the web try, a search on www.riscos.org

Finally....

Well thats about it for this series of articles. I hope you have found them helpful. Remember you are using a computer that has the most efficient CPU in the world, as well as being reasonably straight forward to program, especially with the help of the best operating system, RISC OS, and BBC BASIC.

So go ahead and enjoy your programming and remember you can never make too many backups of your source code!

Brain Pickard

 Index