Files
tsvm/doc/tvdos.tex
2026-06-06 22:04:13 +09:00

949 lines
58 KiB
TeX

\chapter{\thedos}
\thedos\ is a Disk Operating System (usually) bundled with the distribution of the \thismachine.
All \thedos-related features requires the DOS to be fully loaded.
\chapter{Bootstrapping}
\index{boot process}\thedos\ goes through the following progress to deliver the \code{A:\rs} prompt:
\section{Probing Bootable Devices}
The BIOS will probe serial devices to find the first bootable drive. If found, the port number of the drive is written to the \code{\_BIOS} object, then it attempts to load and run the bootloader.
\section{The Bootloader}
The Bootloader is a short program that loads the \code{TVDOS.SYS} file.
\section{TVDOS.SYS}
\thedos.SYS loads the system libraries and variables, installs the filesystem and input drivers, and then runs the boot script.
Boot Procedure:
\begin{enumerate}
\item define \code{\_TVDOS} objects
\item probe filesystem devices
\item initialise DOS variables
\item install filesystem drivers
\item install input device drivers
\item install \code{GL} using the external file
\item run \code{\rs{}commandrc} to set up the environment
\item hand the screen to the virtual-console manager, \code{\rs{}tvdos\rs{}VTMGR.SYS}
\item when the manager exits, run \code{\rs{}AUTOEXEC.BAT} as a bare fallback shell
\end{enumerate}
Steps 7--9 are run through \code{command.js}: \thedos.SYS loads \code{command.js}, which initialises the \code{shell.*} functions (the coreutils and a patched \code{require}), and then parses and runs the requested script.
\section{The Boot Configuration: commandrc and AUTOEXEC.BAT}
\index{commandrc}\index{AUTOEXEC.BAT}The boot configuration is split across two files with deliberately different jobs. The split exists because \thedos\ runs several independent shell sessions at once (see \emph{Virtual Consoles}): the environment must be established in \emph{every} session, but applications should be launched only \emph{once per session}.
\code{\rs{}commandrc} is the \textbf{environment} file. It holds \code{set} commands and nothing else, and \thedos.SYS runs it in every context --- the boot console and every virtual-console pane. Because it has no \code{.BAT} extension, the boot block runs it line by line. A typical \code{commandrc} configures the search paths and the keyboard layout:
\begin{lstlisting}
set PATH=\tbas;\hopper\bin;$PATH
set INCLPATH=\hopper\include;$INCLPATH
set HELPPATH=\hopper\help;$HELPPATH
set KEYBOARD=us_qwerty
\end{lstlisting}
\code{\rs{}AUTOEXEC.BAT} is the \textbf{per-console launch} script. It is run once for each console: by each virtual-console pane as it starts, and by the boot console as the fallback once the virtual-console manager has exited. It performs work that must happen per session --- registering the input method, then starting the interactive shell:
\begin{lstlisting}
command -fancy
\end{lstlisting}
Variables are set or changed with the \textbf{set} command. A value may refer to the previous value of a variable with \code{\$NAME}, as the \code{\$PATH} above does, which appends to rather than replaces the existing search path.
\chapter{Virtual Consoles}
\index{virtual consoles}\thedos\ runs up to six independent shell sessions at once, called \textbf{virtual consoles}. Only one is shown on the physical screen at a time; the others keep running in the background. This is managed by \code{VTMGR.SYS}, which the boot process starts automatically (see \emph{Bootstrapping}).
\section{Switching Consoles}
\begin{outline}
\1\textbf{Alt-1} through \textbf{Alt-6} switch to that console. A console is created the first time it is selected, and re-created if its shell has exited.
\1the \code{chvt} \emph{N} command switches to console \emph{N} from within a script or a running shell.
\1\textbf{Alt-0} shuts the virtual-console manager down entirely, after which the boot console's \code{AUTOEXEC.BAT} runs as a single bare shell.
\end{outline}
Console 1 is created at boot; consoles 2--6 are created on first use. Each console runs its own \code{command -fancy} shell, with its own working directory and screen, but they all share the same environment set up by \code{commandrc}. The prompt of consoles 2--6 is prefixed with \code{[\emph{N}]} so the active console is always identifiable.
\section{Concurrency}
Switching is truly concurrent: a console keeps running even while it is not on screen, and you can switch away from a long-running command and back again without interrupting it. A console that is waiting for keyboard input is parked until it is brought to the foreground.
\section{Well-behaved Applications}
Most programs need no special handling to run inside a virtual console, because they draw through the \code{con} library and the \code{print} family, which \thedos\ routes to the correct console automatically. The one exception is a program that writes to the text area \emph{directly} through \code{graphics.getGpuMemBase()}: such a program would paint the physical screen and bleed into whichever console is visible. Applications that need direct text-area access must resolve their writes through a console-aware base address; the bundled applications that do this are already adapted.
\chapter{Coreutils}
\index{coreutils (DOS)}Coreutils are the core ``commands'' of \thedos.
\begin{outline}
\1\dossynopsis{cat}[file]{Reads a file and pipes its contents to the pipe, or to the console if no pipes are specified.}
\1\dossynopsis{cd}[dir]{Change the current working directory. Alias: chdir}
\1\dossynopsis{chvt}[N]{Switches to virtual console \emph{N} (1--6). Only meaningful inside a virtual console. See \emph{Virtual Consoles}.}
\1\dossynopsis{cls}{Clears the text buffer and the framebuffer if available.}
\1\dossynopsis{cp}[from to]{Make copies of the specified file. The source file must not be a directory. Alias: copy}
\1\dossynopsis{date}{Prints the system date. Alias: time}
\1\dossynopsis{dir}[path]{Lists the contents of the specifed path, or the current working directory if no arguments were given. Alias: ls}
\1\dossynopsis{del}[file]{Deletes the file. Aliases: erase, rm}
\1\dossynopsis{echo}[text]{Print the given text or a variable.}
\1\dossynopsis{exit}{Exits the current command processor.}
\1\dossynopsis{mkdir}[path]{Creates a directory. Aliase: md}
\1\dossynopsis{mv}[from to]{Moves or renames the file. Aliase: move}
\1\dossynopsis{panic}{Deliberately raises an error in the command processor. A diagnostic aid.}
\1\dossynopsis{rem}{Comment-out the line.}
\1\dossynopsis{set}[key=value]{Sets the global variable \code{key} to \code{value}, or displays the list of global variables if no arguments were given.}
\1\dossynopsis{ver}{Prints the version of \thedos.}
\1\dossynopsis{which}[program]{Reports how a name would resolve: as a shell built-in, or as the full path of the executable found on the \code{PATH}. Alias: where}
\end{outline}
\chapter{Built-in Apps}
\index{built-in apps (DOS)}Built-in Applications are the programs shipped with the standard distribution of \thedos\ that are written for users' convenience.
This chapter lists the general-purpose applications. The applications for playing and converting media have a chapter of their own, \emph{Media Playback and Formats}.
\section{Shells and Languages}
\begin{outline}
\1\dossynopsis{command}{The default text-based \thedos\ shell. Call with \code{command -fancy} for a more \ae sthetically pleasing look.}
\1\dossynopsis{basica}{Invokes a BASIC interpreter stored in the ROM. If no BASIC ROM is present, nothing is done.}
\1\dossynopsis{basic}{If your system is bundled with a software-based BASIC, invokes the BASIC interpreter stored on the disk.}
\end{outline}
\section{Files and Text}
\begin{outline}
\1\dossynopsis{edit}[file]{The interactive full-screen text editor.}
\1\dossynopsis{zfm}{Z File Manager. A two-panel graphical interface for navigating the system with the arrow keys. Press Z to switch panels.}
\1\dossynopsis{less}[file]{Lets the user read long text, even when it is wider and/or taller than the screen. Supports pipes.}
\1\dossynopsis{hexdump}[file]{Prints the contents of a file in a hexadecimal view. Supports pipes.}
\1\dossynopsis{printfile}[file]{Prints the contents of a text file with line numbers. Useful for making descriptive screenshots.}
\1\dossynopsis{touch}[file]{Updates a file's modification date. The file is created if it does not exist.}
\1\dossynopsis{tee}[file]{In a pipe, copies the incoming stream to a file \emph{and} passes it on to the next command.}
\1\dossynopsis{writeto}[file]{In a pipe, writes the incoming stream to a file.}
\end{outline}
\section{Storage and Archives}
\begin{outline}
\1\dossynopsis{drives}{Lists the connected and mounted disk drives.}
\1\dossynopsis{defrag}{Defragments the current drive.}
\1\dossynopsis{gzip}[file]{Compresses a file in place, or decompresses it with \code{-d}, or writes to standard output with \code{-c}. As with the \code{gzip} library, the actual format used is Zstandard.}
\1\dossynopsis{lfs}{Creates and extracts \thedos\ Linear File Strip (\code{.lfs}) archives --- a simple way to bundle a directory tree into one file.}
\1\dossynopsis{autorun}[file]{Runs a program or plays a media file directly from a sequential (tape) device.}
\end{outline}
\section{Networking and Packages}
\begin{outline}
\1\dossynopsis{geturl}[url]{Reads the contents at a web address and stores it to the disk. Requires an Internet adapter.}
\1\dossynopsis{telcom}[port]{A terminal program for talking to a device or modem on a serial port.}
\1\dossynopsis{hopper}{The \thedos\ package manager, for installing and managing optional software. Alias: hop.}
\end{outline}
\section{Miscellaneous}
\begin{outline}
\1\dossynopsis{synopsis}[program]{Prints a command's one-line summary together with an auto-generated synopsis --- its usage line, arguments, options and constraints --- read from the command's \code{.synopsis} description. With no \code{program} it describes itself. Alias: help.}
\1\dossynopsis{color}{Changes the background and foreground colour of the active session.}
\1\dossynopsis{true}{Returns errorlevel 0 upon execution.}
\1\dossynopsis{false}{Returns errorlevel 1 upon execution.}
\end{outline}
The music tracker and editor (invoked as \code{microtone}) is also bundled, but it --- together with the music format it authors --- is large enough to warrant its own manual and is not covered here.
\chapter{Media Playback and Formats}
\index{media playback (DOS)}\thedos\ can display images, play video and play audio, in several formats. Decoding is hardware-accelerated by the graphics and audio adapters, so even the more sophisticated formats play back smoothly. This chapter is written for someone who wants to \emph{use} these formats; it describes what each is for and which command plays it, not how the codecs work internally.
Most players accept the \code{-i} flag, which turns on \textbf{interactive mode}: an on-screen progress bar, a visualiser where appropriate, and playback control. Without it, the player simply plays the file and exits. Across the players, holding \textbf{Backspace} stops playback.
\section{Still Images}
\index{iPF}The machine's native still-image format is \textbf{iPF} (Interchangeable Picture Format). The graphics hardware can also decode ordinary JPEG, PNG and TGA images directly.
\begin{outline}
\1\dossynopsis{decodeipf}[file]{Decodes an iPF image onto the framebuffer.}
\1\dossynopsis{encodeipf}[1/2 imagefile ipffile]{Encodes an image file (\code{.jpg}, \code{.png}, \code{.bmp}, \code{.tga}) into iPF, using the graphics hardware. The first argument selects the iPF type (1 or 2).}
\end{outline}
The TAV video format below also have still-picture variants, used for high-fidelity images.
\section{Video}
\thedos\ supports three families of video, in rough order of age and sophistication:
\begin{outline}
\1\textbf{MV1} --- the legacy movie format, carrying iPF (or plain palette) frames with MP2 audio. Created on the machine with \code{encodemov} / \code{encodemov2}.
\1\textbf{TEV} (\thismachine\ Enhanced Video) --- a modern format with markedly better compression than iPF movies, taking full advantage of the 4096-colour hardware.
\1\textbf{TAV} (\thismachine\ Advanced Video) --- the current format and successor to TEV, offering the best compression and image quality.
\end{outline}
TEV and TAV files are prepared on a host computer and copied to the disk for playback; MOV and iPF content can also be produced on the machine itself. Both TEV and TAV are encoded at a chosen \emph{quality level} when they are made, trading file size against fidelity --- as a viewer you simply play the result.
\begin{outline}
\1\dossynopsis{playmv1}[file]{Plays a MV1-format movie.}
\1\dossynopsis{playtev}[file]{Plays a TEV-format video.}
\1\dossynopsis{playtav}[file]{Plays a TAV-format video.}
\1\dossynopsis{movprobe}[file]{Prints a movie's properties (dimensions, frame rate, audio, \dots) without playing it.}
\1\dossynopsis{playucf}[file]{Plays a chaptered movie (UCF), presenting a chapter selector.}
\end{outline}
\section{Audio}
\index{MP2}\index{TAD}For sound, \thedos\ plays standard \textbf{MP2} (MPEG-1 Audio Layer II), raw and wave-wrapped PCM, and its own compressed format, \textbf{TAD} (\thismachine\ Advanced Audio). All audio plays back at the hardware's 32\,kHz stereo. As with video, TAD files are prepared on a host computer; MP2 is a widely interchangeable format, and PCM/WAV are uncompressed.
\begin{outline}
\1\dossynopsis{playmp2}[file]{Plays MP2 (MPEG-1 Audio Layer II) audio.}
\1\dossynopsis{playtad}[file]{Plays TAD audio.}
\1\dossynopsis{playpcm}[file]{Plays raw PCM audio.}
\1\dossynopsis{playwav}[file]{Plays linear-PCM or ADPCM \code{.wav} audio.}
\end{outline}
\section{Music}
\index{Taud}\thismachine\ has a native tracker music format, \textbf{Taud}, played by the built-in tracker engine (see the \emph{Audio} chapter in the \thismachine\ part). Unlike the streamed audio formats above, a Taud file is a compact \emph{score} --- samples plus the patterns that sequence them --- so a whole piece of music occupies very little space.
\begin{outline}
\1\dossynopsis{playtaud}[file]{Plays a Taud module, with a text-mode visualiser. An optional second argument selects which song within the file to play. Hold Backspace to exit.}
\end{outline}
Authoring Taud music, and the format itself, are the subject of a separate manual and are not described here.
\chapter{Writing Your Own Apps}
\index{user apps (DOS)}User-made Applications are basically a standard Javascript program, but \thedos\ provides DOS extensions for convenience.
User apps are invoked through \thedos\ to inject the command-line arguments and some necessary functionalities.
\section{Command-line Arguments}
The command line arguments are given via the array of strings named `exec\_args`.
Index zero holds the name used to invoke the app, and the rest hold the actual arguments.
\section{Describing Your App: the Synopsis File}
\index{synopsis file}Every command should ship a machine-readable description of its own command-line interface, written in the \thedos\ Synopsis Format (TSF) and described in full in the \emph{Command Synopsis Format} chapter.
The rule is simple: the synopsis lives \emph{next to} the program, with the program's full filename plus a \code{.synopsis} extension. An app installed as \code{cp.js} in \code{\rs{}tvdos\rs{}bin} therefore ships alongside it as \code{\rs{}tvdos\rs{}bin\rs{}cp.js.synopsis}. \thedos\ uses these files to generate help text and tab-completion, so providing one makes your app a first-class citizen of the system.
Built-in coreutils have no on-disk executable to sit beside, so their synopses live together in \code{\rs{}tvdos\rs{}synopsis}, named for the command (for example \code{\rs{}tvdos\rs{}synopsis\rs{}cp.synopsis}). Either way, once a synopsis is in place the \code{synopsis} command --- and its \code{help} alias --- will display it, and the shell will tab-complete against it.
\section{Invoking Coreutils on the user Apps}
DOS coreutils and some of the internal functions can be used on Javascript program.
To invoke the coreutils, use \code{\_G.shell.coreutils.*}
\begin{outline}
\1\inlinesynopsis[\_G.shell]{resolvePathInput}[path]{Returns path object for the input path, relative to the current working directory. Object contains:}
\2\argsynopsis{full}{fully-qualified path}
\2\argsynopsis{string}{fully-qualified path without the drive letter}
\2\argsynopsis{drive}{drive letter of the path}
\2\argsynopsis{pwd}{working directory for the path}
\1\inlinesynopsis[\_G.shell]{getPwdString}[]{Returns the current working directory as a string.}
\1\inlinesynopsis[\_G.shell]{getCurrentDrive}[]{Returns the drive letter of the current working drive.}
\1\inlinesynopsis[\_G.shell]{execute}[command]{Executes the DOS command.}
\end{outline}
\section{Termination Check Injection}
Due to the non-preemptive nature of the virtual machine, the termination\footnote{Default key combination: Shift+Ctrl+T+R} signal must be explicitly captured by the app, and taking care of it by yourself can be extremely tedious. Fortunately \thedos\ parses the user-written program and injects those checks accordingly.
While- and For-loops are always have such checks injected, but the `read()` is not checked for the termination.
\chapter{Command Synopsis Format}
\label{ch:tsf}
\index{TSF}\index{synopsis format}The \thedos\ Synopsis Format (TSF) is the language in which a command describes its own command-line interface. Every command is expected to ship a TSF document --- a file with the command's full name plus a \code{.synopsis} extension, stored in the same directory as the program. The command \code{cp.js} in \code{\rs{}tvdos\rs{}bin}, for instance, is accompanied by \code{cp.js.synopsis} in that same directory. \thedos\ reads these documents to drive shell completion, help generation, and argument validation.
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY and OPTIONAL in this chapter are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in all capitals.
\section{Scope}
A TSF document describes a command's grammar: its options and flags, positional arguments, subcommands, argument types, completion sources and validation constraints.
A TSF document MUST be valid JSON, and MUST be encoded so that its byte stream contains only ASCII characters: any character outside the ASCII range (U+0000--U+007F) MUST be written as a JSON \texttt{\textbackslash u} escape (a backslash, \texttt{u}, and four hexadecimal digits) rather than as a literal multibyte character. Consumers MUST decode such escapes per the JSON specification.
\section{Design Goals}
TSF SHALL be machine-readable, human-authorable, and able to support automatic shell completion, automatic help generation, parser generation and GUI generation.
The structured synopsis grammar SHALL be the sole normative description of command syntax; every other representation, including human-readable usage strings, is treated as output generated from it.
\section{Document Structure}
A TSF document SHALL contain one JSON object.
\begin{lstlisting}
{
"tsfVersion": "1.0",
"name": "cp",
"summary": "Copy files and directories",
"symbols": {},
"synopsis": {}
}
\end{lstlisting}
Its fields are:
\begin{tabulary}{\textwidth}{lclL}
Field & Req. & Type & Notes \\
\hline
tsfVersion & yes & string & Version of TSF this document targets. \\
name & yes & string & Command name as invoked. \\
summary & yes & string & One-line description. \\
symbols & yes & object & The symbol table. \\
synopsis & yes & object & The synopsis grammar root node. \\
description & no & string & Free-form long description for help generation. \\
constraints & no & array & Constraint objects. \\
metadata & no & object & Free-form, non-normative data for authors and hosts. \\
\end{tabulary}
\section{The Symbol Table}
All command elements SHALL be declared in the symbol table, and the synopsis grammar SHALL reference them by identifier.
\begin{lstlisting}
"symbols": {
"recursive": { "kind": "option", "long": "--recursive", "short": "-r" },
"source": { "kind": "positional", "type": "path" }
}
\end{lstlisting}
Every symbol has a \code{kind}, one of \code{option}, \code{positional}, \code{subcommand} or \code{group}.
\section{Argument Descriptors}
An \emph{argument descriptor} describes a single consumed value. The same shape is used in two places: directly on a \code{positional} symbol, and as the \code{value} of an \code{option} symbol.
\begin{tabulary}{\textwidth}{lclL}
Field & Req. & Type & Notes \\
\hline
type & no & string & One of the built-in types. Defaults to \code{string}. \\
name & no & string & Metavar shown in generated usage (e.g.\ FILE, WHEN). \\
values & cond. & array & Permitted values; REQUIRED when \code{type} is \code{enum}. \\
default & no & any & Default value, for help and GUI prefill. \\
validation & no & object & Value-level validation (below). \\
completion & no & object & Completion override. \\
summary & no & string & Short description of the value. \\
\end{tabulary}
Each entry in \code{values} SHALL be either a bare JSON value or an object \code{\{ "value": v, "summary": s \}}; the optional per-value summary feeds completion hints and help.
\subsection{Validation}
The \code{validation} object expresses checks the grammar cannot:
\begin{tabulary}{\textwidth}{lL}
Field & Notes \\
\hline
pattern & A regular expression the value MUST match (string-like types). \\
minimum & Inclusive lower bound (numeric types). \\
maximum & Inclusive upper bound (numeric types). \\
minLength & Minimum length in characters (string-like types). \\
maxLength & Maximum length in characters (string-like types). \\
\end{tabulary}
The regular-expression flavour for \code{pattern} is implementation-defined; authors are RECOMMENDED to keep to a portable subset. As the document is ASCII-only, any non-ASCII character within a pattern MUST be escaped.
\section{Option Symbols}
\begin{lstlisting}
"output": {
"kind": "option",
"long": "--output",
"short": "-o",
"summary": "Write output to FILE",
"value": { "name": "FILE", "type": "file", "required": true }
}
\end{lstlisting}
\begin{tabulary}{\textwidth}{lclL}
Field & Req. & Type & Notes \\
\hline
kind & yes & string & \code{option}. \\
long & cond. & string & Long form, e.g.\ \code{--recursive}. \\
short & cond. & string & Short form, e.g.\ \code{-r}. \\
summary & no & string & One-line description. \\
value & no & object & Argument descriptor. Omit for a bare flag. \\
negatable & no & boolean & If true, a \code{--no-<long>} form is also accepted. \\
\end{tabulary}
At least one of \code{long} or \code{short} SHALL exist. The \code{value} object MAY carry a \code{required} field (default true): when false, the argument is optional, as in \code{--color} with or without \code{=WHEN}. How often an option may repeat is expressed in the grammar (via \code{repeat} or \code{oneOrMore}), not by a field on the symbol.
\section{Positional Symbols}
A positional symbol is an argument descriptor plus its \code{kind}.
\begin{lstlisting}
"source": { "kind": "positional", "type": "path", "summary": "Source file" }
\end{lstlisting}
Its fields are \code{kind} (\code{positional}) followed by the argument-descriptor fields (\code{type}, \code{name}, \code{values}, \code{default}, \code{validation}, \code{completion}, \code{summary}). Whether a positional is required or optional is expressed in the grammar by wrapping it in \code{optional} or not, keeping a single source of truth.
\section{Subcommand Symbols}
\begin{lstlisting}
"clone": { "kind": "subcommand", "summary": "Clone repository", "tsf": "git.clone" }
\end{lstlisting}
A subcommand MAY reference an embedded or an external TSF document through its \code{tsf} field; resolution of that reference is implementation-specific.
\section{Group Symbols}
A group collects related symbols --- typically options --- so the grammar can refer to them collectively. A group is what backs the conventional \code{[OPTION...]} slot.
\begin{lstlisting}
"commonOptions": {
"kind": "group",
"summary": "Common options",
"members": ["recursive", "force", "verbose"]
}
\end{lstlisting}
A group has \code{kind} (\code{group}), a \code{members} array of symbol identifiers, and an optional \code{summary}. A reference to a group is equivalent to a \code{choice} over its members; wrapping that reference in \code{repeat} yields the familiar ``any number of these options, in any order'' behaviour.
\section{The Synopsis Grammar}
The \code{synopsis} object describes valid invocations as a tree of nodes. Every node has a \code{type}:
\begin{tabulary}{\textwidth}{lL}
Node & Meaning \\
\hline
sequence & All children appear in order. Has a \code{children} array. (\code{A B C}) \\
choice & Exactly one child appears. Has a \code{children} array. (\code{(A | B | C)}) \\
optional & The single \code{child} appears zero or one time. (\code{[A]}) \\
repeat & The single \code{child} appears zero or more times. (\code{A...}) \\
oneOrMore & The single \code{child} appears at least once; sugar for \code{sequence[A, repeat[A]]}. (\code{A [A...]}) \\
reference & References a \code{symbol} by identifier. A reference to a group expands to a \code{choice} over its members. \\
\end{tabulary}
\section{A Complete Example}
The human-readable form \code{cp [OPTION...] SOURCE DEST} is expressed as:
\begin{lstlisting}
"symbols": {
"recursive": { "kind": "option", "long": "--recursive", "short": "-r" },
"force": { "kind": "option", "long": "--force", "short": "-f" },
"options": { "kind": "group", "members": ["recursive", "force"] },
"source": { "kind": "positional", "type": "path", "name": "SOURCE" },
"destination": { "kind": "positional", "type": "path", "name": "DEST" }
},
"synopsis": {
"type": "sequence",
"children": [
{ "type": "repeat", "child": { "type": "reference", "symbol": "options" } },
{ "type": "reference", "symbol": "source" },
{ "type": "reference", "symbol": "destination" }
]
}
\end{lstlisting}
Because \code{options} is a declared symbol, the \code{[OPTION...]} slot satisfies the rule that every referenced element exists in the symbol table.
\section{Argument Types}
The built-in primitive types are \code{string}, \code{integer}, \code{float}, \code{boolean}, \code{path}, \code{file}, \code{directory}, \code{url}, \code{hostname}, \code{user}, \code{group}, \code{command} and \code{enum}.
A descriptor whose \code{type} is \code{enum} SHALL provide a \code{values} array. The \code{values} array MAY also be supplied for non-\code{enum} types as a soft suggestion list that informs completion without restricting valid input. Unknown types SHALL be interpreted as \code{string}; implementations are RECOMMENDED to emit a diagnostic when they do, since an unknown type is usually an authoring error. Each type carries a default completion behaviour --- \code{path}, \code{file} and \code{directory} complete against the filesystem, \code{user} and \code{group} against the account databases, \code{enum} against its \code{values} --- which a \code{completion} block overrides.
\section{Completion}
If a descriptor has no \code{completion} block, completion is derived automatically from its \code{type}. A block overrides that default and names a \code{method}:
\begin{tabulary}{\textwidth}{lL}
method & Notes \\
\hline
type & Use the default completion implied by the \code{type}. (Implicit when no block is present.) \\
enum & Complete from the descriptor's \code{values}. (Implicit when \code{type} is \code{enum}.) \\
internal & Use a named provider resolved by the host (a \code{provider} field names it). \\
command & Run a command whose output supplies the candidates. \\
list & Offer a static inline list of suggestions, without restricting input. \\
none & Suppress completion for this value. \\
\end{tabulary}
\section{Constraints}
Constraints describe relationships not expressible in the grammar, and are listed in the root \code{constraints} array. Symmetric constraints use a \code{symbols} field; asymmetric constraints use \code{subject} and \code{targets}.
\begin{outline}
\1\inlinesynopsis{conflicts}{symmetric --- the listed \code{symbols} are mutually exclusive.}
\1\inlinesynopsis{requires}{asymmetric --- if \code{subject} is present, every symbol in \code{targets} MUST also be present.}
\1\inlinesynopsis{implies}{asymmetric derivation --- if \code{subject} is present, every symbol in \code{targets} is implicitly set (a side effect, not a rejection).}
\1\inlinesynopsis{cardinality}{symmetric --- constrains how many of the listed \code{symbols} may appear, via \code{minimum} and \code{maximum}.}
\end{outline}
Three of these (\code{conflicts}, \code{requires}, \code{cardinality}) are validation predicates that decide whether an invocation is well-formed; \code{implies} instead sets a value.
\begin{lstlisting}
"constraints": [
{ "type": "conflicts", "symbols": ["stdout", "output"] },
{ "type": "cardinality", "symbols": ["create", "extract", "list"],
"minimum": 1, "maximum": 1 }
]
\end{lstlisting}
\section{Generated Usage and Compatibility}
Implementations SHOULD generate human-readable usage text from the synopsis grammar; that text is non-authoritative output, the grammar remaining the sole normative description.
TSF distinguishes additive content, which may be ignored safely, from structural content, which may not. Unknown \emph{fields} on otherwise-valid objects SHALL be ignored, so future minor versions MAY add fields without breaking existing documents or consumers. Unknown \emph{grammar node types} and unknown \emph{symbol kinds} SHALL cause the document to be rejected or to enter an explicitly defined degraded mode, because ignoring them would silently change the set of accepted invocations.
Authors and hosts that need to attach implementation-specific data SHOULD do so inside the root \code{metadata} object or under field names prefixed with \code{x-}; names without that prefix are reserved for future versions of this specification. Consumers SHOULD report the highest \code{tsfVersion} they support so producers can downgrade gracefully.
\section{How \thedos\ Uses Synopsis Documents}
A synopsis document is put to work in three ways. The shell uses it for \emph{tab-completion}: when you press Tab part-way through an argument, the grammar tells the shell whether to offer option flags, a fixed set of \code{enum} values, a subcommand name, or filesystem entries (for the \code{path}, \code{file} and \code{directory} types). The \code{synopsis} command --- and its \code{help} alias --- uses it for \emph{help}, printing the summary, a generated usage line, and the arguments, options and constraints. Authors may further use the \code{validation} and \code{constraints} sections to check an invocation before acting on it.
\thedos\ finds a command's document the same way it finds the command itself: an app's synopsis sits next to its executable (\code{geturl.js} beside \code{geturl.js.synopsis}), while the built-in coreutils, having no file of their own, keep theirs in \code{\rs{}tvdos\rs{}synopsis}. Aliases resolve to the canonical command's document, so \code{ls} is described by \code{dir.synopsis}. Parsed documents are cached under \code{\rs{}tvdos\rs{}cache\rs{}synopsis} so that repeated completions and help lookups stay fast; the cache is rebuilt on demand and may be deleted at any time.
\chapter{Pipes}
\index{pipe (DOS)}Pipe is a way to chain the IO of the one program/command into the different programs/commands in series.
A pipe can be either named or anonymous: named pipes are ones that are created by the user while the anonymous pipes are created by the DOS process as a result of the command pipelining.
\section{Command Pipelining}
In \thedos, a pipe can be used to route the output of a command into the other command. For example, \code{dir | less} will route the output of the \code{dir} into the text viewer called \code{less} so that the user can take their time examining the list of files in the directory, even if the list is taller that the terminal's height.
\section{User-defined Pipe}
A user program can create and interact with the pipe so long as it's \emph{named}. The contents of the pipe can be read and modified just like a Javascript variable.
Named pipes can be retrieved on \code{\_G.shell.pipes.*}
\section{Pipe-related Functions}
\begin{outline}
\1\inlinesynopsis[\_G.shell]{getPipe}[]{Returns the currently opened pipe. \code{undefined} is returned if no pipes are opened.}
\1\inlinesynopsis[\_G.shell]{appendToCurrentPipe}[text]{Appends the given text to the current pipe.}
\1\inlinesynopsis[\_G.shell]{pushAnonPipe}[contents]{Pushes an anonymous pipe to the current pipe stack.}
\1\inlinesynopsis[\_G.shell]{pushPipe}[pipeName, contents]{Pushes the pipe of given name to the current pipe stack.}
\1\inlinesynopsis[\_G.shell]{hasPipe}[]{Returns true if there is a pipe currently opened.}
\1\inlinesynopsis[\_G.shell]{removePipe}[]{Destroys the currently opened pipe and returns it. Any pipes on the pipe stack will be shifted down to become the next current pipe.}
\end{outline}
\chapter{File I/O}
\index{filesystem (DOS)}In \thedos, drives are assigned with a drive letter, and the drive currently booted on is always drive \textbf{A}.
\section{The File Descriptor}
\index{file descriptor (DOS)}A file is virtualised through the \emph{file descriptor} which provides the functions to manipulate the file. Do note that when a file descriptor is created, the file is not yet opened by the drive.
To create a file descriptor, use the provided function \code{files.open(fullPath)}. \code{fullPath} is a fully qualified path of the file that includes the drive letter.
\section{Manipulating a File}
A file has folliwing properties and can be manipulated using following functions:
Properties:
\begin{outline}
\1\propertysynopsis{size}{Int}{Returns a size of the file in bytes.}
\1\propertysynopsis{path}{String}{Returns a path (NOT including the drive letter) of the file. Paths are started with, and separated using reverse solidus.}
\1\propertysynopsis{fullPath}{String}{Returns a fully qualified path (including the drive letter) of the file. Paths are separated using reverse solidus.}
\1\propertysynopsis{driverID}{String}{Returns a filesystem driver ID associated with the file.}
\1\propertysynopsis{driver}{[Object object]}{Returns a filesystem driver (a Javascript object) for the file.}
\1\propertysynopsis{isDirectory}{Boolean}{Returns true if the path is a directory.}
\1\propertysynopsis{name}{String}{Returns the name part of the file's path.}
\1\propertysynopsis{parentPath}{String}{Returns a parent path of the file.}
\1\propertysynopsis{exists}{Boolean}{Returns true if the file exists on the device.}
\end{outline}
Functions:
\begin{outline}
\1\formalsynopsis{pread}{pointer: Int, count: Int, offset: Int}{Reads the file bytewise and puts it to the memory starting from the pointer.}
\2\argsynopsis{count}{how many bytes to read}
\2\argsynopsis{offset}{when reading a file, how many bytes to skip initially}
\1\formalsynopsis{bread}{}[Array]{Reads the file bytewise and returns the content in Javascript array.}
\1\formalsynopsis{sread}{}[String]{Reads the file textwise and returns the content in Javascript string.}
\1\formalsynopsis{pwrite}{pointer: Int, count: Int, offset: Int}
{Writes the bytes stored in the memory starting from the pointer to file.\\Note: due to the limitation of the protocol, the non-zero offset is not supported on the serial-connected disk drives.}
\2\argsynopsis{count}{how many bytes to write}
\2\argsynopsis{offset}{when writing to the file, how many bytes on the file to skip before writing a first byte.}
\1\formalsynopsis{bwrite}{bytes: Int8Array}{Writes the bytes to the file.}
\1\formalsynopsis{swrite}{string: String}{Writes the string to the file.}
\1\formalsynopsis{pappend}{pointer: Int, count: Int}{Appends the bytes stored in the memory starting from the pointer to the end of the file.}
\2\argsynopsis{count}{how many bytes to write}
\1\formalsynopsis{bappend}{bytes: Int8Array}{Appends the bytes to the end of the file.}
\1\formalsynopsis{sappend}{string: String}{Appends the string to the end of the file.}
\1\formalsynopsis{flush}{}{Flush the contents on the write buffer to the file immediately. Will do nothing if there is no write buffer implemented --- a write operation will always be performed imemdiately in such cases.}
\1\formalsynopsis{close}{}{Tells the underlying device (usually a disk drive) to close a file. When dealing with multiple files on a single disk drive (in which can only have a single active---or opened---file), the underlying filesystem driver will automatically swap the files around, so this function is normally unused.}
\1\formalsynopsis{list}{}[Array or undefined]{Lists files inside of the directory. If the path is indeed a directory, an array of file descriptors will be returned; \code{undefined} otherwise.}
\1\formalsynopsis{touch}{}[Boolean]{Updates the file's access time if the file exists; a new file will be created otherwise. Returns true if successful.}
\1\formalsynopsis{mkDir}{}[Boolean]{Creates a directory to the path. Returns true if successful.}
\1\formalsynopsis{mkFile}{}[Boolean]{Creates a new file to the path. Returns true if successful.}
\1\formalsynopsis{remove}{}[Boolean]{Removes a file. Returns true if successful.}
\end{outline}
\section{The Device Files}
\index{device file}Some devices are also virtualised through the file descriptor, and they are given a special drive letter of \code{\$}. (e.g. \code{\$:\rs{}RND})
\begin{outline}
\1\inlinesynopsis{RND}{returns random bytes upon reading}
\2\argsynopsis{pread}{returns the specified number of random bytes}
\1\inlinesynopsis{NUL}{returns EOF upon reading}
\2\argsynopsis{pread}{returns the specified number of EOFs}
\2\argsynopsis{bread}{returns an empty array}
\2\argsynopsis{sread}{returns an empty string}
\1\inlinesynopsis{ZERO}{returns zero upon reading}
\2\argsynopsis{pread}{returns the specified number of zeros}
\1\inlinesynopsis{TMP}{a scratch area for temporary files, addressed as \code{\$:\rs{}TMP\rs{}\dots}. Files written here are not expected to persist.}
\1\inlinesynopsis{CON}{manipulates the screen text buffer, disregarding the colours}
\2\argsynopsis{pread}{reads the texts as bytes.}
\2\argsynopsis{bread}{reads the texts as bytes.}
\2\argsynopsis{sread}{reads the texts as a string.}
\2\argsynopsis{pwrite}{writes the bytes from the given pointer.}
\2\argsynopsis{bwrite}{identical to \code{print()} except the given byte array will be casted to string.}
\2\argsynopsis{swrite}{identical to \code{print()}.}
\1\inlinesynopsis{FBIPF}{decodes IPF-formatted image to the framebuffer. Use the bundled \code{encodeipf.js} for the encoding.}
\2\argsynopsis{pwrite, bwrite}{decodes the given IPF binary data. Offsets and counts for \code{pwrite} are ignored.}
\end{outline}
\chapter{Input Event Handling}
\thedos\ provides the library for handling the keyboard and mouse events.
\dosnamespaceis{Input}{input}
\begin{outline}
\1\inlinesynopsis{changeKeyLayout}[layoutName]{Changes the key layout. The key layout file must be stored as \code{A:\rs{}tvdos\rs{}layoutName.key}}
\1\inlinesynopsis{withEvent}[callback]{Invokes the callback function when an input event is available.}
\end{outline}
\subsection{Input Events}
An input event is a Javascript array of the form $$ [\mathrm{event\ name,\ arg_1,\ arg_2 \cdots arg_n}] $$ where the event name is one of \textbf{key\_down}, \textbf{key\_change}, \textbf{mouse\_down}, \textbf{mouse\_up}, \textbf{mouse\_move} or \textbf{mouse\_wheel}. Every event ends with the current key-press buffer (\code{keycode0} through \code{keycode7}), so a handler can detect modifier keys held during a mouse action.
\begin{outline}
\1arguments for \textbf{key\_down}:
\2\argsynopsis{\argN{1}}{Key symbol (string) of the head key}
\2\argsynopsis{\argN{2}}{Repeat count of the key event}
\2\argsynopsis{\argN{3}..\argN{10}}{The keycodes of the pressed keys}
\1arguments for \textbf{key\_change} (a key was released):
\2\argsynopsis{\argN{1}}{Key symbol (string) of the key that went up}
\2\argsynopsis{\argN{2}}{0}
\2\argsynopsis{\argN{3}..\argN{10}}{The keycodes of the keys still held down}
\1arguments for \textbf{mouse\_down} / \textbf{mouse\_up}:
\2\argsynopsis{\argN{1}}{X-position of the mouse cursor}
\2\argsynopsis{\argN{2}}{Y-position of the mouse cursor}
\2\argsynopsis{\argN{3}}{Button mask: 1 = left, 2 = right, 4 = middle (for \textbf{mouse\_up}, the button that was released)}
\2\argsynopsis{\argN{4}..}{The key-press buffer}
\1arguments for \textbf{mouse\_move}:
\2\argsynopsis{\argN{1}}{X-position of the mouse cursor}
\2\argsynopsis{\argN{2}}{Y-position of the mouse cursor}
\2\argsynopsis{\argN{3}}{Currently-held button mask (non-zero while dragging)}
\2\argsynopsis{\argN{4}}{X-position of the mouse cursor on the previous frame}
\2\argsynopsis{\argN{5}}{Y-position of the mouse cursor on the previous frame}
\2\argsynopsis{\argN{6}..}{The key-press buffer}
\1arguments for \textbf{mouse\_wheel}:
\2\argsynopsis{\argN{1}}{X-position of the mouse cursor}
\2\argsynopsis{\argN{2}}{Y-position of the mouse cursor}
\2\argsynopsis{\argN{3}}{$-1$ for a wheel notch up, $+1$ for a notch down}
\2\argsynopsis{\argN{4}..}{The key-press buffer}
\end{outline}
\chapter{The Graphics Library}
\thedos\ provides the library for drawing pixels to the screen.
\dosnamespaceis{Graphics}{GL}
Classes:
\begin{outline}
\1\inlinesynopsis[new GL]{Texture}[width: Int, height: Int, bytes: Int8Array]{Creates an GL Texture.}
\1\inlinesynopsis[new GL]{MonoTex}[width: Int, height: Int, bytes: Int8Array]{Creates an 1bpp Texture.}
\1\inlinesynopsis[new GL]{SpriteSheet}[tileWidth: Int, tileHeight: Int, texture: Texture or MonoTex]{Creates an Spritesheet backed by the given texture.}
\end{outline}
Functions:
\begin{outline}
\1\formalsynopsis{drawTexImage}{texture, x, y, framebuffer, fgcol, bgcol}{Draws the texture to the framebuffer. Transparency will be ignored.}
\2\argsynopsis{texture}{A pattern to draw. Must be an instance of the GL.Texture or GL.MonoTex.}
\2\argsynopsis{x, y}{Top-left position of the painting area}
\2\argsynopsis{framebuffer}{The target framebuffer on which the patterns are to be painted}
\2\argsynopsis{fgcol, bgcol}{Fore- and background colour for the GL.MonoTex.}
\1\formalsynopsis{drawTexImageOver}{texture, x, y, framebuffer, fgcol}{Same as the \code{drawTexImage} except the transparency will be taken into account.}
\1\formalsynopsis{drawTexPattern}{texture, x, y, width, height, framebuffer, fgcol, bgcol}{Fills the given area with the texture by tiling it. Transparency will be ignored.}
\2\argsynopsis{texture}{A pattern to draw. Must be an instance of the GL.Texture or GL.MonoTex.}
\2\argsynopsis{x, y}{Top-left position of the painting area}
\2\argsynopsis{width, height}{Width and the height of the painting area}
\2\argsynopsis{framebuffer}{The target framebuffer on which the patterns are to be painted}
\2\argsynopsis{fgcol, bgcol}{Fore- and background colour for the GL.MonoTex.}
\1\formalsynopsis{drawTexPatternOver}{texture, x, y, width, height, framebuffer, fgcol}{Same as the \code{drawTexPattern} except the transparency will be taken into account.}
\1\formalsynopsis{drawSprite}{sheet, xi, yi, x, y, framebuffer, overrideFG, overrideBG}{Paints the sprite to the framebuffer. Transparency will be ignored.}
\2\argsynopsis{xi, yi}{XY-index in the Spritesheet, zero-based.}
\2\argsynopsis{x, y}{Top-left position on the framebuffer where the sprite will be drawn into.}
\2\argsynopsis{overrideFG, overrideBG}{Optional; if specified, non-transparent pixel in the sprite will take the foreground, and the transparent ones will take the background colour instead of their original colours.}
\1\formalsynopsis{drawSpriteOver}{sheet, xi, yi, x, y, framebuffer, overrideFG}{Same as the \code{drawSprite} except the transparency will be taken into account.}
\end{outline}
\chapter{External Libraries}
External libraries are packaged codes with the intention of being re-used by other programs, and can be loaded using the \code{require()} function.
\section{Loading the Libraries}
External libraries can be stored in following locations:
\begin{enumerate}
\item \code{A:\rs{}tvdos\rs{}include}, the home of the libraries bundled with \thedos
\item any directory listed in the \code{INCLPATH} variable (configured in \code{commandrc})
\item a path relative to the user program
\item an absolute path that can be anywhere
\end{enumerate}
and can be loaded by:
\begin{enumerate}
\item \code{let name = require(libraryname)} // no .mjs extension; searches the include directory and INCLPATH
\item \code{let name = require(./libraryname)} // the relative path must start with a dot-slash
\item \code{let name = require(A:/path/to/library.mjs)} // full path WITH the .mjs extension
\end{enumerate}
\section{Writing Your Own Libraries}
Codes in the library must be exported to be made available to other programs, and \thedos\ provides \code{exports} variable for the purpose.
Functions and variables can be exported by defining the \code{exports} object; example code:
\begin{lstlisting}
function foo() {
println("Hello, module!")
}
const BAR = 127
// following line exports the function and the variable
exports = { foo, BAR }
\end{lstlisting}
\chapter{Bundled Libraries}
\index{bundled libraries (DOS)}\thedos\ ships a set of ready-made libraries in \code{A:\rs{}tvdos\rs{}include}. Each is loaded with \code{require} using its base name --- \code{require("fs")}, \code{require("psg")}, and so on --- and returns the object the library exports. This chapter documents each in turn.
\section{fs --- Filesystem (NodeJS-compatible)}
\index{fs (library)}\code{fs} wraps the \thedos\ file interface in the familiar NodeJS \code{fs} API. Synchronous (\code{*Sync}) calls, callback-style asynchronous calls, and a \code{promises} namespace are all provided; because the machine has no real concurrency, the ``asynchronous'' calls execute immediately and then invoke the callback or resolve the promise. Binary data is exchanged as \code{Uint8Array}; supplying an encoding (\code{"utf8"}, \code{"binary"}, \dots) exchanges strings instead.
\begin{lstlisting}
let fs = require("fs")
let txt = fs.readFileSync("A:/etc/motd", "utf8")
fs.writeFileSync("A:/tmp/hello.txt", "hi", "utf8")
fs.readdirSync("A:/").forEach(println)
\end{lstlisting}
Frequently used members:
\begin{outline}
\1\inlinesynopsis{readFileSync}[path, options]{reads an entire file.}
\1\inlinesynopsis{writeFileSync}[path, data, options]{writes (and truncates) a file.}
\1\inlinesynopsis{appendFileSync}[path, data, options]{appends to a file.}
\1\inlinesynopsis{existsSync}[path]{whether a path exists.}
\1\inlinesynopsis{statSync}[path]{returns a \code{Stats} object.}
\1\inlinesynopsis{readdirSync}[path]{lists a directory.}
\1\inlinesynopsis{mkdirSync}[path]{creates a directory.}
\1\inlinesynopsis{unlinkSync, rmSync, rmdirSync}[path]{remove files / directories.}
\1\inlinesynopsis{renameSync, copyFileSync, cpSync}[src, dest]{rename and copy.}
\1\inlinesynopsis{openSync, readSync, writeSync, closeSync}{the low-level descriptor calls.}
\end{outline}
The corresponding callback forms (\code{readFile}, \code{writeFile}, \dots), the \code{promises} namespace, the \code{Stats} and \code{Dirent} classes, and the usual \code{constants} are also exported.
\section{getopt --- Option Parsing}
\index{getopt (library)}\code{getopt} is a port of the POSIX \code{getopt()} routine. Construct a \code{BasicParser} from an option string and the argument vector, then call \code{getopt()} repeatedly; each call returns an object \code{\{ option, optarg \}} for the next option, or \code{undefined} at the end. A colon after a letter in the option string means that option takes an argument; long aliases are written in parentheses.
\begin{lstlisting}
let getopt = require("getopt")
let parser = new getopt.BasicParser("r(recursive)o:(output)", exec_args)
let o
while ((o = parser.getopt()) !== undefined) {
switch (o.option) {
case 'r': /* recursive */ break
case 'o': let target = o.optarg; break
}
}
\end{lstlisting}
\section{gl --- Graphics Library}
\index{gl (library)}\code{gl} is the same graphics-drawing library installed at boot as the \code{GL} namespace; \code{require("gl")} returns the same set of textures, sprite-sheets and drawing functions. It is documented in full in the \emph{The Graphics Library} chapter.
\section{net --- Internet Text Fetch}
\index{net (library)}\code{net} fetches text over the network through an attached HTTP modem, translating ordinary URLs into the form the modem expects.
\begin{outline}
\1\inlinesynopsis{isAvailable}[]{true if an HTTP modem is attached.}
\1\inlinesynopsis{getHttpDrive}[]{the drive letter bound to the HTTP modem, or null.}
\1\inlinesynopsis{fetchText}[url]{fetches a URL and returns the body as a string, or null on failure.}
\1\inlinesynopsis{fetchTextOrThrow}[url]{as \code{fetchText}, but throws on failure.}
\1\inlinesynopsis{open}[url]{returns a file descriptor backed by the modem, whose \code{sread}/\code{bread} trigger the fetch.}
\1\inlinesynopsis{toModemPath}[url]{returns the \code{<drive>:\rs{}<url>} path the fetch would use.}
\end{outline}
\section{pcm --- PCM and ADPCM Decoding}
\index{pcm (library)}\code{pcm} decodes linear PCM and Microsoft ADPCM into the audio hardware's 8-bit format, resampling to the 32\,kHz hardware rate as needed. It underpins the \code{playpcm} and \code{playwav} players.
\begin{outline}
\1\inlinesynopsis{decodeLPCM}[inPtr, outPtr, inputLen, config]{decodes linear PCM. \code{config} gives \code{nChannels}, \code{bitsPerSample}, \code{samplingRate} and \code{blockSize}.}
\1\inlinesynopsis{decodeMS\_ADPCM}[inPtr, outPtr, blockSize, config]{decodes one Microsoft-ADPCM block.}
\1\inlinesynopsis{HW\_SAMPLING\_RATE}{the hardware sampling rate, 32000.}
\end{outline}
The sample-conversion helpers \code{s8Tou8}, \code{s16Tou8}, \code{u16Tos16} and \code{randomRound} are exported too.
\section{psg --- Programmable Sound Generator}
\index{psg (library)}\code{psg} is a software sound generator: it synthesises classic chiptune waveforms into a buffer and sends the result to a playhead as PCM. It is handy for sound effects and simple music without authoring a full tracker module.
\begin{outline}
\1\inlinesynopsis{makeBuffer}[length]{allocates a mixing buffer; \code{makeBufferNative} / \code{freeBufferNative} use native memory.}
\1\inlinesynopsis{clearBuffer}[buf, offsetSec, lengthSec]{silences part of a buffer.}
\1\inlinesynopsis{makeSquare}{writes a square wave (with duty cycle) into a buffer.}
\1\inlinesynopsis{makeTriangle, makeAliasedTriangle, makeAliasedTriangleNES}{triangle-wave variants.}
\1\inlinesynopsis{makeNoise}{writes an LFSR noise waveform.}
\1\inlinesynopsis{sendBuffer, sendBufferFast}[buf, playhead, offsetSec, lengthSec]{uploads a buffer to a playhead for playback.}
\end{outline}
The wave generators take an offset, frequency, amplitude, pan and a mixing operation, so several voices can be layered into one buffer.
\section{taud --- Tracker Module Loading}
\index{taud (library)}\code{taud} moves Taud music between disk and the tracker hardware. It is intentionally small; authoring Taud music belongs to the separate Taud manual.
\begin{outline}
\1\inlinesynopsis{uploadTaudFile}[inFile, songIndex, playhead]{loads one song from a Taud file into the tracker hardware and prepares the given playhead to play it.}
\1\inlinesynopsis{captureTrackerDataToFile}[outFile]{writes the current tracker state (samples, instruments, patterns and cue sheet) out to a single-song Taud file.}
\end{outline}
\section{typesetter --- Rich-text Layout}
\index{typesetter (library)}\code{typesetter} wraps, aligns and justifies text for the console using a small markup language. It returns an array of strings, each padded to the requested width, ready to print.
\begin{outline}
\1\inlinesynopsis{typeset}[text, width, opts]{lays out text to the given width (default: the rest of the current row). \code{opts.defaultAlign} is one of \code{l}, \code{c}, \code{r} or \code{j} (justified).}
\1\inlinesynopsis{typesetText}[text, width, defaultAlign]{the same, with the width always given explicitly.}
\1\inlinesynopsis{expandEntities}[s]{expands the named entities (glyphs, accidentals, arrows, \code{\&nbsp;}, \dots).}
\end{outline}
The markup understands \code{<b>}/\code{<s>} for emphasis, \code{<c>}/\code{<r>}/\code{<l>} for per-line alignment, and \code{<o>} for a hanging-indent box anchored at the cursor column.
\section{wintex --- TUI Windows}
\index{wintex (library)}\code{wintex} provides a small text-mode window toolkit: framed, titled windows with their own input and drawing handlers, plus scrolling helpers and a ready-made dialog box.
\begin{outline}
\1\inlinesynopsis{WindowObject}[x, y, w, h, inputProcessor, drawContents, title, drawFrame]{constructs a window; the two handler functions receive input events and redraw the contents.}
\1\inlinesynopsis{showDialog}[opts]{displays a modal dialog (message, buttons, \dots) and returns the user's choice.}
\1\inlinesynopsis{scrollVert, scrollHorz}{compute new cursor and scroll positions for scrollable lists and fields.}
\end{outline}
\section{lfs --- Archive Extraction}
\index{lfs (library)}\code{lfs} extracts \thedos\ Linear File Strip (\code{.lfs}) archives programmatically, transparently inflating any compressed entries.
\begin{outline}
\1\inlinesynopsis{extractOne}[archive, filename]{extracts a single named entry and returns its file descriptor (under \code{\$:\rs{}TMP}).}
\1\inlinesynopsis{extractAll}[archive]{unpacks the whole archive and returns the directory descriptor.}
\end{outline}
Both take an optional \code{autoDecompress} flag (default true) and require a relative-path archive.
\section{mload --- Bulk File Loading}
\index{mload (library)}\code{mload} is a single function for packaged apps that need to pre-load resources into memory. Given an array of absolute paths, it loads each into memory and returns an array of the corresponding pointers (or \code{null} where a file could not be loaded).
\begin{lstlisting}
let mload = require("mload")
let [fontPtr, sheetPtr] = mload(["A:/app/font.bin", "A:/app/sheet.bin"])
\end{lstlisting}
\section{seqread --- Sequential Disk Reading}
\index{seqread (library)}\code{seqread} reads a file from a serial-connected disk drive as a stream, which is more efficient than random access for whole-file consumption (media players, archive readers).
\begin{outline}
\1\inlinesynopsis{prepare}[fullPath]{opens a file for sequential reading.}
\1\inlinesynopsis{readBytes}[length, ptr]{reads bytes into memory (allocating a buffer if no pointer is given).}
\1\inlinesynopsis{readInt, readShort, readOneByte, readFourCC, readString}{read typed values from the stream.}
\1\inlinesynopsis{skip}[n]{skips ahead; \code{seek}/\code{rewind} reposition the stream.}
\1\inlinesynopsis{getReadCount}[]{the number of bytes read so far.}
\end{outline}
\section{seqreadtape --- Sequential Tape Reading}
\index{seqreadtape (library)}\code{seqreadtape} is the counterpart to \code{seqread} for high-speed tape devices, addressed as \code{\$:\rs{}TAPE0}\dots\code{\$:\rs{}TAPE3}. It reads in large chunks rather than being limited to the serial block size, and exposes the same reading interface (\code{prepare}, \code{readBytes}, \code{readInt}, \dots, \code{skip}, \code{seek}, \code{rewind}) plus \code{close}, \code{isReady} and \code{getCurrentTapeDevice}.
\section{font --- Character ROM}
\index{font (library)}\code{font} replaces the displayed character set by uploading a font file into the graphics adapter's character ROM.
\begin{outline}
\1\inlinesynopsis{setLowRom}[fullPath]{loads characters 0--127 from a font file.}
\1\inlinesynopsis{setHighRom}[fullPath]{loads characters 128--255 from a font file.}
\1\inlinesynopsis{resetLowRom, resetHighRom}[]{restore the default character set.}
\end{outline}
\section{keysym --- Key Symbol Constants}
\index{keysym (library)}\code{keysym} is a table of named key codes (\code{A}, \code{ENTER}, \code{UP}, \code{SHIFT\_LEFT}, \dots) for use with the \code{input} library's events. Note that these are the keycodes carried by \code{input.withEvent} events, which differ from the character codes returned by \code{con.getch}.
\begin{lstlisting}
let key = require("keysym")
input.withEvent((e) => {
if (e[0] === "key_down" && e.includes(key.ESCAPE)) quit()
})
\end{lstlisting}
\section{tbas --- Terran BASIC Runtime}
\index{tbas (library)}\code{tbas} is the runtime support library for compiled Terran BASIC programs. It supplies the BASIC built-in functions (\code{PRINT}, \code{SIN}, \code{LEFT}, \dots) and the helpers the BASIC compiler emits. It is loaded automatically by a compiled program and is not normally used by hand; it is documented here only for completeness.
\section{synopsis --- Command Synopsis Loading}
\index{synopsis (library)}\code{synopsis} loads and caches TSF \code{.synopsis} documents (see the \emph{Command Synopsis Format} chapter) and answers the questions the shell and the \code{synopsis} command ask of them. It resolves a command name to its document --- an app's sits beside the executable, a coreutil's in \code{A:\rs{}tvdos\rs{}synopsis} --- parses it into a model, and caches the result both in memory and on disk.
\begin{outline}
\1\inlinesynopsis{getCompletion}[command, priorArgs, word]{returns the completion candidates for the word being typed: option flags, enum values, subcommand names, and whether the shell should additionally offer filesystem entries.}
\1\inlinesynopsis{getModel}[command]{the parsed model --- summary, options, positional arguments, subcommands and constraints --- or null when the command has no synopsis.}
\1\inlinesynopsis{getSummary}[command]{the one-line summary, or null.}
\1\inlinesynopsis{getUsage}[command]{a generated usage string, such as \code{cp SOURCE DEST}.}
\1\inlinesynopsis{resolveSynopsisPath}[command]{the full path of the command's synopsis document, or null.}
\1\inlinesynopsis{registerProvider}[name, fn]{registers an \code{internal} completion provider that \code{fn(word)} supplies candidates for; \code{commands} and \code{envvars} are built in.}
\1\inlinesynopsis{clearCache}[]{drops the in-memory caches.}
\end{outline}