12/09/95 Introduction ============ OSLib is a set of functions and C headers to provide complete coverage of the RISC O S application programmer's interface in C. It provides access from C code to all RISC O S system calls ("SWI's") which is efficient: often, memory access is completely avoided; type-safe: every argument can be type-checked by the compiler; obvious: a SWI is called by the "obvious" syntax; complete: every SWI is covered; register-safe: hides (often idiosyncratic) register allocation; language-independent: although the headers are specific to C, the library is not - any A P C S-conformant language can call it. It also provides names for all the data structures and reason codes used by the A P I. Code that uses it is superior to similar code using _kernel_swi() or _swix(), both in terms of the compile-time checking that is available, and the size and speed of the code generated. In addition to the module files, there is also a file "types.h" which contains macros and types useful for any programme. This file explains how OSLib relates to the P R M descriptions of the SWI's in complete detail. It is best read in conjunction with the header files themselves. The OSLib Structuring Conventions === ===== =========== =========== Each header file provides function prototypes typedefs macros that completely specify the interface to a module. All symbols provided by ".h" are of the form _ (except some of the form _). For functions, is the name of the SWI, converted to lower-case, and with underscores inserted between each word: SWI Function --- -------- OS_ReadLine os_read_line Wimp_CreateWindow wimp_create_window For typedefs, is a sequence of words in lower-case, separated by underscores: Type Meaning ---- ------- os_colour a palette entry value (0xbbggrr00) wimp_window a window definition structure For macros, is a sequence of words in upper-case, separated by underscores: Macro Meaning ----- ------- os_MODEVAR_XEIG_FACTOR a value for os_read_mode_variable error_ESCAPE the error number of Escape (17) wimp_ICON_NAME_LIMIT the largest length of an icon name (all macros for maximum values end in _LIMIT). There is also a special set of macros provided for more convenient handling of structures with a variable number of trailing parts, as used for mode selectors or Wimp window definitions, for example: Macro Meaning ----- ------- os_PALETTE (nc) Declarator for a palette with |nc| colours os_SIZEOF_PALETTE (nc) Size of that palette wimp_WINDOW (ni) Declarator for a window with |ni| icons wimp_SIZEOF_WINDOW (ni) Size of the complete window definition Furthermore, every SWI number and SWI reason code has a macro defined of the form _ (in mixed case form): Macro Value ----- ----- OS_WriteC 0 Font_FindFont 0x40081 These are rarely needed. In fact, for each SWI, two functions are provided: one has a name prefixed with an x and returns a value of type |os_error *|, which is non- null if the SWI fails; the other does not start with an x, and never returns an error: if the SWI fails, a signal (|SIGOSERROR|, number 10) is raised, and may be caught by installing a signal handler for it. The non-x form can therefore return a result, instead of having to write to an output argument: for example, the SWI Wimp_CreateWindow can be called by either of the prototypes os_error *xwimp_create_window (wimp_window *window, wimp_w *w); wimp_w wimp_create_window (wimp_window *window); They have exactly the same effect if all goes well, but if there is an error, the first just returns it, while the other raises |SIGOSERROR| and does not return at all. (If you use the second, the signal handler for |SIGOSERROR| can use _kernel_last_oserror() to find out what the error was.) The function prototype is derived from the SWI description in the following way. Arguments corresponding to input registers appear first in order in the argument list, followed by the addresses of variables in which the output registers' values are to be written. If an output register value is not needed, a null pointer can be written in that position. (Since |int| and all pointer types are 32 bits on the ARM, it doesn't matter which type of null pointer is used.) Most of the types are fully defined: e g, a |wimp_window| is a structure type with all the fields defined with the right type and name to be passed to any SWI that uses it. Some types are "abstract," however: they are exported by a module, but the internal structure is not accessible to its clients. The best way of representing this in C is by an unspecified pointer type: a definition of the form typedef struct * where the structure is never defined in full. Doing this allows the compiler to typecheck arguments of these types, as in the wimp_- create_window() example above. In addition, various modules use values from name spaces that are centrally allocated by Acorn. These include: |error_|, |message_|, |...v|, |event_| and |upcall_|, and they each have their own prefix. They correspond to error numbers, WIMP message numbers, vectors (which are reason codes for OS_CallAVector), service calls (reason codes for OS_ServiceCall), events (reason codes for OS_GenerateEvent) and upcalls (reason codes for OS_UpCall), respectively. In cases where name clashes are possible, the module name occurs in the part: error_BUFFER_MANAGER_BUFFER_TOO_SMALL error_COLOUR_TRANS_BAD_DEPTH (Vectors are represented by a suffix, |...v|, rather than a prefix, since this is familiar.) These rules mean that if you are writing code for RISC O S, and you avoid (a) all names that start with the name of any ".h" you need to include; (b) all names that start with |error|, |message|, |event| or |upcall|, or end with |v|; then no names clashes will occur. Types.h ======= All OSLib header files ensure that "types.h" is included. This file contains various types, macros for values, and function-like macros that are generally useful. There are further macros in "macros.h." They are described here: Types ----- |bits|: Used for flags values and masks, normally consisting of fields of 1 or more bits. Macros are normally provided to help with getting these fields out: for a 1-bit field, a macro |X| such that To Use -- --- Read the field (v & X) != NONE Write a 0 v &= ~X Write a 1 v |= X and for a multi-bit field, macros X and X_SHIFT such that To Use -- --- Read the field (v & X) >> X_SHIFT Write a value |w| v = (v & ~X) | w << X_SHIFT. |bool|: Used for a truth value. |byte|: Used for a single byte value, as an alternative to |char| when the values are just data, rather than being characters as such. It is an unsigned type, which means that 2 instructions can be saved in P C C mode, where |char| is signed and must be sign-extended. Constant-like macros ------------- ------ |NULL|: the usual C null pointer constant. |FALSE|: value of type |bool| representing falsehood. |TRUE|: value of type |bool| representing truth. |NONE|: value of type |bits| with all bits clear. |ALL|: value of type |bits| with all bits set. |SKIP|: may be used as a "don't care" value for |int|, pointers, |bits|. |SIG_LIMIT|: largest signal number + 1. |_C|, |_Z|, |_N|, |_V|: masks for the flags in the P S R. |DEC_WIDTH|: the length of INT_MAX (2147483647) printed with %d. |SHORT_DEC_WIDTH|: the length of SHRT_MAX (32767) printed with %hd. |LONG_DEC_WIDTH|: the length of LONG_MAX (2147483647) printed with %ld. |OCT_WIDTH|: the length of UINT_MAX (37777777777) printed with %o. |SHORT_OCT_WIDTH|: the length of USHRT_MAX (177777) printed with %ho. |LONG_OCT_WIDTH|: the length of ULONG_MAX (37777777777) printed with %lo. |UNSIGNED_WIDTH|: the length of UINT_MAX (4294967295) printed with %u. |SHORT_UNSIGNED_WIDTH|: the length of USHRT_MAX (65535) printed with %hu. |LONG_UNSIGNED_WIDTH|: the length of ULONG_MAX (4294967295) printed with %lu. |HEX_WIDTH|: the length of UINT_MAX (FFFFFFFF) printed with %x. |SHORT_HEX_WIDTH|: the length of USHRT_MAX (FFFF) printed with %hx. |LONG_HEX_WIDTH|: the length of ULONG_MAX (FFFFFFFF) printed with %lx. |FLT_WIDTH|: the precision needed to distinguish 1 + FLT_EPSILON from 1 printed with %f. |DBL_WIDTH|: the precision needed to distinguish 1 + DBL_EPSILON from 1 printed with %f. |LDBL_WIDTH|: the precision needed to distinguish 1 + LDBL_EPSILON from 1 printed with %Lf. |FLT_EXP_WIDTH|: the length of the exponent of FLT_MAX printed with %f. |DBL_EXP_WIDTH|: the length of the exponent of DBL_MAX printed with %f. |LDBL_EXP_WIDTH|: the length of the exponent of LDBL_MAX printed with %Lf. |ERROR|: intended as an "out-of-band" value to be returned by functions like fgetc() which return |EOF| on end-of-file. |ERROR| may be used to indicate an error condition. |UNKNOWN|: used to declare (but not define) arrays of unknown size, as an argument to the type macros (e g, wimp_WINDOW()) described above. Function-like macros ------------- ------ |WHETHER|: convert a bool value to a string. |MAX|: larger of two values. |MIN|: smaller of two values. |MAXAB|: larger of two values and assign. |MINAB|: smaller of two values and assign. |ABS|: absolute value. |SGN|: signum. |DIM|: positive difference. |SQR|: square. |RATIO|: integer division, rounding to nearer. |BOOL|: convert an integer to bool. |UCHAR|: character corresponding to a digit (upper case preferred). |LCHAR|: ditto, lower case. |BINEXP|: 2 to the power of. |ISDIGIT|: a digit? |ISXDIGIT|: a hex digit? |DIGIT|: if a digit, then which, else undefined. |XDIGIT|: if a hex digit, then which, else undefined. |DBLEQ|: equality for floating-point numbers (requires a tolerance) |BIT|: the bit at an offset from a pointer. |SET|: set a bit at an offset from a pointer. |CLR|: clear a bit at an offset from a pointer. |CLEAR|: clear a string. |EMPTY|: is a string empty? |NCOPY|: copy at most a given number of characters from a string, and terminate it. |STR_|: helper macro for STR. |STR|: convert a macro to source string. |COUNT|: number of elements in an array. |ALIGN|: round an integer up to the next multiple of 4. |WORD|: assembles an |int| from an unaligned byte pointer. |SHORT|: assembles a |short| from an unaligned byte pointer. Macros that "change the language" ------ ---- ------- --- --------- (Not everyone likes using these!) |AS| is the same as |.|, but can be used to let the reader know that the left operand is a |union| rather than a |struct|. |ASREF| is the same as |->|, but can be used to let the reader know that the left operand is a |union *| rather than a |struct *|. |_| is the same as |,|, but if used to separate the arguments of a macro call is not recognised as a comma by the preprocessor. This lets you write macros with variable numbers of arguments. |__swi| is understood as a compiler directive by C release 5 (but not by earlier versions of C, nor by CFront) to call a function using a SWI instruction rather than by a BL. "Types.h" therefore includes some directives to enable the uses of |__swi| in the headers to be "hidden" in these cases by using -D__swi on the cc command line. Macros to suppress compiler warnings ------ -- -------- -------- -------- |NOT_USED|: Suppress "variable not used" message. |UNSET|: Suppress "variable may be used before being set" message. Additional Structuring Rules ========== =========== ===== The design aims listed in paragraph 1 are not completely compatible. In order to provide type-safety, it is necessary to provide multiple veneers for some SWI's. These fall into two classes: where a SWI has multiple reason codes, each reason code is provided with a separate function; and where a SWI has two or more different variants, each variant is provided with its own function. Reason codes ------ ----- Many SWI's have several reason codes each of which has its own distinct purpose and register usage. In this case, each reason code is provided with its own function. (SWI's with a "reason code" parameter which does not affect the interpretation of the other parameters are not treated like this: instead, the reason codes are just provided as macros.) In some cases, one reason code may itself have different sub-reason codes. In every case, the functions that are derived from the SWI use the entirety of the SWI name (with no underscore) as their prefix. Some have their own header file (e g, OS_SpriteOp, reason codes in "osspriteop.h"), others share the header file with the SWI (e g, PDriver_MiscOp, reason codes in "pdriver.h"). Toolbox_object_misc_op() has a dynamically extensible set of reason codes provided by object modules: these use the name of the object, and are in the object's header file. Where a header file name is longer than 10 characters, the full name should be used in |#include| statements. FileCore-based filing systems will truncate the file name to 10 characters, if the *Configure option 'Truncate' is 'On' (which is recommended). This means that, for example, the line |#include "messagetrans.h"| will work on all filing systems: FileCore-based ones will convert the name to "messagetra.h." OS_WriteI might be considered to have a reason code (the character to be written), but since it is part of the SWI number, each character has a separate function. This is not worthwhile for printing characters, but for other VDU codes gives useful calls like os_clg() (to clear the graphics window), os_bell() (to ring the bell) etc. (The complete list is in "os.h.") Many of the reason codes of OS_CallAVector have been omitted, namely, those where there are many sub-reason codes, and the purpose of the vector is exactly duplicated by a SWI or SWI's. So you will not find findv_- openin() (to call sub-reason code OSFind_OpenIn of reason code FindV of SWI OS_CallAVector), because it is identical in purpose to osfind_openin(). If there are reason codes but no SWI, the reason codes do have functions provided (e g, PaletteV), and if there are no reason codes, the function is provided even if it does duplicate a SWI - for example, you can call os_writec() or wrchv() with the same effect (unless you are implementing them, of course). This is all just to keep the library size down: if it causes a problem, it can be reviewed. (Since calls to OS_GenerateEvent and OS_UpCall are already distributed through many files, vectored versions of them would have to be distributed in the same way.) Lastly, calls provided by FileSwitch are in "fileswitch.h", unless they have their own header. (The separate headers are "osargs.h," osfile.h," osfind.h," osfscontrol.h," "osgbpb.h.") Also, some of the types that might be expected to be defined in these headers are actually defined in "fileswitch.h," to avoid circular references. Variants -------- Some SWI's have two or more different functions that call them, because they have two different calling conventions that cannot be easily captured using C's type system. These should be obvious from the name; failing that, the header file says exactly which SWI is called, and which values are placed into which registers. A few examples are described here, to give an idea of why variants exist. Colourtrans_select_table_for_sprite() (variant of colourtrans_select_- table()) takes an |osspriteop_area *| and an |osspriteop_id|, rather than an |os_mode| and an |os_palette *|, as its first two arguments. The same applies to colourtrans_generate_table_for_sprite() (variant of colourtrans_- generate_table()) and colourtrans_select_gcol_table_for_sprite() (variant of colourtrans_select_gcol_table()). Os_read_colour() (variant of os_set_colour()) is provided so that os_set_colour() can remain backwards-compatible. Squash_compress_return_sizes() (variant of squash_compress()) and squash_decompress_return_sizes() (variant of squash_decompress()) are provided because of the different purposes dependent on bit 3 of R0. Stringset{set,get}selected_string() and stringset{set,get}selected_index() are both provided, to allow correct typechecking of the string or integer argument. Territory_read_symbols() is divided into 3 variants, territory_read_- boolean_symbols(), territory_read_integer_symbols() and territory_read_- string_symbols(), because different symbols have different types. Wimp_send_message_to_window() (variant of wimp_send_message()) takes a |wimp_w| and a |wimp_i| rather than a |wimp_t|, and returns a result (the task handle) which wimp_send_message() does not. Conclusion ========== Although there seem to be a lot of rules, that is because the RISC O S A P I is large. For normal application programming, most of it is not needed: applications don't normally deal with vectors, upcalls or service calls, for example, or do any direct access to disc structures. However, this is not ruled out. The C headers contain the complete description of exactly what each function does in terms of the mapping of the arguments to ARM registers, so no other documentation is needed. OSLib provides a very convenient interface to the RISC O S programmer, since all the facilities of the C compiler are available to catch errors and generate good code. It is conceptually very small, in that it is completely documented by this file. As a bonus, code written using it is smaller and faster than code written using other means. Disclaimer ========== OSLib is copyright © 1995 Acorn Computers Ltd. It is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. Fault reports and suggestions for improvement may be sent to the author, Jonathan Coxhead . Although this is an Acorn address, OSLib is not an official Acorn product.