A simple idea for CA65.
In every library that would benefit from initialization, one could use this macro code rather than explicitly doing so for each library.
For example, in an included file:
setLibraryInitRoutine initFooAnother file:
setLibraryInitRoutine initBarThen, somewhere suitable in the main calling code (somewhere after reset):
executeLibraryInitRoutinesCode:
.ifndef _INIT_LIB_H_
_INIT_LIB_H_ = 1
.scope initialize
initCounter .set 0
.endscope
.macro setLibraryInitRoutine proc
initialize::initCounter .set initialize::initCounter + 1 ; first entry starts at 1
initialize::.ident(.sprintf("%s%d","_Library_Init_List_", initialize::initCounter)) = proc
.endmacro
.macro executeLibraryInitRoutines
.if .not initialize::initCounter
.exitmacro ; if none, do nothing
.endif
.repeat initialize::initCounter, I
jsr initialize::.ident(.sprintf("%s%d","_Library_Init_List_", I+1))
.endrepeat
.endmacro
.endif
I wonder if the
constructor and destructor system built into ca65 might help you implement something like this.
Probably, but I don't really understand how they work.
I looked into using the built-in functions, and they would probably work fine, but they seem overly complex for what I want to accomplish.
I thought that ca65 could allow each module to denote an constructor and destructor routine, and then at runtime you can get a list of 16-bit pointers to these to iterate over.
Pretty much. I may still try it, it's pretty close to what I am doing, but the linker generates the list and passes you an address to the list (of constructors). Then you have to write code to jump to the list of address, which will require RAM usage to do so. A small list of JSR seems effective enough (as long as it stays small).
I don't think it uses any RAM. It should put the list of routine addresses in ROM. I'm not sure how it could use any RAM anyway.
Any global non-const statics will use RAM that must be initialized at startup. Other than that, it should just be calling the requisite code, which should all be in ROM.
If you look at crt0.s it should give you an idea of what it does.
For my own purposes, though, I just rewrote crt0 and got rid of all setup except the RAM initialization. I had cut the CRT down so much that nothing in it needed init code anyway.
What I was trying to say and obviously failed to communicate properly was that the code that's going to walk the list of addresses is going to somehow have to use RAM, since when using only CA65 you have to write the code to call the list of constructors. The default routine uses quite a bit of RAM and self modifying code. (condes.s)
Here.
I'm pretty certain the RTS trick would let you get away with just using temporary bits of stack for walking the array. At least as long as you have fewer than 128 or 256 function pointers. You're talking about JSR, so you're already positing a functional stack.
It's not a big deal, I could use a bit of RAM, I just see it as more complex that is needed, though it does have the advantage of working across different modules. (A pre-compiled obj file could have it's init routine called from another source.)
But to argue the point, I don't see how to code such a routine that is not going to have to use RAM to create a pointer to get the pointers. EDIT: Disregard that, something like this might work:
Code:
ldy #0
loop:
tya
pha
lda __CONSTRUCTOR_TABLE__,y
pha
iny
lda __CONSTRUCTOR_TABLE__,y
pha
rts
pla
tay
iny
iny
cmp #<(__CONSTRUCTOR_COUNT__*2)
bcc loop
EDIT2: For the sake of clarity: I think the list is intended to be called from the bottom (load y first and decrement it).
How about this? (untested) Only uses the stack (which is pretty much a given as it's calling init routines which end in RTS).
Code:
call_ctors:
lda #<(__CONSTRUCTOR_COUNT__*2)
beq @none
@loop: sec
sbc #2
pha
jsr @call
pla
bne @loop
@none: rts
@call: tay
lda __CONSTRUCTOR_TABLE__+1,y
pha
lda __CONSTRUCTOR_TABLE__,y
pha
php
rti
Okay I'll give it a try in my actual code, since there are some nice advantages.
Ah, yeah I didn't realize condes.s did that (as I said, I threw it away anyway). I dunno if I'd call that "quite a bit" of RAM, but it's definitely more than trivial. Seems a strange way to go about that, but I guess it was probably written with an architecture more like C64 in mind.
blargg, your code works fine. I only tested it on one "constructor" but I see no reason it wouldn't work for more.