Complete Dartmouth and Commodore disk operations #28

Open
opened 2026-08-02 22:40:26 -04:00 by tachikoma · 0 comments
Collaborator

Problem

akbasic exposes part of the Dartmouth and Commodore disk vocabulary, but several commands are still refused or absent even where the host filesystem can provide a safe and deterministic equivalent.

At c5d13f00f6fd2d6f4759d252b30165a533a1863e:

  • src/runtime_disk.c:381-400 refuses DIRECTORY and CATALOG.
  • src/runtime_disk.c:697-738 refuses HEADER, COLLECT, BACKUP, and BOOT.
  • src/runtime_disk.c:265-376 implements only the file forms of SCRATCH, RENAME, and COPY.
  • src/runtime_commands.c:606-712 gives DLOAD and DSAVE no Dartmouth current-program-name contract.
  • src/parser_commands.c:471-497 requires a special spaced PRINT # spelling and has no BASIC 2.0 OPEN, CLOSE, CMD, or GET# compatibility forms.
  • docs/09-files-and-disk.md:125-141 and docs/13-differences.md:196-202 document the missing surface.

Functional consequence: programs written around either the Dartmouth timesharing workflow or Commodore disk channels need source changes, and directory traversal, recursive copies, and host boot requests cannot be expressed.

Blast radius: high. This touches parsing, runtime modes, public runtime state, host integration, filesystem behavior, compatibility documentation, and dependency APIs.

Inventory

Dartmouth treated these as DTSS operating-system commands around BASIC rather than statements inside a stored program. They are still part of the requested interactive BASIC workflow:

  • NEW [name]
  • OLD name
  • SAVE [name]
  • REPLACE [name]
  • RENAME name
  • CAT and CATALOG
  • UNSAVE [name]
  • Bare SCRATCH, which clears the program in memory

The Commodore BASIC 2.0 and 7.0 disk surface is:

  • File and program operations: APPEND, BLOAD, BSAVE, CONCAT, COPY, DLOAD or LOAD, DSAVE or SAVE, DVERIFY or VERIFY, RECORD, RENAME, SCRATCH, and RUN "filename"
  • Channels: DOPEN, DCLOSE, OPEN, CLOSE, CMD, GET#, INPUT#, and PRINT#
  • Directory and media commands: CATALOG, DIRECTORY, DCLEAR, COLLECT, HEADER, and BACKUP
  • Boot: BOOT

akbasic already implements APPEND, BLOAD, BSAVE, CONCAT, file-only COPY, DCLEAR, DCLOSE, DLOAD or LOAD, DOPEN, DSAVE or SAVE, DVERIFY or VERIFY, INPUT#, PRINT#, RECORD, file-pair RENAME, and file SCRATCH.

The remaining work is the Dartmouth name lifecycle, BASIC 2.0 channel aliases, CMD, GET#, RUN "filename", directory listings, recursive COPY, the safe COLLECT mapping, and the host-mediated BOOT mapping. HEADER and BACKUP need explicit final refusal contracts because a host directory is not a Commodore disk.

Behavioral contract

Paths and drives

  • Reads resolve a relative path against the process working directory first, then against the loaded source directory.
  • Writes, deletes, renames, copies, and directory listings use the explicit path relative to the process working directory.
  • Parse D0, U8, and ON U8, but accept only drive 0 and unit 8. Refuse every other drive or device with AKBASIC_ERR_DEVICE.
  • Refuse symlinks for recursive operations rather than following them outside the requested tree.

Dartmouth program names

Store a bounded current-program path on akbasic_Runtime.

  • NEW ["name"] clears the program and optionally sets its current name.
  • OLD name loads the named program and makes that path current.
  • SAVE [name] writes the named or current program and refuses to overwrite an existing file.
  • REPLACE [name] writes the named or current program and permits replacement.
  • UNSAVE [name] removes the stored copy without changing the program in memory.
  • RENAME name changes only the current program name.
  • Accept bare names and quoted paths.
  • Bare SCRATCH clears the program while retaining the current name. SCRATCH "path" remains the Commodore file-delete form.
  • Preserve RENAME "old","new"; also accept the exact Commodore form RENAME "old" TO "new".
  • Keep DSAVE as the explicit overwrite-capable Commodore save operation.

Directory and copy operations

Implement DIRECTORY, CATALOG, and CAT through one backend:

  • Sort entries bytewise for deterministic output.
  • Print directory names with a trailing /.
  • Print file byte sizes.
  • Accept an optional basename filter using * and ?.
  • Do not depend on host enumeration order.

