| In-Line Functions In Keil C51 |
In most 8051 applications, this not a problem as there is generally 256 on-chip RAM potentially available as stack. Even after allowing for a few registerbanks, there is normally sufficient stack space for deeply nested functions.
However, in the case of the 8031 and reduced devices such as the 87C751, every byte of RAM is critical. In the latter case, there are only 64 bytes!
A trick which can both save stack and reduce run time is to use macros with parameters to act like in-line functions. The ability to create macros with replaceable parameters is not commonly used but on limited RAM variants, it can be very useful.
Here, a strcpy() function created as a macro named Inline_Strcpy. Whilst it looks like a normal function, it does not actually have any fixed addresses or local data of its own. The \ characters serve to allow the macro definition to continue to a new line, in this case, to preserve the function-like appearance.
It is called like a normal function with the parameters to be passed enclosed in ( ). However, no CALL is used and the necessary code is created in-line. The end result is that a strcpy is performed but no new RAM or stack is required.
Please note however, the drawback with this very simple example is that the source and destination pointers are modified by the copying process and so is rather suspect!
A further benefit in this example is that the notional pointers s1 and s2 are automatically memory-specific and thus very efficient. Thus in situations where the same function must operate on pointer data in a variety of memory spaces, slow generic pointers are not required.
#define Inline_Strcpy(s1,s2) {\ while((*s1 = *s2) != 0) {\
*s1++ ; *s2++; } \
}
char xdata *out_buffx = { } ;
char xdata *in_buffx = { Hello } ;
char idata *in_buffi = { Hello } ;
char idata *out_buffi = { } ;
char code *in_buffc = { Hello } ;
void main(void) {
Inline_Strcpy(out_buffx,in_buffx) // In line functions
Inline_Strcpy(out_buffi,in_buffi)
Inline_Strcpy(out_buffx,in_buffc)
}
Another good example of how a macro with parameters can be use to aid source readability is in the optimisation feature earlier in this newsletter. The interpolation calculation that originally formed a subroutine could easily be redefined as a macro with 5 parameters, realising a ram and run time saving at the expense of code size.
Note that r, the fifth parameter, represents the return value which has to be passed to the macro so that it has somewhere to put the result!
#define interp_sub(x,y,n,d,r) y -= x ; \
if(!CY) { r = (unsigned char) (x + (unsigned char)(((unsigned int)(n * y))/d)) ;\
} else { r = (unsigned char) (x - (unsigned char)(((unsigned int)(n * -y))/d)) ; }
This is then called by:
/* Interpolate 2D Map Values */ /* Macro With Parameters Used */ interp_sub(map_x1y1,map_x2y1,x_temp1,x_temp2,result_y1)
and later it it is reused with different parameters thus:
interp_sub(map_x1y2,map_x2y2,x_temp1,x_temp2,result_y2)
To summarise, parameter macros are a good way of telling C51 about a generalised series of operations whose memory spaces or input values change, in programs where speed or RAM usage is critical.