281 lines
24 KiB
HTML
281 lines
24 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: Menus / 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: Menus</a></h1><p class='date'>Posted on January 10th, 2017.</p><p>Our <a href="https://en.wikipedia.org/wiki/CHIP-8">CHIP-8</a> emulator in Common Lisp is almost complete. It can play
|
||
|
games, and we've got a rudimentary debugging system in place so we can figure
|
||
|
out where things go wrong.</p>
|
||
|
|
||
|
<p>Up to now we've been communicating with the running emulator mostly through
|
||
|
NREPL or SLIME. This is fine for development, but in this post we'll add some
|
||
|
much-needed polish in the form of menus. This is the kind of boring work that
|
||
|
often gets left until the end during game development, so let's just get it out
|
||
|
of the way.</p>
|
||
|
|
||
|
<p>The full series of posts so far:</p>
|
||
|
|
||
|
<ol>
|
||
|
<li><a href="../../../2016/12/chip8-cpu/index.html">CHIP-8 in Common Lisp: The CPU</a></li>
|
||
|
<li><a href="../../../2016/12/chip8-graphics/index.html">CHIP-8 in Common Lisp: Graphics</a></li>
|
||
|
<li><a href="../../../2016/12/chip8-input/index.html">CHIP-8 in Common Lisp: Input</a></li>
|
||
|
<li><a href="../../../2016/12/chip8-sound/index.html">CHIP-8 in Common Lisp: Sound</a></li>
|
||
|
<li><a href="../chip8-disassembly/index.html">CHIP-8 in Common Lisp: Disassembly</a></li>
|
||
|
<li><a href="../chip8-debugging-infrastructure/index.html">CHIP-8 in Common Lisp: Debugging Infrastructure</a></li>
|
||
|
<li><a href="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-architecture">Architecture</a></li><li><a href="index.html#s2-adding-a-main-window">Adding a Main Window</a></li><li><a href="index.html#s3-updating-the-screen">Updating the Screen</a></li><li><a href="index.html#s4-adding-menus">Adding Menus</a><ol><li><a href="index.html#s5-the-file-menu">The File Menu</a></li><li><a href="index.html#s6-the-display-menu">The Display Menu</a></li><li><a href="index.html#s7-the-sound-menu">The Sound Menu</a></li></ol></li><li><a href="index.html#s8-results">Results</a></li><li><a href="index.html#s9-future">Future</a></li></ol>
|
||
|
|
||
|
<h2 id="s1-architecture"><a href="index.html#s1-architecture">Architecture</a></h2>
|
||
|
|
||
|
<p>Qtools has some <a href="https://shinmera.github.io/qtools/#QTOOLS:DEFINE-MENU">rudimentary support</a> for menus. Unfortunately it
|
||
|
won't quite work with our emulator as-is, so we'll need to shuffle things around
|
||
|
a bit. When we added the screen to the emulator back in the <a href="../../../2016/12/chip8-graphics/index.html">graphics</a> post
|
||
|
we just created a subclass of <code>QGLWidget</code> and passed it along to
|
||
|
<code>with-main-window</code>. This works for displaying the screen, but if you try to
|
||
|
<code>define-menu</code> on this widget Qtools will signal an error.</p>
|
||
|
|
||
|
<p>What we need to do is create a <code>QMainWindow</code> widget instead, and put our
|
||
|
<code>QGLWidget</code> inside of that. Then we can add some menus to the main window and
|
||
|
everything should work great. It will end up being structured like this:</p>
|
||
|
|
||
|
<pre class="lineart">
|
||
|
QMainWindow
|
||
|
╔══════════════════════════════════╗
|
||
|
║ Menu1 │ Menu2 │ ... ║
|
||
|
║───────┴───────┴──────────────────║
|
||
|
║ ┌──────────────────────────────┐ ║
|
||
|
║ │..............................│ ║
|
||
|
║ │..............................│ ║
|
||
|
║ │..........QGLWidget...........│ ║
|
||
|
║ │.........(the screen).........│ ║
|
||
|
║ │..............................│ ║
|
||
|
║ │..............................│ ║
|
||
|
║ │..............................│ ║
|
||
|
║ └──────────────────────────────┘ ║
|
||
|
╚══════════════════════════════════╝
|
||
|
</pre>
|
||
|
|
||
|
<h2 id="s2-adding-a-main-window"><a href="index.html#s2-adding-a-main-window">Adding a Main Window</a></h2>
|
||
|
|
||
|
<p>We'll start by creating the <code>QMainWindow</code> widget. It will just have a single
|
||
|
slot to keep track of the <code>chip</code> struct it's displaying:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-widget</span></i> main-window <span class="paren2">(<span class="code">QMainWindow</span>)</span>
|
||
|
<span class="paren2">(<span class="code"><span class="paren3">(<span class="code">chip <span class="keyword">:accessor</span> main-chip <span class="keyword">:initarg</span> <span class="keyword">:chip</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>Next we'll define our screen widget to be a <em>subwidget</em> of the main window:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-subwidget</span></i> <span class="paren2">(<span class="code">main-window screen</span>)</span> <span class="paren2">(<span class="code">make-instance 'screen</span>)</span>
|
||
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">screen-chip screen</span>)</span> chip</span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>This ensures the screen widget will get cleaned up properly when the main window
|
||
|
is closed.</p>
|
||
|
|
||
|
<p>Now we need to move some of the initialization code that used to be in the
|
||
|
screen widget up into the main window, and also add a bit more to connect
|
||
|
everything properly:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-initializer</span></i> <span class="paren2">(<span class="code">main-window main-setup</span>)</span>
|
||
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">q+:window-title main-window</span>)</span> <span class="string">"cl-chip8"</span>
|
||
|
<span class="paren3">(<span class="code">q+:central-widget main-window</span>)</span> screen
|
||
|
<span class="paren3">(<span class="code">q+:focus-proxy main-window</span>)</span> screen</span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>The <code>window-title</code> needs to go on the top-level widget, so we can pull that out
|
||
|
of the screen's initializer. We also set the screen to be the "central widget"
|
||
|
of the main window. You can read the <a href="https://doc.qt.io/qt-4.8/qmainwindow.html">Qt docs</a> for the full
|
||
|
story, but essentially a <code>QMainWindow</code> is just a container for other widgets and
|
||
|
we need to designate one as the primary widget.</p>
|
||
|
|
||
|
<p>We'll also want to set the <a href="https://doc.qt.io/qt-4.8/qwidget.html#setFocusProxy">focus proxy</a> of the main window to be the screen,
|
||
|
because we want the screen to handle keyboard input just like before. Setting
|
||
|
the focus proxy tells Qt that whenever the main window gets focused it should
|
||
|
actually focus the screen instead. If we didn't do this, then if the main
|
||
|
window itself got focused (which can happen when tabbing through applications,
|
||
|
clicking the title bar, etc) it wouldn't propagate the keystrokes down to the
|
||
|
screen.</p>
|
||
|
|
||
|
<p>This is all kind of fiddly stuff, but it's the polish that separates a toy
|
||
|
project from something that actually feels <a href="http://makegames.tumblr.com/post/1136623767/finishing-a-game">finished</a>.</p>
|
||
|
|
||
|
<p>We'll also want to change our <code>die</code> function to close this main window, not just
|
||
|
the screen. I'm going to cheat just a little bit and use a global variable to
|
||
|
hold the currently-running main window for easy access:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defparameter</span></i> <span class="special">*main-window*</span> nil</span>)</span> <span class="comment">; will get set later
|
||
|
</span>
|
||
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> die <span class="paren2">(<span class="code"></span>)</span>
|
||
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">chip8::chip-running <span class="paren4">(<span class="code">main-chip <span class="special">*main-window*</span></span>)</span></span>)</span> nil</span>)</span>
|
||
|
<span class="paren2">(<span class="code">q+:close <span class="special">*main-window*</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>Finally, <code>run-gui</code> will need to start up a <code>main-window</code> instead of a screen:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> make-main-window <span class="paren2">(<span class="code">chip</span>)</span>
|
||
|
<span class="paren2">(<span class="code">make-instance 'main-window <span class="keyword">:chip</span> chip</span>)</span></span>)</span>
|
||
|
|
||
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> run-gui <span class="paren2">(<span class="code">chip thunk</span>)</span>
|
||
|
<span class="paren2">(<span class="code"><i><span class="symbol">with-main-window</span></i>
|
||
|
<span class="paren3">(<span class="code">window <span class="paren4">(<span class="code">setf <span class="special">*main-window*</span> <span class="paren5">(<span class="code">make-main-window chip</span>)</span></span>)</span></span>)</span> <span class="comment">; NEW
|
||
|
</span> <span class="paren3">(<span class="code">funcall thunk</span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>I usually try not to use the return value of a <code>setf</code> form because I think it's
|
||
|
kind of ugly, but it saved an entire <code>let</code> here so decided to I break my own
|
||
|
style rule.</p>
|
||
|
|
||
|
<h2 id="s3-updating-the-screen"><a href="index.html#s3-updating-the-screen">Updating the Screen</a></h2>
|
||
|
|
||
|
<p>The only thing we need to change for the <code>screen</code> is its initializer:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-initializer</span></i> <span class="paren2">(<span class="code">screen screen-setup</span>)</span>
|
||
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">q+:focus-policy screen</span>)</span> <span class="paren3">(<span class="code">q+:qt.strong-focus</span>)</span>
|
||
|
<span class="paren3">(<span class="code">q+:fixed-size screen</span>)</span> <span class="paren3">(<span class="code">values <span class="special">*width*</span> <span class="special">*height*</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>We've removed the useless <code>window-title</code> that used to be here. Instead we set
|
||
|
the focus policy to the screen to accept all kinds of focus. If you <em>don't</em> set
|
||
|
this the widget will never be able to get focus (and thus receive keyboard
|
||
|
events). We also leave in the size setting. The <code>QMainWindow</code> seems to pick up
|
||
|
this size and scale itself appropriately, which is nice.</p>
|
||
|
|
||
|
<p>That's it for the architectural changes. All the screen's drawing and
|
||
|
input-handling code can remain unchanged.</p>
|
||
|
|
||
|
<h2 id="s4-adding-menus"><a href="index.html#s4-adding-menus">Adding Menus</a></h2>
|
||
|
|
||
|
<p>We'll add a couple of menus to make it a bit easier to use the emulator without
|
||
|
falling back to the Lisp REPL. There's a lot of things we could add, so I'll
|
||
|
just cover a couple basic options.</p>
|
||
|
|
||
|
<h3 id="s5-the-file-menu"><a href="index.html#s5-the-file-menu">The File Menu</a></h3>
|
||
|
|
||
|
<p>Let's start with a simple <code>File</code> menu that will just have two items:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>Load a ROM</li>
|
||
|
<li>Quit the emulator</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>The menu definition is pretty straightforward:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-menu</span></i> <span class="paren2">(<span class="code">main-window File</span>)</span>
|
||
|
<span class="paren2">(<span class="code"><span class="keyword">:item</span> <span class="paren3">(<span class="code"><span class="string">"Load ROM..."</span> <span class="paren4">(<span class="code">ctrl o</span>)</span></span>)</span> <span class="paren3">(<span class="code">load-rom main-window</span>)</span></span>)</span>
|
||
|
<span class="paren2">(<span class="code"><span class="keyword">:item</span> <span class="paren3">(<span class="code"><span class="string">"Quit"</span> <span class="paren4">(<span class="code">ctrl q</span>)</span></span>)</span> <span class="paren3">(<span class="code">die</span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>Note that we use the Windows-centric shortcut key names. Qt will handle
|
||
|
translating those to Mac-friendly versions when running on OS X.</p>
|
||
|
|
||
|
<p>We'll hide the details of loading the ROM in a <code>load-rom</code> helper function:</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">main-window</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">rom <span class="paren5">(<span class="code">get-rom-path main-window</span>)</span></span>)</span></span>)</span>
|
||
|
<span class="paren3">(<span class="code">when rom
|
||
|
<span class="paren4">(<span class="code">chip8::load-rom <span class="paren5">(<span class="code">main-chip main-window</span>)</span> rom</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p><code>load-rom</code> just gets the path to load and loads it into the <code>chip</code> struct,
|
||
|
assuming it's not <code>nil</code>. Once again we use a helper function to hide the
|
||
|
details of getting the path to the ROM, because I'm a firm believer in <a href="https://groups.google.com/forum/message/raw?msg=comp.lang.lisp/9SKZ5YJUmBg/Fj05OZQomzIJ">one
|
||
|
function to a function</a>:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defparameter</span></i> <span class="special">*default-directory*</span>
|
||
|
<span class="paren2">(<span class="code">uiop:native-namestring
|
||
|
<span class="paren3">(<span class="code">asdf:system-source-directory <span class="keyword">:cl-chip8</span></span>)</span></span>)</span></span>)</span>
|
||
|
|
||
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> get-rom-path <span class="paren2">(<span class="code">window</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">path <span class="paren5">(<span class="code">q+:qfiledialog-get-open-file-name
|
||
|
window <span class="comment">; parent widget
|
||
|
</span> <span class="string">"Load ROM"</span> <span class="comment">; dialog title
|
||
|
</span> <span class="special">*default-directory*</span> <span class="comment">; starting directory
|
||
|
</span> <span class="string">"ROM Files (*.rom);;All Files (*)"</span></span>)</span></span>)</span></span>)</span> <span class="comment">; filters
|
||
|
</span> <span class="paren3">(<span class="code"><i><span class="symbol">if</span></i> <span class="paren4">(<span class="code">string= path <span class="string">""</span></span>)</span>
|
||
|
nil
|
||
|
path</span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>Qt has a nice static method <code>QFileDialog.getOpenFileName</code> that we can use to do
|
||
|
the heavy lifting. It takes a parent widget (our main window), a title,
|
||
|
a directory to start in, and a file filter string.</p>
|
||
|
|
||
|
<p>I've used some handy ASDF and UIOP functions to tell the file dialog to start in
|
||
|
the directory where the emulator's code is located, because that's where I store
|
||
|
my own ROMs. Another option would be to have it start in the user's home
|
||
|
directory, or to make the location configurable.</p>
|
||
|
|
||
|
<p>The filter string is actually parsed by Qt, and by default will prevent the user
|
||
|
from selecting any file that doesn't end in <code>.rom</code>. You might want to add a few
|
||
|
more options extensions here if you think people will have named their ROMs
|
||
|
differently. We also add a second filter that will let the user select <em>any</em>
|
||
|
file, in case they have a ROM with a filename we haven't anticipated. The
|
||
|
result looks like this:</p>
|
||
|
|
||
|
<p><a href="http://stevelosh.com/static/images/blog/2017/01/chip8-file-select.png"><img src="http://stevelosh.com/static/images/blog/2017/01/chip8-file-select.png" alt="Screenshot of the file selection dialog"></a></p>
|
||
|
|
||
|
<p>Note that if the user cancels out of the file selection dialog Qt will return an
|
||
|
empty string. We'll check for that and return a more Lispy <code>nil</code> from the
|
||
|
function.</p>
|
||
|
|
||
|
<p>That's it for the <code>File</code> menu. It's basic, but it's a lot nicer to load ROMs
|
||
|
through a normal dialog than to have to poke at the <code>chip</code> in the REPL.</p>
|
||
|
|
||
|
<h3 id="s6-the-display-menu"><a href="index.html#s6-the-display-menu">The Display Menu</a></h3>
|
||
|
|
||
|
<p>Back in the <a href="../../../2016/12/chip8-graphics/index.html">graphics</a> post we saw how some ROMs expect the CHIP-8 to wrap
|
||
|
sprites around the screen when their coordinates get too large, and other ROMs
|
||
|
require that they <em>not</em> wrap. There's no good way to automatically detect this
|
||
|
(aside from hashing particular ROMs and hard-coding the setting for those) so
|
||
|
we'll expose this as an option to the user in a <code>Display</code> menu.</p>
|
||
|
|
||
|
<p>The menu itself is simple — we'll make a submenu that will contain the options
|
||
|
and a helper function to actually do the work:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-menu</span></i> <span class="paren2">(<span class="code">main-window Display</span>)</span>
|
||
|
<span class="paren2">(<span class="code"><span class="keyword">:menu</span> <span class="string">"Screen Wrapping"</span>
|
||
|
<span class="paren3">(<span class="code"><span class="keyword">:item</span> <span class="string">"On"</span> <span class="paren4">(<span class="code">set-screen-wrapping main-window t</span>)</span></span>)</span>
|
||
|
<span class="paren3">(<span class="code"><span class="keyword">:item</span> <span class="string">"Off"</span> <span class="paren4">(<span class="code">set-screen-wrapping main-window nil</span>)</span></span>)</span></span>)</span></span>)</span>
|
||
|
|
||
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> set-screen-wrapping <span class="paren2">(<span class="code">main-window enabled</span>)</span>
|
||
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">chip8::chip-screen-wrapping-enabled <span class="paren4">(<span class="code">main-chip main-window</span>)</span></span>)</span>
|
||
|
enabled</span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>Now we've got a simple little menu for turning screen wrapping off and on:</p>
|
||
|
|
||
|
<p><a href="http://stevelosh.com/static/images/blog/2017/01/chip8-display.png"><img src="http://stevelosh.com/static/images/blog/2017/01/chip8-display.png" alt="Screenshot of the display menu"></a></p>
|
||
|
|
||
|
<p>You might also want to reset the emulator automatically whenever this option is
|
||
|
changed, because toggle screen wrapping as the emulator is running can
|
||
|
produce... <em>interesting</em> results. But I like breaking games in fun ways, so
|
||
|
I left it as-is.</p>
|
||
|
|
||
|
<p>In a perfect world these options wouldn't be vanilla <code>:item</code>s but would instead
|
||
|
be part of a <a href="https://doc.qt.io/qt-4.8/qactiongroup.html"><code>QActionGroup</code></a>. This would tell Qt to treat these
|
||
|
items as a group, put a checkmark next to the currently-selected one, and so on.
|
||
|
Unfortunately Qtools doesn't have a menu content type for action groups and the
|
||
|
thought of implementing <a href="https://github.com/Shinmera/qtools/blob/23e3e44/widget-menu.lisp#L67-L86">something like this</a> makes me nauseas, so
|
||
|
I'm satisfied with the <code>:item</code> kludge.</p>
|
||
|
|
||
|
<h3 id="s7-the-sound-menu"><a href="index.html#s7-the-sound-menu">The Sound Menu</a></h3>
|
||
|
|
||
|
<p>Our final menu will allow the user to select what kind of sound the buzzer
|
||
|
should play, because it would be a shame to let all our work in the <a href="../../../2016/12/chip8-sound/index.html">sound</a>
|
||
|
post go to waste. We'll implement the <code>Sound</code> menu just like the <code>Display</code>
|
||
|
menu:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-menu</span></i> <span class="paren2">(<span class="code">main-window Sound</span>)</span>
|
||
|
<span class="paren2">(<span class="code"><span class="keyword">:menu</span> <span class="string">"Sound Type"</span>
|
||
|
<span class="paren3">(<span class="code"><span class="keyword">:item</span> <span class="string">"Sine"</span> <span class="paren4">(<span class="code">set-sound-type main-window <span class="keyword">:sine</span></span>)</span></span>)</span>
|
||
|
<span class="paren3">(<span class="code"><span class="keyword">:item</span> <span class="string">"Square"</span> <span class="paren4">(<span class="code">set-sound-type main-window <span class="keyword">:square</span></span>)</span></span>)</span>
|
||
|
<span class="paren3">(<span class="code"><span class="keyword">:item</span> <span class="string">"Sawtooth"</span> <span class="paren4">(<span class="code">set-sound-type main-window <span class="keyword">:sawtooth</span></span>)</span></span>)</span>
|
||
|
<span class="paren3">(<span class="code"><span class="keyword">:item</span> <span class="string">"Triangle"</span> <span class="paren4">(<span class="code">set-sound-type main-window <span class="keyword">:triangle</span></span>)</span></span>)</span></span>)</span></span>)</span>
|
||
|
|
||
|
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> set-sound-type <span class="paren2">(<span class="code">main-window type</span>)</span>
|
||
|
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">chip8::chip-sound-type <span class="paren4">(<span class="code">main-chip main-window</span>)</span></span>)</span> type</span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<h2 id="s8-results"><a href="index.html#s8-results">Results</a></h2>
|
||
|
|
||
|
<p>And with that we've got a basic menu system for the emulator:</p>
|
||
|
|
||
|
<p><a href="http://stevelosh.com/static/images/blog/2017/01/chip8-menu.png"><img src="http://stevelosh.com/static/images/blog/2017/01/chip8-menu.png" alt="Screenshot of the full menu"></a></p>
|
||
|
|
||
|
<h2 id="s9-future"><a href="index.html#s9-future">Future</a></h2>
|
||
|
|
||
|
<p>We're nearing the end of the series. The next post will be about adding
|
||
|
a graphical debugging interface, and will probably be the last in the series.</p>
|
||
|
|
||
|
<p>(Unless I get ambitious and try making a curses-based ASCII UI...)</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>
|