Skip to content

Implement some missing Hyppo DOS traps, with long filename support - #946

Open
psnr wants to merge 1 commit into
MEGA65:developmentfrom
psnr:hyppo-dos-9-traps
Open

Implement some missing Hyppo DOS traps, with long filename support#946
psnr wants to merge 1 commit into
MEGA65:developmentfrom
psnr:hyppo-dos-9-traps

Conversation

@psnr

@psnr psnr commented Aug 30, 2026

Copy link
Copy Markdown

Fills in several Hyppo DOS trap slots (getdisksize, getcwd, mkdir, rmdir, seekfile, rename), finishes fstat, and extends mkfile with long-filename support.
Also adds writing of VFAT long names (Hyppo could already read them, not create them), and fixes the current working directory not being carried across a freeze.

See the commit message for full details on each trap.

Tested on MEGA65 R6 hardware: all new traps, long filenames enabled and
disabled, and freeze/resume across getcwd/mkdir/rmdir/rename including with
directories deleted or renamed out from under a frozen task's saved cwd.

Note on headroom: this uses up most of what Hyppo had left below the
fixed data area — free space there drops from roughly 1.6KB before this
branch to about 38 bytes now.

Several DOS trap slots were bare labels sharing a single
jmp invalid_subfunction, so calling them just reported an invalid
sub-function. This fills some of them in, finishes fstat, and extends
mkfile. All of them follow the existing convention: sub-function in
A, STA $D640, then CLV, and carry clear on return means failure with a
reason in dos_geterrorcode (A=$38).

  A=$08  getdisksize   Y = destination page. Fills $YY00 with the fields
                       already tracked in dos_disk_table: sectors per
                       cluster, cluster count, FAT size and so on. It
                       does not count free clusters, which would mean a
                       full FAT scan.

  A=$0A  getcwd        Y = destination page. $YY00 = drive number,
                       $YY01 = offset of a NUL-terminated path within
                       the same page. Resolved on demand by walking the
                       cwd up to the root - there is no persistent path
                       to go stale, so the answer is right however the
                       cwd got where it is.

  A=$0E  mkdir         Name via setname (A=$2E) first. Creates a
                       sub-directory in the cwd, with its own "." and
                       ".." entries. Fails with file_exists if the name
                       is taken.

  A=$10  rmdir         Name via setname first. Refuses anything that is
                       not a directory, or a directory holding more than
                       "." and "..".

  A=$1E  mkfile        Already worked for 8.3 names, contiguous
                       allocation and all. Now runs the name through the
                       long-name path, reserving the extra dirent slots
                       the VFAT pieces need rather than the single slot
                       it used to take.

  A=$24  seekfile      X/Y/Z = 24-bit sector number within the open
                       file. Walks the cluster chain forward without
                       transferring any data, which is the cheap way to
                       reach a sector - reading there instead would move
                       every byte in between. FAT32 chains are singly
                       linked, so seeking is always from the start, and
                       there is no length to bounds-check against: going
                       past the end fails when the chain runs out.

  A=$28  fstat         Existed, but never called
                       hypervisor_setup_copy_region, so it wrote through
                       whatever destination the previous trap had
                       established - after the usual setname + findfile
                       that is the caller's filename buffer - and
                       ignored its own Y. Now Y = destination page for
                       the raw 32-byte dirent.

  A=$2A  rename        Renames the open file, or the open directory from
                       opendir. A name that still fits 8.3 is rewritten
                       in place; one that needs VFAT pieces moves the
                       entry to a large enough run of free slots. Either
                       way the attributes, dates, cluster and length are
                       carried across untouched.

Long filenames
--------------
Hyppo could already read long names - dos_readdir has understood VFAT
pieces for a long time, behind the enable switch that syspart patches
into disable_lfn_byte. What it could not do is create them, so anything
Hyppo wrote got a plain 8.3 name regardless.

That side is now implemented: pieces are written with their checksum,
short names are built by truncating and appending ~1 through ~9 until
one does not collide with an existing entry, the preceding pieces are
removed when a file is deleted, and rename relocates an entry when the
new name needs more slots than the old one occupied. getcwd returns the
long name of each level where there is one.

The existing switch still governs all of it. With long names disabled
every path falls back to 8.3, and so do entries that have no long name
or whose pieces fail their checksum against the short entry - a stale
chain is ignored rather than trusted.

One consequence worth knowing: a path segment can now be 64 characters
instead of 12, so getcwd's 255-byte buffer can be exhausted at about
four levels rather than nineteen. It checks before writing and returns
dos_errorcode_path_too_long, an error code that has been defined and
unused until now.

Freeze and unfreeze
-------------------
A task's current directory is now carried across a freeze in the process
descriptor. Previously, a task frozen in one directory always resumed in
the root directory. Now saving cwd on freeze and on resume the saved cluster
is only trusted if the directory is still listed in its parent, since
removing a directory leaves its own "." and ".." intact and a freed cluster
may since have been handed to a file. If it has gone, its parent is tried
in turn.

Other changes / Hyppo space usage
---------------------------------
The 4510's 16-bit relative branches replace the branch-over-jmp idiom in
twenty places, buying back 43 bytes. Even so, this uses up most of what
hyppo had left: the space below DOSDiskTable_Start goes from about 1.6KB
free to 38 bytes. ACME is now run with --strict-segments, because the
fixed data areas from $BB00 up are placed by winding * backwards, and it
otherwise only warns once code has already grown into them.

Are these changes worth the space it takes up? Do we want to use the space
available to other traps instead?

Tests
-----
hyppotest could not execute the branches: of the ten opcodes only BCC
was both present and correct, four took their displacement from the low
byte alone and four were missing entirely.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant