Commits

Anonymous committed af80f4a

migrated to site generator

  • Participants
  • Parent commits a0c6fed

Comments (0)

Files changed (22)

+#!/bin/sh
+./poole.py --build site || exit 1
+
+cp -rf site/output/* .
+
+./poole.py --serve site
-<!DOCTYPE HTML> 
-<html lang=en>
-<head>
-	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-	<title>zserge's home</title>
-	<meta name="description" content="zserge's home page: as simple as it should be" />
-	<meta name="keywords" content="software, minimalism, linux, embedded, avr, android, golang, KISS" />
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+		<title>zserge's home page: as simple as it should be</title>
+		<meta name="description" content="Minimalist open-source software for *nix and embedded systems" />
+		<meta name="keywords" content="software, minimalism, linux, embedded, avr, android, golang, KISS" />
+		<link rel="stylesheet" type="text/css" href="/style.css" />
+	</head>
 	<script type="text/javascript">
 
-	var _gaq = _gaq || [];
-	_gaq.push(['_setAccount', 'UA-16690724-5']);
-	_gaq.push(['_trackPageview']);
+		var _gaq = _gaq || [];
+		_gaq.push(['_setAccount', 'UA-16690724-5']);
+		_gaq.push(['_trackPageview']);
 
-	(function() {
-	var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-	ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-	var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-})();
-</script>
-</head>
-<body>
-<div id="container">
-	<div id="content">
-<h1>zserge's home page</h1>
-
+		(function() {
+			var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+			ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+				var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+		})();
+	</script>
+	<body>
+		<div id="container">
+			<div id="content"><h1>zserge's home page</h1>
 <p>Hi! I'm Serge. I'm a programmer.</p>
-
 <h2>I like</h2>
-
 <p>I like: simplicity, unix, C, embedded systems, android, Go language. </p>
-
 <p>I also like: music, retro computers and games, esoteric programming languages.
 But most of all I like my girlfriend, Anna.</p>
-
 <h2>Projects</h2>
-
 <p>It would be great if my works would be useful to someone.
 They are free, open source, and very simple:</p>
-
 <ul>
-<li><a href="jsmn.html">jsmn</a>  - JSON parser for small systems
-[<a href="http://bitbucket.org/zserge/jsmn">repo</a>]</li>
-<li><a href="six.html">six</a> - very simple IRC client [<a href="http://bitbucket.org/zserge/six">repo</a>]</li>
+<li><a href="/jsmn.html">jsmn</a> - JSON parser for small systems
+  [<a href="http://bitbucket.org/zserge/jsmn">repo</a>]</li>
+<li><a href="/six.html">six</a> - very simple IRC client 
+  [<a href="http://bitbucket.org/zserge/six">repo</a>]</li>
 <li>gotftp - basic TFTP server written in Go
-[<a href="http://bitbucket.org/zserge/gotftp">repo</a>]</li>
-<li>j1vm - J1 Forth CPU simulator and compiler [<a href="http://bitbucket.org/zserge/j1vm">repo</a>]</li>
-<li><a href="nikl.html">nikl</a> - micro-framework for programming embedded systems [<a href="http://bitbucket.org/zserge/nikl">repo</a>]</li>
-<li><a href="ivm.html">ivm</a> - simple forth virtual machine (a part of <a href="nikl.html">nikl</a>) [<a href="http://bitbucket.org/zserge/nikl">repo</a>]</li>
+    [<a href="http://bitbucket.org/zserge/gotftp">repo</a>]</li>
+<li>j1vm - J1 Forth CPU simulator and compiler 
+  [<a href="http://bitbucket.org/zserge/j1vm">repo</a>]</li>
+<li><a href="/nikl.html">nikl</a> - micro-framework for programming embedded systems 
+  [<a href="http://bitbucket.org/zserge/nikl">repo</a>]</li>
+<li><a href="/ivm.html">ivm</a> - simple forth virtual machine (a part of <a href="/nikl.html">nikl</a>) 
+  [<a href="http://bitbucket.org/zserge/nikl">repo</a>]</li>
 </ul>
-
 <h2>Contact</h2>
-
 <p>I'm not a social guy, but </p>
-
 <ul>
 <li>You can email me at google mail: zaitsev.serge</li>
 <li>Or follow on twitter: <a href="http://twitter.com/zsergo">@zsergo</a></li>
 <li>Or read my <a href="http://zserge.wordpress.com">blog</a> (in russian)</li>
-</ul>
-	</div>
-</div>
-</body>
+</ul></div>
+		</div>
+	</body>
 </html>
-<!DOCTYPE HTML> 
-<html lang=en>
-<head>
-	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-	<title>jsmn</title>
-	<meta name="description" content="The most simple JSON parser in C for small systems" />
-	<meta name="keywords" content="json, parse, c, unix, embedded, KISS, minimal, memory allocation" />
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+		<title>The most simple JSON parser in C for small systems</title>
+		<meta name="description" content="Minimalist open-source software for *nix and embedded systems" />
+		<meta name="keywords" content="json, parse, c, unix, embedded, KISS, minimal, memory allocation" />
+		<link rel="stylesheet" type="text/css" href="/style.css" />
+	</head>
 	<script type="text/javascript">
 
-	var _gaq = _gaq || [];
-	_gaq.push(['_setAccount', 'UA-16690724-5']);
-	_gaq.push(['_trackPageview']);
+		var _gaq = _gaq || [];
+		_gaq.push(['_setAccount', 'UA-16690724-5']);
+		_gaq.push(['_trackPageview']);
 
-	(function() {
-	var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-	ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-	var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-})();
-</script>
-</head>
-<body>
-<div id="container">
-	<div id="content">
-<h1>JSMN</h1>
-
+		(function() {
+			var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+			ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+				var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+		})();
+	</script>
+	<body>
+		<div id="container">
+			<div id="content"><h1>JSMN</h1>
 <p>jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C.  It can be
 easily integrated into resource-limited or embedded projects.</p>
-
 <p>You can find more information about JSON format at <a href="http://www.json.org/">json.org</a></p>
-
 <p>Library sources are available at <a href="https://bitbucket.org/zserge/jsmn/wiki/Home">bitbucket.org/zserge/jsmn</a></p>
-
 <h2>Philosophy</h2>
-
 <p>Most JSON parsers offer you a bunch of functions to load JSON data, parse it
 and extract any value by its name. jsmn proves that checking the correctness of
 every JSON packet or allocating temporary objects to store parsed JSON fields
 often is an overkill. </p>
-
 <p>JSON format itself is extremely simple, so why should we complicate it?</p>
-
 <p>jsmn is designed to be  <strong>robust</strong> (it should work fine even with erroneous
 data), <strong>fast</strong> (it should parse data on the fly), <strong>portable</strong> (no superfluous
 dependencies or non-standard C extensions). An of course, <strong>simplicity</strong> is a
 key feature - simple code style, simple algorithm, simple integration into
 other projects.</p>
-
 <h2>Features</h2>
-
 <ul>
 <li>compatible with C89</li>
 <li>no dependencies (even libc!)</li>
 <li>incremental single-pass parsing</li>
 <li>library code is covered with unit-tests</li>
 </ul>
-
 <h2>Design</h2>
-
 <p>The rudimentary jsmn object is a <strong>token</strong>. Let's consider a JSON string:</p>
-
 <pre><code>'{ "name" : "Jack", "age" : 27 }'
 </code></pre>
-
 <p>It holds the following tokens:</p>
-
 <ul>
 <li>Object: <code>{ "name" : "Jack", "age" : 27}</code> (the whole object)</li>
 <li>Strings: <code>"name"</code>, <code>"Jack"</code>, <code>"age"</code> (keys and some values)</li>
 <li>Number: <code>27</code></li>
 </ul>
-
 <p>In jsmn, tokens do not hold any data, but point to token boundaries in JSON
 string instead. In the example above jsmn will create tokens like: Object
 [0..31], String [3..7], String [12..16], String [20..23], Number [27..29].</p>
-
 <p>Every jsmn token has a type, which indicates the type of corresponding JSON
 token. jsmn supports the following token types:</p>
-
 <ul>
 <li>Object - a container of key-value pairs, e.g.:
-<code>{ "foo":"bar", "x":0.3 }</code></li>
+    <code>{ "foo":"bar", "x":0.3 }</code></li>
 <li>Array - a sequence of values, e.g.:
-<code>[ 1, 2, 3 ]</code></li>
+    <code>[ 1, 2, 3 ]</code></li>
 <li>String - a quoted sequence of chars, e.g.: <code>"foo"</code></li>
 <li>Primitive - a number, a boolean (<code>true</code>, <code>false</code>) or <code>null</code></li>
 </ul>
-
 <p>Besides start/end positions, jsmn tokens for complex types (like arrays
 or objects) also contain a number of child items, so you can easily follow
 object hierarchy.</p>
-
 <p>This approach provides enough information for parsing any JSON data and makes
 it possible to use zero-copy techniques.</p>
-
 <h2>Install</h2>
-
 <p>To clone the repository you should have mercurial installed. Just run:</p>
-
 <pre><code>$ hg clone http://bitbucket.org/zserge/jsmn jsmn
 </code></pre>
-
 <p>Repository layout is simple: jsmn.c and jsmn.h are library files; demo.c is an
 example of how to use jsmn (it is also used in unit tests); test.sh is a test
 script. You will also find README, LICENSE and Makefile files inside.</p>
-
 <p>To build the library, run <code>make</code>. It is also recommended to run <code>make test</code>.
 Let me know, if some tests fail.</p>
-
 <p>If build was successful, you should get a <code>libjsmn.a</code> library.
 The header file you should include is called <code>"jsmn.h"</code>.</p>
-
 <h2>API</h2>
-
 <p>Token types are described by <code>jsmntype_t</code>:</p>
-
 <pre><code>typedef enum {
     JSMN_OBJECT,
     JSMN_ARRAY,
     JSMN_PRIMITIVE
 } jsmntype_t;
 </code></pre>
-
 <p><strong>Note:</strong> Unlike JSON data types, primitive tokens are not divided into
 numbers, booleans and null, because one can easily tell the type using the
 first character:</p>
-
 <ul>
 <li><code>'t', 'f'</code> - boolean </li>
 <li><code>'n'</code> - null</li>
 <li><code>'-', '0'..'9'</code> - number</li>
 </ul>
-
 <p>Token is an object of <code>jsmntok_t</code> type:</p>
-
 <pre><code>typedef struct {
     jsmntype_t type; // Token type
     int start;       // Token start position
     int size;        // Number of child (nested) tokens
 } jsmntok_t;
 </code></pre>
-
 <p><strong>Note:</strong> string tokens point to the first character after
 the opening quote and the previous symbol before final quote. This was made 
 to simplify string extraction from JSON data.</p>
-
 <p>All job is done by <code>jsmn_parser</code> object. You can initialize a new parser using:</p>
-
 <pre><code>struct jsmn_parser parser;
 jsmntok_t tokens[10];
 
 // 10 - number of tokens available
 jsmn_init_parser(&amp;parser, js, tokens, 10);
 </code></pre>
-
 <p>This will create a parser, that can parse up to 10 JSON tokens from <code>js</code> string.</p>
-
 <p>Later, you can use <code>jsmn_parse(&amp;parser)</code> function to process JSON string with the parser.
 If something goes wrong, you will get an error. Error will be one of these:</p>
-
 <ul>
 <li><code>JSMN_SUCCESS</code> - everything went fine. String was parsed</li>
 <li><code>JSMN_ERROR_INVAL</code> - bad token, JSON string is corrupted</li>
 <li><code>JSMN_ERROR_NOMEM</code> - not enough tokens, JSON string is too large</li>
 <li><code>JSMN_ERROR_PART</code> - JSON string is too short, expecting more JSON data</li>
 </ul>
-
 <p>If you get <code>JSON_ERROR_NOMEM</code>, you can re-allocate more tokens and call
 <code>jsmn_parse</code> once more.  If you read json data from the stream, you can
 periodically call <code>jsmn_parse</code> and check if return value is <code>JSON_ERROR_PART</code>.
 You will get this error until you reach the end of JSON data.</p>
-
 <h2>Other info</h2>
-
 <p>This software is distributed under <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>,
- so feel free to integrate it in your commercial products.</p>
-	</div>
-</div>
-</body>
+ so feel free to integrate it in your commercial products.</p></div>
+		</div>
+	</body>
 </html>
-<!DOCTYPE HTML> 
-<html lang=en>
-<head>
-	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-	<title>nikl</title>
-	<meta name="description" content="Ultra-leightweight logging for embedded system" />
-	<meta name="keywords" content="log, embedded, variable-length, printf, format" />
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+		<title>Ultra-leightweight logging for embedded system</title>
+		<meta name="description" content="Minimalist open-source software for *nix and embedded systems" />
+		<meta name="keywords" content="log, embedded, variable-length, printf, format" />
+		<link rel="stylesheet" type="text/css" href="/style.css" />
+	</head>
 	<script type="text/javascript">
 
-	var _gaq = _gaq || [];
-	_gaq.push(['_setAccount', 'UA-16690724-5']);
-	_gaq.push(['_trackPageview']);
+		var _gaq = _gaq || [];
+		_gaq.push(['_setAccount', 'UA-16690724-5']);
+		_gaq.push(['_trackPageview']);
 
-	(function() {
-	var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-	ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-	var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-})();
-</script>
-</head>
-<body>
-<div id="container">
-	<div id="content">
-<h1>log.h</h1>
-
-<p>log.h is a ultra-leight logging library for <a href="nikl.html">nikl</a> framework.
+		(function() {
+			var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+			ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+				var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+		})();
+	</script>
+	<body>
+		<div id="container">
+			<div id="content"><h1>log.h</h1>
+<p>log.h is a ultra-leight logging library for <a href="/nikl.html">nikl</a> framework.
 You can use it to profile your code or debug time-critical parts.</p>
-
 <h2>idea</h2>
-
 <p>Real hackers don't use debuggers, they use <code>printf</code> instead, right?
 But <code>printf</code> is often too heavy for your tiny 8-bit MCU.</p>
-
 <p>Nikl's logger sends only printf payload (e.g. arguments, without even format 
 string) and some information to find the line in the source file and 
 get format string from there. </p>
-
 <p>It allows you to send (let's assume that sending 1 byte takes 1 instruction):</p>
-
 <ul>
 <li><code>TRACE()</code> message: 5 bytes, 10 instructions.</li>
 <li><code>LOG("status =  %d", s);</code> where <code>s=2</code>, s is a 32-bit number: 6 bytes, 20
-instructions.</li>
+    instructions.</li>
 </ul>
-
 <h2>protocol</h2>
-
 <p>Data is sent using very simple datagram protocol. Each datagram has the format:</p>
-
 <pre><code>+-----+----------+---------+---------+-----+-----+
 | STX | FILE_TAG | LINE_HI | LINE_LO | ... | ETX |
 +-----+----------+---------+---------+-----+-----+
 </code></pre>
-
 <ul>
 <li><code>STX</code> - start of the datagram. <code>STX = 0xFC</code></li>
 <li><code>ETX</code> - end of the datagram. <code>ETX = 0xFD</code></li>
 <li><code>FILE_TAG</code> - label of the source file (1 byte)</li>
 <li><code>LINE_HI</code> and <code>LINE_LO</code> - high and low byte of <code>__LINE__</code> value (2 bytes in total).</li>
 </ul>
-
 <p>After <code>LINE_LO</code> but before <code>ETX</code> byte there is datagram body. Datagram body can
 contain 3 data types:</p>
-
 <ul>
 <li>Integer numbers</li>
 <li>NULL-terminated strings</li>
 <li>Binary data of known length</li>
 </ul>
-
 <p>Let's see how these type are encoded.</p>
-
 <h2>data types</h2>
-
 <p>Integers are passed using variable-length format.
 Everything depends on the 1st byte:</p>
-
 <ul>
 <li><code>0nnnnnnn</code>:  7-bit number</li>
 <li><code>10nnnnnn nnnnnnnn</code>: 14-bit number</li>
 <li><code>11111101</code> - ETX</li>
 <li><code>11111011</code> - Zero-terminated string</li>
 <li><code>11111111 ....</code> - variable-length data. Length is a variable-length number. 
-Data comes after length.</li>
+    Data comes after length.</li>
 </ul>
-
 <p>Easy, right?</p>
-
 <h2>internals</h2>
-
 <p>Well, sending TRACE() is simple - it's just a function that sends 5 bytes.</p>
-
 <p>LOG() is somewhat more complicated. Normally, LOG() arguments are passed
 comma-separated. As you know, statements in C can be also written separated
 with commas. All we need to do is to wrap each parameter with a function,
 depending on its type. Unfortunatelly, there seems to be no way to do this 
 automatically, so the programmer must to it manually:</p>
-
 <pre><code>LOG("status = %d", _I(status)); /* _I() - for numbers */
 LOG("request = %s", _S(req));   /* _S() - for strings */
 LOG("payload = %v", _D(buf, 512)); /* _D() for binary data */
