This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Init routines.

Init routines.
by on (#110492)
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 initFoo
Another file:setLibraryInitRoutine initBar
Then, somewhere suitable in the main calling code (somewhere after reset): executeLibraryInitRoutines

Code:

.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
Re: Init routines.
by on (#110496)
I wonder if the constructor and destructor system built into ca65 might help you implement something like this.
Re: Init routines.
by on (#110499)
Probably, but I don't really understand how they work.
Re: Init routines.
by on (#110518)
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.
Re: Init routines.
by on (#110522)
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.
Re: Init routines.
by on (#110523)
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).
Re: Init routines.
by on (#110524)
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.
Re: Init routines.
by on (#110525)
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.
Re: Init routines.
by on (#110530)
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.
Re: Init routines.
by on (#110531)
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.
Re: Init routines.
by on (#110532)
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).
Re: Init routines.
by on (#110533)
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
Re: Init routines.
by on (#110534)
Okay I'll give it a try in my actual code, since there are some nice advantages.
Re: Init routines.
by on (#110535)
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.
Re: Init routines.
by on (#110536)
blargg, your code works fine. I only tested it on one "constructor" but I see no reason it wouldn't work for more.