Filing Systems — MOS API

Eight (well, nine) filing systems on the BBC family, all sharing a common API. For chip-level direct disc access (bypassing the FS), see wd1770.

Filing systems available

#NameBREAK key* cmdWorkspace pages (abs + priv)
0none
1CFS 1200 baudSPACE*TAPE0 + 0
2CFS 300 baud*TAPE 30 + 0
3RFSSHIFT-SPACE*ROM0 + 0
4DFSD*DISC / *DISK9 + 2
5NFSN*NET2 + 2
6TelesoftT*TELESOFT12 + 8
7IEEE*IEEE2 + 1
8ADFSA or F*ADFS / *FADFS14 + 1
9HFS(host)
10VideodiscQ or L*VFS / *LVFS

OSARGS A=0 Y=0 returns the active FS number.

The standard API

Six entry points + one vector. All Tube-aware.

CallAddressVectorFunction
OSFILE&FFDD&212 FILEVWhole-file load/save + attribute R/W
OSARGS&FFDA&214 ARGSVFile pointer / length / flush, FS-id query
OSBGET&FFD7&216 BGETVRead 1 byte from open file
OSBPUT&FFD4&218 BPUTVWrite 1 byte to open file
OSGBPB&FFD1&21A GBPBVMulti-byte transfer, directory list
OSFIND&FFCE&21C FINDVOpen/close file
&21E FSCVOS-internal dispatch (FSCV-only)

OSFILE — whole-file operations

LDA #action      ; see table
LDX #pb MOD 256
LDY #pb DIV 256
JSR &FFDD

Parameter block (18 bytes):

+0..1   filename ptr (terminated by CR)
+2..5   load address (4 bytes, LSB first)
+6..9   exec address
+10..13 start address / length (depending on action)
+14..17 end address / file attributes

Actions:

AFunction
0Save memory block. Set +10/+14 to start/end. Returns length + attrs.
1Write catalogue info (load, exec, attrs all at once)
2Write load address
3Write exec address
4Write attributes
5Read catalogue info. Exit A: 0=not found, 1=file, 2=directory, &FF=E-attr (ADFS).
6Delete file (returns catalogue info first)
7Create empty file (Master only). Length in +10..+13.
255Load named file. If +6 = 0, use specified load address; otherwise use file’s stored address.

File attributes (byte at +14)

BitSet means
0R — owner can read
1W — owner can write
2E — owner can execute
3L — locked (can’t delete)
4r — others can read
5w — others can write
6e — others can execute

DFS only honours bit 3 (L=locked).

NFS uses +15..+17 for date (day, month, year-1981 packed into 12 bits).

OSFIND — open / close

LDA #action      ; 0/64/128/192
LDX #fname MOD 256   ; for open
LDY #fname DIV 256
JSR &FFCE
AActionReturns
0Close. Y=0 closes all, Y=handle closes one.
64Open for input.A = handle, 0 if failed
128Open for output (creates if absent).A = handle
192Open for random access.A = handle

Default file size on creation: 16 KB (DFS), 64 KB (ADFS).

OSARGS — file pointer / attributes / FS query

LDA #action
LDX #zp_4byte_buffer    ; zp address for transfer
LDY #file_handle        ; or 0 for non-file queries
JSR &FFDA
AYAction
00A returns FS number
0handleRead sequential pointer into 4-byte zp buffer
10Return remaining command-line address in zp
1handleWrite sequential pointer from zp
2handleReturn file length in zp (4 bytes)
2550Flush all open files to disc
255handleFlush one file

OSBGET / OSBPUT — single byte

LDY #handle
JSR &FFD7          ; OSBGET: returns A=byte, C=1 if EOF
                   ; advances sequential pointer
LDA #byte
LDY #handle
JSR &FFD4          ; OSBPUT: writes byte, advances pointer

Fast Tube OSBPUT: from a 6502 second processor, OSBYTE &9D (X=byte, Y=handle) is faster than going through OSBPUT — bypasses one level of vector dispatch (naug-ch18-tube).

OSGBPB — bulk transfer

LDA #action      ; 1-8
LDX #pb MOD 256
LDY #pb DIV 256
JSR &FFD1

Parameter block:

+0    file handle (or disc cycle number for A=8)
+1..4 memory address (LSB first)
+5..8 byte count (or filename count for A=8)
+9..12 sequential pointer
AAction
1Write bytes at the specified sequential position
2Append bytes at current position
3Read bytes from specified position
4Read bytes from current position
5Read disc title, boot option, drive number
6Read current directory + drive name
7Read current library + drive
8Read filenames from current directory

Exit C=1 if transfer couldn’t complete (partial); +5..+8 holds remaining byte count.

