<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Nate&#039;s Blog</title>
	<atom:link href="https://oldblog.natebarney.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://oldblog.natebarney.com</link>
	<description>Abandon all hope, ye who enter here</description>
	<lastBuildDate>Thu, 25 Sep 2025 23:08:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>ApOPL3xy Emulator</title>
		<link>https://oldblog.natebarney.com/2025/09/23/apopl3xy-emulator/</link>
					<comments>https://oldblog.natebarney.com/2025/09/23/apopl3xy-emulator/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Wed, 24 Sep 2025 01:35:34 +0000</pubDate>
				<category><![CDATA[ApOPL3xy]]></category>
		<category><![CDATA[electronics]]></category>
		<category><![CDATA[music]]></category>
		<category><![CDATA[opl3]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[projects]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=840</guid>

					<description><![CDATA[Last week, I posted about the ApOPL3xy reaching the version 1.0 milestone. This time, I want to discuss the emulator I wrote for it. I think it&#8217;s pretty cool. The emulator was initially conceived as a way to run the code on a computer with all the debugging tools that I&#8217;m used to having at [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="has-medium-font-size">Last week, I <a href="https://blog.natebarney.com/2025/09/14/apopl3xy-1-0/" data-type="post" data-id="816" target="_blank" rel="noreferrer noopener">posted</a> about the ApOPL3xy reaching the version 1.0 milestone. This time, I want to discuss the emulator I wrote for it. I think it&#8217;s pretty cool. The emulator was initially conceived as a way to run the code on a computer with all the debugging tools that I&#8217;m used to having at my disposal. It was certainly useful for that, but it grew into a project of its own.</p>



<h2 class="wp-block-heading">Demo Videos</h2>



<p>Before I get into some of the more technical details, I want to show some video captures of the emulator running. And if you happen to find yourself thinking that it looks like something you&#8217;d want to play with, I&#8217;m releasing it all as open source. There will be some links <a href="#download">later in the post</a> where you can download the emulator, and for the ambitious, all the files needed to build your own hardware ApOPL3xy are available as well.</p>



<h3 class="wp-block-heading">VGM Player</h3>



<p>One of the most iconic pieces composed for the OPL3 is &#8220;At Doom&#8217;s Gate,&#8221; the music for the first level (Episode 1 Mission1, or E1M1) of Doom. <a href="https://vgmrips.net/" data-type="link" data-id="https://vgmrips.net/" target="_blank" rel="noreferrer noopener">vgmrips.net</a> has the <a href="https://vgmrips.net/packs/pack/doom-pc" data-type="link" data-id="https://vgmrips.net/packs/pack/doom-pc" target="_blank" rel="noreferrer noopener">VGM capture of this song</a> available for download, and I can&#8217;t think of a better song to demonstrate the VGM player feature. Note that most, if not all, VGM files only use the first two output channels, since no 4-channel OPL3 sound cards were commonly available.</p>



<p>The numbered buttons below the display correspond to the colored buttons on the hardware, and the two groups of three buttons, labeled ↺, ↻, and ☟, represent the rotary encoders being turned counter-clockwise, turned clockwise, and pressed like a button, respectively. At some point, I may improve these to be a more skeuomorphic control, but the buttons get the job done. </p>



<figure class="wp-block-video max640"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-At-Dooms-Gate.mp4" playsinline></video><figcaption class="wp-element-caption">Doom: At Doom&#8217;s Gate (E1M1)</figcaption></figure>



<h3 class="wp-block-heading">MIDI Player</h3>



<p>My last post showed the new MIDI file player feature on the hardware ApOPL3xy, but due to difficulties filming the backlit LCD screen, it wasn&#8217;t easy to see what was on the display. The next video shows the emulator playing a MIDI version of the Doctor Who theme music. As before,  I arbitrarily assigned MIDI channels to output channels to make the VU meters a bit more interesting. </p>



<figure class="wp-block-video max640"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-Doctor-Who.mp4" playsinline></video><figcaption class="wp-element-caption">Doctor Who Theme</figcaption></figure>



<h3 class="wp-block-heading">Loop Controls</h3>



<p>Between the last post and this one, I added loop controls to both the VGM and MIDI players. It&#8217;s a small feature, but potentially useful. The loop indicator is just to the left of the total time in the player interface.  It starts out as a right-pointing arrow, meaning no loop. The other loop modes are single loop, represented by an arrow that curves from the right to the left, and continual loop, represented by two arrows pointing at each other&#8217;s tail. The 5 button cycles through these modes. </p>



<p>This video demonstrates the single loop mode being used for a MIDI file with a version of the Pac-Man music. It also shows the page down menu navigation function, which is mapped to button 9.</p>



<figure class="wp-block-video max640"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-Pac-Man.mp4" playsinline></video><figcaption class="wp-element-caption">Pac-Man</figcaption></figure>



<h3 class="wp-block-heading">MIDI Input</h3>



<p>My primary motivation for building the ApOPL3xy was to enable MIDI input to control an OPL3 chip. So far, I&#8217;ve demonstrated playback functionality, but I want to show the live MIDI capability as well. Unfortunately, I&#8217;m not very skilled at the piano, so I just play a few scales in the following video. The piano keyboard in the video is an open source tool called <a href="https://vmpk.sourceforge.io/" data-type="link" data-id="https://vmpk.sourceforge.io/" target="_blank" rel="noreferrer noopener">VMPK</a> (Virtual MIDI Piano Keyboard), which I did not write, but am simply using.</p>



<p>The video shows selecting a MIDI input source for the emulator and using the Channel Editor feature to show the patches assigned to MIDI channels. Then, a few scales are played using a few different patches.</p>



<figure class="wp-block-video max640"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-MIDI-Input.mp4" playsinline></video><figcaption class="wp-element-caption">MIDI Input</figcaption></figure>



<h3 class="wp-block-heading">MIDI Output</h3>



<p>The ApOPL3xy also has a MIDI output port, which sends MIDI messages from the MIDI player to an external device. It also optionally echoes messages received from the MIDI input port, but that echoing is not demonstrated here. What is demonstrated is using VMPK as an external device to visualize the notes being played. In this video, VMPK is configured to display each MIDI channel with a different color. The MIDI file being played is the <a href="https://www.youtube.com/watch?v=7HV3oyxr0Eg" data-type="link" data-id="https://www.youtube.com/watch?v=7HV3oyxr0Eg" target="_blank" rel="noreferrer noopener">battle music</a> from Final Fantasy VII.</p>



<figure class="wp-block-video max640"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-Final-Fantasy-VII.mp4" playsinline></video><figcaption class="wp-element-caption">Final Fantasy VII Battle Theme</figcaption></figure>



<h2 class="wp-block-heading">Implementation Details</h2>



<p>The ApOPL3xy firmware interacts with several pieces of hardware, all of which need to be emulated to be able to run the firmware purely in software.</p>



<h3 class="wp-block-heading">Microcontroller</h3>



<p>The hardware ApOPL3xy has an <a href="https://www.microchip.com/en-us/product/ATmega1284p" data-type="link" data-id="https://www.microchip.com/en-us/product/ATmega1284p" target="_blank" rel="noreferrer noopener">ATmega1284p</a> microcontroller to run the firmware. Because the main goal of the emulator is to support debugging, this microcontroller is not emulated. Instead of running the compiled AVR firmware on an emulated ATmega1284p, the firmware is recompiled to native code for the computer running the emulator, with some conditionally-compiled hooks in a few places to tie in the other emulated hardware.</p>



<h3 class="wp-block-heading">OPL3</h3>



<p>This is probably the most important piece of hardware to emulate, and also the most daunting. Fortunately, the open source project <a href="https://www.dosbox.com/" data-type="link" data-id="https://www.dosbox.com/" target="_blank" rel="noreferrer noopener">DOSBox</a> has code to emulate the <a href="https://en.wikipedia.org/wiki/Yamaha_OPL#OPL2" target="_blank" rel="noreferrer noopener">OPL2</a> and <a href="https://en.wikipedia.org/wiki/Yamaha_OPL#OPL3" data-type="link" data-id="https://en.wikipedia.org/wiki/Yamaha_OPL#OPL3" target="_blank" rel="noreferrer noopener">OPL3</a> chips, so I adapted it for my use. This code exposes a function to set registers on the emulated OPL chip, which I used instead of the <a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface" target="_blank" rel="noreferrer noopener">SPI</a> interface <a href="https://blog.natebarney.com/2023/10/08/apopl3xy-hardware-design/#custom-spi-interface" data-type="link" data-id="https://blog.natebarney.com/2023/10/08/apopl3xy-hardware-design/#custom-spi-interface" target="_blank" rel="noreferrer noopener">I built</a> for the hardware ApOPL3xy for that purpose. The DOSBox code also exposes a function to get output audio samples, which I used to populate the audio buffers (more on this later).</p>



<p>The OPL3 supports up to 4 channels of audio output, but sound cards incorporating the chip rarely if ever used more than 2. DOSBox didn&#8217;t bother implementing support for channels 3 and 4, so I modified it to add that support. It was actually easier than I expected. Since they had already gone to the trouble to implement 2 channels, most of the work involved increasing some array sizes and loop bounds from 2 to 4.</p>



<h3 class="wp-block-heading">LCD Character Display</h3>



<p>The standard 20&#215;4 LCD character display used by the ApOPL3xy is based on a driver chip called the <a href="https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller" target="_blank" rel="noreferrer noopener">HD44780</a>. I broke the emulation of this device up into two parts: the HD44780 emulation and the drawing of the LCD panel itself.</p>



<p>The HD44780 supports several commands which are sent by writing to its control and data registers. To emulate this chip, I wrote a C++ class to represent all of its internal registers, RAM, and ROM, and then implemented each command as a separate function that operates on the internal state. I then implemented a wrapper function which accepts commands or data, and dispatches to the correct underlying function(s). This is called by the emulated firmware instead of using SPI, similar to the OPL3.</p>



<p>For the HD44780 emulation, I also wrote a function to retrieve the output state, e.g., which pixels are on and which are off. This is used by the LCD panel emulation to actually display the pixels. I used the open-source, cross-platform GUI library <a href="https://wxwidgets.org/" target="_blank" rel="noreferrer noopener">wxWidgets</a> to implement the emulator&#8217;s GUI, and the LCD panel is implemented as a custom widget that does its own drawing with the graphics routines provided by the library.</p>



<h3 class="wp-block-heading">Audio Output</h3>



<p>To implement audio output for the emulator, I used the open-source, cross-platform library <a href="https://www.portaudio.com/" target="_blank" rel="noreferrer noopener">PortAudio</a>. This library abstracts all the platform-specific audio APIs and presents a unified interface to interact with the audio system. With it, the emulator is able to select audio output devices, sample rate, latency, and number of channels. (These options are exposed to the user via the Audio Settings dialog window.) To play the synthesized audio, the emulator extracts audio samples from the emulated OPL3 and sends them to the host audio system using PortAudio&#8217;s interface.</p>



<h3 class="wp-block-heading">VU Meters and Gain Controls</h3>



<p>Each output channel on the physical ApOPL3xy has a <a href="https://en.wikipedia.org/wiki/VU_meter" target="_blank" rel="noreferrer noopener">VU meter</a> (well, not a true VU meter, just a linear amplitude display) and a gain control. Emulating this in wxWidgets was pretty straightforward. These are in separate tool windows that the user can display and dismiss.</p>



<p>The gain controls are slider widgets. To implement gain, the emulator reads the values from the sliders and uses them to adjust the audio samples before sending them to PortAudio for playback.</p>



<p>The VU meters are custom widgets that draw themselves based on a given amplitude. The emulator determines the current amplitude of each channel from the audio samples before sending them to PortAudio, and sends the amplitude values to their respective VU meter for display.</p>



<h3 class="wp-block-heading">MIDI Input and Output</h3>



<p>To emulate the MIDI input and output ports, I used the open-source, cross-platform library <a href="https://github.com/thestk/rtmidi" target="_blank" rel="noreferrer noopener">RtMidi</a>. This library abstracts the host computer&#8217;s MIDI system, and enables enumeration of, reading from, and writing to system MIDI ports. The MIDI In and MIDI Out dialogs allow the user to select which, if any, MIDI ports to read from and write to.</p>



<p>If a MIDI input port is selected, MIDI events are read from it and injected into the firmware&#8217;s MIDI queue, instead of reading from the microcontroller&#8217;s serial interface. Similarly, instead of writing output MIDI events to the microcontroller&#8217;s serial interface, the emulator writes them to the host MIDI port selected for output.</p>



<h3 class="wp-block-heading">MicroSD Card</h3>



<p>The hardware ApOPL3xy includes a MicroSD card reader, which is read by the firmware using the open-source <a href="https://github.com/greiman/SdFat" target="_blank" rel="noreferrer noopener">SdFat</a> library. This library abstracts both the SPI messaging needed to interact with the card itself, as well as handling the FAT filesystem that keeps track of which files are where in the card&#8217;s storage.</p>



<p>To emulate this, I wrote a mock implementation of the subset of SdFat&#8217;s interface that the firmware uses, but instead of accessing an SD card, it uses the standard C++ <a href="https://en.cppreference.com/w/cpp/filesystem.html" target="_blank" rel="noreferrer noopener"><code>std::filesystem</code></a> library to access the host&#8217;s filesystem through the host OS. The user can &#8220;insert&#8221; a virtual SD card by using the Mount SD Card dialog window to select the directory to serve as the root directory of the virtual card. The user can &#8220;eject&#8221; a virtual SD card by using the Unmount SD Card menu option.</p>



<h3 class="wp-block-heading">SRAM and EEPROM</h3>



<p>The ATmega1284p microcontroller has 16 kiB of RAM and 4 kiB of EEPROM storage built in. This isn&#8217;t enough for everything I wanted the ApOPL3xy to be able to do, so I added SPI <a href="https://en.wikipedia.org/wiki/Static_random-access_memory" target="_blank" rel="noreferrer noopener">SRAM</a> and <a href="https://en.wikipedia.org/wiki/EEPROM" target="_blank" rel="noreferrer noopener">EEPROM</a> chips (128 kiB each) to the design. The SRAM on the microcontroller itself doesn&#8217;t need to be emulated; the emulator just uses the host&#8217;s system RAM for that. The microcontroller&#8217;s EEPROM isn&#8217;t used by the firmware at all, so that doesn&#8217;t need to be emulated either. The add-on chips, however, do need to be emulated.</p>



<p>The SRAM chip was very simple to emulate. I simply allocated an array of the right size and provided functions to read from and write to it. The emulator uses these functions instead of sending SPI commands like the firmware does on physical hardware. The EEPROM was very similar,  except that a file is used instead an in-memory array, since EEPROMs are nonvolatile.</p>



<p>If the emulator finds no EEPROM file in the expected location on startup,  it generates a default one and uses that. This has turned out to be useful for setting up a hardware ApOPL3xy. Since the format of the EEPROM is identical between the emulator and the hardware, one can copy the EEPROM file from the emulator to a MicroSD card and load it onto the hardware.</p>



<h3 class="wp-block-heading">Input Controls</h3>



<p>The ApOPL3xy has 10 buttons and 2 rotary encoders (which can also act as buttons) which are used to navigate menus and issue commands. The hardware version of these are connected to a shift register, which the firmware reads via SPI, interprets, debounces, and adds to an input event queue. The emulator instead provides GUI buttons for these, and pressing a button injects an input event directly into the queue. These buttons are also mapped to (hopefully) intuitive keyboard shortcuts.</p>



<h3 class="wp-block-heading">Miscellaneous</h3>



<p>There were a few other things that needed to be replaced or mocked to be able to compile and run the firmware in an emulated context.</p>



<ul class="wp-block-list">
<li>The microcontroller framework used by the ApOPL3xy&#8217;s firmware provides functions for timing that use the microcontroller&#8217;s on-chip timers. These were replaced with versions that use the standard C++ <a href="https://en.cppreference.com/w/cpp/header/chrono.html" target="_blank" rel="noreferrer noopener"><code>std::chrono</code></a> library instead.</li>



<li>The AVR architecture used by the ATmega1284p requires the use of special functions to read data out of program code (e.g. constant string data) instead of SRAM. These were replaced with simple pass-through functions, since the host machine for the emulator doesn&#8217;t have this restriction.</li>



<li>Various utility functions like <code>min()</code> and <code>max()</code> provided by the framework needed to be reimplemented.</li>
</ul>



<h2 class="wp-block-heading" id="download">Download Links</h2>



<h3 class="wp-block-heading">Emulator</h3>



<p>If you&#8217;re interested in trying out the ApOPL3xy emulator, you can download pre-compiled binaries here:</p>



<ul class="wp-block-list">
<li><a href="https://www.natebarney.com/files/apopl3xy/emulator/1.0.1/apopl3xy-emulator-1.0.1-win.zip" data-type="link" data-id="https://www.natebarney.com/files/apopl3xy/emulator/1.0.1/apopl3xy-emulator-1.0.1-win.zip">ApOPL3xy Emulator v1.0.1 (Windows)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/emulator/1.0.1/apopl3xy-emulator-1.0.1-mac.zip">ApOPL3xy Emulator v1.0.1 (MacOS)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/emulator/1.0.1/apopl3xy-emulator-1.0.1-ubuntu24.04.zip">ApOPL3xy Emulator v1.0.1 (Ubuntu 24.04)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/emulator/1.0.1/apopl3xy-emulator-1.0.1-fedora42.zip">ApOPL3xy Emulator v1.0.1 (Fedora 42)</a></li>
</ul>



<p>Note that on Windows and MacOS, the fact that this binary isn&#8217;t signed may cause the operating system to complain. To get past this on MacOS, see <a href="https://support.apple.com/guide/mac-help/open-a-mac-app-from-an-unknown-developer-mh40616/mac">https://support.apple.com/guide/mac-help/open-a-mac-app-from-an-unknown-developer-mh40616/mac</a>. Microsoft doesn&#8217;t seem to have a good page on this, but here&#8217;s a potentially helpful <a href="https://www.google.com/search?q=windows+unknown+publisher">google search</a>.</p>



<h3 class="wp-block-heading">Music Files</h3>



<p>Here are some music files that you can play using the ApOPL3xy:</p>



<ul class="wp-block-list">
<li><a href="https://www.natebarney.com/files/apopl3xy/music/vgm.zip">VGM Files</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/music/vgm.zip" data-type="link" data-id="https://www.natebarney.com/files/apopl3xy/music/vgm.zip">MIDI Files</a></li>
</ul>



<h3 class="wp-block-heading">Source Code</h3>



<p>The combined source code for the emulator and firmware is available below. It&#8217;s released under the terms of the MIT License, except for the OPL emulation code from DOSBox, which is released under the LGPL 2.1 license. I&#8217;ll probably post this to github at some point, but the code is a bit messy at the moment, and I might like to clean it up first.</p>



<ul class="wp-block-list">
<li><a href="https://www.natebarney.com/files/apopl3xy/firmware/1.0.1/apopl3xy-firmware-1.0.1-src.zip" data-type="link" data-id="https://www.natebarney.com/files/apopl3xy/firmware/1.0.1/apopl3xy-firmware-1.0.1-src.zip">ApOPL3xy Emulator and Firmware v1.0.1 (Source Code)</a></li>
</ul>



<h3 class="wp-block-heading">Hardware Design Files</h3>



<p>If you&#8217;re interested and brave enough to want to build your own hardware ApOPL3xy, here are the files you&#8217;ll need. Note that the documentation is rather lacking at the moment, but I think someone determined enough could figure it out. If you try and have difficulties, let me know and I&#8217;ll be happy to help.</p>



<ul class="wp-block-list">
<li><a href="https://www.natebarney.com/files/apopl3xy/pcb/rev3/apopl3xy-pcb-rev3-gerbers.zip">ApOPL3xy PCB rev3 (Gerbers)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/pcb/rev3/apopl3xy-bill_of_materials-rev3.zip">ApOPL3xy PCB rev3 (Bill of Materials)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/pcb/rev3/apopl3xy-schematic-rev3.zip">ApOPL3xy PCB rev3 (Schematic)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/pcb/rev3/apopl3xy-pcb-rev3-kicad.zip">ApOPL3xy PCB rev3 (KiCad Design Files)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/firmware/1.0.1/apopl3xy-firmware-1.0.1-hex.zip">ApOPL3xy Firmware v1.0.1 (HEX File)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/firmware/1.0.1/apopl3xy-eeprom-1.0.1.zip">ApOPL3xy EEPROM Image v1.0.1 (BIN File)</a></li>



<li><a href="https://www.natebarney.com/files/apopl3xy/firmware/1.0.1/apopl3xy-manual-1.0.1.zip">ApOPL3xy User Manual v1.0.1 (PDF File)</a></li>
</ul>



<h2 class="wp-block-heading">Closing Thoughts</h2>



<p>I&#8217;m quite pleased with how this project turned out. What began as an attempt to make debugging easier grew into something kinda cool on its own. It also enables me to share the ApOPL3xy with more people than I would have otherwise. If you do try it out, I&#8217;d love to hear what you think. I have my own ideas of what could be improved, but there&#8217;s no substitute for real user feedback.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2025/09/23/apopl3xy-emulator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-At-Dooms-Gate.mp4" length="4037082" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-Doctor-Who.mp4" length="3999713" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-Pac-Man.mp4" length="904541" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-MIDI-Input.mp4" length="2139536" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Emulator-Final-Fantasy-VII.mp4" length="4748167" type="video/mp4" />

			</item>
		<item>
		<title>ApOPL3xy 1.0</title>
		<link>https://oldblog.natebarney.com/2025/09/14/apopl3xy-1-0/</link>
					<comments>https://oldblog.natebarney.com/2025/09/14/apopl3xy-1-0/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Mon, 15 Sep 2025 01:04:55 +0000</pubDate>
				<category><![CDATA[ApOPL3xy]]></category>
		<category><![CDATA[electronics]]></category>
		<category><![CDATA[music]]></category>
		<category><![CDATA[opl3]]></category>
		<category><![CDATA[projects]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=816</guid>

					<description><![CDATA[It’s been a long journey, but I finally have the ApOPL3xy hardware and software to the point where I can call it version 1.0.]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s been a long journey, but I finally have the ApOPL3xy hardware and software to the point where I can call it version 1.0. I&#8217;ll make another post in a few days with links to the firmware source code, the PCB design files, and emulator binaries. (Update: here&#8217;s that <a href="https://blog.natebarney.com/2025/09/23/apopl3xy-emulator/" data-type="post" data-id="840" target="_blank" rel="noreferrer noopener">post</a>.) But for now, I just want to show it off a little.</p>



<h3 class="wp-block-heading">MIDI Player</h3>



<p>One of the things I&#8217;ve added to the firmware recently is the ability to play MIDI files from the SD card. Here are a few videos of this in action. These videos are a little dark. It was hard to find a balance between blowing out the display and making the videos way too dark, so I did the best I could. I mapped the MIDI channels to somewhat arbitrary output audio channels to make the VU meters a little more interesting.</p>



<figure class="wp-block-video max640"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Video-Killed-the-Radio-Star.mp4" playsinline></video><figcaption class="wp-element-caption">Video Killed the Radio Star</figcaption></figure>



<figure class="wp-block-video max640"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Star-Wars-Medley.mp4" playsinline></video><figcaption class="wp-element-caption">Star Wars Medley</figcaption></figure>



<figure class="wp-block-video max640"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Super-Mario-World.mp4" playsinline></video><figcaption class="wp-element-caption">Super Mario World</figcaption></figure>



<h3 class="wp-block-heading">PCB Photos</h3>



<p>Here are some high(ish) resolution photos of the assembled Rev 3 board, for those who are into that sort of thing. Also included is a photo of the bodges necessary on the Rev 2 board. To view the images at full resolution, right-click (or long-press) the image and select &#8220;Open Image in New Tab &#8221; or similar.</p>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="2560" height="1920" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-scaled.jpg" alt="ApOPL3xy Rev 3 PCB (Front)" class="wp-image-825" style="width:640px" title="" srcset="https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-scaled.jpg 2560w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-300x225.jpg 300w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-1024x768.jpg 1024w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-768x576.jpg 768w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-1536x1152.jpg 1536w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-2048x1536.jpg 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /><figcaption class="wp-element-caption">ApOPL3xy Rev 3 PCB (Front)</figcaption></figure>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="2560" height="1920" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-Modules-Removed-scaled.jpg" alt="ApOPL3xy Rev 3 PCB (Front, Modules Removed)" class="wp-image-826" style="width:640px" srcset="https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-Modules-Removed-scaled.jpg 2560w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-Modules-Removed-300x225.jpg 300w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-Modules-Removed-1024x768.jpg 1024w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-Modules-Removed-768x576.jpg 768w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-Modules-Removed-1536x1152.jpg 1536w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Front-Modules-Removed-2048x1536.jpg 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /><figcaption class="wp-element-caption">ApOPL3xy Rev 3 PCB (Front, Modules Removed)</figcaption></figure>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="2560" height="1920" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Back-scaled.jpg" alt="ApOPL3xy Rev 3 PCB (Back)" class="wp-image-824" style="width:640px" title="" srcset="https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Back-scaled.jpg 2560w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Back-300x225.jpg 300w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Back-1024x768.jpg 1024w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Back-768x576.jpg 768w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Back-1536x1152.jpg 1536w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev3-PCB-Back-2048x1536.jpg 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /><figcaption class="wp-element-caption">ApOPL3xy Rev 3 PCB (Back)</figcaption></figure>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="2560" height="1920" src="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev2-PCB-Bodges-scaled.jpg" alt="ApOPL3xy Rev 2 PCB (Bodges)" class="wp-image-823" style="width:640px" srcset="https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev2-PCB-Bodges-scaled.jpg 2560w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev2-PCB-Bodges-300x225.jpg 300w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev2-PCB-Bodges-1024x768.jpg 1024w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev2-PCB-Bodges-768x576.jpg 768w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev2-PCB-Bodges-1536x1152.jpg 1536w, https://oldblog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Rev2-PCB-Bodges-2048x1536.jpg 2048w" sizes="auto, (max-width: 2560px) 100vw, 2560px" /><figcaption class="wp-element-caption">ApOPL3xy Rev 2 PCB Bodges</figcaption></figure>



<h3 class="wp-block-heading">Future Work</h3>



<p>Now that the ApOPL3xy has reached this milestone, I&#8217;ll probably leave it alone for a while. But, such things are never truly finished. I have several ideas for user interface improvements, such as patch and bank copying, M3U playlist support, and most interesting to me, a drum machine mode (suggested by a co-worker). I&#8217;m sure I&#8217;ll come back to this eventually to make these and other improvements, but it sure feels good to be able to call it done (for now)!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2025/09/14/apopl3xy-1-0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Video-Killed-the-Radio-Star.mp4" length="60705592" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Star-Wars-Medley.mp4" length="47801042" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/09/ApOPL3xy-Super-Mario-World.mp4" length="38279552" type="video/mp4" />

			</item>
		<item>
		<title>Damon PCM</title>
		<link>https://oldblog.natebarney.com/2025/03/31/damon-pcm/</link>
					<comments>https://oldblog.natebarney.com/2025/03/31/damon-pcm/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Mon, 31 Mar 2025 04:54:51 +0000</pubDate>
				<category><![CDATA[Retro Computers]]></category>
		<category><![CDATA[6502]]></category>
		<category><![CDATA[assembly]]></category>
		<category><![CDATA[computers]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[music]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[projects]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=789</guid>

					<description><![CDATA[I’ve made and posted a new version of Damon: The Rocket Jockey which adds an updated music track.]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve made and posted a new version of <a href="https://blog.natebarney.com/2024/06/02/damon-the-rocket-jockey/" data-type="post" data-id="646" target="_blank" rel="noreferrer noopener">Damon: The Rocket Jockey</a> which adds an updated music track.</p>



<h3 class="wp-block-heading">Updated Music</h3>



<p>Off and on over the past several months, I&#8217;ve been working on a 3D <a href="https://en.wikipedia.org/wiki/Voxel" data-type="link" data-id="https://en.wikipedia.org/wiki/Voxel" target="_blank" rel="noreferrer noopener">voxel</a>-art version of Damon for modern computers. It&#8217;s almost finished, but it has been for a while. One day I&#8217;ll finish it and make a post about it. It just needs a menu screen for configuring the controls. But, that&#8217;s not what this post is about.</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="576" src="https://blog.natebarney.com/wp-content/uploads/2025/03/damon3d-screenshot-1024x576.png" alt="" class="wp-image-790" style="aspect-ratio:16/9;object-fit:cover;width:960px" srcset="https://oldblog.natebarney.com/wp-content/uploads/2025/03/damon3d-screenshot-1024x576.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2025/03/damon3d-screenshot-300x169.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2025/03/damon3d-screenshot-768x432.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2025/03/damon3d-screenshot-1536x864.png 1536w, https://oldblog.natebarney.com/wp-content/uploads/2025/03/damon3d-screenshot-2048x1152.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Damon 3D Screenshot</figcaption></figure>



<p>I mention the 3D game because as part of that project, I wrote an updated version of the game&#8217;s music. The original music, being for a 1980&#8217;s 8-bit computer, was a bit simple and repetitive. I thought I might be able to jazz it up a bit, so I gave it a shot in <a href="https://www.ableton.com/en/live/what-is-live/" data-type="link" data-id="https://www.ableton.com/en/live/what-is-live/" target="_blank" rel="noreferrer noopener">Ableton Live</a>. I&#8217;m pretty pleased with the result.</p>



<figure class="wp-block-audio"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2025/03/damon3d-music.mp3"></audio></figure>



<h3 class="wp-block-heading">Back-Porting</h3>



<p>But, as I said, this post isn&#8217;t about the 3D game; it&#8217;s about the version for the Commander X16. I was looking through the <a href="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md" data-type="link" data-id="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md" target="_blank" rel="noreferrer noopener">VERA</a> documentation recently, and I learned that it supports <a href="https://en.wikipedia.org/wiki/Pulse-code_modulation" data-type="link" data-id="https://en.wikipedia.org/wiki/Pulse-code_modulation" target="_blank" rel="noreferrer noopener">PCM</a> audio. (VERA stands for Versatile Embedded Retro Adapter, and it&#8217;s the primary graphics and sound chip used by the Commander X16. PCM stands for Pulse-Code Modulation, which is a way of encoding digital audio.) I thought it would be fun to see if I could get the Commander X16 version of the game to play the music from the 3D game. There were a few challenges to overcome, but I was ultimately able to get it working.</p>



<h3 class="wp-block-heading">Resolution Reduction</h3>



<p>PCM audio is made up of a sequence of &#8220;samples&#8221;, each of which represents the value of the audio waveform at an instant of time. For example, CD-quality audio and the Damon 3D music both use two channels of 16-bit samples (-32,768 to 32,767) at 44,100 Hz (samples per second). Doing the math, this ends up taking 176,400 bytes <em>per second</em>, or just over 10.5 MB for a minute of audio. While this is trivial for modern computers, it&#8217;s way too much for something like the Commander X16.</p>



<p>The VERA PCM system supports a sample rate of up to 48,828.125 Hz (25 MHz / 512), and the rate can be configured by specifying a value between 0 and 128, inclusive, scaling linearly between 0 Hz (no playback) and the maximum rate. After playing around a bit, I settled on the value 21, which gives a rate of approximately 8,010.86 Hz. I used <a href="https://www.audacityteam.org/" target="_blank" rel="noreferrer noopener">Audacity</a> to resample the audio from 44,100 Hz to 8,011 Hz, which is close enough.</p>



<p>With this sample rate reduction, by mixing the stereo down to mono, and by using 8-bit samples instead of 16-bit, 1 second of audio can be brought from 176,400 bytes/second down to 8,011 bytes/second, a roughly 95.5% reduction in size! This obviously reduces the audio quality, but surprisingly, it sounds pretty okay despite the severe reduction in resolution.</p>



<figure class="wp-block-audio"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2025/03/damon-x16-music-pcm.wav"></audio></figure>



<p>One interesting thing to note is that the <a href="https://en.wikipedia.org/wiki/WAV" target="_blank" rel="noreferrer noopener">WAV file format</a> treats 8-bit samples as unsigned <a href="https://en.wikipedia.org/wiki/Offset_binary" target="_blank" rel="noreferrer noopener">offset binary</a>, but VERA treats 8-bit samples as signed <a href="https://en.wikipedia.org/wiki/Two%27s_complement" target="_blank" rel="noreferrer noopener">two&#8217;s complement</a>. To handle this difference, it was necessary to write a python script to parse and convert the PCM data from the WAV files.</p>



<h3 class="wp-block-heading">Tempo Adjustment</h3>



<p>The new version of the music was rendered at 120 bpm. The original music was a bit faster, at 148 bpm. Since I wanted the player to be able to switch between the two versions of the music, I needed the two versions&#8217; tempos to match up. With all the extra percussion in the new version, I felt like it sounded right at 120 bpm, so I adjusted the game code to generate the older music at that tempo as well.</p>



<figure class="wp-block-audio"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2025/03/damon-x16-music-orig-148bpm.wav"></audio><figcaption class="wp-element-caption">Damon X16 Original Music (148 bpm)</figcaption></figure>



<figure class="wp-block-audio"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2025/03/damon-x16-music-orig-120bpm.wav"></audio><figcaption class="wp-element-caption">Damon X16 Original Music (120 bpm)</figcaption></figure>



<p>I think the older music is a little better at the faster tempo, but that would probably be too fast for the new music, and it seems more important to have the two versions sync up.</p>



<h3 class="wp-block-heading">Clip Sequences</h3>



<p>To reduce duplication of redundant sections of audio, and to have more control over what parts of the music get played when, I broke the audio up into 1-measure clips, which at 120 bpm are 2 seconds each. For example, the music includes several measures of the bass line playing with no melody over it. There&#8217;s no need to store more than one of those. Similarly, other musical phrases are repeated in the song, and the game only stores a single copy of each distinct phrase.</p>



<p>There are a few other special-purpose audio clips, such as the ominous drone as the letters slide in on the title screen, and the level start and level complete music. Each of these is stored in its own file as well.</p>



<p>The audio clip files are stored in the same filesystem directory as the main program, and the first thing the game does is load each of them from disk into high memory. A stock X16 has 512 kiB of memory, and this is expandable to 2 MiB. All the clips together, after the resolution reduction, use about 220 kiB, so there was no problem fitting them all in. High memory is <a href="https://en.wikipedia.org/wiki/Bank_switching" data-type="link" data-id="https://en.wikipedia.org/wiki/Bank_switching" target="_blank" rel="noreferrer noopener">banked</a> in the Commander X16, each bank appearing at <kbd>$A000</kbd>&#8211;<kbd>$C000</kbd> when it&#8217;s selected. This didn&#8217;t present significant difficulty, as the PCM data is mostly read sequentially.</p>



<p>To handle the sequencing of audio clips I wrote a small interpreter with instructions to do things like &#8220;play this clip&#8221; or &#8220;jump to this other part of the sequence&#8221;, which made it much simpler to manage the clips and play the right one at the right time. I discussed the interpreter technique briefly in my <a href="https://blog.natebarney.com/2024/06/02/damon-the-rocket-jockey/#Interpreters" data-type="link" data-id="https://blog.natebarney.com/2024/06/02/damon-the-rocket-jockey/#Interpreters" target="_blank" rel="noreferrer noopener">previous post</a>. If you&#8217;d like more information about this, you&#8217;re welcome to check out the source code on <a href="https://github.com/natebarney/damon-the-rocket-jockey" target="_blank" rel="noreferrer noopener">github</a>, or ping me directly.</p>



<h3 class="wp-block-heading">Gameplay Video</h3>



<p>Here&#8217;s a short recording I made with the X16 emulator of the game using the new music. To compare this to the previous music, check out the gameplay video in my <a href="https://blog.natebarney.com/2024/06/02/damon-the-rocket-jockey/#The_Game" data-type="link" data-id="https://blog.natebarney.com/2024/06/02/damon-the-rocket-jockey/#The_Game" target="_blank" rel="noreferrer noopener">previous post</a>.</p>



<figure class="wp-block-video aligncenter"><video height="1080" style="aspect-ratio: 1920 / 1080;" width="1920" controls src="https://blog.natebarney.com/wp-content/uploads/2025/03/damon-pcm.mp4"></video><figcaption class="wp-element-caption">Damon: The Rocket Jocky Gameplay with PCM Music</figcaption></figure>



<h3 class="wp-block-heading">Conclusion</h3>



<p>I&#8217;m quite pleased with how this experiment turned out. I think it adds a bit more fun to the game. If you&#8217;d like to try it yourself, you can find it at <a href="http://natebarney.com/damon" data-type="link" data-id="natebarney.com/damon" target="_blank" rel="noreferrer noopener">natebarney.com/damon</a> or on the <a href="https://cx16forum.com/forum/viewtopic.php?t=7656" target="_blank" rel="noreferrer noopener">Commander X16 forums</a>. The source code is available at <a href="https://github.com/natebarney/damon-the-rocket-jockey" target="_blank" rel="noreferrer noopener">github.com/natebarney/damon-the-rocket-jockey</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2025/03/31/damon-pcm/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/03/damon3d-music.mp3" length="1512176" type="audio/mpeg" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/03/damon-x16-music-pcm.wav" length="756816" type="audio/wav" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/03/damon-x16-music-orig-148bpm.wav" length="568950" type="audio/wav" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/03/damon-x16-music-orig-120bpm.wav" length="710966" type="audio/wav" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2025/03/damon-pcm.mp4" length="2646148" type="video/mp4" />

			</item>
		<item>
		<title>Damon: The Rocket Jockey</title>
		<link>https://oldblog.natebarney.com/2024/06/02/damon-the-rocket-jockey/</link>
					<comments>https://oldblog.natebarney.com/2024/06/02/damon-the-rocket-jockey/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Sun, 02 Jun 2024 23:16:01 +0000</pubDate>
				<category><![CDATA[Retro Computers]]></category>
		<category><![CDATA[6502]]></category>
		<category><![CDATA[assembly]]></category>
		<category><![CDATA[commander-x16]]></category>
		<category><![CDATA[computers]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[projects]]></category>
		<category><![CDATA[software]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=646</guid>

					<description><![CDATA[About two months ago, I received in the mail my very own Commander X16 computer. This is a modern 8-bit computer envisioned and produced by David Murray (a.k.a The 8-Bit Guy) along with a thriving community. It's inspired by Commodore computers from the 1980's, primarily the Commodore 64. I hooked it up and started playing with it...]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Introduction</h2>



<p>About two months ago, I received in the mail my very own <a href="https://www.commanderx16.com/" target="_blank" rel="noreferrer noopener">Commander X16</a> computer. This is a modern 8-bit computer envisioned and produced by David Murray (a.k.a <a href="https://www.youtube.com/@The8BitGuy" data-type="link" data-id="https://www.youtube.com/@The8BitGuy" target="_blank" rel="noreferrer noopener">The 8-Bit Guy</a>) along with a <a href="https://cx16forum.com/forum/" data-type="link" data-id="https://cx16forum.com/forum/" target="_blank" rel="noreferrer noopener">thriving community</a>. It&#8217;s inspired by Commodore computers from the 1980&#8217;s, primarily the <a href="https://en.wikipedia.org/wiki/Commodore_64" target="_blank" rel="noreferrer noopener">Commodore 64</a>. I hooked it up and started playing with it, and immediately fell in love with it. It&#8217;s so <em>Commodore-y</em>. But it runs at 8 MHz instead of 1 MHz, has VGA output, and has a whole bunch of other modern improvements. It came with several games and other pieces of software, and I enjoyed messing around with those for a while.</p>



<p>What I really wanted to do, however, was <em>write</em> something for the machine. But what to write? A game would be the most fun to work on, but game design is not my strong suit, nor are graphics or sound design. After giving it some thought, I decided to make a clone of an existing game, relying on the game, graphics, and sound design from that game. I chose to clone <a href="https://gb64.com/game.php?id=20100" data-type="link" data-id="https://gb64.com/game.php?id=20100" target="_blank" rel="noreferrer noopener">Nomad: The Space Pirate</a>, a Commodore 64 game I played a lot as a kid.</p>



<p>I thought this was a good game to clone for several reasons. First, It&#8217;s a fun game (at least I think so). Second, it&#8217;s relatively simple; there&#8217;s a single static screen per level, and only a few moving objects, so no scrolling or crazy sprite multiplexing would be required. Third, it&#8217;s a pretty obscure game. I haven&#8217;t met anyone outside my immediate family that&#8217;s ever heard of it. This obscurity meant that I would be unlikely to be beaten to the punch while my version was still in development. If I&#8217;d chosen something like Pac-Man or Tetris, on the other hand, my game would probably only be one of many similar games available on the platform.</p>



<p>I also needed a name for the project. I couldn&#8217;t simply call it <strong>Nomad: The Space Pirate</strong>; that would be at best confusing, and at worst copyright infringement. I noticed that Nomad spelled backwards was Damon. That&#8217;s a good start, but if Nomad is a Space Pirate, what should Damon be? I decided that he should be a Rocket Jockey, which sounds cool and is fun to say. And, he does ride a rocket around, so it&#8217;s not inaccurate. Thus, <strong>Damon: The Rocket Jockey</strong> was conceived.</p>



<h2 class="wp-block-heading">The Game</h2>



<p>The goal of the game is to fly your ship around a grid, picking up pellets. Every level has one or more enemies, which the player must avoid or shoot, as crashing into them causes the player to lose a ship. Shooting an enemy destroys it, but it immediately respawns in a corner of the screen unless all the pellets have already been collected.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="640" height="480" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-screenshot.png" alt="" class="wp-image-648" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-screenshot.png 640w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-screenshot-300x225.png 300w" sizes="auto, (max-width: 640px) 100vw, 640px" /><figcaption class="wp-element-caption"><strong>Damon: The Rocket Jockey – Gameplay Screenshot</strong><br>(White Ship: Player, Purple Ship: Enemy)</figcaption></figure>



<p>Once all pellets are collected, and all enemies destroyed, the player advances to the next level. Collecting pellets and destroying enemies award points, and a bonus ship is awarded every 10,000 points. Each level, another enemy is added (up to a maximum of 5), and in every level after the first, barrier blocks are placed in different locations around the play area. Neither the player nor enemies can fly through the barriers, but the player can shoot through them.</p>



<p>If you&#8217;d like to play the game, I&#8217;ve made it available at <a href="http://natebarney.com/damon" target="_blank" rel="noreferrer noopener">natebarney.com/damon</a>. This page uses the Commander X16 web emulator to run the game right in your web browser. It doesn&#8217;t really work on smart phones or tablets however, so you&#8217;ll need to access it from a desktop or laptop computer. (For Science, I tried it on the Playstation 4 web browser. Unsurprisingly, it didn&#8217;t even load.) There are also links there to download the game if you&#8217;d prefer to play it using the standalone <a href="https://github.com/X16Community/x16-emulator/releases" data-type="link" data-id="https://github.com/X16Community/x16-emulator/releases" target="_blank" rel="noreferrer noopener">Commander X16 Emulator</a> or even a real Commander X16.</p>



<p>If I&#8217;m having a really good game, I can sometimes make it to level 5, with my top score being something like 13,000 points. I&#8217;ve never made it to level 6, usually running out of ships at level 3 or 4, and my average score is something like 7,500 points. Here&#8217;s a video of me having a decent game, although I struggled a bit during level 3. It was captured directly from the VGA output of my physical Commander X16.</p>



<figure class="wp-block-video aligncenter max640"><video height="480" style="aspect-ratio: 640 / 480;" width="640" controls preload="auto" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-gameplay.mp4"></video><figcaption class="wp-element-caption">Gameplay Video</figcaption></figure>



<h2 class="wp-block-heading">Development</h2>



<p>The rest of this post will cover the development process for the game and discuss how or why I did things the way I did them. I probably won&#8217;t dive much into the actual code, but if you&#8217;d like to have a look at that, it&#8217;s available on <a href="https://github.com/natebarney/damon-the-rocket-jockey" data-type="link" data-id="https://github.com/natebarney/damon-the-rocket-jockey" target="_blank" rel="noreferrer noopener">GitHub</a>.</p>



<h3 class="wp-block-heading">Goals</h3>



<p>My primary goal for this project was authenticity. I wanted my version to look, sound, and <em>feel</em> like the original. I think I largely succeeded, but there are, of course, some minor differences.</p>



<p>Another goal, perhaps related to the first, was to write the game entirely in assembly language. The original was almost certainly written this way, and I wanted to do the same. I did allow myself the luxury of using a modern cross-assembler (<a href="https://cc65.github.io/doc/ca65.html" data-type="link" data-id="https://cc65.github.io/doc/ca65.html" target="_blank" rel="noreferrer noopener">ca65</a>) and linker (<a href="https://cc65.github.io/doc/ld65.html" data-type="link" data-id="https://cc65.github.io/doc/ld65.html" target="_blank" rel="noreferrer noopener">ld65</a>), but I think that&#8217;s a minor concession. If I try my hand at writing another X16 game, I will probably try writing it in C. The <a href="https://cc65.github.io/" data-type="link" data-id="https://cc65.github.io/" target="_blank" rel="noreferrer noopener">cc65 suite</a> includes a C compiler, and I haven&#8217;t done C for the 6502 before, so that should be interesting. But for this project, I stuck to assembly language.</p>



<p>My third goal was to write everything myself, from scratch. The author of the original game wrote what he wrote, and it&#8217;s copyrighted. Plus, it&#8217;s more fun to figure everything out myself. So, I didn&#8217;t look at the disassembled code for the original. I did use the machine language monitor in the <a href="https://vice-emu.sourceforge.io/" data-type="link" data-id="https://vice-emu.sourceforge.io/" target="_blank" rel="noreferrer noopener">VICE</a> emulator to locate the memory address where the number of the player&#8217;s remaining ships was stored, so I could cheat by giving myself extra lives to see what the higher levels looked like. (This game gets hard pretty fast!) But, I didn&#8217;t look at the code.</p>



<p>Finally, I set myself a challenge goal of making my game smaller (in bytes) than the original. If I met the other goals but didn&#8217;t meet this one, I&#8217;d still consider the project a success, but it&#8217;s fun to see how tight one can make one&#8217;s code. As it turns out, the original was approximately 15K, and when finished, my version was just over 11K, so I count that as a win.</p>



<h3 class="wp-block-heading">Graphics</h3>



<p>The graphics for this game are primarily tile-based, with stationary graphical elements and text being composed of a 40&#215;30 grid of 8&#215;8 pixel tiles with 1 bpp (bit per pixel), giving a total screen resolution of 320&#215;240. This is actually one of the minor differences I mentioned before. The Commodore 64 has a screen resolution of 320&#215;200 pixels, or 40&#215;25 tiles, but both systems use a 4:3 aspect ratio. This makes the C64 have a more vertically stretched appearance, and is the reason my clone has black bars at the top and bottom of the screen.</p>



<p>The elements that move (e.g. ships and bullets) are made using sprites instead of tiles. The Commander X16&#8217;s video chip, the <a href="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md#chapter-9-vera-programmers-reference" data-type="link" data-id="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md#chapter-9-vera-programmers-reference" target="_blank" rel="noreferrer noopener">VERA</a>, supports up to 128 hardware sprites, of varying sizes and color depths, which it can overlay onto the stationary tiles at more-or-less arbitrary positions.</p>



<p>One of the first steps I took when developing this game was to grab a bunch of screenshots of the original game using VICE, and hand-copy out all of the graphics onto graph paper. I then used these notes to enter the tile and sprite data into assembly source code files, to be used by the rest of the game.</p>



<h4 class="wp-block-heading">Tiles</h4>



<p>The border around the screen, the pellets, and the blocks in the play area are all static graphical elements drawn using tiles. The blocks, and somewhat surprisingly, the pellets, are actually 16&#215;16 pixels each, so they&#8217;re made of 2&#215;2 arrangements of tiles.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="481" height="321" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-tiles.png" alt="" class="wp-image-667" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-tiles.png 481w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-tiles-300x200.png 300w" sizes="auto, (max-width: 481px) 100vw, 481px" /><figcaption class="wp-element-caption">Graphic Tiles</figcaption></figure>



<p>The level 1 (green) and level 5 (light red) blocks are made of four different tiles each, but the blocks for levels 2-4 (brown, medium grey, and orange) and the barrier (light grey) blocks simply repeat the same tile 4 times. Levels 6-10 use the same block graphics as 1-5, but with different colors and barrier block layouts. Levels 11-99 are exactly the same as level 10. The border (purple) is a single tile thick. Interestingly, some of the individual pellet tiles do double duty as single quotes and periods on the title screen.</p>



<p>Text in the game is also displayed using tiles. I was able to capture almost all of these tiles from the original game, since most of them were used in various places, especially the title screen. I did have to guess at a few letters and symbols, <kbd>J</kbd>, <kbd>W</kbd>, <kbd>X</kbd>, <kbd>Z</kbd>, and <kbd>:</kbd>, that weren&#8217;t used anywhere in the original.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="641" height="401" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-font.png" alt="" class="wp-image-664" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-font.png 641w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-font-300x188.png 300w" sizes="auto, (max-width: 641px) 100vw, 641px" /><figcaption class="wp-element-caption">Font Tiles</figcaption></figure>



<h4 class="wp-block-heading">Sprites</h4>



<p>A sprite is an image that the video chip can overlay on top of the tiles, at arbitrary positions not necessarily constrained to the tile grid. Each sprite has an independent position attribute, and simply updating the position moves the whole sprite image. This allows movement to be implemented in an efficient way.</p>



<p>Sprites on the Commodore 64 are 24&#215;21 pixels, 1 bit-per-pixel (There is a 2 bpp mode, but Nomad didn&#8217;t use it.) The VERA chip supports sprites that are 8, 16, 32, or 64 pixels wide, and 8, 16, 32, or 64 pixels high. These dimensions may be mixed, so one could define a 64&#215;8 sprite if one wanted to. Each sprite on the VERA may also be either 4 or 8 bpp.</p>



<p>There&#8217;s quite a bit of mismatch between the sprite capabilities of the two systems. Fortunately, both the player and enemy ship sprites fit nicely within 16&#215;16, and the bullet sprites similarly fit within 8&#215;8. That works well, but there&#8217;s still a color depth mismatch. Since VERA doesn&#8217;t support 1 bpp sprites, I wrote a routine to read 1 bpp sprite definitions out of system RAM, and pad them out to 4 bpp when copying them to video RAM (VRAM). That way, I didn&#8217;t need to store a bunch of unused color depth data in the program itself.</p>



<p>VERA has the ability to flip each individual sprite either horizontally, or vertically, or both (or neither). This was useful, as each ship needs to be able to face 4 different directions. By making use of this feature, I only needed to store a horizontal definition and a vertical definition per ship, instead of all four. </p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="401" height="321" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-sprites.png" alt="" class="wp-image-670" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-sprites.png 401w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-sprites-300x240.png 300w" sizes="auto, (max-width: 401px) 100vw, 401px" /><figcaption class="wp-element-caption">Ship and Bullet Sprites</figcaption></figure>



<h4 class="wp-block-heading">Animation</h4>



<p>Sprites are also useful for animation. Each <a href="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md#sprite-attributes" data-type="link" data-id="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md#sprite-attributes" target="_blank" rel="noreferrer noopener">sprite entry</a> in the video chip holds a pointer to the image data in VRAM that will be used to draw the sprite. It&#8217;s trivial to update the pointer and instantly change what the sprite looks like. The game uses this technique for player and enemy death animations, and for the large, animated block letters on the title screen.</p>



<p>When either an enemy or the player is destroyed, an explosion animation replaces the ship image. In the case of the player&#8217;s destruction, this is also followed by a winking skull and crossbones. Each of these animations consists of three separate frames. The explosion frames are played sequentially, and the winking skull frames are played sequentially and then in reverse.</p>



<p>Unfortunately, neither of these animations fit inside a 16&#215;16 pixel sprite, instead making use of most of the C64&#8217;s 24&#215;21 sprite resolution. To display these with the VERA, they need to be defined as 32&#215;32 sprites. But, to save space, I defined them in the code using 24&#215;21, and wrote a routine to pad them out to the required 32&#215;32 when copying them to VRAM.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="721" height="421" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-death-sprites.png" alt="" class="wp-image-673" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-death-sprites.png 721w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-death-sprites-300x175.png 300w" sizes="auto, (max-width: 721px) 100vw, 721px" /><figcaption class="wp-element-caption">Explosion and Skull Animation Sprites</figcaption></figure>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="721" height="316" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-death-animation.gif" alt="" class="wp-image-674"/><figcaption class="wp-element-caption">Explosion and Skull Animations</figcaption></figure>



<h4 class="wp-block-heading">Title Screen</h4>



<p>The block letters on the title screen both move and animate, so they&#8217;re also drawn using sprites. These sprites presented two new challenges, both related to features present on the C64 but not on the Commander X16, one much simpler than the other to resolve.</p>



<figure class="wp-block-image aligncenter size-full max720"><img loading="lazy" decoding="async" width="1201" height="631" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-letter-sprites.png" alt="" class="wp-image-682" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-letter-sprites.png 1201w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-letter-sprites-300x158.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-letter-sprites-1024x538.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-letter-sprites-768x404.png 768w" sizes="auto, (max-width: 1201px) 100vw, 1201px" /><figcaption class="wp-element-caption">Title Screen Block Letter Sprites</figcaption></figure>



<figure class="wp-block-image aligncenter size-full max720"><img loading="lazy" decoding="async" width="1200" height="210" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-letter-animation.gif" alt="" class="wp-image-690"/><figcaption class="wp-element-caption">Title Screen Block Letter Animation</figcaption></figure>



<p>The first and simpler of the two challenges relates to sprite size. Recall that C64 sprites are 24&#215;21 pixels. On the C64, but not the X16, sprites can be rendered as double-width, double-height, or both (or neither). This doesn&#8217;t add any extra detail to the sprite image. It merely doubles each pixel in the appropriate dimension(s) to increase the overall size.</p>



<p>The original game used this feature for the title screen block letters to make them double-width, for a total size of 48&#215;21 pixels each. This means that, on the X16, the sprites need to be horizontally doubled and then padded out to 64&#215;32. But, storing the doubled pixels directly in the game code would be wasteful. So, I wrote another sprite loading routine to double each pixel horizontally from 24&#215;21 to 48&#215;21 and pad the whole thing out to 64&#215;32 when copying a sprite from system RAM to VRAM.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="721" height="211" src="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-sprite-expandx.png" alt="" class="wp-image-696" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-sprite-expandx.png 721w, https://oldblog.natebarney.com/wp-content/uploads/2024/05/damon-sprite-expandx-300x88.png 300w" sizes="auto, (max-width: 721px) 100vw, 721px" /><figcaption class="wp-element-caption">Normal and Width-Doubled Sprites</figcaption></figure>



<p>The second challenge was trickier. If you watched the gameplay video above, you might have noticed that the letters slide in from off-screen. The C64 allows sprites to be positioned partially or even completely off-screen, which makes achieving this effect relatively straightforward. However, the Commander X16 doesn&#8217;t support this. Setting a sprite&#8217;s X or Y position to 0 will put the sprite against the left or top edge of the screen, but the sprite will be fully visible. The coordinates are unsigned integers, so trying to set them to negative values results in large positive values instead, and the sprite isn&#8217;t displayed at all.</p>



<p>The key to solving this puzzle is that, on the X16, you don&#8217;t always have to set the sprite image pointer at the <em>beginning</em> of the sprite image data. So, I padded the sprite image data with extra blank lines at the bottom, and as a new letter is just starting to emerge from off-screen, I position the sprite at the very top of the screen. I then set the sprite image pointer to the first blank line past the actual sprite image data, so all that shows is a blank sprite. Then, each frame, I move the sprite image pointer one line up, to give the illusion of the sprite coming in from off-screen. As soon as the sprite image pointer is at the top of the sprite image data, I switch from moving the sprite image pointer up to moving the sprite position down. This gives a seamless transition as the letter continues to move down the screen.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="540" height="421" src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-emerge.gif" alt="" class="wp-image-769"/><figcaption class="wp-element-caption">Sprite Emergence from Off-Screen via Sprite Image Pointer Manipulation</figcaption></figure>



<h3 class="wp-block-heading">Sound and Music</h3>



<p>The sound effects in the game are pretty simple. There are only three sound effects: the player&#8217;s bullet sound, the ship destruction crash sound, and the bonus ship sound. I implemented all of these using the <a href="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md#programmable-sound-generator-psg" data-type="link" data-id="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md#programmable-sound-generator-psg" target="_blank" rel="noreferrer noopener">Programmable Sound Generator (PSG)</a> that&#8217;s built into the VERA. To determine the parameters of the sounds, I recorded them from the original running in VICE, and loaded them into <a href="https://www.audacityteam.org/" data-type="link" data-id="https://www.audacityteam.org/" target="_blank" rel="noreferrer noopener">Audacity</a>, an open-source audio editor. This allowed me to visually see the waveforms used and measure durations, and the <a href="https://manual.audacityteam.org/man/plot_spectrum.html" data-type="link" data-id="https://manual.audacityteam.org/man/plot_spectrum.html" target="_blank" rel="noreferrer noopener">fourier analysis tool</a> it provides enabled me to determine roughly the frequencies to use.</p>



<p>The bullet sound uses a triangle waveform and does a linear sweep from about 880 Hz down to about 546 Hz over roughly half a second. It does stop whenever the bullet disappears, such as when it hits a wall or an enemy, so it might not make it to the end frequency. The ship destruction sound uses a noise waveform at about 4,400 Hz, and the base frequency doesn&#8217;t change, but the volume drops off linearly from full to zero over about half a second. The bonus ship sound uses a sawtooth waveform, with a constant frequency of about 1,134 Hz at full volume for about a sixth of a second.</p>



<figure class="wp-block-audio aligncenter max720"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-bullet-sound.mp3"></audio><figcaption class="wp-element-caption">Bullet Sound</figcaption></figure>



<figure class="wp-block-audio aligncenter max720"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-crash-sound.mp3"></audio><figcaption class="wp-element-caption">Ship Destruction Sound</figcaption></figure>



<figure class="wp-block-audio aligncenter max720"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-bonus-ship-sound.mp3"></audio><figcaption class="wp-element-caption">Bonus Ship Sound</figcaption></figure>



<p>The music is also pretty simple. The title/background music is the same 8 notes repeated endlessly, although the in-game tempo is slightly faster than the title screen tempo. It reminds me of the bass line from the <a href="https://youtu.be/oysMt8iL9UE" data-type="link" data-id="https://youtu.be/oysMt8iL9UE" target="_blank" rel="noreferrer noopener">Peter Gunn Theme</a>. It is pretty repetitive, and in fact, a member of the Commander X16 forums asked for a button to turn it off! I kinda like it myself, but maybe that&#8217;s just nostalgia talking.</p>



<figure class="wp-block-image aligncenter size-full max720"><img loading="lazy" decoding="async" width="1467" height="226" src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-title-background-music.png" alt="" class="wp-image-715" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-title-background-music.png 1467w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-title-background-music-300x46.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-title-background-music-1024x158.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-title-background-music-768x118.png 768w" sizes="auto, (max-width: 1467px) 100vw, 1467px" /><figcaption class="wp-element-caption">Title/Background Music</figcaption></figure>



<figure class="wp-block-audio aligncenter max720"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-title-music.mp3"></audio><figcaption class="wp-element-caption">Title Music</figcaption></figure>



<figure class="wp-block-audio aligncenter max720"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-background-music.mp3"></audio><figcaption class="wp-element-caption">Background Music</figcaption></figure>



<p>The other two pieces of music in the game are the level start and level complete jingles. The level start music is simply a rapid chromatic scale from A♯3 to G4. The level complete music is the most complex of all of them, and my friend Matt pointed out that it&#8217;s actually a sped-up, minor version of the opening measures of <a href="https://youtu.be/q7v7102X7d0&amp;t=9" data-type="link" data-id="https://youtu.be/q7v7102X7d0&amp;t=9" target="_blank" rel="noreferrer noopener">Pictures at an Exhibition: Promenade</a> by Modest Mussorgsky.</p>



<figure class="wp-block-image aligncenter size-full max720"><img loading="lazy" decoding="async" width="1453" height="274" src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-level-start-music.png" alt="" class="wp-image-719" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-start-music.png 1453w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-start-music-300x57.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-start-music-1024x193.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-start-music-768x145.png 768w" sizes="auto, (max-width: 1453px) 100vw, 1453px" /><figcaption class="wp-element-caption">Level Start Music</figcaption></figure>



<figure class="wp-block-audio aligncenter max720"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-level-start-music.mp3"></audio><figcaption class="wp-element-caption">Level Start Music</figcaption></figure>



<figure class="wp-block-image aligncenter size-full max720"><img loading="lazy" decoding="async" width="1915" height="310" src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-level-complete-music.png" alt="" class="wp-image-772" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-complete-music.png 1915w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-complete-music-300x49.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-complete-music-1024x166.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-complete-music-768x124.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-level-complete-music-1536x249.png 1536w" sizes="auto, (max-width: 1915px) 100vw, 1915px" /><figcaption class="wp-element-caption">Level Complete Music</figcaption></figure>



<figure class="wp-block-audio aligncenter max720"><audio controls src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-level-complete-music.mp3"></audio><figcaption class="wp-element-caption">Level Complete Music</figcaption></figure>



<p>For all of the music, I used the <a href="https://en.m.wikipedia.org/wiki/Yamaha_YM2151" data-type="link" data-id="https://en.m.wikipedia.org/wiki/Yamaha_YM2151" target="_blank" rel="noreferrer noopener">Yamaha YM2151 (OPM)</a> FM synthesis chip built into the Commander X16. The X16 comes with a full set of synthesizer patches for this chip, burned into the ROM, and I played with some of those for a bit, but none of them sounded quite right. I ended up making my own patches by tediously tweaking the parameters until I got a sound that I felt was close enough to the original. The OPM chip&#8217;s parameters felt unusual to me, since my experience with FM is primarily with the <a href="https://en.m.wikipedia.org/wiki/Yamaha_OPL" data-type="link" data-id="https://en.m.wikipedia.org/wiki/Yamaha_OPL" target="_blank" rel="noreferrer noopener">OPL</a> line of chips. For example, the envelope generator on the OPM has two separate decay phases. But, after playing around for a while, I think I&#8217;ve figured it out well enough.</p>



<p>The patch for the title/background music uses FM to approximate a sawtooth waveform, and the envelope has a sharp attack and decay, but a slow release, sort of like a plucked string. The patch for the level start and level complete jingles uses a similar sawtooth waveform, but has a slightly softer attack and decay.</p>



<h3 class="wp-block-heading">Enemy AI</h3>



<p>I fretted about the enemy AI for weeks before implementing it. After all, it&#8217;s what really makes the game challenging and fun to play. It seemed like a very complex part of the game to write, so I kept putting it off and worked on other parts, letting the problem simmer in the back of my mind. Soon enough, the time came when I couldn&#8217;t make any more progress without addressing this problem.  Fortunately, a thought had occurred to me that might make it tractable: <a href="https://en.m.wikipedia.org/wiki/Markov_chain" data-type="link" data-id="https://en.m.wikipedia.org/wiki/Markov_chain" target="_blank" rel="noreferrer noopener">Markov chains</a>.</p>



<p>A Markov chain is a process that makes decisions based only on its current state. Any previous history is irrelevant to what happens next. Instead of following some complicated overarching strategy, the enemies could simply make decisions in the moment, with whatever information they have at the time. I realized that the only time an enemy needs to make a decision is when it&#8217;s at an intersection. Any other time, it just keeps moving forward. So I watched the enemies move in the original game with this viewpoint in mind, to try to determine what rules it used. I came away with a set of rules that seems to be pretty close.</p>



<p>These are the rules I implemented for an enemy in its normal state:</p>



<ol class="wp-block-list">
<li>If the enemy is at a wall, start moving in the reverse direction.</li>



<li>If the enemy has line-of-sight to the player, to the left, right, or straight ahead, move toward the player.</li>



<li>If the enemy is facing a barrier block, turn left or right with equal probability.</li>



<li>Otherwise, decide whether to turn with 50% probability, and if so, turn left or right with equal probability.</li>
</ol>



<p>That&#8217;s all there is to it. If there are multiple enemies, they don&#8217;t coordinate. Each follows the same set of rules independently. It&#8217;s surprising how complex and apparently organized the resulting behavior can seem. However, this is for enemies in their normal state. Respawning enemies follow a different set of rules:</p>



<ol class="wp-block-list">
<li>Select the corner of the screen furthest from the player and respawn there.</li>



<li>Start moving vertically away from the corner.</li>



<li>If the player is visible at an intersection, turn in there and enter normal state.</li>



<li>Otherwise, at each intersection but the last, turn in and enter normal state with 25% probability.</li>



<li>At the last intersection, always turn in and enter normal state.</li>
</ol>



<p>However, there is one more aspect of enemy behavior that I have yet to cover. Some readers may have noticed that in the ship and bullet sprite figure above, there appears to be a bullet for the enemy. That&#8217;s exactly what that is. The enemies can shoot back.</p>



<p>It only starts happening at level 8, so it&#8217;s kind of ridiculous to even have bothered implementing it, since I don&#8217;t think anyone will ever reach level 8. But I&#8217;ve been wrong before, and in any case, the original game does it, so I wanted my clone to do it too. I only discovered it myself when cheating to see the higher level layouts.</p>



<p>The enemies only have one bullet to share among themselves. The Commodore 64 can only display 8 sprites at a time, unless the game does something fancy like sprite multiplexing, which Nomad apparently doesn&#8217;t do. So, we have the player, the player&#8217;s bullet, and 5 enemies, which comes to 7 sprites, leaving one left for the enemy bullet. I wonder if the enemies max out at 5 because the programmer wanted to save a sprite for the enemy bullet, or if 5 enemies was selected for other reasons, and since there was one sprite left over, he decided to use it for this. I guess there&#8217;s really no way to know for sure.</p>



<p>The first time I implemented enemy fire, the enemies were deadly snipers, immediately shooting and killing the player as soon as they had a shot. In order to tone this down a bit, I added a couple of constraints. The first was a reload time. At the beginning of a round, and after each time an enemy fires, the enemies have to wait for their bullet to reload before they can fire again. I set this to 5 seconds, because that seemed to approximate the rate of enemy fire in the original game. The second constraint was reaction time. Once an enemy sees the player and decides to fire, it won&#8217;t actually fire until 400 ms later.</p>



<p>Here are the resulting enemy fire rules, processed every frame in which the enemy isn&#8217;t busy making other decisions:</p>



<ol class="wp-block-list">
<li>If the level is less than 8, don&#8217;t fire.</li>



<li>If the bullet is still reloading, don&#8217;t fire.</li>



<li>If the bullet is already in flight, don&#8217;t fire.</li>



<li>If the enemy is pointing at the player, start a 400 ms countdown and fire when it elapses, if possible.</li>
</ol>



<p>These rules seem to result in behavior that loosely approximates that of the original game. Since it will likely never happen in practice, I think that&#8217;s close enough.</p>



<h3 class="wp-block-heading">Collision Detection</h3>



<p>Sprite collision occurs when two sprites overlap on the screen. It&#8217;s important to be able to detect when this happens, and between which sprites, to know when an enemy or player ship should be destroyed. The VERA chip used by the Commander X16 supports <a href="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md#sprite-collisions" data-type="link" data-id="https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md#sprite-collisions">hardware collision detection</a>, but the way it works can be a little confusing at first.</p>



<h4 class="wp-block-heading">Hardware Collision Detection</h4>



<p>Each sprite has a 4-bit collision mask, which can be thought of as defining 4 independent collision groups, one per bit. Each bit in the sprite collision mask is set to 1 for groups the sprite is a member of. Other bits are set to 0. The VERA keeps track of a 4-bit overall collision result, which it sets to all 0&#8217;s at the start of each frame. As the VERA renders each pixel, if it draws a sprite on top of another sprite, it checks their collision masks by bitwise-ANDing them together. If the result of the AND is non-zero (i.e. the two sprites share at least one collision group), the previous overall collision result is bitwise-ORed with the result of the AND to give the updated overall collision result. At the end of the frame, the overall collision result contains a 1 bit for each collision group that experienced a collision during that frame.</p>



<p>The program/game can register to receive a CPU interrupt for sprite collisions by specifying a 4-bit value (let&#8217;s call it the collision interrupt mask) indicating which collision groups it&#8217;s interested in. If, at the end of the frame, the overall collision result is non-zero, it is bitwise-ANDed with the collision interrupt mask. The result of this AND isn&#8217;t stored anywhere, but if it&#8217;s non-zero, the VERA generates a CPU interrupt and reports the overall collision result as part of its interrupt status register.</p>



<p>When the CPU receives an interrupt, it stops what it&#8217;s doing and jumps to a special routine called the Interrupt Service Routine (ISR), commonly known as an interrupt handler. The handler checks various hardware registers to see what generated the interrupt, and performs any actions that are needed to handle that interrupt. When it&#8217;s done, the interrupt handler returns the CPU to what it was doing before the interrupt occurred.</p>



<p>It&#8217;s good practice not to spend too much time in an interrupt handler. The interrupt handler for this game, when it receives a sprite collision interrupt, simply stores the overall collision result from the interrupt status register into a location in memory, to be processed as part of the normal game loop.</p>



<p>This game uses 3 different collision groups, one for the player&#8217;s ship, one for the player&#8217;s bullet, and one for the enemies&#8217; bullet. Here&#8217;s a table of the collision group memberships:</p>



<figure class="wp-block-table aligncenter"><table><thead><tr><th>Sprite</th><th class="has-text-align-center" data-align="center">Player Bullet<br>Group</th><th class="has-text-align-center" data-align="center">Player Ship<br>Group</th><th class="has-text-align-center" data-align="center">Enemy Bullet<br>Group</th></tr></thead><tbody><tr><td>Player Bullet</td><td class="has-text-align-center" data-align="center">1</td><td class="has-text-align-center" data-align="center">0</td><td class="has-text-align-center" data-align="center">0</td></tr><tr><td>Enemy Ship</td><td class="has-text-align-center" data-align="center">1</td><td class="has-text-align-center" data-align="center">1</td><td class="has-text-align-center" data-align="center">0</td></tr><tr><td>Player Ship</td><td class="has-text-align-center" data-align="center">0</td><td class="has-text-align-center" data-align="center">1</td><td class="has-text-align-center" data-align="center">1</td></tr><tr><td>Enemy Bullet</td><td class="has-text-align-center" data-align="center">0</td><td class="has-text-align-center" data-align="center">0</td><td class="has-text-align-center" data-align="center">1</td></tr></tbody></table></figure>



<figure class="wp-block-image aligncenter size-full max720"><img loading="lazy" decoding="async" width="1260" height="630" src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-collision-venn-diagram.png" alt="" class="wp-image-742" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-collision-venn-diagram.png 1260w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-collision-venn-diagram-300x150.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-collision-venn-diagram-1024x512.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-collision-venn-diagram-768x384.png 768w" sizes="auto, (max-width: 1260px) 100vw, 1260px" /><figcaption class="wp-element-caption">Collision Groups</figcaption></figure>



<p>This works really well for a single enemy. The VERA checks for collisions, and the set of groups with collisions tells the game exactly what needs to happen. If the player ship group or enemy bullet group has a collision, the player ship is destroyed. If the player bullet group has a collision, the enemy ship is destroyed. However, this runs into difficulties when multiple enemies are introduced. To resolve them, some software collision detection is also needed.</p>



<h4 class="wp-block-heading">Software Collision Detection</h4>



<p>When there is more than one enemy, the collision groups alone don&#8217;t provide enough information, so the game needs to do some additional checks to determine what needs to happen next. For example, if the player bullet group has a collision, that doesn&#8217;t specify <em>which</em> enemy was hit.</p>



<p>To resolve this issue, the bullets and ships are assigned &#8220;hit boxes,&#8221; which are axis-aligned rectangles relative to the sprite&#8217;s coordinate space. (Axis-aligned means each side of the rectangle is parallel to either the X- or the Y-axis.) Hit boxes are entirely a software concept; the VERA chip has no notion of them. When the VERA informs the game of a collision in the player bullet group, for example, the game does an intersection test between the bullet&#8217;s hit box and each enemy&#8217;s hit box. If it finds an intersection, that&#8217;s the enemy that should be destroyed.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="401" height="321" src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-sprite-hit-boxes.png" alt="" class="wp-image-736" srcset="https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-sprite-hit-boxes.png 401w, https://oldblog.natebarney.com/wp-content/uploads/2024/06/damon-sprite-hit-boxes-300x240.png 300w" sizes="auto, (max-width: 401px) 100vw, 401px" /><figcaption class="wp-element-caption">Ship and Bullet Sprite Hit Boxes</figcaption></figure>



<p>The player bullet group isn&#8217;t the only group that needs these extra checks, however. Every enemy is also a member of the player ship collision group. This means they can collide with the player ship, as expected. Some readers might be wondering why it would matter <em>which</em> enemy the player collided with. It, of course, doesn&#8217;t matter, as far as that goes. But since enemies are all in the same collision group, when they collide <em>with each other</em>, that registers a collision in that group. Without extra checks to determine whether the player was involved, any time two or more enemies overlapped, the player&#8217;s ship would spontaneously explode. I actually tried that experiment, For Science, as I was implementing multiple enemies. It was pretty funny to watch.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="640" height="480" src="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-spontaneous-explosion.gif" alt="" class="wp-image-750"/><figcaption class="wp-element-caption">Without Software Collision Detection</figcaption></figure>



<p>The game <em>is</em> able to rely solely on hardware detection for collisions between the player ship and enemy bullet, however, since there&#8217;s only one of each. It&#8217;s only when there are multiple sprites of a given type (e.g. enemies) that the software algorithm is required. Therefore, there&#8217;s no hit box defined for the enemy bullet.</p>



<p>There are a couple of reasons to use axis-aligned hit boxes. The first is that they only require four numbers to fully specify: top, left, bottom, and right. The second is that it makes intersection testing really simple. The trick to doing an intersection test between axis-aligned rectangles is to check all the ways that they might <em>not</em> intersect. If the left coordinate of one rectangle is greater than the right coordinate of the other, or vice-versa, they clearly don&#8217;t intersect. The same holds true in the other direction with the top and bottom coordinates. If none of these four checks returns true, then the rectangles <em>do</em> intersect. Neat, huh?</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2024/06/no-intersection-vertical.svg" alt="" class="wp-image-738"/><figcaption class="wp-element-caption">Vertically Non-Intersecting Rectangles</figcaption></figure>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2024/06/no-intersection-horizontal.svg" alt="" class="wp-image-739"/><figcaption class="wp-element-caption">Horizontally Non-Intersecting Rectangles</figcaption></figure>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2024/06/intersection.svg" alt="" class="wp-image-740"/><figcaption class="wp-element-caption">Intersecting Rectangles</figcaption></figure>



<p>The significant advantage to using hit boxes for software collision detection is that it&#8217;s much faster than checking each sprite against each other sprite, pixel by pixel. This could be done, but it would be very computationally expensive (and harder to write as well). The obvious disadvantage is that it&#8217;s less precise than the pixel-perfect hardware collision detection. To deal with this, I shrank the player ship&#8217;s hit box a little. Otherwise, there could be spurious collisions when two empty sprite corners &#8220;overlap.&#8221; Since the game is hard enough already, I erred on the side of forgiving.</p>



<p>If the VERA supported seven or more collision groups, this software collision detection would be unnecessary (for this game). In that case, the one player ship collision group could be replaced with five new collision groups, one for each enemy ship, and the collision groups themselves would provide enough information about what to do. In fact, as I&#8217;m writing this, it occurs to me that if the enemies were partitioned into two groups, that could reduce the average number of checks the software collision detection would need to make. Fortunately, the game runs well enough without this optimization that I don&#8217;t feel the need to implement it.</p>



<h3 class="wp-block-heading">Miscellaneous Techniques</h3>



<p>In this section, I&#8217;ll describe several programming techniques that I found useful when developing this game.</p>



<h4 class="wp-block-heading">Game Loop and Hierarchical Init / Update</h4>



<p>The core of any game is the game loop. This is the bit of code that runs over and over again until the game exits, handling everything—input, graphics, sound, music, game state, etc. When the game first starts, it calls some initialization routines, often shortened to &#8220;init.&#8221; Once init is complete, the game enters the game loop, calling the update routine repeatedly.</p>



<p>I found it useful to establish multiple, hierarchical init and update routine pairs. There are of course, the master init and update routines, at the top of the hierarchy. But, for example, the title screen has its own init and update routines, only called when the game is displaying the title screen. There are also pairs of routines for the player, the enemies, the bullets, the music, the sound effects, and several others. Each layer of the hierarchy knows when and whether to call its subordinate sets of routines. In this way, the game is able to manage multiple independent operations apparently simultaneously, without becoming a huge mess of spaghetti code.</p>



<h4 class="wp-block-heading">V-Blank Interrupt</h4>



<p>If the game ran its update loop as fast as it possibly could, this would cause multiple problems. Some iterations of the loop take less time than others, depending on what needs to be done each time. So, the game would speed up and slow down erratically. The Commander X16 itself can run at different speeds. It defaults to 8 MHz, but it can also run at 4 Mhz or 2 MHz, if the user desires. Changing this would alter the frame rate and behavior of the game as well. Finally, the graphics would be updated at effectively random times throughout the frame update, so at any one time, the screen would display part of one frame, and part of another. Possibly more than two, depending on how fast each loop iteration is.</p>



<p>To fix all of these issues, a common technique is to synchronize the game loop to the <a href="https://en.wikipedia.org/wiki/Vertical_blanking_interval" data-type="link" data-id="https://en.wikipedia.org/wiki/Vertical_blanking_interval" target="_blank" rel="noreferrer noopener">vertical blanking interval</a>, or V-blank. A V-blank happens only once per frame, after the entire frame has been drawn. It also happens very regularly. The Commander X16 uses a frame rate of 60 Hz, so a V-blank happens pretty much exactly 60 times a second.</p>



<p>The VERA chip can be configured to cause a CPU interrupt on V-blank, so I enabled that, and set up my interrupt handler to set a flag variable to 1 when a V-blank interrupt occurs. The main game loop waits for that flag to become 1, performs a single update, sets the flag to 0, and waits for it to become 1 again. This allows the game to update at a smooth 60 frames-per-second, without any of the issues described above.</p>



<h4 class="wp-block-heading">State Machines</h4>



<p>Several of the entities in the game have complicated behavior. For example, an enemy can be moving, deciding whether to turn, exploding, or respawning. All of these behaviors could be implemented in a monolithic update routine, but that would make keeping track of all of the variables that might affect things a nightmare. Instead, entities with complicated behaviors were given a <a href="https://en.wikipedia.org/wiki/Finite-state_machine" data-type="link" data-id="https://en.wikipedia.org/wiki/Finite-state_machine" target="_blank" rel="noreferrer noopener">state machine</a>.</p>



<p>With a state machine approach, the entity has a state variable, which roughly corresponds to &#8220;what it&#8217;s doing right now.&#8221; The behavior of the entity differs depending on what state it&#8217;s in, and some events can move the entity to a new state. For example, if the enemy is in the &#8220;moving&#8221; state, and it&#8217;s hit by the player&#8217;s bullet, it transitions to the &#8220;exploding&#8221; state, and begins playing the exploding animation. Once the animation is complete, it transitions to the &#8220;respawning&#8221; state, unless all the pellets in the level have been collected, in which case it transitions to the &#8220;retired&#8221; state instead.</p>



<p>State machines fit nicely into the hierarchical init/update scheme described above. Each state has an init routine, which is called when the entity transitions to that state, and an update routine, which defines the behavior of the entity while it&#8217;s in that state. Entities&#8217; main update routines are often nothing more than a lookup of the state value and a dispatch to the appropriate state-specific routine. </p>



<p>Entities don&#8217;t have to be moving objects to benefit from state machines. For example, the game itself has states corresponding to displaying the title screen, displaying the &#8220;Get Ready&#8221; screen, playing the level start music, and running the main game. The title screen has states for sliding letters in and animating the letters. It&#8217;s an extremely useful concept for organizing game code.</p>



<h4 class="wp-block-heading">Object-Oriented Design</h4>



<p>In general, when developing software, I follow the motto &#8220;make it work, then make it pretty.&#8221; This means that, in the beginning, exploratory phase of development, code organization is given second priority to getting something working at all. Once the code works, then it&#8217;s time to refactor the code and make it nice and neat. Often, with larger projects, this cycle repeats more than once. At some point, I&#8217;ve usually gotten enough things working (but not pretty) that adding anything new is challenging. That&#8217;s when I know it&#8217;s time for a &#8220;make it pretty&#8221; phase. Once that&#8217;s done, I can add more features to the software.</p>



<p>During the development of this game, I went through several such cycles, and during one of the &#8220;make it pretty&#8221; phases, I noticed I had a lot of repeated code for entities that move around the screen (e.g. ships and bullets). I wanted to try to collapse this duplicated code down to one common set of routines, and even though I was working in assembly language, I found utility in the principles of <a href="https://en.wikipedia.org/wiki/Object-oriented_programming" data-type="link" data-id="https://en.wikipedia.org/wiki/Object-oriented_programming" target="_blank" rel="noreferrer noopener">object-oriented programming (OOP)</a>.</p>



<p>Of course, the OOP techniques I employed were rudimentary compared to the capabilities of languages like C++ or Java. Data encapsulation (e.g. <kbd>private</kbd> or <kbd>protected</kbd>), for example, is more of a high-level language feature, and in assembly language, there is no enforcement of such things. But, objects themselves can be done, or at least approximated, in assembly language just as well as in any high-level language.</p>



<p>Going back to the moving entity example, I identified all of the common variables used by these entities and grouped them together into a single data definition (struct). ca65&#8217;s <a href="https://cc65.github.io/doc/ca65.html#ss11.102" data-type="link" data-id="https://cc65.github.io/doc/ca65.html#ss11.102" target="_blank" rel="noreferrer noopener"><kbd>.STRUCT</kbd></a> directive was very useful for this. I was then able to declare multiple instances of that struct, one for each moving entity, and pass a pointer to that struct into the routines that implemented motion.</p>



<p>I even have one example of simple inheritance. The struct for enemies has all of the data members that a moving entity struct has, but it adds another after those to keep track of the reaction time delay for firing its gun. Because the enemy struct starts with all of the same data as the moving entity struct, I can pass a pointer to an enemy struct to a routine expecting a moving entity struct, and everything works.</p>



<h4 class="wp-block-heading">Interpreters</h4>



<p>A few things in the game needed to be &#8220;scripted,&#8221; or follow a predetermined sequence of steps. For example, the music follows a fixed sequence of notes, and animations follow a fixed sequence of frames. For each of these, to make changing the &#8220;scripts&#8221; easier, I implemented a rudimentary interpreter. Such an interpreter is given a list of commands, each with its parameters, stored in read-only memory (ROM).</p>



<p>The interpreter looks at the first command, executes it (i.e. performs the specified action), then goes to the next command in the list to execute that one. Some commands can change which instruction is next, to enable the creation of loops. This was useful for the title/background music as well as the title screen animations, both of which loop. For the animation interpreter, I implemented several commands, including sprite positioning, setting the sprite image, setting the sprite color, and a delay command. I was impressed by the utility of this approach, and I expect to use it again if/when I write another game.</p>



<h2 class="wp-block-heading">Closing Thoughts</h2>



<p>I am quite proud of what I achieved with this project. I feel I met all of my goals, learned a lot, and added something to the Commander X16 community. I think the game is fun to play too! I&#8217;ve spent several hours playing it already. As usual, if you have questions on any particular topic, feel I haven&#8217;t explained something well enough, have encountered bugs in the game, have a suggestion for improvement, or even have ideas for future projects or topics, I&#8217;d love to hear from you. Feel free to leave a comment below, or contact me directly if you have my contact info.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2024/06/02/damon-the-rocket-jockey/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2024/05/damon-gameplay.mp4" length="10190976" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-bullet-sound.mp3" length="10448" type="audio/mpeg" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-crash-sound.mp3" length="9194" type="audio/mpeg" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-bonus-ship-sound.mp3" length="3760" type="audio/mpeg" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-title-music.mp3" length="56841" type="audio/mpeg" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-background-music.mp3" length="52661" type="audio/mpeg" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-level-start-music.mp3" length="14627" type="audio/mpeg" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2024/06/damon-level-complete-music.mp3" length="43466" type="audio/mpeg" />

			</item>
		<item>
		<title>ApOPL3xy Hardware Design</title>
		<link>https://oldblog.natebarney.com/2023/10/08/apopl3xy-hardware-design/</link>
					<comments>https://oldblog.natebarney.com/2023/10/08/apopl3xy-hardware-design/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Mon, 09 Oct 2023 02:03:48 +0000</pubDate>
				<category><![CDATA[ApOPL3xy]]></category>
		<category><![CDATA[electronics]]></category>
		<category><![CDATA[music]]></category>
		<category><![CDATA[opl3]]></category>
		<category><![CDATA[projects]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=267</guid>

					<description><![CDATA[I&#8217;ve been working on designing and building a MIDI synthesizer (called the ApOPL3xy) based on the OPL3 FM synthesis chip and the ATmega1284 microcontroller. I&#8217;ve made a couple of posts about it (here and here) and have gotten some good questions from some people about how this or that works under the hood. So, for [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve been working on designing and building a MIDI synthesizer (called the ApOPL3xy) based on the OPL3 FM synthesis chip and the ATmega1284 microcontroller. I&#8217;ve made a couple of posts about it (<a rel="noreferrer noopener" href="https://blog.natebarney.com/2023/06/25/apopl3xy-a-midi-opl3-based-fm-synthesizer/" data-type="post" data-id="233" target="_blank">here</a> and <a rel="noreferrer noopener" href="https://blog.natebarney.com/2023/07/03/apopl3xy-update-patches-omni-mode-and-percussion/" data-type="post" data-id="250" target="_blank">here</a>) and have gotten some good questions from some people about how this or that works under the hood. So, for this post, I thought I&#8217;d dig a little deeper into the technical details. I&#8217;ll go over the various components and how they&#8217;re connected together. If needed, I may make other posts focusing on some of the components in further detail.</p>



<h3 class="wp-block-heading">Microcontroller</h3>



<p>The microcontroller that drives the system is the Microchip <a rel="noreferrer noopener" href="https://www.microchip.com/en-us/product/ATmega1284" data-type="URL" data-id="https://www.microchip.com/en-us/product/ATmega1284" target="_blank">ATmega1284</a>. (It used to be made by Atmel, before <a rel="noreferrer noopener" href="https://www.microchip.com/en-us/announcements/microchip-technology-inc-acquires-atmel" data-type="URL" data-id="https://www.microchip.com/en-us/announcements/microchip-technology-inc-acquires-atmel" target="_blank">Microchip bought them</a>.) The ATmega1284 is an 8-bit <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/AVR_microcontrollers" target="_blank">AVR</a> microcontroller, similar to but more powerful than the <a href="https://www.microchip.com/en-us/product/ATMEGA328P" data-type="URL" data-id="https://www.microchip.com/en-us/product/ATMEGA328P" target="_blank" rel="noreferrer noopener">ATmega328P</a> at the heart of the <a href="https://docs.arduino.cc/hardware/uno-rev3" data-type="URL" data-id="https://docs.arduino.cc/hardware/uno-rev3" target="_blank" rel="noreferrer noopener">Arduino Uno R3</a>.</p>



<h5 class="wp-block-heading">Hardware</h5>



<p>Chips in the AVR line of microcontrollers have integrated <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Flash_memory" data-type="URL" data-id="https://en.wikipedia.org/wiki/Flash_memory" target="_blank">flash memory</a> for storing programs, <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Static_random-access_memory" data-type="URL" data-id="https://en.wikipedia.org/wiki/Static_random-access_memory" target="_blank">SRAM</a> for program variables and stack, and an <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/EEPROM" data-type="URL" data-id="https://en.wikipedia.org/wiki/EEPROM" target="_blank">EEPROM</a> for non-volatile data storage. Most have hardware implementations of several communication protocols, such as <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface" data-type="URL" data-id="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface" target="_blank">SPI</a>, <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/I%C2%B2C" data-type="URL" data-id="https://en.wikipedia.org/wiki/I%C2%B2C" target="_blank">I<sup>2</sup>C</a>, and <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Transistor%E2%80%93transistor_logic#Serial_signaling" data-type="URL" data-id="https://en.wikipedia.org/wiki/Transistor%E2%80%93transistor_logic#Serial_signaling" target="_blank">TTL Serial</a> (basically <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/RS-232" data-type="URL" data-id="https://en.wikipedia.org/wiki/RS-232" target="_blank">RS-232</a> but with different voltage levels).</p>



<p>Whereas the ATmega328P has 32 kB of flash, 2 kB of SRAM, and 1 kB of EEPROM, the ATmega1284 has 128 kB of flash, 16 kB of SRAM, and 4 kB of EEPROM. I had initially designed the ApOPL3xy around the ATmega328P, but soon found the memory constraints too limiting, especially the 2 kB of SRAM. So, I upgraded to the ATmega1284. 128 kB of program flash and 16 kB of on-chip SRAM should be more than enough for the initial version, and leave plenty of room for future expansion.</p>



<p>The microcontroller in the ApOPL3xy runs at 5V and 20 MHz. A quartz crystal is used to generate the clock signal. 20 MHz is the maximum clock frequency for which the ATmega1284 is rated, and so far, it seems to be sufficient.</p>



<h5 class="wp-block-heading">Programming</h5>



<p>Arduino boards each have a USB port connected to a USB-to-serial bridge chip, which is used by special bootloader code on the microcontroller to receive new program code, so that all one needs to program an Arduino is a USB cable. When using a bare microcontroller as the ApOPL3xy does, the way to program it is ISP (In-circuit Serial Programming), sometimes called ICSP. This uses the SPI bus to upload code to the microcontroller (configured as the slave). However, to do this, one needs a separate device to act as the SPI master and upload the code to the microcontroller. I&#8217;m using the <a rel="noreferrer noopener" href="https://onlinedocs.microchip.com/pr/GUID-BEB648AA-8539-418A-9EFB-118BDC9DC77B-en-US-1/index.html" data-type="URL" data-id="https://onlinedocs.microchip.com/pr/GUID-BEB648AA-8539-418A-9EFB-118BDC9DC77B-en-US-1/index.html" target="_blank">AVRISP-mkII</a> programmer for this project. There are many others, but I have read that some of them have trouble with chips that have more than 64 kB of flash. I have, so far, not had any problems with the AVRISP-mkII, but note that the compiled firmware is still less than 64 kB in size, so that&#8217;s not entirely conclusive.</p>



<p>I&#8217;m using the <a rel="noreferrer noopener" href="https://platformio.org/" data-type="URL" data-id="https://platformio.org/" target="_blank">PlatformIO</a> IDE to edit, compile, and upload code to the microcontroller. This could be done with the official <a rel="noreferrer noopener" href="https://www.arduino.cc/en/software" data-type="URL" data-id="https://www.arduino.cc/en/software" target="_blank">Arduino IDE</a> as well (with the <a rel="noreferrer noopener" href="https://github.com/MCUdude/MightyCore" data-type="URL" data-id="https://github.com/MCUdude/MightyCore" target="_blank">MightyCore</a> board definitions added), and I was doing it that way for a while. However, the Arduino IDE is not very developer-friendly for large projects, and it makes simple things like having multiple source files much more difficult than they should be. PlatformIO, on the other hand, is much easier to use and I find myself fighting with it much less than with the Arduino IDE. PlatformIO is a <a rel="noreferrer noopener" href="https://code.visualstudio.com/" data-type="URL" data-id="https://code.visualstudio.com/" target="_blank">Visual Studio Code</a> extension, so you&#8217;ll need that installed, but it&#8217;s cross-platform, supports a large number of microcontrollers, and can use libraries written for the Arduino IDE. In theory, it even supports debugging over <a rel="noreferrer noopener" href="https://en.m.wikipedia.org/wiki/JTAG" data-type="URL" data-id="https://en.m.wikipedia.org/wiki/JTAG" target="_blank">JTAG</a> if the chip supports it. I haven&#8217;t tried that yet, but the ATmega1284 does claim to support it. PlatformIO is still a tad rough around the edges, but it&#8217;s so much better than the Arduino IDE experience that I highly recommend it.</p>



<h3 class="wp-block-heading">Peripheral Devices</h3>



<p>The ApOPL3xy has several peripheral devices attached to the microcontroller. &#8220;Peripheral&#8221; here means that the device is separate from the microcontroller chip, not necessarily that it&#8217;s detachable from the circuit board. Each of these peripherals communicates with the microcontroller using the SPI protocol. The one exception to this is the MIDI input port, which communicates using TTL Serial and is therefore connected to one of the microcontroller&#8217;s <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Universal_synchronous_and_asynchronous_receiver-transmitter" data-type="URL" data-id="https://en.wikipedia.org/wiki/Universal_synchronous_and_asynchronous_receiver-transmitter" target="_blank">USART</a> (i.e. hardware serial) interfaces.</p>



<p>The ApOPL3xy contains the following peripheral devices:</p>



<ul class="wp-block-list">
<li>OPL3 FM synthesis chip (Yamaha <a href="https://www.yamaha-tech.com/wiki/Yamaha_YMF262" data-type="URL" data-id="https://www.yamaha-tech.com/wiki/Yamaha_YMF262" target="_blank" rel="noreferrer noopener">YMF262</a>)</li>



<li><a rel="noreferrer noopener" href="https://www.adafruit.com/product/198" data-type="URL" data-id="https://www.adafruit.com/product/198" target="_blank">20&#215;4 LCD character display module</a> (with Hitachi <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller" data-type="URL" data-id="https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller" target="_blank">HD44780</a>-compatible controller)</li>



<li>Input controls
<ul class="wp-block-list">
<li>2 <a href="https://blog.natebarney.com/2023/07/24/ec11-rotary-encoders/" data-type="post" data-id="384" target="_blank" rel="noreferrer noopener">EC11</a> <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Incremental_encoder" data-type="URL" data-id="https://en.wikipedia.org/wiki/Incremental_encoder" target="_blank">incremental</a> <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Rotary_encoder" data-type="URL" data-id="https://en.wikipedia.org/wiki/Rotary_encoder" target="_blank">rotary encoders</a></li>



<li>10 tactile push buttons</li>
</ul>
</li>



<li><a href="https://www.amazon.com/gp/product/B07BJ2P6X6" data-type="URL" data-id="https://www.amazon.com/gp/product/B07BJ2P6X6" target="_blank" rel="noreferrer noopener">MicroSD card module</a></li>



<li>128 kB SRAM (Microchip <a href="https://www.microchip.com/en-us/product/23LCV1024" data-type="URL" data-id="https://www.microchip.com/en-us/product/23LCV1024" target="_blank" rel="noreferrer noopener">23LCV1024</a>)</li>



<li>128 kB EEPROM (Microchip <a href="https://www.microchip.com/en-us/product/25AA1024" data-type="URL" data-id="https://www.microchip.com/en-us/product/25AA1024" target="_blank" rel="noreferrer noopener">25AA1024</a>)</li>



<li><a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MIDI" data-type="URL" data-id="https://en.wikipedia.org/wiki/MIDI" target="_blank">MIDI</a> 5-pin <a href="https://en.wikipedia.org/wiki/DIN_connector" data-type="URL" data-id="https://en.wikipedia.org/wiki/DIN_connector" target="_blank" rel="noreferrer noopener">DIN</a> input port
<ul class="wp-block-list">
<li>I&#8217;m thinking about adding a MIDI thru port as well</li>
</ul>
</li>



<li>Reset button</li>
</ul>



<h3 class="wp-block-heading">Reset Circuit</h3>



<p>The reset button is connected to the reset pins of both the ATmega1284 and the OPL3. Since this signal is not being handled by a GPIO pin, software debouncing is not really an option. I also wanted the reset signal to start out active, so that the chips connected to it would be reset at power on. So, I designed a <a href="https://en.wikipedia.org/wiki/RC_circuit" data-type="link" data-id="https://en.wikipedia.org/wiki/RC_circuit" target="_blank" rel="noreferrer noopener">simple RC (resistor-capacitor) circuit</a>, with its analog output converted to digital by means of two gates from a <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC14" target="_blank">74HC14</a> (six <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Schmitt_trigger" target="_blank">Schmitt-trigger</a> inverters).</p>



<figure class="wp-block-image aligncenter size-medium"><img loading="lazy" decoding="async" width="300" height="270" src="https://blog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Reset-Circuit-300x270.png" alt="" class="wp-image-642" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Reset-Circuit-300x270.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Reset-Circuit-768x691.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Reset-Circuit.png 971w" sizes="auto, (max-width: 300px) 100vw, 300px" /><figcaption class="wp-element-caption">ApOPL3xy Reset Circuit</figcaption></figure>



<p>The reset signal for both chips is active low (as it is for most chips with a reset pin), so the way this circuit works is as follows. One lead of a 10 μF capacitor is connected to <kbd>GND</kbd> (0V), so the other lead starts out at 0V as well. The second lead is connected to <kbd>V<sub>CC</sub></kbd> (+5V) through a 10kΩ resistor and a 1kΩ resistor in series, so the capacitor gradually charges to +5V over time. After about 60 ms, the voltage at the second lead becomes high enough to trigger the first inverter, which goes low, and then the second inverter goes high, deactivating the reset signal.</p>



<p>When the reset button is pressed, it connects the second lead of the capacitor to ground through just the 1kΩ resistor, allowing it to discharge quickly, bringing the reset signal low (active). The signal stays low until the reset button is released, at which time, it takes about another 60 ms for the capacitor to charge up enough to bring the reset signal high again. If the button bounces when pressed, that high frequency oscillation is filtered out by the slow-charging capacitor, which is acting as a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Low-pass_filter" data-type="link" data-id="https://en.wikipedia.org/wiki/Low-pass_filter" target="_blank">low-pass filter</a>.</p>



<p>Here are a couple of captures from my oscilloscope illustrating the behavior of the circuit. In these images, the yellow trace measures the voltage across the capacitor, the pink trace measures the output of the first inverter, and the blue trace measures the output of the second inverter, which is the reset signal itself. The first image shows the behavior as the system is powered on. You can see the capacitor charging and how the schmitt-trigger inverters react to it.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="800" height="420" src="https://blog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Power-On-Reset.png" alt="" class="wp-image-635" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Power-On-Reset.png 800w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Power-On-Reset-300x158.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Power-On-Reset-768x403.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">ApOPL3xy Power-On Reset Oscilloscope Capture</figcaption></figure>



<p>The second image shows the behavior when the reset button is pressed. In this capture, you can see the capacitor quickly discharging as soon as the button is pressed, and remain discharged until the button is released, then start slowly charging again.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="800" height="420" src="https://blog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Reset-Button.png" alt="" class="wp-image-636" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Reset-Button.png 800w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Reset-Button-300x158.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/ApOPL3xy-Reset-Button-768x403.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">ApOPL3xy Reset Button Oscilloscope Capture</figcaption></figure>



<h3 class="wp-block-heading" id="custom-spi-interface">Custom SPI Interface</h3>



<p>Three of the peripherals (MicroSD, SRAM, EEPROM) connected to the microcontroller natively communicate using SPI. The others do not, but in order to conserve <a href="https://en.wikipedia.org/wiki/General-purpose_input/output" data-type="URL" data-id="https://en.wikipedia.org/wiki/General-purpose_input/output" target="_blank" rel="noreferrer noopener">GPIO</a> pins, I built an SPI interface for them (except for the MIDI port) using shift registers. In a <a href="https://blog.natebarney.com/2023/07/16/gpio-pins-shift-registers-and-spi/" data-type="URL" data-id="https://blog.natebarney.com/2023/07/16/gpio-pins-shift-registers-and-spi/" target="_blank" rel="noreferrer noopener">recent post</a>, I described how this works in more detail. I could have used something a bit fancier, like the Microchip <a href="https://www.microchip.com/en-us/product/MCP23S17" data-type="URL" data-id="https://www.microchip.com/en-us/product/MCP23S17" target="_blank" rel="noreferrer noopener">MCP23S17</a> SPI I/O expander. But they&#8217;re more expensive and more proprietary than standard 74HC shift registers, and the shift registers work just fine.</p>



<p>The SPI interface is built from one <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC595" target="_blank">74HC595</a> (serial-in / parallel-out shift register), two <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC165" data-type="URL" data-id="https://www.ti.com/product/SN74HC165" target="_blank">74HC165</a>&#8216;s (parallel-in / serial-out shift registers), and one gate each from a 74HC14 and a <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC125" data-type="URL" data-id="https://www.ti.com/product/SN74HC125" target="_blank">74HC125</a> (four tri-state buffers). I could have used a <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC04" target="_blank">74HC04</a> instead of a 74HC14, as I&#8217;m not using the Schmitt trigger functionality, but I already had a 74HC14 in the design for the reset button circuit, and I wasn&#8217;t using all of its gates. This setup provides eight output pins and sixteen input pins, and uses only three of the microcontroller&#8217;s pins. Two of these pins, <kbd>SCK</kbd> and <kbd>MOSI</kbd>, are shared among all SPI devices, so this really only uses one extra pin: <span style="text-decoration: overline;"><kbd>SS</kbd></span>.</p>



<p>Since the LCD character display and the OPL3 sound chip are controlled independently from one another, I was able to use the same eight output pins from the 74HC595 for both, and so they&#8217;re connected to each of these peripherals&#8217; data busses. The sixteen input pins are connected to the two rotary encoders (three pins each) and the ten push buttons (one pin each).</p>



<h3 class="wp-block-heading">OPL3 FM Synthesis Chip</h3>



<p>If the ATmega1284 is the heart of the ApOPL3xy (and if you&#8217;ll forgive my briefly waxing poetic), then the Yamaha YMF262 (a.k.a. OPL3) is its soul. Well, perhaps brain and larynx would be a better metaphor; it just doesn&#8217;t have the same ring. But I digress…</p>



<p>The OPL3 is a sound synthesizer chip that implements <a rel="noreferrer noopener" href="https://en.m.wikipedia.org/wiki/Frequency_modulation_synthesis" data-type="URL" data-id="https://en.m.wikipedia.org/wiki/Frequency_modulation_synthesis" target="_blank">frequency modulation (FM) synthesis</a>. It was used in Creative Labs&#8217; popular <a rel="noreferrer noopener" href="https://en.m.wikipedia.org/wiki/Sound_Blaster_16" data-type="URL" data-id="https://en.m.wikipedia.org/wiki/Sound_Blaster_16" target="_blank">Sound Blaster 16</a> sound card released in 1992 for IBM-compatible PC&#8217;s, and many PC games of that era used it to produce their in-game music.</p>



<h5 class="wp-block-heading">FM Synthesis</h5>



<p>FM synthesis is a method of producing sound waves by combining simpler waves. The basic sound-producing unit in a synthesizer is the oscillator. An oscillator takes a few parameters: frequency/pitch, amplitude/volume, and waveform, and produces the sound wave with those characteristics. In the OPL line of synthesizer chips, and often with FM in general, oscillators are referred to as operators. The reason for this is the way FM synthesis uses oscillators.</p>



<p>Each voice, or independently controlled sound source, in FM is built from two or more oscillators. However, usually only one of these oscillators, called the carrier, actually produces sound. The other oscillators, called modulators for reasons which will soon be apparent, <em>modulate</em> the carrier by adjusting the carrier&#8217;s frequency up or down based on the amplitude of the waveform produced by the modulator. This can produce a wide variety of sounds, many of which approximate different musical instruments fairly well. With more than two oscillators, there may be multiple carriers sounding at once, and/or multiple modulators, modulating carriers or even other modulators. The reason FM oscillators are often called operators is that they <em>operate</em> on each other in this way.</p>



<p>The OPL3 contains thirty-six operators, which are paired together to provide eighteen independent two-operator voices. Up to six pairs of voices can be combined to create four-operator sounds, at the cost of a corresponding decrease in the number of simultaneous voices. If percussion mode is enabled, three two-operator melodic voices are exchanged for five percussion voices—one two-operator voice and four one-operator voices, for a total of twenty independent voices. (One-operator voices are not technically FM, since there&#8217;s no modulation happening, but they can be produced by the OPL3.)</p>



<h5 class="wp-block-heading">Microcontroller Interface</h5>



<p>The microcontroller interface to the OPL3 consists of a chip select pin (<span style="text-decoration: overline;"><kbd>CS</kbd></span>), a write enable pin (<span style="text-decoration: overline;"><kbd>WR</kbd></span>), a read enable pin (<span style="text-decoration: overline;"><kbd>RD</kbd></span>), two address pins (<kbd>A0</kbd>, <kbd>A1</kbd>), eight data pins (<kbd>D0</kbd>&#8211;<kbd>D7</kbd>), an interrupt request output pin (<span style="text-decoration: overline;"><kbd>IRQ</kbd></span>), and a reset pin (<span style="text-decoration: overline;"><kbd>IC</kbd></span>, or Initial Clear). To control the behavior of the synthesizer (e.g. play a note, adjust sound settings, etc.), the microcontroller uses these pins to set the value(s) of one or more registers within the chip. I won&#8217;t go into detail about the specific registers and their values, because the unofficial <a rel="noreferrer noopener" href="https://www.fit.vutbr.cz/~arnost/opl/opl3.html" data-type="URL" data-id="https://www.fit.vutbr.cz/~arnost/opl/opl3.html" target="_blank">OPL3 Programmer&#8217;s Guide</a> has already done an excellent job of that. They&#8217;re also described in the <a rel="noreferrer noopener" href="https://pdf1.alldatasheet.com/datasheet-pdf/view/103368/ETC/YMF262.html" data-type="URL" data-id="https://pdf1.alldatasheet.com/datasheet-pdf/view/103368/ETC/YMF262.html" target="_blank">datasheet</a>, but I find that does a rather poorer job.</p>



<p>To set a register, the microcontroller first needs to set the <span style="text-decoration: overline;"><kbd>CS</kbd></span> pin low (if it&#8217;s not already) to select the chip, the <kbd>A0</kbd> pin low to indicate it&#8217;s selecting a register, the <kbd>A1</kbd> pin either high or low to select one of the two banks of registers, and the <kbd>D0</kbd>&#8211;<kbd>D7</kbd> pins to the index of the register to set. The <span style="text-decoration: overline;"><kbd>WR</kbd></span> pin is then pulsed low then high to write the register index. Next, <kbd>A0</kbd> is set high to indicate the register&#8217;s value is being set, and <kbd>D0</kbd>&#8211;<kbd>D7</kbd> are set to the new value for the register. The <span style="text-decoration: overline;"><kbd>WR</kbd></span> pin is once again pulsed low then high, this time to write the register value. Finally, if the microcontroller is done setting registers, it can set the <span style="text-decoration: overline;"><kbd>CS</kbd></span> pin high to deselect the chip.</p>



<p>The ApOPL3xy connects the OPL3&#8217;s pins as follows. <span style="text-decoration: overline;"><kbd>CS</kbd></span> is connected to <kbd>GND</kbd> to permanently select the chip. <kbd>A0</kbd>, <kbd>A1</kbd>, <span style="text-decoration: overline;"><kbd>IC</kbd></span>, and <span style="text-decoration: overline;"><kbd>WR</kbd></span> are connected to individual GPIO pins on the ATmega1284, configured as output pins. (<kbd>A0</kbd> actually shares a GPIO pin with the LCD module&#8217;s <kbd>RS</kbd> pin.) <kbd>D0</kbd>&#8211;<kbd>D7</kbd> are connected to the eight shared output pins from the custom SPI interface built from shift registers. This pin sharing works because the OPL3&#8217;s <span style="text-decoration: overline;"><kbd>WR</kbd></span> is only brought low (active) when any other device using the shared pins is inactive. When <span style="text-decoration: overline;"><kbd>WR</kbd></span> is high, the OPL3 doesn&#8217;t care what the values of the address and data pins are (except for setup and hold times, but those are so short that, even at 20 MHz, it&#8217;s hard to violate them; see the datasheet for more details on that).</p>



<p>The <span style="text-decoration: overline;"><kbd>IRQ</kbd></span> and <span style="text-decoration: overline;"><kbd>RD</kbd></span> pins are rarely used. The OPL3 can generate timer interrupt signals on the <span style="text-decoration: overline;"><kbd>IRQ</kbd></span> pin to let the microcontroller know when a certain amount of time has elapsed, and the <span style="text-decoration: overline;"><kbd>RD</kbd></span> pin is only able to be used to get information about these interrupts. The Sound Blaster 16 did not use this feature, nor does the ApOPL3xy. Therefore, the ApOPL3xy connects the <span style="text-decoration: overline;"><kbd>RD</kbd></span> pin to <kbd>V<sub>CC</sub></kbd> to permanently disable reads, and leaves the <span style="text-decoration: overline;"><kbd>IRQ</kbd></span> pin disconnected.</p>



<h5 class="wp-block-heading">Digital-to-Analog Conversion</h5>



<p>The OPL3 doesn&#8217;t generate sound directly. Rather, it produces digital representations of waveform amplitude (called samples) at a rate of 49,716 samples per second. The samples are represented as 16-bit offset binary numbers, where 0 represents the most negative value, and 65,535 represents the most positive value. These samples are sent serially to a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Digital-to-analog_converter" data-type="URL" data-id="https://en.wikipedia.org/wiki/Digital-to-analog_converter" target="_blank">Digital-to-Analog Converter (DAC)</a> chip, the <a rel="noreferrer noopener" href="https://datasheetspdf.com/pdf-file/544518/Yamaha/YAC512/1" data-type="URL" data-id="https://datasheetspdf.com/pdf-file/544518/Yamaha/YAC512/1" target="_blank">YAC512</a>, which was designed as a companion chip to the YMF262.</p>



<p>The YAC512 takes the digital samples and converts them to an analog waveform which can be sent to an amplifier and then to a loudspeaker. Each YAC512 supports two audio channels, and the YMF262 can connect to two YAC512&#8217;s, giving four audio channels. Each of the OPL3&#8217;s voices can be configured to be output to any combination of the four audio channels. </p>



<h3 class="wp-block-heading">LCD Character Display Module</h3>



<p>This is a pretty standard component for a lot of homebrew projects. It&#8217;s a twenty-column by four-row character display module with an LED backlight and an integrated controller (Hitachi HD44780). It also comes in a sixteen-column by two-row variety, but I wanted the little bit of extra space afforded by the larger module. Ben Eater has an excellent <a rel="noreferrer noopener" href="https://youtu.be/FY3zTUaykVo" data-type="URL" data-id="https://youtu.be/FY3zTUaykVo" target="_blank">video</a> (to be honest, <em>all</em> of his videos are excellent) in which he connects a 16&#215;2 version to his 65C02-based breadboard computer. The <a rel="noreferrer noopener" href="https://pdf1.alldatasheet.com/datasheet-pdf/view/63673/HITACHI/HD44780.html" data-type="URL" data-id="https://pdf1.alldatasheet.com/datasheet-pdf/view/63673/HITACHI/HD44780.html" target="_blank">datasheet</a> for the HD44780 is surprisingly good, and details all of the instructions that can be sent to the module.</p>



<p>The microcontroller interface consists of eight data pins (<kbd>D0</kbd>&#8211;<kbd>D7</kbd>) and three control pins: enable (<kbd>E</kbd>), register select (<kbd>RS</kbd>), and read/write (<kbd>RW</kbd>). There are five other pins, two for power, two for backlight power, and one to set the contrast of the display, for a total of sixteen pins, but only eleven need to be connected to the microcontroller.</p>



<p>To send instructions to the LCD module, a microcontroller first needs to set <kbd>RW</kbd> low, to signal a write, and <kbd>RS</kbd> low, to signal an instruction. Next, <kbd>D0</kbd>&#8211;<kbd>D7</kbd> are set to the instruction to send, and finally <kbd>E</kbd> is pulsed high then low to send the command. Sending data (i.e.characters) follows the same process except that <kbd>RS</kbd> is set high to signal data rather than an instruction, and <kbd>D0</kbd>&#8211;<kbd>D7</kbd> are set to the <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/ASCII" data-type="URL" data-id="https://en.wikipedia.org/wiki/ASCII" target="_blank">ASCII</a> value of the character to send.</p>



<p>The LCD module also supports a four-bit mode, in which only <kbd>D4</kbd>&#8211;<kbd>D7</kbd> need to be connected. This would be one way to conserve GPIO pins, but the ApOPL3xy doesn&#8217;t take this approach. This mode takes twice as long to send instructions and data. Because each instruction and character is still eight bits, each one takes two cycles to send. However, the main reason the ApOPL3xy uses the eight-bit mode is that it simplifies the code to do so, and the GPIO pressure is dealt with another way (see below).</p>



<p>Character data can be read back out of the LCD module, if desired, by setting <kbd>RS</kbd> and <kbd>RW</kbd> high, pulsing <kbd>E</kbd> high then low, and reading the value of <kbd>D0</kbd>&#8211;<kbd>D7</kbd>. There is also a &#8220;busy&#8221; flag which can be read by setting <kbd>RS</kbd> low, setting <kbd>RW</kbd> high, pulsing <kbd>E</kbd> high then low, and reading the value of <kbd>D7</kbd>. The busy flag indicates that the HD44780 is still processing the last instruction it received.</p>



<p>If an instruction is sent while the busy flag is high, the instruction will not execute, and the HD44780 will take longer to complete its current action, so this should be avoided. However, the datasheet lists the maximum duration for each instruction, so another way to avoid this situation is simply to wait long enough between instructions. This can be slightly slower, but the longest delay needed is about 1.5 ms, and most are less than 50 µs, so it&#8217;s not terrible, and it simplifies the connections if reading doesn&#8217;t need to be supported. Therefore, this is the approach the ApOPL3xy takes.</p>



<p>The ApOPL3xy connects the LCD module&#8217;s pins as follows. <kbd>RS</kbd> and <kbd>E</kbd> are connected to GPIO pins on the ATmega1284, configured as outputs. (<kbd>RS</kbd> shares a pin with the OPL3&#8217;s <kbd>A0</kbd> pin.) <kbd>D0</kbd>&#8211;<kbd>D7</kbd> are connected to the eight shared output pins from the custom SPI interface built from shift registers. As with the OPL3, this pin sharing works because the LCD&#8217;s <kbd>E</kbd> pin is only brought high when any other device using the shared pins is inactive. When <kbd>E</kbd> is low, the LCD doesn&#8217;t care what the values of <kbd>RS</kbd>, <kbd>RW</kbd>, and <kbd>D0</kbd>&#8211;<kbd>D7</kbd> are.</p>



<p>Some readers might be wondering whether the ApOPL3xy uses the Arduino <a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/libraries/liquidcrystal/" data-type="URL" data-id="https://www.arduino.cc/reference/en/libraries/liquidcrystal/" target="_blank">LiquidCrystal</a> library to control the LCD module, and if not, why not. The ApOPL3xy doesn&#8217;t use this library because the library expects all of the LCD&#8217;s pins to be connected directly to the microcontroller&#8217;s GPIO pins, and that&#8217;s not how the module is connected in this case. To do so would have used more GPIO pins than I would have liked. The LCD control code in the ApOPL3xy&#8217;s firmware has an API modeled after LiquidCrystal, because it has a decent design, and because that might be more familiar to some developers.</p>



<h3 class="wp-block-heading">Input Controls</h3>



<p>The user interface for the ApOPL3xy consists of the LCD character display module previously discussed, and a number of input controls. Specifically, two EC11 rotary encoders, and ten momentary tactile push buttons. Each encoder has three output pins, and the ten push buttons have one each, for a total of sixteen. These outputs are connected to the sixteen inputs provided by the 74HC165&#8217;s in the custom SPI interface.</p>



<p>The shift registers in the SPI interface are part of the reason there are so many buttons. With six inputs needed for the two encoders, a single 74HC165 would only provide enough inputs for two more buttons. I felt that this wouldn&#8217;t be enough, so I added another 74HC165 with another eight inputs. Rather than let some of them go unused, I connected a button to each of them, for a total of ten. At present, the firmware only uses five of the buttons, but I&#8217;m sure I&#8217;ll be able to find uses for the others.</p>



<p>The buttons each have two pairs of pins, but the pins in each pair are shorted together, so there are effectively only two pins per button. I believe, but I&#8217;m not 100% certain, that the redundant pins are there to provide structural support when soldered to a circuit board. While the button is pressed, all four pins are shorted together.</p>



<p>EC11 rotary encoders are knobs that can be turned in arbitrarily many discrete steps in either direction, unlike a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Potentiometer" data-type="URL" data-id="https://en.wikipedia.org/wiki/Potentiometer" target="_blank">potentiometer</a>, which turns continuously, but has limited extents. The encoders send a signal for each clockwise and counter-clockwise step, and they also serve as push buttons when pressed. If you&#8217;re curious to know how these work in more detail, check out my <a rel="noreferrer noopener" href="https://blog.natebarney.com/2023/07/24/ec11-rotary-encoders/" data-type="URL" data-id="https://blog.natebarney.com/2023/07/24/ec11-rotary-encoders/" target="_blank">recent post</a> on the topic.</p>



<p>These controls are all wired up in the ApOPL3xy to produce active-low signals. This means that each shift register input pin sees a low signal while, for example, the connected button is pressed, and a high signal otherwise. I don&#8217;t have a strong reason for making the controls&#8217; outputs active-low rather than active-high. It would have worked just as well the other way.</p>



<p>The ApOPL3xy deals with the problem of <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Switch#Contact_bounce" data-type="URL" data-id="https://en.wikipedia.org/wiki/Switch#Contact_bounce" target="_blank">contact bounce</a> by applying a software debouncing algorithm. This algorithm acts as a <a rel="noreferrer noopener" href="https://en.m.wikipedia.org/wiki/Finite-state_machine" data-type="URL" data-id="https://en.m.wikipedia.org/wiki/Finite-state_machine" target="_blank">state machine</a> which only transitions states after a configured minimum time is spent in each state. In this case, the states are (effectively) idle, idle wait, active, and active wait. The wait states are the ones that require minimum durations before transitioning to its non-wait counterpart. If there&#8217;s interest, I could make another post going into detail about the various hardware and software debouncing solutions, and the pros and cons of each.</p>



<h3 class="wp-block-heading">MicroSD Card Module</h3>



<p>The ApOPL3xy includes a MicroSD card module to allow it to read <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/VGM_(file_format)" data-type="URL" data-id="https://en.wikipedia.org/wiki/VGM_(file_format)" target="_blank">VGM</a> (and eventually MIDI) files for playback, as well as to load and save data to and from the EEPROM. I&#8217;m looking into the possibility of using a bootloader for the ATmega1284 that can load new firmware from the SD card, so an ISP programmer wouldn&#8217;t be required, but my investigation into that is not yet complete.</p>



<p>The MicroSD module I&#8217;m using was meant for Arduinos. It&#8217;s a small board with a slot for the card, a pin header to connect to the microcontroller, and a few extra components to shift voltage levels between the 5V the microcontroller uses and the 3.3V that the card uses. I may, in some future version of the ApOPL3xy, ditch the separate module and just incorporate a card slot and a level shifter chip directly. The card itself contains all the circuitry needed for the actual control and data interface, so it would be relatively straightforward. The module is convenient however, so that&#8217;s what I&#8217;m using for now.</p>



<p>SD (and MicroSD) cards natively use a four-bit parallel protocol, and that&#8217;s the way most modern devices (e.g. computers, smartphones, etc.) talk to them. Using this mode enables the highest transfer rates that the card can support. However, these cards also provide an SPI interface, which is how most smaller microcontrollers, including the ApOPL3xy, talk to them. This method is slower, but still fast enough for how the ApOPL3xy uses the card, it&#8217;s supported in hardware by the microcontroller, and it only uses up one additional GPIO pin for the SPI <span style="text-decoration: overline"><kbd>SS</kbd></span> signal.</p>



<p>One issue I ran into is that SD cards are not particularly well-behaved when it comes to SPI. Specifically, the cards don&#8217;t seem to release the <kbd>MISO</kbd> line when its <span style="text-decoration: overline"><kbd>SS</kbd></span> line is brought high, interfering with all the other devices on the SPI bus. At least this is the case with the module I&#8217;m using, but I believe this is a general phenomenon. To fix this, I used a <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC125" data-type="URL" data-id="https://www.ti.com/product/SN74HC125" target="_blank">74HC125</a> tri-state buffer chip to disconnect the SD card&#8217;s MISO line when its <span style="text-decoration: overline"><kbd>SS</kbd></span> line is high. In fact, I already had a 74HC125 with unused gates in the circuit, because I needed to do the same thing with the 74HC165 shift registers as well.</p>



<p>The SPI interface to the SD card provides raw read/write access to the bytes stored on the card. However, that&#8217;s not really sufficient to be able to read and write files on the card. At least, not if the card needs to be able to be read and written by other devices as well. Most storage devices, including SD cards, organize the large amount of data stored on them by using a <a href="https://en.wikipedia.org/wiki/File_system" data-type="URL" data-id="https://en.wikipedia.org/wiki/File_system" target="_blank" rel="noreferrer noopener">filesystem</a>. Filesystems keep track of file metadata like filenames and file size and provide controllers with a way to work with files instead of just raw data.</p>



<p>The topic of filesystems is vast, and well outside the scope of this article, but a common filesystem used on SD cards is called <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/File_Allocation_Table#FAT32" data-type="URL" data-id="https://en.wikipedia.org/wiki/File_Allocation_Table#FAT32" target="_blank">FAT32</a>. FAT32 was developed by Microsoft for <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Windows_95" data-type="URL" data-id="https://en.wikipedia.org/wiki/Windows_95" target="_blank">Windows 95</a>, as an extension to their earlier filesystems <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/File_Allocation_Table#Final_FAT16" data-type="URL" data-id="https://en.wikipedia.org/wiki/File_Allocation_Table#Final_FAT16" target="_blank">FAT16</a> and <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/File_Allocation_Table#FAT12" data-type="URL" data-id="https://en.wikipedia.org/wiki/File_Allocation_Table#FAT12" target="_blank">FAT12</a>, developed for <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MS-DOS" data-type="URL" data-id="https://en.wikipedia.org/wiki/MS-DOS" target="_blank">MS-DOS</a>. FAT32 has become a de facto standard for removable media, as its relative simplicity compared to other filesystems has enabled many different computer operating systems and embedded systems to implement support for it. This is slowly being superseded by Microsoft&#8217;s more modern <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/ExFAT" data-type="URL" data-id="https://en.wikipedia.org/wiki/ExFAT" target="_blank">exFAT</a> filesystem, but support for this is still far from ubiquitous.</p>



<p>The ApOPL3xy expects its SD card to be formatted with the FAT32 filesystem, and rather than implementing a FAT32 filesystem driver from scratch, it makes use of the <a href="https://github.com/greiman/SdFat" data-type="URL" data-id="https://github.com/greiman/SdFat" target="_blank" rel="noreferrer noopener">SdFat</a> library. This library takes care of both the low-level SD card interface for handling raw data and the FAT32 filesystem interface. It&#8217;s been in development for many years, and at the time of this writing, is still in active development. It manages to pack a tremendous amount of capability into a surprisingly small amount of space, and does so very efficiently. It even has optional support for exFAT, but ApOPL3xy doesn&#8217;t enable that, at least not yet.</p>



<h3 class="wp-block-heading">External SRAM and EEPROM</h3>



<p>Even though the ATmega1284 has much larger memory capacities than the ATmega328P, it&#8217;s still not enough to store as many synthesizer patches (i.e. sound settings to emulate various instruments) as I want to be able to. Each patch for the OPL3 (at least as I have currently implemented it) is 23 bytes, plus a 24-byte name string, giving 47 bytes per patch. The <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/General_MIDI" target="_blank">General MIDI</a> specification includes 128 melodic instruments, and 61 percussive instruments, for a total of 189 * 47 = 8,883 bytes, more than half of the available SRAM space, and more than double the available EEPROM space. Furthermore, I&#8217;d eventually like to be able to store and select from multiple banks of patches.</p>



<p>To deal with this, the ApOPL3xy includes an external (i.e. not part of the microcontroller) SRAM chip (Microchip 23LCV1024) and an external EEPROM chip (Microchip 25AA1024), each with 128 kB of space. These chips, like most of the other peripherals in the ApOPL3xy, communicate via the SPI protocol. The specific details of the command interface can be found in the datasheets for these chips, but the basic structure for both chips is: write a command byte (i.e. &#8220;read&#8221;, &#8220;write&#8221;), write a three-byte address, then read or write as many bytes as desired. Because these chips hold 128 kB of data, only seventeen bits are needed to encode an address, and the seven most significant bits of the three-byte address are ignored.</p>



<p>Writing to the EEPROM is slightly more complicated. Its storage is organized into 256-byte pages, and each write operation can only modify a single page. To write to multiple pages, multiple write operations must be performed. To write to a page, first the status register must be checked to ensure that the chip has completed its last operation. Then a &#8220;write enable&#8221; command byte must be sent, then the chip select line must be brought high and then low before the &#8220;write&#8221; command is sent, followed by the 3-byte address, followed by up to 256 bytes of data. Once the last byte of the page is written, the chip select is brought high, and the cycle is repeated for any additional pages.</p>



<h3 class="wp-block-heading">MIDI Port</h3>



<p>The ApOPL3xy uses the <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MIDI" data-type="link" data-id="https://en.wikipedia.org/wiki/MIDI" target="_blank">MIDI</a> protocol to allow an instrument (like a keyboard) to control the FM synthesis chip. MIDI is a fairly straightforward protocol, and the <a rel="noreferrer noopener" href="https://www.midi.org/specifications-old/item/midi-din-electrical-specification" target="_blank">electrical interface</a> is reasonably simple to implement. The MIDI port itself is a female <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/DIN_connector" target="_blank">5-pin DIN</a> jack, and there are a few other components, such as an <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Opto-isolator" target="_blank">optocoupler</a> and a few resistors to provide isolation and level-shifting. The data signals transmitted via this port are essentially <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/RS-232" data-type="link" data-id="https://en.wikipedia.org/wiki/RS-232" target="_blank">RS-232 serial</a> signals, at 31,250 baud with eight data bits, one stop bit, and no parity bits per frame. The USARTs built into the ATmega1284 can directly process this kind of signal, so the data line from the MIDI-In port is connected to one of these.</p>



<p>At present, the breadboard version of the ApOPL3xy includes a single port for MIDI-In, but I will likely add a MIDI-Thru port as well. MIDI-Thru ports are ports which simply output whatever signals came in on the MIDI-In port. They&#8217;re also pretty simple electrically, and require no software support in the microcontroller, so it&#8217;s probably worth adding one, just in case it turns out to be useful.</p>



<h3 class="wp-block-heading">Conclusion</h3>



<p>This covers almost all of the hardware used in the ApOPL3xy. I did leave out the amplifier circuits, because I&#8217;m still fiddling around with their design. Once I sort that out to my satisfaction, I&#8217;ll likely make another post about that. As always, I hope this has been interesting and informative. Let me know if anything is unclear, or if you&#8217;d like more detail on anything in particular. </p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2023/10/08/apopl3xy-hardware-design/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>I Drove a Lamborghini</title>
		<link>https://oldblog.natebarney.com/2023/10/08/i-drove-a-lamborghini/</link>
					<comments>https://oldblog.natebarney.com/2023/10/08/i-drove-a-lamborghini/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Sun, 08 Oct 2023 19:44:21 +0000</pubDate>
				<category><![CDATA[Miscellany]]></category>
		<category><![CDATA[lamborghini]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=622</guid>

					<description><![CDATA[Last Christmas, my girlfriend Donnett got me a gift certificate to a company that offers supercar driving experiences, and I recently cashed it in. I could choose from 8 different cars, but it&#8217;s been a dream of mine for a long time to drive a Lamborghini, so it wasn&#8217;t a difficult decision to go with [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Last Christmas, my girlfriend Donnett got me a gift certificate to a <a href="https://www.thextremexperience.com/" data-type="link" data-id="https://www.thextremexperience.com/" target="_blank" rel="noreferrer noopener">company that offers supercar driving experiences</a>, and I recently cashed it in. I could choose from 8 different cars, but it&#8217;s been a dream of mine for a long time to drive a Lamborghini, so it wasn&#8217;t a difficult decision to go with the <a rel="noreferrer noopener" href="https://www.lamborghini.com/en-en/models/huracan" target="_blank">Lamborghini Huracán</a>. Yesterday, we went out to the <a rel="noreferrer noopener" href="https://www.poconoraceway.com/" target="_blank">Pocono Raceway</a> and I drove it three laps around the track.</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="768" src="https://blog.natebarney.com/wp-content/uploads/2023/10/lambo-1024x768.jpg" alt="" class="wp-image-623" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/10/lambo-1024x768.jpg 1024w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/lambo-300x225.jpg 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/lambo-768x576.jpg 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/lambo-1536x1152.jpg 1536w, https://oldblog.natebarney.com/wp-content/uploads/2023/10/lambo.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">The Lamborghini Huracán I drove</figcaption></figure>



<p>The car has a top speed of about 200 mph, and I had naïvely hoped to get close to that, but it took me a little more time than I thought it would to get a feel for the car and build up confidence. It was also unfortunately raining, and I was a little nervous about skidding, though I probably didn&#8217;t need to be. The instructor was encouraging me to go faster around the corners, and when I did, the car was extremely stable.  I got up to a top speed of about 85 mph, and while I had hoped to do better, it really was so much fun.</p>



<p>Donnett took a few videos with her phone, and here&#8217;s a good shot of the exterior of the car on the straightaway.</p>



<figure class="wp-block-video"><video height="360" style="aspect-ratio: 640 / 360;" width="640" controls src="https://blog.natebarney.com/wp-content/uploads/2023/10/lambo_straightaway.mp4"></video></figure>



<p>I also got a really nice video from the company running the event that shows the view from inside the car, with all sorts of metrics superimposed. (The brakes are really squeaky, but it turns out that&#8217;s just how they are on a Lamborghini.)</p>



<figure class="wp-block-video"><video height="1080" style="aspect-ratio: 1920 / 1080;" width="1920" controls src="https://blog.natebarney.com/wp-content/uploads/2023/10/xtreme_xperience_lamborghini_huracan_2023-10-04.mp4"></video></figure>



<p>I&#8217;m definitely going to do this again (hopefully while it&#8217;s sunny) and with the experience I have under my belt now, I think I&#8217;ll really be able to push some limits next time.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2023/10/08/i-drove-a-lamborghini/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2023/10/lambo_straightaway.mp4" length="1141401" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2023/10/xtreme_xperience_lamborghini_huracan_2023-10-04.mp4" length="345854891" type="video/mp4" />

			</item>
		<item>
		<title>Moon Blaster</title>
		<link>https://oldblog.natebarney.com/2023/08/19/moon-blaster/</link>
					<comments>https://oldblog.natebarney.com/2023/08/19/moon-blaster/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Sat, 19 Aug 2023 04:28:24 +0000</pubDate>
				<category><![CDATA[Retro Computers]]></category>
		<category><![CDATA[6502]]></category>
		<category><![CDATA[assembly]]></category>
		<category><![CDATA[c128]]></category>
		<category><![CDATA[c64]]></category>
		<category><![CDATA[commodore]]></category>
		<category><![CDATA[computers]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[programming]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=580</guid>

					<description><![CDATA[When I was about 11 or 12 years old, I wrote a simple game in Commodore 128 BASIC. It&#8217;s not very fancy, but it is a complete game, with sound, graphics, a title screen, and even a backstory. The object of the game is to fire missiles at a moon moving across the top of [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>When I was about 11 or 12 years old, I wrote a simple game in <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_128" data-type="link" data-id="https://en.wikipedia.org/wiki/Commodore_128" target="_blank">Commodore 128</a> <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/BASIC" data-type="link" data-id="https://en.wikipedia.org/wiki/BASIC" target="_blank">BASIC</a>. It&#8217;s not very fancy, but it <em>is</em> a complete game, with sound, graphics, a title screen, and even a backstory. The object of the game is to fire missiles at a moon moving across the top of the screen. The player can move the missile launcher to one of three bases using the joystick, and (of course) launch missiles with the fire button. By any standard, it&#8217;s not a great game, but I was pretty proud of it at the time.</p>



<p>The C128 included <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_BASIC" data-type="link" data-id="https://en.wikipedia.org/wiki/Commodore_BASIC" target="_blank">Commodore BASIC</a> 7.0, which was much better than the older Commodore BASIC 2.0 that was on the <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_64" data-type="link" data-id="https://en.wikipedia.org/wiki/Commodore_64" target="_blank">C64</a>. BASIC 7.0 had lots of additional commands to control the graphics and sound chips in the machine, read the joysticks, save and load binary files between RAM and disk, etc., and the computer came with a really nice <a href="https://www.commodore.ca/manuals/128_system_guide/toc.htm" data-type="link" data-id="https://www.commodore.ca/manuals/128_system_guide/toc.htm" target="_blank" rel="noreferrer noopener">system guide</a> <a rel="noreferrer noopener" href="http://www.zimmers.net/anonftp/pub/cbm/manuals/c128/C128_System_Guide.pdf" data-type="link" data-id="http://www.zimmers.net/anonftp/pub/cbm/manuals/c128/C128_System_Guide.pdf" target="_blank">[PDF]</a> that described all of them. <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MOS_Technology_6502" data-type="link" data-id="https://en.wikipedia.org/wiki/MOS_Technology_6502" target="_blank">6502</a> <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Machine_code" data-type="link" data-id="https://en.wikipedia.org/wiki/Machine_code" target="_blank">machine language</a> was out of my reach at the time—I didn&#8217;t have an <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Assembly_language#Assembler" data-type="link" data-id="https://en.wikipedia.org/wiki/Assembly_language#Assembler" target="_blank">assembler</a>, or even any reference material. The C128 included a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Machine_code_monitor" data-type="link" data-id="https://en.wikipedia.org/wiki/Machine_code_monitor" target="_blank">machine language monitor</a> with a rudimentary assembler, but I had no idea what it was for. Nevertheless, I decided to try to make a game using the enhanced C128 BASIC, and Moon Blaster was the result.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-Screenshot.png" alt="" class="wp-image-585" style="width:640px" width="640" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-Screenshot.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-Screenshot-300x257.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-Screenshot-768x659.png 768w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Moon Blaster screenshot</figcaption></figure>



<h5 class="wp-block-heading">Machine Language Port</h5>



<p>My <a rel="noreferrer noopener" href="https://blog.natebarney.com/2023/08/05/quest-for-tires/" data-type="link" data-id="https://blog.natebarney.com/2023/08/05/quest-for-tires/" target="_blank">recent foray</a> into C64 game reverse-engineering gave me an idea: re-write Moon Blaster in assembly language for the C64. I&#8217;ve never written a game in machine language for the C64 before, and this game is pretty simple—great for a first project. I had a couple main goals: 1) stay as true as possible to the original game, and 2) build both disk and cartridge versions. I&#8217;m pleased to report that I was successful. Here&#8217;s a short video of the new version of the game <strong><em>(headphone warning, it might be a bit loud)</em></strong>:</p>



<figure class="wp-block-video aligncenter"><video controls="" style="display:block; margin-left: auto; margin-right: auto;" width="320" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-ML-Gameplay.mp4"></video><figcaption style="text-align: center;" class="wp-element-caption">Moon Blaster ML gameplay</figcaption></figure>



<p>If you&#8217;d like to play the game, you can <a href="https://www.natebarney.com/files/moon-blaster/moon-blaster-ml.zip" data-type="link" data-id="https://www.natebarney.com/files/moon-blaster/moon-blaster-ml.zip">download it here</a>. The download contains cartridge and disk images for the new version, as well as the assembly source code for it. It also includes a disk image of the original BASIC game, if you&#8217;d like to compare the two. You&#8217;ll need a C64/C128 emulator (<a rel="noreferrer noopener" href="https://vice-emu.sourceforge.io/" data-type="link" data-id="https://vice-emu.sourceforge.io/" target="_blank">VICE</a> is a good one), or a real C64/C128. (If you&#8217;re using real hardware, I&#8217;ll assume you know how to get the images to it. If not, let me know.) The easiest way to start the game in VICE is to attach the cartridge image, either by using the menu or by pressing <kbd>Alt+C</kbd>. You&#8217;ll need to set up the joystick as well. VICE works with a game controller, or can emulate a joystick using the keyboard. It&#8217;s pretty straightforward to set up, but if you have trouble, let me know.</p>



<h5 class="wp-block-heading">It&#8217;s Not a Bug</h5>



<p>An amusing story (at least to me) about Moon Blaster is the way I inadvertently introduced the concept of a <a href="https://en.wikipedia.org/wiki/Critical_hit" data-type="link" data-id="https://en.wikipedia.org/wiki/Critical_hit">critical hit</a>. The moving objects on the screen—the moon, missile launcher, and missiles—were implemented as <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" data-type="link" data-id="https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" target="_blank">sprites</a>. C128 BASIC included commands for sprite collision detection, which I used to detect when the player got a hit. When two sprites touch, the program jumps to a specified line, which in this case plays the hit sound, updates the score, etc.</p>



<p>Unfortunately, BASIC is so slow that, in the original game, if the player hit the moon close to dead center, the missile collided <em>twice</em> before it was moved back to the launcher. This had the effect of doubling the hit sound and animation, and scoring twice instead of once. I recall trying to fix it, and being stumped. Ultimately, in the grand tradition of <a href="https://web.archive.org/web/20230305155314/http://catb.org/jargon/html/F/feature.html" data-type="link" data-id="https://web.archive.org/web/20230305155314/http://catb.org/jargon/html/F/feature.html" target="_blank" rel="noreferrer noopener">&#8220;It&#8217;s not a bug; it&#8217;s a feature,&#8221;</a> I decided just to add it to the instructions as an &#8220;intentional&#8221; bonus. However, the machine language version runs <em>much</em> faster, so this didn&#8217;t happen, and to stay true to the original, I actually had to write code specifically to replicate this <s>bug</s> feature.</p>



<h3 class="wp-block-heading">Technical Stuff</h3>



<p>The rest of this post contains technical details intended to be of interest to other programmers. If you&#8217;re not a programmer, you&#8217;re welcome to stick around, but if you want to bail at this point, I won&#8217;t mind <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" />. I won&#8217;t go over all of the game code, but there were a few things I was pretty happy with, and I thought they were worth talking about. The assembler I used for this project is <a rel="noreferrer noopener" href="https://cc65.github.io/doc/ca65.html" data-type="link" data-id="https://cc65.github.io/doc/ca65.html" target="_blank">ca65</a>, which is part of the <a rel="noreferrer noopener" href="https://cc65.github.io/" data-type="link" data-id="https://cc65.github.io/" target="_blank">cc65</a> compiler suite. In case it&#8217;s helpful, here&#8217;s a link to a <a rel="noreferrer noopener" href="http://6502.org/users/obelisk/6502/instructions.html" data-type="link" data-id="http://6502.org/users/obelisk/6502/instructions.html" target="_blank">6502 instruction set reference</a>.</p>



<h5 class="wp-block-heading">Linear Feedback Shift Register</h5>



<p>The original game initially used BASIC drawing commands to draw the star field. This was painfully slow, so I ended up changing it to load the bitmap memory from a disk file, which I had saved after running the drawing routines. This was still pretty slow, but was a noticeable improvement. When doing the assembly port, I had the raw power of a 1 MHz processor at my command, so I decided the game should draw the stars at startup every time. Doing this in machine code is way faster than loading a screen image from disk. Plus, with the cartridge version, there wouldn&#8217;t even be a disk.</p>



<p>However, I wanted the stars to be the same every time, too, so I needed a <a href="https://en.wikipedia.org/wiki/Pseudorandom_number_generator" data-type="link" data-id="https://en.wikipedia.org/wiki/Pseudorandom_number_generator" target="_blank" rel="noreferrer noopener">pseudorandom number generator</a>. The <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MOS_Technology_6581" data-type="link" data-id="https://en.wikipedia.org/wiki/MOS_Technology_6581" target="_blank">SID</a> sound chip in the C64 can be <a rel="noreferrer noopener" href="https://youtu.be/-ADjfx79wNg" data-type="link" data-id="https://youtu.be/-ADjfx79wNg" target="_blank">used to generate random numbers</a>, but there&#8217;s not a good way to seed it. So using that approach, the stars would change every game. Instead, I implemented a 16-bit <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Linear-feedback_shift_register" data-type="link" data-id="https://en.wikipedia.org/wiki/Linear-feedback_shift_register" target="_blank">Linear Feedback Shift Register (LFSR)</a> routine, and sampled bits from it to get pseudorandom coordinates for drawing the stars. I used the constants from the Wikipedia article, since that seemed as good a set as any. I don&#8217;t claim it&#8217;s the most optimal LFSR ever written for the 6502, but I think it&#8217;s pretty slick:</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.EXPORTZP LFSR_STATE = $fd

.CODE

.PROC lfsr_16

    ; get XOR of bits 15 and 13
    lda LFSR_STATE+1
    lsr a
    lsr a
    eor LFSR_STATE+1

    ; get XOR of bit 12
    lsr a
    eor LFSR_STATE+1

    ; get XOR of bit 10
    lsr a
    lsr a
    eor LFSR_STATE+1

    ; put result of XORs into carry flag
    lsr a
    lsr a
    lsr a

    ; rotate carry flag into LFSR_STATE as the LSB
    rol LFSR_STATE
    rol LFSR_STATE+1

    rts
.ENDPROC ; lfsr_16</pre>



<h5 class="wp-block-heading">Raster Interrupt Split-Screen</h5>



<p>The game is mostly graphical, but there are text elements to display the score and number of shots fired. C128 BASIC has a command (<kbd><a rel="noreferrer noopener" href="https://www.commodore.ca/manuals/128_system_guide/sect-17a.htm#17.11" data-type="link" data-id="https://www.commodore.ca/manuals/128_system_guide/sect-17a.htm#17.11" target="_blank">CHAR</a></kbd>) to draw character on a bitmap screen. I could have taken the same approach with the machine language port, but that would involve banking in the character ROM and copying the bitmap data for each character. It would be slow (although I had plenty of cycles to do it since the game is so simple) and ugly code. Instead, I decided to go a different direction—changing between graphics and text mode in the middle of the frame.</p>



<p>The <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MOS_Technology_VIC-II" data-type="link" data-id="https://en.wikipedia.org/wiki/MOS_Technology_VIC-II" target="_blank">VIC-II</a> video chip in the C64 can be configured to cause an interrupt when it reaches a specified scan line—a so-called raster interrupt. To achieve a split screen effect, one can enable the raster interrupt at the top of the screen, and then in the interrupt handler, turn on graphics mode, then set the raster interrupt somewhere in the middle of the screen, and turn on text mode. There are a few other details to take care of, but this works surprisingly well. To print the score, I can simply place the characters in the right location in screen memory. Here&#8217;s the code I used to enable the interrupt, and the interrupt handler that implements the split-screen:</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.INCLUDE "vic2.inc"

IRQ_VECTOR = $0314
KERNAL_ISR = $ea31
CIA1_IRQ = $dc0d
CIA2_IRQ = $dd0d

.DATA

FRAME_SYNC: .res 1

.CODE

.PROC setup_irq
    sei

    ; disable CIA-1 interrupts
    lda #%01111111
    sta CIA1_IRQ

    ; clear high bit of raster counter
    and VIC2::CR1
    sta VIC2::CR1

    ; acknowledge pending interrupts
    lda CIA1_IRQ
    lda CIA2_IRQ

    ; set raster interrupt
    lda #0 ; interrupt on this raster line
    sta VIC2::RST

    ; clear frame sync variable
    sta FRAME_SYNC

    ; set interrupt vector
    lda #&lt;split_screen_isr
    sta IRQ_VECTOR
    lda #>split_screen_isr
    sta IRQ_VECTOR+1

    ; enable raster interrupts
    lda #%00000001
    sta VIC2::IMA

    cli
    rts
.ENDPROC ; setup_irq

.PROC teardown_irq
    sei

    ; disable raster interrupts
    lda #%00000000
    sta VIC2::IMA

    ; acknowledge pending interrupts
    asl VIC2::IRQ ; acknowledge VIC-II raster interrupt by clearing low bit

    ; enable CIA-1 interrupts
    lda #%11111111
    sta CIA1_IRQ

    ; restore interrupt vector
    lda #&lt;KERNAL_ISR
    sta IRQ_VECTOR
    lda #>KERNAL_ISR
    sta IRQ_VECTOR+1

    cli
    rts
.ENDPROC ; teardown_irq

.PROC split_screen_isr

    ; clear decimal flag
    cld

    ; look at current raster line to see if we should turn bitmap on or off
    lda VIC2::CR1
    ldx VIC2::RST
    cpx #100
    bcs bitmap_off

    ; turn bitmap mode on
bitmap_on:
    inc FRAME_SYNC
    ora #%00100000  ; bitmap on
    sta VIC2::CR1
    lda VIC2::PTR
    ora #%00001100  ; set bitmap memory to $2000-$3FFF
    and #%11111100
    sta VIC2::PTR
    lda #209        ; next interrupt raster line
    jmp return

    ; turn bitmap mode off
bitmap_off:
    and #%11011111  ; bitmap off
    sta VIC2::CR1
    lda VIC2::PTR
    ora #%00000100  ; set bitmap memory to $0000-$1FFF
    and #%11110100
    sta VIC2::PTR
    lda #0          ; next interrupt raster line

return:
    sta VIC2::RST
    asl VIC2::IRQ   ; acknowledge VIC-II raster interrupt by clearing low bit
    jmp KERNAL_ISR
.ENDPROC ; split_screen_isr
</pre>



<h5 class="wp-block-heading">Exit to BASIC</h5>



<p>C64 games rarely include an option to exit the game and return to BASIC. Even though 64K of RAM was pretty roomy for the time, most games needed a great deal of it, and so banked out the BASIC ROM and clobbered BASIC memory areas. It&#8217;s also possible this was used as a rudimentary form of copy protection. However, the original Moon Blaster included an option to quit; being a BASIC program itself, this was simple to do.</p>



<p>I wanted the machine language port to have this same capability. It was in the original game, and I&#8217;m not worried about copy protection. The game doesn&#8217;t take much memory (less than 5K of code and data, not counting the 8K bitmap and 1K text screen memory areas), and I was able to place everything so as not to interfere with BASIC. To exit to BASIC, the disk-based game can simply jump to the BASIC warm-start routine <kbd>$E38B</kbd>. However, there are two problems with this.</p>



<p>The first problem is that just jumping to BASIC warm-start doesn&#8217;t work at all if you&#8217;re running the cartridge version, because BASIC isn&#8217;t initialized in that case. To address this, I call a few BASIC ROM routines from the cartridge entry point to initialize BASIC to the point that it can be warm-started. Here&#8217;s the code I used to do that:</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="14-16, 21-23" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.INCLUDE "main.inc"
.SEGMENT "START"

; entry point which jumps away to BASIC, so it works right from a cartidge
.PROC start

    ; initialize the KERNAL
    jsr $fda3 ; initialize I/O devices
    jsr $fd50 ; test RAM and initialize memory pointers
    jsr $fd15 ; initialize KERNAL vectors
    jsr $e518 ; initialize the screen and keyboard
    cli ; enable interrupts

    ; initialize BASIC
    jsr $e453 ; initialize BASIC vectors
    jsr $e3bf ; initialize BASIC RAM

    ; run the game
    jsr main

    ; jump to BASIC
    ldx #$80 ; no error code
    jmp $e38b ; BASIC warm start

.ENDPROC ; start</pre>



<p>The second problem is that I wanted the user to be able to re-start the game simply by typing <kbd>RUN</kbd>. This kind of works with the disk version, but that runs the BASIC loader program and loads the whole game from disk again, even though it&#8217;s still in memory. With a cartridge, there would be no BASIC program to <kbd>RUN</kbd> at all. To deal with this, I included a simple BASIC program to restart the game in the constant data section. Before exiting, I replace whatever BASIC program is loaded (possibly none) with this program, which immediately starts the game again when run. To do this, not only the BASIC program area at <kbd>$0801</kbd> needs to be populated, but also several pointers that BASIC uses, starting at <kbd>$2B</kbd> in zero page. (The details about these locations can be found in a <a href="http://sta.c64.org/cbm64mem.html" target="_blank" rel="noreferrer noopener">C64 memory map</a>.) Here&#8217;s the code I used to do that:</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="34-49,51-66" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.RODATA

; BASIC program to re-start the game
;
; 10 SYS 32804
;
BASIC_LOADER_ADDR = $0801
BASIC_LOADER:
.byte $0d, $08, $0a, $00, $9e, $20, $33, $32, $38, $30, $34, $00, $00, $00
BASIC_LOADER_SIZE = * - BASIC_LOADER

BASIC_POINTERS_ADDR = $2b
BASIC_POINTERS:
.byte $01, $08, $0f, $08, $0f, $08, $0f, $08, $00, $80, $00, $00, $00, $80
BASIC_POINTERS_SIZE = * - BASIC_POINTERS

.SEGMENT "MAIN"

; entry point which does an RTS, so it works right from BASIC's SYS command
.PROC main

    ; initialize SID
    jsr init_sid

    ; initialize screen
    jsr init_screen

    ; show title screen
    jsr title_screen

    ; restore screen
    jsr restore_screen

    ; put the BASIC loader into BASIC program memory
    lda #&lt;BASIC_LOADER
    sta $fb
    lda #>BASIC_LOADER
    sta $fc
    lda #&lt;BASIC_LOADER_ADDR
    sta $fd
    lda #>BASIC_LOADER_ADDR
    sta $fe
    ldy #0
loop1:
    lda ($fb),y
    sta ($fd),y
    iny
    cpy #BASIC_LOADER_SIZE
    bne loop1

    ; update the BASIC pointers for the new BASIC program just copied
    lda #&lt;BASIC_POINTERS
    sta $fb
    lda #>BASIC_POINTERS
    sta $fc
    lda #&lt;BASIC_POINTERS_ADDR
    sta $fd
    lda #>BASIC_POINTERS_ADDR
    sta $fe
    ldy #0
loop2:
    lda ($fb),y
    sta ($fd),y
    iny
    cpy #BASIC_POINTERS_SIZE
    bne loop2

    rts

.ENDPROC ; main</pre>



<p>With those two problems solved, the user can cleanly exit the game and then jump right back in, with no load time, regardless of whether the game is in cartridge or disk form. It&#8217;s not earth-shattering or anything, but I think it&#8217;s pretty cool.</p>



<h3 class="wp-block-heading">Closing Thoughts</h3>



<p>I had a lot of fun doing this project, and learned quite a few things. It was neat to revisit something I wrote so many years ago, and to breathe new life into it. The game won&#8217;t win any awards, but that&#8217;s okay. If you download and the play the game, or look at the code, and have any questions about how I did this or that, feel free to ask. If you have any suggestions on how I could have done things better, I&#8217;d love to hear about that too.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2023/08/19/moon-blaster/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-ML-Gameplay.mp4" length="491883" type="video/mp4" />

			</item>
		<item>
		<title>Quest for Tires</title>
		<link>https://oldblog.natebarney.com/2023/08/05/quest-for-tires/</link>
					<comments>https://oldblog.natebarney.com/2023/08/05/quest-for-tires/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Sat, 05 Aug 2023 08:32:35 +0000</pubDate>
				<category><![CDATA[Retro Computers]]></category>
		<category><![CDATA[6502]]></category>
		<category><![CDATA[assembly]]></category>
		<category><![CDATA[c64]]></category>
		<category><![CDATA[commodore]]></category>
		<category><![CDATA[computers]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[reverse-engineering]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=457</guid>

					<description><![CDATA[This is a story of nostalgia, retro-computing, gaming, reverse-engineering, and fixing a decades-old bug. It might be a bit long, and can get rather technical in places (a lot of places), but if you're into that sort of thing, I think you may find it enjoyable. [...]]]></description>
										<content:encoded><![CDATA[
<p>This is a story of nostalgia, retro-computing, gaming, reverse-engineering, and fixing a decades-old bug. It might be a bit long, and can get rather technical in places (a lot of places), but if you&#8217;re into that sort of thing, I think you may find it enjoyable.</p>



<h3 class="wp-block-heading">The Game</h3>



<p>When I was a kid, my family had a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_128" data-type="URL" data-id="https://en.wikipedia.org/wiki/Commodore_128" target="_blank">Commodore 128</a> (C128) computer. The C128 was the successor to the famous <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_64" data-type="URL" data-id="https://en.wikipedia.org/wiki/Commodore_64" target="_blank">Commodore 64</a> (C64), and it could be booted into a C64-compatible mode. I used this computer all the time, for playing games, writing papers for school, and, of course, programming. Probably most of my time on the computer was spent playing games. One of my favorites was a game called <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/B.C.%27s_Quest_for_Tires" data-type="URL" data-id="https://en.wikipedia.org/wiki/B.C.%27s_Quest_for_Tires" target="_blank">B.C.&#8217;s Quest for Tires</a>. It&#8217;s a pretty simple side-scrolling game in which the player moves steadily to the right, and has to jump and duck obstacles to proceed.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="660" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Screenshot.png" alt="" class="wp-image-553" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Screenshot.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Screenshot-300x258.png 300w" sizes="auto, (max-width: 768px) 100vw, 768px" /><figcaption class="wp-element-caption">Quest for Tires screenshot</figcaption></figure>



<p>I had, and still have, a copy (of dubious provenance) of the C64 version of the game on a floppy disk. A couple of years ago, I used a <a rel="noreferrer noopener" href="http://www.go4retro.com/products/zoomfloppy/" data-type="URL" data-id="http://www.go4retro.com/products/zoomfloppy/" target="_blank">ZoomFloppy</a> with my <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_1571" data-type="URL" data-id="https://en.wikipedia.org/wiki/Commodore_1571" target="_blank">Commodore 1571</a> disk drive to make images of many of my old Commodore disks, including the one containing Quest for Tires. I loaded the game up in the <a rel="noreferrer noopener" href="https://vice-emu.sourceforge.io/" data-type="URL" data-id="https://vice-emu.sourceforge.io/" target="_blank">VICE</a> emulator and started playing, but the nostalgia I felt was quickly and rudely interrupted when the game crashed.</p>



<figure style="display: block; margin-left: auto; margin-right: auto;" class="wp-block-video"><video style="display: block; margin-left: auto; margin-right: auto;" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-For-Tires-Crash.mp4" controls="controls" width="384" height="330"></video>
<figcaption style="text-align: center;" class="wp-element-caption">Quest for Tires Gameplay and Crash</figcaption>
</figure>



<p>One of the things the player can do in the game is to change the main character&#8217;s speed, with a minimum speed of 10 and a maximum of 80. Partway through the game, the minimum speed is increased to 40. (There are no units specified, but I assume it&#8217;s meant to be in miles per hour.) The faster you go, the more difficult the game is, but the more points you get along the way. Unfortunately, every time I got the speed up to 80, the game crashed. Everything else seemed to work fine, so I just kept my speed below 80, but it was annoying that the game crashed at all.</p>



<p>A few days ago, I was re-watching <a rel="noreferrer noopener" href="https://www.youtube.com/@8_Bit" data-type="URL" data-id="https://www.youtube.com/@8_Bit" target="_blank">8-Bit Show and Tell</a>&#8216;s YouTube video about <a rel="noreferrer noopener" href="https://youtu.be/r4Px_JIh7Dg" data-type="URL" data-id="https://youtu.be/r4Px_JIh7Dg" target="_blank">fixing an old bug in the C64 version of Pac-Man</a>, and I was inspired to see if I could resolve the mystery of the Quest for Tires crash, and possibly even fix it. (As an aside, if you have any interest in retro-computing in general, and Commodore computers in particular, you should definitely check out that channel. Right now. I&#8217;ll be here when you get back.)</p>



<h3 class="wp-block-heading">Finding the Bug</h3>



<p>To figure out what&#8217;s going on, let&#8217;s use VICE&#8217;s built-in <a href="https://vice-emu.sourceforge.io/vice_12.html" data-type="URL" data-id="https://vice-emu.sourceforge.io/vice_12.html" target="_blank" rel="noreferrer noopener">machine language monitor</a>, which is a <a href="https://en.wikipedia.org/wiki/Machine_code_monitor" data-type="URL" data-id="https://en.wikipedia.org/wiki/Machine_code_monitor" target="_blank" rel="noreferrer noopener">debugging tool</a> that can examine and modify code and data in the machine&#8217;s memory. Some reference material to keep handy will be:</p>



<ul class="wp-block-list">
<li><a rel="noreferrer noopener" href="https://sta.c64.org/" data-type="URL" data-id="https://sta.c64.org/" target="_blank">Joe Forster</a>&#8216;s <a href="https://sta.c64.org/cbm64mem.html" data-type="URL" data-id="https://sta.c64.org/cbm64mem.html" target="_blank" rel="noreferrer noopener">Commodore 64 memory map</a></li>



<li><a rel="noreferrer noopener" href="https://www.pagetable.com/" data-type="URL" data-id="https://www.pagetable.com/" target="_blank">Michael Steil</a>&#8216;s <a rel="noreferrer noopener" href="https://www.pagetable.com/c64ref/" data-type="URL" data-id="https://www.pagetable.com/c64ref/" target="_blank">Ultimate Commodore 64 Reference</a>, especially the <a rel="noreferrer noopener" href="https://www.pagetable.com/c64ref/kernal/" data-type="URL" data-id="https://www.pagetable.com/c64ref/kernal/" target="_blank">C64 KERNAL API</a> section</li>
</ul>



<p>The game on disk consists of two files: <kbd>"QUEST FOR TIRES"</kbd>, which is a short BASIC program that loads and executes the second file, <kbd>"QFT.8000-C010"</kbd>, which is the machine code for the game itself. Based on the filename, it seems pretty likely that the machine code gets loaded to the addresses <kbd>$8000</kbd> through <kbd>$C010</kbd>. (Note: the convention with <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MOS_Technology_6502" data-type="URL" data-id="https://en.wikipedia.org/wiki/MOS_Technology_6502" target="_blank">6502</a>-based computers, like the C64, is to represent <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Hexadecimal" data-type="URL" data-id="https://en.wikipedia.org/wiki/Hexadecimal" target="_blank">hexadecimal</a> numbers like addresses with a leading <kbd>$</kbd>, and <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Binary_number" data-type="URL" data-id="https://en.wikipedia.org/wiki/Binary_number" target="_blank">binary</a> numbers with a leading <kbd>%</kbd>.)</p>



<p>Since the crash is clearly related to changing the player&#8217;s speed, a good place to start might be to look for the code that updates the speed. To change the character&#8217;s speed, the player presses and holds the joystick button and moves the joystick left (to slow down) or right (to speed up), so we should look for instructions that read and process the joystick state.</p>



<h5 class="wp-block-heading">Using the Monitor</h5>



<p>VICE&#8217;s machine language monitor, when active, pauses the computer and presents a prompt where the user can enter commands to examine or modify the state of the computer, or to resume normal execution. Here&#8217;s a summary of the monitor commands we&#8217;ll be using, with abbreviations where applicable:</p>



<ul class="wp-block-list">
<li><kbd>a</kbd> &#8211; <a href="https://en.wikipedia.org/wiki/Assembly_language#Assembler" data-type="URL" data-id="https://en.wikipedia.org/wiki/Assembly_language#Assembler" target="_blank" rel="noreferrer noopener">assemble</a> instructions into memory</li>



<li><kbd>backtrace</kbd> (<kbd>bt</kbd>) &#8211; display the chain of subroutine calls leading to the current instruction</li>



<li><kbd>bank</kbd> &#8211; control which devices and memory ranges are visible in the monitor</li>



<li><kbd>break</kbd> (<kbd>bk</kbd>) &#8211; set a <a href="https://en.wikipedia.org/wiki/Breakpoint" data-type="URL" data-id="https://en.wikipedia.org/wiki/Breakpoint" target="_blank" rel="noreferrer noopener">breakpoint</a> on a memory location</li>



<li><kbd>disass</kbd> (<kbd>d</kbd>) &#8211; <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Disassembler" data-type="URL" data-id="https://en.wikipedia.org/wiki/Disassembler" target="_blank">disassemble</a> the contents of a memory range</li>



<li><kbd>goto</kbd> (<kbd>g</kbd>) &#8211; resume execution, optionally jumping to a new location</li>



<li><kbd>hunt</kbd> (<kbd>h</kbd>) &#8211; search through a memory range for a byte sequence</li>



<li><kbd>load</kbd> (<kbd>l</kbd>) &#8211; load a file from disk into memory</li>



<li><kbd>mem</kbd> (<kbd>m</kbd>) &#8211; display raw contents of a memory range</li>



<li><kbd>next</kbd> (<kbd>n</kbd>) &#8211; execute a single instruction, stepping over subroutine calls</li>



<li><kbd>save</kbd> (<kbd>s</kbd>) &#8211; save a region of memory to a file on disk</li>



<li><kbd>step</kbd> (<kbd>z</kbd>) &#8211; execute a single instruction, stepping into subroutine calls</li>
</ul>



<h5 class="wp-block-heading">Reading the Joystick</h5>



<p>On the C64, the state of joystick port #1 can be read from memory address <kbd>$DC01</kbd>, and the state of joystick port #2 can be read from <kbd>$DC00</kbd>. The value read from the joystick port is encoded in the lowest five bits, and each bit is active low, so <kbd>0</kbd> if pressed and <kbd>1</kbd> if not pressed. The five bits from right to left are:</p>



<ul class="wp-block-list">
<li>Bit 0: joystick up</li>



<li>Bit 1: joystick down</li>



<li>Bit 2: joystick left</li>



<li>Bit 3: joystick right</li>



<li>Bit 4: fire button</li>
</ul>



<p>For example, if the joystick is idle, the binary value read from the joystick port would be <kbd>%00011111</kbd>, or <kbd>$1F</kbd> in hexadecimal. If the joystick is pointed down and to the left, and the fire button is being pressed, the binary value read from the joystick port would be <kbd>%00001001</kbd>, or <kbd>$09</kbd> in hexadecimal. (Actually, the top three bits will probably be different. They&#8217;re related to the state of the keyboard.)</p>



<p>The game is played with the joystick connected to port #2, so it must have some code somewhere to read from <kbd>$DC00</kbd>. Let&#8217;s see if we can find it.</p>



<h5 class="wp-block-heading">Bank Switching</h5>



<p>There&#8217;s one more thing we need to deal with before running our search—<a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Bank_switching" data-type="URL" data-id="https://en.wikipedia.org/wiki/Bank_switching" target="_blank">bank switching</a>. The C64&#8217;s <a href="https://en.wikipedia.org/wiki/MOS_Technology_6510" data-type="URL" data-id="https://en.wikipedia.org/wiki/MOS_Technology_6510" target="_blank" rel="noreferrer noopener">6510</a> CPU has a 16-bit address bus, which means it can address a maximum of 64k (65,536) different memory locations. The C64 has a full 64k of <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Random-access_memory" data-type="URL" data-id="https://en.wikipedia.org/wiki/Random-access_memory" target="_blank">RAM</a>, but it also has <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Read-only_memory" data-type="URL" data-id="https://en.wikipedia.org/wiki/Read-only_memory" target="_blank">ROM</a>s and <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Input/output" data-type="URL" data-id="https://en.wikipedia.org/wiki/Input/output" target="_blank">I/O</a> devices that need to fit into the same address space, which the C64 handles by bank switching. This strategy involves mapping different devices into and out of the address space so that all of the memory and devices can be accessed, though not all at the same time.</p>



<p>The C64, by default, has a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_BASIC" data-type="URL" data-id="https://en.wikipedia.org/wiki/Commodore_BASIC" target="_blank">BASIC</a> ROM mapped into the address range <kbd>$A000</kbd>&#8211;<kbd>$BFFF</kbd>, masking the RAM at those locations, which in our case will contain the second half of the game code. If a program writes to those addresses, the data will be written to the RAM. As a result, with the default bank setup, we can load the game into memory, but if we try to read that code, we&#8217;ll get the contents of the BASIC ROM instead.</p>



<p>Memory location $01 controls what devices are banked in and out, and location $00 controls writes to $01. (By convention, addresses less than $0100 are written with just two hex digits instead of four.) We could write to those addresses with the monitor to bank out the BASIC ROM. However, we can use the monitor&#8217;s <kbd>bank</kbd> command to read from the RAM without changing the computer&#8217;s state, so let&#8217;s stick to that approach, at least for now.</p>



<h5 class="wp-block-heading">searching the code</h5>



<p>Let&#8217;s load the machine code file into memory and search for instructions that refer to <kbd>$DC00</kbd>. The 6510 processor in the C64, like the 6502 it&#8217;s based on, uses <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Endianness" target="_blank">little-endian byte ordering</a>, so instead of searching for the byte sequence <kbd>$DC $00</kbd>, we&#8217;ll need to search for the sequence <kbd>$00 $DC</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$e5d4) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$e5d4) bank ram
(C:$e5d4) h 8000 c00f 00 dc
a089
ad71</pre>



<p>Great, we found two memory addresses which reference that address. Let&#8217;s disassemble and look at the code around the first one, <kbd>$A089</kbd>. To get some context about the code you&#8217;re looking at, a good tactic is to disassemble several bytes before and after the location of interest. So, let&#8217;s disassemble starting at <kbd>$A07E</kbd> and going through <kbd>$A0A5</kbd>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="6" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$e5d4) d a07e a0a5
.C:a07e  AD 0F 12    LDA $120F
.C:a081  D0 3C       BNE $A0BF
.C:a083  AD 5E 40    LDA $405E
.C:a086  D0 37       BNE $A0BF
.C:a088  AD 00 DC    LDA $DC00
.C:a08b  29 10       AND #$10
.C:a08d  F0 14       BEQ $A0A3
.C:a08f  A5 C5       LDA $C5
.C:a091  C9 04       CMP #$04
.C:a093  F0 0E       BEQ $A0A3
.C:a095  C9 06       CMP #$06
.C:a097  F0 07       BEQ $A0A0
.C:a099  C9 05       CMP #$05
.C:a09b  D0 3F       BNE $A0DC
.C:a09d  4C 06 BF    JMP $BF06
.C:a0a0  4C 30 BF    JMP $BF30
.C:a0a3  EE 0F 12    INC $120F</pre>



<p>We can see the instruction <kbd>LDA $DC00</kbd> at address <kbd>$A088</kbd>. This instruction reads the value at address <kbd>$DC00</kbd> (the joystick port) and loads it into the CPU&#8217;s accumulator register, also called the <kbd>A</kbd> register. The next two instructions, <kbd>AND #$10</kbd> and <kbd>BEQ $A0BF</kbd>, check if the fire button is being pressed. However, none of the other joystick bits are being checked here, so it&#8217;s unlikely that this is in-game code. More likely, it&#8217;s part of code that runs at the &#8220;game over&#8221; screen, waiting for the player to press the fire button to start the next game.</p>



<p>Perhaps we&#8217;ll have better luck with the other address where we found the joystick port being accessed, <kbd>$AD71</kbd>. Again, let&#8217;s expand out a bit to get context, and disassemble <kbd>$AD61</kbd> to <kbd>$AD80</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="10" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$a0a6) d ad61 ad80
.C:ad61  D0 AD       BNE $AD10
.C:ad63  70 40       BVS $ADA5
.C:ad65  69 00       ADC #$00
.C:ad67  A2 00       LDX #$00
.C:ad69  20 13 B3    JSR $B313
.C:ad6c  4C 1C AD    JMP $AD1C
.C:ad6f  60          RTS
.C:ad70  AD 00 DC    LDA $DC00
.C:ad73  8D 3D 40    STA $403D
.C:ad76  60          RTS
.C:ad77  AD 39 40    LDA $4039
.C:ad7a  C9 20       CMP #$20
.C:ad7c  D0 03       BNE $AD81
.C:ad7e  4C F6 B2    JMP $B2F6</pre>



<p>This is interesting. The <kbd>LDA $DC00</kbd> instruction reads the value of the joystick port and the next instruction, <kbd>STA $403D</kbd>, stores that value into a memory address. Presumably, this is so that it can be read multiple times by the code without it potentially changing between reads. Let&#8217;s see how many places in the game code read that location. Let&#8217;s look for intances of the instruction <kbd>LDA $403D</kbd>, which assembles to the three-byte sequence <kbd>$AD $3D $40</kbd>. (If we don&#8217;t find much, we can expand the search to other instructions that use that address.)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$ad81) h 8000 c00f ad 3d 40
9fd0
9fd7
9fde
afee
b001
b03d
b044
b05b
b14d
b161</pre>



<p>That instruction is found in ten different places in the code. Not a huge number, but not a small number either. Notice, however, that they seem kind of clustered into groups. This would make sense if we expect that the joystick state might be read multiple times in a subroutine. Let&#8217;s start going through them. To look at the first group of three, let&#8217;s disassemble and examine <kbd>$9FC0</kbd> to <kbd>$9FF</kbd>D:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="9, 12, 15" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$ad81) d 9fc0 9ffd
.C:9fc0  18          CLC
.C:9fc1  69 0F       ADC #$0F
.C:9fc3  8D 30 40    STA $4030
.C:9fc6  CE 25 40    DEC $4025
.C:9fc9  10 20       BPL $9FEB
.C:9fcb  A9 0A       LDA #$0A
.C:9fcd  8D 25 40    STA $4025
.C:9fd0  AD 3D 40    LDA $403D
.C:9fd3  29 10       AND #$10
.C:9fd5  D0 14       BNE $9FEB
.C:9fd7  AD 3D 40    LDA $403D
.C:9fda  29 08       AND #$08
.C:9fdc  F0 10       BEQ $9FEE
.C:9fde  AD 3D 40    LDA $403D
.C:9fe1  29 04       AND #$04
.C:9fe3  D0 06       BNE $9FEB
.C:9fe5  CE 61 40    DEC $4061
.C:9fe8  EE 24 40    INC $4024
.C:9feb  4C 03 A0    JMP $A003
.C:9fee  EE 61 40    INC $4061
.C:9ff1  CE 24 40    DEC $4024
.C:9ff4  AD 24 40    LDA $4024
.C:9ff7  C9 04       CMP #$04
.C:9ff9  B0 08       BCS $A003
.C:9ffb  4C 00 A0    JMP $A000</pre>



<p>This is very promising. The joystick state (<kbd>$403D</kbd>) is read three times, each time to check a different bit. The three bits checked are bit 4 (<kbd>$10</kbd>, fire button), bit 3 (<kbd>$08</kbd>, joystick right), and bit 2 (<kbd>$04</kbd>, joystick left). These are exactly the relevant bits for controlling the speed. The first check, at <kbd>$9FD0</kbd>, looks to see if the fire button is pressed, and if not, jumps ahead to <kbd>$9FEB</kbd>, which immediately jumps to <kbd>$A003</kbd>. </p>



<p>The next two checks only occur if the fire button is pressed. The second check, at <kbd>$9FD7</kbd>, looks to see if the joystick is pointing right (to speed up), and if it is, jumps ahead to <kbd>$9FEE</kbd>, which increments location <kbd>$4061</kbd> and decrements location <kbd>$4024</kbd>. It then checks if the value stored at <kbd>$4024</kbd> is less than <kbd>4</kbd>. If it&#8217;s greater than or equal to 4, it jumps ahead to <kbd>$A003</kbd>, but if it&#8217;s less than <kbd>4</kbd>, it jumps to <kbd>$A000</kbd> instead.</p>



<p>If the second check doesn&#8217;t find the joystick pointing right, the code falls through to the third check, at <kbd>$9FDE</kbd>. This looks to see if the joystick is pointing left (to slow down), and if it&#8217;s not, jumps ahead to <kbd>$9FBE</kbd>, which as we&#8217;ve already seen, immediately jumps to <kbd>$A003</kbd>. If the joystick <em>is</em> pointing left, then <kbd>$4061</kbd> is decremented, and <kbd>$4024</kbd> is incremented, and then the code jumps to <kbd>$A003</kbd>.</p>



<p>This very much appears to be the subroutine that updates the speed based on player input. It seems like <kbd>$A003</kbd> is the location to jump to when we&#8217;re done updating the speed, and <kbd>$4061</kbd> and/or <kbd>$4024</kbd> are good candidate locations for where some representation of the speed is stored. <kbd>$4061</kbd> is incremented when the character speeds up and decremented when the character slows down, so that at least moves in the right direction. <kbd>$4024</kbd> goes in the opposite direction, but it <em>is</em> compared against some kind of limit (<kbd>4</kbd>), which fits with the fact that the in-game speed is limited. Maybe they&#8217;re both different representations of the speed. Let&#8217;s look at the disassembly for <kbd>$A000</kbd> to <kbd>$A020</kbd> and see if we can learn anything more.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffe) d a000 a020
.C:a000  EE 24 40    INC $4024
.C:a003  AD 24 40    LDA $4024
.C:a006  CD 30 40    CMP $4030
.C:a009  90 06       BCC $A011
.C:a00b  AD 30 40    LDA $4030
.C:a00e  8D 24 40    STA $4024
.C:a011  4A          LSR A
.C:a012  4A          LSR A
.C:a013  AC 1B 40    LDY $401B
.C:a016  AE 1A 40    LDX $401A
.C:a019  C0 07       CPY #$07
.C:a01b  D0 04       BNE $A021
.C:a01d  E0 95       CPX #$95
.C:a01f  F0 0C       BEQ $A02D</pre>



<p>Let&#8217;s look at <kbd>$A003</kbd> first, as it&#8217;s the target of several jump and branch instructions. This section of code loads the value at <kbd>$4024</kbd> into the <kbd>A</kbd> register, then compares it to the value at <kbd>$4030</kbd>. If the value at <kbd>$4024</kbd> is less than the value at <kbd>$4030</kbd>, the code jumps ahead to <kbd>$A011</kbd>. Otherwise, if the value at <kbd>$4024</kbd> is greater than or equal to the value at <kbd>$4030</kbd>, the value at <kbd>$4030</kbd> is copied into <kbd>$4024</kbd>, and the next instruction is at <kbd>$A011</kbd>. So, this seems to be setting an upper limit for <kbd>$4024</kbd>, with an adjustable limit value stored at <kbd>$4030</kbd>.</p>



<p><kbd>$A000</kbd> is the address jumped to when the value at <kbd>$4024</kbd> is less than <kbd>4</kbd>. This instruction increments <kbd>$4024</kbd>, and therefore enforces a hard-coded lower limit of <kbd>4</kbd> for this value. The next instruction is <kbd>$A003</kbd>, which, as we&#8217;ve just seen, contains the upper-limit code for the same location. Since <kbd>$A000</kbd> has just enforced the lower limit, the upper-limit code will likely have nothing to do. After this, the code once again reaches <kbd>$A011</kbd>.</p>



<p><kbd>$A011</kbd> seems not to have much more to do with setting the speed, and likely just carries on with other game logic. Nevertheless, I think we&#8217;ve finally achieved our first milestone, which is to find the code which handles the speed. That is, after all, where the bug seems to be. In particular, the crash happens when the player accelerates past 80.</p>



<h5 class="wp-block-heading">Stepping Through the Crash</h5>



<p>The VICE monitor has the capability to set breakpoints, which stop the computer and open the monitor prompt when the breakpoint&#8217;s condition is satisfied. These conditions are usually something like &#8220;stop when execution reaches a certain address&#8221;, but they can also break on reading/writing memory locations, and can be restricted to break only when other memory locations have specific values. It&#8217;s a very powerful debugging tool, because while the computer is paused at a breakpoint, the user can examine and/or modify memory, and step execution forward one instruction at a time.</p>



<p>Since the crash we&#8217;re looking for happens when the player tries to speed up past the limit, and since instruction <kbd>$9FFB</kbd> is executed if and only if a limit is exceeded during a speed-up action, let&#8217;s do the following:</p>



<ol class="wp-block-list">
<li>start the game,</li>



<li>break into the monitor,</li>



<li>set a breakpoint at <kbd>$9FFB</kbd>,</li>



<li>resume the game, and</li>



<li>accelerate past 80.</li>
</ol>



<p>Then, if/when our breakpoint is hit, we can trace through the code and see what might be causing the crash.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$bf5e) bk 9ffb
BREAK: 1  C:$9ffb  (Stop on exec)
(C:$bf5e) g
#1 (Stop on  exec 9ffb)   69/$045,  55/$37
.C:9ffb  4C 00 A0    JMP $A000      - A:03 X:00 Y:00 SP:ff N.-.....   63751795</pre>



<p>Nice, we hit our breakpoint, right when we expected to. Now let&#8217;s trace through the next few instructions to see if we can determine what&#8217;s going wrong.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffb) z
.C:a000  55 24       EOR $24,X      - A:03 X:00 Y:00 SP:ff N.-.....   63751798
(C:$a000) z
.C:a002  40          RTI            - A:03 X:00 Y:00 SP:ff ..-.....   63751802
(C:$a002)</pre>



<p>That&#8217;s definitely very wrong. <kbd>RTI</kbd> is an instruction meant to return from an interrupt handler. Executing that when <em>not</em> in an interrupt handler is a good way to corrupt the stack and crash the computer. We have our smoking gun. But why is this instruction being executed? And why is <kbd>$A000</kbd> now <kbd>EOR $24,X</kbd> instead of the <kbd>INC $4024</kbd> we saw before? The three bytes starting at <kbd>$A000</kbd> are currently <kbd>$55 $24 $40</kbd>. But before we started the game, they were <kbd>$EE $24 $40</kbd>, which disassembles to the expected <kbd>INC $4024</kbd>. Only the first byte is different. Somehow, the byte at <kbd>$A000</kbd> is getting corrupted and breaking the code.</p>



<h3 class="wp-block-heading">Understanding the Bug</h3>



<h5 class="wp-block-heading">The Source of the Corruption</h5>



<p>As I mentioned, the breakpoints in VICE&#8217;s monitor are pretty powerful, and they can be set up to break when a specific memory location is written to. We can use that try to find the culprit. But there&#8217;s a small complication to deal with. We don&#8217;t want to break when loading the machine code into RAM. We only want to break when <kbd>$A000</kbd> changes after that initial load.</p>



<p>So, instead of the typical <kbd>LOAD"*",8</kbd> followed by <kbd>RUN</kbd> to start the game, we&#8217;re going to do things a bit more manually. First we&#8217;ll load the BASIC loader program to look and see what steps it takes to load and start the game. Then we&#8217;ll load the machine code into RAM ourselves, and then set our breakpoint. Finally, we&#8217;ll run the command from the loader program that actually starts the game.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-BASIC-Loader.png" alt="" class="wp-image-478" style="width:576px;height:495px" width="576" height="495" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-BASIC-Loader.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-BASIC-Loader-300x258.png 300w" sizes="auto, (max-width: 576px) 100vw, 576px" /><figcaption class="wp-element-caption">Quest for Tires BASIC Loader Listing</figcaption></figure>



<p>The relevant lines of code for us are 50 and 60. The rest are boilerplate code to display a loading screen and handle some quirks when a BASIC program loads another program. Line 50 loads the machine code with <kbd>LOAD"QFT.8000-C010",8,1</kbd>. Line 60 then executes that code with <kbd>SYS64738</kbd>. SYS is a BASIC command that jumps to an address and starts executing the machine code there. BASIC doesn&#8217;t use hexadecimal, so the address is specified in decimal.</p>



<p>64,738 in decimal is <kbd>$FCE2</kbd> in hexadecimal, which some readers may recognize as the address of the <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/KERNAL" data-type="URL" data-id="https://en.wikipedia.org/wiki/KERNAL" target="_blank">KERNAL</a> routine to reset the computer (without clearing the RAM). The loader program apparently loads the machine code into RAM and then resets the computer, which somehow causes the game to start. We&#8217;ll revisit this a bit later. For now, we know what steps we need to take to set our breakpoint and find out what&#8217;s corrupting memory at address <kbd>$A000</kbd>. Let&#8217;s do that now.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$e5cf) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$e5cf) bk store a000
WATCH: 1  C:$a000  (Stop on store)
(C:$e5cf) bank default
(C:$e5cf) g fce2
#1 (Stop on store a000)    2/$002,  10/$0a
.C:fd73  91 C1       STA ($C1),Y    - A:55 X:94 Y:00 SP:fd ..-..I.C 2315364035
(C:$fd75) m c1 c2
>C:00c1  00 a0                                                ..</pre>



<p>And there&#8217;s our culprit. For some reason, the instruction at <kbd>$FD73</kbd> is being executed, and that instruction stores the value of the <kbd>A</kbd> register (<kbd>$55</kbd>) into the location pointed to by <kbd>$C1</kbd> and <kbd>$C2</kbd>, which is <kbd>$A000</kbd> (remember, the CPU is little-endian). The value of the <kbd>Y</kbd> register is added to the destination address before the store happens, but <kbd>Y</kbd> is currently <kbd>0</kbd>, so that doesn&#8217;t change anything. What is address <kbd>$FD73</kbd>, and why is it writing into our program&#8217;s code? Why is it even executing at all? The monitor&#8217;s <kbd>backtrace</kbd> command might help us find out.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$00c3) bt
(0) 800f
(C:$00c3)</pre>



<p>Interesting. The caller of this routine is at <kbd>$800F</kbd>, which is very near the start of our game&#8217;s machine code. Let&#8217;s disassemble the code from the beginning and look at <kbd>$800F</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="10" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$00c3) d 8000 8020
.C:8000  00          BRK
.C:8001  C0 20       CPY #$20
.C:8003  80 C3       NOOP #$C3
.C:8005  C2 CD       NOOP #$CD
.C:8007  38          SEC
.C:8008  30 8E       BMI $7F98
.C:800a  16 D0       ASL $D0,X
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
.C:8019  78          SEI
.C:801a  4C 47 FE    JMP $FE47
.C:801d  8D 18 03    STA $0318
.C:8020  20 BC F6    JSR $F6BC
(C:$8023)</pre>



<h5 class="wp-block-heading">RAMTAS</h5>



<p>At <kbd>$800F</kbd>, we find the instruction <kbd>JSR $FD50</kbd>. This instruction calls the subroutine at the specified address. <kbd>$FD50</kbd> is pretty close to the offending instruction, <kbd>$FD73</kbd>, so this makes sense. <kbd>$FD50</kbd> is within the KERNAL ROM&#8217;s address space, and is in fact a documented routine, named RAMTAS, that can be called by other programs. The pagetable.com KERNAL API reference says this about RAMTAS:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Perform RAM test</strong></p>



<ul class="wp-block-list">
<li>Communication registers: A, X, Y</li>



<li>Preparatory routines: None</li>



<li>Error returns: None</li>



<li>Stack requirements: 2</li>



<li>Registers affected: A, X, Y</li>
</ul>



<p><strong>Description</strong>: This routine is used to test RAM and set the top and bottom of memory pointers accordingly. It also clears locations $00 to $0101 and $0200 to $03FF. It also allocates the cassette buffer, and sets the screen base to $0400. Normally, this routine is called as part of the initialization process of a Commodore 64 program cartridge.</p>
</blockquote>



<p>and also this:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>RAMTAS</strong></p>



<p>This routine clears zero-page RAM (locations $02-$FF) and initializes Kernal memory pointers in zero page. For the 64 only, the routine also clears pages 2 and 3 (locations $0200-$03FF), tests all RAM locations from $0400 upwards until ROM is encountered, and sets the top-of-memory pointer. For the 128, the routine sets the BASIC restart vector ($0A00) to point to BASIC&#8217;s cold-start entry address, $4000.</p>
</blockquote>



<p>This line is particularly interesting: <em>&#8220;Normally, this routine is called as part of the initialization process of a Commodore 64 program cartridge.&#8221;</em> Quest for Tires was released both on floppy disk and on ROM cartridge, and there were some utility programs available to convert a cartridge program to be able to load and run from disk. Furthermore, ROM cartridges use the memory space <kbd>$8000</kbd>&#8211;<kbd>$9FFF</kbd> for 8k cartridges, and <kbd>$8000</kbd>&#8211;<kbd>$BFFF</kbd> for 16k cartridges. This latter range matches our code&#8217;s address range almost exactly. It seems probable that this floppy disk copy of the game was originally created by converting from the cartridge version. The extra 16 bytes at the end could be some glue code to help with the conversion.</p>



<h5 class="wp-block-heading">Cartridge Conversion</h5>



<p>Why does it matter that this code was converted from a cartridge? First, it explains why the BASIC loader resets the computer to start the game. Cartridges are inserted before powering on the computer, and when the booting system notices that a cartridge is plugged in, it starts executing code from that cartridge. Also, since the game was originally a cartridge, it needs to call the RAMTAS routine itself, as the KERNAL doesn&#8217;t do that automatically when a cartridge is inserted.</p>



<p>The RAMTAS routine, among other things, writes a <kbd>$55</kbd> byte (sound familiar?) to each memory location and reads it back to make sure it matches. If the byte matches, the original byte that was stored at the location is restored, and testing moves on to the next address. If the byte doesn&#8217;t match, the routine assumes it&#8217;s found a ROM, stops testing RAM, and updates some KERNAL pointers to indicate where the top and bottom of RAM are. Take a look at the <a rel="noreferrer noopener" href="https://www.pagetable.com/c64ref/c64disasm/" data-type="URL" data-id="https://www.pagetable.com/c64ref/c64disasm/" target="_blank">C64 BASIC &amp; KERNAL ROM Disassembly</a> section of Michael Steil&#8217;s reference site for full details on this routine.</p>



<p>When the game is run from a ROM cartridge, it&#8217;s mapped into memory at <kbd>$8000</kbd>&#8211;<kbd>$BFFF</kbd>. As part of its initialization, it calls RAMTAS, which then starts testing RAM, but stops at <kbd>$8000</kbd>, because it hits the ROM. But, when the game is in RAM instead, RAMTAS keeps going when it hits <kbd>$8000</kbd>. This is generally non-destructive, since each byte is put back after it passes the RAM test. However, recall that the BASIC ROM is mapped in at <kbd>$A000</kbd>&#8211;<kbd>$BFFF</kbd>, the first byte of which is exactly our problem address.</p>



<p>Here&#8217;s the crucial point. RAMTAS writes <kbd>$55</kbd> to <kbd>$A000</kbd>, and reads back a different value (the first byte in the BASIC ROM), so it (correctly) concludes that it&#8217;s found a ROM. However, <em>it has already</em> <em>written to the RAM underneath the ROM at that address</em>, which is holding our program! Furthermore, RAMTAS doesn&#8217;t try to restore the byte, since it thinks it&#8217;s from a ROM, and what would be the point of trying to write to ROM? This is the root cause of our corrupted byte.</p>



<p>But shouldn&#8217;t the BASIC ROM be banked out already? It&#8217;s masking half of the game code, so it will need to be banked out for the game to be able to run. Let&#8217;s take a look at that glue code at <kbd>$C000</kbd> to <kbd>$C00F</kbd>.</p>



<h5 class="wp-block-heading">Glue Code</h5>



<p>When the C64 boots up, it looks at addresses <kbd>$8004</kbd> to <kbd>$8008</kbd> for the byte sequence <kbd>$C3 $C2 $CD $38 $30</kbd>. That&#8217;s <a href="https://en.wikipedia.org/wiki/PETSCII" data-type="URL" data-id="https://en.wikipedia.org/wiki/PETSCII" target="_blank" rel="noreferrer noopener">PETSCII</a> for &#8220;CBM80&#8221;. &#8220;CBM&#8221; stands for Commodore Business Machines. I&#8217;m not sure what the &#8220;80&#8221; signifies. If that sequence is found, the system sets the <a href="https://en.wikipedia.org/wiki/Non-maskable_interrupt" data-type="URL" data-id="https://en.wikipedia.org/wiki/Non-maskable_interrupt" target="_blank" rel="noreferrer noopener">NMI (Non-Maskable Interrupt)</a> <a href="https://en.wikipedia.org/wiki/Interrupt_vector_table" data-type="URL" data-id="https://en.wikipedia.org/wiki/Interrupt_vector_table" target="_blank" rel="noreferrer noopener">vector</a> to point to the address found at <kbd>$8002</kbd> and <kbd>$8003</kbd>. The C64&#8217;s Restore key is wired to the CPU&#8217;s NMI line, so pressing that key will trigger an NMI and jump to the address pointed to by the NMI vector. After setting up the vector, the system jumps to the address pointed to by <kbd>$8000</kbd> and <kbd>$8001</kbd> and starts executing code there.</p>



<p>If we look at memory at <kbd>$8000</kbd> to <kbd>$8008</kbd>, we find the CBM80 signature at <kbd>$8004</kbd>, and the two pointers at <kbd>$8000</kbd> and <kbd>$8002</kbd>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8023) m 8004 8008
>C:8004  c3 c2 cd 38  30                                      ...80
(C:$8009) m 8000 8001
>C:8000  00 c0                                                ..
(C:$8002) m 8002 8003
>C:8002  20 80                                                 .</pre>



<p>This is definitely a cartridge conversion, or at the very least, is using the cartridge mechanism. <kbd>$8000</kbd> points to <kbd>$C000</kbd>, so that&#8217;s where the system will start executing code once it&#8217;s finished booting. Notably, this is just outside the 16k ROM cartridge address range, so it must have been added by the conversion utility. Let&#8217;s look at that code:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8004) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  AA          TAX
.C:c008  AA          TAX
.C:c009  AA          TAX
.C:c00a  AA          TAX
.C:c00b  AA          TAX
.C:c00c  AA          TAX
.C:c00d  AA          TAX
.C:c00e  AA          TAX
.C:c00f  AA          TAX</pre>



<p>Aha, the first two instructions set the memory bank register to <kbd>$36</kbd>, or <kbd>%00110110</kbd> in binary. The default value for this register is <kbd>$37</kbd>, or <kbd>%00110111</kbd> in binary, so this clears the lowest-order bit. That happens to be the bit that controls the BASIC ROM, and setting it to <kbd>0</kbd> banks out that ROM. This is the sort of thing we should expect to see here.</p>



<p>The next instruction jumps to the start of the game code. It&#8217;s likely that the original cartridge had the entry point at <kbd>$8000</kbd> set to <kbd>$8009</kbd>, and the conversion utility redirected execution to <kbd>$C000</kbd>, where it put code to bank out the BASIC ROM (because that&#8217;s necessary when running from RAM) and then jump back to where the code would have started. The remaining <kbd>$AA</kbd> bytes are simply padding to ensure that the file size is a multiple of 16 bytes. That&#8217;s not really necessary, but it doesn&#8217;t hurt anything either.</p>



<p>This technique is sometimes referred to as patching, although that term is more broadly applicable than this specific approach. The additional code itself is sometimes called patch code, for hopefully obvious reasons, or glue code, since its purpose is to stick together things that normally don&#8217;t go together (in this case, code from a ROM cartridge running from RAM).</p>



<p>If the glue code sets the bank register to bank out the BASIC ROM, why is the RAMTAS routine hitting it and messing up the game code? Let&#8217;s trace through the glue code and see if we can find out.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$c010) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$c010) bk c000
BREAK: 1  C:$c000  (Stop on exec)
(C:$c010) g fce2
#1 (Stop on  exec c000)   60/$03c,  17/$11
.C:c000  A9 36       LDA #$36       - A:C3 X:00 Y:91 SP:ff ..-..IZC    6482922
(C:$c000) m 00 01
>C:0000  2f 37                                                /7
(C:$0002) z
.C:c002  85 01       STA $01        - A:36 X:00 Y:91 SP:ff ..-..I.C    6482924
(C:$c002) z
.C:c004  4C 09 80    JMP $8009      - A:36 X:00 Y:91 SP:ff ..-..I.C    6482927
(C:$c004) m 00 01
>C:0000  2f 36                                                /6
(C:$0002) z
.C:8009  8E 16 D0    STX $D016      - A:36 X:00 Y:91 SP:ff ..-..I.C    6482930
(C:$8009) z
.C:800c  20 A3 FD    JSR $FDA3      - A:36 X:00 Y:91 SP:ff ..-..I.C    6482934
(C:$800c) m 00 01
>C:0000  2f 36                                                /6
(C:$0002) n
.C:800f  20 50 FD    JSR $FD50      - A:D7 X:FF Y:91 SP:ff N.-..I.C    6483073
(C:$800f) m 00 01
>C:0000  2f 37                                                /7</pre>



<p>As we can see from this trace, The glue code does, in fact, successfully bank out the BASIC ROM, and it stays banked out right up until the <kbd>JSR $FDA3</kbd> subroutine call at <kbd>$800C</kbd>. That routine must have banked it back in, just before the call to RAMTAS (<kbd>$FD50</kbd>). What is this routine?</p>



<h5 class="wp-block-heading">IOINIT</h5>



<p>According to pagetable.com&#8217;s KERNAL ROM disassembly and KERNAL API reference, <kbd>$FDA3</kbd> contains a KERNAL routine named IOINIT. This is what it has to say about that routine:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Initialize I/O devices</strong></p>



<ul class="wp-block-list">
<li>Communication registers: None</li>



<li>Preparatory routines: None</li>



<li>Error returns:</li>



<li>Stack requirements: None</li>



<li>Registers affected: A, X, Y</li>
</ul>



<p><strong>Description</strong>: This routine initializes all input/output devices and routines. It is normally called as part of the initialization procedure of a Commodore 64 program cartridge.</p>



<p>EXAMPLE:</p>



<p><kbd>    JSR IOINIT</kbd></p>
</blockquote>



<p>and also this:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Initialize I/O devices</strong></p>



<p><strong>Called by</strong>: None.</p>



<p>JMP FDA3 to initialize the CIA registers, the 6510 I/O data registers, the SID chip registers, start CIA #1 timer A, enable CIA #1 timer A interrupts, and set the serial clock output line high.</p>



<p>The closest equivalent to this routine on the VIC is JMP FDF9 to initialize the 6522 registers, set VIA #2 timer 1 value, start timer 1, and enable timer 1 interrupts.</p>



<p>Since these routines are called during system reset, the main use for IOINIT is for an autostart cartridge that wants to use the same I/O settings that the Kernal normally uses.</p>
</blockquote>



<p>So, ultimately, the cartridge conversion utility used a patch and some glue code to bank out the BASIC ROM to let the game run, but wasn&#8217;t clever enough to notice that IOINIT banks it back in right away, and then RAMTAS comes along and clobbers the code at <kbd>$A000</kbd>, leading to the crash bug we&#8217;ve been diagnosing. This is almost everything we need to know to understand why the game crashes when it does, but one small question remains. If the BASIC ROM has been banked in over the top of the second half of the game code, how does the game run at all?</p>



<p>The answer is that, a few instructions later, the game code itself banks the BASIC ROM back out again, and so it&#8217;s only banked in for a brief moment, during which RAMTAS gets confused and introduces the bug.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$0002) d 8009 8033
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
.C:8019  78          SEI
.C:801a  4C 47 FE    JMP $FE47
.C:801d  8D 18 03    STA $0318
.C:8020  20 BC F6    JSR $F6BC
.C:8023  20 15 FD    JSR $FD15
.C:8026  20 A3 FD    JSR $FDA3
.C:8029  20 18 E5    JSR $E518
.C:802c  58          CLI
.C:802d  A9 36       LDA #$36
.C:802f  85 01       STA $01
.C:8031  4C CD 8F    JMP $8FCD</pre>



<p>At <kbd>$802D</kbd>&#8211;<kbd>$8030</kbd>, the game sets <kbd>$01</kbd> back to <kbd>$36</kbd>, banking out the BASIC ROM (for good this time) and then jumps off to <kbd>$8FCD</kbd>, which presumably shows the title screen and then starts the game itself.</p>



<h3 class="wp-block-heading">Fixing the Bug</h3>



<p>Now that we&#8217;ve figured out what&#8217;s going wrong, how do we fix it? Through my journey diagnosing this issue, I&#8217;ve tried several different approaches, some of which were successful. In this section, I&#8217;ll describe some of the successful fixes and discuss their pros and cons.</p>



<h5 class="wp-block-heading">Fix 1: Avoid the Corrupted Instruction</h5>



<p>The first fix I tried that actually worked was to adjust to code to skip over the instruction at <kbd>$A000</kbd>, instead achieving the same effect by jumping somewhere else. Recall that the correct instruction is <kbd>INC $4024</kbd>, which is followed by <kbd>LDA $4024</kbd> at location <kbd>$A003</kbd>. Let&#8217;s see if we can find another place in the code that does that:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8034) h 8000 bfff ee 24 40
9fe8
a000</pre>



<p>There are only two instances of that instruction in the entire program. One of them is <kbd>$A000</kbd>, which is what we&#8217;re trying to avoid. Let&#8217;s look at the other one at <kbd>$9FE8</kbd>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="2-3" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8034) d 9fe8 9ffd
.C:9fe8  EE 24 40    INC $4024
.C:9feb  4C 03 A0    JMP $A003
.C:9fee  EE 61 40    INC $4061
.C:9ff1  CE 24 40    DEC $4024
.C:9ff4  AD 24 40    LDA $4024
.C:9ff7  C9 04       CMP #$04
.C:9ff9  B0 08       BCS $A003
.C:9ffb  4C 00 A0    JMP $A000</pre>



<p>As expected, the code at <kbd>$9FE8</kbd> executes the same increment instruction as the one that got corrupted. However, somewhat miraculously, it then unconditionally jumps to <kbd>$A003</kbd>, which is immediately after the corrupted instruction, and just where we need to go next. So, if we replace every jump to $A000 with a jump to $9FE8 instead, that should avoid the corruption and do what the original program intended. Let&#8217;s look for the places we need to change:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffe) h 8000 bfff 4c 00 a0
9ffb
bffb</pre>



<p>Only two places found. The first ($9FFB), we just saw above. What about the second?</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="14" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffe) d bfe0 bfff
.C:bfe0  01 80       ORA ($80,X)
.C:bfe2  01 80       ORA ($80,X)
.C:bfe4  01 80       ORA ($80,X)
.C:bfe6  01 80       ORA ($80,X)
.C:bfe8  01 1E       ORA ($1E,X)
.C:bfea  1F 20 46    SLO $4620,X
.C:bfed  47 48       SRE $48
.C:bfef  6E 6F 70    ROR $706F
.C:bff2  97 98       SAX $98,Y
.C:bff4  BF C0 E6    LAX $E6C0,Y
.C:bff7  E7 E8       ISB $E8
.C:bff9  B0 08       BCS $C003
.C:bffb  4C 00 A0    JMP $A000
.C:bffe  F9 07 A9    SBC $A907,Y</pre>



<p>This is basically gibberish, so it&#8217;s probably not code. It&#8217;s likely some sort of data, perhaps graphics or sound data. We shouldn&#8217;t need to worry about this, so the only <kbd>JMP $A000</kbd> instruction we&#8217;ve found that we need to update is the one at <kbd>$9FFB</kbd>. Note that there could be conditional branch instructions or indirect jump instructions that target <kbd>$A000</kbd>.</p>



<p>The branch instructions would be within the 256 bytes around <kbd>$A000</kbd>, since they can only jump a limited distance. That&#8217;s still lot of disassembly code to show, however, so perhaps you&#8217;ll take my word for it that I looked and didn&#8217;t see any. Indirect jumps to a specific location are hard to find, since it would require dynamic analysis of the code to determine the jump target. If we miss one, the game might still crash, but let&#8217;s try it and see what happens. The following commands make the change and then save the updated code to a new file on disk called <kbd>"FIX1.8000-C010"</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="12" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$c000) a 9ffb
.9ffb  jmp $9fe8
.9ffe  
(C:$9ffe) d 9fe8 9ffd
.C:9fe8  EE 24 40    INC $4024
.C:9feb  4C 03 A0    JMP $A003
.C:9fee  EE 61 40    INC $4061
.C:9ff1  CE 24 40    DEC $4024
.C:9ff4  AD 24 40    LDA $4024
.C:9ff7  C9 04       CMP #$04
.C:9ff9  B0 08       BCS $A003
.C:9ffb  4C E8 9F    JMP $9FE8
(C:$9ffe) bank ram
(C:$9ffe) s "fix1.8000-c010" 8 8000 c00f
Saving file `fix1.8000-c010' from $8000 to $c00f</pre>



<p>Next, we need to update the BASIC loader to load our fixed version of the code:</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Loader-Fix-1.png" alt="" class="wp-image-541" style="width:576px;height:495px" width="576" height="495" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Loader-Fix-1.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Loader-Fix-1-300x258.png 300w" sizes="auto, (max-width: 576px) 100vw, 576px" /><figcaption class="wp-element-caption">Quest for Tires BASIC Loader (Fix 1)</figcaption></figure>



<p>Now, all that&#8217;s left to do is try it! I started this section by saying I would only discuss successful fixes, so there&#8217;s not much suspense, but here&#8217;s a video of this fixed code running, with the speed going all the way up to 80 without crashing! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<figure style="display: block; margin-left: auto; margin-right: auto;" class="wp-block-video"><video style="display: block; margin-left: auto; margin-right: auto;" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-For-Tires-Fix1.mp4" controls="controls" width="384" height="330"></video>
<figcaption style="text-align: center;" class="wp-element-caption">Quest for Tires Gameplay (Fix 1)</figcaption>
</figure>



<p>Clearly, this fix works, which is great. One downside, however, is that it slightly changes the number of cycles it takes to execute the speed update code during the main game loop. In practice, it&#8217;s unlikely to matter, but it might subtly change the gameplay. Another drawback is more aesthetic—it&#8217;s not the same control flow as what was originally published. Let&#8217;s see how we might improve on this.</p>



<h5 class="wp-block-heading">Fix 2: Undo the Corruption</h5>



<p>Instead of modifying the main game loop, maybe there&#8217;s a way we can repair the damage RAMTAS did to our code. We know exactly which byte it clobbered and what the value was before said clobbering. Perhaps we can borrow the trick that the cartridge conversion utility used, and insert some code right after RAMTAS returns that restores the byte&#8217;s original value. We have nine padding bytes to work with, and since this is stored on a disk, we could add quite a bit of extra code if we needed to. But, I think we can fit what we need to do into the existing padding bytes.</p>



<p>What we want to do is store the value <kbd>$EE</kbd> into the location <kbd>$A000</kbd> as soon as possible after the call to RAMTAS returns. One way to do that is to write our own subroutine that calls RAMTAS and then immediately fixes up memory before returning. That would look something like this:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="26-29" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffe) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$9ffe) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  AA          TAX
.C:c008  AA          TAX
.C:c009  AA          TAX
.C:c00a  AA          TAX
.C:c00b  AA          TAX
.C:c00c  AA          TAX
.C:c00d  AA          TAX
.C:c00e  AA          TAX
.C:c00f  AA          TAX
(C:$c010) a c007
.c007  jsr $fd50
.c00a  lda #$ee
.c00c  sta $a000
.c00f  rts
.c010
(C:$c010) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  20 50 FD    JSR $FD50
.C:c00a  A9 EE       LDA #$EE
.C:c00c  8D 00 A0    STA $A000
.C:c00f  60          RTS</pre>



<p>We used up all the padding bytes, but we now have the routine we need stored in memory. Next, we need to patch the game code to call our routine instead of calling RAMTAS directly:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="4,14" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$c010) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
(C:$8019) a 800f
.800f  jsr $c007
.8012
(C:$8012) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 07 C0    JSR $C007
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI</pre>



<p>And that should do it. Let&#8217;s save the code, set some breakpoints, start the game, and trace through to make sure our patch restores the value of <kbd>$A000</kbd> to <kbd>$EE</kbd> and then resumes normal execution, as we expect:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="12,19,25" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8019) bank ram
(C:$8019) s "fix2.8000-c010" 8 8000 c00f
Saving file `fix2.8000-c010' from $8000 to $c00f
(C:$8019) bk 800f
BREAK: 1  C:$800f  (Stop on exec)
(C:$8019) bk c00a
BREAK: 2  C:$c00a  (Stop on exec)
(C:$8019) g fce2
#1 (Stop on  exec 800f)   77/$04d,  49/$31
.C:800f  20 07 C0    JSR $C007      - A:D7 X:FF Y:A0 SP:ff N.-..I.C   49939549
(C:$800f) m a000 a000
>C:a000  ee                                                   .
(C:$a001) z
.C:c007  20 50 FD    JSR $FD50      - A:D7 X:FF Y:A0 SP:fd N.-..I.C   49939555
(C:$c007) n
#2 (Stop on  exec c00a)  156/$09c,  62/$3e
.C:c00a  A9 EE       LDA #$EE       - A:04 X:00 Y:A0 SP:fd ..-..I..   52115762
(C:$c00a) m a000 a000
>C:a000  55                                                   U
(C:$a001) z
.C:c00c  8D 00 A0    STA $A000      - A:EE X:00 Y:A0 SP:fd N.-..I..   52115764
(C:$c00c) z
.C:c00f  60          RTS            - A:EE X:00 Y:A0 SP:fd N.-..I..   52115768
(C:$c00f) m a000 a000
>C:a000  ee                                                   .
(C:$a001) z
.C:8012  20 15 FD    JSR $FD15      - A:EE X:00 Y:A0 SP:ff N.-..I..   52115774</pre>



<p>It worked! The trace shows that execution gets diverted to our new routine. Before we call RAMTAS, <kbd>$A000</kbd> has the correct value. After RAMTAS returns, the value is corrupted. But after the next two instructions, it&#8217;s restored to the correct value again. Finally, our routine returns, and the game continues as if nothing happened.</p>



<p>This is much nicer than the first fix, because it doesn&#8217;t modify code in the middle of the game loop. It&#8217;s still annoying, however, that we have to let the code get corrupted in the first place. Perhaps we can improve this further.</p>



<h5 class="wp-block-heading">Fix 3: Prevent the Corruption</h5>



<p>Recall that the reason RAMTAS corrupts our program is that, even though the glue code banked out the BASIC ROM, the call to IOINIT banked it back in right before RAMTAS is called. What if we inserted some code between those two calls to bank it out again. Then RAMTAS should be able to complete without screwing anything up. Let&#8217;s give it a shot. First we set up our routine in the padding area, as before. This time, however, instead of calling RAMTAS right away, we set the bank register first. Then we jump directly to the RAMTAS routine, so that when it returns, it will return to our routine&#8217;s caller.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="25-27" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8012) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$8012) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  AA          TAX
.C:c008  AA          TAX
.C:c009  AA          TAX
.C:c00a  AA          TAX
.C:c00b  AA          TAX
.C:c00c  AA          TAX
.C:c00d  AA          TAX
.C:c00e  AA          TAX
.C:c00f  AA          TAX
(C:$c010) a c007
.c007  lda #$36
.c009  sta $01
.c00b  jmp fd50
.c00e  
(C:$c00e) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  A9 36       LDA #$36
.C:c009  85 01       STA $01
.C:c00b  4C 50 FD    JMP $FD50
.C:c00e  AA          TAX
.C:c00f  AA          TAX</pre>



<p>Then, as before, we replace the original call to RAMTAS with a call to our routine instead:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="4,14" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$c010) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
(C:$8019) a 800f
.800f  jsr $c007
.8012  
(C:$8012) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 07 C0    JSR $C007
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI</pre>



<p>All right, let&#8217;s try this out.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="13,16,25,31,34" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8019) bank ram
(C:$8019) s "fix3.8000-c010" 8 8000 c00f
Saving file `fix3.8000-c010' from $8000 to $c00f
(C:$8019) bk 800f
BREAK: 1  C:$800f  (Stop on exec)
(C:$8019) bk 8012
BREAK: 2  C:$8012  (Stop on exec)
(C:$8019) g fce2
#1 (Stop on  exec 800f)  184/$0b8,  60/$3c
.C:800f  20 07 C0    JSR $C007      - A:D7 X:FF Y:0A SP:ff N.-..I.C    4200295
(C:$800f) bank default
(C:$800f) m 00 01
>C:0000  2f 37                                                /7
(C:$0002) bank ram
(C:$0002) m a000 a000
>C:a000  ee                                                   .
(C:$a001) z
.C:c007  A9 36       LDA #$36       - A:D7 X:FF Y:0A SP:fd N.-..I.C    4200301
(C:$c007) z
.C:c009  85 01       STA $01        - A:36 X:FF Y:0A SP:fd ..-..I.C    4200303
(C:$c009) z
.C:c00b  4C 50 FD    JMP $FD50      - A:36 X:FF Y:0A SP:fd ..-..I.C    4200306
(C:$c00b) bank default
(C:$c00b) m 00 01
>C:0000  2f 36                                                /6
(C:$0002) g
#2 (Stop on  exec 8012)   39/$027,   2/$02
.C:8012  20 15 FD    JSR $FD15      - A:04 X:11 Y:D0 SP:ff ..-..I..    6994392
(C:$8012) bank default
(C:$8012) m 00 01
>C:0000  2f 36                                                /6
(C:$0002) bank ram
(C:$0002) m a000 a000
>C:a000  ee                                                   .</pre>



<p>This worked nicely. Because we set the bank register before calling RAMTAS, <kbd>$A000</kbd> was never corrupted. As before, the game continues after our patch, none the wiser. This is a pretty good fix, but there&#8217;s one thing I can think of that might be even better.</p>



<h5 class="wp-block-heading">Fix 4: Prevent the Corruption, but Even Better</h5>



<p>The reason the game code calls IOINIT and RAMTAS is that, as a ROM cartridge, these routines would not be called by the KERNAL during bootup. However, we&#8217;re not running this code from ROM anymore, and there&#8217;s no cartridge in the expansion port. Maybe these calls are redundant, and could be entirely eliminated. Let&#8217;s remove the calls, and set some breakpoints to see if the routines still get called from somewhere else.</p>



<p>To remove these subroutine calls, the simplest thing to do is overwrite them with the NOP instruction. That&#8217;s a one-byte instruction that tells the processor to do nothing for a couple of clock cycles. Each subroutine call assembles to three bytes of code, so we need to put in six NOPs in total, starting at <kbd>$800C</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="5-6,20-25" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$e5cd) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$e5cd) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
(C:$8019) a 800c
.800c  nop
.800d  nop
.800e  nop
.800f  nop
.8010  nop
.8011  nop
.8012  
(C:$8012) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  EA          NOP
.C:800d  EA          NOP
.C:800e  EA          NOP
.C:800f  EA          NOP
.C:8010  EA          NOP
.C:8011  EA          NOP
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI</pre>



<p>Okay, let&#8217;s try it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="9-10" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8019) bank ram
(C:$8019) s "fix4.8000-c010" 8 8000 c00f
Saving file `fix4.8000-c010' from $8000 to $c00f
(C:$8019) bk fda3
BREAK: 1  C:$fda3  (Stop on exec)
(C:$8019) bk fd50
BREAK: 2  C:$fd50  (Stop on exec)
(C:$8019) g fce2
#1 (Stop on  exec fda3)   66/$042,   2/$02
.C:fda3  A9 7F       LDA #$7F       - A:31 X:30 Y:FF SP:fa N.-..I..   34382337
(C:$fda3) g</pre>



<p>The breakpoint at IOINIT was hit, so the KERNAL must be calling that for us. The breakpoint at RAMTAS wasn&#8217;t hit. Interestingly, however, the game still started and ran just fine, and did not exhibit the crash bug. Apparently neither of these calls was needed. So, we have a fix that consists solely of removing two subroutine calls in the initialization code for the game, and replacing them with NOPs. That feels pretty elegant to me, and so I&#8217;m going to call this solved. There may be even more elegant solutions, and I&#8217;d love to hear about them, but I&#8217;m pretty happy with this.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>When I started the process of diagnosing and fixing this crash, I had no idea what I&#8217;d find, or whether it would be something I could fix. It was a lot of fun though. There were quite a few twists and turns, and I&#8217;ve now fixed the copy of a game I&#8217;ve had since childhood, which is pretty cool.</p>



<p>Out of curiosity, I tried a few different copies of Quest for Tires that I acquired from <em>places</em> (ahem), both disk and cartridge images, and none of them exhibited this bug. So, it seems that it was a quirk of my particular copy. (I&#8217;m honestly not even sure exactly where the disk came from.) I suppose I could just play those copies, but it feels very satisfying to be able to play the version I grew up with, albeit slightly altered, without any more crashing.</p>



<p>This is the first time I&#8217;ve dug into the code of a commercial C64 game to try to make modifications, and I might not have been as efficient as someone more experienced in this domain. If I&#8217;ve made mistakes, or made inaccurate statements, or done things the hard way, I&#8217;d love to hear about it. I&#8217;d also very much like to hear about other solutions people might come up with to fix the crash. In any case, I hope you found this at least a bit entertaining, and possibly even educational. I definitely learned a lot.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2023/08/05/quest-for-tires/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-For-Tires-Crash.mp4" length="767058" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-For-Tires-Fix1.mp4" length="1483013" type="video/mp4" />

			</item>
		<item>
		<title>EC11 Rotary Encoders</title>
		<link>https://oldblog.natebarney.com/2023/07/24/ec11-rotary-encoders/</link>
					<comments>https://oldblog.natebarney.com/2023/07/24/ec11-rotary-encoders/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Mon, 24 Jul 2023 06:39:56 +0000</pubDate>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[arduino]]></category>
		<category><![CDATA[electronics]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=384</guid>

					<description><![CDATA[EC11 incremental rotary encoders are user-interface controls for electronic devices. They&#8217;re particularly useful for quickly adjusting settings through a range of possible values, or for scrolling through lists or menus. They&#8217;re also relatively inexpensive, and pretty easy to find. The way these devices work is rather interesting, and somewhat surprising if you&#8217;re unfamiliar with it. [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>EC11 <a rel="noreferrer noopener" href="https://en.m.wikipedia.org/wiki/Incremental_encoder" data-type="URL" data-id="https://en.m.wikipedia.org/wiki/Incremental_encoder" target="_blank">incremental</a> <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Rotary_encoder" data-type="URL" data-id="https://en.wikipedia.org/wiki/Rotary_encoder" target="_blank">rotary encoders</a> are user-interface controls for electronic devices. They&#8217;re particularly useful for quickly adjusting settings through a range of possible values, or for scrolling through lists or menus. They&#8217;re also relatively inexpensive, and pretty easy to find. The way these devices work is rather interesting, and somewhat surprising if you&#8217;re unfamiliar with it. In the remainder of this post, for brevity, I&#8217;ll simply refer to them as EC11&#8217;s. </p>



<figure class="wp-block-image aligncenter size-medium is-resized"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11-300x169.jpg" alt="" class="wp-image-406" width="600" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-300x169.jpg 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-768x432.jpg 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /><figcaption class="wp-element-caption">EC11 Rotary Encoder</figcaption></figure>



<p>EC11&#8217;s look similar to <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Potentiometer" target="_blank">potentiometers</a>, but they&#8217;re very different devices. A potentiometer turns smoothly through a limited range of motion, but an EC11 turns in discrete steps, and its range of rotation is unlimited. A potentiometer is a variable resistor divider, but an EC11 has nothing to do with resistance. Instead, when it rotates, it produces signals that indicate the direction of rotation. EC11&#8217;s can also function as push buttons.</p>



<p>As a side note, I&#8217;m not sure <em>why</em> they&#8217;re called &#8220;EC11&#8221; encoders. My web search on this topic has turned up nothing useful. I&#8217;ve also seen references to EC12 and EC16 encoders, that presumably differ in some important way.  There are likely other types as well. The best I&#8217;ve been able to come up with is that &#8220;EC&#8221; stands for &#8220;EnCoder&#8221;, and the 11 means 11 mm, for (I guess) the shaft length. However, apparently, EC12&#8217;s and EC16&#8217;s don&#8217;t have the push button feature that EC11&#8217;s have, so I&#8217;m not convinced that&#8217;s the whole story. If you know more about this, please let me know. </p>



<h3 class="wp-block-heading">Electrical Interface</h3>



<p>An EC11 has five pins. There is no standard pin labeling, at least not that I could find, but it&#8217;s common for the pins to be labeled <kbd>A</kbd>, <kbd>B</kbd>, <kbd>C</kbd>, <kbd>D</kbd>, and <kbd>E</kbd>. The pinout diagram below is one possible labeling, and is the one I&#8217;ll use for this post. Note, however, that some documents have the <kbd>A</kbd> and <kbd>B</kbd> pin labels swapped.</p>



<figure class="wp-block-image aligncenter size-medium is-resized"><img loading="lazy" decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11-Pinout-204x300.png" alt="" class="wp-image-389" width="204" height="300" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Pinout-204x300.png 204w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Pinout.png 533w" sizes="auto, (max-width: 204px) 100vw, 204px" /><figcaption class="wp-element-caption">EC11 Rotary Encoder Pinout (Top View)</figcaption></figure>



<p>Pins D and E are used for the button functionality. They are normally disconnected from each other, but are shorted together when the shaft is pressed like a button. This is relatively straightforward, and behaves just like almost any other push button. The other signals are a little more surprising. Pins <kbd>A</kbd>, <kbd>B</kbd>, and <kbd>C</kbd> provide information about rotations, but they&#8217;re not simply &#8220;clockwise,&#8221; &#8220;counterclockwise,&#8221; and &#8220;ground&#8221; pins, as I initially expected. </p>



<p><kbd>C</kbd> serves as a common pin, and <kbd>A</kbd> and <kbd>B</kbd> work together to indicate whether the shaft is being turned, and in which direction. They do this by connecting and disconnecting from C using something called <a rel="noreferrer noopener" href="https://en.m.wikipedia.org/wiki/Incremental_encoder#Quadrature_outputs" data-type="URL" data-id="https://en.m.wikipedia.org/wiki/Incremental_encoder#Quadrature_outputs" target="_blank">quadrature encoding</a>. When the EC11 is not being turned, <kbd>A</kbd>, <kbd>B</kbd>, and <kbd>C</kbd> are all disconnected from each other. As the shaft is turned clockwise through one step, the following connections and disconnections happen in sequence:</p>



<ol class="wp-block-list">
<li><kbd>A</kbd> is connected to <kbd>C</kbd>,</li>



<li><kbd>B</kbd> is connected to <kbd>C</kbd>,</li>



<li><kbd>A</kbd> is disconnected from <kbd>C</kbd>,</li>



<li><kbd>B</kbd> is disconnected from <kbd>C</kbd>.</li>
</ol>



<p>For counter-clockwise rotations, the roles of <kbd>A</kbd> and <kbd>B</kbd> are reversed. Note that only one of the two signals changes at any one time. This encoding enables a simple mechanical design for the device, and also eliminates ambiguous transition states that could arise if both signals changed at nearly, but not exactly, the same time.</p>



<p>To illustrate these signals a little better, I connected an EC11 to an oscilloscope and took some captures. (An oscilloscope shows a graph of voltage on the vertical axis vs. time on the horizontal axis.) In all of these images, <kbd>A</kbd> is the yellow trace, and <kbd>B</kbd> is the blue trace. For the first two images, <kbd>C</kbd> was connected to <kbd>V<sub>CC</sub></kbd> (+5V), and <kbd>A</kbd> and <kbd>B</kbd> were each pulled down to <kbd>GND</kbd> (0V) with 10kΩ <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Pull-up_resistor" data-type="URL" data-id="https://en.wikipedia.org/wiki/Pull-up_resistor" target="_blank">pull-down resistors</a>. This means that <kbd>A</kbd> and <kbd>B</kbd> are normally low, but are high while connected to <kbd>C</kbd>. The first image shows clockwise rotation, and the second image shows counter-clockwise rotation.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="800" height="420" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11-Positive-Clockwise.png" alt="" class="wp-image-380" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Positive-Clockwise.png 800w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Positive-Clockwise-300x158.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Positive-Clockwise-768x403.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">EC11, Positive Polarity, Clockwise Rotation</figcaption></figure>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="800" height="420" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11-Positive-Counter-clockwise.png" alt="" class="wp-image-379" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Positive-Counter-clockwise.png 800w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Positive-Counter-clockwise-300x158.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Positive-Counter-clockwise-768x403.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">EC11, Positive Polarity, Counter-Clockwise Rotation</figcaption></figure>



<p>For the next two images, <kbd>C</kbd> was connected to <kbd>GND</kbd>, and <kbd>A</kbd> and <kbd>B</kbd> were each pulled up to <kbd>V<sub>CC</sub></kbd> with 10kΩ pull-up resistors. This means that <kbd>A</kbd> and <kbd>B</kbd> are normally high, but are low while connected to <kbd>C</kbd>. As before, the first image shows clockwise rotation, and the second image shows counter-clockwise rotation.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="800" height="420" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11-Negative-Clockwise.png" alt="" class="wp-image-382" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Negative-Clockwise.png 800w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Negative-Clockwise-300x158.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Negative-Clockwise-768x403.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">EC11, Negative Polarity, Clockwise Rotation</figcaption></figure>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="800" height="420" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11-Negative-Counter-clockwise.png" alt="" class="wp-image-381" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Negative-Counter-clockwise.png 800w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Negative-Counter-clockwise-300x158.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Negative-Counter-clockwise-768x403.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">EC11, Negative Polarity, Counter-Clockwise Rotation</figcaption></figure>



<p>The choice of connection method may depend on various factors in the larger circuit, but often it will work just fine either way, and the choice is made arbitrarily or by personal preference of the circuit designer.</p>



<h3 class="wp-block-heading">Mechanism</h3>



<p>Before we dig into this section, I should point out that I haven&#8217;t actually taken one of these devices apart, so what follows about the specifics of their construction is speculation and educated guesses on my part. The precise nature and arrangement of the internal parts may differ from what I describe. However, I believe the basic principles are correct.</p>



<p>My understanding is that inside the body of an EC11 are two wipers, or conductive contacts—one connected to pin <kbd>A</kbd> and one to pin <kbd>B</kbd>. The shaft is attached to a rotating plate with a pattern of conductive pads that can make electrical connections to the wipers. These pads are connected to pin <kbd>C</kbd>. It&#8217;s a bit hard to explain with words, so here&#8217;s a diagram:</p>



<figure class="wp-block-image aligncenter size-medium is-resized"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11_Mechanism-300x161.png" alt="" class="wp-image-387" width="600" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11_Mechanism-300x161.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11_Mechanism-768x411.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11_Mechanism.png 1018w" sizes="(max-width: 300px) 100vw, 300px" /><figcaption class="wp-element-caption">Hypothetical EC11 Rotary Encoder Mechanism (Top View)</figcaption></figure>



<p>This diagram is very approximate. For instance, actual EC11&#8217;s have many more than four positions. The ones I&#8217;m using have twenty. However, it should communicate the basic idea. As the shaft and plate rotate, but the wipers stay stationary, one wiper will connect to a pad, then the other wiper will, and then the first wiper will disconnect, then the second will, just as described above. The mechanism also includes <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Detent" data-type="URL" data-id="https://en.wikipedia.org/wiki/Detent" target="_blank">detents</a> that cause the shaft and plate to snap into place with the wipers between pads.</p>



<p>As I said, some of the details above may not be exactly correct. It&#8217;s possible that the wipers move with the shaft over stationary pads. It&#8217;s possible that the pads are not on a plate perpendicular to the shaft, but actually placed on the surface of the shaft itself, with the wipers oriented sideways. It&#8217;s possible all these methods, and others, could be used by different models of encoders. However, I&#8217;m reasonably confident that the general principles outlined above are correct. I welcome feedback and/or corrections from anyone who knows more about this than I do.</p>



<h3 class="wp-block-heading">Contact Bounce</h3>



<p>Things like switches, buttons, and rotary encoders are all subject to a phenomenon known as <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Switch#Contact_bounce" target="_blank">contact bounce</a>. This means that the physical conductors inside the mechanism can actually bounce off of each other and introduce spurious connections and disconnections before finally settling down to the correct state. This can make it appear as though, for example, a button was pressed several times when the user only meant to press it once.</p>



<p>To demonstrate contact bounce with the EC11, I took another oscilloscope capture, this time significantly zoomed in on the time (horizontal) axis. In the previous captures, the oscilloscope was set to 20 ms per division (i.e. background grid cell). To be able to show the bouncing, I set the oscilloscope to 100 μs, or 0.1 ms, per division. There is some inherent randomness to the bouncing.  Sometimes the contacts don&#8217;t bounce at all. Sometimes they bounce a great deal. I ended up turning the EC11 a few times to get a good example of bouncing to capture. The following image was taken with the EC11 connected with positive polarity, and the shaft was rotated clockwise.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="800" height="420" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11-Contact-Bounce.png" alt="" class="wp-image-408" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Contact-Bounce.png 800w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Contact-Bounce-300x158.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Contact-Bounce-768x403.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /><figcaption class="wp-element-caption">EC11 Contact Bounce</figcaption></figure>



<p>There are several ways to deal with contact bounce, including both hardware and software solutions, with varying levels of complexity. It might be worth going into this topic in more detail in a future post. However, one simple software solution is to keep track of the previously read value of the input pin, and introduce a delay between reads. The software can then react when there&#8217;s a difference between the previous value and the current value. If the delay is sufficiently short, the user won&#8217;t notice it, and if the delay is sufficiently long, the bouncing will have stopped by the time the pin is read again. In practice, I&#8217;ve found that delays of a few milliseconds work pretty well.</p>



<h3 class="wp-block-heading">Arduino Sketch</h3>



<p>I&#8217;ve put together a simple circuit and Arduino sketch to demonstrate how to use these devices. Feel free to use or adapt this code in your own projects. We&#8217;ll first take a look at the hardware setup, then we&#8217;ll discuss the code.</p>



<p>This example uses an Arduino Uno R3, but it should work with almost any Arduino. Similarly, while this example (somewhat arbitrarily) uses Arduino pins 2, 3, and 4 to connect to the EC11, any free GPIO pins should do. The EC11 is connected with positive polarity. (Changing the hardware connections and sketch code to use the EC11 with negative polarity is relatively straightforward, and is left as an exercise for the reader.)</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/07/EC11-Arduino-Breadboard-1024x604.png" alt="" class="wp-image-412" width="768" height="453" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Arduino-Breadboard-1024x604.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Arduino-Breadboard-300x177.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Arduino-Breadboard-768x453.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Arduino-Breadboard-1536x906.png 1536w, https://oldblog.natebarney.com/wp-content/uploads/2023/07/EC11-Arduino-Breadboard.png 1603w" sizes="auto, (max-width: 768px) 100vw, 768px" /><figcaption class="wp-element-caption">Arduino EC11 Example Circuit</figcaption></figure>



<p>Connect the Arduino&#8217;s <kbd>5V</kbd> pin to the positive breadboard rails and connect the Arduino&#8217;s <kbd>GND</kbd> to the negative breadboard rails. Connect EC11 pins <kbd>C</kbd> and <kbd>E</kbd> to the positive rails as well. Connect EC11 pins <kbd>A</kbd>, <kbd>B</kbd>, and <kbd>D</kbd> to the negative breadboard rails through 10kΩ resistors. (Since these are pull-down resistors, the resistor values themselves don&#8217;t matter too much, as long as they&#8217;re large-ish.) Connect EC11 pin <kbd>A</kbd> to Arduino pin <kbd>3</kbd>, EC11 pin <kbd>B</kbd> to Arduino pin <kbd>4</kbd>, and EC11 pin <kbd>D</kbd> to Arduino pin <kbd>2</kbd>.</p>



<p>Now that the circuit is hooked up, we can move on to the code. The following code uses a simple polling loop with a basic software debouncing scheme, and simply prints messages over the serial connection when the EC11 is rotated or pressed. It should be straightforward to adapt this code to do other things on these events instead.</p>



<p>To read the rotation state, we first check to see if pin <kbd>A</kbd> is going from low to high. This rising edge occurs every time the EC11 is rotated. The direction is then determined by looking at the <kbd>B</kbd> pin at the moment of <kbd>A</kbd>&#8216;s rising edge. In a clockwise rotation, <kbd>A</kbd> goes high before <kbd>B</kbd> does, so while <kbd>A</kbd> is going high, <kbd>B</kbd> will still be low. In a counter-clockwise rotation, <kbd>B</kbd> goes high before <kbd>A</kbd>, so while <kbd>A</kbd> is going high, <kbd>B</kbd> will already be high. If this isn&#8217;t clear, it might help to take another look at the oscilloscope captures above.</p>



<p>We only need to check B when we&#8217;ve already determined there&#8217;s a rising edge on <kbd>A</kbd>, so the code below omits that check in other cases. Also note that we don&#8217;t need to debounce <kbd>B</kbd>, because bouncing happens right after a state change, and we only read <kbd>B</kbd> well before or after its state changes.</p>



<p>The button press events are handled slightly differently from the rotation events. We check for both rising and falling edges on the button input. A rising edge corresponds to the button being pressed, and a falling edge corresponds to the button being released. It&#8217;s sometimes useful to track both types of button events, so they&#8217;re included in the example.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">//
// ec11_example.ino
//
// Example Arduino sketch to demonstrate reading an EC11 rotary encoder
//

// Pin definitions
#define EC11_PIN_A 3
#define EC11_PIN_B 4
#define EC11_PIN_D 2

// Number of milliseconds to wait between pin reads, for software debouncing
#define DEBOUNCE_DELAY_MILLIS 3

// Global variables to hold the previous state of the EC11 pins, initialized
// to LOW. We don't need to track the previous state of B to debounce it
// because we don't read it near an edge, and any bouncing should already
// have stopped by the time we do read it.
int prevA = LOW;
int prevD = LOW;

// Runs once at beginning of sketch
void setup()
{
  // Set pins to input
  pinMode(EC11_PIN_A, INPUT);
  pinMode(EC11_PIN_B, INPUT);
  pinMode(EC11_PIN_D, INPUT);

  // Initialize serial connection
  Serial.begin(9600);
}

// Runs continually in a loop after setup is complete
void loop()
{
  // Local variables to hold the current state of the pins
  int currentA;
  int currentB;
  int currentD;

  // Wait a bit to allow any contact bounce to settle
  delay(DEBOUNCE_DELAY_MILLIS);

  // Process rotation pins
  currentA = digitalRead(EC11_PIN_A);
  if ((prevA == LOW) &amp;&amp; (currentA == HIGH))
  {
    // There's a rising edge on A, so the EC11 is being rotated. The state
    // of B at the rising edge of A indicates the direction. If B is low,
    // then A went high first, and the rotation is clockwise. If B is high,
    // then B went high first, and the rotation is counter-clockwise.
    currentB = digitalRead(EC11_PIN_B);

    if (currentB == LOW)
    {
      Serial.println("Clockwise");
    }
    else
    {
      Serial.println("Counter-clockwise");
    }
  }

  // Process button pin
  currentD = digitalRead(EC11_PIN_D);
  if ((prevD == LOW) &amp;&amp; (currentD == HIGH))
  {
    Serial.println("Pressed");
  }
  else if ((prevD == HIGH) &amp;&amp; (currentD == LOW))
  {
    Serial.println("Released");
  }

  // update previous state globals
  prevA = currentA;
  prevD = currentD;
}</pre>



<p>After building this circuit and compiling and uploading the sketch to the Arduino, open the serial monitor and set it to 9600 baud. Then, turn and press the EC11. You should see output similar to the following in the serial monitor:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Clockwise
Counter-clockwise
Pressed
Released
Pressed
Clockwise
Counter-clockwise
Released</pre>



<h3 class="wp-block-heading">Summary</h3>



<p>So, that&#8217;s about as much as I can say about the EC11 rotary encoder. It&#8217;s a cool little device, and can be used to build pretty nice user interfaces for hobbyist projects using Arduino&#8217;s, or Raspberry Pi&#8217;s, or any other microcontroller. Feel free to reach out if you have any questions/comments/corrections about anything in this post.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2023/07/24/ec11-rotary-encoders/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GPIO Pins, Shift Registers, and SPI</title>
		<link>https://oldblog.natebarney.com/2023/07/16/gpio-pins-shift-registers-and-spi/</link>
					<comments>https://oldblog.natebarney.com/2023/07/16/gpio-pins-shift-registers-and-spi/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Sun, 16 Jul 2023 15:35:56 +0000</pubDate>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[electronics]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=274</guid>

					<description><![CDATA[When using microcontrollers, like the ATmega line of chips, or boards based on them, like the Arduino series, General Purpose Input/Output (GPIO) pins are often at a premium.  The Arduino Uno R3 (based on the ATmega328P), for example, has a maximum of fourteen GPIO pins [...]]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading">Microcontroller GPIO Pins</h3>



<p>When using microcontrollers, like the ATmega line of chips, or boards based on them, like the Arduino series, <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/General-purpose_input/output" data-type="URL" data-id="https://en.wikipedia.org/wiki/General-purpose_input/output" target="_blank">General Purpose Input/Output (GPIO)</a> pins are often at a premium.  The <a rel="noreferrer noopener" href="https://docs.arduino.cc/hardware/uno-rev3" data-type="URL" data-id="https://docs.arduino.cc/hardware/uno-rev3" target="_blank">Arduino Uno R3</a> (based on the <a href="https://www.microchip.com/en-us/product/ATMEGA328P" data-type="URL" data-id="https://www.microchip.com/en-us/product/ATMEGA328P" target="_blank" rel="noreferrer noopener">ATmega328P</a>), for example, has a maximum of fourteen GPIO pins, and that&#8217;s if you&#8217;re not using some of the pins&#8217; special functions.</p>



<p>It&#8217;s very easy to run out of these GPIO pins if multiple devices are connected directly to them. Each LED uses one pin, configured as an output. Each button uses one pin, configured as an input. A <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Seven-segment_display" data-type="URL" data-id="https://en.wikipedia.org/wiki/Seven-segment_display" target="_blank">seven-segment display</a> uses eight outputs (there&#8217;s a decimal point that&#8217;s not counted in the name for some reason). In its most minimal configuration, an <a href="https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller" data-type="URL" data-id="https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller">LCD character display</a> module uses six pins, but can be configured to use as many as eleven pins. All this quickly adds up.</p>



<h3 class="wp-block-heading">Shift Registers</h3>



<p>A common solution to the problem of too few GPIO pins is to use one or more shift registers. Simply put, a <a rel="noreferrer noopener" href="https://en.m.wikipedia.org/wiki/Shift_register" data-type="URL" data-id="https://en.m.wikipedia.org/wiki/Shift_register" target="_blank">shift register</a> is a chip that converts a single signal changing over time (i.e. serial) to multiple signals at the same time (i.e. parallel), or vice-versa. This solution is so common that the Arduino core library has functions specifically to support their use, and there are <a href="https://duckduckgo.com/?q=arduino+shift+register+tutorial" data-type="URL" data-id="https://duckduckgo.com/?q=arduino+shift+register+tutorial" target="_blank" rel="noreferrer noopener">lots of Arduino/shift-register tutorials</a> to be found on the internet. (I guess I&#8217;m adding to that list.)</p>



<p>There are many different types of shift register available, but two of the most popular are the <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC595" data-type="URL" data-id="https://www.ti.com/product/SN74HC595" target="_blank">74HC595</a> (to add output pins) and the <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC165" data-type="URL" data-id="https://www.ti.com/product/SN74HC165" target="_blank">74HC165</a> (to add input pins). These chips are easy to use, inexpensive, and readily available. You can get them from electronics suppliers like <a rel="noreferrer noopener" href="https://www.mouser.com/c/?q=74hc595" data-type="URL" data-id="https://www.mouser.com/c/?q=74hc595" target="_blank">Mouser</a> and <a href="https://www.digikey.com/en/products/filter/shift-registers/712?s=N4IgTCBcDaIOwBYAWBjArATjSAugXyA" data-type="URL" data-id="https://www.digikey.com/en/products/filter/shift-registers/712?s=N4IgTCBcDaIOwBYAWBjArATjSAugXyA">DigiKey</a>, and they&#8217;re even available from <a rel="noreferrer noopener" href="https://www.amazon.com/s?k=74hc595" data-type="URL" data-id="https://www.amazon.com/s?k=74hc595" target="_blank">Amazon</a>.</p>



<p>The basic principle behind shift registers, and the reason for their name, is that each chip holds a number of bits (typically eight) in a small memory circuit called a register, and these bits <em>shift</em> from one to the next when a clock pulse is applied. Bit 0 shifts into bit 1, bit 1 shifts into bit 2, and so on. The new value for bit 0 comes from a serial input pin at the time of the clock pulse. The last bit falls out of the shift register, and is made available on one of the chip&#8217;s output pins, called serial output. Typically, the shift happens on the <em>rising</em> edge of the clock pulse (i.e. from low to high).</p>



<p>The fact that each shift register has a serial input pin that feeds into bit 0 and a serial output pin that holds the value of the last bit as it&#8217;s shifted out means that these chips may be <em>cascaded</em>, or chained together, by connecting the serial output of one chip to the serial input of another, and by connecting their clocks together. Two 8-bit shift registers cascaded together, for example, behave as if they were a single 16-bit shift register. The number of chips that can be cascaded into a single effective register is typically limited only by things like available power and the capacitance of the connecting wires, but the chain would need to be pretty long for those things to start to matter.</p>



<p>There are a few disadvantages to using shift registers for GPIO expansion. First, it&#8217;s slower. Instead of reading or writing several pins at once, the microcontroller has to process them one at a time. Second, the pins can&#8217;t be read or written independently. To read/write bit 4, for example, bits 0-3 must first be read/written. When the shift register is being used to add output pins, this means the software needs to keep track of what bits have been written. Third, most shift registers are unidirectional. That is, each type of chip provides either inputs or outputs, but not both. (The <a rel="noreferrer noopener" href="https://www.ti.com/product/CD74HC299" data-type="URL" data-id="https://www.ti.com/product/CD74HC299" target="_blank">74HC299</a> is one exception to this.) In practice, however, these disadvantages are often easily overcome and are more than made up for by the added inputs and/or outputs.</p>



<h5 class="wp-block-heading">74HC595</h5>



<p>The 74HC595 is an 8-bit Serial-In / Parallel-Out (SIPO) shift register. It shifts in one bit from its serial input pin (pin 14) on each rising edge of its serial clock pin (pin 11), and has eight parallel output pins (pins 15 and 1-7). However, the bits in the shift register are not immediately made available on the output pins.</p>



<p>The 74HC595 internally has two registers: the shift register and the storage register (sometimes called the latch register). The shift register behaves as described above. However, the parallel output pins are not connected directly to the shift register, but rather to the storage register. The contents of the shift register are transferred to the storage register (and therefore the output pins) on the rising edge of a second clock pin (pin 12), called the storage clock pin (or sometimes the latch pin).</p>



<p>This dual-register configuration may seem like a needless complication. Worse, instead of just two GPIO pins (serial input and serial clock), it uses those plus a third (storage clock). However, there is a good reason for this design. Some devices connected to the 75HC595&#8217;s output pins may not react well to the data shifting along one bit at a time. The two-register approach allows data bits to be shifted in while keeping the output pins unchanged, then all made available effectively simultaneously once the complete set of bits is ready.</p>



<p>If a device can tolerate seeing the data shift one bit at a time, the serial clock and storage clock pins may be connected together. This causes the 74HC595 to behave (more or less) as if the outputs were connected directly to the internal shift register, and it only requires a total of two GPIO pins instead of three.</p>



<p>To interface a 74HC595 with an Arduino (or similar microcontroller), connect the serial input, serial clock, and storage clock pins of the 74HC595 to GPIO pins on the microcontroller, with all three configured as outputs. Then, in the microcontroller code (i.e. Arduino sketch), write each bit to the shift register as follows.</p>



<ol class="wp-block-list">
<li>Set the serial input pin to the value of the data bit.</li>



<li>Bring the serial clock pin low, then high, to create a rising edge and shift the bit.</li>



<li>Repeat steps 1 and 2 until all bits have been shifted.</li>



<li>Once all bits have been shifted, bring the storage clock low then high to transfer the bits to the parallel output pins.</li>
</ol>



<p>Step 4 can be omitted if the serial clock and storage clock are tied together. The Arduino core library function <kbd><a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/language/functions/advanced-io/shiftout/" data-type="URL" data-id="https://www.arduino.cc/reference/en/language/functions/advanced-io/shiftout/" target="_blank">shiftOut()</a></kbd> performs steps 1 and 2 in a loop to write eight bits. </p>



<p>The 74HC595 has a few other useful features, including tri-state outputs and a reset pin. The <a rel="noreferrer noopener" href="https://www.ti.com/lit/ds/symlink/sn74hc595.pdf" data-type="URL" data-id="https://www.ti.com/lit/ds/symlink/sn74hc595.pdf" target="_blank">datasheet</a> has more information about these if you&#8217;re interested, but they&#8217;re not particularly relevant to this post, so that&#8217;s all I&#8217;ll say about them here.</p>



<h5 class="wp-block-heading">74HC165</h5>



<p>The 74HC165 is an 8-bit Parallel-In / Serial-Out (PISO) shift register. Data bits are loaded into the chip all at once from eight parallel input pins (pins 11-14 and 3-6) while the parallel load pin (pin 1) is set low. Once the bits are loaded, they can be shifted out one at a time to the serial output pin (pin 9) by pulsing the serial clock pin (pin 2). Bits are shifted on the rising edge of the clock pulse, and the serial input pin (pin 10) provides a new value for bit 0 on each pulse.</p>



<p>The process to interface the 74HC165 with a microcontroller is very similar to the process for the 74HC595. Connect the 74HC165&#8217;s serial clock and parallel load pins to output pins on the microcontroller, and connect the 74HC165&#8217;s serial output pin to a microcontroller input pin. Then, in software, perform the following:</p>



<ol class="wp-block-list">
<li>Bring the parallel load pin low, then high to latch the inputs into the shift register.</li>



<li>Bring the serial clock pin low.</li>



<li>Bring the serial clock pin high to create a rising edge and shift out a bit.</li>



<li>Read and process the data bit from the serial output pin.</li>



<li>Bring the serial clock pin back low to prepare for the next bit.</li>



<li>Repeat steps 3 through 5 for each bit.</li>
</ol>



<p>The Arduino core library function <kbd><a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/language/functions/advanced-io/shiftin/" data-type="URL" data-id="https://www.arduino.cc/reference/en/language/functions/advanced-io/shiftin/" target="_blank">shiftIn()</a></kbd> performs steps 3 through 5 in a loop to read eight bits. </p>



<p>Like the 74HC595, the 74HC165 has additional features, such as clock inhibit and complementary serial output, that I&#8217;m not going to discuss here. For more information, see the <a rel="noreferrer noopener" href="https://www.ti.com/lit/ds/symlink/sn74hc165.pdf" data-type="URL" data-id="https://www.ti.com/lit/ds/symlink/sn74hc165.pdf" target="_blank">datasheet</a>. </p>



<h3 class="wp-block-heading">Serial Peripheral Interface (SPI)</h3>



<p>The <a rel="noreferrer noopener" href="https://en.m.wikipedia.org/wiki/Serial_Peripheral_Interface" data-type="URL" data-id="https://en.m.wikipedia.org/wiki/Serial_Peripheral_Interface" target="_blank">Serial Peripheral Interface (SPI)</a> protocol is another way to reduce the number of microntroller GPIO pins needed for a project. There are many devices that support SPI natively, and there are quite a few available on <a rel="noreferrer noopener" href="https://www.amazon.com/s?k=spi+arduino+module" data-type="URL" data-id="https://www.amazon.com/s?k=spi+arduino+module" target="_blank">Amazon</a>, for instance. The protocol is relatively simple, and it&#8217;s straightforward to implement in software. Ben Eater has an excellent <a rel="noreferrer noopener" href="https://youtu.be/MCi7dCBhVpQ" data-type="URL" data-id="https://youtu.be/MCi7dCBhVpQ" target="_blank">video</a> in which he interfaces an SPI device with a <a rel="noreferrer noopener" href="https://www.westerndesigncenter.com/wdc/w65c02s-chip.php" data-type="URL" data-id="https://www.westerndesigncenter.com/wdc/w65c02s-chip.php" target="_blank">65C02</a> microprocessor using assembly code. However, most microcontrollers, including Arduinos, have hardware support for communicating via SPI. This enables much faster speeds than would be possible with a software implementation.</p>



<p>SPI is, as its name implies, a serial protocol. This means that one bit is transferred at a time. Actually, that&#8217;s not quite true, since SPI supports full-duplex bidirectional communication. This means that one bit is sent and one bit is received at the same time. It&#8217;s also a sychronous protocol, which means that the timing is controlled by a dedicated clock signal. Finally, it&#8217;s an asymmetric protocol. This means that the communication is entirely controlled by one device, called the master (sometimes controller). This is usually the microcontroller. The other device is called the slave (sometimes peripheral), and it sends and receives data only as directed by the master. Multiple slave devices can be connected to a single master.</p>



<p>The basic setup for SPI uses four different signals, each on a separate pin:</p>



<ul class="wp-block-list">
<li><strong>Serial Clock (<kbd>SCK</kbd>)</strong>: This is the signal that controls the rate of the data transfer, and is driven by the master. One bit is transferred in each direction when this signal is pulsed. SPI doesn&#8217;t specify whether the bit is transferred on the rising or falling edge of the clock signal. Instead, that&#8217;s left up to the individual implementation. Most microcontrollers will have an option to specify this as part of the SPI mode setting. SPI modes 0 and 3 transfer on the rising edge, and modes 1 and 2 transfer on the falling edge. Mode 0 differs from 3, and 1 from 2, in the level of the clock signal when idle. Modes 0 and 1 keep the clock low when idle, and 2 and 3 keep it high. The datasheet for the slave device will specify which mode(s) it expects, but mode 0 is typical.</li>



<li><strong>Master Out / Slave In (<kbd>MOSI</kbd>)</strong>: This is a data transmission signal over which the master sends data bits to the slave, at the rate dictated by the <kbd>SCK</kbd> signal. Note that this way of naming the signal is unambiguous about direction, and can be labeled the same on both master and slave devices. If the slave device is read-only (e.g. a sensor of some sort), this signal may be omitted. This signal is also sometimes called Controller Out / Peripheral In (<kbd>COPI</kbd>). On slave devices, this signal may also be called Data In (<kbd>DI</kbd>, <kbd>DIN</kbd>) or Serial Data In (<kbd>SDI</kbd>).</li>



<li><strong>Master In / Slave Out (<kbd>MISO</kbd>)</strong>: This is a data transmission signal over which the slave sends data bits to the master, at the rate dictated by the <kbd>SCK</kbd> signal. If the slave device is write-only (e.g. a display of some sort), this signal may be omitted. This signal is also sometimes called Controller In / Peripheral Out (<kbd>COPI</kbd>). On slave devices, this signal may also be called Data Out (<kbd>DO</kbd>, <kbd>DOUT</kbd>) or Serial Data Out (<kbd>SDO</kbd>).</li>



<li><strong>Slave Select (<span style="text-decoration: overline;"><kbd>SS</kbd></span>)</strong>: This is a control signal which activates the slave (i.e. tells it to pay attention to the other signals). The overbar on the abbreviation indicates that this signal is active low, and is pronounced by adding &#8220;bar&#8221; after the abbreviation (i.e. &#8220;ess ess bar&#8221;). In contexts where overbars are typographically difficult, this may be rendered as <kbd>/SS</kbd> or <kbd>SSB</kbd> (&#8220;B&#8221; for &#8220;Bar&#8221;). Active low means that the slave is selected when this signal is low, and deselected when the signal is high. When multiple slave devices are connected, each device is assigned its own <span style="text-decoration: overline;"><kbd>SS</kbd></span> signal. The other signals are shared among all slave devices. Usually, only one slave is selected at any one time. This signal is also sometimes called Chip Select (<span style="text-decoration: overline;"><kbd>CS</kbd></span>, <kbd>/CS</kbd>, <kbd>CSB</kbd>).</li>
</ul>



<p>To perform SPI transfers in software, make sure the relevant signals are connected between the master and slave, and perform the following:</p>



<ol class="wp-block-list">
<li>Bring the <span style="text-decoration: overline"><kbd>SS</kbd></span> pin low to select the slave device.</li>



<li>Set the <kbd>MOSI</kbd> pin to the value of the outgoing bit. (Omit for read-only slave.)</li>



<li>Pulse the <kbd>SCK</kbd> pin (high then low, or low then high, depending on mode).</li>



<li>Read the incoming bit from the <kbd>MISO</kbd> pin. (Omit for write-only slave.)</li>



<li>Repeat steps 2 through 4 until all bits are transferred.</li>



<li>Bring the <span style="text-decoration: overline;"><kbd>SS</kbd></span> pin high to deselect the slave device.</li>
</ol>



<p>As previously mentioned, most microcontrollers have SPI support built in hardware. The SPI hardware in the Arduino Uno&#8217;s ATmega328P can run the serial clock at up to half the rate of the system clock, which is 16 MHz on an Arduino Uno, so the Arduino can do SPI at up to 8 MHz. This is significantly faster than would be possible with a pure software implementation. Some microcontrollers can run the SPI serial clock at 10&#8217;s or even 100&#8217;s of MHz.</p>



<p>To use the microcontroller&#8217;s SPI hardware to perform transfers, <kbd>MOSI</kbd>, <kbd>MISO</kbd>, and <kbd>SCK</kbd> on the slave should be connected to the corresponding SPI pins on the microcontroller. On the Arduino Uno R3, <kbd>MOSI</kbd> is pin 11, <kbd>MISO</kbd> is pin 12, and <kbd>SCK</kbd> is pin 13. The slave&#8217;s <span style="text-decoration: overline"><kbd>SS</kbd></span> pin can be connected to any free GPIO pin on the master, configured as an output. The Arduino has a pin labeled <span style="text-decoration: overline"><kbd>SS</kbd></span> (pin 10), but it&#8217;s only necessary to use that when connecting the Arduino as a slave to another SPI master.</p>



<p>Once everything is connected, issue the microcontroller instructions to enable and use the SPI hardware. In an Arduino sketch, this can be done with the <a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/language/functions/communication/spi/" data-type="URL" data-id="https://www.arduino.cc/reference/en/language/functions/communication/spi/" target="_blank">SPI</a> library, specifically the functions <kbd><a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/language/functions/communication/spi/begin/" data-type="URL" data-id="https://www.arduino.cc/reference/en/language/functions/communication/spi/begin/" target="_blank">SPI.begin()</a></kbd> (called once to enable the SPI hardware), <kbd><a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/language/functions/communication/spi/begintransaction/" data-type="URL" data-id="https://www.arduino.cc/reference/en/language/functions/communication/spi/begintransaction/" target="_blank">SPI.beginTransaction()</a></kbd> (called once before each batch of transfers), <kbd><a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/language/functions/communication/spi/transfer/" data-type="URL" data-id="https://www.arduino.cc/reference/en/language/functions/communication/spi/transfer/" target="_blank">SPI.transfer()</a></kbd> (called any number of times to transfer a batch of eight bits each time), and <kbd><a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/language/functions/communication/spi/endtransaction/" data-type="URL" data-id="https://www.arduino.cc/reference/en/language/functions/communication/spi/endtransaction/" target="_blank">SPI.endTransaction()</a></kbd> (called once after each batch of transfers). If you need to disable the SPI hardware again for some reason, that can be done with function <kbd><a rel="noreferrer noopener" href="https://www.arduino.cc/reference/en/language/functions/communication/spi/end/" data-type="URL" data-id="https://www.arduino.cc/reference/en/language/functions/communication/spi/end/" target="_blank">SPI.end()</a></kbd>. The <span style="text-decoration: overline"><kbd>SS</kbd></span> pin must still be handled explicitly by the software, but the others will be taken care of by the hardware. </p>



<h3 class="wp-block-heading">Using Shift Registers with SPI</h3>



<p>You may have noticed that the protocols for shift registers and SPI are remarkably similar, and wondered if they could be used together. In fact, they can! (If you didn&#8217;t notice, don&#8217;t worry too much; it took me way longer than I care to admit to notice it myself.) With just a few extra chips, 74HC595 and 74HC165 can be used to build a hardware SPI interface for a device which normally uses a parallel interface, thereby significantly reducing the number of GPIO pins used by the design, while only sacrificing a little bit of speed.</p>



<p>Shift registers and SPI each have serial clock signals, so those can simply be connected together. Similarly, SPI&#8217;s <kbd>MOSI</kbd> signal can be connected to the serial input signal of a 74HC595 shift register, and SPI&#8217;s <kbd>MISO</kbd> signal can be connected to the serial output signal of a 74HC165 shift register (although there&#8217;s a complication with <kbd>MISO</kbd> that we&#8217;ll discuss a little later). This takes care of three of the four SPI signals, and two of the three signals needed for each shift register.</p>



<p>The remaining signals, SPI&#8217;s <span style="text-decoration: overline;"><kbd>SS</kbd></span>, the 74HC595&#8217;s storage clock, and the 74HC165&#8217;s parallel load require just a little more thought. Recall that <span style="text-decoration: overline;"><kbd>SS</kbd></span> is brought low before an SPI transfer, and high after the transfer is complete. Recall also that the 74HC595&#8217;s storage clock needs a rising edge to move the data from the shift register to the storage register. That rising edge can be provided at just the right time by connecting <span style="text-decoration: overline;"><kbd>SS</kbd></span> to the 74HC595&#8217;s storage clock pin. This does mean that the slave device must be deselected after transferring each set of bits, but in practice, that&#8217;s not a significant disadvantage, just something to be aware of.</p>



<p>Before each transfer, the 74HC165&#8217;s parallel load pin needs to be set low to load data into the shift register, and high to enable that data to be shifted out. This is the inverse of the <span style="text-decoration: overline;"><kbd>SS</kbd></span> signal, which is set high then low before each transfer. To correct this, we can use an <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Inverter_(logic_gate)" data-type="URL" data-id="https://en.wikipedia.org/wiki/Inverter_(logic_gate)" target="_blank">inverter</a> (a.k.a. a NOT gate) to flip the <span style="text-decoration: overline;"><kbd>SS</kbd></span> signal to be what we need for the parallel load pin. The <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC04" data-type="URL" data-id="https://www.ti.com/product/SN74HC04" target="_blank">74HC04</a> has six inverters, which is more than we need, but will work for our purposes. (The inverters we&#8217;re not using could be used in other parts of a larger circuit, or their inputs can simply be connected to <kbd>GND</kbd> or <kbd>V<sub>CC</sub></kbd> to keep them from floating and introducing noise into the circuit.) If we connect <span style="text-decoration: overline;"><kbd>SS</kbd></span> to the input of one of these inverters, and the output of that inverter to the 74HC165&#8217;s parallel load pin, the 74HC165 will continually load data into its shift register until it&#8217;s selected, at which point it will latch that data and make it ready to be shifted out by the other SPI signals.</p>



<p>If this SPI interface that we&#8217;ve built out of shift registers is the only slave connected to the SPI pins, then we&#8217;re done; all that&#8217;s left is to connect the shift registers&#8217; parallel inputs and outputs to the device. However, recall that if the SPI hardware is shared, <kbd>MOSI</kbd>, <kbd>MISO</kbd>, and <kbd>SCK</kbd> are also shared among all slave devices. For the 74HC595, this is no problem. It will shift bits in and out of its shift register even while another slave is selected, but its parallel output pins will stay latched, because they are controlled by its <span style="text-decoration: overline;"><kbd>SS</kbd></span> signal. The 74HC165 is another matter. If another slave is selected and <kbd>SCK</kbd> is pulsed, the 74HC165 will still shift bits out into the <kbd>MISO</kbd> pin, interfering with the signal coming from the other slave.</p>



<p>To fix this, we need to connect the 74HC165&#8217;s serial output to <kbd>MISO</kbd> only while its <span style="text-decoration: overline;"><kbd>SS</kbd></span> signal is low. This can be accomplished with a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Three-state_logic" data-type="URL" data-id="https://en.wikipedia.org/wiki/Three-state_logic" target="_blank">tri-state</a> buffer. A tri-state buffer is a logic gate that has a data input, a control input, and a data output. The control input is active low, and when it&#8217;s low, the value on the data input is propagated to the data output. When the control input is high, the data output is set to high impedance, which basically means it&#8217;s not connected. The <a rel="noreferrer noopener" href="https://www.ti.com/product/SN74HC125" data-type="URL" data-id="https://www.ti.com/product/SN74HC125" target="_blank">74HC125</a> contains four tri-state buffers. Again, we only need one, but that&#8217;s okay. We connect the 74HC165&#8217;s serial output to the data input of one of the 74HC125&#8217;s tri-state buffers, connect the output of that tri-state buffer to <kbd>MISO</kbd>, and connect <span style="text-decoration: overline;"><kbd>SS</kbd></span> to the tri-state buffer&#8217;s control input. Now, the 74HC165 can only send data on <kbd>MISO</kbd> when it&#8217;s selected, which is exactly what we want.</p>



<p>As previously mentioned, shift registers of the same type may be cascaded together to create shift registers that hold more bits. This remains true when using them with SPI. If you&#8217;re building an SPI interface for a device that has sixteen inputs and twenty-four outputs, you can cascade two 74HC595&#8217;s together and three 74HC165&#8217;s. The principles are the same; there are just more bits. If the cascaded chains are of unequal length, then each transfer should include enough bits for the longest chain. The extra bits for the shorter chain can safely be ignored by the software. If the device is read-only or write-only, the interface can be built using only one of the two types of shift registers.</p>



<h3 class="wp-block-heading">Conclusion</h3>



<p>Both SPI and shift registers are useful ways to reduce the number of microcontroller GPIO pins needed for a design. Combining them (and adding a bit of glue logic) is a great way to do so without sacrificing much speed. I&#8217;ve used this technique in some of my projects and it&#8217;s worked very well. If you use this in your projects, I&#8217;d enjoy hearing about what went well or not so well. I hope that this post has been informative and enjoyable. If I&#8217;ve made any errors or left out important details, please do let me know.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2023/07/16/gpio-pins-shift-registers-and-spi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
