<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<title>WDJ</title>
	<subtitle>cr1901&#x27;s personal blog and consulting website.</subtitle>
	<link href="https://www.wdj-consulting.com/blog/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="https://www.wdj-consulting.com/blog/"/>
	<generator uri="https://www.getzola.org/">Zola</generator>
	<updated>2024-09-20T00:00:00+00:00</updated>
	<id>https://www.wdj-consulting.com/blog/atom.xml</id>
  <!-- My addition: Do not include draft posts in Atom Feed. -->

  
	<entry xml:lang="en">
		<title>How To Download Your Twitter Archive Using `curl`</title>
		<published>2024-09-18T00:00:00+00:00</published>
		<updated>2024-09-20T00:00:00+00:00</updated>
		<link href="https://www.wdj-consulting.com/blog/twitter-archive/" type="text/html"/>
		<id>https://www.wdj-consulting.com/blog/twitter-archive/</id>
		<content type="html">&lt;h1 id=&quot;how-to-download-your-twitter-archive-using-curl&quot;&gt;How To Download Your Twitter Archive Using &lt;code&gt;curl&lt;&#x2F;code&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Without fail, for the past several months, every time I&#x27;ve tried to download
my Twitter archive from my web browser, I&#x27;ve not been able to do so. The
download gets to about 50 or so megabytes, downloading at like 50 kilobytes
per second, before uncermoniously failing:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;twitter-archive&#x2F;nJHpIDKeEP.png&quot; alt=&quot;Snippet of my Firefox downloads panel showing a zip file of my Twitter archive. Underneath the download name is the status &amp;quot;Failed&amp;quot;, along with a button on the right to retry.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I couldn&#x27;t tell you why downloading from my browser fails. Maybe it&#x27;s a Firefox
bug. Maybe it&#x27;s a Twitter bug&lt;a id=&quot;rev-fn-1&quot; href=&quot;#fn-1&quot;&gt;&lt;sup&gt;1&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
. Maybe my hardware is old. It could
be a combination. Regardless of the reason, I want to download my Twitter
archive, and I&#x27;m being prevented from doing so.&lt;&#x2F;p&gt;
&lt;p&gt;Each time I remember to creating an up-to-date archive, I try the direct
browser link, &lt;em&gt;completely forgetting&lt;&#x2F;em&gt; that it&#x27;s not going to work. And each
time, I have to relearn the process I went through to successfully download my
archive. So this is my attempt to document it for future-me and others.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;All subsequent text assumes that you have requested your Twitter archive,
and that Twitter has notified you that your archive is ready.&lt;&#x2F;em&gt; I suggest trying
to download no later than 4 days after Twitter notified you, to give you
a few days to experiment before Twitter deletes the archive.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-doesn-t-work-a-direct-link-and-wget-or-curl&quot;&gt;What Doesn&#x27;t Work- A Direct Link And &lt;code&gt;wget&lt;&#x2F;code&gt; Or &lt;code&gt;curl&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;After watching the download fail a couple of times &lt;a id=&quot;rev-fn-2&quot; href=&quot;#fn-2&quot;&gt;&lt;sup&gt;2&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
, I
copied the download link to my clipboard and pasted it into &lt;code&gt;wget&lt;&#x2F;code&gt; and &lt;code&gt;curl&lt;&#x2F;code&gt;
in frustration:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;twitter-archive&#x2F;ConEmu64_wv4n6X3ekK.png&quot; alt=&quot;Screenshot of failing attempts to download my Twitter archive directly using wget and curl. wget returns a &amp;quot;401 Unauthorized&amp;quot; error, while curl doesn&#x27;t emit any output or error information. But I assure you neither command succeeded.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I &lt;em&gt;knew&lt;&#x2F;em&gt; in my gut that this would not work and it &lt;em&gt;cannot&lt;&#x2F;em&gt; possibly work. Yet
I did it anyway. I am &lt;a href=&quot;https:&#x2F;&#x2F;superuser.com&#x2F;questions&#x2F;1614636&#x2F;how-to-use-wget-to-download-twitter-archive&quot;&gt;not the only one&lt;&#x2F;a&gt;
either. The simple solutions are tempting when one is frustrated.&lt;&#x2F;p&gt;
&lt;p&gt;The reason the download fails is that Twitter wants some extra data in the
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;List_of_HTTP_header_fields&quot;&gt;HTTP headers&lt;&#x2F;a&gt;&lt;a id=&quot;rev-fn-3&quot; href=&quot;#fn-3&quot;&gt;&lt;sup&gt;3&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
. &lt;a href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;wget&#x2F;&quot;&gt;&lt;code&gt;wget&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
and &lt;a href=&quot;https:&#x2F;&#x2F;curl.se&#x2F;&quot;&gt;&lt;code&gt;curl&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; do not- and &lt;em&gt;cannot&lt;&#x2F;em&gt;- provide these headers without your help. You
can use your web browser to find the correct data needed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-does-work-your-web-browser-monitor-and-curl&quot;&gt;What &lt;em&gt;Does&lt;&#x2F;em&gt; Work- Your Web Browser Monitor And &lt;code&gt;curl&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;preparing-the-browser-and-monitor&quot;&gt;Preparing The Browser And Monitor&lt;&#x2F;h3&gt;
&lt;p&gt;Both Firefox and Chromium-based browsers provide tools to get the required data-
the Network Monitor. This post focuses on Firefox because that&#x27;s what I use,
but the process for Chromium should be similar.&lt;&#x2F;p&gt;
&lt;p&gt;To prepare Firefox and Monitor, I open a new Firefox window, and set the
address to &lt;code&gt;about:blank&lt;&#x2F;code&gt;&lt;a id=&quot;rev-fn-4&quot; href=&quot;#fn-4&quot;&gt;&lt;sup&gt;4&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
. The Monitor can be accessed by
right-clicking the &lt;code&gt;about:blank&lt;&#x2F;code&gt; page, and selecting &lt;code&gt;Inspect&lt;&#x2F;code&gt; from the pop-up:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;twitter-archive&#x2F;firefox_qA4S8Kz1tE.png&quot; alt=&quot;Screenshot of a newly-opened about:blank page, showing a context menu after I right clicked. My mouse cursor is hovering over the Inspect option of the context menu.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Clicking &lt;code&gt;Inspect&lt;&#x2F;code&gt; opens the Firefox Developer Tools. The Network Monitor tab
in Developer Tools should look something like below:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Picture of Firefox Developer Tools window with the Network
Monitor tab open. This picture was taken right after I right-clicked `Inspect`
in the `about:blank` page. The Monitor tab is &amp;#x27;empty&amp;#x27;, and is
asking me to reload the page to start collecting data.&quot; src=&quot;firefox_cAMS8l6w3a.png&quot;&gt;
&lt;figcaption&gt;By default, Developer Tools opens up to the Inspector tab. You need to switch
over to the Network tab to open the Network Monitor.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;collecting-headers&quot;&gt;Collecting Headers&lt;&#x2F;h3&gt;
&lt;p&gt;The web browser and Monitor are prepared. We will get the data we need to make
a &lt;code&gt;curl&lt;&#x2F;code&gt; suceed at downloading our Twitter archive succeed by &lt;em&gt;first&lt;&#x2F;em&gt; using our
newly-opened browser window to (start) download(ing) our Twitter archive.&lt;&#x2F;p&gt;
&lt;p&gt;You can get the direct download link to your archive by right-clicking the
&amp;quot;Download archive&amp;quot; from the &amp;quot;Donwload an archive of your data&amp;quot; page, like so:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;twitter-archive&#x2F;firefox_xsssq5vz1L.png&quot; alt=&quot;Snippet of the Twitter webpage to download your archive. A &amp;quot;Download archive&amp;quot; button is on the right side. I have right-clicked this button to open a context menu. My mouse cursor is hovering over Copy Link in this context menu.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then, copy the link to the fresh &lt;code&gt;about:blank&lt;&#x2F;code&gt; page browser window we just opened:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;twitter-archive&#x2F;firefox_FWeE9GsjW6-pixel.png&quot; alt=&quot;The same about:blank page, except the direct download link to my Twitter archive has been pasted into the context menu. I have removed part of the download link that I&#x27;m unsure is sensitive or not.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Once you press enter in the address bar of that &lt;code&gt;about:blank&lt;&#x2F;code&gt; page, the Network
Monitor will collect data about HTTP(S) request(s) the browser is making behind
the scenes.&lt;&#x2F;p&gt;
&lt;p&gt;As a side effect of the HTTP(S) request(s), your browser should open a Save
Dialog. &lt;em&gt;You can ignore it.&lt;&#x2F;em&gt; The important headers that &lt;code&gt;curl&lt;&#x2F;code&gt; needs should be
a row in the Network Monitor tab.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Picture of Firefox Developer Tools window, with the Network
inspector tab open. The inspect shows exactly one network request with my
Twitter archive link. I have right clicked the request row to open a context
menu and have selected &amp;#x27;Copy Value&amp;#x27; and &amp;#x27;Copy as cURL (POSIX)&amp;#x27;
with my cursor. Other context menu options include &amp;#x27;Copy URL&amp;#x27;,
&amp;#x27;Copy as cURL (Windows)&amp;#x27;, &amp;#x27;Copy as Powershell&amp;#x27;, and
&amp;#x27;Copy as Fetch&amp;#x27;.&quot; src=&quot;firefox_nYj8QXoS6o.png&quot;&gt;
&lt;figcaption&gt;As of this writing (2024-09-18), accessing the direct archive download link
from the &lt;code&gt;about:blank&lt;&#x2F;code&gt; page should result in a lone request captured
by the Monitor. The request should be a file of the form &lt;code&gt;twitter-{{date}}-{{hash}}.zip&lt;&#x2F;code&gt;;
that&#x27;s your archive!&lt;&#x2F;p&gt;

&lt;p&gt;Right clicking this request gives you several options to &quot;fill in&quot; the
missing required data to download your archive using &lt;code&gt;curl&lt;&#x2F;code&gt; or other
tools. You may want to try all the options to experiment.&lt;a id=&quot;rev-fn-5&quot; href=&quot;#fn-5&quot;&gt;&lt;sup&gt;5&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;If you right-click this row, you will get several options to copy the headers
missing from your &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;twitter-archive&#x2F;#what-doesn-t-work-a-direct-link-and-wget-or-curl&quot;&gt;initial&lt;&#x2F;a&gt;
&lt;code&gt;wget&lt;&#x2F;code&gt; or &lt;code&gt;curl&lt;&#x2F;code&gt; command. In my case, I have chosen &lt;code&gt;Copy as cURL (POSIX)&lt;&#x2F;code&gt;, which
copies a &lt;code&gt;curl&lt;&#x2F;code&gt; command-line invocation with the relevant arguments&#x2F;header to
your clipboard. &lt;&#x2F;p&gt;
&lt;p&gt;After I paste the clipboard &lt;code&gt;curl&lt;&#x2F;code&gt; command into my terminal &lt;em&gt;with an explicit
output file&lt;&#x2F;em&gt;, the download begins at a reasonable speed. Most importantly,
&lt;em&gt;the download doesn&#x27;t fail halfway through&lt;&#x2F;em&gt;&lt;a id=&quot;rev-fn-6&quot; href=&quot;#fn-6&quot;&gt;&lt;sup&gt;6&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Terminal snippet showing my Twitter archive successfully downloading using `curl`.
A progress bar is underneath my curl invocation showing a download in progress. I have removed
arguments to `curl` that might be sensitive- mainly the cookies.&quot; src=&quot;ConEmu64_osTOYZElja-pixel.png&quot;&gt;
&lt;figcaption&gt;I provide an extra &lt;code&gt;--output&lt;&#x2F;code&gt; argument to &lt;code&gt;curl&lt;&#x2F;code&gt; to
specify where to write the archive data, e.g.

&lt;pre&gt;
curl https:&#x2F;&#x2F;example.com&#x2F;example.zip --output example.zip
&lt;&#x2F;pre&gt;

Without &lt;code&gt;--output&lt;&#x2F;code&gt;, &lt;code&gt;curl&lt;&#x2F;code&gt; will refuse to download your archive,
because &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ZIP_(file_format)&quot;&gt;ZIP files&lt;&#x2F;a&gt;
are binary data. &lt;em&gt;The &lt;code&gt;--output&lt;&#x2F;code&gt; argument is not part of the
clipboard data from Monitor!&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The net effect of going to developer console and creating a &lt;code&gt;curl&lt;&#x2F;code&gt; invocation
is that Twitter cannot tell the difference between your web browser and
&lt;code&gt;curl&lt;&#x2F;code&gt; downloading your archive&lt;!-- &lt;a id=&quot;rev-fn-2&quot; href=&quot;#fn-2&quot;&gt;&lt;sup&gt;2&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 --&gt;. All is well in my
archiving world for now&lt;a id=&quot;rev-fn-7&quot; href=&quot;#fn-7&quot;&gt;&lt;sup&gt;7&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;failure-modes&quot;&gt;Failure Modes&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes &lt;code&gt;curl&lt;&#x2F;code&gt; may fail to download the archive, even with the relevant
headers provided by the browser.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Terminal snippet showing my Twitter archive failing to download
using `curl`. Instead of showing a progress bar indicating a download has begun,
the command has immediately exited without any output. I have removed
arguments to `curl` that might be sensitive- mainly the cookies.&quot; src=&quot;ConEmu64_06EN6vreMU-pixel.png&quot;&gt;
&lt;figcaption&gt;Despite the lack of error output, this &lt;code&gt;curl&lt;&#x2F;code&gt; invocation has failed to
download an archive.&lt;&#x2F;p&gt;

&lt;p&gt;Me forgetting the &lt;code&gt;output&lt;&#x2F;code&gt; parameter is unrelated to the error
above (&lt;em&gt;although that is indeed another error :)&lt;&#x2F;em&gt;).&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;My experience is that the time window that Twitter allows you to download
the link is short. If you see the above image, you need to redo the verification
to download your archive and get back to the download link page, like here:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;twitter-archive&#x2F;firefox_EDE6e2E6To.png&quot; alt=&quot;Snippet of the Twitter webpage to download your archive. The top of the snippet shows a back arrow, and the title &amp;quot;Donwload an archive of your data&amp;quot;. A &amp;quot;Download archive&amp;quot; button is on the right side.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Getting back to this page should re-verify you, and should refresh the time
window that the download link is valid.&lt;&#x2F;p&gt;
&lt;p&gt;However, &lt;em&gt;don&#x27;t click the download link this time!&lt;&#x2F;em&gt; The &lt;code&gt;curl&lt;&#x2F;code&gt; invocation you
copied from the browser inspector earlier &lt;em&gt;should&lt;&#x2F;em&gt; still be valid, even after
you get back to the direct download link page. Instead, run &lt;code&gt;curl&lt;&#x2F;code&gt; again with
the params added by Monitor and an &lt;code&gt;--output&lt;&#x2F;code&gt; param, and the download should
succeed&lt;a id=&quot;rev-fn-8&quot; href=&quot;#fn-8&quot;&gt;&lt;sup&gt;8&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;happy-downloading-make-copies&quot;&gt;Happy Downloading! Make Copies!&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s not clear to me how long Twitter will last. For various reasons, I &lt;em&gt;want&lt;&#x2F;em&gt;
to have an archive of my data in case the site goes under, especially considering
that  present-me and future-me often need to be reminded of the context of
past-me&lt;a id=&quot;rev-fn-9&quot; href=&quot;#fn-9&quot;&gt;&lt;sup&gt;9&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;p&gt;I back up my archive irregularly; maybe I should do something about that. But
when I &lt;em&gt;do&lt;&#x2F;em&gt; attempt a backup, I am persistent. I don&#x27;t know if it&#x27;ll be the
last backup I can make before my backups are the only access to
(that portion of) past-me that I have left. Even if the web browser direct
download works for you, let this post be a reminder to make an archive
of your data &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Right_to_be_forgotten&quot;&gt;if you so wish&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In the meantime, I&#x27;ll work on following my own advice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;&#x2F;h2&gt;
&lt;p id=&quot;fn-1&quot;&gt;&lt;a href=&quot;#rev-fn-1&quot;&gt;1&lt;&#x2F;a&gt; I feel like there&#x27;s been a lot more of these Twitter bugs since late 2022.
Couldn&#x27;t tell you why, it&#x27;s a mystery...&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-2&quot;&gt;&lt;a href=&quot;#rev-fn-2&quot;&gt;2&lt;&#x2F;a&gt; I also got locked out of accessing the download link at all due to repeated
failures for 24 hours. Go me.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-3&quot;&gt;&lt;a href=&quot;#rev-fn-3&quot;&gt;3&lt;&#x2F;a&gt; At the very least, Twitter wants &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Headers&#x2F;Cookie&quot;&gt;
cookie&lt;&#x2F;a&gt; headers. But other headers may be required, and I didn&#x27;t experiment
to figure out the mandatory ones.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-4&quot;&gt;&lt;a href=&quot;#rev-fn-4&quot;&gt;4&lt;&#x2F;a&gt; It&#x27;s not clear to me when Monitor starts collecting data after the
Developer Tools are open. To make looking at Monitor&#x27;s data easier on my eyes,
I prefer starting from a blank page.&lt;&#x2F;p&gt;

&lt;em&gt;This step may not be required, but I prefer to not deviate from what I know
works.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-5&quot;&gt;&lt;a href=&quot;#rev-fn-5&quot;&gt;5&lt;&#x2F;a&gt; In my case, although I run Firefox on Windows, I ultimately wanted to store my
Twitter archive on an &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Network-attached_storage&quot;&gt;NAS&lt;&#x2F;a&gt;
running Linux.&lt;&#x2F;p&gt;

&lt;p&gt;Using &lt;code&gt;curl&lt;&#x2F;code&gt; on Linux, I can skip the intermediate download to a
Windows machine. I&#x27;m sure &lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;powershell&#x2F;module&#x2F;microsoft.powershell.utility&#x2F;invoke-webrequest?view=powershell-7.4&quot;&gt;&lt;code&gt;curl&lt;&#x2F;code&gt; on PowerShell&lt;&#x2F;a&gt;
works fine too.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-6&quot;&gt;&lt;a href=&quot;#rev-fn-6&quot;&gt;6&lt;&#x2F;a&gt; Even if it did fail, I vaguely recall being able to use &lt;code&gt;curl&lt;&#x2F;code&gt; to
resume the download from where I left off.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-7&quot;&gt;&lt;a href=&quot;#rev-fn-7&quot;&gt;7&lt;&#x2F;a&gt; All is &lt;em&gt;never&lt;&#x2F;em&gt; well in my archiving world. I&#x27;ll be organizing data until
I&#x27;m six feet under, I&#x27;m afraid...&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-8&quot;&gt;&lt;a href=&quot;#rev-fn-8&quot;&gt;8&lt;&#x2F;a&gt; At least, I &lt;em&gt;hope&lt;&#x2F;em&gt; it succeeds. If you try the procedure in this post
and it fails, I&#x27;m interested to know. &lt;a href=&quot;&#x2F;about#contact-info&quot;&gt;Contact me&lt;&#x2F;a&gt;
in all the usual places!&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-9&quot;&gt;&lt;a href=&quot;#rev-fn-9&quot;&gt;9&lt;&#x2F;a&gt; In fact, it&#x27;s not clear to me that the method described will continue to work
for future-me (today is 2024-09-18). For instance, Google seems to have introduced
elaborate code to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ytdl-org&#x2F;youtube-dl&#x2F;issues&#x2F;32905&quot;&gt;stop&lt;&#x2F;a&gt; &lt;code&gt;youtube-dl&lt;&#x2F;code&gt;
from working, and that already has to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ytdl-org&#x2F;youtube-dl&#x2F;blob&#x2F;c5098961b04ce83f4615f2a846c84f803b072639&#x2F;youtube_dl&#x2F;jsinterp.py&quot;&gt;emulate&lt;&#x2F;a&gt; a decent chunk of a browser due to Javascript.&lt;&#x2F;p&gt;

&lt;p&gt;I&#x27;ll have to deal with that when the time comes.&lt;&#x2F;p&gt;
&lt;!-- &lt;p id=&quot;fn-2&quot;&gt;&lt;a href=&quot;#rev-fn-2&quot;&gt;2&lt;&#x2F;a&gt; This is similar to how the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ytdl-org&#x2F;youtube-dl&quot;&gt;youtube-dl&lt;&#x2F;a&gt;
Youtube Downloader works. A &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ytdl-org&#x2F;youtube-dl&#x2F;blob&#x2F;c5098961b04ce83f4615f2a846c84f803b072639&#x2F;youtube_dl&#x2F;jsinterp.py&quot;&gt;
Javascript parser&lt;&#x2F;a&gt; subset is required to mimic more parts of a browser.&lt;&#x2F;p&gt;
 --&gt;
</content>
	</entry>
  

  
	<entry xml:lang="en">
		<title>Using `z3` To Solve Logic Puzzles</title>
		<published>2023-08-15T00:00:00+00:00</published>
		<updated>2023-08-15T00:00:00+00:00</updated>
		<link href="https://www.wdj-consulting.com/blog/logicpuzzle-z3/" type="text/html"/>
		<id>https://www.wdj-consulting.com/blog/logicpuzzle-z3/</id>
		<content type="html">&lt;h1 id=&quot;using-z3-to-solve-logic-puzzles&quot;&gt;Using &lt;code&gt;z3&lt;&#x2F;code&gt; To Solve Logic Puzzles&lt;&#x2F;h1&gt;