FSCV — internal dispatch (don’t call from user code)

The OS calls FSCV (&21E) for filing-system housekeeping. User code shouldn’t call FSCV directly, but service ROMs may intercept FSCV to add custom filing-system commands.

Reason codes: see naug-ch16-filing §16.1.7.

Filing-system OSBYTEs

OSBYTEFunction
&77 (119)Close all *SPOOL/*EXEC files
&7F (127)EOF check (X = handle, returns X ≠ 0 if EOF)
&82 (130)Read 32-bit machine high-order address (Tube prefix)
&87 (135)(also char at cursor — naug-ch13-video)
&89 (137)Cassette motor (X=0/1)
&8B (139)Perform *OPT (X = option, Y = value)
&8C (140)Select CFS (X=0 default, X=3 = 300 baud, X=12 = 1200)
&8D (141)Select RFS
&9D (157)Fast Tube OSBPUT
&B7 (183)R/W CFS/RFS switch flag
&C6 (198)R/W *EXEC handle
&C7 (199)R/W *SPOOL handle
&6D (109)Make temporary FS permanent (Master)

OSWORD calls used by filing systems

OSWORDFSFunction
&7ATelesoftIssue Telesoftware command
&7DDFSRead disc cycle number
&7EDFSRead disc / directory size
&7FDFSIssue raw 1770 FDC command
&80IEEEIssue IEEE-488 command

OSWORD &7F is the direct-FDC path — bypasses DFS’s high-level routines and lets you issue Type I/II/III commands directly via the parameter block. Useful for custom disc formats, IBM/CP/M disc reads, and disc repair tools. Parameter block layout in naug-ch16-filing.

Per-filing-system variation summary

FSOSFILEOSARGSOSGBPBOSFINDOSWORDs added
CFSload/save onlyFS-id only(limited Master)input + output (max 2)none
RFSload onlyFS-id only(A=4 Master)input only (1 file)none
DFSfullfullfullfull&7D, &7E, &7F
ADFSfullfullfullfullDFS set + extensions
NFSfull + datesfullfullfullmany (see Econet AUG)
Telesoftload onlyFS-id onlynoneinput only&7A
IEEEnot supportedFS-id onlynoneinput + output (16 ch)&80

Master FS handler — multiple FS simultaneously

On Master, the FS handler in HAZEL (&DF00-&DFFF, see shadow-ram) lets you run with current, library, and temporary filing systems all known simultaneously. Use the prefix syntax:

*LOAD -DISC-prog          rem use DFS for this command only
*ADFS                      rem switch persistently
*LIB :0.$                  rem set the library FS path

HAZEL’s state:

AddrUse
&DF00Current FS #
&DF01Active FS #
&DF02Library FS #
&DF03Current FS ROM #
&DF05, &DF06Min, max file handle
&DF06-&DFC1Up to 17 × 11-byte FS info blocks
&DFD4-&DFDD*MOVE workspace
&DFDA-&DFDBCopy of FSCV for active FS

Bypass-MOS strategies for fast disc I/O

  1. OSWORD &7F (DFS direct FDC command) — lets you issue Type II read/write sector commands with custom parameter blocks. Still goes through DFS workspace but skips file-level dispatch.
  2. Direct &FE28/&FE29/&FE2A/&FE2B writes (wd1770) — fully bypass DFS. Must install NMI handler yourself.
  3. Stay with OSGBPB for sequential reads — already the fastest MOS-blessed bulk transfer.

The trade-offs:

PathCost per sectorRisk
OSFIND + OSBGET 256×very high (per-byte vector dispatch)low
OSFIND + OSGBPB 256 bytesmoderate (one OSGBPB call)low
OSWORD &7Flow (one OSWORD + sector-level transfer)low
Direct 1770 + own NMIminimumhigh — your NMI handler can crash

For game / demo loaders: OSGBPB with a custom NMI handler captured from DFS is the usual sweet spot. For disc utilities (sector editor, copier): direct 1770 access is mandatory.

See also

  • wd1770 — FDC chip reference.
  • naug-ch16-filing — Detailed catalogue formats (DFS, ADFS), per-FS specifics.
  • calls — All MOS entry points including filing-system vectors.
  • paged-roms — Filing systems implement themselves as service ROMs; service-call dispatch.
  • disc-formats — DFS / ADFS sector-level layout (catalogue, FSM, root directory).
  • shadow-ram — Master HAZEL FS-handler workspace.

This wiki is curated by Claude following the LLM-Wiki methodology — a human curates source documents, the LLM compiles structured cross-linked markdown. Content may contain errors, omissions, or stale claims. For authoritative information refer to the original source documents in the bbc-documents GitHub archive.