- •Chapter 1. Introduction
- •Support for all 8051 Variants
- •Books About the C Language
- •Chapter 2. Compiling with the Cx51 Compiler
- •Environment Variables
- •Running Cx51 from the Command Prompt
- •ERRORLEVEL
- •Cx51 Output Files
- •Control Directives
- •Directive Categories
- •Reference
- •Chapter 3. Language Extensions
- •Keywords
- •Memory Areas
- •Program Memory
- •Internal Data Memory
- •External Data Memory
- •Far Memory
- •Special Function Register Memory
- •Memory Models
- •Small Model
- •Compact Model
- •Large Model
- •Memory Types
- •Explicitly Declared Memory Types
- •Implicit Memory Types
- •Data Types
- •Bit Types
- •Special Function Registers
- •sbit
- •Absolute Variable Location
- •Pointers
- •Generic Pointers
- •Pointer Conversions
- •Abstract Pointers
- •Function Declarations
- •Function Parameters and the Stack
- •Passing Parameters in Registers
- •Function Return Values
- •Specifying the Memory Model for a Function
- •Specifying the Register Bank for a Function
- •Register Bank Access
- •Interrupt Functions
- •Reentrant Functions
- •Chapter 4. Preprocessor
- •Directives
- •Stringize Operator
- •Predefined Macro Constants
- •Chapter 5. 8051 Derivatives
- •Analog Devices MicroConverter B2 Series
- •Atmel 89x8252 and Variants
- •Dallas 80C320, 420, 520, and 530
- •Arithmetic Accelerator
- •Data Pointers
- •Library Routines
- •Philips 8xC750, 8xC751, and 8xC752
- •Philips 80C51MX Architecture
- •Philips and Atmel WM Dual DPTR
- •Customization Files
- •STARTUP.A51
- •INIT.A51
- •XBANKING.A51
- •Basic I/O Functions
- •Memory Allocation Functions
- •Optimizer
- •General Optimizations
- •Options for Code Generation
- •Segment Naming Conventions
- •Data Objects
- •Program Objects
- •Interfacing C Programs to Assembler
- •Function Parameters
- •Parameter Passing in Registers
- •Parameter Passing in Fixed Memory Locations
- •Function Return Values
- •Using the SRC Directive
- •Register Usage
- •Overlaying Segments
- •Example Routines
- •Small Model Example
- •Compact Model Example
- •Large Model Example
- •Data Storage Formats
- •Bit Variables
- •Signed and Unsigned Long Integers
- •Generic and Far Pointers
- •Floating-point Numbers
- •Accessing Absolute Memory Locations
- •Absolute Memory Access Macros
- •Linker Location Controls
- •The _at_ Keyword
- •Debugging
- •Chapter 7. Error Messages
- •Fatal Errors
- •Actions
- •Errors
- •Syntax and Semantic Errors
- •Warnings
- •Chapter 8. Library Reference
- •Intrinsic Routines
- •Library Files
- •Standard Types
- •va_list
- •Absolute Memory Access Macros
- •CBYTE
- •CWORD
- •DBYTE
- •DWORD
- •FARRAY, FCARRAY
- •FVAR, FCVAR,
- •PBYTE
- •PWORD
- •XBYTE
- •XWORD
- •Routines by Category
- •Buffer Manipulation
- •Character Conversion and Classification
- •Data Conversion
- •Math Routines
- •Memory Allocation Routines
- •Stream Input and Output Routines
- •String Manipulation Routines
- •Miscellaneous Routines
- •Include Files
- •8051 Special Function Register Include Files
- •ABSACC.H
- •ASSERT.H
- •CTYPE.H
- •INTRINS.H
- •MATH.H
- •SETJMP.H
- •STDARG.H
- •STDDEF.H
- •STDIO.H
- •STDLIB.H
- •STRING.H
- •Reference
- •Compiler-related Differences
- •Library-related Differences
- •Appendix B. Version Differences
- •Version 6.0 Differences
- •Version 5 Differences
- •Version 4 Differences
- •Version 3.4 Differences
- •Version 3.2 Differences
- •Version 3.0 Differences
- •Version 2 Differences
- •Appendix C. Writing Optimum Code
- •Memory Model
- •Variable Location
- •Variable Size
- •Unsigned Types
- •Local Variables
- •Other Sources
- •Appendix D. Compiler Limits
- •Appendix E. Byte Ordering
- •Recursive Code Reference Error
- •Problems Using the printf Routines
- •Uncalled Functions
- •Using Monitor-51
- •Trouble with the bdata Memory Type
- •Function Pointers
- •Glossary
- •Index
176 |
Chapter 6. Advanced Programming Techniques |
|
|
Data Storage Formats
This section describes the storage formats of the data types available in the Cx51 compiler. The Cx51 compiler offers a number of basic data types to use in your C programs. The following table lists these data types along with their size requirements and value ranges.
Data Type |
Bits |
Bytes |
Value Range |
Bit |
1 |
— |
0 to 1 |
signed char |
8 |
1 |
-128 to +127 |
unsigned char |
8 |
1 |
0 to 255 |
Enum |
8 / 16 |
1 or 2 |
-128 to +127 or -32768 to +32767 |
signed short |
16 |
2 |
-32768 to +32767 |
unsigned short |
16 |
2 |
0 to 65535 |
signed int |
16 |
2 |
-32768 to +32767 |
unsigned int |
16 |
2 |
0 to 65535 |
signed long |
32 |
4 |
-2147483648 to 2147483647 |
unsigned long |
32 |
4 |
0 to 4294967295 |
Float |
32 |
4 |
±1.175494E-38 to ±3.402823E+38 |
data *, idata *, pdata * |
8 |
1 |
0x00 to 0xFF |
code*, xdata * |
16 |
2 |
0x0000 to 0xFFFF |
generic pointer |
24 |
3 |
Memory type (1 byte); Offset (2 bytes) 0 to 0xFFFF |
Other data types, like structures and unions, may contain scalars from this table. All elements of these data types are allocated sequentially and are byte-aligned
6 |
due to the 8-bit architecture of the 8051 family. |
|
Bit Variables
Scalars of type bit are stored using a single bit. Pointers to bits and arrays of bits are not allowed. Bit objects are always located in the bit-addressable internal memory of the 8051 CPU. The BL51 Linker/Locator overlays bit objects if possible.
Keil Software — Cx51 Compiler User’s Guide |
177 |
|
|
Signed and Unsigned Characters,
Pointers to data, idata, and pdata
Scalars of type char are stored in a single byte (8 bits). Memory-specific pointers that reference data, idata, and pdata are also stored using a single byte (8 bits). If an enum can be represented with an 8 bit value, the enum is also stored in a single byte.
Signed and Unsigned Integers,
Enumerations, Pointers to xdata and code
Scalars of type int, short, and enum, and memory-specific pointers that reference xdata or code are all stored using two bytes (16 bits). The high-order byte is stored first, followed by the low-order byte. For example, an integer value of 0x1234 is stored in memory as follows:
Address |
+0 |
+1 |
Contents |
0x12 |
0x34 |
|
|
|
Signed and Unsigned Long Integers |
|
|
|
|||
Scalars of type long are stored using four bytes (32 bits). The bytes are stored in |
|
|||||
high to low order. For example, the long value 0x12345678 is stored in memory |
6 |
|||||
as follows: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Address |
+0 |
+1 |
+2 |
+3 |
|
|
|
|
|
|
|
|
|
Contents |
0x12 |
0x34 |
0x56 |
0x78 |
|
|
|
|
|
|
|
|
|
178 |
Chapter 6. Advanced Programming Techniques |
|
|
Generic and Far Pointers
Generic pointers have no declared explicit memory type. They may point to any memory area on the 8051. These pointers are stored using three bytes (24 bits). The first byte contains a value that indicates the memory area or memory type.
The remaining two bytes contain the address offset with the high-order byte first. The following memory format is used:
Address |
+0 |
+1 |
+2 |
Contents |
Memory Type |
Offset; High-Order Byte |
Offset; Low-Order Byte |
|
|
|
|
Depending on the compiler version that you are using, the memory type byte has the following values:
Memory Type |
idata / data / bdata |
Xdata |
pdata |
code |
C51 Compiler (8051 devices) |
0x00 |
0x01 |
0xFE |
0xFF |
CX51 Compiler (Philips 80C51MX) |
0x7F |
0x00 |
0x00 |
0x80 |
|
|
|
|
|
The Philips 80C51MX architecture supports new CPU instructions that operate on a universal pointer. Universal pointers are identical with Cx51 generic pointers.
The format of the generic pointers is also used for pointers with the memory type far. Therefore, any other memory type values are used to address far memory space.
|
|
The following example shows the memory storage of a generic pointer (on the |
||||
6 |
|
C51 compiler) that references address 0x1234 in the xdata memory area. |
||||
|
|
|
|
|
|
|
|
Address |
+0 |
+1 |
+2 |
|
|
|
|
Contents |
0x01 |
0x12 |
0x34 |
|
|
||||||
|
|
|
|
|
|
|
Keil Software — Cx51 Compiler User’s Guide |
179 |
|
|
Floating-point Numbers
Scalars of type float are stored using four bytes (32-bits). The format used follows the IEEE-754 standard.
A floating-point number is expressed as the product of two parts: the mantissa and a power of two. For example:
±mantissa × 2exponent
The mantissa represents the actual binary digits of the floating-point number.
The power of two is represented by the exponent. The stored form of the exponent is an 8-bit value from 0 to 255. The actual value of the exponent is calculated by subtracting 127 from the stored value (0 to 255) giving a range of – 127 to +128.
The mantissa is a 24-bit value (representing about seven decimal digits) whose most significant bit (MSB) is always 1 and is, therefore, not stored. There is also a sign bit that indicates whether the floating-point number is positive or negative.
Floating-point numbers are stored on byte boundaries in the following format:
Address |
+0 |
+1 |
+2 |
+3 |
|
|
Contents |
SEEE EEEE |
EMMM MMMM |
MMMM MMMM |
MMMM MMMM |
|
|
where: |
|
|
|
|
|
6 |
S |
represents the sign bit where 1 is negative and 0 is positive. |
|||||
E |
is the exponent with an offset of 127. |
|
|
|
||
|
|
|
||||
M |
is the 24-bit mantissa (stored in 23 bits). |
|
|
|
Zero is a special value denoted with an exponent field of 0 and a mantissa of 0.
180 Chapter 6. Advanced Programming Techniques
The floating-point number -12.5 is stored as a hexadecimal value of 0xC1480000. In memory, this value appears as follows:
Address |
+0 |
+1 |
+2 |
+3 |
Contents |
0xC1 |
0x48 |
0x00 |
0x00 |
|
|
|
|
|
It is fairly simple to convert floating-point numbers to and from their hexadecimal storage equivalents. The following example demonstrates how this is done for the value -12.5 shown above.
The floating-point storage representation is not an intuitive format. To convert this to a floating-point number, the bits must be separated as specified in the floating-point number storage format table shown above. For example:
Address |
+0 |
+1 |
+2 |
+3 |
Format |
SEEEEEEE |
EMMMMMMM |
MMMMMMMM |
MMMMMMMM |
Binary |
11000001 |
01001000 |
00000000 |
00000000 |
Hex |
00 |
00 |
48 |
C1 |
|
|
|
|
|
From this illustration, you can determine the following information:
The sign bit is 1, indicating a negative number.
The exponent value is 10000010 binary or 130 decimal. Subtracting 127 from 130 leaves 3, which is the actual exponent.
6 |
The mantissa appears as the following binary number: |
|
10010000000000000000000 |
||
|
Keil Software — Cx51 Compiler User’s Guide |
181 |
|
|
There is an understood binary point at the left of the mantissa that is always preceded by a 1. This digit is omitted from the stored form of the floating-point number. Adding 1 and the binary point to the beginning of the mantissa gives the following value:
1.10010000000000000000000
Next, adjust the mantissa for the exponent. A negative exponent moves the decimal point to the left. A positive exponent moves the decimal point to the right. Because the exponent is three, the mantissa is adjusted as follows:
1100.10000000000000000000
The result is a binary floating-point number. Binary digits to the left of the decimal point represent the power of two corresponding to their position. For example, 1100 represents (1 × 23) + (1 × 22) + (0 × 21) + (0 × 20), which is 12.
Binary digits to the right of the decimal point also represent the power of two
corresponding to their position. However, the powers are negative. For example, .100… represents (1 × 2-1) + (0 × 2-2) + (0 × 2-3) + … which equals
.5.
The sum of these values is 12.5. Because the sign bit was set, this number should be negative. So, the hexadecimal value 0xC1480000 is -12.5.
6
182 |
Chapter 6. Advanced Programming Techniques |
|
|
Floating-point Errors
The 8051 does not contain an interrupt vector to trap floating-point errors; therefore, your software must appropriately respond to these error conditions.
In addition to the normal floating-point values, a floating-point number may contain a binary error value. These values are defined as a part of the IEEE standard and are used whenever an error occurs during normal processing of floating-point operations. Your code should check for possible arithmetic errors at the end of each floating-point operation.
Name |
Value |
Meaning |
NaN |
0xFFFFFFF |
Not a number |
+INF |
0x7F80000 |
Positive infinity (positive overflow) |
-INF |
0xFF80000 |
Negative infinity (negative overflow) |
NOTE
The Cx51 library function _chkfloat_ lets you quickly check floating-point status.
You can use the following union to store floating-point values.
|
|
union f |
{ |
|
|
|
float |
f; |
/* Floating-point value */ |
|
|
unsigned long ul; |
/* Unsigned long value */ |
|
|
|
}; |
|
|
6 |
|
|
||
|
This union contains a float and an unsigned long in order to perform floating- |
|||
|
point math operations and to respond to the IEEE error states. |
|||
|
|
|
|
|
Keil Software — Cx51 Compiler User’s Guide |
183 |
|
|
For example:
#define |
NaN |
0xFFFFFFFF |
/* Not a number (error) */ |
|
#define |
plusINF |
0x7F800000 |
/* Positive overflow |
*/ |
#define |
minusINF |
0xFF800000 |
/* Negative overflow |
*/ |
union f { |
|
|
|
|
float |
|
f; |
/* Floating-point value */ |
|
unsigned long |
ul; |
/* Unsigned long value */ |
||
}; |
|
|
|
|
void main (void) |
{ |
|
|
|
float a, b; |
|
|
|
|
union f x; |
|
|
|
x.f = a * b;
if (x.ul == NaN || x.ul == plusINF || x.ul == minusINF) { /* handle the error */
}
else {
/* result is correct */
}
}
6