-</code></pre>
-	</div>
-</div>
-</body>
+</code></pre></div>
+		</div>
+	</body>
 </html>
-<!DOCTYPE HTML> 
-<html lang=en>
-<head>
-	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-	<title>nikl</title>
-	<meta name="description" content="Microframework for embedded systems development" />
-	<meta name="keywords" content="embedded system, rtos, protothread, development, avr, pic, arm, microcontroller, programming" />
-
-	<link rel="stylesheet" href="style.css" type="text/css"/>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+		<title>Microframework for embedded systems development</title>
+		<meta name="description" content="Minimalist open-source software for *nix and embedded systems" />
+		<meta name="keywords" content="zserge, minimalism, unix, embedded" />
+		<link rel="stylesheet" type="text/css" href="/style.css" />
+	</head>
 	<script type="text/javascript">
 
-	var _gaq = _gaq || [];
-	_gaq.push(['_setAccount', 'UA-16690724-5']);
-	_gaq.push(['_trackPageview']);
+		var _gaq = _gaq || [];
+		_gaq.push(['_setAccount', 'UA-16690724-5']);
+		_gaq.push(['_trackPageview']);
 
-	(function() {
-	var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-	ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-	var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-})();
-</script>
-</head>
-<body>
-<div id="container">
-	<div id="content">
-<h1>nikl</h1>
-
+		(function() {
+			var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+			ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+				var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+		})();
+	</script>
+	<body>
+		<div id="container">
+			<div id="content"><h1>nikl</h1>
 <p>Nikl is a micro-framework for small embedded devices like AVR
 microcontrollers.</p>
