mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
286 lines
16 KiB
TeX
286 lines
16 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 follwing progress to deliver the \code{A:\rs} prompt:
|
|
|
|
\section{Probing Bootable Devices}
|
|
BIOS
|
|
|
|
\section{The Bootloader}
|
|
LOADBOOT
|
|
|
|
Then the Bootsector will try to read and execute \code{A:\rs{}tvdos\rs{}TVDOS.SYS}
|
|
|
|
\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}
|
|
|
|
\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{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{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{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.}
|
|
\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 fully-qualified path of the input path, relative to the current working directory.}
|
|
\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 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: Uint8Array}{Writes the bytes to the file.}
|
|
\1\formalsynopsis{swrite}{string: String}{Writes the string to 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: Uint8Array]{Creates an GL Texture.}
|
|
\1\inlinesynopsis[new GL]{MonoTex}[width: Int, height: Int, bytes: Uint8Array]{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}
|