www.riscos.com Technical Support: |
|
The program environment refers to the conditions under which a program or module executes. There are three aspects to this environment.
A handler is a piece of code called when certain conditions occur. RISC OS provides a set of default handlers, so that a sensible default action will occur under such conditions. Here is a brief list of the kinds of conditions that we are talking about:
There are several ways of executing a piece of code. You can:
The first two are described in the chapter entitled Modules. They are really the same thing. When a file is *RMRun, it is loaded into the relocatable module area. Its initialisation code is called, so that it can claim workspace etc, then its start code is called.
A module can also cause its own start entry point to be called if it wants to become the current application, using OS_Module. BASIC is an example of this. The *BASIC command is recognised by the OS using the BASIC module's * Command table. The OS calls the routine which handles the *BASIC command, and this routine calls OS_Module with the reason code 'enter'. For details on calling modules see the chapter entitled Modules.
The third case applies to files which have no file type, or have type &FF8. In the first case, the file is loaded at its load address, then it is started as an application through its execution address. If the file type is &FF8, the file is loaded at &8000 and started as an application there. See also the Transient programs below.
Finally, if you call a machine code program using the *Go command, it becomes the current application. (This implies that you shouldn't use *Go to call RAM-based routines from a language, as the routine can't return - R14 contains no return address at this point.)
In all of these cases, the program is called in user mode, with interrupts enabled. Where a module is called, R12 points to the module's private word.
A file with type &FFC (Utility) must contain position independent code. When such a file is *Run, it is loaded into the RMA and executed. This is used when you want to run a utility and then return to the program environment that you were in before running it. On entry to a transient program, registers are as follows:
R0 = pointer to command line
R1 = pointer to command tail
R12 = pointer to workspace
R13 = pointer to workspace end (stack)
R14 = return address
User mode, interrupts enabled
The workspace is 1024 bytes long, in the location given by R12 and R13 on entry. If more is required, it may be allocated from the RMA. The utility should return using MOV PC,R14 (freeing any extra workspace first). It does not become the current application and must not call OS_Exit; see the chapter entitled Ending a task below.
Note that R0 points to the first character of the command name, and R1 points to the first character of the command tail (with spaces skipped). This will be a control character if there were no parameters.
When a utility returns, the space it occupies is freed. Utilities are nestable - you can execute one utility from within another.
Note that utilities are viewed as system extensions. This means that they must only use the X form SWIs, so that the error handler is not called by their actions. A utility can return with an error by setting V and pointing R0 at an error block as usual.
Before describing the calls which control the application program's environment, it is worth explaining how to leave an application. In general, a simple 'return from subroutine' using MOV PC,R14 won't suffice. Instead, you should use a routine called OS_Exit. This passes control back to a well-defined place, which defaults to the supervisor * prompt, but could equally be a location in the previous application.
*Quit is equivalent to a call to OS_Exit.
OS_ExitAndDie is like OS_Exit, but will kill a named module as well. This may be used, for example, when a module is specific to a particular application and you wish to kill the module when the application exits.
The system variables, maintained by the operating system in the system heap, provide a convenient way by which programs can communicate. Variables are accessed by their textual name. The name may contain any non-space, non-control character. When a variable is created, the case of the letters is preserved. However, when names are looked up the case is ignored, and you can use the characters '#' and '*' - just like looking up filenames.
You should avoid the use of wholly numeric names for system variables, such as 123, as this causes difficulties when the GS string operations are used to look up a variable's contents. In particular, they will always take <123> to mean the ASCII code 123, and will not attempt to look up the name as a variable. See the chapter entitled Conversions for details of the GS calls, specifically OS_GSRead and OS_GSTrans.
There are several types of system variable:
A classic example of using a macro is to set the command line prompt CLI$Prompt to the current time using:
*SetMacro CLI$prompt <Sys$Time><&20>
Every time the prompt is displayed, it shows the current time, followed by a space.
All the above types can be set with OS_SetVarVal and read with OS_ReadVarVal.
Any non-code variable can be removed using *Unset. *Show will list the setting of one or more variables.
OS_GetEnv is a multi-purpose SWI that provides three useful pieces of information:
This can be processed with OS_ReadArgs, which is described on OS_ReadArgs of the Conversions.
This can be altered with reason code 0 of OS_ChangeEnvironment; see OS_ChangeEnvironment for more details.
OS_WriteEnv allows you to set the program start time and the command string.
Handlers are short routines used to cope with special conditions that can occur under RISC OS. Here is a complete list of the handlers:
Handler |
---|
Undefined instruction |
Prefetch abort |
Data abort |
Address exception |
Error |
CallBack |
BreakPoint |
Escape |
Event |
Exit |
Unused SWI |
UpCall |
All of the calls that install user handlers pass through ChangeEnvironmentV. This can be intercepted to stop a subprogram changing parts of the environment that its parent wants to keep: for example, a debugger.
Before reading this section, you should be familiar with the chapters entitled Software vectors and Hardware vectors, since many of these handlers are directly called from these vectors.
OS_ChangeEnvironment is the central SWI for handlers. There are several other routines that perform subsets of its actions. You are strongly recommended to use OS_ChangeEnvironment in any new applications as the others are only provided for compatibility.
The other calls are OS_Control, OS_SetEnv, OS_CallBack, OS_BreakCtrl and OS_UnusedSWI.
OS_ReadDefaultHandler allows you to get the address and details of any of the default handlers. This would be used if you wished to set up a well-defined state before running a subprogram: for example, the Desktop does so.
When a handler is called, you should not expect to be able to see the foreground application's registers. You should only rely on those registers explicitly defined in each handler as being meaningful on entry.
You should take care not to corrupt R14_SVC during handler code. This implies saving it on the stack if you use SWIs; see the chapter entitled Interrupts and handling them for details. The details of each of the handlers follows:
These handlers are all called from hardware vectors. For a description of them see the chapter entitled Hardware vectors. These handlers are all entered with the processor in SVC mode.
All of the default handlers simply generate errors, which are passed to the current error handler.
The error handler is called after any error has been generated. It is called by the default routine on the error vector; thus any routines using this vector should always 'pass it on'. Continuing after an error is not generally recommended. You should always use the X form SWIs if you wish to stay in control even when an error occurs.
The error handler is entered in User mode with interrupts enabled. Note that if the error handler is set up using OS_ChangeEnvironment, the workspace pointer is passed in R0, not R12 as is usual for other handlers.
The error handler must provide an error buffer of size 256 bytes, the address of which should be set along with the handler address. On an error the buffer will be set to contain the following:
Offset | Contents |
---|---|
0 - 3 | PC when error occurred |
4 - 7 | Error number provided with the error. |
8... | Error string, terminated with a 0 |
The default error handler reports the error message and number - although applications frequently set up their own error handlers. BASIC is one such example.
This handler is called when the SWI OS_BreakPt is called. All the user mode registers are dumped into a buffer (the register save block), and then the handler is entered in SVC mode.
When setting the address of a replacement break point handler you must also specify the address of the register save block, which must be word aligned and 16 words long. You can also specify a pointer to workspace to pass in R12 when your handler is called.
The following code is suitable to restore the user registers and return:
ADR R14, saveblock ; get address of saved registers LDMIA R14, {R0-R14}^ ; load user registers from block; ; note that user R13,R14 are altered MOV R0, R0 ; no-op after forcing User mode LDR R14, [R14,#15*4] ; load user PC into SVC R14 MOVS PC, R14 ; return to correct address and mode
The default handler displays the message 'Break point at &xxxxx' and calls OS_Exit.
This handler is called when an escape condition is detected. See the chapter entitled Character Input for details of this. You can specify a pointer to workspace to pass in R12 when this handler is called.
When the handler is entered, registers have the following values:
R11 | bit 6 set, implying escape condition |
R12 | pointer to workspace, if set up - should never be 1 |
R13 | a full, descending stack pointer |
To continue after an escape, the handler should reload the PC with the contents of R14. If R12 contains 1 on return then the CallBack flag is set; for details of the action this causes, see the chapter entitled CallBack. Typically (eg for BASIC), the handler will set an internal flag which is checked by the foreground program.
This handler is called by the default owner of EventV when an event occurs. You can specify a pointer to workspace to pass in R12 when this handler is called.
When the handler is entered the processor is in either SVC or IRQ mode, with the following register values:
R0 | event reason code |
R1... | parameters according to event code |
R12 | pointer to workspace, if set up - should never be 1 |
R13 | a full, descending stack pointer |
To continue after an event, the handler should reload the PC with the contents of R14. Again, if R12 contains 1 on return then the CallBack flag is set; for details of the action this causes, see the chapter entitled CallBack.
This handler is called when the SWIs OS_Exit or OS_ExitAndDie are called. It is entered with the processor in user mode. You can specify a pointer to workspace to pass in R12 when this handler is called.
This handler is called by the default owner of the UKSWIV. (When a SWI is called, RISC OS first checks if it is a kernel SWI; it then checks if it is a module SWI by looking at its hash table constructed from the headers of initialised modules. It then calls UKSWIV; this allows a user routine on that vector to try to deal with the SWI. If there is no such routine, or the one(s) that is present passes the call on, then the default owner of the vector - which is the kernel - calls the Unused SWI handler).
The default handler returns the error 'SWI &xxxxxxxx not known', or just 'SWI not known' if the SWI was called from an IRQ process.
You can specify a pointer to workspace to pass in R12 when this handler is called.
When the handler is entered the processor is in SVC mode, with interrupts in the same state as the caller. The registers have the following values:
R11 | SWI number (Bit 17 clear) |
R13 | SVC stack pointer |
R14 | user PC with V cleared |
R10, R11 and R12 are stacked and are free for your own use.
This handler is called by the default owner of UpCallV when OS_UpCall is called. OS_UpCall is used to warn your program of errors and situations that you may be able to recover from. See the chapters entitled Software vectors and Communications within RISC OS. You can specify a pointer to workspace to pass in R12 when this handler is called.
This handler is called whenever RISC OS's internal CallBack flag is set, and the system next exits to User mode with interrupts enabled. It uses a register save block (the address of which should be set along with the handler address) in which all the registers are dumped when the handler is called. This must be word-aligned and 16 words long. You can specify a pointer to workspace to pass in R12 when this handler is called. A more detailed description follows.
There are two types of CallBack usage under RISC OS:
Transient CallBacks may be called on the system being threaded out of - that is, when it enters User mode with interrupts enabled. They can also be called when RISC OS is idling; for example, while it is waiting in OS_ReadC.
Transient CallBacks are usually set up by an interrupt routine that needs to do complex processing that would take too long in an interrupt, or that needs to call a non-re-entrant SWI. OS_AddCallBack tells RISC OS that the interrupt routine wishes to be 'called back' when the machine is in a state that no longer imposes the restrictions associated with an interrupt routine. OS_RemoveCallBack removes a transient CallBack; this is most useful if the module is being killed before the transient CallBack has been serviced.
Transient CallBacks can safely be used by many clients.
You must not rely on any relationship between the order in which Transient CallBacks are added and the order in which they are called.
Transient CallBacks are not calle between successive lines of an Obey file, nor when the screen scrolling is disabled by the Scroll Lock or Ctrl-Shift keys.
The CallBack handler is only ever called on the system being threaded out of - that is, when it enters User mode with interrupts enabled. Unlike transient CallBacks, it is not called when RISC OS is idle. This means that you cannot rely on being called back within any given time. You must take this into consideration before using a CallBack handler.
Also, you must not allow a second CallBack before your first one has completed; see the chapter entitled Application Notes for an example of how to implement a semaphore to prevent this.
The CallBack code is called in IRQ or supervisor mode with interrupts disabled. The PC stored in the save block will be a user mode PC with interrupts enabled. Note that if the currently active program has interrupts disabled or is running in supervisor mode, CallBack is not used.
In the simple case the CallBack routine should be exited by:
ADR R14, saveblock ; get address of saved registers LDMIA R14, {R0-R14}^ ; load user registers from block - ; note that user R13,R14 are altered MOV R0, R0 ; no-op after forcing User mode LDR R14, [R14,#15*4] ; load user PC into SVC R14 MOVS PC, R14 ; return to correct address and mode
In RISC OS 3 (version 3.10) or later, the supervisor stack must also be empty when the CallBack handler is called. This ensures that certain module SWIs that temporarily enter User mode (so that transient CallBacks are called) do not cause the CallBack handler to be called.
This is a pointer to the address of: the last application started, or the last error handler called, or the last exit handler called. It is used by OS_Module to determine whether a module can be killed.
In order to deal correctly with the various ways in which applications can be run, and killed off, the following approach has been developed for setting up the program environment when an application starts, and restoring it when it is killed. The basic problems are:
Typically these are handled for you by run-time language libraries, and so if you are writing in a high-level language you do not need to worry.
However, if you are yourself writing a run-time language library, or if you are writing your application in machine code (for example as a module which runs as a Wimp task) you must take one of these two possible approaches:
When you start an application, you must:
Note that you must store the previous values not only for Exit, Error and UpCall handlers, but also for any other handlers that are set up.
If your error handler is called and you want to call the 'external' error handler (eg BASIC if '-quit' was on the command line), you should:
If your exit handler is called you should:
If your UpCall handler is called and R0 = UpCall_NewApplication (256), you should:
The approach described above ensures that it is not possible for your application to be terminated without it first restoring all handlers to their original values.
Read/write handler addresses
R0 = pointer to error handler, or 0 to read
R1 = pointer to error buffer, or 0 to read
R2 = pointer to escape handler, or 0 to read
R3 = pointer to event handler, or 0 to read
R0 = pointer to previous error handler
R1 = pointer to previous error buffer
R2 = pointer to previous escape handler
R3 = pointer to previous event handler
Interrupts are not enabled
Fast interrupts are enabled
Processor is in IRQ or SVC mode
SWI cannot be re-entered as interrupts are disabled
OS_Control sets some of the exception handlers. The addresses of the error handler, error handler buffer, escape handler and event handler are passed in R0 - R3. Zero for any of these means no change - hence you can read the current value. The error buffer must be 256 bytes long.
Note that the call OS_ChangeEnvironment provides all of the facilities that this call provides, and should be used in preference. In fact, this call uses OS_ChangeEnvironment.
ChangeEnvironmentV
Read environment parameters
--
R0 = pointer to environment string
R1 = permitted RAM limit (ie highest address available + 1)
R2 = pointer to real time the program was started (5 bytes)
Interrupt status is unaltered
Fast interrupt status is unaltered
Processor is in SVC mode
SWI is re-entrant
This SWI reads some information about the program environment.
The environment string pointed to by R0 is normally a copy of the command line used to start the program. However, there may be some circumstances where a command line was not used to start the program; in such cases a meaningful string is still passed. For example, if a module is started using OS_Module 2, the string passed will be '<module title> <parameter string passed in R2 on entry to OS_Module>'.
R1 returns the address of the byte above the last one available to the application. You can alter this using reason code 0 of OS_ChangeEnvironment.
The five bytes pointed to by R2 give the real time the program was started: ie centiseconds since 00:00:00 01-Jan-1900.
You can set these values using OS_WriteEnv.
None
R0 = pointer to error buffer
R1 = 'ABEX' (&58454241) if return code is to be set
R2 = return code
Never returns
Interrupt status is unaltered
Fast interrupt status is unaltered
Processor is in USR mode
SWI is not re-entrant
When OS_Exit is called, control returns to the most recent exit handler. The BASIC statement QUIT performs an OS_Exit. Before executing OS_Exit, however, you must restore any of the handlers changed in starting the application.
If the exiting program wishes to return with a return code, it must set R1 to the hex value shown above, and R2 to the desired value. The value should be zero to indicate no error; otherwise the value should indicate the severity of the error, so 1, for example might indicate a trivial error or warning. The return value is assigned to the variable Sys$ReturnCode, which can be interrogated by any program using OS_ReadVarVal.
If the returned value is greater than the value of the system variable Sys$RCLimit, RISC OS also gives the error 'Return code limit exceeded' (&1E2). The user can alter the value of Sys$RCLimit to control which errors are returned; your application should not itself alter the variable.
None
Set environment parameters
R0 = pointer to exit handler, or 0 to read
R1 = permitted RAM limit (ie highest address available + 1), or 0 to read
R4 = pointer to undefined instruction handler, or 0 to read
R5 = pointer to prefetch abort handler, or 0 to read
R6 = pointer to data abort handler, or 0 to read
R7 = pointer to address exception handler, or 0 to read
R0 = pointer to previous exit handler
R1 = previous permitted RAM limit (ie highest address available + 1)
R4 = pointer to previous undefined instruction handler
R5 = pointer to previous prefetch abort handler
R6 = pointer to previous data abort handler
R7 = pointer to previous address exception handler
Interrupts are disabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant
OS_SetEnv sets several of the handlers for a program.
Note that the call OS_ChangeEnvironment provides all of the facilities that this call provides, and should be used in preference. In fact, this call uses OS_ChangeEnvironment.
ChangeEnvironmentV
R0 = pointer to CallBack register save block, or 0 to read
R1 = pointer to CallBack handler, or 0 to read
R0 = pointer to previous CallBack register save block
R1 = pointer to previous CallBack handler
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
OS_CallBack sets up the address of the CallBack handler and the register save block, zero for either value meaning no change - hence you can read the current value. The register save block must be word-aligned and 16 words long.
Note that the call OS_ChangeEnvironment provides all of the facilities that this call provides, and should be used in preference. In fact, this call uses OS_ChangeEnvironment.
ChangeEnvironmentV
Cause a break point trap to occur and the BreakPoint handler to be entered
--
--
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
Not defined
When OS_BreakPt is executed, all the user mode registers are saved in a block and the BreakPoint handler is called. The saved registers are only guaranteed to be correct for user mode.
The default handler displays the message 'Break point at &xxxxx' and calls OS_Exit.
This SWI would be placed in code by the debugger at required breakpoints.
None
R0 = pointer to BreakPoint register save block, or 0 to read
R1 = pointer to BreakPoint handler, or 0 to read
R0 = pointer to previous BreakPoint register save block
R1 = pointer to previous BreakPoint handler
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
OS_BreakCtrl sets up the address of the BreakPoint handler and the BreakPoint register save block, zero for either value meaning no change - hence you can read the current value. The register save block must be word-aligned and 16 words long.
Note that the call OS_ChangeEnvironment provides all of the facilities that this call provides, and should be used in preference. In fact, this call uses OS_ChangeEnvironment.
ChangeEnvironmentV
R0 = pointer to unused SWI handler; or 0 to read
R0 = pointer to previous unused SWI handler
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is not enabled
OS_UnusedSWI sets up the address of the unused SWI handler, zero meaning no change - hence you can read the current value.
Note that the call OS_ChangeEnvironment provides all of the facilities that this call provides, and should be used in preference. In fact, this call uses OS_ChangeEnvironment.
ChangeEnvironmentV
--
--
Interrupts are disabled
Fast interrupts are enabled
Processor is in SVC mode
SWI cannot be re-entered because interrupts are disabled
OS_SetCallBack sets the CallBack flag and so causes entry to the CallBack handler when the system next exits to user mode code with interrupts enabled (apart, of course, from the exit from this SWI). This SWI may be used if the code linked into the system (via a vector or as a SWI handler, etc) is required to do things on exit from the system.
None
R0 = pointer to variable name, which may be wildcarded (using '*' and '#')
R1 = pointer to buffer to hold variable value
R2 = maximum length of buffer, or bit 31 set to check existence/length of variable
R3 = context pointer (used with wildcarded names), or 0 for first call
R4 = 3 if an expanded string is to be converted on return
R0 preserved; or corrupted if R2 bit 31 was set on entry
R1 preserved
R2 = number of bytes read
R3 = new context pointer (null-terminated)
R4 = variable type
Interrupts are enabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
OS_ReadVarVal reads a variable and returns its value and its type.
Before reading a variable you must check the length of the data that will be returned. To do so, call XOS_ReadVarVal with R2 set to a value less than zero (bit 31 set) on entry. You can also use this to check for the existence of a variable.
When using the call in this manner, you may get an error on exit, which you should ignore. This feature is not available under RISC OS 2; in this case you may assume that the length of the variable will be at most 256 bytes.
For a wildcarded name R3 should be 0 on entry the first time the call is made, and thereafter preserved from the previous call. On exit, R3 points to the name of the variable found. This enables all matches to be found. The XOS_ReadVarVal form of the call should be used if you don't want an error to occur after the last name has been found.
R4, if set to 3 on entry, indicates that a suitable conversion to a string should be performed. String variables are unaltered, numbers are converted to (signed) decimal strings, and macros are OS_GSTrans'd.
If R4 isn't 3 on entry, the un-OS_GSTrans'd version of a macro is returned, and the four-byte binary of a number is returned.
The type of the variable read is returned in R4 as follows:
Value | Type | |
---|---|---|
VarType_String | (0) | String |
VarType_Number | (1) | 4 byte (signed) integer |
VarType_Macro | (2) | Macro |
Returned strings are not terminated, and you should use the length returned in R2 when reading them.
See the chapter entitled Application Notes for an example of reading a variable.
None
R0 = pointer to variable name, which may be wildcarded (* and #) if updating/deleting
R1 = pointer to variable value
R2 = length of value, or negative to delete the variable
R3 = context pointer (used with wildcarded names), or 0 for first call
R4 = variable type
R0 - R2 preserved
R3 = new context pointer (null-terminated)
R4 = variable type created if expression is evaluated
Interrupts are enabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant
OS_SetVarVal either creates, updates or deletes a variable. The variable's name is pointed to by R0, and may be terminated by any character whose ASCII value is 32 or less. It may be wildcarded if the variable is to be updated or deleted (ie if it already exists).
To delete a variable, R2 must be negative on entry; if the variable is a code variable (see below), the variable type in R4 must be set (ie R4 = 16).
When creating or updating a variable, R1 must point to the value to be assigned. The interpretation of this value depends on the type given in R4 as follows:
Value | Type | |
---|---|---|
VarType_String | (0) | Value is a string which will be OS_GSTrans'd immediately |
VarType_Number | (1) | Value is a 4 byte (signed) integer |
VarType_Macro | (2) | Value is a string which will be OS_GSTrans'd each time it is used |
VarType_Expanded | (3) | Value is a string which will be evaluated as an expression using OS_EvaluateExpression, and assigned to a number or string variable, depending on the expression type |
VarType_LiteralString | (4) | Value is a literal string (ie it will not be OS_GSTrans'd) |
VarType_Code | (16) | Special case (see below) |
With the exception of a literal string, all strings must be terminated by a linefeed (ASCII 10) or carriage return (ASCII 13) or null (ASCII 0).
If the call is successful, R3 is updated to point to the new context so allowing the next match of a wildcarded name to be obtained on a subsequent call. R4 returns the type created if an expression was evaluated (ie if R4 was 3 on entry).
When R4 is set to 16 on entry (and R2 0) a code variable may be created. In this case R1 is the pointer to the code fragment associated with the variable, and R2 is the length of the code fragment. This code must be word-aligned and takes the following format:
Offset | Contents |
---|---|
0 | Branch instruction to entry point for write operation |
4 | Entry point for read operation |
8... | Body of code... |
Values are always written to (and read from) code variables as strings. The entry for the write operation is called whenever the variable is to be set, as follows:
R1 = pointer to the value to be used
R2 = length of value
R1, R2, R4, R10 - R12 may be corrupted
The entry for the read operation is called whenever the variable is to be read by a call to OS_ReadVarVal, as follows:
--
R0 = pointer to value
R1 = corrupted
R2 = length of value
Both entries are called in SVC mode, therefore if any SWIs are used, R14 must be saved on the stack so that it does not become corrupted. The SVC stack is used, and no workspace is reserved. You can return errors by setting the V flag as usual.
See the chapter entitled Application Notes for an example of a code variable.
Note that when a function key is input, the appropriate variable Key$n is read using OS_ReadVarVal. Therefore by creating your own code variables with these names, you can cause the reading of a function key to cause a routine to be called instead of just a string being read.
OS_SetVarVal can return the following errors:
None
R0 = handler number
R1 = pointer to new handler, or 0 to read
R2 = value of R12 with which to call the handler, or 0 to read
R3 = pointer to buffer (if appropriate), or 0 to read
R0 preserved
R1 = pointer to previous handler
R2 = previous value of R12 with which to call the handler
R3 = pointer to previous buffer
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
OS_ChangeEnvironment is a single routine which performs the actions of OS_Control, OS_SetEnv, OS_CallBack, OS_BreakCtrl, and OS_UnusedSWI. In fact, all of those routines use this call. In new programs, you should always use this call in preference to the earlier ones.
For full details of the handlers, see the section earlier in this chapter.
On entry, R0 contains a code which determines which particular handler's address is to be set up. The new address is passed in R1. R0 also determines whether R2 and R3 are relevant or not. This is summarised in the table below:
The 'Memory limit' (handler 0) is the permitted RAM limit, as used by OS_GetEnv. The 'Application space' (handler 14) is the amount of read/write memory in application space. Consequently it should always be the case that Application space Memory limit.
'Other exceptions' (handler 5) is for future expansion.
The error buffer (handler 6) must be 256 bytes long.
The register buffers (handlers 7 and 8) must be word-aligned and 16 words long.
Handler 13 sets the address of the area in memory where the registers are dumped when one of the exceptions (1 - 5) occurs, if the default handlers are used. Again, this must be word-aligned and 16 words long.
Note that in order to perform its function, OS_ChangeEnvironment vectors through ChangeEnvironmentV. A routine linked onto this vector can stop the change from happening by setting R1 (and if appropriate R2, R3) to zero and passing the call on; see the chapter entitled Software vectors.
OS_Control, OS_SetEnv, OS_CallBack
OS_BreakCtrl, OS_UnusedSWI
ChangeEnvironmentV
R0 = pointer to environment string
R1 = pointer to real time the program was started (5 bytes)
R0, R1 preserved
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call sets the environment string and start time for an application, as returned by OS_GetEnv. For more details see OS_GetEnv on OS_GetEnv.
This SWI is mainly used by debuggers.
None
Kill a module and pass control to the most recent exit handler
R0 = pointer to error buffer
R1 = 'ABEX' (&58454241) if return code is to be set
R2 = return code
R3 = pointer to module name
never returns
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant
This SWI is like OS_Exit, except that it will kill a module before exiting. R3 points to a string containing the module's name. For more details, see OS_Exit on OS_Exit.
None
R0 = address to call
R1 = value of R12 to be called with
R0 = preserved
R1 = preserved
Interrupts are disabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call places a transient CallBack on a list of tasks who want to be called as soon as RISC OS is not busy. Usually, this will be just before returning from a SWI or while waiting for a key and so on.
This SWI is usually called from an interrupt routine that needs to do complex processing that would take too long in an interrupt, or that needs to call a non-re-entrant SWI. Note that you don't also need to call OS_SetCallBack, which is only needed when using the CallBack handler.
A routine called by this mechanism must preserve all registers and return by
MOV PC, R14
None
R0 = reason code (0 - 16)
R0 preserved
R1 = pointer to default handler
R2 = pointer to workspace
R3 = pointer to buffer
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
Using the same handler number in R0 as those in OS_ChangeEnvironment (see R0 Handler R2 R3), this SWI returns details about the default handler.
Zero in R1, R2 or R3 on exit means that it is not relevant.
None
R0 = address that was to be called
R1 = value of R12 that the routine was to be called with
R0, R1 preserved
Interrupts are disabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call removes a transient CallBack from the list. You should do so if your module has an outstanding CallBack request, but will not be able to service the request when it is granted - for example if the module is being killed.
This call is not available in RISC OS 2, which can cause problems. For example, if a module is being killed and it has outstanding CallBack requests, it must refuse to die, otherwise the CallBack may be granted after that memory has been reused for something else.
None
hexadecimal_address | address of machine code to call |
environment | environment string to pass to machine code |
*Go calls machine code at the given address, passing it an optional environment string. If the address is omitted, it defaults to &8000, which is where application programs (such as the C compiler) are loaded.
*Go enters an application, and you cannot use it to run machine code subroutines.
None
None
None
*Quit
None
*Quit exits from the current application - that is, it returns to the previous context.
*GOS
None
*Set varname value
varnamea variable name, or a wildcard specification for a single variable name |
valuestring value to GSTrans and then assign to the system variable varname |
*Set assigns a string value to a system variable, like an assignment statement in a programming language. For example:
*Set varname text
assigns the string 'text' to the variable varname. The string is OS_GSTrans'd before it is assigned.
Another use for the *Set command is to change the name of a command to one which is more convenient for the user:
establishes name as an alternative name for the command cname; for example after:
*Set Alias$Aid Help
the command *Aid is now a synonym for *Help; both commands access the help system. Another example is:
*Set Alias$Mode Echo |<22>|<%0>
The command implements a new command *Mode, which sets the screen to mode 12 (in the above case). The Echo command reflects the string which follows it; |<22> generates the ASCII character 22, Ctrl V, which is equivalent to the VDU command to change mode. |<%0> reads the first parameter from the command line, and generates the corresponding ASCII code.
*Set Sys$Year 1992
*SetEval, *SetMacro, *Unset
None
Evaluates an expression and assigns its value to a system variable
*SetEval varname expression
varname | a valid variable name |
expression | a valid Command Line expression |
*SetEval evaluates an expression and assigns its value to a system variable.
See the chapter entitled Evaluation operators for a description of the operators that you can use.
*Set rate 12
*SetEval rate rate + 1
*Show rate
*SetEval fred "jim"+"sheila"
*Show Fred
*Set, *SetMacro, *Unset, *Eval
None
Assigns a string macro to a system variable
varname | a variable name, or a wildcard specification for a single variable name |
macro | string value to assign to the system variable varname, which is GSTrans'd each time it is read |
*SetMacro assigns a string value to a system variable, like function definition in a programming language. For example:
*Set varname text
assigns the string 'text' to the variable varname. The string is not OS_GSTrans'd before it is assigned; instead it is OS_GSTrans'd each time the variable is read.
This resets the Command Line prompt, which appears as the first item on each line, to be the current time whenever the prompt is given. Compare this with using the *Set command:
*Set fred <Sys$Time>
*Show fred
FRED : 13:43:59
the *Show command issued five minutes later will still produce:
*Show fred
FRED : 13:43:59
Notice that the time is fixed at the time the *Set command was performed, in contrast to the *SetMacro command.
*Set, *SetEval, *Unset
*Show [variable_spec]
variable_spec | a variable name or a wildcard specification for a set of variable names |
*Show displays the name, type and current value of any system variables matching the name given as a parameter. These include the 'special' system variables, which may be altered, but which cannot be deleted.
If no name is given, all system variables are displayed.
*Set, *SetEval, *SetMacro
None
*Unset variable_spec
variable_spec | a variable name or a wildcard specification for a variable name |
*Unset deletes a system variable, which may be specified using wildcards.
*Set, *SetEval, *SetMacro
None
Here is a short example of reading a variable using OS_ReadVarVal:
;Print all sys$ variable names ADR R1, valBuffer ;Buffer to place value MOV R3, #0 ;Initial context .loop ADR R0, strName ;Wildcarded name to find MOV R2, #bufferLen ;Length of value buffer SWI "XOS_ReadVarVal" ;Non-error reporting one MOVVSS PC,R14 ;Return and clear V MOV R0, R3 ;Get address of name SWI "OS_Write0" ;Print it SWI "OS_NewLine" ;and new line B loop ;again .... .strName EQUS "SYS$*" + CHR$0
Below is a complete example of a program to create a variable called Mode. The read action is to return the current display mode, and the write action to set the mode.
.start ADR R0, varName ;Pointer to the name ADR R1, code ;Start of code body MOV R2, #endCode-code ;Length of code body MOV R3, #0 ;Context pointer MOV R4, #&10 ;'special' type SWI "OS_SetVarVal" ;Create it MOV PC, R14 ;Return .code B writeCode ;Branch to write code .readCode STMFD R13!, {R14} ;Save return address MOV R0, #&87 ;OS_Byte read mode number SWI "XOS_Byte" MOV R0, R2 ;Mode in R0 for conversion ADR R1, buffer ;Buffer for ASCII conversion MOV R2, #4 ;Max len of buffer SWI "XOS_BinaryToDecimal" MOV R0, R1 ;Pointer in R0 ;length already in R2 LDMFD R13!, {PC} ;Return .writeCode STMFD R13!, {R0,R14} ;Save registers SWI "XOS_ReadUnsigned" ;R1 set correctly already SWIVC &20100+22 ;VDU mode change MOVVC R0,R2 ;Get integer read in R0 SWIVC "XOS_WriteC" ;Do mode change LDMVCFD R13!, {R0,PC} ;Return without error ADD R13, R13, #4 ;Move stack pointer past R0, so we LDMFD R13!, {R1,PC} ;don't overwrite error pointer .buffer EQUD 0 ;Buffer for string conversion .endCode .varName EQUS "Mode " ;Name of variable
The routine at 'start' creates the variable. Obviously as the code body is copied into the system heap, it must be position independent. The two routines readCode and writeCode are called whenever an access to the variable is made. For example, a *Set Mode command will call the write code entry, and *Show Sys$Mode or *Echo Mode will call the read entry.
Notice that in the body of the code variable, only XOS_ SWIs are used. This is because it is important that errors are not generated when the read or write code executes. A more rigorous version of the code above would check V after each SWI and return if it was set.
The next example shows the use of OS_AddCallBack; it prints 'Run away!' after 2 seconds:
DIM code 100 P%=code [ .alarm STMFD R13!, {R14} SWI "XOS_WriteS" EQUS "Run away!" EQUB 10:EQUB 13:EQUB 0 ALIGN LDMFD R13!, {PC} .timer STMFD R13!, {R0,R14} MOV R0, R12 ; set up for us by BASIC bit ; R12 is not used in alarm, ; so R1 here is don't-care SWI "XOS_AddCallBack" LDMFD R13!, {R0, PC} ] SYS "OS_CallAfter",200,timer,alarm
The final example shows a CallBack handler, with a semaphore to prevent recursive CallBack; it prints 'Run away!' when mouse buttons are pressed.
DIM code 200 P%=code [ .sema EQUB 1 ALIGN .saveblock :]:P%=P%+16*4:[ .callback ; entered here in a privileged mode, ; with interrupts disabled ; first thing to do is enable IRQs TEQP PC, #3 ; force SVC mode, IRQs on. SWI "XOS_WriteS" EQUS "Run away!" EQUB 10:EQUB 13:EQUB 0 ALIGN ADR R14, saveblock LDMIA R14, {R0-R14}^ ; most registers reloaded MOV R0, R0 TEQP PC, #3+(1<<27) ; disable IRQs for sema update MOV R14, #1 ; and return STRB R14, sema LDR R14, saveblock+15*4 ; must not allow another CallBack ; request until the stashed PC is safe MOVS PC, R14 ; return, enableing IRQs etc .events CMP R0, #10 ; mouse button state change? MOVNES PC, R14 ; no - run away STMFD R13!, {R14} LDRB R12, sema ; possibly request CallBack MOV R14, #0 STRB R14, sema ; and disable any futher requests LDMFD R13!, {PC} ; until that one serviced. ] SYS "OS_ChangeEnvironment",7,callback,0,saveblock,0 TO ,ocall,,osave REM Note that we aren't using R12 in the CallBack handler; REM if this was in a module, for example, sema would be in the workspace, REM and we would have to access it R12-relative; R12 would therefore be REM set to be the workspace pointer on entry. SYS "OS_ChangeEnvironment",10,events,0 TO ,oldev *FX 14,10 REPEAT UNTILINKEY -1: REM loop until shift *FX 13,10 SYS "OS_ChangeEnvironment",10,oldev,0 SYS"OS_ChangeEnvironment",7,ocall,,osave REM Note that in both the above calls, the R12 values are explicitly left REM alone, because we didn't use them earlier.