Author |
Topic: Using subroutines in called files (Read 735 times) |
|
JonR
New Member
member is offline


Posts: 24
|
 |
Using subroutines in called files
« Thread started on: Mar 1st, 2009, 10:44am » |
|
Thinking about the limitation that CALLed programs should not normally define subroutines I wonder whether using the following structure for CALLed code will remove the problem.
The idea is to reinitialise the procedure chain (term?) whilst the main program is temporarily disabled. This should allow routines in the CALLed file to take precedence over those in the main program. Clearing the procedure chain before exiting forces BASIC to forget about any routines declared in the called file - calling these from the main program causes a No such FN/PROC error.
Code:
DEFPROCyKYT3yk:ENDPROC :REM Dummy routine
P%=!PAGE :REM Store first program word
!PAGE=&FFFF00 :REM Temporarily clear program
!328=0 :REM Clear subroutines
PROCyKYT3yk :REM Initialise subroutines
!PAGE=P% :REM Restore program
REM ...code goes here...
!328=0 :REM Clear subroutines
RETURN
REM Subroutine definitions
Caveat: The CALLed file must not call any routines in the main program.
It would be useful to adapt a test case that will normally cause BB4W to crash to see if this technique resolves that case.
Edit: Further testing shows that this method appears safe when the CALLed code is overwritten by something else.
JonR
|
« Last Edit: Mar 1st, 2009, 9:03pm by JonR » |
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Using subroutines in called files
« Reply #1 on: Mar 1st, 2009, 9:08pm » |
|
Quote:The idea is to reinitialise the procedure chain (term?) whilst the main program is temporarily disabled. This should allow routines in the CALLed file to take precedence over those in the main program. |
|
The problem with that idea is that it will result in a memory leak. When you force a re-scan of the PROCs (having zeroed the pointer to the linked list) their addresses will be stored on the heap as usual, but it will be a new block of heap each time!
Try copying your listed code into a file called CALLJON.BBC and calling it as follows:
Code:
PRINT END
CALL "CALLJON"
PRINT END
CALL "CALLJON"
PRINT END
The problem should be apparent.
Richard.
P.S. Thank you for rescuing the Conforums board from the doldrums!
|
|
Logged
|
|
|
|
JonR
New Member
member is offline


Posts: 24
|
 |
