\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 follwing progress to deliver the \code{A:\rs} prompt: \section{Probing Bootable Devices} The BIOS will probe serial devices to find first bootable drive. If found, port number of the driver is written to the \code{\_BIOS} object, then 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 will load system libraries and variables and then will try to run the boot script by executing \code{A:\rs{}AUTOEXEC.BAT} 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 GL using the external file \item execute \code{AUTOEXEC.BAT} \begin{enumerate} \item execute \code{command.js} with proper arguments \item \code{command.js} to initialise \code{shell.*} functions (this includes coreutils and patched version of \code{require}) \item \code{command.js} to parse and run \code{AUTOEXEC.BAT} \end{enumerate} \end{enumerate} \section{AUTOEXEC.BAT} AUTOEXEC can setup user-specific variables (e.g. keyboard layout) and launch the command shell of your choice, \code{COMMAND} is the most common shell. Variables can be set or changed using \textbf{SET} commands. \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{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{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.} \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 is written for users' convenience. This chapter will only briefly list and describe the applications. \begin{outline} \1\dossynopsis{basica}{Invokes a BASIC interpreter stored in the ROM. If no BASIC rom is present, nothing will be done.} \1\dossynopsis{basic}{If your system is bundled with a software-based BASIC, this command will invoke the BASIC interpreter stored in the disk.} \1\dossynopsis{color}{Changes the background and the foreground of the active session.} \1\dossynopsis{command}{The default text-based DOS shell. Call with \code{command -fancy} for more \ae sthetically pleasing looks.} \1\dossynopsis{decodeipf}[file]{Decodes the IPF-formatted image to the framebuffer using the graphics processor.} \1\dossynopsis{drives}{Shows the list of the connected and mounted disk drives.} \1\dossynopsis{edit}[file]{The interactive full-screen text editor.} \1\dossynopsis{encodeipf}[1/2 imagefile ipffile]{Encodes the given image file (.jpg, .png, .bmp, .tga) to the IPF format using the graphics hardware.} \1\dossynopsis{false}{Returns errorlevel 1 upon execution.} \1\dossynopsis{geturl}[url]{Reads contents on the web address and store it to the disk. Requires Internet adapter.} \1\dossynopsis{hexdump}[file]{Prints out the contents of a file in hexadecimal view. Supports pipe.} \1\dossynopsis{less}[file]{Allows user to read the long text, even if they are wider and/or taller than the screen. Supports pipe.} \1\dossynopsis{playmov}[file]{Plays tsvmmov-formatted video. Use -i flag for playback control.} \1\dossynopsis{playmp2}[file]{Plays MP2 (MPEG-1 Audio Layer II) formatted audio. Use -i flag for playback control.} \1\dossynopsis{playpcm}[file]{Plays raw PCM audio. Use -i flag for playback control.} \1\dossynopsis{playwav}[file]{Plays linear PCM/ADPCM audio. Use -i flag for playback control.} \1\dossynopsis{printfile}[file]{Prints out the contents of a textfile with line numbers. Useful for making descriptive screenshots.} \1\dossynopsis{touch}[file]{Updates a file's modification date. New file will be created if the specified file does not exist.} \1\dossynopsis{true}{Returns errorlevel 0 upon execution.} \1\dossynopsis{zfm}{Z File Manager. A two-panel graphical user interface to navigate the system using arrow keys. Hit Z to switch panels.} \end{outline} \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{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{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{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} Input events are Javascript array of: $$ [\mathrm{event\ name,\ arg_1,\ arg_2 \cdots arg_n}] $$, where: \begin{outline} \1event name --- one of following: \textbf{key\_down}, \textbf{mouse\_down}, \textbf{mouse\_move} \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{mouse\_down}: \2\argsynopsis{\argN{1}}{X-position of the mouse cursor} \2\argsynopsis{\argN{2}}{Y-position of the mouse cursor} \2\argsynopsis{\argN{3}}{Always the integer 1.} \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}}{1 if the mouse button is held down (i.e. dragging), 0 otherwise} \2\argsynopsis{\argN{4}}{X-position of the mouse cursor on the previous frame (previous V-blank of the screen)} \2\argsynopsis{\argN{5}}{Y-position of the mouse cursor on the previous frame} \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} \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 \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}