www.riscos.com Technical Support:
Programmer's Reference Manual


Writing a FileCore module

Adding your own module to FileCore

FileCore does not know how to communicate directly with the hardware that your filing system uses. Your module must provide these facilities, and declare the entry points to FileCore.

This chapter describes how to add a filing system to FileCore. You should also see the chapter entitled Modules for more information on how to write a module.

Declaring your module

When your module initialises, it must inform FileCore of its existence. You must call FileCore_Create to do this. R0 tells FileCore where to find a descriptor block. This in turn tells FileCore the locations of all the entry points to your module's low level routines that interface with the hardware:

Descriptor block

This table shows the offsets from the start of the descriptor block, and the meaning of each word in the block:

Offset Contains
0 Bit flags
3 Filing system number (see the chapter entitled FileSwitch)
4 Offset of filing system title from module base
8 Offset of boot text from module base
12 Offset of low-level disc op entry from module base
16 Offset of low-level miscellaneous entry from module base

The flag bits in the descriptor block have the following meanings:

Bit Meaning when set
0 Hard discs need FIQ
1 Floppy discs need FIQ
2 Reserved - must be zero (was supports background operations under RISC OS 2).
3 Use only scratch space when a temporary buffer is needed
4 Hard discs support mount like floppies do (ie they fill in sector size, heads, sectors per track and density)
5 Hard discs support poll change (ie the poll change call works for hard discs and returns a sensible value; also locking them gives a sensible result)
6 Floppy discs support power-eject
7 Hard discs support power-eject

RISC OS 2 only uses bits 0 - 3; it ignores other bits.

Wherever possible, you should make hard discs support mount like floppies do, and hence set bit 4. If you do not do so, FileCore may have trouble mounting discs that use an alien format, as it then has no way of determining their geometry, and so has to make some assumptions that may be invalid.

FileCore_Create starts a new instantiation of FileCore, and, on return to your module, R0 points to the workspace that has been reserved for that new instantiation of FileCore. You must store this pointer in your module's workspace for future calls to FileCore; it is this value that tells FileCore which filing system you are (as well as enabling it to find its workspace!).

Unlike filing systems that are added under FileSwitch, the boot text offset cannot be -1 to call a routine.

Temporary buffers

The table below shows areas which may be used for temporary buffers when bit 3 of the flag word is not set:

Scratch space Spare screen area Wimp free pool RMA heap System heap Application area Directory cache
FSEntry_Func 8 [YES] [YES] [YES] [YES] [YES] [NO] [NO]
FSEntry_Close [YES] [YES] [YES] [YES] [YES] [NO] [NO]
FSEntry_Args 7 [YES] [YES] [YES] [YES] [YES] [NO] [NO]
AllocCompact [YES] [YES] [YES] [YES] [YES] [NO] [NO]
Compact [YES] [YES] [YES] [YES] [YES] [NO] [NO]
*Backup X X [NO] [NO] [YES] [YES] [YES] [NO] [YES]
*Backup X Y [YES] [YES] [YES] [YES] [YES] [NO] [NO]
*Backup X X q [NO] [NO] [YES] [YES] [YES] [YES] [YES]
*Backup X Y q [YES] [YES] [YES] [YES] [YES] [YES] [NO]
*Compact [YES] [YES] [YES] [YES] [YES] [NO] [NO]

where AllocCompact is the auto-compact triggered when allocating space for a file, and Compact is a normal auto-compact.

Selecting your filing system

Your filing system should provide a * Command to select itself, such as *ADFS or *Net. This must call OS_FSControl 14 to inform FileSwitch that the module has been selected, thus:

        STMFD   r13!, {r14}
        MOV     r0, #FSControl_SelectFS
        ADR     r1, FilingSystemName
        SWI     XOS_FSControl
        LDMFD   r13!, {pc}

For full details of OS_FSControl 14, see OS_FSControl 14.

Other * Commands

There are no other * Commands that your filing system must provide. For many FileCore-based systems the range it provides will be enough, and your module need add no more.

Implementing SWI calls

SWI calls in a FileCore module are usually implemented by simply:

  • loading R8 with the pointer to the FileCore instance private word for your module
  • calling the corresponding FileCore SWI.

