From: Ullrich von Bassewitz (uz_at_musoftware.de)
Date: 2003-05-01 23:53:36
On Thu, May 01, 2003 at 02:30:40PM -0700, Shawn Jefferson wrote: > Ullrich: What things did you think could be put into a constructor? I saw a bunch of stuff around changing the margins and cursor state, as well as a "conio init". There is also some stdio definitions but these probably have to be there (?) A constructor is used to run initialization code that is needed when one specific module is used. The docs for the assembler (ca65) do contain a more detailed explanation together with an example. The problem constructors (and destructors) solve is the following: If part of your library needs initialization (say conio, or file i/o) this initialization must only be called when the specific parts of the library are actually used. If a C program doesn't use file i/o, there's no need to initialize it. In fact, doing so would include code and data that is not needed. If you have libraries (like the one cc65 comes with) that contain many optional parts, initialization for all these parts can eat up quite some memory, and if you're calling the initialization code from the startup module, *all* programs will have the overhead, even if they don't use anything from the library. > How does a constructor work? Does it get put into the constructor/destructor table when a module containing a constructor function is linked to your program, and then crt0.s calls a function that jsrs to each of the constructors? A constructor is nothing more than a command that tells the assembler to export a name in a special way. The linker is able to build a table containing all the marked identifiers from all modules linked into an executable. If you look into the standard linker configs, you will find something like FEATURES { CONDES: segment = RODATA, type = constructor, label = __CONSTRUCTOR_TABLE__, count = __CONSTRUCTOR_COUNT__; CONDES: segment = RODATA, type = destructor, label = __DESTRUCTOR_TABLE__, count = __DESTRUCTOR_COUNT__; # condes functions with type 2 are called in the interrupt CONDES: segment = RODATA, type = 2, label = __IRQFUNC_TABLE__, count = __IRQFUNC_COUNT__; } This tells the linker to build a table with the name __CONSTRUCTOR_TABLE__ for all contructors, another one with the name __DESTRUCTOR_TABLE__ with all destructors, and a third one with the name __IRQFUNC_TABLE__ for all identifiers marked as .CONDES type 2 (you can see from that example that the use is not limited to constructors and destructors). The startup file contains library calls that run the constructors/destructors in the table: jsr initlib ; Call module constructors jsr donelib ; Call module destructors This means that the startup code doesn't need to know, which modules need initialization. It just calls some subroutines that in turn call all functions from a table, which is built by the linker. This does also mean that adding another subroutine has a very low overhead: Just two bytes in the table. One thing to care about is that the tables do only contain functions from modules linked to the executable. So if there is no reference to a module, it won't get linked to the executable, and it's constructor is never called. So a constructor has to go into a module that is shared by all modules that implement a common API. Regards Uz -- Ullrich von Bassewitz uz_at_musoftware.de ---------------------------------------------------------------------- To unsubscribe from the list send mail to majordomo_at_musoftware.de with the string "unsubscribe cc65" in the body(!) of the mail.
This archive was generated by hypermail 2.1.3 : 2003-05-01 23:53:59 CEST