&lt;p&gt;Recently, I learned about &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Tacit_programming&quot;&gt;point-free programming&lt;&#x2F;a&gt;.
The day after I learned about it, I was &lt;a href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@cr1901&#x2F;110628259381839329&quot;&gt;idly wishing&lt;&#x2F;a&gt;
for a puzzle book on converting program code to point-free form. A point-free
programming puzzle book would be a good exercise on how well I can &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Function_composition&quot;&gt;compose&lt;&#x2F;a&gt; functions.
Sadly, I&#x27;m not aware of such a puzzle book, nor am I qualified to write one.&lt;&#x2F;p&gt;
&lt;p&gt;However, thinking of puzzles again reminded me of how I liked &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Logic_puzzle&quot;&gt;logic puzzle&lt;&#x2F;a&gt;
books when I was younger (anyone remember &lt;a href=&quot;https:&#x2F;&#x2F;www.pennydellpuzzles.com&#x2F;&quot;&gt;PennyPress&lt;&#x2F;a&gt;?).
A few years ago, I solved a logic puzzle using an SMT solver out of curiosity.
I was motivated to do this after seeing &lt;a href=&quot;https:&#x2F;&#x2F;www.clairexen.net&#x2F;&quot;&gt;Claire Wolf&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;oe1cxw&#x2F;status&#x2F;893451915970924544&quot;&gt;doing the same&lt;&#x2F;a&gt;
to solve a similar &amp;quot;guess the combination to the lock given hints&amp;quot; puzzle. I&#x27;d
intended to write a blog post about how I mapped the word problem of a logic
puzzle to an SMT query, but I never followed up on it.&lt;&#x2F;p&gt;
&lt;p&gt;I know my last post was in late 2019. 2020 through 2023 has not been kind to
the world and our collective social&#x2F;mental bandwidth. Mine included. So while
the thought of fun logic puzzles from my youth were fresh in my head yesterday
(2023-06-30), I forced myself remember how I solved a logic puzzle with an
SMT solver 5 years ago. And &lt;em&gt;at last&lt;&#x2F;em&gt; I&#x27;m going to document it here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;&#x2F;h2&gt;
&lt;p&gt;This post assumes a background in &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Boolean_algebra&quot;&gt;Boolean logic&lt;&#x2F;a&gt;
and&#x2F;or one programming language (along with how to use its Boolean operators).
Familiarity with &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;S-expression&quot;&gt;S-expressions&lt;&#x2F;a&gt; is
also a plus, because the input to the &lt;a href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;project&#x2F;z3-3&#x2F;&quot;&gt;&lt;code&gt;z3&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
program we&#x27;re using &lt;a href=&quot;http:&#x2F;&#x2F;smtlib.cs.uiowa.edu&#x2F;&quot;&gt;uses S-expressions&lt;&#x2F;a&gt;. However,
I will be explaining all the input we send to &lt;code&gt;z3&lt;&#x2F;code&gt; line-by-line. So feel free to
learn about S-expressions at your own pace while or after reading this post.&lt;&#x2F;p&gt;
&lt;p&gt;When referencing concepts common in other programming languages, I refer to
&lt;a href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; documentation, because at this writing
(2023-07-01), it&#x27;s easiest for me to find the analogous concepts in the Rust
language&#x27;s documentation.&lt;&#x2F;p&gt;
&lt;p&gt;And lastly, you should know what a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Logic_puzzle&quot;&gt;logic puzzle&lt;&#x2F;a&gt;
is. Enjoying solving logic puzzles is a nice bonus :).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-an-smt-solver&quot;&gt;What is an SMT Solver?&lt;&#x2F;h2&gt;
&lt;p&gt;Before I explain what an SMT solver is, and how to use them to solve puzzles,
I need to explain what SAT and SAT solvers are as prerequisites.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sat&quot;&gt;SAT&lt;&#x2F;h3&gt;
&lt;p&gt;Suppose you have some Boolean expressions- a bunch of Boolean variables chained together-
with Boolean operators- &lt;code&gt;&amp;amp;&amp;amp;&lt;&#x2F;code&gt;, &lt;code&gt;||&lt;&#x2F;code&gt;, &lt;code&gt;!&lt;&#x2F;code&gt;, etc. Here is an example &lt;em&gt;query&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;A || (B &amp;amp;&amp;amp; !C)
B &amp;amp;&amp;amp; C
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;SAT, or SATisfiability, is the process of determining whether a set of
Boolean expressions like above can &lt;em&gt;all&lt;&#x2F;em&gt; be made &lt;code&gt;true&lt;&#x2F;code&gt; (&lt;code&gt;1&lt;&#x2F;code&gt;) for &lt;em&gt;one or more&lt;&#x2F;em&gt;
combination of the values for the given Boolean variables. &lt;&#x2F;p&gt;
&lt;p&gt;Given &lt;code&gt;n&lt;&#x2F;code&gt; Boolean variables, where each variable has 2 possible values- &lt;code&gt;true&lt;&#x2F;code&gt; (&lt;code&gt;1&lt;&#x2F;code&gt;)
and &lt;code&gt;false&lt;&#x2F;code&gt; (&lt;code&gt;0&lt;&#x2F;code&gt;)- you can determine whether there&#x27;s an assignment that satisfies
all the Boolean expressions by &amp;quot;substituting all combinations of values for the 
variables in the expressions&amp;quot; (brute-forcing). &lt;&#x2F;p&gt;
&lt;p&gt;For instance, here&#x27;s how we can brute-force the above query in (up to) 8 (&lt;code&gt;2^3&lt;&#x2F;code&gt;)
easy steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;A=0,B=0,C=0&lt;&#x2F;code&gt;: &lt;code&gt;UNSAT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;0 || (0 &amp;amp;&amp;amp; !0) = 0
0 &amp;amp;&amp;amp; 0 = 0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;A=1,B=0,C=0&lt;&#x2F;code&gt;: &lt;code&gt;UNSAT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;1 || (0 &amp;amp;&amp;amp; !0) = 1
0 &amp;amp;&amp;amp; 0 = 0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;A=0,B=1,C=0&lt;&#x2F;code&gt;: &lt;code&gt;UNSAT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;0 || (1 &amp;amp;&amp;amp; !0) = 1
1 &amp;amp;&amp;amp; 0 = 0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;A=1,B=1,C=0&lt;&#x2F;code&gt;: &lt;code&gt;UNSAT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;1 || (1 &amp;amp;&amp;amp; !0) = 1
1 &amp;amp;&amp;amp; 0 = 0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;A=0,B=0,C=1&lt;&#x2F;code&gt;: &lt;code&gt;UNSAT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;0 || (0 &amp;amp;&amp;amp; !1) = 0
0 &amp;amp;&amp;amp; 0 = 0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;A=1,B=0,C=1&lt;&#x2F;code&gt;: &lt;code&gt;UNSAT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;1 || (0 &amp;amp;&amp;amp; !1) = 1
0 &amp;amp;&amp;amp; 1 = 0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;A=0,B=1,C=1&lt;&#x2F;code&gt;: &lt;code&gt;UNSAT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;0 || (1 &amp;amp;&amp;amp; !1) = 0
1 &amp;amp;&amp;amp; 1 = 1
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;A=1,B=1,C=1&lt;&#x2F;code&gt;: &lt;code&gt;SAT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;1 || (1 &amp;amp;&amp;amp; !1) = 1
1 &amp;amp;&amp;amp; 1 = 1
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Indeed, our query is &lt;code&gt;SAT&lt;&#x2F;code&gt;isfiable when all three Boolean variables
are set to &lt;code&gt;1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sat-solvers&quot;&gt;SAT Solvers&lt;&#x2F;h3&gt;
&lt;p&gt;When checking for satisfiability, if &lt;em&gt;just one&lt;&#x2F;em&gt; combination of variable values
makes all the expressions in your query equal to &lt;code&gt;1&lt;&#x2F;code&gt;, you&#x27;re done- the query is
&lt;code&gt;SAT&lt;&#x2F;code&gt;. Unfortunately, this means you have to try &lt;code&gt;2^n&lt;&#x2F;code&gt; combinations of
values for &lt;code&gt;n&lt;&#x2F;code&gt; Boolean variables if you have a query that is &lt;code&gt;UNSAT&lt;&#x2F;code&gt;isfiable. And
&lt;em&gt;even if&lt;&#x2F;em&gt; the query is &lt;code&gt;SAT&lt;&#x2F;code&gt;, you may have to try many combinations of
variable values before you find a combination that returns &lt;code&gt;SAT&lt;&#x2F;code&gt;, such as the
above example.&lt;&#x2F;p&gt;
&lt;p&gt;To make matters worse, real-world SAT problems can have thousands of expressions
and millions of variables. Unlike my toy example above, it is completely
impractical to try to brute-force &lt;code&gt;2^1000000&lt;&#x2F;code&gt; different combinations of Boolean
variables. It &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;NP-complete&quot;&gt;is believed&lt;&#x2F;a&gt;
that for some SAT problems, there is no better strategy than brute forcing all &lt;code&gt;2^n&lt;&#x2F;code&gt; 
combinations. However, real-world SAT problems are better behaved and can be solved
efficiently using, for instance, the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;DPLL_algorithm&quot;&gt;Davis–Putnam–Logemann–Loveland&lt;&#x2F;a&gt;
(DPLL) algorithm or &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Conflict-driven_clause_learning&quot;&gt;Conflict-Driven Clause Learning&lt;&#x2F;a&gt;
(CDCL) algorithm.&lt;&#x2F;p&gt;
&lt;p&gt;SAT solvers are a type of computer program optimized for solving Boolean
satisfiability problems. They take advantage of these algorithms and hueristics
common to real-world SAT problems to efficiently find whether a query is &lt;code&gt;SAT&lt;&#x2F;code&gt;
or &lt;code&gt;UNSAT&lt;&#x2F;code&gt;&lt;a id=&quot;rev-fn-1&quot; href=&quot;#fn-1&quot;&gt;&lt;sup&gt;1&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;smt-and-smt-solvers&quot;&gt;SMT And SMT Solvers&lt;&#x2F;h3&gt;
&lt;p&gt;Satisfiability Modulo Theory (SMT) problems and solvers are what you get when
you&#x27;re no longer limited to queries of &lt;em&gt;just&lt;&#x2F;em&gt; Boolean variables. Queries can be
taken over Booleans, reals, integers, arrays of Booleans, functions,
enumerations, and many more! You can even create queries of combinations of
these data types!  As might be expected, by relying on queries over data types
besides Booleans can result in dramatic speedups, as well as dramatic increases
in complexity :).&lt;&#x2F;p&gt;
&lt;p&gt;While some data types map nicely&lt;a id=&quot;rev-fn-2&quot; href=&quot;#fn-2&quot;&gt;&lt;sup&gt;2&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 to Boolean expressions such as
&lt;a href=&quot;https:&#x2F;&#x2F;SMT-LIB.cs.uiowa.edu&#x2F;theories-FixedSizeBitVectors.shtml&quot;&gt;bitvectors&lt;&#x2F;a&gt;,
modern SMT solver use &lt;em&gt;theories&lt;&#x2F;em&gt; to map more complex data types back to Boolean
expressions. Just like how Booleans are governed by the theory of Boolean algebra, each data
type an SMT solver supports has a theory that explains &amp;quot;what you can and cannot
do&amp;quot; with the data type. For instance, consider the following expressions, comparing equality
of integers &lt;code&gt;I&lt;&#x2F;code&gt;, &lt;code&gt;J&lt;&#x2F;code&gt;, and &lt;code&gt;K&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(I == 1) &amp;amp;&amp;amp; (J == I + 1)
(I == 2) || (K == J + 3)
(I == 2) &amp;amp;&amp;amp; (J == K + 2)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The expressions &lt;code&gt;I == 1&lt;&#x2F;code&gt;, &lt;code&gt;J == I + 1&lt;&#x2F;code&gt;, &lt;code&gt;I == 2&lt;&#x2F;code&gt;, &lt;code&gt;K == J + 3&lt;&#x2F;code&gt;, &lt;code&gt;J ==K + 2&lt;&#x2F;code&gt; and
all yield Boolean values, and can therefore be converted to &lt;strong&gt;and from&lt;&#x2F;strong&gt;
Boolean values &lt;code&gt;A&lt;&#x2F;code&gt;, &lt;code&gt;B&lt;&#x2F;code&gt;, &lt;code&gt;C&lt;&#x2F;code&gt;, &lt;code&gt;D&lt;&#x2F;code&gt;, and &lt;code&gt;E&lt;&#x2F;code&gt; respectively: &lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;A &amp;amp;&amp;amp; B
C || D
C &amp;amp;&amp;amp; E
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While Boolean expressions can be solved for using the above algorithms for
SAT, solving for equality is governed by the &lt;a href=&quot;https:&#x2F;&#x2F;www21.in.tum.de&#x2F;teaching&#x2F;sar&#x2F;SS20&#x2F;6.pdf&quot;&gt;congruence closure&lt;&#x2F;a&gt;,
and the theory of integers is governed by, well, &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Number_theory&quot;&gt;number theory&lt;&#x2F;a&gt;.
The DPLL&#x2F;CDCL algorithms can be modified to handle conversion to and from Boolean
expressions, as well as handle congruence closure and other theories. This
is known as the DPLL(T) framework&lt;a id=&quot;rev-fn-3&quot; href=&quot;#fn-3&quot;&gt;&lt;sup&gt;3&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;z3-and-smt-lib&quot;&gt;&lt;code&gt;z3&lt;&#x2F;code&gt; And SMT-LIB&lt;&#x2F;h3&gt;
&lt;p&gt;Our SMT solver of choice will be Microsoft Research&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;project&#x2F;z3-3&#x2F;&quot;&gt;&lt;code&gt;z3&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. Other SMT
solvers exist, such as &lt;a href=&quot;https:&#x2F;&#x2F;yices.csl.sri.com&#x2F;&quot;&gt;&lt;code&gt;yices&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and thanks to
each using different algorithms, there is not a single SMT solver that solves
all practical SMT problems better than the others. Speedups and slowdowns can be
dramatic depending on which SMT solver you use&lt;a id=&quot;rev-fn-4&quot; href=&quot;#fn-4&quot;&gt;&lt;sup&gt;4&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
. However, a query to solve
a logic puzzle will not tax any SMT solver, so seeing that I&#x27;m familiar with its
&lt;a href=&quot;https:&#x2F;&#x2F;z3prover.github.io&#x2F;api&#x2F;html&#x2F;&quot;&gt;API&lt;&#x2F;a&gt;, I chose &lt;code&gt;z3&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For our purposes, &lt;code&gt;z3&lt;&#x2F;code&gt; is invoked as the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;z3 -smt2 model.txt
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;!--
```
yices-smt2 model-yices.txt
```
--&gt;
&lt;p&gt;&lt;code&gt;model.txt&lt;&#x2F;code&gt; is a text file we will create in &lt;a href=&quot;http:&#x2F;&#x2F;SMT-LIB.cs.uiowa.edu&#x2F;&quot;&gt;SMT-LIB&lt;&#x2F;a&gt; format.
SMT-LIB is an &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;S-expression&quot;&gt;S-expression&lt;&#x2F;a&gt;-based text
input&#x2F;output format widely-supported by SMT solvers, &lt;code&gt;z3&lt;&#x2F;code&gt; included. It contains
commands to construct input queries for many different theories, run the solver
on your query via &lt;code&gt;check-sat&lt;&#x2F;code&gt;, and examine the solver&#x27;s output. In particular,
after running &lt;code&gt;check-sat&lt;&#x2F;code&gt;, you can use SMT-LIB to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Display the value of the variables the solver used to satisfy your query
(&lt;code&gt;get-model&lt;&#x2F;code&gt;, where the values are written in terms of &lt;code&gt;define-fun&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;Display a proof that your query is &lt;code&gt;UNSAT&lt;&#x2F;code&gt; (&lt;code&gt;get-proof&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Eval&quot;&gt;Evaluate&lt;&#x2F;a&gt; functions that the solver
knows about, including functions &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#uninterpreted-functions&quot;&gt;defined for you&lt;&#x2F;a&gt;
by the solver (&lt;code&gt;get-value&lt;&#x2F;code&gt;)!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As is custom for an S-expression format, SMT-LIB has a &lt;em&gt;large&lt;&#x2F;em&gt; amount of
parantheses to represent &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Nesting_(computing)&quot;&gt;nested&lt;&#x2F;a&gt;
data structures. While I show a generic example of how to use all our needed
SMT-LIB commands, I will be skimming over the meaning of the parentheses nesting
for this example application.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;an-example-in-smt-lib-format&quot;&gt;An Example In SMT-LIB Format&lt;&#x2F;h4&gt;
&lt;p&gt;Like SAT solvers, SMT solvers can handle Boolean expressions just fine, as
long as they&#x27;re in the SMT-LIB format. Using the SAT &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#sat&quot;&gt;example&lt;&#x2F;a&gt; earlier
in the post, the equivalent SMT-LIB query would look something like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(set-option :produce-models true)
(set-logic QF_UF)

(declare-const a Bool)
(declare-const b Bool)
(declare-const c Bool)

(assert (or a (and b (not c)))) ; A || (B &amp;amp;&amp;amp; C)
(assert (and b c)) ; B &amp;amp;&amp;amp; C
(check-sat)
(get-model)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are a few things to note comparing the SAT example I created above:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The first line &lt;code&gt;set-option :produce-models true&lt;&#x2F;code&gt; enables the &lt;code&gt;get-model&lt;&#x2F;code&gt;
command. The second line &lt;code&gt;set-logic&lt;&#x2F;code&gt; restricts the data types and theories
that the solver will use. The SMT-LIB website &lt;a href=&quot;https:&#x2F;&#x2F;smtlib.cs.uiowa.edu&#x2F;logics.shtml&quot;&gt;provides&lt;&#x2F;a&gt;
a catalog of logics that can be used with &lt;code&gt;set-logic&lt;&#x2F;code&gt;, and &lt;code&gt;QF_UF&lt;&#x2F;code&gt; is a simple
sub-logic suitable for a Boolean-only query.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Although &lt;code&gt;z3&lt;&#x2F;code&gt; tolerates the first two lines missing, its good practice to
add those lines for other solvers like &lt;code&gt;yices&lt;&#x2F;code&gt; and for optimizing&lt;&#x2F;em&gt;. Assuming that
some data types and theories can&#x27;t occur in a given query sometimes allows
the solver to speed up proofs.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;!-- `QF_UF` means [quantifier-free](https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Quantifier_(logic))
logic with the theory of [uninterpreted functions](#uninterpreted-functions).
We are ignoring quantifiers for this post; see the footnotes. Although we
_will_ be using uninterpreted functions for logic puzzles, we aren&#x27;t actually
using them in the above example. The SMT-LIB [catalog](https:&#x2F;&#x2F;smtlib.cs.uiowa.edu&#x2F;logics.shtml)
doesn&#x27;t consider every conceivable combination of data types and theories.
Unfortunately, as far as I can tell, there&#x27;s no `QF` logic by itself to represent
&quot;just Boolean theory&quot;, so I chose `QF_UF` as a simple superset logic that
_is_ supported. --&gt;
&lt;!-- z3 has introduced solver-specific logics like `FD` for [Finite Domain](https:&#x2F;&#x2F;theory.stanford.edu&#x2F;~nikolaj&#x2F;programmingz3.html#sec-incrementality)
and `S` for [Strings](https:&#x2F;&#x2F;github.com&#x2F;Z3Prover&#x2F;z3&#x2F;issues&#x2F;3062) to the alphabet soup of SMT-LIB logics. For the logic puzzle, we use `QF_FD`
because it&#x27;s the [closest logic](https:&#x2F;&#x2F;github.com&#x2F;Z3Prover&#x2F;z3&#x2F;blob&#x2F;241e845da8427b9b0f09f4dbaf5593bc0f8dded4&#x2F;src&#x2F;solver&#x2F;check_logic.cpp#L196) I could find to what I actually want: uninterpreted functions, datatypes, and &lt;code&gt;Int&lt;&#x2F;code&gt;s. --&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;Variables are not implicit in SMT-LIB, and need to be declared with
&lt;code&gt;declare-const&lt;&#x2F;code&gt;. &lt;code&gt;declare-const&lt;&#x2F;code&gt; takes a variable name, and a type- &lt;code&gt;Bool&lt;&#x2F;code&gt; in
this case, but an SMT solver understands other types. During &lt;code&gt;check-sat&lt;&#x2F;code&gt;, the
solver will fill in a value for you for each variable. &lt;em&gt;The value of a variable
is the same everywhere that the variable is used&lt;&#x2F;em&gt;, hence the &lt;code&gt;const&lt;&#x2F;code&gt; part.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;As is traditional with S-expressions, operators such as &lt;code&gt;and&lt;&#x2F;code&gt;, &lt;code&gt;or&lt;&#x2F;code&gt;, etc
are written in &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Polish_notation&quot;&gt;prefix notation&lt;&#x2F;a&gt;
rather than &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Infix_notation&quot;&gt;infix notation&lt;&#x2F;a&gt;.
Parentheses are required around operators and their arguments to actually
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Eval#Theory&quot;&gt;evaluate&lt;&#x2F;a&gt; the expression&#x2F;do
the calculation&lt;a id=&quot;rev-fn-5&quot; href=&quot;#fn-5&quot;&gt;&lt;sup&gt;5&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The Boolean expressions you want to check for satisfiability aren&#x27;t
implicit either; you must tell the solver about your query using one more
more &lt;code&gt;assert&lt;&#x2F;code&gt;s. The solver must find a way to make &lt;em&gt;all&lt;&#x2F;em&gt;
&lt;code&gt;assert&lt;&#x2F;code&gt;s &lt;code&gt;true&lt;&#x2F;code&gt; simultaneously for your query to return &lt;code&gt;SAT&lt;&#x2F;code&gt;.
A query without any &lt;code&gt;assert&lt;&#x2F;code&gt;s &lt;a href=&quot;https:&#x2F;&#x2F;math.stackexchange.com&#x2F;questions&#x2F;96080&#x2F;in-satisfiability-what-is-the-difference-between-the-empty-clause-and-the-empty&quot;&gt;returns &lt;code&gt;true&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Comments are preceded with &lt;code&gt;;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;check-sat&lt;&#x2F;code&gt; checks whether your query is &lt;code&gt;SAT&lt;&#x2F;code&gt; or &lt;code&gt;UNSAT&lt;&#x2F;code&gt;, and nothing else.
In contrast, &lt;code&gt;get-model&lt;&#x2F;code&gt; returns the value of the variables the solver used to satisfy your query
&lt;em&gt;if your query was &lt;code&gt;SAT&lt;&#x2F;code&gt;&lt;&#x2F;em&gt;. In this case, the output of the solver, including
the variable values chosen, matches the SAT example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;sat
(
  (define-fun b () Bool
    true)
  (define-fun a () Bool
    true)
  (define-fun c () Bool
    true)
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Additionally, there is also &lt;code&gt;get-value&lt;&#x2F;code&gt;. As will become clear later, &lt;code&gt;get-value&lt;&#x2F;code&gt; is sometimes more useful than
&lt;code&gt;get-model&lt;&#x2F;code&gt; for presenting information about a model which satisfies
your query. &lt;code&gt;get-value&lt;&#x2F;code&gt; is especially when you&#x27;re interested in the return values of
&lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#uninterpreted-functions&quot;&gt;uninterpreted functions&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Although not represented in the above example, there are a few additional
operators available to the &lt;code&gt;Bool&lt;&#x2F;code&gt; theory in SMT-LIB that are not available in
a SAT solver due to being &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Polymorphism_(computer_science)&quot;&gt;polymorphic&lt;&#x2F;a&gt;:
&lt;code&gt;=&lt;&#x2F;code&gt;, &lt;code&gt;distinct&lt;&#x2F;code&gt;, and &lt;code&gt;ite&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;=&lt;&#x2F;code&gt; checks whether two generic types &lt;code&gt;T&lt;&#x2F;code&gt; have equal value. If &lt;code&gt;T&lt;&#x2F;code&gt; is
&lt;code&gt;Bool&lt;&#x2F;code&gt;, this is equivalent to &lt;code&gt;XNOR&lt;&#x2F;code&gt;. &lt;strong&gt;Unlike many programming languages,
SMT-LIB uses single-equals (&lt;code&gt;=&lt;&#x2F;code&gt;), not double-equals (&lt;code&gt;==&lt;&#x2F;code&gt;) for comparison.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;distinct&lt;&#x2F;code&gt; checks whether two generic types &lt;code&gt;T&lt;&#x2F;code&gt; are not of equal value.
If &lt;code&gt;T&lt;&#x2F;code&gt; is &lt;code&gt;Bool&lt;&#x2F;code&gt;, this is equivalent to &lt;code&gt;XOR&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ite&lt;&#x2F;code&gt; corresponds to &lt;code&gt;if&lt;&#x2F;code&gt;-&lt;code&gt;then&lt;&#x2F;code&gt;-&lt;code&gt;else&lt;&#x2F;code&gt; &lt;em&gt;expressions&lt;&#x2F;em&gt; in your favorite
programming language. The &lt;code&gt;if&lt;&#x2F;code&gt; &lt;em&gt;predicate&lt;&#x2F;em&gt; requires &lt;code&gt; Bool&lt;&#x2F;code&gt;, while the
&lt;code&gt;then&lt;&#x2F;code&gt; and &lt;code&gt;else&lt;&#x2F;code&gt; branches of the expression require a &lt;code&gt;T&lt;&#x2F;code&gt; as input.&lt;&#x2F;p&gt;
&lt;p&gt;The entire expression returns a &lt;code&gt;T&lt;&#x2F;code&gt; as output. It returns the value of
the &lt;code&gt;then&lt;&#x2F;code&gt; branch when the &lt;code&gt;if&lt;&#x2F;code&gt; predicate evaluates to &lt;code&gt;true&lt;&#x2F;code&gt;, and
returns the value of the &lt;code&gt;else&lt;&#x2F;code&gt; branch when the &lt;code&gt;if&lt;&#x2F;code&gt; predicate evaluates
to &lt;code&gt;false&lt;&#x2F;code&gt;. When &lt;code&gt;T&lt;&#x2F;code&gt; is &lt;code&gt;Bool&lt;&#x2F;code&gt;, &lt;code&gt;ite&lt;&#x2F;code&gt; has the following truth table:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;I&lt;&#x2F;th&gt;&lt;th&gt;T&lt;&#x2F;th&gt;&lt;th&gt;E&lt;&#x2F;th&gt;&lt;th&gt;O&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;While the above example doesn&#x27;t really use an SMT solver&#x27;s true power, it &lt;em&gt;does&lt;&#x2F;em&gt;
illustrate the fundamentals of how to interface with one. We will discuss how
to use other datatypes and theories with SMT-LIB as we turn a logic puzzle
into an SMT query.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;smt-and-logic-puzzles&quot;&gt;SMT And Logic Puzzles&lt;&#x2F;h2&gt;
&lt;p&gt;With the whirlwind introduction to SMT out of the way, my goal with this post is
to show you how to map the word problem presented by a logic puzzle to a 
&lt;code&gt;SAT&lt;&#x2F;code&gt;isfiable SMT query. I will introduce an example logic puzzle first, and
then show how to use various SMT-LIB commands to create some datatypes
and constraints to represent the puzzle.&lt;&#x2F;p&gt;
&lt;p&gt;At a high level, if you visualize a logic puzzle grid, such as the one below,
the conversion process looks like this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The SMT theory of algebraic datatypes &lt;!-- &lt;a id=&quot;rev-fn-5&quot; href=&quot;#fn-5&quot;&gt;&lt;sup&gt;5&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 --&gt; will be used to
represent the headers of each row and column of data.&lt;&#x2F;li&gt;
&lt;li&gt;Correlating each row to the correct columns of data will be represented by
the theory of uninterpreted functions &lt;!-- &lt;a id=&quot;rev-fn-5&quot; href=&quot;#fn-5&quot;&gt;&lt;sup&gt;5&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 --&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Clues&#x2F;hints and constraints &lt;em&gt;implicit&lt;&#x2F;em&gt; in a logic puzzle will be represented
by &lt;code&gt;assert&lt;&#x2F;code&gt;s.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure&gt;
&lt;img alt=&quot;An empty logic puzzle grid corresponding to the puzzle described
in the next section.&quot; src=&quot;cats-puzzle-blank.png&quot;&gt;
&lt;figcaption&gt;This empty logic puzzle contains 9 rows and columns, along with headers for
each row and column. The column headers, in left-to-right order, are: Batman,
Jake, Dibii, Lazer, Sleep, Ball, 1, 2, and 3. The row headers in top-to-bottom
order, are: Ruby, Spot, and Starbuck, 1, 2, 3, Lazer, Sleep, and Ball.&lt;&#x2F;p&gt;

&lt;p&gt;Each set of 3 row and column headers belong to the same &lt;em&gt;category&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Batman, Jake, Dibii are &lt;em&gt;tom&lt;&#x2F;em&gt;cats.&lt;&#x2F;li&gt;
&lt;li&gt;Ruby, Spot, and Starbuck are &lt;a href=&quot;https:&#x2F;&#x2F;spotpetins.com&#x2F;blog&#x2F;cat-tips&#x2F;what-is-a-female-cat-called&quot;&gt;&lt;em&gt;queen&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; cats.&lt;&#x2F;li&gt;
&lt;li&gt;Lazer, sleep, and ball are &lt;em&gt;activities&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;1, 2, and 3 are &lt;em&gt;number of kittens&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;p&gt;To solve the puzzle, you are intended to mark each table &lt;em&gt;entry&lt;&#x2F;em&gt;
a ✓ if the row and column are associated with each other, or ✗ if the row and
column are &lt;em&gt;not&lt;&#x2F;em&gt; associated with each other, based on clues given to
you (not represented in the above image).&lt;&#x2F;p&gt;

&lt;p&gt;A table entry whose row and column are of the same category does not
need to be marked. The pictured table represents this by completely omitting
such entries, making the table similar to an
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Triangular_matrix&quot;&gt;upper triangular matrix&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;p&gt;The bottom part of the puzzle consists of a table with the columns &lt;em&gt;queen&lt;&#x2F;em&gt;,
&lt;em&gt;tom&lt;&#x2F;em&gt;, &lt;em&gt;activity&lt;&#x2F;em&gt;, and &lt;em&gt;number of kittens&lt;&#x2F;em&gt;. The column
&lt;em&gt;queen&lt;&#x2F;em&gt; is filled in for you with Ruby, Spot, and Starbuck. You fill
the remaining rows of this table based on which row and columns you marked
with a ✓ in the above table.&lt;&#x2F;p&gt;

&lt;p&gt;I got the logic puzzle template (before filling in the headers) from
&lt;a href=&quot;https:&#x2F;&#x2F;daydreampuzzles.com&#x2F;logic-printables&#x2F;&quot;&gt;Daydream Puzzles&lt;&#x2F;a&gt;.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;an-example-puzzle&quot;&gt;An Example Puzzle&lt;&#x2F;h2&gt;
&lt;p&gt;For this post, I&#x27;ll be using a puzzle from &lt;a href=&quot;https:&#x2F;&#x2F;www.ahapuzzles.com&#x2F;logic&#x2F;logic-puzzles&#x2F;cats-in-spring&#x2F;&quot;&gt;Aha! Puzzles&lt;&#x2F;a&gt;
as an example. I&#x27;m not going to copy the puzzle description, since a person
presumably spent time to create the puzzle (among others), and I don&#x27;t want to
steal their work. Instead I will paraphrase the puzzle and grid, which
describes a set of cats.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;description&quot;&gt;Description&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;There are three &lt;a href=&quot;https:&#x2F;&#x2F;spotpetins.com&#x2F;blog&#x2F;cat-tips&#x2F;what-is-a-female-cat-called&quot;&gt;queens&lt;&#x2F;a&gt;
named &lt;em&gt;Ruby&lt;&#x2F;em&gt;, &lt;em&gt;Spot&lt;&#x2F;em&gt;, and &lt;em&gt;Starbuck&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Each queen has a partner, favorite activity, and number of kittens.
&lt;strong&gt;As is customary for this type of problem, it is implied that each Queen has
a different partner, favorite activity, and number of kittens.&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The possible partners (toms, as in &lt;em&gt;tom&lt;&#x2F;em&gt;cats) are: &lt;em&gt;Batman&lt;&#x2F;em&gt;, &lt;em&gt;Jake&lt;&#x2F;em&gt;, and &lt;em&gt;Dibii&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Activities include: chasing a &lt;em&gt;Lazer&lt;&#x2F;em&gt;, &lt;em&gt;Sleep&lt;&#x2F;em&gt;ing, and chasing a &lt;em&gt;Ball&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Each queen&#x2F;tom pair has between &lt;em&gt;1&lt;&#x2F;em&gt; to &lt;em&gt;3&lt;&#x2F;em&gt; kittens (inclusive).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Given the above information, and the following clues, figure out each queen&#x27;s
partner, favorite activity, and the number of kittens they have.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;clues&quot;&gt;Clues&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;em&gt;The puzzle is intended to have a unique answer&lt;&#x2F;em&gt;. However, using only the
description above, there are multiple correct answers. The clues below are
intended to limit the multiple correct answers down to a single unique answer&lt;a id=&quot;rev-fn-6&quot; href=&quot;#fn-6&quot;&gt;&lt;sup&gt;6&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Batman&#x27;s partner likes to chase a ball. She was not Starbuck.&lt;&#x2F;li&gt;
&lt;li&gt;Dibii&#x27;s partner liked the lazer.&lt;&#x2F;li&gt;
&lt;li&gt;Ruby was a cat who liked to sleep.&lt;&#x2F;li&gt;
&lt;li&gt;Starbuck has two more kittens than whoever Batman&#x27;s companion is.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;mapping-the-puzzle-to-an-smt-query&quot;&gt;Mapping The Puzzle To An SMT Query&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have a puzzle, we can map the textual description to a query and
a mathematical model that the SMT solver will return. Let&#x27;s create the query
piece by piece, starting with initial setup code:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(set-option :produce-models true)
(set-logic ALL)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The first two lines we&#x27;ve already seen, but we have a new logic &lt;code&gt;ALL&lt;&#x2F;code&gt;. &lt;code&gt;set-logic ALL&lt;&#x2F;code&gt;
is a bit of a cop-out; SMT-LIB allows &lt;code&gt;ALL&lt;&#x2F;code&gt; for applications which generate
queries on-the-fly. &lt;code&gt;ALL&lt;&#x2F;code&gt; is not intended for handwritten queries. Unfortunately, &lt;code&gt;z3&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Z3Prover&#x2F;z3&#x2F;blob&#x2F;df8ccce08e5ea7b329765a9570a0e348ccd1eaa8&#x2F;src&#x2F;solver&#x2F;check_logic.cpp#L70-L216&quot;&gt;doesn&#x27;t&lt;&#x2F;a&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Z3Prover&#x2F;z3&#x2F;blob&#x2F;df8ccce08e5ea7b329765a9570a0e348ccd1eaa8&#x2F;src&#x2F;solver&#x2F;smt_logics.cpp#L162-L164&quot;&gt;really&lt;&#x2F;a&gt;
have that many logics that support datatypes. And none of the logics that &lt;em&gt;do&lt;&#x2F;em&gt;
support datatypes seem to support &lt;em&gt;both&lt;&#x2F;em&gt; integers and uninterpreted functions.&lt;a id=&quot;rev-fn-7&quot; href=&quot;#fn-7&quot;&gt;&lt;sup&gt;7&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;

So, without any better options for now, I&#x27;m just making explicit that &lt;code&gt;z3&lt;&#x2F;code&gt; should use
&amp;quot;whatever means necessary&amp;quot; to solve the logic puzzle&lt;a id=&quot;rev-fn-8&quot; href=&quot;#fn-8&quot;&gt;&lt;sup&gt;8&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;enums&quot;&gt;Enums&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;declare-datatypes&lt;&#x2F;code&gt; is part of the most recent version of SMT-LIB, version 2.6.
It is a very general function that allows you to create complex data types,
including &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Recursive_data_type&quot;&gt;recursive&lt;&#x2F;a&gt;
data types. In our usage we will only be using it to create the equivalent of &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch06-01-defining-an-enum.html&quot;&gt;&lt;code&gt;enum&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;s without data &lt;!-- and [`pair`](https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;primitive.tuple.html)s --&gt;
in other programming languages&lt;a id=&quot;rev-fn-9&quot; href=&quot;#fn-9&quot;&gt;&lt;sup&gt;9&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;p&gt;In general, you create an &lt;code&gt;enum&lt;&#x2F;code&gt; &lt;code&gt;Foo&lt;&#x2F;code&gt; whose &lt;em&gt;variants&lt;&#x2F;em&gt; &lt;code&gt;A&lt;&#x2F;code&gt;, &lt;code&gt;B&lt;&#x2F;code&gt;, &lt;code&gt;C&lt;&#x2F;code&gt; (and more, if wish) have
data using the following syntax&lt;a id=&quot;rev-fn-10&quot; href=&quot;#fn-10&quot;&gt;&lt;sup&gt;10&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
:&lt;&#x2F;p&gt;
&lt;!-- (the `enum` doesn&#x27;t have to have _just_ 3 variants, the
[equivalence relation](https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Equivalence_relation) applies
to as many or little variants as you want without any loss of generality) don&#x27;t --&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(declare-datatypes ((Foo 0)) (((A) (B) (C))))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Anywhere in your query where a value of type &lt;code&gt;Foo&lt;&#x2F;code&gt; is used, the SMT solver
has no choice but to make the value either &lt;code&gt;A&lt;&#x2F;code&gt;, &lt;code&gt;B&lt;&#x2F;code&gt;, or &lt;code&gt;C&lt;&#x2F;code&gt;. Additionally, the
following properties hold&lt;a id=&quot;rev-fn-11&quot; href=&quot;#fn-11&quot;&gt;&lt;sup&gt;11&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;A = A&lt;&#x2F;code&gt;, &lt;code&gt;B = B&lt;&#x2F;code&gt;, and &lt;code&gt;C = C&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Reflexive_relation&quot;&gt;reflexivity&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;A != B&lt;&#x2F;code&gt;, &lt;code&gt;B != C&lt;&#x2F;code&gt;, and &lt;code&gt;A != C&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Antisymmetric_relation&quot;&gt;antisymmetry&lt;&#x2F;a&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This can be confirmed with the following query, which returns &lt;code&gt;SAT&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(declare-datatypes ((Foo 0)) (((A) (B) (C))))
(assert (and (= A A) (= B B) (= C C)))
(assert (and (distinct A B) (distinct A C) (distinct B C)))
(check-sat)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the above syntax and properties in mind, the names of the cats and their
activities correspond very nicely to &lt;code&gt;enum&lt;&#x2F;code&gt;s whose variants don&#x27;t have data:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(declare-datatypes ((Tom 0)) (((Batman) (Jake) (Dibii))))
(declare-datatypes ((Queen 0)) (((Ruby) (Spot) (Starbuck))))
(declare-datatypes ((Activities 0)) (((Lazer) (Sleep) (Ball))))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;uninterpreted-functions&quot;&gt;Uninterpreted Functions&lt;&#x2F;h3&gt;
&lt;p&gt;Functions form the backbone of programming languages and mathematics, and SMT
formulas are no exception. We can create &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Pure_function&quot;&gt;pure functions&lt;&#x2F;a&gt;
to abstract away common parts of our query using &lt;code&gt;define-fun&lt;&#x2F;code&gt;. Here is an
example &lt;code&gt;add&lt;&#x2F;code&gt; function that &amp;quot;adds one to its input &lt;code&gt;Int&lt;&#x2F;code&gt;&amp;quot;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(define-fun add ((x Int)) Int (+ x 1))
(check-sat)
(get-value ((add 1) (add 2) (add 10)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The above query &lt;a href=&quot;https:&#x2F;&#x2F;math.stackexchange.com&#x2F;questions&#x2F;96080&#x2F;in-satisfiability-what-is-the-difference-between-the-empty-clause-and-the-empty&quot;&gt;returns &lt;code&gt;SAT&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
in response to &lt;code&gt;check-sat&lt;&#x2F;code&gt;. After &lt;code&gt;check-sat&lt;&#x2F;code&gt;, your model has an &lt;code&gt;add&lt;&#x2F;code&gt; function
that the solver can call.&lt;&#x2F;p&gt;
&lt;p&gt;We don&#x27;t have any &lt;code&gt;assert&lt;&#x2F;code&gt;s in the above query, so &lt;code&gt;get-model&lt;&#x2F;code&gt; returns
nothing. However, we can display the result of calls to our &lt;code&gt;add&lt;&#x2F;code&gt; function using &lt;code&gt;get-value&lt;&#x2F;code&gt;.
&lt;code&gt;get-value&lt;&#x2F;code&gt; takes a single argument, but this single argument can
take an arbitrary number of &lt;em&gt;terms&lt;&#x2F;em&gt;. Each of the three terms to the &lt;code&gt;get-value&lt;&#x2F;code&gt;
call above will make the solver &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Read%E2%80%93eval%E2%80%93print_loop&quot;&gt;read, evaluate, and print&lt;&#x2F;a&gt;
the results of calling your &lt;code&gt;add&lt;&#x2F;code&gt; function on various integers- exciting!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;sat
(((add 1) 2)
 ((add 2) 3)
 ((add 10) 11))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;!-- &lt;code&gt;get-value&lt;&#x2F;code&gt; requires an extra set of parentheses areound --&gt;
&lt;p&gt;Not only can we define functions, we can also &lt;em&gt;declare&lt;&#x2F;em&gt; functions, without
bothering to define&#x2F;fill in the function body! We can request the solver to fill
in the function body for us by using &lt;code&gt;declare-fun&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(declare-fun add_two (Int) Int)
(check-sat)
(get-model)
(get-value ((add_two 1)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The above query returns &lt;code&gt;sat&lt;&#x2F;code&gt; in response to &lt;code&gt;check-sat&lt;&#x2F;code&gt;. &lt;code&gt;get-model&lt;&#x2F;code&gt; &lt;!-- &lt;a id=&quot;rev-fn-8&quot; href=&quot;#fn-8&quot;&gt;&lt;sup&gt;8&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 --&gt;
will then return the &lt;em&gt;definition&lt;&#x2F;em&gt; of &lt;code&gt;add_two&lt;&#x2F;code&gt; that the solver created:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;sat
(
  (define-fun add_two ((x!0 Int)) Int
    0)
)
(((add_two 1) 0))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this case &lt;code&gt;add_two&lt;&#x2F;code&gt; returns the literal &lt;code&gt;0&lt;&#x2F;code&gt;. Not particularly interesting.
What if we add an &lt;code&gt;assert&lt;&#x2F;code&gt;, to force &lt;code&gt;add_two&lt;&#x2F;code&gt; to return &lt;code&gt;3&lt;&#x2F;code&gt; when given the
input &lt;code&gt;1&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(declare-fun add_two (Int) Int)
(assert (= (add_two 1) 3))
(check-sat)
(get-model)
(get-value ((add_two 1)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;sat
(
  (define-fun add_two ((x!0 Int)) Int
    3)
)
(((add_two 1) 3))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s a function that returns &lt;code&gt;3&lt;&#x2F;code&gt; for all inputs. Now the solver is being cute.
&amp;quot;Technically correct&amp;quot; is the best type of correct. The solver won&#x27;t create a
more interesting function body unless forced to via proper constraints on the
input arguments and return values&lt;a id=&quot;rev-fn-12&quot; href=&quot;#fn-12&quot;&gt;&lt;sup&gt;12&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;p&gt;We will constrain our uninterpreted functions shortly, but for now, let&#x27;s
declare 4 uninterpreted functions with &lt;code&gt;declare-fun&lt;&#x2F;code&gt;. These functions will in
fact return the answers for our logic puzzle, once the solver fills them in:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(declare-fun QueenPartner (Queen) Tom)
(declare-fun QueenActivity (Queen) Activities)
(declare-fun QueenKittens (Queen) Int)
(declare-fun TomPartner (Tom) Queen)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To be specific:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;QueenPartner&lt;&#x2F;code&gt; returns the &lt;code&gt;Tom&lt;&#x2F;code&gt; partner of the given &lt;code&gt;Queen&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;QueenActivity&lt;&#x2F;code&gt; returns the given queen&#x27;s favorite activity.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;QueenKittens&lt;&#x2F;code&gt; returns the number of kittens a given &lt;code&gt;Queen&lt;&#x2F;code&gt; has as an &lt;code&gt;Int&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;TomPartner&lt;&#x2F;code&gt; returns the &lt;code&gt;Queen&lt;&#x2F;code&gt; partner of the given &lt;code&gt;Tom&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;implicit-constraints&quot;&gt;Implicit Constraints&lt;&#x2F;h3&gt;
&lt;p&gt;By their very nature, logic puzzles contain some usually-implied &lt;em&gt;rules&lt;&#x2F;em&gt; that we
must follow when solving them if we don&#x27;t want incoherent, nonsensical
answers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Each possible choice in each category is &lt;em&gt;different&lt;&#x2F;em&gt; from all the other choices.
It should be apparent that, for instance, Batman and Dibii are different tomcats
(&lt;em&gt;uniqueness&lt;&#x2F;em&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;There are a finite number of choices for each category, and this number is
the same for each category. E.g. each of the &lt;em&gt;three&lt;&#x2F;em&gt; queens can choose from one
of &lt;em&gt;three&lt;&#x2F;em&gt; activities. There is no fourth &amp;quot;secret&amp;quot; activity that the logic
puzzle knows about and we don&#x27;t (&lt;em&gt;finiteness&lt;&#x2F;em&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;As mentioned &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#smt-and-logic-puzzles&quot;&gt;earlier&lt;&#x2F;a&gt;, each queen has a mutually-exclusive
partner, number of kittens, and activity with respect to other queens.&lt;&#x2F;li&gt;
&lt;li&gt;We can also deduce that a tom has the same number of kittens and activity as
its queen partner.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A logic puzzle grid such as &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#smt-and-logic-puzzles&quot;&gt;above&lt;&#x2F;a&gt; visually makes it easy
to follow these rules. Unfortunately, an SMT solver knows nothing about logic
puzzles without our help, so we must explain those &lt;em&gt;implicit constraints&lt;&#x2F;em&gt; in our
query. We use &lt;code&gt;assert&lt;&#x2F;code&gt;s to force an SMT solver to reject nonsensical-but-valid
answers to a query of variables, where in the case of a logic puzzle, our
variables are our uninterpreted functions.&lt;&#x2F;p&gt;
&lt;p&gt;Our &lt;code&gt;enum&lt;&#x2F;code&gt;s we created via &lt;code&gt;declare-datatype&lt;&#x2F;code&gt; already encode these
uniqueness&#x2F;finiteness constraints mentioned above. We can also
represent the number of kittens each cat pair has as a &lt;code&gt;NumKitten&lt;&#x2F;code&gt;s &lt;code&gt;enum&lt;&#x2F;code&gt;,
but for convenience that will become clear shortly, I chose to use an &lt;code&gt;Int&lt;&#x2F;code&gt;.
While &lt;code&gt;Int&lt;&#x2F;code&gt;s have a uniqueness constraint (&lt;code&gt;1&lt;&#x2F;code&gt; is different than &lt;code&gt;2&lt;&#x2F;code&gt;), in SMT-LIB, &lt;code&gt;Int&lt;&#x2F;code&gt;s
do not have a finiteness constraint. Thus without additional information, the solver 
free to set &lt;code&gt;NumKitten&lt;&#x2F;code&gt;s for each cat pair to &lt;code&gt;1&lt;&#x2F;code&gt;, &lt;code&gt;10&lt;&#x2F;code&gt;, or even &lt;code&gt;1,234,567&lt;&#x2F;code&gt;, among
other fine &lt;code&gt;Int&lt;&#x2F;code&gt;s, if the solver so chooses.&lt;&#x2F;p&gt;
&lt;p&gt;The puzzle&#x27;s description &lt;del&gt;implies&lt;&#x2F;del&gt; outright says that the number of kittens
each cat pair must be between &lt;code&gt;1&lt;&#x2F;code&gt; and &lt;code&gt;3&lt;&#x2F;code&gt; inclusive. We can constrain the values
for number of kittens the solver will try using an &lt;code&gt;assert&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;; Integer constraint
(assert (and 
    (&amp;gt;= (QueenKittens Ruby) 1) (&amp;lt;= (QueenKittens Ruby) 3)
    (&amp;gt;= (QueenKittens Spot) 1) (&amp;lt;= (QueenKittens Spot) 3)
    (&amp;gt;= (QueenKittens Starbuck) 1) (&amp;lt;= (QueenKittens Starbuck) 3)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While the uniquess constraints of &lt;code&gt;enum&lt;&#x2F;code&gt;s and &lt;code&gt;Int&lt;&#x2F;code&gt;s gets us far, it&#x27;s not
quite enough. As quoted &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#description&quot;&gt;above&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;As is customary for this type of problem, it is implied that each queen has
a different partner, favorite activity, and number of kittens.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Although the solver recognizes that each tom is a different cat, without additional
information, nothing prevents the solver from giving an answer which assigns, for
instance, two queens to the same tomcat partner.&lt;&#x2F;p&gt;
&lt;p&gt;We can further constrain the solver from trying to assign two queens the same
partner, number of kittens, and activity by constraining the &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#uninterpreted-functions&quot;&gt;uninterpreted functions&lt;&#x2F;a&gt;
we defined earlier via more &lt;code&gt;assert&lt;&#x2F;code&gt;s. For instance, the statement
&lt;code&gt;(assert (not (= (QueenPartner Ruby) (QueenPartner Spot)))&lt;&#x2F;code&gt; tells the SMT solver
&amp;quot;&lt;em&gt;However&lt;&#x2F;em&gt; you decide to fill the function body, I need the function &lt;code&gt;QueenPartner&lt;&#x2F;code&gt;
to &lt;em&gt;not&lt;&#x2F;em&gt; return the same value for &lt;code&gt;Ruby&lt;&#x2F;code&gt; and &lt;code&gt;Spot&lt;&#x2F;code&gt;&amp;quot;. To properly constrain our
query, you need one &lt;code&gt;assert&lt;&#x2F;code&gt; for each possible pair of values in each category,
which can be combined into a single large &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Logical_conjunction&quot;&gt;conjuction&lt;&#x2F;a&gt;&lt;a id=&quot;rev-fn-13&quot; href=&quot;#fn-13&quot;&gt;&lt;sup&gt;13&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
:&lt;&#x2F;p&gt;
&lt;!-- Each cat, activity, and number of kittens is a _different_ entity and the
solver should treat all of them as such. Unfortunately, while the SMT knows that
a variable of type `queen` cannot take on a value of, say, Batman the tomcat,
the SMT solver _does not_ know that Batman, Jake, and Dibii are different tomcats.
If you do not tell --&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;; &amp;quot;Only one dot in each row&amp;#x2F;col&amp;quot; consistency check.
(assert (and
    (not (= (QueenPartner Ruby) (QueenPartner Spot)))
    (not (= (QueenPartner Ruby) (QueenPartner Starbuck)))
    (not (= (QueenPartner Spot) (QueenPartner Starbuck)))
    (not (= (QueenKittens Ruby) (QueenKittens Spot)))
    (not (= (QueenKittens Ruby) (QueenKittens Starbuck)))
    (not (= (QueenKittens Spot) (QueenKittens Starbuck)))
    (not (= (QueenActivity Ruby) (QueenActivity Spot)))
    (not (= (QueenActivity Ruby) (QueenActivity Starbuck)))
    (not (= (QueenActivity Spot) (QueenActivity Starbuck)))))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The above check is morally equivalent to a human making sure a logic 
grid has only one ✓ per row and column for each &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Combination&quot;&gt;combination&lt;&#x2F;a&gt;
of values in each category. Because of the uniqueness constraints for each
&lt;code&gt;enum&lt;&#x2F;code&gt; and &lt;code&gt;Int&lt;&#x2F;code&gt;, the above is sufficient to make sure the solver knows that
each queen has a mutually exclusive partner, number of kittens, and activity
&lt;em&gt;and that such a solution exists&lt;&#x2F;em&gt;&lt;a id=&quot;rev-fn-14&quot; href=&quot;#fn-14&quot;&gt;&lt;sup&gt;14&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, each cat pair shares a favorite activity and the number kittens they have.
So far, we&#x27;ve only written constraints for each queen in the cat pair.
However, we don&#x27;t need to rewrite the constraints for each tom! There is another
consistency check we need, and a side-effect, we can represent each tom&#x27;s
favorite activity and number of kittens for free in terms of this constraint.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;TomPartner&lt;&#x2F;code&gt; uninterpreted function returns the &lt;code&gt;Queen&lt;&#x2F;code&gt; who is the partner
of the given &lt;code&gt;Tom&lt;&#x2F;code&gt;. Likewise, the &lt;code&gt;QueenPartner&lt;&#x2F;code&gt; function returns the &lt;code&gt;Tom&lt;&#x2F;code&gt;
whose partner is the given &lt;code&gt;Queen&lt;&#x2F;code&gt;. Without extra constraints, the SMT solver
doesn&#x27;t know &amp;quot;obvious&amp;quot; facts like &amp;quot;&#x27;the partner of the partner&#x27; of a cat is
the same cat you started with&amp;quot;&lt;a id=&quot;rev-fn-15&quot; href=&quot;#fn-15&quot;&gt;&lt;sup&gt;15&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
. We can represent this fact with
another &lt;code&gt;assert&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;; All other constraints for Tom are implied if the partner of the partner is the same.
(assert (and
    (= (QueenPartner (TomPartner Batman)) Batman)
    (= (QueenPartner (TomPartner Jake)) Jake)
    (= (QueenPartner (TomPartner Dibii)) Dibii)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After this constraint, &lt;code&gt;TomPartner&lt;&#x2F;code&gt; unambiguously &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Map_(mathematics)&quot;&gt;maps&lt;&#x2F;a&gt;
a tom back to queen. Since a cat pair has the same favorite activity and number
of kittens, the solver now has enough information to solve for each tom&#x27;s
favorite activity and number of kittens in terms of queens.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;explicit-constraints&quot;&gt;Explicit Constraints&lt;&#x2F;h3&gt;
&lt;p&gt;The implicit constraits are &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Necessity_and_sufficiency&quot;&gt;necessary but not sufficient&lt;&#x2F;a&gt; to
constrain the logic puzzle to a unique answer. If an SMT solver is presented
with only the above lines, but there will be multiple ways to fill in our
uninterpreted functions which satisfy that query. The solver will choose to 
present to you&lt;a id=&quot;rev-fn-16&quot; href=&quot;#fn-16&quot;&gt;&lt;sup&gt;16&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 one interpretation for each function out of all
valid ones, and chances are, it won&#x27;t be the intended answer to the logic
puzzle based on the clues.&lt;&#x2F;p&gt;
&lt;p&gt;If you incorporate the clues, however, the SMT solver &lt;em&gt;should&lt;&#x2F;em&gt;&lt;a id=&quot;rev-fn-17&quot; href=&quot;#fn-17&quot;&gt;&lt;sup&gt;17&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;

be limited to only one valid answer to your query. In this sense, the clues given
&lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#clues&quot;&gt;above&lt;&#x2F;a&gt; form the &lt;em&gt;explicit&lt;&#x2F;em&gt;, outright-stated constraints unique to a
specific logic puzzle. Explicit constraints are handled identically to the
implicit constraints in the &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#implicit-constraints&quot;&gt;previous section&lt;&#x2F;a&gt;, by
mapping a textual description of the clues described earlier to &lt;code&gt;asserts&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Batman&#x27;s partner likes to chase a ball. She was not Starbuck.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;; 1. Batman chose the female who liked to chase a ball, but she was not Starbuck.
(assert (= (QueenActivity (TomPartner Batman)) Ball))
(assert (not (= (TomPartner Batman) Starbuck)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Dibii&#x27;s partner liked the lazer.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;; 2. Dibii&amp;#x27;s companion liked to chase the laser light.
(assert (= (QueenActivity (TomPartner Dibii)) Lazer))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Ruby was a cat who liked to sleep.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;; 3. Ruby loved to cuddle up to her male for a long afternoon nap in the sun.
(assert (= (QueenActivity Ruby) Sleep))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Starbuck has two more kittens than whoever Batman&#x27;s companion is.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This clue is the reason I represented the number of kittens as an &lt;code&gt;Int&lt;&#x2F;code&gt;. While
I would have to reinvent addition if I used a &lt;code&gt;NumKittens&lt;&#x2F;code&gt; &lt;code&gt;enum&lt;&#x2F;code&gt;, the SMT
solver already knows the theory of &lt;code&gt;Int&lt;&#x2F;code&gt;s and can add them using &lt;code&gt;+&lt;&#x2F;code&gt;&lt;a id=&quot;rev-fn-18&quot; href=&quot;#fn-18&quot;&gt;&lt;sup&gt;18&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;; 4. Starbuck had two more kittens than Batman&amp;#x27;s companion.
(assert (= (QueenKittens Starbuck)
           (+ (QueenKittens (TomPartner Batman)) 2)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;getting-an-answer&quot;&gt;Getting An Answer&lt;&#x2F;h3&gt;
&lt;p&gt;Once, we add &lt;code&gt;check-sat&lt;&#x2F;code&gt;, our query text file is finished, and we can feed it
to &lt;code&gt;z3&lt;&#x2F;code&gt; using the &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;#z3-and-smt-lib&quot;&gt;invocation described&lt;&#x2F;a&gt; to check whether our
query is satisfiable. You can download the completed query &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;logicpuzzle-z3&#x2F;.&#x2F;model-z3-qf.txt&quot;&gt;here&lt;&#x2F;a&gt;
and run it &lt;a href=&quot;https:&#x2F;&#x2F;microsoft.github.io&#x2F;z3guide&#x2F;playground&#x2F;Freeform%20Editing&#x2F;&quot;&gt;online&lt;&#x2F;a&gt;
(as of 2023-08-13) if you wish.&lt;&#x2F;p&gt;
&lt;p&gt;Many times, knowing &lt;code&gt;SAT&lt;&#x2F;code&gt; or &lt;code&gt;UNSAT&lt;&#x2F;code&gt; is &amp;quot;good enough&amp;quot;. But for a logic puzzle,
we actually want the variable values. &lt;code&gt;get-model&lt;&#x2F;code&gt; works, but this shows
the definition of the uninterpreted functions you asked the solver to fill in.
While seeing the function bodies are useful information, for solving a logic puzzle,
it&#x27;s probably better to use &lt;code&gt;get-value&lt;&#x2F;code&gt; to see individual outputs of each
&lt;em&gt;now&lt;&#x2F;em&gt;-interpreted function for &amp;quot;interesting&amp;quot; inputs.&lt;&#x2F;p&gt;
&lt;p&gt;To compare and contrast, I use all of &lt;code&gt;check-sat&lt;&#x2F;code&gt;, &lt;code&gt;get-model&lt;&#x2F;code&gt;, and &lt;code&gt;get-value&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;(check-sat)
(get-model)

(echo &amp;quot;
Spot:&amp;quot;)
(get-value ((QueenPartner Spot)
            (QueenKittens Spot)
            (QueenActivity Spot)))

(echo &amp;quot;
Ruby:&amp;quot;)
(get-value ((QueenPartner Ruby)
            (QueenKittens Ruby)
            (QueenActivity Ruby)))

(echo &amp;quot;
Starbuck:&amp;quot;)
(get-value ((QueenPartner Starbuck)
            (QueenKittens Starbuck)
            (QueenActivity Starbuck)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Running the entire query through my local copy of &lt;code&gt;z3&lt;&#x2F;code&gt; gives the following
output:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lisp&quot; class=&quot;language-lisp &quot;&gt;&lt;code class=&quot;language-lisp&quot; data-lang=&quot;lisp&quot;&gt;sat
(
  (define-fun QueenKittens ((x!0 Queen)) Int
    (ite (= x!0 Spot) 1
    (ite (= x!0 Starbuck) 3
      2)))
  (define-fun QueenPartner ((x!0 Queen)) Tom
    (ite (= x!0 Spot) Batman
    (ite (= x!0 Starbuck) Dibii
      Jake)))
  (define-fun QueenActivity ((x!0 Queen)) Activities
    (ite (= x!0 Starbuck) Lazer
    (ite (= x!0 Ruby) Sleep
      Ball)))
  (define-fun TomPartner ((x!0 Tom)) Queen
    (ite (= x!0 Jake) Ruby
    (ite (= x!0 Dibii) Starbuck
      Spot)))
)


Spot:
(((QueenPartner Spot) Batman)
 ((QueenKittens Spot) 1)
 ((QueenActivity Spot) Ball))

Ruby:
(((QueenPartner Ruby) Jake)
 ((QueenKittens Ruby) 2)
 ((QueenActivity Ruby) Sleep))

Starbuck:
(((QueenPartner Starbuck) Dibii)
 ((QueenKittens Starbuck) 3)
 ((QueenActivity Starbuck) Lazer))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;First off, the query is &lt;code&gt;sat&lt;&#x2F;code&gt;, which is a good start! The model returned by
&lt;code&gt;get-model&lt;&#x2F;code&gt; isn&#x27;t hard for me to follow either. I could fairly quickly figure
out each answer to the logic puzzle using &lt;code&gt;get-model&lt;&#x2F;code&gt; alone, and I&#x27;m sure anyone
else reading this post could too. But using &lt;code&gt;get-value&lt;&#x2F;code&gt; to evaluate the
functions the solver filled in for us gives us the logic puzzle answers directly,
ready to be written down! So let&#x27;s save ourselves the trouble and populate the
puzzle&#x27;s answers thanks to &lt;code&gt;get-value&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Was the solver correct? Well, as of this writing (2023-07-23), Aha! Puzzles
lets you solve logic puzzles interactively online. I&#x27;ll just let the below
screenshot speak for itself:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;An image from Firefox showing the Aha! Puzzles website popup with
a solved logic puzzle grid behind the popup. A table of answers to the puzzle
is to the right side of the grid, also partially obscured. The popup says &amp;amp;quot;Congratulations, you win!&amp;amp;quot;,
showing a solve time of 19 seconds.&quot; src=&quot;firefox_nSGLgqMNYU.png&quot;&gt;
&lt;figcaption&gt;The popup says &quot;Congratulations, you win!&quot;, so indeed &lt;code&gt;z3&lt;&#x2F;code&gt; was correct.
Although the logic puzzle grid and answer table is obscured by the popup, there
is &lt;em&gt;just&lt;&#x2F;em&gt; enough of the grid and answer table shown to verify that I
filled in the puzzle with the answers returned by &lt;code&gt;z3&lt;&#x2F;code&gt;.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;conclusion-next-steps&quot;&gt;Conclusion&#x2F;Next Steps&lt;&#x2F;h2&gt;
&lt;p&gt;I still find logic puzzles fun to work through manually. To me, asking a machine
to solve every logic puzzle for you defeats the satisfaction of working your
brain to solve the puzzle yourself. So while I hope the above example provides
some insight as to how to map word problems to an SMT query&#x2F;model, I wouldn&#x27;t
suggest that you start solving &lt;em&gt;every&lt;&#x2F;em&gt; logic puzzle with an SMT solver.&lt;&#x2F;p&gt;
&lt;p&gt;That said, I found that trying to map a logic puzzle to an SMT solver query was
&lt;em&gt;also&lt;&#x2F;em&gt; intellectually stimulating. It is a low-stakes problem that gave me the
opportunity to exercise my skills at creating SMT queries, as opposed to my
typical usage where I have a program &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;YosysHQ&#x2F;sby&quot;&gt;generate&lt;&#x2F;a&gt;
queries and models for me. I was excited after I got my query working, and I
wanted to document the result.&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of generating queries and models, it&#x27;s possible to take this logic
puzzle-solving use case further by creating a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Domain-specific_language&quot;&gt;Domain-Specific Language&lt;&#x2F;a&gt; (DSL).
Like how the previously-linked SymbiYosys converts Verilog to SMT-LIB queries,
this DSL would convert a more convenient textual representation of a logic
puzzle into SMT-LIB queries. It&#x27;s a bit more up-front work than writing SMT-LIB
queries manually, and probably not worth the time spent other than for learning.
However, since this project is &lt;em&gt;meant&lt;&#x2F;em&gt; to be low-stakes and for fun, I may look
into a DSL in the future for a follow-up post. It&#x27;s about time I wrote a(&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cr1901&#x2F;hl2html&quot;&gt;nother&lt;&#x2F;a&gt;)
compiler anyway.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thanks&quot;&gt;Thanks&lt;&#x2F;h2&gt;
&lt;p&gt;I would like to thank my friends &lt;a href=&quot;https:&#x2F;&#x2F;teh.entar.net&#x2F;@Screwtapello&quot;&gt;Screwtape&lt;&#x2F;a&gt;
and &lt;a href=&quot;https:&#x2F;&#x2F;rosenzweig.io&#x2F;&quot;&gt;Alyssa Rosenzweig&lt;&#x2F;a&gt; for their general feedback and
a few last-minute corrections on the final draft of this post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;&#x2F;h2&gt;
&lt;p id=&quot;fn-1&quot;&gt;&lt;a href=&quot;#rev-fn-1&quot;&gt;1&lt;&#x2F;a&gt; I will not be discussing how to write a SAT solver; for those interested I suggest
&lt;a href=&quot;https:&#x2F;&#x2F;sahandsaba.com&#x2F;understanding-sat-by-implementing-a-simple-sat-solver-in-python.html&quot;&gt;this tutorial&lt;&#x2F;a&gt; 
for building a SAT solver in Python. The tutorial is based on
&lt;a href=&quot;https:&#x2F;&#x2F;www-cs-faculty.stanford.edu&#x2F;~knuth&#x2F;&quot;&gt;Donald Knuth&#x27;s&lt;&#x2F;a&gt; &quot;SAT0W&quot; solver.&lt;&#x2F;p&gt;

&lt;p&gt;Although I have not personally done so yet, I would probably also look at Knuth&#x27;s &quot;SAT10&quot;
solver, which is based on the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;DPLL_algorithm&quot;&gt;Davis–Putnam–Logemann–Loveland&lt;&#x2F;a&gt;
(DPLL) algorithm.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-2&quot;&gt;&lt;a href=&quot;#rev-fn-2&quot;&gt;2&lt;&#x2F;a&gt; To be honest, I&#x27;m going by what &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Satisfiability_modulo_theories#Solver_approaches&quot;&gt;
Wikipedia says&lt;&#x2F;a&gt; here. There&#x27;s no external reference:

&lt;blockquote&gt;
Early attempts for solving SMT instances involved translating them to Boolean
SAT instances (e.g., a 32-bit integer variable would be encoded by 32 single-bit
variables with appropriate weights and word-level operations such as &#x27;plus&#x27;
would be replaced by lower-level logic operations on the bits) and passing this
formula to a Boolean SAT solver.
&lt;&#x2F;blockquote&gt;&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-3&quot;&gt;&lt;a href=&quot;#rev-fn-3&quot;&gt;3&lt;&#x2F;a&gt; Just like for SAT solvers, I will not be discussing how to write an SMT solver.
While in the process of writing this post, I have been learning about the
DPLL(T) framework myself. I&#x27;ve found the following set of slides useful:&lt;&#x2F;p&gt;

&lt;!-- &lt;p&gt;I still have a few questions (see next paragraphs), but I&#x27;ve found the
following set of slides useful:&lt;&#x2F;p&gt; --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;courses.cs.washington.edu&#x2F;courses&#x2F;cse507&#x2F;16sp&#x2F;lectures&#x2F;L7.pdf&quot;&gt;Lecture 7&lt;&#x2F;a&gt;
of &lt;a href=&quot;https:&#x2F;&#x2F;emina.github.io&#x2F;&quot;&gt;Emina Torlak&#x27;s&lt;&#x2F;a&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;courses.cs.washington.edu&#x2F;courses&#x2F;cse507&#x2F;16sp&#x2F;index.html&quot;&gt;CSE507&lt;&#x2F;a&gt;.
In particular, Emina&#x27;s slides outline the DPLL(T) algorithm in terms of
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Conflict-driven_clause_learning&quot;&gt;Conflict-Driven
Clause Learning&lt;&#x2F;a&gt; (CDCL), an extension of DPLL.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;web.eecs.umich.edu&#x2F;~weimerw&#x2F;2017-590&#x2F;lectures&#x2F;weimer-proof-02.pdf&quot;&gt;Lecture 2&lt;&#x2F;a&gt;
of &lt;a href=&quot;https:&#x2F;&#x2F;web.eecs.umich.edu&#x2F;~weimerw&#x2F;&quot;&gt;Wes Weimer&#x27;s&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;web.eecs.umich.edu&#x2F;~weimerw&#x2F;2017-590&#x2F;&quot;&gt;CS590&lt;&#x2F;a&gt;.
&lt;&#x2F;ul&gt;

&lt;!--  &lt;p&gt;In particular, Emina&#x27;s slides outline the DPLL(T) algorithm in terms of
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Conflict-driven_clause_learning&quot;&gt;Conflict-Driven
Clause Learning&lt;&#x2F;a&gt; (CDCL), an extension of DPLL.&lt;&#x2F;p&gt;

&lt;p&gt;Based on the Wikipedia description, I &lt;em&gt;believe&lt;&#x2F;em&gt;, but have not confirmed,
that each CDCL step in the loop of the DPLL(T) algorithm runs until a conflict
is found (step 12 of Wikipedia article), at which point the CDCL routine
returns and the returned term is negated when the next loop iteration starts.&lt;&#x2F;p&gt; --&gt;&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-4&quot;&gt;&lt;a href=&quot;#rev-fn-4&quot;&gt;4&lt;&#x2F;a&gt; In my own experience, &lt;code&gt;yices&lt;&#x2F;code&gt; is significantly faster at many problems involving
formal verification of HDL. However, it is &lt;em&gt;not&lt;&#x2F;em&gt; universal. This is why the
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;YosysHQ&#x2F;sby&quot;&gt;Symbiyosys&lt;&#x2F;a&gt; front end for HDL
verification will run the same query against multiple SMT solvers in parallel
if you have them installed.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-5&quot;&gt;&lt;a href=&quot;#rev-fn-5&quot;&gt;5&lt;&#x2F;a&gt; If you don&#x27;t include parentheses to actually evaluate operators, weird things
you probably didn&#x27;t intend will happen. For instance, try comparing the result
of the query &lt;code&gt;(assert (= or true false true))&lt;&#x2F;code&gt; to
&lt;code&gt;(assert (= (or true false) true))&lt;&#x2F;code&gt;.

&lt;!--  &lt;p&gt;For instance,  &lt;code&gt;(assert (= or true false true))&lt;&#x2F;code&gt; will return
&lt;code&gt;UNSAT&lt;&#x2F;code&gt;, because the &lt;code&gt;=&lt;&#x2F;code&gt; operator is comparing the value
of the &lt;code&gt;or&lt;&#x2F;code&gt; operator (&lt;em&gt;whatever that is&lt;&#x2F;em&gt;) to the constants
&lt;code&gt;true&lt;&#x2F;code&gt; and &lt;code&gt;false&lt;&#x2F;code&gt;. Apparently, these three things are
the same type (otherwise a type error when evaluating &lt;code&gt;=&lt;&#x2F;code&gt; would occur),
but &lt;em&gt;not&lt;&#x2F;em&gt; the same value, according to &lt;code&gt;z3&lt;&#x2F;code&gt; :).&lt;&#x2F;p&gt;

&lt;p&gt;On the other hand &lt;code&gt;(assert (= (or true false) true))&lt;&#x2F;code&gt; returns &lt;code&gt;SAT&lt;&#x2F;code&gt;,
because the &lt;code&gt;=&lt;&#x2F;code&gt; operator is comparing the value of evaluating
&lt;code&gt;(or true false)&lt;&#x2F;code&gt; to that of &lt;code&gt;true&lt;&#x2F;code&gt;. &lt;code&gt;(or true false)&lt;&#x2F;code&gt;
is indeed &lt;code&gt;true&lt;&#x2F;code&gt;, based on how Boolean OR-ing works, so the &lt;code&gt;assert&lt;&#x2F;code&gt;
is satisfied. --&gt;&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-6&quot;&gt;&lt;a href=&quot;#rev-fn-6&quot;&gt;6&lt;&#x2F;a&gt; More succinctly, the clues &quot;constrain the solution space&quot;.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-7&quot;&gt;&lt;a href=&quot;#rev-fn-7&quot;&gt;7&lt;&#x2F;a&gt; The logic that&#x27;s closest to what I need, Quantifier-Free &lt;a href=&quot;https:&#x2F;&#x2F;theory.stanford.edu&#x2F;~nikolaj&#x2F;programmingz3.html#sec-incrementality&quot;&gt;Finite Domain&lt;&#x2F;a&gt; (&lt;code&gt;QF_FD&lt;&#x2F;code&gt;),
has some &lt;a href= &quot;https:&#x2F;&#x2F;github.com&#x2F;Z3Prover&#x2F;z3&#x2F;blob&#x2F;df8ccce08e5ea7b329765a9570a0e348ccd1eaa8&#x2F;RELEASE_NOTES.md?plain=1#L556-L560)&quot;
&lt;a&gt;additional restrictions&lt;&#x2F;a&gt; which cause my logic puzzle query to return
&lt;code&gt;UNKNOWN&lt;&#x2F;code&gt;. I&#x27;m not actually certain why it returns &lt;code&gt;UNKNOWN&lt;&#x2F;code&gt;,
but I &lt;em&gt;suspect&lt;&#x2F;em&gt; I&#x27;m running afoul of the restrictions by using datatypes 
with uninterpreted functions, and not &lt;em&gt;just&lt;&#x2F;em&gt; equality (&lt;code&gt;=&lt;&#x2F;code&gt;) comparisons.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-8&quot;&gt;&lt;a href=&quot;#rev-fn-8&quot;&gt;8&lt;&#x2F;a&gt; I belive `ALL` is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Z3Prover&#x2F;z3&#x2F;blob&#x2F;df8ccce08e5ea7b329765a9570a0e348ccd1eaa8&#x2F;src&#x2F;api&#x2F;api_context.h#L68&quot;&gt;
&lt;code&gt;z3&lt;&#x2F;code&gt;&#x27;s default logic anyway.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-9&quot;&gt;&lt;a href=&quot;#rev-fn-9&quot;&gt;9&lt;&#x2F;a&gt; &lt;code&gt;declare-datatypes&lt;&#x2F;code&gt; is not required, and the logic puzzle can
be solved without them. In fact, I &lt;em&gt;have&lt;&#x2F;em&gt; constructed a working
&lt;a href=&quot;.&#x2F;model-yices-uf.txt&quot;&gt;query without datatypes&lt;&#x2F;a&gt; for this logic puzzle- 
&lt;a href=&quot;.&#x2F;model-yices-uflia.txt&quot;&gt;twice&lt;&#x2F;a&gt;! I wanted to solve the puzzle with
&lt;code&gt;yices&lt;&#x2F;code&gt; as well as z3 as an exercise; while &lt;code&gt;yices&lt;&#x2F;code&gt; does
support &lt;code&gt;enum&lt;&#x2F;code&gt;s and the like, they are not compatible with SMT-LIB.&lt;&#x2F;p&gt;

&lt;p&gt;As shown in the linked &lt;code&gt;yices&lt;&#x2F;code&gt; examples, &lt;code&gt;declare-sort&lt;&#x2F;code&gt;,
&lt;code&gt;declare-const&lt;&#x2F;code&gt;, and &lt;code&gt;declare-fun&lt;&#x2F;code&gt;, plus some &lt;code&gt;assert&lt;&#x2F;code&gt;
constraints can emulate &lt;code&gt;declare-datatypes&lt;&#x2F;code&gt; and finite integers reasonably. However, for
a handwritten example, I wanted to focus on creating constraints appropriate
to a logic puzzle rather than reinventing &lt;code&gt;enum&lt;&#x2F;code&gt;s.&lt;&#x2F;p&gt;

&lt;!-- &lt;p&gt;For our purposes &lt;code&gt;declare-datatypes&lt;&#x2F;code&gt; is a more concise version
of &lt;code&gt;declare-sort&lt;&#x2F;code&gt; to declare an enum or pair type, and &lt;code&gt;declare-const&lt;&#x2F;code&gt;
to delcare an enum&#x27;s variants, &lt;code&gt;declare-fun&lt;&#x2F;code&gt; to access either part of
a pair, plus some constraints via &lt;code&gt;assert&lt;&#x2F;code&gt;s.&lt;&#x2F;p&gt;

&lt;p&gt;From the solver&#x27;s point-of-view, datatypes have useful assumptions encoded into
them, such as each value (variant) of an enum not being equal to any other. These
assumptions abstract the required &lt;code&gt;assert&lt;&#x2F;code&gt; constraints above away,
to make queries quite a bit more concise. I think for a handwritten example,
the removed boilerplate is worth it without making the solvers job more complex.
So for the sake of this post, I opt to use &lt;code&gt;declare-datatypes&lt;&#x2F;code&gt;. --&gt;&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-10&quot;&gt;&lt;a href=&quot;#rev-fn-10&quot;&gt;10&lt;&#x2F;a&gt; The integer argument to &lt;code&gt;declare-datatypes&lt;&#x2F;code&gt; is used for &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Parametric_polymorphism&quot;&gt;
parametric polymorphism&lt;&#x2F;a&gt;. It represents, informally, &quot;the number of types to
be filled in later&quot;.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-11&quot;&gt;&lt;a href=&quot;#rev-fn-11&quot;&gt;11&lt;&#x2F;a&gt; What &lt;code&gt;A&lt;&#x2F;code&gt;, &lt;code&gt;B&lt;&#x2F;code&gt;, and &lt;code&gt;C&lt;&#x2F;code&gt; actually &lt;em&gt;are&lt;&#x2F;em&gt; in
terms of &quot;bits in memory somewhere&quot; doesn&#x27;t matter as long as the solver
manipulates these values of type &lt;code&gt;Foo&lt;&#x2F;code&gt; according to the above rules.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-12&quot;&gt;&lt;a href=&quot;#rev-fn-12&quot;&gt;12&lt;&#x2F;a&gt; To make the example more interesting, I tried to use &lt;code&gt;assert&lt;&#x2F;code&gt;s to
get the solver to generate a function body which uses &lt;code&gt;+&lt;&#x2F;code&gt; (integer addition),
but got bored and gave up. This post is getting long anyway.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-13&quot;&gt;&lt;a href=&quot;#rev-fn-13&quot;&gt;13&lt;&#x2F;a&gt; Readers familiar with using &lt;code&gt;z3&lt;&#x2F;code&gt; may notice that I can more-succinctly
represent the exclusivity constraints using a &lt;code&gt;forall&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Quantifier_(logic)&quot;&gt;quantifier&lt;&#x2F;a&gt;.
Indeed, this works, and was how I &lt;a href=&quot;.&#x2F;model-z3.txt&quot;&gt;originally&lt;&#x2F;a&gt; solved
the example logic puzzle. Except for an extra &lt;a href=&quot;https:&#x2F;&#x2F;microsoft.github.io&#x2F;z3guide&#x2F;docs&#x2F;theories&#x2F;Datatypes&#x2F;#records&quot;&gt;
&lt;code&gt;pair&lt;&#x2F;code&gt; datatype&lt;&#x2F;a&gt;, the quantifier version of our query is
very similar to the &lt;a href=&quot;.&#x2F;model-z3-qf.txt&quot;&gt;quantifier-free query&lt;&#x2F;a&gt; constructed
in this blog post; just compare the comments.

&lt;p&gt;However, I decided &lt;em&gt;not&lt;&#x2F;em&gt; not to discuss quantifiers in this post for a
few reasons:

&lt;ol&gt;
&lt;li&gt;The quantifiers are not required for enumerations with finite variants to
create the constraints.&lt;&#x2F;li&gt;
&lt;li&gt;I am not exceptionally familiar with using quantifiers in
SMT queries and when they are &lt;em&gt;actually required&lt;&#x2F;em&gt;. I haven&#x27;t needed them
for HDL verification, for instance.&lt;&#x2F;li&gt;
&lt;li&gt;Quantifiers &lt;a href=&quot;https:&#x2F;&#x2F;microsoft.github.io&#x2F;z3guide&#x2F;docs&#x2F;logic&#x2F;Quantifiers&quot;&gt;complicate&lt;&#x2F;a&gt;
the SMT solver&#x27;s job. While queries without quantifiers can always be determined
to be &lt;code&gt;SAT&lt;&#x2F;code&gt; or &lt;code&gt;UNSAT&lt;&#x2F;code&gt;, some queries with quanitifers
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Decidability_(logic)&quot;&gt;are undecidable&lt;&#x2F;a&gt;.
As of this writing (2023-07-02) I don&#x27;t think I&#x27;m qualified to discuss
undecidability other than in passing.
&lt;&#x2F;li&gt;
&lt;li&gt;Not all SMT solvers support quantifiers (e.g. &lt;code&gt;yices&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

&lt;p&gt;Therefore, while the quantifiers are likely harmless here, I&#x27;d rather 
them when they are &lt;em&gt;actually required&lt;&#x2F;em&gt;, not just as a means to make
queries take fewer lines.&lt;&#x2F;p&gt;&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-14&quot;&gt;&lt;a href=&quot;#rev-fn-14&quot;&gt;14&lt;&#x2F;a&gt; If two &lt;code&gt;enum&lt;&#x2F;code&gt; variants could be equal each other in value (i.e. not &lt;code&gt;distinct&lt;&#x2F;code&gt;),
it would be impossible for our uninterpreted functions to return three &lt;code&gt;distinct&lt;&#x2F;code&gt;
&lt;code&gt;enum&lt;&#x2F;code&gt; variants to three different values of input &lt;code&gt;enum&lt;&#x2F;code&gt;
variants.

To show this, play with this commented query &lt;a href=&quot;https:&#x2F;&#x2F;microsoft.github.io&#x2F;z3guide&#x2F;playground&#x2F;Freeform%20Editing&#x2F;&quot;&gt;online&lt;&#x2F;a&gt;:
&lt;pre&gt;
(declare-sort Foo 0) ; declare-sort declares a new type, like `Int`, `Bool`.
                     ; It has fewer assumptions baked-in than `declare-datatypes`.

; Create some named values for our types.
; These are like our `enum` variants.
(declare-const foo_one Foo)
(declare-const foo_two Foo) 
(declare-const foo_three Foo)

(declare-sort Bar 0)
(declare-const bar_one Bar)
(declare-const bar_two Bar)
(declare-const bar_three Bar)

; Our uninterpreted function. Takes a `Foo`, returns a `Bar`.
(declare-fun my-fun (Foo) Bar)

; Ensure each constant has a unique value. This is implied 
; `declare-datatypes`.
;
; Change _any_ of the 6 asserts from `distinct` to `=`.
; The solver will return `unsat`.
(assert (distinct foo_one foo_two))
(assert (distinct foo_one foo_three))
(assert (distinct foo_two foo_three))

(assert (distinct bar_one bar_two))
(assert (distinct bar_one bar_three))
(assert (distinct bar_two bar_three))

; Prevent the solver from trying to create more values for our types
; from thin air. This is implied in `declare-datatypes`.
(assert (and
    (or
        (= (my-fun foo_one) bar_one)
        (= (my-fun foo_one) bar_two)
        (= (my-fun foo_one) bar_three))
    (or
        (= (my-fun foo_two) bar_one)
        (= (my-fun foo_two) bar_two)
        (= (my-fun foo_two) bar_three))
    (or
        (= (my-fun foo_three) bar_one)
        (= (my-fun foo_three) bar_two)
        (= (my-fun foo_three) bar_three))))

; Each input to my-fun should return a unique output.
; This is impossible unless foo_{one, two, three} have
; different values according to `=`.
(assert (distinct (my-fun foo_one) (my-fun foo_two)))
(assert (distinct (my-fun foo_one) (my-fun foo_three)))
(assert (distinct (my-fun foo_two) (my-fun foo_three)))

(check-sat)
(get-model)
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-15&quot;&gt;&lt;a href=&quot;#rev-fn-15&quot;&gt;15&lt;&#x2F;a&gt; While I praise the SMT solver for being open-minded, polyamory is not within
the scope of this logic puzzle.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-16&quot;&gt;&lt;a href=&quot;#rev-fn-16&quot;&gt;16&lt;&#x2F;a&gt; By default an SMT solver will give you &lt;em&gt;one&lt;&#x2F;em&gt; answer. It is possible to
get &lt;em&gt;all&lt;&#x2F;em&gt; possible answers to an SMT query. Since a logic puzzle is
intended to have a single answer, ensuring that the number of unique answers is
1 would be another good consistency check at the cost of execution time. Unfortunately,
I don&#x27;t remember how to do this.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-17&quot;&gt;&lt;a href=&quot;#rev-fn-17&quot;&gt;17&lt;&#x2F;a&gt; The puzzle &lt;em&gt;should&lt;&#x2F;em&gt; have a unique answer if it was designed properly.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-18&quot;&gt;&lt;a href=&quot;#rev-fn-18&quot;&gt;18&lt;&#x2F;a&gt; I chose to write the &lt;code&gt;assert&lt;&#x2F;code&gt; in terms of &lt;code&gt;Int&lt;&#x2F;code&gt; and &lt;code&gt;+&lt;&#x2F;code&gt;
as an example. When numbers you can choose from in a category aren&#x27;t sequential,
and&#x2F;or the clues are more complicated, I think it&#x27;s useful to be able to model
clues with other theories the SMT solver provides.&lt;&#x2F;p&gt;

&lt;p&gt;In this case, because we know that &lt;code&gt;NumKittens&lt;&#x2F;code&gt; is constrained
from &lt;code&gt;1&lt;&#x2F;code&gt; to &lt;code&gt;3&lt;&#x2F;code&gt; inclusive, you could just as easily
derive the constraint as:

&lt;pre&gt;
(assert (= (QueenKittens Starbuck) 3))
(assert (= (QueenKittens (TomPartner Batman)) 1))
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
</content>
	</entry>
  

  

  

  
	<entry xml:lang="en">
		<title>A Tour Of ethflop</title>
		<published>2019-12-11T00:00:00+00:00</published>
		<updated>2022-04-29T00:00:00+00:00</updated>
		<link href="https://www.wdj-consulting.com/blog/ethflop/" type="text/html"/>
		<id>https://www.wdj-consulting.com/blog/ethflop/</id>
		<content type="html">&lt;h1 id=&quot;a-tour-of-ethflop&quot;&gt;A Tour Of ethflop&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;ethflop.sourceforge.net&quot;&gt;ethflop&lt;&#x2F;a&gt; is a new (2019!) &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Terminate_and_stay_resident_program&quot;&gt;Terminate and Stay
Resident&lt;&#x2F;a&gt;
(TSR) program for IBM PCs and compatibles running a DOS flavor. ethflop will
convert either your drive A:\ or B:\ floppy drive into a networked drive that
uses Ethernet frames to talk to a Linux server. The Linux server runs a daemon
called &lt;code&gt;ethflopd&lt;&#x2F;code&gt; that can supply floppy images in place of physical disks.&lt;&#x2F;p&gt;
&lt;p&gt;I first found out about ethflop from a &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;FreeDOS_Project&#x2F;status&#x2F;1199114211705675776&quot;&gt;retweet&lt;&#x2F;a&gt;
of the FreeDOS Project&#x27;s account. I knew then and there I &lt;em&gt;at least&lt;&#x2F;em&gt; had
to try this out. It&#x27;s like I was the target audience! And if you:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Have old hardware &amp;quot;optimized&amp;quot; for running DOS.&lt;&#x2F;li&gt;
&lt;li&gt;Enjoy (responsibly or otherwise!) networking your old computers to other old
and new computers.&lt;&#x2F;li&gt;
&lt;li&gt;Have a large number of floppy images that you downloaded over the years.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;then I think ethflop is worth looking at! I imagine a number of
vintage-oriented people who would be happy with having access to a bunch of
floppy images from their DOS machine &lt;em&gt;without needing to write them to physical
floppies!&lt;&#x2F;em&gt; So I&#x27;ve decided to take a look myself and write down my initial
experiences using ethflop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-hardware-setup-for-testing&quot;&gt;My Hardware Setup (For Testing)&lt;&#x2F;h2&gt;
&lt;p&gt;ethflop requires two machines to use: a Linux server which runs a daemon
(&lt;code&gt;ethflopd&lt;&#x2F;code&gt;) that has access to some floppy images, and a machine running DOS
which interfaces to the Linux daemon to access said floppy images (using the
TSR portion of &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt;). I present the hardware setup I used in the next
two sections for &lt;del&gt;showing off my PC AT&lt;&#x2F;del&gt; &lt;ins&gt;giving the rest of this
post context&lt;&#x2F;ins&gt;. Feel free to &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;ethflop&#x2F;#compiling-and-installing-ethflopd&quot;&gt;skip&lt;&#x2F;a&gt;
this section and refer back to it when necessary; it is mostly inessential.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-286-dos-setup&quot;&gt;My 286 (DOS) Setup&lt;&#x2F;h3&gt;
&lt;p&gt;My 286 is an early IBM PC AT running at 6 MHz. It has 2 floppy drives- drive A:\
is a 1.2MB High Density drive, and drive B:\ is a 360kB Double Density drive.
Additionally, my AT has one full height hard drive at drive C:\
(the original &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Computer_Memories_Inc.&quot;&gt;CMI&lt;&#x2F;a&gt;
hard drive), and one half-height hard drive at D:\ that sits rather snugly
in the slot under the 2 floppy drives. The C:\ drive is the boot drive and the
D:\ drive is used mainly for receiving temporary files that I subsequently
forget to clean up :).&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Picture of my IBM PC AT&quot; src=&quot;ctty.jpg&quot;&gt;
&lt;figcaption&gt;Picture of my IBM PC AT, showing both floppy drives. The screen includes some
DOS commands I ran which are relevant to this blog post, including unloading
the &lt;a href=&quot;https:&#x2F;&#x2F;shh.thathost.com&#x2F;pub-dos&#x2F;&quot;&gt;&lt;code&gt;DOSED&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
autocompleter (as it interferes with &lt;code&gt;CTTY&lt;&#x2F;code&gt;), loading a packet
driver, and then changing the console to a serial port via &lt;code&gt;CTTY&lt;&#x2F;code&gt;.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Because of &lt;!-- [the way](#extra-how-ethflop-works) --&gt; the way ethflop works, you lose access
to one of the floppy drives; I chose to use drive B:\ with ethflop for testing
purposes. The hard drives are unaffected, and I&#x27;ve yet to test whether you can
reliably use ethflop with a boot floppy. &lt;em&gt;If you do not have a physical drive B:\, you must use drive A:\ with ethflop.&lt;&#x2F;em&gt;
This is because DOS will emulate a virtual B:\ drive using drive A:\ if no
physical drive is detected, and no B:\ drive requests will ever be sent to
ethflop.&lt;&#x2F;p&gt;
&lt;p&gt;My Network Interface Card (NIC) is a 3Com 3c509, which is still a fairly common
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Industry_Standard_Architecture&quot;&gt;ISA&lt;&#x2F;a&gt; NIC that
can be even be found for sale off Ebay. 3Com still had packet drivers on their
website for the 3c509 when I was searching for them in late 2010, but they
pulled an Intel and they are long gone. Fortunately, the Internet Archive &lt;a href=&quot;http:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20060314185204&#x2F;http:&#x2F;&#x2F;support.3com.com&#x2F;infodeli&#x2F;tools&#x2F;nic&#x2F;3c509.htm&quot;&gt;comes through&lt;&#x2F;a&gt;
yet again!&lt;&#x2F;p&gt;
&lt;p&gt;Due to me misunderstanding the Ebay listing at the time, I bought a model of
3c509 which only has &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;10BASE2&quot;&gt;thinnet (10BASE2)&lt;&#x2F;a&gt;
and &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;10BASE5&quot;&gt;thicknet (10BASE5)&lt;&#x2F;a&gt; connections.
Fortunately, 10BASE2 to 10BASE-T converters are still common- if way too
expensive- and occassionally one can buy an old hub off Ebay with the same
hardware&lt;a id=&quot;rev-fn-1&quot; href=&quot;#fn-1&quot;&gt;&lt;sup&gt;1&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 as these converters. I&#x27;m not in a position to really
offer concrete advice on setting up 10BASE2 right now. The moral here is get
an ISA NIC with an RJ-45 connector, which is the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Ethernet_over_twisted_pair&quot;&gt;twisted pair (10BASE-T)&lt;&#x2F;a&gt;
Ethernet that you know and love.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;linux-setup&quot;&gt;Linux Setup&lt;&#x2F;h3&gt;
&lt;p&gt;I used my headless &lt;a href=&quot;https:&#x2F;&#x2F;www.asus.com&#x2F;us&#x2F;Single-Board-Computer&#x2F;Tinker-Board&#x2F;&quot;&gt;Tinker Board&lt;&#x2F;a&gt;
to run the Linux daemon. My distro is DietPi, though I imagine the distro
really doesn&#x27;t matter; the source to &lt;code&gt;ethflopd&lt;&#x2F;code&gt; is contained in a single C file
and I had no troubles compiling and running it. Notably, I&#x27;m only using the
Tinker Board&#x27;s wifi, and I can confirm &lt;em&gt;&lt;code&gt;ethflopd&lt;&#x2F;code&gt; will work if used with a
&lt;code&gt;wlan&lt;&#x2F;code&gt; interface.&lt;&#x2F;em&gt; I&#x27;m assuming my router takes care of converting from raw
Ethernet frames to 802.11 frames, because they are &lt;a href=&quot;https:&#x2F;&#x2F;dot11ap.wordpress.com&#x2F;ieee-802-11-frame-format-vs-ieee-802-3-frame-format&#x2F;&quot;&gt;not compatible&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;An SSH session with my Tinker Board.&quot; src=&quot;linux-img.png&quot;&gt;
&lt;figcaption&gt;This image shows a screen capture of an SSH session with the Tinker Board.
The listing shows I am in the floppy image directory and I have a few floppy
images prepared to share with the PC AT. I couldn&#x27;t think of anything better to
show off a running headless system :).&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;For ease of testing, I am interfacing to DOS using a serial port and &lt;code&gt;minicom&lt;&#x2F;code&gt; on
the Tinker Board. It is possible to redirect DOS input and output to a serial
port (&lt;code&gt;COM2&lt;&#x2F;code&gt; in my case) using the &lt;code&gt;CTTY&lt;&#x2F;code&gt; command. Using &lt;code&gt;minicom&lt;&#x2F;code&gt; makes it
&lt;em&gt;much&lt;&#x2F;em&gt; easier to capture DOS command output compared to taking photos. The
picture of my AT above was taken &lt;em&gt;just&lt;&#x2F;em&gt; after I invoked &lt;code&gt;CTTY&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compiling-and-installing&quot;&gt;Compiling and Installing&lt;&#x2F;h2&gt;
&lt;p&gt;Before using ethflop, one has to compile and install it first. I didn&#x27;t have
any issues with compiling and installing, but I&#x27;ve documented my experiences
on how to do it for others just in case.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;compiling-and-installing-ethflopd&quot;&gt;Compiling and Installing &lt;code&gt;ethflopd&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;To start, download the source distribution from the ethflop &lt;a href=&quot;http:&#x2F;&#x2F;ethflop.sourceforge.net&quot;&gt;website&lt;&#x2F;a&gt;
using &lt;code&gt;wget&lt;&#x2F;code&gt; or other means. I recommend downloading the source to a Linux
machine. Once you unzip (&lt;code&gt;unzip -d ethflop ethflop-20191003.zip&lt;&#x2F;code&gt;) the source
distribution, you will see a directory like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wjones@DietPi:~&amp;#x2F;src&amp;#x2F;ethflop$ ls -l
total 120
-rw-r--r-- 1 wjones wjones 41865 Oct  3 16:48 ethflop.asm
-rw-r--r-- 1 wjones wjones  3696 Oct  3 17:04 ethflop.com
-rwxr-xr-x 1 wjones wjones 18568 Nov 25 22:15 ethflopd
-rw-r--r-- 1 wjones wjones 38305 Oct  3 16:50 ethflopd.c
-rw-r--r-- 1 wjones wjones  6192 Oct  3 16:55 ethflop.txt
-rw-r--r-- 1 wjones wjones   803 Oct  3 17:02 Makefile
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; tsr is written in &lt;a href=&quot;https:&#x2F;&#x2F;nasm.us&quot;&gt;&lt;code&gt;nasm&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;-style assembly.
While &lt;code&gt;nasm&lt;&#x2F;code&gt; is provided by many Linux package repos, the author provided an
already-assembled &lt;code&gt;.COM&lt;&#x2F;code&gt; file for convenience. You will need to transfer this
file to your DOS machine.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;ethflopd&lt;&#x2F;code&gt; portion of ethflop needs to be compiled manually. Type &lt;code&gt;make&lt;&#x2F;code&gt;
and you will get a binary at &lt;code&gt;ethflopd&lt;&#x2F;code&gt;. Any recent Linux should be able to run
&lt;code&gt;ethflopd&lt;&#x2F;code&gt;. As of this writing (12-8-2019), I have not tested the daemon on
non-Linux systems.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s no install target, so you need to copy &lt;code&gt;ethflopd&lt;&#x2F;code&gt; to its final location
manually. In my case, I copied the file to my private bin directory:
&lt;code&gt;cp ethflopd ~&#x2F;.local&#x2F;bin&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, create a storage directory for your floppy images: &lt;code&gt;mkdir ~&#x2F;src&#x2F;img&lt;&#x2F;code&gt;.
This is the directory &lt;code&gt;ethflopd&lt;&#x2F;code&gt; will use to present virtual floppy disks to
the DOS machine. Optionally, you can fill this directory with your floppy
images now :).&lt;&#x2F;p&gt;
&lt;h4 id=&quot;optional-access-control&quot;&gt;Optional Access Control&lt;&#x2F;h4&gt;
&lt;p&gt;I recommend setting the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Setuid&quot;&gt;setuid&lt;&#x2F;a&gt;
bit as well so you can execute the binary without needing sudo. Since this is
likely a private binary for your use alone, on a multiuser system (where you
have &lt;code&gt;sudo&lt;&#x2F;code&gt; privileges) you probably dont want others running &lt;code&gt;ethflopd&lt;&#x2F;code&gt;
either. I believe the following commands accomplish both:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Change the owner and group of &lt;code&gt;ethflopd&lt;&#x2F;code&gt; to &lt;code&gt;root&lt;&#x2F;code&gt; and your user&#x27;s
&lt;a href=&quot;https:&#x2F;&#x2F;unix.stackexchange.com&#x2F;questions&#x2F;410367&#x2F;how-to-get-the-primary-group-of-a-user&quot;&gt;primary group&lt;&#x2F;a&gt;
respectively. As far as I know, the owner &lt;em&gt;must&lt;&#x2F;em&gt; be root so &lt;code&gt;ethflopd&lt;&#x2F;code&gt; can
create its runtime files and bind to an interface after &lt;code&gt;setuid&lt;&#x2F;code&gt; takes effect.
On the other hand, the group is your primary group, so you can still run the
executable.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;sudo chown root:wjones ~&amp;#x2F;.local&amp;#x2F;bin&amp;#x2F;ethflopd
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Set the &lt;code&gt;setuid&lt;&#x2F;code&gt; bit on &lt;code&gt;ethflopd&lt;&#x2F;code&gt;. Make sure the group owner has executable
permissions while the &amp;quot;others&amp;quot; group has the executable bit cleared. This
ensures that only you or root can run &lt;code&gt;ethflopd&lt;&#x2F;code&gt; &lt;em&gt;if that&#x27;s what you want.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;sudo chmod 4754 ~&amp;#x2F;.local&amp;#x2F;bin&amp;#x2F;ethflopd
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;installing-ethflop-com&quot;&gt;Installing &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;At a minimum, you will need to transfer &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; and a packet driver for
your particular NIC to your retro machine. If you can&#x27;t find a packet driver
for your card easily, &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;cr1901&quot;&gt;tweet&lt;&#x2F;a&gt; or
&lt;a href=&quot;mailto:&#x2F;&#x2F;thor0505@comcast.net&quot;&gt;email&lt;&#x2F;a&gt; me and I&#x27;ll see if I can help. I don&#x27;t
have a &lt;em&gt;large&lt;&#x2F;em&gt; collection of packet drivers myself (CNET, Kingston, 3Com), but
I can point you in the right direction.&lt;a id=&quot;rev-fn-2&quot; href=&quot;#fn-2&quot;&gt;&lt;sup&gt;2&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Once you have &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; and a packet driver on your retro machine, you&#x27;re
good to go! There is no special installation required; &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; is
self-contained. It&#x27;s probably a good idea to make sure &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; and your
packet driver can be seen by the DOS &lt;code&gt;PATH&lt;&#x2F;code&gt; though.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, one has to actually &lt;em&gt;get&lt;&#x2F;em&gt; &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; from the distribution you
unzipped on your Linux machine to the vintage machine in order to actually
&lt;em&gt;use&lt;&#x2F;em&gt; ethflop to its full potential. About that...&lt;&#x2F;p&gt;
&lt;h4 id=&quot;transferring-ethflop-com-to-the-target-machine&quot;&gt;&lt;em&gt;Transferring&lt;&#x2F;em&gt; &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; To The Target Machine&lt;&#x2F;h4&gt;
&lt;p&gt;Unfortunately, &lt;em&gt;getting&lt;&#x2F;em&gt; files from a modern machine to a retro DOS machine is
quite a topic in and of itself. There&#x27;s various ways to do this- serial port,
floppy, over the network, cdrom attached to the parallel port, zip disks, etc.
Depending on how much extra hardware you have, transferring a file from a new
to old machine ranges from fun to very tedious.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;my-preferred-transfer-method-tcp-ip&quot;&gt;My Preferred Transfer Method- TCP&#x2F;IP!&lt;&#x2F;h5&gt;
&lt;p&gt;It was fun for me personally to transfer &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; to an old machine
because I had set up TCP&#x2F;IP on my my PC AT years ago using &lt;a href=&quot;http:&#x2F;&#x2F;www.brutman.com&#x2F;mTCP&#x2F;&quot;&gt;mTCP&lt;&#x2F;a&gt;.
mTCP applications integrate the TCP&#x2F;IP stack into their binaries. The main
distribution provides an FTP client (&lt;code&gt;FTP&lt;&#x2F;code&gt;) and HTTP downloader (&lt;code&gt;HTGET&lt;&#x2F;code&gt;),
among other useful programs. I have also written &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cr1901&#x2F;nwbackup&quot;&gt;my own&lt;&#x2F;a&gt;
software using mTCP.&lt;&#x2F;p&gt;
&lt;p&gt;For transferring files from a new to old machine, I normally prefer using the
&lt;code&gt;FTP&lt;&#x2F;code&gt; client, but recently, I&#x27;ve had trouble setting up a temporary working
FTP server using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;giampaolo&#x2F;pyftpdlib&quot;&gt;&lt;code&gt;pyftpdlib&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
Specifically, the server claims to transfer the file, but mTCP &lt;code&gt;FTP&lt;&#x2F;code&gt; terminates
the transfer immediately. Instead I decided to use &lt;code&gt;python3&lt;&#x2F;code&gt;&#x27;s &lt;code&gt;http.server&lt;&#x2F;code&gt;
module and then download the file to my AT using &lt;code&gt;HTGET&lt;&#x2F;code&gt;. Specifically:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On the Linux&#x2F;modern side, invoke an HTTP server in the ethflop directory:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wjones@DietPi:~&amp;#x2F;src&amp;#x2F;ethflop$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http:&amp;#x2F;&amp;#x2F;0.0.0.0:8000&amp;#x2F;) ...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;On the DOS&#x2F;retro side, connect to the newly-invoked HTTP server and
download &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; using &lt;code&gt;HTGET&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;HTGET -o ETHFLOP.COM 192.168.1.162:8000&amp;#x2F;ethflop.com
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In less than a second, I had &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; safely on my AT! The power of TCP&#x2F;IP
and Ethernet still amazes me.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t remember the exact details of how I got mTCP onto my AT initially, but
as I recall I had to use a third computer with a zip drive and 5.25&amp;quot; floppy
drive as an intermediate. I used a USB Zip drive to copy files from my laptop.
The files were read by the intermediate computer&#x27;s &lt;em&gt;internal&lt;&#x2F;em&gt; Zip drive, and
then copied onto the intermediate&#x27;s 5.25&amp;quot; 1.2MB drive, which I then could write
onto the AT&#x27;s hard disk.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;bootstrapping&quot;&gt;Bootstrapping&lt;&#x2F;h5&gt;
&lt;p&gt;&lt;strong&gt;I apologize in advance for the length of this section. This needs to be spun
off into a blog post by itself. Feel free to &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;ethflop&#x2F;#usage&quot;&gt;skip&lt;&#x2F;a&gt; to the next
section.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In the absence of a working network connection, I understand using floppy disks
for data transfer to old systems is sometimes not an option. While a floppy
drive is pretty much the lowest common denominator of hardware
for IBM PCs, clones, and descendants, I&#x27;ve seen a few issues:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Getting blank 5.25&amp;quot; floppies is inconvenient in 2019.&lt;&#x2F;li&gt;
&lt;li&gt;Even after you get a 5.25&amp;quot; drive for a second machine, I don&#x27;t think any of
the modern USB 5.25&amp;quot; floppy drive interfaces support writing disks
(no demand?). So you need to setup up an intermediate machine as I described
in the previous section.&lt;&#x2F;li&gt;
&lt;li&gt;Occassionally, I&#x27;ve seen people with broken floppy drives who want to install
software onto their machine.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I think serial port is the method for transferring data between new and old
machines that makes the &lt;em&gt;least&lt;&#x2F;em&gt; amount of assumptions about available hardware.
A serial port in &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;D-subminiature&quot;&gt;DB-9 or DB-25&lt;&#x2F;a&gt;
form factor is pretty a common hardware addition to even the oldest IBM PC and
clones. Serial ports are traditionally accessed through the DOS command line by
the special names &lt;code&gt;COM[1-4]&lt;&#x2F;code&gt; or &lt;code&gt;AUX&lt;&#x2F;code&gt; (for COMmmunications or AUXilary). Modern
OSes have good support for DB-9 serial ports via &lt;a href=&quot;https:&#x2F;&#x2F;www.adafruit.com&#x2F;product&#x2F;18&quot;&gt;USB adapters&lt;&#x2F;a&gt;
or &lt;a href=&quot;https:&#x2F;&#x2F;www.asix.com.tw&#x2F;products.php?op=pItemdetail&amp;amp;PItemID=122;74;110&amp;amp;PLine=74&quot;&gt;PCI Express&lt;&#x2F;a&gt;
because RS-232 is still widely used for better or worse. You will need a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Null_modem&quot;&gt;null-modem&lt;&#x2F;a&gt;
cable or adapter to connect your modern and retro computer together; these are
still widely available to purchase.&lt;&#x2F;p&gt;
&lt;p&gt;There are a few &lt;a href=&quot;https:&#x2F;&#x2F;retrocomputing.stackexchange.com&#x2F;a&#x2F;9762&quot;&gt;bootstrap&lt;&#x2F;a&gt;
mechanisms that have been developed over the years for DOS. They mostly involve
typing in scripts (like BASIC) and&#x2F;or small exectuables whose &lt;a href=&quot;ftp:&#x2F;&#x2F;kermit.columbia.edu&#x2F;kermit&#x2F;a&#x2F;tcomtxt.asm&quot;&gt;opcodes&lt;&#x2F;a&gt;
or &lt;a href=&quot;http:&#x2F;&#x2F;www.columbia.edu&#x2F;kermit&#x2F;archive.html#boofile&quot;&gt;encoding&lt;&#x2F;a&gt; only use
ASCII characters. Because the bootstraps only create files with ASCII
characters, one can use the DOS &lt;code&gt;COPY&lt;&#x2F;code&gt;command to read bootstrap programs from
the serial port, such as &lt;code&gt;COPY COM1 XFER.COM&lt;&#x2F;code&gt;. The ASCII restriction is because
DOS uses the &lt;code&gt;EOF&lt;&#x2F;code&gt; ASCII character (&lt;code&gt;^Z&lt;&#x2F;code&gt;) to know when the transfer is
finished.&lt;a id=&quot;rev-fn-3&quot; href=&quot;#fn-3&quot;&gt;&lt;sup&gt;3&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Bootstrap programs often implement a minimal &lt;a href=&quot;http:&#x2F;&#x2F;www.columbia.edu&#x2F;kermit&#x2F;kermit.html&quot;&gt;Kermit protocol&lt;&#x2F;a&gt;
receiver program. Unlike the DOS &lt;code&gt;COPY&lt;&#x2F;code&gt; command, the Kermit protocol can send
and receive files with arbitrary bytes, and the protocol was designed to be
extremely portable to new machines. The Kermit Project took the bootstrap
problem of &amp;quot;transferring the file-transfer program to a new computer&amp;quot;
seriously; all the bootstrap links I provided in this section are part of the
Kermit archive at Columbia.&lt;&#x2F;p&gt;
&lt;p&gt;I use either &lt;code&gt;minicom&lt;&#x2F;code&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;ttssh2.osdn.jp&#x2F;index.html.en&quot;&gt;Tera Term&lt;&#x2F;a&gt; as
my serial transfer programs on machines with modern OSes. Both of these
programs understand the Kermit protocol &lt;em&gt;and&lt;&#x2F;em&gt; can be used to send a file down
the serial line as-is for the DOS &lt;code&gt;COPY&lt;&#x2F;code&gt; command. However, &lt;em&gt;you must manually
send the &lt;code&gt;EOF&lt;&#x2F;code&gt; (&lt;code&gt;^Z&lt;&#x2F;code&gt;) control character yourself afterwards in both programs.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I can vouch personally that &lt;a href=&quot;ftp:&#x2F;&#x2F;kermit.columbia.edu&#x2F;kermit&#x2F;a&#x2F;msbrcv.bas&quot;&gt;&lt;code&gt;msbrcv.bas&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
has worked for me in the past with &lt;code&gt;QBASIC&lt;&#x2F;code&gt;. I have not tested in older DOS
versions using &lt;code&gt;BASIC&lt;&#x2F;code&gt; or &lt;code&gt;BASICA&lt;&#x2F;code&gt; as of this writing (12-8-2017). I plan to
test transfers with &lt;a href=&quot;ftp:&#x2F;&#x2F;kermit.columbia.edu&#x2F;kermit&#x2F;a&#x2F;tcomtxt.asm&quot;&gt;&lt;code&gt;TCOM.COM&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
in the near future, which may be preferable since it doesn&#x27;t require BASIC.
Really, I should probably write a blog post on bootstrapping and move this
whole section there :).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;starting-and-stopping-ethflopd&quot;&gt;Starting and Stopping &lt;code&gt;ethflopd&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;To start the daemon, supply the following command line arguments in order:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The network interface you wish to listen on (&lt;code&gt;eth0&lt;&#x2F;code&gt;, &lt;code&gt;wlan0&lt;&#x2F;code&gt;- &lt;em&gt;wireless interfaces work&lt;&#x2F;em&gt;, etc.) .&lt;&#x2F;li&gt;
&lt;li&gt;The storage directory of your floppy images.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;My invocation looks like: &lt;code&gt;ethflopd wlan0 ~&#x2F;src&#x2F;img&#x2F;&lt;&#x2F;code&gt;. There is an optional
&lt;code&gt;-f&lt;&#x2F;code&gt; option to run the daemon in the foreground as well. I find this useful if
you wish to invoke &lt;code&gt;ethflopd&lt;&#x2F;code&gt; for as long as you&#x27;re running your DOS machine,
as opposed to running &lt;code&gt;ethflopd&lt;&#x2F;code&gt; continuously.&lt;&#x2F;p&gt;
&lt;p&gt;Once &lt;code&gt;ethflopd&lt;&#x2F;code&gt; is running, it will listen for the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;MAC_address&quot;&gt;MAC address&lt;&#x2F;a&gt;
of a networked DOS computer running &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; to send and receive virtual
floppy images. From the DOS side, detecting the MAC address of a machine
running &lt;code&gt;ethflopd&lt;&#x2F;code&gt; is automatic. To reiterate, there is no IP address setup
because ethflop uses raw Ethernet&#x2F;Wifi frames to transfer data.&lt;&#x2F;p&gt;
&lt;p&gt;To stop the daemon, you need to &lt;code&gt;kill&lt;&#x2F;code&gt; the PID associated with &lt;code&gt;ethflopd&lt;&#x2F;code&gt; or
run &lt;code&gt;pkill ethflopd&lt;&#x2F;code&gt;. &lt;code&gt;sudo&lt;&#x2F;code&gt; may be required depending on your particular
setup. If &lt;code&gt;ethflop&lt;&#x2F;code&gt; is running in the foreground, &lt;code&gt;^C&lt;&#x2F;code&gt; works fine as
well :); &lt;code&gt;ethflopd&lt;&#x2F;code&gt; has the same cleanup code for both &lt;code&gt;kill&lt;&#x2F;code&gt; and &lt;code&gt;^C&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-guided-tour-of-ethflop-com&quot;&gt;A Guided Tour Of &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Once the daemon is running on the Linux machine, the first thing to do on the
DOS end is... read the help text :). I have copied the output of running
ethflop without any arguments for your convenience!&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop
ethflop v0.6 - a floppy drive emulator over Ethernet
Copyright (C) 2019 Mateusz Viste

=== USAGE ====================================================================
ethflop a           installs the ethflop TSR as A:
ethflop b           installs ethflop as B: (works only if you have a real B:)
ethflop i DISKNAME  &amp;#x27;inserts&amp;#x27; the virtual floppy named &amp;#x27;DISKNAME&amp;#x27;
ethflop ip DSKNAME  same as &amp;#x27;i&amp;#x27;, but the inserted floppy is WRITE PROTECTED
ethflop r OLD NEW   renames virt. floppy &amp;#x27;OLD&amp;#x27; to &amp;#x27;NEW&amp;#x27;
ethflop e           &amp;#x27;ejects&amp;#x27; currently loaded virtual floppy
ethflop nSZ DSKNAME creates a new virt. floppy DSKNAME, SZ KB big. SZ can be:
                    360, 720, 1200, 1440, 2880, 4800, 8100, 9600, 15500, 31000
ethflop l           displays the list of available virt. floppies
ethflop d DISKNAME  DELETES virt. floppy named DISKNAME - BE CAREFUL!
ethflop s           displays current status of the ethflop TSR
ethflop u           unloads the ethflop TSR

=== NOTES ====================================================================
 * Disk names must be 1 to 8 characters long. Only A-Z, 0-9 and &amp;#x27;_-&amp;#x27; allowed.
 * ethflop requires the presence of an ethflop server on the local network.

=== LICENSE ==================================================================
ethflop is published under the terms of the ISC license. See ETHFLOP.TXT.
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;ethflop can basically be divided into three command types:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Load&#x2F;unload TSR (&lt;code&gt;a&lt;&#x2F;code&gt;, &lt;code&gt;b&lt;&#x2F;code&gt;, &lt;code&gt;u&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Query commands (&lt;code&gt;l&lt;&#x2F;code&gt;, &lt;code&gt;s&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Operate on virtual disks (&lt;code&gt;i&lt;&#x2F;code&gt;, &lt;code&gt;ip&lt;&#x2F;code&gt;, &lt;code&gt;r&lt;&#x2F;code&gt;, &lt;code&gt;e&lt;&#x2F;code&gt;, &lt;code&gt;n&lt;&#x2F;code&gt;, &lt;code&gt;d&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;loading-unloading-the-tsr&quot;&gt;Loading&#x2F;Unloading the TSR&lt;&#x2F;h4&gt;
&lt;h5 id=&quot;packet-driver-installation&quot;&gt;Packet Driver Installation&lt;&#x2F;h5&gt;
&lt;p&gt;The &lt;del&gt;first&lt;&#x2F;del&gt; &lt;ins&gt;second&lt;&#x2F;ins&gt; thing to do is to make sure your packet
driver is loaded into memory. In my case with the 3c509, this looks like:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;3c5x9pd 0x60

3Com EtherLink 10 ISA Packet Driver v1.2
(C) Copyright 1993 3Com Corp.  All rights reserved.
Error: The interrupt requested is already in use by another packet driver.
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The lone argument to &lt;code&gt;3C5X9PD.COM&lt;&#x2F;code&gt; is the x86 interrupt number to use to
call into the packet driver. Interrupt &lt;code&gt;0x60&lt;&#x2F;code&gt; is a good traditional default, as
indicated by the error message telling me I already loaded the driver.&lt;&#x2F;p&gt;
&lt;p&gt;Some packet drivers require more options to set up, but chances are the
default options provided in your NIC manual or by the supplied configuration
program- as is the case with the 3c509- will work correctly. Older machines may
require a bit more fine tuning, but between three different NIC manufactuters
over a dozen DOS machines, I&#x27;ve had the default options not work exactly
once&lt;a id=&quot;rev-fn-4&quot; href=&quot;#fn-4&quot;&gt;&lt;sup&gt;4&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
. Manually assigning resources becomes more important if you
have multiple NICs installed in a retro machine, but this is rare.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;tsr-installation&quot;&gt;TSR Installation&lt;&#x2F;h5&gt;
&lt;p&gt;After you&#x27;re sure a packet driver is loaded, then it&#x27;s time to load the TSR! As
I said before, if you only have a single physical floppy drive, you must use
&lt;code&gt;ethflop a&lt;&#x2F;code&gt;. However, seeing as I &lt;em&gt;do&lt;&#x2F;em&gt; have a physical B:\ drive, I installed
ethflop at drive B:\:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop b
server found at F0:03:8C:90:B2:A1
current virt. floppy: &amp;lt;NONE&amp;gt;
ethflop has been installed
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; will attempt to &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Broadcast_address#Ethernet&quot;&gt;automatically find&lt;&#x2F;a&gt;
a server running &lt;code&gt;ethflopd&lt;&#x2F;code&gt;  without user invervention. It &lt;em&gt;can&lt;&#x2F;em&gt; take multiple
tries to load the driver depending on your network connection quality. However,
I&#x27;ve found that once &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; &lt;em&gt;finds&lt;&#x2F;em&gt; the server, any subsequent network
errors are transient and the TSR keeps working.&lt;&#x2F;p&gt;
&lt;p&gt;Once ethflop is installed, you lose access to the underlying physical drive.
&lt;em&gt;This also means that you can&#x27;t use ethflop if you don&#x27;t have a physical floppy
drive.&lt;&#x2F;em&gt;&lt;a id=&quot;rev-fn-5&quot; href=&quot;#fn-5&quot;&gt;&lt;sup&gt;5&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 The virtual floppy drive presented by ethflop will be
empty; you must either load or create a virtual disk before accessing the
drive, or else you will get DOS&#x27; famous error message:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;B:


Seek error reading drive B
Abort, Retry, Fail?f
Current drive is no longer valid&amp;gt;C:

C:\&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I have not tested whether ethflop can coexist with applications using a DOS
TCP&#x2F;IP stack, though I can&#x27;t see any immediate problems.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;unloading-ethflop-com&quot;&gt;Unloading &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt;&lt;&#x2F;h5&gt;
&lt;p&gt;When you&#x27;re done playing with virtual floppies and&#x2F;or need to access the
physical drive, type &lt;code&gt;ethflop u&lt;&#x2F;code&gt; to uninstall the TSR:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop u
ethflop has been uninstalled
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You will get access back to your underlying physical floppy drive as well as
the NIC after unloading.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;querying-commands&quot;&gt;Querying commands&lt;&#x2F;h4&gt;
&lt;p&gt;You can get information about the ethflop TSR and what disks are available by
running &lt;code&gt;ethflop s&lt;&#x2F;code&gt; and &lt;code&gt;ethflop l&lt;&#x2F;code&gt; respectively:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop s
ethflop is currently installed as drive B:
server is at F0:03:8C:90:B2:A1
current virt. floppy: &amp;lt;NONE&amp;gt;
C:\&amp;gt;ethflop l
 msdos213    mydisk
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Uh oh, one of my virtual disks is missing from the list! As it turns out, &lt;em&gt;the
file extension for your images matter&lt;&#x2F;em&gt;! I&#x27;ve been sloppy about this in the
past, switching between &lt;code&gt;.ima&lt;&#x2F;code&gt; or &lt;code&gt;.img&lt;&#x2F;code&gt; on a whim. Renaming &lt;code&gt;mtcpget.ima&lt;&#x2F;code&gt; to
&lt;code&gt;mtcpget.img&lt;&#x2F;code&gt; in my image directory on the Tinker Board makes all three images
appear during a query:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop l
 msdos213   mtcpget    mydisk
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Much better!&lt;&#x2F;p&gt;
&lt;h4 id=&quot;using-virtual-disks&quot;&gt;Using Virtual Disks&lt;&#x2F;h4&gt;
&lt;p&gt;Commands to operate on virtual disks are the bulk of &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt;&#x27;s
functionality. Any typical action you would perform on a physical floppy disk
has an equivalent coded into &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i&lt;&#x2F;code&gt;- Insert a virtual disk (place a disk into the disk drive).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;e&lt;&#x2F;code&gt;- Eject a virtual disk (remove the disk from the disk drive).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ip&lt;&#x2F;code&gt;- Write-protect and insert a virtual disk (cover a notch or slide a
switch on the disk, then insert).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;r&lt;&#x2F;code&gt;- Rename a virtual disk (change the label on the sleeve&#x2F;cover).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;n&lt;&#x2F;code&gt;- Create a new virtual disk (go to the store and buy some floppies).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;d&lt;&#x2F;code&gt;- Delete a virutal disk (erase a good floppy or discard a bad floppy).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I present these commands in the order I tried them out in my initial ETHFLOP
session, which matches the list above.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;insert&quot;&gt;Insert&lt;&#x2F;h5&gt;
&lt;p&gt;Let&#x27;s insert a disk! I chose &lt;code&gt;mtcpget&lt;&#x2F;code&gt; because I actually forgot the contents
of this image and wanted to see what I used it for. Listing the root directory
using &lt;code&gt;DIR&lt;&#x2F;code&gt; works without a hitch:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop i mtcpget
Disk MTCPGET loaded (360 KiB)
C:\&amp;gt;B:

B:\&amp;gt;dir

 Volume in drive B has no label
 Volume Serial Number is AEA9-1EC3
 Directory of B:\

MTCPGET  BAT       870 01-01-80   3:34p
PKT_DRVS     &amp;lt;DIR&amp;gt;     05-16-12   1:52p
UTILS        &amp;lt;DIR&amp;gt;     05-16-12   1:53p
CFG          &amp;lt;DIR&amp;gt;     05-16-12   1:53p
BATCH        &amp;lt;DIR&amp;gt;     05-16-12   2:15p
GO       BAT         0 05-16-12   2:15p
        6 file(s)        870 bytes
                      101376 bytes free

B:\&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Looks like I used this disk to bootstrap mTCP onto new machines. Displaying
the contents of the &lt;code&gt;MTCPGET.BAT&lt;&#x2F;code&gt; file using &lt;code&gt;TYPE&lt;&#x2F;code&gt; confirms this. Good to
know that accessing files works fine as well.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;B:\&amp;gt;type MTCPGET.BAT
REM ECHO OFF
ECHO This batch file retrieves the mTCP stack and programs using mTCP FTP.
ECHO Make sure environment is unset and batch variables are set correctly before using this file.
ECHO In addition, make sure the packet driver is functional.
PAUSE

SET SRCDRIV=A:
SET PATH=%SRCDRIV%\UTILS
SET DESTDRIV=C:
SET MACHINE=8088
SET MTCPCFG=%SRCDRIV%\CFG\BATCHTCP.CFG
SET SERVER=192.168.1.3

ECHO Switching to destination drive...
%DESTDRIV%

ECHO Obtaining DHCP configuration...
DHCP

ECHO Obtaining mTCP stack&amp;#x2F;programs...
FTP %SERVER% &amp;lt; %SRCDRIV%\CFG\MTCP.FTP

ECHO Obtaining machine-specific files...
FTP %SERVER% &amp;lt; %SRCDRIV%\CFG\%MACHINE%.FTP

ECHO Switching back to source drive...
%SRCDRIV%

ECHO This batch file has completed executing.
ECHO It is recommended to restart the computer at this time to restore the environment.
PAUSE
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h5 id=&quot;eject&quot;&gt;Eject&lt;&#x2F;h5&gt;
&lt;p&gt;I didn&#x27;t have much to do with &lt;code&gt;MTCPGET&lt;&#x2F;code&gt;, so I soon ejected this disk. I tested
that this worked by deliberately accessing a non-existent disk. I am curious
that &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Abort,_Retry,_Fail%3F#Description&quot;&gt;&lt;code&gt;f&lt;&#x2F;code&gt;ailing&lt;&#x2F;a&gt;
on the first error successfully read out a blank volume label, but even with
that behavior, the eject command works fine to me:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;B:\&amp;gt;ethflop e
Disk MTCPGET ejected
B:\&amp;gt;dir


Seek error reading drive B
Abort, Retry, Fail?f
Volume in drive B has no label

Seek error reading drive B
Abort, Retry, Fail?f

Not ready reading drive B
Abort, Retry, Fail?f
Fail on INT 24


Seek error reading drive B
Abort, Retry, Fail?f
Current drive is no longer valid&amp;gt;C:
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h5 id=&quot;write-protect-then-insert&quot;&gt;Write Protect, Then Insert&lt;&#x2F;h5&gt;
&lt;p&gt;Because I live life on the edge, I decided to test whether write-protect works
on one of my pristine, old MS-DOS images. If this fails, then I&#x27;ve lost...
pretty much nothing, because this is a backup copy of the image :). I don&#x27;t
know why the volume label is missing 3 hex digits- I will investigate and
update later.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop ip msdos213
Disk MSDOS213 loaded (360 KiB (write-protected))
C:\&amp;gt;B:

B:\&amp;gt;mkdir FOO

Write protect error writing drive B
Abort, Retry, Fail?f
Fail on INT 24 - FOO

B:\&amp;gt;dir

 Volume in drive B is E107-A
 Directory of B:\

BIN          &amp;lt;DIR&amp;gt;     08-27-11  12:51p
COMMAND  COM     16421 07-16-84   9:50a
ALTCHAR  SYS       431 04-04-84   8:22a
AUTOEXEC BAT        23 04-04-84   8:11a
        4 file(s)      16875 bytes
                       75776 bytes free

B:\&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since write-protected succeeded according to the error, it looks like I saved
myself an extra &lt;code&gt;rsync&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;cp&lt;&#x2F;code&gt; command to reset my MS-DOS image. And based on
the modification date of &lt;code&gt;BIN&lt;&#x2F;code&gt;, this image isn&#x27;t pristine&#x2F;original at all!
Ah well...&lt;&#x2F;p&gt;
&lt;h5 id=&quot;rename&quot;&gt;Rename&lt;&#x2F;h5&gt;
&lt;p&gt;Renaming a virtual disk has interesting behavior- the rename operation appears
to be case insensitive, even on the Linux side! I expected the image filename
on the server to be renamed with the same case that I typed it at the DOS
prompt:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;B:\&amp;gt;ethflop r mydisk MYDBAK
Disk MYDISK renamed to MYDBAK
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, it looks like either &lt;code&gt;ethflopd&lt;&#x2F;code&gt; or &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; sanitizes the image
name so that only lowercase image filenames are created on the server side:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wjones@DietPi:~&amp;#x2F;src&amp;#x2F;img$ ls
msdos213.img  mtcpget.img  mydbak.img
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I did not try renaming a virtual disk that&#x27;s inserted. I will update this
post in the future with the behavior when I have time to test.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;MYDISK&lt;&#x2F;code&gt; was an image I had created in a prior ethflop session. I renamed it to
&lt;code&gt;MYDBAK&lt;&#x2F;code&gt; (&lt;code&gt;BAK&lt;&#x2F;code&gt; as in &amp;quot;backup&amp;quot;) because I want to duplicate the process of
creating &lt;code&gt;MYDISK&lt;&#x2F;code&gt; with a fresh new image &lt;em&gt;for reasons I will describe in the
next section&lt;&#x2F;em&gt;. Speaking of which...&lt;&#x2F;p&gt;
&lt;h5 id=&quot;create-an-image&quot;&gt;Create An Image&lt;&#x2F;h5&gt;
&lt;p&gt;The most powerful feature of ethflop in my opinion is the ability to create
new virtual disks to store on the server. Compared to copying floppies,
creating and populating a virtual disk is much more convenient to quickly share
a new disk with your other networked computers. You can even store your shiny
new image image on an FTP server or BBS (those things still exist, right?) to
share with other like-minded retro-enthusiasts in record time!&lt;&#x2F;p&gt;
&lt;p&gt;To create an image, use the &lt;code&gt;n&lt;&#x2F;code&gt; command and supply a size and disk name as
arguments:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;B:\&amp;gt;ethflop n360 mydisk
Disk MYDISK created (360 KiB)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Listing the image directory contents shows that I indeed create a new floppy
image. Note that the image owner and group match those of the user invoking
&lt;code&gt;ethflopd&lt;&#x2F;code&gt; (after &lt;code&gt;setuid&lt;&#x2F;code&gt; takes effect). The permissions of &lt;code&gt;mydbak.img&lt;&#x2F;code&gt; in
the listing were from a previous &lt;code&gt;ethflopd&lt;&#x2F;code&gt; session using &lt;code&gt;sudo&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wjones@DietPi:~&amp;#x2F;src&amp;#x2F;img$ ls -l
total 848
-rw-r--r-- 1 wjones wjones 368640 Sep 22  2012 msdos213.img
-rw-r--r-- 1 wjones wjones 368640 May 16  2012 mtcpget.img
-rw-r--r-- 1 root   root   368640 Dec  4 13:53 mydbak.img
-rw-r--r-- 1 root   wjones 368640 Dec  5 13:19 mydisk.img
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After creating an image, you need to insert your shiny new disk before using
it. Based on the &lt;code&gt;Seek error&lt;&#x2F;code&gt; messages, I was having network trouble at the
time. However, I &lt;em&gt;did&lt;&#x2F;em&gt; eventually manage to connect to the server:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;B:\&amp;gt;ethflop i mydisk
ERROR: you must first eject your current virtual floppy (MSDOS213)
B:\&amp;gt;ethflop e
Disk MSDOS213 ejected
B:\&amp;gt;ethflop i mydisk

Seek error reading drive B
Abort, Retry, Fail?r

Seek error reading drive B
Abort, Retry, Fail?r

Seek error reading drive B
Abort, Retry, Fail?r

Seek error reading drive B
Abort, Retry, Fail?f
Invalid drive specification
Disk MYDISK loaded (360 KiB)
B:\&amp;gt;dir

 Volume in drive B has no label
 Volume Serial Number is 5DE9-4A1E
 Directory of B:\

File not found

B:\&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; connected to the server, I didn&#x27;t have any further trouble.&lt;&#x2F;p&gt;
&lt;p&gt;On a lark, I decided to try to recreate my existing &lt;code&gt;MYDBAK&lt;&#x2F;code&gt; virtual disk,
including the &lt;code&gt;TEST&lt;&#x2F;code&gt; directory and &lt;code&gt;TEST.TXT&lt;&#x2F;code&gt; file. I was curious what bytes,
if any, would be different between the two images beyond timestamps.
Unfortunately, I made a mistake and accidentally quoted the contents of
&lt;code&gt;TEST.TXT&lt;&#x2F;code&gt;. My mistake will come up &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;ethflop&#x2F;#unexpected-directory-entries&quot;&gt;again&lt;&#x2F;a&gt; a
bit later.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;B:\&amp;gt;mkdir TEST

Not ready writing drive B
Abort, Retry, Fail?r

B:\&amp;gt;cd TEST

B:\TEST&amp;gt;echo &amp;quot;This is a test file&amp;quot; &amp;gt; TEST.TXT

B:\TEST&amp;gt;ethflop e
Disk MYDISK ejected
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h6 id=&quot;unsupported-hardware-floppy-density-emulation&quot;&gt;Unsupported Hardware Floppy Density Emulation&lt;&#x2F;h6&gt;
&lt;p&gt;You can even create images of floppy densities that the hardware doesn&#x27;t
support! In the below snippet, I create a 1.44MB 3.5&amp;quot; virtual disk and populate
it much the same way as the previous examples, minus the quotes this time :):&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop n1440 hddisk
Disk HDDISK created (1440 KiB)
C:\&amp;gt;ethflop i hddisk
Disk HDDISK loaded (1440 KiB)
C:\&amp;gt;B:

B:\&amp;gt;mkdir TEST

B:\&amp;gt;dir

 Volume in drive B has no label
 Volume Serial Number is 5DE9-4BEB
 Directory of B:\

TEST         &amp;lt;DIR&amp;gt;     12-05-19   1:27p
        1 file(s)          0 bytes
                     1457152 bytes free

B:\TEST&amp;gt;echo This is a test file &amp;gt; TEST.TXT

B:\TEST&amp;gt;type TEST.TXT
This is a test file

B:\TEST&amp;gt;ethflop e
Disk HDDISK ejected
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By doing a hexdump on the server (&lt;code&gt;alias hd=&#x27;od -Ax -t x1z&#x27;&lt;&#x2F;code&gt;), you can see the
image indeed is the size of a 1.44MB disk, and includes the file I just created
starting at &lt;code&gt;004400&lt;&#x2F;code&gt;. File metadata- known in FAT file system terms as the
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Design_of_the_FAT_file_system#Directory_entry&quot;&gt;directory entry&lt;&#x2F;a&gt;-
is also visible starting at &lt;code&gt;004240&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wjones@DietPi:~&amp;#x2F;src&amp;#x2F;img$ hd hddisk.img
000000 eb fe 90 4d 53 44 4f 53 35 2e 30 00 02 01 01 00  &amp;gt;...MSDOS5.0.....&amp;lt;
000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00  &amp;gt;...@............&amp;lt;
000020 00 00 00 00 00 00 29 eb 4b e9 5d 4e 4f 20 4e 41  &amp;gt;......).K.]NO NA&amp;lt;
000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 00 00  &amp;gt;ME    FAT12   ..&amp;lt;
000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
0001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa  &amp;gt;..............U.&amp;lt;
000200 f0 ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
001400 f0 ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
001410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
002600 54 45 53 54 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;TEST       .....&amp;lt;
002610 00 00 00 00 00 00 62 6b 85 4f 02 00 00 00 00 00  &amp;gt;......bk.O......&amp;lt;
002620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
004200 2e 20 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;.          .....&amp;lt;
004210 00 00 00 00 00 00 62 6b 85 4f 02 00 00 00 00 00  &amp;gt;......bk.O......&amp;lt;
004220 2e 2e 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;..         .....&amp;lt;
004230 00 00 00 00 00 00 62 6b 85 4f 00 00 00 00 00 00  &amp;gt;......bk.O......&amp;lt;
004240 54 45 53 54 20 20 20 20 54 58 54 20 00 00 00 00  &amp;gt;TEST    TXT ....&amp;lt;
004250 00 00 00 00 00 00 aa 6b 85 4f 03 00 16 00 00 00  &amp;gt;.......k.O......&amp;lt;
004260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
004400 54 68 69 73 20 69 73 20 61 20 74 65 73 74 20 66  &amp;gt;This is a test f&amp;lt;
004410 69 6c 65 20 0d 0a 00 60 a9 16 eb 00 02 2f 00 00  &amp;gt;ile ...`.....&amp;#x2F;..&amp;lt;
004420 46 44 49 53 4b 20 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;FDISK   COM.....&amp;lt;
004430 00 00 00 00 00 00 00 60 a9 16 f1 00 d8 df 00 00  &amp;gt;.......`........&amp;lt;
004440 46 4f 52 4d 41 54 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;FORMAT  COM.....&amp;lt;
004450 00 00 00 00 00 00 00 60 a9 16 0d 01 8f 80 00 00  &amp;gt;.......`........&amp;lt;
004460 4d 49 52 52 4f 52 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;MIRROR  COM.....&amp;lt;
004470 00 00 00 00 00 00 00 60 a9 16 1e 01 f9 46 00 00  &amp;gt;.......`.....F..&amp;lt;
004480 33 43 35 58 39 50 44 20 43 4f 4d 00 00 00 00 00  &amp;gt;3C5X9PD COM.....&amp;lt;
004490 00 00 00 00 00 00 ac 80 85 26 89 13 bc 31 00 00  &amp;gt;.........&amp;amp;...1..&amp;lt;
0044a0 53 48 41 52 45 20 20 20 45 58 45 00 00 00 00 00  &amp;gt;SHARE   EXE.....&amp;lt;
0044b0 00 00 00 00 00 00 00 60 a9 16 2a 01 90 2a 00 00  &amp;gt;.......`..*..*..&amp;lt;
0044c0 53 4d 41 52 54 44 52 56 53 59 53 00 00 00 00 00  &amp;gt;SMARTDRVSYS.....&amp;lt;
0044d0 00 00 00 00 00 00 00 60 a9 16 30 01 83 20 00 00  &amp;gt;.......`..0.. ..&amp;lt;
0044e0 53 59 53 20 20 20 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;SYS     COM.....&amp;lt;
0044f0 00 00 00 00 00 00 00 60 a9 16 35 01 70 34 00 00  &amp;gt;.......`..5.p4..&amp;lt;
004500 55 4e 44 45 4c 45 54 45 45 58 45 00 00 00 00 00  &amp;gt;UNDELETEEXE.....&amp;lt;
004510 00 00 00 00 00 00 00 60 a9 16 3c 01 64 36 00 00  &amp;gt;.......`..&amp;lt;.d6..&amp;lt;
004520 55 4e 46 4f 52 4d 41 54 43 4f 4d 00 00 00 00 00  &amp;gt;UNFORMATCOM.....&amp;lt;
004530 00 00 00 00 00 00 00 60 a9 16 43 01 90 48 00 00  &amp;gt;.......`..C..H..&amp;lt;
004540 58 43 4f 50 59 20 20 20 45 58 45 00 00 00 00 00  &amp;gt;XCOPY   EXE.....&amp;lt;
004550 00 00 00 00 00 00 00 60 a9 16 4d 01 bc 3d 00 00  &amp;gt;.......`..M..=..&amp;lt;
004560 44 4f 53 4b 45 59 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;DOSKEY  COM.....&amp;lt;
004570 00 00 00 00 00 00 00 60 a9 16 55 01 fc 16 00 00  &amp;gt;.......`..U.....&amp;lt;
004580 44 4f 53 53 48 45 4c 4c 56 49 44 00 00 00 00 00  &amp;gt;DOSSHELLVID.....&amp;lt;
004590 00 00 00 00 00 00 00 60 a9 16 58 01 f6 24 00 00  &amp;gt;.......`..X..$..&amp;lt;
0045a0 54 43 50 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;TCP        .....&amp;lt;
0045b0 00 00 00 00 00 00 ee 10 24 c8 aa 04 00 00 00 00  &amp;gt;........$.......&amp;lt;
0045c0 44 4f 53 53 48 45 4c 4c 43 4f 4d 00 00 00 00 00  &amp;gt;DOSSHELLCOM.....&amp;lt;
0045d0 00 00 00 00 00 00 00 60 a9 16 63 01 02 12 00 00  &amp;gt;.......`..c.....&amp;lt;
0045e0 44 4f 53 53 48 45 4c 4c 45 58 45 00 00 00 00 00  &amp;gt;DOSSHELLEXE.....&amp;lt;
0045f0 00 00 00 00 00 00 00 60 a9 16 66 01 0e 98 03 00  &amp;gt;.......`..f.....&amp;lt;
004600 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
168000
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I assume the density of floppy images you can create and write depends
on the DOS version you&#x27;re running. My 286 runs PC-DOS 5.0, which supports
1.44MB disks just fine, but I know my 286 BIOS does &lt;em&gt;not&lt;&#x2F;em&gt; support 1.44MB
disks. I don&#x27;t know at present how &lt;code&gt;ETHFLOP.COM&lt;&#x2F;code&gt; interacts with the BIOS
bookkeeping, if at all. &lt;strong&gt;Until I know for sure, I am using this feature with
caution.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In the above dump of &lt;code&gt;hddisk.img&lt;&#x2F;code&gt;, the data starting at &lt;code&gt;FDISK&lt;&#x2F;code&gt; and beyond
appear to be legitimate directory entries from my AT&#x27;s hard disk. &lt;em&gt;I am not
sure why &lt;code&gt;ethflopd&lt;&#x2F;code&gt; is creating these entries.&lt;&#x2F;em&gt; My guess is this behavior isn&#x27;t
intentional, as none of these directory entries are actually reachable; bytes
&lt;code&gt;002620&lt;&#x2F;code&gt; and &lt;code&gt;004260&lt;&#x2F;code&gt; would need to be &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Design_of_the_FAT_file_system#Directory_entry&quot;&gt;nonzero&lt;&#x2F;a&gt;
to indicate more files are present in either the root or &lt;code&gt;TEST&lt;&#x2F;code&gt; directories.&lt;&#x2F;p&gt;
&lt;p&gt;Seeing as I&#x27;m not storing sensitive data on my AT, I&#x27;m not exactly worried
about extra data being copied to my newly-created virtual disks. However, I
found the behavior strange enough to be worth noting. If you&#x27;re a retro
enthusiast who stores sensitive data on their retro machines (&lt;em&gt;I&#x27;m&lt;&#x2F;em&gt; not gonna
judge you), I would check to make sure newly-created virtual images don&#x27;t
accidentally copy sensitive data to the server. In the meantime, I&#x27;ll look
further into this behavior.&lt;&#x2F;p&gt;
&lt;h6 id=&quot;unexpected-directory-entries&quot;&gt;Unexpected Directory Entries&lt;&#x2F;h6&gt;
&lt;p&gt;What follows is my attempts to flesh out why extra directory entries are
created. If you don&#x27;t feel like looking at hexdumps, &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;ethflop&#x2F;#delete&quot;&gt;skip&lt;&#x2F;a&gt; to the
next section!&lt;&#x2F;p&gt;
&lt;p&gt;The extra directory entries seem to disappear when I &lt;em&gt;recreate&lt;&#x2F;em&gt; an existing
image. For example, my second (&lt;em&gt;and still wrong&lt;&#x2F;em&gt;) attempt to duplicate &lt;code&gt;mydbak.img&lt;&#x2F;code&gt;
from a fresh copy of &lt;code&gt;mydisk.img&lt;&#x2F;code&gt; resulted in the following hexdumps:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wjones@DietPi:~&amp;#x2F;src&amp;#x2F;img$ hd mydisk.img
000000 eb fe 90 4d 53 44 4f 53 35 2e 30 00 02 02 01 00  &amp;gt;...MSDOS5.0.....&amp;lt;
000010 02 70 00 d0 02 fd 02 00 09 00 02 00 00 00 00 00  &amp;gt;.p..............&amp;lt;
000020 00 00 00 00 00 00 29 1e 4a e9 5d 4e 4f 20 4e 41  &amp;gt;......).J.]NO NA&amp;lt;
000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 00 00  &amp;gt;ME    FAT12   ..&amp;lt;
000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
0001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa  &amp;gt;..............U.&amp;lt;
000200 fd ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
000600 fd ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
000610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
000a00 54 45 53 54 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;TEST       .....&amp;lt;
000a10 00 00 00 00 00 00 b7 6a 85 4f 02 00 00 00 00 00  &amp;gt;.......j.O......&amp;lt;
000a20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
001800 2e 20 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;.          .....&amp;lt;
001810 00 00 00 00 00 00 b7 6a 85 4f 02 00 00 00 00 00  &amp;gt;.......j.O......&amp;lt;
001820 2e 2e 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;..         .....&amp;lt;
001830 00 00 00 00 00 00 b7 6a 85 4f 00 00 00 00 00 00  &amp;gt;.......j.O......&amp;lt;
001840 54 45 53 54 20 20 20 20 54 58 54 20 00 00 00 00  &amp;gt;TEST    TXT ....&amp;lt;
001850 00 00 00 00 00 00 c9 6a 85 4f 03 00 18 00 00 00  &amp;gt;.......j.O......&amp;lt;
001860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
001c00 22 54 68 69 73 20 69 73 20 61 20 74 65 73 74 20  &amp;gt;&amp;quot;This is a test &amp;lt;
001c10 66 69 6c 65 22 20 0d 0a 00 00 00 00 00 00 00 00  &amp;gt;file&amp;quot; ..........&amp;lt;
001c20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
05a000
wjones@DietPi:~&amp;#x2F;src&amp;#x2F;img$ hd mydbak.img
000000 eb fe 90 4d 53 44 4f 53 35 2e 30 00 02 02 01 00  &amp;gt;...MSDOS5.0.....&amp;lt;
000010 02 70 00 d0 02 fd 02 00 09 00 02 00 00 00 00 00  &amp;gt;.p..............&amp;lt;
000020 00 00 00 00 00 00 29 7c 00 e8 5d 4e 4f 20 4e 41  &amp;gt;......)|..]NO NA&amp;lt;
000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 00 00  &amp;gt;ME    FAT12   ..&amp;lt;
000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
0001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa  &amp;gt;..............U.&amp;lt;
000200 fd ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
000600 fd ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
000610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
000a00 54 45 53 54 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;TEST       .....&amp;lt;
000a10 00 00 00 00 00 00 9d 6e 84 4f 02 00 00 00 00 00  &amp;gt;.......n.O......&amp;lt;
000a20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
001800 2e 20 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;.          .....&amp;lt;
001810 00 00 00 00 00 00 9d 6e 84 4f 02 00 00 00 00 00  &amp;gt;.......n.O......&amp;lt;
001820 2e 2e 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;..         .....&amp;lt;
001830 00 00 00 00 00 00 9d 6e 84 4f 00 00 00 00 00 00  &amp;gt;.......n.O......&amp;lt;
001840 54 45 53 54 20 20 20 20 54 58 54 20 00 00 00 00  &amp;gt;TEST    TXT ....&amp;lt;
001850 00 00 00 00 00 00 b0 6e 84 4f 03 00 16 00 00 00  &amp;gt;.......n.O......&amp;lt;
001860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
001c00 54 68 69 73 20 69 73 20 61 20 74 65 73 74 20 66  &amp;gt;This is a test f&amp;lt;
001c10 69 6c 65 2e 0d 0a 00 60 a9 16 eb 00 02 2f 00 00  &amp;gt;ile....`.....&amp;#x2F;..&amp;lt;
001c20 46 44 49 53 4b 20 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;FDISK   COM.....&amp;lt;
001c30 00 00 00 00 00 00 00 60 a9 16 f1 00 d8 df 00 00  &amp;gt;.......`........&amp;lt;
001c40 46 4f 52 4d 41 54 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;FORMAT  COM.....&amp;lt;
001c50 00 00 00 00 00 00 00 60 a9 16 0d 01 8f 80 00 00  &amp;gt;.......`........&amp;lt;
001c60 4d 49 52 52 4f 52 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;MIRROR  COM.....&amp;lt;
001c70 00 00 00 00 00 00 00 60 a9 16 1e 01 f9 46 00 00  &amp;gt;.......`.....F..&amp;lt;
001c80 33 43 35 58 39 50 44 20 43 4f 4d 00 00 00 00 00  &amp;gt;3C5X9PD COM.....&amp;lt;
001c90 00 00 00 00 00 00 ac 80 85 26 89 13 bc 31 00 00  &amp;gt;.........&amp;amp;...1..&amp;lt;
001ca0 53 48 41 52 45 20 20 20 45 58 45 00 00 00 00 00  &amp;gt;SHARE   EXE.....&amp;lt;
001cb0 00 00 00 00 00 00 00 60 a9 16 2a 01 90 2a 00 00  &amp;gt;.......`..*..*..&amp;lt;
001cc0 53 4d 41 52 54 44 52 56 53 59 53 00 00 00 00 00  &amp;gt;SMARTDRVSYS.....&amp;lt;
001cd0 00 00 00 00 00 00 00 60 a9 16 30 01 83 20 00 00  &amp;gt;.......`..0.. ..&amp;lt;
001ce0 53 59 53 20 20 20 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;SYS     COM.....&amp;lt;
001cf0 00 00 00 00 00 00 00 60 a9 16 35 01 70 34 00 00  &amp;gt;.......`..5.p4..&amp;lt;
001d00 55 4e 44 45 4c 45 54 45 45 58 45 00 00 00 00 00  &amp;gt;UNDELETEEXE.....&amp;lt;
001d10 00 00 00 00 00 00 00 60 a9 16 3c 01 64 36 00 00  &amp;gt;.......`..&amp;lt;.d6..&amp;lt;
001d20 55 4e 46 4f 52 4d 41 54 43 4f 4d 00 00 00 00 00  &amp;gt;UNFORMATCOM.....&amp;lt;
001d30 00 00 00 00 00 00 00 60 a9 16 43 01 90 48 00 00  &amp;gt;.......`..C..H..&amp;lt;
001d40 58 43 4f 50 59 20 20 20 45 58 45 00 00 00 00 00  &amp;gt;XCOPY   EXE.....&amp;lt;
001d50 00 00 00 00 00 00 00 60 a9 16 4d 01 bc 3d 00 00  &amp;gt;.......`..M..=..&amp;lt;
001d60 44 4f 53 4b 45 59 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;DOSKEY  COM.....&amp;lt;
001d70 00 00 00 00 00 00 00 60 a9 16 55 01 fc 16 00 00  &amp;gt;.......`..U.....&amp;lt;
001d80 44 4f 53 53 48 45 4c 4c 56 49 44 00 00 00 00 00  &amp;gt;DOSSHELLVID.....&amp;lt;
001d90 00 00 00 00 00 00 00 60 a9 16 58 01 f6 24 00 00  &amp;gt;.......`..X..$..&amp;lt;
001da0 54 43 50 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;TCP        .....&amp;lt;
001db0 00 00 00 00 00 00 ee 10 24 c8 aa 04 00 00 00 00  &amp;gt;........$.......&amp;lt;
001dc0 44 4f 53 53 48 45 4c 4c 43 4f 4d 00 00 00 00 00  &amp;gt;DOSSHELLCOM.....&amp;lt;
001dd0 00 00 00 00 00 00 00 60 a9 16 63 01 02 12 00 00  &amp;gt;.......`..c.....&amp;lt;
001de0 44 4f 53 53 48 45 4c 4c 45 58 45 00 00 00 00 00  &amp;gt;DOSSHELLEXE.....&amp;lt;
001df0 00 00 00 00 00 00 00 60 a9 16 66 01 0e 98 03 00  &amp;gt;.......`..f.....&amp;lt;
001e00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
05a000
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;mydbak.img&lt;&#x2F;code&gt; was created during a previous session and contains directory
entries from my AT hard disk, while &lt;code&gt;mydisk.img&lt;&#x2F;code&gt; does not. When I recreate
&lt;code&gt;mydisk.img&lt;&#x2F;code&gt; &lt;em&gt;yet again&lt;&#x2F;em&gt;, I see the same results- no directory entries.&lt;&#x2F;p&gt;
&lt;p&gt;It was at this point I realized I accidentally included quotes in &lt;code&gt;TEST.TXT&lt;&#x2F;code&gt;
while duplicating the layout of &lt;code&gt;mydbak.img&lt;&#x2F;code&gt;. On a hunch, I &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;ethflop&#x2F;#delete&quot;&gt;deleted&lt;&#x2F;a&gt;
&lt;code&gt;mydisk.img&lt;&#x2F;code&gt; completely, created a new disk, and created &lt;code&gt;TEST&lt;&#x2F;code&gt; and &lt;code&gt;TEST.TXT&lt;&#x2F;code&gt;
&lt;em&gt;correctly&lt;&#x2F;em&gt; this time. The hexdump of my new &lt;code&gt;mydisk.img&lt;&#x2F;code&gt; looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wjones@DietPi:~&amp;#x2F;src&amp;#x2F;img$ hd mydisk.img
000000 eb fe 90 4d 53 44 4f 53 35 2e 30 00 02 02 01 00  &amp;gt;...MSDOS5.0.....&amp;lt;
000010 02 70 00 d0 02 fd 02 00 09 00 02 00 00 00 00 00  &amp;gt;.p..............&amp;lt;
000020 00 00 00 00 00 00 29 1e 4a e9 5d 4e 4f 20 4e 41  &amp;gt;......).J.]NO NA&amp;lt;
000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 00 00  &amp;gt;ME    FAT12   ..&amp;lt;
000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
0001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa  &amp;gt;..............U.&amp;lt;
000200 fd ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
000600 fd ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
000610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
000a00 54 45 53 54 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;TEST       .....&amp;lt;
000a10 00 00 00 00 00 00 b7 6a 85 4f 02 00 00 00 00 00  &amp;gt;.......j.O......&amp;lt;
000a20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
001800 2e 20 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;.          .....&amp;lt;
001810 00 00 00 00 00 00 b7 6a 85 4f 02 00 00 00 00 00  &amp;gt;.......j.O......&amp;lt;
001820 2e 2e 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;..         .....&amp;lt;
001830 00 00 00 00 00 00 b7 6a 85 4f 00 00 00 00 00 00  &amp;gt;.......j.O......&amp;lt;
001840 54 45 53 54 20 20 20 20 54 58 54 20 00 00 00 00  &amp;gt;TEST    TXT ....&amp;lt;
001850 00 00 00 00 00 00 10 6c 85 4f 03 00 17 00 00 00  &amp;gt;.......l.O......&amp;lt;
001860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
001c00 54 68 69 73 20 69 73 20 61 20 74 65 73 74 20 66  &amp;gt;This is a test f&amp;lt;
001c10 69 6c 65 2e 20 0d 0a 0e 24 c8 02 00 00 00 00 00  &amp;gt;ile. ...$.......&amp;lt;
001c20 2e 2e 20 20 20 20 20 20 20 20 20 10 00 00 00 00  &amp;gt;..         .....&amp;lt;
001c30 00 00 00 00 00 00 68 0e 24 c8 00 00 00 00 00 00  &amp;gt;......h.$.......&amp;lt;
001c40 43 4f 55 4e 54 52 59 20 53 59 53 00 00 00 00 00  &amp;gt;COUNTRY SYS.....&amp;lt;
001c50 00 00 00 00 00 00 00 60 a9 16 3f 00 16 45 00 00  &amp;gt;.......`..?..E..&amp;lt;
001c60 45 47 41 20 20 20 20 20 53 59 53 00 00 00 00 00  &amp;gt;EGA     SYS.....&amp;lt;
001c70 00 00 00 00 00 00 00 60 a9 16 48 00 15 13 00 00  &amp;gt;.......`..H.....&amp;lt;
001c80 4b 45 59 42 20 20 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;KEYB    COM.....&amp;lt;
001c90 00 00 00 00 00 00 00 60 a9 16 4b 00 3b 3b 00 00  &amp;gt;.......`..K.;;..&amp;lt;
001ca0 4b 45 59 42 4f 41 52 44 53 59 53 00 00 00 00 00  &amp;gt;KEYBOARDSYS.....&amp;lt;
001cb0 00 00 00 00 00 00 00 60 a9 16 53 00 3e 95 00 00  &amp;gt;.......`..S.&amp;gt;...&amp;lt;
001cc0 4e 4c 53 46 55 4e 43 20 45 58 45 00 00 00 00 00  &amp;gt;NLSFUNC EXE.....&amp;lt;
001cd0 00 00 00 00 00 00 00 60 a9 16 66 00 6c 1b 00 00  &amp;gt;.......`..f.l...&amp;lt;
001ce0 44 49 53 50 4c 41 59 20 53 59 53 00 00 00 00 00  &amp;gt;DISPLAY SYS.....&amp;lt;
001cf0 00 00 00 00 00 00 00 60 a9 16 6a 00 a5 3d 00 00  &amp;gt;.......`..j..=..&amp;lt;
001d00 45 47 41 20 20 20 20 20 43 50 49 00 00 00 00 00  &amp;gt;EGA     CPI.....&amp;lt;
001d10 00 00 00 00 00 00 00 60 a9 16 72 00 e0 e5 00 00  &amp;gt;.......`..r.....&amp;lt;
001d20 48 49 4d 45 4d 20 20 20 53 59 53 00 00 00 00 00  &amp;gt;HIMEM   SYS.....&amp;lt;
001d30 00 00 00 00 00 00 00 60 a9 16 8f 00 20 2d 00 00  &amp;gt;.......`.... -..&amp;lt;
001d40 4d 4f 44 45 20 20 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;MODE    COM.....&amp;lt;
001d50 00 00 00 00 00 00 00 60 a9 16 95 00 21 5c 00 00  &amp;gt;.......`....!\..&amp;lt;
001d60 53 45 54 56 45 52 20 20 45 58 45 00 00 00 00 00  &amp;gt;SETVER  EXE.....&amp;lt;
001d70 00 00 00 00 00 00 00 60 a9 16 a1 00 e1 2e 00 00  &amp;gt;.......`........&amp;lt;
001d80 41 4e 53 49 20 20 20 20 53 59 53 00 00 00 00 00  &amp;gt;ANSI    SYS.....&amp;lt;
001d90 00 00 00 00 00 00 00 60 a9 16 a7 00 3a 23 00 00  &amp;gt;.......`....:#..&amp;lt;
001da0 44 45 42 55 47 20 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;DEBUG   COM.....&amp;lt;
001db0 00 00 00 00 00 00 00 60 a9 16 ac 00 8a 50 00 00  &amp;gt;.......`.....P..&amp;lt;
001dc0 45 44 4c 49 4e 20 20 20 43 4f 4d 00 00 00 00 00  &amp;gt;EDLIN   COM.....&amp;lt;
001dd0 00 00 00 00 00 00 00 60 a9 16 b7 00 52 31 00 00  &amp;gt;.......`....R1..&amp;lt;
001de0 45 4d 4d 33 38 36 20 20 45 58 45 00 00 00 00 00  &amp;gt;EMM386  EXE.....&amp;lt;
001df0 00 00 00 00 00 00 00 60 a9 16 be 00 5e 66 01 00  &amp;gt;.......`....^f..&amp;lt;
001e00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  &amp;gt;................&amp;lt;
*
05a000
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I see directory entries yet again, and &lt;em&gt;they&#x27;re not even the same ones present
in &lt;code&gt;mydbak.img&lt;&#x2F;code&gt;!&lt;&#x2F;em&gt; Now I&#x27;m confident this behavior isn&#x27;t intentional!&lt;&#x2F;p&gt;
&lt;h5 id=&quot;delete&quot;&gt;Delete&lt;&#x2F;h5&gt;
&lt;p&gt;Because I&#x27;m cautious about creating virtual disks of densities&#x2F;sizes that my
hardware doesn&#x27;t support, I decided to delete &lt;code&gt;HDDISK&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;hddisk&lt;&#x2F;code&gt; to remove the
temptation:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;ethflop d hddisk
Disk HDDISK has been deleted
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On the server side, there are no surprises- &lt;code&gt;hddisk.img&lt;&#x2F;code&gt; is gone. If someone
&lt;em&gt;really&lt;&#x2F;em&gt; wants it back, they can recreate it from the hexdump!&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wjones@DietPi:~&amp;#x2F;src&amp;#x2F;img$ ls
msdos213.img  mtcpget.img  mydbak.img  mydisk.img
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At this point, I had tested ethflop&#x27;s functionality to my satisfaction and
turned off the AT for the day. I&#x27;m sure I&#x27;ll be using both of them again very
soon.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-verdict&quot;&gt;The Verdict&lt;&#x2F;h2&gt;
&lt;p&gt;ethflop works pretty much flawlessly for accessing my myriad of floppy images
I&#x27;ve accumulated over the years. As if you couldn&#x27;t guess, I love seeing old
hardware given a new lease on life. This applies especially when new software
&lt;em&gt;extends&lt;&#x2F;em&gt; the functionality of old hardware to- or beyond- what it was capable
of during its useful life. So to &lt;a href=&quot;http:&#x2F;&#x2F;mateusz.viste.fr&#x2F;&quot;&gt;Mateusz Viste&lt;&#x2F;a&gt;,
I sincerely thank you for writing ethflop!&lt;&#x2F;p&gt;
&lt;p&gt;I have a large number of floppy images I&#x27;ve accumulated over the years,
but not enough 5.25&amp;quot; floppies&lt;a id=&quot;rev-fn-6&quot; href=&quot;#fn-6&quot;&gt;&lt;sup&gt;6&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 to write them all. In the past I&#x27;ve
reused disks I&#x27;ve no longer needed (but had an image backup) in order to
&amp;quot;time division multiplex&amp;quot; floppy disks. But I have plenty of images which only
work properly as boot disks. Since I can&#x27;t load ethflop during boot, I like to
save my physical floppies for stuff like install or rescue disks. I think I
will be using ethflop frequently from here on out for my vintage pursuits,
unless I&#x27;m just in the mood to play with &lt;em&gt;real&lt;&#x2F;em&gt;, physical floppy disks :).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;future-ideas&quot;&gt;Future Ideas&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;m not in the right frame of mind to try this now, but I wonder if it&#x27;d be
possible to extend ethflop to emulate a virtual D:\ drive (I dare not try
replacing drive C:\!)? Or better yet, is it possible to extend ethflop into
an MSDOS block device driver instead of a TSR? What about systems with 4
floppy drives? They&#x27;re rare, but the original IBM PC disk controller supports
them &lt;em&gt;in principle&lt;&#x2F;em&gt;&lt;a id=&quot;rev-fn-7&quot; href=&quot;#fn-7&quot;&gt;&lt;sup&gt;7&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
. Oh, and I think I can even use ethflop to
implement &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Diskcopy&quot;&gt;&lt;code&gt;DISKCOPY&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; over the network
for unimaged floppies!&lt;&#x2F;p&gt;
&lt;p&gt;Mateusz Viste also wrote a &lt;a href=&quot;http:&#x2F;&#x2F;etherdfs.sourceforge.net&quot;&gt;networked file system&lt;&#x2F;a&gt;
for DOS that I want to check out soon- that also might give me some ideas.
There are many neat DOS and floppy project ideas still waiting to be pulled
from the ether to give old machines new life...&lt;&#x2F;p&gt;
&lt;!-- ## Extra- How ethflop Works
If you&#x27;re not interested in how DOS works (at a medium-high level), feel free
to skip reading this section. Otherwise, read on!

### Mini DOS Primer
If you _are_ interested in some DOS internals but haven&#x27;t studied
them before, here is some relevant info for previous and remaining sections:

* A DOS device driver is a set of x86 routines honoring certain conventions
  that the DOS kernel uses to talk to hardware. Like Unix variants, DOS
  uses human-readable identifiers to identify device drivers. Unlike Unix
  variants, these identifiers become reserved names that can&#x27;t be used for
  files.

  Additionally, like Unix flavors, DOS has two types of device drivers-
  character devices and block devices. Character device drivers implement
  MSDOS features such as `TYPE FOO.TXT &gt; $DRIVER_NAME`. Block device drivers
  implement drive letters in DOS.

  Traditionally, DOS device drivers could only be loaded at boot by reading
  a file named `CONFIG.SYS`. The third-party `DEVLOAD` [program](https:&#x2F;&#x2F;www.infradead.org&#x2F;devload&#x2F;)
  provides code to load device drivers after boot. The story of `DEVLOAD` is
  quite fascinating if you&#x27;re a DOS enthusiast, so I encourage others to
  read the Project Writeup.

* The DOS kernel can usually only handle a single task&#x2F;executable at a time.
  It is possible to load routines that can be used by executables run at a
  later time that change the default behavior of DOS. These are known as
  Terminate and Stay Resident (TSR) programs. Most (all?) TSRs will [hook](https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;37057157&#x2F;what-does-interrupt-hooking-mean)
  x86 interrupts to change the behavior of DOS and the BIOS. Both DOS and the
  BIOS use [software interrupts](https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;INT_(x86_instruction))
  or hardware interrupts (e.g. the floppy controller telling the CPU &quot;I&#x27;m done
  transferring data!&quot;) to complete various tasks, like e.g. loading a file
  from floppy disk to memory.

### How ethflop Actually Works
ethflop is a TSR that hooks the (software) interrupt `0x13` floppy disk
services provided by the BIOS. Since DOS reuses BIOS-provided APIs in a
number of cases instead of reimplementing their functionality&lt;a id=&quot;rev-fn-8&quot; href=&quot;#fn-8&quot;&gt;&lt;sup&gt;8&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
,
it is often sufficient to change BIOS functionality in order to change DOS
functionality.
--&gt;
&lt;!--
### Is ethflop Even Possible?
This section mainly exists because I wanted to document my initial
understanding of how ethflop works (and why I was drawn to it) versus my
current understanding! It is completely optional, so feel free to skip.

I didn&#x27;t initially think ethflop was possible to implement within the
restrictions of a DOS environment. Thinking of workarounds&#x2F;solutions to all
the issues I _perceived_ made me tap into knowledge I haven&#x27;t used in years and
was otherwise a fun (for some value of fun) exercise.

I had some misconceptions about how ethflop works. Specifically, I thought
ethflop was a DOS block device driver that implemented the block device
routines via by Ethernet card&#x27;s packet driver. Based on this misconception, I
was _very_ curious about ethflop&#x27;s implementation; such a device driver would
not be easy to implement, and I would imagine a number of edge cases that
would crash DOS.

In the past, information about DOS
[data structure internals](http:&#x2F;&#x2F;stanislavs.org&#x2F;helppc&#x2F;sft.html)
and [device drivers](https:&#x2F;&#x2F;www.drdobbs.com&#x2F;writing-ms-dos-device-drivers&#x2F;184402277)
were hard to find online&lt;a id=&quot;rev-fn-2&quot; href=&quot;#fn-2&quot;&gt;&lt;sup&gt;2&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
.

Additionally, while I&#x27;ve heard of MSDOS device drivers that are basically
vestigial&lt;a id=&quot;rev-fn-3&quot; href=&quot;#fn-3&quot;&gt;&lt;sup&gt;3&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
, I&#x27;d never heard an MSDOS device driver that also
required a TSR to function. The ethflop website specifically mentions that
ethflop requires a packet driver, which is one TSR. Secondly, the executable
`ETHFLOP.COM` itself installs as a TSR.

A device driver which relied on one or more TSRs could not be loaded via
`CONFIG.SYS`

At th

I vaguely know such device drivers already exist, but I would be curious how
difficult it would be to implement basic network-attached storage as a DOS
block device driver of my own? Could I reuse the packet driver for this
(meaning `DEVLOAD`-only)? Could I somehow multiplex using the packet driver for
the device driver as well as a second DOS application that talks over the
network? I sense plenty of opportunities for beautiful and horrifying hacks!

### How ethflop Actually Works
After playing with ethflop, I ended up skimming the source to `ethflop.asm`
satisfy my own curiosity. Turns out I grossly misunderstood how ethflop works.
For starters, ethflop is a TSR that hooks
--&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;&#x2F;h2&gt;
&lt;p id=&quot;fn-1&quot;&gt;&lt;a href=&quot;#rev-fn-1&quot;&gt;1&lt;&#x2F;a&gt; I&#x27;ve opened my 10BASE2 to 10BASET converter before; the conversion is done by
a chip that was manufactured by National Semiconductor in the early&#x2F;mid-90s.
Unfortunately, its name escapes me, and I can&#x27;t check in the immediate future.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-2&quot;&gt;&lt;a href=&quot;#rev-fn-2&quot;&gt;2&lt;&#x2F;a&gt; I&#x27;m unsure if a &quot;generic&quot; packet driver exists, though I&#x27;m aware that the
packet driver spec maintainer &lt;a href=&quot;http:&#x2F;&#x2F;russnelson.com&quot;&gt;Russ Nelson&lt;&#x2F;a&gt;
used to have a generic template that vendors used to create their packet
drivers.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-3&quot;&gt;&lt;a href=&quot;#rev-fn-3&quot;&gt;3&lt;&#x2F;a&gt; Interfacing DOS with the serial port could be a blog post in and of itself,
I&#x27;m afraid. It&#x27;s very easy to get a very helpful &lt;code&gt;Write failure&lt;&#x2F;code&gt; or
&lt;code&gt;General failure&lt;&#x2F;code&gt; when accessing the serial port (e.g.
&lt;code&gt;COPY COM2 FOO.TXT&lt;&#x2F;code&gt; or &lt;code&gt;CTTY COM2&lt;&#x2F;code&gt;) just by breathing
wrong. What &lt;em&gt;best&lt;&#x2F;em&gt; matches my experience is that DOS uses &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;RS-232#RTS,_CTS,_and_RTR&quot;&gt;flow control&lt;&#x2F;a&gt;
for sending but not receiving (&lt;a href=&quot;http:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20010817014431&#x2F;http:&#x2F;&#x2F;www.algonet.se&#x2F;~dennisgr&#x2F;z88-dark.htm&quot;&gt;Source&lt;&#x2F;a&gt;).
Disabling flow control in your serial terminal or transfer program is a safe
option.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-4&quot;&gt;&lt;a href=&quot;#rev-fn-4&quot;&gt;4&lt;&#x2F;a&gt; I don&#x27;t remember the exact details right now (and will check later), but I had
an old 8-bit ISA CNet NIC that required a few kB of &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Upper_memory_area&quot;&gt;Upper Memory&lt;&#x2F;a&gt;.
The manufacturer-set default switches for the memory area- yes, this had to be
set manually!- conflicted with the memory area commonly used for hard drive
controller (segment &lt;code&gt;0xc800&lt;&#x2F;code&gt;). This was even mentioned in the manual I got with
the card, but of course I didn&#x27;t read it first :).&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-5&quot;&gt;&lt;a href=&quot;#rev-fn-5&quot;&gt;5&lt;&#x2F;a&gt; Originally, the IBM PC had provisions for 4 floppy drives, though
I don&#x27;t remember how DOS assigns drive letters in this case, especially in the
case where hard disks exist as well. This might be an interesting addition to
ethflop to keep Drives A:\ and B:\ free.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-6&quot;&gt;&lt;a href=&quot;#rev-fn-6&quot;&gt;6&lt;&#x2F;a&gt; I think I have about 50 5.25&quot; floppies overall? Over the years I&#x27;ve accumulated
approximately 30 Double Density (360kB) and 20 High Density (1.2MB) disks.
Sadly, it is easier for me to write and read 5.25&quot; disks than 3.5&quot; disks ever
since my USB floppy drive broke...&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-7&quot;&gt;&lt;a href=&quot;#rev-fn-7&quot;&gt;7&lt;&#x2F;a&gt; As far as I&#x27;m aware, no commercial product was ever released that used the
DB37 connector on the original IBM PC floppy controller ISA card. However, the
pinout matches the more traditional 34-pin edge connector, so an adapter
isn&#x27;t difficult.&lt;&#x2F;p&gt;
&lt;!-- &lt;p id=&quot;fn-8&quot;&gt;&lt;a href=&quot;#rev-fn-8&quot;&gt;8&lt;&#x2F;a&gt; This is in contrast to x86 Linux, BSD, Windows, or any hobbyist OS using
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Protected_mode&quot;&gt;protected mode&lt;&#x2F;a&gt;.
While I imagine DOS started out using the BIOS services as a space saving
measure, I imagine there was also a backwards compatibility element later on
that made a more efficient reimplementation of BIOS services impossible.&lt;&#x2F;p&gt;


&lt;p id=&quot;fn-5&quot;&gt;&lt;a href=&quot;#rev-fn-5&quot;&gt;5&lt;&#x2F;a&gt; In my opinion, info is _still_ hard to find though the situation has gotten
better. Back when I was actively learning about DOS, [DEVDRIV.DOC](https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;MS-DOS&#x2F;blob&#x2F;master&#x2F;v2.0&#x2F;bin&#x2F;DEVDRIV.DOC)
didn&#x27;t exist yet. I&#x27;ll seek out my old [VCF](http:&#x2F;&#x2F;www.vcfed.org&#x2F;forum&#x2F;forum.php)
forum posts later, but aside from a PDF here and there, information on DOS data
structures and device driver internals were relegated to unscanned physical
books.&lt;&#x2F;p&gt;


&lt;p id=&quot;fn-6&quot;&gt;&lt;a href=&quot;#rev-fn-6&quot;&gt;6&lt;&#x2F;a&gt; For instance, citation needed, but my current understanding of Expanded
Memory Managers is that the [EMM spec](http:&#x2F;&#x2F;www.phatcode.net&#x2F;res&#x2F;218&#x2F;files&#x2F;limems40.txt)
mandates loading a device driver to indicate that a manager is present via the
device name. However, there isn&#x27;t actually any _code_ associated with the
device driver that DOS uses. Rather, all Expanded Memory routines are accessed
via x86 interrupt 0x67.&lt;&#x2F;p&gt;
 --&gt;
</content>
	</entry>
  

  
	<entry xml:lang="en">
		<title>Porting a New Board To Migen</title>
		<published>2017-09-25T00:00:00+00:00</published>
		<updated>2019-06-09T00:00:00+00:00</updated>
		<link href="https://www.wdj-consulting.com/blog/migen-port/" type="text/html"/>
		<id>https://www.wdj-consulting.com/blog/migen-port/</id>
		<content type="html">&lt;h1 id=&quot;porting-a-new-board-to-migen&quot;&gt;Porting a New Board To Migen&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;em&gt;Taps mic&lt;&#x2F;em&gt; Is this thing on? So it&#x27;s really been 11 months since I last
wrote something? I really need to change that! I&#x27;m going to &lt;em&gt;attempt&lt;&#x2F;em&gt;
to write smaller articles in between my larger, more involved ones to keep
me motivated to keep writing.&lt;&#x2F;p&gt;
&lt;p&gt;So today, I&#x27;m going to write about a simpler topic I meant to discuss last
year, but never got around to it: How to get started with
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&quot;&gt;Migen&lt;&#x2F;a&gt; on your Shiny New FPGA Development
Board! This post assumes you have previous experience with Python 3 and
experience synthesizing digital logic on FPGAs with Verilog.
&lt;em&gt;However, no Migen experience is assumed!&lt;&#x2F;em&gt; Yes, you can port your a
development board to Migen without having used Migen before!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-migen-and-why-use-it&quot;&gt;What Is Migen- And Why Use It?&lt;&#x2F;h2&gt;
&lt;p&gt;Migen is a Python library (framework, maybe?) to build digital circuits
either for simulation, synthesis on an FPGA, or ASIC fabrication (the latter
of which is mostly untested). Migen is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;SpinalHDL&quot;&gt;one&lt;&#x2F;a&gt;
&lt;a href=&quot;http:&#x2F;&#x2F;www.myhdl.org&quot;&gt;of&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;chisel.eecs.berkeley.edu&quot;&gt;many&lt;&#x2F;a&gt;
attempts to solve some deficiences with Verilog and VHDL, the languages most
commonly used in industry to develop digital integrated circuits (ICs). If
necessary, a designer can use and import Verilog&#x2F;VHDL directly into their
designs using one of the above languages too.&lt;&#x2F;p&gt;
&lt;p&gt;All the languages
above either emit Verilog or an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;freechipsproject&#x2F;firrtl&quot;&gt;Intermediate Representation&lt;&#x2F;a&gt;
that can be transformed into Verilog&#x2F;VHDL. I can think of a few reasons why:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;FPGA synthesis&#x2F;ASIC front-ends are typically proprietary, can&#x27;t be readily
modified, and mostly only support Verilog and VHDL as input.&lt;&#x2F;li&gt;
&lt;li&gt;Many issues with Verilog, such as synthesis-simulation behavior mismatch,
don&#x27;t occur if Verilog can be emitted in a controlled manner, as is done
with the above languages.&lt;a id=&quot;rev-fn-1&quot; href=&quot;#fn-1&quot;&gt;&lt;sup&gt;1&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;From my own experience looking at yosys, which can be used as the front-
end Verilog compiler
to an &lt;a href=&quot;http:&#x2F;&#x2F;www.clifford.at&#x2F;icestorm&#x2F;&quot;&gt;open-source synthesis toolchain&lt;&#x2F;a&gt;,
there is little gain to targeting a file format meant for FPGA
synthesis (or ASIC fabrication) directly from a higher-level language.
Targeting the synthesis file format directly must be shown to be bug-free
with respect to having generated Verilog &lt;em&gt;and then&lt;&#x2F;em&gt; generating the
synthesis file format from the Verilog; this is nontrivial.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;media.readthedocs.org&#x2F;pdf&#x2F;mithro-migen&#x2F;latest&#x2F;mithro-migen.pdf&quot;&gt;Migen manual&lt;&#x2F;a&gt;
discusses its rationale for existing, but the important
reasons that apply to me personally are that Migen:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Avoids simulation-synthesis behavior mismatches in generated output.&lt;&#x2F;li&gt;
&lt;li&gt;Makes it easy to automate digital logic design idioms that are tedious in
Verilog alone, such as Finite-State Machines and Asynchronous FIFOs.&lt;&#x2F;li&gt;
&lt;li&gt;Prevents classes of bugs in Verilog code, such as ensuring each signal
has an initial value and unused cases in &lt;code&gt;switch&lt;&#x2F;code&gt; statements are
handled.&lt;&#x2F;li&gt;
&lt;li&gt;Handles assignments satisfying
&amp;quot;same sensitivity list, multiple always blocks&amp;quot; by concatenating them all
into a single &lt;code&gt;always&lt;&#x2F;code&gt; block, whereas Verilog forbids this for synthesis.
In my experience, this increases code clarity.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As an added bonus, I can write a Migen design once and, within reason,
generate a bitstream of my design without needing a separate User
Constraints File (UCF) for each board that Migen supports. This facilitates
design reuse by others who may not have the same board as I do, but has a
new board with the required I&#x2F;O peripherals anyway.&lt;&#x2F;p&gt;
&lt;p&gt;For the above reasons, I am far more productive writing HDL than I ever was
writing Verilog.&lt;a id=&quot;rev-fn-2&quot; href=&quot;#fn-2&quot;&gt;&lt;sup&gt;2&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h3 id=&quot;choice-of-tools-disclaimer&quot;&gt;Choice of Tools Disclaimer&lt;&#x2F;h3&gt;
&lt;p&gt;Of the linked languages above, I have only personally used Migen. Migen was
the first Verilog alternative I personally discovered when I started using
FPGAs for projects again in 2015 (after a 3 year break). When I first saw
the typical code structure of a Migen project, I immediately felt at home
writing my own basic designs, and could easily predict which Migen code
would emit which Verilog constructs without reading the source. In fact, I
ported my own Shiny New FPGA Development Board I just bought to Migen before
I even tested my first Migen design!&lt;&#x2F;p&gt;
&lt;p&gt;Because of my extremely positive first impressions, and time spent learning
Migen&#x27;s more complicated features, I&#x27;ve had little incentive to learn a new
HDL. That said, I maintain it is up to the reader to experiment and decide
which HDL works best for their FPGA&#x2F;ASIC projects. I only speak for my own
experiences, and the point of me writing this post is to make porting a new
board to Migen easier than when I learned how to do it. The code re-use
aspect of Migen is important to me, and when done correctly, a port to a new
board is very low-maintenance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;leveraging-python-to-build-fpga-applications&quot;&gt;Leveraging Python To Build FPGA Applications&lt;&#x2F;h2&gt;
&lt;p&gt;To motivate how to port a new development board, I need to show Migen code
right now. If you haven&#x27;t seen Migen before now, don&#x27;t panic! I&#x27;ll briefly
explain each section:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;from migen import *
from migen.build.platforms import icestick

class Rot(Module):
    def __init__(self):
        self.clk_freq = 12000000
        self.ready = Signal()
        self.rot = Signal(4)
        self.divider = Signal(max=self.clk_freq)
        self.d1 = Signal()
        self.d2 = Signal()
        self.d3 = Signal()
        self.d4 = Signal()
        self.d5 = Signal()

        ###
        self.comb += [j.eq(self.rot[i]) for i, j in enumerate([self.d1, self.d2, self.d3, self.d4])]
        self.comb += [self.d5.eq(1)]

        self.sync += [
            If(self.ready,
                If(self.divider == int(self.clk_freq) - 1,
                    self.divider.eq(0),
                    self.rot.eq(Cat(self.rot[-1], self.rot[:-1]))
                ).Else(
                    self.divider.eq(self.divider + 1)
                )
            ).Else(
                self.ready.eq(1),
                self.rot.eq(1),
                self.divider.eq(0)
            )
        ]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you&#x27;ve never seen Migen code before, and&#x2F;or are unfamiliar with its
layout, I&#x27;ll explain some of the more interesting points here in comparison
to Verilog:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;from migen import *
from migen.build.platforms import icestick
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Migen by default exports a number
of primitives to get started writing &lt;code&gt;Modules&lt;&#x2F;code&gt;.  Other Migen constructs,
such as a library of useful building blocks (&lt;code&gt;migen.genlib&lt;&#x2F;code&gt;) and Verilog
code generation (&lt;code&gt;migen.fhdl.verilog&lt;&#x2F;code&gt;) must be imported manually.
&lt;code&gt;migen.build.platforms&lt;&#x2F;code&gt; contains all the FPGA development platforms Migen
supports; the goal of this blog post will be to reimplement the
&lt;a href=&quot;http:&#x2F;&#x2F;www.latticesemi.com&#x2F;icestick&quot;&gt;iCEstick&lt;&#x2F;a&gt; development board for use
from Migen.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;class Rot(Module):
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A &lt;code&gt;Module&lt;&#x2F;code&gt; is the basic unit describing digital behavior in Migen.
Connections between &lt;code&gt;Module&lt;&#x2F;code&gt;s are typically made by declaring instance
variables that can be shared between &lt;code&gt;Module&lt;&#x2F;code&gt;s, and using &lt;code&gt;submodules&lt;&#x2F;code&gt;.
Submodule syntax is described in the manual, and is required to share
connections between modules.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;self.ready = Signal(1)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A &lt;code&gt;Signal&lt;&#x2F;code&gt; is the basic data type in Migen. Unlike
Verilog, which requires keywords to distinguish between data storage
(&lt;code&gt;reg&lt;&#x2F;code&gt;) and connections&#x2F;nets (&lt;code&gt;wire&lt;&#x2F;code&gt;), Migen can infer from a signal&#x27;s
usage whether or not the signal stores data. The &lt;code&gt;1&lt;&#x2F;code&gt; indicates the signal
is one-bit wide.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;self.divider = Signal(max=self.clk_freq)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In addition to providing a bit width, I can tell Migen the maximum value a
&lt;code&gt;Signal&lt;&#x2F;code&gt; is expected to take, and Migen will initialize its width to
&lt;code&gt;log2(max) + 1&lt;&#x2F;code&gt;. &lt;em&gt;However, nothing prevents the signal from exceeding max
when run in a simulator or FPGA.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;###
self.comb += [j.eq(self.rot[i]) for i, j in enumerate([self.d1, self.d2, self.d3, self.d4])]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By convention, data-type declarations are separated from code describing
how code should behave using &lt;code&gt;###&lt;&#x2F;code&gt;. Migen statements relating connections
of &lt;code&gt;Signal&lt;&#x2F;code&gt;s and other data types must be appended to either a &lt;code&gt;comb&lt;&#x2F;code&gt; or
&lt;code&gt;sync&lt;&#x2F;code&gt; attribute. Connections are typically made using the &lt;code&gt;eq&lt;&#x2F;code&gt; function
of &lt;code&gt;Signal&lt;&#x2F;code&gt;s. Python&#x27;s slice notation accesses individual or subsets of
bits of a &lt;code&gt;Signal&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;comb&lt;&#x2F;code&gt;, or combinationial, statements are analogous to Verilog&#x27;s &lt;code&gt;assign&lt;&#x2F;code&gt;
keyword or combinational &lt;code&gt;always @(*)&lt;&#x2F;code&gt; blocks, depending on the Migen data
type&#x2F;construct. In combinational logic, the output immediately
changes in response to a changing or just-initialized) input without any
concept of time (in theory).&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;self.sync += [
        If(self.ready,
            If(self.divider == int(self.clk_freq) - 1,
                self.divider.eq(0),
                self.rot.eq(Cat(self.rot[-1], self.rot[:-1]))
            ).Else(
                self.divider.eq(self.divider + 1)
            )
        ).Else(
            self.ready.eq(1),
            self.rot.eq(1),
            self.divider.eq(0)
        )
    ]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By contrast, appending to the Migen &lt;code&gt;sync&lt;&#x2F;code&gt;, or synchronous, attribute emits
Verilog code whose output only changes in response to a &lt;em&gt;positive edge&lt;&#x2F;em&gt;
clock transition of a given clock, using the following syntax:
&lt;code&gt;module.sync.my_clk += [...]&lt;&#x2F;code&gt;. If the clock is omitted, the clock defaults
to &lt;code&gt;sys&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In synchronous&#x2F;sequential logic, outputs do not change immediately in
response to a changing or just- initialized input; the output only
registers a new value based on its input signals in response to a low-to-
high transition of another, usually periodic, signal. Migen only &lt;code&gt;always @(posedge clk)&lt;&#x2F;code&gt; blocks, so a &lt;code&gt;negedge&lt;&#x2F;code&gt; clock must be created by inverting
an existing clock, such as &lt;code&gt;self.comb += [neg_clk.eq(~pos_clk)]&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As one might expect, &lt;code&gt;If().Elif().Else()&lt;&#x2F;code&gt; is analogous to Verilog &lt;code&gt;if&lt;&#x2F;code&gt;,
&lt;code&gt;else if&lt;&#x2F;code&gt;, and &lt;code&gt;else&lt;&#x2F;code&gt; blocks. Migen will generate the correct style of
&lt;code&gt;always&lt;&#x2F;code&gt; block to represent signal transitions regardless of whether the
&lt;code&gt;If()&lt;&#x2F;code&gt; statement is part of a &lt;code&gt;comb&lt;&#x2F;code&gt; or &lt;code&gt;sync&lt;&#x2F;code&gt; block.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I omitted discussing any Migen data types, input arguments to their
constructors, other features provided by the package, and common code idioms
that I didn&#x27;t use above for the sake of keeping this blog post on point.
Most of the user-facing features&#x2F;constructors are documented in the user
manual. I can discuss features (and behavior) not mentioned in the manual in
a follow-up post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-a-new-board&quot;&gt;Adding a New Board&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;www.wdj-consulting.com&#x2F;blog&#x2F;migen-port&#x2F;#leveraging-python-to-build-fpga-applications&quot;&gt;above code&lt;&#x2F;a&gt; was adapted
from the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cseed&#x2F;arachne-pnr&#x2F;tree&#x2F;master&#x2F;examples&#x2F;rot&quot;&gt;rot.v&lt;&#x2F;a&gt;
example of Arachne PNR. In words, the above code turns on an LED, counts for
12,000,000 clock cycles, then turns off the previous LED and lights another
of 4 LEDs; after 4 LEDs the cycle repeats. A fifth LED is always kept on as
soon as the FPGA loads its bitstream.&lt;&#x2F;p&gt;
&lt;p&gt;Our goal is to get this simple Migen design to work on a new FPGA
development board, walking through the process. Since this example is
tailored to the iCE40 FPGA family, I&#x27;m choosing to port the iCEstick board
to Migen... which already has a port...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;interactive-porting&quot;&gt;Interactive Porting&lt;&#x2F;h3&gt;
&lt;p&gt;My original intention was to write this blog post &lt;em&gt;while&lt;&#x2F;em&gt; I was
creating the &lt;code&gt;icestick.py&lt;&#x2F;code&gt; platform file to be committed into &lt;code&gt;migen.build&lt;&#x2F;code&gt;.
Unfortunately, at the time, Migen did not have any support for targeting the
IceStorm synthesis toolchain.&lt;a id=&quot;rev-fn-3&quot; href=&quot;#fn-3&quot;&gt;&lt;sup&gt;3&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 So I ended up
&lt;a href=&quot;https:&#x2F;&#x2F;ssl.serverraum.org&#x2F;lists-archive&#x2F;devel&#x2F;2016-May&#x2F;004205.html&quot;&gt;implementing&lt;&#x2F;a&gt;
the IceStorm backend and doing my blog post as intended went by the wayside
while debugging.&lt;&#x2F;p&gt;
&lt;p&gt;That said, I&#x27;m going to attempt to simulate the process of adding a board
from the beginning. I will only assume that the IceStorm backend to Migen
exists, but the &lt;code&gt;icestick.py&lt;&#x2F;code&gt; board file does not.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;we-need-a-constraints-file&quot;&gt;We Need a Constraints File&lt;&#x2F;h3&gt;
&lt;p&gt;Before I can start writing a board file for iCEstick, we need to know which
FPGA pins are connected to what. For many boards, the manufacturer will
provide a full User Constraints File (UCF) with this information. For
demonstrative purposes&lt;a id=&quot;rev-fn-4&quot; href=&quot;#fn-4&quot;&gt;&lt;sup&gt;4&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
 however, I will examine the schematic
diagram of iCEstick instead to create my Migen board file. This can be found
in a file provided by Lattice at a changing URL called
&amp;quot;icestickusermanual.pdf&amp;quot;.&lt;&#x2F;p&gt;
&lt;p&gt;We need to know the format of FPGA pin identifiers that Arachne PNR, the
place-and-route tool for IceStorm, will expect as well. The format differs
for each of the FPGA manufacturers and even between FPGA families of the same
manufacturer; Xilinx, for example uses &lt;code&gt;[A-Z]?[0-9]*&lt;&#x2F;code&gt;, as does the Lattice
ECP3 family. Fortunately, IceStorm uses pin numbers that correspond
to the device package, and these are easily visible on the schematic:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Page of iCEStick schematic&quot; src=&quot;icestick-schem.png&quot;&gt;
&lt;figcaption&gt;One side (port) of connections of the iCE40 FPGA to
iCEstick peripherals. In this image, LED, IrDA, and one side of 600 mil
breadboard-compatible breakout connections can be seen.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;If we examine the schematic and user manual, we will find the following
peripherals:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;5 LEDs&lt;&#x2F;li&gt;
&lt;li&gt;2x6 Digilient PMOD connector&lt;&#x2F;li&gt;
&lt;li&gt;IrDA transceiver&lt;&#x2F;li&gt;
&lt;li&gt;SPI Flash&lt;&#x2F;li&gt;
&lt;li&gt;FT2232H UART (one channel- the other is used for programming)&lt;&#x2F;li&gt;
&lt;li&gt;16 Additional I&#x2F;O pins&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We might not have an actual &lt;em&gt;full&lt;&#x2F;em&gt; constraints file to work with due to
how &lt;code&gt;arachne-pnr&lt;&#x2F;code&gt; works, but we have all the information to create a Migen
board file anyway, since we have the schematics. Armed with this
information, we can start creating a board file for iCEStick.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;anatomy-of-a-migen-board-file&quot;&gt;Anatomy of a Migen Board File&lt;&#x2F;h3&gt;
&lt;p&gt;Relative to the root of the migen package, migen places board definition
files under the &lt;code&gt;build&#x2F;platforms&lt;&#x2F;code&gt; directory. &lt;em&gt;All paths in this section,
unless otherwise noted are relative to the package root.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;platform-class&quot;&gt;Platform Class&lt;&#x2F;h4&gt;
&lt;pre&gt;&lt;code&gt;from migen.build.generic_platform import *
from migen.build.lattice import LatticePlatform
from migen.build.lattice.programmer import IceStormProgrammer

class Platform(LatticePlatform):
    default_clk_name = &amp;quot;clk12&amp;quot;
    default_clk_period = 83.333

    def __init__(self):
        LatticePlatform.__init__(self, &amp;quot;ice40-1k-tq144&amp;quot;, _io, _connectors,
            toolchain=&amp;quot;icestorm&amp;quot;)

    def create_programmer(self):
        return IceStormProgrammer()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A board file consists of the definition of Python &lt;code&gt;class&lt;&#x2F;code&gt; conventionally
named &lt;code&gt;Platform&lt;&#x2F;code&gt;. &lt;code&gt;Platform&lt;&#x2F;code&gt; should inherit from a class defined for each
supported FPGA manufacturer. As of this writing, Migen exports
&lt;code&gt;AlteraPlatform&lt;&#x2F;code&gt;, &lt;code&gt;XilinxPlatform&lt;&#x2F;code&gt;, and &lt;code&gt;LatticePlatform&lt;&#x2F;code&gt;, and more are
possible in the future. Vendor platforms are defined in a subdirectory under
&lt;code&gt;build&lt;&#x2F;code&gt; for each vendor, in the file &lt;code&gt;platform.py&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Each FPGA vendor in turn inherits from &lt;code&gt;GenericPlatform&lt;&#x2F;code&gt;, which
is defined in &lt;code&gt;build&#x2F;generic_platform.py&lt;&#x2F;code&gt; and exports a number of useful
methods for use in Migen code (I&#x27;ll introduce them as needed). The
&lt;code&gt;GenericPlatform&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&#x2F;blob&#x2F;master&#x2F;migen&#x2F;build&#x2F;generic_platform.py#L230&quot;&gt;constructor&lt;&#x2F;a&gt;
accepts the following arguments:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;device&lt;&#x2F;code&gt;- A string indicating the FPGA device on your board.&lt;a id=&quot;rev-fn-5&quot; href=&quot;#fn-5&quot;&gt;&lt;sup&gt;5&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;

The string format is vendor-toolchain specific; in the case of IceStorm,
the format is currently &lt;code&gt;&amp;quot;ice40-{1k,8k}-{package}&amp;quot;&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;io&lt;&#x2F;code&gt;- A list of tuples of a specific format which I&#x27;ll describe shortly.
The list represents all non-user-expandable I&#x2F;O resources on your current
board. For instance, LEDs, SPI flash, and ADC connections to your FPGA
would be placed in the &lt;code&gt;io&lt;&#x2F;code&gt; list.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;connectors&lt;&#x2F;code&gt;- A list of tuples with a specific layout, which I&#x27;ll describe
shortly. The list represents user-expandable I&#x2F;O that by default is not
connecte to any piece of hardware, [Pmod](https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;
Pmod_Interface, Pmod) headers.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;name&lt;&#x2F;code&gt;- I don&#x27;t know what this input argument does exactly. Like other
places in Migen with a &lt;code&gt;name&lt;&#x2F;code&gt; input argument, it&#x27;s meant to control how
variable names are generated in the Verilog output. I don&#x27;t believe it&#x27;s
used by any board file, so I&#x27;m ignoring it.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;toolchain&lt;&#x2F;code&gt;- The same FPGA vendor can have multiple software suites for
their FPGA families, or third-party toolchains can exist. For instance,
Xilinx has both the End-of-Life ISE Toolchain and Vivado software
available. Additionally, Lattice provides the Diamond toolchain for their
higher-end FPGAs while Project IceStorm is an unaffialited open-source
synthesis flow for the iCE40 family. Thus, the vendor platform constructors
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&#x2F;blob&#x2F;master&#x2F;migen&#x2F;build&#x2F;lattice&#x2F;platform.py#L8&quot;&gt;also supply&lt;&#x2F;a&gt;
a &lt;code&gt;toolchain&lt;&#x2F;code&gt; keyword argument to choose which toolchain to eventually
invoke. In the case of iCEStick, we use Migen&#x27;s &lt;code&gt;icestorm&lt;&#x2F;code&gt; backend.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A &lt;code&gt;Platform&lt;&#x2F;code&gt; class definition should also define the class variables
&lt;code&gt;default_clk_name&lt;&#x2F;code&gt; and &lt;code&gt;default_clk_period&lt;&#x2F;code&gt;, which are used by
&lt;code&gt;GenericPlatform&lt;&#x2F;code&gt;. &lt;code&gt;default_clk_name&lt;&#x2F;code&gt; should match the name of a resource in
the &lt;code&gt;io&lt;&#x2F;code&gt; list that represents a clock input to the FPGA.
&lt;code&gt;default_clk_period&lt;&#x2F;code&gt; is used by vendor-specific logic in Migen to create a
clock constraint in nanoseconds for &lt;code&gt;default_clk_name&lt;&#x2F;code&gt;. &lt;em&gt;The default
clock is associated with the &lt;code&gt;sys&lt;&#x2F;code&gt; clock domain for &lt;code&gt;sync&lt;&#x2F;code&gt; statements.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, the &lt;code&gt;create_programmer&lt;&#x2F;code&gt; function should return a vendor-specific
programmer. Adding a programmer is beyond the scope of this article. If a
board can support more than one programming tool, the convention is to return
a programmer based on a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&#x2F;blob&#x2F;master&#x2F;migen&#x2F;build&#x2F;platforms&#x2F;minispartan6.py#L120-L126&quot;&gt;&lt;code&gt;programmer&lt;&#x2F;code&gt; class variable&lt;&#x2F;a&gt;
for the given board. This function can be omitted if no programmer fits, or
one can be created on-the-fly using &lt;code&gt;GenericPlatform.create_programmer&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;finalization&quot;&gt;Finalization&lt;&#x2F;h4&gt;
&lt;p&gt;Some platforms Migen supports, such as the
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&#x2F;blob&#x2F;master&#x2F;migen&#x2F;build&#x2F;platforms&#x2F;lx9_microboard.py#L119-L131&quot;&gt;LX9 Microboard&lt;&#x2F;a&gt;,
have a &lt;code&gt;do_finalize&lt;&#x2F;code&gt; method. Finalization in Migen allows a user to defer
adding logic to their design until overall resource usage is known. In
particular, LX9 Microboard has an Ethernet peripheral, and the Ethernet
clocks should use separate timing constraints from the rest of the design.
The linked code detects whether the Ethernet peripheral was used using
&lt;code&gt;lookup_request(&amp;quot;eth_clocks&amp;quot;)&lt;&#x2F;code&gt; from &lt;code&gt;GenericPlatform&lt;&#x2F;code&gt;, and adds appropriate
platform constraints to the current design to be synthesized if necessary.
If the Ethernet peripheral was not used in the design, the extra constraints
are not added, and the &lt;code&gt;ConstraintError&lt;&#x2F;code&gt; from &lt;code&gt;lookup_request&lt;&#x2F;code&gt; is ignored.&lt;&#x2F;p&gt;
&lt;p&gt;Finalization operates on an internal Migen data structure
called &lt;code&gt;Fragment&lt;&#x2F;code&gt;s. &lt;code&gt;Fragment&lt;&#x2F;code&gt;s require knowledge of Migen internals to use
properly, so for the time being I suggest following the linked example if
you need to add constraints conditionally. Of course, timing constraints and
other User Constraints File data can be added at any point in your design
manually using &lt;code&gt;add_period_constraint&lt;&#x2F;code&gt; and &lt;code&gt;add_platform_command&lt;&#x2F;code&gt;
respectively, both from &lt;code&gt;GenericPlatform&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;iCEStick does not have any peripherals which need special constraints, and
only a single clock; Migen will automatically add a constraint for the
default clock. More importantly, in the case of IceStorm&#x2F;iCEStick, only a
global clock constraint is supported due to limitations in specifying
constraints. Therefore, I omit the &lt;code&gt;do_finalize&lt;&#x2F;code&gt; method for the iCEStick
board file. However, one use I have found for &lt;code&gt;do_finalize&lt;&#x2F;code&gt;
in platforms compatible with IceStorm is to automatically instantiate pins
with pullup resistors enabled. This gets around the limitations of Arachne
PNR&#x27;s constraints file format without needing to instantiate Verilog
primitives directly in the top level of a Migen source file, and I can show
code upon request.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;i-o-and-connectors&quot;&gt;I&#x2F;O and Connectors&lt;&#x2F;h4&gt;
&lt;p&gt;After defining a &lt;code&gt;Platform&lt;&#x2F;code&gt; class for your board, all you need to do
is fill in a list of &lt;code&gt;_io&lt;&#x2F;code&gt; and &lt;code&gt;_connectors&lt;&#x2F;code&gt; in your board file, pass
them into your &lt;code&gt;Platform&lt;&#x2F;code&gt;&#x27;s vendor-specific base class constructor, and
Migen will take care of the rest!&lt;&#x2F;p&gt;
&lt;p&gt;As I stated before, &lt;code&gt;io&lt;&#x2F;code&gt; and &lt;code&gt;connectors&lt;&#x2F;code&gt; input arguments to the
vendor-specific platform constructor are lists of tuples with a specific
format. Let&#x27;s start with an I&#x2F;O tuple:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;io_name, id, Pins(&amp;quot;pin_name&amp;quot;, &amp;quot;pin_name&amp;quot;) or Subsignal(...), IOStandard(&amp;quot;std_name&amp;quot;), Misc(&amp;quot;misc&amp;quot;))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;An &lt;code&gt;io_name&lt;&#x2F;code&gt; is the name of the peripheral, and should match the string
passed into the &lt;code&gt;request&lt;&#x2F;code&gt; function to gain access to the peripheral&#x27;s signals
from Migen. &lt;code&gt;id&lt;&#x2F;code&gt; is a number to distinguish multiple copies of identically-
functioning peripherals, such as LEDs. For simple peripherals, &lt;code&gt;Pins&lt;&#x2F;code&gt; is a
helper class which should contain strings corresponding to the vendor-
specific pin identifiers where the peripheral connects to the FPGA; in the
case of IceStorm, there are just the pin numbers as defined on the package
pinout. I will discuss &lt;code&gt;Subsignal&lt;&#x2F;code&gt;s in the next paragraph. These tuple
entries are used to create inputs and output names in the Migen-generated
Verilog, and provide a variable-name to FPGA pin mapping in a Migen-
generated User Constraints File (UCF)&lt;&#x2F;p&gt;
&lt;p&gt;Without going into excess detail&lt;a id=&quot;rev-fn-6&quot; href=&quot;#fn-6&quot;&gt;&lt;sup&gt;6&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
,
&lt;code&gt;Subsignals&lt;&#x2F;code&gt; are a helper class for resources
that use FPGA pins which can be seperated cleanly by purpose. The inputs to
a &lt;code&gt;Subsignal&lt;&#x2F;code&gt; constructor are identical to an I&#x2F;O tuple entry, except with
&lt;code&gt;id&lt;&#x2F;code&gt; omitted. The net effect for the end user is that a resource is
encapsulated as a class whose Migen &lt;code&gt;Signals&lt;&#x2F;code&gt;
are accessed via the class&#x27; members, i.e. &lt;code&gt;comb += [my_resource.my_sig.eq(5)]&lt;&#x2F;code&gt;.
This is known as a &lt;code&gt;Record&lt;&#x2F;code&gt; in Migen. &lt;code&gt;Records&lt;&#x2F;code&gt; also come with a number of
useful &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&#x2F;blob&#x2F;master&#x2F;migen&#x2F;genlib&#x2F;record.py&quot;&gt;methods&lt;&#x2F;a&gt;
for constructing Migen statements quickly. Think of them as analogous to C
&lt;code&gt;structs&lt;&#x2F;code&gt;. It is up to your judgment whether an I&#x2F;O peripheral should use
&lt;code&gt;Subsignals&lt;&#x2F;code&gt;, but in general, I notice that Migen board files make heavy use
of them.&lt;&#x2F;p&gt;
&lt;p&gt;The remaining inputs to an I&#x2F;O tuple entry are optional. &lt;code&gt;IOStandard&lt;&#x2F;code&gt; is
another helper class which contains a toolchain-specific string that
identifies which voltages&#x2F;logic standard should use. And lastly, the &lt;code&gt;Misc&lt;&#x2F;code&gt;
helper class contains a space-separated string of other information that
should be placed into the User Constraints File along with &lt;code&gt;IOStandard&lt;&#x2F;code&gt;.
Such information includes &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&#x2F;blob&#x2F;master&#x2F;migen&#x2F;build&#x2F;platforms&#x2F;mercury.py#L35&amp;#x27;&quot;&gt;slew rate&lt;&#x2F;a&gt;
and whether pullups should be enabled. &lt;em&gt;These are in fact currently ignored
in the IceStorm toolchain, but for my own reference I have filled them in as
necessary.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A connector tuple is a bit simpler:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(conn_name, &amp;quot;pin_name, pin_name, pin_name,...&amp;quot;)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;conn_name&lt;&#x2F;code&gt; is analogous to &lt;code&gt;io_name&lt;&#x2F;code&gt;. The second element of a connector
tuple is a space-separated string of pin names matching the vendor&#x27;s format
which indicates which pins on the FPGA are associated with that particular
connector. Ideally, the pins should be listed in some order that makes sense
for the connector.&lt;&#x2F;p&gt;
&lt;p&gt;By default, pins that are associated with connectors are not exposed by
the &lt;code&gt;Platform&lt;&#x2F;code&gt; via the &lt;code&gt;request&lt;&#x2F;code&gt; method. Instead, a user needs to
notify the platform that they wish to use the connector as extra I&#x2F;O using
the &lt;code&gt;GenericPlatform.add_extension&lt;&#x2F;code&gt; method. Here is an example adding a
&lt;a href=&quot;https:&#x2F;&#x2F;blog.digilentinc.com&#x2F;new-i2c-standard-for-pmods&#x2F;&quot;&gt;PMOD I2C peripheral&lt;&#x2F;a&gt;
using &lt;code&gt;add_extension&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;my_i2c_device = [
    (&amp;quot;i2c_device&amp;quot;, 0,
        Subsignal(&amp;quot;sdc&amp;quot;, Pins(&amp;quot;PMOD:2&amp;quot;), Misc(&amp;quot;PULLUP&amp;quot;)),
        Subsignal(&amp;quot;sda&amp;quot;, Pins(&amp;quot;PMOD:3&amp;quot;), Misc(&amp;quot;PULLUP&amp;quot;))
    )
]

plat.add_extension(my_i2c_device)
plat.request(&amp;quot;i2c_device&amp;quot;)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that adding a peripheral using &lt;code&gt;add_extension&lt;&#x2F;code&gt; is similar to adding
a peripheral to the &lt;code&gt;io&lt;&#x2F;code&gt; list, except that the &lt;code&gt;Pins()&lt;&#x2F;code&gt; element takes on
the form &lt;code&gt;&amp;quot;conn_name:index&amp;quot;&lt;&#x2F;code&gt;. &lt;code&gt;conn_name&lt;&#x2F;code&gt; should match a tuple in the
&lt;code&gt;connectors&lt;&#x2F;code&gt; list, and &lt;code&gt;index&lt;&#x2F;code&gt; is a zero-based index into the string
of FPGA pins associated with the connector. This allows you to create
peripherals on-the-fly that are (in theory) board and vendor-agnostic.&lt;&#x2F;p&gt;
&lt;p&gt;With the last concepts out of the way, let&#x27;s jump right into creating the
&lt;code&gt;_io&lt;&#x2F;code&gt; and &lt;code&gt;_connectors&lt;&#x2F;code&gt; list for our Platform. Each listed peripheral
is implied to be a tuple inside &lt;code&gt;_io = [...] or _connectors = [...])&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;quot;user_led&amp;quot;, 0, Pins(&amp;quot;99&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
(&amp;quot;user_led&amp;quot;, 1, Pins(&amp;quot;98&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
(&amp;quot;user_led&amp;quot;, 2, Pins(&amp;quot;97&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
(&amp;quot;user_led&amp;quot;, 3, Pins(&amp;quot;96&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
(&amp;quot;user_led&amp;quot;, 4, Pins(&amp;quot;95&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;user_led&lt;&#x2F;code&gt;s are simple peripherals found on just about every development
board; iCEStick has 5 of them, all identical in function (but the 5th one is
green!). Resources with identical function that differ only in a pin should
each be declared in their own tuple, incrementing the &lt;code&gt;id&lt;&#x2F;code&gt; index.&lt;&#x2F;p&gt;
&lt;p&gt;Resource signal names are by convention; if a resource does not yet exist,
it&#x27;s up to you what you want to name the resource. However, I suggest
looking at other board files for prior examples. &lt;code&gt;user_led&lt;&#x2F;code&gt;, &lt;code&gt;user_btn&lt;&#x2F;code&gt;,
&lt;code&gt;serial.rx&lt;&#x2F;code&gt;, &lt;code&gt;serial.tx&lt;&#x2F;code&gt;, &lt;code&gt;spiflash&lt;&#x2F;code&gt;, and &lt;code&gt;audio&lt;&#x2F;code&gt; are all commonly-used I&#x2F;O
names used between board files.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;quot;serial&amp;quot;, 0,
    Subsignal(&amp;quot;rx&amp;quot;, Pins(&amp;quot;9&amp;quot;)),
    Subsignal(&amp;quot;tx&amp;quot;, Pins(&amp;quot;8&amp;quot;), Misc(&amp;quot;PULLUP&amp;quot;)),
    Subsignal(&amp;quot;rts&amp;quot;, Pins(&amp;quot;7&amp;quot;), Misc(&amp;quot;PULLUP&amp;quot;)),
    Subsignal(&amp;quot;cts&amp;quot;, Pins(&amp;quot;4&amp;quot;), Misc(&amp;quot;PULLUP&amp;quot;)),
    Subsignal(&amp;quot;dtr&amp;quot;, Pins(&amp;quot;3&amp;quot;), Misc(&amp;quot;PULLUP&amp;quot;)),
    Subsignal(&amp;quot;dsr&amp;quot;, Pins(&amp;quot;2&amp;quot;), Misc(&amp;quot;PULLUP&amp;quot;)),
    Subsignal(&amp;quot;dcd&amp;quot;, Pins(&amp;quot;1&amp;quot;), Misc(&amp;quot;PULLUP&amp;quot;)),
    IOStandard(&amp;quot;LVTTL&amp;quot;),
),
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next we have another common peripheral- a UART&#x2F;serial port. A UART peripheral
makes sense to divide using &lt;code&gt;Subsignal&lt;&#x2F;code&gt;s, since each pin has a distinct
purpose. Although in practice most users will only use &lt;code&gt;rx&lt;&#x2F;code&gt; and
&lt;code&gt;tx&lt;&#x2F;code&gt;&lt;a id=&quot;rev-fn-7&quot; href=&quot;#fn-7&quot;&gt;&lt;sup&gt;7&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
, I include all possible pins just in case. I don&#x27;t remember
why I included &lt;code&gt;PULLUP&lt;&#x2F;code&gt; as constraints information for a majority of pins.
Note that it&#x27;s perfectly okay to associate a constraint with all &lt;code&gt;Subsignal&lt;&#x2F;code&gt;s at once, as I do for the (unused) &lt;code&gt;IOStandard&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;quot;irda&amp;quot;, 0,
    Subsignal(&amp;quot;rx&amp;quot;, Pins(&amp;quot;106&amp;quot;)),
    Subsignal(&amp;quot;tx&amp;quot;, Pins(&amp;quot;105&amp;quot;)),
    Subsignal(&amp;quot;sd&amp;quot;, Pins(&amp;quot;107&amp;quot;)),
    IOStandard(&amp;quot;LVCMOS33&amp;quot;)
),
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The infrared port on iCEStick is another serial port, sans most of the
control signals. I omit the optional I&#x2F;O tuple&#x2F;&lt;code&gt;Subsignal&lt;&#x2F;code&gt; entries here, and
define the &lt;code&gt;IOStandard&lt;&#x2F;code&gt; similarly to the previous &lt;code&gt;serial&lt;&#x2F;code&gt; peripheral.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;quot;spiflash&amp;quot;, 0,
    Subsignal(&amp;quot;cs_n&amp;quot;, Pins(&amp;quot;71&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
    Subsignal(&amp;quot;clk&amp;quot;, Pins(&amp;quot;70&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
    Subsignal(&amp;quot;mosi&amp;quot;, Pins(&amp;quot;67&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
    Subsignal(&amp;quot;miso&amp;quot;, Pins(&amp;quot;68&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;))
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;spiflash&lt;&#x2F;code&gt; and its &lt;code&gt;Subsignal&lt;&#x2F;code&gt; have standardized, self-explanatory names.
I suggest using these signal names when appropriate for all peripherals
connected via an &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Serial_Peripheral_Interface_Bus&quot;&gt;SPI bus&lt;&#x2F;a&gt;.
I don&#x27;t remember why I added the &lt;code&gt;IOStandard&lt;&#x2F;code&gt; per-signal instead of all-at-
once here, but the net effect would be the same either way if IceStorm made
use of &lt;code&gt;IOStandard&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;quot;clk12&amp;quot;, 0, Pins(&amp;quot;21&amp;quot;), IOStandard(&amp;quot;LVCMOS33&amp;quot;)),
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Clock signals should be included as well, with at least one clock&#x27;s &lt;code&gt;io_name&lt;&#x2F;code&gt;
matching the &lt;code&gt;default_clk_name&lt;&#x2F;code&gt;. Migen may automatically &lt;code&gt;request&lt;&#x2F;code&gt; a clock
for your design if certain conditions are met; for now, assume you don&#x27;t have
to &lt;code&gt;request&lt;&#x2F;code&gt; the clock.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;quot;GPIO0&amp;quot;, &amp;quot;44 45 47 48 56 60 61 62&amp;quot;),
(&amp;quot;GPIO1&amp;quot;, &amp;quot;119 118 117 116 115 114 113 112&amp;quot;),
(&amp;quot;PMOD&amp;quot;, &amp;quot;78 79 80 81 87 88 90 91&amp;quot;),
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And lastly we have the connectors. The connector pins for &lt;code&gt;GPIO0-1&lt;&#x2F;code&gt; and
&lt;code&gt;PMOD&lt;&#x2F;code&gt; are ordered in increasing pin order, which matches the order they
are laid out on their respective connectors. &lt;em&gt;This is a happy coincidence.
Make sure to check your schematic and declare FPGA pins in connector order,
not the other way around!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-our-design-for-our-new-board&quot;&gt;Building Our Design For Our &amp;quot;New&amp;quot; Board&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have created our board file, we now need to write the remaining
logic to attach the board&#x27;s I&#x2F;O to our &lt;a href=&quot;#leveraging-python-to-build-
fpga-applications&quot;&gt;Rot top level&lt;&#x2F;a&gt;. Assuming
the &lt;code&gt;Rot&lt;&#x2F;code&gt; module is already defined, we can create a script that will
synthesize our design like so:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;if __name__ == &amp;quot;__main__&amp;quot;:
    plat = icestick.Platform()
    m = Rot()
    m.comb += [plat.request(&amp;quot;user_led&amp;quot;).eq(l) for l in [m.d1, m.d2, m.d3, m.d4, m.d5]]
    plat.build(m, run=True, build_dir=&amp;quot;rot&amp;quot;, build_name=&amp;quot;rot_migen&amp;quot;)
    plat.create_programmer().flash(0, &amp;quot;rot&amp;#x2F;rot_migen.bin&amp;quot;)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once again, I will explain each line. It should be appended to the &lt;code&gt;Rot&lt;&#x2F;code&gt;
top level and then run using your Python 3 interpreter:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;plat = icestick.Platform()
m = Rot()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We create our platform and our &lt;code&gt;Rot&lt;&#x2F;code&gt; module here. &lt;code&gt;plat&lt;&#x2F;code&gt; contains a number
of useful helper methods that help customize what is eventually sent to the
synthesis flow.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;m.comb += [plat.request(&amp;quot;user_led&amp;quot;).eq(l) for l in [m.d1, m.d2, m.d3, m.d4, m.d5]]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This list comprehension is how we actually connect our I&#x2F;O to the LED
rotation module. &lt;code&gt;plat.request(&amp;quot;user_led&amp;quot;)&lt;&#x2F;code&gt; will create a Migen &lt;code&gt;Signal&lt;&#x2F;code&gt;
or &lt;code&gt;Record&lt;&#x2F;code&gt; which can be used to assign to &lt;code&gt;Signals&lt;&#x2F;code&gt; and &lt;code&gt;Records&lt;&#x2F;code&gt; in an
existing &lt;code&gt;Module&lt;&#x2F;code&gt;. &lt;code&gt;GenericPlatform&lt;&#x2F;code&gt; does bookkeeping to figure out which
&lt;code&gt;Signals&lt;&#x2F;code&gt; should be considered I&#x2F;Os to&#x2F;from the generated Verilog top
level (and consequently, mapped to a User Constraints File).&lt;&#x2F;p&gt;
&lt;p&gt;Repeatedly calling &lt;code&gt;request&lt;&#x2F;code&gt; for a given resource
name will return a new &lt;code&gt;Signal&lt;&#x2F;code&gt; or &lt;code&gt;Record&lt;&#x2F;code&gt; of the same type, throwing a
&lt;code&gt;ConstraintError&lt;&#x2F;code&gt; exception if there are no more available resources&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;plat.build(m, run=True, build_dir=&amp;quot;rot&amp;quot;, build_name=&amp;quot;rot_migen&amp;quot;)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;GenericPlatform.build&lt;&#x2F;code&gt; function will emit Verilog output, a User
Constraints File specific to your design, a batch file or shell script to
invoke the synthesis flow, and any other files required as input to the
synthesis toolchain. Optionally, Migen will invoke the generated script to
create your design if input argument &lt;code&gt;run&lt;&#x2F;code&gt; is true. &lt;code&gt;build_dir&lt;&#x2F;code&gt; is self-
explanatory, and &lt;code&gt;build_name&lt;&#x2F;code&gt; controls the filename
of generated files.&lt;&#x2F;p&gt;
&lt;p&gt;I pass in my top level &lt;code&gt;Rot&lt;&#x2F;code&gt; &lt;code&gt;Module&lt;&#x2F;code&gt; to the &lt;code&gt;build&lt;&#x2F;code&gt; function.
&lt;code&gt;GenericPlatform&lt;&#x2F;code&gt; has internal logic to detect which signals were declared
as I&#x2F;O in the board file while generating Verilog input and output
arguments.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;plat.create_programmer().flash(0, &amp;quot;rot&amp;#x2F;rot_migen.bin&amp;quot;)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This line is optional, but if included, Migen will invoke the &lt;code&gt;iceprog&lt;&#x2F;code&gt;
FTDI MPSSE programmer for iCEStick automatically after creating a
bitstream using IceStorm. The first input argument is the start address to
start programming, and the second argument is the name of the output
bitstream. The name can be inferred from &lt;code&gt;build_name&lt;&#x2F;code&gt; and the toolchain&#x27;s
default extension for bitstreams.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If all goes well, and you were following along, you should now have a
blinking LED example on your iCEStick! The final iCEStick platform board
file is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&#x2F;blob&#x2F;master&#x2F;migen&#x2F;build&#x2F;platforms&#x2F;icestick.py&quot;&gt;here&lt;&#x2F;a&gt;,
which you can use as a reference, and I&#x27;ve made the &lt;code&gt;Rot&lt;&#x2F;code&gt; top level available
as a &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;cr1901&#x2F;afc0442405fa4727802182ff9eac0e84&quot;&gt;gist&lt;&#x2F;a&gt;.
Take a look at the output files Migen generated, including the output
Verilog and User Constraints File, to get a feel of how our &amp;quot;shiny new&amp;quot;
board file was used!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;happy-hacking&quot;&gt;Happy Hacking!&lt;&#x2F;h2&gt;
&lt;p&gt;If you have read up to this point, you now have some grasp on the Migen
framework, and can now start using Migen in your own designs on your own
development (or even deployed) designs!&lt;&#x2F;p&gt;
&lt;p&gt;Porting Migen to support your design takes relatively little effort, and you
will quickly make up the time spent porting. Besides automating HDL idioms
that are tedious to write by hand (such as FSMs), and generating Verilog
that is free of certain bug classes, Migen saves time  as it automates
generating input files and build commands for your synthesis toolchain, and
then invoking the synthesis flow automatically. Additionally, if you write
your top-level &lt;code&gt;Module&lt;&#x2F;code&gt; and glue code correctly, you can have a single HDL
design that runs on all platforms that Migen supports, even between vendors,
with much less effort than is required to do the same in Verilog
alone!&lt;a id=&quot;rev-fn-8&quot; href=&quot;#fn-8&quot;&gt;&lt;sup&gt;8&lt;&#x2F;sup&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Migen is certainly a step in the right direction for the future of hacking
with FPGAs, and I hope you as the reader give it a try with your Shiny New
Development Board like I did, and see whether your experiences were as
positive as mine.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;d like to thank &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;m_labs_ltd&quot;&gt;Sébastien Bourdeauducq&lt;&#x2F;a&gt;,
the primary architect and maintainer of Migen, for looking over an initial
version of this post and offering feedback.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;&#x2F;h2&gt;
&lt;p id=&quot;fn-1&quot;&gt;&lt;a href=&quot;#rev-fn-1&quot;&gt;1&lt;&#x2F;a&gt; For better or worse, emitting Verilog requires someone intimately
familiar with the Verilog specification. Like all good specifications, it is
terse, and requires a lot of memorization. But both Yosys and Migen are by
and large the work of one individual each, so it can be done.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-2&quot;&gt;&lt;a href=&quot;#rev-fn-2&quot;&gt;2&lt;&#x2F;a&gt; In the interest of fairness, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;olofk&#x2F;fusesoc&quot;&gt;fusesoc&lt;&#x2F;a&gt;
exists now to alleviate the burdern of Verilog code-sharing. I was unaware of
its existence when I started using FPGAs again. I think Migen build i
ntegration would be an interesting project; in general, I find
setting up a board-agnostic Migen design easier than w&#x2F; FuseSoC, but
importing Verilog code as a package is still incredibly useful.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-3&quot;&gt;&lt;a href=&quot;#rev-fn-3&quot;&gt;3&lt;&#x2F;a&gt; I erroneously assumed that because the Xilinx
backend code can use the yosys Verilog compiler that there was support for
support for yosys and by extension IceStorm in the Lattice backend. Not
having had any Lattice FPGAs before iCEstick (yes, I jumped on the FOSS FPGA
toolchain bandwagon), I never actually bothered to check beforehand!&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-4&quot;&gt;&lt;a href=&quot;#rev-fn-4&quot;&gt;4&lt;&#x2F;a&gt; Historically, I&#x27;ve found that IceStorm Verilog code samples only define the
pins their designs actually use. Unlike Quartus or ISE, Arachne PNR will
error out instead of warn if constraints that are defined aren&#x27;t actually
used. Migen doesn&#x27;t have this issue because it will only generate constraints
that are actually used for Arachne PNR.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-5&quot;&gt;&lt;a href=&quot;#rev-fn-5&quot;&gt;5&lt;&#x2F;a&gt; Each &lt;code&gt;Platform&lt;&#x2F;code&gt; consists of a single FPGA and attached
peripherals. I assume a board with multiple FPGAs should implement a
&lt;code&gt;Platform&lt;&#x2F;code&gt; for each FPGA in a single board file.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-6&quot;&gt;&lt;a href=&quot;#rev-fn-6&quot;&gt;6&lt;&#x2F;a&gt; Subsignals also &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;m-labs&#x2F;migen&#x2F;blob&#x2F;master&#x2F;migen&#x2F;build&#x2F;generic_platform.py#L209-L221&quot;&gt;modify&lt;&#x2F;a&gt;
how signal names are generated for the remainder of the synthesis toolchain.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-7&quot;&gt;&lt;a href=&quot;#rev-fn-7&quot;&gt;7&lt;&#x2F;a&gt; iCEStick disappoints me in that the engineers wired &lt;em&gt;nearly&lt;&#x2F;em&gt; all the
connections required to use the FT2232H in FT245 queue mode, which is much
faster than a serial port; most dev boards do not bother connecting anything
besides serial TX and RX, and possibly RTS&#x2F;CTS. But alas, there&#x27;s still not
enough control connections to use queue mode.&lt;&#x2F;p&gt;
&lt;p id=&quot;fn-8&quot;&gt;&lt;a href=&quot;#rev-fn-8&quot;&gt;8&lt;&#x2F;a&gt; Portability of code gives me joy, and HDL portability is no exception.&lt;&#x2F;p&gt;
</content>
	</entry>
  

  
	<entry xml:lang="en">
		<title>NMOS IC Reverse Engineering</title>
		<published>2016-10-12T00:00:00+00:00</published>
		<updated>2016-10-12T00:00:00+00:00</updated>
		<link href="https://www.wdj-consulting.com/blog/nmos-sample/" type="text/html"/>
		<id>https://www.wdj-consulting.com/blog/nmos-sample/</id>
		<content type="html">&lt;h1 id=&quot;nmos-ic-reverse-engineering&quot;&gt;NMOS IC Reverse Engineering&lt;&#x2F;h1&gt;
&lt;p&gt;Recently, I was &lt;del&gt;tricked&lt;&#x2F;del&gt; &lt;ins&gt;talked&lt;&#x2F;ins&gt; into examining the die
of an integrated circuit (IC)- the YM2151, a single-chip FM synthesizer. I
like these old Yamaha chips, so I don&#x27;t really mind doing it, but it&#x27;s
definitely a long term project that I expect to take months. However, one
doesn&#x27;t need to RE a significant portion of an IC to understand the basics
of RE. Information about a chip&#x27;s operation can be gleamed by examining
small units, and predicting their relation to each other.&lt;&#x2F;p&gt;
&lt;p&gt;Information on doing IC reverse-engineering is still kind of limited,
although projects like &lt;a href=&quot;https:&#x2F;&#x2F;siliconpr0n.org&quot;&gt;siliconpr0n&lt;&#x2F;a&gt; and whatever
&lt;a href=&quot;http:&#x2F;&#x2F;www.righto.com&#x2F;2014&#x2F;10&#x2F;how-z80s-registers-are-implemented-down.html&quot;&gt;Ken Shirrif&lt;&#x2F;a&gt;
is working on at any given time are changing that. Since I am learning how
to RE ICs, I decided to document how I decoded a small ROM in the YM2151
that I &lt;em&gt;suspect&lt;&#x2F;em&gt; is being used as part of the control state machine. This
small ROM demonstrates the basics of REing ICs, including:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Separating the various layers of an IC die.&lt;&#x2F;li&gt;
&lt;li&gt;Mapping ROM inputs and outputs.&lt;&#x2F;li&gt;
&lt;li&gt;Manually reading out the ROM contents.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;obtaining-a-die-image&quot;&gt;Obtaining a Die Image&lt;&#x2F;h2&gt;
&lt;p&gt;Before we can examine an IC die, we have to actually digitally capture an
image of the die. I will not be discussing this in detail, but getting a die
image typically involves:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Removing the IC package with corrosive chemicals, called decap.&lt;&#x2F;li&gt;
&lt;li&gt;Taking a number of pictures using a digital camera with a microscope.&lt;&#x2F;li&gt;
&lt;li&gt;Stitching all the individual images together to produce a full map of the
IC die with individual transistors visible.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I really don&#x27;t have access to the equipment to do this even at a small scale,
but luckily this work was done previously in the case of YM2151 (20x
magnification). I defer to Travis Goodspeed&#x27;s article in section 9 of &lt;a href=&quot;https:&#x2F;&#x2F;archive.org&#x2F;details&#x2F;pocorgtfo04&quot;&gt;POC||GTFO #4&lt;&#x2F;a&gt;)
if interested in doing decap yourself.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;YM2151 die image.&quot; src=&quot;ym2151_die.png&quot;&gt;
&lt;figcaption&gt;Die image of YM2151 at 20x magnification, full die. The full-size
image is 617MB &lt;a href=&quot;https:&#x2F;&#x2F;siliconpr0n.org&#x2F;map&#x2F;yamaha&#x2F;ym2151&#x2F;mz_mit20x&#x2F;&quot;&gt;Source&lt;&#x2F;a&gt;.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;what-is-nmos&quot;&gt;What is NMOS?&lt;&#x2F;h2&gt;
&lt;p&gt;The YM2151 uses an &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Depletion-load_NMOS_logic&quot;&gt;NMOS process&lt;&#x2F;a&gt;.
The NMOS logic family uses n-type metal oxide semiconductor field-effect
transistors (MOSFETs) to create digital logic gates. MOSFETs are four-
terminal devices that either permit or prevent current from flowing between
two of the terminals, called the drain and source, depending on the voltage
of the third terminal, called the gate, relative to either the drain or
source. The fourth terminal, called the body, is negligible for now.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mosfet-schematic-symbols&quot;&gt;MOSFET Schematic Symbols&lt;&#x2F;h3&gt;
&lt;p&gt;The below picture shows two different types of MOSFETS:
n-type depletion and n-type enhancement mode. On each MOSFET, the center
terminal attached to the left column is the gate input. The source and drain
are the bottom and top terminals, respectively. Each MOSFET has the source
connected to a fourth terminal, which in turn connects to an arrow pointing
inward to the right column, indicating n-type MOSFETs. Put simply, the arrow
direction corresponds to device polarity at a specific area within the
MOSFET. The source-body connection is a side effect of the MOSFET symbol I
used, and we can ignore it. A segmented right column represents enhancement
mode, and a solid right column represents depletion mode.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Two NMOS Circuits showing the schematic symbols for MOSFETs.&quot; src=&quot;NMOS-Intro.png&quot;&gt;
&lt;figcaption&gt;A depletion and enhancement mode n-type MOSFET circuit and an equivalent
circuit. The depletion mode MOSFET acts as a resistor, and the enhancement
mode MOSFET as a switch. A quick rationale on the schematic symbol is given
in the above paragraph. On is 5V, off 0V.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;A MOSFET is a symmetrical device, so drain and source can be
labeled arbitrarily. However, according to Sedra and Smith, drain is by
convention always at a higher voltage than source.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;quick-theory-of-mosfets&quot;&gt;Quick &amp;quot;Theory&amp;quot; of MOSFETs&lt;&#x2F;h3&gt;
&lt;p&gt;&amp;quot;n-type&amp;quot; refers to the properties of the silicon that carries current through
a MOSFET. For the purposes of this blog post, I need not go into its
properties.&lt;&#x2F;p&gt;
&lt;p&gt;In hand-calculations, a MOSFET has at least three &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;MOSFET#Modes_of_operation&quot;&gt;modes of operation&lt;&#x2F;a&gt;.
I&#x27;m not interested in doing simulation in this blog post (maybe in the
future I will!), but for completeness, the regions are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Cutoff, no conduction from drain to source.&lt;&#x2F;li&gt;
&lt;li&gt;Triode, linear region, acts like a resistor.&lt;&#x2F;li&gt;
&lt;li&gt;Saturated, increasing gate voltage does not increase current.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;!-- &lt;p&gt;When operating MOSFETs as gates&#x2F;switches, we are typically
interested in cutoff and the linear region so we can create voltage dividers
with well-defined resistances. This way, the entire circuit can operate at
the proper on and off voltage ranges. This is called &quot;ratioed logic&quot;,
according to LINK(`http:&#x2F;&#x2F;web.cs.mun.ca&#x2F;~paul&#x2F;transistors&#x2F;node1.html&#x27;, `this
useful page&#x27;)&lt;&#x2F;p&gt;
--&gt;
&lt;!-- http:&#x2F;&#x2F;ece424.cankaya.edu.tr&#x2F;uploads&#x2F;files&#x2F;Chap16-1-NMOS-Inverter.pdf --&gt;
&lt;p&gt;Both enchancement mode and depletion mode MOSFETs are used in the NMOS logic
family. Depletion-mode MOSFETs conduct current even when the gate and
source is at the same voltage; the gate must have a lower voltage than the
source for cutoff to occur. They are commonly, &lt;em&gt;but not exclusively&lt;&#x2F;em&gt;, used
as pullup resistors, operating in the linear region and always on.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, enhancement-mode MOSFETs are used to implement logic
gates by either allowing current to pass or not. They operate in cutoff and
saturation. The gate must be at a higher voltage than the source to conduct
current. Voltage drop tends to be negligible in the enhancement-mode
MOSFETs; see ratioed logic in the previous link for more information.&lt;&#x2F;p&gt;
&lt;p&gt;With the above two paragraphs in mind, here is an exercise: How would you
implement an NMOS NOR gate (hint: MOSFETs in parallel)? NAND gate (hint:
MOSFETs in series)? Inverter (NOT) (hint: Look carefully at my MOSFET image)?
Notice how gates with inverters are easiest to implement?
I wonder if that&#x27;s a reason active-low inputs and outputs are so common in
these chips?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;am-i-dealing-with-nmos&quot;&gt;Am &lt;em&gt;I&lt;&#x2F;em&gt; Dealing With NMOS?&lt;&#x2F;h3&gt;
&lt;p&gt;How does one detect an NMOS chip? To be honest, I was told ahead of time that
the YM2151 uses an NMOS process. The year that this chip was first produced
(1984-5) is also a hint. Howvever, when compared to &lt;a href=&quot;https:&#x2F;&#x2F;siliconpr0n.org&#x2F;map&#x2F;yamaha&#x2F;ymf262-m&#x2F;mz_mit50xn&quot;&gt;CMOS die&lt;&#x2F;a&gt;
of a similar-era Yamaha chip, I notice a few differences:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Only one size of via in a CMOS die, many in NMOS.&lt;&#x2F;li&gt;
&lt;li&gt;No obvious indication of pullup MOSFETs in CMOS, prevalent in NMOS.&lt;&#x2F;li&gt;
&lt;li&gt;The CMOS die is more neatly organized, compared to NMOS.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Right now, the above list probably isn&#x27;t all that meaningful :P. I&#x27;ll discuss
what I mean in the next section. As you may expect, my method of analysis
only works for ICs made with an NMOS process. However, this is still useful
for preserving many old chips where fully emulating their behavior is
desired (YM2151) or even required (security chips) to preserve hardware.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ic-layers&quot;&gt;IC Layers&lt;&#x2F;h2&gt;
&lt;p&gt;I decided to digitize (or vectorize) a ROM at the top of the chip,
approximately one third of the length longways.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Empty section of YM2151 die to be vectorized.&quot; src=&quot;Capture_1.PNG&quot;&gt;
&lt;figcaption&gt;Section of the YM2151 I intend to vectorize, before we begin any work.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;ICs- well, not cutting-edge ones anyway- tend to be made by applying planar
layers of conducting and semiconductor material. Therefore, it tends to be
safe to represent each layer of material as a 2d layer in a vector image,
not worrying about layer depth or wells that would be apparent in a 3d
cutaway. Each layer can thus be inferred and by examining intersections and
outlines left over during decap.&lt;&#x2F;p&gt;
&lt;p&gt;I can tell the above is a ROM from the equal-width strips of metal (remember,
metal tends to be consistently colored) and the circular &amp;quot;holes&amp;quot; distributed
throughout the metal strips. These &amp;quot;holes&amp;quot; are properly referred to as
&amp;quot;vias&amp;quot;. Vias are drilled holes, forming a connection with any layer that
intersects at their location.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, there exist buried contacts that directly connect (typically?
always?) the poly and active layers. A metal layer can be placed on top of a
buried contact without creating a connection. Buried contacts can frequently
be identified by a light square outline where poly and active intersect, but
this is not guaranteed. Sometimes buried contacts must be inferred from
context. The takeaway here is: vias and buried contacts form two additional
logical layers that need to be vectorized.&lt;&#x2F;p&gt;
&lt;p&gt;Buried contacts are different from MOSFET gates b&#x2F;c a MOSFET gate is not a
direct connection. A gate has a layer of insulating oxide separating the
polysilicon and the active layer&#x2F;wells underneath. However, it tends to be
obvious from context and visual inspection which type of connection exists,
even without having a 3d cutaway view which would show the differences.&lt;&#x2F;p&gt;
&lt;p&gt;Not all ICs have the same number of layers or the same layer material type,
but in the case of NMOS, it&#x27;s safe to divide an IC die into at least three
layers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Metal, conducting material. Typically a whiteish hue that stands out, in
the case of aluminum (which is what YM2151 uses).&lt;&#x2F;li&gt;
&lt;li&gt;Polysilicon, pure silicon. Used for MOSFET gates. Color cannot be assumed,
but outlines are obvious.&lt;&#x2F;li&gt;
&lt;li&gt;Active, doped silicon used for drain and source. Color cannot be assumed,
but outlines are obvious.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;let-s-digitize&quot;&gt;Let&#x27;s Digitize!&lt;&#x2F;h2&gt;
&lt;p&gt;With the above out of the way, let&#x27;s digitize the metal layer and vias of
the ROM. Please note that in some images that I may miss a section :P. I
correct it in a later step unless otherwise noted.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;caption&gt;Layer Color Table&lt;&#x2F;caption&gt;
&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Layer&lt;&#x2F;td&gt;&lt;td&gt;Color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Metal&lt;&#x2F;td&gt;&lt;td&gt;Yellow&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Polysilicon&lt;&#x2F;td&gt;&lt;td&gt;Red&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Active&lt;&#x2F;td&gt;&lt;td&gt;Blue&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Via&lt;&#x2F;td&gt;&lt;td&gt;Green&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Buried Contact&lt;&#x2F;td&gt;&lt;td&gt;Pink&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;figure&gt;
&lt;img alt=&quot;ROM inputs digitized.&quot; src=&quot;Capture_2.PNG&quot;&gt;
&lt;figcaption&gt;Metal layer is easily visible. Here, the ROM matrix, ROM inputs and power
and ground rails are digitized. I will show how to infer the latter three
shortly.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure&gt;
&lt;img alt=&quot;Vias vectorized.&quot; src=&quot;Capture_3.PNG&quot;&gt;
&lt;figcaption&gt;Here, we have digitized most of the vias of interest.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The image has become a bit crowded after digitizing the metal and via
layers, so for the time being I will disable them.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start looking for transistors. I personally like to start with
pullups, becausepullups have a very distinctive shape on an NMOS die, and
the power rail can also be inferred. As mentioned before, NMOS pullups are
depletion-mode MOSFETs, and they have very large gate widths to create a
current path even without an applied gate voltage. Additionally, to provide
the pullup effect, there exists a connection to the active layer on the
source side of the MOSFET that
looks like a hook.&lt;&#x2F;p&gt;
&lt;p&gt;Pullup MOSFETs thus tend to look like &amp;quot;rectangles with a hook&amp;quot;, with
slightly more emphasis on the hook to create the source-to-gate connection.
We can now safely vectorize the pullups, and immediate polysilicon traces
emanating from the pullups.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;First depletion mode MOSFET vectorized!&quot; src=&quot;Capture_4.PNG&quot;&gt;
&lt;figcaption&gt;We have vectorized our first MOSFET gate! A depletion mode MOSFET at that.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure&gt;
&lt;img alt=&quot;All depletion mode MOSFETs vectorized!&quot; src=&quot;Capture_5.PNG&quot;&gt;
&lt;figcaption&gt;All pullups, polysilicon layer, vectorized. By process of elimination we
know the remaining two sides of the ROM are the inputs or outputs.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;In our depletion mode pullups, the active layer consisting of source and
drain runs through the center of the wide gate. We can trace out the active
layer completely now, but I deliberately stopped short. The crossing of two
layers near the pullups at the bottom is signficant.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Starting to vectorize the active layer.&quot; src=&quot;Capture_6.PNG&quot;&gt;
&lt;figcaption&gt;We can start vectorizing the active layer that forms the source and drain of
the pullups.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Notice that each strip of the metal layer connecting at the top of the ROM
terminates in a via. The via connects to a layer below, either active or
poly, that runs across the length of the ROM. This layer abruptly terminates
after crossing the active layer that directly connects to the pullup&#x27;s
source. There was a transistor formed due to that crossing during
fabrication! We can safely assume them to be enhancement mode
transistors used as switches due to the gate size.&lt;&#x2F;p&gt;
&lt;p&gt;Our unknown layer &lt;em&gt;must&lt;&#x2F;em&gt; be poly because they form the gate of a transistor.
Furthermore, Because the metal at the top of the ROM attaches directly to
the gate of a transistor for each input, the top of the ROM must be our
input. By process of elimination, the output of our ROM is on the right.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Extraction of enhancement-mode transistors.&quot; src=&quot;Capture_7.PNG&quot;&gt;
&lt;figcaption&gt;Our first enhancement mode transistors. Notice how much smaller the gate is
for each compared to depletion mode.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now I decided to take a break from the poly and vectorize the buried
contacts. The buried contacts in this section are all visible as squares at
poly and active crossings. Since a pullup &lt;em&gt;must&lt;&#x2F;em&gt; have a buried contact to
connect the gate and source, let&#x27;s start with the pullups. Can you find the
outline of the other buried contacts before scrolling to the second image?&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;A few buried contacts have been vectorized.&quot; src=&quot;Capture_8.PNG&quot;&gt;
&lt;figcaption&gt;Some buried contacts due to pullup connections have been digitized.
All buried contacts of interest in this section are visible.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure&gt;
&lt;img alt=&quot;Same image as above, but with all remaining buried contacts vectorized.&quot; src=&quot;Capture_9.PNG&quot;&gt;
&lt;figcaption&gt;Remaining buried contacts of interest digitized.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now, I finish the active layer, which by process of elimination is going to
be the remaining unvectorized traces. These form a number of enhancement-
mode MOSFET switches distributed through the ROM matrix. &lt;em&gt;Anywhere the poly
crosses the active layer is a transitor!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Active layer has been digitized. The ROM is fully vectorized, but not all layers are visible.&quot; src=&quot;Capture_11.PNG&quot;&gt;
&lt;figcaption&gt;Remaining active layer forming the ROM matrix digitized. I accidentally
missed part of the active layer at the bottom of the second column when
taking these images.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;With the active layer (minus my mistake) digitized, the ROM has been fully
vectorized. I re-enable the metal and via layers to show the final result.
Additionally, I vectorized a few more sections all all layers, only one of
which is relevant to the ROM.&lt;&#x2F;p&gt;
&lt;p&gt;The thick metal trace below the ROM which connects to the active layer of
the ROM matrix (at the source terminals of the enhancement mode MOSFETs
immediately attached to depletion mode pullups), is in fact a ground trace.
From experience, I can expect the active columns running through
the ROM matrix to be connected to ground. I will explore why in the next
section.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Fully vectorized ROM with all layers visible.&quot; src=&quot;Capture_13.PNG&quot;&gt;
&lt;figcaption&gt;Fully vectorized ROM, plus some extra connections.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;schematic-capture&quot;&gt;Schematic Capture&lt;&#x2F;h2&gt;
&lt;p&gt;I am arbitrarily labeling the leftmost and bottommost bus lines the LSBs of
the input and output, respectively. Thus, bit positions increase as one
travels from left to right and bottom to top of the ROM matrix.&lt;&#x2F;p&gt;
&lt;p&gt;Initially, I had intermediate images of my progress creating the schematic.
Unfortunately, for various formatting reasons (repeated transistor numbers,
inconsistent resolution), the intermediate images didn&#x27;t turn out how I liked, so I removed them.&lt;&#x2F;p&gt;
&lt;p&gt;I created the schematic in a manner very similar to how I vectorized
starting with the pullups, then adding the inputs and their corresponding
MOSFET switch connections. Then I added the ROM outputs. Next, I added the
remaining wires that run down the ROM matrix columnwise, which attach to the
source of the switch enhancement mode MOSFETs and pullup depletion-mode
MOSFETs respectively. I finished schematic capture by adding the additional
switch transistors that exist anywhere the active layer crosses poly within
the matrix.&lt;&#x2F;p&gt;
&lt;p&gt;For each trio of column wires, the leftmost wire is the ROM input, the middle
wire is the active layer running across each row of the ROM matrix, and the
rightmost wire is attached to its corresponding pullup.&lt;&#x2F;p&gt;
&lt;!-- &lt;strong&gt;I didn&#x27;t realize that the thick trace below the ROM was a
ground trace until after I was done creating the schematic. So GND is not
present until the last image.&lt;&#x2F;strong&gt;

SCHEM(1, `Schematic considering only pullups.&#x27;)
SCHEM(2, `Input bus lines and MOSFET inteface added to schematic.&#x27;)
SCHEM(3, `&#x27;)
SCHEM(4, `&#x27;) --&gt;
&lt;figure&gt;
&lt;img alt=&quot;Fully extracted schematic of our vectorized region of interest in terms of MOSFETs.&quot; src=&quot;NMOS-RE-Sample_6.png&quot;&gt;
&lt;figcaption&gt;Full schematic. This 5x10 ROM contains nearly 60 transistors!&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I made a mistake when drawing the above schematic. By convention, the source
should be at a lower voltage than the drain, but for the transistors within
the ROM matrix, I accidentally swapped source and drain. In an IC, this does
not matter, as a MOSFET is symmetric and swapping source and drain does not
affect device operation (for our intents and purposes). However, without
this disclaimer, I&#x27;m sure I will confuse people. Perhaps the drain and
source distinction is best ignored for this schematic.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reading-out-the-rom-contents&quot;&gt;Reading Out the ROM Contents&lt;&#x2F;h2&gt;
&lt;p&gt;With the above schematic, we can gleam some interesting information about
how the ROM works. I assume the ROM inputs are either always a valid 1 or 0,
because I am assuming that this ROM is driven by internal control logic.&lt;&#x2F;p&gt;
&lt;p&gt;If any given bus input is 0, the input will not turn on the switch
transistors at the bottom of the ROM, placed immediately before pullups.
This means that the pullups are not actively driven low, and the source
terminal of the pullups remains at a high logic value. The logical high is
propogated to all transistors whose gates are connected to the pullup
source; these transistors are on. All transistors whose gates are
attached to the bus input are in cutoff and have no effect on circuit
operation.&lt;&#x2F;p&gt;
&lt;p&gt;Notice that the metal corresponding to each bit output is attached to all
transistors in a row in parallel. This means that if &lt;em&gt;any&lt;&#x2F;em&gt; of the
transistors in a given row are on, the entire metal row, and consequently
the output, is pulled low as well. This is also called &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Wired_logic_connection&quot;&gt;wired-AND&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In a similar manner, if any given bus input is 1, the input will turn on the
switch transistor and the logical level at the pullup source will be driven
low. All transistors whose gates are attached to the pullup source terminal
will be in cutoff and will not drive the metal strips low. However, because
the bus input is logical high, any transistors whose gate is attached to the
bus input will drive its corresponding bit output low.&lt;&#x2F;p&gt;
&lt;p&gt;We now have enough information to devise boolean expressions and a truth
table for the entire ROM!&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;caption&gt;Output Bits Driven Low For Each Input Bus Line&lt;&#x2F;caption&gt;
&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Bus Line+Value&lt;&#x2F;td&gt; &lt;td&gt;Output Bits Driven Low&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;0&lt;&#x2F;sub&gt; High&lt;&#x2F;td&gt; &lt;td&gt;O&lt;sub&gt;1&lt;&#x2F;sub&gt;, O&lt;sub&gt;6&lt;&#x2F;sub&gt;, O&lt;sub&gt;7&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;0&lt;&#x2F;sub&gt; Low&lt;&#x2F;td&gt;  &lt;td&gt;O&lt;sub&gt;0&lt;&#x2F;sub&gt;, O&lt;sub&gt;5&lt;&#x2F;sub&gt;, O&lt;sub&gt;8&lt;&#x2F;sub&gt;, O&lt;sub&gt;9&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;1&lt;&#x2F;sub&gt; High&lt;&#x2F;td&gt; &lt;td&gt;O&lt;sub&gt;0&lt;&#x2F;sub&gt;, O&lt;sub&gt;3&lt;&#x2F;sub&gt;, O&lt;sub&gt;6&lt;&#x2F;sub&gt;, O&lt;sub&gt;7&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;1&lt;&#x2F;sub&gt; Low&lt;&#x2F;td&gt;  &lt;td&gt;O&lt;sub&gt;1&lt;&#x2F;sub&gt;, O&lt;sub&gt;4&lt;&#x2F;sub&gt;, O&lt;sub&gt;5&lt;&#x2F;sub&gt;, O&lt;sub&gt;8&lt;&#x2F;sub&gt;, O&lt;sub&gt;9&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;2&lt;&#x2F;sub&gt; High&lt;&#x2F;td&gt; &lt;td&gt;O&lt;sub&gt;2&lt;&#x2F;sub&gt;, O&lt;sub&gt;5&lt;&#x2F;sub&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;2&lt;&#x2F;sub&gt; Low&lt;&#x2F;td&gt;  &lt;td&gt;O&lt;sub&gt;0&lt;&#x2F;sub&gt;, O&lt;sub&gt;1&lt;&#x2F;sub&gt;, O&lt;sub&gt;3&lt;&#x2F;sub&gt;, O&lt;sub&gt;4&lt;&#x2F;sub&gt;, O&lt;sub&gt;6&lt;&#x2F;sub&gt;, O&lt;sub&gt;7&lt;&#x2F;sub&gt;, O&lt;sub&gt;8&lt;&#x2F;sub&gt;, O&lt;sub&gt;9&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;3&lt;&#x2F;sub&gt; High&lt;&#x2F;td&gt; &lt;td&gt;O&lt;sub&gt;1&lt;&#x2F;sub&gt;, O&lt;sub&gt;2&lt;&#x2F;sub&gt;, O&lt;sub&gt;3&lt;&#x2F;sub&gt;, O&lt;sub&gt;6&lt;&#x2F;sub&gt;, O&lt;sub&gt;7&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;3&lt;&#x2F;sub&gt; Low&lt;&#x2F;td&gt;  &lt;td&gt;O&lt;sub&gt;0&lt;&#x2F;sub&gt;, O&lt;sub&gt;4&lt;&#x2F;sub&gt;, O&lt;sub&gt;5&lt;&#x2F;sub&gt;, O&lt;sub&gt;8&lt;&#x2F;sub&gt;, O&lt;sub&gt;9&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;4&lt;&#x2F;sub&gt; High&lt;&#x2F;td&gt; &lt;td&gt;O&lt;sub&gt;6&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;sub&gt;4&lt;&#x2F;sub&gt; Low&lt;&#x2F;td&gt;  &lt;td&gt;O&lt;sub&gt;0&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;table&gt;
&lt;caption&gt;Output Bus Line Equations&lt;&#x2F;caption&gt;
&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Bus Line&lt;&#x2F;td&gt; &lt;td&gt;Boolean Expression&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;0&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(~I&lt;sub&gt;0&lt;&#x2F;sub&gt; | I&lt;sub&gt;1&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;2&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;3&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;4&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;1&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(I&lt;sub&gt;0&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;1&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;2&lt;&#x2F;sub&gt; | I&lt;sub&gt;3&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;2&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(I&lt;sub&gt;2&lt;&#x2F;sub&gt; | I&lt;sub&gt;3&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;3&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(I&lt;sub&gt;1&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;2&lt;&#x2F;sub&gt; | I&lt;sub&gt;3&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;4&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(~I&lt;sub&gt;1&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;2&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;3&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;5&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(~I&lt;sub&gt;0&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;1&lt;&#x2F;sub&gt; | I&lt;sub&gt;2&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;3&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;6&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(I&lt;sub&gt;0&lt;&#x2F;sub&gt; | I&lt;sub&gt;1&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;2&lt;&#x2F;sub&gt; | I&lt;sub&gt;3&lt;&#x2F;sub&gt; | I&lt;sub&gt;4&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;7&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(I&lt;sub&gt;0&lt;&#x2F;sub&gt; | I&lt;sub&gt;1&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;2&lt;&#x2F;sub&gt; | I&lt;sub&gt;3&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;8&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(I&lt;sub&gt;0&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;1&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;2&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;3&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;sub&gt;9&lt;&#x2F;sub&gt;&lt;&#x2F;td&gt;   &lt;td&gt;~(I&lt;sub&gt;0&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;1&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;2&lt;&#x2F;sub&gt; | ~I&lt;sub&gt;3&lt;&#x2F;sub&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;table&gt;
&lt;caption&gt;Extracted ROM Contents of Analyzed Section. Underscores
are for clarity.&lt;&#x2F;caption&gt;
&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Input Bus&lt;&#x2F;td&gt;  &lt;td&gt;Output Bus&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0b0_0000&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_0001&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_0010&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_0011&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_0100&lt;&#x2F;td&gt;  &lt;td&gt;0b00_1100_1000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_0101&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_1000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_0110&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0010&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_0111&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_1000&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_1001&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_1010&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_1011&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0010_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_1100&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_1101&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_1110&lt;&#x2F;td&gt;  &lt;td&gt;0b11_0001_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b0_1111&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0001_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_0000&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_0001&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_0010&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_0011&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_0100&lt;&#x2F;td&gt;  &lt;td&gt;0b00_1000_1000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_0101&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_1000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_0110&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0010&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_0111&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_1000&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_1001&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_1010&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_1011&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0010_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_1100&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_1101&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0000_0001&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_1110&lt;&#x2F;td&gt;  &lt;td&gt;0b11_0001_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0b1_1111&lt;&#x2F;td&gt;  &lt;td&gt;0b00_0001_0000&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;As we can see, a number of inputs result in zero state outputs, and the MSB
only changes the output of two ROM entries depending on whether its set or
not. Perhaps a number of these states are illegal and just given a default
output? I wonder what this ROM is used for? When I figure it out, I&#x27;ll make
an edit to this page!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;future-direction&quot;&gt;Future Direction&lt;&#x2F;h2&gt;
&lt;p&gt;As readers can probably see by now, digitizing and REing old ICs is
completely doable, if tedious. Personally, I would say it&#x27;s more mechanical
than reversing a binary with IDA or radare2, once you know what to look for.
However, like REing a binary, it does take a long time to fully RE an IC.&lt;&#x2F;p&gt;
&lt;p&gt;There are tools to automate the schematic capture process of an IC, and aid
in analysis as well. Olivier Galibert&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;galibert&#x2F;dietools&quot;&gt;dietools&lt;&#x2F;a&gt;
are one example that I hope to discuss in future posts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;As of writing this post (October 12, 2016), my work in vectorizing and
schematic capture of the YM2151 can be found &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cr1901&#x2F;ym2151-decap&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;anecdote&quot;&gt;Anecdote&lt;&#x2F;h3&gt;
&lt;p&gt;Back in 2011, I discovered that the MAME project was decapping ICs to defeat
security&#x2F;protection circuits on old arcade boards that prevented them from
being emulated properly. The me in 2011 thought this was the most
fascinating thing, the &amp;quot;last bastion&amp;quot; of proper accurate emulation. I never
thought I would have the skill set required to do IC analysis.&lt;&#x2F;p&gt;
&lt;p&gt;Even up until summer 2016, I said that I wouldn&#x27;t do IC reverse engineering,
despite preservation of old technology being important to me. I felt it was
beyond my comprehension, and that I would not be able to learn how to
identify features in a reasonable amount of time. With help from others, I
was wrong, and I&#x27;m glad that I was. If you&#x27;re on the fence about
learning a new technical subject, don&#x27;t hesitate. We&#x27;re all smart, and filled
with doubt. Others will be willing to help!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;thanks&quot;&gt;Thanks&lt;&#x2F;h3&gt;
&lt;p&gt;I would like to thank members of siliconpr0n for looking over this post,
especially Olivier Galibert for correcting a few mistakes. Additionally, I&#x27;d
like to thank Digi-Key for their extremely useful &lt;a href=&quot;http:&#x2F;&#x2F;www.digikey.com&#x2F;schemeit&#x2F;&quot;&gt;Scheme-it&lt;&#x2F;a&gt;
schematic program, which I used to create the schematics (including the nice
arrow!).&lt;&#x2F;p&gt;
</content>
	</entry>
  

  
	<entry xml:lang="en">
		<title>Floppy Disk Notes</title>
		<published>2016-02-10T00:00:00+00:00</published>
		<updated>2022-04-30T00:00:00+00:00</updated>
		<link href="https://www.wdj-consulting.com/blog/floppy-lit/" type="text/html"/>
		<id>https://www.wdj-consulting.com/blog/floppy-lit/</id>
		<content type="html">&lt;h1 id=&quot;floppy-disk-notes&quot;&gt;Floppy Disk Notes&lt;&#x2F;h1&gt;
&lt;p&gt;What follows is a set of links that I&#x27;ve collected over the years that
give technical information on how to encode&#x2F;decode floppy disks signals, as
well as the theory of operation behind this dying medium.&lt;&#x2F;p&gt;
&lt;p&gt;I find reading these documents important for preservation purposes,
especially since computer programmers of the past relied on these engineering
details for copy protection purposes.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, a deep understanding of internals may one day help to recover
data that is thought to be lost using statistical analysis of the read signal.&lt;&#x2F;p&gt;
&lt;p&gt;Links are ordered relatively in the order I read them&#x2F;I recommend reading
them, and sections tend to build upon each other.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;general&quot;&gt;General&lt;&#x2F;h2&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.hermannseib.com&#x2F;documents&#x2F;floppy.pdf&gt;The Floppy User Guide&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;A good technical overall technical description of how a floppy drive accesses
data.&lt;&#x2F;dd&gt;

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;floppy-drive&quot;&gt;Floppy Drive&lt;&#x2F;h2&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.mirrorservice.org&#x2F;sites&#x2F;www.bitsavers.org&#x2F;pdf&#x2F;shugart&#x2F;SA8xx&#x2F;50664-0_SA800_801_Theory_of_Operations_Apr76.pdf&gt;SA800&amp;#x2F;801 Diskette Storage Drive Theory of Operations&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;Without question, the most important document on this list.
If you read any document, read this. It&#x27;s not quite enough information
to build a floppy drive from scratch, but it&#x27;s enough to bring someone
interested up to speed. Hard to believe this document is 40 years old in 2016!&lt;&#x2F;dd&gt;

&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.mirrorservice.org&#x2F;sites&#x2F;www.bitsavers.org&#x2F;pdf&#x2F;shugart&#x2F;_specs&#x2F;SA850_450_Read_Channel_Analysis_Dec79.pdf&gt;SA850&amp;#x2F;SA450 Read Channel Analysis Internal Memo&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;This internal memo donated by a Shugart employee includes a
floppy drive read head transfer function analysis based on experiments
Shugart did in the late 70&#x27;s.&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;phase-locked-loops-plls&quot;&gt;Phase-Locked Loops (PLLs)&lt;&#x2F;h2&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.fulviofrisone.com&#x2F;attachments&#x2F;article&#x2F;466&#x2F;Phaselock%20Techniques%20(Gardner-2005).pdf&gt;Phaselock Techniques, Floyd M. Gardner&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;A monograph on analog PLLs. Does not discuss All-Digital PLLs (ADPLLs).&lt;&#x2F;dd&gt;

&lt;dt&gt;&lt;a href=https:&#x2F;&#x2F;www.nxp.com&#x2F;files-static&#x2F;rf_if&#x2F;doc&#x2F;app_note&#x2F;AN535.pdf&gt;NXP Phase Locked Loops Design Fundamentals Application Note&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;A quick reference for analog PLL design.&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;encodings&quot;&gt;Encodings&lt;&#x2F;h2&gt;
&lt;h3&gt;&lt;abbr title=&quot;Modified Frequency Modulation&quot;&gt;MFM&lt;&#x2F;abbr&gt;&lt;&#x2F;h3&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.bitsavers.org&#x2F;components&#x2F;national&#x2F;_dataSheets&#x2F;DP8473&#x2F;AN-505_Floppy_Disk_Data_Separator_Design_Guide_for_the_DP8473_Feb89.pdf&gt;Floppy Disk Data Separator Design Guide for the DP8473&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;To be written.&lt;&#x2F;dd&gt;

&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;bitsavers.informatik.uni-stuttgart.de&#x2F;magazines&#x2F;Computer_Design&#x2F;198002_Encoding-Decoding_Techniques_Double_Floppy_Disc_Capacity.pdf&gt;Encoding&amp;#x2F;Decoding Techniques Double Floppy Disc Capacity&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;Gives background on more complicated physical phenomenon associated with floppy drive recording, such as magnetic domain shifting.&lt;&#x2F;dd&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20150212042616&#x2F;http:&#x2F;&#x2F;www.analog-innovations.com&#x2F;SED&#x2F;FloppyDataExtractor.pdf&gt;Floppy Data Extractor&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;A schematic for a minimum component data separator that does not require a
PLL, but uses a digital equivalent. Perhaps a simple &lt;abbr title=&quot;All Digital
Phase-Locked Loop&quot;&gt;ADPLL&lt;&#x2F;abbr&gt;?&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;h3&gt;&lt;abbr title=&quot;Run-Length Limited&quot;&gt;RLL&lt;&#x2F;abbr&gt;&lt;&#x2F;h3&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.google.com&#x2F;patents&#x2F;US3689899&gt;IBM&amp;#x27;s Patent for (1,8)&amp;#x2F;(2,7) RLL&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;I&#x27;m not aware of any floppy formats that use (2,7) RLL, but hard drives
that descend from MFM floppy drive encodings do use RLL. RLL decoding is far
more involved than FM&#x2F;MFM.&lt;&#x2F;dd&gt;

&lt;&#x2F;dl&gt;
&lt;h3&gt;&lt;abbr title=&quot;Group Code Recording&quot;&gt;GCR&lt;&#x2F;abbr&gt;&lt;&#x2F;h3&gt;
This is a format used by Apple II drives and descendants. Software has
more control over this format, so there are more opportunities for
elaborate data protection compared to the IBM platforms. TODO when I have
time to examine non-IBM formats.
&lt;h2 id=&quot;track-formats&quot;&gt;Track Formats&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;ibm-3740-fm-single-density&quot;&gt;IBM 3740 (FM, Single Density)&lt;&#x2F;h3&gt;
&lt;p&gt;TODO. Described in Shugart&#x27;s Theory of Operations manual.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ibm-system-34-mfm-double-density&quot;&gt;IBM System 34 (MFM, Double Density)&lt;&#x2F;h3&gt;
&lt;p&gt;TODO. Described in various documents on this page, but I&#x27;ve not yet found
a document dedicated to explaining the format.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;floppy-disk-controller-ics&quot;&gt;Floppy Disk Controller ICs&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;nec-765&quot;&gt;NEC 765&lt;&#x2F;h3&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.classiccmp.org&#x2F;dunfield&#x2F;r&#x2F;765.pdf&gt;765 Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;The FDC used in IBM PCs. It is not capable of writing raw data at the level
of the IBM track formats. Thus, attempting to write copy-protected floppies
is likely to fail with this controller.&lt;&#x2F;dd&gt;

&lt;dt&gt;&lt;a href=https:&#x2F;&#x2F;archive.org&#x2F;details&#x2F;bitsavers_necdatashe79_1461697&gt;765 Application Note&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;NEC created an application note to discuss how to integrate the 765 into a
&quot;new&quot; system, either using DMA or polling on receipt of interrupts.&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;ti-tms279x&quot;&gt;TI TMS279X&lt;&#x2F;h3&gt;&lt;&#x2F;h3&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;info-coach.fr&#x2F;atari&#x2F;documents&#x2F;general&#x2F;fd&#x2F;TMS279X_DataSheet.pdf&gt;TMS279X Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;Includes a diagram of the IBM System 34 track format.&lt;&#x2F;dd&gt;

&lt;&#x2F;dl&gt;
&lt;h3&gt;NI DP8473&lt;&#x2F;h3&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=DS009384.PDF&gt;DP8473 Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;A successor to the 765 that is capable of handing formats such as 1.2MB High
Density (HD) disks&lt;&#x2F;dd&gt;

&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.bitsavers.org&#x2F;components&#x2F;national&#x2F;_dataSheets&#x2F;DP8473&#x2F;AN-631_Design_Guide_for_DP8473_in_a_PC-AT_Dec89.pdf&gt;Design Guide for DP8473 in a PC-AT&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;TODO. It appears I lost my original commentary on this document.&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;floppy-disk-controller-cards&quot;&gt;Floppy Disk Controller Cards&lt;&#x2F;h2&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=http:&#x2F;&#x2F;www.minuszerodegrees.net&#x2F;oa&#x2F;OA%20-%20IBM%205.25%20Diskette%20Drive%20Adapter.pdf&gt;IBM PC FDC Card (765)&lt;&#x2F;a&gt;&lt;&#x2F;dt&gt;
&lt;dd&gt;Includes schematics. The PLL circuit on the last page is in particular worth
analyzing.&lt;&#x2F;dd&gt;

&lt;&#x2F;dl&gt;
&lt;p&gt;If anyone has any interesting new documents to add, please feel free
to contact me, and I will add them to this page with credit!&lt;&#x2F;p&gt;
</content>
	</entry>
  
</feed>