Re: Using subroutines in called files
« Reply #2 on: Mar 1st, 2009, 9:49pm » |
|
Storing and restoring the head of the callers linked list would mean that the procedure chain for the main program would not need to be needlessly recreated which is likely to be the cause of the larger memory leak. Ideally CALLed code should not have any routines at all!
Code:
DEFPROCyKYT3yk:ENDPROC :REM Dummy routine (the name must be unique to the file it is in)
P%=!PAGE :REM Store first program word
Q%=!328 :REM Store subroutines
!PAGE=&FFFF00 :REM Temporarily clear program
!328=0 :REM Clear subroutines
PROCyKYT3yk :REM Initialise subroutines
!PAGE=P% :REM Restore program
REM Body of CALLed routine
!328=Q% :REM Restore subroutines
RETURN
Where the above code is not called repeatedly the one time wastage for each CALLed file using the technique seems acceptable. In the case where the file would be called repeatedly the programmer is already encouraged to incorporate the code into the main program or a library.
However, in IDE macros using a cut down version of the above should moot the "routines in CALLed macros can't share names with the currently edited program" problem:
Code:
DEFPROCyKYT3yk:ENDPROC :REM Dummy routine (the name must be unique to the file it is in)
P%=!PAGE :REM Store first program word
!PAGE=&FFFF00 :REM Temporarily clear program
Q%=!328 :REM Store subroutines
!328=0 :REM Clear subroutines
PROCyKYT3yk :REM Initialise subroutines
!PAGE=P% :REM Restore program
REM Rest of generic macro header...
REM Body of macro
RETURN
REM Subroutines
I should probably post that code on this forum in another thread...
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Using subroutines in called files
« Reply #3 on: Mar 1st, 2009, 10:00pm » |
|
Quote:The problem with that idea is that it will result in a memory leak. |
|
Here's a technique that solves the problem of the CALLed module potentially being loaded at a different memory address each time, but without causing a memory leak:
Code:
ok% = TRUE
ON ERROR LOCAL ok% = FALSE
IF ok% PROC_mustn`t_exist
RESTORE ERROR
REM ...code goes here...
PROCsub1
PROCsub2
RETURN
REM Subroutine definitions
DEF PROCsub1
PRINT "sub1"
ENDPROC
DEF PROCsub2
PRINT "sub2"
ENDPROC Instead of zeroing the base pointer, this technique forces a PROC re-scan by attempting to execute a non-existent PROC.
It doesn't have the effect of changing the priority (a PROC with the same name in the main program will take precedence).
Richard.
|
|
Logged
|
|
|
|
Malcolm
Guest
|
 |
Re: Using subroutines in called files
« Reply #4 on: Mar 1st, 2009, 10:19pm » |
|
Is all that code in the CALLed file?
I don't understand why that works. Can you add a couple of words of explanation for the not so expert.
Malcolm.
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Using subroutines in called files
« Reply #5 on: Mar 1st, 2009, 10:56pm » |
|
Here's a version combining Jon's method with mine so that it both changes the priority (so that the PROCs within the CALLed module have priority over those in the main program) and works correctly even if the module is loaded at a different address each time (without a memory leak). It also has the advantage that the CALLed module can still execute PROCs and FNs in the main program:
Code: P% = !PAGE
!PAGE = &FFFF00
ok% = TRUE
ON ERROR LOCAL ok% = FALSE
IF ok% PROC_mustn`t_exist
RESTORE ERROR
!PAGE = P%
REM ...code goes here...
PROCsub1
PROCsub2
ok% = TRUE
ON ERROR LOCAL ok% = FALSE
IF ok% PROC_mustn`t_exist
RESTORE ERROR
RETURN
REM Subroutine definitions
DEF PROCsub1
PRINT "sub1"
ENDPROC
DEF PROCsub2
PRINT "sub2"
ENDPROC Here's how it works. Firstly the main program is temporarily disabled, exactly as with Jon's method. A re-scan is forced, by attempting to execute a non-existent PROC; since the main program is disabled the only addresses added/updated are those of PROCs in the CALLed module (and libraries). Now the main program is enabled again.
At this point the CALLed module can safely execute PROCs defined therein, because their addresses have been updated.
Finally, another re-scan is forced, but this time with the main program enabled. Now the normal priority rules take effect, so if the main program has one or more PROCs with names matching those in the CALLed module or libraries, the former will take priority.
Richard.
|
|
Logged
|
|
|
|
Malcolm
Guest
|
 |
Re: Using subroutines in called files
« Reply #6 on: Mar 2nd, 2009, 05:45am » |
|
Wow. Little did I know what I had started when I asked the questions, thanks both.
It has gone from my limited and faulty implementation to a much wider general case solution.
Can I test my understanding? Normally a list is kept of where the DEF's reside. This is a linked list ordered by name so if there are two PROCs with the same name the second DEF position address will overwrite the first. Here we are first forcing a rescan of just the CALLed part to make sure those addresses are correct in case a previous CALL had left a different address for that DEF name. Then you are rescanning the main program just before returning to reinsert the main program's address of a DEF PROC previously overwritten by an address in the CALLed part.
So the second part is only needed if you have duplicate PROC names in the Main and CALLed parts. But the first part and forced rescan is for repetative CALL's of the same file or repeated CALLs to different files that have a DEFPROC of a particular name which might be in different position each time, either because of changes to HIMEM etc. or in different CALLed files.
regards,
Malcolm
|
|
Logged
|
|
|
|
Malcolm
Guest
|
 |
Re: Using subroutines in called files
« Reply #7 on: Mar 2nd, 2009, 2:05pm » |
|
Addendum: I see that you do need the second scan of the DEF's that are in the main program to flush any old references to the CALLed file just run.
So all the code that Richard proposed is needed every time for safe operation if you want to put DEFPROC's in a CALLable file.
Malcolm
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Using subroutines in called files
« Reply #8 on: Mar 2nd, 2009, 5:22pm » |
|
Quote:I see that you do need the second scan of the DEF's that are in the main program to flush any old references to the CALLed file just run. |
|
I don't see that myself. I thought your earlier comment that "the second part is only needed if you have duplicate PROC names in the Main and CALLed parts" (or duplicate names in the main program and a library) was correct.
If you're writing a macro you have to assume there may be duplicate names, and do the second scan anyway. Nevertheless I'd be interested to know why you think it's always necessary.
Richard.
|
« Last Edit: Mar 2nd, 2009, 5:31pm by admin » |
Logged
|
|
|
|
MalcolM
Guest
|
 |
Re: Using subroutines in called files
« Reply #9 on: Mar 3rd, 2009, 12:36am » |
|
I was thinking that the DEF in the CALLed program would remain in the PROC's list and so a call to that procedure from the main program could result in an erroneous address. So removing it from the list stops that possibility.
Am I over processing the brain on this one?
Malcolm.
|
|
Logged
|
|
|
|
Malcolm
Guest
|
 |
Re: Using subroutines in called files
« Reply #10 on: Mar 3rd, 2009, 03:26am » |
|
When does the position of the DEF's get recorded normally? Is it as part of the Run initialization or does it wait until the first unrecognized PROCname is encountered.
Similarly in a CALLed module.
Malcolm.
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Using subroutines in called files
« Reply #11 on: Mar 3rd, 2009, 08:25am » |
|
Quote:I was thinking that the DEF in the CALLed program would remain in the PROC's list and so a call to that procedure from the main program could result in an erroneous address. So removing it from the list stops that possibility. |
|
It does remain in the list of PROCs and it will result in a nasty crash if you try to execute it from the main program, but doing the second scan makes no difference to that. As with ordinary variables, arrays etc., once the PROC is recorded on the heap you can't "remove" it again (other than with CLEAR). It's also the reason that LOCAL variables are visible globally.
It's (arguably) one of the biggest weaknesses of BBC BASIC, but it's always been that way.
Richard.
|
|
Logged
|
|
|
|
|