fef474b553
FossilOrigin-Name: 1eb3cb8402f8b572a94d10bad8132327990cdc90e4f34535b944f8a7f881cc58
3205 lines
119 KiB
HTML
3205 lines
119 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html>
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>RETRO Handbook</title>
|
||
<link rel="stylesheet" href="https://stackedit.io/style.css" />
|
||
</head>
|
||
|
||
<body class="stackedit">
|
||
<div class="stackedit__left">
|
||
<div class="stackedit__toc">
|
||
|
||
<ul>
|
||
<li><a href="#retro-a-modern-pragmatic-forth">RETRO: a Modern, Pragmatic Forth</a></li>
|
||
<li><a href="#obtaining-retro">Obtaining RETRO</a>
|
||
<ul>
|
||
<li><a href="#stable-releases">Stable Releases</a></li>
|
||
<li><a href="#snapshots">Snapshots</a></li>
|
||
<li><a href="#repository">Repository</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#building-retro-on-bsd">Building RETRO on BSD</a>
|
||
<ul>
|
||
<li><a href="#requirements">Requirements</a></li>
|
||
<li><a href="#process">Process</a></li>
|
||
<li><a href="#executables">Executables</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#building-retro-on-linux">Building RETRO on Linux</a>
|
||
<ul>
|
||
<li><a href="#requirements-1">Requirements</a></li>
|
||
<li><a href="#process-1">Process</a></li>
|
||
<li><a href="#executables-1">Executables</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#building-retro-on-macos">Building RETRO on macOS</a>
|
||
<ul>
|
||
<li><a href="#requirements-2">Requirements</a></li>
|
||
<li><a href="#process-2">Process</a></li>
|
||
<li><a href="#executables-2">Executables</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#building-retro-on-windows">Building RETRO on Windows</a>
|
||
<ul>
|
||
<li><a href="#setup-build-environment">Setup Build Environment</a></li>
|
||
<li><a href="#prepare-source">Prepare Source</a></li>
|
||
<li><a href="#build">Build</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#starting-retro">Starting RETRO</a>
|
||
<ul>
|
||
<li><a href="#interactive">Interactive</a></li>
|
||
<li><a href="#using-in-a-pipe">Using In a Pipe</a></li>
|
||
<li><a href="#running-a-program-in-a-file">Running A Program In A File</a></li>
|
||
<li><a href="#scripting">Scripting</a></li>
|
||
<li><a href="#command-line-arguments">Command Line Arguments</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#basic-interactions">Basic Interactions</a></li>
|
||
<li><a href="#syntax">Syntax</a>
|
||
<ul>
|
||
<li><a href="#tokens">Tokens</a></li>
|
||
<li><a href="#prefixes">Prefixes</a></li>
|
||
<li><a href="#word-classes">Word Classes</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#a-quick-tutorial">A Quick Tutorial</a></li>
|
||
<li><a href="#using-the-glossary">Using The Glossary</a>
|
||
<ul>
|
||
<li><a href="#example-entry">Example Entry</a></li>
|
||
<li><a href="#reading-the-entry">Reading The Entry</a></li>
|
||
<li><a href="#access-online">Access Online</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#programming-techniques">Programming Techniques</a></li>
|
||
<li><a href="#unu-simple-literate-source-files">Unu: Simple, Literate Source Files</a></li>
|
||
<li><a href="#naming-conventions">Naming Conventions</a>
|
||
<ul>
|
||
<li><a href="#general-guidelines">General Guidelines</a></li>
|
||
<li><a href="#typical-format">Typical Format</a></li>
|
||
<li><a href="#case">Case</a></li>
|
||
<li><a href="#namespaces">Namespaces</a></li>
|
||
<li><a href="#tips">Tips</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#stack-diagrams">Stack Diagrams</a></li>
|
||
<li><a href="#word-classes-1">Word Classes</a>
|
||
<ul>
|
||
<li><a href="#how-it-works">How It Works</a></li>
|
||
<li><a href="#using-classes">Using Classes</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#using-combinators">Using Combinators</a>
|
||
<ul>
|
||
<li><a href="#types-of-combinators">Types of Combinators</a></li>
|
||
<li><a href="#compositional">Compositional</a></li>
|
||
<li><a href="#execution-flow">Execution Flow</a></li>
|
||
<li><a href="#data-flow">Data Flow</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#the-stacks">The Stacks</a>
|
||
<ul>
|
||
<li><a href="#data-stack">Data Stack</a></li>
|
||
<li><a href="#address-stack">Address Stack</a></li>
|
||
<li><a href="#floating-point-stack">Floating Point Stack</a></li>
|
||
<li><a href="#tips-2">Tips</a></li>
|
||
<li><a href="#notes">Notes</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-arrays">Working With Arrays</a>
|
||
<ul>
|
||
<li><a href="#namespace">Namespace</a></li>
|
||
<li><a href="#creating-arrays">Creating Arrays</a></li>
|
||
<li><a href="#accessing-elements">Accessing Elements</a></li>
|
||
<li><a href="#find-the-length">Find The Length</a></li>
|
||
<li><a href="#duplicate">Duplicate</a></li>
|
||
<li><a href="#filtering">Filtering</a></li>
|
||
<li><a href="#mapping">Mapping</a></li>
|
||
<li><a href="#reduce">Reduce</a></li>
|
||
<li><a href="#search">Search</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-a-buffer">Working With a Buffer</a>
|
||
<ul>
|
||
<li><a href="#namespace-1">Namespace</a></li>
|
||
<li><a href="#implementation">Implementation</a></li>
|
||
<li><a href="#limitations">Limitations</a></li>
|
||
<li><a href="#set-the-active-buffer">Set The Active Buffer</a></li>
|
||
<li><a href="#add-value">Add Value</a></li>
|
||
<li><a href="#fetch-last-value">Fetch Last Value</a></li>
|
||
<li><a href="#get-data-about-the-buffer">Get Data About The Buffer</a></li>
|
||
<li><a href="#reset">Reset</a></li>
|
||
<li><a href="#example">Example</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-characters">Working With Characters</a>
|
||
<ul>
|
||
<li><a href="#prefix">Prefix</a></li>
|
||
<li><a href="#namespace-2">Namespace</a></li>
|
||
<li><a href="#classification">Classification</a></li>
|
||
<li><a href="#conversions">Conversions</a></li>
|
||
<li><a href="#io">I/O</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-the-dictionary">Working With The Dictionary</a>
|
||
<ul>
|
||
<li><a href="#namespace-3">Namespace</a></li>
|
||
<li><a href="#variables">Variables</a></li>
|
||
<li><a href="#header-structure">Header Structure</a></li>
|
||
<li><a href="#accessing-fields">Accessing Fields</a></li>
|
||
<li><a href="#shortcuts-for-the-latest-header">Shortcuts For The Latest Header</a></li>
|
||
<li><a href="#adding-headers">Adding Headers</a></li>
|
||
<li><a href="#searching">Searching</a></li>
|
||
<li><a href="#iteration">Iteration</a></li>
|
||
<li><a href="#listing-words">Listing Words</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-floating-point">Working With Floating Point</a>
|
||
<ul>
|
||
<li><a href="#prefix-1">Prefix</a></li>
|
||
<li><a href="#namespace-4">Namespace</a></li>
|
||
<li><a href="#operation">Operation</a></li>
|
||
<li><a href="#constants">Constants</a></li>
|
||
<li><a href="#comparisons">Comparisons</a></li>
|
||
<li><a href="#basic-math">Basic Math</a></li>
|
||
<li><a href="#geometry">Geometry</a></li>
|
||
<li><a href="#storage-and-retrieval">Storage and Retrieval</a></li>
|
||
<li><a href="#io-1">I/O</a></li>
|
||
<li><a href="#encoded-values">Encoded Values</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-numbers">Working With Numbers</a>
|
||
<ul>
|
||
<li><a href="#token-prefix">Token Prefix</a></li>
|
||
<li><a href="#namespace-5">Namespace</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-pointers">Working With Pointers</a>
|
||
<ul>
|
||
<li><a href="#prefix-2">Prefix</a></li>
|
||
<li><a href="#examples">Examples</a></li>
|
||
<li><a href="#notes-1">Notes</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-strings">Working With Strings</a>
|
||
<ul>
|
||
<li><a href="#prefix-3">Prefix</a></li>
|
||
<li><a href="#namespace-6">Namespace</a></li>
|
||
<li><a href="#lifetime">Lifetime</a></li>
|
||
<li><a href="#mutability">Mutability</a></li>
|
||
<li><a href="#searching-1">Searching</a></li>
|
||
<li><a href="#comparisons-1">Comparisons</a></li>
|
||
<li><a href="#extraction">Extraction</a></li>
|
||
<li><a href="#joining">Joining</a></li>
|
||
<li><a href="#tokenization">Tokenization</a></li>
|
||
<li><a href="#conversions-1">Conversions</a></li>
|
||
<li><a href="#cleanup">Cleanup</a></li>
|
||
<li><a href="#combinators">Combinators</a></li>
|
||
<li><a href="#other">Other</a></li>
|
||
<li><a href="#controlling-the-temporary-buffers">Controlling The Temporary Buffers</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#working-with-assembly-language">Working With Assembly Language</a>
|
||
<ul>
|
||
<li><a href="#assembling-a-standalone-file">Assembling A Standalone File</a></li>
|
||
<li><a href="#runtime-assembler">Runtime Assembler</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#lexical-scope">Lexical Scope</a>
|
||
<ul>
|
||
<li><a href="#example-1">Example</a></li>
|
||
<li><a href="#notes-2">Notes</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#internals">Internals</a></li>
|
||
<li><a href="#internals-nga-virtual-machine">Internals: Nga Virtual Machine</a>
|
||
<ul>
|
||
<li><a href="#overview">Overview</a></li>
|
||
<li><a href="#instrution-table">Instrution Table</a></li>
|
||
<li><a href="#encoding">Encoding</a></li>
|
||
<li><a href="#shifts">Shifts</a></li>
|
||
<li><a href="#queries-memory-stacks">Queries: Memory, Stacks</a></li>
|
||
<li><a href="#io-devices">I/O Devices</a></li>
|
||
<li><a href="#trivia">Trivia</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#internals-interface-layers">Internals: Interface Layers</a></li>
|
||
<li><a href="#internals-the-retro-image">Internals: The Retro Image</a>
|
||
<ul>
|
||
<li><a href="#format">Format</a></li>
|
||
<li><a href="#header">Header</a></li>
|
||
<li><a href="#layout">Layout</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#additional-tools">Additional Tools</a>
|
||
<ul>
|
||
<li><a href="#retro">retro</a></li>
|
||
<li><a href="#retro-describe">retro-describe</a></li>
|
||
<li><a href="#retro-embedimage">retro-embedimage</a></li>
|
||
<li><a href="#retro-extend">retro-extend</a></li>
|
||
<li><a href="#retro-muri">retro-muri</a></li>
|
||
<li><a href="#retro-unu">retro-unu</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#advanced-builds">Advanced Builds</a>
|
||
<ul>
|
||
<li><a href="#reduced-memory">Reduced Memory</a></li>
|
||
<li><a href="#custom-image">Custom Image</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#the-optional-retro-compiler">The Optional Retro Compiler</a>
|
||
<ul>
|
||
<li><a href="#requirements-3">Requirements</a></li>
|
||
<li><a href="#building">Building</a></li>
|
||
<li><a href="#installing">Installing</a></li>
|
||
<li><a href="#using">Using</a></li>
|
||
<li><a href="#known-limitations">Known Limitations</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#errors">Errors</a>
|
||
<ul>
|
||
<li><a href="#non-fatal">Non-Fatal</a></li>
|
||
<li><a href="#fatal">Fatal</a></li>
|
||
<li><a href="#rationale">Rationale</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#security-concerns">Security Concerns</a>
|
||
<ul>
|
||
<li><a href="#runtime-checks">Runtime Checks</a></li>
|
||
<li><a href="#isolation">Isolation</a></li>
|
||
<li><a href="#future-direction">Future Direction</a></li>
|
||
<li><a href="#rationale-1">Rationale</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#technical-notes-and-reflections">Technical Notes and Reflections</a>
|
||
<ul>
|
||
<li><a href="#metacompilation-and-assembly">Metacompilation and Assembly</a></li>
|
||
<li><a href="#the-path-to-self-hosting">The Path to Self Hosting</a></li>
|
||
<li><a href="#prefixes-as-a-language-element">Prefixes as a Language Element</a></li>
|
||
<li><a href="#on-the-kernel-wordset">On The Kernel Wordset</a></li>
|
||
<li><a href="#on-the-evolution-of-ngaro-into-nga">On The Evolution Of Ngaro Into Nga</a></li>
|
||
<li><a href="#retro-11-2011---2019-a-look-back">RETRO 11 (2011 - 2019): A Look Back</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#historical-papers-and-notes">Historical Papers and Notes</a>
|
||
<ul>
|
||
<li><a href="#on-the-naming-of-retro">On the Naming of RETRO</a></li>
|
||
<li><a href="#the-design-philosophy-of-retro-native-forth">The Design Philosophy of RETRO Native Forth</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="stackedit__right">
|
||
<div class="stackedit__html">
|
||
<h1 id="retro-a-modern-pragmatic-forth">RETRO: a Modern, Pragmatic Forth</h1>
|
||
<p>Welcome to RETRO, my personal take on the Forth language. This<br>
|
||
is a modern system primarily targetting desktop, mobile, and<br>
|
||
servers, though it can also be used on some larger (ARM, MIPS32)<br>
|
||
embedded systems.</p>
|
||
<p>The language is Forth. It is untyped, uses a stack to pass data<br>
|
||
between functions called words, and a dictionary which tracks<br>
|
||
the word names and data structures.</p>
|
||
<p>But it’s not a traditional Forth. RETRO draws influences from<br>
|
||
many sources and takes a unique approach to the language.</p>
|
||
<p>RETRO has a large vocabulary of words. Keeping a copy of the<br>
|
||
Glossary on hand is highly recommended as you learn to use RETRO.</p>
|
||
<p>This book will hopefully help you develop a better understanding<br>
|
||
of RETRO and how it works.</p>
|
||
<h1 id="obtaining-retro">Obtaining RETRO</h1>
|
||
<p>The RETRO source code can be obtained from <a href="http://forthworks.com/retro">http://forthworks.com/retro</a><br>
|
||
or gopher://forthworks.com/1/retro</p>
|
||
<h2 id="stable-releases">Stable Releases</h2>
|
||
<p>I periodically make stable releases. This will typically happen<br>
|
||
two to four times per year. These are good for those needing a<br>
|
||
solid base that doesn’t change frequently.</p>
|
||
<h2 id="snapshots">Snapshots</h2>
|
||
<p>A lot of development happens between releases. I make snapshots<br>
|
||
of my working source tree nightly (and often more often).</p>
|
||
<p>This is what I personally recommend for most users. It reflects<br>
|
||
my latest system and is normally reliable as it’s used daily in<br>
|
||
production.</p>
|
||
<p>The latest snapshot can be downloaded from the following stable<br>
|
||
URLs:</p>
|
||
<ul>
|
||
<li><a href="http://forthworks.com/retro/r/latest.tar.gz">http://forthworks.com/retro/r/latest.tar.gz</a></li>
|
||
<li>gopher://forthworks.com/9/retro/r/latest.tar.gz</li>
|
||
</ul>
|
||
<h2 id="repository">Repository</h2>
|
||
<p>I use a Fossil repository to manage development. To obtain a<br>
|
||
copy of the repository install Fossil and:</p>
|
||
<pre><code>fossil clone http://forthworks.com:8000 retro.fossil
|
||
mkdir retro
|
||
cd retro
|
||
fossil open /path/to/retro.fossil
|
||
</code></pre>
|
||
<p>See the Fossil documentation for details on using Fossil to<br>
|
||
keep your local copy of the repository current.</p>
|
||
<p>This will let you stay current with my latest changes faster<br>
|
||
than the snapshots, but you may occasionally encounter bigger<br>
|
||
problems as some commits may be in a partially broken state.</p>
|
||
<p>If you have problems, check the version of Fossil you are<br>
|
||
using. I am currently using Fossil 2.7, you may experience<br>
|
||
issues checking out or cloning if using older versions.</p>
|
||
<h1 id="building-retro-on-bsd">Building RETRO on BSD</h1>
|
||
<p>RETRO is well supported on BSD (FreeBSD, NetBSD, OpenBSD)<br>
|
||
systems. It should build on a base install of any of these<br>
|
||
without issue.</p>
|
||
<h2 id="requirements">Requirements</h2>
|
||
<ul>
|
||
<li>c compiler</li>
|
||
<li>make</li>
|
||
</ul>
|
||
<h2 id="process">Process</h2>
|
||
<p>Run <code>make</code></p>
|
||
<p>This will build the toolchain and then the main <code>retro</code><br>
|
||
executable.</p>
|
||
<h2 id="executables">Executables</h2>
|
||
<p>In the <code>bin/</code> directory:</p>
|
||
<pre><code>retro
|
||
retro-unu
|
||
retro-muri
|
||
retro-extend
|
||
retro-embedimage
|
||
retro-describe
|
||
</code></pre>
|
||
<h1 id="building-retro-on-linux">Building RETRO on Linux</h1>
|
||
<p>Building on Linux is pretty easy. You’ll need to make sure<br>
|
||
you have a C compiler, headers, and make.</p>
|
||
<h2 id="requirements-1">Requirements</h2>
|
||
<ul>
|
||
<li>c compiler (tested: clang, tcc, gcc)</li>
|
||
<li>development headers</li>
|
||
<li>make</li>
|
||
</ul>
|
||
<h2 id="process-1">Process</h2>
|
||
<p>Run:</p>
|
||
<pre><code>make -f Makefile.linux
|
||
</code></pre>
|
||
<p>This will build the toolchain and then the main <code>retro</code><br>
|
||
executable.</p>
|
||
<h2 id="executables-1">Executables</h2>
|
||
<p>In the <code>bin/</code> directory:</p>
|
||
<pre><code>retro
|
||
retro-unu
|
||
retro-muri
|
||
retro-extend
|
||
retro-embedimage
|
||
retro-describe
|
||
</code></pre>
|
||
<h1 id="building-retro-on-macos">Building RETRO on macOS</h1>
|
||
<p>To build on macOS, you will need the command line tools from<br>
|
||
Xcode. Install these and you should be able to build and use<br>
|
||
RETRO.</p>
|
||
<h2 id="requirements-2">Requirements</h2>
|
||
<ul>
|
||
<li>command line tools from Xcode</li>
|
||
</ul>
|
||
<h2 id="process-2">Process</h2>
|
||
<p>Run <code>make</code></p>
|
||
<p>This will build the toolchain and then the main <code>retro</code><br>
|
||
executable.</p>
|
||
<h2 id="executables-2">Executables</h2>
|
||
<p>In the <code>bin/</code> directory:</p>
|
||
<pre><code>retro
|
||
retro-unu
|
||
retro-muri
|
||
retro-extend
|
||
retro-embedimage
|
||
</code></pre>
|
||
<h1 id="building-retro-on-windows">Building RETRO on Windows</h1>
|
||
<p>It is possible to build RETRO on Windows, though a few of the<br>
|
||
extensions are not supported:</p>
|
||
<ul>
|
||
<li>no <code>unix:</code> words</li>
|
||
<li>no <code>gopher:</code> words</li>
|
||
</ul>
|
||
<p>This is currently more difficult than on a Unix host. If you have<br>
|
||
Windows 10 and WSL, it may be better to build under that (using<br>
|
||
the Linux instructions).</p>
|
||
<h2 id="setup-build-environment">Setup Build Environment</h2>
|
||
<p>RETRO on Windows is built with TCC.</p>
|
||
<p>Go to <a href="http://download.savannah.gnu.org/releases/tinycc/">http://download.savannah.gnu.org/releases/tinycc/</a></p>
|
||
<p>Download the <em>winapi-full</em> and <em>tcc-xxxx-bin</em> packages for your<br>
|
||
system. Decompress them, copy the headers from the winapi<br>
|
||
package into the tcc directory.</p>
|
||
<h2 id="prepare-source">Prepare Source</h2>
|
||
<p>You’ll need to comment out (or remove) some things before RETRO<br>
|
||
will build.</p>
|
||
<p>In <em>rre.c</em>:</p>
|
||
<ul>
|
||
<li>remove includes for unistd.h, sys/sockets.h, netinet/in.h,<br>
|
||
netdb.h, errno.h, sys/wait.h, signal.h</li>
|
||
<li>remove the #define USE_TERMIOS line</li>
|
||
<li>change the #define NUM_DEVICES to 6</li>
|
||
<li>remove io_unix_handler and io_gopher_handler from IO_deviceHandlers</li>
|
||
<li>remove io_unix_query and io_gopher_query from IO_queryHandlers</li>
|
||
</ul>
|
||
<p>In <em>image-functions.c</em>:</p>
|
||
<ul>
|
||
<li>remove includes for unistd.h</li>
|
||
</ul>
|
||
<p>In <em>image-functions.h</em>:</p>
|
||
<ul>
|
||
<li>remove includes for unistd.h</li>
|
||
</ul>
|
||
<p>In <em>io\filesystem.c</em>:</p>
|
||
<ul>
|
||
<li>remove includes for unistd.h</li>
|
||
</ul>
|
||
<p>In <em>io\floatingpoint.c</em>:</p>
|
||
<ul>
|
||
<li>remove includes for unistd.h</li>
|
||
</ul>
|
||
<h2 id="build">Build</h2>
|
||
<p>\path\to\tcc rre.c image-functions.c io\filesystem.c io\floatingpoint.c -o retro.exe</p>
|
||
<h1 id="starting-retro">Starting RETRO</h1>
|
||
<p>RETRO can be run for scripting or interactive use.</p>
|
||
<h2 id="interactive">Interactive</h2>
|
||
<p>To start it interactively, run: <code>retro</code> without any command line<br>
|
||
arguments, or with <code>-i</code>, <code>-s</code>, or <code>-i,c</code>.</p>
|
||
<p>Starting the interactive system:</p>
|
||
<pre><code>retro
|
||
</code></pre>
|
||
<p>Or:</p>
|
||
<pre><code>retro -i
|
||
</code></pre>
|
||
<p>This should be sufficient for most uses.</p>
|
||
<p>Starting the interactive system (without displaying the <code>Ok</code><br>
|
||
prompt or startup banner):</p>
|
||
<pre><code>retro -s
|
||
</code></pre>
|
||
<p>RETRO also has a <em>character breaking</em> mode, in which input is<br>
|
||
processed directly as entered, this is started with the <code>-c</code><br>
|
||
option:</p>
|
||
<pre><code>retro -i,c
|
||
</code></pre>
|
||
<h2 id="using-in-a-pipe">Using In a Pipe</h2>
|
||
<p>If using a Unix shell and piping input between processes, you<br>
|
||
will probably want to use <code>-s</code> to supress the startup messages<br>
|
||
and <code>Ok</code> prompt that normally appear.</p>
|
||
<p>E.g.,</p>
|
||
<pre><code>echo "'lol s:put nl" | retro -s
|
||
</code></pre>
|
||
<h2 id="running-a-program-in-a-file">Running A Program In A File</h2>
|
||
<p>You can run code in a file very easily. This is simply:</p>
|
||
<pre><code>retro filename
|
||
</code></pre>
|
||
<p>You can follow the filename with any arguments that it may need.<br>
|
||
These will be accessible to the program via the <code>sys:argc</code> and<br>
|
||
<code>sys:argv</code> words.</p>
|
||
<p>Source files must be written in Unu format.</p>
|
||
<h2 id="scripting">Scripting</h2>
|
||
<p>You can use RETRO to write scripts. Add a shebang:</p>
|
||
<pre><code>#!/usr/bin/env retro
|
||
</code></pre>
|
||
<p>And make the file executable.</p>
|
||
<p>Source files must be written in Unu format.</p>
|
||
<h2 id="command-line-arguments">Command Line Arguments</h2>
|
||
<p>For a summary of the full command line arguments available:</p>
|
||
<pre><code>Scripting Usage:
|
||
|
||
retro filename [script arguments...]
|
||
|
||
Interactive Usage:
|
||
|
||
retro [-h] [-i] [-c] [-s] [-f filename] [-t]
|
||
|
||
-h Display this help text
|
||
-i Interactive mode (line buffered)
|
||
-c Interactive mode (character buffered)
|
||
-s Suppress the 'ok' prompt and keyboard
|
||
echo in interactive mode
|
||
-f filename Run the contents of the specified file
|
||
-t Run tests (in ``` blocks) in any loaded files
|
||
</code></pre>
|
||
<h1 id="basic-interactions">Basic Interactions</h1>
|
||
<p>Start RETRO in interactive mode:</p>
|
||
<pre><code>retro -i
|
||
</code></pre>
|
||
<p>You should see something similar to this:</p>
|
||
<pre><code>RETRO 12 (rx-2019.6)
|
||
8388608 MAX, TIB @ 1025, Heap @ 9374
|
||
|
||
Ok
|
||
</code></pre>
|
||
<p>At this point you are at the <em>listener</em>, which reads and<br>
|
||
processes your input. You are now set to begin exploring<br>
|
||
RETRO.</p>
|
||
<p>To exit, run <code>bye</code>:</p>
|
||
<pre><code>bye
|
||
</code></pre>
|
||
<h1 id="syntax">Syntax</h1>
|
||
<p>RETRO has more syntax than a traditional Forth due to ideas<br>
|
||
borrowed from ColorForth and some design decisions. This has<br>
|
||
some useful traits, and helps to make the language more<br>
|
||
consistent.</p>
|
||
<h2 id="tokens">Tokens</h2>
|
||
<p>Input is divided into a series of whitespace delimited tokens.<br>
|
||
Each of these is then processed individually. There are no<br>
|
||
parsing words in RETRO.</p>
|
||
<p>Tokens may have a single character <em>prefix</em>, which RETRO will<br>
|
||
use to decide how to process the token.</p>
|
||
<h2 id="prefixes">Prefixes</h2>
|
||
<p>Prefixes are single characters added to the start of a token<br>
|
||
to guide the compiler. The use of these is a major way in<br>
|
||
which RETRO differs from traditional Forth.</p>
|
||
<p>When a token is passed to <code>interpret</code>, RETRO first takes the<br>
|
||
intitial character and looks to see if there is a word that<br>
|
||
matches this. If so, it will pass the rest of the token to<br>
|
||
that word to handle.</p>
|
||
<p>In a traditional Forth, the interpret process is something<br>
|
||
like:</p>
|
||
<pre><code>get token
|
||
is token in the dictionary?
|
||
yes:
|
||
is it immediate?
|
||
yes: call the word.
|
||
no: are we interpreting?
|
||
yes: call the word
|
||
no: compile a call to the word
|
||
no:
|
||
is it a number?
|
||
yes: are we interpreting?
|
||
yes: push the number to the stack
|
||
no: compile the number as a literal
|
||
no: report an error ("not found")
|
||
</code></pre>
|
||
<p>In RETRO, the interpret process is basically:</p>
|
||
<pre><code>get token
|
||
does the first character match a `prefix:` word?
|
||
yes: pass the token to the prefix handler
|
||
no: is token a word in the dictionary?
|
||
yes: push the XT to the stack and call the
|
||
class handler
|
||
no: report an error ("not found")
|
||
</code></pre>
|
||
<p>All of the actual logic for how to deal with tokens is moved<br>
|
||
to the individual prefix handlers, and the logic for handling<br>
|
||
words is moved to word class handlers.</p>
|
||
<p>This means that prefixes are used for a lot of things. Numbers?<br>
|
||
Handled by a <code>#</code> prefix. Strings? Use the <code>'</code> prefix. Comments?<br>
|
||
Use <code>(</code>. Making a new word? Use the <code>:</code> prefix.</p>
|
||
<p>The major prefixes are:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Prefix</th>
|
||
<th>Used For</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>@</td>
|
||
<td>Fetch from variable</td>
|
||
</tr>
|
||
<tr>
|
||
<td>!</td>
|
||
<td>Store into variable</td>
|
||
</tr>
|
||
<tr>
|
||
<td>&</td>
|
||
<td>Pointer to named item</td>
|
||
</tr>
|
||
<tr>
|
||
<td>#</td>
|
||
<td>Numbers</td>
|
||
</tr>
|
||
<tr>
|
||
<td>$</td>
|
||
<td>ASCII characters</td>
|
||
</tr>
|
||
<tr>
|
||
<td>’</td>
|
||
<td>Strings</td>
|
||
</tr>
|
||
<tr>
|
||
<td>(</td>
|
||
<td>Comments</td>
|
||
</tr>
|
||
<tr>
|
||
<td>:</td>
|
||
<td>Define a word</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><p>The individual prefixes will be covered in more detail in the<br>
|
||
later chapters on working with different data types.</p>
|
||
<h2 id="word-classes">Word Classes</h2>
|
||
<p>Word classes are words which take a pointer and do something<br>
|
||
with it. These are covered in detail in their own chapter,<br>
|
||
but essentially they decide <em>how</em> to execute or compile specific<br>
|
||
types of words.</p>
|
||
<h1 id="a-quick-tutorial">A Quick Tutorial</h1>
|
||
<p>Programming in RETRO is all about creating words to solve<br>
|
||
the problem at hand. Words operate on data, which can be<br>
|
||
kept in memory or on the stack.</p>
|
||
<p>Let’s look at this by solving a small problem: writing a<br>
|
||
word to determine if a string is a palindrome.</p>
|
||
<p>A palindrome is a phrase which reads the same backward<br>
|
||
and forward.</p>
|
||
<p>We first need a string to look at. Starting with something<br>
|
||
easy:</p>
|
||
<pre><code>'anna
|
||
</code></pre>
|
||
<p>Looking in the Glossary, there is a <code>s:reverse</code> word for<br>
|
||
reversing a string. We can find <code>dup</code> to copy a value, and<br>
|
||
<code>s:eq?</code> to compare two strings. So testing:</p>
|
||
<pre><code>'anna dup s:reverse s:eq?
|
||
</code></pre>
|
||
<p>This yields -1 (<code>TRUE</code>) as expected. So we can easily<br>
|
||
name it:</p>
|
||
<pre><code>:palindrome dup s:reverse s:eq? ;
|
||
</code></pre>
|
||
<p>Naming uses the <code>:</code> prefix to add a new word to the dictionary.<br>
|
||
The words that make up the definition are then placed, with a<br>
|
||
final word (<code>;</code>) ending the definition. We can then use this:</p>
|
||
<pre><code>'anna palindrome?
|
||
</code></pre>
|
||
<p>Once defined there is no difference between our new word and<br>
|
||
any of the words already provided by the RETRO system.</p>
|
||
<h1 id="using-the-glossary">Using The Glossary</h1>
|
||
<p>The Glossary is a valuable resource. It provides information<br>
|
||
on the RETRO words.</p>
|
||
<h2 id="example-entry">Example Entry</h2>
|
||
<pre><code>f:+
|
||
|
||
Data: -
|
||
Addr: -
|
||
Float: FF-F
|
||
|
||
Add two floating point numbers, returning the result.
|
||
|
||
Class: class:word | Namespace: f | Interface Layer: rre
|
||
|
||
Example #1:
|
||
|
||
.3.1 .22 f:+
|
||
</code></pre>
|
||
<h2 id="reading-the-entry">Reading The Entry</h2>
|
||
<p>An entry starts with the word name.</p>
|
||
<p>This is followed by the stack effect for each stack. All RETRO<br>
|
||
systems have Data and Address stacks, some also include a<br>
|
||
floating point stack).</p>
|
||
<p>The stack effect diagrams are followed by a short description<br>
|
||
of the word.</p>
|
||
<p>After the description is a line providing some useful data. This<br>
|
||
includes the class handler, namespace prefix, and the interface<br>
|
||
layer that provides the word.</p>
|
||
<p>Words in all systems will be listed as <code>all</code>. Some words (like<br>
|
||
the <code>pb:</code> words) are only on specific systems like iOS. These<br>
|
||
can be identified by looking at the interface layer field.</p>
|
||
<p>At the end of the entry may be an example or two.</p>
|
||
<h2 id="access-online">Access Online</h2>
|
||
<p>The latest Glossary can be browsed at <a href="http://forthworks.com:9999">http://forthworks.com:9999</a><br>
|
||
or gopher://forthworks.com:9999</p>
|
||
<h1 id="programming-techniques">Programming Techniques</h1>
|
||
<p>The upcoming chapters provide helpful information on using RETRO<br>
|
||
with different types of data and hints on how to solve problems<br>
|
||
in a way consistent with the RETRO system.</p>
|
||
<h1 id="unu-simple-literate-source-files">Unu: Simple, Literate Source Files</h1>
|
||
<p>RETRO is written in a literate style. Most of the sources<br>
|
||
are in a format called Unu. This allows easy mixing of<br>
|
||
commentary and code blocks, making it simple to document<br>
|
||
the code.</p>
|
||
<p>As an example,</p>
|
||
<pre><code># Determine The Average Word Name Length
|
||
|
||
To determine the average length of a word name two values
|
||
are needed. First, the total length of all names in the
|
||
Dictionary:
|
||
|
||
~~~
|
||
#0 [ d:name s:length + ] d:for-each
|
||
~~~
|
||
|
||
And then the number of words in the Dictionary:
|
||
|
||
~~~
|
||
#0 [ drop n:inc ] d:for-each
|
||
~~~
|
||
|
||
With these, a simple division is all that's left.
|
||
|
||
~~~
|
||
/
|
||
~~~
|
||
|
||
Finally, display the results:
|
||
|
||
|
||
~~~
|
||
'Average_name_length:_%n\n s:format s:put
|
||
~~~
|
||
</code></pre>
|
||
<p>This illustrates the format. Only code in the fenced blocks<br>
|
||
(between ~~~ pairs) get extracted and run.</p>
|
||
<p>(Note: this only applies to <em>source files</em>; fences are not used<br>
|
||
when entering code interactively).</p>
|
||
<h1 id="naming-conventions">Naming Conventions</h1>
|
||
<p>Word names in RETRO generally follow the following conventions.</p>
|
||
<h2 id="general-guidelines">General Guidelines</h2>
|
||
<ul>
|
||
<li>Readability is important</li>
|
||
<li>Be consistent</li>
|
||
<li>Don’t use a prefix as the first character of a name</li>
|
||
<li>Use short names for indices</li>
|
||
</ul>
|
||
<h2 id="typical-format">Typical Format</h2>
|
||
<p>The word names will generally follow a form like:</p>
|
||
<pre><code>[namespace:]name
|
||
</code></pre>
|
||
<p>The <code>namespace:</code> is optional, but recommended for consistency<br>
|
||
with the rest of the system and to make it easier to identify<br>
|
||
related words.</p>
|
||
<h2 id="case">Case</h2>
|
||
<p>Word names are lowercase, with a dash (-) for compound names.</p>
|
||
<pre><code>hello
|
||
drop-pair
|
||
s:for-each
|
||
</code></pre>
|
||
<p>Variables use TitleCase, with no dash between compound names.</p>
|
||
<pre><code>Base
|
||
Heap
|
||
StringBuffers
|
||
</code></pre>
|
||
<p>Constants are UPPERCASE, with a dash (-) for compound names.</p>
|
||
<pre><code>TRUE
|
||
FALSE
|
||
f:PI
|
||
MAX-STRING-LENGTH
|
||
</code></pre>
|
||
<h2 id="namespaces">Namespaces</h2>
|
||
<p>Words are grouped into broad namespaces by attaching a short<br>
|
||
prefix string to the start of a name.</p>
|
||
<p>The common namespaces are:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Prefix</th>
|
||
<th>Contains</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>a:</td>
|
||
<td>Words operating on simple arrays</td>
|
||
</tr>
|
||
<tr>
|
||
<td>ASCII:</td>
|
||
<td>ASCII character constants for control characters</td>
|
||
</tr>
|
||
<tr>
|
||
<td>buffer:</td>
|
||
<td>Words for operating on a simple linear LIFO buffer</td>
|
||
</tr>
|
||
<tr>
|
||
<td>c:</td>
|
||
<td>Words for operating on ASCII character data</td>
|
||
</tr>
|
||
<tr>
|
||
<td>class:</td>
|
||
<td>Contains class handlers for words</td>
|
||
</tr>
|
||
<tr>
|
||
<td>d:</td>
|
||
<td>Words operating on the Dictionary</td>
|
||
</tr>
|
||
<tr>
|
||
<td>err:</td>
|
||
<td>Words for handling errors</td>
|
||
</tr>
|
||
<tr>
|
||
<td>io:</td>
|
||
<td>General I/O words</td>
|
||
</tr>
|
||
<tr>
|
||
<td>n:</td>
|
||
<td>Words operating on numeric data</td>
|
||
</tr>
|
||
<tr>
|
||
<td>prefix:</td>
|
||
<td>Contains prefix handlers</td>
|
||
</tr>
|
||
<tr>
|
||
<td>s:</td>
|
||
<td>Words operating on string data</td>
|
||
</tr>
|
||
<tr>
|
||
<td>v:</td>
|
||
<td>Words operating on variables</td>
|
||
</tr>
|
||
<tr>
|
||
<td>file:</td>
|
||
<td>File I/O words</td>
|
||
</tr>
|
||
<tr>
|
||
<td>f:</td>
|
||
<td>Floating Point words</td>
|
||
</tr>
|
||
<tr>
|
||
<td>gopher:</td>
|
||
<td>Gopher protocol words</td>
|
||
</tr>
|
||
<tr>
|
||
<td>unix:</td>
|
||
<td>Unix system call words</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><h2 id="tips">Tips</h2>
|
||
<p>Avoid using a prefix as the first character of a word name. RETRO<br>
|
||
will look for prefixes first, this will prevent direct use of<br>
|
||
the work in question.</p>
|
||
<p>To find a list of prefix characters, do:</p>
|
||
<pre><code>'prefix: d:words-with
|
||
</code></pre>
|
||
<h1 id="stack-diagrams">Stack Diagrams</h1>
|
||
<p>Most words in RETRO have a stack comment. These look like:</p>
|
||
<pre><code>(-)
|
||
(nn-n)
|
||
</code></pre>
|
||
<p>As with all comments, a stack comment begins with <code>(</code> and<br>
|
||
should end with a <code>)</code>. There are two parts to the comment.<br>
|
||
On the left side of the <code>-</code> is what the word <em>consumes</em>. On<br>
|
||
the right is what it <em>leaves</em>.</p>
|
||
<p>RETRO uses a short notation, with one character per value<br>
|
||
taken or left. In general, the following symbols represent<br>
|
||
certain types of values.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Notation</th>
|
||
<th>Represents</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>b, n, m, o, x, y, z</td>
|
||
<td>generic numeric values</td>
|
||
</tr>
|
||
<tr>
|
||
<td>s</td>
|
||
<td>string</td>
|
||
</tr>
|
||
<tr>
|
||
<td>v</td>
|
||
<td>variable</td>
|
||
</tr>
|
||
<tr>
|
||
<td>p, a</td>
|
||
<td>pointers</td>
|
||
</tr>
|
||
<tr>
|
||
<td>q</td>
|
||
<td>quotation</td>
|
||
</tr>
|
||
<tr>
|
||
<td>d</td>
|
||
<td>dictionary header</td>
|
||
</tr>
|
||
<tr>
|
||
<td>f</td>
|
||
<td><code>TRUE</code> or <code>FALSE</code> flag.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><p>In the case of something like <code>(xyz-m)</code>, RETRO expects z to be<br>
|
||
on the top of the stack, with y below it and x below the y<br>
|
||
value. And after execution, a single value (m) will be left on<br>
|
||
the stack.</p>
|
||
<p>Words with no stack effect have a comment of (-)</p>
|
||
<h1 id="word-classes-1">Word Classes</h1>
|
||
<p>Word classes are one of the two elements at the heart of<br>
|
||
RETRO’s interpreter.</p>
|
||
<p>There are different types of words in a Forth system. At a<br>
|
||
minimum there are data words, regular words, and immediate<br>
|
||
words. There are numerous approaches to dealing with this.</p>
|
||
<p>In RETRO I define special words which receive a pointer and<br>
|
||
decide how to deal with it. These are grouped into a <code>class:</code><br>
|
||
namespace.</p>
|
||
<h2 id="how-it-works">How It Works</h2>
|
||
<p>When a word is found in the dictionary, RETRO will push a<br>
|
||
pointer to the definition (the <code>d:xt</code> field) to the stack<br>
|
||
and then call the word specified by the <code>d:class</code> field.</p>
|
||
<p>The word called is responsible for processing the pointer<br>
|
||
passed to it.</p>
|
||
<p>As a simple case, let’s look at <code>immediate</code> words. These are<br>
|
||
words which will always be called when encountered. A common<br>
|
||
strategy is to have an immediacy bit which the interpreter<br>
|
||
will look at, but RETRO uses a class for this. The class is<br>
|
||
defined:</p>
|
||
<pre><code>:class:immediate (a-) call ;
|
||
</code></pre>
|
||
<p>Or a normal word. These should be called at interpret time<br>
|
||
or compiled into definitions. The handler for this can look<br>
|
||
like:</p>
|
||
<pre><code>:class:word (a-) compiling? [ compile:call ] [ call ] choose ;
|
||
</code></pre>
|
||
<h2 id="using-classes">Using Classes</h2>
|
||
<p>The ability to add new classes is useful. If I wanted to add<br>
|
||
a category of word that preserves an input value, I could do<br>
|
||
it with a class:</p>
|
||
<pre><code>:class:duplicating (a-)
|
||
compiling? [ &dup compile:call ] [ &dup dip ] choose
|
||
class:word ;
|
||
|
||
:duplicating &class:duplicating reclass ;
|
||
|
||
:. n:put nl ; duplicating
|
||
#100 . . .
|
||
</code></pre>
|
||
<h1 id="using-combinators">Using Combinators</h1>
|
||
<p>A combinator is a function that consumes functions as input.<br>
|
||
They are used heavily by the RETRO system.</p>
|
||
<h2 id="types-of-combinators">Types of Combinators</h2>
|
||
<p>Combinators are divided into three primary types: compositional,<br>
|
||
execution flow, and data flow.</p>
|
||
<h2 id="compositional">Compositional</h2>
|
||
<p>A compositional combinator takes elements from the stack and<br>
|
||
returns a new quote.</p>
|
||
<p><code>curry</code> takes a value and a quote and returns a new quote<br>
|
||
applying the specified quote to the specified value. As an<br>
|
||
example,</p>
|
||
<pre><code>:acc (n-) here swap , [ dup v:inc fetch ] curry ;
|
||
</code></pre>
|
||
<p>This would create an accumulator function, which takes an<br>
|
||
initial value and returns a quote that will increase the<br>
|
||
accumulator by 1 each time it is invoked. It will also return<br>
|
||
the latest value. So:</p>
|
||
<pre><code>#10 acc
|
||
dup call n:put
|
||
dup call n:put
|
||
dup call n:put
|
||
</code></pre>
|
||
<h2 id="execution-flow">Execution Flow</h2>
|
||
<p>Combinators of this type execute other functions.</p>
|
||
<h3 id="fundamental">Fundamental</h3>
|
||
<p><code>call</code> takes a quote and executes it immediately.</p>
|
||
<pre><code>[ #1 n:put ] call
|
||
&words call
|
||
</code></pre>
|
||
<h3 id="conditionals">Conditionals</h3>
|
||
<p>RETRO provides three primary combinators for use with<br>
|
||
conditional execution of quotes. These are <code>choose</code>, <code>if</code>,<br>
|
||
and <code>-if</code>.</p>
|
||
<p><code>choose</code> takes a flag and two quotes from the stack. If the<br>
|
||
flag is true, the first quote is executed. If false, the<br>
|
||
second quote is executed.</p>
|
||
<pre><code>#-1 [ 'true s:put ] [ 'false s:put ] choose
|
||
#0 [ 'true s:put ] [ 'false s:put ] choose
|
||
</code></pre>
|
||
<p><code>if</code> takes a flag and one quote from the stack. If the flag is<br>
|
||
true, the quote is executed. If false, the quote is discarded.</p>
|
||
<pre><code>#-1 [ 'true s:put ] if
|
||
#0 [ 'true s:put ] if
|
||
</code></pre>
|
||
<p><code>-if</code> takes a flag and one quote from the stack. If the flag is<br>
|
||
false, the quote is executed. If true, the quote is discarded.</p>
|
||
<pre><code>#-1 [ 'false s:put ] -if
|
||
#0 [ 'false s:put ] -if
|
||
</code></pre>
|
||
<p>RETRO also provides <code>case</code> and <code>s:case</code> for use when you have<br>
|
||
multiple values to check against. This is similar to a <code>switch</code><br>
|
||
in C.</p>
|
||
<p><code>case</code> takes two numbers and a quote. The initial value is<br>
|
||
compared to the second one. If they match, the quote is<br>
|
||
executed. If false, the quote is discarded and the initial<br>
|
||
value is left on the stack.</p>
|
||
<p>Additionally, if the first value was matched, <code>case</code> will exit<br>
|
||
the calling function, but if false, it returns to the calling<br>
|
||
function.</p>
|
||
<p><code>s:case</code> works the same way, but for strings instead of simple<br>
|
||
values.</p>
|
||
<pre><code>:test (n-)
|
||
#1 [ 'Yes s:put ] case
|
||
#2 [ 'No s:put ] case
|
||
drop 'No idea s:put ;
|
||
</code></pre>
|
||
<h3 id="looping">Looping</h3>
|
||
<p>Several combinators are available for handling various looping<br>
|
||
constructs.</p>
|
||
<p><code>while</code> takes a quote from the stack and executes it repeatedly<br>
|
||
as long as the quote returns a true flag on the stack. This flag<br>
|
||
must be well formed and equal -1 or 0.</p>
|
||
<pre><code>#10 [ dup n:put sp n:dec dup 0 -eq? ] while
|
||
</code></pre>
|
||
<p><code>times</code> takes a count and quote from the stack. The quote will<br>
|
||
be executed the number of times specified. No indexes are pushed<br>
|
||
to the stack.</p>
|
||
<pre><code>#1 #10 [ dup n:put sp n:inc ] times drop
|
||
</code></pre>
|
||
<p>There is also a <code>times<with-index></code> variation that provides<br>
|
||
access to the loop index (via <code>I</code>) and parent loop indexes<br>
|
||
(via <code>J</code> and <code>K</code>).</p>
|
||
<pre><code>#10 [ I n:put sp ] times<with-index>
|
||
</code></pre>
|
||
<h2 id="data-flow">Data Flow</h2>
|
||
<p>These combinators exist to simplify stack usage in various<br>
|
||
circumstances.</p>
|
||
<h3 id="preserving">Preserving</h3>
|
||
<p>Preserving combinators execute code while preserving portions<br>
|
||
of the data stack.</p>
|
||
<p><code>dip</code> takes a value and a quote, moves the value off the main<br>
|
||
stack temporarily, executes the quote, and then restores the<br>
|
||
value.</p>
|
||
<pre><code>#10 #20 [ n:inc ] dip
|
||
</code></pre>
|
||
<p>Would yield the following on the stack:</p>
|
||
<pre><code>11 20
|
||
</code></pre>
|
||
<p><code>sip</code> is similar to <code>dip</code>, but leaves a copy of the original<br>
|
||
value on the stack during execution of the quote. So:</p>
|
||
<pre><code>#10 [ n:inc ] sip
|
||
</code></pre>
|
||
<p>Leaves us with:</p>
|
||
<pre><code>11 10
|
||
</code></pre>
|
||
<h3 id="cleave">Cleave</h3>
|
||
<p>Cleave combinators apply multiple quotations to a single value<br>
|
||
or set of values.</p>
|
||
<p><code>bi</code> takes a value and two quotes, it then applies each quote to<br>
|
||
a copy of the value.</p>
|
||
<pre><code>#100 [ n:inc ] [ n:dec ] bi
|
||
</code></pre>
|
||
<p><code>tri</code> takes a value and three quotes. It then applies each quote<br>
|
||
to a copy of the value.</p>
|
||
<pre><code>#100 [ n:inc ] [ n:dec ] [ dup * ] tri
|
||
</code></pre>
|
||
<h3 id="spread">Spread</h3>
|
||
<p>Spread combinators apply multiple quotations to multiple values.<br>
|
||
The asterisk suffixed to these function names signifies that<br>
|
||
they are spread combinators.</p>
|
||
<p><code>bi*</code> takes two values and two quotes. It applies the first<br>
|
||
quote to the first value and the second quote to the second<br>
|
||
value.</p>
|
||
<pre><code>#1 #2 [ n:inc ] [ #2 * ] bi*
|
||
</code></pre>
|
||
<p><code>tri*</code> takes three values and three quotes, applying the<br>
|
||
first quote to the first value, the second quote to the<br>
|
||
second value, and the third quote to the third value.</p>
|
||
<pre><code>#1 #2 #3 [ n:inc ] [ #2 * ] [ n:dec ] tri*
|
||
</code></pre>
|
||
<h3 id="apply">Apply</h3>
|
||
<p>Apply combinators apply a single quotation to multiple values.<br>
|
||
The @ sign suffixed to these function names signifies that they<br>
|
||
are apply combinators.</p>
|
||
<p><code>bi@</code> takes two values and a quote. It then applies the quote to<br>
|
||
each value.</p>
|
||
<pre><code>#1 #2 [ n:inc ] bi@
|
||
</code></pre>
|
||
<p><code>tri@</code> takes three values and a quote. It then applies the quote<br>
|
||
to each value.</p>
|
||
<pre><code>#1 #2 #3 [ n:inc ] tri@
|
||
</code></pre>
|
||
<p>RETRO also provides <code>for-each</code> combinators for various data<br>
|
||
structures. The exact usage of these varies; consult the<br>
|
||
Glossary and relevant chapters for more details on these.</p>
|
||
<h1 id="the-stacks">The Stacks</h1>
|
||
<p>The stacks are a defining feature of Forth. They are are used<br>
|
||
to pass data between words and to track return addresses for<br>
|
||
function calls.</p>
|
||
<p>RETRO always has two stacks, and optionally (if built with<br>
|
||
floating point support) a third.</p>
|
||
<h2 id="data-stack">Data Stack</h2>
|
||
<p>This is the primary stack. Values are placed here, passed to<br>
|
||
words which consume them and then return results. When I<br>
|
||
refer to “the stack”, this is the one I mean. Learning to use<br>
|
||
the stack is a crucial part to making effective use of RETRO.</p>
|
||
<h3 id="placing-values-on-the-stack">Placing Values On The Stack</h3>
|
||
<p>Values can be placed on the stack directly.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Example</th>
|
||
<th>Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>#300123</code></td>
|
||
<td>Push the number <code>300123</code> to the stack</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>$h</code></td>
|
||
<td>Push the ASCII code for <code>h</code> to the stack</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>'hello_world</code></td>
|
||
<td>Push a pointer to a string to the stack</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>&fetch</code></td>
|
||
<td>Push the address of <code>fetch</code> to the stack</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><h3 id="reordering-the-stack">Reordering The Stack</h3>
|
||
<p>RETRO provides a number of <em>shufflers</em> for reordering items<br>
|
||
on the stack.</p>
|
||
<p>Some of the most common ones are:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Word</th>
|
||
<th>Before</th>
|
||
<th>After</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>dup</td>
|
||
<td>#1</td>
|
||
<td>#1 #1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>drop</td>
|
||
<td>#1 #2</td>
|
||
<td>#1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>swap</td>
|
||
<td>#1 #2</td>
|
||
<td>#2 #1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>over</td>
|
||
<td>#1 #2</td>
|
||
<td>#1 #2 #1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>tuck</td>
|
||
<td>#1 #2</td>
|
||
<td>#2 #1 #2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>nip</td>
|
||
<td>#1 #2</td>
|
||
<td>#2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>rot</td>
|
||
<td>#1 #2 #3</td>
|
||
<td>#3 #1 #2</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><p>You can use <code>push</code> and <code>pop</code> to move values to and from the<br>
|
||
address stack. Make sure you <code>pop</code> them back before the word<br>
|
||
ends or RETRO will crash. These two words can not be used<br>
|
||
at the interpreter.</p>
|
||
<p>There is also a special one, <code>reorder</code>, which allows for big<br>
|
||
stack restructuring. This is slow but can be very useful.</p>
|
||
<p>As an example, let’s say we have four values:</p>
|
||
<pre><code>#1 #2 #3 #4
|
||
</code></pre>
|
||
<p>And we want them to become:</p>
|
||
<pre><code>#4 #3 #2 #1
|
||
</code></pre>
|
||
<p>Doing this with the basic shufflers is difficult. You could end<br>
|
||
up with something similar to:</p>
|
||
<pre><code>swap rot push rot pop swap
|
||
</code></pre>
|
||
<p>But with <code>reorder</code>, you can just express the before and after<br>
|
||
states:</p>
|
||
<pre><code>'abcd 'dcba reorder
|
||
</code></pre>
|
||
<h3 id="resetting-the-stack">Resetting The Stack</h3>
|
||
<p>If you need to quickly empty the stack, use <code>reset</code>.</p>
|
||
<h3 id="get-the-stack-depth">Get The Stack Depth</h3>
|
||
<p>To find out how many items are on the stack, use <code>depth</code>.</p>
|
||
<h3 id="displaying-the-stack">Displaying The Stack</h3>
|
||
<p>You can display the stack by running <code>dump-stack</code>.</p>
|
||
<h3 id="data-flow-combinators">Data Flow Combinators</h3>
|
||
<p>RETRO provides <em>combinators</em> for working with data order on<br>
|
||
the stack. These are covered in a later chapter and are worth<br>
|
||
learning to use as they can help provide a cleaner, more<br>
|
||
structured means of working.</p>
|
||
<h3 id="tips-1">Tips</h3>
|
||
<p>The stack is <em>not</em> an array in addressable memory. Don’t try<br>
|
||
to treat it like one.</p>
|
||
<h2 id="address-stack">Address Stack</h2>
|
||
<p>This stack primarily holds return addresses for function calls.<br>
|
||
You normally won’t need to directly interact with this stack,<br>
|
||
but you can use <code>push</code> and <code>pop</code> to move values between the<br>
|
||
data stack and this.</p>
|
||
<h2 id="floating-point-stack">Floating Point Stack</h2>
|
||
<p>If you are using a build with floating point support a third<br>
|
||
stack will be present. Floating point values are kept and<br>
|
||
passed between words using this.</p>
|
||
<p>See the Floating Point chapter for more details on this.</p>
|
||
<h2 id="tips-2">Tips</h2>
|
||
<p>I recommend keeping the data stack shallow. Don’t try to juggle<br>
|
||
too much; it’s better to factor definitions into shorter ones<br>
|
||
that deal with simpler parts of the stack values than to have<br>
|
||
a big definition with a lot of complex shuffling.</p>
|
||
<h2 id="notes">Notes</h2>
|
||
<p>The standard system is configured with a very deep data stack<br>
|
||
(around 2,000 items) and an address stack that is 3x deeper.<br>
|
||
In actual use, your programs are unlikely to ever need this,<br>
|
||
but if you do, keep the limits in mind.</p>
|
||
<h1 id="working-with-arrays">Working With Arrays</h1>
|
||
<p>RETRO offers a number of words for operating on statically sized<br>
|
||
arrays.</p>
|
||
<h2 id="namespace">Namespace</h2>
|
||
<p>The words operating on arrays are kept in an <code>a:</code> namespace.</p>
|
||
<h2 id="creating-arrays">Creating Arrays</h2>
|
||
<p>The easiest way to create an array is to wrap the values in a<br>
|
||
<code>{</code> and <code>}</code> pair:</p>
|
||
<pre><code>{ #1 #2 #3 #4 }
|
||
{ 'this 'is 'an 'array 'of 'strings }
|
||
{ 'this 'is 'a 'mixed 'array #1 #2 #3 }
|
||
</code></pre>
|
||
<p>You can also make an array from a quotation which returns<br>
|
||
values and the number of values to store in the a:</p>
|
||
<pre><code>[ #1 #2 #3 #3 ] a:counted-results
|
||
[ #1 #2 #3 #3 ] a:make
|
||
</code></pre>
|
||
<h2 id="accessing-elements">Accessing Elements</h2>
|
||
<p>You can access a specific value with <code>a:nth</code> and <code>fetch</code> or<br>
|
||
<code>store</code>:</p>
|
||
<pre><code>{ #1 #2 #3 #4 } #3 a:nth fetch
|
||
</code></pre>
|
||
<h2 id="find-the-length">Find The Length</h2>
|
||
<p>Use <code>a:length</code> to find the size of the array.</p>
|
||
<pre><code>{ #1 #2 #3 #4 } a:length
|
||
</code></pre>
|
||
<h2 id="duplicate">Duplicate</h2>
|
||
<p>Use <code>a:dup</code> to make a copy of an a:</p>
|
||
<pre><code>{ #1 #2 #3 #4 } a:dup
|
||
</code></pre>
|
||
<h2 id="filtering">Filtering</h2>
|
||
<p>RETRO provides <code>a:filter</code> which extracts matching values<br>
|
||
from an array. This is used like:</p>
|
||
<pre><code>{ #1 #2 #3 #4 #5 #6 #7 #8 } [ n:even? ] a:filter
|
||
</code></pre>
|
||
<p>The quote will be passed each value in the array and should<br>
|
||
return TRUE or FALSE. Values that lead to TRUE will be collected<br>
|
||
into a new array.</p>
|
||
<h2 id="mapping">Mapping</h2>
|
||
<p><code>a:map</code> applies a quotation to each item in an array and<br>
|
||
constructs a new array from the returned values.</p>
|
||
<p>Example:</p>
|
||
<pre><code>{ #1 #2 #3 } [ #10 * ] a:map
|
||
</code></pre>
|
||
<h2 id="reduce">Reduce</h2>
|
||
<p><code>a:reduce</code> takes an array, a starting value, and a quote. It<br>
|
||
executes the quote once for each item in the array, passing the<br>
|
||
item and the value to the quote. The quote should consume both<br>
|
||
and return a new value.</p>
|
||
<pre><code>{ #1 #2 #3 } #0 [ + ] a:reduce
|
||
</code></pre>
|
||
<h2 id="search">Search</h2>
|
||
<p>RETRO provides <code>a:contains?</code> and <code>a:contains-string?</code><br>
|
||
to search an array for a value (either a number or string) and<br>
|
||
return either TRUE or FALSE.</p>
|
||
<pre><code>#100 { #1 #2 #3 } a:contains?
|
||
'test { 'abc 'def 'test 'ghi } a:contains-string?
|
||
</code></pre>
|
||
<h1 id="working-with-a-buffer">Working With a Buffer</h1>
|
||
<p>RETRO provides words for operating on a linear memory area.<br>
|
||
This can be useful in building strings or custom data<br>
|
||
structures.</p>
|
||
<h2 id="namespace-1">Namespace</h2>
|
||
<p>Words operating on the buffer are kept in the <code>buffer:</code><br>
|
||
namespace.</p>
|
||
<h2 id="implementation">Implementation</h2>
|
||
<p>A buffer is a linear sequence of memory. The buffer words<br>
|
||
provide a means of incrementally storing and retrieving<br>
|
||
values from it.</p>
|
||
<p>The buffer words keep track of the start and end of the<br>
|
||
buffer. They also ensure that an <code>ASCII:NULL</code> is written<br>
|
||
after the last value, which make using them for string<br>
|
||
data easy.</p>
|
||
<h2 id="limitations">Limitations</h2>
|
||
<p>Only one buffer can be active at a time. RETRO provides a<br>
|
||
<code>buffer:preserve</code> combinator to allow using a second one<br>
|
||
before returning to the prior one.</p>
|
||
<h2 id="set-the-active-buffer">Set The Active Buffer</h2>
|
||
<p>To set a buffer as the active one use <code>buffer:set</code>. This takes<br>
|
||
an address.</p>
|
||
<p>The buffer will be assumed to be empty. The inital value will<br>
|
||
be set to ASCII:NULL.</p>
|
||
<h2 id="add-value">Add Value</h2>
|
||
<p>Use <code>buffer:add</code> to append a value to the buffer. This takes<br>
|
||
a single value and will also add an ASCII:NULL after the end<br>
|
||
of the buffer.</p>
|
||
<h2 id="fetch-last-value">Fetch Last Value</h2>
|
||
<p>To return the last value in the buffer you can use <code>buffer:get</code>.<br>
|
||
This removes the value and sets an ASCII:NULL in the memory<br>
|
||
location the returned value occupied.</p>
|
||
<h2 id="get-data-about-the-buffer">Get Data About The Buffer</h2>
|
||
<p>RETRO provides <code>buffer:start</code> to get the initial address in<br>
|
||
the buffer, <code>buffer:end</code> to get the last address (ignoring the<br>
|
||
ASCII:NULL), and <code>buffer:size</code> to return the number of values<br>
|
||
in the buffer.</p>
|
||
<h2 id="reset">Reset</h2>
|
||
<p>You can reset a buffer to the empty state using <code>buffer:empty</code>.</p>
|
||
<h2 id="example">Example</h2>
|
||
<p>To begin, create a memory region to use as a buffer.</p>
|
||
<pre><code>'Test d:create #1025 allot
|
||
</code></pre>
|
||
<p>Then you can set this as the current buffer:</p>
|
||
<pre><code>&Test buffer:set
|
||
</code></pre>
|
||
<p>When a buffer is set, the vocabulary sets an internal<br>
|
||
index to the first address in it. This will be<br>
|
||
incremented when you add data and decremented when you<br>
|
||
remove data.</p>
|
||
<p>Let’s add some stuff using <code>buffer:add</code>:</p>
|
||
<pre><code>#100 buffer:add
|
||
#200 buffer:add
|
||
#300 buffer:add
|
||
</code></pre>
|
||
<p>And then retreive the values:</p>
|
||
<pre><code>buffer:get n:put nl
|
||
buffer:get n:put nl
|
||
buffer:get n:put nl
|
||
</code></pre>
|
||
<p>You can remove all values using <code>buffer:empty</code>:</p>
|
||
<pre><code>#100 buffer:add
|
||
#200 buffer:add
|
||
#300 buffer:add
|
||
buffer:empty
|
||
</code></pre>
|
||
<p>And ask the buffer how many items it contains:</p>
|
||
<pre><code>buffer:size n:put nl
|
||
#100 buffer:add
|
||
#200 buffer:add
|
||
#300 buffer:add
|
||
buffer:size n:put nl
|
||
buffer:empty
|
||
</code></pre>
|
||
<p>The other functions are <code>buffer:start</code>, which returns<br>
|
||
the address of the buffer, <code>buffer:end</code>, which returns<br>
|
||
the address of the last value, and <code>buffer:preserve</code>.<br>
|
||
The first is easy to demo:</p>
|
||
<pre><code>buffer:start Test eq? n:put nl
|
||
</code></pre>
|
||
<p>The last one is useful. Only one buffer is ever active<br>
|
||
at a given time. The <code>buffer:preserve</code> combinator lets<br>
|
||
you execute a word, saving and restoring the current<br>
|
||
buffer indexes. So the word could assign and use a new<br>
|
||
buffer and this will reset the previous one after<br>
|
||
control returns.</p>
|
||
<p>There are a few notes that need to be considered. The<br>
|
||
preserve combinator saves the start and current index<br>
|
||
but <em>not</em> the contents. If the word you call uses the<br>
|
||
same buffer, the contents will remain altered.</p>
|
||
<p>Finally, the buffer words have one interesting trait:<br>
|
||
they store an ASCII NULL after adding each item to the<br>
|
||
buffer. This lets one use them to build strings easily.</p>
|
||
<pre><code>Test buffer:set
|
||
$h buffer:add
|
||
$e buffer:add
|
||
$l buffer:add
|
||
$l buffer:add
|
||
$o buffer:add
|
||
$, buffer:add
|
||
#32 buffer:add
|
||
$w buffer:add
|
||
$o buffer:add
|
||
$r buffer:add
|
||
$l buffer:add
|
||
$d buffer:add
|
||
buffer:start s:put nl
|
||
</code></pre>
|
||
<h1 id="working-with-characters">Working With Characters</h1>
|
||
<p>RETRO provides words for working with ASCII characters.</p>
|
||
<h2 id="prefix">Prefix</h2>
|
||
<p>Character constants are returned using the <code>$</code> prefix.</p>
|
||
<h2 id="namespace-2">Namespace</h2>
|
||
<p>Words operating on characters are in the <code>c:</code> namespace.</p>
|
||
<h2 id="classification">Classification</h2>
|
||
<p>RETRO provides a number of words to determine if a character<br>
|
||
fits into predefined groups.</p>
|
||
<p>The primary words for this are:</p>
|
||
<ul>
|
||
<li><code>c:consonant?</code></li>
|
||
<li><code>c:digit?</code></li>
|
||
<li><code>c:letter?</code></li>
|
||
<li><code>c:lowercase?</code></li>
|
||
<li><code>c:uppercase?</code></li>
|
||
<li><code>c:visible?</code></li>
|
||
<li><code>c:vowel?</code></li>
|
||
<li><code>c:whitespace?</code></li>
|
||
</ul>
|
||
<p>There are also corresponding “not” forms:</p>
|
||
<ul>
|
||
<li><code>c:-consonant?</code></li>
|
||
<li><code>c:-digit?</code></li>
|
||
<li><code>c:-lowercase?</code></li>
|
||
<li><code>c:-uppercase?</code></li>
|
||
<li><code>c:-visible?</code></li>
|
||
<li><code>c:-vowel?</code></li>
|
||
<li><code>c:-whitespace?</code></li>
|
||
</ul>
|
||
<p>All of these take a character and return either a <code>TRUE</code> or<br>
|
||
<code>FALSE</code> flag.</p>
|
||
<h2 id="conversions">Conversions</h2>
|
||
<p>A few words are provided to convert case. Each takes a character<br>
|
||
and returns the modified character.</p>
|
||
<ul>
|
||
<li><code>c:to-lower</code></li>
|
||
<li><code>c:to-number</code></li>
|
||
<li><code>c:to-upper</code></li>
|
||
<li><code>c:toggle-case</code></li>
|
||
</ul>
|
||
<p>RETRO also has <code>c:to-string</code>, which takes a character and<br>
|
||
creates a new temporary string with the character.</p>
|
||
<h2 id="io">I/O</h2>
|
||
<p>Characters can be displayed using <code>c:put</code>.</p>
|
||
<pre><code>$a c:put
|
||
</code></pre>
|
||
<p>With the default system on BSD, Linux, and macOS (and other<br>
|
||
Unix style hosts), <code>c:get</code> is provided to read input. This<br>
|
||
may be buffered, depending on the host.</p>
|
||
<h1 id="working-with-the-dictionary">Working With The Dictionary</h1>
|
||
<p>The Dictionary is a linked list containing the dictionary<br>
|
||
headers.</p>
|
||
<h2 id="namespace-3">Namespace</h2>
|
||
<p>Words operating on the dictionary are in the <code>d:</code> namespace.</p>
|
||
<h2 id="variables">Variables</h2>
|
||
<p><code>Dictionary</code> is a variable holding a pointer to the most recent<br>
|
||
header.</p>
|
||
<h2 id="header-structure">Header Structure</h2>
|
||
<p>Each entry follows the following structure:</p>
|
||
<pre><code>Offset Contains
|
||
------ ---------------------------
|
||
0000 Link to Prior Header
|
||
0001 Link to XT
|
||
0002 Link to Class Handler
|
||
0003+ Word name (null terminated)
|
||
</code></pre>
|
||
<p>RETRO provides words for accessing the fields in a portable<br>
|
||
manner. It’s recommended to use these to allow for future<br>
|
||
revision of the header structure.</p>
|
||
<h2 id="accessing-fields">Accessing Fields</h2>
|
||
<p>Given a pointer to a header, you can use <code>d:xt</code>, <code>d:class</code>,<br>
|
||
and <code>d:name</code> to access the address of each specific field.<br>
|
||
There is no <code>d:link</code>, as the link will always be the first<br>
|
||
field.</p>
|
||
<h2 id="shortcuts-for-the-latest-header">Shortcuts For The Latest Header</h2>
|
||
<p>RETRO provides several words for operating on the most recent<br>
|
||
header.</p>
|
||
<p><code>d:last</code> returns a pointer to the latest header. <code>d:last<xt></code><br>
|
||
will give the contents of the <code>d:xt</code> field for the latest<br>
|
||
header. There are also <code>d:last<class></code> and <code>d:last<name></code>.</p>
|
||
<h2 id="adding-headers">Adding Headers</h2>
|
||
<p>Two words exist for making new headers. The easy one is<br>
|
||
<code>d:create</code>. This takes a string for the name and makes a<br>
|
||
new header with the class set to <code>class:data</code> and the XT<br>
|
||
field pointing to <code>here</code>.</p>
|
||
<p>Example:</p>
|
||
<pre><code>'Base d:create
|
||
</code></pre>
|
||
<p>The other is <code>d:add-header</code>. This takes a string, a pointer<br>
|
||
to the class handler, and a pointer for the XT field and<br>
|
||
builds a new header using these.</p>
|
||
<p>Example:</p>
|
||
<pre><code>'Base &class:data #10000 d:add-header
|
||
</code></pre>
|
||
<h2 id="searching">Searching</h2>
|
||
<p>RETRO provides two words for searching the dictionary.</p>
|
||
<p><code>d:lookup</code> takes a string and tries to find it in the<br>
|
||
dictionary. It will return a pointer to the dictionary header<br>
|
||
or a value of zero if the word was not found.</p>
|
||
<p><code>d:lookup-xt</code> takes a pointer and will return the dictionary<br>
|
||
header that has this as the <code>d:xt</code> field, or zero if no match<br>
|
||
is found.</p>
|
||
<h2 id="iteration">Iteration</h2>
|
||
<p>You can use the <code>d:for-each</code> combinator to iterate over all<br>
|
||
entries in the dictionary. For instance, to display the names<br>
|
||
of all words:</p>
|
||
<pre><code>[ d:name s:put sp ] d:for-each
|
||
</code></pre>
|
||
<p>For each entry, this combinator will push a pointer to the<br>
|
||
entry to the stack and call the quotation.</p>
|
||
<h2 id="listing-words">Listing Words</h2>
|
||
<p>Most Forth systems provide WORDS for listing the names of all<br>
|
||
words in the dictionary. RETRO does as well, but this is named<br>
|
||
<code>d:words</code>.</p>
|
||
<p>This isn’t super useful as looking through several hundred<br>
|
||
names is annoying. RETRO also provides <code>d:words-with</code> to help<br>
|
||
in filtering the results.</p>
|
||
<p>Example:</p>
|
||
<pre><code>'class: d:words-with
|
||
</code></pre>
|
||
<h1 id="working-with-floating-point">Working With Floating Point</h1>
|
||
<p>Some RETRO systems include support for floating point numbers.<br>
|
||
When present, this is built over the system <code>libm</code> using the<br>
|
||
C <code>double</code> type.</p>
|
||
<p>Floating point values are typically 64 bit IEEE 754 double<br>
|
||
precision (1 bit for the sign, 11 bits for the exponent, and<br>
|
||
the remaining 52 bits for the value), i.e. 15 decimal digits<br>
|
||
of precision.</p>
|
||
<h2 id="prefix-1">Prefix</h2>
|
||
<p>Floating point numbers start with a <code>.</code></p>
|
||
<p>Examples:</p>
|
||
<p>Token Value<br>
|
||
.1 1.0<br>
|
||
.0.5 0.5<br>
|
||
.-.4 -0.4<br>
|
||
.1.3 1.3</p>
|
||
<h2 id="namespace-4">Namespace</h2>
|
||
<p>Floating point words are in the <code>f:</code> namespace. There is also<br>
|
||
a related <code>e:</code> namespace for <em>encoded values</em>, which allows<br>
|
||
storing of floats in standard memory.</p>
|
||
<h2 id="operation">Operation</h2>
|
||
<p>Floating point values exist on a separate stack, and are bigger<br>
|
||
than the standard memory cells, so can not be directly stored<br>
|
||
and fetched from memory.</p>
|
||
<p>The floating point system also provides an alternate stack that<br>
|
||
can be used to temporarily store values.</p>
|
||
<p>The following words exist for arranging values on the floating<br>
|
||
point stack. These are direct analogs to the non-prefiexd words<br>
|
||
for dealing with the data stack.</p>
|
||
<ul>
|
||
<li><code>f:nip</code></li>
|
||
<li><code>f:over</code></li>
|
||
<li><code>f:depth</code></li>
|
||
<li><code>f:drop</code></li>
|
||
<li><code>f:drop-pair</code></li>
|
||
<li><code>f:dup</code></li>
|
||
<li><code>f:dup-pair</code></li>
|
||
<li><code>f:dump-stack</code></li>
|
||
<li><code>f:tuck</code></li>
|
||
<li><code>f:swap</code></li>
|
||
<li><code>f:rot</code></li>
|
||
</ul>
|
||
<p>For the secondary floating point stack, the following words are<br>
|
||
provided:</p>
|
||
<ul>
|
||
<li><code>f:push</code></li>
|
||
<li><code>f:pop</code></li>
|
||
<li><code>f:adepth</code></li>
|
||
<li><code>f:dump-astack</code></li>
|
||
</ul>
|
||
<h2 id="constants">Constants</h2>
|
||
<pre><code>| Name | Returns |
|
||
| -------- | ----------------- |
|
||
| `f:E` | Euler's number |
|
||
| `f:-INF` | Negative infinity |
|
||
| `f:INF` | Positive infinity |
|
||
| `f:NAN` | Not a Number |
|
||
| `f:PI` | PI |
|
||
</code></pre>
|
||
<h2 id="comparisons">Comparisons</h2>
|
||
<p>The basic set of comparators are the same as those for<br>
|
||
operating on integers. These are:</p>
|
||
<ul>
|
||
<li><code>f:-eq?</code></li>
|
||
<li><code>f:between?</code></li>
|
||
<li><code>f:eq?</code></li>
|
||
<li><code>f:gt?</code></li>
|
||
<li><code>f:lt?</code></li>
|
||
<li><code>f:negative?</code></li>
|
||
<li><code>f:positive?</code></li>
|
||
<li><code>f:case</code></li>
|
||
</ul>
|
||
<p>There are also a few additions for comparing to special values<br>
|
||
like infinity and NaN.</p>
|
||
<ul>
|
||
<li><code>f:-inf?</code></li>
|
||
<li><code>f:inf?</code></li>
|
||
<li><code>f:nan?</code></li>
|
||
</ul>
|
||
<h2 id="basic-math">Basic Math</h2>
|
||
<ul>
|
||
<li><code>f:*</code></li>
|
||
<li><code>f:+</code></li>
|
||
<li><code>f:-</code></li>
|
||
<li><code>f:/</code></li>
|
||
<li><code>f:abs</code></li>
|
||
<li><code>f:floor</code></li>
|
||
<li><code>f:inc</code></li>
|
||
<li><code>f:limit</code></li>
|
||
<li><code>f:max</code></li>
|
||
<li><code>f:min</code></li>
|
||
<li><code>f:negate</code></li>
|
||
<li><code>f:power</code></li>
|
||
<li><code>f:ceiling</code></li>
|
||
<li><code>f:dec</code></li>
|
||
<li><code>f:log</code></li>
|
||
<li><code>f:sqrt</code></li>
|
||
<li><code>f:square</code></li>
|
||
<li><code>f:round</code></li>
|
||
<li><code>f:sign</code></li>
|
||
<li><code>f:signed-sqrt</code></li>
|
||
<li><code>f:signed-square</code></li>
|
||
</ul>
|
||
<h2 id="geometry">Geometry</h2>
|
||
<p>RETRO provides a small number of words for doing geometric<br>
|
||
related calculations.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Word</th>
|
||
<th>Returns</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>f:acos</code></td>
|
||
<td>arc cosine</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>f:asin</code></td>
|
||
<td>arc sine</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>f:atan</code></td>
|
||
<td>arc tangent</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>f:cos</code></td>
|
||
<td>cosine</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>f:sin</code></td>
|
||
<td>sine</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>f:tan</code></td>
|
||
<td>tangent</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><h2 id="storage-and-retrieval">Storage and Retrieval</h2>
|
||
<p>By leveraging the encoded value functions, RETRO is able to<br>
|
||
allow storage of floating point values in memory. This does<br>
|
||
have a tradeoff in accuracy as the memory cells are considerably<br>
|
||
smaller than a full floating point size.</p>
|
||
<p>You can use <code>f:fetch</code> to fetch a floating point value and<br>
|
||
<code>f:store</code> to store one.</p>
|
||
<p>If you need more precision, try Kiyoshi Yoneda’s FloatVar<br>
|
||
example (<code>example/FloatVar.forth</code>), which includes words to<br>
|
||
store and retrieve values using multiple cells.</p>
|
||
<ul>
|
||
<li><code>f:to-number</code></li>
|
||
<li><code>f:to-string</code></li>
|
||
</ul>
|
||
<h2 id="io-1">I/O</h2>
|
||
<p>The floating point vocabulary has a single I/O word, <code>f:put</code>,<br>
|
||
for the display of floating point numbers.</p>
|
||
<h2 id="encoded-values">Encoded Values</h2>
|
||
<p>RETRO provides a means of encoding and decoding floating point<br>
|
||
values into standard integer cells. This is based on the paper<br>
|
||
“Encoding floating point values to shorter integers” by Kiyoshi<br>
|
||
Yoneda and Charles Childers.</p>
|
||
<ul>
|
||
<li><code>f:E1</code></li>
|
||
<li><code>f:to-e</code></li>
|
||
<li><code>e:-INF</code></li>
|
||
<li><code>e:-inf?</code></li>
|
||
<li><code>e:INF</code></li>
|
||
<li><code>e:MAX</code></li>
|
||
<li><code>e:MIN</code></li>
|
||
<li><code>e:NAN</code></li>
|
||
<li><code>e:clip</code></li>
|
||
<li><code>e:inf?</code></li>
|
||
<li><code>e:max?</code></li>
|
||
<li><code>e:min?</code></li>
|
||
<li><code>e:n?</code></li>
|
||
<li><code>e:nan?</code></li>
|
||
<li><code>e:put</code></li>
|
||
<li><code>e:to-f</code></li>
|
||
<li><code>e:zero?</code></li>
|
||
</ul>
|
||
<h1 id="working-with-numbers">Working With Numbers</h1>
|
||
<p>Numbers in RETRO are signed, 32 bit integers with a range of<br>
|
||
-2,147,483,648 to 2,147,483,647.</p>
|
||
<h2 id="token-prefix">Token Prefix</h2>
|
||
<p>All numbers start with a <code>#</code> prefix.</p>
|
||
<h2 id="namespace-5">Namespace</h2>
|
||
<p>Most words operating on numbers are in the <code>n:</code> namespace.</p>
|
||
<h1 id="working-with-pointers">Working With Pointers</h1>
|
||
<h2 id="prefix-2">Prefix</h2>
|
||
<p>Pointers are returned by the <code>&</code> prefix.</p>
|
||
<h2 id="examples">Examples</h2>
|
||
<pre><code>'Base var
|
||
&Base fetch
|
||
#10 &Base store
|
||
|
||
#10 &n:inc call
|
||
</code></pre>
|
||
<h2 id="notes-1">Notes</h2>
|
||
<p>The use of <code>&</code> to get a pointer to a data structure (with a<br>
|
||
word class of <code>class:data</code>) is not required. I like to use it<br>
|
||
anyway as it makes my intent a little clearer.</p>
|
||
<p>Pointers are useful with combinators. Consider:</p>
|
||
<pre><code>:abs dup n:negative? [ n:negate ] if ;
|
||
</code></pre>
|
||
<p>Since the target quote body is a single word, it is more<br>
|
||
efficient to use a pointer instead:</p>
|
||
<pre><code>:abs dup n:negative? &n:negate if ;
|
||
</code></pre>
|
||
<p>The advantages are speed (saves a level of call/return by<br>
|
||
avoiding the quotation) and size (for the same reason).<br>
|
||
This may be less readable though, so consider the balance<br>
|
||
of performance to readability when using this approach.</p>
|
||
<h1 id="working-with-strings">Working With Strings</h1>
|
||
<p>Strings in RETRO are NULL terminated sequences of values<br>
|
||
representing characters. Being NULL terminated, they can’t<br>
|
||
contain a NULL (ASCII 0).</p>
|
||
<p>The character words in RETRO are built around ASCII, but<br>
|
||
strings can contain UTF8 encoded data if the host platform<br>
|
||
allows. Words like <code>s:length</code> will return the number of bytes,<br>
|
||
not the number of logical characters in this case.</p>
|
||
<h2 id="prefix-3">Prefix</h2>
|
||
<p>Strings begin with a single <code>'</code>.</p>
|
||
<pre><code>'Hello
|
||
'This_is_a_string
|
||
'This_is_a_much_longer_string_12345_67890_!!!
|
||
</code></pre>
|
||
<p>RETRO will replace spaces with underscores. If you need both<br>
|
||
spaces and underscores in a string, escape the underscores and<br>
|
||
use <code>s:format</code>:</p>
|
||
<pre><code>'This_has_spaces_and_under\_scored_words. s:format
|
||
</code></pre>
|
||
<h2 id="namespace-6">Namespace</h2>
|
||
<p>Words operating on strings are in the <code>s:</code> namespace.</p>
|
||
<h2 id="lifetime">Lifetime</h2>
|
||
<p>At the interpreter, strings get allocated in a rotating buffer.<br>
|
||
This is used by the words operating on strings, so if you need<br>
|
||
to keep them around, use <code>s:keep</code> or <code>s:copy</code> to move them to<br>
|
||
more permanent storage.</p>
|
||
<p>In a definition, the string is compiled inline and so is in<br>
|
||
permanent memory.</p>
|
||
<p>You can manually manage the string lifetime by using <code>s:keep</code><br>
|
||
to place it into permanent memory or <code>s:temp</code> to copy it to<br>
|
||
the rotating buffer.</p>
|
||
<h2 id="mutability">Mutability</h2>
|
||
<p>Strings are mutable. If you need to ensure that a string is<br>
|
||
not altered, make a copy before operating on it or see the<br>
|
||
individual glossary entries for notes on words that may do<br>
|
||
this automatically.</p>
|
||
<h2 id="searching-1">Searching</h2>
|
||
<p>RETRO provides four words for searching within a string.</p>
|
||
<p><code>s:contains-char?</code><br>
|
||
<code>s:contains-string?</code><br>
|
||
<code>s:index-of</code><br>
|
||
<code>s:index-of-string</code></p>
|
||
<h2 id="comparisons-1">Comparisons</h2>
|
||
<p><code>s:eq?</code><br>
|
||
<code>s:case</code></p>
|
||
<h2 id="extraction">Extraction</h2>
|
||
<p>To obtain a new string containing the first <code>n</code> characters from<br>
|
||
a source string, use <code>s:left</code>:</p>
|
||
<pre><code>'Hello_World #5 s:left
|
||
</code></pre>
|
||
<p>To obtain a new string containing the last <code>n</code> characters from<br>
|
||
a source string, use <code>s:right</code>:</p>
|
||
<pre><code>'Hello_World #5 s:right
|
||
</code></pre>
|
||
<p>If you need to extract data from the middle of the string, use<br>
|
||
<code>s:substr</code>. This takes a string, the offset of the first<br>
|
||
character, and the number of characters to extract.</p>
|
||
<pre><code>'Hello_World #3 #5 s:substr
|
||
</code></pre>
|
||
<h2 id="joining">Joining</h2>
|
||
<p>You can use <code>s:append</code> or <code>s:prepend</code> to merge two strings.</p>
|
||
<pre><code>'First 'Second s:append
|
||
'Second 'First s:prepend
|
||
</code></pre>
|
||
<h2 id="tokenization">Tokenization</h2>
|
||
<p><code>s:tokenize</code><br>
|
||
<code>s:tokenize-on-string</code><br>
|
||
<code>s:split</code><br>
|
||
<code>s:split-on-string</code></p>
|
||
<h2 id="conversions-1">Conversions</h2>
|
||
<p>To convert the case of a string, RETRO provides <code>s:to-lower</code><br>
|
||
and <code>s:to-upper</code>.</p>
|
||
<p><code>s:to-number</code> is provided to convert a string to an integer<br>
|
||
value. This has a few limitations:</p>
|
||
<ul>
|
||
<li>only supports decimal</li>
|
||
<li>non-numeric characters will result in incorrect values</li>
|
||
</ul>
|
||
<h2 id="cleanup">Cleanup</h2>
|
||
<p>RETRO provides a handful of words for cleaning up strings.</p>
|
||
<p><code>s:chop</code> will remove the last character from a string. This<br>
|
||
is done by replacing it with an ASCII:NULL.</p>
|
||
<p><code>s:trim</code> removes leading and trailing whitespace from a string.<br>
|
||
For more control, there is also <code>s:trim-left</code> and <code>s:trim-right</code><br>
|
||
which let you trim just the leading or trailing end as desired.</p>
|
||
<h2 id="combinators">Combinators</h2>
|
||
<p><code>s:for-each</code><br>
|
||
<code>s:filter</code><br>
|
||
<code>s:map</code></p>
|
||
<h2 id="other">Other</h2>
|
||
<p><code>s:evaluate</code><br>
|
||
<code>s:copy</code><br>
|
||
<code>s:reverse</code><br>
|
||
<code>s:hash</code><br>
|
||
<code>s:length</code><br>
|
||
<code>s:replace</code><br>
|
||
<code>s:format</code><br>
|
||
<code>s:empty</code></p>
|
||
<h2 id="controlling-the-temporary-buffers">Controlling The Temporary Buffers</h2>
|
||
<p>As dicussed in the Lifetime subsection, temporary strings are<br>
|
||
allocated in a rotating buffer. The details of this can be<br>
|
||
altered by updating two variables.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Variable</th>
|
||
<th>Holds</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>TempStrings</td>
|
||
<td>The number of temporary strings</td>
|
||
</tr>
|
||
<tr>
|
||
<td>TempStringMax</td>
|
||
<td>The maximum length of a temporary string</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><p>For example, to increase the number of temporary strings to<br>
|
||
48:</p>
|
||
<pre><code>#48 !TempStrings
|
||
</code></pre>
|
||
<p>The defaults are:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Variable</th>
|
||
<th>Default</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>TempStrings</td>
|
||
<td>32</td>
|
||
</tr>
|
||
<tr>
|
||
<td>TempStringMax</td>
|
||
<td>512</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><p>It’s also important to note that altering these will affect<br>
|
||
the memory map for all temporary buffers. Do not use anything<br>
|
||
already in the buffers after updating these or you will risk<br>
|
||
data corruption and possible crashes.</p>
|
||
<h1 id="working-with-assembly-language">Working With Assembly Language</h1>
|
||
<p>RETRO runs on a virtual machine called Nga. It provides a<br>
|
||
standard assembler for this called <em>Muri</em>.</p>
|
||
<p>Muri is a simple, multipass model that’s not fancy, but<br>
|
||
suffices for RETRO’s needs.</p>
|
||
<h2 id="assembling-a-standalone-file">Assembling A Standalone File</h2>
|
||
<p>A small example (<em>test.muri</em>)</p>
|
||
<pre><code>~~~
|
||
i liju....
|
||
r main
|
||
: c:put
|
||
i liiire..
|
||
i 0
|
||
: main
|
||
i lilica..
|
||
d 97
|
||
i liju....
|
||
r main
|
||
~~~
|
||
</code></pre>
|
||
<p>Assembling it:</p>
|
||
<pre><code>retro-muri test.muri
|
||
</code></pre>
|
||
<p>So breaking down: Muri extracts the assembly code blocks to<br>
|
||
assemble, then proceeds to do the assembly. Each source line<br>
|
||
starts with a directive, followed by a space, and then ending<br>
|
||
with a value.</p>
|
||
<dl>
|
||
<dt>The directives are:</dt>
|
||
<dd>value is a label<br>
|
||
i value is an instruction bundle<br>
|
||
d value is a numeric value<br>
|
||
r value is a reference<br>
|
||
s value is a string to inline</dd>
|
||
</dl>
|
||
<p>Instructions for Nga are provided as bundles. Each memory<br>
|
||
location can store up to four instructions. And each instruction<br>
|
||
gets a two character identifier.</p>
|
||
<p>From the list of instructions:</p>
|
||
<pre><code>0 nop 5 push 10 ret 15 fetch 20 div 25 zret
|
||
1 lit 6 pop 11 eq 16 store 21 and 26 end
|
||
2 dup 7 jump 12 neq 17 add 22 or 27 ienum
|
||
3 drop 8 call 13 lt 18 sub 23 xor 28 iquery
|
||
4 swap 9 ccall 14 gt 19 mul 24 shift 29 iinvoke
|
||
</code></pre>
|
||
<p>This reduces to:</p>
|
||
<pre><code>0 .. 5 pu 10 re 15 fe 20 di 25 zr
|
||
1 li 6 po 11 eq 16 st 21 an 26 en
|
||
2 du 7 ju 12 ne 17 ad 22 or 27 ie
|
||
3 dr 8 ca 13 lt 18 su 23 xo 28 iq
|
||
4 sw 9 cc 14 gt 19 mu 24 sh 29 ii
|
||
</code></pre>
|
||
<p>Most are just the first two letters of the instruction name. I<br>
|
||
use <code>..</code> instead of <code>no</code> for <code>NOP</code>, and the first letter of<br>
|
||
each I/O instruction name. So a bundle may look like:</p>
|
||
<pre><code>dumure..
|
||
</code></pre>
|
||
<p>(This would correspond to <code>dup multiply return nop</code>).</p>
|
||
<h2 id="runtime-assembler">Runtime Assembler</h2>
|
||
<p>RETRO also has a runtime variation of Muri that can be used<br>
|
||
when you need to generate more optimal code. So one can write:</p>
|
||
<pre><code>:n:square dup * ;
|
||
</code></pre>
|
||
<p>Or:</p>
|
||
<pre><code>:n:square as{ 'dumure.. i }as ;
|
||
</code></pre>
|
||
<p>The second one will be faster, as the entire definition is one<br>
|
||
bundle, which reduces memory reads and decoding by 2/3.</p>
|
||
<p>Doing this is less readable, so I only recommend doing so after<br>
|
||
you have finalized working RETRO level code and determined the<br>
|
||
best places to optimize.</p>
|
||
<p>The runtime assembler has the following directives:</p>
|
||
<pre><code>i value is an instruction bundle
|
||
d value is a numeric value
|
||
r value is a reference
|
||
</code></pre>
|
||
<p>Additionally, in the runtime assembler, these are reversed:</p>
|
||
<pre><code>'dudumu.. i
|
||
</code></pre>
|
||
<p>Instead of:</p>
|
||
<pre><code>i dudumu..
|
||
</code></pre>
|
||
<h1 id="lexical-scope">Lexical Scope</h1>
|
||
<p>RETRO has a single dictionary, but does provide a means of using<br>
|
||
lexical scope to keep this dictionary clean.</p>
|
||
<h2 id="example-1">Example</h2>
|
||
<pre><code>{{
|
||
'A var
|
||
:++A &A v:inc ;
|
||
---reveal---
|
||
:B ++A ++A @A n:put nl ;
|
||
}}
|
||
</code></pre>
|
||
<p>In this example, the lexical namespace is created with <code>{{</code>. A<br>
|
||
variable (<code>A</code>) and word (<code>++A</code>) are defined. Then a marker is<br>
|
||
set with <code>---reveal---</code>. Another word (<code>B</code>) is defined, and the<br>
|
||
lexical area is closed with <code>}}</code>.</p>
|
||
<p>The headers between <code>{{</code> and <code>---reveal---</code> are then hidden from<br>
|
||
the dictionary, leaving only the headers between <code>---reveal---</code><br>
|
||
and <code>}}</code> exposed.</p>
|
||
<h2 id="notes-2">Notes</h2>
|
||
<p>This only affects word visibility within the scoped area. As an<br>
|
||
example:</p>
|
||
<pre><code>:a #1 ;
|
||
|
||
{{
|
||
:a #2 ;
|
||
---reveal---
|
||
:b 'a s:evaluate n:put ;
|
||
}}
|
||
</code></pre>
|
||
<p>In this, after <code>}}</code> closes the area, the <code>:a #2 ;</code> is hidden and<br>
|
||
the <code>s:evaluate</code> will find the <code>:a #1 ;</code> when <code>b</code> is run.</p>
|
||
<h1 id="internals">Internals</h1>
|
||
<p>The next few chapters dive into RETRO’s architecture. If you<br>
|
||
seek to implement a port to a new platform or to extend the<br>
|
||
I/O functionality you’ll find helpful information here.</p>
|
||
<h1 id="internals-nga-virtual-machine">Internals: Nga Virtual Machine</h1>
|
||
<h2 id="overview">Overview</h2>
|
||
<p>At the heart of RETRO is a simple MISC (minimal instruction<br>
|
||
set computer) processor for a dual stack architecture.</p>
|
||
<p>This is a very simple and straightforward system. There are<br>
|
||
30 instructions. The memory is a linear array of signed 32<br>
|
||
bit values. And there are two stacks: one for data and one<br>
|
||
for return addresses.</p>
|
||
<h2 id="instrution-table">Instrution Table</h2>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Opcode</th>
|
||
<th>Muri</th>
|
||
<th>Full Name</th>
|
||
<th>Data Stack</th>
|
||
<th>Address Stack</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>…</td>
|
||
<td>nop</td>
|
||
<td>-</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>li</td>
|
||
<td>lit</td>
|
||
<td>-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>du</td>
|
||
<td>dup</td>
|
||
<td>n-nn</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>dr</td>
|
||
<td>drop</td>
|
||
<td>n-</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4</td>
|
||
<td>sw</td>
|
||
<td>swap</td>
|
||
<td>xy-yx</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>pu</td>
|
||
<td>push</td>
|
||
<td>n-</td>
|
||
<td>-n</td>
|
||
</tr>
|
||
<tr>
|
||
<td>6</td>
|
||
<td>po</td>
|
||
<td>pop</td>
|
||
<td>-n</td>
|
||
<td>n-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>7</td>
|
||
<td>ju</td>
|
||
<td>jump</td>
|
||
<td>a-</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>8</td>
|
||
<td>ca</td>
|
||
<td>call</td>
|
||
<td>a-</td>
|
||
<td>-A</td>
|
||
</tr>
|
||
<tr>
|
||
<td>9</td>
|
||
<td>cc</td>
|
||
<td>conditional call</td>
|
||
<td>af-</td>
|
||
<td>-A</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>re</td>
|
||
<td>return</td>
|
||
<td>-</td>
|
||
<td>A-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>11</td>
|
||
<td>eq</td>
|
||
<td>equality</td>
|
||
<td>xy-f</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>12</td>
|
||
<td>ne</td>
|
||
<td>inequality</td>
|
||
<td>xy-f</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>13</td>
|
||
<td>lt</td>
|
||
<td>less than</td>
|
||
<td>xy-f</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>14</td>
|
||
<td>gt</td>
|
||
<td>greater than</td>
|
||
<td>xy-f</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>15</td>
|
||
<td>fe</td>
|
||
<td>fetch</td>
|
||
<td>a-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>16</td>
|
||
<td>st</td>
|
||
<td>store</td>
|
||
<td>na-</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>17</td>
|
||
<td>ad</td>
|
||
<td>addition</td>
|
||
<td>xy-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>18</td>
|
||
<td>su</td>
|
||
<td>subtraction</td>
|
||
<td>xy-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>19</td>
|
||
<td>mu</td>
|
||
<td>multiplication</td>
|
||
<td>xy-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>20</td>
|
||
<td>di</td>
|
||
<td>divide & remainder</td>
|
||
<td>xy-rq</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>21</td>
|
||
<td>an</td>
|
||
<td>bitwise and</td>
|
||
<td>xy-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>22</td>
|
||
<td>or</td>
|
||
<td>bitwise or</td>
|
||
<td>xy-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>23</td>
|
||
<td>xo</td>
|
||
<td>bitwise xor</td>
|
||
<td>xy-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>24</td>
|
||
<td>sh</td>
|
||
<td>shift</td>
|
||
<td>xy-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>25</td>
|
||
<td>zr</td>
|
||
<td>zero return</td>
|
||
<td>n-?</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>26</td>
|
||
<td>en</td>
|
||
<td>end</td>
|
||
<td>-</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>27</td>
|
||
<td>ie</td>
|
||
<td>i/o enumerate</td>
|
||
<td>-n</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>28</td>
|
||
<td>iq</td>
|
||
<td>i/o query</td>
|
||
<td>n-xy</td>
|
||
<td>-</td>
|
||
</tr>
|
||
<tr>
|
||
<td>29</td>
|
||
<td>ii</td>
|
||
<td>i/o invoke</td>
|
||
<td>…n-</td>
|
||
<td>-</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><h2 id="encoding">Encoding</h2>
|
||
<p>Up to four instructions can be packed into each memory cell.</p>
|
||
<p>As an example,</p>
|
||
<pre><code>Opcode 1 Opcode 2 Opcode 3 Opcode 4
|
||
00000000:00000000:00000000:00000000
|
||
</code></pre>
|
||
<p>If we have a bundle of <code>lidumu..</code>, it would look like:</p>
|
||
<pre><code> li du mu ..
|
||
00000001:00000010:00010011:00000000
|
||
</code></pre>
|
||
<p>Each <code>li</code> should have a value in the following cell(s). These<br>
|
||
values will be pushed to the stack. E.g., <code>lili....</code> and<br>
|
||
1, 2:</p>
|
||
<pre><code>00000001:00000001:00000000:00000000
|
||
00000000 00000000 00000000 00000001 (1)
|
||
00000000 00000000 00000000 00000010 (2)
|
||
</code></pre>
|
||
<h2 id="shifts">Shifts</h2>
|
||
<p><code>sh</code> performs a bitwise arithmetic shift operation.</p>
|
||
<p>This takes two values:</p>
|
||
<pre><code>xy
|
||
</code></pre>
|
||
<p>And returns a single one:</p>
|
||
<pre><code>z
|
||
</code></pre>
|
||
<p>If y is positive, this shifts <code>x</code> right by <code>y</code> bits. If negative,<br>
|
||
it shifts left.</p>
|
||
<h2 id="queries-memory-stacks">Queries: Memory, Stacks</h2>
|
||
<p>The <code>fe</code> instruction allows queries of some data related to<br>
|
||
the Nga VM state. These are returned by reading from negative<br>
|
||
addresses:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Address</th>
|
||
<th>Returns</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>-1</td>
|
||
<td>Data stack depth</td>
|
||
</tr>
|
||
<tr>
|
||
<td>-2</td>
|
||
<td>Address stack depth</td>
|
||
</tr>
|
||
<tr>
|
||
<td>-3</td>
|
||
<td>Maximum Image Size</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><h2 id="io-devices">I/O Devices</h2>
|
||
<p>Nga provides three instructions for interacting with I/O devices.<br>
|
||
These are:</p>
|
||
<pre><code>ie i/o enumerate returns the number of attached devices
|
||
iq i/o query returns information about a device
|
||
ii i/o interact invokes an interaction with a device
|
||
</code></pre>
|
||
<p>As an example, with an implementation providing an output source,<br>
|
||
a block storage system, and keyboard:</p>
|
||
<pre><code>ie will return `3` since there are three i/o devices
|
||
0 iq will return 0 0, since the first device is a screen (0)
|
||
with a version of 0
|
||
1 iq will return 1 3, since the second device is a block
|
||
storage (3), with a version of 1
|
||
2 iq will return 0 1, since the third device is a keyboard
|
||
(1), with a version of 0
|
||
</code></pre>
|
||
<dl>
|
||
<dt>In this case, some interactions can be defined:</dt>
|
||
<dd>
|
||
<p>c:put<br>
|
||
i liiire…<br>
|
||
d 0</p>
|
||
</dd>
|
||
<dd>
|
||
<p>c:get<br>
|
||
i liiire…<br>
|
||
d 2</p>
|
||
</dd>
|
||
</dl>
|
||
<p>Setup the stack, push the device ID to the stack, and then use<br>
|
||
<code>ii</code> to invoke the interaction.</p>
|
||
<p>A RETRO system requires one I/O device (a generic output for a<br>
|
||
single character). This must be the first device, and must have<br>
|
||
a device ID of 0.</p>
|
||
<p>All other devices are optional and can be specified in any order.</p>
|
||
<p>The currently supported and reserved device identifiers are:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Device Type</th>
|
||
<th>Notes</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0000</td>
|
||
<td>Generic Output</td>
|
||
<td>Always present as device 0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>0001</td>
|
||
<td>Keyboard</td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td>0002</td>
|
||
<td>Floating Point</td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td>0003</td>
|
||
<td>Block Storage</td>
|
||
<td>Raw, 1024 cell blocks</td>
|
||
</tr>
|
||
<tr>
|
||
<td>0004</td>
|
||
<td>Filesystem</td>
|
||
<td>Unix-style Files</td>
|
||
</tr>
|
||
<tr>
|
||
<td>0005</td>
|
||
<td>Network: Gopher</td>
|
||
<td>Make gopher requests</td>
|
||
</tr>
|
||
<tr>
|
||
<td>0006</td>
|
||
<td>Network: HTTP</td>
|
||
<td>Make HTTP requests</td>
|
||
</tr>
|
||
<tr>
|
||
<td>0007</td>
|
||
<td>Network: Sockets</td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td>0008</td>
|
||
<td>Syscalls: Unix</td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td>0009</td>
|
||
<td>Scripting Hooks</td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td>0010</td>
|
||
<td>Random Number</td>
|
||
<td></td>
|
||
</tr>
|
||
</tbody>
|
||
</table><p>This list may be revised in the future. The only guaranteed<br>
|
||
stable indentifier is 0000 for generic output.</p>
|
||
<h2 id="trivia">Trivia</h2>
|
||
<p>There are 810,000 possible combinations of instructions. Only<br>
|
||
73 are used in the implementation of RETRO.</p>
|
||
<h1 id="internals-interface-layers">Internals: Interface Layers</h1>
|
||
<p>Nga provides a virtual processor and an extensible way of adding<br>
|
||
I/O devices, but does not provide any I/O itself. Adding I/O is<br>
|
||
the responsability of the <em>interface layer</em>.</p>
|
||
<p>An interface layer will wrap Nga, providing at least one I/O<br>
|
||
device (a generic output target), and a means of interacting<br>
|
||
with the <em>retro image</em>.</p>
|
||
<p>It’s expected that this layer will be host specific, adding any<br>
|
||
system interactions that are needed via the I/O instructions.<br>
|
||
The image will typically be extended with words to use these.</p>
|
||
<h1 id="internals-the-retro-image">Internals: The Retro Image</h1>
|
||
<p>The actual RETRO language is stored as a memory image for Nga.</p>
|
||
<h2 id="format">Format</h2>
|
||
<p>The image file is a flat, linear sequence of signed 32-bit<br>
|
||
values. Each value is stored in little endian format. The<br>
|
||
size is not fixed. An interface should check when loading to<br>
|
||
ensure that the physical image is not larger than the emulated<br>
|
||
memory.</p>
|
||
<h2 id="header">Header</h2>
|
||
<p>The image will start with two cells. The first is a liju…<br>
|
||
instruction, the second is the target address for the jump.<br>
|
||
This serves to skip over the rest of the data and reach the<br>
|
||
actual entry point.</p>
|
||
<p>This is followed by a pointer to the most recent dictionary<br>
|
||
header, a pointer to the next free address in memory, and<br>
|
||
then the RETRO version number.</p>
|
||
<pre><code>| Offset | Contains |
|
||
| ------ | --------------------------- |
|
||
| 0 | lit call nop nop |
|
||
| 1 | Pointer to main entry point |
|
||
| 2 | Dictionary |
|
||
| 3 | Heap |
|
||
| 4 | RETRO version |
|
||
</code></pre>
|
||
<p>The actual code starts after this header.</p>
|
||
<p>The version number is the year and month. As an example,<br>
|
||
the 12.2019.6 release will have a version number of<br>
|
||
<code>201906</code>.</p>
|
||
<h2 id="layout">Layout</h2>
|
||
<p>Assuming an Nga built with 524287 cells of memory:</p>
|
||
<pre><code>| RANGE | CONTAINS |
|
||
| --------------- | ---------------------------- |
|
||
| 0 - 1024 | rx kernel |
|
||
| 1025 - 1535 | token input buffer |
|
||
| 1536 + | start of heap space |
|
||
| ............... | free memory for your use |
|
||
| 506879 | buffer for string evaluate |
|
||
| 507904 | temporary strings (32 * 512) |
|
||
| 524287 | end of memory |
|
||
</code></pre>
|
||
<p>The buffers at the end of memory will resize when specific<br>
|
||
variables related to them are altered.</p>
|
||
<h1 id="additional-tools">Additional Tools</h1>
|
||
<p>In addition to the core <code>retro</code> binary, the <code>bin</code> directory<br>
|
||
will contain a few other tools.</p>
|
||
<h2 id="retro">retro</h2>
|
||
<p>This is the main RETRO binary.</p>
|
||
<h2 id="retro-describe">retro-describe</h2>
|
||
<p>This is a program that looks up entries in the Glossary.</p>
|
||
<p>At the command line, you can use it like:</p>
|
||
<pre><code>retro-describe s:for-each
|
||
</code></pre>
|
||
<h2 id="retro-embedimage">retro-embedimage</h2>
|
||
<p>This is a program which generates a C file with the ngaImage<br>
|
||
contents. It’s used when building <code>retro</code>.</p>
|
||
<pre><code>retro-embedimage ngaImage
|
||
</code></pre>
|
||
<p>The output is written to stdout; redirect it as needed.</p>
|
||
<h2 id="retro-extend">retro-extend</h2>
|
||
<p>This is a program which compiles code into the ngaImage.<br>
|
||
It’s used when building <code>retro</code> and when you want to make a<br>
|
||
standalone image with custom additions.</p>
|
||
<p>Example command line:</p>
|
||
<pre><code>retro-extend ngaImage example/rot13.forth
|
||
</code></pre>
|
||
<p>Pass the image name as the first argument, and then file names<br>
|
||
as susequent ones. Do <em>not</em> use this for things relying on I/O<br>
|
||
apart from the basic console output as it doesn’t emulate other<br>
|
||
devices. If you need to load in things that rely on using the<br>
|
||
optional I/O devices, see the Advanced Builds chapter.</p>
|
||
<h2 id="retro-muri">retro-muri</h2>
|
||
<p>This is the assembler for Nga. It’s used to build the initial<br>
|
||
RETRO kernel and can be used by other tools as well.</p>
|
||
<h2 id="retro-unu">retro-unu</h2>
|
||
<p>This is the literate source extraction tool for RETRO. It<br>
|
||
is used in building <code>retro</code>.</p>
|
||
<p>Example usage:</p>
|
||
<pre><code>retro-unu literate/RetroForth.md
|
||
</code></pre>
|
||
<p>Output is written to stdout; redirect as neeeded.</p>
|
||
<h1 id="advanced-builds">Advanced Builds</h1>
|
||
<h2 id="reduced-memory">Reduced Memory</h2>
|
||
<p>RETRO can be built for reduced memory targets. This is primarily<br>
|
||
intendeded for use with embedded targets, but can be useful on<br>
|
||
large machines as well.</p>
|
||
<p>To do this, <code>make</code> with <code>CFLAGS</code> set to <code>-DMEM1024K</code>, <code>-DMEM512K</code>,<br>
|
||
<code>-DMEM256K</code>, <code>-DMEM192K</code>, <code>-DMEM128K</code>’ or <code>-DMEM96K</code>. These will<br>
|
||
target memory sizes as specified per the following table.</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th></th>
|
||
<th>1024kB</th>
|
||
<th>512kB</th>
|
||
<th>256kB</th>
|
||
<th>192kB</th>
|
||
<th>128kB</th>
|
||
<th>96kB</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Total Bytes</td>
|
||
<td>1,048,579</td>
|
||
<td>524,288</td>
|
||
<td>262,144</td>
|
||
<td>196,608</td>
|
||
<td>131,072</td>
|
||
<td>98,304</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Image</td>
|
||
<td>968,000</td>
|
||
<td>450,000</td>
|
||
<td>192,000</td>
|
||
<td>126,000</td>
|
||
<td>96,000</td>
|
||
<td>72,000</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Data Stack</td>
|
||
<td>512</td>
|
||
<td>512</td>
|
||
<td>512</td>
|
||
<td>512</td>
|
||
<td>512</td>
|
||
<td>512</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Address Stack</td>
|
||
<td>1,024</td>
|
||
<td>1,024</td>
|
||
<td>1,024</td>
|
||
<td>1,024</td>
|
||
<td>1,024</td>
|
||
<td>1,024</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Remaining</td>
|
||
<td>79,040</td>
|
||
<td>72,752</td>
|
||
<td>68,608</td>
|
||
<td>69,072</td>
|
||
<td>33,536</td>
|
||
<td>24,768</td>
|
||
</tr>
|
||
</tbody>
|
||
</table><p>The remaining memory is available for use by the VM.</p>
|
||
<h2 id="custom-image">Custom Image</h2>
|
||
<p>For users of BSD, Linux, macOS, you can customize the image at<br>
|
||
build time.</p>
|
||
<p>In the top level directory is a <code>package</code> directory containing<br>
|
||
a file named <code>list</code>. You can add files to compile into your<br>
|
||
system by adding them to the <code>list</code> and rebuilding.</p>
|
||
<p>Example:</p>
|
||
<p>If you have wanted to include the NumbersWithoutPrefixes.forth<br>
|
||
example, add:</p>
|
||
<pre><code>~~~
|
||
'example/NumbersWithoutPrefixes.forth include
|
||
~~~
|
||
</code></pre>
|
||
<p>To the start of the <code>list</code> file and then run <code>make</code> again. The<br>
|
||
newly built <code>bin/retro</code> will now include your additions.</p>
|
||
<h1 id="the-optional-retro-compiler">The Optional Retro Compiler</h1>
|
||
<p>In addition to the base system, users of RETRO on Unix hosts<br>
|
||
with ELF executables can build and use the <code>retro-compiler</code><br>
|
||
to generate turnkey executables.</p>
|
||
<h2 id="requirements-3">Requirements</h2>
|
||
<ul>
|
||
<li>Unix host</li>
|
||
<li>ELF executable support</li>
|
||
<li>objcpy in the $PATH</li>
|
||
</ul>
|
||
<h2 id="building">Building</h2>
|
||
<p>BSD users:</p>
|
||
<p>make bin/retro-compiler</p>
|
||
<p>Linux users:</p>
|
||
<p>make -f Makefile.linux bin/retro-compiler</p>
|
||
<h2 id="installing">Installing</h2>
|
||
<p>Copy <code>bin/retro-compiler</code> to somewhere in your $PATH.</p>
|
||
<h2 id="using">Using</h2>
|
||
<p><code>retro-compiler</code> takes two arguments: the source file to<br>
|
||
compile and the name of the word to use as the main entry<br>
|
||
point.</p>
|
||
<p>Example:</p>
|
||
<p>Given a <code>hello.forth</code>:</p>
|
||
<pre><code>:hello 'Hello_World! s:put nl ;
|
||
</code></pre>
|
||
<p>Use:</p>
|
||
<pre><code>retro-compiler hello.forth hello
|
||
</code></pre>
|
||
<p>The compiler will generate an <code>a.out</code> file which you can<br>
|
||
then rename.</p>
|
||
<h2 id="known-limitations">Known Limitations</h2>
|
||
<p>This does not provide the scripting support for command line<br>
|
||
arguments that the standard <code>retro</code> interface offers.</p>
|
||
<p>A copy of <code>objcopy</code> needs to be in the path for compilation<br>
|
||
to work.</p>
|
||
<p>The current working directory must be writable.</p>
|
||
<p>This only supports hosts using ELF executables.</p>
|
||
<p>The output file name is fixed to <code>a.out</code>.</p>
|
||
<h1 id="errors">Errors</h1>
|
||
<p>RETRO does only minimal error checking.</p>
|
||
<h2 id="non-fatal">Non-Fatal</h2>
|
||
<p>A non-fatal error will be reported on <em>word not found</em> during<br>
|
||
interactive or compile time. Note that this only applies to<br>
|
||
calls: if you try to get a pointer to an undefined word, the<br>
|
||
returned pointer will be zero.</p>
|
||
<h2 id="fatal">Fatal</h2>
|
||
<p>A number of conditions are known to cause fatal errors. The<br>
|
||
main ones are stack overflow, stack underflow, and division<br>
|
||
by zero.</p>
|
||
<p>On these, RETRO will generally exit. For stack depth issues,<br>
|
||
the VM will attempt to display an error prior to exiting.</p>
|
||
<p>In some cases, the VM may get stuck in an endless loop. If this<br>
|
||
occurs, try using CTRL+C to kill the process, or kill it using<br>
|
||
whatever means your host system provides.</p>
|
||
<h2 id="rationale">Rationale</h2>
|
||
<p>Error checks are useful, but slow - especially on a minimal<br>
|
||
system like RETRO. The overhead of doing depth or other checks<br>
|
||
adds up quickly.</p>
|
||
<p>As an example, adding a depth check to <code>drop</code> increases the<br>
|
||
time to use it 250,000 times in a loop from 0.16 seconds to<br>
|
||
1.69 seconds.</p>
|
||
<h1 id="security-concerns">Security Concerns</h1>
|
||
<p>The standard RETRO is not a good choice for applications<br>
|
||
needing to be highly secure.</p>
|
||
<h2 id="runtime-checks">Runtime Checks</h2>
|
||
<p>The RETRO system performs only minimal checks. It will not<br>
|
||
load an image larger than the max set at build time. And<br>
|
||
stack over/underflow are checked for as code executes.</p>
|
||
<p>The system does not attempt to validate anything else, it’s<br>
|
||
quite easy to crash.</p>
|
||
<h2 id="isolation">Isolation</h2>
|
||
<p>The VM itself and the core code is self contained. Nga does<br>
|
||
not make use of malloc/free, and uses only standard system<br>
|
||
libraries. It’s possible for buffer overruns within the image<br>
|
||
(overwriting Nga code), but the RETRO image shouldn’t leak<br>
|
||
into the C portions.</p>
|
||
<p>I/O presents a bigger issue. Anything involving I/O, especially<br>
|
||
with the <code>unix:</code> words, may be a vector for attacks.</p>
|
||
<h2 id="future-direction">Future Direction</h2>
|
||
<p>I’m not planning to add anything to the <em>image</em> side as, for me,<br>
|
||
the performance hit due to added checks is bigger than the<br>
|
||
benefits.</p>
|
||
<p>The story is different on the VM side. I’ve already begun taking<br>
|
||
steps to address some of the issues, using functions that check<br>
|
||
for overruns with strings and doing some minor testing for these<br>
|
||
conditions. I will be gradually addressing the various I/O<br>
|
||
related extensions, though it’s unlikely to ever be fully guarded<br>
|
||
against attacks.</p>
|
||
<h2 id="rationale-1">Rationale</h2>
|
||
<p>RETRO is, primarily, a personal system. I’m running code I wrote<br>
|
||
to solve problems I face. On the occasions where I run code sent<br>
|
||
to me by others, I read it carefully first and then run inside a<br>
|
||
sandboxed environment if I’m worried about anything in it.</p>
|
||
<h1 id="technical-notes-and-reflections">Technical Notes and Reflections</h1>
|
||
<p>This is a collection of short papers providing some additional<br>
|
||
background and reflections on design decisions.</p>
|
||
<h2 id="metacompilation-and-assembly">Metacompilation and Assembly</h2>
|
||
<p>RETRO 10 and 11 were written in themselves using a metacompiler.<br>
|
||
I had been fascinated by this idea for a long time and was able<br>
|
||
to explore it heavily. While I still find it to be a good idea,<br>
|
||
the way I ended up doing it was problematic.</p>
|
||
<p>The biggest issue I faced was that I wanted to do this in one<br>
|
||
step, where loading the RETRO source would create a new image<br>
|
||
in place of the old one, switch to the new one, and then load<br>
|
||
the higher level parts of the language over this. In retrospect,<br>
|
||
this was a really bad idea.</p>
|
||
<p>My earlier design for RETRO was very flexible. I allowed almost<br>
|
||
everything to be swapped out or extended at any time. This made<br>
|
||
it extremely easy to customize the language and environment, but<br>
|
||
made it crucial to keep track of what was in memory and what had<br>
|
||
been patched so that the metacompiler wouldn’t refer to anything<br>
|
||
in the old image during the relocation and control change. It<br>
|
||
was far too easy to make a mistake, discover that elements of<br>
|
||
the new image were broken, and then have to go and revert many<br>
|
||
changes to try to figure out what went wrong.</p>
|
||
<p>This was also complicated by the fact that I built new images<br>
|
||
as I worked, and, while a new image could be built from the last<br>
|
||
built one, it wasn’t always possible to build a new image from<br>
|
||
the prior release version. (Actually, it was often worse - I<br>
|
||
failed to check in every change as I went, so often even the<br>
|
||
prior commits couldn’t rebuild the latest images).</p>
|
||
<p>For RETRO 12 I wanted to avoid this problem, so I decided to go<br>
|
||
back to writing the kernel (“Rx”) in assembly. I actually wrote<br>
|
||
a Machine Forth dialect to generate the initial assembly, before<br>
|
||
eventually hand tuning the final results to its current state.</p>
|
||
<p>I could (and likely will eventually) write the assembler in<br>
|
||
RETRO, but the current one is in C, and is built as part of the<br>
|
||
standard toolchain.</p>
|
||
<p>My VM actually has two assemblers. The older one is Naje. This<br>
|
||
was intended to be fairly friendly to work with, and handles<br>
|
||
many of the details of packing instructions for the user. Here<br>
|
||
is an example of a small program in it:</p>
|
||
<pre><code>:square
|
||
dup
|
||
mul
|
||
ret
|
||
:main
|
||
lit 35
|
||
lit &square
|
||
call
|
||
lit &square
|
||
call
|
||
end
|
||
</code></pre>
|
||
<p>The other assembler is Muri. This is a far more minimalistic<br>
|
||
assembler, but I’ve actually grown to prefer it. The above<br>
|
||
example in Muri would become:</p>
|
||
<pre><code>i liju....
|
||
r main
|
||
: square
|
||
i dumure..
|
||
: main
|
||
i lilica..
|
||
d 35
|
||
r square
|
||
i en......
|
||
</code></pre>
|
||
<p>In Muri, each instruction is reduced to two characters, and the<br>
|
||
bundlings are listed as part of an instruction bundle (lines<br>
|
||
starting with <code>i</code>). This is less readable if you aren’t very<br>
|
||
familiar with Nga’s assembly and packing rules, but allows a<br>
|
||
very quick, efficient way of writing assembly for those who are.</p>
|
||
<p>I eventually rewrote the kernel in the Muri style as it’s what<br>
|
||
I prefer, and since there’s not much need to make changes in it.</p>
|
||
<h2 id="the-path-to-self-hosting">The Path to Self Hosting</h2>
|
||
<p>RETRO is an image based Forth system running on a lightweight<br>
|
||
virtual machine. This is the story of how that image is made.</p>
|
||
<p>The first RETRO to use an image based approach was RETRO 10.<br>
|
||
The earliest images were built using a compiler written in<br>
|
||
Toka, an earlier experimental stack language I had written.<br>
|
||
It didn’t take long to want to drop the dependency on Toka,<br>
|
||
so I rewrote the image compiler in RETRO and then began<br>
|
||
development at a faster pace.</p>
|
||
<p>RETRO 11 was built using the last RETRO 10 image and an<br>
|
||
evolved version of the metacompiler. This worked well, but<br>
|
||
I eventually found it to be problematic.</p>
|
||
<p>One of the issues I faced was the inability to make a new<br>
|
||
image from the prior stable release. Since I develop and<br>
|
||
test changes incrementally, I reached a point where the<br>
|
||
current metacompiler and image required each other. This<br>
|
||
wasn’t a fatal flaw, but it was annoying.</p>
|
||
<p>Perhaps more critical was the fragility of the system. In<br>
|
||
R11 small mistakes could result in a corrupt image. The test<br>
|
||
suite helped identify some of these, but there were a few<br>
|
||
times I was forced to dig back through the version control<br>
|
||
history to recover a working image.</p>
|
||
<p>The fragile nature was amplified by some design decisions.<br>
|
||
In R11, after the initial kernel was built, it would be<br>
|
||
moved to memory address 0, then control would jump into the<br>
|
||
new kernel to finish building the higher level parts.</p>
|
||
<p>Handling this was a tricky task. In R11 almost everything<br>
|
||
could be revectored, so the metacompiler had to ensure that<br>
|
||
it didn’t rely on anything in the old image during the move.<br>
|
||
This caused a large number of issues over R11’s life.</p>
|
||
<p>So on to RETRO 12. I decided that this would be different.<br>
|
||
First, the kernel would be assembly, with an external tool<br>
|
||
to generate the core image. The kernel is in <code>Rx.md</code> and the<br>
|
||
assembler is <code>Muri</code>. To load the standard library, I wrote a<br>
|
||
second tool, <code>retro-extend</code>. This separation has allowed me<br>
|
||
many fewer headaches as I can make changes more easily and<br>
|
||
rebuild from scratch when necessary.</p>
|
||
<p>But I miss self-hosting. So last fall I decided to resolve<br>
|
||
this. And today I’m pleased to say that it is now done.</p>
|
||
<p>There are a few parts to this.</p>
|
||
<p><strong>Unu</strong>. I use a Markdown variation with fenced code blocks.<br>
|
||
The tool I wrote in C to extract these is called <code>unu</code>. For<br>
|
||
a self hosting RETRO, I rewrote this as a combinator that<br>
|
||
reads in a file and runs another word against each line in the<br>
|
||
file. So I could display the code block contents by doing:</p>
|
||
<pre><code>'filename [ s:put nl ] unu
|
||
</code></pre>
|
||
<p>This made it easier to implement the other tools.</p>
|
||
<p><strong>Muri</strong>. This is my assembler. It’s minimalistic, fast, and<br>
|
||
works really well for my purposes. RETRO includes a runtime<br>
|
||
version of this (using <code>as{</code>, <code>}as</code>, <code>i</code>, <code>d</code>, and <code>r</code>), so<br>
|
||
all I needed for this was to write a few words to parse the<br>
|
||
lines and run the corresponding runtime words. As with the C<br>
|
||
version, this is a two pass assembler.</p>
|
||
<p>Muri generates a new <code>ngaImage</code> with the kernel. To create a<br>
|
||
full image I needed a way to load in the standard library and<br>
|
||
I/O extensions.</p>
|
||
<p>This is handled by <strong>retro-extend</strong>. This is where it gets<br>
|
||
more complex. I implemented the Nga virtual machine in RETRO<br>
|
||
to allow this to run the new image in isolation from the<br>
|
||
host image. The new ngaImage is loaded, the interpreter is<br>
|
||
located, and each token is passed to the interpreter. Once<br>
|
||
done, the new image is written to disk.</p>
|
||
<p>So at this point I’m pleased to say that I can now develop<br>
|
||
RETRO using only an existing copy of RETRO (VM+image) and<br>
|
||
tools (unu, muri, retro-extend, and a line oriented text<br>
|
||
editor) written in RETRO.</p>
|
||
<p>This project has delivered some additional side benefits.<br>
|
||
During the testing I was able to use it to identify a few<br>
|
||
bugs in the I/O extensions, and the Nga-in-RETRO will replace<br>
|
||
the older attempt at this in the debugger, allowing a safer<br>
|
||
testing environment.</p>
|
||
<p>What issues remain?</p>
|
||
<p>The extend process is <em>slow</em>. On my main development server<br>
|
||
(Linode 1024, OpenBSD 6.4, 64-bit) it takes a bit over five<br>
|
||
minutes to complete loading the standard library, and a few<br>
|
||
additional depending on the I/O drivers selected.</p>
|
||
<p>Most of the performance issues come from running Nga-in-RETRO<br>
|
||
to isolate the new image from the host one. It’d be possible<br>
|
||
to do something a bit more clever (e.g., running a RETRO<br>
|
||
instance using the new image via a subprocess and piping in<br>
|
||
the source, or doing relocations of the data), but this is<br>
|
||
less error prone and will work on all systems that I plan to<br>
|
||
support (including, with a few minor adjustments, the native<br>
|
||
hardware versions [assuming the existance of mass storage]).</p>
|
||
<p>Sources:</p>
|
||
<p><strong>Unu</strong></p>
|
||
<ul>
|
||
<li><a href="http://forth.works/c8820f85e0c52d32c7f9f64c28f435c0">http://forth.works/c8820f85e0c52d32c7f9f64c28f435c0</a></li>
|
||
<li>gopher://forth.works/0/c8820f85e0c52d32c7f9f64c28f435c0</li>
|
||
</ul>
|
||
<p><strong>Muri</strong></p>
|
||
<ul>
|
||
<li><a href="http://forth.works/09d6c4f3f8ab484a31107dca780058e3">http://forth.works/09d6c4f3f8ab484a31107dca780058e3</a></li>
|
||
<li>gopher://forth.works/0/09d6c4f3f8ab484a31107dca780058e3</li>
|
||
</ul>
|
||
<p><strong>retro-extend</strong></p>
|
||
<ul>
|
||
<li><a href="http://forth.works/c812416f397af11db58e97388a3238f2">http://forth.works/c812416f397af11db58e97388a3238f2</a></li>
|
||
<li>gopher://forth.works/0/c812416f397af11db58e97388a3238f2</li>
|
||
</ul>
|
||
<h2 id="prefixes-as-a-language-element">Prefixes as a Language Element</h2>
|
||
<p>A big change in RETRO 12 was the elimination of the traditional<br>
|
||
parser from the language. This was a sacrifice due to the lack<br>
|
||
of an I/O model. RETRO has no way to know <em>how</em> input is given<br>
|
||
to the <code>interpret</code> word, or whether anything else will ever be<br>
|
||
passed into it.</p>
|
||
<p>And so <code>interpret</code> operates only on the current token. The core<br>
|
||
language does not track what came before or attempt to guess at<br>
|
||
what might come in the future.</p>
|
||
<p>This leads into the prefixes. RETRO 11 had a complicated system<br>
|
||
for prefixes, with different types of prefixes for words that<br>
|
||
parsed ahead (e.g., strings) and words that operated on the<br>
|
||
current token (e.g., <code>@</code>). RETRO 12 eliminates all of these in<br>
|
||
favor of just having a single prefix model.</p>
|
||
<p>The first thing <code>interpret</code> does is look to see if the first<br>
|
||
character in a token matches a <code>prefix:</code> word. If it does, it<br>
|
||
passes the rest of the token as a string pointer to the prefix<br>
|
||
specific handler to deal with. If there is no valid prefix<br>
|
||
found, it tries to find it in the dictionary. Assuming that it<br>
|
||
finds the words, it passes the <code>d:xt</code> field to the handler that<br>
|
||
<code>d:class</code> points to. Otherwise it calls <code>err:notfound</code>.</p>
|
||
<p>This has an important implication: <em>words can not reliably<br>
|
||
have names that start with a prefix character.</em></p>
|
||
<p>It also simplifies things. Anything that would normally parse<br>
|
||
becomes a prefix handler. So creating a new word? Use the <code>:</code><br>
|
||
prefix. Strings? Use <code>'</code>. Pointers? Try <code>&</code>. And so on. E.g.,</p>
|
||
<pre><code>In ANS | In RETRO
|
||
: foo ... ; | :foo ... ;
|
||
' foo | &foo
|
||
: bar ... ['] foo ; | :bar ... &foo ;
|
||
s" hello world!" | 'hello_world!
|
||
</code></pre>
|
||
<p>If you are familiar with ColorForth, prefixes are a similar<br>
|
||
idea to colors, but can be defined by the user as normal words.</p>
|
||
<p>After doing this for quite a while I rather like it. I can see<br>
|
||
why Chuck Moore eventually went towards ColorForth as using<br>
|
||
color (or prefixes in my case) does simplify the implementation<br>
|
||
in many ways.</p>
|
||
<h2 id="on-the-kernel-wordset">On The Kernel Wordset</h2>
|
||
<p>In implementing the RETRO 12 kernel (called Rx) I had to decide<br>
|
||
on what functionality would be needed. It was important to me<br>
|
||
that this be kept clean and minimalistic, as I didn’t want to<br>
|
||
spend a lot of time changing it as time progressed. It’s far<br>
|
||
nicer to code at the higher level, where the RETRO language is<br>
|
||
functional, as opposed to writing more assembly code.</p>
|
||
<p>So what made it in?</p>
|
||
<p>Primitives</p>
|
||
<p>These are words that map directly to Nga instructions.</p>
|
||
<pre><code>dup drop swap call eq? -eq? lt? gt?
|
||
fetch store + - * /mod and or
|
||
xor shift push pop 0;
|
||
</code></pre>
|
||
<p>Memory</p>
|
||
<pre><code>fetch-next store-next , s,
|
||
</code></pre>
|
||
<p>Strings</p>
|
||
<pre><code>s:to-number s:eq? s:length
|
||
</code></pre>
|
||
<p>Flow Control</p>
|
||
<pre><code>choose if -if repeat again
|
||
</code></pre>
|
||
<p>Compiler & Interpreter</p>
|
||
<pre><code>Compiler Heap ; [ ] Dictionary
|
||
d:link d:class d:xt d:name d:add-header
|
||
class:word class:primitive class:data class:macro
|
||
prefix:: prefix:# prefix:& prefix:$
|
||
interpret d:lookup err:notfound
|
||
</code></pre>
|
||
<p>I <em>could</em> slightly reduce this. The $ prefix could be defined in<br>
|
||
higher level code, and I don’t strictly <em>need</em> to expose the<br>
|
||
<code>fetch-next</code> and <code>store-next</code> here. But since the are already<br>
|
||
implemented as dependencies of the words in the kernel, it would<br>
|
||
be a bit wasteful to redefine them later in higher level code.</p>
|
||
<p>With these words the rest of the language can be built up. Note<br>
|
||
that the Rx kernel does not provide any I/O words. It’s assumed<br>
|
||
that the RETRO interfaces will add these as best suited for the<br>
|
||
systems they run on.</p>
|
||
<p>There is another small bit. All images start with a few key<br>
|
||
pointers in fixed offsets of memory. These are:</p>
|
||
<pre><code>| Offset | Contains |
|
||
| ------ | --------------------------- |
|
||
| 0 | lit call nop nop |
|
||
| 1 | Pointer to main entry point |
|
||
| 2 | Dictionary |
|
||
| 3 | Heap |
|
||
| 4 | RETRO version identifier |
|
||
</code></pre>
|
||
<p>An interface can use the dictionary pointer and knowledge of the<br>
|
||
dictionary format for a specific RETRO version to identify the<br>
|
||
location of essential words like <code>interpret</code> and <code>err:notfound</code><br>
|
||
when implementing the user facing interface.</p>
|
||
<h2 id="on-the-evolution-of-ngaro-into-nga">On The Evolution Of Ngaro Into Nga</h2>
|
||
<p>When I decided to begin work on what became RETRO 12, I knew<br>
|
||
the process would involve updating Ngaro, the virtual machine<br>
|
||
that RETRO 10 and 11 ran on.</p>
|
||
<p>Ngaro rose out of an earlier experimental virtual machine I had<br>
|
||
written back in 2005-2006. This earlier VM, called Maunga, was<br>
|
||
very close to what Ngaro ended up being, though it had a very<br>
|
||
different approach to I/O. (All I/O in Maunga was intended to be<br>
|
||
memory mapped; Ngaro adopted a port based I/O system).</p>
|
||
<p>Ngaro itself evolved along with RETRO, gaining features like<br>
|
||
automated skipping of NOPs and a LOOP opcode to help improve<br>
|
||
performance. But the I/O model proved to be a problem. When I<br>
|
||
created Ngaro, I had the idea that I would always be able to<br>
|
||
assume a console/terminal style environment. The assumption was<br>
|
||
that all code would be entered via the keyboard (or maybe a<br>
|
||
block editor), and that proved to be the fundamental flaw as<br>
|
||
time went on.</p>
|
||
<p>As RETRO grew it was evident that the model had some serious<br>
|
||
problems. Need to load code from a file? The VM and language had<br>
|
||
functionality to pretend it was being typed in. Want to run on<br>
|
||
something like a browser, Android, or iOS? The VM would need to<br>
|
||
be implemented in a way that simulates input being typed into<br>
|
||
the VM via a simulated keyboard. And RETRO was built around this.<br>
|
||
I couldn’t change it because of a promise to maintain, as much<br>
|
||
as possible, source compatibility for a period of at least five<br>
|
||
years.</p>
|
||
<p>When the time came to fix this, I decided at the start to keep<br>
|
||
the I/O model separate from the core VM. I also decided that the<br>
|
||
core RETRO language would provide some means of interpreting<br>
|
||
code without requiring an assumption that a traditional terminal<br>
|
||
was being used.</p>
|
||
<p>So Nga began. I took the opportunity to simplify the instruction<br>
|
||
set to just 26 essential instructions, add support for packing<br>
|
||
multiple instructions per memory location (allowing a long due<br>
|
||
reduction in memory footprint), and to generally just make a far<br>
|
||
simpler design.</p>
|
||
<p>I’ve been pleased with Nga. On its own it really isn’t useful<br>
|
||
though. So with RETRO I embed it into a larger framework that<br>
|
||
adds some basic I/O functionality. The <em>interfaces</em> handle the<br>
|
||
details of passing tokens into the language and capturing any<br>
|
||
output. They are free to do this in whatever model makes most<br>
|
||
sense on a given platform.</p>
|
||
<p>So far I’ve implemented:</p>
|
||
<pre><code>- a scripting interface, reading input from a file and
|
||
offering file i/o, gopher, and reading from stdin, and
|
||
sending output to stdout.
|
||
- an interactive interface, built around ncurses, reading
|
||
input from stdin, and displaying output to a scrolling
|
||
buffer.
|
||
- an iOS interface, built around a text editor, directing
|
||
output to a separate interface pane.
|
||
- an interactive block editor, using a gopher-based block
|
||
data store. Output is displayed to stdout, and input is
|
||
done via the blocks being evaluated or by reading from
|
||
stdin.
|
||
</code></pre>
|
||
<p>In all cases, the only common I/O word that has to map to an<br>
|
||
exposed instruction is <code>putc</code>, to display a single character to<br>
|
||
some output device. There is no requirement for a traditional<br>
|
||
keyboard input model.</p>
|
||
<p>By doing this I was able to solve the biggest portability issue<br>
|
||
with the RETRO 10/11 model, and make a much simpler, cleaner<br>
|
||
language in the end.</p>
|
||
<h2 id="retro-11-2011---2019-a-look-back">RETRO 11 (2011 - 2019): A Look Back</h2>
|
||
<p>So it’s now been about five years since the last release of RETRO<br>
|
||
11. While I still see some people obtaining and using it, I’ve<br>
|
||
moved on to the twelth generation of RETRO. It’s time for me to<br>
|
||
finally retire RETRO 11.</p>
|
||
<p>As I prepare to do so, I thought I’d take a brief look back.</p>
|
||
<p>RETRO 11 began life in 2011. It grew out of RETRO 10, which was<br>
|
||
the first version of RETRO to not be written in x86 assembly<br>
|
||
language. For R10 and R11, I wrote a portable virtual machine<br>
|
||
(with numerous implementations) and the Forth dialect was kept<br>
|
||
in an image file which ran on the VM.</p>
|
||
<p>RETRO 10 worked, but was always a bit too sloppy and changed<br>
|
||
drastically between releases. The major goal of RETRO 11 was to<br>
|
||
provide a stable base for a five year period. In retrospect,<br>
|
||
this was mostly achieved. Code from earlier releases normally<br>
|
||
needed only minor adjustments to run on later releases, though<br>
|
||
newer releases added significantly to the language.</p>
|
||
<p>There were seven releases.</p>
|
||
<ul>
|
||
<li>Release 11.0: 2011, July</li>
|
||
<li>Release 11.1: 2011, November</li>
|
||
<li>Release 11.2: 2012, January</li>
|
||
<li>Release 11.3: 2012, March</li>
|
||
<li>Release 11.4: 2012, July</li>
|
||
<li>Release 11.5: 2013, March</li>
|
||
<li>Release 11.6: 2014, August</li>
|
||
</ul>
|
||
<p>Development was fast until 11.4. This was the point at which I<br>
|
||
had to slow down due to RSI problems. It was also the point<br>
|
||
which I started experiencing some problems with the metacompiler<br>
|
||
(as discussed previously).</p>
|
||
<p>RETRO 11 was flexible. All colon definitions were setup as hooks,<br>
|
||
allowing new functionality to be layered in easily. This allowed<br>
|
||
the later releases to add things like vocabularies, search order,<br>
|
||
tab completion, and keyboard remapping. This all came at a cost<br>
|
||
though: later things could use the hooks to alter behavior of<br>
|
||
existing words, so it was necessary to use a lot of caution to<br>
|
||
ensure that the layers didn’t break the earlier code.</p>
|
||
<p>The biggest issue was the I/O model. RETRO 11 and the Ngaro VM<br>
|
||
assumed the existence of a console environment. All input was<br>
|
||
required to be input at the keyboard, and all output was to be<br>
|
||
shown on screen. This caused some problems. Including code from<br>
|
||
a file required some tricks, temporarily rewriting the keyboard<br>
|
||
input function to read from the file. It also became a major<br>
|
||
issue when I wrote the iOS version. The need to simulate the<br>
|
||
keyboard and console complicated everything and I had to spend<br>
|
||
a considerable amount of effort to deal with battery performance<br>
|
||
resulting from the I/O polling and wait states.</p>
|
||
<p>But on the whole it worked well. I used RETRO 11.6 until I started<br>
|
||
work on RETRO 12 in late 2016, and continued running some tools<br>
|
||
written in R11 until the first quarter of last year.</p>
|
||
<p>The final image file was 23,137 cells (92,548 bytes). This was<br>
|
||
bloated by keeping some documentation (stack comments and short<br>
|
||
descriptions) in the image, which started in 11.4. This contained<br>
|
||
269 words.</p>
|
||
<p>I used RETRO 11 for a wide variety of tasks. A small selection of<br>
|
||
things that were written includes:</p>
|
||
<ul>
|
||
<li>a pastebin</li>
|
||
<li>front end to ii (irc client)</li>
|
||
<li>small explorations of interactive fiction</li>
|
||
<li>irc log viewer</li>
|
||
<li>tool to create html from templates</li>
|
||
<li>tool to automate creation of an SVCD from a set of photos</li>
|
||
<li>tools to generate reports from data sets for my employer</li>
|
||
</ul>
|
||
<p>In the end, I’m happy with how RETRO 11 turned out. I made some<br>
|
||
mistakes in embracing too much complexity, but despite this it<br>
|
||
was a successful system for many years.</p>
|
||
<h1 id="historical-papers-and-notes">Historical Papers and Notes</h1>
|
||
<h2 id="on-the-naming-of-retro">On the Naming of RETRO</h2>
|
||
<p>Taken from <a href="http://lists.tunes.org/archives/tunes-lll/1999-July/000121.html">http://lists.tunes.org/archives/tunes-lll/1999-July/000121.html</a></p>
|
||
<p>On Fri, Jul 30, 1999 at 07:43:54PM -0400, Paul Dufresne wrote:</p>
|
||
<blockquote>
|
||
<p>My brother did found it funny that Retro is called like that.<br>
|
||
For him retro means going back (generally in time) so this<br>
|
||
does not looks like a name of a OS to come. So he’d like to<br>
|
||
know from where the name came.</p>
|
||
</blockquote>
|
||
<p>Heheh, here’s the story: When I started playing with OS stuff<br>
|
||
last year (not seriously), I was reading about some old things<br>
|
||
like FORTH and ITS, dating back to the 1960’s and 70’s. The<br>
|
||
past few years in America, there’s been a revival of disco<br>
|
||
music (along with bell bottoms, platform shoes, and all that<br>
|
||
crap) and they call it “retro”. Now, my OS was named by<br>
|
||
musicians… I was telling a fellow musician about my ideas,<br>
|
||
how it would be cool to have a small OS that isn’t bloated and<br>
|
||
unmanageable like Windows… go back to the 70’s and resurrect<br>
|
||
a line of software that died out. He goes “hmm… sounds kinda<br>
|
||
retro…”</p>
|
||
<p>I think it sounds kinda rebellious, which is a Good Thing now<br>
|
||
that everybody hates the M$ empire. :) It seems like other<br>
|
||
people are as sick of the future as I am. Look at TUNES, the<br>
|
||
idea there isn’t to make some great new invention, just take<br>
|
||
some decades-old ideas and combine them in one OS. The first<br>
|
||
time I saw Knuth’s “Art of Computer Programming” in the library<br>
|
||
I thought “god that looks old… 1973!!! nevermind…” Now it’s<br>
|
||
my programming bible. Find me something better published in<br>
|
||
the 90’s… if such a thing exists, it’ll be like a needle in a<br>
|
||
haystack. “Newer” doesn’t necessarily mean “better”.</p>
|
||
<pre><code>New cars = flimsier
|
||
New farming methods = more devastating
|
||
New version of Netscape = more bloat, more bullshit
|
||
</code></pre>
|
||
<p>One thing is better now: computer hardware. Give me 70’s<br>
|
||
software on 90’s and 00’s hardware :)</p>
|
||
<ul>
|
||
<li>Tom Novelli <a href="mailto:tcn@tunes.org">tcn@tunes.org</a></li>
|
||
</ul>
|
||
<h2 id="the-design-philosophy-of-retro-native-forth">The Design Philosophy of RETRO Native Forth</h2>
|
||
<p>Computer software is a technology in its infancy, a mere fifty years<br>
|
||
old. The last 25 years in particular have seen an explosion in the<br>
|
||
software business. However, software has seen little innovation while<br>
|
||
hardware technology has improved phenomenally (notwithstanding the advent<br>
|
||
of lousy slave-made parts). Proven software techniques of forty years ago<br>
|
||
have yet to reach widespread use, in deference to the “latest and<br>
|
||
greatest” proprietary solutions of dubious value. Thanks to agressive<br>
|
||
marketing, we make huge investments in these dead-end technologies<br>
|
||
(through our businesses and governments, if not personally) and we end up<br>
|
||
with a reliance on a heap of complicated, error-prone, poorly understood<br>
|
||
junk software.</p>
|
||
<p>Complexity will dominate the software industry for the foreseeable<br>
|
||
future. The Retro philosophy is a simple alternative for those willing to<br>
|
||
make a clean break with legacy software. A Retro system can communicate<br>
|
||
with other systems, but it won’t run much legacy software, especially<br>
|
||
proprietary software without source code. An emulation layer could be<br>
|
||
added, but doing so would defeat the purpose of a simple operating system.<br>
|
||
I think TCP/IP support is all the compatibility that’s needed.</p>
|
||
<p>At first Retro will appeal to computer hobbyists and electronic<br>
|
||
engineers. Once the rough edges are smoothed out, it could catch on with<br>
|
||
ordinary folks who don’t like waiting five minutes just to check their<br>
|
||
email (not to mention the long hours of setup and maintenance). Game<br>
|
||
programmers who take their craft seriously may also be interested.<br>
|
||
Businesses might even see a use for it, if the managers decide it’s more<br>
|
||
cost-effective to carefully design software for specific needs, rather<br>
|
||
than buying off-the-shelf crap and spending countless manhours working<br>
|
||
around the bugs. Since it’s not practical for businesses to make a clean<br>
|
||
break, my advice is to run Retro (and its ilk) on separate machines<br>
|
||
connected by a network. Retro is efficient enough to run on older<br>
|
||
machines that would otherwise sit idle, being too slow for the latest<br>
|
||
Microsoft bloatware (or Linux, for that matter).</p>
|
||
<p>I strive to avoid the extraneous. That applies even to proven<br>
|
||
technologies, if I don’t need them. If my computer isn’t set up for<br>
|
||
people to log in over the network, I don’t want security features; they<br>
|
||
just get in the way. If I’m only running programs I wrote, I should be<br>
|
||
able to run them with full access to the hardware; I don’t need protection<br>
|
||
from viruses. If I download something I don’t trust, then I can run it in<br>
|
||
an isolated process, which is customary with Unix and kin. But that’s not<br>
|
||
core functionality. All that’s needed is the flexibility to add things<br>
|
||
like security, graphical interfaces, and distributed processing - if the<br>
|
||
need ever arises.</p>
|
||
<p>In programming languagues, I was misled. It’s the Tower of Babel all<br>
|
||
over again. The thousands of languages in existence all fall into a<br>
|
||
handful of archetypes: Assembler, LISP, FORTRAN and FORTH represent the<br>
|
||
earliest descendants of nearly all languages. I hesitate to name a<br>
|
||
definitive “object-oriented” language, and here’s why: Object-Oriented<br>
|
||
programming is just a technique, and any language will suffice, even<br>
|
||
Assembler. The complexites of fancy languages like Ada and C++ are a<br>
|
||
departure from reality – the reality of the actual physical machine.<br>
|
||
When it all boils down, even LISP, FORTRAN and FORTH are only extensions<br>
|
||
of the machine.</p>
|
||
<p>I chose FORTH as the “native tongue” of Retro. LISP, FORTRAN, and<br>
|
||
other languages can be efficiently implemented as extensions of FORTH, but<br>
|
||
the reverse isn’t so efficient. Theoretically all languages are<br>
|
||
equivalent, but when design time, compilation time, and complexity are<br>
|
||
accounted for, FORTH is most efficient. FORTH also translates most<br>
|
||
directly to the hardware. (In fact, FORTH has been implemented in<br>
|
||
hardware; these “stack machines” are extremely efficient.) FORTH is also<br>
|
||
the easiest language to implement from scratch - a major concern when<br>
|
||
you’re trying to make a clean break. So with simplicity in mind, FORTH<br>
|
||
was the obvious choice.</p>
|
||
<p>I’m perfectly happy working with text only, and I go to great lengths<br>
|
||
to avoid using the standard graphical environments, which have major<br>
|
||
problems: windows, pulldown menus, and mice. Windows can’t share the<br>
|
||
screen nicely; that idea is hopeless. Pulldowns are tedious. Mice get in<br>
|
||
the way of typing without reducing the need for it; all they give me is<br>
|
||
tendonitis. Their main use is for drawing.</p>
|
||
<p>Some of my favorite interfaces: Telix, Telegard BBS, Pine, Pico, Lynx,<br>
|
||
and ScreamTracker. All “hotkey” interfaces where you press a key or two<br>
|
||
to perform an action. Usually the important commands are listed at the<br>
|
||
bottom of the screen, or at least on a help screen. The same principles<br>
|
||
apply to graphical interfaces: use the full screen, except for a status<br>
|
||
and menu area on one edge. Resist the temptation to clutter up the<br>
|
||
screen.</p>
|
||
<p>As for switching between programs, the Windows methods suck; the only<br>
|
||
thing worse is Unix job control (jobs, fg, and such). The Linux method is<br>
|
||
tolerable: Alt-Arrows, Alt-F1, Alt-F2, etc. Still, things could be<br>
|
||
better: F11 and F12 cycle back and forth through all open programs; Alt-F1<br>
|
||
assigns the currently selected program to F1, and likewise for the other<br>
|
||
function keys. Programs just won’t use function keys - Control and Alt<br>
|
||
combinations are less awkward and easier to remember, besides. I’ll also<br>
|
||
want a “last channel” key and a “task list” key; maybe I’ll borrow those<br>
|
||
stupid Win95 keys. The Pause key will do like it says - pause the current<br>
|
||
program - and Ctrl-Pause (Break) will kill it.</p>
|
||
<p>One more thing: consistency. I like programs to look different so I<br>
|
||
can tell them apart, but the keys should be the same as much as possible.<br>
|
||
Keys should be configured in one place, for all programs. Finally,<br>
|
||
remember the most consistent interface, one of the few constants<br>
|
||
throughout the history of computing - the text screen and keyboard, and<br>
|
||
the teletypewriter before that. Don’t overlook it.</p>
|
||
<p>More to come, maybe… :)</p>
|
||
<p>“If it’s on line, it’s a work in progress.”</p>
|
||
<p>Tom Novelli, 3/4/2000</p>
|
||
|
||
</div>
|
||
</div>
|
||
</body>
|
||
|
||
</html>
|
||
|