Extend COPY to copy complete directory trees:

  • The destination must not exist; do not merge trees.
  • Reject copies onto the source or below the source.
  • Refuse symlinks.
  • Bound traversal depth at 32.
  • On failure, remove only destination objects created by that invocation. Never remove a pre-existing path.
  • This makes a host-filesystem BACKUP alias unnecessary; keep BACKUP refused as a physical-disk operation.

BASIC 2.0 channels

  • OPEN supports disk device 8 only and maps its logical file and mode onto the existing bounded channel table. Refuse non-disk devices.
  • CLOSE maps to DCLOSE.
  • CMD redirects ordinary PRINT output to an open channel until an empty PRINT# or closure of that channel.
  • GET# reads one character at a time.
  • Add read-only ST and ST# status values and update them for successful reads and EOF.
  • Keep INPUT# as line-oriented input.
  • Accept the compact Commodore spellings PRINT#, INPUT#, and GET# without requiring a space before #.

Media commands

  • COLLECT is a successful no-op after validating that only the default drive and unit were requested. A host filesystem owns its allocation metadata.
  • HEADER remains an AKBASIC_ERR_DEVICE refusal and must never delete or format a directory.
  • BACKUP remains an AKBASIC_ERR_DEVICE refusal and must never infer a physical-disk operation from host paths.

RUN and BOOT

  • RUN "filename" clears the current program, loads the file, records it as current, and runs it.
  • Allow BOOT only as an unnumbered direct REPL command.
  • A case-insensitive .bas target performs NEW, DLOAD, and RUN.
  • Any other target records a bounded boot request and switches to a new AKBASIC_MODE_BOOT.
  • Add akbasic_runtime_take_boot_request() so an embedding host can consume the request.
  • The standalone driver may execute the request with direct execv and one filename. Never use a shell and never accept embedded argument text.
  • The library must never execute a process or exit.
  • Refuse numbered or stored BOOT statements.

Dependency work

Do not bypass libakstdlib with direct platform calls. Complete and release the dependency APIs first, then update the pinned submodule:

This issue stays blocked until those interfaces are available in a released libakstdlib revision.

Implementation order

  1. Add, test, document, and release the three libakstdlib interfaces; update the akbasic submodule pin.
  2. Add bounded current-program, boot-request, channel-redirection, and status state to the runtime.
  3. Extend scanner and parser support for compact # verbs, TO, drive and unit selectors, and Dartmouth bare names.
  4. Implement the Dartmouth program-name lifecycle and RUN "filename".
  5. Implement deterministic directory listings and recursive COPY.
  6. Implement OPEN, CLOSE, CMD, GET#, ST, and ST#.
  7. Implement host-mediated BOOT, the validated COLLECT no-op, and permanent destructive-command refusals.
  8. Update the user guide, verb reference, compatibility notes, architecture guide, and generated API documentation.

Acceptance tests

Add executable tests for:

  • Every accepted and refused drive or unit spelling.
  • Every Dartmouth named and unnamed command, including overwrite refusal and retained in-memory source after UNSAVE.
  • SAVE versus REPLACE and DSAVE.
  • Both file-rename syntaxes and both SCRATCH meanings.
  • Stable directory order, sizes, directory suffixes, wildcard filtering, empty directories, and inaccessible paths.
  • Recursive copies, the 32-level limit, self and descendant rejection, symlink refusal, and cleanup after an injected partial failure.
  • OPEN, CLOSE, CMD, compact channel spellings, character-at-a-time GET#, and EOF status.
  • RUN "filename", BASIC BOOT, external boot requests, embedded-host consumption, REPL-only enforcement, and direct one-path process invocation.
  • HEADER and BACKUP proving that no filesystem mutation occurred.
  • Documentation examples through the existing documentation harness.

Run both supported build configurations, the complete CTest suite, ASan and UBSan, coverage, and focused mutation testing for parser branches, recursive cleanup, channel state, and boot-mode transitions.

References


Tachikoma (Codex GPT-5, context window size not exposed)

