I believe that I have finally figured out the deal with relocation.
So, the structure of libraries will look like this: (Small sample library)
NOTE: As of right now, this will only support libraries written in SPASM. However, since ZDS also spits out the assembly source, you could technically use C code in a library as well, I guess.
Code:
function(string) is part of a collection of macros that builds a jump table file that looks like this:
Code:
libname("SAMPLELIB") <- Sets the name of the library
libvers(1) <- Sets the version of the library
This jump table simply provides ZDS something static to link against. This above code block will be placed at the start of the program. This prevents us from having to do countless relocations if a library's functions are used a lot. Not only that, but it prevents the C program from (technically) having a relocations table of its own.
Now, ZDS cannot simply use these functions, as they have not been prototyped. This header file fixes that:
Code:
Now we can use these functions properly. However, there needs to be a simple kernel linker on-calc in order to do this. The jump table holds the offsets of the functions from the start of the library (technically), and all that needs to be done is add the pointer of the library to the offsets. Boom, the C program is working properly, and has entry points. Yay.
However, there is still the issue of relocations within the library itself. If you take a look back at the first code block, you may see this:
Code:
Silly, I know. But it gets the point across. The absolute address has to be resolved. So, the numreloc() macro that you may have seen earlier needs to be set to the number of these macros. (It generates an error if the number you supplied doesn't match the number it counted, and then tells you how many it counted). It creates a relocation table at the start of the library in which the location of the address to update is stored. So in this case, the relocation table would be 6 bytes long. We don't have to worry about much else, because pointers and all the rest work exactly the same way.
Now, we are almost there. What if LibraryA wants to use functions in LibraryB and vice-verca? Well, it can simply use exactly the same jump table file as ZDS uses! The on-calc loader will be able to handle it exactly the same, it just needs to make sure to only load the library once though, probably through setting a flag and updating a pointer.
The best part is that the macros and things have already been written, I just have to figure out some kinks in the loader. Right now I plan to do things in this order:
Code:
Ans thus I solved the problem that you all wanted to do. It works quite well, in my opinion. Could it use some fine-tuning? Sure. Things like only including functions that the program uses from the library sounds like an important issue. Technically though that is really easy to fix at some point too, as it just requires the removals of jump instructions from the include file. Please let me know what you guys think! (And before you ask, yes, it easily supports multiple libraries in this way. )
Edit: In this way, I can also make sure that the relocation table of the library doesn't have to be copied to RAM. This should help save a bit of space in some instances.
So, the structure of libraries will look like this: (Small sample library)
NOTE: As of right now, this will only support libraries written in SPASM. However, since ZDS also spits out the assembly source, you could technically use C code in a library as well, I guess.
Code:
libname("SAMPLELIB")
libvers(1)
numreloc(2)
lib_begin()
function("CE_set8bpp")
_set8bpp:
ld a,$27
_:
ld (mpLcdCtrl),a
ret
function("CE_set16bpp")
_set16bpp:
ld a,$2D
jr -_
function("CE_setHLTo259")
_setHLTo259:
relocate()
call ldhl
relocate()
jp ldhl
ldhl:
ld hl,259
ret
lib_end()
function(string) is part of a collection of macros that builds a jump table file that looks like this:
Code:
db "SAMPLELIB",0
db 1
_CE_set8bpp:
jp 0
_CE_set16bpp:
jp 7
_CE_setHLTo259:
jp 11
libname("SAMPLELIB") <- Sets the name of the library
libvers(1) <- Sets the version of the library
This jump table simply provides ZDS something static to link against. This above code block will be placed at the start of the program. This prevents us from having to do countless relocations if a library's functions are used a lot. Not only that, but it prevents the C program from (technically) having a relocations table of its own.
Now, ZDS cannot simply use these functions, as they have not been prototyped. This header file fixes that:
Code:
// Include the assembly file directly into it
#pragma asm ".include \"SAMPLELIB.inc\""
/**************************************************
Sets the LCD mode to 8bpp.
**************************************************/
void CE_set8bpp(void);
/**************************************************
Sets the LCD mode to (Default) 16bpp.
**************************************************/
void CE_set16bpp(void);
Now we can use these functions properly. However, there needs to be a simple kernel linker on-calc in order to do this. The jump table holds the offsets of the functions from the start of the library (technically), and all that needs to be done is add the pointer of the library to the offsets. Boom, the C program is working properly, and has entry points. Yay.
However, there is still the issue of relocations within the library itself. If you take a look back at the first code block, you may see this:
Code:
function("CE_setHLTo259")
_setHLTo259:
relocate()
call ldhl
relocate()
jp ldhl
ldhl:
ld hl,259
ret
Silly, I know. But it gets the point across. The absolute address has to be resolved. So, the numreloc() macro that you may have seen earlier needs to be set to the number of these macros. (It generates an error if the number you supplied doesn't match the number it counted, and then tells you how many it counted). It creates a relocation table at the start of the library in which the location of the address to update is stored. So in this case, the relocation table would be 6 bytes long. We don't have to worry about much else, because pointers and all the rest work exactly the same way.
Now, we are almost there. What if LibraryA wants to use functions in LibraryB and vice-verca? Well, it can simply use exactly the same jump table file as ZDS uses! The on-calc loader will be able to handle it exactly the same, it just needs to make sure to only load the library once though, probably through setting a flag and updating a pointer.
The best part is that the macros and things have already been written, I just have to figure out some kinks in the loader. Right now I plan to do things in this order:
Code:
Library and Program both exist in Archive
Load C program to UserMem
Check if dependencies exist
Load dependencies after program in RAM
Update program entry points
Update library dependencies
Resolve inner library addresses.
Enjoy.
Ans thus I solved the problem that you all wanted to do. It works quite well, in my opinion. Could it use some fine-tuning? Sure. Things like only including functions that the program uses from the library sounds like an important issue. Technically though that is really easy to fix at some point too, as it just requires the removals of jump instructions from the include file. Please let me know what you guys think! (And before you ask, yes, it easily supports multiple libraries in this way. )
Edit: In this way, I can also make sure that the relocation table of the library doesn't have to be copied to RAM. This should help save a bit of space in some instances.