RTR logo

BBC BASIC for SDL 2.0

Library Routines



Introduction to libraries

The INSTALL statement allows you to load a library containing functions and procedures which can be called from within your program, without them appearing in the program listing. If you, or somebody else, has written a number of useful functions which you might want to call from several different programs, this provides a convenient way of packaging and distributing them.

Because the procedures and functions do not form part of your program the size of its file is reduced, and if a bug is found in one of the functions it can be corrected by updating the library without having to modify each of the programs in which it is used.

You can build up your own set of such files, but BBC BASIC for SDL 2.0 is supplied with the following libraries:

These can be found in the lib sub-directory (folder) in the main BBC BASIC directory.

Array and matrix functions

The arraylib library contains a set of procedures and functions for performing arithmetic and matrix operations on 1- and 2-dimensional arrays. These include adding two matrices together, multiplying two matrices, transposing a matrix and inverting a matrix.

The library should be loaded from your program using the statement:

INSTALL @lib$+"arraylib"

The functions contained are:

In BBC BASIC for SDL 2.0 several of these operations are incorporated within the interpreter. Using the built-in operations will be considerably faster than using the library routines; see the Array arithmetic section for details. The library routines which are not supported as built-in operations are PROC_transpose, PROC_invert and FN_det.

PROC_add(A(), B)

PROC_add adds a scalar value B to all the elements of 1D or 2D (numeric) array A(), and returns the result in A():
DIM N(3)
N() = 1, 2, 3, 4
PROC_add(N(), 5)
PRINT N(0),N(1),N(2),N(3)
When executed this program will print:
         6         7         8         9

PROC_mul(A(), B)

PROC_mul multiplies all the elements of 1D or 2D (numeric) array A() by the scalar value B, and returns the result in A():
DIM N(3)
N() = 1, 2, 3, 4
PROC_mul(N(), 2)
PRINT N(0),N(1),N(2),N(3)
When executed this program will print:
         2         4         6         8

PROC_sum(A(), B())

PROC_sum adds 1D or 2D (numeric) arrays A() and B() together, and returns the result in A(). A() and B() must have the same dimensions.
DIM N(3), S(3)
N() = 1, 2, 3, 4
S() = 5, 6, 7, 8
PROC_sum(N(), S())
PRINT N(0),N(1),N(2),N(3)
When executed this program will print:
         6         8        10        12

PROC_dot(A(), B(), C())

PROC_dot multiplies 2D matrices A() and B() together and returns the result in C(). The number of columns of A() must equal the number of rows of B(), the number of columns of C() must equal the number of columns of B() and the number of rows of C() must equal the number of rows of A().
DIM N(0,2), S(2,1), D(0,1)
N() = 1, 2, 3
S() = 4, 5, 6, 7, 8, 9
PROC_dot(N(), S(), D())
PRINT D(0,0) D(0,1)
When executed this program will print:
        40        46

PROC_transpose(A(), B())

PROC_transpose transposes 2D matrix A() and returns the result in B(). The number of columns of A() must equal the number of rows of B() and the number of rows of A() must equal the number of columns of B().
DIM N(1,2), T(2,1)
N() = 1, 2, 3, 4, 5, 6
PROC_transpose(N(), T())
PRINT T(0,0) T(0,1)
PRINT T(1,0) T(1,1)
PRINT T(2,0) T(2,1)
When executed this program will print:
         1         4
         2         5
         3         6

PROC_invert(A())

PROC_invert inverts square matrix A() and returns the result in A().
DIM M(2,2)
M() = 2,0,6,8,1,-4,0,5,7
PROC_invert(M())
PRINT M(0,0) M(0,1) M(0,2)
PRINT M(1,0) M(1,1) M(1,2)
PRINT M(2,0) M(2,1) M(2,2)
When executed this program will print:
   0.09184   0.10204  -0.02041
  -0.19048   0.04762   0.19048
   0.13605  -0.03401   0.00680

FN_mod(A())

FN_mod returns the modulus (the square-root of the sum of the squares of all the elements) of a 1D or 2D array.
DIM M(2,2)
M() = 2,0,6,8,1,-4,0,5,7
PRINT FN_mod(M())
When executed this program will print:
13.96424

FN_det(A())

FN_det returns the determinant of a square array.
DIM M(2,2)
M() = 2,0,6,8,1,-4,0,5,7
PRINT FN_det(M())
When executed this program will print:
294

Dialogue boxes and controls

The dlglib library contains a set of procedures and functions for creating and controlling dialogue boxes and standalone GUI controls (boxes, buttons etc.).

The library should be loaded from your program using the statement:

INSTALL @lib$+"dlglib"
Some of the functions contained are: Other functions are described in the context of the control(s) with which they are used, or in the sections Updating the contents of a dialogue box and Reading the contents of a dialogue box.

FN_newdialog

Before you can use a dialogue box, you must first specify its size and title. This should be done only once, typically in an initialisation routine, for each dialogue box your program contains (the dialogue box can subsequently be displayed as many times as you like):
dlg% = FN_newdialog(title$,cx%,cy%)
The string title$ specifies the title of the dialogue box, which is displayed in its title bar. The parameters cx% and cy% specify the width and height, respectively, of the dialogue box in dialogue box units (see below). IF cx% and/or cy% is set to zero this signifies that the subsequently-defined controls are to be displayed on the main window rather than in a dialogue box; in that case title$ is ignored.

Dialogue boxes are automatically scaled according to the size of the font, therefore it is important that you select the required font before calling FN_newdialog. So typically the code to create a dialogue box will be something like this:

OSCLI "FONT """ + @lib$ + "DejaVuSans"",12"
dlg% = FN_newdialog("Dialogue box", 160, 140)
The size of the dialogue box, and the positions and sizes of controls within the dialogue box, are specified in dialogue box units, each of which corresponds to approximately one-eighth of the height of the selected font. So if the character height is 16 pixels, each dialogue box unit is 2 pixels. However if the width and/or height of the dialogue box is set to zero, signifying that the control(s) will be displayed on the main window, the positions and sizes of the controls are specified in pixels.

The returned value (dlg% in this case) identifies the dialogue box, and must be stored for use in the subsequent procedures. Immediately after calling FN_newdialog() you should define each of the controls in the dialogue box by calling one or more of the following procedures; again they should be called just once, during your program's initialisation phase. The order in which you define the controls is important, because it determines the tab order of the controls.

PROC_button