## Problem akbasic exposes part of the Dartmouth and Commodore disk vocabulary, but several commands are still refused or absent even where the host filesystem can provide a safe and deterministic equivalent. At `c5d13f00f6fd2d6f4759d252b30165a533a1863e`: - `src/runtime_disk.c:381-400` refuses `DIRECTORY` and `CATALOG`. - `src/runtime_disk.c:697-738` refuses `HEADER`, `COLLECT`, `BACKUP`, and `BOOT`. - `src/runtime_disk.c:265-376` implements only the file forms of `SCRATCH`, `RENAME`, and `COPY`. - `src/runtime_commands.c:606-712` gives `DLOAD` and `DSAVE` no Dartmouth current-program-name contract. - `src/parser_commands.c:471-497` requires a special spaced `PRINT #` spelling and has no BASIC 2.0 `OPEN`, `CLOSE`, `CMD`, or `GET#` compatibility forms. - `docs/09-files-and-disk.md:125-141` and `docs/13-differences.md:196-202` document the missing surface. Functional consequence: programs written around either the Dartmouth timesharing workflow or Commodore disk channels need source changes, and directory traversal, recursive copies, and host boot requests cannot be expressed. Blast radius: high. This touches parsing, runtime modes, public runtime state, host integration, filesystem behavior, compatibility documentation, and dependency APIs. ## Inventory Dartmouth treated these as DTSS operating-system commands around BASIC rather than statements inside a stored program. They are still part of the requested interactive BASIC workflow: - `NEW [name]` - `OLD name` - `SAVE [name]` - `REPLACE [name]` - `RENAME name` - `CAT` and `CATALOG` - `UNSAVE [name]` - Bare `SCRATCH`, which clears the program in memory The Commodore BASIC 2.0 and 7.0 disk surface is: - File and program operations: `APPEND`, `BLOAD`, `BSAVE`, `CONCAT`, `COPY`, `DLOAD` or `LOAD`, `DSAVE` or `SAVE`, `DVERIFY` or `VERIFY`, `RECORD`, `RENAME`, `SCRATCH`, and `RUN "filename"` - Channels: `DOPEN`, `DCLOSE`, `OPEN`, `CLOSE`, `CMD`, `GET#`, `INPUT#`, and `PRINT#` - Directory and media commands: `CATALOG`, `DIRECTORY`, `DCLEAR`, `COLLECT`, `HEADER`, and `BACKUP` - Boot: `BOOT` akbasic already implements `APPEND`, `BLOAD`, `BSAVE`, `CONCAT`, file-only `COPY`, `DCLEAR`, `DCLOSE`, `DLOAD` or `LOAD`, `DOPEN`, `DSAVE` or `SAVE`, `DVERIFY` or `VERIFY`, `INPUT#`, `PRINT#`, `RECORD`, file-pair `RENAME`, and file `SCRATCH`. The remaining work is the Dartmouth name lifecycle, BASIC 2.0 channel aliases, `CMD`, `GET#`, `RUN "filename"`, directory listings, recursive `COPY`, the safe `COLLECT` mapping, and the host-mediated `BOOT` mapping. `HEADER` and `BACKUP` need explicit final refusal contracts because a host directory is not a Commodore disk. ## Behavioral contract ### Paths and drives - Reads resolve a relative path against the process working directory first, then against the loaded source directory. - Writes, deletes, renames, copies, and directory listings use the explicit path relative to the process working directory. - Parse `D0`, `U8`, and `ON U8`, but accept only drive 0 and unit 8. Refuse every other drive or device with `AKBASIC_ERR_DEVICE`. - Refuse symlinks for recursive operations rather than following them outside the requested tree. ### Dartmouth program names Store a bounded current-program path on `akbasic_Runtime`. - `NEW ["name"]` clears the program and optionally sets its current name. - `OLD name` loads the named program and makes that path current. - `SAVE [name]` writes the named or current program and refuses to overwrite an existing file. - `REPLACE [name]` writes the named or current program and permits replacement. - `UNSAVE [name]` removes the stored copy without changing the program in memory. - `RENAME name` changes only the current program name. - Accept bare names and quoted paths. - Bare `SCRATCH` clears the program while retaining the current name. `SCRATCH "path"` remains the Commodore file-delete form. - Preserve `RENAME "old","new"`; also accept the exact Commodore form `RENAME "old" TO "new"`. - Keep `DSAVE` as the explicit overwrite-capable Commodore save operation. ### Directory and copy operations Implement `DIRECTORY`, `CATALOG`, and `CAT` through one backend: - Sort entries bytewise for deterministic output. - Print directory names with a trailing `/`. - Print file byte sizes. - Accept an optional basename filter using `*` and `?`. - Do not depend on host enumeration order. Extend `COPY` to copy complete directory trees: - The destination must not exist; do not merge trees. - Reject copies onto the source or below the source. - Refuse symlinks. - Bound traversal depth at 32. - On failure, remove only destination objects created by that invocation. Never remove a pre-existing path. - This makes a host-filesystem `BACKUP` alias unnecessary; keep `BACKUP` refused as a physical-disk operation. ### BASIC 2.0 channels - `OPEN` supports disk device 8 only and maps its logical file and mode onto the existing bounded channel table. Refuse non-disk devices. - `CLOSE` maps to `DCLOSE`. - `CMD` redirects ordinary `PRINT` output to an open channel until an empty `PRINT#` or closure of that channel. - `GET#` reads one character at a time. - Add read-only `ST` and `ST#` status values and update them for successful reads and EOF. - Keep `INPUT#` as line-oriented input. - Accept the compact Commodore spellings `PRINT#`, `INPUT#`, and `GET#` without requiring a space before `#`. ### Media commands - `COLLECT` is a successful no-op after validating that only the default drive and unit were requested. A host filesystem owns its allocation metadata. - `HEADER` remains an `AKBASIC_ERR_DEVICE` refusal and must never delete or format a directory. - `BACKUP` remains an `AKBASIC_ERR_DEVICE` refusal and must never infer a physical-disk operation from host paths. ### RUN and BOOT - `RUN "filename"` clears the current program, loads the file, records it as current, and runs it. - Allow `BOOT` only as an unnumbered direct REPL command. - A case-insensitive `.bas` target performs `NEW`, `DLOAD`, and `RUN`. - Any other target records a bounded boot request and switches to a new `AKBASIC_MODE_BOOT`. - Add `akbasic_runtime_take_boot_request()` so an embedding host can consume the request. - The standalone driver may execute the request with direct `execv` and one filename. Never use a shell and never accept embedded argument text. - The library must never execute a process or exit. - Refuse numbered or stored `BOOT` statements. ## Dependency work Do not bypass libakstdlib with direct platform calls. Complete and release the dependency APIs first, then update the pinned submodule: - [libakstdlib issue 9: stat and file-type wrappers](https://source.starfort.tech/andrew/libakstdlib/issues/9) - [libakstdlib issue 10: bounded directory enumeration](https://source.starfort.tech/andrew/libakstdlib/issues/10) - [libakstdlib issue 11: direct process replacement](https://source.starfort.tech/andrew/libakstdlib/issues/11) This issue stays blocked until those interfaces are available in a released libakstdlib revision. ## Implementation order 1. Add, test, document, and release the three libakstdlib interfaces; update the akbasic submodule pin. 2. Add bounded current-program, boot-request, channel-redirection, and status state to the runtime. 3. Extend scanner and parser support for compact `#` verbs, `TO`, drive and unit selectors, and Dartmouth bare names. 4. Implement the Dartmouth program-name lifecycle and `RUN "filename"`. 5. Implement deterministic directory listings and recursive `COPY`. 6. Implement `OPEN`, `CLOSE`, `CMD`, `GET#`, `ST`, and `ST#`. 7. Implement host-mediated `BOOT`, the validated `COLLECT` no-op, and permanent destructive-command refusals. 8. Update the user guide, verb reference, compatibility notes, architecture guide, and generated API documentation. ## Acceptance tests Add executable tests for: - Every accepted and refused drive or unit spelling. - Every Dartmouth named and unnamed command, including overwrite refusal and retained in-memory source after `UNSAVE`. - `SAVE` versus `REPLACE` and `DSAVE`. - Both file-rename syntaxes and both `SCRATCH` meanings. - Stable directory order, sizes, directory suffixes, wildcard filtering, empty directories, and inaccessible paths. - Recursive copies, the 32-level limit, self and descendant rejection, symlink refusal, and cleanup after an injected partial failure. - `OPEN`, `CLOSE`, `CMD`, compact channel spellings, character-at-a-time `GET#`, and EOF status. - `RUN "filename"`, BASIC `BOOT`, external boot requests, embedded-host consumption, REPL-only enforcement, and direct one-path process invocation. - `HEADER` and `BACKUP` proving that no filesystem mutation occurred. - Documentation examples through the existing documentation harness. Run both supported build configurations, the complete CTest suite, ASan and UBSan, coverage, and focused mutation testing for parser branches, recursive cleanup, channel state, and boot-mode transitions. ## References - [Dartmouth BASIC command summary](https://www.dartmouth.edu/basicfifty/commands.html) - [Dartmouth BASIC manual, 1964](https://www.dartmouth.edu/basicfifty/basicmanual_1964.pdf) - [Commodore 128 Programmer Reference Guide](https://www.devili.iki.fi/pub/Commodore/docs/books/C128_Programmers_Reference_OCR.pdf) - [Commodore 128 System Guide disk-command appendix](https://www.commodore.ca/manuals/128_system_guide/app-l.htm) --- Tachikoma (Codex GPT-5, context window size not exposed)
tachikoma added this to the 0.2.0 milestone 2026-08-02 22:40:26 -04:00
tachikoma added the api-gapblast-radius:highstatus::blocked labels 2026-08-02 22:40:26 -04:00
Sign in to join this conversation.