For example, here is how a module might implement a DiscOp SWI:

        STMFD   r13!, {r8, r14}     ; R12 points to module workspace
        LDR     r8, [r12, #offset]  ; R8 <- pointer to FileCore private word
        SWI     XFileCore_DiscOp
        LDMFD   r13!, {r8, pc}

Usually DiscOp, Drives, FreeSpace and DescribeDisc are implemented like this. Of course you can add any extra SWI calls that are necessary.

Removing your filing system

The finalise entry of your module must remove its instantiation of FileCore. For full details of how to do so, see the chapter entitled Finalisation Code.

Returning errors

Your module has to return errors through FileCore as follows:

The V flag must be set, and R0 is used to indicate the error:

  • If bit 30 of R0 is set then, after clearing bit 30 of R0, it is a pointer to an error block.
  • If bit 31 of R0 is set and bit 30 is clear, then R0 is a disc error:

    bits 0 - 20 are the disc byte address / 256
    bits 21 - 23 are the drive number
    bits 24 - 29 are the disc error number

  • Else bits 30 - 31 are clear, and R0 is an error number:

    bits 0 - 7 are an error number (see list below)
    bits 8 - 29 are clear

In the latter two cases FileCore will generate a suitable error block.

The error numbers that may be returned are:

Error Token Default text
11 ExtEscape Escape
94 Defect Can't map defect out
95 TooManyDefects Too many defects
96 CantDelCsd Can't delete current directory
97 CantDelLib Can't delete library
98 CompactReq Compaction required
99 MapFull Free space map full
9A BadDisc Disc not formatted (not ADFS format)
9B TooManyDiscs Too many discs
9D BadUp Illegal use of ^
9E AmbigDisc Ambiguous disc name
9F NotRefDisc Not same disc
A0 InUse FileCore in use
A1 BadParms Bad parameters
A2 CantDelUrd Can't delete user root directory
A5 Buffer No room for buffer
A6 Workspace FileCore Workspace corrupt
A7 MultipleClose Multiple file closing errors
A8 BrokenDir Broken directory
A9 BadFsMap Bad free space map
AA OneBadFsMap One copy of map corrupt (use *CheckMap)
AB BadDefectList Bad defect list
AC BadDrive Bad drive
AD Size Sizes don't match (backups)
AF DestDefects Destination disc has defects (backups)
B0 BadRename Bad RENAME
B3 DirFull Directory full
B4 DirNotEmpty Directory not empty
BD Access Access violation
C0 TooManyOpen Too many open files
C2 Open File open
C3 Locked Locked
C4 Exists Already exists
C5 Types Types don't match
C6 DiscFull Disc full
C7 Disc Disc error
C9 WriteProt Protected disc
CA DataLost Data lost
CC BadName Bad name
CF BadAtt Bad attribute
D3 DriveEmpty Drive empty
D4 DiscNotFound Disc not found
D5 DiscNotPresent Disc not present
D6 NotFound Not found
D7 DiscNotFileCore FileCore does not understand this disc
D8 NotToAnImageYouDont Operation inapplicable to disc images
DE Channel Channel
FD WildCards Wild cards
FE BadCom Bad command

Module interfaces

The next section describes the interfaces to FileCore that your module must provide.

Module interfaces

Your module must provide two interfaces to FileCore: one for DiscOps, and one for other miscellaneous functions.

DiscOp entry

The entry for DiscOps does much of the work for a DiscOp SWI. It is passed the same values as FileCore_DiscOp (see FileCore_DiscOp), except:

  • an extra reason code is added to R1 allow background processing
  • consequently R1 is no longer used to point to an alternative disc record instead R5 always points to a disc record
  • R6 points to a boot block (for hard disc operations only), with the special value &80000000 indicating that none is available.

These are the reason codes that may be passed in R1:

Value Meaning Uses Updates
0 Verify R2, R4 R2, R4
1 Read sectors R2, R3, R4 R2, R3, R4
2 Write sectors R2, R3, R4 R2, R3, R4
3 Floppy disc: read track R2, R3
Hard disc: read Id R2, R3
4 Write track R2, R3
5 Seek (used only to park) R2
6 Restore R2
7 Floppy disc: step in
8 Floppy disc: step out
15 Hard disc: specify R2

The reason codes you must support are 0, 1, 2, 5 and 6. You must complete the entire operation requested, or give an error if you are unable to do so.

Your routine must preserve R1 - R13 inclusive, except where noted otherwise above, ie:

  • R2 must be incremented by the amount transferred for Ops 0, 1 and 2
  • R3 must be incremented appropriately for Ops 1 and 2
  • R4 must be decremented by the amount transferred for Ops 0, 1 and 2

You must also preserve the N, Z and C flags.

Returning errors

If there is no error then R0 must be zero on exit and the V flag clear. If there is an error then V must be set and R0 must be one of the following:

Value Meaning
R0 < &100 internal FileCore error number
Bit 30 set, bit 31 clear pointer to error block
Bit 30 clear, bit 31 set disc error bits:
bits 0 - 20 = disc byte address / 256
bits 21 - 23 = drive
bits 24 - 29 = disc error number

For a list of internal FileCore error numbers, see the chapter entitled Disc errors.

Background transfer

If bit 8 of R1 is set, then transfer may be wholly or partially in the background. This is an optional extension to improve performance. To reduce rotational latency the protocol also provides for transfers of indeterminate length.

R3 points to a list of address/length word pairs, specifying an exact number of sectors. The length given in R4 is treated as the length of the foreground part of the transfer. R5 is a pointer to the disc record.

Your module should return to the caller when the foreground part is complete, leaving a background process scheduled by interrupts from the controller. This process should terminate when it finds an address/length pair with a zero length field.

The foreground process can add pairs to the list at any time. To get the maximum decoupling between the processes your module should update the list after each sector. This updating must be atomic (use the STMIA instruction). Your module must be able to retry in the background.

The list is extended as below:

Offset Contents
-8 Process error
-4 Process status
0 1st address
4 1st length
8 2nd address
12 2nd length
16 3rd address
20 3rd length
n Loop back marker -n (where n is a multiple of 8)
n+4 Length of zero

Process error is set by the caller to 0; on an error your module should set this to describe the error in the format described above.

The bits in process status are:

Bit Meaning when set
31 process active
30 process can be extended
0 - 29 pointer to block giving position of any error

Bits 31 and 30 are set by the caller and cleared by your module. Your module must have IRQs disabled from updating the final pair in the list to clearing the active bit.

A negative address of -n indicates that your module has reached the end of the table, and should get the next address/length pair from the start of the scatter list n bytes earlier.

Your module may be called with the scatter pointer (R3) not pointing to the first (address/length) pair. So, to find the addresses of Process error and Process status, you must search for the end of list. From this you may then calculate the start of the scatter block.

MiscOp entry

The entry for MiscOps does much of the work for a MiscOp SWI. It is passed the same values as FileCore_MiscOp (see FileCore_MiscOp) - save for one reason code, noted below, which can be passed extra parameters.

  • Although FileCore_MiscOp is not available in RISC OS 2, you must still provide this entry point, as other SWIs also use it. (The MiscOp SWI merely provides a convenient way of directly calling this entry point.)

These are the reason codes that may be passed in R0:

Value Meaning
0 Mount
1 Poll changed
2 Lock drive
3 Unlock drive
4 Poll period
5 Eject disc

The reason codes you must support are 0, 2 and 3; for floppy drives, you must also support reason codes 1 and 4.

Your routine must preserve registers, and the N, Z and C flags - except where specifically stated otherwise.

When this entry point is called, R12 is a pointer to your FileCore module's private word.

You may only return an error from reason code 0 (Mount). This must be done in the same way as for the DiscOp entry; see the chapter entitled Returning errors.

For drives with disc sensing, reason code 1 (Poll changed) must always return changed in the spun-down state. If the drive is spun-up, you must return maybe changed if the drive has been permanently spun-up since the last 'Poll changed'; other wise you must return changed:

'Poll changed' returns for drives with disc sensing

Under RISC OS 2, the values returned from MiscOp 1 (Poll changed) in bits 4, 5, and 8 - 10 of R3 are ignored by FileCore.

Reason codes 2 and 3 (Lock/Unlock drive) must always perform that action. You must not try to track the state of the drive locking; FileCore counts lock/unlock calls itself, and only calls your module when it should actually lock or unlock the drive.

Reason code 5 (Eject disc) will never be called if bits 6 and 7 of the descriptor block are clear, since this indicates that no drives support power-ejection. Otherwise it may get called in a variety of situations: for example, after dismounting all discs as part of shutting down all filing systems.

Reason code 5 is also called whenever FileCore issues an UpCall 1 (medium not present), or an UpCall 2 (medium not known). In this case, the top bit of the drive number is set, indicating that a disc should be ejected from the drive considered to be most appropriate. The values passed to the UpCall in R4 (the iteration count) and in R5 (the minimum timeout period) are also passed on in the same registers to the MiscOp entry point. The filing system may treat these as appropriate; for example, it may choose to eject only on iteration 0 for an auto-insert detect drive, as doing further ejects may make it hard to get a new disc into the drive.

For more details of OS_UpCall 1 and 2, see OS_UpCall 1 and 2.

This edition Copyright © 3QD Developments Ltd 2015
Last Edit: Tue,03 Nov 2015