This defines a pushbutton control. All dialogue boxes should normally have at least one pushbutton, labelled OK, which enables the user to confirm that his or her input is complete:
PROC_button(dlg%, text$, id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The string text$ specifies the text to appear inside the button (e.g. "OK") or in the case of a BS_BITMAP or BS_ICON button (see below) the path and filename of the image file to be displayed in the button.

The x% and y% parameters specify the position of the button within the dialogue box and the cx% and cy% parameters specify the size of the button (in dialogue box units). Vertical positions are measured from the top of the dialogue box.

The id% parameter is a unique identifier of the pushbutton; all the items within a particular dialogue box must have different ID values. You can choose any value (within reason) but values 1 to 9 are special because they cause the dialogue box to be exited and control returned to your program when the button is clicked. IDs 1 and 2 are reserved for the OK and Cancel buttons respectively so should only be used for that purpose. Alternatively you can set this parameter to a value returned from FN_setproc which will cause the specified procedure to be called when the button is clicked.

The style% parameter can be zero, but other values allow you to modify the appearance or behaviour of the pushbutton. Setting style% to &80 (BS_BITMAP) or &40 (BS_ICON) indicates that the button will display the bitmap image whose filename was specified in the text$ parameter (BS_ICON differs from BS_BITMAP in suppressing the rectangle normally drawn around the button). Adding WS_DISABLED (&8000000) to the style value causes the button to be initially disabled (greyed-out); adding WS_VISIBLE (&10000000) causes it to be (initially) invisible.

PROC_checkbox

A checkbox is a small square which can either be checked (contain a tick mark) or not. When the user clicks on the box, it toggles between being checked and being unchecked. A checkbox is used to select one of two states (e.g. off or on).
PROC_checkbox(dlg%, text$, id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The text$ parameter specifies the text to appear alongside the checkbox, the x% and y% parameters specify the position of the checkbox within the dialogue box and the cx% and cy% parameters specify the size of the checkbox and its associated text (in dialogue box units). The id% parameter is a unique identifier for the checkbox.

The style% parameter can be zero; setting it to 1 causes the box to be initially checked. Adding WS_DISABLED (&8000000) to the style value causes the checkbox to be initially disabled (greyed-out); adding WS_VISIBLE (&10000000) causes it to be (initially) invisible.

PROC_radiobutton

A radio button is a small circle which can either be checked (contain a central spot) or not. Radio buttons are used in groups of two or more, where only one of the buttons is checked at any one time. When the user clicks one of the buttons it becomes checked, and all the other buttons in the group become unchecked. A radio button is used to select one of two or more states.
PROC_radiobutton(dlg%, text$, id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The text$ parameter specifies the text to appear alongside the radio button, the x% and y% parameters specify the position of the radio button within the dialogue box and the cx% and cy% parameters specify the size of the radio button and its associated text (in dialogue box units). The id% parameter is a unique identifier for the radio button.

The style% parameter can be zero; setting it to 1 causes the button to be initially checked. Adding WS_DISABLED (&8000000) to the style value causes the radio button to be initially disabled (greyed-out); adding WS_VISIBLE (&10000000) causes it to be (initially) invisible.

Radio buttons defined consecutively, by calling PROC_radiobutton, are grouped: checking one automatically unchecks the others. Groups of radio buttons should be separated by at least one control of a different type.

PROC_groupbox

A group box is a rectangle which is used to enclose a number of items within the dialogue box, thus emphasising that they are grouped together.
PROC_groupbox(dlg%, text$, id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The text$ parameter specifies the text to appear in the top edge of the group box, the x% and y% parameters specify the position of the group box within the dialogue box and the cx% and cy% parameters specify the size of the group box (in dialogue box units). The id% parameter is an identifier for the group box (this is unlikely to be used). The style% parameter should usually be zero.

PROC_textbox

A text box is a rectangular field into which the user can type a single line of text.
PROC_textbox(dlg%, text$, id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The text$ parameter specifies the initial text (if any) to appear in the text box, the x% and y% parameters specify the position of the text box within the dialogue box and the cx% and cy% parameters specify the size of the text box (in dialogue box units); ensure that the height is sufficient for the size of text you are using. The id% parameter is a unique identifier for the text box.

The style% parameter can be zero; setting it to ES_NUMBER (&2000) causes the text box to accept only numeric input. Adding WS_DISABLED (&8000000) to the style value causes the text box to be initially disabled (greyed-out); adding WS_VISIBLE (&10000000) causes it to be (initially) invisible.

PROC_static

A static item is a rectangular area containing (usually) a text string. This may be used to label another item or to be simply informative.
PROC_static(dlg%, text$, id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The text$ parameter specifies the required text, the x% and y% parameters specify the position of the rectangle within the dialogue box and the cx% and cy% parameters specify the size of the rectangle (in dialogue box units). The id% parameter is a unique identifier for the static item (this is unlikely to be used).

The style% parameter may be zero, but other values allow you to modify the appearance of the static item. By default text is left-justified within the rectangle but setting style% to 1 (SS_CENTER) causes the text to be centred within the rectangle and setting it to 2 (SS_RIGHT) causes the text to be right-justified within the rectangle.

PROC_listbox

A list box displays a list of two or more items from which the user can select one.
PROC_listbox(dlg%, "", id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The string parameter is unused and should be set to an empty string, the x% and y% parameters specify the position of the list box within the dialogue box and the cx% and cy% parameters specify the size of the list box (in dialogue box units). The id% parameter is a unique identifier for the list box.

The style% parameter can be zero; adding LBS_USETABSTOPS (&80) causes the list box to expand tab characters, adding WS_VSCROLL (&200000) includes a vertical scroll bar. Adding WS_DISABLED (&8000000) to the style value causes the text box to be initially disabled (greyed-out); adding WS_VISIBLE (&10000000) causes it to be (initially) invisible.

If the list box is created with the LBS_USETABSTOPS style, set the column positions by calling PROC_setlistboxcols() as follows:

PROC_setlistboxcols(dlg%, id%, columns%(), cols%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the list box. The columns% array contains a zero-based list of column widths in dialogue-box units. Note that only one list box can have the LBS_USETABSTOPS style.

To set or update the contents of the list box call PROC_setlistboxarray() as follows:

PROC_setlistboxarray(dlg%, id%, array$(), items%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the list box. The string array should contain one element per list box item, with items% indicating the total number of items. Index zero is unused, so the items should be in array$(1) to array$(items%).

PROC_combobox

A combo box consists of a list and a selection field. The list presents the options a user can select, and the selection field displays the current selection.
PROC_combobox(dlg%, text$, id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The text$ parameter specifies the initial text (if any) to appear in the selection field, the x% and y% parameters specify the position of the combo box within the dialogue box and the cx% and cy% parameters specify the dropped-down size of the combo box (in dialogue box units). The id% parameter is a unique identifier for the combo box.

The style% parameter can be zero; adding WS_DISABLED (&8000000) to the style value causes the combo box to be initially disabled (greyed-out); adding WS_VISIBLE (&10000000) causes it to be (initially) invisible.

To set or update the contents of the combo box call PROC_setcomboboxarray() as follows:

PROC_setcomboboxarray(dlg%, id%, array$(), items%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the combo box. The string array should contain one element per combo box item, with items% indicating the total number of items. Index zero is unused, so the items should be in array$(1) to array$(items%).

PROC_trackbar

A trackbar allows the use to set a parameter by moving a pointer horizontally.
PROC_trackbar(dlg%, "", id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The string parameter is ignored and should be set to an empty string, the x% and y% parameters specify the position of the trackbar within the dialogue box and the cx% and cy% parameters specify the size of the trackbar (in dialogue box units). The id% parameter is a unique identifier for the trackbar.

The style% parameter can be zero; adding WS_DISABLED (&8000000) to the style value causes the trackbar to be initially disabled (greyed-out); adding WS_VISIBLE (&10000000) causes it to be (initially) invisible.

To set the minimum, maximum and current values of the trackbar call PROC_settrackbarpos() as follows:

PROC_settrackbarpos(dlg%, id%, current%, minimum%, maximum%)

FN_showdialog and FN_showdialogex

Once the size and contents of the dialogue box have been defined, it may be displayed on the screen. Whilst the creation of the dialogue box and the definition of its contents should be done just once, the box may subsequently be displayed as many times as you like. However before displaying the dialogue box you should first set the appropriate colour palette and (optionally) register a callback routine. You should also re-select the appropriate font if it might have changed since the dialogue box was defined:
PROC_setdialogpalette(dark%)
PROC_registerdlgcallback(dlg%, FNmycb()) : REM optional
OSCLI "FONT """ + @lib$ + "DejaVuSans"",12"
The dark% parameter should be set to TRUE if you want the dialogue box to use a 'dark' style, and to FALSE if you want it to use the normal 'light' style. The optional callback routine FNmycb() is called whilst the dialogue box remains open, both when a keypress is detected and periodically. This allows you to monitor activity and if necessary update the dialogue box contents as things change. See Monitoring dialogue box changes for more details.

Now you can display the dialogue box:

result% = FN_showdialog(dlg%, x%, y%)
alternatively:
result% = FN_showdialogex(dlg%, x%, y%, dark%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog, the x% and y% parameters set the position of the dialogue box (top-left corner) in dialogue box units. If you set both x% and y% to &FFFFFFFF80000000 the dialogue box will be centered in the window. The optional dark% parameter should be set to TRUE if you want the dialogue box to use a 'dark' style, and to FALSE if you want it to use the normal 'light' style.

The function will return when the user clicks on a button with an ID number in the range 1 to 9, with the returned value equal to that ID.

FN_polldialog

If, when you called FN_newdialog(), you set the width and/or height parameter to zero, this signifies that you want the various control or controls to be displayed on the main output window rather than in a dialogue box. In that case you should not call FN_showdialog() (or FN_showdialogex()) but periodically call FN_polldialog() to activate the controls. Here is an example of how it should be used:
DIM Click%(2)
ON MOUSE Click%() = @msg%,@wparam%,@lparam% : RETURN
PROC_setdialogpalette
PROC_refreshdialog(dlg%)
REPEAT
  key% = INKEY(1)  
  REM Do anything that you need to do periodically here
  result% = FN_polldialog(dlg%, key%, Click%())
UNTIL result% = 1 OR result% = 2

Monitoring dialogue box changes

Whilst a dialogue box remains open you may monitor changes by registering a callback routine, as shown above. The callback routine should be defined with the following signature:
DEF FNmycb(D%, K%)
LOCAL N%
...
= N%
The routine will be called both periodically (in which case K% will be -1) or when a keypress is detected (in which case K% will be set to the key code). In both cases D% identifies the dialogue box (it is the value originally returned from FN_newdialog). If you return zero from the callback function the dialogue box will remain open; if you return a value in the range 1 to 9 it will exit, as if a button with that ID was pressed.

In the callback routine you may read the contents of the various controls (see Reading the contents of a dialogue box) and if necessary update them (see Updating the contents of a dialogue box). The simplest possible callback routine results in pressing the Enter (Return) key having the same effect as clicking on the OK button:

DEF FNmycb(D%,K%)
IF K% = 13 THEN = 1
= 0

Updating the contents of a dialogue box

In many cases the contents of the dialogue box are defined when the items within it are created, and never need to be changed again. However you may want to change the contents at other times; a number of procedures are provided to achieve that, as shown below. Note that after making any changes you must refresh the dialogue box to update the display:
PROC_refreshdialog(dlg%)

Updating the text (or image) associated with a control

To change the text associated with a control (which may for example be the contents of a text box or a label for a check box):
PROC_setdlgitemtext(dlg%, id%, text$)
The dlg% parameter is the value returned from FN_newdialog, the id% parameter is the identifier for the item in question and text$ is the new text string to be associated with the item (or in the case of a BS_BITMAP or BS_ICON button the path and filename of the new image file).

Updating the contents of a list box or combo box

To update the list of strings in a list box or combo box modify the contents of the associated array and then do the following:
PROC_setlistboxarray(dlg%, id%, array$(), items%)
or
PROC_setcomboboxarray(dlg%, id%, array$(), items%)
where the value id% is the identifier of the list box or combo box. Strictly speaking if the number of items doesn't change, and if the list box does not have the LBS_USETABSTOPS style, you can simply update the contents of the associated array, but it does no harm to call the procedure as well.

To empty a list box or combo box of its contents set all the elements of the array to a NUL string (array$() = "") and call the appropriate procedure with items% set to zero.

If you wish to set the current selection (which item in the list box or combo box is highlighted) do this:

PROC_setlistboxselect(dlg%, id%, selitem%)
or
PROC_setlcomboboxselect(dlg%, id%, selitem%)

Updating the state of a checkbox or radiobutton

To update the state of a checkbox or set of radio buttons, do this:
PROC_checkdlgitem(dlg%, id%, checked%)
Where checked% is TRUE to set the checked state or FALSE to uncheck it. In the case of a set of radio buttons you should explicitly set only one of the buttons to the checked state and the rest to the unchecked state, the dialogue box cannot be relied upon to do this automatically.

Disabling and enabling dialogue box items

You may wish to disable one or more items in a dialogue box, for example you may want to disable a button if it is not appropriate to click it in the current circumstances. An item which is disabled is shown in grey.

To disable or enable an item you can use PROC_enabledlgitem():

PROC_enabledlgitem(dlg%, id%, enable%)
Here id% is the identifier of the item you want to affect. To disable the item set enable% to FALSE, to re-enable it set it to TRUE.

Hiding and showing dialogue box items

You may wish to hide one or more items in a dialogue box, for example you may want to have two items in the same place, only one of which is visible.

To hide or show an item you can use PROC_showdlgitem():

PROC_showdlgitem(dlg%, id%, show%)
Here id% is the identifier of the item you want to affect. To hide the item set show% to FALSE, to show it again set it to TRUE.

Reading the contents of a dialogue box

Since the whole purpose of a dialogue box is to receive user input, it is vital that the current contents can be determined, particularly when the OK button is pressed.

To read the text associated with a dialogue box item (which may for example be the contents of an edit box or the current selection of a combo box) you can use FN_getdlgitemtext():

text$ = FN_getdlgitemtext(dlg%, id%)
The parameter dlg% is the value returned from FN_newdialog and the parameter id% is the identifier of the item in question.

In the case of a list box you can alternatively read the index of the selected item as follows:

selected% = FN_getlistboxselect(dlg%, id%)
where the value id% is the identifier for the list box. The returned value selected% gives the index of the currently selected item. If no item is selected, zero is returned.

Similarly to determine which item is selected in a combo box do the following:

selected% = FN_getcomboboxselect(dlg%, id%)
To discover the current state of a check box or radio button, you can use FN_isdlgitemchecked():
state% = FN_isdlgitemchecked(dlg%, id%)
where the value id% is the identifier for the check box or radio button. The value state% is set to FALSE if the button is not checked and to TRUE if it is checked.

To discover the current setting of a trackbar, call FN_gettrackbarpos():

setting% = FN_gettrackbarpos(dlg%, id%)

Determining when a button is clicked

You must be able to determine when the user has clicked on a button, so that (for example) when the OK button is clicked the contents of the dialogue box can be read and the box removed from the screen. The simplest method is to allocate the button an ID number in the range 1 to 9, which when clicked will cause FN_showdialog() to return. Alternatively (especially if you do not want the button press to terminate the dialogue box) allocate it an ID number returned from the FN_setproc function; see PROC_button for more details.

PROC_closedialog

When the user has clicked on OK, and the contents of the dialogue box have been processed, the box should (generally) be removed from the screen:
PROC_closedialog(dlg%)
The dialogue box template remains in memory, so you can display it again at any time by calling FN_showdialog.

The dialogue box should also be removed whenever your program returns to immediate mode (for example if an error occurs or the END statement is executed) or when your program's window is closed by the user. You will probbaly need to use ON ERROR and ON CLOSE to achieve this:

ON CLOSE PROC_closedialog(dlg%):QUIT
ON ERROR PROC_closedialog(dlg%):PRINT'REPORT$:END
Because the dialogue box uses space on the heap, it is essential that you remove it before executing a CLEAR, CHAIN or RUN statement.

FN_setproc(PROCname())

This function takes as a parameter the name of a procedure and returns an ID number which you can use when creating a control. Using this function automates the process of executing a procedure when a button is clicked.

The procedure must be defined to receive two parameters:

DEF PROChandler(D%, I%)
where D% and I% receive a value identifying the dialogue box (the value returned by FN_newdialog()) and the ID number of the button clicked respectively.

Formatting and conversion

BBC BASIC does not include the PRINT USING statement which, in many dialects of BASIC, provides a convenient means of formatting numeric output. Although equivalent effects may be obtained by using the format-control variable @%, if necessary in conjunction with string-manipulation functions (e.g. MID$), they are less straightforward to achieve.

BBC BASIC also lacks the UPPER$ (or UCASE$) and LOWER$ (or LCASE$) functions provided in some dialects of BASIC to convert strings to uppercase (capitals) or lowercase characters respectively.

The fnusing library provides replacements for these operations. It should be loaded from your program using the statement:

INSTALL @lib$+"fnusing"
Alternatively, since the functions are quite short, you might prefer to incorporate them in your own program (use the Insert command from the File menu).

The functions contained are:

FNusing(format$,value)

The FNusing function takes two parameters, a format string and a numeric value. It will normally be used within a PRINT statement in the following context:
PRINT FNusing(fmt1$,val1) FNusing(fmt2$,val2) .....
A significant difference from the conventional PRINT USING statement is that each format string can only refer to one numeric value, so you must call FNusing for each value you want to output.

The format string is a string literal or variable containing special formatting characters, as follows:

# The hash character is used to represent a digit position. Digit positions are always filled: if the number has fewer digits than positions specified it is right-justified (preceded by spaces) in the field. A decimal point may be inserted at any position in the field and numbers are rounded as necessary. For example:
PRINT FNusing("##.##",.78)
 0.78
PRINT FNusing("###.##",987.654)
987.65
+ A plus sign at the beginning or end of the format field causes the sign of the number (plus or minus) to be printed before or after the number. For example:
PRINT FNusing("+###.##",2.4)
  +2.40
PRINT FNusing("##.##+",55.678)
55.68+
PRINT FNusing("##.##+",-3)
 3.00-
A minus sign at the end of the format field causes negative numbers to be printed with a trailing minus sign. For example:
PRINT FNusing("##.##-",-68.95)
68.95-
PRINT FNusing("###.##-",-7)
  7.00-
** A double asterisk at the beginning of the format field causes leading spaces in the field to be filled with asterisks. The ** also specifies two more digit positions. For example:
PRINT FNusing("**#.#",12.39)
*12.4
PRINT FNusing("**##.##",-0.9)
**-0.90
$$ A double dollar (or pound) sign at the beginning of the format field causes a dollar (or pound) sign to be printed to the immediate left of the formatted number. The $$ also specifies two more digit positions, one of which is the currency symbol. For example:
PRINT FNusing("$$###.##",45.67)
  $45.67
PRINT FNusing("££###.##",123.45)
 £123.45
**$ A **$ (or **£) at the beginning of the format field combines the effects of the previous two formats. Leading spaces are filled with asterisks, and a dollar (or pound) sign is printed before the number. **$ specifies three more digit positions, one of which is the currency symbol. For example:
PRINT FNusing("**$##.##",2.34)
***$2.34
, A comma to the left of the decimal point in the format string causes a comma to be printed between every third digit before the decimal point. For example:
PRINT FNusing("#,###.##",1234.5)
1,234.50
PRINT FNusing("##,###,###",1E6)
 1,000,000
^^^^ Four carets may be placed after the digit characters to specify exponential format. The four carets allow space for "E-xx" to be printed. For example:
PRINT FNusing("##.##^^^^",234.56)
 2.35E2
PRINT FNusing("##.##^^^^",1E-30)
 1.00E-30
If the format string includes any characters other than those listed above, they are incorporated verbatim in the output string. For example:

PRINT FNusing("Price ££#.## including VAT",29.99)
Price £29.99 including VAT
If the number cannot be represented in the format supplied, question marks are printed:
PRINT FNusing("##.##",123)
?????

FNlower(string$)

The FNlower function takes a string parameter and returns a string in which capital letters (A to Z inclusive), if any, have been converted to lowercase (a to z).
PRINT FNlower("The Quick Brown Fox")
the quick brown fox

FNupper(string$)

The FNupper function takes a string parameter and returns a string in which lowercase letters (a to z inclusive), if any, have been converted to capitals (A to Z).
PRINT FNupper("The Quick Brown Fox")
THE QUICK BROWN FOX

Calendar functions

The datelib library contains a set of procedures and functions for performing operations on dates. The library should be loaded from your program using the statement:
INSTALL @lib$+"datelib"

The functions contained are:

FN_mjd(day%, month%, year%)

This function takes a date (consisting of a day-of-month, a month and a year) and converts it to the corresponding Modified Julian Day number. The Modified Julian Day is a count of days starting from Wednesday 17th November 1858 (which is MJD 0). Days prior to that date have negative MJD numbers. You can easily calculate the number of days between two different dates by subtracting their Modified Julian Day numbers.

The parameters supplied are the day of the month (1-31), the month number (1-12) and the year number (1-9999). Note that the functions in the DATELIB library will behave consistently for any date in that range (for example, converting from DMY to MJD and back will return the original values) but should not normally be used for dates prior to the introduction of the Gregorian calendar (in the UK on Thursday 14th September 1752, MJD −38779). For earlier dates the day, month and year values may not be correct, and since use of the old Julian calendar persisted in some countries until as late as 1927 care should be taken when using this function.

FN_day(mjd%)

This function takes a Modified Julian Day number and returns the day-of-month (1-31) to which it corresponds.

FN_month(mjd%)

This function takes a Modified Julian Day number and returns the month (1-12) to which it corresponds.

FN_year(mjd%)

This function takes a Modified Julian Day number and returns the year (1-9999) to which it corresponds.

FN_dow(mjd%)

This function takes a Modified Julian Day number and returns the day-of-week (0-6, where 0 is Sunday) to which it corresponds.

FN_dim(month%, year%)

This function takes a month (1-12) and a year (1-9999) and returns the number of days in the month (in the range 28 to 31). By setting month% to 2 (February) you can use this function to determine whether the year is a Leap Year.

FN_today

This function returns the Modified Julian Day number corresponding to today's date (assuming the PC's clock is correctly set).

FN_date$(mjd%, format$)

This function takes a Modified Julian Day number and a format string, and returns a formatted string containing the date. The format string can contain any of the following codes:
dDay of month as digits with no leading zero.
ddDay of month as digits with leading zero for single-digit days.
dddDay of week as a three-letter abbreviation.
ddddDay of week as its full name.
MMonth as digits with no leading zero.
MMMonth as digits with leading zero for single-digit months.
MMMMonth as a three-letter abbreviation.
MMMMMonth as its full name.
yYear as last two digits, but with no leading zero.
yyYear as last two digits, but with leading zero for years less than 10.
yyyyYear represented by full four digits.
For example:
date$ = FN_date$(mjd%, "ddd dd MMM yyyy")
will return a string of the form "Sun 22 Feb 2004".

FN_readdate(date$, code$, minyear%)

This function parses a string containing a date, and returns the corresponding Modified Julian Day number. In addition to the date you must supply a string containing one of the following codes: "dmy", "mdy", "ymd", "ydm", "dym" or "myd"; this informs the function of the order in which the various elements of the date (day, month, year) are present in the string. If the year is specified as a four digit number the third parameter is not used; if only the last two digits of the year are specified the third parameter is the minimum year number to return. For example if the third parameter is 1950, two digit year numbers correspond to the years 1950 to 2049 inclusive.

The FN_readdate function attempts to make sense of the date string however it is formatted, so long as the elements are in the specified order. For example it will accept "22/2/2004", "22 Feb 04", "22-02-04" etc. If it cannot make sense of the string it will return the value &80000000.

3D graphics

The ogllib, gleslib and webgllib libraries contain a set of procedures and functions for displaying and animating three-dimensional graphics. They provide an interface to OpenGL (desktop editions), OpenGL ES (mobile editions) and WebGL (in-browser edition) respectively. The library should be loaded from your program using a command similar to this (the required alternative library will be automatically installed, if necessary, but when building an application bundle you may need to include it separately in the list of embedded modules):
INSTALL @lib$+"ogllib"

The functions contained are:

FN_initgl(hw%%,cull%,light%)

This function initialises the 3D graphics system. If the returned value is zero it indicates that 3D graphics are not available (this should not happen in practice).

The hw%% parameter is the handle of the window which is to contain the 3D graphics. It should normally be set to @hwnd% if the graphics are to be displayed in BBC BASIC's main output window. Note that you cannot mix OpenGL graphics and normal BBC BASIC output (text or graphics) in the same window.

The cull% parameter specifies the culling mode, which determines whether surfaces behave as single sided or double sided. Possible values are 1 (none), 2 (clockwise) or 3 (counterclockwise). If in doubt, set to 1.

The light% parameter determines whether the lighting engine is enabled. Set to 1 to enable lighting or to 0 to disable lighting. When lighting is disabled all objects appear normally as if uniformly illuminated. When lighting is enabled it is necessary for all objects to include surface normals in the vertex description.

FN_load3d(pdev%,file$,num%,fmt%,size%)

This function loads an object or scene (comprising a set of triangles, each consisting of three vertices) from a file. It returns a pointer to a vertex buffer; if zero is returned the file could not be opened.

The pdev% parameter is the value returned from FN_initgl. The file$ parameter is the name of a file containing vertex data in Flexible Vertex Format.

The num%, fmt% and size% parameters are outputs from FN_load3d and are set to the number of vertices, the vertex format and the size in bytes of each vertex respectively.

The file format is as follows:

Number of vertices (4 bytes, LSB first)
Vertex format (2 bytes, LSB first)
Vertex size in bytes (2 bytes, LSB first)
Data for each vertex (see below)

Vertex description

The vertex data must conform to the Flexible Vertex Format. Each vertex consists of one or more of the following items, in the specified order:
CodeSizeDataComments
&00212XYZ position Always required
&01012Surface normal When lighting used
&0404Diffuse colour When neither texture nor material specified
&0804Specular colour For shiny objects
&1008UV texture coordinates When texture specified
To obtain the vertex format code add together the codes for the items included. To obtain the vertex size add together the sizes of the items included. The XYZ position and surface normal items each consist of three 4-byte floating point numbers (see FN_f4). The diffuse colour and specular colour items each consist of 4-byte colour values (&FFrrggbb). The texture coordinates consist of a pair of 4-byte floating point numbers. The simplest vertex description consists of an XYZ position and a diffuse colour (format &042; size 16 bytes). See FN_f4 for an example of creating a file in this format.

FN_loadtexture(pdev%,file$)

This function loads a texture map from an image file (BMP, GIF, JPEG or PNG format) and returns a pointer to the texture; if zero is returned the file could not be opened or recognised. The filename must begin with a drive letter to distinguish it from a web URL.

The pdev% parameter is the value returned from FN_initgl. The file$ parameter is the (path and) filename of the image file.

Note that in WebGL (only) the image must have 'power-of-two' dimensions, i.e. both the width and the height must be powers of two (256, 512, 1024 etc.).

PROC_release(pobj%)

This function releases an object (3D device, vertex buffer or texture) when it is no longer required. It should be used to free the resources used by the object(s), otherwise you may eventually run out of memory.

The pobj% parameter is the value returned from FN_initgl, FN_load3d or FN_loadtexture.

FN_f4(num)

This function converts a number to a 4-byte (single precision) floating point value. Many floating point values used by OpenGL (e.g. within vertex descriptions) are in this format. As an illustration of its use the following code segment creates a file containing a single triangle consisting of three vertices, suitable for being loaded by FN_load3d:
F% = OPENOUT"TRIANGLE.FVF"
PROC4(3):REM 3 vertices
PROC4(&100042):REM vertex size &10 and format &42
PROC4(FN_f4(-1.0)):PROC4(FN_f4(-1.0)):PROC4(FN_f4(1.0)):PROC4(&FF0000FF)
PROC4(FN_f4(1.0)):PROC4(FN_f4(-1.0)):PROC4(FN_f4(1.0)):PROC4(&FF00FF00)
PROC4(FN_f4(0.0)):PROC4(FN_f4(1.0)):PROC4(FN_f4(0.0)):PROC4(&FFFF0000)
CLOSE #F%
DEF PROC4(A%):BPUT#F%,A%:BPUT#F%,A%>>8:BPUT#F%,A%>>16:BPUT#F%,A%>>24:ENDPROC

PROC_render(pdev%,....)

This procedure draws a 2D view of the 3D world to the screen. It takes 24 parameters as follows:
pdev% The value returned from FN_initgl.
bcol% The background colour (&FFrrggbb).
nlight% The number of lights. Set to zero if lighting is not used.
light%() An array of adjusted pointers to light structures (see note 1).
nobj% The number of objects (i.e. vertex buffers).
mat%() An array of adjusted pointers to material structures (see note 2).
tex%() An array of texture pointers (e.g. returned from FN_loadtexture).
vbuf%() An array of vertex buffer pointers (e.g. returned from FN_load3d).
vnum%() An array of vertex counts (e.g. returned from FN_load3d).
vfmt%() An array of vertex format codes (e.g. returned from FN_load3d).
vsize%() An array of vertex sizes (e.g. returned from FN_load3d).
yaw() An array of yaw angles (rotations about the Y-axis).
pitch() An array of pitch angles (rotations about the X-axis).
roll() An array of roll angles (rotations about the Z-axis).
X() An array of translations along the X-axis.
Y() An array of translations along the Y-axis.
Z() An array of translations along the Z-axis.
eye() An array eye(0), eye(1), eye(2) holding the XYZ coordinates of the eye or camera.
look() An array look(0), look(1), look(2) holding the XYZ coordinates of a point on the eyeline.
fov The vertical field-of-view in radians (equivalent to the camera's zoom).
ar The aspect ratio of the 3D graphics window (width/height).
zn The distance from the camera to the near plane (objects nearer than this are invisible).
zf The distance from the camera to the far plane (objects further away than this are invisible).
roll The camera's roll angle (in radians).
Notes:
  1. A light can be created as follows. The Type% member is 1 for a point-source light (the direction is ignored), 2 for a spotlight (both the position and direction are important) and 3 for a directional light (the position is ignored).
    DIM light{Type%, Diffuse{r%,g%,b%,a%}, Specular{r%,g%,b%,a%}, \
    \ Ambient{r%,g%,b%,a%}, Position{x%,y%,z%}, Direction{x%,y%,z%}, \
    \ Range%, Falloff%, Attenuation0%, Attenuation1%, Attenuation2%, \
    \ Theta%, Phi%}
    light%(0) = light{} - PAGE + !340
    
    light.Type% = 3               : REM directional light
    light.Diffuse.r% = FN_f4(1)   : REM red component
    light.Diffuse.g% = FN_f4(1)   : REM green component
    light.Diffuse.b% = FN_f4(0)   : REM blue component
    light.Specular.r% = FN_f4(1)   : REM red specular
    light.Specular.g% = FN_f4(1)   : REM green specular
    light.Specular.b% = FN_f4(0)   : REM blue specular
    light.Ambient.r% = FN_f4(0)   : REM red ambient light
    light.Ambient.g% = FN_f4(0)   : REM green ambient light
    light.Ambient.b% = FN_f4(0)   : REM blue ambient light
    light.Direction.x% = FN_f4(0) : REM X component of direction
    light.Direction.y% = FN_f4(0) : REM Y component of direction
    light.Direction.z% = FN_f4(1) : REM Z component of direction
    
  2. A material can be created as follows.
    DIM material{Diffuse{r%,g%,b%,a%}, Ambient{r%,g%,b%,a%}, \
    \   Specular{r%,g%,b%,a%}, Emissive{r%,g%,b%,a%}, Power%}
    mat%(0) = material{} - PAGE + !340
    
    material.Diffuse.r% = FN_f4(0.0) : REM red component of diffuse colour
    material.Diffuse.g% = FN_f4(0.7) : REM green component of diffuse colour
    material.Diffuse.b% = FN_f4(1.0) : REM blue component of diffuse colour
    material.Ambient.r% = FN_f4(0.25)   : REM red component of ambient 
    material.Ambient.g% = FN_f4(0.25)   : REM green component of ambient
    material.Ambient.b% = FN_f4(0.25)   : REM blue component of ambient
    material.Specular.r% = FN_f4(1.0) : REM red component of specular colour
    material.Specular.g% = FN_f4(1.0) : REM green component of specular colour
    material.Specular.b% = FN_f4(1.0) : REM blue component of specular colour
    material.Power% = FN_f4(100)
    
  3. The arrays of object parameters should contain at least as many elements as the value of nobj%. Unused arrays, for example mat%() or tex%(), should contain zeros (this is the initial state following DIM).

  4. Rotations take place around world axes in the order roll then pitch then yaw.

Plotting angled ellipses

The BBC BASIC for SDL 2.0 ELLIPSE statement plots only axis-aligned ellipses. The ELLIPSE library contains the procedures PROCellipse and PROCellipsefill which provide the facility to plot an outline or filled ellipse rotated by a specified angle. The library should be loaded from your program using the statement:
INSTALL @lib$+"ellipse"

PROCellipse(x,y,a,b,angle)

This procedure plots an outline ellipse centred at graphics coordinates x,y and with radii of length a and b. The fifth parameter specifies that the ellipse should be rotated anticlockwise by angle radians (if the angle is zero the ellipse is plotted with the a axis horizontal, as with the normal ELLIPSE statement).

The ellipse is drawn in the current graphics foreground colour and mode, as specified by GCOL.

PROCellipsefill(x,y,a,b,angle)

This procedure works in an identical fashion to PROCellipse, except that a filled (solid) ellipse is plotted. Again, the ellipse is drawn in the current graphics foreground colour and mode, as specified by GCOL.

Sorting data arrays

The SORTLIB library provides a fast, highly optimised and flexible means of sorting data contained in arrays. SORTLIB will sort byte arrays, integer arrays, floating point arrays and string arrays. The library should be loaded from your program using the statement:
INSTALL @lib$+"sortlib"
It contains the single function FN_sortinit.

FN_sortinit(dir%,smode%)

Before any sorting operations can be carried out the library must be initialised as follows:
sort%% = FN_sortinit(dir%,smode%)
where dir% determines the sorting direction (0 = ascending, 1 = descending) and smode% is ingored in this implementation. Note that it is most important that a 64-bit variable (%% suffix) or a variant variable (no suffix) is used to contain the returned value, since this may be a 64-bit address.

If you prefer, you can initialise it multiple times with the different options and then CALL the appropriate variable when needed:

sortascending%% = FN_sortinit(0,0)
sortdescending%% = FN_sortinit(1,0)
To sort the contents of an entire array do the following:
C% = DIM(array(),DIM(array()))+1 
CALL sort%%, array(0)
To sort the contents of a '1-based' array (where the first element has the subscript 1) do the following:
C% = DIM(array(),DIM(array())) 
CALL sort%%, array(1)
To sort only part of an array, set C% to the number of elements you want to sort and specify the first element to be sorted:
C% = howmany% 
CALL sort%%, array(first%)
To sort multiple arrays according to the contents of a key array do the following:
C% = size% 
CALL sort%%, keyarray(0), array2$(0), array3%(0)...
There can be any number of dependent arrays of any type. If the primary key array contains two or more identical elements, the remaining array(s) will be used as secondary keys, in the order specified.

To sort a two-dimensional array, where the contents of the first row are used as a key for the other rows, do the following:

DIM array(2,999)
C% = DIM(array(),DIM(array()))+1
CALL sort%%, array(0,0), array(1,0), array(2,0)

Socket (network) connections

The SOCKLIB library contains a set of procedures and functions for making socket connections to a remote computer via a local area network or the internet, and transferring data in both directions.

The library should be loaded from your program using the statement:

INSTALL @lib$+"socklib"

The functions contained are:

PROC_initsockets

PROC_initsockets must be called (once) before any other functions in the SOCKLIB library are accessed; by default it allows up to 16 sockets to be opened simultaneously. If this is insufficient pass the required maximum number of sockets as a parameter, for example:
PROC_initsockets(100)

PROC_exitsockets

PROC_exitsockets must be called after all socket operations have been completed. You should normally incorporate ON ERROR and ON CLOSE statements in your program to ensure that PROC_exitsockets is called even if the program is terminated unexpectedly. If you don't do so your program might not work correctly if executed a second time.

FN_gethostname

FN_gethostname returns, as a string, the network name of your local computer (the one on which BBC BASIC for SDL 2.0 is running).

FN_tcplisten(host$,port$)

FN_tcplisten creates a listening socket to listen for incoming TCP/IP connections. If your program needs to respond to connections made from a remote computer (a web server would be an example) it must call FN_tcplisten. The function takes two string parameters: the name of the local computer (typically as returned from FN_gethostname) and the port name or number on which you want to listen. For example if you are listening for incoming HTTP (Hyper Text Transport Protocol) connections you would normally set port$ to "80".

If the listening socket is created successfully the socket number is returned. If the call fails a negative number is returned (see the code of SOCKLIB.BBC for details). You can call FN_socketerror to discover more about the error.

FN_tcpconnect(host$,port$)

FN_tcpconnect makes a TCP/IP connection to a remote machine. You would call this function if your program needs to initiate a connection (a program to send email would be an example). The function takes two string parameters: the name or IP address of the remote computer and the port name or number on which to make the connection. For example if you were connecting to an SMTP mail server would would normally set port$ to "25" or "mail". If an IP address rather than a machine name is specified, it must be supplied as a string of the form "xx.xx.xx.xx".

If the connection is made successfully the socket number is returned. If the call fails a negative number is returned (see the code of SOCKLIB.BBC for details). You can call FN_socketerror to discover more about the error.

FN_check_connection(socket%)

FN_check_connection checks to see if an incoming connection has been received by a listening socket; it takes as a parameter the socket number (as returned from FN_tcplisten). If no connection has been received zero is returned, and the listening socket continues to listen for a connection. If an incoming connection has been received, FN_check_connection returns the socket number on which the connection has been opened and the listening socket is closed. If you want to listen for further connections you can call FN_check_connectionM instead.

FN_writesocket(socket%,buffer%,size%)

FN_writesocket writes data to a connected socket. It takes three parameters: the socket number (as returned from FN_tcpconnect or FN_check_connection), the address of a buffer containing the data, and the length of the data in bytes. If the function succeeds it returns the number of bytes sent.

Note that it is possible for FN_writesocket to return a value less than the total length of the data. This indicates that only some of the data has been sent, and you should make further calls (adjusting the values of buffer% and size% accordingly) until all the data has been sent.

FN_writelinesocket(socket%,string$)

FN_writelinesocket writes a string, followed by the characters carriage return (CHR$13) and line feed (CHR$10), to a connected socket. It takes two parameters: the socket number (as returned from FN_tcpconnect or FN_check_connection) and the string to be sent. If the function succeeds it returns zero.

FN_readsocket(socket%,buffer%,size%)

FN_readsocket reads data from a connected socket. It takes three parameters: the socket number (as returned from FN_tcpconnect or FN_check_connection), the address of a buffer to receive the data, and the maximum length of the data in bytes (the size of the buffer). If the function succeeds it returns the number of data bytes received. If the socket has been disconnected (e.g. by the remote computer) −1 is returned.

FN_readsocket does not wait for data to be received. If no data has been received since the last call, it returns zero.

FN_readlinesocket(socket%,maxtime%,string$)

FN_readlinesocket reads a line from a connected socket (a line is defined as a string of characters terminated by CRLF, LFCR or LF, where CR signifies carriage return and LF signifies line feed); the terminator is not returned as part of the string. It takes three parameters: the socket number (as returned from FN_tcpconnect or FN_check_connection), a maximum time to wait in centiseconds (e.g. 100 signifies one second) and the name of the string variable in which the line will be returned. If the function succeeds it returns the length of the string in characters. If the socket has been disconnected (e.g. by the remote computer) −1 is returned.

FN_getpeername(socket%)

FN_getpeername returns the IP address of the remote machine to which a socket is connected, as a string of the form "xx.xx.xx.xx". It takes as a parameter the socket number (as returned from FN_tcpconnect, FN_udpsocket or FN_check_connection). If the function fails an empty string is returned.

PROC_closesocket(socket%)

PROC_closesocket closes a socket connection. It takes as a parameter the socket number (as returned from FN_tcpconnect, FN_udpsocket or FN_check_connection).

FN_socketerror$

FN_socketerror$ returns a string corresponding to the most recent socket error to have occurred (if any).

FN_check_connectionM(socket%)

FN_check_connectionM works the same as FN_check_connection except that the listening socket is not closed. This allows you to continue listening for more connections.

FN_sethost(host$)

FN_sethost accepts the name or address of a host in the form of a string and returns its IPv4 address, in network byte order (i.e. big-endian). If the host name is not recognised zero is returned.

FN_setport(service$)

FN_setport accepts the name of a service or a port number in the form of a string and returns the port, in network byte order. If the service name is not recognised zero is returned.

FN_udpsocket(host$,port$)

FN_udpsocket creates a UDP (User Datagram Protocol) socket which may optionally be bound to a port (specified either as a decimal string, e.g. "1234", or the name of a well-known service, e.g. "time") and/or a host (specified either as a dotted IP address "xx.xx.xx.xx" or a name). If you do not want to bind the socket, pass empty strings as the two parameters. If the socket was not successfully created, a value less than or equal to zero is returned.

FN_sendtosocket(socket%,buffer%,size%,host%,port%)

FN_sendtosocket sends a data packet via a UDP socket. Its parameters are the socket number (as returned from FN_udpsocket), the address of the data packet in memory, the length of the data packet, the host address to send to (e.g. as returned from FN_sethost or FN_recvfromsocket, or use &FFFFFFFF to broadcast the packet) and the port number to use (as returned from FN_setport. The function returns the number of bytes sent or a negative value in the event of an error.

FN_recvfromsocket(socket%,buffer%,size%,host%,port%)

FN_recvfromsocket receives a data packet via a UDP socket. The socket should normally have been bound to the port on which the incoming data is expected to arrive. Its parameters are the socket number (as returned from FN_udpsocket), the memory address at which received data will be stored, the size of the memory buffer, a variable in which the address of the host (peer) will be returned (in network byte order) and a variable in which the port number used by the host (peer) will be returned (again in network byte order). The function returns the number of bytes received, or zero if no incoming packet is available.

Antialiased graphics

The built-in BBC BASIC graphics statements are not antialiased. As a result things like curves and gently-sloping lines can have aliases (or jaggies) causing the appearance to be impaired. The aagfxlib library contains a set of procedures and functions for drawing antialiased graphics.

The library should be loaded from your program using the statement:

INSTALL @lib$+"aagfxlib"

The functions contained are:

PROC_aaline(x1, y1, x2, y2, thickness, colour%, style%)

PROC_aaline draws a straight line between two points. It takes seven parameters: the x and y coordinates of the start of the line and the x and y coordinates of the end of the line (in BBC BASIC graphics units), the thickness of the line in pixels, the colour of the line and the line style. Note that the coordinates and thickness do not need to be integers.

The colour must be supplied as a 32-bit integer &AABBGGRR where AA is the alpha (opacity) value in the range 0 (fully transparent) to &FF (fully opaque), and RR, GG and BB are the red, green and blue components of the colour in the range 0 (none) to &FF (maximum).

The style can be zero or a combination of one or more of the following values (note: these are not all the same values as used by the Windows GDIPLIB library):

LineEndTriangle = 1
LineEndRound = 2
LineEndArrow = 3
LineStartTriangle = &100
LineStartRound = &200
LineStartArrow = &300
LineDot = &10000
LineDash  = &20000
LineDotDash = &30000

PROC_aapolyline(points%, x(), y(), thickness, colour%, style%)

PROC_aapolyline draws a set of joined straight line segments between specified points. It takes six parameters: the number of points (inclusive), an array containing the x-coordinates of the points, an array containing the y-coordinates of the points (in BBC BASIC graphics units), the line thickness in pixels, the line colour and the line style. Note that the arrays must not be integer arrays, and that the first point corresponds to subscript zero. If you are drawing a closed polygon the number of points should be one more than the number of vertices, with the first and last points coinciding.

The colour and style values are the same as used with PROC_aaline, except that dotted and dashed lines are not supported.

PROC_aabezier(x1, y1, x2, y2, x3, y3, x4, y4, thickness, colour%, style%)

PROC_aabezier draws a Bezier curve. It takes eleven parameters: the x and y coordinates of four control points (in BBC BASIC graphics units), the line thickness in pixels, the line colour and the line style. Note that the coordinates and thickness do not need to be integers.

The colour and style values are the same as used with PROC_aaline, except that dotted and dashed lines are not supported.

PROC_aapolybezier(points%, x(), y(), thickness, colour%, style%)

PROC_aapolybezier draws a set of joined Bezier curves. It takes six parameters: the number of control points, an array containing the x-coordinates of the points, an array containing the y-coordinates of the points (in BBC BASIC graphics units), the line thickness in pixels, the line colour and the line style. The number of control points should be 3n + 1 where n is the total number of Bezier curves. Note that the arrays must not be integer arrays, and that the first point corresponds to subscript zero.

The colour and style values are the same as used with PROC_aaline, except that dotted and dashed lines are not supported.

PROC_aafillbezier(points%, x(), y(), colour%)

PROC_aafillbezier fills a closed shape drawn using a set of Bezier curves. It takes four parameters: the number of control points, an array containing the x-coordinates of the points, an array containing the y-coordinates of the points (in BBC BASIC graphics units) and the colour. The number of control points should be 3n + 1 where n (>= 2) is the total number of Bezier curves. Note that the arrays must not be integer arrays, and that the first point corresponds to subscript zero.

The colour value is the same as used with PROC_aaline.

PROC_aaarc(xc, yc, xr, yr, as, ad, thickness, colour%, style%)

PROC_aaarc draws an outline (axis-aligned) elliptical arc. It takes nine parameters: the x and y coordinates of the centre of the ellipse (in BBC BASIC graphics units), the x and y radii, the start angle (degrees, clockwise from the x-axis), the length of the arc (degrees, clockwise), the line thickness in pixels, the line colour and the line style.

Note that the start angle and the length of the arc are assumed to apply to the circular arc before foreshortening to an ellipse. This is usually what is wanted if you are drawing a circular arc as if viewed obliquely (for example as in a pie-chart) but may not be how other graphics libraries interpret these parameters.

The colour and style values are the same as used with PROC_aaline, except that dotted and dashed lines are not supported.

PROC_aasector(xc, yc, xr, yr, as, ad, colour%)

PROC_aasector draws a filled (axis-aligned) elliptical sector. It takes seven parameters: the x and y coordinates of the centre of the ellipse (in BBC BASIC graphics units), the x and y radii, the start angle (degrees, clockwise from the x-axis), the angle subtended by the sector (degrees, clockwise) and the colour.

Note that the start angle and the length of the arc are assumed to apply to the circular sector before foreshortening to an ellipse. This is usually what is wanted if you are drawing a circular sector as if viewed obliquely (for example as in a pie-chart) but may not be how other graphics libraries interpret these parameters.

The colour value is the same as used with PROC_aaline.

PROC_aapolygon(vertices%, x(), y(), colour%)

PROC_aapolygon draws a filled polygon. It takes four parameters: the number of vertices, an array containing the x-coordinates of the vertices, an array containing the y-coordinates of the vertices (in BBC BASIC graphics units) and the colour. Note that the arrays must not be integer arrays, and that the first vertex corresponds to subscript zero.

The colour value is the same as used with PROC_aaline.

PROC_aaellipse(x, y, a, b, thickness, rotation, colour%)

PROC_aaellipse draws an angled outline ellipse (or at least a very close approximation to one). It takes seven parameters: the coordinates x,y of the centre of the ellipse, the major and minor radii of the ellipse a & b respectively, the line thickness in pixels, the angle of rotation (radians) where a positive value indicates an anticlockwise rotation, and the colour.

The colour value is the same as used with PROC_aaline.

PROC_aaellipsefill(x, y, a, b, rotation, colour%)

PROC_aaellipsefill draws an angled filled ellipse (or at least a very close approximation to one). It takes six parameters: the coordinates x,y of the centre of the ellipse, the major and minor radii of the ellipse a & b respectively, the angle of rotation (radians) where a positive value indicates an anticlockwise rotation and the colour.

The colour value is the same as used with PROC_aaline.

String manipulation

The STRINGLIB library contains several functions for manipulating or creating character strings. It should be loaded from your program as follows:
INSTALL @lib$+"stringlib"
The functions contained are:

FN_lower(A$)

FN_lower converts a string to lower case.
PRINT FN_lower("The Quick Brown Fox")
the quick brown fox

FN_upper(A$)

FN_upper converts a string to upper case (capitals).
PRINT FN_upper("The Quick Brown Fox")
THE QUICK BROWN FOX

FN_title(A$)

FN_title converts a string to title case (the first letter of each word is converted to a capital).
PRINT FN_title("the quick brown fox")
The Quick Brown Fox

FN_binary(N%)

FN_binary converts the supplied integer parameter to a binary string.
PRINT FN_binary(22)
10110

FN_tobase(num%, base%, min%)

FN_tobase converts the integer value num% to a string in number base base% with leading zeroes added (if necessary) so that the total number of characters is at least min%.
PRINT FN_tobase(22, 8, 4)
0026

FN_findreplace(text$, old$, new$, start%)

FN_findreplace searches the string text$ for occurrences of the substring old$, and if found replaces them with the string new$, optionally specifying where in the original string the search should start. The parameter text$ is modified, so it must be a variable. The returned value is the number of replacements made.
text$ = "The quick brown fox"
num% = FN_findreplace(text$, "brown", "silver", 0)
PRINT text$
The quick silver fox

FN_findreplacei(text$, old$, new$, start%)

FN_findreplacei works the same as FN_findreplace except that a case-insensitive comparison is made.
text$ = "The quick brown fox"
num% = FN_findreplacei(text$, "BROWN", "silver", 0)
PRINT text$
The quick silver fox

FN_instrr(A$, B$, S%)

FN_instrr works like the INSTR function except that the position of the last rather than the first match is returned (effectively the search takes place in reverse, starting at the end of the string). The third parameter specifies where to start the search (set it to zero to search the entire string).
PRINT FN_instrr("the quick brown fox", "o", 0)
18

FN_instri(A$, B$, S%)

FN_instri works like the INSTR function except that a case-insensitive comparison is made.
PRINT FN_instri("the quick brown fox", "O", 0)
13

FN_instrri(A$, B$, S%)

FN_instrri works like FN_instrr except that a case-insensitive comparison is made.
PRINT FN_instrri("the quick brown fox", "O", 0)
18

FN_trim(A$)

FN_trim removes leading and trailing spaces from the supplied string.
PRINT """" FN_trim("   The quick brown fox   ") """"
"The quick brown fox"

FN_split(text$, delim$, array$())

FN_split splits a string into parts at the specified delimiter, and stores the parts in the specified array. The value returned is the total number of parts stored in the array (if the string does not contain the specified delimiter, the returned value is 1 and the entire string is copied into element zero of the array). If the array doesn't already exist it is created. If the array exists, but it is not large enough, the original array is discarded (the memory it occupied is leaked) and a new array created.
parts% = FN_split("The quick brown fox", " ", a$())
PRINT a$(1)
quick

FN_join(array$(), delim$, num%)

FN_join reverses the effect of FN_split. It returns a string consisting of the first num% elements of the supplied array (starting at element zero), separated by the supplied delimiter.
parts% = FN_split("The quick brown fox", " ", a$())
PRINT FN_join(a$(), "+", parts%)
The+quick+brown+fox

Multiple output windows

The MULTIWIN library provides the capability of having two or more largely independent output windows, each capable of receiving normal BBC BASIC text and graphics output. The library should be loaded from your program as follows:
INSTALL @lib$+"multiwin"
The functions contained are: There are a few minor restrictions in its use:
  1. The REFRESH command is disabled.
  2. The text cursor (caret) can only be shown in the main window.
  3. The output windows share the same colour palette.
  4. The @ox% and @oy% system variables cannot be used.

PROC_multiwin(extra%)

PROC_multiwin sets up the multiple-window capability and should be called just once during the initialisation phase of your program. It takes one parameter, which is the maximum number of extra output windows required (i.e. in addition to the normal main window).

FN_createwin(n%, text$, x%, y%, w%, h%, i%, s%, e%)

FN_createwin creates a new output window. The parameters are as follows: n% is the window number (between 1 and the value specified in the call to PROC_multiwin), text$ is the window title, x% is the horizontal position of the window, y% is the vertical position of the window, w% is the width of the window, h% is the height of the window, i% is an ID number (ignored), s% is the window style and e% is the extended window style (ignored).

Normally s% can be set to zero. The function returns the handle of the created window.

PROC_selectwin(win%)

PROC_selectwin selects a window to be the active output window; it takes one parameter, the window number (either 0, to select the main window, or between 1 and the value specified in the call to PROC_multiwin). It also has the effect of refreshing the previously selected window, that is rendering its contents to the screen.

After calling PROC_selectwin all text and graphics output goes to the selected window.

PROC_closewin(win%)

PROC_closewin closes one of the extra windows. It takes one parameter, the window number (between 1 and the value specified in the call to PROC_multiwin). Each call to FN_createwin should be paired with a call to PROC_closewin and you should ensure that your program contains appropriate ON ERROR and ON CLOSE statements to guarantee that the additional windows are closed before exit.

You cannot close the currently-selected output window; if necessary precede the statement with a PROC_selectwin(0).

No-wait function replacements

The NOWAIT library provides replacements for the GET/GET$ and INKEY/INKEY$ functions, and for the WAIT, SOUND and INPUT statements which, unlike the native versions, don't block or stall the processing of asynchronous events such as ON SYS and ON TIME interrupts. The library should be loaded from your program as follows:
INSTALL @lib$+"nowait"
The functions contained are:

FNget and FNget$

FNget and FNget$ directly replace the GET and GET$ functions.

FNinkey(t%) and FNinkey$(t%)

FNinkey and FNinkey$ directly replace the INKEY and INKEY$ functions when used with a positive parameter (the maximum time to wait in centiseconds).

PROCwait(t%)

PROCwait directly replaces the WAIT statement. It takes a single parameter, being the approximate time to wait in centiseconds.

PROCsound(c%, a%, p%, d%)

PROCsound directly replaces the SOUND statement. It takes four parameters: channel/effects, amplitude/envelope, pitch and duration.

FNinput

FNinput provides a partial replacement for the INPUT statement; it returns a string containing the user's response. If you need to display a prompt string, do so using PRINT before calling FNinput. If you need to input a numeric value rather than a string, use VAL to perform the conversion.

High speed timers

The standard ON TIME timers have two disadvantages: firstly their minimum period is 10 milliseconds (a maximum rate of 100 Hz) and secondly they must share the same interrupt service routine. These limitations may be overcome by using the TIMERLIB library. The library should be loaded from your program as follows:
INSTALL @lib$+"timerlib"
The functions contained are: Since the timer interrupts available using this library may occur at rates up to 1000 Hz, the problem of stalling them is particularly severe. To ensure a timely response to the interrupts, and reduce the likelihood of flooding the event queue, you should avoid using any time-consuming statements and functions such as INKEY, INPUT, GET, SOUND or WAIT. If necessary you can make use of the replacement routines in the NOWAIT library.

Also, pay particular attention to the precautions described under Notes on the use of interrupts.

FN_ontimer(period%, PROCcallback, flags%)

FN_ontimer sets up a timer. It takes three parameters: period% is the required period in milliseconds, PROCcallback is the name of the procedure to be called with the specified periodicity and flags% is ignored (only periodic timers are currently supported).

The function returns an identifier for use in a subsequent PROC_killtimer, or zero if the timer could not be created.

PROC_killtimer(timerid%)

PROC_killtimer cancels the timer whose identifier is passed as a parameter. It is important to cancel all periodic timers before quitting your program. You should normally incorporate ON CLOSE and ON ERROR statements to ensure this happens however the program is terminated.

Parsing XML files

XML (Extensible Markup Language) files are increasingly commonly used, for example they are the format in which RSS feeds are delivered. The XMLLIB library provides a number of functions to simplify the parsing of XML files. The library should be loaded from your program as follows:
INSTALL @lib$+"xmllib"
The functions contained are:

PROC_initXML(xmlobj{}, xmlfile$)

PROC_initXML initialises the parser and opens the file xmlfile$, which contains the XML data. It returns the structure xmlobj{}, which must be passed to the other library functions as required; xmlobj{} need not be (and should not be) initialised prior to calling PROC_initXML.

You can concurrently parse multiple XML files by using separate object structures for each file.

FN_isTag(xmlobj{})

FN_isTag returns TRUE if the next token to be returned by FN_nextToken is a tag and FALSE if not. It takes one parameter: the object structure returned by PROC_initXML.

FN_nextToken(xmlobj{})

FN_nextToken sequentially finds each token in the XML file, and returns a string containing that token. It takes one parameter: the object structure returned by PROC_initXML.

A token can be either a tag or the text contained between two tags; if it is a tag the first character of the returned string will be "<" and the last character will be ">".

FN_getLevel(xmlobj{})

FN_getLevel returns the current level in the hierarchy (where the base level is 1). It takes one parameter: the object structure returned by PROC_initXML.

At each level of nesting the value returned increases by one. If PROC_exitXML has been called the returned value will be zero.

FN_skipTo(xmlobj{}, tagname$, level%)

FN_skipTo scans forwards through the XML file until the specified tag is found, or until the level in the hierarchy is lower than a specified value. It takes three parameters: the object structure returned by PROC_initXML, the name of the tag for which to search and the minimum level. The value returned is the level at which the tag was found, or 0 if the tag was not found within the specified scope (or the end-of-file has been reached).

If the third parameter is set to zero the function will scan the remainder of the file; if the third parameter is set equal to the current level (as returned by FN_getLevel) only the rest of the current 'block' will be searched.

FN_skipToRet(xmlobj{}, tagname$, level%, token$)

FN_skipToRet is similar to FN_skipTo except that it returns, in the string variable specified as the fourth parameter, the token corresponding to the specified tag.

PROC_exitXML(xmlobj{})

PROC_exitXML closes the XML file and cancels the parser. It takes one parameter: the object structure returned by PROC_initXML.

FN_repEnt(text$)

FN_repEnt replaces entity references in the supplied text string; the string is modified (so must be specified as a variable). Entity references are special representations of characters which otherwise have a special meaning in XML (for example <, >, ', " and &).

Note that the returned string may contain Unicode characters encoded as UTF-8; see User-defined modes for how to display such characters.

Extending the 32-bit x86 assembler

The ASMLIB library provides support for some additional 32-bit x86 assembler instructions, in particular the CMOV (conditional move) instructions available on the Pentium Pro and later processors, and the SSE (Streaming SIMD Extension) instructions available on the Pentium III and later processors. The library should be loaded from your program as follows:
INSTALL @lib$+"asmlib"
The functions contained are: There are a number of limitations in the use of the ASMLIB library:
  1. The additional instructions are accepted only in lower case.
  2. There must be only one instruction per line.
  3. Labels must be placed on separate lines.
  4. Assembler code using the extra instructions cannot be compiled unless all the Crunch options are disabled. As this usually isn't acceptable, you should place the assembler source code in a separate file which you execute using CALL, and give the file an extension other than .BBC to prevent crunching, even if the Crunch embedded program files option is enabled.

FN_asmext

FN_asmext enables the assembler extensions. It should be used in the line immediately preceding the opening bracket ([) of your assembler code, in the following context:
ON ERROR LOCAL [OPT FN_asmext : ]
[OPT pass%
Assembler code starts here...
You should add a RESTORE ERROR statement after the closing bracket (]) of your assembler code, unless that is shortly followed by an ENDPROC or end-of-function (which automatically restore the ERROR status).

FN_cpuid(level%, cpuid{})

FN_cpuid allows you to test whether the processor supports the additional instructions; you should always perform such a test berfore using them. FN_cpuid takes two parameters: a level value specifying what information is required and a structure cpuid{} in which the requested information is returned (the structure should not be initialised prior to calling the function). The function returns FALSE if the CPU does not support the CPUID instruction and TRUE otherwise.

The returned structure contains the members A%, B%, C% and D% corresponding to the values returned in the eax, ebx, ecx and edx registers by the CPUID instruction. To test for the availability of the CMOV (conditional move) instructions use the following code:

IF FN_cpuid(1, cpuid{}) IF cpuid.D% AND &8000 THEN
  REM CMOV instructions available
ELSE
  REM CMOV instructions not available
ENDIF
To test for the availability of the SSE (Streaming SIMD Extension) instructions use the following code:
IF FN_cpuid(1, cpuid{}) IF cpuid.D% AND &2000000 THEN
  REM SSE instructions available
ELSE
  REM SSE instructions not available
ENDIF
To discover the Vendor ID string use code similar to the following:
IF FN_cpuid(0, cpuid{}) THEN
  PRINT "Vendor ID string: " LEFT$($$cpuid{},12)
ELSE
  PRINT "Vendor ID string not available"
ENDIF

More assembler extensions

The ASMLIB2 library further extends the 32-bit x86 assembler, supporting in particular the SSE2 (Streaming SIMD Extension 2) instructions available on the Pentium 4 and later processors. The library should be loaded from your program as follows:
INSTALL @lib$+"asmlib2"
ASMLIB2 is a superset of ASMLIB. It supports all the assembler extensions provided by ASMLIB and contains compatible functions. It is never necessary to INSTALL both ASMLIB and ASMLIB2.

To test for the availability of the SSE2 instructions use the following code:

IF FN_cpuid(1, cpuid{}) IF cpuid.D% AND &4000000 THEN
  REM SSE2 instructions available
ELSE
  REM SSE2 instructions not available
ENDIF

2D image plotting, scaling and rotating

The imglib library provides facilities for loading and plotting (optionally scaled, flipped and/or rotated) 2D images. It is compatible with the equivalent BBC BASIC for Windows library but is hardware (GPU) accelerated so typically is very much faster.

The library should be loaded from your program as follows:

INSTALL @lib$+"imglib"
The functions contained are:

PROC_imgInit

This initialises the library. It should normally be called just once during the initialisation phase of your program. If you are using the imglib library in conjunction with the multiwin library, images may be plotted in only one of the windows. In that case you should ensure that you call PROCimgInit, and all the other functions in the library, only when that window has been selected.

PROC_imgExit

This closes down the library, and (importantly) frees the memory occupied by all the images that were loaded since the last call to PROC_imgInit. It should normally be called during the shutdown phase of your program (you may need to use ON CLOSE and ON ERROR statements to ensure that it is called however the program terminates). Alternatively if you are loading large numbers of images and you want to conserve memory, you can call PROC_imgExit followed by another PROC_imgInit at any time to free up resources.

FN_imgLoad

FN_imgLoad() loads an image from a file, as follows:
image%% = FN_imgLoad(image$)
The image$ parameter should contain the path and filename of the image to be loaded, which may be in BMP, GIF, JPG or PNG format (a few less common formats are also supported, such as PSD, PPM and TGA). If the format supports an alpha channel (i.e. transparency) the this will be taken into account when the image is plotted. The value returned is used to refer to the image subsequently, it must be stored in a 64-bit (%% suffix) or variant (no suffix) variable. If the value returned is zero the file could not be opened, so this must always be tested.

FN_imgLoadAnimatedGIF

FN_imgLoadAnimatedGIF() loads a file containing an animated GIF, as follows:
image%% = FN_imgLoadAnimatedGIF(anigif$)
The anigif$ parameter should contain the path and filename of the animated GIF file; transparency, if any, will be taken into account when the image is plotted. The value returned is used to refer to the animation subsequently, it must be stored in a 64-bit (%% suffix) or variant (no suffix) variable; if zero the file could not be opened, so this must always be tested.

FN_imgFrame

FN_imgFrame() determines which frame in an Animated GIF will be displayed by a subsequent PROC_imgPlot, as follows:
delay% = FN_imgFrame(image%%, frame%)
The image%% parameter should contain the value returned by FN_imgLoadAnimatedGIF and the frame% parameter the frame number (starting at zero for the first frame). The function returns the period of time, in centiseconds (1/100 second) for which that frame should be displayed. If zero is returned the requested frame does not exist, i.e. the frame number is greater than or equal to the number of frames in the animation.

PROC_imgMult

PROC_imgMult() (optionally) modifies the image by attenuating the colour and/or alpha (opacity) components by specified amounts, as follows:
PROC_imgMult(image%%, red, green, blue, alpha)
The image%% parameter is the value returned from a previous call to FN_imgLoad or FN_imgLoadAnimatedGIF; it must not be zero. The red, green, blue and alpha parameters are multiplication factors, in the range 0.0 to 1.0, which determine by how much the different components should be attenuated. For example if you want to plot a shadow you might set the red, green and blue factors to zero to suppress the colour altogether, and the alpha factor to (say) 0.5 to set the degree to which what is 'behind' the image is darkened by the shadow.

PROC_imgPlot

PROC_imgPlot() plots an image, with optional scaling, flipping and/or rotation, as follows:
PROC_imgPlot(image%%, xpos, ypos, xscale, yscale, angle)
The image%% parameter is the value returned from a previous call to FN_imgLoad or FN_imgLoadAnimatedGIF; it must not be zero. The xpos and ypos parameters specify the coordinates, in BBC BASIC graphics units, of the centre of the image; they need not be integers. The xscale and yscale parameters specify by how much to scale the image in the horizontal and vertical directions respectively; the image may be reduced or increased in size. To avoid image distortion you are recommended to set xscale and yscale to the same value. If xscale or yscale (or both) are negative the image is flipped (mirrored) in that direction. The angle parameter specifies by what angle, if any, the image should be rotated around its centre; the units are degrees in a clockwise direrction.

PROC_imgSize

PROC_imgSize() returns the original dimensions of the image, in pixels:
PROC_imgSize(image%%, width%, height%)
The image%% parameter is the value returned from a previous call to FN_imgLoad or FN_imgLoadAnimatedGIF; it must not be zero.

UTF-8 string functions

Although BBC BASIC for SDL 2.0 supports UTF-8 (Unicode) text, when enabled using the VDU 23,22 command, the built-in string functions still assume that text strings consist of 8-bit characters (i.e. bytes) rather than UTF-8 characters (which may each be from 1 to 3 bytes long). The UTF8LIB library provides a set of replacement functions which count characters rather than bytes. The library should be loaded from your program as follows:
INSTALL @lib$+"utf8lib"
The functions contained are: The usage of the functions is identical to their standard equivalents, except that if any of the parameters is optional in the standard version it is not optional in the UTF-8 version.

Serialised synchronous events

The EVENTLIB library supports delivering ON MOUSE, ON MOVE, ON SYS and ON TIME events to (potentially) multiple handler routines in a serialised, synchronous, fashion. This can resolve issues such as re-entrant calls to event handlers (which may result in them being executed 'out of sequence') and dispatching events to multiple modules that may need them.

The library should be loaded from your program using the statement:

INSTALL @lib$+"eventlib"

The functions contained are:

PROC_eventinit

PROC_eventinit initialises the library and sets up a 32-event FIFO (First In First Out) queue. Importantly, it can be called multiple times so in a modular program each section of code needing to receive event notifications can call it independently. All the ON... events except ON CLOSE are processed.

PROC_eventregister(WM_xxxx, PROChandler())

PROC_eventregister registers a procedure to be called when the specified event occurs; the event is identified by its @msg% value and should be specified as the Windows Constant which refers to the specific event (e.g. WM_TIMER). PROC_eventregister may be called multiple times with the same event identifier, in which case all the specified handlers will be called sequentially. The handler needs to be defined to receive three parameters as follows:
DEF PROChandler(msg%, wparam%, lparam%) 
If you need to be informed that the event queue overflowed, you can register a handler for that by specifying an event ID of zero:
PROC_eventregister(0, PROCoverflow())

PROC_eventpoll

PROC_eventpoll polls the queue to see if any events are pending, and if so calls their registered handlers (if any) in the sequence in which the events occurred. If multiple handlers are registered for an event, they are all called (the last one registered is called first). Typically a program will wait in a polling loop such as:
REPEAT
  PROC_eventpoll
  WAIT 0
UNTIL FALSE 

Multi-line text editor control

The filedlg library extends the dlglib library by providing a multi-line text editor control. It is used in exactly the same way as the other dialogue box controls. The libraries should be loaded from your program as follows:
INSTALL @lib$+"dlglib"
INSTALL @lib$+"editbox"

The functions contained include:

PROC_editbox

Creates a multi-line text editor control.
PROC_editbox(dlg%, "", id%, x%, y%, cx%, cy%, style%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog. The string parameter is ignored, the x% and y% parameters specify the position of the edit box within the dialogue box and the cx% and cy% parameters specify the size of the edit box (in dialogue box units). The id% parameter is a unique identifier for the edit box.

The style% parameter can be zero; adding WS_HSCROLL (&100000) includes a horizontal scroll bar and adding WS_VSCROLL (&200000) includes a vertical scroll bar. Adding WS_BORDER (&800000) disables the border normally draw around the control, adding ES_READONLY (&800) disables the ability for the user to edit the contents. Adding WS_DISABLED (&8000000) to the style value causes the edit box to be initially disabled (greyed-out); adding WS_VISIBLE (&10000000) causes it to be (initially) invisible.

PROC_seteditboxarray

You must set a string array to contain the text displayed in the edit box, with enough elements to accommodate the longest document you will ever want to display or edit; this array may be empty or include text that you wish to be initially presented for editing:
PROC_seteditboxarray(dlg%, id%, array$(), lines%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box. The lines% parameter indicates the current number of lines of text, starting at index zero, so the text, if any, should be in array$(0) to array$(lines%-1).

PROC_seteditboxchanged

You can set or clear the 'changed' flag as follows; normally you will want to clear it when the contents of the edit box have been saved:
PROC_seteditboxchanged(dlg%, id%, changed%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box. The changed% parameter should be set to FALSE or TRUE.

PROC_seteditboxselect

You can set the selection (highlighted region) or position the caret (text cursor) as follows:
PROC_seteditboxselect(dlg%, id%, cx%, cy%, ax%, ay%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box. The cx% and cy% parameters set the horizontal and vertical caret (text cursor) coordinates, zero-based. The ax% and ay% parameters set the horizontal and vertical anchor coordinates, zero-based. If the caret and anchor coordinates are different, the region between them is selected (highlighted).

PROC_seteditboxscroll

You can set the horizontal and vertical scroll positions as follows:
PROC_seteditboxscroll(dlg%, id%, hscroll%, vscroll%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box. The hscroll% parameter sets the horizontal scroll position and the vscroll% parameter sets the vertical scroll position, both in pixels.

PROC_editboxedit

You can perform an editing operation (exactly as the user can do from the keyboard) as follows:
PROC_editboxedit(dlg%, id%, keycode%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box. The keycode% parameter is the code of the keyboard shortcut which performs the wanted editing operation (e.g. 3 corresponding to Ctrl+C for copy, 22 corresponding to Ctrl+V for paste etc.).

FN_geteditboxlinecount

You can discover the current number of lines in the document being edited as follows:
nlines% = FN_geteditboxlinecount(dlg%, id%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box.

FN_iseditboxchanged

You can determine whether the document has been changed (edited by the user) as follows:
changed% = FN_iseditboxchanged(dlg%, id%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box. The function will return TRUE if editing has taken place, and FALSE if not.

PROC_geteditboxselect

You can discover the caret (text cursor) position and selected region (if any) as follows:
PROC_geteditboxselect(dlg%, id%, cx%, cy%, ax%, ay%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box. The current caret position will be returned in cx% and cy%, and the current anchor position in ax% and ay%. If the caret and anchor positions are the same, no text is selected.

PROC_geteditboxscroll

You can discover the horizontal and vertical scroll positions, in pixels, as follows:
PROC_geteditboxscroll(dlg%, id%, hscroll%, vscroll%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box. The horizontal and vertical scroll positions are returned in hscroll% and vscroll% respectively.

FN_editboxcanundo

You can discover whether an undo operation is currently possible as follows:
canundo% = FN_editboxcanundo(dlg%, id%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box.

FN_editboxcanredo

You can discover whether a redo operation is currently possible as follows:
canredo% = FN_editboxcanredo(dlg%, id%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box.

FN_getselectedtext

You can read the currently-selected text (if any) as follows:
selectedtext$ = FN_getselectedtext(dlg%, id%)
The dlg% parameter identifies the dialogue box, and is the value returned from FN_newdialog; the id% parameter identifies the edit box.

File selector dialogue

The filedlg library provides a simple File Selector dialogue which allows the user to navigate the file system to choose a file or directory. The library requires the dlglib, sortlib and stringlib libraries to be installed, so should be loaded from your program as follows:
INSTALL @lib$+"dlglib"
INSTALL @lib$+"sortlib"
INSTALL @lib$+"stringlib"
INSTALL @lib$+"filedlg"

The functions contained are:

FN_filedlg and FN_filedlgex

These functions open a File Selector dialogue box and allow the user to navigate the file system and make a selection. They differ only in that FN_filedlgex() allows the dialogue box to be positioned, and for the 'dark mode' to be enabled. You may wish to select a specific font before calling these functions.
selected$ = FN_filedlg(title$, okverb$, inidir$, filetype$, filter$, flag%)
selected$ = FN_filedlgex(title$, okverb$, inidir$, filetype$, filter$, flag%, dark%, xpos%, ypos%)
The parameters are as follows:

title$The string to appear in the title bar.
okverb$The caption to be displayed in the button which confirms file or directory selection (e.g. "Open", "Save" or "Select").
inidir$The directory to be initially displayed. Set to "" to remember the directory selected in the previous call.
filetype$The human-readable description of file type e.g. "BASIC files". This must be quite short to fit in the space available.
filter$One or more file extensions (including the dot), separated by semicolons, e.g. ".bbc;.bas".
Set to "." for a 'Browse for Folder' functionality (only directories will be listed).
flag%Set to 0 to disable the 'All files' button, set to 1 to enable it.
dark%Set to TRUE to enable 'dark mode', set to FALSE for the normal colour scheme (the colour palette is modified).
xpos% &
ypos%
Set the position of the file selector, in dialogue box units (which are determined by the current font).
Set both to &FFFFFFFF80000000 to centre the dialogue in the window.

The string value returned is the selected file or directory, or an empty string if the selection was cancelled.

Drop-down and popup menus

The menulib library supports a menu bar, drop-down menus and popup (context) menus. It should be loaded from your program as follows:
INSTALL @lib$+"menulib"

The functions contained are:

PROC_setmenupalette

This procedure programs the palette with the standard colours used by menus:
PROC_setmenupalette(dark%)
The dark% parameter should be set to TRUE if you want the menus to use a 'dark' style, and to FALSE if you want them to use the normal 'light' style.

FN_createmenubar

This function creates (but does not display) a menu bar and returns an integer value that refers to that menu bar:
menubar% = FN_createmenubar

PROC_addmenu

This procedure adds a drop-down menu to the menu bar, up to a maximum of 20. The order in which you call PROC_addmenu() determines the order in which the menus appear on the bar, left-to-right:
PROC_addmenu(menubar%, menu$(), menu%())
The menubar% parameter identifies the menu bar to which the drop-down menu will be added, it is the value returned by FN_createmenubar. The two array parameters menu$() and menu%() define the items in the menu. In each case the first element (index 0) contains information about the menu, and the subsequent elements (indices 1 upwards) contain information about the individual selections within the menu.

menu$(0) contains the name of the menu, which is displayed in the menu bar. This should normally be preceded and followed by at least one space, to act as a horizontal separator. To specify which character is used with the Alt key to open the menu, precede it with an ampersand (&). menu%(0) contains the number of items in the menu (these may be actual selections or separators).

The remaining elements in the arrays define the individual menu items. menu$(N%) contains the name of the selection, or an empty string in the case of a separator. It may optionally contain a Tab (CHR$9) character to separate the name from a keyboard shortcut description. To specify which character in the name is used with the Alt key to activate the selection, precede it with an ampersand (&). menu%(N%) contains a numeric ID of the menu selection (less than 4096), or zero in the case of a separator. Adding &1000 to this number causes the selection to be disabled (greyed out); adding &2000 causes a checkmark (tick) to appear to the left of the item.

A typical menu definition is as follows:

DIM filemenu$(6), filemenu%(6)
filemenu%() = 6, 20, 21, &1000+22, 23, 0, 24
filemenu$() = "  &File  ", \
\             "&New"+CHR$9+"Ctrl+N", \
\             "&Open"+CHR$9+"Ctrl+O", \
\             "&Save"+CHR$9+"Ctrl+S", \
\             "Save &As", \
\             "", \
\             "E&xit"
PROC_addmenu(menubar%, filemenu$(), filemenu%())

PROC_drawmenubar

This procedure draws the menu bar at the top of the screen, containing the menus you have added. You must ensure that the correct font and colour palette are selected:
PROC_setmenupalette(dark%)
PROC_drawmenubar(menubar%)
The menubar% parameter identifies the menu bar to be drawn, it is the value returned by FN_createmenubar.

FN_pollmenu

This function must be called periodically (preferably at least 20 times per second, e.g. from a timer interrupt or from your 'main loop') in order for the menus to respond to mouse and keyboard activity. You must ensure that the correct colour palette and font are selected, alternatively you may select the font in the supplied callback routine:
selection% = FN_pollmenu(menubar%, ^PROCcallback())
The menubar% parameter identifies the menu bar which will be polled, it is the value returned by FN_createmenubar. The function returns zero if no menu selection has been made, or the ID number of the menu item if a selection has been made.

The second parameter may be zero, or the address of a callback routine that will be called in order to select the required font (note the use of the circumflex 'address-of' operator). The callback routine should be defined similar to this:

DEF PROCcallback(N%)
CASE N% OF
  WHEN 0: OSCLI "FONT " + MenuFont$
  WHEN -1: OSCLI "FONT " + NormalFont$
ENDCASE
ENDPROC

FN_openmenu

This function is used to open a popup (context) menu, independently of the menu bar; typically such a menu is displayed when you right-click using the mouse or press F10 (more commonly Shift+F10 in Windows). You must ensure that the correct colour palette and font are selected, alternatively you may select the font in the supplied callback routine:
selection% = FN_openmenu(xpos%, ypos%, menu$(), menu%(), ^PROCcallback())
The xpos% and ypos% parameters determine where the menu is displayed, specified as the top left-hand corner of the menu in BBC BASIC graphics coordinates. The menu$() and menu%() parameters define the contents of the menu, exactly as in PROC_addmenu; the menu name in menu$(0) is not displayed but it should still be set. The final parameter specifies an optional callback routine to select the required font (see FN_pollmenu); if this is not required, set the parameter to zero.

The function returns zero if the menu is dismissed without a selection being made, or the ID number of the menu item if a selection has been made.

MODE 7 alternative character sets

The mode7lib library supports multiple character sets in MODE 7. It should be loaded from your program as follows:
INSTALL @lib$+"mode7lib"

The functions contained are:

PROC_saa5050

Select the (default) English character set:
PROC_saa5050(set%)
The set% parameter should be set to 0 to select English as the primary character set and to 1 to select English as the secondary character set. To use the secondary character set you must enable it using VDU 23,18,3,1| after which VDU 155 toggles between primary and secondary sets (each row starts in the primary character set).

PROC_saa5051

As PROC_saa5050 except that the German character set is selected.

PROC_saa5052

As PROC_saa5050 except that the Swedish character set is selected.

PROC_saa5053

As PROC_saa5050 except that the Italian character set is selected.

PROC_saa5054

As PROC_saa5050 except that the Belgian character set is selected.

PROC_saa5055

As PROC_saa5050 except that the US ASCII character set is selected.

PROC_saa5056

As PROC_saa5050 except that the Hebrew character set is selected.

PROC_saa5057

As PROC_saa5050 except that the Cyrillic (Russian) character set is selected.

PROC_saa5059

As PROC_saa5050 except that the Greek character set is selected.

PROC_saa505x

Select a MODE 7 character set according to the supplied parameter:
PROC_saa505x(set%, language%)
The set% parameter acts as it does in PROC_saa5050. The language% parameter selects the desired language: 0 for English, 1 for German, 2 for Swedish, 3 for Italian, 4 for Belgian, 5 for US ASCII, 6 for Hebrew, 7 for Russian or 9 for Greek.

High performance 2D graphics for games

The gfxlib library contains a set of procedures and functions for plotting 2D graphics, which are optimised for use in high-performance video games. It emulates, partially, the functionality of the GFXLIB and GFXLIB2 libraries for BBC BASIC for Windows.

This library is documented separately here.

Left CONTENTS

CONTINUE Right


Best viewed with Any Browser Valid HTML 3.2!
© Richard Russell 2021