I have a code resource that has reached the 32K limit so now I'll need to break it off into a segment that is CALLed from my main code. Is this what DCOD's are for? And where can I find documentation on DCOD's in the FB manuals?
Also, is there a demo somewhere which shows how to call an external code resource?
It would be so nice if FB had a separate manual on writing code resources. :-)
DCOD's are handy when you want to reuse your code in multiple projects because it's already compiled. It also let you sell your code in a compiled form if you want. C programmers could easily use your DCOD's.
If you reach the 32K limit, you can call multiple DCOD's from your MAIN, or you can call a DCOD from a DCOD, but it can be a bit more complicated if you want the first-called DCOD to return a variable.
See in the Reference manual, keyword: CALL (resources)
If you are writing anything that uses the mini-app runtime:
You cannot, in FB, use more than 32 K code.
DCODs can be a work around in this situation, but beware each DCOD will also give you 3K of runtime, so if you cut all you FNs up into DCODs you'll soon see the extra baggage that this will give you.
The bright young sparks on this list (Mars are you still there) may be disposed to explain how you can knit your own jump table to get round the 32 K limit, but this tends to get above my particular comprehension.
There may be a conflict in the terms that everyone is using. Did the original poster meant that he needed a SEGMENT statement in his main? Or is he actually creating a menu def or system extension?
Actually, I'm writing a TOLZ resource. I'm also working on an INIT that may exceed the 32K limit in the near future (two separate projects).
I think the trick will be to find a way to change each INCLUDE file into its own DCOD, that is by having one entry point into an ENTERPROC% which is essentially a dispatch function. The problem would be when different numbers of parameters are passed to different functions--you would probably have handle this by passing the dispatch routine the address of a parameter list (maybe the address of the first variable that was DIMMED in your calling function) and by sharing a common globals file, if this is possible.
Further clarification will be appreciated.
I have a related question already :-)
Can you use DEF FN's in a DCOD? ie If you are converting an INCLUDE file into a DCOD and the INCLUDE file has several DEF FN's at the top of it, can you leave the DEF FN's intact and will they be called properly from the DCOD? Since globally saved pointers are globally accessible, I could see this being possible, but want to make sure. Has anyone tried this?
I can think of a few skanky tricks you could pull with @FN and FN USING, but unfortunately everything I've come up with so far is either hard, or would make FB's globals die.
One thing you could do would be to make a label/enterproc/exitproc trio for every DCOD function you want to make available to your main program:
LOCAL FN foo
OK, so you have two valid entry points in your code resource now. Unfortunately, there's no (clean) way to find them from outside. So here's how you get around it. Make a main entry point which sends them "outside" to anyone who wants to know:
LOCAL FN bar
_fnPtrLen = 4
This whole mess would get compiled into a DCOD or something. To use this added package of functions, load and lock the DCOD, then call it:
&fnArrayPtr& + 0 * _fnPtrLen, LINE "foo"
&fnArrayPtr& + 1 * _fnPtrLen, LINE "bar"
Once this is done, myFnsArray&(0) will contain the address of Foo and
myCodeHndl& = FN GETRESOURCE(_"DCOD", _somerandomid)
LONF IF myCodeHndl&
err = FN HLOCK(myCodeHndl&)
'Do not, under any circumstances, unlock this resource until you are
ready to dump it.
'Now call the code; pass it the address of the function pointer array.
myCodePtr& = [myCodeHndl&]
FN reportSomeHorrendousError("Couldn't load a necessary resource.")
myFnsArray&(1) will contain the address of Bar. You can call these addresses with CALL, passing whatever parameters you like. Just make sure they match the parameters your ENTERPROC inside the code resource uses!
This isn't trivial to implement, and you can't share globals across resources (not that a code resource really ought to have many globals in the first place), but it should do the trick. And it's a lot simpler than trying to cram all fifty functions into one entry point!
Yes and no. You can use DEF FN, but you don't have access to the application's globals. So you must pass the global addresses to the DCOD before you can call any of the FNs.
Thanks, Chris. I may seem a little clueless here, but how do you go about passing the address of the globals that FB is using? I'm familiar with the method of defining your own global space that Bill explained, but the FB DCOD's don't seem to bother with this. They have a way of directly accessing the globals that are being used by the main app. How is this done?
OK. First set up the globals in your project.
gFNfred& = @FN fred
gFNMatilda& = @FN Matilda
The parameters for your DCOD should include some type of selector. Here's a rough idea of what would be required.
_dcodSelInit = 1 'DCOD
_dcodSelFred = 2 'DCOD gets FN Fred's address
_dcodSelMat = 3 'DCOD gets FN Matilda's address
_dcodSelBoo = 4 'DCOD makes a scary sound
_dcodSelQuit = 5 'DCOD shuts down
The DCOD accepts these values as parameters.
CALL "DCOD",1001,(_dcodSelInit, 0&)
CALL "DCOD",1001,(_dcodSelFred, gFNfred&)
CALL "DCOD",1001,(_dcodSelMat , gFNMatilda&)
There's still one thing that's confusing me: in the code for the FB Debugger and Project Manager Tools, the DCOD's have total access to the globals without having to have any special values passed to them. They even share the same globals file as the main via the GLOBALS statement at the top of the file. I tried setting up my test program using the same structure, though, and any global values that I set in the main came out as zeros in the DCOD. Just wondering if there's something I'm missing that would allow total access to globals.
In those tools, the DCOD doesn't really have total access to all globals. Some DCODs do however, because they blockmove all vars from the caller into a local copy of the globals file. I don't recommend it for a tool tho.
Actually, wouldn't you just need to set up A6 properly in your DCOD (isn't this the register that FB uses to access globals?) I tried messing with this without success, though.
I also tried callling my test DCOD (which attempts to access globals) from the TOLZ shell supplied with FB and actually trashed the system file--a first with FB. Luckily I always boot from a RAM disk when programming. :-)
Any further information will be appreciated, for the purpose of deeper understanding. But in practice I will use the global passing technique that you suggested in a previous post.
<< Actually, wouldn't you just need to set up A6 properly in your DCOD (isn't this the register that FB uses to access globals?) I tried messing with this without success, though. >>
It's A5 actually. MPW-built code resources use A4, but I think FB uses A5 for everything.
A6 is the local variable space.
<< I also tried callling my test DCOD (which attempts to access globals) from the TOLZ shell supplied with FB and actually trashed the system file--a first with FB. Luckily I always boot from a RAM disk when programming. >>
Tolzes are deep mystery. I attempted to write one, once, that would intercept FB's 'this module is not compiled' message and instead have it start compiling that module. I didn't get too far...
I'd rigorously test everything you write *outside* of the Tolz format first. There will be enough blind tripwires just getting your code to run as a tool anyway.
Just wondering how to get access to globals in a DCOD. Some of the examples that come in the tools folder do this. For example, the source code for the Project Manager has a DCOD that has a GLOBALS statement at the top and seems to access and change global variables without any problems. However, when I set up a test DCOD and called it from a test program, I couldn't get it to print out the correct global values--it always prints zero.
The above-mentioned DCOD does some register and resource work on entry, so this may be what is required. It's not clear, though, since this part of the code is thinly commented.
The way I do it is to allocate a pointer in the main that is the size of the globals I need, then I pass the pointer to the code resource and access the globals in both the main and the code resource as offsets of the pointer.
Define your globals structure in both your main program and your code resource. Next allocate a long int for your globals pointer in your main program's globals as follows...
Then allocate your pointer in your main as follows...
DIM RECORD myGlobals
DIM END RECORD.myGlobals
gPtr& = FN NEWPTR(_myGlobals)
Each time you enter your code resource, pass it your globals pointer as a parameter and you will be able to access your globals in both your main program and your code resource via offsets from the pointer as follows...
gPtr&.var1& = 2
gPtr&.var3% = 74