-
 <h2>HISTORY</h2>
-
 <p>One day I decided to write a real-time OS. It had multitasking, semaphores and
 supported only ARM architecture. Then I tried to port it to use with the AVRs,
 but context switching was slow, memory was very limited and my OS became
 generally useless.</p>
-
 <p>Then, I started another project called avOS, that some kind of a kernel that
 was not real-time, and was widely using protothreads for pseudo-multitasking.
 Later the project was abandoned, but now I'm trying to give it a new born.</p>
-
 <h2>PHILOSOPHY</h2>
-
 <ul>
 <li>No .c files, until they are really needed</li>
 <li>This implies - no build system. You can take any compinations of modules you
-need and just copy or symlink them to your project directory</li>
+  need and just copy or symlink them to your project directory</li>
 </ul>
-
 <h2>STRUCTURE</h2>
-
 <p>You should see "/inc" folder. There is where all the modules are stored.
 Another folder is "/test". This is a folder with unit tests. </p>
-
 <p>Let's go deeper. Inside "/inc" folder you should find modules for</p>
-
 <ul>
 <li>bare-bone unit-testing (test.h)</li>
 <li>list-based data structures (list.h)</li>
 <li>FORTH virtual machine (ivm.h)</li>
 </ul>
-
 <p>For the IVM there is a simple comiler written in Python (see "/utils/ivmc").</p>
-
 <h2>HOW TO USE TESTS</h2>
-
 <p>There is a bunch of unit-testing frameworks. But they all are an overkill for
 tiny embedded systems. If you just need to write a few tests for your device
 and have a possibility to detect which test failed - you can use test.h.</p>
-
 <p>Here's an example of typical usage. Assume we have a board that works with data
 on the SD card. We need to be sure that SD card is valid, and hardware is ready
 to use.  I still doubt if it is a correct usage of unit tests, as it seems like
 we test hardware, but this is how it works for me:</p>
-
 <pre><code>/* Use custom error notification, e.g. show line number on LCD,
  * or send message and line number over UART, or just turn the
  * error indicating LED on */
 
 ...
 </code></pre>
-
 <p>Ain't it simple? You basically write test functions that do some assertions.
 Then you call these functions and if the return value is non-zero - it means
 that the test has failed. In this case TEST_NOTIFY function will be called and
 it's your chance to tell user that something was wrong</p>
-
 <ul>
 <li>your test functions must return 0 at the end to indicate success</li>
 <li>you need to test values using check(condition) macro.</li>
 <li>you need to run your test functions using test(func, descr) function</li>
 <li>if you use your own TEST_NOTIFY implementation you can print test name
-(msg argument) and line number (line argument), where the error occurred.</li>
+  (msg argument) and line number (line argument), where the error occurred.</li>
 </ul>
-
 <h2>HOW TO USE LISTS</h2>
-
 <p>Lists are stolen from linux kernel in a shameless way. They are not so easy-to-use
 as more common <sys/queue.h>, but are more flexible.</p>
-
 <p>To define an object, that can be linked into the list, do:</p>
-
 <pre><code>typedef struct {
     /* My data */
     int value1;
     list_t list;
 } my_obj;
 </code></pre>
-
 <p>To create a list head, do:</p>
-
 <pre><code>list_t my_obj_list;
 </code></pre>
-
 <p>Next, initialize list:</p>
-
 <pre><code>list_init(&amp;my_obj_list);
 </code></pre>
-
 <p>And now you can do everything you want with your lists:</p>
-
 <pre><code>my_obj *a, *b, *c; /* object to add to the list */
 list_t *tmp; /* list iterator */
 
     printf("%d\n", iterator-&gt;value1);
 }
 </code></pre>
-
 <p>For now only double-linked lists are implemented. Soon single-linked lists and
 queues will be added.</p>
-
 <h2>HOW TO USE IVM</h2>
-
-<p>IVM is not that simple. See IVM.README for more details to how use it.</p>
-	</div>
-</div>
-</body>
+<p>IVM is not that simple. See IVM.README for more details to how use it.</p></div>
+		</div>
+	</body>
 </html>

File pages/index.mkd

