1190 lines
No EOL
97 KiB
HTML
1190 lines
No EOL
97 KiB
HTML
<!DOCTYPE html>
|
|
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='http://stevelosh.com/static/css/style.css' rel='stylesheet' type='text/css' /><link href='http://stevelosh.com/static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>CHIP-8 in Common Lisp: The CPU / Steve Losh</title></head><body><header><a id='logo' href='http://stevelosh.com/'>Steve Losh</a><nav><a href='http://stevelosh.com/blog/'>Blog</a> - <a href='http://stevelosh.com/projects/'>Projects</a> - <a href='http://stevelosh.com/photography/'>Photography</a> - <a href='http://stevelosh.com/links/'>Links</a> - <a href='http://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>CHIP-8 in Common Lisp: The CPU</a></h1><p class='date'>Posted on December 19th, 2016.</p><p>A while back I decided to try to write a Game Boy emulator in Common Lisp based
|
|
on <a href="http://imrannazar.com/GameBoy-Emulation-in-JavaScript">this series of articles</a>. I made some good progress but eventually
|
|
got bogged down because I was trying to learn a bunch of complex new things at
|
|
once:</p>
|
|
|
|
<ul>
|
|
<li>How to write an emulator</li>
|
|
<li>How to use Qt with Common Lisp</li>
|
|
<li>How the Game Boy works internally</li>
|
|
</ul>
|
|
|
|
<p>Instead of dragging on, I decided to take a break and try something simpler:
|
|
a <a href="https://en.wikipedia.org/wiki/CHIP-8">CHIP-8</a> emulator/interpreter. The CHIP-8 is much simpler than the Game
|
|
Boy, which made it easier to experiment with the rest of the infrastructure.</p>
|
|
|
|
<p>In this post and a couple of future ones I'll walk through all of my CHIP-8
|
|
emulator implementation. To give you a rough idea of the size of the project,
|
|
<code>cloc</code> reports:</p>
|
|
|
|
<ul>
|
|
<li>Basic emulator: 415 lines</li>
|
|
<li>Debugging/disassembling infrastructure: 142 lines</li>
|
|
<li>Screen GUI: 153 lines</li>
|
|
<li>Graphical debugger: 295 lines</li>
|
|
</ul>
|
|
|
|
<p>This first post will deal with emulating the CHIP-8's CPU.</p>
|
|
|
|
<p>The full series of posts so far:</p>
|
|
|
|
<ol>
|
|
<li><a href="index.html">CHIP-8 in Common Lisp: The CPU</a></li>
|
|
<li><a href="../chip8-graphics/index.html">CHIP-8 in Common Lisp: Graphics</a></li>
|
|
<li><a href="../chip8-input/index.html">CHIP-8 in Common Lisp: Input</a></li>
|
|
<li><a href="../chip8-sound/index.html">CHIP-8 in Common Lisp: Sound</a></li>
|
|
<li><a href="../../../2017/01/chip8-disassembly/index.html">CHIP-8 in Common Lisp: Disassembly</a></li>
|
|
<li><a href="../../../2017/01/chip8-debugging-infrastructure/index.html">CHIP-8 in Common Lisp: Debugging Infrastructure</a></li>
|
|
<li><a href="../../../2017/01/chip8-menus/index.html">CHIP-8 in Common Lisp: Menus</a></li>
|
|
</ol>
|
|
|
|
<p>The full emulator source is on <a href="https://bitbucket.org/sjl/cl-chip8">BitBucket</a> and <a href="https://github.com/sjl/cl-chip8">GitHub</a>.</p>
|
|
|
|
<ol class="table-of-contents"><li><a href="index.html#s1-libraries">Libraries</a></li><li><a href="index.html#s2-chip-8-references">CHIP-8 References</a></li><li><a href="index.html#s3-the-main-data-structure">The Main Data Structure</a><ol><li><a href="index.html#s4-registers">Registers</a></li><li><a href="index.html#s5-the-stack">The Stack</a></li><li><a href="index.html#s6-memory">Memory</a></li><li><a href="index.html#s7-currently-loaded-rom">Currently-Loaded ROM</a></li><li><a href="index.html#s8-the-flag-register">The Flag Register</a></li></ol></li><li><a href="index.html#s9-removing-tedium">Removing Tedium</a></li><li><a href="index.html#s10-infrastructure">Infrastructure</a><ol><li><a href="index.html#s11-resetting">Resetting</a></li><li><a href="index.html#s12-loading-roms">Loading ROMs</a></li><li><a href="index.html#s13-the-main-loop-s">The Main Loop(s)</a></li><li><a href="index.html#s14-individual-cycles">Individual Cycles</a></li><li><a href="index.html#s15-instruction-dispatch">Instruction Dispatch</a></li></ol></li><li><a href="index.html#s16-instructions">Instructions</a><ol><li><a href="index.html#s17-random-numbers">Random Numbers</a></li><li><a href="index.html#s18-define-instruction">Define-Instruction</a></li><li><a href="index.html#s19-jumps-and-calls">Jumps and Calls</a></li><li><a href="index.html#s20-binary-coded-decimal">Binary-Coded Decimal</a></li><li><a href="index.html#s21-arithmetic">Arithmetic</a></li><li><a href="index.html#s22-shifting">Shifting</a></li><li><a href="index.html#s23-logical-operations">Logical Operations</a></li><li><a href="index.html#s24-macro-map">Macro-Map</a></li><li><a href="index.html#s25-branching">Branching</a></li><li><a href="index.html#s26-loads">Loads</a></li></ol></li><li><a href="index.html#s27-future">Future</a></li></ol>
|
|
|
|
<h2 id="s1-libraries"><a href="index.html#s1-libraries">Libraries</a></h2>
|
|
|
|
<p>The emulator uses a few Common Lisp libraries to make things easier:</p>
|
|
|
|
<ul>
|
|
<li><a href="https://common-lisp.net/project/bordeaux-threads/">bordeaux-threads</a> to handle threading.</li>
|
|
<li><a href="https://github.com/nightfly19/cl-arrows">cl-arrows</a> for an implementation of Clojure's <code>-<></code> threading macro.</li>
|
|
<li><a href="https://github.com/sjl/cl-losh">cl-losh</a> is my own personal utility library.</li>
|
|
<li><a href="https://filonenko-mikhail.github.io/cl-portaudio/">cl-portaudio</a> for audio.</li>
|
|
<li><a href="https://common-lisp.net/project/iterate/">iterate</a> for a much nicer version of <code>loop</code> called <code>iterate</code>. My utility
|
|
library contains several <code>iterate</code> drivers (some of which I've written about
|
|
before).</li>
|
|
<li><a href="https://shinmera.github.io/qtools/">qtools</a> to handle creating a GUI with Qt.</li>
|
|
<li><a href="https://github.com/tarballs-are-good/quickutil">quickutil</a> for some miscellaneous utility functions from Alexandria and
|
|
elsewhere.</li>
|
|
</ul>
|
|
|
|
<p>I'll try to remember to mention whenever I use a function that's not built-in to
|
|
Common Lisp, but I might forget, in which case it's probably in one of these.</p>
|
|
|
|
<h2 id="s2-chip-8-references"><a href="index.html#s2-chip-8-references">CHIP-8 References</a></h2>
|
|
|
|
<p>There's a good amount of information available about the CHIP-8 online. The
|
|
references I used most often were:</p>
|
|
|
|
<ul>
|
|
<li><a href="http://devernay.free.fr/hacks/chip8/C8TECH10.HTM">Cowgod's "CHIP-8 Technical Reference"</a></li>
|
|
<li><a href="http://www.multigesture.net/articles/how-to-write-an-emulator-CHIP-8-interpreter/">Laurence Muller's "How to write an emulator (CHIP-8 interpreter)"</a></li>
|
|
<li><a href="http://mattmik.com/files/chip8/mastering/chip8.html">Matthew Mikolay's "Mastering CHIP-8"</a></li>
|
|
<li><a href="https://github.com/AfBu/haxe-CHIP-8-emulator/wiki/\(Super\)CHIP-8-Secrets">(Super)CHIP 8 Secrets</a></li>
|
|
</ul>
|
|
|
|
<h2 id="s3-the-main-data-structure"><a href="index.html#s3-the-main-data-structure">The Main Data Structure</a></h2>
|
|
|
|
<p>Let's dive into the code. We'll start with the main data structure that will
|
|
hold an instance of a CHIP-8 for emulation:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defstruct</span></i> chip
|
|
<span class="paren2">(<span class="code">running t <span class="keyword">:type</span> boolean</span>)</span>
|
|
<span class="comment">; ...more to come later
|
|
</span> </span>)</span></span></code></pre>
|
|
|
|
<p>We're using a Lisp struct instead of a CLOS class because we're going to be
|
|
accessing the fields of this thing <em>a lot</em> and CLOS accessors can be
|
|
comparatively slow. This is one of the very few concessions we'll make to
|
|
performance.</p>
|
|
|
|
<p>The first field is <code>running</code>, which is just a boolean that represents whether
|
|
the emulator is currently running.</p>
|
|
|
|
<p>We'll eventually have multiple threads poking at this struct, and they'll
|
|
generally be doing something like <code>(loop :while (chip-running chip) :do ...)</code>.
|
|
That way when we want to quit the emulator we can just set <code>running</code> to <code>nil</code>
|
|
and everything will (eventually) stop.</p>
|
|
|
|
<h3 id="s4-registers"><a href="index.html#s4-registers">Registers</a></h3>
|
|
|
|
<p>The CHIP-8 has sixteen main registers, as well as a few other special ones:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defstruct</span></i> chip
|
|
<span class="comment">; ...
|
|
</span> <span class="paren2">(<span class="code">registers <span class="paren3">(<span class="code">make-array 16 <span class="keyword">:element-type</span> 'int8</span>)</span>
|
|
<span class="keyword">:type</span> <span class="paren3">(<span class="code">simple-array int8 <span class="paren4">(<span class="code">16</span>)</span></span>)</span>
|
|
<span class="keyword">:read-only</span> t</span>)</span>
|
|
<span class="paren2">(<span class="code">index 0 <span class="keyword">:type</span> int16</span>)</span>
|
|
<span class="paren2">(<span class="code">program-counter #x200 <span class="keyword">:type</span> int12</span>)</span>
|
|
<span class="comment">; ...
|
|
</span> </span>)</span></span></code></pre>
|
|
|
|
<p>We'll keep the main registers in an array, and the others will just be separate
|
|
slots. I've added type declarations in the struct for two reasons:</p>
|
|
|
|
<ul>
|
|
<li>With a high <code>safety</code> declaration many implementations (including SBCL, the one
|
|
I'm using) will type check at runtime to make sure we're setting things
|
|
appropriately.</li>
|
|
<li>With low <code>safety</code> and high <code>speed</code> declarations, SBCL can generate much faster
|
|
code if it knows the types of the struct slots.</li>
|
|
</ul>
|
|
|
|
<p>To make the integer types a bit less wordy I've defined some simple synonyms:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">deftype</span></i> int4 <span class="paren2">(<span class="code"></span>)</span> '<span class="paren2">(<span class="code">unsigned-byte 4</span>)</span></span>)</span>
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">deftype</span></i> int8 <span class="paren2">(<span class="code"></span>)</span> '<span class="paren2">(<span class="code">unsigned-byte 8</span>)</span></span>)</span>
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">deftype</span></i> int12 <span class="paren2">(<span class="code"></span>)</span> '<span class="paren2">(<span class="code">unsigned-byte 12</span>)</span></span>)</span>
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">deftype</span></i> int16 <span class="paren2">(<span class="code"></span>)</span> '<span class="paren2">(<span class="code">unsigned-byte 16</span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>So <code>registers</code> is a 16-element simple array of <code>(unsigned-byte 8)</code>s. It's
|
|
<code>read-only</code> because we'll be changing the <em>elements</em> of the array, but we should
|
|
never be swapping out the entire array itself.</p>
|
|
|
|
<p>The index register is 16 bits, and the program counter can store up to 12 bits
|
|
(we'll never need more than that because of the size of the CHIP-8's memory).</p>
|
|
|
|
<p>Also note that the program counter starts at address <code>#x200</code>, because that's
|
|
where the ROM data eventually gets loaded into the CHIP-8 memory.</p>
|
|
|
|
<h3 id="s5-the-stack"><a href="index.html#s5-the-stack">The Stack</a></h3>
|
|
|
|
<p>The CHIP-8 also has an internal stack to store return addresses when calling
|
|
procedures. We'll just use a Lisp vector with a fill pointer for this:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defstruct</span></i> chip
|
|
<span class="comment">; ...
|
|
</span> <span class="paren2">(<span class="code">stack <span class="paren3">(<span class="code">make-array 16 <span class="keyword">:element-type</span> 'int12 <span class="keyword">:fill-pointer</span> 0</span>)</span>
|
|
<span class="keyword">:type</span> <span class="paren3">(<span class="code">vector int12 16</span>)</span>
|
|
<span class="keyword">:read-only</span> t</span>)</span>
|
|
<span class="comment">; ...
|
|
</span> </span>)</span></span></code></pre>
|
|
|
|
<h3 id="s6-memory"><a href="index.html#s6-memory">Memory</a></h3>
|
|
|
|
<p>The CHIP-8 has 4 kilobytes of main memory:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defconstant</span></i> +memory-size+ <span class="paren2">(<span class="code">* 1024 4</span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defstruct</span></i> chip
|
|
<span class="comment">; ...
|
|
</span> <span class="paren2">(<span class="code">memory <span class="paren3">(<span class="code">make-array +memory-size+ <span class="keyword">:element-type</span> 'int8</span>)</span>
|
|
<span class="keyword">:type</span> <span class="paren3">(<span class="code">simple-array int8 <span class="paren4">(<span class="code">#.+memory-size+</span>)</span></span>)</span>
|
|
<span class="keyword">:read-only</span> t</span>)</span>
|
|
<span class="comment">; ...
|
|
</span> </span>)</span></span></code></pre>
|
|
|
|
<p>Pretty simple. Note the <code>#.</code> reader macro trick/hack to be able to use
|
|
a variable where we normally need a raw type specifier.</p>
|
|
|
|
<h3 id="s7-currently-loaded-rom"><a href="index.html#s7-currently-loaded-rom">Currently-Loaded ROM</a></h3>
|
|
|
|
<p>Finally we'll add a slot for keeping track of the path to the currently-loaded
|
|
ROM, for easy resetting later:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defstruct</span></i> chip
|
|
<span class="comment">; ...
|
|
</span> <span class="paren2">(<span class="code">loaded-rom nil <span class="keyword">:type</span> <span class="paren3">(<span class="code">or null string</span>)</span></span>)</span>
|
|
<span class="comment">; ...
|
|
</span> </span>)</span></span></code></pre>
|
|
|
|
<p>That's it for now. We'll need a few more slots once we get to things like
|
|
graphics and sound, but I'll introduce them when we need them.</p>
|
|
|
|
<h3 id="s8-the-flag-register"><a href="index.html#s8-the-flag-register">The Flag Register</a></h3>
|
|
|
|
<p>Register number 15, or <code>#xF</code> in hex, is special. It's nicknamed the "flag"
|
|
register and gets set specially by certain instructions. We could just access
|
|
it like the rest of the registers in our code:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">setf <span class="paren2">(<span class="code">aref <span class="paren3">(<span class="code">chip-registers chip</span>)</span> 15</span>)</span> 1</span>)</span> <span class="comment">; set flag to 1
|
|
</span><span class="paren1">(<span class="code">print <span class="paren2">(<span class="code">aref <span class="paren3">(<span class="code">chip-registers chip</span>)</span> #xF</span>)</span></span>)</span> <span class="comment">; print the flag</span></span></code></pre>
|
|
|
|
<p>But even with the <code>#xF</code> hex index to get that mnemonic "F" this is a bit too
|
|
hard to read. Let's define some extra reader and writer functions to clean
|
|
things up:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> chip-flag <span class="paren2">(<span class="code">chip</span>)</span>
|
|
<span class="paren2">(<span class="code">aref <span class="paren3">(<span class="code">chip-registers chip</span>)</span> #xF</span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> <span class="paren2">(<span class="code">setf chip-flag</span>)</span> <span class="paren2">(<span class="code">new-value chip</span>)</span>
|
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">aref <span class="paren4">(<span class="code">chip-registers chip</span>)</span> #xF</span>)</span> new-value</span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p><a href="https://github.com/sjl/cl-losh/blob/master/DOCUMENTATION.markdown#defun-inline-macro"><code>defun-inline</code></a> is from my utility library — it just <code>defun</code>s the
|
|
function and <code>declaim</code>s it inline all in one step. Now things are much nicer:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">setf <span class="paren2">(<span class="code">chip-flag chip</span>)</span> 1</span>)</span> <span class="comment">; set flag to 1
|
|
</span><span class="paren1">(<span class="code">print <span class="paren2">(<span class="code">chip-flag chip</span>)</span></span>)</span> <span class="comment">; print the flag</span></span></code></pre>
|
|
|
|
<h2 id="s9-removing-tedium"><a href="index.html#s9-removing-tedium">Removing Tedium</a></h2>
|
|
|
|
<p>What we've got so far <em>works</em>, but I want to add one more piece of syntactic
|
|
sugar before moving on.</p>
|
|
|
|
<p>We're going to be accessing the slots of the <code>chip</code> struct a <em>lot</em>, and it's
|
|
going to get tedious to write <code>(chip-SLOT chip)</code> over and over again. For
|
|
example, when we're resetting the emulator we'll have to do something like this:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> reset <span class="paren2">(<span class="code">chip</span>)</span>
|
|
<span class="paren2">(<span class="code">fill <span class="paren3">(<span class="code">chip-memory chip</span>)</span> 0</span>)</span>
|
|
<span class="paren2">(<span class="code">fill <span class="paren3">(<span class="code">chip-registers chip</span>)</span> 0</span>)</span>
|
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">chip-running chip</span>)</span> t
|
|
<span class="paren3">(<span class="code">chip-program-counter chip</span>)</span> #x200
|
|
<span class="paren3">(<span class="code">fill-pointer <span class="paren4">(<span class="code">chip-stack chip</span>)</span></span>)</span> 0</span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This is annoying. Languages like Javascript and Python use <code>.</code> for slot
|
|
access, so it ends up being a bit more concise: <code>chip.memory()</code> instead of
|
|
<code>(chip-memory chip)</code>.</p>
|
|
|
|
<p>We could use Lisp's <code>with-accessors</code> to clean up the actual usage a bit:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> reset <span class="paren2">(<span class="code">chip</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">with-accessors</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">memory chip-memory</span>)</span>
|
|
<span class="paren4">(<span class="code">registers chip-registers</span>)</span>
|
|
<span class="paren4">(<span class="code">running chip-running</span>)</span>
|
|
<span class="paren4">(<span class="code">program-counter chip-program-counter</span>)</span>
|
|
<span class="paren4">(<span class="code">stack chip-stack</span>)</span></span>)</span>
|
|
chip
|
|
<span class="paren3">(<span class="code">fill memory 0</span>)</span>
|
|
<span class="paren3">(<span class="code">fill registers 0</span>)</span>
|
|
<span class="paren3">(<span class="code">setf running t
|
|
program-counter #x200
|
|
<span class="paren4">(<span class="code">fill-pointer stack</span>)</span> 0</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>The <em>usage</em> looks much nicer now (<code>(fill memory 0)</code> is wonderfully readable) but
|
|
we haven't actually fixed anything. We've just shifted all the typing a few
|
|
lines up. But this is Lisp, we can do better!</p>
|
|
|
|
<p>Ideally what we'd like is to be able to do say something like <code>(with-chip (chip)
|
|
...)</code> to mean the giant <code>with-accessors</code> form above:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> reset <span class="paren2">(<span class="code">chip</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren3">(<span class="code">chip</span>)</span>
|
|
<span class="paren3">(<span class="code">fill memory 0</span>)</span>
|
|
<span class="paren3">(<span class="code">fill registers 0</span>)</span>
|
|
<span class="paren3">(<span class="code">setf running t
|
|
program-counter #x200
|
|
<span class="paren4">(<span class="code">fill-pointer stack</span>)</span> 0</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This is nice and readable. Some folks will dislike the fact that it introduces
|
|
new variable bindings that are "hidden" in the macro definition, but I like the
|
|
concision you get from it, especially for a small project like this.</p>
|
|
|
|
<p>We could write <code>with-chip</code> ourselves, if we wanted to. It would look something
|
|
like this:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> <i><span class="symbol">with-chip</span></i> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">chip</span>)</span> &body body</span>)</span>
|
|
`<span class="paren2">(<span class="code"><i><span class="symbol">with-accessors</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">memory chip-memory</span>)</span>
|
|
<span class="paren4">(<span class="code">registers chip-registers</span>)</span>
|
|
<span class="comment">; ...
|
|
</span> </span>)</span>
|
|
,chip
|
|
,@body</span>)</span>`
|
|
</span>)</span></span></code></pre>
|
|
|
|
<p>It's not hard to write, just tedious. But we're using Lisp, so anything tedious
|
|
calls out for abstraction. Another macro from my utility library is
|
|
<a href="https://github.com/sjl/cl-losh/blob/master/DOCUMENTATION.markdown#define-with-macro-macro"><code>define-with-macro</code></a>. This is a macro-defining macro that
|
|
we can use to define <code>with-chip</code> for us:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-with-macro</span></i> chip
|
|
running memory stack registers index program-counter flag</span>)</span></span></code></pre>
|
|
|
|
<p>If the hairiness of a macro-defining macro scares you, don't worry about it. We
|
|
could have just written it by hand as shown above. I've just needed <code>with-FOO</code>
|
|
macros like these often enough that it's been worth it to abstract away the
|
|
tedium of writing them.</p>
|
|
|
|
<p>So one way or another we've got a nice <code>with-chip</code> macro that will let us access
|
|
the fields of our struct with bare names.</p>
|
|
|
|
<h2 id="s10-infrastructure"><a href="index.html#s10-infrastructure">Infrastructure</a></h2>
|
|
|
|
<p>We'll need to define a bit of infrastructure for running things before we jump
|
|
into implementing the CHIP-8 CPU instructions.</p>
|
|
|
|
<h3 id="s11-resetting"><a href="index.html#s11-resetting">Resetting</a></h3>
|
|
|
|
<p>Before we can emulate a ROM, we need to read it into our memory array. I've
|
|
chosen to do this in the <code>reset</code> function so we can just <code>(reset my-chip)</code> to
|
|
reload everything at once — it wouldn't make much sense to load a new ROM
|
|
<em>without</em> resetting the rest of the emulation state (though the results could
|
|
be... "interesting").</p>
|
|
|
|
<p><code>reset</code> looks like this:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> reset <span class="paren2">(<span class="code">chip</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren3">(<span class="code">chip</span>)</span>
|
|
<span class="paren3">(<span class="code">fill memory 0</span>)</span>
|
|
<span class="paren3">(<span class="code">fill registers 0</span>)</span>
|
|
<span class="paren3">(<span class="code">replace memory <span class="paren4">(<span class="code">read-file-into-byte-vector loaded-rom</span>)</span>
|
|
<span class="keyword">:start1</span> #x200</span>)</span>
|
|
<span class="paren3">(<span class="code">setf running t
|
|
program-counter #x200
|
|
<span class="paren4">(<span class="code">fill-pointer stack</span>)</span> 0</span>)</span></span>)</span>
|
|
<span class="paren2">(<span class="code">values</span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This is pretty self explanatory, except for the actual ROM-loading bit:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">replace memory <span class="paren2">(<span class="code">read-file-into-byte-vector loaded-rom</span>)</span>
|
|
<span class="keyword">:start1</span> #x200</span>)</span></span></code></pre>
|
|
|
|
<p><code>replace</code> is just the standard Common Lisp <code>replace</code> function that copies the
|
|
contents of one sequence into another. <code>read-file-into-byte-vector</code> is from
|
|
Alexandria, and will just read the file at <code>loaded-rom</code> into a byte vector.
|
|
Then all we need to do is say that we want to start the copying at index <code>#x200</code>
|
|
in the destination, because that's where the CHIP-8 ROM data is supposed start
|
|
(there's other internal data (like font sprites) before it, which we'll see
|
|
later).</p>
|
|
|
|
<p>One last thing to note is that <code>reset</code> just returns <code>(values)</code>, i.e. it returns
|
|
nothing at all. This is something I sometimes do for functions I call at the
|
|
REPL for side effects, to avoid returning a meaningless result. I think of it
|
|
as an application of <a href="http://www.linfo.org/rule_of_silence.html">The Rule of Silence</a> for Lisp.</p>
|
|
|
|
<h3 id="s12-loading-roms"><a href="index.html#s12-loading-roms">Loading ROMs</a></h3>
|
|
|
|
<p>Now that we've got a reset function, it's trivial to define a function to load
|
|
a new ROM into our emulator:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> load-rom <span class="paren2">(<span class="code">chip filename</span>)</span>
|
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">chip-loaded-rom chip</span>)</span> filename</span>)</span>
|
|
<span class="paren2">(<span class="code">reset chip</span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>I didn't bother with the <code>with-chip</code> macro here because we're only accessing
|
|
a single field.</p>
|
|
|
|
<h3 id="s13-the-main-loop-s"><a href="index.html#s13-the-main-loop-s">The Main Loop(s)</a></h3>
|
|
|
|
<p>Now let's wrap things up into a nice interface. We'll define a top-level <code>run</code>
|
|
function that will be what we call to fire up the emulator:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defparameter</span></i> <span class="special">*c*</span> nil</span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> run <span class="paren2">(<span class="code">rom-filename</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">chip <span class="paren5">(<span class="code">make-chip</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">setf <span class="special">*c*</span> chip</span>)</span>
|
|
<span class="paren3">(<span class="code">load-rom chip rom-filename</span>)</span>
|
|
<span class="paren3">(<span class="code">run-cpu chip</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This will get more complicated in the future, but for now it's pretty simple.
|
|
Make a new <code>chip</code> object, load the specified ROM, and start emulating.</p>
|
|
|
|
<p>I've added a <code>*c*</code> global variable that's bound to the currently-running
|
|
emulator so we can poke at it in NREPL or SLIME as it's running.</p>
|
|
|
|
<p>Now to write <code>run-cpu</code>:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defconstant</span></i> +cycles-per-second+ 500</span>)</span>
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defconstant</span></i> +cycles-before-sleep+ 10</span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> run-cpu <span class="paren2">(<span class="code">chip</span>)</span>
|
|
<span class="paren2">(<span class="code">iterate
|
|
<span class="paren3">(<span class="code">while <span class="paren4">(<span class="code">chip-running chip</span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">emulate-cycle chip</span>)</span>
|
|
<span class="paren3">(<span class="code">for tick <span class="keyword">:every-nth</span> +cycles-before-sleep+ <span class="keyword">:do</span>
|
|
<span class="paren4">(<span class="code">sleep <span class="paren5">(<span class="code">/ +cycles-before-sleep+ +cycles-per-second+</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>CHIP-8 was designed to run on much weaker hardware than we have these days.
|
|
Even though we're emulating it, if we just run as fast as possible we'll be
|
|
running <em>far</em> too fast to be playable.</p>
|
|
|
|
<p><code>+cycles-per-second+</code> is a constant describing our ideal emulation speed.
|
|
Anything from 300 to 800 or so is playable, depending on how fast you want games
|
|
to run. We'll just use 500 for now as a happy medium.</p>
|
|
|
|
<p>It would be wasteful to call <code>(sleep)</code> after every single cycle, so instead
|
|
we'll batch them together: run 10 instructions, sleep for a bit, repeat. This
|
|
is the job of the <code>(for ... every-nth N)</code> iterate driver, which is also in my
|
|
utility library. Every 10 iterations through the loop it will sleep for the
|
|
appropriate amount of time.</p>
|
|
|
|
<p>The size of the cycle batches is arbitrary — larger batches will result in fewer
|
|
<code>(sleep)</code> calls, but if you go too large you'll start noticing the emulator
|
|
getting "jumpy". 10-cycle batches at 500 cycles per second means that the
|
|
emulator will sleep for about 1/50 of a second each time, which isn't too
|
|
noticeable.</p>
|
|
|
|
<p>Now we've got the main loop all set up and just need to emulate each individual
|
|
cycle.</p>
|
|
|
|
<h3 id="s14-individual-cycles"><a href="index.html#s14-individual-cycles">Individual Cycles</a></h3>
|
|
|
|
<p>CHIP-8 instructions are each two bytes long, and are stored
|
|
<a href="https://en.wikipedia.org/wiki/Endianness">big-endian</a> in memory. So to emulate
|
|
a single cycle we:</p>
|
|
|
|
<ul>
|
|
<li>Read the two bytes starting at the program counter and concatenate them to get the instruction.</li>
|
|
<li>Advance the program counter (to avoid having to do it inside every single instruction).</li>
|
|
<li>Dispatch to the appropriate instruction code.</li>
|
|
</ul>
|
|
|
|
<p>We'll define <code>emulate-cycle</code> and a couple of helper functions for this:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> chop <span class="paren2">(<span class="code">size integer</span>)</span>
|
|
<span class="paren2">(<span class="code">ldb <span class="paren3">(<span class="code">byte size 0</span>)</span> integer</span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> cat-bytes <span class="paren2">(<span class="code">high-order low-order</span>)</span>
|
|
<span class="paren2">(<span class="code">dpb high-order <span class="paren3">(<span class="code">byte 8 8</span>)</span> low-order</span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> emulate-cycle <span class="paren2">(<span class="code">chip</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren3">(<span class="code">chip</span>)</span>
|
|
<span class="paren3">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren4">(<span class="code"><span class="paren5">(<span class="code">instruction <span class="paren6">(<span class="code">cat-bytes <span class="paren1">(<span class="code">aref memory program-counter</span>)</span>
|
|
<span class="paren1">(<span class="code">aref memory <span class="paren2">(<span class="code">1+ program-counter</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">zapf program-counter <span class="paren5">(<span class="code">chop 12 <span class="paren6">(<span class="code">+ % 2</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">dispatch-instruction chip instruction</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p><code>chop</code> truncates an integer to the given number of bits. <code>cat-bytes</code>
|
|
concatenates two bytes. These are pretty simple, but I prefer the more
|
|
descriptive names over writing <code>dpb</code> (deposit byte) and <code>ldb</code> (load byte)
|
|
everywhere.</p>
|
|
|
|
<p><code>zapf</code> is from my utility library. I've written <a href="../../08/playing-with-syntax/index.html">an entire post</a> about
|
|
it.</p>
|
|
|
|
<p>What happens when the program counter is at the final index into memory? None
|
|
of the CHIP-8 references I found specify what should happen. We could signal an
|
|
error, or just wrap around to 0. I've chosen the latter by chopping <code>(+
|
|
program-counter 2)</code> to 12 bits, but signaling an error would be easy too.</p>
|
|
|
|
<h3 id="s15-instruction-dispatch"><a href="index.html#s15-instruction-dispatch">Instruction Dispatch</a></h3>
|
|
|
|
<p>The CHIP-8's instruction scheme is a bit different than most others that I've
|
|
seen.</p>
|
|
|
|
<p>For systems like the Game Boy instructions have a one- or two-byte opcode, with
|
|
arguments following. For systems like this you can dispatch on opcode by doing
|
|
a giant <code>case</code> statement:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">case opcode
|
|
<span class="paren2">(<span class="code">#x00 <span class="paren3">(<span class="code">op-foo ...</span>)</span></span>)</span>
|
|
<span class="paren2">(<span class="code">#x01 <span class="paren3">(<span class="code">op-bar ...</span>)</span></span>)</span>
|
|
<span class="comment">; ...
|
|
</span> </span>)</span></span></code></pre>
|
|
|
|
<p>But a more efficient way to do it is often to shove all the <code>op-</code> functions into
|
|
an array, and then just use the opcode itself as the index into the array to
|
|
find the function:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">funcall <span class="paren2">(<span class="code">aref opcodes opcode</span>)</span> ...</span>)</span></span></code></pre>
|
|
|
|
<p>But the CHIP-8 differentiates instructions a bit strangely. All instructions
|
|
are two bytes long, <em>including</em> their arguments. Instead of just using the
|
|
first N bits as the opcode, some instructions use a combination of high- and
|
|
low-order bits. For example, the logical <code>AND</code>, <code>OR</code>, and <code>XOR</code> are specified
|
|
by starting the instruction with an <code>8</code> nibble and ending it with <code>1</code>, <code>2</code>, or
|
|
<code>3</code>, with the "arguments" being the two nibbles in the middle:</p>
|
|
|
|
<pre><code>8xy1 - OR Vx, Vy
|
|
8xy2 - AND Vx, Vy
|
|
8xy3 - XOR Vx, Vy
|
|
</code></pre>
|
|
|
|
<p>This makes it rather annoying to use some kind of function table approach.
|
|
There are only a few dozen opcodes, so instead of trying to hack something
|
|
together we'll just use a big old <code>case</code> instead:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> dispatch-instruction <span class="paren2">(<span class="code">chip instruction</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">macrolet</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">call <span class="paren5">(<span class="code">name</span>)</span> `<span class="paren5">(<span class="code">,name chip instruction</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">ecase <span class="paren4">(<span class="code">logand #xF000 instruction</span>)</span>
|
|
<span class="paren4">(<span class="code">#x0000 <span class="paren5">(<span class="code">ecase instruction
|
|
<span class="paren6">(<span class="code">#x00E0 <span class="paren1">(<span class="code">call op-cls</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x00EE <span class="paren1">(<span class="code">call op-ret</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x1000 <span class="paren5">(<span class="code">call op-jp-imm</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x2000 <span class="paren5">(<span class="code">call op-call</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x3000 <span class="paren5">(<span class="code">call op-se-reg-imm</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x4000 <span class="paren5">(<span class="code">call op-sne-reg-imm</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x5000 <span class="paren5">(<span class="code">ecase <span class="paren6">(<span class="code">logand #x000F instruction</span>)</span>
|
|
<span class="paren6">(<span class="code">#x0 <span class="paren1">(<span class="code">call op-se-reg-reg</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x6000 <span class="paren5">(<span class="code">call op-ld-reg<imm</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x7000 <span class="paren5">(<span class="code">call op-add-reg<imm</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x8000 <span class="paren5">(<span class="code">ecase <span class="paren6">(<span class="code">logand #x000F instruction</span>)</span>
|
|
<span class="paren6">(<span class="code">#x0 <span class="paren1">(<span class="code">call op-ld-reg<reg</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x1 <span class="paren1">(<span class="code">call op-or</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x2 <span class="paren1">(<span class="code">call op-and</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x3 <span class="paren1">(<span class="code">call op-xor</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x4 <span class="paren1">(<span class="code">call op-add-reg<reg</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x5 <span class="paren1">(<span class="code">call op-sub-reg<reg</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x6 <span class="paren1">(<span class="code">call op-shr</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x7 <span class="paren1">(<span class="code">call op-subn-reg<reg</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#xE <span class="paren1">(<span class="code">call op-shl</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#x9000 <span class="paren5">(<span class="code">ecase <span class="paren6">(<span class="code">logand #x000F instruction</span>)</span>
|
|
<span class="paren6">(<span class="code">#x0 <span class="paren1">(<span class="code">call op-sne-reg-reg</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#xA000 <span class="paren5">(<span class="code">call op-ld-i<imm</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#xB000 <span class="paren5">(<span class="code">call op-jp-imm+reg</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#xC000 <span class="paren5">(<span class="code">call op-rand</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#xD000 <span class="paren5">(<span class="code">call op-draw</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#xE000 <span class="paren5">(<span class="code">ecase <span class="paren6">(<span class="code">logand #x00FF instruction</span>)</span>
|
|
<span class="paren6">(<span class="code">#x9E <span class="paren1">(<span class="code">call op-skp</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#xA1 <span class="paren1">(<span class="code">call op-sknp</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">#xF000 <span class="paren5">(<span class="code">ecase <span class="paren6">(<span class="code">logand #x00FF instruction</span>)</span>
|
|
<span class="paren6">(<span class="code">#x07 <span class="paren1">(<span class="code">call op-ld-reg<dt</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x0A <span class="paren1">(<span class="code">call op-ld-reg<key</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x15 <span class="paren1">(<span class="code">call op-ld-dt<reg</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x18 <span class="paren1">(<span class="code">call op-ld-st<reg</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x1E <span class="paren1">(<span class="code">call op-add-index<reg</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x29 <span class="paren1">(<span class="code">call op-ld-font<vx</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x33 <span class="paren1">(<span class="code">call op-ld-bcd<vx</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x55 <span class="paren1">(<span class="code">call op-ld-mem<regs</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">#x65 <span class="paren1">(<span class="code">call op-ld-regs<mem</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>The <code>macrolet</code> at the beginning saves us a bit of typing, but otherwise this is
|
|
just a big boring table to figure out which instruction to call.</p>
|
|
|
|
<p>That's all the boring infrastructure work we need — on to implementing the
|
|
actual instructions.</p>
|
|
|
|
<h2 id="s16-instructions"><a href="index.html#s16-instructions">Instructions</a></h2>
|
|
|
|
<p>The CHIP-8 supports thirty-six instructions, all of which we'll need to
|
|
implement. We'll start with the simpler ones, and we'll be leaving some of the
|
|
others (the graphics/sound related ones) for later articles.</p>
|
|
|
|
<p>(In contrast, the Game Boy has roughly five hundred instructions, so now you
|
|
might see why I thought CHIP-8 would be simpler!)</p>
|
|
|
|
<h3 id="s17-random-numbers"><a href="index.html#s17-random-numbers">Random Numbers</a></h3>
|
|
|
|
<p>We'll start with the <code>RND</code> instruction, which generates random numbers. This
|
|
may seem like an odd place to start, but I want to describe some extra syntactic
|
|
sugar and <code>RND</code> is a nice standalone instruction to use as an example.</p>
|
|
|
|
<p>Cowgod's reference describes <code>RND</code> like so:</p>
|
|
|
|
<pre><code>Cxkk - RND Vx, byte
|
|
Set Vx = random byte AND kk.
|
|
|
|
The interpreter generates a random number from 0 to 255,
|
|
which is then ANDed with the value kk. The results are
|
|
stored in Vx.
|
|
</code></pre>
|
|
|
|
<p>A first stab at the implementation in Lisp might look like this:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> op-rand <span class="paren2">(<span class="code">chip instruction</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">reg <span class="paren5">(<span class="code">logand #x0F00 instruction</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">mask <span class="paren5">(<span class="code">logand #x00FF instruction</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren4">(<span class="code">chip</span>)</span>
|
|
<span class="paren4">(<span class="code">setf <span class="paren5">(<span class="code">aref registers reg</span>)</span>
|
|
<span class="paren5">(<span class="code">logand <span class="paren6">(<span class="code">random 256</span>)</span> mask</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This would work, but it's an awful lot of code for something that just takes
|
|
a sentence or two to describe. We're going to be defining a lot of
|
|
instructions, so let's take a few minutes and make our lives easier.</p>
|
|
|
|
<h3 id="s18-define-instruction"><a href="index.html#s18-define-instruction">Define-Instruction</a></h3>
|
|
|
|
<p>We'll create a <code>define-instruction</code> macro that will abstract away some of the
|
|
boring bits. The goal is to end up with instruction definitions that read as
|
|
close to the documentation as possible.</p>
|
|
|
|
<p>We'll start by removing the need to use the <code>with-chip</code> macro. Every
|
|
instruction needs to deal with the <code>chip</code> struct, so let's not repeat ourselves
|
|
thirty-six times. Every instruction will also take <code>chip</code> and <code>instruction</code>
|
|
arguments, so we can remove those too:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> <i><span class="symbol">define-instruction</span></i> <span class="paren2">(<span class="code">name &body body</span>)</span>
|
|
`<span class="paren2">(<span class="code"><i><span class="symbol">defun</span></i> ,name <span class="paren3">(<span class="code">chip instruction</span>)</span>
|
|
<span class="paren3">(<span class="code">declare <span class="paren4">(<span class="code">ignorable instruction</span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren4">(<span class="code">chip</span>)</span>
|
|
,@body</span>)</span>
|
|
nil</span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-rand
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">reg <span class="paren5">(<span class="code">logand #x0F00 instruction</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">mask <span class="paren5">(<span class="code">logand #x00FF instruction</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">setf <span class="paren4">(<span class="code">aref registers reg</span>)</span>
|
|
<span class="paren4">(<span class="code">logand <span class="paren5">(<span class="code">random 256</span>)</span> mask</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>That's a little better. We're going to be calling these instructions a <em>lot</em>,
|
|
so it wouldn't hurt to add a type declaration for each function. This is easy
|
|
because they all take and return the same types:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> <i><span class="symbol">define-instruction</span></i> <span class="paren2">(<span class="code">name &body body</span>)</span>
|
|
`<span class="paren2">(<span class="code"><i><span class="symbol">progn</span></i>
|
|
<span class="paren3">(<span class="code">declaim <span class="paren4">(<span class="code">ftype <span class="paren5">(<span class="code"><i><span class="symbol">function</span></i> <span class="paren6">(<span class="code">chip int16</span>)</span> null</span>)</span> ,name</span>)</span></span>)</span> <span class="comment">; NEW
|
|
</span> <span class="paren3">(<span class="code"><i><span class="symbol">defun</span></i> ,name <span class="paren4">(<span class="code">chip instruction</span>)</span>
|
|
<span class="paren4">(<span class="code">declare <span class="paren5">(<span class="code">ignorable instruction</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren5">(<span class="code">chip</span>)</span>
|
|
,@body</span>)</span>
|
|
nil</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>Now all of our instruction functions will have type hints, and SBCL can check
|
|
them (if <code>safety</code> is high) or use them to generate faster code (if <code>speed</code> is
|
|
high).</p>
|
|
|
|
<p>We'll be accessing the values of registers quite often (<code>(aref registers ...)</code>)
|
|
so let's add a little <code>macrolet</code> to make that read nicer:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> <i><span class="symbol">define-instruction</span></i> <span class="paren2">(<span class="code">name &body body</span>)</span>
|
|
`<span class="paren2">(<span class="code"><i><span class="symbol">progn</span></i>
|
|
<span class="paren3">(<span class="code">declaim <span class="paren4">(<span class="code">ftype <span class="paren5">(<span class="code"><i><span class="symbol">function</span></i> <span class="paren6">(<span class="code">chip int16</span>)</span> null</span>)</span> ,name</span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code"><i><span class="symbol">defun</span></i> ,name <span class="paren4">(<span class="code">chip instruction</span>)</span>
|
|
<span class="paren4">(<span class="code">declare <span class="paren5">(<span class="code">ignorable instruction</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren5">(<span class="code">chip</span>)</span>
|
|
<span class="paren5">(<span class="code"><i><span class="symbol">macrolet</span></i> <span class="paren6">(<span class="code"><span class="paren1">(<span class="code">register <span class="paren2">(<span class="code">index</span>)</span> <span class="comment">; NEW
|
|
</span> `<span class="paren2">(<span class="code">aref registers ,index</span>)</span></span>)</span></span>)</span> <span class="comment">; NEW
|
|
</span> ,@body</span>)</span></span>)</span>
|
|
nil</span>)</span></span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-rand
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">reg <span class="paren5">(<span class="code">logand #x0F00 instruction</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">mask <span class="paren5">(<span class="code">logand #x00FF instruction</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">setf <span class="paren4">(<span class="code">register reg</span>)</span> <span class="comment">; NEW
|
|
</span> <span class="paren4">(<span class="code">logand <span class="paren5">(<span class="code">random 256</span>)</span> mask</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>Now instead of <code>(aref registers ...)</code> we can just say <code>(register ...)</code>. Cool.</p>
|
|
|
|
<p>The one thing that still bothers me is having to manually pull the instruction
|
|
arguments out of the instruction with bitmasking. It would be much nicer if we
|
|
could just declare what the arguments look like and have the computer generate
|
|
the appropriate masking code to deal with them. The goal is to be able to write
|
|
something like this:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-rand <span class="paren2">(<span class="code">_ r <span class="paren3">(<span class="code">mask 2</span>)</span></span>)</span> <span class="comment">;; RND
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">register r</span>)</span>
|
|
<span class="paren3">(<span class="code">logand <span class="paren4">(<span class="code">random 256</span>)</span> mask</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>The <code>(_ r (mask 2))</code> is the "argument list" of the instruction. It says:</p>
|
|
|
|
<ul>
|
|
<li>A single-nibble value which we don't care about: <code>_</code></li>
|
|
<li>A single-nibble value should be bound to <code>r</code>: <code>r</code></li>
|
|
<li>A two-nibble value should be bound to <code>mask</code>: <code>(mask 2)</code></li>
|
|
</ul>
|
|
|
|
<p>Compare the Lisp code and Cowgod's documentation:</p>
|
|
|
|
<pre><code>Cxkk - RND Vx, byte
|
|
Set Vx = random byte AND kk.
|
|
|
|
(define-instruction op-rand (_ r (mask 2)) ;; RND
|
|
(setf (register r)
|
|
(logand (random 256) mask)))</code></pre>
|
|
|
|
<p>Cowgod uses <code>V</code> as a shorthand for <code>register</code>, and calls the argument <code>x</code>
|
|
instead of <code>r</code>, but otherwise I think this is pretty damn close to the
|
|
documentation for actual running code.</p>
|
|
|
|
<p>So let's implement the argument-parsing macro. Feel free to skip this if you're
|
|
not really comfortable with macros. But it's a good demonstration of how you
|
|
can use them to get a nice clean language for a simple project.</p>
|
|
|
|
<p>First we'll wrap the <code>body</code> in a <code>let</code>:</p>
|
|
|
|
<pre><code><span class="code"> <span class="comment">; NEW
|
|
</span><span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> <i><span class="symbol">define-instruction</span></i> <span class="paren2">(<span class="code">name argument-list &body body</span>)</span>
|
|
`<span class="paren2">(<span class="code"><i><span class="symbol">progn</span></i>
|
|
<span class="paren3">(<span class="code">declaim <span class="paren4">(<span class="code">ftype <span class="paren5">(<span class="code"><i><span class="symbol">function</span></i> <span class="paren6">(<span class="code">chip int16</span>)</span> null</span>)</span> ,name</span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code"><i><span class="symbol">defun</span></i> ,name <span class="paren4">(<span class="code">chip instruction</span>)</span>
|
|
<span class="paren4">(<span class="code">declare <span class="paren5">(<span class="code">ignorable instruction</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren5">(<span class="code">chip</span>)</span>
|
|
<span class="paren5">(<span class="code"><i><span class="symbol">macrolet</span></i> <span class="paren6">(<span class="code"><span class="paren1">(<span class="code">register <span class="paren2">(<span class="code">index</span>)</span>
|
|
`<span class="paren2">(<span class="code">aref registers ,index</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code"><i><span class="symbol">let</span></i> ,<span class="paren1">(<span class="code">parse-instruction-argument-bindings argument-list</span>)</span> <span class="comment">; NEW
|
|
</span> ,@body</span>)</span></span>)</span>
|
|
nil</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>We'll use a helper function to parse the argument list into a list of <code>let</code>
|
|
bindings, instead of trying to cram it all into this one macro. It might look
|
|
scary, but it's not actually too bad:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> parse-instruction-argument-bindings <span class="paren2">(<span class="code">argument-list</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">flet</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">normalize-arg <span class="paren5">(<span class="code">arg</span>)</span>
|
|
<span class="paren5">(<span class="code">destructuring-bind <span class="paren6">(<span class="code">symbol &optional <span class="paren1">(<span class="code">nibbles 1</span>)</span></span>)</span>
|
|
<span class="paren6">(<span class="code">ensure-list arg</span>)</span>
|
|
<span class="paren6">(<span class="code">list symbol nibbles</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">iterate
|
|
<span class="paren4">(<span class="code">for <span class="paren5">(<span class="code">symbol nibbles</span>)</span> <span class="keyword">:in</span> <span class="paren5">(<span class="code">mapcar #'normalize-arg argument-list</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">for position <span class="keyword">:first</span> 3 <span class="keyword">:then</span> <span class="paren5">(<span class="code">- position nibbles</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">when <span class="paren5">(<span class="code">not <span class="paren6">(<span class="code">eql symbol '_</span>)</span></span>)</span>
|
|
<span class="paren5">(<span class="code">collect `<span class="paren6">(<span class="code">,symbol <span class="paren1">(<span class="code">ldb <span class="paren2">(<span class="code">byte ,<span class="paren3">(<span class="code">* nibbles 4</span>)</span>
|
|
,<span class="paren3">(<span class="code">* position 4</span>)</span></span>)</span>
|
|
instruction</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p><code>normalize-arg</code> takes each argument from the list and turns it into <code>(symbol
|
|
nibbles)</code>, so we don't have to write the lengths for single-nibble arguments
|
|
like this: <code>((_ 1) (r 1) (mask 2))</code>.</p>
|
|
|
|
<p>Then we loop through each <code>(symbol nibbles)</code> pair in the argument list. We
|
|
start at position <code>3</code> in the byte (the highest-order nibble) because we want to
|
|
write the argument list from left to right, high-order to low-order, like the
|
|
documentation.</p>
|
|
|
|
<p>For each pair, if the symbol is anything other than <code>_</code> we collect a <code>let</code>
|
|
binding with the appropriate <code>ldb</code> call for it. So for our example of <code>(_
|
|
r (mask 2))</code> we end up with two <code>let</code> bindings:</p>
|
|
|
|
<ul>
|
|
<li><code>(r (ldb (byte 4 8) instruction))</code></li>
|
|
<li><code>(mask (ldb (byte 8 0) instruction))</code></li>
|
|
</ul>
|
|
|
|
<p>Notice that we only bother parsing out the bindings we <em>need</em> from the
|
|
instruction. For instructions that don't need any arguments we'll end up with
|
|
an empty <code>(let () ...)</code> which the compiler will optimize away.</p>
|
|
|
|
<p>This might seem a bit hairy. The nice thing about using a helper function like
|
|
this is that we can call it on its own with a variety of argument lists to see
|
|
what gets generated:</p>
|
|
|
|
<pre><code>(map nil #'print
|
|
(parse-instruction-argument-bindings '(_ r (mask 2))))
|
|
|
|
(R (LDB (BYTE 4 8) INSTRUCTION))
|
|
(MASK (LDB (BYTE 8 0) INSTRUCTION))
|
|
|
|
(map nil #'print
|
|
(parse-instruction-argument-bindings '(_ x y _)))
|
|
|
|
(X (LDB (BYTE 4 8) INSTRUCTION))
|
|
(Y (LDB (BYTE 4 4) INSTRUCTION))
|
|
|
|
(map nil #'print
|
|
(parse-instruction-argument-bindings '(_ (foo 3))))
|
|
|
|
(FOO (LDB (BYTE 12 0) INSTRUCTION))</code></pre>
|
|
|
|
<p>And that wraps up <code>define-instruction</code>. So instead of writing <code>RND</code> as
|
|
a vanilla function:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> op-rand <span class="paren2">(<span class="code">chip instruction</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">reg <span class="paren5">(<span class="code">logand #x0F00 instruction</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">mask <span class="paren5">(<span class="code">logand #x00FF instruction</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code"><i><span class="symbol">with-chip</span></i> <span class="paren4">(<span class="code">chip</span>)</span>
|
|
<span class="paren4">(<span class="code">setf <span class="paren5">(<span class="code">aref registers reg</span>)</span>
|
|
<span class="paren5">(<span class="code">logand <span class="paren6">(<span class="code">random 256</span>)</span> mask</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>We can do something much cleaner:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-rand <span class="paren2">(<span class="code">_ r <span class="paren3">(<span class="code">mask 2</span>)</span></span>)</span> <span class="comment">;; RND
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">register r</span>)</span>
|
|
<span class="paren3">(<span class="code">logand <span class="paren4">(<span class="code">random 256</span>)</span> mask</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This is going to pay off nicely as we implement the other thirty-five
|
|
instructions.</p>
|
|
|
|
<h3 id="s19-jumps-and-calls"><a href="index.html#s19-jumps-and-calls">Jumps and Calls</a></h3>
|
|
|
|
<p>The CHIP-8 has two instructions for jumping directly to an address. The first
|
|
jumps to an immediate, literal address. The second adds an immediate value to
|
|
whatever is in register <code>#x0</code> and jumps to the result:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-jp-imm <span class="paren2">(<span class="code">_ <span class="paren3">(<span class="code">target 3</span>)</span></span>)</span> <span class="comment">;; JP addr
|
|
</span> <span class="paren2">(<span class="code">setf program-counter target</span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-jp-imm+reg <span class="paren2">(<span class="code">_ <span class="paren3">(<span class="code">target 3</span>)</span></span>)</span> <span class="comment">;; JP V0 + addr
|
|
</span> <span class="paren2">(<span class="code">setf program-counter <span class="paren3">(<span class="code">chop 12 <span class="paren4">(<span class="code">+ target <span class="paren5">(<span class="code">register 0</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>Our <code>define-instruction</code> macro is already paying off. The immediate values of
|
|
these arguments are three nibbles each, but our macro doesn't care.</p>
|
|
|
|
<p>There are also two instructions for implementing traditional function calls and
|
|
returns using the CHIP-8's stack:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-call <span class="paren2">(<span class="code">_ <span class="paren3">(<span class="code">target 3</span>)</span></span>)</span> <span class="comment">;; CALL addr
|
|
</span> <span class="paren2">(<span class="code">vector-push program-counter stack</span>)</span>
|
|
<span class="paren2">(<span class="code">setf program-counter target</span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-ret <span class="paren2">(<span class="code"></span>)</span> <span class="comment">;; RET
|
|
</span> <span class="paren2">(<span class="code">setf program-counter <span class="paren3">(<span class="code">vector-pop stack</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>We just use the built in <code>vector-push</code> and <code>vector-pop</code> functions to manage the
|
|
stack's fill pointer trivially.</p>
|
|
|
|
<p>Note that we're NOT using <code>vector-push-extend</code> because the CHIP-8 stack is
|
|
defined by the spec to only hold at most sixteen addresses — any ROM that tries
|
|
to push more is broken.</p>
|
|
|
|
<h3 id="s20-binary-coded-decimal"><a href="index.html#s20-binary-coded-decimal">Binary-Coded Decimal</a></h3>
|
|
|
|
<p>Let's take another self-contained instruction next: <code>BCD</code>, which stands for
|
|
<a href="https://en.wikipedia.org/wiki/Binary-coded_decimal">binary-coded decimal</a> (some references call this instruction <code>LD B, Vx</code>
|
|
but I think it's different enough to deserve its own name).</p>
|
|
|
|
<p>The problem this instruction is designed to solve goes something like this.
|
|
Let's say you've got a game where the player has a score that changes over time.
|
|
You can store the player's score in a register or memory somewhere, and
|
|
add/subtract to/from it with the normal arithmetic opcodes no problem.</p>
|
|
|
|
<p>But now you want to <em>display</em> the score to the player, and you probably want to
|
|
do that in base 10 because humans are generally bad at reading binary or hex.
|
|
So how do you take a byte like <code>#xA5</code> and determine the hundreds, tens, and ones
|
|
sprites to draw on the screen to read "165 points"?</p>
|
|
|
|
<p>This is what the <code>BCD</code> instruction does. It takes a single argument (the
|
|
register) and stores the hundreds, tens, and ones digits of that register's
|
|
value into three separate bytes of memory, starting at wherever the <code>index</code>
|
|
register is currently pointing.</p>
|
|
|
|
<pre class="lineart">
|
|
Register V3 Index Register
|
|
┌───┐ ┌───┐
|
|
│135│ │...│
|
|
└───┘ └─┬─┘
|
|
┌─────────────┘
|
|
│
|
|
▼
|
|
┌───┬───┬───┬───┬───┐
|
|
│ │ │ │ │ │ Memory
|
|
└───┴───┴───┴───┴───┘
|
|
|
|
Run BCD V3 instruction =================
|
|
|
|
Register V3 Index Register
|
|
┌───┐ ┌───┐
|
|
│135│ │...│
|
|
└───┘ └─┬─┘
|
|
┌─────────────┘
|
|
│
|
|
▼
|
|
┌───┬───┬───┬───┬───┐
|
|
│ 1 │ 3 │ 5 │ │ │ Memory
|
|
└───┴───┴───┴───┴───┘
|
|
</pre>
|
|
|
|
<p>We'll split this into two parts to make it easier to read. First we'll make
|
|
a <code>digit</code> function to retrieve a digit of a number:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> digit <span class="paren2">(<span class="code">position integer &optional <span class="paren3">(<span class="code">base 10</span>)</span></span>)</span>
|
|
<span class="paren2">(<span class="code">-<> integer
|
|
<span class="paren3">(<span class="code">floor <> <span class="paren4">(<span class="code">expt base position</span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">mod <> base</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>We could have hard-coded the base 10, but part of the Common Lisp tradition is
|
|
making flexible functions that can be reused in the future when it's not much
|
|
harder to do so.</p>
|
|
|
|
<p>Let's make sure it works on its own:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">digit 0 135</span>)</span>
|
|
5
|
|
|
|
<span class="paren1">(<span class="code">digit 1 135</span>)</span>
|
|
3
|
|
|
|
<span class="paren1">(<span class="code">digit 2 135</span>)</span>
|
|
1
|
|
|
|
<span class="paren1">(<span class="code">digit 0 #xD6 16</span>)</span>
|
|
6
|
|
|
|
<span class="paren1">(<span class="code">digit 1 #xD6 16</span>)</span>
|
|
13 <span class="comment">; 13 in base 10 == D in base 16</span></span></code></pre>
|
|
|
|
<p>And then we can define the actual operation:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-ld-bcd<vx <span class="paren2">(<span class="code">_ r _ _</span>)</span> <span class="comment">;; LD B, Vx
|
|
</span> <span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">number <span class="paren5">(<span class="code">register r</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">setf <span class="paren4">(<span class="code">aref memory <span class="paren5">(<span class="code">+ index 0</span>)</span></span>)</span> <span class="paren4">(<span class="code">digit 2 number</span>)</span>
|
|
<span class="paren4">(<span class="code">aref memory <span class="paren5">(<span class="code">+ index 1</span>)</span></span>)</span> <span class="paren4">(<span class="code">digit 1 number</span>)</span>
|
|
<span class="paren4">(<span class="code">aref memory <span class="paren5">(<span class="code">+ index 2</span>)</span></span>)</span> <span class="paren4">(<span class="code">digit 0 number</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<h3 id="s21-arithmetic"><a href="index.html#s21-arithmetic">Arithmetic</a></h3>
|
|
|
|
<p>Next up are the arithmetic instructions. The CHIP-8 only supports addition and
|
|
subtraction — there are no multiplication or division instructions.</p>
|
|
|
|
<p>The first two instructions are <code>ADD</code> and <code>SUB</code>:</p>
|
|
|
|
<pre><code>ADD Vx, Vy → Vx = Vx + Vy
|
|
SUB Vx, Vy → Vx = Vx - Vy
|
|
</code></pre>
|
|
|
|
<p>Once again we'll start by creating two helper functions to perform the 8-bit
|
|
addition/subtraction with overflow/underflow. These functions will return
|
|
a second value that represents the carry/borrow bit.</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> +_8 <span class="paren2">(<span class="code">x y</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">result <span class="paren5">(<span class="code">+ x y</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">chop 8 result</span>)</span>
|
|
<span class="paren4">(<span class="code"><i><span class="symbol">if</span></i> <span class="paren5">(<span class="code">> result 255</span>)</span> 1 0</span>)</span></span>)</span></span>)</span></span>)</span> <span class="comment">; carry
|
|
</span>
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> -_8 <span class="paren2">(<span class="code">x y</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">result <span class="paren5">(<span class="code">- x y</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">chop 8 result</span>)</span>
|
|
<span class="paren4">(<span class="code"><i><span class="symbol">if</span></i> <span class="paren5">(<span class="code">> x y</span>)</span> 1 0</span>)</span></span>)</span></span>)</span></span>)</span> <span class="comment">; borrow</span></span></code></pre>
|
|
|
|
<p>And now the instructions themselves are trivial:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-add-reg<reg <span class="paren2">(<span class="code">_ rx ry</span>)</span> <span class="comment">;; ADD Vx, Vy (8-bit)
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">register rx</span>)</span> flag</span>)</span>
|
|
<span class="paren3">(<span class="code">+_8 <span class="paren4">(<span class="code">register rx</span>)</span> <span class="paren4">(<span class="code">register ry</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-sub-reg<reg <span class="paren2">(<span class="code">_ rx ry</span>)</span> <span class="comment">;; SUB Vx, Vy (8-bit)
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">register rx</span>)</span> flag</span>)</span>
|
|
<span class="paren3">(<span class="code">-_8 <span class="paren4">(<span class="code">register rx</span>)</span> <span class="paren4">(<span class="code">register ry</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This takes advantage of the fact that you can use <code>(setf (values ...) ...)</code> to
|
|
assign the multiple values returned by a function, without binding them to local
|
|
variables.</p>
|
|
|
|
<p>Notice how we just assign to <code>flag</code>. Under the hood that <code>flag</code> has been bound
|
|
with our <code>with-chip</code> macro to mean <code>(chip-flag chip)</code>, which we defined way back
|
|
in the beginning to mean <code>(aref (chip-registers chip) #xF)</code>. But isn't it much
|
|
nicer to just say <code>(setf flag ...)</code>?</p>
|
|
|
|
<p>There's also a <code>SUBN</code> instruction for subtracting the operands in reverse order
|
|
(but still storing the result in the first):</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-subn-reg<reg <span class="paren2">(<span class="code">_ rx ry</span>)</span> <span class="comment">;; SUBN Vx, Vy (8-bit)
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">register rx</span>)</span> flag</span>)</span>
|
|
<span class="comment">;; subtraction order is swapped for SUBN
|
|
</span> <span class="paren3">(<span class="code">-_8 <span class="paren4">(<span class="code">register ry</span>)</span> <span class="paren4">(<span class="code">register rx</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>Next is an <code>ADD</code> instruction that takes an immediate value. Unlike the other
|
|
instructions this one does <em>not</em> set the flag for some reason (that was a fun
|
|
bug to track down):</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-add-reg<imm <span class="paren2">(<span class="code">_ r <span class="paren3">(<span class="code">immediate 2</span>)</span></span>)</span> <span class="comment">;; ADD Vx, Imm
|
|
</span> <span class="comment">;; For some weird reason the ADD immediate op doesn't set the flag
|
|
</span> <span class="paren2">(<span class="code">zapf <span class="paren3">(<span class="code">register r</span>)</span> <span class="paren3">(<span class="code">+_8 % immediate</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>Because Common Lisp will just ignore extra return values if we don't use them,
|
|
we can just use our <code>+_8</code> helper function here too and ignore the carry result.</p>
|
|
|
|
<p>There's also a single 16-bit <code>ADD</code> instruction that adds the value in
|
|
a particular register to the index register. It too ignores the <code>flag</code>:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-add-index<reg <span class="paren2">(<span class="code">_ r</span>)</span> <span class="comment">;; ADD I, Vx (16-bit)
|
|
</span> <span class="paren2">(<span class="code">zapf index <span class="paren3">(<span class="code">chop 16 <span class="paren4">(<span class="code">+ % <span class="paren5">(<span class="code">register r</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<h3 id="s22-shifting"><a href="index.html#s22-shifting">Shifting</a></h3>
|
|
|
|
<p>The CHIP-8 has two bit-shifting instructions: <code>SHR</code> and <code>SHL</code> which shift
|
|
a register's contents right or left by a single bit. Both of these instructions
|
|
also set the flag to the bit that got shifted "off the end" of the register.</p>
|
|
|
|
<p>We'll define two more helpers to do the actual 8-bit shifting and keep track of
|
|
the bit that falls off the end:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> get-bit <span class="paren2">(<span class="code">position integer</span>)</span>
|
|
<span class="paren2">(<span class="code">ldb <span class="paren3">(<span class="code">byte 1 position</span>)</span> integer</span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> >>_8 <span class="paren2">(<span class="code">v</span>)</span>
|
|
<span class="paren2">(<span class="code">values <span class="paren3">(<span class="code">ash v -1</span>)</span>
|
|
<span class="paren3">(<span class="code">get-bit 0 v</span>)</span></span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> <<_8 <span class="paren2">(<span class="code">v</span>)</span>
|
|
<span class="paren2">(<span class="code">values <span class="paren3">(<span class="code">chop 8 <span class="paren4">(<span class="code">ash v 1</span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">get-bit 7 v</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>The instructions themselves are trivial:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-shr <span class="paren2">(<span class="code">_ r</span>)</span> <span class="comment">;; SHR
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">register r</span>)</span> flag</span>)</span>
|
|
<span class="paren3">(<span class="code">>>_8 <span class="paren4">(<span class="code">register r</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-shl <span class="paren2">(<span class="code">_ r</span>)</span> <span class="comment">;; SHL
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">register r</span>)</span> flag</span>)</span>
|
|
<span class="paren3">(<span class="code"><<_8 <span class="paren4">(<span class="code">register r</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p><strong>Update:</strong> as <a href="https://news.ycombinator.com/item?id=13217352">fernly pointed out</a>, this <a href="https://groups.yahoo.com/neo/groups/rcacosmac/conversations/topics/328">may not have been the
|
|
original intended behavior</a>. If we want to make our emulator match
|
|
the original intent, it's pretty simple:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-shr <span class="paren2">(<span class="code">_ rx ry</span>)</span> <span class="comment">;; SHR
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">register ry</span>)</span> flag</span>)</span>
|
|
<span class="paren3">(<span class="code">>>_8 <span class="paren4">(<span class="code">register rx</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-shl <span class="paren2">(<span class="code">_ rx ry</span>)</span> <span class="comment">;; SHL
|
|
</span> <span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">values <span class="paren4">(<span class="code">register ry</span>)</span> flag</span>)</span>
|
|
<span class="paren3">(<span class="code"><<_8 <span class="paren4">(<span class="code">register rx</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>But I suspect that this will probably break some ROMs that rely on the
|
|
incorrectly-documented behavior.</p>
|
|
|
|
<h3 id="s23-logical-operations"><a href="index.html#s23-logical-operations">Logical Operations</a></h3>
|
|
|
|
<p>Next up are the logical <code>AND</code>/<code>OR</code>/<code>XOR</code> instructions. We could define these
|
|
like so:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-and <span class="paren2">(<span class="code">_ destination source _</span>)</span>
|
|
<span class="paren2">(<span class="code">zapf <span class="paren3">(<span class="code">register destination</span>)</span> <span class="paren3">(<span class="code">logand % <span class="paren4">(<span class="code">register source</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-or <span class="paren2">(<span class="code">_ destination source _</span>)</span>
|
|
<span class="paren2">(<span class="code">zapf <span class="paren3">(<span class="code">register destination</span>)</span> <span class="paren3">(<span class="code">logior % <span class="paren4">(<span class="code">register source</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-xor <span class="paren2">(<span class="code">_ destination source _</span>)</span>
|
|
<span class="paren2">(<span class="code">zapf <span class="paren3">(<span class="code">register destination</span>)</span> <span class="paren3">(<span class="code">logxor % <span class="paren4">(<span class="code">register source</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This works, but aside from the name and the operation they're all identical.
|
|
The next few groups of instructions are also going to be similar, so let's step
|
|
back for a moment and see if we can abstract away the tedium.</p>
|
|
|
|
<h3 id="s24-macro-map"><a href="index.html#s24-macro-map">Macro-Map</a></h3>
|
|
|
|
<p>Instead of typing the same thing over and over, we'd like to just say what we
|
|
want once. The traditional way to do this is with <code>macrolet</code>:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">macrolet</span></i>
|
|
<span class="paren2">(<span class="code"><span class="paren3">(<span class="code"><i><span class="symbol">define-logical-instruction</span></i> <span class="paren4">(<span class="code">name <i><span class="symbol">function</span></i></span>)</span>
|
|
`<span class="paren4">(<span class="code"><i><span class="symbol">define-instruction</span></i> ,name <span class="paren5">(<span class="code">_ destination source _</span>)</span>
|
|
<span class="paren5">(<span class="code">zapf <span class="paren6">(<span class="code">register destination</span>)</span>
|
|
<span class="paren6">(<span class="code">,<i><span class="symbol">function</span></i> % <span class="paren1">(<span class="code">register source</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">define-logical-instruction</span></i> op-and logand</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">define-logical-instruction</span></i> op-or logior</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">define-logical-instruction</span></i> op-xor logxor</span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>Now we're only writing out the actual functionality once instead of three times.
|
|
That's better, but I'm still not satisfied. Using <code>macrolet</code> means I need to
|
|
think of a name for the macro that I'm just going to use within this block and
|
|
throw away, and naming things is hard.</p>
|
|
|
|
<p>What I <em>really</em> want to do here is just "map" a macro over a bunch of arguments
|
|
and be done with it. I've played around a bit and ended up with a macro called
|
|
<code>macro-map</code> that does just that:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> macro-map <span class="paren2">(<span class="code">lambda-list items &rest body</span>)</span>
|
|
<span class="paren2">(<span class="code"><i><span class="symbol">with-gensyms</span></i> <span class="paren3">(<span class="code">macro</span>)</span>
|
|
`<span class="paren3">(<span class="code"><i><span class="symbol">macrolet</span></i> <span class="paren4">(<span class="code"><span class="paren5">(<span class="code">,macro ,<span class="paren6">(<span class="code">ensure-list lambda-list</span>)</span> ,@body</span>)</span></span>)</span>
|
|
,@<span class="paren4">(<span class="code">iterate <span class="paren5">(<span class="code">for item <span class="keyword">:in</span> items</span>)</span>
|
|
<span class="paren5">(<span class="code">collect `<span class="paren6">(<span class="code">,macro ,@<span class="paren1">(<span class="code">ensure-list item</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p><code>macro-map</code> takes a lambda list, list of arguments, and a body for the macro and
|
|
builds the <code>macrolet</code> we wrote by hand earlier using a <code>gensym</code> for the name so
|
|
we don't have to waste brain cells thinking of one. Now we can define our
|
|
logical operations all at once:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">macro-map <span class="comment">;; AND/OR/XOR
|
|
</span> <span class="paren2">(<span class="code">NAME OP</span>)</span>
|
|
<span class="paren2">(<span class="code"><span class="paren3">(<span class="code">op-and logand</span>)</span>
|
|
<span class="paren3">(<span class="code">op-or logior</span>)</span>
|
|
<span class="paren3">(<span class="code">op-xor logxor</span>)</span></span>)</span>
|
|
`<span class="paren2">(<span class="code"><i><span class="symbol">define-instruction</span></i> ,name <span class="paren3">(<span class="code">_ destination source _</span>)</span>
|
|
<span class="paren3">(<span class="code">zapf <span class="paren4">(<span class="code">register destination</span>)</span> <span class="paren4">(<span class="code">,op % <span class="paren5">(<span class="code">register source</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>This actually ends up being one line of code longer than the copy/pasted version
|
|
because I like to linebreak liberally, but the important thing is that we only
|
|
say each thing we need to say once.</p>
|
|
|
|
<p>Notice how this almost starts to look like a table of data rather than code.
|
|
I've taken advantage of the fact that the Lisp reader reads everything as
|
|
uppercase by default and uppercased the "header" row (the lambda list) to make
|
|
it stand out a bit more.</p>
|
|
|
|
<p>If you squint a little bit you might imagine how defining a set of related
|
|
instructions could almost look like <a href="https://i.imgur.com/8jbXmVj.png">a page from a CPU
|
|
manual</a>.</p>
|
|
|
|
<h3 id="s25-branching"><a href="index.html#s25-branching">Branching</a></h3>
|
|
|
|
<p>Now that we've got a way to define batches of similar instructions without going
|
|
crazy we can tackle the branching instructions.</p>
|
|
|
|
<p>Most CPUs have some form of "jump to some location if zero, otherwise continue"
|
|
instruction to implement branching. The CHIP-8 needs to fit every instruction
|
|
into two bytes, so it does something a bit simpler. Instead of an arbitrary
|
|
jump on a condition, it has a series of <em>skip</em> instructions.</p>
|
|
|
|
<p>For example, <code>SE Vx, 0</code> means "skip the next instruction if register X is zero".
|
|
Skipping the next instruction is a simple as incrementing the program counter by
|
|
an extra two bytes.</p>
|
|
|
|
<p>There are four variants of this instruction:</p>
|
|
|
|
<pre><code>SE Vx, Immediate → Skip when Vx equals Immediate
|
|
SNE Vx, Immediate → Skip when Vx does not equal Immediate
|
|
SE Vx, Vy → Skip when Vx equals Vy
|
|
SNE Vx, Vy → Skip when Vx does not equal Vy
|
|
</code></pre>
|
|
|
|
<p>Let's use <code>macro-map</code> to write this out as a table of code:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">macro-map <span class="comment">;; SE/SNE
|
|
</span> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">NAME TEST X-ARG X-FORM Y-ARG Y-FORM</span>)</span>
|
|
<span class="paren3">(<span class="code"><span class="paren4">(<span class="code">op-se-reg-imm = <span class="paren5">(<span class="code">r 1</span>)</span> <span class="paren5">(<span class="code">register r</span>)</span> <span class="paren5">(<span class="code">immediate 2</span>)</span> immediate</span>)</span>
|
|
<span class="paren4">(<span class="code">op-sne-reg-imm not= <span class="paren5">(<span class="code">r 1</span>)</span> <span class="paren5">(<span class="code">register r</span>)</span> <span class="paren5">(<span class="code">immediate 2</span>)</span> immediate</span>)</span>
|
|
<span class="paren4">(<span class="code">op-se-reg-reg = <span class="paren5">(<span class="code">rx 1</span>)</span> <span class="paren5">(<span class="code">register rx</span>)</span> <span class="paren5">(<span class="code">ry 1</span>)</span> <span class="paren5">(<span class="code">register ry</span>)</span></span>)</span>
|
|
<span class="paren4">(<span class="code">op-sne-reg-reg not= <span class="paren5">(<span class="code">rx 1</span>)</span> <span class="paren5">(<span class="code">register rx</span>)</span> <span class="paren5">(<span class="code">ry 1</span>)</span> <span class="paren5">(<span class="code">register ry</span>)</span></span>)</span></span>)</span></span>)</span>
|
|
`<span class="paren2">(<span class="code"><i><span class="symbol">define-instruction</span></i> ,name <span class="paren3">(<span class="code">_ ,x-arg ,y-arg</span>)</span>
|
|
<span class="paren3">(<span class="code">when <span class="paren4">(<span class="code">,test ,x-form ,y-form</span>)</span>
|
|
<span class="paren4">(<span class="code">incf program-counter 2</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>After the macroexpansion we end up with things like:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-sne-reg-imm <span class="paren2">(<span class="code">_ <span class="paren3">(<span class="code">r 1</span>)</span> <span class="paren3">(<span class="code">immediate 2</span>)</span></span>)</span>
|
|
<span class="paren2">(<span class="code">when <span class="paren3">(<span class="code">not= <span class="paren4">(<span class="code">register r</span>)</span> immediate</span>)</span>
|
|
<span class="paren3">(<span class="code">incf program-counter 2</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>We'll also need <code>not=</code> itself to avoid having to do messy things inside the
|
|
macro body:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun-inline</span></i> not= <span class="paren2">(<span class="code">x y</span>)</span>
|
|
<span class="paren2">(<span class="code">not <span class="paren3">(<span class="code">= x y</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>Just one more group of instructions left (for this post).</p>
|
|
|
|
<h3 id="s26-loads"><a href="index.html#s26-loads">Loads</a></h3>
|
|
|
|
<p>The final group of instructions we'll look at for now is the <code>LD</code> family of
|
|
loads. Normally we'd implement these first, but I wanted to introduce
|
|
<code>macro-map</code> as gently as possible.</p>
|
|
|
|
<p>Most of the <code>LD</code> instructions simply take a value from a source and stick it
|
|
into a destination, and we can implement them as a single <code>setf</code> form:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">macro-map <span class="comment">;; LD
|
|
</span> <span class="paren2">(<span class="code">NAME ARGLIST DESTINATION SOURCE</span>)</span>
|
|
<span class="paren2">(<span class="code"><span class="paren3">(<span class="code">op-ld-i<imm <span class="paren4">(<span class="code">_ <span class="paren5">(<span class="code">value 3</span>)</span></span>)</span> index value</span>)</span>
|
|
<span class="paren3">(<span class="code">op-ld-reg<imm <span class="paren4">(<span class="code">_ r <span class="paren5">(<span class="code">value 2</span>)</span></span>)</span> <span class="paren4">(<span class="code">register r</span>)</span> value</span>)</span>
|
|
<span class="paren3">(<span class="code">op-ld-reg<reg <span class="paren4">(<span class="code">_ rx ry _</span>)</span> <span class="paren4">(<span class="code">register rx</span>)</span> <span class="paren4">(<span class="code">register ry</span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">op-ld-reg<dt <span class="paren4">(<span class="code">_ r _ _</span>)</span> <span class="paren4">(<span class="code">register r</span>)</span> delay-timer</span>)</span>
|
|
<span class="paren3">(<span class="code">op-ld-dt<reg <span class="paren4">(<span class="code">_ r _ _</span>)</span> delay-timer <span class="paren4">(<span class="code">register r</span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="code">op-ld-st<reg <span class="paren4">(<span class="code">_ r _ _</span>)</span> sound-timer <span class="paren4">(<span class="code">register r</span>)</span></span>)</span></span>)</span>
|
|
`<span class="paren2">(<span class="code"><i><span class="symbol">define-instruction</span></i> ,name ,arglist
|
|
<span class="paren3">(<span class="code">setf ,destination ,source</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>We haven't talked about the timers yet, so don't worry about them. I'm leaving
|
|
them in so you can see how nice the <code>macro-map</code> is when you need to define lots
|
|
of similar operations at once.</p>
|
|
|
|
<p>There are two other more interesting <code>LD</code> instructions which move data between
|
|
multiple registers and memory.</p>
|
|
|
|
<p><code>LD [I], n</code> loads consecutive bytes of memory with the contents of registers
|
|
<code>V0</code> through <code>Vn</code>, starting at wherever the index register is pointing. For
|
|
example, <code>LD [I], 2</code> would be:</p>
|
|
|
|
<pre class="lineart">
|
|
V0 V1 V2 V3 V4 ...
|
|
┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐
|
|
│ │ │ │ │ │ │ │ │ │ ...
|
|
└─┬┘ └─┬┘ └─┬┘ └──┘ └──┘
|
|
└───┐└──┐ └─┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌───┬───┬───┬───┬───┐
|
|
│ │ │ │ │ │ Memory
|
|
└───┴───┴───┴───┴───┘
|
|
▲ ┌───┐
|
|
└──│...│ Index Register
|
|
└───┘
|
|
</pre>
|
|
|
|
<p><code>LD n, [I]</code> does the opposite: it loads the contents of memory into the
|
|
registers <code>V0</code> through <code>Vn</code>.</p>
|
|
|
|
<p>Because we've used Lisp arrays for both the registers and memory, these
|
|
instructions are really lovely to implement — they end up being just a single
|
|
call to <code>replace</code>:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-ld-mem<regs <span class="paren2">(<span class="code">_ n _ _</span>)</span> <span class="comment">;; LD [I] < Vn
|
|
</span> <span class="paren2">(<span class="code">replace memory registers <span class="keyword">:start1</span> index <span class="keyword">:end2</span> <span class="paren3">(<span class="code">1+ n</span>)</span></span>)</span></span>)</span>
|
|
|
|
<span class="paren1">(<span class="code"><i><span class="symbol">define-instruction</span></i> op-ld-regs<mem <span class="paren2">(<span class="code">_ n _ _</span>)</span> <span class="comment">;; LD Vn < [I]
|
|
</span> <span class="paren2">(<span class="code">replace registers memory <span class="keyword">:end1</span> <span class="paren3">(<span class="code">1+ n</span>)</span> <span class="keyword">:start2</span> index</span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>That's it for the <code>LD</code> instructions (for now) and for the instructions in
|
|
general!</p>
|
|
|
|
<h2 id="s27-future"><a href="index.html#s27-future">Future</a></h2>
|
|
|
|
<p>With these instructions implemented (and with some stubs for the rest) we can
|
|
load and run a ROM and it will push bytes around in memory and mostly work. In
|
|
the next few posts we'll look at the next steps to getting a fully-functional
|
|
emulator up and running, including:</p>
|
|
|
|
<ul>
|
|
<li>Graphics and input</li>
|
|
<li>Sound</li>
|
|
<li>Debugging</li>
|
|
</ul>
|
|
|
|
<p><em>Thanks to <a href="https://twitter.com/jamesnvc">James Cash</a> for reading a draft of
|
|
this post.</em></p>
|
|
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a> ・ <a href='https://twitter.com/stevelosh/'>Twitter</a> ・ <a href='https://instagram.com/thirtytwobirds/'>Instagram</a> ・ <a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html> |