Files and the SD Card

The Agon's SD card holds the boot files, your saved modules and any files your programs write. VIOS reads and writes it directly through a FAT32 driver on the eZ80; nothing goes through MOS once VIOS runs. While the host harness runs on the Mac, files there can be reached too, under /host/.

Card format and path rules

  • The card must be FAT32. Other formats are refused ("not FAT32").
  • Names are 8.3: up to 8 characters, a dot, up to 3 characters (NOTES.TXT, APP.FTH). Long names are not supported.
  • Names are case-insensitive: /vios/vios.bin and /VIOS/VIOS.BIN are the same file.
  • Paths are absolute and start with /; there is no current directory.
  • A path is 1 to 127 printable characters with no spaces.
  • Directories must exist before you write into them. SAVE and BOOT-TRIAL create /VIOS and the package directories themselves.

What is on the card

A card prepared with tools/vios_sd_install.py and used for a while looks like this:

Path Written by Contents
/autoexec.txt the installer (one viosboot line added) MOS runs it at power-on
/mos/viosboot.bin the installer the boot stub: loads VIOS from the ESP or from the SD copy
/vios/vios.bin, /vios/vios.man the installer SD copy of the VIOS resident and its manifest (size, CRC)
/vios/boot.cnt the boot stub unfinished-boot counter; VIOS deletes it once healthy
/VIOS/BOOT0.REC, /VIOS/BOOT1.REC BOOT-TRIAL, startup the journaled boot record
/VIOS/BOOT_A.VIB, /VIOS/BOOT_B.VIB BOOT-TRIAL the two boot slots (module lists)
/VIOS/CACHE_A.VIC, /VIOS/CACHE_B.VIC startup boot-module cache, one per slot
/VIOS/xxxxxxxx/yyyyyyyy.VIM SAVE, BOOT-TRIAL module packages, content-addressed
/VIOS/USER.FTH you Forth source loaded at every boot
anything else you your files

/vios and /VIOS are the same directory (case-insensitive names). Files ending in .TMP are temporaries of an interrupted save; VIOS writes every package, cache and slot through a temporary and a rename, so the final name is either complete or absent. See Modules and Persistence for packages, slots and the cache, and Installing for the boot files.

Listing a directory: DIR

DIR path lists one directory, one line per entry:

DIR /VIOS
entry BOOT0.REC size 32 dir 0
entry BOOT_A.VIB size 348 dir 0
entry 2E5A9E54 size 0 dir 1
entry USER.FTH size 41 dir 0

dir 1 marks a subdirectory. From the Mac, the CLI's :ls /VIOS shows the same list, and :put LOCAL /PATH and :get /PATH [LOCAL] copy text files to and from the card.

Running source files: LOAD and INCLUDE

LOAD path reads a Forth source file and evaluates it, exactly as if you had typed it. INCLUDE is the same command under its other common name. Both are console commands: type them on their own, not inside a definition.

S" /VIOS/LIB.FTH" S" : DOUBLE 2 * ;" FILE-WRITE .
S" /VIOS/APP.FTH" S" INCLUDE /VIOS/LIB.FTH 21 DOUBLE ." FILE-WRITE .
LOAD /VIOS/APP.FTH
0 0 42

Rules:

  • A source file is at most 4096 bytes and must not contain NUL bytes.
  • Files can LOAD or INCLUDE other files up to 4 deep.
  • A file must end outside a definition, or LOAD fails with "incomplete definition in source file".
  • Commands such as MODULE-LOAD, DIR or another LOAD work inside a file, one per line or between words, as at the console.
  • Unlike module source, a loaded file may print, open files, spawn tasks: it runs as ordinary console input.
  • Words defined by LOAD are ordinary RAM definitions: they are gone after a restart unless the file is loaded again. To keep code, make it a module.

/host/ paths load files from the Mac (see below), which is the fastest edit-and-run loop: edit on the Mac, LOAD /host/app.fth on the Agon.

USER.FTH: your startup file

If /VIOS/USER.FTH exists, VIOS prints user startup: /VIOS/USER.FTH and loads it once at every boot, after the boot slot's modules and before AUTORUN. It is the place for settings and small conveniences that are not worth a module:

\ /VIOS/USER.FTH
DECIMAL
: HI ." Hello from USER.FTH" CR ;
HI

