Transient Utility files (filetype &FFC) contain position independent code which is loaded into the RMA and executed in User Mode.
The purpose of a Utility is for when you want to run a small utility and then return back to the program environment that you were in prior to calling the Utility (unlike loading an Absolute program which would normally replace the current program).
On entry to a transient Utility, the registers are as follows:
Entry conditions | |
---|---|
R0 | Pointer to first character of command the line |
R1 | Pointer to first character of the command tail (will be a control character if no parameters given) |
R12 | Pointer to 1024 bytes of workspace |
R13 | Pointer to end of workspace, for stack |
R14 | Return address |
A transient Utility is entered in User Mode with interrupts enabled. You should only call X form SWIs, and if there is an error, you should exit by setting the V flag and pointing R0 to an error block.
The operating system provides you with 1024 bytes of workspace, which includes the stack descending downwards from the end of the workspace. If more space is required, it should be claimed from the RMA.
You must exit the transient Utility with MOV PC, R14
(after freeing any RMA claims). As you are never the “current program”, you must not call OS_Exit.
Aemulor may trigger problems with some Utilities. If the Utility is known to be 32 bit safe, it should be marked as such by appending the four bytes ‘3
’ ‘2
’ ‘O
’ (upper case ‘o’) and ‘K
’ (in that order!) to the end of the file.
This can be achieved by adding this to the end of the Utility source:
DCS "32OK"
Please note, this does not mean that the Utility is 32-bit only; it should be used to indicate that the Utility will operate correctly on both 26- and 32-bit architectures.
RISC OS Ltd has defined a header for Utility programs.
This is documented here for completeness, though please note that RISC OS 5 does not make use of such a header, and Aemulor would still require the “32OK” word at the end of the file.
ROLtd Utility Header | ||
---|---|---|
+0 | Branch to entry point (to skip over header) | |
+4 | First magic value &79766748 (“Hgvy”) | |
+8 | Second magic value &216C6776 (“vgl!”) | |
+12 | Read only size (or 0 to mean the size of the file) | |
+16 | Read/write size (or 0 to mean the size of the file) | |
+20 | Either 26 or 32 depending on whether built for 26 bit or 32 bit systems |
The two magic values are ROT13 for “Utility!”.
This is a simple but potentially very useful Utility that will list all open files, plus provide information on the file status.
The output looks like this:
*ListOpen
255 RW SDFS::RISCOSpi.$.!Boot.Loader
254 R U devices#bulk;size16385:$.USB6
253 W! U devices#bulk;size16385:$.USB6
252 R Resources:$.Fonts.Homerton.Medium.Outlines0
251 W! U devices#endpoint1;interface0;alternate0;bulk;size32768:$.USB7
250 R U devices#endpoint2;interface0;alternate0;bulk;size32768:$.USB7
*
The status indicators are:
R
– file opened with Read accessW
– file opened with Write access!
– file has been written toE
– file is at EOFU
– file is unbufferedX
– data has been lostREM >makeListOpen REM REM Creates a transient Utility that lists all open files REM REM Rick Murray, 2014/10/31 REM This code is licenced as "whatever, dude". I'm not bothered. REM It's for a RISC OS Open wiki example. So have fun with it. DIM code% 320, L% -1 FOR l% = 8 TO 10 STEP 2 P% = code% [ OPT l% .begin ; RISC OS 4/6 Utility header B start EQUD &79766748 EQUD &216C6776 EQUD end - begin EQUD 0 EQUD 32 .start MOV R8, #255 ; start at file handle 255 and work backwards .checkfile ; Read info on this handle MOV R0, #254 ; OS_Args 254 -> read info on file handle MOV R1, R8 SWI "XOS_Args" ; can only call X form SWIs in Utilities MOVVS PC, R14 ; bomb out upon an error here ; Validate it MOV R7, R0 ; stash the stream status word in R7 TST R7, #2048 ; check this is a valid handle BNE nextfile ; if not, try the next one ; Output first the file handle number MOV R0, R8 MOV R1, R12 MOV R2, #4 ; range is 0-255 (unsigned) only SWI "XOS_ConvertCardinal1" MOVVS PC, R14 ; Work out how long the string is to right-align it SUBS R6, R1, R0 ; R6 = terminator - buffer start SWILE (&20100+32) ; OS_WriteI for space padding SUBS R6, R6, #1 SWILE (&20100+32) ; padding SUBS R6, R6, #1 SWILE (&20100+32) ; padding SWI "XOS_Write0" ; write the handle value SWI (&20100+32) ; separator space MOVVS PC, R14 ; Now we want to write flags (or not) as ; RW!EUX ; |||||| ; |||||'- data lost ; ||||'-- unbuffered ; |||'--- at EOF ; ||'---- file has been written to ; |'----- file has write access ; '------ file has read access TST R7, #(1<<6) ; bit 6 = Read access MOVNE R0, #ASC("R") MOVEQ R0, #ASC(" ") SWI "XOS_WriteC" TST R7, #(1<<7) ; bit 7 = Write access MOVNE R0, #ASC("W") MOVEQ R0, #ASC(" ") SWI "XOS_WriteC" TST R7, #(1<<8) ; bit 8 = has been written to MOVNE R0, #ASC("!") MOVEQ R0, #ASC(" ") SWI "XOS_WriteC" TST R7, #(1<<9) ; bit 9 = at EOF MOVNE R0, #ASC("E") MOVEQ R0, #ASC(" ") SWI "XOS_WriteC" TST R7, #(1<<10) ; bit 10 = unbuffered stream MOVNE R0, #ASC("U") MOVEQ R0, #ASC(" ") SWI "XOS_WriteC" TST R7, #(1<<13) ; bit 13 (ho! ho!) = data lost MOVNE R0, #ASC("X") MOVEQ R0, #ASC(" ") SWI "XOS_WriteC" SWI (&20100+32) ; separator space MOVVS PC, R14 ; Now determine the file NAME associated with this handle MOV R0, #7 ; OS_Args 7 -> read pathname of open file MOV R1, R8 ; (PRM 2-58) MOV R2, R12 ; pointer to buffer MOV R5, #512 ; workspace is 1024 so assume okay to give 512 SWI "XOS_Args" MOVVS PC, R14 ; poke a null word at the end in case 512 bytes wasn't enough! MOV R2, #0 ; null MOV R3, R12 ; point to workspace ADD R3, R3, #512 ; add 512 STR R2, [R3] ; stick the null there ; Write the pathname, and a newline MOV R0, R12 SWI "XOS_Write0" SWI "XOS_NewLine" MOVVS PC, R14 .nextfile SUBS R8, R8, #1 ; for as long as there are file handles BPL checkfile ; check 'em ; otherwise, we're done here MOV PC, R14 ; flag ourselves as 32 bit okay ALIGN ; just in case(!) EQUS "32OK" .end ] NEXT SYS "OS_File", 10, "$.ListOpen", &FFC,, code%, P% END