OSLib Developers' Guide
Purpose:
This guide provides information and guidelines to developers wh propose
to make submissions for inclusion in OSLib.
The following information is extracted from Jonathan's instructions
Very brief and cryptic notes about the source release of OSLib
It's 14Mb built. The part you use (headers + library) is 3Mb
big.
This is 5.4. See ChangeLog for changes since 5.3.
Error Handling
The most important coding point to emphasise is that *all*
errors are detected and reported, no matter how deeply nested we may be
into the call stack or however small the function in progress. This is
done by keeping to a strict discipline (when I can be bothered :-)
Every function that everyone has ever written can return
an error. Normally in RISC O S code this is done by returning an |os_error
*|. In UNIX-style code, it is done by returning -1 and setting |errno|. Whatever
the mechanism is, it *must* be checked when calling the function. Every
function called *must* then have its error return checked. If there was
an error, a |goto finish;| is executed immediately. (The only thing that
can be done before "go to finish" is to assign the current function's error
indicator.) At |finish:|, any local resources that may have been claimed
(in C, chiefly malloced memory and file handlers, but under RISC O S also
windows, fonts, vectors, etc) are released. The current function then returns
the error back to its own caller.
In this way, when any function returns an error, the whole
of the call stack is unwound to the top level where it can be handled cleanly.
This is a just a low-level way of emulating C++-style exceptions.
Regression Testing
The other important point is regression testing. When a change
is made, everything is differed with the previous version to make sure
that
nothing has changed for the worse. For the objasm files, I've provided
s;0 directories, but it should be done for headers too: that's easy because
they are textual anyway, and you already have copies. The objasm diff checks
that the *code* is the same. (The objasm files aren't used for anything
else, but the code that writes them is the same as the code that writes
the tiny files that make up the library as a whole, so it's important.)
This is your guarantee that everything you do will work.
Generating the library
Installation is roughly as follows: run SetVars; open DefMod;
run !Make (twice) (you need yacc (or Bison) for this); copy the DefMod
absolute into the OSLib directory, or into your path; run !Make in OSlib.
In Progress
There are sketches or starts or initial hacks at some more
swi files in InProgress.
The comments in the objasm files "Registers available for
scratch use" was a piece of work-in-progress. The idea was from Mark Wooding,
I think: he suggested that instead of stacking values that must be saved
over the SWI, it would be more efficient to save them in registers that
were not otherwise used. (These are V1--V6 if the SWI will corrupt them,
as A P C S demands they are preserved by a function call, and any registers
containing addresses of output arguments that are needed on entry to the
SWI or corrupted by it.) This is commented out, not finished, and doesn't
work. Feel free to give it a shot.
DefMod can make SrcEdit help files for either C or assembler---no-one
wants these, though. I also started something to do with Pascal, as well
as a start to native C++ support (so you wouldn't need the |extern "C"|
directives, and potentially allow overloaded functions to call the same
SWI).
NDEBUG
I think that defining NDEBUG would stop lookup.c working.
That was probably a mistake.
DefMod and SWILib
DefMod is a RISC O S programme written in C, and it looks as though
it uses OSLib. If it did, it could never have been written, as it logically
precedes OSLib. So how was this done?
It uses a hand-crafted set of macros called SWILib. This
defines the functions provided by OSLib as macros expanding to calls to
|_swi(x?)|---but just the SWI's that DefMod uses. (SWILib was the first
version of OSLib. The first version of DefMod generated SWILib-style headers:
macros rather than function declarations.) Do not change this, or you could
potentially end up in a situation where the whole system could not get
off the ground.
UNKNOWN types
I have been asked to explain the way the [UNKNOWN] types work. When
a type (e g, wimp_menu, wimp_window) has a repeating final part, DefMod
also generates some extra macros wimp_MENU (n) and wimp_SIZEOF_MENU (n).
You can use the first to write out data structures in your code. It does
show how flexible the C type system is, if you use it well.
Here's a menu definition I use in another of my proggies ...
wimp_MENU (2) Main_Menu =
{
"PlaceName",
wimp_COLOUR_BLACK,
wimp_COLOUR_LIGHT_GREY,
wimp_COLOUR_BLACK,
SKIP,
9*wimp_CHAR_XSIZE,
wimp_MENU_ITEM_HEIGHT,
wimp_MENU_ITEM_GAP,
{
{
wimp_MENU_GIVE_WARNING,
wimp_DEFER_SUB_MENU,
wimp_ICON_TEXT | wimp_ICON_FILLED |
wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT
|
wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT,
"Info"
},
{
wimp_MENU_LAST,
wimp_NO_SUB_MENU,
wimp_ICON_TEXT | wimp_ICON_FILLED |
wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT
|
wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT,
"Quit"
}
}
};
This is then used in calls like
wimp_create_menu ((wimp_menu *) &Main_Menu);
---it needs to be cast. If you want to build a structure like this on the
fly, you can write
wimp_menu *menu = malloc (wimp_SIZEOF_MENU (2));
and use references like
... menu->entries [1].data AS indirected_text.text ...
to get at the text of the 1st icon.
So these macros can let you write some very complicated
data structures right in the code, without needing to read them from a
resource file, but still in a reasonably maintainable way.
Hierarchy
In general, you you must always make sure you know where a
module fits in with other modules. There must be a strict hierarchy, with
files only needing (the NEEDS statement) files in an acyclic fashion. That's
not say things can't be introduced into the middle (JPEG needs OS, but
PDriver needs JPEG, even though JPEG didn't exist till long after PDriver
was first thought of.) DrawFile is another complicated one: does DrawFile
need Font (Draw objects can contain font handles), or does Font need DrawFile
(the Font manager can write font outlines to a Draw group). This is a tie
really, and it's broken in an unpleasant way. It might have been better
to invent a "graphics.h" to be included by both, but that would break the
correspondence between swi files and modules.
Extensible Interfaces
There are lots of cases in RISC O S of extensible interfaces:
Wimp messages can be defined by anyone, for example. It is not good in
these cases to put all of them into a file (swi.Wimp): this would mean that
the file would have to be extended all the time. Instead, the basic interface
is provided in 1 file, and other people can write their own header files.
This is what's done in swi.Gadget. No gadget types are defined there: that's
all done in a distributed way. This is also why the wimp_message struct
only has a few of the defined message data types in its data field. (It
has all of the ones defined as "system" and "Wimp" types.) Ones defined
by modules (e g, message_HELP_REQUEST, printing messages, alarm messages)
are defined by their respective modules. Otherwise, every time a new module
added a message, "wimp.h" would have to be updated. This means that some
otherwise avoidable type casts are needed; oh well.
Help Files
DefMod writes HelpData and Index files for use by StrongHelp.
Then Alexander Thoukidides' ConvHelp can turn all of these into a StrongHelp
help file. If you use Zap, you can bind F1 to mje_helpcontext, and then
you point to a SWI name in your code and get help on it in 1 keystroke.
This help is all linked together internally, and in 2 clicks you can get
all the valid values for any constant.
Parsing
You need a version of YACC or Bison. The one I used was ported
by me from the Berkeley YACC sources. I could have used Bison, but then
OSlib would have fallen under the G P L. At that time, I wanted to keep
the 'swi' files private, so that there wouldn't be incompatible extensions
to widely-used modules; but I'm convinced now that that isn't a problem.
The approach to the parsing is is unconventional in 2 ways:
-
(1) I didn't use Lex (or Flex): everything is done by the grammar in defmod.y.
This was just so I didn't have to learn Lex as well as YACC, and to see
if it could be done.
-
(2) String values are not put in a symbol table and carefully managed.
Instead, the grammar passes round whole strings of up to 255 characters
on the stack and copies them every time a character is appended. This is
hopelessly slow and inefficient---but it works, and it's "fast enough".
The limit is not checked in the code, despite what I said above. Sorry.
Jonathan Coxhead
Webmaster: Tony van
der Hoff
Home