z88dk will link a C/asm program against libraries, only pulling out functions that are actually used by the program which is exactly what you want, especially as your library grows! It is a very good idea to write all your functions in asm. Library code should be as optimal as possible so that we can get at that “90% of execution time in asm and 10% in C” thing to maximize performance). C programs will be able to call these asm subroutines no problem as long as the header file tells the compiler how to do it and the asm file collects the C parameters in the way specified in the header file.
For example, the beginning of this asm function looks like:
5 PUBLIC sp1_IterateSprChar_callee 6 PUBLIC ASMDISP_SP1_ITERATESPRCHAR_CALLEE 7 8 EXTERN l_jpix
The library function name and entry point is PUBLIC. An asm constant I want made globally visible is PUBLIC when this library function is used. The library function “l_jpix” is referred to from within this function; the call to it causes its code to be included as well. Another asm function in another file somewhere else whill use “EXTERN ASMDISP_SP1_ITERATESPRCHAR_CALLEE” (of course after including the function by specifying “EXTERN sp1_IterateSprChar_callee”) to access the constant published by the XDEF directive.
To compile a library you can use this command:
z80asm -d -ns -nm -Mo -xlibname @file.lst
where 'file.lst' contains a list of asm functions to include in the library.
Example code snippet from ”/z88dk/libsrc/sprites/software/sp1/spectrum.lst“
spectrum/sprites/_sp1_struct_cs_prototype spectrum/sprites/_sp1_struct_ss_prototype spectrum/sprites/sp1_AddColSpr spectrum/sprites/sp1_AddColSpr_callee ...
This would add the asm functions “_sp1_struct_cs_prototype.asm”, etc to the library created “libname.lib”.
To compile a C program using this library you'd add ”-llibname“ to the compile command. You may have to add some path info if the lib is not located in the place z88dk expects to find it (z88dk/lib/clibs).
The header file is for the C compiler and indicates what functions are in the library and what linkage to use when calling them (linkage is how the parameters are passed to the function).
A header file would look something like:
#ifndef _MYHEADER #define _MYHEADER extern void __LIB__ myfunc(int a, int b); extern void __LIB__ init(void); #endif
The __LIB__ qaulifier will cause the C compiler to translate these header functions into sequences of “LIB myfunc”, “LIB init”, etc so that the linker will know those references are for library functions. The C compiler will generate code like “call myfunc” and “call init” to call these functions.
There are 3 linkage types:
To use these specific linkage types, in the header file you need to add __FASTCALL__ or __CALLEE__ (or nothing for the standard linkage) next to the __LIB__ in the function prototypes.
eg:
extern void __LIB__ __CALLEE__ myfunc(int a, int b);
One more complication regarding __CALLEE__ functions: the C compiler can only call functions through function pointers using standard C linkage. There is a nice way to allow the function to be called both normally (with __CALLEE__), and as a function pointer (standard). In this header file you will spot this:
218 extern void __LIB__ sp1_IterateSprChar(struct sp1_ss *s, void *hook1); ... 242 extern void __CALLEE__ __LIB__ sp1_IterateSprChar_callee(struct sp1_ss *s, void *hook1); ... 257 #define sp1_IterateSprChar(a,b) sp1_IterateSprChar_callee(a,b)
The C program will call the library function using this much nicer name as in “sp1_IterateSprChar(s,f);” The pre-processor will change this to a call to “sp1_IterateSprChar_callee(s,f);” so that the __CALLEE__ linkage function gets called by default. However, if the program tries to call the function using a function pointer:
function_pointer = sp1_IterateSprChar; (function_pointer)(s,f);
The preprocessor does not make the callee substitution because the number of parameters doesn't match. In this case the library function “sp1_IterateSprChar” is called using normal C linkage. You can see that this function is only a shell that calls into the real callee function: sp1_IterateSprChar.asm
For this to work the sp1_IterateSprChar_callee function needs to pass the address of the code after popping off the parameters, or the displacement between this and the entry point. It makes this public by useing the XDEF directive. This also gives asm programmers the option of calling your library functions setting up parameters in registers rather than in the C-way. This is well worth doing!
Makefile: /z88dk/libsrc/sprites/software/sp1/Makefile
The Makefile variables are defined in: /z88dk/libsrc/Make.config
A lst file listing all the asm files to include in the library when “make sp1-spectrum” is run:
/z88dk/libsrc/sprites/software/sp1/spectrum.lst
The corresponding header file for inclusion in C programs showing use of __FASTCALL__ and __CALLEE__:
/z88dk/libsrc/sprites/software/sp1/spectrum/spectrum-sp1.h
One of the asm implementations “sp1_IterateSprChar_callee” whose function prototype can be found in the header above:
extern void __CALLEE__ __LIB__ sp1_IterateSprChar_callee(struct sp1_ss *s, void *hook1);
/z88dk/libsrc/sprites/software/sp1/spectrum/sprites/sp1_IterateSprChar_callee.asm
This page is based on a forum post by Alvin (http://www.z88dk.org/forum/viewtopic.php?id=2637)