From: dseal@armltd.co.uk (David Seal) Newsgroups: comp.sys.acorn Subject: Re: ARM Assembly Language Query (was Re: RISC Assembly Language Query...) Date: 20 Feb 93 13:13:10 GMT Organization: A.R.M. Ltd, Swaffham Bulbeck, Cambs, UK In article <930218.154413@ia.ph.bham.ac.uk> HR-5@ia.ph.bham.ac.uk (John Fussell) writes: >Can anyone explain why the following assembly command: >MOV R3,#511 >gives a 'bad immediate constant' error on my A440, whereas the command: >MOV R3,#512 >compiles ok ?? > . >Infact, any number greater than 256 gives this error unless it is some >integer power of 2. > . >Yours, extremely frustrated at having 32 bit registers which I can't use! First, I've corrected the title of this posting to "ARM Assembly Language Query", since "RISC" refers to a more general class of processors than just the ARM. You *can* use the 32-bit registers: you've just got to load them in a different way. Also, there are plenty of numbers greater than 256 which are OK and yet not powers of 2 - e.g. 260. There are restrictions on what numbers can be immediate constants in instructions, because it is not possible for a 32-bit instruction to hold a 32-bit immediate constant *and* information about what to do with the constant! To get around this, the ARM chooses to allow you an 8-bit immediate constant in the instruction, which can additionally be rotated right by 0, 2, 4, 6, 8, ... 28 or 30 bits. This means that you are allowed: * any of the numbers 0..255; * any of the numbers 0..255 multiplied by 4, since rotating a number right by 30 bits is equivalent to rotating it left by 2 bits, which is equivalent to shifting it left by 2 bits or multiplying it by 4 if the number is in the range 0..255; * any of the numbers 0..255 multiplied by 4^2 = 16, since rotating a number right by 28 bits is equivalent to rotating it left by 4 bits, which is equivalent to shifting it left by 4 bits or multiplying it by 16 if the number is in the range 0..255; * any of the numbers 0..255 multiplied by 4^3 = 64, using a right rotation by 26 bits similarly; * any of the numbers 0..255 multiplied by 4^4 = 256, ... * any of the numbers 0..255 multiplied by 4^12 = 16777216, using a right rotation by 8 bits similarly; * various "wrapped round the end" bit patterns created by right rotations by 2, 4 or 6 bits: the most useful of these is the hexadecimal number &FC000003 (255 rotated right 6 bits), which can be used as a bit mask to separate the program counter bits from the flags and mode bits in R15. But these limitations on what can appear in an *immediate constant* in an instruction don't affect what numbers you can have in *registers*: registers can contain the full range of 32-bit numbers. To put a number which is not a valid immediate constant in a 32-bit register, there are two main approaches: (1) Construct the number in the register by using a sequence of instructions - e.g. to get 511 in R3, use something like: MOV R3,#256 ADD R3,R3,#255 Using this approach, any sixteen-bit number can be generated with just two instructions: use a MOV to load its top 8 bits, then an ADD (or ORR or EOR) to produce its bottom 8 bits. Lots of other things can be loaded with similar sequences - e.g. any 16-bit number multiplied by 4, 4^2, 4^3, ... or 4^8. Similarly, a number like the hexadecimal constant &FF00FF can be loaded by breaking it up into two rotated 8-bit constants. Other useful instructions for generating constants include: ADD Rx,Rx,Rx,LSL #n, which multiplies Rx by 2^n+1; and RSB Rx,Rx,Rx,LSL #n, which multiplies Rx by 2^n-1. Two instruction sequences will manage a lot of useful constants; three instructions will manage a lot more - e.g. the hexadecimal constant &33333333, which is useful in doing BCD (Binary Coded Decimal) arithmetic, can be constructed in R4 with the sequence: MOV R4,#&33 ORR R4,R4,R4,LSL #8 ;Now R4 contains &3333 ORR R4,R4,R4,LSL #16 ;Now R4 contains &33333333 Not all possible constants can be loaded with two or even three instructions: in the most general case, you need four instructions, one for each byte of the constant. So to load &123F876E into R9, for instance, the sequence might be: MOV R9,#&12000000 ORR R9,R9,#&3F0000 ORR R9,R9,#&8700 ORR R9,R9,#&6E However, this isn't really the most efficient way to produce such a constant in a register: for this, the second approach will probably use less space and time: (2) Produce a word containing the desired constant in memory and load it from there. E.g. to load R9 with &123F876E as before: LDR R9,ConstantHex123F876E ... ConstantHex123F876E DCD &123F876E Note that the place you're loading from does need to be addressable by an LDR instruction from where you are: this means that it needs to be within about 4K bytes. If it isn't, the assembler will complain... Also, if you're using "objasm", there is a facility to help you do this, known as "literals". You write the code as follows: LDR R9,=&123F876E The "=" sign acts as a directive to the assembler to generate a word containing the constant when it can (see below for details of this) and to address it from here - i.e. basically, it produces similar code to the above, but saves you the trouble of thinking up a label for every constant you use! Normally, these literal words are generated by the assembler when it gets to the END directive at the end of your assembly. For long assemblies, this may result in them being too far away from the LDR instruction and the assembler will complain. You should then use the "LTORG" directive at some suitable point (or points) to put these words - i.e. places where there is no risk that they will be executed as instructions, such as after the unconditional branch or return instruction at the end of each routine. By the way, the assembler cannot work these points out for itself, even though it knows which instructions are unconditional branches. This is because calculated branches and similar constructions would be disrupted. E.g. in the standard branch table code sequence: CMP Rx,#N ADDLS PC,PC,Rx,LSL #2 B OutOfRange ;To code for Rx > N case B Routine0 ;To code for Rx = 0 case B Routine1 ;To code for Rx = 1 case B Routine2 ;To code for Rx = 2 case : : : : B RoutineN ;To code for Rx = N case it would be disastrous if the assembler decided to insert the literals following one of the unconditional branch instructions! So it really is necessary for the author of the code to write the LTORG directives to tell the assembler which places are safe. One final aside: it would have been possible to design the ARM so that a 32-bit instruction could be followed by a 32-bit word holding the constant. Indeed, this technique is used on some processors. However, it's not an approach that is used much by RISC processors: the reason is that it causes problems with decoding and pipelining instructions. Decoding is awkward because a word sometimes needs to be decoded as an instruction and sometimes kept as an immediate constant; pipelining because the pipeline sometimes discovers it has to treat what looked like two separate instruction words as a single instruction with an immediate constant. This creates a "hole" in the pipeline, which results in extra delays and complexity in the logic. David Seal