I often use MAKEWORD and MAKEDWORD macros, which are defined to construct longer types from multiple shorter ones. e.g. -
#define MAKEWORD( h, l ) \ ( ( ( WORD )( h ) << 8 ) | ( BYTE )( l ) )
which constructs a word from two bytes.
When used with constants, the calculations are all carried out at compile time and their efficiency doesn't matter. When used with data variables, code is generated the compiler's optimisation comes into play. The 8 bit shifts can be resolved to simple mov operations.
Keil does a good job optimising the MAKEWORD macro above, but does a lot less well with a similar macro to construct an unsigned long from two words or four bytes.
The following function works much better, by doing nothing! The trick is to define the word parameters so that they are passed in the same registers as a ULONG is returned, in the correct byte order. The function can the simply return, since the return value is the parameters.
This requires that the low word is passed as the first parameter, which means it is loaded to [R6|R7]. The high word is passed as the second parameter, in [R4|R5]. A ULONG is returned in [R4|R5|R6|R7], so all is as required on entry.
The line -
$reguse _make_dword ()
is important. It informs the compiler that no registers are disturbed. Without this, the calling function may generate extra code to preserve registers.
Code saved depends on too many variables to give an exact figure, but I get back around 20 bytes for a single use within a 'C' module. Your mileage may vary.
The code should also be much faster, although I haven't tested this.
A51?MAKE_DWORD segment code rseg A51?MAKE_DWORD ;/ DWORD make_dword( WORD lo, WORD hi ); ;/____________________________________________________________________________ $reguse _make_dword () public _make_dword _make_dword: ret