-zserge's home page
-==================
-
-Hi! I'm Serge. I'm a programmer.
-
-I like
-------
-
-I like: simplicity, unix, C, embedded systems, android, Go language. 
-
-I also like: music, retro computers and games, esoteric programming languages.
-But most of all I like my girlfriend, Anna.
-
-Projects
---------
-
-It would be great if my works would be useful to someone.
-They are free, open source, and very simple:
-
-* [jsmn](jsmn.html)  - JSON parser for small systems
-[[repo](http://bitbucket.org/zserge/jsmn)]
-* [six](six.html) - very simple IRC client [[repo](http://bitbucket.org/zserge/six)]
-* gotftp - basic TFTP server written in Go
-	[[repo](http://bitbucket.org/zserge/gotftp)]
-* j1vm - J1 Forth CPU simulator and compiler [[repo](http://bitbucket.org/zserge/j1vm)]
-* [nikl](nikl.html) - micro-framework for programming embedded systems [[repo](http://bitbucket.org/zserge/nikl)]
-* [ivm](ivm.html) - simple forth virtual machine (a part of [nikl](nikl.html)) [[repo](http://bitbucket.org/zserge/nikl)]
-
-Contact
--------
-
-I'm not a social guy, but 
-
-* You can email me at google mail: zaitsev.serge
-* Or follow on twitter: [@zsergo](http://twitter.com/zsergo)
-* Or read my [blog](http://zserge.wordpress.com) (in russian)
-

File pages/j1vm.mkd

-IVM
-===
-
-IVM (IV Machine or Forth Machine) is a very simple virtual machine
-for small embedded devices. It's a software implementation of
-[J1 Forth CPU](http://http://excamera.com/sphinx/fpga-j1.html) and is binary
-compatible with it.
-IVM is a stack machine and is designed to run Forth code, but
-can be used as a general-purpose VM as well.
-
-HOW CAN I USE IT?
------------------
-
-There can be various scenarios, but in general if you:
-
-* want to customize your software without reprogramming the firmware
-* want to isolate and control your software
-* have enough storage to put virtual machine there (it's about 1K!)
-* have storage for the software running inside virtual machine (it's usually 
-  more compact than native code)
-* can write critical parts in native code and map them to VM memory-mapped I/O
-* can forgive than it run 10 times slower than native code
-
-...then you should try running IVM.
-
-DESIGN
-------
-
-IVM is designed to be as small as possible (less than 1K) to fit even the
-smallest micros, and it should bring them to the new level by possibility of
-running external code.
-
-IVM is very flexible and easy to customize.
-
-IVM code is stored in the "/inc/ivm.h" file. It has two functions:
-
-- ivm_reset() - brings the VM back to the default state
-- ivm_step(op) - executes instruction "op" on the virtual machine
-
-You need to implement some functionality by your own:
-
-- Fetching instructions. Each instruction is a 2-byte word. Instruction 
-index is stored in the `ivm_pc` variable
-- Memory access. You must implement `ivm_mem_put` and `ivm_mem_get` functions
-to read and write to the memory. It is not a part of IVM code, because you can
-use your I/O ports in a memory mapped way, or you might want to cache some
-address range if you use external memory chip. Also, you decide what is the amount
-of RAM available to the VM.
-
-INSTRUCTION SET
----------------
-
-IVM has a simple instruction set compatible with the J1 Forth CPU.
-
-IVM has two stacks - data stack (DS) and return stack (RS).  Default depth for
-both of them is 16 levels (**WARNING: Original J1 has 32 levels!**)
-
-Basically, there are 5 types of the instructions:
-
-- LIT: put a number on the DS
-
-- JMP: jump to address
-
-- JZ: jump to address if the value on the top of the DS is zero. 
-This instruction also deletes this value from the top of the DS.
-
-- CALL: store current address to the RS and jump to the address.
-This instruction makes it possible to implement functions.
-
-- ALU: perform ariphmetic operation
-
-There are 16 types of ALU instructions, each of them can also manipulate
-DS and RS stacks, change PC and work with memory. For more details
-check the J1 project.
-
-COMPILER
---------
-
-If you know Forth, you can use crosscompiler from the J1 project.
-
-ALternatively, there is a separate project of developer tools for
-J1 CPU - [j1vm](http://bitbucket.org/zserge/j1vm)
-
-There is a Forth compiler `j1c` and a simulator (`j1vm`).
-
-IVM FORTH BASICS
-----------------
-
-**Syntax**
-
-Forth has reverse Polish notation. So, to add two number you should
-write `2 3 +` and to add three numbers - `1 2 3 + +`.
-
-This is so, because Forth program is executed from left to right, so if you
-write `open read close` it will mean firth open, then read and finally close.
-This sounds more intuitive, right?
-
-Comments in Forth are like:
-
-	( this is a comment )
-	\ this is a single line comment
-
-**Stacks**
-
-*IVM has two stacks, so how do they work?*
-
-Data stack is a place where temporary values are stored and where
-all the calculations happen. So, if you write `2 3 +` it means:
-"put 2 on the top of the data stack, then put 3 over it, then run
-ALU function +". Function "+" fetches two items from the top of
-the stack, add them, and puts the result on the data stack as well,
-replacing that two items. So, if stack was like "1 2 3" before 
-calling "+", after that the stack will be "1 5".
-
-There is a specific notation that helps you to know how functions
-manipulate data stack. It's written like a comment. This is how
-we would describe "+" function: `( a b -- a+b )`. See? There were 
-`a` and `b` on the top, but we replace them with `a+b` sum.
-
-How would you call this function: `( a b -- b a )`? Right, it's `swap`.
-And this one: `( a -- a a )` is `dup`, because it duplicates the top item.
-And this one: `( a -- )` is `drop`, because it drops the top item from
-the stack.
-
-*But why there are two stacks?*
-
-The second stack is a return stack, but it does more than storing
-return addresses when calling functions. You can put your local values
-there when playing with data stack, and fetch them later.
-
-Whan if you need a function like `( a b c d -- a+b c+d )`?
-First you call `+` and get `( a b c+d )`. But now you need to 
-remove that `c+d` from the top of the stack! You can move that item
-to the return stack. Use functions `>r` and `r>` to do that.
-The first one is "put-to-return-stack" and the second one is
-"fetch-from-return-stack", but it's obvious because of the arrow
-position. So, this is your code for the function above:
-
-	( a b c d -- a+b c+d)
-	+ ( a b c+d )
-	>r ( a b )
-	+ ( a+b )
-	r>  ( a+b c+d )
-
-**Memory**
-
-If you need to store global variables, you can use memory-access functions: 
-`@` and `!`. They are "fetch" and "store" 
-
-It means, that `100 @` fetches 16-bit value from address 100, and `5 100 !` writes value 5 to address 100. At this moment variables and constants become handy.
-
-Both variables and constants differ from what you normally see in Forth. To make a global variable you write:
-
-	var my-var
-
-This allocates new variable address in RAM. If you need to make a constant definition write:
-
-	equ X 10
-
-Now you can use them in your code: `my-var @` or `X my-var !`. Great thing is that you can use constants like variables if you need to use specific address (e.g. for memory-mapped I/O): 
-
-	equ GPIO 1234
-	( GPIO & 0x80 )
-	GPIO @ 128 & 
-
-Control structures
-------------------
-
-At this point Forth is just an assembly language with weird syntax. Yes, it's compact, it's easy to learn and it's fast, but it's too much low-level. How do I make a loop? How to branch my code?
-
-It's easy. First, about branches. Internally, they use JZ/JMP instructions. The syntax is like:
-
-	<condition> if <then-branch> else <else-branch> then
-	<condition> if <then-branch> then
-
-	: max ( a b -- max[a,b] )
-		over over ( a b a b )
-		> ( a b a>b )
-		if ( a b )
-			drop ( a )
-		else
-			nip ( b )
-		then
-
-There are several types of loops in the current implementation:
-
-	begin <loop-body> again ( infinite loop )
-	begin <loop-body> <condition> until ( do .. while )
-

File pages/jsmn.mkd

-
-JSMN
-====
-
-jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C.  It can be
-easily integrated into resource-limited or embedded projects.
-
-You can find more information about JSON format at [json.org][1]
-
-Library sources are available at [bitbucket.org/zserge/jsmn][2]
-
-Philosophy
-----------
-
-Most JSON parsers offer you a bunch of functions to load JSON data, parse it
-and extract any value by its name. jsmn proves that checking the correctness of
-every JSON packet or allocating temporary objects to store parsed JSON fields
-often is an overkill. 
-
-JSON format itself is extremely simple, so why should we complicate it?
-
-jsmn is designed to be	**robust** (it should work fine even with erroneous
-data), **fast** (it should parse data on the fly), **portable** (no superfluous
-dependencies or non-standard C extensions). An of course, **simplicity** is a
-key feature - simple code style, simple algorithm, simple integration into
-other projects.
-
-Features
---------
-
-* compatible with C89
-* no dependencies (even libc!)
-* highly portable (tested on x86/amd64, ARM, AVR)
-* about 200 lines of code
-* extremely small code footprint
-* API contains only 2 functions
-* no dynamic memory allocation
-* incremental single-pass parsing
-* library code is covered with unit-tests
-
-Design
-------
-
-The rudimentary jsmn object is a **token**. Let's consider a JSON string:
-
-	'{ "name" : "Jack", "age" : 27 }'
-
-It holds the following tokens:
-
-* Object: `{ "name" : "Jack", "age" : 27}` (the whole object)
-* Strings: `"name"`, `"Jack"`, `"age"` (keys and some values)
-* Number: `27`
-
-In jsmn, tokens do not hold any data, but point to token boundaries in JSON
-string instead. In the example above jsmn will create tokens like: Object
-[0..31], String [3..7], String [12..16], String [20..23], Number [27..29].
-
-Every jsmn token has a type, which indicates the type of corresponding JSON
-token. jsmn supports the following token types:
-
-* Object - a container of key-value pairs, e.g.:
-	`{ "foo":"bar", "x":0.3 }`
-* Array - a sequence of values, e.g.:
-	`[ 1, 2, 3 ]`
-* String - a quoted sequence of chars, e.g.: `"foo"`
-* Primitive - a number, a boolean (`true`, `false`) or `null`
-
-Besides start/end positions, jsmn tokens for complex types (like arrays
-or objects) also contain a number of child items, so you can easily follow
-object hierarchy.
-
-This approach provides enough information for parsing any JSON data and makes
-it possible to use zero-copy techniques.
-
-Install
--------
-
-To clone the repository you should have mercurial installed. Just run:
-
-	$ hg clone http://bitbucket.org/zserge/jsmn jsmn
-
-Repository layout is simple: jsmn.c and jsmn.h are library files; demo.c is an
-example of how to use jsmn (it is also used in unit tests); test.sh is a test
-script. You will also find README, LICENSE and Makefile files inside.
-
-To build the library, run `make`. It is also recommended to run `make test`.
-Let me know, if some tests fail.
-
-If build was successful, you should get a `libjsmn.a` library.
-The header file you should include is called `"jsmn.h"`.
-
-API
----
-
-Token types are described by `jsmntype_t`:
-
-	typedef enum {
-		JSMN_OBJECT,
-		JSMN_ARRAY,
-		JSMN_STRING,
-		JSMN_PRIMITIVE
-	} jsmntype_t;
-
-**Note:** Unlike JSON data types, primitive tokens are not divided into
-numbers, booleans and null, because one can easily tell the type using the
-first character:
-
-* <code>'t', 'f'</code> - boolean 
-* <code>'n'</code> - null
-* <code>'-', '0'..'9'</code> - number
-
-Token is an object of `jsmntok_t` type:
-
-	typedef struct {
-		jsmntype_t type; // Token type
-		int start;       // Token start position
-		int end;         // Token end position
-		int size;        // Number of child (nested) tokens
-	} jsmntok_t;
-
-**Note:** string tokens point to the first character after
-the opening quote and the previous symbol before final quote. This was made 
-to simplify string extraction from JSON data.
-
-All job is done by `jsmn_parser` object. You can initialize a new parser using:
-
-	struct jsmn_parser parser;
-	jsmntok_t tokens[10];
-
-	// js - pointer to JSON string
-	// tokens - an array of tokens available
-	// 10 - number of tokens available
-	jsmn_init_parser(&parser, js, tokens, 10);
-
-This will create a parser, that can parse up to 10 JSON tokens from `js` string.
-
-Later, you can use `jsmn_parse(&parser)` function to process JSON string with the parser.
-If something goes wrong, you will get an error. Error will be one of these:
-
-* `JSMN_SUCCESS` - everything went fine. String was parsed
-* `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted
-* `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large
-* `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data
-
-If you get `JSON_ERROR_NOMEM`, you can re-allocate more tokens and call
-`jsmn_parse` once more.  If you read json data from the stream, you can
-periodically call `jsmn_parse` and check if return value is `JSON_ERROR_PART`.
-You will get this error until you reach the end of JSON data.
-
-Other info
-----------
-
-This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php),
- so feel free to integrate it in your commercial products.
-
-[1]: http://www.json.org/
-[2]: https://bitbucket.org/zserge/jsmn/wiki/Home

File pages/log.mkd

-log.h
-=====
-
-log.h is a ultra-leight logging library for [nikl](nikl.html) framework.
-You can use it to profile your code or debug time-critical parts.
-
-idea
-----
-
-Real hackers don't use debuggers, they use `printf` instead, right?
-But `printf` is often too heavy for your tiny 8-bit MCU.
-
-Nikl's logger sends only printf payload (e.g. arguments, without even format 
-string) and some information to find the line in the source file and 
-get format string from there. 
-
-It allows you to send (let's assume that sending 1 byte takes 1 instruction):
-
-* `TRACE()` message: 5 bytes, 10 instructions.
-* `LOG("status =  %d", s);` where `s=2`, s is a 32-bit number: 6 bytes, 20
-	instructions.
-
-protocol
---------
-
-Data is sent using very simple datagram protocol. Each datagram has the format:
-
-	+-----+----------+---------+---------+-----+-----+
-	| STX | FILE_TAG | LINE_HI | LINE_LO | ... | ETX |
-	+-----+----------+---------+---------+-----+-----+
-
-* `STX` - start of the datagram. `STX = 0xFC`
-* `ETX` - end of the datagram. `ETX = 0xFD`
-* `FILE_TAG` - label of the source file (1 byte)
-* `LINE_HI` and `LINE_LO` - high and low byte of `__LINE__` value (2 bytes in total).
-
-After `LINE_LO` but before `ETX` byte there is datagram body. Datagram body can
-contain 3 data types:
-
-* Integer numbers
-* NULL-terminated strings
-* Binary data of known length
-
-Let's see how these type are encoded.
-
-data types
-----------
-
-Integers are passed using variable-length format.
-Everything depends on the 1st byte:
-
-* `0nnnnnnn`:  7-bit number
-* `10nnnnnn nnnnnnnn`: 14-bit number
-* `110nnnnn nnnnnnnn nnnnnnnn`: 21-bit number
-* `11100nnn nnnnnnnn nnnnnnnn nnnnnnnn`: 27-bit number
-* `11101nnn nnnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn`: 35-bit number
-* `11111100` - STX
-* `11111101` - ETX
-* `11111011` - Zero-terminated string
-* `11111111 ....` - variable-length data. Length is a variable-length number. 
-	Data comes after length.
-
-Easy, right?
-
-internals
----------
-
-Well, sending TRACE() is simple - it's just a function that sends 5 bytes.
-
-LOG() is somewhat more complicated. Normally, LOG() arguments are passed
-comma-separated. As you know, statements in C can be also written separated
-with commas. All we need to do is to wrap each parameter with a function,
-depending on its type. Unfortunatelly, there seems to be no way to do this 
-automatically, so the programmer must to it manually:
-
-	LOG("status = %d", _I(status)); /* _I() - for numbers */
-	LOG("request = %s", _S(req));   /* _S() - for strings */
-	LOG("payload = %v", _D(buf, 512)); /* _D() for binary data */
-
-

File pages/nikl.mkd

-nikl
-====
-
-Nikl is a micro-framework for small embedded devices like AVR
-microcontrollers.
-
-HISTORY
--------
-
-One day I decided to write a real-time OS. It had multitasking, semaphores and
-supported only ARM architecture. Then I tried to port it to use with the AVRs,
-but context switching was slow, memory was very limited and my OS became
-generally useless.
-
-Then, I started another project called avOS, that some kind of a kernel that
-was not real-time, and was widely using protothreads for pseudo-multitasking.
-Later the project was abandoned, but now I'm trying to give it a new born.
-
-PHILOSOPHY
-----------
-
-* No .c files, until they are really needed
-* This implies - no build system. You can take any compinations of modules you
-  need and just copy or symlink them to your project directory
-
-
-STRUCTURE
----------
-
-You should see "/inc" folder. There is where all the modules are stored.
-Another folder is "/test". This is a folder with unit tests. 
-
-Let's go deeper. Inside "/inc" folder you should find modules for
-
-* bare-bone unit-testing (test.h)
-* list-based data structures (list.h)
-* FORTH virtual machine (ivm.h)
-
-For the IVM there is a simple comiler written in Python (see "/utils/ivmc").
-
-HOW TO USE TESTS
-----------------
-
-There is a bunch of unit-testing frameworks. But they all are an overkill for
-tiny embedded systems. If you just need to write a few tests for your device
-and have a possibility to detect which test failed - you can use test.h.
-
-Here's an example of typical usage. Assume we have a board that works with data
-on the SD card. We need to be sure that SD card is valid, and hardware is ready
-to use.  I still doubt if it is a correct usage of unit tests, as it seems like
-we test hardware, but this is how it works for me:
-
-	/* Use custom error notification, e.g. show line number on LCD,
-	 * or send message and line number over UART, or just turn the
-	 * error indicating LED on */
-	#define TEST_NOTIFY(msg, line)	lcd_display_number(line)
-
-	#include <test.h>
-
-	/* Check that after hardware initialization SD card is present */
-	int test_sdcard_available() {
-		int part_id;
-		sdcard_init();
-		check(sdcard_available() == 1);
-
-		/* Ensure that FAT partition ID is 1..4 */
-		part_id = sdcard_find_fat_partition();
-		check(part_id > 0 && part_id <= 4);
-
-		return 0;
-	}
-
-	/* Check that toggling LED port works correctly */
-	int test_leds() {
-		LED_PORT |= (1 << GREEN_LED);
-		check(LED_PORT & (1 << GREEN_LED));
-		LED_PORT &= ~(1 << GREEN_LED);
-		check((LED_PORT & ~(1 << GREEN_LED)) == 0);
-	}
-
-	....
-
-	test(test_sdcard_available(), "sdcard test");
-	test(test_leds(), "LED test");
-
-	...
-
-Ain't it simple? You basically write test functions that do some assertions.
-Then you call these functions and if the return value is non-zero - it means
-that the test has failed. In this case TEST_NOTIFY function will be called and
-it's your chance to tell user that something was wrong
-
-* your test functions must return 0 at the end to indicate success
-* you need to test values using check(condition) macro.
-* you need to run your test functions using test(func, descr) function
-* if you use your own TEST_NOTIFY implementation you can print test name
-  (msg argument) and line number (line argument), where the error occurred.
-
-HOW TO USE LISTS
-----------------
-
-Lists are stolen from linux kernel in a shameless way. They are not so easy-to-use
-as more common <sys/queue.h>, but are more flexible.
-
-To define an object, that can be linked into the list, do:
-
-	typedef struct {
-		/* My data */
-		int value1;
-		int value2;
-		/* List pointer */
-		list_t list;
-	} my_obj;
-
-To create a list head, do:
-
-	list_t my_obj_list;
-
-Next, initialize list:
-
-	list_init(&my_obj_list);
-
-And now you can do everything you want with your lists:
-
-	my_obj *a, *b, *c; /* object to add to the list */
-	list_t *tmp; /* list iterator */
-
-	list_add(&my_obj_list, &a->list);
-	list_add(&my_obj_list, &b->list);
-	list_add(&my_obj_list, &c->list);
-
-	list_del(&b->list);
-
-	list_for_each(&my_obj_list, tmp) {
-		/* This is how to get the object using list_entry() */
-		my_obj *iterator;
-		iterator = list_entry(tmp, my_obj, list);
-		printf("%d\n", iterator->value1);
-	}
-
-For now only double-linked lists are implemented. Soon single-linked lists and
-queues will be added.
-
-HOW TO USE IVM
---------------
-
-IVM is not that simple. See IVM.README for more details to how use it.
-

File pages/six.mkd

-six
-===
-
-six - simple IRC client kept simple (that's what the name means: sicks ->
-six). six is really tiny piece of software - only 200 SLOC.
-
-It's mainly inspired by [sic](http://tools.suckless.org/sic).
-
-install
--------
-
-Get the sources and compile it. The only dependency is Readline.
-
-	hg clone https://bitbucket.org/zserge/six
-	cd six
-	make
-
-If you want to install it - copy `six` binary to your `/usr/local/bin`
-folder. I hope one day I will add a `make install` rule.
-
-usage
------
-
-In short, you can run it like:
-
-	six <host>
-
-For more automated usage you can specify your nickname, channel name,
-password (if you are not paranoid) or a custom startup script:
-
-	six -n john -k pa55w0rd -j "#johnchan" irc.server.net
-
-	six -s <commands> irc.server.net
-
-commands
---------
-
-After your connected to the server and passed authorization step
-you can use these commands:
-
-* `:j channel` - join the channel
-* `:l channel` - leave the channel
-* `:m nick msg` - send message to the user `<nick>`
-* `s nick` - set default interlocutor to `<nick>`
-
-Here's a typical session:
-
-	:j #johnchan
-	hi all!       <-- everyone on #johnchan reads this
-	:m jack hi, jack  <-- only Jack reads this
-	:m jane hi, jane <-- only Jane reads this
-
-	:s jane       <-- now you are talking to Jane by default
-	i'm using six IRC client and it's the best! <-- Jane reads this
-	wnat to try it?                             <-- and this, too
-
-Another piece of sugar is autoreply. When someone send you a message
-and you press `<tab>` his nickname is written as a recepient of your next
-message:
-
-	<john> hi!
-	(here you press Tab)
-	> :m john _ <-- here's your cursor
-	
-See?
-
-bitlbee
--------
-
-I also like using six with bitlbee. I start it like:
-
-	six -s "PRIVMSG root :IDENTIFY myPassw0rd" localhost
-
-And then I easily use my jabber in the terminal.
-
-screenshots
------------
-
-If your terminal looks nice - `six` also looks nice ;)
-
-Here's my six+urxvt+tmux
-
-![screenshot](six.png)
+#!/usr/bin/env python
+
+# =============================================================================
+#
+#    Poole - A damn simple static website generator.
+#    Copyright (C) 2009 Oben Sonne <obensonne@googlemail.com>
+#
+#    This file is part of Poole.
+#
+#    Poole is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    Poole is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with Poole.  If not, see <http://www.gnu.org/licenses/>.
+#
+# =============================================================================
+
+from __future__ import with_statement
+
+import codecs
+import glob
+import imp
+import optparse
+import os
+import os.path
+from os.path import join as opj
+from os.path import exists as opx
+import re
+import shutil
+import StringIO
+import sys
+import traceback
+import urlparse
+
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+from BaseHTTPServer import HTTPServer
+
+try:
+    import markdown
+except ImportError:
+    print("error: need python-markdown, get it from "
+          "http://www.freewisdom.org/projects/python-markdown/Installation")
+    sys.exit(1)
+
+# =============================================================================
+# init site
+# =============================================================================
+
+EXAMPLE_FILES =  {
+
+"page.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset={{ __encoding__ }}" />
+    <title>poole - {{ page["title"] }}</title>
+    <meta name="description" content="{{ page.get("description", "a poole site") }}" />
+    <meta name="keywords" content="{{ page.get("keywords", "poole") }}" />
+    <link rel="stylesheet" type="text/css" href="poole.css" />
+</head>
+<body>
+    <div id="box">
+    <div id="header">
+         <h1>a poole site</h1>
+         <h2>{{ page["title"] }}</h2>
+    </div>
+    <div id="menu">
+    <!--%
+        mpages = [p for p in pages if "menu-position" in p]
+        mpages.sort(key=lambda p: int(p["menu-position"]))
+        entry = '<span class="%s"><a href="%s">%s</a></span>'
+        for p in mpages:
+            style = p["title"] == page["title"] and "current" or ""
+            print(entry % (style, p["url"], p["title"]))
+    %-->
+    </div>
+    <div id="content">{{ __content__ }}</div>
+    </div>
+    <div id="footer">
+        Built with <a href="http://bitbucket.org/obensonne/poole">Poole</a>
+        &middot;
+        Licensed as <a href="http://creativecommons.org/licenses/by-sa/3.0">CC-SA</a>
+        &middot;
+        <a href="http://validator.w3.org/check?uri=referer">Validate me</a>
+    </div>
+</body>
+</html>
+""",
+
+# -----------------------------------------------------------------------------
+
+opj("input", "index.md"): """
+title: home
+menu-position: 0
+---
+
+## Welcome to Poole
+
+In Poole you write your pages in [markdown][md]. It's easier to write
+markdown than HTML.
+
+Poole is made for simple websites you just want to get done, without installing
+a bunch of requirements and without learning a template engine.
+
+In a build, Poole copies every file from the *input* directory to the *output*
+directory. During that process every markdown file (ending with *md*, *mkd*,
+*mdown* or *markdown*) is converted to HTML using the project's `page.html`
+as a skeleton.
+
+[md]: http://daringfireball.net/projects/markdown/
+""",
+
+# -----------------------------------------------------------------------------
+
+opj("input", "logic.md"): """
+menu-position: 4
+---
+Poole has basic support for content generation using Python code inlined in
+page files. This is everything but a clear separation of logic and content but
+for simple sites this is just a pragmatic way to get things done fast.
+For instance the menu on this page is generated by some inlined Python code in
+the project's `page.html` file.
+
+Just ignore this feature if you don't need it :)
+
+Content generation by inlined Python code is good to add some zest to your
+site. If you use it a lot, you better go with more sophisticated site
+generators like [Hyde](http://ringce.com/hyde).
+""",
+
+# -----------------------------------------------------------------------------
+
+opj("input", "layout.md"): """
+menu-position: 3
+---
+Every page of a poole site is based on *one global template file*, `page.html`.
+All you need to adjust the site layout is to
+
+ * edit the page template `page.html` and
+ * extend or edit the style file `input/poole.css`.
+""",
+
+opj("input", "blog.md"): """
+menu-position: 10
+---
+Poole has basic blog support. If an input page's file name has a structure like
+`page-title.YYYY-MM-DD.post-title.md`, e.g. `blog.201010-02-27.read_this.md`,
+Poole recognizes the date and post title and sets them as attributes of the
+page. These attributes can then be used to generate a list of blog posts:
+
+<!--%
+from datetime import datetime
+posts = [p for p in pages if "post" in p] # get all blog post pages
+posts.sort(key=lambda p: p.get("date"), reverse=True) # sort post pages by date
+for p in posts:
+    date = datetime.strptime(p.date, "%Y-%m-%d").strftime("%B %d, %Y")
+    print "  * **[%s](%s)** - %s" % (p.post, p.url, date) # markdown list item
+%-->
+
+Have a look into `input/blog.md` to see how it works. Feel free to adjust it
+to your needs.
+""",
+
+# -----------------------------------------------------------------------------
+
+opj("input", "blog.2010-02-22.Doctors_in_my_penguin.md") : """
+
+---
+## {{ page["post"] }}
+
+*Posted at
+<!--%
+from datetime import datetime
+print datetime.strptime(page["date"], "%Y-%m-%d").strftime("%B %d, %Y")
+%-->*
+
+There is a bank in my eel, your argument is invalid.
+
+More nonsense at <http://automeme.net/>.
+""",
+
+# -----------------------------------------------------------------------------
+
+opj("input", "blog.2010-03-01.I_ate_all the pokemans.md"): """
+
+## {{ page["post"] }}
+
+*Posted at <!--{ page["date"] }-->.*
+
+What *are* interior crocodile alligators? We just don't know.
+
+More nonsense at <http://automeme.net/>.
+""",
+
+# -----------------------------------------------------------------------------
+
+opj("input", "poole.css"): """
+body {
+    font-family: sans;
+    width: 800px;
+    margin: 1em auto;
+    color: #2e3436;
+}
+div#box {
+    border: solid #2e3436 1px;
+}
+div#header, div#menu, div#content, div#footer {
+    padding: 1em;
+}
+div#menu {
+    background-color: #2e3436;
+    padding: 0.6em 0 0.6em 0;
+}
+#menu span {
+    background-color: #2e3436;
+    font-weight: bold;
+    padding: 0.6em;
+}
+#menu span.current {
+    background-color: #555753;
+}
+#menu a {
+    color: #fefefc;
+    text-decoration: none;
+}
+div#footer {
+    color: gray;
+    text-align: center;
+    font-size: small;
+}
+div#footer a {
+    color: gray;
+    text-decoration: none;
+}
+pre {
+    border: dotted black 1px;
+    background: #eeeeec;
+    font-size: small;
+    padding: 1em;
+}
+
+"""
+}
+
+def init(project):
+    """Initialize a site project."""
+
+    if not opx(project):
+        os.makedirs(project)
+
+    if os.listdir(project):
+        print("error  : project dir %s is not empty, abort" % project)
+        sys.exit(1)
+
+    os.mkdir(opj(project, "input"))
+    os.mkdir(opj(project, "output"))
+
+    for fname, content in EXAMPLE_FILES.items():
+        with open(opj(project, fname), 'w') as fp:
+            fp.write(content)
+
+    print("success: initialized project")
+
+# =============================================================================
+# build site
+# =============================================================================
+
+MKD_PATT = r'\.(?:md|mkd|mdown|markdown)$'
+
+class Page(dict):
+    """Abstraction of a source page."""
+
+    _re_eom = re.compile(r'^---+ *\r?\n?$')
+    _re_vardef = re.compile(r'^([^\n:=]+?)[:=]((?:.|\n )*)', re.MULTILINE)
+    _sec_macros = "macros"
+    _modmacs = None
+
+    def __init__(self, templ, fname, strip, opts):
+        """Create a new page.
+
+        @param templ: template dictionary
+        @param fname: full path to page input file
+        @param strip: portion of path to strip from `fname` for deployment
+        @param opts: command line options
+
+        """
+        super(Page, self).__init__()
+
+        self.update(templ)
+
+        self["url"] = re.sub(MKD_PATT, ".html", fname)
+        self["url"] = self["url"][len(strip):].lstrip(os.path.sep)
+        self["url"] = self["url"].replace(os.path.sep, "/")
+
+        self["fname"] = fname
+
+        with codecs.open(fname, 'r', opts.input_enc) as fp:
+            self.raw = fp.readlines()
+
+        # split raw content into macro definitions and real content
+        vardefs = ""
+        self.source = ""
+        for line in self.raw:
+            if not vardefs and self._re_eom.match(line):
+                vardefs = self.source
+                self.source = "" # only macro defs until here, reset source
+            else:
+                self.source += line
+
+        for key, val in self._re_vardef.findall(vardefs):
+            key = key.strip()
+            val = val.strip()
+            val = re.sub(r' *\n +', ' ', val) # clean out line continueation
+            self[key] = val
+
+        basename = os.path.basename(fname)
+
+        fpatt = r'(.+?)(?:\.([0-9]+-[0-9]+-[0-9]+)(?:\.(.*))?)?%s' % MKD_PATT
+        title, date, post = re.match(fpatt, basename).groups()
+        title = title.replace("_", " ")
+        post = post and post.replace("_", " ") or None
+        self["title"] = self.get("title", title)
+        if date and "date" not in self: self["date"] = date
+        if post and "post" not in self: self["post"] = post
+
+        self.html = ""
+
+    def __getattribute__(self, name):
+
+        try:
+            return super(Page, self).__getattribute__(name)
+        except AttributeError, e:
+            if name in self:
+                return self[name]
+            raise e
+
+# -----------------------------------------------------------------------------
+
+def build(project, opts):
+    """Build a site project."""
+
+    # -------------------------------------------------------------------------
+    # utilities
+    # -------------------------------------------------------------------------
+
+    def abort_iex(page, itype, inline, exc):
+        """Abort because of an exception in inlined Python code."""
+        print("error  : Python %s in %s failed" % (itype, page.fname))
+        print((" %s raising the exception " % itype).center(79, "-"))
+        print(inline)
+        print(" exception ".center(79, "-"))
+        print(exc)
+        sys.exit(1)
+
+    # -------------------------------------------------------------------------
+    # regex patterns and replacements
+    # -------------------------------------------------------------------------
+
+    regx_escp = re.compile(r'\\((?:(?:&lt;|<)!--|{)(?:{|%))') # escaped code
+    repl_escp = r'\1'
+    regx_rurl = re.compile(r'(?<=(?:(?:\n| )src|href)=")([^#/&%].*?)(?=")')
+    repl_rurl = lambda m: urlparse.urljoin(opts.base_url, m.group(1))
+
+    regx_eval = re.compile(r'(?<!\\)(?:(?:<!--|{){)(.*?)(?:}(?:-->|}))', re.S)
+
+    def repl_eval(m):
+        """Replace a Python expression block by its evaluation."""
+
+        expr = m.group(1)
+        try:
+            repl = eval(expr, macros.copy())
+        except:
+            abort_iex(page, "expression", expr, traceback.format_exc())
+        else:
+            if not isinstance(repl, basestring): # e.g. numbers
+                repl = unicode(repl)
+            elif not isinstance(repl, unicode):
+                repl = repl.decode("utf-8")
+            return repl
+
+    regx_exec = re.compile(r'(?<!\\)(?:(?:<!--|{)%)(.*?)(?:%(?:-->|}))', re.S)
+
+    def repl_exec(m):
+        """Replace a block of Python statements by their standard output."""
+
+        stmt = m.group(1).replace("\r\n", "\n")
+
+        # base indentation
+        ind_lvl = len(re.findall(r'^(?: *\n)*( *)', stmt, re.MULTILINE)[0])
+        ind_rex = re.compile(r'^ {0,%d}' % ind_lvl, re.MULTILINE)
+        stmt = ind_rex.sub('', stmt)
+
+        # execute
+        sys.stdout = StringIO.StringIO()
+        try:
+            exec stmt in macros.copy()
+        except:
+            sys.stdout = sys.__stdout__
+            abort_iex(page, "statements", stmt, traceback.format_exc())
+        else:
+            repl = sys.stdout.getvalue()[:-1] # remove last line break
+            sys.stdout = sys.__stdout__
+            if not isinstance(repl, unicode):
+                repl = repl.decode(opts.input_enc)
+            return repl
+
+    # -------------------------------------------------------------------------
+    # preparations
+    # -------------------------------------------------------------------------
+
+    dir_in = opj(project, "input")
+    dir_out = opj(project, "output")
+    page_html = opj(project, "page.html")
+
+    # check required files and folders
+    for pelem in (page_html, dir_in, dir_out):
+        if not opx(pelem):
+            print("error  : %s does not exist, looks like project has not been "
+                  "initialized, abort" % pelem)
+            sys.exit(1)
+
+    # prepare output directory
+    for fod in glob.glob(opj(dir_out, "*")):
+        if os.path.isdir(fod):
+            shutil.rmtree(fod)
+        else:
+            os.remove(fod)
+    if not opx(dir_out):
+        os.mkdir(dir_out)
+
+    # macro module
+    class nomod: pass # dummy module, something we can set attributes on
+    fname = opj(opts.project, "macros.py")
+    macros = {"__encoding__": opts.output_enc}
+    macmod = opx(fname) and imp.load_source("macros", fname) or nomod
+    setattr(macmod, "options", opts)
+    setattr(macmod, "project", project)
+    setattr(macmod, "input", dir_in)
+    setattr(macmod, "output", dir_out)
+    for attr in dir(macmod):
+        if not attr.startswith("_"):
+            macros[attr] = getattr(macmod, attr)
+
+    # -------------------------------------------------------------------------
+    # process input files
+    # -------------------------------------------------------------------------
+
+    pages = []
+    page_global = macros.get("page", {})
+    for cwd, dirs, files in os.walk(dir_in.decode(opts.filename_enc)):
+        cwd_site = cwd[len(dir_in):].lstrip(os.path.sep)
+        for sdir in dirs[:]:
+            if re.search(opts.ignore, opj(cwd_site, sdir)):
+                dirs.remove(sdir)
+            else:
+                os.mkdir(opj(dir_out, cwd_site, sdir))
+        for f in files:
+            if re.search(opts.ignore, opj(cwd_site, f)):
+                pass
+            elif re.search(MKD_PATT, f):
+                page = Page(page_global, opj(cwd, f), dir_in, opts)
+                pages.append(page)
+            else:
+                shutil.copy(opj(cwd, f), opj(dir_out, cwd_site))
+
+    pages.sort(key=lambda p: int(p.get("sval", "0")))
+
+    macros["pages"] = pages
+    macmod.pages = pages
+
+    # -------------------------------------------------------------------------
+    # run pre-convert hooks in macro module (named 'once' before)
+    # -------------------------------------------------------------------------
+
+    hooks = [a for a in dir(macmod) if re.match(r'hook_preconvert_|once_', a)]
+    for fn in sorted(hooks):
+        getattr(macmod, fn)()
+
+    # -------------------------------------------------------------------------
+    # convert pages (markdown to HTML)
+    # -------------------------------------------------------------------------
+
+    for page in pages:
+
+        print("info   : convert %s" % page.fname)
+
+        # replace expressions and statements in page source
+        macmod.page = page
+        macros["page"] = page
+        out = regx_eval.sub(repl_eval, page.source)
+        out = regx_exec.sub(repl_exec, out)
+
+        # convert to HTML
+        page.html = markdown.Markdown(extensions=opts.md_ext).convert(out)
+
+    # -------------------------------------------------------------------------
+    # run post-convert hooks in macro module
+    # -------------------------------------------------------------------------
+
+    hooks = [a for a in dir(macmod) if a.startswith("hook_postconvert_")]
+    for fn in sorted(hooks):
+        getattr(macmod, fn)()
+
+    # -------------------------------------------------------------------------
+    # render complete HTML pages
+    # -------------------------------------------------------------------------
+
+    with codecs.open(opj(project, "page.html"), 'r', opts.input_enc) as fp:
+        skeleton = fp.read()
+
+    for page in pages:
+
+        print("info   : render %s" % page.url)
+
+        # replace expressions and statements in page.html
+        macmod.page = page
+        macros["page"] = page
+        macros["__content__"] = page.html
+        out = regx_eval.sub(repl_eval, skeleton)
+        out = regx_exec.sub(repl_exec, out)
+
+        # un-escape escaped python code blocks
+        out = regx_escp.sub(repl_escp, out)
+
+        # make relative links absolute
+        out = regx_rurl.sub(repl_rurl, out)
+
+        # write HTML page
+        fname = page.fname.replace(dir_in, dir_out)
+        fname = re.sub(MKD_PATT, ".html", fname)
+        with codecs.open(fname, 'w', opts.output_enc) as fp:
+            fp.write(out)
+
+    print("success: built project")
+
+# =============================================================================
+# serve site
+# =============================================================================
+
+def serve(project, port):
+    """Temporary serve a site project."""
+
+    root = opj(project, "output")
+    if not os.listdir(project):
+        print("error  : output dir is empty (build project first!), abort")
+        sys.exit(1)
+
+    os.chdir(root)
+    server = HTTPServer(('', port), SimpleHTTPRequestHandler)
+    server.serve_forever()
+
+# =============================================================================
+# options
+# =============================================================================
+
+def options():
+    """Parse and validate command line arguments."""
+
+    usage = ("Usage: %prog --init [path/to/project]\n"
+             "       %prog --build [OPTIONS] [path/to/project]\n"
+             "       %prog --serve [OPTIONS] [path/to/project]\n"
+             "\n"
+             "       Project path is optional, '.' is used as default.")
+
+    op = optparse.OptionParser(usage=usage)
+
+    op.add_option("-i" , "--init", action="store_true", default=False,
+                  help="init project")
+    op.add_option("-b" , "--build", action="store_true", default=False,
+                  help="build project")
+    op.add_option("-s" , "--serve", action="store_true", default=False,
+                  help="serve project")
+
+    og = optparse.OptionGroup(op, "Build options")
+    og.add_option("", "--base-url", default="/", metavar="URL",
+                  help="base url for relative links (default: /)")
+    og.add_option("" , "--ignore", default=r"^\.|~$", metavar="REGEX",
+                  help="input files to ignore (default: '^\.|~$')")
+    og.add_option("" , "--md-ext", default=[], metavar="EXT",
+                  action="append", help="enable a markdown extension")
+    og.add_option("", "--input-enc", default="utf-8", metavar="ENC",
+                  help="encoding of input pages (default: utf-8)")
+    og.add_option("", "--output-enc", default="utf-8", metavar="ENC",
+                  help="encoding of output pages (default: utf-8)")
+    og.add_option("", "--filename-enc", default="utf-8", metavar="ENC",
+                  help="encoding of file names (default: utf-8)")
+    op.add_option_group(og)
+
+    og = optparse.OptionGroup(op, "Serve options")
+    og.add_option("" , "--port", default=8080,
+                  metavar="PORT", type="int",
+                  help="port for serving (default: 8080)")
+    op.add_option_group(og)
+
+    opts, args = op.parse_args()
+
+    if opts.init + opts.build + opts.serve < 1:
+        op.print_help()
+        op.exit()
+
+    opts.project = args and args[0] or "."
+
+    return opts
+
+# =============================================================================
+# main
+# =============================================================================
+
+def main():
+
+    opts = options()
+
+    if opts.init:
+        init(opts.project)
+    if opts.build:
+        build(opts.project, opts)
+    if opts.serve:
+        serve(opts.project, opts.port)
+
+if __name__ == '__main__':
+
+    main()

File site/input/images/six.png

Added
New image

File site/input/index.mkd

+title: zserge's home page: as simple as it should be
+keywords: software, minimalism, linux, embedded, avr, android, golang, KISS
+
+---
+
+zserge's home page
+==================
+
+Hi! I'm Serge. I'm a programmer.
+
+I like
+------
+
+I like: simplicity, unix, C, embedded systems, android, Go language. 
+
+I also like: music, retro computers and games, esoteric programming languages.
+But most of all I like my girlfriend, Anna.
+
+Projects
+--------
+
+It would be great if my works would be useful to someone.
+They are free, open source, and very simple:
+
+* [jsmn](jsmn.html) - JSON parser for small systems
+  [[repo](http://bitbucket.org/zserge/jsmn)]
+* [six](six.html) - very simple IRC client 
+  [[repo](http://bitbucket.org/zserge/six)]
+* gotftp - basic TFTP server written in Go
+	[[repo](http://bitbucket.org/zserge/gotftp)]
+* j1vm - J1 Forth CPU simulator and compiler 
+  [[repo](http://bitbucket.org/zserge/j1vm)]
+* [nikl](nikl.html) - micro-framework for programming embedded systems 
+  [[repo](http://bitbucket.org/zserge/nikl)]
+* [ivm](ivm.html) - simple forth virtual machine (a part of [nikl](nikl.html)) 
+  [[repo](http://bitbucket.org/zserge/nikl)]
+
+Contact
+-------
+
+I'm not a social guy, but 
+
+* You can email me at google mail: zaitsev.serge
+* Or follow on twitter: [@zsergo](http://twitter.com/zsergo)
+* Or read my [blog](http://zserge.wordpress.com) (in russian)
+

File site/input/j1vm.mkd

+title: Tiny virtual machine for Forth language
+keywords: virtual machine, vm, forth, interpreter, compiler, assembler, stack, embedded, small, retro
+
+
+----
+
+IVM
+===
+
+IVM (IV Machine or Forth Machine) is a very simple virtual machine
+for small embedded devices. It's a software implementation of
+[J1 Forth CPU](http://http://excamera.com/sphinx/fpga-j1.html) and is binary
+compatible with it.
+IVM is a stack machine and is designed to run Forth code, but
+can be used as a general-purpose VM as well.
+
+HOW CAN I USE IT?
+-----------------
+
+There can be various scenarios, but in general if you:
+
+* want to customize your software without reprogramming the firmware
+* want to isolate and control your software
+* have enough storage to put virtual machine there (it's about 1K!)
+* have storage for the software running inside virtual machine (it's usually 
+  more compact than native code)
+* can write critical parts in native code and map them to VM memory-mapped I/O
+* can forgive than it run 10 times slower than native code
+
+...then you should try running IVM.
+
+DESIGN
+------
+
+IVM is designed to be as small as possible (less than 1K) to fit even the
+smallest micros, and it should bring them to the new level by possibility of
+running external code.
+
+IVM is very flexible and easy to customize.
+
+IVM code is stored in the "/inc/ivm.h" file. It has two functions:
+
+- ivm_reset() - brings the VM back to the default state
+- ivm_step(op) - executes instruction "op" on the virtual machine
+
+You need to implement some functionality by your own:
+
+- Fetching instructions. Each instruction is a 2-byte word. Instruction 
+index is stored in the `ivm_pc` variable
+- Memory access. You must implement `ivm_mem_put` and `ivm_mem_get` functions
+to read and write to the memory. It is not a part of IVM code, because you can
+use your I/O ports in a memory mapped way, or you might want to cache some
+address range if you use external memory chip. Also, you decide what is the amount
+of RAM available to the VM.
+
+INSTRUCTION SET
+---------------
+
+IVM has a simple instruction set compatible with the J1 Forth CPU.
+
+IVM has two stacks - data stack (DS) and return stack (RS).  Default depth for
+both of them is 16 levels (**WARNING: Original J1 has 32 levels!**)
+
+Basically, there are 5 types of the instructions:
+
+- LIT: put a number on the DS
+
+- JMP: jump to address
+
+- JZ: jump to address if the value on the top of the DS is zero. 
+This instruction also deletes this value from the top of the DS.
+
+- CALL: store current address to the RS and jump to the address.
+This instruction makes it possible to implement functions.
+
+- ALU: perform ariphmetic operation
+
+There are 16 types of ALU instructions, each of them can also manipulate
+DS and RS stacks, change PC and work with memory. For more details
+check the J1 project.
+
+COMPILER
+--------
+
+If you know Forth, you can use crosscompiler from the J1 project.
+
+ALternatively, there is a separate project of developer tools for
+J1 CPU - [j1vm](http://bitbucket.org/zserge/j1vm)
+
+There is a Forth compiler `j1c` and a simulator (`j1vm`).
+
+IVM FORTH BASICS
+----------------
+
+**Syntax**
+
+Forth has reverse Polish notation. So, to add two number you should
+write `2 3 +` and to add three numbers - `1 2 3 + +`.
+
+This is so, because Forth program is executed from left to right, so if you
+write `open read close` it will mean firth open, then read and finally close.
+This sounds more intuitive, right?
+
+Comments in Forth are like:
+
+	( this is a comment )
+	\ this is a single line comment
+
+**Stacks**
+
+*IVM has two stacks, so how do they work?*
+
+Data stack is a place where temporary values are stored and where
+all the calculations happen. So, if you write `2 3 +` it means:
+"put 2 on the top of the data stack, then put 3 over it, then run
+ALU function +". Function "+" fetches two items from the top of
+the stack, add them, and puts the result on the data stack as well,
+replacing that two items. So, if stack was like "1 2 3" before 
+calling "+", after that the stack will be "1 5".
+
+There is a specific notation that helps you to know how functions
+manipulate data stack. It's written like a comment. This is how
+we would describe "+" function: `( a b -- a+b )`. See? There were 
+`a` and `b` on the top, but we replace them with `a+b` sum.
+
+How would you call this function: `( a b -- b a )`? Right, it's `swap`.
+And this one: `( a -- a a )` is `dup`, because it duplicates the top item.
+And this one: `( a -- )` is `drop`, because it drops the top item from
+the stack.
+
+*But why there are two stacks?*
+
+The second stack is a return stack, but it does more than storing
+return addresses when calling functions. You can put your local values
+there when playing with data stack, and fetch them later.
+
+Whan if you need a function like `( a b c d -- a+b c+d )`?
+First you call `+` and get `( a b c+d )`. But now you need to 
+remove that `c+d` from the top of the stack! You can move that item
+to the return stack. Use functions `>r` and `r>` to do that.
+The first one is "put-to-return-stack" and the second one is
+"fetch-from-return-stack", but it's obvious because of the arrow
+position. So, this is your code for the function above:
+
+	( a b c d -- a+b c+d)
+	+ ( a b c+d )
+	>r ( a b )
+	+ ( a+b )
+	r>  ( a+b c+d )
+
+**Memory**
+
+If you need to store global variables, you can use memory-access functions: 
+`@` and `!`. They are "fetch" and "store" 
+
+It means, that `100 @` fetches 16-bit value from address 100, and `5 100 !` writes value 5 to address 100. At this moment variables and constants become handy.
+
+Both variables and constants differ from what you normally see in Forth. To make a global variable you write:
+
+	var my-var
+
+This allocates new variable address in RAM. If you need to make a constant definition write:
+
+	equ X 10