_Basil

This manual is still to be updated to reflect the changes in versions from 1.40, but is essentially correct.

[Introduction

 Do you use BASIC libraries? Maybe you write your own or use one of the systems such as "DrWimp" or "EventShell". They are a great saving of programming effort.

 The library code is loaded into an application's workspace so that it is accessible to the program, but this uses memory. Moreover, the same code is loaded into each application that uses the library. Many programs do not use the whole, or even a substantial part, of the library, so that more memory than neccessary is used. Does this waste of memory concern you?

 One way to help is to link the library into your program, transferring only the used code, but this must be done after every major change. Another is to break the library into sections and only load those required. A further saving is to make the sections into overlays, but this needs very careful management.

 Perhaps you have noticed that 'C' programmers have a shared library. Maybe you have tried a support module such "WimpExt", but found that it it is quite large and that you are no longer just programming in BASIC.

 If you go back before RISC OS you may have used BASIC's immediate command 'INSTALL' which puts a library permanently in memory so that any program loaded can use it. Of course, only one program at a time could be loaded, so it is redundant in an age of multi-tasking.

 I have been through all these permutations, searching for my Holy Grail, true BASIC shared libraries. Basil is my answer, and it turns out to be surprisingly simple after all.
]
[Getting started with Basil

 The Basil module is loaded in the usual way in a !Run file, eg:

> RMEnsure Basil 1.40 RMLoad Basil:Modules.Basil

 At the start of the program, link to the library list and ensure the required libraries with, eg:

> REM>!RunImage
> *BasilLink
> *BasilFind MyLibrary Basil:Libraries.MyLib
> *BasilFind ...

 Use the library routines as usual.

 Before ending, declare that the program no longer needs the libraries with, eg:

> *Basil_Lose MyLibrary
> *Basil_Lose ...
> END

 It is not essential for your program to lose libraries, but it is the friendly thing to do. Basil will free the memory when a library is not being used.

 Alternatively you may be able to use an already loaded shared library system with, eg:

> REM>!RunImage
> *BasilLink SharedLib
]
[How Basil works

 My BASIC Reference Manual says: "Basic libraries are stored as a word, which is a pointer to the next library (0 denoting the end of the list). The word is followed immediately by the BASIC program which forms the library."

 There are two library lists maintained by BASIC, with pointers in the workspace designated LIBRARYLIST and INSTALLLIST. The former is for libraries loaded into the variable heap with the keyword 'LIBRARY', and is the one normally used. The latter is for libraries loaded at the top of memory using the immediate command 'INSTALL'. 'INSTALL' cannot be used inside a program and is is effectively redundant, leaving INSTALLLIST to be used by Basil.

 The Basil module puts an empty BASIC program into its workspace to act as a link library and as an end marker. (I know this is slightly redundant, but it is flexible to program, and is retained from earlier versions)

 A BASIC program calling *BasilLink causes the address of the link library to be put into INSTALLLIST, faking an 'INSTALL' command.

 *BasilFind looks for a library in the list. If it is not found it loads a default file into the relocatable memory area (RMA), checks that the it contains the required library, and links it into the list. A counter is incremented to record that the library is in use.

 The keywords 'LIBRARY' and 'OVERLAY' are unaffected.

 *BasilLose decrements the counter and if the count is zero the memory in RMA is released and the links in the list are adjusted.

 Maintainers of shared library systems can load their libraries into one of Basil's multiple lists with *BasilEnsure, and make them available to programmers very simply.

 To see what libraries are in a list use *BasilList, which acts a bit like the immediate command 'LVAR'.

 Because BASIC caches the memory addresses of routines when they have been used once, the Basil module and its found libraries cannot be moved or replaced without crashing a client program. If Find and Lose pairs are used consistently there will be no problems, but Basil will not die while it is in use. In case of emergency *BasilReset will remove all libraries, but it should be used with care.

 There are SWI equivalents to the main commands, but these are now deprecated. Basil also provides two SWIs and two service calls that module libraries and my RFSFiles module use to link into a list. They should not be used in a program.

 Details of the commands and SWIs are in the Appendices.
]
[Using libraries with Basil

{Multiple independent library lists
 The description of 'How Basil Works' is of the basic operation, but Basil can support any number of lists by using new instantiations of the module. This does not increase the number of modules loaded, but provides extra link libraries to be the roots of the new lists. A BASIC program can only link to one list at a time, but with care it can swap between lists.

 To use a new list it is only necessary to follow each of the commands with an identical and unique reference name. The commands without a name are actually using a list called 'Base' but that is transparent to the user. The lists that have been created can be seen by calling *Modules. They will appear as 'Basil%Base', 'Basil%MyLib' etc.

 Although the method of Finding and Losing libraries in a program is efficient with memory and therefore to be encouraged, it is quite likely that libraries will be used from a library system, such as SharedBLib or JFShared. These could be used in that efficient way, but it would be more convenient and manageable if they linked all their libraries into a unique list, which then requires minimal effort within a program; eg: just *BasilLink SharedBLib.

 For this to work, the resource application containing the libraries has to be written using Basil. This should be done by their maintainers/authors, but they could be modified by users. See appendix 'Library resource applications'.
}
{Format of library headers

 The BASIC code inside a Basil library should conform to the normal recommendations for libraries; Basil is not concerned with this.

 However, Basil needs to find the name of a library, and it uses the line number of the first line to count the number of finds.

 So the first line must be line 0  and be formatted this way:

> 0 REM> {sp} <name> [{sp}+ <version>] {sp} [<other>]

. {sp}  	= 0 or more spaces
. {sp}+ 	= 1 or more spaces
. <name>	= unique name
. <version>	= version number - optional
. <other>	= any other information, such as a date string

 For example:

> 0 REM> MyLibrary 1.34 (29 Feb 2000)

 This is quite a common way of doing things anyway.

 At the moment Basil does not use the version number but may in future, so it should be included for compatibility.
 
  At the moment Basil forces line number 0, but in the future this may be used to discriminate libraries from general programs. Line 0 can be entered in any editor showing line numbers. StrongED can be set up to save with lines numbered from 0. Zap can be set up with physical line numbers starting at 0. The BASIC direct commands AUTO and RENUMBER accept 0 as their first parameter.
}
{Conflicting versions of libraries

 Because Basil keeps libraries once they have been loaded and are in use, it may not be possible to install a required newer library version if an older with one with the same title is already installed.

 A similar problem existed when, for example, the SharedCLibrary was not in ROM but was RMEnsured by applications. It was not uncommon for a newer version to replace an older one and cause C programs to crash when they could no longer find their pointers.

 The solution was to make sure that only the latest version of the module was visible to all applications, in the !System.Modules directory. !SysMerge, or on RiscPCs the System configuration, is used to make sure that this is so.

 I recommend that this common-use practice is used with Basil libraries, and the '!Basil' application is one way of holding libraries to do this. Basil modules can be put in the System as normal. See appendix 'Library resource applications' for how other library systems can be configured.

 When developing libraries, SYS"Basil_Find" can be configured to force a new version of a found library to the beginning of the list, leaving the old version resident. This should not be used in a finished program, as it leads to a memory leakage, and there is ambiguity when libraries are lost. This is now deprecated.
}
{Names of library routines

 With several libraries installed and each containing many procedures and functions there is great scope for the existence of duplicates. A program will use the first routine it finds in the list, and the order of libraries is undefined.

 To avoid these conflicts I recommend a naming convention identical to that used for SWIs. The first part of a name should be the library name, followed by '_' and then the name of the routine.

 For example, if the library title is 'MyLibrary' then the procedure to make a sound could be PROCMyLibrary_MakeSound().

 The introduction of multiple library list makes this a far less serious problem.

 See 'Feedback' for more about this.
}
{Basil module libraries

 From version 1.10, Basil provides for library modules to install and remove themselves. The advantage is that the libraries remain at the same place in memory if not killed or reinitialised; the disadvantage is that they have an overhead of about 200 bytes and they do not free memory when not in use. The modules will be created by the !Basilica application. [not yet implemented]

. They are shown after *BasilList with a "*" suffix.

. They always have a count of 0.

. They are found by *BasilFind" but otherwise unaffected.

. They are not removed by *BasilLose.

. They are removed from the list by *BasilReset.

. The modules remain in memory and they re-install the libraries when Basil is loaded or re-initialised.

 See 'Installing libraries from memory' for details and warnings about creating module libraries.

 With multiple library lists and the use of *BasilEnsure in !Run files, module libraries become less useful. All but the last of the bulletted items above apply to such libraries too.

 My RFSFiles module allows BASIC files to be added to ResourceFS, and they may install and remove themselves in the same way as library modules. The overhead is slightly less, and it is more flexible.
}
{Overlay libraries

 If reducing memory usage is important, Basil can be used to implement overlays, independent of the OVERLAY statement. This is done by calling *BasilFind just before using a library, and *BasilLose just after. This can be nested, and even recursive I think, but like all such calls, they must be in pairs.
}
]
[Application workspace

 When most of a program is contained in Basil libraries, the application workspace claimed with *WimpSlot can be much reduced.

 The BASIC language workspace is just under 4k. The pagesize on a RiscPC is 4k and on a 1M Archimedes 8k. So the smallest wimpslot must be 8k. Many simple utilities can run in this if the variables and code take less than 4k.

 Unfortunately a 2M Archimedes takes 16k and a 4M takes 32k. This additional space is inevitably wasted without some tricky program combining.

 However, if memory is to be claimed for data, say sprites, windows, messages or drawfiles, then the space can be used.
]
[Installing module libraries

 Basil module libraries and my RFSFiles module use the facility to install BASIC libraries held in memory, the RMA in these cases.

 The libraries must conform to the Basil requirements and there must also be one writeable word immediately before the BASIC code. The address of this word is passed in SWIs "Basil_Install" and "Basil_Remove".

 To allow for operations on the Basil module, the controlling program must be able to respond to service calls, and will therefore be a module too.

 It is essential to respond to "Service_BasilResetting" by removing all linked libraries, so that Basil can reset. It is likely that removed libraries will be relinked in response to a following "Service_
BasilStarting". (I know that BasilReset could just remove module libraries, but I have at all stages guarded against arbitrary changes to libraries).

 NOTE: BASIC routines are only looked for by name when first used. Thereafter only the address of the routine is used. This is the reason for not allowing libraries to be arbitrarily removed from memory. A benefit of this when installing from memory is that routines that have already been used continue to be available to a program, even if the library has been unlinked, although the use of new routines will produce error 30, 'No such function/procedure'. When the library is re-linked it will be useable as before.
]
[Copyright

 Basil and its documentation are  Steve Drain, Kappa.

. mailto:basil.program@kappa.zetnet.co.uk
. http://www.users.zetnet.co.uk/kappa/
. snailto:Steve Drain, 161a Pymore Road, Bridport, Dorset DT6 3AW

 This version maybe freely distributed and included unchanged in disc libraries and at ftp sites. It is not in the public domain.

 The source code is not included in this package but I can sent it to anyone who is interested.
]
[Appendix 1 - SWIs

{Basil_Link (&50400)

 Links a BASIC program to a library list.

 =>	R0	= 0, or a specific value of INSTALLLIST to use
	R4	= 0, or pointer to name of library list to use

 <=	R0	= the value of INSTALLLIST used
	R4 preserved

 	The default <listname> is Base.
 	The use of this SWI is deprecated in favour of *BasilLink.
 	This call puts the address of the library list into INSTALLLIST.
 	It should be used at the start of a program to link to the list required. The list is created if it does not exist.
 	It can be used at any other time to link to another list but care is needed when handling different <lists>.
 	Note that this SWI only has meaning when called from a BASIC program and the results are otherwise unpredictable. It checks that it has been made from a BASIC program but it is not proof against major changes to BASIC.
 	Possible error is 'Not in BASIC'.
}
{Basil_Find (&50401)

 Ensures a library is available to a BASIC program.

 =>	R0	= pointer to the name of the library to find
	R1	= pointer to full filename of the default file to load
	R2	= version required * 100 [not implemented]
	R3	= 0, or any other value to force default library to load
	R4	= 0, or pointer to name of library list to use

 <=	R0	= count of uses of the library
	R1	= address of the library block
	R2	= version found [not implemented]

 	The default library list is Base.
 	The list of libraries is searched for the name in R0. If it is found its count is incremented. Otherwise the file named in R1 is loaded into RMA. The name is checked again and if not found the memory is freed and an error returned. If found, it is linked to the list.
 	Version number 0 means any version, and is the default.
 	The flag in R3 can be used to load a new version of a library while retaining the old one in the list. In a development environment, this allows programs to continue to find routines they are already using. However, more memory is used for each new version, and SWI "Basil_Lose" will lose the latest library first, which could be ambiguous. This should not be used in completed programs.
 	This call has no effect on module libraries and libraries loaded with *BasilEnsure and the count returned in R0 is 0
 	Possible errors are 'Library not found' and 'List not found'.
}
{Basil_Lose (&50402)

 Releases a library that is no longer required by a BASIC program.

 =>	R0	= pointer to the name of the library to lose
 	R4	= 0, or pointer to name of library list to use

 <=	R0 corrupted

 	The default library list is Base.
 	The list of libraries is searched for the name in R0. If it is found its count is decremented and if this is then zero the memory is freed. Otherwise an error is returned.
 	This call has no effect on module libraries and libraries loaded with *BasilEnsure.
 	Possible errors are 'Library not found' and 'List not found'.
}
{Basil_Install (&50403)

 Installs a library in memory to the Basil list.

 =>	R0	= pointer to address of the library to install
 	R4	= 0, or pointer to name of library list to use

 <=	R0 preserved

 	The default library list is Base.
 	This call may be used by other modules to link their libraries to Basil.
}
{Basil_Remove (&50404)

 Removes a library in memory from the Basil list .

 =>	R0	= pointer to address of the library to remove
 	R4	= 0, or pointer to name of library list to use

 <=	R0 preserved

 	The default library list is Base.
 	This call may be used by other modules to un-link their libraries from Basil.
 	Possible error is 'Library not found'.
}
]
[Appendix 2 - Commands

{*BasilCreate

 Ensure that a library is installed.

 *BasilEnsure <libname> <filename> <listname>

 	This call cannot be used without a <listname>, so that libraries are not permanently added to the Base list. The list is created if it does not already exist.
 	The list of libraries is searched for the <libname>. If it is not found the <filename> is loaded into RMA. The name is checked again and if not found the memory is freed and an error returned. If it is now found it is linked into the list.
 	The count is set to zero and the library appears with a '*' from *BasilList. It cannot be removed with *BasilLose but only with *BasilReset.
 	This call is intended to be used in a !Run file, particularly in a library resource application.
 	Possible error is 'Library not found'.
}
{*BasilEnsure

 Ensure that a library is installed.

 *BasilEnsure <libname> <filename> <listname>

 	This call cannot be used without a <listname>, so that libraries are not permanently added to the Base list. The list is created if it does not already exist.
 	The list of libraries is searched for the <libname>. If it is not found the <filename> is loaded into RMA. The name is checked again and if not found the memory is freed and an error returned. If it is now found it is linked into the list.
 	The count is set to zero and the library appears with a '*' from *BasilList. It cannot be removed with *BasilLose but only with *BasilReset.
 	This call is intended to be used in a !Run file, particularly in a library resource application.
 	Possible error is 'Library not found'.
}
{*BasilLink

 Link a BASIC program to a library list.

 *BasilLink [<listname>]

 	The default <listname> is Base, and the list is created if it does not already exist.
 	This call checks that it has been made from a BASIC program and puts the address of the library list into INSTALLLIST.
 	It should be used at the start of a program to link to the list required.
 	It can be used at any other time to link to another list but care is needed when handling different <lists>.
 	Possible error is 'Not in BASIC'.
 	This call is preferred to SWI Basil_Link, which is now deprecated.
}
{*BasilFind

 Ensure that a library is available to a BASIC program.

 *BasilFind <libname> <filename> [<listname>]

 	The default <listname> is Base.
 	The list of libraries is searched for the <libname>. If it is not found the <filename> is loaded into RMA. The name is checked again and if not found the memory is freed and an error returned. If it is now found it is linked into the list.
 	The count is incremented each time the library is found.
 	Possible errors are 'Library not found' and 'List not found'.
}
{*BasilLose

 Release a library that is no longer required by a BASIC program.

 *BasilLose <libname> [<listname>]

 	The default <listname> is Base.
 	The list of libraries is searched for the <libname>. If it is found its count is decremented and if this is then zero the memory is freed. Otherwise an error is returned.
 	This call has no effect on module libraries or libraries loaded with *BasilEnsure.
 	Possible errors are 'Library not found' and 'List not found'.
}
{*BasilList

 Print the names of the libraries in a linked list.

 *BasilList [<listname>]

 	The default <listname> is Base.
 	This call prints the first lines of the BASIC libraries in the list.
 	Libraries installed by other modules or libraries loaded with *BasilEnsure are suffixed with "*".
}
{*BasilReset

 Empties a linked list and clears memory.

 *BasilReset [<listname>]

 	The default list is Base.
 	This call is only intended to be used during development of a program.
 	If a program has found a library, but has not lost it, perhaps after a crash, Basil assumes that the list is still in use and that instantiation cannot be killed.
 	If it is certain that no programs are using the list, this command will empty the list of linked libraries and free the RMA used by found libraries. The list can then be removed completely with *RMKill.
 	Although libraries that have been installed in the list by other modules will be unlinked, they remain in memory and should re-install when the list is restarted.
 	Do not *RMKill Basil itself as it would remove Base and the subsequent operation of Basil would be undefined.
 	Note that only the named list is cleared, and this command may need to be repeated for other lists.
}
]
[Appendix 3 - Service calls

{Service_BasilStarting (&80880)

 The Basil module has been loaded or re-initialised.

 =>	R1	= &80880
 	R2	= number of the intantiation
 <=	None

 	Modules that have libraries to link to a Basil list should install them. This will usually follow their removal after a reset
}
{Service_BasilResetting (&80801)

 The Basil module is being reset.

 =>	R1	= &80880
 	R4	= pointer to list name
 <=	None

 	Modules with libraries linked to the Basil list must remove them. They may also take other action, as no new routines can be called in them.
}
]
[Appendix 4 - Errors

{&814900, "Basil is in use"

 An attempt to kill a list or to reinitialise Basil has been made while libraries have been found but not lost. In emergency use *BasilReset to lose all libraries in a list.

 Note: Libraries added by other modules with SWI Basil_Install will not cause this error, and they may be re-linked if service call BasilStarting is trapped.
}
{&814901, "Not in BASIC

 An attempt has been made to use *BasilLink or SWI Basil_Link outside a BASIC environment.

 The method used to generate this error is only 'legal' with *BasilLink, which is preferred over the SWI.
}
{&814902, "Library not found"

 An attempt to find a library in the list has failed and the default file did not exist, was not type FFB or did not contain the correct library.

 An attempt has been made to remove a library using an incorrect address. This is only used internally.
}
{&814903, "List not found"

 An attempt has been made to use a named list that does not exist.
}
]
[Appendix 5 - History

 The early history of how Basil came about is in the file 'History'; this is from the first release version on.

{Version 1.01 (20 Jan 1998)

 This has been thoroughly tested by me, but beta-testers have given very little feedback, so either it works or the program is no use!

 I have reduced the core library to a pair of useful procedures and put the rest in libraries; I could be persuaded to change that.

 This release arises from a change of ISP and website.
}
{Version 1.02 (26 Jan 1998)

 On further consideration I have removed the core library.

 The included documentation has been revised and reduced.
}
{Version 1.12 (14 Feb 1998)

 Included SWI"Basil_Install" and SWI"Basil_Remove" to be used by library modules. Other code adjusted to allow for modules.

 Defined and used "Service_BasilStarting" and "Service_BasilResetting", so that modules can remove and re-install libraries.

 Added a flag to SWI"Basil_Find" to force the loading of the default file. This can reduce the need for *BasilReset while developing libraries.

 Some internal tidying of search code.
}
{Version 1.15 (25 Jun 1998)

 A little code tidying and a moderate updating of the documentation.

 My module 'RFSFiles' has been extended to integrate closely with 'Basil' so that BASIC files added to ResourceFS can link to the list as module libraries. This echoes a much earlier version of 'Basil', and is now my own preferred method. My 'Basilisk' resource application, in development, uses both modules to provide an object-oriented way of programming in BASIC.
}
{Version 1.16 (25 Jan 1999)

 Bug: Basil_Find no longer returned count and address of a library since the previous code tidying!
}
{Version 1.20 (26 Jan 1999)

 Prompted by discussing of 'lazy task swapping' and desktop responsiveness, I have added SWIs Basil_Library and Basil_Release, primarily to allow the main code of a program to be outside application workspace.

 RMA is used, but a dynamic area for all libraries is next step.

 NOTE: The experimental v1.20 with BASIC code has not been pursued.
}
{Version 1.21 (26 Sep 1999)

 This only fixes a nasty bug introduced into the code for version 1.20.
}
{Version 1.22 (29 Aug 2000)

 This only fixes a bug in the unknown SWI routine.
}
{Version 1.23 (28 Aug 2001)

 This is a reorganization of the StrongHelp manual, and Basil_Link has at last been made to reflect what the manual says it does, but no-one has noticed.
}
{Version 1.30 (14 Dec 2001)

 This is a major change introducing multiple independent library lists,

 *Commands are made the recommended API, with *BasilLink, *BasilFind, *BasilLose and *BasilEnsure.

 SWIs Basil_Library and Basil_Release are no longer documented or supported.
}
{Version 1.31 (16 Dec 2001)

 This was a last minute change to the API for *BasilEnsure.
}
{Version 1.41 (29 Mar 2004)

 32-bit compatible.

 *BasilCreate always required to create a list.

 Revised StrongHelp manual to emphasise library resource applications.
}
{Future

 Include version checking, but how this would operate is not clear.

 Use a dynamic area for libraries.
}
]
[Appendix 6 - Feedback

 I would welcome comments on some different aspects of implementing Basil, as well as the documentation.

{Using ResourceFS

 It is quite feasible for the BASIC libraries to be registered with ResourceFS and to appear in Resources. From there a user could inspect the code and maybe run it to produce some action - I have started a StrongHelp manual in this way. The penalties are that the code /is/ accessible and that each library would be a bit larger. I decided not to do it this way.

 My module RFSFiles is developed from earlier versions of Basil which did use ResourcesFS. It now integrates closely with Basil.
}
{The SWI interface

 It is usually neater to use SWIs to interface with a module, and they can also return values, which might be useful. However, *commands are more transparent to a user and provide information about the BASIC environment, but they do make a module significantly larger.
}
{The value of INSTALLLIST

 The value of INSTALLLIST (&86A0) has been the same in all versions of BASIC V and VI (BASIC64) up to 1.20. Basil assumes this value by default for SWI Basil_Link, but allows a different one to be used if this becomes necessary.

 Part of the reason for now using *commands is that I now know how to get at the BASIC information passed by to OS_CLI by BASIC, which is overwritten when the command get to the module.
}
{Using Basil outside BASIC

 Since *BasilLink writes directly to application workspace it would probably be disastrous to use it except in a BASIC program. Basil protects the user from this possibility by detecting a BASIC program environment.

 Libraries can be successfully manipulated with *BasilFind and *BasilLose outside BASIC, but this might lead to problems with open !Run files. *BasilEnsure should certainly be used instead.

 Are there any operations that would be useful beyond these?
}
{Library routine names

 If a unique naming convention is used, as suggested above, the routine names get quite large and they should not be crunched. SWIs get around this by having fixed length numbers that are only referenced by their names.

 What is needed is for library routine names to be compact but unique (and probably cryptic!) and for a separate library to translate from readable names while writing and debugging. Then a crunch program could take a translation file and produce the compact form.

 At the extreme, the names could be digital eg PROC_50405 with blocks of numbers allocated like SWI numbers!

 I have more detailed ideas on this and a specification for an application to do some of the work. Is it worthwhile?
}
{Naming conventions

 You may notice that the utilities library use the character '@' (ASC64) as a separator in routine names. That this is legal is not well known, and I hope thereby to keep the names out of conflict with any other routines you may come across. The character '`' (ASC96) is also legal in routine names, and variables.

 I have prefixed the names of library files that have Basil headers with '' so that they are recognizable. My Basilisk system uses '' (ALT-s) as a prefix.
}
]