If it fails, the console shows user startup failed: and the error, the stacks are reset, and boot continues; fix the file (from the Mac with :put, or with FILE-WRITE) or delete it with FILE-REMOVE. An endless loop in USER.FTH is stopped by the normal execution budget.

Whole files: FILE-READ, FILE-WRITE, FILE-REMOVE

For small files, three words move a whole file at once. They return 0 for success, not a Forth true flag:

Word Stack Result
FILE-WRITE ( addr u src n -- ior ) create or replace file addr u with n bytes from src; 0 ok, 1 error, 2 if n > 4096
FILE-READ ( addr u dest cap -- n ior ) read the whole file into dest (at most cap, at most 4096 bytes); length and 0, or 0 and 1
FILE-REMOVE ( addr u -- ior ) delete a file or an empty directory; 0 ok, 1 error

FS-ERROR prints the last file system message, and SD-MOUNT ( -- ior ) mounts the card again, for example after you swapped it (0 = mounted).

CREATE BUF 256 ALLOT
S" /VIOS/HELLO.TXT" S" Hello, SD card!" FILE-WRITE .
S" /VIOS/HELLO.TXT" BUF 256 FILE-READ . DUP .  BUF SWAP TYPE CR
S" /VIOS/NOPE.TXT" BUF 256 FILE-READ . .  FS-ERROR
S" /VIOS/HELLO.TXT" FILE-REMOVE .
0 0 15 Hello, SD card!
1 0 file not found
0

FILE-WRITE flushes the file to the card before it returns, so the data survives a power cut right after. FILE-READ fails with "file exceeds buffer" when the file is larger than cap; read big files as a stream instead. These words work on SD paths only, not on /host/.

Common FS-ERROR messages: file not found, path not found, invalid name (not 8.3), access denied or full, file exceeds buffer, not mounted, not FAT32, disk full: short file write, write protected.

Streams: OPEN, READ, WRITE, CLOSE

For files of any size, open a handle and move data in pieces (see Handles, Pipes and Devices for the full handle model and error codes). Up to 4 files can be open at once. A file opened for writing (mode 2) is created or truncated; there is no append mode and no seek.

S" /VIOS/BIG.DAT" 2 OPEN CONSTANT BH
: FILL-BIG 50 0 DO BUF 100 BH WRITE DROP LOOP ;
FILL-BIG  BH CLOSE .
S" /VIOS/BIG.DAT" BUF 256 FILE-READ . .  FS-ERROR
0 1 0 file exceeds buffer

The file is 5000 bytes, too big for FILE-READ. Count its bytes with a read loop:

VARIABLE FH
: FILE-SIZE ( addr u -- n )
  1 OPEN FH !  0
  BEGIN BUF 256 FH @ READ  DUP 0 > WHILE + REPEAT
  DROP  FH @ CLOSE DROP ;
S" /VIOS/BIG.DAT" FILE-SIZE .
S" /VIOS/BIG.DAT" FILE-REMOVE .
5000 0

READ returns 0 at the end of the file and a negative code on an error, so 0 > ends the loop either way.

Files on the Mac: /host/

While tools/vios_harness.py runs on the Mac, every path that starts with /host/ names a file in the harness's host directory, ~/VIOS-host unless you start the harness with --host-root DIR (--host-root "" turns the service off):

.venv/bin/python tools/vios_harness.py --backend resident --tcp 127.0.0.1:8765 --host-root ~/VIOS-host

LOAD, INCLUDE, OPEN, READ, WRITE and CLOSE accept /host/ paths; FILE-READ, FILE-WRITE, FILE-REMOVE and DIR do not. Paths are relative to the host directory and cannot use .. or escape it through a symbolic link; at most 4 host files are open at once. Each operation is one request over the USB link carrying up to 200 bytes; without a harness the request times out after about a second and OPEN returns -5.

LOAD /host/game.fth

Limits

Limit Value
File system FAT32, 8.3 names, absolute paths
Path length 1..127 printable characters, no spaces
LOAD / INCLUDE file size 4096 bytes
LOAD / INCLUDE nesting 4
FILE-READ / FILE-WRITE size 4096 bytes
Open files 4 SD streams, 8 handles in total
Module package 260-byte header + source (up to 4096) + 4-byte CRC
/host/ request 200 bytes per read or write; 4 open host files

The console command SYNC name only prints @name; it is a marker for host tools and does not flush the card. VIOS flushes on its own: FILE-WRITE before it returns, streams when they are closed.