[Back] [Top] [Next]

9 Other C51 Extensions

9.1 Special Function Bits

.v.A frustration for assembler programmers with the old C51 version was the need to use bit masks when testing for specific bits with chars and ints, despite there being a good set of bit-orientated assembler instructions within the 8051. In version 3, however, it is possible to force data into the bit-addressable area (starting at 0x20) where the 8051's bit instructions can be used.

An example is testing the sign of a char by checking for bit = 1.

Here the char is declared as "bdata" thus:


   bdata char test_char ;

sign_bit is defined as:

   sbit sign_bit = test_char ^ 7 ;

to use this:

  
     test_char = counter ;
     if(sign_bit) { /* test_char is negative */ }

the opcodes executed are:


     MOV   A,counter    ;
     MOV   test_char,A  ;
     JNB   0,DONE       ;
     /* Negative */

All of which is a lot faster than using bit masks and &'s!

The important points are that the "bdata" tells C51 and L51 that this variable is to be placed in the bit-addressable RAM area and the "sbit sign_bit = test_char ^ 7" tells C51 to assume that a bit called sign_bit will be located at position 7 in the test_char byte.

Byte Number: test_char           20H    Start Of BDATA area
Bit Number:  0,1,2,3,4,5,6,7<_ sign_bit
Byte Number:                     21H 
Bit Number:  8,9,10,11,12,13,14,15 
Byte Number:                     22H 
Bit Number:  16,17,18,19,20,21,22,23,24.....

The situation with ints is somewhat more complicated. The problem is that the 8051 does not store things as you first expect. The same sign test for an int would require bit 7 to be tested. This is because the 8051 stores int's high byte at the lower address. Thus bit 7 is the highest bit of the higher byte and 15 is the highest bit of the lower.


Byte Number: test_int(high)          20H    
Bit Number:  0,1,2,3,4,5,6,7 
 
Byte Number: test_int+1(low)         21H 
Bit Number:  8,9,10,11,12,13,14,15

Bit locations in an integer

9.2 Support For 80C517/537 32-bit Maths Unit

The Siemens 80C537 and 80C517A group have a special hardware maths unit, the MDU, aimed at speeding-up number-crunching applications.

9.2.1 The MDU - How To Use It

To allow the 8051 to cope with 16 and 32-bit ("int" and "long") multiplication and division, the Siemens 80C517 variant has a special maths co-processor (MDU) integrated on the cpu silicon. A 32-bit normalise and shift is also included for floating point number support. It also has 8 data pointers to make accessing external RAM more efficient.

The compiler can take advantage of these enhancements if the "MOD517" switch is used, either as a #pragma or as a command line extension. This will result in the use of the MDU to perform > 8 bit multiplies and divides. However a special set of runtime libraries is required from Keil for linking.

Using the MDU will typically yield a runtime improvement of 6 to 9 times the basic 8051 cpu for 32 bit unsigned integer arithmetic.

Optionally the blanket use of the 80C517 enhancements after MOD517 can be selectively disabled by the NOMDU and NODP pragmas. Predictably NOMDU will inhibit the use of the maths unit, while NODP will stop the eight data pointers being used.

9.2.2 The 8 Datapointers

To speed up block data moves between external addresses, the 517A has 8 datapointers. These are only used by C51 in the memcpy() and strcpy() library functions.

The general "MOD517" switch will enable their use. Note that the strcat() routine does not use the additional data pointers.

If the extra pointers are to be used both in background and interrupt functions, the DPSEL register is automatically stacked on entry to the interrupt and a new DPSEL value allocated for the duration of the function.

9.2.3 80C517 - Things To Be Aware Of

The 80C517 MDU is used effectively like a hardware subroutine, as it is not actually part of the 8051 cpu. As such it is subject to normal sub-routine rules regarding re-entrancy. If, as an example, both a background program and an interrupt routine try to use the MDU simultaneously, the background calculation will be corrupted. This is because the MDU input and output registers are fixed locations and the interrupt will simply overwrite the background values.

To allow the background user to detect corruption of the MDU registers, the MDEF bit is provided within the ARCON register. After any background use of the MDU, a check should be made for this flag being set. If so, the calculation must be repeated. Appropriate use of the NOMDU pragma could be used instead.

Note: the compiler does not do this - the user must add the following code to overcome the problem:


#pragma MOD517 
#include "reg517.h"

    long x,y,z ;
    func()
      {
      while(1)
         { 
           x = y / z ;      /* 32-bit calculation */
           if(MDEF == 0)    /* If corruption has */
              { break ; }   /* occurred then repeat */
         }                  /* else exit loop */
      }

9.3 87C751 Support

The Philips 87C751 differs from the normal 8051 CPU by having a 2k code space with no option for external ROM. This renders the long LJMP and LCALL instructions redundant. To cope with this the compiler must be forced to not generate long branch instructions but to use AJMPs and ACALLs instead

9.3.1 87C751 - Steps To Take

  1. Invoke C51 with C51 myfile.c ROM(SMALL) NOINTVECTOR or use "#pragma ROM(SMALL)"
  2. Use the INIT751.A51 startup file in the LIB directory.
  3. Do not use floating point arithmetic, integer or long divides, printf, scanf etc., as they all use LCALLs.
  4. A special 87C751 library package is available which will contain short call versions of the standard library routines.

9.3.2 Integer Promotion

Automatic integer promotion within IF statements is incorporated in version >= 3.40 to meet recent ANSI stipulations in this area. This makes porting code from Microsoft or Borland PC C compilers much easier. Thus any char(s) within a conditional statement are pre-cast to int before the compare is performed. This makes some sense on 16 bit machines where int is as efficient as char but, in the 8051, char is the natural size for data and so some loss of efficiency results. Fortunately Keil have provided "#pragma NOINTPROMOTE" to disable this feature! In this case explicit casts should be used if another data type might result from an operation.

To show why this #pragma is important, this C fragment's code sizes are influenced thus:

char c ; unsigned char c1, c2 ; int i ;
main() {
   if((char)c == 0xff) c = 0 ;
   if((char)c == -1) c = 1 ;
   i = (char)c + 5 ;
   
   if((char)c1 < (char)c2 + 4) c1 = 0 ;

   }

Code Sizes

47 bytes - C51 v3.20 
49 bytes - C51 v3.40 (INTPROMOTE) 
63 bytes - C51 v3.40 (NOINTPROMOTE)

Again this goes to show that C portability compromises efficiency in 8051 programs...


[Back] [Top] [Next]