The Effects of Casting on Code Quality

The 56800x family is a native 16-bit architecture. Type casting to and from 16-bit data types requires extra instruction words and cycles. Use 16-bit types (int, short, unsigned int, unsigned short) whenever possible to minimize to program memory required for the application. Also be aware that ANSI-C requires implicit promotion of integral types for arithmetic operations and this may cause implicit type casting. Of course, favoring 16-bit data types may cause an increase in the total data size of an application. The trade off between program and data memory will have to be judged for each application. In general, if program memory is the limiting resource, favor 16-bit types. If data memory is the limiting resource, then using 8-bit data types where possible may be preferred.

Casting ints to char or long types are usually the least costly in terms of words and cycles. Since accumulators (A,B,C,D registers in the 56800E) are the only registers capable of holding 32-bit quantities, they must be used for long operations. Accumulators are composed of two individually addressable 16-bit parts, the MSP or most significant portion and the LSP or least significant portion. The MSP is often treated as a 16-bit register containing an int or short sized quantity (16-bits). An int to long cast requires an asr16 instruction to move the MSP to the LSP of the accumulator.

Listing: Example 8: Casting an integer to a Long Data Type
int ls;
long ll;
    ll = (long)ls;
move.w X:(SP-2),A;
asr16 A,A
move.l A10,X:(SP-4)

Bytes or char variables are stored as portions of integer sized registers. The 56800E does not contain 8-bit registers. An int to char cast requires an explicit sign extension (sxt.b) of the integer to properly format the register so that the sign bit of the char is extended into the entire word. This is required for proper arithmetic operations on the char since arithmetic in C occurs on integers by definition. Also, the 56800E only performs 16-bit and 32-bit arithmetic.

Listing: Example 9: Casting an int to a char Data Type
  char lc;
  int ls;

  lc = (char)ls;

Assembly output:
move.w X:(SP-2),A
sxt.b A,A
move.b A1,X:(SP) 

Chars that are converted to int or long first require a sign extension of the byte into an integer value. If the char is converted to a long, an addition asr16 is required to convert to a 32-bit value.

Listing: Example 10: Casting a char to long
  long ll;
  char lc;

  ll = (long)lc;

Assembly output:
moveu.b X:(SP),A
sxt.b A,A
asr16 A,A
move.l A10,X:(SP-4)

It should be clear now that casting causes runtime penalties in terms of code size and cycles. Sometimes the perceived benefit of using shorter data types to save data memory results in runtime costs.

The 56800E has a unique model for handling pointers to character data. Although the data memory is organized by words, that is, each address points to a word (two bytes) of data, individual bytes within a word can be still be addressed. The compiler handles this addressing invisibly, but the programmer should be aware of the costs of converting from byte pointers to word pointers and vice versa.

A byte address is generated by the compiler when the programmer chooses to use character data to represent an object. Strings are character data by default in the 56800E compiler and are addressed with byte pointers. Special instructions in the 56800E instruction set expect to see and operate on byte pointer values. A word pointer may be converted to a byte pointer by multiplying the word address by two. Similarly, a byte address is converted to a word address by dividing the byte address by two. When a byte pointer is cast to a word pointer, an explicit, runtime conversion of the pointer quantity is performed. The cost is a one word, one cycle penalty to bit shift the address value to the left, that is, multiply by two, to convert to a byte pointer. The cost is the same to convert to a word pointer, except the shift is to the right, effectively dividing by two. The void pointer is a byte pointer since the void pointer should be able to represent any data type, including chars. Since there is a runtime penalty for converting pointer types, casts back and forth should be limited for efficient C programs. This may be a factor when the void pointer is used to point to generic data and cast to the proper type at runtime. The following listing shows the effect of casting byte and word pointers.

Listing: Example 11: Casting Byte and Word Pointers
  void * pvoid;
  int vint;
  int * pint;
  char *pchar;
  
  pint = (int *)&vint;
adda #-5,SP,R0
move.w R0,X:(SP-6)

  pvoid = (void *)pint;
moveu.w X:(SP-6),R0
asla R0,R0
move.w R0,X:(SP-4)

  pchar = (char *)pint;
move.w X:(SP-6),R0
asla R0,R0
move.w R0,X:(SP-7)

  pint = (int *)pvoid;
moveu.w X:(SP-4),R0
lsra R0
move.w R0,X:(SP-6)